admin 初始化
This commit is contained in:
parent
12ecc21e37
commit
51d369d4c1
1
.env.development
Normal file
1
.env.development
Normal file
@ -0,0 +1 @@
|
||||
VITE_BASE_URL= 'https://vue3.go-admin.dev'
|
||||
0
.env.production
Normal file
0
.env.production
Normal file
15
.eslintrc.js
Normal file
15
.eslintrc.js
Normal file
@ -0,0 +1,15 @@
|
||||
module.exports = {
|
||||
extends: [
|
||||
// add more generic rulesets here, such as:
|
||||
// 'eslint:recommended',
|
||||
'plugin:vue/base',
|
||||
'plugin:vue/vue3-essential',
|
||||
// 'plugin:vue/vue3-strongly-recommended'
|
||||
//'plugin:vue/vue3-recommended',
|
||||
// 'plugin:vue/recommended' // Use this if you are using Vue.js 2.x.
|
||||
],
|
||||
rules: {
|
||||
// override/add rules settings here, such as:
|
||||
// 'vue/no-unused-vars': 'error'
|
||||
}
|
||||
}
|
||||
36
.gitignore
vendored
36
.gitignore
vendored
@ -1,11 +1,31 @@
|
||||
# ---> Vue
|
||||
# gitignore template for Vue.js projects
|
||||
#
|
||||
# Recommended template: Node.gitignore
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# TODO: where does this rule come from?
|
||||
docs/_book
|
||||
yarn.lock
|
||||
|
||||
# TODO: where does this rule come from?
|
||||
test/
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
# node_modules
|
||||
node_modules
|
||||
package-lock.json
|
||||
package-lock.json
|
||||
|
||||
320
README.md
320
README.md
@ -1,4 +1,318 @@
|
||||
# hospital-admin
|
||||
# Vue 3 + Vite
|
||||
|
||||
互联网医院
|
||||
前端
|
||||
This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.volar)
|
||||
# go-admin
|
||||
|
||||
<img align="right" width="320" src="https://gitee.com/mydearzwj/image/raw/master/img/go-admin.svg">
|
||||
|
||||
|
||||
[](https://github.com/go-admin-team/go-admin)
|
||||
[](https://github.com/go-admin-team/go-admin/releases)
|
||||
[](https://github.com/go-admin-team/go-admin)
|
||||
|
||||
[English](https://github.com/go-admin-team/go-admin/blob/master/README.md) | 简体中文
|
||||
|
||||
|
||||
基于Gin + Vue + Argo Design UI的前后端分离权限管理系统,系统初始化极度简单,只需要配置文件中,修改数据库连接,系统支持多指令操作,迁移指令可以让初始化数据库信息变得更简单,服务指令可以很简单的启动api服务
|
||||
|
||||
[在线文档](https://doc.go-admin.dev)
|
||||
|
||||
[github在线文档](https://wenjianzhang.github.io)
|
||||
|
||||
[gitee在线文档](http://mydearzwj.gitee.io/go-admin-doc/)
|
||||
|
||||
[后端项目](https://github.com/go-admin-team/go-admin)
|
||||
|
||||
[视频教程](https://space.bilibili.com/565616721/channel/detail?cid=125737)
|
||||
|
||||
## ✨ 特性
|
||||
|
||||
- 遵循 RESTful API 设计规范
|
||||
|
||||
- 基于 GIN WEB API 框架,提供了丰富的中间件支持(用户认证、跨域、访问日志、追踪ID等)
|
||||
|
||||
- 基于Casbin的 RBAC 访问控制模型
|
||||
|
||||
- JWT 认证
|
||||
|
||||
- 支持 Swagger 文档(基于swaggo)
|
||||
|
||||
- 基于 GORM 的数据库存储,可扩展多种类型数据库
|
||||
|
||||
- 配置文件简单的模型映射,快速能够得到想要的配置
|
||||
|
||||
- 代码生成工具
|
||||
|
||||
- 表单构建工具
|
||||
|
||||
- 多指令模式
|
||||
|
||||
- TODO: 单元测试
|
||||
|
||||
|
||||
## 🎁 内置
|
||||
|
||||
1. 用户管理:用户是系统操作者,该功能主要完成系统用户配置。
|
||||
2. 部门管理:配置系统组织机构(公司、部门、小组),树结构展现支持数据权限。
|
||||
3. 岗位管理:配置系统用户所属担任职务。
|
||||
4. 菜单管理:配置系统菜单,操作权限,按钮权限标识,接口权限等。
|
||||
5. 角色管理:角色菜单权限分配、设置角色按机构进行数据范围权限划分。
|
||||
6. 字典管理:对系统中经常使用的一些较为固定的数据进行维护。
|
||||
7. 参数管理:对系统动态配置常用参数。
|
||||
8. 操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。
|
||||
9. 登录日志:系统登录日志记录查询包含登录异常。
|
||||
1. 接口文档:根据业务代码自动生成相关的api接口文档。
|
||||
1. 代码生成:根据数据表结构生成对应的增删改查相对应业务,全程可视化操作,让基本业务可以零代码实现。
|
||||
1. 表单构建:自定义页面样式,拖拉拽实现页面布局。
|
||||
1. 服务监控:查看一些服务器的基本信息。
|
||||
1. 内容管理:demo功能,下设分类管理、内容管理。可以参考使用方便快速入门。
|
||||
|
||||
## 准备工作
|
||||
|
||||
你需要在本地安装 [go] [gin] [node](http://nodejs.org/) 和 [git](https://git-scm.com/)
|
||||
|
||||
同时配套了系列教程包含视频和文档,如何从下载完成到熟练使用,强烈建议大家先看完这些教程再来实践本项目!!!
|
||||
|
||||
### 轻松实现go-admin写出第一个应用 - 文档教程
|
||||
|
||||
[步骤一 - 基础内容介绍](http://doc.zhangwj.com/go-admin-site/guide/intro/tutorial01.html)
|
||||
|
||||
[步骤二 - 实际应用 - 编写增删改查](http://doc.zhangwj.com/go-admin-site/guide/intro/tutorial02.html)
|
||||
|
||||
### 手把手教你从入门到放弃 - 视频教程
|
||||
|
||||
[如何启动go-admin](https://www.bilibili.com/video/BV1z5411x7JG)
|
||||
|
||||
[使用生成工具轻松实现业务](https://www.bilibili.com/video/BV1Dg4y1i79D)
|
||||
|
||||
[v1.1.0版本代码生成工具-释放双手](https://www.bilibili.com/video/BV1N54y1i71P) [进阶]
|
||||
|
||||
[多命令启动方式讲解以及IDE配置](https://www.bilibili.com/video/BV1Fg4y1q7ph)
|
||||
|
||||
[go-admin菜单的配置说明](https://www.bilibili.com/video/BV1Wp4y1D715) [必看]
|
||||
|
||||
[如何配置菜单信息以及接口信息](https://www.bilibili.com/video/BV1zv411B7nG) [必看]
|
||||
|
||||
[go-admin权限配置使用说明](https://www.bilibili.com/video/BV1rt4y197d3) [必看]
|
||||
|
||||
[go-admin数据权限使用说明](https://www.bilibili.com/video/BV1LK4y1s71e) [必看]
|
||||
|
||||
|
||||
**如有问题请先看上述使用文档和文章,若不能满足,欢迎 issue 和 pr ,视频教程和文档持续更新中**
|
||||
|
||||
## 📦 本地开发
|
||||
|
||||
### 环境要求
|
||||
|
||||
go 1.17
|
||||
|
||||
node版本: v16 +
|
||||
|
||||
npm版本: 6.14.11
|
||||
|
||||
### 开发目录创建
|
||||
|
||||
```bash
|
||||
|
||||
# 创建开发目录
|
||||
mkdir goadmin
|
||||
cd goadmin
|
||||
```
|
||||
|
||||
### 获取代码
|
||||
|
||||
> 重点注意:两个项目必须放在同一文件夹下;
|
||||
|
||||
```bash
|
||||
# 获取后端代码
|
||||
git clone https://github.com/go-admin-team/go-admin.git
|
||||
|
||||
# 获取前端代码
|
||||
git clone https://github.com/go-admin-team/go-admin-ui.git
|
||||
|
||||
```
|
||||
|
||||
|
||||
### 启动说明
|
||||
|
||||
#### 前端启动说明
|
||||
|
||||
```bash
|
||||
# 安装依赖
|
||||
yarn install 或 npm install
|
||||
|
||||
# 运行
|
||||
yarn run dev 或 npm run dev
|
||||
|
||||
# 编译
|
||||
yarn run build 或 npm run build
|
||||
```
|
||||
|
||||
#### 服务端启动说明
|
||||
|
||||
```bash
|
||||
# 进入 go-admin 后端项目
|
||||
cd ./go-admin
|
||||
|
||||
# 编译项目
|
||||
go build
|
||||
|
||||
# 修改配置
|
||||
# 文件路径 go-admin/config/settings.yml
|
||||
vi ./config/setting.yml
|
||||
|
||||
# 1. 配置文件中修改数据库信息
|
||||
# 注意: settings.database 下对应的配置数据
|
||||
# 2. 确认log路径
|
||||
```
|
||||
|
||||
:::tip ⚠️注意 在windows环境如果没有安装中CGO,会出现这个问题;
|
||||
|
||||
```bash
|
||||
E:\go-admin>go build
|
||||
# github.com/mattn/go-sqlite3
|
||||
cgo: exec /missing-cc: exec: "/missing-cc": file does not exist
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```bash
|
||||
D:\Code\go-admin>go build
|
||||
# github.com/mattn/go-sqlite3
|
||||
cgo: exec gcc: exec: "gcc": executable file not found in %PATH%
|
||||
```
|
||||
|
||||
[解决cgo问题进入](https://doc.go-admin.dev/guide/other/faq.html#_5-cgo-exec-missing-cc-exec-missing-cc-file-does-not-exist)
|
||||
|
||||
:::
|
||||
|
||||
#### 初始化数据库,以及服务启动
|
||||
|
||||
``` bash
|
||||
# 首次配置需要初始化数据库资源信息
|
||||
# macOS or linux 下使用
|
||||
$ ./go-admin migrate -c=config/settings.dev.yml
|
||||
|
||||
# ⚠️注意:windows 下使用
|
||||
$ go-admin.exe migrate -c=config/settings.dev.yml
|
||||
|
||||
|
||||
# 启动项目,也可以用IDE进行调试
|
||||
# macOS or linux 下使用
|
||||
$ ./go-admin server -c config/settings.yml
|
||||
|
||||
|
||||
# ⚠️注意:windows 下使用
|
||||
$ go-admin.exe server -c config/settings.yml
|
||||
```
|
||||
|
||||
#### 使用docker 编译启动
|
||||
|
||||
```shell
|
||||
# 编译镜像
|
||||
docker build -t go-admin .
|
||||
|
||||
# 启动容器,第一个go-admin是容器名字,第二个go-admin是镜像名称
|
||||
# -v 映射配置文件 本地路径:容器路径
|
||||
docker run --name go-admin -p 8000:8000 -v /config/settings.yml:/config/settings.yml -d go-admin-server
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 文档生成
|
||||
|
||||
```bash
|
||||
go generate
|
||||
```
|
||||
|
||||
#### 交叉编译
|
||||
```bash
|
||||
# windows
|
||||
env GOOS=windows GOARCH=amd64 go build main.go
|
||||
|
||||
# or
|
||||
# linux
|
||||
env GOOS=linux GOARCH=amd64 go build main.go
|
||||
```
|
||||
|
||||
### UI交互端启动说明
|
||||
|
||||
```bash
|
||||
|
||||
# 安装依赖
|
||||
npm install
|
||||
|
||||
# 建议不要直接使用 cnpm 安装依赖,会有各种诡异的 bug。可以通过如下操作解决 npm 下载速度慢的问题
|
||||
npm install --registry=https://registry.npm.taobao.org
|
||||
|
||||
# 启动服务
|
||||
npm run dev
|
||||
```
|
||||
|
||||
## 🎬 在线体验
|
||||
> admin / 123456
|
||||
|
||||
演示地址:[http://www.go-admin.dev](http://www.go-admin.dev/#/login)
|
||||
|
||||
|
||||
## 📨 互动
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td><img src="https://raw.githubusercontent.com/wenjianzhang/image/master/img/wx.png" width="180px"></td>
|
||||
<td><img src="https://raw.githubusercontent.com/wenjianzhang/image/master/img/qq.png" width="200px"></td>
|
||||
<td><img src="https://raw.githubusercontent.com/wenjianzhang/image/master/img/qq2.png" width="200px"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>微信</td>
|
||||
<td>此群已满</td>
|
||||
<td><a target="_blank" href="https://shang.qq.com/wpa/qunwpa?idkey=0f2bf59f5f2edec6a4550c364242c0641f870aa328e468c4ee4b7dbfb392627b"><img border="0" src="https://pub.idqqimg.com/wpa/images/group.png" alt="go-admin技术交流乙号" title="go-admin技术交流乙号"></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## 💎 主要成员
|
||||
|
||||
<a href="https://github.com/wenjianzhang"> <img src="https://avatars.githubusercontent.com/u/3890175?s=460&u=20eac63daef81588fbac611da676b99859319251&v=4" width="80px"></a>
|
||||
<a href="https://github.com/lwnmengjing"> <img src="https://avatars.githubusercontent.com/u/12806223?s=400&u=a89272dce50100b77b4c0d5c81c718bf78ebb580&v=4" width="80px"></a>
|
||||
<a href="https://github.com/chengxiao"> <img src="https://avatars.githubusercontent.com/u/1379545?s=460&u=557da5503d0ac4a8628df6b4075b17853d5edcd9&v=4" width="80px"></a>
|
||||
<a href="https://github.com/bing127"> <img src="https://avatars.githubusercontent.com/u/31166183?s=460&u=c085bff88df10bb7676c8c0351ba9dcd031d1fb3&v=4" width="80px"></a>
|
||||
|
||||
|
||||
|
||||
## JetBrains 开源证书支持
|
||||
|
||||
`go-admin` 项目一直以来都是在 JetBrains 公司旗下的 GoLand 集成开发环境中进行开发,基于 **free JetBrains Open Source license(s)** 正版免费授权,在此表达我的谢意。
|
||||
|
||||
<a href="https://www.jetbrains.com/?from=kubeadm-ha" target="_blank"><img src="https://raw.githubusercontent.com/panjf2000/illustrations/master/jetbrains/jetbrains-variant-4.png" width="250" align="middle"/></a>
|
||||
|
||||
|
||||
## 🤝 特别感谢
|
||||
1. [chengxiao](https://github.com/chengxiao)
|
||||
2. [gin](https://github.com/gin-gonic/gin)
|
||||
2. [casbin](https://github.com/casbin/casbin)
|
||||
2. [spf13/viper](https://github.com/spf13/viper)
|
||||
2. [gorm](https://github.com/jinzhu/gorm)
|
||||
2. [gin-swagger](https://github.com/swaggo/gin-swagger)
|
||||
2. [jwt-go](https://github.com/dgrijalva/jwt-go)
|
||||
2. [vue-element-admin](https://github.com/PanJiaChen/vue-element-admin)
|
||||
2. [ruoyi-vue](https://gitee.com/y_project/RuoYi-Vue)
|
||||
2. [form-generator](https://github.com/JakHuang/form-generator)
|
||||
|
||||
## 🤟 打赏
|
||||
|
||||
> 如果你觉得这个项目帮助到了你,你可以帮作者买一杯果汁表示鼓励 :tropical_drink:
|
||||
|
||||
<img class="no-margin" src="https://raw.githubusercontent.com/wenjianzhang/image/master/img/pay.png" height="200px" >
|
||||
|
||||
## 🤝 链接
|
||||
[Go开发者成长线路图](http://www.golangroadmap.com/)
|
||||
|
||||
## 🔑 License
|
||||
|
||||
[MIT](https://github.com/go-admin-team/go-admin/blob/master/LICENSE.md)
|
||||
|
||||
Copyright (c) 2020 wenjianzhang
|
||||
16
components.d.ts
vendored
Normal file
16
components.d.ts
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
// @ts-nocheck
|
||||
// Generated by unplugin-vue-components
|
||||
// Read more: https://github.com/vuejs/core/pull/3399
|
||||
import '@vue/runtime-core'
|
||||
|
||||
export {}
|
||||
|
||||
declare module '@vue/runtime-core' {
|
||||
export interface GlobalComponents {
|
||||
DeleteModal: typeof import('./src/components/DeleteModal.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
}
|
||||
}
|
||||
12
index.html
Normal file
12
index.html
Normal file
@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
14
jsconfig.json
Normal file
14
jsconfig.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES6",
|
||||
"module": "commonjs",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"@/*": ["src/*"]
|
||||
}
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
4
mock/index.js
Normal file
4
mock/index.js
Normal file
@ -0,0 +1,4 @@
|
||||
import user from './sys_post'
|
||||
export default [
|
||||
user,
|
||||
]
|
||||
36
mock/sys_post.js
Normal file
36
mock/sys_post.js
Normal file
@ -0,0 +1,36 @@
|
||||
import Mock from 'mockjs'
|
||||
|
||||
const mock = {
|
||||
'list|10': [
|
||||
{
|
||||
'roleId|+1': 1,
|
||||
'roleName|1': [
|
||||
'系统管理员',
|
||||
'福州系统运维',
|
||||
'产品管理',
|
||||
'前端开发',
|
||||
'后端开发'
|
||||
],
|
||||
'roleKey': 'admin',
|
||||
'roleSort|+1': 0,
|
||||
'status|0-1': 0,
|
||||
'createAt': `${Mock.Random.date()} ${Mock.Random.time()}`
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
const login = {
|
||||
url: '/api/admin/post',
|
||||
method: 'get',
|
||||
timeout: 500,
|
||||
statusCode: 200,
|
||||
response: {
|
||||
code: 200,
|
||||
message: '登陆成功',
|
||||
data: Mock.toJSONSchema(mock).template
|
||||
}
|
||||
}
|
||||
|
||||
export default [
|
||||
login,
|
||||
]
|
||||
40
package.json
Normal file
40
package.json
Normal file
@ -0,0 +1,40 @@
|
||||
{
|
||||
"name": "quark_arco",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "^5.0.1",
|
||||
"@codemirror/lang-html": "^6.4.3",
|
||||
"@codemirror/lang-javascript": "^6.1.7",
|
||||
"@codemirror/lang-json": "^6.0.1",
|
||||
"@codemirror/theme-one-dark": "^6.1.2",
|
||||
"@vueuse/core": "^10.1.2",
|
||||
"axios": "^1.4.0",
|
||||
"codemirror": "^6.0.1",
|
||||
"cropperjs": "^1.5.13",
|
||||
"i": "^0.3.7",
|
||||
"js-cookie": "^3.0.5",
|
||||
"npm": "^9.6.6",
|
||||
"pinia": "^2.0.36",
|
||||
"vue": "^3.2.47",
|
||||
"vue-codemirror": "^6.1.1",
|
||||
"vue-router": "^4.1.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@arco-design/web-vue": "^2.45.3",
|
||||
"@vitejs/plugin-vue": "^4.2.1",
|
||||
"unplugin-vue-components": "^0.24.1",
|
||||
"eslint": "^8.40.0",
|
||||
"eslint-plugin-vue": "^9.12.0",
|
||||
"mockjs": "^1.1.0",
|
||||
"sass": "^1.62.1",
|
||||
"vite": "^4.3.5",
|
||||
"vite-plugin-mock": "^3.0.0",
|
||||
"vite-svg-loader": "^4.0.0"
|
||||
}
|
||||
}
|
||||
19
prettier.config.js
Normal file
19
prettier.config.js
Normal file
@ -0,0 +1,19 @@
|
||||
module.exports = {
|
||||
"arrowParens": "always",
|
||||
"bracketSameLine": false,
|
||||
"bracketSpacing": true,
|
||||
"embeddedLanguageFormatting": "auto",
|
||||
"htmlWhitespaceSensitivity": "css",
|
||||
"insertPragma": false,
|
||||
"jsxSingleQuote": false,
|
||||
"printWidth": 80,
|
||||
"proseWrap": "preserve",
|
||||
"quoteProps": "as-needed",
|
||||
"requirePragma": false,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5",
|
||||
"useTabs": false,
|
||||
"vueIndentScriptAndStyle": false
|
||||
}
|
||||
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
BIN
public/login_left_bg.jpg
Normal file
BIN
public/login_left_bg.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 257 KiB |
3
src/App.vue
Normal file
3
src/App.vue
Normal file
@ -0,0 +1,3 @@
|
||||
<template>
|
||||
<router-view></router-view>
|
||||
</template>
|
||||
30
src/api/admin/login.js
Normal file
30
src/api/admin/login.js
Normal file
@ -0,0 +1,30 @@
|
||||
import request from '../../utils/request';
|
||||
export function login(data) {
|
||||
return request({
|
||||
url:'/api/v1/login',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function getCaptcha() {
|
||||
return request({
|
||||
url:'/api/v1/captcha',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function getAppConfig() {
|
||||
return request({
|
||||
url:'/api/v1/app-config',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 根据角色获取菜单
|
||||
export function getUserMenuRole() {
|
||||
return request({
|
||||
url:'/api/v1/menurole',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
48
src/api/admin/menu.js
Normal file
48
src/api/admin/menu.js
Normal file
@ -0,0 +1,48 @@
|
||||
import request from '../../utils/request'
|
||||
|
||||
const url = '/api/v1/menu';
|
||||
|
||||
export function getMenu(params) {
|
||||
return request({
|
||||
url,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function getMenuDetails(menuId) {
|
||||
return request({
|
||||
url: `${url}/${menuId}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function addMenu(data) {
|
||||
return request({
|
||||
url,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function removeMenu(data) {
|
||||
return request({
|
||||
url,
|
||||
method: 'delete',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改菜单
|
||||
* @param {Object} data
|
||||
* @param {Number} id
|
||||
* @returns
|
||||
*/
|
||||
export function updateMenu(data, id) {
|
||||
return request({
|
||||
url: `${url}/${id}`,
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
35
src/api/admin/post.js
Normal file
35
src/api/admin/post.js
Normal file
@ -0,0 +1,35 @@
|
||||
import request from '../../utils/request'
|
||||
|
||||
const url = '/api/v1/post';
|
||||
|
||||
export function getPost(params) {
|
||||
return request({
|
||||
url,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function addPost(data) {
|
||||
return request({
|
||||
url,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function removePost(data) {
|
||||
return request({
|
||||
url,
|
||||
method: 'delete',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function updatePost(data, id) {
|
||||
return request({
|
||||
url: `${url}/${id}`,
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
50
src/api/admin/role.js
Normal file
50
src/api/admin/role.js
Normal file
@ -0,0 +1,50 @@
|
||||
import request from '../../utils/request';
|
||||
|
||||
const url = '/api/v1/role';
|
||||
export function getRole(params) {
|
||||
return request({
|
||||
url,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function addRole(data) {
|
||||
return request({
|
||||
url,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function removeRole(data) {
|
||||
return request({
|
||||
url,
|
||||
method: 'delete',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function updateRole(data, id) {
|
||||
return request({
|
||||
url: `${url}/${id}`,
|
||||
method: 'put',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
export function updateRoleScoped(data) {
|
||||
return request({
|
||||
url:'/api/v1/roledatascope',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function getRoleMenuTree(params, roleId) {
|
||||
return request({
|
||||
url:`/api/v1/roleMenuTreeselect/${roleId}`,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
35
src/api/admin/sys-api.js
Normal file
35
src/api/admin/sys-api.js
Normal file
@ -0,0 +1,35 @@
|
||||
import request from '../../utils/request';
|
||||
|
||||
const url = '/api/v1/sys-api';
|
||||
|
||||
export function getSysApi(params) {
|
||||
return request({
|
||||
url,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function addSysApi(data) {
|
||||
return request({
|
||||
url,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function removeSysApi(data) {
|
||||
return request({
|
||||
url,
|
||||
method: 'delete',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function updateSysApi(data, id) {
|
||||
return request({
|
||||
url: `${url}/${id}`,
|
||||
method: 'put',
|
||||
data,
|
||||
})
|
||||
}
|
||||
35
src/api/admin/sys-config.js
Normal file
35
src/api/admin/sys-config.js
Normal file
@ -0,0 +1,35 @@
|
||||
import request from '../../utils/request';
|
||||
|
||||
const url = '/api/v1/config';
|
||||
|
||||
export function getSysConfig(params) {
|
||||
return request({
|
||||
url,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function addSysConfig(data) {
|
||||
return request({
|
||||
url,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function removeSysConfig(data) {
|
||||
return request({
|
||||
url,
|
||||
method: 'delete',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function updateSysConfig(data, id) {
|
||||
return request({
|
||||
url: `${url}/${id}`,
|
||||
method: 'put',
|
||||
data,
|
||||
})
|
||||
}
|
||||
35
src/api/admin/sys-dept.js
Normal file
35
src/api/admin/sys-dept.js
Normal file
@ -0,0 +1,35 @@
|
||||
import request from '../../utils/request';
|
||||
|
||||
const url = '/api/v1/dept';
|
||||
|
||||
export function getDept(params) {
|
||||
return request({
|
||||
url,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function addDept(data) {
|
||||
return request({
|
||||
url,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function removeDept(data) {
|
||||
return request({
|
||||
url,
|
||||
method: 'delete',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function updateDept(data, id) {
|
||||
return request({
|
||||
url: `${url}/${id}`,
|
||||
method: 'put',
|
||||
data,
|
||||
})
|
||||
}
|
||||
35
src/api/admin/sys-dict-data.js
Normal file
35
src/api/admin/sys-dict-data.js
Normal file
@ -0,0 +1,35 @@
|
||||
import request from '@/utils/request';
|
||||
|
||||
const url = '/api/v1/dict/data';
|
||||
|
||||
export function getDictData(params) {
|
||||
return request({
|
||||
url,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function addDictData(data) {
|
||||
return request({
|
||||
url,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function updateDictData(data, dictCode) {
|
||||
return request({
|
||||
url: `${url}/${dictCode}`,
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteDictData(data) {
|
||||
return request({
|
||||
url,
|
||||
method: 'delete',
|
||||
data
|
||||
})
|
||||
}
|
||||
43
src/api/admin/sys-dict.js
Normal file
43
src/api/admin/sys-dict.js
Normal file
@ -0,0 +1,43 @@
|
||||
import request from '../../utils/request';
|
||||
|
||||
const url = '/api/v1/dict/type';
|
||||
|
||||
export function getDictType(params) {
|
||||
return request({
|
||||
url,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function addDictType(data) {
|
||||
return request({
|
||||
url,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function removeDictType(data) {
|
||||
return request({
|
||||
url,
|
||||
method: 'delete',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function updateDictType(data, id) {
|
||||
return request({
|
||||
url: `${url}/${id}`,
|
||||
method: 'put',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
// 获取字典选择框列表
|
||||
export function optionselect() {
|
||||
return request({
|
||||
url: '/api/v1/dict/type-option-select',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
19
src/api/admin/sys-login-log.js
Normal file
19
src/api/admin/sys-login-log.js
Normal file
@ -0,0 +1,19 @@
|
||||
import request from '../../utils/request';
|
||||
|
||||
const url = '/api/v1/sys-login-log';
|
||||
|
||||
export function getSysLoginLog(params) {
|
||||
return request({
|
||||
url,
|
||||
method: 'GET',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function removeSysLoginLog(data) {
|
||||
return request({
|
||||
url,
|
||||
method: 'DELETE',
|
||||
data
|
||||
})
|
||||
}
|
||||
19
src/api/admin/sys-opera-log.js
Normal file
19
src/api/admin/sys-opera-log.js
Normal file
@ -0,0 +1,19 @@
|
||||
import request from '../../utils/request';
|
||||
|
||||
const url = '/api/v1/sys-opera-log';
|
||||
|
||||
export function getSysOperaLog(params) {
|
||||
return request({
|
||||
url,
|
||||
method: 'get',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
export function removeSysOperaLog(data) {
|
||||
return request({
|
||||
url,
|
||||
method: 'delete',
|
||||
data,
|
||||
});
|
||||
}
|
||||
67
src/api/admin/sys-user.js
Normal file
67
src/api/admin/sys-user.js
Normal file
@ -0,0 +1,67 @@
|
||||
import request from '../../utils/request';
|
||||
|
||||
const url = '/api/v1/sys-user';
|
||||
|
||||
|
||||
export function getUser(params) {
|
||||
return request({
|
||||
url,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function getInfo() {
|
||||
return request({
|
||||
url:'/api/v1/getinfo',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function addUser(data) {
|
||||
return request({
|
||||
url,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function removeUser(data) {
|
||||
return request({
|
||||
url,
|
||||
method: 'delete',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function updateUser(data) {
|
||||
return request({
|
||||
url,
|
||||
method: 'put',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
export function updateUserStatus(data) {
|
||||
return request({
|
||||
url: '/api/v1/user/status',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function resetUserPwd(data) {
|
||||
return request({
|
||||
url:'/api/v1/user/pwd/reset',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 获取当前登录用户信息
|
||||
export function getCurrentUser(uid) {
|
||||
return request({
|
||||
url: `${url}/${uid}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
18
src/api/profile/profile.js
Normal file
18
src/api/profile/profile.js
Normal file
@ -0,0 +1,18 @@
|
||||
import request from '../../utils/request';
|
||||
|
||||
// 获取用户信息
|
||||
export function getUserProfile() {
|
||||
return request({
|
||||
url: '/api/v1/user/profile',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 修改用户密码
|
||||
export function putUserPwd(data) {
|
||||
return request({
|
||||
url: '/api/v1/user/pwd/set',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
62
src/api/sys-job.js
Normal file
62
src/api/sys-job.js
Normal file
@ -0,0 +1,62 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询SysJob列表
|
||||
export function listSysJob(query) {
|
||||
return request({
|
||||
url: '/api/v1/sysjob',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询SysJob详细
|
||||
export function getSysJob(jobId) {
|
||||
return request({
|
||||
url: '/api/v1/sysjob/' + jobId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增SysJob
|
||||
export function addSysJob(data) {
|
||||
return request({
|
||||
url: '/api/v1/sysjob',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改SysJob
|
||||
export function updateSysJob(data) {
|
||||
return request({
|
||||
url: '/api/v1/sysjob',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除SysJob
|
||||
export function delSysJob(data) {
|
||||
return request({
|
||||
url: '/api/v1/sysjob',
|
||||
method: 'delete',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 移除SysJob
|
||||
export function removeJob(jobId) {
|
||||
return request({
|
||||
url: '/api/v1/job/remove/' + jobId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 启动SysJob
|
||||
export function startJob(jobId) {
|
||||
return request({
|
||||
url: '/api/v1/job/start/' + jobId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
10
src/api/sys-tools/monitor.js
Normal file
10
src/api/sys-tools/monitor.js
Normal file
@ -0,0 +1,10 @@
|
||||
import request from '../../utils/request'
|
||||
|
||||
const url = '/api/v1/server-monitor';
|
||||
|
||||
export function getServerMonitor() {
|
||||
return request({
|
||||
url,
|
||||
method: 'GET',
|
||||
})
|
||||
}
|
||||
103
src/api/tools/gen.js
Normal file
103
src/api/tools/gen.js
Normal file
@ -0,0 +1,103 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询生成表数据
|
||||
export function listTable(query) {
|
||||
return request({
|
||||
url: '/api/v1/sys/tables/page',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
// 查询db数据库列表
|
||||
export function listDbTable(query) {
|
||||
return request({
|
||||
url: '/api/v1/db/tables/page',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询表详细信息
|
||||
export function getGenTable(tableId) {
|
||||
return request({
|
||||
url: '/api/v1/sys/tables/info/' + tableId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function getGenTableInfo(tablename) {
|
||||
return request({
|
||||
url: '/api/v1/sys/tables?tableName=' + tablename,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 修改代码生成信息
|
||||
export function updateGenTable(data) {
|
||||
return request({
|
||||
url: '/api/v1/sys/tables/info',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 导入表
|
||||
export function importTable(data) {
|
||||
return request({
|
||||
url: '/api/v1/sys/tables/info',
|
||||
method: 'post',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
// 预览生成代码
|
||||
export function previewTable(tableId) {
|
||||
return request({
|
||||
url: '/api/v1/gen/preview/' + tableId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
// 删除表数据
|
||||
export function delTable(tableId) {
|
||||
return request({
|
||||
url: '/api/v1/sys/tables/info/' + tableId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 生成代码到项目
|
||||
export function toProjectTable(tableId) {
|
||||
return request({
|
||||
url: '/api/v1/gen/toproject/' + tableId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 生成接口数据到迁移脚本
|
||||
export function apiToFile(tableId) {
|
||||
return request({
|
||||
url: '/api/v1/gen/apitofile/' + tableId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function toProjectTableCheckRole(tableId, ischeckrole) {
|
||||
return request({
|
||||
url: '/api/v1/gen/toproject/' + tableId + '?ischeckrole=' + ischeckrole,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 生成菜单到数据库
|
||||
export function toDBTable(tableId) {
|
||||
return request({
|
||||
url: '/api/v1/gen/todb/' + tableId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function getTableTree() {
|
||||
return request({
|
||||
url: '/api/v1/gen/tabletree',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
104
src/components/DeleteModal.vue
Normal file
104
src/components/DeleteModal.vue
Normal file
@ -0,0 +1,104 @@
|
||||
<template>
|
||||
<a-modal v-model:visible="deleteVisible" :title="data.length > 1 ? '批量删除' : '删除'" title-align="start" @close="handleClose" width="400px">
|
||||
<a-form :model="data" :rules="rules" ref="modalFormRef" size="medium" label-align="left" auto-label-width>
|
||||
<a-alert type="warning" style="margin-bottom: 16px;" v-if="data.length > 1">您正在批量删除,请确认删除数据是否正确</a-alert>
|
||||
<a-form-item label="摘要" label-col-flex="100px">
|
||||
<a-tag color="gray" style="padding: 0 0px; margin-right: 5px">
|
||||
<template #icon>
|
||||
<a-tag color="arcoblue">数量</a-tag>
|
||||
</template>
|
||||
<a-tag style="margin-left: -4px">{{ data.length }}</a-tag>
|
||||
</a-tag>
|
||||
</a-form-item>
|
||||
<a-form-item field="deleteConfirmation" label="删除确认" label-col-flex="100px">
|
||||
<a-input v-model.trim="data.deleteConfirmation" placeholder="请输入 DELETE 以确认删除"></a-input>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<template #footer>
|
||||
<a-button @click="handleClose"><template #icon><icon-close /></template>取消</a-button>
|
||||
<a-button type="primary" @click="handleConfirm"><template #icon><icon-check /></template>确认</a-button>
|
||||
</template>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, toRefs, watch, unref, getCurrentInstance } from 'vue';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const props = defineProps({
|
||||
// 数组名称
|
||||
data: {
|
||||
type: Array,
|
||||
},
|
||||
// 是否显示
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
// 更新接口
|
||||
apiDelete: {
|
||||
type: Function,
|
||||
},
|
||||
});
|
||||
|
||||
const { data, visible, apiDelete } = toRefs(props);
|
||||
|
||||
const emits = defineEmits(['deleteVisibleChange']);
|
||||
|
||||
// Akiraka 20230210 对话框弹出
|
||||
const deleteVisible = ref(false);
|
||||
|
||||
// Akiraka 20230210 删除数据校验
|
||||
const rules = reactive({
|
||||
deleteConfirmation: [
|
||||
{
|
||||
required: true,
|
||||
validator: (value, cb) => {
|
||||
return new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
if ( "DELETE" !== value) {
|
||||
cb('请输入 DELETE')
|
||||
}
|
||||
resolve()
|
||||
}, 500)
|
||||
})
|
||||
}
|
||||
}
|
||||
],
|
||||
});
|
||||
|
||||
// Akiraka 20230210 关闭弹窗
|
||||
const handleClose = () => {
|
||||
emits('deleteVisibleChange', deleteVisible.value = false);
|
||||
// Akiraka 20230210 关闭弹窗
|
||||
deleteVisible.value = false;
|
||||
}
|
||||
|
||||
// Akiraka 20230210 监听事件
|
||||
watch(() => props.visible,(value) => {
|
||||
if (value) {
|
||||
// Akiraka 20230210 打开或关闭对话框
|
||||
deleteVisible.value = value;
|
||||
}
|
||||
})
|
||||
|
||||
// Akiraka 20230210 确认按钮 => 开始数据检查
|
||||
const handleConfirm = () => {
|
||||
proxy.$refs.modalFormRef.validate((valid) => {
|
||||
if (!valid) {
|
||||
// Akiraka 20230210 请求接口
|
||||
props.apiDelete({ ids: data.value }).then(response => {
|
||||
// Akiraka 20230210 关闭弹窗
|
||||
deleteVisible.value = false;
|
||||
Message.success("数据删除成功");
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
</style>
|
||||
22
src/directive/permission/permission.js
Normal file
22
src/directive/permission/permission.js
Normal file
@ -0,0 +1,22 @@
|
||||
import { useUserStore } from '@/store/userInfo';
|
||||
|
||||
export default {
|
||||
checkPermission(el, binding) {
|
||||
const store = useUserStore();
|
||||
const { value } = binding;
|
||||
const all_permission = '*:*:*'
|
||||
const permissions = store.userInfo && store.userInfo.permissions;
|
||||
|
||||
if (typeof value === 'string') {
|
||||
const hasPermission = permissions.some((permission) => {
|
||||
return all_permission === permission || value === permission;
|
||||
})
|
||||
|
||||
if (!hasPermission) {
|
||||
el.parentNode && el.parentNode.removeChild(el);
|
||||
}
|
||||
} else {
|
||||
throw new Error(`请设置操作权限标签值`)
|
||||
}
|
||||
}
|
||||
}
|
||||
0
src/icons/index.js
Normal file
0
src/icons/index.js
Normal file
1
src/icons/svg/api-management.svg
Normal file
1
src/icons/svg/api-management.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg width="24" height="24" viewBox="0 0 48 48" fill="currentColor"><path fill-rule="evenodd" clip-rule="evenodd" d="M4 15a1 1 0 001 1h2a1 1 0 001-1V8h7a1 1 0 001-1V5a1 1 0 00-1-1H6a2 2 0 00-2 2v9zm4 18a1 1 0 00-1-1H5a1 1 0 00-1 1v9a2 2 0 002 2h9a1 1 0 001-1v-2a1 1 0 00-1-1H8v-7zm35-17a1 1 0 001-1V6a2 2 0 00-2-2h-9a1 1 0 00-1 1v2a1 1 0 001 1h7v7a1 1 0 001 1h2zm1 17a1 1 0 00-1-1h-2a1 1 0 00-1 1v7h-7a1 1 0 00-1 1v2a1 1 0 001 1h9a2 2 0 002-2v-9zM32.835 11h-6.108c-6.512 0-11.882 4.804-12.636 11h-1.992c-.382 0-.52.046-.66.134a.855.855 0 00-.325.378c-.074.162-.114.324-.114.77v1.436c0 .446.04.608.114.77.075.163.185.291.324.378.14.088.279.134.66.134h2.157c1.179 5.706 6.315 10 12.472 10h6.108c.405 0 .552-.041.7-.12a.819.819 0 00.344-.337c.079-.145.121-.29.121-.688V31h.901c.382 0 .52-.046.66-.134a.855.855 0 00.325-.378c.074-.162.114-.324.114-.77v-1.436c0-.446-.04-.608-.114-.77a.855.855 0 00-.325-.378c-.14-.088-.278-.134-.66-.134H34v-7h.901c.382 0 .52-.046.66-.134a.855.855 0 00.325-.378c.074-.162.114-.324.114-.77v-1.436c0-.446-.04-.608-.114-.77a.855.855 0 00-.325-.378c-.14-.088-.278-.134-.66-.134H34v-3.855c0-.398-.042-.543-.121-.688a.819.819 0 00-.344-.338c-.148-.078-.295-.119-.7-.119zm-2.744 3.571h-3.637c-5.02 0-9.09 3.998-9.09 8.929s4.07 8.929 9.09 8.929h3.637V14.57z" fill="currentColor"/></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
12
src/layout/components/AppMain.vue
Normal file
12
src/layout/components/AppMain.vue
Normal file
@ -0,0 +1,12 @@
|
||||
<template>
|
||||
<div class="app-main">
|
||||
<router-view v-slot="{ Component, route }">
|
||||
<transition name="fade-transform" mode="out-in">
|
||||
<component :is="Component" :key="route" />
|
||||
</transition>
|
||||
</router-view>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
</style>
|
||||
76
src/layout/components/Avatar/index.vue
Normal file
76
src/layout/components/Avatar/index.vue
Normal file
@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<a-dropdown position="bl" :style="{ top: '52px' }">
|
||||
<div class="avatar-container">
|
||||
<a-avatar :size="32" :style="{ backgroundColor: '#3370ff' }">
|
||||
<img alt="avatar" :src="userInfo.avatar" />
|
||||
</a-avatar>
|
||||
<div class="user-info">
|
||||
<div class="user-info-name">{{ userInfo.name }}</div>
|
||||
<!-- <div class="user-info-desc">{{ userInfo.introduction }}</div> -->
|
||||
</div>
|
||||
</div>
|
||||
<template #content>
|
||||
<a-doption @click="$router.push('/profile')">
|
||||
<template #icon>
|
||||
<icon-settings />
|
||||
</template>
|
||||
<template #default>用户设置</template>
|
||||
</a-doption>
|
||||
<a-doption @click="handleLogout()">
|
||||
<template #icon>
|
||||
<icon-export />
|
||||
</template>
|
||||
<template #default>退出登陆</template>
|
||||
</a-doption>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { getCurrentInstance } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import {
|
||||
IconSettings,
|
||||
IconExport,
|
||||
} from '@arco-design/web-vue/es/icon';
|
||||
import { useUserStore } from '@/store/userInfo';
|
||||
import { clearLocalStorage } from '@/utils/storage';
|
||||
|
||||
const store = useUserStore();
|
||||
const { userInfo } = storeToRefs(store);
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const handleLogout = () => {
|
||||
proxy.$modal.warning({
|
||||
title: '提示',
|
||||
content: '确定注销并退出登陆系统吗?',
|
||||
hideCancel: false,
|
||||
onOk: () => {
|
||||
window.sessionStorage.removeItem('token');
|
||||
clearLocalStorage();
|
||||
proxy.$router.push('/login');
|
||||
},
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.avatar-container {
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
.user-info {
|
||||
margin-left: 10px;
|
||||
&-name {
|
||||
color: var(--color-text-1);
|
||||
font-weight: 700;
|
||||
padding-top: 8px;
|
||||
}
|
||||
&-desc {
|
||||
color: var(--color-text-1);
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
95
src/layout/components/Menu/Menu.vue
Normal file
95
src/layout/components/Menu/Menu.vue
Normal file
@ -0,0 +1,95 @@
|
||||
<template>
|
||||
<div class="sider-logo">
|
||||
<img :src="store.sysConfig.sys_app_logo" />
|
||||
<span class="sider-title" v-if="!props.collapsed">{{store.sysConfig.sys_app_name}}</span>
|
||||
</div>
|
||||
<a-menu
|
||||
class="menu"
|
||||
@menu-item-click="handleMenuClick"
|
||||
:default-open-keys="['/admin']"
|
||||
:default-selected-keys="defaultSelectKeys"
|
||||
:auto-open-selected="true"
|
||||
>
|
||||
<sub-menu :menu-list="permissionStore.menuList" />
|
||||
</a-menu>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onBeforeMount } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { useUserStore } from '@/store/userInfo';
|
||||
import { usePermissionStore } from '@/store/permission';
|
||||
|
||||
import SubMenu from './SubMenu.vue';
|
||||
|
||||
const store = useUserStore();
|
||||
const permissionStore = usePermissionStore();
|
||||
|
||||
const props = defineProps({
|
||||
collapsed: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
// 默认菜单选中
|
||||
const defaultSelectKeys = ref([]);
|
||||
|
||||
// 刷新保持菜单选中
|
||||
const keepDefaultSelect = () => {
|
||||
defaultSelectKeys.value = [];
|
||||
defaultSelectKeys.value.push(route.fullPath);
|
||||
};
|
||||
|
||||
const handleMenuClick = (key) => {
|
||||
router.push(key);
|
||||
};
|
||||
|
||||
onBeforeMount(() => {
|
||||
keepDefaultSelect();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.sider-logo {
|
||||
margin: 10px 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid var(--color-text-4);
|
||||
& img {
|
||||
height: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
.sider-title {
|
||||
margin-left: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 16px;
|
||||
color: var(--color-text-4);
|
||||
}
|
||||
|
||||
.left-side {
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
font-size: 18px;
|
||||
line-height: 50px;
|
||||
text-align: center;
|
||||
transition: all 0.3s ease-in-out;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: #e5e5e5;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
.arco-menu-indent {
|
||||
width: 30px;
|
||||
}
|
||||
</style>
|
||||
37
src/layout/components/Menu/SubMenu.vue
Normal file
37
src/layout/components/Menu/SubMenu.vue
Normal file
@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<template v-for="menu in props.menuList" :key="menu.menuId">
|
||||
<a-sub-menu
|
||||
v-if="menu.children && menu.menuType == 'M' && menu.visible == 0"
|
||||
:key="menu.path"
|
||||
>
|
||||
<template #icon>
|
||||
<component :is="menu.icon" />
|
||||
</template>
|
||||
<template #title>{{ menu.title }}</template>
|
||||
<sub-menu :menuList="menu.children" />
|
||||
</a-sub-menu>
|
||||
<a-menu-item
|
||||
v-if="menu.menuType == 'C' && menu.visible && isRouteParams(menu.path)"
|
||||
:key="menu.path"
|
||||
>{{ menu.title }}</a-menu-item>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
menuList: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
|
||||
// 判断是否路由需要带参匹配
|
||||
const isRouteParams = computed(() => {
|
||||
return (path) => {
|
||||
if (path.indexOf(':') == -1) return true;
|
||||
return false;
|
||||
}
|
||||
})
|
||||
</script>
|
||||
93
src/layout/components/Navbar.vue
Normal file
93
src/layout/components/Navbar.vue
Normal file
@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<div class="navbar">
|
||||
<div class="left-side" @click="onCollapse">
|
||||
<icon-menu-fold v-if="!props.collapsed" />
|
||||
<icon-menu-unfold v-else />
|
||||
</div>
|
||||
<ul class="right-side">
|
||||
<li>
|
||||
<a-button shape="circle" @click="handleDarkTheme">
|
||||
<template #icon>
|
||||
<component
|
||||
:is="!darkTheme ? IconSunFill : IconMoonFill"
|
||||
></component>
|
||||
</template>
|
||||
</a-button>
|
||||
</li>
|
||||
<li>
|
||||
<Avatar />
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import Avatar from './Avatar/index.vue';
|
||||
import {
|
||||
IconMenuFold,
|
||||
IconMenuUnfold,
|
||||
IconSunFill,
|
||||
IconMoonFill,
|
||||
} from '@arco-design/web-vue/es/icon';
|
||||
|
||||
const darkTheme = ref(false);
|
||||
|
||||
const props = defineProps({
|
||||
collapsed: Boolean,
|
||||
});
|
||||
|
||||
const emit = defineEmits(['onCollapse']);
|
||||
|
||||
const onCollapse = () => {
|
||||
emit('onCollapse');
|
||||
};
|
||||
|
||||
// 切换亮色和暗色
|
||||
const handleDarkTheme = () => {
|
||||
const theme = document.body.getAttribute('arco-theme');
|
||||
if (!theme) {
|
||||
document.body.setAttribute('arco-theme', 'dark');
|
||||
darkTheme.value = true;
|
||||
} else {
|
||||
document.body.removeAttribute('arco-theme');
|
||||
darkTheme.value = false;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.navbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
height: 50px;
|
||||
border-bottom: 1px solid #e5e6eb;
|
||||
box-shadow: 0 2px 5px 0 rgba(0,0,0, .1);
|
||||
|
||||
.left-side {
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
font-size: 18px;
|
||||
line-height: 50px;
|
||||
text-align: center;
|
||||
transition: all 0.3s ease-in-out;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: #e5e5e5;
|
||||
}
|
||||
}
|
||||
|
||||
.right-side {
|
||||
list-style: none;
|
||||
display: flex;
|
||||
padding-right: 30px;
|
||||
|
||||
& > li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
2
src/layout/components/index.js
Normal file
2
src/layout/components/index.js
Normal file
@ -0,0 +1,2 @@
|
||||
export { default as AppMain } from './AppMain.vue';
|
||||
export { default as Navbar } from './Navbar.vue';
|
||||
37
src/layout/index.vue
Normal file
37
src/layout/index.vue
Normal file
@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<a-layout :style="{ height: '100vh' }">
|
||||
<a-layout-sider theme="dark" breakpoint="lg" :width="220" collapsible :collapsed="collapsed" @collapse="onCollapse">
|
||||
<Menu :collapsed="collapsed" />
|
||||
</a-layout-sider>
|
||||
<a-layout>
|
||||
<a-layout-header>
|
||||
<Navbar :collapsed="collapsed" @on-collapse="onCollapse" />
|
||||
</a-layout-header>
|
||||
<a-layout-content class="layout-content">
|
||||
<AppMain />
|
||||
</a-layout-content>
|
||||
</a-layout>
|
||||
</a-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { AppMain, Navbar } from './components';
|
||||
import Menu from './components/Menu/Menu.vue';
|
||||
|
||||
const collapsed = ref(false);
|
||||
const onCollapse = () => {
|
||||
collapsed.value = !collapsed.value;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import '../style/index.scss';
|
||||
@import '../style/transition.scss';
|
||||
@import '../style/dark-theme.scss';
|
||||
|
||||
.layout-content {
|
||||
padding: 16px;
|
||||
background-color: #f2f3f5;
|
||||
}
|
||||
</style>
|
||||
39
src/main.js
Normal file
39
src/main.js
Normal file
@ -0,0 +1,39 @@
|
||||
import { createApp } from 'vue';
|
||||
import { createPinia } from 'pinia';
|
||||
import App from './App.vue';
|
||||
import ArcoVue from '@arco-design/web-vue';
|
||||
import { Message, Modal, Notification } from '@arco-design/web-vue';
|
||||
import '@arco-design/web-vue/dist/arco.css';
|
||||
import router from './router/';
|
||||
import { parseTime } from '@/utils/parseTime';
|
||||
|
||||
// Directive
|
||||
import permission from '@/directive/permission/permission';
|
||||
|
||||
// 引入 Arco 图标库
|
||||
import * as ArcoIconModules from '@arco-design/web-vue/es/icon';
|
||||
|
||||
console.log(import.meta.env);
|
||||
|
||||
// Initialize the Pinia instance
|
||||
const pinia = createPinia();
|
||||
const app = createApp(App);
|
||||
|
||||
app.directive('has', permission.checkPermission);
|
||||
|
||||
// 挂载全局变量
|
||||
app.config.globalProperties.message = Message;
|
||||
app.config.globalProperties.modal = Modal;
|
||||
app.config.globalProperties.notification = Notification;
|
||||
app.config.globalProperties.parseTime = parseTime;
|
||||
|
||||
// 挂载全局图标
|
||||
for(const name in ArcoIconModules){
|
||||
app.component(name,ArcoIconModules[name])
|
||||
}
|
||||
|
||||
app.use(ArcoVue);
|
||||
app.use(router);
|
||||
app.use(pinia);
|
||||
app.mount('#app');
|
||||
|
||||
115
src/router/index.js
Normal file
115
src/router/index.js
Normal file
@ -0,0 +1,115 @@
|
||||
import { createWebHashHistory, createRouter, createWebHistory } from 'vue-router';
|
||||
import Layout from '../layout/index.vue';
|
||||
import { useUserStore } from '../store/userInfo';
|
||||
import { usePermissionStore } from '../store/permission';
|
||||
import Watermark from '@/utils/watermark.js';
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/',
|
||||
name: '/',
|
||||
redirect: 'admin',
|
||||
component: Layout,
|
||||
children: [
|
||||
{
|
||||
path: '/profile',
|
||||
name: 'profile',
|
||||
component: () => import('../views/profile/index.vue'),
|
||||
meta: {
|
||||
title: '个人设置',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/403',
|
||||
name: '403',
|
||||
component: () => import('../views/error-page/403.vue'),
|
||||
meta: {
|
||||
title: '找不到页面',
|
||||
},
|
||||
},
|
||||
{
|
||||
hide: true,
|
||||
path: '/:catchAll(.*)',
|
||||
component: () => import('../views/error-page/404.vue'),
|
||||
meta: {
|
||||
title: '找不到页面',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/500',
|
||||
name: '500',
|
||||
component: () => import('../views/error-page/500.vue'),
|
||||
meta: {
|
||||
title: '找不到页面',
|
||||
},
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
name: 'login',
|
||||
component: () => import('../views/login/index.vue'),
|
||||
},
|
||||
];
|
||||
|
||||
const router = createRouter({
|
||||
// createWebHashHistory URL 带井号
|
||||
// createWebHistory URL 去井号
|
||||
history: createWebHistory(),
|
||||
routes: routes,
|
||||
});
|
||||
|
||||
// beforeEach router
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
const store = useUserStore();
|
||||
|
||||
const permissionStore = usePermissionStore();
|
||||
|
||||
// 获取系统配置信息
|
||||
await store.getSysConfig();
|
||||
|
||||
// 判断用户Token是否获取
|
||||
if (to.name !== 'login' && !store.token) {
|
||||
next({ name: 'login' });
|
||||
} else {
|
||||
// 判断判断权限有无获取
|
||||
if (store.token && store.roles.length === 0) {
|
||||
store.getUserInfo();
|
||||
await permissionStore.getMenuRole();
|
||||
|
||||
permissionStore.addRouters.forEach((route) => {
|
||||
router.addRoute('/', route);
|
||||
});
|
||||
// next(to.fullPath);
|
||||
// 如果 addRoute 并未完成,路由守卫会一层一层的执行执行,直到 addRoute 完成,找到对应的路由
|
||||
next({ ...to, replace: true })
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
// afterEach Router
|
||||
router.afterEach((to) => {
|
||||
const store = useUserStore();
|
||||
|
||||
// 修改网页标题
|
||||
if (to.name !== 'login') {
|
||||
document.title = `${to.meta.title} - ${store.sysConfig.sys_app_name}`;
|
||||
} else {
|
||||
document.title = store.sysConfig.sys_app_name;
|
||||
}
|
||||
|
||||
// Vincent 2023004 修复加载水印的bug
|
||||
if (store.userInfo != undefined){
|
||||
if ( store.userInfo.name != undefined ) {
|
||||
Watermark.set(store.userInfo.name)
|
||||
} else {
|
||||
Watermark.out() // 清除水印
|
||||
}
|
||||
} else{
|
||||
Watermark.out() // 清除水印
|
||||
}
|
||||
});
|
||||
export default router;
|
||||
60
src/store/permission.js
Normal file
60
src/store/permission.js
Normal file
@ -0,0 +1,60 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { getUserMenuRole } from '@/api/admin/login';
|
||||
|
||||
|
||||
const modules = import.meta.glob('../views/**/*.vue');
|
||||
|
||||
export const usePermissionStore = defineStore('permisson', {
|
||||
state: () => {
|
||||
return {
|
||||
addRouters: [],
|
||||
menuList: [],
|
||||
};
|
||||
},
|
||||
getters: {
|
||||
getRoutes: (state) => state.addRouters,
|
||||
},
|
||||
actions: {
|
||||
setMenuList(menus) {
|
||||
this.menuList = menus;
|
||||
},
|
||||
GenerateRoutes(routeList) {
|
||||
const routes = [];
|
||||
|
||||
routeList.forEach((item) => {
|
||||
const route = {};
|
||||
// if (item.visible == 0) {
|
||||
if (item.menuType === 'M' || item.menuType === 'C') {
|
||||
route.path = item.path;
|
||||
route.name = item.menuName;
|
||||
if (item.menuType === 'M') {
|
||||
route.component = modules[`../views/index.vue`];
|
||||
} else if (item.menuType === 'C') {
|
||||
route.component = modules[`../views${item.component}.vue`] || modules['../views/error-page/888.vue'];
|
||||
}
|
||||
route.meta = {
|
||||
title: item.title,
|
||||
permission: item.permission,
|
||||
};
|
||||
}
|
||||
|
||||
if (item.children) {
|
||||
route.children = this.GenerateRoutes(item.children);
|
||||
}
|
||||
routes.push(route);
|
||||
// }
|
||||
});
|
||||
|
||||
return routes;
|
||||
},
|
||||
async getMenuRole() {
|
||||
const res = await getUserMenuRole();
|
||||
console.log(res.data);
|
||||
this.setMenuList(res.data);
|
||||
this.addRouters = await this.GenerateRoutes(res.data);
|
||||
},
|
||||
ClearMenuList() {
|
||||
this.menuList = [];
|
||||
}
|
||||
},
|
||||
});
|
||||
48
src/store/userInfo.js
Normal file
48
src/store/userInfo.js
Normal file
@ -0,0 +1,48 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { setLocalStorage, getLocalStorage } from '@/utils/storage';
|
||||
import { getInfo } from '@/api/admin/sys-user';
|
||||
import { getAppConfig } from '@/api/admin/login';
|
||||
|
||||
export const useUserStore = defineStore('user', {
|
||||
state: () => {
|
||||
return {
|
||||
token: window.sessionStorage.getItem('token') || '',
|
||||
uid: window.sessionStorage.getItem('uid') || '',
|
||||
sysConfig: getLocalStorage('sysConfig'),
|
||||
userInfo: '',
|
||||
}
|
||||
},
|
||||
getters: {
|
||||
roles: (state) => state.userInfo.roles || [],
|
||||
},
|
||||
actions: {
|
||||
setToken(token) {
|
||||
this.token = token;
|
||||
|
||||
window.sessionStorage.setItem('token', token);
|
||||
},
|
||||
async getUserInfo() {
|
||||
try {
|
||||
const res = await getInfo();
|
||||
// window.sessionStorage.setItem('uid', res.data.userId);
|
||||
window.localStorage.setItem('uid', res.data.userId);
|
||||
this.userInfo = res.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
},
|
||||
async getSysConfig() {
|
||||
try {
|
||||
const res = await getAppConfig();
|
||||
setLocalStorage('sysConfig', res.data);
|
||||
this.sysConfig = res.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
},
|
||||
userLogout() {
|
||||
this.token = null;
|
||||
this.userInfo = null;
|
||||
}
|
||||
})
|
||||
34
src/style/dark-theme.scss
Normal file
34
src/style/dark-theme.scss
Normal file
@ -0,0 +1,34 @@
|
||||
// Dark Theme
|
||||
|
||||
$dark-bg-1: #17171A;
|
||||
$color-bg-2: #393A3C;
|
||||
$color-bg-3: #2a2a2b;
|
||||
$color-bg-4: #313132;
|
||||
$color-bg-5: #373739;
|
||||
|
||||
$--color-text-1: rgba(255, 255, 255, 0.9);
|
||||
$--color-text-2: rgba(255, 255, 255, 0.7);
|
||||
$--color-text-3: rgba(255, 255, 255, 0.5);
|
||||
$--color-text-4: rgba(255, 255, 255, 0.3);
|
||||
|
||||
body[arco-theme='dark'] {
|
||||
.navbar {
|
||||
background-color: $dark-bg-1;
|
||||
}
|
||||
.arco-layout-content {
|
||||
background-color: $color-bg-2
|
||||
}
|
||||
.app-container {
|
||||
background-color: $color-bg-3;
|
||||
}
|
||||
.navbar {
|
||||
border-bottom: 1px solid $--color-text-4;
|
||||
.left-side {
|
||||
// background-color: $color-bg-2;
|
||||
color: $--color-text-1;
|
||||
&:hover {
|
||||
background-color: $color-bg-5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
88
src/style/index.scss
Normal file
88
src/style/index.scss
Normal file
File diff suppressed because one or more lines are too long
28
src/style/transition.scss
Normal file
28
src/style/transition.scss
Normal file
@ -0,0 +1,28 @@
|
||||
// global transition css
|
||||
|
||||
/* fade */
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.28s;
|
||||
}
|
||||
|
||||
.fade-enter,
|
||||
.fade-leave-active {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* fade-transform */
|
||||
.fade-transform-leave-active,
|
||||
.fade-transform-enter-active {
|
||||
transition: all .5s;
|
||||
}
|
||||
|
||||
.fade-transform-enter-from {
|
||||
opacity: 0;
|
||||
transform: translateX(-30px);
|
||||
}
|
||||
|
||||
.fade-transform-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateX(30px);
|
||||
}
|
||||
3
src/style/variables.scss
Normal file
3
src/style/variables.scss
Normal file
@ -0,0 +1,3 @@
|
||||
// base color
|
||||
$primary-font-color: #1d2129;
|
||||
$secondary-font-color: #86909c;
|
||||
41
src/utils/parseTime.js
Normal file
41
src/utils/parseTime.js
Normal file
@ -0,0 +1,41 @@
|
||||
// 日期格式化
|
||||
export function parseTime(time, pattern) {
|
||||
if (arguments.length === 0 || !time) {
|
||||
return null
|
||||
}
|
||||
if (time.indexOf('01-01-01') > -1) {
|
||||
return '-'
|
||||
}
|
||||
const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}'
|
||||
let date
|
||||
if (typeof time === 'object') {
|
||||
date = time
|
||||
} else {
|
||||
if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
|
||||
time = parseInt(time)
|
||||
}
|
||||
if ((typeof time === 'number') && (time.toString().length === 10)) {
|
||||
time = time * 1000
|
||||
}
|
||||
date = new Date(time)
|
||||
}
|
||||
const formatObj = {
|
||||
y: date.getFullYear(),
|
||||
m: date.getMonth() + 1,
|
||||
d: date.getDate(),
|
||||
h: date.getHours(),
|
||||
i: date.getMinutes(),
|
||||
s: date.getSeconds(),
|
||||
a: date.getDay()
|
||||
}
|
||||
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
|
||||
let value = formatObj[key]
|
||||
// Note: getDay() returns 0 on Sunday
|
||||
if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value] }
|
||||
if (result.length > 0 && value < 10) {
|
||||
value = '0' + value
|
||||
}
|
||||
return value || 0
|
||||
})
|
||||
return time_str
|
||||
}
|
||||
58
src/utils/request.js
Normal file
58
src/utils/request.js
Normal file
@ -0,0 +1,58 @@
|
||||
import axios from 'axios';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import { useUserStore } from '../store/userInfo'
|
||||
|
||||
// create an axios instance
|
||||
console.log("________"+import.meta.env.VITE_BASE_URL)
|
||||
const service = axios.create({
|
||||
baseURL:import.meta.env.VITE_BASE_URL,
|
||||
timeout: 8000,
|
||||
});
|
||||
|
||||
// request interceptor
|
||||
service.interceptors.request.use(
|
||||
(config) => {
|
||||
// Store 必须在拦截器内部导入,在外部导入会显示 Pinia 未初始化
|
||||
const store = useUserStore();
|
||||
// 设置请求头部 Authorization
|
||||
if (store.token) {
|
||||
config.headers['Authorization'] = 'Bearer ' + store.token;
|
||||
config.headers['Content-Type'] = 'application/json'
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(error) => {
|
||||
console.error(error);
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
// response interceptor
|
||||
service.interceptors.response.use(
|
||||
(response) => {
|
||||
return response.data;
|
||||
},
|
||||
(error) => {
|
||||
const store = useUserStore();
|
||||
const { code, msg } = error.response.data;
|
||||
// 如果过期则退出登录
|
||||
if (code === 401) {
|
||||
Message.error({
|
||||
content: 'Token 已过期, 请重新登陆',
|
||||
duration: 3000
|
||||
});
|
||||
// 重定向路由到登陆页面
|
||||
store.userLogout();
|
||||
// Akiraka 20230410 重定向到登录页面
|
||||
return router.push('/login');
|
||||
} else {
|
||||
Message.error({
|
||||
content: error.message,
|
||||
duration: 3000
|
||||
})
|
||||
return Promise.reject(msg);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export default service;
|
||||
41
src/utils/storage.js
Normal file
41
src/utils/storage.js
Normal file
@ -0,0 +1,41 @@
|
||||
// LocalStorage
|
||||
|
||||
/**
|
||||
* 存储LocalStorage
|
||||
* @param {string, Object} name
|
||||
* @param {*} value
|
||||
*/
|
||||
export const setLocalStorage = (name, value) => {
|
||||
if (!name) throw new Error('name must be specified');
|
||||
if (typeof value !== 'string') {
|
||||
value = JSON.stringify(value);
|
||||
}
|
||||
window.localStorage.setItem(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取LocalStorage
|
||||
* @param {string, Object} name
|
||||
* @returns
|
||||
*/
|
||||
export const getLocalStorage = (name) => {
|
||||
if(!name) throw new Error('name must be specified');
|
||||
const value = window.localStorage.getItem(name);
|
||||
return JSON.parse(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除指定name
|
||||
* @param {*} name
|
||||
*/
|
||||
export const removeLocalStorage = (name) => {
|
||||
if (!name) throw new Error('name must be specified');
|
||||
window.localStorage.removeItem(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空所有LocalStorage
|
||||
*/
|
||||
export const clearLocalStorage = () => {
|
||||
window.localStorage.clear();
|
||||
}
|
||||
60
src/utils/watermark.js
Normal file
60
src/utils/watermark.js
Normal file
@ -0,0 +1,60 @@
|
||||
let watermark = {}
|
||||
|
||||
let setWatermark = (str) => {
|
||||
let id = '1.23452384164.123412415'
|
||||
|
||||
if (document.getElementById(id) !== null) {
|
||||
document.body.removeChild(document.getElementById(id))
|
||||
}
|
||||
|
||||
let can = document.createElement('canvas')
|
||||
can.width = 200
|
||||
can.height = 120
|
||||
|
||||
let cans = can.getContext('2d')
|
||||
cans.rotate(-15 * Math.PI / 150)
|
||||
cans.font = '18px Vedana'
|
||||
cans.fillStyle = 'rgba(200, 200, 200, 0.20)'
|
||||
cans.textAlign = 'left'
|
||||
cans.textBaseline = 'Middle'
|
||||
cans.fillText(str, can.width / 8, can.height / 2)
|
||||
|
||||
let div = document.createElement('div')
|
||||
div.id = id
|
||||
div.style.pointerEvents = 'none'
|
||||
div.style.top = '35px'
|
||||
div.style.left = '200px'
|
||||
div.style.position = 'fixed'
|
||||
div.style.zIndex = '100000'
|
||||
div.style.width = document.documentElement.clientWidth + 'px'
|
||||
div.style.height = document.documentElement.clientHeight + 'px'
|
||||
div.style.background = 'url(' + can.toDataURL('image/png') + ') left top repeat'
|
||||
document.body.appendChild(div)
|
||||
return id
|
||||
}
|
||||
|
||||
// 该方法只允许调用一次
|
||||
watermark.set = (str) => {
|
||||
let id = setWatermark(str)
|
||||
setInterval(() => {
|
||||
if (document.getElementById(id) === null) {
|
||||
id = setWatermark(str)
|
||||
}
|
||||
}, 500)
|
||||
window.onresize = () => {
|
||||
setWatermark(str)
|
||||
}
|
||||
}
|
||||
|
||||
const outWatermark = (id) => {
|
||||
if (document.getElementById(id) !== null) {
|
||||
const div = document.getElementById(id)
|
||||
div.style.display = 'none'
|
||||
}
|
||||
}
|
||||
watermark.out = () => {
|
||||
const str = '1.23452384164.123412415'
|
||||
outWatermark(str)
|
||||
}
|
||||
|
||||
export default watermark
|
||||
253
src/views/admin/dict/data.vue
Normal file
253
src/views/admin/dict/data.vue
Normal file
@ -0,0 +1,253 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<a-form :model="queryForm" layout="inline">
|
||||
<a-form-item field="dictType" label="字典名称">
|
||||
<a-select v-model="queryForm.dictType" :options="dictTypeOptions" :field-names="{ value: 'dictType', label: 'dictName' }" />
|
||||
</a-form-item>
|
||||
<a-form-item field="dictTag" label="字典标签">
|
||||
<a-input v-model="queryForm.dictTag" placeholder="请输入字典标签" />
|
||||
</a-form-item>
|
||||
<a-form-item field="status" label="字典状态">
|
||||
<a-select v-model="queryForm.status" placeholder="请选择字典状态">
|
||||
<a-option :value="2">正常</a-option>
|
||||
<a-option :value="1">关闭</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-space>
|
||||
<a-button v-has="'admin:sysDictData:query'" type="primary">搜索</a-button>
|
||||
<a-button>重置</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<a-divider />
|
||||
|
||||
<div class="table-action">
|
||||
<a-button v-has="'admin:sysDictData:add'" type="primary" @click="handleAdd">新增</a-button>
|
||||
</div>
|
||||
|
||||
<a-table
|
||||
:data="tableData"
|
||||
:columns="columns"
|
||||
:pagination="{ 'show-total': true, 'show-jumper': true, 'show-page-size': true, total: pager.count, current: currentPage }"
|
||||
@page-change="handlePageChange"
|
||||
>
|
||||
<template #createdAt="{ record }">
|
||||
{{ parseTime(record.createdAt) }}
|
||||
</template>
|
||||
|
||||
<template #action="{ record }">
|
||||
<a-button v-has="'admin:sysDictData:edit'" type="text" @click="handleEdit(record)">修改</a-button>
|
||||
<a-button v-has="'admin:sysDictData:remove'" type="text" @click="() => { deleteVisible = true; deleteData = [record.dictCode]; }">删除</a-button>
|
||||
</template>
|
||||
|
||||
<template #status="{ record }">
|
||||
<a-tag v-if="record.status == 2" color="green">正常</a-tag>
|
||||
<a-tag v-else-if="record.status == 1" color="red">停用</a-tag>
|
||||
</template>
|
||||
</a-table>
|
||||
|
||||
<a-modal
|
||||
v-model:visible="modalVisible"
|
||||
:title="modalTitle"
|
||||
title-align="start"
|
||||
@close="$refs.modalFormRef.resetFields()"
|
||||
@before-ok="handleBeforeOk"
|
||||
@ok="handleSubmit"
|
||||
>
|
||||
<a-form :model="modalForm" :rules="rules" ref="modalFormRef">
|
||||
<a-form-item field="dictType" label="字典类型">
|
||||
<a-input v-model="modalForm.dictType" disabled></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item field="dictLabel" label="数据标签">
|
||||
<a-input
|
||||
v-model="modalForm.dictLabel"
|
||||
placeholder="请输入数据标签"
|
||||
></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item field="dictValue" label="数据键值">
|
||||
<a-input
|
||||
v-model="modalForm.dictValue"
|
||||
placeholder="请输入数据键值"
|
||||
></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item field="dictSort" label="显示排序">
|
||||
<a-input-number
|
||||
v-model="modalForm.dictSort"
|
||||
:default-value="0"
|
||||
mode="button"
|
||||
></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item field="status" label="状态">
|
||||
<a-radio-group v-model="modalForm.status">
|
||||
<a-radio :value="2">正常</a-radio>
|
||||
<a-radio :value="1">停用</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item field="remark" label="备注">
|
||||
<a-textarea v-model="modalForm.remark"></a-textarea>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
<!-- Akiraka 20230223 删除与批量删除 开始 -->
|
||||
<DeleteModal
|
||||
:data="deleteData"
|
||||
:visible="deleteVisible"
|
||||
:apiDelete="deleteDictData"
|
||||
@deleteVisibleChange="() => deleteVisible = false"
|
||||
/>
|
||||
<!-- Akiraka 20230223 删除与批量删除 结束 -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref, getCurrentInstance, onBeforeMount, watch } from 'vue';
|
||||
import { getDictData, addDictData, updateDictData, deleteDictData } from '@/api/admin/sys-dict-data';
|
||||
import { getDictType } from '@/api/admin/sys-dict';
|
||||
|
||||
// Akiraka 20230210 删除数据
|
||||
const deleteData = ref([])
|
||||
// Akiraka 20230210 删除对话框
|
||||
const deleteVisible = ref(false)
|
||||
// Akiraka 20230210 监听删除事件
|
||||
watch(() => deleteVisible.value ,(value) => {
|
||||
if ( value == false ) {
|
||||
getDictDataInfo({...proxy.$route.params, ...pager});
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
// 当前页码
|
||||
const currentPage = ref(1);
|
||||
|
||||
// 分页
|
||||
const pager = {
|
||||
count: 0,
|
||||
pageIndex: 1,
|
||||
pageSize: 10,
|
||||
};
|
||||
|
||||
// Rules
|
||||
const rules = {
|
||||
dictLabel: [{ required: true, message: '请输入数据标签' }],
|
||||
dictValue: [{ required: true, message: '请输入数据键值' }],
|
||||
dictSort: [{ required: true, message: '请选择排序' }],
|
||||
};
|
||||
|
||||
// Modal
|
||||
const modalVisible = ref(false);
|
||||
const modalTitle = ref('默认标题');
|
||||
|
||||
// DictType Options
|
||||
const dictTypeOptions = ref([]);
|
||||
|
||||
// 搜索表单
|
||||
const queryForm = reactive({});
|
||||
|
||||
// Modal表单
|
||||
const modalForm = reactive({
|
||||
status: 2
|
||||
});
|
||||
|
||||
// 表格
|
||||
const tableData = ref([]);
|
||||
const columns = [
|
||||
{ title: '字典编码', dataIndex: 'dictCode' },
|
||||
{ title: '字典标签', dataIndex: 'dictLabel' },
|
||||
{ title: '字典键值', dataIndex: 'dictValue' },
|
||||
{ title: '字典排序', dataIndex: 'dictSort' },
|
||||
{ title: '状态', dataIndex: 'status', slotName: 'status' },
|
||||
{ title: '备注', dataIndex: 'remark' },
|
||||
{ title: '创建时间', dataIndex: 'createdAt', slotName: 'createdAt' },
|
||||
{ title: '操作', dataIndex: 'action', slotName: 'action' },
|
||||
];
|
||||
|
||||
// 新增字典数据
|
||||
const handleAdd = () => {
|
||||
modalVisible.value = true;
|
||||
modalTitle.value = '新增字典数据';
|
||||
|
||||
Object.assign(modalForm, proxy.$route.params);
|
||||
};
|
||||
|
||||
// 修改字典数据
|
||||
const handleEdit = (record) => {
|
||||
modalVisible.value = true;
|
||||
modalTitle.value = '修改字典数据';
|
||||
|
||||
Object.assign(modalForm, proxy.$route.params, record);
|
||||
console.log(modalForm)
|
||||
};
|
||||
|
||||
// 提交表单前检查
|
||||
const handleBeforeOk = (done) => {
|
||||
proxy.$refs.modalFormRef.validate((err) => {
|
||||
if (err) {
|
||||
proxy.$message.error('表单校验失败');
|
||||
done(false);
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
const handleSubmit = async () => {
|
||||
if (modalForm.dictCode) {
|
||||
const { code, msg } = await updateDictData(modalForm, modalForm.dictCode);
|
||||
if ( code == 200 ) {
|
||||
proxy.$notification.success('修改成功');
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
} else {
|
||||
const { code, msg } = await addDictData(modalForm);
|
||||
if (code == 200 ) {
|
||||
proxy.$notification.success('新增成功');
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
}
|
||||
getDictDataInfo({...proxy.$route.params, ...pager});
|
||||
}
|
||||
|
||||
// 页码改变
|
||||
const handlePageChange = (pagerIndex) => {
|
||||
currentPage.value = pagerIndex;
|
||||
pager.pageIndex = pagerIndex;
|
||||
getDictDataInfo({...proxy.$route.params, ...pager});
|
||||
}
|
||||
|
||||
// 获取字典数据
|
||||
const getDictDataInfo = async (params = {}) => {
|
||||
const { data, code, msg } = await getDictData(params);
|
||||
if ( code == 200 ) {
|
||||
tableData.value = data.list;
|
||||
Object.assign(pager, { count: data.count, pageIndex: data.pageIndex, pageSize: data.pageSize });
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
};
|
||||
|
||||
// 获取字典类型
|
||||
const getDictTypeInfo = async () => {
|
||||
const res = await getDictType({ pageSize: 1000 });
|
||||
|
||||
dictTypeOptions.value = res.data.list;
|
||||
Object.assign(queryForm, proxy.$route.params);
|
||||
};
|
||||
|
||||
onBeforeMount(() => {
|
||||
getDictDataInfo(proxy.$route.params);
|
||||
getDictTypeInfo();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.table-action {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
</style>
|
||||
268
src/views/admin/dict/index.vue
Normal file
268
src/views/admin/dict/index.vue
Normal file
@ -0,0 +1,268 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<a-form :model="queryForm" ref="queryFormRef" layout="inline">
|
||||
<a-form-item field="dictName" label="字典名称">
|
||||
<a-input v-model="queryForm.dictName" placeholder="请输入字典名称" @press-enter="handleQuery" />
|
||||
</a-form-item>
|
||||
<a-form-item field="dictType" label="字典类型">
|
||||
<a-input v-model="queryForm.dictType" placeholder="请输入字典类型" @press-enter="handleQuery" />
|
||||
</a-form-item>
|
||||
<a-form-item field="status" label="状态">
|
||||
<a-select v-model="queryForm.status" placeholder="请选择字典状态">
|
||||
<a-option :value="2">正常</a-option>
|
||||
<a-option :value="1">停用</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-space>
|
||||
<a-button v-has="'admin:sysDictType:query'" type="primary" @click="handleQuery"><icon-search /> 搜索</a-button>
|
||||
<a-button v-has="'admin:sysDictType:query'" @click="handleResetQuery"><icon-loop /> 重置</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<a-divider />
|
||||
|
||||
<div class="action">
|
||||
<a-space>
|
||||
<a-button v-has="'admin:sysDictType:add'" type="primary" @click="handleAdd"><icon-plus /> 新增</a-button>
|
||||
<a-button v-has="'system:sysdicttype:remove'" type="primary" status="danger" @click="() => { deleteVisible = true; }"><icon-delete /> 批量删除</a-button>
|
||||
<a-button type="primary" status="warning" disabled><icon-download /> 导出</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
|
||||
<!-- table -->
|
||||
<a-table
|
||||
:columns="tableColumns"
|
||||
:data="tableData"
|
||||
:row-selection="{ type: 'checkbox', showCheckedAll: true }"
|
||||
:pagination="{ 'show-total': true, 'show-jumper': true, 'show-page-size': true, total: pager.count, current: currentPage }"
|
||||
row-key="id"
|
||||
@selection-change="(selection) => {deleteData = selection;}"
|
||||
@page-change="handlePageChange"
|
||||
@page-size-change="handlePageSizeChange"
|
||||
>
|
||||
<template #dictType="{ record }">
|
||||
<router-link :to="`/admin/dict/data/${record.dictType}`">{{ record.dictType }}</router-link>
|
||||
</template>
|
||||
|
||||
<template #status="{ record }">
|
||||
<a-tag v-if="record.status == 2" color="green">正常</a-tag>
|
||||
<a-tag v-else color="red">停用</a-tag>
|
||||
</template>
|
||||
|
||||
<template #createdAt="{ record }">
|
||||
{{ parseTime(record.createdAt) }}
|
||||
</template>
|
||||
|
||||
<template #action="{ record }">
|
||||
<a-button v-has="'admin:sysDictType:edit'" type="text" @click="handleUpdate(record)"><icon-edit /> 修改</a-button>
|
||||
<a-button v-has="'admin:sysDictType:remove'" type="text" @click="() => { deleteVisible = true; deleteData = [record.id]; }"><icon-edit /> 删除</a-button>
|
||||
</template>
|
||||
</a-table>
|
||||
|
||||
<!-- Modal弹框 -->
|
||||
<a-modal
|
||||
v-model:visible="modalVisible"
|
||||
title-align="start"
|
||||
:title="modalTitle"
|
||||
@before-ok="handleSubmit"
|
||||
@close="$refs.modalFormRef.resetFields()"
|
||||
>
|
||||
<a-form :model="modalForm" ref="modalFormRef">
|
||||
<a-form-item field="dictName" label="字典名称">
|
||||
<a-input
|
||||
v-model="modalForm.dictName"
|
||||
placeholder="请输入字典名称"
|
||||
:disabled="modalForm.id ? true : false"
|
||||
></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item field="dictType" label="字典类型">
|
||||
<a-input
|
||||
v-model="modalForm.dictType"
|
||||
placeholder="请输入字典类型"
|
||||
:disabled="modalForm.id ? true : false"
|
||||
></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item field="status" label="状态">
|
||||
<a-radio-group v-model="modalForm.status">
|
||||
<a-radio :value="2">正常</a-radio>
|
||||
<a-radio :value="1">停用</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item field="remark" label="备注">
|
||||
<a-textarea
|
||||
v-model="modalForm.remark"
|
||||
placeholder="请输入内容备注"
|
||||
></a-textarea>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
|
||||
<!-- Akiraka 20230223 删除与批量删除 开始 -->
|
||||
<DeleteModal
|
||||
:data="deleteData"
|
||||
:visible="deleteVisible"
|
||||
:apiDelete="removeDictType"
|
||||
@deleteVisibleChange="() => deleteVisible = false"
|
||||
/>
|
||||
<!-- Akiraka 20230223 删除与批量删除 结束 -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref, getCurrentInstance, nextTick, onMounted, watch } from 'vue';
|
||||
import { getDictType, addDictType, removeDictType, updateDictType } from '@/api/admin/sys-dict';
|
||||
|
||||
// Akiraka 20230210 删除数据
|
||||
const deleteData = ref([])
|
||||
// Akiraka 20230210 删除对话框
|
||||
const deleteVisible = ref(false)
|
||||
// Akiraka 20230210 监听删除事件
|
||||
watch(() => deleteVisible.value ,(value) => {
|
||||
if ( value == false ) {
|
||||
getSysDictTypeInfo({ ...pager, ...queryForm });
|
||||
}
|
||||
})
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const currentPage = ref(1);
|
||||
|
||||
// pager
|
||||
const pager = {
|
||||
count: 0,
|
||||
pageIndex: 1,
|
||||
pageSize: 10,
|
||||
};
|
||||
|
||||
// form
|
||||
const modalForm = reactive({
|
||||
status: 2,
|
||||
});
|
||||
|
||||
// Table
|
||||
const tableData = ref([]);
|
||||
const tableColumns = [
|
||||
{ title: '编号', dataIndex: 'id' },
|
||||
{ title: '字典名称', dataIndex: 'dictName' },
|
||||
{ title: '字典类型', dataIndex: 'dictType', slotName: 'dictType' },
|
||||
{ title: '字典状态', dataIndex: 'status', slotName: 'status' },
|
||||
{ title: '备注', dataIndex: 'remark' },
|
||||
{ title: '创建时间', dataIndex: 'createdAt', slotName: 'createdAt' },
|
||||
{ title: '操作', slotName: 'action' },
|
||||
];
|
||||
|
||||
// Modal
|
||||
const modalVisible = ref(false);
|
||||
const modalTitle = ref('默认标题');
|
||||
|
||||
const { queryForm, handleQuery, handleResetQuery } = useQueryDict();
|
||||
// 搜索
|
||||
function useQueryDict() {
|
||||
const queryForm = reactive({});
|
||||
|
||||
const handleQuery = () => {
|
||||
getSysDictTypeInfo(queryForm);
|
||||
};
|
||||
|
||||
const handleResetQuery = () => {
|
||||
proxy.$refs.queryFormRef.resetFields();
|
||||
|
||||
currentPage.value = 1;
|
||||
getSysDictTypeInfo(queryForm);
|
||||
};
|
||||
|
||||
return {
|
||||
queryForm,
|
||||
handleQuery,
|
||||
handleResetQuery,
|
||||
};
|
||||
}
|
||||
|
||||
// 页码改变
|
||||
const handlePageChange = (page) => {
|
||||
pager.pageIndex = page;
|
||||
currentPage.value = page;
|
||||
|
||||
getSysDictTypeInfo({ ...pager, ...queryForm });
|
||||
};
|
||||
|
||||
// 每页数据量改变
|
||||
const handlePageSizeChange = (pageSize) => {
|
||||
pager.pageSize = pageSize;
|
||||
|
||||
getSysDictTypeInfo({ ...pager, ...queryForm });
|
||||
};
|
||||
|
||||
// 新增
|
||||
const handleAdd = () => {
|
||||
modalVisible.value = true;
|
||||
modalTitle.value = '新增字典';
|
||||
};
|
||||
|
||||
// 修改
|
||||
const handleUpdate = async (record) => {
|
||||
modalVisible.value = true;
|
||||
modalTitle.value = '修改字典';
|
||||
|
||||
await nextTick();
|
||||
Object.assign(modalForm, record);
|
||||
};
|
||||
|
||||
// Submit
|
||||
const handleSubmit = (done) => {
|
||||
proxy.$refs.modalFormRef.validate(async (err) => {
|
||||
if (!err) {
|
||||
try {
|
||||
if (Reflect.has(modalForm, 'id')) {
|
||||
const { code, msg } = await updateDictType(modalForm, modalForm.id);
|
||||
if (code == 200 ) {
|
||||
proxy.$notification.success('更新成功');
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
} else {
|
||||
const { code, msg } = await addDictType(modalForm);
|
||||
currentPage.value = Math.ceil(++pager.count / pager.pageSize);
|
||||
pager.pageIndex = currentPage.value;
|
||||
if (code == 200 ) {
|
||||
proxy.$notification.success('新增成功');
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
}
|
||||
modalVisible.value = false;
|
||||
getSysDictTypeInfo(pager);
|
||||
done();
|
||||
} catch (e) {
|
||||
done(false);
|
||||
}
|
||||
} else {
|
||||
proxy.$message.error('表单校验失败!');
|
||||
done(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 获取字典数据
|
||||
const getSysDictTypeInfo = async (params = {}) => {
|
||||
const { data, code, msg } = await getDictType(params);
|
||||
if ( code == 200 ) {
|
||||
tableData.value = data.list;
|
||||
Object.assign(pager, { count: data.count, pageIndex: data.pageIndex, pageSize: data.pageSize });
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getSysDictTypeInfo();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.action {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
</style>
|
||||
254
src/views/admin/sys-api/index.vue
Normal file
254
src/views/admin/sys-api/index.vue
Normal file
@ -0,0 +1,254 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<a-form :model="queryForm" ref="queryFormRef" layout="inline">
|
||||
<a-form-item field="title" label="标题">
|
||||
<a-input v-model="queryForm.title" placeholder="请输入标题" @press-enter="handleQuery" />
|
||||
</a-form-item>
|
||||
<a-form-item field="path" label="地址">
|
||||
<a-input v-model="queryForm.path" placeholder="请输入地址" @press-enter="handleQuery" />
|
||||
</a-form-item>
|
||||
<a-form-item field="action" label="类型">
|
||||
<a-select v-model="queryForm.action" placeholder="请选择类型">
|
||||
<a-option>GET</a-option>
|
||||
<a-option>POST</a-option>
|
||||
<a-option>PUT</a-option>
|
||||
<a-option>DELETE</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-space size="medium">
|
||||
<a-button v-has="'admin:sysApi:query'" type="primary" @click="handleQuery">
|
||||
<template #icon>
|
||||
<icon-search />
|
||||
</template>
|
||||
搜索
|
||||
</a-button>
|
||||
<a-button @click="handlResetQuery">
|
||||
<template #icon>
|
||||
<icon-loop />
|
||||
</template>
|
||||
重置
|
||||
</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<a-divider />
|
||||
|
||||
<!-- Table -->
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
:pagination="{
|
||||
'show-total': true,
|
||||
'show-jumper': true,
|
||||
'show-page-size': true,
|
||||
total: pager.count,
|
||||
current: currentPage,
|
||||
}"
|
||||
row-key="id"
|
||||
@page-change="handlePageChange"
|
||||
@page-size-change="handlePageSizeChange"
|
||||
>
|
||||
<template #reqType="{ record }">
|
||||
<a-tag v-if="record.action.toLowerCase() === 'get'" color="cyan">{{record.action}}</a-tag>
|
||||
<a-tag v-else-if="record.action.toLowerCase() === 'post'" color="gold" >{{ record.action }}</a-tag>
|
||||
<a-tag v-else-if="record.action.toLowerCase() === 'put'" color="green" >{{ record.action }}</a-tag>
|
||||
<a-tag v-else-if="record.action.toLowerCase() === 'delete'" color="pinkpurple" >{{ record.action }}</a-tag>
|
||||
</template>
|
||||
<template #createdAt="{ record }"> {{ parseTime(record.createdAt) }}</template>
|
||||
<template #action="{ record }">
|
||||
<a-button v-has="'admin:sysApi:edit'" type="text" @click="handleUpdate(record)"><icon-edit /> 修改 </a-button>
|
||||
</template>
|
||||
</a-table>
|
||||
|
||||
<!-- Drawer -->
|
||||
<a-drawer
|
||||
:visible="drawerVisible"
|
||||
:width="450"
|
||||
@before-ok="handleDrawerSubmit"
|
||||
@cancel="handleDrawerCancel"
|
||||
>
|
||||
<template #title> 修改接口管理 </template>
|
||||
<a-form :model="drawerForm" ref="drawerFormRef" :rules="rules">
|
||||
<a-form-item field="handle" label="Handle">
|
||||
<a-input v-model="drawerForm.handle" placeholder="请输入Handle" />
|
||||
</a-form-item>
|
||||
<a-form-item field="title" label="标题">
|
||||
<a-input v-model="drawerForm.title" placeholder="请输入标题" />
|
||||
</a-form-item>
|
||||
<a-form-item field="type" label="类型">
|
||||
<a-select
|
||||
v-model="drawerForm.type"
|
||||
placeholder="请选择类型"
|
||||
allow-clear
|
||||
>
|
||||
<a-option>SYS</a-option>
|
||||
<a-option>BUS</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item field="action" label="方式">
|
||||
<a-select
|
||||
v-model="drawerForm.action"
|
||||
placeholder="请选择请求方式"
|
||||
allow-clear
|
||||
>
|
||||
<a-option>GET</a-option>
|
||||
<a-option>POST</a-option>
|
||||
<a-option>PUT</a-option>
|
||||
<a-option>DELETE</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item field="path" label="路径">
|
||||
<a-input v-model="drawerForm.path" disabled />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref, getCurrentInstance, onMounted, nextTick } from 'vue';
|
||||
import { IconSearch, IconLoop } from '@arco-design/web-vue/es/icon';
|
||||
import { getSysApi,addSysApi,updateSysApi } from '@/api/admin/sys-api';
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
// 分页当前页
|
||||
const currentPage = ref(1);
|
||||
|
||||
// Pager
|
||||
const pager = {
|
||||
count: 0,
|
||||
pageIndex: 1,
|
||||
pageSize: 10,
|
||||
};
|
||||
|
||||
// From 定义
|
||||
const queryForm = reactive({});
|
||||
const drawerForm = reactive({});
|
||||
|
||||
// From 校验规则
|
||||
const rules = {
|
||||
title: [{ required: true, message: '请输入标题' }],
|
||||
method: [{ required: true, message: '请选择方式' }],
|
||||
};
|
||||
|
||||
// 抽屉显示
|
||||
const drawerVisible = ref(false);
|
||||
|
||||
// Table Columns
|
||||
const columns = [
|
||||
{ title: '标题', dataIndex: 'title' },
|
||||
{ title: '请求类型', dataIndex: 'action', slotName: 'reqType' },
|
||||
{ title: '请求信息', dataIndex: 'path' },
|
||||
{ title: '创建时间', dataIndex: 'createdAt', slotName: 'createdAt' },
|
||||
{ title: '操作', slotName: 'action' },
|
||||
];
|
||||
|
||||
// Table Data
|
||||
const tableData = ref([]);
|
||||
|
||||
// Drawer 确定事件
|
||||
// 异步关闭Modal需要调用 done()
|
||||
const handleDrawerSubmit = (done) => {
|
||||
proxy.$refs.drawerFormRef.validate(async (valid) => {
|
||||
if (!valid) {
|
||||
let res;
|
||||
if (!drawerForm.id) {
|
||||
const { code, msg } = await addSysApi(drawerForm);
|
||||
if (code == 200 ) {
|
||||
proxy.$notification.success('新增成功');
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
} else {
|
||||
const { code, msg } = await updateSysApi(drawerForm, drawerForm.id);
|
||||
if (code == 200 ) {
|
||||
proxy.$notification.success('修改成功');
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
}
|
||||
drawerVisible.value = false;
|
||||
done();
|
||||
getSysApiInfo(pager);
|
||||
} else {
|
||||
proxy.$message.error('表单校验失败');
|
||||
done(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Drawer 关闭事件
|
||||
const handleDrawerCancel = () => {
|
||||
drawerVisible.value = false;
|
||||
};
|
||||
|
||||
// 查询
|
||||
const handleQuery = async () => {
|
||||
getSysApiInfo(queryForm);
|
||||
currentPage.value = 1;
|
||||
};
|
||||
|
||||
// 重置查询
|
||||
const handlResetQuery = () => {
|
||||
proxy.$refs.queryFormRef.resetFields();
|
||||
|
||||
handlePageChange(1);
|
||||
};
|
||||
|
||||
// 修改
|
||||
const handleUpdate = async (record) => {
|
||||
drawerVisible.value = true;
|
||||
// updateSysApi(record);
|
||||
await nextTick();
|
||||
Object.assign(drawerForm, record);
|
||||
};
|
||||
|
||||
/**
|
||||
* 分页改变
|
||||
* @param {Number} [page]
|
||||
*/
|
||||
const handlePageChange = (page) => {
|
||||
pager.pageIndex = page;
|
||||
|
||||
// 修改当前页码
|
||||
currentPage.value = page;
|
||||
getSysApiInfo({ ...pager, ...queryForm });
|
||||
};
|
||||
|
||||
// 每页数据量
|
||||
const handlePageSizeChange = (pageSize) => {
|
||||
pager.pageSize = pageSize;
|
||||
getSysApiInfo({ ...pager, ...queryForm });
|
||||
};
|
||||
|
||||
// 获取接口信息
|
||||
const getSysApiInfo = async (params = {}) => {
|
||||
const { data, code, msg } = await getSysApi(params);
|
||||
if ( code == 200 ) {
|
||||
tableData.value = data.list;
|
||||
Object.assign(pager, { count: data.count, pageIndex: data.pageIndex, pageSize: data.pageSize });
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getSysApiInfo(pager);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.pagination {
|
||||
margin-top: 12px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
// Table 操作列样式
|
||||
.arco-table-th:last-child > span {
|
||||
margin-left: 15px;
|
||||
}
|
||||
</style>
|
||||
328
src/views/admin/sys-config/index.vue
Normal file
328
src/views/admin/sys-config/index.vue
Normal file
@ -0,0 +1,328 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<a-form :model="queryForm" ref="queryFormRef" layout="inline">
|
||||
<a-form-item field="configName" label="参数名称">
|
||||
<a-input
|
||||
v-model="queryForm.configName"
|
||||
placeholder="请输入参数名称"
|
||||
></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item field="configKey" label="参数键名">
|
||||
<a-input
|
||||
v-model="queryForm.configKey"
|
||||
placeholder="请输入参数键名"
|
||||
></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item field="configType" label="系统内置">
|
||||
<a-select v-model="queryForm.configType" placeholder="选择系统内置">
|
||||
<a-option value="Y">是</a-option>
|
||||
<a-option value="N">否</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleQuery()"><icon-search /> 搜索</a-button>
|
||||
<a-button @click="handleResetQuery()"><icon-loop /> 重置</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<!-- 分割线 -->
|
||||
<a-divider />
|
||||
|
||||
<div class="action">
|
||||
<a-space>
|
||||
<a-button v-has="'admin:sysConfig:add'" type="primary" @click="handleAdd"><icon-plus /> 新增</a-button>
|
||||
<a-button v-has="'admin:sysConfig:remove'" type="primary" status="danger" disabled><icon-delete /> 删除</a-button>
|
||||
<a-button type="primary" status="warning" disabled><icon-download /> 导出</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
|
||||
<a-table
|
||||
:data="tableData"
|
||||
:columns="columns"
|
||||
:pagination="{ 'show-total': true, 'show-jumper': true, 'show-page-size': true, total: pager.count, current: currentPage }"
|
||||
@page-change="handlePageChange"
|
||||
@page-size-change="handlePageSizeChange"
|
||||
>
|
||||
<template #configType="{ record }">
|
||||
<a-tag v-if="record.configType === 'Y'" color="green">是</a-tag>
|
||||
<a-tag v-else-if="record.configType === 'N'" color="red">否</a-tag>
|
||||
</template>
|
||||
|
||||
<template #createdAt="{ record }">
|
||||
{{ parseTime(record.createdAt) }}
|
||||
</template>
|
||||
|
||||
<template #action="{ record }">
|
||||
<a-button v-has="'admin:sysConfig:edit'" type="text" @click="handleUpdate(record)"><icon-edit /> 修改</a-button>
|
||||
<a-button v-has="'admin:sysConfig:edit'" type="text" @click="() => { deleteVisible = true; deleteData = [record.id]; }"><icon-delete /> 删除</a-button>
|
||||
</template>
|
||||
</a-table>
|
||||
|
||||
<a-modal
|
||||
v-model:visible="visible"
|
||||
:title="title"
|
||||
title-align="start"
|
||||
@before-ok="handleBeforeOk"
|
||||
@close="handleResetModalForm"
|
||||
>
|
||||
<a-form :model="modalForm" :rules="rules" ref="modalFormRef">
|
||||
<a-form-item field="configName" label="参数名称">
|
||||
<a-input
|
||||
v-model="modalForm.configName"
|
||||
placeholder="请输入参数名称"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item field="configKey" label="参数键名">
|
||||
<a-input v-model="modalForm.configKey" placeholder="请输入参数键名" />
|
||||
</a-form-item>
|
||||
<a-form-item field="configValue" label="参数键值">
|
||||
<a-input
|
||||
v-model="modalForm.configValue"
|
||||
placeholder="请输入参数键值"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item field="configType" label="系统内置">
|
||||
<a-radio-group v-model="modalForm.configType">
|
||||
<a-radio value="Y">是</a-radio>
|
||||
<a-radio value="N">否</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item field="isFrontend" label="前台显示">
|
||||
<a-radio-group v-model="modalForm.isFrontend">
|
||||
<a-radio :value="1">是</a-radio>
|
||||
<a-radio :value="0">否</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item field="remark" label="备注">
|
||||
<a-textarea
|
||||
v-model="modalForm.remark"
|
||||
placeholder="请输入备注内容"
|
||||
></a-textarea>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
|
||||
<!-- Akiraka 20230223 删除与批量删除 开始 -->
|
||||
<DeleteModal
|
||||
:data="deleteData"
|
||||
:visible="deleteVisible"
|
||||
:apiDelete="removeSysConfig"
|
||||
@deleteVisibleChange="() => deleteVisible = false"
|
||||
/>
|
||||
<!-- Akiraka 20230223 删除与批量删除 结束 -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref, getCurrentInstance, watch } from 'vue';
|
||||
import { getSysConfig, addSysConfig, removeSysConfig, updateSysConfig } from '@/api/admin/sys-config';
|
||||
|
||||
// Akiraka 20230210 删除数据
|
||||
const deleteData = ref([])
|
||||
// Akiraka 20230210 删除对话框
|
||||
const deleteVisible = ref(false)
|
||||
// Akiraka 20230210 监听删除事件
|
||||
watch(() => deleteVisible.value ,(value) => {
|
||||
if ( value == false ) {
|
||||
getSysConfigInfo(pager);
|
||||
}
|
||||
})
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const currentPage = ref(1);
|
||||
|
||||
const pager = {
|
||||
count: 0,
|
||||
pageIndex: 1,
|
||||
pageSize: 10,
|
||||
};
|
||||
|
||||
const { queryForm, handleQuery, handleResetQuery } = useQueryForm();
|
||||
const { tableData, columns, handlePageChange, handlePageSizeChange } =
|
||||
useTableData();
|
||||
const {
|
||||
rules,
|
||||
title,
|
||||
visible,
|
||||
modalForm,
|
||||
handleAdd,
|
||||
handleUpdate,
|
||||
handleBeforeOk,
|
||||
} = useModal();
|
||||
|
||||
function useQueryForm() {
|
||||
const queryForm = reactive({});
|
||||
|
||||
const handleQuery = () => {
|
||||
getSysConfigInfo(queryForm);
|
||||
};
|
||||
|
||||
const handleResetQuery = () => {
|
||||
proxy.$refs.queryFormRef.resetFields();
|
||||
|
||||
handlePageChange(1);
|
||||
};
|
||||
|
||||
return {
|
||||
queryForm,
|
||||
handleQuery,
|
||||
handleResetQuery,
|
||||
};
|
||||
}
|
||||
|
||||
function useTableData() {
|
||||
const tableData = ref([]);
|
||||
const columns = [
|
||||
{ title: '编码', dataIndex: 'id', width: 70 },
|
||||
{ title: '名称', dataIndex: 'configName' },
|
||||
{ title: '键名', dataIndex: 'configKey' },
|
||||
{
|
||||
title: '内置',
|
||||
dataIndex: 'configType',
|
||||
slotName: 'configType',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
dataIndex: 'remark',
|
||||
width: 350,
|
||||
ellipsis: true,
|
||||
tooltip: true,
|
||||
},
|
||||
{ title: '创建时间', dataIndex: 'createdAt', slotName: 'createdAt' },
|
||||
{ title: '操作', dataIndex: 'action', slotName: 'action' },
|
||||
];
|
||||
|
||||
// 分页改变
|
||||
const handlePageChange = (page) => {
|
||||
pager.pageIndex = page;
|
||||
getSysConfigInfo(pager);
|
||||
};
|
||||
|
||||
// 每页数据量
|
||||
const handlePageSizeChange = (pageSize) => {
|
||||
pager.pageSize = pageSize;
|
||||
getSysConfigInfo(pager);
|
||||
};
|
||||
|
||||
return {
|
||||
columns,
|
||||
tableData,
|
||||
handlePageChange,
|
||||
handlePageSizeChange,
|
||||
};
|
||||
}
|
||||
|
||||
function useModal() {
|
||||
const visible = ref(false);
|
||||
const title = ref('默认标题');
|
||||
const modalForm = reactive({
|
||||
configType: 'Y',
|
||||
isFrontend: 1,
|
||||
});
|
||||
|
||||
const rules = {
|
||||
configName: [{ required: true, message: '请输入参数名称' }],
|
||||
configKey: [{ required: true, message: '请输入参数键名' }],
|
||||
configValue: [{ required: true, message: '请输入参数键值' }],
|
||||
};
|
||||
|
||||
const handleAdd = () => {
|
||||
visible.value = true;
|
||||
title.value = '新增参数';
|
||||
};
|
||||
|
||||
const handleUpdate = async (record) => {
|
||||
visible.value = true;
|
||||
title.value = '修改参数';
|
||||
|
||||
Object.assign(modalForm, record);
|
||||
};
|
||||
|
||||
const handleBeforeOk = (done) => {
|
||||
proxy.$refs.modalFormRef.validate(async (err) => {
|
||||
if (!err) {
|
||||
try {
|
||||
const msg = await handleSubmit(modalForm);
|
||||
proxy.$message.success(msg);
|
||||
done();
|
||||
getSysConfigInfo();
|
||||
} catch (e) {
|
||||
proxy.$message.error(e);
|
||||
done(false);
|
||||
}
|
||||
} else {
|
||||
proxy.message.error('表单校验失败');
|
||||
done(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
rules,
|
||||
title,
|
||||
visible,
|
||||
modalForm,
|
||||
handleAdd,
|
||||
handleUpdate,
|
||||
handleBeforeOk,
|
||||
};
|
||||
}
|
||||
|
||||
// 获取系统配置
|
||||
const getSysConfigInfo = async (params = {}) => {
|
||||
const { data, code, msg } = await getSysConfig(params);
|
||||
if ( code == 200 ) {
|
||||
tableData.value = data.list;
|
||||
Object.assign(pager, { count: data.count, pageIndex: data.pageIndex, pageSize: data.pageSize });
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
};
|
||||
|
||||
// 提交
|
||||
const handleSubmit = (data) => {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
let res;
|
||||
if (!data.id) {
|
||||
const { code, msg } = await addSysConfig(data);
|
||||
if (code == 200 ) {
|
||||
proxy.$notification.success('新增成功');
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
} else {
|
||||
const { code, msg } = await updateSysConfig(data, data.id);
|
||||
if (code == 200 ) {
|
||||
proxy.$notification.success('修改成功');
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 重置表单
|
||||
const handleResetModalForm = () => {
|
||||
proxy.$refs.modalFormRef.resetFields();
|
||||
|
||||
modalForm.id = null;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getSysConfigInfo();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.action {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
</style>
|
||||
266
src/views/admin/sys-dept/index.vue
Normal file
266
src/views/admin/sys-dept/index.vue
Normal file
@ -0,0 +1,266 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<a-form :model="queryForm" ref="queryFormRef" layout="inline">
|
||||
<a-form-item field="deptName" label="部门名称">
|
||||
<a-input v-model="queryForm.deptName" placeholder="请输入部门名称" @press-enter="handleQuery" />
|
||||
</a-form-item>
|
||||
<a-form-item field="status" label="部门状态">
|
||||
<a-select v-model="queryForm.status" placeholder="请选择部门状态">
|
||||
<a-option :value="2">正常</a-option>
|
||||
<a-option :value="1">停用</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-space>
|
||||
<a-button v-has="'admin:sysDept:query'" type="primary" @click="handleQuery"><icon-search /> 搜索</a-button>
|
||||
<a-button @click="handleResetQuery"><icon-loop /> 重置</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<a-divider />
|
||||
|
||||
<div class="action">
|
||||
<a-button v-has="'admin:sysDept:add'" type="primary" @click="handleAdd()"><icon-plus /> 新增</a-button>
|
||||
</div>
|
||||
|
||||
<!-- 异步数据需要defualt-expanded-keys 传入所有行Key才能默认展开 -->
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
:pagination="false"
|
||||
:default-expanded-keys="[1]"
|
||||
row-key="deptId"
|
||||
>
|
||||
<template #status="{ record }">
|
||||
<a-tag color="green" v-if="record.status === 2">正常</a-tag>
|
||||
<a-tag color="red" v-else> 停用 </a-tag>
|
||||
</template>
|
||||
<template #createdAt="{ record }">
|
||||
{{ parseTime(record.createdAt) }}
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<a-button v-has="'admin:sysDept:edit'" type="text" @click="handleUpdate(record)"><icon-edit /> 修改</a-button>
|
||||
<a-button v-has="'admin:sysDept:add'" type="text" @click="handleAdd(record)"><icon-plus /> 新增</a-button>
|
||||
<a-button v-has="'admin:sysDept:remove'" type="text" @click="() => { deleteVisible = true; deleteData = [record.deptId]; }"><icon-delete /> 删除</a-button>
|
||||
</template>
|
||||
</a-table>
|
||||
|
||||
<!-- Modal -->
|
||||
<a-modal
|
||||
v-model:visible="modalVisible"
|
||||
:title="modalTitle"
|
||||
title-align="start"
|
||||
@before-ok="handleBeforeOk"
|
||||
@close="() => $refs.modalFormRef.resetFields()"
|
||||
>
|
||||
<a-form :model="modalForm" :rules="rules" ref="modalFormRef">
|
||||
<a-form-item field="parentId" label="上级部门">
|
||||
<a-tree-select
|
||||
v-model="modalForm.parentId"
|
||||
:data="tableData"
|
||||
:field-names="{ key: 'deptId', title: 'deptName' }"
|
||||
placeholder="请选择上级部门"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item field="deptName" label="部门名称">
|
||||
<a-input v-model="modalForm.deptName" placeholder="请输入部门名称" />
|
||||
</a-form-item>
|
||||
<a-form-item field="leader" label="负责人">
|
||||
<a-input v-model="modalForm.leader" placeholder="请输入负责人" />
|
||||
</a-form-item>
|
||||
<a-form-item field="phone" label="联系电话">
|
||||
<a-input v-model="modalForm.phone" placeholder="请输入联系电话" />
|
||||
</a-form-item>
|
||||
<a-form-item field="email" label="邮箱">
|
||||
<a-input v-model="modalForm.email" placeholder="请输入邮箱" />
|
||||
</a-form-item>
|
||||
<a-form-item field="status" label="部门状态">
|
||||
<a-radio-group v-model="modalForm.status">
|
||||
<a-radio :value="2">正常</a-radio>
|
||||
<a-radio :value="1">停用</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
|
||||
<!-- Akiraka 20230223 删除与批量删除 开始 -->
|
||||
<DeleteModal
|
||||
:data="deleteData"
|
||||
:visible="deleteVisible"
|
||||
:apiDelete="removeDept"
|
||||
@deleteVisibleChange="() => deleteVisible = false"
|
||||
/>
|
||||
<!-- Akiraka 20230223 删除与批量删除 结束 -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref, onMounted, getCurrentInstance, nextTick, watch } from 'vue';
|
||||
import { getDept, addDept, removeDept, updateDept } from '@/api/admin/sys-dept';
|
||||
|
||||
// Akiraka 20230210 删除数据
|
||||
const deleteData = ref([])
|
||||
// Akiraka 20230210 删除对话框
|
||||
const deleteVisible = ref(false)
|
||||
// Akiraka 20230210 监听删除事件
|
||||
watch(() => deleteVisible.value ,(value) => {
|
||||
if ( value == false ) {
|
||||
getDeptInfo(queryForm);
|
||||
}
|
||||
})
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
// Modal
|
||||
const modalVisible = ref(false);
|
||||
const modalTitle = ref('默认标题');
|
||||
|
||||
// Form
|
||||
const queryForm = reactive({});
|
||||
const modalForm = reactive({
|
||||
status: 2,
|
||||
});
|
||||
|
||||
// rules
|
||||
const rules = {
|
||||
parentId: [{ required: true, message: '请选择上级部门' }],
|
||||
deptName: [{ required: true, message: '请输入部门名称' }],
|
||||
leader: [{ required: true, message: '请输入负责人' }],
|
||||
};
|
||||
|
||||
// Columns
|
||||
const columns = [
|
||||
{
|
||||
title: '部门名称',
|
||||
dataIndex: 'deptName',
|
||||
},
|
||||
{
|
||||
title: '排序',
|
||||
dataIndex: 'sort',
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
slotName: 'status',
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createdAt',
|
||||
slotName: 'createdAt',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
slotName: 'action',
|
||||
},
|
||||
];
|
||||
|
||||
// Table Data
|
||||
const tableData = ref([]);
|
||||
|
||||
// 查询
|
||||
const handleQuery = async () => {
|
||||
const res = await getDept(queryForm);
|
||||
tableData.value = deepDelChildren(res.data);
|
||||
};
|
||||
|
||||
// 重置查询
|
||||
const handleResetQuery = () => {
|
||||
proxy.$refs.queryFormRef.resetFields();
|
||||
|
||||
getDeptInfo(queryForm);
|
||||
}
|
||||
|
||||
// 新增
|
||||
const handleAdd = ({ deptId, status = 2 } = {}) => {
|
||||
modalVisible.value = true;
|
||||
modalTitle.value = '新增部门';
|
||||
|
||||
if (deptId) Object.assign(modalForm, {parentId: deptId, status});
|
||||
};
|
||||
|
||||
// 修改
|
||||
const handleUpdate = async (record) => {
|
||||
modalVisible.value = true;
|
||||
modalTitle.value = '修改部门信息';
|
||||
|
||||
await nextTick();
|
||||
const { parentId, deptName, leader, phone, email, status, deptId } = record;
|
||||
Object.assign(modalForm, {
|
||||
parentId,
|
||||
deptName,
|
||||
leader,
|
||||
phone,
|
||||
email,
|
||||
status,
|
||||
deptId,
|
||||
});
|
||||
};
|
||||
|
||||
// 递归删除空Children
|
||||
function deepDelChildren(data) {
|
||||
const depts = data;
|
||||
let len = depts?.length;
|
||||
// let len = depts && depts.length;
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
if (depts[i].children.length > 0) {
|
||||
deepDelChildren(depts[i].children);
|
||||
} else {
|
||||
delete depts[i].children;
|
||||
}
|
||||
}
|
||||
|
||||
return depts;
|
||||
}
|
||||
|
||||
// Modal 表单提交前检查
|
||||
const handleBeforeOk = (done) => {
|
||||
proxy.$refs.modalFormRef.validate(async (err) => {
|
||||
if (!err) {
|
||||
let res;
|
||||
if (Reflect.has(modalForm, 'deptId')) {
|
||||
const { code, msg } = await updateDept(modalForm, modalForm.deptId);
|
||||
if (code == 200 ) {
|
||||
proxy.$notification.success('修改成功');
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
} else {
|
||||
const { code, msg } = await addDept(modalForm);
|
||||
if (code == 200 ) {
|
||||
proxy.$notification.success('新增成功');
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
}
|
||||
done();
|
||||
getDeptInfo();
|
||||
} else {
|
||||
proxy.$message.error('数据校验失败');
|
||||
done(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 获取部门信息
|
||||
const getDeptInfo = async (params = {}) => {
|
||||
const { data, code, msg } = await getDept(params);
|
||||
if ( code == 200 ) {
|
||||
tableData.value = deepDelChildren(data);
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getDeptInfo();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.action {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
</style>
|
||||
169
src/views/admin/sys-login-log/index.vue
Normal file
169
src/views/admin/sys-login-log/index.vue
Normal file
@ -0,0 +1,169 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<a-form :model="queryForm" ref="queryFormRef" layout="inline">
|
||||
<a-form-item field="username" label="用户名">
|
||||
<a-input v-model="queryForm.username" placeholder="请输入用户名" @press-enter="handleQuery" />
|
||||
</a-form-item>
|
||||
<a-form-item field="status" label="状态">
|
||||
<a-select v-model="queryForm.status" placeholder="请选择系统登录日志状态">
|
||||
<a-option :value="2">正常</a-option>
|
||||
<a-option :value="1">关闭</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item field="ipaddr" label="IP地址">
|
||||
<a-input v-model="queryForm.ipaddr" placeholder="请输入IP地址" />
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleQuery"><icon-search /> 搜索</a-button>
|
||||
<a-button @click="handleResetQuery"><icon-loop /> 重置</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<a-divider />
|
||||
|
||||
<div class="action">
|
||||
<a-space>
|
||||
<a-button
|
||||
type="primary"
|
||||
status="danger"
|
||||
:disabled="selectedKeys.length === 0"
|
||||
@click="handleBatchDelete"
|
||||
><icon-delete /> 批量删除</a-button
|
||||
>
|
||||
</a-space>
|
||||
</div>
|
||||
|
||||
<a-table
|
||||
:data="tableData"
|
||||
:columns="columns"
|
||||
row-key="id"
|
||||
:row-selection="{ type: 'checkbox', showCheckedAll: true }"
|
||||
:pagination="{
|
||||
'show-total': true,
|
||||
'show-jumper': true,
|
||||
'show-page-size': true,
|
||||
current: currentPage,
|
||||
total: pager.count,
|
||||
}"
|
||||
v-model:selectedKeys="selectedKeys"
|
||||
@page-change="handlePageChange"
|
||||
@page-size-change="handlePageSizeChange"
|
||||
>
|
||||
<template #status="{ record }">
|
||||
<a-tag v-if="record.status == 2" color="green">正常</a-tag>
|
||||
<a-tag v-if="record.status == 1" color="red">失败</a-tag>
|
||||
</template>
|
||||
<template #loginTime="{ record }">
|
||||
{{ parseTime(record.loginTime) }}
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<a-popconfirm
|
||||
content="是否删除当前数据?"
|
||||
@ok="removeSysLoginLogInfo([record.id])"
|
||||
>
|
||||
<a-button type="text" status="danger">删除</a-button>
|
||||
</a-popconfirm>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, getCurrentInstance } from 'vue';
|
||||
import { getSysLoginLog, removeSysLoginLog } from '@/api/admin/sys-login-log';
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
// 当前页
|
||||
const currentPage = ref(1);
|
||||
|
||||
// Pager
|
||||
const pager = {
|
||||
count: 0,
|
||||
pageIndex: 1,
|
||||
pageSize: 10,
|
||||
};
|
||||
|
||||
// 表格多选
|
||||
const selectedKeys = ref([]);
|
||||
|
||||
const queryForm = reactive({});
|
||||
|
||||
const tableData = ref([]);
|
||||
const columns = [
|
||||
{ title: '用户名', dataIndex: 'username' },
|
||||
{ title: '类型', dataIndex: 'msg' },
|
||||
{ title: '状态', dataIndex: 'status', slotName: 'status' },
|
||||
{ title: 'IP地址', dataIndex: 'ipaddr' },
|
||||
{ title: '登陆时间', dataIndex: 'loginTime', slotName: 'loginTime' },
|
||||
{ title: '操作', slotName: 'action', slotName: 'action' },
|
||||
];
|
||||
|
||||
// 查询
|
||||
const handleQuery = () => {
|
||||
getSysLoginLogInfo({ ...pager, ...queryForm });
|
||||
}
|
||||
|
||||
// 重置查询
|
||||
const handleResetQuery = () => {
|
||||
proxy.$refs.queryFormRef.resetFields();
|
||||
handlePageChange(1);
|
||||
};
|
||||
|
||||
// 页码改变
|
||||
const handlePageChange = (page) => {
|
||||
currentPage.value = page;
|
||||
|
||||
pager.pageIndex = page;
|
||||
getSysLoginLogInfo({ ...pager, ...queryForm });
|
||||
};
|
||||
|
||||
// 每页数据量
|
||||
const handlePageSizeChange = (pageSize) => {
|
||||
pager.pageSize = pageSize;
|
||||
getSysLoginLogInfo({ ...pager, ...queryForm });
|
||||
};
|
||||
|
||||
// 批量删除
|
||||
const handleBatchDelete = () => {
|
||||
proxy.$modal.confirm({
|
||||
title: '注意',
|
||||
content: '是否删除当前所有勾选数据?',
|
||||
onOk: () => {
|
||||
removeSysLoginLogInfo(selectedKeys.value);
|
||||
},
|
||||
onCancel: () => {
|
||||
proxy.$message.info('已取消操作');
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 获取登陆日志信息
|
||||
const getSysLoginLogInfo = async (params = {}) => {
|
||||
const res = await getSysLoginLog(params);
|
||||
const { count, list, pageIndex, pageSize } = res.data;
|
||||
|
||||
tableData.value = list;
|
||||
Object.assign(pager, { total: count, pageIndex, pageSize });
|
||||
};
|
||||
|
||||
// 删除登陆日志信息
|
||||
const removeSysLoginLogInfo = async (ids) => {
|
||||
const res = await removeSysLoginLog({ ids });
|
||||
proxy.$message.success(res.msg);
|
||||
|
||||
getSysLoginLogInfo();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getSysLoginLogInfo();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.action {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
</style>
|
||||
350
src/views/admin/sys-menu/index.vue
Normal file
350
src/views/admin/sys-menu/index.vue
Normal file
@ -0,0 +1,350 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<a-form :model="queryForm" ref="queryFormRef" layout="inline">
|
||||
<a-form-item field="title" label="菜单名称">
|
||||
<a-input v-model="queryForm.title" placeholder="请输入菜单名称" @press-enter="handleQuery"/>
|
||||
</a-form-item>
|
||||
<a-form-item field="visible" label="状态">
|
||||
<a-select v-model="queryForm.visible" placeholder="请选择菜单状态">
|
||||
<a-option value="1">显示</a-option>
|
||||
<a-option value="0">隐藏</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleQuery">搜索</a-button>
|
||||
<a-button @click="$refs.queryFormRef.resetFields()">重置</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<a-divider />
|
||||
|
||||
<!-- 动作 -->
|
||||
<div class="action">
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleAddMenu()">新增菜单</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
|
||||
<!-- 菜单管理列表 -->
|
||||
<a-table :columns="columns" :data="tableData" row-key="menuId" :pagination="false">
|
||||
<template #icon="{ record }">
|
||||
<component :is="record.icon" :style="{ fontSize: '18px' }"></component>
|
||||
</template>
|
||||
<template #menutype="{ record }">
|
||||
<a-tag color="purple" v-if="record.menuType === 'M'">目录</a-tag>
|
||||
<a-tag color="orange" v-else-if="record.menuType === 'C'">菜单</a-tag>
|
||||
<a-tag color="blue" v-else-if="record.menuType === 'F'">按钮</a-tag>
|
||||
</template>
|
||||
<template #isFrame="{ record }">
|
||||
<a-tag v-if="record.isFrame == '1'" color="green">内部</a-tag>
|
||||
<a-tag v-else color="red">外部</a-tag>
|
||||
</template>
|
||||
<template #visible="{ record }">
|
||||
<a-tag v-if="record.visible == '0'" color="green">显示</a-tag>
|
||||
<a-tag v-else color="red">隐藏</a-tag>
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<a-button v-has="'admin:sysMenu:add'" type="text" @click="handleAddMenu(record.menuId)">新增</a-button>
|
||||
<a-button v-has="'admin:sysMenu:edit'" type="text" @click="handleUpdate(record)">修改</a-button>
|
||||
<a-button v-has="'admin:sysMenu:remove'" type="text" @click="() => { deleteVisible = true; deleteData = [record.menuId]; }">删除</a-button>
|
||||
</template>
|
||||
</a-table>
|
||||
|
||||
<!-- 菜单管理新增与提交弹窗 -->
|
||||
<a-modal v-model:visible="modalVisible" :title="modalTitle" title-align="start" :width="800" modal-class="menu-modal" @before-ok="handleSubmit" @close="() => {$refs.modalFormRef.resetFields(); modalForm.menuId = null;}">
|
||||
<a-form :model="modalForm" :rules="modalRules" ref="modalFormRef" auto-label-width label-align="left">
|
||||
<a-form-item field="menuType" label="菜单类型">
|
||||
<a-radio-group v-model="modalForm.menuType">
|
||||
<a-radio value="M">目录</a-radio>
|
||||
<a-radio value="C">菜单</a-radio>
|
||||
<a-radio value="F">按钮</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="12">
|
||||
<a-form-item field="parentId" label="上级菜单">
|
||||
<a-tree-select v-model="modalForm.parentId" :data="tableData" :field-names="{ key: 'menuId', icon: '_' }" :allow-search="true" :filter-tree-node="filterTreeNode" placeholder="请选择上级菜单"/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="title" label="菜单标题">
|
||||
<a-input v-model="modalForm.title" placeholder="请输入菜单标题" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="12">
|
||||
<a-form-item field="icon" label="菜单图标">
|
||||
<a-select v-model="modalForm.icon" :options="parseIconName" :field-names="{ name: 'value' }" :trigger-props="{ contentClass: 'iconselect-trigger' }" allow-search placeholder="请选择菜单图标">
|
||||
<template #option="{ data }">
|
||||
<component :is="data.value" />
|
||||
</template>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="sort" label="显示排序">
|
||||
<a-input-number v-model="modalForm.sort" mode="button" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="12" v-if="modalForm.menuType !== 'F'">
|
||||
<a-form-item field="menuName" label="路由名称">
|
||||
<a-input v-model="modalForm.menuName" placeholder="请输入路由名称" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12" v-if="modalForm.menuType !== 'F'">
|
||||
<a-form-item field="component" label="组件名称">
|
||||
<a-input v-model="modalForm.component" placeholder="请输入组件名称"/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="12" v-if="modalForm.menuType !== 'F'">
|
||||
<a-form-item field="path" label="路由地址">
|
||||
<a-input v-model="modalForm.path" placeholder="请输入路由地址" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12" v-if="modalForm.menuType === 'C' || modalForm.menuType === 'F'">
|
||||
<a-form-item field="permission" label="权限标识">
|
||||
<a-input v-model="modalForm.permission" placeholder="请输入权限标识"/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-form-item field="isFrame" label="是否外链" v-if="modalForm.menuType !== 'F'">
|
||||
<a-radio-group v-model="modalForm.isFrame">
|
||||
<a-radio value="0">是</a-radio>
|
||||
<a-radio value="1">否</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item field="visible" label="菜单状态" v-if="modalForm.menuType !== 'F'">
|
||||
<a-radio-group v-model="modalForm.visible">
|
||||
<a-radio value="0">显示</a-radio>
|
||||
<a-radio value="1">隐藏</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item field="apis" label="API权限" v-if="modalForm.menuType !== 'M'">
|
||||
<a-transfer v-model:model-value="modalForm.apis" :data="transferData" :title="['未授权', '已授权']" show-search />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
|
||||
<!-- Akiraka 20230210 删除与批量删除 开始 -->
|
||||
<DeleteModal
|
||||
:data="deleteData"
|
||||
:visible="deleteVisible"
|
||||
:apiDelete="removeMenu"
|
||||
@deleteVisibleChange="() => deleteVisible = false"
|
||||
/>
|
||||
<!-- Akiraka 20230210 删除与批量删除 结束 -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref, getCurrentInstance, computed, watch, watchEffect } from 'vue';
|
||||
import * as ArcoIconModules from '@arco-design/web-vue/es/icon';
|
||||
import { getMenu } from '@/api/admin/menu';
|
||||
import { getSysApi } from '@/api/admin/sys-api';
|
||||
import { addMenu, removeMenu, updateMenu, getMenuDetails } from '@/api/admin/menu';
|
||||
|
||||
// Akiraka 20230210 删除数据
|
||||
const deleteData = ref([])
|
||||
// Akiraka 20230210 删除对话框
|
||||
const deleteVisible = ref(false)
|
||||
// Akiraka 20230210 监听删除事件
|
||||
watch(() => deleteVisible.value ,(value) => {
|
||||
if ( value == false ) {
|
||||
getSysMenuInfo(pager);
|
||||
}
|
||||
})
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const { queryForm, handleQuery } = useQueryData();
|
||||
|
||||
// 默认页码
|
||||
const currentPage = ref(1);
|
||||
// 表格分页
|
||||
const pager = {
|
||||
count: 0,
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
};
|
||||
|
||||
// QueryModel
|
||||
function useQueryData() {
|
||||
// 表单查询
|
||||
const queryForm = reactive({});
|
||||
const handleQuery = () => {
|
||||
getSysMenuInfo(queryForm);
|
||||
};
|
||||
|
||||
return {
|
||||
queryForm,
|
||||
handleQuery,
|
||||
};
|
||||
}
|
||||
|
||||
// 默认表单
|
||||
const modalForm = reactive({
|
||||
menuType: 'M',
|
||||
sort: 0,
|
||||
isFrame: '1',
|
||||
visible: '0',
|
||||
});
|
||||
|
||||
// 表单校验
|
||||
const modalRules = {
|
||||
title: [{ required: true, message: '请输入菜单名称' }],
|
||||
path: [{ required: true, message: '请输入菜单路由地址' }],
|
||||
component: [
|
||||
{ required: true, message: '请输入菜单路由地址' },
|
||||
{ pattern: /^[\/A-Za-z.-.()*]+$/, trigger: 'blur', message: '校验规则: 只允许输入字母 a-z 或大写 A-Z 与 -'}
|
||||
],
|
||||
};
|
||||
|
||||
// 监听事件 20220715
|
||||
watchEffect(() => {
|
||||
// 当菜单类型为目录时组件地址则为 Layout
|
||||
if (modalForm.menuType == "M") {
|
||||
modalForm.component = "Layout"
|
||||
} else {
|
||||
// 当菜单类型设置为菜单时 如果为 Layout 则为空
|
||||
if (modalForm.component == "Layout") {
|
||||
modalForm.component = ""
|
||||
// 当菜单类型设置为菜单时 编辑页面显示正常内容
|
||||
} else {
|
||||
modalForm.component = modalForm.component
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
// Table
|
||||
const columns = [
|
||||
{ title: '菜单名称', dataIndex: 'title',width: "220" },
|
||||
// { title: '图标', dataIndex: 'icon', slotName: 'icon' },
|
||||
{ title: '路由地址', dataIndex: 'path', width: "400" },
|
||||
{ title: '组件地址', dataIndex: 'component', width: "300" },
|
||||
{ title: '排序', dataIndex: 'sort', width: "80" },
|
||||
{ title: '类型', dataIndex: 'menuType', slotName: 'menutype', width: "100" },
|
||||
{ title: '是否外联', dataIndex: 'isFrame', slotName: 'isFrame', width: "100" },
|
||||
{ title: '显示状态', dataIndex: 'visible', slotName: 'visible', width: "100" },
|
||||
{ title: '操作', slotName: 'action' ,width: "220", fixed: "right" },
|
||||
];
|
||||
const tableData = ref([]);
|
||||
|
||||
// Transfer Data
|
||||
const transferData = ref([]);
|
||||
|
||||
// Modal
|
||||
const modalVisible = ref(false);
|
||||
const modalTitle = ref('默认标题');
|
||||
|
||||
// 创建新菜单
|
||||
const handleAddMenu = (parentId = null) => {
|
||||
modalVisible.value = true;
|
||||
modalTitle.value = '新增菜单';
|
||||
if (parentId) modalForm.parentId = parentId;
|
||||
getSysMenuInfo();
|
||||
};
|
||||
|
||||
// TreeSearchFilter
|
||||
const filterTreeNode = (searchVal, nodeData) => {
|
||||
return nodeData.title.indexOf(searchVal) > -1;
|
||||
};
|
||||
|
||||
// 修改菜单
|
||||
const handleUpdate = async (record) => {
|
||||
const res = await getMenuDetails(record.menuId);
|
||||
Object.assign(modalForm, res.data);
|
||||
|
||||
modalTitle.value = '修改菜单';
|
||||
modalVisible.value = true;
|
||||
};
|
||||
|
||||
// handleSubmit 新增与修改按钮方法 20220713
|
||||
const handleSubmit = (done) => {
|
||||
proxy.$refs.modalFormRef.validate(async (valid) => {
|
||||
if (!valid) {
|
||||
if (!modalForm.menuId) {
|
||||
console.log("dasdasd",modalForm)
|
||||
const { success } = await addMenu(modalForm);
|
||||
if (success) proxy.$message.success('新增成功');
|
||||
} else {
|
||||
const { success } = await updateMenu(modalForm, modalForm.menuId);
|
||||
if (success) proxy.$message.success('修改成功');
|
||||
}
|
||||
getSysMenuInfo(pager);
|
||||
done();
|
||||
} else {
|
||||
proxy.$message.error('表单校验失败');
|
||||
done(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 获取菜单信息
|
||||
const getSysMenuInfo = async (params = {}) => {
|
||||
const { code, data, msg } = await getMenu(params);
|
||||
if ( code == 200 ) {
|
||||
tableData.value = data;
|
||||
} else {
|
||||
proxy.$notification.error(msg)
|
||||
}
|
||||
};
|
||||
|
||||
// 获取API接口信息
|
||||
const getSysApiInfo = async () => {
|
||||
const { code, data, msg } = await getSysApi({ pageSize: 10000, type: 'BUS' });
|
||||
if ( code == 200 ) {
|
||||
transferData.value = data.list.map((item) => {
|
||||
return { value: item.id, label: item.title };
|
||||
});
|
||||
} else {
|
||||
proxy.$notification.error(msg)
|
||||
}
|
||||
};
|
||||
|
||||
// 转换Icon Object 为 list
|
||||
const parseIconName = computed(() => {
|
||||
const iconNameList = [];
|
||||
|
||||
for (let key in ArcoIconModules) {
|
||||
if (ArcoIconModules[key].name) {
|
||||
iconNameList.push({ value: ArcoIconModules[key].name });
|
||||
}
|
||||
}
|
||||
|
||||
return iconNameList;
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
getSysMenuInfo();
|
||||
getSysApiInfo();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
// 覆盖默认 select trigger 样式
|
||||
.iconselect-trigger .arco-select-dropdown-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.iconselect-trigger .arco-select-option {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
// 覆盖默认穿梭框样式
|
||||
.menu-modal {
|
||||
.arco-transfer-view {
|
||||
height: 350px;
|
||||
width: 250px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
185
src/views/admin/sys-oper-log/index.vue
Normal file
185
src/views/admin/sys-oper-log/index.vue
Normal file
@ -0,0 +1,185 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<a-form :model="queryForm" ref="queryFormRef" layout="inline">
|
||||
<a-form-item field="status" label="状态">
|
||||
<a-select
|
||||
v-model="queryForm.status"
|
||||
placeholder="请选择系统操作日志状态"
|
||||
>
|
||||
<a-option :value="2">正常</a-option>
|
||||
<a-option :value="1">关闭</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item field="createdAt" label="创建时间">
|
||||
<a-range-picker
|
||||
disabled
|
||||
v-model="queryForm.createdAt"
|
||||
style="width: 254px"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleQuery"
|
||||
><icon-search /> 搜索</a-button
|
||||
>
|
||||
<a-button @click="handleResetQuery"><icon-loop /> 重置</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<a-divider />
|
||||
|
||||
<div class="action">
|
||||
<a-space>
|
||||
<a-button
|
||||
type="primary"
|
||||
status="danger"
|
||||
@click="handleBatchDelete"
|
||||
:disabled="selectedKeys.length === 0"
|
||||
><icon-delete /> 批量删除</a-button
|
||||
>
|
||||
</a-space>
|
||||
</div>
|
||||
|
||||
<a-table
|
||||
:data="tableData"
|
||||
:columns="columns"
|
||||
:row-selection="{ type: 'checkbox', showCheckedAll: true }"
|
||||
row-key="id"
|
||||
:pagination="{
|
||||
'show-total': true,
|
||||
'show-jumper': true,
|
||||
'show-page-size': true,
|
||||
current: currentPage,
|
||||
total: pager.count,
|
||||
}"
|
||||
v-model:selectedKeys="selectedKeys"
|
||||
@page-change="handlePageChange"
|
||||
@page-size-change="handlePageSizeChange"
|
||||
>
|
||||
<template #status="{ record }">
|
||||
<a-tag v-if="record.status == 2" color="green">正常</a-tag>
|
||||
<a-tag v-if="record.status == 1" color="red">关闭</a-tag>
|
||||
</template>
|
||||
<template #operTime="{ record }">
|
||||
{{ parseTime(record.operTime) }}
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<a-popconfirm
|
||||
content="是否删除当前数据?"
|
||||
type="warning"
|
||||
@ok="removeSysOperaLogInfo([record.id])"
|
||||
>
|
||||
<a-button type="text" status="danger">删除</a-button>
|
||||
</a-popconfirm>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, getCurrentInstance } from 'vue';
|
||||
import { getSysOperaLog, removeSysOperaLog } from '@/api/admin/sys-opera-log';
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
// 当前页
|
||||
const currentPage = ref(1);
|
||||
|
||||
// 分页
|
||||
const pager = {
|
||||
count: 0,
|
||||
pageIndex: 1,
|
||||
pageSize: 10,
|
||||
};
|
||||
|
||||
//
|
||||
const selectedKeys = ref([]);
|
||||
|
||||
const queryForm = reactive({});
|
||||
|
||||
const tableData = ref([]);
|
||||
const columns = [
|
||||
{ title: '编号', dataIndex: 'id' },
|
||||
{ title: '请求信息', dataIndex: 'operUrl', width: 500 },
|
||||
{ title: '操作人员', dataIndex: 'operName' },
|
||||
{ title: '状态', dataIndex: 'status', slotName: 'status' },
|
||||
{ title: '操作日期', dataIndex: 'operTime', slotName: 'operTime' },
|
||||
{ title: '操作', slotName: 'action', slotName: 'action' },
|
||||
];
|
||||
|
||||
// 查询
|
||||
const handleQuery = () => {
|
||||
handlePageChange(1);
|
||||
};
|
||||
|
||||
// 重置查询
|
||||
const handleResetQuery = () => {
|
||||
proxy.$refs.queryFormRef.resetFields();
|
||||
handlePageChange(1);
|
||||
};
|
||||
|
||||
// 批量删除
|
||||
const handleBatchDelete = () => {
|
||||
console.log(selectedKeys.value);
|
||||
if (selectedKeys.value.length === 0) {
|
||||
proxy.$message.error('请勾选需要删除的数据!');
|
||||
return;
|
||||
}
|
||||
|
||||
proxy.$modal.confirm({
|
||||
title: '注意',
|
||||
content: '是否批量删除选中?',
|
||||
onOk: () => {
|
||||
removeSysOperaLogInfo(selectedKeys.value);
|
||||
},
|
||||
onCancel: () => {
|
||||
proxy.$message.info('已取消操作');
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// 页码改变
|
||||
const handlePageChange = (page) => {
|
||||
currentPage.value = page;
|
||||
|
||||
pager.pageIndex = page;
|
||||
getSysOperaLogInfo({ ...pager, ...queryForm });
|
||||
};
|
||||
|
||||
//
|
||||
const handlePageSizeChange = (pageSize) => {
|
||||
pager.pageSize = pageSize;
|
||||
getSysOperaLogInfo({ ...pager, ...queryForm });
|
||||
};
|
||||
|
||||
// 获取操作日志
|
||||
const getSysOperaLogInfo = async (params = {}) => {
|
||||
const res = await getSysOperaLog(params);
|
||||
const { count, list, pageIndex, pageSize } = res.data;
|
||||
tableData.value = list;
|
||||
|
||||
Object.assign(pager, { total: count, pageIndex, pageSize });
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除操作日志
|
||||
* @params {array} ids
|
||||
*/
|
||||
const removeSysOperaLogInfo = async (ids) => {
|
||||
const res = await removeSysOperaLog({ ids });
|
||||
proxy.$message.success(res.msg);
|
||||
|
||||
getSysOperaLogInfo();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getSysOperaLogInfo();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.action {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
</style>
|
||||
289
src/views/admin/sys-post/index.vue
Normal file
289
src/views/admin/sys-post/index.vue
Normal file
@ -0,0 +1,289 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<a-form :model="queryForm" ref="queryFormRef" layout="inline">
|
||||
<a-form-item field="postCode" label="岗位编码">
|
||||
<a-input v-model="queryForm.postCode" placeholder="请输入岗位编码" @press-enter="handleQuery" />
|
||||
</a-form-item>
|
||||
<a-form-item field="postName" label="岗位名称">
|
||||
<a-input v-model="queryForm.postName" placeholder="请输入岗位名称" @press-enter="handleQuery" />
|
||||
</a-form-item>
|
||||
<a-form-item field="status" label="岗位状态">
|
||||
<a-select v-model="queryForm.status" placeholder="请选择岗位状态" :style="{ width: '181px' }">
|
||||
<a-option :value="2">正常</a-option>
|
||||
<a-option :value="1">停用</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-space>
|
||||
<a-button v-has="'admin:sysPost:query'" type="primary" @click="handleQuery"><icon-search /> 搜索</a-button>
|
||||
<a-button @click="handleResetQuery"><icon-loop /> 重置</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<a-divider />
|
||||
|
||||
<!-- action -->
|
||||
<div class="action">
|
||||
<a-space>
|
||||
<a-button v-has="'admin:sysPost:add'" type="primary" @click="handleAdd"><icon-plus /> 新增 </a-button>
|
||||
<a-button v-has="'admin:sysPost:remove'" type="primary" status="danger" @click="() => { deleteVisible = true; }"><icon-delete /> 批量删除 </a-button>
|
||||
<a-button type="primary" status="warning" disabled><icon-download /> 导出 </a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
|
||||
<!-- table -->
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
:pagination="{ 'show-total': true, 'show-jumper': true, 'show-page-size': true, total: pager.count, current: currentPage }"
|
||||
:row-selection="{ type: 'checkbox', showCheckedAll: true }"
|
||||
row-key="postId"
|
||||
@selection-change="(selection) => {deleteData = selection;}"
|
||||
@page-change="handlePageChange"
|
||||
@page-size-change="handlePageSizeChange"
|
||||
>
|
||||
<template #createdAt="{ record }">
|
||||
{{ parseTime(record.createdAt) }}
|
||||
</template>
|
||||
<template #status="{ record }">
|
||||
<a-tag v-if="record.status == 2" color="green">正常</a-tag>
|
||||
<a-tag v-else color="red">停用</a-tag>
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<a-space>
|
||||
<a-button v-has="'admin:sysPost:edit'" type="text" @click="handleUpdate(record)"><icon-edit /> 修改</a-button>
|
||||
<a-button v-has="'admin:sysPost:remove'" type="text" @click="() => { deleteVisible = true; deleteData = [record.postId]; }"><icon-delete /> 删除</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-table>
|
||||
|
||||
<!-- Modal -->
|
||||
<a-modal
|
||||
v-model:visible="modalVisible"
|
||||
:title="modalTitle"
|
||||
title-align="start"
|
||||
@before-ok="handleSubmit"
|
||||
@close="() => {$refs.modalFormRef.resetFields(); modalForm.postId = null;}"
|
||||
>
|
||||
<a-form :model="modalForm" :rules="rules" ref="modalFormRef">
|
||||
<a-form-item field="postName" label="岗位名称">
|
||||
<a-input v-model="modalForm.postName" placeholder="请输入岗位名称" />
|
||||
</a-form-item>
|
||||
<a-form-item field="postCode" label="岗位编码">
|
||||
<a-input v-model="modalForm.postCode" placeholder="请输入岗位编码" />
|
||||
</a-form-item>
|
||||
<a-form-item field="sort" label="岗位排序">
|
||||
<a-input-number
|
||||
v-model="modalForm.sort"
|
||||
mode="button"
|
||||
:default-value="0"
|
||||
:style="{ width: '150px' }"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item field="status" label="岗位状态">
|
||||
<a-radio-group v-model="modalForm.status">
|
||||
<a-radio :value="2"> 正常 </a-radio>
|
||||
<a-radio :value="1"> 停用 </a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item field="remark" label="备注">
|
||||
<a-textarea v-model="modalForm.remark" placeholder="请输入备注内容" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
|
||||
<!-- Akiraka 20230223 删除与批量删除 开始 -->
|
||||
<DeleteModal
|
||||
:data="deleteData"
|
||||
:visible="deleteVisible"
|
||||
:apiDelete="removePost"
|
||||
@deleteVisibleChange="() => deleteVisible = false"
|
||||
/>
|
||||
<!-- Akiraka 20230223 删除与批量删除 结束 -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref, getCurrentInstance, onMounted, nextTick, watch } from 'vue';
|
||||
import { getPost, addPost, removePost, updatePost } from '@/api/admin/post';
|
||||
import { parseTime } from '@/utils/parseTime';
|
||||
|
||||
// Akiraka 20230210 删除数据
|
||||
const deleteData = ref([])
|
||||
// Akiraka 20230210 删除对话框
|
||||
const deleteVisible = ref(false)
|
||||
// Akiraka 20230210 监听删除事件
|
||||
watch(() => deleteVisible.value ,(value) => {
|
||||
if ( value == false ) {
|
||||
getPostInfo(pager);
|
||||
}
|
||||
})
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const currentPage = ref(1);
|
||||
// Pager
|
||||
const pager = {
|
||||
count: 0,
|
||||
pageIndex: 1,
|
||||
pageSize: 10,
|
||||
};
|
||||
// form
|
||||
const queryForm = reactive({});
|
||||
const modalForm = reactive({
|
||||
sort: 0,
|
||||
status: 2,
|
||||
});
|
||||
|
||||
// Rules
|
||||
const rules = {
|
||||
postName: [{ required: true, message: '请输入岗位名称' }],
|
||||
postCode: [{ required: true, message: '请输入岗位编码' }],
|
||||
sort: [{ required: true, message: '请选择岗位排序' }],
|
||||
};
|
||||
|
||||
// Modal
|
||||
const modalVisible = ref(false);
|
||||
const modalTitle = ref('默认标题');
|
||||
|
||||
// Batch Del List
|
||||
let batchList = [];
|
||||
|
||||
// Table Columns
|
||||
const columns = [
|
||||
{ title: '岗位编号', dataIndex: 'postId' },
|
||||
{ title: '岗位编码', dataIndex: 'postCode' },
|
||||
{ title: '岗位名称', dataIndex: 'postName' },
|
||||
{ title: '岗位排序', dataIndex: 'sort' },
|
||||
{ title: '状态', dataIndex: 'status', slotName: 'status' },
|
||||
{ title: '创建时间', dataIndex: 'createdAt', slotName: 'createdAt' },
|
||||
{ title: '操作', slotName: 'action' },
|
||||
];
|
||||
|
||||
// Table Data
|
||||
const tableData = ref([]);
|
||||
|
||||
// 新增
|
||||
const handleAdd = () => {
|
||||
modalVisible.value = true;
|
||||
modalTitle.value = '新增岗位';
|
||||
};
|
||||
|
||||
// 修改
|
||||
const handleUpdate = async (record) => {
|
||||
modalVisible.value = true;
|
||||
modalTitle.value = '修改岗位';
|
||||
|
||||
await nextTick();
|
||||
Object.assign(modalForm, record);
|
||||
};
|
||||
|
||||
// Modal ok
|
||||
// 异步关闭Modal需要调用 done()
|
||||
const handleSubmit = (done) => {
|
||||
proxy.$refs.modalFormRef.validate(async (valid) => {
|
||||
if (!valid) {
|
||||
let res;
|
||||
if (!modalForm.postId) {
|
||||
const { code, msg } = await addPost(modalForm);
|
||||
if (code == 200 ) {
|
||||
proxy.$notification.success('新增成功');
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
} else {
|
||||
const { code, msg } = await updatePost(modalForm, modalForm.postId);
|
||||
if (code == 200 ) {
|
||||
proxy.$notification.success('更新成功');
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
}
|
||||
done();
|
||||
getPostInfo(pager);
|
||||
} else {
|
||||
proxy.$message.error('表单校验失败');
|
||||
done(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 批量删除
|
||||
const handleBatchDelete = () => {
|
||||
if (batchList.length !== 0) {
|
||||
proxy.$modal.warning({
|
||||
title: '提示',
|
||||
content: '是否批量删除以下选中的数据?',
|
||||
hideCancel: false,
|
||||
onOk: async () => {
|
||||
const res = await removePost({ ids: batchList });
|
||||
proxy.$message.success(res.msg);
|
||||
getPostInfo(pager);
|
||||
},
|
||||
onCancel: () => {
|
||||
proxy.$message.info('已取消批量删除数据');
|
||||
},
|
||||
});
|
||||
} else {
|
||||
proxy.$message.error('请勾选需要删除的数据!');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 分页改变
|
||||
* @param {Number} [page]
|
||||
*/
|
||||
const handlePageChange = (page) => {
|
||||
pager.pageIndex = page;
|
||||
|
||||
// 修改当前页码
|
||||
currentPage.value = page;
|
||||
getPostInfo({ ...pager, ...queryForm });
|
||||
};
|
||||
|
||||
// 每页数据量
|
||||
const handlePageSizeChange = (pageSize) => {
|
||||
pager.pageSize = pageSize;
|
||||
getPostInfo({ ...pager, ...queryForm });
|
||||
};
|
||||
|
||||
// 获取岗位信息
|
||||
const getPostInfo = async (params = {}) => {
|
||||
const { data, code, msg } = await getPost(params);
|
||||
if ( code == 200 ) {
|
||||
tableData.value = data.list;
|
||||
Object.assign(pager, { count: data.count, pageIndex: data.pageIndex, pageSize: data.pageSize });
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
};
|
||||
|
||||
// 查询岗位信息
|
||||
const handleQuery = async () => {
|
||||
const params = {
|
||||
pageIndex: pager.pageIndex,
|
||||
pageSize: pager.pageSize,
|
||||
...queryForm,
|
||||
};
|
||||
|
||||
getPostInfo(params);
|
||||
};
|
||||
|
||||
// 重置搜索
|
||||
const handleResetQuery = () => {
|
||||
proxy.$refs.queryFormRef.resetFields();
|
||||
|
||||
getPostInfo(queryForm);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getPostInfo(pager);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.action {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
</style>
|
||||
382
src/views/admin/sys-role/index.vue
Normal file
382
src/views/admin/sys-role/index.vue
Normal file
@ -0,0 +1,382 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<a-form :model="queryForm" ref="queryFormRef" layout="inline">
|
||||
<a-form-item field="roleName" label="角色名称">
|
||||
<a-input v-model="queryForm.roleName" placeholder="请输入角色名称" @press-enter="handleQuery" />
|
||||
</a-form-item>
|
||||
<a-form-item field="roleKey" label="权限字符">
|
||||
<a-input v-model="queryForm.roleKey" placeholder="请输入权限字符" @press-enter="handleQuery" />
|
||||
</a-form-item>
|
||||
<a-form-item field="status" label="状态">
|
||||
<a-select v-model="queryForm.status" placeholder="请选择角色状态">
|
||||
<a-option :value="2">正常</a-option>
|
||||
<a-option :value="1">停用</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleQuery"><icon-search /> 搜索</a-button>
|
||||
<a-button @click="handleResetQuery"><icon-loop /> 重置</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<a-divider />
|
||||
|
||||
<div class="action">
|
||||
<a-space>
|
||||
<a-button v-has="'admin:sysRole:add'" type="primary" @click="handleAdd"><icon-plus /> 新增</a-button>
|
||||
<a-button v-has="'admin:sysRole:remove'" type="primary" status="danger" @click="() => { deleteVisible = true; }"><icon-delete /> 批量删除</a-button>
|
||||
<a-button type="primary" status="warning" disabled><icon-download /> 导出</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
:pagination="{ 'show-total': true, 'show-jumper': true, 'show-page-size': true, total: pager.count, current: currentPage }"
|
||||
row-key="roleId"
|
||||
:row-selection="{ type: 'checkbox', showCheckedAll: true }"
|
||||
@selection-change="(selection) => {deleteData = selection;}"
|
||||
@select="handleSelect"
|
||||
@page-change="handlePageChange"
|
||||
@page-size-change="handlePageSizeChange"
|
||||
>
|
||||
<template #status="{ record }">
|
||||
<a-tag v-if="record.status == 2" color="green">正常</a-tag>
|
||||
<a-tag v-else color="red">停用</a-tag>
|
||||
</template>
|
||||
<template #createdAt="{ record }">
|
||||
{{ parseTime(record.createdAt) }}
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<a-space>
|
||||
<a-button v-has="'admin:sysRole:edit'" type="text" @click="handleUpdate(record)"><icon-edit /> 修改</a-button>
|
||||
<a-button v-has="'admin:sysRole:update'" type="text" @click="handleDataScope(record)"><icon-check-circle /> 数据权限 </a-button>
|
||||
<a-button v-has="'admin:sysRole:remove'" type="text" @click="() => { deleteVisible = true; deleteData = [record.roleId]; }"><icon-check-circle /> 删除 </a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-table>
|
||||
|
||||
<!-- Role Modal -->
|
||||
<a-modal
|
||||
v-model:visible="modalVisible"
|
||||
:title="title"
|
||||
title-align="start"
|
||||
@before-ok="handleBeforeOk"
|
||||
@close="handleCancel"
|
||||
>
|
||||
<a-form :model="modalForm" :rules="rules" ref="modalFormRef">
|
||||
<a-form-item field="roleName" label="角色名称">
|
||||
<a-input v-model="modalForm.roleName" placeholder="请输入角色名称" />
|
||||
</a-form-item>
|
||||
<a-form-item field="roleKey" label="权限字符">
|
||||
<a-input v-model="modalForm.roleKey" placeholder="请输入权限字符" />
|
||||
</a-form-item>
|
||||
<a-form-item field="roleSort" label="角色排序">
|
||||
<a-input-number
|
||||
v-model="modalForm.roleSort"
|
||||
mode="button"
|
||||
:default-value="0"
|
||||
:style="{ width: '150px' }"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item field="status" label="状态">
|
||||
<a-radio-group v-model="modalForm.status">
|
||||
<a-radio value="2">正常</a-radio>
|
||||
<a-radio value="1">停用</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item label="权限设置">
|
||||
<a-tree
|
||||
v-model:checked-keys="checkedKeys"
|
||||
:checkable="true"
|
||||
:check-strictly="false"
|
||||
:data="treeData"
|
||||
:default-expand-all="false"
|
||||
:field-names="{ key: 'id', title: 'label' }"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item field="remark" label="备注">
|
||||
<a-textarea v-model="modalForm.remark" placeholder="请输入备注内容" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
|
||||
<!-- DataScope Modal -->
|
||||
<a-modal
|
||||
v-model:visible="scopedModalVisible"
|
||||
:title="title"
|
||||
title-align="start"
|
||||
@before-ok="handleScopeBeforeOk"
|
||||
@close="handleCancel"
|
||||
>
|
||||
<a-form :model="scopeForm">
|
||||
<a-form-item field="roleName" label="角色名称">
|
||||
<a-input v-model="scopeForm.roleName" disabled />
|
||||
</a-form-item>
|
||||
<a-form-item field="roleKey" label="权限字符">
|
||||
<a-input v-model="scopeForm.roleKey" disabled />
|
||||
</a-form-item>
|
||||
<a-form-item field="dataScope" label="权限范围">
|
||||
<a-select v-model="scopeForm.dataScope" placeholder="请选择权限范围">
|
||||
<a-option
|
||||
v-for="item in dataScopeOptions"
|
||||
:key="item.value"
|
||||
:value="item.value"
|
||||
:label="item.label"
|
||||
/>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
|
||||
<!-- Akiraka 20230223 删除与批量删除 开始 -->
|
||||
<DeleteModal
|
||||
:data="deleteData"
|
||||
:visible="deleteVisible"
|
||||
:apiDelete="removeRole"
|
||||
@deleteVisibleChange="() => deleteVisible = false"
|
||||
/>
|
||||
<!-- Akiraka 20230223 删除与批量删除 结束 -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref, getCurrentInstance, nextTick, watch } from 'vue';
|
||||
import { getRole, addRole, updateRole, removeRole, updateRoleScoped, getRoleMenuTree } from '@/api/admin/role';
|
||||
|
||||
// Akiraka 20230210 删除数据
|
||||
const deleteData = ref([])
|
||||
// Akiraka 20230210 删除对话框
|
||||
const deleteVisible = ref(false)
|
||||
// Akiraka 20230210 监听删除事件
|
||||
watch(() => deleteVisible.value ,(value) => {
|
||||
if ( value == false ) {
|
||||
getRoleInfo({ ...pager, ...queryForm });
|
||||
}
|
||||
})
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const currentPage = ref(1);
|
||||
// Pager
|
||||
const pager = {
|
||||
count: 0,
|
||||
pageIndex: 1,
|
||||
pageSize: 10,
|
||||
};
|
||||
|
||||
// Batch Delete List
|
||||
const batchDeleteList = ref([]);
|
||||
|
||||
// Form
|
||||
const queryForm = reactive({});
|
||||
const modalForm = reactive({
|
||||
sort: 0,
|
||||
status: '2',
|
||||
});
|
||||
const scopeForm = reactive({});
|
||||
|
||||
// rules
|
||||
const rules = {
|
||||
roleName: [{ required: true, message: '请输入角色名称' }],
|
||||
roleKey: [{ required: true, message: '请输入权限字符' }],
|
||||
};
|
||||
|
||||
// ScopeOption
|
||||
const dataScopeOptions = [
|
||||
{
|
||||
value: '1',
|
||||
label: '全部数据权限',
|
||||
},
|
||||
{
|
||||
value: '2',
|
||||
label: '自定数据权限',
|
||||
},
|
||||
{
|
||||
value: '3',
|
||||
label: '本部门数据权限',
|
||||
},
|
||||
{
|
||||
value: '4',
|
||||
label: '本部门及以下数据权限',
|
||||
},
|
||||
{
|
||||
value: '5',
|
||||
label: '仅本人数据权限',
|
||||
},
|
||||
];
|
||||
|
||||
// Table Columns
|
||||
const columns = [
|
||||
{ title: '编号', dataIndex: 'roleId' },
|
||||
{ title: '角色名称', dataIndex: 'roleName' },
|
||||
{ title: '权限字符', dataIndex: 'roleKey' },
|
||||
{ title: '排序', dataIndex: 'roleSort' },
|
||||
{ title: '状态', dataIndex: 'status', slotName: 'status' },
|
||||
{ title: '创建时间', dataIndex: 'createdAt', slotName: 'createdAt' },
|
||||
{ title: '操作', slotName: 'action', width: 250 },
|
||||
];
|
||||
|
||||
// Table Data;
|
||||
const tableData = ref([]);
|
||||
|
||||
// Tree Data;
|
||||
const checkedKeys = ref([]);
|
||||
const treeData = ref([]);
|
||||
|
||||
// Modal
|
||||
const modalVisible = ref(false);
|
||||
const scopedModalVisible = ref(false);
|
||||
const title = ref('默认标题');
|
||||
|
||||
// Table Select
|
||||
const handleSelect = (rowKey) => {
|
||||
batchDeleteList.value = rowKey;
|
||||
};
|
||||
|
||||
// 查询
|
||||
const handleQuery = async () => {
|
||||
const res = await getRole({ ...pager, ...queryForm });
|
||||
const { count, list, pageIndex, pageSize } = res.data;
|
||||
|
||||
tableData.value = list;
|
||||
Object.assign(pager, { count, pageIndex, pageSize });
|
||||
};
|
||||
|
||||
// 重置查询
|
||||
const handleResetQuery = () => {
|
||||
proxy.$refs.queryFormRef.resetFields();
|
||||
getRoleInfo(queryForm);
|
||||
};
|
||||
|
||||
// 创建
|
||||
const handleAdd = () => {
|
||||
modalVisible.value = true;
|
||||
title.value = '创建角色';
|
||||
};
|
||||
|
||||
// 修改角色
|
||||
const handleUpdate = async (record) => {
|
||||
modalVisible.value = true;
|
||||
title.value = '修改角色';
|
||||
|
||||
await nextTick();
|
||||
Object.assign(modalForm, record);
|
||||
|
||||
// 显示勾选的菜单,checkedKeys 传入id数组即可
|
||||
const menuIdsChecked = [];
|
||||
record.sysMenu.forEach((item) => {
|
||||
menuIdsChecked.push(item.menuId);
|
||||
});
|
||||
checkedKeys.value = menuIdsChecked;
|
||||
};
|
||||
|
||||
// 分配数据权限
|
||||
const handleDataScope = async (record) => {
|
||||
scopedModalVisible.value = true;
|
||||
title.value = '分配数据权限';
|
||||
|
||||
const { roleKey, roleName, dataScope, roleId } = record;
|
||||
await nextTick();
|
||||
Object.assign(scopeForm, { roleKey, roleName, dataScope, roleId });
|
||||
};
|
||||
|
||||
/**
|
||||
* 分页改变
|
||||
* @param {Number} [page]
|
||||
*/
|
||||
const handlePageChange = (page) => {
|
||||
pager.pageIndex = page;
|
||||
|
||||
// 修改当前页码
|
||||
currentPage.value = page;
|
||||
getRoleInfo({ ...pager, ...queryForm });
|
||||
};
|
||||
|
||||
// 每页数据量
|
||||
const handlePageSizeChange = (pageSize) => {
|
||||
pager.pageSize = pageSize;
|
||||
getRoleInfo({ ...pager, ...queryForm });
|
||||
};
|
||||
|
||||
// 角色管理表单提交
|
||||
// 异步关闭表单需要使用 done() 回调函数
|
||||
const handleBeforeOk = (done) => {
|
||||
proxy.$refs.modalFormRef.validate(async (valid) => {
|
||||
// 如果 valid 为空则数据校验通过
|
||||
if (!valid) {
|
||||
modalForm.menuIds = checkedKeys.value;
|
||||
let res;
|
||||
if (modalForm.roleId) {
|
||||
const { code, msg } = await updateRole(modalForm, modalForm.roleId);
|
||||
if (code == 200 ) {
|
||||
proxy.$notification.success('更新成功');
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
} else {
|
||||
const { code, msg } = await addRole(modalForm);
|
||||
if (code == 200 ) {
|
||||
proxy.$notification.success('新增成功');
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
}
|
||||
getRoleInfo();
|
||||
done();
|
||||
} else {
|
||||
proxy.$message.error('数据校验失败');
|
||||
done(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 数据权限表单提交
|
||||
const handleScopeBeforeOk = async (done) => {
|
||||
const res = await updateRoleScoped(scopeForm);
|
||||
proxy.$message.success(res.msg);
|
||||
done();
|
||||
|
||||
getRoleInfo();
|
||||
};
|
||||
|
||||
// 重置数据表单
|
||||
const handleCancel = () => {
|
||||
modalVisible.value = false;
|
||||
modalForm.roleId = null;
|
||||
scopeForm.roleId = null;
|
||||
checkedKeys.value = [];
|
||||
proxy.$refs.modalFormRef.resetFields();
|
||||
};
|
||||
|
||||
// 获取角色信息
|
||||
const getRoleInfo = async (params = {}) => {
|
||||
const { data, code, msg } = await getRole(params);
|
||||
if ( code == 200 ) {
|
||||
tableData.value = data.list;
|
||||
Object.assign(pager, { count: data.count, pageIndex: data.pageIndex, pageSize: data.pageSize });
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
};
|
||||
|
||||
// 获取角色菜单信息
|
||||
const getRoleMenuTreeInfo = async () => {
|
||||
const res = await getRoleMenuTree({}, 0);
|
||||
treeData.value = res.data.menus;
|
||||
checkedKeys.value = res.data.checkedKeys;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getRoleInfo();
|
||||
getRoleMenuTreeInfo();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style setup>
|
||||
.action {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
</style>
|
||||
77
src/views/admin/sys-set/index.vue
Normal file
77
src/views/admin/sys-set/index.vue
Normal file
@ -0,0 +1,77 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<a-tabs default-active-key="1" position="left" :style="{ textAlign: 'right' }">
|
||||
<a-tab-pane key="1" title="系统内置">
|
||||
<a-form :model="form" :style="{ width: '50%' }">
|
||||
<a-form-item field="sys_app_name" label="系统名称">
|
||||
<a-input v-model="form.sys_app_name"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item field="sys_app_logo" label="系统Logo">
|
||||
<a-space>
|
||||
<div class="upload-logo-preview">
|
||||
<img width="150" height="150" />
|
||||
</div>
|
||||
<a-upload action="/">
|
||||
<template #upload-button>
|
||||
<div class="upload-logo-card">
|
||||
<div class="upload-logo-card-text">
|
||||
<IconPlus />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</a-upload>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
<a-form-item field="sys_user_initPassword" label="初始密码">
|
||||
<a-input-password v-model="form.sys_user_initPassword"></a-input-password>
|
||||
</a-form-item>
|
||||
<a-form-item field="sys_index_skinName" label="皮肤样式">
|
||||
<a-select v-model="form.sys_index_skinName">
|
||||
<a-option>蓝色</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item field="sys_index_sideTheme" label="侧边栏主题">
|
||||
<a-select v-model="form.sys_index_sideTheme">
|
||||
<a-option>深色主题</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-space>
|
||||
<a-button type="primary">提交</a-button>
|
||||
<a-button>重置</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="2" title="其它">暂无内容</a-tab-pane>
|
||||
</a-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive } from 'vue';
|
||||
import { IconPlus } from '@arco-design/web-vue/es/icon'
|
||||
|
||||
const form = reactive({});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.upload-logo-preview {
|
||||
font-size: 0px;
|
||||
}
|
||||
|
||||
.upload-logo-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
box-sizing: border-box;
|
||||
border: 1px dashed #c0ccda;
|
||||
border-radius: 5px;
|
||||
color: #4e5969;
|
||||
&-text {
|
||||
font-size: 24px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
31
src/views/admin/sys-user/components/TreeDept.vue
Normal file
31
src/views/admin/sys-user/components/TreeDept.vue
Normal file
@ -0,0 +1,31 @@
|
||||
<template>
|
||||
<div class="tree-container">
|
||||
<a-tree
|
||||
:data="props.data"
|
||||
:field-names="{ key: 'deptId', title: 'deptName' }"
|
||||
block-node
|
||||
@select="handleTreeSelect"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits(['nodeClick']);
|
||||
|
||||
const handleTreeSelect = (selectKeys) => {
|
||||
emits('nodeClick', { deptId: `/${selectKeys}/` });
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.tree-container {
|
||||
padding-right: 20px;
|
||||
}
|
||||
</style>
|
||||
621
src/views/admin/sys-user/index.vue
Normal file
621
src/views/admin/sys-user/index.vue
Normal file
@ -0,0 +1,621 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!-- Query -->
|
||||
<a-form ref="queryFormRef" :model="queryForm" layout="inline">
|
||||
<a-form-item field="deptName" label="部门名称">
|
||||
<a-input v-model="queryForm.deptName" placeholder="请输入部门名称" @press-enter="handleQuery" />
|
||||
</a-form-item>
|
||||
<a-form-item field="username" label="用户名称">
|
||||
<a-input v-model="queryForm.username" placeholder="请输入用户名称" @press-enter="handleQuery" />
|
||||
</a-form-item>
|
||||
<a-form-item field="phone" label="手机号码">
|
||||
<a-input v-model="queryForm.phone" placeholder="请输入用户手机号" @press-enter="handleQuery" />
|
||||
</a-form-item>
|
||||
<a-form-item field="status" label="用户状态">
|
||||
<a-select
|
||||
v-model="queryForm.status"
|
||||
placeholder="请选择用户状态"
|
||||
:style="{ width: '205px' }"
|
||||
>
|
||||
<a-option value="2">正常</a-option>
|
||||
<a-option value="1">停用</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
|
||||
<a-divider direction="vertical" :style="{ height: '30px' }" />
|
||||
<a-form-item class="form-action">
|
||||
<a-space size="medium">
|
||||
<a-button v-has="'admin:sysUser:query'" type="primary" @click="handleQuery"><icon-search /> 搜索</a-button>
|
||||
<a-button @click="handleResetQuery"><icon-loop /> 重置</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<!-- divider -->
|
||||
<a-divider />
|
||||
|
||||
<!-- Table -->
|
||||
<a-row>
|
||||
<a-col :span="4">
|
||||
<!-- tree组件只在组件第一次渲染时展开,此处等待数据加载完成再渲染组件 -->
|
||||
<tree-dept v-if="treeDeptData" :data="treeDeptData" @node-click="getSysUserInfo" />
|
||||
</a-col>
|
||||
<a-col :span="20">
|
||||
<!-- Action -->
|
||||
<a-space class="action">
|
||||
<a-button v-has="'admin:sysUser:add'" type="primary" @click="handleAdd" data-test="newUser"><icon-plus /> 新增</a-button>
|
||||
<a-button v-has="'admin:sysUser:remove'" type="primary" status="danger" @click="() => { deleteVisible = true; }"><icon-delete /> 批量删除</a-button>
|
||||
</a-space>
|
||||
|
||||
<!-- Table -->
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
:bordered="false"
|
||||
:row-selection="{ type: 'checkbox', showCheckedAll: true }"
|
||||
:pagination="{ 'show-total': true, 'show-jumper': true, 'show-page-size': true, total: pager.count, current: currentPage }"
|
||||
row-key="userId"
|
||||
@selection-change="(selection) => {deleteData = selection;}"
|
||||
@page-change="handlePageChange"
|
||||
@page-size-change="handlePageSizeChange"
|
||||
>
|
||||
<template #dept="{ record }">
|
||||
{{ record.dept.deptName }}
|
||||
</template>
|
||||
<template #status="{ record }">
|
||||
<a-switch
|
||||
v-model="record.status"
|
||||
checked-value="2"
|
||||
unchecked-value="1"
|
||||
@change="handleSwitchChange(record)"
|
||||
/>
|
||||
</template>
|
||||
<template #createdAt="{ record }">
|
||||
{{ parseTime(record.createdAt) }}
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<a-button v-has="'admin:sysUser:edit'" type="text" @click="handleUpdate(record)"><icon-edit /> 修改</a-button>
|
||||
<a-button v-has="'admin:sysUser:edit'" type="text" @click="() => { deleteVisible = true; deleteData = [record.userId]; }"><icon-delete /> 删除</a-button>
|
||||
<a-button v-has="'admin:sysUser:resetPassword'" type="text" @click="handleReset(record.userId)"><icon-refresh /> 重置</a-button>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<!-- Modal -->
|
||||
<a-modal
|
||||
v-model:visible="modalVisible"
|
||||
title-align="start"
|
||||
:width="600"
|
||||
@cancel="handleModalCancel('modalFormRef')"
|
||||
@before-ok="handleBeforeOk"
|
||||
>
|
||||
<template #title>
|
||||
{{ modalTitle }}
|
||||
</template>
|
||||
<a-form
|
||||
ref="modalFormRef"
|
||||
:model="modalForm"
|
||||
:rules="rules"
|
||||
auto-label-width
|
||||
>
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item field="nickName" label="用户昵称">
|
||||
<a-input
|
||||
v-model="modalForm.nickName"
|
||||
placeholder="请输入用户昵称"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="deptId" label="所属部门">
|
||||
<a-tree-select
|
||||
v-model="modalForm.deptId"
|
||||
:data="treeDeptData"
|
||||
:field-names="{ key: 'deptId', title: 'deptName' }"
|
||||
placeholder="请选择所属部门"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="phone" label="手机号码">
|
||||
<a-input v-model="modalForm.phone" placeholder="请输入手机号码" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="email" label="邮箱">
|
||||
<a-input v-model="modalForm.email" placeholder="请输入邮箱" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="username" label="用户名称">
|
||||
<a-input
|
||||
v-model="modalForm.username"
|
||||
placeholder="请输入用户名称"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col v-if="!modalForm.userId" :span="12">
|
||||
<a-form-item field="password" label="用户密码">
|
||||
<a-input-password
|
||||
v-model="modalForm.password"
|
||||
placeholder="请输入用户密码"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="sex" label="用户性别">
|
||||
<a-select v-model="modalForm.sex" placeholder="请选择用户性别">
|
||||
<a-option value="0"> 男 </a-option>
|
||||
<a-option value="1"> 女 </a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="status" label="状态">
|
||||
<a-radio-group v-model="modalForm.status">
|
||||
<a-radio value="2"> 正常 </a-radio>
|
||||
<a-radio value="1"> 停用 </a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="postId" label="岗位">
|
||||
<a-select v-model="modalForm.postId" placeholder="请选择岗位">
|
||||
<a-option
|
||||
v-for="item in postList"
|
||||
:key="item.postId"
|
||||
:value="item.postId"
|
||||
:label="item.postName"
|
||||
/>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="roleId" label="角色">
|
||||
<a-select v-model="modalForm.roleId" placeholder="请选择角色">
|
||||
<a-option
|
||||
v-for="item in roleList"
|
||||
:key="item.roleId"
|
||||
:value="item.roleId"
|
||||
:label="item.roleName"
|
||||
/>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-form-item field="remark" label="备注">
|
||||
<a-textarea placeholder="请输入备注" allow-clear />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
|
||||
<a-modal
|
||||
v-model:visible="resetPwdVisible"
|
||||
title="重置密码"
|
||||
@before-ok="handleResetPwd"
|
||||
@cancel="$refs.resetPwdFormRef.resetFields()"
|
||||
>
|
||||
<a-form
|
||||
ref="resetPwdFormRef"
|
||||
:model="resetPwdForm"
|
||||
:rules="resetPwdRules"
|
||||
auto-label-width
|
||||
>
|
||||
<a-form-item field="password" label="新密码">
|
||||
<a-input-password
|
||||
v-model="resetPwdForm.password"
|
||||
placeholder="请输入新密码"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item field="repeatPwd" label="确认密码">
|
||||
<a-input-password
|
||||
v-model="resetPwdForm.repeatPwd"
|
||||
placeholder="请输入确认密码"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
|
||||
<!-- Akiraka 20230223 删除与批量删除 开始 -->
|
||||
<DeleteModal
|
||||
:data="deleteData"
|
||||
:visible="deleteVisible"
|
||||
:apiDelete="removeUser"
|
||||
@deleteVisibleChange="() => deleteVisible = false"
|
||||
/>
|
||||
<!-- Akiraka 20230223 删除与批量删除 结束 -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref, getCurrentInstance, onMounted, watch } from 'vue';
|
||||
import { IconSearch, IconLoop } from '@arco-design/web-vue/es/icon';
|
||||
import TreeDept from './components/TreeDept.vue';
|
||||
import { getUser, addUser, updateUser, removeUser, updateUserStatus, resetUserPwd } from '@/api/admin/sys-user';
|
||||
import { getRole } from '@/api/admin/role';
|
||||
import { getPost } from '@/api/admin/post';
|
||||
import { getDept } from '@/api/admin/sys-dept';
|
||||
|
||||
// Akiraka 20230210 删除数据
|
||||
const deleteData = ref([])
|
||||
// Akiraka 20230210 删除对话框
|
||||
const deleteVisible = ref(false)
|
||||
// Akiraka 20230210 监听删除事件
|
||||
watch(() => deleteVisible.value ,(value) => {
|
||||
if ( value == false ) {
|
||||
getSysUserInfo({ ...pager, ...queryForm });
|
||||
}
|
||||
})
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
// Query
|
||||
const { queryForm, handleQuery, handleResetQuery } = useQueryData();
|
||||
// ApiInfo
|
||||
const { currentPage, getSysPostInfo, getSysRoleInfo, getSysDeptTreeInfo, getSysUserInfo } =
|
||||
useApiInfo();
|
||||
|
||||
// Pager
|
||||
const pager = reactive({
|
||||
count: 0,
|
||||
pageIndex: 1,
|
||||
pageSize: 10
|
||||
});
|
||||
|
||||
// Reset Pwd
|
||||
const {
|
||||
resetPwdForm,
|
||||
resetPwdVisible,
|
||||
resetPwdRules,
|
||||
handleReset,
|
||||
handleResetPwd,
|
||||
} = useResetPwd();
|
||||
|
||||
// Table Operate
|
||||
const {
|
||||
columns,
|
||||
tableData,
|
||||
treeDeptData,
|
||||
roleList,
|
||||
postList,
|
||||
handlePageChange,
|
||||
handlePageSizeChange,
|
||||
handleSwitchChange,
|
||||
} = useTableList();
|
||||
|
||||
// ModalForm Operate
|
||||
const {
|
||||
rules,
|
||||
modalForm,
|
||||
modalVisible,
|
||||
modalTitle,
|
||||
handleAdd,
|
||||
handleUpdate,
|
||||
handleBeforeOk,
|
||||
handleModalCancel,
|
||||
} = useModalOperate();
|
||||
|
||||
|
||||
function useQueryData() {
|
||||
const queryForm = reactive({});
|
||||
|
||||
// 查询
|
||||
const handleQuery = () => {
|
||||
getSysUserInfo(queryForm);
|
||||
};
|
||||
|
||||
// 重置查询
|
||||
const handleResetQuery = () => {
|
||||
proxy.$refs.queryFormRef.resetFields();
|
||||
|
||||
getSysUserInfo(queryForm);
|
||||
};
|
||||
|
||||
return { queryForm, handleQuery, handleResetQuery };
|
||||
}
|
||||
|
||||
function useResetPwd() {
|
||||
const resetPwdVisible = ref(false);
|
||||
const resetPwdForm = reactive({});
|
||||
|
||||
// Rules
|
||||
const resetPwdRules = {
|
||||
password: [{ required: true, message: '请输入密码' }],
|
||||
repeatPwd: [
|
||||
{
|
||||
required: true,
|
||||
message: '请重复输入密码',
|
||||
},
|
||||
{
|
||||
validator: (value, cb) => {
|
||||
if (value !== resetPwdForm.password) {
|
||||
cb('两次输入的密码不一致');
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// 用户重置密码API参数
|
||||
const resetParams = {};
|
||||
|
||||
// 重置
|
||||
const handleReset = (userId) => {
|
||||
resetPwdVisible.value = true;
|
||||
resetParams.userId = userId;
|
||||
};
|
||||
|
||||
// 重置用户密码
|
||||
const handleResetPwd = (done) => {
|
||||
proxy.$refs.resetPwdFormRef.validate(async (err) => {
|
||||
if (!err) {
|
||||
resetParams.password = resetPwdForm.password;
|
||||
const { msg } = await resetUserPwd(resetParams);
|
||||
proxy.$message.success(msg);
|
||||
done();
|
||||
getSysUserInfo();
|
||||
} else {
|
||||
done(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
resetPwdForm,
|
||||
resetPwdVisible,
|
||||
resetPwdRules,
|
||||
handleReset,
|
||||
handleResetPwd,
|
||||
};
|
||||
}
|
||||
|
||||
function useApiInfo() {
|
||||
const currentPage = ref(1);
|
||||
/**
|
||||
* 获取用户信息
|
||||
* @param {*} [params]
|
||||
*/
|
||||
const getSysUserInfo = async (params = {}) => {
|
||||
const { data, code, msg } = await getUser(params);
|
||||
if ( code == 200 ) {
|
||||
tableData.value = data.list;
|
||||
Object.assign(pager, { count: data.count, pageIndex: data.pageIndex, pageSize: data.pageSize });
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
};
|
||||
|
||||
// 获取角色信息
|
||||
const getSysRoleInfo = async () => {
|
||||
const res = await getRole();
|
||||
roleList.value = res.data.list;
|
||||
};
|
||||
|
||||
// 获取岗位信息
|
||||
const getSysPostInfo = async () => {
|
||||
const res = await getPost();
|
||||
postList.value = res.data.list;
|
||||
};
|
||||
|
||||
// 获取部门树信息
|
||||
const getSysDeptTreeInfo = async () => {
|
||||
const res = await getDept();
|
||||
treeDeptData.value = res.data;
|
||||
};
|
||||
|
||||
return {
|
||||
currentPage,
|
||||
getSysPostInfo,
|
||||
getSysRoleInfo,
|
||||
getSysDeptTreeInfo,
|
||||
getSysUserInfo,
|
||||
};
|
||||
}
|
||||
|
||||
function useTableList() {
|
||||
// 部门数据
|
||||
const treeDeptData = ref();
|
||||
const roleList = ref([]);
|
||||
const postList = ref([]);
|
||||
|
||||
// Table columns
|
||||
const columns = [
|
||||
{
|
||||
title: '编号',
|
||||
dataIndex: 'userId',
|
||||
},
|
||||
{
|
||||
title: '登录名',
|
||||
dataIndex: 'username',
|
||||
},
|
||||
{
|
||||
title: '昵称',
|
||||
dataIndex: 'nickName',
|
||||
},
|
||||
{
|
||||
title: '部门',
|
||||
dataIndex: 'deptName',
|
||||
slotName: 'dept',
|
||||
},
|
||||
{
|
||||
title: '手机号',
|
||||
dataIndex: 'phone',
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
slotName: 'status',
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createdAt',
|
||||
slotName: 'createdAt',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
slotName: 'action',
|
||||
},
|
||||
];
|
||||
|
||||
// Table Data
|
||||
const tableData = ref([]);
|
||||
|
||||
/**
|
||||
* 分页改变
|
||||
* @param {Number} [page]
|
||||
*/
|
||||
const handlePageChange = (page) => {
|
||||
pager.pageIndex = page;
|
||||
|
||||
// 修改当前页码
|
||||
currentPage.value = page;
|
||||
getSysUserInfo({ ...pager, ...queryForm });
|
||||
};
|
||||
|
||||
// 每页数据量
|
||||
const handlePageSizeChange = (pageSize) => {
|
||||
pager.pageSize = pageSize;
|
||||
getSysUserInfo({ ...pager, ...queryForm });
|
||||
};
|
||||
|
||||
// 用户状态快速切换
|
||||
const handleSwitchChange = (record) => {
|
||||
proxy.$modal.warning({
|
||||
title: '注意',
|
||||
content: `是否${record.status == 1 ? '停用' : '启用'} ${
|
||||
record.username
|
||||
} 用户?`,
|
||||
hideCancel: false,
|
||||
onOk: async () => {
|
||||
const params = { userId: record.userId, status: record.status };
|
||||
const res = await updateUserStatus(params);
|
||||
proxy.$message.success(res.msg);
|
||||
},
|
||||
onCancel: () => {
|
||||
record.status = record.status == '2' ? '1' : '2';
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
treeDeptData,
|
||||
roleList,
|
||||
postList,
|
||||
columns,
|
||||
tableData,
|
||||
handlePageChange,
|
||||
handlePageSizeChange,
|
||||
handleSwitchChange,
|
||||
};
|
||||
}
|
||||
|
||||
function useModalOperate() {
|
||||
const modalVisible = ref(false);
|
||||
const modalTitle = ref('默认标题');
|
||||
|
||||
// Form
|
||||
const modalForm = reactive({ status: '2' });
|
||||
|
||||
// AddRules
|
||||
const rules = {
|
||||
nickName: [{ required: true, message: '请输入用户昵称' }],
|
||||
deptId: [{ required: true, message: '请选择所属部门' }],
|
||||
phone: [{ required: true, message: '请输入手机号' }],
|
||||
email: [
|
||||
{ required: true, message: '请输入邮箱' },
|
||||
{ type: 'email', message: '请输入正确的邮箱格式' },
|
||||
],
|
||||
username: [{ required: true, message: '请输入用户名称' }],
|
||||
password: [{ required: true, message: '请输入用户密码' }],
|
||||
};
|
||||
|
||||
// Modal 取消后重置表单
|
||||
const handleModalCancel = (formEl) => {
|
||||
modalVisible.value = false;
|
||||
resetForm(formEl);
|
||||
modalForm.userId = null;
|
||||
};
|
||||
|
||||
// 新增用户
|
||||
const handleAdd = () => {
|
||||
modalVisible.value = true;
|
||||
modalTitle.value = '新增用户';
|
||||
};
|
||||
|
||||
/**
|
||||
* 修改用户
|
||||
* @param {Object} val
|
||||
*/
|
||||
const handleUpdate = (val) => {
|
||||
modalVisible.value = true;
|
||||
Object.assign(modalForm, val);
|
||||
};
|
||||
|
||||
// 重置Form
|
||||
const resetForm = (formEl) => {
|
||||
if (!formEl) return;
|
||||
proxy.$refs[formEl].resetFields();
|
||||
};
|
||||
|
||||
// 表单提交
|
||||
const handleBeforeOk = (done) => {
|
||||
proxy.$refs.modalFormRef.validate(async (valid) => {
|
||||
if (!valid) {
|
||||
if (!modalForm.userId) {
|
||||
const { code, msg } = await addUser(modalForm);
|
||||
if (code == 200 ) {
|
||||
proxy.$notification.success('新增成功');
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
} else {
|
||||
const { code, msg } = await updateUser(modalForm, modalForm.userId);
|
||||
if (code == 200 ) {
|
||||
proxy.$notification.success('更新成功');
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
}
|
||||
done();
|
||||
proxy.$refs.modalFormRef.resetFields();
|
||||
getSysUserInfo();
|
||||
} else {
|
||||
proxy.$message.error('表单校验失败');
|
||||
done(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
rules,
|
||||
modalForm,
|
||||
modalVisible,
|
||||
modalTitle,
|
||||
handleAdd,
|
||||
handleUpdate,
|
||||
handleBeforeOk,
|
||||
handleModalCancel,
|
||||
};
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getSysUserInfo();
|
||||
getSysDeptTreeInfo();
|
||||
getSysRoleInfo();
|
||||
getSysPostInfo();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@media screen and (max-width: 1720px) {
|
||||
.form-action {
|
||||
margin-top: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.action {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
</style>
|
||||
410
src/views/dev-tools/gen/editTable.vue
Normal file
410
src/views/dev-tools/gen/editTable.vue
Normal file
@ -0,0 +1,410 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<a-form :model="dataInfo" :rules="rules" ref="modalFormRef" auto-label-width size="mini">
|
||||
<a-card bordered>
|
||||
<!-- 卡片插槽 开始 -->
|
||||
<template #actions>
|
||||
<a-button type="primary" @click="handleSubmit" long>提交</a-button>
|
||||
<a-button type="primary" @click="close" long>返回</a-button>
|
||||
</template>
|
||||
<!-- 卡片插槽 结束 -->
|
||||
<a-tabs default-active-key="2">
|
||||
<a-tab-pane key="1" title="基本信息">
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="12">
|
||||
<a-form-item field="tableName">
|
||||
<template #label>
|
||||
数据表名称
|
||||
<a-tooltip content="数据库表名称,针对gorm对应的table()使用,⚠️这里必须是蛇形结构">
|
||||
<icon-question-circle-fill />
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input v-model="dataInfo.tableName" placeholder="请输入表名称" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="tableComment">
|
||||
<template #label>
|
||||
菜单名称
|
||||
<a-tooltip content="同步的数据库表名称,生成配置数据时,用作菜单名称">
|
||||
<icon-question-circle-fill />
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input v-model="dataInfo.tableComment" placeholder="请输入菜单名称" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="className">
|
||||
<template #label>
|
||||
结构体模型名称
|
||||
<a-tooltip content="结构体模型名称,代码中的struct名称定义使用">
|
||||
<icon-question-circle-fill />
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input v-model="dataInfo.className" placeholder="请输入" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="functionAuthor" label="作者名称">
|
||||
<a-input v-model="dataInfo.functionAuthor" placeholder="请输入作者名称" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item field="remark" label="备注">
|
||||
<a-textarea v-model="dataInfo.remark" placeholder="请输入备注" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-tab-pane>
|
||||
|
||||
<a-tab-pane key="2" title="字段信息">
|
||||
<a-alert type="warning" style="margin-bottom: 16px;">表字段中的id、create_by、update_by、created_at、updated_at、deleted_at的字段在此列表中已经隐藏.</a-alert>
|
||||
<a-table :data="tableData" :virtual-list-props="{height:600}" :pagination="false" :scroll="{x: '100%'}">
|
||||
<template #columns>
|
||||
<!-- <a-table-column title="序号" data-index="tableId" :width="180" ellipsis tooltip/> -->
|
||||
<a-table-column title="字段列名" data-index="columnName"/>
|
||||
<a-table-column title="字段描述" :width="120">
|
||||
<template #cell="{ record }">
|
||||
<a-input v-model="record.columnComment" />
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="物理类型" data-index="columnType" align="center" :width="120" />
|
||||
<a-table-column title="go类型" align="center" :width="140">
|
||||
<template #cell="{ record }">
|
||||
<a-select v-model="record.goType" placeholder="请选择 ...">
|
||||
<a-option value="int64">int64</a-option>
|
||||
<a-option value="string">string</a-option>
|
||||
</a-select>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="go属性" align="center" >
|
||||
<template #cell="{ record }">
|
||||
<a-input v-model="record.goField"/>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="json属性" align="center" >
|
||||
<template #cell="{ record }">
|
||||
<a-input v-model="record.jsonField"/>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="编辑" align="center" :width="60">
|
||||
<template #cell="{ record }">
|
||||
<a-switch v-model="record.isInsert" type="large" checked-value="1" unchecked-value="0">
|
||||
<template #checked-icon>
|
||||
<icon-check/>
|
||||
</template>
|
||||
<template #unchecked-icon>
|
||||
<icon-close/>
|
||||
</template>
|
||||
</a-switch>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="列表" align="center" :width="60">
|
||||
<template #cell="{ record }">
|
||||
<a-switch v-model="record.isList" type="large" checked-value="1" unchecked-value="0">
|
||||
<template #checked-icon>
|
||||
<icon-check/>
|
||||
</template>
|
||||
<template #unchecked-icon>
|
||||
<icon-close/>
|
||||
</template>
|
||||
</a-switch>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="查询" align="center" :width="60">
|
||||
<template #cell="{ record }">
|
||||
<a-switch v-model="record.isQuery" type="large" checked-value="1" unchecked-value="0">
|
||||
<template #checked-icon>
|
||||
<icon-check/>
|
||||
</template>
|
||||
<template #unchecked-icon>
|
||||
<icon-close/>
|
||||
</template>
|
||||
</a-switch>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="查询方式" :width="90">
|
||||
<template #cell="{ record }">
|
||||
<a-select v-model="record.queryType" placeholder="请选择 ...">
|
||||
<a-option label="=" value="EQ" />
|
||||
<a-option label="!=" value="NE" />
|
||||
<a-option label=">" value="GT" />
|
||||
<a-option label=">=" value="GTE" />
|
||||
<a-option label="<" value="LT" />
|
||||
<a-option label="<=" value="LTE" />
|
||||
<a-option label="LIKE" value="LIKE" />
|
||||
</a-select>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="必填" :width="70">
|
||||
<template #cell="{ record }">
|
||||
<!-- <a-checkbox v-model="record.isRequired"/> -->
|
||||
<a-switch v-model="record.isRequired" type="large" checked-value="1" unchecked-value="0">
|
||||
<template #checked-icon>
|
||||
<icon-check/>
|
||||
</template>
|
||||
<template #unchecked-icon>
|
||||
<icon-close/>
|
||||
</template>
|
||||
</a-switch>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="显示类型" :width="140">
|
||||
<template #cell="{ record }">
|
||||
<a-select v-model="record.htmlType" placeholder="请选择 ...">
|
||||
<a-option value="input">文本框</a-option>
|
||||
<a-option value="select">下拉框</a-option>
|
||||
<a-option value="radio">单选框</a-option>
|
||||
<a-option value="textarea">文本域</a-option>
|
||||
</a-select>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="字典类型">
|
||||
<template #cell="{ record }">
|
||||
<a-select v-model="record.dictType" allow-clear allow-search placeholder="请选择">
|
||||
<a-option v-for="dict in dictOptions" :key="dict.dictType" :label="dict.dictName" :value="dict.dictType" >
|
||||
<a-descriptions :column="1" size="mini">
|
||||
<a-descriptions-item :label="dict.dictName">
|
||||
{{ dict.dictType }}
|
||||
</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
</a-option>
|
||||
</a-select>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="关系表">
|
||||
<template #cell="{ record }">
|
||||
<a-select v-model="record.fkTableName" allow-clear allow-search placeholder="请选择" @change="handleChangeConfig(record)">
|
||||
<a-option v-for="table in tableTree" :key="table.tableName" :label="table.tableName" :value="table.tableName">
|
||||
<span style="float: left; margin-right: 5px; font-size: 12px;">{{ table.tableName }}</span>
|
||||
<span style="float: right; color: #8492a6; font-size: 13px">{{ table.tableComment }}</span>
|
||||
</a-option>
|
||||
</a-select>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="关系表key">
|
||||
<template #cell="{ record }">
|
||||
<a-select v-model="record.fkLabelId" allow-clear allow-search placeholder="请选择">
|
||||
<a-option v-for="column in record.fkCol" :key="column.columnName" :label="column.columnName" :value="column.jsonField">
|
||||
<span style="float: left; margin-right: 5px; font-size: 12px;">{{ column.jsonField }}</span>
|
||||
<span style="float: right; color: #8492a6; font-size: 12px;">{{ column.columnComment }}</span>
|
||||
</a-option>
|
||||
</a-select>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="关系表value">
|
||||
<template #cell="{ record }">
|
||||
<a-select v-model="record.fkLabelName" allow-clear allow-search placeholder="请选择">
|
||||
<a-option v-for="column in record.fkCol" :key="column.columnName" :label="column.columnName" :value="column.jsonField">
|
||||
<span style="float: left; margin-right: 5px; font-size: 12px;">{{ column.jsonField }}</span>
|
||||
<span style="float: right; color: #8492a6; font-size: 13px">{{ column.columnComment }}</span>
|
||||
</a-option>
|
||||
</a-select>
|
||||
</template>
|
||||
</a-table-column>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-tab-pane>
|
||||
|
||||
<a-tab-pane key="3" title="生成信息">
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="12">
|
||||
<a-form-item field="tplCategory" label="生成模板">
|
||||
<a-select v-model="dataInfo.tplCategory">
|
||||
<a-option label="关系表(增删改查)" value="crud" />
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="packageName">
|
||||
<template #label>
|
||||
应用名
|
||||
<a-tooltip content="应用名,例如:在app文件夹下将该功能发到那个应用中,默认:admin">
|
||||
<icon-question-circle-fill />
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input v-model="dataInfo.packageName" placeholder="请输入应用名" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="businessName">
|
||||
<template #label>
|
||||
业务名
|
||||
<a-tooltip content="可理解为功能英文名,例如 user">
|
||||
<icon-question-circle-fill />
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input v-model="dataInfo.businessName" placeholder="请输入业务名" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="functionName" label="功能描述">
|
||||
<template #label>
|
||||
功能描述
|
||||
<a-tooltip content="同步的数据库表备注,用作类描述,例如:用户">
|
||||
<icon-question-circle-fill />
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input v-model="dataInfo.functionName" placeholder="请输入功能描述" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="moduleName">
|
||||
<template #label>
|
||||
接口路径
|
||||
<a-tooltip content="接口路径,例如:api/v1/{sys-user}">
|
||||
<icon-question-circle-fill />
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input v-model="dataInfo.moduleName" placeholder="请输入接口路径" >
|
||||
<template #prepend>api/{version}/</template>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</a-card>
|
||||
</a-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref, getCurrentInstance, onMounted, unref } from 'vue';
|
||||
import { optionselect as getDictOptionselect } from '@/api/admin/sys-dict'
|
||||
import { getGenTable, updateGenTable, getTableTree } from '@/api/tools/gen';
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
// Rules
|
||||
const rules = {
|
||||
tableName: [
|
||||
{ required: true, message: '请输入表名称' },
|
||||
{ match: /^[a-z\._]*$/g, message: '只允许小写字母,例如 sys_demo 格式'}
|
||||
],
|
||||
tableComment: [
|
||||
{ required: true, message: '请输入菜单名称' },
|
||||
],
|
||||
className: [
|
||||
{ required: true, message: '请输入模型名称' },
|
||||
{ match: /^[A-Z][A-z0-9]*$/g, message: '只允许小写字母,例如 sys_demo 格式'}
|
||||
],
|
||||
functionAuthor: [
|
||||
{ required: true, message: '请输入作者' },
|
||||
{ match: /^[A-Za-z]+$/, message: '必须以大写字母开头,例如 SysDemo 格式'}
|
||||
],
|
||||
tplCategory: [
|
||||
{ required: true, message: '请选择生成模板' },
|
||||
],
|
||||
packageName: [
|
||||
{ required: true, message: '请输入生成包路径' },
|
||||
],
|
||||
moduleName: [
|
||||
{ required: true, message: '请输入生成模块名' },
|
||||
{ match: /^[a-z\-]*[a-z]$/g, message: '只允许小写字母,例如 sys-demo 格式'}
|
||||
],
|
||||
businessName: [
|
||||
{ required: true, message: '请输入生成业务名' },
|
||||
{ match: /^[a-z][A-Za-z]+$/, message: '校验规则: 只允许输入字母 a-z 或大写 A-Z ,并且小写字母开头'}
|
||||
],
|
||||
functionName: [
|
||||
{ required: true, message: '请输入生成功能名' },
|
||||
],
|
||||
}
|
||||
|
||||
// 字符串转布尔类型
|
||||
const stringToBool = (value) => {
|
||||
if (value == "1") {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 表详细信息
|
||||
const dataInfo = ref({})
|
||||
// 默认表格内容
|
||||
const tableData = ref([]);
|
||||
// 获取 服务数据
|
||||
const getGenTableQuery = async (params = {}) => {
|
||||
const res = await getGenTable(params);
|
||||
tableData.value = res.data.list;
|
||||
// 编辑 isInsert
|
||||
// 列表 isList
|
||||
// 查询 isQuery
|
||||
// 必填 isRequired
|
||||
dataInfo.value = res.data.info;
|
||||
};
|
||||
|
||||
|
||||
// 接收数据库表树
|
||||
const tableTree = ref([]);
|
||||
// 获取数据库表树
|
||||
const getTableTreeQuery = async (params = {}) => {
|
||||
getTableTree().then(response => {
|
||||
tableTree.value = response.data
|
||||
tableTree.value.unshift({ tableId: 0, className: '请选择' })
|
||||
})
|
||||
};
|
||||
// 关系表
|
||||
const handleChangeConfig = (row) => {
|
||||
tableTree.value.filter(function(item) {
|
||||
if (item.tableName === row.fkTableName) {
|
||||
row.fkCol = item.columns
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 字典类型列表
|
||||
const dictOptions = ref([]);
|
||||
// 查询字典下拉列表
|
||||
const getDictOption = async () => {
|
||||
const res =await getDictOptionselect();
|
||||
dictOptions.value = res.data
|
||||
};
|
||||
|
||||
|
||||
const modalFormRef = ref(null);
|
||||
// handleSubmit 修改按钮方法 20221101
|
||||
const handleSubmit = () => {
|
||||
const genTable = Object.assign({}, dataInfo.value)
|
||||
genTable.columns = tableData.value
|
||||
genTable.params = {
|
||||
treeCode: genTable.treeCode,
|
||||
treeName: genTable.treeName,
|
||||
treeParentCode: genTable.treeParentCode
|
||||
}
|
||||
|
||||
proxy.$refs.modalFormRef.validate(async (valid) => {
|
||||
if (!valid) {
|
||||
const { code, msg } = await updateGenTable(genTable);
|
||||
if (code == 200 ) {
|
||||
proxy.$notification.success('修改成功');
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
// 成功调用关闭方法
|
||||
close()
|
||||
} else {
|
||||
proxy.$message.error('表单校验失败');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/** 关闭按钮 */
|
||||
const close = () => {
|
||||
proxy.$router.push({ path: '/dev-tools/gen', query: { t: Date.now() }})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
const { tableId } = proxy.$route.query
|
||||
getGenTableQuery(tableId);
|
||||
getTableTreeQuery()
|
||||
getDictOption()
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
</style>
|
||||
145
src/views/dev-tools/gen/importTable.vue
Normal file
145
src/views/dev-tools/gen/importTable.vue
Normal file
@ -0,0 +1,145 @@
|
||||
<template>
|
||||
<a-modal v-model:visible="visible" title="导入表" @ok="handleImportTable" :ok-loading="loading" @cancel="visible = false" width="50%">
|
||||
<a-form :model="queryForm" ref="queryFormRef" layout="inline">
|
||||
<a-form-item field="tableName" label="表名称">
|
||||
<a-input v-model="queryForm.tableName" placeholder="请输入表名称" />
|
||||
</a-form-item>
|
||||
<a-form-item field="tableComment" label="表描述">
|
||||
<a-input v-model="queryForm.tableComment" placeholder="请输入表描述" />
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleQuery"><icon-search /> 搜索</a-button>
|
||||
<a-button @click="handleResetQuery"><icon-loop /> 重置</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<a-divider />
|
||||
|
||||
<a-table
|
||||
:data="tableData"
|
||||
:pagination="{
|
||||
'show-total': true,
|
||||
'show-jumper': true,
|
||||
'show-page-size': true,
|
||||
total: pager.count,
|
||||
current: currentPage,
|
||||
}"
|
||||
:row-selection="{ type: 'checkbox', showCheckedAll: true }"
|
||||
row-key="tableName"
|
||||
@selection-change="selectionChange"
|
||||
@page-change="handlePageChange"
|
||||
@page-size-change="handlePageSizeChange"
|
||||
>
|
||||
<template #columns>
|
||||
<a-table-column title="表名称" data-index="tableName" :width="180" ellipsis tooltip/>
|
||||
<a-table-column title="表描述" data-index="tableComment" :width="180" ellipsis tooltip/>
|
||||
<a-table-column title="创建时间" :width="180" ellipsis>
|
||||
<template #cell="{ record }">
|
||||
{{ parseTime(record.createTime) }}
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="更新时间" :width="180" ellipsis>
|
||||
<template #cell="{ record }">
|
||||
{{ parseTime(record.updateTime) }}
|
||||
</template>
|
||||
</a-table-column>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref, onMounted, getCurrentInstance } from 'vue';
|
||||
import { listDbTable,importTable,listTable} from '@/api/tools/gen';
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
// 对话框默认值
|
||||
const visible = ref(false);
|
||||
|
||||
// 按钮加载中状态
|
||||
const loading = ref(false)
|
||||
|
||||
// 对话框确认
|
||||
const handleImportTable = () => {
|
||||
loading.value = true
|
||||
importTable({ tables: batchList.join(',') }).then(res => {
|
||||
if (res.code === 200) {
|
||||
proxy.$message.info(res.msg);
|
||||
// 关闭对话框
|
||||
visible.value = false;
|
||||
// 完成刷新下页面
|
||||
location.reload();
|
||||
} else {
|
||||
proxy.$message.info(res.msg);
|
||||
}
|
||||
loading.value = false
|
||||
})
|
||||
};
|
||||
|
||||
const queryForm = reactive({});
|
||||
// 查询列表信息
|
||||
const handleQuery = async () => {
|
||||
const params = {
|
||||
pageIndex: pager.pageIndex,
|
||||
pageSize: pager.pageSize,
|
||||
...queryForm,
|
||||
};
|
||||
|
||||
getDbTables(params);
|
||||
};
|
||||
// 重置搜索
|
||||
const handleResetQuery = () => {
|
||||
proxy.$refs.queryFormRef.resetFields();
|
||||
getDbTables(queryForm);
|
||||
}
|
||||
|
||||
|
||||
const tableData = ref([]);
|
||||
// Batch Del List
|
||||
let batchList = [];
|
||||
// Table 勾选数据
|
||||
const selectionChange = (rowKeys) => {
|
||||
batchList = rowKeys;
|
||||
};
|
||||
/**
|
||||
* 分页改变
|
||||
* @param {Number} [page]
|
||||
*/
|
||||
const handlePageChange = (page) => {
|
||||
pager.pageIndex = page;
|
||||
// 修改当前页码
|
||||
currentPage.value = page;
|
||||
getDbTables({ ...pager, ...queryForm });
|
||||
};
|
||||
|
||||
// 每页数据量
|
||||
const handlePageSizeChange = (pageSize) => {
|
||||
pager.pageSize = pageSize;
|
||||
getDbTables({ ...pager, ...queryForm });
|
||||
};
|
||||
|
||||
// 初始页数
|
||||
const currentPage = ref(1);
|
||||
// Pager
|
||||
const pager = {
|
||||
count: 0,
|
||||
pageIndex: 1,
|
||||
pageSize: 10,
|
||||
};
|
||||
|
||||
// 获取列表数据
|
||||
const getDbTables = async (params = {}) => {
|
||||
const { data, code, msg } = await listDbTable(params);
|
||||
if ( code == 200 ) {
|
||||
tableData.value = data.list;
|
||||
Object.assign(pager, { count: data.count, pageIndex: data.pageIndex, pageSize: data.pageSize });
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getDbTables(pager);
|
||||
});
|
||||
</script>
|
||||
270
src/views/dev-tools/gen/index.vue
Normal file
270
src/views/dev-tools/gen/index.vue
Normal file
@ -0,0 +1,270 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<a-form :model="queryForm" ref="queryFormRef" layout="inline">
|
||||
<a-form-item field="tableName" label="表名称">
|
||||
<a-input v-model="queryForm.tableName" placeholder="请输入表名称" @press-enter="handleQuery" />
|
||||
</a-form-item>
|
||||
<a-form-item field="tableComment" label="表描述">
|
||||
<a-input v-model="queryForm.tableComment" placeholder="请输入描述" @press-enter="handleQuery" />
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleQuery"><icon-search /> 搜索</a-button>
|
||||
<a-button @click="handleResetQuery"><icon-loop /> 重置</a-button>
|
||||
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<a-divider />
|
||||
|
||||
<!-- action -->
|
||||
<div class="action">
|
||||
<a-space>
|
||||
<a-button type="primary" @click="openImportTable"><icon-plus /> 导入 </a-button>
|
||||
<a-button type="primary" status="danger" @click="() => { deleteVisible = true; }"><icon-delete /> 批量删除</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
|
||||
<!-- table -->
|
||||
<a-table
|
||||
:data="tableData"
|
||||
:row-selection="{ type: 'checkbox', showCheckedAll: true }"
|
||||
row-key="tableId"
|
||||
@selection-change="(selection) => {deleteData = selection;}"
|
||||
@page-change="handlePageChange"
|
||||
@page-size-change="handlePageSizeChange"
|
||||
>
|
||||
<template #columns>
|
||||
<a-table-column title="序号" data-index="tableId" :width="180" ellipsis tooltip/>
|
||||
<a-table-column title="表名称" data-index="tableName" :width="180" ellipsis tooltip/>
|
||||
<a-table-column title="表描述" data-index="tableComment" :width="180" ellipsis tooltip/>
|
||||
<a-table-column title="模型名称" data-index="className" :width="180" ellipsis tooltip/>
|
||||
<a-table-column title="动作" :width="370" align="center" >
|
||||
<template #cell="{ record }">
|
||||
<a-button-group>
|
||||
<a-button size="small" @click="handleEditTable(record)">编辑</a-button>
|
||||
<a-button size="small" @click="handlePreview(record)">预览</a-button>
|
||||
<a-popconfirm content="正在使用代码生成请确认?" okText="生成" type="warning" @ok="handleToProject(record)">
|
||||
<a-button size="small">代码生成 </a-button>
|
||||
</a-popconfirm>
|
||||
<a-popconfirm content="正在使用【菜单以及API生成到数据库】请确认?" okText="写入DB" type="warning" @ok="handleToDB(record)">
|
||||
<a-button size="small">生成配置 </a-button>
|
||||
</a-popconfirm>
|
||||
<a-popconfirm content="正在使用代码生成配置迁移脚本请确认?" okText="迁移" type="warning" @ok="handleToApiFile(record)">
|
||||
<a-button size="small">生成迁移脚本 </a-button>
|
||||
</a-popconfirm>
|
||||
<a-button size="small" @click="() => { deleteVisible = true; deleteData = [record.jobId]; }">删除</a-button>
|
||||
</a-button-group>
|
||||
</template>
|
||||
</a-table-column>
|
||||
</template>
|
||||
</a-table>
|
||||
|
||||
<!-- 导入对话框 -->
|
||||
<import-table v-model:visible="modalVisible" />
|
||||
|
||||
<!-- 预览代码对话框 -->
|
||||
<a-modal v-model:visible="codeVisible" title="预览代码" width="80%">
|
||||
<a-space style="margin-bottom: 16px;">
|
||||
<a-tag v-for="(value, key) of preview.data" :key="key" bordered color="#165dff" @click="codeChange(key)">{{ key.substring(key.lastIndexOf('/')+1,key.indexOf('.go.template')) }}</a-tag>
|
||||
</a-space>
|
||||
<!-- 代码编辑器 -->
|
||||
<codemirror v-model="codestr" :style="{ height: '600px' }" :autofocus="true" :indent-with-tab="true" :tab-size="2" :extensions="extensions" />
|
||||
</a-modal>
|
||||
|
||||
<!-- Akiraka 20230223 删除与批量删除 开始 -->
|
||||
<DeleteModal
|
||||
:data="deleteData"
|
||||
:visible="deleteVisible"
|
||||
:apiDelete="delTable"
|
||||
@deleteVisibleChange="() => deleteVisible = false"
|
||||
/>
|
||||
<!-- Akiraka 20230223 删除与批量删除 结束 -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref, getCurrentInstance, onMounted, nextTick, watch } from 'vue';
|
||||
import importTable from './importTable.vue'
|
||||
import { listTable,previewTable,delTable,apiToFile,toProjectTableCheckRole,toDBTable } from '@/api/tools/gen';
|
||||
import { parseTime } from '@/utils/parseTime';
|
||||
import { Codemirror } from 'vue-codemirror'
|
||||
import { javascript } from '@codemirror/lang-javascript'
|
||||
import { oneDark } from '@codemirror/theme-one-dark'
|
||||
|
||||
// Akiraka 20230210 删除数据
|
||||
const deleteData = ref([])
|
||||
// Akiraka 20230210 删除对话框
|
||||
const deleteVisible = ref(false)
|
||||
// Akiraka 20230210 监听删除事件
|
||||
watch(() => deleteVisible.value ,(value) => {
|
||||
if ( value == false ) {
|
||||
getSysConfigInfo(pager);
|
||||
}
|
||||
})
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const currentPage = ref(1);
|
||||
// Pager
|
||||
const pager = {
|
||||
count: 0,
|
||||
pageIndex: 1,
|
||||
pageSize: 10,
|
||||
};
|
||||
// form
|
||||
const queryForm = reactive({});
|
||||
const modalForm = reactive({
|
||||
sort: 0,
|
||||
status: 2,
|
||||
});
|
||||
|
||||
const modalVisible = ref(false);
|
||||
const openImportTable = () => {
|
||||
modalVisible.value = true;
|
||||
};
|
||||
// Batch Del List
|
||||
let batchList = [];
|
||||
|
||||
// Table Data
|
||||
const tableData = ref([]);
|
||||
|
||||
// 修改
|
||||
const handleUpdate = async (record) => {
|
||||
modalVisible.value = true;
|
||||
modalTitle.value = '修改岗位';
|
||||
await nextTick();
|
||||
Object.assign(modalForm, record);
|
||||
};
|
||||
|
||||
// Table 勾选数据
|
||||
const selectionChange = (rowKeys) => {
|
||||
batchList = rowKeys;
|
||||
};
|
||||
|
||||
/**
|
||||
* 分页改变
|
||||
* @param {Number} [page]
|
||||
*/
|
||||
const handlePageChange = (page) => {
|
||||
pager.pageIndex = page;
|
||||
// 修改当前页码
|
||||
currentPage.value = page;
|
||||
getListTable({ ...pager, ...queryForm });
|
||||
};
|
||||
|
||||
// 每页数据量
|
||||
const handlePageSizeChange = (pageSize) => {
|
||||
pager.pageSize = pageSize;
|
||||
getListTable({ ...pager, ...queryForm });
|
||||
};
|
||||
|
||||
// 获取列表数据
|
||||
const getListTable = async (params = {}) => {
|
||||
const { data, code, msg } = await listTable(params);
|
||||
if ( code == 200 ) {
|
||||
tableData.value = data.list;
|
||||
Object.assign(pager, { count: data.count, pageIndex: data.pageIndex, pageSize: data.pageSize });
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
};
|
||||
|
||||
// 查询列表信息
|
||||
const handleQuery = async () => {
|
||||
const params = {
|
||||
pageIndex: pager.pageIndex,
|
||||
pageSize: pager.pageSize,
|
||||
...queryForm,
|
||||
};
|
||||
|
||||
getListTable(params);
|
||||
};
|
||||
|
||||
// 重置搜索
|
||||
const handleResetQuery = () => {
|
||||
proxy.$refs.queryFormRef.resetFields();
|
||||
getListTable(queryForm);
|
||||
}
|
||||
|
||||
// 按钮块
|
||||
// 编辑
|
||||
const handleEditTable = (row) => {
|
||||
const tableId = row.tableId || this.ids[0]
|
||||
proxy.$router.push({ path: '/dev-tools/editTable', query: { tableId: tableId }})
|
||||
}
|
||||
// 预览代码参数
|
||||
// 预览代码对话框
|
||||
const codeVisible = ref(false);
|
||||
// 预览参数
|
||||
const preview = ref({
|
||||
open: false,
|
||||
title: '代码预览',
|
||||
data: {},
|
||||
activeName: 'api.go'
|
||||
})
|
||||
/** 预览按钮 */
|
||||
const handlePreview = (row) => {
|
||||
codeVisible.value = true;
|
||||
previewTable(row.tableId).then(response => {
|
||||
console.log("sadasdasdasd",response)
|
||||
preview.value.data = response.data
|
||||
preview.value.open = true
|
||||
codeChange('template/api.go.template')
|
||||
})
|
||||
}
|
||||
// 代码编辑器参数
|
||||
const cmOptions = ref({
|
||||
tabSize: 4,
|
||||
theme: 'material-palenight',
|
||||
mode: 'text/javascript',
|
||||
lineNumbers: true,
|
||||
line: true
|
||||
})
|
||||
const extensions = [javascript(), oneDark];
|
||||
const codestr = ref('')
|
||||
// 预览代码 代码更改
|
||||
const codeChange = (e) => {
|
||||
if (e.indexOf('js') > -1) {
|
||||
|
||||
cmOptions.value.mode = 'text/javascript'
|
||||
}
|
||||
if (e.indexOf('model') > -1 || e.indexOf('router') > -1 || e.indexOf('api') > -1 || e.indexOf('service') > -1 || e.indexOf('dto') > -1) {
|
||||
cmOptions.value.mode = 'text/x-go'
|
||||
}
|
||||
if (e.indexOf('vue') > -1) {
|
||||
cmOptions.value.mode = 'text/x-vue'
|
||||
}
|
||||
codestr.value = preview.value.data[e]
|
||||
}
|
||||
// 代码生成
|
||||
const handleToProject = (row) => {
|
||||
console.log("dasdasd",row)
|
||||
toProjectTableCheckRole(row.tableId, false).then((response) => {
|
||||
proxy.$message.success(response.msg);
|
||||
}).catch(function() {})
|
||||
}
|
||||
// 生成配置
|
||||
const handleToDB = (row) => {
|
||||
toDBTable(row.tableId).then((response) => {
|
||||
proxy.$message.success(response.msg);
|
||||
}).catch(function() {})
|
||||
}
|
||||
// 生成迁移脚本
|
||||
const handleToApiFile = (row) => {
|
||||
apiToFile(row.tableId, true).then((response) => {
|
||||
proxy.$message.success(response.msg);
|
||||
}).catch(function() {})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getListTable(pager);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.action {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
</style>
|
||||
11
src/views/dev-tools/swagger/index.vue
Normal file
11
src/views/dev-tools/swagger/index.vue
Normal file
@ -0,0 +1,11 @@
|
||||
<template>
|
||||
<iframe class="iframe-swagger" src="http://124.222.72.164:8000/swagger/index.html"></iframe>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.iframe-swagger {
|
||||
width: 100%;
|
||||
height: calc(100vh - 90px);
|
||||
border: none
|
||||
}
|
||||
</style>
|
||||
25
src/views/error-page/403.vue
Normal file
25
src/views/error-page/403.vue
Normal file
@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<a-result
|
||||
status="403"
|
||||
subtitle="对不起,您没有访问该资源的权限"
|
||||
>
|
||||
<template #extra>
|
||||
<a-space>
|
||||
<a-button type="primary">
|
||||
返回
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-result>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.app-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
height: calc(100vh - 122px);
|
||||
}
|
||||
</style>
|
||||
25
src/views/error-page/404.vue
Normal file
25
src/views/error-page/404.vue
Normal file
@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<a-result
|
||||
status="404"
|
||||
subtitle="抱歉,页面走丢了~"
|
||||
>
|
||||
<template #extra>
|
||||
<a-space>
|
||||
<a-button type="primary">
|
||||
返回
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-result>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.app-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
height: calc(100vh - 122px);
|
||||
}
|
||||
</style>
|
||||
25
src/views/error-page/500.vue
Normal file
25
src/views/error-page/500.vue
Normal file
@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<a-result
|
||||
status="500"
|
||||
subtitle="抱歉,服务器出了点问题~"
|
||||
>
|
||||
<template #extra>
|
||||
<a-space>
|
||||
<a-button type="primary">
|
||||
返回
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-result>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.app-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
height: calc(100vh - 122px);
|
||||
}
|
||||
</style>
|
||||
19
src/views/error-page/888.vue
Normal file
19
src/views/error-page/888.vue
Normal file
@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<a-result
|
||||
subtitle="找不到组件地址,请检查菜单: 菜单管理 > 对应菜单 > 组件管理"
|
||||
>
|
||||
<template #extra>
|
||||
</template>
|
||||
</a-result>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.app-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
height: calc(100vh - 122px);
|
||||
}
|
||||
</style>
|
||||
3
src/views/index.vue
Normal file
3
src/views/index.vue
Normal file
@ -0,0 +1,3 @@
|
||||
<template>
|
||||
<router-view></router-view>
|
||||
</template>
|
||||
290
src/views/login/index.vue
Normal file
290
src/views/login/index.vue
Normal file
@ -0,0 +1,290 @@
|
||||
<template>
|
||||
<div class="account">
|
||||
<div class="account-container">
|
||||
<div class="account-wrap-login">
|
||||
<div class="login-pic">
|
||||
<div>
|
||||
<img
|
||||
src="/public/login_left_bg.jpg"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="login-form" style="padding: 3rem !important">
|
||||
<div class="login-form-container">
|
||||
<div class="account-top">
|
||||
<div class="account-top-logo">
|
||||
<img :src="store.sysConfig.sys_app_logo" />
|
||||
<span class="project-title">用户登录</span>
|
||||
</div>
|
||||
<div class="account-top-desc">横看成峰侧成岭 远近高低各不同</div>
|
||||
</div>
|
||||
<!-- 登录表单 -->
|
||||
<a-form
|
||||
:model="loginForm"
|
||||
:rules="loginRules"
|
||||
ref="loginFormRef"
|
||||
layout="vertical"
|
||||
@keyup.enter="handleLogin"
|
||||
>
|
||||
<a-form-item field="userName" hide-asterisk>
|
||||
<a-input
|
||||
v-model="loginForm.userName"
|
||||
placeholder="请输入用户名"
|
||||
>
|
||||
<template #prefix>
|
||||
<icon-user />
|
||||
</template>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
<a-form-item field="passWord" hide-asterisk>
|
||||
<a-input-password
|
||||
v-model="loginForm.passWord"
|
||||
placeholder="请输入密码"
|
||||
>
|
||||
<template #prefix>
|
||||
<icon-lock />
|
||||
</template>
|
||||
</a-input-password>
|
||||
</a-form-item>
|
||||
<div style="display: flex">
|
||||
<div style="width: 65%">
|
||||
<a-form-item field="code" hide-asterisk>
|
||||
<a-input
|
||||
v-model="loginForm.code"
|
||||
placeholder="请输入验证码"
|
||||
>
|
||||
<template #prefix>
|
||||
<icon-safe />
|
||||
</template>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
</div>
|
||||
<div style="width: 5%"></div>
|
||||
<div style="width: 20%">
|
||||
<a-form-item field="code" hide-asterisk>
|
||||
<img
|
||||
:src="captchUrl"
|
||||
class="captcha"
|
||||
@click="loadCaptcha()"
|
||||
/>
|
||||
</a-form-item>
|
||||
</div>
|
||||
</div>
|
||||
<a-space direction="vertical" size="medium">
|
||||
<a-checkbox>记住密码</a-checkbox>
|
||||
<a-button
|
||||
size="large"
|
||||
type="primary"
|
||||
:loading="loading"
|
||||
long
|
||||
@click="handleLogin"
|
||||
>登录</a-button
|
||||
>
|
||||
</a-space>
|
||||
</a-form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, getCurrentInstance } from 'vue';
|
||||
import { IconUser, IconLock, IconSafe } from '@arco-design/web-vue/es/icon';
|
||||
import { login, getCaptcha } from '@/api/admin/login';
|
||||
import { useUserStore } from '@/store/userInfo';
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
const store = useUserStore();
|
||||
// form
|
||||
const loginForm = reactive({});
|
||||
// 验证码
|
||||
const captchUrl = ref(null);
|
||||
// 按钮loading
|
||||
const loading = ref(false);
|
||||
|
||||
// rules
|
||||
const loginRules = {
|
||||
userName: [{ required: true, message: '请输入用户名' }],
|
||||
passWord: [{ required: true, message: '请输入密码' }],
|
||||
code: [{ required: true, message: '请输入验证码' }],
|
||||
};
|
||||
|
||||
// 获取验证码
|
||||
const loadCaptcha = async () => {
|
||||
const res = await getCaptcha();
|
||||
captchUrl.value = res.data;
|
||||
loginForm.uuid = res.id;
|
||||
};
|
||||
|
||||
// 登陆
|
||||
const handleLogin = () => {
|
||||
loading.value = true;
|
||||
// 如果 valid 为空则校验成功
|
||||
proxy.$refs.loginFormRef.validate(async (valid) => {
|
||||
if (!valid) {
|
||||
try {
|
||||
const { code, token, msg } = await login(loginForm);
|
||||
if ( code == 200 ) {
|
||||
await store.setToken(token);
|
||||
proxy.$message.success({
|
||||
content: '登陆成功',
|
||||
duration: 2000,
|
||||
});
|
||||
setTimeout(() => {
|
||||
proxy.$router.push('/admin/sys-api');
|
||||
loading.value = false;
|
||||
}, 500);
|
||||
} else {
|
||||
proxy.$message.error(`登陆失败:${msg}`);
|
||||
}
|
||||
} catch (err) {
|
||||
// 登录失败 重新获取验证码
|
||||
loadCaptcha();
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
await loadCaptcha();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.captcha {
|
||||
width: 100px;
|
||||
height: 32px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
*,
|
||||
:before,
|
||||
:after {
|
||||
box-sizing: border-box;
|
||||
border-width: 0;
|
||||
border-style: solid;
|
||||
border-color: #e5e7eb;
|
||||
}
|
||||
|
||||
// 输入框重写
|
||||
.arco-input-wrapper {
|
||||
border-radius: 20px;
|
||||
height: 40px;
|
||||
border: 1px solid #ddd;
|
||||
background: #fff;
|
||||
}
|
||||
// 输入框 重写框颜色
|
||||
.arco-input-wrapper:hover {
|
||||
background: #fff;
|
||||
border: 1px solid #1e6fff;
|
||||
}
|
||||
|
||||
.account {
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.account-container {
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 15px;
|
||||
background: #9053c7;
|
||||
background: linear-gradient(-135deg, #c850c0, #4158d0);
|
||||
}
|
||||
.account-wrap-login {
|
||||
width: 960px;
|
||||
height: 554px;
|
||||
// background: #fff;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
// justify-content:space-between;
|
||||
// padding:30px 95px 33px
|
||||
}
|
||||
.account-wrap-login .login-pic {
|
||||
background-color: #0259e6 !important;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
width: 50%;
|
||||
}
|
||||
.account-wrap-login .login-pic img {
|
||||
max-width: 100%;
|
||||
}
|
||||
.account-wrap-login .login-form {
|
||||
width: 50%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: #fff;
|
||||
}
|
||||
.account-wrap-login .login-form-container {
|
||||
margin: auto;
|
||||
width: 100%;
|
||||
}
|
||||
.account-wrap-login .login-form-title {
|
||||
padding-bottom: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
@media (max-width: 991px) {
|
||||
.account-wrap-login .login-pic {
|
||||
display: none;
|
||||
}
|
||||
.account-wrap-login .login-form {
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
.account-wrap-login .account-top {
|
||||
text-align: center;
|
||||
}
|
||||
.account-wrap-login .account-top-logo {
|
||||
text-align: center;
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.account-wrap-login .account-top-logo img {
|
||||
width: 45px;
|
||||
}
|
||||
.account-wrap-login .account-top-logo .project-title {
|
||||
background: linear-gradient(
|
||||
92.06deg,
|
||||
#33c2ff -17.9%,
|
||||
#1e6fff 43.39%,
|
||||
#1e6fff 99.4%
|
||||
);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
font-size: 24px;
|
||||
line-height: 1.25;
|
||||
font-weight: 500;
|
||||
margin-left: 10px;
|
||||
}
|
||||
.account-wrap-login .account-top-desc {
|
||||
font-size: 14px;
|
||||
color: #808695;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.account-wrap-login {
|
||||
width: 100%;
|
||||
padding: 30px;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
// 20230105 自动填充背景色问题
|
||||
:deep(.arco-input-wrapper .arco-input.arco-input-size-medium) {
|
||||
box-shadow: 0 0 0px 1000px #fff inset;
|
||||
}
|
||||
</style>
|
||||
1
src/views/profile/api-management.svg
Normal file
1
src/views/profile/api-management.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg width="24" height="24" viewBox="0 0 48 48" fill="currentColor"><path fill-rule="evenodd" clip-rule="evenodd" d="M4 15a1 1 0 001 1h2a1 1 0 001-1V8h7a1 1 0 001-1V5a1 1 0 00-1-1H6a2 2 0 00-2 2v9zm4 18a1 1 0 00-1-1H5a1 1 0 00-1 1v9a2 2 0 002 2h9a1 1 0 001-1v-2a1 1 0 00-1-1H8v-7zm35-17a1 1 0 001-1V6a2 2 0 00-2-2h-9a1 1 0 00-1 1v2a1 1 0 001 1h7v7a1 1 0 001 1h2zm1 17a1 1 0 00-1-1h-2a1 1 0 00-1 1v7h-7a1 1 0 00-1 1v2a1 1 0 001 1h9a2 2 0 002-2v-9zM32.835 11h-6.108c-6.512 0-11.882 4.804-12.636 11h-1.992c-.382 0-.52.046-.66.134a.855.855 0 00-.325.378c-.074.162-.114.324-.114.77v1.436c0 .446.04.608.114.77.075.163.185.291.324.378.14.088.279.134.66.134h2.157c1.179 5.706 6.315 10 12.472 10h6.108c.405 0 .552-.041.7-.12a.819.819 0 00.344-.337c.079-.145.121-.29.121-.688V31h.901c.382 0 .52-.046.66-.134a.855.855 0 00.325-.378c.074-.162.114-.324.114-.77v-1.436c0-.446-.04-.608-.114-.77a.855.855 0 00-.325-.378c-.14-.088-.278-.134-.66-.134H34v-7h.901c.382 0 .52-.046.66-.134a.855.855 0 00.325-.378c.074-.162.114-.324.114-.77v-1.436c0-.446-.04-.608-.114-.77a.855.855 0 00-.325-.378c-.14-.088-.278-.134-.66-.134H34v-3.855c0-.398-.042-.543-.121-.688a.819.819 0 00-.344-.338c-.148-.078-.295-.119-.7-.119zm-2.744 3.571h-3.637c-5.02 0-9.09 3.998-9.09 8.929s4.07 8.929 9.09 8.929h3.637V14.57z" fill="currentColor"/></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
234
src/views/profile/index.vue
Normal file
234
src/views/profile/index.vue
Normal file
@ -0,0 +1,234 @@
|
||||
<template>
|
||||
<a-row :gutter="8" class="dict-wrapper">
|
||||
<a-col :span="8">
|
||||
<a-card hoverable>
|
||||
<div style="text-align: center;">
|
||||
<a-space direction="vertical" fill>
|
||||
<a-avatar :size="120">
|
||||
<template v-if="userInfoForm.user.avatar == '' ">
|
||||
<img alt="avatar" :src="store.sysConfig.sys_user_avatar" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<img alt="avatar" :src="userInfoForm.user.avatar"/>
|
||||
</template>
|
||||
</a-avatar>
|
||||
<a-typography-title :heading="6">{{userInfoForm.user.nickName}}</a-typography-title>
|
||||
</a-space>
|
||||
</div>
|
||||
<a-divider />
|
||||
<a-card :bordered="false">
|
||||
<a-descriptions title="用户详情" :column="1">
|
||||
<a-descriptions-item label="用户昵称">{{userInfoForm.user.nickName}}</a-descriptions-item>
|
||||
<a-descriptions-item label="登录账号">{{userInfoForm.user.username}}</a-descriptions-item>
|
||||
<a-descriptions-item label="用户手机">{{userInfoForm.user.phone}}</a-descriptions-item>
|
||||
<a-descriptions-item label="用户邮箱">{{userInfoForm.user.email}}</a-descriptions-item>
|
||||
<a-descriptions-item label="用户性别">
|
||||
<template v-if="userInfoForm.user.sex == '0' "><a-tag color="arcoblue">男</a-tag></template>
|
||||
<template v-else-if="userInfoForm.user.sex == '1' "><a-tag color="pinkpurple">女</a-tag></template>
|
||||
<template v-else><a-tag color="gold">中性</a-tag></template>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="所属部门" v-for="item of userInfoForm.posts" >
|
||||
<a-tag>{{ item.postName }}</a-tag>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="用户角色" v-for="item of userInfoForm.roles" >
|
||||
<a-tag>{{ item.roleName }}</a-tag>
|
||||
</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
</a-card>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :span="16">
|
||||
<a-card hoverable>
|
||||
<a-tabs>
|
||||
<a-tab-pane key="1">
|
||||
<template #title>
|
||||
<icon-exclamation-circle /> 基础信息
|
||||
</template>
|
||||
<a-card hoverable :bordered="false">
|
||||
<a-form :model="modalForm" :rules="rules" ref="modalFormRef" label-align="left" auto-label-width>
|
||||
<a-form-item field="nickName" label="用户昵称">
|
||||
<a-input v-model="modalForm.nickName" placeholder="请输入用户昵称" />
|
||||
</a-form-item>
|
||||
<a-form-item field="phone" label="手机号码">
|
||||
<a-input v-model="modalForm.phone" placeholder="请输入手机号码" />
|
||||
</a-form-item>
|
||||
<a-form-item field="email" label="用户邮箱">
|
||||
<a-input v-model="modalForm.email" placeholder="请输入邮箱" />
|
||||
</a-form-item>
|
||||
<a-form-item field="sex" label="用户性别">
|
||||
<a-radio-group v-model="modalForm.sex">
|
||||
<a-radio value="0">男</a-radio>
|
||||
<a-radio value="1">女</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-space>
|
||||
<a-button @click="handleSubmit" type="primary">保存</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-card>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="2">
|
||||
<template #title>
|
||||
<icon-user /> 修改密码
|
||||
</template>
|
||||
<a-card hoverable :bordered="false">
|
||||
<a-form :model="userPwdForm" :rules="rules" ref="userPwdFormRef" label-align="left" auto-label-width>
|
||||
<a-form-item field="oldPassword" label="旧密码">
|
||||
<a-input v-model="userPwdForm.oldPassword" placeholder="请输入旧密码" />
|
||||
</a-form-item>
|
||||
<a-form-item field="newPassword" label="新密码">
|
||||
<a-input v-model="userPwdForm.newPassword" placeholder="请输入新密码" />
|
||||
</a-form-item>
|
||||
<a-form-item field="confirmPassword" label="确认密码">
|
||||
<a-input v-model="userPwdForm.confirmPassword" placeholder="请输入确认密码" />
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-space>
|
||||
<a-button @click="handleSubmitUserPwd" type="primary">保存</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-card>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref, getCurrentInstance } from 'vue';
|
||||
import { useUserStore } from '@/store/userInfo';
|
||||
import { updateUser } from '@/api/admin/sys-user';
|
||||
import { getUserProfile,putUserPwd } from '@/api/profile/profile';
|
||||
|
||||
// use Store
|
||||
const store = useUserStore();
|
||||
|
||||
// 获取当前实例
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const userInfoForm = ref({
|
||||
user: {},
|
||||
posts: [],
|
||||
roles: []
|
||||
});
|
||||
|
||||
const userPwdForm = reactive({});
|
||||
|
||||
// 默认表单
|
||||
const modalForm = reactive({});
|
||||
|
||||
// Rules
|
||||
const rules = {
|
||||
nickName: [{ required: true, message: '请输入用户昵称' }],
|
||||
phone: [
|
||||
{ required: true, message: '请输入联系手机' },
|
||||
// { match: /^1[3456789]\d{9}$/, message: '校验规则: 13011112222'}
|
||||
],
|
||||
email: [
|
||||
{ required: true, message: '请输入邮箱' },
|
||||
// { match: /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(\.[a-zA-Z0-9_-])+/, message: '校验规则: aka@aka.com'}
|
||||
],
|
||||
sex: [
|
||||
{ required: true, message: '请选择性别' },
|
||||
// { match: /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(\.[a-zA-Z0-9_-])+/, message: '校验规则: aka@aka.com'}
|
||||
],
|
||||
oldPassword: [
|
||||
{ required: true, message: '输入旧密码' },
|
||||
// { match: /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(\.[a-zA-Z0-9_-])+/, message: '校验规则: aka@aka.com'}
|
||||
],
|
||||
newPassword: [
|
||||
{ required: true, message: '输入新密码' },
|
||||
// { match: /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(\.[a-zA-Z0-9_-])+/, message: '校验规则: aka@aka.com'}
|
||||
],
|
||||
confirmPassword: [
|
||||
{ required: true, message: '确认新密码', validator: (value, cb) => {
|
||||
return new Promise(resolve => {
|
||||
window.setTimeout(() => {
|
||||
if (userPwdForm.newPassword !== value) {
|
||||
cb('两次输入的密码不一致')
|
||||
}
|
||||
resolve()
|
||||
}, 1)
|
||||
})
|
||||
} }
|
||||
// { match: /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(\.[a-zA-Z0-9_-])+/, message: '校验规则: aka@aka.com'}
|
||||
],
|
||||
};
|
||||
|
||||
const equalToPassword = (value, callback) => {
|
||||
if (modalForm.newPassword !== value) {
|
||||
cb('两次输入的密码不一致')
|
||||
console.log("aaa")
|
||||
} else {
|
||||
cb('密码输入正确')
|
||||
console.log("bbb")
|
||||
}
|
||||
}
|
||||
|
||||
// handleSubmit 新增与修改按钮方法 20220713
|
||||
const handleSubmit = () => {
|
||||
proxy.$refs.modalFormRef.validate(async (valid) => {
|
||||
if (!valid) {
|
||||
const { success } = await updateUser(modalForm);
|
||||
if (success) proxy.$message.success('信息修改成功');
|
||||
getCurrentUserInfo();
|
||||
} else {
|
||||
proxy.$message.error('表单校验失败');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 修改用户密码 20221025
|
||||
const handleSubmitUserPwd = () => {
|
||||
proxy.$refs.userPwdFormRef.validate(async (valid) => {
|
||||
if (!valid) {
|
||||
const { res } = await putUserPwd(userPwdForm);
|
||||
console.loh("success",res)
|
||||
if (success) proxy.$message.success('密码修改成功');
|
||||
getCurrentUserInfo();
|
||||
} else {
|
||||
proxy.$message.error('表单校验失败');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const getCurrentUserInfo = async () => {
|
||||
const res = await getUserProfile()
|
||||
userInfoForm.value = res.data
|
||||
Object.assign(modalForm, res.data.user);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getCurrentUserInfo();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.userinfo-row {
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
.userinfo-item {
|
||||
&-label {
|
||||
width: 140px;
|
||||
color: #4e5969;
|
||||
text-align: right;
|
||||
}
|
||||
&-value {
|
||||
width: 140px;
|
||||
color: $primary-font-color;
|
||||
}
|
||||
div {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.userinfo-form {
|
||||
width: 520px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
</style>
|
||||
277
src/views/schedule/index.vue
Normal file
277
src/views/schedule/index.vue
Normal file
@ -0,0 +1,277 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<a-form :model="queryForm" ref="queryFormRef" layout="inline">
|
||||
<a-form-item field="jobName" label="任务名称">
|
||||
<a-input v-model="queryForm.jobName" placeholder="请输入任务名称" @press-enter="handleQuery" />
|
||||
</a-form-item>
|
||||
<a-form-item field="jobGroup" label="任务分组">
|
||||
<a-select v-model="queryForm.jobGroup" placeholder="请选择任务分组">
|
||||
<a-option value="DEFAULT">默认</a-option>
|
||||
<a-option value="SYSTEM">系统</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item field="status" label="状态">
|
||||
<a-select v-model="queryForm.status" placeholder="请选择任务状态">
|
||||
<a-option :value="2">正常</a-option>
|
||||
<a-option :value="1">关闭</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleQuery">查询</a-button>
|
||||
<a-button @click="handleResetQuery">重置</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<a-divider />
|
||||
|
||||
<div class="table-action">
|
||||
<a-button type="primary" @click="handleAdd">新增定时任务</a-button>
|
||||
</div>
|
||||
|
||||
<a-table :data="tableData" :columns="columns" :pagination="{ 'show-total': true, 'show-jumper': true, 'show-page-size': true, total: pager.count, current: currentPage }">
|
||||
<template #status="{ record }">
|
||||
<a-tag v-if="record.status == 2" color="green">正常</a-tag>
|
||||
<a-tag v-if="record.status == 1" color="red">停用</a-tag>
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<a-button type="text" @click="handleUpdate(record)">修改</a-button>
|
||||
<a-button type="text" status="success" v-if="record.entry_id == 0" @click="handleStart(record.jobId)">启动</a-button>
|
||||
<a-button type="text" status="danger" v-if="record.entry_id !== 0" @click="handleStop(record.jobId)">停止</a-button>
|
||||
<a-button type="text" status="danger" @click="() => { deleteVisible = true; deleteData = [record.jobId]; }">删除</a-button>
|
||||
</template>
|
||||
</a-table>
|
||||
|
||||
<a-modal
|
||||
v-model:visible="modalVisible"
|
||||
title-align="start"
|
||||
:title="modalTitle"
|
||||
:on-before-ok="onBeforeOk"
|
||||
@ok="handleOk"
|
||||
@cancel="$refs.modalFormRef.resetFields()"
|
||||
>
|
||||
<a-form
|
||||
:model="modalForm"
|
||||
:rules="rules"
|
||||
ref="modalFormRef"
|
||||
auto-label-width
|
||||
>
|
||||
<a-form-item field="jobName" label="任务名称">
|
||||
<a-input
|
||||
v-model="modalForm.jobName"
|
||||
placeholder="请输入任务名称"
|
||||
></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item field="jobGroup" label="任务分组">
|
||||
<a-select v-model="modalForm.jobGroup" placeholder="请选择任务分组">
|
||||
<a-option value="DEFAULT">默认</a-option>
|
||||
<a-option value="SYSTEM">系统</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item field="invokeTarget" label="调用目标">
|
||||
<a-input
|
||||
v-model="modalForm.invokeTarget"
|
||||
placeholder="调用目标"
|
||||
></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item field="args" label="目标参数">
|
||||
<a-input v-model="modalForm.args" placeholder="目标参iuu数"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item field="cronExpression" label="Cron表达式">
|
||||
<a-input
|
||||
v-model="modalForm.cronExpression"
|
||||
placeholder="Cron表达式"
|
||||
></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item field="concurrent" label="是否并发">
|
||||
<a-radio-group v-model="modalForm.concurrent" type="button">
|
||||
<a-radio :value="0">允许</a-radio>
|
||||
<a-radio :value="1">禁止</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item field="jobType" label="调用类型">
|
||||
<a-radio-group v-model="modalForm.jobType" type="button">
|
||||
<a-radio :value="1">接口</a-radio>
|
||||
<a-radio :value="2">函数</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item field="misfirePolicy" label="执行策略">
|
||||
<a-radio-group v-model="modalForm.misfirePolicy" type="button">
|
||||
<a-radio :value="1">立即执行</a-radio>
|
||||
<a-radio :value="2">执行一次</a-radio>
|
||||
<a-radio :value="3">放弃执行</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item field="status" label="状态">
|
||||
<a-select v-model="modalForm.status">
|
||||
<a-option :value="2">正常</a-option>
|
||||
<a-option :value="1">停用</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
|
||||
<!-- Akiraka 20230223 删除与批量删除 开始 -->
|
||||
<DeleteModal
|
||||
:data="deleteData"
|
||||
:visible="deleteVisible"
|
||||
:apiDelete="delSysJob"
|
||||
@deleteVisibleChange="() => deleteVisible = false"
|
||||
/>
|
||||
<!-- Akiraka 20230223 删除与批量删除 结束 -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref, onMounted, getCurrentInstance, watch } from 'vue';
|
||||
import { listSysJob, addSysJob, updateSysJob, delSysJob, startJob, removeJob } from '@/api/sys-job';
|
||||
|
||||
// Akiraka 20230210 删除数据
|
||||
const deleteData = ref([])
|
||||
// Akiraka 20230210 删除对话框
|
||||
const deleteVisible = ref(false)
|
||||
// Akiraka 20230210 监听删除事件
|
||||
watch(() => deleteVisible.value ,(value) => {
|
||||
if ( value == false ) {
|
||||
getSysJobListInfo(queryForm);
|
||||
}
|
||||
})
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const currentPage = ref(1);
|
||||
// Pager
|
||||
const pager = {
|
||||
count: 0,
|
||||
pageIndex: 1,
|
||||
pageSize: 10,
|
||||
};
|
||||
|
||||
const queryForm = reactive({});
|
||||
const modalForm = reactive({
|
||||
concurrent: 1,
|
||||
jobType: 1,
|
||||
misfirePolicy: 1,
|
||||
status: 2,
|
||||
});
|
||||
|
||||
// 表单检验规则
|
||||
const rules = {
|
||||
jobName: [{ required: true, message: '请输入任务名称' }],
|
||||
jobGroup: [{ required: true, message: '请选择任务分组' }],
|
||||
invokeTarget: [{ required: true, message: '请输入调用目标' }],
|
||||
cronExpression: [{ required: true, message: '请输入Cron表达式' }],
|
||||
status: [{ required: true, message: '请选择状态' }],
|
||||
};
|
||||
|
||||
const modalVisible = ref(false);
|
||||
const modalTitle = ref('默认标题');
|
||||
|
||||
const tableData = ref([]);
|
||||
const columns = [
|
||||
{ title: '编号', dataIndex: 'jobId' },
|
||||
{ title: '任务名称', dataIndex: 'jobName' },
|
||||
{ title: '任务分组', dataIndex: 'jobGroup' },
|
||||
{ title: '任务表达式', dataIndex: 'cronExpression' },
|
||||
{ title: '调用目标', dataIndex: 'invokeTarget' },
|
||||
{ title: '状态', dataIndex: 'status', slotName: 'status' },
|
||||
{ title: '操作', slotName: 'action' },
|
||||
];
|
||||
|
||||
// 查询任务
|
||||
const handleQuery = () => {
|
||||
getSysJobListInfo(queryForm);
|
||||
}
|
||||
|
||||
// 重置查询
|
||||
const handleResetQuery = () => {
|
||||
proxy.$refs.queryFormRef.resetFields();
|
||||
|
||||
getSysJobListInfo();
|
||||
}
|
||||
|
||||
// 新增任务
|
||||
const handleAdd = () => {
|
||||
modalVisible.value = true;
|
||||
modalTitle.value = '新增任务';
|
||||
};
|
||||
|
||||
// 修改任务
|
||||
const handleUpdate = (record) => {
|
||||
modalVisible.value = true;
|
||||
modalTitle.value = '修改任务';
|
||||
|
||||
Object.assign(modalForm, record);
|
||||
};
|
||||
|
||||
// 启动定时任务
|
||||
const handleStart = async (jobId) => {
|
||||
await startJob(jobId);
|
||||
proxy.$notification.success('启动任务成功!')
|
||||
|
||||
getSysJobListInfo();
|
||||
}
|
||||
|
||||
// 关闭定时任务
|
||||
const handleStop = async (jobId) => {
|
||||
await removeJob(jobId);
|
||||
proxy.$notification.success('已停止任务!')
|
||||
|
||||
getSysJobListInfo();
|
||||
}
|
||||
|
||||
// Modal 触发oK事件前
|
||||
const onBeforeOk = (done) => {
|
||||
proxy.$refs.modalFormRef.validate((err) => {
|
||||
if (!err) {
|
||||
return done();
|
||||
} else {
|
||||
proxy.$message.error('表单校验失败');
|
||||
return done(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Modal 触发ok事件
|
||||
const handleOk = async () => {
|
||||
if (modalForm.jobId) {
|
||||
const { code, msg } = await updateSysJob(modalForm);
|
||||
if (code == 200 ) {
|
||||
proxy.$notification.success('修改成功');
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
} else {
|
||||
const { code, msg } = await addSysJob(modalForm);
|
||||
if (code == 200 ) {
|
||||
proxy.$notification.success('新增成功');
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
}
|
||||
getSysJobListInfo();
|
||||
};
|
||||
|
||||
// 获取系统任务信息
|
||||
const getSysJobListInfo = async (params = {}) => {
|
||||
const { data, code, msg } = await listSysJob(params);
|
||||
if ( code == 200 ) {
|
||||
tableData.value = data.list;
|
||||
Object.assign(pager, { count: data.count, pageIndex: data.pageIndex, pageSize: data.pageSize });
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
getSysJobListInfo();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.table-action {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
</style>
|
||||
160
src/views/sys-tools/monitor/index.vue
Normal file
160
src/views/sys-tools/monitor/index.vue
Normal file
@ -0,0 +1,160 @@
|
||||
<template>
|
||||
<div class="card-wrapper">
|
||||
<a-row :gutter="12">
|
||||
<a-col :span="12">
|
||||
<a-card title="服务器信息">
|
||||
<div class="card-item card-item-server">
|
||||
<span class="card-item-title">主机名称</span>
|
||||
<span class="card-item-desc">{{ SystemInfo?.os?.hostName}}</span>
|
||||
</div>
|
||||
<div class="card-item card-item-server">
|
||||
<span class="card-item-title">操作系统</span>
|
||||
<span class="card-item-desc">{{ SystemInfo?.os?.goOs }}</span>
|
||||
</div>
|
||||
<div class="card-item card-item-server">
|
||||
<span class="card-item-title">服务器IP</span>
|
||||
<span class="card-item-desc">{{ SystemInfo?.os?.ip }}</span>
|
||||
</div>
|
||||
<div class="card-item card-item-server">
|
||||
<span class="card-item-title">系统架构</span>
|
||||
<span class="card-item-desc">{{ SystemInfo?.os?.arch }}</span>
|
||||
</div>
|
||||
<div class="card-item card-item-server">
|
||||
<span class="card-item-title">CPU</span>
|
||||
<span class="card-item-desc">{{ SystemInfo?.cpu?.cpuInfo[0]?.modelName }}</span>
|
||||
</div>
|
||||
<div class="card-item card-item-server">
|
||||
<span class="card-item-title">当前时间</span>
|
||||
<span class="card-item-desc">{{ SystemInfo?.os?.time }}</span>
|
||||
</div>
|
||||
</a-card>
|
||||
</a-col>
|
||||
|
||||
<a-col :span="12">
|
||||
<a-card :title="SystemInfo.location">
|
||||
<a-row :gutter="12">
|
||||
<a-col :span="12">
|
||||
<div class="card-item">
|
||||
<span class="card-item-title">系统</span>
|
||||
<span class="card-item-desc">Linux</span>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<div class="card-item">
|
||||
<span class="card-item-title">时间</span>
|
||||
<span class="card-item-desc">2022-05-06 15:32:22</span>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="12">
|
||||
<a-col :span="12">
|
||||
<div class="card-item">
|
||||
<span class="card-item-title">内存</span>
|
||||
<span class="card-item-desc">129MB/2349MB</span>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<div class="card-item">
|
||||
<span class="card-item-title">在线时间</span>
|
||||
<span class="card-item-desc">{{ SystemInfo.bootTime }}分钟</span>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="12">
|
||||
<a-col :span="12">
|
||||
<div class="card-item">
|
||||
<span class="card-item-title">交换</span>
|
||||
<span class="card-item-desc">0/0</span>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<div class="card-item">
|
||||
<span class="card-item-title">硬盘</span>
|
||||
<span class="card-item-desc">{{ SystemInfo?.disk?.used }}GB/{{ SystemInfo?.disk?.total }}GB</span>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="12">
|
||||
<a-col :span="12">
|
||||
<div class="card-item">
|
||||
<span class="card-item-title">下载</span>
|
||||
<span class="card-item-desc">{{ SystemInfo?.net?.in }}KB</span>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<div class="card-item">
|
||||
<span class="card-item-title">上传</span>
|
||||
<span class="card-item-desc">{{ SystemInfo?.net?.out }}KB</span>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<!-- progress -->
|
||||
<div class="progress-wrapper">
|
||||
<span class="progress-title">CPU</span>
|
||||
<a-progress status="success" :percent="SystemInfo?.cpu?.percent / 100" />
|
||||
</div>
|
||||
<div class="progress-wrapper">
|
||||
<span class="progress-title">RAM</span>
|
||||
<a-progress status="warning" :percent="SystemInfo?.mem?.percent / 100" />
|
||||
</div>
|
||||
<div class="progress-wrapper">
|
||||
<span class="progress-title">硬盘</span>
|
||||
<a-progress :percent="SystemInfo?.disk?.percent / 100" />
|
||||
</div>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, reactive } from 'vue';
|
||||
import { getServerMonitor } from '@/api/sys-tools/monitor';
|
||||
|
||||
|
||||
const SystemInfo = reactive({});
|
||||
|
||||
const getServerMonitorInfo = async () => {
|
||||
const res = await getServerMonitor();
|
||||
|
||||
Object.assign(SystemInfo, res)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getServerMonitorInfo();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card-wrapper {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.card-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 15px 0;
|
||||
border-bottom: 1px solid rgb(229, 230, 235);
|
||||
&-title {
|
||||
color: $primary-font-color;
|
||||
}
|
||||
&-desc {
|
||||
color: $secondary-font-color;
|
||||
}
|
||||
}
|
||||
|
||||
.card-item-server:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.progress-wrapper {
|
||||
margin-top: 15px;
|
||||
display: flex;
|
||||
.progress-title {
|
||||
font-weight: bold;
|
||||
margin-right: 10px;
|
||||
width: 50px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
51
vite.config.js
Normal file
51
vite.config.js
Normal file
@ -0,0 +1,51 @@
|
||||
import { join } from 'path'
|
||||
import { defineConfig } from 'vite';
|
||||
import { viteMockServe } from 'vite-plugin-mock';
|
||||
import vue from '@vitejs/plugin-vue';
|
||||
import svgLoader from 'vite-svg-loader';
|
||||
import { VuetifyResolver } from 'unplugin-vue-components/resolvers';
|
||||
import Components from 'unplugin-vue-components/vite';
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
viteMockServe({
|
||||
mockPath: '/mock',
|
||||
}),
|
||||
svgLoader(),
|
||||
viteMockServe({
|
||||
mockPath: '/mock',
|
||||
}),
|
||||
Components({
|
||||
resolvers: [VuetifyResolver()],
|
||||
include: [/\.vue$/, /\.vue\?vue/, /\.md$/],
|
||||
}),
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': join(__dirname, 'src'),
|
||||
}
|
||||
},
|
||||
server: {
|
||||
host: true,
|
||||
port: 1798,
|
||||
//secure: false,
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'https://vue3.go-admin.dev',
|
||||
changeOrigin: true, //开启跨域
|
||||
rewrite: (path) => path.replace(/^\/api/, '')
|
||||
}
|
||||
}
|
||||
},
|
||||
// 引入全局scss变量
|
||||
css: {
|
||||
preprocessorOptions: {
|
||||
scss: {
|
||||
additionalData: `@import "@/style/variables.scss";`
|
||||
}
|
||||
}
|
||||
},
|
||||
publicDir: '/public'
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user