Merge branch 'master' into release

This commit is contained in:
kailong321200875 2023-12-26 11:05:20 +08:00
commit 098bcba4bd
38 changed files with 710 additions and 450 deletions

View File

@ -1,5 +1,5 @@
# 环境
NODE_ENV=development
VITE_NODE_ENV=development
# 接口前缀
VITE_API_BASE_PATH=
@ -9,3 +9,9 @@ VITE_BASE_PATH=/
# 标题
VITE_APP_TITLE=ElementAdmin
# 是否全量引入element-plus样式
VITE_USE_ALL_ELEMENT_PLUS_STYLE=true
# 是否开启mock
VITE_USE_MOCK=true

View File

@ -1,5 +1,5 @@
# 环境
NODE_ENV=production
VITE_NODE_ENV=production
# 接口前缀
VITE_API_BASE_PATH=
@ -21,3 +21,15 @@ VITE_OUT_DIR=dist-dev
# 标题
VITE_APP_TITLE=ElementAdmin
# 是否包分析
VITE_USE_BUNDLE_ANALYZER=false
# 是否全量引入element-plus样式
VITE_USE_ALL_ELEMENT_PLUS_STYLE=false
# 是否开启mock
VITE_USE_MOCK=true
# 是否切割css
VITE_USE_CSS_SPLIT=false

View File

@ -1,5 +1,5 @@
# 环境
NODE_ENV=production
VITE_NODE_ENV=production
# 接口前缀
VITE_API_BASE_PATH=
@ -21,3 +21,15 @@ VITE_OUT_DIR=dist-pro
# 标题
VITE_APP_TITLE=ElementAdmin
# 是否包分析
VITE_USE_BUNDLE_ANALYZER=false
# 是否全量引入element-plus样式
VITE_USE_ALL_ELEMENT_PLUS_STYLE=false
# 是否开启mock
VITE_USE_MOCK=true
# 是否切割css
VITE_USE_CSS_SPLIT=false

View File

@ -1,5 +1,5 @@
# 环境
NODE_ENV=production
VITE_NODE_ENV=production
# 接口前缀
VITE_API_BASE_PATH=
@ -21,3 +21,15 @@ VITE_OUT_DIR=dist-pro
# 标题
VITE_APP_TITLE=ElementAdmin
# 是否包分析
VITE_USE_BUNDLE_ANALYZER=true
# 是否全量引入element-plus样式
VITE_USE_ALL_ELEMENT_PLUS_STYLE=false
# 是否开启mock
VITE_USE_MOCK=true
# 是否切割css
VITE_USE_CSS_SPLIT=false

View File

@ -1,5 +1,5 @@
# 环境
NODE_ENV=production
VITE_NODE_ENV=production
# 接口前缀
VITE_API_BASE_PATH=
@ -21,3 +21,15 @@ VITE_OUT_DIR=dist-test
# 标题
VITE_APP_TITLE=ElementAdmin
# 是否包分析
VITE_USE_BUNDLE_ANALYZER=false
# 是否全量引入element-plus样式
VITE_USE_ALL_ELEMENT_PLUS_STYLE=false
# 是否开启mock
VITE_USE_MOCK=true
# 是否切割css
VITE_USE_CSS_SPLIT=false

2
.gitignore vendored
View File

@ -6,3 +6,5 @@ dist-ssr
/dist*
*-lock.*
pnpm-debug
stats.html

View File

@ -3,7 +3,6 @@
/dist*
/public/*
/docs/*
/vite.config.ts
/src/types/env.d.ts
/docs/**/*
/plop/**/*

View File

@ -50,7 +50,7 @@ Online examples do not apply to menu filtering by default, but directly use Stat
- [Es6+](http://es6.ruanyifeng.com/) - Familiar with es6 basic syntax
- [Vue-Router-Next](https://next.router.vuejs.org/) - Familiar with the basic use of vue-router
- [Element-Plus](https://element-plus.org/) - Familiar with the basic use of element-plus
- [Faker.js](https://github.com/faker-js/faker#readme) - Generate massive amounts of fake contextual data
- [Mock.js](https://github.com/nuysoft/Mock) - mockjs basic syntax
## Install and use

View File

@ -50,7 +50,7 @@ vue-element-plus-admin 的定位是后台集成方案,不太适合当基础模
- [Es6+](http://es6.ruanyifeng.com/) - 熟悉 es6 基本语法
- [Vue-Router-Next](https://next.router.vuejs.org/) - 熟悉 vue-router 基本使用
- [Element-Plus](https://element-plus.org/) - element-plus 基本使用
- [Faker.js](https://github.com/faker-js/faker#readme) - 生成大量伪造的上下文数据
- [Mock.js](https://github.com/nuysoft/Mock) - mockjs 基本语法
## 安装和使用

View File

@ -1,5 +1,5 @@
import { toAnyString } from '@/utils'
import { faker } from '@faker-js/faker'
import Mock from 'mockjs'
import { SUCCESS_CODE } from '@/constants'
const departmentList: any = []
@ -11,64 +11,71 @@ for (let i = 0; i < 5; i++) {
// 部门名称
departmentName: citys[i],
id: toAnyString(),
createTime: faker.date.anytime(),
status: faker.number.int({ min: 0, max: 1 }),
createTime: '@datetime',
// 状态
status: Mock.Random.integer(0, 1),
// 备注
remark: faker.lorem.sentence(),
remark: '@cword(10, 15)',
children: [
{
// 部门名称
departmentName: '研发部',
createTime: faker.date.anytime(),
// 状态
status: faker.number.int({ min: 0, max: 1 }),
id: toAnyString(),
remark: faker.lorem.sentence()
createTime: '@datetime',
// 状态
status: Mock.Random.integer(0, 1),
// 备注
remark: '@cword(10, 15)'
},
{
// 部门名称
departmentName: '产品部',
createTime: faker.date.anytime(),
// 状态
status: faker.number.int({ min: 0, max: 1 }),
id: toAnyString(),
remark: faker.lorem.sentence()
createTime: '@datetime',
// 状态
status: Mock.Random.integer(0, 1),
// 备注
remark: '@cword(10, 15)'
},
{
// 部门名称
departmentName: '运营部',
createTime: faker.date.anytime(),
// 状态
status: faker.number.int({ min: 0, max: 1 }),
id: toAnyString(),
remark: faker.lorem.sentence()
createTime: '@datetime',
// 状态
status: Mock.Random.integer(0, 1),
// 备注
remark: '@cword(10, 15)'
},
{
// 部门名称
departmentName: '市场部',
createTime: faker.date.anytime(),
// 状态
status: faker.number.int({ min: 0, max: 1 }),
id: toAnyString(),
remark: faker.lorem.sentence()
createTime: '@datetime',
// 状态
status: Mock.Random.integer(0, 1),
// 备注
remark: '@cword(10, 15)'
},
{
// 部门名称
departmentName: '销售部',
createTime: faker.date.anytime(),
// 状态
status: faker.number.int({ min: 0, max: 1 }),
id: toAnyString(),
remark: faker.lorem.sentence()
createTime: '@datetime',
// 状态
status: Mock.Random.integer(0, 1),
// 备注
remark: '@cword(10, 15)'
},
{
// 部门名称
departmentName: '客服部',
createTime: faker.date.anytime(),
// 状态
status: faker.number.int({ min: 0, max: 1 }),
id: toAnyString(),
remark: faker.lorem.sentence()
createTime: '@datetime',
// 状态
status: Mock.Random.integer(0, 1),
// 备注
remark: '@cword(10, 15)'
}
]
})
@ -110,18 +117,20 @@ export default [
// 根据pageSize来创建数据
const mockList: any = []
for (let i = 0; i < pageSize; i++) {
mockList.push({
// 用户名
username: faker.person.firstName(),
// 账号
account: faker.person.lastName(),
// 邮箱
email: faker.internet.email(),
// 创建时间
createTime: faker.date.anytime(),
// 用户id
id: toAnyString()
})
mockList.push(
Mock.mock({
// 用户名
username: '@cname',
// 账号
account: '@first',
// 邮箱
email: '@EMAIL',
// 创建时间
createTime: '@datetime',
// 用户id
id: toAnyString()
})
)
}
return {
code: SUCCESS_CODE,

View File

@ -1,4 +1,4 @@
import { faker } from '@faker-js/faker'
import Mock from 'mockjs'
import { SUCCESS_CODE } from '@/constants'
const timeout = 1000
@ -19,7 +19,7 @@ export default [
component: '#',
redirect: '/dashboard/analysis',
name: 'Dashboard',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 1,
type: 0,
parentId: undefined,
@ -34,7 +34,7 @@ export default [
path: 'analysis',
component: 'views/Dashboard/Analysis',
name: 'Analysis',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 2,
type: 1,
parentId: 1,
@ -59,7 +59,7 @@ export default [
path: 'workplace',
component: 'views/Dashboard/Workplace',
name: 'Workplace',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 3,
type: 1,
parentId: 1,
@ -93,7 +93,7 @@ export default [
icon: 'clarity:document-solid'
},
name: 'ExternalLink',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 4,
type: 0,
parentId: undefined,
@ -102,7 +102,7 @@ export default [
{
path: 'https://element-plus-admin-doc.cn/',
name: 'DocumentLink',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 5,
type: 1,
parentId: 4,
@ -118,7 +118,7 @@ export default [
component: '#',
redirect: '/level/menu1/menu1-1/menu1-1-1',
name: 'Level',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 6,
type: 0,
parentId: undefined,
@ -132,7 +132,7 @@ export default [
path: 'menu1',
name: 'Menu1',
component: '##',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 7,
type: 0,
parentId: 6,
@ -146,7 +146,7 @@ export default [
path: 'menu1-1',
name: 'Menu11',
component: '##',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 8,
type: 0,
parentId: 7,
@ -161,7 +161,7 @@ export default [
path: 'menu1-1-1',
name: 'Menu111',
component: 'views/Level/Menu111',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 9,
type: 1,
parentId: 8,
@ -176,7 +176,7 @@ export default [
path: 'menu1-2',
name: 'Menu12',
component: 'views/Level/Menu12',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 10,
type: 1,
parentId: 7,
@ -191,7 +191,7 @@ export default [
path: 'menu2',
name: 'Menu2Demo',
component: 'views/Level/Menu2',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 11,
type: 1,
parentId: 6,
@ -207,7 +207,7 @@ export default [
component: '#',
redirect: '/example/example-dialog',
name: 'Example',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 12,
type: 0,
parentId: undefined,
@ -222,7 +222,7 @@ export default [
path: 'example-dialog',
component: 'views/Example/Dialog/ExampleDialog',
name: 'ExampleDialog',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 13,
type: 1,
parentId: 12,
@ -253,7 +253,7 @@ export default [
path: 'example-page',
component: 'views/Example/Page/ExamplePage',
name: 'ExamplePage',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 14,
type: 1,
parentId: 12,
@ -284,7 +284,7 @@ export default [
path: 'example-add',
component: 'views/Example/Page/ExampleAdd',
name: 'ExampleAdd',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 15,
type: 1,
parentId: 12,
@ -302,7 +302,7 @@ export default [
path: 'example-edit',
component: 'views/Example/Page/ExampleEdit',
name: 'ExampleEdit',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 16,
type: 1,
parentId: 12,
@ -320,7 +320,7 @@ export default [
path: 'example-detail',
component: 'views/Example/Page/ExampleDetail',
name: 'ExampleDetail',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 17,
type: 1,
parentId: 12,

View File

@ -1,4 +1,4 @@
import { faker } from '@faker-js/faker'
import Mock from 'mockjs'
import { SUCCESS_CODE } from '@/constants'
import { toAnyString } from '@/utils'
@ -731,7 +731,7 @@ const menus = [
component: '#',
redirect: '/dashboard/analysis',
name: 'Dashboard',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 1,
meta: {
title: '首页',
@ -743,7 +743,7 @@ const menus = [
path: 'analysis',
component: 'views/Dashboard/Analysis',
name: 'Analysis',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 2,
meta: {
title: '分析页',
@ -754,7 +754,7 @@ const menus = [
path: 'workplace',
component: 'views/Dashboard/Workplace',
name: 'Workplace',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 3,
meta: {
title: '工作台',
@ -771,13 +771,13 @@ const menus = [
icon: 'clarity:document-solid'
},
name: 'ExternalLink',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 4,
children: [
{
path: 'https://element-plus-admin-doc.cn/',
name: 'DocumentLink',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 5,
meta: {
title: '文档'
@ -790,7 +790,7 @@ const menus = [
component: '#',
redirect: '/level/menu1/menu1-1/menu1-1-1',
name: 'Level',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 6,
meta: {
title: '菜单',
@ -801,7 +801,7 @@ const menus = [
path: 'menu1',
name: 'Menu1',
component: '##',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 7,
redirect: '/level/menu1/menu1-1/menu1-1-1',
meta: {
@ -812,7 +812,7 @@ const menus = [
path: 'menu1-1',
name: 'Menu11',
component: '##',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 8,
redirect: '/level/menu1/menu1-1/menu1-1-1',
meta: {
@ -824,7 +824,7 @@ const menus = [
path: 'menu1-1-1',
name: 'Menu111',
component: 'views/Level/Menu111',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 9,
permission: ['edit', 'add', 'delete'],
meta: {
@ -838,7 +838,7 @@ const menus = [
path: 'menu1-2',
name: 'Menu12',
component: 'views/Level/Menu12',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 10,
permission: ['edit', 'add', 'delete'],
meta: {
@ -852,7 +852,7 @@ const menus = [
path: 'menu2',
name: 'Menu2Demo',
component: 'views/Level/Menu2',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 11,
permission: ['edit', 'add', 'delete'],
meta: {
@ -867,7 +867,7 @@ const menus = [
component: '#',
redirect: '/example/example-dialog',
name: 'Example',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 12,
meta: {
title: '综合示例',
@ -879,7 +879,7 @@ const menus = [
path: 'example-dialog',
component: 'views/Example/Dialog/ExampleDialog',
name: 'ExampleDialog',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 13,
permission: ['edit', 'add', 'delete'],
meta: {
@ -891,7 +891,7 @@ const menus = [
path: 'example-page',
component: 'views/Example/Page/ExamplePage',
name: 'ExamplePage',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 14,
permission: ['edit', 'add', 'delete'],
meta: {
@ -903,7 +903,7 @@ const menus = [
path: 'example-add',
component: 'views/Example/Page/ExampleAdd',
name: 'ExampleAdd',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 15,
permission: ['edit', 'add', 'delete'],
meta: {
@ -920,7 +920,7 @@ const menus = [
path: 'example-edit',
component: 'views/Example/Page/ExampleEdit',
name: 'ExampleEdit',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 16,
permission: ['edit', 'add', 'delete'],
meta: {
@ -937,7 +937,7 @@ const menus = [
path: 'example-detail',
component: 'views/Example/Page/ExampleDetail',
name: 'ExampleDetail',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 17,
permission: ['edit', 'add', 'delete'],
meta: {
@ -959,7 +959,7 @@ const menus = [
component: '#',
redirect: '/dashboard/analysis',
name: 'Dashboard',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 1,
meta: {
title: '首页',
@ -971,7 +971,7 @@ const menus = [
path: 'analysis',
component: 'views/Dashboard/Analysis',
name: 'Analysis',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 2,
meta: {
title: '分析页',
@ -982,7 +982,7 @@ const menus = [
path: 'workplace',
component: 'views/Dashboard/Workplace',
name: 'Workplace',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 3,
meta: {
title: '工作台',
@ -1001,13 +1001,13 @@ const menus = [
icon: 'clarity:document-solid'
},
name: 'ExternalLink',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 4,
children: [
{
path: 'https://element-plus-admin-doc.cn/',
name: 'DocumentLink',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 5,
meta: {
title: '文档'
@ -1020,7 +1020,7 @@ const menus = [
component: '#',
redirect: '/level/menu1/menu1-1/menu1-1-1',
name: 'Level',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 6,
meta: {
title: '菜单',
@ -1031,7 +1031,7 @@ const menus = [
path: 'menu1',
name: 'Menu1',
component: '##',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 7,
redirect: '/level/menu1/menu1-1/menu1-1-1',
meta: {
@ -1042,7 +1042,7 @@ const menus = [
path: 'menu1-1',
name: 'Menu11',
component: '##',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 8,
redirect: '/level/menu1/menu1-1/menu1-1-1',
meta: {
@ -1054,7 +1054,7 @@ const menus = [
path: 'menu1-1-1',
name: 'Menu111',
component: 'views/Level/Menu111',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 9,
permission: ['edit', 'add', 'delete'],
meta: {
@ -1068,7 +1068,7 @@ const menus = [
path: 'menu1-2',
name: 'Menu12',
component: 'views/Level/Menu12',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 10,
permission: ['edit', 'add', 'delete'],
meta: {
@ -1082,7 +1082,7 @@ const menus = [
path: 'menu2',
name: 'Menu2Demo',
component: 'views/Level/Menu2',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 11,
permission: ['edit', 'add', 'delete'],
meta: {
@ -1099,7 +1099,7 @@ const menus = [
component: '#',
redirect: '/example/example-dialog',
name: 'Example',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 12,
meta: {
title: '综合示例',
@ -1111,7 +1111,7 @@ const menus = [
path: 'example-detail',
component: 'views/Example/Page/ExampleDetail',
name: 'ExampleDetail',
status: faker.number.int({ min: 0, max: 1 }),
status: Mock.Random.integer(0, 1),
id: 17,
permission: ['edit', 'add', 'delete'],
meta: {
@ -1130,16 +1130,18 @@ const menus = [
]
for (let i = 0; i < 4; i++) {
List.push({
id: toAnyString(),
// timestamp: +Mock.Random.date('T'),
roleName: roleNames[i],
role: faker.lorem.sentence(1),
status: faker.number.int({ min: 0, max: 1 }),
createTime: faker.date.anytime(),
remark: faker.lorem.sentence(),
menu: menus[i]
})
List.push(
Mock.mock({
id: toAnyString(),
// timestamp: +Mock.Random.date('T'),
roleName: roleNames[i],
role: '@first',
status: Mock.Random.integer(0, 1),
createTime: '@datetime',
remark: '@cword(10, 15)',
menu: menus[i]
})
)
}
export default [

View File

@ -1,4 +1,4 @@
import { faker } from '@faker-js/faker'
import Mock from 'mockjs'
import { SUCCESS_CODE } from '@/constants'
import { toAnyString } from '@/utils'
@ -36,144 +36,103 @@ interface TreeListProps {
let List: ListProps[] = []
for (let i = 0; i < count; i++) {
List.push({
id: toAnyString(),
// timestamp: +Mock.Random.date('T'),
author: faker.person.firstName(),
title: faker.lorem.sentence(),
content: baseContent,
importance: faker.number.int({ min: 1, max: 3 }),
display_time: faker.date.anytime(),
pageviews: faker.number.int({ min: 300, max: 5000 }),
image_uri: faker.image.url({
width: faker.number.int({ min: 200, max: 400 }),
height: faker.number.int({ min: 200, max: 400 })
}),
video_uri:
'//sf1-cdn-tos.huoshanstatic.com/obj/media-fe/xgplayer_doc_video/mp4/xgplayer-demo-720p.mp4'
})
List.push(
Mock.mock({
id: toAnyString(),
// timestamp: +Mock.Random.date('T'),
author: '@first',
title: '@title(5, 10)',
content: baseContent,
importance: '@integer(1, 3)',
display_time: '@datetime',
pageviews: '@integer(100, 500)',
image_uri: Mock.Random.image('@integer(100, 500)x@integer(100, 500)'),
video_uri:
'//sf1-cdn-tos.huoshanstatic.com/obj/media-fe/xgplayer_doc_video/mp4/xgplayer-demo-720p.mp4'
})
)
}
const treeList: TreeListProps[] = []
for (let i = 0; i < count; i++) {
treeList.push({
id: toAnyString(),
// timestamp: +Mock.Random.date('T'),
author: faker.person.firstName(),
title: faker.lorem.sentence(),
content: baseContent,
importance: faker.number.int({ min: 1, max: 3 }),
display_time: faker.date.anytime(),
pageviews: faker.number.int({ min: 300, max: 5000 }),
image_uri: faker.image.url({
width: faker.number.int({ min: 200, max: 400 }),
height: faker.number.int({ min: 200, max: 400 })
}),
video_uri:
'//sf1-cdn-tos.huoshanstatic.com/obj/media-fe/xgplayer_doc_video/mp4/xgplayer-demo-720p.mp4',
children: [
{
id: toAnyString(),
// timestamp: +Mock.Random.date('T'),
author: faker.person.firstName(),
title: faker.lorem.sentence(),
content: baseContent,
importance: faker.number.int({ min: 1, max: 3 }),
display_time: faker.date.anytime(),
pageviews: faker.number.int({ min: 300, max: 5000 }),
image_uri: faker.image.url({
width: faker.number.int({ min: 200, max: 400 }),
height: faker.number.int({ min: 200, max: 400 })
}),
video_uri:
'//sf1-cdn-tos.huoshanstatic.com/obj/media-fe/xgplayer_doc_video/mp4/xgplayer-demo-720p.mp4',
children: [
{
id: toAnyString(),
// timestamp: +Mock.Random.date('T'),
author: faker.person.firstName(),
title: faker.lorem.sentence(),
content: baseContent,
importance: faker.number.int({ min: 1, max: 3 }),
display_time: faker.date.anytime(),
pageviews: faker.number.int({ min: 300, max: 5000 }),
image_uri: faker.image.url({
width: faker.number.int({ min: 200, max: 400 }),
height: faker.number.int({ min: 200, max: 400 })
}),
video_uri:
'//sf1-cdn-tos.huoshanstatic.com/obj/media-fe/xgplayer_doc_video/mp4/xgplayer-demo-720p.mp4'
},
{
id: toAnyString(),
// timestamp: +Mock.Random.date('T'),
author: faker.person.firstName(),
title: faker.lorem.sentence(),
content: baseContent,
importance: faker.number.int({ min: 1, max: 3 }),
display_time: faker.date.anytime(),
pageviews: faker.number.int({ min: 300, max: 5000 }),
image_uri: faker.image.url({
width: faker.number.int({ min: 200, max: 400 }),
height: faker.number.int({ min: 200, max: 400 })
}),
video_uri:
'//sf1-cdn-tos.huoshanstatic.com/obj/media-fe/xgplayer_doc_video/mp4/xgplayer-demo-720p.mp4'
}
]
},
{
id: toAnyString(),
// timestamp: +Mock.Random.date('T'),
author: faker.person.firstName(),
title: faker.lorem.sentence(),
content: baseContent,
importance: faker.number.int({ min: 1, max: 3 }),
display_time: faker.date.anytime(),
pageviews: faker.number.int({ min: 300, max: 5000 }),
image_uri: faker.image.url({
width: faker.number.int({ min: 200, max: 400 }),
height: faker.number.int({ min: 200, max: 400 })
}),
video_uri:
'//sf1-cdn-tos.huoshanstatic.com/obj/media-fe/xgplayer_doc_video/mp4/xgplayer-demo-720p.mp4'
},
{
id: toAnyString(),
// timestamp: +Mock.Random.date('T'),
author: faker.person.firstName(),
title: faker.lorem.sentence(),
content: baseContent,
importance: faker.number.int({ min: 1, max: 3 }),
display_time: faker.date.anytime(),
pageviews: faker.number.int({ min: 300, max: 5000 }),
image_uri: faker.image.url({
width: faker.number.int({ min: 200, max: 400 }),
height: faker.number.int({ min: 200, max: 400 })
}),
video_uri:
'//sf1-cdn-tos.huoshanstatic.com/obj/media-fe/xgplayer_doc_video/mp4/xgplayer-demo-720p.mp4'
},
{
id: toAnyString(),
// timestamp: +Mock.Random.date('T'),
author: faker.person.firstName(),
title: faker.lorem.sentence(),
content: baseContent,
importance: faker.number.int({ min: 1, max: 3 }),
display_time: faker.date.anytime(),
pageviews: faker.number.int({ min: 300, max: 5000 }),
image_uri: faker.image.url({
width: faker.number.int({ min: 200, max: 400 }),
height: faker.number.int({ min: 200, max: 400 })
}),
video_uri:
'//sf1-cdn-tos.huoshanstatic.com/obj/media-fe/xgplayer_doc_video/mp4/xgplayer-demo-720p.mp4'
}
]
// image_uri
})
treeList.push(
Mock.mock({
id: toAnyString(),
// timestamp: +Mock.Random.date('T'),
author: '@first',
title: '@title(5, 10)',
content: baseContent,
importance: '@integer(1, 3)',
display_time: '@datetime',
pageviews: '@integer(300, 5000)',
children: [
{
id: toAnyString(),
// timestamp: +Mock.Random.date('T'),
author: '@first',
title: '@title(5, 10)',
content: baseContent,
importance: '@integer(1, 3)',
display_time: '@datetime',
pageviews: '@integer(300, 5000)',
children: [
{
id: toAnyString(),
// timestamp: +Mock.Random.date('T'),
author: '@first',
title: '@title(5, 10)',
content: baseContent,
importance: '@integer(1, 3)',
display_time: '@datetime',
pageviews: '@integer(300, 5000)'
},
{
id: toAnyString(),
// timestamp: +Mock.Random.date('T'),
author: '@first',
title: '@title(5, 10)',
content: baseContent,
importance: '@integer(1, 3)',
display_time: '@datetime',
pageviews: '@integer(300, 5000)'
}
]
},
{
id: toAnyString(),
// timestamp: +Mock.Random.date('T'),
author: '@first',
title: '@title(5, 10)',
content: baseContent,
importance: '@integer(1, 3)',
display_time: '@datetime',
pageviews: '@integer(300, 5000)'
},
{
id: toAnyString(),
// timestamp: +Mock.Random.date('T'),
author: '@first',
title: '@title(5, 10)',
content: baseContent,
importance: '@integer(1, 3)',
display_time: '@datetime',
pageviews: '@integer(300, 5000)'
},
{
id: toAnyString(),
// timestamp: +Mock.Random.date('T'),
author: '@first',
title: '@title(5, 10)',
content: baseContent,
importance: '@integer(1, 3)',
display_time: '@datetime',
pageviews: '@integer(300, 5000)'
}
]
// image_uri
})
)
}
const cardList = [

View File

@ -27,9 +27,7 @@
"icon": "esno ./scripts/icon.ts"
},
"dependencies": {
"@faker-js/faker": "^8.3.1",
"@iconify/iconify": "^3.1.1",
"@iconify/vue": "^4.1.1",
"@vueuse/core": "^10.7.0",
"@wangeditor/editor": "^5.1.23",
"@wangeditor/editor-for-vue": "^5.1.10",
@ -41,49 +39,50 @@
"driver.js": "^1.3.1",
"echarts": "^5.4.3",
"echarts-wordcloud": "^2.1.0",
"element-plus": "^2.4.3",
"element-plus": "^2.4.4",
"lodash-es": "^4.17.21",
"mitt": "^3.0.1",
"nprogress": "^0.2.0",
"pinia": "^2.1.7",
"pinia-plugin-persistedstate": "^3.2.0",
"pinia-plugin-persistedstate": "^3.2.1",
"qrcode": "^1.5.3",
"qs": "^6.11.2",
"url": "^0.11.3",
"vue": "3.3.10",
"vue": "3.3.13",
"vue-draggable-plus": "^0.3.2",
"vue-i18n": "9.8.0",
"vue-json-pretty": "^2.2.4",
"vue-json-pretty": "^2.3.0",
"vue-router": "^4.2.5",
"vue-types": "^5.1.1",
"xgplayer": "^3.0.10"
"xgplayer": "^3.0.11"
},
"devDependencies": {
"@commitlint/cli": "^18.4.3",
"@commitlint/config-conventional": "^18.4.3",
"@iconify/json": "^2.2.153",
"@intlify/unplugin-vue-i18n": "^1.5.0",
"@purge-icons/generated": "^0.10.0",
"@iconify/json": "^2.2.160",
"@intlify/unplugin-vue-i18n": "^2.0.0",
"@types/fs-extra": "^11.0.4",
"@types/inquirer": "^9.0.7",
"@types/lodash-es": "^4.17.12",
"@types/node": "^20.10.3",
"@types/mockjs": "^1.0.10",
"@types/node": "^20.10.5",
"@types/nprogress": "^0.2.3",
"@types/qrcode": "^1.5.5",
"@types/qs": "^6.9.10",
"@types/sortablejs": "^1.15.7",
"@typescript-eslint/eslint-plugin": "^6.13.2",
"@typescript-eslint/parser": "^6.13.2",
"@typescript-eslint/eslint-plugin": "^6.15.0",
"@typescript-eslint/parser": "^6.15.0",
"@unocss/transformer-variant-group": "^0.58.0",
"@vitejs/plugin-legacy": "^5.2.0",
"@vitejs/plugin-vue": "^4.5.1",
"@vitejs/plugin-vue": "^4.5.2",
"@vitejs/plugin-vue-jsx": "^3.1.0",
"autoprefixer": "^10.4.16",
"chalk": "^5.3.0",
"consola": "^3.2.3",
"eslint": "^8.55.0",
"eslint": "^8.56.0",
"eslint-config-prettier": "^9.1.0",
"eslint-define-config": "^2.0.0",
"eslint-plugin-prettier": "^5.0.1",
"eslint-plugin-prettier": "^5.1.0",
"eslint-plugin-vue": "^9.19.2",
"esno": "^4.0.0",
"fs-extra": "^11.2.0",
@ -91,22 +90,24 @@
"inquirer": "^9.2.12",
"less": "^4.2.0",
"lint-staged": "^15.2.0",
"mockjs": "^1.1.0",
"plop": "^4.0.0",
"postcss": "^8.4.32",
"postcss-html": "^1.5.0",
"postcss-less": "^6.0.0",
"prettier": "^3.1.0",
"prettier": "^3.1.1",
"rimraf": "^5.0.5",
"rollup": "^4.6.1",
"stylelint": "^15.11.0",
"rollup": "^4.9.1",
"rollup-plugin-visualizer": "^5.11.0",
"stylelint": "^16.0.2",
"stylelint-config-html": "^1.1.0",
"stylelint-config-recommended": "^13.0.0",
"stylelint-config-standard": "^34.0.0",
"stylelint-order": "^6.0.3",
"terser": "^5.25.0",
"stylelint-config-recommended": "^14.0.0",
"stylelint-config-standard": "^35.0.0",
"stylelint-order": "^6.0.4",
"terser": "^5.26.0",
"typescript": "5.3.3",
"unocss": "^0.58.0",
"vite": "5.0.6",
"vite": "5.0.10",
"vite-plugin-ejs": "^1.7.0",
"vite-plugin-eslint": "^1.8.1",
"vite-plugin-mock": "^2.9.6",

View File

@ -2,9 +2,9 @@
import { computed } from 'vue'
import { useAppStore } from '@/store/modules/app'
import { ConfigGlobal } from '@/components/ConfigGlobal'
import { isDark } from '@/utils/is'
import { useDesign } from '@/hooks/web/useDesign'
import { useStorage } from '@/hooks/web/useStorage'
import { useDark } from '@vueuse/core'
import { ElNotification } from 'element-plus'
const { getPrefixCls } = useDesign()
@ -16,19 +16,20 @@ const currentSize = computed(() => appStore.getCurrentSize)
const greyMode = computed(() => appStore.getGreyMode)
const { getStorage } = useStorage()
const isDark = useDark({
valueDark: 'dark',
valueLight: 'light'
})
//
const setDefaultTheme = () => {
if (getStorage('isDark') !== null) {
appStore.setIsDark(getStorage('isDark'))
return
}
const isDarkTheme = isDark()
appStore.setIsDark(isDarkTheme)
}
setDefaultTheme()
isDark.value = appStore.getIsDark
ElNotification({
title: '提示',
type: 'warning',
duration: 0,
dangerouslyUseHTMLString: true,
message:
'<div><p><strong>遇事不决,请先查阅常见问题,说不定你能找到相关解答</strong></p><p><a href="https://element-plus-admin-doc.cn/guide/fqa.html" target="_blank">链接地址</a></p></div>'
})
</script>
<template>

View File

@ -1,4 +1,4 @@
import { AxiosResponse, AxiosRequestHeaders, InternalAxiosRequestConfig } from './types'
import { AxiosResponse, InternalAxiosRequestConfig } from './types'
import { ElMessage } from 'element-plus'
import qs from 'qs'
import { SUCCESS_CODE } from '@/constants'
@ -7,7 +7,7 @@ import { useUserStoreWithOut } from '@/store/modules/user'
const defaultRequestInterceptors = (config: InternalAxiosRequestConfig) => {
if (
config.method === 'post' &&
(config.headers as AxiosRequestHeaders)['Content-Type'] === 'application/x-www-form-urlencoded'
config.headers['Content-Type'] === 'application/x-www-form-urlencoded'
) {
config.data = qs.stringify(config.data)
}

View File

@ -3,7 +3,6 @@ import { computed, unref } from 'vue'
import { ElIcon } from 'element-plus'
import { propTypes } from '@/utils/propTypes'
import { useDesign } from '@/hooks/web/useDesign'
import { Icon } from '@iconify/vue'
const { getPrefixCls } = useDesign()
@ -40,20 +39,27 @@ const getIconifyStyle = computed(() => {
<use :xlink:href="symbolId" />
</svg>
<Icon v-else :icon="icon" :style="getIconifyStyle" />
<!-- <Icon v-else :icon="icon" :style="getIconifyStyle" /> -->
<div :class="`${icon} iconify`" :style="getIconifyStyle"></div>
</ElIcon>
</template>
<style lang="less" scoped>
@prefix-cls: ~'@{namespace}-icon';
.@{prefix-cls},
.iconify {
&:hover {
:deep(svg) {
.@{prefix-cls} {
:deep(svg) {
&:hover {
// stylelint-disable-next-line
color: v-bind(hoverColor) !important;
}
}
}
.iconify {
&:hover {
// stylelint-disable-next-line
color: v-bind(hoverColor) !important;
}
}
</style>

View File

@ -12,9 +12,10 @@ export const useRenderMenuItem = (
menuMode: 'vertical' | 'horizontal'
) => {
const renderMenuItem = (routers: AppRouteRecordRaw[], parentPath = '/') => {
return routers.map((v) => {
const meta = v.meta ?? {}
if (!meta.hidden) {
return routers
.filter((v) => !v.meta?.hidden)
.map((v) => {
const meta = v.meta ?? {}
const { oneShowingChild, onlyOneChild } = hasOneShowingChild(v.children, v)
const fullPath = isUrl(v.path) ? v.path : pathResolve(parentPath, v.path) // getAllParentPath<AppRouteRecordRaw>(allRouters, v.path).join('/')
@ -48,8 +49,7 @@ export const useRenderMenuItem = (
</ElSubMenu>
)
}
}
})
})
}
return {

View File

@ -174,7 +174,8 @@ const copyConfig = async () => {
//
topToolBorderColor: '${appStore.getTheme.topToolBorderColor}'
}
`
`,
legacy: true
})
if (!isSupported) {
ElMessage.error(t('setting.copyFailed'))

View File

@ -275,6 +275,10 @@ export default defineComponent({
setProps({ size })
}
const confirmSetColumn = (columns: TableColumn[]) => {
setProps({ columns })
}
expose({
setProps,
setColumn,
@ -434,6 +438,7 @@ export default defineComponent({
align={v.align || align}
headerAlign={v.headerAlign || headerAlign}
label={v.label}
fixed={v.fixed}
width="65px"
></ElTableColumn>
)
@ -543,11 +548,12 @@ export default defineComponent({
</div>
) : (
<>
{unref(getProps).showAction ? (
{unref(getProps).showAction && !unref(getProps).customContent ? (
<TableActions
columns={unref(getProps).columns}
onChangSize={changSize}
onRefresh={refresh}
onConfirm={confirmSetColumn}
/>
) : null}
<ElTable ref={elTableRef} data={unref(getProps).data} {...unref(getBindValue)}>

View File

@ -0,0 +1,165 @@
<script setup lang="ts">
import {
ElDrawer,
ElCheckbox,
ElCheckboxGroup,
ElText,
ElRadioButton,
ElRadioGroup
} from 'element-plus'
import { TableColumn } from '../types'
import { PropType, ref, watch, unref } from 'vue'
import { cloneDeep } from 'lodash-es'
import { DEFAULT_FILTER_COLUMN } from '@/constants'
import { VueDraggable } from 'vue-draggable-plus'
const modelValue = defineModel<boolean>()
const props = defineProps({
columns: {
type: Array as PropType<TableColumn[]>,
default: () => []
}
})
const emit = defineEmits(['confirm'])
const oldColumns = ref<TableColumn[]>()
const settingColumns = ref<TableColumn[]>()
//
const hiddenColumns = ref<TableColumn[]>([])
const defaultCheckColumns = ref<string[]>([])
const checkColumns = ref<string[]>([])
const checkAll = ref(false)
const isIndeterminate = ref(true)
const handleCheckAllChange = (val: boolean) => {
checkColumns.value = val ? unref(defaultCheckColumns) : []
isIndeterminate.value = false
}
const handleCheckedColumnsChange = (value: string[]) => {
const checkedCount = value.length
checkAll.value = checkedCount === unref(defaultCheckColumns)?.length
isIndeterminate.value = checkedCount > 0 && checkedCount < unref(defaultCheckColumns)?.length
}
const confirm = () => {
const newColumns = cloneDeep(unref(settingColumns))?.map((item) => {
const fixed = unref(settingColumns)?.find((col) => col.field === item.field)?.fixed
item.hidden = !!!unref(checkColumns)?.includes(item.field)
item.fixed = fixed ? fixed : undefined
return item
})
emit('confirm', [...unref(hiddenColumns), ...(newColumns || [])])
modelValue.value = false
}
const restore = () => {
initColumns([...unref(hiddenColumns), ...(unref(oldColumns) || [])], true)
}
const initColumns = (columns: TableColumn[], isReStore = false) => {
const newColumns = columns?.filter((item) => {
if (!isReStore) {
item.fixed = item.fixed !== void 0 ? item.fixed : undefined
}
return (item.type && !DEFAULT_FILTER_COLUMN.includes(item.type)) || !item.type
})
if (!unref(oldColumns)) {
oldColumns.value = cloneDeep(newColumns)
}
settingColumns.value = cloneDeep(newColumns)
hiddenColumns.value = cloneDeep(
columns?.filter((item) => item.type && DEFAULT_FILTER_COLUMN.includes(item.type))
)
defaultCheckColumns.value = unref(settingColumns)?.map((item) => item.field) || []
checkColumns.value =
unref(settingColumns)
?.filter((item) => !item.hidden)
?.map((item) => item.field) || []
if (unref(checkColumns)?.length === unref(defaultCheckColumns)?.length) {
checkAll.value = true
isIndeterminate.value = false
}
}
watch(
() => props.columns,
(columns) => {
initColumns(columns)
},
{
immediate: true
}
)
</script>
<template>
<ElDrawer v-model="modelValue" title="列设置" size="350px">
<div>
<div class="flex items-center justify-between">
<div class="flex items-center justify-between">
<ElCheckbox
v-model="checkAll"
:indeterminate="isIndeterminate"
@change="handleCheckAllChange"
/>
<ElText class="ml-8px!">{{ checkColumns.length }} / {{ settingColumns?.length }}</ElText>
</div>
<ElText>固定 / 排序</ElText>
</div>
<div v-if="settingColumns?.length">
<VueDraggable
v-model="settingColumns"
target=".el-checkbox-group"
handle=".handle"
:animation="150"
>
<ElCheckboxGroup
ref="draggableWrap"
v-model="checkColumns"
@change="handleCheckedColumnsChange"
>
<div
v-for="item in settingColumns"
:key="item.field"
class="flex items-center justify-between mt-12px"
>
<ElCheckbox :label="item.field">
{{ item.label }}
</ElCheckbox>
<div class="flex items-center">
<ElRadioGroup size="small" v-model="item.fixed">
<ElRadioButton label="left">
<Icon icon="ep:arrow-left" />
</ElRadioButton>
<ElRadioButton :label="undefined">
<Icon icon="ep:close" />
</ElRadioButton>
<ElRadioButton label="right">
<Icon icon="ep:arrow-right" />
</ElRadioButton>
</ElRadioGroup>
<div class="ml-12px cursor-move handle"><Icon icon="ep:rank" /></div>
</div>
</div>
</ElCheckboxGroup>
</VueDraggable>
</div>
</div>
<template #footer>
<div>
<BaseButton @click="restore">还原</BaseButton>
<BaseButton type="primary" @click="confirm">确定</BaseButton>
</div>
</template>
</ElDrawer>
</template>

View File

@ -1,10 +1,11 @@
<script lang="tsx">
import { defineComponent, unref, computed, PropType } from 'vue'
import { ElTooltip, ElDropdown, ElDropdownMenu, ElDropdownItem, ComponentSize } from 'element-plus'
import { defineComponent, unref, computed, PropType, ref } from 'vue'
import { ElDropdown, ElDropdownMenu, ElDropdownItem, ComponentSize } from 'element-plus'
import { Icon } from '@/components/Icon'
import { useI18n } from '@/hooks/web/useI18n'
import { useAppStore } from '@/store/modules/app'
import { TableColumn } from '../types'
import ColumnSetting from './ColumnSetting.vue'
const appStore = useAppStore()
const sizeMap = computed(() => appStore.sizeMap)
@ -13,14 +14,19 @@ const { t } = useI18n()
export default defineComponent({
name: 'TableActions',
components: {
ColumnSetting
},
props: {
columns: {
type: Array as PropType<TableColumn[]>,
default: () => []
}
},
emits: ['refresh', 'changSize'],
setup(_, { emit }) {
emits: ['refresh', 'changSize', 'confirm'],
setup(props, { emit }) {
const showSetting = ref(false)
const refresh = () => {
emit('refresh')
}
@ -29,54 +35,71 @@ export default defineComponent({
emit('changSize', size)
}
const confirm = (columns: TableColumn[]) => {
emit('confirm', columns)
}
const showColumnSetting = () => {
showSetting.value = true
}
return () => (
<>
<div class="text-right h-28px flex items-center justify-end">
<ElTooltip content={t('common.refresh')} placement="top">
<span onClick={refresh}>
<Icon
icon="ant-design:sync-outlined"
class="cursor-pointer"
hover-color="var(--el-color-primary)"
/>
</span>
</ElTooltip>
<div title="刷新" class="w-30px h-20px flex items-center justify-end" onClick={refresh}>
<Icon
icon="ant-design:sync-outlined"
class="cursor-pointer"
hover-color="var(--el-color-primary)"
/>
</div>
<ElTooltip content={t('common.size')} placement="top">
<ElDropdown trigger="click" onCommand={changSize}>
{{
default: () => {
return (
<span>
<Icon
icon="ant-design:column-height-outlined"
class="cursor-pointer mr-8px ml-8px"
hover-color="var(--el-color-primary)"
/>
</span>
)
},
dropdown: () => {
return (
<ElDropdownMenu>
{{
default: () => {
return unref(sizeMap).map((v) => {
return (
<ElDropdownItem key={v} command={v}>
{t(`size.${v}`)}
</ElDropdownItem>
)
})
}
}}
</ElDropdownMenu>
)
}
}}
</ElDropdown>
</ElTooltip>
<ElDropdown trigger="click" onCommand={changSize}>
{{
default: () => {
return (
<div title="尺寸" class="w-30px h-20px flex items-center justify-end">
<Icon
icon="ant-design:column-height-outlined"
class="cursor-pointer"
hover-color="var(--el-color-primary)"
/>
</div>
)
},
dropdown: () => {
return (
<ElDropdownMenu>
{{
default: () => {
return unref(sizeMap).map((v) => {
return (
<ElDropdownItem key={v} command={v}>
{t(`size.${v}`)}
</ElDropdownItem>
)
})
}
}}
</ElDropdownMenu>
)
}
}}
</ElDropdown>
<div
title="列设置"
class="w-30px h-20px flex items-center justify-end"
onClick={showColumnSetting}
>
<Icon
icon="ant-design:setting-outlined"
class="cursor-pointer"
hover-color="var(--el-color-primary)"
/>
</div>
</div>
<ColumnSetting v-model={showSetting.value} columns={props.columns} onConfirm={confirm} />
</>
)
}

View File

@ -22,3 +22,8 @@ export const NO_REDIRECT_WHITE_LIST = ['/login']
*
*/
export const NO_RESET_WHITE_LIST = ['Redirect', 'Login', 'NoFind', 'Root']
/**
*
*/
export const DEFAULT_FILTER_COLUMN = ['expand', 'selection']

View File

@ -8,7 +8,7 @@ interface Option {
const emitter = mitt()
export const useEmitt = (option?: Option) => {
export const useEventBus = (option?: Option) => {
if (option) {
emitter.on(option.name, option.callback)
@ -18,6 +18,9 @@ export const useEmitt = (option?: Option) => {
}
return {
emitter
on: emitter.on,
off: emitter.off,
emit: emitter.emit,
all: emitter.all
}
}

View File

@ -48,7 +48,9 @@ export default {
lengthRange: 'The length should be between {min} and {max}',
notSpace: 'Spaces are not allowed',
notSpecialCharacters: 'Special characters are not allowed',
isEqual: 'The two are not equal'
isEqual: 'The two are not equal',
// 列设置
setting: 'Setting'
},
lock: {
lockScreen: 'Lock screen',

View File

@ -48,7 +48,8 @@ export default {
lengthRange: '长度在 {min} 到 {max} 个字符',
notSpace: '不能包含空格',
notSpecialCharacters: '不能包含特殊字符',
isEqual: '两次输入不一致'
isEqual: '两次输入不一致',
setting: '设置'
},
lock: {
lockScreen: '锁定屏幕',

View File

@ -12,6 +12,12 @@ export const setupElementPlus = (app: App<Element>) => {
app.use(plugin)
})
// 为了开发环境启动更快,一次性引入所有样式
if (import.meta.env.VITE_USE_ALL_ELEMENT_PLUS_STYLE === 'true') {
import('element-plus/dist/index.css')
return
}
components.forEach((component) => {
app.component(component.name!, component)
})

View File

@ -1,3 +1 @@
import 'virtual:svg-icons-register'
import '@purge-icons/generated'

View File

@ -59,41 +59,35 @@ const columns = reactive<TableColumn[]>([
type: 'index'
},
{
field: 'content',
label: t('tableDemo.header'),
children: [
{
field: 'title',
label: t('tableDemo.title')
},
{
field: 'author',
label: t('tableDemo.author')
},
{
field: 'display_time',
label: t('tableDemo.displayTime')
},
{
field: 'importance',
label: t('tableDemo.importance'),
formatter: (_: Recordable, __: TableColumn, cellValue: number) => {
return (
<ElTag type={cellValue === 1 ? 'success' : cellValue === 2 ? 'warning' : 'danger'}>
{cellValue === 1
? t('tableDemo.important')
: cellValue === 2
? t('tableDemo.good')
: t('tableDemo.commonly')}
</ElTag>
)
}
},
{
field: 'pageviews',
label: t('tableDemo.pageviews')
}
]
field: 'title',
label: t('tableDemo.title')
},
{
field: 'author',
label: t('tableDemo.author')
},
{
field: 'display_time',
label: t('tableDemo.displayTime')
},
{
field: 'importance',
label: t('tableDemo.importance'),
formatter: (_: Recordable, __: TableColumn, cellValue: number) => {
return (
<ElTag type={cellValue === 1 ? 'success' : cellValue === 2 ? 'warning' : 'danger'}>
{cellValue === 1
? t('tableDemo.important')
: cellValue === 2
? t('tableDemo.good')
: t('tableDemo.commonly')}
</ElTag>
)
}
},
{
field: 'pageviews',
label: t('tableDemo.pageviews')
},
{
field: 'action',

View File

@ -2,7 +2,7 @@
import { Waterfall } from '@/components/Waterfall'
import { ContentWrap } from '@/components/ContentWrap'
import { useI18n } from '@/hooks/web/useI18n'
import { faker } from '@faker-js/faker'
import Mock from 'mockjs'
import { ref, unref } from 'vue'
import { toAnyString } from '@/utils'
@ -12,17 +12,17 @@ const getList = () => {
const list: any = []
for (let i = 0; i < 20; i++) {
// 100, 500
const height = faker.number.int({ min: 100, max: 500 })
const width = faker.number.int({ min: 100, max: 500 })
list.push({
width,
height,
id: toAnyString(),
image_uri: faker.image.url({
const height = Mock.Random.integer(100, 500)
const width = Mock.Random.integer(100, 500)
list.push(
Mock.mock({
width,
height
height,
id: toAnyString(),
// httphttps
image_uri: Mock.Random.image(`${width}x${height}`).replace('http://', 'https://')
})
})
)
}
data.value = [...unref(data), ...list]
if (unref(data).length >= 60) {

View File

@ -5,9 +5,9 @@ import { ref, unref } from 'vue'
import { useI18n } from '@/hooks/web/useI18n'
import { useRouter } from 'vue-router'
import { saveTableApi } from '@/api/table'
import { useEmitt } from '@/hooks/event/useEmitt'
import { useEventBus } from '@/hooks/event/useEventBus'
const { emitter } = useEmitt()
const { emit } = useEventBus()
const { push, go } = useRouter()
@ -28,7 +28,7 @@ const save = async () => {
loading.value = false
})
if (res) {
emitter.emit('getList', 'add')
emit('getList', 'add')
push('/example/example-page')
}
}
@ -43,10 +43,10 @@ const save = async () => {
<BaseButton @click="go(-1)">
{{ t('common.back') }}
</BaseButton>
<BaseButton type="primary" :loading="loading" @click="save">
{{ t('exampleDemo.save') }}
<BaseButton type="primary" :loading="loading" @click="save"
>{{ t('exampleDemo.save') }}
</BaseButton>
</template>
</ContentDetailWrap>
</template>
@/hooks/event/useEmitt
@/hooks/event/useEventBus

View File

@ -6,9 +6,9 @@ import { useI18n } from '@/hooks/web/useI18n'
import { useRouter, useRoute } from 'vue-router'
import { saveTableApi, getTableDetApi } from '@/api/table'
import { TableData } from '@/api/table/types'
import { useEmitt } from '@/hooks/event/useEmitt'
import { useEventBus } from '@/hooks/event/useEventBus'
const { emitter } = useEmitt()
const { emit } = useEventBus()
const { push, go } = useRouter()
@ -42,7 +42,7 @@ const save = async () => {
loading.value = false
})
if (res) {
emitter.emit('getList', 'editor')
emit('getList', 'editor')
push('/example/example-page')
}
}
@ -63,4 +63,4 @@ const save = async () => {
</template>
</ContentDetailWrap>
</template>
@/hooks/event/useEmitt
@/hooks/event/useEventBus

View File

@ -9,7 +9,7 @@ import { useTable } from '@/hooks/web/useTable'
import { TableData } from '@/api/table/types'
import { reactive, ref, unref } from 'vue'
import { useRouter } from 'vue-router'
import { useEmitt } from '@/hooks/event/useEmitt'
import { useEventBus } from '@/hooks/event/useEventBus'
import { CrudSchema, useCrudSchemas } from '@/hooks/web/useCrudSchemas'
import { BaseButton } from '@/components/Button'
@ -50,7 +50,7 @@ const { getList, getElTableExpose, delList } = tableMethods
getList()
useEmitt({
useEventBus({
name: 'getList',
callback: (type: string) => {
if (type === 'add') {
@ -294,4 +294,4 @@ const action = (row: TableData, type: string) => {
/>
</ContentWrap>
</template>
@/hooks/event/useEmitt
@/hooks/event/useEventBus

View File

@ -20,12 +20,8 @@ module.exports = {
'function-no-unknown': null,
'no-empty-source': null,
'named-grid-areas-no-invalid': null,
'unicode-bom': 'never',
'no-descending-specificity': null,
'font-family-no-missing-generic-family-keyword': null,
'declaration-colon-space-after': 'always-single-line',
'declaration-colon-space-before': 'never',
'declaration-block-trailing-semicolon': null,
'rule-empty-line-before': [
'always',
{

View File

@ -1,8 +1,8 @@
declare module 'vue' {
export interface GlobalComponents {
Icon: (typeof import('../components/Icon/src/Icon.vue'))['default']
Permission: (typeof import('../components/Permission/src/Permission.vue'))['default']
BaseButton: (typeof import('../components/Button/src/Button.vue'))['default']
Icon: (typeof import('../src/components/Icon/index'))['Icon']
Permission: (typeof import('../src/components/Permission/index'))['Permission']
BaseButton: (typeof import('../src/components/Button/index'))['BaseButton']
}
}

4
types/global.d.ts vendored
View File

@ -1,5 +1,5 @@
import type { CSSProperties } from 'vue'
import { AxiosRequestHeaders } from 'axios'
import { RawAxiosRequestHeaders } from 'axios'
declare global {
declare interface Fn<T = any> {
(...arg: T[]): T
@ -40,7 +40,7 @@ declare global {
data?: any
url?: string
method?: AxiosMethod
headers?: AxiosRequestHeaders
headers?: RawAxiosRequestHeaders
responseType?: AxiosResponseType
}

View File

@ -1,4 +1,4 @@
import { defineConfig, toEscapedSelector as e, presetUno } from 'unocss'
import { defineConfig, toEscapedSelector as e, presetUno, presetIcons } from 'unocss'
import transformerVariantGroup from '@unocss/transformer-variant-group'
export default defineConfig({
@ -111,6 +111,16 @@ ${selector}:after {
}
]
],
presets: [presetUno({ dark: 'class', attributify: false })],
transformers: [transformerVariantGroup()]
presets: [
presetUno({ dark: 'class', attributify: false }),
presetIcons({
prefix: ''
})
],
transformers: [transformerVariantGroup()],
content: {
pipeline: {
include: [/\.(vue|svelte|[jt]sx|mdx?|astro|elm|php|phtml|html|ts)($|\?)/]
}
}
})

View File

@ -5,13 +5,14 @@ import Vue from '@vitejs/plugin-vue'
import VueJsx from '@vitejs/plugin-vue-jsx'
import progress from 'vite-plugin-progress'
import EslintPlugin from 'vite-plugin-eslint'
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 VueI18nPlugin from "@intlify/unplugin-vue-i18n/vite"
import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite'
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
import { createStyleImportPlugin, ElementPlusResolve } from 'vite-plugin-style-import'
import UnoCSS from 'unocss/vite'
import { visualizer } from 'rollup-plugin-visualizer'
// https://vitejs.dev/config/
const root = process.cwd()
@ -24,7 +25,7 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
let env = {} as any
const isBuild = command === 'build'
if (!isBuild) {
env = loadEnv((process.argv[3] === '--mode' ? process.argv[4] : process.argv[3]), root)
env = loadEnv(process.argv[3] === '--mode' ? process.argv[4] : process.argv[3], root)
} else {
env = loadEnv(mode, root)
}
@ -39,19 +40,23 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
}),
VueJsx(),
progress(),
createStyleImportPlugin({
resolves: [ElementPlusResolve()],
libs: [{
libraryName: 'element-plus',
esModule: true,
resolveStyle: (name) => {
if (name === 'click-outside') {
return ''
}
return `element-plus/es/components/${name.replace(/^el-/, '')}/style/css`
}
}]
}),
env.VITE_USE_ALL_ELEMENT_PLUS_STYLE === 'false'
? createStyleImportPlugin({
resolves: [ElementPlusResolve()],
libs: [
{
libraryName: 'element-plus',
esModule: true,
resolveStyle: (name) => {
if (name === 'click-outside') {
return ''
}
return `element-plus/es/components/${name.replace(/^el-/, '')}/style/css`
}
}
]
})
: undefined,
EslintPlugin({
cache: false,
include: ['src/**/*.vue', 'src/**/*.ts', 'src/**/*.tsx'] // 检查的文件
@ -67,22 +72,23 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
svgoOptions: true
}),
PurgeIcons(),
viteMockServe({
ignore: /^\_/,
mockPath: 'mock',
localEnabled: !isBuild,
prodEnabled: isBuild,
injectCode: `
env.VITE_USE_MOCK === 'true'
? viteMockServe({
ignore: /^\_/,
mockPath: 'mock',
localEnabled: !isBuild,
prodEnabled: isBuild,
injectCode: `
import { setupProdMockServer } from '../mock/_createProductionServer'
setupProdMockServer()
`
}),
})
: undefined,
ViteEjsPlugin({
title: env.VITE_APP_TITLE
}),
UnoCSS(),
// sveltekit(),
UnoCSS()
],
css: {
@ -106,17 +112,28 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
}
]
},
esbuild: {
pure: env.VITE_DROP_CONSOLE === 'true' ? ['console.log'] : undefined,
drop: env.VITE_DROP_DEBUGGER === 'true' ? ['debugger'] : undefined
},
build: {
minify: 'terser',
target: 'es2015',
outDir: env.VITE_OUT_DIR || 'dist',
sourcemap: env.VITE_SOURCEMAP === 'true' ? 'inline' : false,
sourcemap: env.VITE_SOURCEMAP === 'true',
// brotliSize: false,
terserOptions: {
compress: {
drop_debugger: env.VITE_DROP_DEBUGGER === 'true',
drop_console: env.VITE_DROP_CONSOLE === 'true'
rollupOptions: {
plugins: env.VITE_USE_BUNDLE_ANALYZER === 'true' ? [visualizer()] : undefined,
// 拆包
output: {
manualChunks: {
'vue-chunks': ['vue', 'vue-router', 'pinia', 'vue-i18n'],
'element-plus': ['element-plus'],
'wang-editor': ['@wangeditor/editor', '@wangeditor/editor-for-vue'],
echarts: ['echarts', 'echarts-wordcloud']
}
}
}
},
cssCodeSplit: !(env.VITE_USE_CSS_SPLIT === 'false')
},
server: {
port: 4000,
@ -125,7 +142,7 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
'/api': {
target: 'http://127.0.0.1:8000',
changeOrigin: true,
rewrite: path => path.replace(/^\/api/, '')
rewrite: (path) => path.replace(/^\/api/, '')
}
},
hmr: {