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
+12 -12
View File
@@ -55,7 +55,7 @@
</div>
<div class="flex items-center gap-2">
<span class="font-semibold text-base-content/70 w-20">角色</span>
<span class="badge" :class="user?.role > 0 ? 'badge-warning' : 'badge-ghost'">{{
<span class="badge" :class="(user?.role || 0) > 0 ? 'badge-warning' : 'badge-ghost'">{{
getRoleName(user?.role || 0) }}</span>
</div>
</div>
@@ -149,8 +149,8 @@
</div>
</template>
<script setup>
import { ref, onMounted, computed } from 'vue'
<script setup lang="ts">
import { onMounted, computed } from 'vue'
import BreadcrumbHeader from '@/components/dashboard/BreadcrumbHeader.vue';
import { useAuthStore } from '@/stores/auth';
import { useRouter } from 'vue-router';
@@ -167,18 +167,18 @@ onMounted(async () => {
});
const getTimeOfDay = () => {
const getTimeOfDay = (): string => {
const hour = new Date().getHours();
if (hour < 12) return '早上好';
if (hour < 18) return '下午好';
return '晚上好';
};
const formatQuota = (used, total) => {
const formatQuota = (used: number, total: number): string => {
if (total === 0) return '无限制';
// 格式化金额
const formatCurrency = (amount) => {
const formatCurrency = (amount: number): string => {
if (amount === 0) return '$0';
return `$${amount.toFixed(2)}`;
};
@@ -188,7 +188,7 @@ const formatQuota = (used, total) => {
};
const getRoleName = (role) => {
const getRoleName = (role: number): string => {
switch (role) {
case 20: return 'Root';
case 10: return 'Admin';
@@ -197,7 +197,7 @@ const getRoleName = (role) => {
};
// 格式化日期时间
function formatDateTime(unixTimestamp) {
function formatDateTime(unixTimestamp?: number): string {
// 如果时间戳不存在或为0,返回'未知'
if (!unixTimestamp) return '未知';
@@ -216,7 +216,7 @@ function formatDateTime(unixTimestamp) {
}
// 获取背景渐变类
const getGradientClass = () => {
const getGradientClass = (): string => {
const hour = new Date().getHours();
if (hour < 6) return 'bg-gradient-to-r from-[#e0f2f1] to-[#1a1a1a] bg-opacity-50 backdrop-blur-lg'; // 深夜到黎明:柔和的薄荷绿渐变到微黑
if (hour < 12) return 'bg-gradient-to-r from-[#8cc7f1] to-[#cf6f26] bg-opacity-50 backdrop-blur-lg'; // 早晨:温暖的杏仁色渐变到深灰
@@ -229,7 +229,7 @@ const getGradientClass = () => {
// 计算配额百分比
const quotaPercentage = computed(() => {
if (!user.value || user.value.unlimited_quota || !user.value.quota) return 0;
return (user.value.used_quota / user.value.quota) * 100;
return (user.value.used_quota ?? 0) / user.value.quota * 100;
});
// 获取配额颜色
@@ -249,12 +249,12 @@ const getQuotaColorClass = computed(() => {
});
// 用户最后活动时间
const getLastActive = () => {
const getLastActive = (): string => {
if (!user.value || !user.value?.updated_at) return '未知';
const lastActive = new Date(user.value.updated_at * 1000);
const now = new Date();
const diff = now - lastActive;
const diff = now.getTime() - lastActive.getTime();
// 转换为天/小时/分钟
const days = Math.floor(diff / (1000 * 60 * 60 * 24));