feat: 全面升级 Wot UI v2 并修复暗黑模式适配
- 修复 TabBar 切换激活状态不同步问题 - 修复大量 Wot UI 2.x 图标名称不匹配 - 修复暗黑模式下主题变量错误注入导致亮色模式显示异常 - 优化个人中心 UI(头像标记、图标背景色、菜单结构) - 重构首页图表时间选择器为自定义分段选择器 - 修复登录弹窗文字丢失及验证顺序 - 修复设置页面布局、退出登录按钮及 cell 点击态
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "youlai-app",
|
||||
"version": "0.1.2",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"dev:app": "uni -p app",
|
||||
"dev:app-android": "uni -p app-android",
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
:model-value="isChecked(item.value)"
|
||||
:indeterminate="isIndeterminate(item.value)"
|
||||
:disabled="item.disabled"
|
||||
shape="square"
|
||||
type="square"
|
||||
/>
|
||||
</view>
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ export interface TabbarItem {
|
||||
/** 默认 TabBar 配置 */
|
||||
const DEFAULT_TABBAR_ITEMS: Omit<TabbarItem, "active">[] = [
|
||||
{ name: "home", value: null, title: "首页", icon: "home" },
|
||||
{ name: "work", value: null, title: "工作台", icon: "laptop" },
|
||||
{ name: "work", value: null, title: "工作台", icon: "apps" },
|
||||
{ name: "mine", value: null, title: "我的", icon: "user" },
|
||||
];
|
||||
|
||||
|
||||
@@ -60,23 +60,12 @@ export const menuConfig: WorkMenuGroup[] = [
|
||||
url: "/pages/work/notice/index",
|
||||
perm: "sys:notice:list",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "系统监控",
|
||||
children: [
|
||||
{
|
||||
icon: "/static/icons/log.svg",
|
||||
title: "系统日志",
|
||||
url: "/pages/work/log/index",
|
||||
perm: "sys:log:list",
|
||||
},
|
||||
{
|
||||
icon: "/static/icons/dashboard.svg",
|
||||
title: "应用监控",
|
||||
url: "https://www.baidu.com",
|
||||
perm: "",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -17,7 +17,7 @@ export default {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<wd-config-provider :theme-vars="themeVars" :custom-class="theme" :theme="theme">
|
||||
<wd-config-provider :theme-vars="themeVars" :custom-class="theme === 'dark' ? 'dark' : ''" :theme="theme === 'dark' ? 'dark' : ''">
|
||||
<slot />
|
||||
<wd-notify />
|
||||
<wd-toast />
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
<template>
|
||||
<wd-config-provider :theme-vars="themeVars" :custom-class="theme" :theme="theme">
|
||||
<slot />
|
||||
<wd-tabbar
|
||||
:model-value="activeTabbar.name"
|
||||
bordered
|
||||
safe-area-inset-bottom
|
||||
fixed
|
||||
@change="handleTabbarChange"
|
||||
>
|
||||
<view :class="`wot-theme-${theme}`" :style="themeCSSVars">
|
||||
<wd-config-provider :theme-vars="themeVars" :theme="theme === 'dark' ? 'dark' : ''">
|
||||
<slot />
|
||||
<wd-tabbar
|
||||
:model-value="activeTabbar.name"
|
||||
bordered
|
||||
safe-area-inset-bottom
|
||||
fixed
|
||||
@change="handleTabbarChange"
|
||||
>
|
||||
<wd-tabbar-item
|
||||
v-for="(item, index) in tabbarList"
|
||||
:key="index"
|
||||
@@ -17,41 +18,63 @@
|
||||
:icon="item.icon"
|
||||
/>
|
||||
</wd-tabbar>
|
||||
<wd-notify />
|
||||
<wd-toast />
|
||||
<wd-dialog />
|
||||
</wd-config-provider>
|
||||
<wd-notify />
|
||||
<wd-toast />
|
||||
<wd-dialog />
|
||||
</wd-config-provider>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useThemeStore } from "@/store";
|
||||
import { useTabbar } from "@/composables/useTabbar";
|
||||
import { useRouter, useRoute } from "uni-mini-router";
|
||||
import { useRouter } from "uni-mini-router";
|
||||
import { storeToRefs } from "pinia";
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const themeStore = useThemeStore();
|
||||
const { themeVars, theme } = storeToRefs(themeStore);
|
||||
const { themeVars, theme, themeCSSVars } = storeToRefs(themeStore);
|
||||
const { activeTabbar, getTabbarItemValue, setTabbarItemActive, tabbarList } = useTabbar();
|
||||
|
||||
/** 页面路径 -> tabbar name 映射(兼容带/不带前缀) */
|
||||
const ROUTE_TO_TABBAR_NAME: Record<string, string> = {
|
||||
"/pages/index/index": "home",
|
||||
"/pages/work/index": "work",
|
||||
"/pages/mine/index": "mine",
|
||||
"pages/index/index": "home",
|
||||
"pages/work/index": "work",
|
||||
"pages/mine/index": "mine",
|
||||
};
|
||||
|
||||
function handleTabbarChange({ value }: { value: string }) {
|
||||
setTabbarItemActive(value);
|
||||
router.pushTab({ name: value });
|
||||
}
|
||||
|
||||
// 监听路由变化更新 tabbar 激活状态
|
||||
watch(
|
||||
() => route.name,
|
||||
(name) => {
|
||||
if (name && name !== activeTabbar.value.name) {
|
||||
setTabbarItemActive(name);
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
/** 根据当前页面路径同步 tabbar 激活状态 */
|
||||
function syncTabbarActive() {
|
||||
const pages = getCurrentPages();
|
||||
const currentPage = pages[pages.length - 1];
|
||||
const currentRoute = (currentPage?.route as string) || "";
|
||||
|
||||
if (!currentRoute) return;
|
||||
|
||||
const tabName = ROUTE_TO_TABBAR_NAME[currentRoute];
|
||||
if (tabName && tabName !== activeTabbar.value.name) {
|
||||
setTabbarItemActive(tabName);
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
syncTabbarActive();
|
||||
|
||||
// 小程序 switchTab 时 layout 组件被复用,onMounted 不会再次触发
|
||||
// 使用定时轮询兜底,确保 tabbar 激活状态始终与当前页面一致
|
||||
// #ifdef MP-WEIXIN || MP-ALIPAY || MP-BAIDU || MP-TOUTIAO || MP-QQ || MP-KUAISHOU || MP-LARK || MP-XHS || MP-JD
|
||||
const timer = setInterval(syncTabbarActive, 300);
|
||||
onUnmounted(() => clearInterval(timer));
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
uni.hideTabBar();
|
||||
// #endif
|
||||
@@ -62,7 +85,7 @@ onMounted(() => {
|
||||
export default {
|
||||
options: {
|
||||
addGlobalClass: true,
|
||||
virtualHost: true,
|
||||
virtualHost: false,
|
||||
styleIsolation: "shared",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
"animationDuration": 300
|
||||
},
|
||||
"pages": [
|
||||
// GENERATED BY UNI-PAGES, PLATFORM: MP-WEIXIN
|
||||
{
|
||||
"path": "pages/index/index",
|
||||
"type": "home",
|
||||
@@ -223,7 +222,6 @@
|
||||
"backgroundColor": "@tabBgColor",
|
||||
"borderStyle": "@tabBorderStyle",
|
||||
"list": [
|
||||
// GENERATED BY UNI-PAGES, PLATFORM: MP-WEIXIN
|
||||
{
|
||||
"pagePath": "pages/index/index"
|
||||
},
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
v-for="(item, index) in quickNavList"
|
||||
:key="index"
|
||||
use-slot
|
||||
@itemclick="handleNavClickWithGuard(item)"
|
||||
@click="handleNavClickWithGuard(item)"
|
||||
>
|
||||
<view class="nav-item">
|
||||
<image class="nav-item__icon" :src="item.icon" mode="aspectFit" />
|
||||
@@ -27,7 +27,7 @@
|
||||
<view class="m-24rpx">
|
||||
<view class="notice-bar" @click="handleNoticeClick">
|
||||
<view class="notice-bar__icon">
|
||||
<wd-icon name="check-outline" size="32rpx" color="var(--color-success)" />
|
||||
<wd-icon name="check" size="32rpx" color="var(--color-success)" />
|
||||
</view>
|
||||
<view class="notice-bar__content">
|
||||
<text class="notice-bar__text">{{ noticeText || "暂无通知公告" }}</text>
|
||||
@@ -62,20 +62,27 @@
|
||||
</view>
|
||||
|
||||
<!-- 访问趋势图表 -->
|
||||
<view class="mt-24rpx">
|
||||
<view class="m-24rpx">
|
||||
<wd-card custom-class="chart-card">
|
||||
<template #title>
|
||||
<view class="flex-between">
|
||||
<text class="text-28rpx font-semibold">访问趋势</text>
|
||||
<wd-radio-group
|
||||
v-model="recentDaysRange"
|
||||
shape="button"
|
||||
inline
|
||||
@change="handleDataRangeChange"
|
||||
>
|
||||
<wd-radio :value="7">近7天</wd-radio>
|
||||
<wd-radio :value="15">近15天</wd-radio>
|
||||
</wd-radio-group>
|
||||
<view class="chart-header">
|
||||
<text class="chart-header__title">访问趋势</text>
|
||||
<view class="segment-control">
|
||||
<view
|
||||
class="segment-control__item"
|
||||
:class="{ 'is-active': recentDaysRange === 7 }"
|
||||
@click="switchRange(7)"
|
||||
>
|
||||
近7天
|
||||
</view>
|
||||
<view
|
||||
class="segment-control__item"
|
||||
:class="{ 'is-active': recentDaysRange === 15 }"
|
||||
@click="switchRange(15)"
|
||||
>
|
||||
近15天
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -238,8 +245,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: "访客数", data: data.uvList || [] },
|
||||
{ name: "浏览量", data: data.pvList || [] },
|
||||
],
|
||||
})
|
||||
);
|
||||
@@ -260,7 +267,8 @@ function handleNoticeClick() {
|
||||
router.push({ path: "/pages/work/notice/index" });
|
||||
}
|
||||
|
||||
function handleDataRangeChange({ value }: { value: number }) {
|
||||
function switchRange(value: number) {
|
||||
if (recentDaysRange.value === value) return;
|
||||
recentDaysRange.value = value;
|
||||
loadVisitTrendData();
|
||||
}
|
||||
@@ -309,6 +317,42 @@ onShow(() => {
|
||||
height: 420rpx;
|
||||
}
|
||||
|
||||
.chart-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 24rpx;
|
||||
}
|
||||
|
||||
.chart-header__title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.segment-control {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 4rpx;
|
||||
background: var(--color-bg-tertiary);
|
||||
border: 1rpx solid var(--color-border);
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
.segment-control__item {
|
||||
padding: 8rpx 20rpx;
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
color: var(--color-text-secondary);
|
||||
border-radius: 8rpx;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.segment-control__item.is-active {
|
||||
color: var(--color-text-inverse);
|
||||
background: var(--color-primary);
|
||||
}
|
||||
|
||||
.section--overlay {
|
||||
position: relative;
|
||||
z-index: var(--z-sticky);
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
<view v-if="loginMode !== 'WECHAT'" class="login__form">
|
||||
<!-- 用户名/手机号 -->
|
||||
<view class="login__form-item login__field">
|
||||
<wd-icon name="person" size="20" color="var(--color-text-placeholder)" />
|
||||
<wd-icon name="user" size="20" color="var(--color-text-placeholder)" />
|
||||
<input
|
||||
v-model="formData.username"
|
||||
class="login__field-input"
|
||||
@@ -61,7 +61,7 @@
|
||||
|
||||
<!-- 图形验证码(密码登录时显示) -->
|
||||
<view v-if="loginMode === 'PASSWORD'" class="login__form-item login__field">
|
||||
<wd-icon name="shield" size="20" color="var(--color-text-placeholder)" />
|
||||
<wd-icon name="code-square" size="20" color="var(--color-text-placeholder)" />
|
||||
<input
|
||||
v-model="formData.captchaCode"
|
||||
class="login__field-input"
|
||||
@@ -80,7 +80,7 @@
|
||||
|
||||
<!-- 短信验证码 -->
|
||||
<view v-else class="login__form-item login__field">
|
||||
<wd-icon name="shield" size="20" color="var(--color-text-placeholder)" />
|
||||
<wd-icon name="lock" size="20" color="var(--color-text-placeholder)" />
|
||||
<input
|
||||
v-model="formData.code"
|
||||
class="login__field-input"
|
||||
@@ -163,7 +163,7 @@
|
||||
|
||||
<!-- 协议勾选 -->
|
||||
<view class="login__policy">
|
||||
<wd-checkbox v-model="isAgreePolicy" shape="square" size="32rpx">
|
||||
<wd-checkbox v-model="isAgreePolicy" type="square" size="32rpx">
|
||||
<text class="login__policy-text">
|
||||
我已阅读并同意
|
||||
<text class="login__policy-link" @click.stop="navigateToAgreement('user')">
|
||||
@@ -204,7 +204,7 @@
|
||||
</view>
|
||||
|
||||
<view class="login__field">
|
||||
<wd-icon name="shield" size="20" color="var(--color-text-placeholder)" />
|
||||
<wd-icon name="lock" size="20" color="var(--color-text-placeholder)" />
|
||||
<input
|
||||
v-model="bindMobileForm.code"
|
||||
class="login__field-input"
|
||||
@@ -236,17 +236,7 @@
|
||||
</wd-popup>
|
||||
|
||||
<!-- 协议确认弹窗 -->
|
||||
<wd-dialog selector="policy-box" root-portal>
|
||||
<view class="policy-dialog__content">
|
||||
请阅读并同意有来技术
|
||||
<text class="policy-dialog__link" @click.stop="navigateToAgreement('user')">
|
||||
《用户协议》
|
||||
</text>
|
||||
<text class="policy-dialog__link" @click.stop="navigateToAgreement('privacy')">
|
||||
《隐私政策》
|
||||
</text>
|
||||
</view>
|
||||
</wd-dialog>
|
||||
<wd-dialog selector="policy-box" root-portal />
|
||||
|
||||
<wd-toast />
|
||||
</view>
|
||||
@@ -367,8 +357,11 @@ const toggleLoginMode = () => {
|
||||
const openPolicyDialog = (action: "FORM" | "WECHAT_PHONE", phoneCode = "") => {
|
||||
pendingLoginAction.value = action;
|
||||
pendingWechatPhoneCode.value = phoneCode;
|
||||
message
|
||||
.confirm({ title: "提示" })
|
||||
dialog
|
||||
.confirm({
|
||||
title: "提示",
|
||||
msg: "请阅读并同意《用户协议》与《隐私政策》",
|
||||
})
|
||||
.then(async () => {
|
||||
isAgreePolicy.value = true;
|
||||
const act = pendingLoginAction.value;
|
||||
@@ -386,12 +379,7 @@ const openPolicyDialog = (action: "FORM" | "WECHAT_PHONE", phoneCode = "") => {
|
||||
|
||||
// 表单登录
|
||||
async function doFormLogin() {
|
||||
if (!canSubmit.value) {
|
||||
toast.error(
|
||||
loginMode.value === "PASSWORD" ? "请输入用户名和密码" : "请输入正确的手机号和验证码"
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (isLoading.value) return;
|
||||
if (isLoading.value) return;
|
||||
isLoading.value = true;
|
||||
try {
|
||||
@@ -420,6 +408,14 @@ async function doFormLogin() {
|
||||
}
|
||||
|
||||
const handleLogin = async () => {
|
||||
// 先校验表单必填项
|
||||
if (!canSubmit.value) {
|
||||
toast.error(
|
||||
loginMode.value === "PASSWORD" ? "请输入用户名和密码" : "请输入正确的手机号和验证码"
|
||||
);
|
||||
return;
|
||||
}
|
||||
// 再校验隐私协议
|
||||
if (!isAgreePolicy.value) {
|
||||
openPolicyDialog("FORM");
|
||||
return;
|
||||
@@ -893,17 +889,6 @@ onShow(() => uni.setNavigationBarTitle({ title: "" }));
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.policy-dialog__content {
|
||||
font-size: 26rpx;
|
||||
line-height: 1.625;
|
||||
color: var(--color-text-secondary);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.policy-dialog__link {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.login__bind-panel {
|
||||
padding: 48rpx;
|
||||
}
|
||||
|
||||
@@ -27,9 +27,6 @@
|
||||
lazy-load
|
||||
/>
|
||||
<view v-if="isLogin" class="profile-card__online-dot" />
|
||||
<view v-if="genderIconName" class="profile-card__gender" :class="genderIconClass">
|
||||
<wd-icon :name="genderIconName" size="12" color="var(--color-text-inverse)" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="profile-card__main" @click="isLogin ? openProfile() : navigateToLogin()">
|
||||
@@ -46,7 +43,7 @@
|
||||
</view>
|
||||
<view class="profile-tag">
|
||||
<wd-icon
|
||||
:name="isLogin && deptNameText ? 'home' : 'secured'"
|
||||
:name="isLogin && deptNameText ? 'home' : 'safe'"
|
||||
size="12"
|
||||
color="var(--color-text-inverse)"
|
||||
/>
|
||||
@@ -65,13 +62,6 @@
|
||||
>
|
||||
<wd-icon name="notification" size="16" color="var(--color-text-inverse)" />
|
||||
</view>
|
||||
<view
|
||||
class="profile-card__action-btn"
|
||||
aria-label="主题设置"
|
||||
@click.stop="openThemeSettings"
|
||||
>
|
||||
<wd-icon name="setting1" size="16" color="var(--color-text-inverse)" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="!isLogin" class="profile-card__action" @click.stop="navigateToLogin()">
|
||||
@@ -101,7 +91,7 @@
|
||||
<text class="community-card__desc">开源更新、实战内容、交流群入口,统一在这里查看</text>
|
||||
</view>
|
||||
<view class="community-card__arrow">
|
||||
<wd-icon name="arrow-right" size="16" color="var(--color-text-secondary)" />
|
||||
<wd-icon name="right" size="16" color="var(--color-text-secondary)" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -117,7 +107,7 @@
|
||||
</view>
|
||||
<view class="quick-card" @click="openAccount">
|
||||
<view class="quick-card__icon quick-card__icon--success">
|
||||
<wd-icon name="secured" size="28" color="var(--color-success-dark)" />
|
||||
<wd-icon name="safe" size="28" color="var(--color-success)" />
|
||||
</view>
|
||||
<view class="quick-card__body">
|
||||
<text class="quick-card__title">账号安全</text>
|
||||
@@ -134,24 +124,24 @@
|
||||
<view class="menu-list menu-list--flat">
|
||||
<view class="menu-row" @click="openNetworkTest">
|
||||
<view class="menu-row__icon menu-row__icon--warning">
|
||||
<wd-icon name="server" size="18" color="var(--color-warning-dark)" />
|
||||
<wd-icon name="tool" size="18" color="var(--color-primary)" />
|
||||
</view>
|
||||
<view class="menu-row__main">
|
||||
<text class="menu-row__title">网络检测</text>
|
||||
<text class="menu-row__desc">检测接口连通性</text>
|
||||
</view>
|
||||
<wd-icon name="arrow-right" size="16" color="var(--color-text-placeholder)" />
|
||||
<wd-icon name="right" size="16" color="var(--color-text-placeholder)" />
|
||||
</view>
|
||||
<view class="menu-row" @click="handleClearCache">
|
||||
<view class="menu-row__icon menu-row__icon--danger">
|
||||
<wd-icon name="delete-thin" size="18" color="var(--color-danger-dark)" />
|
||||
<wd-icon name="delete" size="18" color="var(--color-danger)" />
|
||||
</view>
|
||||
<view class="menu-row__main">
|
||||
<text class="menu-row__title">清理缓存</text>
|
||||
<text class="menu-row__desc">显示当前缓存大小</text>
|
||||
</view>
|
||||
<text class="menu-row__value">{{ cacheSize }}</text>
|
||||
<wd-icon name="arrow-right" size="16" color="var(--color-text-placeholder)" />
|
||||
<wd-icon name="right" size="16" color="var(--color-text-placeholder)" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -161,34 +151,34 @@
|
||||
<view class="menu-list menu-list--flat">
|
||||
<view class="menu-row" @click="openSettings">
|
||||
<view class="menu-row__icon menu-row__icon--primary">
|
||||
<wd-icon name="setting" size="18" :color="`rgba(77, 128, 240, 0.12)`" />
|
||||
<wd-icon name="settings" size="18" color="var(--color-primary)" />
|
||||
</view>
|
||||
<view class="menu-row__main">
|
||||
<text class="menu-row__title">系统设置</text>
|
||||
<text class="menu-row__desc">主题、语言、通知等设置</text>
|
||||
</view>
|
||||
<wd-icon name="arrow-right" size="16" color="var(--color-text-placeholder)" />
|
||||
<wd-icon name="right" size="16" color="var(--color-text-placeholder)" />
|
||||
</view>
|
||||
<view class="menu-row" @click="openUserAgreement">
|
||||
<view class="menu-row__icon menu-row__icon--success">
|
||||
<wd-icon name="secured" size="18" :color="`rgba(52, 209, 157, 0.12)`" />
|
||||
<wd-icon name="safe" size="18" color="var(--color-success)" />
|
||||
</view>
|
||||
<view class="menu-row__main">
|
||||
<text class="menu-row__title">用户协议</text>
|
||||
<text class="menu-row__desc">了解产品使用规则</text>
|
||||
</view>
|
||||
<wd-icon name="arrow-right" size="16" color="var(--color-text-placeholder)" />
|
||||
<wd-icon name="right" size="16" color="var(--color-text-placeholder)" />
|
||||
</view>
|
||||
<view class="menu-row" @click="openAbout">
|
||||
<view class="menu-row__icon menu-row__icon--teal">
|
||||
<wd-icon name="info-circle" size="18" :color="`rgba(52, 209, 157, 0.12)`" />
|
||||
<view class="menu-row__icon menu-row__icon--info">
|
||||
<wd-icon name="info-circle" size="18" color="var(--color-info)" />
|
||||
</view>
|
||||
<view class="menu-row__main">
|
||||
<text class="menu-row__title">关于系统</text>
|
||||
<text class="menu-row__desc">产品介绍与联系方式</text>
|
||||
</view>
|
||||
<text class="menu-row__value">v{{ appVersion }}</text>
|
||||
<wd-icon name="arrow-right" size="16" color="var(--color-text-placeholder)" />
|
||||
<wd-icon name="right" size="16" color="var(--color-text-placeholder)" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -250,30 +240,12 @@ const hasUserProfile = computed(() => {
|
||||
return !!(info && (info.userId || info.username || info.nickname));
|
||||
});
|
||||
|
||||
const genderValue = computed(() => userInfo.value?.gender);
|
||||
const normalizedGender = computed(() => {
|
||||
const v = genderValue.value;
|
||||
const n = typeof v === "number" ? v : Number(v);
|
||||
return Number.isFinite(n) ? n : 0;
|
||||
});
|
||||
|
||||
const deptNameText = computed(() => {
|
||||
if (!isLogin.value) return "";
|
||||
return userInfo.value?.deptName || "";
|
||||
});
|
||||
|
||||
const genderIconName = computed(() => {
|
||||
if (!isLogin.value) return "";
|
||||
if (normalizedGender.value === 1) return "gender-male";
|
||||
if (normalizedGender.value === 2) return "gender-female";
|
||||
return "";
|
||||
});
|
||||
|
||||
const genderIconClass = computed(() => {
|
||||
if (normalizedGender.value === 1) return "profile-card__gender--male";
|
||||
if (normalizedGender.value === 2) return "profile-card__gender--female";
|
||||
return "";
|
||||
});
|
||||
|
||||
const fetchUserInfoIfNeeded = async () => {
|
||||
if (!isLogin.value || hasUserProfile.value) return;
|
||||
@@ -481,8 +453,7 @@ const openOfficialAccount = () => {
|
||||
letter-spacing: 2rpx;
|
||||
}
|
||||
|
||||
.profile-tag,
|
||||
.profile-card__gender {
|
||||
.profile-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -514,6 +485,8 @@ const openOfficialAccount = () => {
|
||||
.profile-card__left {
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
}
|
||||
|
||||
.profile-card__avatar {
|
||||
@@ -525,27 +498,15 @@ const openOfficialAccount = () => {
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.profile-card__gender {
|
||||
position: absolute;
|
||||
right: -4rpx;
|
||||
bottom: 2rpx;
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
border: 2rpx solid var(--color-text-inverse);
|
||||
border-radius: 50%;
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.profile-card__online-dot {
|
||||
position: absolute;
|
||||
right: 6rpx;
|
||||
bottom: 6rpx;
|
||||
width: 18rpx;
|
||||
height: 18rpx;
|
||||
right: -4rpx;
|
||||
bottom: -4rpx;
|
||||
width: 16rpx;
|
||||
height: 16rpx;
|
||||
background: var(--color-success);
|
||||
border: 3rpx solid var(--color-border-glass-strong);
|
||||
border: 2rpx solid var(--color-bg);
|
||||
border-radius: 50%;
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.profile-card__actions {
|
||||
@@ -592,13 +553,7 @@ const openOfficialAccount = () => {
|
||||
border-radius: 999rpx;
|
||||
}
|
||||
|
||||
.profile-card__gender--male {
|
||||
background: var(--color-primary);
|
||||
}
|
||||
|
||||
.profile-card__gender--female {
|
||||
background: var(--color-danger);
|
||||
}
|
||||
|
||||
.profile-card__main {
|
||||
flex: 1;
|
||||
@@ -856,11 +811,11 @@ const openOfficialAccount = () => {
|
||||
border-radius: 22rpx;
|
||||
|
||||
&--primary {
|
||||
background: rgba(77, 128, 240, 0.12);
|
||||
background: var(--color-primary-alpha-15);
|
||||
}
|
||||
|
||||
&--warning {
|
||||
background: var(--color-warning-light);
|
||||
background: var(--color-primary-alpha-15);
|
||||
}
|
||||
|
||||
&--danger {
|
||||
@@ -868,11 +823,11 @@ const openOfficialAccount = () => {
|
||||
}
|
||||
|
||||
&--success {
|
||||
background: rgba(52, 209, 157, 0.12);
|
||||
background: var(--color-success-light);
|
||||
}
|
||||
|
||||
&--teal {
|
||||
background: rgba(52, 209, 157, 0.12);
|
||||
&--info {
|
||||
background: var(--color-info-light);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
<!-- 性别 -->
|
||||
<view class="profile-form__section">
|
||||
<view class="profile-form__label">性别</view>
|
||||
<wd-radio-group v-model="profileForm.gender" shape="button" class="gender-group">
|
||||
<wd-radio-group v-model="profileForm.gender" type="button" class="gender-group">
|
||||
<wd-radio :value="1" class="gender-radio">男</wd-radio>
|
||||
<wd-radio :value="2" class="gender-radio">女</wd-radio>
|
||||
</wd-radio-group>
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
:rules="rules.nickname"
|
||||
/>
|
||||
<wd-cell title="性别" title-width="160rpx" center prop="gender" :rules="rules.gender">
|
||||
<wd-radio-group v-model="userProfileForm.gender" shape="button" class="ef-radio-group">
|
||||
<wd-radio-group v-model="userProfileForm.gender" type="button" class="ef-radio-group">
|
||||
<wd-radio :value="1">男</wd-radio>
|
||||
<wd-radio :value="2">女</wd-radio>
|
||||
</wd-radio-group>
|
||||
|
||||
@@ -1,27 +1,32 @@
|
||||
<template>
|
||||
<view class="page">
|
||||
<wd-cell-group>
|
||||
<wd-cell title="主题设置" icon="setting1" is-link @click="navigateToTheme" />
|
||||
<wd-cell title="用户协议" icon="user" is-link @click="navigateToUserAgreement" />
|
||||
<wd-cell title="关于我们" icon="info-circle" is-link @click="navigateToAbout" />
|
||||
</wd-cell-group>
|
||||
|
||||
<wd-cell-group custom-style="margin-top:40rpx">
|
||||
<wd-cell title="网络测试" icon="wifi" is-link @click="navigateToNetworkTest" />
|
||||
<wd-cell
|
||||
title="清空缓存"
|
||||
icon="delete1"
|
||||
:value="cacheSize"
|
||||
clickable
|
||||
@click="handleClearCache"
|
||||
/>
|
||||
</wd-cell-group>
|
||||
|
||||
<view v-if="isLogin" class="logout-section">
|
||||
<wd-button custom-class="logout-btn" plain @click="handleLogout">退出登录</wd-button>
|
||||
<view class="page settings-page">
|
||||
<view class="settings-group-wrap">
|
||||
<wd-cell-group>
|
||||
<wd-cell title="主题设置" icon="settings" is-link @click="navigateToTheme" />
|
||||
<wd-cell title="用户协议" icon="user" is-link @click="navigateToUserAgreement" />
|
||||
<wd-cell title="关于我们" icon="info-circle" is-link @click="navigateToAbout" />
|
||||
</wd-cell-group>
|
||||
</view>
|
||||
|
||||
<view class="settings-group-wrap">
|
||||
<wd-cell-group custom-style="margin-top:24rpx">
|
||||
<wd-cell title="网络测试" icon="wifi" is-link @click="navigateToNetworkTest" />
|
||||
<wd-cell
|
||||
title="清空缓存"
|
||||
icon="delete"
|
||||
:value="cacheSize"
|
||||
clickable
|
||||
@click="handleClearCache"
|
||||
/>
|
||||
</wd-cell-group>
|
||||
</view>
|
||||
|
||||
<view v-if="isLogin" class="logout-section">
|
||||
<wd-button type="danger" block custom-class="logout-btn" @click="handleLogout">
|
||||
退出登录
|
||||
</wd-button>
|
||||
</view>
|
||||
|
||||
<!-- 使用wot-design-uni的Loading组件 -->
|
||||
<wd-loading
|
||||
v-if="isClearing"
|
||||
v-model="isClearing"
|
||||
@@ -35,11 +40,10 @@
|
||||
<script lang="ts" setup>
|
||||
import { useUserStore } from "@/store";
|
||||
import { onLoad } from "@dcloudio/uni-app";
|
||||
import { useToast, useDialog } from "@wot-ui/ui";
|
||||
import { useToast } from "@wot-ui/ui";
|
||||
|
||||
const userStore = useUserStore();
|
||||
const toast = useToast();
|
||||
const { messageBox } = useDialog();
|
||||
const isLogin = computed(() => !!userStore.userInfo);
|
||||
|
||||
// 主题设置
|
||||
@@ -149,14 +153,21 @@ const handleClearCache = async () => {
|
||||
};
|
||||
|
||||
// 退出登录
|
||||
const handleLogout = async () => {
|
||||
try {
|
||||
await messageBox({ title: "提示", msg: "确定要退出登录吗?", type: "warning" });
|
||||
userStore.logout();
|
||||
toast.success("已退出登录");
|
||||
} catch {
|
||||
// 用户取消
|
||||
}
|
||||
const handleLogout = () => {
|
||||
uni.showModal({
|
||||
title: "提示",
|
||||
content: "确定要退出登录吗?",
|
||||
confirmColor: "#ff4757",
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
userStore.logout();
|
||||
toast.success("已退出登录");
|
||||
setTimeout(() => {
|
||||
uni.reLaunch({ url: "/pages/mine/index" });
|
||||
}, 800);
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// 检查登录状态
|
||||
@@ -182,17 +193,27 @@ onLoad(() => {
|
||||
margin-top: 60rpx;
|
||||
}
|
||||
|
||||
:deep(.logout-btn) {
|
||||
width: 80%;
|
||||
height: 80rpx;
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
border-radius: 40rpx;
|
||||
.settings-page {
|
||||
padding: 24rpx;
|
||||
}
|
||||
|
||||
&:active {
|
||||
opacity: 0.8;
|
||||
transform: scale(0.98);
|
||||
}
|
||||
.settings-group-wrap {
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.logout-section {
|
||||
margin-top: 48rpx;
|
||||
}
|
||||
|
||||
:deep(.wd-cell:active) {
|
||||
background-color: var(--color-bg-tertiary) !important;
|
||||
}
|
||||
|
||||
:deep(.logout-btn) {
|
||||
height: 88rpx;
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
border-radius: 24rpx;
|
||||
}
|
||||
|
||||
.loading-center {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<!-- 预设颜色选择 -->
|
||||
<wd-cell title="预设主题色">
|
||||
<template #default>
|
||||
<view class="flex gap-16rpx">
|
||||
<view class="flex flex-wrap gap-12rpx">
|
||||
<view
|
||||
v-for="(color, index) in themeColorOptions"
|
||||
:key="index"
|
||||
@@ -54,7 +54,7 @@
|
||||
<wd-cell-group title="效果预览" border custom-class="mt-24rpx">
|
||||
<wd-cell>
|
||||
<template #default>
|
||||
<view class="flex gap-24rpx py-12rpx">
|
||||
<view class="flex items-center justify-center gap-24rpx py-12rpx">
|
||||
<wd-button type="primary" size="small">主要按钮</wd-button>
|
||||
<text class="text-28rpx font-500" :style="{ color: currentThemeColor }">
|
||||
主题色文本
|
||||
@@ -67,7 +67,9 @@
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<view class="mt-32rpx">
|
||||
<wd-button type="info" size="large" block @click="handleResetTheme">重置为默认主题</wd-button>
|
||||
<wd-button type="primary" plain size="large" block @click="handleResetTheme">
|
||||
重置为默认主题
|
||||
</wd-button>
|
||||
</view>
|
||||
|
||||
<!-- 自定义颜色输入弹窗 -->
|
||||
@@ -88,7 +90,12 @@
|
||||
<text class="text-28rpx color-text-secondary">请输入十六进制颜色值</text>
|
||||
<view class="flex items-center gap-16rpx mt-16rpx">
|
||||
<view class="color-dot-lg" :style="{ backgroundColor: customColor || '#FF4757' }" />
|
||||
<wd-input v-model="customColor" placeholder="例如: #FF4757" :maxlength="7" />
|
||||
<wd-input
|
||||
v-model="customColor"
|
||||
placeholder="例如: #FF4757"
|
||||
:maxlength="7"
|
||||
custom-class="theme-input"
|
||||
/>
|
||||
</view>
|
||||
<text class="text-24rpx color-text-placeholder mt-12rpx">支持格式:#RGB 或 #RRGGBB</text>
|
||||
</view>
|
||||
@@ -280,4 +287,9 @@ onShow(() => {
|
||||
padding: 24rpx 32rpx;
|
||||
border-top: 1rpx solid var(--color-border);
|
||||
}
|
||||
|
||||
:deep(.theme-input) {
|
||||
background-color: var(--color-bg-secondary) !important;
|
||||
color: var(--color-text) !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
</wd-cell-group>
|
||||
</wd-form>
|
||||
<view class="popup-actions">
|
||||
<wd-button type="info" plain @click="closeDictDialog">取消</wd-button>
|
||||
<wd-button type="info" variant="plain" @click="closeDictDialog">取消</wd-button>
|
||||
<wd-button type="primary" :loading="isSubmitting" @click="submitDictForm">保存</wd-button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
</wd-cell-group>
|
||||
</wd-form>
|
||||
<view class="popup-actions">
|
||||
<wd-button type="info" plain @click="closeItemDialog">取消</wd-button>
|
||||
<wd-button type="info" variant="plain" @click="closeItemDialog">取消</wd-button>
|
||||
<wd-button type="primary" :loading="isSubmitting" @click="submitItemForm">保存</wd-button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
v-for="(child, childIndex) in item.children"
|
||||
:key="childIndex"
|
||||
use-slot
|
||||
@itemclick="handleNavClick(child)"
|
||||
@click="handleNavClick(child)"
|
||||
>
|
||||
<view class="work-grid__icon p-2">
|
||||
<image class="w-72rpx h-72rpx rounded-8rpx" :src="child.icon" />
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
<wd-cell v-if="logDetail.errorMsg" title="错误信息" :value="logDetail.errorMsg" />
|
||||
</wd-cell-group>
|
||||
<view class="popup-actions">
|
||||
<wd-button type="info" plain block @click="closeLogDetail">关闭</wd-button>
|
||||
<wd-button type="info" variant="plain" block @click="closeLogDetail">关闭</wd-button>
|
||||
</view>
|
||||
</view>
|
||||
</wd-popup>
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
/>
|
||||
<wd-input v-model="formData.name" label="菜单名称" required />
|
||||
<wd-cell title="菜单类型" required>
|
||||
<wd-radio-group v-model="formData.type" size="smalll" shape="button" cell>
|
||||
<wd-radio-group v-model="formData.type" size="small" type="button">
|
||||
<wd-radio value="C">目录</wd-radio>
|
||||
<wd-radio value="M">菜单</wd-radio>
|
||||
<wd-radio value="B">按钮</wd-radio>
|
||||
@@ -100,7 +100,7 @@
|
||||
</wd-form>
|
||||
</scroll-view>
|
||||
<view class="popup-actions">
|
||||
<wd-button type="info" plain @click="closeMenuDialog">取消</wd-button>
|
||||
<wd-button type="info" variant="plain" @click="closeMenuDialog">取消</wd-button>
|
||||
<wd-button type="primary" :loading="isSubmitting" @click="submitMenuForm">保存</wd-button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
<rich-text :nodes="noticeDetail.content" class="text-28rpx" />
|
||||
</view>
|
||||
<view class="popup-actions">
|
||||
<wd-button type="info" plain block @click="closeNoticeDetail">关闭</wd-button>
|
||||
<wd-button type="info" variant="plain" block @click="closeNoticeDetail">关闭</wd-button>
|
||||
</view>
|
||||
</view>
|
||||
</wd-popup>
|
||||
@@ -110,14 +110,14 @@
|
||||
<wd-cell-group border>
|
||||
<wd-input v-model="formData.title" label="标题" required placeholder="请输入通知标题" />
|
||||
<wd-cell title="优先级">
|
||||
<wd-radio-group v-model="formData.level" shape="button">
|
||||
<wd-radio-group v-model="formData.level" type="button">
|
||||
<wd-radio value="L">低</wd-radio>
|
||||
<wd-radio value="M">中</wd-radio>
|
||||
<wd-radio value="H">高</wd-radio>
|
||||
</wd-radio-group>
|
||||
</wd-cell>
|
||||
<wd-cell title="目标类型">
|
||||
<wd-radio-group v-model="formData.targetType" shape="button">
|
||||
<wd-radio-group v-model="formData.targetType" type="button">
|
||||
<wd-radio :value="1">全体</wd-radio>
|
||||
<wd-radio :value="2">指定用户</wd-radio>
|
||||
</wd-radio-group>
|
||||
@@ -132,7 +132,7 @@
|
||||
</wd-cell-group>
|
||||
</wd-form>
|
||||
<view class="popup-actions">
|
||||
<wd-button type="info" plain @click="closeNoticeForm">取消</wd-button>
|
||||
<wd-button type="info" variant="plain" @click="closeNoticeForm">取消</wd-button>
|
||||
<wd-button type="primary" :loading="isSubmitting" @click="submitNoticeForm">
|
||||
保存
|
||||
</wd-button>
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
|
||||
<view class="action-bar">
|
||||
<view class="action-bar__left">
|
||||
<wd-button size="small" plain @click="handleSelectAll">全选</wd-button>
|
||||
<wd-button size="small" plain @click="handleClearAll">取消全选</wd-button>
|
||||
<wd-button size="small" variant="plain" @click="handleSelectAll">全选</wd-button>
|
||||
<wd-button size="small" variant="plain" @click="handleClearAll">取消全选</wd-button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -69,7 +69,7 @@
|
||||
|
||||
<!-- 固定底部操作栏 -->
|
||||
<view class="bottom-bar">
|
||||
<wd-button type="info" plain @click="handleCancel">取消</wd-button>
|
||||
<wd-button type="info" variant="plain" @click="handleCancel">取消</wd-button>
|
||||
<wd-button type="primary" :loading="isSubmitting" @click="handleSubmit">保存</wd-button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
</wd-cell-group>
|
||||
</wd-form>
|
||||
<view class="popup-actions">
|
||||
<wd-button type="info" plain @click="closeRoleDialog">取消</wd-button>
|
||||
<wd-button type="info" variant="plain" @click="closeRoleDialog">取消</wd-button>
|
||||
<wd-button type="primary" :loading="isSubmitting" @click="submitRoleForm">保存</wd-button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
/>
|
||||
<cu-date-query v-model="queryParams.createTime" label="创建时间" />
|
||||
<view class="popup-actions">
|
||||
<wd-button type="info" plain @click="resetUserFilter">重置</wd-button>
|
||||
<wd-button type="info" variant="plain" @click="resetUserFilter">重置</wd-button>
|
||||
<wd-button type="primary" @click="applyUserFilter">查询</wd-button>
|
||||
</view>
|
||||
</view>
|
||||
@@ -148,7 +148,7 @@
|
||||
</wd-cell-group>
|
||||
</wd-form>
|
||||
<view class="popup-actions">
|
||||
<wd-button type="info" plain @click="closeUserDialog">取消</wd-button>
|
||||
<wd-button type="info" variant="plain" @click="closeUserDialog">取消</wd-button>
|
||||
<wd-button type="primary" :loading="isSubmitting" @click="submitUserForm">保存</wd-button>
|
||||
</view>
|
||||
</view>
|
||||
@@ -184,7 +184,7 @@
|
||||
</wd-cell-group>
|
||||
</wd-form>
|
||||
<view class="popup-actions">
|
||||
<wd-button type="info" plain @click="resetPwdDialog.visible = false">取消</wd-button>
|
||||
<wd-button type="info" variant="plain" @click="resetPwdDialog.visible = false">取消</wd-button>
|
||||
<wd-button
|
||||
type="primary"
|
||||
:loading="resetPwdDialog.isSubmitting"
|
||||
|
||||
@@ -26,8 +26,10 @@ export const useThemeStore = defineStore("theme", () => {
|
||||
Storage.get<ThemeColorOption>(THEME_COLOR_KEY, themeColorOptions[0])
|
||||
);
|
||||
|
||||
/** 主题变量(响应式对象) */
|
||||
const themeVars = reactive({
|
||||
/**
|
||||
* 深色主题变量(仅在 dark 模式下注入)
|
||||
*/
|
||||
const darkThemeVars = {
|
||||
darkBackground: "#1f2937",
|
||||
darkBackground2: "#111827",
|
||||
darkBackground3: "#1e293b",
|
||||
@@ -38,7 +40,45 @@ export const useThemeStore = defineStore("theme", () => {
|
||||
darkColor: "#f9fafb",
|
||||
darkColor2: "#9ca3af",
|
||||
darkColor3: "#6b7280",
|
||||
colorTheme: currentThemeColor.value.primary,
|
||||
filledOppo: "#1f2937",
|
||||
cardBg: "#1f2937",
|
||||
cardBorderColor: "#374151",
|
||||
cardTitleColor: "#f9fafb",
|
||||
cardContentColor: "#9ca3af",
|
||||
gridItemBg: "#1f2937",
|
||||
gridItemTextColor: "#f9fafb",
|
||||
gridItemIconColor: "#9ca3af",
|
||||
cellBg: "#1f2937",
|
||||
popupBg: "#1f2937",
|
||||
searchBg: "#1f2937",
|
||||
searchInputBg: "#374151",
|
||||
inputBg: "#374151",
|
||||
textareaBg: "#374151",
|
||||
pickerBg: "#1f2937",
|
||||
dialogBg: "#1f2937",
|
||||
actionSheetBg: "#1f2937",
|
||||
tabsNavBg: "#1f2937",
|
||||
tabbarBg: "#1f2937",
|
||||
navbarBg: "#1f2937",
|
||||
buttonNormalBg: "#374151",
|
||||
borderMain: "#374151",
|
||||
borderLight: "#4b5563",
|
||||
textMain: "#f9fafb",
|
||||
textSecondary: "#9ca3af",
|
||||
textPlaceholder: "#6b7280",
|
||||
iconMain: "#f9fafb",
|
||||
iconSecondary: "#9ca3af",
|
||||
baseBlack: "#1f2937",
|
||||
baseWhite: "#f9fafb",
|
||||
};
|
||||
|
||||
/** 主题变量(计算属性,根据模式动态切换) */
|
||||
const themeVars = computed(() => {
|
||||
const base = { colorTheme: currentThemeColor.value.primary };
|
||||
if (theme.value === "dark") {
|
||||
return { ...base, ...darkThemeVars };
|
||||
}
|
||||
return base;
|
||||
});
|
||||
|
||||
// ==========================================================================
|
||||
@@ -48,6 +88,27 @@ export const useThemeStore = defineStore("theme", () => {
|
||||
/** 是否为暗黑模式 */
|
||||
const isDark = computed(() => theme.value === "dark");
|
||||
|
||||
/**
|
||||
* 将 themeVars 转换为 CSS 变量内联样式字符串
|
||||
* light 模式下只注入主题色,避免覆盖 Wot UI 默认亮色样式
|
||||
* dark 模式下注入完整的深色变量覆盖
|
||||
*/
|
||||
const kebabCase = (str: string) =>
|
||||
str
|
||||
.replace(/^./, (s) => s.toLowerCase())
|
||||
.replace(/([a-z])([A-Z])/g, "$1-$2")
|
||||
.replace(/(\d)/g, "-$1")
|
||||
.toLowerCase();
|
||||
|
||||
const themeCSSVars = computed(() => {
|
||||
if (theme.value === "light") {
|
||||
return `--wot-color-theme:${themeVars.value.colorTheme};`;
|
||||
}
|
||||
return Object.entries(themeVars.value)
|
||||
.map(([key, val]) => `--wot-${kebabCase(key)}:${val}`)
|
||||
.join(";");
|
||||
});
|
||||
|
||||
// ==========================================================================
|
||||
// 方法
|
||||
// ==========================================================================
|
||||
@@ -79,17 +140,12 @@ export const useThemeStore = defineStore("theme", () => {
|
||||
const setCurrentThemeColor = (color: ThemeColorOption) => {
|
||||
currentThemeColor.value = color;
|
||||
Storage.set(THEME_COLOR_KEY, color);
|
||||
themeVars.colorTheme = color.primary;
|
||||
};
|
||||
|
||||
/**
|
||||
* 初始化主题
|
||||
*/
|
||||
const initTheme = () => {
|
||||
// 更新主题变量中的颜色
|
||||
themeVars.colorTheme = currentThemeColor.value.primary;
|
||||
|
||||
// 设置导航栏颜色
|
||||
nextTick(() => {
|
||||
setNavigationBarColor();
|
||||
});
|
||||
@@ -107,6 +163,7 @@ export const useThemeStore = defineStore("theme", () => {
|
||||
|
||||
// 计算属性
|
||||
isDark,
|
||||
themeCSSVars,
|
||||
|
||||
// 方法
|
||||
toggleTheme,
|
||||
|
||||
@@ -33,6 +33,8 @@ page {
|
||||
--color-warning-light: #fff4e6;
|
||||
--color-danger: #ff4757;
|
||||
--color-danger-light: #fff1f2;
|
||||
--color-info: #3b82f6;
|
||||
--color-info-light: #dbeafe;
|
||||
--color-wx-green: #22c55e;
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
@@ -130,6 +132,8 @@ page {
|
||||
--color-warning-light: #78350f;
|
||||
--color-danger: #f87171;
|
||||
--color-danger-light: #7f1d1d;
|
||||
--color-info: #60a5fa;
|
||||
--color-info-light: #1e3a5f;
|
||||
--color-wx-green: #22c55e;
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
@@ -192,4 +196,7 @@ page {
|
||||
--wot-color-text-secondary: var(--color-text-secondary);
|
||||
--wot-color-text-placeholder: var(--color-text-placeholder);
|
||||
--wot-color-border: var(--color-border);
|
||||
--wot-radio-button-bg: var(--color-bg-tertiary);
|
||||
--wot-radio-button-checked-bg: var(--color-primary);
|
||||
--wot-radio-label-color: var(--color-text);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user