mirror of
https://github.com/xiangshu233/vue3-vant4-mobile.git
synced 2025-04-06 03:57:47 +08:00
commit
bff68425c4
@ -53,6 +53,7 @@
|
|||||||
"qs": "^6.11.2",
|
"qs": "^6.11.2",
|
||||||
"vant": "^4.8.1",
|
"vant": "^4.8.1",
|
||||||
"vue": "^3.3.13",
|
"vue": "^3.3.13",
|
||||||
|
"vue-i18n": "^9.10.1",
|
||||||
"vue-router": "4.2.5"
|
"vue-router": "4.2.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
41
pnpm-lock.yaml
generated
41
pnpm-lock.yaml
generated
@ -47,6 +47,9 @@ dependencies:
|
|||||||
vue:
|
vue:
|
||||||
specifier: ^3.3.13
|
specifier: ^3.3.13
|
||||||
version: 3.3.13(typescript@5.3.3)
|
version: 3.3.13(typescript@5.3.3)
|
||||||
|
vue-i18n:
|
||||||
|
specifier: ^9.10.1
|
||||||
|
version: 9.10.2(vue@3.3.13)
|
||||||
vue-router:
|
vue-router:
|
||||||
specifier: 4.2.5
|
specifier: 4.2.5
|
||||||
version: 4.2.5(vue@3.3.13)
|
version: 4.2.5(vue@3.3.13)
|
||||||
@ -1316,6 +1319,27 @@ packages:
|
|||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@intlify/core-base@9.10.2:
|
||||||
|
resolution: {integrity: sha512-HGStVnKobsJL0DoYIyRCGXBH63DMQqEZxDUGrkNI05FuTcruYUtOAxyL3zoAZu/uDGO6mcUvm3VXBaHG2GdZCg==}
|
||||||
|
engines: {node: '>= 16'}
|
||||||
|
dependencies:
|
||||||
|
'@intlify/message-compiler': 9.10.2
|
||||||
|
'@intlify/shared': 9.10.2
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@intlify/message-compiler@9.10.2:
|
||||||
|
resolution: {integrity: sha512-ntY/kfBwQRtX5Zh6wL8cSATujPzWW2ZQd1QwKyWwAy5fMqJyyixHMeovN4fmEyCqSu+hFfYOE63nU94evsy4YA==}
|
||||||
|
engines: {node: '>= 16'}
|
||||||
|
dependencies:
|
||||||
|
'@intlify/shared': 9.10.2
|
||||||
|
source-map-js: 1.0.2
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@intlify/shared@9.10.2:
|
||||||
|
resolution: {integrity: sha512-ttHCAJkRy7R5W2S9RVnN9KYQYPIpV2+GiS79T4EE37nrPyH6/1SrOh3bmdCRC1T3ocL8qCDx7x2lBJ0xaITU7Q==}
|
||||||
|
engines: {node: '>= 16'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@jridgewell/gen-mapping@0.3.3:
|
/@jridgewell/gen-mapping@0.3.3:
|
||||||
resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==}
|
resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==}
|
||||||
engines: {node: '>=6.0.0'}
|
engines: {node: '>=6.0.0'}
|
||||||
@ -1449,6 +1473,7 @@ packages:
|
|||||||
resolution: {integrity: sha512-u3XkZVvxcvlAOlQJ3UsD1rFvLWqu4Ef/Ggl40WAVCuogf4S1nJPHh5RTgqYFpCOvuGJ7H5yGHabjFKEZGExk5Q==}
|
resolution: {integrity: sha512-u3XkZVvxcvlAOlQJ3UsD1rFvLWqu4Ef/Ggl40WAVCuogf4S1nJPHh5RTgqYFpCOvuGJ7H5yGHabjFKEZGExk5Q==}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
@ -1457,6 +1482,7 @@ packages:
|
|||||||
resolution: {integrity: sha512-0XSYN/rfWShW+i+qjZ0phc6vZ7UWI8XWNz4E/l+6edFt+FxoEghrJHjX1EY/kcUGCnZzYYRCl31SNdfOi450Aw==}
|
resolution: {integrity: sha512-0XSYN/rfWShW+i+qjZ0phc6vZ7UWI8XWNz4E/l+6edFt+FxoEghrJHjX1EY/kcUGCnZzYYRCl31SNdfOi450Aw==}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
@ -1465,6 +1491,7 @@ packages:
|
|||||||
resolution: {integrity: sha512-LmYIO65oZVfFt9t6cpYkbC4d5lKHLYv5B4CSHRpnANq0VZUQXGcCPXHzbCXCz4RQnx7jvlYB1ISVNCE/omz5cw==}
|
resolution: {integrity: sha512-LmYIO65oZVfFt9t6cpYkbC4d5lKHLYv5B4CSHRpnANq0VZUQXGcCPXHzbCXCz4RQnx7jvlYB1ISVNCE/omz5cw==}
|
||||||
cpu: [riscv64]
|
cpu: [riscv64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
@ -1473,6 +1500,7 @@ packages:
|
|||||||
resolution: {integrity: sha512-kr8rEPQ6ns/Lmr/hiw8sEVj9aa07gh1/tQF2Y5HrNCCEPiCBGnBUt9tVusrcBBiJfIt1yNaXN6r1CCmpbFEDpg==}
|
resolution: {integrity: sha512-kr8rEPQ6ns/Lmr/hiw8sEVj9aa07gh1/tQF2Y5HrNCCEPiCBGnBUt9tVusrcBBiJfIt1yNaXN6r1CCmpbFEDpg==}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
@ -1481,6 +1509,7 @@ packages:
|
|||||||
resolution: {integrity: sha512-t4QSR7gN+OEZLG0MiCgPqMWZGwmeHhsM4AkegJ0Kiy6TnJ9vZ8dEIwHw1LcZKhbHxTY32hp9eVCMdR3/I8MGRw==}
|
resolution: {integrity: sha512-t4QSR7gN+OEZLG0MiCgPqMWZGwmeHhsM4AkegJ0Kiy6TnJ9vZ8dEIwHw1LcZKhbHxTY32hp9eVCMdR3/I8MGRw==}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
@ -7268,6 +7297,18 @@ packages:
|
|||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/vue-i18n@9.10.2(vue@3.3.13):
|
||||||
|
resolution: {integrity: sha512-ECJ8RIFd+3c1d3m1pctQ6ywG5Yj8Efy1oYoAKQ9neRdkLbuKLVeW4gaY5HPkD/9ssf1pOnUrmIFjx2/gkGxmEw==}
|
||||||
|
engines: {node: '>= 16'}
|
||||||
|
peerDependencies:
|
||||||
|
vue: ^3.0.0
|
||||||
|
dependencies:
|
||||||
|
'@intlify/core-base': 9.10.2
|
||||||
|
'@intlify/shared': 9.10.2
|
||||||
|
'@vue/devtools-api': 6.5.1
|
||||||
|
vue: 3.3.13(typescript@5.3.3)
|
||||||
|
dev: false
|
||||||
|
|
||||||
/vue-router@4.2.5(vue@3.3.13):
|
/vue-router@4.2.5(vue@3.3.13):
|
||||||
resolution: {integrity: sha512-DIUpKcyg4+PTQKfFPX88UWhlagBEBEfJ5A8XDXRJLUnZOvcpMF8o/dnL90vpVkGaPbjvXazV/rC1qBKrZlFugw==}
|
resolution: {integrity: sha512-DIUpKcyg4+PTQKfFPX88UWhlagBEBEfJ5A8XDXRJLUnZOvcpMF8o/dnL90vpVkGaPbjvXazV/rC1qBKrZlFugw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
1
src/components/locale-picker/index.ts
Normal file
1
src/components/locale-picker/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default as LocalePicker } from './index.vue'
|
39
src/components/locale-picker/index.vue
Normal file
39
src/components/locale-picker/index.vue
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<template>
|
||||||
|
<van-popup position="bottom" round @update:show="emit('update:show', $event)">
|
||||||
|
<van-picker
|
||||||
|
v-model="language"
|
||||||
|
:columns="localeList"
|
||||||
|
@confirm="handleMenuClick"
|
||||||
|
@cancel="emit('update:show', false);"
|
||||||
|
/>
|
||||||
|
</van-popup>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, unref, watchEffect } from 'vue'
|
||||||
|
import { useLocale } from '@/locales/useLocale'
|
||||||
|
import { type LocaleType, localeList } from '@/locales/config'
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:show'])
|
||||||
|
const language = ref()
|
||||||
|
const selectedKeys = ref<string[]>([])
|
||||||
|
|
||||||
|
const { changeLocale, getLocale } = useLocale()
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
selectedKeys.value = [unref(getLocale)]
|
||||||
|
})
|
||||||
|
|
||||||
|
async function toggleLocale(lang: LocaleType | string) {
|
||||||
|
await changeLocale(lang as LocaleType)
|
||||||
|
selectedKeys.value = [lang as string]
|
||||||
|
emit('update:show', false)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleMenuClick({ selectedValues: [key] }) {
|
||||||
|
if (unref(getLocale) === key) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
toggleLocale(key as string)
|
||||||
|
}
|
||||||
|
</script>
|
1
src/components/title-i18n/index.ts
Normal file
1
src/components/title-i18n/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default as TitleI18n } from './index.vue'
|
26
src/components/title-i18n/index.vue
Normal file
26
src/components/title-i18n/index.vue
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<template>
|
||||||
|
<i18n-t tag="span" :keypath="getTitle" scope="global" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { type PropType, computed } from 'vue'
|
||||||
|
import { useLangStore } from '@/store/modules/lang'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
title: {
|
||||||
|
type: [String, Object] as PropType<string | Title18n | any>,
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const localeStore = useLangStore()
|
||||||
|
|
||||||
|
const getTitle = computed(() => {
|
||||||
|
const { title = '' } = props
|
||||||
|
if (typeof title === 'object') {
|
||||||
|
return title?.[localeStore.lang] ?? title
|
||||||
|
}
|
||||||
|
return title
|
||||||
|
})
|
||||||
|
</script>
|
70
src/hooks/useI18n.ts
Normal file
70
src/hooks/useI18n.ts
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import type { Composer } from 'vue-i18n'
|
||||||
|
import * as locales from '@/locales'
|
||||||
|
|
||||||
|
type I18nGlobalTranslation = Composer['t']
|
||||||
|
type I18nTranslationRestParameters = [string, any]
|
||||||
|
|
||||||
|
function getKey(namespace: string | undefined, key: string) {
|
||||||
|
if (!namespace) {
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
if (key.startsWith(namespace)) {
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
return `${namespace}.${key}`
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useI18n(namespace?: string): {
|
||||||
|
t: I18nGlobalTranslation
|
||||||
|
} {
|
||||||
|
const i18n = locales.i18n
|
||||||
|
const normalFn = {
|
||||||
|
t: (key: string) => {
|
||||||
|
return getKey(namespace, key)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!i18n) {
|
||||||
|
return normalFn
|
||||||
|
}
|
||||||
|
|
||||||
|
const { t } = i18n.global
|
||||||
|
|
||||||
|
const tFn: I18nGlobalTranslation = (key: string, ...arg: any[]) => {
|
||||||
|
if (!key) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
if (!key.includes('.') && !namespace) {
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
return t(getKey(namespace, key), ...(arg as I18nTranslationRestParameters))
|
||||||
|
}
|
||||||
|
console.log(i18n.global)
|
||||||
|
return Object.assign(i18n.global, { t: tFn })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 国际化转换工具函数,主要用于处理动态路由的title
|
||||||
|
* @param {string | Title18n} message message
|
||||||
|
* @param isI18n 默认为true,获取对应的翻译文本,否则返回本身
|
||||||
|
* @returns message
|
||||||
|
*/
|
||||||
|
export function transformI18n(message: string | Title18n = '', isI18n = true) {
|
||||||
|
if (!message) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
const i18n = locales.i18n
|
||||||
|
|
||||||
|
// 处理动态路由的title, 格式 {zh_CN:"",en_US:""}
|
||||||
|
if (typeof message === 'object') {
|
||||||
|
return message[i18n.global?.locale]
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isI18n && typeof message === 'string') {
|
||||||
|
return i18n.global.t(message)
|
||||||
|
}
|
||||||
|
return message
|
||||||
|
}
|
||||||
|
|
||||||
|
// 主要用于配合vscode i18nn ally插件的提示。此功能仅用于路由和菜单。请在其他地方使用 vue-i18n 的 useI18n
|
||||||
|
export const t = (key: string) => key
|
@ -1,7 +1,11 @@
|
|||||||
<!-- eslint-disable prettier/prettier -->
|
<!-- eslint-disable prettier/prettier -->
|
||||||
<template>
|
<template>
|
||||||
<div class="h-screen flex flex-col">
|
<div class="h-screen flex flex-col">
|
||||||
<van-nav-bar v-if="getShowHeader" placeholder fixed :title="getTitle" />
|
<van-nav-bar v-if="getShowHeader" placeholder fixed>
|
||||||
|
<template #title>
|
||||||
|
<TitleI18n :title="getTitle" />
|
||||||
|
</template>
|
||||||
|
</van-nav-bar>
|
||||||
<routerView class="flex-1 overflow-x-hidden">
|
<routerView class="flex-1 overflow-x-hidden">
|
||||||
<template #default="{ Component, route }">
|
<template #default="{ Component, route }">
|
||||||
<!--
|
<!--
|
||||||
@ -27,7 +31,7 @@
|
|||||||
<template #icon>
|
<template #icon>
|
||||||
<i :class="menu.meta?.icon" />
|
<i :class="menu.meta?.icon" />
|
||||||
</template>
|
</template>
|
||||||
{{ menu.meta?.title }}
|
<TitleI18n :title="menu.meta?.title" />
|
||||||
</van-tabbar-item>
|
</van-tabbar-item>
|
||||||
</van-tabbar>
|
</van-tabbar>
|
||||||
</div>
|
</div>
|
||||||
@ -39,6 +43,7 @@ import { computed } from 'vue'
|
|||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import type { RouteRecordRaw } from 'vue-router'
|
import type { RouteRecordRaw } from 'vue-router'
|
||||||
import { useRouteStore } from '@/store/modules/route'
|
import { useRouteStore } from '@/store/modules/route'
|
||||||
|
import { TitleI18n } from '@/components/title-i18n'
|
||||||
|
|
||||||
const routeStore = useRouteStore()
|
const routeStore = useRouteStore()
|
||||||
// 需要缓存的路由组件
|
// 需要缓存的路由组件
|
||||||
|
21
src/locales/config.ts
Normal file
21
src/locales/config.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
export type LocaleType = keyof typeof localeMap
|
||||||
|
|
||||||
|
export const localeMap = {
|
||||||
|
zh_CN: 'zh_CN',
|
||||||
|
en_US: 'en_US',
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export const localeList = [
|
||||||
|
{
|
||||||
|
value: localeMap.en_US,
|
||||||
|
text: 'English',
|
||||||
|
icon: '🇺🇸',
|
||||||
|
title: 'Language',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: localeMap.zh_CN,
|
||||||
|
text: '简体中文',
|
||||||
|
icon: '🇨🇳',
|
||||||
|
title: '语言',
|
||||||
|
},
|
||||||
|
]
|
37
src/locales/helper.ts
Normal file
37
src/locales/helper.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import { set } from 'lodash-es'
|
||||||
|
import type { LocaleType } from './config'
|
||||||
|
|
||||||
|
export const loadLocalePool: LocaleType[] = []
|
||||||
|
|
||||||
|
export function setHtmlPageLang(locale: LocaleType) {
|
||||||
|
document.querySelector('html')?.setAttribute('lang', locale)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setLoadLocalePool(cb: (loadLocalePool: LocaleType[]) => void) {
|
||||||
|
cb(loadLocalePool)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function genMessage(langs: Record<string, Record<string, any>>, prefix = 'lang') {
|
||||||
|
const obj: Recordable = {}
|
||||||
|
|
||||||
|
Object.keys(langs).forEach((key) => {
|
||||||
|
const langFileModule = langs[key].default
|
||||||
|
let fileName = key.replace(`./${prefix}/`, '').replace(/^\.\//, '')
|
||||||
|
const lastIndex = fileName.lastIndexOf('.')
|
||||||
|
fileName = fileName.substring(0, lastIndex)
|
||||||
|
const keyList = fileName.split('/')
|
||||||
|
const moduleName = keyList.shift()
|
||||||
|
const objKey = keyList.join('.')
|
||||||
|
|
||||||
|
if (moduleName) {
|
||||||
|
if (objKey) {
|
||||||
|
set(obj, moduleName, obj[moduleName] || {})
|
||||||
|
set(obj[moduleName], objKey, langFileModule)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
set(obj, moduleName, langFileModule || {})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return obj
|
||||||
|
}
|
41
src/locales/index.ts
Normal file
41
src/locales/index.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { createI18n } from 'vue-i18n'
|
||||||
|
import type { App } from 'vue'
|
||||||
|
import { setHtmlPageLang, setLoadLocalePool } from './helper.js'
|
||||||
|
import { localeMap } from './config.js'
|
||||||
|
import { useLangStoreWidthOut } from '@/store/modules/lang.js'
|
||||||
|
|
||||||
|
async function createI18nOptions() {
|
||||||
|
const localeStore = useLangStoreWidthOut()
|
||||||
|
const locale = localeStore.getLanguage
|
||||||
|
const defaultLocal = await import(`./package/${locale}.ts`)
|
||||||
|
const message = defaultLocal.default?.message ?? {}
|
||||||
|
|
||||||
|
setHtmlPageLang(locale)
|
||||||
|
setLoadLocalePool((loadLocalePool) => {
|
||||||
|
loadLocalePool.push(locale)
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
locale,
|
||||||
|
legacy: false,
|
||||||
|
fallbackLocale: localeMap.zh_CN, // set fallback locale
|
||||||
|
messages: {
|
||||||
|
[locale]: message as { [key: string]: string },
|
||||||
|
},
|
||||||
|
globalInjection: true,
|
||||||
|
silentTranslationWarn: true, // true - warning off
|
||||||
|
missingWarn: false,
|
||||||
|
silentFallbackWarn: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getI18n = (async () => createI18n(await createI18nOptions()))()
|
||||||
|
|
||||||
|
export const i18n: Awaited<typeof getI18n> = null as any
|
||||||
|
|
||||||
|
getI18n.then(res => (i18n = res))
|
||||||
|
|
||||||
|
export async function setupI18n(app: App) {
|
||||||
|
await getI18n
|
||||||
|
app.use(i18n)
|
||||||
|
}
|
40
src/locales/package/en-US/common.ts
Normal file
40
src/locales/package/en-US/common.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
export default {
|
||||||
|
okText: 'OK',
|
||||||
|
closeText: 'Close',
|
||||||
|
cancelText: 'Cancel',
|
||||||
|
loadingText: 'Loading...',
|
||||||
|
saveText: 'Save',
|
||||||
|
delText: 'Delete',
|
||||||
|
resetText: 'Reset',
|
||||||
|
searchText: 'Search',
|
||||||
|
queryText: 'Search',
|
||||||
|
changeLanguageText: 'Change Language',
|
||||||
|
|
||||||
|
inputText: 'Please enter',
|
||||||
|
chooseText: 'Please choose',
|
||||||
|
|
||||||
|
redo: 'Refresh',
|
||||||
|
back: 'Back',
|
||||||
|
|
||||||
|
light: 'Light',
|
||||||
|
dark: 'Dark',
|
||||||
|
|
||||||
|
// 性别
|
||||||
|
Gender: {
|
||||||
|
male: 'Male',
|
||||||
|
female: 'Female',
|
||||||
|
},
|
||||||
|
Industry: {
|
||||||
|
student: 'Student',
|
||||||
|
worker: 'Worker',
|
||||||
|
education: 'Education',
|
||||||
|
finance: 'Finance',
|
||||||
|
health: 'Health',
|
||||||
|
life: 'Life',
|
||||||
|
tech: 'Technology',
|
||||||
|
IT: 'IT',
|
||||||
|
manager: 'Manager',
|
||||||
|
salesman: 'Salesman',
|
||||||
|
other: 'Other',
|
||||||
|
},
|
||||||
|
}
|
28
src/locales/package/en-US/component.ts
Normal file
28
src/locales/package/en-US/component.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
export default {
|
||||||
|
excel: {
|
||||||
|
exportModalTitle: 'Export data',
|
||||||
|
fileType: 'File type',
|
||||||
|
fileName: 'File name',
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
putAway: 'Put away',
|
||||||
|
unfold: 'Unfold',
|
||||||
|
maxTip: 'The number of characters should be less than {0}',
|
||||||
|
apiSelectNotFound: 'Wait for data loading to complete...',
|
||||||
|
},
|
||||||
|
table: {
|
||||||
|
settingDens: 'Density',
|
||||||
|
settingDensDefault: 'Default',
|
||||||
|
settingDensMiddle: 'Middle',
|
||||||
|
settingDensSmall: 'Compact',
|
||||||
|
settingColumn: 'Column settings',
|
||||||
|
settingColumnShow: 'Column display',
|
||||||
|
settingIndexColumnShow: 'Index Column',
|
||||||
|
settingFixedLeft: 'Fixed Left',
|
||||||
|
settingFixedRight: 'Fixed Right',
|
||||||
|
settingFullScreen: 'Full Screen',
|
||||||
|
settingBordered: 'Bordered',
|
||||||
|
index: 'Index',
|
||||||
|
total: 'total of {total}',
|
||||||
|
},
|
||||||
|
}
|
21
src/locales/package/en-US/layout.ts
Normal file
21
src/locales/package/en-US/layout.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
export default {
|
||||||
|
footer: { home: 'Home', chart: 'Chart', example: 'Example', my: 'My' },
|
||||||
|
setting: {
|
||||||
|
darkMode: 'Dark mode',
|
||||||
|
animation: 'Animation',
|
||||||
|
sysTheme: 'System theme',
|
||||||
|
switchAnimation: 'Switch animation',
|
||||||
|
animationType: 'Animation type',
|
||||||
|
changeLanguage: 'Language',
|
||||||
|
//
|
||||||
|
diabloMode: 'Diablo mode',
|
||||||
|
systemThemeColors: 'System theme colors',
|
||||||
|
pageAnimationSwitch: 'Page animation switch',
|
||||||
|
gradient: 'Gradient',
|
||||||
|
flashes: 'Flashes',
|
||||||
|
slide: 'Slide',
|
||||||
|
fade: 'Fade',
|
||||||
|
zoomFade: 'Zoom fade',
|
||||||
|
bottomFade: 'Bottom fade',
|
||||||
|
},
|
||||||
|
}
|
4
src/locales/package/en-US/routes/account.ts
Normal file
4
src/locales/package/en-US/routes/account.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export default {
|
||||||
|
settings: 'settings',
|
||||||
|
about: 'about',
|
||||||
|
}
|
11
src/locales/package/en-US/routes/basic.ts
Normal file
11
src/locales/package/en-US/routes/basic.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
export default {
|
||||||
|
login: 'Login',
|
||||||
|
rememberMe: 'Remember Me',
|
||||||
|
forgetPassword: 'forget Password',
|
||||||
|
register: 'Register',
|
||||||
|
phone: 'Phone',
|
||||||
|
code: 'Code',
|
||||||
|
sendCode: 'Send Code',
|
||||||
|
policy: 'I agree to xxx\'s privacy policy',
|
||||||
|
errorLogList: 'Error Log',
|
||||||
|
}
|
6
src/locales/package/en-US/routes/dashboard.ts
Normal file
6
src/locales/package/en-US/routes/dashboard.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export default {
|
||||||
|
dashboard: 'Dashboard',
|
||||||
|
about: 'About',
|
||||||
|
workbench: 'Workbench',
|
||||||
|
analysis: 'Analysis',
|
||||||
|
}
|
20
src/locales/package/en-US/routes/demo.ts
Normal file
20
src/locales/package/en-US/routes/demo.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
export default {
|
||||||
|
demo: 'Demo',
|
||||||
|
button: 'Button Extension',
|
||||||
|
modal: 'Draggable Modal',
|
||||||
|
form: {
|
||||||
|
demo: 'Form Demo',
|
||||||
|
basic: 'Basic Form',
|
||||||
|
rule: 'Rule Form',
|
||||||
|
dynamic: 'Dynamic Form',
|
||||||
|
customForm: 'Custom Form Component',
|
||||||
|
},
|
||||||
|
table: {
|
||||||
|
demo: 'Table Demo',
|
||||||
|
searchTable: 'Search Table',
|
||||||
|
editRowTable: 'Editable Rows',
|
||||||
|
wzry: 'Honor of Kings',
|
||||||
|
lol: 'League of Legends',
|
||||||
|
},
|
||||||
|
icon: 'Custom Icon',
|
||||||
|
}
|
34
src/locales/package/en-US/routes/my.ts
Normal file
34
src/locales/package/en-US/routes/my.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
export default {
|
||||||
|
settings: 'UserInfo',
|
||||||
|
accountSetting: 'AccountSetting',
|
||||||
|
privacy: 'Privacy',
|
||||||
|
logonout: 'Logonout',
|
||||||
|
basicInfo: 'Basic Information',
|
||||||
|
avatar: 'Avatar',
|
||||||
|
username: 'Username',
|
||||||
|
nickname: 'Nickname',
|
||||||
|
gender: 'Gender',
|
||||||
|
sign: 'Sign',
|
||||||
|
trade: 'Trade',
|
||||||
|
cover: 'Cover',
|
||||||
|
phone: 'Phone',
|
||||||
|
email: 'Email',
|
||||||
|
address: 'Address',
|
||||||
|
save: 'Save',
|
||||||
|
cancel: 'Cancel',
|
||||||
|
change: 'Change',
|
||||||
|
oldpassword: 'Old Password',
|
||||||
|
password: 'Password',
|
||||||
|
newpassword: 'New Password',
|
||||||
|
confirmpassword: 'Confirm Password',
|
||||||
|
oldpasswordtips: 'Old Password',
|
||||||
|
newpasswordtips: 'New Password',
|
||||||
|
changePassword: 'Change Password',
|
||||||
|
confirmLogout: 'Are you sure to log out?',
|
||||||
|
logout: 'Logout',
|
||||||
|
logoutSuccess: 'Logout Success',
|
||||||
|
logoutFailed: 'Logout Failed',
|
||||||
|
editUserInfo: 'Modify Personal Information',
|
||||||
|
editNickname: 'Edit Nickname',
|
||||||
|
editSignature: 'Edit Signature',
|
||||||
|
}
|
13
src/locales/package/en_US.ts
Normal file
13
src/locales/package/en_US.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import antdLocale from 'vant/es/locale/lang/en-US'
|
||||||
|
import { genMessage } from '../helper'
|
||||||
|
|
||||||
|
const modulesFiles = import.meta.glob<Recordable>('./en-US/**/*.ts', { eager: true })
|
||||||
|
|
||||||
|
export default {
|
||||||
|
message: {
|
||||||
|
...genMessage(modulesFiles, 'en-US'),
|
||||||
|
antdLocale,
|
||||||
|
},
|
||||||
|
dateLocale: null,
|
||||||
|
dateLocaleName: 'en-US',
|
||||||
|
}
|
41
src/locales/package/zh-CN/common.ts
Normal file
41
src/locales/package/zh-CN/common.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
export default {
|
||||||
|
okText: '确认',
|
||||||
|
closeText: '关闭',
|
||||||
|
cancelText: '取消',
|
||||||
|
loadingText: '加载中...',
|
||||||
|
saveText: '保存',
|
||||||
|
delText: '删除',
|
||||||
|
resetText: '重置',
|
||||||
|
searchText: '搜索',
|
||||||
|
queryText: '查询',
|
||||||
|
changeLanguageText: '切换语言',
|
||||||
|
|
||||||
|
inputText: '请输入',
|
||||||
|
chooseText: '请选择',
|
||||||
|
|
||||||
|
redo: '刷新',
|
||||||
|
back: '返回',
|
||||||
|
|
||||||
|
light: '亮色主题',
|
||||||
|
dark: '黑暗主题',
|
||||||
|
|
||||||
|
// 性别
|
||||||
|
Gender: {
|
||||||
|
male: '男',
|
||||||
|
female: '女',
|
||||||
|
},
|
||||||
|
// 行业
|
||||||
|
Industry: {
|
||||||
|
student: '学生',
|
||||||
|
worker: '职工',
|
||||||
|
education: '教育',
|
||||||
|
finance: '金融',
|
||||||
|
health: '健康',
|
||||||
|
life: '生活',
|
||||||
|
tech: '科技',
|
||||||
|
IT: 'IT',
|
||||||
|
manager: '经理',
|
||||||
|
salesman: '销售员',
|
||||||
|
other: '其他',
|
||||||
|
},
|
||||||
|
}
|
32
src/locales/package/zh-CN/component.ts
Normal file
32
src/locales/package/zh-CN/component.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
export default {
|
||||||
|
excel: {
|
||||||
|
exportModalTitle: '导出数据',
|
||||||
|
fileType: '文件类型',
|
||||||
|
fileName: '文件名',
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
putAway: '收起',
|
||||||
|
unfold: '展开',
|
||||||
|
|
||||||
|
maxTip: '字符数应小于{0}位',
|
||||||
|
|
||||||
|
apiSelectNotFound: '请等待数据加载完成...',
|
||||||
|
},
|
||||||
|
table: {
|
||||||
|
settingDens: '密度',
|
||||||
|
settingDensDefault: '默认',
|
||||||
|
settingDensMiddle: '中等',
|
||||||
|
settingDensSmall: '紧凑',
|
||||||
|
settingColumn: '列设置',
|
||||||
|
settingColumnShow: '列展示',
|
||||||
|
settingIndexColumnShow: '序号列',
|
||||||
|
settingFixedLeft: '固定到左侧',
|
||||||
|
settingFixedRight: '固定到右侧',
|
||||||
|
settingFullScreen: '全屏',
|
||||||
|
settingBordered: '边框',
|
||||||
|
|
||||||
|
index: '序号',
|
||||||
|
|
||||||
|
total: '共 {total} 条数据',
|
||||||
|
},
|
||||||
|
}
|
21
src/locales/package/zh-CN/layout.ts
Normal file
21
src/locales/package/zh-CN/layout.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
export default {
|
||||||
|
footer: { home: '首页', chart: '图表', example: '示例', my: '我的' },
|
||||||
|
setting: {
|
||||||
|
darkMode: '主题模式',
|
||||||
|
animation: '动画',
|
||||||
|
sysTheme: '系统主题设置',
|
||||||
|
switchAnimation: '切换动画',
|
||||||
|
animationType: '动画类型',
|
||||||
|
changeLanguage: '语言',
|
||||||
|
//
|
||||||
|
diabloMode: '暗黑模式',
|
||||||
|
systemThemeColors: '系统主题色',
|
||||||
|
pageAnimationSwitch: '页面切换动画',
|
||||||
|
gradient: '渐变',
|
||||||
|
flashes: '闪现',
|
||||||
|
slide: '滑动',
|
||||||
|
fade: '消退',
|
||||||
|
zoomFade: '缩放消退',
|
||||||
|
bottomFade: '底部消退',
|
||||||
|
},
|
||||||
|
}
|
4
src/locales/package/zh-CN/routes/account.ts
Normal file
4
src/locales/package/zh-CN/routes/account.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export default {
|
||||||
|
settings: '个人设置',
|
||||||
|
about: '关于',
|
||||||
|
}
|
11
src/locales/package/zh-CN/routes/basic.ts
Normal file
11
src/locales/package/zh-CN/routes/basic.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
export default {
|
||||||
|
login: '登录',
|
||||||
|
rememberMe: '记住我',
|
||||||
|
forgetPassword: '忘记密码',
|
||||||
|
register: '注册',
|
||||||
|
phone: '手机号',
|
||||||
|
code: '验证码',
|
||||||
|
sendCode: '发送验证码',
|
||||||
|
policy: '我已阅读并同意《用户协议》',
|
||||||
|
errorLogList: '错误日志列表',
|
||||||
|
}
|
6
src/locales/package/zh-CN/routes/dashboard.ts
Normal file
6
src/locales/package/zh-CN/routes/dashboard.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export default {
|
||||||
|
dashboard: '仪表盘',
|
||||||
|
about: '关于',
|
||||||
|
workbench: '工作台',
|
||||||
|
analysis: '分析页',
|
||||||
|
}
|
20
src/locales/package/zh-CN/routes/demo.ts
Normal file
20
src/locales/package/zh-CN/routes/demo.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
export default {
|
||||||
|
demo: 'demo演示',
|
||||||
|
button: '按钮的扩展',
|
||||||
|
modal: '可拖拽弹窗',
|
||||||
|
form: {
|
||||||
|
demo: '表单演示',
|
||||||
|
basic: '基础表单',
|
||||||
|
rule: '表单校验',
|
||||||
|
dynamic: '动态表单',
|
||||||
|
customForm: '自定义表单组件',
|
||||||
|
},
|
||||||
|
table: {
|
||||||
|
demo: '表格演示',
|
||||||
|
searchTable: '查询表格',
|
||||||
|
editRowTable: '可编辑行',
|
||||||
|
wzry: '王者荣耀',
|
||||||
|
lol: '英雄联盟',
|
||||||
|
},
|
||||||
|
icon: '自定义图标',
|
||||||
|
}
|
34
src/locales/package/zh-CN/routes/my.ts
Normal file
34
src/locales/package/zh-CN/routes/my.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
export default {
|
||||||
|
settings: '个人信息',
|
||||||
|
accountSetting: '账户与安全',
|
||||||
|
privacy: '隐私政策',
|
||||||
|
logonout: '退出登录',
|
||||||
|
basicInfo: '基本信息',
|
||||||
|
avatar: '头像',
|
||||||
|
username: '用户名',
|
||||||
|
nickname: '昵称',
|
||||||
|
gender: '性别',
|
||||||
|
sign: '个性签名',
|
||||||
|
trade: '行业',
|
||||||
|
cover: '封面',
|
||||||
|
phone: '手机号',
|
||||||
|
email: '邮箱',
|
||||||
|
address: '地址',
|
||||||
|
save: '保存',
|
||||||
|
cancel: '取消',
|
||||||
|
change: '修改',
|
||||||
|
oldpassword: '旧密码',
|
||||||
|
password: '密码',
|
||||||
|
newpassword: '新密码',
|
||||||
|
confirmpassword: '确认密码',
|
||||||
|
oldpasswordtips: '请输入旧密码',
|
||||||
|
newpasswordtips: '请输入新密码',
|
||||||
|
changePassword: '修改密码',
|
||||||
|
confirmLogout: '确认退出登录?',
|
||||||
|
logout: '退出登录',
|
||||||
|
logoutSuccess: '退出登录成功',
|
||||||
|
logoutFailed: '退出登录失败',
|
||||||
|
editUserInfo: '编辑个人信息',
|
||||||
|
editNickname: '编辑昵称',
|
||||||
|
editSignature: '编辑个性签名',
|
||||||
|
}
|
11
src/locales/package/zh_CN.ts
Normal file
11
src/locales/package/zh_CN.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import antdLocale from 'vant/es/locale/lang/zh-CN'
|
||||||
|
import { genMessage } from '../helper'
|
||||||
|
|
||||||
|
const modulesFiles = import.meta.glob<Recordable>('./zh-CN/**/*.ts', { eager: true })
|
||||||
|
|
||||||
|
export default {
|
||||||
|
message: {
|
||||||
|
...genMessage(modulesFiles, 'zh-CN'),
|
||||||
|
antdLocale,
|
||||||
|
},
|
||||||
|
}
|
72
src/locales/useLocale.ts
Normal file
72
src/locales/useLocale.ts
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/**
|
||||||
|
* Multi-language related operations
|
||||||
|
*/
|
||||||
|
import { computed, unref } from 'vue'
|
||||||
|
import { Locale as VantLocale } from 'vant'
|
||||||
|
import { loadLocalePool, setHtmlPageLang } from './helper'
|
||||||
|
import type { LocaleType } from './config'
|
||||||
|
import { i18n } from '.'
|
||||||
|
|
||||||
|
import { useLangStoreWidthOut } from '@/store/modules/lang'
|
||||||
|
|
||||||
|
interface LangModule {
|
||||||
|
message: Recordable
|
||||||
|
dateLocale: Recordable
|
||||||
|
dateLocaleName: string
|
||||||
|
}
|
||||||
|
|
||||||
|
function setI18nLanguage(locale: LocaleType) {
|
||||||
|
const localeStore = useLangStoreWidthOut()
|
||||||
|
if (i18n.mode === 'legacy') {
|
||||||
|
i18n.global.locale = locale
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
(i18n.global.locale as any).value = locale
|
||||||
|
}
|
||||||
|
localeStore.setLanguage(locale)
|
||||||
|
setHtmlPageLang(locale)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useLocale() {
|
||||||
|
const localeStore = useLangStoreWidthOut()
|
||||||
|
const getLocale = computed(() => localeStore.getLanguage)
|
||||||
|
|
||||||
|
const getAntdLocale = computed<any>((): any => {
|
||||||
|
return i18n.global.getLocaleMessage(unref(getLocale)).antdLocale
|
||||||
|
})
|
||||||
|
|
||||||
|
// Switching the language will change the locale of useI18n
|
||||||
|
// And submit to configuration modification
|
||||||
|
async function changeLocale(locale: LocaleType) {
|
||||||
|
const globalI18n = i18n.global
|
||||||
|
const currentLocale = unref(globalI18n.locale)
|
||||||
|
|
||||||
|
if (currentLocale === locale) {
|
||||||
|
return locale
|
||||||
|
}
|
||||||
|
const langModule = ((await import(`./package/${locale}.ts`)) as any).default as LangModule
|
||||||
|
if (!langModule) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const { message } = langModule
|
||||||
|
|
||||||
|
if (loadLocalePool.includes(locale)) {
|
||||||
|
VantLocale.use(locale, message.antdLocale)
|
||||||
|
setI18nLanguage(locale)
|
||||||
|
return locale
|
||||||
|
}
|
||||||
|
|
||||||
|
VantLocale.use(locale, message.antdLocale)
|
||||||
|
globalI18n.setLocaleMessage(locale, message)
|
||||||
|
loadLocalePool.push(locale)
|
||||||
|
|
||||||
|
setI18nLanguage(locale)
|
||||||
|
return locale
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
getLocale,
|
||||||
|
changeLocale,
|
||||||
|
getAntdLocale,
|
||||||
|
}
|
||||||
|
}
|
@ -14,6 +14,7 @@ import { createApp } from 'vue'
|
|||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
import router, { setupRouter } from './router'
|
import router, { setupRouter } from './router'
|
||||||
import { setupStore } from '@/store'
|
import { setupStore } from '@/store'
|
||||||
|
import { setupI18n } from '@/locales'
|
||||||
|
|
||||||
async function bootstrap() {
|
async function bootstrap() {
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
@ -21,6 +22,7 @@ async function bootstrap() {
|
|||||||
setupStore(app)
|
setupStore(app)
|
||||||
// 挂载路由
|
// 挂载路由
|
||||||
setupRouter(app)
|
setupRouter(app)
|
||||||
|
await setupI18n(app)
|
||||||
await router.isReady()
|
await router.isReady()
|
||||||
// 路由准备就绪后挂载APP实例
|
// 路由准备就绪后挂载APP实例
|
||||||
app.mount('#app', true)
|
app.mount('#app', true)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import type { RouteRecordRaw } from 'vue-router'
|
import type { RouteRecordRaw } from 'vue-router'
|
||||||
|
import { t } from '@/hooks/useI18n'
|
||||||
|
|
||||||
const Layout = () => import('@/layout/index.vue')
|
const Layout = () => import('@/layout/index.vue')
|
||||||
|
|
||||||
@ -9,7 +10,7 @@ const routeModuleList: Array<RouteRecordRaw> = [
|
|||||||
redirect: '/dashboard/index',
|
redirect: '/dashboard/index',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
meta: {
|
meta: {
|
||||||
title: '主控台',
|
title: t('layout.footer.home'),
|
||||||
icon: 'i-simple-icons:atlassian',
|
icon: 'i-simple-icons:atlassian',
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
@ -29,7 +30,7 @@ const routeModuleList: Array<RouteRecordRaw> = [
|
|||||||
redirect: '/message/index',
|
redirect: '/message/index',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
meta: {
|
meta: {
|
||||||
title: '图表',
|
title: t('layout.footer.chart'),
|
||||||
icon: 'i-simple-icons:soundcharts',
|
icon: 'i-simple-icons:soundcharts',
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
@ -49,7 +50,7 @@ const routeModuleList: Array<RouteRecordRaw> = [
|
|||||||
redirect: '/example/index',
|
redirect: '/example/index',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
meta: {
|
meta: {
|
||||||
title: '示例',
|
title: t('layout.footer.example'),
|
||||||
icon: 'i-material-symbols:award-star',
|
icon: 'i-material-symbols:award-star',
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
@ -69,7 +70,7 @@ const routeModuleList: Array<RouteRecordRaw> = [
|
|||||||
redirect: '/my/index',
|
redirect: '/my/index',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
meta: {
|
meta: {
|
||||||
title: '我的',
|
title: t('layout.footer.my'),
|
||||||
icon: 'i-simple-icons:docsify',
|
icon: 'i-simple-icons:docsify',
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
@ -90,7 +91,7 @@ const routeModuleList: Array<RouteRecordRaw> = [
|
|||||||
path: '/editUserInfo',
|
path: '/editUserInfo',
|
||||||
name: 'EditUserInfo',
|
name: 'EditUserInfo',
|
||||||
meta: {
|
meta: {
|
||||||
title: '编辑个人信息',
|
title: t('routes.my.editUserInfo'),
|
||||||
innerPage: true,
|
innerPage: true,
|
||||||
},
|
},
|
||||||
component: () => import('@/views/my/EditUserInfo.vue'),
|
component: () => import('@/views/my/EditUserInfo.vue'),
|
||||||
@ -99,9 +100,9 @@ const routeModuleList: Array<RouteRecordRaw> = [
|
|||||||
path: '/editNickname',
|
path: '/editNickname',
|
||||||
name: 'EditNickname',
|
name: 'EditNickname',
|
||||||
meta: {
|
meta: {
|
||||||
title: '修改昵称(该页面已缓存)',
|
title: t('routes.my.editNickname'),
|
||||||
innerPage: true,
|
innerPage: true,
|
||||||
keepAlive: true,
|
keepAlive: false,
|
||||||
},
|
},
|
||||||
component: () => import('@/views/my/EditNickname.vue'),
|
component: () => import('@/views/my/EditNickname.vue'),
|
||||||
},
|
},
|
||||||
@ -109,7 +110,7 @@ const routeModuleList: Array<RouteRecordRaw> = [
|
|||||||
path: '/editSign',
|
path: '/editSign',
|
||||||
name: 'EditSign',
|
name: 'EditSign',
|
||||||
meta: {
|
meta: {
|
||||||
title: '修改签名',
|
title: t('routes.my.editSignature'),
|
||||||
innerPage: true,
|
innerPage: true,
|
||||||
},
|
},
|
||||||
component: () => import('@/views/my/EditSign.vue'),
|
component: () => import('@/views/my/EditSign.vue'),
|
||||||
@ -118,7 +119,7 @@ const routeModuleList: Array<RouteRecordRaw> = [
|
|||||||
path: '/accountSetting',
|
path: '/accountSetting',
|
||||||
name: 'AccountSetting',
|
name: 'AccountSetting',
|
||||||
meta: {
|
meta: {
|
||||||
title: '账号与安全',
|
title: t('routes.my.accountSetting'),
|
||||||
innerPage: true,
|
innerPage: true,
|
||||||
},
|
},
|
||||||
component: () => import('@/views/my/AccountSetting.vue'),
|
component: () => import('@/views/my/AccountSetting.vue'),
|
||||||
@ -127,7 +128,7 @@ const routeModuleList: Array<RouteRecordRaw> = [
|
|||||||
path: '/changePassword',
|
path: '/changePassword',
|
||||||
name: 'ChangePassword',
|
name: 'ChangePassword',
|
||||||
meta: {
|
meta: {
|
||||||
title: '修改登录密码',
|
title: t('routes.my.changePassword'),
|
||||||
innerPage: true,
|
innerPage: true,
|
||||||
},
|
},
|
||||||
component: () => import('@/views/my/ChangePassword.vue'),
|
component: () => import('@/views/my/ChangePassword.vue'),
|
||||||
@ -136,7 +137,7 @@ const routeModuleList: Array<RouteRecordRaw> = [
|
|||||||
path: '/themeSetting',
|
path: '/themeSetting',
|
||||||
name: 'ThemeSetting',
|
name: 'ThemeSetting',
|
||||||
meta: {
|
meta: {
|
||||||
title: '主题设置',
|
title: t('layout.setting.sysTheme'),
|
||||||
innerPage: true,
|
innerPage: true,
|
||||||
},
|
},
|
||||||
component: () => import('@/views/my/ThemeSetting.vue'),
|
component: () => import('@/views/my/ThemeSetting.vue'),
|
||||||
|
42
src/store/modules/lang.ts
Normal file
42
src/store/modules/lang.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import { defineStore } from 'pinia'
|
||||||
|
import { LANG_SETTING } from '../mutation-types'
|
||||||
|
import { createStorage } from '@/utils/Storage'
|
||||||
|
import { store } from '@/store'
|
||||||
|
import { type LocaleType, localeMap } from '@/locales/config'
|
||||||
|
|
||||||
|
interface LocaleState {
|
||||||
|
lang: LocaleType
|
||||||
|
}
|
||||||
|
|
||||||
|
const Storage = createStorage({ storage: localStorage })
|
||||||
|
|
||||||
|
const locaLang = Storage.get(LANG_SETTING)
|
||||||
|
const browserLanguage = navigator.language || (navigator as any).userLanguage
|
||||||
|
const lan = locaLang || (/zh.*/i.test(browserLanguage) ? localeMap.zh_CN : localeMap.en_US)
|
||||||
|
if (!locaLang) {
|
||||||
|
Storage.set(LANG_SETTING, lan)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useLangStore = defineStore({
|
||||||
|
id: 'app-lang',
|
||||||
|
state: (): LocaleState => ({
|
||||||
|
lang: Storage.get(LANG_SETTING, localeMap.zh_CN),
|
||||||
|
}),
|
||||||
|
getters: {
|
||||||
|
getLanguage(): LocaleType {
|
||||||
|
return this.lang ?? localeMap.zh_CN
|
||||||
|
},
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
// 设置语言
|
||||||
|
setLanguage(data: LocaleType): void {
|
||||||
|
this.lang = data
|
||||||
|
Storage.set(LANG_SETTING, data)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// Need to be used outside the setup
|
||||||
|
export function useLangStoreWidthOut() {
|
||||||
|
return useLangStore(store)
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
export const ACCESS_TOKEN = 'ACCESS-TOKEN' // 用户token
|
export const ACCESS_TOKEN = 'ACCESS-TOKEN' // 用户token
|
||||||
export const CURRENT_USER = 'CURRENT-USER' // 当前用户信息
|
export const CURRENT_USER = 'CURRENT-USER' // 当前用户信息
|
||||||
export const DESIGN_SETTING = 'DESIGN-SETTING' // 当前用户主题信息
|
export const DESIGN_SETTING = 'DESIGN-SETTING' // 当前用户主题信息
|
||||||
|
export const LANG_SETTING = 'LANG-SETTING' // 当前语言
|
||||||
|
@ -2,9 +2,7 @@
|
|||||||
<div class="h-screen flex flex-col items-center justify-center p-60px">
|
<div class="h-screen flex flex-col items-center justify-center p-60px">
|
||||||
<div class="wel-box w-full flex flex-col items-center justify-between">
|
<div class="wel-box w-full flex flex-col items-center justify-between">
|
||||||
<Logo class="!h-30 !w-30" />
|
<Logo class="!h-30 !w-30" />
|
||||||
<div class="text-darkBlue dark:text-garyWhite mb-4 mt-12 text-center text-2xl font-black">
|
<div class="text-darkBlue dark:text-garyWhite mb-4 mt-12 text-center text-2xl font-black" />
|
||||||
{{ title }}
|
|
||||||
</div>
|
|
||||||
<div class="mb-6 mt-4 w-full">
|
<div class="mb-6 mt-4 w-full">
|
||||||
<van-swipe class="h-30" :autoplay="3000" :indicator-color="designStore.appTheme">
|
<van-swipe class="h-30" :autoplay="3000" :indicator-color="designStore.appTheme">
|
||||||
<van-swipe-item
|
<van-swipe-item
|
||||||
@ -26,8 +24,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue'
|
||||||
import { useDesignSettingStore } from '@/store/modules/designSetting'
|
import { useDesignSettingStore } from '@/store/modules/designSetting'
|
||||||
import { useGlobSetting } from '@/hooks/setting'
|
|
||||||
import Logo from '@/components/Logo.vue'
|
import Logo from '@/components/Logo.vue'
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
@ -35,9 +33,6 @@ defineOptions({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const designStore = useDesignSettingStore()
|
const designStore = useDesignSettingStore()
|
||||||
const globSetting = useGlobSetting()
|
|
||||||
|
|
||||||
const { title } = globSetting
|
|
||||||
|
|
||||||
const getSwipeText = computed(() => {
|
const getSwipeText = computed(() => {
|
||||||
return [
|
return [
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="my-4">
|
<div class="my-4">
|
||||||
<van-cell-group inset>
|
<van-cell-group inset>
|
||||||
<van-cell center title="🌓 暗黑模式">
|
<van-cell center :title="`🌓 ${$t('layout.setting.diabloMode')}`">
|
||||||
<template #right-icon>
|
<template #right-icon>
|
||||||
<i inline-block align-middle i="dark:carbon-moon carbon-sun" />
|
<i inline-block align-middle i="dark:carbon-moon carbon-sun" />
|
||||||
<span class="ml-2">{{ isDark ? 'Dark' : 'Light' }}</span>
|
<span class="ml-2">{{ isDark ? 'Dark' : 'Light' }}</span>
|
||||||
@ -9,15 +9,21 @@
|
|||||||
<van-switch v-model="checked" size="22" @click="toggle()" />
|
<van-switch v-model="checked" size="22" @click="toggle()" />
|
||||||
</template>
|
</template>
|
||||||
</van-cell>
|
</van-cell>
|
||||||
<template v-for="item in menuItems" :key="item.route">
|
<van-cell center is-link :title="$t('layout.setting.changeLanguage')" @click="showLanguagePicker = true">
|
||||||
<van-cell :title="item.title" :to="item.route" is-link />
|
<template #icon>
|
||||||
|
<i class="i-material-symbols:language mr-1 text-xl" />
|
||||||
</template>
|
</template>
|
||||||
|
</van-cell>
|
||||||
</van-cell-group>
|
</van-cell-group>
|
||||||
|
|
||||||
|
<LocalePicker v-model:show="showLanguagePicker" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
import { useDark, useToggle } from '@vueuse/core'
|
import { useDark, useToggle } from '@vueuse/core'
|
||||||
|
import { LocalePicker } from '@/components/locale-picker'
|
||||||
import { useDesignSettingStore } from '@/store/modules/designSetting'
|
import { useDesignSettingStore } from '@/store/modules/designSetting'
|
||||||
|
|
||||||
const designStore = useDesignSettingStore()
|
const designStore = useDesignSettingStore()
|
||||||
@ -26,6 +32,7 @@ const isDark = useDark({
|
|||||||
valueDark: 'dark',
|
valueDark: 'dark',
|
||||||
valueLight: 'light',
|
valueLight: 'light',
|
||||||
})
|
})
|
||||||
|
const showLanguagePicker = ref(false)
|
||||||
|
|
||||||
const checked = ref(isDark.value)
|
const checked = ref(isDark.value)
|
||||||
|
|
||||||
@ -35,11 +42,6 @@ function toggle() {
|
|||||||
toggleDark()
|
toggleDark()
|
||||||
designStore.setDarkMode(isDark.value ? 'dark' : 'light')
|
designStore.setDarkMode(isDark.value ? 'dark' : 'light')
|
||||||
}
|
}
|
||||||
|
|
||||||
const menuItems = [
|
|
||||||
{ title: '🐗 keep-alive', route: '/editNickname' },
|
|
||||||
{ title: '🦘 404 页演示', route: '/404' },
|
|
||||||
]
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
|
45
src/views/exception/403.vue
Normal file
45
src/views/exception/403.vue
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<template>
|
||||||
|
<div class="page-container flex flex-col justify-center">
|
||||||
|
<div class="text-center">
|
||||||
|
<img src="~@/assets/icons/exception/403.svg" alt="">
|
||||||
|
</div>
|
||||||
|
<div class="text-center">
|
||||||
|
<h1 class="text-base text-gray-500">
|
||||||
|
抱歉,你无权访问该页面
|
||||||
|
</h1>
|
||||||
|
<n-button type="info" @click="goHome">
|
||||||
|
回到首页
|
||||||
|
</n-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
function goHome() {
|
||||||
|
router.push('/')
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.page-container {
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 50px 0;
|
||||||
|
height: 100vh;
|
||||||
|
|
||||||
|
.text-center {
|
||||||
|
h1 {
|
||||||
|
color: #666;
|
||||||
|
padding: 20px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 350px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -15,6 +15,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
function goHome() {
|
function goHome() {
|
||||||
router.push('/')
|
router.push('/')
|
||||||
|
45
src/views/exception/500.vue
Normal file
45
src/views/exception/500.vue
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<template>
|
||||||
|
<div class="page-container flex flex-col justify-center">
|
||||||
|
<div class="text-center">
|
||||||
|
<img src="~@/assets/icons/exception/500.svg" alt="">
|
||||||
|
</div>
|
||||||
|
<div class="text-center">
|
||||||
|
<h1 class="text-base text-gray-500">
|
||||||
|
抱歉,服务器出错了
|
||||||
|
</h1>
|
||||||
|
<n-button type="info" @click="goHome">
|
||||||
|
回到首页
|
||||||
|
</n-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
function goHome() {
|
||||||
|
router.push('/')
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.page-container {
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 50px 0;
|
||||||
|
height: 100vh;
|
||||||
|
|
||||||
|
.text-center {
|
||||||
|
h1 {
|
||||||
|
color: #666;
|
||||||
|
padding: 20px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 350px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -4,7 +4,7 @@
|
|||||||
v-model="formData.username"
|
v-model="formData.username"
|
||||||
class="enter-y mb-4 items-center !rounded-md"
|
class="enter-y mb-4 items-center !rounded-md"
|
||||||
name="username"
|
name="username"
|
||||||
placeholder="用户名"
|
:placeholder="$t('routes.my.username')"
|
||||||
:rules="getFormRules.username"
|
:rules="getFormRules.username"
|
||||||
>
|
>
|
||||||
<template #left-icon>
|
<template #left-icon>
|
||||||
@ -16,7 +16,7 @@
|
|||||||
v-model="formData.mobile"
|
v-model="formData.mobile"
|
||||||
class="enter-y mb-4 items-center !rounded-md"
|
class="enter-y mb-4 items-center !rounded-md"
|
||||||
name="password"
|
name="password"
|
||||||
placeholder="手机号码"
|
:placeholder="$t('routes.my.phone')"
|
||||||
:rules="getFormRules.mobile"
|
:rules="getFormRules.mobile"
|
||||||
>
|
>
|
||||||
<template #left-icon>
|
<template #left-icon>
|
||||||
@ -29,7 +29,7 @@
|
|||||||
class="enter-y mb-10 items-center !rounded-md"
|
class="enter-y mb-10 items-center !rounded-md"
|
||||||
center
|
center
|
||||||
clearable
|
clearable
|
||||||
placeholder="请输入短信验证码"
|
:placeholder="$t('routes.basic.code')"
|
||||||
:rules="getFormRules.sms"
|
:rules="getFormRules.sms"
|
||||||
>
|
>
|
||||||
<template #left-icon>
|
<template #left-icon>
|
||||||
@ -37,7 +37,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<template #button>
|
<template #button>
|
||||||
<van-button size="small" type="primary">
|
<van-button size="small" type="primary">
|
||||||
发送验证码
|
{{ $t('routes.basic.sendCode') }}
|
||||||
</van-button>
|
</van-button>
|
||||||
</template>
|
</template>
|
||||||
</van-field>
|
</van-field>
|
||||||
@ -48,7 +48,7 @@
|
|||||||
native-type="submit"
|
native-type="submit"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
>
|
>
|
||||||
重 置
|
{{ $t('common.resetText') }}
|
||||||
</van-button>
|
</van-button>
|
||||||
|
|
||||||
<van-button
|
<van-button
|
||||||
@ -58,12 +58,13 @@
|
|||||||
block
|
block
|
||||||
@click="handleBackLogin"
|
@click="handleBackLogin"
|
||||||
>
|
>
|
||||||
返 回
|
{{ $t('common.back') }}
|
||||||
</van-button>
|
</van-button>
|
||||||
</van-form>
|
</van-form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { computed, reactive, ref, unref } from 'vue'
|
||||||
import type { FormInstance } from 'vant'
|
import type { FormInstance } from 'vant'
|
||||||
import { LoginStateEnum, useFormRules, useLoginState } from './useLogin'
|
import { LoginStateEnum, useFormRules, useLoginState } from './useLogin'
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
v-model="formData.username"
|
v-model="formData.username"
|
||||||
class="enter-y mb-4 items-center !rounded-md"
|
class="enter-y mb-4 items-center !rounded-md"
|
||||||
name="username"
|
name="username"
|
||||||
placeholder="用户名"
|
:placeholder="$t('routes.my.username')"
|
||||||
:rules="getFormRules.username"
|
:rules="getFormRules.username"
|
||||||
>
|
>
|
||||||
<template #left-icon>
|
<template #left-icon>
|
||||||
@ -16,7 +16,7 @@
|
|||||||
class="enter-y mb-4 items-center !rounded-md"
|
class="enter-y mb-4 items-center !rounded-md"
|
||||||
:type="switchPassType ? 'password' : 'text'"
|
:type="switchPassType ? 'password' : 'text'"
|
||||||
name="password"
|
name="password"
|
||||||
placeholder="密码"
|
:placeholder="$t('routes.my.password')"
|
||||||
:rules="getFormRules.password"
|
:rules="getFormRules.password"
|
||||||
@click-right-icon="switchPassType = !switchPassType"
|
@click-right-icon="switchPassType = !switchPassType"
|
||||||
>
|
>
|
||||||
@ -32,9 +32,9 @@
|
|||||||
<div class="enter-y mb-10 w-full flex justify-between px-5px">
|
<div class="enter-y mb-10 w-full flex justify-between px-5px">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<van-switch v-model="rememberMe" size="18px" class="mr-8px" />
|
<van-switch v-model="rememberMe" size="18px" class="mr-8px" />
|
||||||
<span>记住我</span>
|
<span>{{ $t('routes.basic.rememberMe') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<a @click="setLoginState(LoginStateEnum.RESET_PASSWORD)">忘记密码?</a>
|
<a @click="setLoginState(LoginStateEnum.RESET_PASSWORD)">{{ $t('routes.basic.forgetPassword') }}?</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<van-button
|
<van-button
|
||||||
@ -44,7 +44,7 @@
|
|||||||
native-type="submit"
|
native-type="submit"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
>
|
>
|
||||||
登 录
|
{{ $t('routes.basic.login') }}
|
||||||
</van-button>
|
</van-button>
|
||||||
<van-button
|
<van-button
|
||||||
class="enter-y !rounded-md"
|
class="enter-y !rounded-md"
|
||||||
@ -53,12 +53,14 @@
|
|||||||
block
|
block
|
||||||
@click="setLoginState(LoginStateEnum.REGISTER)"
|
@click="setLoginState(LoginStateEnum.REGISTER)"
|
||||||
>
|
>
|
||||||
注 册
|
{{ $t('routes.basic.register') }}
|
||||||
</van-button>
|
</van-button>
|
||||||
</van-form>
|
</van-form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { computed, onMounted, reactive, ref, unref } from 'vue'
|
||||||
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { showFailToast, showLoadingToast, showSuccessToast } from 'vant'
|
import { showFailToast, showLoadingToast, showSuccessToast } from 'vant'
|
||||||
import type { FormInstance } from 'vant'
|
import type { FormInstance } from 'vant'
|
||||||
import { LoginStateEnum, useFormRules, useLoginState } from './useLogin'
|
import { LoginStateEnum, useFormRules, useLoginState } from './useLogin'
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
v-model="formData.username"
|
v-model="formData.username"
|
||||||
class="enter-y items-center !rounded-md"
|
class="enter-y items-center !rounded-md"
|
||||||
name="username"
|
name="username"
|
||||||
placeholder="用户名"
|
:placeholder="$t('routes.my.username')"
|
||||||
:rules="getFormRules.username"
|
:rules="getFormRules.username"
|
||||||
>
|
>
|
||||||
<template #left-icon>
|
<template #left-icon>
|
||||||
@ -17,7 +17,7 @@
|
|||||||
v-model="formData.mobile"
|
v-model="formData.mobile"
|
||||||
class="enter-y items-center !rounded-md"
|
class="enter-y items-center !rounded-md"
|
||||||
name="password"
|
name="password"
|
||||||
placeholder="手机号码"
|
:placeholder="$t('routes.my.phone')"
|
||||||
:rules="getFormRules.mobile"
|
:rules="getFormRules.mobile"
|
||||||
>
|
>
|
||||||
<template #left-icon>
|
<template #left-icon>
|
||||||
@ -30,7 +30,7 @@
|
|||||||
class="enter-y items-center !rounded-md"
|
class="enter-y items-center !rounded-md"
|
||||||
center
|
center
|
||||||
clearable
|
clearable
|
||||||
placeholder="请输入短信验证码"
|
:placeholder="$t('routes.basic.code')"
|
||||||
:rules="getFormRules.sms"
|
:rules="getFormRules.sms"
|
||||||
>
|
>
|
||||||
<template #left-icon>
|
<template #left-icon>
|
||||||
@ -38,7 +38,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<template #button>
|
<template #button>
|
||||||
<van-button size="small" type="primary">
|
<van-button size="small" type="primary">
|
||||||
发送验证码
|
{{ $t('routes.basic.sendCode') }}
|
||||||
</van-button>
|
</van-button>
|
||||||
</template>
|
</template>
|
||||||
</van-field>
|
</van-field>
|
||||||
@ -48,7 +48,7 @@
|
|||||||
class="enter-y items-center !rounded-md"
|
class="enter-y items-center !rounded-md"
|
||||||
:type="switchPassType ? 'password' : 'text'"
|
:type="switchPassType ? 'password' : 'text'"
|
||||||
name="password"
|
name="password"
|
||||||
placeholder="密码"
|
:placeholder="$t('routes.my.password')"
|
||||||
:rules="getFormRules.password"
|
:rules="getFormRules.password"
|
||||||
@click-right-icon="switchPassType = !switchPassType"
|
@click-right-icon="switchPassType = !switchPassType"
|
||||||
>
|
>
|
||||||
@ -66,7 +66,7 @@
|
|||||||
class="enter-y items-center !rounded-md"
|
class="enter-y items-center !rounded-md"
|
||||||
:type="switchConfirmPassType ? 'password' : 'text'"
|
:type="switchConfirmPassType ? 'password' : 'text'"
|
||||||
name="confirmPassword"
|
name="confirmPassword"
|
||||||
placeholder="确认密码"
|
:placeholder="$t('routes.my.confirmpassword')"
|
||||||
:rules="getFormRules.confirmPassword"
|
:rules="getFormRules.confirmPassword"
|
||||||
@click-right-icon="switchConfirmPassType = !switchConfirmPassType"
|
@click-right-icon="switchConfirmPassType = !switchConfirmPassType"
|
||||||
>
|
>
|
||||||
@ -86,7 +86,7 @@
|
|||||||
>
|
>
|
||||||
<template #input>
|
<template #input>
|
||||||
<van-checkbox v-model="formData.policy" icon-size="14px" shape="square">
|
<van-checkbox v-model="formData.policy" icon-size="14px" shape="square">
|
||||||
我同意 xxx 隐私政策
|
{{ $t('routes.basic.policy') }}
|
||||||
</van-checkbox>
|
</van-checkbox>
|
||||||
</template>
|
</template>
|
||||||
</van-field>
|
</van-field>
|
||||||
@ -99,7 +99,7 @@
|
|||||||
native-type="submit"
|
native-type="submit"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
>
|
>
|
||||||
注 册
|
{{ $t('routes.basic.register') }}
|
||||||
</van-button>
|
</van-button>
|
||||||
|
|
||||||
<van-button
|
<van-button
|
||||||
@ -109,12 +109,13 @@
|
|||||||
block
|
block
|
||||||
@click="handleBackLogin"
|
@click="handleBackLogin"
|
||||||
>
|
>
|
||||||
返 回
|
{{ $t('common.back') }}
|
||||||
</van-button>
|
</van-button>
|
||||||
</van-form>
|
</van-form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { computed, reactive, ref, unref } from 'vue'
|
||||||
import type { FormInstance } from 'vant'
|
import type { FormInstance } from 'vant'
|
||||||
|
|
||||||
import { LoginStateEnum, useFormRules, useLoginState } from './useLogin'
|
import { LoginStateEnum, useFormRules, useLoginState } from './useLogin'
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import type { FieldRule } from 'vant'
|
import type { FieldRule } from 'vant'
|
||||||
|
import { computed, ref, unref } from 'vue'
|
||||||
|
|
||||||
export enum LoginStateEnum {
|
export enum LoginStateEnum {
|
||||||
LOGIN,
|
LOGIN,
|
||||||
|
@ -1,105 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="my-card m-40px rounded-2xl p-30px shadow-xl">
|
|
||||||
<div ref="chartRef" :style="{ height: '350px' }" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import type { EChartsOption } from 'echarts'
|
|
||||||
import { useECharts } from '@/hooks/web/useECharts'
|
|
||||||
|
|
||||||
const chartRef = ref<HTMLDivElement | null>(null)
|
|
||||||
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>)
|
|
||||||
|
|
||||||
const chartOptions: EChartsOption = {
|
|
||||||
tooltip: {
|
|
||||||
trigger: 'axis',
|
|
||||||
axisPointer: {
|
|
||||||
// Use axis to trigger tooltip
|
|
||||||
type: 'shadow', // 'shadow' as default; can also be 'line' or 'shadow'
|
|
||||||
},
|
|
||||||
},
|
|
||||||
legend: {},
|
|
||||||
grid: {
|
|
||||||
left: '1%',
|
|
||||||
right: '7%',
|
|
||||||
bottom: '3%',
|
|
||||||
containLabel: true,
|
|
||||||
},
|
|
||||||
xAxis: {
|
|
||||||
type: 'value',
|
|
||||||
},
|
|
||||||
yAxis: {
|
|
||||||
type: 'category',
|
|
||||||
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
|
|
||||||
},
|
|
||||||
series: [
|
|
||||||
{
|
|
||||||
name: 'Direct',
|
|
||||||
type: 'bar',
|
|
||||||
stack: 'total',
|
|
||||||
label: {
|
|
||||||
show: true,
|
|
||||||
},
|
|
||||||
emphasis: {
|
|
||||||
focus: 'series',
|
|
||||||
},
|
|
||||||
data: [320, 302, 301, 334, 390, 330, 320],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Mail Ad',
|
|
||||||
type: 'bar',
|
|
||||||
stack: 'total',
|
|
||||||
label: {
|
|
||||||
show: true,
|
|
||||||
},
|
|
||||||
emphasis: {
|
|
||||||
focus: 'series',
|
|
||||||
},
|
|
||||||
data: [120, 132, 101, 134, 90, 230, 210],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Affiliate Ad',
|
|
||||||
type: 'bar',
|
|
||||||
stack: 'total',
|
|
||||||
label: {
|
|
||||||
show: true,
|
|
||||||
},
|
|
||||||
emphasis: {
|
|
||||||
focus: 'series',
|
|
||||||
},
|
|
||||||
data: [220, 182, 191, 234, 290, 330, 310],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Video Ad',
|
|
||||||
type: 'bar',
|
|
||||||
stack: 'total',
|
|
||||||
label: {
|
|
||||||
show: true,
|
|
||||||
},
|
|
||||||
emphasis: {
|
|
||||||
focus: 'series',
|
|
||||||
},
|
|
||||||
data: [150, 212, 201, 154, 190, 330, 410],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Search Engine',
|
|
||||||
type: 'bar',
|
|
||||||
stack: 'total',
|
|
||||||
label: {
|
|
||||||
show: true,
|
|
||||||
},
|
|
||||||
emphasis: {
|
|
||||||
focus: 'series',
|
|
||||||
},
|
|
||||||
data: [820, 832, 901, 934, 1290, 1330, 1320],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
setOptions(chartOptions)
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped></style>
|
|
@ -1,15 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<lineChart />
|
<lineChart />
|
||||||
<barChart />
|
|
||||||
<pieChart />
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import lineChart from './lineChart.vue'
|
import lineChart from './lineChart.vue'
|
||||||
import barChart from './barChart.vue'
|
|
||||||
import pieChart from './pieChart.vue'
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="my-card m-40px rounded-2xl p-30px shadow-xl">
|
<div class="my-card m-10px mt-20px rounded-2xl p-10px shadow-xl">
|
||||||
<div ref="chartRef" :style="{ height: '350px' }" />
|
<div ref="chartRef" :style="{ height: '350px' }" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import type { Ref } from 'vue'
|
||||||
|
import { onMounted, ref } from 'vue'
|
||||||
import type { EChartsOption } from 'echarts'
|
import type { EChartsOption } from 'echarts'
|
||||||
import { useECharts } from '@/hooks/web/useECharts'
|
import { useECharts } from '@/hooks/web/useECharts'
|
||||||
|
|
||||||
|
@ -1,64 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="my-card m-40px rounded-2xl p-30px shadow-xl">
|
|
||||||
<div ref="chartRef" :style="{ height: '350px' }" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import type { EChartsOption } from 'echarts'
|
|
||||||
import { useECharts } from '@/hooks/web/useECharts'
|
|
||||||
|
|
||||||
const chartRef = ref<HTMLDivElement | null>(null)
|
|
||||||
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>)
|
|
||||||
|
|
||||||
const chartOptions: EChartsOption = {
|
|
||||||
tooltip: {
|
|
||||||
trigger: 'item',
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
top: '5%',
|
|
||||||
left: 'center',
|
|
||||||
},
|
|
||||||
series: [
|
|
||||||
{
|
|
||||||
name: 'Access From',
|
|
||||||
type: 'pie',
|
|
||||||
radius: ['40%', '70%'],
|
|
||||||
center: ['50%', '60%'],
|
|
||||||
avoidLabelOverlap: false,
|
|
||||||
itemStyle: {
|
|
||||||
borderRadius: 10,
|
|
||||||
borderColor: '#fff',
|
|
||||||
borderWidth: 2,
|
|
||||||
},
|
|
||||||
label: {
|
|
||||||
show: false,
|
|
||||||
position: 'center',
|
|
||||||
},
|
|
||||||
emphasis: {
|
|
||||||
label: {
|
|
||||||
show: true,
|
|
||||||
fontSize: '40',
|
|
||||||
fontWeight: 'bold',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
labelLine: {
|
|
||||||
show: false,
|
|
||||||
},
|
|
||||||
data: [
|
|
||||||
{ value: 1048, name: 'Search Engine' },
|
|
||||||
{ value: 735, name: 'Direct' },
|
|
||||||
{ value: 580, name: 'Email' },
|
|
||||||
{ value: 484, name: 'Union Ads' },
|
|
||||||
{ value: 300, name: 'Video Ads' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
setOptions(chartOptions)
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped></style>
|
|
@ -3,7 +3,7 @@
|
|||||||
<NavBar />
|
<NavBar />
|
||||||
<van-field
|
<van-field
|
||||||
v-model="username"
|
v-model="username"
|
||||||
label="用户名"
|
:label="$t('routes.my.username')"
|
||||||
readonly
|
readonly
|
||||||
label-class="font-bold"
|
label-class="font-bold"
|
||||||
input-align="right"
|
input-align="right"
|
||||||
@ -12,7 +12,7 @@
|
|||||||
/>
|
/>
|
||||||
<van-field
|
<van-field
|
||||||
v-model="afterPhone"
|
v-model="afterPhone"
|
||||||
label="手机号"
|
:label="$t('routes.my.phone')"
|
||||||
readonly
|
readonly
|
||||||
is-link
|
is-link
|
||||||
label-class="font-bold"
|
label-class="font-bold"
|
||||||
@ -21,7 +21,7 @@
|
|||||||
:border="false"
|
:border="false"
|
||||||
/>
|
/>
|
||||||
<van-field
|
<van-field
|
||||||
label="修改登录密码"
|
:label="$t('routes.my.changePassword')"
|
||||||
readonly
|
readonly
|
||||||
label-class="font-bold"
|
label-class="font-bold"
|
||||||
input-align="right"
|
input-align="right"
|
||||||
@ -34,6 +34,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue'
|
||||||
import NavBar from './components/NavBar.vue'
|
import NavBar from './components/NavBar.vue'
|
||||||
import { useUserStore } from '@/store/modules/user'
|
import { useUserStore } from '@/store/modules/user'
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<NavBar />
|
<NavBar />
|
||||||
<p>修改登录密码页面</p>
|
<p>{{ $t('routes.my.changePassword') }} 页面</p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<NavBar>
|
<NavBar>
|
||||||
<template #right>
|
<template #right>
|
||||||
<span @click="handleNickname">保存</span>
|
<span @click="handleNickname">{{ $t('common.saveText') }}</span>
|
||||||
</template>
|
</template>
|
||||||
</NavBar>
|
</NavBar>
|
||||||
<van-form ref="formRef">
|
<van-form ref="formRef">
|
||||||
@ -28,6 +28,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { onMounted, reactive, ref } from 'vue'
|
||||||
import type { FormInstance } from 'vant'
|
import type { FormInstance } from 'vant'
|
||||||
import { showToast } from 'vant'
|
import { showToast } from 'vant'
|
||||||
import NavBar from './components/NavBar.vue'
|
import NavBar from './components/NavBar.vue'
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<NavBar>
|
<NavBar>
|
||||||
<template #right>
|
<template #right>
|
||||||
<span @click="handleNickname">保存</span>
|
<span @click="handleNickname">{{ $t('common.saveText') }}</span>
|
||||||
</template>
|
</template>
|
||||||
</NavBar>
|
</NavBar>
|
||||||
<van-form ref="formRef">
|
<van-form ref="formRef">
|
||||||
@ -13,7 +13,7 @@
|
|||||||
clearable
|
clearable
|
||||||
rows="4"
|
rows="4"
|
||||||
autosize
|
autosize
|
||||||
label="签名"
|
:label="$t('routes.my.sign')"
|
||||||
type="textarea"
|
type="textarea"
|
||||||
maxlength="70"
|
maxlength="70"
|
||||||
placeholder="随知修行乃当务之急,然怠惰度日至今"
|
placeholder="随知修行乃当务之急,然怠惰度日至今"
|
||||||
@ -24,6 +24,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { onMounted, reactive, ref } from 'vue'
|
||||||
import type { FormInstance } from 'vant'
|
import type { FormInstance } from 'vant'
|
||||||
import { showToast } from 'vant'
|
import { showToast } from 'vant'
|
||||||
import NavBar from './components/NavBar.vue'
|
import NavBar from './components/NavBar.vue'
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<NavBar />
|
<NavBar />
|
||||||
<van-divider>基本信息</van-divider>
|
<van-divider>{{ $t('routes.my.basicInfo') }}</van-divider>
|
||||||
<van-field
|
<van-field
|
||||||
label="头像"
|
:label="$t('routes.my.avatar')"
|
||||||
label-class="font-bold"
|
label-class="font-bold"
|
||||||
input-align="right"
|
input-align="right"
|
||||||
:center="true"
|
:center="true"
|
||||||
@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
<van-field
|
<van-field
|
||||||
v-model="state.nickname"
|
v-model="state.nickname"
|
||||||
label="昵称"
|
:label="$t('routes.my.nickname')"
|
||||||
readonly
|
readonly
|
||||||
label-class="font-bold"
|
label-class="font-bold"
|
||||||
input-align="right"
|
input-align="right"
|
||||||
@ -36,7 +36,7 @@
|
|||||||
|
|
||||||
<van-field
|
<van-field
|
||||||
v-model="state.genderText"
|
v-model="state.genderText"
|
||||||
label="性别"
|
:label="$t('routes.my.gender')"
|
||||||
readonly
|
readonly
|
||||||
label-class="font-bold"
|
label-class="font-bold"
|
||||||
input-align="right"
|
input-align="right"
|
||||||
@ -48,7 +48,7 @@
|
|||||||
|
|
||||||
<van-field
|
<van-field
|
||||||
v-model="state.sign"
|
v-model="state.sign"
|
||||||
label="签名"
|
:label="$t('routes.my.sign')"
|
||||||
readonly
|
readonly
|
||||||
label-class="font-bold"
|
label-class="font-bold"
|
||||||
input-align="right"
|
input-align="right"
|
||||||
@ -59,7 +59,7 @@
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<van-field
|
<van-field
|
||||||
label="主页封面"
|
:label="$t('routes.my.cover')"
|
||||||
label-class="font-bold"
|
label-class="font-bold"
|
||||||
input-align="right"
|
input-align="right"
|
||||||
:center="true"
|
:center="true"
|
||||||
@ -80,7 +80,7 @@
|
|||||||
|
|
||||||
<van-field
|
<van-field
|
||||||
v-model="state.industryText"
|
v-model="state.industryText"
|
||||||
label="行业"
|
:label="$t('routes.my.trade')"
|
||||||
readonly
|
readonly
|
||||||
label-class="font-bold"
|
label-class="font-bold"
|
||||||
input-align="right"
|
input-align="right"
|
||||||
@ -97,7 +97,11 @@
|
|||||||
:columns="genderColumns"
|
:columns="genderColumns"
|
||||||
@confirm="handleGender"
|
@confirm="handleGender"
|
||||||
@cancel="showGenderPicker = false"
|
@cancel="showGenderPicker = false"
|
||||||
/>
|
>
|
||||||
|
<template #option="item">
|
||||||
|
{{ $t(item.text) }}
|
||||||
|
</template>
|
||||||
|
</van-picker>
|
||||||
</van-popup>
|
</van-popup>
|
||||||
|
|
||||||
<van-popup v-model:show="showIndustryPicker" position="bottom" round>
|
<van-popup v-model:show="showIndustryPicker" position="bottom" round>
|
||||||
@ -106,19 +110,27 @@
|
|||||||
:columns="industryColumns"
|
:columns="industryColumns"
|
||||||
@confirm="handleIndustry"
|
@confirm="handleIndustry"
|
||||||
@cancel="showIndustryPicker = false"
|
@cancel="showIndustryPicker = false"
|
||||||
/>
|
>
|
||||||
|
<template #option="item">
|
||||||
|
{{ $t(item.text) }}
|
||||||
|
</template>
|
||||||
|
</van-picker>
|
||||||
</van-popup>
|
</van-popup>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { onMounted, reactive, ref } from 'vue'
|
||||||
import { showToast } from 'vant'
|
import { showToast } from 'vant'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
import NavBar from './components/NavBar.vue'
|
import NavBar from './components/NavBar.vue'
|
||||||
import UploaderImage from './components/UploaderImage.vue'
|
import UploaderImage from './components/UploaderImage.vue'
|
||||||
import type { FormColumns } from './pickColumns'
|
import type { FormColumns } from './pickColumns'
|
||||||
import { genderColumns, industryColumns } from './pickColumns'
|
import { genderColumns, industryColumns } from './pickColumns'
|
||||||
import { useUserStore } from '@/store/modules/user'
|
import { useUserStore } from '@/store/modules/user'
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
const { avatar, gender, industry, cover } = userStore.getUserInfo
|
const { avatar, gender, industry, cover } = userStore.getUserInfo
|
||||||
|
|
||||||
@ -135,21 +147,21 @@ const state = reactive({
|
|||||||
})
|
})
|
||||||
|
|
||||||
function handleGender({ selectedOptions }) {
|
function handleGender({ selectedOptions }) {
|
||||||
state.genderText = selectedOptions[0].text
|
state.genderText = t(selectedOptions[0].text)
|
||||||
showToast(JSON.stringify(selectedOptions))
|
showToast(JSON.stringify(selectedOptions))
|
||||||
// do something
|
// do something
|
||||||
showGenderPicker.value = false
|
showGenderPicker.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleIndustry({ selectedOptions }) {
|
function handleIndustry({ selectedOptions }) {
|
||||||
state.industryText = selectedOptions[0].text
|
state.industryText = t(selectedOptions[0].text)
|
||||||
showToast(JSON.stringify(selectedOptions))
|
showToast(JSON.stringify(selectedOptions))
|
||||||
// do something
|
// do something
|
||||||
showIndustryPicker.value = false
|
showIndustryPicker.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFromText(columns: FormColumns[], value = 0) {
|
function getFromText(columns: FormColumns[], value = 0) {
|
||||||
return columns.find(item => item.value === value)?.text
|
return t(columns.find(item => item.value === value)?.text as string)
|
||||||
}
|
}
|
||||||
|
|
||||||
function initState() {
|
function initState() {
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<NavBar />
|
<NavBar />
|
||||||
<van-divider>主题模式</van-divider>
|
<van-divider>{{ $t('layout.setting.darkMode') }}</van-divider>
|
||||||
<van-cell-group inset>
|
<van-cell-group inset>
|
||||||
<van-cell center title="暗黑模式">
|
<van-cell center :title="$t('layout.setting.diabloMode')">
|
||||||
<template #right-icon>
|
<template #right-icon>
|
||||||
<van-switch v-model="getDarkMode" size="22" />
|
<van-switch v-model="getDarkMode" size="22" />
|
||||||
</template>
|
</template>
|
||||||
</van-cell>
|
</van-cell>
|
||||||
</van-cell-group>
|
</van-cell-group>
|
||||||
|
|
||||||
<van-divider>系统主题色</van-divider>
|
<van-divider>{{ $t('layout.setting.systemThemeColors') }}</van-divider>
|
||||||
<div flex="~" justify="center">
|
<div flex="~" justify="center">
|
||||||
<div grid="~ cols-8 gap-2">
|
<div grid="~ cols-8 gap-2">
|
||||||
<span
|
<span
|
||||||
@ -33,14 +33,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<van-divider>页面切换动画</van-divider>
|
<van-divider>{{ $t('layout.setting.pageAnimationSwitch') }}</van-divider>
|
||||||
<van-cell-group inset>
|
<van-cell-group inset>
|
||||||
<van-cell center title="开启动画">
|
<van-cell center :title="$t('layout.setting.switchAnimation')">
|
||||||
<template #right-icon>
|
<template #right-icon>
|
||||||
<van-switch v-model="designStore.isPageAnimate" size="22" />
|
<van-switch v-model="designStore.isPageAnimate" size="22" />
|
||||||
</template>
|
</template>
|
||||||
</van-cell>
|
</van-cell>
|
||||||
<van-cell center title="动画类型">
|
<van-cell center :title="$t('layout.setting.animationType')">
|
||||||
<van-field
|
<van-field
|
||||||
v-model="animateState.text"
|
v-model="animateState.text"
|
||||||
readonly
|
readonly
|
||||||
@ -68,6 +68,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { computed, reactive } from 'vue'
|
||||||
import { useDark, useToggle } from '@vueuse/core'
|
import { useDark, useToggle } from '@vueuse/core'
|
||||||
import NavBar from './components/NavBar.vue'
|
import NavBar from './components/NavBar.vue'
|
||||||
import { useDesignSettingStore } from '@/store/modules/designSetting'
|
import { useDesignSettingStore } from '@/store/modules/designSetting'
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<van-nav-bar @click-left="router.back">
|
<van-nav-bar @click-left="router.back">
|
||||||
<template #title>
|
<template #title>
|
||||||
{{ getTitle }}
|
<TitleI18n :title="getTitle" />
|
||||||
</template>
|
</template>
|
||||||
<template #left>
|
<template #left>
|
||||||
<i class="i-ic:sharp-arrow-back-ios" text-xl />
|
<i class="i-ic:sharp-arrow-back-ios" text-xl />
|
||||||
@ -13,6 +13,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import { TitleI18n } from '@/components/title-i18n'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const currentRoute = useRoute()
|
const currentRoute = useRoute()
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<div :style="getUserCoverBg" class="my-bg h-70" />
|
<div :style="getUserCoverBg" class="my-bg h-70" />
|
||||||
<div
|
<div
|
||||||
class="my-card relative mx-6 flex flex-col items-center rounded-2xl pb-2 shadow-xl -top-18"
|
class="my-card relative mx-6 flex flex-col items-center rounded-2xl pb-3 shadow-xl -top-18"
|
||||||
>
|
>
|
||||||
<van-image
|
<van-image
|
||||||
class="h-22 w-22 border-2 border-solid !absolute -top-10"
|
class="h-22 w-22 border-2 border-solid !absolute -top-10"
|
||||||
@ -20,31 +20,31 @@
|
|||||||
</div>
|
</div>
|
||||||
<van-divider class="w-full" />
|
<van-divider class="w-full" />
|
||||||
|
|
||||||
<van-cell :border="false" title="个人信息" is-link to="/editUserInfo">
|
<van-cell :border="false" :title="$t('routes.my.settings')" is-link to="/editUserInfo">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<i class="i-mingcute:idcard-fill mr-2 text-xl" />
|
<i class="i-mingcute:idcard-fill mr-2 text-xl" />
|
||||||
</template>
|
</template>
|
||||||
</van-cell>
|
</van-cell>
|
||||||
|
|
||||||
<van-cell :border="false" title="账号与安全" is-link to="/accountSetting">
|
<van-cell :border="false" :title="$t('routes.my.accountSetting')" is-link to="/accountSetting">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<i class="i-material-symbols:account-box mr-2 text-xl" />
|
<i class="i-material-symbols:account-box mr-2 text-xl" />
|
||||||
</template>
|
</template>
|
||||||
</van-cell>
|
</van-cell>
|
||||||
|
|
||||||
<van-cell :border="false" title="主题设置" is-link to="/themeSetting">
|
<van-cell :border="false" :title="$t('layout.setting.sysTheme')" is-link to="/themeSetting">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<i class="i-material-symbols:palette mr-2 text-xl" />
|
<i class="i-material-symbols:palette mr-2 text-xl" />
|
||||||
</template>
|
</template>
|
||||||
</van-cell>
|
</van-cell>
|
||||||
|
|
||||||
<van-cell :border="false" title="隐私政策" is-link>
|
<van-cell :border="false" :title="$t('routes.my.privacy')" is-link>
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<i class="i-material-symbols:list-alt-rounded mr-2 text-xl" />
|
<i class="i-material-symbols:list-alt-rounded mr-2 text-xl" />
|
||||||
</template>
|
</template>
|
||||||
</van-cell>
|
</van-cell>
|
||||||
|
|
||||||
<van-cell :border="false" title="退出登录" is-link @click="showLogoutAction = true">
|
<van-cell :border="false" :title="$t('routes.my.logonout')" is-link @click="showLogoutAction = true">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<i class="i-solar:logout-3-bold mr-2 text-xl" />
|
<i class="i-solar:logout-3-bold mr-2 text-xl" />
|
||||||
</template>
|
</template>
|
||||||
@ -54,8 +54,8 @@
|
|||||||
v-model:show="showLogoutAction"
|
v-model:show="showLogoutAction"
|
||||||
teleport="body"
|
teleport="body"
|
||||||
:actions="logoutActions"
|
:actions="logoutActions"
|
||||||
cancel-text="取消"
|
:cancel-text="$t('common.cancelText')"
|
||||||
description="确认退出登录吗"
|
:description="$t('routes.my.confirmLogout')"
|
||||||
close-on-click-action
|
close-on-click-action
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -63,21 +63,25 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { computed, ref } from 'vue'
|
||||||
import { showToast } from 'vant'
|
import { showToast } from 'vant'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
import { useUserStore } from '@/store/modules/user'
|
import { useUserStore } from '@/store/modules/user'
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
|
|
||||||
const showLogoutAction = ref(false)
|
const showLogoutAction = ref(false)
|
||||||
|
|
||||||
const { nickname, avatar, cover, sign } = userStore.getUserInfo
|
const { nickname, avatar, cover, sign } = userStore.getUserInfo
|
||||||
|
|
||||||
const logoutActions = [
|
const logoutActions = [
|
||||||
{
|
{
|
||||||
name: '退出登录',
|
name: t('routes.my.logout'),
|
||||||
color: '#ee0a24',
|
color: '#ee0a24',
|
||||||
callback: () => {
|
callback: () => {
|
||||||
userStore.Logout()
|
userStore.Logout()
|
||||||
showToast('退出成功')
|
showToast(t('routes.my.logoutSuccess'))
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
@ -4,27 +4,20 @@ export interface FormColumns {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const genderColumns: FormColumns[] = [
|
export const genderColumns: FormColumns[] = [
|
||||||
{ text: '男', value: 0 },
|
{ text: 'common.Gender.male', value: 0 },
|
||||||
{ text: '女', value: 1 },
|
{ text: 'common.Gender.female', value: 1 },
|
||||||
]
|
]
|
||||||
|
|
||||||
export const industryColumns: FormColumns[] = [
|
export const industryColumns: FormColumns[] = [
|
||||||
{ text: '不展示', value: 0 },
|
{ text: 'common.Industry.student', value: 0 },
|
||||||
{ text: '学生', value: 1 },
|
{ text: 'common.Industry.worker', value: 1 },
|
||||||
{ text: '自由职业', value: 2 },
|
{ text: 'common.Industry.education', value: 2 },
|
||||||
{ text: 'IT/互联网/通信', value: 3 },
|
{ text: 'common.Industry.finance', value: 3 },
|
||||||
{ text: '金融', value: 4 },
|
{ text: 'common.Industry.health', value: 4 },
|
||||||
{ text: '健康/医疗', value: 5 },
|
{ text: 'common.Industry.life', value: 5 },
|
||||||
{ text: '工业/制造业', value: 6 },
|
{ text: 'common.Industry.tech', value: 6 },
|
||||||
{ text: '零售', value: 7 },
|
{ text: 'common.Industry.IT', value: 7 },
|
||||||
{ text: '贸易', value: 8 },
|
{ text: 'common.Industry.manager', value: 8 },
|
||||||
{ text: '教育/科研', value: 9 },
|
{ text: 'common.Industry.salesman', value: 9 },
|
||||||
{ text: '培训', value: 10 },
|
{ text: 'common.Industry.other', value: 10 },
|
||||||
{ text: '房地产/建筑', value: 11 },
|
|
||||||
{ text: '文化/艺术', value: 12 },
|
|
||||||
{ text: '影视/娱乐', value: 13 },
|
|
||||||
{ text: '法律/会计/咨询', value: 14 },
|
|
||||||
{ text: '媒体/广告/公关', value: 15 },
|
|
||||||
{ text: '体育/健身', value: 16 },
|
|
||||||
{ text: '企事业单位', value: 17 },
|
|
||||||
]
|
]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user