mirror of
				https://github.com/continew-org/continew-admin-ui.git
				synced 2025-11-04 10:57:08 +08:00 
			
		
		
		
	feat: 新增应用配置开关属性,迁移主题配置至 src/config/setting.ts,新增色弱模式与哀悼模式
				
					
				
			This commit is contained in:
		@@ -13,3 +13,6 @@ VITE_BASE = '/'
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# 是否开启开发者工具
 | 
					# 是否开启开发者工具
 | 
				
			||||||
VITE_OPEN_DEVTOOLS = false
 | 
					VITE_OPEN_DEVTOOLS = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# 应用配置面板
 | 
				
			||||||
 | 
					VITE_APP_SETTING = true
 | 
				
			||||||
@@ -9,3 +9,6 @@ VITE_API_WS_URL = 'wss://api.continew.top'
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# 地址前缀
 | 
					# 地址前缀
 | 
				
			||||||
VITE_BASE = '/'
 | 
					VITE_BASE = '/'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# 应用配置面板
 | 
				
			||||||
 | 
					VITE_APP_SETTING = false
 | 
				
			||||||
@@ -14,3 +14,6 @@ VITE_BASE = '/test'
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# 是否开启开发者工具
 | 
					# 是否开启开发者工具
 | 
				
			||||||
VITE_OPEN_DEVTOOLS = true
 | 
					VITE_OPEN_DEVTOOLS = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# 应用配置面板
 | 
				
			||||||
 | 
					VITE_APP_SETTING = false
 | 
				
			||||||
@@ -1,13 +0,0 @@
 | 
				
			|||||||
{
 | 
					 | 
				
			||||||
  "theme": "light",
 | 
					 | 
				
			||||||
  "themeColor": "#165DFF",
 | 
					 | 
				
			||||||
  "tab": true,
 | 
					 | 
				
			||||||
  "tabMode": "card-gutter",
 | 
					 | 
				
			||||||
  "animate": false,
 | 
					 | 
				
			||||||
  "animateMode": "zoom-fade",
 | 
					 | 
				
			||||||
  "menuCollapse": true,
 | 
					 | 
				
			||||||
  "menuAccordion": true,
 | 
					 | 
				
			||||||
  "menuDark": false,
 | 
					 | 
				
			||||||
  "copyrightDisplay": true,
 | 
					 | 
				
			||||||
  "layout": "left"
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										19
									
								
								src/config/setting.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/config/setting.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					export const defaultSettings: App.AppSettings = {
 | 
				
			||||||
 | 
					  theme: 'light',
 | 
				
			||||||
 | 
					  themeColor: '#165DFF',
 | 
				
			||||||
 | 
					  tab: true,
 | 
				
			||||||
 | 
					  tabMode: 'card-gutter',
 | 
				
			||||||
 | 
					  animate: false,
 | 
				
			||||||
 | 
					  animateMode: 'zoom-fade',
 | 
				
			||||||
 | 
					  menuCollapse: true,
 | 
				
			||||||
 | 
					  menuAccordion: true,
 | 
				
			||||||
 | 
					  menuDark: false,
 | 
				
			||||||
 | 
					  copyrightDisplay: true,
 | 
				
			||||||
 | 
					  layout: 'left',
 | 
				
			||||||
 | 
					  enableColorWeaknessMode: false,
 | 
				
			||||||
 | 
					  enableMourningMode: false,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					// 根据环境返回配置
 | 
				
			||||||
 | 
					export const getSettings = (): App.AppSettings => {
 | 
				
			||||||
 | 
					  return defaultSettings
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,8 +1,11 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <a-drawer v-model:visible="visible" title="项目配置" width="300px" unmount-on-close :footer="false">
 | 
					  <a-drawer v-model:visible="visible" title="项目配置" width="300px" unmount-on-close :footer="false">
 | 
				
			||||||
    <a-space :size="15" direction="vertical" fill>
 | 
					    <a-space :size="15" direction="vertical" fill>
 | 
				
			||||||
      <a-divider orientation="center">系统布局</a-divider>
 | 
					      <a-alert v-if="settingOpen" :show-icon="false" type="info">
 | 
				
			||||||
      <a-row justify="center">
 | 
					        「复制配置」按钮,并将配置粘贴到 src/config/settings.ts 文件中。
 | 
				
			||||||
 | 
					      </a-alert>
 | 
				
			||||||
 | 
					      <a-divider v-if="settingOpen" orientation="center">系统布局</a-divider>
 | 
				
			||||||
 | 
					      <a-row v-if="settingOpen" justify="center">
 | 
				
			||||||
        <a-space>
 | 
					        <a-space>
 | 
				
			||||||
          <a-badge>
 | 
					          <a-badge>
 | 
				
			||||||
            <template #content>
 | 
					            <template #content>
 | 
				
			||||||
@@ -35,9 +38,9 @@
 | 
				
			|||||||
        ></ColorPicker>
 | 
					        ></ColorPicker>
 | 
				
			||||||
      </a-row>
 | 
					      </a-row>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <a-divider orientation="center">界面显示</a-divider>
 | 
					      <a-divider v-if="settingOpen" orientation="center">界面显示</a-divider>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <a-descriptions :column="1" :align="{ value: 'right' }" :value-style="{ paddingRight: 0 }">
 | 
					      <a-descriptions v-if="settingOpen" :column="1" :align="{ value: 'right' }" :value-style="{ paddingRight: 0 }">
 | 
				
			||||||
        <a-descriptions-item label="页签显示">
 | 
					        <a-descriptions-item label="页签显示">
 | 
				
			||||||
          <a-switch v-model="appStore.tab" />
 | 
					          <a-switch v-model="appStore.tab" />
 | 
				
			||||||
        </a-descriptions-item>
 | 
					        </a-descriptions-item>
 | 
				
			||||||
@@ -70,10 +73,28 @@
 | 
				
			|||||||
        <a-descriptions-item label="水印">
 | 
					        <a-descriptions-item label="水印">
 | 
				
			||||||
          <a-switch v-model="appStore.isOpenWatermark" />
 | 
					          <a-switch v-model="appStore.isOpenWatermark" />
 | 
				
			||||||
        </a-descriptions-item>
 | 
					        </a-descriptions-item>
 | 
				
			||||||
        <a-descriptions-item v-if="appStore.isOpenWatermark" label="水印信息">
 | 
					        <a-descriptions-item label="水印信息">
 | 
				
			||||||
          <a-input v-model="appStore.watermark" placeholder="留空则显示用户名" />
 | 
					          <a-input v-model="appStore.watermark" placeholder="留空则显示用户名" />
 | 
				
			||||||
        </a-descriptions-item>
 | 
					        </a-descriptions-item>
 | 
				
			||||||
      </a-descriptions>
 | 
					      </a-descriptions>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      <a-divider orientation="center">其它</a-divider>
 | 
				
			||||||
 | 
					      <a-descriptions :column="1" :align="{ value: 'right' }" :value-style="{ paddingRight: 0 }">
 | 
				
			||||||
 | 
					        <a-descriptions-item label="色弱模式">
 | 
				
			||||||
 | 
					          <a-switch v-model="appStore.enableColorWeaknessMode" />
 | 
				
			||||||
 | 
					        </a-descriptions-item>
 | 
				
			||||||
 | 
					        <a-descriptions-item v-if="settingOpen" label="哀悼模式">
 | 
				
			||||||
 | 
					          <a-switch v-model="appStore.enableMourningMode" />
 | 
				
			||||||
 | 
					        </a-descriptions-item>
 | 
				
			||||||
 | 
					      </a-descriptions>
 | 
				
			||||||
 | 
					      <a-space v-if="settingOpen" direction="vertical" fill>
 | 
				
			||||||
 | 
					        <a-button type="primary" long @click="copySettings">
 | 
				
			||||||
 | 
					          <template #icon>
 | 
				
			||||||
 | 
					            <icon-copy />
 | 
				
			||||||
 | 
					          </template>
 | 
				
			||||||
 | 
					          复制配置
 | 
				
			||||||
 | 
					        </a-button>
 | 
				
			||||||
 | 
					      </a-space>
 | 
				
			||||||
    </a-space>
 | 
					    </a-space>
 | 
				
			||||||
  </a-drawer>
 | 
					  </a-drawer>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
@@ -81,13 +102,15 @@
 | 
				
			|||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import { ColorPicker } from 'vue-color-kit'
 | 
					import { ColorPicker } from 'vue-color-kit'
 | 
				
			||||||
import 'vue-color-kit/dist/vue-color-kit.css'
 | 
					import 'vue-color-kit/dist/vue-color-kit.css'
 | 
				
			||||||
 | 
					import { useClipboard } from '@vueuse/core'
 | 
				
			||||||
 | 
					import { Message } from '@arco-design/web-vue'
 | 
				
			||||||
import LayoutItem from './components/LayoutItem.vue'
 | 
					import LayoutItem from './components/LayoutItem.vue'
 | 
				
			||||||
import { useAppStore } from '@/stores'
 | 
					import { useAppStore } from '@/stores'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
defineOptions({ name: 'SettingDrawer' })
 | 
					defineOptions({ name: 'SettingDrawer' })
 | 
				
			||||||
const appStore = useAppStore()
 | 
					const appStore = useAppStore()
 | 
				
			||||||
const visible = ref(false)
 | 
					const visible = ref(false)
 | 
				
			||||||
 | 
					const settingOpen = JSON.parse(import.meta.env.VITE_APP_SETTING)
 | 
				
			||||||
const tabModeList: App.TabItem[] = [
 | 
					const tabModeList: App.TabItem[] = [
 | 
				
			||||||
  { label: '卡片', value: 'card' },
 | 
					  { label: '卡片', value: 'card' },
 | 
				
			||||||
  { label: '间隔卡片', value: 'card-gutter' },
 | 
					  { label: '间隔卡片', value: 'card-gutter' },
 | 
				
			||||||
@@ -138,6 +161,36 @@ const changeColor = (colorObj: ColorObj) => {
 | 
				
			|||||||
  appStore.setThemeColor(colorObj.hex)
 | 
					  appStore.setThemeColor(colorObj.hex)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 复制配置
 | 
				
			||||||
 | 
					const copySettings = () => {
 | 
				
			||||||
 | 
					  const settings: App.AppSettings = {
 | 
				
			||||||
 | 
					    theme: 'light',
 | 
				
			||||||
 | 
					    themeColor: appStore.themeColor,
 | 
				
			||||||
 | 
					    tab: appStore.tab,
 | 
				
			||||||
 | 
					    tabMode: appStore.tabMode,
 | 
				
			||||||
 | 
					    animate: appStore.animate,
 | 
				
			||||||
 | 
					    animateMode: appStore.animateMode,
 | 
				
			||||||
 | 
					    menuCollapse: appStore.menuCollapse,
 | 
				
			||||||
 | 
					    menuAccordion: appStore.menuAccordion,
 | 
				
			||||||
 | 
					    menuDark: appStore.menuDark,
 | 
				
			||||||
 | 
					    copyrightDisplay: appStore.copyrightDisplay,
 | 
				
			||||||
 | 
					    layout: appStore.layout,
 | 
				
			||||||
 | 
					    isOpenWatermark: appStore.isOpenWatermark,
 | 
				
			||||||
 | 
					    watermark: appStore.watermark,
 | 
				
			||||||
 | 
					    enableColorWeaknessMode: appStore.enableColorWeaknessMode,
 | 
				
			||||||
 | 
					    enableMourningMode: appStore.enableMourningMode,
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const settingJson = JSON.stringify(settings, null, 2)
 | 
				
			||||||
 | 
					  const { isSupported, copy } = useClipboard({ source: settingJson })
 | 
				
			||||||
 | 
					  if (isSupported) {
 | 
				
			||||||
 | 
					    copy(settingJson)
 | 
				
			||||||
 | 
					    Message.success({ content: '复制成功!' })
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    Message.error({ content: '请检查浏览器权限是否开启' })
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
defineExpose({ open })
 | 
					defineExpose({ open })
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -80,7 +80,6 @@ import { isMobile } from '@/utils'
 | 
				
			|||||||
import { getToken } from '@/utils/auth'
 | 
					import { getToken } from '@/utils/auth'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
defineOptions({ name: 'HeaderRight' })
 | 
					defineOptions({ name: 'HeaderRight' })
 | 
				
			||||||
 | 
					 | 
				
			||||||
let socket: WebSocket
 | 
					let socket: WebSocket
 | 
				
			||||||
onBeforeUnmount(() => {
 | 
					onBeforeUnmount(() => {
 | 
				
			||||||
  if (socket) {
 | 
					  if (socket) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,11 +2,11 @@ import { defineStore } from 'pinia'
 | 
				
			|||||||
import { computed, reactive, toRefs } from 'vue'
 | 
					import { computed, reactive, toRefs } from 'vue'
 | 
				
			||||||
import { generate, getRgbStr } from '@arco-design/color'
 | 
					import { generate, getRgbStr } from '@arco-design/color'
 | 
				
			||||||
import { type BasicConfig, listSiteOptionDict } from '@/apis'
 | 
					import { type BasicConfig, listSiteOptionDict } from '@/apis'
 | 
				
			||||||
import defaultSettings from '@/config/setting.json'
 | 
					import { getSettings } from '@/config/setting'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const storeSetup = () => {
 | 
					const storeSetup = () => {
 | 
				
			||||||
  // App配置
 | 
					  // App配置
 | 
				
			||||||
  const settingConfig = reactive({ ...defaultSettings }) as App.SettingConfig
 | 
					  const settingConfig = reactive({ ...getSettings() }) as App.AppSettings
 | 
				
			||||||
  // 页面切换动画类名
 | 
					  // 页面切换动画类名
 | 
				
			||||||
  const transitionName = computed(() => (settingConfig.animate ? settingConfig.animateMode : ''))
 | 
					  const transitionName = computed(() => (settingConfig.animate ? settingConfig.animateMode : ''))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -81,6 +81,27 @@ const storeSetup = () => {
 | 
				
			|||||||
    document.title = config.SITE_TITLE || ''
 | 
					    document.title = config.SITE_TITLE || ''
 | 
				
			||||||
    document.querySelector('link[rel="shortcut icon"]')?.setAttribute('href', config.SITE_FAVICON || '/favicon.ico')
 | 
					    document.querySelector('link[rel="shortcut icon"]')?.setAttribute('href', config.SITE_FAVICON || '/favicon.ico')
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  // 监听 色弱模式 和 哀悼模式
 | 
				
			||||||
 | 
					  watch([
 | 
				
			||||||
 | 
					    () => settingConfig.enableMourningMode,
 | 
				
			||||||
 | 
					    () => settingConfig.enableColorWeaknessMode,
 | 
				
			||||||
 | 
					  ], ([mourningMode, colorWeaknessMode]) => {
 | 
				
			||||||
 | 
					    const filters = [] as string[]
 | 
				
			||||||
 | 
					    if (mourningMode) {
 | 
				
			||||||
 | 
					      filters.push('grayscale(100%)')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (colorWeaknessMode) {
 | 
				
			||||||
 | 
					      filters.push('invert(80%)')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // 如果没有任何滤镜条件,移除 `filter` 样式
 | 
				
			||||||
 | 
					    if (filters.length === 0) {
 | 
				
			||||||
 | 
					      document.documentElement.style.removeProperty('filter')
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      document.documentElement.style.setProperty('filter', filters.join(' '))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }, {
 | 
				
			||||||
 | 
					    immediate: true,
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const getFavicon = () => {
 | 
					  const getFavicon = () => {
 | 
				
			||||||
    return siteConfig.SITE_FAVICON
 | 
					    return siteConfig.SITE_FAVICON
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										30
									
								
								src/types/app.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										30
									
								
								src/types/app.d.ts
									
									
									
									
										vendored
									
									
								
							@@ -1,20 +1,22 @@
 | 
				
			|||||||
declare namespace App {
 | 
					declare namespace App {
 | 
				
			||||||
  /** 系统配置 */
 | 
					  interface AppSettings {
 | 
				
			||||||
  interface SettingConfig {
 | 
					    theme: 'light' | 'dark'
 | 
				
			||||||
    theme: 'light' | 'dark' // 主题
 | 
					    themeColor: string
 | 
				
			||||||
    themeColor: string // 主题色
 | 
					    tab: boolean
 | 
				
			||||||
    tab: boolean // 是否显示页签
 | 
					    tabMode: 'card' | 'card-gutter' | 'rounded'
 | 
				
			||||||
    tabMode: TabType // 页签风格
 | 
					    animate: boolean
 | 
				
			||||||
    animate: boolean // 是否显示动画
 | 
					    animateMode: 'zoom-fade' | 'slide-dynamic-origin' | 'fade-slide' | 'fade' | 'fade-bottom' | 'fade-scale'
 | 
				
			||||||
    animateMode: AnimateType // 动画类名
 | 
					    menuCollapse: boolean
 | 
				
			||||||
    menuCollapse: boolean // 左侧菜单折叠状态
 | 
					    menuAccordion: boolean
 | 
				
			||||||
    menuAccordion: boolean // 左侧菜单手风琴效果
 | 
					    menuDark: boolean
 | 
				
			||||||
    copyrightDisplay: boolean // 是否显示底部版权信息
 | 
					    copyrightDisplay: boolean
 | 
				
			||||||
    menuDark: boolean // 菜单深色模式
 | 
					 | 
				
			||||||
    layout: 'left' | 'mix'
 | 
					    layout: 'left' | 'mix'
 | 
				
			||||||
    isOpenWatermark: boolean // 是否开启水印
 | 
					    isOpenWatermark?: boolean
 | 
				
			||||||
    watermark: string // 水印
 | 
					    watermark?: string
 | 
				
			||||||
 | 
					    enableColorWeaknessMode?: boolean
 | 
				
			||||||
 | 
					    enableMourningMode?: boolean
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** 导航页签的样式类型 */
 | 
					  /** 导航页签的样式类型 */
 | 
				
			||||||
  type TabType = 'card' | 'card-gutter' | 'rounded'
 | 
					  type TabType = 'card' | 'card-gutter' | 'rounded'
 | 
				
			||||||
  interface TabItem {
 | 
					  interface TabItem {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1
									
								
								src/types/env.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								src/types/env.d.ts
									
									
									
									
										vendored
									
									
								
							@@ -5,6 +5,7 @@ interface ImportMetaEnv {
 | 
				
			|||||||
  readonly VITE_API_PREFIX: string
 | 
					  readonly VITE_API_PREFIX: string
 | 
				
			||||||
  readonly VITE_API_BASE_URL: string
 | 
					  readonly VITE_API_BASE_URL: string
 | 
				
			||||||
  readonly VITE_BASE: string
 | 
					  readonly VITE_BASE: string
 | 
				
			||||||
 | 
					  readonly VITE_APP_SETTING: string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface ImportMeta {
 | 
					interface ImportMeta {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user