ts转js
This commit is contained in:
@@ -1,3 +1,2 @@
|
||||
/node_modules/*
|
||||
/dist/*
|
||||
/src/main.ts
|
||||
/dist/*
|
||||
@@ -42,6 +42,6 @@
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
46
src/App.vue
46
src/App.vue
@@ -4,31 +4,31 @@
|
||||
</ElConfigProvider>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useUserStore } from './store/modules/user'
|
||||
import zh from 'element-plus/es/locale/lang/zh-cn'
|
||||
import en from 'element-plus/es/locale/lang/en'
|
||||
import { systemUpgrade } from './utils/sys'
|
||||
import { toggleTransition } from './utils/ui/animation'
|
||||
import { checkStorageCompatibility } from './utils/storage'
|
||||
import { initializeTheme } from './hooks/core/useTheme'
|
||||
<script setup>
|
||||
import { useUserStore } from './store/modules/user'
|
||||
import zh from 'element-plus/es/locale/lang/zh-cn'
|
||||
import en from 'element-plus/es/locale/lang/en'
|
||||
import { systemUpgrade } from './utils/sys'
|
||||
import { toggleTransition } from './utils/ui/animation'
|
||||
import { checkStorageCompatibility } from './utils/storage'
|
||||
import { initializeTheme } from './hooks/core/useTheme'
|
||||
|
||||
const userStore = useUserStore()
|
||||
const { language } = storeToRefs(userStore)
|
||||
const userStore = useUserStore()
|
||||
const { language } = storeToRefs(userStore)
|
||||
|
||||
const locales = {
|
||||
zh: zh,
|
||||
en: en
|
||||
}
|
||||
const locales = {
|
||||
zh: zh,
|
||||
en: en
|
||||
}
|
||||
|
||||
onBeforeMount(() => {
|
||||
toggleTransition(true)
|
||||
initializeTheme()
|
||||
})
|
||||
onBeforeMount(() => {
|
||||
toggleTransition(true)
|
||||
initializeTheme()
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
checkStorageCompatibility()
|
||||
toggleTransition(false)
|
||||
systemUpgrade()
|
||||
})
|
||||
onMounted(() => {
|
||||
checkStorageCompatibility()
|
||||
toggleTransition(false)
|
||||
systemUpgrade()
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -5,8 +5,8 @@ import request from '@/utils/http'
|
||||
* @param params 登录参数
|
||||
* @returns 登录响应
|
||||
*/
|
||||
export function fetchLogin(params: Api.Auth.LoginParams) {
|
||||
return request.post<Api.Auth.LoginResponse>({
|
||||
export function fetchLogin(params) {
|
||||
return request.post({
|
||||
url: '/api/auth/login',
|
||||
params
|
||||
// showSuccessMessage: true // 显示成功消息
|
||||
@@ -19,7 +19,7 @@ export function fetchLogin(params: Api.Auth.LoginParams) {
|
||||
* @returns 用户信息
|
||||
*/
|
||||
export function fetchGetUserInfo() {
|
||||
return request.get<Api.Auth.UserInfo>({
|
||||
return request.get({
|
||||
url: '/api/user/info'
|
||||
// 自定义请求头
|
||||
// headers: {
|
||||
24
src/api/system-manage.js
Normal file
24
src/api/system-manage.js
Normal file
@@ -0,0 +1,24 @@
|
||||
import request from '@/utils/http'
|
||||
|
||||
// 获取用户列表
|
||||
export function fetchGetUserList(params) {
|
||||
return request.get({
|
||||
url: '/api/user/list',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// 获取角色列表
|
||||
export function fetchGetRoleList(params) {
|
||||
return request.get({
|
||||
url: '/api/role/list',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// 获取菜单列表
|
||||
export function fetchGetMenuList() {
|
||||
return request.get({
|
||||
url: '/api/v3/system/menus/simple'
|
||||
})
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import request from '@/utils/http'
|
||||
import { AppRouteRecord } from '@/types/router'
|
||||
|
||||
// 获取用户列表
|
||||
export function fetchGetUserList(params: Api.SystemManage.UserSearchParams) {
|
||||
return request.get<Api.SystemManage.UserList>({
|
||||
url: '/api/user/list',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// 获取角色列表
|
||||
export function fetchGetRoleList(params: Api.SystemManage.RoleSearchParams) {
|
||||
return request.get<Api.SystemManage.RoleList>({
|
||||
url: '/api/role/list',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// 获取菜单列表
|
||||
export function fetchGetMenuList() {
|
||||
return request.get<AppRouteRecord[]>({
|
||||
url: '/api/v3/system/menus/simple'
|
||||
})
|
||||
}
|
||||
@@ -22,8 +22,6 @@
|
||||
*/
|
||||
|
||||
import { createI18n } from 'vue-i18n'
|
||||
import type { I18n, I18nOptions } from 'vue-i18n'
|
||||
import { LanguageEnum } from '@/enums/appEnum'
|
||||
import { getSystemStorage } from '@/utils/storage'
|
||||
import { StorageKeyManager } from '@/utils/storage/storage-key-manager'
|
||||
|
||||
@@ -40,8 +38,8 @@ const storageKeyManager = new StorageKeyManager()
|
||||
* 语言消息对象
|
||||
*/
|
||||
const messages = {
|
||||
[LanguageEnum.EN]: enMessages,
|
||||
[LanguageEnum.ZH]: zhMessages
|
||||
'en': enMessages,
|
||||
'zh': zhMessages
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -49,15 +47,15 @@ const messages = {
|
||||
* 用于语言切换下拉框
|
||||
*/
|
||||
export const languageOptions = [
|
||||
{ value: LanguageEnum.ZH, label: '简体中文' },
|
||||
{ value: LanguageEnum.EN, label: 'English' }
|
||||
{ value: 'zh', label: '简体中文' },
|
||||
{ value: 'en', label: 'English' }
|
||||
]
|
||||
|
||||
/**
|
||||
* 从存储中获取语言设置
|
||||
* @returns 语言设置,如果获取失败则返回默认语言
|
||||
*/
|
||||
const getDefaultLanguage = (): LanguageEnum => {
|
||||
const getDefaultLanguage = () => {
|
||||
// 尝试从版本化的存储中获取语言设置
|
||||
try {
|
||||
const storageKey = storageKeyManager.getStorageKey('user')
|
||||
@@ -65,7 +63,7 @@ const getDefaultLanguage = (): LanguageEnum => {
|
||||
|
||||
if (userStore) {
|
||||
const { language } = JSON.parse(userStore)
|
||||
if (language && Object.values(LanguageEnum).includes(language)) {
|
||||
if (language && Object.values(['zh', 'en']).includes(language)) {
|
||||
return language
|
||||
}
|
||||
}
|
||||
@@ -78,7 +76,7 @@ const getDefaultLanguage = (): LanguageEnum => {
|
||||
const sys = getSystemStorage()
|
||||
if (sys) {
|
||||
const { user } = JSON.parse(sys)
|
||||
if (user?.language && Object.values(LanguageEnum).includes(user.language)) {
|
||||
if (user?.language && Object.values(['zh', 'en']).includes(user.language)) {
|
||||
return user.language
|
||||
}
|
||||
}
|
||||
@@ -87,37 +85,30 @@ const getDefaultLanguage = (): LanguageEnum => {
|
||||
}
|
||||
|
||||
// 返回默认语言
|
||||
console.debug('[i18n] 使用默认语言:', LanguageEnum.ZH)
|
||||
return LanguageEnum.ZH
|
||||
console.debug('[i18n] 使用默认语言:', 'zh')
|
||||
return 'zh'
|
||||
}
|
||||
|
||||
/**
|
||||
* i18n 配置选项
|
||||
*/
|
||||
const i18nOptions: I18nOptions = {
|
||||
const i18nOptions = {
|
||||
locale: getDefaultLanguage(),
|
||||
legacy: false,
|
||||
globalInjection: true,
|
||||
fallbackLocale: LanguageEnum.ZH,
|
||||
fallbackLocale: 'zh',
|
||||
messages
|
||||
}
|
||||
|
||||
/**
|
||||
* i18n 实例
|
||||
*/
|
||||
const i18n: I18n = createI18n(i18nOptions)
|
||||
|
||||
/**
|
||||
* 翻译函数类型
|
||||
*/
|
||||
interface Translation {
|
||||
(key: string): string
|
||||
}
|
||||
const i18n = createI18n(i18nOptions)
|
||||
|
||||
/**
|
||||
* 全局翻译函数
|
||||
* 可在任何地方使用,无需导入 useI18n
|
||||
*/
|
||||
export const $t = i18n.global.t as Translation
|
||||
export const $t = i18n.global.t
|
||||
|
||||
export default i18n
|
||||
20
src/main.js
Normal file
20
src/main.js
Normal file
@@ -0,0 +1,20 @@
|
||||
import App from './App.vue'
|
||||
import { createApp } from 'vue'
|
||||
import { initStore } from './store' // Store
|
||||
import { initRouter } from './router' // Router
|
||||
import language from './locales' // 国际化
|
||||
import '@styles/core/tailwind.css' // tailwind
|
||||
import '@styles/index.scss' // 样式
|
||||
import { setupGlobDirectives } from './directives'
|
||||
import { setupErrorHandle } from './utils/sys/error-handle'
|
||||
|
||||
document.addEventListener('touchstart', function () {}, { passive: false })
|
||||
|
||||
const app = createApp(App)
|
||||
initStore(app)
|
||||
initRouter(app)
|
||||
setupGlobDirectives(app)
|
||||
setupErrorHandle(app)
|
||||
|
||||
app.use(language)
|
||||
app.mount('#app')
|
||||
25
src/main.ts
25
src/main.ts
@@ -1,25 +0,0 @@
|
||||
import App from './App.vue'
|
||||
import { createApp } from 'vue'
|
||||
import { initStore } from './store' // Store
|
||||
import { initRouter } from './router' // Router
|
||||
import language from './locales' // 国际化
|
||||
import '@styles/core/tailwind.css' // tailwind
|
||||
import '@styles/index.scss' // 样式
|
||||
import '@utils/sys/console.ts' // 控制台输出内容
|
||||
import { setupGlobDirectives } from './directives'
|
||||
import { setupErrorHandle } from './utils/sys/error-handle'
|
||||
|
||||
document.addEventListener(
|
||||
'touchstart',
|
||||
function () {},
|
||||
{ passive: false }
|
||||
)
|
||||
|
||||
const app = createApp(App)
|
||||
initStore(app)
|
||||
initRouter(app)
|
||||
setupGlobDirectives(app)
|
||||
setupErrorHandle(app)
|
||||
|
||||
app.use(language)
|
||||
app.mount('#app')
|
||||
@@ -1,13 +0,0 @@
|
||||
// ANSI 转义码生成网站 https://patorjk.com/software/taag/#p=display&f=Big&t=ABB%0A
|
||||
const asciiArt = `
|
||||
\x1b[32m欢迎使用 Art Design Pro!
|
||||
\x1b[0m
|
||||
\x1b[36m哇!你居然在用我的项目~ 好用的话别忘了去 GitHub 点个 ★Star 呀,你的支持就是我更新的超强动力!祝使用体验满分💯
|
||||
\x1b[0m
|
||||
\x1b[33mGitHub: https://github.com/Daymychen/art-design-pro
|
||||
\x1b[0m
|
||||
\x1b[31m技术支持(QQ群): 1038930070,和开发者一起交流~ 群里有小伙伴实时答疑,遇到问题不用慌!
|
||||
\x1b[0m
|
||||
`
|
||||
|
||||
console.log(asciiArt)
|
||||
@@ -11,21 +11,13 @@
|
||||
<p class="sub-title">{{ $t('forgetPassword.subTitle') }}</p>
|
||||
<div class="mt-5">
|
||||
<span class="input-label" v-if="showInputLabel">账号</span>
|
||||
<ElInput
|
||||
class="custom-height"
|
||||
:placeholder="$t('forgetPassword.placeholder')"
|
||||
v-model.trim="username"
|
||||
/>
|
||||
<ElInput class="custom-height" :placeholder="$t('forgetPassword.placeholder')"
|
||||
v-model.trim="username" />
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 15px">
|
||||
<ElButton
|
||||
class="w-full custom-height"
|
||||
type="primary"
|
||||
@click="register"
|
||||
:loading="loading"
|
||||
v-ripple
|
||||
>
|
||||
<ElButton class="w-full custom-height" type="primary" @click="register" :loading="loading"
|
||||
v-ripple>
|
||||
{{ $t('forgetPassword.submitBtnText') }}
|
||||
</ElButton>
|
||||
</div>
|
||||
@@ -41,22 +33,22 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineOptions({ name: 'ForgetPassword' })
|
||||
<script setup>
|
||||
defineOptions({ name: 'ForgetPassword' })
|
||||
|
||||
const router = useRouter()
|
||||
const showInputLabel = ref(false)
|
||||
const router = useRouter()
|
||||
const showInputLabel = ref(false)
|
||||
|
||||
const username = ref('')
|
||||
const loading = ref(false)
|
||||
const username = ref('')
|
||||
const loading = ref(false)
|
||||
|
||||
const register = async () => {}
|
||||
const register = async () => { }
|
||||
|
||||
const toLogin = () => {
|
||||
router.push({ name: 'Login' })
|
||||
}
|
||||
const toLogin = () => {
|
||||
router.push({ name: 'Login' })
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@import '../login/style.css';
|
||||
@import '../login/style.css';
|
||||
</style>
|
||||
|
||||
@@ -10,65 +10,36 @@
|
||||
<div class="form">
|
||||
<h3 class="title">{{ $t('login.title') }}</h3>
|
||||
<p class="sub-title">{{ $t('login.subTitle') }}</p>
|
||||
<ElForm
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="rules"
|
||||
:key="formKey"
|
||||
@keyup.enter="handleSubmit"
|
||||
style="margin-top: 25px"
|
||||
>
|
||||
<ElForm ref="formRef" :model="formData" :rules="rules" :key="formKey" @keyup.enter="handleSubmit"
|
||||
style="margin-top: 25px">
|
||||
<ElFormItem prop="account">
|
||||
<ElSelect v-model="formData.account" @change="setupAccount">
|
||||
<ElOption
|
||||
v-for="account in accounts"
|
||||
:key="account.key"
|
||||
:label="account.label"
|
||||
:value="account.key"
|
||||
>
|
||||
<ElOption v-for="account in accounts" :key="account.key" :label="account.label"
|
||||
:value="account.key">
|
||||
<span>{{ account.label }}</span>
|
||||
</ElOption>
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
<ElFormItem prop="username">
|
||||
<ElInput
|
||||
class="custom-height"
|
||||
:placeholder="$t('login.placeholder.username')"
|
||||
v-model.trim="formData.username"
|
||||
/>
|
||||
<ElInput class="custom-height" :placeholder="$t('login.placeholder.username')"
|
||||
v-model.trim="formData.username" />
|
||||
</ElFormItem>
|
||||
<ElFormItem prop="password">
|
||||
<ElInput
|
||||
class="custom-height"
|
||||
:placeholder="$t('login.placeholder.password')"
|
||||
v-model.trim="formData.password"
|
||||
type="password"
|
||||
autocomplete="off"
|
||||
show-password
|
||||
/>
|
||||
<ElInput class="custom-height" :placeholder="$t('login.placeholder.password')"
|
||||
v-model.trim="formData.password" type="password" autocomplete="off" show-password />
|
||||
</ElFormItem>
|
||||
|
||||
<!-- 推拽验证 -->
|
||||
<div class="relative pb-5 mt-6">
|
||||
<div
|
||||
class="relative z-[2] overflow-hidden select-none rounded-lg border border-transparent tad-300"
|
||||
:class="{ '!border-[#FF4E4F]': !isPassing && isClickPass }"
|
||||
>
|
||||
<ArtDragVerify
|
||||
ref="dragVerify"
|
||||
v-model:value="isPassing"
|
||||
:text="$t('login.sliderText')"
|
||||
textColor="var(--art-gray-700)"
|
||||
:successText="$t('login.sliderSuccessText')"
|
||||
progressBarBg="var(--main-color)"
|
||||
:background="isDark ? '#26272F' : '#F1F1F4'"
|
||||
handlerBg="var(--default-box-color)"
|
||||
/>
|
||||
<div class="relative z-[2] overflow-hidden select-none rounded-lg border border-transparent tad-300"
|
||||
:class="{ '!border-[#FF4E4F]': !isPassing && isClickPass }">
|
||||
<ArtDragVerify ref="dragVerify" v-model:value="isPassing" :text="$t('login.sliderText')"
|
||||
textColor="var(--art-gray-700)" :successText="$t('login.sliderSuccessText')"
|
||||
progressBarBg="var(--main-color)" :background="isDark ? '#26272F' : '#F1F1F4'"
|
||||
handlerBg="var(--default-box-color)" />
|
||||
</div>
|
||||
<p
|
||||
class="absolute top-0 z-[1] px-px mt-2 text-xs text-[#f56c6c] tad-300"
|
||||
:class="{ 'translate-y-10': !isPassing && isClickPass }"
|
||||
>
|
||||
<p class="absolute top-0 z-[1] px-px mt-2 text-xs text-[#f56c6c] tad-300"
|
||||
:class="{ 'translate-y-10': !isPassing && isClickPass }">
|
||||
{{ $t('login.placeholder.slider') }}
|
||||
</p>
|
||||
</div>
|
||||
@@ -83,13 +54,8 @@
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 30px">
|
||||
<ElButton
|
||||
class="w-full custom-height"
|
||||
type="primary"
|
||||
@click="handleSubmit"
|
||||
:loading="loading"
|
||||
v-ripple
|
||||
>
|
||||
<ElButton class="w-full custom-height" type="primary" @click="handleSubmit"
|
||||
:loading="loading" v-ripple>
|
||||
{{ $t('login.btnText') }}
|
||||
</ElButton>
|
||||
</div>
|
||||
@@ -107,178 +73,168 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import AppConfig from '@/config'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { HttpError } from '@/utils/http/error'
|
||||
import { fetchLogin } from '@/api/auth'
|
||||
import { ElNotification, type FormInstance, type FormRules } from 'element-plus'
|
||||
import { useSettingStore } from '@/store/modules/setting'
|
||||
<script setup>
|
||||
import AppConfig from '@/config'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { HttpError } from '@/utils/http/error'
|
||||
import { fetchLogin } from '@/api/auth'
|
||||
import { ElNotification } from 'element-plus'
|
||||
import { useSettingStore } from '@/store/modules/setting'
|
||||
|
||||
defineOptions({ name: 'Login' })
|
||||
defineOptions({ name: 'Login' })
|
||||
|
||||
const settingStore = useSettingStore()
|
||||
const { isDark } = storeToRefs(settingStore)
|
||||
const { t, locale } = useI18n()
|
||||
const formKey = ref(0)
|
||||
const settingStore = useSettingStore()
|
||||
const { isDark } = storeToRefs(settingStore)
|
||||
const { t, locale } = useI18n()
|
||||
const formKey = ref(0)
|
||||
|
||||
// 监听语言切换,重置表单
|
||||
watch(locale, () => {
|
||||
formKey.value++
|
||||
})
|
||||
// 监听语言切换,重置表单
|
||||
watch(locale, () => {
|
||||
formKey.value++
|
||||
})
|
||||
|
||||
type AccountKey = 'super' | 'admin' | 'user'
|
||||
|
||||
export interface Account {
|
||||
key: AccountKey
|
||||
label: string
|
||||
userName: string
|
||||
password: string
|
||||
roles: string[]
|
||||
const accounts = computed(() => [
|
||||
{
|
||||
key: 'super',
|
||||
label: t('login.roles.super'),
|
||||
userName: 'Super',
|
||||
password: '123456',
|
||||
roles: ['R_SUPER']
|
||||
},
|
||||
{
|
||||
key: 'admin',
|
||||
label: t('login.roles.admin'),
|
||||
userName: 'Admin',
|
||||
password: '123456',
|
||||
roles: ['R_ADMIN']
|
||||
},
|
||||
{
|
||||
key: 'user',
|
||||
label: t('login.roles.user'),
|
||||
userName: 'User',
|
||||
password: '123456',
|
||||
roles: ['R_USER']
|
||||
}
|
||||
])
|
||||
|
||||
const accounts = computed<Account[]>(() => [
|
||||
{
|
||||
key: 'super',
|
||||
label: t('login.roles.super'),
|
||||
userName: 'Super',
|
||||
password: '123456',
|
||||
roles: ['R_SUPER']
|
||||
},
|
||||
{
|
||||
key: 'admin',
|
||||
label: t('login.roles.admin'),
|
||||
userName: 'Admin',
|
||||
password: '123456',
|
||||
roles: ['R_ADMIN']
|
||||
},
|
||||
{
|
||||
key: 'user',
|
||||
label: t('login.roles.user'),
|
||||
userName: 'User',
|
||||
password: '123456',
|
||||
roles: ['R_USER']
|
||||
const dragVerify = ref()
|
||||
|
||||
const userStore = useUserStore()
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const isPassing = ref(false)
|
||||
const isClickPass = ref(false)
|
||||
|
||||
const systemName = AppConfig.systemInfo.name
|
||||
const formRef = ref()
|
||||
|
||||
const formData = reactive({
|
||||
account: '',
|
||||
username: '',
|
||||
password: '',
|
||||
rememberPassword: true
|
||||
})
|
||||
|
||||
const rules = computed(() => ({
|
||||
username: [{ required: true, message: t('login.placeholder.username'), trigger: 'blur' }],
|
||||
password: [{ required: true, message: t('login.placeholder.password'), trigger: 'blur' }]
|
||||
}))
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
onMounted(() => {
|
||||
setupAccount('super')
|
||||
})
|
||||
|
||||
// 设置账号
|
||||
const setupAccount = (key) => {
|
||||
const selectedAccount = accounts.value.find((account) => account.key === key)
|
||||
formData.account = key
|
||||
formData.username = selectedAccount?.userName ?? ''
|
||||
formData.password = selectedAccount?.password ?? ''
|
||||
}
|
||||
|
||||
// 登录
|
||||
const handleSubmit = async () => {
|
||||
if (!formRef.value) return
|
||||
|
||||
try {
|
||||
// 表单验证
|
||||
const valid = await formRef.value.validate()
|
||||
if (!valid) return
|
||||
|
||||
// 拖拽验证
|
||||
if (!isPassing.value) {
|
||||
isClickPass.value = true
|
||||
return
|
||||
}
|
||||
])
|
||||
|
||||
const dragVerify = ref()
|
||||
loading.value = true
|
||||
|
||||
const userStore = useUserStore()
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const isPassing = ref(false)
|
||||
const isClickPass = ref(false)
|
||||
// 登录请求
|
||||
const { username, password } = formData
|
||||
|
||||
const systemName = AppConfig.systemInfo.name
|
||||
const formRef = ref<FormInstance>()
|
||||
const { token, refreshToken } = await fetchLogin({
|
||||
userName: username,
|
||||
password
|
||||
})
|
||||
|
||||
const formData = reactive({
|
||||
account: '',
|
||||
username: '',
|
||||
password: '',
|
||||
rememberPassword: true
|
||||
})
|
||||
|
||||
const rules = computed<FormRules>(() => ({
|
||||
username: [{ required: true, message: t('login.placeholder.username'), trigger: 'blur' }],
|
||||
password: [{ required: true, message: t('login.placeholder.password'), trigger: 'blur' }]
|
||||
}))
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
onMounted(() => {
|
||||
setupAccount('super')
|
||||
})
|
||||
|
||||
// 设置账号
|
||||
const setupAccount = (key: AccountKey) => {
|
||||
const selectedAccount = accounts.value.find((account: Account) => account.key === key)
|
||||
formData.account = key
|
||||
formData.username = selectedAccount?.userName ?? ''
|
||||
formData.password = selectedAccount?.password ?? ''
|
||||
}
|
||||
|
||||
// 登录
|
||||
const handleSubmit = async () => {
|
||||
if (!formRef.value) return
|
||||
|
||||
try {
|
||||
// 表单验证
|
||||
const valid = await formRef.value.validate()
|
||||
if (!valid) return
|
||||
|
||||
// 拖拽验证
|
||||
if (!isPassing.value) {
|
||||
isClickPass.value = true
|
||||
return
|
||||
}
|
||||
|
||||
loading.value = true
|
||||
|
||||
// 登录请求
|
||||
const { username, password } = formData
|
||||
|
||||
const { token, refreshToken } = await fetchLogin({
|
||||
userName: username,
|
||||
password
|
||||
})
|
||||
|
||||
// 验证token
|
||||
if (!token) {
|
||||
throw new Error('Login failed - no token received')
|
||||
}
|
||||
|
||||
// 存储 token 和登录状态
|
||||
userStore.setToken(token, refreshToken)
|
||||
userStore.setLoginStatus(true)
|
||||
|
||||
// 登录成功处理
|
||||
showLoginSuccessNotice()
|
||||
|
||||
// 获取 redirect 参数,如果存在则跳转到指定页面,否则跳转到首页
|
||||
const redirect = route.query.redirect as string
|
||||
router.push(redirect || '/')
|
||||
} catch (error) {
|
||||
// 处理 HttpError
|
||||
if (error instanceof HttpError) {
|
||||
// console.log(error.code)
|
||||
} else {
|
||||
// 处理非 HttpError
|
||||
// ElMessage.error('登录失败,请稍后重试')
|
||||
console.error('[Login] Unexpected error:', error)
|
||||
}
|
||||
} finally {
|
||||
loading.value = false
|
||||
resetDragVerify()
|
||||
// 验证token
|
||||
if (!token) {
|
||||
throw new Error('Login failed - no token received')
|
||||
}
|
||||
}
|
||||
|
||||
// 重置拖拽验证
|
||||
const resetDragVerify = () => {
|
||||
dragVerify.value.reset()
|
||||
}
|
||||
// 存储 token 和登录状态
|
||||
userStore.setToken(token, refreshToken)
|
||||
userStore.setLoginStatus(true)
|
||||
|
||||
// 登录成功提示
|
||||
const showLoginSuccessNotice = () => {
|
||||
setTimeout(() => {
|
||||
ElNotification({
|
||||
title: t('login.success.title'),
|
||||
type: 'success',
|
||||
duration: 2500,
|
||||
zIndex: 10000,
|
||||
message: `${t('login.success.message')}, ${systemName}!`
|
||||
})
|
||||
}, 1000)
|
||||
// 登录成功处理
|
||||
showLoginSuccessNotice()
|
||||
|
||||
// 获取 redirect 参数,如果存在则跳转到指定页面,否则跳转到首页
|
||||
const redirect = route.query.redirect
|
||||
router.push(redirect || '/')
|
||||
} catch (error) {
|
||||
// 处理 HttpError
|
||||
if (error instanceof HttpError) {
|
||||
// console.log(error.code)
|
||||
} else {
|
||||
// 处理非 HttpError
|
||||
// ElMessage.error('登录失败,请稍后重试')
|
||||
console.error('[Login] Unexpected error:', error)
|
||||
}
|
||||
} finally {
|
||||
loading.value = false
|
||||
resetDragVerify()
|
||||
}
|
||||
}
|
||||
|
||||
// 重置拖拽验证
|
||||
const resetDragVerify = () => {
|
||||
dragVerify.value.reset()
|
||||
}
|
||||
|
||||
// 登录成功提示
|
||||
const showLoginSuccessNotice = () => {
|
||||
setTimeout(() => {
|
||||
ElNotification({
|
||||
title: t('login.success.title'),
|
||||
type: 'success',
|
||||
duration: 2500,
|
||||
zIndex: 10000,
|
||||
message: `${t('login.success.message')}, ${systemName}!`
|
||||
})
|
||||
}, 1000)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@import './style.css';
|
||||
@import './style.css';
|
||||
</style>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.el-select__wrapper) {
|
||||
height: 40px !important;
|
||||
}
|
||||
:deep(.el-select__wrapper) {
|
||||
height: 40px !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -10,64 +10,36 @@
|
||||
<div class="form">
|
||||
<h3 class="title">{{ $t('register.title') }}</h3>
|
||||
<p class="sub-title">{{ $t('register.subTitle') }}</p>
|
||||
<ElForm
|
||||
class="mt-7.5"
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="rules"
|
||||
label-position="top"
|
||||
:key="formKey"
|
||||
>
|
||||
<ElForm class="mt-7.5" ref="formRef" :model="formData" :rules="rules" label-position="top"
|
||||
:key="formKey">
|
||||
<ElFormItem prop="username">
|
||||
<ElInput
|
||||
class="custom-height"
|
||||
v-model.trim="formData.username"
|
||||
:placeholder="$t('register.placeholder.username')"
|
||||
/>
|
||||
<ElInput class="custom-height" v-model.trim="formData.username"
|
||||
:placeholder="$t('register.placeholder.username')" />
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem prop="password">
|
||||
<ElInput
|
||||
class="custom-height"
|
||||
v-model.trim="formData.password"
|
||||
:placeholder="$t('register.placeholder.password')"
|
||||
type="password"
|
||||
autocomplete="off"
|
||||
show-password
|
||||
/>
|
||||
<ElInput class="custom-height" v-model.trim="formData.password"
|
||||
:placeholder="$t('register.placeholder.password')" type="password" autocomplete="off"
|
||||
show-password />
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem prop="confirmPassword">
|
||||
<ElInput
|
||||
class="custom-height"
|
||||
v-model.trim="formData.confirmPassword"
|
||||
:placeholder="$t('register.placeholder.confirmPassword')"
|
||||
type="password"
|
||||
autocomplete="off"
|
||||
@keyup.enter="register"
|
||||
show-password
|
||||
/>
|
||||
<ElInput class="custom-height" v-model.trim="formData.confirmPassword"
|
||||
:placeholder="$t('register.placeholder.confirmPassword')" type="password"
|
||||
autocomplete="off" @keyup.enter="register" show-password />
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem prop="agreement">
|
||||
<ElCheckbox v-model="formData.agreement">
|
||||
{{ $t('register.agreeText') }}
|
||||
<RouterLink
|
||||
style="color: var(--theme-color); text-decoration: none"
|
||||
to="/privacy-policy"
|
||||
>{{ $t('register.privacyPolicy') }}</RouterLink
|
||||
>
|
||||
<RouterLink style="color: var(--theme-color); text-decoration: none"
|
||||
to="/privacy-policy">{{ $t('register.privacyPolicy') }}</RouterLink>
|
||||
</ElCheckbox>
|
||||
</ElFormItem>
|
||||
|
||||
<div style="margin-top: 15px">
|
||||
<ElButton
|
||||
class="w-full custom-height"
|
||||
type="primary"
|
||||
@click="register"
|
||||
:loading="loading"
|
||||
v-ripple
|
||||
>
|
||||
<ElButton class="w-full custom-height" type="primary" @click="register" :loading="loading"
|
||||
v-ripple>
|
||||
{{ $t('register.submitBtnText') }}
|
||||
</ElButton>
|
||||
</div>
|
||||
@@ -85,160 +57,152 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
<script setup>
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
defineOptions({ name: 'Register' })
|
||||
defineOptions({ name: 'Register' })
|
||||
|
||||
interface RegisterForm {
|
||||
username: string
|
||||
password: string
|
||||
confirmPassword: string
|
||||
agreement: boolean
|
||||
const USERNAME_MIN_LENGTH = 3
|
||||
const USERNAME_MAX_LENGTH = 20
|
||||
const PASSWORD_MIN_LENGTH = 6
|
||||
const REDIRECT_DELAY = 1000
|
||||
|
||||
const { t, locale } = useI18n()
|
||||
const router = useRouter()
|
||||
const formRef = ref()
|
||||
|
||||
const loading = ref(false)
|
||||
const formKey = ref(0)
|
||||
|
||||
// 监听语言切换,重置表单
|
||||
watch(locale, () => {
|
||||
formKey.value++
|
||||
})
|
||||
|
||||
const formData = reactive({
|
||||
username: '',
|
||||
password: '',
|
||||
confirmPassword: '',
|
||||
agreement: false
|
||||
})
|
||||
|
||||
/**
|
||||
* 验证密码
|
||||
* 当密码输入后,如果确认密码已填写,则触发确认密码的验证
|
||||
*/
|
||||
const validatePassword = (_rule, value, callback) => {
|
||||
if (!value) {
|
||||
callback(new Error(t('register.placeholder.password')))
|
||||
return
|
||||
}
|
||||
|
||||
const USERNAME_MIN_LENGTH = 3
|
||||
const USERNAME_MAX_LENGTH = 20
|
||||
const PASSWORD_MIN_LENGTH = 6
|
||||
const REDIRECT_DELAY = 1000
|
||||
|
||||
const { t, locale } = useI18n()
|
||||
const router = useRouter()
|
||||
const formRef = ref<FormInstance>()
|
||||
|
||||
const loading = ref(false)
|
||||
const formKey = ref(0)
|
||||
|
||||
// 监听语言切换,重置表单
|
||||
watch(locale, () => {
|
||||
formKey.value++
|
||||
})
|
||||
|
||||
const formData = reactive<RegisterForm>({
|
||||
username: '',
|
||||
password: '',
|
||||
confirmPassword: '',
|
||||
agreement: false
|
||||
})
|
||||
|
||||
/**
|
||||
* 验证密码
|
||||
* 当密码输入后,如果确认密码已填写,则触发确认密码的验证
|
||||
*/
|
||||
const validatePassword = (_rule: any, value: string, callback: (error?: Error) => void) => {
|
||||
if (!value) {
|
||||
callback(new Error(t('register.placeholder.password')))
|
||||
return
|
||||
}
|
||||
|
||||
if (formData.confirmPassword) {
|
||||
formRef.value?.validateField('confirmPassword')
|
||||
}
|
||||
|
||||
callback()
|
||||
if (formData.confirmPassword) {
|
||||
formRef.value?.validateField('confirmPassword')
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证确认密码
|
||||
* 检查确认密码是否与密码一致
|
||||
*/
|
||||
const validateConfirmPassword = (
|
||||
_rule: any,
|
||||
value: string,
|
||||
callback: (error?: Error) => void
|
||||
) => {
|
||||
if (!value) {
|
||||
callback(new Error(t('register.rule.confirmPasswordRequired')))
|
||||
return
|
||||
}
|
||||
callback()
|
||||
}
|
||||
|
||||
if (value !== formData.password) {
|
||||
callback(new Error(t('register.rule.passwordMismatch')))
|
||||
return
|
||||
}
|
||||
|
||||
callback()
|
||||
/**
|
||||
* 验证确认密码
|
||||
* 检查确认密码是否与密码一致
|
||||
*/
|
||||
const validateConfirmPassword = (
|
||||
_rule,
|
||||
value,
|
||||
callback
|
||||
) => {
|
||||
if (!value) {
|
||||
callback(new Error(t('register.rule.confirmPasswordRequired')))
|
||||
return
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证用户协议
|
||||
* 确保用户已勾选同意协议
|
||||
*/
|
||||
const validateAgreement = (_rule: any, value: boolean, callback: (error?: Error) => void) => {
|
||||
if (!value) {
|
||||
callback(new Error(t('register.rule.agreementRequired')))
|
||||
return
|
||||
}
|
||||
callback()
|
||||
if (value !== formData.password) {
|
||||
callback(new Error(t('register.rule.passwordMismatch')))
|
||||
return
|
||||
}
|
||||
|
||||
const rules = computed<FormRules<RegisterForm>>(() => ({
|
||||
username: [
|
||||
{ required: true, message: t('register.placeholder.username'), trigger: 'blur' },
|
||||
{
|
||||
min: USERNAME_MIN_LENGTH,
|
||||
max: USERNAME_MAX_LENGTH,
|
||||
message: t('register.rule.usernameLength'),
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
password: [
|
||||
{ required: true, validator: validatePassword, trigger: 'blur' },
|
||||
{
|
||||
min: PASSWORD_MIN_LENGTH,
|
||||
message: t('register.rule.passwordLength'),
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
confirmPassword: [{ required: true, validator: validateConfirmPassword, trigger: 'blur' }],
|
||||
agreement: [{ validator: validateAgreement, trigger: 'change' }]
|
||||
}))
|
||||
callback()
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册用户
|
||||
* 验证表单后提交注册请求
|
||||
*/
|
||||
const register = async () => {
|
||||
if (!formRef.value) return
|
||||
|
||||
try {
|
||||
await formRef.value.validate()
|
||||
loading.value = true
|
||||
|
||||
// TODO: 替换为真实 API 调用
|
||||
// const params = {
|
||||
// username: formData.username,
|
||||
// password: formData.password
|
||||
// }
|
||||
// const res = await AuthService.register(params)
|
||||
// if (res.code === ApiStatus.success) {
|
||||
// ElMessage.success('注册成功')
|
||||
// toLogin()
|
||||
// }
|
||||
|
||||
// 模拟注册请求
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
ElMessage.success('注册成功')
|
||||
toLogin()
|
||||
}, REDIRECT_DELAY)
|
||||
} catch (error) {
|
||||
console.error('表单验证失败:', error)
|
||||
loading.value = false
|
||||
}
|
||||
/**
|
||||
* 验证用户协议
|
||||
* 确保用户已勾选同意协议
|
||||
*/
|
||||
const validateAgreement = (_rule, value, callback) => {
|
||||
if (!value) {
|
||||
callback(new Error(t('register.rule.agreementRequired')))
|
||||
return
|
||||
}
|
||||
callback()
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转到登录页面
|
||||
*/
|
||||
const toLogin = () => {
|
||||
const rules = computed(() => ({
|
||||
username: [
|
||||
{ required: true, message: t('register.placeholder.username'), trigger: 'blur' },
|
||||
{
|
||||
min: USERNAME_MIN_LENGTH,
|
||||
max: USERNAME_MAX_LENGTH,
|
||||
message: t('register.rule.usernameLength'),
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
password: [
|
||||
{ required: true, validator: validatePassword, trigger: 'blur' },
|
||||
{
|
||||
min: PASSWORD_MIN_LENGTH,
|
||||
message: t('register.rule.passwordLength'),
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
confirmPassword: [{ required: true, validator: validateConfirmPassword, trigger: 'blur' }],
|
||||
agreement: [{ validator: validateAgreement, trigger: 'change' }]
|
||||
}))
|
||||
|
||||
/**
|
||||
* 注册用户
|
||||
* 验证表单后提交注册请求
|
||||
*/
|
||||
const register = async () => {
|
||||
if (!formRef.value) return
|
||||
|
||||
try {
|
||||
await formRef.value.validate()
|
||||
loading.value = true
|
||||
|
||||
// TODO: 替换为真实 API 调用
|
||||
// const params = {
|
||||
// username: formData.username,
|
||||
// password: formData.password
|
||||
// }
|
||||
// const res = await AuthService.register(params)
|
||||
// if (res.code === ApiStatus.success) {
|
||||
// ElMessage.success('注册成功')
|
||||
// toLogin()
|
||||
// }
|
||||
|
||||
// 模拟注册请求
|
||||
setTimeout(() => {
|
||||
router.push({ name: 'Login' })
|
||||
loading.value = false
|
||||
ElMessage.success('注册成功')
|
||||
toLogin()
|
||||
}, REDIRECT_DELAY)
|
||||
} catch (error) {
|
||||
console.error('表单验证失败:', error)
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转到登录页面
|
||||
*/
|
||||
const toLogin = () => {
|
||||
setTimeout(() => {
|
||||
router.push({ name: 'Login' })
|
||||
}, REDIRECT_DELAY)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@import '../login/style.css';
|
||||
@import '../login/style.css';
|
||||
</style>
|
||||
|
||||
@@ -28,14 +28,14 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import CardList from './modules/card-list.vue'
|
||||
import ActiveUser from './modules/active-user.vue'
|
||||
import SalesOverview from './modules/sales-overview.vue'
|
||||
import NewUser from './modules/new-user.vue'
|
||||
import Dynamic from './modules/dynamic-stats.vue'
|
||||
import TodoList from './modules/todo-list.vue'
|
||||
import AboutProject from './modules/about-project.vue'
|
||||
<script setup>
|
||||
import CardList from './modules/card-list.vue'
|
||||
import ActiveUser from './modules/active-user.vue'
|
||||
import SalesOverview from './modules/sales-overview.vue'
|
||||
import NewUser from './modules/new-user.vue'
|
||||
import Dynamic from './modules/dynamic-stats.vue'
|
||||
import TodoList from './modules/todo-list.vue'
|
||||
import AboutProject from './modules/about-project.vue'
|
||||
|
||||
defineOptions({ name: 'Console' })
|
||||
defineOptions({ name: 'Console' })
|
||||
</script>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
<script setup>
|
||||
import AppConfig from '@/config'
|
||||
import { WEB_LINKS } from '@/utils/constants'
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
* 在新标签页中打开指定 URL
|
||||
* @param url 要打开的网页地址
|
||||
*/
|
||||
const goPage = (url: string): void => {
|
||||
const goPage = (url) => {
|
||||
window.open(url, '_blank', 'noopener,noreferrer')
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -24,11 +24,7 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
interface UserStatItem {
|
||||
name: string
|
||||
num: string
|
||||
}
|
||||
<script setup>
|
||||
|
||||
// 最近9个月
|
||||
const xAxisLabels = ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月']
|
||||
@@ -40,7 +36,7 @@
|
||||
* 用户统计数据列表
|
||||
* 包含总用户量、总访问量、日访问量和周同比等关键指标
|
||||
*/
|
||||
const list: UserStatItem[] = [
|
||||
const list = [
|
||||
{ name: '总用户量', num: '32k' },
|
||||
{ name: '总访问量', num: '128k' },
|
||||
{ name: '日访问量', num: '1.2k' },
|
||||
|
||||
@@ -27,21 +27,13 @@
|
||||
</ElRow>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
interface CardDataItem {
|
||||
des: string
|
||||
icon: string
|
||||
startVal: number
|
||||
duration: number
|
||||
num: number
|
||||
change: string
|
||||
}
|
||||
<script setup>
|
||||
|
||||
/**
|
||||
* 卡片统计数据列表
|
||||
* 展示总访问次数、在线访客数、点击量和新用户等核心数据指标
|
||||
*/
|
||||
const dataList = reactive<CardDataItem[]>([
|
||||
const dataList = reactive([
|
||||
{
|
||||
des: '总访问次数',
|
||||
icon: 'ri:pie-chart-line',
|
||||
|
||||
@@ -23,18 +23,12 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
interface DynamicItem {
|
||||
username: string
|
||||
type: string
|
||||
target: string
|
||||
}
|
||||
|
||||
<script setup>
|
||||
/**
|
||||
* 用户动态列表
|
||||
* 记录用户的关注、发文、提问、兑换等各类活动
|
||||
*/
|
||||
const list = reactive<DynamicItem[]>([
|
||||
const list = reactive([
|
||||
{
|
||||
username: '中小鱼',
|
||||
type: '关注了',
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
<script setup>
|
||||
import avatar1 from '@/assets/images/avatar/avatar1.webp'
|
||||
import avatar2 from '@/assets/images/avatar/avatar2.webp'
|
||||
import avatar3 from '@/assets/images/avatar/avatar3.webp'
|
||||
@@ -62,16 +62,6 @@
|
||||
import avatar5 from '@/assets/images/avatar/avatar5.webp'
|
||||
import avatar6 from '@/assets/images/avatar/avatar6.webp'
|
||||
|
||||
interface UserTableItem {
|
||||
username: string
|
||||
province: string
|
||||
sex: 0 | 1
|
||||
age: number
|
||||
percentage: number
|
||||
pro: number
|
||||
color: string
|
||||
avatar: string
|
||||
}
|
||||
|
||||
const ANIMATION_DELAY = 100
|
||||
|
||||
@@ -81,7 +71,7 @@
|
||||
* 新用户表格数据
|
||||
* 包含用户基本信息和完成进度
|
||||
*/
|
||||
const tableData = reactive<UserTableItem[]>([
|
||||
const tableData = reactive([
|
||||
{
|
||||
username: '中小鱼',
|
||||
province: '北京',
|
||||
@@ -148,7 +138,7 @@
|
||||
* 添加进度条动画效果
|
||||
* 延迟后将进度值从 0 更新到目标百分比,触发动画
|
||||
*/
|
||||
const addAnimation = (): void => {
|
||||
const addAnimation = () => {
|
||||
setTimeout(() => {
|
||||
tableData.forEach((item) => {
|
||||
item.pro = item.percentage
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
<script setup>
|
||||
/**
|
||||
* 全年访问量数据
|
||||
* 记录每月的访问量统计
|
||||
|
||||
@@ -25,18 +25,12 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
interface TodoItem {
|
||||
username: string
|
||||
date: string
|
||||
complate: boolean
|
||||
}
|
||||
|
||||
<script setup>
|
||||
/**
|
||||
* 待办事项列表
|
||||
* 记录每日工作任务及完成状态
|
||||
*/
|
||||
const list = reactive<TodoItem[]>([
|
||||
const list = reactive([
|
||||
{
|
||||
username: '查看今天工作内容',
|
||||
date: '上午 09:30',
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
<script setup>
|
||||
import imgUrl from '@imgs/svg/403.svg'
|
||||
defineOptions({ name: 'Exception403' })
|
||||
</script>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
<script setup>
|
||||
import imgUrl from '@imgs/svg/404.svg'
|
||||
defineOptions({ name: 'Exception404' })
|
||||
</script>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
<script setup>
|
||||
import imgUrl from '@imgs/svg/500.svg'
|
||||
defineOptions({ name: 'Exception500' })
|
||||
</script>
|
||||
|
||||
@@ -20,10 +20,6 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineOptions({ name: 'AppLayout' })
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@use './style';
|
||||
</style>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
<script setup>
|
||||
import { IframeRouteManager } from '@/router/core'
|
||||
|
||||
defineOptions({ name: 'IframeView' })
|
||||
@@ -18,7 +18,7 @@
|
||||
const route = useRoute()
|
||||
const isLoading = ref(true)
|
||||
const iframeUrl = ref('')
|
||||
const iframeRef = ref<HTMLIFrameElement | null>(null)
|
||||
const iframeRef = ref(null)
|
||||
|
||||
/**
|
||||
* 初始化 iframe URL
|
||||
@@ -36,7 +36,7 @@
|
||||
* 处理 iframe 加载完成事件
|
||||
* 隐藏加载状态
|
||||
*/
|
||||
const handleIframeLoad = (): void => {
|
||||
const handleIframeLoad = () => {
|
||||
isLoading.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -23,6 +23,6 @@
|
||||
</ArtResultPage>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
<script setup>
|
||||
defineOptions({ name: 'ResultFail' })
|
||||
</script>
|
||||
|
||||
@@ -16,6 +16,6 @@
|
||||
</ArtResultPage>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
<script setup>
|
||||
defineOptions({ name: 'ResultSuccess' })
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user