Merge remote-tracking branch 'upstream/master' into dev
This commit is contained in:
39
CHANGELOG.md
39
CHANGELOG.md
@@ -1,9 +1,46 @@
|
||||
|
||||
# 2.11.5 (2024/6/18)
|
||||
|
||||
## ✨ feat
|
||||
|
||||
- 支持后端文件导入([#142](https://github.com/youlaitech/vue3-element-admin/pull/142)) [@cshaptx4869](https://github.com/cshaptx4869)
|
||||
|
||||
|
||||
## 🐛 fix
|
||||
- vue-dev-tools 插件导致菜单路由切换卡死,暂时关闭 ([28349e](https://github.com/youlaitech/vue3-element-admin/commit/28349efe147afab36531ba148eaac3a448fe6c71)) [@haoxianrui](https://github.com/haoxianrui)
|
||||
|
||||
|
||||
|
||||
# 2.11.4 (2024/6/16)
|
||||
|
||||
## ✨ feat
|
||||
|
||||
- 操作栏增加render配置参数([#138](https://github.com/youlaitech/vue3-element-admin/pull/140)) [@cshaptx4869](https://github.com/cshaptx4869)
|
||||
- 左侧工具栏增加type配置参数([#141](https://github.com/youlaitech/vue3-element-admin/pull/141)) [@diamont1001](https://github.com/diamont1001)
|
||||
|
||||
## ♻️ refactor
|
||||
- 更换权限分配弹窗类型为 drawer 并添加父子联动开关([2d9193](https://github.com/youlaitech/vue3-element-admin/commit/2d9193c47fd224f01f82b9c0b2bbeb5e7cb33584)) [@haoxianrui](https://github.com/haoxianrui)
|
||||
|
||||
|
||||
|
||||
# 2.11.3 (2024/6/11)
|
||||
|
||||
## ✨ feat
|
||||
|
||||
- 支持默认工具栏的导入([#138](https://github.com/youlaitech/vue3-element-admin/pull/138)) [@cshaptx4869](https://github.com/cshaptx4869)
|
||||
- 添加CURD导入示例([19e7bb](https://github.com/youlaitech/vue3-element-admin/commit/eab91effd6a01d5a3d9257249c8d06aa252b3bf8)) [@cshaptx4869](https://github.com/cshaptx4869)
|
||||
|
||||
## ♻️ refactor
|
||||
- 修改导出全量数据选项文本([904fec](https://github.com/youlaitech/vue3-element-admin/commit/904fecad65217650482fcdbb10ffb7f3d27eb9ea)) [@cshaptx4869](https://github.com/cshaptx4869)
|
||||
|
||||
## 🐛 fix
|
||||
- 菜单列表未适配el-icon导致图标不显示问题修复([e72b68](https://github.com/youlaitech/vue3-element-admin/commit/e72b68337562b5a7ea24ad55bbe00023e1266b40)) [@haoxianrui](https://github.com/haoxianrui)
|
||||
|
||||
# 2.11.2 (2024/6/8)
|
||||
|
||||
## ✨ feat
|
||||
|
||||
- 支持表格远程筛选([#131](https://github.com/youlaitech/vue3-element-admin/pull/119)) [@cshaptx4869](https://github.com/cshaptx4869)
|
||||
- 支持表格远程筛选([#131](https://github.com/youlaitech/vue3-element-admin/pull/131)) [@cshaptx4869](https://github.com/cshaptx4869)
|
||||
- 支持标签输入框([#132](https://github.com/youlaitech/vue3-element-admin/pull/132)) [@cshaptx4869](https://github.com/cshaptx4869)
|
||||
- 表单项支持tips配置([#133](https://github.com/youlaitech/vue3-element-admin/pull/133)) [@cshaptx4869](https://github.com/cshaptx4869)
|
||||
- 前端导出支持全量数据([#134](https://github.com/youlaitech/vue3-element-admin/pull/134)) [@cshaptx4869](https://github.com/cshaptx4869)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<div align="center">
|
||||
<img src="https://img.shields.io/badge/Vue-3.4.27-brightgreen.svg"/>
|
||||
<img src="https://img.shields.io/badge/Vite-5.2.11-green.svg"/>
|
||||
<img src="https://img.shields.io/badge/Vue-3.4.29-brightgreen.svg"/>
|
||||
<img src="https://img.shields.io/badge/Vite-5.3.1-green.svg"/>
|
||||
<img src="https://img.shields.io/badge/Element Plus-2.7.5-blue.svg"/>
|
||||
<img src="https://img.shields.io/badge/license-MIT-green.svg"/>
|
||||
<a href="https://gitee.com/youlaiorg" target="_blank">
|
||||
|
||||
17
README.md
17
README.md
@@ -1,6 +1,6 @@
|
||||
<div align="center">
|
||||
<img src="https://img.shields.io/badge/Vue-3.4.27-brightgreen.svg"/>
|
||||
<img src="https://img.shields.io/badge/Vite-5.2.11-green.svg"/>
|
||||
<img src="https://img.shields.io/badge/Vue-3.4.29-brightgreen.svg"/>
|
||||
<img src="https://img.shields.io/badge/Vite-5.3.1-green.svg"/>
|
||||
<img src="https://img.shields.io/badge/Element Plus-2.7.5-blue.svg"/>
|
||||
<img src="https://img.shields.io/badge/license-MIT-green.svg"/>
|
||||
<a href="https://gitee.com/youlaiorg" target="_blank">
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
- **基础设施**:动态路由、按钮权限、国际化、代码规范、Git 提交规范、常用组件封装。
|
||||
|
||||
- **持续更新**:自2021年起,该项目持续开源更新,实时更新工具和依赖,积累了广泛的用户群体。
|
||||
- **持续更新**:项目持续开源更新,实时更新工具和依赖。
|
||||
|
||||
|
||||
|
||||
@@ -44,17 +44,18 @@
|
||||
|
||||
## 项目地址
|
||||
|
||||
| 项目 | Gitee | Github | GitCode |
|
||||
| ---- | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
|
||||
| 项目 | Gitee | Github |
|
||||
| ---- | ------------------------------------------------------------ | ------------------------------------------------------------ |
|
||||
| 前端 | [vue3-element-admin](https://gitee.com/youlaiorg/vue3-element-admin) | [vue3-element-admin](https://github.com/youlaitech/vue3-element-admin) | [vue3-element-admin](https://gitcode.net/youlai/vue3-element-admin) |
|
||||
| 后端 | [youlai-boot](https://gitee.com/youlaiorg/youlai-boot) | [youlai-boot](https://github.com/haoxianrui/youlai-boot.git) | [youlai-boot](https://gitcode.net/youlai/youlai-boot) |
|
||||
| 精简版 | [vue3-element-admin-thin](https://gitee.com/cshaptx4869/vue3-element-admin-thin) | [vue3-element-admin-thin](https://github.com/youlaitech/vue3-element-admin-thin) |
|
||||
| 后端 | [youlai-boot](https://gitee.com/youlaiorg/youlai-boot) | [youlai-boot](https://github.com/haoxianrui/youlai-boot.git) |
|
||||
|
||||
## 环境准备
|
||||
|
||||
| 环境 | 名称版本 | 下载地址 |
|
||||
| -------------------- | :----------------------------------------------------------- | ------------------------------------------------------------ |
|
||||
| **开发工具** | VSCode | [下载](https://code.visualstudio.com/Download) |
|
||||
| **运行环境** | Node ≥18 | [下载](http://nodejs.cn/download) |
|
||||
| **开发工具** | VSCode | [下载](https://code.visualstudio.com/Download) |
|
||||
| **运行环境** | Node ≥18 (其中 20.6.0 版本不可用) | [下载](http://nodejs.cn/download) |
|
||||
|
||||
|
||||
## 项目启动
|
||||
|
||||
@@ -36,6 +36,7 @@ export default defineMock([
|
||||
id: 1,
|
||||
parentId: 0,
|
||||
name: "有来技术",
|
||||
code: "YOULAI",
|
||||
sort: 1,
|
||||
status: 1,
|
||||
children: [
|
||||
@@ -43,6 +44,7 @@ export default defineMock([
|
||||
id: 2,
|
||||
parentId: 1,
|
||||
name: "研发部门",
|
||||
code: "RD001",
|
||||
sort: 1,
|
||||
status: 1,
|
||||
children: [],
|
||||
@@ -53,6 +55,7 @@ export default defineMock([
|
||||
id: 3,
|
||||
parentId: 1,
|
||||
name: "测试部门",
|
||||
code: "QA001",
|
||||
sort: 1,
|
||||
status: 1,
|
||||
children: [],
|
||||
@@ -126,6 +129,7 @@ const deptMap: Record<string, any> = {
|
||||
1: {
|
||||
id: 1,
|
||||
name: "有来技术",
|
||||
code: "YOULAI",
|
||||
parentId: 0,
|
||||
status: 1,
|
||||
sort: 1,
|
||||
@@ -133,6 +137,7 @@ const deptMap: Record<string, any> = {
|
||||
2: {
|
||||
id: 2,
|
||||
name: "研发部门",
|
||||
code: "RD001",
|
||||
parentId: 1,
|
||||
status: 1,
|
||||
sort: 1,
|
||||
@@ -140,6 +145,7 @@ const deptMap: Record<string, any> = {
|
||||
3: {
|
||||
id: 3,
|
||||
name: "测试部门",
|
||||
code: "QA001",
|
||||
parentId: 1,
|
||||
status: 1,
|
||||
sort: 1,
|
||||
|
||||
@@ -5,11 +5,11 @@ export default defineMock([
|
||||
url: "dict/:code/options",
|
||||
method: ["GET"],
|
||||
body: ({ params }) => {
|
||||
const typeCode = params.code;
|
||||
const code = params.code;
|
||||
|
||||
let list = null;
|
||||
|
||||
if (typeCode == "gender") {
|
||||
if (code == "gender") {
|
||||
list = [
|
||||
{
|
||||
value: "1",
|
||||
@@ -21,7 +21,7 @@ export default defineMock([
|
||||
},
|
||||
{
|
||||
value: "0",
|
||||
label: "未知",
|
||||
label: "保密",
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -35,7 +35,7 @@ export default defineMock([
|
||||
},
|
||||
|
||||
{
|
||||
url: "dict/types/page",
|
||||
url: "dict/page",
|
||||
method: ["GET"],
|
||||
body: {
|
||||
code: "00000",
|
||||
@@ -46,104 +46,37 @@ export default defineMock([
|
||||
name: "性别",
|
||||
code: "gender",
|
||||
status: 1,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "状态",
|
||||
code: "status",
|
||||
status: 1,
|
||||
dictItems: [
|
||||
{
|
||||
id: 1,
|
||||
name: "男",
|
||||
value: "1",
|
||||
sort: 1,
|
||||
status: 1,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "女",
|
||||
value: "2",
|
||||
sort: 2,
|
||||
status: 1,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "保密",
|
||||
value: "0",
|
||||
sort: 3,
|
||||
status: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
total: 2,
|
||||
total: 1,
|
||||
},
|
||||
msg: "一切ok",
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
url: "dict/page",
|
||||
method: ["GET"],
|
||||
body: {
|
||||
code: "00000",
|
||||
data: {
|
||||
list: [
|
||||
{
|
||||
id: 1,
|
||||
name: "男",
|
||||
value: "1",
|
||||
status: 1,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "女",
|
||||
value: "2",
|
||||
status: 1,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "未知",
|
||||
value: "0",
|
||||
status: 1,
|
||||
},
|
||||
],
|
||||
total: 3,
|
||||
},
|
||||
msg: "一切ok",
|
||||
},
|
||||
},
|
||||
|
||||
// 新增字典类型
|
||||
{
|
||||
url: "dict/types",
|
||||
method: ["POST"],
|
||||
body({ body }) {
|
||||
return {
|
||||
code: "00000",
|
||||
data: null,
|
||||
msg: "新增字典类型" + body.name + "成功",
|
||||
};
|
||||
},
|
||||
},
|
||||
|
||||
// 获取字典类型表单数据
|
||||
{
|
||||
url: "dict/types/:id/form",
|
||||
method: ["GET"],
|
||||
body: ({ params }) => {
|
||||
return {
|
||||
code: "00000",
|
||||
data: dictTypeMap[params.id],
|
||||
msg: "一切ok",
|
||||
};
|
||||
},
|
||||
},
|
||||
|
||||
// 修改字典类型
|
||||
{
|
||||
url: "dict/types/:id",
|
||||
method: ["PUT"],
|
||||
body({ body }) {
|
||||
return {
|
||||
code: "00000",
|
||||
data: null,
|
||||
msg: "修改字典类型" + body.name + "成功",
|
||||
};
|
||||
},
|
||||
},
|
||||
|
||||
// 删除字典类型
|
||||
{
|
||||
url: "dict/types/:id",
|
||||
method: ["DELETE"],
|
||||
body({ params }) {
|
||||
return {
|
||||
code: "00000",
|
||||
data: null,
|
||||
msg: "删除字典类型" + params.id + "成功",
|
||||
};
|
||||
},
|
||||
},
|
||||
|
||||
// 新增字典
|
||||
{
|
||||
url: "dict",
|
||||
@@ -178,7 +111,7 @@ export default defineMock([
|
||||
return {
|
||||
code: "00000",
|
||||
data: null,
|
||||
msg: "修改字典类型" + body.name + "成功",
|
||||
msg: "修改字典" + body.name + "成功",
|
||||
};
|
||||
},
|
||||
},
|
||||
@@ -197,51 +130,39 @@ export default defineMock([
|
||||
},
|
||||
]);
|
||||
|
||||
// 字典类型映射表数据
|
||||
const dictTypeMap: Record<string, any> = {
|
||||
1: {
|
||||
id: 1,
|
||||
name: "性别",
|
||||
code: "gender",
|
||||
status: 1,
|
||||
remark: null,
|
||||
},
|
||||
2: {
|
||||
id: 2,
|
||||
name: "状态",
|
||||
code: "status",
|
||||
status: 1,
|
||||
remark: null,
|
||||
},
|
||||
};
|
||||
|
||||
// 字典映射表数据
|
||||
const dictMap: Record<string, any> = {
|
||||
1: {
|
||||
id: 1,
|
||||
typeCode: "gender",
|
||||
name: "男",
|
||||
value: "1",
|
||||
status: 1,
|
||||
sort: 1,
|
||||
remark: null,
|
||||
},
|
||||
2: {
|
||||
id: 2,
|
||||
typeCode: "gender",
|
||||
name: "女",
|
||||
value: "2",
|
||||
status: 1,
|
||||
sort: 2,
|
||||
remark: null,
|
||||
},
|
||||
3: {
|
||||
id: 3,
|
||||
typeCode: "gender",
|
||||
name: "未知",
|
||||
value: "0",
|
||||
status: 1,
|
||||
sort: 1,
|
||||
remark: null,
|
||||
code: "00000",
|
||||
data: {
|
||||
id: 1,
|
||||
name: "性别",
|
||||
code: "gender",
|
||||
status: 1,
|
||||
dictItems: [
|
||||
{
|
||||
id: 1,
|
||||
name: "男",
|
||||
value: "1",
|
||||
sort: 1,
|
||||
status: 1,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "女",
|
||||
value: "2",
|
||||
sort: 2,
|
||||
status: 1,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "未知",
|
||||
value: "0",
|
||||
sort: 3,
|
||||
status: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
msg: "一切ok",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -454,7 +454,8 @@ export default defineMock([
|
||||
parentId: 0,
|
||||
name: "系统管理",
|
||||
type: "CATALOG",
|
||||
path: "/system",
|
||||
routeName: "",
|
||||
routePath: "/system",
|
||||
component: "Layout",
|
||||
sort: 1,
|
||||
visible: 1,
|
||||
@@ -467,7 +468,8 @@ export default defineMock([
|
||||
parentId: 1,
|
||||
name: "用户管理",
|
||||
type: "MENU",
|
||||
path: "user",
|
||||
routeName: "User",
|
||||
routePath: "user",
|
||||
component: "system/user/index",
|
||||
sort: 1,
|
||||
visible: 1,
|
||||
@@ -475,12 +477,28 @@ export default defineMock([
|
||||
redirect: null,
|
||||
perm: null,
|
||||
children: [
|
||||
{
|
||||
id: 105,
|
||||
parentId: 2,
|
||||
name: "用户查询",
|
||||
type: "BUTTON",
|
||||
routeName: null,
|
||||
routePath: "",
|
||||
component: null,
|
||||
sort: 0,
|
||||
visible: 1,
|
||||
icon: "",
|
||||
redirect: null,
|
||||
perm: "sys:user:query",
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
id: 31,
|
||||
parentId: 2,
|
||||
name: "用户新增",
|
||||
type: "BUTTON",
|
||||
path: "",
|
||||
routeName: null,
|
||||
routePath: "",
|
||||
component: null,
|
||||
sort: 1,
|
||||
visible: 1,
|
||||
@@ -494,7 +512,8 @@ export default defineMock([
|
||||
parentId: 2,
|
||||
name: "用户编辑",
|
||||
type: "BUTTON",
|
||||
path: "",
|
||||
routeName: null,
|
||||
routePath: "",
|
||||
component: null,
|
||||
sort: 2,
|
||||
visible: 1,
|
||||
@@ -508,7 +527,8 @@ export default defineMock([
|
||||
parentId: 2,
|
||||
name: "用户删除",
|
||||
type: "BUTTON",
|
||||
path: "",
|
||||
routeName: null,
|
||||
routePath: "",
|
||||
component: null,
|
||||
sort: 3,
|
||||
visible: 1,
|
||||
@@ -522,7 +542,8 @@ export default defineMock([
|
||||
parentId: 2,
|
||||
name: "重置密码",
|
||||
type: "BUTTON",
|
||||
path: "",
|
||||
routeName: null,
|
||||
routePath: "",
|
||||
component: null,
|
||||
sort: 4,
|
||||
visible: 1,
|
||||
@@ -531,6 +552,36 @@ export default defineMock([
|
||||
perm: "sys:user:password:reset",
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
id: 106,
|
||||
parentId: 2,
|
||||
name: "用户导入",
|
||||
type: "BUTTON",
|
||||
routeName: null,
|
||||
routePath: "",
|
||||
component: null,
|
||||
sort: 5,
|
||||
visible: 1,
|
||||
icon: "",
|
||||
redirect: null,
|
||||
perm: "sys:user:import",
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
id: 107,
|
||||
parentId: 2,
|
||||
name: "用户导出",
|
||||
type: "BUTTON",
|
||||
routeName: null,
|
||||
routePath: "",
|
||||
component: null,
|
||||
sort: 6,
|
||||
visible: 1,
|
||||
icon: "",
|
||||
redirect: null,
|
||||
perm: "sys:user:export",
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -538,7 +589,8 @@ export default defineMock([
|
||||
parentId: 1,
|
||||
name: "角色管理",
|
||||
type: "MENU",
|
||||
path: "role",
|
||||
routeName: "Role",
|
||||
routePath: "role",
|
||||
component: "system/role/index",
|
||||
sort: 2,
|
||||
visible: 1,
|
||||
@@ -551,7 +603,8 @@ export default defineMock([
|
||||
parentId: 3,
|
||||
name: "角色新增",
|
||||
type: "BUTTON",
|
||||
path: "",
|
||||
routeName: null,
|
||||
routePath: "",
|
||||
component: null,
|
||||
sort: 1,
|
||||
visible: 1,
|
||||
@@ -565,7 +618,8 @@ export default defineMock([
|
||||
parentId: 3,
|
||||
name: "角色编辑",
|
||||
type: "BUTTON",
|
||||
path: "",
|
||||
routeName: null,
|
||||
routePath: "",
|
||||
component: null,
|
||||
sort: 2,
|
||||
visible: 1,
|
||||
@@ -579,7 +633,8 @@ export default defineMock([
|
||||
parentId: 3,
|
||||
name: "角色删除",
|
||||
type: "BUTTON",
|
||||
path: "",
|
||||
routeName: null,
|
||||
routePath: "",
|
||||
component: null,
|
||||
sort: 3,
|
||||
visible: 1,
|
||||
@@ -595,7 +650,8 @@ export default defineMock([
|
||||
parentId: 1,
|
||||
name: "菜单管理",
|
||||
type: "MENU",
|
||||
path: "menu",
|
||||
routeName: "Menu",
|
||||
routePath: "menu",
|
||||
component: "system/menu/index",
|
||||
sort: 3,
|
||||
visible: 1,
|
||||
@@ -608,7 +664,8 @@ export default defineMock([
|
||||
parentId: 4,
|
||||
name: "菜单新增",
|
||||
type: "BUTTON",
|
||||
path: "",
|
||||
routeName: null,
|
||||
routePath: "",
|
||||
component: null,
|
||||
sort: 1,
|
||||
visible: 1,
|
||||
@@ -617,26 +674,13 @@ export default defineMock([
|
||||
perm: "sys:menu:add",
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
id: 74,
|
||||
parentId: 4,
|
||||
name: "菜单编辑",
|
||||
type: "BUTTON",
|
||||
path: "",
|
||||
component: null,
|
||||
sort: 3,
|
||||
visible: 1,
|
||||
icon: "",
|
||||
redirect: null,
|
||||
perm: "sys:menu:edit",
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
id: 75,
|
||||
parentId: 4,
|
||||
name: "菜单删除",
|
||||
type: "BUTTON",
|
||||
path: "",
|
||||
routeName: null,
|
||||
routePath: "",
|
||||
component: null,
|
||||
sort: 3,
|
||||
visible: 1,
|
||||
@@ -645,6 +689,21 @@ export default defineMock([
|
||||
perm: "sys:menu:delete",
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
id: 74,
|
||||
parentId: 4,
|
||||
name: "菜单编辑",
|
||||
type: "BUTTON",
|
||||
routeName: null,
|
||||
routePath: "",
|
||||
component: null,
|
||||
sort: 3,
|
||||
visible: 1,
|
||||
icon: "",
|
||||
redirect: null,
|
||||
perm: "sys:menu:edit",
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -652,7 +711,8 @@ export default defineMock([
|
||||
parentId: 1,
|
||||
name: "部门管理",
|
||||
type: "MENU",
|
||||
path: "dept",
|
||||
routeName: "Dept",
|
||||
routePath: "dept",
|
||||
component: "system/dept/index",
|
||||
sort: 4,
|
||||
visible: 1,
|
||||
@@ -665,7 +725,8 @@ export default defineMock([
|
||||
parentId: 5,
|
||||
name: "部门新增",
|
||||
type: "BUTTON",
|
||||
path: "",
|
||||
routeName: null,
|
||||
routePath: "",
|
||||
component: null,
|
||||
sort: 1,
|
||||
visible: 1,
|
||||
@@ -679,7 +740,8 @@ export default defineMock([
|
||||
parentId: 5,
|
||||
name: "部门编辑",
|
||||
type: "BUTTON",
|
||||
path: "",
|
||||
routeName: null,
|
||||
routePath: "",
|
||||
component: null,
|
||||
sort: 2,
|
||||
visible: 1,
|
||||
@@ -693,7 +755,8 @@ export default defineMock([
|
||||
parentId: 5,
|
||||
name: "部门删除",
|
||||
type: "BUTTON",
|
||||
path: "",
|
||||
routeName: null,
|
||||
routePath: "",
|
||||
component: null,
|
||||
sort: 3,
|
||||
visible: 1,
|
||||
@@ -709,7 +772,8 @@ export default defineMock([
|
||||
parentId: 1,
|
||||
name: "字典管理",
|
||||
type: "MENU",
|
||||
path: "dict",
|
||||
routeName: "Dict",
|
||||
routePath: "dict",
|
||||
component: "system/dict/index",
|
||||
sort: 5,
|
||||
visible: 1,
|
||||
@@ -722,7 +786,8 @@ export default defineMock([
|
||||
parentId: 6,
|
||||
name: "字典类型新增",
|
||||
type: "BUTTON",
|
||||
path: "",
|
||||
routeName: null,
|
||||
routePath: "",
|
||||
component: null,
|
||||
sort: 1,
|
||||
visible: 1,
|
||||
@@ -736,7 +801,8 @@ export default defineMock([
|
||||
parentId: 6,
|
||||
name: "字典类型编辑",
|
||||
type: "BUTTON",
|
||||
path: "",
|
||||
routeName: null,
|
||||
routePath: "",
|
||||
component: null,
|
||||
sort: 2,
|
||||
visible: 1,
|
||||
@@ -750,7 +816,8 @@ export default defineMock([
|
||||
parentId: 6,
|
||||
name: "字典类型删除",
|
||||
type: "BUTTON",
|
||||
path: "",
|
||||
routeName: null,
|
||||
routePath: "",
|
||||
component: null,
|
||||
sort: 3,
|
||||
visible: 1,
|
||||
@@ -764,7 +831,8 @@ export default defineMock([
|
||||
parentId: 6,
|
||||
name: "字典数据新增",
|
||||
type: "BUTTON",
|
||||
path: "",
|
||||
routeName: null,
|
||||
routePath: "",
|
||||
component: null,
|
||||
sort: 4,
|
||||
visible: 1,
|
||||
@@ -778,7 +846,8 @@ export default defineMock([
|
||||
parentId: 6,
|
||||
name: "字典数据编辑",
|
||||
type: "BUTTON",
|
||||
path: "",
|
||||
routeName: null,
|
||||
routePath: "",
|
||||
component: null,
|
||||
sort: 5,
|
||||
visible: 1,
|
||||
@@ -792,7 +861,8 @@ export default defineMock([
|
||||
parentId: 6,
|
||||
name: "字典数据删除",
|
||||
type: "BUTTON",
|
||||
path: "",
|
||||
routeName: null,
|
||||
routePath: "",
|
||||
component: null,
|
||||
sort: 6,
|
||||
visible: 1,
|
||||
@@ -810,7 +880,8 @@ export default defineMock([
|
||||
parentId: 0,
|
||||
name: "接口文档",
|
||||
type: "CATALOG",
|
||||
path: "/api",
|
||||
routeName: null,
|
||||
routePath: "/api",
|
||||
component: "Layout",
|
||||
sort: 7,
|
||||
visible: 1,
|
||||
@@ -823,7 +894,8 @@ export default defineMock([
|
||||
parentId: 40,
|
||||
name: "Apifox",
|
||||
type: "MENU",
|
||||
path: "apifox",
|
||||
routeName: null,
|
||||
routePath: "apifox",
|
||||
component: "demo/api/apifox",
|
||||
sort: 1,
|
||||
visible: 1,
|
||||
@@ -832,34 +904,6 @@ export default defineMock([
|
||||
perm: null,
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
id: 103,
|
||||
parentId: 40,
|
||||
name: "Swagger",
|
||||
type: "MENU",
|
||||
path: "swagger",
|
||||
component: "demo/api/swagger",
|
||||
sort: 2,
|
||||
visible: 0,
|
||||
icon: "api",
|
||||
redirect: "",
|
||||
perm: null,
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
id: 104,
|
||||
parentId: 40,
|
||||
name: "Knife4j",
|
||||
type: "MENU",
|
||||
path: "knife4j",
|
||||
component: "demo/api/knife4j",
|
||||
sort: 3,
|
||||
visible: 0,
|
||||
icon: "api",
|
||||
redirect: "",
|
||||
perm: null,
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -867,12 +911,13 @@ export default defineMock([
|
||||
parentId: 0,
|
||||
name: "平台文档",
|
||||
type: "CATALOG",
|
||||
path: "/doc",
|
||||
routeName: null,
|
||||
routePath: "/doc",
|
||||
component: "Layout",
|
||||
sort: 8,
|
||||
visible: 1,
|
||||
icon: "document",
|
||||
redirect: null,
|
||||
redirect: "https://juejin.cn/post/7228990409909108793",
|
||||
perm: null,
|
||||
children: [
|
||||
{
|
||||
@@ -880,7 +925,8 @@ export default defineMock([
|
||||
parentId: 26,
|
||||
name: "平台文档(内嵌)",
|
||||
type: "EXTLINK",
|
||||
path: "internal-doc",
|
||||
routeName: null,
|
||||
routePath: "internal-doc",
|
||||
component: "demo/internal-doc",
|
||||
sort: 1,
|
||||
visible: 1,
|
||||
@@ -894,7 +940,8 @@ export default defineMock([
|
||||
parentId: 26,
|
||||
name: "平台文档(外链)",
|
||||
type: "EXTLINK",
|
||||
path: "https://juejin.cn/post/7228990409909108793",
|
||||
routeName: null,
|
||||
routePath: "https://juejin.cn/post/7228990409909108793",
|
||||
component: "",
|
||||
sort: 2,
|
||||
visible: 1,
|
||||
@@ -910,12 +957,13 @@ export default defineMock([
|
||||
parentId: 0,
|
||||
name: "多级菜单",
|
||||
type: "CATALOG",
|
||||
path: "/multi-level",
|
||||
routeName: null,
|
||||
routePath: "/multi-level",
|
||||
component: "Layout",
|
||||
sort: 9,
|
||||
visible: 1,
|
||||
icon: "cascader",
|
||||
redirect: "/multi-level/multi-level1",
|
||||
redirect: "",
|
||||
perm: null,
|
||||
children: [
|
||||
{
|
||||
@@ -923,12 +971,13 @@ export default defineMock([
|
||||
parentId: 20,
|
||||
name: "菜单一级",
|
||||
type: "MENU",
|
||||
path: "multi-level1",
|
||||
routeName: null,
|
||||
routePath: "multi-level1",
|
||||
component: "demo/multi-level/level1",
|
||||
sort: 1,
|
||||
visible: 1,
|
||||
icon: "",
|
||||
redirect: "/multi-level/multi-level2",
|
||||
redirect: "",
|
||||
perm: null,
|
||||
children: [
|
||||
{
|
||||
@@ -936,12 +985,13 @@ export default defineMock([
|
||||
parentId: 21,
|
||||
name: "菜单二级",
|
||||
type: "MENU",
|
||||
path: "multi-level2",
|
||||
routeName: null,
|
||||
routePath: "multi-level2",
|
||||
component: "demo/multi-level/children/level2",
|
||||
sort: 1,
|
||||
visible: 1,
|
||||
icon: "",
|
||||
redirect: "/multi-level/multi-level2/multi-level3-1",
|
||||
redirect: null,
|
||||
perm: null,
|
||||
children: [
|
||||
{
|
||||
@@ -949,7 +999,8 @@ export default defineMock([
|
||||
parentId: 22,
|
||||
name: "菜单三级-1",
|
||||
type: "MENU",
|
||||
path: "multi-level3-1",
|
||||
routeName: null,
|
||||
routePath: "multi-level3-1",
|
||||
component: "demo/multi-level/children/children/level3-1",
|
||||
sort: 1,
|
||||
visible: 1,
|
||||
@@ -963,7 +1014,8 @@ export default defineMock([
|
||||
parentId: 22,
|
||||
name: "菜单三级-2",
|
||||
type: "MENU",
|
||||
path: "multi-level3-2",
|
||||
routeName: null,
|
||||
routePath: "multi-level3-2",
|
||||
component: "demo/multi-level/children/children/level3-2",
|
||||
sort: 2,
|
||||
visible: 1,
|
||||
@@ -983,7 +1035,8 @@ export default defineMock([
|
||||
parentId: 0,
|
||||
name: "组件封装",
|
||||
type: "CATALOG",
|
||||
path: "/component",
|
||||
routeName: null,
|
||||
routePath: "/component",
|
||||
component: "Layout",
|
||||
sort: 10,
|
||||
visible: 1,
|
||||
@@ -991,14 +1044,45 @@ export default defineMock([
|
||||
redirect: "",
|
||||
perm: null,
|
||||
children: [
|
||||
{
|
||||
id: 108,
|
||||
parentId: 36,
|
||||
name: "增删改查",
|
||||
type: "MENU",
|
||||
routeName: null,
|
||||
routePath: "curd",
|
||||
component: "demo/curd/index",
|
||||
sort: 0,
|
||||
visible: 1,
|
||||
icon: "",
|
||||
redirect: "",
|
||||
perm: null,
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
id: 109,
|
||||
parentId: 36,
|
||||
name: "列表选择器",
|
||||
type: "MENU",
|
||||
routeName: null,
|
||||
routePath: "table-select",
|
||||
component: "demo/table-select/index",
|
||||
sort: 1,
|
||||
visible: 1,
|
||||
icon: "",
|
||||
redirect: "",
|
||||
perm: null,
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
id: 37,
|
||||
parentId: 36,
|
||||
name: "富文本编辑器",
|
||||
type: "MENU",
|
||||
path: "wang-editor",
|
||||
routeName: null,
|
||||
routePath: "wang-editor",
|
||||
component: "demo/wang-editor",
|
||||
sort: 1,
|
||||
sort: 2,
|
||||
visible: 1,
|
||||
icon: "",
|
||||
redirect: "",
|
||||
@@ -1010,22 +1094,9 @@ export default defineMock([
|
||||
parentId: 36,
|
||||
name: "图片上传",
|
||||
type: "MENU",
|
||||
path: "upload",
|
||||
routeName: null,
|
||||
routePath: "upload",
|
||||
component: "demo/upload",
|
||||
sort: 2,
|
||||
visible: 1,
|
||||
icon: "",
|
||||
redirect: "",
|
||||
perm: null,
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
id: 39,
|
||||
parentId: 36,
|
||||
name: "图标选择器",
|
||||
type: "MENU",
|
||||
path: "icon-selector",
|
||||
component: "demo/icon-selector",
|
||||
sort: 3,
|
||||
visible: 1,
|
||||
icon: "",
|
||||
@@ -1038,7 +1109,8 @@ export default defineMock([
|
||||
parentId: 36,
|
||||
name: "字典组件",
|
||||
type: "MENU",
|
||||
path: "dict-demo",
|
||||
routeName: null,
|
||||
routePath: "dict-demo",
|
||||
component: "demo/dict",
|
||||
sort: 4,
|
||||
visible: 1,
|
||||
@@ -1048,30 +1120,63 @@ export default defineMock([
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
id: 93,
|
||||
id: 39,
|
||||
parentId: 36,
|
||||
name: "签名",
|
||||
name: "图标选择器",
|
||||
type: "MENU",
|
||||
path: "signature",
|
||||
component: "demo/signature",
|
||||
sort: 6,
|
||||
routeName: null,
|
||||
routePath: "icon-selector",
|
||||
component: "demo/icon-selector",
|
||||
sort: 4,
|
||||
visible: 1,
|
||||
icon: "",
|
||||
redirect: "",
|
||||
perm: null,
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 110,
|
||||
parentId: 0,
|
||||
name: "路由参数",
|
||||
type: "CATALOG",
|
||||
routeName: null,
|
||||
routePath: "/route-param",
|
||||
component: "Layout",
|
||||
sort: 11,
|
||||
visible: 1,
|
||||
icon: "el-icon-ElementPlus",
|
||||
redirect: null,
|
||||
perm: null,
|
||||
children: [
|
||||
{
|
||||
id: 94,
|
||||
parentId: 36,
|
||||
name: "表格",
|
||||
id: 111,
|
||||
parentId: 110,
|
||||
name: "参数(type=1)",
|
||||
type: "MENU",
|
||||
path: "table",
|
||||
component: "demo/table",
|
||||
sort: 7,
|
||||
routeName: null,
|
||||
routePath: "route-param-type1",
|
||||
component: "demo/route-param",
|
||||
sort: 1,
|
||||
visible: 1,
|
||||
icon: "",
|
||||
redirect: "",
|
||||
icon: "el-icon-Star",
|
||||
redirect: null,
|
||||
perm: null,
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
id: 112,
|
||||
parentId: 110,
|
||||
name: "参数(type=2)",
|
||||
type: "MENU",
|
||||
routeName: null,
|
||||
routePath: "route-param-type2",
|
||||
component: "demo/route-param",
|
||||
sort: 2,
|
||||
visible: 1,
|
||||
icon: "el-icon-StarFilled",
|
||||
redirect: null,
|
||||
perm: null,
|
||||
children: [],
|
||||
},
|
||||
@@ -1082,9 +1187,10 @@ export default defineMock([
|
||||
parentId: 0,
|
||||
name: "功能演示",
|
||||
type: "CATALOG",
|
||||
path: "/function",
|
||||
routeName: null,
|
||||
routePath: "/function",
|
||||
component: "Layout",
|
||||
sort: 11,
|
||||
sort: 12,
|
||||
visible: 1,
|
||||
icon: "menu",
|
||||
redirect: "",
|
||||
@@ -1095,11 +1201,12 @@ export default defineMock([
|
||||
parentId: 89,
|
||||
name: "Icons",
|
||||
type: "MENU",
|
||||
path: "icon-demo",
|
||||
routeName: null,
|
||||
routePath: "icon-demo",
|
||||
component: "demo/icons",
|
||||
sort: 2,
|
||||
visible: 1,
|
||||
icon: "el-icon-edit",
|
||||
icon: "el-icon-Notification",
|
||||
redirect: "",
|
||||
perm: null,
|
||||
children: [],
|
||||
@@ -1109,7 +1216,8 @@ export default defineMock([
|
||||
parentId: 89,
|
||||
name: "Websocket",
|
||||
type: "MENU",
|
||||
path: "/function/websocket",
|
||||
routeName: null,
|
||||
routePath: "/function/websocket",
|
||||
component: "demo/websocket",
|
||||
sort: 3,
|
||||
visible: 1,
|
||||
@@ -1123,7 +1231,8 @@ export default defineMock([
|
||||
parentId: 89,
|
||||
name: "敬请期待...",
|
||||
type: "CATALOG",
|
||||
path: "other",
|
||||
routeName: null,
|
||||
routePath: "other/:id",
|
||||
component: "demo/other",
|
||||
sort: 4,
|
||||
visible: 1,
|
||||
@@ -1153,6 +1262,10 @@ export default defineMock([
|
||||
value: 2,
|
||||
label: "用户管理",
|
||||
children: [
|
||||
{
|
||||
value: 105,
|
||||
label: "用户查询",
|
||||
},
|
||||
{
|
||||
value: 31,
|
||||
label: "用户新增",
|
||||
@@ -1169,6 +1282,14 @@ export default defineMock([
|
||||
value: 88,
|
||||
label: "重置密码",
|
||||
},
|
||||
{
|
||||
value: 106,
|
||||
label: "用户导入",
|
||||
},
|
||||
{
|
||||
value: 107,
|
||||
label: "用户导出",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -1197,14 +1318,14 @@ export default defineMock([
|
||||
value: 73,
|
||||
label: "菜单新增",
|
||||
},
|
||||
{
|
||||
value: 74,
|
||||
label: "菜单编辑",
|
||||
},
|
||||
{
|
||||
value: 75,
|
||||
label: "菜单删除",
|
||||
},
|
||||
{
|
||||
value: 74,
|
||||
label: "菜单编辑",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -1265,14 +1386,6 @@ export default defineMock([
|
||||
value: 41,
|
||||
label: "Apifox",
|
||||
},
|
||||
{
|
||||
value: 103,
|
||||
label: "Swagger",
|
||||
},
|
||||
{
|
||||
value: 104,
|
||||
label: "Knife4j",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -1319,6 +1432,14 @@ export default defineMock([
|
||||
value: 36,
|
||||
label: "组件封装",
|
||||
children: [
|
||||
{
|
||||
value: 108,
|
||||
label: "增删改查",
|
||||
},
|
||||
{
|
||||
value: 109,
|
||||
label: "列表选择器",
|
||||
},
|
||||
{
|
||||
value: 37,
|
||||
label: "富文本编辑器",
|
||||
@@ -1327,21 +1448,27 @@ export default defineMock([
|
||||
value: 38,
|
||||
label: "图片上传",
|
||||
},
|
||||
{
|
||||
value: 39,
|
||||
label: "图标选择器",
|
||||
},
|
||||
{
|
||||
value: 95,
|
||||
label: "字典组件",
|
||||
},
|
||||
{
|
||||
value: 93,
|
||||
label: "签名",
|
||||
value: 39,
|
||||
label: "图标选择器",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: 110,
|
||||
label: "路由参数",
|
||||
children: [
|
||||
{
|
||||
value: 111,
|
||||
label: "参数(type=1)",
|
||||
},
|
||||
{
|
||||
value: 94,
|
||||
label: "表格",
|
||||
value: 112,
|
||||
label: "参数(type=2)",
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -1428,7 +1555,8 @@ const menuMap: Record<string, any> = {
|
||||
parentId: 0,
|
||||
name: "系统管理",
|
||||
type: "CATALOG",
|
||||
path: "/system",
|
||||
routeName: "",
|
||||
routePath: "/system",
|
||||
component: "Layout",
|
||||
perm: null,
|
||||
visible: 1,
|
||||
@@ -1437,13 +1565,15 @@ const menuMap: Record<string, any> = {
|
||||
redirect: "/system/user",
|
||||
keepAlive: null,
|
||||
alwaysShow: null,
|
||||
params: null,
|
||||
},
|
||||
2: {
|
||||
id: 2,
|
||||
parentId: 1,
|
||||
name: "用户管理",
|
||||
type: "MENU",
|
||||
path: "user",
|
||||
routeName: "User",
|
||||
routePath: "user",
|
||||
component: "system/user/index",
|
||||
perm: null,
|
||||
visible: 1,
|
||||
@@ -1458,7 +1588,8 @@ const menuMap: Record<string, any> = {
|
||||
parentId: 1,
|
||||
name: "角色管理",
|
||||
type: "MENU",
|
||||
path: "role",
|
||||
routeName: "Role",
|
||||
routePath: "role",
|
||||
component: "system/role/index",
|
||||
perm: null,
|
||||
visible: 1,
|
||||
@@ -1473,7 +1604,8 @@ const menuMap: Record<string, any> = {
|
||||
parentId: 1,
|
||||
name: "菜单管理",
|
||||
type: "MENU",
|
||||
path: "menu",
|
||||
routeName: "Menu",
|
||||
routePath: "menu",
|
||||
component: "system/menu/index",
|
||||
perm: null,
|
||||
visible: 1,
|
||||
@@ -1488,7 +1620,8 @@ const menuMap: Record<string, any> = {
|
||||
parentId: 1,
|
||||
name: "部门管理",
|
||||
type: "MENU",
|
||||
path: "dept",
|
||||
routeName: "Dept",
|
||||
routePath: "dept",
|
||||
component: "system/dept/index",
|
||||
perm: null,
|
||||
visible: 1,
|
||||
@@ -1503,7 +1636,8 @@ const menuMap: Record<string, any> = {
|
||||
parentId: 1,
|
||||
name: "字典管理",
|
||||
type: "MENU",
|
||||
path: "dict",
|
||||
routeName: "Dict",
|
||||
routePath: "dict",
|
||||
component: "system/dict/index",
|
||||
perm: null,
|
||||
visible: 1,
|
||||
|
||||
16
package.json
16
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vue3-element-admin",
|
||||
"version": "2.11.3",
|
||||
"version": "2.12.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
@@ -63,7 +63,7 @@
|
||||
"sockjs-client": "1.6.1",
|
||||
"sortablejs": "^1.15.2",
|
||||
"stompjs": "^2.3.3",
|
||||
"vue": "^3.4.27",
|
||||
"vue": "^3.4.29",
|
||||
"vue-i18n": "9.9.1",
|
||||
"vue-router": "^4.3.3"
|
||||
},
|
||||
@@ -73,14 +73,14 @@
|
||||
"@iconify-json/ep": "^1.1.15",
|
||||
"@types/color": "^3.0.6",
|
||||
"@types/lodash": "^4.17.5",
|
||||
"@types/node": "^20.14.2",
|
||||
"@types/node": "^20.14.3",
|
||||
"@types/nprogress": "^0.2.3",
|
||||
"@types/path-browserify": "^1.0.2",
|
||||
"@types/sockjs-client": "^1.5.4",
|
||||
"@types/sortablejs": "^1.15.8",
|
||||
"@types/stompjs": "^2.3.9",
|
||||
"@typescript-eslint/eslint-plugin": "^7.13.0",
|
||||
"@typescript-eslint/parser": "^7.13.0",
|
||||
"@typescript-eslint/eslint-plugin": "^7.13.1",
|
||||
"@typescript-eslint/parser": "^7.13.1",
|
||||
"@vitejs/plugin-vue": "^5.0.5",
|
||||
"@vitejs/plugin-vue-jsx": "^3.1.0",
|
||||
"autoprefixer": "^10.4.19",
|
||||
@@ -98,7 +98,7 @@
|
||||
"postcss-html": "^1.7.0",
|
||||
"postcss-scss": "^4.0.9",
|
||||
"prettier": "^3.3.2",
|
||||
"sass": "^1.77.5",
|
||||
"sass": "^1.77.6",
|
||||
"stylelint": "^16.6.1",
|
||||
"stylelint-config-html": "^1.1.0",
|
||||
"stylelint-config-recess-order": "^4.6.0",
|
||||
@@ -111,10 +111,10 @@
|
||||
"unplugin-auto-import": "^0.17.6",
|
||||
"unplugin-icons": "^0.18.5",
|
||||
"unplugin-vue-components": "^0.26.0",
|
||||
"vite": "^5.2.13",
|
||||
"vite": "^5.3.1",
|
||||
"vite-plugin-mock-dev-server": "^1.5.0",
|
||||
"vite-plugin-svg-icons": "^2.0.1",
|
||||
"vite-plugin-vue-devtools": "^7.2.1",
|
||||
"vite-plugin-vue-devtools": "^7.3.0",
|
||||
"vue-tsc": "^2.0.21"
|
||||
},
|
||||
"repository": "https://gitee.com/youlaiorg/vue3-element-admin.git",
|
||||
|
||||
72
src/api/auth.ts
Normal file
72
src/api/auth.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import request from "@/utils/request";
|
||||
|
||||
const AUTH_BASE_URL = "/api/v1/auth";
|
||||
|
||||
class AuthAPI {
|
||||
/** 登录 接口*/
|
||||
static login(data: LoginData) {
|
||||
const formData = new FormData();
|
||||
formData.append("username", data.username);
|
||||
formData.append("password", data.password);
|
||||
formData.append("captchaKey", data.captchaKey);
|
||||
formData.append("captchaCode", data.captchaCode);
|
||||
return request<any, LoginResult>({
|
||||
url: `${AUTH_BASE_URL}/login`,
|
||||
method: "post",
|
||||
data: formData,
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/** 注销 接口*/
|
||||
static logout() {
|
||||
return request({
|
||||
url: `${AUTH_BASE_URL}/logout`,
|
||||
method: "delete",
|
||||
});
|
||||
}
|
||||
|
||||
/** 获取验证码 接口*/
|
||||
static getCaptcha() {
|
||||
return request<any, CaptchaResult>({
|
||||
url: `${AUTH_BASE_URL}/captcha`,
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default AuthAPI;
|
||||
|
||||
/** 登录请求参数 */
|
||||
export interface LoginData {
|
||||
/** 用户名 */
|
||||
username: string;
|
||||
/** 密码 */
|
||||
password: string;
|
||||
/** 验证码缓存key */
|
||||
captchaKey: string;
|
||||
/** 验证码 */
|
||||
captchaCode: string;
|
||||
}
|
||||
|
||||
/** 登录响应 */
|
||||
export interface LoginResult {
|
||||
/** 访问token */
|
||||
accessToken?: string;
|
||||
/** 过期时间(单位:毫秒) */
|
||||
expires?: number;
|
||||
/** 刷新token */
|
||||
refreshToken?: string;
|
||||
/** token 类型 */
|
||||
tokenType?: string;
|
||||
}
|
||||
|
||||
/** 验证码响应 */
|
||||
export interface CaptchaResult {
|
||||
/** 验证码缓存key */
|
||||
captchaKey: string;
|
||||
/** 验证码图片Base64字符串 */
|
||||
captchaBase64: string;
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
import request from "@/utils/request";
|
||||
import { CaptchaResult, LoginData, LoginResult } from "./model";
|
||||
|
||||
class AuthAPI {
|
||||
/**
|
||||
* 登录API
|
||||
*
|
||||
* @param data {LoginData}
|
||||
* @returns
|
||||
*/
|
||||
static login(data: LoginData) {
|
||||
const formData = new FormData();
|
||||
formData.append("username", data.username);
|
||||
formData.append("password", data.password);
|
||||
formData.append("captchaKey", data.captchaKey || "");
|
||||
formData.append("captchaCode", data.captchaCode || "");
|
||||
return request<any, LoginResult>({
|
||||
url: "/api/v1/auth/login",
|
||||
method: "post",
|
||||
data: formData,
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 注销API
|
||||
*/
|
||||
static logout() {
|
||||
return request({
|
||||
url: "/api/v1/auth/logout",
|
||||
method: "delete",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取验证码
|
||||
*/
|
||||
static getCaptcha() {
|
||||
return request<any, CaptchaResult>({
|
||||
url: "/api/v1/auth/captcha",
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default AuthAPI;
|
||||
@@ -1,59 +0,0 @@
|
||||
/**
|
||||
* 登录请求参数
|
||||
*/
|
||||
export interface LoginData {
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
username: string;
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
password: string;
|
||||
|
||||
/**
|
||||
* 验证码缓存key
|
||||
*/
|
||||
captchaKey?: string;
|
||||
|
||||
/**
|
||||
* 验证码
|
||||
*/
|
||||
captchaCode?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录响应
|
||||
*/
|
||||
export interface LoginResult {
|
||||
/**
|
||||
* 访问token
|
||||
*/
|
||||
accessToken?: string;
|
||||
/**
|
||||
* 过期时间(单位:毫秒)
|
||||
*/
|
||||
expires?: number;
|
||||
/**
|
||||
* 刷新token
|
||||
*/
|
||||
refreshToken?: string;
|
||||
/**
|
||||
* token 类型
|
||||
*/
|
||||
tokenType?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证码响应
|
||||
*/
|
||||
export interface CaptchaResult {
|
||||
/**
|
||||
* 验证码缓存key
|
||||
*/
|
||||
captchaKey: string;
|
||||
/**
|
||||
* 验证码图片Base64字符串
|
||||
*/
|
||||
captchaBase64: string;
|
||||
}
|
||||
130
src/api/dept.ts
Normal file
130
src/api/dept.ts
Normal file
@@ -0,0 +1,130 @@
|
||||
import request from "@/utils/request";
|
||||
|
||||
const DEPT_BASE_URL = "/api/v1/dept";
|
||||
|
||||
class DeptAPI {
|
||||
/**
|
||||
* 获取部门列表
|
||||
*
|
||||
* @param queryParams 查询参数(可选)
|
||||
* @returns 部门树形表格数据
|
||||
*/
|
||||
static getList(queryParams?: DeptQuery) {
|
||||
return request<any, DeptVO[]>({
|
||||
url: `${DEPT_BASE_URL}`,
|
||||
method: "get",
|
||||
params: queryParams,
|
||||
});
|
||||
}
|
||||
|
||||
/** 获取部门下拉列表 */
|
||||
static getOptions() {
|
||||
return request<any, OptionType[]>({
|
||||
url: `${DEPT_BASE_URL}/options`,
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取部门详情表单数据
|
||||
*
|
||||
* @param id 部门ID
|
||||
* @returns 部门详情表单数据
|
||||
*/
|
||||
static getFormData(id: number) {
|
||||
return request<any, DeptForm>({
|
||||
url: `${DEPT_BASE_URL}/${id}/form`,
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增部门
|
||||
*
|
||||
* @param data 部门表单数据
|
||||
* @returns 请求结果
|
||||
*/
|
||||
static add(data: DeptForm) {
|
||||
return request({
|
||||
url: `${DEPT_BASE_URL}`,
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改部门
|
||||
*
|
||||
* @param id 部门ID
|
||||
* @param data 部门表单数据
|
||||
* @returns 请求结果
|
||||
*/
|
||||
static update(id: number, data: DeptForm) {
|
||||
return request({
|
||||
url: `${DEPT_BASE_URL}/${id}`,
|
||||
method: "put",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除部门
|
||||
*
|
||||
* @param ids 部门ID,多个以英文逗号(,)分隔
|
||||
* @returns 请求结果
|
||||
*/
|
||||
static deleteByIds(ids: string) {
|
||||
return request({
|
||||
url: `${DEPT_BASE_URL}/${ids}`,
|
||||
method: "delete",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default DeptAPI;
|
||||
|
||||
/** 部门查询参数 */
|
||||
export interface DeptQuery {
|
||||
/** 搜索关键字 */
|
||||
keywords?: string;
|
||||
/** 状态 */
|
||||
status?: number;
|
||||
}
|
||||
|
||||
/** 部门类型 */
|
||||
export interface DeptVO {
|
||||
/** 子部门 */
|
||||
children?: DeptVO[];
|
||||
/** 创建时间 */
|
||||
createTime?: Date;
|
||||
/** 部门ID */
|
||||
id?: number;
|
||||
/** 部门名称 */
|
||||
name?: string;
|
||||
/** 部门编号 */
|
||||
code?: string;
|
||||
/** 父部门ID */
|
||||
parentId?: number;
|
||||
/** 排序 */
|
||||
sort?: number;
|
||||
/** 状态(1:启用;0:禁用) */
|
||||
status?: number;
|
||||
/** 修改时间 */
|
||||
updateTime?: Date;
|
||||
}
|
||||
|
||||
/** 部门表单类型 */
|
||||
export interface DeptForm {
|
||||
/** 部门ID(新增不填) */
|
||||
id?: number;
|
||||
/** 部门名称 */
|
||||
name?: string;
|
||||
/** 部门编号 */
|
||||
code?: string;
|
||||
/** 父部门ID */
|
||||
parentId: number;
|
||||
/** 排序 */
|
||||
sort?: number;
|
||||
/** 状态(1:启用;0:禁用) */
|
||||
status?: number;
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
import request from "@/utils/request";
|
||||
import { DeptForm, DeptQuery, DeptVO } from "./model";
|
||||
|
||||
class DeptAPI {
|
||||
/**
|
||||
* 部门树形表格
|
||||
*
|
||||
* @param queryParams
|
||||
*/
|
||||
static getList(queryParams?: DeptQuery) {
|
||||
return request<any, DeptVO[]>({
|
||||
url: "/api/v1/dept",
|
||||
method: "get",
|
||||
params: queryParams,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 部门下拉列表
|
||||
*/
|
||||
static getOptions() {
|
||||
return request<any, OptionType[]>({
|
||||
url: "/api/v1/dept/options",
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取部门详情
|
||||
*
|
||||
* @param id
|
||||
*/
|
||||
static getFormData(id: number) {
|
||||
return request<any, DeptForm>({
|
||||
url: "/api/v1/dept/" + id + "/form",
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增部门
|
||||
*
|
||||
* @param data
|
||||
*/
|
||||
static add(data: DeptForm) {
|
||||
return request({
|
||||
url: "/api/v1/dept",
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改部门
|
||||
*
|
||||
* @param id
|
||||
* @param data
|
||||
*/
|
||||
static update(id: number, data: DeptForm) {
|
||||
return request({
|
||||
url: "/api/v1/dept/" + id,
|
||||
method: "put",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除部门
|
||||
*
|
||||
* @param ids
|
||||
*/
|
||||
static deleteByIds(ids: string) {
|
||||
return request({
|
||||
url: "/api/v1/dept/" + ids,
|
||||
method: "delete",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default DeptAPI;
|
||||
@@ -1,71 +0,0 @@
|
||||
/**
|
||||
* 部门查询参数
|
||||
*/
|
||||
export interface DeptQuery {
|
||||
keywords?: string;
|
||||
status?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 部门类型
|
||||
*/
|
||||
export interface DeptVO {
|
||||
/**
|
||||
* 子部门
|
||||
*/
|
||||
children?: DeptVO[];
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
createTime?: Date;
|
||||
/**
|
||||
* 部门ID
|
||||
*/
|
||||
id?: number;
|
||||
/**
|
||||
* 部门名称
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* 父部门ID
|
||||
*/
|
||||
parentId?: number;
|
||||
/**
|
||||
* 排序
|
||||
*/
|
||||
sort?: number;
|
||||
/**
|
||||
* 状态(1:启用;0:禁用)
|
||||
*/
|
||||
status?: number;
|
||||
/**
|
||||
* 修改时间
|
||||
*/
|
||||
updateTime?: Date;
|
||||
}
|
||||
|
||||
/**
|
||||
* 部门表单类型
|
||||
*/
|
||||
export interface DeptForm {
|
||||
/**
|
||||
* 部门ID(新增不填)
|
||||
*/
|
||||
id?: number;
|
||||
/**
|
||||
* 部门名称
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* 父部门ID
|
||||
*/
|
||||
parentId: number;
|
||||
/**
|
||||
* 排序
|
||||
*/
|
||||
sort?: number;
|
||||
/**
|
||||
* 状态(1:启用;0:禁用)
|
||||
*/
|
||||
status?: number;
|
||||
}
|
||||
183
src/api/dict.ts
Normal file
183
src/api/dict.ts
Normal file
@@ -0,0 +1,183 @@
|
||||
import request from "@/utils/request";
|
||||
|
||||
const DICT_BASE_URL = "/api/v1/dict";
|
||||
|
||||
class DictAPI {
|
||||
/**
|
||||
* 获取字典分页列表
|
||||
*
|
||||
* @param queryParams 查询参数
|
||||
* @returns 字典分页结果
|
||||
*/
|
||||
static getPage(queryParams: DictPageQuery) {
|
||||
return request<any, PageResult<DictPageVO[]>>({
|
||||
url: `${DICT_BASE_URL}/page`,
|
||||
method: "get",
|
||||
params: queryParams,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字典表单数据
|
||||
*
|
||||
* @param id 字典ID
|
||||
* @returns 字典表单数据
|
||||
*/
|
||||
static getFormData(id: number) {
|
||||
return request<any, ResponseData<DictForm>>({
|
||||
url: `${DICT_BASE_URL}/${id}/form`,
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增字典
|
||||
*
|
||||
* @param data 字典表单数据
|
||||
* @returns 请求结果
|
||||
*/
|
||||
static add(data: DictForm) {
|
||||
return request({
|
||||
url: `${DICT_BASE_URL}`,
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改字典
|
||||
*
|
||||
* @param id 字典ID
|
||||
* @param data 字典表单数据
|
||||
* @returns 请求结果
|
||||
*/
|
||||
static update(id: number, data: DictForm) {
|
||||
return request({
|
||||
url: `${DICT_BASE_URL}/${id}`,
|
||||
method: "put",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除字典
|
||||
*
|
||||
* @param ids 字典ID,多个以英文逗号(,)分隔
|
||||
* @returns 请求结果
|
||||
*/
|
||||
static deleteByIds(ids: string) {
|
||||
return request({
|
||||
url: `${DICT_BASE_URL}/${ids}`,
|
||||
method: "delete",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字典的数据项
|
||||
*
|
||||
* @param typeCode 字典编码
|
||||
* @returns 字典数据项
|
||||
*/
|
||||
static getOptions(code: string) {
|
||||
return request<any, OptionType[]>({
|
||||
url: `${DICT_BASE_URL}/${code}/options`,
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default DictAPI;
|
||||
|
||||
/**
|
||||
* 字典查询参数
|
||||
*/
|
||||
export interface DictPageQuery extends PageQuery {
|
||||
/**
|
||||
* 关键字(字典名称/编码)
|
||||
*/
|
||||
keywords?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字典分页对象
|
||||
*/
|
||||
export interface DictPageVO {
|
||||
/**
|
||||
* 字典ID
|
||||
*/
|
||||
id: number;
|
||||
/**
|
||||
* 字典名称
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* 字典编码
|
||||
*/
|
||||
code: string;
|
||||
/**
|
||||
* 字典状态(1-启用,0-禁用)
|
||||
*/
|
||||
status: number;
|
||||
/**
|
||||
* 字典项列表
|
||||
*/
|
||||
dictItems: DictItem[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 字典项
|
||||
*/
|
||||
export interface DictItem {
|
||||
/**
|
||||
* 字典项ID
|
||||
*/
|
||||
id?: number;
|
||||
/**
|
||||
* 字典项名称
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* 字典项值
|
||||
*/
|
||||
value?: string;
|
||||
/**
|
||||
* 排序
|
||||
*/
|
||||
sort?: number;
|
||||
/**
|
||||
* 状态(1-启用,0-禁用)
|
||||
*/
|
||||
status?: number;
|
||||
}
|
||||
|
||||
// TypeScript 类型声明
|
||||
|
||||
/**
|
||||
* 字典
|
||||
*/
|
||||
export interface DictForm {
|
||||
/**
|
||||
* 字典ID
|
||||
*/
|
||||
id?: number;
|
||||
/**
|
||||
* 字典名称
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* 字典编码
|
||||
*/
|
||||
code?: string;
|
||||
/**
|
||||
* 字典状态(1-启用,0-禁用)
|
||||
*/
|
||||
status?: number;
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
remark?: string;
|
||||
/**
|
||||
* 字典数据项列表
|
||||
*/
|
||||
dictItems?: DictItem[];
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
import request from "@/utils/request";
|
||||
import {
|
||||
DictTypeQuery,
|
||||
DictTypePageResult,
|
||||
DictTypeForm,
|
||||
DictQuery,
|
||||
DictForm,
|
||||
DictPageResult,
|
||||
} from "./model";
|
||||
|
||||
class DictAPI {
|
||||
/**
|
||||
* 字典类型分页列表
|
||||
*
|
||||
* @param queryParams
|
||||
*/
|
||||
static getDictTypePage(queryParams: DictTypeQuery) {
|
||||
return request<any, DictTypePageResult>({
|
||||
url: "/api/v1/dict/types/page",
|
||||
method: "get",
|
||||
params: queryParams,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 字典类型表单数据
|
||||
*
|
||||
* @param id
|
||||
*/
|
||||
static getDictTypeForm(id: number) {
|
||||
return request<any, ResponseData<DictTypeForm>>({
|
||||
url: "/api/v1/dict/types/" + id + "/form",
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增字典类型
|
||||
*
|
||||
* @param data
|
||||
*/
|
||||
static addDictType(data: DictTypeForm) {
|
||||
return request({
|
||||
url: "/api/v1/dict/types",
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改字典类型
|
||||
*
|
||||
* @param id
|
||||
* @param data
|
||||
*/
|
||||
static updateDictType(id: number, data: DictTypeForm) {
|
||||
return request({
|
||||
url: "/api/v1/dict/types/" + id,
|
||||
method: "put",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除字典类型
|
||||
*/
|
||||
static deleteDictTypes(ids: string) {
|
||||
return request({
|
||||
url: "/api/v1/dict/types/" + ids,
|
||||
method: "delete",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字典类型的数据项
|
||||
*
|
||||
* @param typeCode 字典类型编码
|
||||
*/
|
||||
static getDictOptions(typeCode: string) {
|
||||
return request<any, OptionType[]>({
|
||||
url: "/api/v1/dict/" + typeCode + "/options",
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 字典分页列表
|
||||
*/
|
||||
static getDictPage(queryParams: DictQuery) {
|
||||
return request<any, DictPageResult>({
|
||||
url: "/api/v1/dict/page",
|
||||
method: "get",
|
||||
params: queryParams,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字典表单数据
|
||||
*
|
||||
* @param id
|
||||
*/
|
||||
static getDictFormData(id: number) {
|
||||
return request<any, DictForm>({
|
||||
url: "/api/v1/dict/" + id + "/form",
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增字典
|
||||
*
|
||||
* @param data
|
||||
*/
|
||||
static addDict(data: DictForm) {
|
||||
return request({
|
||||
url: "/api/v1/dict",
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改字典项
|
||||
*
|
||||
* @param id
|
||||
* @param data
|
||||
*/
|
||||
static updateDict(id: number, data: DictForm) {
|
||||
return request({
|
||||
url: "/api/v1/dict/" + id,
|
||||
method: "put",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除字典
|
||||
*
|
||||
* @param ids 字典项ID,多个以英文逗号(,)分割
|
||||
*/
|
||||
static deleteDictByIds(ids: string) {
|
||||
return request({
|
||||
url: "/api/v1/dict/" + ids,
|
||||
method: "delete",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default DictAPI;
|
||||
@@ -1,142 +0,0 @@
|
||||
/**
|
||||
* 字典类型查询参数
|
||||
*/
|
||||
export interface DictTypeQuery extends PageQuery {
|
||||
/**
|
||||
* 关键字(字典类型名称/编码)
|
||||
*/
|
||||
keywords?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字典类型分页对象
|
||||
*/
|
||||
export interface DictTypePageVO {
|
||||
/**
|
||||
* 字典类型ID
|
||||
*/
|
||||
id: number;
|
||||
/**
|
||||
* 类型编码
|
||||
*/
|
||||
code: string;
|
||||
/**
|
||||
* 类型名称
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* 状态(1:启用;0:禁用)
|
||||
*/
|
||||
status?: number;
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字典分页项类型声明
|
||||
*/
|
||||
export type DictTypePageResult = PageResult<DictTypePageVO[]>;
|
||||
|
||||
/**
|
||||
* 字典表单类型声明
|
||||
*/
|
||||
export interface DictTypeForm {
|
||||
/**
|
||||
* 字典类型ID
|
||||
*/
|
||||
id?: number;
|
||||
/**
|
||||
* 类型名称
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* 类型编码
|
||||
*/
|
||||
code?: string;
|
||||
/**
|
||||
* 类型状态:1:启用;0:禁用
|
||||
*/
|
||||
status: number;
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字典查询参数
|
||||
*/
|
||||
export interface DictQuery extends PageQuery {
|
||||
/**
|
||||
* 字典项名称
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* 字典类型编码
|
||||
*/
|
||||
typeCode?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字典分页对象
|
||||
*/
|
||||
export interface DictPageVO {
|
||||
/**
|
||||
* 字典ID
|
||||
*/
|
||||
id?: number;
|
||||
/**
|
||||
* 字典名称
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* 状态(1:启用;0:禁用)
|
||||
*/
|
||||
status?: number;
|
||||
/**
|
||||
* 字典值
|
||||
*/
|
||||
value?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字典分页
|
||||
*/
|
||||
export type DictPageResult = PageResult<DictPageVO[]>;
|
||||
|
||||
/**
|
||||
* 字典表单
|
||||
*/
|
||||
export interface DictForm {
|
||||
/**
|
||||
* 字典ID
|
||||
*/
|
||||
id?: number;
|
||||
/**
|
||||
* 字典名称
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* 排序
|
||||
*/
|
||||
sort?: number;
|
||||
/**
|
||||
* 状态(1:启用;0:禁用)
|
||||
*/
|
||||
status?: number;
|
||||
/**
|
||||
* 类型编码
|
||||
*/
|
||||
typeCode?: string;
|
||||
/**
|
||||
* 值
|
||||
*/
|
||||
value?: string;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
remark?: string;
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
import request from "@/utils/request";
|
||||
import { FileInfo } from "./model";
|
||||
|
||||
class FileAPI {
|
||||
/**
|
||||
@@ -35,3 +34,13 @@ class FileAPI {
|
||||
}
|
||||
|
||||
export default FileAPI;
|
||||
|
||||
/**
|
||||
* 文件API类型声明
|
||||
*/
|
||||
export interface FileInfo {
|
||||
/** 文件名 */
|
||||
name: string;
|
||||
/** 文件路径 */
|
||||
url: string;
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
/**
|
||||
* 文件API类型声明
|
||||
*/
|
||||
export interface FileInfo {
|
||||
name: string;
|
||||
url: string;
|
||||
}
|
||||
209
src/api/menu.ts
Normal file
209
src/api/menu.ts
Normal file
@@ -0,0 +1,209 @@
|
||||
import request from "@/utils/request";
|
||||
// 菜单基础URL
|
||||
const MENU_BASE_URL = "/api/v1/menus";
|
||||
|
||||
class MenuAPI {
|
||||
/**
|
||||
* 获取路由列表
|
||||
*
|
||||
* @returns 路由列表
|
||||
*/
|
||||
static getRoutes() {
|
||||
return request<any, RouteVO[]>({
|
||||
url: `${MENU_BASE_URL}/routes`,
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取菜单树形列表
|
||||
*
|
||||
* @param queryParams 查询参数
|
||||
* @returns 菜单树形列表
|
||||
*/
|
||||
static getList(queryParams: MenuQuery) {
|
||||
return request<any, MenuVO[]>({
|
||||
url: `${MENU_BASE_URL}`,
|
||||
method: "get",
|
||||
params: queryParams,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取菜单下拉数据源
|
||||
*
|
||||
* @returns 菜单下拉数据源
|
||||
*/
|
||||
static getOptions() {
|
||||
return request<any, OptionType[]>({
|
||||
url: `${MENU_BASE_URL}/options`,
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取菜单表单数据
|
||||
*
|
||||
* @param id 菜单ID
|
||||
* @returns 菜单表单数据
|
||||
*/
|
||||
static getFormData(id: number) {
|
||||
return request<any, MenuForm>({
|
||||
url: `${MENU_BASE_URL}/${id}/form`,
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加菜单
|
||||
*
|
||||
* @param data 菜单表单数据
|
||||
* @returns 请求结果
|
||||
*/
|
||||
static add(data: MenuForm) {
|
||||
return request({
|
||||
url: `${MENU_BASE_URL}`,
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改菜单
|
||||
*
|
||||
* @param id 菜单ID
|
||||
* @param data 菜单表单数据
|
||||
* @returns 请求结果
|
||||
*/
|
||||
static update(id: string, data: MenuForm) {
|
||||
return request({
|
||||
url: `${MENU_BASE_URL}/${id}`,
|
||||
method: "put",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除菜单
|
||||
*
|
||||
* @param id 菜单ID
|
||||
* @returns 请求结果
|
||||
*/
|
||||
static deleteById(id: number) {
|
||||
return request({
|
||||
url: `${MENU_BASE_URL}/${id}`,
|
||||
method: "delete",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default MenuAPI;
|
||||
|
||||
import { MenuTypeEnum } from "@/enums/MenuTypeEnum";
|
||||
|
||||
/** 菜单查询参数 */
|
||||
export interface MenuQuery {
|
||||
/** 搜索关键字 */
|
||||
keywords?: string;
|
||||
}
|
||||
|
||||
/** 菜单视图对象 */
|
||||
export interface MenuVO {
|
||||
/** 子菜单 */
|
||||
children?: MenuVO[];
|
||||
/** 组件路径 */
|
||||
component?: string;
|
||||
/** ICON */
|
||||
icon?: string;
|
||||
/** 菜单ID */
|
||||
id?: number;
|
||||
/** 菜单名称 */
|
||||
name?: string;
|
||||
/** 父菜单ID */
|
||||
parentId?: number;
|
||||
/** 按钮权限标识 */
|
||||
perm?: string;
|
||||
/** 跳转路径 */
|
||||
redirect?: string;
|
||||
/** 路由名称 */
|
||||
routeName?: string;
|
||||
/** 路由相对路径 */
|
||||
routePath?: string;
|
||||
/** 菜单排序(数字越小排名越靠前) */
|
||||
sort?: number;
|
||||
/** 菜单 */
|
||||
type?: MenuTypeEnum;
|
||||
/** 菜单是否可见(1:显示;0:隐藏) */
|
||||
visible?: number;
|
||||
}
|
||||
|
||||
/** 菜单表单对象 */
|
||||
export interface MenuForm {
|
||||
/** 菜单ID */
|
||||
id?: string;
|
||||
/** 父菜单ID */
|
||||
parentId?: number;
|
||||
/** 菜单名称 */
|
||||
name?: string;
|
||||
/** 菜单是否可见(1-是 0-否) */
|
||||
visible: number;
|
||||
/** ICON */
|
||||
icon?: string;
|
||||
/** 排序 */
|
||||
sort?: number;
|
||||
/** 路由名称 */
|
||||
routeName?: string;
|
||||
/** 路由路径 */
|
||||
routePath?: string;
|
||||
/** 组件路径 */
|
||||
component?: string;
|
||||
/** 跳转路由路径 */
|
||||
redirect?: string;
|
||||
/** 菜单 */
|
||||
type?: MenuTypeEnum;
|
||||
/** 权限标识 */
|
||||
perm?: string;
|
||||
/** 【菜单】是否开启页面缓存 */
|
||||
keepAlive?: number;
|
||||
/** 【目录】只有一个子路由是否始终显示 */
|
||||
alwaysShow?: number;
|
||||
/** 参数 */
|
||||
params?: KeyValue[];
|
||||
}
|
||||
|
||||
interface KeyValue {
|
||||
key: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
/** RouteVO,路由对象 */
|
||||
export interface RouteVO {
|
||||
/** 子路由列表 */
|
||||
children: RouteVO[];
|
||||
/** 组件路径 */
|
||||
component?: string;
|
||||
/** 路由属性 */
|
||||
meta?: Meta;
|
||||
/** 路由名称 */
|
||||
name?: string;
|
||||
/** 路由路径 */
|
||||
path?: string;
|
||||
/** 跳转链接 */
|
||||
redirect?: string;
|
||||
}
|
||||
|
||||
/** Meta,路由属性 */
|
||||
export interface Meta {
|
||||
/** 【目录】只有一个子路由是否始终显示 */
|
||||
alwaysShow?: boolean;
|
||||
/** 是否隐藏(true-是 false-否) */
|
||||
hidden?: boolean;
|
||||
/** ICON */
|
||||
icon?: string;
|
||||
/** 【菜单】是否开启页面缓存 */
|
||||
keepAlive?: boolean;
|
||||
/** 拥有路由权限的角色编码 */
|
||||
roles?: string[];
|
||||
/** 路由title */
|
||||
title?: string;
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
import request from "@/utils/request";
|
||||
import { MenuQuery, MenuVO, MenuForm, RouteVO } from "./model";
|
||||
|
||||
class MenuAPI {
|
||||
/**
|
||||
* 获取路由列表
|
||||
*/
|
||||
static getRoutes() {
|
||||
return request<any, RouteVO[]>({
|
||||
url: "/api/v1/menus/routes",
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取菜单树形列表
|
||||
*
|
||||
* @param queryParams
|
||||
*/
|
||||
static getList(queryParams: MenuQuery) {
|
||||
return request<any, MenuVO[]>({
|
||||
url: "/api/v1/menus",
|
||||
method: "get",
|
||||
params: queryParams,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取菜单下拉数据源
|
||||
*/
|
||||
static getOptions() {
|
||||
return request<any, OptionType[]>({
|
||||
url: "/api/v1/menus/options",
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取菜单表单数据
|
||||
*
|
||||
* @param id
|
||||
*/
|
||||
static getFormData(id: number) {
|
||||
return request<any, MenuForm>({
|
||||
url: "/api/v1/menus/" + id + "/form",
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加菜单
|
||||
*
|
||||
* @param data
|
||||
*/
|
||||
static add(data: MenuForm) {
|
||||
return request({
|
||||
url: "/api/v1/menus",
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改菜单
|
||||
*
|
||||
* @param id
|
||||
* @param data
|
||||
*/
|
||||
static update(id: string, data: MenuForm) {
|
||||
return request({
|
||||
url: "/api/v1/menus/" + id,
|
||||
method: "put",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除菜单
|
||||
*
|
||||
* @param id 菜单ID
|
||||
*/
|
||||
static deleteById(id: number) {
|
||||
return request({
|
||||
url: "/api/v1/menus/" + id,
|
||||
method: "delete",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default MenuAPI;
|
||||
@@ -1,188 +0,0 @@
|
||||
import { MenuTypeEnum } from "@/enums/MenuTypeEnum";
|
||||
|
||||
/**
|
||||
* 菜单查询参数类型
|
||||
*/
|
||||
export interface MenuQuery {
|
||||
keywords?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 菜单视图对象类型
|
||||
*/
|
||||
export interface MenuVO {
|
||||
/**
|
||||
* 子菜单
|
||||
*/
|
||||
children?: MenuVO[];
|
||||
/**
|
||||
* 组件路径
|
||||
*/
|
||||
component?: string;
|
||||
/**
|
||||
* ICON
|
||||
*/
|
||||
icon?: string;
|
||||
/**
|
||||
* 菜单ID
|
||||
*/
|
||||
id?: number;
|
||||
/**
|
||||
* 菜单名称
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* 父菜单ID
|
||||
*/
|
||||
parentId?: number;
|
||||
/**
|
||||
* 按钮权限标识
|
||||
*/
|
||||
perm?: string;
|
||||
/**
|
||||
* 跳转路径
|
||||
*/
|
||||
redirect?: string;
|
||||
/**
|
||||
* 路由名称
|
||||
*/
|
||||
routeName?: string;
|
||||
/**
|
||||
* 路由相对路径
|
||||
*/
|
||||
routePath?: string;
|
||||
/**
|
||||
* 菜单排序(数字越小排名越靠前)
|
||||
*/
|
||||
sort?: number;
|
||||
/**
|
||||
* 菜单类型
|
||||
*/
|
||||
type?: MenuTypeEnum;
|
||||
/**
|
||||
* 菜单是否可见(1:显示;0:隐藏)
|
||||
*/
|
||||
visible?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 菜单表单对象类型
|
||||
*/
|
||||
export interface MenuForm {
|
||||
/**
|
||||
* 菜单ID
|
||||
*/
|
||||
id?: string;
|
||||
/**
|
||||
* 父菜单ID
|
||||
*/
|
||||
parentId?: number;
|
||||
/**
|
||||
* 菜单名称
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* 菜单是否可见(1:是;0:否;)
|
||||
*/
|
||||
visible: number;
|
||||
icon?: string;
|
||||
/**
|
||||
* 排序
|
||||
*/
|
||||
sort?: number;
|
||||
/**
|
||||
* 组件路径
|
||||
*/
|
||||
component?: string;
|
||||
/**
|
||||
* 路由路径
|
||||
*/
|
||||
path?: string;
|
||||
/**
|
||||
* 跳转路由路径
|
||||
*/
|
||||
redirect?: string;
|
||||
|
||||
/**
|
||||
* 菜单类型
|
||||
*/
|
||||
type?: MenuTypeEnum;
|
||||
|
||||
/**
|
||||
* 权限标识
|
||||
*/
|
||||
perm?: string;
|
||||
/**
|
||||
* 【菜单】是否开启页面缓存
|
||||
*/
|
||||
keepAlive?: number;
|
||||
|
||||
/**
|
||||
* 【目录】只有一个子路由是否始终显示
|
||||
*/
|
||||
alwaysShow?: number;
|
||||
|
||||
params?: KeyValue[];
|
||||
}
|
||||
|
||||
interface KeyValue {
|
||||
key: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* RouteVO,路由对象
|
||||
*/
|
||||
export interface RouteVO {
|
||||
/**
|
||||
* 子路由列表
|
||||
*/
|
||||
children: RouteVO[];
|
||||
/**
|
||||
* 组件路径
|
||||
*/
|
||||
component?: string;
|
||||
meta?: Meta;
|
||||
/**
|
||||
* 路由名称
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* 路由路径
|
||||
*/
|
||||
path?: string;
|
||||
/**
|
||||
* 跳转链接
|
||||
*/
|
||||
redirect?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Meta,路由属性类型
|
||||
*/
|
||||
export interface Meta {
|
||||
/**
|
||||
* 【目录】只有一个子路由是否始终显示
|
||||
*/
|
||||
alwaysShow?: boolean;
|
||||
/**
|
||||
* 是否隐藏(true-是 false-否)
|
||||
*/
|
||||
hidden?: boolean;
|
||||
/**
|
||||
* ICON
|
||||
*/
|
||||
icon?: string;
|
||||
/**
|
||||
* 【菜单】是否开启页面缓存
|
||||
*/
|
||||
keepAlive?: boolean;
|
||||
/**
|
||||
* 拥有路由权限的角色编码
|
||||
*/
|
||||
roles?: string[];
|
||||
/**
|
||||
* 路由title
|
||||
*/
|
||||
title?: string;
|
||||
}
|
||||
140
src/api/role.ts
Normal file
140
src/api/role.ts
Normal file
@@ -0,0 +1,140 @@
|
||||
import request from "@/utils/request";
|
||||
|
||||
const ROLE_BASE_URL = "/api/v1/roles";
|
||||
|
||||
class RoleAPI {
|
||||
/** 获取角色分页数据 */
|
||||
static getPage(queryParams?: RolePageQuery) {
|
||||
return request<any, PageResult<RolePageVO[]>>({
|
||||
url: `${ROLE_BASE_URL}/page`,
|
||||
method: "get",
|
||||
params: queryParams,
|
||||
});
|
||||
}
|
||||
|
||||
/** 获取角色下拉数据源 */
|
||||
static getOptions() {
|
||||
return request<any, OptionType[]>({
|
||||
url: `${ROLE_BASE_URL}/options`,
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取角色的菜单ID集合
|
||||
*
|
||||
* @param roleId 角色ID
|
||||
* @returns 角色的菜单ID集合
|
||||
*/
|
||||
static getRoleMenuIds(roleId: number) {
|
||||
return request<any, number[]>({
|
||||
url: `${ROLE_BASE_URL}/${roleId}/menuIds`,
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 分配菜单权限
|
||||
*
|
||||
* @param roleId 角色ID
|
||||
* @param data 菜单ID集合
|
||||
* @returns 请求结果
|
||||
*/
|
||||
static updateRoleMenus(roleId: number, data: number[]) {
|
||||
return request({
|
||||
url: `${ROLE_BASE_URL}/${roleId}/menus`,
|
||||
method: "put",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取角色表单数据
|
||||
*
|
||||
* @param id 角色ID
|
||||
* @returns 角色表单数据
|
||||
*/
|
||||
static getFormData(id: number) {
|
||||
return request<any, RoleForm>({
|
||||
url: `${ROLE_BASE_URL}/${id}/form`,
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
/** 添加角色 */
|
||||
static add(data: RoleForm) {
|
||||
return request({
|
||||
url: `${ROLE_BASE_URL}`,
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新角色
|
||||
*
|
||||
* @param id 角色ID
|
||||
* @param data 角色表单数据
|
||||
*/
|
||||
static update(id: number, data: RoleForm) {
|
||||
return request({
|
||||
url: `${ROLE_BASE_URL}/${id}`,
|
||||
method: "put",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除角色,多个以英文逗号(,)分割
|
||||
*
|
||||
* @param ids 角色ID字符串,多个以英文逗号(,)分割
|
||||
*/
|
||||
static deleteByIds(ids: string) {
|
||||
return request({
|
||||
url: `${ROLE_BASE_URL}/${ids}`,
|
||||
method: "delete",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default RoleAPI;
|
||||
|
||||
/** 角色分页查询参数 */
|
||||
export interface RolePageQuery extends PageQuery {
|
||||
/** 搜索关键字 */
|
||||
keywords?: string;
|
||||
}
|
||||
|
||||
/** 角色分页对象 */
|
||||
export interface RolePageVO {
|
||||
/** 角色编码 */
|
||||
code?: string;
|
||||
/** 角色ID */
|
||||
id?: number;
|
||||
/** 角色名称 */
|
||||
name?: string;
|
||||
/** 排序 */
|
||||
sort?: number;
|
||||
/** 角色状态 */
|
||||
status?: number;
|
||||
/** 创建时间 */
|
||||
createTime?: Date;
|
||||
/** 修改时间 */
|
||||
updateTime?: Date;
|
||||
}
|
||||
|
||||
/** 角色表单对象 */
|
||||
export interface RoleForm {
|
||||
/** 角色ID */
|
||||
id?: number;
|
||||
/** 角色编码 */
|
||||
code: string;
|
||||
/** 数据权限 */
|
||||
dataScope?: number;
|
||||
/** 角色名称 */
|
||||
name: string;
|
||||
/** 排序 */
|
||||
sort?: number;
|
||||
/** 角色状态(1-正常;0-停用) */
|
||||
status?: number;
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
import request from "@/utils/request";
|
||||
import { RoleQuery, RolePageResult, RoleForm } from "./model";
|
||||
|
||||
class RoleAPI {
|
||||
/**
|
||||
* 获取角色分页数据
|
||||
*
|
||||
* @param queryParams
|
||||
*/
|
||||
static getPage(queryParams?: RoleQuery) {
|
||||
return request<any, RolePageResult>({
|
||||
url: "/api/v1/roles/page",
|
||||
method: "get",
|
||||
params: queryParams,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取角色下拉数据源
|
||||
*
|
||||
* @param queryParams
|
||||
*/
|
||||
static getOptions(queryParams?: RoleQuery) {
|
||||
return request<any, OptionType[]>({
|
||||
url: "/api/v1/roles/options",
|
||||
method: "get",
|
||||
params: queryParams,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取角色的菜单ID集合
|
||||
*
|
||||
* @param queryParams
|
||||
*/
|
||||
static getRoleMenuIds(roleId: number) {
|
||||
return request<any, number[]>({
|
||||
url: "/api/v1/roles/" + roleId + "/menuIds",
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 分配菜单权限给角色
|
||||
*
|
||||
* @param queryParams
|
||||
*/
|
||||
static updateRoleMenus(roleId: number, data: number[]) {
|
||||
return request({
|
||||
url: "/api/v1/roles/" + roleId + "/menus",
|
||||
method: "put",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取角色表单数据
|
||||
*
|
||||
* @param id
|
||||
*/
|
||||
static getFormData(id: number) {
|
||||
return request<any, RoleForm>({
|
||||
url: "/api/v1/roles/" + id + "/form",
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加角色
|
||||
*
|
||||
* @param data
|
||||
*/
|
||||
static add(data: RoleForm) {
|
||||
return request({
|
||||
url: "/api/v1/roles",
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新角色
|
||||
*
|
||||
* @param id
|
||||
* @param data
|
||||
*/
|
||||
static update(id: number, data: RoleForm) {
|
||||
return request({
|
||||
url: "/api/v1/roles/" + id,
|
||||
method: "put",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除角色,多个以英文逗号(,)分割
|
||||
*
|
||||
* @param ids
|
||||
*/
|
||||
static deleteByIds(ids: string) {
|
||||
return request({
|
||||
url: "/api/v1/roles/" + ids,
|
||||
method: "delete",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default RoleAPI;
|
||||
@@ -1,78 +0,0 @@
|
||||
/**
|
||||
* 角色查询参数
|
||||
*/
|
||||
export interface RoleQuery extends PageQuery {
|
||||
keywords?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 角色分页对象
|
||||
*/
|
||||
export interface RolePageVO {
|
||||
/**
|
||||
* 角色编码
|
||||
*/
|
||||
code?: string;
|
||||
|
||||
/**
|
||||
* 角色ID
|
||||
*/
|
||||
id?: number;
|
||||
/**
|
||||
* 角色名称
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* 排序
|
||||
*/
|
||||
sort?: number;
|
||||
/**
|
||||
* 角色状态
|
||||
*/
|
||||
status?: number;
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
createTime?: Date;
|
||||
/**
|
||||
* 修改时间
|
||||
*/
|
||||
updateTime?: Date;
|
||||
}
|
||||
|
||||
/**
|
||||
* 角色分页
|
||||
*/
|
||||
export type RolePageResult = PageResult<RolePageVO[]>;
|
||||
|
||||
/**
|
||||
* 角色表单对象
|
||||
*/
|
||||
export interface RoleForm {
|
||||
/**
|
||||
* 角色ID
|
||||
*/
|
||||
id?: number;
|
||||
|
||||
/**
|
||||
* 角色编码
|
||||
*/
|
||||
code: string;
|
||||
/**
|
||||
* 数据权限
|
||||
*/
|
||||
dataScope?: number;
|
||||
|
||||
/**
|
||||
* 角色名称
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* 排序
|
||||
*/
|
||||
sort?: number;
|
||||
/**
|
||||
* 角色状态(1-正常;0-停用)
|
||||
*/
|
||||
status?: number;
|
||||
}
|
||||
232
src/api/user.ts
Normal file
232
src/api/user.ts
Normal file
@@ -0,0 +1,232 @@
|
||||
import request from "@/utils/request";
|
||||
|
||||
const USER_BASE_URL = "/api/v1/users";
|
||||
|
||||
class UserAPI {
|
||||
/**
|
||||
* 获取当前登录用户信息
|
||||
*
|
||||
* @returns 登录用户昵称、头像信息,包括角色和权限
|
||||
*/
|
||||
static getInfo() {
|
||||
return request<any, UserInfo>({
|
||||
url: `${USER_BASE_URL}/me`,
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户分页列表
|
||||
*
|
||||
* @param queryParams 查询参数
|
||||
*/
|
||||
static getPage(queryParams: UserPageQuery) {
|
||||
return request<any, PageResult<UserPageVO[]>>({
|
||||
url: `${USER_BASE_URL}/page`,
|
||||
method: "get",
|
||||
params: queryParams,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户表单详情
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @returns 用户表单详情
|
||||
*/
|
||||
static getFormData(userId: number) {
|
||||
return request<any, UserForm>({
|
||||
url: `${USER_BASE_URL}/${userId}/form`,
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加用户
|
||||
*
|
||||
* @param data 用户表单数据
|
||||
*/
|
||||
static add(data: UserForm) {
|
||||
return request({
|
||||
url: `${USER_BASE_URL}`,
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改用户
|
||||
*
|
||||
* @param id 用户ID
|
||||
* @param data 用户表单数据
|
||||
*/
|
||||
static update(id: number, data: UserForm) {
|
||||
return request({
|
||||
url: `${USER_BASE_URL}/${id}`,
|
||||
method: "put",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改用户密码
|
||||
*
|
||||
* @param id 用户ID
|
||||
* @param password 新密码
|
||||
*/
|
||||
static updatePassword(id: number, password: string) {
|
||||
return request({
|
||||
url: `${USER_BASE_URL}/${id}/password`,
|
||||
method: "patch",
|
||||
params: { password: password },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除用户,多个以英文逗号(,)分割
|
||||
*
|
||||
* @param ids 用户ID字符串,多个以英文逗号(,)分割
|
||||
*/
|
||||
static deleteByIds(ids: string) {
|
||||
return request({
|
||||
url: `${USER_BASE_URL}/${ids}`,
|
||||
method: "delete",
|
||||
});
|
||||
}
|
||||
|
||||
/** 下载用户导入模板 */
|
||||
static downloadTemplate() {
|
||||
return request({
|
||||
url: `${USER_BASE_URL}/template`,
|
||||
method: "get",
|
||||
responseType: "arraybuffer",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出用户
|
||||
*
|
||||
* @param queryParams 查询参数
|
||||
*/
|
||||
static export(queryParams: UserPageQuery) {
|
||||
return request({
|
||||
url: `${USER_BASE_URL}/export`,
|
||||
method: "get",
|
||||
params: queryParams,
|
||||
responseType: "arraybuffer",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入用户
|
||||
*
|
||||
* @param deptId 部门ID
|
||||
* @param file 导入文件
|
||||
*/
|
||||
static import(deptId: number, file: File) {
|
||||
const formData = new FormData();
|
||||
formData.append("file", file);
|
||||
return request({
|
||||
url: `${USER_BASE_URL}/import`,
|
||||
method: "post",
|
||||
params: { deptId: deptId },
|
||||
data: formData,
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data",
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default UserAPI;
|
||||
|
||||
/** 登录用户信息 */
|
||||
export interface UserInfo {
|
||||
/** 用户ID */
|
||||
userId?: number;
|
||||
|
||||
/** 用户名 */
|
||||
username?: string;
|
||||
|
||||
/** 昵称 */
|
||||
nickname?: string;
|
||||
|
||||
/** 头像URL */
|
||||
avatar?: string;
|
||||
|
||||
/** 角色 */
|
||||
roles: string[];
|
||||
|
||||
/** 权限 */
|
||||
perms: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户分页查询对象
|
||||
*/
|
||||
export interface UserPageQuery extends PageQuery {
|
||||
/** 搜索关键字 */
|
||||
keywords?: string;
|
||||
|
||||
/** 用户状态 */
|
||||
status?: number;
|
||||
|
||||
/** 部门ID */
|
||||
deptId?: number;
|
||||
|
||||
/** 开始时间 */
|
||||
startTime?: string;
|
||||
|
||||
/** 结束时间 */
|
||||
endTime?: string;
|
||||
}
|
||||
|
||||
/** 用户分页对象 */
|
||||
export interface UserPageVO {
|
||||
/** 用户头像URL */
|
||||
avatar?: string;
|
||||
/** 创建时间 */
|
||||
createTime?: Date;
|
||||
/** 部门名称 */
|
||||
deptName?: string;
|
||||
/** 用户邮箱 */
|
||||
email?: string;
|
||||
/** 性别 */
|
||||
genderLabel?: string;
|
||||
/** 用户ID */
|
||||
id?: number;
|
||||
/** 手机号 */
|
||||
mobile?: string;
|
||||
/** 用户昵称 */
|
||||
nickname?: string;
|
||||
/** 角色名称,多个使用英文逗号(,)分割 */
|
||||
roleNames?: string;
|
||||
/** 用户状态(1:启用;0:禁用) */
|
||||
status?: number;
|
||||
/** 用户名 */
|
||||
username?: string;
|
||||
}
|
||||
|
||||
/** 用户表单类型 */
|
||||
export interface UserForm {
|
||||
/** 用户头像 */
|
||||
avatar?: string;
|
||||
/** 部门ID */
|
||||
deptId?: number;
|
||||
/** 邮箱 */
|
||||
email?: string;
|
||||
/** 性别 */
|
||||
gender?: number;
|
||||
/** 用户ID */
|
||||
id?: number;
|
||||
/** 手机号 */
|
||||
mobile?: string;
|
||||
/** 昵称 */
|
||||
nickname?: string;
|
||||
/** 角色ID集合 */
|
||||
roleIds?: number[];
|
||||
/** 用户状态(1:正常;0:禁用) */
|
||||
status?: number;
|
||||
/** 用户名 */
|
||||
username?: string;
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
import request from "@/utils/request";
|
||||
import { UserForm, UserInfo, UserPageVO, UserQuery } from "./model";
|
||||
|
||||
class UserAPI {
|
||||
/**
|
||||
* 登录成功后获取用户信息(昵称、头像、权限集合和角色集合)
|
||||
*/
|
||||
static getInfo() {
|
||||
return request<any, UserInfo>({
|
||||
url: "/api/v1/users/me",
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户分页列表
|
||||
*
|
||||
* @param queryParams
|
||||
*/
|
||||
static getPage(queryParams: UserQuery) {
|
||||
return request<any, PageResult<UserPageVO[]>>({
|
||||
url: "/api/v1/users/page",
|
||||
method: "get",
|
||||
params: queryParams,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户表单详情
|
||||
*
|
||||
* @param userId
|
||||
*/
|
||||
static getFormData(userId: number) {
|
||||
return request<any, UserForm>({
|
||||
url: "/api/v1/users/" + userId + "/form",
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加用户
|
||||
*
|
||||
* @param data
|
||||
*/
|
||||
static add(data: UserForm) {
|
||||
return request({
|
||||
url: "/api/v1/users",
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改用户
|
||||
*
|
||||
* @param id
|
||||
* @param data
|
||||
*/
|
||||
static update(id: number, data: UserForm) {
|
||||
return request({
|
||||
url: "/api/v1/users/" + id,
|
||||
method: "put",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改用户密码
|
||||
*
|
||||
* @param id
|
||||
* @param password
|
||||
*/
|
||||
static updatePassword(id: number, password: string) {
|
||||
return request({
|
||||
url: "/api/v1/users/" + id + "/password",
|
||||
method: "patch",
|
||||
params: { password: password },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除用户
|
||||
*
|
||||
* @param ids
|
||||
*/
|
||||
static deleteByIds(ids: string) {
|
||||
return request({
|
||||
url: "/api/v1/users/" + ids,
|
||||
method: "delete",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载用户导入模板
|
||||
*
|
||||
* @returns
|
||||
*/
|
||||
static downloadTemplate() {
|
||||
return request({
|
||||
url: "/api/v1/users/template",
|
||||
method: "get",
|
||||
responseType: "arraybuffer",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出用户
|
||||
*
|
||||
* @param queryParams
|
||||
* @returns
|
||||
*/
|
||||
static export(queryParams: UserQuery) {
|
||||
return request({
|
||||
url: "/api/v1/users/export",
|
||||
method: "get",
|
||||
params: queryParams,
|
||||
responseType: "arraybuffer",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入用户
|
||||
*
|
||||
* @param file
|
||||
*/
|
||||
static import(deptId: number, file: File) {
|
||||
const formData = new FormData();
|
||||
formData.append("file", file);
|
||||
return request({
|
||||
url: "/api/v1/users/import",
|
||||
method: "post",
|
||||
params: { deptId: deptId },
|
||||
data: formData,
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data",
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default UserAPI;
|
||||
@@ -1,115 +0,0 @@
|
||||
/**
|
||||
* 登录用户信息
|
||||
*/
|
||||
export interface UserInfo {
|
||||
userId?: number;
|
||||
username?: string;
|
||||
nickname?: string;
|
||||
avatar?: string;
|
||||
roles: string[];
|
||||
perms: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户查询对象类型
|
||||
*/
|
||||
export interface UserQuery extends PageQuery {
|
||||
keywords?: string;
|
||||
status?: number;
|
||||
deptId?: number;
|
||||
startTime?: string;
|
||||
endTime?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户分页对象
|
||||
*/
|
||||
export interface UserPageVO {
|
||||
/**
|
||||
* 用户头像地址
|
||||
*/
|
||||
avatar?: string;
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
createTime?: Date;
|
||||
/**
|
||||
* 部门名称
|
||||
*/
|
||||
deptName?: string;
|
||||
/**
|
||||
* 用户邮箱
|
||||
*/
|
||||
email?: string;
|
||||
/**
|
||||
* 性别
|
||||
*/
|
||||
genderLabel?: string;
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
id?: number;
|
||||
/**
|
||||
* 手机号
|
||||
*/
|
||||
mobile?: string;
|
||||
/**
|
||||
* 用户昵称
|
||||
*/
|
||||
nickname?: string;
|
||||
/**
|
||||
* 角色名称,多个使用英文逗号(,)分割
|
||||
*/
|
||||
roleNames?: string;
|
||||
/**
|
||||
* 用户状态(1:启用;0:禁用)
|
||||
*/
|
||||
status?: number;
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
username?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户表单类型
|
||||
*/
|
||||
export interface UserForm {
|
||||
/**
|
||||
* 用户头像
|
||||
*/
|
||||
avatar?: string;
|
||||
/**
|
||||
* 部门ID
|
||||
*/
|
||||
deptId?: number;
|
||||
/**
|
||||
* 邮箱
|
||||
*/
|
||||
email?: string;
|
||||
/**
|
||||
* 性别
|
||||
*/
|
||||
gender?: number;
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
id?: number;
|
||||
mobile?: string;
|
||||
/**
|
||||
* 昵称
|
||||
*/
|
||||
nickname?: string;
|
||||
/**
|
||||
* 角色ID集合
|
||||
*/
|
||||
roleIds?: number[];
|
||||
/**
|
||||
* 用户状态(1:正常;0:禁用)
|
||||
*/
|
||||
status?: number;
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
username?: string;
|
||||
}
|
||||
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
@@ -29,11 +29,22 @@
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
<!-- 导入 -->
|
||||
<template v-else-if="item === 'import'">
|
||||
<el-button
|
||||
v-hasPerm="[`${contentConfig.pageName}:${item}`]"
|
||||
type="default"
|
||||
icon="upload"
|
||||
@click="handleToolbar(item)"
|
||||
>
|
||||
导入
|
||||
</el-button>
|
||||
</template>
|
||||
<!-- 导出 -->
|
||||
<template v-else-if="item === 'export'">
|
||||
<el-button
|
||||
v-hasPerm="[`${contentConfig.pageName}:${item}`]"
|
||||
type="primary"
|
||||
type="default"
|
||||
icon="download"
|
||||
@click="handleToolbar(item)"
|
||||
>
|
||||
@@ -407,28 +418,28 @@
|
||||
</el-dialog>
|
||||
<!-- 导入弹窗 -->
|
||||
<el-dialog
|
||||
v-model="importsModalVisible"
|
||||
v-model="importModalVisible"
|
||||
:align-center="true"
|
||||
title="导入数据"
|
||||
width="600px"
|
||||
style="padding-right: 0"
|
||||
@close="handleCloseImportsModal"
|
||||
@close="handleCloseImportModal"
|
||||
>
|
||||
<!-- 滚动 -->
|
||||
<el-scrollbar max-height="60vh">
|
||||
<!-- 表单 -->
|
||||
<el-form
|
||||
ref="importsFormRef"
|
||||
ref="importFormRef"
|
||||
label-width="auto"
|
||||
style="padding-right: var(--el-dialog-padding-primary)"
|
||||
:model="importsFormData"
|
||||
:rules="importsFormRules"
|
||||
:model="importFormData"
|
||||
:rules="importFormRules"
|
||||
>
|
||||
<el-form-item label="文件名" prop="files">
|
||||
<el-upload
|
||||
class="w-full"
|
||||
ref="uploadRef"
|
||||
v-model:file-list="importsFormData.files"
|
||||
v-model:file-list="importFormData.files"
|
||||
accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel"
|
||||
:drag="true"
|
||||
:limit="1"
|
||||
@@ -443,7 +454,7 @@
|
||||
<div class="el-upload__tip">
|
||||
*.xlsx / *.xls
|
||||
<el-link
|
||||
v-if="contentConfig.importsTemplate"
|
||||
v-if="contentConfig.importTemplate"
|
||||
type="primary"
|
||||
icon="download"
|
||||
:underline="false"
|
||||
@@ -462,12 +473,12 @@
|
||||
<div style="padding-right: var(--el-dialog-padding-primary)">
|
||||
<el-button
|
||||
type="primary"
|
||||
:disabled="importsFormData.files.length === 0"
|
||||
@click="handleImportsSubmit"
|
||||
:disabled="importFormData.files.length === 0"
|
||||
@click="handleImportSubmit"
|
||||
>
|
||||
确 定
|
||||
</el-button>
|
||||
<el-button @click="handleCloseImportsModal">取 消</el-button>
|
||||
<el-button @click="handleCloseImportModal">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
@@ -575,8 +586,8 @@ function handleSelectionChange(selection: any[]) {
|
||||
}
|
||||
|
||||
// 刷新
|
||||
function handleRefresh() {
|
||||
fetchPageData(lastFormData);
|
||||
function handleRefresh(isRestart = false) {
|
||||
fetchPageData(lastFormData, isRestart);
|
||||
}
|
||||
|
||||
// 删除
|
||||
@@ -595,7 +606,7 @@ function handleDelete(id?: number | string) {
|
||||
if (props.contentConfig.deleteAction) {
|
||||
props.contentConfig.deleteAction(ids).then(() => {
|
||||
ElMessage.success("删除成功");
|
||||
fetchPageData({}, true);
|
||||
handleRefresh(true);
|
||||
});
|
||||
} else {
|
||||
ElMessage.error("未配置deleteAction");
|
||||
@@ -695,20 +706,22 @@ function handleExports() {
|
||||
}
|
||||
|
||||
// 导入表单
|
||||
let isFileImport = false;
|
||||
const uploadRef = ref<UploadInstance>();
|
||||
const importsModalVisible = ref(false);
|
||||
const importsFormRef = ref<FormInstance>();
|
||||
const importsFormData = reactive<{
|
||||
const importModalVisible = ref(false);
|
||||
const importFormRef = ref<FormInstance>();
|
||||
const importFormData = reactive<{
|
||||
files: UploadUserFile[];
|
||||
}>({
|
||||
files: [],
|
||||
});
|
||||
const importsFormRules: FormRules = {
|
||||
const importFormRules: FormRules = {
|
||||
files: [{ required: true, message: "请选择文件" }],
|
||||
};
|
||||
// 打开导入弹窗
|
||||
function handleOpenImportsModal() {
|
||||
importsModalVisible.value = true;
|
||||
function handleOpenImportModal(isFile: boolean = false) {
|
||||
importModalVisible.value = true;
|
||||
isFileImport = isFile;
|
||||
}
|
||||
// 覆盖前一个文件
|
||||
function handleFileExceed(files: File[]) {
|
||||
@@ -719,11 +732,11 @@ function handleFileExceed(files: File[]) {
|
||||
}
|
||||
// 下载导入模板
|
||||
function handleDownloadTemplate() {
|
||||
const importsTemplate = props.contentConfig.importsTemplate;
|
||||
if (typeof importsTemplate === "string") {
|
||||
window.open(importsTemplate);
|
||||
} else if (typeof importsTemplate === "function") {
|
||||
importsTemplate().then((response) => {
|
||||
const importTemplate = props.contentConfig.importTemplate;
|
||||
if (typeof importTemplate === "string") {
|
||||
window.open(importTemplate);
|
||||
} else if (typeof importTemplate === "function") {
|
||||
importTemplate().then((response) => {
|
||||
const fileData = response.data;
|
||||
const fileName = decodeURI(
|
||||
response.headers["content-disposition"].split(";")[1].split("=")[1]
|
||||
@@ -731,21 +744,40 @@ function handleDownloadTemplate() {
|
||||
saveXlsx(fileData, fileName);
|
||||
});
|
||||
} else {
|
||||
ElMessage.error("未配置importsTemplate");
|
||||
ElMessage.error("未配置importTemplate");
|
||||
}
|
||||
}
|
||||
// 导入确认
|
||||
const handleImportsSubmit = useThrottleFn(() => {
|
||||
importsFormRef.value?.validate((valid: boolean) => {
|
||||
valid && handleImports();
|
||||
const handleImportSubmit = useThrottleFn(() => {
|
||||
importFormRef.value?.validate((valid: boolean) => {
|
||||
if (valid) {
|
||||
if (isFileImport) {
|
||||
handleImport();
|
||||
} else {
|
||||
handleImports();
|
||||
}
|
||||
}
|
||||
});
|
||||
}, 3000);
|
||||
// 关闭导入弹窗
|
||||
function handleCloseImportsModal() {
|
||||
importsModalVisible.value = false;
|
||||
importsFormRef.value?.resetFields();
|
||||
function handleCloseImportModal() {
|
||||
importModalVisible.value = false;
|
||||
importFormRef.value?.resetFields();
|
||||
nextTick(() => {
|
||||
importsFormRef.value?.clearValidate();
|
||||
importFormRef.value?.clearValidate();
|
||||
});
|
||||
}
|
||||
// 文件导入
|
||||
function handleImport() {
|
||||
const importAction = props.contentConfig.importAction;
|
||||
if (importAction === undefined) {
|
||||
ElMessage.error("未配置importAction");
|
||||
return;
|
||||
}
|
||||
importAction(importFormData.files[0].raw as File).then(() => {
|
||||
ElMessage.success("导入数据成功");
|
||||
handleCloseImportModal();
|
||||
handleRefresh(true);
|
||||
});
|
||||
}
|
||||
// 导入
|
||||
@@ -756,7 +788,7 @@ function handleImports() {
|
||||
return;
|
||||
}
|
||||
// 获取选择的文件
|
||||
const file = importsFormData.files[0].raw as File;
|
||||
const file = importFormData.files[0].raw as File;
|
||||
// 创建Workbook实例
|
||||
const workbook = new ExcelJS.Workbook();
|
||||
// 使用FileReader对象来读取文件内容
|
||||
@@ -803,7 +835,8 @@ function handleImports() {
|
||||
}
|
||||
importsAction(data).then(() => {
|
||||
ElMessage.success("导入数据成功");
|
||||
handleCloseImportsModal();
|
||||
handleCloseImportModal();
|
||||
handleRefresh(true);
|
||||
});
|
||||
})
|
||||
.catch((error) => console.log(error));
|
||||
@@ -823,7 +856,7 @@ function handleToolbar(name: string) {
|
||||
handleOpenExportsModal();
|
||||
break;
|
||||
case "imports":
|
||||
handleOpenImportsModal();
|
||||
handleOpenImportModal();
|
||||
break;
|
||||
case "search":
|
||||
emit("searchClick");
|
||||
@@ -834,6 +867,9 @@ function handleToolbar(name: string) {
|
||||
case "delete":
|
||||
handleDelete();
|
||||
break;
|
||||
case "import":
|
||||
handleOpenImportModal(true);
|
||||
break;
|
||||
case "export":
|
||||
emit("exportClick");
|
||||
break;
|
||||
@@ -878,11 +914,11 @@ function handleModify(
|
||||
// 分页切换
|
||||
function handleSizeChange(value: number) {
|
||||
pagination.pageSize = value;
|
||||
fetchPageData(lastFormData);
|
||||
handleRefresh();
|
||||
}
|
||||
function handleCurrentChange(value: number) {
|
||||
pagination.currentPage = value;
|
||||
fetchPageData(lastFormData);
|
||||
handleRefresh();
|
||||
}
|
||||
|
||||
// 远程数据筛选
|
||||
|
||||
@@ -92,8 +92,10 @@ export interface IContentConfig<T = any> {
|
||||
exportAction?: (queryParams: T) => Promise<any>;
|
||||
// 前端全量导出的网络请求函数(需返回promise)
|
||||
exportsAction?: (queryParams: T) => Promise<IObject[]>;
|
||||
// 前端导入模板
|
||||
importsTemplate?: string | (() => Promise<any>);
|
||||
// 导入模板
|
||||
importTemplate?: string | (() => Promise<any>);
|
||||
// 后端导入的网络请求函数(需返回promise)
|
||||
importAction?: (file: File) => Promise<any>;
|
||||
// 前端导入的网络请求函数(需返回promise)
|
||||
importsAction?: (data: IObject[]) => Promise<any>;
|
||||
// 主键名(默认为id)
|
||||
@@ -102,6 +104,7 @@ export interface IContentConfig<T = any> {
|
||||
toolbar?: Array<
|
||||
| "add"
|
||||
| "delete"
|
||||
| "import"
|
||||
| "export"
|
||||
| {
|
||||
auth: string;
|
||||
|
||||
@@ -20,9 +20,9 @@ import DictAPI from "@/api/dict";
|
||||
|
||||
const props = defineProps({
|
||||
/**
|
||||
* 字典类型编码(eg: 性别-gender)
|
||||
* 字典编码(eg: 性别-gender)
|
||||
*/
|
||||
typeCode: {
|
||||
code: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
@@ -39,14 +39,17 @@ const props = defineProps({
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits(["update:modelValue"]); // 父组件监听事件,同步子组件值的变化给父组件
|
||||
const emits = defineEmits(["update:modelValue"]);
|
||||
|
||||
const options: Ref<OptionType[]> = ref([]); // 字典下拉数据源
|
||||
const options: Ref<OptionType[]> = ref([]);
|
||||
|
||||
const selectedValue = ref<string | number | undefined>();
|
||||
|
||||
watch([options, () => props.modelValue], ([newOptions, newModelValue]) => {
|
||||
if (newOptions.length === 0) return; // 下拉数据源加载未完成不回显
|
||||
if (newOptions.length === 0) {
|
||||
// 下拉数据源加载未完成不回显
|
||||
return;
|
||||
}
|
||||
if (newModelValue == undefined) {
|
||||
selectedValue.value = undefined;
|
||||
return;
|
||||
@@ -65,8 +68,8 @@ function handleChange(val?: string | number | undefined) {
|
||||
}
|
||||
|
||||
onBeforeMount(() => {
|
||||
// 根据字典类型编码(typeCode)获取字典选项
|
||||
DictAPI.getDictOptions(props.typeCode).then((data) => {
|
||||
// 根据字典编码获取字典项
|
||||
DictAPI.getOptions(props.code).then((data) => {
|
||||
options.value = data;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<template>
|
||||
<!-- 如果菜单项没有隐藏则显示 -->
|
||||
<div v-if="!item.meta || !item.meta.hidden">
|
||||
<!-- 显示具有单个子路由的菜单项或没有子路由的父路由 -->
|
||||
<!-- 显示只有一个子路由或没有子路由的菜单项 -->
|
||||
<template
|
||||
v-if="
|
||||
hasOneShowingChild(item.children, item as RouteRecordRaw) &&
|
||||
@@ -59,7 +60,7 @@ import { RouteRecordRaw } from "vue-router";
|
||||
|
||||
const props = defineProps({
|
||||
/**
|
||||
* 路由(eg:user)123
|
||||
* 当前路由项对象
|
||||
*/
|
||||
item: {
|
||||
type: Object,
|
||||
@@ -67,47 +68,46 @@ const props = defineProps({
|
||||
},
|
||||
|
||||
/**
|
||||
* 父层级完整路由路径(eg:/system)
|
||||
* 父层级完整路由路径
|
||||
*/
|
||||
basePath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
/**
|
||||
* 是否为嵌套路由
|
||||
*/
|
||||
isNest: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const onlyOneChild = ref(); // 临时变量,唯一子路由
|
||||
const onlyOneChild = ref();
|
||||
|
||||
/**
|
||||
* 判断当前路由是否只有一个子路由
|
||||
*
|
||||
* 1:如果只有一个子路由: 返回 true
|
||||
* 2:如果无子路由 :返回 true
|
||||
* 判断当前路由是否只有一个显示的子路由
|
||||
*
|
||||
* @param children 子路由数组
|
||||
* @param parent 当前路由
|
||||
* @param parent 父级路由对象
|
||||
* @returns 布尔值,表示是否只有一个显示的子路由
|
||||
*/
|
||||
function hasOneShowingChild(
|
||||
children: RouteRecordRaw[] = [],
|
||||
parent: RouteRecordRaw
|
||||
) {
|
||||
// 子路由集合
|
||||
// 筛选出需要显示的子路由
|
||||
const showingChildren = children.filter((route: RouteRecordRaw) => {
|
||||
if (route.meta?.hidden) {
|
||||
// 过滤不显示的子路由
|
||||
return false;
|
||||
} else {
|
||||
route.meta!.hidden = false;
|
||||
// 临时变量(多个子路由 onlyOneChild 变量是用不上的)
|
||||
onlyOneChild.value = route;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// 如果只有一个子路由, 返回 true
|
||||
// 如果只有一个或没有显示的子路由
|
||||
if (showingChildren.length === 1) {
|
||||
return true;
|
||||
}
|
||||
@@ -121,9 +121,10 @@ function hasOneShowingChild(
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析路由路径(相对路径 → 绝对路径)
|
||||
* 解析路由路径,将相对路径转换为绝对路径
|
||||
*
|
||||
* @param routePath 路由路径
|
||||
* @returns 绝对路径
|
||||
*/
|
||||
function resolvePath(routePath: string) {
|
||||
if (isExternal(routePath)) {
|
||||
|
||||
@@ -41,9 +41,14 @@ const appStore = useAppStore();
|
||||
const permissionStore = usePermissionStore();
|
||||
const router = useRouter();
|
||||
|
||||
// 避免 activeTopMenuPath 缓存被清理,从当前路由路径获取顶部菜单路径,eg. /system/user → /system
|
||||
const activeTopMenuPath = useRoute().path.replace(/\/[^\/]+$/, "") || "/";
|
||||
appStore.activeTopMenu(activeTopMenuPath);
|
||||
|
||||
// 激活的顶部菜单路径
|
||||
const activePath = computed(() => appStore.activeTopMenuPath);
|
||||
|
||||
// 顶部菜单集合
|
||||
// 混合模式顶部菜单集合
|
||||
const mixTopMenus = ref<RouteRecordRaw[]>([]);
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
ref="tagRef"
|
||||
v-for="tag in visitedViews"
|
||||
:key="tag.fullPath"
|
||||
:class="'tags-item ' + (isActive(tag) ? 'active' : '')"
|
||||
:class="'tags-item ' + (tagsViewStore.isActive(tag) ? 'active' : '')"
|
||||
:to="{ path: tag.path, query: tag.query }"
|
||||
@click.middle="!isAffix(tag) ? closeSelectedTag(tag) : ''"
|
||||
@contextmenu.prevent="openContentMenu(tag, $event)"
|
||||
@@ -190,10 +190,6 @@ function moveToCurrentTag() {
|
||||
});
|
||||
}
|
||||
|
||||
function isActive(tag: TagView) {
|
||||
return tag.path === route.path;
|
||||
}
|
||||
|
||||
function isAffix(tag: TagView) {
|
||||
return tag?.affix;
|
||||
}
|
||||
@@ -228,26 +224,10 @@ function refreshSelectedTag(view: TagView) {
|
||||
});
|
||||
}
|
||||
|
||||
function toLastView(visitedViews: TagView[], view?: TagView) {
|
||||
const latestView = visitedViews.slice(-1)[0];
|
||||
if (latestView && latestView.fullPath) {
|
||||
router.push(latestView.fullPath);
|
||||
} else {
|
||||
// now the default is to redirect to the home page if there is no tags-view,
|
||||
// you can adjust it according to your needs.
|
||||
if (view?.name === "Dashboard") {
|
||||
// to reload home page
|
||||
router.replace("/redirect" + view.fullPath);
|
||||
} else {
|
||||
router.push("/");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function closeSelectedTag(view: TagView) {
|
||||
tagsViewStore.delView(view).then((res: any) => {
|
||||
if (isActive(view)) {
|
||||
toLastView(res.visitedViews, view);
|
||||
if (tagsViewStore.isActive(view)) {
|
||||
tagsViewStore.toLastView(res.visitedViews, view);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -255,14 +235,14 @@ function closeSelectedTag(view: TagView) {
|
||||
function closeLeftTags() {
|
||||
tagsViewStore.delLeftViews(selectedTag.value).then((res: any) => {
|
||||
if (!res.visitedViews.find((item: any) => item.path === route.path)) {
|
||||
toLastView(res.visitedViews);
|
||||
tagsViewStore.toLastView(res.visitedViews);
|
||||
}
|
||||
});
|
||||
}
|
||||
function closeRightTags() {
|
||||
tagsViewStore.delRightViews(selectedTag.value).then((res: any) => {
|
||||
if (!res.visitedViews.find((item: any) => item.path === route.path)) {
|
||||
toLastView(res.visitedViews);
|
||||
tagsViewStore.toLastView(res.visitedViews);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -276,7 +256,7 @@ function closeOtherTags() {
|
||||
|
||||
function closeAllTags(view: TagView) {
|
||||
tagsViewStore.delAllViews().then((res: any) => {
|
||||
toLastView(res.visitedViews, view);
|
||||
tagsViewStore.toLastView(res.visitedViews, view);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -32,14 +32,14 @@ export const constantRoutes: RouteRecordRaw[] = [
|
||||
{
|
||||
path: "dashboard",
|
||||
component: () => import("@/views/dashboard/index.vue"),
|
||||
name: "Dashboard", // 用于 keep-alive, 必须与SFC自动推导或者显示声明的组件name一致
|
||||
// https://cn.vuejs.org/guide/built-ins/keep-alive.html#include-exclude
|
||||
// 用于 keep-alive 功能,需要与 SFC 中自动推导或显式声明的组件名称一致
|
||||
// 参考文档: https://cn.vuejs.org/guide/built-ins/keep-alive.html#include-exclude
|
||||
name: "Dashboard",
|
||||
meta: {
|
||||
title: "dashboard",
|
||||
icon: "homepage",
|
||||
affix: true,
|
||||
keepAlive: true,
|
||||
alwaysShow: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -7,19 +7,23 @@ import { store } from "@/store";
|
||||
import { DeviceEnum } from "@/enums/DeviceEnum";
|
||||
import { SidebarStatusEnum } from "@/enums/SidebarStatusEnum";
|
||||
|
||||
// setup
|
||||
export const useAppStore = defineStore("app", () => {
|
||||
// state
|
||||
// 设备类型
|
||||
const device = useStorage("device", DeviceEnum.DESKTOP);
|
||||
// 布局大小
|
||||
const size = useStorage("size", defaultSettings.size);
|
||||
// 语言
|
||||
const language = useStorage("language", defaultSettings.language);
|
||||
// 侧边栏状态
|
||||
const sidebarStatus = useStorage("sidebarStatus", SidebarStatusEnum.CLOSED);
|
||||
|
||||
const sidebar = reactive({
|
||||
opened: sidebarStatus.value === SidebarStatusEnum.OPENED,
|
||||
withoutAnimation: false,
|
||||
});
|
||||
|
||||
// 顶部菜单激活路径
|
||||
const activeTopMenuPath = useStorage("activeTopMenuPath", "");
|
||||
|
||||
/**
|
||||
* 根据语言标识读取对应的语言包
|
||||
*/
|
||||
@@ -31,7 +35,7 @@ export const useAppStore = defineStore("app", () => {
|
||||
}
|
||||
});
|
||||
|
||||
// actions
|
||||
// 切换侧边栏
|
||||
function toggleSidebar() {
|
||||
sidebar.opened = !sidebar.opened;
|
||||
sidebarStatus.value = sidebar.opened
|
||||
@@ -39,20 +43,28 @@ export const useAppStore = defineStore("app", () => {
|
||||
: SidebarStatusEnum.CLOSED;
|
||||
}
|
||||
|
||||
// 关闭侧边栏
|
||||
function closeSideBar() {
|
||||
sidebar.opened = false;
|
||||
sidebarStatus.value = SidebarStatusEnum.CLOSED;
|
||||
}
|
||||
|
||||
// 打开侧边栏
|
||||
function openSideBar() {
|
||||
sidebar.opened = true;
|
||||
sidebarStatus.value = SidebarStatusEnum.OPENED;
|
||||
}
|
||||
|
||||
// 切换设备
|
||||
function toggleDevice(val: string) {
|
||||
device.value = val;
|
||||
}
|
||||
|
||||
/**
|
||||
* 改变布局大小
|
||||
*
|
||||
* @param val 布局大小 default | small | large
|
||||
*/
|
||||
function changeSize(val: string) {
|
||||
size.value = val;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { RouteRecordRaw } from "vue-router";
|
||||
import { constantRoutes } from "@/router";
|
||||
import { store } from "@/store";
|
||||
import MenuAPI from "@/api/menu";
|
||||
import { RouteVO } from "@/api/menu/model";
|
||||
import MenuAPI, { RouteVO } from "@/api/menu";
|
||||
|
||||
const modules = import.meta.glob("../../views/**/**.vue");
|
||||
const Layout = () => import("@/layout/index.vue");
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
export const useTagsViewStore = defineStore("tagsView", () => {
|
||||
const visitedViews = ref<TagView[]>([]);
|
||||
const cachedViews = ref<string[]>([]);
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
/**
|
||||
* 添加已访问视图到已访问视图列表中
|
||||
*/
|
||||
@@ -189,6 +190,44 @@ export const useTagsViewStore = defineStore("tagsView", () => {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭当前tagView
|
||||
*/
|
||||
function closeCurrentView() {
|
||||
const tags: TagView = {
|
||||
name: route.name as string,
|
||||
title: route.meta.title as string,
|
||||
path: route.path,
|
||||
fullPath: route.fullPath,
|
||||
affix: route.meta?.affix,
|
||||
keepAlive: route.meta?.keepAlive,
|
||||
query: route.query,
|
||||
};
|
||||
delView(tags).then((res: any) => {
|
||||
if (isActive(tags)) {
|
||||
toLastView(res.visitedViews, tags);
|
||||
}
|
||||
});
|
||||
}
|
||||
function isActive(tag: TagView) {
|
||||
return tag.path === route.path;
|
||||
}
|
||||
|
||||
function toLastView(visitedViews: TagView[], view?: TagView) {
|
||||
const latestView = visitedViews.slice(-1)[0];
|
||||
if (latestView && latestView.fullPath) {
|
||||
router.push(latestView.fullPath);
|
||||
} else {
|
||||
// now the default is to redirect to the home page if there is no tags-view,
|
||||
// you can adjust it according to your needs.
|
||||
if (view?.name === "Dashboard") {
|
||||
// to reload home page
|
||||
router.replace("/redirect" + view.fullPath);
|
||||
} else {
|
||||
router.push("/");
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
visitedViews,
|
||||
cachedViews,
|
||||
@@ -207,5 +246,8 @@ export const useTagsViewStore = defineStore("tagsView", () => {
|
||||
delAllViews,
|
||||
delAllVisitedViews,
|
||||
delAllCachedViews,
|
||||
closeCurrentView,
|
||||
isActive,
|
||||
toLastView,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -3,8 +3,8 @@ import UserAPI from "@/api/user";
|
||||
import { resetRouter } from "@/router";
|
||||
import { store } from "@/store";
|
||||
|
||||
import { LoginData } from "@/api/auth/model";
|
||||
import { UserInfo } from "@/api/user/model";
|
||||
import { LoginData } from "@/api/auth";
|
||||
import { UserInfo } from "@/api/user";
|
||||
import { TOKEN_KEY } from "@/enums/CacheEnum";
|
||||
|
||||
export const useUserStore = defineStore("user", () => {
|
||||
|
||||
81
src/styles/login.scss
Normal file
81
src/styles/login.scss
Normal file
@@ -0,0 +1,81 @@
|
||||
.login-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
background: url("@/assets/images/login-background-light.jpg") no-repeat center
|
||||
right;
|
||||
|
||||
.top-bar {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.login-card {
|
||||
width: 400px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: 4%;
|
||||
|
||||
@media (width <= 640px) {
|
||||
width: 340px;
|
||||
}
|
||||
|
||||
.input-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.captcha-image {
|
||||
height: 48px;
|
||||
cursor: pointer;
|
||||
border-top-right-radius: 6px;
|
||||
border-bottom-right-radius: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.icp-info {
|
||||
position: absolute;
|
||||
bottom: 4px;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.el-form-item {
|
||||
background: var(--el-input-bg-color);
|
||||
border: 1px solid var(--el-border-color);
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.el-input {
|
||||
.el-input__wrapper {
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
box-shadow: none;
|
||||
|
||||
&.is-focus,
|
||||
&:hover {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
input:-webkit-autofill {
|
||||
/* 通过延时渲染背景色变相去除背景颜色 */
|
||||
transition: background-color 1000s ease-in-out 0s;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
html.dark .login-container {
|
||||
background: url("@/assets/images/login-background-dark.jpg") no-repeat center
|
||||
right;
|
||||
}
|
||||
@@ -13,6 +13,7 @@ declare module "vue" {
|
||||
Breadcrumb: (typeof import("./../components/Breadcrumb/index.vue"))["default"];
|
||||
CURD: (typeof import("./../components/CURD/index.vue"))["default"];
|
||||
DeptTree: (typeof import("./../views/system/user/components/dept-tree.vue"))["default"];
|
||||
UserImport: (typeof import("./../views/system/user/components/user-import.vue"))["default"];
|
||||
Dictionary: (typeof import("./../components/Dictionary/index.vue"))["default"];
|
||||
DictItem: (typeof import("./../views/system/dict/components/dict-item.vue"))["default"];
|
||||
ElBacktop: (typeof import("element-plus/es"))["ElBacktop"];
|
||||
60
src/types/router.d.ts
vendored
Normal file
60
src/types/router.d.ts
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
import "vue-router";
|
||||
|
||||
declare module "vue-router" {
|
||||
// https://router.vuejs.org/zh/guide/advanced/meta.html#typescript
|
||||
// 可以通过扩展 RouteMeta 接口来输入 meta 字段
|
||||
interface RouteMeta {
|
||||
/**
|
||||
* 菜单名称
|
||||
* @example 'Dashboard'
|
||||
*/
|
||||
title?: string;
|
||||
|
||||
/**
|
||||
* 菜单图标
|
||||
* @example 'el-icon-edit'
|
||||
*/
|
||||
icon?: string;
|
||||
|
||||
/**
|
||||
* 是否隐藏菜单项
|
||||
* true 隐藏, false 显示
|
||||
* @default false
|
||||
*/
|
||||
hidden?: boolean;
|
||||
|
||||
/**
|
||||
* 始终显示父级菜单,即使只有一个子菜单
|
||||
* true 显示父级菜单, false 隐藏父级菜单,显示唯一子节点
|
||||
* @default false
|
||||
*/
|
||||
alwaysShow?: boolean;
|
||||
|
||||
/**
|
||||
* 是否固定在页签上
|
||||
* true 固定, false 不固定
|
||||
* @default false
|
||||
*/
|
||||
affix?: boolean;
|
||||
|
||||
/**
|
||||
* 是否缓存页面
|
||||
* true 缓存, false 不缓存
|
||||
* @default false
|
||||
*/
|
||||
keepAlive?: boolean;
|
||||
|
||||
/**
|
||||
* 是否在面包屑导航中隐藏
|
||||
* true 隐藏, false 显示
|
||||
* @default false
|
||||
*/
|
||||
breadcrumb?: boolean;
|
||||
|
||||
/**
|
||||
* 拥有访问该菜单权限的角色编码集合
|
||||
* @example ['admin', 'editor']
|
||||
*/
|
||||
roles?: string[];
|
||||
}
|
||||
}
|
||||
24
src/typings/router.d.ts
vendored
24
src/typings/router.d.ts
vendored
@@ -1,24 +0,0 @@
|
||||
import "vue-router";
|
||||
|
||||
declare module "vue-router" {
|
||||
// https://router.vuejs.org/zh/guide/advanced/meta.html#typescript
|
||||
// 可以通过扩展 RouteMeta 接口来输入 meta 字段
|
||||
interface RouteMeta {
|
||||
/** 菜单名称 */
|
||||
title?: string;
|
||||
/** 菜单图标 */
|
||||
icon?: string;
|
||||
/** 菜单是否隐藏 */
|
||||
hidden?: boolean;
|
||||
/** 只有一个子路由是否始终显示 */
|
||||
alwaysShow?: boolean;
|
||||
/** 是否固定页签 */
|
||||
affix?: boolean;
|
||||
/** 是否缓存页面 */
|
||||
keepAlive?: boolean;
|
||||
/** 是否在面包屑上隐藏 */
|
||||
breadcrumb?: boolean;
|
||||
/** 拥有菜单权限的角色编码集合 */
|
||||
roles?: string[];
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import DeptAPI from "@/api/dept";
|
||||
import RoleAPI from "@/api/role";
|
||||
import UserAPI from "@/api/user";
|
||||
import type { UserForm } from "@/api/user/model";
|
||||
import UserAPI, { UserForm } from "@/api/user";
|
||||
import type { IModalConfig } from "@/components/CURD/types";
|
||||
|
||||
const modalConfig: IModalConfig<UserForm> = {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import UserAPI from "@/api/user";
|
||||
import RoleAPI from "@/api/role";
|
||||
import type { UserQuery } from "@/api/user/model";
|
||||
import type { UserPageQuery } from "@/api/user";
|
||||
import type { IContentConfig } from "@/components/CURD/types";
|
||||
|
||||
const contentConfig: IContentConfig<UserQuery> = {
|
||||
const contentConfig: IContentConfig<UserPageQuery> = {
|
||||
pageName: "sys:user",
|
||||
table: {
|
||||
border: true,
|
||||
@@ -25,8 +25,11 @@ const contentConfig: IContentConfig<UserQuery> = {
|
||||
return UserAPI.getPage(params);
|
||||
},
|
||||
deleteAction: UserAPI.deleteByIds,
|
||||
importAction(file) {
|
||||
return UserAPI.import(1, file);
|
||||
},
|
||||
exportAction: UserAPI.export,
|
||||
importsTemplate: UserAPI.downloadTemplate,
|
||||
importTemplate: UserAPI.downloadTemplate,
|
||||
importsAction(data) {
|
||||
// 模拟导入数据
|
||||
console.log("importsAction", data);
|
||||
@@ -42,13 +45,8 @@ const contentConfig: IContentConfig<UserQuery> = {
|
||||
toolbar: [
|
||||
"add",
|
||||
"delete",
|
||||
"import",
|
||||
"export",
|
||||
{
|
||||
name: "import",
|
||||
icon: "upload",
|
||||
text: "导入",
|
||||
auth: "import",
|
||||
},
|
||||
{
|
||||
name: "custom1",
|
||||
icon: "plus",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import DeptAPI from "@/api/dept";
|
||||
import RoleAPI from "@/api/role";
|
||||
import UserAPI from "@/api/user";
|
||||
import type { UserForm } from "@/api/user/model";
|
||||
import type { UserForm } from "@/api/user";
|
||||
import type { IModalConfig } from "@/components/CURD/types";
|
||||
import { DeviceEnum } from "@/enums/DeviceEnum";
|
||||
import { useAppStore } from "@/store";
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
@submit-click="handleSubmitClick"
|
||||
>
|
||||
<template #gender="scope">
|
||||
<dictionary v-model="scope.formData[scope.prop]" type-code="gender" />
|
||||
<dictionary v-model="scope.formData[scope.prop]" code="gender" />
|
||||
</template>
|
||||
</page-modal>
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
@submit-click="handleSubmitClick"
|
||||
>
|
||||
<template #gender="scope">
|
||||
<dictionary v-model="scope.formData[scope.prop]" type-code="gender" />
|
||||
<dictionary v-model="scope.formData[scope.prop]" code="gender" />
|
||||
</template>
|
||||
</page-modal>
|
||||
</template>
|
||||
@@ -113,8 +113,8 @@ async function handleEditClick(row: IObject) {
|
||||
// 其他工具栏
|
||||
function handleToolbarClick(name: string) {
|
||||
console.log(name);
|
||||
if (name === "import") {
|
||||
ElMessage.success("点击了导入按钮");
|
||||
if (name === "custom1") {
|
||||
ElMessage.success("点击了自定义1按钮");
|
||||
}
|
||||
}
|
||||
// 其他操作列
|
||||
|
||||
@@ -15,14 +15,14 @@ const numberValue = ref(1); // 性别(值为Number)
|
||||
>
|
||||
<el-form>
|
||||
<el-form-item label="性别">
|
||||
<dictionary v-model="stringValue" type-code="gender" />
|
||||
<dictionary v-model="stringValue" code="gender" />
|
||||
<el-link :underline="false" type="primary" class="ml-5"
|
||||
>值为String: const value = ref("1");
|
||||
</el-link>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="性别">
|
||||
<dictionary v-model="numberValue" type-code="gender" />
|
||||
<dictionary v-model="numberValue" code="gender" />
|
||||
<el-link :underline="false" type="success" class="ml-5"
|
||||
>值为Number: const value = ref(1);
|
||||
</el-link>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="login-container">
|
||||
<!-- 顶部 -->
|
||||
<div class="absolute-lt flex-x-end p-3 w-full">
|
||||
<!-- 顶部工具栏 -->
|
||||
<div class="top-bar">
|
||||
<el-switch
|
||||
v-model="isDark"
|
||||
inline-prompt
|
||||
@@ -12,7 +12,7 @@
|
||||
<lang-select class="ml-2 cursor-pointer" />
|
||||
</div>
|
||||
<!-- 登录表单 -->
|
||||
<el-card class="!border-none !bg-transparent !rounded-4% w-100 <sm:w-85">
|
||||
<el-card class="login-card">
|
||||
<div class="text-center relative">
|
||||
<h2>{{ defaultSettings.title }}</h2>
|
||||
<el-tag class="ml-2 absolute-rt">{{ defaultSettings.version }}</el-tag>
|
||||
@@ -26,7 +26,7 @@
|
||||
>
|
||||
<!-- 用户名 -->
|
||||
<el-form-item prop="username">
|
||||
<div class="flex-y-center w-full">
|
||||
<div class="input-wrapper">
|
||||
<svg-icon icon-class="user" class="mx-2" />
|
||||
<el-input
|
||||
ref="username"
|
||||
@@ -46,7 +46,7 @@
|
||||
placement="right"
|
||||
>
|
||||
<el-form-item prop="password">
|
||||
<div class="flex-y-center w-full">
|
||||
<div class="input-wrapper">
|
||||
<svg-icon icon-class="lock" class="mx-2" />
|
||||
<el-input
|
||||
v-model="loginData.password"
|
||||
@@ -54,7 +54,7 @@
|
||||
type="password"
|
||||
name="password"
|
||||
@keyup="checkCapslock"
|
||||
@keyup.enter="handleLogin"
|
||||
@keyup.enter="handleLoginSubmit"
|
||||
size="large"
|
||||
class="h-[48px] pr-2"
|
||||
show-password
|
||||
@@ -65,7 +65,7 @@
|
||||
|
||||
<!-- 验证码 -->
|
||||
<el-form-item prop="captchaCode">
|
||||
<div class="flex-y-center w-full">
|
||||
<div class="input-wrapper">
|
||||
<svg-icon icon-class="captcha" class="mx-2" />
|
||||
<el-input
|
||||
v-model="loginData.captchaCode"
|
||||
@@ -73,13 +73,13 @@
|
||||
size="large"
|
||||
class="flex-1"
|
||||
:placeholder="$t('login.captchaCode')"
|
||||
@keyup.enter="handleLogin"
|
||||
@keyup.enter="handleLoginSubmit"
|
||||
/>
|
||||
|
||||
<el-image
|
||||
@click="getCaptcha"
|
||||
:src="captchaBase64"
|
||||
class="rounded-tr-md rounded-br-md cursor-pointer h-[48px]"
|
||||
class="captcha-image"
|
||||
/>
|
||||
</div>
|
||||
</el-form-item>
|
||||
@@ -90,7 +90,7 @@
|
||||
type="primary"
|
||||
size="large"
|
||||
class="w-full"
|
||||
@click.prevent="handleLogin"
|
||||
@click.prevent="handleLoginSubmit"
|
||||
>{{ $t("login.login") }}
|
||||
</el-button>
|
||||
|
||||
@@ -103,7 +103,7 @@
|
||||
</el-card>
|
||||
|
||||
<!-- ICP备案 -->
|
||||
<div class="absolute bottom-1 text-[10px] text-center" v-show="icpVisible">
|
||||
<div class="icp-info" v-show="icpVisible">
|
||||
<p>
|
||||
Copyright © 2021 - 2024 youlai.tech All Rights Reserved. 有来技术
|
||||
版权所有
|
||||
@@ -114,35 +114,48 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useSettingsStore, useUserStore } from "@/store";
|
||||
import AuthAPI from "@/api/auth";
|
||||
import { LoginData } from "@/api/auth/model";
|
||||
import type { FormInstance } from "element-plus";
|
||||
// 外部库和依赖
|
||||
import { LocationQuery, useRoute } from "vue-router";
|
||||
|
||||
// 内部依赖
|
||||
import { useSettingsStore, useUserStore } from "@/store";
|
||||
import AuthAPI, { LoginData } from "@/api/auth";
|
||||
import router from "@/router";
|
||||
import defaultSettings from "@/settings";
|
||||
import { ThemeEnum } from "@/enums/ThemeEnum";
|
||||
|
||||
// 类型定义
|
||||
import type { FormInstance } from "element-plus";
|
||||
|
||||
// 导入 login.scss 文件
|
||||
import "@/styles/login.scss";
|
||||
|
||||
// 使用导入的依赖和库
|
||||
const userStore = useUserStore();
|
||||
const settingsStore = useSettingsStore();
|
||||
const route = useRoute();
|
||||
|
||||
// Internationalization
|
||||
// 窗口高度
|
||||
const { height } = useWindowSize();
|
||||
// 国际化 Internationalization
|
||||
const { t } = useI18n();
|
||||
|
||||
// Reactive states
|
||||
// 是否暗黑模式
|
||||
const isDark = ref(settingsStore.theme === ThemeEnum.DARK);
|
||||
// 是否显示 ICP 备案信息
|
||||
const icpVisible = ref(true);
|
||||
const loading = ref(false); // 按钮loading
|
||||
const isCapslock = ref(false); // 是否大写锁定
|
||||
const captchaBase64 = ref(); // 验证码图片Base64字符串
|
||||
const loginFormRef = ref<FormInstance>(); // 登录表单ref
|
||||
const { height } = useWindowSize();
|
||||
// 按钮 loading 状态
|
||||
const loading = ref(false);
|
||||
// 是否大写锁定
|
||||
const isCapslock = ref(false);
|
||||
// 验证码图片Base64字符串
|
||||
const captchaBase64 = ref();
|
||||
// 登录表单ref
|
||||
const loginFormRef = ref<FormInstance>();
|
||||
|
||||
const loginData = ref<LoginData>({
|
||||
username: "admin",
|
||||
password: "123456",
|
||||
});
|
||||
} as LoginData);
|
||||
|
||||
const loginRules = computed(() => {
|
||||
return {
|
||||
@@ -183,8 +196,8 @@ function getCaptcha() {
|
||||
});
|
||||
}
|
||||
|
||||
// 登录
|
||||
function handleLogin() {
|
||||
/** 登录表单提交 */
|
||||
function handleLoginSubmit() {
|
||||
loginFormRef.value?.validate((valid: boolean) => {
|
||||
if (valid) {
|
||||
loading.value = true;
|
||||
@@ -204,7 +217,7 @@ function handleLogin() {
|
||||
});
|
||||
}
|
||||
|
||||
// 解析 redirect 字符串 为 path 和 queryParams
|
||||
/** 解析 redirect 字符串 为 path 和 queryParams */
|
||||
function parseRedirect(): {
|
||||
path: string;
|
||||
queryParams: Record<string, string>;
|
||||
@@ -252,43 +265,4 @@ onMounted(() => {
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
html.dark .login-container {
|
||||
background: url("@/assets/images/login-bg-dark.jpg") no-repeat center right;
|
||||
}
|
||||
|
||||
.login-container {
|
||||
overflow-y: auto;
|
||||
background: url("@/assets/images/login-bg.jpg") no-repeat center right;
|
||||
|
||||
@apply wh-full flex-center;
|
||||
|
||||
.login-form {
|
||||
padding: 30px 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.el-form-item {
|
||||
background: var(--el-input-bg-color);
|
||||
border: 1px solid var(--el-border-color);
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
:deep(.el-input) {
|
||||
.el-input__wrapper {
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
box-shadow: none;
|
||||
|
||||
&.is-focus,
|
||||
&:hover {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
input:-webkit-autofill {
|
||||
/* 通过延时渲染背景色变相去除背景颜色 */
|
||||
transition: background-color 1000s ease-in-out 0s;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped></style>
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
<el-button
|
||||
v-hasPerm="['sys:dept:add']"
|
||||
type="success"
|
||||
@click="openDialog(0, undefined)"
|
||||
@click="handleOpenDialog(0, undefined)"
|
||||
><i-ep-plus />新增</el-button
|
||||
>
|
||||
<el-button
|
||||
@@ -60,6 +60,7 @@
|
||||
>
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column prop="name" label="部门名称" min-width="200" />
|
||||
<el-table-column prop="code" label="部门编号" width="200" />
|
||||
<el-table-column prop="status" label="状态" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.status == 1" type="success">正常</el-tag>
|
||||
@@ -76,7 +77,7 @@
|
||||
type="primary"
|
||||
link
|
||||
size="small"
|
||||
@click.stop="openDialog(scope.row.id, undefined)"
|
||||
@click.stop="handleOpenDialog(scope.row.id, undefined)"
|
||||
><i-ep-plus />新增
|
||||
</el-button>
|
||||
<el-button
|
||||
@@ -84,12 +85,12 @@
|
||||
type="primary"
|
||||
link
|
||||
size="small"
|
||||
@click.stop="openDialog(scope.row.parentId, scope.row.id)"
|
||||
@click.stop="handleOpenDialog(scope.row.parentId, scope.row.id)"
|
||||
><i-ep-edit />编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
v-hasPerm="['sys:dept:delete']"
|
||||
type="primary"
|
||||
type="danger"
|
||||
link
|
||||
size="small"
|
||||
@click.stop="handleDelete(scope.row.id)"
|
||||
@@ -105,7 +106,7 @@
|
||||
v-model="dialog.visible"
|
||||
:title="dialog.title"
|
||||
width="600px"
|
||||
@closed="closeDialog"
|
||||
@closed="handleCloseDialog"
|
||||
>
|
||||
<el-form
|
||||
ref="deptFormRef"
|
||||
@@ -126,6 +127,9 @@
|
||||
<el-form-item label="部门名称" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入部门名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="部门编号" prop="code">
|
||||
<el-input v-model="formData.code" placeholder="请输入部门编号" />
|
||||
</el-form-item>
|
||||
<el-form-item label="显示排序" prop="sort">
|
||||
<el-input-number
|
||||
v-model="formData.sort"
|
||||
@@ -145,7 +149,7 @@
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="handleSubmit"> 确 定 </el-button>
|
||||
<el-button @click="closeDialog"> 取 消 </el-button>
|
||||
<el-button @click="handleCloseDialog"> 取 消 </el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
@@ -158,8 +162,7 @@ defineOptions({
|
||||
inheritAttrs: false,
|
||||
});
|
||||
|
||||
import DeptAPI from "@/api/dept";
|
||||
import { DeptVO, DeptForm, DeptQuery } from "@/api/dept/model";
|
||||
import DeptAPI, { DeptVO, DeptForm, DeptQuery } from "@/api/dept";
|
||||
|
||||
const queryFormRef = ref(ElForm);
|
||||
const deptFormRef = ref(ElForm);
|
||||
@@ -183,12 +186,15 @@ const formData = reactive<DeptForm>({
|
||||
});
|
||||
|
||||
const rules = reactive({
|
||||
parentId: [{ required: true, message: "上级部门不能为空", trigger: "blur" }],
|
||||
parentId: [
|
||||
{ required: true, message: "上级部门不能为空", trigger: "change" },
|
||||
],
|
||||
name: [{ required: true, message: "部门名称不能为空", trigger: "blur" }],
|
||||
code: [{ required: true, message: "部门编号不能为空", trigger: "blur" }],
|
||||
sort: [{ required: true, message: "显示排序不能为空", trigger: "blur" }],
|
||||
});
|
||||
|
||||
/** 查询 */
|
||||
/** 查询部门 */
|
||||
function handleQuery() {
|
||||
loading.value = true;
|
||||
DeptAPI.getList(queryParams).then((data) => {
|
||||
@@ -197,7 +203,7 @@ function handleQuery() {
|
||||
});
|
||||
}
|
||||
|
||||
/**重置查询 */
|
||||
/** 重置查询 */
|
||||
function handleResetQuery() {
|
||||
queryFormRef.value.resetFields();
|
||||
handleQuery();
|
||||
@@ -208,27 +214,23 @@ function handleSelectionChange(selection: any) {
|
||||
ids.value = selection.map((item: any) => item.id);
|
||||
}
|
||||
|
||||
/** 获取部门下拉数据 */
|
||||
async function loadDeptOptions() {
|
||||
DeptAPI.getOptions().then((data) => {
|
||||
deptOptions.value = [
|
||||
{
|
||||
value: 0,
|
||||
label: "顶级部门",
|
||||
children: data,
|
||||
},
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开弹窗
|
||||
* 打开部门弹窗
|
||||
*
|
||||
* @param parentId 父部门ID
|
||||
* @param deptId 部门ID
|
||||
*/
|
||||
async function openDialog(parentId?: number, deptId?: number) {
|
||||
await loadDeptOptions();
|
||||
async function handleOpenDialog(parentId?: number, deptId?: number) {
|
||||
// 加载部门下拉数据
|
||||
const data = await DeptAPI.getOptions();
|
||||
deptOptions.value = [
|
||||
{
|
||||
value: 0,
|
||||
label: "顶级部门",
|
||||
children: data,
|
||||
},
|
||||
];
|
||||
|
||||
dialog.visible = true;
|
||||
if (deptId) {
|
||||
dialog.title = "修改部门";
|
||||
@@ -241,17 +243,17 @@ async function openDialog(parentId?: number, deptId?: number) {
|
||||
}
|
||||
}
|
||||
|
||||
/** 表单提交 */
|
||||
/** 提交部门表单 */
|
||||
function handleSubmit() {
|
||||
deptFormRef.value.validate((valid: any) => {
|
||||
if (valid) {
|
||||
const deptId = formData.id;
|
||||
loading.value = true;
|
||||
const deptId = formData.id;
|
||||
if (deptId) {
|
||||
DeptAPI.update(deptId, formData)
|
||||
.then(() => {
|
||||
ElMessage.success("修改成功");
|
||||
closeDialog();
|
||||
handleCloseDialog();
|
||||
handleQuery();
|
||||
})
|
||||
.finally(() => (loading.value = false));
|
||||
@@ -259,7 +261,7 @@ function handleSubmit() {
|
||||
DeptAPI.add(formData)
|
||||
.then(() => {
|
||||
ElMessage.success("新增成功");
|
||||
closeDialog();
|
||||
handleCloseDialog();
|
||||
handleQuery();
|
||||
})
|
||||
.finally(() => (loading.value = false));
|
||||
@@ -281,22 +283,26 @@ function handleDelete(deptId?: number) {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}).then(() => {
|
||||
DeptAPI.deleteByIds(deptIds).then(() => {
|
||||
ElMessage.success("删除成功");
|
||||
handleResetQuery();
|
||||
});
|
||||
});
|
||||
}).then(
|
||||
() => {
|
||||
loading.value = true;
|
||||
DeptAPI.deleteByIds(deptIds)
|
||||
.then(() => {
|
||||
ElMessage.success("删除成功");
|
||||
handleResetQuery();
|
||||
})
|
||||
.finally(() => (loading.value = false));
|
||||
},
|
||||
() => {
|
||||
ElMessage.info("已取消删除");
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/** 关闭弹窗 */
|
||||
function closeDialog() {
|
||||
function handleCloseDialog() {
|
||||
dialog.visible = false;
|
||||
resetForm();
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
function resetForm() {
|
||||
deptFormRef.value.resetFields();
|
||||
deptFormRef.value.clearValidate();
|
||||
|
||||
|
||||
@@ -1,308 +0,0 @@
|
||||
<!-- 字典数据 -->
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<div class="search-container">
|
||||
<!-- 搜索表单 -->
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<el-form-item label="关键字" prop="name">
|
||||
<el-input
|
||||
v-model="queryParams.name"
|
||||
placeholder="字典名称"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleQuery"
|
||||
><i-ep-search />搜索</el-button
|
||||
>
|
||||
<el-button @click="handleResetQuery"> <i-ep-refresh />重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<el-card shadow="never">
|
||||
<template #header>
|
||||
<el-button
|
||||
v-hasPerm="['sys:dict:add']"
|
||||
type="success"
|
||||
@click="openDialog()"
|
||||
><i-ep-plus />新增</el-button
|
||||
>
|
||||
<el-button
|
||||
v-hasPerm="['sys:dict:delete']"
|
||||
type="danger"
|
||||
:disabled="ids.length === 0"
|
||||
@click="handleDelete()"
|
||||
><i-ep-delete />删除</el-button
|
||||
>
|
||||
</template>
|
||||
|
||||
<!-- 数据表格 -->
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="dictList"
|
||||
border
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="50" />
|
||||
<el-table-column label="字典名称" prop="name" />
|
||||
<el-table-column label="字典值" prop="value" />
|
||||
<el-table-column label="状态" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.status === 1" type="success">启用</el-tag>
|
||||
<el-tag v-else type="info">禁用</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column fixed="right" label="操作" align="center">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
v-hasPerm="['sys:dict:edit']"
|
||||
type="primary"
|
||||
link
|
||||
@click="openDialog(scope.row.id)"
|
||||
><i-ep-edit />编辑</el-button
|
||||
>
|
||||
<el-button
|
||||
v-hasPerm="['sys:dict:delete']"
|
||||
type="primary"
|
||||
link
|
||||
@click.stop="handleDelete(scope.row.id)"
|
||||
><i-ep-delete />删除</el-button
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-if="total > 0"
|
||||
v-model:total="total"
|
||||
v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="handleQuery"
|
||||
/>
|
||||
</el-card>
|
||||
|
||||
<!-- 表单弹窗 -->
|
||||
<el-dialog
|
||||
v-model="dialog.visible"
|
||||
:title="dialog.title"
|
||||
width="500px"
|
||||
@close="closeDialog"
|
||||
>
|
||||
<el-form
|
||||
ref="dataFormRef"
|
||||
:model="formData"
|
||||
:rules="rules"
|
||||
label-width="100px"
|
||||
>
|
||||
<el-form-item label="字典名称">{{ typeName }}</el-form-item>
|
||||
<el-form-item label="字典名称" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入字典名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="字典值" prop="value">
|
||||
<el-input v-model="formData.value" placeholder="字典值" />
|
||||
</el-form-item>
|
||||
<el-form-item label="排序" prop="sort">
|
||||
<el-input-number
|
||||
v-model="formData.sort"
|
||||
controls-position="right"
|
||||
:min="0"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-radio-group v-model="formData.status">
|
||||
<el-radio :label="1">正常</el-radio>
|
||||
<el-radio :label="0">停用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="formData.remark" type="textarea" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="handleSubmit">确 定</el-button>
|
||||
<el-button @click="closeDialog">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineOptions({
|
||||
name: "DictData",
|
||||
inheritAttrs: false,
|
||||
});
|
||||
|
||||
import DictAPI from "@/api/dict";
|
||||
import { DictPageVO, DictForm, DictQuery } from "@/api/dict/model";
|
||||
|
||||
const props = defineProps({
|
||||
typeCode: {
|
||||
type: String,
|
||||
default: () => {
|
||||
return "";
|
||||
},
|
||||
},
|
||||
typeName: {
|
||||
type: String,
|
||||
default: () => {
|
||||
return "";
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.typeCode,
|
||||
(newVal: string) => {
|
||||
queryParams.typeCode = newVal;
|
||||
formData.typeCode = newVal;
|
||||
handleResetQuery();
|
||||
}
|
||||
);
|
||||
|
||||
const queryFormRef = ref(ElForm);
|
||||
const dataFormRef = ref(ElForm);
|
||||
|
||||
const loading = ref(false);
|
||||
const ids = ref<number[]>([]);
|
||||
const total = ref(0);
|
||||
|
||||
const queryParams = reactive<DictQuery>({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
typeCode: props.typeCode,
|
||||
});
|
||||
|
||||
const dictList = ref<DictPageVO[]>();
|
||||
|
||||
const dialog = reactive({
|
||||
title: "",
|
||||
visible: false,
|
||||
});
|
||||
|
||||
const formData = reactive<DictForm>({
|
||||
status: 1,
|
||||
typeCode: props.typeCode,
|
||||
sort: 1,
|
||||
});
|
||||
|
||||
const rules = reactive({
|
||||
name: [{ required: true, message: "请输入字典名称", trigger: "blur" }],
|
||||
value: [{ required: true, message: "请输入字典值", trigger: "blur" }],
|
||||
});
|
||||
|
||||
/** 查询 */
|
||||
function handleQuery() {
|
||||
if (queryParams.typeCode) {
|
||||
loading.value = true;
|
||||
DictAPI.getDictPage(queryParams)
|
||||
.then((data) => {
|
||||
dictList.value = data.list;
|
||||
total.value = data.total;
|
||||
})
|
||||
.finally(() => (loading.value = false));
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置查询 */
|
||||
function handleResetQuery() {
|
||||
queryFormRef.value.resetFields();
|
||||
queryParams.pageNum = 1;
|
||||
handleQuery();
|
||||
}
|
||||
|
||||
/**
|
||||
* 行checkbox change事件
|
||||
*
|
||||
* @param selection
|
||||
*/
|
||||
function handleSelectionChange(selection: any) {
|
||||
ids.value = selection.map((item: any) => item.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开字典表单弹窗
|
||||
*
|
||||
* @param dictId 字典ID
|
||||
*/
|
||||
function openDialog(dictId?: number) {
|
||||
dialog.visible = true;
|
||||
if (dictId) {
|
||||
dialog.title = "修改字典";
|
||||
DictAPI.getDictFormData(dictId).then((data) => {
|
||||
Object.assign(formData, data);
|
||||
});
|
||||
} else {
|
||||
dialog.title = "新增字典";
|
||||
}
|
||||
}
|
||||
|
||||
/** 字典表单提交 */
|
||||
function handleSubmit() {
|
||||
dataFormRef.value.validate((isValid: boolean) => {
|
||||
if (isValid) {
|
||||
loading.value = false;
|
||||
const dictId = formData.id;
|
||||
if (dictId) {
|
||||
DictAPI.updateDict(dictId, formData)
|
||||
.then(() => {
|
||||
ElMessage.success("修改成功");
|
||||
closeDialog();
|
||||
handleResetQuery();
|
||||
})
|
||||
.finally(() => (loading.value = false));
|
||||
} else {
|
||||
DictAPI.addDict(formData)
|
||||
.then(() => {
|
||||
ElMessage.success("新增成功");
|
||||
closeDialog();
|
||||
handleResetQuery();
|
||||
})
|
||||
.finally(() => (loading.value = false));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** 关闭弹窗 */
|
||||
function closeDialog() {
|
||||
dialog.visible = false;
|
||||
resetForm();
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
function resetForm() {
|
||||
dataFormRef.value.resetFields();
|
||||
dataFormRef.value.clearValidate();
|
||||
|
||||
formData.id = undefined;
|
||||
formData.status = 1;
|
||||
formData.sort = 1;
|
||||
formData.typeCode = props.typeCode;
|
||||
}
|
||||
|
||||
/** 删除字典 */
|
||||
function handleDelete(dictId?: number) {
|
||||
const dictIds = [dictId || ids.value].join(",");
|
||||
if (!dictIds) {
|
||||
ElMessage.warning("请勾选删除项");
|
||||
return;
|
||||
}
|
||||
|
||||
ElMessageBox.confirm("确认删除已选中的数据项?", "警告", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}).then(() => {
|
||||
DictAPI.deleteDictByIds(dictIds).then(() => {
|
||||
ElMessage.success("删除成功");
|
||||
handleResetQuery();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
handleQuery();
|
||||
});
|
||||
</script>
|
||||
@@ -1,5 +1,4 @@
|
||||
<!--字典类型-->
|
||||
|
||||
<!-- 分类字典 -->
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<div class="search-container">
|
||||
@@ -7,7 +6,7 @@
|
||||
<el-form-item label="关键字" prop="name">
|
||||
<el-input
|
||||
v-model="queryParams.keywords"
|
||||
placeholder="字典类型名称/编码"
|
||||
placeholder="字典名称"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
@@ -16,19 +15,16 @@
|
||||
<el-button type="primary" @click="handleQuery()"
|
||||
><i-ep-search />搜索</el-button
|
||||
>
|
||||
<el-button @click="handleResetQuery()"
|
||||
<el-button @click="handleResetClick()"
|
||||
><i-ep-refresh />重置</el-button
|
||||
>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<el-card shadow="never" class="table-container">
|
||||
<template #header>
|
||||
<el-button
|
||||
v-hasPerm="['sys:dict_type:add']"
|
||||
type="success"
|
||||
@click="openDialog()"
|
||||
<el-card shadow="never">
|
||||
<div class="mb-[10px]">
|
||||
<el-button type="success" @click="handleAddClick()"
|
||||
><i-ep-plus />新增</el-button
|
||||
>
|
||||
<el-button
|
||||
@@ -37,44 +33,46 @@
|
||||
@click="handleDelete()"
|
||||
><i-ep-delete />删除</el-button
|
||||
>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
highlight-current-row
|
||||
:data="dictTypeList"
|
||||
:data="tableData"
|
||||
border
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="字典类型名称" prop="name" width="200" />
|
||||
<el-table-column label="字典类型编码" prop="code" width="200" />
|
||||
<el-table-column label="状态" align="center" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.status === 1" type="success">启用</el-tag>
|
||||
<el-tag v-else type="info">禁用</el-tag>
|
||||
|
||||
<el-table-column type="expand" label="字典项列表" width="100">
|
||||
<template #default="props">
|
||||
<el-table :data="props.row.dictItems">
|
||||
<el-table-column label="字典项键" prop="name" width="200" />
|
||||
<el-table-column label="字典项值" prop="value" align="center" />
|
||||
<el-table-column label="排序" prop="sort" align="center" />
|
||||
</el-table>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="字典名称" prop="name" />
|
||||
<el-table-column label="字典编码" prop="code" />
|
||||
<el-table-column label="状态" prop="status">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.status === 1 ? 'success' : 'info'">
|
||||
{{ scope.row.status === 1 ? "启用" : "禁用" }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" prop="remark" align="center" />
|
||||
<el-table-column fixed="right" label="操作" align="center" width="220">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
size="small"
|
||||
@click.stop="openDictDialog(scope.row)"
|
||||
><i-ep-Collection />字典数据</el-button
|
||||
>
|
||||
<el-button
|
||||
v-hasPerm="['sys:dict_type:edit']"
|
||||
type="primary"
|
||||
link
|
||||
size="small"
|
||||
@click.stop="openDialog(scope.row.id)"
|
||||
@click.stop="handleEditClick(scope.row.id, scope.row.name)"
|
||||
><i-ep-edit />编辑</el-button
|
||||
>
|
||||
<el-button
|
||||
v-hasPerm="['sys:dict_type:delete']"
|
||||
type="primary"
|
||||
type="danger"
|
||||
link
|
||||
size="small"
|
||||
@click.stop="handleDelete(scope.row.id)"
|
||||
@@ -93,71 +91,124 @@
|
||||
/>
|
||||
</el-card>
|
||||
|
||||
<el-dialog
|
||||
<!--字典弹窗-->
|
||||
<el-drawer
|
||||
v-model="dialog.visible"
|
||||
:title="dialog.title"
|
||||
width="500px"
|
||||
@close="closeDialog"
|
||||
size="70%"
|
||||
@close="handleCloseDialog"
|
||||
>
|
||||
<el-form
|
||||
ref="dataFormRef"
|
||||
:model="formData"
|
||||
:rules="rules"
|
||||
label-width="80px"
|
||||
:rules="computedRules"
|
||||
label-width="100px"
|
||||
:inline="true"
|
||||
>
|
||||
<el-form-item label="字典名称" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入字典名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="字典编码" prop="code">
|
||||
<el-input v-model="formData.code" placeholder="请输入字典编码" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-radio-group v-model="formData.status">
|
||||
<el-radio :label="1">正常</el-radio>
|
||||
<el-radio :label="0">停用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input
|
||||
v-model="formData.remark"
|
||||
type="textarea"
|
||||
placeholder="字典类型备注"
|
||||
:autosize="{ minRows: 2, maxRows: 4 }"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-card shadow="never">
|
||||
<el-form-item label="字典名称" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入字典名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="字典编码" prop="code">
|
||||
<el-input v-model="formData.code" placeholder="请输入字典编码" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="状态">
|
||||
<el-radio-group v-model="formData.status">
|
||||
<el-radio :value="1">启用</el-radio>
|
||||
<el-radio :value="0">禁用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="never" class="mt-5">
|
||||
<template #header>
|
||||
<div class="flex-x-between">
|
||||
<span>字典项</span>
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
@click.stop="handleAddAttrClick"
|
||||
><i-ep-plus />新增字典</el-button
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
highlight--currentrow
|
||||
:data="formData.dictItems"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column label="字典项名称" width="200">
|
||||
<template #default="scope">
|
||||
<el-form-item :prop="'dictItems.' + scope.$index + '.name'">
|
||||
<el-input v-model="scope.row.name" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="字典项值" width="200">
|
||||
<template #default="scope">
|
||||
<el-form-item :prop="'dictItems.' + scope.$index + '.value'">
|
||||
<el-input v-model="scope.row.value" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="排序">
|
||||
<template #default="scope">
|
||||
<el-form-item :prop="'dictItems.' + scope.$index + '.sort'">
|
||||
<el-input v-model="scope.row.sort" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" prop="status">
|
||||
<template #default="scope">
|
||||
<el-form-item :prop="'dictItems.' + scope.$index + '.status'">
|
||||
<el-switch
|
||||
v-model="scope.row.status"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
/>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column
|
||||
fixed="right"
|
||||
label="操作"
|
||||
align="center"
|
||||
width="120"
|
||||
>
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
type="danger"
|
||||
link
|
||||
size="small"
|
||||
@click.stop="handleDeleteAttrClick(scope.$index)"
|
||||
><i-ep-delete />删除</el-button
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="handleSubmit">确 定</el-button>
|
||||
<el-button @click="closeDialog">取 消</el-button>
|
||||
<el-button type="primary" @click="handleSubmitClick">确 定</el-button>
|
||||
<el-button @click="handleCloseDialog">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!--字典数据弹窗-->
|
||||
<el-dialog
|
||||
v-model="dictDataDialog.visible"
|
||||
:title="dictDataDialog.title"
|
||||
width="1000px"
|
||||
@close="closeDictDialog"
|
||||
>
|
||||
<dict-item
|
||||
v-model:typeCode="selectedDictType.typeCode"
|
||||
v-model:typeName="selectedDictType.typeName"
|
||||
/>
|
||||
</el-dialog>
|
||||
</el-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineOptions({
|
||||
name: "DictType",
|
||||
inheritAttrs: false,
|
||||
name: "Dict",
|
||||
inherititems: false,
|
||||
});
|
||||
|
||||
import DictAPI from "@/api/dict";
|
||||
|
||||
import { DictTypePageVO, DictTypeQuery, DictTypeForm } from "@/api/dict/model";
|
||||
import DictAPI, { DictPageQuery, DictPageVO, DictForm } from "@/api/dict";
|
||||
|
||||
const queryFormRef = ref(ElForm);
|
||||
const dataFormRef = ref(ElForm);
|
||||
@@ -166,33 +217,42 @@ const loading = ref(false);
|
||||
const ids = ref<number[]>([]);
|
||||
const total = ref(0);
|
||||
|
||||
const queryParams = reactive<DictTypeQuery>({
|
||||
const queryParams = reactive<DictPageQuery>({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
});
|
||||
|
||||
const dictTypeList = ref<DictTypePageVO[]>();
|
||||
const tableData = ref<DictPageVO[]>();
|
||||
|
||||
// 字典弹窗
|
||||
const dialog = reactive({
|
||||
title: "",
|
||||
visible: false,
|
||||
});
|
||||
|
||||
const formData = reactive<DictTypeForm>({
|
||||
status: 1,
|
||||
const formData = reactive<DictForm>({});
|
||||
|
||||
const computedRules = computed(() => {
|
||||
const rules: Partial<Record<string, any>> = {
|
||||
name: [{ required: true, message: "请输入字典名称", trigger: "blur" }],
|
||||
code: [{ required: true, message: "请输入字典编码", trigger: "blur" }],
|
||||
};
|
||||
if (formData.dictItems) {
|
||||
formData.dictItems.forEach((attr, index) => {
|
||||
rules[`dictItems.${index}.name`] = [
|
||||
{ required: true, message: "请输入字典项名称", trigger: "blur" },
|
||||
];
|
||||
});
|
||||
}
|
||||
return rules;
|
||||
});
|
||||
|
||||
const rules = reactive({
|
||||
name: [{ required: true, message: "请输入字典类型名称", trigger: "blur" }],
|
||||
code: [{ required: true, message: "请输入字典类型编码", trigger: "blur" }],
|
||||
});
|
||||
|
||||
/** 查询 */
|
||||
// 查询
|
||||
function handleQuery() {
|
||||
loading.value = true;
|
||||
DictAPI.getDictTypePage(queryParams)
|
||||
DictAPI.getPage(queryParams)
|
||||
.then((data) => {
|
||||
dictTypeList.value = data.list;
|
||||
tableData.value = data.list;
|
||||
total.value = data.total;
|
||||
})
|
||||
.finally(() => {
|
||||
@@ -200,56 +260,57 @@ function handleQuery() {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置查询
|
||||
*/
|
||||
function handleResetQuery() {
|
||||
// 重置查询
|
||||
function handleResetClick() {
|
||||
queryFormRef.value.resetFields();
|
||||
queryParams.pageNum = 1;
|
||||
handleQuery();
|
||||
}
|
||||
|
||||
/** 行复选框选中 */
|
||||
// 行选择
|
||||
function handleSelectionChange(selection: any) {
|
||||
ids.value = selection.map((item: any) => item.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开字典类型表单弹窗
|
||||
*
|
||||
* @param dicTypeId 字典类型ID
|
||||
*/
|
||||
function openDialog(dicTypeId?: number) {
|
||||
// 新增字典
|
||||
function handleAddClick() {
|
||||
dialog.visible = true;
|
||||
if (dicTypeId) {
|
||||
dialog.title = "修改字典类型";
|
||||
DictAPI.getDictTypeForm(dicTypeId).then((data) => {
|
||||
Object.assign(formData, data);
|
||||
});
|
||||
} else {
|
||||
dialog.title = "新增字典类型";
|
||||
}
|
||||
dialog.title = "新增字典";
|
||||
}
|
||||
|
||||
/** 字典类型表单提交 */
|
||||
function handleSubmit() {
|
||||
/**
|
||||
* 编辑字典
|
||||
*
|
||||
* @param id 字典ID
|
||||
*/
|
||||
function handleEditClick(id: number, name: string) {
|
||||
dialog.visible = true;
|
||||
dialog.title = "【" + name + "】字典修改";
|
||||
DictAPI.getFormData(id).then((data) => {
|
||||
Object.assign(formData, data);
|
||||
});
|
||||
}
|
||||
|
||||
// 提交字典表单
|
||||
function handleSubmitClick() {
|
||||
dataFormRef.value.validate((isValid: boolean) => {
|
||||
console.log("isValid", isValid);
|
||||
if (isValid) {
|
||||
loading.value = false;
|
||||
const dictTypeId = formData.id;
|
||||
if (dictTypeId) {
|
||||
DictAPI.updateDictType(dictTypeId, formData)
|
||||
loading.value = true;
|
||||
const id = formData.id;
|
||||
if (id) {
|
||||
DictAPI.update(id, formData)
|
||||
.then(() => {
|
||||
ElMessage.success("修改成功");
|
||||
closeDialog();
|
||||
handleCloseDialog();
|
||||
handleQuery();
|
||||
})
|
||||
.finally(() => (loading.value = false));
|
||||
} else {
|
||||
DictAPI.addDictType(formData)
|
||||
DictAPI.add(formData)
|
||||
.then(() => {
|
||||
ElMessage.success("新增成功");
|
||||
closeDialog();
|
||||
handleCloseDialog();
|
||||
handleQuery();
|
||||
})
|
||||
.finally(() => (loading.value = false));
|
||||
@@ -258,63 +319,62 @@ function handleSubmit() {
|
||||
});
|
||||
}
|
||||
|
||||
/** 关闭字典类型弹窗 */
|
||||
function closeDialog() {
|
||||
/** 关闭字典弹窗 */
|
||||
function handleCloseDialog() {
|
||||
dialog.visible = false;
|
||||
resetForm();
|
||||
}
|
||||
|
||||
/** 重置字典类型表单 */
|
||||
function resetForm() {
|
||||
dataFormRef.value.resetFields();
|
||||
dataFormRef.value.clearValidate();
|
||||
|
||||
formData.id = undefined;
|
||||
formData.status = 1;
|
||||
}
|
||||
|
||||
/** 删除字典类型 */
|
||||
function handleDelete(dictTypeId?: number) {
|
||||
const dictTypeIds = [dictTypeId || ids.value].join(",");
|
||||
if (!dictTypeIds) {
|
||||
/**
|
||||
* 删除字典
|
||||
*
|
||||
* @param id 字典ID
|
||||
*/
|
||||
function handleDelete(id?: number) {
|
||||
const attrGroupIds = [id || ids.value].join(",");
|
||||
if (!attrGroupIds) {
|
||||
ElMessage.warning("请勾选删除项");
|
||||
return;
|
||||
}
|
||||
|
||||
ElMessageBox.confirm("确认删除已选中的数据项?", "警告", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}).then(() => {
|
||||
DictAPI.deleteDictTypes(dictTypeIds).then(() => {
|
||||
ElMessage.success("删除成功");
|
||||
handleResetQuery();
|
||||
});
|
||||
});
|
||||
}).then(
|
||||
() => {
|
||||
DictAPI.deleteByIds(attrGroupIds).then(() => {
|
||||
ElMessage.success("删除成功");
|
||||
handleResetClick();
|
||||
});
|
||||
},
|
||||
() => {
|
||||
ElMessage.info("已取消删除");
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const dictDataDialog = reactive({
|
||||
title: "",
|
||||
visible: false,
|
||||
});
|
||||
|
||||
const selectedDictType = reactive({ typeCode: "", typeName: "" }); // 当前选中的字典类型
|
||||
|
||||
/** 打开字典数据弹窗 */
|
||||
function openDictDialog(row: DictTypePageVO) {
|
||||
dictDataDialog.visible = true;
|
||||
dictDataDialog.title = "【" + row.name + "】字典数据";
|
||||
|
||||
selectedDictType.typeCode = row.code;
|
||||
selectedDictType.typeName = row.name;
|
||||
// 新增字典
|
||||
function handleAddAttrClick() {
|
||||
formData.dictItems = formData.dictItems ?? [];
|
||||
formData.dictItems.push({ sort: 1, status: 1 });
|
||||
}
|
||||
|
||||
/** 关闭字典数据弹窗 */
|
||||
function closeDictDialog() {
|
||||
dictDataDialog.visible = false;
|
||||
// 删除字典
|
||||
function handleDeleteAttrClick(index: number) {
|
||||
if (formData.dictItems && formData.dictItems.length > 0) {
|
||||
formData.dictItems.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
handleQuery();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.el-form--inline .el-form-item {
|
||||
margin-top: 18px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<el-button
|
||||
v-hasPerm="['sys:menu:add']"
|
||||
type="success"
|
||||
@click="handleDialogOpen(0)"
|
||||
@click="handleOpenDialog(0)"
|
||||
>
|
||||
<template #icon><i-ep-plus /></template>
|
||||
新增</el-button
|
||||
@@ -84,11 +84,18 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column
|
||||
label="路由名称"
|
||||
align="left"
|
||||
width="150"
|
||||
prop="routeName"
|
||||
/>
|
||||
|
||||
<el-table-column
|
||||
label="路由路径"
|
||||
align="left"
|
||||
width="150"
|
||||
prop="path"
|
||||
prop="routePath"
|
||||
/>
|
||||
|
||||
<el-table-column
|
||||
@@ -122,7 +129,7 @@
|
||||
type="primary"
|
||||
link
|
||||
size="small"
|
||||
@click.stop="handleDialogOpen(scope.row.id)"
|
||||
@click.stop="handleOpenDialog(scope.row.id)"
|
||||
>
|
||||
<i-ep-plus />新增
|
||||
</el-button>
|
||||
@@ -132,7 +139,7 @@
|
||||
type="primary"
|
||||
link
|
||||
size="small"
|
||||
@click.stop="handleDialogOpen(undefined, scope.row.id)"
|
||||
@click.stop="handleOpenDialog(undefined, scope.row.id)"
|
||||
>
|
||||
<i-ep-edit />编辑
|
||||
</el-button>
|
||||
@@ -150,19 +157,17 @@
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<el-dialog
|
||||
<el-drawer
|
||||
v-model="dialog.visible"
|
||||
:title="dialog.title"
|
||||
destroy-on-close
|
||||
append-to-body
|
||||
width="1000px"
|
||||
@close="closeDialog"
|
||||
@close="handleCloseDialog"
|
||||
size="50%"
|
||||
>
|
||||
<el-form
|
||||
ref="menuFormRef"
|
||||
:model="formData"
|
||||
:rules="rules"
|
||||
label-width="160px"
|
||||
label-width="100px"
|
||||
>
|
||||
<el-form-item label="父级菜单" prop="parentId">
|
||||
<el-tree-select
|
||||
@@ -196,7 +201,29 @@
|
||||
label="外链地址"
|
||||
prop="path"
|
||||
>
|
||||
<el-input v-model="formData.path" placeholder="请输入外链完整路径" />
|
||||
<el-input
|
||||
v-model="formData.routePath"
|
||||
placeholder="请输入外链完整路径"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item
|
||||
v-if="formData.type == MenuTypeEnum.MENU"
|
||||
prop="routeName"
|
||||
>
|
||||
<template #label>
|
||||
<div>
|
||||
路由名称
|
||||
<el-tooltip placement="bottom" effect="light">
|
||||
<template #content>
|
||||
如果需要开启缓存,需保证页面 defineOptions 中的 name
|
||||
与此处一致,建议使用驼峰。
|
||||
</template>
|
||||
<i-ep-QuestionFilled class="inline-block" />
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
<el-input v-model="formData.routeName" placeholder="User" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item
|
||||
@@ -204,8 +231,7 @@
|
||||
formData.type == MenuTypeEnum.CATALOG ||
|
||||
formData.type == MenuTypeEnum.MENU
|
||||
"
|
||||
label=""
|
||||
prop="path"
|
||||
prop="routePath"
|
||||
>
|
||||
<template #label>
|
||||
<div>
|
||||
@@ -222,10 +248,10 @@
|
||||
</template>
|
||||
<el-input
|
||||
v-if="formData.type == MenuTypeEnum.CATALOG"
|
||||
v-model="formData.path"
|
||||
v-model="formData.routePath"
|
||||
placeholder="system"
|
||||
/>
|
||||
<el-input v-else v-model="formData.path" placeholder="user" />
|
||||
<el-input v-else v-model="formData.routePath" placeholder="user" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item
|
||||
@@ -405,10 +431,10 @@
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="closeDialog">取 消</el-button>
|
||||
<el-button @click="handleCloseDialog">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</el-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -418,8 +444,7 @@ defineOptions({
|
||||
inheritAttrs: false,
|
||||
});
|
||||
|
||||
import MenuAPI from "@/api/menu";
|
||||
import { MenuQuery, MenuForm, MenuVO } from "@/api/menu/model";
|
||||
import MenuAPI, { MenuQuery, MenuForm, MenuVO } from "@/api/menu";
|
||||
import { MenuTypeEnum } from "@/enums/MenuTypeEnum";
|
||||
|
||||
const queryFormRef = ref(ElForm);
|
||||
@@ -458,7 +483,8 @@ const rules = reactive({
|
||||
parentId: [{ required: true, message: "请选择顶级菜单", trigger: "blur" }],
|
||||
name: [{ required: true, message: "请输入菜单名称", trigger: "blur" }],
|
||||
type: [{ required: true, message: "请选择菜单类型", trigger: "blur" }],
|
||||
path: [{ required: true, message: "请输入路由路径", trigger: "blur" }],
|
||||
routeName: [{ required: true, message: "请输入路由名称", trigger: "blur" }],
|
||||
routePath: [{ required: true, message: "请输入路由路径", trigger: "blur" }],
|
||||
component: [{ required: true, message: "请输入组件路径", trigger: "blur" }],
|
||||
visible: [{ required: true, message: "请输入路由路径", trigger: "blur" }],
|
||||
});
|
||||
@@ -496,7 +522,7 @@ function handleRowClick(row: MenuVO) {
|
||||
* @param parentId 父菜单ID
|
||||
* @param menuId 菜单ID
|
||||
*/
|
||||
function handleDialogOpen(parentId?: number, menuId?: number) {
|
||||
function handleOpenDialog(parentId?: number, menuId?: number) {
|
||||
MenuAPI.getOptions()
|
||||
.then((data) => {
|
||||
menuOptions.value = [{ value: 0, label: "顶级菜单", children: data }];
|
||||
@@ -541,13 +567,13 @@ function submitForm() {
|
||||
if (menuId) {
|
||||
MenuAPI.update(menuId, formData.value).then(() => {
|
||||
ElMessage.success("修改成功");
|
||||
closeDialog();
|
||||
handleCloseDialog();
|
||||
handleQuery();
|
||||
});
|
||||
} else {
|
||||
MenuAPI.add(formData.value).then(() => {
|
||||
ElMessage.success("新增成功");
|
||||
closeDialog();
|
||||
handleCloseDialog();
|
||||
handleQuery();
|
||||
});
|
||||
}
|
||||
@@ -566,18 +592,26 @@ function handleDelete(menuId: number) {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
})
|
||||
.then(() => {
|
||||
MenuAPI.deleteById(menuId).then(() => {
|
||||
ElMessage.success("删除成功");
|
||||
handleQuery();
|
||||
});
|
||||
})
|
||||
.catch(() => ElMessage.info("已取消删除"));
|
||||
}).then(
|
||||
() => {
|
||||
loading.value = true;
|
||||
MenuAPI.deleteById(menuId)
|
||||
.then(() => {
|
||||
ElMessage.success("删除成功");
|
||||
handleQuery();
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
},
|
||||
() => {
|
||||
ElMessage.info("已取消删除");
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// 关闭弹窗
|
||||
function closeDialog() {
|
||||
function handleCloseDialog() {
|
||||
dialog.visible = false;
|
||||
menuFormRef.value.resetFields();
|
||||
menuFormRef.value.clearValidate();
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
<el-card shadow="never" class="table-container">
|
||||
<template #header>
|
||||
<el-button type="success" @click="openDialog()"
|
||||
<el-button type="success" @click="handleOpenDialog()"
|
||||
><i-ep-plus />新增</el-button
|
||||
>
|
||||
<el-button
|
||||
@@ -60,7 +60,7 @@
|
||||
type="primary"
|
||||
size="small"
|
||||
link
|
||||
@click="openMenuDialog(scope.row)"
|
||||
@click="handleOpenAssignPermDialog(scope.row)"
|
||||
>
|
||||
<i-ep-position />分配权限
|
||||
</el-button>
|
||||
@@ -68,12 +68,12 @@
|
||||
type="primary"
|
||||
size="small"
|
||||
link
|
||||
@click="openDialog(scope.row.id)"
|
||||
@click="handleOpenDialog(scope.row.id)"
|
||||
>
|
||||
<i-ep-edit />编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
type="danger"
|
||||
size="small"
|
||||
link
|
||||
@click="handleDelete(scope.row.id)"
|
||||
@@ -98,7 +98,7 @@
|
||||
v-model="dialog.visible"
|
||||
:title="dialog.title"
|
||||
width="500px"
|
||||
@close="closeDialog"
|
||||
@close="handleCloseDialog"
|
||||
>
|
||||
<el-form
|
||||
ref="roleFormRef"
|
||||
@@ -143,74 +143,111 @@
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="handleSubmit">确 定</el-button>
|
||||
<el-button @click="closeDialog">取 消</el-button>
|
||||
<el-button @click="handleCloseDialog">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 分配菜单弹窗 -->
|
||||
<el-dialog
|
||||
v-model="menuDialogVisible"
|
||||
<!-- 分配权限弹窗 -->
|
||||
<el-drawer
|
||||
v-model="assignPermDialogVisible"
|
||||
:title="'【' + checkedRole.name + '】权限分配'"
|
||||
width="800px"
|
||||
size="500"
|
||||
>
|
||||
<el-scrollbar v-loading="loading" max-height="600px">
|
||||
<el-tree
|
||||
ref="menuRef"
|
||||
node-key="value"
|
||||
show-checkbox
|
||||
:data="menuList"
|
||||
:default-expand-all="true"
|
||||
<div class="flex-x-between">
|
||||
<el-input
|
||||
v-model="permKeywords"
|
||||
clearable
|
||||
class="w-[200px]"
|
||||
placeholder="菜单权限名称"
|
||||
>
|
||||
<template #default="{ data }">
|
||||
{{ data.label }}
|
||||
<template #prefix>
|
||||
<i-ep-search />
|
||||
</template>
|
||||
</el-tree>
|
||||
</el-scrollbar>
|
||||
</el-input>
|
||||
|
||||
<div class="flex-center">
|
||||
<el-button type="primary" size="small" plain @click="togglePermTree"
|
||||
><i-ep-switch />{{ isExpanded ? "收缩" : "展开" }}</el-button
|
||||
>
|
||||
<el-checkbox
|
||||
v-model="parentChildLinked"
|
||||
@change="handleparentChildLinkedChange"
|
||||
class="ml-5"
|
||||
>父子联动
|
||||
</el-checkbox>
|
||||
|
||||
<el-tooltip placement="bottom">
|
||||
<template #content>
|
||||
如果只需勾选菜单权限,不需要勾选子菜单或者按钮权限,请关闭父子联动
|
||||
</template>
|
||||
<i-ep-QuestionFilled
|
||||
class="ml-1 color-[--el-color-primary] inline-block cursor-pointer"
|
||||
/>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-tree
|
||||
ref="permTreeRef"
|
||||
node-key="value"
|
||||
show-checkbox
|
||||
:data="menuPermOptions"
|
||||
:filter-node-method="handlePermFilter"
|
||||
:default-expand-all="true"
|
||||
:check-strictly="!parentChildLinked"
|
||||
class="mt-5"
|
||||
>
|
||||
<template #default="{ data }">
|
||||
{{ data.label }}
|
||||
</template>
|
||||
</el-tree>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="handleRoleMenuSubmit"
|
||||
<el-button type="primary" @click="handleAssignPermSubmit"
|
||||
>确 定</el-button
|
||||
>
|
||||
<el-button @click="menuDialogVisible = false">取 消</el-button>
|
||||
<el-button @click="assignPermDialogVisible = false">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</el-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import RoleAPI from "@/api/role";
|
||||
import MenuAPI from "@/api/menu";
|
||||
|
||||
import { RolePageVO, RoleForm, RoleQuery } from "@/api/role/model";
|
||||
|
||||
defineOptions({
|
||||
name: "Role",
|
||||
inheritAttrs: false,
|
||||
});
|
||||
|
||||
import RoleAPI, { RolePageVO, RoleForm, RolePageQuery } from "@/api/role";
|
||||
import MenuAPI from "@/api/menu";
|
||||
|
||||
const queryFormRef = ref(ElForm);
|
||||
const roleFormRef = ref(ElForm);
|
||||
const menuRef = ref(ElTree);
|
||||
const permTreeRef = ref<InstanceType<typeof ElTree>>();
|
||||
|
||||
const loading = ref(false);
|
||||
const ids = ref<number[]>([]);
|
||||
const total = ref(0);
|
||||
|
||||
const queryParams = reactive<RoleQuery>({
|
||||
const queryParams = reactive<RolePageQuery>({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
});
|
||||
|
||||
// 角色表格数据
|
||||
const roleList = ref<RolePageVO[]>();
|
||||
// 菜单权限下拉
|
||||
const menuPermOptions = ref<OptionType[]>([]);
|
||||
|
||||
// 弹窗
|
||||
const dialog = reactive({
|
||||
title: "",
|
||||
visible: false,
|
||||
});
|
||||
|
||||
// 角色表单
|
||||
const formData = reactive<RoleForm>({
|
||||
sort: 1,
|
||||
status: 1,
|
||||
@@ -225,15 +262,18 @@ const rules = reactive({
|
||||
status: [{ required: true, message: "请选择状态", trigger: "blur" }],
|
||||
});
|
||||
|
||||
const menuDialogVisible = ref(false);
|
||||
|
||||
const menuList = ref<OptionType[]>([]);
|
||||
|
||||
// 选中的角色
|
||||
interface CheckedRole {
|
||||
id?: number;
|
||||
name?: string;
|
||||
}
|
||||
let checkedRole: CheckedRole = reactive({});
|
||||
const checkedRole = ref<CheckedRole>({});
|
||||
const assignPermDialogVisible = ref(false);
|
||||
|
||||
const permKeywords = ref("");
|
||||
const isExpanded = ref(true);
|
||||
|
||||
const parentChildLinked = ref(true);
|
||||
|
||||
/** 查询 */
|
||||
function handleQuery() {
|
||||
@@ -254,13 +294,13 @@ function handleResetQuery() {
|
||||
handleQuery();
|
||||
}
|
||||
|
||||
/** 行checkbox 选中事件 */
|
||||
/** 行复选框选中记录选中ID集合 */
|
||||
function handleSelectionChange(selection: any) {
|
||||
ids.value = selection.map((item: any) => item.id);
|
||||
}
|
||||
|
||||
/** 打开角色表单弹窗 */
|
||||
function openDialog(roleId?: number) {
|
||||
/** 打开角色弹窗 */
|
||||
function handleOpenDialog(roleId?: number) {
|
||||
dialog.visible = true;
|
||||
if (roleId) {
|
||||
dialog.title = "修改角色";
|
||||
@@ -272,7 +312,7 @@ function openDialog(roleId?: number) {
|
||||
}
|
||||
}
|
||||
|
||||
/** 角色保存提交 */
|
||||
/** 提交角色表单 */
|
||||
function handleSubmit() {
|
||||
roleFormRef.value.validate((valid: any) => {
|
||||
if (valid) {
|
||||
@@ -282,7 +322,7 @@ function handleSubmit() {
|
||||
RoleAPI.update(roleId, formData)
|
||||
.then(() => {
|
||||
ElMessage.success("修改成功");
|
||||
closeDialog();
|
||||
handleCloseDialog();
|
||||
handleResetQuery();
|
||||
})
|
||||
.finally(() => (loading.value = false));
|
||||
@@ -290,7 +330,7 @@ function handleSubmit() {
|
||||
RoleAPI.add(formData)
|
||||
.then(() => {
|
||||
ElMessage.success("新增成功");
|
||||
closeDialog();
|
||||
handleCloseDialog();
|
||||
handleResetQuery();
|
||||
})
|
||||
.finally(() => (loading.value = false));
|
||||
@@ -299,14 +339,10 @@ function handleSubmit() {
|
||||
});
|
||||
}
|
||||
|
||||
/** 关闭表单弹窗 */
|
||||
function closeDialog() {
|
||||
/** 关闭角色弹窗 */
|
||||
function handleCloseDialog() {
|
||||
dialog.visible = false;
|
||||
resetForm();
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
function resetForm() {
|
||||
roleFormRef.value.resetFields();
|
||||
roleFormRef.value.clearValidate();
|
||||
|
||||
@@ -327,37 +363,41 @@ function handleDelete(roleId?: number) {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}).then(() => {
|
||||
loading.value = true;
|
||||
RoleAPI.deleteByIds(roleIds)
|
||||
.then(() => {
|
||||
ElMessage.success("删除成功");
|
||||
handleResetQuery();
|
||||
})
|
||||
.finally(() => (loading.value = false));
|
||||
});
|
||||
}).then(
|
||||
() => {
|
||||
loading.value = true;
|
||||
RoleAPI.deleteByIds(roleIds)
|
||||
.then(() => {
|
||||
ElMessage.success("删除成功");
|
||||
handleResetQuery();
|
||||
})
|
||||
.finally(() => (loading.value = false));
|
||||
},
|
||||
() => {
|
||||
ElMessage.info("已取消删除");
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/** 打开分配菜单弹窗 */
|
||||
async function openMenuDialog(row: RolePageVO) {
|
||||
/** 打开分配菜单权限弹窗 */
|
||||
async function handleOpenAssignPermDialog(row: RolePageVO) {
|
||||
const roleId = row.id;
|
||||
if (roleId) {
|
||||
checkedRole = {
|
||||
id: roleId,
|
||||
name: row.name,
|
||||
};
|
||||
menuDialogVisible.value = true;
|
||||
assignPermDialogVisible.value = true;
|
||||
loading.value = true;
|
||||
|
||||
checkedRole.value.id = roleId;
|
||||
checkedRole.value.name = row.name;
|
||||
|
||||
// 获取所有的菜单
|
||||
menuList.value = await MenuAPI.getOptions();
|
||||
menuPermOptions.value = await MenuAPI.getOptions();
|
||||
|
||||
// 回显角色已拥有的菜单
|
||||
RoleAPI.getRoleMenuIds(roleId)
|
||||
.then((data) => {
|
||||
const checkedMenuIds = data;
|
||||
checkedMenuIds.forEach((menuId) =>
|
||||
menuRef.value.setChecked(menuId, true, false)
|
||||
permTreeRef.value!.setChecked(menuId, true, false)
|
||||
);
|
||||
})
|
||||
.finally(() => {
|
||||
@@ -366,19 +406,19 @@ async function openMenuDialog(row: RolePageVO) {
|
||||
}
|
||||
}
|
||||
|
||||
/** 角色分配菜单保存提交 */
|
||||
function handleRoleMenuSubmit() {
|
||||
const roleId = checkedRole.id;
|
||||
/** 分配菜单权限提交 */
|
||||
function handleAssignPermSubmit() {
|
||||
const roleId = checkedRole.value.id;
|
||||
if (roleId) {
|
||||
const checkedMenuIds: number[] = menuRef.value
|
||||
.getCheckedNodes(false, true)
|
||||
const checkedMenuIds: number[] = permTreeRef
|
||||
.value!.getCheckedNodes(false, true)
|
||||
.map((node: any) => node.value);
|
||||
|
||||
loading.value = true;
|
||||
RoleAPI.updateRoleMenus(roleId, checkedMenuIds)
|
||||
.then(() => {
|
||||
ElMessage.success("分配权限成功");
|
||||
menuDialogVisible.value = false;
|
||||
assignPermDialogVisible.value = false;
|
||||
handleResetQuery();
|
||||
})
|
||||
.finally(() => {
|
||||
@@ -387,6 +427,40 @@ function handleRoleMenuSubmit() {
|
||||
}
|
||||
}
|
||||
|
||||
/** 展开/收缩 菜单权限树 */
|
||||
function togglePermTree() {
|
||||
isExpanded.value = !isExpanded.value;
|
||||
if (permTreeRef.value) {
|
||||
Object.values(permTreeRef.value.store.nodesMap).forEach((node: any) => {
|
||||
if (isExpanded.value) {
|
||||
node.expand();
|
||||
} else {
|
||||
node.collapse();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** 权限筛选 */
|
||||
watch(permKeywords, (val) => {
|
||||
permTreeRef.value!.filter(val);
|
||||
});
|
||||
|
||||
function handlePermFilter(
|
||||
value: string,
|
||||
data: {
|
||||
[key: string]: any;
|
||||
}
|
||||
) {
|
||||
if (!value) return true;
|
||||
return data.label.includes(value);
|
||||
}
|
||||
|
||||
/** 父子菜单节点是否联动 change*/
|
||||
function handleparentChildLinkedChange(val: any) {
|
||||
parentChildLinked.value = val;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
handleQuery();
|
||||
});
|
||||
|
||||
145
src/views/system/user/components/user-import.vue
Normal file
145
src/views/system/user/components/user-import.vue
Normal file
@@ -0,0 +1,145 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
:align-center="true"
|
||||
title="导入数据"
|
||||
width="600px"
|
||||
@close="handleClose"
|
||||
>
|
||||
<el-scrollbar max-height="60vh">
|
||||
<el-form
|
||||
ref="importFormRef"
|
||||
label-width="auto"
|
||||
style="padding-right: var(--el-dialog-padding-primary)"
|
||||
:model="importFormData"
|
||||
:rules="importFormRules"
|
||||
>
|
||||
<el-form-item label="文件名" prop="files">
|
||||
<el-upload
|
||||
class="w-full"
|
||||
ref="uploadRef"
|
||||
v-model:file-list="importFormData.files"
|
||||
accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel"
|
||||
:drag="true"
|
||||
:limit="1"
|
||||
:auto-upload="false"
|
||||
:on-exceed="handleFileExceed"
|
||||
>
|
||||
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
|
||||
<div class="el-upload__text">
|
||||
将文件拖到此处,或<em>点击上传</em>
|
||||
</div>
|
||||
<template #tip>
|
||||
<div class="el-upload__tip">
|
||||
*.xlsx / *.xls
|
||||
<el-link
|
||||
type="primary"
|
||||
icon="download"
|
||||
:underline="false"
|
||||
@click="handleDownloadTemplate"
|
||||
>
|
||||
下载模板
|
||||
</el-link>
|
||||
</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-scrollbar>
|
||||
<template #footer>
|
||||
<div style="padding-right: var(--el-dialog-padding-primary)">
|
||||
<el-button
|
||||
type="primary"
|
||||
:disabled="importFormData.files.length === 0"
|
||||
@click="handleSubmit"
|
||||
>
|
||||
确 定
|
||||
</el-button>
|
||||
<el-button @click="handleClose">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { type UploadUserFile } from "element-plus";
|
||||
|
||||
import UserAPI from "@/api/user";
|
||||
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(["update:visible", "import-success"]);
|
||||
|
||||
const dialogVisible = computed({
|
||||
get: () => props.visible,
|
||||
set: (val) => {
|
||||
emit("update:visible", val);
|
||||
},
|
||||
});
|
||||
|
||||
const importFormRef = ref(null);
|
||||
const uploadRef = ref(null);
|
||||
|
||||
const importFormData = reactive<{
|
||||
files: UploadUserFile[];
|
||||
}>({
|
||||
files: [],
|
||||
});
|
||||
|
||||
const importFormRules = {
|
||||
files: [{ required: true, message: "文件不能为空", trigger: "blur" }],
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
dialogVisible.value = false;
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!importFormData.files.length) {
|
||||
ElMessage.warning("请选择文件");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await UserAPI.import(1, importFormData.files[0].raw as File);
|
||||
ElMessage.success("上传成功");
|
||||
emit("import-success");
|
||||
handleClose();
|
||||
} catch (error) {
|
||||
ElMessage.error("上传失败");
|
||||
}
|
||||
};
|
||||
|
||||
const handleFileExceed = () => {
|
||||
ElMessage.warning("只能上传一个文件");
|
||||
};
|
||||
|
||||
const handleDownloadTemplate = () => {
|
||||
UserAPI.downloadTemplate().then((response: any) => {
|
||||
const fileData = response.data;
|
||||
const fileName = decodeURI(
|
||||
response.headers["content-disposition"].split(";")[1].split("=")[1]
|
||||
);
|
||||
const fileType =
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8";
|
||||
|
||||
const blob = new Blob([fileData], { type: fileType });
|
||||
const downloadUrl = window.URL.createObjectURL(blob);
|
||||
|
||||
const downloadLink = document.createElement("a");
|
||||
downloadLink.href = downloadUrl;
|
||||
downloadLink.download = fileName;
|
||||
|
||||
document.body.appendChild(downloadLink);
|
||||
downloadLink.click();
|
||||
|
||||
document.body.removeChild(downloadLink);
|
||||
window.URL.revokeObjectURL(downloadUrl);
|
||||
});
|
||||
};
|
||||
</script>
|
||||
@@ -59,12 +59,12 @@
|
||||
|
||||
<el-card shadow="never" class="table-container">
|
||||
<template #header>
|
||||
<div class="flex justify-between">
|
||||
<div class="flex-x-between">
|
||||
<div>
|
||||
<el-button
|
||||
v-hasPerm="['sys:user:add']"
|
||||
type="success"
|
||||
@click="openDialog('user-form')"
|
||||
@click="handleOpenDialog()"
|
||||
><i-ep-plus />新增</el-button
|
||||
>
|
||||
<el-button
|
||||
@@ -76,19 +76,10 @@
|
||||
>
|
||||
</div>
|
||||
<div>
|
||||
<el-dropdown split-button>
|
||||
导入
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item @click="downloadTemplate">
|
||||
<i-ep-download />下载模板</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item @click="openDialog('user-import')">
|
||||
<i-ep-top />导入数据</el-dropdown-item
|
||||
>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
<el-button class="ml-3" @click="handleOpenImportDialog"
|
||||
><template #icon><i-ep-upload /></template>导入</el-button
|
||||
>
|
||||
|
||||
<el-button class="ml-3" @click="handleExport"
|
||||
><template #icon><i-ep-download /></template>导出</el-button
|
||||
>
|
||||
@@ -162,7 +153,7 @@
|
||||
type="primary"
|
||||
size="small"
|
||||
link
|
||||
@click="resetPassword(scope.row)"
|
||||
@click="hancleResetPassword(scope.row)"
|
||||
><i-ep-refresh-left />重置密码</el-button
|
||||
>
|
||||
<el-button
|
||||
@@ -170,12 +161,12 @@
|
||||
type="primary"
|
||||
link
|
||||
size="small"
|
||||
@click="openDialog('user-form', scope.row.id)"
|
||||
@click="handleOpenDialog(scope.row.id)"
|
||||
><i-ep-edit />编辑</el-button
|
||||
>
|
||||
<el-button
|
||||
v-hasPerm="['sys:user:delete']"
|
||||
type="primary"
|
||||
type="danger"
|
||||
link
|
||||
size="small"
|
||||
@click="handleDelete(scope.row.id)"
|
||||
@@ -196,17 +187,15 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 弹窗 -->
|
||||
<el-dialog
|
||||
<!-- 用户表单弹窗 -->
|
||||
<el-drawer
|
||||
v-model="dialog.visible"
|
||||
:title="dialog.title"
|
||||
:width="dialog.width"
|
||||
append-to-body
|
||||
@close="closeDialog"
|
||||
@close="handleCloseDialog"
|
||||
>
|
||||
<!-- 用户新增/编辑表单 -->
|
||||
<el-form
|
||||
v-if="dialog.type === 'user-form'"
|
||||
ref="userFormRef"
|
||||
:model="formData"
|
||||
:rules="rules"
|
||||
@@ -228,7 +217,7 @@
|
||||
<el-tree-select
|
||||
v-model="formData.deptId"
|
||||
placeholder="请选择所属部门"
|
||||
:data="deptList"
|
||||
:data="deptOptions"
|
||||
filterable
|
||||
check-strictly
|
||||
:render-after-expand="false"
|
||||
@@ -236,13 +225,13 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="性别" prop="gender">
|
||||
<dictionary v-model="formData.gender" type-code="gender" />
|
||||
<dictionary v-model="formData.gender" code="gender" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="角色" prop="roleIds">
|
||||
<el-select v-model="formData.roleIds" multiple placeholder="请选择">
|
||||
<el-option
|
||||
v-for="item in roleList"
|
||||
v-for="item in roleOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
@@ -274,55 +263,19 @@
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<!-- 用户导入表单 -->
|
||||
<el-form
|
||||
v-else-if="dialog.type === 'user-import'"
|
||||
:model="importData"
|
||||
label-width="100px"
|
||||
>
|
||||
<el-form-item label="部门">
|
||||
<el-tree-select
|
||||
v-model="importData.deptId"
|
||||
placeholder="请选择部门"
|
||||
:data="deptList"
|
||||
filterable
|
||||
check-strictly
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Excel文件">
|
||||
<el-upload
|
||||
ref="uploadRef"
|
||||
action=""
|
||||
drag
|
||||
accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel"
|
||||
:limit="1"
|
||||
:auto-upload="false"
|
||||
:file-list="importData.fileList"
|
||||
:on-change="handleFileChange"
|
||||
:on-exceed="handleFileExceed"
|
||||
>
|
||||
<el-icon class="el-icon--upload">
|
||||
<i-ep-upload-filled />
|
||||
</el-icon>
|
||||
<div class="el-upload__text">
|
||||
将文件拖到此处,或
|
||||
<em>点击上传</em>
|
||||
</div>
|
||||
<template #tip>
|
||||
<div>xls/xlsx files</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<!-- 弹窗底部操作按钮 -->
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="handleSubmit">确 定</el-button>
|
||||
<el-button @click="closeDialog">取 消</el-button>
|
||||
<el-button @click="handleCloseDialog">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</el-drawer>
|
||||
|
||||
<!-- 用户导入弹窗 -->
|
||||
<user-import
|
||||
v-model:visible="importDialogVisible"
|
||||
@import-success="handleOpenImportDialogSuccess"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -332,30 +285,28 @@ defineOptions({
|
||||
inheritAttrs: false,
|
||||
});
|
||||
|
||||
import UserAPI from "@/api/user";
|
||||
import UserAPI, { UserForm, UserPageQuery, UserPageVO } from "@/api/user";
|
||||
import DeptAPI from "@/api/dept";
|
||||
import RoleAPI from "@/api/role";
|
||||
|
||||
import { UserForm, UserQuery, UserPageVO } from "@/api/user/model";
|
||||
import type { UploadInstance } from "element-plus";
|
||||
import { genFileId } from "element-plus";
|
||||
const queryFormRef = ref(ElForm);
|
||||
const userFormRef = ref(ElForm);
|
||||
|
||||
const queryFormRef = ref(ElForm); // 查询表单
|
||||
const userFormRef = ref(ElForm); // 用户表单
|
||||
const uploadRef = ref<UploadInstance>(); // 上传组件
|
||||
|
||||
const loading = ref(false); // 加载状态
|
||||
const removeIds = ref([]); // 删除用户ID集合 用于批量删除
|
||||
const queryParams = reactive<UserQuery>({
|
||||
const loading = ref(false);
|
||||
const removeIds = ref([]);
|
||||
const total = ref(0);
|
||||
const pageData = ref<UserPageVO[]>();
|
||||
/** 部门下拉选项 */
|
||||
const deptOptions = ref<OptionType[]>();
|
||||
/** 角色下拉选项 */
|
||||
const roleOptions = ref<OptionType[]>();
|
||||
/** 用户查询参数 */
|
||||
const queryParams = reactive<UserPageQuery>({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
});
|
||||
const dateTimeRange = ref("");
|
||||
const total = ref(0); // 数据总数
|
||||
const pageData = ref<UserPageVO[]>(); // 用户分页数据
|
||||
const deptList = ref<OptionType[]>(); // 部门下拉数据源
|
||||
const roleList = ref<OptionType[]>(); // 角色下拉数据源
|
||||
|
||||
const dateTimeRange = ref("");
|
||||
watch(dateTimeRange, (newVal) => {
|
||||
if (newVal) {
|
||||
queryParams.startTime = newVal[0];
|
||||
@@ -363,27 +314,21 @@ watch(dateTimeRange, (newVal) => {
|
||||
}
|
||||
});
|
||||
|
||||
// 弹窗对象
|
||||
/** 用户弹窗对象 */
|
||||
const dialog = reactive({
|
||||
visible: false,
|
||||
type: "user-form",
|
||||
width: 800,
|
||||
title: "",
|
||||
});
|
||||
|
||||
/** 导入弹窗显示状态 */
|
||||
const importDialogVisible = ref(false);
|
||||
|
||||
// 用户表单数据
|
||||
const formData = reactive<UserForm>({
|
||||
status: 1,
|
||||
});
|
||||
|
||||
// 用户导入数据
|
||||
const importData = reactive({
|
||||
deptId: undefined,
|
||||
file: undefined,
|
||||
fileList: [],
|
||||
});
|
||||
|
||||
// 校验规则
|
||||
/** 用户表单校验规则 */
|
||||
const rules = reactive({
|
||||
username: [{ required: true, message: "用户名不能为空", trigger: "blur" }],
|
||||
nickname: [{ required: true, message: "用户昵称不能为空", trigger: "blur" }],
|
||||
@@ -430,13 +375,13 @@ function handleResetQuery() {
|
||||
handleQuery();
|
||||
}
|
||||
|
||||
/** 行选中 */
|
||||
/** 行复选框选中记录选中ID集合 */
|
||||
function handleSelectionChange(selection: any) {
|
||||
removeIds.value = selection.map((item: any) => item.id);
|
||||
}
|
||||
|
||||
/** 重置密码 */
|
||||
function resetPassword(row: { [key: string]: any }) {
|
||||
function hancleResetPassword(row: { [key: string]: any }) {
|
||||
ElMessageBox.prompt(
|
||||
"请输入用户「" + row.username + "」的新密码",
|
||||
"重置密码",
|
||||
@@ -456,110 +401,63 @@ function resetPassword(row: { [key: string]: any }) {
|
||||
});
|
||||
}
|
||||
|
||||
/** 加载角色下拉数据源 */
|
||||
async function loadRoleOptions() {
|
||||
RoleAPI.getOptions().then((data) => {
|
||||
roleList.value = data;
|
||||
});
|
||||
}
|
||||
|
||||
/** 加载部门下拉数据源 */
|
||||
async function loadDeptOptions() {
|
||||
DeptAPI.getOptions().then((data) => {
|
||||
deptList.value = data;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开弹窗
|
||||
*
|
||||
* @param type 弹窗类型 用户表单:user-form | 用户导入:user-import
|
||||
* @param id 用户ID
|
||||
*/
|
||||
async function openDialog(type: string, id?: number) {
|
||||
async function handleOpenDialog(id?: number) {
|
||||
dialog.visible = true;
|
||||
dialog.type = type;
|
||||
// 加载角色下拉数据源
|
||||
roleOptions.value = await RoleAPI.getOptions();
|
||||
// 加载部门下拉数据源
|
||||
deptOptions.value = await DeptAPI.getOptions();
|
||||
|
||||
if (dialog.type === "user-form") {
|
||||
// 用户表单弹窗
|
||||
await loadDeptOptions();
|
||||
await loadRoleOptions();
|
||||
if (id) {
|
||||
dialog.title = "修改用户";
|
||||
UserAPI.getFormData(id).then((data) => {
|
||||
Object.assign(formData, { ...data });
|
||||
});
|
||||
} else {
|
||||
dialog.title = "新增用户";
|
||||
}
|
||||
} else if (dialog.type === "user-import") {
|
||||
// 用户导入弹窗
|
||||
dialog.title = "导入用户";
|
||||
dialog.width = 600;
|
||||
loadDeptOptions();
|
||||
if (id) {
|
||||
dialog.title = "修改用户";
|
||||
UserAPI.getFormData(id).then((data) => {
|
||||
Object.assign(formData, { ...data });
|
||||
});
|
||||
} else {
|
||||
dialog.title = "新增用户";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭弹窗
|
||||
*
|
||||
* @param type 弹窗类型 用户表单:user-form | 用户导入:user-import
|
||||
*/
|
||||
function closeDialog() {
|
||||
/** 关闭弹窗 */
|
||||
function handleCloseDialog() {
|
||||
dialog.visible = false;
|
||||
if (dialog.type === "user-form") {
|
||||
userFormRef.value.resetFields();
|
||||
userFormRef.value.clearValidate();
|
||||
userFormRef.value.resetFields();
|
||||
userFormRef.value.clearValidate();
|
||||
|
||||
formData.id = undefined;
|
||||
formData.status = 1;
|
||||
} else if (dialog.type === "user-import") {
|
||||
importData.file = undefined;
|
||||
importData.fileList = [];
|
||||
}
|
||||
formData.id = undefined;
|
||||
formData.status = 1;
|
||||
}
|
||||
|
||||
/** 表单提交 */
|
||||
const handleSubmit = useThrottleFn(() => {
|
||||
if (dialog.type === "user-form") {
|
||||
userFormRef.value.validate((valid: any) => {
|
||||
if (valid) {
|
||||
const userId = formData.id;
|
||||
loading.value = true;
|
||||
if (userId) {
|
||||
UserAPI.update(userId, formData)
|
||||
.then(() => {
|
||||
ElMessage.success("修改用户成功");
|
||||
closeDialog();
|
||||
handleResetQuery();
|
||||
})
|
||||
.finally(() => (loading.value = false));
|
||||
} else {
|
||||
UserAPI.add(formData)
|
||||
.then(() => {
|
||||
ElMessage.success("新增用户成功");
|
||||
closeDialog();
|
||||
handleResetQuery();
|
||||
})
|
||||
.finally(() => (loading.value = false));
|
||||
}
|
||||
userFormRef.value.validate((valid: any) => {
|
||||
if (valid) {
|
||||
const userId = formData.id;
|
||||
loading.value = true;
|
||||
if (userId) {
|
||||
UserAPI.update(userId, formData)
|
||||
.then(() => {
|
||||
ElMessage.success("修改用户成功");
|
||||
handleCloseDialog();
|
||||
handleResetQuery();
|
||||
})
|
||||
.finally(() => (loading.value = false));
|
||||
} else {
|
||||
UserAPI.add(formData)
|
||||
.then(() => {
|
||||
ElMessage.success("新增用户成功");
|
||||
handleCloseDialog();
|
||||
handleResetQuery();
|
||||
})
|
||||
.finally(() => (loading.value = false));
|
||||
}
|
||||
});
|
||||
} else if (dialog.type === "user-import") {
|
||||
if (!importData?.deptId) {
|
||||
ElMessage.warning("请选择部门");
|
||||
return false;
|
||||
}
|
||||
if (!importData?.file) {
|
||||
ElMessage.warning("上传Excel文件不能为空");
|
||||
return false;
|
||||
}
|
||||
UserAPI.import(importData?.deptId, importData?.file).then((data) => {
|
||||
ElMessage.success("导入用户成功");
|
||||
closeDialog();
|
||||
handleResetQuery();
|
||||
});
|
||||
}
|
||||
});
|
||||
}, 3000);
|
||||
|
||||
/** 删除用户 */
|
||||
@@ -574,51 +472,29 @@ function handleDelete(id?: number) {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}).then(function () {
|
||||
UserAPI.deleteByIds(userIds).then(() => {
|
||||
ElMessage.success("删除成功");
|
||||
handleResetQuery();
|
||||
});
|
||||
});
|
||||
}).then(
|
||||
function () {
|
||||
loading.value = true;
|
||||
UserAPI.deleteByIds(userIds)
|
||||
.then(() => {
|
||||
ElMessage.success("删除成功");
|
||||
handleResetQuery();
|
||||
})
|
||||
.finally(() => (loading.value = false));
|
||||
},
|
||||
function () {
|
||||
ElMessage.info("已取消删除");
|
||||
}
|
||||
);
|
||||
}
|
||||
/** 打开导入弹窗 */
|
||||
function handleOpenImportDialog() {
|
||||
importDialogVisible.value = true;
|
||||
}
|
||||
|
||||
/** 下载导入模板 */
|
||||
function downloadTemplate() {
|
||||
UserAPI.downloadTemplate().then((response: any) => {
|
||||
const fileData = response.data;
|
||||
const fileName = decodeURI(
|
||||
response.headers["content-disposition"].split(";")[1].split("=")[1]
|
||||
);
|
||||
const fileType =
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8";
|
||||
|
||||
const blob = new Blob([fileData], { type: fileType });
|
||||
const downloadUrl = window.URL.createObjectURL(blob);
|
||||
|
||||
const downloadLink = document.createElement("a");
|
||||
downloadLink.href = downloadUrl;
|
||||
downloadLink.download = fileName;
|
||||
|
||||
document.body.appendChild(downloadLink);
|
||||
downloadLink.click();
|
||||
|
||||
document.body.removeChild(downloadLink);
|
||||
window.URL.revokeObjectURL(downloadUrl);
|
||||
});
|
||||
}
|
||||
|
||||
/** Excel文件 Change */
|
||||
function handleFileChange(file: any) {
|
||||
importData.file = file.raw;
|
||||
}
|
||||
|
||||
/** Excel文件 Exceed */
|
||||
function handleFileExceed(files: any) {
|
||||
uploadRef.value!.clearFiles();
|
||||
const file = files[0];
|
||||
file.uid = genFileId();
|
||||
uploadRef.value!.handleStart(file);
|
||||
importData.file = file;
|
||||
/** 导入用户成功 */
|
||||
function handleOpenImportDialogSuccess() {
|
||||
handleQuery();
|
||||
}
|
||||
|
||||
/** 导出用户 */
|
||||
|
||||
@@ -12,24 +12,17 @@
|
||||
"lib": ["esnext", "dom"],
|
||||
"baseUrl": ".",
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"jsx": "preserve",
|
||||
"jsxFactory": "h",
|
||||
"jsxFragmentFactory": "Fragment",
|
||||
"paths": {
|
||||
"@/*": ["src/*"]
|
||||
},
|
||||
"types": ["vite/client", "unplugin-icons/types/vue", "element-plus/global"],
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */,
|
||||
"allowSyntheticDefaultImports": true /* 允许默认导入 */,
|
||||
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
|
||||
|
||||
"jsx": "preserve",
|
||||
"jsxFactory": "h",
|
||||
"jsxFragmentFactory": "Fragment"
|
||||
"types": ["vite/client", "unplugin-icons/types/vue", "element-plus/global"]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.vue",
|
||||
"src/typings/**/*.d.ts",
|
||||
"mock/**/*.ts",
|
||||
"vite.config.ts"
|
||||
],
|
||||
"include": ["mock/**/*.ts", "src/**/*.ts", "src/**/*.vue", "vite.config.ts"],
|
||||
"exclude": ["node_modules", "dist", "**/*.js"]
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
|
||||
vueTemplate: true,
|
||||
// 指定自动导入函数TS类型声明文件路径 (false:关闭自动生成)
|
||||
dts: false,
|
||||
// dts: "src/typings/auto-imports.d.ts",
|
||||
// dts: "src/types/auto-imports.d.ts",
|
||||
}),
|
||||
Components({
|
||||
resolvers: [
|
||||
@@ -116,7 +116,7 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
|
||||
dirs: ["src/components", "src/**/components"],
|
||||
// 指定自动导入组件TS类型声明文件路径 (false:关闭自动生成)
|
||||
dts: false,
|
||||
// dts: "src/typings/components.d.ts",
|
||||
// dts: "src/types/components.d.ts",
|
||||
}),
|
||||
Icons({
|
||||
// 自动安装图标库
|
||||
@@ -128,9 +128,9 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
|
||||
// 指定symbolId格式
|
||||
symbolId: "icon-[dir]-[name]",
|
||||
}),
|
||||
VueDevTools({
|
||||
/* VueDevTools({
|
||||
openInEditorHost: `http://localhost:${env.VITE_APP_PORT}`,
|
||||
}),
|
||||
}), */
|
||||
],
|
||||
// 预加载项目必需的组件
|
||||
optimizeDeps: {
|
||||
|
||||
Reference in New Issue
Block a user