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 @@
-
+
-
+
-
-
- {{ noticeText }}
- {{ noticeText }}
-
-
- {{ noticeText }}
+ {{ noticeText || "暂无通知公告" }}
@@ -55,7 +49,7 @@
- 访客æ•?/text>
+ 访客数
@@ -67,7 +61,7 @@
- 浏览é‡?/text>
+ 浏览量
@@ -99,8 +93,8 @@
inline
@change="handleDataRangeChange"
>
- ��/wd-radio>
- �5�/wd-radio>
+ 近7天
+ 近15天
@@ -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 @@
- �/text>
+ ‹
-
+
@@ -34,7 +34,7 @@
-
+
@@ -80,7 +80,7 @@
/>
-
+
- {{ smsCountdown > 0 ? `${smsCountdown}s` : "获取验证ç ? }}
+ {{ smsCountdown > 0 ? `${smsCountdown}s` : "获取验证码" }}
@@ -112,16 +112,16 @@
:disabled="loading"
@click="handleLogin"
>
- {{ loading ? "登录ä¸?.." : "ç™?å½? }}
+ {{ loading ? "登录中..." : "登 录" }}
- {{ loginMode === "PASSWORD" ? "忘记密码ï¼? : "记得密码ï¼? }}
+ {{ loginMode === "PASSWORD" ? "忘记密码?" : "记得密码?" }}
- {{ loginMode === "PASSWORD" ? "验证码登å½? : "密码登录" }}
+ {{ loginMode === "PASSWORD" ? "验证码登录" : "密码登录" }}
@@ -135,7 +135,8 @@
@getphonenumber="handleWechatPhoneLogin"
>
- 微信一键登å½?
+ 微信一键登录
+
@@ -146,7 +147,7 @@
-
+
其他登录方式
@@ -166,10 +167,14 @@
- 我已阅读并同æ„?
- 《用户协议ã€?
- �
- 《隐私政策ã€?
+ 我已阅读并同意
+
+ 《用户协议》
+
+ 与
+
+ 《隐私政策》
+
@@ -177,7 +182,7 @@
-
+
@@ -233,8 +238,9 @@
- 请阅读并同意有来技æœ? 《用户协议ã€?/text>
- 《隐私政策ã€?/text>
+ 请阅读并同意有来技术
+ 《用户协议》
+ 《隐私政策》
@@ -262,7 +268,8 @@ const toast = useToast();
const message = useMessage("policy-box");
const userStore = useUserStore();
-// 状æ€?const loading = ref(false);
+// 状态
+const loading = ref(false);
const showPassword = ref(false);
const isAgreePolicy = ref(false);
const loginMode = ref<"PASSWORD" | "SMS" | "WECHAT">("PASSWORD");
@@ -276,13 +283,15 @@ const formData = ref({
captchaCode: "",
});
-// 图形验证ç ?const captchaId = ref("");
+// 图形验证码
+const captchaId = ref("");
const captchaBase64 = ref("");
const captchaLoading = ref(false);
const redirect = ref("/pages/index/index");
-// 绑定手机号相å…?const showBindMobilePopup = ref(false);
+// 绑定手机号相关
+const showBindMobilePopup = ref(false);
const bindLoading = ref(false);
const bindSmsCountdown = ref(0);
const bindSmsTimer = ref | null>(null);
@@ -296,7 +305,8 @@ const bindMobileForm = ref({
const pendingLoginAction = ref<"FORM" | "WECHAT_PHONE" | null>(null);
const pendingWechatPhoneCode = ref("");
-// 获取图形验证ç ?const fetchCaptcha = async () => {
+// 获取图形验证码
+const fetchCaptcha = async () => {
if (captchaLoading.value) return;
try {
captchaLoading.value = true;
@@ -305,16 +315,17 @@ const pendingWechatPhoneCode = ref("");
captchaId.value = res.captchaId;
captchaBase64.value = res.captchaBase64;
} catch (e) {
- console.error("获取验证码失è´?, e);
+ console.error("获取验证码失败", e);
} finally {
captchaLoading.value = false;
}
};
-// 计算属æ€?const loginModeDesc = computed(() => {
+// 计算属性
+const loginModeDesc = computed(() => {
const modeMap = {
PASSWORD: "使用账号密码登录",
- SMS: "使用手机验证码登å½?,
+ SMS: "使用手机验证码登录",
WECHAT: "使用微信快捷登录",
};
return modeMap[loginMode.value];
@@ -351,11 +362,13 @@ onLoad((options: any) => {
// #endif
// #ifndef MP-WEIXIN
- // 非微信环境强制使用密码登å½? if (loginMode.value === "WECHAT") {
+ // 非微信环境强制使用密码登录
+ if (loginMode.value === "WECHAT") {
loginMode.value = "PASSWORD";
}
// #endif
- // 获取图形验证ç ? fetchCaptcha();
+ // 获取图形验证码
+ fetchCaptcha();
});
onShow(() => {
@@ -412,9 +425,9 @@ const openPolicyDialog = (action: "FORM" | "WECHAT_PHONE", phoneCode: string = "
async function doFormLogin() {
if (!canSubmit.value) {
if (loginMode.value === "PASSWORD") {
- toast.error("请输入用户名和密ç ?);
+ toast.error("请输入用户名和密码");
} else if (loginMode.value === "SMS") {
- toast.error("请输入正确的手机号和验证ç ?);
+ toast.error("请输入正确的手机号和验证码");
}
return;
}
@@ -443,7 +456,8 @@ async function doFormLogin() {
setTimeout(() => uni.reLaunch({ url: redirect.value }), 800);
} catch (error: any) {
toast.error(error?.message || "登录失败");
- // 登录失败刷新验证ç ? if (loginMode.value === "PASSWORD") {
+ // 登录失败刷新验证码
+ if (loginMode.value === "PASSWORD") {
fetchCaptcha();
}
} finally {
@@ -467,16 +481,16 @@ const handleSendCode = async () => {
return;
}
if (!isValidMobile(mobile)) {
- toast.error("请输入正确的手机å�?);
+ toast.error("请输入正确的手机号");
return;
}
try {
await AuthAPI.sendSmsLoginCode(mobile);
- toast.success("验证码已发é€?);
+ toast.success("验证码已发送");
startSmsCountdown(smsCountdown, smsTimer);
} catch (error: any) {
- toast.error(error?.message || "发送失è´?);
+ toast.error(error?.message || "发送失败");
}
};
@@ -560,16 +574,16 @@ const handleSendBindCode = async () => {
if (bindSmsCountdown.value > 0) return;
const mobile = bindMobileForm.value.mobile.trim();
if (!isValidMobile(mobile)) {
- toast.error("请输入正确的手机å�?);
+ toast.error("请输入正确的手机号");
return;
}
try {
await AuthAPI.sendSmsLoginCode(mobile);
- toast.success("验证码已发é€?);
+ toast.success("验证码已发送");
startSmsCountdown(bindSmsCountdown, bindSmsTimer);
} catch (error: any) {
- toast.error(error?.message || "发送失è´?);
+ toast.error(error?.message || "发送失败");
}
};
@@ -586,7 +600,7 @@ const handleBindMobile = async () => {
if (bindLoading.value) return;
const { mobile, code } = bindMobileForm.value;
if (!isValidMobile(mobile)) {
- toast.error("请输入正确的手机å�?);
+ toast.error("请输入正确的手机号");
return;
}
if (!code.trim()) {
@@ -732,7 +746,8 @@ const handleBack = () => {
letter-spacing: 0.08em;
}
-// 主内å®?.login-main {
+// 主内容
+.login-main {
position: relative;
z-index: 10;
display: flex;
@@ -819,7 +834,8 @@ const handleBack = () => {
}
}
-// 输入框容å™?.input-box {
+// 输入框容器
+.input-box {
display: flex;
align-items: center;
height: 48px;
@@ -852,7 +868,8 @@ const handleBack = () => {
padding: 8px;
}
-// 主按é’?.btn-primary {
+// 主按钮
+.btn-primary {
position: relative;
display: flex;
align-items: center;
@@ -950,7 +967,8 @@ const handleBack = () => {
}
}
-// 验证码按é’?.sms-btn {
+// 验证码按钮
+.sms-btn {
display: flex;
align-items: center;
justify-content: center;
@@ -1017,7 +1035,8 @@ const handleBack = () => {
border-bottom-color: #60a5fa;
}
-// 分割çº?.divider {
+// 分割线
+.divider {
display: flex;
align-items: center;
margin-top: 24px;
@@ -1075,7 +1094,8 @@ const handleBack = () => {
color: #2563eb;
}
-// 绑定手机号弹çª?.bind-popup {
+// 绑定手机号弹窗
+.bind-popup {
padding: 24px;
}
diff --git a/src/pages/mine/account/index.vue b/src/pages/mine/account/index.vue
index 4081979..6231d11 100644
--- a/src/pages/mine/account/index.vue
+++ b/src/pages/mine/account/index.vue
@@ -1,34 +1,40 @@
-
+
-
+
-
+
-
+
diff --git a/src/pages/mine/feedback/index.vue b/src/pages/mine/feedback/index.vue
index d789561..849f1b1 100644
--- a/src/pages/mine/feedback/index.vue
+++ b/src/pages/mine/feedback/index.vue
@@ -1,5 +1,5 @@
-
+
选填,最多上传3张图片
diff --git a/src/pages/mine/index.vue b/src/pages/mine/index.vue
index ce3609f..ff9d078 100644
--- a/src/pages/mine/index.vue
+++ b/src/pages/mine/index.vue
@@ -198,7 +198,7 @@ const userInfo = computed(() => userStore.userInfo);
const defaultAvatar = "/static/images/default-avatar.png";
const hasAccessToken = ref(!!getAccessToken());
-const isLogin = computed(() => hasAccessToken.value);
+const isLogin = computed(() => !!getAccessToken());
const headerBackground = computed(() => {
const color = currentThemeColor.value || "#4d80f0";
@@ -517,8 +517,8 @@ const helpItems = computed(() => [
position: absolute;
top: 0;
right: 0;
- bottom: 320rpx;
left: 0;
+ height: 520rpx;
pointer-events: none;
}
diff --git a/src/pages/mine/official/index.vue b/src/pages/mine/official/index.vue
index bd4b1ed..c897d90 100644
--- a/src/pages/mine/official/index.vue
+++ b/src/pages/mine/official/index.vue
@@ -1,5 +1,5 @@
-
+