Merge pull request #301 from kailong321200875/master
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
NODE_ENV=development
|
NODE_ENV=development
|
||||||
|
|
||||||
# 接口前缀
|
# 接口前缀
|
||||||
VITE_API_BASEPATH=base
|
VITE_API_BASE_PATH=base
|
||||||
|
|
||||||
# 打包路径
|
# 打包路径
|
||||||
VITE_BASE_PATH=/
|
VITE_BASE_PATH=/
|
||||||
|
|||||||
2
.env.dev
2
.env.dev
@@ -2,7 +2,7 @@
|
|||||||
NODE_ENV=production
|
NODE_ENV=production
|
||||||
|
|
||||||
# 接口前缀
|
# 接口前缀
|
||||||
VITE_API_BASEPATH=dev
|
VITE_API_BASE_PATH=dev
|
||||||
|
|
||||||
# 打包路径
|
# 打包路径
|
||||||
VITE_BASE_PATH=/dist-dev/
|
VITE_BASE_PATH=/dist-dev/
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
NODE_ENV=production
|
NODE_ENV=production
|
||||||
|
|
||||||
# 接口前缀
|
# 接口前缀
|
||||||
VITE_API_BASEPATH=pro
|
VITE_API_BASE_PATH=pro
|
||||||
|
|
||||||
# 打包路径
|
# 打包路径
|
||||||
VITE_BASE_PATH=/vue-element-plus-admin/
|
VITE_BASE_PATH=/vue-element-plus-admin/
|
||||||
|
|||||||
2
.env.pro
2
.env.pro
@@ -2,7 +2,7 @@
|
|||||||
NODE_ENV=production
|
NODE_ENV=production
|
||||||
|
|
||||||
# 接口前缀
|
# 接口前缀
|
||||||
VITE_API_BASEPATH=pro
|
VITE_API_BASE_PATH=pro
|
||||||
|
|
||||||
# 打包路径
|
# 打包路径
|
||||||
VITE_BASE_PATH=/
|
VITE_BASE_PATH=/
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
NODE_ENV=production
|
NODE_ENV=production
|
||||||
|
|
||||||
# 接口前缀
|
# 接口前缀
|
||||||
VITE_API_BASEPATH=test
|
VITE_API_BASE_PATH=test
|
||||||
|
|
||||||
# 打包路径
|
# 打包路径
|
||||||
VITE_BASE_PATH=/dist-test/
|
VITE_BASE_PATH=/dist-test/
|
||||||
|
|||||||
1
.github/workflows/auto-merge.yml
vendored
1
.github/workflows/auto-merge.yml
vendored
@@ -24,6 +24,7 @@ jobs:
|
|||||||
- name: Automerge
|
- name: Automerge
|
||||||
uses: 'pascalgn/automerge-action@v0.14.3'
|
uses: 'pascalgn/automerge-action@v0.14.3'
|
||||||
env:
|
env:
|
||||||
|
BASE_BRANCHES: 'release'
|
||||||
GITHUB_TOKEN: '${{ secrets.TOKEN }}'
|
GITHUB_TOKEN: '${{ secrets.TOKEN }}'
|
||||||
MERGE_LABELS: ''
|
MERGE_LABELS: ''
|
||||||
MERGE_FILTER_AUTHOR: 'kailong321200875'
|
MERGE_FILTER_AUTHOR: 'kailong321200875'
|
||||||
|
|||||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -1,7 +1,7 @@
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- release
|
||||||
|
|
||||||
name: Release
|
name: Release
|
||||||
|
|
||||||
|
|||||||
0
.husky/commit-msg
Normal file → Executable file
0
.husky/commit-msg
Normal file → Executable file
0
.husky/pre-commit
Normal file → Executable file
0
.husky/pre-commit
Normal file → Executable file
219
mock/department/index.ts
Normal file
219
mock/department/index.ts
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
import config from '@/config/axios/config'
|
||||||
|
import { MockMethod } from 'vite-plugin-mock'
|
||||||
|
import { toAnyString } from '@/utils'
|
||||||
|
import Mock from 'mockjs'
|
||||||
|
|
||||||
|
const { code } = config
|
||||||
|
|
||||||
|
const departmentList: any = []
|
||||||
|
|
||||||
|
const citys = ['厦门总公司', '北京分公司', '上海分公司', '福州分公司', '深圳分公司', '杭州分公司']
|
||||||
|
|
||||||
|
for (let i = 0; i < 5; i++) {
|
||||||
|
departmentList.push({
|
||||||
|
// 部门名称
|
||||||
|
departmentName: citys[i],
|
||||||
|
id: toAnyString(),
|
||||||
|
createTime: '@datetime',
|
||||||
|
// 状态
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
// 备注
|
||||||
|
remark: '@cword(10, 15)',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
// 部门名称
|
||||||
|
departmentName: '研发部',
|
||||||
|
createTime: '@datetime',
|
||||||
|
// 状态
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: toAnyString(),
|
||||||
|
remark: '@cword(10, 15)'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 部门名称
|
||||||
|
departmentName: '产品部',
|
||||||
|
createTime: '@datetime',
|
||||||
|
// 状态
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: toAnyString(),
|
||||||
|
remark: '@cword(10, 15)'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 部门名称
|
||||||
|
departmentName: '运营部',
|
||||||
|
createTime: '@datetime',
|
||||||
|
// 状态
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: toAnyString(),
|
||||||
|
remark: '@cword(10, 15)'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 部门名称
|
||||||
|
departmentName: '市场部',
|
||||||
|
createTime: '@datetime',
|
||||||
|
// 状态
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: toAnyString(),
|
||||||
|
remark: '@cword(10, 15)'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 部门名称
|
||||||
|
departmentName: '销售部',
|
||||||
|
createTime: '@datetime',
|
||||||
|
// 状态
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: toAnyString(),
|
||||||
|
remark: '@cword(10, 15)'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 部门名称
|
||||||
|
departmentName: '客服部',
|
||||||
|
createTime: '@datetime',
|
||||||
|
// 状态
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: toAnyString(),
|
||||||
|
remark: '@cword(10, 15)'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export default [
|
||||||
|
// 列表接口
|
||||||
|
{
|
||||||
|
url: '/department/list',
|
||||||
|
method: 'get',
|
||||||
|
response: () => {
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
code: code,
|
||||||
|
data: {
|
||||||
|
list: departmentList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
url: '/department/table/list',
|
||||||
|
method: 'get',
|
||||||
|
response: () => {
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
code: code,
|
||||||
|
data: {
|
||||||
|
list: departmentList,
|
||||||
|
total: 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
url: '/department/users',
|
||||||
|
method: 'get',
|
||||||
|
timeout: 1000,
|
||||||
|
response: ({ query }) => {
|
||||||
|
const { pageSize } = query
|
||||||
|
// 根据pageSize来创建数据
|
||||||
|
const mockList: any = []
|
||||||
|
for (let i = 0; i < pageSize; i++) {
|
||||||
|
mockList.push(
|
||||||
|
Mock.mock({
|
||||||
|
// 用户名
|
||||||
|
username: '@cname',
|
||||||
|
// 账号
|
||||||
|
account: '@first',
|
||||||
|
// 邮箱
|
||||||
|
email: '@EMAIL',
|
||||||
|
// 创建时间
|
||||||
|
createTime: '@datetime',
|
||||||
|
// 角色
|
||||||
|
role: '@first',
|
||||||
|
// 用户id
|
||||||
|
id: toAnyString()
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
code: code,
|
||||||
|
data: {
|
||||||
|
total: 100,
|
||||||
|
list: mockList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 保存接口
|
||||||
|
{
|
||||||
|
url: '/department/user/save',
|
||||||
|
method: 'post',
|
||||||
|
timeout: 1000,
|
||||||
|
response: () => {
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
code: code,
|
||||||
|
data: 'success'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 删除接口
|
||||||
|
{
|
||||||
|
url: '/department/user/delete',
|
||||||
|
method: 'post',
|
||||||
|
response: ({ body }) => {
|
||||||
|
const ids = body.ids
|
||||||
|
if (!ids) {
|
||||||
|
return {
|
||||||
|
code: '500',
|
||||||
|
message: '请选择需要删除的数据'
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
code: code,
|
||||||
|
data: 'success'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 保存接口
|
||||||
|
{
|
||||||
|
url: '/department/save',
|
||||||
|
method: 'post',
|
||||||
|
timeout: 1000,
|
||||||
|
response: () => {
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
code: code,
|
||||||
|
data: 'success'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 删除接口
|
||||||
|
{
|
||||||
|
url: '/department/delete',
|
||||||
|
method: 'post',
|
||||||
|
response: ({ body }) => {
|
||||||
|
const ids = body.ids
|
||||||
|
if (!ids) {
|
||||||
|
return {
|
||||||
|
code: '500',
|
||||||
|
message: '请选择需要删除的数据'
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
code: code,
|
||||||
|
data: 'success'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
] as MockMethod[]
|
||||||
250
mock/menu/index.ts
Normal file
250
mock/menu/index.ts
Normal file
@@ -0,0 +1,250 @@
|
|||||||
|
import config from '@/config/axios/config'
|
||||||
|
import { MockMethod } from 'vite-plugin-mock'
|
||||||
|
import Mock from 'mockjs'
|
||||||
|
|
||||||
|
const { code } = config
|
||||||
|
|
||||||
|
const timeout = 1000
|
||||||
|
|
||||||
|
export default [
|
||||||
|
// 列表接口
|
||||||
|
{
|
||||||
|
url: '/menu/list',
|
||||||
|
method: 'get',
|
||||||
|
timeout,
|
||||||
|
response: () => {
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
code: code,
|
||||||
|
data: {
|
||||||
|
list: [
|
||||||
|
{
|
||||||
|
path: '/dashboard',
|
||||||
|
component: '#',
|
||||||
|
redirect: '/dashboard/analysis',
|
||||||
|
name: 'Dashboard',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 1,
|
||||||
|
meta: {
|
||||||
|
title: '首页',
|
||||||
|
icon: 'ant-design:dashboard-filled',
|
||||||
|
alwaysShow: true
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'analysis',
|
||||||
|
component: 'views/Dashboard/Analysis',
|
||||||
|
name: 'Analysis',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 2,
|
||||||
|
meta: {
|
||||||
|
title: '分析页',
|
||||||
|
noCache: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'workplace',
|
||||||
|
component: 'views/Dashboard/Workplace',
|
||||||
|
name: 'Workplace',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 3,
|
||||||
|
meta: {
|
||||||
|
title: '工作台',
|
||||||
|
noCache: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/external-link',
|
||||||
|
component: '#',
|
||||||
|
meta: {
|
||||||
|
title: '文档',
|
||||||
|
icon: 'clarity:document-solid'
|
||||||
|
},
|
||||||
|
name: 'ExternalLink',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 4,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'https://element-plus-admin-doc.cn/',
|
||||||
|
name: 'DocumentLink',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 5,
|
||||||
|
meta: {
|
||||||
|
title: '文档'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/level',
|
||||||
|
component: '#',
|
||||||
|
redirect: '/level/menu1/menu1-1/menu1-1-1',
|
||||||
|
name: 'Level',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 6,
|
||||||
|
meta: {
|
||||||
|
title: '菜单',
|
||||||
|
icon: 'carbon:skill-level-advanced'
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'menu1',
|
||||||
|
name: 'Menu1',
|
||||||
|
component: '##',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 7,
|
||||||
|
redirect: '/level/menu1/menu1-1/menu1-1-1',
|
||||||
|
meta: {
|
||||||
|
title: '菜单1'
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'menu1-1',
|
||||||
|
name: 'Menu11',
|
||||||
|
component: '##',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 8,
|
||||||
|
redirect: '/level/menu1/menu1-1/menu1-1-1',
|
||||||
|
meta: {
|
||||||
|
title: '菜单1-1',
|
||||||
|
alwaysShow: true
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'menu1-1-1',
|
||||||
|
name: 'Menu111',
|
||||||
|
component: 'views/Level/Menu111',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 9,
|
||||||
|
permission: ['edit', 'add', 'delete'],
|
||||||
|
meta: {
|
||||||
|
title: '菜单1-1-1'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'menu1-2',
|
||||||
|
name: 'Menu12',
|
||||||
|
component: 'views/Level/Menu12',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 10,
|
||||||
|
permission: ['edit', 'add', 'delete'],
|
||||||
|
meta: {
|
||||||
|
title: '菜单1-2'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'menu2',
|
||||||
|
name: 'Menu2Demo',
|
||||||
|
component: 'views/Level/Menu2',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 11,
|
||||||
|
permission: ['edit', 'add', 'delete'],
|
||||||
|
meta: {
|
||||||
|
title: '菜单2'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/example',
|
||||||
|
component: '#',
|
||||||
|
redirect: '/example/example-dialog',
|
||||||
|
name: 'Example',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 12,
|
||||||
|
meta: {
|
||||||
|
title: '综合示例',
|
||||||
|
icon: 'ep:management',
|
||||||
|
alwaysShow: true
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'example-dialog',
|
||||||
|
component: 'views/Example/Dialog/ExampleDialog',
|
||||||
|
name: 'ExampleDialog',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 13,
|
||||||
|
permission: ['edit', 'add', 'delete'],
|
||||||
|
meta: {
|
||||||
|
title: '综合示例-弹窗',
|
||||||
|
permission: ['edit', 'add']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'example-page',
|
||||||
|
component: 'views/Example/Page/ExamplePage',
|
||||||
|
name: 'ExamplePage',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 14,
|
||||||
|
permission: ['edit', 'add', 'delete'],
|
||||||
|
meta: {
|
||||||
|
title: '综合示例-页面',
|
||||||
|
permission: ['edit', 'add']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'example-add',
|
||||||
|
component: 'views/Example/Page/ExampleAdd',
|
||||||
|
name: 'ExampleAdd',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 15,
|
||||||
|
permission: ['edit', 'add', 'delete'],
|
||||||
|
meta: {
|
||||||
|
title: '综合示例-新增',
|
||||||
|
noTagsView: true,
|
||||||
|
noCache: true,
|
||||||
|
hidden: true,
|
||||||
|
showMainRoute: true,
|
||||||
|
activeMenu: '/example/example-page',
|
||||||
|
permission: ['delete', 'add']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'example-edit',
|
||||||
|
component: 'views/Example/Page/ExampleEdit',
|
||||||
|
name: 'ExampleEdit',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 16,
|
||||||
|
permission: ['edit', 'add', 'delete'],
|
||||||
|
meta: {
|
||||||
|
title: '综合示例-编辑',
|
||||||
|
noTagsView: true,
|
||||||
|
noCache: true,
|
||||||
|
hidden: true,
|
||||||
|
showMainRoute: true,
|
||||||
|
activeMenu: '/example/example-page',
|
||||||
|
permission: ['delete', 'add']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'example-detail',
|
||||||
|
component: 'views/Example/Page/ExampleDetail',
|
||||||
|
name: 'ExampleDetail',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 17,
|
||||||
|
permission: ['edit', 'add', 'delete'],
|
||||||
|
meta: {
|
||||||
|
title: '综合示例-详情',
|
||||||
|
noTagsView: true,
|
||||||
|
noCache: true,
|
||||||
|
hidden: true,
|
||||||
|
showMainRoute: true,
|
||||||
|
activeMenu: '/example/example-page',
|
||||||
|
permission: ['delete', 'edit']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
] as MockMethod[]
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
import config from '@/config/axios/config'
|
import config from '@/config/axios/config'
|
||||||
import { MockMethod } from 'vite-plugin-mock'
|
import { MockMethod } from 'vite-plugin-mock'
|
||||||
|
import Mock from 'mockjs'
|
||||||
|
import { toAnyString } from '@/utils'
|
||||||
|
|
||||||
const { code } = config
|
const { code } = config
|
||||||
|
|
||||||
@@ -105,14 +107,6 @@ const adminList = [
|
|||||||
meta: {
|
meta: {
|
||||||
title: 'UseForm'
|
title: 'UseForm'
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'ref-form',
|
|
||||||
component: 'views/Components/Form/RefForm',
|
|
||||||
name: 'RefForm',
|
|
||||||
meta: {
|
|
||||||
title: 'RefForm'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -143,13 +137,29 @@ const adminList = [
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'ref-table',
|
path: 'tree-table',
|
||||||
component: 'views/Components/Table/RefTable',
|
component: 'views/Components/Table/TreeTable',
|
||||||
name: 'RefTable',
|
name: 'TreeTable',
|
||||||
meta: {
|
meta: {
|
||||||
title: 'RefTable'
|
title: 'TreeTable'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'table-image-preview',
|
||||||
|
component: 'views/Components/Table/TableImagePreview',
|
||||||
|
name: 'TableImagePreview',
|
||||||
|
meta: {
|
||||||
|
title: 'router.PicturePreview'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// {
|
||||||
|
// path: 'ref-table',
|
||||||
|
// component: 'views/Components/Table/RefTable',
|
||||||
|
// name: 'RefTable',
|
||||||
|
// meta: {
|
||||||
|
// title: 'RefTable'
|
||||||
|
// }
|
||||||
|
// }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -259,14 +269,6 @@ const adminList = [
|
|||||||
meta: {
|
meta: {
|
||||||
title: 'router.inputPassword'
|
title: 'router.inputPassword'
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'sticky',
|
|
||||||
component: 'views/Components/Sticky',
|
|
||||||
name: 'Sticky',
|
|
||||||
meta: {
|
|
||||||
title: 'router.sticky'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -290,13 +292,21 @@ const adminList = [
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'useCrudSchemas',
|
path: 'useOpenTab',
|
||||||
component: 'views/hooks/useCrudSchemas',
|
component: 'views/hooks/useOpenTab',
|
||||||
name: 'UseCrudSchemas',
|
name: 'UseOpenTab',
|
||||||
meta: {
|
meta: {
|
||||||
title: 'useCrudSchemas'
|
title: 'useOpenTab'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// {
|
||||||
|
// path: 'useCrudSchemas',
|
||||||
|
// component: 'views/hooks/useCrudSchemas',
|
||||||
|
// name: 'UseCrudSchemas',
|
||||||
|
// meta: {
|
||||||
|
// title: 'useCrudSchemas'
|
||||||
|
// }
|
||||||
|
// }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -462,6 +472,59 @@ const adminList = [
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/authorization',
|
||||||
|
component: '#',
|
||||||
|
redirect: '/authorization/user',
|
||||||
|
name: 'Authorization',
|
||||||
|
meta: {
|
||||||
|
title: 'router.authorization',
|
||||||
|
icon: 'eos-icons:role-binding',
|
||||||
|
alwaysShow: true
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'department',
|
||||||
|
component: 'views/Authorization/Department/Department',
|
||||||
|
name: 'Department',
|
||||||
|
meta: {
|
||||||
|
title: 'router.department'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'user',
|
||||||
|
component: 'views/Authorization/User/User',
|
||||||
|
name: 'User',
|
||||||
|
meta: {
|
||||||
|
title: 'router.user'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'menu',
|
||||||
|
component: 'views/Authorization/Menu/Menu',
|
||||||
|
name: 'Menu',
|
||||||
|
meta: {
|
||||||
|
title: 'router.menuManagement'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'role',
|
||||||
|
component: 'views/Authorization/Role/Role',
|
||||||
|
name: 'Role',
|
||||||
|
meta: {
|
||||||
|
title: 'router.role'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'test',
|
||||||
|
component: 'views/Authorization/Test/Test',
|
||||||
|
name: 'Test',
|
||||||
|
meta: {
|
||||||
|
title: 'router.permission'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -481,6 +544,8 @@ const testList: string[] = [
|
|||||||
'/components/table',
|
'/components/table',
|
||||||
'/components/table/default-table',
|
'/components/table/default-table',
|
||||||
'/components/table/use-table',
|
'/components/table/use-table',
|
||||||
|
'/components/table/tree-table',
|
||||||
|
'/components/table/table-image-preview',
|
||||||
'/components/table/ref-table',
|
'/components/table/ref-table',
|
||||||
'/components/editor-demo',
|
'/components/editor-demo',
|
||||||
'/components/editor-demo/editor',
|
'/components/editor-demo/editor',
|
||||||
@@ -498,7 +563,8 @@ const testList: string[] = [
|
|||||||
'/Components/Sticky',
|
'/Components/Sticky',
|
||||||
'/hooks',
|
'/hooks',
|
||||||
'/hooks/useWatermark',
|
'/hooks/useWatermark',
|
||||||
'/hooks/useCrudSchemas',
|
'/hooks/useOpenTab',
|
||||||
|
// '/hooks/useCrudSchemas',
|
||||||
'/level',
|
'/level',
|
||||||
'/level/menu1',
|
'/level/menu1',
|
||||||
'/level/menu1/menu1-1',
|
'/level/menu1/menu1-1',
|
||||||
@@ -511,12 +577,441 @@ const testList: string[] = [
|
|||||||
'/example/example-add',
|
'/example/example-add',
|
||||||
'/example/example-edit',
|
'/example/example-edit',
|
||||||
'/example/example-detail',
|
'/example/example-detail',
|
||||||
|
'/authorization',
|
||||||
|
'/authorization/department',
|
||||||
|
'/authorization/user',
|
||||||
|
'/authorization/role',
|
||||||
|
'/authorization/menu',
|
||||||
|
'/authorization/test',
|
||||||
'/error',
|
'/error',
|
||||||
'/error/404-demo',
|
'/error/404-demo',
|
||||||
'/error/403-demo',
|
'/error/403-demo',
|
||||||
'/error/500-demo'
|
'/error/500-demo'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
const List: any[] = []
|
||||||
|
|
||||||
|
const roleNames = ['超级管理员', '管理员', '普通用户', '游客']
|
||||||
|
const menus = [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
path: '/dashboard',
|
||||||
|
component: '#',
|
||||||
|
redirect: '/dashboard/analysis',
|
||||||
|
name: 'Dashboard',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 1,
|
||||||
|
meta: {
|
||||||
|
title: '首页',
|
||||||
|
icon: 'ant-design:dashboard-filled',
|
||||||
|
alwaysShow: true
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'analysis',
|
||||||
|
component: 'views/Dashboard/Analysis',
|
||||||
|
name: 'Analysis',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 2,
|
||||||
|
meta: {
|
||||||
|
title: '分析页',
|
||||||
|
noCache: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'workplace',
|
||||||
|
component: 'views/Dashboard/Workplace',
|
||||||
|
name: 'Workplace',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 3,
|
||||||
|
meta: {
|
||||||
|
title: '工作台',
|
||||||
|
noCache: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/external-link',
|
||||||
|
component: '#',
|
||||||
|
meta: {
|
||||||
|
title: '文档',
|
||||||
|
icon: 'clarity:document-solid'
|
||||||
|
},
|
||||||
|
name: 'ExternalLink',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 4,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'https://element-plus-admin-doc.cn/',
|
||||||
|
name: 'DocumentLink',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 5,
|
||||||
|
meta: {
|
||||||
|
title: '文档'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/level',
|
||||||
|
component: '#',
|
||||||
|
redirect: '/level/menu1/menu1-1/menu1-1-1',
|
||||||
|
name: 'Level',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 6,
|
||||||
|
meta: {
|
||||||
|
title: '菜单',
|
||||||
|
icon: 'carbon:skill-level-advanced'
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'menu1',
|
||||||
|
name: 'Menu1',
|
||||||
|
component: '##',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 7,
|
||||||
|
redirect: '/level/menu1/menu1-1/menu1-1-1',
|
||||||
|
meta: {
|
||||||
|
title: '菜单1'
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'menu1-1',
|
||||||
|
name: 'Menu11',
|
||||||
|
component: '##',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 8,
|
||||||
|
redirect: '/level/menu1/menu1-1/menu1-1-1',
|
||||||
|
meta: {
|
||||||
|
title: '菜单1-1',
|
||||||
|
alwaysShow: true
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'menu1-1-1',
|
||||||
|
name: 'Menu111',
|
||||||
|
component: 'views/Level/Menu111',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 9,
|
||||||
|
permission: ['edit', 'add', 'delete'],
|
||||||
|
meta: {
|
||||||
|
title: '菜单1-1-1',
|
||||||
|
permission: ['edit', 'add', 'delete']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'menu1-2',
|
||||||
|
name: 'Menu12',
|
||||||
|
component: 'views/Level/Menu12',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 10,
|
||||||
|
permission: ['edit', 'add', 'delete'],
|
||||||
|
meta: {
|
||||||
|
title: '菜单1-2',
|
||||||
|
permission: ['edit', 'add', 'delete']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'menu2',
|
||||||
|
name: 'Menu2Demo',
|
||||||
|
component: 'views/Level/Menu2',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 11,
|
||||||
|
permission: ['edit', 'add', 'delete'],
|
||||||
|
meta: {
|
||||||
|
title: '菜单2',
|
||||||
|
permission: ['edit', 'add', 'delete']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/example',
|
||||||
|
component: '#',
|
||||||
|
redirect: '/example/example-dialog',
|
||||||
|
name: 'Example',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 12,
|
||||||
|
meta: {
|
||||||
|
title: '综合示例',
|
||||||
|
icon: 'ep:management',
|
||||||
|
alwaysShow: true
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'example-dialog',
|
||||||
|
component: 'views/Example/Dialog/ExampleDialog',
|
||||||
|
name: 'ExampleDialog',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 13,
|
||||||
|
permission: ['edit', 'add', 'delete'],
|
||||||
|
meta: {
|
||||||
|
title: '综合示例-弹窗',
|
||||||
|
permission: ['edit', 'add', 'delete']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'example-page',
|
||||||
|
component: 'views/Example/Page/ExamplePage',
|
||||||
|
name: 'ExamplePage',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 14,
|
||||||
|
permission: ['edit', 'add', 'delete'],
|
||||||
|
meta: {
|
||||||
|
title: '综合示例-页面',
|
||||||
|
permission: ['edit', 'add', 'delete']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'example-add',
|
||||||
|
component: 'views/Example/Page/ExampleAdd',
|
||||||
|
name: 'ExampleAdd',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 15,
|
||||||
|
permission: ['edit', 'add', 'delete'],
|
||||||
|
meta: {
|
||||||
|
title: '综合示例-新增',
|
||||||
|
noTagsView: true,
|
||||||
|
noCache: true,
|
||||||
|
hidden: true,
|
||||||
|
showMainRoute: true,
|
||||||
|
activeMenu: '/example/example-page',
|
||||||
|
permission: ['edit', 'add', 'delete']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'example-edit',
|
||||||
|
component: 'views/Example/Page/ExampleEdit',
|
||||||
|
name: 'ExampleEdit',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 16,
|
||||||
|
permission: ['edit', 'add', 'delete'],
|
||||||
|
meta: {
|
||||||
|
title: '综合示例-编辑',
|
||||||
|
noTagsView: true,
|
||||||
|
noCache: true,
|
||||||
|
hidden: true,
|
||||||
|
showMainRoute: true,
|
||||||
|
activeMenu: '/example/example-page',
|
||||||
|
permission: ['edit', 'add', 'delete']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'example-detail',
|
||||||
|
component: 'views/Example/Page/ExampleDetail',
|
||||||
|
name: 'ExampleDetail',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 17,
|
||||||
|
permission: ['edit', 'add', 'delete'],
|
||||||
|
meta: {
|
||||||
|
title: '综合示例-详情',
|
||||||
|
noTagsView: true,
|
||||||
|
noCache: true,
|
||||||
|
hidden: true,
|
||||||
|
showMainRoute: true,
|
||||||
|
activeMenu: '/example/example-page',
|
||||||
|
permission: ['edit', 'add', 'delete']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
path: '/dashboard',
|
||||||
|
component: '#',
|
||||||
|
redirect: '/dashboard/analysis',
|
||||||
|
name: 'Dashboard',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 1,
|
||||||
|
meta: {
|
||||||
|
title: '首页',
|
||||||
|
icon: 'ant-design:dashboard-filled',
|
||||||
|
alwaysShow: true
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'analysis',
|
||||||
|
component: 'views/Dashboard/Analysis',
|
||||||
|
name: 'Analysis',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 2,
|
||||||
|
meta: {
|
||||||
|
title: '分析页',
|
||||||
|
noCache: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'workplace',
|
||||||
|
component: 'views/Dashboard/Workplace',
|
||||||
|
name: 'Workplace',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 3,
|
||||||
|
meta: {
|
||||||
|
title: '工作台',
|
||||||
|
noCache: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
path: '/external-link',
|
||||||
|
component: '#',
|
||||||
|
meta: {
|
||||||
|
title: '文档',
|
||||||
|
icon: 'clarity:document-solid'
|
||||||
|
},
|
||||||
|
name: 'ExternalLink',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 4,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'https://element-plus-admin-doc.cn/',
|
||||||
|
name: 'DocumentLink',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 5,
|
||||||
|
meta: {
|
||||||
|
title: '文档'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/level',
|
||||||
|
component: '#',
|
||||||
|
redirect: '/level/menu1/menu1-1/menu1-1-1',
|
||||||
|
name: 'Level',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 6,
|
||||||
|
meta: {
|
||||||
|
title: '菜单',
|
||||||
|
icon: 'carbon:skill-level-advanced'
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'menu1',
|
||||||
|
name: 'Menu1',
|
||||||
|
component: '##',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 7,
|
||||||
|
redirect: '/level/menu1/menu1-1/menu1-1-1',
|
||||||
|
meta: {
|
||||||
|
title: '菜单1'
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'menu1-1',
|
||||||
|
name: 'Menu11',
|
||||||
|
component: '##',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 8,
|
||||||
|
redirect: '/level/menu1/menu1-1/menu1-1-1',
|
||||||
|
meta: {
|
||||||
|
title: '菜单1-1',
|
||||||
|
alwaysShow: true
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'menu1-1-1',
|
||||||
|
name: 'Menu111',
|
||||||
|
component: 'views/Level/Menu111',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 9,
|
||||||
|
permission: ['edit', 'add', 'delete'],
|
||||||
|
meta: {
|
||||||
|
title: '菜单1-1-1',
|
||||||
|
permission: ['edit', 'add', 'delete']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'menu1-2',
|
||||||
|
name: 'Menu12',
|
||||||
|
component: 'views/Level/Menu12',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 10,
|
||||||
|
permission: ['edit', 'add', 'delete'],
|
||||||
|
meta: {
|
||||||
|
title: '菜单1-2',
|
||||||
|
permission: ['edit', 'add', 'delete']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'menu2',
|
||||||
|
name: 'Menu2Demo',
|
||||||
|
component: 'views/Level/Menu2',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 11,
|
||||||
|
permission: ['edit', 'add', 'delete'],
|
||||||
|
meta: {
|
||||||
|
title: '菜单2',
|
||||||
|
permission: ['edit', 'add', 'delete']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
path: '/example',
|
||||||
|
component: '#',
|
||||||
|
redirect: '/example/example-dialog',
|
||||||
|
name: 'Example',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 12,
|
||||||
|
meta: {
|
||||||
|
title: '综合示例',
|
||||||
|
icon: 'ep:management',
|
||||||
|
alwaysShow: true
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'example-detail',
|
||||||
|
component: 'views/Example/Page/ExampleDetail',
|
||||||
|
name: 'ExampleDetail',
|
||||||
|
status: Mock.Random.integer(0, 1),
|
||||||
|
id: 17,
|
||||||
|
permission: ['edit', 'add', 'delete'],
|
||||||
|
meta: {
|
||||||
|
title: '综合示例-详情',
|
||||||
|
noTagsView: true,
|
||||||
|
noCache: true,
|
||||||
|
hidden: true,
|
||||||
|
showMainRoute: true,
|
||||||
|
activeMenu: '/example/example-page',
|
||||||
|
permission: ['edit', 'add', 'delete']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
for (let i = 0; i < 4; 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 [
|
export default [
|
||||||
// 列表接口
|
// 列表接口
|
||||||
{
|
{
|
||||||
@@ -532,5 +1027,21 @@ export default [
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
url: '/role/table',
|
||||||
|
method: 'get',
|
||||||
|
timeout,
|
||||||
|
response: () => {
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
code: code,
|
||||||
|
data: {
|
||||||
|
list: List,
|
||||||
|
total: 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
] as MockMethod[]
|
] as MockMethod[]
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ const count = 100
|
|||||||
const baseContent =
|
const baseContent =
|
||||||
'<p>I am testing data, I am testing data.</p><p><img src="https://wpimg.wallstcn.com/4c69009c-0fd4-4153-b112-6cb53d1cf943"></p>'
|
'<p>I am testing data, I am testing data.</p><p><img src="https://wpimg.wallstcn.com/4c69009c-0fd4-4153-b112-6cb53d1cf943"></p>'
|
||||||
|
|
||||||
let List: {
|
interface ListProps {
|
||||||
id: string
|
id: string
|
||||||
author: string
|
author: string
|
||||||
title: string
|
title: string
|
||||||
@@ -20,11 +20,63 @@ let List: {
|
|||||||
importance: number
|
importance: number
|
||||||
display_time: string
|
display_time: string
|
||||||
pageviews: number
|
pageviews: number
|
||||||
}[] = []
|
image_uri: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TreeListProps {
|
||||||
|
id: string
|
||||||
|
author: string
|
||||||
|
title: string
|
||||||
|
content: string
|
||||||
|
importance: number
|
||||||
|
display_time: string
|
||||||
|
pageviews: number
|
||||||
|
children: TreeListProps[]
|
||||||
|
}
|
||||||
|
|
||||||
|
let List: ListProps[] = []
|
||||||
|
|
||||||
for (let i = 0; i < count; i++) {
|
for (let i = 0; i < count; i++) {
|
||||||
List.push(
|
List.push(
|
||||||
Mock.mock({
|
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)',
|
||||||
|
image_uri: Mock.Random.image('@integer(300, 5000)x@integer(300, 5000)')
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const treeList: TreeListProps[] = []
|
||||||
|
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
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(),
|
id: toAnyString(),
|
||||||
// timestamp: +Mock.Random.date('T'),
|
// timestamp: +Mock.Random.date('T'),
|
||||||
author: '@first',
|
author: '@first',
|
||||||
@@ -33,12 +85,81 @@ for (let i = 0; i < count; i++) {
|
|||||||
importance: '@integer(1, 3)',
|
importance: '@integer(1, 3)',
|
||||||
display_time: '@datetime',
|
display_time: '@datetime',
|
||||||
pageviews: '@integer(300, 5000)'
|
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
|
// image_uri
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default [
|
export default [
|
||||||
|
// 树形列表接口
|
||||||
|
{
|
||||||
|
url: '/example/treeList',
|
||||||
|
method: 'get',
|
||||||
|
timeout,
|
||||||
|
response: ({ query }) => {
|
||||||
|
const { title, pageIndex, pageSize } = query
|
||||||
|
const mockList = treeList.filter((item) => {
|
||||||
|
if (title && item.title.indexOf(title) < 0) return false
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
const pageList = mockList.filter(
|
||||||
|
(_, index) => index < pageSize * pageIndex && index >= pageSize * (pageIndex - 1)
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
code: code,
|
||||||
|
data: {
|
||||||
|
total: mockList.length,
|
||||||
|
list: pageList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
// 列表接口
|
// 列表接口
|
||||||
{
|
{
|
||||||
url: '/example/list',
|
url: '/example/list',
|
||||||
|
|||||||
102
package.json
102
package.json
@@ -1,16 +1,16 @@
|
|||||||
{
|
{
|
||||||
"name": "vue-element-plus-admin",
|
"name": "vue-element-plus-admin",
|
||||||
"version": "1.9.9",
|
"version": "2.0。0",
|
||||||
"description": "一套基于vue3、element-plus、typesScript、vite4的后台集成方案。",
|
"description": "一套基于vue3、element-plus、typesScript、vite4的后台集成方案。",
|
||||||
"author": "Archer <502431556@qq.com>",
|
"author": "Archer <502431556@qq.com>",
|
||||||
"private": false,
|
"private": false,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"i": "pnpm install",
|
"i": "pnpm install",
|
||||||
"dev": "vite --mode base",
|
"dev": "vite --mode base",
|
||||||
"ts:check": "vue-tsc --noEmit",
|
"ts:check": "vue-tsc --noEmit --skipLibCheck",
|
||||||
"build:pro": "vite build --mode pro",
|
"build:pro": "vite build --mode pro",
|
||||||
"build:gitee": "vite build --mode gitee",
|
"build:gitee": "vite build --mode gitee",
|
||||||
"build:dev": "npm run ts:check && vite build --mode dev",
|
"build:dev": "vite build --mode dev",
|
||||||
"build:test": "npm run ts:check && vite build --mode test",
|
"build:test": "npm run ts:check && vite build --mode test",
|
||||||
"serve:pro": "vite preview --mode pro",
|
"serve:pro": "vite preview --mode pro",
|
||||||
"serve:dev": "vite preview --mode dev",
|
"serve:dev": "vite preview --mode dev",
|
||||||
@@ -26,88 +26,90 @@
|
|||||||
"p": "plop"
|
"p": "plop"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@iconify/iconify": "^3.1.0",
|
"@iconify/iconify": "^3.1.1",
|
||||||
"@vueuse/core": "^10.1.2",
|
"@iconify/vue": "^4.1.1",
|
||||||
|
"@vueuse/core": "^10.2.1",
|
||||||
"@wangeditor/editor": "^5.1.23",
|
"@wangeditor/editor": "^5.1.23",
|
||||||
"@wangeditor/editor-for-vue": "^5.1.10",
|
"@wangeditor/editor-for-vue": "^5.1.10",
|
||||||
"@zxcvbn-ts/core": "^3.0.0",
|
"@zxcvbn-ts/core": "^3.0.3",
|
||||||
"animate.css": "^4.1.1",
|
"animate.css": "^4.1.1",
|
||||||
"axios": "^1.4.0",
|
"axios": "^1.4.0",
|
||||||
"dayjs": "^1.11.7",
|
"dayjs": "^1.11.9",
|
||||||
"echarts": "^5.4.2",
|
"echarts": "^5.4.3",
|
||||||
"echarts-wordcloud": "^2.1.0",
|
"echarts-wordcloud": "^2.1.0",
|
||||||
"element-plus": "2.3.4",
|
"element-plus": "^2.3.8",
|
||||||
"intro.js": "^7.0.1",
|
"intro.js": "^7.0.1",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"mitt": "^3.0.0",
|
"mitt": "^3.0.1",
|
||||||
"mockjs": "^1.1.0",
|
"mockjs": "^1.1.0",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"pinia": "^2.0.36",
|
"pinia": "^2.1.4",
|
||||||
"pinia-plugin-persist": "^1.0.0",
|
"pinia-plugin-persist": "^1.0.0",
|
||||||
"qrcode": "^1.5.3",
|
"qrcode": "^1.5.3",
|
||||||
"qs": "^6.11.1",
|
"qs": "^6.11.2",
|
||||||
"url": "^0.11.0",
|
"sortablejs": "^1.15.0",
|
||||||
"vue": "3.2.47",
|
"url": "^0.11.1",
|
||||||
|
"vue": "3.3.4",
|
||||||
"vue-i18n": "9.2.2",
|
"vue-i18n": "9.2.2",
|
||||||
"vue-router": "^4.1.6",
|
"vue-router": "^4.2.4",
|
||||||
"vue-types": "^5.0.2",
|
"vue-types": "^5.1.0"
|
||||||
"web-storage-cache": "^1.1.1"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@commitlint/cli": "^17.6.3",
|
"@commitlint/cli": "^17.6.7",
|
||||||
"@commitlint/config-conventional": "^17.6.3",
|
"@commitlint/config-conventional": "^17.6.7",
|
||||||
"@iconify/json": "^2.2.62",
|
"@iconify/json": "^2.2.92",
|
||||||
"@intlify/unplugin-vue-i18n": "^0.10.0",
|
"@intlify/unplugin-vue-i18n": "^0.12.2",
|
||||||
"@purge-icons/generated": "^0.9.0",
|
"@purge-icons/generated": "^0.9.0",
|
||||||
"@types/intro.js": "^5.1.1",
|
"@types/intro.js": "^5.1.1",
|
||||||
"@types/lodash-es": "^4.17.7",
|
"@types/lodash-es": "^4.17.8",
|
||||||
"@types/node": "^20.1.1",
|
"@types/node": "^20.4.2",
|
||||||
"@types/nprogress": "^0.2.0",
|
"@types/nprogress": "^0.2.0",
|
||||||
"@types/qrcode": "^1.5.0",
|
"@types/qrcode": "^1.5.1",
|
||||||
"@types/qs": "^6.9.7",
|
"@types/qs": "^6.9.7",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.59.5",
|
"@types/sortablejs": "^1.15.1",
|
||||||
"@typescript-eslint/parser": "^5.59.5",
|
"@typescript-eslint/eslint-plugin": "^6.1.0",
|
||||||
"@unocss/transformer-variant-group": "^0.51.12",
|
"@typescript-eslint/parser": "^6.1.0",
|
||||||
"@vitejs/plugin-legacy": "^4.0.3",
|
"@unocss/transformer-variant-group": "^0.53.5",
|
||||||
"@vitejs/plugin-vue": "^4.2.1",
|
"@vitejs/plugin-legacy": "^4.1.0",
|
||||||
|
"@vitejs/plugin-vue": "^4.2.3",
|
||||||
"@vitejs/plugin-vue-jsx": "^3.0.1",
|
"@vitejs/plugin-vue-jsx": "^3.0.1",
|
||||||
"@vue-macros/volar": "^0.9.8",
|
"@vue-macros/volar": "^0.12.2",
|
||||||
"autoprefixer": "^10.4.14",
|
"autoprefixer": "^10.4.14",
|
||||||
"consola": "^3.1.0",
|
"consola": "^3.2.3",
|
||||||
"eslint": "^8.40.0",
|
"eslint": "^8.45.0",
|
||||||
"eslint-config-prettier": "^8.8.0",
|
"eslint-config-prettier": "^8.8.0",
|
||||||
"eslint-define-config": "^1.20.0",
|
"eslint-define-config": "^1.21.0",
|
||||||
"eslint-plugin-prettier": "^4.2.1",
|
"eslint-plugin-prettier": "^5.0.0",
|
||||||
"eslint-plugin-vue": "^9.11.1",
|
"eslint-plugin-vue": "^9.15.1",
|
||||||
"husky": "^8.0.3",
|
"husky": "^8.0.3",
|
||||||
"less": "^4.1.3",
|
"less": "^4.1.3",
|
||||||
"lint-staged": "^13.2.2",
|
"lint-staged": "^13.2.3",
|
||||||
"plop": "^3.1.2",
|
"plop": "^3.1.2",
|
||||||
"postcss": "^8.4.23",
|
"postcss": "^8.4.26",
|
||||||
"postcss-html": "^1.5.0",
|
"postcss-html": "^1.5.0",
|
||||||
"postcss-less": "^6.0.0",
|
"postcss-less": "^6.0.0",
|
||||||
"prettier": "^2.8.8",
|
"prettier": "^3.0.0",
|
||||||
"rimraf": "^5.0.0",
|
"rimraf": "^5.0.1",
|
||||||
"rollup": "^3.21.5",
|
"rollup": "^3.26.3",
|
||||||
"stylelint": "^15.6.1",
|
"stylelint": "^15.10.1",
|
||||||
"stylelint-config-html": "^1.1.0",
|
"stylelint-config-html": "^1.1.0",
|
||||||
"stylelint-config-prettier": "^9.0.5",
|
"stylelint-config-prettier": "^9.0.5",
|
||||||
"stylelint-config-recommended": "^12.0.0",
|
"stylelint-config-recommended": "^13.0.0",
|
||||||
"stylelint-config-standard": "^33.0.0",
|
"stylelint-config-standard": "^34.0.0",
|
||||||
"stylelint-order": "^6.0.3",
|
"stylelint-order": "^6.0.3",
|
||||||
"terser": "^5.17.2",
|
"terser": "^5.19.1",
|
||||||
"typescript": "5.0.4",
|
"typescript": "5.1.6",
|
||||||
"unocss": "^0.51.12",
|
"unocss": "^0.53.5",
|
||||||
"unplugin-vue-define-options": "^1.3.5",
|
"unplugin-vue-define-options": "^1.3.11",
|
||||||
"vite": "4.3.5",
|
"vite": "4.4.4",
|
||||||
"vite-plugin-ejs": "^1.6.4",
|
"vite-plugin-ejs": "^1.6.4",
|
||||||
"vite-plugin-eslint": "^1.8.1",
|
"vite-plugin-eslint": "^1.8.1",
|
||||||
"vite-plugin-mock": "^3.0.0",
|
"vite-plugin-mock": "2.9.6",
|
||||||
"vite-plugin-progress": "^0.0.7",
|
"vite-plugin-progress": "^0.0.7",
|
||||||
"vite-plugin-purge-icons": "^0.9.2",
|
"vite-plugin-purge-icons": "^0.9.2",
|
||||||
"vite-plugin-style-import": "2.0.0",
|
"vite-plugin-style-import": "2.0.0",
|
||||||
"vite-plugin-svg-icons": "^2.0.1",
|
"vite-plugin-svg-icons": "^2.0.1",
|
||||||
"vue-tsc": "^1.6.4"
|
"vue-tsc": "^1.8.5"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 14.18.0"
|
"node": ">= 14.18.0"
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { useAppStore } from '@/store/modules/app'
|
|||||||
import { ConfigGlobal } from '@/components/ConfigGlobal'
|
import { ConfigGlobal } from '@/components/ConfigGlobal'
|
||||||
import { isDark } from '@/utils/is'
|
import { isDark } from '@/utils/is'
|
||||||
import { useDesign } from '@/hooks/web/useDesign'
|
import { useDesign } from '@/hooks/web/useDesign'
|
||||||
import { useCache } from '@/hooks/web/useCache'
|
import { useStorage } from '@/hooks/web/useStorage'
|
||||||
|
|
||||||
const { getPrefixCls } = useDesign()
|
const { getPrefixCls } = useDesign()
|
||||||
|
|
||||||
@@ -16,12 +16,12 @@ const currentSize = computed(() => appStore.getCurrentSize)
|
|||||||
|
|
||||||
const greyMode = computed(() => appStore.getGreyMode)
|
const greyMode = computed(() => appStore.getGreyMode)
|
||||||
|
|
||||||
const { wsCache } = useCache()
|
const { getStorage } = useStorage()
|
||||||
|
|
||||||
// 根据浏览器当前主题设置系统主题色
|
// 根据浏览器当前主题设置系统主题色
|
||||||
const setDefaultTheme = () => {
|
const setDefaultTheme = () => {
|
||||||
if (wsCache.get('isDark') !== null) {
|
if (getStorage('isDark') !== null) {
|
||||||
appStore.setIsDark(wsCache.get('isDark'))
|
appStore.setIsDark(getStorage('isDark'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const isDarkTheme = isDark()
|
const isDarkTheme = isDark()
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import request from '@/config/axios'
|
import request from '@/config/axios'
|
||||||
|
|
||||||
// 获取所有字典
|
// 获取所有字典
|
||||||
export const getDictApi = (): Promise<IResponse> => {
|
export const getDictApi = () => {
|
||||||
return request.get({ url: '/dict/list' })
|
return request.get({ url: '/dict/list' })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 模拟获取某个字典
|
// 模拟获取某个字典
|
||||||
export const getDictOneApi = async (): Promise<IResponse> => {
|
export const getDictOneApi = async () => {
|
||||||
return request.get({ url: '/dict/one' })
|
return request.get({ url: '/dict/one' })
|
||||||
}
|
}
|
||||||
|
|||||||
30
src/api/department/index.ts
Normal file
30
src/api/department/index.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import request from '@/config/axios'
|
||||||
|
import { DepartmentListResponse, DepartmentUserParams, DepartmentUserResponse } from './types'
|
||||||
|
|
||||||
|
export const getDepartmentApi = () => {
|
||||||
|
return request.get<DepartmentListResponse>({ url: '/department/list' })
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getUserByIdApi = (params: DepartmentUserParams) => {
|
||||||
|
return request.get<DepartmentUserResponse>({ url: '/department/users', params })
|
||||||
|
}
|
||||||
|
|
||||||
|
export const deleteUserByIdApi = (ids: string[] | number[]) => {
|
||||||
|
return request.post({ url: '/department/user/delete', data: { ids } })
|
||||||
|
}
|
||||||
|
|
||||||
|
export const saveUserApi = (data: any) => {
|
||||||
|
return request.post({ url: '/department/user/save', data })
|
||||||
|
}
|
||||||
|
|
||||||
|
export const saveDepartmentApi = (data: any) => {
|
||||||
|
return request.post({ url: '/department/save', data })
|
||||||
|
}
|
||||||
|
|
||||||
|
export const deleteDepartmentApi = (ids: string[] | number[]) => {
|
||||||
|
return request.post({ url: '/department/delete', data: { ids } })
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getDepartmentTableApi = (params: any) => {
|
||||||
|
return request.get({ url: '/department/table/list', params })
|
||||||
|
}
|
||||||
32
src/api/department/types.ts
Normal file
32
src/api/department/types.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
export interface DepartmentItem {
|
||||||
|
id: string
|
||||||
|
departmentName: string
|
||||||
|
children?: DepartmentItem[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DepartmentListResponse {
|
||||||
|
list: DepartmentItem[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DepartmentUserParams {
|
||||||
|
pageSize: number
|
||||||
|
pageIndex: number
|
||||||
|
id: string
|
||||||
|
username?: string
|
||||||
|
account?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DepartmentUserItem {
|
||||||
|
id: string
|
||||||
|
username: string
|
||||||
|
account: string
|
||||||
|
email: string
|
||||||
|
createTime: string
|
||||||
|
role: string
|
||||||
|
department: DepartmentItem
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DepartmentUserResponse {
|
||||||
|
list: DepartmentUserItem[]
|
||||||
|
total: number
|
||||||
|
}
|
||||||
5
src/api/menu/index.ts
Normal file
5
src/api/menu/index.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
export const getMenuListApi = () => {
|
||||||
|
return request.get({ url: '/menu/list' })
|
||||||
|
}
|
||||||
5
src/api/role/index.ts
Normal file
5
src/api/role/index.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
export const getRoleListApi = () => {
|
||||||
|
return request.get({ url: '/role/table' })
|
||||||
|
}
|
||||||
@@ -1,10 +1,14 @@
|
|||||||
import request from '@/config/axios'
|
import request from '@/config/axios'
|
||||||
import type { TableData } from './types'
|
import type { TableData } from './types'
|
||||||
|
|
||||||
export const getTableListApi = (params: any): Promise<IResponse> => {
|
export const getTableListApi = (params: any) => {
|
||||||
return request.get({ url: '/example/list', params })
|
return request.get({ url: '/example/list', params })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getTreeTableListApi = (params: any) => {
|
||||||
|
return request.get({ url: '/example/treeList', params })
|
||||||
|
}
|
||||||
|
|
||||||
export const saveTableApi = (data: Partial<TableData>): Promise<IResponse> => {
|
export const saveTableApi = (data: Partial<TableData>): Promise<IResponse> => {
|
||||||
return request.post({ url: '/example/save', data })
|
return request.post({ url: '/example/save', data })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
import ConfigGlobal from './src/ConfigGlobal.vue'
|
import ConfigGlobal from './src/ConfigGlobal.vue'
|
||||||
|
|
||||||
|
export type { ConfigGlobalTypes } from './src/types'
|
||||||
|
|
||||||
export { ConfigGlobal }
|
export { ConfigGlobal }
|
||||||
|
|||||||
@@ -1,20 +1,19 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { provide, computed, watch, onMounted } from 'vue'
|
import { provide, computed, watch, onMounted } from 'vue'
|
||||||
import { propTypes } from '@/utils/propTypes'
|
import { propTypes } from '@/utils/propTypes'
|
||||||
import { ElConfigProvider } from 'element-plus'
|
import { ComponentSize, ElConfigProvider } from 'element-plus'
|
||||||
import { useLocaleStore } from '@/store/modules/locale'
|
import { useLocaleStore } from '@/store/modules/locale'
|
||||||
import { useWindowSize } from '@vueuse/core'
|
import { useWindowSize } from '@vueuse/core'
|
||||||
import { useAppStore } from '@/store/modules/app'
|
import { useAppStore } from '@/store/modules/app'
|
||||||
import { setCssVar } from '@/utils'
|
import { setCssVar } from '@/utils'
|
||||||
import { useDesign } from '@/hooks/web/useDesign'
|
import { useDesign } from '@/hooks/web/useDesign'
|
||||||
import { ElementPlusSize } from '@/types/elementPlus'
|
|
||||||
|
|
||||||
const { variables } = useDesign()
|
const { variables } = useDesign()
|
||||||
|
|
||||||
const appStore = useAppStore()
|
const appStore = useAppStore()
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
size: propTypes.oneOf<ElementPlusSize>(['default', 'small', 'large']).def('default')
|
size: propTypes.oneOf<ComponentSize>(['default', 'small', 'large']).def('default')
|
||||||
})
|
})
|
||||||
|
|
||||||
provide('configGlobal', props)
|
provide('configGlobal', props)
|
||||||
|
|||||||
5
src/components/ConfigGlobal/src/types/index.ts
Normal file
5
src/components/ConfigGlobal/src/types/index.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { ComponentSize } from 'element-plus'
|
||||||
|
|
||||||
|
export interface ConfigGlobalTypes {
|
||||||
|
size?: ComponentSize
|
||||||
|
}
|
||||||
@@ -1,11 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ElCard, ElButton } from 'element-plus'
|
import { ElCard } from 'element-plus'
|
||||||
import { propTypes } from '@/utils/propTypes'
|
import { propTypes } from '@/utils/propTypes'
|
||||||
import { useDesign } from '@/hooks/web/useDesign'
|
import { useDesign } from '@/hooks/web/useDesign'
|
||||||
import { ref, onMounted, defineEmits } from 'vue'
|
|
||||||
import { Sticky } from '@/components/Sticky'
|
|
||||||
import { useI18n } from '@/hooks/web/useI18n'
|
|
||||||
const { t } = useI18n()
|
|
||||||
|
|
||||||
const { getPrefixCls } = useDesign()
|
const { getPrefixCls } = useDesign()
|
||||||
|
|
||||||
@@ -15,45 +11,15 @@ defineProps({
|
|||||||
title: propTypes.string.def(''),
|
title: propTypes.string.def(''),
|
||||||
message: propTypes.string.def('')
|
message: propTypes.string.def('')
|
||||||
})
|
})
|
||||||
const emit = defineEmits(['back'])
|
|
||||||
const offset = ref(85)
|
|
||||||
const contentDetailWrap = ref()
|
|
||||||
onMounted(() => {
|
|
||||||
offset.value = contentDetailWrap.value.getBoundingClientRect().top
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="[`${prefixCls}-container`, 'relative bg-[#fff]']" ref="contentDetailWrap">
|
<div :class="[`${prefixCls}-container`, 'relative']">
|
||||||
<Sticky :offset="offset">
|
|
||||||
<div
|
|
||||||
:class="[
|
|
||||||
`${prefixCls}-header`,
|
|
||||||
'flex b-b-1 h-50px items-center text-center bg-white pr-10px'
|
|
||||||
]"
|
|
||||||
>
|
|
||||||
<div :class="[`${prefixCls}-header__back`, 'flex pl-10px pr-10px ']">
|
|
||||||
<el-button @click="emit('back')">
|
|
||||||
<Icon icon="ep:arrow-left" class="mr-5px" />
|
|
||||||
{{ t('common.back') }}
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
<div :class="[`${prefixCls}-header__title`, 'flex flex-1 justify-center']">
|
|
||||||
<slot name="title">
|
|
||||||
<label class="text-16px font-700">{{ title }}</label>
|
|
||||||
</slot>
|
|
||||||
</div>
|
|
||||||
<div :class="[`${prefixCls}-header__right`, 'flex pl-10px pr-10px']">
|
|
||||||
<slot name="right"></slot>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Sticky>
|
|
||||||
<div style="padding: var(--app-content-padding)">
|
|
||||||
<ElCard :class="[`${prefixCls}-body`, 'mb-20px']" shadow="never">
|
<ElCard :class="[`${prefixCls}-body`, 'mb-20px']" shadow="never">
|
||||||
<div>
|
<div class="mb-20px pb-20px" style="border-bottom: 1px solid var(--el-border-color)">
|
||||||
<slot></slot>
|
<slot name="header"></slot>
|
||||||
</div>
|
</div>
|
||||||
|
<slot></slot>
|
||||||
</ElCard>
|
</ElCard>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import ContextMenu from './src/ContextMenu.vue'
|
|||||||
import { ElDropdown } from 'element-plus'
|
import { ElDropdown } from 'element-plus'
|
||||||
import type { RouteLocationNormalizedLoaded } from 'vue-router'
|
import type { RouteLocationNormalizedLoaded } from 'vue-router'
|
||||||
|
|
||||||
|
export type { ContextMenuSchema } from './src/types'
|
||||||
|
|
||||||
export interface ContextMenuExpose {
|
export interface ContextMenuExpose {
|
||||||
elDropdownMenuRef: ComponentRef<typeof ElDropdown>
|
elDropdownMenuRef: ComponentRef<typeof ElDropdown>
|
||||||
tagItem: RouteLocationNormalizedLoaded
|
tagItem: RouteLocationNormalizedLoaded
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { PropType, ref } from 'vue'
|
|||||||
import { useI18n } from '@/hooks/web/useI18n'
|
import { useI18n } from '@/hooks/web/useI18n'
|
||||||
import { useDesign } from '@/hooks/web/useDesign'
|
import { useDesign } from '@/hooks/web/useDesign'
|
||||||
import type { RouteLocationNormalizedLoaded } from 'vue-router'
|
import type { RouteLocationNormalizedLoaded } from 'vue-router'
|
||||||
import { contextMenuSchema } from '../../../types/contextMenu'
|
import { ContextMenuSchema } from './types'
|
||||||
const { getPrefixCls } = useDesign()
|
const { getPrefixCls } = useDesign()
|
||||||
|
|
||||||
const prefixCls = getPrefixCls('context-menu')
|
const prefixCls = getPrefixCls('context-menu')
|
||||||
@@ -15,7 +15,7 @@ const emit = defineEmits(['visibleChange'])
|
|||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
schema: {
|
schema: {
|
||||||
type: Array as PropType<contextMenuSchema[]>,
|
type: Array as PropType<ContextMenuSchema[]>,
|
||||||
default: () => []
|
default: () => []
|
||||||
},
|
},
|
||||||
trigger: {
|
trigger: {
|
||||||
@@ -28,7 +28,7 @@ const props = defineProps({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const command = (item: contextMenuSchema) => {
|
const command = (item: ContextMenuSchema) => {
|
||||||
item.command && item.command(item)
|
item.command && item.command(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
7
src/components/ContextMenu/src/types/index.ts
Normal file
7
src/components/ContextMenu/src/types/index.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export interface ContextMenuSchema {
|
||||||
|
disabled?: boolean
|
||||||
|
divided?: boolean
|
||||||
|
icon?: string
|
||||||
|
label: string
|
||||||
|
command?: (item: ContextMenuSchema) => void
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
import Descriptions from './src/Descriptions.vue'
|
import Descriptions from './src/Descriptions.vue'
|
||||||
|
|
||||||
|
export type { DescriptionsSchema } from './src/types'
|
||||||
|
|
||||||
export { Descriptions }
|
export { Descriptions }
|
||||||
|
|||||||
@@ -1,23 +1,32 @@
|
|||||||
<script setup lang="ts">
|
<script lang="tsx">
|
||||||
import { ElCollapseTransition, ElDescriptions, ElDescriptionsItem, ElTooltip } from 'element-plus'
|
import { ElCollapseTransition, ElDescriptions, ElDescriptionsItem, ElTooltip } from 'element-plus'
|
||||||
import { useDesign } from '@/hooks/web/useDesign'
|
import { useDesign } from '@/hooks/web/useDesign'
|
||||||
import { propTypes } from '@/utils/propTypes'
|
import { propTypes } from '@/utils/propTypes'
|
||||||
import { ref, unref, PropType, computed, useAttrs, useSlots } from 'vue'
|
import { ref, unref, PropType, computed, defineComponent } from 'vue'
|
||||||
import { useAppStore } from '@/store/modules/app'
|
import { useAppStore } from '@/store/modules/app'
|
||||||
import { DescriptionsSchema } from '@/types/descriptions'
|
import { DescriptionsSchema } from './types'
|
||||||
|
import { Icon } from '@/components/Icon'
|
||||||
|
import { get } from 'lodash-es'
|
||||||
|
|
||||||
const appStore = useAppStore()
|
const appStore = useAppStore()
|
||||||
|
|
||||||
const mobile = computed(() => appStore.getMobile)
|
const mobile = computed(() => appStore.getMobile)
|
||||||
|
|
||||||
const attrs = useAttrs()
|
const { getPrefixCls } = useDesign()
|
||||||
|
|
||||||
const slots = useSlots()
|
const prefixCls = getPrefixCls('descriptions')
|
||||||
|
|
||||||
const props = defineProps({
|
export default defineComponent({
|
||||||
|
name: 'Descriptions',
|
||||||
|
props: {
|
||||||
title: propTypes.string.def(''),
|
title: propTypes.string.def(''),
|
||||||
message: propTypes.string.def(''),
|
message: propTypes.string.def(''),
|
||||||
collapse: propTypes.bool.def(true),
|
collapse: propTypes.bool.def(true),
|
||||||
|
border: propTypes.bool.def(true),
|
||||||
|
column: propTypes.number.def(2),
|
||||||
|
size: propTypes.oneOf(['large', 'default', 'small']).def('default'),
|
||||||
|
direction: propTypes.oneOf(['horizontal', 'vertical']).def('horizontal'),
|
||||||
|
extra: propTypes.string.def(''),
|
||||||
schema: {
|
schema: {
|
||||||
type: Array as PropType<DescriptionsSchema[]>,
|
type: Array as PropType<DescriptionsSchema[]>,
|
||||||
default: () => []
|
default: () => []
|
||||||
@@ -26,13 +35,9 @@ const props = defineProps({
|
|||||||
type: Object as PropType<any>,
|
type: Object as PropType<any>,
|
||||||
default: () => ({})
|
default: () => ({})
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
setup(props, { slots, attrs }) {
|
||||||
const { getPrefixCls } = useDesign()
|
const getBindValue = computed((): any => {
|
||||||
|
|
||||||
const prefixCls = getPrefixCls('descriptions')
|
|
||||||
|
|
||||||
const getBindValue = computed(() => {
|
|
||||||
const delArr: string[] = ['title', 'message', 'collapse', 'schema', 'data', 'class']
|
const delArr: string[] = ['title', 'message', 'collapse', 'schema', 'data', 'class']
|
||||||
const obj = { ...attrs, ...props }
|
const obj = { ...attrs, ...props }
|
||||||
for (const key in obj) {
|
for (const key in obj) {
|
||||||
@@ -40,10 +45,13 @@ const getBindValue = computed(() => {
|
|||||||
delete obj[key]
|
delete obj[key]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (unref(mobile)) {
|
||||||
|
obj.direction = 'vertical'
|
||||||
|
}
|
||||||
return obj
|
return obj
|
||||||
})
|
})
|
||||||
|
|
||||||
const getBindItemValue = (item: DescriptionsSchema) => {
|
const getBindItemValue = (item: DescriptionsSchema) => {
|
||||||
const delArr: string[] = ['field']
|
const delArr: string[] = ['field']
|
||||||
const obj = { ...item }
|
const obj = { ...item }
|
||||||
for (const key in obj) {
|
for (const key in obj) {
|
||||||
@@ -52,83 +60,85 @@ const getBindItemValue = (item: DescriptionsSchema) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return obj
|
return obj
|
||||||
}
|
}
|
||||||
|
|
||||||
// 折叠
|
// 折叠
|
||||||
const show = ref(true)
|
const show = ref(true)
|
||||||
|
|
||||||
const toggleClick = () => {
|
const toggleClick = () => {
|
||||||
if (props.collapse) {
|
if (props.collapse) {
|
||||||
show.value = !unref(show)
|
show.value = !unref(show)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
return () => {
|
||||||
|
return (
|
||||||
<div
|
<div
|
||||||
:class="[
|
class={[
|
||||||
prefixCls,
|
prefixCls,
|
||||||
'bg-[var(--el-color-white)] dark:bg-[var(--el-bg-color)] dark:border-[var(--el-border-color)] dark:border-1px'
|
'bg-[var(--el-color-white)] dark:bg-[var(--el-bg-color)] dark:border-[var(--el-border-color)] dark:border-1px'
|
||||||
]"
|
]}
|
||||||
>
|
>
|
||||||
|
{props.title ? (
|
||||||
<div
|
<div
|
||||||
v-if="title"
|
class={[
|
||||||
:class="[
|
|
||||||
`${prefixCls}-header`,
|
`${prefixCls}-header`,
|
||||||
'h-50px flex justify-between items-center b-b-1 border-solid border-[var(--tags-view-border-color)] px-10px cursor-pointer dark:border-[var(--el-border-color)]'
|
'relative h-50px flex justify-between items-center layout-border__bottom px-10px cursor-pointer'
|
||||||
]"
|
]}
|
||||||
@click="toggleClick"
|
onClick={toggleClick}
|
||||||
>
|
>
|
||||||
<div :class="[`${prefixCls}-header__title`, 'relative font-18px font-bold ml-10px']">
|
<div class={[`${prefixCls}-header__title`, 'relative font-18px font-bold ml-10px']}>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
{{ title }}
|
{props.title}
|
||||||
<ElTooltip v-if="message" :content="message" placement="right">
|
{props.message ? (
|
||||||
<Icon icon="ep:warning" class="ml-5px" />
|
<ElTooltip content={props.message} placement="right">
|
||||||
|
<Icon icon="bi:question-circle-fill" class="ml-5px" size={14} />
|
||||||
</ElTooltip>
|
</ElTooltip>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Icon v-if="collapse" :icon="show ? 'ep:arrow-down' : 'ep:arrow-up'" />
|
{props.collapse ? <Icon icon={show.value ? 'ep:arrow-down' : 'ep:arrow-up'} /> : null}
|
||||||
</div>
|
</div>
|
||||||
|
) : null}
|
||||||
|
|
||||||
<ElCollapseTransition>
|
<ElCollapseTransition>
|
||||||
<div v-show="show" :class="[`${prefixCls}-content`, 'p-10px']">
|
<div v-show={unref(show)} class={[`${prefixCls}-content`]}>
|
||||||
<ElDescriptions
|
<ElDescriptions {...unref(getBindValue)}>
|
||||||
:column="2"
|
{{
|
||||||
border
|
extra: () => (slots['extra'] ? slots['extra']() : props.extra),
|
||||||
:direction="mobile ? 'vertical' : 'horizontal'"
|
default: () => {
|
||||||
v-bind="getBindValue"
|
return props.schema.map((item) => {
|
||||||
>
|
return (
|
||||||
<template v-if="slots['extra']" #extra>
|
<ElDescriptionsItem key={item.field} {...getBindItemValue(item)}>
|
||||||
<slot name="extra"></slot>
|
{{
|
||||||
</template>
|
label: () => (item.slots?.label ? item.slots?.label(item) : item.label),
|
||||||
<ElDescriptionsItem
|
default: () =>
|
||||||
v-for="item in schema"
|
item.slots?.default
|
||||||
:key="item.field"
|
? item.slots?.default(props.data)
|
||||||
v-bind="getBindItemValue(item)"
|
: get(props.data, item.field)
|
||||||
>
|
}}
|
||||||
<template #label>
|
|
||||||
<slot
|
|
||||||
:name="`${item.field}-label`"
|
|
||||||
:row="{
|
|
||||||
label: item.label
|
|
||||||
}"
|
|
||||||
>{{ item.label }}</slot
|
|
||||||
>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #default>
|
|
||||||
<slot :name="item.field" :row="data">{{ data[item.field] }}</slot>
|
|
||||||
</template>
|
|
||||||
</ElDescriptionsItem>
|
</ElDescriptionsItem>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}}
|
||||||
</ElDescriptions>
|
</ElDescriptions>
|
||||||
</div>
|
</div>
|
||||||
</ElCollapseTransition>
|
</ElCollapseTransition>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
@prefix-cls: ~'@{namespace}-descriptions';
|
@prefix-cls: ~'@{namespace}-descriptions';
|
||||||
|
|
||||||
|
:deep(.@{elNamespace}-descriptions__header) {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
.@{prefix-cls}-header {
|
.@{prefix-cls}-header {
|
||||||
&__title {
|
&__title {
|
||||||
&::after {
|
&::after {
|
||||||
|
|||||||
@@ -8,4 +8,8 @@ export interface DescriptionsSchema {
|
|||||||
labelAlign?: 'left' | 'center' | 'right'
|
labelAlign?: 'left' | 'center' | 'right'
|
||||||
className?: string
|
className?: string
|
||||||
labelClassName?: string
|
labelClassName?: string
|
||||||
|
slots?: {
|
||||||
|
default?: (...args: any[]) => JSX.Element | null
|
||||||
|
label?: (...args: any[]) => JSX.Element | null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -10,7 +10,7 @@ const props = defineProps({
|
|||||||
modelValue: propTypes.bool.def(false),
|
modelValue: propTypes.bool.def(false),
|
||||||
title: propTypes.string.def('Dialog'),
|
title: propTypes.string.def('Dialog'),
|
||||||
fullscreen: propTypes.bool.def(true),
|
fullscreen: propTypes.bool.def(true),
|
||||||
maxHeight: propTypes.oneOfType([String, Number]).def('500px')
|
maxHeight: propTypes.oneOfType([String, Number]).def('400px')
|
||||||
})
|
})
|
||||||
|
|
||||||
const getBindValue = computed(() => {
|
const getBindValue = computed(() => {
|
||||||
@@ -50,7 +50,6 @@ watch(
|
|||||||
)
|
)
|
||||||
|
|
||||||
const dialogStyle = computed(() => {
|
const dialogStyle = computed(() => {
|
||||||
console.log(unref(dialogHeight))
|
|
||||||
return {
|
return {
|
||||||
height: unref(dialogHeight)
|
height: unref(dialogHeight)
|
||||||
}
|
}
|
||||||
@@ -64,20 +63,34 @@ const dialogStyle = computed(() => {
|
|||||||
destroy-on-close
|
destroy-on-close
|
||||||
lock-scroll
|
lock-scroll
|
||||||
draggable
|
draggable
|
||||||
|
top="0"
|
||||||
:close-on-click-modal="false"
|
:close-on-click-modal="false"
|
||||||
|
:show-close="false"
|
||||||
>
|
>
|
||||||
<template #header>
|
<template #header="{ close }">
|
||||||
<div class="flex justify-between">
|
<div class="flex justify-between items-center h-54px pl-15px pr-15px relative">
|
||||||
<slot name="title">
|
<slot name="title">
|
||||||
{{ title }}
|
{{ title }}
|
||||||
</slot>
|
</slot>
|
||||||
|
<div
|
||||||
|
class="h-54px flex justify-between items-center absolute top-[50%] right-15px translate-y-[-50%]"
|
||||||
|
>
|
||||||
<Icon
|
<Icon
|
||||||
v-if="fullscreen"
|
v-if="fullscreen"
|
||||||
class="mr-18px cursor-pointer is-hover mt-2px z-10"
|
class="cursor-pointer is-hover !h-54px mr-10px"
|
||||||
:icon="isFullscreen ? 'zmdi:fullscreen-exit' : 'zmdi:fullscreen'"
|
:icon="isFullscreen ? 'radix-icons:exit-full-screen' : 'radix-icons:enter-full-screen'"
|
||||||
color="var(--el-color-info)"
|
color="var(--el-color-info)"
|
||||||
|
hover-color="var(--el-color-primary)"
|
||||||
@click="toggleFull"
|
@click="toggleFull"
|
||||||
/>
|
/>
|
||||||
|
<Icon
|
||||||
|
class="cursor-pointer is-hover !h-54px"
|
||||||
|
icon="ep:close"
|
||||||
|
hover-color="var(--el-color-primary)"
|
||||||
|
color="var(--el-color-info)"
|
||||||
|
@click="close"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -92,28 +105,28 @@ const dialogStyle = computed(() => {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
.@{elNamespace}-dialog__header {
|
.@{elNamespace}-overlay-dialog {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.@{elNamespace}-dialog {
|
||||||
|
margin: 0 !important;
|
||||||
|
&__header {
|
||||||
margin-right: 0 !important;
|
margin-right: 0 !important;
|
||||||
border-bottom: 1px solid var(--tags-view-border-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.@{elNamespace}-dialog__footer {
|
|
||||||
border-top: 1px solid var(--tags-view-border-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.is-hover {
|
|
||||||
&:hover {
|
|
||||||
color: var(--el-color-primary) !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark {
|
|
||||||
.@{elNamespace}-dialog__header {
|
|
||||||
border-bottom: 1px solid var(--el-border-color);
|
border-bottom: 1px solid var(--el-border-color);
|
||||||
|
padding: 0;
|
||||||
|
height: 54px;
|
||||||
}
|
}
|
||||||
|
&__body {
|
||||||
.@{elNamespace}-dialog__footer {
|
padding: 15px !important;
|
||||||
|
}
|
||||||
|
&__footer {
|
||||||
border-top: 1px solid var(--el-border-color);
|
border-top: 1px solid var(--el-border-color);
|
||||||
}
|
}
|
||||||
|
&__headerbtn {
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -99,7 +99,6 @@ const handleChange = (editor: IDomEditor) => {
|
|||||||
// 组件销毁时,及时销毁编辑器
|
// 组件销毁时,及时销毁编辑器
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
const editor = unref(editorRef.value)
|
const editor = unref(editorRef.value)
|
||||||
if (editor === null) return
|
|
||||||
|
|
||||||
// 销毁,并移除 editor
|
// 销毁,并移除 editor
|
||||||
editor?.destroy()
|
editor?.destroy()
|
||||||
@@ -116,12 +115,12 @@ defineExpose({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="border-1 border-solid border-[var(--tags-view-border-color)] z-99">
|
<div class="border-1 border-solid border-[var(--el-border-color)] z-10">
|
||||||
<!-- 工具栏 -->
|
<!-- 工具栏 -->
|
||||||
<Toolbar
|
<Toolbar
|
||||||
:editor="editorRef"
|
:editor="editorRef"
|
||||||
:editorId="editorId"
|
:editorId="editorId"
|
||||||
class="b-b-1 border-solid border-[var(--tags-view-border-color)]"
|
class="border-0 b-b-1 border-solid border-[var(--el-border-color)]"
|
||||||
/>
|
/>
|
||||||
<!-- 编辑器 -->
|
<!-- 编辑器 -->
|
||||||
<Editor
|
<Editor
|
||||||
|
|||||||
@@ -1,15 +1,48 @@
|
|||||||
import Form from './src/Form.vue'
|
import Form from './src/Form.vue'
|
||||||
import { ElForm } from 'element-plus'
|
import type { FormSchema, FormSetProps } from './src/types'
|
||||||
import { FormSchema, FormSetPropsType } from '@/types/form'
|
export type {
|
||||||
|
ComponentNameEnum,
|
||||||
|
ComponentName,
|
||||||
|
InputComponentProps,
|
||||||
|
AutocompleteComponentProps,
|
||||||
|
InputNumberComponentProps,
|
||||||
|
SelectOption,
|
||||||
|
SelectComponentProps,
|
||||||
|
SelectV2ComponentProps,
|
||||||
|
CascaderComponentProps,
|
||||||
|
SwitchComponentProps,
|
||||||
|
RateComponentProps,
|
||||||
|
ColorPickerComponentProps,
|
||||||
|
TransferComponentProps,
|
||||||
|
RadioOption,
|
||||||
|
RadioGroupComponentProps,
|
||||||
|
RadioButtonComponentProps,
|
||||||
|
CheckboxOption,
|
||||||
|
CheckboxGroupComponentProps,
|
||||||
|
DividerComponentProps,
|
||||||
|
DatePickerComponentProps,
|
||||||
|
DateTimePickerComponentProps,
|
||||||
|
TimePickerComponentProps,
|
||||||
|
TimeSelectComponentProps,
|
||||||
|
ColProps,
|
||||||
|
FormSetProps,
|
||||||
|
FormItemProps,
|
||||||
|
FormSchema,
|
||||||
|
FormProps,
|
||||||
|
PlaceholderModel,
|
||||||
|
InputPasswordComponentProps,
|
||||||
|
TreeSelectComponentProps
|
||||||
|
} from './src/types'
|
||||||
|
|
||||||
export interface FormExpose {
|
export interface FormExpose {
|
||||||
setValues: (data: Recordable) => void
|
setValues: (data: Recordable) => void
|
||||||
setProps: (props: Recordable) => void
|
setProps: (props: Recordable) => void
|
||||||
delSchema: (field: string) => void
|
delSchema: (field: string) => void
|
||||||
addSchema: (formSchema: FormSchema, index?: number) => void
|
addSchema: (formSchema: FormSchema, index?: number) => void
|
||||||
setSchema: (schemaProps: FormSetPropsType[]) => void
|
setSchema: (schemaProps: FormSetProps[]) => void
|
||||||
formModel: Recordable
|
formModel: Recordable
|
||||||
getElFormRef: () => ComponentRef<typeof ElForm>
|
getComponentExpose: (field: string) => any
|
||||||
|
getFormItemExpose: (field: string) => any
|
||||||
}
|
}
|
||||||
|
|
||||||
export { Form }
|
export { Form }
|
||||||
|
|||||||
@@ -1,302 +0,0 @@
|
|||||||
<script lang="tsx">
|
|
||||||
import { PropType, defineComponent, ref, computed, unref, watch, onMounted } from 'vue'
|
|
||||||
import { ElForm, ElFormItem, ElRow, ElCol, ElTooltip } from 'element-plus'
|
|
||||||
import { componentMap } from './componentMap'
|
|
||||||
import { propTypes } from '@/utils/propTypes'
|
|
||||||
import { getSlot } from '@/utils/tsxHelper'
|
|
||||||
import {
|
|
||||||
setTextPlaceholder,
|
|
||||||
setGridProp,
|
|
||||||
setComponentProps,
|
|
||||||
setItemComponentSlots,
|
|
||||||
initModel,
|
|
||||||
setFormItemSlots
|
|
||||||
} from './helper'
|
|
||||||
import { useRenderSelect } from './components/useRenderSelect'
|
|
||||||
import { useRenderRadio } from './components/useRenderRadio'
|
|
||||||
import { useRenderCheckbox } from './components/useRenderCheckbox'
|
|
||||||
import { useDesign } from '@/hooks/web/useDesign'
|
|
||||||
import { findIndex } from '@/utils'
|
|
||||||
import { set } from 'lodash-es'
|
|
||||||
import { FormProps } from './types'
|
|
||||||
import { Icon } from '@/components/Icon'
|
|
||||||
import { FormSchema, FormSetPropsType } from '@/types/form'
|
|
||||||
|
|
||||||
const { getPrefixCls } = useDesign()
|
|
||||||
|
|
||||||
const prefixCls = getPrefixCls('form')
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: 'Form',
|
|
||||||
props: {
|
|
||||||
// 生成Form的布局结构数组
|
|
||||||
schema: {
|
|
||||||
type: Array as PropType<FormSchema[]>,
|
|
||||||
default: () => []
|
|
||||||
},
|
|
||||||
// 是否需要栅格布局
|
|
||||||
isCol: propTypes.bool.def(true),
|
|
||||||
// 表单数据对象
|
|
||||||
model: {
|
|
||||||
type: Object as PropType<Recordable>,
|
|
||||||
default: () => ({})
|
|
||||||
},
|
|
||||||
// 是否自动设置placeholder
|
|
||||||
autoSetPlaceholder: propTypes.bool.def(true),
|
|
||||||
// 是否自定义内容
|
|
||||||
isCustom: propTypes.bool.def(false),
|
|
||||||
// 表单label宽度
|
|
||||||
labelWidth: propTypes.oneOfType([String, Number]).def('auto')
|
|
||||||
},
|
|
||||||
emits: ['register'],
|
|
||||||
setup(props, { slots, expose, emit }) {
|
|
||||||
// element form 实例
|
|
||||||
const elFormRef = ref<ComponentRef<typeof ElForm>>()
|
|
||||||
|
|
||||||
// useForm传入的props
|
|
||||||
const outsideProps = ref<FormProps>({})
|
|
||||||
|
|
||||||
const mergeProps = ref<FormProps>({})
|
|
||||||
|
|
||||||
const getProps = computed(() => {
|
|
||||||
const propsObj = { ...props }
|
|
||||||
Object.assign(propsObj, unref(mergeProps))
|
|
||||||
return propsObj
|
|
||||||
})
|
|
||||||
|
|
||||||
// 表单数据
|
|
||||||
const formModel = ref<Recordable>({})
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
emit('register', unref(elFormRef)?.$parent, unref(elFormRef))
|
|
||||||
})
|
|
||||||
|
|
||||||
// 对表单赋值
|
|
||||||
const setValues = (data: Recordable = {}) => {
|
|
||||||
formModel.value = Object.assign(unref(formModel), data)
|
|
||||||
}
|
|
||||||
|
|
||||||
const setProps = (props: FormProps = {}) => {
|
|
||||||
mergeProps.value = Object.assign(unref(mergeProps), props)
|
|
||||||
outsideProps.value = props
|
|
||||||
}
|
|
||||||
|
|
||||||
const delSchema = (field: string) => {
|
|
||||||
const { schema } = unref(getProps)
|
|
||||||
|
|
||||||
const index = findIndex(schema, (v: FormSchema) => v.field === field)
|
|
||||||
if (index > -1) {
|
|
||||||
schema.splice(index, 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const addSchema = (formSchema: FormSchema, index?: number) => {
|
|
||||||
const { schema } = unref(getProps)
|
|
||||||
if (index !== void 0) {
|
|
||||||
schema.splice(index, 0, formSchema)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
schema.push(formSchema)
|
|
||||||
}
|
|
||||||
|
|
||||||
const setSchema = (schemaProps: FormSetPropsType[]) => {
|
|
||||||
const { schema } = unref(getProps)
|
|
||||||
for (const v of schema) {
|
|
||||||
for (const item of schemaProps) {
|
|
||||||
if (v.field === item.field) {
|
|
||||||
set(v, item.path, item.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const getElFormRef = (): ComponentRef<typeof ElForm> => {
|
|
||||||
return unref(elFormRef) as ComponentRef<typeof ElForm>
|
|
||||||
}
|
|
||||||
|
|
||||||
expose({
|
|
||||||
setValues,
|
|
||||||
formModel,
|
|
||||||
setProps,
|
|
||||||
delSchema,
|
|
||||||
addSchema,
|
|
||||||
setSchema,
|
|
||||||
getElFormRef
|
|
||||||
})
|
|
||||||
|
|
||||||
// 监听表单结构化数组,重新生成formModel
|
|
||||||
watch(
|
|
||||||
() => unref(getProps).schema,
|
|
||||||
(schema = []) => {
|
|
||||||
formModel.value = initModel(schema, unref(formModel))
|
|
||||||
},
|
|
||||||
{
|
|
||||||
immediate: true,
|
|
||||||
deep: true
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// 渲染包裹标签,是否使用栅格布局
|
|
||||||
const renderWrap = () => {
|
|
||||||
const { isCol } = unref(getProps)
|
|
||||||
const content = isCol ? (
|
|
||||||
<ElRow gutter={20}>{renderFormItemWrap()}</ElRow>
|
|
||||||
) : (
|
|
||||||
renderFormItemWrap()
|
|
||||||
)
|
|
||||||
return content
|
|
||||||
}
|
|
||||||
|
|
||||||
// 是否要渲染el-col
|
|
||||||
const renderFormItemWrap = () => {
|
|
||||||
// hidden属性表示隐藏,不做渲染
|
|
||||||
const { schema = [], isCol } = unref(getProps)
|
|
||||||
|
|
||||||
return schema
|
|
||||||
.filter((v) => !v.hidden)
|
|
||||||
.map((item) => {
|
|
||||||
// 如果是 Divider 组件,需要自己占用一行
|
|
||||||
const isDivider = item.component === 'Divider'
|
|
||||||
const Com = componentMap['Divider'] as ReturnType<typeof defineComponent>
|
|
||||||
return isDivider ? (
|
|
||||||
<Com {...{ contentPosition: 'left', ...item.componentProps }}>{item?.label}</Com>
|
|
||||||
) : isCol ? (
|
|
||||||
// 如果需要栅格,需要包裹 ElCol
|
|
||||||
<ElCol {...setGridProp(item.colProps)}>{renderFormItem(item)}</ElCol>
|
|
||||||
) : (
|
|
||||||
renderFormItem(item)
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 渲染formItem
|
|
||||||
const renderFormItem = (item: FormSchema) => {
|
|
||||||
// 单独给只有options属性的组件做判断
|
|
||||||
const notRenderOptions = ['SelectV2', 'Cascader', 'Transfer']
|
|
||||||
const componentSlots = (item?.componentProps as any)?.slots || {}
|
|
||||||
const slotsMap: Recordable = {
|
|
||||||
...setItemComponentSlots(unref(formModel), componentSlots)
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
item?.component !== 'SelectV2' &&
|
|
||||||
item?.component !== 'Cascader' &&
|
|
||||||
item?.componentProps?.options
|
|
||||||
) {
|
|
||||||
slotsMap.default = () => renderOptions(item)
|
|
||||||
}
|
|
||||||
|
|
||||||
const formItemSlots: Recordable = setFormItemSlots(slots, item.field)
|
|
||||||
// 如果有 labelMessage,自动使用插槽渲染
|
|
||||||
if (item?.labelMessage) {
|
|
||||||
formItemSlots.label = () => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<span>{item.label}</span>
|
|
||||||
<ElTooltip placement="right" raw-content>
|
|
||||||
{{
|
|
||||||
content: () => <span v-html={item.labelMessage}></span>,
|
|
||||||
default: () => (
|
|
||||||
<Icon
|
|
||||||
icon="ep:warning"
|
|
||||||
size={16}
|
|
||||||
color="var(--el-color-primary)"
|
|
||||||
class="ml-2px relative top-1px"
|
|
||||||
></Icon>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</ElTooltip>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<ElFormItem {...(item.formItemProps || {})} prop={item.field} label={item.label || ''}>
|
|
||||||
{{
|
|
||||||
...formItemSlots,
|
|
||||||
default: () => {
|
|
||||||
const Com = componentMap[item.component as string] as ReturnType<
|
|
||||||
typeof defineComponent
|
|
||||||
>
|
|
||||||
|
|
||||||
const { autoSetPlaceholder } = unref(getProps)
|
|
||||||
|
|
||||||
return slots[item.field] ? (
|
|
||||||
getSlot(slots, item.field, formModel.value)
|
|
||||||
) : (
|
|
||||||
<Com
|
|
||||||
vModel={formModel.value[item.field]}
|
|
||||||
{...(autoSetPlaceholder && setTextPlaceholder(item))}
|
|
||||||
{...setComponentProps(item)}
|
|
||||||
style={item.componentProps?.style}
|
|
||||||
{...(notRenderOptions.includes(item?.component as string) &&
|
|
||||||
item?.componentProps?.options
|
|
||||||
? { options: item?.componentProps?.options || [] }
|
|
||||||
: {})}
|
|
||||||
>
|
|
||||||
{{ ...slotsMap }}
|
|
||||||
</Com>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
</ElFormItem>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 渲染options
|
|
||||||
const renderOptions = (item: FormSchema) => {
|
|
||||||
switch (item.component) {
|
|
||||||
case 'Select':
|
|
||||||
const { renderSelectOptions } = useRenderSelect(slots)
|
|
||||||
return renderSelectOptions(item)
|
|
||||||
case 'Radio':
|
|
||||||
case 'RadioButton':
|
|
||||||
const { renderRadioOptions } = useRenderRadio()
|
|
||||||
return renderRadioOptions(item)
|
|
||||||
case 'Checkbox':
|
|
||||||
case 'CheckboxButton':
|
|
||||||
const { renderCheckboxOptions } = useRenderCheckbox()
|
|
||||||
return renderCheckboxOptions(item)
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 过滤传入Form组件的属性
|
|
||||||
const getFormBindValue = () => {
|
|
||||||
// 避免在标签上出现多余的属性
|
|
||||||
const delKeys = ['schema', 'isCol', 'autoSetPlaceholder', 'isCustom', 'model']
|
|
||||||
const props = { ...unref(getProps) }
|
|
||||||
for (const key in props) {
|
|
||||||
if (delKeys.indexOf(key) !== -1) {
|
|
||||||
delete props[key]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return props
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => (
|
|
||||||
<ElForm
|
|
||||||
ref={elFormRef}
|
|
||||||
{...getFormBindValue()}
|
|
||||||
model={props.isCustom ? props.model : formModel}
|
|
||||||
class={prefixCls}
|
|
||||||
>
|
|
||||||
{{
|
|
||||||
// 如果需要自定义,就什么都不渲染,而是提供默认插槽
|
|
||||||
default: () => {
|
|
||||||
const { isCustom } = unref(getProps)
|
|
||||||
return isCustom ? getSlot(slots, 'default') : renderWrap()
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
</ElForm>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="less" scoped>
|
|
||||||
.@{elNamespace}-form.@{namespace}-form .@{elNamespace}-row {
|
|
||||||
margin-right: 0 !important;
|
|
||||||
margin-left: 0 !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,7 +1,15 @@
|
|||||||
<script lang="tsx">
|
<script lang="tsx">
|
||||||
import { PropType, defineComponent, ref, computed, unref, watch, onMounted } from 'vue'
|
import { PropType, defineComponent, ref, computed, unref, watch, onMounted } from 'vue'
|
||||||
import { ElForm, ElFormItem, ElRow, ElCol, ElTooltip } from 'element-plus'
|
import {
|
||||||
import { componentMap } from './componentMap'
|
ElForm,
|
||||||
|
ElFormItem,
|
||||||
|
ElRow,
|
||||||
|
ElCol,
|
||||||
|
FormRules,
|
||||||
|
ComponentSize
|
||||||
|
// FormItemProp
|
||||||
|
} from 'element-plus'
|
||||||
|
import { componentMap } from './helper/componentMap'
|
||||||
import { propTypes } from '@/utils/propTypes'
|
import { propTypes } from '@/utils/propTypes'
|
||||||
import { getSlot } from '@/utils/tsxHelper'
|
import { getSlot } from '@/utils/tsxHelper'
|
||||||
import {
|
import {
|
||||||
@@ -9,19 +17,27 @@ import {
|
|||||||
setGridProp,
|
setGridProp,
|
||||||
setComponentProps,
|
setComponentProps,
|
||||||
setItemComponentSlots,
|
setItemComponentSlots,
|
||||||
initModel,
|
initModel
|
||||||
setFormItemSlots
|
|
||||||
} from './helper'
|
} from './helper'
|
||||||
import { useRenderSelect } from './components/useRenderSelect'
|
import { useRenderSelect } from './components/useRenderSelect'
|
||||||
import { useRenderRadio } from './components/useRenderRadio'
|
import { useRenderRadio } from './components/useRenderRadio'
|
||||||
import { useRenderCheckbox } from './components/useRenderCheckbox'
|
import { useRenderCheckbox } from './components/useRenderCheckbox'
|
||||||
import { useDesign } from '@/hooks/web/useDesign'
|
import { useDesign } from '@/hooks/web/useDesign'
|
||||||
import { findIndex } from '@/utils'
|
import { findIndex } from '@/utils'
|
||||||
import { set } from 'lodash-es'
|
import { get, set } from 'lodash-es'
|
||||||
import { FormProps } from './types'
|
import { FormProps } from './types'
|
||||||
import { Icon } from '@/components/Icon'
|
import {
|
||||||
import { FormSchema, FormSetPropsType } from '@/types/form'
|
FormSchema,
|
||||||
import { ComponentNameEnum, SelectComponentProps } from '@/types/components.d'
|
FormSetProps,
|
||||||
|
ComponentNameEnum,
|
||||||
|
SelectComponentProps,
|
||||||
|
RadioGroupComponentProps,
|
||||||
|
CheckboxGroupComponentProps
|
||||||
|
} from './types'
|
||||||
|
|
||||||
|
const { renderSelectOptions } = useRenderSelect()
|
||||||
|
const { renderRadioOptions } = useRenderRadio()
|
||||||
|
const { renderCheckboxOptions } = useRenderCheckbox()
|
||||||
|
|
||||||
const { getPrefixCls } = useDesign()
|
const { getPrefixCls } = useDesign()
|
||||||
|
|
||||||
@@ -39,7 +55,7 @@ export default defineComponent({
|
|||||||
isCol: propTypes.bool.def(true),
|
isCol: propTypes.bool.def(true),
|
||||||
// 表单数据对象
|
// 表单数据对象
|
||||||
model: {
|
model: {
|
||||||
type: Object as PropType<Recordable>,
|
type: Object as PropType<any>,
|
||||||
default: () => ({})
|
default: () => ({})
|
||||||
},
|
},
|
||||||
// 是否自动设置placeholder
|
// 是否自动设置placeholder
|
||||||
@@ -47,7 +63,30 @@ export default defineComponent({
|
|||||||
// 是否自定义内容
|
// 是否自定义内容
|
||||||
isCustom: propTypes.bool.def(false),
|
isCustom: propTypes.bool.def(false),
|
||||||
// 表单label宽度
|
// 表单label宽度
|
||||||
labelWidth: propTypes.oneOfType([String, Number]).def('auto')
|
labelWidth: propTypes.oneOfType([String, Number]).def('auto'),
|
||||||
|
rules: {
|
||||||
|
type: Object as PropType<FormRules>,
|
||||||
|
default: () => ({})
|
||||||
|
},
|
||||||
|
labelPosition: propTypes.oneOf(['left', 'right', 'top']).def('right'),
|
||||||
|
labelSuffix: propTypes.string.def(''),
|
||||||
|
hideRequiredAsterisk: propTypes.bool.def(false),
|
||||||
|
requireAsteriskPosition: propTypes.oneOf(['left', 'right']).def('left'),
|
||||||
|
showMessage: propTypes.bool.def(true),
|
||||||
|
inlineMessage: propTypes.bool.def(false),
|
||||||
|
statusIcon: propTypes.bool.def(false),
|
||||||
|
validateOnRuleChange: propTypes.bool.def(true),
|
||||||
|
size: {
|
||||||
|
type: String as PropType<ComponentSize>,
|
||||||
|
default: undefined
|
||||||
|
},
|
||||||
|
disabled: propTypes.bool.def(false),
|
||||||
|
scrollToError: propTypes.bool.def(false),
|
||||||
|
scrollToErrorOffset: propTypes.oneOfType([Boolean, Object]).def(undefined)
|
||||||
|
// onValidate: {
|
||||||
|
// type: Function as PropType<(prop: FormItemProp, isValid: boolean, message: string) => void>,
|
||||||
|
// default: () => {}
|
||||||
|
// }
|
||||||
},
|
},
|
||||||
emits: ['register'],
|
emits: ['register'],
|
||||||
setup(props, { slots, expose, emit }) {
|
setup(props, { slots, expose, emit }) {
|
||||||
@@ -65,8 +104,14 @@ export default defineComponent({
|
|||||||
return propsObj
|
return propsObj
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 存储表单实例
|
||||||
|
const formComponents = ref({})
|
||||||
|
|
||||||
|
// 存储form-item实例
|
||||||
|
const formItemComponents = ref({})
|
||||||
|
|
||||||
// 表单数据
|
// 表单数据
|
||||||
const formModel = ref<Recordable>({})
|
const formModel = ref<Recordable>(props.model)
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
emit('register', unref(elFormRef)?.$parent, unref(elFormRef))
|
emit('register', unref(elFormRef)?.$parent, unref(elFormRef))
|
||||||
@@ -79,6 +124,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
const setProps = (props: FormProps = {}) => {
|
const setProps = (props: FormProps = {}) => {
|
||||||
mergeProps.value = Object.assign(unref(mergeProps), props)
|
mergeProps.value = Object.assign(unref(mergeProps), props)
|
||||||
|
// @ts-ignore
|
||||||
outsideProps.value = props
|
outsideProps.value = props
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,7 +146,7 @@ export default defineComponent({
|
|||||||
schema.push(formSchema)
|
schema.push(formSchema)
|
||||||
}
|
}
|
||||||
|
|
||||||
const setSchema = (schemaProps: FormSetPropsType[]) => {
|
const setSchema = (schemaProps: FormSetProps[]) => {
|
||||||
const { schema } = unref(getProps)
|
const { schema } = unref(getProps)
|
||||||
for (const v of schema) {
|
for (const v of schema) {
|
||||||
for (const item of schemaProps) {
|
for (const item of schemaProps) {
|
||||||
@@ -111,8 +157,42 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const getElFormRef = (): ComponentRef<typeof ElForm> => {
|
const getOptions = async (fn: Function, item: FormSchema) => {
|
||||||
return unref(elFormRef) as ComponentRef<typeof ElForm>
|
const options = await fn()
|
||||||
|
setSchema([
|
||||||
|
{
|
||||||
|
field: item.field,
|
||||||
|
path:
|
||||||
|
item.component === ComponentNameEnum.TREE_SELECT
|
||||||
|
? 'componentProps.data'
|
||||||
|
: 'componentProps.options',
|
||||||
|
value: options
|
||||||
|
}
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 获取表单组件实例
|
||||||
|
* @param filed 表单字段
|
||||||
|
*/
|
||||||
|
const getComponentExpose = (filed: string) => {
|
||||||
|
return unref(formComponents)[filed]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 获取formItem实例
|
||||||
|
* @param filed 表单字段
|
||||||
|
*/
|
||||||
|
const getFormItemExpose = (filed: string) => {
|
||||||
|
return unref(formItemComponents)[filed]
|
||||||
|
}
|
||||||
|
|
||||||
|
const setComponentRefMap = (ref: any, filed: string) => {
|
||||||
|
formComponents.value[filed] = ref
|
||||||
|
}
|
||||||
|
|
||||||
|
const setFormItemRefMap = (ref: any, filed: string) => {
|
||||||
|
formItemComponents.value[filed] = ref
|
||||||
}
|
}
|
||||||
|
|
||||||
expose({
|
expose({
|
||||||
@@ -122,7 +202,8 @@ export default defineComponent({
|
|||||||
delSchema,
|
delSchema,
|
||||||
addSchema,
|
addSchema,
|
||||||
setSchema,
|
setSchema,
|
||||||
getElFormRef
|
getComponentExpose,
|
||||||
|
getFormItemExpose
|
||||||
})
|
})
|
||||||
|
|
||||||
// 监听表单结构化数组,重新生成formModel
|
// 监听表单结构化数组,重新生成formModel
|
||||||
@@ -154,7 +235,7 @@ export default defineComponent({
|
|||||||
const { schema = [], isCol } = unref(getProps)
|
const { schema = [], isCol } = unref(getProps)
|
||||||
|
|
||||||
return schema
|
return schema
|
||||||
.filter((v) => !v.hidden)
|
.filter((v) => !v.remove)
|
||||||
.map((item) => {
|
.map((item) => {
|
||||||
// 如果是 Divider 组件,需要自己占用一行
|
// 如果是 Divider 组件,需要自己占用一行
|
||||||
const isDivider = item.component === 'Divider'
|
const isDivider = item.component === 'Divider'
|
||||||
@@ -172,101 +253,121 @@ export default defineComponent({
|
|||||||
|
|
||||||
// 渲染formItem
|
// 渲染formItem
|
||||||
const renderFormItem = (item: FormSchema) => {
|
const renderFormItem = (item: FormSchema) => {
|
||||||
// 单独给只有options属性的组件做判断
|
// 如果有optionApi,优先使用optionApi
|
||||||
// const notRenderOptions = ['SelectV2', 'Cascader', 'Transfer']
|
if (item.optionApi) {
|
||||||
|
// 内部自动调用接口,不影响其它渲染
|
||||||
|
getOptions(item.optionApi, item)
|
||||||
|
}
|
||||||
|
const formItemSlots: Recordable = {
|
||||||
|
default: () => {
|
||||||
|
if (item?.formItemProps?.slots?.default) {
|
||||||
|
return item?.formItemProps?.slots?.default(formModel.value)
|
||||||
|
} else {
|
||||||
|
const Com = componentMap[item.component as string] as ReturnType<typeof defineComponent>
|
||||||
|
|
||||||
|
const { autoSetPlaceholder } = unref(getProps)
|
||||||
|
|
||||||
const componentSlots = (item?.componentProps as any)?.slots || {}
|
const componentSlots = (item?.componentProps as any)?.slots || {}
|
||||||
const slotsMap: Recordable = {
|
const slotsMap: Recordable = {
|
||||||
...setItemComponentSlots(unref(formModel), componentSlots)
|
...setItemComponentSlots(componentSlots)
|
||||||
}
|
}
|
||||||
// 如果是select组件,并且没有自定义模板,自动渲染options
|
// // 如果是select组件,并且没有自定义模板,自动渲染options
|
||||||
if (item.component === ComponentNameEnum.SELECT) {
|
if (item.component === ComponentNameEnum.SELECT) {
|
||||||
slotsMap.default = !componentSlots.default
|
slotsMap.default = !componentSlots.default
|
||||||
? () => renderOptions(item)
|
? () => renderSelectOptions(item)
|
||||||
: () => {
|
: () => {
|
||||||
return componentSlots.default(
|
return componentSlots.default(
|
||||||
unref((item?.componentProps as SelectComponentProps)?.options)
|
unref((item?.componentProps as SelectComponentProps)?.options)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// if (
|
|
||||||
// item?.component !== 'SelectV2' &&
|
|
||||||
// item?.component !== 'Cascader' &&
|
|
||||||
// item?.componentProps?.options
|
|
||||||
// ) {
|
|
||||||
// slotsMap.default = () => renderOptions(item)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const formItemSlots: Recordable = setFormItemSlots(slots, item.field)
|
// 虚拟列表
|
||||||
// 如果有 labelMessage,自动使用插槽渲染
|
if (item.component === ComponentNameEnum.SELECT_V2 && componentSlots.default) {
|
||||||
// if (item?.labelMessage) {
|
slotsMap.default = ({ item }) => {
|
||||||
// formItemSlots.label = () => {
|
return componentSlots.default(item)
|
||||||
// return (
|
}
|
||||||
// <>
|
}
|
||||||
// <span>{item.label}</span>
|
|
||||||
// <ElTooltip placement="right" raw-content>
|
// 单选框组和按钮样式
|
||||||
// {{
|
if (
|
||||||
// content: () => <span v-html={item.labelMessage}></span>,
|
item.component === ComponentNameEnum.RADIO_GROUP ||
|
||||||
// default: () => (
|
item.component === ComponentNameEnum.RADIO_BUTTON
|
||||||
// <Icon
|
) {
|
||||||
// icon="ep:warning"
|
slotsMap.default = !componentSlots.default
|
||||||
// size={16}
|
? () => renderRadioOptions(item)
|
||||||
// color="var(--el-color-primary)"
|
: () => {
|
||||||
// class="ml-2px relative top-1px"
|
return componentSlots.default(
|
||||||
// ></Icon>
|
unref((item?.componentProps as CheckboxGroupComponentProps)?.options)
|
||||||
// )
|
)
|
||||||
// }}
|
}
|
||||||
// </ElTooltip>
|
}
|
||||||
// </>
|
|
||||||
// )
|
// 多选框组和按钮样式
|
||||||
// }
|
if (
|
||||||
// }
|
item.component === ComponentNameEnum.CHECKBOX_GROUP ||
|
||||||
|
item.component === ComponentNameEnum.CHECKBOX_BUTTON
|
||||||
|
) {
|
||||||
|
slotsMap.default = !componentSlots.default
|
||||||
|
? () => renderCheckboxOptions(item)
|
||||||
|
: () => {
|
||||||
|
return componentSlots.default(
|
||||||
|
unref((item?.componentProps as RadioGroupComponentProps)?.options)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Comp = () => {
|
||||||
|
// 如果field是多层路径,需要转换成对象
|
||||||
|
const itemVal = computed({
|
||||||
|
get: () => {
|
||||||
|
return get(formModel.value, item.field)
|
||||||
|
},
|
||||||
|
set: (val) => {
|
||||||
|
set(formModel.value, item.field, val)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ElFormItem {...(item.formItemProps || {})} prop={item.field} label={item.label || ''}>
|
|
||||||
{{
|
|
||||||
default: () => {
|
|
||||||
const Com = componentMap[item.component as string] as ReturnType<
|
|
||||||
typeof defineComponent
|
|
||||||
>
|
|
||||||
|
|
||||||
const { autoSetPlaceholder } = unref(getProps)
|
|
||||||
|
|
||||||
return slots[item.field] ? (
|
|
||||||
getSlot(slots, item.field, formModel.value)
|
|
||||||
) : (
|
|
||||||
<Com
|
<Com
|
||||||
vModel={formModel.value[item.field]}
|
vModel={itemVal.value}
|
||||||
|
ref={(el: any) => setComponentRefMap(el, item.field)}
|
||||||
{...(autoSetPlaceholder && setTextPlaceholder(item))}
|
{...(autoSetPlaceholder && setTextPlaceholder(item))}
|
||||||
{...setComponentProps(item)}
|
{...setComponentProps(item)}
|
||||||
style={item.componentProps?.style}
|
style={item.componentProps?.style || {}}
|
||||||
>
|
>
|
||||||
{{ ...slotsMap }}
|
{{ ...slotsMap }}
|
||||||
</Com>
|
</Com>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}}
|
|
||||||
|
return <>{Comp()}</>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (item?.formItemProps?.slots?.label) {
|
||||||
|
formItemSlots.label = (...args: any[]) => {
|
||||||
|
return (item?.formItemProps?.slots as any)?.label(...args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (item?.formItemProps?.slots?.error) {
|
||||||
|
formItemSlots.error = (...args: any[]) => {
|
||||||
|
return (item?.formItemProps?.slots as any)?.error(...args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<ElFormItem
|
||||||
|
v-show={!item.hidden}
|
||||||
|
ref={(el: any) => setFormItemRefMap(el, item.field)}
|
||||||
|
{...(item.formItemProps || {})}
|
||||||
|
prop={item.field}
|
||||||
|
label={item.label || ''}
|
||||||
|
>
|
||||||
|
{formItemSlots}
|
||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 渲染options
|
|
||||||
const renderOptions = (item: FormSchema) => {
|
|
||||||
switch (item.component) {
|
|
||||||
case ComponentNameEnum.SELECT:
|
|
||||||
const { renderSelectOptions } = useRenderSelect(slots)
|
|
||||||
return renderSelectOptions(item)
|
|
||||||
case 'Radio':
|
|
||||||
case 'RadioButton':
|
|
||||||
const { renderRadioOptions } = useRenderRadio()
|
|
||||||
return renderRadioOptions(item)
|
|
||||||
case 'Checkbox':
|
|
||||||
case 'CheckboxButton':
|
|
||||||
const { renderCheckboxOptions } = useRenderCheckbox()
|
|
||||||
return renderCheckboxOptions(item)
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 过滤传入Form组件的属性
|
// 过滤传入Form组件的属性
|
||||||
const getFormBindValue = () => {
|
const getFormBindValue = () => {
|
||||||
// 避免在标签上出现多余的属性
|
// 避免在标签上出现多余的属性
|
||||||
@@ -277,14 +378,14 @@ export default defineComponent({
|
|||||||
delete props[key]
|
delete props[key]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return props
|
return props as FormProps
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => (
|
return () => (
|
||||||
<ElForm
|
<ElForm
|
||||||
ref={elFormRef}
|
ref={elFormRef}
|
||||||
{...getFormBindValue()}
|
{...getFormBindValue()}
|
||||||
model={props.isCustom ? props.model : formModel}
|
model={unref(getProps).isCustom ? unref(getProps).model : formModel}
|
||||||
class={prefixCls}
|
class={prefixCls}
|
||||||
>
|
>
|
||||||
{{
|
{{
|
||||||
|
|||||||
@@ -1,19 +1,25 @@
|
|||||||
import { FormSchema } from '@/types/form'
|
import { FormSchema, ComponentNameEnum, CheckboxGroupComponentProps } from '../types'
|
||||||
import { ElCheckbox, ElCheckboxButton } from 'element-plus'
|
import { ElCheckbox, ElCheckboxButton } from 'element-plus'
|
||||||
import { defineComponent } from 'vue'
|
import { defineComponent } from 'vue'
|
||||||
|
|
||||||
export const useRenderCheckbox = () => {
|
export const useRenderCheckbox = () => {
|
||||||
const renderCheckboxOptions = (item: FormSchema) => {
|
const renderCheckboxOptions = (item: FormSchema) => {
|
||||||
// 如果有别名,就取别名
|
// 如果有别名,就取别名
|
||||||
const labelAlias = item?.componentProps?.optionsAlias?.labelField
|
const componentProps = item?.componentProps as CheckboxGroupComponentProps
|
||||||
const valueAlias = item?.componentProps?.optionsAlias?.valueField
|
const valueAlias = componentProps?.props?.value || 'value'
|
||||||
const Com = (item.component === 'Checkbox' ? ElCheckbox : ElCheckboxButton) as ReturnType<
|
const labelAlias = componentProps?.props?.label || 'label'
|
||||||
typeof defineComponent
|
const disabledAlias = componentProps?.props?.disabled || 'disabled'
|
||||||
>
|
const Com = (
|
||||||
return item?.componentProps?.options?.map((option) => {
|
item.component === ComponentNameEnum.CHECKBOX_GROUP ? ElCheckbox : ElCheckboxButton
|
||||||
|
) as ReturnType<typeof defineComponent>
|
||||||
|
return componentProps?.options?.map((option) => {
|
||||||
const { value, ...other } = option
|
const { value, ...other } = option
|
||||||
return (
|
return (
|
||||||
<Com {...other} label={option[valueAlias || 'value']}>
|
<Com
|
||||||
|
{...other}
|
||||||
|
disabled={option[disabledAlias || 'disabled']}
|
||||||
|
label={option[valueAlias || 'value']}
|
||||||
|
>
|
||||||
{option[labelAlias || 'label']}
|
{option[labelAlias || 'label']}
|
||||||
</Com>
|
</Com>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,19 +1,25 @@
|
|||||||
import { FormSchema } from '@/types/form'
|
import { FormSchema, ComponentNameEnum, RadioGroupComponentProps } from '../types'
|
||||||
import { ElRadio, ElRadioButton } from 'element-plus'
|
import { ElRadio, ElRadioButton } from 'element-plus'
|
||||||
import { defineComponent } from 'vue'
|
import { defineComponent } from 'vue'
|
||||||
|
|
||||||
export const useRenderRadio = () => {
|
export const useRenderRadio = () => {
|
||||||
const renderRadioOptions = (item: FormSchema) => {
|
const renderRadioOptions = (item: FormSchema) => {
|
||||||
// 如果有别名,就取别名
|
// 如果有别名,就取别名
|
||||||
const labelAlias = item?.componentProps?.optionsAlias?.labelField
|
const componentProps = item?.componentProps as RadioGroupComponentProps
|
||||||
const valueAlias = item?.componentProps?.optionsAlias?.valueField
|
const valueAlias = componentProps?.props?.value || 'value'
|
||||||
const Com = (item.component === 'Radio' ? ElRadio : ElRadioButton) as ReturnType<
|
const labelAlias = componentProps?.props?.label || 'label'
|
||||||
typeof defineComponent
|
const disabledAlias = componentProps?.props?.disabled || 'disabled'
|
||||||
>
|
const Com = (
|
||||||
return item?.componentProps?.options?.map((option) => {
|
item.component === ComponentNameEnum.RADIO_GROUP ? ElRadio : ElRadioButton
|
||||||
|
) as ReturnType<typeof defineComponent>
|
||||||
|
return componentProps?.options?.map((option) => {
|
||||||
const { value, ...other } = option
|
const { value, ...other } = option
|
||||||
return (
|
return (
|
||||||
<Com {...other} label={option[valueAlias || 'value']}>
|
<Com
|
||||||
|
{...other}
|
||||||
|
disabled={option[disabledAlias || 'disabled']}
|
||||||
|
label={option[valueAlias || 'value']}
|
||||||
|
>
|
||||||
{option[labelAlias || 'label']}
|
{option[labelAlias || 'label']}
|
||||||
</Com>
|
</Com>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,22 +1,20 @@
|
|||||||
import { ElOption, ElOptionGroup } from 'element-plus'
|
import { ElOption, ElOptionGroup } from 'element-plus'
|
||||||
import { getSlot } from '@/utils/tsxHelper'
|
import { FormSchema, SelectComponentProps, SelectOption } from '../types'
|
||||||
import { Slots } from 'vue'
|
|
||||||
import { FormSchema } from '@/types/form'
|
|
||||||
import { SelectComponentProps, SelectOption } from '@/types/components'
|
|
||||||
|
|
||||||
export const useRenderSelect = (slots: Slots) => {
|
export const useRenderSelect = () => {
|
||||||
// 渲染 select options
|
// 渲染 select options
|
||||||
const renderSelectOptions = (item: FormSchema) => {
|
const renderSelectOptions = (item: FormSchema) => {
|
||||||
const componentsProps = item.componentProps as SelectComponentProps
|
const componentsProps = item?.componentProps as SelectComponentProps
|
||||||
const optionGroupDefaultSlot = componentsProps.slots?.optionGroupDefault
|
const optionGroupDefaultSlot = componentsProps?.slots?.optionGroupDefault
|
||||||
// 如果有别名,就取别名
|
// 如果有别名,就取别名
|
||||||
const labelAlias = componentsProps?.labelAlias
|
const labelAlias = componentsProps?.props?.label
|
||||||
|
const keyAlias = componentsProps?.props?.key
|
||||||
return componentsProps?.options?.map((option) => {
|
return componentsProps?.options?.map((option) => {
|
||||||
if (option?.options?.length) {
|
if (option?.options?.length) {
|
||||||
return optionGroupDefaultSlot ? (
|
return optionGroupDefaultSlot ? (
|
||||||
optionGroupDefaultSlot(option)
|
optionGroupDefaultSlot(option)
|
||||||
) : (
|
) : (
|
||||||
<ElOptionGroup label={option[labelAlias || 'label']}>
|
<ElOptionGroup label={option[labelAlias || 'label']} key={option[keyAlias || 'key']}>
|
||||||
{{
|
{{
|
||||||
default: () =>
|
default: () =>
|
||||||
option?.options?.map((v) => {
|
option?.options?.map((v) => {
|
||||||
@@ -35,17 +33,17 @@ export const useRenderSelect = (slots: Slots) => {
|
|||||||
const renderSelectOptionItem = (item: FormSchema, option: SelectOption) => {
|
const renderSelectOptionItem = (item: FormSchema, option: SelectOption) => {
|
||||||
// 如果有别名,就取别名
|
// 如果有别名,就取别名
|
||||||
const componentsProps = item.componentProps as SelectComponentProps
|
const componentsProps = item.componentProps as SelectComponentProps
|
||||||
const labelAlias = componentsProps?.labelAlias
|
const labelAlias = componentsProps?.props?.label
|
||||||
const valueAlias = componentsProps?.valueAlias
|
const valueAlias = componentsProps?.props?.value
|
||||||
|
const keyAlias = componentsProps?.props?.key
|
||||||
const optionDefaultSlot = componentsProps.slots?.optionDefault
|
const optionDefaultSlot = componentsProps.slots?.optionDefault
|
||||||
|
|
||||||
const { label, value, ...other } = option
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ElOption
|
<ElOption
|
||||||
{...other}
|
{...option}
|
||||||
label={labelAlias ? option[labelAlias] : label}
|
key={option[keyAlias || 'key']}
|
||||||
value={valueAlias ? option[valueAlias] : value}
|
label={option[labelAlias || 'label']}
|
||||||
|
value={option[valueAlias || 'value']}
|
||||||
>
|
>
|
||||||
{{
|
{{
|
||||||
default: () => (optionDefaultSlot ? optionDefaultSlot(option) : undefined)
|
default: () => (optionDefaultSlot ? optionDefaultSlot(option) : undefined)
|
||||||
|
|||||||
@@ -16,15 +16,18 @@ import {
|
|||||||
ElTimeSelect,
|
ElTimeSelect,
|
||||||
ElTransfer,
|
ElTransfer,
|
||||||
ElAutocomplete,
|
ElAutocomplete,
|
||||||
ElDivider
|
ElDivider,
|
||||||
|
ElTreeSelect,
|
||||||
|
ElUpload
|
||||||
} from 'element-plus'
|
} from 'element-plus'
|
||||||
import { InputPassword } from '@/components/InputPassword'
|
import { InputPassword } from '@/components/InputPassword'
|
||||||
import { Editor } from '@/components/Editor'
|
import { Editor } from '@/components/Editor'
|
||||||
import { ComponentName } from '@/types/components'
|
import { ComponentName } from '../types'
|
||||||
|
|
||||||
const componentMap: Recordable<Component, ComponentName> = {
|
const componentMap: Recordable<Component, ComponentName> = {
|
||||||
Radio: ElRadioGroup,
|
RadioGroup: ElRadioGroup,
|
||||||
Checkbox: ElCheckboxGroup,
|
RadioButton: ElRadioGroup,
|
||||||
|
CheckboxGroup: ElCheckboxGroup,
|
||||||
CheckboxButton: ElCheckboxGroup,
|
CheckboxButton: ElCheckboxGroup,
|
||||||
Input: ElInput,
|
Input: ElInput,
|
||||||
Autocomplete: ElAutocomplete,
|
Autocomplete: ElAutocomplete,
|
||||||
@@ -41,9 +44,10 @@ const componentMap: Recordable<Component, ComponentName> = {
|
|||||||
Divider: ElDivider,
|
Divider: ElDivider,
|
||||||
TimeSelect: ElTimeSelect,
|
TimeSelect: ElTimeSelect,
|
||||||
SelectV2: ElSelectV2,
|
SelectV2: ElSelectV2,
|
||||||
RadioButton: ElRadioGroup,
|
|
||||||
InputPassword: InputPassword,
|
InputPassword: InputPassword,
|
||||||
Editor: Editor
|
Editor: Editor,
|
||||||
|
TreeSelect: ElTreeSelect,
|
||||||
|
Upload: ElUpload
|
||||||
}
|
}
|
||||||
|
|
||||||
export { componentMap }
|
export { componentMap }
|
||||||
@@ -1,10 +1,8 @@
|
|||||||
import { useI18n } from '@/hooks/web/useI18n'
|
import { useI18n } from '@/hooks/web/useI18n'
|
||||||
import { unref, type Slots } from 'vue'
|
import { PlaceholderModel, FormSchema, ComponentNameEnum, ColProps } from '../types'
|
||||||
import { getSlot } from '@/utils/tsxHelper'
|
|
||||||
import { PlaceholderMoel } from './types'
|
|
||||||
import { FormSchema } from '@/types/form'
|
|
||||||
import { ColProps } from '@/types/components'
|
|
||||||
import { isFunction } from '@/utils/is'
|
import { isFunction } from '@/utils/is'
|
||||||
|
import { firstUpperCase, humpToDash } from '@/utils'
|
||||||
|
import { set, get } from 'lodash-es'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
@@ -14,20 +12,32 @@ const { t } = useI18n()
|
|||||||
* @returns 返回提示信息对象
|
* @returns 返回提示信息对象
|
||||||
* @description 用于自动设置placeholder
|
* @description 用于自动设置placeholder
|
||||||
*/
|
*/
|
||||||
export const setTextPlaceholder = (schema: FormSchema): PlaceholderMoel => {
|
export const setTextPlaceholder = (schema: FormSchema): PlaceholderModel => {
|
||||||
const textMap = ['Input', 'Autocomplete', 'InputNumber', 'InputPassword']
|
const textMap = [
|
||||||
const selectMap = ['Select', 'TimePicker', 'DatePicker', 'TimeSelect', 'TimeSelect']
|
ComponentNameEnum.INPUT,
|
||||||
if (textMap.includes(schema?.component as string)) {
|
ComponentNameEnum.AUTOCOMPLETE,
|
||||||
|
ComponentNameEnum.INPUT_NUMBER,
|
||||||
|
ComponentNameEnum.INPUT_PASSWORD
|
||||||
|
]
|
||||||
|
const selectMap = [
|
||||||
|
ComponentNameEnum.SELECT,
|
||||||
|
ComponentNameEnum.TIME_PICKER,
|
||||||
|
ComponentNameEnum.DATE_PICKER,
|
||||||
|
ComponentNameEnum.TIME_SELECT,
|
||||||
|
ComponentNameEnum.SELECT_V2
|
||||||
|
]
|
||||||
|
if (textMap.includes(schema?.component as ComponentNameEnum)) {
|
||||||
return {
|
return {
|
||||||
placeholder: t('common.inputText')
|
placeholder: t('common.inputText')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (selectMap.includes(schema?.component as string)) {
|
if (selectMap.includes(schema?.component as ComponentNameEnum)) {
|
||||||
// 一些范围选择器
|
// 一些范围选择器
|
||||||
const twoTextMap = ['datetimerange', 'daterange', 'monthrange', 'datetimerange', 'daterange']
|
const twoTextMap = ['datetimerange', 'daterange', 'monthrange', 'datetimerange', 'daterange']
|
||||||
if (
|
if (
|
||||||
twoTextMap.includes(
|
twoTextMap.includes(
|
||||||
(schema?.componentProps?.type || schema?.componentProps?.isRange) as string
|
((schema?.componentProps as any)?.type ||
|
||||||
|
(schema?.componentProps as any)?.isRange) as string
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
@@ -74,14 +84,30 @@ export const setGridProp = (col: ColProps = {}): ColProps => {
|
|||||||
*/
|
*/
|
||||||
export const setComponentProps = (item: FormSchema): Recordable => {
|
export const setComponentProps = (item: FormSchema): Recordable => {
|
||||||
// const notNeedClearable = ['ColorPicker']
|
// const notNeedClearable = ['ColorPicker']
|
||||||
|
// 拆分事件并组合
|
||||||
|
const onEvents = (item?.componentProps as any)?.on || {}
|
||||||
|
const newOnEvents: Recordable = {}
|
||||||
|
|
||||||
|
for (const key in onEvents) {
|
||||||
|
if (onEvents[key]) {
|
||||||
|
newOnEvents[`on${firstUpperCase(key)}`] = (...args: any[]) => {
|
||||||
|
onEvents[key](...args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const componentProps: Recordable = {
|
const componentProps: Recordable = {
|
||||||
clearable: true,
|
clearable: true,
|
||||||
...item.componentProps
|
...item.componentProps,
|
||||||
|
...newOnEvents
|
||||||
}
|
}
|
||||||
// 需要删除额外的属性
|
// 需要删除额外的属性
|
||||||
if (componentProps.slots) {
|
if (componentProps.slots) {
|
||||||
delete componentProps.slots
|
delete componentProps.slots
|
||||||
}
|
}
|
||||||
|
if (componentProps.on) {
|
||||||
|
delete componentProps.on
|
||||||
|
}
|
||||||
return componentProps
|
return componentProps
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,16 +116,16 @@ export const setComponentProps = (item: FormSchema): Recordable => {
|
|||||||
* @param formModel 表单数据
|
* @param formModel 表单数据
|
||||||
* @param slotsProps 插槽属性
|
* @param slotsProps 插槽属性
|
||||||
*/
|
*/
|
||||||
export const setItemComponentSlots = (formModel: any, slotsProps: Recordable = {}): Recordable => {
|
export const setItemComponentSlots = (slotsProps: Recordable = {}): Recordable => {
|
||||||
const slotObj: Recordable = {}
|
const slotObj: Recordable = {}
|
||||||
for (const key in slotsProps) {
|
for (const key in slotsProps) {
|
||||||
if (slotsProps[key]) {
|
if (slotsProps[key]) {
|
||||||
if (isFunction(slotsProps[key])) {
|
if (isFunction(slotsProps[key])) {
|
||||||
slotObj[key] = () => {
|
slotObj[humpToDash(key)] = (...args: any[]) => {
|
||||||
return slotsProps[key]?.(formModel)
|
return slotsProps[key]?.(...args)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
slotObj[key] = () => {
|
slotObj[humpToDash(key)] = () => {
|
||||||
return slotsProps[key]
|
return slotsProps[key]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -118,34 +144,19 @@ export const setItemComponentSlots = (formModel: any, slotsProps: Recordable = {
|
|||||||
export const initModel = (schema: FormSchema[], formModel: Recordable) => {
|
export const initModel = (schema: FormSchema[], formModel: Recordable) => {
|
||||||
const model: Recordable = { ...formModel }
|
const model: Recordable = { ...formModel }
|
||||||
schema.map((v) => {
|
schema.map((v) => {
|
||||||
// 如果是hidden,就删除对应的值
|
if (v.remove) {
|
||||||
if (v.hidden) {
|
|
||||||
delete model[v.field]
|
delete model[v.field]
|
||||||
} else if (v.component && v.component !== 'Divider') {
|
} else if (v.component !== 'Divider') {
|
||||||
const hasField = Reflect.has(model, v.field)
|
// const hasField = Reflect.has(model, v.field)
|
||||||
|
const hasField = get(model, v.field)
|
||||||
// 如果先前已经有值存在,则不进行重新赋值,而是采用现有的值
|
// 如果先前已经有值存在,则不进行重新赋值,而是采用现有的值
|
||||||
model[v.field] = hasField ? model[v.field] : v.value !== void 0 ? v.value : ''
|
set(
|
||||||
|
model,
|
||||||
|
v.field,
|
||||||
|
hasField !== void 0 ? get(model, v.field) : v.value !== void 0 ? v.value : undefined
|
||||||
|
)
|
||||||
|
// model[v.field] = hasField ? model[v.field] : v.value !== void 0 ? v.value : undefined
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return model
|
return model
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param slots 插槽
|
|
||||||
* @param field 字段名
|
|
||||||
* @returns 返回FormIiem插槽
|
|
||||||
*/
|
|
||||||
export const setFormItemSlots = (slots: Slots, field: string): Recordable => {
|
|
||||||
const slotObj: Recordable = {}
|
|
||||||
if (slots[`${field}-error`]) {
|
|
||||||
slotObj['error'] = (data: Recordable) => {
|
|
||||||
return getSlot(slots, `${field}-error`, data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (slots[`${field}-label`]) {
|
|
||||||
slotObj['label'] = (data: Recordable) => {
|
|
||||||
return getSlot(slots, `${field}-label`, data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return slotObj
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
import { FormSchema } from '@/types/form'
|
|
||||||
|
|
||||||
export interface PlaceholderMoel {
|
|
||||||
placeholder?: string
|
|
||||||
startPlaceholder?: string
|
|
||||||
endPlaceholder?: string
|
|
||||||
rangeSeparator?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type FormProps = {
|
|
||||||
schema?: FormSchema[]
|
|
||||||
isCol?: boolean
|
|
||||||
model?: Recordable
|
|
||||||
autoSetPlaceholder?: boolean
|
|
||||||
isCustom?: boolean
|
|
||||||
labelWidth?: string | number
|
|
||||||
} & Recordable
|
|
||||||
663
src/components/Form/src/types/index.ts
Normal file
663
src/components/Form/src/types/index.ts
Normal file
@@ -0,0 +1,663 @@
|
|||||||
|
import {
|
||||||
|
AutocompleteProps,
|
||||||
|
InputNumberProps,
|
||||||
|
CascaderProps,
|
||||||
|
CascaderNode,
|
||||||
|
CascaderValue,
|
||||||
|
SwitchProps,
|
||||||
|
ComponentSize,
|
||||||
|
InputProps,
|
||||||
|
RateProps,
|
||||||
|
ColorPickerProps,
|
||||||
|
TransferProps,
|
||||||
|
RadioGroupProps,
|
||||||
|
RadioButtonProps,
|
||||||
|
CheckboxGroupProps,
|
||||||
|
DividerProps,
|
||||||
|
DatePickerProps,
|
||||||
|
FormItemProps as ElFormItemProps,
|
||||||
|
FormProps as ElFormProps,
|
||||||
|
ISelectProps,
|
||||||
|
UploadProps
|
||||||
|
} from 'element-plus'
|
||||||
|
import { IEditorConfig } from '@wangeditor/editor'
|
||||||
|
import { CSSProperties } from 'vue'
|
||||||
|
|
||||||
|
export interface PlaceholderModel {
|
||||||
|
placeholder?: string
|
||||||
|
startPlaceholder?: string
|
||||||
|
endPlaceholder?: string
|
||||||
|
rangeSeparator?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ComponentNameEnum {
|
||||||
|
RADIO_GROUP = 'RadioGroup',
|
||||||
|
RADIO_BUTTON = 'RadioButton',
|
||||||
|
CHECKBOX_GROUP = 'CheckboxGroup',
|
||||||
|
CHECKBOX_BUTTON = 'CheckboxButton',
|
||||||
|
INPUT = 'Input',
|
||||||
|
AUTOCOMPLETE = 'Autocomplete',
|
||||||
|
INPUT_NUMBER = 'InputNumber',
|
||||||
|
SELECT = 'Select',
|
||||||
|
CASCADER = 'Cascader',
|
||||||
|
SWITCH = 'Switch',
|
||||||
|
SLIDER = 'Slider',
|
||||||
|
TIME_PICKER = 'TimePicker',
|
||||||
|
DATE_PICKER = 'DatePicker',
|
||||||
|
RATE = 'Rate',
|
||||||
|
COLOR_PICKER = 'ColorPicker',
|
||||||
|
TRANSFER = 'Transfer',
|
||||||
|
DIVIDER = 'Divider',
|
||||||
|
TIME_SELECT = 'TimeSelect',
|
||||||
|
SELECT_V2 = 'SelectV2',
|
||||||
|
INPUT_PASSWORD = 'InputPassword',
|
||||||
|
EDITOR = 'Editor',
|
||||||
|
TREE_SELECT = 'TreeSelect',
|
||||||
|
UPLOAD = 'Upload'
|
||||||
|
}
|
||||||
|
|
||||||
|
type CamelCaseComponentName = keyof typeof ComponentNameEnum extends infer K
|
||||||
|
? K extends string
|
||||||
|
? K extends `${infer A}_${infer B}`
|
||||||
|
? `${Capitalize<Lowercase<A>>}${Capitalize<Lowercase<B>>}`
|
||||||
|
: Capitalize<Lowercase<K>>
|
||||||
|
: never
|
||||||
|
: never
|
||||||
|
|
||||||
|
export type ComponentName = CamelCaseComponentName
|
||||||
|
|
||||||
|
export interface InputPasswordComponentProps {
|
||||||
|
strength?: boolean
|
||||||
|
style?: CSSProperties
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InputComponentProps extends Partial<InputProps> {
|
||||||
|
rows?: number
|
||||||
|
on?: {
|
||||||
|
blur?: (event: FocusEvent) => void
|
||||||
|
focus?: (event: FocusEvent) => void
|
||||||
|
change?: (value: string | number) => void
|
||||||
|
clear?: () => void
|
||||||
|
input?: (value: string | number) => void
|
||||||
|
}
|
||||||
|
slots?: {
|
||||||
|
prefix?: (...args: any[]) => JSX.Element | null
|
||||||
|
suffix?: (...args: any[]) => JSX.Element | null
|
||||||
|
prepend?: (...args: any[]) => JSX.Element | null
|
||||||
|
append?: (...args: any[]) => JSX.Element | null
|
||||||
|
}
|
||||||
|
style?: CSSProperties
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AutocompleteComponentProps extends Partial<AutocompleteProps> {
|
||||||
|
on?: {
|
||||||
|
select?: (item: any) => void
|
||||||
|
change?: (value: string | number) => void
|
||||||
|
}
|
||||||
|
slots?: {
|
||||||
|
default?: (...args: any[]) => JSX.Element | null
|
||||||
|
prefix?: (...args: any[]) => JSX.Element | null
|
||||||
|
suffix?: (...args: any[]) => JSX.Element | null
|
||||||
|
prepend?: (...args: any[]) => JSX.Element | null
|
||||||
|
append?: (...args: any[]) => JSX.Element | null
|
||||||
|
}
|
||||||
|
style?: CSSProperties
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InputNumberComponentProps extends Partial<InputNumberProps> {
|
||||||
|
on?: {
|
||||||
|
change?: (currentValue: number | undefined, oldValue: number | undefined) => void
|
||||||
|
blur?: (event: FocusEvent) => void
|
||||||
|
focus?: (event: FocusEvent) => void
|
||||||
|
}
|
||||||
|
style?: CSSProperties
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SelectOption {
|
||||||
|
label?: string
|
||||||
|
disabled?: boolean
|
||||||
|
value?: any
|
||||||
|
key?: string | number
|
||||||
|
options?: SelectOption[]
|
||||||
|
[key: string]: any
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SelectComponentProps extends Omit<Partial<ISelectProps>, 'options'> {
|
||||||
|
/**
|
||||||
|
* 数据源的字段别名
|
||||||
|
*/
|
||||||
|
props?: {
|
||||||
|
key?: string
|
||||||
|
value?: string
|
||||||
|
label?: string
|
||||||
|
children?: string
|
||||||
|
}
|
||||||
|
on?: {
|
||||||
|
change?: (value: string | number | boolean | Object) => void
|
||||||
|
visibleChange?: (visible: boolean) => void
|
||||||
|
removeTag?: (tag: any) => void
|
||||||
|
clear?: () => void
|
||||||
|
blur?: (event: FocusEvent) => void
|
||||||
|
focus?: (event: FocusEvent) => void
|
||||||
|
}
|
||||||
|
slots?: {
|
||||||
|
default?: (options: SelectOption[]) => JSX.Element[] | null
|
||||||
|
optionGroupDefault?: (item: SelectOption) => JSX.Element
|
||||||
|
optionDefault?: (option: SelectOption) => JSX.Element | null
|
||||||
|
prefix?: (...args: any[]) => JSX.Element | null
|
||||||
|
empty?: (...args: any[]) => JSX.Element | null
|
||||||
|
}
|
||||||
|
options?: SelectOption[]
|
||||||
|
style?: CSSProperties
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SelectV2ComponentProps {
|
||||||
|
multiple?: boolean
|
||||||
|
disabled?: boolean
|
||||||
|
valueKey?: string
|
||||||
|
size?: ComponentSize
|
||||||
|
clearable?: boolean
|
||||||
|
clearIcon?: string | JSX.Element | null
|
||||||
|
collapseTags?: boolean
|
||||||
|
multipleLimit?: number
|
||||||
|
name?: string
|
||||||
|
effect?: string
|
||||||
|
autocomplete?: string
|
||||||
|
placeholder?: string
|
||||||
|
filterable?: boolean
|
||||||
|
allowCreate?: boolean
|
||||||
|
reserveKeyword?: boolean
|
||||||
|
noDataText?: string
|
||||||
|
popperClass?: string
|
||||||
|
teleported?: boolean
|
||||||
|
persistent?: boolean
|
||||||
|
popperOptions?: any
|
||||||
|
automaticDropdown?: boolean
|
||||||
|
height?: number
|
||||||
|
scrollbarAlwaysOn?: boolean
|
||||||
|
remote?: boolean
|
||||||
|
remoteMethod?: (query: string) => void
|
||||||
|
validateEvent?: boolean
|
||||||
|
placement?: AutocompleteProps['placement']
|
||||||
|
collapseTagsTooltip?: boolean
|
||||||
|
on?: {
|
||||||
|
change?: (value: string | number | boolean | Object) => void
|
||||||
|
visibleChange?: (visible: boolean) => void
|
||||||
|
removeTag?: (tag: any) => void
|
||||||
|
clear?: () => void
|
||||||
|
blur?: (event: FocusEvent) => void
|
||||||
|
focus?: (event: FocusEvent) => void
|
||||||
|
}
|
||||||
|
options?: SelectOption[]
|
||||||
|
slots?: {
|
||||||
|
default?: (option: SelectOption) => JSX.Element | null
|
||||||
|
}
|
||||||
|
style?: CSSProperties
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CascaderComponentProps {
|
||||||
|
options?: Record<string, unknown>[]
|
||||||
|
props?: CascaderProps
|
||||||
|
size?: ComponentSize
|
||||||
|
placeholder?: string
|
||||||
|
disabled?: boolean
|
||||||
|
clearable?: boolean
|
||||||
|
showAllLevels?: boolean
|
||||||
|
collapseTags?: boolean
|
||||||
|
collapseTagsTooltip?: boolean
|
||||||
|
separator?: string
|
||||||
|
filterable?: boolean
|
||||||
|
filterMethod?: (node: CascaderNode, keyword: string) => boolean
|
||||||
|
debounce?: number
|
||||||
|
beforeFilter?: (value: string) => boolean
|
||||||
|
popperClass?: string
|
||||||
|
teleported?: boolean
|
||||||
|
tagType?: ElementPlusInfoType
|
||||||
|
validateEvent?: boolean
|
||||||
|
on?: {
|
||||||
|
change?: (value: CascaderValue) => void
|
||||||
|
expandChange?: (value: CascaderValue) => void
|
||||||
|
blur?: (event: FocusEvent) => void
|
||||||
|
focus?: (event: FocusEvent) => void
|
||||||
|
visibleChange?: (value: boolean) => void
|
||||||
|
removeTag?: (value: CascaderNode['valueByOption']) => void
|
||||||
|
}
|
||||||
|
slots?: {
|
||||||
|
default?: (...args: any[]) => JSX.Element | null
|
||||||
|
empty?: (...args: any[]) => JSX.Element | null
|
||||||
|
}
|
||||||
|
style?: CSSProperties
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SwitchComponentProps extends Partial<SwitchProps> {
|
||||||
|
on?: {
|
||||||
|
change?: (value: boolean | string | number) => void
|
||||||
|
}
|
||||||
|
style?: CSSProperties
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RateComponentProps extends Partial<RateProps> {
|
||||||
|
on?: {
|
||||||
|
change?: (value: number) => void
|
||||||
|
}
|
||||||
|
style?: CSSProperties
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ColorPickerComponentProps extends Partial<ColorPickerProps> {
|
||||||
|
on?: {
|
||||||
|
change?: (value: string) => void
|
||||||
|
activeChange?: (value: string) => void
|
||||||
|
}
|
||||||
|
style?: CSSProperties
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TransferComponentProps extends Partial<TransferProps> {
|
||||||
|
on?: {
|
||||||
|
change?: (
|
||||||
|
value: number | string,
|
||||||
|
direction: 'left' | 'right',
|
||||||
|
movedKeys: string[] | number[]
|
||||||
|
) => void
|
||||||
|
leftCheckChange?: (value: any[]) => void
|
||||||
|
rightCheckChange?: (value: any[]) => void
|
||||||
|
}
|
||||||
|
slots?: {
|
||||||
|
default?: (...args: any[]) => JSX.Element | null
|
||||||
|
leftFooter?: (...args: any[]) => JSX.Element | null
|
||||||
|
rightFooter?: (...args: any[]) => JSX.Element | null
|
||||||
|
}
|
||||||
|
style?: CSSProperties
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RadioOption {
|
||||||
|
label?: string
|
||||||
|
value?: string | number | boolean
|
||||||
|
disabled?: boolean
|
||||||
|
border?: boolean
|
||||||
|
size?: ComponentSize
|
||||||
|
name?: string
|
||||||
|
[key: string]: any
|
||||||
|
}
|
||||||
|
export interface RadioGroupComponentProps extends Partial<RadioGroupProps> {
|
||||||
|
options?: RadioOption[]
|
||||||
|
/**
|
||||||
|
* 数据源的字段别名
|
||||||
|
*/
|
||||||
|
props?: {
|
||||||
|
label?: string
|
||||||
|
value?: string
|
||||||
|
disabled?: string
|
||||||
|
}
|
||||||
|
on?: {
|
||||||
|
change?: (value: string | number | boolean) => void
|
||||||
|
}
|
||||||
|
slots?: {
|
||||||
|
default?: (...args: any[]) => JSX.Element[] | null
|
||||||
|
}
|
||||||
|
style?: CSSProperties
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RadioButtonComponentProps extends Partial<RadioButtonProps> {
|
||||||
|
options?: RadioOption[]
|
||||||
|
/**
|
||||||
|
* 数据源的字段别名
|
||||||
|
*/
|
||||||
|
props?: {
|
||||||
|
label?: string
|
||||||
|
value?: string
|
||||||
|
disabled?: string
|
||||||
|
}
|
||||||
|
on?: {
|
||||||
|
change?: (value: string | number | boolean) => void
|
||||||
|
}
|
||||||
|
slots?: {
|
||||||
|
default?: (...args: any[]) => JSX.Element[] | null
|
||||||
|
}
|
||||||
|
style?: CSSProperties
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CheckboxOption {
|
||||||
|
label?: string
|
||||||
|
value?: string | number | boolean
|
||||||
|
disabled?: boolean
|
||||||
|
trueLabel?: string | number
|
||||||
|
falseLabel?: string | number
|
||||||
|
border?: boolean
|
||||||
|
size?: ComponentSize
|
||||||
|
name?: string
|
||||||
|
checked?: boolean
|
||||||
|
indeterminate?: boolean
|
||||||
|
validateEvent?: boolean
|
||||||
|
tabindex?: number | string
|
||||||
|
id?: string
|
||||||
|
controls?: boolean
|
||||||
|
[key: string]: any
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CheckboxGroupComponentProps extends Partial<CheckboxGroupProps> {
|
||||||
|
options?: CheckboxOption[]
|
||||||
|
/**
|
||||||
|
* 数据源的字段别名
|
||||||
|
*/
|
||||||
|
props?: {
|
||||||
|
label?: string
|
||||||
|
value?: string
|
||||||
|
disabled?: string
|
||||||
|
}
|
||||||
|
on?: {
|
||||||
|
change?: (value: string | number | boolean) => void
|
||||||
|
}
|
||||||
|
slots?: {
|
||||||
|
default?: (...args: any[]) => JSX.Element[] | null
|
||||||
|
}
|
||||||
|
style?: CSSProperties
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DividerComponentProps extends Partial<DividerProps> {
|
||||||
|
on?: {
|
||||||
|
change?: (value: number) => void
|
||||||
|
input?: (value: number) => void
|
||||||
|
}
|
||||||
|
style?: CSSProperties
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DatePickerComponentProps extends Partial<DatePickerProps> {
|
||||||
|
on?: {
|
||||||
|
change?: (value: string | Date | number | string[]) => void
|
||||||
|
blur?: (event: FocusEvent) => void
|
||||||
|
focus?: (event: FocusEvent) => void
|
||||||
|
calendarChange?: (val: [Date, Date]) => void
|
||||||
|
panelChange?: (date, mode, view) => void
|
||||||
|
visibleChange?: (visibility: boolean) => void
|
||||||
|
}
|
||||||
|
slots?: {
|
||||||
|
default?: (...args: any[]) => JSX.Element | null
|
||||||
|
rangeSeparator?: (...args: any[]) => JSX.Element | null
|
||||||
|
}
|
||||||
|
style?: CSSProperties
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DateTimePickerComponentProps {
|
||||||
|
readonly?: boolean
|
||||||
|
disabled?: boolean
|
||||||
|
editable?: boolean
|
||||||
|
clearable?: boolean
|
||||||
|
size?: ComponentSize
|
||||||
|
placeholder?: string
|
||||||
|
startPlaceholder?: string
|
||||||
|
endPlaceholder?: string
|
||||||
|
timeArrowControl?: boolean
|
||||||
|
type?: 'year' | 'month' | 'date' | 'datetime' | 'datetimerange' | 'daterange' | 'week'
|
||||||
|
format?: string
|
||||||
|
popperClass?: string
|
||||||
|
rangeSeparator?: string
|
||||||
|
defaultValue?: Date | [Date, Date]
|
||||||
|
defaultTime?: Date | [Date, Date]
|
||||||
|
valueFormat?: string
|
||||||
|
id?: string
|
||||||
|
name?: string
|
||||||
|
unlinkPanels?: boolean
|
||||||
|
prefixIcon?: string | JSX.Element
|
||||||
|
clearIcon?: string | JSX.Element
|
||||||
|
shortcuts?: Array<{ text: string; value: Date | Function }>
|
||||||
|
disabledDate?: (date: Date) => boolean
|
||||||
|
cellClassName?: string | ((date: Date) => string | undefined)
|
||||||
|
teleported?: boolean
|
||||||
|
on?: {
|
||||||
|
change?: (value: string | Date | number | string[]) => void
|
||||||
|
blur?: (event: FocusEvent) => void
|
||||||
|
focus?: (event: FocusEvent) => void
|
||||||
|
calendarChange?: (val: [Date, Date]) => void
|
||||||
|
visibleChange?: (visibility: boolean) => void
|
||||||
|
}
|
||||||
|
slots?: {
|
||||||
|
default?: (...args: any[]) => JSX.Element | null
|
||||||
|
rangeSeparator?: (...args: any[]) => JSX.Element | null
|
||||||
|
}
|
||||||
|
style?: CSSProperties
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TimePickerComponentProps {
|
||||||
|
readonly?: boolean
|
||||||
|
disabled?: boolean
|
||||||
|
editable?: boolean
|
||||||
|
clearable?: boolean
|
||||||
|
size?: ComponentSize
|
||||||
|
placeholder?: string
|
||||||
|
startPlaceholder?: string
|
||||||
|
endPlaceholder?: string
|
||||||
|
isRange?: boolean
|
||||||
|
arrowControl?: boolean
|
||||||
|
popperClass?: string
|
||||||
|
rangeSeparator?: string
|
||||||
|
format?: string
|
||||||
|
defaultValue?: Date | [Date, Date]
|
||||||
|
id?: string
|
||||||
|
name?: string
|
||||||
|
label?: string
|
||||||
|
prefixIcon?: string | JSX.Element
|
||||||
|
clearIcon?: string | JSX.Element
|
||||||
|
disabledHours?: (role: string, comparingDate?: any) => number[]
|
||||||
|
disabledMinutes?: (hour: number, role: string, comparingDate?: any) => number[]
|
||||||
|
disabledSeconds?: (hour: number, minute: number, role: string, comparingDate?: any) => number[]
|
||||||
|
teleported?: boolean
|
||||||
|
tabindex?: number | string
|
||||||
|
on?: {
|
||||||
|
change: (
|
||||||
|
val: number | string | Date | [number, number] | [string, string] | [Date, Date]
|
||||||
|
) => void
|
||||||
|
blur?: (event: FocusEvent) => void
|
||||||
|
focus?: (event: FocusEvent) => void
|
||||||
|
visibleChange?: (visibility: boolean) => void
|
||||||
|
}
|
||||||
|
style?: CSSProperties
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TimeSelectComponentProps {
|
||||||
|
disabled?: boolean
|
||||||
|
editable?: boolean
|
||||||
|
clearable?: boolean
|
||||||
|
size?: ComponentSize
|
||||||
|
placeholder?: string
|
||||||
|
name?: string
|
||||||
|
effect?: string
|
||||||
|
prefixIcon?: string | JSX.Element
|
||||||
|
clearIcon?: string | JSX.Element
|
||||||
|
start?: string
|
||||||
|
end?: string
|
||||||
|
step?: string
|
||||||
|
minTime?: string
|
||||||
|
maxTime?: string
|
||||||
|
format?: string
|
||||||
|
on?: {
|
||||||
|
change?: (val: string) => void
|
||||||
|
blur?: (event: FocusEvent) => void
|
||||||
|
focus?: (event: FocusEvent) => void
|
||||||
|
}
|
||||||
|
style?: CSSProperties
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EditorComponentProps {
|
||||||
|
editorConfig?: IEditorConfig
|
||||||
|
style?: CSSProperties
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ColProps {
|
||||||
|
span?: number
|
||||||
|
xs?: number
|
||||||
|
sm?: number
|
||||||
|
md?: number
|
||||||
|
lg?: number
|
||||||
|
xl?: number
|
||||||
|
tag?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FormSetProps {
|
||||||
|
field: string
|
||||||
|
path: string
|
||||||
|
value: any
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FormItemProps extends Partial<ElFormItemProps> {
|
||||||
|
style?: CSSProperties
|
||||||
|
slots?: {
|
||||||
|
default?: (...args: any[]) => JSX.Element | null
|
||||||
|
label?: (...args: any[]) => JSX.Element | null
|
||||||
|
error?: (...args: any[]) => JSX.Element | null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UploadComponentProps extends Partial<UploadProps> {
|
||||||
|
slots?: {
|
||||||
|
default?: (...args: any[]) => JSX.Element | null
|
||||||
|
trigger?: (...args: any[]) => JSX.Element | null
|
||||||
|
tip?: (...args: any[]) => JSX.Element | null
|
||||||
|
file?: (...args: any[]) => JSX.Element | null
|
||||||
|
}
|
||||||
|
style?: CSSProperties
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TreeSelectComponentProps
|
||||||
|
extends Omit<Partial<SelectComponentProps>, 'props' | 'on' | 'slots'> {
|
||||||
|
data?: any[]
|
||||||
|
emptyText?: string
|
||||||
|
nodeKey?: string
|
||||||
|
props?: {
|
||||||
|
children?: string
|
||||||
|
label?: string | ((...args: any[]) => string)
|
||||||
|
disabled?: string | ((...args: any[]) => string)
|
||||||
|
isLeaf?: string | ((...args: any[]) => string)
|
||||||
|
class?: string | ((...args: any[]) => string)
|
||||||
|
}
|
||||||
|
renderAfterExpand?: boolean
|
||||||
|
load?: (...args: any[]) => Promise<any>
|
||||||
|
renderContent?: (...args: any[]) => JSX.Element | null
|
||||||
|
highlightCurrent?: boolean
|
||||||
|
defaultExpandAll?: boolean
|
||||||
|
expandOnClickNode?: boolean
|
||||||
|
checkOnClickNode?: boolean
|
||||||
|
autoExpandParent?: boolean
|
||||||
|
defaultExpandedKeys?: any[]
|
||||||
|
showCheckbox?: boolean
|
||||||
|
checkStrictly?: boolean
|
||||||
|
defaultCheckedKeys?: any[]
|
||||||
|
currentNodeKey?: string | number
|
||||||
|
filterNodeMethod?: (...args: any[]) => boolean
|
||||||
|
accordion?: boolean
|
||||||
|
indent?: number
|
||||||
|
icon?: string | ((...args: any[]) => JSX.Element | null)
|
||||||
|
lazy?: boolean
|
||||||
|
draggable?: boolean
|
||||||
|
allowDrag?: (...args: any[]) => boolean
|
||||||
|
allowDrop?: (...args: any[]) => boolean
|
||||||
|
on?: {
|
||||||
|
change?: (value: string | number | boolean | Object) => void
|
||||||
|
visibleChange?: (visible: boolean) => void
|
||||||
|
removeTag?: (tag: any) => void
|
||||||
|
clear?: () => void
|
||||||
|
blur?: (event: FocusEvent) => void
|
||||||
|
focus?: (event: FocusEvent) => void
|
||||||
|
nodeClick?: (...args: any[]) => void
|
||||||
|
nodeContextMenu?: (...args: any[]) => void
|
||||||
|
checkChange?: (...args: any[]) => void
|
||||||
|
check?: (...args: any[]) => void
|
||||||
|
currentChange?: (...args: any[]) => void
|
||||||
|
nodeExpand?: (...args: any[]) => void
|
||||||
|
nodeCollapse?: (...args: any[]) => void
|
||||||
|
nodeDragStart?: (...args: any[]) => void
|
||||||
|
nodeDragEnter?: (...args: any[]) => void
|
||||||
|
nodeDragLeave?: (...args: any[]) => void
|
||||||
|
nodeDragOver?: (...args: any[]) => void
|
||||||
|
nodeDragEnd?: (...args: any[]) => void
|
||||||
|
nodeDrop?: (...args: any[]) => void
|
||||||
|
}
|
||||||
|
slots?: {
|
||||||
|
default?: (...args: any[]) => JSX.Element | null
|
||||||
|
optionGroupDefault?: (item: SelectOption) => JSX.Element
|
||||||
|
optionDefault?: (option: SelectOption) => JSX.Element | null
|
||||||
|
prefix?: (...args: any[]) => JSX.Element | null
|
||||||
|
empty?: (...args: any[]) => JSX.Element | null
|
||||||
|
}
|
||||||
|
style?: CSSProperties
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FormSchema {
|
||||||
|
/**
|
||||||
|
* 唯一标识
|
||||||
|
*/
|
||||||
|
field: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标题
|
||||||
|
*/
|
||||||
|
label?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* col组件属性
|
||||||
|
*/
|
||||||
|
colProps?: ColProps
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表单组件属性,具体可以查看element-plus文档
|
||||||
|
*/
|
||||||
|
componentProps?:
|
||||||
|
| InputComponentProps
|
||||||
|
| AutocompleteComponentProps
|
||||||
|
| InputNumberComponentProps
|
||||||
|
| SelectComponentProps
|
||||||
|
| SelectV2ComponentProps
|
||||||
|
| CascaderComponentProps
|
||||||
|
| SwitchComponentProps
|
||||||
|
| RateComponentProps
|
||||||
|
| ColorPickerComponentProps
|
||||||
|
| TransferComponentProps
|
||||||
|
| RadioGroupComponentProps
|
||||||
|
| RadioButtonComponentProps
|
||||||
|
| DividerComponentProps
|
||||||
|
| DatePickerComponentProps
|
||||||
|
| DateTimePickerComponentProps
|
||||||
|
| TimePickerComponentProps
|
||||||
|
| InputPasswordComponentProps
|
||||||
|
| TreeSelectComponentProps
|
||||||
|
| UploadComponentProps
|
||||||
|
| any
|
||||||
|
|
||||||
|
/**
|
||||||
|
* formItem组件属性,具体可以查看element-plus文档
|
||||||
|
*/
|
||||||
|
formItemProps?: FormItemProps
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 渲染的组件名称
|
||||||
|
*/
|
||||||
|
component?: ComponentName
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始值
|
||||||
|
*/
|
||||||
|
value?: any
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否隐藏,如果为true,会连同值一同删除,类似v-if
|
||||||
|
*/
|
||||||
|
remove?: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 样式隐藏,不会把值一同删掉,类似v-show
|
||||||
|
*/
|
||||||
|
hidden?: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns 远程加载下拉项
|
||||||
|
*/
|
||||||
|
optionApi?: any
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FormProps extends Partial<ElFormProps> {
|
||||||
|
schema?: FormSchema[]
|
||||||
|
isCol?: boolean
|
||||||
|
model?: Recordable
|
||||||
|
autoSetPlaceholder?: boolean
|
||||||
|
isCustom?: boolean
|
||||||
|
[key: string]: any
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
import Icon from './src/Icon.vue'
|
import Icon from './src/Icon.vue'
|
||||||
|
|
||||||
|
export type { IconTypes } from './src/types'
|
||||||
|
|
||||||
export { Icon }
|
export { Icon }
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, unref, ref, watch, nextTick } from 'vue'
|
import { computed, unref } from 'vue'
|
||||||
import { ElIcon } from 'element-plus'
|
import { ElIcon } from 'element-plus'
|
||||||
import { propTypes } from '@/utils/propTypes'
|
import { propTypes } from '@/utils/propTypes'
|
||||||
import Iconify from '@purge-icons/generated'
|
|
||||||
import { useDesign } from '@/hooks/web/useDesign'
|
import { useDesign } from '@/hooks/web/useDesign'
|
||||||
|
import { Icon } from '@iconify/vue'
|
||||||
|
|
||||||
const { getPrefixCls } = useDesign()
|
const { getPrefixCls } = useDesign()
|
||||||
|
|
||||||
@@ -15,11 +15,10 @@ const props = defineProps({
|
|||||||
// icon color
|
// icon color
|
||||||
color: propTypes.string,
|
color: propTypes.string,
|
||||||
// icon size
|
// icon size
|
||||||
size: propTypes.number.def(16)
|
size: propTypes.number.def(16),
|
||||||
|
hoverColor: propTypes.string
|
||||||
})
|
})
|
||||||
|
|
||||||
const elRef = ref<ElRef>(null)
|
|
||||||
|
|
||||||
const isLocal = computed(() => props.icon.startsWith('svg-icon:'))
|
const isLocal = computed(() => props.icon.startsWith('svg-icon:'))
|
||||||
|
|
||||||
const symbolId = computed(() => {
|
const symbolId = computed(() => {
|
||||||
@@ -33,36 +32,6 @@ const getIconifyStyle = computed(() => {
|
|||||||
color
|
color
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const updateIcon = async (icon: string) => {
|
|
||||||
if (unref(isLocal)) return
|
|
||||||
|
|
||||||
const el = unref(elRef)
|
|
||||||
if (!el) return
|
|
||||||
|
|
||||||
await nextTick()
|
|
||||||
|
|
||||||
if (!icon) return
|
|
||||||
|
|
||||||
const svg = Iconify.renderSVG(icon, {})
|
|
||||||
if (svg) {
|
|
||||||
el.textContent = ''
|
|
||||||
el.appendChild(svg)
|
|
||||||
} else {
|
|
||||||
const span = document.createElement('span')
|
|
||||||
span.className = 'iconify'
|
|
||||||
span.dataset.icon = icon
|
|
||||||
el.textContent = ''
|
|
||||||
el.appendChild(span)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => props.icon,
|
|
||||||
(icon: string) => {
|
|
||||||
updateIcon(icon)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -71,8 +40,20 @@ watch(
|
|||||||
<use :xlink:href="symbolId" />
|
<use :xlink:href="symbolId" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
<span v-else ref="elRef" :class="$attrs.class" :style="getIconifyStyle">
|
<Icon v-else :icon="icon" :style="getIconifyStyle" />
|
||||||
<span class="iconify" :data-icon="symbolId"></span>
|
|
||||||
</span>
|
|
||||||
</ElIcon>
|
</ElIcon>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
@prefix-cls: ~'@{namespace}-icon';
|
||||||
|
|
||||||
|
.@{prefix-cls},
|
||||||
|
.iconify {
|
||||||
|
&:hover {
|
||||||
|
:deep(svg) {
|
||||||
|
// stylelint-disable-next-line
|
||||||
|
color: v-bind(hoverColor) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -2,4 +2,5 @@ export interface IconTypes {
|
|||||||
size?: number
|
size?: number
|
||||||
color?: string
|
color?: string
|
||||||
icon: string
|
icon: string
|
||||||
|
hoverColor?: string
|
||||||
}
|
}
|
||||||
@@ -12,7 +12,7 @@ export function createImageViewer(options: ImageViewerProps) {
|
|||||||
initialIndex = 0,
|
initialIndex = 0,
|
||||||
infinite = true,
|
infinite = true,
|
||||||
hideOnClickModal = false,
|
hideOnClickModal = false,
|
||||||
appendToBody = false,
|
teleported = false,
|
||||||
zIndex = 2000,
|
zIndex = 2000,
|
||||||
show = true
|
show = true
|
||||||
} = options
|
} = options
|
||||||
@@ -23,7 +23,7 @@ export function createImageViewer(options: ImageViewerProps) {
|
|||||||
propsData.initialIndex = initialIndex
|
propsData.initialIndex = initialIndex
|
||||||
propsData.infinite = infinite
|
propsData.infinite = infinite
|
||||||
propsData.hideOnClickModal = hideOnClickModal
|
propsData.hideOnClickModal = hideOnClickModal
|
||||||
propsData.appendToBody = appendToBody
|
propsData.teleported = teleported
|
||||||
propsData.zIndex = zIndex
|
propsData.zIndex = zIndex
|
||||||
propsData.show = show
|
propsData.show = show
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ const props = defineProps({
|
|||||||
initialIndex: propTypes.number.def(0),
|
initialIndex: propTypes.number.def(0),
|
||||||
infinite: propTypes.bool.def(true),
|
infinite: propTypes.bool.def(true),
|
||||||
hideOnClickModal: propTypes.bool.def(false),
|
hideOnClickModal: propTypes.bool.def(false),
|
||||||
appendToBody: propTypes.bool.def(false),
|
teleported: propTypes.bool.def(false),
|
||||||
show: propTypes.bool.def(false)
|
show: propTypes.bool.def(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,6 @@ export interface ImageViewerProps {
|
|||||||
initialIndex?: number
|
initialIndex?: number
|
||||||
infinite?: boolean
|
infinite?: boolean
|
||||||
hideOnClickModal?: boolean
|
hideOnClickModal?: boolean
|
||||||
appendToBody?: boolean
|
teleported?: boolean
|
||||||
show?: boolean
|
show?: boolean
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
import Infotip from './src/Infotip.vue'
|
import Infotip from './src/Infotip.vue'
|
||||||
|
|
||||||
|
export type { InfoTipSchema } from './src/types'
|
||||||
|
|
||||||
export { Infotip }
|
export { Infotip }
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { PropType } from 'vue'
|
|||||||
import { Highlight } from '@/components/Highlight'
|
import { Highlight } from '@/components/Highlight'
|
||||||
import { useDesign } from '@/hooks/web/useDesign'
|
import { useDesign } from '@/hooks/web/useDesign'
|
||||||
import { propTypes } from '@/utils/propTypes'
|
import { propTypes } from '@/utils/propTypes'
|
||||||
import { TipSchema } from '@/types/infoTip'
|
import { InfoTipSchema } from './types'
|
||||||
|
|
||||||
const { getPrefixCls } = useDesign()
|
const { getPrefixCls } = useDesign()
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ const prefixCls = getPrefixCls('infotip')
|
|||||||
defineProps({
|
defineProps({
|
||||||
title: propTypes.string.def(''),
|
title: propTypes.string.def(''),
|
||||||
schema: {
|
schema: {
|
||||||
type: Array as PropType<Array<string | TipSchema>>,
|
type: Array as PropType<Array<string | InfoTipSchema>>,
|
||||||
required: true,
|
required: true,
|
||||||
default: () => []
|
default: () => []
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
export interface TipSchema {
|
export interface InfoTipSchema {
|
||||||
label: string
|
label: string
|
||||||
keys?: string[]
|
keys?: string[]
|
||||||
}
|
}
|
||||||
@@ -32,10 +32,6 @@ const emit = defineEmits(['update:modelValue'])
|
|||||||
// 设置input的type属性
|
// 设置input的type属性
|
||||||
const textType = ref<'password' | 'text'>('password')
|
const textType = ref<'password' | 'text'>('password')
|
||||||
|
|
||||||
const changeTextType = () => {
|
|
||||||
textType.value = unref(textType) === 'text' ? 'password' : 'text'
|
|
||||||
}
|
|
||||||
|
|
||||||
// 输入框的值
|
// 输入框的值
|
||||||
const valueRef = ref(props.modelValue)
|
const valueRef = ref(props.modelValue)
|
||||||
|
|
||||||
@@ -53,19 +49,11 @@ const getPasswordStrength = computed(() => {
|
|||||||
const zxcvbnRef = zxcvbn(unref(valueRef)) as ZxcvbnResult
|
const zxcvbnRef = zxcvbn(unref(valueRef)) as ZxcvbnResult
|
||||||
return value ? zxcvbnRef.score : -1
|
return value ? zxcvbnRef.score : -1
|
||||||
})
|
})
|
||||||
|
|
||||||
const getIconName = computed(() =>
|
|
||||||
unref(textType) === 'password' ? 'ant-design:eye-invisible-outlined' : 'ant-design:eye-outlined'
|
|
||||||
)
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="[prefixCls, `${prefixCls}--${configGlobal?.size}`]">
|
<div :class="[prefixCls, `${prefixCls}--${configGlobal?.size}`]">
|
||||||
<ElInput v-bind="$attrs" v-model="valueRef" :type="textType">
|
<ElInput v-bind="$attrs" v-model="valueRef" showPassword :type="textType" />
|
||||||
<template #suffix>
|
|
||||||
<Icon class="el-input__icon cursor-pointer" :icon="getIconName" @click="changeTextType" />
|
|
||||||
</template>
|
|
||||||
</ElInput>
|
|
||||||
<div
|
<div
|
||||||
v-if="strength"
|
v-if="strength"
|
||||||
:class="`${prefixCls}__bar`"
|
:class="`${prefixCls}__bar`"
|
||||||
@@ -116,7 +104,9 @@ const getIconName = computed(() =>
|
|||||||
height: inherit;
|
height: inherit;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
border-radius: inherit;
|
border-radius: inherit;
|
||||||
transition: width 0.5s ease-in-out, background 0.25s;
|
transition:
|
||||||
|
width 0.5s ease-in-out,
|
||||||
|
background 0.25s;
|
||||||
|
|
||||||
&[data-score='0'] {
|
&[data-score='0'] {
|
||||||
width: 20%;
|
width: 20%;
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
import LocaleDropdown from './src/LocaleDropdown.vue'
|
import LocaleDropdown from './src/LocaleDropdown.vue'
|
||||||
|
|
||||||
|
export type { Language, LocaleDropdownType } from './src/types'
|
||||||
|
|
||||||
export { LocaleDropdown }
|
export { LocaleDropdown }
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import { useRenderMenuItem } from './components/useRenderMenuItem'
|
|||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { isUrl } from '@/utils/is'
|
import { isUrl } from '@/utils/is'
|
||||||
import { useDesign } from '@/hooks/web/useDesign'
|
import { useDesign } from '@/hooks/web/useDesign'
|
||||||
import { LayoutType } from '@/types/layout'
|
|
||||||
|
|
||||||
const { getPrefixCls } = useDesign()
|
const { getPrefixCls } = useDesign()
|
||||||
|
|
||||||
@@ -124,15 +123,15 @@ export default defineComponent({
|
|||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
@prefix-cls: ~'@{namespace}-menu';
|
@prefix-cls: ~'@{namespace}-menu';
|
||||||
|
|
||||||
.is-active--after {
|
// .is-active--after {
|
||||||
position: absolute;
|
// position: absolute;
|
||||||
top: 0;
|
// top: 0;
|
||||||
right: 0;
|
// right: 0;
|
||||||
width: 4px;
|
// width: 4px;
|
||||||
height: 100%;
|
// height: 100%;
|
||||||
background-color: var(--el-color-primary);
|
// background-color: var(--el-color-primary);
|
||||||
content: '';
|
// content: '';
|
||||||
}
|
// }
|
||||||
|
|
||||||
.@{prefix-cls} {
|
.@{prefix-cls} {
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -182,9 +181,9 @@ export default defineComponent({
|
|||||||
.@{elNamespace}-menu-item.is-active {
|
.@{elNamespace}-menu-item.is-active {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&:after {
|
// &:after {
|
||||||
.is-active--after;
|
// .is-active--after;
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置子菜单的背景颜色
|
// 设置子菜单的背景颜色
|
||||||
@@ -205,9 +204,9 @@ export default defineComponent({
|
|||||||
position: relative;
|
position: relative;
|
||||||
background-color: var(--left-menu-collapse-bg-active-color) !important;
|
background-color: var(--left-menu-collapse-bg-active-color) !important;
|
||||||
|
|
||||||
&:after {
|
// &:after {
|
||||||
.is-active--after;
|
// .is-active--after;
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -255,15 +254,15 @@ export default defineComponent({
|
|||||||
<style lang="less">
|
<style lang="less">
|
||||||
@prefix-cls: ~'@{namespace}-menu-popper';
|
@prefix-cls: ~'@{namespace}-menu-popper';
|
||||||
|
|
||||||
.is-active--after {
|
// .is-active--after {
|
||||||
position: absolute;
|
// position: absolute;
|
||||||
top: 0;
|
// top: 0;
|
||||||
right: 0;
|
// right: 0;
|
||||||
width: 4px;
|
// width: 4px;
|
||||||
height: 100%;
|
// height: 100%;
|
||||||
background-color: var(--el-color-primary);
|
// background-color: var(--el-color-primary);
|
||||||
content: '';
|
// content: '';
|
||||||
}
|
// }
|
||||||
|
|
||||||
.@{prefix-cls}--vertical,
|
.@{prefix-cls}--vertical,
|
||||||
.@{prefix-cls}--horizontal {
|
.@{prefix-cls}--horizontal {
|
||||||
@@ -292,9 +291,9 @@ export default defineComponent({
|
|||||||
background-color: var(--left-menu-bg-active-color) !important;
|
background-color: var(--left-menu-bg-active-color) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:after {
|
// &:after {
|
||||||
.is-active--after;
|
// .is-active--after;
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
4
src/components/Permission/index.ts
Normal file
4
src/components/Permission/index.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import Permission from './src/Permission.vue'
|
||||||
|
import { hasPermi } from './src/utils'
|
||||||
|
|
||||||
|
export { Permission, hasPermi }
|
||||||
29
src/components/Permission/src/Permission.vue
Normal file
29
src/components/Permission/src/Permission.vue
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { propTypes } from '@/utils/propTypes'
|
||||||
|
import { computed, unref } from 'vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
|
const { currentRoute } = useRouter()
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
permission: propTypes.string.def()
|
||||||
|
})
|
||||||
|
|
||||||
|
const currentPermission = computed(() => {
|
||||||
|
return unref(currentRoute)?.meta?.permission || []
|
||||||
|
})
|
||||||
|
|
||||||
|
const hasPermission = computed(() => {
|
||||||
|
const permission = unref(props.permission)
|
||||||
|
if (!permission) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return unref(currentPermission).includes(permission)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<template v-if="hasPermission">
|
||||||
|
<slot></slot>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
14
src/components/Permission/src/utils.ts
Normal file
14
src/components/Permission/src/utils.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { useI18n } from '@/hooks/web/useI18n'
|
||||||
|
import router from '@/router'
|
||||||
|
|
||||||
|
export const hasPermi = (value: string) => {
|
||||||
|
const { t } = useI18n()
|
||||||
|
const permission = (router.currentRoute.value.meta.permission || []) as string[]
|
||||||
|
if (!value) {
|
||||||
|
throw new Error(t('permission.hasPermission'))
|
||||||
|
}
|
||||||
|
if (permission.includes(value)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
import Qrcode from './src/Qrcode.vue'
|
import Qrcode from './src/Qrcode.vue'
|
||||||
|
|
||||||
|
export type { QrcodeLogo } from './src/types'
|
||||||
|
|
||||||
export { Qrcode }
|
export { Qrcode }
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { cloneDeep } from 'lodash-es'
|
|||||||
import { propTypes } from '@/utils/propTypes'
|
import { propTypes } from '@/utils/propTypes'
|
||||||
import { useDesign } from '@/hooks/web/useDesign'
|
import { useDesign } from '@/hooks/web/useDesign'
|
||||||
import { isString } from '@/utils/is'
|
import { isString } from '@/utils/is'
|
||||||
import { QrcodeLogo } from '@/types/qrcode'
|
import { QrcodeLogo } from '@/components/Qrcode'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
// img 或者 canvas,img不支持logo嵌套
|
// img 或者 canvas,img不支持logo嵌套
|
||||||
|
|||||||
@@ -1,3 +1,15 @@
|
|||||||
|
import { FormSchema, FormSetProps } from '../Form'
|
||||||
import Search from './src/Search.vue'
|
import Search from './src/Search.vue'
|
||||||
|
|
||||||
|
export type { SearchProps } from './src/types'
|
||||||
|
|
||||||
|
export interface SearchExpose {
|
||||||
|
setValues: (data: Recordable) => void
|
||||||
|
setProps: (props: Recordable) => void
|
||||||
|
delSchema: (field: string) => void
|
||||||
|
addSchema: (formSchema: FormSchema, index?: number) => void
|
||||||
|
setSchema: (schemaProps: FormSetProps[]) => void
|
||||||
|
formModel: Recordable
|
||||||
|
}
|
||||||
|
|
||||||
export { Search }
|
export { Search }
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="tsx">
|
||||||
import { Form } from '@/components/Form'
|
import { Form, FormSchema, FormSetProps } from '@/components/Form'
|
||||||
import { PropType, computed, unref, ref } from 'vue'
|
import { PropType, computed, unref, ref, watch, onMounted } from 'vue'
|
||||||
import { propTypes } from '@/utils/propTypes'
|
import { propTypes } from '@/utils/propTypes'
|
||||||
import { ElButton } from 'element-plus'
|
|
||||||
import { useI18n } from '@/hooks/web/useI18n'
|
|
||||||
import { useForm } from '@/hooks/web/useForm'
|
import { useForm } from '@/hooks/web/useForm'
|
||||||
import { findIndex } from '@/utils'
|
import { findIndex } from '@/utils'
|
||||||
import { cloneDeep } from 'lodash-es'
|
import { cloneDeep, set } from 'lodash-es'
|
||||||
import { FormSchema } from '@/types/form'
|
import { initModel } from '@/components/Form/src/helper'
|
||||||
|
import ActionButton from './components/ActionButton.vue'
|
||||||
const { t } = useI18n()
|
import { SearchProps } from './types'
|
||||||
|
import { FormItemProp } from 'element-plus'
|
||||||
|
import { isObject, isEmptyVal } from '@/utils/is'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
// 生成Form的布局结构数组
|
// 生成Form的布局结构数组
|
||||||
@@ -24,41 +24,72 @@ const props = defineProps({
|
|||||||
// 操作按钮风格位置
|
// 操作按钮风格位置
|
||||||
layout: propTypes.string.validate((v: string) => ['inline', 'bottom'].includes(v)).def('inline'),
|
layout: propTypes.string.validate((v: string) => ['inline', 'bottom'].includes(v)).def('inline'),
|
||||||
// 底部按钮的对齐方式
|
// 底部按钮的对齐方式
|
||||||
buttomPosition: propTypes.string
|
buttonPosition: propTypes.string
|
||||||
.validate((v: string) => ['left', 'center', 'right'].includes(v))
|
.validate((v: string) => ['left', 'center', 'right'].includes(v))
|
||||||
.def('center'),
|
.def('center'),
|
||||||
showSearch: propTypes.bool.def(true),
|
showSearch: propTypes.bool.def(true),
|
||||||
showReset: propTypes.bool.def(true),
|
showReset: propTypes.bool.def(true),
|
||||||
// 是否显示伸缩
|
// 是否显示伸缩
|
||||||
expand: propTypes.bool.def(false),
|
showExpand: propTypes.bool.def(false),
|
||||||
// 伸缩的界限字段
|
// 伸缩的界限字段
|
||||||
expandField: propTypes.string.def(''),
|
expandField: propTypes.string.def(''),
|
||||||
inline: propTypes.bool.def(true),
|
inline: propTypes.bool.def(true),
|
||||||
|
// 是否去除空值项
|
||||||
|
removeNoValueItem: propTypes.bool.def(true),
|
||||||
model: {
|
model: {
|
||||||
type: Object as PropType<Recordable>,
|
type: Object as PropType<Recordable>,
|
||||||
default: () => ({})
|
default: () => ({})
|
||||||
}
|
},
|
||||||
|
searchLoading: propTypes.bool.def(false),
|
||||||
|
resetLoading: propTypes.bool.def(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['search', 'reset'])
|
const emit = defineEmits(['search', 'reset', 'register', 'validate'])
|
||||||
|
|
||||||
const visible = ref(true)
|
const visible = ref(true)
|
||||||
|
|
||||||
|
// 表单数据
|
||||||
|
const formModel = ref<Recordable>(props.model)
|
||||||
|
|
||||||
const newSchema = computed(() => {
|
const newSchema = computed(() => {
|
||||||
let schema: FormSchema[] = cloneDeep(props.schema)
|
const propsComputed = unref(getProps)
|
||||||
if (props.expand && props.expandField && !unref(visible)) {
|
let schema: FormSchema[] = cloneDeep(propsComputed.schema)
|
||||||
const index = findIndex(schema, (v: FormSchema) => v.field === props.expandField)
|
if (propsComputed.showExpand && propsComputed.expandField && !unref(visible)) {
|
||||||
if (index > -1) {
|
const index = findIndex(schema, (v: FormSchema) => v.field === propsComputed.expandField)
|
||||||
const length = schema.length
|
schema.map((v, i) => {
|
||||||
schema.splice(index + 1, length)
|
if (i >= index) {
|
||||||
|
v.hidden = true
|
||||||
|
} else {
|
||||||
|
v.hidden = false
|
||||||
}
|
}
|
||||||
|
return v
|
||||||
|
})
|
||||||
}
|
}
|
||||||
if (props.layout === 'inline') {
|
if (propsComputed.layout === 'inline') {
|
||||||
schema = schema.concat([
|
schema = schema.concat([
|
||||||
{
|
{
|
||||||
field: 'action',
|
field: 'action',
|
||||||
formItemProps: {
|
formItemProps: {
|
||||||
labelWidth: '0px'
|
labelWidth: '0px',
|
||||||
|
slots: {
|
||||||
|
default: () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<ActionButton
|
||||||
|
showSearch={propsComputed.showSearch}
|
||||||
|
showReset={propsComputed.showReset}
|
||||||
|
showExpand={propsComputed.showExpand}
|
||||||
|
searchLoading={propsComputed.searchLoading}
|
||||||
|
resetLoading={propsComputed.resetLoading}
|
||||||
|
visible={visible.value}
|
||||||
|
onExpand={setVisible}
|
||||||
|
onReset={reset}
|
||||||
|
onSearch={search}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
@@ -66,81 +97,167 @@ const newSchema = computed(() => {
|
|||||||
return schema
|
return schema
|
||||||
})
|
})
|
||||||
|
|
||||||
const { register, elFormRef, methods } = useForm({
|
const { formRegister, formMethods } = useForm()
|
||||||
model: props.model || {}
|
const { getElFormExpose, getFormData, getFormExpose } = formMethods
|
||||||
|
|
||||||
|
// useSearch传入的props
|
||||||
|
const outsideProps = ref<SearchProps>({})
|
||||||
|
|
||||||
|
const mergeProps = ref<SearchProps>({})
|
||||||
|
|
||||||
|
const getProps = computed(() => {
|
||||||
|
const propsObj = { ...props }
|
||||||
|
Object.assign(propsObj, unref(mergeProps))
|
||||||
|
return propsObj
|
||||||
})
|
})
|
||||||
|
|
||||||
const search = async () => {
|
const setProps = (props: SearchProps = {}) => {
|
||||||
await unref(elFormRef)?.validate(async (isValid) => {
|
mergeProps.value = Object.assign(unref(mergeProps), props)
|
||||||
if (isValid) {
|
// @ts-ignore
|
||||||
const { getFormData } = methods
|
outsideProps.value = props
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听表单结构化数组,重新生成formModel
|
||||||
|
watch(
|
||||||
|
() => unref(newSchema),
|
||||||
|
async (schema = []) => {
|
||||||
|
formModel.value = initModel(schema, unref(formModel))
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true,
|
||||||
|
deep: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const filterModel = async () => {
|
||||||
const model = await getFormData()
|
const model = await getFormData()
|
||||||
|
if (unref(getProps).removeNoValueItem) {
|
||||||
|
// 使用reduce过滤空值,并返回一个新对象
|
||||||
|
return Object.keys(model).reduce((prev, next) => {
|
||||||
|
const value = model[next]
|
||||||
|
if (!isEmptyVal(value)) {
|
||||||
|
if (isObject(value)) {
|
||||||
|
if (Object.keys(value).length > 0) {
|
||||||
|
prev[next] = value
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
prev[next] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return prev
|
||||||
|
}, {})
|
||||||
|
}
|
||||||
|
return model
|
||||||
|
}
|
||||||
|
|
||||||
|
const search = async () => {
|
||||||
|
const elFormExpose = await getElFormExpose()
|
||||||
|
await elFormExpose?.validate(async (isValid) => {
|
||||||
|
if (isValid) {
|
||||||
|
const model = await filterModel()
|
||||||
emit('search', model)
|
emit('search', model)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const reset = async () => {
|
const reset = async () => {
|
||||||
unref(elFormRef)?.resetFields()
|
const elFormExpose = await getElFormExpose()
|
||||||
const { getFormData } = methods
|
elFormExpose?.resetFields()
|
||||||
const model = await getFormData()
|
const model = await filterModel()
|
||||||
emit('reset', model)
|
emit('reset', model)
|
||||||
}
|
}
|
||||||
|
|
||||||
const bottonButtonStyle = computed(() => {
|
const bottomButtonStyle = computed(() => {
|
||||||
return {
|
return {
|
||||||
textAlign: props.buttomPosition as unknown as 'left' | 'center' | 'right'
|
textAlign: unref(getProps).buttonPosition as unknown as 'left' | 'center' | 'right'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const setVisible = () => {
|
const setVisible = async () => {
|
||||||
unref(elFormRef)?.resetFields()
|
|
||||||
visible.value = !unref(visible)
|
visible.value = !unref(visible)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const setSchema = (schemaProps: FormSetProps[]) => {
|
||||||
|
const { schema } = unref(getProps)
|
||||||
|
for (const v of schema) {
|
||||||
|
for (const item of schemaProps) {
|
||||||
|
if (v.field === item.field) {
|
||||||
|
set(v, item.path, item.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对表单赋值
|
||||||
|
const setValues = async (data: Recordable = {}) => {
|
||||||
|
formModel.value = Object.assign(props.model, unref(formModel), data)
|
||||||
|
const formExpose = await getFormExpose()
|
||||||
|
formExpose?.setValues(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
const delSchema = (field: string) => {
|
||||||
|
const { schema } = unref(getProps)
|
||||||
|
|
||||||
|
const index = findIndex(schema, (v: FormSchema) => v.field === field)
|
||||||
|
if (index > -1) {
|
||||||
|
schema.splice(index, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const addSchema = (formSchema: FormSchema, index?: number) => {
|
||||||
|
const { schema } = unref(getProps)
|
||||||
|
if (index !== void 0) {
|
||||||
|
schema.splice(index, 0, formSchema)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
schema.push(formSchema)
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultExpose = {
|
||||||
|
getElFormExpose,
|
||||||
|
setProps,
|
||||||
|
setSchema,
|
||||||
|
setValues,
|
||||||
|
delSchema,
|
||||||
|
addSchema
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
emit('register', defaultExpose)
|
||||||
|
})
|
||||||
|
|
||||||
|
defineExpose(defaultExpose)
|
||||||
|
|
||||||
|
const onFormValidate = (prop: FormItemProp, isValid: boolean, message: string) => {
|
||||||
|
emit('validate', prop, isValid, message)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Form
|
<Form
|
||||||
|
:model="formModel"
|
||||||
:is-custom="false"
|
:is-custom="false"
|
||||||
:label-width="labelWidth"
|
:label-width="getProps.labelWidth"
|
||||||
hide-required-asterisk
|
hide-required-asterisk
|
||||||
:inline="inline"
|
:inline="getProps.inline"
|
||||||
:is-col="isCol"
|
:is-col="getProps.isCol"
|
||||||
:schema="newSchema"
|
:schema="newSchema"
|
||||||
@register="register"
|
@register="formRegister"
|
||||||
>
|
@validate="onFormValidate"
|
||||||
<template #action>
|
/>
|
||||||
<div v-if="layout === 'inline'">
|
|
||||||
<ElButton v-if="showSearch" type="primary" @click="search">
|
|
||||||
<Icon icon="ep:search" class="mr-5px" />
|
|
||||||
{{ t('common.query') }}
|
|
||||||
</ElButton>
|
|
||||||
<ElButton v-if="showReset" @click="reset">
|
|
||||||
<Icon icon="ep:refresh-right" class="mr-5px" />
|
|
||||||
{{ t('common.reset') }}
|
|
||||||
</ElButton>
|
|
||||||
<ElButton v-if="expand" text @click="setVisible">
|
|
||||||
{{ t(visible ? 'common.shrink' : 'common.expand') }}
|
|
||||||
<Icon :icon="visible ? 'ant-design:up-outlined' : 'ant-design:down-outlined'" />
|
|
||||||
</ElButton>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</Form>
|
|
||||||
|
|
||||||
<template v-if="layout === 'bottom'">
|
<template v-if="layout === 'bottom'">
|
||||||
<div :style="bottonButtonStyle">
|
<div :style="bottomButtonStyle">
|
||||||
<ElButton v-if="showSearch" type="primary" @click="search">
|
<ActionButton
|
||||||
<Icon icon="ep:search" class="mr-5px" />
|
:show-reset="getProps.showReset"
|
||||||
{{ t('common.query') }}
|
:show-search="getProps.showSearch"
|
||||||
</ElButton>
|
:show-expand="getProps.showExpand"
|
||||||
<ElButton v-if="showReset" @click="reset">
|
:search-loading="getProps.searchLoading"
|
||||||
<Icon icon="ep:refresh-right" class="mr-5px" />
|
:reset-loading="getProps.resetLoading"
|
||||||
{{ t('common.reset') }}
|
@expand="setVisible"
|
||||||
</ElButton>
|
@reset="reset"
|
||||||
<ElButton v-if="expand" text @click="setVisible">
|
@search="search"
|
||||||
{{ t(visible ? 'common.shrink' : 'common.expand') }}
|
/>
|
||||||
<Icon :icon="visible ? 'ant-design:up-outlined' : 'ant-design:down-outlined'" />
|
|
||||||
</ElButton>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
59
src/components/Search/src/components/ActionButton.vue
Normal file
59
src/components/Search/src/components/ActionButton.vue
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ElButton } from 'element-plus'
|
||||||
|
import { useIcon } from '@/hooks/web/useIcon'
|
||||||
|
import { propTypes } from '@/utils/propTypes'
|
||||||
|
import { useI18n } from '@/hooks/web/useI18n'
|
||||||
|
|
||||||
|
const emit = defineEmits(['search', 'reset', 'expand'])
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
showSearch: propTypes.bool.def(true),
|
||||||
|
showReset: propTypes.bool.def(true),
|
||||||
|
showExpand: propTypes.bool.def(false),
|
||||||
|
visible: propTypes.bool.def(true),
|
||||||
|
searchLoading: propTypes.bool.def(false),
|
||||||
|
resetLoading: propTypes.bool.def(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
const onSearch = () => {
|
||||||
|
emit('search')
|
||||||
|
}
|
||||||
|
|
||||||
|
const onReset = () => {
|
||||||
|
emit('reset')
|
||||||
|
}
|
||||||
|
|
||||||
|
const onExpand = () => {
|
||||||
|
emit('expand')
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ElButton
|
||||||
|
v-if="showSearch"
|
||||||
|
type="primary"
|
||||||
|
:loading="searchLoading"
|
||||||
|
:icon="useIcon({ icon: 'ep:search' })"
|
||||||
|
@click="onSearch"
|
||||||
|
>
|
||||||
|
{{ t('common.query') }}
|
||||||
|
</ElButton>
|
||||||
|
<ElButton
|
||||||
|
v-if="showReset"
|
||||||
|
:loading="resetLoading"
|
||||||
|
:icon="useIcon({ icon: 'ep:refresh-right' })"
|
||||||
|
@click="onReset"
|
||||||
|
>
|
||||||
|
{{ t('common.reset') }}
|
||||||
|
</ElButton>
|
||||||
|
<ElButton
|
||||||
|
v-if="showExpand"
|
||||||
|
:icon="useIcon({ icon: visible ? 'ep:arrow-down' : 'ep:arrow-up' })"
|
||||||
|
text
|
||||||
|
@click="onExpand"
|
||||||
|
>
|
||||||
|
{{ t(visible ? 'common.shrink' : 'common.expand') }}
|
||||||
|
</ElButton>
|
||||||
|
</template>
|
||||||
16
src/components/Search/src/types/index.ts
Normal file
16
src/components/Search/src/types/index.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { FormSchema } from '@/components/Form'
|
||||||
|
|
||||||
|
export interface SearchProps {
|
||||||
|
schema?: FormSchema[]
|
||||||
|
isCol?: boolean
|
||||||
|
labelWidth?: string | number
|
||||||
|
layout?: 'inline' | 'bottom'
|
||||||
|
buttonPosition?: 'left' | 'right' | 'center'
|
||||||
|
showSearch?: boolean
|
||||||
|
showReset?: boolean
|
||||||
|
showExpand?: boolean
|
||||||
|
expandField?: string
|
||||||
|
inline?: boolean
|
||||||
|
removeNoValueItem?: boolean
|
||||||
|
model?: Recordable
|
||||||
|
}
|
||||||
@@ -10,10 +10,12 @@ import { trim, setCssVar } from '@/utils'
|
|||||||
import ColorRadioPicker from './components/ColorRadioPicker.vue'
|
import ColorRadioPicker from './components/ColorRadioPicker.vue'
|
||||||
import InterfaceDisplay from './components/InterfaceDisplay.vue'
|
import InterfaceDisplay from './components/InterfaceDisplay.vue'
|
||||||
import LayoutRadioPicker from './components/LayoutRadioPicker.vue'
|
import LayoutRadioPicker from './components/LayoutRadioPicker.vue'
|
||||||
import { useCache } from '@/hooks/web/useCache'
|
import { useStorage } from '@/hooks/web/useStorage'
|
||||||
import { useClipboard } from '@vueuse/core'
|
import { useClipboard } from '@vueuse/core'
|
||||||
import { useDesign } from '@/hooks/web/useDesign'
|
import { useDesign } from '@/hooks/web/useDesign'
|
||||||
|
|
||||||
|
const { removeStorage } = useStorage()
|
||||||
|
|
||||||
const { getPrefixCls } = useDesign()
|
const { getPrefixCls } = useDesign()
|
||||||
|
|
||||||
const prefixCls = getPrefixCls('setting')
|
const prefixCls = getPrefixCls('setting')
|
||||||
@@ -47,7 +49,6 @@ const setHeaderTheme = (color: string) => {
|
|||||||
setCssVar('--top-header-bg-color', color)
|
setCssVar('--top-header-bg-color', color)
|
||||||
setCssVar('--top-header-text-color', textColor)
|
setCssVar('--top-header-text-color', textColor)
|
||||||
setCssVar('--top-header-hover-color', textHoverColor)
|
setCssVar('--top-header-hover-color', textHoverColor)
|
||||||
setCssVar('--layout-border-color', topToolBorderColor)
|
|
||||||
appStore.setTheme({
|
appStore.setTheme({
|
||||||
topHeaderBgColor: color,
|
topHeaderBgColor: color,
|
||||||
topHeaderTextColor: textColor,
|
topHeaderTextColor: textColor,
|
||||||
@@ -92,10 +93,6 @@ const setMenuTheme = (color: string) => {
|
|||||||
appStore.setTheme(theme)
|
appStore.setTheme(theme)
|
||||||
appStore.setCssVarTheme()
|
appStore.setCssVarTheme()
|
||||||
}
|
}
|
||||||
if (layout.value === 'top' && !appStore.getIsDark) {
|
|
||||||
headerTheme.value = '#fff'
|
|
||||||
setHeaderTheme('#fff')
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听layout变化,重置一些主题色
|
// 监听layout变化,重置一些主题色
|
||||||
watch(
|
watch(
|
||||||
@@ -191,10 +188,9 @@ const copyConfig = async () => {
|
|||||||
|
|
||||||
// 清空缓存
|
// 清空缓存
|
||||||
const clear = () => {
|
const clear = () => {
|
||||||
const { wsCache } = useCache()
|
removeStorage('layout')
|
||||||
wsCache.delete('layout')
|
removeStorage('theme')
|
||||||
wsCache.delete('theme')
|
removeStorage('isDark')
|
||||||
wsCache.delete('isDark')
|
|
||||||
window.location.reload()
|
window.location.reload()
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -202,7 +198,7 @@ const clear = () => {
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
:class="prefixCls"
|
:class="prefixCls"
|
||||||
class="fixed top-[45%] right-0 w-40px h-40px flex items-center justify-center bg-[var(--el-color-primary)] cursor-pointer"
|
class="fixed top-[45%] right-0 w-40px h-40px flex items-center justify-center bg-[var(--el-color-primary)] cursor-pointer z-10"
|
||||||
@click="drawer = true"
|
@click="drawer = true"
|
||||||
>
|
>
|
||||||
<Icon icon="ant-design:setting-outlined" color="#fff" />
|
<Icon icon="ant-design:setting-outlined" color="#fff" />
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { ElDropdown, ElDropdownMenu, ElDropdownItem } from 'element-plus'
|
import { ElDropdown, ElDropdownMenu, ElDropdownItem, ComponentSize } from 'element-plus'
|
||||||
import { useAppStore } from '@/store/modules/app'
|
import { useAppStore } from '@/store/modules/app'
|
||||||
import { useI18n } from '@/hooks/web/useI18n'
|
import { useI18n } from '@/hooks/web/useI18n'
|
||||||
import { propTypes } from '@/utils/propTypes'
|
import { propTypes } from '@/utils/propTypes'
|
||||||
import { useDesign } from '@/hooks/web/useDesign'
|
import { useDesign } from '@/hooks/web/useDesign'
|
||||||
import { ElementPlusSize } from '@/types/elementPlus'
|
|
||||||
|
|
||||||
const { getPrefixCls } = useDesign()
|
const { getPrefixCls } = useDesign()
|
||||||
|
|
||||||
@@ -21,7 +20,7 @@ const appStore = useAppStore()
|
|||||||
|
|
||||||
const sizeMap = computed(() => appStore.sizeMap)
|
const sizeMap = computed(() => appStore.sizeMap)
|
||||||
|
|
||||||
const setCurrentSize = (size: ElementPlusSize) => {
|
const setCurrentSize = (size: ComponentSize) => {
|
||||||
appStore.setCurrentSize(size)
|
appStore.setCurrentSize(size)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
import Sticky from './src/Sticky.vue'
|
|
||||||
|
|
||||||
export { Sticky }
|
|
||||||
@@ -1,141 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { propTypes } from '@/utils/propTypes'
|
|
||||||
import { ref, onMounted, onActivated, shallowRef } from 'vue'
|
|
||||||
import { useEventListener, useWindowSize, isClient } from '@vueuse/core'
|
|
||||||
import type { CSSProperties } from 'vue'
|
|
||||||
const props = defineProps({
|
|
||||||
// 距离顶部或者底部的距离(单位px)
|
|
||||||
offset: propTypes.number.def(0),
|
|
||||||
// 设置元素的堆叠顺序
|
|
||||||
zIndex: propTypes.number.def(999),
|
|
||||||
// 设置指定的class
|
|
||||||
className: propTypes.string.def(''),
|
|
||||||
// 定位方式,默认为(top),表示距离顶部位置,可以设置为top或者bottom
|
|
||||||
position: {
|
|
||||||
type: String,
|
|
||||||
validator: function (value: string) {
|
|
||||||
return ['top', 'bottom'].indexOf(value) !== -1
|
|
||||||
},
|
|
||||||
default: 'top'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const width = ref('auto' as string)
|
|
||||||
const height = ref('auto' as string)
|
|
||||||
const isSticky = ref(false)
|
|
||||||
const refSticky = shallowRef<HTMLElement>()
|
|
||||||
const scrollContainer = shallowRef<HTMLElement | Window>()
|
|
||||||
const { height: windowHeight } = useWindowSize()
|
|
||||||
onMounted(() => {
|
|
||||||
height.value = refSticky.value?.getBoundingClientRect().height + 'px'
|
|
||||||
|
|
||||||
scrollContainer.value = getScrollContainer(refSticky.value!, true)
|
|
||||||
useEventListener(scrollContainer, 'scroll', handleScroll)
|
|
||||||
useEventListener('resize', handleReize)
|
|
||||||
handleScroll()
|
|
||||||
})
|
|
||||||
onActivated(() => {
|
|
||||||
handleScroll()
|
|
||||||
})
|
|
||||||
|
|
||||||
const camelize = (str: string): string => {
|
|
||||||
return str.replace(/-(\w)/g, (_, c) => (c ? c.toUpperCase() : ''))
|
|
||||||
}
|
|
||||||
|
|
||||||
const getStyle = (element: HTMLElement, styleName: keyof CSSProperties): string => {
|
|
||||||
if (!isClient || !element || !styleName) return ''
|
|
||||||
|
|
||||||
let key = camelize(styleName)
|
|
||||||
if (key === 'float') key = 'cssFloat'
|
|
||||||
try {
|
|
||||||
const style = element.style[styleName]
|
|
||||||
if (style) return style
|
|
||||||
const computed = document.defaultView?.getComputedStyle(element, '')
|
|
||||||
return computed ? computed[styleName] : ''
|
|
||||||
} catch {
|
|
||||||
return element.style[styleName]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const isScroll = (el: HTMLElement, isVertical?: boolean): boolean => {
|
|
||||||
if (!isClient) return false
|
|
||||||
const key = (
|
|
||||||
{
|
|
||||||
undefined: 'overflow',
|
|
||||||
true: 'overflow-y',
|
|
||||||
false: 'overflow-x'
|
|
||||||
} as const
|
|
||||||
)[String(isVertical)]!
|
|
||||||
const overflow = getStyle(el, key)
|
|
||||||
return ['scroll', 'auto', 'overlay'].some((s) => overflow.includes(s))
|
|
||||||
}
|
|
||||||
|
|
||||||
const getScrollContainer = (
|
|
||||||
el: HTMLElement,
|
|
||||||
isVertical: boolean
|
|
||||||
): Window | HTMLElement | undefined => {
|
|
||||||
if (!isClient) return
|
|
||||||
let parent = el
|
|
||||||
while (parent) {
|
|
||||||
if ([window, document, document.documentElement].includes(parent)) return window
|
|
||||||
if (isScroll(parent, isVertical)) return parent
|
|
||||||
parent = parent.parentNode as HTMLElement
|
|
||||||
}
|
|
||||||
return parent
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleScroll = () => {
|
|
||||||
width.value = refSticky.value!.getBoundingClientRect().width! + 'px'
|
|
||||||
if (props.position === 'top') {
|
|
||||||
const offsetTop = refSticky.value?.getBoundingClientRect().top
|
|
||||||
if (offsetTop !== undefined && offsetTop < props.offset) {
|
|
||||||
sticky()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
reset()
|
|
||||||
} else {
|
|
||||||
const offsetBottom = refSticky.value?.getBoundingClientRect().bottom
|
|
||||||
|
|
||||||
if (offsetBottom !== undefined && offsetBottom > windowHeight.value - props.offset) {
|
|
||||||
sticky()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
reset()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const handleReize = () => {
|
|
||||||
if (isSticky.value && refSticky.value) {
|
|
||||||
width.value = refSticky.value.getBoundingClientRect().width + 'px'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const sticky = () => {
|
|
||||||
if (isSticky.value) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
isSticky.value = true
|
|
||||||
}
|
|
||||||
const reset = () => {
|
|
||||||
if (!isSticky.value) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
width.value = 'auto'
|
|
||||||
isSticky.value = false
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<div :style="{ height: height, zIndex: zIndex }" ref="refSticky">
|
|
||||||
<div
|
|
||||||
:class="className"
|
|
||||||
:style="{
|
|
||||||
top: position === 'top' ? offset + 'px' : '',
|
|
||||||
bottom: position !== 'top' ? offset + 'px' : '',
|
|
||||||
zIndex: zIndex,
|
|
||||||
position: isSticky ? 'fixed' : 'static',
|
|
||||||
width: width,
|
|
||||||
height: height
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<slot>
|
|
||||||
<div>sticky</div>
|
|
||||||
</slot>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
@@ -1,11 +1,20 @@
|
|||||||
import Table from './src/Table.vue'
|
import Table from './src/Table.vue'
|
||||||
import { ElTable } from 'element-plus'
|
import { ElTable } from 'element-plus'
|
||||||
import { TableSetPropsType } from '@/types/table'
|
import { TableColumn, TableSetProps } from './src/types'
|
||||||
|
|
||||||
|
export type {
|
||||||
|
TableColumn,
|
||||||
|
TableSlotDefault,
|
||||||
|
Pagination,
|
||||||
|
TableSetProps,
|
||||||
|
TableProps
|
||||||
|
} from './src/types'
|
||||||
|
|
||||||
export interface TableExpose {
|
export interface TableExpose {
|
||||||
setProps: (props: Recordable) => void
|
setProps: (props: Recordable) => void
|
||||||
setColumn: (columnProps: TableSetPropsType[]) => void
|
setColumn: (columnProps: TableSetProps[]) => void
|
||||||
selections: Recordable[]
|
addColumn: (column: TableColumn, index?: number) => void
|
||||||
|
delColumn: (field: string) => void
|
||||||
elTableRef: ComponentRef<typeof ElTable>
|
elTableRef: ComponentRef<typeof ElTable>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,30 @@
|
|||||||
<script lang="tsx">
|
<script lang="tsx">
|
||||||
import { ElTable, ElTableColumn, ElPagination } from 'element-plus'
|
import {
|
||||||
|
ElTable,
|
||||||
|
ElTableColumn,
|
||||||
|
ElPagination,
|
||||||
|
ComponentSize,
|
||||||
|
ElTooltipProps,
|
||||||
|
ElImage
|
||||||
|
} from 'element-plus'
|
||||||
import { defineComponent, PropType, ref, computed, unref, watch, onMounted } from 'vue'
|
import { defineComponent, PropType, ref, computed, unref, watch, onMounted } from 'vue'
|
||||||
import { propTypes } from '@/utils/propTypes'
|
import { propTypes } from '@/utils/propTypes'
|
||||||
import { setIndex } from './helper'
|
import { setIndex } from './helper'
|
||||||
|
import type { TableProps, TableColumn, Pagination, TableSetProps } from './types'
|
||||||
|
import { set, get } from 'lodash-es'
|
||||||
|
import { CSSProperties } from 'vue'
|
||||||
import { getSlot } from '@/utils/tsxHelper'
|
import { getSlot } from '@/utils/tsxHelper'
|
||||||
import type { TableProps } from './types'
|
import TableActions from './components/TableActions.vue'
|
||||||
import { set } from 'lodash-es'
|
// import Sortable from 'sortablejs'
|
||||||
import { TableColumn, TableSlotDefault, Pagination, TableSetPropsType } from '../../../types/table'
|
// import { Icon } from '@/components/Icon'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'Table',
|
name: 'Table',
|
||||||
props: {
|
props: {
|
||||||
pageSize: propTypes.number.def(10),
|
pageSize: propTypes.number.def(10),
|
||||||
currentPage: propTypes.number.def(1),
|
currentPage: propTypes.number.def(1),
|
||||||
// 是否多选
|
// 是否展示表格的工具栏
|
||||||
selection: propTypes.bool.def(true),
|
showAction: propTypes.bool.def(false),
|
||||||
// 是否所有的超出隐藏,优先级低于schema中的showOverflowTooltip,
|
// 是否所有的超出隐藏,优先级低于schema中的showOverflowTooltip,
|
||||||
showOverflowTooltip: propTypes.bool.def(true),
|
showOverflowTooltip: propTypes.bool.def(true),
|
||||||
// 表头
|
// 表头
|
||||||
@@ -23,7 +33,7 @@ export default defineComponent({
|
|||||||
default: () => []
|
default: () => []
|
||||||
},
|
},
|
||||||
// 展开行
|
// 展开行
|
||||||
expand: propTypes.bool.def(false),
|
// expand: propTypes.bool.def(false),
|
||||||
// 是否展示分页
|
// 是否展示分页
|
||||||
pagination: {
|
pagination: {
|
||||||
type: Object as PropType<Pagination>,
|
type: Object as PropType<Pagination>,
|
||||||
@@ -46,10 +56,140 @@ export default defineComponent({
|
|||||||
data: {
|
data: {
|
||||||
type: Array as PropType<Recordable[]>,
|
type: Array as PropType<Recordable[]>,
|
||||||
default: () => []
|
default: () => []
|
||||||
}
|
|
||||||
},
|
},
|
||||||
emits: ['update:pageSize', 'update:currentPage', 'register'],
|
// 是否自动预览
|
||||||
setup(props, { attrs, slots, emit, expose }) {
|
preview: {
|
||||||
|
type: Array as PropType<string[]>,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
// sortable: propTypes.bool.def(false),
|
||||||
|
height: propTypes.oneOfType([Number, String]),
|
||||||
|
maxHeight: propTypes.oneOfType([Number, String]),
|
||||||
|
stripe: propTypes.bool.def(false),
|
||||||
|
border: propTypes.bool.def(true),
|
||||||
|
size: {
|
||||||
|
type: String as PropType<ComponentSize>,
|
||||||
|
validator: (v: ComponentSize) => ['medium', 'small', 'mini'].includes(v)
|
||||||
|
},
|
||||||
|
fit: propTypes.bool.def(true),
|
||||||
|
showHeader: propTypes.bool.def(true),
|
||||||
|
highlightCurrentRow: propTypes.bool.def(false),
|
||||||
|
currentRowKey: propTypes.oneOfType([Number, String]),
|
||||||
|
// row-class-name, 类型为 (row: Recordable, rowIndex: number) => string | string
|
||||||
|
rowClassName: {
|
||||||
|
type: [Function, String] as PropType<(row: Recordable, rowIndex: number) => string | string>,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
rowStyle: {
|
||||||
|
type: [Function, Object] as PropType<
|
||||||
|
(row: Recordable, rowIndex: number) => Recordable | CSSProperties
|
||||||
|
>,
|
||||||
|
default: () => undefined
|
||||||
|
},
|
||||||
|
cellClassName: {
|
||||||
|
type: [Function, String] as PropType<
|
||||||
|
(row: Recordable, column: any, rowIndex: number) => string | string
|
||||||
|
>,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
cellStyle: {
|
||||||
|
type: [Function, Object] as PropType<
|
||||||
|
(row: Recordable, column: any, rowIndex: number) => Recordable | CSSProperties
|
||||||
|
>,
|
||||||
|
default: () => undefined
|
||||||
|
},
|
||||||
|
headerRowClassName: {
|
||||||
|
type: [Function, String] as PropType<(row: Recordable, rowIndex: number) => string | string>,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
headerRowStyle: {
|
||||||
|
type: [Function, Object] as PropType<
|
||||||
|
(row: Recordable, rowIndex: number) => Recordable | CSSProperties
|
||||||
|
>,
|
||||||
|
default: () => undefined
|
||||||
|
},
|
||||||
|
headerCellClassName: {
|
||||||
|
type: [Function, String] as PropType<
|
||||||
|
(row: Recordable, column: any, rowIndex: number) => string | string
|
||||||
|
>,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
headerCellStyle: {
|
||||||
|
type: [Function, Object] as PropType<
|
||||||
|
(row: Recordable, column: any, rowIndex: number) => Recordable | CSSProperties
|
||||||
|
>,
|
||||||
|
default: () => undefined
|
||||||
|
},
|
||||||
|
rowKey: propTypes.string.def('id'),
|
||||||
|
emptyText: propTypes.string.def('No Data'),
|
||||||
|
defaultExpandAll: propTypes.bool.def(false),
|
||||||
|
expandRowKeys: {
|
||||||
|
type: Array as PropType<string[]>,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
defaultSort: {
|
||||||
|
type: Object as PropType<{ prop: string; order: string }>,
|
||||||
|
default: () => ({})
|
||||||
|
},
|
||||||
|
tooltipEffect: {
|
||||||
|
type: String as PropType<'dark' | 'light'>,
|
||||||
|
default: 'dark'
|
||||||
|
},
|
||||||
|
tooltipOptions: {
|
||||||
|
type: Object as PropType<
|
||||||
|
Pick<
|
||||||
|
ElTooltipProps,
|
||||||
|
| 'effect'
|
||||||
|
| 'enterable'
|
||||||
|
| 'hideAfter'
|
||||||
|
| 'offset'
|
||||||
|
| 'placement'
|
||||||
|
| 'popperClass'
|
||||||
|
| 'popperOptions'
|
||||||
|
| 'showAfter'
|
||||||
|
| 'showArrow'
|
||||||
|
>
|
||||||
|
>,
|
||||||
|
default: () => ({
|
||||||
|
enterable: true,
|
||||||
|
placement: 'top',
|
||||||
|
showArrow: true,
|
||||||
|
hideAfter: 200,
|
||||||
|
popperOptions: { strategy: 'fixed' }
|
||||||
|
})
|
||||||
|
},
|
||||||
|
showSummary: propTypes.bool.def(false),
|
||||||
|
sumText: propTypes.string.def('Sum'),
|
||||||
|
summaryMethod: {
|
||||||
|
type: Function as PropType<(param: { columns: any[]; data: any[] }) => any[]>,
|
||||||
|
default: () => undefined
|
||||||
|
},
|
||||||
|
spanMethod: {
|
||||||
|
type: Function as PropType<
|
||||||
|
(param: { row: any; column: any; rowIndex: number; columnIndex: number }) => any[]
|
||||||
|
>,
|
||||||
|
default: () => undefined
|
||||||
|
},
|
||||||
|
selectOnIndeterminate: propTypes.bool.def(true),
|
||||||
|
indent: propTypes.number.def(16),
|
||||||
|
lazy: propTypes.bool.def(false),
|
||||||
|
load: {
|
||||||
|
type: Function as PropType<(row: Recordable, treeNode: any, resolve: Function) => void>,
|
||||||
|
default: () => undefined
|
||||||
|
},
|
||||||
|
treeProps: {
|
||||||
|
type: Object as PropType<{ hasChildren?: string; children?: string; label?: string }>,
|
||||||
|
default: () => ({ hasChildren: 'hasChildren', children: 'children', label: 'label' })
|
||||||
|
},
|
||||||
|
tableLayout: {
|
||||||
|
type: String as PropType<'auto' | 'fixed'>,
|
||||||
|
default: 'fixed'
|
||||||
|
},
|
||||||
|
scrollbarAlwaysOn: propTypes.bool.def(false),
|
||||||
|
flexible: propTypes.bool.def(false)
|
||||||
|
},
|
||||||
|
emits: ['update:pageSize', 'update:currentPage', 'register', 'refresh', 'sortable-change'],
|
||||||
|
setup(props, { attrs, emit, slots, expose }) {
|
||||||
const elTableRef = ref<ComponentRef<typeof ElTable>>()
|
const elTableRef = ref<ComponentRef<typeof ElTable>>()
|
||||||
|
|
||||||
// 注册
|
// 注册
|
||||||
@@ -73,12 +213,39 @@ export default defineComponent({
|
|||||||
return propsObj
|
return propsObj
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// const sortableEl = ref()
|
||||||
|
// 初始化拖拽
|
||||||
|
// const initDropTable = () => {
|
||||||
|
// const el = unref(elTableRef)?.$el.querySelector('.el-table__body tbody')
|
||||||
|
// if (!el) return
|
||||||
|
// if (unref(sortableEl)) unref(sortableEl).destroy()
|
||||||
|
|
||||||
|
// sortableEl.value = Sortable.create(el, {
|
||||||
|
// handle: '.table-move',
|
||||||
|
// animation: 180,
|
||||||
|
// onEnd(e: any) {
|
||||||
|
// emit('sortable-change', e)
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
// watch(
|
||||||
|
// () => getProps.value.sortable,
|
||||||
|
// async (v) => {
|
||||||
|
// await nextTick()
|
||||||
|
// v && initDropTable()
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// immediate: true
|
||||||
|
// }
|
||||||
|
// )
|
||||||
|
|
||||||
const setProps = (props: TableProps = {}) => {
|
const setProps = (props: TableProps = {}) => {
|
||||||
mergeProps.value = Object.assign(unref(mergeProps), props)
|
mergeProps.value = Object.assign(unref(mergeProps), props)
|
||||||
outsideProps.value = props
|
outsideProps.value = { ...props } as any
|
||||||
}
|
}
|
||||||
|
|
||||||
const setColumn = (columnProps: TableSetPropsType[], columnsChildren?: TableColumn[]) => {
|
const setColumn = (columnProps: TableSetProps[], columnsChildren?: TableColumn[]) => {
|
||||||
const { columns } = unref(getProps)
|
const { columns } = unref(getProps)
|
||||||
for (const v of columnsChildren || columns) {
|
for (const v of columnsChildren || columns) {
|
||||||
for (const item of columnProps) {
|
for (const item of columnProps) {
|
||||||
@@ -91,16 +258,36 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const selections = ref<Recordable[]>([])
|
const addColumn = (column: TableColumn, index?: number) => {
|
||||||
|
const { columns } = unref(getProps)
|
||||||
|
if (index) {
|
||||||
|
columns.splice(index, 0, column)
|
||||||
|
} else {
|
||||||
|
columns.push(column)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const selectionChange = (selection: Recordable[]) => {
|
const delColumn = (field: string) => {
|
||||||
selections.value = selection
|
const { columns } = unref(getProps)
|
||||||
|
const index = columns.findIndex((item) => item.field === field)
|
||||||
|
if (index > -1) {
|
||||||
|
columns.splice(index, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const refresh = () => {
|
||||||
|
emit('refresh')
|
||||||
|
}
|
||||||
|
|
||||||
|
const changSize = (size: ComponentSize) => {
|
||||||
|
setProps({ size })
|
||||||
}
|
}
|
||||||
|
|
||||||
expose({
|
expose({
|
||||||
setProps,
|
setProps,
|
||||||
setColumn,
|
setColumn,
|
||||||
selections,
|
delColumn,
|
||||||
|
addColumn,
|
||||||
elTableRef
|
elTableRef
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -149,44 +336,44 @@ export default defineComponent({
|
|||||||
)
|
)
|
||||||
|
|
||||||
const getBindValue = computed(() => {
|
const getBindValue = computed(() => {
|
||||||
const bindValue: Recordable = { ...attrs, ...props }
|
const bindValue: Recordable = { ...attrs, ...unref(getProps) }
|
||||||
delete bindValue.columns
|
delete bindValue.columns
|
||||||
delete bindValue.data
|
delete bindValue.data
|
||||||
return bindValue
|
return bindValue
|
||||||
})
|
})
|
||||||
|
|
||||||
const renderTableSelection = () => {
|
const renderTreeTableColumn = (columnsChildren: TableColumn[]) => {
|
||||||
const { selection, reserveSelection, align, headerAlign } = unref(getProps)
|
const { align, headerAlign, showOverflowTooltip, preview } = unref(getProps)
|
||||||
// 渲染多选
|
|
||||||
return selection ? (
|
|
||||||
<ElTableColumn
|
|
||||||
type="selection"
|
|
||||||
reserveSelection={reserveSelection}
|
|
||||||
align={align}
|
|
||||||
headerAlign={headerAlign}
|
|
||||||
width="50"
|
|
||||||
></ElTableColumn>
|
|
||||||
) : undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
const renderTableExpand = () => {
|
|
||||||
const { align, headerAlign, expand } = unref(getProps)
|
|
||||||
// 渲染展开行
|
|
||||||
return expand ? (
|
|
||||||
<ElTableColumn type="expand" align={align} headerAlign={headerAlign}>
|
|
||||||
{{
|
|
||||||
// @ts-ignore
|
|
||||||
default: (data: TableSlotDefault) => getSlot(slots, 'expand', data)
|
|
||||||
}}
|
|
||||||
</ElTableColumn>
|
|
||||||
) : undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
const rnderTreeTableColumn = (columnsChildren: TableColumn[]) => {
|
|
||||||
const { align, headerAlign, showOverflowTooltip } = unref(getProps)
|
|
||||||
return columnsChildren.map((v) => {
|
return columnsChildren.map((v) => {
|
||||||
const props = { ...v }
|
if (v.hidden) return null
|
||||||
|
const props = { ...v } as any
|
||||||
if (props.children) delete props.children
|
if (props.children) delete props.children
|
||||||
|
|
||||||
|
const children = v.children
|
||||||
|
|
||||||
|
const slots = {
|
||||||
|
default: (...args: any[]) => {
|
||||||
|
const data = args[0]
|
||||||
|
let isImageUrl = false
|
||||||
|
if (preview.length) {
|
||||||
|
isImageUrl = preview.some((item) => (item as string) === v.field)
|
||||||
|
}
|
||||||
|
|
||||||
|
return children && children.length
|
||||||
|
? renderTreeTableColumn(children)
|
||||||
|
: props?.slots?.default
|
||||||
|
? props.slots.default(args)
|
||||||
|
: v?.formatter
|
||||||
|
? v?.formatter?.(data.row, data.column, get(data.row, v.field), data.$index)
|
||||||
|
: isImageUrl
|
||||||
|
? renderPreview(get(data.row, v.field))
|
||||||
|
: get(data.row, v.field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (props?.slots?.header) {
|
||||||
|
slots['header'] = (...args: any[]) => props.slots.header(args)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ElTableColumn
|
<ElTableColumn
|
||||||
showOverflowTooltip={showOverflowTooltip}
|
showOverflowTooltip={showOverflowTooltip}
|
||||||
@@ -195,23 +382,28 @@ export default defineComponent({
|
|||||||
{...props}
|
{...props}
|
||||||
prop={v.field}
|
prop={v.field}
|
||||||
>
|
>
|
||||||
{{
|
{slots}
|
||||||
default: (data: TableSlotDefault) =>
|
|
||||||
v.children && v.children.length
|
|
||||||
? rnderTableColumn(v.children)
|
|
||||||
: // @ts-ignore
|
|
||||||
getSlot(slots, v.field, data) ||
|
|
||||||
v?.formatter?.(data.row, data.column, data.row[v.field], data.$index) ||
|
|
||||||
data.row[v.field],
|
|
||||||
// @ts-ignore
|
|
||||||
header: getSlot(slots, `${v.field}-header`)
|
|
||||||
}}
|
|
||||||
</ElTableColumn>
|
</ElTableColumn>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const rnderTableColumn = (columnsChildren?: TableColumn[]) => {
|
const renderPreview = (url: string) => {
|
||||||
|
return (
|
||||||
|
<div class="flex items-center">
|
||||||
|
<ElImage
|
||||||
|
src={url}
|
||||||
|
fit="cover"
|
||||||
|
class="w-[100%] h-100px"
|
||||||
|
lazy
|
||||||
|
preview-src-list={[url]}
|
||||||
|
preview-teleported
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderTableColumn = (columnsChildren?: TableColumn[]) => {
|
||||||
const {
|
const {
|
||||||
columns,
|
columns,
|
||||||
reserveIndex,
|
reserveIndex,
|
||||||
@@ -219,19 +411,19 @@ export default defineComponent({
|
|||||||
currentPage,
|
currentPage,
|
||||||
align,
|
align,
|
||||||
headerAlign,
|
headerAlign,
|
||||||
showOverflowTooltip
|
showOverflowTooltip,
|
||||||
|
reserveSelection,
|
||||||
|
preview
|
||||||
} = unref(getProps)
|
} = unref(getProps)
|
||||||
return [...[renderTableExpand()], ...[renderTableSelection()]].concat(
|
|
||||||
(columnsChildren || columns).map((v) => {
|
return (columnsChildren || columns).map((v) => {
|
||||||
// 自定生成序号
|
if (v.hidden) return null
|
||||||
if (v.type === 'index') {
|
if (v.type === 'index') {
|
||||||
return (
|
return (
|
||||||
<ElTableColumn
|
<ElTableColumn
|
||||||
type="index"
|
type="index"
|
||||||
index={
|
index={
|
||||||
v.index
|
v.index ? v.index : (index) => setIndex(reserveIndex, index, pageSize, currentPage)
|
||||||
? v.index
|
|
||||||
: (index) => setIndex(reserveIndex, index, pageSize, currentPage)
|
|
||||||
}
|
}
|
||||||
align={v.align || align}
|
align={v.align || align}
|
||||||
headerAlign={v.headerAlign || headerAlign}
|
headerAlign={v.headerAlign || headerAlign}
|
||||||
@@ -239,9 +431,46 @@ export default defineComponent({
|
|||||||
width="65px"
|
width="65px"
|
||||||
></ElTableColumn>
|
></ElTableColumn>
|
||||||
)
|
)
|
||||||
|
} else if (v.type === 'selection') {
|
||||||
|
return (
|
||||||
|
<ElTableColumn
|
||||||
|
type="selection"
|
||||||
|
reserveSelection={reserveSelection}
|
||||||
|
align={align}
|
||||||
|
headerAlign={headerAlign}
|
||||||
|
width="50"
|
||||||
|
></ElTableColumn>
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
const props = { ...v }
|
const props = { ...v } as any
|
||||||
if (props.children) delete props.children
|
if (props.children) delete props.children
|
||||||
|
|
||||||
|
const children = v.children
|
||||||
|
|
||||||
|
const slots = {
|
||||||
|
default: (...args: any[]) => {
|
||||||
|
const data = args[0]
|
||||||
|
|
||||||
|
let isImageUrl = false
|
||||||
|
if (preview.length) {
|
||||||
|
isImageUrl = preview.some((item) => (item as string) === v.field)
|
||||||
|
}
|
||||||
|
|
||||||
|
return children && children.length
|
||||||
|
? renderTreeTableColumn(children)
|
||||||
|
: props?.slots?.default
|
||||||
|
? props.slots.default(args)
|
||||||
|
: v?.formatter
|
||||||
|
? v?.formatter?.(data.row, data.column, get(data.row, v.field), data.$index)
|
||||||
|
: isImageUrl
|
||||||
|
? renderPreview(get(data.row, v.field))
|
||||||
|
: get(data.row, v.field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (props?.slots?.header) {
|
||||||
|
slots['header'] = (...args: any[]) => props.slots.header(args)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ElTableColumn
|
<ElTableColumn
|
||||||
showOverflowTooltip={showOverflowTooltip}
|
showOverflowTooltip={showOverflowTooltip}
|
||||||
@@ -250,37 +479,49 @@ export default defineComponent({
|
|||||||
{...props}
|
{...props}
|
||||||
prop={v.field}
|
prop={v.field}
|
||||||
>
|
>
|
||||||
{{
|
{slots}
|
||||||
default: (data: TableSlotDefault) =>
|
|
||||||
v.children && v.children.length
|
|
||||||
? rnderTreeTableColumn(v.children)
|
|
||||||
: // @ts-ignore
|
|
||||||
getSlot(slots, v.field, data) ||
|
|
||||||
v?.formatter?.(data.row, data.column, data.row[v.field], data.$index) ||
|
|
||||||
data.row[v.field],
|
|
||||||
// @ts-ignore
|
|
||||||
header: () => getSlot(slots, `${v.field}-header`) || v.label
|
|
||||||
}}
|
|
||||||
</ElTableColumn>
|
</ElTableColumn>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => (
|
return () => {
|
||||||
|
const tableSlots = {}
|
||||||
|
if (getSlot(slots, 'empty')) {
|
||||||
|
tableSlots['empty'] = (...args: any[]) => getSlot(slots, 'empty', args)
|
||||||
|
}
|
||||||
|
if (getSlot(slots, 'append')) {
|
||||||
|
tableSlots['append'] = (...args: any[]) => getSlot(slots, 'append', args)
|
||||||
|
}
|
||||||
|
|
||||||
|
// const { sortable } = unref(getProps)
|
||||||
|
|
||||||
|
// const sortableEl = sortable ? (
|
||||||
|
// <ElTableColumn
|
||||||
|
// className="table-move cursor-move"
|
||||||
|
// type="sortable"
|
||||||
|
// prop="sortable"
|
||||||
|
// width="60px"
|
||||||
|
// align="center"
|
||||||
|
// >
|
||||||
|
// <Icon icon="ant-design:drag-outlined" />
|
||||||
|
// </ElTableColumn>
|
||||||
|
// ) : null
|
||||||
|
|
||||||
|
return (
|
||||||
<div v-loading={unref(getProps).loading}>
|
<div v-loading={unref(getProps).loading}>
|
||||||
<ElTable
|
{unref(getProps).showAction ? (
|
||||||
// @ts-ignore
|
<TableActions
|
||||||
ref={elTableRef}
|
columns={unref(getProps).columns}
|
||||||
data={unref(getProps).data}
|
onChangSize={changSize}
|
||||||
onSelection-change={selectionChange}
|
onRefresh={refresh}
|
||||||
{...unref(getBindValue)}
|
/>
|
||||||
>
|
) : null}
|
||||||
|
<ElTable ref={elTableRef} data={unref(getProps).data} {...unref(getBindValue)}>
|
||||||
{{
|
{{
|
||||||
default: () => rnderTableColumn(),
|
default: () => renderTableColumn(),
|
||||||
// @ts-ignore
|
...tableSlots
|
||||||
append: () => getSlot(slots, 'append')
|
|
||||||
}}
|
}}
|
||||||
</ElTable>
|
</ElTable>
|
||||||
{unref(getProps).pagination ? (
|
{unref(getProps).pagination ? (
|
||||||
@@ -294,5 +535,6 @@ export default defineComponent({
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
151
src/components/Table/src/components/TableActions.vue
Normal file
151
src/components/Table/src/components/TableActions.vue
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
<script lang="tsx">
|
||||||
|
import { defineComponent, unref, computed, PropType, watch } from 'vue'
|
||||||
|
import {
|
||||||
|
ElTooltip,
|
||||||
|
ElDropdown,
|
||||||
|
ElDropdownMenu,
|
||||||
|
ElDropdownItem,
|
||||||
|
ComponentSize
|
||||||
|
// ElPopover,
|
||||||
|
// ElTree
|
||||||
|
} 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 { cloneDeep } from 'lodash-es'
|
||||||
|
// import { eachTree } from '@/utils/tree'
|
||||||
|
|
||||||
|
const appStore = useAppStore()
|
||||||
|
const sizeMap = computed(() => appStore.sizeMap)
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'TableActions',
|
||||||
|
props: {
|
||||||
|
columns: {
|
||||||
|
type: Array as PropType<TableColumn[]>,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
emits: ['refresh', 'changSize'],
|
||||||
|
setup(props, { emit }) {
|
||||||
|
const refresh = () => {
|
||||||
|
emit('refresh')
|
||||||
|
}
|
||||||
|
|
||||||
|
const changSize = (size: ComponentSize) => {
|
||||||
|
emit('changSize', size)
|
||||||
|
}
|
||||||
|
|
||||||
|
const columns = computed(() => {
|
||||||
|
return cloneDeep(props.columns).filter((v) => {
|
||||||
|
// 去掉type为selection的列和expand的列
|
||||||
|
if (v.type !== 'selection' && v.type !== 'expand') {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => columns.value,
|
||||||
|
(newColumns) => {
|
||||||
|
console.log('columns change:', newColumns)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deep: 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>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
||||||
|
{/* <ElTooltip content={t('common.columnSetting')} placement="top"> */}
|
||||||
|
{/* <ElPopover trigger="click" placement="left">
|
||||||
|
{{
|
||||||
|
default: () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<ElTree
|
||||||
|
data={unref(columns)}
|
||||||
|
show-checkbox
|
||||||
|
default-checked-keys={unref(defaultCheckeds)}
|
||||||
|
draggable
|
||||||
|
node-key="field"
|
||||||
|
allow-drop={(_draggingNode: any, _dropNode: any, type: string) => {
|
||||||
|
if (type === 'inner') {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onNode-drag-end={onNodeDragEnd}
|
||||||
|
onCheck-change={onCheckChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
reference: () => {
|
||||||
|
return (
|
||||||
|
<Icon
|
||||||
|
icon="ant-design:setting-outlined"
|
||||||
|
class="cursor-pointer"
|
||||||
|
hoverColor="var(--el-color-primary)"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
</ElPopover> */}
|
||||||
|
{/* </ElTooltip> */}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
import { Pagination, TableColumn } from '@/types/table'
|
|
||||||
|
|
||||||
export type TableProps = {
|
|
||||||
pageSize?: number
|
|
||||||
currentPage?: number
|
|
||||||
// 是否多选
|
|
||||||
selection?: boolean
|
|
||||||
// 是否所有的超出隐藏,优先级低于schema中的showOverflowTooltip,
|
|
||||||
showOverflowTooltip?: boolean
|
|
||||||
// 表头
|
|
||||||
columns?: TableColumn[]
|
|
||||||
// 是否展示分页
|
|
||||||
pagination?: Pagination | undefined
|
|
||||||
// 仅对 type=selection 的列有效,类型为 Boolean,为 true 则会在数据更新之后保留之前选中的数据(需指定 row-key)
|
|
||||||
reserveSelection?: boolean
|
|
||||||
// 加载状态
|
|
||||||
loading?: boolean
|
|
||||||
// 是否叠加索引
|
|
||||||
reserveIndex?: boolean
|
|
||||||
// 对齐方式
|
|
||||||
align?: 'left' | 'center' | 'right'
|
|
||||||
// 表头对齐方式
|
|
||||||
headerAlign?: 'left' | 'center' | 'right'
|
|
||||||
data?: Recordable
|
|
||||||
expand?: boolean
|
|
||||||
} & Recordable
|
|
||||||
97
src/components/Table/src/types/index.ts
Normal file
97
src/components/Table/src/types/index.ts
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
import { TableProps as ElTableProps } from 'element-plus'
|
||||||
|
export interface TableColumn {
|
||||||
|
field: string
|
||||||
|
label?: string
|
||||||
|
type?: string
|
||||||
|
/**
|
||||||
|
* 是否隐藏
|
||||||
|
*/
|
||||||
|
hidden?: boolean
|
||||||
|
children?: TableColumn[]
|
||||||
|
slots?: {
|
||||||
|
default?: (...args: any[]) => JSX.Element | JSX.Element[] | null
|
||||||
|
header?: (...args: any[]) => JSX.Element | null
|
||||||
|
}
|
||||||
|
index?: number | ((index: number) => number)
|
||||||
|
columnKey?: string
|
||||||
|
width?: string | number
|
||||||
|
minWidth?: string | number
|
||||||
|
fixed?: boolean | 'left' | 'right'
|
||||||
|
renderHeader?: (...args: any[]) => JSX.Element | null
|
||||||
|
// sortable?: boolean
|
||||||
|
sortMethod?: (...args: any[]) => number
|
||||||
|
sortBy?: string | string[] | ((...args: any[]) => string | string[])
|
||||||
|
sortOrders?: (string | null)[]
|
||||||
|
resizable?: boolean
|
||||||
|
formatter?: (...args: any[]) => any
|
||||||
|
showOverflowTooltip?: boolean
|
||||||
|
align?: 'left' | 'center' | 'right'
|
||||||
|
headerAlign?: 'left' | 'center' | 'right'
|
||||||
|
className?: string
|
||||||
|
labelClassName?: string
|
||||||
|
selectable?: (...args: any[]) => boolean
|
||||||
|
reserveSelection?: boolean
|
||||||
|
filters?: Array<{ text: string; value: string }>
|
||||||
|
filterPlacement?: string
|
||||||
|
filterMultiple?: boolean
|
||||||
|
filterMethod?: (...args: any[]) => boolean
|
||||||
|
filteredValue?: string[]
|
||||||
|
[key: string]: any
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TableSlotDefault {
|
||||||
|
row: Recordable
|
||||||
|
column: TableColumn
|
||||||
|
$index: number
|
||||||
|
[key: string]: any
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Pagination {
|
||||||
|
small?: boolean
|
||||||
|
background?: boolean
|
||||||
|
pageSize?: number
|
||||||
|
defaultPageSize?: number
|
||||||
|
total?: number
|
||||||
|
pageCount?: number
|
||||||
|
pagerCount?: number
|
||||||
|
currentPage?: number
|
||||||
|
defaultCurrentPage?: number
|
||||||
|
layout?: string
|
||||||
|
pageSizes?: number[]
|
||||||
|
popperClass?: string
|
||||||
|
prevText?: string
|
||||||
|
nextText?: string
|
||||||
|
disabled?: boolean
|
||||||
|
hideOnSinglePage?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TableSetProps {
|
||||||
|
field: string
|
||||||
|
path: string
|
||||||
|
value: any
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TableProps extends Omit<Partial<ElTableProps<any[]>>, 'data'> {
|
||||||
|
pageSize?: number
|
||||||
|
currentPage?: number
|
||||||
|
showAction?: boolean
|
||||||
|
// 是否所有的超出隐藏,优先级低于schema中的showOverflowTooltip,
|
||||||
|
showOverflowTooltip?: boolean
|
||||||
|
// 表头
|
||||||
|
columns?: TableColumn[]
|
||||||
|
// 是否展示分页
|
||||||
|
pagination?: Pagination | undefined
|
||||||
|
// 仅对 type=selection 的列有效,类型为 Boolean,为 true 则会在数据更新之后保留之前选中的数据(需指定 row-key)
|
||||||
|
reserveSelection?: boolean
|
||||||
|
// 加载状态
|
||||||
|
loading?: boolean
|
||||||
|
// 是否叠加索引
|
||||||
|
reserveIndex?: boolean
|
||||||
|
// 对齐方式
|
||||||
|
align?: 'left' | 'center' | 'right'
|
||||||
|
// 表头对齐方式
|
||||||
|
headerAlign?: 'left' | 'center' | 'right'
|
||||||
|
preview?: string[]
|
||||||
|
sortable?: boolean
|
||||||
|
data?: Recordable
|
||||||
|
}
|
||||||
@@ -35,6 +35,8 @@ const appStore = useAppStore()
|
|||||||
|
|
||||||
const tagsViewIcon = computed(() => appStore.getTagsViewIcon)
|
const tagsViewIcon = computed(() => appStore.getTagsViewIcon)
|
||||||
|
|
||||||
|
const isDark = computed(() => appStore.getIsDark)
|
||||||
|
|
||||||
// 初始化tag
|
// 初始化tag
|
||||||
const initTags = () => {
|
const initTags = () => {
|
||||||
affixTagArr.value = filterAffixTags(unref(routers))
|
affixTagArr.value = filterAffixTags(unref(routers))
|
||||||
@@ -73,7 +75,7 @@ const closeAllTags = () => {
|
|||||||
toLastView()
|
toLastView()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 关闭其他
|
// 关闭其它
|
||||||
const closeOthersTags = () => {
|
const closeOthersTags = () => {
|
||||||
tagsViewStore.delOthersViews(unref(selectedTag) as RouteLocationNormalizedLoaded)
|
tagsViewStore.delOthersViews(unref(selectedTag) as RouteLocationNormalizedLoaded)
|
||||||
}
|
}
|
||||||
@@ -270,7 +272,8 @@ watch(
|
|||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
icon="ep:d-arrow-left"
|
icon="ep:d-arrow-left"
|
||||||
:color="appStore.getIsDark ? 'var(--el-text-color-regular)' : '#333'"
|
color="var(--el-text-color-placeholder)"
|
||||||
|
:hover-color="isDark ? '#fff' : 'var(--el-color-black)'"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<div class="overflow-hidden flex-1">
|
<div class="overflow-hidden flex-1">
|
||||||
@@ -386,7 +389,8 @@ watch(
|
|||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
icon="ep:d-arrow-right"
|
icon="ep:d-arrow-right"
|
||||||
:color="appStore.getIsDark ? 'var(--el-text-color-regular)' : '#333'"
|
color="var(--el-text-color-placeholder)"
|
||||||
|
:hover-color="isDark ? '#fff' : 'var(--el-color-black)'"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
@@ -396,7 +400,8 @@ watch(
|
|||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
icon="ant-design:reload-outlined"
|
icon="ant-design:reload-outlined"
|
||||||
:color="appStore.getIsDark ? 'var(--el-text-color-regular)' : '#333'"
|
color="var(--el-text-color-placeholder)"
|
||||||
|
:hover-color="isDark ? '#fff' : 'var(--el-color-black)'"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<ContextMenu
|
<ContextMenu
|
||||||
@@ -459,7 +464,8 @@ watch(
|
|||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
icon="ant-design:setting-outlined"
|
icon="ant-design:setting-outlined"
|
||||||
:color="appStore.getIsDark ? 'var(--el-text-color-regular)' : '#333'"
|
color="var(--el-text-color-placeholder)"
|
||||||
|
:hover-color="isDark ? '#fff' : 'var(--el-color-black)'"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
@@ -476,36 +482,29 @@ watch(
|
|||||||
|
|
||||||
&__tool {
|
&__tool {
|
||||||
position: relative;
|
position: relative;
|
||||||
&:after {
|
|
||||||
|
&::before {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 1px;
|
top: 1px;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: calc(~'100% - 1px');
|
height: calc(~'100% - 1px');
|
||||||
border-left: 1px solid var(--layout-border-color);
|
border-left: 1px solid var(--el-border-color);
|
||||||
content: '';
|
content: '';
|
||||||
}
|
}
|
||||||
|
|
||||||
&--first {
|
&--first {
|
||||||
&:after {
|
&::before {
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
&:before {
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 1px;
|
top: 1px;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: calc(~'100% - 1px');
|
height: calc(~'100% - 1px');
|
||||||
border-right: 1px solid var(--layout-border-color);
|
border-right: 1px solid var(--el-border-color);
|
||||||
|
border-left: none;
|
||||||
content: '';
|
content: '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
|
||||||
:deep(span) {
|
|
||||||
color: var(--el-color-black) !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__item {
|
&__item {
|
||||||
@@ -544,7 +543,7 @@ watch(
|
|||||||
background-color: var(--el-color-primary);
|
background-color: var(--el-color-primary);
|
||||||
border: 1px solid var(--el-color-primary);
|
border: 1px solid var(--el-color-primary);
|
||||||
.@{prefix-cls}__item--close {
|
.@{prefix-cls}__item--close {
|
||||||
:deep(span) {
|
:deep(svg) {
|
||||||
color: var(--el-color-white) !important;
|
color: var(--el-color-white) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -554,14 +553,8 @@ watch(
|
|||||||
.dark {
|
.dark {
|
||||||
.@{prefix-cls} {
|
.@{prefix-cls} {
|
||||||
&__tool {
|
&__tool {
|
||||||
&:hover {
|
|
||||||
:deep(span) {
|
|
||||||
color: #fff !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&--first {
|
&--first {
|
||||||
&:after {
|
&::after {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -582,7 +575,7 @@ watch(
|
|||||||
background-color: var(--el-color-primary);
|
background-color: var(--el-color-primary);
|
||||||
border: 1px solid var(--el-color-primary);
|
border: 1px solid var(--el-color-primary);
|
||||||
.@{prefix-cls}__item--close {
|
.@{prefix-cls}__item--close {
|
||||||
:deep(span) {
|
:deep(svg) {
|
||||||
color: var(--el-color-white) !important;
|
color: var(--el-color-white) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ElDropdown, ElDropdownMenu, ElDropdownItem, ElMessageBox } from 'element-plus'
|
import { ElDropdown, ElDropdownMenu, ElDropdownItem, ElMessageBox } from 'element-plus'
|
||||||
import { useI18n } from '@/hooks/web/useI18n'
|
import { useI18n } from '@/hooks/web/useI18n'
|
||||||
import { useCache } from '@/hooks/web/useCache'
|
import { useStorage } from '@/hooks/web/useStorage'
|
||||||
import { resetRouter } from '@/router'
|
import { resetRouter } from '@/router'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { loginOutApi } from '@/api/login'
|
import { loginOutApi } from '@/api/login'
|
||||||
@@ -24,7 +24,7 @@ const prefixCls = getPrefixCls('user-info')
|
|||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
const { wsCache } = useCache()
|
const { clear } = useStorage()
|
||||||
|
|
||||||
const { replace } = useRouter()
|
const { replace } = useRouter()
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ const loginOut = () => {
|
|||||||
.then(async () => {
|
.then(async () => {
|
||||||
const res = await loginOutApi().catch(() => {})
|
const res = await loginOutApi().catch(() => {})
|
||||||
if (res) {
|
if (res) {
|
||||||
wsCache.clear()
|
clear()
|
||||||
tagsViewStore.delAllViews()
|
tagsViewStore.delAllViews()
|
||||||
resetRouter() // 重置静态路由表
|
resetRouter() // 重置静态路由表
|
||||||
replace('/login')
|
replace('/login')
|
||||||
@@ -94,7 +94,9 @@ const toDocument = () => {
|
|||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
.fade-bottom-enter-active,
|
.fade-bottom-enter-active,
|
||||||
.fade-bottom-leave-active {
|
.fade-bottom-leave-active {
|
||||||
transition: opacity 0.25s, transform 0.3s;
|
transition:
|
||||||
|
opacity 0.25s,
|
||||||
|
transform 0.3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fade-bottom-enter-from {
|
.fade-bottom-enter-from {
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useI18n } from '@/hooks/web/useI18n'
|
import { useI18n } from '@/hooks/web/useI18n'
|
||||||
import { ref, unref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { Dialog } from '@/components/Dialog'
|
import { Dialog } from '@/components/Dialog'
|
||||||
import { Form } from '@/components/Form'
|
import { Form } from '@/components/Form'
|
||||||
import { useForm } from '@/hooks/web/useForm'
|
import { useForm } from '@/hooks/web/useForm'
|
||||||
import { reactive, computed } from 'vue'
|
import { reactive, computed } from 'vue'
|
||||||
import { useValidator } from '@/hooks/web/useValidator'
|
import { useValidator } from '@/hooks/web/useValidator'
|
||||||
import { FormSchema } from '@/types/form'
|
import { FormSchema } from '@/components/Form'
|
||||||
import { ElButton } from 'element-plus'
|
import { ElButton } from 'element-plus'
|
||||||
import { useDesign } from '@/hooks/web/useDesign'
|
import { useDesign } from '@/hooks/web/useDesign'
|
||||||
import { useLockStore } from '@/store/modules/lock'
|
import { useLockStore } from '@/store/modules/lock'
|
||||||
@@ -54,14 +54,13 @@ const schema: FormSchema[] = reactive([
|
|||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
const { register, formRef, methods } = useForm({
|
const { formRegister, formMethods } = useForm()
|
||||||
schema
|
|
||||||
})
|
|
||||||
|
|
||||||
const { getFormData } = methods
|
const { getFormData, getElFormExpose } = formMethods
|
||||||
|
|
||||||
const handleLock = () => {
|
const handleLock = async () => {
|
||||||
unref(formRef)?.validate(async (valid) => {
|
const formExpose = await getElFormExpose()
|
||||||
|
formExpose?.validate(async (valid) => {
|
||||||
if (valid) {
|
if (valid) {
|
||||||
dialogVisible.value = false
|
dialogVisible.value = false
|
||||||
const formData = await getFormData()
|
const formData = await getFormData()
|
||||||
@@ -86,7 +85,7 @@ const handleLock = () => {
|
|||||||
<img src="@/assets/imgs/avatar.jpg" alt="" class="w-70px h-70px rounded-[50%]" />
|
<img src="@/assets/imgs/avatar.jpg" alt="" class="w-70px h-70px rounded-[50%]" />
|
||||||
<span class="text-14px my-10px text-[var(--top-header-text-color)]">Archer</span>
|
<span class="text-14px my-10px text-[var(--top-header-text-color)]">Archer</span>
|
||||||
</div>
|
</div>
|
||||||
<Form :is-col="false" :rules="rules" @register="register" />
|
<Form :is-col="false" :schema="schema" :rules="rules" @register="formRegister" />
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<ElButton type="primary" @click="handleLock">{{ t('lock.lock') }}</ElButton>
|
<ElButton type="primary" @click="handleLock">{{ t('lock.lock') }}</ElButton>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ import { ref } from 'vue'
|
|||||||
import { ElInput, ElButton } from 'element-plus'
|
import { ElInput, ElButton } from 'element-plus'
|
||||||
import { resetRouter } from '@/router'
|
import { resetRouter } from '@/router'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { useCache } from '@/hooks/web/useCache'
|
import { useStorage } from '@/hooks/web/useStorage'
|
||||||
import { useLockStore } from '@/store/modules/lock'
|
import { useLockStore } from '@/store/modules/lock'
|
||||||
import { useI18n } from '@/hooks/web/useI18n'
|
import { useI18n } from '@/hooks/web/useI18n'
|
||||||
import { useNow } from './useNow'
|
import { useNow } from '@/hooks/web/useNow'
|
||||||
import { useDesign } from '@/hooks/web/useDesign'
|
import { useDesign } from '@/hooks/web/useDesign'
|
||||||
import { Icon } from '@/components/Icon'
|
import { Icon } from '@/components/Icon'
|
||||||
import { loginOutApi } from '@/api/login'
|
import { loginOutApi } from '@/api/login'
|
||||||
@@ -14,7 +14,7 @@ import { useTagsViewStore } from '@/store/modules/tagsView'
|
|||||||
|
|
||||||
const tagsViewStore = useTagsViewStore()
|
const tagsViewStore = useTagsViewStore()
|
||||||
|
|
||||||
const { wsCache } = useCache()
|
const { clear } = useStorage()
|
||||||
|
|
||||||
const { replace } = useRouter()
|
const { replace } = useRouter()
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ async function unLock() {
|
|||||||
async function goLogin() {
|
async function goLogin() {
|
||||||
const res = await loginOutApi().catch(() => {})
|
const res = await loginOutApi().catch(() => {})
|
||||||
if (res) {
|
if (res) {
|
||||||
wsCache.clear()
|
clear()
|
||||||
tagsViewStore.delAllViews()
|
tagsViewStore.delAllViews()
|
||||||
resetRouter() // 重置静态路由表
|
resetRouter() // 重置静态路由表
|
||||||
lockStore.resetLockInfo()
|
lockStore.resetLockInfo()
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import type { App } from 'vue'
|
import type { App } from 'vue'
|
||||||
import { Icon } from './Icon'
|
import { Icon } from './Icon'
|
||||||
|
import { Permission } from './Permission'
|
||||||
|
|
||||||
export const setupGlobCom = (app: App<Element>): void => {
|
export const setupGlobCom = (app: App<Element>): void => {
|
||||||
app.component('Icon', Icon)
|
app.component('Icon', Icon)
|
||||||
|
app.component('Permission', Permission)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {
|
|||||||
AxiosRequestHeaders,
|
AxiosRequestHeaders,
|
||||||
AxiosError,
|
AxiosError,
|
||||||
InternalAxiosRequestConfig
|
InternalAxiosRequestConfig
|
||||||
} from './type'
|
} from './types'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import qs from 'qs'
|
import qs from 'qs'
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import config from './config'
|
|||||||
|
|
||||||
const { defaultHeaders } = config
|
const { defaultHeaders } = config
|
||||||
|
|
||||||
const request = (option: any) => {
|
const request = (option: AxiosConfig) => {
|
||||||
const { url, method, params, data, headersType, responseType } = option
|
const { url, method, params, data, headersType, responseType } = option
|
||||||
return service.request({
|
return service.request({
|
||||||
url: url,
|
url: url,
|
||||||
@@ -19,17 +19,17 @@ const request = (option: any) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
get: <T = any>(option: any) => {
|
get: <T = any>(option: AxiosConfig) => {
|
||||||
return request({ method: 'get', ...option }) as unknown as T
|
return request({ method: 'get', ...option }) as Promise<IResponse<T>>
|
||||||
},
|
},
|
||||||
post: <T = any>(option: any) => {
|
post: <T = any>(option: AxiosConfig) => {
|
||||||
return request({ method: 'post', ...option }) as unknown as T
|
return request({ method: 'post', ...option }) as Promise<IResponse<T>>
|
||||||
},
|
},
|
||||||
delete: <T = any>(option: any) => {
|
delete: <T = any>(option: AxiosConfig) => {
|
||||||
return request({ method: 'delete', ...option }) as unknown as T
|
return request({ method: 'delete', ...option }) as Promise<IResponse<T>>
|
||||||
},
|
},
|
||||||
put: <T = any>(option: any) => {
|
put: <T = any>(option: AxiosConfig) => {
|
||||||
return request({ method: 'put', ...option }) as unknown as T
|
return request({ method: 'put', ...option }) as Promise<IResponse<T>>
|
||||||
},
|
},
|
||||||
cancelRequest: (url: string | string[]) => {
|
cancelRequest: (url: string | string[]) => {
|
||||||
return service.cancelRequest(url)
|
return service.cancelRequest(url)
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import config, { defaultRequestInterceptors, defaultResponseInterceptors } from './config'
|
import config, { defaultRequestInterceptors, defaultResponseInterceptors } from './config'
|
||||||
|
|
||||||
import { AxiosInstance, InternalAxiosRequestConfig, RequestConfig, AxiosResponse } from './type'
|
import { AxiosInstance, InternalAxiosRequestConfig, RequestConfig, AxiosResponse } from './types'
|
||||||
|
|
||||||
const { interceptors, baseUrl } = config
|
const { interceptors, baseUrl } = config
|
||||||
export const PATH_URL = baseUrl[import.meta.env.VITE_API_BASEPATH]
|
export const PATH_URL = baseUrl[import.meta.env.VITE_API_BASE_PATH]
|
||||||
|
|
||||||
const { requestInterceptors, responseInterceptors } = interceptors
|
const { requestInterceptors, responseInterceptors } = interceptors
|
||||||
|
|
||||||
|
|||||||
@@ -1,28 +1,18 @@
|
|||||||
import type { App, Directive, DirectiveBinding } from 'vue'
|
import type { App, Directive, DirectiveBinding } from 'vue'
|
||||||
import { useI18n } from '@/hooks/web/useI18n'
|
import { useI18n } from '@/hooks/web/useI18n'
|
||||||
import { useCache } from '@/hooks/web/useCache'
|
import router from '@/router'
|
||||||
import { intersection } from 'lodash-es'
|
|
||||||
import { isArray } from '@/utils/is'
|
|
||||||
import { useAppStoreWithOut } from '@/store/modules/app'
|
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const { wsCache } = useCache()
|
|
||||||
const appStore = useAppStoreWithOut()
|
|
||||||
|
|
||||||
// 全部权限
|
const hasPermission = (value: string): boolean => {
|
||||||
const all_permission = ['*.*.*']
|
const permission = (router.currentRoute.value.meta.permission || []) as string[]
|
||||||
const hasPermission = (value: string | string[]): boolean => {
|
|
||||||
const permissions = wsCache.get(appStore.getUserInfo).permissions as string[]
|
|
||||||
if (!value) {
|
if (!value) {
|
||||||
throw new Error(t('permission.hasPermission'))
|
throw new Error(t('permission.hasPermission'))
|
||||||
}
|
}
|
||||||
if (!isArray(value)) {
|
if (permission.includes(value)) {
|
||||||
return permissions?.includes(value as string)
|
|
||||||
}
|
|
||||||
if (all_permission[0] === permissions[0]) {
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return (intersection(value, permissions) as string[]).length > 0
|
return false
|
||||||
}
|
}
|
||||||
function hasPermi(el: Element, binding: DirectiveBinding) {
|
function hasPermi(el: Element, binding: DirectiveBinding) {
|
||||||
const value = binding.value
|
const value = binding.value
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
/**
|
|
||||||
* 配置浏览器本地存储的方式,可直接存储对象数组。
|
|
||||||
*/
|
|
||||||
|
|
||||||
import WebStorageCache from 'web-storage-cache'
|
|
||||||
|
|
||||||
type CacheType = 'sessionStorage' | 'localStorage'
|
|
||||||
|
|
||||||
export const useCache = (type: CacheType = 'sessionStorage') => {
|
|
||||||
const wsCache: WebStorageCache = new WebStorageCache({
|
|
||||||
storage: type
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
wsCache
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ConfigGlobalTypes } from '@/types/configGlobal'
|
import { ConfigGlobalTypes } from '@/components/ConfigGlobal'
|
||||||
import { inject } from 'vue'
|
import { inject } from 'vue'
|
||||||
|
|
||||||
export const useConfigGlobal = () => {
|
export const useConfigGlobal = () => {
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
import { reactive } from 'vue'
|
import { reactive } from 'vue'
|
||||||
import { eachTree, treeMap, filter } from '@/utils/tree'
|
import { eachTree, treeMap, filter } from '@/utils/tree'
|
||||||
import { findIndex } from '@/utils'
|
import { FormSchema } from '@/components/Form'
|
||||||
import { useDictStoreWithOut } from '@/store/modules/dict'
|
import { TableColumn } from '@/components/Table'
|
||||||
import { useI18n } from '@/hooks/web/useI18n'
|
import { DescriptionsSchema } from '@/components/Descriptions'
|
||||||
import type { AxiosPromise } from 'axios'
|
|
||||||
import { FormSchema } from '@/types/form'
|
|
||||||
import { TableColumn } from '@/types/table'
|
|
||||||
import { DescriptionsSchema } from '@/types/descriptions'
|
|
||||||
|
|
||||||
export type CrudSchema = Omit<TableColumn, 'children'> & {
|
export type CrudSchema = Omit<TableColumn, 'children'> & {
|
||||||
search?: CrudSearchParams
|
search?: CrudSearchParams
|
||||||
@@ -16,39 +12,25 @@ export type CrudSchema = Omit<TableColumn, 'children'> & {
|
|||||||
children?: CrudSchema[]
|
children?: CrudSchema[]
|
||||||
}
|
}
|
||||||
|
|
||||||
type CrudSearchParams = {
|
interface CrudSearchParams extends Omit<FormSchema, 'field'> {
|
||||||
// 是否显示在查询项
|
// 是否隐藏在查询项
|
||||||
show?: boolean
|
hidden?: boolean
|
||||||
// 字典名称,会去取全局的字典
|
}
|
||||||
dictName?: string
|
|
||||||
// 接口
|
|
||||||
api?: () => Promise<any>
|
|
||||||
// 搜索字段
|
|
||||||
field?: string
|
|
||||||
} & Omit<FormSchema, 'field'>
|
|
||||||
|
|
||||||
type CrudTableParams = {
|
interface CrudTableParams extends Omit<TableColumn, 'field'> {
|
||||||
// 是否显示表头
|
// 是否隐藏表头
|
||||||
show?: boolean
|
hidden?: boolean
|
||||||
} & Omit<FormSchema, 'field'>
|
}
|
||||||
|
|
||||||
type CrudFormParams = {
|
interface CrudFormParams extends Omit<FormSchema, 'field'> {
|
||||||
// 字典名称,会去取全局的字典
|
// 是否隐藏表单项
|
||||||
dictName?: string
|
hidden?: boolean
|
||||||
// 接口
|
}
|
||||||
api?: () => Promise<any>
|
|
||||||
// 是否显示表单项
|
|
||||||
show?: boolean
|
|
||||||
} & Omit<FormSchema, 'field'>
|
|
||||||
|
|
||||||
type CrudDescriptionsParams = {
|
interface CrudDescriptionsParams extends Omit<DescriptionsSchema, 'field'> {
|
||||||
// 是否显示表单项
|
// 是否隐藏表单项
|
||||||
show?: boolean
|
hidden?: boolean
|
||||||
} & Omit<DescriptionsSchema, 'field'>
|
}
|
||||||
|
|
||||||
const dictStore = useDictStoreWithOut()
|
|
||||||
|
|
||||||
const { t } = useI18n()
|
|
||||||
|
|
||||||
interface AllSchemas {
|
interface AllSchemas {
|
||||||
searchSchema: FormSchema[]
|
searchSchema: FormSchema[]
|
||||||
@@ -71,13 +53,14 @@ export const useCrudSchemas = (
|
|||||||
detailSchema: []
|
detailSchema: []
|
||||||
})
|
})
|
||||||
|
|
||||||
const searchSchema = filterSearchSchema(crudSchema, allSchemas)
|
const searchSchema = filterSearchSchema(crudSchema)
|
||||||
|
// @ts-ignore
|
||||||
allSchemas.searchSchema = searchSchema || []
|
allSchemas.searchSchema = searchSchema || []
|
||||||
|
|
||||||
const tableColumns = filterTableSchema(crudSchema)
|
const tableColumns = filterTableSchema(crudSchema)
|
||||||
allSchemas.tableColumns = tableColumns || []
|
allSchemas.tableColumns = tableColumns || []
|
||||||
|
|
||||||
const formSchema = filterFormSchema(crudSchema, allSchemas)
|
const formSchema = filterFormSchema(crudSchema)
|
||||||
allSchemas.formSchema = formSchema
|
allSchemas.formSchema = formSchema
|
||||||
|
|
||||||
const detailSchema = filterDescriptionsSchema(crudSchema)
|
const detailSchema = filterDescriptionsSchema(crudSchema)
|
||||||
@@ -89,55 +72,26 @@ export const useCrudSchemas = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 过滤 Search 结构
|
// 过滤 Search 结构
|
||||||
const filterSearchSchema = (crudSchema: CrudSchema[], allSchemas: AllSchemas): FormSchema[] => {
|
const filterSearchSchema = (crudSchema: CrudSchema[]): FormSchema[] => {
|
||||||
const searchSchema: FormSchema[] = []
|
const searchSchema: FormSchema[] = []
|
||||||
|
const length = crudSchema.length
|
||||||
|
|
||||||
// 获取字典列表队列
|
for (let i = 0; i < length; i++) {
|
||||||
const searchRequestTask: Array<() => Promise<void>> = []
|
const schemaItem = crudSchema[i]
|
||||||
|
// 判断是否隐藏
|
||||||
eachTree(crudSchema, (schemaItem: CrudSchema) => {
|
if (!schemaItem?.search?.hidden) {
|
||||||
// 判断是否显示
|
|
||||||
if (schemaItem?.search?.show) {
|
|
||||||
const searchSchemaItem = {
|
const searchSchemaItem = {
|
||||||
// 默认为 input
|
component: schemaItem?.search?.component || 'Input',
|
||||||
component: schemaItem.search.component || 'Input',
|
|
||||||
componentProps: {},
|
|
||||||
...schemaItem.search,
|
...schemaItem.search,
|
||||||
field: schemaItem?.search?.field || schemaItem.field,
|
field: schemaItem.field,
|
||||||
label: schemaItem.search?.label || schemaItem.label
|
label: schemaItem.label
|
||||||
}
|
|
||||||
|
|
||||||
if (searchSchemaItem.dictName) {
|
|
||||||
// 如果有 dictName 则证明是从字典中获取数据
|
|
||||||
const dictArr = dictStore.getDictObj[searchSchemaItem.dictName]
|
|
||||||
searchSchemaItem.componentProps!.options = filterOptions(dictArr)
|
|
||||||
} else if (searchSchemaItem.api) {
|
|
||||||
searchRequestTask.push(async () => {
|
|
||||||
const res = await (searchSchemaItem.api as () => AxiosPromise)()
|
|
||||||
if (res) {
|
|
||||||
const index = findIndex(allSchemas.searchSchema, (v: FormSchema) => {
|
|
||||||
return v.field === searchSchemaItem.field
|
|
||||||
})
|
|
||||||
if (index !== -1) {
|
|
||||||
allSchemas.searchSchema[index]!.componentProps!.options = filterOptions(
|
|
||||||
res,
|
|
||||||
searchSchemaItem.componentProps.optionsAlias?.labelField
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除不必要的字段
|
// 删除不必要的字段
|
||||||
delete searchSchemaItem.show
|
delete searchSchemaItem.hidden
|
||||||
delete searchSchemaItem.dictName
|
|
||||||
|
|
||||||
searchSchema.push(searchSchemaItem)
|
searchSchema.push(searchSchemaItem)
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
|
||||||
for (const task of searchRequestTask) {
|
|
||||||
task()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return searchSchema
|
return searchSchema
|
||||||
@@ -147,7 +101,7 @@ const filterSearchSchema = (crudSchema: CrudSchema[], allSchemas: AllSchemas): F
|
|||||||
const filterTableSchema = (crudSchema: CrudSchema[]): TableColumn[] => {
|
const filterTableSchema = (crudSchema: CrudSchema[]): TableColumn[] => {
|
||||||
const tableColumns = treeMap<CrudSchema>(crudSchema, {
|
const tableColumns = treeMap<CrudSchema>(crudSchema, {
|
||||||
conversion: (schema: CrudSchema) => {
|
conversion: (schema: CrudSchema) => {
|
||||||
if (schema?.table?.show !== false) {
|
if (!schema?.table?.hidden) {
|
||||||
return {
|
return {
|
||||||
...schema.table,
|
...schema.table,
|
||||||
...schema
|
...schema
|
||||||
@@ -166,56 +120,28 @@ const filterTableSchema = (crudSchema: CrudSchema[]): TableColumn[] => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 过滤 form 结构
|
// 过滤 form 结构
|
||||||
const filterFormSchema = (crudSchema: CrudSchema[], allSchemas: AllSchemas): FormSchema[] => {
|
const filterFormSchema = (crudSchema: CrudSchema[]): FormSchema[] => {
|
||||||
const formSchema: FormSchema[] = []
|
const formSchema: FormSchema[] = []
|
||||||
|
const length = crudSchema.length
|
||||||
|
|
||||||
// 获取字典列表队列
|
for (let i = 0; i < length; i++) {
|
||||||
const formRequestTask: Array<() => Promise<void>> = []
|
const formItem = crudSchema[i]
|
||||||
|
// 判断是否隐藏
|
||||||
eachTree(crudSchema, (schemaItem: CrudSchema) => {
|
if (!formItem?.form?.hidden) {
|
||||||
// 判断是否显示
|
|
||||||
if (schemaItem?.form?.show !== false) {
|
|
||||||
const formSchemaItem = {
|
const formSchemaItem = {
|
||||||
// 默认为 input
|
component: formItem?.form?.component || 'Input',
|
||||||
component: schemaItem?.form?.component || 'Input',
|
...formItem.form,
|
||||||
componentProps: {},
|
field: formItem.field,
|
||||||
...schemaItem.form,
|
label: formItem.label
|
||||||
field: schemaItem.field,
|
|
||||||
label: schemaItem.search?.label || schemaItem.label
|
|
||||||
}
|
|
||||||
|
|
||||||
if (formSchemaItem.dictName) {
|
|
||||||
// 如果有 dictName 则证明是从字典中获取数据
|
|
||||||
const dictArr = dictStore.getDictObj[formSchemaItem.dictName]
|
|
||||||
formSchemaItem.componentProps!.options = filterOptions(dictArr)
|
|
||||||
} else if (formSchemaItem.api) {
|
|
||||||
formRequestTask.push(async () => {
|
|
||||||
const res = await (formSchemaItem.api as () => AxiosPromise)()
|
|
||||||
if (res) {
|
|
||||||
const index = findIndex(allSchemas.formSchema, (v: FormSchema) => {
|
|
||||||
return v.field === formSchemaItem.field
|
|
||||||
})
|
|
||||||
if (index !== -1) {
|
|
||||||
allSchemas.formSchema[index]!.componentProps!.options = filterOptions(
|
|
||||||
res,
|
|
||||||
formSchemaItem.componentProps.optionsAlias?.labelField
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除不必要的字段
|
// 删除不必要的字段
|
||||||
delete formSchemaItem.show
|
delete formSchemaItem.hidden
|
||||||
delete formSchemaItem.dictName
|
|
||||||
|
|
||||||
formSchema.push(formSchemaItem)
|
formSchema.push(formSchemaItem)
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
|
||||||
for (const task of formRequestTask) {
|
|
||||||
task()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return formSchema
|
return formSchema
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,8 +150,8 @@ const filterDescriptionsSchema = (crudSchema: CrudSchema[]): DescriptionsSchema[
|
|||||||
const descriptionsSchema: FormSchema[] = []
|
const descriptionsSchema: FormSchema[] = []
|
||||||
|
|
||||||
eachTree(crudSchema, (schemaItem: CrudSchema) => {
|
eachTree(crudSchema, (schemaItem: CrudSchema) => {
|
||||||
// 判断是否显示
|
// 判断是否隐藏
|
||||||
if (schemaItem?.detail?.show !== false) {
|
if (!schemaItem?.detail?.hidden) {
|
||||||
const descriptionsSchemaItem = {
|
const descriptionsSchemaItem = {
|
||||||
...schemaItem.detail,
|
...schemaItem.detail,
|
||||||
field: schemaItem.field,
|
field: schemaItem.field,
|
||||||
@@ -233,7 +159,7 @@ const filterDescriptionsSchema = (crudSchema: CrudSchema[]): DescriptionsSchema[
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 删除不必要的字段
|
// 删除不必要的字段
|
||||||
delete descriptionsSchemaItem.show
|
delete descriptionsSchemaItem.hidden
|
||||||
|
|
||||||
descriptionsSchema.push(descriptionsSchemaItem)
|
descriptionsSchema.push(descriptionsSchemaItem)
|
||||||
}
|
}
|
||||||
@@ -241,15 +167,3 @@ const filterDescriptionsSchema = (crudSchema: CrudSchema[]): DescriptionsSchema[
|
|||||||
|
|
||||||
return descriptionsSchema
|
return descriptionsSchema
|
||||||
}
|
}
|
||||||
|
|
||||||
// 给options添加国际化
|
|
||||||
const filterOptions = (options: Recordable, labelField?: string) => {
|
|
||||||
return options?.map((v: Recordable) => {
|
|
||||||
if (labelField) {
|
|
||||||
v['labelField'] = t(v.labelField)
|
|
||||||
} else {
|
|
||||||
v['label'] = t(v.label)
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import type { Form, FormExpose } from '@/components/Form'
|
import type { Form, FormExpose } from '@/components/Form'
|
||||||
import type { ElForm } from 'element-plus'
|
import type { ElForm, ElFormItem } from 'element-plus'
|
||||||
import { ref, unref, nextTick } from 'vue'
|
import { ref, unref, nextTick } from 'vue'
|
||||||
import type { FormProps } from '@/components/Form/src/types'
|
import { FormSchema, FormSetProps, FormProps } from '@/components/Form'
|
||||||
import { FormSchema, FormSetPropsType } from '@/types/form'
|
|
||||||
|
|
||||||
export const useForm = (props?: FormProps) => {
|
export const useForm = () => {
|
||||||
// From实例
|
// From实例
|
||||||
const formRef = ref<typeof Form & FormExpose>()
|
const formRef = ref<typeof Form & FormExpose>()
|
||||||
|
|
||||||
@@ -31,6 +30,10 @@ export const useForm = (props?: FormProps) => {
|
|||||||
|
|
||||||
// 一些内置的方法
|
// 一些内置的方法
|
||||||
const methods = {
|
const methods = {
|
||||||
|
/**
|
||||||
|
* @description 设置form组件的props
|
||||||
|
* @param props form组件的props
|
||||||
|
*/
|
||||||
setProps: async (props: FormProps = {}) => {
|
setProps: async (props: FormProps = {}) => {
|
||||||
const form = await getForm()
|
const form = await getForm()
|
||||||
form?.setProps(props)
|
form?.setProps(props)
|
||||||
@@ -39,20 +42,26 @@ export const useForm = (props?: FormProps) => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 设置form的值
|
||||||
|
* @param data 需要设置的数据
|
||||||
|
*/
|
||||||
setValues: async (data: Recordable) => {
|
setValues: async (data: Recordable) => {
|
||||||
const form = await getForm()
|
const form = await getForm()
|
||||||
form?.setValues(data)
|
form?.setValues(data)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @description 设置schema
|
||||||
* @param schemaProps 需要设置的schemaProps
|
* @param schemaProps 需要设置的schemaProps
|
||||||
*/
|
*/
|
||||||
setSchema: async (schemaProps: FormSetPropsType[]) => {
|
setSchema: async (schemaProps: FormSetProps[]) => {
|
||||||
const form = await getForm()
|
const form = await getForm()
|
||||||
form?.setSchema(schemaProps)
|
form?.setSchema(schemaProps)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @description 新增schema
|
||||||
* @param formSchema 需要新增数据
|
* @param formSchema 需要新增数据
|
||||||
* @param index 在哪里新增
|
* @param index 在哪里新增
|
||||||
*/
|
*/
|
||||||
@@ -62,6 +71,7 @@ export const useForm = (props?: FormProps) => {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @description 删除schema
|
||||||
* @param field 删除哪个数据
|
* @param field 删除哪个数据
|
||||||
*/
|
*/
|
||||||
delSchema: async (field: string) => {
|
delSchema: async (field: string) => {
|
||||||
@@ -70,19 +80,51 @@ export const useForm = (props?: FormProps) => {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @description 获取表单数据
|
||||||
* @returns form data
|
* @returns form data
|
||||||
*/
|
*/
|
||||||
getFormData: async <T = Recordable>(): Promise<T> => {
|
getFormData: async <T = Recordable>(): Promise<T> => {
|
||||||
const form = await getForm()
|
const form = await getForm()
|
||||||
return form?.formModel as T
|
return form?.formModel as T
|
||||||
}
|
},
|
||||||
}
|
|
||||||
|
|
||||||
props && methods.setProps(props)
|
/**
|
||||||
|
* @description 获取表单组件的实例
|
||||||
|
* @param field 表单项唯一标识
|
||||||
|
* @returns component instance
|
||||||
|
*/
|
||||||
|
getComponentExpose: async (field: string) => {
|
||||||
|
const form = await getForm()
|
||||||
|
return form?.getComponentExpose(field)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取formItem组件的实例
|
||||||
|
* @param field 表单项唯一标识
|
||||||
|
* @returns formItem instance
|
||||||
|
*/
|
||||||
|
getFormItemExpose: async (field: string) => {
|
||||||
|
const form = await getForm()
|
||||||
|
return form?.getFormItemExpose(field) as ComponentRef<typeof ElFormItem>
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取ElForm组件的实例
|
||||||
|
* @returns ElForm instance
|
||||||
|
*/
|
||||||
|
getElFormExpose: async () => {
|
||||||
|
await getForm()
|
||||||
|
return unref(elFormRef)
|
||||||
|
},
|
||||||
|
|
||||||
|
getFormExpose: async () => {
|
||||||
|
await getForm()
|
||||||
|
return unref(formRef)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
register,
|
formRegister: register,
|
||||||
formRef: elFormRef,
|
formMethods: methods
|
||||||
methods
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { h } from 'vue'
|
import { h } from 'vue'
|
||||||
import type { VNode } from 'vue'
|
import type { VNode } from 'vue'
|
||||||
import { Icon } from '@/components/Icon'
|
import { Icon, IconTypes } from '@/components/Icon'
|
||||||
import { IconTypes } from '@/types/icon'
|
|
||||||
|
|
||||||
export const useIcon = (props: IconTypes): VNode => {
|
export const useIcon = (props: IconTypes): VNode => {
|
||||||
return h(Icon, props)
|
return h(Icon, props)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { dateUtil } from '@/utils/dateUtil'
|
|||||||
import { reactive, toRefs } from 'vue'
|
import { reactive, toRefs } from 'vue'
|
||||||
import { tryOnMounted, tryOnUnmounted } from '@vueuse/core'
|
import { tryOnMounted, tryOnUnmounted } from '@vueuse/core'
|
||||||
|
|
||||||
export function useNow(immediate = true) {
|
export const useNow = (immediate = true) => {
|
||||||
let timer: IntervalHandle
|
let timer: IntervalHandle
|
||||||
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
91
src/hooks/web/useSearch.ts
Normal file
91
src/hooks/web/useSearch.ts
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
import { ref, unref, nextTick } from 'vue'
|
||||||
|
import { FormSchema, FormSetProps } from '@/components/Form'
|
||||||
|
import { SearchExpose, SearchProps } from '@/components/Search'
|
||||||
|
|
||||||
|
export const useSearch = () => {
|
||||||
|
// Search实例
|
||||||
|
const searchRef = ref<SearchExpose>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ref Search实例
|
||||||
|
* @param elRef ElForm实例
|
||||||
|
*/
|
||||||
|
const register = (ref: SearchExpose) => {
|
||||||
|
searchRef.value = ref
|
||||||
|
}
|
||||||
|
|
||||||
|
const getSearch = async () => {
|
||||||
|
await nextTick()
|
||||||
|
const search = unref(searchRef)
|
||||||
|
if (!search) {
|
||||||
|
console.error('The Search is not registered. Please use the register method to register')
|
||||||
|
}
|
||||||
|
return search
|
||||||
|
}
|
||||||
|
|
||||||
|
// 一些内置的方法
|
||||||
|
const methods = {
|
||||||
|
/**
|
||||||
|
* @description 设置search组件的props
|
||||||
|
* @param field FormItem的field
|
||||||
|
*/
|
||||||
|
setProps: async (props: SearchProps = {}) => {
|
||||||
|
const search = await getSearch()
|
||||||
|
search?.setProps(props)
|
||||||
|
if (props.model) {
|
||||||
|
search?.setValues(props.model)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 设置form的值
|
||||||
|
* @param data 需要设置的数据
|
||||||
|
*/
|
||||||
|
setValues: async (data: Recordable) => {
|
||||||
|
const search = await getSearch()
|
||||||
|
search?.setValues(data)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 设置schema
|
||||||
|
* @param schemaProps 需要设置的schemaProps
|
||||||
|
*/
|
||||||
|
setSchema: async (schemaProps: FormSetProps[]) => {
|
||||||
|
const search = await getSearch()
|
||||||
|
search?.setSchema(schemaProps)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 新增schema
|
||||||
|
* @param formSchema 需要新增数据
|
||||||
|
* @param index 在哪里新增
|
||||||
|
*/
|
||||||
|
addSchema: async (formSchema: FormSchema, index?: number) => {
|
||||||
|
const search = await getSearch()
|
||||||
|
search?.addSchema(formSchema, index)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 删除schema
|
||||||
|
* @param field 删除哪个数据
|
||||||
|
*/
|
||||||
|
delSchema: async (field: string) => {
|
||||||
|
const search = await getSearch()
|
||||||
|
search?.delSchema(field)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取表单数据
|
||||||
|
* @returns form data
|
||||||
|
*/
|
||||||
|
getFormData: async <T = Recordable>(): Promise<T> => {
|
||||||
|
const search = await getSearch()
|
||||||
|
return search?.formModel as T
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
searchRegister: register,
|
||||||
|
searchMethods: methods
|
||||||
|
}
|
||||||
|
}
|
||||||
31
src/hooks/web/useStorage.ts
Normal file
31
src/hooks/web/useStorage.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { isArray, isObject } from '@/utils/is'
|
||||||
|
|
||||||
|
export const useStorage = (type: 'sessionStorage' | 'localStorage' = 'sessionStorage') => {
|
||||||
|
const setStorage = (key: string, value: any) => {
|
||||||
|
window[type].setItem(key, isArray(value) || isObject(value) ? JSON.stringify(value) : value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getStorage = (key: string) => {
|
||||||
|
const value = window[type].getItem(key)
|
||||||
|
try {
|
||||||
|
return JSON.parse(value || '')
|
||||||
|
} catch (error) {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeStorage = (key: string) => {
|
||||||
|
window[type].removeItem(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
const clear = () => {
|
||||||
|
window[type].clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
setStorage,
|
||||||
|
getStorage,
|
||||||
|
removeStorage,
|
||||||
|
clear
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,91 +1,57 @@
|
|||||||
import { Table, TableExpose } from '@/components/Table'
|
|
||||||
import { ElTable, ElMessageBox, ElMessage } from 'element-plus'
|
|
||||||
import { ref, reactive, watch, computed, unref, nextTick } from 'vue'
|
|
||||||
import { get } from 'lodash-es'
|
|
||||||
import type { TableProps } from '@/components/Table/src/types'
|
|
||||||
import { useI18n } from '@/hooks/web/useI18n'
|
import { useI18n } from '@/hooks/web/useI18n'
|
||||||
import { TableSetPropsType } from '@/types/table'
|
import { Table, TableExpose, TableProps, TableSetProps, TableColumn } from '@/components/Table'
|
||||||
|
import { ElTable, ElMessageBox, ElMessage } from 'element-plus'
|
||||||
|
import { ref, watch, unref, nextTick, onMounted } from 'vue'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
interface TableResponse<T = any> {
|
interface UseTableConfig {
|
||||||
total: number
|
/**
|
||||||
list: T[]
|
* 是否初始化的时候请求一次
|
||||||
pageNumber: number
|
*/
|
||||||
pageSize: number
|
immediate?: boolean
|
||||||
|
fetchDataApi: () => Promise<{
|
||||||
|
list: any[]
|
||||||
|
total?: number
|
||||||
|
}>
|
||||||
|
fetchDelApi?: () => Promise<boolean>
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UseTableConfig<T = any> {
|
export const useTable = (config: UseTableConfig) => {
|
||||||
getListApi: (option: any) => Promise<IResponse<TableResponse<T>>>
|
const { immediate = true } = config
|
||||||
delListApi?: (option: any) => Promise<IResponse>
|
|
||||||
// 返回数据格式配置
|
|
||||||
response: {
|
|
||||||
list: string
|
|
||||||
total?: string
|
|
||||||
}
|
|
||||||
// 默认传递的参数
|
|
||||||
defaultParams?: Recordable
|
|
||||||
props?: TableProps
|
|
||||||
}
|
|
||||||
|
|
||||||
interface TableObject<T = any> {
|
const loading = ref(false)
|
||||||
pageSize: number
|
const currentPage = ref(1)
|
||||||
currentPage: number
|
const pageSize = ref(10)
|
||||||
total: number
|
const total = ref(0)
|
||||||
tableList: T[]
|
const dataList = ref<any[]>([])
|
||||||
params: any
|
|
||||||
loading: boolean
|
|
||||||
currentRow: Nullable<T>
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useTable = <T = any>(config?: UseTableConfig<T>) => {
|
|
||||||
const tableObject = reactive<TableObject<T>>({
|
|
||||||
// 页数
|
|
||||||
pageSize: 10,
|
|
||||||
// 当前页
|
|
||||||
currentPage: 1,
|
|
||||||
// 总条数
|
|
||||||
total: 10,
|
|
||||||
// 表格数据
|
|
||||||
tableList: [],
|
|
||||||
// AxiosConfig 配置
|
|
||||||
params: {
|
|
||||||
...(config?.defaultParams || {})
|
|
||||||
},
|
|
||||||
// 加载中
|
|
||||||
loading: true,
|
|
||||||
// 当前行的数据
|
|
||||||
currentRow: null
|
|
||||||
})
|
|
||||||
|
|
||||||
const paramsObj = computed(() => {
|
|
||||||
return {
|
|
||||||
...tableObject.params,
|
|
||||||
pageSize: tableObject.pageSize,
|
|
||||||
pageIndex: tableObject.currentPage
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => tableObject.currentPage,
|
() => currentPage.value,
|
||||||
() => {
|
() => {
|
||||||
methods.getList()
|
methods.getList()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => tableObject.pageSize,
|
() => pageSize.value,
|
||||||
() => {
|
() => {
|
||||||
// 当前页不为1时,修改页数后会导致多次调用getList方法
|
// 当前页不为1时,修改页数后会导致多次调用getList方法
|
||||||
if (tableObject.currentPage === 1) {
|
if (unref(currentPage) === 1) {
|
||||||
methods.getList()
|
methods.getList()
|
||||||
} else {
|
} else {
|
||||||
tableObject.currentPage = 1
|
currentPage.value = 1
|
||||||
methods.getList()
|
methods.getList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (immediate) {
|
||||||
|
methods.getList()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// Table实例
|
// Table实例
|
||||||
const tableRef = ref<typeof Table & TableExpose>()
|
const tableRef = ref<typeof Table & TableExpose>()
|
||||||
|
|
||||||
@@ -106,91 +72,122 @@ export const useTable = <T = any>(config?: UseTableConfig<T>) => {
|
|||||||
return table
|
return table
|
||||||
}
|
}
|
||||||
|
|
||||||
const delData = async (ids: string[] | number[]) => {
|
|
||||||
const res = await (config?.delListApi && config?.delListApi(ids))
|
|
||||||
if (res) {
|
|
||||||
ElMessage.success(t('common.delSuccess'))
|
|
||||||
|
|
||||||
// 计算出临界点
|
|
||||||
const currentPage =
|
|
||||||
tableObject.total % tableObject.pageSize === ids.length || tableObject.pageSize === 1
|
|
||||||
? tableObject.currentPage > 1
|
|
||||||
? tableObject.currentPage - 1
|
|
||||||
: tableObject.currentPage
|
|
||||||
: tableObject.currentPage
|
|
||||||
|
|
||||||
tableObject.currentPage = currentPage
|
|
||||||
methods.getList()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const methods = {
|
const methods = {
|
||||||
|
/**
|
||||||
|
* 获取表单数据
|
||||||
|
*/
|
||||||
getList: async () => {
|
getList: async () => {
|
||||||
tableObject.loading = true
|
loading.value = true
|
||||||
const res = await config?.getListApi(unref(paramsObj)).finally(() => {
|
try {
|
||||||
tableObject.loading = false
|
const res = await config?.fetchDataApi()
|
||||||
})
|
console.log('fetchDataApi res', res)
|
||||||
if (res) {
|
if (res) {
|
||||||
tableObject.tableList = get(res.data || {}, config?.response.list as string)
|
dataList.value = res.list
|
||||||
tableObject.total = get(res.data || {}, config?.response?.total as string) || 0
|
total.value = res.total || 0
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log('fetchDataApi error')
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 设置table组件的props
|
||||||
|
* @param props table组件的props
|
||||||
|
*/
|
||||||
setProps: async (props: TableProps = {}) => {
|
setProps: async (props: TableProps = {}) => {
|
||||||
const table = await getTable()
|
const table = await getTable()
|
||||||
table?.setProps(props)
|
table?.setProps(props)
|
||||||
},
|
},
|
||||||
setColumn: async (columnProps: TableSetPropsType[]) => {
|
|
||||||
|
/**
|
||||||
|
* @description 设置column
|
||||||
|
* @param columnProps 需要设置的列
|
||||||
|
*/
|
||||||
|
setColumn: async (columnProps: TableSetProps[]) => {
|
||||||
const table = await getTable()
|
const table = await getTable()
|
||||||
table?.setColumn(columnProps)
|
table?.setColumn(columnProps)
|
||||||
},
|
},
|
||||||
getSelections: async () => {
|
|
||||||
|
/**
|
||||||
|
* @description 新增column
|
||||||
|
* @param tableColumn 需要新增数据
|
||||||
|
* @param index 在哪里新增
|
||||||
|
*/
|
||||||
|
addColumn: async (tableColumn: TableColumn, index?: number) => {
|
||||||
const table = await getTable()
|
const table = await getTable()
|
||||||
return (table?.selections || []) as T[]
|
table?.addColumn(tableColumn, index)
|
||||||
},
|
},
|
||||||
// 与Search组件结合
|
|
||||||
setSearchParams: (data: Recordable) => {
|
/**
|
||||||
tableObject.currentPage = 1
|
* @description 删除column
|
||||||
tableObject.params = Object.assign(tableObject.params, {
|
* @param field 删除哪个数据
|
||||||
pageSize: tableObject.pageSize,
|
*/
|
||||||
pageIndex: tableObject.currentPage,
|
delColumn: async (field: string) => {
|
||||||
...data
|
const table = await getTable()
|
||||||
})
|
table?.delColumn(field)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取ElTable组件的实例
|
||||||
|
* @returns ElTable instance
|
||||||
|
*/
|
||||||
|
getElTableExpose: async () => {
|
||||||
|
await getTable()
|
||||||
|
return unref(elTableRef)
|
||||||
|
},
|
||||||
|
|
||||||
|
refresh: () => {
|
||||||
methods.getList()
|
methods.getList()
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// sortableChange: (e: any) => {
|
||||||
|
// console.log('sortableChange', e)
|
||||||
|
// const { oldIndex, newIndex } = e
|
||||||
|
// dataList.value.splice(newIndex, 0, dataList.value.splice(oldIndex, 1)[0])
|
||||||
|
// // to do something
|
||||||
|
// }
|
||||||
// 删除数据
|
// 删除数据
|
||||||
delList: async (ids: string[] | number[], multiple: boolean, message = true) => {
|
delList: async (idsLength: number) => {
|
||||||
const tableRef = await getTable()
|
const { fetchDelApi } = config
|
||||||
if (multiple) {
|
if (!fetchDelApi) {
|
||||||
if (!tableRef?.selections.length) {
|
console.warn('fetchDelApi is undefined')
|
||||||
ElMessage.warning(t('common.delNoData'))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if (!tableObject.currentRow) {
|
|
||||||
ElMessage.warning(t('common.delNoData'))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (message) {
|
|
||||||
ElMessageBox.confirm(t('common.delMessage'), t('common.delWarning'), {
|
ElMessageBox.confirm(t('common.delMessage'), t('common.delWarning'), {
|
||||||
confirmButtonText: t('common.delOk'),
|
confirmButtonText: t('common.delOk'),
|
||||||
cancelButtonText: t('common.delCancel'),
|
cancelButtonText: t('common.delCancel'),
|
||||||
type: 'warning'
|
type: 'warning'
|
||||||
}).then(async () => {
|
}).then(async () => {
|
||||||
await delData(ids)
|
const res = await fetchDelApi()
|
||||||
})
|
if (res) {
|
||||||
} else {
|
ElMessage.success(t('common.delSuccess'))
|
||||||
await delData(ids)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
config?.props && methods.setProps(config.props)
|
// 计算出临界点
|
||||||
|
const current =
|
||||||
|
unref(total) % unref(pageSize) === idsLength || unref(pageSize) === 1
|
||||||
|
? unref(currentPage) > 1
|
||||||
|
? unref(currentPage) - 1
|
||||||
|
: unref(currentPage)
|
||||||
|
: unref(currentPage)
|
||||||
|
|
||||||
|
currentPage.value = current
|
||||||
|
methods.getList()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
register,
|
tableRegister: register,
|
||||||
elTableRef,
|
tableMethods: methods,
|
||||||
tableObject,
|
tableState: {
|
||||||
methods
|
currentPage,
|
||||||
|
pageSize,
|
||||||
|
total,
|
||||||
|
dataList,
|
||||||
|
loading
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,11 @@ export default {
|
|||||||
delOk: 'OK',
|
delOk: 'OK',
|
||||||
delCancel: 'Cancel',
|
delCancel: 'Cancel',
|
||||||
delNoData: 'Please select the data to delete',
|
delNoData: 'Please select the data to delete',
|
||||||
delSuccess: 'Deleted successfully'
|
delSuccess: 'Deleted successfully',
|
||||||
|
refresh: 'Refresh',
|
||||||
|
fullscreen: 'Fullscreen',
|
||||||
|
size: 'Size',
|
||||||
|
columnSetting: 'Column setting'
|
||||||
},
|
},
|
||||||
lock: {
|
lock: {
|
||||||
lockScreen: 'Lock screen',
|
lockScreen: 'Lock screen',
|
||||||
@@ -154,7 +158,13 @@ export default {
|
|||||||
role: 'Role management',
|
role: 'Role management',
|
||||||
document: 'Document',
|
document: 'Document',
|
||||||
inputPassword: 'InputPassword',
|
inputPassword: 'InputPassword',
|
||||||
sticky: 'Sticky'
|
sticky: 'Sticky',
|
||||||
|
treeTable: 'Tree table',
|
||||||
|
PicturePreview: 'Table Image Preview',
|
||||||
|
department: 'Department management',
|
||||||
|
menuManagement: 'Menu management',
|
||||||
|
// 权限测试页面
|
||||||
|
permission: 'Permission test page'
|
||||||
},
|
},
|
||||||
permission: {
|
permission: {
|
||||||
hasPermission: 'Please set the operation permission value'
|
hasPermission: 'Please set the operation permission value'
|
||||||
@@ -241,8 +251,11 @@ export default {
|
|||||||
transfer: 'Transfer',
|
transfer: 'Transfer',
|
||||||
render: 'Render',
|
render: 'Render',
|
||||||
radio: 'Radio',
|
radio: 'Radio',
|
||||||
|
radioGroup: 'Radio Group',
|
||||||
button: 'Button',
|
button: 'Button',
|
||||||
checkbox: 'Checkbox',
|
checkbox: 'Checkbox',
|
||||||
|
checkboxButton: 'Checkbox Button',
|
||||||
|
checkboxGroup: 'Checkbox Group',
|
||||||
slider: 'Slider',
|
slider: 'Slider',
|
||||||
datePicker: 'Date Picker',
|
datePicker: 'Date Picker',
|
||||||
shortcuts: 'Shortcuts',
|
shortcuts: 'Shortcuts',
|
||||||
@@ -277,7 +290,26 @@ export default {
|
|||||||
set: 'Set',
|
set: 'Set',
|
||||||
subitem: 'Subitem',
|
subitem: 'Subitem',
|
||||||
formValidation: 'Form validation',
|
formValidation: 'Form validation',
|
||||||
verifyReset: 'Verify reset'
|
verifyReset: 'Verify reset',
|
||||||
|
// 富文本编辑器
|
||||||
|
richText: 'Rich text',
|
||||||
|
form: 'Form',
|
||||||
|
// 远程加载
|
||||||
|
remoteLoading: 'Remote loading',
|
||||||
|
// 聚焦
|
||||||
|
focus: 'Focus',
|
||||||
|
treeSelect: 'Tree Select',
|
||||||
|
showCheckbox: 'Show Checkbox',
|
||||||
|
selectAnyLevel: 'Select Any Level',
|
||||||
|
multiple: 'Multiple',
|
||||||
|
filterable: 'Filterable',
|
||||||
|
// 自定义节点内容
|
||||||
|
customContent: 'Custom content',
|
||||||
|
// 懒加载
|
||||||
|
lazyLoad: 'Lazy load',
|
||||||
|
upload: 'Upload',
|
||||||
|
// 用户头像
|
||||||
|
userAvatar: 'User avatar'
|
||||||
},
|
},
|
||||||
guideDemo: {
|
guideDemo: {
|
||||||
guide: 'Guide',
|
guide: 'Guide',
|
||||||
@@ -361,7 +393,13 @@ export default {
|
|||||||
left: 'left',
|
left: 'left',
|
||||||
center: 'center',
|
center: 'center',
|
||||||
right: 'right',
|
right: 'right',
|
||||||
dynamicOptions: 'Dynamic options'
|
dynamicOptions: 'Dynamic options',
|
||||||
|
// 删除单选框
|
||||||
|
deleteRadio: 'Delete radio',
|
||||||
|
// 还原单选框
|
||||||
|
restoreRadio: 'Restore radio',
|
||||||
|
loading: 'Loading',
|
||||||
|
reset: 'Reset'
|
||||||
},
|
},
|
||||||
stickyDemo: {
|
stickyDemo: {
|
||||||
sticky: 'Sticky'
|
sticky: 'Sticky'
|
||||||
@@ -392,7 +430,14 @@ export default {
|
|||||||
hiddenExpandedRows: 'Hidden expanded rows',
|
hiddenExpandedRows: 'Hidden expanded rows',
|
||||||
changeTitle: 'Change title',
|
changeTitle: 'Change title',
|
||||||
header: 'Header',
|
header: 'Header',
|
||||||
selectAllNone: 'Select all / none'
|
selectAllNone: 'Select all / none',
|
||||||
|
delOrAddAction: 'Delete or add action',
|
||||||
|
showOrHiddenStripe: 'Show or hidden stripe',
|
||||||
|
showOrHiddenBorder: 'Show or hidden border',
|
||||||
|
fixedHeaderOrAuto: 'Fixed header or auto',
|
||||||
|
getSelections: 'Get selections',
|
||||||
|
preview: 'Preview',
|
||||||
|
showOrHiddenSortable: 'Show or hidden sortable'
|
||||||
},
|
},
|
||||||
richText: {
|
richText: {
|
||||||
richText: 'Rich text',
|
richText: 'Rich text',
|
||||||
@@ -446,7 +491,45 @@ export default {
|
|||||||
role: 'Role',
|
role: 'Role',
|
||||||
remark: 'Remark',
|
remark: 'Remark',
|
||||||
remarkMessage1: 'Back end control routing permission',
|
remarkMessage1: 'Back end control routing permission',
|
||||||
remarkMessage2: 'Front end control routing permission'
|
remarkMessage2: 'Front end control routing permission',
|
||||||
|
// 部门列表
|
||||||
|
departmentList: 'Department list',
|
||||||
|
// 搜索部门
|
||||||
|
searchDepartment: 'Search department',
|
||||||
|
account: 'Account',
|
||||||
|
email: 'Email',
|
||||||
|
createTime: 'Create time',
|
||||||
|
// 所属部门
|
||||||
|
department: 'Department',
|
||||||
|
departmentName: 'Department name',
|
||||||
|
status: 'Status',
|
||||||
|
enable: 'Enable',
|
||||||
|
disable: 'Disable',
|
||||||
|
superiorDepartment: 'Superior department'
|
||||||
|
},
|
||||||
|
menu: {
|
||||||
|
menuName: 'Menu name',
|
||||||
|
icon: 'Icon',
|
||||||
|
// 权限
|
||||||
|
permission: 'Permission',
|
||||||
|
component: 'Component',
|
||||||
|
path: 'Path',
|
||||||
|
status: 'Status',
|
||||||
|
hidden: 'Hidden',
|
||||||
|
alwaysShow: 'Always show',
|
||||||
|
noCache: 'No cache',
|
||||||
|
breadcrumb: 'Breadcrumb',
|
||||||
|
affix: 'Affix',
|
||||||
|
noTagsView: 'No tags view',
|
||||||
|
activeMenu: 'Active menu',
|
||||||
|
canTo: 'Can to',
|
||||||
|
name: 'Name'
|
||||||
|
},
|
||||||
|
role: {
|
||||||
|
roleName: 'Role name',
|
||||||
|
role: 'Role',
|
||||||
|
// 菜单分配
|
||||||
|
menu: 'Menu allocation'
|
||||||
},
|
},
|
||||||
inputPasswordDemo: {
|
inputPasswordDemo: {
|
||||||
title: 'InputPassword',
|
title: 'InputPassword',
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export default {
|
|||||||
closeTab: '关闭标签页',
|
closeTab: '关闭标签页',
|
||||||
closeTheLeftTab: '关闭左侧标签页',
|
closeTheLeftTab: '关闭左侧标签页',
|
||||||
closeTheRightTab: '关闭右侧标签页',
|
closeTheRightTab: '关闭右侧标签页',
|
||||||
closeOther: '关闭其他标签页',
|
closeOther: '关闭其它标签页',
|
||||||
closeAll: '关闭全部标签页',
|
closeAll: '关闭全部标签页',
|
||||||
prevLabel: '上一步',
|
prevLabel: '上一步',
|
||||||
nextLabel: '下一步',
|
nextLabel: '下一步',
|
||||||
@@ -40,7 +40,11 @@ export default {
|
|||||||
delOk: '确定',
|
delOk: '确定',
|
||||||
delCancel: '取消',
|
delCancel: '取消',
|
||||||
delNoData: '请选择需要删除的数据',
|
delNoData: '请选择需要删除的数据',
|
||||||
delSuccess: '删除成功'
|
delSuccess: '删除成功',
|
||||||
|
refresh: '刷新',
|
||||||
|
fullscreen: '全屏',
|
||||||
|
size: '尺寸',
|
||||||
|
columnSetting: '列设置'
|
||||||
},
|
},
|
||||||
lock: {
|
lock: {
|
||||||
lockScreen: '锁定屏幕',
|
lockScreen: '锁定屏幕',
|
||||||
@@ -102,7 +106,7 @@ export default {
|
|||||||
register: '注册',
|
register: '注册',
|
||||||
checkPassword: '确认密码',
|
checkPassword: '确认密码',
|
||||||
login: '登录',
|
login: '登录',
|
||||||
otherLogin: '其他登录方式',
|
otherLogin: '其它登录方式',
|
||||||
remember: '记住我',
|
remember: '记住我',
|
||||||
hasUser: '已有账号?去登录',
|
hasUser: '已有账号?去登录',
|
||||||
forgetPassword: '忘记密码',
|
forgetPassword: '忘记密码',
|
||||||
@@ -154,7 +158,12 @@ export default {
|
|||||||
role: '角色管理',
|
role: '角色管理',
|
||||||
document: '文档',
|
document: '文档',
|
||||||
inputPassword: '密码输入框',
|
inputPassword: '密码输入框',
|
||||||
sticky: '黏性'
|
sticky: '黏性',
|
||||||
|
treeTable: '树形表格',
|
||||||
|
PicturePreview: '表格图片预览',
|
||||||
|
department: '部门管理',
|
||||||
|
menuManagement: '菜单管理',
|
||||||
|
permission: '权限测试页'
|
||||||
},
|
},
|
||||||
permission: {
|
permission: {
|
||||||
hasPermission: '请设置操作权限值'
|
hasPermission: '请设置操作权限值'
|
||||||
@@ -241,8 +250,11 @@ export default {
|
|||||||
transfer: '穿梭框',
|
transfer: '穿梭框',
|
||||||
render: '渲染器',
|
render: '渲染器',
|
||||||
radio: '单选框',
|
radio: '单选框',
|
||||||
|
radioGroup: '单选框组',
|
||||||
button: '按钮',
|
button: '按钮',
|
||||||
checkbox: '多选框',
|
checkbox: '多选框',
|
||||||
|
checkboxButton: '多选框按钮',
|
||||||
|
checkboxGroup: '多选框组',
|
||||||
slider: '滑块',
|
slider: '滑块',
|
||||||
datePicker: '日期选择器',
|
datePicker: '日期选择器',
|
||||||
shortcuts: '快捷选项',
|
shortcuts: '快捷选项',
|
||||||
@@ -276,7 +288,23 @@ export default {
|
|||||||
set: '设置',
|
set: '设置',
|
||||||
subitem: '子项',
|
subitem: '子项',
|
||||||
formValidation: '表单验证',
|
formValidation: '表单验证',
|
||||||
verifyReset: '验证重置'
|
verifyReset: '验证重置',
|
||||||
|
// 富文本编辑器
|
||||||
|
richText: '富文本编辑器',
|
||||||
|
form: '表单',
|
||||||
|
// 远程加载
|
||||||
|
remoteLoading: '远程加载',
|
||||||
|
// 聚焦
|
||||||
|
focus: '聚焦',
|
||||||
|
treeSelect: '树形选择器',
|
||||||
|
showCheckbox: '显示复选框',
|
||||||
|
selectAnyLevel: '选择任意级别',
|
||||||
|
multiple: '多选',
|
||||||
|
filterable: '可筛选',
|
||||||
|
customContent: '自定义内容',
|
||||||
|
lazyLoad: '懒加载',
|
||||||
|
upload: '上传',
|
||||||
|
userAvatar: '用户头像'
|
||||||
},
|
},
|
||||||
guideDemo: {
|
guideDemo: {
|
||||||
guide: '引导页',
|
guide: '引导页',
|
||||||
@@ -358,7 +386,13 @@ export default {
|
|||||||
left: '左',
|
left: '左',
|
||||||
center: '中',
|
center: '中',
|
||||||
right: '右',
|
right: '右',
|
||||||
dynamicOptions: '动态选项'
|
dynamicOptions: '动态选项',
|
||||||
|
// 删除单选框
|
||||||
|
deleteRadio: '删除单选框',
|
||||||
|
// 还原单选框
|
||||||
|
restoreRadio: '还原单选框',
|
||||||
|
loading: '加载中',
|
||||||
|
reset: '重置'
|
||||||
},
|
},
|
||||||
stickyDemo: {
|
stickyDemo: {
|
||||||
sticky: '黏性'
|
sticky: '黏性'
|
||||||
@@ -389,7 +423,14 @@ export default {
|
|||||||
hiddenExpandedRows: '隐藏展开行',
|
hiddenExpandedRows: '隐藏展开行',
|
||||||
changeTitle: '修改标题',
|
changeTitle: '修改标题',
|
||||||
header: '头部',
|
header: '头部',
|
||||||
selectAllNone: '全选/全不选'
|
selectAllNone: '全选/全不选',
|
||||||
|
delOrAddAction: '删除/添加操作列',
|
||||||
|
showOrHiddenStripe: '显示/隐藏斑马纹',
|
||||||
|
showOrHiddenBorder: '显示/隐藏边框',
|
||||||
|
fixedHeaderOrAuto: '固定头部/自动',
|
||||||
|
getSelections: '获取多选数据',
|
||||||
|
preview: '封面',
|
||||||
|
showOrHiddenSortable: '显示/隐藏排序'
|
||||||
},
|
},
|
||||||
richText: {
|
richText: {
|
||||||
richText: '富文本',
|
richText: '富文本',
|
||||||
@@ -442,7 +483,45 @@ export default {
|
|||||||
role: '角色',
|
role: '角色',
|
||||||
remark: '备注',
|
remark: '备注',
|
||||||
remarkMessage1: '后端控制路由权限',
|
remarkMessage1: '后端控制路由权限',
|
||||||
remarkMessage2: '前端控制路由权限'
|
remarkMessage2: '前端控制路由权限',
|
||||||
|
// 部门列表
|
||||||
|
departmentList: '部门列表',
|
||||||
|
searchDepartment: '搜索部门',
|
||||||
|
account: '账号',
|
||||||
|
email: '邮箱',
|
||||||
|
createTime: '创建时间',
|
||||||
|
// 所属部门
|
||||||
|
department: '所属部门',
|
||||||
|
departmentName: '部门名称',
|
||||||
|
status: '状态',
|
||||||
|
// 启用
|
||||||
|
enable: '启用',
|
||||||
|
// 禁用
|
||||||
|
disable: '禁用',
|
||||||
|
// 上级部门
|
||||||
|
superiorDepartment: '上级部门'
|
||||||
|
},
|
||||||
|
menu: {
|
||||||
|
menuName: '菜单名称',
|
||||||
|
icon: '图标',
|
||||||
|
permission: '权限标识',
|
||||||
|
component: '组件',
|
||||||
|
path: '路径',
|
||||||
|
status: '状态',
|
||||||
|
hidden: '是否隐藏',
|
||||||
|
alwaysShow: '是否一直显示',
|
||||||
|
noCache: '是否清除缓存',
|
||||||
|
breadcrumb: '是否显示面包屑',
|
||||||
|
affix: '是否固定在标签页',
|
||||||
|
noTagsView: '是否隐藏标签页',
|
||||||
|
activeMenu: '高亮菜单',
|
||||||
|
canTo: '是否可跳转',
|
||||||
|
name: '组件名称'
|
||||||
|
},
|
||||||
|
role: {
|
||||||
|
roleName: '角色名称',
|
||||||
|
role: '角色',
|
||||||
|
menu: '菜单分配'
|
||||||
},
|
},
|
||||||
inputPasswordDemo: {
|
inputPasswordDemo: {
|
||||||
title: '密码输入框',
|
title: '密码输入框',
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import router from './router'
|
import router from './router'
|
||||||
import { useAppStoreWithOut } from '@/store/modules/app'
|
import { useAppStoreWithOut } from '@/store/modules/app'
|
||||||
import { useCache } from '@/hooks/web/useCache'
|
import { useStorage } from '@/hooks/web/useStorage'
|
||||||
import type { RouteRecordRaw } from 'vue-router'
|
import type { RouteRecordRaw } from 'vue-router'
|
||||||
import { useTitle } from '@/hooks/web/useTitle'
|
import { useTitle } from '@/hooks/web/useTitle'
|
||||||
import { useNProgress } from '@/hooks/web/useNProgress'
|
import { useNProgress } from '@/hooks/web/useNProgress'
|
||||||
@@ -15,7 +15,7 @@ const appStore = useAppStoreWithOut()
|
|||||||
|
|
||||||
const dictStore = useDictStoreWithOut()
|
const dictStore = useDictStoreWithOut()
|
||||||
|
|
||||||
const { wsCache } = useCache()
|
const { getStorage } = useStorage()
|
||||||
|
|
||||||
const { start, done } = useNProgress()
|
const { start, done } = useNProgress()
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ const whiteList = ['/login'] // 不重定向白名单
|
|||||||
router.beforeEach(async (to, from, next) => {
|
router.beforeEach(async (to, from, next) => {
|
||||||
start()
|
start()
|
||||||
loadStart()
|
loadStart()
|
||||||
// if (!wsCache.get(appStore.getUserInfo)) {
|
if (getStorage(appStore.getUserInfo)) {
|
||||||
if (to.path === '/login') {
|
if (to.path === '/login') {
|
||||||
next({ path: '/' })
|
next({ path: '/' })
|
||||||
} else {
|
} else {
|
||||||
@@ -44,8 +44,8 @@ router.beforeEach(async (to, from, next) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 开发者可根据实际情况进行修改
|
// 开发者可根据实际情况进行修改
|
||||||
const roleRouters = wsCache.get('roleRouters') || []
|
const roleRouters = getStorage('roleRouters') || []
|
||||||
const userInfo = wsCache.get(appStore.getUserInfo)
|
const userInfo = getStorage(appStore.getUserInfo)
|
||||||
|
|
||||||
// 是否使用动态路由
|
// 是否使用动态路由
|
||||||
if (appStore.getDynamicRouter) {
|
if (appStore.getDynamicRouter) {
|
||||||
@@ -65,13 +65,13 @@ router.beforeEach(async (to, from, next) => {
|
|||||||
permissionStore.setIsAddRouters(true)
|
permissionStore.setIsAddRouters(true)
|
||||||
next(nextData)
|
next(nextData)
|
||||||
}
|
}
|
||||||
// } else {
|
} else {
|
||||||
// if (whiteList.indexOf(to.path) !== -1) {
|
if (whiteList.indexOf(to.path) !== -1) {
|
||||||
// next()
|
next()
|
||||||
// } else {
|
} else {
|
||||||
// next(`/login?redirect=${to.path}`) // 否则全部重定向到登录页
|
next(`/login?redirect=${to.path}`) // 否则全部重定向到登录页
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
router.afterEach((to) => {
|
router.afterEach((to) => {
|
||||||
|
|||||||
@@ -148,198 +148,198 @@ export const asyncRouterMap: AppRouteRecordRaw[] = [
|
|||||||
meta: {
|
meta: {
|
||||||
title: t('router.defaultForm')
|
title: t('router.defaultForm')
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'use-form',
|
||||||
|
component: () => import('@/views/Components/Form/UseFormDemo.vue'),
|
||||||
|
name: 'UseForm',
|
||||||
|
meta: {
|
||||||
|
title: 'UseForm'
|
||||||
}
|
}
|
||||||
// {
|
|
||||||
// path: 'use-form',
|
|
||||||
// component: () => import('@/views/Components/Form/UseFormDemo.vue'),
|
|
||||||
// name: 'UseForm',
|
|
||||||
// meta: {
|
|
||||||
// title: 'UseForm'
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// {
|
|
||||||
// path: 'ref-form',
|
|
||||||
// component: () => import('@/views/Components/Form/RefForm.vue'),
|
|
||||||
// name: 'RefForm',
|
|
||||||
// meta: {
|
|
||||||
// title: 'RefForm'
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
// {
|
|
||||||
// path: 'table',
|
|
||||||
// component: getParentLayout(),
|
|
||||||
// redirect: '/components/table/default-table',
|
|
||||||
// name: 'TableDemo',
|
|
||||||
// meta: {
|
|
||||||
// title: t('router.table'),
|
|
||||||
// alwaysShow: true
|
|
||||||
// },
|
|
||||||
// children: [
|
|
||||||
// {
|
|
||||||
// path: 'default-table',
|
|
||||||
// component: () => import('@/views/Components/Table/DefaultTable.vue'),
|
|
||||||
// name: 'DefaultTable',
|
|
||||||
// meta: {
|
|
||||||
// title: t('router.defaultTable')
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// path: 'use-table',
|
|
||||||
// component: () => import('@/views/Components/Table/UseTableDemo.vue'),
|
|
||||||
// name: 'UseTable',
|
|
||||||
// meta: {
|
|
||||||
// title: 'UseTable'
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// path: 'ref-table',
|
|
||||||
// component: () => import('@/views/Components/Table/RefTable.vue'),
|
|
||||||
// name: 'RefTable',
|
|
||||||
// meta: {
|
|
||||||
// title: 'RefTable'
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// ]
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// path: 'editor-demo',
|
|
||||||
// component: getParentLayout(),
|
|
||||||
// redirect: '/components/editor-demo/editor',
|
|
||||||
// name: 'EditorDemo',
|
|
||||||
// meta: {
|
|
||||||
// title: t('router.editor'),
|
|
||||||
// alwaysShow: true
|
|
||||||
// },
|
|
||||||
// children: [
|
|
||||||
// {
|
|
||||||
// path: 'editor',
|
|
||||||
// component: () => import('@/views/Components/Editor/Editor.vue'),
|
|
||||||
// name: 'Editor',
|
|
||||||
// meta: {
|
|
||||||
// title: t('router.richText')
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// ]
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// path: 'search',
|
|
||||||
// component: () => import('@/views/Components/Search.vue'),
|
|
||||||
// name: 'Search',
|
|
||||||
// meta: {
|
|
||||||
// title: t('router.search')
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// path: 'descriptions',
|
|
||||||
// component: () => import('@/views/Components/Descriptions.vue'),
|
|
||||||
// name: 'Descriptions',
|
|
||||||
// meta: {
|
|
||||||
// title: t('router.descriptions')
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// path: 'image-viewer',
|
|
||||||
// component: () => import('@/views/Components/ImageViewer.vue'),
|
|
||||||
// name: 'ImageViewer',
|
|
||||||
// meta: {
|
|
||||||
// title: t('router.imageViewer')
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// path: 'dialog',
|
|
||||||
// component: () => import('@/views/Components/Dialog.vue'),
|
|
||||||
// name: 'Dialog',
|
|
||||||
// meta: {
|
|
||||||
// title: t('router.dialog')
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// path: 'icon',
|
|
||||||
// component: () => import('@/views/Components/Icon.vue'),
|
|
||||||
// name: 'Icon',
|
|
||||||
// meta: {
|
|
||||||
// title: t('router.icon')
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// path: 'echart',
|
|
||||||
// component: () => import('@/views/Components/Echart.vue'),
|
|
||||||
// name: 'Echart',
|
|
||||||
// meta: {
|
|
||||||
// title: t('router.echart')
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// path: 'count-to',
|
|
||||||
// component: () => import('@/views/Components/CountTo.vue'),
|
|
||||||
// name: 'CountTo',
|
|
||||||
// meta: {
|
|
||||||
// title: t('router.countTo')
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// path: 'qrcode',
|
|
||||||
// component: () => import('@/views/Components/Qrcode.vue'),
|
|
||||||
// name: 'Qrcode',
|
|
||||||
// meta: {
|
|
||||||
// title: t('router.qrcode')
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// path: 'highlight',
|
|
||||||
// component: () => import('@/views/Components/Highlight.vue'),
|
|
||||||
// name: 'Highlight',
|
|
||||||
// meta: {
|
|
||||||
// title: t('router.highlight')
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// path: 'infotip',
|
|
||||||
// component: () => import('@/views/Components/Infotip.vue'),
|
|
||||||
// name: 'Infotip',
|
|
||||||
// meta: {
|
|
||||||
// title: t('router.infotip')
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// path: 'input-password',
|
|
||||||
// component: () => import('@/views/Components/InputPassword.vue'),
|
|
||||||
// name: 'InputPassword',
|
|
||||||
// meta: {
|
|
||||||
// title: t('router.inputPassword')
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// path: 'sticky',
|
|
||||||
// component: () => import('@/views/Components/Sticky.vue'),
|
|
||||||
// name: 'Sticky',
|
|
||||||
// meta: {
|
|
||||||
// title: t('router.sticky')
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'table',
|
||||||
|
component: getParentLayout(),
|
||||||
|
redirect: '/components/table/default-table',
|
||||||
|
name: 'TableDemo',
|
||||||
|
meta: {
|
||||||
|
title: t('router.table'),
|
||||||
|
alwaysShow: true
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'default-table',
|
||||||
|
component: () => import('@/views/Components/Table/DefaultTable.vue'),
|
||||||
|
name: 'DefaultTable',
|
||||||
|
meta: {
|
||||||
|
title: t('router.defaultTable')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'use-table',
|
||||||
|
component: () => import('@/views/Components/Table/UseTableDemo.vue'),
|
||||||
|
name: 'UseTable',
|
||||||
|
meta: {
|
||||||
|
title: 'UseTable'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'tree-table',
|
||||||
|
component: () => import('@/views/Components/Table/TreeTable.vue'),
|
||||||
|
name: 'TreeTable',
|
||||||
|
meta: {
|
||||||
|
title: t('router.treeTable')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'table-image-preview',
|
||||||
|
component: () => import('@/views/Components/Table/TableImagePreview.vue'),
|
||||||
|
name: 'TableImagePreview',
|
||||||
|
meta: {
|
||||||
|
title: t('router.PicturePreview')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'editor-demo',
|
||||||
|
component: getParentLayout(),
|
||||||
|
redirect: '/components/editor-demo/editor',
|
||||||
|
name: 'EditorDemo',
|
||||||
|
meta: {
|
||||||
|
title: t('router.editor'),
|
||||||
|
alwaysShow: true
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'editor',
|
||||||
|
component: () => import('@/views/Components/Editor/Editor.vue'),
|
||||||
|
name: 'Editor',
|
||||||
|
meta: {
|
||||||
|
title: t('router.richText')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'search',
|
||||||
|
component: () => import('@/views/Components/Search.vue'),
|
||||||
|
name: 'Search',
|
||||||
|
meta: {
|
||||||
|
title: t('router.search')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'descriptions',
|
||||||
|
component: () => import('@/views/Components/Descriptions.vue'),
|
||||||
|
name: 'Descriptions',
|
||||||
|
meta: {
|
||||||
|
title: t('router.descriptions')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'image-viewer',
|
||||||
|
component: () => import('@/views/Components/ImageViewer.vue'),
|
||||||
|
name: 'ImageViewer',
|
||||||
|
meta: {
|
||||||
|
title: t('router.imageViewer')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'dialog',
|
||||||
|
component: () => import('@/views/Components/Dialog.vue'),
|
||||||
|
name: 'Dialog',
|
||||||
|
meta: {
|
||||||
|
title: t('router.dialog')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'icon',
|
||||||
|
component: () => import('@/views/Components/Icon.vue'),
|
||||||
|
name: 'Icon',
|
||||||
|
meta: {
|
||||||
|
title: t('router.icon')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'echart',
|
||||||
|
component: () => import('@/views/Components/Echart.vue'),
|
||||||
|
name: 'Echart',
|
||||||
|
meta: {
|
||||||
|
title: t('router.echart')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'count-to',
|
||||||
|
component: () => import('@/views/Components/CountTo.vue'),
|
||||||
|
name: 'CountTo',
|
||||||
|
meta: {
|
||||||
|
title: t('router.countTo')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'qrcode',
|
||||||
|
component: () => import('@/views/Components/Qrcode.vue'),
|
||||||
|
name: 'Qrcode',
|
||||||
|
meta: {
|
||||||
|
title: t('router.qrcode')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'highlight',
|
||||||
|
component: () => import('@/views/Components/Highlight.vue'),
|
||||||
|
name: 'Highlight',
|
||||||
|
meta: {
|
||||||
|
title: t('router.highlight')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'infotip',
|
||||||
|
component: () => import('@/views/Components/Infotip.vue'),
|
||||||
|
name: 'Infotip',
|
||||||
|
meta: {
|
||||||
|
title: t('router.infotip')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'input-password',
|
||||||
|
component: () => import('@/views/Components/InputPassword.vue'),
|
||||||
|
name: 'InputPassword',
|
||||||
|
meta: {
|
||||||
|
title: t('router.inputPassword')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/hooks',
|
||||||
|
component: Layout,
|
||||||
|
redirect: '/hooks/useWatermark',
|
||||||
|
name: 'Hooks',
|
||||||
|
meta: {
|
||||||
|
title: 'hooks',
|
||||||
|
icon: 'ic:outline-webhook',
|
||||||
|
alwaysShow: true
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'useWatermark',
|
||||||
|
component: () => import('@/views/hooks/useWatermark.vue'),
|
||||||
|
name: 'UseWatermark',
|
||||||
|
meta: {
|
||||||
|
title: 'useWatermark'
|
||||||
|
}
|
||||||
|
}
|
||||||
// {
|
// {
|
||||||
// path: '/hooks',
|
// path: 'useOpenTab',
|
||||||
// component: Layout,
|
// component: () => import('@/views/hooks/useOpenTab.vue'),
|
||||||
// redirect: '/hooks/useWatermark',
|
// name: 'UseOpenTab',
|
||||||
// name: 'Hooks',
|
|
||||||
// meta: {
|
// meta: {
|
||||||
// title: 'hooks',
|
// title: 'useOpenTab'
|
||||||
// icon: 'ic:outline-webhook',
|
// }
|
||||||
// alwaysShow: true
|
|
||||||
// },
|
|
||||||
// children: [
|
|
||||||
// {
|
|
||||||
// path: 'useWatermark',
|
|
||||||
// component: () => import('@/views/hooks/useWatermark.vue'),
|
|
||||||
// name: 'UseWatermark',
|
|
||||||
// meta: {
|
|
||||||
// title: 'useWatermark'
|
|
||||||
// }
|
// }
|
||||||
// },
|
|
||||||
// {
|
// {
|
||||||
// path: 'useCrudSchemas',
|
// path: 'useCrudSchemas',
|
||||||
// component: () => import('@/views/hooks/useCrudSchemas.vue'),
|
// component: () => import('@/views/hooks/useCrudSchemas.vue'),
|
||||||
@@ -348,135 +348,135 @@ export const asyncRouterMap: AppRouteRecordRaw[] = [
|
|||||||
// title: 'useCrudSchemas'
|
// title: 'useCrudSchemas'
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// ]
|
]
|
||||||
// },
|
},
|
||||||
// {
|
{
|
||||||
// path: '/level',
|
path: '/level',
|
||||||
// component: Layout,
|
component: Layout,
|
||||||
// redirect: '/level/menu1/menu1-1/menu1-1-1',
|
redirect: '/level/menu1/menu1-1/menu1-1-1',
|
||||||
// name: 'Level',
|
name: 'Level',
|
||||||
// meta: {
|
meta: {
|
||||||
// title: t('router.level'),
|
title: t('router.level'),
|
||||||
// icon: 'carbon:skill-level-advanced'
|
icon: 'carbon:skill-level-advanced'
|
||||||
// },
|
},
|
||||||
// children: [
|
children: [
|
||||||
// {
|
{
|
||||||
// path: 'menu1',
|
path: 'menu1',
|
||||||
// name: 'Menu1',
|
name: 'Menu1',
|
||||||
// component: getParentLayout(),
|
component: getParentLayout(),
|
||||||
// redirect: '/level/menu1/menu1-1/menu1-1-1',
|
redirect: '/level/menu1/menu1-1/menu1-1-1',
|
||||||
// meta: {
|
meta: {
|
||||||
// title: t('router.menu1')
|
title: t('router.menu1')
|
||||||
// },
|
},
|
||||||
// children: [
|
children: [
|
||||||
// {
|
{
|
||||||
// path: 'menu1-1',
|
path: 'menu1-1',
|
||||||
// name: 'Menu11',
|
name: 'Menu11',
|
||||||
// component: getParentLayout(),
|
component: getParentLayout(),
|
||||||
// redirect: '/level/menu1/menu1-1/menu1-1-1',
|
redirect: '/level/menu1/menu1-1/menu1-1-1',
|
||||||
// meta: {
|
meta: {
|
||||||
// title: t('router.menu11'),
|
title: t('router.menu11'),
|
||||||
// alwaysShow: true
|
alwaysShow: true
|
||||||
// },
|
},
|
||||||
// children: [
|
children: [
|
||||||
// {
|
{
|
||||||
// path: 'menu1-1-1',
|
path: 'menu1-1-1',
|
||||||
// name: 'Menu111',
|
name: 'Menu111',
|
||||||
// component: () => import('@/views/Level/Menu111.vue'),
|
component: () => import('@/views/Level/Menu111.vue'),
|
||||||
// meta: {
|
meta: {
|
||||||
// title: t('router.menu111')
|
title: t('router.menu111')
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// ]
|
]
|
||||||
// },
|
},
|
||||||
// {
|
{
|
||||||
// path: 'menu1-2',
|
path: 'menu1-2',
|
||||||
// name: 'Menu12',
|
name: 'Menu12',
|
||||||
// component: () => import('@/views/Level/Menu12.vue'),
|
component: () => import('@/views/Level/Menu12.vue'),
|
||||||
// meta: {
|
meta: {
|
||||||
// title: t('router.menu12')
|
title: t('router.menu12')
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// ]
|
]
|
||||||
// },
|
},
|
||||||
// {
|
{
|
||||||
// path: 'menu2',
|
path: 'menu2',
|
||||||
// name: 'Menu2',
|
name: 'Menu2',
|
||||||
// component: () => import('@/views/Level/Menu2.vue'),
|
component: () => import('@/views/Level/Menu2.vue'),
|
||||||
// meta: {
|
meta: {
|
||||||
// title: t('router.menu2')
|
title: t('router.menu2')
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// ]
|
]
|
||||||
// },
|
},
|
||||||
// {
|
{
|
||||||
// path: '/example',
|
path: '/example',
|
||||||
// component: Layout,
|
component: Layout,
|
||||||
// redirect: '/example/example-dialog',
|
redirect: '/example/example-dialog',
|
||||||
// name: 'Example',
|
name: 'Example',
|
||||||
// meta: {
|
meta: {
|
||||||
// title: t('router.example'),
|
title: t('router.example'),
|
||||||
// icon: 'ep:management',
|
icon: 'ep:management',
|
||||||
// alwaysShow: true
|
alwaysShow: true
|
||||||
// },
|
},
|
||||||
// children: [
|
children: [
|
||||||
// {
|
{
|
||||||
// path: 'example-dialog',
|
path: 'example-dialog',
|
||||||
// component: () => import('@/views/Example/Dialog/ExampleDialog.vue'),
|
component: () => import('@/views/Example/Dialog/ExampleDialog.vue'),
|
||||||
// name: 'ExampleDialog',
|
name: 'ExampleDialog',
|
||||||
// meta: {
|
meta: {
|
||||||
// title: t('router.exampleDialog')
|
title: t('router.exampleDialog')
|
||||||
// }
|
}
|
||||||
// },
|
},
|
||||||
// {
|
{
|
||||||
// path: 'example-page',
|
path: 'example-page',
|
||||||
// component: () => import('@/views/Example/Page/ExamplePage.vue'),
|
component: () => import('@/views/Example/Page/ExamplePage.vue'),
|
||||||
// name: 'ExamplePage',
|
name: 'ExamplePage',
|
||||||
// meta: {
|
meta: {
|
||||||
// title: t('router.examplePage')
|
title: t('router.examplePage')
|
||||||
// }
|
}
|
||||||
// },
|
},
|
||||||
// {
|
{
|
||||||
// path: 'example-add',
|
path: 'example-add',
|
||||||
// component: () => import('@/views/Example/Page/ExampleAdd.vue'),
|
component: () => import('@/views/Example/Page/ExampleAdd.vue'),
|
||||||
// name: 'ExampleAdd',
|
name: 'ExampleAdd',
|
||||||
// meta: {
|
meta: {
|
||||||
// title: t('router.exampleAdd'),
|
title: t('router.exampleAdd'),
|
||||||
// noTagsView: true,
|
noTagsView: true,
|
||||||
// noCache: true,
|
noCache: true,
|
||||||
// hidden: true,
|
hidden: true,
|
||||||
// canTo: true,
|
canTo: true,
|
||||||
// activeMenu: '/example/example-page'
|
activeMenu: '/example/example-page'
|
||||||
// }
|
}
|
||||||
// },
|
},
|
||||||
// {
|
{
|
||||||
// path: 'example-edit',
|
path: 'example-edit',
|
||||||
// component: () => import('@/views/Example/Page/ExampleEdit.vue'),
|
component: () => import('@/views/Example/Page/ExampleEdit.vue'),
|
||||||
// name: 'ExampleEdit',
|
name: 'ExampleEdit',
|
||||||
// meta: {
|
meta: {
|
||||||
// title: t('router.exampleEdit'),
|
title: t('router.exampleEdit'),
|
||||||
// noTagsView: true,
|
noTagsView: true,
|
||||||
// noCache: true,
|
noCache: true,
|
||||||
// hidden: true,
|
hidden: true,
|
||||||
// canTo: true,
|
canTo: true,
|
||||||
// activeMenu: '/example/example-page'
|
activeMenu: '/example/example-page'
|
||||||
// }
|
}
|
||||||
// },
|
},
|
||||||
// {
|
{
|
||||||
// path: 'example-detail',
|
path: 'example-detail',
|
||||||
// component: () => import('@/views/Example/Page/ExampleDetail.vue'),
|
component: () => import('@/views/Example/Page/ExampleDetail.vue'),
|
||||||
// name: 'ExampleDetail',
|
name: 'ExampleDetail',
|
||||||
// meta: {
|
meta: {
|
||||||
// title: t('router.exampleDetail'),
|
title: t('router.exampleDetail'),
|
||||||
// noTagsView: true,
|
noTagsView: true,
|
||||||
// noCache: true,
|
noCache: true,
|
||||||
// hidden: true,
|
hidden: true,
|
||||||
// canTo: true,
|
canTo: true,
|
||||||
// activeMenu: '/example/example-page'
|
activeMenu: '/example/example-page'
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// ]
|
]
|
||||||
// },
|
},
|
||||||
{
|
{
|
||||||
path: '/error',
|
path: '/error',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
@@ -513,36 +513,80 @@ export const asyncRouterMap: AppRouteRecordRaw[] = [
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/authorization',
|
||||||
|
component: Layout,
|
||||||
|
redirect: '/authorization/user',
|
||||||
|
name: 'Authorization',
|
||||||
|
meta: {
|
||||||
|
title: t('router.authorization'),
|
||||||
|
icon: 'eos-icons:role-binding',
|
||||||
|
alwaysShow: true
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'department',
|
||||||
|
component: () => import('@/views/Authorization/Department/Department.vue'),
|
||||||
|
name: 'Department',
|
||||||
|
meta: {
|
||||||
|
title: t('router.department')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'user',
|
||||||
|
component: () => import('@/views/Authorization/User/User.vue'),
|
||||||
|
name: 'User',
|
||||||
|
meta: {
|
||||||
|
title: t('router.user')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'menu',
|
||||||
|
component: () => import('@/views/Authorization/Menu/Menu.vue'),
|
||||||
|
name: 'Menu',
|
||||||
|
meta: {
|
||||||
|
title: t('router.menuManagement')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'role',
|
||||||
|
component: () => import('@/views/Authorization/Role/Role.vue'),
|
||||||
|
name: 'Role',
|
||||||
|
meta: {
|
||||||
|
title: t('router.role')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'test',
|
||||||
|
component: () => import('@/views/Authorization/Test/Test.vue'),
|
||||||
|
name: 'Test',
|
||||||
|
meta: {
|
||||||
|
title: t('router.permission'),
|
||||||
|
permission: ['add', 'edit', 'delete']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/dynamic',
|
||||||
|
component: Layout,
|
||||||
|
redirect: '/404',
|
||||||
|
name: 'Dynamic',
|
||||||
|
meta: {
|
||||||
|
hidden: true
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'tab/:id',
|
||||||
|
component: () => import('@/components/Dynamic/src/Dynamic.vue'),
|
||||||
|
name: 'Dynamic',
|
||||||
|
meta: {
|
||||||
|
title: '详情页'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
// {
|
|
||||||
// path: '/authorization',
|
|
||||||
// component: Layout,
|
|
||||||
// redirect: '/authorization/user',
|
|
||||||
// name: 'Authorization',
|
|
||||||
// meta: {
|
|
||||||
// title: t('router.authorization'),
|
|
||||||
// icon: 'eos-icons:role-binding',
|
|
||||||
// alwaysShow: true
|
|
||||||
// },
|
|
||||||
// children: [
|
|
||||||
// {
|
|
||||||
// path: 'user',
|
|
||||||
// component: () => import('@/views/Authorization/User.vue'),
|
|
||||||
// name: 'User',
|
|
||||||
// meta: {
|
|
||||||
// title: t('router.user')
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// path: 'role',
|
|
||||||
// component: () => import('@/views/Authorization/Role.vue'),
|
|
||||||
// name: 'Role',
|
|
||||||
// meta: {
|
|
||||||
// title: t('router.role')
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// ]
|
|
||||||
// }
|
|
||||||
]
|
]
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user