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
+21 -21
View File
@@ -48,7 +48,7 @@
@change="updateStatus(token)" />
</td>
<!-- <td class="font-mono text-xs px-2 py-3">{{ token.key }}</td> -->
<td class="px-2 py-3">{{ token.expired_at == 0 ? 'Never' : unixToDate(token.expired_at) }}</td>
<td class="px-2 py-3">{{ token.expired_at == 0 ? 'Never' : unixToDate(token.expired_at ?? 0) }}</td>
<td class="px-2 py-3">
<template v-if="token.unlimited_quota">
<Infinity />
@@ -103,54 +103,54 @@
</div>
</template>
<script setup>
import { ref, onMounted, inject, computed, watch } from 'vue';
import { useRouter } from 'vue-router';
<script setup lang="ts">
import { ref, onMounted, watch, computed } from 'vue';
import BreadcrumbHeader from '@/components/dashboard/BreadcrumbHeader.vue';
import QRCodeCard from '@/components/QRCodeCard.vue';
import QRCodeCard from '@/components/common/QRCodeCard.vue';
import TokenNew from '@/views/dashboard/TokenNew.vue';
import { useAuthStore } from '@/stores/auth';
import {
EyeIcon, PlusIcon, TrashIcon, Infinity, Eraser
} from 'lucide-vue-next';
} from '@lucide/vue';
import { unixToDate } from '@/utils/format-date';
import { useToast } from '@/composables/toast';
import type { TokenInfo } from '@/types';
const router = useRouter();
const authStore = useAuthStore();
const user = computed(() => authStore.user);
const { setToast } = inject('toast');
const { setToast } = useToast();
onMounted(async () => {
await authStore.refreshProfile();
})
watch(() => authStore.user, async (newUser) => {
if (newUser.expired_at > 0) {
watch(() => authStore.user, (newUser) => {
if (newUser && newUser.expired_at && newUser.expired_at > 0) {
newUser.format_expired_at = unixToDate(newUser.expired_at);
}
})
const updateStatus = async (token) => {
const updateStatus = async (token: TokenInfo) => {
console.log(token);
try {
const res = await authStore.updateToken({ userid: token.userid, id: token.id, name: token.name, active: token.active });
if (res.data?.code == 200) {
setToast(`Token ${token.name} updated`, 'success');
}
} catch (error) {
} catch (error: any) {
token.active = !token.active
console.log(error.response.data.error);
setToast(error.response.data.error, 'error');
}
}
const confirmRevokeToken = async (token) => {
const confirmRevokeToken = async (token: TokenInfo) => {
if (confirm(`确认删除 ${token.name}?`)) {
await revokeToken(token);
}
}
const revokeToken = async (token) => {
const revokeToken = async (token: TokenInfo) => {
try {
const res = await authStore.deleteToken(token.id);
if (res.data?.code == 200) {
@@ -158,12 +158,12 @@ const revokeToken = async (token) => {
}
await authStore.refreshProfile();
} catch (error) {
} catch (error: any) {
setToast(error.response.data.error, 'error');
}
}
const cleanUsedToken = async (token) => {
const cleanUsedToken = async (token: TokenInfo) => {
if (token.used_quota == 0 || token.used_quota == null) {
return;
@@ -175,19 +175,19 @@ const cleanUsedToken = async (token) => {
setToast(`Token ${token.name} used quota reset`, 'success');
}
await authStore.refreshProfile();
} catch (error) {
} catch (error: any) {
console.log(error);
setToast(error, 'error');
}
}
const showTokenModel = ref(false);
const tokenRef = ref(null);
const viewToken = (token) => {
const tokenRef = ref<HTMLDialogElement | null>(null);
const viewToken = (token: TokenInfo) => {
const dialog = tokenRef.value;
if (dialog) {
if (!dialog.hasAttribute('open')) {
qrCodeValue.value = token.key;
qrCodeValue.value = token.key || '';
dialog.showModal();
} else {
if (dialog.hasAttribute('open')) {
@@ -202,7 +202,7 @@ const qrCodeValue = ref('');
// 关闭模态框
const modalRef = ref(null);
const modalRef = ref<HTMLDialogElement | null>(null);
const closeModal = async () => {
if (modalRef.value) {
modalRef.value.close();