From add03103dcb37559bcb09824e219b4ba826f16ef Mon Sep 17 00:00:00 2001 From: bootx Date: Tue, 23 Jun 2026 22:28:15 +0800 Subject: [PATCH] =?UTF-8?q?feat(h5):=20=E9=87=8D=E6=9E=84=E6=B7=B1?= =?UTF-8?q?=E8=89=B2=E6=A8=A1=E5=BC=8F=E4=B8=8E=E9=A6=96=E9=A1=B5,=20?= =?UTF-8?q?=E7=BB=9F=E4=B8=80=E5=8D=8F=E8=AE=AE=E4=B8=8E=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 清理脚手架冗余; 深色模式全局跟随系统 + iOS 深灰背景; 首页 logo/欢迎语/版本号; DaxPay logo; 版本 4.0.0 + 改名 daxpay-h5 --- package.json | 16 +- public/logo.svg | 57 +-- src/assets/icons/logo.svg | 57 +-- src/enums/breakpointEnum.ts | 28 -- src/enums/cacheEnum.ts | 14 - src/hooks/core/useTimeout.ts | 48 --- src/hooks/event/useBreakpoint.ts | 95 ---- src/hooks/event/useEventListener.ts | 61 --- src/hooks/event/useWindowSizeFn.ts | 35 -- src/hooks/index.ts | 3 - src/hooks/setting/useDesignSetting.ts | 3 +- src/hooks/use-async.ts | 16 - src/hooks/useDomWidth.ts | 23 - src/hooks/useOnline.ts | 30 -- src/hooks/useTime.ts | 55 --- src/layout/components/FloatingNavBar.vue | 525 ----------------------- src/layout/index.vue | 12 +- src/main.ts | 3 + src/settings/componentSetting.ts | 15 - src/settings/designSetting.ts | 8 +- src/store/modules/designSetting.ts | 46 +- src/store/mutation-types.ts | 1 - src/styles/common.less | 13 +- src/utils/dateUtil.ts | 12 - src/utils/domUtils.ts | 199 --------- src/views/home/index.vue | 52 ++- src/views/my/ThemeSetting.vue | 24 -- 27 files changed, 152 insertions(+), 1299 deletions(-) delete mode 100644 src/enums/breakpointEnum.ts delete mode 100644 src/enums/cacheEnum.ts delete mode 100644 src/hooks/core/useTimeout.ts delete mode 100644 src/hooks/event/useBreakpoint.ts delete mode 100644 src/hooks/event/useEventListener.ts delete mode 100644 src/hooks/event/useWindowSizeFn.ts delete mode 100644 src/hooks/index.ts delete mode 100644 src/hooks/use-async.ts delete mode 100644 src/hooks/useDomWidth.ts delete mode 100644 src/hooks/useOnline.ts delete mode 100644 src/hooks/useTime.ts delete mode 100644 src/layout/components/FloatingNavBar.vue delete mode 100644 src/settings/componentSetting.ts delete mode 100644 src/store/mutation-types.ts delete mode 100644 src/utils/dateUtil.ts delete mode 100644 src/utils/domUtils.ts diff --git a/package.json b/package.json index 11693e9..c06a0cc 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { - "name": "daxpay-gateway-ui", + "name": "daxpay-h5", "type": "module", - "version": "3.1.0", + "version": "4.0.0", "private": true, "packageManager": "pnpm@10.32.1", "license": "LGPL-3.0-or-later", @@ -27,16 +27,13 @@ "lint:lint-staged": "lint-staged" }, "dependencies": { - "@vueuse/core": "^14.3.0", "axios": "^1.18.1", - "date-fns": "^3.0.6", "lodash-es": "^4.18.1", "nprogress": "^0.2.0", "pinia": "^3.0.4", "pinia-plugin-persistedstate": "^4.7.1", "qs": "^6.15.2", "vant": "^4.9.24", - "vconsole": "^3.15.1", "vue": "^3.5.38", "vue-router": "^5.1.0" }, @@ -59,7 +56,6 @@ "@vitejs/plugin-vue": "^6.0.7", "autoprefixer": "^10.5.0", "cross-env": "^7.0.3", - "cz-git": "^1.13.1", "dotenv": "^16.3.1", "eslint": "^9.39.4", "eslint-plugin-format": "^2.0.1", @@ -71,7 +67,6 @@ "postcss": "^8.5.15", "postcss-mobile-forever": "^4.0.0", "rimraf": "^6.1.3", - "rollup": "^4.62.2", "rollup-plugin-visualizer": "^5.11.0", "simple-git-hooks": "^2.13.1", "terser": "^5.48.0", @@ -80,8 +75,8 @@ "unocss": "^66.7.2", "unplugin-auto-import": "^21.0.0", "unplugin-vue-components": "^32.1.0", + "vconsole": "^3.15.1", "vite": "^8.1.0", - "vite-plugin-compression": "^0.5.1", "vite-plugin-html": "^3.2.2", "vite-plugin-svg-icons": "^2.0.1", "vue-tsc": "^3.3.5" @@ -99,10 +94,5 @@ }, "lint-staged": { "*": "eslint --fix" - }, - "config": { - "commitizen": { - "path": "node_modules/cz-git" - } } } diff --git a/public/logo.svg b/public/logo.svg index f896240..af325a3 100644 --- a/public/logo.svg +++ b/public/logo.svg @@ -1,38 +1,21 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + dax-pay-无汉字-svg + + + + + + + + + + + + + + + diff --git a/src/assets/icons/logo.svg b/src/assets/icons/logo.svg index f896240..af325a3 100644 --- a/src/assets/icons/logo.svg +++ b/src/assets/icons/logo.svg @@ -1,38 +1,21 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + dax-pay-无汉字-svg + + + + + + + + + + + + + + + diff --git a/src/enums/breakpointEnum.ts b/src/enums/breakpointEnum.ts deleted file mode 100644 index fa42ea3..0000000 --- a/src/enums/breakpointEnum.ts +++ /dev/null @@ -1,28 +0,0 @@ -export enum sizeEnum { - XS = 'XS', - SM = 'SM', - MD = 'MD', - LG = 'LG', - XL = 'XL', - XXL = 'XXL', -} - -export enum screenEnum { - XS = 480, - SM = 576, - MD = 768, - LG = 992, - XL = 1200, - XXL = 1600, -} - -const screenMap = new Map() - -screenMap.set(sizeEnum.XS, screenEnum.XS) -screenMap.set(sizeEnum.SM, screenEnum.SM) -screenMap.set(sizeEnum.MD, screenEnum.MD) -screenMap.set(sizeEnum.LG, screenEnum.LG) -screenMap.set(sizeEnum.XL, screenEnum.XL) -screenMap.set(sizeEnum.XXL, screenEnum.XXL) - -export { screenMap } diff --git a/src/enums/cacheEnum.ts b/src/enums/cacheEnum.ts deleted file mode 100644 index 7a96b03..0000000 --- a/src/enums/cacheEnum.ts +++ /dev/null @@ -1,14 +0,0 @@ -// token key -export const TOKEN_KEY = 'TOKEN' - -// user info key -export const USER_INFO_KEY = 'USER__INFO__' - -// role info key -export const ROLES_KEY = 'ROLES__KEY__' - -// base global local key -export const BASE_LOCAL_CACHE_KEY = 'LOCAL__CACHE__KEY__' - -// base global session key -export const BASE_SESSION_CACHE_KEY = 'SESSION__CACHE__KEY__' diff --git a/src/hooks/core/useTimeout.ts b/src/hooks/core/useTimeout.ts deleted file mode 100644 index 8302f54..0000000 --- a/src/hooks/core/useTimeout.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { tryOnUnmounted } from '@vueuse/core' -import { ref, watch } from 'vue' -import { isFunction } from '@/utils/is' - -export function useTimeoutFn(handle: Fn, wait: number, native = false) { - if (!isFunction(handle)) { - throw new Error('handle is not Function!') - } - - const { readyRef, stop, start } = useTimeoutRef(wait) - if (native) { - handle() - } - else { - watch( - readyRef, - (maturity) => { - maturity && handle() - }, - { immediate: false }, - ) - } - return { readyRef, stop, start } -} - -export function useTimeoutRef(wait: number) { - const readyRef = ref(false) - - let timer: TimeoutHandle - - function stop(): void { - readyRef.value = false - timer && window.clearTimeout(timer) - } - - function start(): void { - stop() - timer = setTimeout(() => { - readyRef.value = true - }, wait) - } - - start() - - tryOnUnmounted(stop) - - return { readyRef, stop, start } -} diff --git a/src/hooks/event/useBreakpoint.ts b/src/hooks/event/useBreakpoint.ts deleted file mode 100644 index a97eb7c..0000000 --- a/src/hooks/event/useBreakpoint.ts +++ /dev/null @@ -1,95 +0,0 @@ -import type { ComputedRef } from 'vue' -import { computed, ref, unref } from 'vue' -import { screenEnum, screenMap, sizeEnum } from '@/enums/breakpointEnum' -import { useEventListener } from '@/hooks/event/useEventListener' - -let globalScreenRef: ComputedRef -let globalWidthRef: ComputedRef -let globalRealWidthRef: ComputedRef - -export interface CreateCallbackParams { - screen: ComputedRef - width: ComputedRef - realWidth: ComputedRef - screenEnum: typeof screenEnum - screenMap: Map - sizeEnum: typeof sizeEnum -} - -export function useBreakpoint() { - return { - screenRef: computed(() => unref(globalScreenRef)), - widthRef: globalWidthRef, - screenEnum, - realWidthRef: globalRealWidthRef, - } -} - -// Just call it once -export function createBreakpointListen(fn?: (opt: CreateCallbackParams) => void) { - const screenRef = ref(sizeEnum.XL) - const realWidthRef = ref(window.innerWidth) - - function getWindowWidth() { - const width = document.body.clientWidth - const xs = screenMap.get(sizeEnum.XS)! - const sm = screenMap.get(sizeEnum.SM)! - const md = screenMap.get(sizeEnum.MD)! - const lg = screenMap.get(sizeEnum.LG)! - const xl = screenMap.get(sizeEnum.XL)! - if (width < xs) { - screenRef.value = sizeEnum.XS - } - else if (width < sm) { - screenRef.value = sizeEnum.SM - } - else if (width < md) { - screenRef.value = sizeEnum.MD - } - else if (width < lg) { - screenRef.value = sizeEnum.LG - } - else if (width < xl) { - screenRef.value = sizeEnum.XL - } - else { - screenRef.value = sizeEnum.XXL - } - realWidthRef.value = width - } - - useEventListener({ - el: window, - name: 'resize', - - listener: () => { - getWindowWidth() - resizeFn() - }, - // wait: 100, - }) - - getWindowWidth() - globalScreenRef = computed(() => unref(screenRef)) - globalWidthRef = computed((): number => screenMap.get(unref(screenRef)!)!) - globalRealWidthRef = computed((): number => unref(realWidthRef)) - - function resizeFn() { - fn?.({ - screen: globalScreenRef, - width: globalWidthRef, - realWidth: globalRealWidthRef, - screenEnum, - screenMap, - sizeEnum, - }) - } - - resizeFn() - return { - screenRef: globalScreenRef, - screenEnum, - widthRef: globalWidthRef, - realWidthRef: globalRealWidthRef, - } -} diff --git a/src/hooks/event/useEventListener.ts b/src/hooks/event/useEventListener.ts deleted file mode 100644 index 990c7ad..0000000 --- a/src/hooks/event/useEventListener.ts +++ /dev/null @@ -1,61 +0,0 @@ -import type { Ref } from 'vue' - -import { useDebounceFn, useThrottleFn } from '@vueuse/core' -import { ref, unref, watch } from 'vue' - -export type RemoveEventFn = () => void - -export interface UseEventParams { - el?: Element | Ref | Window | any - name: string - listener: EventListener - options?: boolean | AddEventListenerOptions - autoRemove?: boolean - isDebounce?: boolean - wait?: number -} - -export function useEventListener({ - el = window, - name, - listener, - options, - autoRemove = true, - isDebounce = true, - wait = 80, -}: UseEventParams): { removeEvent: RemoveEventFn } { - let remove: RemoveEventFn = () => { - } - const isAddRef = ref(false) - - if (el) { - const element: Ref = ref(el as Element) - - const handler = isDebounce ? useDebounceFn(listener, wait) : useThrottleFn(listener, wait) - const realHandler = wait ? handler : listener - const removeEventListener = (e: Element) => { - isAddRef.value = true - e.removeEventListener(name, realHandler, options) - } - const addEventListener = (e: Element) => e.addEventListener(name, realHandler, options) - - const removeWatch = watch( - element, - (v, _ov, cleanUp) => { - if (v) { - !unref(isAddRef) && addEventListener(v) - cleanUp(() => { - autoRemove && removeEventListener(v) - }) - } - }, - { immediate: true }, - ) - - remove = () => { - removeEventListener(element.value) - removeWatch() - } - } - return { removeEvent: remove } -} diff --git a/src/hooks/event/useWindowSizeFn.ts b/src/hooks/event/useWindowSizeFn.ts deleted file mode 100644 index 14c437c..0000000 --- a/src/hooks/event/useWindowSizeFn.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { tryOnMounted, tryOnUnmounted, useDebounceFn } from '@vueuse/core' - -interface WindowSizeOptions { - once?: boolean - immediate?: boolean - listenerOptions?: AddEventListenerOptions | boolean -} - -export function useWindowSizeFn(fn: Fn, wait = 150, options?: WindowSizeOptions) { - let handler = () => { - fn() - } - const handleSize = useDebounceFn(handler, wait) - handler = handleSize - - const start = () => { - if (options && options.immediate) { - handler() - } - window.addEventListener('resize', handler) - } - - const stop = () => { - window.removeEventListener('resize', handler) - } - - tryOnMounted(() => { - start() - }) - - tryOnUnmounted(() => { - stop() - }) - return [start, stop] -} diff --git a/src/hooks/index.ts b/src/hooks/index.ts deleted file mode 100644 index 280e2cb..0000000 --- a/src/hooks/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { useAsync } from './use-async' - -export { useAsync } diff --git a/src/hooks/setting/useDesignSetting.ts b/src/hooks/setting/useDesignSetting.ts index 328261d..324a8f6 100644 --- a/src/hooks/setting/useDesignSetting.ts +++ b/src/hooks/setting/useDesignSetting.ts @@ -4,7 +4,8 @@ import { useDesignSettingStore } from '@/store/modules/designSetting' export function useDesignSetting() { const designStore = useDesignSettingStore() - const getDarkMode = computed(() => designStore.darkMode) + // 当前实际生效主题(跟随系统 prefers-color-scheme 派生) + const getDarkMode = computed(() => designStore.getDarkMode) const getAppTheme = computed(() => designStore.appTheme) diff --git a/src/hooks/use-async.ts b/src/hooks/use-async.ts deleted file mode 100644 index b5bc18f..0000000 --- a/src/hooks/use-async.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { isReactive, isRef } from 'vue' - -function setLoading(loading, val) { - if (loading !== undefined && isRef(loading)) { - loading.value = val - } - else if (loading !== undefined && isReactive(loading)) { - loading.loading = val - } -} - -export async function useAsync(func: Promise, loading: any): Promise { - setLoading(loading, true) - - return await func.finally(() => setLoading(loading, false)) -} diff --git a/src/hooks/useDomWidth.ts b/src/hooks/useDomWidth.ts deleted file mode 100644 index ecc00a0..0000000 --- a/src/hooks/useDomWidth.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { debounce } from 'lodash-es' -import { onMounted, onUnmounted, ref } from 'vue' - -/** - * description: 获取页面宽度 - */ - -export function useDomWidth() { - const domWidth = ref(window.innerWidth) - - function resize() { - domWidth.value = document.body.clientWidth - } - - onMounted(() => { - window.addEventListener('resize', debounce(resize, 80)) - }) - onUnmounted(() => { - window.removeEventListener('resize', resize) - }) - - return domWidth -} diff --git a/src/hooks/useOnline.ts b/src/hooks/useOnline.ts deleted file mode 100644 index 2ade223..0000000 --- a/src/hooks/useOnline.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { onMounted, onUnmounted, ref } from 'vue' - -/** - * @description 用户网络是否可用 - */ -export function useOnline() { - const online = ref(true) - - const showStatus = (val) => { - online.value = typeof val == 'boolean' ? val : val.target.online - } - - // 在页面加载后,设置正确的网络状态 - navigator.onLine ? showStatus(true) : showStatus(false) - - onMounted(() => { - // 开始监听网络状态的变化 - window.addEventListener('online', showStatus) - - window.addEventListener('offline', showStatus) - }) - onUnmounted(() => { - // 移除监听网络状态的变化 - window.removeEventListener('online', showStatus) - - window.removeEventListener('offline', showStatus) - }) - - return { online } -} diff --git a/src/hooks/useTime.ts b/src/hooks/useTime.ts deleted file mode 100644 index b9dc880..0000000 --- a/src/hooks/useTime.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { onMounted, onUnmounted, ref } from 'vue' - -/** - * @description 获取本地时间 - */ -export function useTime() { - let timer // 定时器 - const year = ref(0) // 年份 - const month = ref(0) // 月份 - const week = ref('') // 星期几 - const day = ref(0) // 天数 - const hour = ref(0) // 小时 - const minute = ref(0) // 分钟 - const second = ref(0) // 秒 - - // 更新时间 - const updateTime = () => { - const date = new Date() - year.value = date.getFullYear() - month.value = date.getMonth() + 1 - week.value = '日一二三四五六'.charAt(date.getDay()) - day.value = date.getDate() - hour.value - = (`${date.getHours()}`)?.padStart(2, '0') - || new Intl.NumberFormat(undefined, { minimumIntegerDigits: 2 }).format(date.getHours()) - minute.value - = (`${date.getMinutes()}`)?.padStart(2, '0') - || new Intl.NumberFormat(undefined, { minimumIntegerDigits: 2 }).format(date.getMinutes()) - second.value = date.getSeconds() - } - - // 原生时间格式化 - // new Intl.DateTimeFormat('zh', { - // year: 'numeric', - // month: '2-digit', - // day: '2-digit', - // hour: '2-digit', - // minute: '2-digit', - // second: '2-digit', - // hour12: false - // }).format(new Date()) - - updateTime() - - onMounted(() => { - clearInterval(timer) - timer = setInterval(updateTime, 1000) - }) - - onUnmounted(() => { - clearInterval(timer) - }) - - return { month, day, hour, minute, second, week } -} diff --git a/src/layout/components/FloatingNavBar.vue b/src/layout/components/FloatingNavBar.vue deleted file mode 100644 index b63c733..0000000 --- a/src/layout/components/FloatingNavBar.vue +++ /dev/null @@ -1,525 +0,0 @@ -
- -
- - - - - diff --git a/src/layout/index.vue b/src/layout/index.vue index 9fa813f..b91f69d 100644 --- a/src/layout/index.vue +++ b/src/layout/index.vue @@ -1,7 +1,7 @@ @@ -20,17 +18,11 @@ import { computed } from 'vue' import { useDesignSettingStore } from '@/store/modules/designSetting' import { useRouteStore } from '@/store/modules/route' -import FloatingNavBar from './components/FloatingNavBar.vue' const routeStore = useRouteStore() const designStore = useDesignSettingStore() const keepAliveComponents = computed(() => routeStore.keepAliveComponents) - -const tabbarItems = [ - { label: 'Home', path: '/home/index', icon: 'i-ph:house-line' }, - { label: 'Theme', path: '/themeSetting', icon: 'i-ph:palette' }, -] diff --git a/src/main.ts b/src/main.ts index ba7c010..f8cd5d0 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,5 +1,6 @@ import { createApp } from 'vue' import { setupStore } from '@/store' +import { useDesignSettingWithOut } from '@/store/modules/designSetting' import App from './App.vue' import router, { setupRouter } from './router' import 'virtual:uno.css' @@ -27,6 +28,8 @@ async function bootstrap() { const app = createApp(App) // 挂载状态管理 setupStore(app) + // 初始化全局主题:跟随系统 prefers-color-scheme(不支持手动修改) + useDesignSettingWithOut().initSystemListener() // 挂载路由 setupRouter(app) await router.isReady() diff --git a/src/settings/componentSetting.ts b/src/settings/componentSetting.ts deleted file mode 100644 index 8e3d802..0000000 --- a/src/settings/componentSetting.ts +++ /dev/null @@ -1,15 +0,0 @@ -export default { - upload: { - // 考虑接口规范不同 - apiSetting: { - // 集合字段名 - infoField: 'result', - // 图片地址字段名 - imgField: 'imagePath', - }, - // 最大上传图片大小 - maxSize: 1, - // 图片上传类型 - fileType: ['image/png', 'image/jpg', 'image/jpeg', 'image/gif', 'image/svg+xml'], - }, -} diff --git a/src/settings/designSetting.ts b/src/settings/designSetting.ts index 7370d16..7a2a058 100644 --- a/src/settings/designSetting.ts +++ b/src/settings/designSetting.ts @@ -1,8 +1,8 @@ // app theme preset color export interface DesignSettingState { - // 系统主题 - darkMode: 'light' | 'dark' + // 系统是否深色(跟随 prefers-color-scheme,只读派生,不持久化) + systemPrefersDark: boolean // 系统风格 appTheme: string // 系统内置风格 @@ -36,8 +36,8 @@ export const appThemeList: string[] = [ ] const setting: DesignSettingState = { - // 深色主题 - darkMode: 'dark', + // 系统是否深色(初始 false,由 initSystemListener 读取系统并覆盖) + systemPrefersDark: false, // 系统主题色 appTheme: '#5d9dfe', // 系统内置主题色列表 diff --git a/src/store/modules/designSetting.ts b/src/store/modules/designSetting.ts index cd4eb77..1509d7c 100644 --- a/src/store/modules/designSetting.ts +++ b/src/store/modules/designSetting.ts @@ -3,19 +3,30 @@ import { defineStore } from 'pinia' import designSetting from '@/settings/designSetting' import { store } from '@/store' -const { darkMode, appTheme, appThemeList, isPageAnimate, pageAnimateType } = designSetting +const { systemPrefersDark, appTheme, appThemeList, isPageAnimate, pageAnimateType } = designSetting + +// 是否已绑定系统主题监听,避免重复绑定 +let systemListenerBound = false + +/** 同步 的 dark / light class,驱动全局深色样式(common.less 的 html.dark / html.light) */ +function syncHtmlThemeClass(isDark: boolean) { + const html = document.documentElement + html.classList.toggle('dark', isDark) + html.classList.toggle('light', !isDark) +} export const useDesignSettingStore = defineStore('app-design-setting', { state: (): DesignSettingState => ({ - darkMode, + systemPrefersDark, appTheme, appThemeList, isPageAnimate, pageAnimateType, }), getters: { + // 当前实际生效的主题(跟随系统 prefers-color-scheme) getDarkMode(): 'light' | 'dark' { - return this.darkMode + return this.systemPrefersDark ? 'dark' : 'light' }, getAppTheme(): string { return this.appTheme @@ -31,17 +42,40 @@ export const useDesignSettingStore = defineStore('app-design-setting', { }, }, actions: { - setDarkMode(mode: 'light' | 'dark'): void { - this.darkMode = mode + /** + * 初始化系统主题监听:读取 prefers-color-scheme 并监听其变化 + * 全局跟随系统,不支持手动修改 + */ + initSystemListener() { + // 非浏览器环境(如 SSR)仅按当前 state 同步一次 + if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') { + syncHtmlThemeClass(this.systemPrefersDark) + return + } + // 首次读取系统主题并同步到 + this.systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches + syncHtmlThemeClass(this.systemPrefersDark) + // 已绑定过则不再重复添加监听(应用生命周期内常驻) + if (systemListenerBound) { + return + } + systemListenerBound = true + const mql = window.matchMedia('(prefers-color-scheme: dark)') + const handler = (e: MediaQueryListEvent) => { + this.systemPrefersDark = e.matches + syncHtmlThemeClass(this.systemPrefersDark) + } + mql.addEventListener('change', handler) }, setPageAnimateType(type: string): void { this.pageAnimateType = type }, }, - // 持久化 + // 仅持久化用户可配置项,跟随系统的 systemPrefersDark 不持久化 persist: { key: 'DESIGN-SETTING', storage: localStorage, + pick: ['appTheme', 'appThemeList', 'isPageAnimate', 'pageAnimateType'], }, }) diff --git a/src/store/mutation-types.ts b/src/store/mutation-types.ts deleted file mode 100644 index 8decc3d..0000000 --- a/src/store/mutation-types.ts +++ /dev/null @@ -1 +0,0 @@ -export const DESIGN_SETTING = 'DESIGN-SETTING' // 当前用户主题信息 diff --git a/src/styles/common.less b/src/styles/common.less index 7f00fd9..30a14bf 100644 --- a/src/styles/common.less +++ b/src/styles/common.less @@ -16,8 +16,13 @@ html.dark { color-scheme: dark !important; } + // 覆盖 vant 深色背景变量(vant 默认 --van-background 为 #000,对比度过强) + --van-background: #1c1c1e; + --van-background-2: #1c1c1e; + --van-background-3: #2c2c2e; + body { - background-color: #000000; + background-color: #1c1c1e; color: rgba(255, 255, 255, 0.9) !important; } } @@ -108,9 +113,9 @@ html.light { html.dark { .my-card { backdrop-filter: blur(12px); - background: rgba(18, 18, 18, 0.85); + background: rgba(44, 44, 46, 0.85); border: 1px solid rgba(255, 255, 255, 0.08); - box-shadow: 0 4px 24px rgba(0, 0, 0, 0.4); + box-shadow: 0 4px 24px rgba(0, 0, 0, 0.3); } .van-cell-group--inset { @@ -120,7 +125,7 @@ html.dark { } .van-cell-group--inset .van-cell { - background: rgba(18, 18, 18, 0.6); + background: #2c2c2e; } .van-cell-group .van-cell::after { diff --git a/src/utils/dateUtil.ts b/src/utils/dateUtil.ts deleted file mode 100644 index d4f2843..0000000 --- a/src/utils/dateUtil.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { format } from 'date-fns' - -const DATE_TIME_FORMAT = 'yyyy-MM-dd HH:mm:ss' -const DATE_FORMAT = 'YYYY-MM-DD ' - -export function formatToDateTime(date: number | Date, formatStr = DATE_TIME_FORMAT): string { - return format(date, formatStr) -} - -export function formatToDate(date: number | Date, formatStr = DATE_FORMAT): string { - return format(date, formatStr) -} diff --git a/src/utils/domUtils.ts b/src/utils/domUtils.ts deleted file mode 100644 index cb4be64..0000000 --- a/src/utils/domUtils.ts +++ /dev/null @@ -1,199 +0,0 @@ -/* eslint-disable ts/ban-ts-comment */ -import type { FunctionArgs } from '@vueuse/core' -import { upperFirst } from 'lodash-es' - -export interface ViewportOffsetResult { - left: number - top: number - right: number - bottom: number - rightIncludeBody: number - bottomIncludeBody: number -} - -export function getBoundingClientRect(element: Element): DOMRect | number { - if (!element || !element.getBoundingClientRect) { - return 0 - } - return element.getBoundingClientRect() -} - -function trim(string: string) { - return (string || '').replace(/^\s+|\s+$/g, '') -} - -/* istanbul ignore next */ -export function hasClass(el: Element, cls: string) { - if (!el || !cls) { - return false - } - if (cls.includes(' ')) { - throw new Error('className should not contain space.') - } - if (el.classList) { - return el.classList.contains(cls) - } - else { - return (` ${el.className} `).includes(` ${cls} `) - } -} - -/* istanbul ignore next */ -export function addClass(el: Element, cls: string) { - if (!el) { - return - } - let curClass = el.className - const classes = (cls || '').split(' ') - - for (let i = 0, j = classes.length; i < j; i++) { - const clsName = classes[i] - if (!clsName) { - continue - } - - if (el.classList) { - el.classList.add(clsName) - } - else if (!hasClass(el, clsName)) { - curClass += ` ${clsName}` - } - } - if (!el.classList) { - el.className = curClass - } -} - -/* istanbul ignore next */ -export function removeClass(el: Element, cls: string) { - if (!el || !cls) { - return - } - const classes = cls.split(' ') - let curClass = ` ${el.className} ` - - for (let i = 0, j = classes.length; i < j; i++) { - const clsName = classes[i] - if (!clsName) { - continue - } - - if (el.classList) { - el.classList.remove(clsName) - } - else if (hasClass(el, clsName)) { - curClass = curClass.replace(` ${clsName} `, ' ') - } - } - if (!el.classList) { - el.className = trim(curClass) - } -} -/** - * Get the left and top offset of the current element - * left: the distance between the leftmost element and the left side of the document - * top: the distance from the top of the element to the top of the document - * right: the distance from the far right of the element to the right of the document - * bottom: the distance from the bottom of the element to the bottom of the document - * rightIncludeBody: the distance between the leftmost element and the right side of the document - * bottomIncludeBody: the distance from the bottom of the element to the bottom of the document - * - * @description: - */ -export function getViewportOffset(element: Element): ViewportOffsetResult { - const doc = document.documentElement - - const docScrollLeft = doc.scrollLeft - const docScrollTop = doc.scrollTop - const docClientLeft = doc.clientLeft - const docClientTop = doc.clientTop - - const pageXOffset = window.pageXOffset - const pageYOffset = window.pageYOffset - - const box = getBoundingClientRect(element) - - const { left: retLeft, top: rectTop, width: rectWidth, height: rectHeight } = box as DOMRect - - const scrollLeft = (pageXOffset || docScrollLeft) - (docClientLeft || 0) - const scrollTop = (pageYOffset || docScrollTop) - (docClientTop || 0) - const offsetLeft = retLeft + pageXOffset - const offsetTop = rectTop + pageYOffset - - const left = offsetLeft - scrollLeft - const top = offsetTop - scrollTop - - const clientWidth = window.document.documentElement.clientWidth - const clientHeight = window.document.documentElement.clientHeight - return { - left, - top, - right: clientWidth - rectWidth - left, - bottom: clientHeight - rectHeight - top, - rightIncludeBody: clientWidth - left, - bottomIncludeBody: clientHeight - top, - } -} - -export function hackCss(attr: string, value: string) { - const prefix: string[] = ['webkit', 'Moz', 'ms', 'OT'] - - const styleObj: any = {} - prefix.forEach((item) => { - styleObj[`${item}${upperFirst(attr)}`] = value - }) - return { - ...styleObj, - [attr]: value, - } -} - -/* istanbul ignore next */ -export function on( - element: Element | HTMLElement | Document | Window, - event: string, - handler: EventListenerOrEventListenerObject, -): void { - if (element && event && handler) { - element.addEventListener(event, handler, false) - } -} - -/* istanbul ignore next */ -export function off( - element: Element | HTMLElement | Document | Window, - event: string, - handler: Fn, -): void { - if (element && event && handler) { - element.removeEventListener(event, handler, false) - } -} - -/* istanbul ignore next */ -export function once(el: HTMLElement, event: string, fn: EventListener): void { - const listener = function (this: any, ...args: unknown[]) { - if (fn) { - // @ts-expect-error - fn.apply(this, args) - } - off(el, event, listener) - } - on(el, event, listener) -} - -export function useRafThrottle(fn: T): T { - let locked = false - // @ts-expect-error - return function (...args: any[]) { - if (locked) { - return - } - locked = true - window.requestAnimationFrame(() => { - // @ts-expect-error - fn.apply(this, args) - locked = false - }) - } -} diff --git a/src/views/home/index.vue b/src/views/home/index.vue index d57a408..5c18c51 100644 --- a/src/views/home/index.vue +++ b/src/views/home/index.vue @@ -1,14 +1,29 @@ diff --git a/src/views/my/ThemeSetting.vue b/src/views/my/ThemeSetting.vue index 3be4eda..542a4de 100644 --- a/src/views/my/ThemeSetting.vue +++ b/src/views/my/ThemeSetting.vue @@ -1,14 +1,5 @@