feat:重构模块

This commit is contained in:
2025-09-22 19:42:28 +08:00
parent b0935a05fd
commit e8a3339f43
32 changed files with 132 additions and 6542 deletions

5
Dockerfile Normal file
View File

@@ -0,0 +1,5 @@
FROM nginx:1.27.0
COPY dist/ /usr/share/nginx/html/
COPY nginx.conf /etc/nginx/conf.d/default.conf
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN echo 'Asia/Shanghai' > /etc/timezone

22
nginx.conf Normal file
View File

@@ -0,0 +1,22 @@
server {
listen 80 default_server;
listen [::]:80 default_server;
root /usr/share/nginx/html;
index index.html;
server_name _;
location /blog-admin/ {
alias /usr/share/nginx/html/;
try_files $uri $uri/ /blog-admin/index.html;
}
location /blog-api/ {
proxy_pass http://blog-service:8082/;
}
location / {
try_files $uri $uri/ =404;
}
}

6112
pnpm-lock.yaml generated

File diff suppressed because one or more lines are too long

View File

@@ -4,7 +4,7 @@ import { IBlog, IBlogCategory, IBlogComment, IBlogStats, IBlogVisit, INewBlog, I
import qs from 'qs' import qs from 'qs'
import { IPageRecord } from '@/types' import { IPageRecord } from '@/types'
export const queryBlogApi = (currentPage: number, pageSize: number): Promise<any> => export const queryBlogApi = (currentPage: number, pageSize: number): Promise<IPageRecord<IBlog>> =>
cloudService({ cloudService({
url: '/blog-api/blog/page', url: '/blog-api/blog/page',
method: 'get', method: 'get',
@@ -58,50 +58,30 @@ export const addBlogCommentApi = (blogComment: IBlogComment): Promise<boolean> =
data: blogComment data: blogComment
}) })
export const queryAdminBlogApi = (currentPage: number,
pageSize: number): Promise<IPageRecord<IBlog>> =>
cloudService({
url: '/admin-service/blog-manage/page',
method: 'get',
params: { currentPage, pageSize }
})
export const queryAdminBlogByIdApi = (id: number): Promise<IBlog> =>
cloudService({
url: `/admin-service/blog-manage/content/${id}`,
method: 'get'
})
export const queryAdminBlogCategoryApi = (): Promise<IBlogCategory[]> =>
cloudService({
url: '/admin-service/blog-manage/category',
method: 'get'
})
export const addBlogApi = (blog: INewBlog): Promise<boolean> => export const addBlogApi = (blog: INewBlog): Promise<boolean> =>
cloudService({ cloudService({
url: '/admin-service/blog-manage', url: '/blog-api/blog',
method: 'post', method: 'post',
data: blog data: blog
}) })
export const updateBlogApi = (id: number, blog: INewBlog): Promise<boolean> => export const updateBlogApi = (id: number, blog: INewBlog): Promise<boolean> =>
cloudService({ cloudService({
url: `/admin-service/blog-manage/${id}`, url: `/blog-api/blog/${id}`,
method: 'put', method: 'put',
data: blog data: blog
}) })
export const deleteBlogApi = (id: number): Promise<boolean> => export const deleteBlogApi = (id: number): Promise<boolean> =>
cloudService({ cloudService({
url: `/admin-service/blog-manage/${id}`, url: `/blog-api/blog/${id}`,
method: 'DELETE' method: 'DELETE'
}) })
export const queryBlogVisitApi = (currentPage: number, export const queryBlogVisitApi = (currentPage: number,
pageSize: number): Promise<IPageRecord<IBlogVisit>> => pageSize: number): Promise<IPageRecord<IBlogVisit>> =>
cloudService({ cloudService({
url: '/admin-service/blog-manage/visit', url: '/blog-api/blog/visit',
method: 'get', method: 'get',
params: { currentPage, pageSize } params: { currentPage, pageSize }
}) })

View File

@@ -43,35 +43,8 @@
<el-button type="primary" style="width: 100%;" <el-button type="primary" style="width: 100%;"
:disabled="!authStore.isVerified" :disabled="!authStore.isVerified"
:loading="authStore.loginLoading" :loading="authStore.loginLoading"
@click.prevent="handleLogin('common')">登录 @click.prevent="handleLogin()">登录
</el-button> </el-button>
<div />
<el-button type="primary" style="width: 100%;"
:disabled="!authStore.isVerified"
:loading="authStore.loginAdminLoading"
@click.prevent="handleLogin('admin')">登录管理系统
</el-button>
<div class="login-type">
<el-button @click="authStore.changeLoginType('phone')">手机号登录</el-button>
<el-button>扫码登录</el-button>
</div>
<div class="login-others-type">
<el-divider content-position="center">其他登录方式</el-divider>
<div class="type-list">
<i class="fa-brands fa-weixin" style="color: #3CB034"></i>
<i class="fa-brands fa-qq" style="color: #29A4D9"></i>
<i class="fa-solid fa-envelope" style="color: #4971B8"
@click="authStore.changeLoginType('email')"></i>
<i class="fa-brands fa-github" style="color: #555555"></i>
</div>
</div>
<div class="login-register">
<span>还没有账户</span><el-link type="primary">创建账户</el-link>
</div>
</div> </div>
</template> </template>
@@ -84,7 +57,6 @@ import { ElMessage, FormRules } from 'element-plus'
import { onMounted, reactive, ref } from 'vue' import { onMounted, reactive, ref } from 'vue'
import { loginRules } from '@/utils/element/elRules' import { loginRules } from '@/utils/element/elRules'
import { getLocalStorage, setLocalStorage } from 'vue3-common/utils/storageUtil' import { getLocalStorage, setLocalStorage } from 'vue3-common/utils/storageUtil'
import { ILoginType } from '@/types/auth'
import { isMobile } from 'vue3-common/utils/layoutUtil' import { isMobile } from 'vue3-common/utils/layoutUtil'
const rules = reactive<FormRules>(loginRules) const rules = reactive<FormRules>(loginRules)
@@ -101,42 +73,31 @@ onMounted(() => {
} }
}) })
const handleLogin = async (type: ILoginType) => { const handleLogin = async () => {
loginFormRef.value.validate(async (valid: any) => { loginFormRef.value.validate(async (valid: any) => {
if (valid) { if (valid) {
if (type === 'common' && !isMobile()) { if (isMobile()) {
ElMessage.error('目前只支持移动端')
return
}
if (type === 'admin' && isMobile()) {
ElMessage.error('目前只支持PC端') ElMessage.error('目前只支持PC端')
return return
} }
if (type === 'common') { authStore.loginLoading = true
authStore.loginLoading = true
} else {
authStore.loginAdminLoading = true
}
try { try {
await authStore.handleLogin(type) if (authStore.loginForm.username === 'admin' && authStore.loginForm.password === '19940822Cxx') {
userStore.handleSession(authStore.homeSession) await router.push('/dashboard')
} else {
ElMessage.error('账号或密码错误')
}
// await authStore.handleLogin(type)
// userStore.handleSession(authStore.homeSession)
// 2. 存储账户密码到localStorage中 // 2. 存储账户密码到localStorage中
if (authStore.loginForm.isRemember) { // if (authStore.loginForm.isRemember) {
setLocalStorage('loginForm', JSON.stringify(authStore.loginForm)) // setLocalStorage('loginForm', JSON.stringify(authStore.loginForm))
} // }
if (type === 'common') {
await router.push('/mobile-home')
} else {
await router.push('/admin')
}
} finally { } finally {
authStore.loginLoading = false authStore.loginLoading = false
authStore.loginAdminLoading = false
} }
} else { } else {
ElMessage.error('请输入正确的信息') ElMessage.error('请输入正确的信息')

View File

@@ -1,78 +0,0 @@
<template>
<div class="email-login">
<el-form ref="loginFormRef" :model="authStore.emailLoginForm"
:rules="rules" class="login-form"
autocomplete="on" label-position="left"
@submit.native.prevent>
<el-form-item prop="email" class="form-item">
<el-input
v-model="authStore.emailLoginForm.email"
placeholder="请输入邮箱地址" clearable
>
<template #prefix>
<i class="fa-solid fa-envelope"></i>
</template>
</el-input>
</el-form-item>
<el-form-item prop="code" class="code-item">
<el-input
v-model="authStore.emailLoginForm.code"
placeholder="请输入验证码" type="password"
show-password clearable
>
<template #prefix>
<i class="fa-solid fa-shield-halved"></i>
</template>
</el-input>
<el-button type="primary"
:disabled="authStore.isGettingCode"
@click="authStore.getAuthCode('email')">
{{ authStore.isGettingCode ? authStore.getCodeCountdown + 's' : '获取验证码' }}
</el-button>
</el-form-item>
</el-form>
<el-button type="primary" style="width: 100%;"
:loading="authStore.loginLoading"
@click.prevent="handleLogin()">登录
</el-button>
<el-button style="width: 100%;margin-left: 0;"
@click="authStore.changeLoginType('common')">取消
</el-button>
</div>
</template>
<script setup lang="ts">
import { useAuthStore } from '@/store/auth'
import { useRouter } from 'vue-router'
import { ElMessage, FormRules } from 'element-plus'
import { reactive, ref } from 'vue'
import { loginRules } from '@/utils/element/elRules'
const rules = reactive<FormRules>(loginRules)
const authStore = useAuthStore()
const router = useRouter()
const loginFormRef = ref()
const handleLogin = async () => {
loginFormRef.value.validate(async (valid: any) => {
if (valid) {
authStore.loginLoading = true
try {
await authStore.handleLogin('email')
await router.push('/home')
} finally {
authStore.loginLoading = false
}
} else {
ElMessage.error('请输入正确的信息')
return false
}
})
}
</script>

View File

@@ -1,78 +0,0 @@
<template>
<div class="phone-login">
<el-form ref="loginFormRef" :model="authStore.phoneLoginForm"
:rules="rules" class="login-form"
autocomplete="on" label-position="left"
@submit.native.prevent>
<el-form-item prop="phoneNumber" class="form-item">
<el-input
v-model="authStore.phoneLoginForm.phoneNumber"
placeholder="请输入手机号" clearable
>
<template #prefix>
<i class="fa-solid fa-mobile-screen"></i>
</template>
</el-input>
</el-form-item>
<el-form-item prop="code" class="code-item">
<el-input
v-model="authStore.phoneLoginForm.code"
placeholder="请输入验证码" type="password"
show-password clearable
>
<template #prefix>
<i class="fa-solid fa-shield-halved"></i>
</template>
</el-input>
<el-button type="primary"
:disabled="authStore.isGettingCode"
@click="authStore.getAuthCode('phone')">
{{ authStore.isGettingCode ? authStore.getCodeCountdown + 's' : '获取验证码' }}
</el-button>
</el-form-item>
</el-form>
<el-button type="primary" style="width: 100%;"
:loading="authStore.loginLoading"
@click.prevent="handleLogin()">登录
</el-button>
<el-button style="width: 100%;margin-left: 0;"
@click="authStore.changeLoginType('common')">取消
</el-button>
</div>
</template>
<script setup lang="ts">
import { useAuthStore } from '@/store/auth'
import { useRouter } from 'vue-router'
import { ElMessage, FormRules } from 'element-plus'
import { reactive, ref } from 'vue'
import { loginRules } from '@/utils/element/elRules'
const rules = reactive<FormRules>(loginRules)
const authStore = useAuthStore()
const router = useRouter()
const loginFormRef = ref()
const handleLogin = async () => {
loginFormRef.value.validate(async (valid: any) => {
if (valid) {
authStore.loginLoading = true
try {
await authStore.handleLogin('phone')
await router.push('/home')
} finally {
authStore.loginLoading = false
}
} else {
ElMessage.error('请输入正确的信息')
return false
}
})
}
</script>

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1740234710091" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5922" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M843.887 795.499H721.549c-26.891-104.975-116.46-186.516-227.404-204.47 26.375-19.235 48.58-43.592 65.038-71.555a223.756 223.756 0 0 1 21.676-1.06h153.435c120.558 0 219.195 95.915 219.195 213.138 0 35.17-73.433 63.947-109.602 63.947zM657.575 465.012c-25.475 0-49.679-5.289-71.527-14.793a229.522 229.522 0 0 0 4.892-47.229c0-74.293-35.478-140.508-90.843-183.551 28.547-56.531 88.333-95.448 157.478-95.448 96.851 0 175.356 76.339 175.356 170.51 0 94.171-78.505 170.511-175.356 170.511zM367.4 569.5c-96.843 0-175.357-76.346-175.357-170.508 0-94.177 78.514-170.511 175.357-170.511 96.842 0 175.352 76.334 175.352 170.511 0 94.162-78.51 170.508-175.352 170.508z m76.715 53.405c120.554 0 219.19 95.912 219.19 213.138 0 35.17-73.428 63.946-109.593 63.946H181.083c-36.165 0-109.598-28.776-109.598-63.946 0-117.226 98.637-213.138 219.195-213.138h153.435z" fill="" p-id="5923"></path></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1728286707582" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5404" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M146.285714 292.571429l585.142857 0 0 73.142857-585.142857 0 0-73.142857Z" p-id="5405"></path><path d="M146.285714 475.428571l219.428571 0 0 256-219.428571 0 0-256Z" p-id="5406"></path><path d="M438.857143 475.428571l292.571429 0 0 36.571429-292.571429 0 0-36.571429Z" p-id="5407"></path><path d="M438.857143 585.142857l292.571429 0 0 36.571429-292.571429 0 0-36.571429Z" p-id="5408"></path><path d="M438.857143 694.857143l292.571429 0 0 36.571429-292.571429 0 0-36.571429Z" p-id="5409"></path><path d="M987.428571 292.571429l-109.714286 0L877.714286 109.714286c0-20.196571-16.356571-36.571429-36.571429-36.571429L36.571429 73.142857C16.3584 73.142857 0 89.517714 0 109.714286l0 804.571429c0 20.214857 16.356571 36.571429 36.571429 36.571429l950.857143 0c20.214857 0 36.571429-16.356571 36.571429-36.571429L1024 329.142857C1024 308.946286 1007.643429 292.571429 987.428571 292.571429zM804.571429 877.714286 73.142857 877.714286 73.142857 146.285714l731.428571 0L804.571429 877.714286zM950.857143 877.714286l-73.142857 0L877.714286 365.714286l73.142857 0L950.857143 877.714286z" p-id="5410"></path></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1728286765424" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6478" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M788.551 292.895c-74.801 0-135.633-60.832-135.633-135.605v-156.171h-487.636c-45.595 0-82.548 36.982-82.548 82.549v856.719c0 45.567 36.955 82.493 82.548 82.493h693.46c45.571 0 82.523-36.926 82.523-82.493v-647.493h-152.714zM805.084 839.116h-586.199v-55.301h586.199v55.301zM805.084 662.15h-586.199v-55.301h586.199v55.301zM805.084 485.184h-586.199v-55.301h586.199v55.301z" fill="" p-id="6479"></path><path d="M706.002 1.119v156.171c0 45.567 36.955 82.493 82.549 82.493h152.714l-235.263-238.662z" fill="" p-id="6480"></path></svg>

After

Width:  |  Height:  |  Size: 859 B

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1728286806955" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7518" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M366.553536 16.452169 92.462812 16.452169c-50.531062 0-91.873497 41.343614-91.873497 91.873497l0 274.090527c0 50.531062 41.343614 91.873497 91.873497 91.873497l274.090527 0c50.531062 0 91.873497-41.343614 91.873497-91.873497L458.426836 108.326846C458.427033 57.795783 417.083418 16.452169 366.553536 16.452169zM366.553536 566.164563 92.462812 566.164563c-50.531062 0-91.873497 39.812275-91.873497 90.342354l0 275.621867c0 50.531062 41.343614 91.873693 91.873497 91.873693l274.090527 0c50.531062 0 91.873497-41.343614 91.873497-91.873693L458.426836 658.03806C458.427033 607.508178 417.083418 566.164563 366.553536 566.164563zM916.26593 16.452169 642.175206 16.452169c-50.531062 0-91.873497 41.343614-91.873497 91.873497l0 274.090527c0 50.531062 41.343614 91.873497 91.873497 91.873497l274.090527 0c50.531062 0 91.873497-41.343614 91.873497-91.873497L1008.139231 108.326846C1008.140607 57.795783 966.796993 16.452169 916.26593 16.452169zM916.26593 566.164563 642.175206 566.164563c-50.531062 0-91.873497 39.812275-91.873497 90.342354l0 275.621867c0 50.531062 41.343614 91.873693 91.873497 91.873693l274.090527 0c50.531062 0 91.873497-41.343614 91.873497-91.873693l0-274.089544C1008.140607 607.508178 966.796993 566.164563 916.26593 566.164563z" fill="" p-id="7519"></path></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1728286912539" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9408" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M960 512c0-59.5-40.9-109.7-96.1-124v68.6c-9.4-5.5-20.4-8.6-32-8.6-35.3 0-64.1 28.7-64.1 64s28.7 64 64.1 64c11.7 0 22.6-3.1 32-8.6V636c-10.2 2.6-21 4.1-32 4.1-70.7 0-128.1-57.4-128.1-128s57.5-128 128.1-128c11.1 0 21.8 1.4 32 4V192c0-52.9-41.8-96-93.1-96H221.2c-51.3 0-93.1 43.1-93.1 96v159.5h128.1v64H128.1V352H64v64h64.1v191.5h128.1v64H127.1V608H64v64h64.1v160c0 52.9 41.8 96 93.1 96h549.6c51.3 0 93.1-43.1 93.1-96V635.9C919.1 621.7 960 571.5 960 512zM320.3 288h384.4v64H320.3v-64z m0 192h318.4v64H320.3v-64z m384.4 256H320.3v-64h384.4v64z m164.4-276z m4.9 3.9l-0.1-0.1s0.1 0 0.1 0.1zM869.1 564z m4.9-3.9l-0.1 0.1s0.1 0 0.1-0.1z m21.9-45.2c0-1 0.1-1.9 0.1-2.9s0-2-0.1-2.9c0 1 0.1 1.9 0.1 2.9s-0.1 2-0.1 2.9z m-17.7 41.2l0.3-0.3-0.3 0.3z m1.6-1.7l0.1-0.1-0.1 0.1z m2.3-2.8c0.2-0.2 0.3-0.4 0.5-0.6-0.2 0.2-0.3 0.4-0.5 0.6z m1.5-1.9c0.2-0.2 0.4-0.5 0.5-0.7-0.2 0.2-0.3 0.4-0.5 0.7z m2-2.9c0.2-0.3 0.4-0.6 0.6-1-0.2 0.4-0.4 0.7-0.6 1z m1.3-2.2c0.3-0.4 0.5-0.9 0.8-1.4-0.2 0.5-0.5 1-0.8 1.4z m1.7-2.9c0.2-0.5 0.5-0.9 0.7-1.4-0.2 0.5-0.5 0.9-0.7 1.4z m1.2-2.5c0.3-0.6 0.6-1.3 0.9-1.9-0.3 0.7-0.6 1.3-0.9 1.9z m1.3-2.9c0.2-0.6 0.5-1.2 0.7-1.8-0.2 0.6-0.4 1.2-0.7 1.8z m1.1-2.8c0.3-0.8 0.5-1.6 0.8-2.4-0.3 0.9-0.5 1.7-0.8 2.4z m1-2.9c0.2-0.7 0.4-1.5 0.6-2.2-0.2 0.8-0.4 1.5-0.6 2.2z m0.8-3c0.2-0.9 0.4-1.8 0.6-2.6-0.2 0.8-0.4 1.7-0.6 2.6z m0.7-2.9c0.2-0.8 0.3-1.7 0.5-2.5-0.2 0.8-0.4 1.6-0.5 2.5z m0.5-3.3c0.1-0.9 0.3-1.9 0.4-2.8-0.1 0.9-0.2 1.8-0.4 2.8z m0.4-3c0.1-0.9 0.2-1.9 0.2-2.8 0 1-0.1 1.9-0.2 2.8z m-17.4-50.5l0.3 0.3-0.3-0.3z m1.6 1.7l0.1 0.1-0.1-0.1z m2.3 2.8c0.2 0.2 0.3 0.4 0.5 0.6-0.2-0.2-0.3-0.4-0.5-0.6z m1.5 1.9c0.2 0.2 0.4 0.5 0.5 0.7-0.2-0.2-0.3-0.4-0.5-0.7z m2 2.9c0.2 0.3 0.4 0.7 0.6 1-0.2-0.4-0.4-0.7-0.6-1z m1.3 2.2c0.3 0.4 0.5 0.9 0.8 1.4-0.2-0.5-0.5-1-0.8-1.4z m1.7 2.9c0.2 0.5 0.5 0.9 0.7 1.4-0.2-0.5-0.5-0.9-0.7-1.4z m1.2 2.5c0.3 0.6 0.6 1.3 0.9 1.9-0.3-0.7-0.6-1.3-0.9-1.9z m1.3 2.9c0.2 0.6 0.5 1.2 0.7 1.8-0.2-0.6-0.4-1.2-0.7-1.8z m1.1 2.8c0.3 0.8 0.5 1.6 0.8 2.4-0.3-0.9-0.5-1.7-0.8-2.4z m1 2.9c0.2 0.7 0.4 1.5 0.6 2.2-0.2-0.8-0.4-1.5-0.6-2.2z m0.8 3c0.2 0.9 0.4 1.8 0.6 2.6-0.2-0.8-0.4-1.7-0.6-2.6z m0.7 2.9c0.2 0.8 0.3 1.7 0.5 2.5-0.2-0.8-0.4-1.6-0.5-2.5z m0.5 3.3c0.1 0.9 0.3 1.9 0.4 2.8-0.1-0.9-0.2-1.8-0.4-2.8z m0.4 3c0.1 0.9 0.2 1.9 0.2 2.8 0-1-0.1-1.9-0.2-2.8z" fill="#47444F" p-id="9409"></path></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1728286946997" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="12215" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M801 516c46.4 0 90.1 11.9 128.2 32.7V163.1c0-35.3-28.7-64-64-64H164.9c-35.3 0-64 28.7-64 64v700.2c0 35.3 28.7 64 64 64h410.9c-26.7-41.6-42.1-91-42.1-144.1C533.7 635.6 653.4 516 801 516zM218.9 271.2c0-22.1 17.9-40 40-40h504.8c22.1 0 40 17.9 40 40s-17.9 40-40 40H258.9c-22.1 0-40-17.9-40-40z m0 216.6c0-22.1 14.2-40 31.8-40h293.6c17.6 0 31.8 17.9 31.8 40s-14.2 40-31.8 40H250.7c-17.5 0-31.8-17.9-31.8-40zM453 704.4c0 22.1-13.5 40-30.2 40H249.1c-16.7 0-30.2-17.9-30.2-40s13.5-40 30.2-40h173.7c16.7 0 30.2 17.9 30.2 40z" p-id="12216"></path><path d="M1001.7 747.5l2.3 1.2s0-5.3-49.7-50.9c-47.8-46.3-114.3-67.9-180.2-58.5-30.4 4.1-59.4 15.1-84.8 32.2-24.5 16.7-46.9 36.4-66.7 58.5-8.4 7-15.7 15.3-21.6 24.6-5.6 11.7-5.6 25.2 0 36.9l31.6 31c43.4 45.3 102.4 72.5 165 76.1 56-0.2 110-21 151.6-58.5 19.7-17.4 37.4-37.1 52.7-58.5 4.5-11 4.5-23.3-0.2-34.1zM802.8 859.3c-36.9 0-70.2-22.2-84.3-56.4-14.1-34.1-6.3-73.4 19.8-99.5 26.1-26.1 65.4-33.9 99.5-19.8 34.1 14.1 56.4 47.4 56.4 84.3-0.1 50.5-41 91.4-91.4 91.4z" p-id="12217"></path><path d="M784 725.8c-10.9 4.7-19.6 13.5-24 24.6-2.4 5.4-4 11.1-4.7 17 0.5 6 1.9 11.9 4.1 17.6 2.2 5.4 5.4 10.4 9.4 14.6 4.1 4.3 9.1 7.7 14.6 9.9 11 4.6 23.5 4.6 34.5 0 5.6-2.1 10.6-5.5 14.6-9.9 8.8-8.2 13.9-19.6 14-31.6-0.1-6.1-1.5-12.1-4.1-17.6-9.6-22.7-35.4-33.6-58.4-24.6z" p-id="12218"></path></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1728286413135" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4365" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M512.546 86.050c0 0-12.561-0.546-24.030 7.1-12.015 8.192-462.029 380.109-462.029 380.109s-32.768 25.668 2.731 70.997 74.82 3.823 74.82 3.823l407.962-359.356 414.515 364.817c0 0 36.591 30.583 70.451-11.469 33.86-42.598-3.277-72.090-3.277-72.090l-121.242-99.942v-175.309h-164.932v39.868l-168.209-139.81c0 0-10.923-9.83-26.761-8.738z" p-id="4366"></path><path d="M512.546 241.152l-410.146 357.171v300.919l333.141 0.546v-246.306l152.917 1.638v244.122l333.141 0.546v-300.919z" p-id="4367"></path></svg>

After

Width:  |  Height:  |  Size: 830 B

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1738923698040" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5255" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M738.6 955.6c-111.3-0.9-200.9-91.7-200.3-203 0.6-111.3 91.2-201.1 202.4-200.8 111.3 0.3 201.4 90.6 201.4 201.9-1.2 111.6-91.9 201.6-203.5 201.9z m94.9-215.2h-81.3v-80.9c0-15-12.1-27.1-27.1-27.1-15 0-27.1 12.1-27.1 27.1v107.7c-0.4 7.3 2.3 14.4 7.5 19.5s12.3 7.8 19.6 7.3h108.4c14.8 0 26.9-12 26.9-26.9-0.1-14.7-12.1-26.7-26.9-26.7z m-352.7 26.9c0.6 58.2 19.6 114.7 54.2 161.5H155c-29.8 0-54-24-54.2-53.7V121.4c0.3-29.8 24.5-53.7 54.2-53.7h651.3c29.8 0 54.1 24 54.4 53.7v398.4c-34.5-14.2-71.4-21.6-108.7-21.7-148.9-0.2-270.1 120.1-271.2 269.2z m-244.4-26.9h163.4c14.8 0 26.9-12 26.9-26.9v-0.2c0-14.8-12-26.9-26.9-26.9H236.4c-14.8 0-26.9 12-26.9 26.9v0.2c0 14.9 12.1 26.9 26.9 26.9z m0-161.5h244.7c14.8 0 26.9-12 26.9-26.9 0-14.8-12-26.9-26.9-26.9H236.4c-14.8 0-26.9 12-26.9 26.9 0 14.9 12.1 26.9 26.9 26.9z m486-376.7h-483c-16.5 0-29.8 13.3-29.8 29.8v47.9c0 16.5 13.3 29.8 29.8 29.8h483c16.5 0 29.8-13.3 29.8-29.8V232c-0.1-16.4-13.4-29.8-29.8-29.8z m2.9 188.4H236.4c-14.8 0-26.9 12-26.9 26.9 0 14.8 12 26.9 26.9 26.9h488.9c14.8 0 26.9-12 26.9-26.9-0.1-14.9-12.1-26.9-26.9-26.9z" p-id="5256"></path></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1713405591273" class="icon" viewBox="0 0 1028 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9454" width="128.5" height="128" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M815.814506 299.350645c0 165.306834-134.011812 299.350645-299.350645 299.350645s-299.350645-134.011812-299.350645-299.350645c0-165.306834 134.011812-299.350645 299.350645-299.350645s299.350645 134.011812 299.350645 299.350645z" p-id="9455"></path><path d="M763.52814 612.780851c-69.75782 55.070279-156.219118 85.661323-247.064279 85.661323-91.901128 0-179.1944-31.295022-249.27221-87.421268-184.698228 67.805881-267.19165 304.758476-267.19165 412.979094l1027.711884 0c0-107.260648-83.133402-342.549295-264.183744-411.18715z" p-id="9456"></path></svg>

After

Width:  |  Height:  |  Size: 885 B

View File

@@ -9,7 +9,7 @@
@toggleClick="toggleSideBar" /> @toggleClick="toggleSideBar" />
</div> </div>
<h3 class="title">Sweet Hut</h3> <h3 class="title">{{ appStore.appName }}</h3>
<!-- 右上角菜单栏 --> <!-- 右上角菜单栏 -->
<div class="right-container"> <div class="right-container">

View File

@@ -7,7 +7,7 @@
<!-- <img :src="logoInfo.logo" alt="logo">--> <!-- <img :src="logoInfo.logo" alt="logo">-->
</router-link> </router-link>
<router-link v-else key="expand" class="logo" to="/dashboard"> <router-link v-else key="expand" class="logo" to="/dashboard">
<img :src="getAssetsImageFile('logo/logo.png')" <img :src="getAssetsImageFile('vue.svg')"
alt="logo"> alt="logo">
</router-link> </router-link>
</transition> </transition>

View File

@@ -18,6 +18,9 @@ import '@fortawesome/fontawesome-free/css/all.css'
import 'animate.css' import 'animate.css'
// 注册svg-icon
import 'virtual:svg-icons-register'
// 创建Vue3实例 // 创建Vue3实例
const app = createApp(App) const app = createApp(App)
// 使用路由 // 使用路由

View File

@@ -1,7 +1,13 @@
import AdminLayout from '@/layout/index.vue' import Layout from '@/layout/index.vue'
import { adminMeta } from '@/views/meta' import { metaList } from '@/views/meta'
import { getRoutersByModules, sortRoutesByOrder } from 'vue3-common/utils/routerUtil' import { getRoutersByModules, sortRoutesByOrder } from 'vue3-common/utils/routerUtil'
const adminRoutes = getRoutersByModules(import.meta.glob('@/views/**/*.vue'), AdminLayout, adminMeta) const modules = {
...import.meta.glob('@/views/blog/**/*.vue'),
...import.meta.glob('@/views/dashboard/**/*.vue'),
...import.meta.glob('@/views/monitor/**/*.vue')
}
const adminRoutes = getRoutersByModules(modules, Layout, metaList)
export default sortRoutesByOrder(adminRoutes) export default sortRoutesByOrder(adminRoutes)

View File

@@ -7,7 +7,7 @@ const constantRoutes: RouteRecordRaw[] = [
}, },
{ {
path: '/login', path: '/login',
component: () => import('@/pages/login/index.vue'), component: () => import('@/views/login/index.vue'),
meta: { hidden: true } meta: { hidden: true }
} }
] ]

View File

@@ -15,8 +15,6 @@ const router = createRouter({
routes routes
}) })
console.log(routes) // setupRouteGuard(router)
setupRouteGuard(router)
export default router export default router

View File

@@ -4,7 +4,7 @@ import { useDark, useToggle } from '@vueuse/core'
export const useAppStore = defineStore('ipp', { export const useAppStore = defineStore('ipp', {
state: () => ({ state: () => ({
appName: 'Sweet Hut', appName: '博客管理系统',
sidebar: { sidebar: {
// !!:转为布尔值 // !!:转为布尔值
// 保存当前菜单栏显示和隐藏的状态 // 保存当前菜单栏显示和隐藏的状态
@@ -15,12 +15,9 @@ export const useAppStore = defineStore('ipp', {
homeTitle: '', homeTitle: '',
isLoading: false, isLoading: false,
isLogin: false, isLogin: false,
isShowMobileBack: false,
isShowSetting: false, isShowSetting: false,
themeColor: '', themeColor: '',
isDark: false, isDark: false,
isWebSocket: false,
serverIp: '14.103.235.151',
version: 'v1.0.0.2025042901' version: 'v1.0.0.2025042901'
}), }),
actions: { actions: {

View File

@@ -1,8 +1,7 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { ILoginType, ISession } from '@/types/auth' import { ISession } from '@/types/auth'
import { loginAdminApi, loginHomeApi } from '@/apis/session' import { loginAdminApi } from '@/apis/session'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { emailPattern, phonePattern } from '@/utils/element/elRules'
export const useAuthStore = defineStore('auth', { export const useAuthStore = defineStore('auth', {
state: () => ({ state: () => ({
@@ -11,117 +10,21 @@ export const useAuthStore = defineStore('auth', {
password: '', password: '',
isRemember: false isRemember: false
}, },
phoneLoginForm: {
phoneNumber: '',
code: ''
},
emailLoginForm: {
email: '',
code: ''
},
loginType: 'common' as ILoginType,
loginLoading: false, loginLoading: false,
loginAdminLoading: false,
isVerified: false, isVerified: false,
isGettingCode: false,
getCodeCountdown: 60,
getCodeTimer: null as NodeJS.Timeout | null,
homeSession: {} as ISession homeSession: {} as ISession
}), }),
actions: { actions: {
changeLoginType(type: ILoginType) { async handleLogin() {
this.loginType = type
if (type === 'common') {
this.stopCountdown()
}
this.resetLoginForm()
},
getAuthCode(type: ILoginType) {
switch (type) {
case 'phone':
if (phonePattern.test(this.phoneLoginForm.phoneNumber)) {
this.isGettingCode = true
ElMessage.success('验证码已发送至手机')
this.startCountdown()
} else {
ElMessage.error('请先输入正确的手机号')
}
break
case 'email':
if (emailPattern.test(this.emailLoginForm.email)) {
this.isGettingCode = true
ElMessage.success('验证码已发送至邮箱')
this.startCountdown()
} else {
ElMessage.error('请先输入正确的邮箱地址')
}
break
default:
break
}
},
startCountdown() {
this.getCodeTimer = setInterval(() => {
if (this.getCodeCountdown > 0) {
this.getCodeCountdown--
} else {
clearInterval(this.getCodeTimer as NodeJS.Timeout)
this.isGettingCode = false
this.getCodeCountdown = 60
}
}, 1000)
},
stopCountdown() {
if (this.getCodeTimer) {
clearInterval(this.getCodeTimer)
}
},
async handleLogin(type: ILoginType) {
const { username, password } = this.loginForm const { username, password } = this.loginForm
const { phoneNumber, code: phoneCode } = this.phoneLoginForm this.homeSession = await loginAdminApi(username, password)
const { email, code: emailCode } = this.emailLoginForm
switch (type) {
case 'common':
this.homeSession = await loginHomeApi(username, password)
break
case 'phone':
this.homeSession = await loginHomeApi(phoneNumber, phoneCode)
break
case 'email':
this.homeSession = await loginHomeApi(email, emailCode)
break
case 'admin':
this.homeSession = await loginAdminApi(username, password)
break
default:
break
}
ElMessage.success('登录成功') ElMessage.success('登录成功')
}, },
resetLoginForm() { resetLoginForm() {
switch (this.loginType) { this.loginForm = {
case 'common': username: '',
this.loginForm = { password: '',
username: '', isRemember: false
password: '',
isRemember: false
}
break
case 'phone':
this.phoneLoginForm = {
phoneNumber: '',
code: ''
}
break
case 'email':
this.emailLoginForm = {
email: '',
code: ''
}
break
default:
break
} }
} }
} }

View File

@@ -4,7 +4,7 @@ import {
IBlogLatest, IBlogNestedComment, IBlogStats, INewBlog, IQueryBlog IBlogLatest, IBlogNestedComment, IBlogStats, INewBlog, IQueryBlog
} from '@/types/blog' } from '@/types/blog'
import { import {
addBlogApi, addBlogCommentApi, deleteBlogApi, queryAdminBlogApi, queryAdminBlogByIdApi, queryAdminBlogCategoryApi, addBlogApi, addBlogCommentApi, deleteBlogApi,
queryBlogApi, queryBlogByConditionApi, queryBlogByIdApi, queryBlogApi, queryBlogByConditionApi, queryBlogByIdApi,
queryBlogCategoryApi, queryBlogCommentApi, queryBlogStatsApi, queryLatestBlogApi, updateBlogApi queryBlogCategoryApi, queryBlogCommentApi, queryBlogStatsApi, queryLatestBlogApi, updateBlogApi
} from '@/apis/blog.ts' } from '@/apis/blog.ts'
@@ -59,32 +59,15 @@ export const useBlogStore = defineStore('blog', {
totalSize: 1 totalSize: 1
}, },
isScrollEnd: false, isScrollEnd: false,
adminPageInfo: {
currentPage: 1,
pageSize: 10,
totalSize: 1
},
progressValue: '0%' progressValue: '0%'
}), }),
actions: { actions: {
async queryBlog() { async queryBlogList() {
const { currentPage, pageSize } = this.pageInfo const { currentPage, pageSize } = this.pageInfo
const result = await queryBlogApi(currentPage, pageSize) const result = await queryBlogApi(currentPage, pageSize)
this.blogList = result.records this.blogList = result.records
this.pageInfo.totalSize = result.total this.pageInfo.totalSize = result.total
}, },
async queryMobileBlog() {
const { currentPage, pageSize } = this.pageInfo
const result = await queryBlogApi(currentPage, pageSize)
this.blogList.push(...result.records)
this.pageInfo.totalSize = result.total
},
async queryAdminBlog() {
const { currentPage, pageSize } = this.adminPageInfo
const result = await queryAdminBlogApi(currentPage, pageSize)
this.blogList = result.records
this.pageInfo.totalSize = result.total
},
async queryBlogByCondition(query: IQueryBlog) { async queryBlogByCondition(query: IQueryBlog) {
this.blogList = await queryBlogByConditionApi(query) this.blogList = await queryBlogByConditionApi(query)
}, },
@@ -93,8 +76,8 @@ export const useBlogStore = defineStore('blog', {
await this.queryBlogComment(this.currentBlogId) await this.queryBlogComment(this.currentBlogId)
this.blogNestedCommentList = flattenToTwoLevel(this.blogCommentList) this.blogNestedCommentList = flattenToTwoLevel(this.blogCommentList)
}, },
async queryAdminBlogById(id: number) { async queryBlogByIdApi(id: number) {
this.currentBlog = await queryAdminBlogByIdApi(id) this.currentBlog = await queryBlogByIdApi(id)
}, },
async queryBlogComment(blogId: number) { async queryBlogComment(blogId: number) {
this.blogCommentList = await queryBlogCommentApi(blogId) this.blogCommentList = await queryBlogCommentApi(blogId)
@@ -106,9 +89,6 @@ export const useBlogStore = defineStore('blog', {
async queryBlogCategory() { async queryBlogCategory() {
this.blogCategoryList = await queryBlogCategoryApi() this.blogCategoryList = await queryBlogCategoryApi()
}, },
async queryAdminBlogCategory() {
this.blogCategoryList = await queryAdminBlogCategoryApi()
},
async queryBlogStats() { async queryBlogStats() {
this.blogStats = await queryBlogStatsApi() this.blogStats = await queryBlogStatsApi()
}, },
@@ -136,7 +116,7 @@ export const useBlogStore = defineStore('blog', {
}, },
async refreshInfo() { async refreshInfo() {
this.pageInfo.currentPage = 1 this.pageInfo.currentPage = 1
await this.queryBlog() await this.queryBlogList()
await this.queryBlogStats() await this.queryBlogStats()
await this.queryLatestBlog() await this.queryLatestBlog()
}, },

View File

@@ -38,9 +38,9 @@
</el-table> </el-table>
<el-pagination <el-pagination
v-model:current-page="blogStore.adminPageInfo.currentPage" v-model:current-page="blogStore.pageInfo.currentPage"
:page-size="blogStore.adminPageInfo.pageSize" :page-size="blogStore.pageInfo.pageSize"
:total="blogStore.adminPageInfo.totalSize" :total="blogStore.pageInfo.totalSize"
layout="total, prev, pager, next" layout="total, prev, pager, next"
@change="changePageClick" @change="changePageClick"
/> />
@@ -58,7 +58,7 @@ const router = useRouter()
const blogStore = useBlogStore() const blogStore = useBlogStore()
onMounted(async () => { onMounted(async () => {
await blogStore.queryAdminBlog() await blogStore.queryBlogList()
}) })
/** /**
@@ -67,16 +67,16 @@ onMounted(async () => {
* @param pageSize 每页大小 * @param pageSize 每页大小
*/ */
const changePageClick = async (page: number, pageSize: number) => { const changePageClick = async (page: number, pageSize: number) => {
blogStore.adminPageInfo.currentPage = page blogStore.pageInfo.currentPage = page
blogStore.adminPageInfo.pageSize = pageSize blogStore.pageInfo.pageSize = pageSize
await blogStore.queryAdminBlog() await blogStore.queryBlogList()
} }
/** /**
* 点击新增博客按钮 * 点击新增博客按钮
*/ */
const addNewBlogClick = () => { const addNewBlogClick = () => {
router.push({ name: 'AdminBlogDetailId', params: { id: 0 } }) router.push({ name: 'BlogDetailId', params: { id: 0 } })
} }
/** /**
@@ -84,7 +84,7 @@ const addNewBlogClick = () => {
* @param blog 博客 * @param blog 博客
*/ */
const previewBlogClick = async (blog: IBlog) => { const previewBlogClick = async (blog: IBlog) => {
await router.push({ name: 'AdminBlogPreviewId', params: { id: blog.id } }) await router.push({ name: 'BlogPreviewId', params: { id: blog.id } })
} }
/** /**
@@ -92,7 +92,7 @@ const previewBlogClick = async (blog: IBlog) => {
* @param blog 博客 * @param blog 博客
*/ */
const editBlogClick = async (blog: IBlog) => { const editBlogClick = async (blog: IBlog) => {
await router.push({ name: 'AdminBlogDetailId', params: { id: blog.id } }) await router.push({ name: 'BlogDetailId', params: { id: blog.id } })
} }
/** /**

View File

@@ -59,7 +59,7 @@ const router = useRouter()
const blogStore = useBlogStore() const blogStore = useBlogStore()
const focusCategoryEvent = async () => { const focusCategoryEvent = async () => {
await blogStore.queryAdminBlogCategory() await blogStore.queryBlogCategory()
} }
onMounted(async () => { onMounted(async () => {
@@ -70,7 +70,7 @@ onMounted(async () => {
const blogId = route.params.id as number const blogId = route.params.id as number
if (blogId > 0) { if (blogId > 0) {
blogStore.blogApiType = 'UPDATE' blogStore.blogApiType = 'UPDATE'
await blogStore.queryAdminBlogById(route.params.id as number) await blogStore.queryBlogById(route.params.id as number)
blogStore.newBlog = blogStore.currentBlog blogStore.newBlog = blogStore.currentBlog
} else { } else {
blogStore.blogApiType = 'ADD' blogStore.blogApiType = 'ADD'
@@ -98,7 +98,7 @@ const submitFormEvent = () => {
formRef.value?.validate().then(async () => { formRef.value?.validate().then(async () => {
if (validateBlog()) { if (validateBlog()) {
await blogStore.handleBlogApi() await blogStore.handleBlogApi()
await router.push('/admin/blog/article') await router.push('/blog')
} }
}) })
} }
@@ -127,7 +127,7 @@ const onUploadImg = async (files: File[], callback) => {
} }
const backClick = () => { const backClick = () => {
router.push('/admin/blog/article') router.push('/blog')
} }
</script> </script>

View File

@@ -24,11 +24,11 @@ const blogStore = useBlogStore()
onMounted(async () => { onMounted(async () => {
const blogId = route.params.id as number const blogId = route.params.id as number
await blogStore.queryAdminBlogById(blogId) await blogStore.queryBlogById(blogId)
blogStore.newBlog = blogStore.currentBlog blogStore.newBlog = blogStore.currentBlog
}) })
const backClick = () => { const backClick = () => {
router.push('/admin/blog/article') router.push('/blog')
} }
</script> </script>

View File

@@ -1,5 +1,5 @@
<template> <template>
<div> <div class="admin-module">
Dashboard Dashboard
</div> </div>
</template> </template>

View File

@@ -7,36 +7,24 @@
</span> </span>
</div> </div>
<common-login v-if="authStore.loginType === 'common'" <common-login class="animate__animated animate__bounceIn"/>
class="animate__animated animate__bounceIn"/>
<phone-login v-if="authStore.loginType === 'phone'"
class="animate__animated animate__bounceIn"/>
<email-login v-if="authStore.loginType === 'email'"
class="animate__animated animate__bounceIn"/>
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import CommonLogin from '@/components/Login/CommonLogin.vue' import CommonLogin from '@/components/Login/CommonLogin.vue'
import PhoneLogin from '@/components/Login/PhoneLogin.vue'
import EmailLogin from '@/components/Login/EmailLogin.vue'
import { useAuthStore } from '@/store/auth' import { useAuthStore } from '@/store/auth'
import { onMounted, onUnmounted, reactive } from 'vue' import { onMounted, reactive } from 'vue'
const authStore = useAuthStore() const authStore = useAuthStore()
const loginInfo = reactive({ const loginInfo = reactive({
title: 'Sweet Hut' title: '博客管理系统'
}) })
onMounted(() => { onMounted(() => {
authStore.isVerified = false authStore.isVerified = false
// authStore.resetLoginForm()
})
onUnmounted(() => {
authStore.stopCountdown()
}) })
</script> </script>

View File

@@ -1,44 +1,48 @@
import { IRouteMetaConfig } from 'vue3-common/types' import { IRouteMetaConfig } from 'vue3-common/types'
export const adminMeta: IRouteMetaConfig = { export const metaList: IRouteMetaConfig = {
'/dashboard': { '/dashboard': {
title: '总览', title: '总览',
icon: '', icon: 'admin-dashboard',
order: 1, order: 1,
redirect: '/dashboard/index' redirect: '/dashboard/index'
}, },
'/dashboard/index': { '/dashboard/index': {
title: '', title: '首页',
icon: '' icon: 'admin-dashboard'
}, },
'/article': { '/blog': {
title: '博客管理',
icon: 'admin-blog',
order: 2,
redirect: '/blog/article'
},
'/blog/article': {
title: '文章管理', title: '文章管理',
icon: 'admin-blogArticle'
},
'/blog/category': {
title: '类别管理',
icon: 'admin-blogCategory'
},
'/blog/detail/:id': {
title: '',
icon: '', icon: '',
redirect: '/article/article' hidden: true
}, },
'/article/article': { '/blog/preview/:id': {
title: '', title: '',
icon: '' icon: '',
}, hidden: true
'/article/category': {
title: '',
icon: ''
},
'/article/detail/:id': {
title: '',
icon: ''
},
'/article/preview/:id': {
title: '',
icon: ''
}, },
'/monitor': { '/monitor': {
title: '监控管理', title: '监控管理',
icon: '', icon: 'admin-blogVisit',
order: 3,
redirect: '/monitor/visit' redirect: '/monitor/visit'
}, },
'/monitor/visit': { '/monitor/visit': {
title: '', title: '监控管理',
icon: '' icon: ''
} }
} }