refactor: frontend TS migration + Tailwind4/daisyUI5 unify + BUILDPLATFORM docker build

- migrate frontend to TypeScript (vue-tsc strict in build), upgrade all deps to latest (Vite 8, Tailwind 4, daisyUI 5, Pinia 4, vue-router 5)
- restructure frontend dirs (api/components/common/layouts/styles/types/views)
- drop Element Plus, add daisyUI TagInput; main CSS 490KB->163KB, entry JS 618KB->1.3KB
- rewrite Dockerfile(.cn): frontend/backend stages pinned to $BUILDPLATFORM, CGO_ENABLED=0 cross-compile, no QEMU in multi-arch builds; add .dockerignore
- local dev: Vite /api proxy + make dev targets; go:embed all:dist with .gitkeep so backend runs without prior frontend build
- fix latent bugs: Keys.vue users ref, Settings.vue undefined userStore, Login.vue Ref-as-error display, res.error misuse
- add REFACTOR_PLAN.md (phased refactor log)
This commit is contained in:
Sakurasan
2026-08-29 23:27:31 +08:00
parent 4a68ff6162
commit ac0a6808ec
55 changed files with 2497 additions and 2318 deletions
+19 -17
View File
@@ -214,22 +214,24 @@
</div>
</template>
<script setup>
import { ref, computed, onMounted, inject } from 'vue';
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue';
import { useRoute } from 'vue-router';
import { Eye, EyeOff, BadgeCheck, Send, CircleX, CircleCheckBig, TrashIcon, Infinity } from 'lucide-vue-next'; // Ensure lucide-vue-next is installed
import { useUserStore } from '../../stores/user';
import { Eye, EyeOff, BadgeCheck, Send, CircleX, CircleCheckBig, TrashIcon, Infinity } from '@lucide/vue';
import { useUserStore } from '@/stores/user';
import BreadcrumbHeader from '@/components/dashboard/BreadcrumbHeader.vue';
import { useToast } from '@/composables/toast';
import type { UserInfo, TokenInfo } from '@/types';
const route = useRoute();
const userStore = useUserStore();
const { setToast } = inject('toast');
const { setToast } = useToast();
const userId = computed(() => route.query.id);
onMounted(async () => {
if (userId.value) {
await userStore.getUser(userId.value);
await userStore.getUser(userId.value as string);
}
});
@@ -237,7 +239,7 @@ const user = computed(() => userStore.user);
const loading = computed(() => userStore.loading); // Access loading state
// 更新状态
const updateStatus = async (user) => {
const updateStatus = async (user: UserInfo) => {
try {
const action = user.active ? 'enable' : 'disable';
const res = await userStore.userOption(action, [user.id]);
@@ -247,7 +249,7 @@ const updateStatus = async (user) => {
setToast(res.data?.error || `用户 ${user.id} ${action} 失败`, 'error');
}
await userStore.refreshUser(user.id);
} catch (error) {
} catch (error: any) {
user.active = !user.active;
console.error('状态更新失败:', error);
// setToast(error.response.data?.error || '状态更新失败', 'error');
@@ -257,7 +259,7 @@ const updateStatus = async (user) => {
const updateUser = async () => {
if (!user.value) return;
try {
const payload = {
const payload: Partial<UserInfo> = {
name: user.value.name,
username: user.value.username,
email: user.value.email,
@@ -270,13 +272,13 @@ const updateUser = async () => {
if (user.value.password) {
payload.password = user.value.password;
}
const res = await userStore.editUser(userId.value, payload);
const res = await userStore.editUser(userId.value as string, payload);
console.log('updateUser', res)
if (res.data?.code == 200) {
setToast(`User ${userId.value} updated`, 'success');
}
await userStore.refreshUser(userId.value);
} catch (err) {
await userStore.refreshUser(userId.value as string);
} catch (err: any) {
console.error('Error updating user:', err.response?.data?.data?.error);
}
};
@@ -290,11 +292,11 @@ const togglePasswordVisibility = () => {
};
// 格式化角色
const formatRole = (role) => {
const formatRole = (role?: number): string => {
switch (true) {
case role > 10:
case (role ?? 0) > 10:
return 'Root';
case role > 0:
case (role ?? 0) > 0:
return 'Admin';
default:
return 'U';
@@ -316,7 +318,7 @@ const formatRole = (role) => {
// }
// 格式化日期
const formatDate = (dateString) => {
const formatDate = (dateString?: number): string | null => {
if (!dateString) return null;
try {
return new Intl.DateTimeFormat('sv-SE', { dateStyle: 'short', timeStyle: 'short' }).format(new Date(dateString * 1000)); // Multiply by 1000 for JavaScript Date
@@ -327,7 +329,7 @@ const formatDate = (dateString) => {
};
// 删除token
const revokeToken = (tokenId) => {
const revokeToken = (tokenId: TokenInfo['id']) => {
console.log('Revoking token:', tokenId);
};