mirror of
https://github.com/xiangshu233/vue3-vant4-mobile.git
synced 2025-04-05 19:42:05 +08:00
commit
bff68425c4
@ -53,6 +53,7 @@
|
||||
"qs": "^6.11.2",
|
||||
"vant": "^4.8.1",
|
||||
"vue": "^3.3.13",
|
||||
"vue-i18n": "^9.10.1",
|
||||
"vue-router": "4.2.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
41
pnpm-lock.yaml
generated
41
pnpm-lock.yaml
generated
@ -47,6 +47,9 @@ dependencies:
|
||||
vue:
|
||||
specifier: ^3.3.13
|
||||
version: 3.3.13(typescript@5.3.3)
|
||||
vue-i18n:
|
||||
specifier: ^9.10.1
|
||||
version: 9.10.2(vue@3.3.13)
|
||||
vue-router:
|
||||
specifier: 4.2.5
|
||||
version: 4.2.5(vue@3.3.13)
|
||||
@ -1316,6 +1319,27 @@ packages:
|
||||
- supports-color
|
||||
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:
|
||||
resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
@ -1449,6 +1473,7 @@ packages:
|
||||
resolution: {integrity: sha512-u3XkZVvxcvlAOlQJ3UsD1rFvLWqu4Ef/Ggl40WAVCuogf4S1nJPHh5RTgqYFpCOvuGJ7H5yGHabjFKEZGExk5Q==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
@ -1457,6 +1482,7 @@ packages:
|
||||
resolution: {integrity: sha512-0XSYN/rfWShW+i+qjZ0phc6vZ7UWI8XWNz4E/l+6edFt+FxoEghrJHjX1EY/kcUGCnZzYYRCl31SNdfOi450Aw==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
@ -1465,6 +1491,7 @@ packages:
|
||||
resolution: {integrity: sha512-LmYIO65oZVfFt9t6cpYkbC4d5lKHLYv5B4CSHRpnANq0VZUQXGcCPXHzbCXCz4RQnx7jvlYB1ISVNCE/omz5cw==}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
@ -1473,6 +1500,7 @@ packages:
|
||||
resolution: {integrity: sha512-kr8rEPQ6ns/Lmr/hiw8sEVj9aa07gh1/tQF2Y5HrNCCEPiCBGnBUt9tVusrcBBiJfIt1yNaXN6r1CCmpbFEDpg==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
@ -1481,6 +1509,7 @@ packages:
|
||||
resolution: {integrity: sha512-t4QSR7gN+OEZLG0MiCgPqMWZGwmeHhsM4AkegJ0Kiy6TnJ9vZ8dEIwHw1LcZKhbHxTY32hp9eVCMdR3/I8MGRw==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
@ -7268,6 +7297,18 @@ packages:
|
||||
- supports-color
|
||||
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):
|
||||
resolution: {integrity: sha512-DIUpKcyg4+PTQKfFPX88UWhlagBEBEfJ5A8XDXRJLUnZOvcpMF8o/dnL90vpVkGaPbjvXazV/rC1qBKrZlFugw==}
|
||||
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 -->
|
||||
<template>
|
||||
<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">
|
||||
<template #default="{ Component, route }">
|
||||
<!--
|
||||
@ -27,7 +31,7 @@
|
||||
<template #icon>
|
||||
<i :class="menu.meta?.icon" />
|
||||
</template>
|
||||
{{ menu.meta?.title }}
|
||||
<TitleI18n :title="menu.meta?.title" />
|
||||
</van-tabbar-item>
|
||||
</van-tabbar>
|
||||
</div>
|
||||
@ -39,6 +43,7 @@ import { computed } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import type { RouteRecordRaw } from 'vue-router'
|
||||
import { useRouteStore } from '@/store/modules/route'
|
||||
import { TitleI18n } from '@/components/title-i18n'
|
||||
|
||||
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 router, { setupRouter } from './router'
|
||||
import { setupStore } from '@/store'
|
||||
import { setupI18n } from '@/locales'
|
||||
|
||||
async function bootstrap() {
|
||||
const app = createApp(App)
|
||||
@ -21,6 +22,7 @@ async function bootstrap() {
|
||||
setupStore(app)
|
||||
// 挂载路由
|
||||
setupRouter(app)
|
||||
await setupI18n(app)
|
||||
await router.isReady()
|
||||
// 路由准备就绪后挂载APP实例
|
||||
app.mount('#app', true)
|
||||
|
@ -1,4 +1,5 @@
|
||||
import type { RouteRecordRaw } from 'vue-router'
|
||||
import { t } from '@/hooks/useI18n'
|
||||
|
||||
const Layout = () => import('@/layout/index.vue')
|
||||
|
||||
@ -9,7 +10,7 @@ const routeModuleList: Array<RouteRecordRaw> = [
|
||||
redirect: '/dashboard/index',
|
||||
component: Layout,
|
||||
meta: {
|
||||
title: '主控台',
|
||||
title: t('layout.footer.home'),
|
||||
icon: 'i-simple-icons:atlassian',
|
||||
},
|
||||
children: [
|
||||
@ -29,7 +30,7 @@ const routeModuleList: Array<RouteRecordRaw> = [
|
||||
redirect: '/message/index',
|
||||
component: Layout,
|
||||
meta: {
|
||||
title: '图表',
|
||||
title: t('layout.footer.chart'),
|
||||
icon: 'i-simple-icons:soundcharts',
|
||||
},
|
||||
children: [
|
||||
@ -49,7 +50,7 @@ const routeModuleList: Array<RouteRecordRaw> = [
|
||||
redirect: '/example/index',
|
||||
component: Layout,
|
||||
meta: {
|
||||
title: '示例',
|
||||
title: t('layout.footer.example'),
|
||||
icon: 'i-material-symbols:award-star',
|
||||
},
|
||||
children: [
|
||||
@ -69,7 +70,7 @@ const routeModuleList: Array<RouteRecordRaw> = [
|
||||
redirect: '/my/index',
|
||||
component: Layout,
|
||||
meta: {
|
||||
title: '我的',
|
||||
title: t('layout.footer.my'),
|
||||
icon: 'i-simple-icons:docsify',
|
||||
},
|
||||
children: [
|
||||
@ -90,7 +91,7 @@ const routeModuleList: Array<RouteRecordRaw> = [
|
||||
path: '/editUserInfo',
|
||||
name: 'EditUserInfo',
|
||||
meta: {
|
||||
title: '编辑个人信息',
|
||||
title: t('routes.my.editUserInfo'),
|
||||
innerPage: true,
|
||||
},
|
||||
component: () => import('@/views/my/EditUserInfo.vue'),
|
||||
@ -99,9 +100,9 @@ const routeModuleList: Array<RouteRecordRaw> = [
|
||||
path: '/editNickname',
|
||||
name: 'EditNickname',
|
||||
meta: {
|
||||
title: '修改昵称(该页面已缓存)',
|
||||
title: t('routes.my.editNickname'),
|
||||
innerPage: true,
|
||||
keepAlive: true,
|
||||
keepAlive: false,
|
||||
},
|
||||
component: () => import('@/views/my/EditNickname.vue'),
|
||||
},
|
||||
@ -109,7 +110,7 @@ const routeModuleList: Array<RouteRecordRaw> = [
|
||||
path: '/editSign',
|
||||
name: 'EditSign',
|
||||
meta: {
|
||||
title: '修改签名',
|
||||
title: t('routes.my.editSignature'),
|
||||
innerPage: true,
|
||||
},
|
||||
component: () => import('@/views/my/EditSign.vue'),
|
||||
@ -118,7 +119,7 @@ const routeModuleList: Array<RouteRecordRaw> = [
|
||||
path: '/accountSetting',
|
||||
name: 'AccountSetting',
|
||||
meta: {
|
||||
title: '账号与安全',
|
||||
title: t('routes.my.accountSetting'),
|
||||
innerPage: true,
|
||||
},
|
||||
component: () => import('@/views/my/AccountSetting.vue'),
|
||||
@ -127,7 +128,7 @@ const routeModuleList: Array<RouteRecordRaw> = [
|
||||
path: '/changePassword',
|
||||
name: 'ChangePassword',
|
||||
meta: {
|
||||
title: '修改登录密码',
|
||||
title: t('routes.my.changePassword'),
|
||||
innerPage: true,
|
||||
},
|
||||
component: () => import('@/views/my/ChangePassword.vue'),
|
||||
@ -136,7 +137,7 @@ const routeModuleList: Array<RouteRecordRaw> = [
|
||||
path: '/themeSetting',
|
||||
name: 'ThemeSetting',
|
||||
meta: {
|
||||
title: '主题设置',
|
||||
title: t('layout.setting.sysTheme'),
|
||||
innerPage: true,
|
||||
},
|
||||
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 CURRENT_USER = 'CURRENT-USER' // 当前用户信息
|
||||
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="wel-box w-full flex flex-col items-center justify-between">
|
||||
<Logo class="!h-30 !w-30" />
|
||||
<div class="text-darkBlue dark:text-garyWhite mb-4 mt-12 text-center text-2xl font-black">
|
||||
{{ title }}
|
||||
</div>
|
||||
<div class="text-darkBlue dark:text-garyWhite mb-4 mt-12 text-center text-2xl font-black" />
|
||||
<div class="mb-6 mt-4 w-full">
|
||||
<van-swipe class="h-30" :autoplay="3000" :indicator-color="designStore.appTheme">
|
||||
<van-swipe-item
|
||||
@ -26,8 +24,8 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useDesignSettingStore } from '@/store/modules/designSetting'
|
||||
import { useGlobSetting } from '@/hooks/setting'
|
||||
import Logo from '@/components/Logo.vue'
|
||||
|
||||
defineOptions({
|
||||
@ -35,9 +33,6 @@ defineOptions({
|
||||
})
|
||||
|
||||
const designStore = useDesignSettingStore()
|
||||
const globSetting = useGlobSetting()
|
||||
|
||||
const { title } = globSetting
|
||||
|
||||
const getSwipeText = computed(() => {
|
||||
return [
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="my-4">
|
||||
<van-cell-group inset>
|
||||
<van-cell center title="🌓 暗黑模式">
|
||||
<van-cell center :title="`🌓 ${$t('layout.setting.diabloMode')}`">
|
||||
<template #right-icon>
|
||||
<i inline-block align-middle i="dark:carbon-moon carbon-sun" />
|
||||
<span class="ml-2">{{ isDark ? 'Dark' : 'Light' }}</span>
|
||||
@ -9,15 +9,21 @@
|
||||
<van-switch v-model="checked" size="22" @click="toggle()" />
|
||||
</template>
|
||||
</van-cell>
|
||||
<template v-for="item in menuItems" :key="item.route">
|
||||
<van-cell :title="item.title" :to="item.route" is-link />
|
||||
</template>
|
||||
<van-cell center is-link :title="$t('layout.setting.changeLanguage')" @click="showLanguagePicker = true">
|
||||
<template #icon>
|
||||
<i class="i-material-symbols:language mr-1 text-xl" />
|
||||
</template>
|
||||
</van-cell>
|
||||
</van-cell-group>
|
||||
|
||||
<LocalePicker v-model:show="showLanguagePicker" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { useDark, useToggle } from '@vueuse/core'
|
||||
import { LocalePicker } from '@/components/locale-picker'
|
||||
import { useDesignSettingStore } from '@/store/modules/designSetting'
|
||||
|
||||
const designStore = useDesignSettingStore()
|
||||
@ -26,6 +32,7 @@ const isDark = useDark({
|
||||
valueDark: 'dark',
|
||||
valueLight: 'light',
|
||||
})
|
||||
const showLanguagePicker = ref(false)
|
||||
|
||||
const checked = ref(isDark.value)
|
||||
|
||||
@ -35,11 +42,6 @@ function toggle() {
|
||||
toggleDark()
|
||||
designStore.setDarkMode(isDark.value ? 'dark' : 'light')
|
||||
}
|
||||
|
||||
const menuItems = [
|
||||
{ title: '🐗 keep-alive', route: '/editNickname' },
|
||||
{ title: '🦘 404 页演示', route: '/404' },
|
||||
]
|
||||
</script>
|
||||
|
||||
<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>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const router = useRouter()
|
||||
function goHome() {
|
||||
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"
|
||||
class="enter-y mb-4 items-center !rounded-md"
|
||||
name="username"
|
||||
placeholder="用户名"
|
||||
:placeholder="$t('routes.my.username')"
|
||||
:rules="getFormRules.username"
|
||||
>
|
||||
<template #left-icon>
|
||||
@ -16,7 +16,7 @@
|
||||
v-model="formData.mobile"
|
||||
class="enter-y mb-4 items-center !rounded-md"
|
||||
name="password"
|
||||
placeholder="手机号码"
|
||||
:placeholder="$t('routes.my.phone')"
|
||||
:rules="getFormRules.mobile"
|
||||
>
|
||||
<template #left-icon>
|
||||
@ -29,7 +29,7 @@
|
||||
class="enter-y mb-10 items-center !rounded-md"
|
||||
center
|
||||
clearable
|
||||
placeholder="请输入短信验证码"
|
||||
:placeholder="$t('routes.basic.code')"
|
||||
:rules="getFormRules.sms"
|
||||
>
|
||||
<template #left-icon>
|
||||
@ -37,7 +37,7 @@
|
||||
</template>
|
||||
<template #button>
|
||||
<van-button size="small" type="primary">
|
||||
发送验证码
|
||||
{{ $t('routes.basic.sendCode') }}
|
||||
</van-button>
|
||||
</template>
|
||||
</van-field>
|
||||
@ -48,7 +48,7 @@
|
||||
native-type="submit"
|
||||
:loading="loading"
|
||||
>
|
||||
重 置
|
||||
{{ $t('common.resetText') }}
|
||||
</van-button>
|
||||
|
||||
<van-button
|
||||
@ -58,12 +58,13 @@
|
||||
block
|
||||
@click="handleBackLogin"
|
||||
>
|
||||
返 回
|
||||
{{ $t('common.back') }}
|
||||
</van-button>
|
||||
</van-form>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, reactive, ref, unref } from 'vue'
|
||||
import type { FormInstance } from 'vant'
|
||||
import { LoginStateEnum, useFormRules, useLoginState } from './useLogin'
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
v-model="formData.username"
|
||||
class="enter-y mb-4 items-center !rounded-md"
|
||||
name="username"
|
||||
placeholder="用户名"
|
||||
:placeholder="$t('routes.my.username')"
|
||||
:rules="getFormRules.username"
|
||||
>
|
||||
<template #left-icon>
|
||||
@ -16,7 +16,7 @@
|
||||
class="enter-y mb-4 items-center !rounded-md"
|
||||
:type="switchPassType ? 'password' : 'text'"
|
||||
name="password"
|
||||
placeholder="密码"
|
||||
:placeholder="$t('routes.my.password')"
|
||||
:rules="getFormRules.password"
|
||||
@click-right-icon="switchPassType = !switchPassType"
|
||||
>
|
||||
@ -32,9 +32,9 @@
|
||||
<div class="enter-y mb-10 w-full flex justify-between px-5px">
|
||||
<div class="flex items-center">
|
||||
<van-switch v-model="rememberMe" size="18px" class="mr-8px" />
|
||||
<span>记住我</span>
|
||||
<span>{{ $t('routes.basic.rememberMe') }}</span>
|
||||
</div>
|
||||
<a @click="setLoginState(LoginStateEnum.RESET_PASSWORD)">忘记密码?</a>
|
||||
<a @click="setLoginState(LoginStateEnum.RESET_PASSWORD)">{{ $t('routes.basic.forgetPassword') }}?</a>
|
||||
</div>
|
||||
|
||||
<van-button
|
||||
@ -44,7 +44,7 @@
|
||||
native-type="submit"
|
||||
:loading="loading"
|
||||
>
|
||||
登 录
|
||||
{{ $t('routes.basic.login') }}
|
||||
</van-button>
|
||||
<van-button
|
||||
class="enter-y !rounded-md"
|
||||
@ -53,12 +53,14 @@
|
||||
block
|
||||
@click="setLoginState(LoginStateEnum.REGISTER)"
|
||||
>
|
||||
注 册
|
||||
{{ $t('routes.basic.register') }}
|
||||
</van-button>
|
||||
</van-form>
|
||||
</template>
|
||||
|
||||
<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 type { FormInstance } from 'vant'
|
||||
import { LoginStateEnum, useFormRules, useLoginState } from './useLogin'
|
||||
|
@ -5,7 +5,7 @@
|
||||
v-model="formData.username"
|
||||
class="enter-y items-center !rounded-md"
|
||||
name="username"
|
||||
placeholder="用户名"
|
||||
:placeholder="$t('routes.my.username')"
|
||||
:rules="getFormRules.username"
|
||||
>
|
||||
<template #left-icon>
|
||||
@ -17,7 +17,7 @@
|
||||
v-model="formData.mobile"
|
||||
class="enter-y items-center !rounded-md"
|
||||
name="password"
|
||||
placeholder="手机号码"
|
||||
:placeholder="$t('routes.my.phone')"
|
||||
:rules="getFormRules.mobile"
|
||||
>
|
||||
<template #left-icon>
|
||||
@ -30,7 +30,7 @@
|
||||
class="enter-y items-center !rounded-md"
|
||||
center
|
||||
clearable
|
||||
placeholder="请输入短信验证码"
|
||||
:placeholder="$t('routes.basic.code')"
|
||||
:rules="getFormRules.sms"
|
||||
>
|
||||
<template #left-icon>
|
||||
@ -38,7 +38,7 @@
|
||||
</template>
|
||||
<template #button>
|
||||
<van-button size="small" type="primary">
|
||||
发送验证码
|
||||
{{ $t('routes.basic.sendCode') }}
|
||||
</van-button>
|
||||
</template>
|
||||
</van-field>
|
||||
@ -48,7 +48,7 @@
|
||||
class="enter-y items-center !rounded-md"
|
||||
:type="switchPassType ? 'password' : 'text'"
|
||||
name="password"
|
||||
placeholder="密码"
|
||||
:placeholder="$t('routes.my.password')"
|
||||
:rules="getFormRules.password"
|
||||
@click-right-icon="switchPassType = !switchPassType"
|
||||
>
|
||||
@ -66,7 +66,7 @@
|
||||
class="enter-y items-center !rounded-md"
|
||||
:type="switchConfirmPassType ? 'password' : 'text'"
|
||||
name="confirmPassword"
|
||||
placeholder="确认密码"
|
||||
:placeholder="$t('routes.my.confirmpassword')"
|
||||
:rules="getFormRules.confirmPassword"
|
||||
@click-right-icon="switchConfirmPassType = !switchConfirmPassType"
|
||||
>
|
||||
@ -86,7 +86,7 @@
|
||||
>
|
||||
<template #input>
|
||||
<van-checkbox v-model="formData.policy" icon-size="14px" shape="square">
|
||||
我同意 xxx 隐私政策
|
||||
{{ $t('routes.basic.policy') }}
|
||||
</van-checkbox>
|
||||
</template>
|
||||
</van-field>
|
||||
@ -99,7 +99,7 @@
|
||||
native-type="submit"
|
||||
:loading="loading"
|
||||
>
|
||||
注 册
|
||||
{{ $t('routes.basic.register') }}
|
||||
</van-button>
|
||||
|
||||
<van-button
|
||||
@ -109,12 +109,13 @@
|
||||
block
|
||||
@click="handleBackLogin"
|
||||
>
|
||||
返 回
|
||||
{{ $t('common.back') }}
|
||||
</van-button>
|
||||
</van-form>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, reactive, ref, unref } from 'vue'
|
||||
import type { FormInstance } from 'vant'
|
||||
|
||||
import { LoginStateEnum, useFormRules, useLoginState } from './useLogin'
|
||||
|
@ -1,4 +1,5 @@
|
||||
import type { FieldRule } from 'vant'
|
||||
import { computed, ref, unref } from 'vue'
|
||||
|
||||
export enum LoginStateEnum {
|
||||
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>
|
||||
<div>
|
||||
<lineChart />
|
||||
<barChart />
|
||||
<pieChart />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import lineChart from './lineChart.vue'
|
||||
import barChart from './barChart.vue'
|
||||
import pieChart from './pieChart.vue'
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
@ -1,10 +1,12 @@
|
||||
<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>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Ref } from 'vue'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import type { EChartsOption } from 'echarts'
|
||||
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 />
|
||||
<van-field
|
||||
v-model="username"
|
||||
label="用户名"
|
||||
:label="$t('routes.my.username')"
|
||||
readonly
|
||||
label-class="font-bold"
|
||||
input-align="right"
|
||||
@ -12,7 +12,7 @@
|
||||
/>
|
||||
<van-field
|
||||
v-model="afterPhone"
|
||||
label="手机号"
|
||||
:label="$t('routes.my.phone')"
|
||||
readonly
|
||||
is-link
|
||||
label-class="font-bold"
|
||||
@ -21,7 +21,7 @@
|
||||
:border="false"
|
||||
/>
|
||||
<van-field
|
||||
label="修改登录密码"
|
||||
:label="$t('routes.my.changePassword')"
|
||||
readonly
|
||||
label-class="font-bold"
|
||||
input-align="right"
|
||||
@ -34,6 +34,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import NavBar from './components/NavBar.vue'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<NavBar />
|
||||
<p>修改登录密码页面</p>
|
||||
<p>{{ $t('routes.my.changePassword') }} 页面</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
<div>
|
||||
<NavBar>
|
||||
<template #right>
|
||||
<span @click="handleNickname">保存</span>
|
||||
<span @click="handleNickname">{{ $t('common.saveText') }}</span>
|
||||
</template>
|
||||
</NavBar>
|
||||
<van-form ref="formRef">
|
||||
@ -28,6 +28,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, reactive, ref } from 'vue'
|
||||
import type { FormInstance } from 'vant'
|
||||
import { showToast } from 'vant'
|
||||
import NavBar from './components/NavBar.vue'
|
||||
|
@ -2,7 +2,7 @@
|
||||
<div>
|
||||
<NavBar>
|
||||
<template #right>
|
||||
<span @click="handleNickname">保存</span>
|
||||
<span @click="handleNickname">{{ $t('common.saveText') }}</span>
|
||||
</template>
|
||||
</NavBar>
|
||||
<van-form ref="formRef">
|
||||
@ -13,7 +13,7 @@
|
||||
clearable
|
||||
rows="4"
|
||||
autosize
|
||||
label="签名"
|
||||
:label="$t('routes.my.sign')"
|
||||
type="textarea"
|
||||
maxlength="70"
|
||||
placeholder="随知修行乃当务之急,然怠惰度日至今"
|
||||
@ -24,6 +24,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, reactive, ref } from 'vue'
|
||||
import type { FormInstance } from 'vant'
|
||||
import { showToast } from 'vant'
|
||||
import NavBar from './components/NavBar.vue'
|
||||
|
@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<div>
|
||||
<NavBar />
|
||||
<van-divider>基本信息</van-divider>
|
||||
<van-divider>{{ $t('routes.my.basicInfo') }}</van-divider>
|
||||
<van-field
|
||||
label="头像"
|
||||
:label="$t('routes.my.avatar')"
|
||||
label-class="font-bold"
|
||||
input-align="right"
|
||||
:center="true"
|
||||
@ -24,7 +24,7 @@
|
||||
|
||||
<van-field
|
||||
v-model="state.nickname"
|
||||
label="昵称"
|
||||
:label="$t('routes.my.nickname')"
|
||||
readonly
|
||||
label-class="font-bold"
|
||||
input-align="right"
|
||||
@ -36,7 +36,7 @@
|
||||
|
||||
<van-field
|
||||
v-model="state.genderText"
|
||||
label="性别"
|
||||
:label="$t('routes.my.gender')"
|
||||
readonly
|
||||
label-class="font-bold"
|
||||
input-align="right"
|
||||
@ -48,7 +48,7 @@
|
||||
|
||||
<van-field
|
||||
v-model="state.sign"
|
||||
label="签名"
|
||||
:label="$t('routes.my.sign')"
|
||||
readonly
|
||||
label-class="font-bold"
|
||||
input-align="right"
|
||||
@ -59,7 +59,7 @@
|
||||
/>
|
||||
|
||||
<van-field
|
||||
label="主页封面"
|
||||
:label="$t('routes.my.cover')"
|
||||
label-class="font-bold"
|
||||
input-align="right"
|
||||
:center="true"
|
||||
@ -80,7 +80,7 @@
|
||||
|
||||
<van-field
|
||||
v-model="state.industryText"
|
||||
label="行业"
|
||||
:label="$t('routes.my.trade')"
|
||||
readonly
|
||||
label-class="font-bold"
|
||||
input-align="right"
|
||||
@ -97,7 +97,11 @@
|
||||
:columns="genderColumns"
|
||||
@confirm="handleGender"
|
||||
@cancel="showGenderPicker = false"
|
||||
/>
|
||||
>
|
||||
<template #option="item">
|
||||
{{ $t(item.text) }}
|
||||
</template>
|
||||
</van-picker>
|
||||
</van-popup>
|
||||
|
||||
<van-popup v-model:show="showIndustryPicker" position="bottom" round>
|
||||
@ -106,19 +110,27 @@
|
||||
:columns="industryColumns"
|
||||
@confirm="handleIndustry"
|
||||
@cancel="showIndustryPicker = false"
|
||||
/>
|
||||
>
|
||||
<template #option="item">
|
||||
{{ $t(item.text) }}
|
||||
</template>
|
||||
</van-picker>
|
||||
</van-popup>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, reactive, ref } from 'vue'
|
||||
import { showToast } from 'vant'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import NavBar from './components/NavBar.vue'
|
||||
import UploaderImage from './components/UploaderImage.vue'
|
||||
import type { FormColumns } from './pickColumns'
|
||||
import { genderColumns, industryColumns } from './pickColumns'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const userStore = useUserStore()
|
||||
const { avatar, gender, industry, cover } = userStore.getUserInfo
|
||||
|
||||
@ -135,21 +147,21 @@ const state = reactive({
|
||||
})
|
||||
|
||||
function handleGender({ selectedOptions }) {
|
||||
state.genderText = selectedOptions[0].text
|
||||
state.genderText = t(selectedOptions[0].text)
|
||||
showToast(JSON.stringify(selectedOptions))
|
||||
// do something
|
||||
showGenderPicker.value = false
|
||||
}
|
||||
|
||||
function handleIndustry({ selectedOptions }) {
|
||||
state.industryText = selectedOptions[0].text
|
||||
state.industryText = t(selectedOptions[0].text)
|
||||
showToast(JSON.stringify(selectedOptions))
|
||||
// do something
|
||||
showIndustryPicker.value = false
|
||||
}
|
||||
|
||||
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() {
|
||||
|
@ -1,16 +1,16 @@
|
||||
<template>
|
||||
<div>
|
||||
<NavBar />
|
||||
<van-divider>主题模式</van-divider>
|
||||
<van-divider>{{ $t('layout.setting.darkMode') }}</van-divider>
|
||||
<van-cell-group inset>
|
||||
<van-cell center title="暗黑模式">
|
||||
<van-cell center :title="$t('layout.setting.diabloMode')">
|
||||
<template #right-icon>
|
||||
<van-switch v-model="getDarkMode" size="22" />
|
||||
</template>
|
||||
</van-cell>
|
||||
</van-cell-group>
|
||||
|
||||
<van-divider>系统主题色</van-divider>
|
||||
<van-divider>{{ $t('layout.setting.systemThemeColors') }}</van-divider>
|
||||
<div flex="~" justify="center">
|
||||
<div grid="~ cols-8 gap-2">
|
||||
<span
|
||||
@ -33,14 +33,14 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<van-divider>页面切换动画</van-divider>
|
||||
<van-divider>{{ $t('layout.setting.pageAnimationSwitch') }}</van-divider>
|
||||
<van-cell-group inset>
|
||||
<van-cell center title="开启动画">
|
||||
<van-cell center :title="$t('layout.setting.switchAnimation')">
|
||||
<template #right-icon>
|
||||
<van-switch v-model="designStore.isPageAnimate" size="22" />
|
||||
</template>
|
||||
</van-cell>
|
||||
<van-cell center title="动画类型">
|
||||
<van-cell center :title="$t('layout.setting.animationType')">
|
||||
<van-field
|
||||
v-model="animateState.text"
|
||||
readonly
|
||||
@ -68,6 +68,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, reactive } from 'vue'
|
||||
import { useDark, useToggle } from '@vueuse/core'
|
||||
import NavBar from './components/NavBar.vue'
|
||||
import { useDesignSettingStore } from '@/store/modules/designSetting'
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<van-nav-bar @click-left="router.back">
|
||||
<template #title>
|
||||
{{ getTitle }}
|
||||
<TitleI18n :title="getTitle" />
|
||||
</template>
|
||||
<template #left>
|
||||
<i class="i-ic:sharp-arrow-back-ios" text-xl />
|
||||
@ -13,6 +13,10 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { computed } from 'vue'
|
||||
import { TitleI18n } from '@/components/title-i18n'
|
||||
|
||||
const router = useRouter()
|
||||
const currentRoute = useRoute()
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
<div>
|
||||
<div :style="getUserCoverBg" class="my-bg h-70" />
|
||||
<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
|
||||
class="h-22 w-22 border-2 border-solid !absolute -top-10"
|
||||
@ -20,31 +20,31 @@
|
||||
</div>
|
||||
<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>
|
||||
<i class="i-mingcute:idcard-fill mr-2 text-xl" />
|
||||
</template>
|
||||
</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>
|
||||
<i class="i-material-symbols:account-box mr-2 text-xl" />
|
||||
</template>
|
||||
</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>
|
||||
<i class="i-material-symbols:palette mr-2 text-xl" />
|
||||
</template>
|
||||
</van-cell>
|
||||
|
||||
<van-cell :border="false" title="隐私政策" is-link>
|
||||
<van-cell :border="false" :title="$t('routes.my.privacy')" is-link>
|
||||
<template #icon>
|
||||
<i class="i-material-symbols:list-alt-rounded mr-2 text-xl" />
|
||||
</template>
|
||||
</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>
|
||||
<i class="i-solar:logout-3-bold mr-2 text-xl" />
|
||||
</template>
|
||||
@ -54,8 +54,8 @@
|
||||
v-model:show="showLogoutAction"
|
||||
teleport="body"
|
||||
:actions="logoutActions"
|
||||
cancel-text="取消"
|
||||
description="确认退出登录吗"
|
||||
:cancel-text="$t('common.cancelText')"
|
||||
:description="$t('routes.my.confirmLogout')"
|
||||
close-on-click-action
|
||||
/>
|
||||
</div>
|
||||
@ -63,21 +63,25 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref } from 'vue'
|
||||
import { showToast } from 'vant'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
|
||||
const { t } = useI18n()
|
||||
const userStore = useUserStore()
|
||||
|
||||
const showLogoutAction = ref(false)
|
||||
|
||||
const { nickname, avatar, cover, sign } = userStore.getUserInfo
|
||||
|
||||
const logoutActions = [
|
||||
{
|
||||
name: '退出登录',
|
||||
name: t('routes.my.logout'),
|
||||
color: '#ee0a24',
|
||||
callback: () => {
|
||||
userStore.Logout()
|
||||
showToast('退出成功')
|
||||
showToast(t('routes.my.logoutSuccess'))
|
||||
},
|
||||
},
|
||||
]
|
||||
|
@ -4,27 +4,20 @@ export interface FormColumns {
|
||||
}
|
||||
|
||||
export const genderColumns: FormColumns[] = [
|
||||
{ text: '男', value: 0 },
|
||||
{ text: '女', value: 1 },
|
||||
{ text: 'common.Gender.male', value: 0 },
|
||||
{ text: 'common.Gender.female', value: 1 },
|
||||
]
|
||||
|
||||
export const industryColumns: FormColumns[] = [
|
||||
{ text: '不展示', value: 0 },
|
||||
{ text: '学生', value: 1 },
|
||||
{ text: '自由职业', value: 2 },
|
||||
{ text: 'IT/互联网/通信', value: 3 },
|
||||
{ text: '金融', value: 4 },
|
||||
{ text: '健康/医疗', value: 5 },
|
||||
{ text: '工业/制造业', value: 6 },
|
||||
{ text: '零售', value: 7 },
|
||||
{ text: '贸易', value: 8 },
|
||||
{ text: '教育/科研', value: 9 },
|
||||
{ text: '培训', value: 10 },
|
||||
{ text: '房地产/建筑', value: 11 },
|
||||
{ text: '文化/艺术', value: 12 },
|
||||
{ text: '影视/娱乐', value: 13 },
|
||||
{ text: '法律/会计/咨询', value: 14 },
|
||||
{ text: '媒体/广告/公关', value: 15 },
|
||||
{ text: '体育/健身', value: 16 },
|
||||
{ text: '企事业单位', value: 17 },
|
||||
{ text: 'common.Industry.student', value: 0 },
|
||||
{ text: 'common.Industry.worker', value: 1 },
|
||||
{ text: 'common.Industry.education', value: 2 },
|
||||
{ text: 'common.Industry.finance', value: 3 },
|
||||
{ text: 'common.Industry.health', value: 4 },
|
||||
{ text: 'common.Industry.life', value: 5 },
|
||||
{ text: 'common.Industry.tech', value: 6 },
|
||||
{ text: 'common.Industry.IT', value: 7 },
|
||||
{ text: 'common.Industry.manager', value: 8 },
|
||||
{ text: 'common.Industry.salesman', value: 9 },
|
||||
{ text: 'common.Industry.other', value: 10 },
|
||||
]
|
||||
|
Loading…
x
Reference in New Issue
Block a user