mirror of
https://github.com/cool-team-official/cool-admin-vue.git
synced 2026-03-18 03:33:26 +00:00
添加vue3
This commit is contained in:
parent
1a63d788f5
commit
3fd0114971
@ -1,2 +1,3 @@
|
|||||||
> 1%
|
> 1%
|
||||||
last 2 versions
|
last 2 versions
|
||||||
|
not dead
|
||||||
|
|||||||
@ -1,21 +0,0 @@
|
|||||||
.DS_Store
|
|
||||||
node_modules
|
|
||||||
/dist
|
|
||||||
|
|
||||||
# local env files
|
|
||||||
.env.local
|
|
||||||
.env.*.local
|
|
||||||
|
|
||||||
# Log files
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
yarn.lock
|
|
||||||
|
|
||||||
# Editor directories and files
|
|
||||||
.idea
|
|
||||||
*.suo
|
|
||||||
*.ntvs*
|
|
||||||
*.njsproj
|
|
||||||
*.sln
|
|
||||||
*.sw?
|
|
||||||
@ -1,6 +1,3 @@
|
|||||||
/public/
|
|
||||||
/dist/
|
|
||||||
/node_modules/
|
|
||||||
/src/icons/svg/
|
|
||||||
/mock/
|
|
||||||
vue.config.js
|
vue.config.js
|
||||||
|
/src/crud
|
||||||
|
/src/core
|
||||||
19
.eslintrc.js
19
.eslintrc.js
@ -3,12 +3,19 @@ module.exports = {
|
|||||||
env: {
|
env: {
|
||||||
node: true
|
node: true
|
||||||
},
|
},
|
||||||
extends: ["plugin:vue/essential", "@vue/prettier"],
|
extends: [
|
||||||
rules: {
|
"plugin:vue/vue3-essential",
|
||||||
"no-console": "off",
|
"eslint:recommended",
|
||||||
"comma-dangle": [2, "never"]
|
"@vue/typescript/recommended",
|
||||||
},
|
"@vue/prettier",
|
||||||
|
"@vue/prettier/@typescript-eslint"
|
||||||
|
],
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
parser: "@typescript-eslint/parser"
|
ecmaVersion: 2020
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
"no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
|
||||||
|
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",
|
||||||
|
"@typescript-eslint/no-explicit-any": ["off"]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
4
.gitignore
vendored
4
.gitignore
vendored
@ -2,6 +2,7 @@
|
|||||||
node_modules
|
node_modules
|
||||||
/dist
|
/dist
|
||||||
|
|
||||||
|
|
||||||
# local env files
|
# local env files
|
||||||
.env.local
|
.env.local
|
||||||
.env.*.local
|
.env.*.local
|
||||||
@ -10,10 +11,11 @@ node_modules
|
|||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
yarn.lock
|
pnpm-debug.log*
|
||||||
|
|
||||||
# Editor directories and files
|
# Editor directories and files
|
||||||
.idea
|
.idea
|
||||||
|
.vscode
|
||||||
*.suo
|
*.suo
|
||||||
*.ntvs*
|
*.ntvs*
|
||||||
*.njsproj
|
*.njsproj
|
||||||
|
|||||||
14
Dockerfile
14
Dockerfile
@ -1,14 +0,0 @@
|
|||||||
FROM node:lts-alpine
|
|
||||||
WORKDIR /build
|
|
||||||
RUN npm config set sass_binary_site=https://npm.taobao.org/mirrors/node-sass
|
|
||||||
RUN npm set registry https://registry.npm.taobao.org
|
|
||||||
COPY package.json /build/package.json
|
|
||||||
RUN npm install
|
|
||||||
COPY ./ /build
|
|
||||||
RUN npm run build
|
|
||||||
|
|
||||||
FROM nginx
|
|
||||||
RUN mkdir /app
|
|
||||||
COPY --from=0 /build/dist /app
|
|
||||||
COPY --from=0 /build/nginx.conf /etc/nginx/nginx.conf
|
|
||||||
EXPOSE 80
|
|
||||||
21
LICENSE
21
LICENSE
@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2021 cool-team-official
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
292
README.md
292
README.md
@ -1,290 +1,24 @@
|
|||||||
<p align="center">
|
# front-next-vue3
|
||||||
<a href="https://show.cool-admin.com/" target="blank"><img src="https://admin.cool-js.com/logo.png" width="200" alt="cool-admin Logo" /></a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p align="center">cool-admin 一个很酷的后台权限管理系统,开源免费,模块化、插件化、极速开发 CRUD,方便快速构建迭代后台管理系统, 到论坛 进一步了解</p>
|
## Project setup
|
||||||
|
```
|
||||||
<p align="center">
|
yarn install
|
||||||
<a href="https://github.com/cool-team-official/cool-admin-vue/blob/master/LICENSE" target="_blank"><img src="https://img.shields.io/badge/license-MIT-green?style=flat-square" alt="GitHub license" />
|
|
||||||
<a href=""><img src="https://img.shields.io/github/package-json/v/cool-team-official/cool-admin-vue?style=flat-square" alt="GitHub tag"></a>
|
|
||||||
<img src="https://img.shields.io/github/last-commit/cool-team-official/cool-admin-vue?style=flat-square" alt="GitHub tag"></a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
## 演示
|
|
||||||
|
|
||||||
[https://show.cool-admin.com](https://show.cool-admin.com)
|
|
||||||
|
|
||||||
- 账户:admin
|
|
||||||
- 密码:123456
|
|
||||||
|
|
||||||
<img src="https://cool-show.oss-cn-shanghai.aliyuncs.com/admin/home-mini.png" alt="Admin Home"></a>
|
|
||||||
|
|
||||||
## 项目后端
|
|
||||||
|
|
||||||
[https://github.com/cool-team-official/cool-admin-midway](https://github.com/cool-team-official/cool-admin-midway)
|
|
||||||
|
|
||||||
## 微信群
|
|
||||||
|
|
||||||
<img width="260" src="https://cool-show.oss-cn-shanghai.aliyuncs.com/admin/wechat.jpeg" alt="Admin Wechat"></a>
|
|
||||||
|
|
||||||
## 微信公众号
|
|
||||||
|
|
||||||
<img width="260" src="https://cool-show.oss-cn-shanghai.aliyuncs.com/admin/mp.jpg" alt="Admin Wechat"></a>
|
|
||||||
|
|
||||||
## 在线社区
|
|
||||||
|
|
||||||
[https://bbs.cool-js.com/](https://bbs.cool-js.com/)
|
|
||||||
|
|
||||||
## 使用条件
|
|
||||||
|
|
||||||
请确保您的操作系统上安装了 Node.js(> = 8.9.0)、@vue/cli (> 3.0.0)。
|
|
||||||
|
|
||||||
## 安装项目依赖
|
|
||||||
|
|
||||||
推荐使用 `yarn`:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
yarn
|
|
||||||
```
|
```
|
||||||
|
|
||||||
解决 `node-sass` 网络慢的方法:
|
### Compiles and hot-reloads for development
|
||||||
|
|
||||||
```shell
|
|
||||||
yarn config set sass-binary-site http://npm.taobao.org/mirrors/node-sass
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 运行应用程序
|
|
||||||
|
|
||||||
安装过程完成后,运行以下命令启动服务。您可以在浏览器中预览网站 [http://localhost:9000](http://localhost:9000)
|
|
||||||
|
|
||||||
```shell
|
|
||||||
yarn serve
|
yarn serve
|
||||||
```
|
```
|
||||||
|
|
||||||
## 极速 CRUD
|
### Compiles and minifies for production
|
||||||
|
```
|
||||||
1. `vscode` 编辑器下输入关键字 `cl-crud`,会生成对应的模板代码:
|
yarn build
|
||||||
|
|
||||||
```html
|
|
||||||
<template>
|
|
||||||
<cl-crud ref="crud" @load="onLoad">
|
|
||||||
<el-row type="flex" align="middle">
|
|
||||||
<!-- 刷新按钮 -->
|
|
||||||
<cl-refresh-btn />
|
|
||||||
<!-- 新增按钮 -->
|
|
||||||
<cl-add-btn />
|
|
||||||
<!-- 删除按钮 -->
|
|
||||||
<cl-multi-delete-btn />
|
|
||||||
<cl-flex1 />
|
|
||||||
<!-- 关键字搜索 -->
|
|
||||||
<cl-search-key />
|
|
||||||
</el-row>
|
|
||||||
|
|
||||||
<el-row>
|
|
||||||
<!-- 数据表格 -->
|
|
||||||
<cl-table v-bind="table"></cl-table>
|
|
||||||
</el-row>
|
|
||||||
|
|
||||||
<el-row type="flex">
|
|
||||||
<cl-flex1 />
|
|
||||||
<!-- 分页控件 -->
|
|
||||||
<cl-pagination />
|
|
||||||
</el-row>
|
|
||||||
|
|
||||||
<!-- 新增、编辑 -->
|
|
||||||
<cl-upsert ref="upsert" v-bind="upsert"></cl-upsert>
|
|
||||||
</cl-crud>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
// 新增、编辑配置
|
|
||||||
upsert: {
|
|
||||||
items: []
|
|
||||||
},
|
|
||||||
// 表格配置
|
|
||||||
table: {
|
|
||||||
columns: []
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
onLoad({ ctx, app }) {
|
|
||||||
// crud 配置
|
|
||||||
ctx.service().done();
|
|
||||||
// 发送 page 接口请求
|
|
||||||
app.refresh();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
2. 编辑数据表格 `cl-table`:
|
### Lints and fixes files
|
||||||
|
```
|
||||||
```js
|
yarn lint
|
||||||
{
|
|
||||||
table: {
|
|
||||||
// 参数与 el-table-column 一致,并支持许多骚操作
|
|
||||||
columns: [
|
|
||||||
// 多选列
|
|
||||||
{
|
|
||||||
type: "selection",
|
|
||||||
width: 60
|
|
||||||
},
|
|
||||||
// 自定义列
|
|
||||||
{
|
|
||||||
label: "昵称",
|
|
||||||
prop: "name"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "账户",
|
|
||||||
prop: "price",
|
|
||||||
sortable: "custom" // 是否添加排序
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "状态",
|
|
||||||
prop: "status",
|
|
||||||
// 字典匹配,使用 el-tag 展示
|
|
||||||
dict: [
|
|
||||||
{
|
|
||||||
label: "启用",
|
|
||||||
value: 1,
|
|
||||||
type: "primary"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "禁用",
|
|
||||||
value: 0,
|
|
||||||
type: "danger"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "创建时间",
|
|
||||||
prop: "createTime"
|
|
||||||
},
|
|
||||||
// 操作按钮列,默认包含:编辑、删除
|
|
||||||
{
|
|
||||||
type: "op"
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
3. 编辑新增、编辑表单 `cl-upsert`:
|
### Customize configuration
|
||||||
|
See [Configuration Reference](https://cli.vuejs.org/config/).
|
||||||
```js
|
|
||||||
{
|
|
||||||
upsert: {
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
label: "昵称",
|
|
||||||
prop: "name",
|
|
||||||
// 参数与 el-form-item 一致
|
|
||||||
props: {},
|
|
||||||
value: "神仙都没用", // 昵称默认值
|
|
||||||
// 渲染参数,支持 slot, 组件实例,jsx
|
|
||||||
component: {
|
|
||||||
name: "el-input", // 可以是任意已注册的组件名
|
|
||||||
props: {}, // 组件的参数
|
|
||||||
on: {} // 组件的回调事件
|
|
||||||
},
|
|
||||||
// 验证规则,与 el-form 一致
|
|
||||||
rules: {
|
|
||||||
required: true,
|
|
||||||
message: "昵称不呢为空"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "存款",
|
|
||||||
prop: "price",
|
|
||||||
component: {
|
|
||||||
name: "el-input-number",
|
|
||||||
props: {
|
|
||||||
min: 0,
|
|
||||||
max: 10000
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "状态",
|
|
||||||
prop: "status",
|
|
||||||
value: 1,
|
|
||||||
component: {
|
|
||||||
name: "el-radio-group",
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
label: "启用",
|
|
||||||
value: 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "禁用",
|
|
||||||
value: 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
4. 绑定 `service`。在 `src/service/` 下新建文件 `test.js`,并编辑:
|
|
||||||
|
|
||||||
```js
|
|
||||||
// src/service/test.js
|
|
||||||
import { BaseService, Service, Permission } from "cl-admin";
|
|
||||||
|
|
||||||
// 请求接口的路径
|
|
||||||
@Service("test")
|
|
||||||
class Test extends BaseService {
|
|
||||||
// 继承 BaseService 后,拥有 page, list, add, delete, update, info 6个基本接口
|
|
||||||
|
|
||||||
// 自定义其他接口
|
|
||||||
@Permission("product") // 权限装饰器,可选
|
|
||||||
product(id) {
|
|
||||||
// this.request() 参数与 axios 一致
|
|
||||||
return this.request({
|
|
||||||
url: "/product",
|
|
||||||
method: "POST",
|
|
||||||
data: {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Test;
|
|
||||||
```
|
|
||||||
|
|
||||||
在 `src/service/` 下的文件,框架会自动根据 `目录结构` 和 `文件名称` 格式化成对应的 `$service` 对象。你现在可以这么使用它:
|
|
||||||
|
|
||||||
```js
|
|
||||||
this.$service.test.page({ page: 1 });
|
|
||||||
this.$service.test.product(1);
|
|
||||||
```
|
|
||||||
|
|
||||||
`service` 编写好后,我们把它绑定在 `crud` 上:
|
|
||||||
|
|
||||||
```js
|
|
||||||
export default {
|
|
||||||
methods: {
|
|
||||||
onLoad({ ctx, app }) {
|
|
||||||
// 绑定 service,这边指定到 test 即可
|
|
||||||
ctx.service(this.$service.test).done();
|
|
||||||
|
|
||||||
// 发起 page 请求
|
|
||||||
app.refresh({
|
|
||||||
// 请求默认参数
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
5. 效果预览
|
|
||||||
|
|
||||||

|
|
||||||
|
|||||||
@ -1,13 +1,3 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
presets: ["@vue/app"],
|
presets: ["@vue/cli-plugin-babel/preset"]
|
||||||
plugins: [
|
|
||||||
["jsx-v-model"],
|
|
||||||
[
|
|
||||||
"component",
|
|
||||||
{
|
|
||||||
libraryName: "element-ui",
|
|
||||||
styleLibraryName: "theme-chalk"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
};
|
};
|
||||||
|
|||||||
84
nginx.conf
84
nginx.conf
@ -1,84 +0,0 @@
|
|||||||
user nginx;
|
|
||||||
worker_processes 1;
|
|
||||||
error_log /var/log/nginx/error.log warn;
|
|
||||||
pid /var/run/nginx.pid;
|
|
||||||
events {
|
|
||||||
worker_connections 1024;
|
|
||||||
}
|
|
||||||
http {
|
|
||||||
include /etc/nginx/mime.types;
|
|
||||||
default_type application/octet-stream;
|
|
||||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
|
||||||
'$status $body_bytes_sent "$http_referer" '
|
|
||||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
|
||||||
access_log /var/log/nginx/access.log main;
|
|
||||||
sendfile on;
|
|
||||||
keepalive_timeout 65;
|
|
||||||
|
|
||||||
server {
|
|
||||||
listen 80;
|
|
||||||
server_name localhost;
|
|
||||||
location / {
|
|
||||||
root /app;
|
|
||||||
index index.html;
|
|
||||||
try_files $uri $uri/ /index.html;
|
|
||||||
}
|
|
||||||
location /api/
|
|
||||||
{
|
|
||||||
proxy_pass http://midway:7001/;
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header REMOTE-HOST $remote_addr;
|
|
||||||
|
|
||||||
#缓存相关配置
|
|
||||||
#proxy_cache cache_one;
|
|
||||||
#proxy_cache_key $host$request_uri$is_args$args;
|
|
||||||
#proxy_cache_valid 200 304 301 302 1h;
|
|
||||||
|
|
||||||
#持久化连接相关配置
|
|
||||||
proxy_connect_timeout 3000s;
|
|
||||||
proxy_read_timeout 86400s;
|
|
||||||
proxy_send_timeout 3000s;
|
|
||||||
#proxy_http_version 1.1;
|
|
||||||
#proxy_set_header Upgrade $http_upgrade;
|
|
||||||
#proxy_set_header Connection "upgrade";
|
|
||||||
|
|
||||||
add_header X-Cache $upstream_cache_status;
|
|
||||||
|
|
||||||
#expires 12h;
|
|
||||||
}
|
|
||||||
|
|
||||||
location /adminer/
|
|
||||||
{
|
|
||||||
proxy_pass http://adminer:8080/;
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header REMOTE-HOST $remote_addr;
|
|
||||||
|
|
||||||
#缓存相关配置
|
|
||||||
#proxy_cache cache_one;
|
|
||||||
#proxy_cache_key $host$request_uri$is_args$args;
|
|
||||||
#proxy_cache_valid 200 304 301 302 1h;
|
|
||||||
|
|
||||||
#持久化连接相关配置
|
|
||||||
proxy_connect_timeout 3000s;
|
|
||||||
proxy_read_timeout 86400s;
|
|
||||||
proxy_send_timeout 3000s;
|
|
||||||
#proxy_http_version 1.1;
|
|
||||||
#proxy_set_header Upgrade $http_upgrade;
|
|
||||||
#proxy_set_header Connection "upgrade";
|
|
||||||
|
|
||||||
add_header X-Cache $upstream_cache_status;
|
|
||||||
|
|
||||||
#expires 12h;
|
|
||||||
}
|
|
||||||
|
|
||||||
error_page 500 502 503 504 /50x.html;
|
|
||||||
location = /50x.html {
|
|
||||||
root /usr/share/nginx/html;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
65
package.json
65
package.json
@ -1,65 +1,60 @@
|
|||||||
{
|
{
|
||||||
"name": "cool-admin-vue",
|
"name": "front-next-vue3",
|
||||||
"version": "3.2.0",
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"serve": "vue-cli-service serve",
|
"serve": "vue-cli-service serve",
|
||||||
"build": "vue-cli-service build",
|
"build": "vue-cli-service build",
|
||||||
"report": "vue-cli-service build --report",
|
"lint": "vue-cli-service lint"
|
||||||
"lint": "vue-cli-service lint",
|
|
||||||
"inspect": "vue inspect --mode=production > output.js"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/composition-api": "^1.0.0-rc.5",
|
"array.prototype.flat": "^1.2.4",
|
||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
"cl-admin": "^1.5.3",
|
"cl-admin": "^1.5.1",
|
||||||
"cl-admin-crud": "^1.6.8",
|
"clipboard": "^2.0.8",
|
||||||
"cl-admin-theme": "^0.0.5",
|
"clone-deep": "^4.0.1",
|
||||||
"clipboard": "^2.0.7",
|
"codemirror": "^5.60.0",
|
||||||
"codemirror": "^5.59.4",
|
|
||||||
"core-js": "^3.6.5",
|
"core-js": "^3.6.5",
|
||||||
"dayjs": "^1.10.4",
|
|
||||||
"echarts": "^5.0.2",
|
"echarts": "^5.0.2",
|
||||||
"element-ui": "^2.15.1",
|
"element-plus": "1.0.2-beta.35",
|
||||||
"js-beautify": "^1.13.5",
|
"js-beautify": "^1.13.5",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
|
"merge": "^2.1.1",
|
||||||
|
"mitt": "^2.1.0",
|
||||||
"mockjs": "^1.1.0",
|
"mockjs": "^1.1.0",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"qs": "^6.9.1",
|
|
||||||
"quill": "^1.3.7",
|
"quill": "^1.3.7",
|
||||||
"socket.io-client": "2.3.1",
|
"socket.io-client": "^4.0.0",
|
||||||
"store": "^2.0.12",
|
"store": "^2.0.12",
|
||||||
"uuid": "^8.3.2",
|
"vue": "^3.0.9",
|
||||||
"vue": "^2.6.11",
|
|
||||||
"vue-codemirror": "^4.0.6",
|
|
||||||
"vue-cron": "^1.0.9",
|
|
||||||
"vue-echarts": "^6.0.0-rc.3",
|
"vue-echarts": "^6.0.0-rc.3",
|
||||||
"vue-router": "^3.2.0",
|
"vue-router": "^4.0.5",
|
||||||
"vuedraggable": "^2.24.3",
|
"vuedraggable": "^4.0.1",
|
||||||
"vuex": "^3.4.0"
|
"vuex": "^4.0.0-0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@typescript-eslint/parser": "^3.0.0",
|
"@types/lodash": "^4.14.168",
|
||||||
"@vue/babel-helper-vue-jsx-merge-props": "^1.0.0",
|
"@typescript-eslint/eslint-plugin": "^2.33.0",
|
||||||
"@vue/babel-preset-jsx": "^1.1.2",
|
"@typescript-eslint/parser": "^2.33.0",
|
||||||
"@vue/cli-plugin-babel": "~4.5.0",
|
"@vue/cli-plugin-babel": "~4.5.0",
|
||||||
"@vue/cli-plugin-eslint": "~4.5.0",
|
"@vue/cli-plugin-eslint": "~4.5.0",
|
||||||
"@vue/cli-plugin-router": "~4.5.0",
|
"@vue/cli-plugin-router": "~4.5.0",
|
||||||
|
"@vue/cli-plugin-typescript": "~4.5.0",
|
||||||
"@vue/cli-plugin-vuex": "~4.5.0",
|
"@vue/cli-plugin-vuex": "~4.5.0",
|
||||||
"@vue/cli-service": "~4.5.0",
|
"@vue/cli-service": "~4.5.0",
|
||||||
|
"@vue/compiler-sfc": "^3.0.0",
|
||||||
"@vue/eslint-config-prettier": "^6.0.0",
|
"@vue/eslint-config-prettier": "^6.0.0",
|
||||||
"babel-eslint": "^10.1.0",
|
"@vue/eslint-config-typescript": "^5.0.2",
|
||||||
"babel-plugin-component": "^1.1.1",
|
|
||||||
"babel-plugin-jsx-v-model": "^2.0.3",
|
|
||||||
"clean-webpack-plugin": "^3.0.0",
|
|
||||||
"eslint": "^6.7.2",
|
"eslint": "^6.7.2",
|
||||||
"eslint-plugin-prettier": "^3.1.3",
|
"eslint-plugin-prettier": "^3.1.3",
|
||||||
"eslint-plugin-vue": "^6.2.2",
|
"eslint-plugin-vue": "^7.0.0-0",
|
||||||
"hard-source-webpack-plugin": "^0.13.1",
|
"hard-source-webpack-plugin": "^0.13.1",
|
||||||
|
"lint-staged": "^9.5.0",
|
||||||
"node-sass": "^4.12.0",
|
"node-sass": "^4.12.0",
|
||||||
"prettier": "^1.19.1",
|
"prettier": "^1.19.1",
|
||||||
"sass-loader": "^8.0.2",
|
"sass-loader": "^8.0.2",
|
||||||
"svg-sprite-loader": "^5.0.0",
|
"svg-sprite-loader": "^6.0.2",
|
||||||
"typescript": "^3.9.3",
|
"typescript": "~3.9.3"
|
||||||
"vue-template-compiler": "^2.6.11",
|
},
|
||||||
"webpack-cli": "^3.3.12"
|
"typings": "types/index.d.ts"
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
plugins: {
|
|
||||||
autoprefixer: {}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 4.2 KiB |
@ -1,99 +1,17 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="referer" content="never" />
|
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||||
<meta name="renderer" content="webkit" />
|
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||||
<meta
|
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||||
name="viewport"
|
</head>
|
||||||
content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=0"
|
<body>
|
||||||
/>
|
<noscript>
|
||||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico" />
|
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||||
<title>COOL-ADMIN</title>
|
</noscript>
|
||||||
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.css) { %>
|
<div id="app"></div>
|
||||||
<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="stylesheet" />
|
<!-- built files will be auto injected -->
|
||||||
<% } %>
|
</body>
|
||||||
<style>
|
|
||||||
html,
|
|
||||||
body,
|
|
||||||
#app {
|
|
||||||
height: 100%;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preload {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
height: 100%;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
background-color: #2f3447;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preload .container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
flex-direction: column;
|
|
||||||
width: 100%;
|
|
||||||
user-select: none;
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preload .name {
|
|
||||||
font-size: 30px;
|
|
||||||
color: #fff;
|
|
||||||
letter-spacing: 5px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preload .title {
|
|
||||||
color: #fff;
|
|
||||||
font-size: 14px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preload .sub-title {
|
|
||||||
color: #ababab;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preload .footer {
|
|
||||||
text-align: center;
|
|
||||||
padding: 10px 0 20px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preload .footer a {
|
|
||||||
font-size: 12px;
|
|
||||||
color: #ababab;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<noscript>
|
|
||||||
<strong
|
|
||||||
>We're sorry but cool-admin doesn't work properly without JavaScript enabled. Please
|
|
||||||
enable it to continue.</strong
|
|
||||||
>
|
|
||||||
</noscript>
|
|
||||||
<div id="app">
|
|
||||||
<div class="preload">
|
|
||||||
<div class="container">
|
|
||||||
<p class="name">COOL-ADMIN</p>
|
|
||||||
<p class="title">正在加载资源...</p>
|
|
||||||
<p class="sub-title">初次加载资源可能需要较多时间 请耐心等待</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="footer">
|
|
||||||
<a href="https://cool-js.com/" target="_blank"> https://cool-js.com </a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %>
|
|
||||||
<script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
|
|
||||||
<% } %>
|
|
||||||
</body>
|
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@ -4,6 +4,6 @@ $--color-danger: $color-danger;
|
|||||||
$--color-warning: $color-warning;
|
$--color-warning: $color-warning;
|
||||||
$--color-info: $color-info;
|
$--color-info: $color-info;
|
||||||
|
|
||||||
$--font-path: "~element-ui/lib/theme-chalk/fonts";
|
$--font-path: "~element-plus/lib/theme-chalk/fonts";
|
||||||
|
|
||||||
@import "~element-ui/packages/theme-chalk/src/index";
|
@import "~element-plus/packages/theme-chalk/src/index";
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
* {
|
* {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
outline: none;
|
|
||||||
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei",
|
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei",
|
||||||
"微软雅黑", Arial, sans-serif;
|
"微软雅黑", Arial, sans-serif;
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
src/assets/logo.png
Normal file
BIN
src/assets/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.7 KiB |
@ -1,20 +1,18 @@
|
|||||||
import store from "store";
|
import store from "store";
|
||||||
import { getUrlParam } from "cl-admin/utils";
|
import { getUrlParam } from "@/core/utils";
|
||||||
|
import { MenuItem } from "@/cool/modules/base/types";
|
||||||
|
|
||||||
// 路由模式
|
// 路由模式
|
||||||
export const routerMode = "history";
|
const routerMode = "history";
|
||||||
|
|
||||||
// 开发模式
|
// 开发模式
|
||||||
export const isDev = process.env.NODE_ENV == "development";
|
const isDev: boolean = process.env.NODE_ENV == "development";
|
||||||
|
|
||||||
// Host
|
// Host
|
||||||
export const host = "https://show.cool-admin.com";
|
const host = "https://show.cool-admin.com";
|
||||||
|
|
||||||
// Socket
|
// 请求地址
|
||||||
export const socketUrl = (isDev ? `${host}` : "") + "/socket";
|
const baseUrl: string = (function() {
|
||||||
|
|
||||||
// 请求地址,本地会使用代理请求
|
|
||||||
export const baseUrl = (function() {
|
|
||||||
let proxy = getUrlParam("proxy");
|
let proxy = getUrlParam("proxy");
|
||||||
|
|
||||||
if (proxy) {
|
if (proxy) {
|
||||||
@ -26,11 +24,14 @@ export const baseUrl = (function() {
|
|||||||
return isDev ? `/${proxy}/admin` : `/api/admin`;
|
return isDev ? `/${proxy}/admin` : `/api/admin`;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
// Socket
|
||||||
|
const socketUrl: string = (isDev ? `${host}` : "") + "/socket";
|
||||||
|
|
||||||
// 阿里字体图标库 https://at.alicdn.com/t/**.css
|
// 阿里字体图标库 https://at.alicdn.com/t/**.css
|
||||||
export const iconfontUrl = ``;
|
const iconfontUrl = ``;
|
||||||
|
|
||||||
// 程序配置参数
|
// 程序配置参数
|
||||||
export const app = store.get("__app__") || {
|
const app: any = store.get("__app__") || {
|
||||||
name: "COOL-ADMIN",
|
name: "COOL-ADMIN",
|
||||||
|
|
||||||
conf: {
|
conf: {
|
||||||
@ -47,4 +48,6 @@ export const app = store.get("__app__") || {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 自定义菜单列表
|
// 自定义菜单列表
|
||||||
export const menuList = [];
|
const menuList: MenuItem[] = [];
|
||||||
|
|
||||||
|
export { routerMode, baseUrl, socketUrl, iconfontUrl, app, isDev, menuList };
|
||||||
@ -1,46 +0,0 @@
|
|||||||
import Crud from "cl-admin-crud";
|
|
||||||
import Theme from "cl-admin-theme";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
modules: [
|
|
||||||
// 基础模块
|
|
||||||
"base",
|
|
||||||
// 文件上传
|
|
||||||
{
|
|
||||||
name: "upload",
|
|
||||||
options: {
|
|
||||||
icon: "el-icon-picture",
|
|
||||||
text: "选择图片"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "crud",
|
|
||||||
value: Crud,
|
|
||||||
options: {
|
|
||||||
crud: {
|
|
||||||
dict: {
|
|
||||||
sort: {
|
|
||||||
prop: "order",
|
|
||||||
order: "sort"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 客服聊天
|
|
||||||
"chat",
|
|
||||||
// 任务管理
|
|
||||||
"task",
|
|
||||||
// 复制指令
|
|
||||||
"copy",
|
|
||||||
// 省市区选择
|
|
||||||
"distpicker",
|
|
||||||
// 示例页
|
|
||||||
"demo",
|
|
||||||
// 主题切换
|
|
||||||
{
|
|
||||||
name: "theme",
|
|
||||||
value: Theme
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
3
src/cool/index.ts
Normal file
3
src/cool/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export default {
|
||||||
|
modules: ["base", "demo", "copy", "upload", "task", "theme", "chat"]
|
||||||
|
};
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
|
|
||||||
const lock = {
|
const lock: any = {
|
||||||
menuCollapse: null,
|
menuCollapse: null,
|
||||||
showAMenu: null
|
showAMenu: null
|
||||||
};
|
};
|
||||||
@ -32,7 +32,7 @@ function iconList() {
|
|||||||
return req
|
return req
|
||||||
.keys()
|
.keys()
|
||||||
.map(req)
|
.map(req)
|
||||||
.map(e => e.default.id)
|
.map((e: any) => e.default.id)
|
||||||
.filter(e => e.includes("icon"))
|
.filter(e => e.includes("icon"))
|
||||||
.sort();
|
.sort();
|
||||||
}
|
}
|
||||||
@ -1,17 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="cl-avatar" :class="[size, shape]" :style="[style]">
|
<div class="cl-avatar" :class="[size, shape]" :style="[style]">
|
||||||
<el-image :src="src" alt="">
|
<el-image :src="src" alt="">
|
||||||
<div slot="error" class="image-slot">
|
<template #error>
|
||||||
<i class="el-icon-picture-outline"></i>
|
<div class="image-slot">
|
||||||
</div>
|
<i class="el-icon-picture-outline"></i>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</el-image>
|
</el-image>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { isNumber } from "cl-admin/utils";
|
import { computed, defineComponent } from "vue";
|
||||||
|
import { isNumber } from "@/core/utils";
|
||||||
|
|
||||||
export default {
|
export default defineComponent({
|
||||||
name: "cl-avatar",
|
name: "cl-avatar",
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
@ -26,17 +29,21 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
setup(props) {
|
||||||
style() {
|
const size = isNumber(props.size) ? props.size + "px" : props.size;
|
||||||
const size = isNumber(this.size) ? this.size + "px" : this.size;
|
|
||||||
|
|
||||||
|
const style = computed(() => {
|
||||||
return {
|
return {
|
||||||
height: size,
|
height: size,
|
||||||
width: size
|
width: size
|
||||||
};
|
};
|
||||||
}
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
style
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@ -76,7 +83,7 @@ export default {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
/deep/.image-slot {
|
:deep(.image-slot) {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@ -1,19 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="cl-codemirror">
|
<div class="cl-codemirror">
|
||||||
<codemirror
|
<textarea class="cl-code" id="editor" :height="height" :width="width"></textarea>
|
||||||
ref="code"
|
|
||||||
v-model="value2"
|
|
||||||
:options="options2"
|
|
||||||
:style="{
|
|
||||||
height,
|
|
||||||
width
|
|
||||||
}"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { codemirror } from "vue-codemirror";
|
import { defineComponent, onMounted, watch } from "vue";
|
||||||
|
import CodeMirror from "codemirror";
|
||||||
import beautifyJs from "js-beautify";
|
import beautifyJs from "js-beautify";
|
||||||
|
|
||||||
import "codemirror/theme/cobalt.css";
|
import "codemirror/theme/cobalt.css";
|
||||||
@ -22,72 +15,94 @@ import "codemirror/addon/hint/show-hint.css";
|
|||||||
import "codemirror/addon/hint/javascript-hint";
|
import "codemirror/addon/hint/javascript-hint";
|
||||||
import "codemirror/mode/javascript/javascript";
|
import "codemirror/mode/javascript/javascript";
|
||||||
|
|
||||||
export default {
|
export default defineComponent({
|
||||||
name: "cl-codemirror",
|
name: "cl-codemirror",
|
||||||
|
|
||||||
components: {
|
|
||||||
codemirror
|
|
||||||
},
|
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
value: String,
|
modelValue: null,
|
||||||
height: String,
|
height: String,
|
||||||
width: String,
|
width: String,
|
||||||
options: Object
|
options: Object
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
emits: ["update:modelValue", "load"],
|
||||||
return {
|
|
||||||
value2: ""
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
setup(props, { emit }) {
|
||||||
value: {
|
let editor: any = null;
|
||||||
immediate: true,
|
|
||||||
handler(val) {
|
// 获取内容
|
||||||
this.value2 = val || "";
|
function getValue() {
|
||||||
}
|
return editor ? editor.getValue() : "";
|
||||||
},
|
|
||||||
value2(val) {
|
|
||||||
this.$emit("input", val);
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
// 设置内容
|
||||||
options2() {
|
function setValue(val?: string) {
|
||||||
return {
|
if (editor) {
|
||||||
|
editor.setValue(beautifyJs(val || getValue()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听内容变化
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
(val: string) => {
|
||||||
|
if (editor) {
|
||||||
|
if (val != getValue().replace(/\s/g, "")) {
|
||||||
|
setValue(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(function() {
|
||||||
|
// 实例化
|
||||||
|
editor = CodeMirror.fromTextArea(document.getElementById("editor"), {
|
||||||
mode: "javascript",
|
mode: "javascript",
|
||||||
theme: "ambiance",
|
theme: "ambiance",
|
||||||
styleActiveLine: true,
|
styleActiveLine: true,
|
||||||
lineNumbers: true,
|
lineNumbers: true,
|
||||||
lineWrapping: true,
|
lineWrapping: true,
|
||||||
indentUnit: 4,
|
indentUnit: 4,
|
||||||
...this.options
|
...props.options
|
||||||
};
|
});
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
// 输入监听
|
||||||
this.$el.onkeydown = e => {
|
editor.on("change", (e: any) => {
|
||||||
let keyCode = e.keyCode || e.which || e.charCode;
|
emit("update:modelValue", e.getValue().replace(/\s/g, ""));
|
||||||
let altKey = e.altKey || e.metaKey;
|
});
|
||||||
let shiftKey = e.shiftKey || e.metaKey;
|
|
||||||
|
|
||||||
if (altKey && shiftKey && keyCode == 70) {
|
// 加载回调
|
||||||
this.setValue();
|
emit("load", editor);
|
||||||
|
|
||||||
|
// 设置编辑框样式
|
||||||
|
const el = editor.display.wrapper;
|
||||||
|
|
||||||
|
if (el) {
|
||||||
|
if (props.height) {
|
||||||
|
el.style.height = props.height || "50px";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.width) {
|
||||||
|
el.style.width = props.width;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
this.setValue(this.value2);
|
// 设置内容
|
||||||
},
|
setValue(props.modelValue);
|
||||||
|
|
||||||
methods: {
|
// shift + alt + f 格式化
|
||||||
setValue(val) {
|
el.onkeydown = (e: any) => {
|
||||||
this.value2 = beautifyJs(val || this.value2);
|
const keyCode = e.keyCode || e.which || e.charCode;
|
||||||
}
|
const altKey = e.altKey || e.metaKey;
|
||||||
|
const shiftKey = e.shiftKey || e.metaKey;
|
||||||
|
|
||||||
|
if (altKey && shiftKey && keyCode == 70) {
|
||||||
|
setValue();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@ -98,10 +113,6 @@ export default {
|
|||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.CodeMirror {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-ambiance * {
|
.cm-s-ambiance * {
|
||||||
font-family: "Consolas";
|
font-family: "Consolas";
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
|
|||||||
@ -15,15 +15,18 @@
|
|||||||
|
|
||||||
<div class="cl-dept-check__tree" v-if="visible">
|
<div class="cl-dept-check__tree" v-if="visible">
|
||||||
<el-tree
|
<el-tree
|
||||||
:data="list"
|
ref="treeRef"
|
||||||
:props="props"
|
|
||||||
:default-checked-keys="checked"
|
|
||||||
:filter-node-method="filterNode"
|
|
||||||
:check-strictly="!form.relevance"
|
|
||||||
highlight-current
|
highlight-current
|
||||||
node-key="id"
|
node-key="id"
|
||||||
show-checkbox
|
show-checkbox
|
||||||
ref="tree"
|
:data="list"
|
||||||
|
:props="{
|
||||||
|
label: 'name',
|
||||||
|
children: 'children'
|
||||||
|
}"
|
||||||
|
:default-checked-keys="checked"
|
||||||
|
:filter-node-method="filterNode"
|
||||||
|
:check-strictly="!form.relevance"
|
||||||
@check-change="onCheckChange"
|
@check-change="onCheckChange"
|
||||||
>
|
>
|
||||||
</el-tree>
|
</el-tree>
|
||||||
@ -31,83 +34,120 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { deepTree } from "cl-admin/utils";
|
import { deepTree } from "@/core/utils";
|
||||||
|
import { ElMessage } from "element-plus";
|
||||||
|
import { defineComponent, inject, nextTick, onMounted, ref, watch } from "vue";
|
||||||
|
|
||||||
export default {
|
export default defineComponent({
|
||||||
name: "cl-dept-check",
|
name: "cl-dept-check",
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
value: Array,
|
modelValue: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
title: String
|
title: String
|
||||||
},
|
},
|
||||||
|
|
||||||
inject: ["form"],
|
emits: ["update:modelValue"],
|
||||||
|
|
||||||
data() {
|
setup(props, { emit }) {
|
||||||
return {
|
// 请求服务
|
||||||
list: [],
|
const $service = inject<any>("$service");
|
||||||
checked: [],
|
|
||||||
keyword: "",
|
|
||||||
props: {
|
|
||||||
label: "name",
|
|
||||||
children: "children"
|
|
||||||
},
|
|
||||||
loading: false,
|
|
||||||
visible: true
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
// 表单值
|
||||||
keyword(val) {
|
const form = inject<any>("form");
|
||||||
this.$refs["tree"].filter(val);
|
|
||||||
},
|
|
||||||
|
|
||||||
value(val) {
|
// 树形列表
|
||||||
this.refreshTree(val);
|
const list = ref<any[]>([]);
|
||||||
|
|
||||||
|
// 已选列表
|
||||||
|
const checked = ref<any>([]);
|
||||||
|
|
||||||
|
// 关键字搜素
|
||||||
|
const keyword = ref<string>("");
|
||||||
|
|
||||||
|
// 加载中
|
||||||
|
const loading = ref<boolean>(false);
|
||||||
|
|
||||||
|
// 是否可见
|
||||||
|
const visible = ref<boolean>(false);
|
||||||
|
|
||||||
|
const treeRef = ref<any>({});
|
||||||
|
|
||||||
|
// 刷新已选列表
|
||||||
|
function refreshTree(val: any[]) {
|
||||||
|
checked.value = val || [];
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
// 刷新树形列表
|
||||||
this.refresh();
|
function refresh() {
|
||||||
},
|
$service.system.dept
|
||||||
|
|
||||||
methods: {
|
|
||||||
refreshTree(val) {
|
|
||||||
this.checked = val || [];
|
|
||||||
},
|
|
||||||
|
|
||||||
refresh() {
|
|
||||||
this.$service.system.dept
|
|
||||||
.list()
|
.list()
|
||||||
.then(res => {
|
.then((res: any[]) => {
|
||||||
this.list = deepTree(res);
|
list.value = deepTree(res);
|
||||||
this.refreshTree(this.value);
|
refreshTree(props.modelValue);
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch((err: string) => {
|
||||||
this.$message.error(err);
|
ElMessage.error(err);
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
filterNode(val, data) {
|
// 过滤节点
|
||||||
|
function filterNode(val: string, data: any) {
|
||||||
if (!val) return true;
|
if (!val) return true;
|
||||||
return data.name.includes(val);
|
return data.name.includes(val);
|
||||||
},
|
|
||||||
|
|
||||||
onCheckStrictlyChange() {
|
|
||||||
this.form.departmentIdList = [];
|
|
||||||
this.visible = false;
|
|
||||||
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.visible = true;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
onCheckChange() {
|
|
||||||
this.$emit("input", this.$refs["tree"].getCheckedKeys());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 是否关联上下级
|
||||||
|
function onCheckStrictlyChange() {
|
||||||
|
visible.value = false;
|
||||||
|
checked.value = [];
|
||||||
|
emit("update:modelValue", []);
|
||||||
|
|
||||||
|
nextTick(() => {
|
||||||
|
visible.value = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听选择
|
||||||
|
function onCheckChange() {
|
||||||
|
emit("update:modelValue", treeRef.value.getCheckedKeys());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听过滤
|
||||||
|
watch(keyword, (val: string) => {
|
||||||
|
treeRef.value.filter(val);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 刷新树
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
(val: any[]) => {
|
||||||
|
refreshTree(val);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
refresh();
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
form,
|
||||||
|
list,
|
||||||
|
checked,
|
||||||
|
keyword,
|
||||||
|
loading,
|
||||||
|
visible,
|
||||||
|
refresh,
|
||||||
|
filterNode,
|
||||||
|
onCheckStrictlyChange,
|
||||||
|
onCheckChange,
|
||||||
|
treeRef
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
120
src/cool/modules/base/components/dept/move.tsx
Normal file
120
src/cool/modules/base/components/dept/move.tsx
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
import { useRefs } from "@/core";
|
||||||
|
import { deepTree } from "@/core/utils";
|
||||||
|
import { ElMessage, ElMessageBox } from "element-plus";
|
||||||
|
import { defineComponent, h, inject, ref } from "vue";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "cl-dept-move",
|
||||||
|
|
||||||
|
emits: ["success", "error"],
|
||||||
|
|
||||||
|
setup(_: any, { emit }) {
|
||||||
|
const $service = inject<any>("$service");
|
||||||
|
const { refs, setRefs } = useRefs();
|
||||||
|
|
||||||
|
// 树形列表
|
||||||
|
const list = ref<any[]>([]);
|
||||||
|
|
||||||
|
// 刷新列表
|
||||||
|
async function refresh() {
|
||||||
|
return await $service.system.dept.list().then(deepTree);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 转移
|
||||||
|
async function toMove(ids: any[]) {
|
||||||
|
list.value = await refresh();
|
||||||
|
|
||||||
|
refs.value.form.open({
|
||||||
|
props: {
|
||||||
|
title: "部门转移",
|
||||||
|
width: "600px",
|
||||||
|
labelWidth: "80px"
|
||||||
|
},
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
label: "选择部门",
|
||||||
|
prop: "dept",
|
||||||
|
component: {
|
||||||
|
name: "slot-move"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
on: {
|
||||||
|
submit: (data: any, { done, close }: any) => {
|
||||||
|
if (!data.dept) {
|
||||||
|
ElMessage.warning("请选择部门");
|
||||||
|
return done();
|
||||||
|
}
|
||||||
|
|
||||||
|
const { name, id } = data.dept;
|
||||||
|
|
||||||
|
ElMessageBox.confirm(`是否将用户转移到部门 ${name} 下`, "提示", {
|
||||||
|
type: "warning"
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
$service.system.user
|
||||||
|
.move({
|
||||||
|
departmentId: id,
|
||||||
|
userIds: ids
|
||||||
|
})
|
||||||
|
.then((res: any) => {
|
||||||
|
ElMessage.success("转移成功");
|
||||||
|
emit("success", res);
|
||||||
|
close();
|
||||||
|
})
|
||||||
|
.catch((err: any) => {
|
||||||
|
console.log(err);
|
||||||
|
ElMessage.error(err);
|
||||||
|
emit("error", err);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(() => null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
refs,
|
||||||
|
list,
|
||||||
|
setRefs,
|
||||||
|
refresh,
|
||||||
|
toMove
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
render(ctx: any) {
|
||||||
|
return (
|
||||||
|
<div class="cl-dept-move">
|
||||||
|
{h(
|
||||||
|
<cl-form ref={ctx.setRefs("form")}></cl-form>,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
"slot-move"({ scope }: any) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
border: "1px solid #eee",
|
||||||
|
borderRadius: "3px",
|
||||||
|
padding: "2px"
|
||||||
|
}}>
|
||||||
|
<el-tree
|
||||||
|
data={ctx.list}
|
||||||
|
props={{
|
||||||
|
label: "name"
|
||||||
|
}}
|
||||||
|
node-key="id"
|
||||||
|
highlight-current
|
||||||
|
onNodeClick={(e: any) => {
|
||||||
|
scope["dept"] = e;
|
||||||
|
}}></el-tree>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
@ -1,104 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="cl-dept-move"></div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { deepTree } from "cl-admin/utils";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "cl-dept-move",
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
async toMove(ids) {
|
|
||||||
this.$crud.openForm({
|
|
||||||
title: "部门转移",
|
|
||||||
width: "600px",
|
|
||||||
props: {
|
|
||||||
"label-width": "80px"
|
|
||||||
},
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
label: "选择部门",
|
|
||||||
prop: "dept",
|
|
||||||
component: {
|
|
||||||
name: "system-user__dept-move",
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
list: []
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
async created() {
|
|
||||||
this.list = await this.$service.system.dept.list().then(deepTree);
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
selectRow(e) {
|
|
||||||
this.$emit("input", e);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
border: "1px solid #eee",
|
|
||||||
"border-radius": "3px",
|
|
||||||
padding: "2px"
|
|
||||||
}}>
|
|
||||||
<el-tree
|
|
||||||
data={this.list}
|
|
||||||
{...{
|
|
||||||
props: {
|
|
||||||
props: {
|
|
||||||
label: "name"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
node-key="id"
|
|
||||||
highlight-current
|
|
||||||
on-node-click={this.selectRow}></el-tree>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
on: {
|
|
||||||
submit: (data, { done, close }) => {
|
|
||||||
if (!data.dept) {
|
|
||||||
this.$message.warning("请选择部门");
|
|
||||||
return done();
|
|
||||||
}
|
|
||||||
|
|
||||||
const { name, id } = data.dept;
|
|
||||||
|
|
||||||
this.$confirm(`是否将用户转移到部门 ${name} 下`, "提示", {
|
|
||||||
type: "warning"
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
this.$service.system.user
|
|
||||||
.move({
|
|
||||||
departmentId: id,
|
|
||||||
userIds: ids
|
|
||||||
})
|
|
||||||
.then(res => {
|
|
||||||
this.$message.success("转移成功");
|
|
||||||
this.$emit("success", res);
|
|
||||||
close();
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
this.$message.error(err);
|
|
||||||
this.$emit("error", err);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(() => {});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
@ -10,7 +10,7 @@
|
|||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li v-if="drag && !browser.isMini">
|
<li v-if="drag && !isMini">
|
||||||
<el-tooltip content="拖动排序">
|
<el-tooltip content="拖动排序">
|
||||||
<i class="el-icon-s-operation" @click="isDrag = true"></i>
|
<i class="el-icon-s-operation" @click="isDrag = true"></i>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
@ -39,14 +39,14 @@
|
|||||||
v-loading="loading"
|
v-loading="loading"
|
||||||
@node-contextmenu="openCM"
|
@node-contextmenu="openCM"
|
||||||
>
|
>
|
||||||
<template slot-scope="{ node, data }">
|
<template #default="{ node, data }">
|
||||||
<div class="cl-dept-tree__node">
|
<div class="cl-dept-tree__node">
|
||||||
<span class="cl-dept-tree__node-label" @click="rowClick(data)">{{
|
<span class="cl-dept-tree__node-label" @click="rowClick(data)">{{
|
||||||
node.label
|
node.label
|
||||||
}}</span>
|
}}</span>
|
||||||
<span
|
<span
|
||||||
class="cl-dept-tree__node-icon"
|
class="cl-dept-tree__node-icon"
|
||||||
v-if="browser.isMini"
|
v-if="isMini"
|
||||||
@click="openCM($event, data, node)"
|
@click="openCM($event, data, node)"
|
||||||
>
|
>
|
||||||
<i class="el-icon-more"></i>
|
<i class="el-icon-more"></i>
|
||||||
@ -55,15 +55,19 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-tree>
|
</el-tree>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<cl-form :ref="setRefs('form')"></cl-form>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { deepTree, isArray, revDeepTree } from "cl-admin/utils";
|
import { defineComponent, inject, onMounted, ref } from "vue";
|
||||||
import { ContextMenu, Form } from "cl-admin-crud";
|
import { ElMessage, ElMessageBox } from "element-plus";
|
||||||
import { mapGetters } from "vuex";
|
import { ContextMenu } from "@/crud";
|
||||||
|
import { useRefs } from "@/core";
|
||||||
|
import { deepTree, isArray, revDeepTree, isPc } from "@/core/utils";
|
||||||
|
|
||||||
export default {
|
export default defineComponent({
|
||||||
name: "cl-dept-tree",
|
name: "cl-dept-tree",
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
@ -77,110 +81,64 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
setup(props, { emit }) {
|
||||||
return {
|
const { refs, setRefs } = useRefs();
|
||||||
list: [],
|
|
||||||
loading: false,
|
|
||||||
isDrag: false
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
// 树形列表
|
||||||
...mapGetters(["browser"])
|
const list = ref<any[]>([]);
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
// 加载中
|
||||||
this.refresh();
|
const loading = ref<boolean>(false);
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
// 是否能拖动
|
||||||
openCM(e, d, n) {
|
const isDrag = ref<boolean>(false);
|
||||||
if (!d) {
|
|
||||||
d = this.list[0] || {};
|
|
||||||
}
|
|
||||||
|
|
||||||
ContextMenu.open(e, {
|
// 请求服务
|
||||||
list: [
|
const $service = inject<any>("$service");
|
||||||
{
|
|
||||||
label: "新增",
|
|
||||||
"suffix-icon": "el-icon-plus",
|
|
||||||
hidden: n && n.level >= this.level,
|
|
||||||
callback: (_, done) => {
|
|
||||||
this.rowEdit({
|
|
||||||
name: "",
|
|
||||||
parentName: d.name,
|
|
||||||
parentId: d.id
|
|
||||||
});
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "编辑",
|
|
||||||
"suffix-icon": "el-icon-edit",
|
|
||||||
callback: (_, done) => {
|
|
||||||
this.rowEdit(d);
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "删除",
|
|
||||||
"suffix-icon": "el-icon-delete",
|
|
||||||
hidden: !Boolean(d.parentId),
|
|
||||||
callback: (_, done) => {
|
|
||||||
this.rowDel(d);
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "新增成员",
|
|
||||||
"suffix-icon": "el-icon-user",
|
|
||||||
callback: (_, done) => {
|
|
||||||
this.$emit("user-add", d);
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
allowDrag({ data }) {
|
// 允许托的规则
|
||||||
|
function allowDrag({ data }: any) {
|
||||||
return data.parentId;
|
return data.parentId;
|
||||||
},
|
}
|
||||||
|
|
||||||
allowDrop(_, dropNode) {
|
// 允许放的规则
|
||||||
|
function allowDrop(_: any, dropNode: any) {
|
||||||
return dropNode.data.parentId;
|
return dropNode.data.parentId;
|
||||||
},
|
}
|
||||||
|
|
||||||
refresh() {
|
// 刷新
|
||||||
this.isDrag = false;
|
function refresh() {
|
||||||
this.loading = true;
|
isDrag.value = false;
|
||||||
|
loading.value = true;
|
||||||
|
|
||||||
this.$service.system.dept
|
$service.system.dept
|
||||||
.list()
|
.list()
|
||||||
.then(res => {
|
.then((res: any[]) => {
|
||||||
this.list = deepTree(res);
|
list.value = deepTree(res);
|
||||||
this.$emit("list-change", this.list);
|
emit("list-change", list.value);
|
||||||
})
|
})
|
||||||
.done(() => {
|
.done(() => {
|
||||||
this.loading = false;
|
loading.value = false;
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
rowClick(e) {
|
// 获取 ids
|
||||||
|
function rowClick(e: any) {
|
||||||
ContextMenu.close();
|
ContextMenu.close();
|
||||||
let ids = e.children ? revDeepTree(e.children).map(e => e.id) : [];
|
const ids = e.children ? revDeepTree(e.children).map(e => e.id) : [];
|
||||||
ids.unshift(e.id);
|
ids.unshift(e.id);
|
||||||
this.$emit("row-click", { item: e, ids });
|
emit("row-click", { item: e, ids });
|
||||||
},
|
}
|
||||||
|
|
||||||
rowEdit(e) {
|
// 编辑部门
|
||||||
|
function rowEdit(e: any) {
|
||||||
const method = e.id ? "update" : "add";
|
const method = e.id ? "update" : "add";
|
||||||
|
|
||||||
Form.open({
|
refs.value.form.open({
|
||||||
title: "编辑部门",
|
title: "编辑部门",
|
||||||
width: "550px",
|
width: "550px",
|
||||||
props: {
|
props: {
|
||||||
"label-width": "100px"
|
labelWidth: "100px"
|
||||||
},
|
},
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
@ -189,7 +147,7 @@ export default {
|
|||||||
value: e.name,
|
value: e.name,
|
||||||
component: {
|
component: {
|
||||||
name: "el-input",
|
name: "el-input",
|
||||||
attrs: {
|
props: {
|
||||||
placeholder: "请填写部门名称"
|
placeholder: "请填写部门名称"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -204,7 +162,7 @@ export default {
|
|||||||
value: e.parentName || "...",
|
value: e.parentName || "...",
|
||||||
component: {
|
component: {
|
||||||
name: "el-input",
|
name: "el-input",
|
||||||
attrs: {
|
props: {
|
||||||
disabled: true
|
disabled: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -224,50 +182,51 @@ export default {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
on: {
|
on: {
|
||||||
submit: (data, { done, close }) => {
|
submit: (data: any, { done, close }: any) => {
|
||||||
this.$service.system.dept[method]({
|
$service.system.dept[method]({
|
||||||
id: e.id,
|
id: e.id,
|
||||||
parentId: e.parentId,
|
parentId: e.parentId,
|
||||||
name: data.name,
|
name: data.name,
|
||||||
orderNum: data.orderNum
|
orderNum: data.orderNum
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.$message.success(`新增部门${data.name}成功`);
|
ElMessage.success(`新增部门${data.name}成功`);
|
||||||
close();
|
close();
|
||||||
this.refresh();
|
refresh();
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch((err: string) => {
|
||||||
this.$message.error(err);
|
ElMessage.error(err);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
rowDel(e) {
|
// 删除部门
|
||||||
const del = f => {
|
function rowDel(e: any) {
|
||||||
this.$service.system.dept
|
const del = (f: boolean) => {
|
||||||
|
$service.system.dept
|
||||||
.delete({
|
.delete({
|
||||||
ids: [e.id],
|
ids: [e.id],
|
||||||
deleteUser: f
|
deleteUser: f
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
if (f) {
|
if (f) {
|
||||||
this.$message.success("删除成功");
|
ElMessage.success("删除成功");
|
||||||
} else {
|
} else {
|
||||||
this.$confirm(
|
ElMessageBox.confirm(
|
||||||
`“${e.name}” 部门的用户已成功转移到 “${e.parentName}” 部门。`,
|
`“${e.name}” 部门的用户已成功转移到 “${e.parentName}” 部门。`,
|
||||||
"删除成功"
|
"删除成功"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.done(() => {
|
.done(() => {
|
||||||
this.refresh();
|
refresh();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
this.$confirm(`该操作会删除 “${e.name}” 部门的所有用户,是否确认?`, "提示", {
|
ElMessageBox.confirm(`该操作会删除 “${e.name}” 部门的所有用户,是否确认?`, "提示", {
|
||||||
type: "warning",
|
type: "warning",
|
||||||
confirmButtonText: "直接删除",
|
confirmButtonText: "直接删除",
|
||||||
cancelButtonText: "保留用户",
|
cancelButtonText: "保留用户",
|
||||||
@ -276,20 +235,23 @@ export default {
|
|||||||
.then(() => {
|
.then(() => {
|
||||||
del(true);
|
del(true);
|
||||||
})
|
})
|
||||||
.catch(action => {
|
.catch((action: string) => {
|
||||||
if (action == "cancel") {
|
if (action == "cancel") {
|
||||||
del(false);
|
del(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
treeOrder(f) {
|
// 部门排序
|
||||||
|
function treeOrder(f: boolean) {
|
||||||
if (f) {
|
if (f) {
|
||||||
this.$confirm("部门架构已发生改变,是否保存?", "提示", {
|
ElMessageBox.confirm("部门架构已发生改变,是否保存?", "提示", {
|
||||||
type: "warning"
|
type: "warning"
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
const deep = (list, pid) => {
|
const ids: any[] = [];
|
||||||
|
|
||||||
|
const deep = (list: any[], pid: any) => {
|
||||||
list.forEach(e => {
|
list.forEach(e => {
|
||||||
e.parentId = pid;
|
e.parentId = pid;
|
||||||
ids.push(e);
|
ids.push(e);
|
||||||
@ -300,11 +262,9 @@ export default {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
let ids = [];
|
deep(list.value, null);
|
||||||
|
|
||||||
deep(this.list, null);
|
$service.system.dept
|
||||||
|
|
||||||
this.$service.system.dept
|
|
||||||
.order(
|
.order(
|
||||||
ids.map((e, i) => {
|
ids.map((e, i) => {
|
||||||
return {
|
return {
|
||||||
@ -315,23 +275,94 @@ export default {
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.$message.success("更新排序成功");
|
ElMessage.success("更新排序成功");
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch((err: string) => {
|
||||||
this.$message.error(err);
|
ElMessage.error(err);
|
||||||
})
|
})
|
||||||
.done(() => {
|
.done(() => {
|
||||||
this.refresh();
|
refresh();
|
||||||
this.isDrag = false;
|
isDrag.value = false;
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(() => {});
|
.catch(() => null);
|
||||||
} else {
|
} else {
|
||||||
this.refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 右键菜单
|
||||||
|
function openCM(e: any, d: any, n: any) {
|
||||||
|
if (!d) {
|
||||||
|
d = list.value[0] || {};
|
||||||
|
}
|
||||||
|
|
||||||
|
ContextMenu.open(e, {
|
||||||
|
list: [
|
||||||
|
{
|
||||||
|
label: "新增",
|
||||||
|
"suffix-icon": "el-icon-plus",
|
||||||
|
hidden: n && n.level >= props.level,
|
||||||
|
callback: (_: any, done: Function) => {
|
||||||
|
rowEdit({
|
||||||
|
name: "",
|
||||||
|
parentName: d.name,
|
||||||
|
parentId: d.id
|
||||||
|
});
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "编辑",
|
||||||
|
"suffix-icon": "el-icon-edit",
|
||||||
|
callback: (_: any, done: Function) => {
|
||||||
|
rowEdit(d);
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "删除",
|
||||||
|
"suffix-icon": "el-icon-delete",
|
||||||
|
hidden: !d.parentId,
|
||||||
|
callback: (_: any, done: Function) => {
|
||||||
|
rowDel(d);
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "新增成员",
|
||||||
|
"suffix-icon": "el-icon-user",
|
||||||
|
callback: (_: any, done: Function) => {
|
||||||
|
emit("user-add", d);
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(function() {
|
||||||
|
refresh();
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
refs,
|
||||||
|
list,
|
||||||
|
loading,
|
||||||
|
isDrag,
|
||||||
|
isMini: !isPc(),
|
||||||
|
setRefs,
|
||||||
|
openCM,
|
||||||
|
allowDrag,
|
||||||
|
allowDrop,
|
||||||
|
refresh,
|
||||||
|
rowClick,
|
||||||
|
rowEdit,
|
||||||
|
rowDel,
|
||||||
|
treeOrder
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@ -360,7 +391,7 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/deep/.el-tree-node__content {
|
:deep(.el-tree-node__content) {
|
||||||
height: 36px;
|
height: 36px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -387,7 +418,7 @@ export default {
|
|||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
|
|
||||||
/deep/.el-tree-node__content {
|
:deep(.el-tree-node__content) {
|
||||||
margin: 0 5px;
|
margin: 0 5px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,144 +1,153 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="cl-editor-quill">
|
<div class="cl-editor-quill">
|
||||||
<div class="editor" :style="style"></div>
|
<div :ref="setRefs('editor')" class="editor" :style="style"></div>
|
||||||
|
|
||||||
<cl-upload-space
|
<cl-upload-space
|
||||||
ref="upload-space"
|
:ref="setRefs('upload-space')"
|
||||||
detail-data
|
detail-data
|
||||||
:show-button="false"
|
:show-button="false"
|
||||||
@confirm="onFileConfirm"
|
@confirm="onUploadSpaceConfirm"
|
||||||
>
|
>
|
||||||
</cl-upload-space>
|
</cl-upload-space>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts">
|
||||||
|
import { computed, defineComponent, onMounted, ref, watch } from "vue";
|
||||||
import Quill from "quill";
|
import Quill from "quill";
|
||||||
import "quill/dist/quill.snow.css";
|
import "quill/dist/quill.snow.css";
|
||||||
import { isNumber } from "cl-admin/utils";
|
import { isNumber } from "@/core/utils";
|
||||||
|
import { useRefs } from "@/core";
|
||||||
|
|
||||||
export default {
|
export default defineComponent({
|
||||||
name: "cl-editor-quill",
|
name: "cl-editor-quill",
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
value: null,
|
options: Object,
|
||||||
|
modelValue: null,
|
||||||
height: [String, Number],
|
height: [String, Number],
|
||||||
width: [String, Number],
|
width: [String, Number]
|
||||||
options: Object
|
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
emits: ["update:modelValue", "load"],
|
||||||
return {
|
|
||||||
content: "",
|
|
||||||
quill: null,
|
|
||||||
cursorIndex: 0
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
setup(props, { emit }) {
|
||||||
style() {
|
const { refs, setRefs } = useRefs();
|
||||||
const height = isNumber(this.height) ? this.height + "px" : this.height;
|
|
||||||
const width = isNumber(this.width) ? this.width + "px" : this.width;
|
let quill: any = null;
|
||||||
|
|
||||||
|
// 文本内容
|
||||||
|
const content = ref<string>("");
|
||||||
|
|
||||||
|
// 光标位置
|
||||||
|
const cursorIndex = ref<number>(0);
|
||||||
|
|
||||||
|
// 上传处理
|
||||||
|
function uploadFileHandler() {
|
||||||
|
const selection = quill.getSelection();
|
||||||
|
|
||||||
|
if (selection) {
|
||||||
|
cursorIndex.value = selection.index;
|
||||||
|
}
|
||||||
|
|
||||||
|
refs.value["upload-space"].open();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 文件确认
|
||||||
|
function onUploadSpaceConfirm(files: any[]) {
|
||||||
|
if (files.length > 0) {
|
||||||
|
files.forEach((file, i) => {
|
||||||
|
const [type] = file.type.split("/");
|
||||||
|
|
||||||
|
quill.insertEmbed(cursorIndex.value + i, type, file.url, Quill.sources.USER);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置内容
|
||||||
|
function setContent(val: string) {
|
||||||
|
quill.root.innerHTML = val || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 编辑框样式
|
||||||
|
const style = computed<any>(() => {
|
||||||
|
const height = isNumber(props.height) ? props.height + "px" : props.height;
|
||||||
|
const width = isNumber(props.width) ? props.width + "px" : props.width;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
height,
|
height,
|
||||||
width
|
width
|
||||||
};
|
};
|
||||||
}
|
});
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
// 监听绑定值
|
||||||
value(val) {
|
watch(
|
||||||
if (val) {
|
() => props.modelValue,
|
||||||
if (val !== this.content) {
|
(val: string) => {
|
||||||
this.setContent(val);
|
if (val) {
|
||||||
|
if (val !== content.value) {
|
||||||
|
setContent(val);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setContent("");
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
this.setContent("");
|
|
||||||
}
|
}
|
||||||
},
|
);
|
||||||
|
|
||||||
content(val) {
|
onMounted(function() {
|
||||||
this.$emit("input", val);
|
// 实例化
|
||||||
}
|
quill = new Quill(refs.value.editor, {
|
||||||
},
|
theme: "snow",
|
||||||
|
placeholder: "输入内容",
|
||||||
|
modules: {
|
||||||
|
toolbar: [
|
||||||
|
["bold", "italic", "underline", "strike"],
|
||||||
|
["blockquote", "code-block"],
|
||||||
|
[{ header: 1 }, { header: 2 }],
|
||||||
|
[{ list: "ordered" }, { list: "bullet" }],
|
||||||
|
[{ script: "sub" }, { script: "super" }],
|
||||||
|
[{ indent: "-1" }, { indent: "+1" }],
|
||||||
|
[{ direction: "rtl" }],
|
||||||
|
[{ size: ["small", false, "large", "huge"] }],
|
||||||
|
[{ header: [1, 2, 3, 4, 5, 6, false] }],
|
||||||
|
[{ color: [] }, { background: [] }],
|
||||||
|
[{ font: [] }],
|
||||||
|
[{ align: [] }],
|
||||||
|
["clean"],
|
||||||
|
["link", "image"]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
...props.options
|
||||||
|
});
|
||||||
|
|
||||||
mounted() {
|
// 添加图片工具
|
||||||
// 实例化
|
quill.getModule("toolbar").addHandler("image", uploadFileHandler);
|
||||||
this.quill = new Quill(this.$el.querySelector(".editor"), {
|
|
||||||
theme: "snow",
|
// 监听输入
|
||||||
placeholder: "输入内容",
|
quill.on("text-change", () => {
|
||||||
modules: {
|
content.value = quill.root.innerHTML;
|
||||||
toolbar: [
|
emit("update:modelValue", content.value);
|
||||||
["bold", "italic", "underline", "strike"],
|
});
|
||||||
["blockquote", "code-block"],
|
|
||||||
[{ header: 1 }, { header: 2 }],
|
// 设置内容
|
||||||
[{ list: "ordered" }, { list: "bullet" }],
|
setContent(props.modelValue);
|
||||||
[{ script: "sub" }, { script: "super" }],
|
|
||||||
[{ indent: "-1" }, { indent: "+1" }],
|
// 加载回调
|
||||||
[{ direction: "rtl" }],
|
emit("load", quill);
|
||||||
[{ size: ["small", false, "large", "huge"] }],
|
|
||||||
[{ header: [1, 2, 3, 4, 5, 6, false] }],
|
|
||||||
[{ color: [] }, { background: [] }],
|
|
||||||
[{ font: [] }],
|
|
||||||
[{ align: [] }],
|
|
||||||
["clean"],
|
|
||||||
["link", "image"]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
...this.options
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 添加文件上传工具
|
return {
|
||||||
this.quill.getModule("toolbar").addHandler("image", this.uploadHandler);
|
refs,
|
||||||
|
content,
|
||||||
// 监听内容变化
|
quill,
|
||||||
this.quill.on("text-change", () => {
|
cursorIndex,
|
||||||
this.content = this.quill.root.innerHTML;
|
style,
|
||||||
});
|
setRefs,
|
||||||
|
setContent,
|
||||||
// 设置默认内容
|
onUploadSpaceConfirm
|
||||||
this.setContent(this.value);
|
};
|
||||||
|
|
||||||
// 加载完回调
|
|
||||||
this.$emit("load", this.quill);
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
uploadHandler() {
|
|
||||||
const selection = this.quill.getSelection();
|
|
||||||
|
|
||||||
if (selection) {
|
|
||||||
this.cursorIndex = selection.index;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$refs["upload-space"].open();
|
|
||||||
},
|
|
||||||
|
|
||||||
onFileConfirm(files) {
|
|
||||||
if (files.length > 0) {
|
|
||||||
// 批量插件图片
|
|
||||||
files.forEach((file, i) => {
|
|
||||||
let [type] = file.type.split("/");
|
|
||||||
|
|
||||||
this.quill.insertEmbed(
|
|
||||||
this.cursorIndex + i,
|
|
||||||
type,
|
|
||||||
file.url,
|
|
||||||
Quill.sources.USER
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 移动光标到图片后一位
|
|
||||||
this.quill.setSelection(this.cursorIndex + files.length);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
setContent(val) {
|
|
||||||
this.quill.root.innerHTML = val || "";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|||||||
@ -1,13 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<svg :class="svgClass" :style="style2" aria-hidden="true">
|
<svg :class="svgClass" :style="style" aria-hidden="true">
|
||||||
<use :xlink:href="iconName"></use>
|
<use :xlink:href="iconName"></use>
|
||||||
</svg>
|
</svg>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { isNumber } from "cl-admin/utils";
|
import { computed, defineComponent, ref } from "vue";
|
||||||
|
import { isNumber } from "@/core/utils";
|
||||||
|
|
||||||
export default {
|
export default defineComponent({
|
||||||
name: "icon-svg",
|
name: "icon-svg",
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
@ -22,30 +23,26 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
setup(props) {
|
||||||
|
const style = ref<any>({
|
||||||
|
fontSize: isNumber(props.size) ? props.size + "px" : props.size
|
||||||
|
});
|
||||||
|
|
||||||
|
const iconName = computed<string>(() => `#${props.name}`);
|
||||||
|
const svgClass = computed<Array<string>>(() => {
|
||||||
|
return ["icon-svg", `icon-svg__${props.name}`, String(props.className || "")];
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
style2: {}
|
style,
|
||||||
};
|
iconName,
|
||||||
},
|
svgClass
|
||||||
|
|
||||||
computed: {
|
|
||||||
iconName() {
|
|
||||||
return `#${this.name}`;
|
|
||||||
},
|
|
||||||
svgClass() {
|
|
||||||
return ["icon-svg", `icon-svg__${this.name}`, this.className];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
this.style2 = {
|
|
||||||
fontSize: isNumber(this.size) ? this.size + "px" : this.size
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style scoped>
|
||||||
.icon-svg {
|
.icon-svg {
|
||||||
width: 1em;
|
width: 1em;
|
||||||
height: 1em;
|
height: 1em;
|
||||||
|
|||||||
@ -1,39 +0,0 @@
|
|||||||
import Avatar from "./avatar";
|
|
||||||
import Scrollbar from "./scrollbar";
|
|
||||||
import RouteNav from "./route-nav";
|
|
||||||
import Process from "./process";
|
|
||||||
import IconSvg from "./icon-svg";
|
|
||||||
import DeptCheck from "./dept/check";
|
|
||||||
import DeptMove from "./dept/move";
|
|
||||||
import DeptTree from "./dept/tree";
|
|
||||||
import MenuSlider from "./menu/slider";
|
|
||||||
import MenuTopbar from "./menu/topbar";
|
|
||||||
import MenuFile from "./menu/file";
|
|
||||||
import MenuIcons from "./menu/icons";
|
|
||||||
import MenuPerms from "./menu/perms";
|
|
||||||
import MenuTree from "./menu/tree";
|
|
||||||
import RoleSelect from "./role/select";
|
|
||||||
import RolePerms from "./role/perms";
|
|
||||||
import EditorQuill from "./editor-quill";
|
|
||||||
import Codemirror from "./codemirror";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
Avatar,
|
|
||||||
Scrollbar,
|
|
||||||
RouteNav,
|
|
||||||
Process,
|
|
||||||
IconSvg,
|
|
||||||
DeptCheck,
|
|
||||||
DeptMove,
|
|
||||||
DeptTree,
|
|
||||||
MenuSlider,
|
|
||||||
MenuTopbar,
|
|
||||||
MenuFile,
|
|
||||||
MenuIcons,
|
|
||||||
MenuPerms,
|
|
||||||
MenuTree,
|
|
||||||
RoleSelect,
|
|
||||||
RolePerms,
|
|
||||||
EditorQuill,
|
|
||||||
Codemirror
|
|
||||||
};
|
|
||||||
39
src/cool/modules/base/components/index.ts
Normal file
39
src/cool/modules/base/components/index.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import Avatar from "./avatar/index.vue";
|
||||||
|
import Scrollbar from "./scrollbar/index.vue";
|
||||||
|
import RouteNav from "./route-nav/index.vue";
|
||||||
|
import Process from "./process/index.vue";
|
||||||
|
import IconSvg from "./icon-svg/index.vue";
|
||||||
|
import DeptCheck from "./dept/check.vue";
|
||||||
|
import DeptMove from "./dept/move";
|
||||||
|
import DeptTree from "./dept/tree.vue";
|
||||||
|
import MenuSlider from "./menu/slider/index";
|
||||||
|
import MenuTopbar from "./menu/topbar.vue";
|
||||||
|
import MenuFile from "./menu/file.vue";
|
||||||
|
import MenuIcons from "./menu/icons.vue";
|
||||||
|
import MenuPerms from "./menu/perms.vue";
|
||||||
|
import MenuTree from "./menu/tree.vue";
|
||||||
|
import RoleSelect from "./role/select.vue";
|
||||||
|
import RolePerms from "./role/perms.vue";
|
||||||
|
import EditorQuill from "./editor-quill/index.vue";
|
||||||
|
import Codemirror from "./codemirror/index.vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
Avatar,
|
||||||
|
Scrollbar,
|
||||||
|
RouteNav,
|
||||||
|
Process,
|
||||||
|
IconSvg,
|
||||||
|
DeptCheck,
|
||||||
|
DeptMove,
|
||||||
|
DeptTree,
|
||||||
|
MenuSlider,
|
||||||
|
MenuTopbar,
|
||||||
|
MenuFile,
|
||||||
|
MenuIcons,
|
||||||
|
MenuPerms,
|
||||||
|
MenuTree,
|
||||||
|
RoleSelect,
|
||||||
|
RolePerms,
|
||||||
|
EditorQuill,
|
||||||
|
Codemirror
|
||||||
|
};
|
||||||
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="cl-menu-file">
|
<div class="cl-menu-file">
|
||||||
<el-select v-model="newValue" allow-create filterable clearable placeholder="请选择">
|
<el-select v-model="path" allow-create filterable clearable placeholder="请选择">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="(item, index) in list"
|
v-for="(item, index) in list"
|
||||||
:key="index"
|
:key="index"
|
||||||
@ -12,55 +12,62 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts">
|
||||||
|
import { defineComponent, ref, watch } from "vue";
|
||||||
|
|
||||||
const files = require
|
const files = require
|
||||||
.context("@/", true, /views\/(?!(components)|(.*\/components)|(index\.js)).*.(js|vue)/)
|
.context("@/", true, /views\/(?!(components)|(.*\/components)|(index\.js)).*.(js|vue)/)
|
||||||
.keys();
|
.keys();
|
||||||
|
|
||||||
export default {
|
export default defineComponent({
|
||||||
name: "cl-menu-file",
|
name: "cl-menu-file",
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
value: [String]
|
modelValue: {
|
||||||
},
|
type: String,
|
||||||
|
default: ""
|
||||||
inject: ["form"],
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
newValue: "",
|
|
||||||
list: []
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
value: {
|
|
||||||
immediate: true,
|
|
||||||
handler(val) {
|
|
||||||
this.newValue = val || "";
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
newValue(val) {
|
|
||||||
this.$emit("input", val);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
created() {
|
emits: ["update:modelValue"],
|
||||||
this.list = files.map(e => {
|
|
||||||
|
setup(props, { emit }) {
|
||||||
|
// 路径
|
||||||
|
const path = ref<string>(props.modelValue);
|
||||||
|
|
||||||
|
// 数据列表
|
||||||
|
const list = ref<any[]>([]);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
val => {
|
||||||
|
path.value = val || "";
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(path, val => {
|
||||||
|
emit("update:modelValue", val);
|
||||||
|
});
|
||||||
|
|
||||||
|
list.value = files.map(e => {
|
||||||
return {
|
return {
|
||||||
value: e.substr(2)
|
value: e.substr(2)
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
path,
|
||||||
|
list
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.cl-menu-file {
|
.cl-menu-file {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
/deep/ .el-select {
|
:deep(.el-select) {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,83 +1,105 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="cl-menu-icons">
|
<div class="cl-menu-icons">
|
||||||
<el-popover
|
<el-popover
|
||||||
ref="iconPopover"
|
:visible="visible"
|
||||||
placement="bottom-start"
|
placement="bottom-start"
|
||||||
trigger="click"
|
trigger="click"
|
||||||
|
width="480px"
|
||||||
popper-class="popper-menu-icon"
|
popper-class="popper-menu-icon"
|
||||||
>
|
>
|
||||||
<el-row :gutter="10" class="list">
|
<el-row :gutter="10" class="list scroller1">
|
||||||
<el-col :span="3" :xs="4" v-for="(item, index) in list" :key="index">
|
<el-col :span="3" :xs="4" v-for="(item, index) in list" :key="index">
|
||||||
<el-button
|
<el-button
|
||||||
size="mini"
|
size="mini"
|
||||||
:class="{ 'is-active': item === value }"
|
:class="{ 'is-active': item === name }"
|
||||||
@click="onUpdate(item)"
|
@click="onChange(item)"
|
||||||
>
|
>
|
||||||
<icon-svg :name="item"></icon-svg>
|
<icon-svg :name="item"></icon-svg>
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</el-popover>
|
|
||||||
|
|
||||||
<el-input
|
<template #reference>
|
||||||
v-model="name"
|
<el-input
|
||||||
v-popover:iconPopover
|
v-model="name"
|
||||||
placeholder="请选择"
|
placeholder="请选择"
|
||||||
@input="onUpdate"
|
clearable
|
||||||
></el-input>
|
@click="open"
|
||||||
|
@input="onChange"
|
||||||
|
></el-input>
|
||||||
|
</template>
|
||||||
|
</el-popover>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts">
|
||||||
|
import { defineComponent, ref, watch } from "vue";
|
||||||
import { iconList } from "@/cool/modules/base";
|
import { iconList } from "@/cool/modules/base";
|
||||||
|
|
||||||
export default {
|
export default defineComponent({
|
||||||
name: "cl-menu-icons",
|
name: "cl-menu-icons",
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
value: String
|
modelValue: {
|
||||||
|
type: String,
|
||||||
|
default: ""
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
emits: ["update:modelValue"],
|
||||||
return {
|
|
||||||
list: [],
|
|
||||||
name: ""
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
setup(props, { emit }) {
|
||||||
value: {
|
// 是否可见
|
||||||
immediate: true,
|
const visible = ref<boolean>(false);
|
||||||
handler(val) {
|
|
||||||
this.name = val;
|
// 图标列表
|
||||||
|
const list = ref<any[]>(iconList());
|
||||||
|
|
||||||
|
// 已选图标
|
||||||
|
const name = ref<string>(props.modelValue);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
val => {
|
||||||
|
name.value = val;
|
||||||
}
|
}
|
||||||
}
|
);
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
function open() {
|
||||||
this.list = iconList();
|
visible.value = true;
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
onUpdate(icon) {
|
|
||||||
this.$emit("input", icon);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function close() {
|
||||||
|
visible.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onChange(val: string) {
|
||||||
|
emit("update:modelValue", val);
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
list,
|
||||||
|
visible,
|
||||||
|
open,
|
||||||
|
close,
|
||||||
|
onChange
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
.
|
.
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.popper-menu-icon {
|
.popper-menu-icon {
|
||||||
width: 480px;
|
|
||||||
max-width: 90%;
|
max-width: 90%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
||||||
.list {
|
.list {
|
||||||
height: 250px;
|
|
||||||
overflow-y: auto;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
height: 250px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-button {
|
.el-button {
|
||||||
|
|||||||
@ -1,97 +1,121 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-cascader
|
<div class="cl-menu-perms">
|
||||||
:options="options"
|
<el-cascader
|
||||||
:props="{ multiple: true }"
|
v-model="value"
|
||||||
separator=":"
|
separator=":"
|
||||||
clearable
|
clearable
|
||||||
filterable
|
filterable
|
||||||
v-model="newValue"
|
:options="options"
|
||||||
@change="onChange"
|
:props="{ multiple: true }"
|
||||||
></el-cascader>
|
@change="onChange"
|
||||||
|
></el-cascader>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts">
|
||||||
export default {
|
import { defineComponent, inject, ref, watch } from "vue";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
name: "cl-menu-perms",
|
name: "cl-menu-perms",
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
value: [String, Number, Array]
|
modelValue: {
|
||||||
},
|
type: String,
|
||||||
|
default: ""
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
options: [],
|
|
||||||
newValue: []
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
value() {
|
|
||||||
this.parse();
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
created() {
|
emits: ["update:modelValue"],
|
||||||
let options = [];
|
|
||||||
let list = [];
|
|
||||||
|
|
||||||
const flat = obj => {
|
setup(props, { emit }) {
|
||||||
for (let i in obj) {
|
const $service = inject("$service");
|
||||||
let { permission } = obj[i];
|
|
||||||
|
|
||||||
if (permission) {
|
// 绑定值
|
||||||
list = [...list, Object.values(permission)].flat();
|
const value = ref<any[]>([]);
|
||||||
} else {
|
|
||||||
flat(obj[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
flat(this.$service);
|
// 权限列表
|
||||||
|
const options = ref<any[]>([]);
|
||||||
|
|
||||||
list.filter(e => e.includes(":"))
|
// 监听改变
|
||||||
.map(e => e.split(":"))
|
function onChange(row: any) {
|
||||||
.forEach(arr => {
|
emit("update:modelValue", row.map((e: any) => e.join(":")).join(","));
|
||||||
const col = (i, d) => {
|
}
|
||||||
let key = arr[i];
|
|
||||||
|
|
||||||
let index = d.findIndex(e => e.label == key);
|
// 解析权限
|
||||||
|
(function parsePerm() {
|
||||||
|
const list: any[] = [];
|
||||||
|
let perms: any[] = [];
|
||||||
|
|
||||||
if (index >= 0) {
|
const flat = (obj: any) => {
|
||||||
col(i + 1, d[index].children);
|
for (const i in obj) {
|
||||||
|
const { permission } = obj[i];
|
||||||
|
|
||||||
|
if (permission) {
|
||||||
|
perms = [...perms, Object.values(permission)].flat();
|
||||||
} else {
|
} else {
|
||||||
let isLast = i == arr.length - 1;
|
flat(obj[i]);
|
||||||
|
|
||||||
d.push({
|
|
||||||
label: key,
|
|
||||||
value: key,
|
|
||||||
children: isLast ? null : []
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!isLast) {
|
|
||||||
col(i + 1, d[d.length - 1].children || []);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
};
|
||||||
|
|
||||||
col(0, options);
|
flat($service);
|
||||||
});
|
|
||||||
|
|
||||||
this.options = options;
|
perms
|
||||||
},
|
.filter(e => e.includes(":"))
|
||||||
|
.map(e => e.split(":"))
|
||||||
|
.forEach(arr => {
|
||||||
|
const col = (i: number, d: any[]) => {
|
||||||
|
const key = arr[i];
|
||||||
|
|
||||||
mounted() {
|
const index = d.findIndex((e: any) => e.label == key);
|
||||||
this.parse();
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
if (index >= 0) {
|
||||||
parse() {
|
col(i + 1, d[index].children);
|
||||||
this.newValue = this.value ? this.value.split(",").map(e => e.split(":")) : [];
|
} else {
|
||||||
},
|
const isLast = i == arr.length - 1;
|
||||||
|
|
||||||
onChange(row) {
|
d.push({
|
||||||
this.$emit("input", row.map(e => e.join(":")).join(","));
|
label: key,
|
||||||
}
|
value: key,
|
||||||
|
children: isLast ? null : []
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!isLast) {
|
||||||
|
col(i + 1, d[d.length - 1].children || []);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
col(0, list);
|
||||||
|
});
|
||||||
|
|
||||||
|
options.value = list;
|
||||||
|
})();
|
||||||
|
|
||||||
|
// 监听值
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
(val: string) => {
|
||||||
|
value.value = val ? val.split(",").map((e: string) => e.split(":")) : [];
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
value,
|
||||||
|
options,
|
||||||
|
onChange
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.cl-menu-perms {
|
||||||
|
:deep(.el-cascader) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@ -1,92 +0,0 @@
|
|||||||
import { mapGetters } from "vuex";
|
|
||||||
import "./index.scss";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "cl-menu-slider",
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
visible: true
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
...mapGetters(["menuList", "menuCollapse", "browser", "app"])
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
menuList() {
|
|
||||||
this.refresh();
|
|
||||||
},
|
|
||||||
"app.conf.showAMenu"() {
|
|
||||||
this.$store.commit("SET_MENU_LIST");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
toView(url) {
|
|
||||||
if (url != this.$route.path) {
|
|
||||||
this.$router.push(url);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
refresh() {
|
|
||||||
this.visible = false;
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
this.visible = true;
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const fn = list => {
|
|
||||||
return list
|
|
||||||
.filter(e => e.isShow)
|
|
||||||
.map(e => {
|
|
||||||
let html = null;
|
|
||||||
|
|
||||||
if (e.type == 0) {
|
|
||||||
html = (
|
|
||||||
<el-submenu
|
|
||||||
popper-class="cl-slider-menu__submenu"
|
|
||||||
index={String(e.id)}
|
|
||||||
key={e.id}>
|
|
||||||
<template slot="title">
|
|
||||||
<icon-svg name={e.icon}></icon-svg>
|
|
||||||
<span slot="title">{e.name}</span>
|
|
||||||
</template>
|
|
||||||
{fn(e.children)}
|
|
||||||
</el-submenu>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
html = (
|
|
||||||
<el-menu-item index={e.path} key={e.path}>
|
|
||||||
<icon-svg name={e.icon}></icon-svg>
|
|
||||||
<span slot="title">{e.name}</span>
|
|
||||||
</el-menu-item>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return html;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
let el = fn(this.menuList);
|
|
||||||
|
|
||||||
return (
|
|
||||||
this.visible && (
|
|
||||||
<div class="cl-slider-menu">
|
|
||||||
<el-menu
|
|
||||||
default-active={this.$route.path}
|
|
||||||
background-color="transparent"
|
|
||||||
collapse-transition={false}
|
|
||||||
collapse={this.browser.isMini ? false : this.menuCollapse}
|
|
||||||
on-select={this.toView}>
|
|
||||||
{el}
|
|
||||||
</el-menu>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@ -30,6 +30,8 @@
|
|||||||
.icon-svg {
|
.icon-svg {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
margin: 0 15px 0 5px;
|
margin: 0 15px 0 5px;
|
||||||
|
position: relative;
|
||||||
|
top: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
span {
|
span {
|
||||||
|
|||||||
122
src/cool/modules/base/components/menu/slider/index.tsx
Normal file
122
src/cool/modules/base/components/menu/slider/index.tsx
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
import { useStore } from "vuex";
|
||||||
|
import { computed, defineComponent, h, ref, watch } from "vue";
|
||||||
|
import "./index.scss";
|
||||||
|
import { useRoute, useRouter } from "vue-router";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "cl-menu-slider",
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
const router = useRouter();
|
||||||
|
const route = useRoute();
|
||||||
|
const store = useStore();
|
||||||
|
|
||||||
|
// 是否可见
|
||||||
|
const visible = ref<boolean>(true);
|
||||||
|
// 菜单列表
|
||||||
|
const menuList = computed(() => store.getters.menuList);
|
||||||
|
// 菜单是否折叠
|
||||||
|
const menuCollapse = computed(() => store.getters.menuCollapse);
|
||||||
|
// 浏览器信息
|
||||||
|
const browser: any = computed(() => store.getters.browser);
|
||||||
|
|
||||||
|
// 页面跳转
|
||||||
|
function toView(url: string) {
|
||||||
|
if (url != route.path) {
|
||||||
|
router.push(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 刷新菜单
|
||||||
|
function refresh() {
|
||||||
|
visible.value = false;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
visible.value = true;
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听菜单变化
|
||||||
|
watch(menuList, refresh);
|
||||||
|
|
||||||
|
return {
|
||||||
|
route,
|
||||||
|
visible,
|
||||||
|
menuList,
|
||||||
|
menuCollapse,
|
||||||
|
browser,
|
||||||
|
toView,
|
||||||
|
refresh
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
render(ctx: any) {
|
||||||
|
function deepMenu(list: any) {
|
||||||
|
return list
|
||||||
|
.filter((e: any) => e.isShow)
|
||||||
|
.map((e: any) => {
|
||||||
|
let html = null;
|
||||||
|
|
||||||
|
if (e.type == 0) {
|
||||||
|
html = h(
|
||||||
|
<el-submenu></el-submenu>,
|
||||||
|
{
|
||||||
|
index: String(e.id),
|
||||||
|
key: e.id
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: () => {
|
||||||
|
return !ctx.menuCollapse ? (
|
||||||
|
<span>
|
||||||
|
<icon-svg name={e.icon}></icon-svg>
|
||||||
|
<span>{e.name}</span>
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<icon-svg name={e.icon}></icon-svg>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
default() {
|
||||||
|
return deepMenu(e.children);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
html = h(
|
||||||
|
<el-menu-item></el-menu-item>,
|
||||||
|
{
|
||||||
|
index: e.path,
|
||||||
|
key: e.id
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title() {
|
||||||
|
return <span>{e.name}</span>;
|
||||||
|
},
|
||||||
|
default() {
|
||||||
|
return <icon-svg name={e.icon}></icon-svg>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return html;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const children = deepMenu(ctx.menuList);
|
||||||
|
|
||||||
|
return (
|
||||||
|
ctx.visible && (
|
||||||
|
<div class="cl-slider-menu">
|
||||||
|
<el-menu
|
||||||
|
default-active={ctx.route.path}
|
||||||
|
background-color="transparent"
|
||||||
|
collapse-transition={false}
|
||||||
|
collapse={ctx.browser.isMini ? false : ctx.menuCollapse}
|
||||||
|
onSelect={ctx.toView}>
|
||||||
|
{children}
|
||||||
|
</el-menu>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="cl-menu-topbar">
|
<div class="app-topbar-menu">
|
||||||
<el-menu
|
<el-menu
|
||||||
:default-active="index"
|
:default-active="index"
|
||||||
mode="horizontal"
|
mode="horizontal"
|
||||||
@ -14,70 +14,80 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { mapMutations } from "vuex";
|
import { computed, defineComponent, onMounted, ref } from "vue";
|
||||||
|
import { useStore } from "vuex";
|
||||||
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import { firstMenu } from "../../utils";
|
import { firstMenu } from "../../utils";
|
||||||
|
|
||||||
export default {
|
export default defineComponent({
|
||||||
name: "cl-menu-topbar",
|
name: "cl-menu-topbar",
|
||||||
|
|
||||||
data() {
|
setup() {
|
||||||
return {
|
// 缓存
|
||||||
index: "0"
|
const store = useStore();
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
// 路由控制
|
||||||
list() {
|
const router = useRouter();
|
||||||
return this.$store.getters.menuGroup.filter(e => e.isShow);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
// 当页路由
|
||||||
const deep = (e, i) => {
|
const route = useRoute();
|
||||||
switch (e.type) {
|
|
||||||
case 0:
|
|
||||||
e.children.forEach(e => {
|
|
||||||
deep(e, i);
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
if (this.$route.path.includes(e.path)) {
|
|
||||||
this.index = String(i);
|
|
||||||
this.SET_MENU_LIST(i);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2:
|
// 选中标识
|
||||||
default:
|
const index = ref<string>("0");
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.list.forEach((e, i) => {
|
// 菜单列表
|
||||||
deep(e, i);
|
const list = computed(() => store.getters.menuGroup.filter((e: any) => e.isShow));
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
// 选择导航
|
||||||
...mapMutations(["SET_MENU_LIST"]),
|
function onSelect(index: number) {
|
||||||
|
store.commit("SET_MENU_LIST", index);
|
||||||
onSelect(index) {
|
|
||||||
this.SET_MENU_LIST(index);
|
|
||||||
|
|
||||||
// 获取第一个菜单地址
|
// 获取第一个菜单地址
|
||||||
const url = firstMenu(this.list[index].children);
|
const url = firstMenu(list.value[index].children);
|
||||||
this.$router.push(url);
|
router.push(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMounted(function() {
|
||||||
|
// 设置默认
|
||||||
|
function deep(e: any, i: number) {
|
||||||
|
switch (e.type) {
|
||||||
|
case 0:
|
||||||
|
e.children.forEach((e: any) => {
|
||||||
|
deep(e, i);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
if (route.path.includes(e.path)) {
|
||||||
|
index.value = String(i);
|
||||||
|
store.commit("SET_MENU_LIST", i);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
list.value.forEach((e: any, i: number) => {
|
||||||
|
deep(e, i);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
index,
|
||||||
|
list,
|
||||||
|
onSelect
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.cl-menu-topbar {
|
.app-topbar-menu {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
|
|
||||||
/deep/.el-menu {
|
:deep(.el-menu) {
|
||||||
height: 50px;
|
height: 50px;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border-bottom: 0;
|
border-bottom: 0;
|
||||||
@ -101,7 +111,7 @@ export default {
|
|||||||
color: $color-primary;
|
color: $color-primary;
|
||||||
}
|
}
|
||||||
|
|
||||||
/deep/.icon-svg {
|
:deep(.icon-svg) {
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,104 +1,129 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="cl-menu-tree">
|
<div class="cl-menu-tree">
|
||||||
<el-popover
|
<el-popover
|
||||||
ref="popover"
|
|
||||||
placement="bottom-start"
|
placement="bottom-start"
|
||||||
trigger="click"
|
trigger="click"
|
||||||
|
width="500px"
|
||||||
popper-class="popper-menu-tree"
|
popper-class="popper-menu-tree"
|
||||||
>
|
>
|
||||||
<el-input size="small" v-model="filterValue">
|
<el-input size="small" v-model="keyword">
|
||||||
<i slot="prefix" class="el-input__icon el-icon-search"></i>
|
<template #prefix>
|
||||||
|
<i class="el-input__icon el-icon-search"></i>
|
||||||
|
</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
|
|
||||||
<el-tree
|
<el-tree
|
||||||
ref="tree"
|
ref="treeRef"
|
||||||
node-key="menuId"
|
node-key="menuId"
|
||||||
:data="treeList"
|
:data="treeList"
|
||||||
:props="props"
|
:props="{
|
||||||
|
label: 'name',
|
||||||
|
children: 'children'
|
||||||
|
}"
|
||||||
:highlight-current="true"
|
:highlight-current="true"
|
||||||
:expand-on-click-node="false"
|
:expand-on-click-node="false"
|
||||||
:default-expanded-keys="expandedKeys"
|
:default-expanded-keys="expandedKeys"
|
||||||
:filter-node-method="filterNode"
|
:filter-node-method="filterNode"
|
||||||
@current-change="currentChange"
|
@current-change="onCurrentChange"
|
||||||
>
|
>
|
||||||
</el-tree>
|
</el-tree>
|
||||||
|
|
||||||
|
<template #reference>
|
||||||
|
<el-input v-model="name" readonly placeholder="请选择"></el-input>
|
||||||
|
</template>
|
||||||
</el-popover>
|
</el-popover>
|
||||||
<el-input v-model="name" v-popover:popover readonly placeholder="请选择"></el-input>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { deepTree } from "cl-admin/utils";
|
import { computed, defineComponent, inject, onMounted, ref, watch } from "vue";
|
||||||
|
import { deepTree } from "@/core/utils";
|
||||||
|
|
||||||
export default {
|
export default defineComponent({
|
||||||
name: "cl-menu-tree",
|
name: "cl-menu-tree",
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
value: [Number, String]
|
modelValue: [Number, String]
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
emits: ["update:modelValue"],
|
||||||
return {
|
|
||||||
filterValue: "",
|
|
||||||
list: [],
|
|
||||||
props: {
|
|
||||||
label: "name",
|
|
||||||
children: "children"
|
|
||||||
},
|
|
||||||
expandedKeys: []
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
setup(props, { emit }) {
|
||||||
filterValue(val) {
|
// 请求服务
|
||||||
this.$refs.tree.filter(val);
|
const $service = inject<any>("$service");
|
||||||
|
|
||||||
|
// 关键字
|
||||||
|
const keyword = ref<string>("");
|
||||||
|
|
||||||
|
// 树形列表
|
||||||
|
const list = ref<any[]>([]);
|
||||||
|
|
||||||
|
// 展开值
|
||||||
|
const expandedKeys = ref<any[]>([]);
|
||||||
|
|
||||||
|
// el-tree 组件
|
||||||
|
const treeRef = ref<any>({});
|
||||||
|
|
||||||
|
// 绑定值回调
|
||||||
|
function onCurrentChange({ id }: any) {
|
||||||
|
emit("update:modelValue", id);
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
// 刷新列表
|
||||||
name() {
|
function refresh() {
|
||||||
const item = this.list.find(e => e.id == this.value);
|
$service.system.menu.list().then((res: any) => {
|
||||||
return item ? item.name : "一级菜单";
|
const _list = res.filter((e: any) => e.type != 2);
|
||||||
},
|
|
||||||
|
|
||||||
treeList() {
|
_list.unshift({
|
||||||
return deepTree(this.list);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
this.menuList();
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
currentChange({ id }) {
|
|
||||||
this.$emit("input", id);
|
|
||||||
},
|
|
||||||
|
|
||||||
menuList() {
|
|
||||||
this.$service.system.menu.list().then(res => {
|
|
||||||
let list = res.filter(e => e.type != 2);
|
|
||||||
|
|
||||||
list.unshift({
|
|
||||||
name: "一级菜单",
|
name: "一级菜单",
|
||||||
id: null
|
id: null
|
||||||
});
|
});
|
||||||
|
|
||||||
this.list = list;
|
list.value = _list;
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
filterNode(value, data) {
|
// 过滤节点
|
||||||
|
function filterNode(value: string, data: any) {
|
||||||
if (!value) return true;
|
if (!value) return true;
|
||||||
return data.name.indexOf(value) !== -1;
|
return data.name.indexOf(value) !== -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 节点名称
|
||||||
|
const name = computed(() => {
|
||||||
|
const item = list.value.find(e => e.id == props.modelValue);
|
||||||
|
return item ? item.name : "一级菜单";
|
||||||
|
});
|
||||||
|
|
||||||
|
// 树形列表
|
||||||
|
const treeList = computed(() => deepTree(list.value));
|
||||||
|
|
||||||
|
// 监听关键字过滤
|
||||||
|
watch(keyword, (val: string) => {
|
||||||
|
treeRef.value.filter(val);
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(function() {
|
||||||
|
refresh();
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
keyword,
|
||||||
|
list,
|
||||||
|
expandedKeys,
|
||||||
|
treeRef,
|
||||||
|
name,
|
||||||
|
treeList,
|
||||||
|
refresh,
|
||||||
|
filterNode,
|
||||||
|
onCurrentChange
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.popper-menu-tree {
|
.popper-menu-tree {
|
||||||
width: 480px;
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
||||||
.el-input {
|
.el-input {
|
||||||
|
|||||||
@ -4,20 +4,19 @@
|
|||||||
<i class="el-icon-arrow-left"></i>
|
<i class="el-icon-arrow-left"></i>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="app-process__scroller" ref="scroller">
|
<div class="app-process__scroller" :ref="setRefs('scroller')">
|
||||||
<div
|
<div
|
||||||
class="app-process__item"
|
class="app-process__item"
|
||||||
v-for="(item, index) in processList"
|
v-for="(item, index) in list"
|
||||||
:key="index"
|
:key="index"
|
||||||
:ref="`item-${index}`"
|
:ref="setRefs(`item-${index}`)"
|
||||||
:class="{ active: item.active }"
|
:class="{ active: item.active }"
|
||||||
:data-index="index"
|
:data-index="index"
|
||||||
@click="onTap(item, index)"
|
@click="onTap(item)"
|
||||||
@contextmenu.stop.prevent="openCM($event, item)"
|
@contextmenu.stop.prevent="openCM($event, item)"
|
||||||
>
|
>
|
||||||
<span>{{ item.label }}</span>
|
<span>{{ item.label }}</span>
|
||||||
|
<i class="el-icon-close" v-if="index > 0" @mousedown.stop="onDel(index)"></i>
|
||||||
<i class="el-icon-close" v-if="index > 0" @click.stop="onDel(index)"></i>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -27,100 +26,136 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { mapGetters, mapMutations } from "vuex";
|
import { computed, reactive, watch } from "vue";
|
||||||
import { ContextMenu } from "cl-admin-crud";
|
import { useStore } from "vuex";
|
||||||
import { last } from "cl-admin/utils";
|
import { useRoute, useRouter } from "vue-router";
|
||||||
|
import { last } from "@/core/utils";
|
||||||
|
import { useRefs } from "@/core";
|
||||||
|
import { ContextMenu } from "@/crud";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "cl-process",
|
name: "cl-process",
|
||||||
|
|
||||||
computed: {
|
setup() {
|
||||||
...mapGetters(["processList"])
|
const router = useRouter();
|
||||||
},
|
const route = useRoute();
|
||||||
|
const store = useStore();
|
||||||
|
const { refs, setRefs } = useRefs();
|
||||||
|
|
||||||
watch: {
|
// 参数配置
|
||||||
"$route.path"(val) {
|
const menu = reactive<any>({
|
||||||
this.adScroll(this.processList.findIndex(e => e.value === val) || 0);
|
current: {}
|
||||||
}
|
});
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
// 数据列表
|
||||||
...mapMutations(["ADD_PROCESS", "DEL_PROCESS", "SET_PROCESS"]),
|
const list = computed(() => store.getters.processList);
|
||||||
|
|
||||||
onTap(item, index) {
|
// 跳转
|
||||||
this.adScroll(index);
|
function toPath() {
|
||||||
this.$router.push(item.value);
|
const active = list.value.find((e: any) => e.active);
|
||||||
},
|
|
||||||
|
|
||||||
onDel(index) {
|
|
||||||
this.DEL_PROCESS(index);
|
|
||||||
this.toPath();
|
|
||||||
},
|
|
||||||
|
|
||||||
openCM(e, item) {
|
|
||||||
ContextMenu.open(e, {
|
|
||||||
list: [
|
|
||||||
{
|
|
||||||
label: "关闭当前",
|
|
||||||
hidden: this.$route.path !== item.value,
|
|
||||||
callback: (_, done) => {
|
|
||||||
this.onDel(this.processList.findIndex(e => e.value == item.value));
|
|
||||||
done();
|
|
||||||
this.toPath();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "关闭其他",
|
|
||||||
callback: (_, done) => {
|
|
||||||
this.SET_PROCESS(
|
|
||||||
this.processList.filter(
|
|
||||||
e => e.value == item.value || e.value == "/"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
done();
|
|
||||||
this.toPath();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "关闭所有",
|
|
||||||
callback: (_, done) => {
|
|
||||||
this.SET_PROCESS(this.processList.filter(e => e.value == "/"));
|
|
||||||
done();
|
|
||||||
this.toPath();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
toPath() {
|
|
||||||
const active = this.processList.find(e => e.active);
|
|
||||||
|
|
||||||
if (!active) {
|
if (!active) {
|
||||||
const next = last(this.processList);
|
const next = last(list.value);
|
||||||
this.$router.push(next ? next.value : "/");
|
router.push(next ? next.value : "/");
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
adScroll(index) {
|
// 移动到
|
||||||
const el = this.$refs[`item-${index}`][0];
|
function scrollTo(left: number) {
|
||||||
|
refs.value.scroller.scrollTo({
|
||||||
if (el) {
|
|
||||||
this.scrollTo(el.offsetLeft + el.clientWidth - this.$refs["scroller"].clientWidth);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
toScroll(f) {
|
|
||||||
this.scrollTo(this.$refs["scroller"].scrollLeft + (f ? -100 : 100));
|
|
||||||
},
|
|
||||||
|
|
||||||
scrollTo(left) {
|
|
||||||
this.$refs["scroller"].scrollTo({
|
|
||||||
left,
|
left,
|
||||||
behavior: "smooth"
|
behavior: "smooth"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 左右移动
|
||||||
|
function toScroll(f: boolean) {
|
||||||
|
scrollTo(refs.value.scroller.scrollLeft + (f ? -100 : 100));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调整滚动位置
|
||||||
|
function adScroll(index: number) {
|
||||||
|
const el = refs.value[`item-${index}`];
|
||||||
|
|
||||||
|
if (el) {
|
||||||
|
scrollTo(el.offsetLeft + el.clientWidth - refs.value.scroller.clientWidth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选择
|
||||||
|
function onTap(item: any, index: number) {
|
||||||
|
adScroll(index);
|
||||||
|
router.push(item.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除
|
||||||
|
function onDel(index: number) {
|
||||||
|
store.commit("DEL_PROCESS", index);
|
||||||
|
toPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 右键菜单
|
||||||
|
function openCM(e: any, item: any) {
|
||||||
|
ContextMenu.open(e, {
|
||||||
|
list: [
|
||||||
|
{
|
||||||
|
label: "关闭当前",
|
||||||
|
hidden: item.value !== route.path,
|
||||||
|
callback: (_: any, done: Function) => {
|
||||||
|
onDel(list.value.findIndex((e: any) => e.value == item.value));
|
||||||
|
done();
|
||||||
|
toPath();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "关闭其他",
|
||||||
|
callback: (_: any, done: Function) => {
|
||||||
|
store.commit(
|
||||||
|
"SET_PROCESS",
|
||||||
|
list.value.filter(
|
||||||
|
(e: any) => e.value == item.value || e.value == "/"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
done();
|
||||||
|
toPath();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "关闭所有",
|
||||||
|
callback: (_: any, done: Function) => {
|
||||||
|
store.commit(
|
||||||
|
"SET_PROCESS",
|
||||||
|
list.value.filter((e: any) => e.value == "/")
|
||||||
|
);
|
||||||
|
done();
|
||||||
|
toPath();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => route.path,
|
||||||
|
function(val) {
|
||||||
|
adScroll(list.value.findIndex((e: any) => e.value === val) || 0);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
refs,
|
||||||
|
setRefs,
|
||||||
|
menu,
|
||||||
|
list,
|
||||||
|
onTap,
|
||||||
|
onDel,
|
||||||
|
toPath,
|
||||||
|
toScroll,
|
||||||
|
adScroll,
|
||||||
|
scrollTo,
|
||||||
|
openCM
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -6,14 +6,17 @@
|
|||||||
|
|
||||||
<div class="scroller">
|
<div class="scroller">
|
||||||
<el-tree
|
<el-tree
|
||||||
:data="list"
|
ref="treeRef"
|
||||||
:props="props"
|
|
||||||
:default-checked-keys="checked"
|
|
||||||
:filter-node-method="filterNode"
|
|
||||||
highlight-current
|
highlight-current
|
||||||
node-key="id"
|
node-key="id"
|
||||||
show-checkbox
|
show-checkbox
|
||||||
ref="tree"
|
:data="list"
|
||||||
|
:props="{
|
||||||
|
label: 'name',
|
||||||
|
children: 'children'
|
||||||
|
}"
|
||||||
|
:default-checked-keys="checked"
|
||||||
|
:filter-node-method="filterNode"
|
||||||
@check-change="save"
|
@check-change="save"
|
||||||
>
|
>
|
||||||
</el-tree>
|
</el-tree>
|
||||||
@ -21,54 +24,50 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { deepTree } from "cl-admin/utils";
|
import { defineComponent, inject, onMounted, ref, watch } from "vue";
|
||||||
|
import { ElMessage } from "element-plus";
|
||||||
|
import { deepTree } from "@/core/utils";
|
||||||
|
|
||||||
export default {
|
export default defineComponent({
|
||||||
name: "cl-role-perms",
|
name: "cl-role-perms",
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
value: Array,
|
modelValue: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
title: String
|
title: String
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
setup(props, { emit }) {
|
||||||
return {
|
const $service = inject<any>("$service");
|
||||||
list: [],
|
|
||||||
checked: [],
|
|
||||||
keyword: "",
|
|
||||||
props: {
|
|
||||||
label: "name",
|
|
||||||
children: "children"
|
|
||||||
},
|
|
||||||
loading: false
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
// 树形列表
|
||||||
keyword(val) {
|
const list = ref<any[]>([]);
|
||||||
this.$refs["tree"].filter(val);
|
|
||||||
},
|
|
||||||
|
|
||||||
value(val) {
|
// 已选列表
|
||||||
this.refreshTree(val);
|
const checked = ref<any[]>([]);
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
// 搜索关键字
|
||||||
this.refresh();
|
const keyword = ref<string>("");
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
// 加载中
|
||||||
refreshTree(val) {
|
const loading = ref<boolean>(false);
|
||||||
|
|
||||||
|
// el-tree 组件
|
||||||
|
const treeRef = ref<any>({});
|
||||||
|
|
||||||
|
// 刷新树
|
||||||
|
function refreshTree(val: any[]) {
|
||||||
if (!val) {
|
if (!val) {
|
||||||
this.checked = [];
|
checked.value = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
let ids = [];
|
const ids: any[] = [];
|
||||||
|
|
||||||
// 处理半选状态
|
// 处理半选状态
|
||||||
let fn = list => {
|
const fn = (list: any[]) => {
|
||||||
list.forEach(e => {
|
list.forEach(e => {
|
||||||
if (e.children) {
|
if (e.children) {
|
||||||
fn(e.children);
|
fn(e.children);
|
||||||
@ -78,41 +77,63 @@ export default {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
fn(this.list);
|
fn(list.value);
|
||||||
|
|
||||||
this.checked = ids.filter(id => (val || []).find(e => e == id));
|
checked.value = ids.filter(id => (val || []).includes(id));
|
||||||
},
|
}
|
||||||
|
|
||||||
refresh() {
|
// 刷新列表
|
||||||
this.$service.system.menu
|
function refresh() {
|
||||||
|
$service.system.menu
|
||||||
.list()
|
.list()
|
||||||
.then(res => {
|
.then((res: any[]) => {
|
||||||
this.list = deepTree(res);
|
list.value = deepTree(res);
|
||||||
|
refreshTree(props.modelValue);
|
||||||
this.refreshTree(this.value);
|
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch((err: string) => {
|
||||||
this.$message.error(err);
|
ElMessage.error(err);
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
filterNode(val, data) {
|
// 过滤节点
|
||||||
|
function filterNode(val: string, data: any) {
|
||||||
if (!val) return true;
|
if (!val) return true;
|
||||||
return data.name.includes(val);
|
return data.name.includes(val);
|
||||||
},
|
|
||||||
|
|
||||||
save() {
|
|
||||||
const tree = this.$refs["tree"];
|
|
||||||
|
|
||||||
// 选中的节点
|
|
||||||
const checked = tree.getCheckedKeys();
|
|
||||||
// 半选中的节点
|
|
||||||
const halfChecked = tree.getHalfCheckedKeys();
|
|
||||||
|
|
||||||
this.$emit("input", [...checked, ...halfChecked]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 保存
|
||||||
|
function save() {
|
||||||
|
// 选中的节点
|
||||||
|
const checked = treeRef.value.getCheckedKeys();
|
||||||
|
// 半选中的节点
|
||||||
|
const halfChecked = treeRef.value.getHalfCheckedKeys();
|
||||||
|
|
||||||
|
emit("update:modelValue", [...checked, ...halfChecked]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 过滤监听
|
||||||
|
watch(keyword, (val: string) => {
|
||||||
|
treeRef.value.filter(val);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 刷新监听
|
||||||
|
watch(() => props.modelValue, refreshTree);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
refresh();
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
list,
|
||||||
|
checked,
|
||||||
|
keyword,
|
||||||
|
loading,
|
||||||
|
treeRef,
|
||||||
|
filterNode,
|
||||||
|
save
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-select v-model="newValue" v-bind="props" multiple @change="onChange">
|
<el-select v-model="value" v-bind="props" multiple @change="onChange">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="(item, index) in list"
|
v-for="(item, index) in list"
|
||||||
:value="item.id"
|
:value="item.id"
|
||||||
@ -9,47 +9,55 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts">
|
||||||
export default {
|
import { defineComponent, inject, onMounted, ref, watch } from "vue";
|
||||||
|
import { isArray } from "@/core/utils";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
name: "cl-role-select",
|
name: "cl-role-select",
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
value: [String, Number, Array],
|
modelValue: [String, Number, Array],
|
||||||
props: Object
|
props: Object
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
emits: ["update:modelValue"],
|
||||||
return {
|
|
||||||
list: [],
|
|
||||||
newValue: undefined
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
setup(props, { emit }) {
|
||||||
value: {
|
// 请求服务
|
||||||
immediate: true,
|
const $service = inject<any>("$service");
|
||||||
handler(val) {
|
|
||||||
let arr = [];
|
|
||||||
|
|
||||||
if (!(val instanceof Array)) {
|
// 数据列表
|
||||||
arr = [val];
|
const list = ref<any[]>([]);
|
||||||
} else {
|
|
||||||
arr = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.newValue = arr.filter(Boolean);
|
// 绑定值
|
||||||
|
const value = ref<any>();
|
||||||
|
|
||||||
|
// 绑定值回调
|
||||||
|
function onChange(val: any) {
|
||||||
|
emit("update:modelValue", val);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析值
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
(val: any) => {
|
||||||
|
value.value = (isArray(val) ? val : [val]).filter(Boolean);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true
|
||||||
}
|
}
|
||||||
}
|
);
|
||||||
},
|
|
||||||
|
|
||||||
async created() {
|
onMounted(async () => {
|
||||||
this.list = await this.$service.system.role.list();
|
list.value = await $service.system.role.list();
|
||||||
},
|
});
|
||||||
|
|
||||||
methods: {
|
return {
|
||||||
onChange(val) {
|
list,
|
||||||
this.$emit("input", val);
|
value,
|
||||||
}
|
onChange
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -15,24 +15,28 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { mapGetters } from "vuex";
|
import { computed, defineComponent, ref, watch } from "vue";
|
||||||
|
import { useStore } from "vuex";
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
|
import { isEmpty } from "@/core/utils";
|
||||||
|
|
||||||
export default {
|
export default defineComponent({
|
||||||
name: "cl-route-nav",
|
name: "cl-route-nav",
|
||||||
|
|
||||||
data() {
|
setup() {
|
||||||
return {
|
const route = useRoute();
|
||||||
list: []
|
const store = useStore();
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
// 数据列表
|
||||||
$route: {
|
const list = ref<any[]>([]);
|
||||||
immediate: true,
|
|
||||||
handler(route) {
|
// 监听路由变化
|
||||||
const deep = item => {
|
watch(
|
||||||
|
() => route,
|
||||||
|
(val: any) => {
|
||||||
|
const deep = (item: any) => {
|
||||||
if (route.path === "/") {
|
if (route.path === "/") {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -54,34 +58,42 @@ export default {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.list = _(this.menuGroup)
|
list.value = _(store.getters.menuGroup)
|
||||||
.map(deep)
|
.map(deep)
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.flattenDeep()
|
.flattenDeep()
|
||||||
.value();
|
.value();
|
||||||
|
|
||||||
if (this.list.length === 0) {
|
if (isEmpty(list.value)) {
|
||||||
this.list.push(route);
|
list.value.push(val);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true
|
||||||
}
|
}
|
||||||
}
|
);
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
// 最后一个节点名称
|
||||||
...mapGetters(["menuGroup", "browser"]),
|
const lastName = computed(() => _.last(list.value).name);
|
||||||
|
|
||||||
lastName() {
|
const browser = computed(() => store.getters.browser);
|
||||||
return _.last(this.list).name;
|
|
||||||
}
|
return {
|
||||||
|
list,
|
||||||
|
lastName,
|
||||||
|
browser
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.cl-route-nav {
|
.cl-route-nav {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
||||||
/deep/.el-breadcrumb {
|
:deep(.el-breadcrumb) {
|
||||||
|
margin: 0 10px;
|
||||||
|
|
||||||
&__inner {
|
&__inner {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
@ -91,7 +103,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
font-size: 14px;
|
font-size: 15px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,12 +19,11 @@
|
|||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { getBrowser } from "cl-admin/utils";
|
import { computed, defineComponent } from "vue";
|
||||||
|
import { getBrowser } from "@/core/utils";
|
||||||
|
|
||||||
const { plat } = getBrowser();
|
export default defineComponent({
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "cl-scrollbar",
|
name: "cl-scrollbar",
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
@ -44,10 +43,16 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
setup() {
|
||||||
width() {
|
const { plat } = getBrowser();
|
||||||
|
|
||||||
|
const width = computed(() => {
|
||||||
return `calc(100% - ${plat == "iphone" ? "10px" : "0px"})`;
|
return `calc(100% - ${plat == "iphone" ? "10px" : "0px"})`;
|
||||||
}
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
width
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,29 +1,16 @@
|
|||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
|
|
||||||
function change(el, binding) {
|
function parse(value: any) {
|
||||||
el.style.display = checkPerm(binding.value) ? el.getAttribute("_display") : "none";
|
|
||||||
}
|
|
||||||
|
|
||||||
function parse(value) {
|
|
||||||
const permission = store.getters.permission;
|
const permission = store.getters.permission;
|
||||||
|
|
||||||
if (typeof value == "string") {
|
if (typeof value == "string") {
|
||||||
return value ? permission.some(e => e.includes(value.replace(/\s/g, ""))) : false;
|
return value ? permission.some((e: any) => e.includes(value.replace(/\s/g, ""))) : false;
|
||||||
} else {
|
} else {
|
||||||
return Boolean(value);
|
return Boolean(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
function checkPerm(value: any) {
|
||||||
inserted(el, binding) {
|
|
||||||
el.setAttribute("_display", el.style.display || "");
|
|
||||||
|
|
||||||
change(el, binding);
|
|
||||||
},
|
|
||||||
update: change
|
|
||||||
};
|
|
||||||
|
|
||||||
export const checkPerm = value => {
|
|
||||||
if (!value) {
|
if (!value) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -34,9 +21,24 @@ export const checkPerm = value => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (value.and) {
|
if (value.and) {
|
||||||
return value.and.some(e => !parse(e)) ? false : true;
|
return value.and.some((e: any) => !parse(e)) ? false : true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return parse(value);
|
return parse(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function change(el: any, binding: any) {
|
||||||
|
el.style.display = checkPerm(binding.value) ? el.getAttribute("_display") : "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
inserted(el: any, binding: any) {
|
||||||
|
el.setAttribute("_display", el.style.display || "");
|
||||||
|
|
||||||
|
change(el, binding);
|
||||||
|
},
|
||||||
|
update: change
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export { checkPerm };
|
||||||
@ -1,17 +0,0 @@
|
|||||||
export default {
|
|
||||||
default_avatar(url) {
|
|
||||||
if (!url) {
|
|
||||||
return require("../static/images/default-avatar.png");
|
|
||||||
}
|
|
||||||
|
|
||||||
return url;
|
|
||||||
},
|
|
||||||
|
|
||||||
default_name(name) {
|
|
||||||
if (!name) {
|
|
||||||
return "未命名";
|
|
||||||
}
|
|
||||||
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@ -1,5 +1,4 @@
|
|||||||
import components from "./components";
|
import components from "./components";
|
||||||
import filters from "./filters";
|
|
||||||
import pages from "./pages";
|
import pages from "./pages";
|
||||||
import views from "./views";
|
import views from "./views";
|
||||||
import store from "./store";
|
import store from "./store";
|
||||||
@ -9,4 +8,4 @@ import { iconList } from "./common";
|
|||||||
import "./static/css/index.scss";
|
import "./static/css/index.scss";
|
||||||
|
|
||||||
export { iconList, checkPerm };
|
export { iconList, checkPerm };
|
||||||
export default { components, filters, pages, views, store, service, directives };
|
export default { components, pages, views, store, service, directives };
|
||||||
@ -15,11 +15,11 @@
|
|||||||
<el-button round @click="navTo">跳转</el-button>
|
<el-button round @click="navTo">跳转</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="link">
|
<ul class="link">
|
||||||
<el-link class="to-home" @click="home">回到首页</el-link>
|
<li @click="home">回到首页</li>
|
||||||
<el-link class="to-back" @click="back">返回上一页</el-link>
|
<li @click="back">返回上一页</li>
|
||||||
<el-link class="to-login" @click="reLogin">重新登录</el-link>
|
<li @click="reLogin">重新登录</li>
|
||||||
</div>
|
</ul>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
@ -32,53 +32,65 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { mapGetters } from "vuex";
|
import { useStore } from "vuex";
|
||||||
import { href } from "cl-admin/utils";
|
import { computed, defineComponent, ref } from "vue";
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
|
import { href } from "@/core/utils";
|
||||||
|
|
||||||
export default {
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
code: Number,
|
code: Number,
|
||||||
desc: String
|
desc: String
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
setup() {
|
||||||
return {
|
const store = useStore();
|
||||||
url: "",
|
const router = useRouter();
|
||||||
isLogout: false
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
const url = ref<string>("");
|
||||||
...mapGetters(["routes", "token"])
|
const isLogout = ref<boolean>(false);
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
const routes = computed(() => store.getters.routes);
|
||||||
navTo() {
|
const token = computed(() => store.getters.token);
|
||||||
this.$router.push(this.url);
|
|
||||||
},
|
|
||||||
|
|
||||||
toLogin() {
|
function navTo() {
|
||||||
this.$router.push("/login");
|
router.push(url.value);
|
||||||
},
|
}
|
||||||
|
|
||||||
reLogin() {
|
function toLogin() {
|
||||||
this.isLogout = true;
|
router.push("/login");
|
||||||
|
}
|
||||||
|
|
||||||
this.$store.dispatch("userLogout").done(() => {
|
function reLogin() {
|
||||||
|
isLogout.value = true;
|
||||||
|
|
||||||
|
store.dispatch("userLogout").done(() => {
|
||||||
href("/login");
|
href("/login");
|
||||||
});
|
});
|
||||||
},
|
|
||||||
|
|
||||||
back() {
|
|
||||||
history.back();
|
|
||||||
},
|
|
||||||
|
|
||||||
home() {
|
|
||||||
this.$router.push("/");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function back() {
|
||||||
|
history.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
function home() {
|
||||||
|
router.push("/");
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
url,
|
||||||
|
isLogout,
|
||||||
|
routes,
|
||||||
|
token,
|
||||||
|
navTo,
|
||||||
|
toLogin,
|
||||||
|
reLogin,
|
||||||
|
back,
|
||||||
|
home
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@ -129,16 +141,19 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.link {
|
.link {
|
||||||
|
display: flex;
|
||||||
margin-top: 40px;
|
margin-top: 40px;
|
||||||
|
|
||||||
a {
|
li {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
transition: all 0.5s;
|
|
||||||
-webkit-transition: all 0.5s;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
margin: 0 15px;
|
margin: 0 20px;
|
||||||
padding-bottom: 2px;
|
list-style: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $color-primary;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,22 +0,0 @@
|
|||||||
export default [
|
|
||||||
{
|
|
||||||
path: "/403",
|
|
||||||
component: () => import("./error-page/403")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/404",
|
|
||||||
component: () => import("./error-page/404")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/500",
|
|
||||||
component: () => import("./error-page/500")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/502",
|
|
||||||
component: () => import("./error-page/502")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/login",
|
|
||||||
component: () => import("./login")
|
|
||||||
}
|
|
||||||
];
|
|
||||||
22
src/cool/modules/base/pages/index.ts
Normal file
22
src/cool/modules/base/pages/index.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
export default [
|
||||||
|
{
|
||||||
|
path: "/403",
|
||||||
|
component: () => import("./error-page/403.vue")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/404",
|
||||||
|
component: () => import("./error-page/404.vue")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/500",
|
||||||
|
component: () => import("./error-page/500.vue")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/502",
|
||||||
|
component: () => import("./error-page/502.vue")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/login",
|
||||||
|
component: () => import("./login/index.vue")
|
||||||
|
}
|
||||||
|
];
|
||||||
@ -1,55 +1,60 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="common-captcha" @click="refresh">
|
<div class="login-captcha" @click="refresh">
|
||||||
<div class="svg" v-html="svg" v-if="svg"></div>
|
<div class="svg" v-html="svg" v-if="svg"></div>
|
||||||
|
|
||||||
<img class="base64" :src="base64" alt="" v-else />
|
<img class="base64" :src="base64" alt="" v-else />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts">
|
||||||
export default {
|
import { defineComponent, inject, ref } from "vue";
|
||||||
data() {
|
import { ElMessage } from "element-plus";
|
||||||
return {
|
|
||||||
svg: "",
|
|
||||||
base64: ""
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
export default defineComponent({
|
||||||
this.refresh();
|
emits: ["update:modelValue", "change"],
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
setup(_, { emit }) {
|
||||||
refresh() {
|
const base64 = ref("");
|
||||||
this.$service.open
|
const svg = ref("");
|
||||||
|
const $service = inject<any>("$service");
|
||||||
|
|
||||||
|
const refresh = () => {
|
||||||
|
$service.open
|
||||||
.captcha({
|
.captcha({
|
||||||
height: 36,
|
height: 36,
|
||||||
width: 110
|
width: 110
|
||||||
})
|
})
|
||||||
.then(({ captchaId, data }) => {
|
.then(({ captchaId, data }: any) => {
|
||||||
if (data.includes(";base64,")) {
|
if (data.includes(";base64,")) {
|
||||||
this.base64 = data;
|
base64.value = data;
|
||||||
} else {
|
} else {
|
||||||
this.svg = data;
|
svg.value = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$emit("input", captchaId);
|
emit("update:modelValue", captchaId);
|
||||||
this.$emit("change", {
|
emit("change", {
|
||||||
base64: this.base64,
|
base64,
|
||||||
svg: this.svg,
|
svg,
|
||||||
captchaId
|
captchaId
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch((err: string) => {
|
||||||
this.$message.error(err);
|
ElMessage.error(err);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
|
refresh();
|
||||||
|
|
||||||
|
return {
|
||||||
|
base64,
|
||||||
|
svg,
|
||||||
|
refresh
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.common-captcha {
|
.login-captcha {
|
||||||
height: 36px;
|
height: 36px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
<img class="logo" src="../../static/images/logo.png" alt="" />
|
<img class="logo" src="../../static/images/logo.png" alt="" />
|
||||||
<p class="desc">COOL ADMIN是一款快速开发后台权限管理系统</p>
|
<p class="desc">COOL ADMIN是一款快速开发后台权限管理系统</p>
|
||||||
|
|
||||||
<el-form ref="form" class="form" size="medium" :disabled="saving">
|
<el-form class="form" size="medium" :disabled="saving">
|
||||||
<el-form-item label="用户名">
|
<el-form-item label="用户名">
|
||||||
<el-input
|
<el-input
|
||||||
placeholder="请输入用户名"
|
placeholder="请输入用户名"
|
||||||
@ -30,91 +30,105 @@
|
|||||||
maxlength="4"
|
maxlength="4"
|
||||||
v-model="form.verifyCode"
|
v-model="form.verifyCode"
|
||||||
auto-complete="off"
|
auto-complete="off"
|
||||||
@keyup.enter.native="next"
|
@keyup.enter="toLogin"
|
||||||
></el-input>
|
></el-input>
|
||||||
|
|
||||||
<captcha
|
<captcha
|
||||||
ref="captcha"
|
:ref="setRefs('captcha')"
|
||||||
class="value"
|
class="value"
|
||||||
v-model="form.captchaId"
|
v-model="form.captchaId"
|
||||||
@change="captchaChange"
|
@change="
|
||||||
|
() => {
|
||||||
|
form.verifyCode = '';
|
||||||
|
}
|
||||||
|
"
|
||||||
></captcha>
|
></captcha>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
<el-button round size="mini" class="submit-btn" @click="next" :loading="saving"
|
<el-button round size="mini" class="submit-btn" @click="toLogin" :loading="saving"
|
||||||
>登录</el-button
|
>登录</el-button
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts">
|
||||||
import Captcha from "./components/captcha";
|
import { defineComponent, reactive, ref } from "vue";
|
||||||
|
import { ElMessage } from "element-plus";
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
|
import { useStore } from "vuex";
|
||||||
|
import Captcha from "./components/captcha.vue";
|
||||||
|
import { useRefs } from "@/core";
|
||||||
|
|
||||||
export default {
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
Captcha
|
Captcha
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
setup() {
|
||||||
return {
|
const router = useRouter();
|
||||||
form: {
|
const store = useStore();
|
||||||
username: "admin",
|
const { refs, setRefs } = useRefs();
|
||||||
password: "123456",
|
|
||||||
captchaId: "",
|
|
||||||
verifyCode: ""
|
|
||||||
},
|
|
||||||
saving: false
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
const saving = ref<boolean>(false);
|
||||||
captchaChange() {
|
|
||||||
this.form.verifyCode = "";
|
|
||||||
},
|
|
||||||
|
|
||||||
async next() {
|
// 登录表单数据
|
||||||
const { username, password, verifyCode } = this.form;
|
const form = reactive({
|
||||||
|
username: "admin",
|
||||||
|
password: "123456",
|
||||||
|
captchaId: "",
|
||||||
|
verifyCode: ""
|
||||||
|
});
|
||||||
|
|
||||||
if (!username) {
|
// 登录
|
||||||
return this.$message.warning("用户名不能为空");
|
async function toLogin() {
|
||||||
|
if (!form.username) {
|
||||||
|
return ElMessage.warning("用户名不能为空");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!password) {
|
if (!form.password) {
|
||||||
return this.$message.warning("密码不能为空");
|
return ElMessage.warning("密码不能为空");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!verifyCode) {
|
if (!form.verifyCode) {
|
||||||
return this.$message.warning("图片验证码不能为空");
|
return ElMessage.warning("图片验证码不能为空");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.saving = true;
|
saving.value = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 登录
|
// 登录
|
||||||
await this.$store.dispatch("userLogin", this.form);
|
await store.dispatch("userLogin", form);
|
||||||
|
|
||||||
// 用户信息
|
// 用户信息
|
||||||
await this.$store.dispatch("userInfo");
|
await store.dispatch("userInfo");
|
||||||
|
|
||||||
// 权限菜单
|
// 权限菜单
|
||||||
let [first] = await this.$store.dispatch("permMenu");
|
const [first] = await store.dispatch("permMenu");
|
||||||
|
|
||||||
if (!first) {
|
if (!first) {
|
||||||
this.$message.error("该账号没有权限");
|
ElMessage.error("该账号没有权限");
|
||||||
} else {
|
} else {
|
||||||
this.$router.push("/");
|
router.push("/");
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.$message.error(err);
|
ElMessage.error(err);
|
||||||
this.$refs.captcha.refresh();
|
refs.value.captcha.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.saving = false;
|
saving.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
refs,
|
||||||
|
form,
|
||||||
|
saving,
|
||||||
|
toLogin,
|
||||||
|
setRefs
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@ -147,7 +161,7 @@ export default {
|
|||||||
letter-spacing: 1px;
|
letter-spacing: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/deep/.el-form {
|
:deep(.el-form) {
|
||||||
width: 300px;
|
width: 300px;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { BaseService, Service } from "cl-admin";
|
import { BaseService, Service } from "@/core";
|
||||||
|
|
||||||
@Service("base/comm")
|
@Service("base/comm")
|
||||||
class Common extends BaseService {
|
class Common extends BaseService {
|
||||||
@ -17,7 +17,7 @@ class Common extends BaseService {
|
|||||||
* @returns
|
* @returns
|
||||||
* @memberof CommonService
|
* @memberof CommonService
|
||||||
*/
|
*/
|
||||||
upload(params) {
|
upload(params: any) {
|
||||||
return this.request({
|
return this.request({
|
||||||
url: "/upload",
|
url: "/upload",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@ -54,7 +54,7 @@ class Common extends BaseService {
|
|||||||
* @returns
|
* @returns
|
||||||
* @memberof CommonService
|
* @memberof CommonService
|
||||||
*/
|
*/
|
||||||
userUpdate(params) {
|
userUpdate(params: any) {
|
||||||
return this.request({
|
return this.request({
|
||||||
url: "/personUpdate",
|
url: "/personUpdate",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@ -4,6 +4,7 @@ import SysUser from "./system/user";
|
|||||||
import SysMenu from "./system/menu";
|
import SysMenu from "./system/menu";
|
||||||
import SysRole from "./system/role";
|
import SysRole from "./system/role";
|
||||||
import SysDept from "./system/dept";
|
import SysDept from "./system/dept";
|
||||||
|
import SysTask from "./system/task";
|
||||||
import SysParam from "./system/param";
|
import SysParam from "./system/param";
|
||||||
import SysLog from "./system/log";
|
import SysLog from "./system/log";
|
||||||
import PluginInfo from "./plugin/info";
|
import PluginInfo from "./plugin/info";
|
||||||
@ -16,6 +17,7 @@ export default {
|
|||||||
menu: new SysMenu(),
|
menu: new SysMenu(),
|
||||||
role: new SysRole(),
|
role: new SysRole(),
|
||||||
dept: new SysDept(),
|
dept: new SysDept(),
|
||||||
|
task: new SysTask(),
|
||||||
param: new SysParam(),
|
param: new SysParam(),
|
||||||
log: new SysLog()
|
log: new SysLog()
|
||||||
},
|
},
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import { BaseService, Service } from "cl-admin";
|
import { BaseService, Service } from "@/core";
|
||||||
|
|
||||||
@Service("base/open")
|
@Service("base/open")
|
||||||
class Open extends BaseService {
|
class Open extends BaseService {
|
||||||
@ -9,7 +9,7 @@ class Open extends BaseService {
|
|||||||
* @returns
|
* @returns
|
||||||
* @memberof CommonService
|
* @memberof CommonService
|
||||||
*/
|
*/
|
||||||
userLogin({ username, password, captchaId, verifyCode }) {
|
userLogin({ username, password, captchaId, verifyCode }: any) {
|
||||||
return this.request({
|
return this.request({
|
||||||
url: "/login",
|
url: "/login",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@ -29,7 +29,7 @@ class Open extends BaseService {
|
|||||||
* @returns
|
* @returns
|
||||||
* @memberof CommonService
|
* @memberof CommonService
|
||||||
*/
|
*/
|
||||||
captcha({ height, width }) {
|
captcha({ height, width }: any) {
|
||||||
return this.request({
|
return this.request({
|
||||||
url: "/captcha",
|
url: "/captcha",
|
||||||
params: {
|
params: {
|
||||||
@ -43,7 +43,7 @@ class Open extends BaseService {
|
|||||||
* 刷新 token
|
* 刷新 token
|
||||||
* @param {string} token
|
* @param {string} token
|
||||||
*/
|
*/
|
||||||
refreshToken(token) {
|
refreshToken(token: string) {
|
||||||
return this.request({
|
return this.request({
|
||||||
url: "/refreshToken",
|
url: "/refreshToken",
|
||||||
params: {
|
params: {
|
||||||
@ -1,9 +1,9 @@
|
|||||||
import { BaseService, Service, Permission } from "cl-admin";
|
import { BaseService, Service, Permission } from "@/core";
|
||||||
|
|
||||||
@Service("base/plugin/info")
|
@Service("base/plugin/info")
|
||||||
class PluginInfo extends BaseService {
|
class PluginInfo extends BaseService {
|
||||||
@Permission("config")
|
@Permission("config")
|
||||||
config(data) {
|
config(data: any) {
|
||||||
return this.request({
|
return this.request({
|
||||||
url: "/config",
|
url: "/config",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@ -12,7 +12,7 @@ class PluginInfo extends BaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Permission("getConfig")
|
@Permission("getConfig")
|
||||||
getConfig(params) {
|
getConfig(params: any) {
|
||||||
return this.request({
|
return this.request({
|
||||||
url: "/getConfig",
|
url: "/getConfig",
|
||||||
params
|
params
|
||||||
@ -20,7 +20,7 @@ class PluginInfo extends BaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Permission("enable")
|
@Permission("enable")
|
||||||
enable(data) {
|
enable(data: any) {
|
||||||
return this.request({
|
return this.request({
|
||||||
url: "/enable",
|
url: "/enable",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@ -1,9 +1,9 @@
|
|||||||
import { BaseService, Service, Permission } from "cl-admin";
|
import { BaseService, Service, Permission } from "@/core";
|
||||||
|
|
||||||
@Service("base/sys/department")
|
@Service("base/sys/department")
|
||||||
class SysDepartment extends BaseService {
|
class SysDepartment extends BaseService {
|
||||||
@Permission("order")
|
@Permission("order")
|
||||||
order(data) {
|
order(data: any) {
|
||||||
return this.request({
|
return this.request({
|
||||||
url: "/order",
|
url: "/order",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import { BaseService, Service, Permission } from "cl-admin";
|
import { BaseService, Service, Permission } from "@/core";
|
||||||
|
|
||||||
@Service("base/sys/log")
|
@Service("base/sys/log")
|
||||||
class SysLog extends BaseService {
|
class SysLog extends BaseService {
|
||||||
@ -18,7 +18,7 @@ class SysLog extends BaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Permission("setKeep")
|
@Permission("setKeep")
|
||||||
setKeep(value) {
|
setKeep(value: any) {
|
||||||
return this.request({
|
return this.request({
|
||||||
url: "/setKeep",
|
url: "/setKeep",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import { BaseService, Service } from "cl-admin";
|
import { BaseService, Service } from "@/core";
|
||||||
|
|
||||||
@Service("base/sys/menu")
|
@Service("base/sys/menu")
|
||||||
class SysMenu extends BaseService {}
|
class SysMenu extends BaseService {}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import { BaseService, Service } from "cl-admin";
|
import { BaseService, Service } from "@/core";
|
||||||
|
|
||||||
@Service("base/sys/param")
|
@Service("base/sys/param")
|
||||||
class SysParam extends BaseService {}
|
class SysParam extends BaseService {}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import { BaseService, Service } from "cl-admin";
|
import { BaseService, Service } from "@/core";
|
||||||
|
|
||||||
@Service("base/sys/role")
|
@Service("base/sys/role")
|
||||||
class SysRole extends BaseService {}
|
class SysRole extends BaseService {}
|
||||||
41
src/cool/modules/base/service/system/task.ts
Normal file
41
src/cool/modules/base/service/system/task.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { BaseService, Service, Permission } from "@/core";
|
||||||
|
|
||||||
|
@Service("base/sys/task")
|
||||||
|
class SysTask extends BaseService {
|
||||||
|
@Permission("stop")
|
||||||
|
stop(data: any) {
|
||||||
|
return this.request({
|
||||||
|
url: "/stop",
|
||||||
|
method: "POST",
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Permission("start")
|
||||||
|
start(data: any) {
|
||||||
|
return this.request({
|
||||||
|
url: "/start",
|
||||||
|
method: "POST",
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Permission("once")
|
||||||
|
once(data: any) {
|
||||||
|
return this.request({
|
||||||
|
url: "/once",
|
||||||
|
method: "POST",
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Permission("log")
|
||||||
|
log(params: any) {
|
||||||
|
return this.request({
|
||||||
|
url: "/log",
|
||||||
|
params
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SysTask;
|
||||||
@ -1,9 +1,9 @@
|
|||||||
import { BaseService, Service, Permission } from "cl-admin";
|
import { BaseService, Service, Permission } from "@/core";
|
||||||
|
|
||||||
@Service("base/sys/user")
|
@Service("base/sys/user")
|
||||||
class SysUser extends BaseService {
|
class SysUser extends BaseService {
|
||||||
@Permission("move")
|
@Permission("move")
|
||||||
move(data) {
|
move(data: any) {
|
||||||
return this.request({
|
return this.request({
|
||||||
url: "/move",
|
url: "/move",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@ -1,50 +0,0 @@
|
|||||||
import { app } from "@/config/env";
|
|
||||||
import { deepMerge, getBrowser } from "cl-admin/utils";
|
|
||||||
import store from "store";
|
|
||||||
|
|
||||||
const browser = getBrowser();
|
|
||||||
|
|
||||||
export default {
|
|
||||||
state: {
|
|
||||||
info: {
|
|
||||||
...app
|
|
||||||
},
|
|
||||||
browser,
|
|
||||||
collapse: browser.isMini ? true : false
|
|
||||||
},
|
|
||||||
getters: {
|
|
||||||
// 应用配置
|
|
||||||
app: state => state.info,
|
|
||||||
// 浏览器信息
|
|
||||||
browser: state => state.browser,
|
|
||||||
// 左侧菜单是否收起
|
|
||||||
menuCollapse: state => state.collapse
|
|
||||||
},
|
|
||||||
actions: {
|
|
||||||
appLoad({ getters, dispatch }) {
|
|
||||||
if (getters.token) {
|
|
||||||
// 读取菜单权限
|
|
||||||
dispatch("permMenu");
|
|
||||||
// 获取用户信息
|
|
||||||
dispatch("userInfo");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mutations: {
|
|
||||||
// 设置浏览器信息
|
|
||||||
SET_BROWSER(state) {
|
|
||||||
state.browser = getBrowser();
|
|
||||||
},
|
|
||||||
|
|
||||||
// 收起左侧菜单
|
|
||||||
COLLAPSE_MENU(state, val = false) {
|
|
||||||
state.collapse = val;
|
|
||||||
},
|
|
||||||
|
|
||||||
// 更新应用配置
|
|
||||||
UPDATE_APP(state, val) {
|
|
||||||
deepMerge(state.info, val);
|
|
||||||
store.set("__app__", state.info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
58
src/cool/modules/base/store/app.ts
Normal file
58
src/cool/modules/base/store/app.ts
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import store from "store";
|
||||||
|
import { deepMerge, getBrowser } from "@/core/utils";
|
||||||
|
import { app } from "@/config/env";
|
||||||
|
|
||||||
|
const browser = getBrowser();
|
||||||
|
|
||||||
|
const state = {
|
||||||
|
info: {
|
||||||
|
...app
|
||||||
|
},
|
||||||
|
browser,
|
||||||
|
collapse: browser.isMini ? true : false
|
||||||
|
};
|
||||||
|
|
||||||
|
const getters = {
|
||||||
|
// 应用配置
|
||||||
|
app: (state: any) => state.info,
|
||||||
|
// 浏览器信息
|
||||||
|
browser: (state: any) => state.browser,
|
||||||
|
// 左侧菜单是否收起
|
||||||
|
menuCollapse: (state: any) => state.collapse
|
||||||
|
};
|
||||||
|
|
||||||
|
const actions = {
|
||||||
|
appLoad({ getters, dispatch }: any) {
|
||||||
|
if (getters.token) {
|
||||||
|
// 读取菜单权限
|
||||||
|
dispatch("permMenu");
|
||||||
|
// 获取用户信息
|
||||||
|
dispatch("userInfo");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const mutations = {
|
||||||
|
// 设置浏览器信息
|
||||||
|
SET_BROWSER(state: any) {
|
||||||
|
state.browser = getBrowser();
|
||||||
|
},
|
||||||
|
|
||||||
|
// 收起左侧菜单
|
||||||
|
COLLAPSE_MENU(state: any, val = false) {
|
||||||
|
state.collapse = val;
|
||||||
|
},
|
||||||
|
|
||||||
|
// 更新应用配置
|
||||||
|
UPDATE_APP(state: any, val: any) {
|
||||||
|
deepMerge(state.info, val);
|
||||||
|
store.set("__app__", state.info);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
state,
|
||||||
|
getters,
|
||||||
|
actions,
|
||||||
|
mutations
|
||||||
|
};
|
||||||
@ -1,143 +0,0 @@
|
|||||||
import { Message } from "element-ui";
|
|
||||||
import { deepTree, revDeepTree, isArray, isEmpty } from "cl-admin/utils";
|
|
||||||
import { revisePath } from "../utils";
|
|
||||||
import router from "@/router";
|
|
||||||
import { menuList } from "@/config/env";
|
|
||||||
import store from "store";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
state: {
|
|
||||||
// 视图路由,type=1
|
|
||||||
routes: store.get("viewRoutes") || [],
|
|
||||||
// 树形菜单
|
|
||||||
group: store.get("menuGroup") || [],
|
|
||||||
// showAMenu 模式下,顶级菜单的序号
|
|
||||||
index: 0,
|
|
||||||
// 左侧菜单
|
|
||||||
menu: [],
|
|
||||||
// 权限列表
|
|
||||||
permission: []
|
|
||||||
},
|
|
||||||
getters: {
|
|
||||||
// 树形菜单列表
|
|
||||||
menuGroup: state => state.group,
|
|
||||||
// 左侧菜单
|
|
||||||
menuList: state => state.menu,
|
|
||||||
// 视图路由
|
|
||||||
routes: state => state.routes,
|
|
||||||
// 权限列表
|
|
||||||
permission: state => state.permission
|
|
||||||
},
|
|
||||||
actions: {
|
|
||||||
// 设置菜单、权限
|
|
||||||
permMenu({ commit, state, getters }) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const next = res => {
|
|
||||||
if (!isArray(res.menus)) {
|
|
||||||
res.menus = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isArray(res.perms)) {
|
|
||||||
res.perms = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const routes = res.menus
|
|
||||||
.filter(e => e.type != 2)
|
|
||||||
.map(e => {
|
|
||||||
return {
|
|
||||||
moduleName: e.moduleName,
|
|
||||||
id: e.id,
|
|
||||||
parentId: e.parentId,
|
|
||||||
path: revisePath(e.router || e.id),
|
|
||||||
viewPath: e.viewPath,
|
|
||||||
type: e.type,
|
|
||||||
name: e.name,
|
|
||||||
icon: e.icon,
|
|
||||||
orderNum: e.orderNum,
|
|
||||||
isShow: isEmpty(e.isShow) ? true : e.isShow,
|
|
||||||
meta: {
|
|
||||||
label: e.name,
|
|
||||||
keepAlive: e.keepAlive
|
|
||||||
},
|
|
||||||
children: []
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
// 转成树形菜单
|
|
||||||
const menuGroup = deepTree(routes);
|
|
||||||
|
|
||||||
// 设置权限
|
|
||||||
commit("SET_PERMIESSION", res.perms);
|
|
||||||
// 设置菜单组
|
|
||||||
commit("SET_MENU_GROUP", menuGroup);
|
|
||||||
// 设置视图路由
|
|
||||||
commit(
|
|
||||||
"SET_VIEW_ROUTES",
|
|
||||||
routes.filter(e => e.type == 1)
|
|
||||||
);
|
|
||||||
// 设置菜单
|
|
||||||
commit("SET_MENU_LIST", state.index);
|
|
||||||
|
|
||||||
resolve(menuGroup);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 监测自定义菜单
|
|
||||||
if (!getters.app.conf.customMenu) {
|
|
||||||
this.$service.common
|
|
||||||
.permMenu()
|
|
||||||
.then(res => {
|
|
||||||
next(res);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
Message.error("菜单加载异常");
|
|
||||||
console.error(err);
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
next({
|
|
||||||
menus: revDeepTree(menuList)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mutations: {
|
|
||||||
// 设置树形菜单列表
|
|
||||||
SET_MENU_GROUP(state, list) {
|
|
||||||
state.group = list;
|
|
||||||
store.set("menuGroup", list);
|
|
||||||
},
|
|
||||||
|
|
||||||
// 设置视图路由
|
|
||||||
SET_VIEW_ROUTES(state, list) {
|
|
||||||
router.$plugin.addViews(list);
|
|
||||||
|
|
||||||
state.routes = list;
|
|
||||||
store.set("viewRoutes", list);
|
|
||||||
},
|
|
||||||
|
|
||||||
// 设置左侧菜单
|
|
||||||
SET_MENU_LIST(state, index) {
|
|
||||||
const { showAMenu } = this.getters.app.conf;
|
|
||||||
|
|
||||||
if (isEmpty(index)) {
|
|
||||||
index = state.index;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (showAMenu) {
|
|
||||||
const { children = [] } = state.group[index] || {};
|
|
||||||
|
|
||||||
state.index = index;
|
|
||||||
state.menu = children;
|
|
||||||
} else {
|
|
||||||
state.menu = state.group;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 设置权限
|
|
||||||
SET_PERMIESSION(state, list) {
|
|
||||||
state.permission = list;
|
|
||||||
store.set("permission", list);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
152
src/cool/modules/base/store/menu.ts
Normal file
152
src/cool/modules/base/store/menu.ts
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
import { ElMessage } from "element-plus";
|
||||||
|
import storage from "store";
|
||||||
|
import store from "@/store";
|
||||||
|
import router from "@/router";
|
||||||
|
import { deepTree, revDeepTree, isArray, isEmpty } from "@/core/utils";
|
||||||
|
import { menuList } from "@/config/env";
|
||||||
|
import { revisePath } from "../utils";
|
||||||
|
import { MenuItem } from "../types";
|
||||||
|
|
||||||
|
const state = {
|
||||||
|
// 视图路由,type=1
|
||||||
|
routes: storage.get("viewRoutes") || [],
|
||||||
|
// 树形菜单
|
||||||
|
group: storage.get("menuGroup") || [],
|
||||||
|
// showAMenu 模式下,顶级菜单的序号
|
||||||
|
index: 0,
|
||||||
|
// 左侧菜单
|
||||||
|
menu: [],
|
||||||
|
// 权限列表
|
||||||
|
permission: storage.get("permission") || []
|
||||||
|
};
|
||||||
|
|
||||||
|
const getters = {
|
||||||
|
// 树形菜单列表
|
||||||
|
menuGroup: (state: any) => state.group,
|
||||||
|
// 左侧菜单
|
||||||
|
menuList: (state: any) => state.menu,
|
||||||
|
// 视图路由
|
||||||
|
routes: (state: any) => state.routes,
|
||||||
|
// 权限列表
|
||||||
|
permission: (state: any) => state.permission
|
||||||
|
};
|
||||||
|
|
||||||
|
const actions = {
|
||||||
|
// 设置菜单、权限
|
||||||
|
permMenu({ commit, state, getters }: any) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const next = (res: any) => {
|
||||||
|
if (!isArray(res.menus)) {
|
||||||
|
res.menus = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isArray(res.perms)) {
|
||||||
|
res.perms = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const routes = res.menus
|
||||||
|
.filter((e: MenuItem) => e.type != 2)
|
||||||
|
.map((e: MenuItem) => {
|
||||||
|
return {
|
||||||
|
id: e.id,
|
||||||
|
parentId: e.parentId,
|
||||||
|
path: revisePath(e.router || String(e.id)),
|
||||||
|
viewPath: e.viewPath,
|
||||||
|
type: e.type,
|
||||||
|
name: e.name,
|
||||||
|
icon: e.icon,
|
||||||
|
orderNum: e.orderNum,
|
||||||
|
isShow: isEmpty(e.isShow) ? true : e.isShow,
|
||||||
|
meta: {
|
||||||
|
label: e.name,
|
||||||
|
keepAlive: e.keepAlive
|
||||||
|
},
|
||||||
|
children: []
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// 转成树形菜单
|
||||||
|
const menuGroup = deepTree(routes);
|
||||||
|
|
||||||
|
// 设置权限
|
||||||
|
commit("SET_PERMIESSION", res.perms);
|
||||||
|
// 设置菜单组
|
||||||
|
commit("SET_MENU_GROUP", menuGroup);
|
||||||
|
// 设置视图路由
|
||||||
|
commit(
|
||||||
|
"SET_VIEW_ROUTES",
|
||||||
|
routes.filter((e: MenuItem) => e.type == 1)
|
||||||
|
);
|
||||||
|
// 设置菜单
|
||||||
|
commit("SET_MENU_LIST", state.index);
|
||||||
|
|
||||||
|
resolve(menuGroup);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 监测自定义菜单
|
||||||
|
if (!getters.app.conf.customMenu) {
|
||||||
|
store.$service.common
|
||||||
|
.permMenu()
|
||||||
|
.then((res: any) => {
|
||||||
|
next(res);
|
||||||
|
})
|
||||||
|
.catch((err: string) => {
|
||||||
|
ElMessage.error("菜单加载异常");
|
||||||
|
console.error(err);
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
next({
|
||||||
|
menus: revDeepTree(menuList)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const mutations = {
|
||||||
|
// 设置树形菜单列表
|
||||||
|
SET_MENU_GROUP(state: any, list: MenuItem[]) {
|
||||||
|
state.group = list;
|
||||||
|
storage.set("menuGroup", list);
|
||||||
|
},
|
||||||
|
|
||||||
|
// 设置视图路由
|
||||||
|
SET_VIEW_ROUTES(state: any, list: MenuItem[]) {
|
||||||
|
router.$plugin.addViews(list);
|
||||||
|
|
||||||
|
state.routes = list;
|
||||||
|
storage.set("viewRoutes", list);
|
||||||
|
},
|
||||||
|
|
||||||
|
// 设置左侧菜单
|
||||||
|
SET_MENU_LIST(state: any, index: number) {
|
||||||
|
const { showAMenu } = store.getters.app.conf;
|
||||||
|
|
||||||
|
if (isEmpty(index)) {
|
||||||
|
index = state.index;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showAMenu) {
|
||||||
|
const { children = [] } = state.group[index] || {};
|
||||||
|
|
||||||
|
state.index = index;
|
||||||
|
state.menu = children;
|
||||||
|
} else {
|
||||||
|
state.menu = state.group;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 设置权限
|
||||||
|
SET_PERMIESSION(state: any, list: Array<any>) {
|
||||||
|
state.permission = list;
|
||||||
|
storage.set("permission", list);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
state,
|
||||||
|
getters,
|
||||||
|
actions,
|
||||||
|
mutations
|
||||||
|
};
|
||||||
@ -1,26 +0,0 @@
|
|||||||
export default {
|
|
||||||
state: {
|
|
||||||
info: {},
|
|
||||||
list: []
|
|
||||||
},
|
|
||||||
|
|
||||||
getters: {
|
|
||||||
// 模块信息
|
|
||||||
modules: state => state.info,
|
|
||||||
// 模块列表
|
|
||||||
moduleList: state => state.list
|
|
||||||
},
|
|
||||||
|
|
||||||
mutations: {
|
|
||||||
SET_MODULE(state, list) {
|
|
||||||
let d = {};
|
|
||||||
|
|
||||||
list.forEach(e => {
|
|
||||||
d[e.name] = e;
|
|
||||||
});
|
|
||||||
|
|
||||||
state.list = list;
|
|
||||||
state.info = d;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
30
src/cool/modules/base/store/module.ts
Normal file
30
src/cool/modules/base/store/module.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
const state = {
|
||||||
|
info: {},
|
||||||
|
list: []
|
||||||
|
};
|
||||||
|
|
||||||
|
const getters = {
|
||||||
|
// 模块信息
|
||||||
|
modules: (state: any) => state.info,
|
||||||
|
// 模块列表
|
||||||
|
moduleList: (state: any) => state.list
|
||||||
|
};
|
||||||
|
|
||||||
|
const mutations = {
|
||||||
|
SET_MODULE(state: any, list: Array<any>) {
|
||||||
|
const d: any = {};
|
||||||
|
|
||||||
|
list.forEach((e: any) => {
|
||||||
|
d[e.name] = e;
|
||||||
|
});
|
||||||
|
|
||||||
|
state.list = list;
|
||||||
|
state.info = d;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
state,
|
||||||
|
getters,
|
||||||
|
mutations
|
||||||
|
};
|
||||||
@ -1,57 +0,0 @@
|
|||||||
const fMenu = {
|
|
||||||
label: "首页",
|
|
||||||
value: "/",
|
|
||||||
active: true
|
|
||||||
};
|
|
||||||
|
|
||||||
export default {
|
|
||||||
state: {
|
|
||||||
list: [fMenu]
|
|
||||||
},
|
|
||||||
getters: {
|
|
||||||
// 页面进程列表
|
|
||||||
processList: state => state.list
|
|
||||||
},
|
|
||||||
mutations: {
|
|
||||||
ADD_PROCESS(state, item) {
|
|
||||||
const index = state.list.findIndex(
|
|
||||||
e => e.value.split("?")[0] === item.value.split("?")[0]
|
|
||||||
);
|
|
||||||
|
|
||||||
state.list.map(e => {
|
|
||||||
e.active = e.value == item.value;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (index < 0) {
|
|
||||||
if (item.value == "/") {
|
|
||||||
item.label = fMenu.label;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item.label) {
|
|
||||||
state.list.push({
|
|
||||||
...item,
|
|
||||||
active: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
state.list[index].active = true;
|
|
||||||
state.list[index].label = item.label;
|
|
||||||
state.list[index].value = item.value;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
DEL_PROCESS(state, index) {
|
|
||||||
if (index != 0) {
|
|
||||||
state.list.splice(index, 1);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
SET_PROCESS(state, list) {
|
|
||||||
state.list = list;
|
|
||||||
},
|
|
||||||
|
|
||||||
RESET_PROCESS(state) {
|
|
||||||
state.list = [fMenu];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
66
src/cool/modules/base/store/process.ts
Normal file
66
src/cool/modules/base/store/process.ts
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
const fMenu = {
|
||||||
|
label: "首页",
|
||||||
|
value: "/",
|
||||||
|
active: true
|
||||||
|
};
|
||||||
|
|
||||||
|
const state = {
|
||||||
|
list: [fMenu]
|
||||||
|
};
|
||||||
|
|
||||||
|
const getters = {
|
||||||
|
// 页面进程列表
|
||||||
|
processList: (state: any) => state.list
|
||||||
|
};
|
||||||
|
|
||||||
|
const actions = {};
|
||||||
|
|
||||||
|
const mutations = {
|
||||||
|
ADD_PROCESS(state: any, item: any) {
|
||||||
|
const index = state.list.findIndex(
|
||||||
|
(e: any) => e.value.split("?")[0] === item.value.split("?")[0]
|
||||||
|
);
|
||||||
|
|
||||||
|
state.list.map((e: any) => {
|
||||||
|
e.active = e.value == item.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (index < 0) {
|
||||||
|
if (item.value == "/") {
|
||||||
|
item.label = fMenu.label;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.label) {
|
||||||
|
state.list.push({
|
||||||
|
...item,
|
||||||
|
active: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
state.list[index].active = true;
|
||||||
|
state.list[index].label = item.label;
|
||||||
|
state.list[index].value = item.value;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
DEL_PROCESS(state: any, index: number) {
|
||||||
|
if (index != 0) {
|
||||||
|
state.list.splice(index, 1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
SET_PROCESS(state: any, list: Array<any>) {
|
||||||
|
state.list = list;
|
||||||
|
},
|
||||||
|
|
||||||
|
RESET_PROCESS(state: any) {
|
||||||
|
state.list = [fMenu];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
state,
|
||||||
|
getters,
|
||||||
|
actions,
|
||||||
|
mutations
|
||||||
|
};
|
||||||
@ -1,102 +0,0 @@
|
|||||||
import { storage, href } from "cl-admin/utils";
|
|
||||||
|
|
||||||
// 用户信息
|
|
||||||
let info = storage.get("userInfo") || {};
|
|
||||||
// 授权标识
|
|
||||||
let token = storage.get("token") || null;
|
|
||||||
|
|
||||||
export default {
|
|
||||||
state: {
|
|
||||||
token,
|
|
||||||
info
|
|
||||||
},
|
|
||||||
getters: {
|
|
||||||
userInfo: state => state.info,
|
|
||||||
token: state => state.token
|
|
||||||
},
|
|
||||||
actions: {
|
|
||||||
// 用户登录
|
|
||||||
userLogin({ commit }, form) {
|
|
||||||
return this.$service.open.userLogin(form).then(res => {
|
|
||||||
commit("SET_TOKEN", res);
|
|
||||||
return res;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// 用户退出
|
|
||||||
userLogout({ dispatch }) {
|
|
||||||
return new Promise(resolve => {
|
|
||||||
this.$service.common.userLogout().done(() => {
|
|
||||||
dispatch("userRemove").then(() => {
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// 用户信息
|
|
||||||
userInfo({ commit }) {
|
|
||||||
return this.$service.common.userInfo().then(res => {
|
|
||||||
commit("SET_USERINFO", res);
|
|
||||||
return res;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// 用户移除
|
|
||||||
userRemove({ commit }) {
|
|
||||||
commit("CLEAR_USER");
|
|
||||||
commit("CLEAR_TOKEN");
|
|
||||||
commit("RESET_PROCESS");
|
|
||||||
commit("SET_MENU_GROUP", []);
|
|
||||||
commit("SET_VIEW_ROUTES", []);
|
|
||||||
commit("SET_MENU_LIST", 0);
|
|
||||||
},
|
|
||||||
|
|
||||||
// 刷新token
|
|
||||||
refreshToken({ commit, dispatch }) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
this.$service.open
|
|
||||||
.refreshToken(storage.get("refreshToken"))
|
|
||||||
.then(res => {
|
|
||||||
commit("SET_TOKEN", res);
|
|
||||||
resolve(res.token);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
dispatch("userRemove");
|
|
||||||
href("/login");
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mutations: {
|
|
||||||
// 设置用户信息
|
|
||||||
SET_USERINFO(state, val) {
|
|
||||||
state.info = val;
|
|
||||||
storage.set("userInfo", val);
|
|
||||||
},
|
|
||||||
|
|
||||||
// 设置授权标识
|
|
||||||
SET_TOKEN(state, { token, expire, refreshToken, refreshExpire }) {
|
|
||||||
// 请求的唯一标识
|
|
||||||
state.token = token;
|
|
||||||
storage.set("token", token, expire);
|
|
||||||
|
|
||||||
// 刷新 token 的唯一标识
|
|
||||||
storage.set("refreshToken", refreshToken, refreshExpire);
|
|
||||||
},
|
|
||||||
|
|
||||||
// 移除授权标识
|
|
||||||
CLEAR_TOKEN(state) {
|
|
||||||
state.token = null;
|
|
||||||
storage.remove("token");
|
|
||||||
storage.remove("refreshToken");
|
|
||||||
},
|
|
||||||
|
|
||||||
// 移除用户信息
|
|
||||||
CLEAR_USER(state) {
|
|
||||||
state.info = {};
|
|
||||||
storage.remove("userInfo");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
109
src/cool/modules/base/store/user.ts
Normal file
109
src/cool/modules/base/store/user.ts
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
import { storage, href } from "@/core/utils";
|
||||||
|
import store from "@/store";
|
||||||
|
import { Token } from "../types";
|
||||||
|
|
||||||
|
const state: any = {
|
||||||
|
// 授权标识
|
||||||
|
token: storage.get("token") || null,
|
||||||
|
// 用户信息
|
||||||
|
info: storage.get("userInfo") || {}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getters = {
|
||||||
|
userInfo: (state: any) => state.info,
|
||||||
|
token: (state: any) => state.token
|
||||||
|
};
|
||||||
|
|
||||||
|
const actions = {
|
||||||
|
// 用户登录
|
||||||
|
userLogin({ commit }: any, form: any): Promise<any> {
|
||||||
|
return store.$service.open.userLogin(form).then((res: Token) => {
|
||||||
|
commit("SET_TOKEN", res);
|
||||||
|
return res;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// 用户退出
|
||||||
|
userLogout({ dispatch }: any): Promise<any> {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
store.$service.common.userLogout().done(() => {
|
||||||
|
dispatch("userRemove").then(() => {
|
||||||
|
resolve(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// 用户信息
|
||||||
|
userInfo({ commit }: any): Promise<any> {
|
||||||
|
return store.$service.common.userInfo().then((res: any) => {
|
||||||
|
commit("SET_USERINFO", res);
|
||||||
|
return res;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// 用户移除
|
||||||
|
userRemove({ commit }: any) {
|
||||||
|
commit("CLEAR_USER");
|
||||||
|
commit("CLEAR_TOKEN");
|
||||||
|
commit("RESET_PROCESS");
|
||||||
|
commit("SET_MENU_GROUP", []);
|
||||||
|
commit("SET_VIEW_ROUTES", []);
|
||||||
|
commit("SET_MENU_LIST", 0);
|
||||||
|
},
|
||||||
|
|
||||||
|
// 刷新token
|
||||||
|
refreshToken({ commit, dispatch }: any) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
store.$service.open
|
||||||
|
.refreshToken(storage.get("refreshToken"))
|
||||||
|
.then((res: any) => {
|
||||||
|
commit("SET_TOKEN", res);
|
||||||
|
resolve(res.token);
|
||||||
|
})
|
||||||
|
.catch((err: Error) => {
|
||||||
|
dispatch("userRemove");
|
||||||
|
href("/login");
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const mutations = {
|
||||||
|
// 设置用户信息
|
||||||
|
SET_USERINFO(state: any, val: any) {
|
||||||
|
state.info = val;
|
||||||
|
storage.set("userInfo", val);
|
||||||
|
},
|
||||||
|
|
||||||
|
// 设置授权标识
|
||||||
|
SET_TOKEN(state: any, { token, expire, refreshToken, refreshExpire }: Token) {
|
||||||
|
// 请求的唯一标识
|
||||||
|
state.token = token;
|
||||||
|
storage.set("token", token, expire);
|
||||||
|
|
||||||
|
// 刷新 token 的唯一标识
|
||||||
|
storage.set("refreshToken", refreshToken, refreshExpire);
|
||||||
|
},
|
||||||
|
|
||||||
|
// 移除授权标识
|
||||||
|
CLEAR_TOKEN(state: any) {
|
||||||
|
state.token = null;
|
||||||
|
storage.remove("token");
|
||||||
|
storage.remove("refreshToken");
|
||||||
|
},
|
||||||
|
|
||||||
|
// 移除用户信息
|
||||||
|
CLEAR_USER(state: any) {
|
||||||
|
state.info = {};
|
||||||
|
storage.remove("userInfo");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
state,
|
||||||
|
getters,
|
||||||
|
actions,
|
||||||
|
mutations
|
||||||
|
};
|
||||||
31
src/cool/modules/base/types/index.d.ts
vendored
Normal file
31
src/cool/modules/base/types/index.d.ts
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
export interface Token {
|
||||||
|
expire: number;
|
||||||
|
refreshExpire: number;
|
||||||
|
refreshToken: string;
|
||||||
|
token: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum MenuType {
|
||||||
|
"目录" = 0,
|
||||||
|
"菜单" = 1,
|
||||||
|
"权限" = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MenuItem {
|
||||||
|
id: number;
|
||||||
|
parentId: number;
|
||||||
|
path: string;
|
||||||
|
router?: string;
|
||||||
|
viewPath?: string;
|
||||||
|
type: MenuType;
|
||||||
|
name: string;
|
||||||
|
icon: string;
|
||||||
|
orderNum: number;
|
||||||
|
isShow: number;
|
||||||
|
keepAlive?: number;
|
||||||
|
meta?: {
|
||||||
|
label: string;
|
||||||
|
keepAlive: number;
|
||||||
|
};
|
||||||
|
children?: MenuItem[];
|
||||||
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
export const revisePath = path => {
|
export const revisePath = (path: string) => {
|
||||||
if (!path) {
|
if (!path) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
@ -10,11 +10,11 @@ export const revisePath = path => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export function firstMenu(list) {
|
export function firstMenu(list: Array<any>) {
|
||||||
let path = "";
|
let path = "";
|
||||||
|
|
||||||
const fn = arr => {
|
const fn = (arr: Array<any>) => {
|
||||||
arr.forEach(e => {
|
arr.forEach((e: any) => {
|
||||||
if (e.type == 1) {
|
if (e.type == 1) {
|
||||||
if (!path) {
|
if (!path) {
|
||||||
path = e.path;
|
path = e.path;
|
||||||
@ -30,7 +30,7 @@ export function firstMenu(list) {
|
|||||||
return path || "/404";
|
return path || "/404";
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createLink(url, id) {
|
export function createLink(url: string, id?: string) {
|
||||||
const link = document.createElement("link");
|
const link = document.createElement("link");
|
||||||
link.href = url;
|
link.href = url;
|
||||||
link.type = "text/css";
|
link.type = "text/css";
|
||||||
@ -38,8 +38,9 @@ export function createLink(url, id) {
|
|||||||
if (id) {
|
if (id) {
|
||||||
link.id = id;
|
link.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
document
|
document
|
||||||
.getElementsByTagName("head")
|
.getElementsByTagName("head")
|
||||||
.item(0)
|
?.item(0)
|
||||||
.appendChild(link);
|
?.appendChild(link);
|
||||||
}
|
}
|
||||||
@ -1,10 +0,0 @@
|
|||||||
export default [
|
|
||||||
{
|
|
||||||
path: "/my/info",
|
|
||||||
component: () => import("./info"),
|
|
||||||
meta: {
|
|
||||||
label: "个人中心",
|
|
||||||
keepAlive: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
];
|
|
||||||
7
src/cool/modules/base/views/index.ts
Normal file
7
src/cool/modules/base/views/index.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export default [
|
||||||
|
{
|
||||||
|
label: "个人中心",
|
||||||
|
path: "/my/info",
|
||||||
|
component: () => import("./info.vue")
|
||||||
|
}
|
||||||
|
];
|
||||||
@ -22,51 +22,56 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { mapGetters } from "vuex";
|
import { ElMessage } from "element-plus";
|
||||||
|
import { defineComponent, inject, reactive, ref } from "vue";
|
||||||
|
import { useStore } from "vuex";
|
||||||
|
|
||||||
export default {
|
export default defineComponent({
|
||||||
data() {
|
name: "sys-info",
|
||||||
return {
|
|
||||||
form: {},
|
|
||||||
saving: false
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
setup() {
|
||||||
...mapGetters(["userInfo"])
|
const store = useStore();
|
||||||
},
|
const $service = inject<any>("$service");
|
||||||
|
|
||||||
mounted() {
|
// 表单数据
|
||||||
this.form = this.userInfo;
|
const form = reactive<any>(store.getters.userInfo);
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
// 保存状态
|
||||||
save() {
|
const saving = ref<boolean>(false);
|
||||||
this.saving = true;
|
|
||||||
|
|
||||||
const { headImg, nickName, password } = this.form;
|
// 保存
|
||||||
|
function save() {
|
||||||
|
const { headImg, nickName, password } = form;
|
||||||
|
|
||||||
this.$service.common
|
saving.value = true;
|
||||||
|
|
||||||
|
$service.common
|
||||||
.userUpdate({
|
.userUpdate({
|
||||||
headImg,
|
headImg,
|
||||||
nickName,
|
nickName,
|
||||||
password
|
password
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.form.password = "";
|
form.password = "";
|
||||||
this.$message.success("修改成功");
|
ElMessage.success("修改成功");
|
||||||
this.$store.dispatch("userInfo");
|
store.dispatch("userInfo");
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch((err: string) => {
|
||||||
this.$message.error(err);
|
ElMessage.error(err);
|
||||||
})
|
})
|
||||||
.done(() => {
|
.done(() => {
|
||||||
this.saving = false;
|
saving.value = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
form,
|
||||||
|
saving,
|
||||||
|
save
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<cl-crud ref="crud" @load="onLoad">
|
<cl-crud :ref="setRefs('crud')" @load="onLoad">
|
||||||
<el-row type="flex">
|
<el-row type="flex">
|
||||||
<cl-refresh-btn></cl-refresh-btn>
|
<cl-refresh-btn></cl-refresh-btn>
|
||||||
|
|
||||||
@ -38,125 +38,124 @@
|
|||||||
</cl-crud>
|
</cl-crud>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { mapGetters } from "vuex";
|
import { defineComponent, inject, reactive, ref } from "vue";
|
||||||
|
import { ElMessage, ElMessageBox } from "element-plus";
|
||||||
|
import { useRefs } from "@/core";
|
||||||
|
import { CrudLoad, Table } from "@/crud/types";
|
||||||
|
|
||||||
export default {
|
export default defineComponent({
|
||||||
data() {
|
name: "sys-log",
|
||||||
return {
|
|
||||||
day: 1,
|
setup() {
|
||||||
table: {
|
const $service = inject<any>("$service");
|
||||||
props: {
|
const { refs, setRefs } = useRefs();
|
||||||
"default-sort": {
|
|
||||||
prop: "createTime",
|
// 天数
|
||||||
order: "descending"
|
const day = ref<number>(1);
|
||||||
}
|
|
||||||
|
// cl-table 配置
|
||||||
|
const table = reactive<Table>({
|
||||||
|
"context-menu": ["refresh"],
|
||||||
|
props: {
|
||||||
|
"default-sort": {
|
||||||
|
prop: "createTime",
|
||||||
|
order: "descending"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
type: "index",
|
||||||
|
label: "#",
|
||||||
|
width: 60
|
||||||
},
|
},
|
||||||
"context-menu": [
|
{
|
||||||
"refresh",
|
prop: "userId",
|
||||||
{
|
label: "用户ID"
|
||||||
label: "清空",
|
},
|
||||||
callback: (_, done) => {
|
{
|
||||||
this.clear();
|
prop: "name",
|
||||||
done();
|
label: "昵称",
|
||||||
}
|
minWidth: 150
|
||||||
}
|
},
|
||||||
],
|
{
|
||||||
columns: [
|
prop: "action",
|
||||||
{
|
label: "请求地址",
|
||||||
type: "index",
|
minWidth: 200,
|
||||||
label: "#",
|
showOverflowTooltip: true
|
||||||
align: "center",
|
},
|
||||||
width: 60
|
{
|
||||||
},
|
prop: "params",
|
||||||
{
|
label: "参数",
|
||||||
prop: "userId",
|
align: "center",
|
||||||
label: "用户ID",
|
minWidth: 200,
|
||||||
align: "center"
|
showOverflowTooltip: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
prop: "name",
|
prop: "ip",
|
||||||
label: "昵称",
|
label: "ip",
|
||||||
align: "center",
|
minWidth: 180
|
||||||
minWidth: "150"
|
},
|
||||||
},
|
{
|
||||||
{
|
prop: "ipAddr",
|
||||||
prop: "action",
|
label: "ip地址",
|
||||||
label: "请求地址",
|
minWidth: 150
|
||||||
align: "center",
|
},
|
||||||
minWidth: "200",
|
{
|
||||||
"show-overflow-tooltip": true
|
prop: "createTime",
|
||||||
},
|
label: "创建时间",
|
||||||
{
|
minWidth: 150,
|
||||||
prop: "params",
|
sortable: true
|
||||||
label: "参数",
|
}
|
||||||
align: "center",
|
]
|
||||||
minWidth: "200",
|
|
||||||
"show-overflow-tooltip": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: "ip",
|
|
||||||
label: "ip",
|
|
||||||
minWidth: "180",
|
|
||||||
align: "center"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: "ipAddr",
|
|
||||||
label: "ip地址",
|
|
||||||
minWidth: "150",
|
|
||||||
align: "center"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: "createTime",
|
|
||||||
label: "创建时间",
|
|
||||||
minWidth: "150",
|
|
||||||
align: "center",
|
|
||||||
sortable: true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
...mapGetters(["permission"])
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
|
||||||
this.$service.system.log.getKeep().then(res => {
|
|
||||||
this.day = res;
|
|
||||||
});
|
});
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
// crud 加载
|
||||||
onLoad({ ctx, app }) {
|
function onLoad({ ctx, app }: CrudLoad) {
|
||||||
ctx.service(this.$service.system.log).done();
|
ctx.service($service.system.log).done();
|
||||||
app.refresh();
|
app.refresh();
|
||||||
},
|
}
|
||||||
|
|
||||||
saveDay() {
|
// 保存天数
|
||||||
this.$service.system.log.setKeep(this.day).then(() => {
|
function saveDay() {
|
||||||
this.$message.success("保存成功");
|
$service.system.log.setKeep(day.value).then(() => {
|
||||||
|
ElMessage.success("保存成功");
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
clear() {
|
// 清空日志
|
||||||
this.$confirm("是否要清空日志", "提示", {
|
function clear() {
|
||||||
|
ElMessageBox.confirm("是否要清空日志", "提示", {
|
||||||
type: "warning"
|
type: "warning"
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.$service.system.log
|
$service.system.log
|
||||||
.clear()
|
.clear()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.$message.success("清空成功");
|
ElMessage.success("清空成功");
|
||||||
this.$refs["crud"].refresh();
|
refs.value.crud.refresh();
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch((err: string) => {
|
||||||
this.$message.error(err);
|
ElMessage.error(err);
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(() => {});
|
.catch(() => null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取天数
|
||||||
|
$service.system.log.getKeep().then((res: number) => {
|
||||||
|
day.value = Number(res);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
refs,
|
||||||
|
day,
|
||||||
|
table,
|
||||||
|
setRefs,
|
||||||
|
onLoad,
|
||||||
|
saveDay,
|
||||||
|
clear
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<cl-crud ref="crud" @load="onLoad" :on-refresh="onRefresh">
|
<cl-crud :ref="setRefs('crud')" :on-refresh="onRefresh" @load="onLoad">
|
||||||
<el-row type="flex">
|
<el-row type="flex">
|
||||||
<cl-refresh-btn />
|
<cl-refresh-btn />
|
||||||
<cl-add-btn />
|
<cl-add-btn />
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<el-row>
|
<el-row>
|
||||||
<cl-table ref="table" v-bind="table" @row-click="onRowClick">
|
<cl-table :ref="setRefs('table')" v-bind="table" @row-click="onRowClick">
|
||||||
<!-- 名称 -->
|
<!-- 名称 -->
|
||||||
<template #column-name="{ scope }">
|
<template #column-name="{ scope }">
|
||||||
<span>{{ scope.row.name }}</span>
|
<span>{{ scope.row.name }}</span>
|
||||||
@ -66,64 +66,191 @@
|
|||||||
</cl-table>
|
</cl-table>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
|
<el-row type="flex">
|
||||||
|
<cl-flex1></cl-flex1>
|
||||||
|
<cl-pagination :props="{ layout: 'total' }"></cl-pagination>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
<!-- 编辑 -->
|
<!-- 编辑 -->
|
||||||
<cl-upsert ref="upsert" v-bind="upsert"></cl-upsert>
|
<cl-upsert v-bind="upsert"></cl-upsert>
|
||||||
</cl-crud>
|
</cl-crud>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { deepTree } from "cl-admin/utils";
|
import { useRefs } from "@/core";
|
||||||
|
import { deepTree } from "@/core/utils";
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
|
import { defineComponent, inject, reactive } from "vue";
|
||||||
|
import { CrudLoad, Table, Upsert, RefreshOp } from "@/crud/types";
|
||||||
|
|
||||||
export default {
|
export default defineComponent({
|
||||||
data() {
|
name: "sys-menu",
|
||||||
return {
|
|
||||||
table: {
|
setup() {
|
||||||
props: {
|
const router = useRouter();
|
||||||
"row-key": "id"
|
const { refs, setRefs } = useRefs();
|
||||||
|
const $service = inject<any>("$service");
|
||||||
|
|
||||||
|
// crud 加载
|
||||||
|
function onLoad({ ctx, app }: CrudLoad) {
|
||||||
|
ctx.service($service.system.menu).done();
|
||||||
|
app.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 刷新监听
|
||||||
|
function onRefresh(_: any, { render }: RefreshOp) {
|
||||||
|
$service.system.menu.list().then((list: any[]) => {
|
||||||
|
list.map(e => {
|
||||||
|
e.permList = e.perms ? e.perms.split(",") : [];
|
||||||
|
});
|
||||||
|
|
||||||
|
render(deepTree(list), {
|
||||||
|
total: list.length
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 行点击展开
|
||||||
|
function onRowClick(row: any, column: any) {
|
||||||
|
if (column.property && row.children) {
|
||||||
|
refs.value.table.toggleRowExpansion(row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 子集新增
|
||||||
|
function upsertAppend({ type, id }: any) {
|
||||||
|
refs.value.crud.rowAppend({
|
||||||
|
parentId: id,
|
||||||
|
type: type + 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置权限
|
||||||
|
function setPermission({ id }: any) {
|
||||||
|
refs.value.crud.rowAppend({
|
||||||
|
parentId: id,
|
||||||
|
type: 2
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 跳转
|
||||||
|
function toUrl(url: string) {
|
||||||
|
router.push(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 表格配置
|
||||||
|
const table = reactive<Table>({
|
||||||
|
props: {
|
||||||
|
"row-key": "id"
|
||||||
|
},
|
||||||
|
"context-menu": [
|
||||||
|
(row: any) => {
|
||||||
|
return {
|
||||||
|
label: "新增",
|
||||||
|
hidden: row.type == 2,
|
||||||
|
callback: (_: any, done: Function) => {
|
||||||
|
upsertAppend(row);
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
};
|
||||||
},
|
},
|
||||||
"context-menu": [
|
"update",
|
||||||
row => {
|
"delete",
|
||||||
return {
|
(row: any) => {
|
||||||
label: "新增",
|
return {
|
||||||
hidden: row.type == 2,
|
label: "权限",
|
||||||
callback: (_, done) => {
|
hidden: row.type != 1,
|
||||||
this.upsertAppend(row);
|
callback: (_: any, done: Function) => {
|
||||||
done();
|
setPermission(row);
|
||||||
}
|
done();
|
||||||
};
|
}
|
||||||
},
|
};
|
||||||
"update",
|
}
|
||||||
"delete",
|
],
|
||||||
row => {
|
columns: [
|
||||||
return {
|
{
|
||||||
|
prop: "name",
|
||||||
|
label: "名称",
|
||||||
|
align: "left",
|
||||||
|
width: 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: "icon",
|
||||||
|
label: "图标",
|
||||||
|
width: 80
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: "type",
|
||||||
|
label: "类型",
|
||||||
|
width: 100,
|
||||||
|
dict: [
|
||||||
|
{
|
||||||
|
label: "目录",
|
||||||
|
value: 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "菜单",
|
||||||
|
value: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
label: "权限",
|
label: "权限",
|
||||||
hidden: row.type != 1,
|
value: 2
|
||||||
callback: (_, done) => {
|
}
|
||||||
this.setPermission(row);
|
]
|
||||||
done();
|
},
|
||||||
}
|
{
|
||||||
};
|
prop: "router",
|
||||||
}
|
label: "节点路由",
|
||||||
],
|
minWidth: 160
|
||||||
columns: [
|
},
|
||||||
{
|
{
|
||||||
prop: "name",
|
prop: "keepAlive",
|
||||||
label: "名称",
|
label: "路由缓存",
|
||||||
align: "left",
|
width: 100
|
||||||
width: 200
|
},
|
||||||
},
|
{
|
||||||
{
|
prop: "viewPath",
|
||||||
prop: "icon",
|
label: "文件路径",
|
||||||
label: "图标",
|
minWidth: 200,
|
||||||
align: "center",
|
showOverflowTooltip: true
|
||||||
width: 80
|
},
|
||||||
},
|
{
|
||||||
{
|
prop: "perms",
|
||||||
prop: "type",
|
label: "权限",
|
||||||
label: "类型",
|
headerAlign: "center",
|
||||||
align: "center",
|
minWidth: 300
|
||||||
width: 100,
|
},
|
||||||
dict: [
|
{
|
||||||
|
prop: "orderNum",
|
||||||
|
label: "排序号",
|
||||||
|
width: 90
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: "updateTime",
|
||||||
|
label: "更新时间",
|
||||||
|
sortable: "custom",
|
||||||
|
width: 150
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "操作",
|
||||||
|
type: "op",
|
||||||
|
buttons: ["slot-add", "edit", "delete"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
// 新增、编辑配置
|
||||||
|
const upsert = reactive<Upsert>({
|
||||||
|
width: "800px",
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
prop: "type",
|
||||||
|
value: 0,
|
||||||
|
label: "节点类型",
|
||||||
|
span: 24,
|
||||||
|
component: {
|
||||||
|
name: "el-radio-group",
|
||||||
|
options: [
|
||||||
{
|
{
|
||||||
label: "目录",
|
label: "目录",
|
||||||
value: 0
|
value: 0
|
||||||
@ -137,236 +264,130 @@ export default {
|
|||||||
value: 2
|
value: 2
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: "router",
|
|
||||||
label: "节点路由",
|
|
||||||
align: "center",
|
|
||||||
"min-width": 160
|
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: "keepAlive",
|
|
||||||
label: "路由缓存",
|
|
||||||
align: "center",
|
|
||||||
width: 100
|
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: "viewPath",
|
|
||||||
label: "文件路径",
|
|
||||||
align: "center",
|
|
||||||
"min-width": 200,
|
|
||||||
"show-overflow-tooltip": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: "perms",
|
|
||||||
label: "权限",
|
|
||||||
"header-align": "center",
|
|
||||||
"min-width": 300
|
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: "orderNum",
|
|
||||||
label: "排序号",
|
|
||||||
align: "center",
|
|
||||||
width: 90
|
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: "updateTime",
|
|
||||||
label: "更新时间",
|
|
||||||
align: "center",
|
|
||||||
sortable: "custom",
|
|
||||||
width: 150
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "操作",
|
|
||||||
align: "center",
|
|
||||||
type: "op",
|
|
||||||
buttons: ["slot-add", "edit", "delete"]
|
|
||||||
}
|
}
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
upsert: {
|
|
||||||
props: {
|
|
||||||
width: "800px"
|
|
||||||
},
|
},
|
||||||
items: [
|
{
|
||||||
{
|
prop: "name",
|
||||||
prop: "type",
|
label: "节点名称",
|
||||||
value: 0,
|
span: 24,
|
||||||
label: "节点类型",
|
component: {
|
||||||
span: 24,
|
name: "el-input",
|
||||||
component: {
|
props: {
|
||||||
name: "el-radio-group",
|
placeholder: "请输入节点名称"
|
||||||
options: [
|
|
||||||
{
|
|
||||||
label: "目录",
|
|
||||||
value: 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "菜单",
|
|
||||||
value: 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "权限",
|
|
||||||
value: 2
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
rules: {
|
||||||
prop: "name",
|
required: true,
|
||||||
label: "节点名称",
|
message: "名称不能为空"
|
||||||
span: 24,
|
}
|
||||||
component: {
|
},
|
||||||
name: "el-input",
|
{
|
||||||
attrs: {
|
prop: "parentId",
|
||||||
placeholder: "请输入节点名称"
|
label: "上级节点",
|
||||||
}
|
span: 24,
|
||||||
},
|
component: {
|
||||||
|
name: "cl-menu-tree"
|
||||||
rules: {
|
}
|
||||||
required: true,
|
},
|
||||||
message: "名称不能为空"
|
{
|
||||||
}
|
prop: "router",
|
||||||
},
|
label: "节点路由",
|
||||||
{
|
span: 24,
|
||||||
prop: "parentId",
|
hidden: ({ scope }: any) => scope.type != 1,
|
||||||
label: "上级节点",
|
component: {
|
||||||
span: 24,
|
name: "el-input",
|
||||||
component: {
|
props: {
|
||||||
name: "cl-menu-tree"
|
placeholder: "请输入节点路由"
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: "router",
|
|
||||||
label: "节点路由",
|
|
||||||
span: 24,
|
|
||||||
hidden: ({ scope }) => scope.type != 1,
|
|
||||||
component: {
|
|
||||||
name: "el-input",
|
|
||||||
attrs: {
|
|
||||||
placeholder: "请输入节点路由"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: "keepAlive",
|
|
||||||
value: true,
|
|
||||||
label: "路由缓存",
|
|
||||||
span: 24,
|
|
||||||
hidden: ({ scope }) => scope.type != 1,
|
|
||||||
component: {
|
|
||||||
name: "el-radio-group",
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
label: "开启",
|
|
||||||
value: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "关闭",
|
|
||||||
value: false
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: "isShow",
|
|
||||||
label: "是否显示",
|
|
||||||
span: 24,
|
|
||||||
value: true,
|
|
||||||
hidden: ({ scope }) => scope.type == 2,
|
|
||||||
flex: false,
|
|
||||||
component: {
|
|
||||||
name: "el-switch"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: "viewPath",
|
|
||||||
label: "文件路径",
|
|
||||||
span: 24,
|
|
||||||
hidden: ({ scope }) => scope.type != 1,
|
|
||||||
component: {
|
|
||||||
name: "cl-menu-file"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: "icon",
|
|
||||||
label: "节点图标",
|
|
||||||
span: 24,
|
|
||||||
hidden: ({ scope }) => scope.type == 2,
|
|
||||||
component: {
|
|
||||||
name: "cl-menu-icons"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
prop: "orderNum",
|
|
||||||
label: "排序号",
|
|
||||||
span: 24,
|
|
||||||
component: {
|
|
||||||
name: "el-input-number",
|
|
||||||
props: {
|
|
||||||
placeholder: "请填写排序号",
|
|
||||||
min: 0,
|
|
||||||
max: 99,
|
|
||||||
"controls-position": "right"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: "perms",
|
|
||||||
label: "权限",
|
|
||||||
span: 24,
|
|
||||||
hidden: ({ scope }) => scope.type != 2,
|
|
||||||
component: {
|
|
||||||
name: "cl-menu-perms"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
},
|
||||||
}
|
{
|
||||||
|
prop: "keepAlive",
|
||||||
|
value: true,
|
||||||
|
label: "路由缓存",
|
||||||
|
span: 24,
|
||||||
|
hidden: ({ scope }: any) => scope.type != 1,
|
||||||
|
component: {
|
||||||
|
name: "el-radio-group",
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: "开启",
|
||||||
|
value: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "关闭",
|
||||||
|
value: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: "isShow",
|
||||||
|
label: "是否显示",
|
||||||
|
span: 24,
|
||||||
|
value: true,
|
||||||
|
hidden: ({ scope }: any) => scope.type == 2,
|
||||||
|
flex: false,
|
||||||
|
component: {
|
||||||
|
name: "el-switch"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: "viewPath",
|
||||||
|
label: "文件路径",
|
||||||
|
span: 24,
|
||||||
|
hidden: ({ scope }: any) => scope.type != 1,
|
||||||
|
component: {
|
||||||
|
name: "cl-menu-file"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: "icon",
|
||||||
|
label: "节点图标",
|
||||||
|
span: 24,
|
||||||
|
hidden: ({ scope }: any) => scope.type == 2,
|
||||||
|
component: {
|
||||||
|
name: "cl-menu-icons"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: "orderNum",
|
||||||
|
label: "排序号",
|
||||||
|
span: 24,
|
||||||
|
component: {
|
||||||
|
name: "el-input-number",
|
||||||
|
props: {
|
||||||
|
placeholder: "请填写排序号",
|
||||||
|
min: 0,
|
||||||
|
max: 99,
|
||||||
|
"controls-position": "right"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: "perms",
|
||||||
|
label: "权限",
|
||||||
|
span: 24,
|
||||||
|
hidden: ({ scope }: any) => scope.type != 2,
|
||||||
|
component: {
|
||||||
|
name: "cl-menu-perms"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
refs,
|
||||||
|
table,
|
||||||
|
upsert,
|
||||||
|
setRefs,
|
||||||
|
onLoad,
|
||||||
|
onRefresh,
|
||||||
|
onRowClick,
|
||||||
|
upsertAppend,
|
||||||
|
setPermission,
|
||||||
|
toUrl
|
||||||
};
|
};
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
onLoad({ ctx, app }) {
|
|
||||||
ctx.service(this.$service.system.menu).done();
|
|
||||||
|
|
||||||
app.refresh();
|
|
||||||
},
|
|
||||||
|
|
||||||
onRefresh(_, { render }) {
|
|
||||||
this.$service.system.menu.list().then(list => {
|
|
||||||
list.map(e => {
|
|
||||||
e.permList = e.perms ? e.perms.split(",") : [];
|
|
||||||
});
|
|
||||||
|
|
||||||
render(deepTree(list));
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
onRowClick(row, column) {
|
|
||||||
if (column.property && row.children) {
|
|
||||||
this.$refs["table"].toggleRowExpansion(row);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
upsertAppend({ type, id }) {
|
|
||||||
this.$refs["crud"].rowAppend({
|
|
||||||
parentId: id,
|
|
||||||
type: type + 1
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
setPermission({ id }) {
|
|
||||||
this.$refs["crud"].rowAppend({
|
|
||||||
parentId: id,
|
|
||||||
type: 2
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
toUrl(url) {
|
|
||||||
this.$router.push(url);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,16 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<cl-crud @load="onLoad">
|
<cl-crud @load="onLoad">
|
||||||
<template #slot-content="{ scope }">
|
|
||||||
<div class="editor" v-for="(item, index) in tab.list" :key="index">
|
|
||||||
<template v-if="tab.index === index">
|
|
||||||
<el-button class="change-btn" size="mini" @click="changeTab(item.to)">{{
|
|
||||||
item.label
|
|
||||||
}}</el-button>
|
|
||||||
<component :is="item.component" height="300px" v-model="scope.data"></component>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<el-row type="flex">
|
<el-row type="flex">
|
||||||
<cl-refresh-btn></cl-refresh-btn>
|
<cl-refresh-btn></cl-refresh-btn>
|
||||||
<cl-add-btn></cl-add-btn>
|
<cl-add-btn></cl-add-btn>
|
||||||
@ -28,13 +17,14 @@
|
|||||||
<cl-pagination></cl-pagination>
|
<cl-pagination></cl-pagination>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<cl-upsert ref="upsert" v-bind="upsert" @open="onUpsertOpen">
|
<cl-upsert :ref="setRefs('upsert')" v-bind="upsert" @open="onUpsertOpen">
|
||||||
<template #slot-content="{ scope }">
|
<template #slot-content="{ scope }">
|
||||||
<div class="editor" v-for="(item, index) in tab.list" :key="index">
|
<div class="editor" v-for="(item, index) in tab.list" :key="index">
|
||||||
<template v-if="tab.index === index">
|
<template v-if="tab.index == index">
|
||||||
<el-button class="change-btn" size="mini" @click="changeTab(item.to)">{{
|
<el-button class="change-btn" size="mini" @click="changeTab(item.to)">{{
|
||||||
item.label
|
item.label
|
||||||
}}</el-button>
|
}}</el-button>
|
||||||
|
|
||||||
<component
|
<component
|
||||||
:is="item.component"
|
:is="item.component"
|
||||||
height="300px"
|
height="300px"
|
||||||
@ -47,158 +37,173 @@
|
|||||||
</cl-crud>
|
</cl-crud>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts">
|
||||||
export default {
|
import { ElMessageBox } from "element-plus";
|
||||||
data() {
|
import { defineComponent, inject, nextTick, reactive } from "vue";
|
||||||
return {
|
import { useRefs } from "@/core";
|
||||||
tab: {
|
import { CrudLoad, Table, Upsert } from "@/crud/types";
|
||||||
index: null,
|
|
||||||
|
|
||||||
list: [
|
export default defineComponent({
|
||||||
{
|
name: "sys-param",
|
||||||
label: "切换富文本编辑器",
|
|
||||||
to: 1,
|
setup() {
|
||||||
component: "cl-codemirror"
|
const $service = inject<any>("$service");
|
||||||
},
|
const { refs, setRefs } = useRefs();
|
||||||
{
|
|
||||||
label: "切换代码编辑器",
|
// 选项卡
|
||||||
to: 0,
|
const tab = reactive<any>({
|
||||||
component: "cl-editor-quill"
|
index: null,
|
||||||
}
|
|
||||||
]
|
list: [
|
||||||
},
|
{
|
||||||
table: {
|
label: "切换富文本编辑器",
|
||||||
columns: [
|
to: 1,
|
||||||
{
|
component: "cl-codemirror"
|
||||||
type: "selection",
|
|
||||||
align: "center",
|
|
||||||
width: 60
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "名称",
|
|
||||||
prop: "name",
|
|
||||||
align: "center",
|
|
||||||
"min-width": 150
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "keyName",
|
|
||||||
prop: "keyName",
|
|
||||||
align: "center",
|
|
||||||
"min-width": 150
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "数据",
|
|
||||||
prop: "data",
|
|
||||||
align: "center",
|
|
||||||
"min-width": 150,
|
|
||||||
"show-overflow-tooltip": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "备注",
|
|
||||||
prop: "remark",
|
|
||||||
align: "center",
|
|
||||||
"min-width": 200,
|
|
||||||
"show-overflow-tooltip": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "操作",
|
|
||||||
align: "center",
|
|
||||||
type: "op"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
upsert: {
|
|
||||||
props: {
|
|
||||||
width: "1000px"
|
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: "切换代码编辑器",
|
||||||
|
to: 0,
|
||||||
|
component: "cl-editor-quill"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
items: [
|
// 表格配置
|
||||||
{
|
const table = reactive<Table>({
|
||||||
prop: "name",
|
columns: [
|
||||||
label: "名称",
|
{
|
||||||
span: 12,
|
type: "selection",
|
||||||
component: {
|
width: 60
|
||||||
name: "el-input",
|
},
|
||||||
attrs: {
|
{
|
||||||
placeholder: "请输入名称"
|
label: "名称",
|
||||||
}
|
prop: "name",
|
||||||
},
|
minWidth: 150
|
||||||
rules: {
|
},
|
||||||
required: true,
|
{
|
||||||
message: "名称不能为空"
|
label: "keyName",
|
||||||
|
prop: "keyName",
|
||||||
|
minWidth: 150
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "数据",
|
||||||
|
prop: "data",
|
||||||
|
minWidth: 150,
|
||||||
|
showOverflowTooltip: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "备注",
|
||||||
|
prop: "remark",
|
||||||
|
minWidth: 200,
|
||||||
|
showOverflowTooltip: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "操作",
|
||||||
|
type: "op"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
// 新增编辑配置
|
||||||
|
const upsert = reactive<Upsert>({
|
||||||
|
width: "1000px",
|
||||||
|
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
prop: "name",
|
||||||
|
label: "名称",
|
||||||
|
span: 12,
|
||||||
|
component: {
|
||||||
|
name: "el-input",
|
||||||
|
props: {
|
||||||
|
placeholder: "请输入名称"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
rules: {
|
||||||
prop: "keyName",
|
required: true,
|
||||||
label: "keyName",
|
message: "名称不能为空"
|
||||||
span: 12,
|
}
|
||||||
component: {
|
},
|
||||||
name: "el-input",
|
{
|
||||||
attrs: {
|
prop: "keyName",
|
||||||
placeholder: "请输入Key"
|
label: "keyName",
|
||||||
}
|
span: 12,
|
||||||
},
|
component: {
|
||||||
rules: {
|
name: "el-input",
|
||||||
required: true,
|
props: {
|
||||||
message: "Key不能为空"
|
placeholder: "请输入Key"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
rules: {
|
||||||
prop: "data",
|
required: true,
|
||||||
label: "数据",
|
message: "Key不能为空"
|
||||||
component: {
|
}
|
||||||
name: "slot-content"
|
},
|
||||||
}
|
{
|
||||||
},
|
prop: "data",
|
||||||
{
|
label: "数据",
|
||||||
prop: "remark",
|
component: {
|
||||||
label: "备注",
|
name: "slot-content"
|
||||||
component: {
|
}
|
||||||
name: "el-input",
|
},
|
||||||
props: {
|
{
|
||||||
type: "textarea"
|
prop: "remark",
|
||||||
},
|
label: "备注",
|
||||||
attrs: {
|
component: {
|
||||||
placeholder: "请输入备注",
|
name: "el-input",
|
||||||
rows: 3
|
props: {
|
||||||
}
|
placeholder: "请输入备注",
|
||||||
|
rows: 3,
|
||||||
|
type: "textarea"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
}
|
]
|
||||||
};
|
});
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
// crud 加载
|
||||||
onLoad({ ctx, app }) {
|
function onLoad({ ctx, app }: CrudLoad) {
|
||||||
ctx.service(this.$service.system.param).done();
|
ctx.service($service.system.param).done();
|
||||||
app.refresh();
|
app.refresh();
|
||||||
},
|
}
|
||||||
|
|
||||||
changeTab(i) {
|
// 切换编辑器
|
||||||
this.$confirm("切换编辑器会清空输入内容,是否继续?", "提示", {
|
function changeTab(i: number) {
|
||||||
|
ElMessageBox.confirm("切换编辑器会清空输入内容,是否继续?", "提示", {
|
||||||
type: "warning"
|
type: "warning"
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.tab.index = i;
|
tab.index = i;
|
||||||
this.$refs["upsert"].setForm("data", "");
|
refs.value.upsert.setForm("data", "");
|
||||||
})
|
})
|
||||||
.catch(() => {});
|
.catch(() => null);
|
||||||
},
|
}
|
||||||
|
|
||||||
onUpsertOpen(isEdit, data) {
|
// 监听打开
|
||||||
this.tab.index = null;
|
function onUpsertOpen(isEdit: boolean, data: any) {
|
||||||
|
tab.index = null;
|
||||||
|
|
||||||
this.$nextTick(() => {
|
nextTick(() => {
|
||||||
if (isEdit) {
|
if (isEdit) {
|
||||||
this.tab.index = /<*>/g.test(data.data) ? 1 : 0;
|
tab.index = /<*>/g.test(data.data) ? 1 : 0;
|
||||||
} else {
|
} else {
|
||||||
this.tab.index = 1;
|
tab.index = 1;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
refs,
|
||||||
|
tab,
|
||||||
|
table,
|
||||||
|
upsert,
|
||||||
|
setRefs,
|
||||||
|
onLoad,
|
||||||
|
changeTab,
|
||||||
|
onUpsertOpen
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@ -14,14 +14,23 @@
|
|||||||
<cl-table v-bind="table">
|
<cl-table v-bind="table">
|
||||||
<template #column-enable="{ scope }">
|
<template #column-enable="{ scope }">
|
||||||
<el-switch
|
<el-switch
|
||||||
v-model="scope.row.enable"
|
v-model="scope.row._enable"
|
||||||
size="mini"
|
size="mini"
|
||||||
:inactive-value="0"
|
|
||||||
:active-value="1"
|
|
||||||
:disabled="!perms.enable"
|
:disabled="!perms.enable"
|
||||||
@change="onEnableChange($event, scope.row)"
|
@change="onEnableChange($event, scope.row)"
|
||||||
></el-switch>
|
></el-switch>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<!-- 配置按钮 -->
|
||||||
|
<template #slot-conf="{ scope }">
|
||||||
|
<el-button
|
||||||
|
type="text"
|
||||||
|
size="mini"
|
||||||
|
v-if="scope.row.view && perms.edit"
|
||||||
|
@click="openConf(scope.row)"
|
||||||
|
>配置</el-button
|
||||||
|
>
|
||||||
|
</template>
|
||||||
</cl-table>
|
</cl-table>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
@ -32,148 +41,38 @@
|
|||||||
</el-row>
|
</el-row>
|
||||||
</cl-crud>
|
</cl-crud>
|
||||||
|
|
||||||
<cl-form ref="form"></cl-form>
|
<!-- 表单 -->
|
||||||
|
<cl-form :ref="setRefs('form')"></cl-form>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts">
|
||||||
|
import { ElMessage } from "element-plus";
|
||||||
|
import { defineComponent, inject, reactive } from "vue";
|
||||||
import { checkPerm } from "@/cool/modules/base";
|
import { checkPerm } from "@/cool/modules/base";
|
||||||
|
import { useRefs } from "@/core";
|
||||||
|
import { CrudLoad, RefreshOp, Table } from "@/crud/types";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "sys-plugin",
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
const $service = inject<any>("$service");
|
||||||
|
const { refs, setRefs } = useRefs();
|
||||||
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
// 编辑权限
|
// 编辑权限
|
||||||
const { config, getConfig, enable } = this.$service.plugin.info.permission;
|
const { config, getConfig, enable } = $service.plugin.info.permission;
|
||||||
|
|
||||||
const perms = {
|
const perms = reactive<any>({
|
||||||
edit: checkPerm({
|
edit: checkPerm({
|
||||||
and: [config, getConfig]
|
and: [config, getConfig]
|
||||||
}),
|
}),
|
||||||
enable: checkPerm(enable)
|
enable: checkPerm(enable)
|
||||||
};
|
});
|
||||||
|
|
||||||
return {
|
// crud 加载
|
||||||
// 权限配置
|
function onLoad({ ctx, app }: CrudLoad) {
|
||||||
perms,
|
ctx.service($service.plugin.info)
|
||||||
// 表格配置
|
|
||||||
table: {
|
|
||||||
props: {
|
|
||||||
"default-sort": {
|
|
||||||
prop: "createTime",
|
|
||||||
order: "descending"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"context-menu": [
|
|
||||||
"refresh",
|
|
||||||
scope => {
|
|
||||||
return {
|
|
||||||
label: "配置",
|
|
||||||
hidden: !perms.edit || !scope.view,
|
|
||||||
callback: (_, done) => {
|
|
||||||
this.openConf(scope);
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
],
|
|
||||||
columns: [
|
|
||||||
{
|
|
||||||
label: "名称",
|
|
||||||
prop: "name",
|
|
||||||
"min-width": 140
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "作者",
|
|
||||||
prop: "author",
|
|
||||||
"min-width": 120
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "联系方式",
|
|
||||||
prop: "contact",
|
|
||||||
"show-overflow-tooltip": true,
|
|
||||||
"min-width": 180
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "功能描述",
|
|
||||||
prop: "description",
|
|
||||||
"show-overflow-tooltip": true,
|
|
||||||
"min-width": 150
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "版本号",
|
|
||||||
prop: "version",
|
|
||||||
"min-width": 110
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "是否启用",
|
|
||||||
prop: "enable",
|
|
||||||
"min-width": 110
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "命名空间",
|
|
||||||
prop: "namespace",
|
|
||||||
"min-width": 110
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "状态",
|
|
||||||
prop: "status",
|
|
||||||
width: 150,
|
|
||||||
dict: [
|
|
||||||
{
|
|
||||||
label: "缺少配置",
|
|
||||||
value: 0,
|
|
||||||
type: "warning"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "可用",
|
|
||||||
value: 1,
|
|
||||||
type: "success"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "配置错误",
|
|
||||||
value: 2,
|
|
||||||
type: "danger"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "未知错误",
|
|
||||||
value: 3,
|
|
||||||
type: "danger"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "创建时间",
|
|
||||||
prop: "createTime",
|
|
||||||
width: 150,
|
|
||||||
sortable: "custom"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "op",
|
|
||||||
width: 120,
|
|
||||||
buttons: [
|
|
||||||
({ scope }) => {
|
|
||||||
return (
|
|
||||||
scope.row.view &&
|
|
||||||
perms.edit && (
|
|
||||||
<el-button
|
|
||||||
type="text"
|
|
||||||
size="mini"
|
|
||||||
onclick={() => {
|
|
||||||
this.openConf(scope.row);
|
|
||||||
}}>
|
|
||||||
配置
|
|
||||||
</el-button>
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
onLoad({ ctx, app }) {
|
|
||||||
ctx.service(this.$service.plugin.info)
|
|
||||||
.set("dict", {
|
.set("dict", {
|
||||||
api: {
|
api: {
|
||||||
page: "list"
|
page: "list"
|
||||||
@ -181,35 +80,40 @@ export default {
|
|||||||
})
|
})
|
||||||
.done();
|
.done();
|
||||||
app.refresh();
|
app.refresh();
|
||||||
},
|
}
|
||||||
|
|
||||||
// 刷新钩子
|
// 刷新钩子
|
||||||
onRefresh(params, { next, render }) {
|
function onRefresh(params: any, { next, render }: RefreshOp) {
|
||||||
next(params).then(res => {
|
next(params).then((res: any) => {
|
||||||
render(res, {
|
const list = res.map((e: any) => {
|
||||||
|
e._enable = e.enable ? true : false;
|
||||||
|
return e;
|
||||||
|
});
|
||||||
|
|
||||||
|
render(list, {
|
||||||
total: res.length
|
total: res.length
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
// 开启、关闭
|
// 开启、关闭
|
||||||
onEnableChange(val, item) {
|
function onEnableChange(val: boolean, item: any) {
|
||||||
this.$service.plugin.info
|
$service.plugin.info
|
||||||
.enable({
|
.enable({
|
||||||
namespace: item.namespace,
|
namespace: item.namespace,
|
||||||
enable: val
|
enable: val
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.$message.success(val ? "开启成功" : "关闭成功");
|
ElMessage.success(val ? "开启成功" : "关闭成功");
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch((err: string) => {
|
||||||
this.$message.error(err);
|
ElMessage.error(err);
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
// 打开配置
|
// 打开配置
|
||||||
async openConf({ name, namespace, view }) {
|
async function openConf({ name, namespace, view }: any) {
|
||||||
const form = await this.$service.plugin.info.getConfig({
|
const form = await $service.plugin.info.getConfig({
|
||||||
namespace
|
namespace
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -221,29 +125,140 @@ export default {
|
|||||||
items = [];
|
items = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$refs.form.open({
|
refs.value.form.open({
|
||||||
title: `${name}配置`,
|
title: `${name}配置`,
|
||||||
items,
|
items,
|
||||||
form,
|
form,
|
||||||
on: {
|
on: {
|
||||||
submit: (data, { close, done }) => {
|
submit: (data: any, { close, done }: any) => {
|
||||||
this.$service.plugin.info
|
$service.plugin.info
|
||||||
.config({
|
.config({
|
||||||
namespace,
|
namespace,
|
||||||
config: data
|
config: data
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.$message.success("保存成功");
|
ElMessage.success("保存成功");
|
||||||
close();
|
close();
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch((err: string) => {
|
||||||
this.$message.error(err);
|
ElMessage.error(err);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 表格配置
|
||||||
|
const table = reactive<Table>({
|
||||||
|
props: {
|
||||||
|
"default-sort": {
|
||||||
|
prop: "createTime",
|
||||||
|
order: "descending"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"context-menu": [
|
||||||
|
"refresh",
|
||||||
|
(scope: any) => {
|
||||||
|
return {
|
||||||
|
label: "配置",
|
||||||
|
hidden: !perms.edit || !scope.view,
|
||||||
|
callback: (_: any, done: Function) => {
|
||||||
|
openConf(scope);
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
],
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
label: "名称",
|
||||||
|
prop: "name",
|
||||||
|
minWidth: 140
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "作者",
|
||||||
|
prop: "author",
|
||||||
|
minWidth: 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "联系方式",
|
||||||
|
prop: "contact",
|
||||||
|
showOverflowTooltip: true,
|
||||||
|
minWidth: 180
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "功能描述",
|
||||||
|
prop: "description",
|
||||||
|
showOverflowTooltip: true,
|
||||||
|
minWidth: 150
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "版本号",
|
||||||
|
prop: "version",
|
||||||
|
minWidth: 110
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "是否启用",
|
||||||
|
prop: "enable",
|
||||||
|
minWidth: 110
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "命名空间",
|
||||||
|
prop: "namespace",
|
||||||
|
minWidth: 110
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "状态",
|
||||||
|
prop: "status",
|
||||||
|
width: 150,
|
||||||
|
dict: [
|
||||||
|
{
|
||||||
|
label: "缺少配置",
|
||||||
|
value: 0,
|
||||||
|
type: "warning"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "可用",
|
||||||
|
value: 1,
|
||||||
|
type: "success"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "配置错误",
|
||||||
|
value: 2,
|
||||||
|
type: "danger"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "未知错误",
|
||||||
|
value: 3,
|
||||||
|
type: "danger"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "创建时间",
|
||||||
|
prop: "createTime",
|
||||||
|
width: 150,
|
||||||
|
sortable: "custom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "op",
|
||||||
|
width: 120,
|
||||||
|
buttons: ["slot-conf"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
refs,
|
||||||
|
perms,
|
||||||
|
table,
|
||||||
|
setRefs,
|
||||||
|
onLoad,
|
||||||
|
onRefresh,
|
||||||
|
onEnableChange,
|
||||||
|
openConf
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -21,143 +21,148 @@
|
|||||||
</cl-crud>
|
</cl-crud>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts">
|
||||||
export default {
|
import { CrudLoad, Table, Upsert } from "@/crud/types";
|
||||||
data() {
|
import { defineComponent, inject, reactive } from "vue";
|
||||||
return {
|
|
||||||
form: {
|
export default defineComponent({
|
||||||
relevance: 1
|
name: "sys-role",
|
||||||
},
|
|
||||||
upsert: {
|
setup() {
|
||||||
props: {
|
const $service = inject<any>("$service");
|
||||||
width: "800px"
|
|
||||||
},
|
// 表单值
|
||||||
items: [
|
const form = reactive<any>({
|
||||||
{
|
relevance: 1
|
||||||
prop: "name",
|
});
|
||||||
label: "名称",
|
|
||||||
span: 12,
|
// 新增、编辑配置
|
||||||
component: {
|
const upsert = reactive<Upsert>({
|
||||||
name: "el-input",
|
width: "800px",
|
||||||
attrs: {
|
|
||||||
placeholder: "请填写名称"
|
items: [
|
||||||
}
|
{
|
||||||
},
|
prop: "name",
|
||||||
rules: {
|
label: "名称",
|
||||||
required: true,
|
span: 12,
|
||||||
message: "名称不能为空"
|
component: {
|
||||||
|
name: "el-input",
|
||||||
|
props: {
|
||||||
|
placeholder: "请填写名称"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
rules: {
|
||||||
prop: "label",
|
required: true,
|
||||||
label: "标识",
|
message: "名称不能为空"
|
||||||
span: 12,
|
|
||||||
component: {
|
|
||||||
name: "el-input",
|
|
||||||
attrs: {
|
|
||||||
placeholder: "请填写标识"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
rules: {
|
|
||||||
required: true,
|
|
||||||
message: "标识不能为空"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: "remark",
|
|
||||||
label: "备注",
|
|
||||||
span: 24,
|
|
||||||
component: {
|
|
||||||
name: "el-input",
|
|
||||||
props: {
|
|
||||||
type: "textarea",
|
|
||||||
rows: 4
|
|
||||||
},
|
|
||||||
attrs: {
|
|
||||||
placeholder: "请填写备注"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "功能权限",
|
|
||||||
prop: "menuIdList",
|
|
||||||
value: [],
|
|
||||||
component: {
|
|
||||||
name: "cl-role-perms"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "数据权限",
|
|
||||||
prop: "departmentIdList",
|
|
||||||
value: [],
|
|
||||||
component: {
|
|
||||||
name: "cl-dept-check"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
table: {
|
|
||||||
props: {
|
|
||||||
"default-sort": {
|
|
||||||
prop: "createTime",
|
|
||||||
order: "descending"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
columns: [
|
{
|
||||||
{
|
prop: "label",
|
||||||
type: "selection",
|
label: "标识",
|
||||||
align: "center",
|
span: 12,
|
||||||
width: "60"
|
component: {
|
||||||
|
name: "el-input",
|
||||||
|
props: {
|
||||||
|
placeholder: "请填写标识"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
rules: {
|
||||||
prop: "name",
|
required: true,
|
||||||
label: "名称",
|
message: "标识不能为空"
|
||||||
align: "center",
|
|
||||||
"min-width": 150
|
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: "label",
|
|
||||||
label: "标识",
|
|
||||||
align: "center",
|
|
||||||
"min-width": 120
|
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: "remark",
|
|
||||||
label: "备注",
|
|
||||||
align: "center",
|
|
||||||
"show-overflow-tooltips": true,
|
|
||||||
"min-width": 150
|
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: "createTime",
|
|
||||||
label: "创建时间",
|
|
||||||
align: "center",
|
|
||||||
sortable: "custom",
|
|
||||||
"min-width": 150
|
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: "updateTime",
|
|
||||||
label: "更新时间",
|
|
||||||
align: "center",
|
|
||||||
sortable: "custom",
|
|
||||||
"min-width": 150
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "操作",
|
|
||||||
align: "center",
|
|
||||||
type: "op"
|
|
||||||
}
|
}
|
||||||
]
|
},
|
||||||
}
|
{
|
||||||
};
|
prop: "remark",
|
||||||
},
|
label: "备注",
|
||||||
|
span: 24,
|
||||||
|
component: {
|
||||||
|
name: "el-input",
|
||||||
|
props: {
|
||||||
|
placeholder: "请填写备注",
|
||||||
|
type: "textarea",
|
||||||
|
rows: 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "功能权限",
|
||||||
|
prop: "menuIdList",
|
||||||
|
value: [],
|
||||||
|
component: {
|
||||||
|
name: "cl-role-perms"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "数据权限",
|
||||||
|
prop: "departmentIdList",
|
||||||
|
value: [],
|
||||||
|
component: {
|
||||||
|
name: "cl-dept-check"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
methods: {
|
// 表格配置
|
||||||
onLoad({ ctx, app }) {
|
const table = reactive<Table>({
|
||||||
ctx.service(this.$service.system.role).done();
|
props: {
|
||||||
|
"default-sort": {
|
||||||
|
prop: "createTime",
|
||||||
|
order: "descending"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
type: "selection",
|
||||||
|
width: 60
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: "name",
|
||||||
|
label: "名称",
|
||||||
|
minWidth: 150
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: "label",
|
||||||
|
label: "标识",
|
||||||
|
minWidth: 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: "remark",
|
||||||
|
label: "备注",
|
||||||
|
showOverflowTooltip: true,
|
||||||
|
minWidth: 150
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: "createTime",
|
||||||
|
label: "创建时间",
|
||||||
|
sortable: "custom",
|
||||||
|
minWidth: 150
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: "updateTime",
|
||||||
|
label: "更新时间",
|
||||||
|
sortable: "custom",
|
||||||
|
minWidth: 150
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "操作",
|
||||||
|
type: "op"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
// crud 加载
|
||||||
|
function onLoad({ ctx, app }: CrudLoad) {
|
||||||
|
ctx.service($service.system.role).done();
|
||||||
app.refresh();
|
app.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
form,
|
||||||
|
upsert,
|
||||||
|
table,
|
||||||
|
onLoad
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -22,7 +22,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<cl-crud ref="crud" :on-refresh="onRefresh" @load="onLoad">
|
<cl-crud :ref="setRefs('crud')" :on-refresh="onRefresh" @load="onLoad">
|
||||||
<el-row type="flex">
|
<el-row type="flex">
|
||||||
<cl-refresh-btn></cl-refresh-btn>
|
<cl-refresh-btn></cl-refresh-btn>
|
||||||
<cl-add-btn></cl-add-btn>
|
<cl-add-btn></cl-add-btn>
|
||||||
@ -35,13 +35,12 @@
|
|||||||
@click="toMove()"
|
@click="toMove()"
|
||||||
>转移</el-button
|
>转移</el-button
|
||||||
>
|
>
|
||||||
<cl-flex1></cl-flex1>
|
|
||||||
<cl-search-key></cl-search-key>
|
<cl-search-key></cl-search-key>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<el-row>
|
<el-row>
|
||||||
<cl-table
|
<cl-table
|
||||||
ref="table"
|
:ref="setRefs('table')"
|
||||||
v-bind="table"
|
v-bind="table"
|
||||||
@selection-change="onSelectionChange"
|
@selection-change="onSelectionChange"
|
||||||
>
|
>
|
||||||
@ -50,7 +49,7 @@
|
|||||||
<cl-avatar
|
<cl-avatar
|
||||||
shape="square"
|
shape="square"
|
||||||
size="medium"
|
size="medium"
|
||||||
:src="scope.row.headImg | default_avatar"
|
:src="scope.row.headImg"
|
||||||
:style="{ margin: 'auto' }"
|
:style="{ margin: 'auto' }"
|
||||||
>
|
>
|
||||||
</cl-avatar>
|
</cl-avatar>
|
||||||
@ -88,327 +87,349 @@
|
|||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<cl-upsert
|
<cl-upsert
|
||||||
ref="upsert"
|
:ref="setRefs('upsert')"
|
||||||
:items="upsert.items"
|
:items="upsert.items"
|
||||||
:on-submit="onUpsertSubmit"
|
:on-submit="onUpsertSubmit"
|
||||||
></cl-upsert>
|
>
|
||||||
|
<template #slot-tips>
|
||||||
|
<div>
|
||||||
|
<i class="el-icon-warning"></i>
|
||||||
|
<span style="margin-left: 6px">新增用户默认密码为:123456</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</cl-upsert>
|
||||||
</cl-crud>
|
</cl-crud>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 部门移动 -->
|
<!-- 部门移动 -->
|
||||||
<cl-dept-move ref="dept-move" @success="refresh({ page: 1 })"></cl-dept-move>
|
<cl-dept-move :ref="setRefs('dept-move')" @success="refresh({ page: 1 })"></cl-dept-move>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { mapGetters } from "vuex";
|
import { computed, inject, reactive, ref, watch } from "vue";
|
||||||
|
import { useStore } from "vuex";
|
||||||
|
import { useRefs } from "@/core";
|
||||||
|
import { Table, Upsert } from "@/crud/types";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
name: "sys-user",
|
||||||
return {
|
|
||||||
isExpand: true,
|
setup() {
|
||||||
selects: {
|
const $service = inject<any>("$service");
|
||||||
dept: {},
|
const store = useStore();
|
||||||
ids: []
|
const { refs, setRefs } = useRefs();
|
||||||
|
|
||||||
|
// 是否展开
|
||||||
|
const isExpand = ref<boolean>(true);
|
||||||
|
|
||||||
|
// 选择项
|
||||||
|
const selects = reactive<any>({
|
||||||
|
dept: {},
|
||||||
|
ids: []
|
||||||
|
});
|
||||||
|
|
||||||
|
// 部门列表
|
||||||
|
const dept = ref<any[]>([]);
|
||||||
|
|
||||||
|
// 表格配置
|
||||||
|
const table = reactive<Table>({
|
||||||
|
props: {
|
||||||
|
"default-sort": {
|
||||||
|
prop: "createTime",
|
||||||
|
order: "descending"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
dept: [],
|
columns: [
|
||||||
table: {
|
{
|
||||||
props: {
|
type: "selection",
|
||||||
"default-sort": {
|
width: 60
|
||||||
prop: "createTime",
|
},
|
||||||
order: "descending"
|
{
|
||||||
|
prop: "headImg",
|
||||||
|
label: "头像"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: "name",
|
||||||
|
label: "姓名",
|
||||||
|
minWidth: 150
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: "username",
|
||||||
|
label: "用户名",
|
||||||
|
minWidth: 150
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: "nickName",
|
||||||
|
label: "昵称",
|
||||||
|
minWidth: 150
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: "departmentName",
|
||||||
|
label: "部门名称",
|
||||||
|
minWidth: 150
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: "roleName",
|
||||||
|
label: "角色",
|
||||||
|
headerAlign: "center",
|
||||||
|
minWidth: 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: "phone",
|
||||||
|
label: "手机号码",
|
||||||
|
minWidth: 150
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: "remark",
|
||||||
|
label: "备注",
|
||||||
|
minWidth: 150
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: "status",
|
||||||
|
label: "状态",
|
||||||
|
minWidth: 120,
|
||||||
|
dict: [
|
||||||
|
{
|
||||||
|
label: "启用",
|
||||||
|
value: 1,
|
||||||
|
type: "success"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "禁用",
|
||||||
|
value: 0,
|
||||||
|
type: "danger"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: "createTime",
|
||||||
|
label: "创建时间",
|
||||||
|
sortable: "custom",
|
||||||
|
minWidth: 150
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "op",
|
||||||
|
buttons: ["slot-move-btn", "edit", "delete"],
|
||||||
|
width: 160
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
// 新增、编辑配置
|
||||||
|
const upsert = reactive<Upsert>({
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
prop: "headImg",
|
||||||
|
label: "头像",
|
||||||
|
span: 24,
|
||||||
|
component: {
|
||||||
|
name: "cl-upload",
|
||||||
|
props: {
|
||||||
|
text: "选择头像",
|
||||||
|
icon: "el-icon-picture"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
columns: [
|
{
|
||||||
{
|
prop: "name",
|
||||||
type: "selection",
|
label: "姓名",
|
||||||
width: 60
|
span: 24,
|
||||||
|
component: {
|
||||||
|
name: "el-input",
|
||||||
|
props: {
|
||||||
|
placeholder: "请填写姓名"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
rules: {
|
||||||
prop: "headImg",
|
required: true,
|
||||||
label: "头像"
|
message: "姓名不能为空"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: "nickName",
|
||||||
|
label: "昵称",
|
||||||
|
span: 12,
|
||||||
|
component: {
|
||||||
|
name: "el-input",
|
||||||
|
props: {
|
||||||
|
placeholder: "请填写昵称"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
rules: {
|
||||||
prop: "name",
|
required: true,
|
||||||
label: "姓名",
|
message: "昵称不能为空"
|
||||||
"min-width": 150
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: "username",
|
||||||
|
label: "用户名",
|
||||||
|
span: 12,
|
||||||
|
component: {
|
||||||
|
name: "el-input",
|
||||||
|
props: {
|
||||||
|
placeholder: "请填写用户名"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
rules: [
|
||||||
prop: "username",
|
{
|
||||||
label: "用户名",
|
required: true,
|
||||||
"min-width": 150
|
message: "用户名不能为空"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: "password",
|
||||||
|
label: "密码",
|
||||||
|
span: 12,
|
||||||
|
hidden: ":isAdd",
|
||||||
|
component: {
|
||||||
|
name: "el-input",
|
||||||
|
props: {
|
||||||
|
placeholder: "请填写密码",
|
||||||
|
type: "password"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
rules: [
|
||||||
prop: "nickName",
|
{
|
||||||
label: "昵称",
|
min: 6,
|
||||||
"min-width": 150
|
max: 16,
|
||||||
|
message: "密码长度在 6 到 16 个字符"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: "roleIdList",
|
||||||
|
label: "角色",
|
||||||
|
span: 24,
|
||||||
|
value: [],
|
||||||
|
component: {
|
||||||
|
name: "cl-role-select",
|
||||||
|
props: {
|
||||||
|
props: {
|
||||||
|
"multiple-limit": 3
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
rules: {
|
||||||
prop: "departmentName",
|
required: true,
|
||||||
label: "部门名称",
|
message: "角色不能为空"
|
||||||
"min-width": 150
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
prop: "roleName",
|
prop: "phone",
|
||||||
label: "角色",
|
label: "手机号码",
|
||||||
"header-align": "center",
|
span: 12,
|
||||||
"min-width": 200
|
component: {
|
||||||
},
|
name: "el-input",
|
||||||
{
|
props: {
|
||||||
prop: "phone",
|
placeholder: "请填写手机号码"
|
||||||
label: "手机号码",
|
}
|
||||||
"min-width": 150
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
prop: "remark",
|
prop: "email",
|
||||||
label: "备注",
|
label: "邮箱",
|
||||||
"min-width": 150
|
span: 12,
|
||||||
},
|
component: {
|
||||||
{
|
name: "el-input",
|
||||||
prop: "status",
|
props: {
|
||||||
label: "状态",
|
placeholder: "请填写邮箱"
|
||||||
"min-width": 120,
|
}
|
||||||
dict: [
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: "remark",
|
||||||
|
label: "备注",
|
||||||
|
span: 24,
|
||||||
|
component: {
|
||||||
|
name: "el-input",
|
||||||
|
props: {
|
||||||
|
placeholder: "请填写备注",
|
||||||
|
type: "textarea",
|
||||||
|
rows: 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: "status",
|
||||||
|
label: "状态",
|
||||||
|
value: 1,
|
||||||
|
component: {
|
||||||
|
name: "el-radio-group",
|
||||||
|
options: [
|
||||||
{
|
{
|
||||||
label: "启用",
|
label: "开启",
|
||||||
value: 1,
|
value: 1
|
||||||
type: "success"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "禁用",
|
label: "关闭",
|
||||||
value: 0,
|
value: 0
|
||||||
type: "danger"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: "createTime",
|
|
||||||
label: "创建时间",
|
|
||||||
sortable: "custom",
|
|
||||||
"min-width": 150
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "op",
|
|
||||||
buttons: ["slot-move-btn", "edit", "delete"],
|
|
||||||
width: 160
|
|
||||||
}
|
}
|
||||||
]
|
},
|
||||||
|
{
|
||||||
|
prop: "tips",
|
||||||
|
hidden: ":isEdit",
|
||||||
|
component: {
|
||||||
|
name: "slot-tips"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
// 浏览器信息
|
||||||
|
const browser = computed(() => store.getters.browser);
|
||||||
|
|
||||||
|
// 监听屏幕大小变化
|
||||||
|
watch(
|
||||||
|
() => browser.value.isMini,
|
||||||
|
(val: boolean) => {
|
||||||
|
isExpand.value = !val;
|
||||||
},
|
},
|
||||||
upsert: {
|
{
|
||||||
items: [
|
immediate: true
|
||||||
{
|
|
||||||
prop: "headImg",
|
|
||||||
label: "头像",
|
|
||||||
span: 24,
|
|
||||||
component: {
|
|
||||||
name: "cl-upload",
|
|
||||||
props: {
|
|
||||||
text: "选择头像",
|
|
||||||
icon: "el-icon-picture"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: "name",
|
|
||||||
label: "姓名",
|
|
||||||
span: 24,
|
|
||||||
component: {
|
|
||||||
name: "el-input",
|
|
||||||
attrs: {
|
|
||||||
placeholder: "请填写姓名"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
rules: {
|
|
||||||
required: true,
|
|
||||||
message: "姓名不能为空"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: "nickName",
|
|
||||||
label: "昵称",
|
|
||||||
span: 12,
|
|
||||||
component: {
|
|
||||||
name: "el-input",
|
|
||||||
attrs: {
|
|
||||||
placeholder: "请填写昵称"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
rules: {
|
|
||||||
required: true,
|
|
||||||
message: "昵称不能为空"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: "username",
|
|
||||||
label: "用户名",
|
|
||||||
span: 12,
|
|
||||||
component: {
|
|
||||||
name: "el-input",
|
|
||||||
attrs: {
|
|
||||||
placeholder: "请填写用户名"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: "用户名不能为空"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: "password",
|
|
||||||
label: "密码",
|
|
||||||
span: 12,
|
|
||||||
hidden: ":isAdd",
|
|
||||||
component: {
|
|
||||||
name: "el-input",
|
|
||||||
attrs: {
|
|
||||||
placeholder: "请填写密码",
|
|
||||||
type: "password"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
min: 6,
|
|
||||||
max: 16,
|
|
||||||
message: "密码长度在 6 到 16 个字符"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: "roleIdList",
|
|
||||||
label: "角色",
|
|
||||||
span: 24,
|
|
||||||
value: [],
|
|
||||||
component: {
|
|
||||||
name: "cl-role-select",
|
|
||||||
props: {
|
|
||||||
props: {
|
|
||||||
"multiple-limit": 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
rules: {
|
|
||||||
required: true,
|
|
||||||
message: "角色不能为空"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: "phone",
|
|
||||||
label: "手机号码",
|
|
||||||
span: 12,
|
|
||||||
component: {
|
|
||||||
name: "el-input",
|
|
||||||
attrs: {
|
|
||||||
placeholder: "请填写手机号码"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: "email",
|
|
||||||
label: "邮箱",
|
|
||||||
span: 12,
|
|
||||||
component: {
|
|
||||||
name: "el-input",
|
|
||||||
attrs: {
|
|
||||||
placeholder: "请填写邮箱"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: "remark",
|
|
||||||
label: "备注",
|
|
||||||
span: 24,
|
|
||||||
component: {
|
|
||||||
name: "el-input",
|
|
||||||
props: {
|
|
||||||
type: "textarea",
|
|
||||||
rows: 4
|
|
||||||
},
|
|
||||||
attrs: {
|
|
||||||
placeholder: "请填写备注"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: "status",
|
|
||||||
label: "状态",
|
|
||||||
value: 1,
|
|
||||||
component: {
|
|
||||||
name: "el-radio-group",
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
label: "开启",
|
|
||||||
value: 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "关闭",
|
|
||||||
value: 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: "tips",
|
|
||||||
hidden: ":isEdit",
|
|
||||||
component: (
|
|
||||||
<div>
|
|
||||||
<i class="el-icon-warning"></i>
|
|
||||||
<span style="margin-left: 6px">新增用户默认密码为:123456</span>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
};
|
);
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
// crud 加载
|
||||||
...mapGetters(["browser"])
|
function onLoad({ ctx, app }: any) {
|
||||||
},
|
ctx.service($service.system.user).done();
|
||||||
|
|
||||||
watch: {
|
|
||||||
"browser.isMini": {
|
|
||||||
immediate: true,
|
|
||||||
handler(val) {
|
|
||||||
this.isExpand = !val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
refresh(params) {
|
|
||||||
this.$refs["crud"].refresh(params);
|
|
||||||
},
|
|
||||||
|
|
||||||
onLoad({ ctx, app }) {
|
|
||||||
ctx.service(this.$service.system.user).done();
|
|
||||||
app.refresh();
|
app.refresh();
|
||||||
},
|
}
|
||||||
|
|
||||||
async onRefresh(params, { next, render }) {
|
// 刷新列表
|
||||||
let { list } = await next(params);
|
function refresh(params: any) {
|
||||||
|
refs.value.crud.refresh(params);
|
||||||
|
}
|
||||||
|
|
||||||
list.map(e => {
|
// 刷新监听
|
||||||
|
async function onRefresh(params: any, { next, render }: any) {
|
||||||
|
const { list } = await next(params);
|
||||||
|
|
||||||
|
list.map((e: any) => {
|
||||||
if (e.roleName) {
|
if (e.roleName) {
|
||||||
this.$set(e, "roleNameList", e.roleName.split(","));
|
e.roleNameList = e.roleName.split(",");
|
||||||
}
|
}
|
||||||
|
|
||||||
e.status = Boolean(e.status);
|
e.status = Boolean(e.status);
|
||||||
});
|
});
|
||||||
|
|
||||||
render(list);
|
render(list);
|
||||||
},
|
}
|
||||||
|
|
||||||
onUpsertSubmit(_, data, { next }) {
|
// 提交钩子
|
||||||
|
function onUpsertSubmit(_: boolean, data: any, { next }: any) {
|
||||||
let departmentId = data.departmentId;
|
let departmentId = data.departmentId;
|
||||||
|
|
||||||
if (!departmentId) {
|
if (!departmentId) {
|
||||||
departmentId = this.selects.dept.id;
|
departmentId = selects.dept.id;
|
||||||
|
|
||||||
if (!departmentId) {
|
if (!departmentId) {
|
||||||
departmentId = this.dept[0].id;
|
departmentId = dept.value[0].id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -416,51 +437,78 @@ export default {
|
|||||||
...data,
|
...data,
|
||||||
departmentId
|
departmentId
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
onSelectionChange(selection) {
|
// 多选监听
|
||||||
this.selects.ids = selection.map(e => e.id);
|
function onSelectionChange(selection: any[]) {
|
||||||
},
|
selects.ids = selection.map(e => e.id);
|
||||||
|
}
|
||||||
|
|
||||||
onDeptRowClick({ item, ids }) {
|
// 部门选择监听
|
||||||
this.selects.dept = item;
|
function onDeptRowClick({ item, ids }: any) {
|
||||||
|
selects.dept = item;
|
||||||
|
|
||||||
this.refresh({
|
refresh({
|
||||||
page: 1,
|
page: 1,
|
||||||
departmentIds: ids
|
departmentIds: ids
|
||||||
});
|
});
|
||||||
|
|
||||||
// 收起
|
// 收起
|
||||||
if (this.browser.isMini) {
|
if (browser.value.isMini) {
|
||||||
this.isExpand = false;
|
isExpand.value = false;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
onDeptUserAdd(item) {
|
// 部门下新增成员
|
||||||
this.$refs["crud"].rowAppend({
|
function onDeptUserAdd(item: any) {
|
||||||
|
refs.value.crud.rowAppend({
|
||||||
departmentId: item.id
|
departmentId: item.id
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
onDeptListChange(list) {
|
// 部门列表监听
|
||||||
this.dept = list;
|
function onDeptListChange(list: any[]) {
|
||||||
},
|
dept.value = list;
|
||||||
|
}
|
||||||
|
|
||||||
deptExpand() {
|
// 是否显示部门
|
||||||
this.isExpand = !this.isExpand;
|
function deptExpand() {
|
||||||
},
|
isExpand.value = !isExpand.value;
|
||||||
|
}
|
||||||
|
|
||||||
async toMove(e) {
|
// 移动成员
|
||||||
|
async function toMove(e: any) {
|
||||||
let ids = [];
|
let ids = [];
|
||||||
|
|
||||||
if (!e) {
|
if (!e) {
|
||||||
ids = this.selects.ids;
|
ids = selects.ids;
|
||||||
} else {
|
} else {
|
||||||
ids = [e.id];
|
ids = [e.id];
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$refs["dept-move"].toMove(ids);
|
refs.value["dept-move"].toMove(ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
refs,
|
||||||
|
isExpand,
|
||||||
|
selects,
|
||||||
|
dept,
|
||||||
|
table,
|
||||||
|
upsert,
|
||||||
|
browser,
|
||||||
|
setRefs,
|
||||||
|
onLoad,
|
||||||
|
refresh,
|
||||||
|
onRefresh,
|
||||||
|
onUpsertSubmit,
|
||||||
|
onSelectionChange,
|
||||||
|
onDeptRowClick,
|
||||||
|
onDeptUserAdd,
|
||||||
|
onDeptListChange,
|
||||||
|
deptExpand,
|
||||||
|
toMove
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
<div class="cl-chat__wrap">
|
<div class="cl-chat__wrap">
|
||||||
<!-- 聊天窗口 -->
|
<!-- 聊天窗口 -->
|
||||||
<cl-dialog
|
<cl-dialog
|
||||||
:visible.sync="visible"
|
v-model="visible"
|
||||||
:title="title"
|
:title="title"
|
||||||
:height="height"
|
:height="height"
|
||||||
:width="width"
|
:width="width"
|
||||||
@ -12,7 +12,7 @@
|
|||||||
'append-to-body': true,
|
'append-to-body': true,
|
||||||
'close-on-click-modal': false
|
'close-on-click-modal': false
|
||||||
}"
|
}"
|
||||||
:controls="['slot-expand', 'cl-flex1', 'fullscreen', 'close']"
|
:controls="['slot-session', 'cl-flex1', 'fullscreen', 'close']"
|
||||||
>
|
>
|
||||||
<div class="cl-chat">
|
<div class="cl-chat">
|
||||||
<!-- 会话列表 -->
|
<!-- 会话列表 -->
|
||||||
@ -29,38 +29,40 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 展开按钮 -->
|
<template #slot-session>
|
||||||
<template #slot-expand>
|
|
||||||
<button v-if="session">
|
<button v-if="session">
|
||||||
<i
|
<i class="el-icon-notebook-2" v-if="sessionVisible" @click="closeSession()"></i>
|
||||||
class="el-icon-notebook-2"
|
<i class="el-icon-arrow-left" v-else @click="openSession()"></i>
|
||||||
v-if="sessionVisible"
|
|
||||||
@click="CLOSE_SESSION()"
|
|
||||||
></i>
|
|
||||||
<i class="el-icon-arrow-left" v-else @click="OPEN_SESSION()"></i>
|
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
</cl-dialog>
|
</cl-dialog>
|
||||||
|
|
||||||
<!-- MP3 -->
|
<!-- MP3 -->
|
||||||
<div class="mp3">
|
<div class="mp3">
|
||||||
<audio style="display: none" ref="sound" src="../static/notify.mp3" controls></audio>
|
<audio
|
||||||
|
style="display: none"
|
||||||
|
:ref="setRefs('sound')"
|
||||||
|
src="../static/notify.mp3"
|
||||||
|
controls
|
||||||
|
></audio>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts">
|
||||||
|
import { computed, defineComponent, h, inject, onUnmounted, provide, ref } from "vue";
|
||||||
|
import { useStore } from "vuex";
|
||||||
|
import { ElNotification } from "element-plus";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { mapGetters, mapMutations } from "vuex";
|
// import io from "socket.io-client";
|
||||||
import io from "socket.io-client";
|
// import { socketUrl } from "@/config/env";
|
||||||
import { socketUrl } from "@/config/env";
|
import Session from "./session.vue";
|
||||||
import Session from "./session";
|
import Message from "./message.vue";
|
||||||
import Message from "./message";
|
import Input from "./input.vue";
|
||||||
import Input from "./input";
|
|
||||||
import eventBus from "../utils/event-bus";
|
|
||||||
import { parseContent } from "../utils";
|
import { parseContent } from "../utils";
|
||||||
|
import { useRefs } from "@/core";
|
||||||
|
|
||||||
export default {
|
export default defineComponent({
|
||||||
name: "cl-chat",
|
name: "cl-chat",
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
@ -80,98 +82,126 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
setup(_, { emit }) {
|
||||||
return {
|
const store = useStore();
|
||||||
modes: ["text", "image", "emoji", "voice", "video"], // 消息类型
|
const { refs, setRefs } = useRefs();
|
||||||
visible: false,
|
const $service = inject<any>("$service");
|
||||||
socket: null
|
const mitt = inject<any>("mitt");
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
provide() {
|
// 当前会话
|
||||||
return {
|
const session = computed(() => store.getters.session);
|
||||||
chat: this
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
// 会话列表是否可见
|
||||||
...mapGetters(["token", "session", "sessionList", "sessionVisible"]),
|
const sessionVisible = computed(() => store.getters.sessionVisible);
|
||||||
|
|
||||||
title() {
|
// 消息类型
|
||||||
return this.session ? `与 ${this.session.nickname} 聊天中` : "聊天对话框";
|
const modes = ["text", "image", "emoji", "voice", "video"];
|
||||||
|
|
||||||
|
// 是否可见
|
||||||
|
const visible = ref<boolean>(false);
|
||||||
|
|
||||||
|
// socket 实例
|
||||||
|
const socket: any = null;
|
||||||
|
|
||||||
|
// 对话框标题
|
||||||
|
const title = computed(() => {
|
||||||
|
return session.value ? `与 ${session.value.nickname} 聊天中` : "聊天对话框";
|
||||||
|
});
|
||||||
|
|
||||||
|
// 打开
|
||||||
|
function open() {
|
||||||
|
visible.value = true;
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
// 关闭
|
||||||
// this.socket = io(`${socketUrl}?isAdmin=true&token=${token}`);
|
function close() {
|
||||||
// this.socket.on("connect", () => {
|
visible.value = false;
|
||||||
// console.log("socket connect");
|
|
||||||
// });
|
|
||||||
// this.socket.on("admin", msg => {
|
|
||||||
// this.onMessage(msg);
|
|
||||||
// });
|
|
||||||
// this.socket.on("error", err => {
|
|
||||||
// console.log(err);
|
|
||||||
// });
|
|
||||||
// this.socket.on("disconnect", () => {
|
|
||||||
// console.log("disconnect connect");
|
|
||||||
// });
|
|
||||||
},
|
|
||||||
|
|
||||||
destroyed() {
|
|
||||||
if (this.socket) {
|
|
||||||
this.socket.close();
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
// 打开会话列表
|
||||||
...mapMutations(["OPEN_SESSION", "CLOSE_SESSION", "UPDATE_SESSION"]),
|
function openSession() {
|
||||||
|
store.commit("OPEN_SESSION");
|
||||||
|
}
|
||||||
|
|
||||||
open() {
|
// 关闭会话列表
|
||||||
this.visible = true;
|
function closeSession() {
|
||||||
},
|
store.commit("CLOSE_SESSION");
|
||||||
|
}
|
||||||
|
|
||||||
close() {
|
// 消息通知
|
||||||
this.visible = false;
|
function notification(msg: string) {
|
||||||
},
|
const { _text } = parseContent(JSON.parse(msg));
|
||||||
|
|
||||||
|
// 播放音乐
|
||||||
|
if (refs.value.sound) {
|
||||||
|
refs.value.sound.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!visible.value) {
|
||||||
|
// 页面消息提示
|
||||||
|
ElNotification({
|
||||||
|
title: "提示",
|
||||||
|
message: h("span", _text)
|
||||||
|
});
|
||||||
|
|
||||||
|
// 浏览器消息通知
|
||||||
|
const NotificationInstance = Notification || window.Notification;
|
||||||
|
if (NotificationInstance) {
|
||||||
|
if (NotificationInstance.permission !== "denied") {
|
||||||
|
NotificationInstance.requestPermission(() => {
|
||||||
|
const n = new Notification("COOL-MALL", {
|
||||||
|
body: _text,
|
||||||
|
icon: "/favicon.ico"
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
n.close();
|
||||||
|
}, 2000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 监听消息
|
// 监听消息
|
||||||
onMessage(msg) {
|
function onMessage(msg: string) {
|
||||||
// 回调
|
// 回调
|
||||||
this.$emit("message", msg);
|
emit("message", msg);
|
||||||
|
|
||||||
// 消息通知
|
// 消息通知
|
||||||
this.notification(msg);
|
notification(msg);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { contentType, fromId, content, msgId } = JSON.parse(msg);
|
const { contentType, fromId, content, msgId } = JSON.parse(msg);
|
||||||
|
|
||||||
// 是否当前
|
// 是否当前
|
||||||
const same = this.session && this.session.userId == fromId;
|
const same = session.value && session.value.userId == fromId;
|
||||||
|
|
||||||
if (same) {
|
if (same) {
|
||||||
// 更新消息
|
// 更新消息
|
||||||
this.UPDATE_SESSION({
|
store.commit("UPDATE_SESSION", {
|
||||||
contentType,
|
contentType,
|
||||||
content
|
content
|
||||||
});
|
});
|
||||||
|
|
||||||
// 追加消息
|
// 追加消息
|
||||||
this.$store.commit("APPEND_MESSAGE_LIST", {
|
store.commit("APPEND_MESSAGE_LIST", {
|
||||||
contentType,
|
contentType,
|
||||||
content: JSON.parse(content),
|
content: JSON.parse(content),
|
||||||
type: 1
|
type: 1
|
||||||
});
|
});
|
||||||
|
|
||||||
|
mitt.emit("message.scrollToBottom");
|
||||||
|
|
||||||
// 阅读消息
|
// 阅读消息
|
||||||
this.$service.im.message.read({
|
$service.im.message.read({
|
||||||
ids: [msgId],
|
ids: [msgId],
|
||||||
session: this.session.id
|
session: session.value.id
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查找会话
|
// 查找会话
|
||||||
const item = this.sessionList.find(e => e.userId == fromId);
|
const item = store.getters.sessionList.find((e: any) => e.userId == fromId);
|
||||||
|
|
||||||
if (item) {
|
if (item) {
|
||||||
if (!same) {
|
if (!same) {
|
||||||
@ -185,57 +215,64 @@ export default {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// 刷新会话列表
|
// 刷新会话列表
|
||||||
eventBus.$emit("session.refresh");
|
mitt.emit("session.refresh");
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("消息格式异常", e);
|
console.error("消息格式异常", e);
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
// 消息通知
|
|
||||||
notification(msg) {
|
|
||||||
const { _text } = parseContent(JSON.parse(msg));
|
|
||||||
|
|
||||||
// 播放音乐
|
|
||||||
if (this.$refs.sound) {
|
|
||||||
this.$refs.sound.play();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.visible) {
|
|
||||||
// 页面消息提示
|
|
||||||
this.$notify({
|
|
||||||
title: "提示",
|
|
||||||
message: this.$createElement("span", _text)
|
|
||||||
});
|
|
||||||
|
|
||||||
// 浏览器消息通知
|
|
||||||
const NotificationInstance = Notification || window.Notification;
|
|
||||||
if (!!NotificationInstance) {
|
|
||||||
if (NotificationInstance.permission !== "denied") {
|
|
||||||
NotificationInstance.requestPermission(status => {
|
|
||||||
let n = new Notification("COOL-MALL", {
|
|
||||||
body: _text,
|
|
||||||
icon: "/favicon.ico"
|
|
||||||
});
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
n.close();
|
|
||||||
}, 2000);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 加载 socket
|
||||||
|
(function() {
|
||||||
|
// socket = io(`${socketUrl}?isAdmin=true&token=${store.getters.token}`);
|
||||||
|
// socket.on("connect", () => {
|
||||||
|
// console.log("socket connect");
|
||||||
|
// });
|
||||||
|
// socket.on("admin", msg => {
|
||||||
|
// onMessage(msg);
|
||||||
|
// });
|
||||||
|
// socket.on("error", err => {
|
||||||
|
// console.log(err);
|
||||||
|
// });
|
||||||
|
// socket.on("disconnect", () => {
|
||||||
|
// console.log("disconnect connect");
|
||||||
|
// });
|
||||||
|
})();
|
||||||
|
|
||||||
|
// 共享参数
|
||||||
|
provide("chat", {
|
||||||
|
modes,
|
||||||
|
socket
|
||||||
|
});
|
||||||
|
|
||||||
|
// 销毁
|
||||||
|
onUnmounted(function() {
|
||||||
|
if (socket) {
|
||||||
|
socket.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
refs,
|
||||||
|
session,
|
||||||
|
sessionVisible,
|
||||||
|
visible,
|
||||||
|
title,
|
||||||
|
setRefs,
|
||||||
|
open,
|
||||||
|
close,
|
||||||
|
openSession,
|
||||||
|
closeSession,
|
||||||
|
onMessage
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.cl-chat__dialog {
|
.cl-chat__dialog {
|
||||||
.el-dialog {
|
.el-dialog__body {
|
||||||
&__body {
|
padding: 0 !important;
|
||||||
padding: 0 !important;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,30 +1,36 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-popover
|
<div>
|
||||||
v-model="visible"
|
<el-popover
|
||||||
placement="top"
|
:visible="visible"
|
||||||
:width="popoverWidth"
|
:width="popoverWidth"
|
||||||
trigger="click"
|
placement="top"
|
||||||
popper-class="popover-emoji"
|
trigger="click"
|
||||||
>
|
popper-class="popper-emoji"
|
||||||
<div class="tool-emoji">
|
>
|
||||||
<div class="tool-emoji__scroller scroller1">
|
<div class="tool-emoji">
|
||||||
<div
|
<div class="tool-emoji__scroller scroller1">
|
||||||
class="tool-emoji__item"
|
<div
|
||||||
v-for="(item, index) in list"
|
class="tool-emoji__item"
|
||||||
:key="index"
|
v-for="(item, index) in list"
|
||||||
@click="select(item)"
|
:key="index"
|
||||||
>
|
@click="select(item)"
|
||||||
<img :src="item" />
|
>
|
||||||
|
<img :src="item" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<img slot="reference" src="../static/images/emoji.png" alt="" />
|
<template #reference>
|
||||||
</el-popover>
|
<img src="../static/images/emoji.png" alt="" @click="open" />
|
||||||
|
</template>
|
||||||
|
</el-popover>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { mapGetters } from "vuex";
|
import { computed, defineComponent, ref } from "vue";
|
||||||
|
import { useStore } from "vuex";
|
||||||
|
|
||||||
// 表情列表
|
// 表情列表
|
||||||
const emoji = {
|
const emoji = {
|
||||||
url: "https://cool-comm.oss-cn-shenzhen.aliyuncs.com/show/imgs/chat/",
|
url: "https://cool-comm.oss-cn-shenzhen.aliyuncs.com/show/imgs/chat/",
|
||||||
@ -120,34 +126,50 @@ const emoji = {
|
|||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export default defineComponent({
|
||||||
data() {
|
setup(_, { emit }) {
|
||||||
|
const store = useStore();
|
||||||
|
|
||||||
|
// 是否可见
|
||||||
|
const visible = ref<boolean>(false);
|
||||||
|
|
||||||
|
// 表情列表
|
||||||
|
const list = ref<any[]>(emoji.list.map(e => emoji.url + e));
|
||||||
|
|
||||||
|
// 弹窗宽度
|
||||||
|
const popoverWidth = computed(() => {
|
||||||
|
const { width } = store.getters.browser;
|
||||||
|
return (width > 500 ? 500 : width) - 24;
|
||||||
|
});
|
||||||
|
|
||||||
|
function open() {
|
||||||
|
visible.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function close() {
|
||||||
|
visible.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function select(e: any) {
|
||||||
|
emit("select", e);
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
visible: false,
|
visible,
|
||||||
list: emoji.list.map(e => emoji.url + e)
|
list,
|
||||||
|
popoverWidth,
|
||||||
|
open,
|
||||||
|
close,
|
||||||
|
select
|
||||||
};
|
};
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
...mapGetters(["browser"]),
|
|
||||||
|
|
||||||
popoverWidth() {
|
|
||||||
return (this.browser.width > 500 ? 500 : this.browser.width) - 24;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
select(e) {
|
|
||||||
this.$emit("select", e);
|
|
||||||
this.visible = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.popover-emoji {
|
.popper-emoji {
|
||||||
padding: 5px;
|
padding: 5px !important;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +0,0 @@
|
|||||||
import Notice from "./notice";
|
|
||||||
import Chat from "./chat";
|
|
||||||
|
|
||||||
export default { Notice, Chat };
|
|
||||||
4
src/cool/modules/chat/components/index.ts
Normal file
4
src/cool/modules/chat/components/index.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import Notice from "./notice.vue";
|
||||||
|
import Chat from "./chat.vue";
|
||||||
|
|
||||||
|
export default { Notice, Chat };
|
||||||
@ -58,7 +58,7 @@
|
|||||||
type="textarea"
|
type="textarea"
|
||||||
resize="none"
|
resize="none"
|
||||||
:rows="5"
|
:rows="5"
|
||||||
@keyup.enter.native="onTextSend"
|
@keyup.enter="onTextSend"
|
||||||
></el-input>
|
></el-input>
|
||||||
|
|
||||||
<el-button type="primary" size="mini" :disabled="!text" @click="onTextSend"
|
<el-button type="primary" size="mini" :disabled="!text" @click="onTextSend"
|
||||||
@ -68,33 +68,63 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { mapMutations } from "vuex";
|
import { defineComponent, inject, nextTick, reactive, ref } from "vue";
|
||||||
import Emoji from "./emoji";
|
import { useStore } from "vuex";
|
||||||
|
import Emoji from "./emoji.vue";
|
||||||
|
|
||||||
export default {
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
Emoji
|
Emoji
|
||||||
},
|
},
|
||||||
|
|
||||||
inject: ["chat"],
|
setup() {
|
||||||
|
const store = useStore();
|
||||||
|
const chat = inject<any>("chat");
|
||||||
|
const mitt = inject<any>("mitt");
|
||||||
|
|
||||||
data() {
|
// 输入值
|
||||||
return {
|
const text = ref<string>("");
|
||||||
text: "",
|
|
||||||
emoji: {
|
// 表情
|
||||||
visible: false
|
const emoji = reactive<any>({
|
||||||
|
visible: false
|
||||||
|
});
|
||||||
|
|
||||||
|
// 追加消息
|
||||||
|
function append(data: any) {
|
||||||
|
store.commit("APPEND_MESSAGE_LIST", data);
|
||||||
|
mitt.emit("message.scrollToBottom");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送消息
|
||||||
|
function send(data: any, isAppend?: boolean) {
|
||||||
|
const { id, userId } = store.getters.session;
|
||||||
|
|
||||||
|
// 格式化内容
|
||||||
|
data.content = JSON.stringify(data.content);
|
||||||
|
|
||||||
|
// 更新消息
|
||||||
|
store.commit("UPDATE_SESSION", data);
|
||||||
|
|
||||||
|
if (chat.socket) {
|
||||||
|
chat.socket.emit(`user@${userId}`, {
|
||||||
|
contentType: data.contentType,
|
||||||
|
type: 0,
|
||||||
|
content: data.content,
|
||||||
|
sessionId: id
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
if (isAppend) {
|
||||||
...mapMutations(["UPDATE_SESSION", "UPDATE_MESSAGE", "APPEND_MESSAGE_LIST"]),
|
append(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 上传前,获取图片预览地址
|
// 上传前,获取图片预览地址
|
||||||
onBeforeUpload(file, key) {
|
function onBeforeUpload(file: any, key: string) {
|
||||||
// 先添加到列表中,等待上传
|
// 先添加到列表中,等待上传
|
||||||
const next = (options = {}) => {
|
function next(options = {}) {
|
||||||
const data = {
|
const data = {
|
||||||
content: {
|
content: {
|
||||||
[`${key}Url`]: ""
|
[`${key}Url`]: ""
|
||||||
@ -103,18 +133,18 @@ export default {
|
|||||||
uid: file.uid,
|
uid: file.uid,
|
||||||
loading: true,
|
loading: true,
|
||||||
progress: "0%",
|
progress: "0%",
|
||||||
contentType: this.chat.modes.indexOf(key),
|
contentType: chat.modes.indexOf(key),
|
||||||
...options
|
...options
|
||||||
};
|
};
|
||||||
|
|
||||||
this.append(data);
|
append(data);
|
||||||
};
|
}
|
||||||
|
|
||||||
// 图片预览
|
// 图片预览
|
||||||
if (key == "image") {
|
if (key == "image") {
|
||||||
const fileReader = new FileReader();
|
const fileReader = new FileReader();
|
||||||
|
|
||||||
fileReader.onload = e => {
|
fileReader.onload = (e: any) => {
|
||||||
const imageUrl = e.target.result;
|
const imageUrl = e.target.result;
|
||||||
const image = new Image();
|
const image = new Image();
|
||||||
|
|
||||||
@ -145,21 +175,21 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
// 上传中
|
// 上传中
|
||||||
onUploadProgress(e, file) {
|
function onUploadProgress(e: any, file: any) {
|
||||||
this.UPDATE_MESSAGE({
|
store.commit("UPDATE_MESSAGE", {
|
||||||
file,
|
file,
|
||||||
data: {
|
data: {
|
||||||
progress: e.percent + "%"
|
progress: e.percent + "%"
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
// 上传成功
|
// 上传成功
|
||||||
onUploadSuccess(res, file, key) {
|
function onUploadSuccess(res: any, file: any, key: string) {
|
||||||
this.UPDATE_MESSAGE({
|
store.commit("UPDATE_MESSAGE", {
|
||||||
file,
|
file,
|
||||||
data: {
|
data: {
|
||||||
loading: false,
|
loading: false,
|
||||||
@ -167,34 +197,34 @@ export default {
|
|||||||
[`${key}Url`]: res.data
|
[`${key}Url`]: res.data
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
callback: this.send
|
callback: send
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
// 发送文本内容
|
// 发送文本内容
|
||||||
onTextSend() {
|
function onTextSend() {
|
||||||
if (this.text) {
|
if (text.value) {
|
||||||
if (this.text.replace(/\n/g, "") !== "") {
|
if (text.value.replace(/\n/g, "") !== "") {
|
||||||
const data = {
|
const data = {
|
||||||
type: 0,
|
type: 0,
|
||||||
contentType: 0,
|
contentType: 0,
|
||||||
content: {
|
content: {
|
||||||
text: this.text
|
text: text.value
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.send(data, true);
|
send(data, true);
|
||||||
|
|
||||||
this.$nextTick(() => {
|
nextTick(() => {
|
||||||
this.text = "";
|
text.value = "";
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
// 图片选择
|
// 图片选择
|
||||||
onImageSelect(res) {
|
function onImageSelect(res: any) {
|
||||||
this.send(
|
send(
|
||||||
{
|
{
|
||||||
content: {
|
content: {
|
||||||
imageUrl: res.data
|
imageUrl: res.data
|
||||||
@ -204,12 +234,12 @@ export default {
|
|||||||
},
|
},
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
|
|
||||||
// 表情选择
|
// 表情选择
|
||||||
onEmojiSelect(url) {
|
function onEmojiSelect(url: string) {
|
||||||
this.emoji.visible = false;
|
emoji.visible = false;
|
||||||
this.send(
|
send(
|
||||||
{
|
{
|
||||||
content: {
|
content: {
|
||||||
imageUrl: url
|
imageUrl: url
|
||||||
@ -219,11 +249,11 @@ export default {
|
|||||||
},
|
},
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
|
|
||||||
// 视频选择
|
// 视频选择
|
||||||
onVideoSelect(url) {
|
function onVideoSelect(url: string) {
|
||||||
this.send(
|
send(
|
||||||
{
|
{
|
||||||
content: {
|
content: {
|
||||||
videoUrl: url
|
videoUrl: url
|
||||||
@ -233,37 +263,22 @@ export default {
|
|||||||
},
|
},
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
},
|
|
||||||
|
|
||||||
// 发送消息
|
|
||||||
send(data, isAppend) {
|
|
||||||
const { id, userId } = this.$store.getters.session;
|
|
||||||
|
|
||||||
// 更新会话消息
|
|
||||||
this.UPDATE_SESSION(data);
|
|
||||||
|
|
||||||
// 发送消息
|
|
||||||
if (this.chat.socket) {
|
|
||||||
this.chat.socket.emit(`user@${userId}`, {
|
|
||||||
contentType: data.contentType,
|
|
||||||
type: 0,
|
|
||||||
content: JSON.stringify(data.content),
|
|
||||||
sessionId: id
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 是否添加到列表中
|
|
||||||
if (isAppend) {
|
|
||||||
this.append(data);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 追加消息
|
|
||||||
append(data) {
|
|
||||||
this.APPEND_MESSAGE_LIST(data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
text,
|
||||||
|
emoji,
|
||||||
|
send,
|
||||||
|
onBeforeUpload,
|
||||||
|
onUploadProgress,
|
||||||
|
onUploadSuccess,
|
||||||
|
onTextSend,
|
||||||
|
onImageSelect,
|
||||||
|
onEmojiSelect,
|
||||||
|
onVideoSelect
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@ -287,7 +302,7 @@ export default {
|
|||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
|
|
||||||
/deep/ img {
|
:deep(img) {
|
||||||
height: 26px;
|
height: 26px;
|
||||||
width: 26px;
|
width: 26px;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
<div class="cl-chat-message" v-loading="!visible && loading" element-loading-text="消息加载中">
|
<div class="cl-chat-message" v-loading="!visible && loading" element-loading-text="消息加载中">
|
||||||
<div
|
<div
|
||||||
class="cl-chat-message__scroller scroller1"
|
class="cl-chat-message__scroller scroller1"
|
||||||
ref="scroller"
|
:ref="setRefs('scroller')"
|
||||||
:style="{
|
:style="{
|
||||||
opacity: visible ? 1 : 0
|
opacity: visible ? 1 : 0
|
||||||
}"
|
}"
|
||||||
@ -76,16 +76,14 @@
|
|||||||
<!-- 语音 -->
|
<!-- 语音 -->
|
||||||
<template v-else-if="item.mode === 'voice'">
|
<template v-else-if="item.mode === 'voice'">
|
||||||
<icon-voice :play="item.isPlay"></icon-voice>
|
<icon-voice :play="item.isPlay"></icon-voice>
|
||||||
<span class="duration"
|
<span class="duration">{{ item.content.duration }}"</span>
|
||||||
>{{ item.content.duration | duration }}"</span
|
|
||||||
>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 视频 -->
|
<!-- 视频 -->
|
||||||
<template v-else-if="item.mode === 'video'">
|
<template v-else-if="item.mode === 'video'">
|
||||||
<div class="item">
|
<div class="item">
|
||||||
<video
|
<video
|
||||||
:poster="item.content.videoUrl | video_poster"
|
:poster="item.content.videoUrl"
|
||||||
:src="item.content.videoUrl"
|
:src="item.content.videoUrl"
|
||||||
controls
|
controls
|
||||||
></video>
|
></video>
|
||||||
@ -111,52 +109,61 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts">
|
||||||
|
import { computed, defineComponent, inject, nextTick, onUnmounted, reactive, ref } from "vue";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { mapGetters } from "vuex";
|
import { ElMessage } from "element-plus";
|
||||||
import { isString } from "cl-admin/utils";
|
import { useStore } from "vuex";
|
||||||
import eventBus from "../utils/event-bus";
|
import { isString } from "@/core/utils";
|
||||||
import IconVoice from "./icon-voice";
|
import IconVoice from "./icon-voice.vue";
|
||||||
|
import { useRefs } from "@/core";
|
||||||
|
|
||||||
export default {
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
IconVoice
|
IconVoice
|
||||||
},
|
},
|
||||||
|
|
||||||
inject: ["chat"],
|
setup() {
|
||||||
|
const store = useStore();
|
||||||
|
const { refs, setRefs } = useRefs();
|
||||||
|
const $service = inject<any>("$service");
|
||||||
|
const chat = inject<any>("chat");
|
||||||
|
const mitt = inject<any>("mitt");
|
||||||
|
|
||||||
data() {
|
// 当前会话信息
|
||||||
return {
|
const session = computed(() => store.getters.session);
|
||||||
loading: false,
|
|
||||||
visible: false,
|
|
||||||
pagination: {
|
|
||||||
page: 1,
|
|
||||||
size: 20,
|
|
||||||
total: 0
|
|
||||||
},
|
|
||||||
voice: {
|
|
||||||
url: "",
|
|
||||||
timer: null
|
|
||||||
},
|
|
||||||
refreshRd: null
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
filters: {
|
// 加载状态
|
||||||
duration(val) {
|
const loading = ref<boolean>(false);
|
||||||
return Math.ceil((val || 1) / 1000);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
// 是否可见
|
||||||
...mapGetters(["userInfo", "session", "messageList"]),
|
const visible = ref<boolean>(false);
|
||||||
|
|
||||||
|
// 分页信息
|
||||||
|
const pagination = reactive<any>({
|
||||||
|
page: 1,
|
||||||
|
size: 20,
|
||||||
|
total: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
// 语音
|
||||||
|
const voice = reactive<any>({
|
||||||
|
url: "",
|
||||||
|
timer: null
|
||||||
|
});
|
||||||
|
|
||||||
|
// 请求随机值
|
||||||
|
const refreshRd = ref<any>(null);
|
||||||
|
|
||||||
|
// 消息列表
|
||||||
|
const list = computed(() => {
|
||||||
|
const { userInfo, messageList } = store.getters;
|
||||||
|
|
||||||
list() {
|
|
||||||
let date = "";
|
let date = "";
|
||||||
|
|
||||||
return this.messageList.map(e => {
|
return messageList.map((e: any) => {
|
||||||
// 时间间隔
|
// 时间间隔
|
||||||
e._date = date
|
const _date = date
|
||||||
? dayjs(e.createTime).isBefore(dayjs(date).add(1, "minute"))
|
? dayjs(e.createTime).isBefore(dayjs(date).add(1, "minute"))
|
||||||
? ""
|
? ""
|
||||||
: e.createTime
|
: e.createTime
|
||||||
@ -170,126 +177,115 @@ export default {
|
|||||||
e = JSON.parse(e);
|
e = JSON.parse(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isString(e.content)) {
|
// 内容
|
||||||
e.content = JSON.parse(e.content);
|
const content = isString(e.content) ? JSON.parse(e.content) : e.content;
|
||||||
}
|
|
||||||
|
|
||||||
// 解析昵称
|
// 昵称
|
||||||
const nickName = e.type == 0 ? this.userInfo.nickName : this.session.nickname;
|
const nickName = e.type == 0 ? userInfo.nickName : session.value.nickname;
|
||||||
|
|
||||||
// 解析头像
|
// 头像
|
||||||
const avatarUrl =
|
const avatarUrl =
|
||||||
e.type == 0
|
e.type == 0
|
||||||
? this.userInfo.avatarUrl || require("../static/images/custom-avatar.png")
|
? userInfo.avatarUrl || require("../static/images/custom-avatar.png")
|
||||||
: this.session.headimgurl;
|
: session.value.headimgurl;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...e,
|
...e,
|
||||||
|
_date,
|
||||||
|
content,
|
||||||
avatarUrl,
|
avatarUrl,
|
||||||
nickName,
|
nickName,
|
||||||
mode: this.chat.modes[e.contentType]
|
mode: chat.modes[e.contentType]
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
beforeCreate() {
|
|
||||||
// 销毁事件
|
|
||||||
eventBus.$off("message.refresh");
|
|
||||||
eventBus.$off("message.scrollToBottom");
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
|
||||||
// 监听列表刷新
|
|
||||||
eventBus.$on("message.refresh", this.refresh);
|
|
||||||
|
|
||||||
// 滚动到底部
|
|
||||||
eventBus.$on("message.scrollToBottom", this.scrollToBottom);
|
|
||||||
},
|
|
||||||
|
|
||||||
destroyed() {
|
|
||||||
// 清除播放
|
|
||||||
clearTimeout(this.voice.timer);
|
|
||||||
|
|
||||||
this.messageList.map(e => {
|
|
||||||
e.isPlay = false;
|
|
||||||
});
|
});
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
// 点击
|
// 点击
|
||||||
onTap(item) {
|
function onTap(item: any) {
|
||||||
// 播放语音
|
// 播放语音
|
||||||
if (item.mode == "voice") {
|
if (item.mode == "voice") {
|
||||||
this.messageList.map(e => {
|
store.getters.messageList.map((e: any) => {
|
||||||
this.$set(e, "isPlay", e.id == item.id ? e.isPlay : false);
|
e.isPlay = e.id == item.id ? e.isPlay : false;
|
||||||
});
|
});
|
||||||
|
|
||||||
item.isPlay = !item.isPlay;
|
item.isPlay = !item.isPlay;
|
||||||
|
|
||||||
if (item.isPlay) {
|
if (item.isPlay) {
|
||||||
this.voice.url = item.content.voiceUrl;
|
voice.url = item.content.voiceUrl;
|
||||||
|
|
||||||
this.$nextTick(() => {
|
nextTick(() => {
|
||||||
this.$refs["voice"].play();
|
refs.value.voice.play();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.$refs["voice"].pause();
|
refs.value.voice.pause();
|
||||||
item.isPlay = false;
|
item.isPlay = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
clearTimeout(this.voice.timer);
|
clearTimeout(voice.timer);
|
||||||
|
|
||||||
this.voice.timer = setTimeout(() => {
|
voice.timer = setTimeout(() => {
|
||||||
item.isPlay = false;
|
item.isPlay = false;
|
||||||
}, item.content.duration);
|
}, item.content.duration);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
|
// 滚动到底部
|
||||||
|
function scrollToBottom() {
|
||||||
|
nextTick(() => {
|
||||||
|
if (refs.value.scroller) {
|
||||||
|
refs.value.scroller.scrollTo({
|
||||||
|
top: 99999,
|
||||||
|
behavior: visible.value ? "smooth" : "auto"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 刷新列表
|
// 刷新列表
|
||||||
refresh(params) {
|
function refresh(params?: any) {
|
||||||
// 请求随机值
|
// 请求随机值
|
||||||
const rd = (this.refreshRd = Math.random());
|
const rd = (refreshRd.value = Math.random());
|
||||||
|
|
||||||
// 请求参数
|
// 请求参数
|
||||||
const data = {
|
const data = {
|
||||||
...this.pagination,
|
...pagination,
|
||||||
...params,
|
...params,
|
||||||
sessionId: this.session.id,
|
sessionId: session.value.id,
|
||||||
order: "createTime",
|
order: "createTime",
|
||||||
sort: "desc"
|
sort: "desc"
|
||||||
};
|
};
|
||||||
|
|
||||||
// 加载动画
|
// 加载动画
|
||||||
this.loading = true;
|
loading.value = true;
|
||||||
|
|
||||||
// 首页处理
|
// 首页处理
|
||||||
if (data.page === 1) {
|
if (data.page === 1) {
|
||||||
this.visible = false;
|
visible.value = false;
|
||||||
this.$store.commit("CLEAR_MESSAGE_LIST");
|
store.commit("CLEAR_MESSAGE_LIST");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 完成
|
// 完成
|
||||||
const done = () => {
|
const done = () => {
|
||||||
this.loading = false;
|
loading.value = false;
|
||||||
this.visible = true;
|
visible.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.$service.im.message
|
$service.im.message
|
||||||
.page(data)
|
.page(data)
|
||||||
.then(res => {
|
.then((res: any) => {
|
||||||
// 防止脏数据
|
// 防止脏数据
|
||||||
if (rd != this.refreshRd) {
|
if (rd != refreshRd.value) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 分页信息
|
// 分页信息
|
||||||
this.pagination = res.pagination;
|
Object.assign(pagination, res.pagination);
|
||||||
|
|
||||||
// 追加数据
|
// 追加数据
|
||||||
this.$store.commit("PREPEND_MESSAGE_LIST", res.list);
|
store.commit("PREPEND_MESSAGE_LIST", res.list);
|
||||||
|
|
||||||
if (data.page === 1) {
|
if (data.page === 1) {
|
||||||
this.scrollToBottom();
|
scrollToBottom();
|
||||||
|
|
||||||
// 首次滚动隐藏
|
// 首次滚动隐藏
|
||||||
setTimeout(done, 0);
|
setTimeout(done, 0);
|
||||||
@ -297,30 +293,53 @@ export default {
|
|||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch((err: string) => {
|
||||||
this.$message.error(err);
|
ElMessage.error(err);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
// 加载更多
|
// 加载更多
|
||||||
onLoadmore() {
|
function onLoadmore() {
|
||||||
this.refresh({ page: this.pagination.page + 1 });
|
refresh({ page: pagination.page + 1 });
|
||||||
},
|
|
||||||
|
|
||||||
// 滚动到底部
|
|
||||||
scrollToBottom() {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
if (this.$refs["scroller"]) {
|
|
||||||
this.$refs["scroller"].scrollTo({
|
|
||||||
top: 99999,
|
|
||||||
behavior: this.visible ? "smooth" : "auto"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 监听列表刷新
|
||||||
|
mitt.on("message.refresh", refresh);
|
||||||
|
// 滚动到底部
|
||||||
|
mitt.on("message.scrollToBottom", scrollToBottom);
|
||||||
|
|
||||||
|
// 销毁
|
||||||
|
onUnmounted(function() {
|
||||||
|
// 移除语音播放
|
||||||
|
clearTimeout(voice.timer);
|
||||||
|
|
||||||
|
list.value.map((e: any) => {
|
||||||
|
e.isPlay = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 移除事件
|
||||||
|
mitt.off("message.refresh", refresh);
|
||||||
|
mitt.off("message.scrollToBottom", scrollToBottom);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
refs,
|
||||||
|
chat,
|
||||||
|
session,
|
||||||
|
loading,
|
||||||
|
visible,
|
||||||
|
pagination,
|
||||||
|
voice,
|
||||||
|
list,
|
||||||
|
setRefs,
|
||||||
|
onTap,
|
||||||
|
refresh,
|
||||||
|
onLoadmore,
|
||||||
|
scrollToBottom
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@ -494,7 +513,7 @@ export default {
|
|||||||
.content {
|
.content {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
|
|
||||||
/deep/.el-image {
|
:deep(.el-image) {
|
||||||
display: block;
|
display: block;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
max-width: 200px;
|
max-width: 200px;
|
||||||
|
|||||||
@ -51,7 +51,7 @@ export default {
|
|||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/deep/.el-badge {
|
:deep(.el-badge) {
|
||||||
transform: scale(0.8);
|
transform: scale(0.8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,7 +15,7 @@
|
|||||||
size="small"
|
size="small"
|
||||||
clearable
|
clearable
|
||||||
@clear="onSearch"
|
@clear="onSearch"
|
||||||
@keyup.enter.native="onSearch"
|
@keyup.enter="onSearch"
|
||||||
></el-input>
|
></el-input>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -52,79 +52,128 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { mapGetters, mapMutations } from "vuex";
|
import { computed, defineComponent, inject, onUnmounted, reactive, ref } from "vue";
|
||||||
import { isEmpty } from "cl-admin/utils";
|
import { useStore } from "vuex";
|
||||||
import { ContextMenu } from "cl-admin-crud";
|
import { ElMessage } from "element-plus";
|
||||||
|
import { isEmpty } from "@/core/utils";
|
||||||
|
import { ContextMenu } from "@/crud";
|
||||||
import { parseContent } from "../utils";
|
import { parseContent } from "../utils";
|
||||||
import eventBus from "../utils/event-bus";
|
|
||||||
|
|
||||||
export default {
|
export default defineComponent({
|
||||||
data() {
|
setup() {
|
||||||
return {
|
const store = useStore();
|
||||||
loading: false,
|
const $service = inject<any>("$service");
|
||||||
pagination: {
|
const mitt = inject<any>("mitt");
|
||||||
page: 1,
|
|
||||||
size: 100,
|
|
||||||
total: 0
|
|
||||||
},
|
|
||||||
keyWord: ""
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
// 当前会话信息
|
||||||
...mapGetters(["sessionList", "session", "browser", "sessionVisible"]),
|
const session = computed(() => store.getters.session);
|
||||||
|
// 是否显示会话列表
|
||||||
|
const sessionVisible = computed(() => store.getters.sessionVisible);
|
||||||
|
// 浏览器信息
|
||||||
|
const browser = computed(() => store.getters.browser);
|
||||||
|
|
||||||
// 列表数据
|
// 加载状态
|
||||||
list() {
|
const loading = ref<boolean>(false);
|
||||||
return this.sessionList
|
|
||||||
.map(e => {
|
// 分页信息
|
||||||
|
const pagination = reactive<any>({
|
||||||
|
page: 1,
|
||||||
|
size: 100,
|
||||||
|
total: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
// 关键字筛选
|
||||||
|
const keyWord = ref<string>("");
|
||||||
|
|
||||||
|
// 刷新列表
|
||||||
|
function refresh(params?: any) {
|
||||||
|
loading.value = true;
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
$service.im.session
|
||||||
|
.page({
|
||||||
|
...pagination,
|
||||||
|
keyWord: keyWord.value,
|
||||||
|
params,
|
||||||
|
order: "updateTime",
|
||||||
|
sort: "desc"
|
||||||
|
})
|
||||||
|
.then((res: any) => {
|
||||||
|
store.commit("SET_SESSION_LIST", res.list);
|
||||||
|
Object.assign(pagination, res.pagination);
|
||||||
|
|
||||||
|
resolve(res);
|
||||||
|
})
|
||||||
|
.catch((err: string) => {
|
||||||
|
ElMessage.error(err);
|
||||||
|
reject(err);
|
||||||
|
})
|
||||||
|
.done(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索关键字
|
||||||
|
function onSearch() {
|
||||||
|
refresh({ page: 1 });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置会话
|
||||||
|
function setSession(item: any) {
|
||||||
|
if (item) {
|
||||||
|
store.commit("SET_SESSION", item);
|
||||||
|
mitt.emit("message.refresh", { page: 1 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 会话详情
|
||||||
|
function toDetail(item?: any) {
|
||||||
|
if (item) {
|
||||||
|
// 点击关闭弹窗
|
||||||
|
if (browser.value.isMini) store.commit("CLOSE_SESSION");
|
||||||
|
|
||||||
|
// 设置为当前会话
|
||||||
|
if (!session.value || session.value.id != item.id) {
|
||||||
|
setSession(item);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
store.commit("CLEAR_SESSION");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 数据列表
|
||||||
|
const list = computed(() => {
|
||||||
|
return store.getters.sessionList
|
||||||
|
.map((e: any) => {
|
||||||
const { _text } = parseContent(e);
|
const { _text } = parseContent(e);
|
||||||
e.lastMessage = _text;
|
return {
|
||||||
return e;
|
...e,
|
||||||
|
lastMessage: _text
|
||||||
|
};
|
||||||
})
|
})
|
||||||
.sort((a, b) => {
|
.sort((a: any, b: any) => {
|
||||||
return a.updateTime < b.updateTime ? 1 : -1;
|
return a.updateTime < b.updateTime ? 1 : -1;
|
||||||
});
|
});
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
beforeCreate() {
|
|
||||||
// 销毁事件
|
|
||||||
eventBus.$off("session.refresh");
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
|
||||||
// 监听列表刷新
|
|
||||||
eventBus.$on("session.refresh", this.refresh);
|
|
||||||
|
|
||||||
// PC 端下首次请求读取第一个消息
|
|
||||||
this.refresh().then(res => {
|
|
||||||
if (!isEmpty(res.list) && !this.browser.isMini) {
|
|
||||||
this.SET_SESSION(res.list[0]);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
...mapMutations(["SET_SESSION_LIST", "SET_SESSION", "CLEAR_SESSION", "CLOSE_SESSION"]),
|
|
||||||
|
|
||||||
// 右键菜单
|
// 右键菜单
|
||||||
openCM(e, id, index) {
|
function openCM(e: any, id: any, index: number) {
|
||||||
ContextMenu.open(e, {
|
ContextMenu.open(e, {
|
||||||
list: [
|
list: [
|
||||||
{
|
{
|
||||||
label: "删除",
|
label: "删除",
|
||||||
icon: "el-icon-delete",
|
icon: "el-icon-delete",
|
||||||
callback: (_, done) => {
|
callback: (_: any, done: Function) => {
|
||||||
this.$service.im.session.delete({
|
$service.im.session.delete({
|
||||||
ids: id
|
ids: id
|
||||||
});
|
});
|
||||||
|
|
||||||
this.list.splice(index, 1);
|
list.value.splice(index, 1);
|
||||||
|
|
||||||
if (id == this.session.id) {
|
if (id == session.value.id) {
|
||||||
this.toDetail();
|
toDetail();
|
||||||
}
|
}
|
||||||
|
|
||||||
done();
|
done();
|
||||||
@ -132,58 +181,38 @@ export default {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
},
|
|
||||||
|
|
||||||
// 刷新列表
|
|
||||||
refresh(params) {
|
|
||||||
this.loading = true;
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
this.$service.im.session
|
|
||||||
.page({
|
|
||||||
...this.pagination,
|
|
||||||
keyWord: this.keyWord,
|
|
||||||
params,
|
|
||||||
order: "updateTime",
|
|
||||||
sort: "desc"
|
|
||||||
})
|
|
||||||
.then(res => {
|
|
||||||
this.SET_SESSION_LIST(res.list);
|
|
||||||
this.pagination = res.pagination;
|
|
||||||
|
|
||||||
resolve(res);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
this.$message.error(err);
|
|
||||||
reject(err);
|
|
||||||
})
|
|
||||||
.done(() => {
|
|
||||||
this.loading = false;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// 搜索关键字
|
|
||||||
onSearch() {
|
|
||||||
this.refresh({ page: 1 });
|
|
||||||
},
|
|
||||||
|
|
||||||
// 会话详情
|
|
||||||
toDetail(item) {
|
|
||||||
if (item) {
|
|
||||||
// 点击关闭弹窗
|
|
||||||
if (this.browser.isMini) this.CLOSE_SESSION();
|
|
||||||
|
|
||||||
// 设置为当前会话
|
|
||||||
if (!this.session || this.session.id != item.id) {
|
|
||||||
this.SET_SESSION(item);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.CLEAR_SESSION();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PC 端下首次请求读取第一个消息
|
||||||
|
refresh().then((res: any) => {
|
||||||
|
if (!isEmpty(res.list) && !browser.value.isMini) {
|
||||||
|
setSession(res.list[0]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 事件监听
|
||||||
|
mitt.on("session.refresh", refresh);
|
||||||
|
|
||||||
|
// 销毁
|
||||||
|
onUnmounted(function() {
|
||||||
|
mitt.off("session.refresh", refresh);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
session,
|
||||||
|
sessionVisible,
|
||||||
|
browser,
|
||||||
|
list,
|
||||||
|
loading,
|
||||||
|
pagination,
|
||||||
|
keyWord,
|
||||||
|
openCM,
|
||||||
|
refresh,
|
||||||
|
onSearch,
|
||||||
|
toDetail
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { BaseService, Service, Permission } from "cl-admin";
|
import { BaseService, Service, Permission } from "@/core";
|
||||||
|
|
||||||
@Service({
|
@Service({
|
||||||
namespace: "im/message",
|
namespace: "im/message",
|
||||||
@ -6,7 +6,7 @@ import { BaseService, Service, Permission } from "cl-admin";
|
|||||||
})
|
})
|
||||||
class ImMessage extends BaseService {
|
class ImMessage extends BaseService {
|
||||||
@Permission("read")
|
@Permission("read")
|
||||||
read(data) {
|
read(data: any) {
|
||||||
return this.request({
|
return this.request({
|
||||||
url: "/read",
|
url: "/read",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import { BaseService, Service, Permission } from "cl-admin";
|
import { BaseService, Service, Permission } from "@/core";
|
||||||
|
|
||||||
@Service({
|
@Service({
|
||||||
namespace: "im/session",
|
namespace: "im/session",
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user