refactor: 暗黑模式适配调整
This commit is contained in:
99
.cursorrules
Normal file
99
.cursorrules
Normal file
@@ -0,0 +1,99 @@
|
||||
## 样式(CSS/SCSS)与 UnoCSS 使用规范(Cursor Rules)
|
||||
|
||||
目标:在不牺牲可读性的前提下最大化复用,减少冗余,保证小程序/H5 多端一致、整洁大方。
|
||||
|
||||
### 1) 总原则
|
||||
- 优先使用 UnoCSS 工具类与已定义的 shortcuts,避免重复造轮子。
|
||||
- 同一工具类组合在同一页面或多个页面中出现 ≥ 2 次时,提取为“语义化类名”。
|
||||
- 禁止使用 `@apply`,禁止在 SCSS 中重复定义 UnoCSS 已提供的工具类或在 `unocss.config.ts` 中已存在的 shortcuts(如:`flex`、`flex-start`、`flex-between`、`flex-col-center` 等)。
|
||||
- 组件库样式覆盖和页面布局样式分离:页面/组件的布局与样式用普通类,覆盖 wot 组件样式用“外部样式类”。
|
||||
|
||||
### 2) 目录与作用域
|
||||
- 页面私有样式:写在当前 `.vue` 的 `<style lang="scss" scoped>` 中。
|
||||
- 多处复用的语义化样式:优先抽到所属页面/模块级样式;跨页面复用再考虑提到 `src/styles` 公共样式文件。
|
||||
- 避免把零碎的、只出现一次的样式放到全局,保持全局样式轻量。
|
||||
|
||||
### 3) 覆盖 wot-design-uni 组件样式(外部样式类)
|
||||
- 使用 `custom-class` 传入类名,禁止对 wot 组件使用 `class` 覆盖样式。
|
||||
- 因为 SFC `scoped`,覆盖子组件需使用 `:deep()`,并加 `!important` 提升优先级。
|
||||
- 在“自定义组件”中使用 wot 组件时,若样式穿透无效,按需开启 `styleIsolation: 'shared'`。
|
||||
- 参考文档:[小程序使用外部样式类(wot-design-uni)](https://wot-design-uni.pages.dev/guide/common-problems.html#%E5%B0%8F%E7%A8%8B%E5%BA%8F%E4%BD%BF%E7%94%A8%E5%A4%96%E9%83%A8%E6%A0%B7%E5%BC%8F%E7%B1%BB)
|
||||
|
||||
示例(按钮):
|
||||
|
||||
```vue
|
||||
<wd-button custom-class="logout-button" plain @click="handleLogout">退出登录</wd-button>
|
||||
```
|
||||
|
||||
```scss
|
||||
/* 页面/组件内 */
|
||||
:deep(.logout-button) {
|
||||
width: 100% !important;
|
||||
height: 80rpx !important;
|
||||
font-size: 32rpx !important;
|
||||
font-weight: bold !important;
|
||||
border-radius: 40rpx !important;
|
||||
}
|
||||
```
|
||||
|
||||
### 4) UnoCSS 约定与最小用法
|
||||
- 预设、shortcuts 位于 `unocss.config.ts`,不要在 SCSS 中重复定义这些类。
|
||||
- 常用 shortcuts(已存在,无需再自定义):
|
||||
- `flex-start`、`flex-between`、`flex-center`、`flex-col-center` 等。
|
||||
- 可以自由组合 UnoCSS 工具类(如:`p-20rpx`、`px-24rpx`、`border-b` 等)。
|
||||
- 不可读的长串组合若重复使用,抽成语义化类名。
|
||||
- 复杂/冗长的原子类组合(例如包含 ≥4 个原子类,且会在项目内重复出现)一律改为“外部样式类”(语义化类名),以避免模板膨胀;模板仅保留必要的少量工具类。
|
||||
- 简单、一次性且语义明确的区域优先使用 UnoCSS 原子类(例如单个 `p-30rpx`、`text-center`、`font-600` 等);
|
||||
复杂/冗长(≥4)或会复用的组合抽为“外部样式类”。
|
||||
|
||||
示例(页面语义化类):
|
||||
|
||||
```vue
|
||||
<view class="card-header">...</view>
|
||||
```
|
||||
|
||||
```scss
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 20rpx 24rpx;
|
||||
border-bottom: 1rpx solid #e5e7eb;
|
||||
}
|
||||
|
||||
:deep(.dark) .card-header { border-bottom-color: #374151; }
|
||||
```
|
||||
|
||||
### 5) 语义化抽取的判定标准
|
||||
- 满足以下任一条件即可抽取为语义化类名:
|
||||
- 同一工具类组合在项目内出现 ≥ 2 次;
|
||||
- 该区域具备明确的语义(如“卡片头部”、“工具项”等);
|
||||
- 需要在暗色模式等主题下统一控制样式。
|
||||
|
||||
推荐命名:`kebab-case`,表达语义而非表现形式,例如:`card-header`、`tool-item`、`user-profile`。
|
||||
|
||||
### 6) 属性书写顺序(遵循项目 Linter)
|
||||
- 基本顺序:`display` → 布局(如 `flex-*`:先 `align-items`,后 `justify-content`)→ 尺寸(`width/height`)→ 间距(`margin/padding`)→ 背景 → 边框 → 圆角 → 阴影 → 过渡/变换。
|
||||
- 避免无意义的属性覆盖;必要时使用 `!important`,仅用于外部样式类覆盖。
|
||||
|
||||
### 7) 深色模式
|
||||
- 使用 `:deep(.dark) .your-class { ... }` 定义暗色样式,保持与明亮模式同一语义类名。
|
||||
|
||||
### 8) 示例对照(本项目中已落地)
|
||||
- 将重复的工具项组合 `w-1/4 flex-col-center mb-30rpx` 抽取为:
|
||||
- `tool-item`(语义化类,含列方向与对齐、宽度 25%、下间距)
|
||||
- 将重复的卡片头部组合 `flex-between p-20rpx px-24rpx border-b ...` 抽取为:
|
||||
- `card-header`(统一控制边距、分隔线与暗色模式)
|
||||
- 将数据统计卡片 `flex p-30rpx mx-30rpx my-20rpx bg-white rounded-16rpx shadow-sm dark:bg-gray-800` 抽取为:
|
||||
- `stats-card`、`stats-item`、`stats-divider`(统一控制布局/间距/暗色色板)
|
||||
- 覆盖 wot 组件样式:
|
||||
- 使用 `custom-class` + `:deep()` + `!important`,不要用 `class`。
|
||||
|
||||
### 9) 禁止项(务必遵守)
|
||||
- 不要在 SCSS 中定义 `.flex`、`.flex-start`、`.flex-between` 等已由 UnoCSS/shortcuts 提供的类。
|
||||
- 不要再使用 `@apply`。
|
||||
- 不要把一次性的样式放到全局公共样式。
|
||||
|
||||
---
|
||||
本规则文件旨在为 Cursor 自动补全与协作提供统一参考,遇到冲突以项目 Lint 规则与 `unocss.config.ts` 为准。
|
||||
|
||||
7
components.d.ts
vendored
7
components.d.ts
vendored
@@ -21,9 +21,12 @@ declare module 'vue' {
|
||||
WdCard: typeof import('wot-design-uni/components/wd-card/wd-card.vue')['default']
|
||||
WdCell: typeof import('wot-design-uni/components/wd-cell/wd-cell.vue')['default']
|
||||
WdCellGroup: typeof import('wot-design-uni/components/wd-cell-group/wd-cell-group.vue')['default']
|
||||
WdCollapse: typeof import('wot-design-uni/components/wd-collapse/wd-collapse.vue')['default']
|
||||
WdCollapseItem: typeof import('wot-design-uni/components/wd-collapse-item/wd-collapse-item.vue')['default']
|
||||
WdConfigProvider: typeof import('wot-design-uni/components/wd-config-provider/wd-config-provider.vue')['default']
|
||||
WdDivider: typeof import('wot-design-uni/components/wd-divider/wd-divider.vue')['default']
|
||||
WdForm: typeof import('wot-design-uni/components/wd-form/wd-form.vue')['default']
|
||||
WdFormItem: typeof import('wot-design-uni/components/wd-form-item/wd-form-item.vue')['default']
|
||||
WdGrid: typeof import('wot-design-uni/components/wd-grid/wd-grid.vue')['default']
|
||||
WdGridItem: typeof import('wot-design-uni/components/wd-grid-item/wd-grid-item.vue')['default']
|
||||
WdIcon: typeof import('wot-design-uni/components/wd-icon/wd-icon.vue')['default']
|
||||
@@ -38,11 +41,15 @@ declare module 'vue' {
|
||||
WdPopup: typeof import('wot-design-uni/components/wd-popup/wd-popup.vue')['default']
|
||||
WdRadio: typeof import('wot-design-uni/components/wd-radio/wd-radio.vue')['default']
|
||||
WdRadioGroup: typeof import('wot-design-uni/components/wd-radio-group/wd-radio-group.vue')['default']
|
||||
WdStatusTip: typeof import('wot-design-uni/components/wd-status-tip/wd-status-tip.vue')['default']
|
||||
WdSwiper: typeof import('wot-design-uni/components/wd-swiper/wd-swiper.vue')['default']
|
||||
WdSwitch: typeof import('wot-design-uni/components/wd-switch/wd-switch.vue')['default']
|
||||
WdTabbar: typeof import('wot-design-uni/components/wd-tabbar/wd-tabbar.vue')['default']
|
||||
WdTabbarItem: typeof import('wot-design-uni/components/wd-tabbar-item/wd-tabbar-item.vue')['default']
|
||||
WdTag: typeof import('wot-design-uni/components/wd-tag/wd-tag.vue')['default']
|
||||
WdText: typeof import('wot-design-uni/components/wd-text/wd-text.vue')['default']
|
||||
WdTextarea: typeof import('wot-design-uni/components/wd-textarea/wd-textarea.vue')['default']
|
||||
WdToast: typeof import('wot-design-uni/components/wd-toast/wd-toast.vue')['default']
|
||||
WdUpload: typeof import('wot-design-uni/components/wd-upload/wd-upload.vue')['default']
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,10 +25,9 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRoute } from "vue-router";
|
||||
import { useThemeStore } from "@/store";
|
||||
import { useTabbar } from "@/composables/useTabbar";
|
||||
import { useRouter } from "uni-mini-router";
|
||||
import { useRouter, useRoute } from "uni-mini-router";
|
||||
import { storeToRefs } from "pinia";
|
||||
|
||||
const router = useRouter();
|
||||
@@ -47,11 +46,8 @@ onMounted(() => {
|
||||
uni.hideTabBar();
|
||||
// #endif
|
||||
nextTick(() => {
|
||||
if (route.name) {
|
||||
const routeName = typeof route.name === "string" ? route.name : "";
|
||||
if (routeName !== activeTabbar.value.name) {
|
||||
setTabbarItemActive(routeName);
|
||||
}
|
||||
if (route.name && route.name !== activeTabbar.value.name) {
|
||||
setTabbarItemActive(route.name);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -43,7 +43,14 @@
|
||||
},
|
||||
{
|
||||
"path": "pages/mine/feedback/index",
|
||||
"type": "page"
|
||||
"type": "page",
|
||||
"name": "feedback",
|
||||
"style": {
|
||||
"navigationBarTitleText": "意见反馈"
|
||||
},
|
||||
"meta": {
|
||||
"requireAuth": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/mine/profile/complete-profile",
|
||||
@@ -117,5 +124,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"__esModule": true,
|
||||
"subPackages": []
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<view class="app-container">
|
||||
<view class="app-container dark:text-[var(--wot-dark-color)]">
|
||||
<wd-swiper
|
||||
v-model:current="current"
|
||||
:list="swiperList"
|
||||
@@ -89,6 +89,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { dayjs } from "wot-design-uni";
|
||||
import { useRouter } from "uni-mini-router";
|
||||
|
||||
// 定义访问统计数据类型
|
||||
interface VisitStatsVO {
|
||||
@@ -257,18 +258,6 @@ onReady(() => {
|
||||
loadVisitStatsData();
|
||||
loadVisitTrendData();
|
||||
});
|
||||
|
||||
onShow(() => {
|
||||
// 确保 tabbar 状态正确
|
||||
const pages = getCurrentPages();
|
||||
if (pages.length > 0) {
|
||||
const currentPage = pages[pages.length - 1];
|
||||
if (currentPage.route === "pages/index/index") {
|
||||
// 通过事件通知 tabbar 布局更新状态
|
||||
uni.$emit("updateTabbar", "index");
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<route lang="json">
|
||||
|
||||
@@ -12,12 +12,7 @@
|
||||
<wd-form v-if="loginType === 'account'" ref="loginFormRef" :model="loginFormData">
|
||||
<!-- 用户名输入框 -->
|
||||
<view class="form-item">
|
||||
<wd-icon
|
||||
name="user"
|
||||
size="22"
|
||||
:color="isDarkMode ? '#7AC5FF' : '#333'"
|
||||
class="input-icon"
|
||||
/>
|
||||
<wd-icon name="user" size="22" class="input-icon" />
|
||||
<input
|
||||
v-model="loginFormData.username"
|
||||
class="form-input input-transparent"
|
||||
@@ -33,7 +28,7 @@
|
||||
<input
|
||||
v-model="loginFormData.password"
|
||||
class="form-input input-transparent"
|
||||
:type="showPassword ? 'text' : 'password'"
|
||||
:password="!showPassword"
|
||||
placeholder="请输入密码"
|
||||
placeholder-class="input-placeholder"
|
||||
/>
|
||||
@@ -135,11 +130,12 @@ const loginFormData = ref<LoginData>({
|
||||
password: "123456",
|
||||
});
|
||||
|
||||
// 获取重定向参数
|
||||
// 仅使用 query 作为重定向来源
|
||||
const redirect = ref("/pages/index/index");
|
||||
onLoad((options) => {
|
||||
if (options && options.redirect) {
|
||||
redirect.value = decodeURIComponent(options.redirect);
|
||||
const fromQuery = options && options.redirect ? decodeURIComponent(options.redirect) : "";
|
||||
if (fromQuery && fromQuery !== "/pages/login/index") {
|
||||
redirect.value = fromQuery;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -176,11 +172,11 @@ const handleAccountLogin = () => {
|
||||
.then(() => {
|
||||
toast.success("登录成功");
|
||||
|
||||
console.log("跳转链接", redirect.value);
|
||||
|
||||
// 账号密码登录直接跳转到重定向页面
|
||||
setTimeout(() => {
|
||||
uni.reLaunch({
|
||||
url: redirect.value,
|
||||
});
|
||||
uni.reLaunch({ url: redirect.value });
|
||||
}, 1000);
|
||||
})
|
||||
.catch((error) => {
|
||||
@@ -211,16 +207,13 @@ const handleWechatPhoneLogin = async (e: any) => {
|
||||
if (result.isNewUser || !userStore.isUserInfoComplete()) {
|
||||
// 跳转到完善信息页面
|
||||
setTimeout(() => {
|
||||
uni.navigateTo({
|
||||
url: `/pages/mine/profile/complete-profile?redirect=${encodeURIComponent(redirect.value)}`,
|
||||
});
|
||||
// 跳转完善资料,redirect 保持在缓存中供目标页读取
|
||||
uni.navigateTo({ url: "/pages/mine/profile/complete-profile" });
|
||||
}, 1000);
|
||||
} else {
|
||||
// 跳转到重定向页面
|
||||
setTimeout(() => {
|
||||
uni.reLaunch({
|
||||
url: redirect.value,
|
||||
});
|
||||
uni.reLaunch({ url: redirect.value });
|
||||
}, 1000);
|
||||
}
|
||||
} catch (error: any) {
|
||||
@@ -256,16 +249,12 @@ const handleWechatLogin = async () => {
|
||||
if (result.isNewUser || !userStore.isUserInfoComplete()) {
|
||||
// 如果信息不完整,跳转到完善信息页面
|
||||
setTimeout(() => {
|
||||
uni.navigateTo({
|
||||
url: `/pages/mine/profile/complete-profile?redirect=${encodeURIComponent(redirect.value)}`,
|
||||
});
|
||||
uni.navigateTo({ url: "/pages/mine/profile/complete-profile" });
|
||||
}, 1000);
|
||||
} else {
|
||||
// 否则直接跳转到重定向页面
|
||||
setTimeout(() => {
|
||||
uni.reLaunch({
|
||||
url: redirect.value,
|
||||
});
|
||||
uni.reLaunch({ url: redirect.value });
|
||||
}, 1000);
|
||||
}
|
||||
// #endif
|
||||
|
||||
@@ -1,23 +1,29 @@
|
||||
<template>
|
||||
<view class="app-container">
|
||||
<view class="app-container dark:text-[var(--wot-dark-color)]">
|
||||
<wd-card>
|
||||
<view class="flex items-center p-2">
|
||||
<wd-img width="60px" height="60px" src="/static/logo.png" mode="aspectFit" class="mr-4" />
|
||||
<view class="flex-1">
|
||||
<text class="text-lg font-bold block mb-1">vue-uniapp-template</text>
|
||||
<text class="text-xs text-gray-500">版本 {{ version }}</text>
|
||||
<view class="about-header">
|
||||
<wd-img
|
||||
width="120rpx"
|
||||
height="120rpx"
|
||||
src="/static/logo.png"
|
||||
mode="aspectFit"
|
||||
class="about-logo"
|
||||
/>
|
||||
<view class="about-meta">
|
||||
<text class="app-title">vue-uniapp-template</text>
|
||||
<text class="app-version">版本 {{ version }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</wd-card>
|
||||
|
||||
<!-- 公司信息区域 -->
|
||||
<wd-card custom-style="margin-top: 20rpx">
|
||||
<view class="p-4">
|
||||
<view class="flex items-center mb-3">
|
||||
<wd-icon name="company" size="18" class="mr-2" />
|
||||
<text class="text-base font-medium">有来开源组织</text>
|
||||
<view class="company-section">
|
||||
<view class="section-title">
|
||||
<wd-icon name="company" size="18" />
|
||||
<text>有来开源组织</text>
|
||||
</view>
|
||||
<text class="text-sm text-gray-500 block pl-6">专注于快速构建和高效开发的应用解决方案</text>
|
||||
<text class="section-subtitle">专注于快速构建和高效开发的应用解决方案</text>
|
||||
</view>
|
||||
</wd-card>
|
||||
|
||||
@@ -25,19 +31,19 @@
|
||||
<wd-card title="优质项目" custom-style="margin-top: 20rpx">
|
||||
<wd-cell-group border>
|
||||
<wd-cell title="vue3-element-admin" icon="desktop">
|
||||
<view slot="label" class="text-xs text-gray-500 mt-1">
|
||||
基于 Vue3 + Vite5 + TypeScript5 + Element-Plus + Pinia 构建的中后台管理模板
|
||||
<view slot="label" class="project-desc">
|
||||
基于 Vue3 + Vite7 + TypeScript5 + Element-Plus + Pinia 构建的中后台管理模板
|
||||
</view>
|
||||
</wd-cell>
|
||||
|
||||
<wd-cell title="vue-uniapp-template" icon="mobile">
|
||||
<view slot="label" class="text-xs text-gray-500 mt-1">
|
||||
<view slot="label" class="project-desc">
|
||||
基于 uni-app + Vue 3 + TypeScript,集成多种工具的移动端应用模板
|
||||
</view>
|
||||
</wd-cell>
|
||||
|
||||
<wd-cell title="youlai-boot" icon="server">
|
||||
<view slot="label" class="text-xs text-gray-500 mt-1">
|
||||
<view slot="label" class="project-desc">
|
||||
基于 Spring Boot 3 + Vue 3 构建的前后端分离单体权限管理系统
|
||||
</view>
|
||||
</wd-cell>
|
||||
@@ -54,9 +60,9 @@
|
||||
</wd-card>
|
||||
|
||||
<!-- 底部版权信息 -->
|
||||
<view class="text-center py-4">
|
||||
<text class="block text-xs text-gray-400">Copyright © {{ getYear() }} 有来开源组织</text>
|
||||
<text class="block text-xs text-gray-400 mt-1">All Rights Reserved</text>
|
||||
<view class="about-footer">
|
||||
<text class="copyright">Copyright © {{ getYear() }} 有来开源组织</text>
|
||||
<text class="copyright copyright-sub">All Rights Reserved</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
@@ -75,14 +81,73 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.wd-card__header) {
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
color: var(--wot-color-text, #333);
|
||||
.about-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
:deep(.wd-cell__icon) {
|
||||
font-size: 36rpx;
|
||||
color: var(--wot-color-theme, #409eff);
|
||||
.about-logo {
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.about-meta {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.app-title {
|
||||
display: block;
|
||||
margin-bottom: 8rpx;
|
||||
font-size: 32rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.app-version {
|
||||
font-size: 24rpx;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.company-section {
|
||||
padding: 24rpx;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 12rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
|
||||
:deep(.wd-icon) {
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.section-subtitle {
|
||||
display: block;
|
||||
padding-left: 32rpx;
|
||||
font-size: 24rpx;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.project-desc {
|
||||
margin-top: 8rpx;
|
||||
font-size: 24rpx;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.about-footer {
|
||||
padding: 32rpx 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.copyright {
|
||||
display: block;
|
||||
font-size: 24rpx;
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
.copyright-sub {
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<view class="faq-container">
|
||||
<view class="wechat">
|
||||
<view class="tips">
|
||||
<view class="app-container dark:text-[var(--wot-dark-color)]">
|
||||
<view>
|
||||
<view class="text-center font-600">
|
||||
<text>长按关注「有来技术」公众号,获取交流群二维码。</text>
|
||||
</view>
|
||||
<view class="flex-center">
|
||||
@@ -12,12 +12,12 @@
|
||||
mode="aspectFit"
|
||||
/>
|
||||
</view>
|
||||
<view>
|
||||
<view class="mt-10rpx">
|
||||
<text>如果交流群的二维码过期,请加微信(</text>
|
||||
<text :user-select="true" :selectable="true">haoxianrui</text>
|
||||
<text>)并备注「前端」、「后端」或「全栈」以获取最新二维码。</text>
|
||||
</view>
|
||||
<view>
|
||||
<view class="mt-10rpx">
|
||||
<text>为确保交流群质量,防止营销广告人群混入,我们采取了此措施。望各位理解!</text>
|
||||
</view>
|
||||
</view>
|
||||
@@ -135,20 +135,4 @@ const pagesStr = ref<string>(`<pre style="background-color: #f9f9fa"><code>
|
||||
onMounted(() => {});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.faq-container {
|
||||
min-height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
.wechat {
|
||||
padding: 30rpx;
|
||||
margin: 20px 0;
|
||||
font-size: 14px;
|
||||
color: var(--wot-card-content-color, rgba(0, 0, 0, 0.45));
|
||||
background-color: #fff;
|
||||
.tips {
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped></style>
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
<template>
|
||||
<view class="app-container">
|
||||
<wd-navbar title="意见反馈" left-arrow @click-left="handleBack" />
|
||||
|
||||
123
|
||||
<view class="app-container dark:text-[var(--wot-dark-color)]">
|
||||
<wd-text size="small">选填,最多上传3张图片</wd-text>
|
||||
<wd-form ref="formRef" :model="formData" :rules="rules">
|
||||
<!-- 问题类型选择 -->
|
||||
<wd-form-item label="问题类型" prop="feedbackType">
|
||||
<wd-radio-group v-model="formData.feedbackType" inline>
|
||||
<wd-radio v-for="item in feedbackTypes" :key="item.value" :value="item.value">
|
||||
@@ -41,7 +37,7 @@
|
||||
</wd-form-item>
|
||||
|
||||
<!-- 提交按钮 -->
|
||||
<view class="submit-btn">
|
||||
<view class="mx-30rpx mt-40rpx">
|
||||
<wd-button type="primary" block :loading="submitting" @click="handleSubmit">
|
||||
提交反馈
|
||||
</wd-button>
|
||||
@@ -50,19 +46,23 @@
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<route lang="json">
|
||||
{
|
||||
"name": "feedback",
|
||||
"style": {
|
||||
"navigationBarTitleText": "意见反馈"
|
||||
},
|
||||
"meta": { "requireAuth": true }
|
||||
}
|
||||
</route>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { checkLogin } from "@/utils/auth";
|
||||
import { useToast } from "wot-design-uni";
|
||||
import { FormRules } from "wot-design-uni/components/wd-form/types";
|
||||
|
||||
const toast = useToast();
|
||||
const formRef = ref();
|
||||
|
||||
// 检查登录状态
|
||||
onLoad(() => {
|
||||
if (!checkLogin()) return;
|
||||
});
|
||||
|
||||
// 问题类型选项
|
||||
const feedbackTypes = [
|
||||
{ label: "功能异常", value: "bug" },
|
||||
@@ -138,51 +138,31 @@ const handleDelete = (detail: { index: number }) => {
|
||||
// 提交反馈
|
||||
const handleSubmit = async () => {
|
||||
// 表单验证
|
||||
try {
|
||||
const { valid } = await formRef.value.validate();
|
||||
const { valid } = await formRef.value.validate();
|
||||
|
||||
if (valid) {
|
||||
submitting.value = true;
|
||||
try {
|
||||
// TODO: 调用提交反馈的接口
|
||||
await new Promise((resolve) => setTimeout(resolve, 1500)); // 模拟提交
|
||||
toast.success("提交成功");
|
||||
if (valid) {
|
||||
submitting.value = true;
|
||||
try {
|
||||
// TODO: 调用提交反馈的接口
|
||||
await new Promise((resolve) => setTimeout(resolve, 1500)); // 模拟提交
|
||||
toast.success("提交成功");
|
||||
|
||||
// 重置表单
|
||||
formRef.value.reset();
|
||||
formData.feedbackType = "bug";
|
||||
formData.description = "";
|
||||
formData.fileList = [];
|
||||
formData.contact = "";
|
||||
// 重置表单
|
||||
formRef.value.reset();
|
||||
formData.feedbackType = "bug";
|
||||
formData.description = "";
|
||||
formData.fileList = [];
|
||||
formData.contact = "";
|
||||
|
||||
// 延迟返回上一页
|
||||
setTimeout(() => {
|
||||
uni.navigateBack();
|
||||
}, 1500);
|
||||
} catch (_error) {
|
||||
toast.error("提交失败,请重试");
|
||||
} finally {
|
||||
submitting.value = false;
|
||||
}
|
||||
// 延迟返回上一页
|
||||
setTimeout(() => {
|
||||
uni.navigateBack();
|
||||
}, 1500);
|
||||
} finally {
|
||||
submitting.value = false;
|
||||
}
|
||||
} catch (_error) {
|
||||
// 表单验证失败
|
||||
console.log("表单验证失败");
|
||||
}
|
||||
};
|
||||
|
||||
// 返回
|
||||
const handleBack = () => {
|
||||
uni.navigateBack();
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.wd-form-item) {
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
margin: 40rpx 30rpx;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped></style>
|
||||
|
||||
@@ -13,7 +13,9 @@
|
||||
</view>
|
||||
<view class="user-details">
|
||||
<block v-if="isLogin">
|
||||
<view class="nickname">{{ userInfo!.nickname || "匿名用户" }}</view>
|
||||
<view class="nickname">
|
||||
{{ userInfo!.nickname || "匿名用户" }}
|
||||
</view>
|
||||
<view class="user-id">ID: {{ userInfo?.username || "0000000" }}</view>
|
||||
</block>
|
||||
<block v-else>
|
||||
@@ -41,56 +43,55 @@
|
||||
</view>
|
||||
|
||||
<!-- 数据统计 -->
|
||||
<view class="stats-container">
|
||||
<view class="stat-item" @click="navigateToSection('wallet')">
|
||||
<view class="stat-value">0.00</view>
|
||||
<view class="">我的余额</view>
|
||||
<view class="stats-card">
|
||||
<view class="stats-item" @click="navigateToSection('wallet')">
|
||||
<view class="mb-8rpx text-36rpx font-600">0.00</view>
|
||||
<view class="text-26rpx text-gray-500">我的余额</view>
|
||||
</view>
|
||||
<view class="divider"></view>
|
||||
<view class="stat-item" @click="navigateToSection('favorites')">
|
||||
<view class="stat-value">0</view>
|
||||
<view class="stat-label">我的收藏</view>
|
||||
<view class="stats-divider"></view>
|
||||
<view class="stats-item" @click="navigateToSection('favorites')">
|
||||
<view class="mb-8rpx text-36rpx font-600">0</view>
|
||||
<view class="text-26rpx text-gray-500">我的收藏</view>
|
||||
</view>
|
||||
<view class="divider"></view>
|
||||
<view class="stat-item" @click="navigateToSection('history')">
|
||||
<view class="stat-value">0</view>
|
||||
<view class="stat-label">浏览历史</view>
|
||||
<view class="stats-divider"></view>
|
||||
<view class="stats-item" @click="navigateToSection('history')">
|
||||
<view class="mb-8rpx text-36rpx font-600">0</view>
|
||||
<view class="text-26rpx text-gray-500">浏览历史</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 常用工具 -->
|
||||
<view class="card-container">
|
||||
<view class="card-header">
|
||||
<view class="card-title">
|
||||
<view class="flex-start">
|
||||
<wd-icon name="tools" size="18" :color="currentThemeColor" />
|
||||
<text>常用工具</text>
|
||||
<text class="ml-12rpx text-28rpx font-600">常用工具</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="tools-grid">
|
||||
<view class="flex flex-wrap p-20rpx pt-20rpx pb-10rpx">
|
||||
<view class="tool-item" @click="navigateToProfile">
|
||||
<view class="tool-icon">
|
||||
<wd-icon name="user" size="24" :color="currentThemeColor" />
|
||||
</view>
|
||||
<view class="tool-label">个人资料</view>
|
||||
<view class="text-24rpx">个人资料</view>
|
||||
</view>
|
||||
|
||||
<view class="tool-item" @click="navigateToFAQ">
|
||||
<view class="tool-icon">
|
||||
<wd-icon name="help-circle" size="24" :color="currentThemeColor" />
|
||||
</view>
|
||||
<view class="tool-label">常见问题</view>
|
||||
<view class="text-24rpx">常见问题</view>
|
||||
</view>
|
||||
<view class="tool-item" @click="handleQuestionFeedback">
|
||||
<view class="tool-icon">
|
||||
<wd-icon name="check-circle" size="24" :color="currentThemeColor" />
|
||||
</view>
|
||||
<view class="tool-label">问题反馈</view>
|
||||
<view class="text-24rpx">问题反馈</view>
|
||||
</view>
|
||||
<view class="tool-item" @click="navigateToAbout">
|
||||
<view class="tool-icon">
|
||||
<wd-icon name="info-circle" size="24" :color="currentThemeColor" />
|
||||
</view>
|
||||
<view class="tool-label">关于我们</view>
|
||||
<view class="text-24rpx">关于我们</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -98,44 +99,47 @@
|
||||
<!-- 推荐服务 -->
|
||||
<view class="card-container">
|
||||
<view class="card-header">
|
||||
<view class="card-title">
|
||||
<view class="flex-start">
|
||||
<wd-icon name="star" size="18" :color="currentThemeColor" />
|
||||
<text>推荐服务</text>
|
||||
<text class="ml-12rpx text-28rpx font-600">推荐服务</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="services-list">
|
||||
<view>
|
||||
<view class="service-item" @click="navigateToSection('services', 'vip')">
|
||||
<view class="service-left">
|
||||
<view class="flex-start">
|
||||
<view class="service-icon">
|
||||
<wd-icon name="dong" size="22" :color="currentThemeColor" />
|
||||
</view>
|
||||
<view class="service-info">
|
||||
<view class="service-name">会员中心</view>
|
||||
<view class="service-desc">解锁更多特权</view>
|
||||
<view>
|
||||
<view class="text-28rpx font-500">会员中心</view>
|
||||
<view class="mt-8rpx text-24rpx text-gray-500">解锁更多特权</view>
|
||||
</view>
|
||||
</view>
|
||||
<wd-icon name="arrow-right" size="14" />
|
||||
</view>
|
||||
<view class="service-item" @click="navigateToSection('services', 'coupon')">
|
||||
<view class="service-left">
|
||||
<view class="flex-start">
|
||||
<view class="service-icon">
|
||||
<wd-icon name="discount" size="22" :color="currentThemeColor" />
|
||||
</view>
|
||||
<view class="service-info">
|
||||
<view class="service-name">优惠券</view>
|
||||
<view class="service-desc">查看我的优惠券</view>
|
||||
<view>
|
||||
<view class="text-28rpx font-500">优惠券</view>
|
||||
<view class="mt-8rpx text-24rpx text-gray-500">查看我的优惠券</view>
|
||||
</view>
|
||||
</view>
|
||||
<wd-icon name="arrow-right" size="14" />
|
||||
</view>
|
||||
<view class="service-item" @click="navigateToSection('services', 'invite')">
|
||||
<view class="service-left">
|
||||
<view
|
||||
class="service-item service-item-last"
|
||||
@click="navigateToSection('services', 'invite')"
|
||||
>
|
||||
<view class="flex-start">
|
||||
<view class="service-icon">
|
||||
<wd-icon name="share" size="22" :color="currentThemeColor" />
|
||||
</view>
|
||||
<view class="service-info">
|
||||
<view class="service-name">邀请有礼</view>
|
||||
<view class="service-desc">邀请好友得奖励</view>
|
||||
<view>
|
||||
<view class="text-28rpx font-500">邀请有礼</view>
|
||||
<view class="mt-8rpx text-24rpx text-gray-500">邀请好友得奖励</view>
|
||||
</view>
|
||||
</view>
|
||||
<wd-icon name="arrow-right" size="14" />
|
||||
@@ -144,14 +148,8 @@
|
||||
</view>
|
||||
|
||||
<!-- 退出登录按钮 -->
|
||||
<view v-if="isLogin" class="logout-btn-wrap">
|
||||
<wd-button
|
||||
class="w-full h-80rpx rounded-40rpx font-bold text-32rpx"
|
||||
plain
|
||||
@click="handleLogout"
|
||||
>
|
||||
退出登录
|
||||
</wd-button>
|
||||
<view v-if="isLogin" class="p-30rpx">
|
||||
<wd-button custom-class="logout-button" plain @click="handleLogout">退出登录</wd-button>
|
||||
</view>
|
||||
|
||||
<wd-toast />
|
||||
@@ -161,6 +159,7 @@
|
||||
<script lang="ts" setup>
|
||||
import { useToast } from "wot-design-uni";
|
||||
import { useUserStore, useThemeStore } from "@/store";
|
||||
import { useRouter } from "uni-mini-router";
|
||||
|
||||
const toast = useToast();
|
||||
const userStore = useUserStore();
|
||||
@@ -170,15 +169,14 @@ const userInfo = computed(() => userStore.userInfo);
|
||||
const isLogin = computed(() => !!userInfo.value);
|
||||
const defaultAvatar = "/static/images/default-avatar.png";
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
// 登录
|
||||
const navigateToLoginPage = () => {
|
||||
const pages = getCurrentPages();
|
||||
const currentPage = pages[pages.length - 1];
|
||||
const currentPagePath = `/${currentPage.route}`;
|
||||
|
||||
uni.navigateTo({
|
||||
url: `/pages/login/index?redirect=${encodeURIComponent(currentPagePath)}`,
|
||||
});
|
||||
router.push({ path: "/pages/login/index", query: { redirect: currentPagePath } });
|
||||
};
|
||||
|
||||
// 退出登录
|
||||
@@ -201,27 +199,27 @@ const navigateToProfile = () => {
|
||||
navigateToLoginPage();
|
||||
return;
|
||||
}
|
||||
uni.navigateTo({ url: "/pages/mine/profile/index" });
|
||||
router.push({ path: "/pages/mine/profile/index" });
|
||||
};
|
||||
|
||||
// 常见问题
|
||||
const navigateToFAQ = () => {
|
||||
uni.navigateTo({ url: "/pages/mine/faq/index" });
|
||||
router.push({ path: "/pages/mine/faq/index" });
|
||||
};
|
||||
|
||||
// 关于我们
|
||||
const navigateToAbout = () => {
|
||||
uni.navigateTo({ url: "/pages/mine/about/index" });
|
||||
router.push({ path: "/pages/mine/about/index" });
|
||||
};
|
||||
|
||||
// 设置
|
||||
const navigateToSettings = () => {
|
||||
uni.navigateTo({ url: "/pages/mine/settings/index" });
|
||||
router.push({ path: "/pages/mine/settings/index" });
|
||||
};
|
||||
|
||||
// 问题反馈
|
||||
const handleQuestionFeedback = () => {
|
||||
uni.navigateTo({ url: "/pages/mine/feedback/index" });
|
||||
router.push({ path: "/pages/mine/feedback/index" });
|
||||
};
|
||||
|
||||
// 导航到各个板块
|
||||
@@ -233,18 +231,6 @@ const navigateToSection = (section: string, subSection?: string) => {
|
||||
icon: "none",
|
||||
});
|
||||
};
|
||||
|
||||
onShow(() => {
|
||||
// 确保 tabbar 状态正确
|
||||
const pages = getCurrentPages();
|
||||
if (pages.length > 0) {
|
||||
const currentPage = pages[pages.length - 1];
|
||||
if (currentPage.route === "pages/mine/index") {
|
||||
// 通过事件通知 tabbar 布局更新状态
|
||||
uni.$emit("updateTabbar", "mine");
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<route lang="json">
|
||||
@@ -269,7 +255,7 @@ onShow(() => {
|
||||
left: 0;
|
||||
z-index: 0;
|
||||
height: 240rpx;
|
||||
background: linear-gradient(to bottom, var(--wot-color-theme), var(--primary-color-light));
|
||||
background: linear-gradient(135deg, var(--wot-color-theme, #165dff) 0%, #667eea 100%);
|
||||
}
|
||||
|
||||
.user-info {
|
||||
@@ -349,213 +335,152 @@ onShow(() => {
|
||||
}
|
||||
}
|
||||
|
||||
// 数据统计
|
||||
.stats-container {
|
||||
display: flex;
|
||||
padding: 30rpx 20rpx;
|
||||
margin: 20rpx 30rpx;
|
||||
background: var(--wot-color-bg-container);
|
||||
border-radius: 16rpx;
|
||||
box-shadow: var(--wot-card-shadow);
|
||||
|
||||
.stat-item {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
.stat-value {
|
||||
margin-bottom: 8rpx;
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: var(--wot-color-text);
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 26rpx;
|
||||
color: var(--wot-color-text-secondary);
|
||||
}
|
||||
}
|
||||
|
||||
.divider {
|
||||
width: 1px;
|
||||
margin: 0 20rpx;
|
||||
background-color: var(--wot-color-border);
|
||||
}
|
||||
}
|
||||
|
||||
// 卡片容器通用样式
|
||||
// 卡片容器
|
||||
.card-container {
|
||||
margin: 24rpx 30rpx;
|
||||
overflow: hidden;
|
||||
background: var(--wot-color-bg-container);
|
||||
background-color: #fff;
|
||||
border-radius: 16rpx;
|
||||
box-shadow: var(--wot-card-shadow);
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 20rpx 24rpx;
|
||||
border-bottom: 1rpx solid var(--wot-color-border);
|
||||
|
||||
.card-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
text {
|
||||
margin-left: 12rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: var(--wot-color-text);
|
||||
}
|
||||
}
|
||||
|
||||
.card-action {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
text {
|
||||
margin-right: 8rpx;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
box-shadow: 0 1rpx 3rpx rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
// 订单状态
|
||||
.order-status {
|
||||
// 卡片头部
|
||||
.card-header {
|
||||
display: flex;
|
||||
padding: 30rpx 0 20rpx;
|
||||
|
||||
.status-item {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
.status-icon {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
margin-bottom: 12rpx;
|
||||
|
||||
.status-badge {
|
||||
position: absolute;
|
||||
top: -10rpx;
|
||||
right: -10rpx;
|
||||
z-index: 2;
|
||||
min-width: 32rpx;
|
||||
height: 32rpx;
|
||||
padding: 0 6rpx;
|
||||
font-size: 20rpx;
|
||||
line-height: 32rpx;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
background-color: #ff4d4f;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.status-label {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 20rpx 24rpx;
|
||||
border-bottom: 1rpx solid #e5e7eb;
|
||||
}
|
||||
|
||||
// 工具网格
|
||||
.tools-grid {
|
||||
// 暗色模式下的卡片样式
|
||||
:deep(.dark) .card-container {
|
||||
background-color: #1f2937;
|
||||
}
|
||||
|
||||
:deep(.dark) .card-header {
|
||||
border-bottom-color: #374151;
|
||||
}
|
||||
|
||||
// 工具项
|
||||
.tool-item {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: 20rpx 0 10rpx;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 25%;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.tool-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 25%;
|
||||
margin-bottom: 30rpx;
|
||||
// 工具图标
|
||||
.tool-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 90rpx;
|
||||
height: 90rpx;
|
||||
margin-bottom: 12rpx;
|
||||
background-color: #f3f4f6;
|
||||
border-radius: 18rpx;
|
||||
transition: transform 0.15s ease;
|
||||
|
||||
.tool-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 90rpx;
|
||||
height: 90rpx;
|
||||
margin-bottom: 12rpx;
|
||||
background-color: var(--wot-color-bg-light);
|
||||
border-radius: 18rpx;
|
||||
transition: transform 0.2s;
|
||||
|
||||
&:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
}
|
||||
|
||||
.tool-label {
|
||||
font-size: 24rpx;
|
||||
color: var(--wot-color-text);
|
||||
}
|
||||
&:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
}
|
||||
|
||||
// 服务列表
|
||||
.services-list {
|
||||
.service-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 24rpx;
|
||||
border-bottom: 1rpx solid var(--wot-color-border);
|
||||
transition: background-color 0.2s;
|
||||
|
||||
&:active {
|
||||
background-color: var(--wot-color-bg-light);
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.service-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.service-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
margin-right: 20rpx;
|
||||
background-color: var(--wot-color-bg-light);
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
|
||||
.service-info {
|
||||
.service-name {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: var(--wot-color-text);
|
||||
}
|
||||
|
||||
.service-desc {
|
||||
margin-top: 8rpx;
|
||||
font-size: 24rpx;
|
||||
color: var(--wot-color-text-secondary);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 暗色模式下的工具图标样式
|
||||
:deep(.dark) .tool-icon {
|
||||
background-color: #374151;
|
||||
}
|
||||
|
||||
// 退出登录按钮
|
||||
.logout-btn-wrap {
|
||||
// 数据统计卡片(减少原子类堆叠)
|
||||
.stats-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 30rpx;
|
||||
margin: 20rpx 30rpx;
|
||||
background-color: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
box-shadow: 0 1rpx 3rpx rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
:deep(.dark) .stats-card {
|
||||
background-color: #1f2937;
|
||||
}
|
||||
|
||||
.stats-item {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.stats-divider {
|
||||
width: 1px;
|
||||
margin: 0 20rpx;
|
||||
background-color: #e5e7eb;
|
||||
}
|
||||
|
||||
:deep(.dark) .stats-divider {
|
||||
background-color: #374151;
|
||||
}
|
||||
|
||||
// 服务项
|
||||
.service-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 24rpx;
|
||||
border-bottom: 1rpx solid #e5e7eb;
|
||||
transition: background-color 0.15s ease;
|
||||
|
||||
&:active {
|
||||
background-color: #f3f4f6;
|
||||
}
|
||||
|
||||
&.service-item-last {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
// 暗色模式下的服务项样式
|
||||
:deep(.dark) .service-item {
|
||||
border-bottom-color: #374151;
|
||||
|
||||
&:active {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
// 服务图标
|
||||
.service-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
margin-right: 20rpx;
|
||||
background-color: #f3f4f6;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
|
||||
// 暗色模式下的服务图标样式
|
||||
:deep(.dark) .service-icon {
|
||||
background-color: #374151;
|
||||
}
|
||||
|
||||
// 登录按钮样式
|
||||
:deep(.btn-login) {
|
||||
font-size: 24rpx !important;
|
||||
border-radius: 20rpx !important;
|
||||
}
|
||||
|
||||
// 退出登录按钮样式
|
||||
:deep(.logout-button) {
|
||||
width: 100% !important;
|
||||
height: 80rpx !important;
|
||||
font-size: 32rpx !important;
|
||||
font-weight: bold !important;
|
||||
border-radius: 40rpx !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
<template>
|
||||
<view class="app-container">
|
||||
<wd-navbar title="完善个人信息" left-arrow @click-left="handleBack" />
|
||||
|
||||
<view class="app-container dark:text-[var(--wot-dark-color)]">
|
||||
<!-- 头部标题 -->
|
||||
<view class="header">
|
||||
<view class="title">完善个人信息</view>
|
||||
@@ -164,7 +162,7 @@ import FileAPI, { type FileInfo } from "@/api/file";
|
||||
const toast = useToast();
|
||||
const userStore = useUserStore();
|
||||
|
||||
// 页面参数
|
||||
// 页面参数:使用 query 读取重定向
|
||||
const redirect = ref("/pages/index/index");
|
||||
|
||||
// 表单数据
|
||||
@@ -356,9 +354,7 @@ const handleComplete = async () => {
|
||||
|
||||
// 跳转到目标页面
|
||||
setTimeout(() => {
|
||||
uni.reLaunch({
|
||||
url: redirect.value,
|
||||
});
|
||||
uni.reLaunch({ url: redirect.value });
|
||||
}, 1000);
|
||||
} catch (error: any) {
|
||||
console.error("完善信息失败:", error);
|
||||
@@ -375,18 +371,11 @@ const handleSkip = () => {
|
||||
content: "跳过信息完善可能会影响部分功能使用,确定要跳过吗?",
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
uni.reLaunch({
|
||||
url: redirect.value,
|
||||
});
|
||||
uni.reLaunch({ url: redirect.value });
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// 返回
|
||||
function handleBack() {
|
||||
uni.navigateBack();
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
<template>
|
||||
<view class="app-container">
|
||||
<wd-navbar title="个人信息" left-arrow @click-left="handleBack" />
|
||||
|
||||
<view class="app-container dark:text-[var(--wot-dark-color)]">
|
||||
<wd-card v-if="userProfile" custom-style="margin-top: 20rpx">
|
||||
<wd-cell-group border>
|
||||
<wd-cell class="avatar-cell" title="头像" center is-link>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<view class="app-container">
|
||||
<view class="app-container dark:text-[var(--wot-dark-color)]">
|
||||
<wd-card>
|
||||
<wd-cell-group border>
|
||||
<wd-cell
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<view class="app-container">
|
||||
<view class="app-container dark:text-[var(--wot-dark-color)]">
|
||||
<wd-collapse v-model="activeNames" accordion>
|
||||
<wd-collapse-item
|
||||
v-for="(section, index) in agreementContent"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<view class="app-container dark-mode">
|
||||
<view class="app-container dark:text-[var(--wot-dark-color)]">
|
||||
<wd-cell-group>
|
||||
<wd-cell v-if="isLogin" title="个人资料" icon="user" is-link @click="navigateToProfile" />
|
||||
<wd-cell
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<view class="app-container">
|
||||
<view class="app-container dark:text-[var(--wot-dark-color)]">
|
||||
<!-- 网络状态展示 -->
|
||||
<wd-card>
|
||||
<wd-cell-group border>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<view class="app-container">
|
||||
<view class="app-container dark:text-[var(--wot-dark-color)]">
|
||||
<wd-status-tip type="search" tip="建设中..." />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -6,7 +6,9 @@ import { createRouter } from "uni-mini-router";
|
||||
function generateRoutes() {
|
||||
const routes = pages.map((page: { path: string; [key: string]: any }) => {
|
||||
const newPath = `/${page.path}`;
|
||||
return { ...page, path: newPath };
|
||||
// 透传 meta 字段(如果 pages.json 中定义了)
|
||||
const meta = page.meta ?? undefined;
|
||||
return { ...page, path: newPath, meta };
|
||||
});
|
||||
|
||||
// 处理分包路由
|
||||
@@ -14,7 +16,8 @@ function generateRoutes() {
|
||||
subPackages.forEach((subPackage: { root: string; pages: any[] }) => {
|
||||
const subRoutes = subPackage.pages.map((page: any) => {
|
||||
const newPath = `/${subPackage.root}/${page.path}`;
|
||||
return { ...page, path: newPath };
|
||||
const meta = page.meta ?? undefined;
|
||||
return { ...page, path: newPath, meta };
|
||||
});
|
||||
routes.push(...subRoutes);
|
||||
});
|
||||
@@ -29,8 +32,10 @@ const router = createRouter({
|
||||
|
||||
// 全局前置守卫
|
||||
router.beforeEach((to, from, next) => {
|
||||
// 检查页面是否需要登录
|
||||
if (to.meta && to.meta.requireAuth && !isLoggedIn()) {
|
||||
const redirectPath = (to.path || "/pages/index/index") as string;
|
||||
// 先取消本次导航,避免在异步对话框中遗漏 next 导致报错
|
||||
next(false);
|
||||
uni.showModal({
|
||||
title: "提示",
|
||||
content: "该功能需要登录后使用",
|
||||
@@ -38,32 +43,20 @@ router.beforeEach((to, from, next) => {
|
||||
cancelText: "返回",
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
// 记住原来要去的页面
|
||||
uni.setStorageSync("redirect", to.fullPath);
|
||||
// 使用 uni 原生导航而不是 router
|
||||
uni.navigateTo({
|
||||
url: "/pages/login/index",
|
||||
});
|
||||
} else {
|
||||
// 取消则返回首页
|
||||
uni.switchTab({
|
||||
url: "/pages/index/index",
|
||||
router.push({
|
||||
path: "/pages/login/index",
|
||||
query: { redirect: encodeURIComponent(redirectPath) },
|
||||
});
|
||||
}
|
||||
},
|
||||
// 确保在取消弹窗时也能调用 next
|
||||
fail: () => {
|
||||
next(false);
|
||||
},
|
||||
});
|
||||
} else {
|
||||
// 继续导航
|
||||
next();
|
||||
}
|
||||
});
|
||||
|
||||
router.afterEach((to) => {
|
||||
console.log("路由跳转完成:", to.path);
|
||||
router.afterEach((to, from) => {
|
||||
console.log("🎯 afterEach 钩子触发:", { to, from });
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
||||
@@ -7,5 +7,5 @@ body,
|
||||
}
|
||||
|
||||
.app-container {
|
||||
padding: 0 10rpx;
|
||||
padding: 10rpx;
|
||||
}
|
||||
|
||||
21
src/types/uni-mini-router.d.ts
vendored
Normal file
21
src/types/uni-mini-router.d.ts
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
// 扩展 uni-mini-router 路由类型,支持从 pages.json 注入的 meta 字段
|
||||
// 该声明文件会被 ts 自动合并到库类型中
|
||||
|
||||
import "uni-mini-router";
|
||||
|
||||
declare module "uni-mini-router" {
|
||||
/** 自定义路由元信息类型,可按需扩展 */
|
||||
interface RouteMeta {
|
||||
/** 是否需要登录 */
|
||||
requireAuth?: boolean;
|
||||
/** 其他自定义字段 */
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
interface Route {
|
||||
/** 来自 pages.json 的 meta 字段(动态生成时透传) */
|
||||
meta?: RouteMeta;
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
||||
Reference in New Issue
Block a user