chore(web): 合并 K22.1 后台收尾
This commit is contained in:
commit
e5c5101450
26 files changed
+44
-618
No files matched your search
@@ -17,4 +17,4 @@ VITE_USE_ALL_ELEMENT_PLUS_STYLE=true
|
|||||||
VITE_USE_MOCK=false
|
VITE_USE_MOCK=false
|
||||||
|
|
||||||
# 是否使用在线图标
|
# 是否使用在线图标
|
||||||
VITE_USE_ONLINE_ICON=true
|
VITE_USE_ONLINE_ICON=false
|
||||||
@@ -29,10 +29,10 @@ VITE_USE_BUNDLE_ANALYZER=false
|
|||||||
VITE_USE_ALL_ELEMENT_PLUS_STYLE=false
|
VITE_USE_ALL_ELEMENT_PLUS_STYLE=false
|
||||||
|
|
||||||
# 是否开启mock
|
# 是否开启mock
|
||||||
VITE_USE_MOCK=true
|
VITE_USE_MOCK=false
|
||||||
|
|
||||||
# 是否切割css
|
# 是否切割css
|
||||||
VITE_USE_CSS_SPLIT=true
|
VITE_USE_CSS_SPLIT=true
|
||||||
|
|
||||||
# 是否使用在线图标
|
# 是否使用在线图标
|
||||||
VITE_USE_ONLINE_ICON=true
|
VITE_USE_ONLINE_ICON=false
|
||||||
@@ -29,10 +29,10 @@ VITE_USE_BUNDLE_ANALYZER=true
|
|||||||
VITE_USE_ALL_ELEMENT_PLUS_STYLE=false
|
VITE_USE_ALL_ELEMENT_PLUS_STYLE=false
|
||||||
|
|
||||||
# 是否开启mock
|
# 是否开启mock
|
||||||
VITE_USE_MOCK=true
|
VITE_USE_MOCK=false
|
||||||
|
|
||||||
# 是否切割css
|
# 是否切割css
|
||||||
VITE_USE_CSS_SPLIT=true
|
VITE_USE_CSS_SPLIT=true
|
||||||
|
|
||||||
# 是否使用在线图标
|
# 是否使用在线图标
|
||||||
VITE_USE_ONLINE_ICON=true
|
VITE_USE_ONLINE_ICON=false
|
||||||
@@ -29,10 +29,10 @@ VITE_USE_BUNDLE_ANALYZER=false
|
|||||||
VITE_USE_ALL_ELEMENT_PLUS_STYLE=false
|
VITE_USE_ALL_ELEMENT_PLUS_STYLE=false
|
||||||
|
|
||||||
# 是否开启mock
|
# 是否开启mock
|
||||||
VITE_USE_MOCK=true
|
VITE_USE_MOCK=false
|
||||||
|
|
||||||
# 是否切割css
|
# 是否切割css
|
||||||
VITE_USE_CSS_SPLIT=false
|
VITE_USE_CSS_SPLIT=false
|
||||||
|
|
||||||
# 是否使用在线图标
|
# 是否使用在线图标
|
||||||
VITE_USE_ONLINE_ICON=true
|
VITE_USE_ONLINE_ICON=false
|
||||||
+8
-25
@@ -2,7 +2,7 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" href="/favicon.ico" />
|
<link rel="icon" type="image/png" href="/brand-mark.png" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title><%= title %></title>
|
<title><%= title %></title>
|
||||||
</head>
|
</head>
|
||||||
@@ -16,7 +16,8 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background: #f0f2f5;
|
color: #f5eee9;
|
||||||
|
background: #100c0a;
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-loading .app-loading-wrap {
|
.app-loading .app-loading-wrap {
|
||||||
@@ -38,8 +39,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.app-loading .app-loading-logo {
|
.app-loading .app-loading-logo {
|
||||||
width: 100px;
|
width: 72px;
|
||||||
margin: 0 auto 15px;
|
margin: 0 auto 15px;
|
||||||
|
border-radius: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-loading .app-loading-item {
|
.app-loading .app-loading-item {
|
||||||
@@ -55,7 +57,7 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border: 4px solid #2d8cf0;
|
border: 4px solid #c4322a;
|
||||||
border-bottom: 0;
|
border-bottom: 0;
|
||||||
border-left-color: transparent;
|
border-left-color: transparent;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
@@ -68,7 +70,7 @@
|
|||||||
left: calc(50% - 20px);
|
left: calc(50% - 20px);
|
||||||
width: 40px;
|
width: 40px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
border: 4px solid #87bdff;
|
border: 4px solid #ff8a72;
|
||||||
border-right: 0;
|
border-right: 0;
|
||||||
border-top-color: transparent;
|
border-top-color: transparent;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
@@ -85,16 +87,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes loader-outter {
|
|
||||||
0% {
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
100% {
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes loader-inner {
|
@keyframes loader-inner {
|
||||||
0% {
|
0% {
|
||||||
transform: rotate(0deg);
|
transform: rotate(0deg);
|
||||||
@@ -105,20 +97,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes loader-inner {
|
|
||||||
0% {
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
100% {
|
|
||||||
transform: rotate(-360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
<div class="app-loading">
|
<div class="app-loading">
|
||||||
<div class="app-loading-wrap">
|
<div class="app-loading-wrap">
|
||||||
<div class="app-loading-title">
|
<div class="app-loading-title">
|
||||||
<img src="/logo.png" class="app-loading-logo" alt="Logo" />
|
<img src="/brand-mark.png" class="app-loading-logo" alt="Logo" />
|
||||||
<div class="app-loading-title"><%= title %></div>
|
<div class="app-loading-title"><%= title %></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="app-loading-item">
|
<div class="app-loading-item">
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
// Author: fruit.com.cn
|
|
||||||
|
|
||||||
import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer'
|
|
||||||
|
|
||||||
const modules = import.meta.glob('./**/*.mock.ts', {
|
|
||||||
import: 'default',
|
|
||||||
eager: true
|
|
||||||
})
|
|
||||||
|
|
||||||
const mockModules: any[] = []
|
|
||||||
Object.keys(modules).forEach(async (key) => {
|
|
||||||
if (key.includes('_')) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
mockModules.push(...(modules[key] as any))
|
|
||||||
})
|
|
||||||
|
|
||||||
export function setupProdMockServer() {
|
|
||||||
createProdMockServer(mockModules)
|
|
||||||
}
|
|
||||||
@@ -1,131 +0,0 @@
|
|||||||
// Author: fruit.com.cn
|
|
||||||
|
|
||||||
import { MockMethod } from 'vite-plugin-mock'
|
|
||||||
import { SUCCESS_CODE } from '@/constants'
|
|
||||||
|
|
||||||
const timeout = 1000
|
|
||||||
|
|
||||||
const adminList = [
|
|
||||||
{
|
|
||||||
path: '/dashboard',
|
|
||||||
component: '#',
|
|
||||||
redirect: '/dashboard/index',
|
|
||||||
name: 'Dashboard',
|
|
||||||
meta: {
|
|
||||||
title: '工作台',
|
|
||||||
icon: 'ant-design:dashboard-filled'
|
|
||||||
},
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: 'index',
|
|
||||||
name: 'dashboard',
|
|
||||||
component: 'views/common/DashboardView',
|
|
||||||
meta: {
|
|
||||||
title: '工作台',
|
|
||||||
icon: 'ant-design:dashboard-filled',
|
|
||||||
affix: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/content',
|
|
||||||
component: '#',
|
|
||||||
redirect: '/content/dramas',
|
|
||||||
name: 'Content',
|
|
||||||
meta: {
|
|
||||||
title: '内容管理',
|
|
||||||
icon: 'ep:video-camera',
|
|
||||||
alwaysShow: true
|
|
||||||
},
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: 'dramas',
|
|
||||||
name: 'dramas',
|
|
||||||
component: 'views/contentManagement/DramaManagementView',
|
|
||||||
meta: {
|
|
||||||
title: '短剧管理'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'episodes',
|
|
||||||
name: 'episodes',
|
|
||||||
component: 'views/contentManagement/EpisodeManagementView',
|
|
||||||
meta: {
|
|
||||||
title: '剧集管理'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/system',
|
|
||||||
component: '#',
|
|
||||||
redirect: '/system/access',
|
|
||||||
name: 'System',
|
|
||||||
meta: {
|
|
||||||
title: '后台管理',
|
|
||||||
icon: 'ep:setting',
|
|
||||||
alwaysShow: true
|
|
||||||
},
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: 'access',
|
|
||||||
name: 'access',
|
|
||||||
component: 'views/permission/RoleManagementView',
|
|
||||||
meta: {
|
|
||||||
title: '角色权限配置'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'admin-users',
|
|
||||||
name: 'admin-users',
|
|
||||||
component: 'views/userManagement/AdminUsersView',
|
|
||||||
meta: {
|
|
||||||
title: '管理员用户列表'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'menus',
|
|
||||||
name: 'menus',
|
|
||||||
component: 'views/permission/MenuManagementView',
|
|
||||||
meta: {
|
|
||||||
title: '菜单管理'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'role-categories',
|
|
||||||
name: 'role-categories',
|
|
||||||
component: 'views/permission/RoleCategoriesView',
|
|
||||||
meta: {
|
|
||||||
title: '角色分类'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'settings',
|
|
||||||
name: 'settings',
|
|
||||||
component: 'views/systemManagement/SettingsView',
|
|
||||||
meta: {
|
|
||||||
title: '系统设置'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
const testList = adminList.filter((route) => route.path !== '/system')
|
|
||||||
|
|
||||||
export default [
|
|
||||||
// 列表接口
|
|
||||||
{
|
|
||||||
url: '/mock/role/list',
|
|
||||||
method: 'get',
|
|
||||||
timeout,
|
|
||||||
response: ({ query }) => {
|
|
||||||
const { roleName } = query
|
|
||||||
return {
|
|
||||||
code: SUCCESS_CODE,
|
|
||||||
data: roleName === 'admin' ? adminList : testList
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
] as MockMethod[]
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
// Author: fruit.com.cn
|
|
||||||
|
|
||||||
import { SUCCESS_CODE } from '@/constants'
|
|
||||||
|
|
||||||
const timeout = 1000
|
|
||||||
|
|
||||||
const List: {
|
|
||||||
username: string
|
|
||||||
password: string
|
|
||||||
role: string
|
|
||||||
roleId: string
|
|
||||||
permissions: string | string[]
|
|
||||||
}[] = [
|
|
||||||
{
|
|
||||||
username: 'admin',
|
|
||||||
password: 'admin123',
|
|
||||||
role: 'admin',
|
|
||||||
roleId: '1',
|
|
||||||
permissions: ['*.*.*']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
username: 'test',
|
|
||||||
password: 'test',
|
|
||||||
role: 'test',
|
|
||||||
roleId: '2',
|
|
||||||
permissions: ['example:dialog:create', 'example:dialog:delete']
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
export default [
|
|
||||||
// 列表接口
|
|
||||||
{
|
|
||||||
url: '/mock/user/list',
|
|
||||||
method: 'get',
|
|
||||||
response: ({ query }) => {
|
|
||||||
const { username, pageIndex, pageSize } = query
|
|
||||||
|
|
||||||
const mockList = List.filter((item) => {
|
|
||||||
if (username && item.username.indexOf(username) < 0) return false
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
const pageList = mockList.filter(
|
|
||||||
(_, index) => index < pageSize * pageIndex && index >= pageSize * (pageIndex - 1)
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
|
||||||
code: SUCCESS_CODE,
|
|
||||||
data: {
|
|
||||||
total: mockList.length,
|
|
||||||
list: pageList
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 登录接口
|
|
||||||
{
|
|
||||||
url: '/mock/user/login',
|
|
||||||
method: 'post',
|
|
||||||
timeout,
|
|
||||||
response: ({ body }) => {
|
|
||||||
const data = body
|
|
||||||
let hasUser = false
|
|
||||||
for (const user of List) {
|
|
||||||
if (user.username === data.username && user.password === data.password) {
|
|
||||||
hasUser = true
|
|
||||||
return {
|
|
||||||
code: SUCCESS_CODE,
|
|
||||||
data: user
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!hasUser) {
|
|
||||||
return {
|
|
||||||
code: 500,
|
|
||||||
message: '账号或密码错误'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 退出接口
|
|
||||||
{
|
|
||||||
url: '/mock/user/loginOut',
|
|
||||||
method: 'get',
|
|
||||||
timeout,
|
|
||||||
response: () => {
|
|
||||||
return {
|
|
||||||
code: SUCCESS_CODE,
|
|
||||||
data: null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@@ -24,8 +24,6 @@
|
|||||||
"prepare": "husky install"
|
"prepare": "husky install"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@element-plus/icons-vue": "^2.3.2",
|
|
||||||
"@faker-js/faker": "^8.4.0",
|
|
||||||
"@iconify/iconify": "^3.1.1",
|
"@iconify/iconify": "^3.1.1",
|
||||||
"@iconify/vue": "^4.1.1",
|
"@iconify/vue": "^4.1.1",
|
||||||
"@vueuse/core": "^10.7.2",
|
"@vueuse/core": "^10.7.2",
|
||||||
@@ -103,7 +101,6 @@
|
|||||||
"vite": "5.0.12",
|
"vite": "5.0.12",
|
||||||
"vite-plugin-ejs": "^1.7.0",
|
"vite-plugin-ejs": "^1.7.0",
|
||||||
"vite-plugin-eslint": "^1.8.1",
|
"vite-plugin-eslint": "^1.8.1",
|
||||||
"vite-plugin-mock": "2.9.6",
|
|
||||||
"vite-plugin-progress": "^0.0.7",
|
"vite-plugin-progress": "^0.0.7",
|
||||||
"vite-plugin-purge-icons": "^0.10.0",
|
"vite-plugin-purge-icons": "^0.10.0",
|
||||||
"vite-plugin-style-import": "2.0.0",
|
"vite-plugin-style-import": "2.0.0",
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 5.9 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 4.2 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 9.0 KiB |
@@ -26,10 +26,6 @@ export const getAdminRoleApi = (
|
|||||||
return request.get({ url: '/admin/auth/menus', params })
|
return request.get({ url: '/admin/auth/menus', params })
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getTestRoleApi = (params: RoleParams): Promise<IResponse<string[]>> => {
|
|
||||||
return request.get({ url: '/mock/role/list2', params })
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 自助改密(首登强制改密也走这个接口) */
|
/** 自助改密(首登强制改密也走这个接口) */
|
||||||
export const changePasswordApi = (data: { oldPassword: string; newPassword: string }) => {
|
export const changePasswordApi = (data: { oldPassword: string; newPassword: string }) => {
|
||||||
return request.post<void>({ url: '/admin/auth/password', data })
|
return request.post<void>({ url: '/admin/auth/password', data })
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 6.1 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 9.0 KiB |
@@ -68,7 +68,12 @@ watch(
|
|||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src="@/assets/imgs/brand-mark.png"
|
src="@/assets/imgs/brand-mark.png"
|
||||||
class="w-[var(--brand-mark-size)] h-[var(--brand-mark-size)] rounded-[var(--brand-mark-radius)] object-cover"
|
class="object-cover"
|
||||||
|
style="
|
||||||
|
width: var(--brand-mark-size);
|
||||||
|
height: var(--brand-mark-size);
|
||||||
|
border-radius: var(--brand-mark-radius);
|
||||||
|
"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
v-if="show"
|
v-if="show"
|
||||||
|
|||||||
+1
-12
@@ -105,25 +105,14 @@ export default {
|
|||||||
login: {
|
login: {
|
||||||
welcome: 'Short-drama workspace',
|
welcome: 'Short-drama workspace',
|
||||||
consoleLabel: 'Content Operations',
|
consoleLabel: 'Content Operations',
|
||||||
message: 'Manage dramas, episodes, publishing, and access.',
|
|
||||||
hint: 'Sign in with an admin account',
|
hint: 'Sign in with an admin account',
|
||||||
noAccount: 'No account yet?',
|
|
||||||
pointDrama: 'Dramas and episodes',
|
|
||||||
pointPublish: 'Publishing',
|
|
||||||
pointAccess: 'Access',
|
|
||||||
username: 'Username',
|
username: 'Username',
|
||||||
password: 'Password',
|
password: 'Password',
|
||||||
register: 'Register',
|
|
||||||
checkPassword: 'Confirm password',
|
|
||||||
login: 'Sign in',
|
login: 'Sign in',
|
||||||
otherLogin: 'Sign in with',
|
|
||||||
remember: 'Remember me',
|
remember: 'Remember me',
|
||||||
hasUser: 'Existing account? Go to login',
|
|
||||||
forgetPassword: 'Forget password',
|
forgetPassword: 'Forget password',
|
||||||
usernamePlaceholder: 'Please input username',
|
usernamePlaceholder: 'Please input username',
|
||||||
passwordPlaceholder: 'Please input password',
|
passwordPlaceholder: 'Please input password'
|
||||||
code: 'Verification code',
|
|
||||||
codePlaceholder: 'Please input verification code'
|
|
||||||
},
|
},
|
||||||
router: {
|
router: {
|
||||||
login: 'Login',
|
login: 'Login',
|
||||||
|
|||||||
+1
-12
@@ -105,25 +105,14 @@ export default {
|
|||||||
login: {
|
login: {
|
||||||
welcome: '短剧内容管理',
|
welcome: '短剧内容管理',
|
||||||
consoleLabel: '内容运营后台',
|
consoleLabel: '内容运营后台',
|
||||||
message: '管理短剧、剧集、发布状态和后台权限。',
|
|
||||||
hint: '使用管理员账号进入工作台',
|
hint: '使用管理员账号进入工作台',
|
||||||
noAccount: '还没有账号?',
|
|
||||||
pointDrama: '短剧与剧集',
|
|
||||||
pointPublish: '发布状态',
|
|
||||||
pointAccess: '角色权限',
|
|
||||||
username: '用户名',
|
username: '用户名',
|
||||||
password: '密码',
|
password: '密码',
|
||||||
register: '注册',
|
|
||||||
checkPassword: '确认密码',
|
|
||||||
login: '登录',
|
login: '登录',
|
||||||
otherLogin: '其它登录方式',
|
|
||||||
remember: '记住我',
|
remember: '记住我',
|
||||||
hasUser: '已有账号?去登录',
|
|
||||||
forgetPassword: '忘记密码',
|
forgetPassword: '忘记密码',
|
||||||
usernamePlaceholder: '请输入用户名',
|
usernamePlaceholder: '请输入用户名',
|
||||||
passwordPlaceholder: '请输入密码',
|
passwordPlaceholder: '请输入密码'
|
||||||
code: '验证码',
|
|
||||||
codePlaceholder: '请输入验证码'
|
|
||||||
},
|
},
|
||||||
router: {
|
router: {
|
||||||
login: '登录',
|
login: '登录',
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
<!-- Author: fruit.com.cn -->
|
<!-- Author: fruit.com.cn -->
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { LoginForm, RegisterForm } from './components'
|
import { LoginForm } from './components'
|
||||||
import { ThemeSwitch } from '@/components/ThemeSwitch'
|
import { ThemeSwitch } from '@/components/ThemeSwitch'
|
||||||
import { LocaleDropdown } from '@/components/LocaleDropdown'
|
import { LocaleDropdown } from '@/components/LocaleDropdown'
|
||||||
import { useI18n } from '@/hooks/web/useI18n'
|
import { useI18n } from '@/hooks/web/useI18n'
|
||||||
import { getCssVar, underlineToHump } from '@/utils'
|
import { getCssVar, underlineToHump } from '@/utils'
|
||||||
import { useAppStore } from '@/store/modules/app'
|
import { useAppStore } from '@/store/modules/app'
|
||||||
import { computed, ref } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { ElScrollbar } from 'element-plus'
|
import { ElScrollbar } from 'element-plus'
|
||||||
|
|
||||||
const appStore = useAppStore()
|
const appStore = useAppStore()
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const isLogin = ref(true)
|
|
||||||
const isDark = computed(() => appStore.getIsDark)
|
const isDark = computed(() => appStore.getIsDark)
|
||||||
const toolColor = computed(() => (isDark.value ? '#e5eaf3' : '#303133'))
|
const toolColor = computed(() => (isDark.value ? '#e5eaf3' : '#303133'))
|
||||||
|
|
||||||
@@ -35,12 +34,6 @@ const themeChange = () => {
|
|||||||
<div class="hero-copy">
|
<div class="hero-copy">
|
||||||
<p class="hero-kicker">{{ t('login.consoleLabel') }}</p>
|
<p class="hero-kicker">{{ t('login.consoleLabel') }}</p>
|
||||||
<h1>{{ t('login.welcome') }}</h1>
|
<h1>{{ t('login.welcome') }}</h1>
|
||||||
<p>{{ t('login.message') }}</p>
|
|
||||||
<ul>
|
|
||||||
<li>{{ t('login.pointDrama') }}</li>
|
|
||||||
<li>{{ t('login.pointPublish') }}</li>
|
|
||||||
<li>{{ t('login.pointAccess') }}</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@@ -50,8 +43,7 @@ const themeChange = () => {
|
|||||||
<LocaleDropdown :color="toolColor" />
|
<LocaleDropdown :color="toolColor" />
|
||||||
</div>
|
</div>
|
||||||
<div class="login-card">
|
<div class="login-card">
|
||||||
<LoginForm v-if="isLogin" @to-register="isLogin = false" />
|
<LoginForm />
|
||||||
<RegisterForm v-else @to-login="isLogin = true" />
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
@@ -130,40 +122,16 @@ const themeChange = () => {
|
|||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 0;
|
|
||||||
font-size: 15px;
|
|
||||||
line-height: 1.7;
|
|
||||||
color: rgb(255 255 255 / 82%);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-kicker {
|
.hero-kicker {
|
||||||
|
margin: 0;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
letter-spacing: 0.08em;
|
letter-spacing: 0.08em;
|
||||||
color: #d9ecff !important;
|
color: #d9ecff !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-copy ul {
|
|
||||||
display: flex;
|
|
||||||
padding: 0;
|
|
||||||
margin: 28px 0 0;
|
|
||||||
list-style: none;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 10px;
|
|
||||||
|
|
||||||
li {
|
|
||||||
padding: 8px 14px;
|
|
||||||
font-size: 13px;
|
|
||||||
color: #fff;
|
|
||||||
background: rgb(255 255 255 / 12%);
|
|
||||||
border: 1px solid rgb(255 255 255 / 18%);
|
|
||||||
border-radius: 999px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-panel {
|
.login-panel {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -228,10 +196,6 @@ const themeChange = () => {
|
|||||||
font-size: 28px;
|
font-size: 28px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-copy ul {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-panel {
|
.login-panel {
|
||||||
padding: 44px 16px 12px;
|
padding: 44px 16px 12px;
|
||||||
}
|
}
|
||||||
@@ -253,10 +217,6 @@ const themeChange = () => {
|
|||||||
padding: 16px 18px 18px;
|
padding: 16px 18px 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-copy p {
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-card {
|
.login-card {
|
||||||
padding: 28px 20px 24px;
|
padding: 28px 20px 24px;
|
||||||
}
|
}
|
||||||
@@ -282,25 +242,6 @@ const themeChange = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-register {
|
|
||||||
display: flex;
|
|
||||||
margin-top: 16px;
|
|
||||||
font-size: 14px;
|
|
||||||
color: var(--el-text-color-secondary);
|
|
||||||
gap: 6px;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
button {
|
|
||||||
padding: 0;
|
|
||||||
font: inherit;
|
|
||||||
color: var(--el-color-primary);
|
|
||||||
cursor: pointer;
|
|
||||||
background: none;
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-card :deep(.el-button--primary) {
|
.login-card :deep(.el-button--primary) {
|
||||||
--el-button-bg-color: var(--app-brand);
|
--el-button-bg-color: var(--app-brand);
|
||||||
--el-button-border-color: var(--app-brand);
|
--el-button-border-color: var(--app-brand);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { Form, FormSchema } from '@/components/Form'
|
|||||||
import { useI18n } from '@/hooks/web/useI18n'
|
import { useI18n } from '@/hooks/web/useI18n'
|
||||||
import { ElCheckbox, ElLink } from 'element-plus'
|
import { ElCheckbox, ElLink } from 'element-plus'
|
||||||
import { useForm } from '@/hooks/web/useForm'
|
import { useForm } from '@/hooks/web/useForm'
|
||||||
import { loginApi, getTestRoleApi, getAdminRoleApi } from '@/api/login'
|
import { loginApi, getAdminRoleApi } from '@/api/login'
|
||||||
import { useAppStore } from '@/store/modules/app'
|
import { useAppStore } from '@/store/modules/app'
|
||||||
import { usePermissionStore } from '@/store/modules/permission'
|
import { usePermissionStore } from '@/store/modules/permission'
|
||||||
import { landingPath } from '@/utils/landing'
|
import { landingPath } from '@/utils/landing'
|
||||||
@@ -19,8 +19,6 @@ import { BaseButton } from '@/components/Button'
|
|||||||
|
|
||||||
const { required } = useValidator()
|
const { required } = useValidator()
|
||||||
|
|
||||||
const emit = defineEmits(['to-register'])
|
|
||||||
|
|
||||||
const appStore = useAppStore()
|
const appStore = useAppStore()
|
||||||
|
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
@@ -113,24 +111,11 @@ const schema = reactive<FormSchema[]>([
|
|||||||
slots: {
|
slots: {
|
||||||
default: () => {
|
default: () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<div class="w-[100%]">
|
||||||
<div class="w-[100%]">
|
<BaseButton loading={loading.value} type="primary" class="w-[100%]" onClick={signIn}>
|
||||||
<BaseButton
|
{t('login.login')}
|
||||||
loading={loading.value}
|
</BaseButton>
|
||||||
type="primary"
|
</div>
|
||||||
class="w-[100%]"
|
|
||||||
onClick={signIn}
|
|
||||||
>
|
|
||||||
{t('login.login')}
|
|
||||||
</BaseButton>
|
|
||||||
</div>
|
|
||||||
<div class="login-register">
|
|
||||||
<span>{t('login.noAccount')}</span>
|
|
||||||
<button type="button" onClick={toRegister}>
|
|
||||||
{t('login.register')}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -218,16 +203,11 @@ const getRole = async () => {
|
|||||||
const params = {
|
const params = {
|
||||||
roleName: formData.username
|
roleName: formData.username
|
||||||
}
|
}
|
||||||
const res =
|
const res = await getAdminRoleApi(params)
|
||||||
appStore.getDynamicRouter && appStore.getServerDynamicRouter
|
|
||||||
? await getAdminRoleApi(params)
|
|
||||||
: await getTestRoleApi(params)
|
|
||||||
if (res) {
|
if (res) {
|
||||||
const routers = res.data || []
|
const routers = res.data || []
|
||||||
userStore.setRoleRouters(routers)
|
userStore.setRoleRouters(routers)
|
||||||
appStore.getDynamicRouter && appStore.getServerDynamicRouter
|
await permissionStore.generateRoutes('server', routers).catch(() => {})
|
||||||
? await permissionStore.generateRoutes('server', routers).catch(() => {})
|
|
||||||
: await permissionStore.generateRoutes('frontEnd', routers).catch(() => {})
|
|
||||||
|
|
||||||
permissionStore.getAddRouters.forEach((route) => {
|
permissionStore.getAddRouters.forEach((route) => {
|
||||||
addRoute(route as RouteRecordRaw) // 动态添加可访问路由表
|
addRoute(route as RouteRecordRaw) // 动态添加可访问路由表
|
||||||
@@ -238,11 +218,6 @@ const getRole = async () => {
|
|||||||
push({ path: redirect.value || landingPath() })
|
push({ path: redirect.value || landingPath() })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 去注册页面
|
|
||||||
const toRegister = () => {
|
|
||||||
emit('to-register')
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -1,172 +0,0 @@
|
|||||||
<!-- Author: fruit.com.cn -->
|
|
||||||
|
|
||||||
<script setup lang="tsx">
|
|
||||||
import { Form, FormSchema } from '@/components/Form'
|
|
||||||
import { reactive, ref } from 'vue'
|
|
||||||
import { useI18n } from '@/hooks/web/useI18n'
|
|
||||||
import { useForm } from '@/hooks/web/useForm'
|
|
||||||
import { ElButton, ElInput, FormRules } from 'element-plus'
|
|
||||||
import { useValidator } from '@/hooks/web/useValidator'
|
|
||||||
|
|
||||||
const emit = defineEmits(['to-login'])
|
|
||||||
|
|
||||||
const { formRegister, formMethods } = useForm()
|
|
||||||
const { getElFormExpose } = formMethods
|
|
||||||
|
|
||||||
const { t } = useI18n()
|
|
||||||
|
|
||||||
const { required } = useValidator()
|
|
||||||
|
|
||||||
const schema = reactive<FormSchema[]>([
|
|
||||||
{
|
|
||||||
field: 'title',
|
|
||||||
colProps: {
|
|
||||||
span: 24
|
|
||||||
},
|
|
||||||
formItemProps: {
|
|
||||||
slots: {
|
|
||||||
default: () => {
|
|
||||||
return (
|
|
||||||
<div class="login-heading">
|
|
||||||
<h2>{t('login.register')}</h2>
|
|
||||||
<p>{t('login.message')}</p>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'username',
|
|
||||||
label: t('login.username'),
|
|
||||||
value: '',
|
|
||||||
component: 'Input',
|
|
||||||
colProps: {
|
|
||||||
span: 24
|
|
||||||
},
|
|
||||||
componentProps: {
|
|
||||||
placeholder: t('login.usernamePlaceholder')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'password',
|
|
||||||
label: t('login.password'),
|
|
||||||
value: '',
|
|
||||||
component: 'InputPassword',
|
|
||||||
colProps: {
|
|
||||||
span: 24
|
|
||||||
},
|
|
||||||
componentProps: {
|
|
||||||
style: {
|
|
||||||
width: '100%'
|
|
||||||
},
|
|
||||||
strength: true,
|
|
||||||
placeholder: t('login.passwordPlaceholder')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'check_password',
|
|
||||||
label: t('login.checkPassword'),
|
|
||||||
value: '',
|
|
||||||
component: 'InputPassword',
|
|
||||||
colProps: {
|
|
||||||
span: 24
|
|
||||||
},
|
|
||||||
componentProps: {
|
|
||||||
style: {
|
|
||||||
width: '100%'
|
|
||||||
},
|
|
||||||
strength: true,
|
|
||||||
placeholder: t('login.passwordPlaceholder')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'code',
|
|
||||||
label: t('login.code'),
|
|
||||||
colProps: {
|
|
||||||
span: 24
|
|
||||||
},
|
|
||||||
formItemProps: {
|
|
||||||
slots: {
|
|
||||||
default: (formData) => {
|
|
||||||
return (
|
|
||||||
<div class="w-[100%] flex">
|
|
||||||
<ElInput v-model={formData.code} placeholder={t('login.codePlaceholder')} />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'register',
|
|
||||||
colProps: {
|
|
||||||
span: 24
|
|
||||||
},
|
|
||||||
formItemProps: {
|
|
||||||
slots: {
|
|
||||||
default: () => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div class="w-[100%]">
|
|
||||||
<ElButton
|
|
||||||
type="primary"
|
|
||||||
class="w-[100%]"
|
|
||||||
loading={loading.value}
|
|
||||||
onClick={loginRegister}
|
|
||||||
>
|
|
||||||
{t('login.register')}
|
|
||||||
</ElButton>
|
|
||||||
</div>
|
|
||||||
<div class="w-[100%] mt-15px">
|
|
||||||
<ElButton class="w-[100%]" onClick={toLogin}>
|
|
||||||
{t('login.hasUser')}
|
|
||||||
</ElButton>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
const rules: FormRules = {
|
|
||||||
username: [required()],
|
|
||||||
password: [required()],
|
|
||||||
check_password: [required()],
|
|
||||||
code: [required()]
|
|
||||||
}
|
|
||||||
|
|
||||||
const toLogin = () => {
|
|
||||||
emit('to-login')
|
|
||||||
}
|
|
||||||
|
|
||||||
const loading = ref(false)
|
|
||||||
|
|
||||||
const loginRegister = async () => {
|
|
||||||
const formRef = await getElFormExpose()
|
|
||||||
formRef?.validate(async (valid) => {
|
|
||||||
if (valid) {
|
|
||||||
try {
|
|
||||||
loading.value = true
|
|
||||||
toLogin()
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<Form
|
|
||||||
:schema="schema"
|
|
||||||
:rules="rules"
|
|
||||||
label-position="top"
|
|
||||||
hide-required-asterisk
|
|
||||||
size="large"
|
|
||||||
class="login-form"
|
|
||||||
@register="formRegister"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
// Author: fruit.com.cn
|
// Author: fruit.com.cn
|
||||||
|
|
||||||
import LoginForm from './LoginForm.vue'
|
import LoginForm from './LoginForm.vue'
|
||||||
import RegisterForm from './RegisterForm.vue'
|
|
||||||
|
|
||||||
export { LoginForm, RegisterForm }
|
export { LoginForm }
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
<!-- Author: fruit.com.cn -->
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { computed } from 'vue'
|
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
const title = computed(() => (route.meta?.title as string) || '菜单页面')
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="page-heading"
|
|
||||||
><div
|
|
||||||
><div class="eyebrow">自定义菜单</div><h1>{{ title }}</h1
|
|
||||||
><p>该菜单已注册,可在菜单管理中把组件路径指向已有页面。</p></div
|
|
||||||
></div
|
|
||||||
>
|
|
||||||
<section class="panel"><el-empty description="菜单已创建,可以继续配置页面内容。" /></section>
|
|
||||||
</template>
|
|
||||||
+1
-1
@@ -31,6 +31,6 @@
|
|||||||
"@types/qrcode"
|
"@types/qrcode"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"include": ["src", "types/**/*.d.ts", "mock/**/*.ts"]
|
"include": ["src", "types/**/*.d.ts"]
|
||||||
// "exclude": ["dist", "node_modules"]
|
// "exclude": ["dist", "node_modules"]
|
||||||
}
|
}
|
||||||
+7
-7
@@ -3,6 +3,7 @@
|
|||||||
import { defineConfig, toEscapedSelector as e, presetUno, presetIcons } from 'unocss'
|
import { defineConfig, toEscapedSelector as e, presetUno, presetIcons } from 'unocss'
|
||||||
import transformerVariantGroup from '@unocss/transformer-variant-group'
|
import transformerVariantGroup from '@unocss/transformer-variant-group'
|
||||||
import { loadEnv } from 'vite'
|
import { loadEnv } from 'vite'
|
||||||
|
import { AppIcons } from './src/constants/icons'
|
||||||
|
|
||||||
const root = process.cwd()
|
const root = process.cwd()
|
||||||
|
|
||||||
@@ -20,13 +21,11 @@ const createPresetIcons = () => {
|
|||||||
} else {
|
} else {
|
||||||
return [
|
return [
|
||||||
presetIcons({
|
presetIcons({
|
||||||
prefix: ''
|
prefix: '',
|
||||||
// 由于默认加载的是所有的图标,启动会非常慢,可以在这里去加载需要的图标,确保启动速度
|
collections: {
|
||||||
// collections: {
|
'material-symbols': () =>
|
||||||
// carbon: () => import('@iconify-json/carbon/icons.json').then(i => i.default),
|
import('@iconify/json/json/material-symbols.json').then((i) => i.default)
|
||||||
// mdi: () => import('@iconify-json/mdi/icons.json').then(i => i.default),
|
}
|
||||||
// logos: () => import('@iconify-json/logos/icons.json').then(i => i.default),
|
|
||||||
// }
|
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -142,6 +141,7 @@ ${selector}:after {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
safelist: Object.values(AppIcons),
|
||||||
presets: [presetUno({ dark: 'class', attributify: false }), ...createPresetIcons()],
|
presets: [presetUno({ dark: 'class', attributify: false }), ...createPresetIcons()],
|
||||||
transformers: [transformerVariantGroup()],
|
transformers: [transformerVariantGroup()],
|
||||||
content: {
|
content: {
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import Vue from '@vitejs/plugin-vue'
|
|||||||
import VueJsx from '@vitejs/plugin-vue-jsx'
|
import VueJsx from '@vitejs/plugin-vue-jsx'
|
||||||
import progress from 'vite-plugin-progress'
|
import progress from 'vite-plugin-progress'
|
||||||
import { ViteEjsPlugin } from "vite-plugin-ejs"
|
import { ViteEjsPlugin } from "vite-plugin-ejs"
|
||||||
import { viteMockServe } from 'vite-plugin-mock'
|
|
||||||
import PurgeIcons from 'vite-plugin-purge-icons'
|
import PurgeIcons from 'vite-plugin-purge-icons'
|
||||||
import VueI18nPlugin from "@intlify/unplugin-vue-i18n/vite"
|
import VueI18nPlugin from "@intlify/unplugin-vue-i18n/vite"
|
||||||
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
|
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
|
||||||
@@ -67,19 +66,6 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
|
|||||||
svgoOptions: true
|
svgoOptions: true
|
||||||
}),
|
}),
|
||||||
PurgeIcons(),
|
PurgeIcons(),
|
||||||
env.VITE_USE_MOCK === 'true'
|
|
||||||
? viteMockServe({
|
|
||||||
ignore: /^\_/,
|
|
||||||
mockPath: 'mock',
|
|
||||||
localEnabled: !isBuild,
|
|
||||||
prodEnabled: isBuild,
|
|
||||||
injectCode: `
|
|
||||||
import { setupProdMockServer } from '../mock/_createProductionServer'
|
|
||||||
|
|
||||||
setupProdMockServer()
|
|
||||||
`
|
|
||||||
})
|
|
||||||
: undefined,
|
|
||||||
ViteEjsPlugin({
|
ViteEjsPlugin({
|
||||||
title: env.VITE_APP_TITLE
|
title: env.VITE_APP_TITLE
|
||||||
}),
|
}),
|
||||||
|
|||||||
Reference in new issue
Block a user