diff --git a/.env.development b/.env.development index ac161a7..7bf5903 100644 --- a/.env.development +++ b/.env.development @@ -8,3 +8,5 @@ VITE_APP_BASE_API = '/dev-api' # API 服务器的 URL VITE_APP_API_URL = https://api.youlai.tech + +VITE_APP_WS_ENDPOINT= ws://localhost:4096/ws \ No newline at end of file diff --git a/README-theme.md b/README-theme.md new file mode 100644 index 0000000..4493fbd --- /dev/null +++ b/README-theme.md @@ -0,0 +1,190 @@ +# Wot Design Uni 主题系统使用指南 + +## 概述 + +本项目基于 Wot Design Uni 的 CSS 变量系统实现了完整的主题定制功能,支持: + +- 🌙 暗黑模式切换 +- 🎨 12 种预设主题色 +- 🎯 自定义主题色 +- 💾 持久化存储 +- 📱 多平台兼容(H5、小程序) + +## 核心文件 + +### 1. 主题变量文件 + +- `src/styles/wot-theme.scss` - Wot Design Uni CSS 变量定制 +- `src/uni.scss` - 引入主题变量文件 + +### 2. 主题 Composable + +- `src/composables/useTheme.ts` - 主题状态管理和工具函数 + +### 3. 主题设置页面 + +- `src/pages/mine/settings/theme/index.vue` - 主题设置界面 +- `src/pages/test-wot-theme.vue` - 主题测试页面 + +## 使用方法 + +### 1. 在组件中使用主题 + +```vue + + + + + +``` + +### 2. 可用的 CSS 变量 + +#### 主题色系 + +- `--wot-color-theme` - 主题色 +- `--wot-color-success` - 成功色 +- `--wot-color-warning` - 警告色 +- `--wot-color-danger` - 危险色 +- `--wot-color-info` - 信息色 + +#### 文本颜色 + +- `--wot-color-text` - 主要文本色 +- `--wot-color-text-secondary` - 次要文本色 +- `--wot-color-text-placeholder` - 占位符文本色 + +#### 背景颜色 + +- `--wot-color-bg` - 主背景色 +- `--wot-color-bg-light` - 浅背景色 +- `--wot-card-bg-color` - 卡片背景色 + +#### 边框颜色 + +- `--wot-color-border` - 边框色 +- `--wot-color-border-light` - 浅边框色 + +### 3. 预设主题色 + +项目提供 12 种预设主题色: + +```javascript +const colorColumns = [ + { value: "#165DFF", label: "蓝色" }, + { value: "#0FC6C2", label: "青绿色" }, + { value: "#722ED1", label: "紫色" }, + { value: "#F5222D", label: "红色" }, + { value: "#FA8C16", label: "橙色" }, + { value: "#FADB14", label: "黄色" }, + { value: "#52C41A", label: "绿色" }, + { value: "#EB2F96", label: "粉色" }, + { value: "#13C2C2", label: "青色" }, + { value: "#1890FF", label: "天蓝色" }, + { value: "#CD5C5C", label: "经典红" }, + { value: "#228B22", label: "自然绿" }, +]; +``` + +## 暗黑模式适配 + +### 自动适配 + +所有使用 Wot CSS 变量的组件都会自动适配暗黑模式。 + +### 手动适配 + +对于自定义组件,使用 CSS 变量即可: + +```scss +.my-component { + background-color: var(--wot-card-bg-color, #fff); + color: var(--wot-color-text, #333); + border: 1px solid var(--wot-color-border, #e5e6eb); +} +``` + +## 测试页面 + +访问 `/pages/test-wot-theme` 可以测试: + +- 暗黑模式切换 +- 主题色切换 +- CSS 变量效果 +- 各种 Wot 组件的主题适配 + +## 注意事项 + +1. **CSS 变量优先级**:使用 `var(--variable-name, fallback)` 格式提供回退值 +2. **暗黑模式检测**:通过 `[data-theme="dark"]` 选择器自动切换 +3. **持久化存储**:主题设置会自动保存到本地存储 +4. **平台兼容**:H5 和小程序环境都有相应的适配处理 + +## 扩展主题 + +### 添加新的主题色 + +1. 在 `colorColumns` 数组中添加新颜色 +2. 在 `wot-theme.scss` 中添加对应的 CSS 类 + +```scss +.theme-custom { + --wot-color-theme: #your-color; +} +``` + +### 自定义 CSS 变量 + +在 `wot-theme.scss` 中添加新的变量: + +```scss +:root { + --wot-custom-color: #your-color; +} + +[data-theme="dark"] { + --wot-custom-color: #your-dark-color; +} +``` + +## 故障排除 + +1. **主题色不生效**:检查是否正确引入了 `wot-theme.scss` +2. **暗黑模式样式异常**:确认使用了正确的 CSS 变量 +3. **小程序兼容问题**:检查条件编译是否正确 + +## 更新日志 + +- ✅ 修复了暗黑模式切换无效的问题 +- ✅ 完善了 Wot Design Uni CSS 变量系统 +- ✅ 优化了主题设置页面的 UI 组件 +- ✅ 添加了完整的主题测试页面 diff --git a/components.d.ts b/components.d.ts index 3f5fa27..1560d44 100644 --- a/components.d.ts +++ b/components.d.ts @@ -11,7 +11,6 @@ declare module 'vue' { CuDict: typeof import('./src/components/cu-dict/index.vue')['default'] CuDictLabel: typeof import('./src/components/cu-dict-label/index.vue')['default'] CuPicker: typeof import('./src/components/cu-picker/index.vue')['default'] - DaTree: typeof import('./src/components/da-tree/index.vue')['default'] Loading1: typeof import('./src/components/qiun-loading/loading1.vue')['default'] Loading2: typeof import('./src/components/qiun-loading/loading2.vue')['default'] Loading3: typeof import('./src/components/qiun-loading/loading3.vue')['default'] @@ -27,10 +26,11 @@ declare module 'vue' { 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'] WdConfigProvider: typeof import('wot-design-uni/components/wd-config-provider/wd-config-provider.vue')['default'] - WdForm: typeof import('wot-design-uni/components/wd-form/wd-form.vue')['default'] + WdDivider: typeof import('wot-design-uni/components/wd-divider/wd-divider.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'] + WdInput: typeof import('wot-design-uni/components/wd-input/wd-input.vue')['default'] WdMessageBox: typeof import('wot-design-uni/components/wd-message-box/wd-message-box.vue')['default'] WdNavbar: typeof import('wot-design-uni/components/wd-navbar/wd-navbar.vue')['default'] WdNoticeBar: typeof import('wot-design-uni/components/wd-notice-bar/wd-notice-bar.vue')['default'] diff --git a/docs/theme-system-guide.md b/docs/theme-system-guide.md new file mode 100644 index 0000000..1467e05 --- /dev/null +++ b/docs/theme-system-guide.md @@ -0,0 +1,311 @@ +# 主题系统使用指南 + +## 概述 + +本项目基于 Wot Design Uni 的 ConfigProvider 组件实现了完整的主题系统,支持: + +- 🌙 暗黑/浅色模式切换 +- 🎨 12种预设主题色 +- 🎯 自定义主题色 +- 💾 主题设置持久化存储 +- 📱 多平台兼容(H5、小程序) + +## 核心文件 + +### 1. useTheme Composable (`src/composables/useTheme.ts`) + +主题管理的核心逻辑,提供: + +```typescript +const { + theme, // 当前主题模式 'light' | 'dark' + themeVars, // ConfigProviderThemeVars 主题变量 + toggleTheme, // 切换主题模式 + setThemeColor, // 设置主题色 + resetTheme, // 重置主题 + initTheme, // 初始化主题 + colorColumns, // 预设主题色列表 +} = useTheme(); +``` + +### 2. 布局文件 + +#### Tabbar 布局 (`src/layouts/tabbar.vue`) + +```vue + +``` + +#### Default 布局 (`src/layouts/default.vue`) + +```vue + +``` + +### 3. 主题设置页面 (`src/pages/mine/settings/theme/index.vue`) + +提供用户界面来: + +- 切换暗黑/浅色模式 +- 选择预设主题色 +- 输入自定义主题色 +- 预览主题效果 +- 重置主题设置 + +## 主题变量 + +### 支持的 ConfigProviderThemeVars + +根据 Wot Design Uni 文档,主要使用: + +```typescript +interface ConfigProviderThemeVars { + colorTheme?: string; // 主题色 + buttonPrimaryBgColor?: string; // 主按钮背景色 + buttonPrimaryColor?: string; // 主按钮文字色 + // ... 更多变量 +} +``` + +### 预设主题色 + +```typescript +const colorColumns = [ + { value: "#165DFF", label: "蓝色" }, + { value: "#0FC6C2", label: "青绿色" }, + { value: "#722ED1", label: "紫色" }, + { value: "#F5222D", label: "红色" }, + { value: "#FA8C16", label: "橙色" }, + { value: "#FADB14", label: "黄色" }, + { value: "#52C41A", label: "绿色" }, + { value: "#EB2F96", label: "粉色" }, + { value: "#13C2C2", label: "青色" }, + { value: "#1890FF", label: "天蓝色" }, + { value: "#CD5C5C", label: "经典红" }, + { value: "#228B22", label: "自然绿" }, +]; +``` + +## 暗黑模式实现 + +### 1. ConfigProvider 主题切换 + +```vue + + + +``` + +### 2. 全局样式适配 + +在 `src/uni.scss` 中定义: + +```scss +.wot-theme-dark { + background-color: #1a1a1a !important; + color: #f5f5f5 !important; + + /* H5 环境 body 样式 */ + body { + background-color: #1a1a1a !important; + color: #f5f5f5 !important; + } + + /* 其他组件暗黑模式适配 */ +} +``` + +### 3. 动态 Body 样式 + +在 `useTheme` 中自动处理: + +```typescript +const applyDarkModeBodyStyle = (isDark: boolean) => { + // #ifdef H5 + if (typeof document !== "undefined") { + const body = document.body; + if (isDark) { + body.style.backgroundColor = "#1a1a1a"; + body.style.color = "#f5f5f5"; + body.classList.add("wot-theme-dark"); + } else { + body.style.backgroundColor = "#f8f8f8"; + body.style.color = "#333"; + body.classList.remove("wot-theme-dark"); + } + } + // #endif +}; +``` + +## 持久化存储 + +主题设置自动保存到本地存储: + +```typescript +// 存储键名 +const THEME_STORAGE_KEY = "app_theme_mode"; +const THEME_COLOR_STORAGE_KEY = "app_theme_color"; + +// 自动监听变化并保存 +watch( + theme, + (newTheme) => { + saveThemeToStorage(newTheme); + applyDarkModeBodyStyle(newTheme === "dark"); + }, + { immediate: true } +); +``` + +## 使用示例 + +### 在页面中使用主题 + +```vue + + + +``` + +### 在组件中响应主题变化 + +```vue + + + + + +``` + +## 最佳实践 + +### 1. 组件开发 + +- 使用 Wot Design Uni 组件时,主题色会自动应用 +- 自定义组件需要手动适配暗黑模式 +- 使用 `:class="{ 'wot-theme-dark': theme === 'dark' }"` 来应用暗黑模式样式 + +### 2. 样式编写 + +```scss +.my-component { + background-color: #fff; + color: #333; + + // 暗黑模式适配 + .wot-theme-dark & { + background-color: #2a2a2a; + color: #f5f5f5; + } +} +``` + +### 3. 主题色使用 + +```vue + +``` + +## 注意事项 + +1. **ConfigProvider 包裹**:确保页面被 ConfigProvider 包裹才能应用主题 +2. **样式优先级**:暗黑模式样式需要足够的优先级,必要时使用 `!important` +3. **平台兼容**:小程序和 H5 的样式处理略有不同,使用条件编译处理 +4. **性能考虑**:主题切换时避免频繁的 DOM 操作 + +## 扩展功能 + +### 添加新的主题色 + +在 `colorColumns` 中添加新的颜色: + +```typescript +export const colorColumns = [ + // ... 现有颜色 + { value: "#FF6B6B", label: "珊瑚红" }, + { value: "#4ECDC4", label: "薄荷绿" }, +]; +``` + +### 添加更多主题变量 + +```typescript +const themeVars = ref({ + colorTheme: getStoredThemeColor(), + buttonPrimaryBgColor: getStoredThemeColor(), + // 添加更多变量 +}); +``` + +### 自定义暗黑模式样式 + +在 `uni.scss` 中添加更多组件的暗黑模式适配: + +```scss +.wot-theme-dark { + // 新组件的暗黑模式样式 + .my-custom-component { + background-color: #2a2a2a; + color: #f5f5f5; + } +} +``` diff --git a/src/App.vue b/src/App.vue index 3f5185a..39fedec 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,14 +1,14 @@ - - diff --git a/src/components/da-tree/props.ts b/src/components/da-tree/props.ts deleted file mode 100644 index 4b710df..0000000 --- a/src/components/da-tree/props.ts +++ /dev/null @@ -1,197 +0,0 @@ -export default { - /** - * 树的数据 - */ - data: { - type: Array, - default: () => [], - }, - /** - * 主题色 - */ - themeColor: { - type: String, - default: "#007aff", - }, - /** - * 是否开启多选,默认单选 - */ - showCheckbox: { - type: Boolean, - default: false, - }, - /** - * 默认选中的节点,注意单选时为单个key,多选时为key的数组 - */ - defaultCheckedKeys: { - type: [Array, String, Number], - default: null, - }, - /** - * 是否默认展开全部 - */ - defaultExpandAll: { - type: Boolean, - default: false, - }, - /** - * 默认展开的节点 - */ - defaultExpandedKeys: { - type: Array, - default: null, - }, - /** - * 筛选关键词 - */ - filterValue: { - type: String, - default: "", - }, - /** - * 是否自动展开到选中的节点,默认不展开 - */ - expandChecked: { - type: Boolean, - default: false, - }, - - /** - * (旧)字段对应内容,默认为 {label: 'label',key: 'key', children: 'children', disabled: 'disabled', append: 'append'} - * 注意:1.5.0版本后不再兼容 - */ - field: { - type: Object, - default: null, - }, - /** - * 标签字段(新,拆分了) - */ - labelField: { - type: String, - default: "label", - }, - /** - * 值字段(新,拆分了) - */ - valueField: { - type: String, - default: "value", - }, - /** - * 下级字段(新,拆分了) - */ - childrenField: { - type: String, - default: "children", - }, - /** - * 禁用字段(新,拆分了) - */ - disabledField: { - type: String, - default: "disabled", - }, - /** - * 末级节点字段(新,拆分了) - */ - leafField: { - type: String, - default: "leaf", - }, - /** - * 副标签字段(新,拆分了) - */ - appendField: { - type: String, - default: "append", - }, - /** - * 排序字段(新,拆分了) - */ - sortField: { - type: String, - default: "sort", - }, - /** - * Api数据返回后的结果路径,支持嵌套如`data.list` - */ - resultField: { - type: String, - default: "", - }, - isLeafFn: { - type: Function, - default: null, - }, - /** - * 是否显示单选图标,默认显示 - */ - showRadioIcon: { - type: Boolean, - default: true, - }, - /** - * 单选时只允许选中末级,默认可随意选中 - */ - onlyRadioLeaf: { - type: Boolean, - default: false, - }, - /** - * 多选时,是否执行父子不关联的任意勾选,默认父子关联 - */ - checkStrictly: { - type: Boolean, - default: false, - }, - /** - * 为 true 时,空的 children 数组会显示展开图标 - */ - loadMode: { - type: Boolean, - default: false, - }, - /** - * 异步加载接口 - */ - loadApi: { - type: Function, - default: null, - }, - /** - * 是否总在首次的时候加载一下内容,来比对是否一致 - */ - alwaysFirstLoad: { - type: Boolean, - default: false, - }, - /** - * 是否渲染(操作)禁用值 - */ - checkedDisabled: { - type: Boolean, - default: false, - }, - /** - * 是否返回已禁用的但已选中的key - */ - packDisabledkey: { - type: Boolean, - default: true, - }, - /** - * 选择框的位置,可选 left/right - */ - checkboxPlacement: { - type: String, - default: "left", - }, - /** - * 子项缩进距离,默认40,单位rpx - */ - indent: { - type: Number, - default: 40, - }, -}; diff --git a/src/components/da-tree/readme.md b/src/components/da-tree/readme.md deleted file mode 100644 index fa3f318..0000000 --- a/src/components/da-tree/readme.md +++ /dev/null @@ -1,310 +0,0 @@ -# da-tree - -一个基于 Vue3 的 tree(树)组件,同时支持主题换色,可能是最适合你的 tree(树)组件 - -组件一直在更新,遇到问题可在下方讨论。 - -`同时更新 Vue2 版本,在此查看 ===>` **[Vue2 版](https://ext.dcloud.net.cn/plugin?id=12692)** - -### 关于使用 - -可在右侧的`使用 HBuilderX 导入插件`或`下载示例项目ZIP`,方便快速上手。 - -可通过下方的示例及文档说明,进一步了解使用组件相关细节参数。 - -插件地址:https://ext.dcloud.net.cn/plugin?id=12384 - -### 组件示例 - -```jsx - -``` - -```js -import { defineComponent, ref } from "vue"; - -/** - * 模拟创建一个接口数据 - */ -function GetApiData(currentNode) { - const { key } = currentNode; - - return new Promise((resolve) => { - setTimeout(() => { - // 模拟返回空数据 - if (key.indexOf("-") > -1) { - return resolve(null); - // return resolve([]) - } - - return resolve([ - { - id: `${key}-1`, - name: `行政部X${key}-1`, - }, - { - id: `${key}-2`, - name: `财务部X${key}-2`, - append: "定义了末项数据", - leaf: true, - }, - { - id: `${key}-3`, - name: `资源部X${key}-3`, - }, - { - id: `${key}-4`, - name: `资源部X${key}-3`, - append: "被禁用,无展开图标", - disabled: true, - }, - ]); - }, 2000); - }); -} - -import DaTree from "@/components/da-tree/index.vue"; -export default defineComponent({ - components: { DaTree }, - setup() { - const DaTreeRef = ref(); - // key的类型必须对应树数据key的类型 - const defaultCheckedKeysValue = ref(["211", "222"]); - const defaultCheckedKeysValue2 = ref("222"); - const defaultExpandKeysValue3 = ref(["212", "231"]); - const roomTreeData = ref([ - { - id: "2", - name: "行政中心", - children: [ - { - id: "21", - name: "行政部", - children: [ - { - id: "211", - name: "行政一部", - children: null, - }, - { - id: "212", - name: "行政二部", - children: [], - disabled: true, - }, - ], - }, - { - id: "22", - name: "财务部", - children: [ - { - id: "221", - name: "财务一部", - children: [], - disabled: true, - }, - { - id: "222", - name: "财务二部", - children: [], - }, - ], - }, - { - id: "23", - name: "人力资源部", - children: [ - { - id: "231", - name: "人力一部", - children: [], - }, - { - id: "232", - name: "人力二部", - append: "更多示例,请下载示例项目查看", - }, - ], - }, - ], - }, - ]); - function doExpandTree(keys, expand) { - DaTreeRef.value?.setExpandedKeys(keys, expand); - - const gek = DaTreeRef.value?.getExpandedKeys(); - console.log("当前已展开的KEY ==>", gek); - } - function doCheckedTree(keys, checked) { - DaTreeRef.value?.setCheckedKeys(keys, checked); - - const gek = DaTreeRef.value?.getCheckedKeys(); - console.log("当前已选中的KEY ==>", gek); - } - function handleTreeChange(allSelectedKeys, currentItem) { - console.log("handleTreeChange ==>", allSelectedKeys, currentItem); - } - function handleExpandChange(expand, currentItem) { - console.log("handleExpandChange ==>", expand, currentItem); - } - return { - DaTreeRef, - roomTreeData, - defaultCheckedKeysValue, - defaultCheckedKeysValue2, - defaultExpandKeysValue3, - handleTreeChange, - handleExpandChange, - GetApiData, - doExpandTree, - doCheckedTree, - }; - }, -}); -``` - -** 更多示例请下载/导入示例项目 ZIP 查看 ** - -### 组件参数 - -| 属性 | 类型 | 默认值 | 必填 | 说明 | -| :------------------ | :------------------------------ | :--------- | :--- | :--------------------------------------------------------------------------- | -| data | `Array` | - | 是 | 树的数据 | -| themeColor | `String` | `#007aff` | 否 | 主题色,十六进制 | -| defaultCheckedKeys | `Array` \| `Number` \| `String` | - | 否 | 默认选中的节点,单选为单个 key,多选为 key 的数组 | -| showCheckbox | `Boolean` | `false` | 否 | 是否开启多选,默认单选 | -| checkStrictly | `Boolean` | `false` | 否 | 多选时,是否执行父子不关联的任意勾选,默认父子关联 | -| showRadioIcon | `Boolean` | `true` | 否 | 是否显示单选图标,默认显示 | -| onlyRadioLeaf | `Boolean` | `true` | 否 | 单选时只允许选中末级,默认可随意选中 | -| defaultExpandAll | `Boolean` | `false` | 否 | 是否默认展开全部 | -| defaultExpandedKeys | `Array` | - | 否 | 默认展开的节点 | -| indent | `Number` | `40` | 否 | 子项缩进距离,单位 rpx | -| checkboxPlacement | `String` | `left` | 否 | 选择框的位置,可选 left/right | -| loadMode | `Boolean` | `false` | 否 | 为 true 时,空的 children 数组会显示展开图标 | -| loadApi | `Function` | - | 否 | 选择框的位置,可选 left/right | -| checkedDisabled | `Boolean` | `false` | 否 | 是否渲染禁用值,默认不渲染 | -| packDisabledkey | `Boolean` | `true` | 否 | 是否返回已禁用的但已选中的 key,默认返回禁用已选值 | -| expandChecked | `Boolean` | `false` | 否 | 是否自动展开到选中的节点,默认不展开 | -| alwaysFirstLoad | `Boolean` | `false` | 否 | 是否总在首次的时候加载一下内容,默认不加载,否则只有展开末级节点才会加载数据 | -| isLeafFn | `Function` | - | 否 | 自定义函数返回来控制数据项的末项 | -| field | `Object` | - | 否 | 字段对应内容,格式参考下方(1.5.0 后移除,请用单独的字段匹配) | -| labelField | `String` | `label` | 否 | 指定节点对象中某个属性为标签字段,默认`label` | -| valueField | `String` | `value` | 否 | 指定节点对象中某个属性为值字段,默认`value` | -| childrenField | `String` | `children` | 否 | 指定节点对象中某个属性为子树节点字段,默认`children` | -| disabledField | `String` | `disabled` | 否 | 指定节点对象中某个属性为禁用字段,默认`disabled` | -| appendField | `String` | `append` | 否 | 指定节点对象中某个属性为副标签字段,默认`append` | -| leafField | `String` | `leaf` | 否 | 指定节点对象中某个属性为末级节点字段,默认`leaf` | -| sortField | `String` | `sort` | 否 | 指定节点对象中某个属性为排序字段,默认`sort` | -| filterValue | `String` | - | 否 | 搜索筛选的关键词,通过输入关键词筛选内容 | - -**field 格式(1.5.0 后移除,请用单独的字段匹配)** - -```js -{ - label: 'label', - key: 'key', - children: 'children', - disabled: 'disabled', - append: 'append' -} -``` - -### 组件事件 - -| 事件名称 | 回调参数 | 说明 | -| :------- | :-------------------------------------- | :-------------- | -| change | `(allCheckedKeys, currentItem) => void` | 选中时回调 | -| expand | `(expandState, currentItem) => void` | 展开/收起时回调 | - -### 组件方法 - -| 方法名称 | 参数 | 说明 | -| :------------------ | :--------------- | :------------------------------------------------------------------------------------------------ | -| setCheckedKeys | `(keys,checked)` | 设置指定 key 的节点选中/取消选中的状态。注: keys 单选时为 key,多选时为 key 的数组 | -| setExpandedKeys | `(keys,expand)` | 设置指定 key 的节点展开/收起的状态,当 keys 为 all 时即代表展开/收起全部。注:keys 为数组或 `all` | -| getCheckedKeys | - | 返回已选的 key | -| getHalfCheckedKeys | - | 返回半选的 key | -| getUncheckedKeys | - | 返回未选的 key | -| getCheckedNodes | - | 返回已选的节点 | -| getUncheckedNodes | - | 返回未选的节点 | -| getHalfCheckedNodes | - | 返回半选的节点 | -| getExpandedKeys | - | 返回已展开的 key | -| getUnexpandedKeys | - | 返回未展开的 key | -| getExpandedNodes | - | 返回已展开的节点 | -| getUnexpandedNodes | - | 返回未展开的节点 | - -### 组件版本 - -v1.4.2 - -### 差异化 - -已通过测试 - -> - H5 页面 -> - 微信小程序 -> - 支付宝、钉钉小程序 -> - 字节跳动、抖音、今日头条小程序 -> - 百度小程序 -> - 飞书小程序 -> - QQ 小程序 -> - 京东小程序 - -未测试 - -> - 快手小程序由于非企业用户暂无演示 -> - 快应用、360 小程序因 Vue3 支持的原因暂无演示 - -### 开发组 - -[@CRLANG](https://crlang.com) diff --git a/src/components/da-tree/utils.ts b/src/components/da-tree/utils.ts deleted file mode 100644 index 5a6ca0b..0000000 --- a/src/components/da-tree/utils.ts +++ /dev/null @@ -1,153 +0,0 @@ -/** 未选 */ -export const unCheckedStatus = 0; -/** 半选 */ -export const halfCheckedStatus = 1; -/** 选中 */ -export const isCheckedStatus = 2; - -/** - * 深拷贝内容 - * @param originData 拷贝对象 - * @author crlang(https://crlang.com) - */ -export function deepClone(originData: any): any { - const type = Object.prototype.toString.call(originData); - let data: any; - if (type === "[object Array]") { - data = []; - for (let i = 0; i < originData.length; i++) { - data.push(deepClone(originData[i])); - } - } else if (type === "[object Object]") { - data = {} as Record; - for (const prop in originData) { - if (Object.prototype.hasOwnProperty.call(originData, prop)) { - // 非继承属性 - data[prop] = deepClone(originData[prop]); - } - } - } else { - data = originData; - } - return data; -} - -/** - * 获取所有指定的节点 - * @param list 列表 - * @param type 类型 - * @param value 值 - * @param packDisabledkey 是否包含禁用节点 - * @author crlang(https://crlang.com) - */ -export function getAllNodes(list: any[], type: string, value: any, packDisabledkey = true): any[] { - if (!list || list.length === 0) { - return []; - } - - const res = []; - for (let i = 0; i < list.length; i++) { - const item = list[i]; - if (item[type] === value) { - if ((packDisabledkey && item.disabled) || !item.disabled) { - res.push(item); - } - } - } - - return res; -} - -/** - * 获取所有指定的key值 - * @param list 列表 - * @param type 类型 - * @param value 值 - * @param packDisabledkey 是否包含禁用节点 - * @author crlang(https://crlang.com) - */ -export function getAllNodeKeys( - list: any[], - type: string, - value: any, - packDisabledkey = true -): string[] | null { - if (!list || list.length === 0) { - return null; - } - - const res: string[] = []; - for (let i = 0; i < list.length; i++) { - const item = list[i]; - if (item[type] === value) { - if ((packDisabledkey && item.disabled) || !item.disabled) { - res.push(item.key); - } - } - } - - return res.length ? res : null; -} - -/** - * 错误输出 - * @param msg 错误消息 - * @param args 附加参数 - */ -export function logError(msg: string, ...args: any[]): void { - console.error(`DaTree: ${msg}`, ...args); -} - -const toString = Object.prototype.toString; - -export function is(val: any, type: string): boolean { - return toString.call(val) === `[object ${type}]`; -} - -/** - * 是否对象(Object) - * @param val 值 - */ -export function isObject(val: any): boolean { - return val !== null && is(val, "Object"); -} - -/** - * 是否数字(Number) - * @param val 值 - */ -export function isNumber(val: any): boolean { - return is(val, "Number"); -} - -/** - * 是否字符串(String) - * @param val 值 - */ -export function isString(val: any): boolean { - return is(val, "String"); -} - -/** - * 是否函数方法(Function) - * @param val 值 - */ -export function isFunction(val: any): boolean { - return typeof val === "function"; -} - -/** - * 是否布尔(Boolean) - * @param val 值 - */ -export function isBoolean(val: any): boolean { - return is(val, "Boolean"); -} - -/** - * 是否数组(Array) - * @param val 值 - */ -export function isArray(val: any): boolean { - return val && Array.isArray(val); -} diff --git a/src/composables/useTheme.ts b/src/composables/useTheme.ts index b5ab3ee..2f7ea7c 100644 --- a/src/composables/useTheme.ts +++ b/src/composables/useTheme.ts @@ -1,59 +1,130 @@ -import { ref, computed } from "vue"; +import { ref, watch } from "vue"; import type { ConfigProviderThemeVars } from "wot-design-uni"; -/* 默认的主题list */ +/* 默认的主题色列表 */ export const colorColumns = [ - { - value: "#0055FE", - label: "蓝色", - }, - { - value: "#CD5C5C", - label: "红色", - }, - { - value: "#228B22", - label: "绿色", - }, + { value: "#165DFF", label: "蓝色" }, + { value: "#0FC6C2", label: "青绿色" }, + { value: "#722ED1", label: "紫色" }, + { value: "#F5222D", label: "红色" }, + { value: "#FA8C16", label: "橙色" }, + { value: "#FADB14", label: "黄色" }, + { value: "#52C41A", label: "绿色" }, + { value: "#EB2F96", label: "粉色" }, + { value: "#13C2C2", label: "青色" }, + { value: "#1890FF", label: "天蓝色" }, + { value: "#CD5C5C", label: "经典红" }, + { value: "#228B22", label: "自然绿" }, ]; -/* 默认的主题 */ -export const initThemState = "light"; +/* 存储键名 */ +const THEME_STORAGE_KEY = "app_theme_mode"; +const THEME_COLOR_STORAGE_KEY = "app_theme_color"; -/* 默认的主题变量 - 只使用wot-design-uni支持的变量 */ -export const initThemeVars: ConfigProviderThemeVars = { - colorTheme: colorColumns[0].value, +/* 从存储中获取主题模式 */ +const getStoredTheme = (): "light" | "dark" => { + try { + const stored = uni.getStorageSync(THEME_STORAGE_KEY); + return stored === "dark" ? "dark" : "light"; + } catch { + return "light"; + } }; -/* 暗黑模式主题变量 */ -export const darkThemeVars: ConfigProviderThemeVars = { - colorTheme: colorColumns[0].value, +/* 从存储中获取主题色 */ +const getStoredThemeColor = (): string => { + try { + const stored = uni.getStorageSync(THEME_COLOR_STORAGE_KEY); + return stored || colorColumns[0].value; + } catch { + return colorColumns[0].value; + } +}; + +/* 保存主题模式到存储 */ +const saveThemeToStorage = (theme: "light" | "dark") => { + try { + uni.setStorageSync(THEME_STORAGE_KEY, theme); + } catch (error) { + console.error("保存主题模式失败:", error); + } +}; + +/* 保存主题色到存储 */ +const saveThemeColorToStorage = (color: string) => { + try { + uni.setStorageSync(THEME_COLOR_STORAGE_KEY, color); + } catch (error) { + console.error("保存主题色失败:", error); + } +}; + +/* 应用暗黑模式的 body 样式 */ +const applyDarkModeBodyStyle = (isDark: boolean) => { + // #ifdef H5 + if (typeof document !== "undefined") { + const body = document.body; + if (isDark) { + body.style.backgroundColor = "#1a1a1a"; + body.style.color = "#f5f5f5"; + body.classList.add("wot-theme-dark"); + } else { + body.style.backgroundColor = "#f8f8f8"; + body.style.color = "#333"; + body.classList.remove("wot-theme-dark"); + } + } + // #endif + + // #ifdef MP + // 小程序环境中设置页面样式 + try { + const pages = getCurrentPages(); + if (pages.length > 0) { + const currentPage = pages[pages.length - 1]; + if (currentPage && currentPage.$vm) { + // 这里可以根据需要设置页面样式 + console.log("小程序暗黑模式:", isDark ? "开启" : "关闭"); + } + } + } catch (error) { + console.error("设置小程序页面样式失败:", error); + } + // #endif }; /* 主题状态 */ -export const themeState = ref(initThemState); +export const theme = ref<"light" | "dark">(getStoredTheme()); /* 主题变量 */ -export const themeVars = ref(initThemeVars); - -/* 计算属性:根据主题状态返回对应的主题变量 */ -export const computedThemeVars = computed(() => { - const baseVars = themeState.value === "dark" ? darkThemeVars : initThemeVars; - return { - ...baseVars, - ...themeVars.value, - }; +export const themeVars = ref({ + colorTheme: getStoredThemeColor(), }); -/* 切换主题 */ +/* 监听主题模式变化,应用 body 样式 */ +watch( + theme, + (newTheme) => { + saveThemeToStorage(newTheme); + applyDarkModeBodyStyle(newTheme === "dark"); + }, + { immediate: true } +); + +/* 监听主题色变化,保存到存储 */ +watch( + () => themeVars.value.colorTheme, + (newColor) => { + if (newColor) { + saveThemeColorToStorage(newColor); + } + }, + { immediate: true } +); + +/* 切换主题模式 */ export const toggleTheme = () => { - themeState.value = themeState.value === "light" ? "dark" : "light"; - // 更新主题变量 - const newBaseVars = themeState.value === "dark" ? darkThemeVars : initThemeVars; - themeVars.value = { - ...newBaseVars, - colorTheme: themeVars.value.colorTheme, // 保持用户选择的主题色 - }; + theme.value = theme.value === "light" ? "dark" : "light"; }; /* 设置主题色 */ @@ -64,13 +135,37 @@ export const setThemeColor = (color: string) => { }; }; +/* 重置主题 */ +export const resetTheme = () => { + theme.value = "light"; + setThemeColor(colorColumns[0].value); +}; + +/* 初始化主题 */ +export const initTheme = () => { + // 应用当前主题的 body 样式 + applyDarkModeBodyStyle(theme.value === "dark"); + + // 确保主题变量已正确设置 + if (!themeVars.value.colorTheme) { + themeVars.value.colorTheme = getStoredThemeColor(); + } + + console.log("主题初始化完成:", { + mode: theme.value, + color: themeVars.value.colorTheme, + }); +}; + /* 导出主题相关的工具 */ export const useTheme = () => { return { - themeState, - themeVars: computedThemeVars, + theme, + themeVars, toggleTheme, setThemeColor, + resetTheme, + initTheme, colorColumns, }; }; diff --git a/src/layouts/default.vue b/src/layouts/default.vue index 54d7c3e..bdd524f 100644 --- a/src/layouts/default.vue +++ b/src/layouts/default.vue @@ -1,11 +1,7 @@ + + diff --git a/src/main.ts b/src/main.ts index a2d3100..5a1b638 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,6 +3,7 @@ import App from "./App.vue"; import "uno.css"; import "@/styles/global.scss"; +import "@/styles/wot-theme.scss"; import { setupStore } from "@/store"; diff --git a/src/pages/index/index.vue b/src/pages/index/index.vue index 3ce1e8c..0702165 100644 --- a/src/pages/index/index.vue +++ b/src/pages/index/index.vue @@ -252,6 +252,18 @@ 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"); + } + } +}); diff --git a/src/pages/mine/settings/theme/index.vue b/src/pages/mine/settings/theme/index.vue index 2b60615..cffd9ab 100644 --- a/src/pages/mine/settings/theme/index.vue +++ b/src/pages/mine/settings/theme/index.vue @@ -7,18 +7,18 @@ - + - + 外观模式 - - + + - + - + 主题色彩 @@ -26,59 +26,78 @@ - 预设主题色 + 预设主题色 - + + + + {{ color.label }} - - - + + + 当前主题色 + + + {{ currentThemeColor }} + + + - + - + 效果预览 - - + + + + 主要按钮 - - + + 主题色文本 - - + + 主题色边框 - - + + 标签 - - - + + + - - 重置为默认主题 - + + 重置为默认主题 + @@ -88,21 +107,24 @@ + + 请输入十六进制颜色值 - 支持格式:#RGB 或 #RRGGBB + + 取消 应用 @@ -114,61 +136,23 @@ @@ -286,15 +255,81 @@ onShow(() => { .theme-settings-container { min-height: 100vh; padding: 20rpx; - background-color: #f8f9fa; + 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; + } } .page-header { padding: 40rpx 20rpx; margin-bottom: 30rpx; text-align: center; - background: linear-gradient(135deg, #165dff 0%, #667eea 100%); + background: linear-gradient(135deg, var(--wot-color-theme, #165dff) 0%, #667eea 100%); border-radius: 16rpx; } @@ -313,25 +348,20 @@ onShow(() => { .setting-section { margin-bottom: 30rpx; - overflow: hidden; - background: #fff; - border-radius: 16rpx; - box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05); - transition: background-color 0.3s ease; } .section-header { display: flex; align-items: center; padding: 30rpx 30rpx 20rpx; - border-bottom: 1rpx solid #f0f0f0; + border-bottom: 1rpx solid var(--wot-color-border, #f0f0f0); } .section-title { margin-left: 12rpx; font-size: 32rpx; font-weight: 600; - color: #333; + color: var(--wot-color-text, #333); } .color-section { @@ -341,58 +371,95 @@ onShow(() => { .color-label { margin-bottom: 20rpx; font-size: 28rpx; - color: #666; + color: var(--wot-color-text-secondary, #666); } .color-grid { - display: grid; - grid-template-columns: repeat(4, 1fr); + 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: 80rpx; - height: 80rpx; + width: calc(25% - 15rpx); + padding: 10rpx; cursor: pointer; - border-radius: 12rpx; - box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1); transition: all 0.3s ease; + + .color-preview { + position: relative; + display: flex; + align-items: center; + justify-content: center; + width: 60rpx; + height: 60rpx; + margin-bottom: 8rpx; + 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); + } + + .color-name { + font-size: 22rpx; + line-height: 1.2; + color: var(--wot-color-text-secondary, #666); + text-align: center; + } + + &.active .color-preview { + box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.2); + transform: scale(1.05); + } + + &:active .color-preview { + transform: scale(0.95); + } } -.color-item.active { - box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.2); - transform: scale(1.1); +.current-theme-section { + padding: 30rpx; } -.color-item:active { - transform: scale(0.95); +.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 #f0f0f0; + border: 2rpx solid var(--wot-color-border, #f0f0f0); border-radius: 8rpx; } -.preview-container { - display: flex; - flex-direction: column; - gap: 20rpx; - padding: 30rpx; -} - -.preview-item { - display: flex; - align-items: center; - justify-content: center; - padding: 20rpx; - background: #f8f9fa; - border-radius: 12rpx; +.current-color-text { + margin-left: 10rpx; + font-size: 28rpx; + font-weight: 500; } .preview-text { @@ -407,22 +474,18 @@ onShow(() => { width: 200rpx; height: 60rpx; font-size: 26rpx; - color: #666; + color: var(--wot-color-text-secondary, #666); border: 2rpx solid; border-radius: 8rpx; } .action-section { - padding: 30rpx; - background: #fff; - border-radius: 16rpx; - box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05); -} - -.custom-color-popup { - padding: 40rpx 30rpx; - background: #fff; - border-radius: 20rpx 20rpx 0 0; + :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)); + } } .popup-header { @@ -435,7 +498,7 @@ onShow(() => { .popup-title { font-size: 32rpx; font-weight: 600; - color: #333; + color: var(--wot-color-text, #333); } .color-input-section { @@ -445,7 +508,7 @@ onShow(() => { .input-label { margin-bottom: 20rpx; font-size: 28rpx; - color: #666; + color: var(--wot-color-text-secondary, #666); } .input-container { @@ -459,28 +522,18 @@ onShow(() => { flex-shrink: 0; width: 60rpx; height: 60rpx; - border: 2rpx solid #f0f0f0; + border: 2rpx solid var(--wot-color-border, #f0f0f0); border-radius: 8rpx; } .color-input { flex: 1; - height: 60rpx; - padding: 0 20rpx; - font-size: 28rpx; - background: #fff; - border: 2rpx solid #e0e0e0; - border-radius: 8rpx; -} - -.color-input:focus { - border-color: #165dff; } .input-tip { margin-left: 80rpx; font-size: 24rpx; - color: #999; + color: var(--wot-color-text-placeholder, #999); } .popup-actions { @@ -501,52 +554,85 @@ onShow(() => { } } -/* 暗黑模式适配 */ -:deep(.wd-config-provider[data-theme="dark"]) { - .theme-settings-container { - background-color: #1a1a1a; +// 全局暗黑模式适配(针对当前页面) +: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%); } - .setting-section { - background: #2a2a2a; + .section-title { + color: var(--wot-color-text, #fff) !important; + } - .section-header { - border-bottom-color: #404040; + .color-label { + color: var(--wot-color-text-secondary, #d1d5db) !important; + } - .section-title { - color: #fff; - } + .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; } } - .preview-container .preview-item { - background: #404040; + :deep(.wd-grid-item) { + color: var(--wot-color-text, #fff) !important; + background-color: var(--wot-card-bg-color, #2a2a2a) !important; } - .action-section { - background: #2a2a2a; + // 专门为颜色预览块重置背景色 + :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: #2a2a2a; - - .popup-title { - color: #fff; - } - - .input-label { - color: #ccc; - } - - .color-input { - color: #fff; - background: #404040; - border-color: #606060; - - &:focus { - border-color: #165dff; - } - } + background: var(--wot-popup-bg-color, #2a2a2a) !important; } } diff --git a/src/styles/wot-theme.scss b/src/styles/wot-theme.scss new file mode 100644 index 0000000..d85bc50 --- /dev/null +++ b/src/styles/wot-theme.scss @@ -0,0 +1,235 @@ +/** + * Wot Design Uni 主题变量定制 + * 基于 ConfigProvider 的 CSS 变量系统 + */ + +/* + * 全局 Wot 主题变量 + * 这些变量会被 ConfigProvider 自动应用到所有 Wot 组件 + */ +:root, +page { + /* ===== 主题色系 ===== */ + --wot-color-theme: #165dff; + --wot-color-success: #0fc6c2; + --wot-color-warning: #ff7d00; + --wot-color-danger: #f5222d; + --wot-color-info: #86909c; + + /* ===== 中性色系 ===== */ + --wot-color-white: #ffffff; + --wot-color-black: #000000; + --wot-color-gray-1: #f7f8fa; + --wot-color-gray-2: #f2f3f5; + --wot-color-gray-3: #e5e6eb; + --wot-color-gray-4: #c9cdd4; + --wot-color-gray-5: #86909c; + --wot-color-gray-6: #4e5969; + --wot-color-gray-7: #272e3b; + --wot-color-gray-8: #1d2129; + + /* ===== 文本颜色 ===== */ + --wot-color-text: #1d2129; + --wot-color-text-secondary: #4e5969; + --wot-color-text-placeholder: #86909c; + --wot-color-text-disabled: #c9cdd4; + + /* ===== 背景颜色 ===== */ + --wot-color-bg: #ffffff; + --wot-color-bg-light: #f7f8fa; + --wot-color-bg-dark: #f2f3f5; + + /* ===== 边框颜色 ===== */ + --wot-color-border: #e5e6eb; + --wot-color-border-light: #f2f3f5; + + /* ===== 按钮相关变量 ===== */ + --wot-button-primary-bg-color: var(--wot-color-theme); + --wot-button-primary-color: #ffffff; + --wot-button-primary-border-color: var(--wot-color-theme); + + --wot-button-success-bg-color: var(--wot-color-success); + --wot-button-success-color: #ffffff; + --wot-button-success-border-color: var(--wot-color-success); + + --wot-button-warning-bg-color: var(--wot-color-warning); + --wot-button-warning-color: #ffffff; + --wot-button-warning-border-color: var(--wot-color-warning); + + --wot-button-danger-bg-color: var(--wot-color-danger); + --wot-button-danger-color: #ffffff; + --wot-button-danger-border-color: var(--wot-color-danger); + + --wot-button-info-bg-color: var(--wot-color-info); + --wot-button-info-color: #ffffff; + --wot-button-info-border-color: var(--wot-color-info); + + /* ===== 输入框相关变量 ===== */ + --wot-input-border-color: var(--wot-color-border); + --wot-input-focus-border-color: var(--wot-color-theme); + --wot-input-placeholder-color: var(--wot-color-text-placeholder); + --wot-input-disabled-bg-color: var(--wot-color-bg-light); + + /* ===== 开关相关变量 ===== */ + --wot-switch-on-bg-color: var(--wot-color-theme); + --wot-switch-off-bg-color: var(--wot-color-gray-3); + + /* ===== 卡片相关变量 ===== */ + --wot-card-bg-color: #ffffff; + --wot-card-border-color: var(--wot-color-border); + --wot-card-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05); + + /* ===== Cell 相关变量 ===== */ + --wot-cell-bg-color: #ffffff; + --wot-cell-border-color: var(--wot-color-border); + --wot-cell-title-color: var(--wot-color-text); + --wot-cell-value-color: var(--wot-color-text-secondary); + + /* ===== 标签相关变量 ===== */ + --wot-tag-primary-bg-color: var(--wot-color-theme); + --wot-tag-primary-color: #ffffff; + --wot-tag-success-bg-color: var(--wot-color-success); + --wot-tag-success-color: #ffffff; + --wot-tag-warning-bg-color: var(--wot-color-warning); + --wot-tag-warning-color: #ffffff; + --wot-tag-danger-bg-color: var(--wot-color-danger); + --wot-tag-danger-color: #ffffff; + + /* ===== 图标相关变量 ===== */ + --wot-icon-color: var(--wot-color-text-secondary); + + /* ===== 分割线相关变量 ===== */ + --wot-divider-color: var(--wot-color-border); + + /* ===== 弹窗相关变量 ===== */ + --wot-popup-bg-color: #ffffff; + --wot-popup-border-radius: 16rpx; + + /* ===== 网格相关变量 ===== */ + --wot-grid-border-color: var(--wot-color-border); +} + +/* ===== 暗黑模式变量 ===== */ +[data-theme="dark"], +.wot-theme-dark { + /* 主题色在暗黑模式下稍微调亮 */ + --wot-color-theme: #4080ff; + --wot-color-success: #1dd1cc; + --wot-color-warning: #ff8c1a; + --wot-color-danger: #ff4757; + --wot-color-info: #9ca3af; + + /* 中性色系 - 暗黑模式 */ + --wot-color-white: #1a1a1a; + --wot-color-black: #ffffff; + --wot-color-gray-1: #2a2a2a; + --wot-color-gray-2: #404040; + --wot-color-gray-3: #606060; + --wot-color-gray-4: #808080; + --wot-color-gray-5: #9ca3af; + --wot-color-gray-6: #d1d5db; + --wot-color-gray-7: #e5e7eb; + --wot-color-gray-8: #f3f4f6; + + /* 文本颜色 - 暗黑模式 */ + --wot-color-text: #ffffff; + --wot-color-text-secondary: #d1d5db; + --wot-color-text-placeholder: #9ca3af; + --wot-color-text-disabled: #6b7280; + + /* 背景颜色 - 暗黑模式 */ + --wot-color-bg: #1a1a1a; + --wot-color-bg-light: #2a2a2a; + --wot-color-bg-dark: #404040; + + /* 边框颜色 - 暗黑模式 */ + --wot-color-border: #404040; + --wot-color-border-light: #606060; + + /* 卡片相关变量 - 暗黑模式 */ + --wot-card-bg-color: #2a2a2a; + --wot-card-border-color: var(--wot-color-border); + --wot-card-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.3); + + /* Cell 相关变量 - 暗黑模式 */ + --wot-cell-bg-color: #2a2a2a; + + /* 弹窗相关变量 - 暗黑模式 */ + --wot-popup-bg-color: #2a2a2a; + + /* 输入框相关变量 - 暗黑模式 */ + --wot-input-disabled-bg-color: var(--wot-color-bg-light); +} + +/* ===== 动态主题色支持 ===== */ +/* 这些变量会被 useTheme composable 动态设置 */ +.theme-blue { + --wot-color-theme: #165dff; +} + +.theme-green { + --wot-color-theme: #0fc6c2; +} + +.theme-orange { + --wot-color-theme: #ff7d00; +} + +.theme-red { + --wot-color-theme: #f5222d; +} + +.theme-purple { + --wot-color-theme: #722ed1; +} + +.theme-pink { + --wot-color-theme: #eb2f96; +} + +.theme-cyan { + --wot-color-theme: #13c2c2; +} + +.theme-yellow { + --wot-color-theme: #faad14; +} + +.theme-lime { + --wot-color-theme: #a0d911; +} + +.theme-indigo { + --wot-color-theme: #2f54eb; +} + +.theme-teal { + --wot-color-theme: #08979c; +} + +.theme-brown { + --wot-color-theme: #8c6239; +} + +/* ===== 全局样式增强 ===== */ +/* 确保暗黑模式下的全局样式 */ +[data-theme="dark"] { + color: var(--wot-color-text); + background-color: var(--wot-color-bg); +} + +/* H5 环境下的 body 样式 */ +/* #ifdef H5 */ +body.dark { + color: #ffffff !important; + background-color: #1a1a1a !important; +} +/* #endif */ + +/* 小程序环境下的 page 样式 */ +/* #ifndef H5 */ +page.dark { + color: #ffffff !important; + background-color: #1a1a1a !important; +} +/* #endif */ diff --git a/src/types/env.d.ts b/src/types/env.d.ts index 0558171..15f48e8 100644 --- a/src/types/env.d.ts +++ b/src/types/env.d.ts @@ -12,6 +12,11 @@ interface ImportMetaEnv { * API 服务器的 URL */ VITE_APP_API_URL: string; + + /** + * WebSocket 端点 + */ + VITE_APP_WS_ENDPOINT: string; } interface ImportMeta { diff --git a/src/uni.scss b/src/uni.scss index d055110..bbd676d 100644 --- a/src/uni.scss +++ b/src/uni.scss @@ -74,3 +74,69 @@ $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; + } + } +}