commit
33335909f9
@ -17,7 +17,7 @@ export const PROXY_CONFIG = {
|
||||
* @转发路径 http://localhost:9999/api/v1/user
|
||||
*/
|
||||
'/api/v1': {
|
||||
target: 'http://localhost:9999',
|
||||
target: 'http://127.0.0.1:9999',
|
||||
changeOrigin: true,
|
||||
},
|
||||
}
|
||||
|
||||
16
web/i18n/index.js
Normal file
16
web/i18n/index.js
Normal file
@ -0,0 +1,16 @@
|
||||
import { createI18n } from 'vue-i18n'
|
||||
import { sStorage } from '@/utils'
|
||||
|
||||
import messages from './messages'
|
||||
|
||||
const currentLocale = sStorage.get('locale')
|
||||
|
||||
const i18n = createI18n({
|
||||
legacy: false,
|
||||
globalInjection: true,
|
||||
locale: currentLocale || 'en',
|
||||
fallbackLocale: 'en',
|
||||
messages: messages
|
||||
})
|
||||
|
||||
export default i18n
|
||||
62
web/i18n/messages/cn.json
Normal file
62
web/i18n/messages/cn.json
Normal file
@ -0,0 +1,62 @@
|
||||
{
|
||||
"lang": "中文",
|
||||
"app_name": "Vue FastAPI Admin",
|
||||
"header": {
|
||||
"label_profile": "个人信息",
|
||||
"label_logout": "退出登录",
|
||||
"label_logout_dialog_title": "提示",
|
||||
"text_logout_confirm": "确认退出?",
|
||||
"text_logout_success": "已退出登录"
|
||||
},
|
||||
"views": {
|
||||
"login": {
|
||||
"text_login": "登录",
|
||||
"message_input_username_password": "请输入用户名和密码",
|
||||
"message_verifying": "正在验证...",
|
||||
"message_login_success": "登录成功"
|
||||
},
|
||||
"workbench": {
|
||||
"label_workbench": "工作台",
|
||||
"text_hello": "hello, {username}",
|
||||
"text_welcome": "今天又是元气满满的一天!",
|
||||
"label_number_of_items": "项目数",
|
||||
"label_upcoming": "待办",
|
||||
"label_information": "消息",
|
||||
"label_project": "项目",
|
||||
"label_more": "更多"
|
||||
},
|
||||
"profile": {
|
||||
"label_profile": "个人中心",
|
||||
"label_modify_information": "修改信息",
|
||||
"label_change_password": "修改密码",
|
||||
"label_avatar": "头像",
|
||||
"label_username": "用户姓名",
|
||||
"label_email": "邮箱",
|
||||
"label_old_password": "旧密码",
|
||||
"label_new_password": "新密码",
|
||||
"label_confirm_password": "确认密码",
|
||||
"placeholder_username": "请填写姓名",
|
||||
"placeholder_email": "请填写邮箱",
|
||||
"placeholder_old_password": "请输入旧密码",
|
||||
"placeholder_new_password": "请输入新密码",
|
||||
"placeholder_confirm_password": "请再次输入新密码",
|
||||
"message_username_required": "请输入昵称",
|
||||
"message_old_password_required": "请输入旧密码",
|
||||
"message_new_password_required": "请输入新密码",
|
||||
"message_password_confirmation_required": "请再次输入密码",
|
||||
"message_password_confirmation_diff": "两次密码输入不一致"
|
||||
},
|
||||
"errors": {
|
||||
"label_error": "错误页",
|
||||
"text_back_to_home": "返回首页"
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
"text": {
|
||||
"update_success": "修改成功"
|
||||
},
|
||||
"buttons": {
|
||||
"update": "修改"
|
||||
}
|
||||
}
|
||||
}
|
||||
62
web/i18n/messages/en.json
Normal file
62
web/i18n/messages/en.json
Normal file
@ -0,0 +1,62 @@
|
||||
{
|
||||
"lang": "English",
|
||||
"app_name": "Vue FastAPI Admin",
|
||||
"header": {
|
||||
"label_profile": "Profile",
|
||||
"label_logout": "Logout",
|
||||
"label_logout_dialog_title": "Hint",
|
||||
"text_logout_confirm": "Logout confirm",
|
||||
"text_logout_success": "Logout success"
|
||||
},
|
||||
"views": {
|
||||
"login": {
|
||||
"text_login": "Login",
|
||||
"message_input_username_password": "Please enter username and password",
|
||||
"message_verifying": "Verifying...",
|
||||
"message_login_success": "Login successful"
|
||||
},
|
||||
"workbench": {
|
||||
"label_workbench": "Workbench",
|
||||
"text_hello": "hello, {username}",
|
||||
"text_welcome": "Today is another day full of energy!",
|
||||
"label_number_of_items": "Number of items",
|
||||
"label_upcoming": "Upcoming",
|
||||
"label_information": "Information",
|
||||
"label_project": "Project",
|
||||
"label_more": "More"
|
||||
},
|
||||
"profile": {
|
||||
"label_profile": "Profile",
|
||||
"label_modify_information": "Modify your information",
|
||||
"label_change_password": "Change password",
|
||||
"label_avatar": "Avatar",
|
||||
"label_username": "Username",
|
||||
"label_email": "Email",
|
||||
"label_old_password": "Old password",
|
||||
"label_new_password": "New password",
|
||||
"label_confirm_password": "Password confirmation",
|
||||
"placeholder_username": "Please fill in your name",
|
||||
"placeholder_email": "Please fill in your email address",
|
||||
"placeholder_old_password": "Please enter the old password",
|
||||
"placeholder_new_password": "Please enter a new password",
|
||||
"placeholder_confirm_password": "Please enter the confirm password",
|
||||
"message_username_required": "Please enter username",
|
||||
"message_old_password_required": "Please enter the old password",
|
||||
"message_new_password_required": "Please enter a new password",
|
||||
"message_password_confirmation_required": "Please enter confirm password",
|
||||
"message_password_confirmation_diff": "Two password inputs are inconsistent"
|
||||
},
|
||||
"errors": {
|
||||
"label_error": "Error",
|
||||
"text_back_to_home": "Back to home"
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
"text": {
|
||||
"update_success": "Update success"
|
||||
},
|
||||
"buttons": {
|
||||
"update": "Update"
|
||||
}
|
||||
}
|
||||
}
|
||||
7
web/i18n/messages/index.js
Normal file
7
web/i18n/messages/index.js
Normal file
@ -0,0 +1,7 @@
|
||||
import * as en from './en.json'
|
||||
import * as cn from './cn.json'
|
||||
|
||||
export default {
|
||||
en,
|
||||
cn
|
||||
}
|
||||
@ -34,6 +34,7 @@
|
||||
"vite-plugin-html": "^3.2.0",
|
||||
"vite-plugin-svg-icons": "^2.0.1",
|
||||
"vue": "^3.3.4",
|
||||
"vue-i18n": "9",
|
||||
"vue-router": "^4.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
1377
web/pnpm-lock.yaml
generated
1377
web/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
30
web/src/layout/components/header/components/Languages.vue
Normal file
30
web/src/layout/components/header/components/Languages.vue
Normal file
@ -0,0 +1,30 @@
|
||||
<template>
|
||||
<n-dropdown :options="options" @select="handleChangeLocale">
|
||||
<n-icon mr-20 size="18" style="cursor: pointer">
|
||||
<icon-mdi:globe/>
|
||||
</n-icon>
|
||||
</n-dropdown>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {useI18n} from 'vue-i18n'
|
||||
import {useAppStore} from "@/store";
|
||||
|
||||
const store = useAppStore()
|
||||
const { availableLocales, t } = useI18n()
|
||||
|
||||
const options = computed(() => {
|
||||
let select = []
|
||||
availableLocales.forEach(locale => {
|
||||
select.push({
|
||||
label: t('lang', 1, {'locale': locale}),
|
||||
key: locale
|
||||
})
|
||||
})
|
||||
return select
|
||||
})
|
||||
|
||||
const handleChangeLocale = (value) => {
|
||||
store.setLocale(value)
|
||||
}
|
||||
</script>
|
||||
@ -11,6 +11,9 @@
|
||||
import { useUserStore } from '@/store'
|
||||
import { renderIcon } from '@/utils'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const {t} = useI18n()
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
@ -18,12 +21,12 @@ const userStore = useUserStore()
|
||||
|
||||
const options = [
|
||||
{
|
||||
label: '个人信息',
|
||||
label: t('header.label_profile'),
|
||||
key: 'profile',
|
||||
icon: renderIcon('mdi-account-arrow-right-outline', { size: '14px' }),
|
||||
},
|
||||
{
|
||||
label: '退出登录',
|
||||
label: t('header.label_logout'),
|
||||
key: 'logout',
|
||||
icon: renderIcon('mdi:exit-to-app', { size: '14px' }),
|
||||
},
|
||||
@ -34,12 +37,12 @@ function handleSelect(key) {
|
||||
router.push('/profile')
|
||||
} else if (key === 'logout') {
|
||||
$dialog.confirm({
|
||||
title: '提示',
|
||||
title: t('header.label_logout_dialog_title'),
|
||||
type: 'warning',
|
||||
content: '确认退出?',
|
||||
content: t('header.text_logout_confirm'),
|
||||
confirm() {
|
||||
userStore.logout()
|
||||
$message.success('已退出登录')
|
||||
$message.success(t('header.text_logout_success'))
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
<BreadCrumb ml-15 hidden sm:block />
|
||||
</div>
|
||||
<div ml-auto flex items-center>
|
||||
<Languages />
|
||||
<ThemeMode />
|
||||
<GithubSite />
|
||||
<FullScreen />
|
||||
@ -18,4 +19,5 @@ import FullScreen from './components/FullScreen.vue'
|
||||
import UserAvatar from './components/UserAvatar.vue'
|
||||
import GithubSite from './components/GithubSite.vue'
|
||||
import ThemeMode from './components/ThemeMode.vue'
|
||||
import Languages from './components/Languages.vue';
|
||||
</script>
|
||||
|
||||
@ -10,6 +10,7 @@ import { setupStore } from '@/store'
|
||||
import App from './App.vue'
|
||||
import { setupDirectives } from './directives'
|
||||
import { useResize } from '@/utils'
|
||||
import i18n from '~/i18n'
|
||||
|
||||
async function setupApp() {
|
||||
const app = createApp(App)
|
||||
@ -19,6 +20,7 @@ async function setupApp() {
|
||||
await setupRouter(app)
|
||||
setupDirectives(app)
|
||||
app.use(useResize)
|
||||
app.use(i18n)
|
||||
app.mount('#app')
|
||||
}
|
||||
|
||||
|
||||
@ -1,8 +1,11 @@
|
||||
import i18n from '~/i18n'
|
||||
const {t} = i18n.global
|
||||
|
||||
const Layout = () => import('@/layout/index.vue')
|
||||
|
||||
export const basicRoutes = [
|
||||
{
|
||||
name: '工作台',
|
||||
name: t('views.workbench.label_workbench'),
|
||||
path: '/',
|
||||
component: Layout,
|
||||
redirect: '/workbench', // 默认跳转到首页
|
||||
@ -10,9 +13,9 @@ export const basicRoutes = [
|
||||
{
|
||||
path: 'workbench',
|
||||
component: () => import('@/views/workbench/index.vue'),
|
||||
name: '工作台',
|
||||
name: t('views.workbench.label_workbench'),
|
||||
meta: {
|
||||
title: '工作台',
|
||||
title: t('views.workbench.label_workbench'),
|
||||
icon: 'icon-park-outline:workbench',
|
||||
affix: true,
|
||||
},
|
||||
@ -21,7 +24,7 @@ export const basicRoutes = [
|
||||
meta: { order: 0 },
|
||||
},
|
||||
{
|
||||
name: '个人中心',
|
||||
name: t('views.profile.label_profile'),
|
||||
path: '/',
|
||||
component: Layout,
|
||||
isHidden: true,
|
||||
@ -29,9 +32,9 @@ export const basicRoutes = [
|
||||
{
|
||||
path: 'profile',
|
||||
component: () => import('@/views/profile/index.vue'),
|
||||
name: '个人中心',
|
||||
name: t('views.profile.label_profile'),
|
||||
meta: {
|
||||
title: '个人中心',
|
||||
title: t('views.profile.label_profile'),
|
||||
icon: 'user',
|
||||
affix: true,
|
||||
},
|
||||
@ -45,7 +48,7 @@ export const basicRoutes = [
|
||||
component: Layout,
|
||||
redirect: '/error-page/404',
|
||||
meta: {
|
||||
title: '错误页',
|
||||
title: t('views.errors.label_error'),
|
||||
icon: 'mdi:alert-circle-outline',
|
||||
order: 99,
|
||||
},
|
||||
|
||||
@ -1,5 +1,10 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { useDark } from '@vueuse/core'
|
||||
import { sStorage } from '@/utils'
|
||||
import i18n from '~/i18n'
|
||||
|
||||
const currentLocale = sStorage.get('locale')
|
||||
const {locale} = i18n.global
|
||||
|
||||
const isDark = useDark()
|
||||
export const useAppStore = defineStore('app', {
|
||||
@ -11,6 +16,7 @@ export const useAppStore = defineStore('app', {
|
||||
/** keepAlive路由的key,重新赋值可重置keepAlive */
|
||||
aliveKeys: {},
|
||||
isDark,
|
||||
locale: currentLocale || 'en'
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
@ -45,5 +51,10 @@ export const useAppStore = defineStore('app', {
|
||||
toggleDark() {
|
||||
this.isDark = !this.isDark
|
||||
},
|
||||
setLocale(newLocale) {
|
||||
this.locale = newLocale
|
||||
locale.value = newLocale
|
||||
sStorage.set('locale', newLocale)
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<icon-custom-unauthorized text-400px text-primary></icon-custom-unauthorized>
|
||||
</template>
|
||||
<template #footer>
|
||||
<n-button type="primary" @click="replace('/')">返回首页</n-button>
|
||||
<n-button type="primary" @click="replace('/')">{{ $t('views.errors.text_back_to_home') }}</n-button>
|
||||
</template>
|
||||
</n-result>
|
||||
</AppPage>
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<icon-custom-forbidden text-400px text-primary></icon-custom-forbidden>
|
||||
</template>
|
||||
<template #footer>
|
||||
<n-button type="primary" @click="replace('/')">返回首页</n-button>
|
||||
<n-button type="primary" @click="replace('/')">{{ $t('views.errors.text_back_to_home') }}</n-button>
|
||||
</template>
|
||||
</n-result>
|
||||
</AppPage>
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<icon-custom-not-found text-400px text-primary></icon-custom-not-found>
|
||||
</template>
|
||||
<template #footer>
|
||||
<n-button type="primary" @click="replace('/')">返回首页</n-button>
|
||||
<n-button type="primary" @click="replace('/')">{{ $t('views.errors.text_back_to_home') }}</n-button>
|
||||
</template>
|
||||
</n-result>
|
||||
</AppPage>
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<icon-custom-server-error text-400px text-primary></icon-custom-server-error>
|
||||
</template>
|
||||
<template #footer>
|
||||
<n-button type="primary" @click="replace('/')">返回首页</n-button>
|
||||
<n-button type="primary" @click="replace('/')">{{ $t('views.errors.text_back_to_home') }}</n-button>
|
||||
</template>
|
||||
</n-result>
|
||||
</AppPage>
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
|
||||
<div w-320 flex-col px-20 py-35>
|
||||
<h5 f-c-c text-24 font-normal color="#6a6a6a">
|
||||
<icon-custom-logo mr-10 text-50 color-primary />{{ title }}
|
||||
<icon-custom-logo mr-10 text-50 color-primary />{{ $t('app_name') }}
|
||||
</h5>
|
||||
<div mt-30>
|
||||
<n-input
|
||||
@ -44,7 +44,7 @@
|
||||
:loading="loading"
|
||||
@click="handleLogin"
|
||||
>
|
||||
登录
|
||||
{{ $t('views.login.text_login') }}
|
||||
</n-button>
|
||||
</div>
|
||||
</div>
|
||||
@ -57,11 +57,11 @@ import { lStorage, setToken } from '@/utils'
|
||||
import bgImg from '@/assets/images/login_bg.webp'
|
||||
import api from '@/api'
|
||||
import { addDynamicRoutes } from '@/router'
|
||||
|
||||
const title = import.meta.env.VITE_TITLE
|
||||
import {useI18n} from 'vue-i18n'
|
||||
|
||||
const router = useRouter()
|
||||
const { query } = useRoute()
|
||||
const {t} = useI18n({ useScope: "global" })
|
||||
|
||||
const loginInfo = ref({
|
||||
username: '',
|
||||
@ -82,14 +82,14 @@ const loading = ref(false)
|
||||
async function handleLogin() {
|
||||
const { username, password } = loginInfo.value
|
||||
if (!username || !password) {
|
||||
$message.warning('请输入用户名和密码')
|
||||
$message.warning(t('views.login.message_input_username_password'))
|
||||
return
|
||||
}
|
||||
try {
|
||||
loading.value = true
|
||||
$message.loading('正在验证...')
|
||||
$message.loading(t('views.login.message_login_success'))
|
||||
const res = await api.login({ username, password: password.toString() })
|
||||
$message.success('登录成功')
|
||||
$message.success(t('views.login.message_login_success'))
|
||||
setToken(res.data.access_token)
|
||||
await addDynamicRoutes()
|
||||
if (query.redirect) {
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { NButton, NForm, NFormItem, NInput, NTabPane, NTabs, NImage } from 'naive-ui'
|
||||
|
||||
import {useI18n} from "vue-i18n";
|
||||
import CommonPage from '@/components/page/CommonPage.vue'
|
||||
import { useUserStore } from '@/store'
|
||||
import api from '@/api'
|
||||
import { is } from '~/src/utils'
|
||||
|
||||
const {t} = useI18n()
|
||||
const userStore = useUserStore()
|
||||
const isLoading = ref(false)
|
||||
|
||||
@ -26,7 +27,7 @@ async function updateProfile() {
|
||||
.then(() => {
|
||||
userStore.setUserInfo(infoForm.value)
|
||||
isLoading.value = false
|
||||
$message.success('修改成功')
|
||||
$message.success(t('common.text.update_success'))
|
||||
})
|
||||
.catch(() => {
|
||||
isLoading.value = false
|
||||
@ -37,7 +38,7 @@ const infoFormRules = {
|
||||
username: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入昵称',
|
||||
message: t('views.profile.message_username_required'),
|
||||
trigger: ['input', 'blur', 'change'],
|
||||
},
|
||||
],
|
||||
@ -77,31 +78,31 @@ const passwordFormRules = {
|
||||
old_password: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入旧密码',
|
||||
message: t('views.profile.message_old_password_required'),
|
||||
trigger: ['input', 'blur', 'change'],
|
||||
},
|
||||
],
|
||||
new_password: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入新密码',
|
||||
message: t('views.profile.message_new_password_required'),
|
||||
trigger: ['input', 'blur', 'change'],
|
||||
},
|
||||
],
|
||||
confirm_password: [
|
||||
{
|
||||
required: true,
|
||||
message: '请再次输入密码',
|
||||
message: t('views.profile.message_password_confirmation_required'),
|
||||
trigger: ['input', 'blur'],
|
||||
},
|
||||
{
|
||||
validator: validatePasswordStartWith,
|
||||
message: '两次密码输入不一致',
|
||||
message: t('views.profile.message_password_confirmation_diff'),
|
||||
trigger: 'input',
|
||||
},
|
||||
{
|
||||
validator: validatePasswordSame,
|
||||
message: '两次密码输入不一致',
|
||||
message: t('views.profile.message_password_confirmation_diff'),
|
||||
trigger: ['blur', 'password-input'],
|
||||
},
|
||||
],
|
||||
@ -121,7 +122,7 @@ function validatePasswordSame(rule, value) {
|
||||
<template>
|
||||
<CommonPage :show-header="false">
|
||||
<NTabs type="line" animated>
|
||||
<NTabPane name="website" tab="修改信息">
|
||||
<NTabPane name="website" :tab="$t('views.profile.label_modify_information')">
|
||||
<div class="m-30 flex items-center">
|
||||
<NForm
|
||||
ref="infoFormRef"
|
||||
@ -132,56 +133,56 @@ function validatePasswordSame(rule, value) {
|
||||
:rules="infoFormRules"
|
||||
class="w-400"
|
||||
>
|
||||
<NFormItem label="头像" path="avatar">
|
||||
<NFormItem :label="$t('views.profile.label_avatar')" path="avatar">
|
||||
<NImage width="100" :src="infoForm.avatar"></NImage>
|
||||
</NFormItem>
|
||||
<NFormItem label="用户姓名" path="username">
|
||||
<NInput v-model:value="infoForm.username" type="text" placeholder="请填写姓名" />
|
||||
<NFormItem :label="$t('views.profile.label_username')" path="username">
|
||||
<NInput v-model:value="infoForm.username" type="text" :placeholder="$t('views.profile.placeholder_username')" />
|
||||
</NFormItem>
|
||||
<NFormItem label="邮箱" path="email">
|
||||
<NInput v-model:value="infoForm.email" type="text" placeholder="请填写邮箱" />
|
||||
<NFormItem :label="$t('views.profile.label_email')" path="email">
|
||||
<NInput v-model:value="infoForm.email" type="text" :placeholder="$t('views.profile.placeholder_email')" />
|
||||
</NFormItem>
|
||||
<NButton type="primary" :loading="isLoading" @click="updateProfile"> 修改 </NButton>
|
||||
<NButton type="primary" :loading="isLoading" @click="updateProfile"> {{$t("common.buttons.update")}} </NButton>
|
||||
</NForm>
|
||||
</div>
|
||||
</NTabPane>
|
||||
<NTabPane name="contact" tab="修改密码">
|
||||
<NTabPane name="contact" :tab="$t('views.profile.label_change_password')">
|
||||
<NForm
|
||||
ref="passwordFormRef"
|
||||
label-placement="left"
|
||||
label-align="left"
|
||||
:model="passwordForm"
|
||||
label-width="100"
|
||||
label-width="200"
|
||||
:rules="passwordFormRules"
|
||||
class="m-30 w-400"
|
||||
class="m-30 w-500"
|
||||
>
|
||||
<NFormItem label="旧密码" path="old_password">
|
||||
<NFormItem :label="$t('views.profile.label_old_password')" path="old_password">
|
||||
<NInput
|
||||
v-model:value="passwordForm.old_password"
|
||||
type="password"
|
||||
show-password-on="mousedown"
|
||||
placeholder="请输入旧密码"
|
||||
:placeholder="$t('views.profile.placeholder_old_password')"
|
||||
/>
|
||||
</NFormItem>
|
||||
<NFormItem label="新密码" path="new_password">
|
||||
<NFormItem :label="$t('views.profile.label_new_password')" path="new_password">
|
||||
<NInput
|
||||
v-model:value="passwordForm.new_password"
|
||||
:disabled="!passwordForm.old_password"
|
||||
type="password"
|
||||
show-password-on="mousedown"
|
||||
placeholder="请输入新密码"
|
||||
:placeholder="$t('views.profile.placeholder_new_password')"
|
||||
/>
|
||||
</NFormItem>
|
||||
<NFormItem label="确认密码" path="confirm_password">
|
||||
<NFormItem :label="$t('views.profile.label_confirm_password')" path="confirm_password">
|
||||
<NInput
|
||||
v-model:value="passwordForm.confirm_password"
|
||||
:disabled="!passwordForm.new_password"
|
||||
type="password"
|
||||
show-password-on="mousedown"
|
||||
placeholder="请再次输入新密码"
|
||||
:placeholder="$t('views.profile.placeholder_confirm_password')"
|
||||
/>
|
||||
</NFormItem>
|
||||
<NButton type="primary" :loading="isLoading" @click="updatePassword"> 修改 </NButton>
|
||||
<NButton type="primary" :loading="isLoading" @click="updatePassword"> {{$t("common.buttons.update")}} </NButton>
|
||||
</NForm>
|
||||
</NTabPane>
|
||||
</NTabs>
|
||||
|
||||
@ -6,8 +6,8 @@
|
||||
<div flex items-center>
|
||||
<img rounded-full width="60" :src="userStore.avatar" />
|
||||
<div ml-10>
|
||||
<p text-20 font-semibold>hello, {{ userStore.name }}</p>
|
||||
<p mt-5 text-14 op-60>今天又是元气满满的一天!</p>
|
||||
<p text-20 font-semibold> {{ $t('views.workbench.text_hello', {username: userStore.name}) }}</p>
|
||||
<p mt-5 text-14 op-60>{{ $t('views.workbench.text_welcome') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<n-space :size="12" :wrap="false">
|
||||
@ -16,9 +16,9 @@
|
||||
</div>
|
||||
</n-card>
|
||||
|
||||
<n-card title="项目" size="small" :segmented="true" mt-15 rounded-10>
|
||||
<n-card :title="$t('views.workbench.label_project')" size="small" :segmented="true" mt-15 rounded-10>
|
||||
<template #header-extra>
|
||||
<n-button text type="primary">更多</n-button>
|
||||
<n-button text type="primary">{{$t('views.workbench.label_more')}}</n-button>
|
||||
</template>
|
||||
<div flex flex-wrap justify-between>
|
||||
<n-card
|
||||
@ -29,7 +29,7 @@
|
||||
title="Vue FastAPI Admin"
|
||||
size="small"
|
||||
>
|
||||
<p op-60>一个基于 Vue3.0、FastAPI、Naive UI 的轻量级后台管理模板</p>
|
||||
<p op-60>{{dummyText}}</p>
|
||||
</n-card>
|
||||
</div>
|
||||
</n-card>
|
||||
@ -39,24 +39,28 @@
|
||||
|
||||
<script setup>
|
||||
import { useUserStore } from '@/store'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const statisticData = [
|
||||
const dummyText = "一个基于 Vue3.0、FastAPI、Naive UI 的轻量级后台管理模板"
|
||||
const {t} = useI18n({ useScope: "global" })
|
||||
|
||||
const statisticData = computed(() => [
|
||||
{
|
||||
id: 0,
|
||||
label: '项目数',
|
||||
label: t('views.workbench.label_number_of_items'),
|
||||
value: '25',
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
label: '待办',
|
||||
label: t('views.workbench.label_upcoming'),
|
||||
value: '4/16',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
label: '消息',
|
||||
label: t('views.workbench.label_information'),
|
||||
value: '12',
|
||||
},
|
||||
]
|
||||
])
|
||||
|
||||
const userStore = useUserStore()
|
||||
</script>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user