feat: 添加主题管理功能,更新系统设置页面,优化用户体验

This commit is contained in:
2025-12-20 03:48:01 +07:00
parent 916cbe9d24
commit 5c06c7ce0a
14 changed files with 196 additions and 70 deletions

5
auto-imports.d.ts vendored
View File

@@ -281,6 +281,7 @@ declare global {
const useTextDirection: typeof import('@vueuse/core').useTextDirection
const useTextSelection: typeof import('@vueuse/core').useTextSelection
const useTextareaAutosize: typeof import('@vueuse/core').useTextareaAutosize
const useTheme: typeof import('./src/composables/useTheme').useTheme
const useThrottle: typeof import('@vueuse/core').useThrottle
const useThrottleFn: typeof import('@vueuse/core').useThrottleFn
const useThrottledRefHistory: typeof import('@vueuse/core').useThrottledRefHistory
@@ -340,6 +341,9 @@ declare global {
export type { QRScanResult } from './src/composables/useQRScanner'
import('./src/composables/useQRScanner')
// @ts-ignore
export type { ThemeMode } from './src/composables/useTheme'
import('./src/composables/useTheme')
// @ts-ignore
export type { Series, TData, WeightChartOptions, TradingViewData, TradingViewOptions } from './src/composables/useTradingView'
import('./src/composables/useTradingView')
// @ts-ignore
@@ -628,6 +632,7 @@ declare module 'vue' {
readonly useTextDirection: UnwrapRef<typeof import('@vueuse/core')['useTextDirection']>
readonly useTextSelection: UnwrapRef<typeof import('@vueuse/core')['useTextSelection']>
readonly useTextareaAutosize: UnwrapRef<typeof import('@vueuse/core')['useTextareaAutosize']>
readonly useTheme: UnwrapRef<typeof import('./src/composables/useTheme')['useTheme']>
readonly useThrottle: UnwrapRef<typeof import('@vueuse/core')['useThrottle']>
readonly useThrottleFn: UnwrapRef<typeof import('@vueuse/core')['useThrottleFn']>
readonly useThrottledRefHistory: UnwrapRef<typeof import('@vueuse/core')['useThrottledRefHistory']>

View File

@@ -592,7 +592,7 @@ const stickyStyle = computed(() => {
}
/* Dark mode 支持 */
@media (prefers-color-scheme: dark) {
.ion-palette-dark {
.ui-tabs {
--ui-tabs-primary: var(--ion-color-primary, #ffffff);
--ui-tabs-primary-rgb: var(--ion-color-primary-rgb, 255, 255, 255);

View File

@@ -0,0 +1,26 @@
import { usePreferredDark } from "@vueuse/core";
export type ThemeMode = "light" | "dark" | "auto";
const STORAGE_KEY = "app-theme-mode";
export function useTheme() {
const prefersDark = usePreferredDark();
const theme = useStorage<ThemeMode>(STORAGE_KEY, "auto");
const isDark = computed(() => {
if (theme.value === "auto") {
return prefersDark.value;
}
return theme.value === "dark";
});
watch(isDark, (dark) => {
document.documentElement.classList.toggle("ion-palette-dark", dark);
}, { immediate: true });
return {
theme,
isDark,
};
}

View File

@@ -315,6 +315,11 @@
"updateNow": "Update Now",
"alreadyLatest": "Already up to date",
"checkUpdateFailed": "Failed to check for updates",
"languageTitle": "Language / 语言"
"languageTitle": "Language / 语言",
"theme": "Theme",
"themeTitle": "Appearance",
"themeLight": "Light",
"themeDark": "Dark",
"themeAuto": "Auto"
}
}

View File

@@ -315,6 +315,11 @@
"updateNow": "立即更新",
"alreadyLatest": "已是最新版本",
"checkUpdateFailed": "检查更新失败",
"languageTitle": "语言 / Language"
"languageTitle": "语言 / Language",
"theme": "主题",
"themeTitle": "外观主题",
"themeLight": "浅色",
"themeDark": "深色",
"themeAuto": "跟随系统"
}
}

View File

@@ -31,8 +31,8 @@ import "@ionic/vue/css/display.css";
*/
// import "@ionic/vue/css/palettes/dark.always.css";
// import "@ionic/vue/css/palettes/dark.class.css";
import "@ionic/vue/css/palettes/dark.system.css";
import "@ionic/vue/css/palettes/dark.class.css";
// import "@ionic/vue/css/palettes/dark.system.css";
/* Theme variables */
import "./theme/index.css";

View File

@@ -65,6 +65,10 @@ const routes: Array<RouteRecordRaw> = [
path: "language",
component: () => import("@/views/system-settings/language.vue"),
},
{
path: "theme",
component: () => import("@/views/system-settings/theme.vue"),
},
],
},
{

View File

@@ -24,7 +24,7 @@ ion-datetime.ui-datetime {
border-radius: 16px;
overflow: hidden;
}
@media (prefers-color-scheme: dark) {
.ion-palette-dark {
ion-datetime.ui-datetime {
--background: rgb(15, 15, 15);
--background-rgb: 15, 15, 15;
@@ -33,3 +33,8 @@ ion-datetime.ui-datetime {
--wheel-fade-background-rgb: 15, 15, 15;
}
}
:root.ios {
--ion-padding: 20px;
--ion-margin: 20px;
}

View File

@@ -1,6 +1,6 @@
/* For information on how to create your own theme, please refer to:
http://ionicframework.com/docs/theming/ */
:root {
html:root {
--ion-color-primary: #0d0d0d;
--ion-color-primary-rgb: 13, 13, 13;
--ion-color-primary-contrast: #ffffff;
@@ -120,8 +120,7 @@ http://ionicframework.com/docs/theming/ */
--ui-background-primary: linear-gradient(180deg, #615fff 0%, #9810fa 100%);
}
@media (prefers-color-scheme: dark) {
:root {
html.ion-palette-dark {
--ion-color-primary: #ffffff;
--ion-color-primary-rgb: 255, 255, 255;
--ion-color-primary-contrast: #000000;
@@ -187,5 +186,4 @@ http://ionicframework.com/docs/theming/ */
--ui-input-background: #1e1e1e;
--ui-input-color: #ffffff;
}
}

View File

@@ -36,7 +36,7 @@ ion-content::part(scroll) {
.item {
@apply px-3 py-1 rounded-full text-xs transition-all;
}
@media (prefers-color-scheme: dark) {
.ion-palette-dark {
.item {
@apply bg-(--ion-color-step-800);
}

View File

@@ -68,7 +68,7 @@ const { user } = useAuth();
--background: #f4f6f8;
--border-color: #c6c5c5;
}
@media (prefers-color-scheme: dark) {
.ion-palette-dark {
.content-wrapper {
--background: #232324;
--border-color: #3a3a3b;

View File

@@ -1,12 +1,18 @@
<script lang="ts" setup>
import { alertController, toastController, useIonRouter } from "@ionic/vue";
import { checkbox, close, information, languageOutline, refresh } from "ionicons/icons";
import { checkbox, close, contrastOutline, information, languageOutline, refresh } from "ionicons/icons";
const { t } = useI18n();
const router = useIonRouter();
const { cacheSize, calculateCacheSize, clearCache } = useCacheSize();
const { isChecking, checkForUpdate } = useAppUpdate();
const { currentLanguage } = useLanguage();
const { theme } = useTheme();
const themeNames = {
light: t("settings.themeLight"),
dark: t("settings.themeDark"),
auto: t("settings.themeAuto"),
};
function handleClearCache() {
clearCache();
@@ -98,6 +104,21 @@ onMounted(() => {
</div>
</div>
</ion-item>
<ion-item button @click="router.push('/system-settings/theme')">
<div class="flex justify-between w-full items-center">
<div class="flex-center space-x-2">
<div class="icon">
<ion-icon :icon="contrastOutline" class="text-lg" />
</div>
<div class="text-sm font-semibold">
{{ t("settings.theme") }}
</div>
</div>
<div class="end">
{{ themeNames[theme] }}
</div>
</div>
</ion-item>
<ion-item button>
<div class="flex justify-between w-full items-center">
<div class="flex-center space-x-2">

View File

@@ -0,0 +1,57 @@
<script lang="ts" setup>
import type { ThemeMode } from "@/composables/useTheme";
import { useIonRouter } from "@ionic/vue";
const { t } = useI18n();
const { theme } = useTheme();
const themes: { value: ThemeMode; label: string }[] = [
{ value: "light", label: t("settings.themeLight") },
{ value: "dark", label: t("settings.themeDark") },
{ value: "auto", label: t("settings.themeAuto") },
];
function handleThemeChange(event: CustomEvent) {
const themeMode = event.detail.value as ThemeMode;
theme.value = themeMode;
}
</script>
<template>
<ion-page>
<ion-header>
<ion-toolbar class="ui-toolbar">
<ion-back-button slot="start" />
<ion-title>{{ t("settings.theme") }}</ion-title>
</ion-toolbar>
</ion-header>
<ion-content :fullscreen="true" class="ion-padding">
<ion-list lines="full">
<ion-radio-group :value="theme" @ion-change="handleThemeChange">
<ion-item v-for="item in themes" :key="item.value">
<ion-radio :value="item.value">
<div class="theme-item">
<div class="font-semibold">
{{ item.label }}
</div>
</div>
</ion-radio>
</ion-item>
</ion-radio-group>
</ion-list>
</ion-content>
</ion-page>
</template>
<style lang='css' scoped>
@reference "tailwindcss";
.theme-item {
@apply py-1;
}
ion-radio {
width: 100%;
}
</style>

View File

@@ -178,7 +178,7 @@ function formatCardNumber(value: string) {
border-color: #ef4444;
}
@media (prefers-color-scheme: dark) {
.ion-palette-dark {
.ui-select {
border-color: #4b5563;
}