diff --git a/components.d.ts b/components.d.ts index 0806678..3a065f4 100644 --- a/components.d.ts +++ b/components.d.ts @@ -7,7 +7,6 @@ export {} declare module 'vue' { export interface GlobalComponents { - Auth: typeof import('./src/components/Auth/index.vue')['default'] CuDateQuery: typeof import('./src/components/cu-date-query/index.vue')['default'] CuPicker: typeof import('./src/components/cu-picker/index.vue')['default'] CustomNavbar: typeof import('./src/components/custom-navbar/index.vue')['default'] diff --git a/src/pages.json b/src/pages.json index 4d30f53..929d5ea 100644 --- a/src/pages.json +++ b/src/pages.json @@ -42,7 +42,11 @@ }, { "path": "pages/mine/account/index", - "type": "page" + "type": "page", + "style": { + "navigationBarTitleText": "账号安全", + "backgroundColor": "#f1f5f9" + } }, { "path": "pages/mine/feedback/index", diff --git a/src/pages/index/index.vue b/src/pages/index/index.vue index 01e6aac..e495b3e 100644 --- a/src/pages/index/index.vue +++ b/src/pages/index/index.vue @@ -1,6 +1,6 @@ @@ -117,15 +111,19 @@ /** * 首页 * - * 功能说明ï¼? * - 自定义导航栏(搜索框ï¼? * - 轮播图展ç¤? * - 快捷导航入口 + * 功能说明: + * - 自定义导航栏(搜索框) + * - 轮播图展示 + * - 快捷导航入口 * - 通知公告 * - 数据统计 * - 访问趋势图表 * * 技术要点: - * - 使用 useNavbar 处理自定义导航栏高度和胶囊避è®? + * - 使用 useNavbar 处理自定义导航栏高度和胶囊避让; * - 使用 BEM 命名规范组织 CSS - * - 支持暗黑模式(通过 CSS 变量ï¼? */ + * - 支持暗黑模式(通过 CSS 变量) + */ import { computed, ref } from "vue"; import { onReady } from "@dcloudio/uni-app"; @@ -156,9 +154,11 @@ interface NavItem { const router = useRouter(); const userStore = useUserStore(); -// custom-navbar 组件内部已处理导航栏高度与胶囊避è®? +// custom-navbar 组件内部已处理导航栏高度与胶囊避让 + +// ============================================================================ +// 响应式数据 // ============================================================================ -// 响应式数æ�?// ============================================================================ const current = ref(0); const recentDaysRange = ref(7); @@ -178,19 +178,23 @@ const appVersion = ref(""); const noticeList = ref([]); const noticeText = computed(() => { + if (!noticeList.value.length) { + return "暂无通知"; + } const titles = noticeList.value .map((n: NoticePageVO) => n.title) .filter(Boolean) - .slice(0, 5) as string[]; - return titles.length ? titles.join(" ") : ""; + .slice(0, 2) as string[]; + return titles.length ? titles.join(" ") : "暂无通知"; }); -const isNoticeScrolling = computed(() => noticeList.value.length > 1); - // 用户权限列表 const userPerms = computed(() => userStore.userInfo?.perms || []); -// 是否已登å½?const isLogged = computed(() => isLoggedIn()); +// 是否已登录 +const isLogged = computed(() => isLoggedIn()); + +const hasAnyPerm = computed(() => userPerms.value.length > 0); // 检查是否有权限 const hasPermission = (perm: string) => { @@ -212,8 +216,9 @@ const defaultNavList = computed(() => { return result; }); -// 快捷入口:已登录按权限过滤,未登录显示默认菜å�?const quickNavList = computed(() => { - if (!isLogged.value) { +// 快捷入口:已登录按权限过滤,未登录显示默认菜单 +const quickNavList = computed(() => { + if (!isLogged.value || !hasAnyPerm.value) { return defaultNavList.value; } const result: { icon: string; title: string; url: string; perm: string }[] = []; @@ -259,6 +264,7 @@ const chartOpts = ref({ function loadAppVersion() { try { + // eslint-disable-next-line @typescript-eslint/no-explicit-any const p: any = (globalThis as any).plus; if (p?.runtime?.version) { appVersion.value = `v${p.runtime.version}`; @@ -275,7 +281,7 @@ async function loadNoticeData() { return; } try { - const { list } = await NoticeAPI.getMyNoticePage({ pageNum: 1, pageSize: 5 }); + const { list } = await NoticeAPI.getMyNoticePage({ pageNum: 1, pageSize: 2 }); noticeList.value = list || []; } catch { noticeList.value = []; @@ -292,9 +298,7 @@ async function loadVisitStatsData() { async function loadVisitTrendData() { const endDate = dayjs().format("YYYY-MM-DD"); - const startDate = dayjs() - .subtract(recentDaysRange.value - 1, "day") - .format("YYYY-MM-DD"); + const startDate = dayjs().subtract(recentDaysRange.value - 1, "day").format("YYYY-MM-DD"); try { const data: VisitTrendVO = await LogAPI.getVisitTrend({ startDate, endDate }); @@ -302,8 +306,8 @@ async function loadVisitTrendData() { JSON.stringify({ categories: (data.dates || []).map((d) => dayjs(d).format("MM-DD")), series: [ - { name: "访客æ•?UV)", data: data.uvList || [] }, - { name: "浏览é‡?PV)", data: data.pvList || [] }, + { name: "访客数(UV)", data: data.uvList || [] }, + { name: "浏览量(PV)", data: data.pvList || [] }, ], }) ); @@ -317,9 +321,14 @@ async function loadVisitTrendData() { // ============================================================================ function handleNavClick(item: NavItem) { - // 未登录时先检查登录状态并跳转登录é¡? if (!checkLogin()) { + // 未登录 / 无权限时:展示默认导航,但点击统一跳登录 + if (!isLogged.value || !hasAnyPerm.value) { + uni.navigateTo({ url: "/pages/login/index" }); return; } + // 已登录但访问受限时,仍做一次登录校验(防 token 过期) + if (!checkLogin()) return; + router.push({ path: item.url }); } @@ -373,28 +382,29 @@ onReady(() => { .hero { position: relative; } + .hero::after { + content: ""; position: absolute; + left: 0; right: 0; bottom: 0; - left: 0; - z-index: 1; height: 120rpx; - pointer-events: none; - content: ""; background: linear-gradient( to bottom, rgba(245, 247, 250, 0) 0%, rgba(245, 247, 250, 0.85) 60%, rgba(245, 247, 250, 1) 100% ); + pointer-events: none; + z-index: 1; } :deep(.swiper-box) { height: 420rpx; overflow: hidden; - border-bottom-right-radius: 32rpx; border-bottom-left-radius: 32rpx; + border-bottom-right-radius: 32rpx; } :deep(.swiper-box .wd-swiper__item), @@ -403,7 +413,8 @@ onReady(() => { } // ============================================================================ -// 导航æ ?// ============================================================================ +// 导航栏 +// ============================================================================ .navbar { position: fixed; @@ -456,15 +467,16 @@ onReady(() => { .section--overlay { position: relative; z-index: 2; - padding: 18rpx 8rpx; margin-top: -140rpx; + padding: 18rpx 8rpx; background: var(--color-bg); border-radius: 24rpx; box-shadow: 0 16rpx 36rpx rgba(0, 0, 0, 0.08); } // ============================================================================ -// 快捷导航é¡?// ============================================================================ +// 快捷导航项 +// ============================================================================ .nav-item { display: flex; @@ -486,27 +498,28 @@ onReady(() => { } // ============================================================================ -// 通知æ ?// ============================================================================ +// 通知栏 +// ============================================================================ .notice-bar { display: flex; align-items: center; - padding: 24rpx 24rpx 24rpx 20rpx; background: #fff; - border: 1rpx solid rgba(0, 0, 0, 0.04); border-radius: 16rpx; + padding: 24rpx 24rpx 24rpx 20rpx; + border: 1rpx solid rgba(0, 0, 0, 0.04); box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.04); &__icon { - display: flex; flex-shrink: 0; - align-items: center; - justify-content: center; width: 48rpx; height: 48rpx; - margin-right: 16rpx; + display: flex; + align-items: center; + justify-content: center; background: rgba(52, 209, 157, 0.1); border-radius: 12rpx; + margin-right: 16rpx; } &__content { @@ -516,35 +529,14 @@ onReady(() => { &__text { font-size: 26rpx; - font-weight: 500; color: #333; - white-space: nowrap; - } - - &__text--gap { - padding-left: 48rpx; - } - - &__marquee { - width: 100%; + font-weight: 500; + display: -webkit-box; + line-clamp: 1; + -webkit-line-clamp: 1; + -webkit-box-orient: vertical; overflow: hidden; } - - &__marquee-inner { - display: inline-flex; - align-items: center; - white-space: nowrap; - animation: notice-marquee 14s linear infinite; - } -} - -@keyframes notice-marquee { - 0% { - transform: translateX(0); - } - 100% { - transform: translateX(-50%); - } } // ============================================================================ @@ -558,49 +550,55 @@ onReady(() => { } .stat-card { - position: relative; + background: #fff; + border-radius: 16rpx; + padding: 24rpx 20rpx; display: flex; flex-direction: column; - padding: 24rpx 20rpx; - overflow: hidden; - background: #fff; - border: 1rpx solid rgba(0, 0, 0, 0.04); - border-radius: 16rpx; box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.04); + border: 1rpx solid rgba(0, 0, 0, 0.04); + position: relative; + overflow: hidden; + // 背景图标容器 &__bg { position: absolute; right: 12rpx; bottom: 12rpx; width: 70rpx; height: 70rpx; - pointer-events: none; opacity: 0.08; + pointer-events: none; } + // SVG 装饰图形 &__icon { width: 100%; height: 100%; + // 用户组图标 - 访客数 &--user { - background-image: url("/static/icons/visitor.svg"); - background-repeat: no-repeat; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 48 48' fill='%232ab789'%3E%3Ccircle cx='24' cy='16' r='8'/%3E%3Ccircle cx='10' cy='20' r='6'/%3E%3Ccircle cx='38' cy='20' r='6'/%3E%3Cpath d='M24 28c-8 0-14 4-14 8v6h28v-6c0-4-6-8-14-8z'/%3E%3Cpath d='M10 28c-4 0-8 2-8 5v5h8v-5c0-2 1-4 3-5-1 0-2 0-3 0z'/%3E%3Cpath d='M38 28c4 0 8 2 8 5v5h-8v-5c0-2-1-4-3-5 1 0 2 0 3 0z'/%3E%3C/svg%3E"); background-size: contain; + background-repeat: no-repeat; } + // 图表图标 - 浏览量 &--eye { - background-image: url("/static/icons/browser.svg"); - background-repeat: no-repeat; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 48 48' fill='%233a8ee6'%3E%3Crect x='4' y='24' width='8' height='20' rx='2'/%3E%3Crect x='16' y='14' width='8' height='30' rx='2'/%3E%3Crect x='28' y='20' width='8' height='24' rx='2'/%3E%3Crect x='40' y='8' width='8' height='36' rx='2'/%3E%3C/svg%3E"); background-size: contain; + background-repeat: no-repeat; } + // 盾牌徽章图标 - 版本 &--gear { - background-image: url("/static/icons/setting.svg"); - background-repeat: no-repeat; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 48 48' fill='%23e8a838'%3E%3Cpath d='M24 4L8 10v12c0 11 8 18 16 22 8-4 16-11 16-22V10L24 4z'/%3E%3Cpath d='M20 24l4 4 8-8' stroke='white' stroke-width='3' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); background-size: contain; + background-repeat: no-repeat; } } + // 彩色卡片变体 &--green { background: linear-gradient(135deg, #f0fdf9 0%, #fff 100%); } @@ -614,18 +612,18 @@ onReady(() => { } &__head { - position: relative; - z-index: 1; display: flex; align-items: center; justify-content: space-between; margin-bottom: 16rpx; + position: relative; + z-index: 1; } &__label { font-size: 24rpx; - font-weight: 500; color: #999; + font-weight: 500; } &__dot { @@ -650,12 +648,12 @@ onReady(() => { } &__num { - position: relative; - z-index: 1; font-size: 48rpx; font-weight: 700; line-height: 1; letter-spacing: -1rpx; + position: relative; + z-index: 1; &--green { color: #2ab789; @@ -674,8 +672,8 @@ onReady(() => { } &--full { - flex-direction: row; grid-column: 1 / -1; + flex-direction: row; align-items: center; justify-content: space-between; padding: 20rpx 20rpx; @@ -690,10 +688,10 @@ onReady(() => { } .stat-card__bg { - right: 16rpx; - bottom: 50%; width: 50rpx; height: 50rpx; + right: 16rpx; + bottom: 50%; transform: translateY(50%); } } diff --git a/src/pages/login/index.vue b/src/pages/login/index.vue index 239693b..d232454 100644 --- a/src/pages/login/index.vue +++ b/src/pages/login/index.vue @@ -9,14 +9,14 @@