wip: 临时提交

This commit is contained in:
Ray.Hao
2025-08-01 14:37:45 +08:00
parent a95fbd6a54
commit d48812821f
22 changed files with 355 additions and 649 deletions

5
components.d.ts vendored
View File

@@ -21,11 +21,16 @@ 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']
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']
WdImg: typeof import('wot-design-uni/components/wd-img/wd-img.vue')['default']
WdImgCropper: typeof import('wot-design-uni/components/wd-img-cropper/wd-img-cropper.vue')['default']
WdInput: typeof import('wot-design-uni/components/wd-input/wd-input.vue')['default']
WdLoading: typeof import('wot-design-uni/components/wd-loading/wd-loading.vue')['default']
WdMessageBox: typeof import('wot-design-uni/components/wd-message-box/wd-message-box.vue')['default']

View File

@@ -1,6 +1,4 @@
<script setup lang="ts">
import { onLaunch, onShow, onHide } from "@dcloudio/uni-app";
onLaunch(() => {
console.log("App Launch");
});

View File

@@ -19,20 +19,10 @@ export type ThemeMode = "light" | "dark";
*/
export interface ThemeState {
theme: ThemeMode;
followSystem: boolean;
hasUserSet: boolean;
currentThemeColor: ThemeColorOption;
themeVars: ConfigProviderThemeVars;
}
/**
* 系统主题状态接口(简化版)
*/
export interface SystemThemeState {
theme: ThemeMode;
themeVars: ConfigProviderThemeVars;
}
/**
* 预定义的主题色选项
*/

View File

@@ -1,132 +1,51 @@
import { defineStore } from "pinia";
import type { ThemeColorOption, ThemeMode, ThemeState } from "@/composables/types/theme";
import type { ThemeColorOption, ThemeMode } from "@/composables/types/theme";
import { themeColorOptions } from "@/composables/types/theme";
import { useThemeStore } from "@/store/modules/theme.store";
/**
* 完整版主题状态管理
* 支持手动切换主题、主题色选择、跟随系统主题等完整功能
*/
export const useThemeStore = defineStore("theme", {
state: (): ThemeState => ({
theme: "light",
followSystem: true, // 是否跟随系统主题
hasUserSet: false, // 用户是否手动设置过主题
currentThemeColor: themeColorOptions[0],
themeVars: {
darkBackground: "#0f0f0f",
darkBackground2: "#1a1a1a",
darkBackground3: "#242424",
darkBackground4: "#2f2f2f",
darkBackground5: "#3d3d3d",
darkBackground6: "#4a4a4a",
darkBackground7: "#606060",
darkColor: "#ffffff",
darkColor2: "#e0e0e0",
darkColor3: "#a0a0a0",
colorTheme: themeColorOptions[0].primary,
},
}),
export function useTheme() {
const store = useThemeStore();
getters: {
isDark: (state) => state.theme === "dark",
},
/**
* 切换暗黑模式
* @param mode 指定主题模式,不传则自动切换
*/
function toggleTheme(mode?: ThemeMode) {
store.toggleTheme(mode);
}
actions: {
/**
* 手动切换主题
* @param mode 指定主题模式,不传则自动切换
*/
toggleTheme(mode?: ThemeMode) {
this.theme = mode || (this.theme === "light" ? "dark" : "light");
this.hasUserSet = true; // 标记用户已手动设置
this.followSystem = false; // 不再跟随系统
this.setNavigationBarColor();
},
/**
* 设置主题色
* @param option 主题色选项
*/
function setThemeColor(option: ThemeColorOption) {
store.setCurrentThemeColor(option);
}
/**
* 设置是否跟随系统主题
* @param follow 是否跟随系统
*/
setFollowSystem(follow: boolean) {
this.followSystem = follow;
if (follow) {
this.hasUserSet = false;
this.initTheme(); // 重新获取系统主题
}
},
/**
* 初始化主题
*/
function initTheme() {
store.initTheme();
}
/**
* 设置导航栏颜色
*/
setNavigationBarColor() {
uni.setNavigationBarColor({
frontColor: this.theme === "light" ? "#000000" : "#ffffff",
backgroundColor: this.theme === "light" ? "#ffffff" : "#000000",
});
},
// 注意全局主题管理已在App.vue中处理
// 包括:系统主题监听、导航栏颜色同步等
// 组件中一般不需要再调用initTheme(),除非有特殊需求
/**
* 设置主题色
* @param color 主题色选项
*/
setCurrentThemeColor(color: ThemeColorOption) {
this.currentThemeColor = color;
this.themeVars.colorTheme = color.primary;
},
return {
theme: computed(() => store.theme),
isDark: computed(() => store.isDark),
currentThemeColor: computed(() => store.currentThemeColor),
themeVars: computed(() => store.themeVars),
/**
* 获取系统主题
* @returns 系统主题模式
*/
getSystemTheme(): ThemeMode {
try {
// #ifdef MP-WEIXIN
// 微信小程序使用 getAppBaseInfo
const appBaseInfo = uni.getAppBaseInfo();
if (appBaseInfo && appBaseInfo.theme) {
return appBaseInfo.theme as ThemeMode;
}
// #endif
themeColorOptions,
// #ifndef MP-WEIXIN
// 其他平台使用 getSystemInfoSync
const systemInfo = uni.getSystemInfoSync();
if (systemInfo && systemInfo.theme) {
return systemInfo.theme as ThemeMode;
}
// #endif
} catch (error) {
console.warn("获取系统主题失败:", error);
}
return "light"; // 默认返回 light
},
initTheme,
toggleTheme,
setThemeColor,
};
}
/**
* 初始化主题
*/
initTheme() {
// 如果用户已手动设置且不跟随系统,保持当前主题
if (this.hasUserSet && !this.followSystem) {
console.log("使用用户设置的主题:", this.theme);
this.setNavigationBarColor();
return;
}
// 获取系统主题
const systemTheme = this.getSystemTheme();
// 如果是首次启动或跟随系统,使用系统主题
if (!this.hasUserSet || this.followSystem) {
this.theme = systemTheme;
if (!this.hasUserSet) {
this.followSystem = true;
console.log("首次启动,使用系统主题:", this.theme);
} else {
console.log("跟随系统主题:", this.theme);
}
}
this.setNavigationBarColor();
},
},
});
// 导出类型和常量供外部使用
export type { ThemeColorOption, ThemeMode };
export { themeColorOptions };

View File

@@ -1,5 +1,5 @@
<script lang="ts" setup>
import { useThemeStore } from "@/composables/useTheme";
import { useThemeStore } from "@/store";
import { storeToRefs } from "pinia";
const themeStore = useThemeStore();

View File

@@ -26,7 +26,7 @@
<script setup lang="ts">
import { useRoute } from "vue-router";
import { useThemeStore } from "@/composables/useTheme";
import { useThemeStore } from "@/store";
import { useTabbar } from "@/composables/useTabbar";
import { useRouter } from "uni-mini-router";
import { storeToRefs } from "pinia";

View File

@@ -55,7 +55,11 @@
},
{
"path": "pages/mine/settings/index",
"type": "page"
"type": "page",
"name": "settings",
"style": {
"navigationBarTitleText": "设置"
}
},
{
"path": "pages/mine/settings/account/index",
@@ -63,7 +67,11 @@
},
{
"path": "pages/mine/settings/agreement/index",
"type": "page"
"type": "page",
"name": "theme",
"style": {
"navigationBarTitleText": "用户协议"
}
},
{
"path": "pages/mine/settings/network/index",
@@ -71,7 +79,11 @@
},
{
"path": "pages/mine/settings/theme/index",
"type": "page"
"type": "page",
"name": "theme",
"style": {
"navigationBarTitleText": "主题设置"
}
}
],
"globalStyle": {

View File

@@ -126,7 +126,7 @@ import { type LoginData } from "@/api/auth";
import { useUserStore } from "@/store/modules/user.store";
import { useToast } from "wot-design-uni";
import { useWechat } from "@/composables/useWechat";
import { useThemeStore } from "@/composables/useTheme";
import { useThemeStore } from "@/composables/types/theme";
import { computed, onMounted } from "vue";
const loginFormRef = ref();

View File

@@ -159,11 +159,8 @@
</template>
<script lang="ts" setup>
import { onShow } from "@dcloudio/uni-app";
import { useToast } from "wot-design-uni";
import { useUserStore } from "@/store/modules/user.store";
import { useThemeStore } from "@/composables/useTheme";
import { computed } from "vue";
import { useUserStore, useThemeStore } from "@/store";
const toast = useToast();
const userStore = useUserStore();

View File

@@ -1,12 +1,5 @@
<template>
<view class="app-container">
<wd-card>
<view class="flex-col-center py-4">
<text class="text-xl font-bold mb-2">用户协议</text>
<text class="text-sm text-gray-500">更新日期2024年3月15日</text>
</view>
</wd-card>
<wd-collapse v-model="activeNames" accordion>
<wd-collapse-item
v-for="(section, index) in agreementContent"
@@ -74,4 +67,13 @@ const handleAgree = () => {
};
</script>
<route lang="json">
{
"name": "theme",
"style": {
"navigationBarTitleText": "用户协议"
}
}
</route>
<style lang="scss" scoped></style>

View File

@@ -1,5 +1,5 @@
<template>
<view class="app-container">
<view class="app-container dark-mode">
<wd-cell-group>
<wd-cell v-if="isLogin" title="个人资料" icon="user" is-link @click="navigateToProfile" />
<wd-cell
@@ -195,6 +195,16 @@ onLoad(() => {
getCacheSize();
});
</script>
<route lang="json">
{
"name": "settings",
"style": {
"navigationBarTitleText": "设置"
}
}
</route>
<style lang="scss" scoped>
.logout-section {
display: flex;

View File

@@ -1,5 +1,5 @@
<template>
<view class="theme-settings-container">
<view class="app-container">
<!-- 页面标题 -->
<view class="page-header">
<text class="page-title">主题设置</text>
@@ -95,16 +95,6 @@
</wd-grid>
</wd-card>
<!-- 跟随系统设置 -->
<wd-card class="setting-section">
<wd-cell title="跟随系统主题" :value="followSystemActive ? '已开启' : '已关闭'">
<wd-switch :model-value="followSystemActive" @change="handleFollowSystemChange" />
</wd-cell>
<view class="follow-system-tip p-3 text-sm text-gray-500">
开启后应用会自动适配系统的深色/浅色模式设置
</view>
</wd-card>
<!-- 操作按钮 -->
<wd-card class="action-section">
<wd-button type="info" size="large" block @click="handleResetTheme">重置为默认主题</wd-button>
@@ -146,17 +136,13 @@
</template>
<script lang="ts" setup>
import { useThemeStore } from "@/composables/useTheme";
import { themeColorOptions } from "@/composables/types/theme";
import { storeToRefs } from "pinia";
import { useTheme } from "@/composables/useTheme";
const themeStore = useThemeStore();
// 将store中的数据解构为响应式引用
const { themeVars } = storeToRefs(themeStore);
// 使用主题组合函数
const { isDark, themeVars, themeColorOptions, toggleTheme, setThemeColor } = useTheme();
// 创建响应式的计算属性来解决类型问题
const isDarkMode = computed(() => themeStore.theme === "dark");
const followSystemActive = computed(() => themeStore.followSystem);
// 创建响应式的计算属性
const isDarkMode = computed(() => isDark.value);
// 自定义颜色输入
const customColor = ref("");
@@ -169,7 +155,7 @@ const currentThemeColor = computed(() => {
// 选择预设颜色
const handleSelectColor = (color: (typeof themeColorOptions)[0]) => {
themeStore.setCurrentThemeColor(color);
setThemeColor(color);
customColor.value = color.primary;
// 提示
@@ -212,7 +198,7 @@ const applyCustomColor = () => {
primary: color,
};
themeStore.setCurrentThemeColor(customColorOption);
setCurrentThemeColor(customColorOption);
showCustomColorInput.value = false;
// 提示
@@ -230,7 +216,7 @@ const handleResetTheme = () => {
content: "确定要重置为默认主题吗?",
success: (res) => {
if (res.confirm) {
themeStore.setCurrentThemeColor(themeColorOptions[0]);
setThemeColor(themeColorOptions[0]);
customColor.value = themeColorOptions[0].primary;
uni.showToast({
@@ -245,7 +231,7 @@ const handleResetTheme = () => {
// 切换暗黑模式
const handleToggleDarkMode = () => {
themeStore.toggleTheme();
toggleTheme();
nextTick(() => {
uni.showToast({
title: `已切换到${isDarkMode.value ? "暗黑" : "浅色"}模式`,
@@ -255,126 +241,50 @@ const handleToggleDarkMode = () => {
});
};
// 处理跟随系统设置的变更
const handleFollowSystemChange = (value: boolean) => {
themeStore.setFollowSystem(value);
};
onLoad(() => {
// 初始化自定义颜色输入框
customColor.value = currentThemeColor.value;
});
onMounted(() => {
// 初始化自定义颜色输入框
customColor.value = currentThemeColor.value;
// 初始化主题
themeStore.initTheme();
});
// 页面显示时确保主题色同步
onShow(() => {
customColor.value = currentThemeColor.value;
console.log("主题设置页面显示,当前主题:", {
mode: isDarkMode.value ? "dark" : "light",
color: currentThemeColor.value,
});
});
</script>
<style lang="scss" scoped>
.theme-settings-container {
min-height: 100vh;
padding: 20rpx;
background-color: var(--wot-color-bg-light, #f8f9fa);
transition: background-color 0.3s ease;
// 强制 Wot 组件应用暗黑模式样式
:deep(.wd-card) {
background: var(--wot-card-bg-color, #fff);
border-radius: 16rpx;
box-shadow: var(--wot-card-shadow, 0 2rpx 12rpx rgba(0, 0, 0, 0.05));
transition: background-color 0.3s ease;
}
:deep(.wd-cell) {
color: var(--wot-color-text, #333);
background-color: var(--wot-card-bg-color, #fff);
.wd-cell__title {
color: var(--wot-color-text, #333) !important;
}
.wd-cell__value {
color: var(--wot-color-text-secondary, #666) !important;
}
.wd-cell__right-icon {
color: var(--wot-color-text-secondary, #999) !important;
}
}
:deep(.wd-grid-item) {
color: var(--wot-color-text, #333) !important;
background-color: var(--wot-card-bg-color, #fff) !important;
}
// 专门为颜色预览块重置背景色
:deep(.color-item) {
background-color: transparent !important;
.color-preview {
background-color: inherit !important;
}
}
:deep(.wd-button) {
&[type="primary"] {
color: #fff !important;
background-color: var(--wot-color-theme, #165dff) !important;
}
&[type="info"] {
color: #fff !important;
background-color: var(--wot-color-info, #909399) !important;
}
}
:deep(.wd-input) {
color: var(--wot-color-text, #333) !important;
background-color: var(--wot-card-bg-color, #fff) !important;
}
:deep(.wd-popup) {
background-color: var(--wot-popup-bg-color, #fff) !important;
}
.custom-color-popup {
padding: 40rpx 30rpx;
background: var(--wot-popup-bg-color, #fff);
border-radius: 20rpx 20rpx 0 0;
<route lang="json">
{
"name": "theme",
"style": {
"navigationBarTitleText": "主题设置"
}
}
</route>
<style lang="scss" scoped>
// 基础布局
.page-header {
padding: 40rpx 20rpx;
margin-bottom: 30rpx;
text-align: center;
background: linear-gradient(135deg, var(--wot-color-theme, #165dff) 0%, #667eea 100%);
border-radius: 16rpx;
}
.page-title {
display: block;
margin-bottom: 10rpx;
font-size: 36rpx;
font-weight: bold;
color: #fff;
}
.page-title {
display: block;
margin-bottom: 10rpx;
font-size: 36rpx;
font-weight: bold;
color: #fff;
}
.page-subtitle {
font-size: 26rpx;
color: rgba(255, 255, 255, 0.8);
.page-subtitle {
font-size: 26rpx;
color: rgba(255, 255, 255, 0.8);
}
}
.setting-section {
@@ -386,45 +296,43 @@ onShow(() => {
align-items: center;
padding: 30rpx 30rpx 20rpx;
border-bottom: 1rpx solid var(--wot-color-border, #f0f0f0);
.section-title {
margin-left: 12rpx;
font-size: 32rpx;
font-weight: 600;
color: var(--wot-color-text, #333);
}
}
.section-title {
margin-left: 12rpx;
font-size: 32rpx;
font-weight: 600;
color: var(--wot-color-text, #333);
}
// 颜色选择区域
.color-section {
padding: 30rpx;
}
.color-label {
margin-bottom: 20rpx;
font-size: 28rpx;
color: var(--wot-color-text-secondary, #666);
}
.color-label {
margin-bottom: 20rpx;
font-size: 28rpx;
color: var(--wot-color-text-secondary, #666);
}
.color-grid {
display: flex;
flex-wrap: wrap;
gap: 20rpx;
justify-content: space-between;
.color-grid {
display: flex;
flex-wrap: wrap;
gap: 20rpx;
justify-content: space-between;
}
}
.color-item {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: calc(25% - 15rpx);
padding: 10rpx;
cursor: pointer;
transition: all 0.3s ease;
.color-preview {
position: relative;
display: flex;
align-items: center;
justify-content: center;
@@ -434,18 +342,17 @@ onShow(() => {
border-radius: 12rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
}
.check-icon {
font-size: 24rpx;
font-weight: bold;
color: #fff;
text-shadow: 0 1rpx 2rpx rgba(0, 0, 0, 0.5);
.check-icon {
font-size: 24rpx;
font-weight: bold;
color: #fff;
text-shadow: 0 1rpx 2rpx rgba(0, 0, 0, 0.5);
}
}
.color-name {
font-size: 22rpx;
line-height: 1.2;
color: var(--wot-color-text-secondary, #666);
text-align: center;
}
@@ -460,39 +367,41 @@ onShow(() => {
}
}
// 当前主题色显示
.current-theme-section {
padding: 30rpx;
.current-theme-item {
display: flex;
align-items: center;
justify-content: space-between;
.current-theme-label {
font-size: 28rpx;
color: var(--wot-color-text-secondary, #666);
}
.current-theme-value {
display: flex;
align-items: center;
.current-color-preview {
width: 40rpx;
height: 40rpx;
border: 2rpx solid var(--wot-color-border, #f0f0f0);
border-radius: 8rpx;
}
.current-color-text {
margin-left: 10rpx;
font-size: 28rpx;
font-weight: 500;
}
}
}
}
.current-theme-item {
display: flex;
align-items: center;
justify-content: space-between;
}
.current-theme-label {
font-size: 28rpx;
color: var(--wot-color-text-secondary, #666);
}
.current-theme-value {
display: flex;
align-items: center;
}
.current-color-preview {
width: 40rpx;
height: 40rpx;
border: 2rpx solid var(--wot-color-border, #f0f0f0);
border-radius: 8rpx;
}
.current-color-text {
margin-left: 10rpx;
font-size: 28rpx;
font-weight: 500;
}
// 效果预览
.preview-text {
font-size: 28rpx;
font-weight: 500;
@@ -510,160 +419,63 @@ onShow(() => {
border-radius: 8rpx;
}
.action-section {
:deep(.wd-card) {
padding: 30rpx;
background: var(--wot-card-bg-color, #fff);
border-radius: 16rpx;
box-shadow: var(--wot-card-shadow, 0 2rpx 12rpx rgba(0, 0, 0, 0.05));
}
}
// 自定义颜色弹窗
.custom-color-popup {
padding: 40rpx 30rpx;
background: var(--wot-popup-bg-color, #fff);
border-radius: 20rpx 20rpx 0 0;
.popup-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 30rpx;
}
.popup-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 30rpx;
.popup-title {
font-size: 32rpx;
font-weight: 600;
color: var(--wot-color-text, #333);
}
.color-input-section {
margin-bottom: 40rpx;
}
.input-label {
margin-bottom: 20rpx;
font-size: 28rpx;
color: var(--wot-color-text-secondary, #666);
}
.input-container {
display: flex;
gap: 20rpx;
align-items: center;
margin-bottom: 10rpx;
}
.color-preview-small {
flex-shrink: 0;
width: 60rpx;
height: 60rpx;
border: 2rpx solid var(--wot-color-border, #f0f0f0);
border-radius: 8rpx;
}
.color-input {
flex: 1;
}
.input-tip {
margin-left: 80rpx;
font-size: 24rpx;
color: var(--wot-color-text-placeholder, #999);
}
.popup-actions {
display: flex;
gap: 20rpx;
}
/* 响应式布局 */
@media (max-width: 750rpx) {
.color-grid {
grid-template-columns: repeat(3, 1fr);
}
}
@media (max-width: 600rpx) {
.color-grid {
grid-template-columns: repeat(2, 1fr);
}
}
// 全局暗黑模式适配(针对当前页面)
:global([data-theme="dark"]) .theme-settings-container {
color: var(--wot-color-text, #fff) !important;
background-color: var(--wot-color-bg, #1a1a1a) !important;
.page-header {
background: linear-gradient(135deg, var(--wot-color-theme, #165dff) 0%, #4a5568 100%);
}
.section-title {
color: var(--wot-color-text, #fff) !important;
}
.color-label {
color: var(--wot-color-text-secondary, #d1d5db) !important;
}
.preview-text {
color: var(--wot-color-text, #fff) !important;
}
.preview-border {
color: var(--wot-color-text-secondary, #d1d5db) !important;
}
.popup-title {
color: var(--wot-color-text, #fff) !important;
}
.input-label {
color: var(--wot-color-text-secondary, #d1d5db) !important;
}
.input-tip {
color: var(--wot-color-text-placeholder, #9ca3af) !important;
}
:deep(.wd-card) {
background: var(--wot-card-bg-color, #2a2a2a) !important;
}
:deep(.wd-cell) {
color: var(--wot-color-text, #fff) !important;
background-color: var(--wot-card-bg-color, #2a2a2a) !important;
.wd-cell__title {
color: var(--wot-color-text, #fff) !important;
}
.wd-cell__value {
color: var(--wot-color-text-secondary, #d1d5db) !important;
}
.wd-cell__right-icon {
color: var(--wot-color-text-secondary, #9ca3af) !important;
.popup-title {
font-size: 32rpx;
font-weight: 600;
color: var(--wot-color-text, #333);
}
}
:deep(.wd-grid-item) {
color: var(--wot-color-text, #fff) !important;
background-color: var(--wot-card-bg-color, #2a2a2a) !important;
.color-input-section {
margin-bottom: 40rpx;
.input-label {
margin-bottom: 20rpx;
font-size: 28rpx;
color: var(--wot-color-text-secondary, #666);
}
.input-container {
display: flex;
gap: 20rpx;
align-items: center;
margin-bottom: 10rpx;
.color-preview-small {
flex-shrink: 0;
width: 60rpx;
height: 60rpx;
border: 2rpx solid var(--wot-color-border, #f0f0f0);
border-radius: 8rpx;
}
.color-input {
flex: 1;
}
}
.input-tip {
margin-left: 80rpx;
font-size: 24rpx;
color: var(--wot-color-text-placeholder, #999);
}
}
// 专门为颜色预览块重置背景色
:deep(.color-item) {
background-color: transparent !important;
}
:deep(.wd-input) {
color: var(--wot-color-text, #fff) !important;
background-color: var(--wot-card-bg-color, #2a2a2a) !important;
}
:deep(.wd-popup) {
background-color: var(--wot-popup-bg-color, #2a2a2a) !important;
}
.custom-color-popup {
background: var(--wot-popup-bg-color, #2a2a2a) !important;
.popup-actions {
display: flex;
gap: 20rpx;
}
}
</style>

View File

@@ -1,49 +1,83 @@
import { defineStore } from "pinia";
import { ref } from "vue";
import { applyThemeToMiniProgram } from "@/utils/theme";
// 从缓存获取主题色
const getThemeColor = (): string => {
const savedColor = uni.getStorageSync("themeColor");
return savedColor || "#165DFF"; // 默认Arco蓝色
};
// 保存主题色到缓存
const setThemeColorCache = (color: string) => {
uni.setStorageSync("themeColor", color);
};
import { useStorage } from "@uni-helper/uni-use";
import type { ThemeColorOption, ThemeMode } from "@/composables/types/theme";
import { themeColorOptions } from "@/composables/types/theme";
export const useThemeStore = defineStore("theme", () => {
// 主题色
const primaryColor = ref<string>(getThemeColor());
const theme = useStorage<ThemeMode>("app-theme", "light");
const currentThemeColor = useStorage<ThemeColorOption>("app-theme-color", themeColorOptions[0]);
// 设置主题色
const setPrimaryColor = (color: string) => {
primaryColor.value = color;
setThemeColorCache(color);
// 主题变量(响应式对象)
const themeVars = reactive({
darkBackground: "#0f0f0f",
darkBackground2: "#1a1a1a",
darkBackground3: "#242424",
darkBackground4: "#2f2f2f",
darkBackground5: "#3d3d3d",
darkBackground6: "#4a4a4a",
darkBackground7: "#606060",
darkColor: "#ffffff",
darkColor2: "#e0e0e0",
darkColor3: "#a0a0a0",
colorTheme: currentThemeColor.value.primary,
});
// 检测运行环境,区分处理
if (typeof document !== "undefined") {
// H5环境
document.documentElement.style.setProperty("--primary-color", color);
// 计算属性
const isDark = computed(() => theme.value === "dark");
// 设置简单的衍生色(不依赖外部工具函数)
document.documentElement.style.setProperty("--primary-color-light", color + "80"); // 添加透明度
document.documentElement.style.setProperty("--primary-color-dark", color);
} else {
// 小程序环境
applyThemeToMiniProgram(color);
}
// 设置导航栏颜色
const setNavigationBarColor = () => {
uni.setNavigationBarColor({
frontColor: theme.value === "light" ? "#000000" : "#ffffff",
backgroundColor: theme.value === "light" ? "#ffffff" : "#000000",
});
};
// 初始化,应用主题色
/**
* 切换主题
* @param mode 指定主题模式,不传则自动切换
*/
const toggleTheme = (mode?: ThemeMode) => {
theme.value = mode || (theme.value === "light" ? "dark" : "light");
setNavigationBarColor();
};
/**
* 设置主题色
* @param color 主题色选项
*/
const setCurrentThemeColor = (color: ThemeColorOption) => {
currentThemeColor.value = color;
themeVars.colorTheme = color.primary;
console.log("主题色已设置:", color.name);
};
/**
* 初始化主题
*/
const initTheme = () => {
setPrimaryColor(primaryColor.value);
// 更新主题变量中的颜色
themeVars.colorTheme = currentThemeColor.value.primary;
// 设置导航栏颜色
nextTick(() => {
setNavigationBarColor();
});
};
return {
primaryColor,
setPrimaryColor,
// 状态
theme,
currentThemeColor,
themeVars,
// 计算属性
isDark,
// 方法
toggleTheme,
setCurrentThemeColor,
setNavigationBarColor,
initTheme,
};
});

View File

@@ -16,12 +16,12 @@ declare global {
const checkLogin: typeof import('../utils/auth')['checkLogin']
const clearAll: typeof import('../utils/storage')['clearAll']
const clearTokens: typeof import('../utils/auth')['clearTokens']
const colorColumns: typeof import('../composables/useTheme')['colorColumns']
const colorColumns: typeof import('../composables/types/theme')['colorColumns']
const computed: typeof import('vue')['computed']
const createApp: typeof import('vue')['createApp']
const createPinia: typeof import('pinia')['createPinia']
const createRouter: typeof import('uni-mini-router')['createRouter']
const currentThemeColor: typeof import('../composables/useTheme')['currentThemeColor']
const currentThemeColor: typeof import('../composables/types/theme')['currentThemeColor']
const customRef: typeof import('vue')['customRef']
const debounce: typeof import('../utils/index')['debounce']
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
@@ -38,7 +38,7 @@ declare global {
const getUserInfo: typeof import('../utils/storage')['getUserInfo']
const guessSerializerType: typeof import('@uni-helper/uni-use')['guessSerializerType']
const h: typeof import('vue')['h']
const initTheme: typeof import('../composables/useTheme')['initTheme']
const initTheme: typeof import('../composables/types/theme')['initTheme']
const inject: typeof import('vue')['inject']
const isLoggedIn: typeof import('../utils/auth')['isLoggedIn']
const isProxy: typeof import('vue')['isProxy']
@@ -99,13 +99,13 @@ declare global {
const ref: typeof import('vue')['ref']
const request: typeof import('../utils/request')['default']
const requireLogin: typeof import('../utils/auth')['requireLogin']
const resetTheme: typeof import('../composables/useTheme')['resetTheme']
const resetTheme: typeof import('../composables/types/theme')['resetTheme']
const resolveComponent: typeof import('vue')['resolveComponent']
const setAccessToken: typeof import('../utils/auth')['setAccessToken']
const setActivePinia: typeof import('pinia')['setActivePinia']
const setMapStoreSuffix: typeof import('pinia')['setMapStoreSuffix']
const setRefreshToken: typeof import('../utils/auth')['setRefreshToken']
const setThemeColor: typeof import('../composables/useTheme')['setThemeColor']
const setThemeColor: typeof import('../composables/types/theme')['setThemeColor']
const setToken: typeof import('../utils/storage')['setToken']
const setUserInfo: typeof import('../utils/storage')['setUserInfo']
const setupStore: typeof import('../store/index')['setupStore']
@@ -114,14 +114,15 @@ declare global {
const shallowRef: typeof import('vue')['shallowRef']
const store: typeof import('../store/index')['store']
const storeToRefs: typeof import('pinia')['storeToRefs']
const theme: typeof import('../composables/useTheme')['theme']
const testThemeSystem: typeof import('../utils/theme-test')['testThemeSystem']
const theme: typeof import('../composables/types/theme')['theme']
const themeColorOptions: typeof import('../composables/useTheme')['themeColorOptions']
const themeVars: typeof import('../composables/useTheme')['themeVars']
const themeVars: typeof import('../composables/types/theme')['themeVars']
const toRaw: typeof import('vue')['toRaw']
const toRef: typeof import('vue')['toRef']
const toRefs: typeof import('vue')['toRefs']
const toValue: typeof import('vue')['toValue']
const toggleTheme: typeof import('../composables/useTheme')['toggleTheme']
const toggleTheme: typeof import('../composables/types/theme')['toggleTheme']
const triggerRef: typeof import('vue')['triggerRef']
const tryOnBackPress: typeof import('@uni-helper/uni-use')['tryOnBackPress']
const tryOnHide: typeof import('@uni-helper/uni-use')['tryOnHide']
@@ -201,8 +202,6 @@ declare module 'vue' {
readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']>
readonly Storage: UnwrapRef<typeof import('../utils/storage')['Storage']>
readonly acceptHMRUpdate: UnwrapRef<typeof import('pinia')['acceptHMRUpdate']>
readonly applyThemeOnPageShow: UnwrapRef<typeof import('../utils/theme')['applyThemeOnPageShow']>
readonly applyThemeToMiniProgram: UnwrapRef<typeof import('../utils/theme')['applyThemeToMiniProgram']>
readonly auth: UnwrapRef<typeof import('../api/auth')['default']>
readonly checkLogin: UnwrapRef<typeof import('../utils/auth')['checkLogin']>
readonly clearAll: UnwrapRef<typeof import('../utils/storage')['clearAll']>
@@ -297,6 +296,7 @@ declare module 'vue' {
readonly shallowRef: UnwrapRef<typeof import('vue')['shallowRef']>
readonly store: UnwrapRef<typeof import('../store/index')['store']>
readonly storeToRefs: UnwrapRef<typeof import('pinia')['storeToRefs']>
readonly themeColorOptions: UnwrapRef<typeof import('../composables/useTheme')['themeColorOptions']>
readonly toRaw: UnwrapRef<typeof import('vue')['toRaw']>
readonly toRef: UnwrapRef<typeof import('vue')['toRef']>
readonly toRefs: UnwrapRef<typeof import('vue')['toRefs']>
@@ -316,6 +316,7 @@ declare module 'vue' {
readonly useStomp: UnwrapRef<typeof import('../composables/useStomp')['useStomp']>
readonly useTabbar: UnwrapRef<typeof import('../composables/useTabbar')['useTabbar']>
readonly useTemplateRef: UnwrapRef<typeof import('vue')['useTemplateRef']>
readonly useTheme: UnwrapRef<typeof import('../composables/useTheme')['useTheme']>
readonly useThemeStore: UnwrapRef<typeof import('../store/modules/theme.store')['useThemeStore']>
readonly useToast: UnwrapRef<typeof import('wot-design-uni')['useToast']>
readonly useUserStore: UnwrapRef<typeof import('../store/modules/user.store')['useUserStore']>

11
src/types/global.d.ts vendored
View File

@@ -1,3 +1,13 @@
// 全局类型声明文件
// 自动引入 virtual 模块类型
/// <reference types="@uni-helper/vite-plugin-uni-pages/client" />
/// <reference types="@dcloudio/types" />
/// <reference types="@uni-helper/uni-types" />
// 如果需要其他 virtual 模块类型,可以在这里添加
// 例如:/// <reference types="xxx/client" />
declare global {
/**
* 分页查询参数
@@ -38,4 +48,5 @@ declare global {
msg: string;
}
}
export {};

View File

@@ -1,12 +0,0 @@
import "uni-mini-router";
declare module "uni-mini-router" {
interface Route {
path: string;
fullPath: string;
meta?: {
requireAuth?: boolean;
[key: string]: any;
};
}
}

35
src/types/uni-pages.d.ts vendored Normal file
View File

@@ -0,0 +1,35 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// Generated by vite-plugin-uni-pages
interface NavigateToOptions {
url: "/pages/index/index" |
"/pages/login/index" |
"/pages/mine/index" |
"/pages/work/index" |
"/pages/mine/about/index" |
"/pages/mine/faq/index" |
"/pages/mine/feedback/index" |
"/pages/mine/profile/complete-profile" |
"/pages/mine/profile/index" |
"/pages/mine/settings/index" |
"/pages/mine/settings/account/index" |
"/pages/mine/settings/agreement/index" |
"/pages/mine/settings/network/index" |
"/pages/mine/settings/theme/index";
}
interface RedirectToOptions extends NavigateToOptions {}
interface SwitchTabOptions {
url: "/pages/index/index" | "/pages/mine/index"
}
type ReLaunchOptions = NavigateToOptions | SwitchTabOptions;
declare interface Uni {
navigateTo(options: UniNamespace.NavigateToOptions & NavigateToOptions): void;
redirectTo(options: UniNamespace.RedirectToOptions & RedirectToOptions): void;
switchTab(options: UniNamespace.SwitchTabOptions & SwitchTabOptions): void;
reLaunch(options: UniNamespace.ReLaunchOptions & ReLaunchOptions): void;
}

View File

@@ -1,16 +0,0 @@
declare module "virtual:uni-pages" {
interface Page {
path: string;
style?: Record<string, any>;
[key: string]: any;
}
interface SubPackage {
root: string;
pages: Page[];
[key: string]: any;
}
export const pages: Page[];
export const subPackages: SubPackage[];
}

View File

@@ -74,69 +74,3 @@ $uni-color-subtitle: #555; // 二级标题颜色
$uni-font-size-subtitle: 18px;
$uni-color-paragraph: #3f536e; // 文章段落颜色
$uni-font-size-paragraph: 15px;
/* 暗黑模式全局样式 */
.wot-theme-dark {
color: #f5f5f5 !important;
background-color: #1a1a1a !important;
/* 页面背景 */
page {
color: #f5f5f5 !important;
background-color: #1a1a1a !important;
}
/* H5 环境 body 样式 */
body {
color: #f5f5f5 !important;
background-color: #1a1a1a !important;
}
/* 通用组件暗黑模式适配 */
.uni-page-wrapper {
color: #f5f5f5 !important;
background-color: #1a1a1a !important;
}
/* 导航栏暗黑模式 */
.uni-navbar {
color: #f5f5f5 !important;
background-color: #2a2a2a !important;
border-bottom-color: #404040 !important;
}
/* 标签栏暗黑模式 */
.uni-tabbar {
background-color: #2a2a2a !important;
border-top-color: #404040 !important;
}
/* 卡片组件暗黑模式 */
.uni-card {
color: #f5f5f5 !important;
background-color: #2a2a2a !important;
}
/* 列表项暗黑模式 */
.uni-list-item {
color: #f5f5f5 !important;
background-color: #2a2a2a !important;
border-bottom-color: #404040 !important;
}
/* 输入框暗黑模式 */
.uni-input {
color: #f5f5f5 !important;
background-color: #404040 !important;
border-color: #606060 !important;
}
/* 按钮暗黑模式适配 */
.uni-button {
&.uni-button-default {
color: #f5f5f5 !important;
background-color: #404040 !important;
border-color: #606060 !important;
}
}
}

View File

@@ -1,33 +0,0 @@
/**
* 小程序主题工具类
* 用于解决小程序环境中CSS变量不能动态设置的问题
*/
// 注入小程序环境的全局样式
export function applyThemeToMiniProgram(primaryColor: string) {
// 确保在小程序环境中执行
if (typeof document !== "undefined") return;
try {
// 设置TabBar样式
uni.setTabBarStyle({
color: "#000000",
selectedColor: primaryColor,
backgroundColor: "#ffffff",
borderStyle: "black",
});
console.log("小程序主题色已应用:", primaryColor);
} catch (error) {
console.error("应用小程序主题色失败:", error);
}
}
// 在页面展示时应用主题
export function applyThemeOnPageShow(primaryColor: string) {
// 各平台小程序可能需要不同处理
const platform = uni.getSystemInfoSync().platform;
console.log(`当前平台: ${platform}, 应用主题色: ${primaryColor}`);
// 某些平台可能需要特定处理
}

View File

@@ -13,7 +13,7 @@
"@/*": ["./src/*"]
},
"lib": ["esnext", "dom"],
"types": ["@dcloudio/types", "@uni-helper/uni-types", "wot-design-uni/global", "./src/types/vite-plugin-uni-pages"]
"types": ["@dcloudio/types", "@uni-helper/uni-types", "wot-design-uni/global"]
},
"vueCompilerOptions": {
// 调整 VolarVue 语言服务工具)解析行为,用于为 uni-app 组件提供 TypeScript 类型

View File

@@ -36,8 +36,15 @@ export default defineConfig(async ({ mode }: ConfigEnv): Promise<UserConfig> =>
// make sure put it before `Uni()`
UnoCss(),
UniLayouts(),
UniPages(),
UniPages({
dts: "src/types/uni-pages.d.ts",
subPackages: ["src/subPages"],
/**
* 排除的页面,相对于 dir 和 subPackages
* @default []
*/
exclude: ["**/components/**/*.*"],
}),
Components({
resolvers: [WotResolver()],
}),