mirror of
https://github.com/cool-team-official/cool-admin-vue.git
synced 2026-03-26 07:13:03 +00:00
发布6.0
This commit is contained in:
parent
76c7045cf8
commit
43f37ca564
@ -1,2 +0,0 @@
|
|||||||
> 1%
|
|
||||||
last 2 versions
|
|
||||||
@ -1,21 +1,5 @@
|
|||||||
.DS_Store
|
|
||||||
node_modules
|
node_modules
|
||||||
/dist
|
.DS_Store
|
||||||
|
dist
|
||||||
# local env files
|
dist-ssr
|
||||||
.env.local
|
*.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 @@
|
|||||||
/public/
|
vite.config.ts
|
||||||
/dist/
|
|
||||||
/node_modules/
|
|
||||||
/src/icons/svg/
|
|
||||||
/mock/
|
|
||||||
vue.config.js
|
|
||||||
66
.eslintrc.js
66
.eslintrc.js
@ -1,14 +1,66 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
root: true,
|
root: true,
|
||||||
env: {
|
env: {
|
||||||
node: true
|
browser: true,
|
||||||
},
|
node: true,
|
||||||
extends: ["plugin:vue/essential", "@vue/prettier"],
|
es6: true
|
||||||
rules: {
|
|
||||||
"no-console": "off",
|
|
||||||
"comma-dangle": [2, "never"]
|
|
||||||
},
|
},
|
||||||
|
parser: "vue-eslint-parser",
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
parser: "@typescript-eslint/parser"
|
parser: "@typescript-eslint/parser",
|
||||||
|
ecmaVersion: 2020,
|
||||||
|
sourceType: "module",
|
||||||
|
jsxPragma: "React",
|
||||||
|
ecmaFeatures: {
|
||||||
|
jsx: true,
|
||||||
|
tsx: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
extends: [
|
||||||
|
"plugin:vue/vue3-recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended",
|
||||||
|
"prettier",
|
||||||
|
"plugin:prettier/recommended"
|
||||||
|
],
|
||||||
|
rules: {
|
||||||
|
"@typescript-eslint/ban-ts-ignore": "off",
|
||||||
|
"@typescript-eslint/explicit-function-return-type": "off",
|
||||||
|
"@typescript-eslint/no-explicit-any": "off",
|
||||||
|
"@typescript-eslint/no-var-requires": "off",
|
||||||
|
"@typescript-eslint/no-empty-function": "off",
|
||||||
|
"vue/component-name-in-template-casing": ["error", "kebab-case"],
|
||||||
|
"vue/component-definition-name-casing": ["error", "kebab-case"],
|
||||||
|
"no-use-before-define": "off",
|
||||||
|
"@typescript-eslint/no-use-before-define": "off",
|
||||||
|
"@typescript-eslint/ban-ts-comment": "off",
|
||||||
|
"@typescript-eslint/ban-types": "off",
|
||||||
|
"@typescript-eslint/no-non-null-assertion": "off",
|
||||||
|
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||||
|
"@typescript-eslint/no-unused-vars": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
argsIgnorePattern: "^h$",
|
||||||
|
varsIgnorePattern: "^h$"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"no-unused-vars": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
argsIgnorePattern: "^h$",
|
||||||
|
varsIgnorePattern: "^h$"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"space-before-function-paren": "off",
|
||||||
|
"vue/attributes-order": "off",
|
||||||
|
"vue/one-component-per-file": "off",
|
||||||
|
"vue/html-closing-bracket-newline": "off",
|
||||||
|
"vue/max-attributes-per-line": "off",
|
||||||
|
"vue/multiline-html-element-content-newline": "off",
|
||||||
|
"vue/multi-word-component-names": "off",
|
||||||
|
"vue/singleline-html-element-content-newline": "off",
|
||||||
|
"vue/attribute-hyphenation": "off",
|
||||||
|
"vue/html-self-closing": "off",
|
||||||
|
"vue/require-default-prop": "off",
|
||||||
|
"vue/v-on-event-hyphenation": "off"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
4
.gitattributes
vendored
Normal file
4
.gitattributes
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
*.js text eol=lf
|
||||||
|
*.json text eol=lf
|
||||||
|
*.ts text eol=lf
|
||||||
|
*.vue text eol=lf
|
||||||
24
.gitignore
vendored
24
.gitignore
vendored
@ -1,20 +1,6 @@
|
|||||||
.DS_Store
|
|
||||||
node_modules
|
node_modules
|
||||||
/dist
|
.DS_Store
|
||||||
|
dist
|
||||||
# local env files
|
dist-ssr
|
||||||
.env.local
|
*.local
|
||||||
.env.*.local
|
pnpm-lock.yaml
|
||||||
|
|
||||||
# Log files
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
|
|
||||||
# Editor directories and files
|
|
||||||
.idea
|
|
||||||
*.suo
|
|
||||||
*.ntvs*
|
|
||||||
*.njsproj
|
|
||||||
*.sln
|
|
||||||
*.sw?
|
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
"tabWidth": 4,
|
"tabWidth": 4,
|
||||||
"useTabs": true,
|
"useTabs": true,
|
||||||
"semi": true,
|
"semi": true,
|
||||||
"jsxBracketSameLine": true,
|
|
||||||
"singleQuote": false,
|
"singleQuote": false,
|
||||||
"printWidth": 100,
|
"printWidth": 100,
|
||||||
"trailingComma": "none"
|
"trailingComma": "none"
|
||||||
|
|||||||
15
.vscode/config.code-snippets
vendored
Normal file
15
.vscode/config.code-snippets
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"module-config": {
|
||||||
|
"prefix": "module-config",
|
||||||
|
"scope": "typescript",
|
||||||
|
"body": [
|
||||||
|
"import { ModuleConfig } from \"/@/cool\";",
|
||||||
|
"",
|
||||||
|
"export default (): ModuleConfig => {",
|
||||||
|
" return {};",
|
||||||
|
"};",
|
||||||
|
""
|
||||||
|
],
|
||||||
|
"description": "module config snippets"
|
||||||
|
}
|
||||||
|
}
|
||||||
61
.vscode/crud.code-snippets
vendored
61
.vscode/crud.code-snippets
vendored
@ -1,10 +1,11 @@
|
|||||||
{
|
{
|
||||||
"cl-crud": {
|
"cl-crud": {
|
||||||
"prefix": "cl-crud",
|
"prefix": "cl-crud",
|
||||||
|
"scope": "vue",
|
||||||
"body": [
|
"body": [
|
||||||
"<template>",
|
"<template>",
|
||||||
" <cl-crud ref=\"crud\" @load=\"onLoad\">",
|
" <cl-crud ref=\"Crud\">",
|
||||||
" <el-row type=\"flex\" align=\"middle\">",
|
" <cl-row>",
|
||||||
" <!-- 刷新按钮 -->",
|
" <!-- 刷新按钮 -->",
|
||||||
" <cl-refresh-btn />",
|
" <cl-refresh-btn />",
|
||||||
" <!-- 新增按钮 -->",
|
" <!-- 新增按钮 -->",
|
||||||
@ -14,45 +15,49 @@
|
|||||||
" <cl-flex1 />",
|
" <cl-flex1 />",
|
||||||
" <!-- 关键字搜索 -->",
|
" <!-- 关键字搜索 -->",
|
||||||
" <cl-search-key />",
|
" <cl-search-key />",
|
||||||
" </el-row>",
|
" </cl-row>",
|
||||||
"",
|
"",
|
||||||
" <el-row>",
|
" <cl-row>",
|
||||||
" <!-- 数据表格 -->",
|
" <!-- 数据表格 -->",
|
||||||
" <cl-table v-bind=\"table\"></cl-table>",
|
" <cl-table ref=\"Table\" />",
|
||||||
" </el-row>",
|
" </cl-row>",
|
||||||
"",
|
"",
|
||||||
" <el-row type=\"flex\">",
|
" <cl-row>",
|
||||||
" <cl-flex1 />",
|
" <cl-flex1 />",
|
||||||
" <!-- 分页控件 -->",
|
" <!-- 分页控件 -->",
|
||||||
" <cl-pagination />",
|
" <cl-pagination />",
|
||||||
" </el-row>",
|
" </cl-row>",
|
||||||
"",
|
"",
|
||||||
" <!-- 新增、编辑 -->",
|
" <!-- 新增、编辑 -->",
|
||||||
" <cl-upsert ref=\"upsert\" v-bind=\"upsert\"></cl-upsert>",
|
" <cl-upsert ref=\"Upsert\" />",
|
||||||
" </cl-crud>",
|
" </cl-crud>",
|
||||||
"</template>",
|
"</template>",
|
||||||
"",
|
"",
|
||||||
"<script>",
|
"<script lang=\"ts\" name=\"菜单名称\" setup>",
|
||||||
"export default {",
|
"import { useCrud, useTable, useUpsert } from \"@cool-vue/crud\";",
|
||||||
" data() {",
|
"import { useCool } from \"/@/cool\";",
|
||||||
" return {",
|
"",
|
||||||
" // 新增、编辑配置",
|
"const { service } = useCool();",
|
||||||
" upsert: {",
|
"",
|
||||||
" items: []",
|
"// cl-upsert",
|
||||||
" },",
|
"const Upsert = useUpsert({",
|
||||||
" // 表格配置",
|
" items: []",
|
||||||
" table: {",
|
"});",
|
||||||
" columns: []",
|
"",
|
||||||
" }",
|
"// cl-table",
|
||||||
" };",
|
"const Table = useTable({",
|
||||||
|
" columns: []",
|
||||||
|
"});",
|
||||||
|
"",
|
||||||
|
"// cl-crud",
|
||||||
|
"const Crud = useCrud(",
|
||||||
|
" {",
|
||||||
|
" service: service.demo.goods",
|
||||||
" },",
|
" },",
|
||||||
" methods: {",
|
" (app) => {",
|
||||||
" onLoad({ ctx, app }) {",
|
" app.refresh();",
|
||||||
" ctx.service(${1}).done();",
|
|
||||||
" app.refresh();",
|
|
||||||
" }",
|
|
||||||
" }",
|
" }",
|
||||||
"};",
|
");",
|
||||||
"</script>",
|
"</script>",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
|||||||
4
.vscode/settings.json
vendored
Normal file
4
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"editor.cursorSmoothCaretAnimation": "on",
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
}
|
||||||
@ -1,11 +1,11 @@
|
|||||||
FROM node:lts-alpine
|
FROM node:lts-alpine
|
||||||
WORKDIR /build
|
WORKDIR /build
|
||||||
# 设置Node-Sass的镜像地址
|
# 设置Node-Sass的镜像地址
|
||||||
RUN npm config set sass_binary_site https://repo.huaweicloud.com/node-sass
|
RUN npm config set sass_binary_site=https://npm.taobao.org/mirrors/node-sass/
|
||||||
# 设置npm镜像
|
# 设置npm镜像
|
||||||
RUN npm config set registry https://repo.huaweicloud.com/repository/npm/
|
RUN npm config set registry https://registry.npm.taobao.org
|
||||||
COPY package.json /build/package.json
|
COPY package.json /build/package.json
|
||||||
RUN npm install
|
RUN yarn
|
||||||
COPY ./ /build
|
COPY ./ /build
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
|
|||||||
246
README.md
246
README.md
@ -1,10 +1,10 @@
|
|||||||
# cool-admin [vue2]
|
# cool-admin [vue3 - ts - vite]
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<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>
|
<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>
|
||||||
|
|
||||||
<p align="center">cool-admin 一个很酷的后台权限管理系统,开源免费,模块化、插件化、极速开发 CRUD,方便快速构建迭代后台管理系统, 到论坛 进一步了解</p>
|
<p align="center">cool-admin 一个很酷的后台权限管理系统,开源免费,模块化、插件化、极速开发 CRUD,方便快速构建迭代后台管理系统, 到<a href="https://cool-js.com" target="_blank">文档</a> 进一步了解</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<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="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" />
|
||||||
@ -38,18 +38,6 @@
|
|||||||
|
|
||||||
<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/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`:
|
推荐使用 `yarn`:
|
||||||
@ -69,233 +57,9 @@ yarn config set sass-binary-site http://npm.taobao.org/mirrors/node-sass
|
|||||||
安装过程完成后,运行以下命令启动服务。您可以在浏览器中预览网站 [http://localhost:9000](http://localhost:9000)
|
安装过程完成后,运行以下命令启动服务。您可以在浏览器中预览网站 [http://localhost:9000](http://localhost:9000)
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
yarn serve
|
yarn dev
|
||||||
```
|
```
|
||||||
|
|
||||||
## 极速 CRUD
|
### 低价服务器
|
||||||
|
|
||||||
1. `vscode` 编辑器下输入关键字 `cl-crud`,会生成对应的模板代码:
|
[阿里云、腾讯云、华为云低价云服务器,不限新老](https://cool-js.com/ad/server.html)
|
||||||
|
|
||||||
```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`:
|
|
||||||
|
|
||||||
```js
|
|
||||||
{
|
|
||||||
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`:
|
|
||||||
|
|
||||||
```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 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
presets: ["@vue/app"],
|
|
||||||
plugins: [
|
|
||||||
["jsx-v-model"],
|
|
||||||
[
|
|
||||||
"component",
|
|
||||||
{
|
|
||||||
libraryName: "element-ui",
|
|
||||||
styleLibraryName: "theme-chalk"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
};
|
|
||||||
68
build/cool/index.ts
Normal file
68
build/cool/index.ts
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import { Plugin } from "vite";
|
||||||
|
import { parseJson } from "./utils";
|
||||||
|
import { createEps, createMenu, createSvg, createTag, getEps } from "./lib";
|
||||||
|
|
||||||
|
export function cool(): Plugin {
|
||||||
|
return {
|
||||||
|
name: "vite-cool",
|
||||||
|
enforce: "pre",
|
||||||
|
configureServer(server) {
|
||||||
|
server.middlewares.use(async (req, res, next) => {
|
||||||
|
function done(data: any) {
|
||||||
|
res.writeHead(200, { "Content-Type": "text/html;charset=UTF-8" });
|
||||||
|
res.end(JSON.stringify(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req.url?.includes("__cool")) {
|
||||||
|
const body = await parseJson(req);
|
||||||
|
let next: any;
|
||||||
|
|
||||||
|
switch (req.url) {
|
||||||
|
// 快速创建菜单
|
||||||
|
case "/__cool_createMenu":
|
||||||
|
next = createMenu(body);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// 创建描述文件
|
||||||
|
case "/__cool_eps":
|
||||||
|
next = createEps(body);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (next) {
|
||||||
|
next.then((data: any) => {
|
||||||
|
done({
|
||||||
|
code: 1000,
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}).catch((err: Error) => {
|
||||||
|
done({
|
||||||
|
code: 1001,
|
||||||
|
message: err.message
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
done({
|
||||||
|
code: 1000
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
transform(code, id) {
|
||||||
|
return createTag(code, id);
|
||||||
|
},
|
||||||
|
transformIndexHtml(html) {
|
||||||
|
return createSvg(html);
|
||||||
|
},
|
||||||
|
config() {
|
||||||
|
return {
|
||||||
|
define: {
|
||||||
|
__EPS__: getEps()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
35
build/cool/lib/eps/config.ts
Normal file
35
build/cool/lib/eps/config.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
export default {
|
||||||
|
entity: {
|
||||||
|
mapping: [
|
||||||
|
{
|
||||||
|
// 自定义匹配
|
||||||
|
custom: ({ entityName, propertyName, type }) => {
|
||||||
|
// status 原本是tinyint,如果是1的话,== true 是可以的,但是不能 === true,请谨慎使用
|
||||||
|
if (propertyName === "status" && type == "tinyint") return "boolean";
|
||||||
|
// 如果没有,返回null或者不返回,则继续遍历其他匹配规则
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "string",
|
||||||
|
test: ["varchar", "text", "simple-json"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "string[]",
|
||||||
|
test: ["simple-array"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "Date",
|
||||||
|
test: ["datetime", "date"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "number",
|
||||||
|
test: ["tinyint", "int", "decimal"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "BigInt",
|
||||||
|
test: ["bigint"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
311
build/cool/lib/eps/index.ts
Normal file
311
build/cool/lib/eps/index.ts
Normal file
@ -0,0 +1,311 @@
|
|||||||
|
import prettier from "prettier";
|
||||||
|
import { isEmpty, last } from "lodash";
|
||||||
|
import { createDir, firstUpperCase, readFile, toCamel } from "../../utils";
|
||||||
|
import { createWriteStream } from "fs";
|
||||||
|
import { join } from "path";
|
||||||
|
import config from "./config";
|
||||||
|
|
||||||
|
interface Options {
|
||||||
|
list: {
|
||||||
|
prefix: string;
|
||||||
|
name: string;
|
||||||
|
columns: any[];
|
||||||
|
api: {
|
||||||
|
name: string;
|
||||||
|
method: string;
|
||||||
|
path: string;
|
||||||
|
summary: string;
|
||||||
|
dts: {
|
||||||
|
parameters: {
|
||||||
|
description: string;
|
||||||
|
schema: {
|
||||||
|
type: string;
|
||||||
|
};
|
||||||
|
name: string;
|
||||||
|
required: boolean;
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
}[];
|
||||||
|
}[];
|
||||||
|
service: {
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 临时目录路径
|
||||||
|
const tempPath = join(__dirname, "../../temp");
|
||||||
|
|
||||||
|
// 获取类型
|
||||||
|
function getType({ entityName, propertyName, type }) {
|
||||||
|
for (const map of config.entity.mapping) {
|
||||||
|
if (map.custom) {
|
||||||
|
const resType = map.custom({ entityName, propertyName, type });
|
||||||
|
if (resType) return resType;
|
||||||
|
}
|
||||||
|
if (map.test) {
|
||||||
|
if (map.test.includes(type)) return map.type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建 Entity
|
||||||
|
function createEntity({ list }: Options) {
|
||||||
|
const t0: any[] = [];
|
||||||
|
|
||||||
|
for (const item of list) {
|
||||||
|
if (!item.name) continue;
|
||||||
|
const t = [`interface ${item.name} {`];
|
||||||
|
for (const col of item.columns || []) {
|
||||||
|
// 描述
|
||||||
|
t.push("\n");
|
||||||
|
t.push("/**\n");
|
||||||
|
t.push(` * ${col.comment}\n`);
|
||||||
|
t.push(" */\n");
|
||||||
|
t.push(
|
||||||
|
`${col.propertyName}?: ${getType({
|
||||||
|
entityName: item.name,
|
||||||
|
propertyName: col.propertyName,
|
||||||
|
type: col.type
|
||||||
|
})};`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
t.push("\n");
|
||||||
|
t.push("/**\n");
|
||||||
|
t.push(` * 任意键值\n`);
|
||||||
|
t.push(" */\n");
|
||||||
|
t.push(`[key: string]: any;`);
|
||||||
|
t.push("}");
|
||||||
|
t0.push(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
return t0.map((e) => e.join("")).join("\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建 Service
|
||||||
|
function createService({ list, service }: Options) {
|
||||||
|
const t0: any[] = [];
|
||||||
|
|
||||||
|
const t1 = [
|
||||||
|
`type Service = {
|
||||||
|
request(options?: {
|
||||||
|
url: string;
|
||||||
|
method?: 'POST' | 'GET' | string;
|
||||||
|
data?: any;
|
||||||
|
params?: any;
|
||||||
|
proxy?: boolean;
|
||||||
|
[key: string]: any;
|
||||||
|
}): Promise<any>;
|
||||||
|
`
|
||||||
|
];
|
||||||
|
|
||||||
|
// 处理数据
|
||||||
|
function deep(d: any, k?: string) {
|
||||||
|
if (!k) k = "";
|
||||||
|
|
||||||
|
for (const i in d) {
|
||||||
|
const name = k + toCamel(firstUpperCase(i.replace(/[:]/g, "")));
|
||||||
|
|
||||||
|
if (d[i].namespace) {
|
||||||
|
// 查找配置
|
||||||
|
const item = list.find((e) => (e.prefix || "").includes(d[i].namespace));
|
||||||
|
|
||||||
|
if (item) {
|
||||||
|
const t = [`interface ${name} {`];
|
||||||
|
|
||||||
|
t1.push(`${i}: ${name};`);
|
||||||
|
|
||||||
|
// 插入方法
|
||||||
|
if (item.api) {
|
||||||
|
// 权限列表
|
||||||
|
const permission: string[] = [];
|
||||||
|
|
||||||
|
item.api.forEach((a) => {
|
||||||
|
// 方法名
|
||||||
|
const n = toCamel(a.name || last(a.path.split("/")) || "").replace(
|
||||||
|
/[:\/-]/g,
|
||||||
|
""
|
||||||
|
);
|
||||||
|
|
||||||
|
if (n) {
|
||||||
|
// 参数类型
|
||||||
|
let q: string[] = [];
|
||||||
|
|
||||||
|
// 参数列表
|
||||||
|
const { parameters = [] } = a.dts || {};
|
||||||
|
|
||||||
|
parameters.forEach((p) => {
|
||||||
|
if (p.description) {
|
||||||
|
q.push(`\n/** ${p.description} */\n`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p.name.includes(":")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const a = `${p.name}${p.required ? "" : "?"}`;
|
||||||
|
const b = `${p.schema.type || "string"}`;
|
||||||
|
|
||||||
|
q.push(`${a}: ${b},`);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isEmpty(q)) {
|
||||||
|
q = ["any"];
|
||||||
|
} else {
|
||||||
|
q.unshift("{");
|
||||||
|
q.push("}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回类型
|
||||||
|
let res = "";
|
||||||
|
|
||||||
|
// 实体名
|
||||||
|
const en = item.name || "any";
|
||||||
|
|
||||||
|
switch (a.path) {
|
||||||
|
case "/page":
|
||||||
|
res = `
|
||||||
|
{
|
||||||
|
pagination: { size: number; page: number; total: number };
|
||||||
|
list: ${en} [];
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "/list":
|
||||||
|
res = `${en} []`;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "/info":
|
||||||
|
res = en;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
res = "any";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 描述
|
||||||
|
t.push("\n");
|
||||||
|
t.push("/**\n");
|
||||||
|
t.push(` * ${a.summary || n}\n`);
|
||||||
|
t.push(" */\n");
|
||||||
|
|
||||||
|
t.push(
|
||||||
|
`${n}(data${q.length == 1 ? "?" : ""}: ${q.join(
|
||||||
|
""
|
||||||
|
)}): Promise<${res}>;`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
permission.push(n);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 权限标识
|
||||||
|
t.push("\n");
|
||||||
|
t.push("/**\n");
|
||||||
|
t.push(" * 权限标识\n");
|
||||||
|
t.push(" */\n");
|
||||||
|
t.push(
|
||||||
|
`permission: { ${permission.map((e) => `${e}: string;`).join("\n")} };`
|
||||||
|
);
|
||||||
|
|
||||||
|
// 权限状态
|
||||||
|
t.push("\n");
|
||||||
|
t.push("/**\n");
|
||||||
|
t.push(" * 权限状态\n");
|
||||||
|
t.push(" */\n");
|
||||||
|
t.push(
|
||||||
|
`_permission: { ${permission
|
||||||
|
.map((e) => `${e}: boolean;`)
|
||||||
|
.join("\n")} };`
|
||||||
|
);
|
||||||
|
|
||||||
|
// 请求
|
||||||
|
t.push("\n");
|
||||||
|
t.push("/**\n");
|
||||||
|
t.push(" * 请求\n");
|
||||||
|
t.push(" */\n");
|
||||||
|
t.push(`request: Service['request']`);
|
||||||
|
}
|
||||||
|
|
||||||
|
t.push("}");
|
||||||
|
t0.push(t);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t1.push(`${i}: {`);
|
||||||
|
deep(d[i], name);
|
||||||
|
t1.push(`},`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 深度
|
||||||
|
deep(service);
|
||||||
|
|
||||||
|
// 结束
|
||||||
|
t1.push("}");
|
||||||
|
|
||||||
|
// 追加
|
||||||
|
t0.push(t1);
|
||||||
|
|
||||||
|
return t0.map((e) => e.join("")).join("\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建描述文件
|
||||||
|
export async function createEps(options: Options) {
|
||||||
|
// 文件内容
|
||||||
|
const text = `
|
||||||
|
declare namespace Eps {
|
||||||
|
${createEntity(options)}
|
||||||
|
${createService(options)}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
// 文本内容
|
||||||
|
const content = prettier.format(text, {
|
||||||
|
parser: "typescript",
|
||||||
|
useTabs: true,
|
||||||
|
tabWidth: 4,
|
||||||
|
endOfLine: "lf",
|
||||||
|
semi: true,
|
||||||
|
singleQuote: false,
|
||||||
|
printWidth: 100,
|
||||||
|
trailingComma: "none"
|
||||||
|
});
|
||||||
|
|
||||||
|
// 创建 temp 目录
|
||||||
|
createDir(tempPath);
|
||||||
|
|
||||||
|
// 创建 eps 描述文件
|
||||||
|
createWriteStream(join(tempPath, "eps.d.ts"), {
|
||||||
|
flags: "w"
|
||||||
|
}).write(content);
|
||||||
|
|
||||||
|
// 创建 eps 数据文件
|
||||||
|
createWriteStream(join(tempPath, "eps.json"), {
|
||||||
|
flags: "w"
|
||||||
|
}).write(
|
||||||
|
JSON.stringify(
|
||||||
|
(options.list || []).map((e) => {
|
||||||
|
const req = e.api.map((a) => {
|
||||||
|
const arr = [a.name ? `/${a.name}` : a.path];
|
||||||
|
|
||||||
|
if (a.method) {
|
||||||
|
arr.push(a.method);
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr;
|
||||||
|
});
|
||||||
|
|
||||||
|
return [e.prefix, e.name || "", req];
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取描述
|
||||||
|
export function getEps() {
|
||||||
|
return JSON.stringify(readFile(join(tempPath, "eps.json")));
|
||||||
|
}
|
||||||
4
build/cool/lib/index.ts
Normal file
4
build/cool/lib/index.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export * from "./eps";
|
||||||
|
export * from "./menu";
|
||||||
|
export * from "./svg";
|
||||||
|
export * from "./tag";
|
||||||
34
build/cool/lib/menu/index.ts
Normal file
34
build/cool/lib/menu/index.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { createWriteStream } from "fs";
|
||||||
|
import prettier from "prettier";
|
||||||
|
import { join } from "path";
|
||||||
|
import { mkdirs } from "../../utils";
|
||||||
|
|
||||||
|
// 创建文件
|
||||||
|
export async function createMenu(options: { viewPath: string; code: string }) {
|
||||||
|
// 格式化内容
|
||||||
|
const content = prettier.format(options.code, {
|
||||||
|
parser: "vue",
|
||||||
|
useTabs: true,
|
||||||
|
tabWidth: 4,
|
||||||
|
endOfLine: "lf",
|
||||||
|
semi: true,
|
||||||
|
jsxBracketSameLine: true,
|
||||||
|
singleQuote: false,
|
||||||
|
printWidth: 100,
|
||||||
|
trailingComma: "none"
|
||||||
|
});
|
||||||
|
|
||||||
|
// 目录路径
|
||||||
|
const dir = (options.viewPath || "").split("/");
|
||||||
|
|
||||||
|
// 文件名
|
||||||
|
const fname = dir.pop();
|
||||||
|
|
||||||
|
// 创建目录
|
||||||
|
const path = mkdirs(`./src/${dir.join("/")}`);
|
||||||
|
|
||||||
|
// 创建文件
|
||||||
|
createWriteStream(join(path, fname || "demo"), {
|
||||||
|
flags: "w"
|
||||||
|
}).write(content);
|
||||||
|
}
|
||||||
54
build/cool/lib/svg/index.ts
Normal file
54
build/cool/lib/svg/index.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import { readFileSync, readdirSync } from "fs";
|
||||||
|
import { extname } from "path";
|
||||||
|
|
||||||
|
function findFiles(dir: string): string[] {
|
||||||
|
const res: string[] = [];
|
||||||
|
const dirs = readdirSync(dir, {
|
||||||
|
withFileTypes: true
|
||||||
|
});
|
||||||
|
for (const d of dirs) {
|
||||||
|
if (d.isDirectory()) {
|
||||||
|
res.push(...findFiles(dir + d.name + "/"));
|
||||||
|
} else {
|
||||||
|
if (extname(d.name) == ".svg") {
|
||||||
|
const svg = readFileSync(dir + d.name)
|
||||||
|
.toString()
|
||||||
|
.replace(/(\r)|(\n)/g, "")
|
||||||
|
.replace(/<svg([^>+].*?)>/, (_: any, $2: any) => {
|
||||||
|
let width = 0;
|
||||||
|
let height = 0;
|
||||||
|
let content = $2.replace(
|
||||||
|
/(width|height)="([^>+].*?)"/g,
|
||||||
|
(_: any, s2: any, s3: any) => {
|
||||||
|
if (s2 === "width") {
|
||||||
|
width = s3;
|
||||||
|
} else if (s2 === "height") {
|
||||||
|
height = s3;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (!/(viewBox="[^>+].*?")/g.test($2)) {
|
||||||
|
content += `viewBox="0 0 ${width} ${height}"`;
|
||||||
|
}
|
||||||
|
return `<symbol id="icon-${d.name.replace(".svg", "")}" ${content}>`;
|
||||||
|
})
|
||||||
|
.replace("</svg>", "</symbol>");
|
||||||
|
res.push(svg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createSvg(html: string) {
|
||||||
|
const res = findFiles("./src/modules/");
|
||||||
|
|
||||||
|
return html.replace(
|
||||||
|
"<body>",
|
||||||
|
`<body>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position: absolute; width: 0; height: 0">
|
||||||
|
${res.join("")}
|
||||||
|
</svg>`
|
||||||
|
);
|
||||||
|
}
|
||||||
32
build/cool/lib/tag/index.ts
Normal file
32
build/cool/lib/tag/index.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { parse, compileScript } from "@vue/compiler-sfc";
|
||||||
|
import magicString from "magic-string";
|
||||||
|
|
||||||
|
export function createTag(code: string, id: string) {
|
||||||
|
if (/\.vue$/.test(id)) {
|
||||||
|
let s: any;
|
||||||
|
const str = () => s || (s = new magicString(code));
|
||||||
|
const { descriptor } = parse(code);
|
||||||
|
|
||||||
|
if (!descriptor.script && descriptor.scriptSetup) {
|
||||||
|
const res = compileScript(descriptor, { id });
|
||||||
|
const { name, lang }: any = res.attrs;
|
||||||
|
|
||||||
|
str().appendLeft(
|
||||||
|
0,
|
||||||
|
`<script lang="${lang}">
|
||||||
|
import { defineComponent } from 'vue'
|
||||||
|
export default defineComponent({
|
||||||
|
name: "${name}"
|
||||||
|
})
|
||||||
|
<\/script>`
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
map: str().generateMap(),
|
||||||
|
code: str().toString()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
1712
build/cool/temp/eps.d.ts
vendored
Normal file
1712
build/cool/temp/eps.d.ts
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
build/cool/temp/eps.json
Normal file
1
build/cool/temp/eps.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
[["/admin/base/comm","",[["/personUpdate","post"],["/uploadMode","get"],["/permmenu","get"],["/person","get"],["/upload","post"],["/logout","post"],["/list"],["/page"],["/info"],["/update"],["/delete"],["/add"]]],["/admin/base/open","",[["/refreshToken","get"],["/captcha","get"],["/login","post"],["/html","get"],["/eps","get"],["/list"],["/page"],["/info"],["/update"],["/delete"],["/add"]]],["/admin/base/sys/department","BaseSysDepartmentEntity",[["/delete","post"],["/update","post"],["/order","post"],["/list","post"],["/add","post"],["/page"],["/info"]]],["/admin/base/sys/log","BaseSysLogEntity",[["/setKeep","post"],["/getKeep","get"],["/clear","post"],["/page","post"],["/list"],["/info"],["/update"],["/delete"],["/add"]]],["/admin/base/sys/menu","BaseSysMenuEntity",[["/create","post"],["/delete","post"],["/update","post"],["/parse","post"],["/info","get"],["/list","post"],["/page","post"],["/add","post"]]],["/admin/base/sys/param","BaseSysParamEntity",[["/delete","post"],["/update","post"],["/html","get"],["/info","get"],["/page","post"],["/add","post"],["/list"]]],["/admin/base/sys/role","BaseSysRoleEntity",[["/delete","post"],["/update","post"],["/info","get"],["/list","post"],["/page","post"],["/add","post"]]],["/admin/base/sys/user","BaseSysUserEntity",[["/delete","post"],["/update","post"],["/move","post"],["/info","get"],["/list","post"],["/page","post"],["/add","post"]]],["/admin/cloud/db","CloudDBEntity",[["/initEntity","post"],["/delete","post"],["/update","post"],["/data","post"],["/info","get"],["/list","post"],["/page","post"],["/add","post"]]],["/admin/cloud/func/info","CloudFuncInfoEntity",[["/invoke","post"],["/delete","post"],["/update","post"],["/info","get"],["/list","post"],["/page","post"],["/add","post"]]],["/admin/cloud/func/log","CloudFuncLogEntity",[["/delete","post"],["/update","post"],["/info","get"],["/list","post"],["/page","post"],["/add","post"]]],["/admin/demo/goods","DemoGoodsEntity",[["/delete","post"],["/update","post"],["/info","get"],["/list","post"],["/page","post"],["/add","post"]]],["/admin/dict/info","DictInfoEntity",[["/delete","post"],["/update","post"],["/data","post"],["/info","get"],["/list","post"],["/page","post"],["/add","post"]]],["/admin/dict/type","DictTypeEntity",[["/delete","post"],["/update","post"],["/info","get"],["/list","post"],["/page","post"],["/add","post"]]],["/admin/iot/device","IotDeviceEntity",[["/delete","post"],["/update","post"],["/info","get"],["/list","post"],["/page","post"],["/add","post"]]],["/admin/iot/message","IotMessageEntity",[["/page","post"],["/list"],["/info"],["/update"],["/delete"],["/add"]]],["/admin/iot/mqtt","",[["/publish","post"],["/config","get"],["/list"],["/page"],["/info"],["/update"],["/delete"],["/add"]]],["/admin/recycle/data","RecycleDataEntity",[["/restore","post"],["/info","get"],["/page","post"],["/list"],["/update"],["/delete"],["/add"]]],["/admin/space/info","SpaceInfoEntity",[["/getConfig","get"],["/delete","post"],["/update","post"],["/info","get"],["/list","post"],["/page","post"],["/add","post"]]],["/admin/space/type","SpaceTypeEntity",[["/delete","post"],["/update","post"],["/info","get"],["/list","post"],["/page","post"],["/add","post"]]],["/admin/task/info","TaskInfoEntity",[["/delete","post"],["/update","post"],["/start","post"],["/once","post"],["/stop","post"],["/info","get"],["/page","post"],["/log","get"],["/add","post"],["/list"]]],["/chat/message","",[["/list"],["/page"],["/info"],["/update"],["/delete"],["/add"]]],["/chat/session","",[["/list"],["/page"],["/info"],["/update"],["/delete"],["/add"]]],["/test","",[["/list"],["/page"],["/info"],["/update"],["/delete"],["/add"]]]]
|
||||||
70
build/cool/utils/index.ts
Normal file
70
build/cool/utils/index.ts
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import fs from "fs";
|
||||||
|
import { join } from "path";
|
||||||
|
|
||||||
|
// 首字母大写
|
||||||
|
export function firstUpperCase(value: string): string {
|
||||||
|
return value.replace(/\b(\w)(\w*)/g, function ($0, $1, $2) {
|
||||||
|
return $1.toUpperCase() + $2;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 横杠转驼峰
|
||||||
|
export function toCamel(str: string): string {
|
||||||
|
return str.replace(/([^-])(?:-+([^-]))/g, function ($0, $1, $2) {
|
||||||
|
return $1 + $2.toUpperCase();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建目录
|
||||||
|
export function createDir(path: string) {
|
||||||
|
if (!fs.existsSync(path)) fs.mkdirSync(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取文件
|
||||||
|
export function readFile(name: string) {
|
||||||
|
try {
|
||||||
|
return fs.readFileSync(name, "utf8");
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析body
|
||||||
|
export function parseJson(req: any): Promise<any> {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
let d = "";
|
||||||
|
req.on("data", function (chunk: Buffer) {
|
||||||
|
d += chunk;
|
||||||
|
});
|
||||||
|
req.on("end", function () {
|
||||||
|
try {
|
||||||
|
resolve(JSON.parse(d));
|
||||||
|
} catch {
|
||||||
|
resolve({});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 深度创建目录
|
||||||
|
export function mkdirs(path: string) {
|
||||||
|
const arr = path.split("/");
|
||||||
|
let p = "";
|
||||||
|
|
||||||
|
arr.forEach((e) => {
|
||||||
|
const t = join(p, e);
|
||||||
|
|
||||||
|
try {
|
||||||
|
fs.statSync(t);
|
||||||
|
} catch (err) {
|
||||||
|
try {
|
||||||
|
fs.mkdirSync(t);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p = t;
|
||||||
|
});
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
163
index.html
Normal file
163
index.html
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<meta name="referer" content="never" />
|
||||||
|
<meta name="renderer" content="webkit" />
|
||||||
|
<meta
|
||||||
|
name="viewport"
|
||||||
|
content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=0"
|
||||||
|
/>
|
||||||
|
<title></title>
|
||||||
|
<link rel="icon" href="favicon.ico" />
|
||||||
|
<style>
|
||||||
|
html,
|
||||||
|
body,
|
||||||
|
#app {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB",
|
||||||
|
"Microsoft YaHei", "微软雅黑", Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preload__wrap {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
background-color: #2f3447;
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
z-index: 9999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preload__title {
|
||||||
|
color: #fff;
|
||||||
|
font-size: 14px;
|
||||||
|
margin: 30px 0 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preload__loading {
|
||||||
|
height: 30px;
|
||||||
|
width: 30px;
|
||||||
|
border-radius: 30px;
|
||||||
|
border: 7px solid currentColor;
|
||||||
|
border-bottom-color: #2f3447 !important;
|
||||||
|
position: relative;
|
||||||
|
animation: r 1s infinite cubic-bezier(0.17, 0.67, 0.83, 0.67),
|
||||||
|
bc 2s infinite ease-in;
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes r {
|
||||||
|
from {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.preload__loading::after,
|
||||||
|
.preload__loading::before {
|
||||||
|
content: "";
|
||||||
|
display: inline-block;
|
||||||
|
position: absolute;
|
||||||
|
bottom: -2px;
|
||||||
|
height: 7px;
|
||||||
|
width: 7px;
|
||||||
|
border-radius: 10px;
|
||||||
|
background-color: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preload__loading::after {
|
||||||
|
left: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preload__loading::before {
|
||||||
|
right: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes bc {
|
||||||
|
0% {
|
||||||
|
color: #689cc5;
|
||||||
|
}
|
||||||
|
|
||||||
|
25% {
|
||||||
|
color: #b3b7e2;
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
color: #93dbe9;
|
||||||
|
}
|
||||||
|
|
||||||
|
75% {
|
||||||
|
color: #abbd81;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
color: #689cc5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="preload__wrap" id="Loading">
|
||||||
|
<div class="preload__container">
|
||||||
|
<p class="preload__name">COOL-ADMIN</p>
|
||||||
|
<div class="preload__loading"></div>
|
||||||
|
<p class="preload__title">正在加载资源...</p>
|
||||||
|
<p class="preload__sub-title">初次加载资源可能需要较多时间 请耐心等待</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="preload__footer">
|
||||||
|
<a href="https://cool-js.com/" target="_blank"> https://cool-js.com </a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.ts"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
41
nginx.conf
41
nginx.conf
@ -14,6 +14,9 @@ http {
|
|||||||
access_log /var/log/nginx/access.log main;
|
access_log /var/log/nginx/access.log main;
|
||||||
sendfile on;
|
sendfile on;
|
||||||
keepalive_timeout 65;
|
keepalive_timeout 65;
|
||||||
|
upstream backend {
|
||||||
|
server midway:8001;
|
||||||
|
}
|
||||||
|
|
||||||
server {
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
@ -25,7 +28,7 @@ http {
|
|||||||
}
|
}
|
||||||
location /api/
|
location /api/
|
||||||
{
|
{
|
||||||
proxy_pass http://midway:7001/;
|
proxy_pass http://backend/;
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
@ -48,6 +51,42 @@ http {
|
|||||||
|
|
||||||
#expires 12h;
|
#expires 12h;
|
||||||
}
|
}
|
||||||
|
# location /im {
|
||||||
|
# proxy_pass http://backend/im;
|
||||||
|
# proxy_connect_timeout 3600s; #配置点1
|
||||||
|
# proxy_read_timeout 3600s; #配置点2,如果没效,可以考虑这个时间配置长一点
|
||||||
|
# proxy_send_timeout 3600s; #配置点3
|
||||||
|
# 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_bind $remote_addr transparent;
|
||||||
|
# proxy_http_version 1.1;
|
||||||
|
# proxy_set_header Upgrade $http_upgrade;
|
||||||
|
# proxy_set_header Connection "upgrade";
|
||||||
|
# # rewrite /socket/(.*) /$1 break;
|
||||||
|
# proxy_redirect off;
|
||||||
|
|
||||||
|
# }
|
||||||
|
|
||||||
|
# location /socket {
|
||||||
|
# proxy_pass http://backend/socket;
|
||||||
|
# proxy_connect_timeout 3600s; #配置点1
|
||||||
|
# proxy_read_timeout 3600s; #配置点2,如果没效,可以考虑这个时间配置长一点
|
||||||
|
# proxy_send_timeout 3600s; #配置点3
|
||||||
|
# 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_bind $remote_addr transparent;
|
||||||
|
# proxy_http_version 1.1;
|
||||||
|
# proxy_set_header Upgrade $http_upgrade;
|
||||||
|
# proxy_set_header Connection "upgrade";
|
||||||
|
# rewrite /socket/(.*) /$1 break;
|
||||||
|
# proxy_redirect off;
|
||||||
|
|
||||||
|
# }
|
||||||
|
|
||||||
|
|
||||||
location /adminer/
|
location /adminer/
|
||||||
{
|
{
|
||||||
|
|||||||
108
package.json
108
package.json
@ -1,65 +1,67 @@
|
|||||||
{
|
{
|
||||||
"name": "cool-admin-vue",
|
"name": "front-next",
|
||||||
"version": "3.2.2",
|
"version": "6.0.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"serve": "vue-cli-service serve",
|
"dev": "vite --host",
|
||||||
"build": "vue-cli-service build",
|
"build": "vite build",
|
||||||
"report": "vue-cli-service build --report",
|
"serve": "vite preview",
|
||||||
"lint": "vue-cli-service lint",
|
"lint:prettier": "prettier --write --loglevel warn \"src/**/*.{js,json,tsx,css,less,scss,vue,html,md}\"",
|
||||||
"inspect": "vue inspect --mode=production > output.js"
|
"lint:eslint": "eslint \"{src}/**/*.{vue,ts,tsx}\" --fix"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^0.21.1",
|
"@cool-vue/crud": "^6.1.7",
|
||||||
"cl-admin": "^1.5.3",
|
"@element-plus/icons-vue": "^2.0.10",
|
||||||
"cl-admin-crud": "^1.6.15",
|
"@vueuse/core": "^9.1.0",
|
||||||
"cl-admin-theme": "^0.0.5",
|
"@wangeditor/editor": "^5.1.23",
|
||||||
"clipboard": "^2.0.7",
|
"@wangeditor/editor-for-vue": "^5.1.12",
|
||||||
"codemirror": "^5.59.4",
|
"axios": "^1.3.4",
|
||||||
"core-js": "^3.6.5",
|
"core-js": "^3.23.5",
|
||||||
"dayjs": "^1.10.4",
|
"dayjs": "^1.11.7",
|
||||||
"echarts": "^5.0.2",
|
"echarts": "^5.3.3",
|
||||||
"element-ui": "^2.15.1",
|
"element-plus": "2.2.28",
|
||||||
"js-beautify": "^1.13.5",
|
"file-saver": "^2.0.5",
|
||||||
|
"lodash-es": "^4.17.21",
|
||||||
|
"mitt": "^3.0.0",
|
||||||
"mockjs": "^1.1.0",
|
"mockjs": "^1.1.0",
|
||||||
|
"monaco-editor": "^0.36.0",
|
||||||
|
"mqtt": "^4.3.7",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"qs": "^6.9.1",
|
"pinia": "^2.0.28",
|
||||||
"quill": "^1.3.7",
|
"quill": "^1.3.7",
|
||||||
"socket.io-client": "2.3.1",
|
"socket.io-client": "^4.5.1",
|
||||||
"store": "^2.0.12",
|
"store": "^2.0.12",
|
||||||
"uuid": "^8.3.2",
|
"ts-wps": "^1.0.5",
|
||||||
"vue": "^2.6.11",
|
"vue": "^3.2.47",
|
||||||
"vue-codemirror": "^4.0.6",
|
"vue-echarts": "^6.2.3",
|
||||||
"vue-cron": "^1.0.9",
|
"vue-router": "^4.1.6",
|
||||||
"vue-echarts": "^6.0.0-rc.3",
|
"vuedraggable": "^4.1.0",
|
||||||
"vue-router": "^3.2.0",
|
"xlsx": "^0.18.5"
|
||||||
"vuedraggable": "^2.24.3",
|
|
||||||
"vuex": "^3.4.0"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@typescript-eslint/parser": "^3.0.0",
|
"@types/lodash-es": "^4.17.6",
|
||||||
"@vue/babel-helper-vue-jsx-merge-props": "^1.0.0",
|
"@types/mockjs": "^1.0.6",
|
||||||
"@vue/babel-preset-jsx": "^1.1.2",
|
"@types/node": "^18.0.6",
|
||||||
"@vue/cli-plugin-babel": "~4.5.0",
|
"@types/nprogress": "^0.2.0",
|
||||||
"@vue/cli-plugin-eslint": "~4.5.0",
|
"@types/prettier": "^2.7.2",
|
||||||
"@vue/cli-plugin-router": "~4.5.0",
|
"@types/quill": "^2.0.10",
|
||||||
"@vue/cli-plugin-vuex": "~4.5.0",
|
"@types/store": "^2.0.2",
|
||||||
"@vue/cli-service": "~4.5.0",
|
"@typescript-eslint/eslint-plugin": "^5.30.6",
|
||||||
"@vue/composition-api": "^1.0.0-rc.5",
|
"@typescript-eslint/parser": "^5.30.6",
|
||||||
"@vue/eslint-config-prettier": "^6.0.0",
|
"@vitejs/plugin-vue": "^4.0.0",
|
||||||
"babel-eslint": "^10.1.0",
|
"@vitejs/plugin-vue-jsx": "^3.0.0",
|
||||||
"babel-plugin-component": "^1.1.1",
|
"@vue/compiler-sfc": "^3.2.37",
|
||||||
"babel-plugin-jsx-v-model": "^2.0.3",
|
"eslint": "^8.20.0",
|
||||||
"clean-webpack-plugin": "^3.0.0",
|
"eslint-config-prettier": "^8.1.0",
|
||||||
"eslint": "^6.7.2",
|
"eslint-plugin-prettier": "^4.2.1",
|
||||||
"eslint-plugin-prettier": "^3.1.3",
|
"eslint-plugin-vue": "^9.2.0",
|
||||||
"eslint-plugin-vue": "^6.2.2",
|
"lodash": "^4.17.21",
|
||||||
"hard-source-webpack-plugin": "^0.13.1",
|
"magic-string": "^0.27.0",
|
||||||
"node-sass": "^4.12.0",
|
"prettier": "^2.8.4",
|
||||||
"prettier": "^1.19.1",
|
"rollup-plugin-visualizer": "^5.9.0",
|
||||||
"sass-loader": "^8.0.2",
|
"sass": "^1.53.0",
|
||||||
"svg-sprite-loader": "^5.0.0",
|
"terser": "^5.16.3",
|
||||||
"typescript": "^3.9.3",
|
"typescript": "^4.7.4",
|
||||||
"vue-template-compiler": "^2.6.11",
|
"vite": "^4.1.4",
|
||||||
"webpack-cli": "^3.3.12"
|
"vite-plugin-compression": "^0.5.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
plugins: {
|
|
||||||
autoprefixer: {}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@ -1,99 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
|
||||||
<meta name="referer" content="never" />
|
|
||||||
<meta name="renderer" content="webkit" />
|
|
||||||
<meta
|
|
||||||
name="viewport"
|
|
||||||
content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=0"
|
|
||||||
/>
|
|
||||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico" />
|
|
||||||
<title>COOL-ADMIN</title>
|
|
||||||
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.css) { %>
|
|
||||||
<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="stylesheet" />
|
|
||||||
<% } %>
|
|
||||||
<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>
|
|
||||||
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
13169
public/theme/black.css
13169
public/theme/black.css
File diff suppressed because it is too large
Load Diff
13215
public/theme/blue.css
13215
public/theme/blue.css
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
13215
public/theme/green.css
13215
public/theme/green.css
File diff suppressed because it is too large
Load Diff
13154
public/theme/purple.css
13154
public/theme/purple.css
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="app">
|
<el-config-provider :locale="zhCn">
|
||||||
<router-view />
|
<router-view />
|
||||||
</div>
|
</el-config-provider>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" src="./assets/css/index.scss"></style>
|
<script lang="ts" setup>
|
||||||
|
import { ElConfigProvider } from "element-plus";
|
||||||
|
import zhCn from "element-plus/lib/locale/lang/zh-cn";
|
||||||
|
</script>
|
||||||
|
|||||||
@ -1,15 +0,0 @@
|
|||||||
$primary: #4165d7;
|
|
||||||
|
|
||||||
$color-primary: var(--color-primary, $primary);
|
|
||||||
$color-success: #67c23a;
|
|
||||||
$color-danger: #f56c6c;
|
|
||||||
$color-info: #909399;
|
|
||||||
$color-warning: #e6a23c;
|
|
||||||
|
|
||||||
:export {
|
|
||||||
colorPrimary: $primary;
|
|
||||||
colorSuccess: $color-success;
|
|
||||||
colorDanger: $color-danger;
|
|
||||||
colorInfo: $color-info;
|
|
||||||
colorWarning: $color-warning;
|
|
||||||
}
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
$--color-primary: $primary;
|
|
||||||
$--color-success: $color-success;
|
|
||||||
$--color-danger: $color-danger;
|
|
||||||
$--color-warning: $color-warning;
|
|
||||||
$--color-info: $color-info;
|
|
||||||
|
|
||||||
$--font-path: "~element-ui/lib/theme-chalk/fonts";
|
|
||||||
|
|
||||||
@import "~element-ui/packages/theme-chalk/src/index";
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.0 KiB |
@ -1,50 +0,0 @@
|
|||||||
import store from "store";
|
|
||||||
import { getUrlParam } from "cl-admin/utils";
|
|
||||||
|
|
||||||
// 路由模式
|
|
||||||
export const routerMode = "history";
|
|
||||||
|
|
||||||
// 开发模式
|
|
||||||
export const isDev = process.env.NODE_ENV == "development";
|
|
||||||
|
|
||||||
// Host
|
|
||||||
export const host = "https://show.cool-admin.com";
|
|
||||||
|
|
||||||
// Socket
|
|
||||||
export const socketUrl = (isDev ? `${host}` : "") + "/socket";
|
|
||||||
|
|
||||||
// 请求地址,本地会使用代理请求
|
|
||||||
export const baseUrl = (function() {
|
|
||||||
let proxy = getUrlParam("proxy");
|
|
||||||
|
|
||||||
if (proxy) {
|
|
||||||
store.set("proxy", proxy);
|
|
||||||
} else {
|
|
||||||
proxy = store.get("proxy") || "dev";
|
|
||||||
}
|
|
||||||
|
|
||||||
return isDev ? `/${proxy}/admin` : `/api/admin`;
|
|
||||||
})();
|
|
||||||
|
|
||||||
// 阿里字体图标库 https://at.alicdn.com/t/**.css
|
|
||||||
export const iconfontUrl = ``;
|
|
||||||
|
|
||||||
// 程序配置参数
|
|
||||||
export const app = store.get("__app__") || {
|
|
||||||
name: "COOL-ADMIN",
|
|
||||||
|
|
||||||
conf: {
|
|
||||||
showAMenu: false, // 是否显示一级菜单栏
|
|
||||||
showRouteNav: true, // 是否显示路由导航栏
|
|
||||||
showProcess: true, // 是否显示页面进程栏
|
|
||||||
customMenu: false // 自定义菜单
|
|
||||||
},
|
|
||||||
|
|
||||||
theme: {
|
|
||||||
color: "", // 主题色
|
|
||||||
url: "" // 主题样式地址
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 自定义菜单列表
|
|
||||||
export const menuList = [];
|
|
||||||
205
src/cool/bootstrap/eps.ts
Normal file
205
src/cool/bootstrap/eps.ts
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
import { isDev, config } from "../config";
|
||||||
|
import { BaseService, service } from "../service";
|
||||||
|
import { toCamel } from "../utils";
|
||||||
|
import { hmr } from "../hook";
|
||||||
|
import { isArray, isEmpty, isObject } from "lodash-es";
|
||||||
|
|
||||||
|
// 获取标签名
|
||||||
|
function getNames(v: any) {
|
||||||
|
return [...Object.getOwnPropertyNames(v.constructor.prototype), ...Object.keys(v)].filter(
|
||||||
|
(e) => !["namespace", "constructor", "request", "permission"].includes(e)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 标签名
|
||||||
|
const names = getNames(new BaseService());
|
||||||
|
|
||||||
|
// 创建
|
||||||
|
export async function createEps() {
|
||||||
|
// 创建描述文件
|
||||||
|
function createDts(list: any[]) {
|
||||||
|
if (!isDev) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function deep(v: any) {
|
||||||
|
for (const i in v) {
|
||||||
|
if (v[i].namespace) {
|
||||||
|
v[i].namespace = v[i].namespace;
|
||||||
|
|
||||||
|
// 模块
|
||||||
|
const item: any = list.find((e: any) => e.prefix.includes(v[i].namespace));
|
||||||
|
|
||||||
|
// 接口
|
||||||
|
const api: any[] = item ? item.api : [];
|
||||||
|
|
||||||
|
// 获取方法集合
|
||||||
|
[...names, ...getNames(v[i])].forEach((e) => {
|
||||||
|
if (!api.find((a) => a.path.includes(e))) {
|
||||||
|
api.push({
|
||||||
|
path: `/${e}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (item) {
|
||||||
|
item.api = api;
|
||||||
|
} else {
|
||||||
|
list.push({
|
||||||
|
prefix: `/${v[i].namespace}`,
|
||||||
|
api
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
deep(v[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deep(service);
|
||||||
|
|
||||||
|
// 本地服务
|
||||||
|
return service.request({
|
||||||
|
url: "/__cool_eps",
|
||||||
|
method: "POST",
|
||||||
|
proxy: false,
|
||||||
|
data: {
|
||||||
|
service,
|
||||||
|
list
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置
|
||||||
|
async function set(d?: any) {
|
||||||
|
const list: any[] = [];
|
||||||
|
|
||||||
|
if (isArray(d)) {
|
||||||
|
d = { d };
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const i in d) {
|
||||||
|
if (isArray(d[i])) {
|
||||||
|
d[i].forEach((e: any) => {
|
||||||
|
// 分隔路径
|
||||||
|
const arr = e.prefix
|
||||||
|
.replace(/\//, "")
|
||||||
|
.replace("admin", "")
|
||||||
|
.split("/")
|
||||||
|
.filter(Boolean)
|
||||||
|
.map(toCamel);
|
||||||
|
|
||||||
|
// 遍历
|
||||||
|
function deep(d: any, i: number) {
|
||||||
|
const k = arr[i];
|
||||||
|
|
||||||
|
if (k) {
|
||||||
|
// 是否最后一个
|
||||||
|
if (arr[i + 1]) {
|
||||||
|
if (!d[k]) {
|
||||||
|
d[k] = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
deep(d[k], i + 1);
|
||||||
|
} else {
|
||||||
|
// 本地不存在则创建实例
|
||||||
|
if (!d[k]) {
|
||||||
|
d[k] = new BaseService({
|
||||||
|
namespace: e.prefix.substr(1, e.prefix.length - 1)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建方法
|
||||||
|
e.api.forEach((a: any) => {
|
||||||
|
// 方法名
|
||||||
|
const n = a.path.replace("/", "");
|
||||||
|
|
||||||
|
// 过滤
|
||||||
|
if (!names.includes(n)) {
|
||||||
|
// 本地不存在则创建
|
||||||
|
if (!d[k][n]) {
|
||||||
|
if (n && !/[-:]/g.test(n)) {
|
||||||
|
d[k][n] = function (data: any) {
|
||||||
|
return this.request({
|
||||||
|
url: a.path,
|
||||||
|
method: a.method,
|
||||||
|
[a.method.toLocaleLowerCase() == "post"
|
||||||
|
? "data"
|
||||||
|
: "params"]: data
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 创建权限
|
||||||
|
if (!d[k].permission) {
|
||||||
|
d[k].permission = {};
|
||||||
|
|
||||||
|
const ks = Array.from(new Set([...names, ...getNames(d[k])]));
|
||||||
|
|
||||||
|
ks.forEach((e) => {
|
||||||
|
d[k].permission[e] = `${d[k].namespace.replace(
|
||||||
|
"admin/",
|
||||||
|
""
|
||||||
|
)}/${e}`.replace(/\//g, ":");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
list.push(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deep(service, 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 缓存数据
|
||||||
|
hmr.setData("service", service);
|
||||||
|
|
||||||
|
createDts(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取
|
||||||
|
async function getEps() {
|
||||||
|
try {
|
||||||
|
// 本地数据
|
||||||
|
let list = JSON.parse(__EPS__ || "[]").map(([prefix, name, api]: any[]) => {
|
||||||
|
return {
|
||||||
|
prefix,
|
||||||
|
name,
|
||||||
|
api: api.map(([path, method]: string[]) => {
|
||||||
|
return {
|
||||||
|
method,
|
||||||
|
path
|
||||||
|
};
|
||||||
|
})
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// 接口数据
|
||||||
|
if (isDev && config.test.eps) {
|
||||||
|
await service
|
||||||
|
.request({
|
||||||
|
url: "/admin/base/open/eps"
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
if (!isEmpty(res) && isObject(res)) {
|
||||||
|
list = res;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (list) {
|
||||||
|
set(list);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("[Eps] 获取失败!", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await getEps();
|
||||||
|
}
|
||||||
23
src/cool/bootstrap/index.ts
Normal file
23
src/cool/bootstrap/index.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { createPinia } from "pinia";
|
||||||
|
import { App } from "vue";
|
||||||
|
import { createModule } from "./module";
|
||||||
|
import { createEps } from "./eps";
|
||||||
|
import { router } from "../router";
|
||||||
|
import { Loading } from "../utils";
|
||||||
|
|
||||||
|
export async function bootstrap(app: App) {
|
||||||
|
// pinia
|
||||||
|
app.use(createPinia());
|
||||||
|
|
||||||
|
// 路由
|
||||||
|
app.use(router);
|
||||||
|
|
||||||
|
// 模块
|
||||||
|
const { eventLoop } = createModule(app);
|
||||||
|
|
||||||
|
// eps
|
||||||
|
await createEps();
|
||||||
|
|
||||||
|
// 加载
|
||||||
|
Loading.set([eventLoop()]);
|
||||||
|
}
|
||||||
112
src/cool/bootstrap/module.ts
Normal file
112
src/cool/bootstrap/module.ts
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
import { App } from "vue";
|
||||||
|
import { isFunction, orderBy } from "lodash-es";
|
||||||
|
import { deepMerge, filename, mergeService } from "../utils";
|
||||||
|
import { service } from "../service";
|
||||||
|
import { module } from "../module";
|
||||||
|
import { hmr } from "../hook";
|
||||||
|
|
||||||
|
// 扫描文件
|
||||||
|
const files: any = import.meta.glob("/src/modules/*/{config.ts,service/**,directives/**}", {
|
||||||
|
eager: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// 模块列表
|
||||||
|
module.list = hmr.getData("modules", []);
|
||||||
|
|
||||||
|
// 解析
|
||||||
|
for (const i in files) {
|
||||||
|
// 分割
|
||||||
|
const [, , , name, action] = i.split("/");
|
||||||
|
|
||||||
|
// 文件名
|
||||||
|
const fname = filename(i);
|
||||||
|
|
||||||
|
// 文件内容
|
||||||
|
const v = files[i]?.default;
|
||||||
|
|
||||||
|
// 模块是否存在
|
||||||
|
const m = module.get(name);
|
||||||
|
|
||||||
|
// 数据
|
||||||
|
const d = m || {
|
||||||
|
name,
|
||||||
|
value: null,
|
||||||
|
services: [],
|
||||||
|
directives: []
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
// 配置参数
|
||||||
|
case "config.ts":
|
||||||
|
d.value = v;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// 请求服务
|
||||||
|
case "service":
|
||||||
|
const s = new v();
|
||||||
|
|
||||||
|
if (s) {
|
||||||
|
d.services?.push({
|
||||||
|
path: s.namespace,
|
||||||
|
value: s
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// 指令
|
||||||
|
case "directives":
|
||||||
|
d.directives?.push({ name: fname, value: v });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m) {
|
||||||
|
module.add(d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建
|
||||||
|
export function createModule(app: App) {
|
||||||
|
// 模块加载
|
||||||
|
const list = orderBy(module.list, "order").map((e) => {
|
||||||
|
const d = isFunction(e.value) ? e.value(app) : e.value;
|
||||||
|
|
||||||
|
if (d) {
|
||||||
|
Object.assign(e, d);
|
||||||
|
|
||||||
|
// 注册组件
|
||||||
|
e.components?.forEach(async (c: any) => {
|
||||||
|
const v = await (isFunction(c) ? c() : c);
|
||||||
|
const n = v.default || v;
|
||||||
|
app.component(n.name, n);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 注册指令
|
||||||
|
e.directives?.forEach((v) => {
|
||||||
|
app.directive(v.name, v.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 安装事件
|
||||||
|
if (d.install) {
|
||||||
|
d.install(app, d.options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 合并
|
||||||
|
deepMerge(service, mergeService(e.services || []));
|
||||||
|
|
||||||
|
return e;
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
// 事件加载
|
||||||
|
async eventLoop() {
|
||||||
|
const events: any = {};
|
||||||
|
|
||||||
|
for (let i = 0; i < list.length; i++) {
|
||||||
|
if (list[i].onLoad) {
|
||||||
|
Object.assign(events, await list[i]?.onLoad?.(events));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
20
src/cool/config/dev.ts
Normal file
20
src/cool/config/dev.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { getUrlParam, storage } from "../utils";
|
||||||
|
import { proxy } from "./proxy";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
// 根地址
|
||||||
|
host: proxy["/dev/"].target,
|
||||||
|
|
||||||
|
// 请求地址
|
||||||
|
get baseUrl() {
|
||||||
|
let proxy = getUrlParam("proxy");
|
||||||
|
|
||||||
|
if (proxy) {
|
||||||
|
storage.set("proxy", proxy);
|
||||||
|
} else {
|
||||||
|
proxy = storage.get("proxy") || "dev";
|
||||||
|
}
|
||||||
|
|
||||||
|
return `/${proxy}/`;
|
||||||
|
}
|
||||||
|
};
|
||||||
55
src/cool/config/index.ts
Normal file
55
src/cool/config/index.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import { Config } from "../types";
|
||||||
|
import dev from "./dev";
|
||||||
|
import prod from "./prod";
|
||||||
|
|
||||||
|
// 是否开发模式
|
||||||
|
export const isDev = import.meta.env.MODE === "development";
|
||||||
|
|
||||||
|
// 配置
|
||||||
|
export const config: Config = {
|
||||||
|
// 项目信息
|
||||||
|
app: {
|
||||||
|
name: "COOL-ADMIN",
|
||||||
|
|
||||||
|
// 菜单
|
||||||
|
menu: {
|
||||||
|
// 是否分组显示
|
||||||
|
isGroup: false,
|
||||||
|
// 自定义菜单列表
|
||||||
|
list: []
|
||||||
|
},
|
||||||
|
|
||||||
|
// 路由
|
||||||
|
router: {
|
||||||
|
// 模式
|
||||||
|
mode: "history",
|
||||||
|
// 转场动画
|
||||||
|
transition: "slide",
|
||||||
|
// 首页组件
|
||||||
|
home: () => import("/$/demo/views/home/index.vue")
|
||||||
|
},
|
||||||
|
|
||||||
|
// 字体图标库
|
||||||
|
iconfont: []
|
||||||
|
},
|
||||||
|
|
||||||
|
// 忽略规则
|
||||||
|
ignore: {
|
||||||
|
// 不显示请求进度条
|
||||||
|
NProgress: ["/", "/base/open/eps", "/base/comm/upload", "/base/comm/uploadMode"],
|
||||||
|
// 页面不需要登录验证
|
||||||
|
token: ["/login", "/401", "/403", "/404", "/500", "/502"]
|
||||||
|
},
|
||||||
|
|
||||||
|
// 调试
|
||||||
|
test: {
|
||||||
|
token: "",
|
||||||
|
mock: false,
|
||||||
|
eps: true
|
||||||
|
},
|
||||||
|
|
||||||
|
// 当前环境
|
||||||
|
...(isDev ? dev : prod)
|
||||||
|
};
|
||||||
|
|
||||||
|
export * from "./proxy";
|
||||||
9
src/cool/config/prod.ts
Normal file
9
src/cool/config/prod.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { proxy } from "./proxy";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
// 根地址
|
||||||
|
host: proxy["/prod/"].target,
|
||||||
|
|
||||||
|
// 请求地址
|
||||||
|
baseUrl: "/api"
|
||||||
|
};
|
||||||
13
src/cool/config/proxy.ts
Normal file
13
src/cool/config/proxy.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
export const proxy = {
|
||||||
|
"/dev/": {
|
||||||
|
target: "http://127.0.0.1:8001",
|
||||||
|
changeOrigin: true,
|
||||||
|
rewrite: (path: string) => path.replace(/^\/dev/, "")
|
||||||
|
},
|
||||||
|
|
||||||
|
"/prod/": {
|
||||||
|
target: "https://show.cool-admin.com",
|
||||||
|
changeOrigin: true,
|
||||||
|
rewrite: (path: string) => path.replace(/^\/prod/, "/api")
|
||||||
|
}
|
||||||
|
};
|
||||||
30
src/cool/hook/browser.ts
Normal file
30
src/cool/hook/browser.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { useEventListener } from "@vueuse/core";
|
||||||
|
import { reactive, watch } from "vue";
|
||||||
|
import { getBrowser } from "../utils";
|
||||||
|
|
||||||
|
const browser = reactive(getBrowser());
|
||||||
|
const events: (() => void)[] = [];
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => browser.screen,
|
||||||
|
() => {
|
||||||
|
events.forEach((ev) => ev());
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
useEventListener(window, "resize", () => {
|
||||||
|
Object.assign(browser, getBrowser());
|
||||||
|
});
|
||||||
|
|
||||||
|
export function useBrowser() {
|
||||||
|
return {
|
||||||
|
browser,
|
||||||
|
onScreenChange(ev: () => void, immediate = true) {
|
||||||
|
events.push(ev);
|
||||||
|
|
||||||
|
if (immediate) {
|
||||||
|
ev();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
23
src/cool/hook/hmr.ts
Normal file
23
src/cool/hook/hmr.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
// 解决热更新后失效问题;
|
||||||
|
const data = import.meta.hot?.data.getData?.() || {};
|
||||||
|
|
||||||
|
if (import.meta.hot) {
|
||||||
|
import.meta.hot.data.getData = () => {
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const hmr = {
|
||||||
|
data,
|
||||||
|
|
||||||
|
setData(key: string, value: any) {
|
||||||
|
data[key] = value;
|
||||||
|
},
|
||||||
|
|
||||||
|
getData(key: string, defaultValue?: any) {
|
||||||
|
if (defaultValue !== undefined && !data[key]) {
|
||||||
|
this.setData(key, defaultValue);
|
||||||
|
}
|
||||||
|
return data[key];
|
||||||
|
}
|
||||||
|
};
|
||||||
52
src/cool/hook/index.ts
Normal file
52
src/cool/hook/index.ts
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import { getCurrentInstance, Ref, reactive } from "vue";
|
||||||
|
import { useRoute, useRouter } from "vue-router";
|
||||||
|
import { service } from "../service";
|
||||||
|
import { useBrowser } from "./browser";
|
||||||
|
import { useMitt } from "./mitt";
|
||||||
|
|
||||||
|
export function useRefs() {
|
||||||
|
const refs = reactive<{ [key: string]: any }>({});
|
||||||
|
function setRefs(name: string) {
|
||||||
|
return (el: any) => {
|
||||||
|
refs[name] = el;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return { refs, setRefs };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useParent(name: string, r: Ref) {
|
||||||
|
const d = getCurrentInstance();
|
||||||
|
|
||||||
|
if (d) {
|
||||||
|
let parent = d.proxy?.$.parent;
|
||||||
|
|
||||||
|
if (parent) {
|
||||||
|
while (parent && parent.type?.name != name) {
|
||||||
|
parent = parent?.parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parent) {
|
||||||
|
if (parent.type.name == name) {
|
||||||
|
r.value = parent.exposed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useCool() {
|
||||||
|
return {
|
||||||
|
service,
|
||||||
|
route: useRoute(),
|
||||||
|
router: useRouter(),
|
||||||
|
mitt: useMitt(),
|
||||||
|
...useBrowser(),
|
||||||
|
...useRefs()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export * from "./browser";
|
||||||
|
export * from "./hmr";
|
||||||
8
src/cool/hook/mitt.ts
Normal file
8
src/cool/hook/mitt.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import Mitt, { Emitter } from "mitt";
|
||||||
|
import { hmr } from "./hmr";
|
||||||
|
|
||||||
|
const mitt: Emitter<any> = hmr.getData("mitt", Mitt());
|
||||||
|
|
||||||
|
export function useMitt() {
|
||||||
|
return mitt;
|
||||||
|
}
|
||||||
@ -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
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
8
src/cool/index.ts
Normal file
8
src/cool/index.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export * from "./service";
|
||||||
|
export * from "./bootstrap";
|
||||||
|
export * from "./hook";
|
||||||
|
export * from "./module";
|
||||||
|
export * from "./router";
|
||||||
|
export * from "./config";
|
||||||
|
export * from "./types/index.d";
|
||||||
|
export { storage } from "./utils";
|
||||||
23
src/cool/module/index.ts
Normal file
23
src/cool/module/index.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { Module } from "../types";
|
||||||
|
import { hmr } from "../hook";
|
||||||
|
|
||||||
|
// 数据列表
|
||||||
|
const list: Module[] = hmr.getData("modules", []);
|
||||||
|
|
||||||
|
// 模块
|
||||||
|
const module = {
|
||||||
|
list,
|
||||||
|
req: Promise.resolve(),
|
||||||
|
get(name: string): Module {
|
||||||
|
// @ts-ignore
|
||||||
|
return this.list.find((e) => e.name == name);
|
||||||
|
},
|
||||||
|
add(data: Module) {
|
||||||
|
this.list.push(data);
|
||||||
|
},
|
||||||
|
wait() {
|
||||||
|
return this.req;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export { module };
|
||||||
@ -1,4 +0,0 @@
|
|||||||
import { iconList } from "./theme";
|
|
||||||
import "./resize";
|
|
||||||
|
|
||||||
export { iconList };
|
|
||||||
@ -1,52 +0,0 @@
|
|||||||
import store from "@/store";
|
|
||||||
|
|
||||||
const lock = {
|
|
||||||
menuCollapse: null,
|
|
||||||
showAMenu: null
|
|
||||||
};
|
|
||||||
|
|
||||||
function resize() {
|
|
||||||
// 更新数据
|
|
||||||
store.commit("SET_BROWSER");
|
|
||||||
|
|
||||||
const { browser, menuCollapse, app } = store.getters;
|
|
||||||
|
|
||||||
if (browser.isMini) {
|
|
||||||
// 小屏幕下隐藏一级菜单
|
|
||||||
if (lock.showAMenu === null) {
|
|
||||||
lock.showAMenu = app.conf.showAMenu;
|
|
||||||
store.commit("UPDATE_APP", {
|
|
||||||
conf: {
|
|
||||||
showAMenu: false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 小屏幕下收起左侧菜单
|
|
||||||
if (lock.menuCollapse === null) {
|
|
||||||
lock.menuCollapse = menuCollapse;
|
|
||||||
store.commit("COLLAPSE_MENU", true);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 大屏幕下显示一级菜单
|
|
||||||
if (lock.showAMenu !== null) {
|
|
||||||
store.commit("UPDATE_APP", {
|
|
||||||
conf: {
|
|
||||||
showAMenu: lock.showAMenu
|
|
||||||
}
|
|
||||||
});
|
|
||||||
lock.showAMenu = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 大屏幕下展开左侧菜单
|
|
||||||
if (lock.menuCollapse !== null) {
|
|
||||||
store.commit("COLLAPSE_MENU", lock.menuCollapse);
|
|
||||||
lock.menuCollapse = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window.onload = function() {
|
|
||||||
window.addEventListener("resize", resize);
|
|
||||||
resize();
|
|
||||||
};
|
|
||||||
@ -1,40 +0,0 @@
|
|||||||
import { iconfontUrl, app } from "@/config/env";
|
|
||||||
import { createLink } from "../utils";
|
|
||||||
import { colorPrimary } from "@/assets/css/common.scss";
|
|
||||||
|
|
||||||
// 主题初始化
|
|
||||||
|
|
||||||
if (app.theme) {
|
|
||||||
const { url, color } = app.theme;
|
|
||||||
|
|
||||||
if (url) {
|
|
||||||
createLink(url, "theme-style");
|
|
||||||
}
|
|
||||||
|
|
||||||
document
|
|
||||||
.getElementsByTagName("body")[0]
|
|
||||||
.style.setProperty("--color-primary", color || colorPrimary);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 字体图标库加载
|
|
||||||
|
|
||||||
if (iconfontUrl) {
|
|
||||||
createLink(iconfontUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
// svg 图标加载
|
|
||||||
|
|
||||||
const req = require.context("@/icons/svg/", false, /\.svg$/);
|
|
||||||
|
|
||||||
req.keys().map(req);
|
|
||||||
|
|
||||||
function iconList() {
|
|
||||||
return req
|
|
||||||
.keys()
|
|
||||||
.map(req)
|
|
||||||
.map(e => e.default.id)
|
|
||||||
.filter(e => e.includes("icon"))
|
|
||||||
.sort();
|
|
||||||
}
|
|
||||||
|
|
||||||
export { iconList };
|
|
||||||
@ -1,96 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="cl-avatar" :class="[size, shape]" :style="[style]">
|
|
||||||
<el-image :src="src" alt="">
|
|
||||||
<div slot="error" class="image-slot">
|
|
||||||
<i class="el-icon-picture-outline"></i>
|
|
||||||
</div>
|
|
||||||
</el-image>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { isNumber } from "cl-admin/utils";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "cl-avatar",
|
|
||||||
|
|
||||||
props: {
|
|
||||||
src: String,
|
|
||||||
size: {
|
|
||||||
type: String,
|
|
||||||
default: "large"
|
|
||||||
},
|
|
||||||
shape: {
|
|
||||||
type: String,
|
|
||||||
default: "circle"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
style() {
|
|
||||||
const size = isNumber(this.size) ? this.size + "px" : this.size;
|
|
||||||
|
|
||||||
return {
|
|
||||||
height: size,
|
|
||||||
width: size
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.cl-avatar {
|
|
||||||
overflow: hidden;
|
|
||||||
background-color: #f7f7f7;
|
|
||||||
|
|
||||||
&.large {
|
|
||||||
height: 50px;
|
|
||||||
width: 50px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.medium {
|
|
||||||
height: 40px;
|
|
||||||
width: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.small {
|
|
||||||
height: 30px;
|
|
||||||
width: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.circle {
|
|
||||||
border-radius: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.square {
|
|
||||||
border-radius: 10%;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-image {
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
/deep/.image-slot {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
i {
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-icon-picture-outline {
|
|
||||||
color: #ccc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,268 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="cl-codemirror">
|
|
||||||
<codemirror
|
|
||||||
ref="code"
|
|
||||||
v-model="value2"
|
|
||||||
:options="options2"
|
|
||||||
:style="{
|
|
||||||
height,
|
|
||||||
width
|
|
||||||
}"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { codemirror } from "vue-codemirror";
|
|
||||||
import beautifyJs from "js-beautify";
|
|
||||||
|
|
||||||
import "codemirror/theme/cobalt.css";
|
|
||||||
import "codemirror/lib/codemirror.css";
|
|
||||||
import "codemirror/addon/hint/show-hint.css";
|
|
||||||
import "codemirror/addon/hint/javascript-hint";
|
|
||||||
import "codemirror/mode/javascript/javascript";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "cl-codemirror",
|
|
||||||
|
|
||||||
components: {
|
|
||||||
codemirror
|
|
||||||
},
|
|
||||||
|
|
||||||
props: {
|
|
||||||
value: String,
|
|
||||||
height: String,
|
|
||||||
width: String,
|
|
||||||
options: Object
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
value2: ""
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
value: {
|
|
||||||
immediate: true,
|
|
||||||
handler(val) {
|
|
||||||
this.value2 = val || "";
|
|
||||||
}
|
|
||||||
},
|
|
||||||
value2(val) {
|
|
||||||
this.$emit("input", val);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
options2() {
|
|
||||||
return {
|
|
||||||
mode: "javascript",
|
|
||||||
theme: "ambiance",
|
|
||||||
styleActiveLine: true,
|
|
||||||
lineNumbers: true,
|
|
||||||
lineWrapping: true,
|
|
||||||
indentUnit: 4,
|
|
||||||
...this.options
|
|
||||||
};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
this.$el.onkeydown = e => {
|
|
||||||
let keyCode = e.keyCode || e.which || e.charCode;
|
|
||||||
let altKey = e.altKey || e.metaKey;
|
|
||||||
let shiftKey = e.shiftKey || e.metaKey;
|
|
||||||
|
|
||||||
if (altKey && shiftKey && keyCode == 70) {
|
|
||||||
this.setValue();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.setValue(this.value2);
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
setValue(val) {
|
|
||||||
this.value2 = beautifyJs(val || this.value2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.cl-codemirror {
|
|
||||||
border-radius: 3px;
|
|
||||||
border: 1px solid #dcdfe6;
|
|
||||||
box-sizing: border-box;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.CodeMirror {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-ambiance * {
|
|
||||||
font-family: "Consolas";
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-ambiance .cm-header {
|
|
||||||
color: blue;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-ambiance .cm-quote {
|
|
||||||
color: #24c2c7;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-ambiance .cm-keyword {
|
|
||||||
color: #a626a4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-ambiance .cm-atom {
|
|
||||||
color: #986801;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-ambiance .cm-number {
|
|
||||||
color: #986801;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-ambiance .cm-def {
|
|
||||||
color: #383a42;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-ambiance .cm-variable {
|
|
||||||
color: #4078f2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-ambiance .cm-variable-2 {
|
|
||||||
color: #eed1b3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-ambiance .cm-variable-3,
|
|
||||||
.cm-s-ambiance .cm-type {
|
|
||||||
color: #faded3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-ambiance .cm-property {
|
|
||||||
color: #333333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-ambiance .cm-operator {
|
|
||||||
color: #0184bc;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-ambiance .cm-comment {
|
|
||||||
color: #555;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-ambiance .cm-string {
|
|
||||||
color: #50a14f;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-ambiance .cm-string-2 {
|
|
||||||
color: #9d937c;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-ambiance .cm-meta {
|
|
||||||
color: #d2a8a1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-ambiance .cm-qualifier {
|
|
||||||
color: yellow;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-ambiance .cm-builtin {
|
|
||||||
color: #9999cc;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-ambiance .cm-bracket {
|
|
||||||
color: #24c2c7;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-ambiance .cm-tag {
|
|
||||||
color: #fee4ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-ambiance .cm-attribute {
|
|
||||||
color: #9b859d;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-ambiance .cm-hr {
|
|
||||||
color: pink;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-ambiance .cm-link {
|
|
||||||
color: #f4c20b;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-ambiance .cm-special {
|
|
||||||
color: #ff9d00;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-ambiance .cm-error {
|
|
||||||
color: #af2018;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-ambiance .CodeMirror-matchingbracket {
|
|
||||||
color: #0f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-ambiance .CodeMirror-nonmatchingbracket {
|
|
||||||
color: #f22;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-ambiance div.CodeMirror-selected {
|
|
||||||
background: rgba(0, 0, 0, 0.15);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-ambiance.CodeMirror-focused div.CodeMirror-selected {
|
|
||||||
background: rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-ambiance .CodeMirror-line::selection,
|
|
||||||
.cm-s-ambiance .CodeMirror-line > span::selection,
|
|
||||||
.cm-s-ambiance .CodeMirror-line > span > span::selection {
|
|
||||||
background: rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-ambiance .CodeMirror-line::-moz-selection,
|
|
||||||
.cm-s-ambiance .CodeMirror-line > span::-moz-selection,
|
|
||||||
.cm-s-ambiance .CodeMirror-line > span > span::-moz-selection {
|
|
||||||
background: rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Editor styling */
|
|
||||||
.cm-s-ambiance.CodeMirror {
|
|
||||||
line-height: 1.4em;
|
|
||||||
color: #383a42;
|
|
||||||
background-color: #f7f7f7;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-ambiance .CodeMirror-gutters {
|
|
||||||
background: #f7f7f7;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-ambiance .CodeMirror-linenumber {
|
|
||||||
color: #666;
|
|
||||||
padding: 0 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-ambiance .CodeMirror-guttermarker {
|
|
||||||
color: #aaa;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-ambiance .CodeMirror-guttermarker-subtle {
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-ambiance .CodeMirror-cursor {
|
|
||||||
border-left: 1px solid #999;
|
|
||||||
margin-left: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-ambiance .CodeMirror-activeline-background {
|
|
||||||
background: none repeat scroll 0% 0% rgba(255, 255, 255, 0.031);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,139 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="cl-dept-check" v-loading="loading">
|
|
||||||
<p v-if="title">{{ title }}</p>
|
|
||||||
|
|
||||||
<div class="cl-dept-check__search">
|
|
||||||
<el-input placeholder="输入关键字进行过滤" v-model="keyword" size="small"> </el-input>
|
|
||||||
<el-switch
|
|
||||||
:active-value="1"
|
|
||||||
:inactive-value="0"
|
|
||||||
v-model="form.relevance"
|
|
||||||
@change="onCheckStrictlyChange"
|
|
||||||
></el-switch
|
|
||||||
>是否关联上下级
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="cl-dept-check__tree" v-if="visible">
|
|
||||||
<el-tree
|
|
||||||
:data="list"
|
|
||||||
:props="props"
|
|
||||||
:default-checked-keys="checked"
|
|
||||||
:filter-node-method="filterNode"
|
|
||||||
:check-strictly="!form.relevance"
|
|
||||||
highlight-current
|
|
||||||
node-key="id"
|
|
||||||
show-checkbox
|
|
||||||
ref="tree"
|
|
||||||
@check-change="onCheckChange"
|
|
||||||
>
|
|
||||||
</el-tree>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { deepTree } from "cl-admin/utils";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "cl-dept-check",
|
|
||||||
|
|
||||||
props: {
|
|
||||||
value: Array,
|
|
||||||
title: String
|
|
||||||
},
|
|
||||||
|
|
||||||
inject: ["form"],
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
list: [],
|
|
||||||
checked: [],
|
|
||||||
keyword: "",
|
|
||||||
props: {
|
|
||||||
label: "name",
|
|
||||||
children: "children"
|
|
||||||
},
|
|
||||||
loading: false,
|
|
||||||
visible: true
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
keyword(val) {
|
|
||||||
this.$refs["tree"].filter(val);
|
|
||||||
},
|
|
||||||
|
|
||||||
value(val) {
|
|
||||||
this.refreshTree(val);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
this.refresh();
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
refreshTree(val) {
|
|
||||||
this.checked = val || [];
|
|
||||||
},
|
|
||||||
|
|
||||||
refresh() {
|
|
||||||
this.$service.system.dept
|
|
||||||
.list()
|
|
||||||
.then(res => {
|
|
||||||
this.list = deepTree(res);
|
|
||||||
this.refreshTree(this.value);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
this.$message.error(err);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
filterNode(val, data) {
|
|
||||||
if (!val) return true;
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.cl-dept-check {
|
|
||||||
&__search {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.el-input {
|
|
||||||
flex: 1;
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-switch {
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__tree {
|
|
||||||
border: 1px solid #dcdfe6;
|
|
||||||
margin-top: 5px;
|
|
||||||
border-radius: 3px;
|
|
||||||
max-height: 200px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
overflow-x: hidden;
|
|
||||||
padding: 5px 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -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>
|
|
||||||
@ -1,422 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="cl-dept-tree">
|
|
||||||
<div class="cl-dept-tree__header">
|
|
||||||
<div>组织架构</div>
|
|
||||||
|
|
||||||
<ul class="cl-dept-tree__op">
|
|
||||||
<li>
|
|
||||||
<el-tooltip content="刷新">
|
|
||||||
<i class="el-icon-refresh" @click="refresh()"></i>
|
|
||||||
</el-tooltip>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li v-if="drag && !browser.isMini">
|
|
||||||
<el-tooltip content="拖动排序">
|
|
||||||
<i class="el-icon-s-operation" @click="isDrag = true"></i>
|
|
||||||
</el-tooltip>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li class="no" v-show="isDrag">
|
|
||||||
<el-button type="text" size="mini" @click="treeOrder(true)">保存</el-button>
|
|
||||||
<el-button type="text" size="mini" @click="treeOrder(false)">取消</el-button>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="cl-dept-tree__container" @contextmenu.prevent="openCM">
|
|
||||||
<el-tree
|
|
||||||
node-key="id"
|
|
||||||
highlight-current
|
|
||||||
default-expand-all
|
|
||||||
:data="list"
|
|
||||||
:props="{
|
|
||||||
label: 'name'
|
|
||||||
}"
|
|
||||||
:draggable="isDrag"
|
|
||||||
:allow-drag="allowDrag"
|
|
||||||
:allow-drop="allowDrop"
|
|
||||||
:expand-on-click-node="false"
|
|
||||||
v-loading="loading"
|
|
||||||
@node-contextmenu="openCM"
|
|
||||||
>
|
|
||||||
<template slot-scope="{ node, data }">
|
|
||||||
<div class="cl-dept-tree__node">
|
|
||||||
<span class="cl-dept-tree__node-label" @click="rowClick(data)">{{
|
|
||||||
node.label
|
|
||||||
}}</span>
|
|
||||||
<span
|
|
||||||
class="cl-dept-tree__node-icon"
|
|
||||||
v-if="browser.isMini"
|
|
||||||
@click="openCM($event, data, node)"
|
|
||||||
>
|
|
||||||
<i class="el-icon-more"></i>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</el-tree>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { deepTree, isArray, revDeepTree } from "cl-admin/utils";
|
|
||||||
import { ContextMenu, Form } from "cl-admin-crud";
|
|
||||||
import { mapGetters } from "vuex";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "cl-dept-tree",
|
|
||||||
|
|
||||||
props: {
|
|
||||||
drag: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
level: {
|
|
||||||
type: Number,
|
|
||||||
default: 99
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
list: [],
|
|
||||||
loading: false,
|
|
||||||
isDrag: false
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
...mapGetters(["browser"])
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
|
||||||
this.refresh();
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
openCM(e, d, n) {
|
|
||||||
if (!d) {
|
|
||||||
d = this.list[0] || {};
|
|
||||||
}
|
|
||||||
|
|
||||||
ContextMenu.open(e, {
|
|
||||||
list: [
|
|
||||||
{
|
|
||||||
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 }) {
|
|
||||||
return data.parentId;
|
|
||||||
},
|
|
||||||
|
|
||||||
allowDrop(_, dropNode) {
|
|
||||||
return dropNode.data.parentId;
|
|
||||||
},
|
|
||||||
|
|
||||||
refresh() {
|
|
||||||
this.isDrag = false;
|
|
||||||
this.loading = true;
|
|
||||||
|
|
||||||
this.$service.system.dept
|
|
||||||
.list()
|
|
||||||
.then(res => {
|
|
||||||
this.list = deepTree(res);
|
|
||||||
this.$emit("list-change", this.list);
|
|
||||||
})
|
|
||||||
.done(() => {
|
|
||||||
this.loading = false;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
rowClick(e) {
|
|
||||||
ContextMenu.close();
|
|
||||||
let ids = e.children ? revDeepTree(e.children).map(e => e.id) : [];
|
|
||||||
ids.unshift(e.id);
|
|
||||||
this.$emit("row-click", { item: e, ids });
|
|
||||||
},
|
|
||||||
|
|
||||||
rowEdit(e) {
|
|
||||||
const method = e.id ? "update" : "add";
|
|
||||||
|
|
||||||
Form.open({
|
|
||||||
title: "编辑部门",
|
|
||||||
width: "550px",
|
|
||||||
props: {
|
|
||||||
"label-width": "100px"
|
|
||||||
},
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
label: "部门名称",
|
|
||||||
prop: "name",
|
|
||||||
value: e.name,
|
|
||||||
component: {
|
|
||||||
name: "el-input",
|
|
||||||
attrs: {
|
|
||||||
placeholder: "请填写部门名称"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
rules: {
|
|
||||||
required: true,
|
|
||||||
message: "部门名称不能为空"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "上级部门",
|
|
||||||
prop: "parentId",
|
|
||||||
value: e.parentName || "...",
|
|
||||||
component: {
|
|
||||||
name: "el-input",
|
|
||||||
attrs: {
|
|
||||||
disabled: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "排序",
|
|
||||||
prop: "orderNum",
|
|
||||||
value: e.orderNum || 0,
|
|
||||||
component: {
|
|
||||||
name: "el-input-number",
|
|
||||||
props: {
|
|
||||||
"controls-position": "right",
|
|
||||||
min: 0,
|
|
||||||
max: 100
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
on: {
|
|
||||||
submit: (data, { done, close }) => {
|
|
||||||
this.$service.system.dept[method]({
|
|
||||||
id: e.id,
|
|
||||||
parentId: e.parentId,
|
|
||||||
name: data.name,
|
|
||||||
orderNum: data.orderNum
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
this.$message.success(`新增部门${data.name}成功`);
|
|
||||||
close();
|
|
||||||
this.refresh();
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
this.$message.error(err);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
rowDel(e) {
|
|
||||||
const del = f => {
|
|
||||||
this.$service.system.dept
|
|
||||||
.delete({
|
|
||||||
ids: [e.id],
|
|
||||||
deleteUser: f
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
if (f) {
|
|
||||||
this.$message.success("删除成功");
|
|
||||||
} else {
|
|
||||||
this.$confirm(
|
|
||||||
`“${e.name}” 部门的用户已成功转移到 “${e.parentName}” 部门。`,
|
|
||||||
"删除成功"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.done(() => {
|
|
||||||
this.refresh();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
this.$confirm(`该操作会删除 “${e.name}” 部门的所有用户,是否确认?`, "提示", {
|
|
||||||
type: "warning",
|
|
||||||
confirmButtonText: "直接删除",
|
|
||||||
cancelButtonText: "保留用户",
|
|
||||||
distinguishCancelAndClose: true
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
del(true);
|
|
||||||
})
|
|
||||||
.catch(action => {
|
|
||||||
if (action == "cancel") {
|
|
||||||
del(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
treeOrder(f) {
|
|
||||||
if (f) {
|
|
||||||
this.$confirm("部门架构已发生改变,是否保存?", "提示", {
|
|
||||||
type: "warning"
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
const deep = (list, pid) => {
|
|
||||||
list.forEach(e => {
|
|
||||||
e.parentId = pid;
|
|
||||||
ids.push(e);
|
|
||||||
|
|
||||||
if (e.children && isArray(e.children)) {
|
|
||||||
deep(e.children, e.id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
let ids = [];
|
|
||||||
|
|
||||||
deep(this.list, null);
|
|
||||||
|
|
||||||
this.$service.system.dept
|
|
||||||
.order(
|
|
||||||
ids.map((e, i) => {
|
|
||||||
return {
|
|
||||||
id: e.id,
|
|
||||||
parentId: e.parentId,
|
|
||||||
orderNum: i
|
|
||||||
};
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.then(() => {
|
|
||||||
this.$message.success("更新排序成功");
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
this.$message.error(err);
|
|
||||||
})
|
|
||||||
.done(() => {
|
|
||||||
this.refresh();
|
|
||||||
this.isDrag = false;
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(() => {});
|
|
||||||
} else {
|
|
||||||
this.refresh();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.cl-dept-tree {
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
&__header {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
height: 40px;
|
|
||||||
padding: 0 10px;
|
|
||||||
background-color: #fff;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
div {
|
|
||||||
font-size: 14px;
|
|
||||||
flex: 1;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
i {
|
|
||||||
font-size: 18px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/deep/.el-tree-node__content {
|
|
||||||
height: 36px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__op {
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
li {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
list-style: none;
|
|
||||||
margin-left: 5px;
|
|
||||||
padding: 5px;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:not(.no):hover {
|
|
||||||
background-color: #eee;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__container {
|
|
||||||
height: calc(100% - 40px);
|
|
||||||
overflow-y: auto;
|
|
||||||
overflow-x: hidden;
|
|
||||||
|
|
||||||
/deep/.el-tree-node__content {
|
|
||||||
margin: 0 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__node {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
&-label {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
flex: 1;
|
|
||||||
height: 100%;
|
|
||||||
font-size: 14px;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-icon {
|
|
||||||
height: 28px;
|
|
||||||
width: 28px;
|
|
||||||
line-height: 28px;
|
|
||||||
text-align: center;
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,245 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="cl-editor-quill">
|
|
||||||
<div class="editor" :style="style"></div>
|
|
||||||
|
|
||||||
<cl-upload-space
|
|
||||||
ref="upload-space"
|
|
||||||
detail-data
|
|
||||||
:show-button="false"
|
|
||||||
@confirm="onFileConfirm"
|
|
||||||
>
|
|
||||||
</cl-upload-space>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import Quill from "quill";
|
|
||||||
import "quill/dist/quill.snow.css";
|
|
||||||
import { isNumber } from "cl-admin/utils";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "cl-editor-quill",
|
|
||||||
|
|
||||||
props: {
|
|
||||||
value: null,
|
|
||||||
height: [String, Number],
|
|
||||||
width: [String, Number],
|
|
||||||
options: Object
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
content: "",
|
|
||||||
quill: null,
|
|
||||||
cursorIndex: 0
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
style() {
|
|
||||||
const height = isNumber(this.height) ? this.height + "px" : this.height;
|
|
||||||
const width = isNumber(this.width) ? this.width + "px" : this.width;
|
|
||||||
|
|
||||||
return {
|
|
||||||
height,
|
|
||||||
width
|
|
||||||
};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
value(val) {
|
|
||||||
if (val) {
|
|
||||||
if (val !== this.content) {
|
|
||||||
this.setContent(val);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.setContent("");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
content(val) {
|
|
||||||
this.$emit("input", val);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
// 实例化
|
|
||||||
this.quill = new Quill(this.$el.querySelector(".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"]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
...this.options
|
|
||||||
});
|
|
||||||
|
|
||||||
// 添加文件上传工具
|
|
||||||
this.quill.getModule("toolbar").addHandler("image", this.uploadHandler);
|
|
||||||
|
|
||||||
// 监听内容变化
|
|
||||||
this.quill.on("text-change", () => {
|
|
||||||
this.content = this.quill.root.innerHTML;
|
|
||||||
});
|
|
||||||
|
|
||||||
// 设置默认内容
|
|
||||||
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>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.cl-editor-quill {
|
|
||||||
background-color: #fff;
|
|
||||||
|
|
||||||
.ql-snow {
|
|
||||||
line-height: 22px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-upload,
|
|
||||||
#quill-upload-btn {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ql-snow {
|
|
||||||
border: 1px solid #dcdfe6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ql-snow .ql-tooltip[data-mode="link"]::before {
|
|
||||||
content: "请输入链接地址:";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ql-snow .ql-tooltip.ql-editing a.ql-action::after {
|
|
||||||
border-right: 0px;
|
|
||||||
content: "保存";
|
|
||||||
padding-right: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ql-snow .ql-tooltip[data-mode="video"]::before {
|
|
||||||
content: "请输入视频地址:";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ql-snow .ql-picker.ql-size .ql-picker-label::before,
|
|
||||||
.ql-snow .ql-picker.ql-size .ql-picker-item::before {
|
|
||||||
content: "14px";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="small"]::before,
|
|
||||||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="small"]::before {
|
|
||||||
content: "10px";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="large"]::before,
|
|
||||||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="large"]::before {
|
|
||||||
content: "18px";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="huge"]::before,
|
|
||||||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="huge"]::before {
|
|
||||||
content: "32px";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ql-snow .ql-picker.ql-header .ql-picker-label::before,
|
|
||||||
.ql-snow .ql-picker.ql-header .ql-picker-item::before {
|
|
||||||
content: "文本";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before,
|
|
||||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
|
|
||||||
content: "标题1";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before,
|
|
||||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
|
|
||||||
content: "标题2";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before,
|
|
||||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
|
|
||||||
content: "标题3";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before,
|
|
||||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
|
|
||||||
content: "标题4";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before,
|
|
||||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
|
|
||||||
content: "标题5";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before,
|
|
||||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
|
|
||||||
content: "标题6";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ql-snow .ql-picker.ql-font .ql-picker-label::before,
|
|
||||||
.ql-snow .ql-picker.ql-font .ql-picker-item::before {
|
|
||||||
content: "标准字体";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="serif"]::before,
|
|
||||||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="serif"]::before {
|
|
||||||
content: "衬线字体";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="monospace"]::before,
|
|
||||||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="monospace"]::before {
|
|
||||||
content: "等宽字体";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,56 +0,0 @@
|
|||||||
<template>
|
|
||||||
<svg :class="svgClass" :style="style2" aria-hidden="true">
|
|
||||||
<use :xlink:href="iconName"></use>
|
|
||||||
</svg>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { isNumber } from "cl-admin/utils";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "icon-svg",
|
|
||||||
|
|
||||||
props: {
|
|
||||||
name: {
|
|
||||||
type: String
|
|
||||||
},
|
|
||||||
className: {
|
|
||||||
type: String
|
|
||||||
},
|
|
||||||
size: {
|
|
||||||
type: [String, Number]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
style2: {}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
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>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.icon-svg {
|
|
||||||
width: 1em;
|
|
||||||
height: 1em;
|
|
||||||
vertical-align: -0.15em;
|
|
||||||
fill: currentColor;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -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
|
|
||||||
};
|
|
||||||
@ -1,77 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="cl-menu-file">
|
|
||||||
<el-select v-model="newValue" allow-create filterable clearable placeholder="请选择">
|
|
||||||
<el-option
|
|
||||||
v-for="(item, index) in list"
|
|
||||||
:key="index"
|
|
||||||
:label="item.value"
|
|
||||||
:value="item.value"
|
|
||||||
>
|
|
||||||
</el-option>
|
|
||||||
</el-select>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
const files = require
|
|
||||||
.context("@/", true, /views\/(?!(components)|(.*\/components)|(index\.js)).*.(js|vue)/)
|
|
||||||
.keys();
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "cl-menu-file",
|
|
||||||
|
|
||||||
props: {
|
|
||||||
value: [String]
|
|
||||||
},
|
|
||||||
|
|
||||||
inject: ["form"],
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
newValue: "",
|
|
||||||
list: []
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
value: {
|
|
||||||
immediate: true,
|
|
||||||
handler(val) {
|
|
||||||
this.newValue = val || "";
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
newValue(val) {
|
|
||||||
this.$emit("input", val);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
|
||||||
this.list = files.map(e => {
|
|
||||||
return {
|
|
||||||
value: e.substr(2)
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.cl-menu-file {
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
/deep/ .el-select {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__module {
|
|
||||||
display: inline-flex;
|
|
||||||
|
|
||||||
.label {
|
|
||||||
width: 40px;
|
|
||||||
text-align: right;
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,95 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="cl-menu-icons">
|
|
||||||
<el-popover
|
|
||||||
ref="iconPopover"
|
|
||||||
placement="bottom-start"
|
|
||||||
trigger="click"
|
|
||||||
popper-class="popper-menu-icon"
|
|
||||||
>
|
|
||||||
<el-row :gutter="10" class="list">
|
|
||||||
<el-col :span="3" :xs="4" v-for="(item, index) in list" :key="index">
|
|
||||||
<el-button
|
|
||||||
size="mini"
|
|
||||||
:class="{ 'is-active': item === value }"
|
|
||||||
@click="onUpdate(item)"
|
|
||||||
>
|
|
||||||
<icon-svg :name="item"></icon-svg>
|
|
||||||
</el-button>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</el-popover>
|
|
||||||
|
|
||||||
<el-input
|
|
||||||
v-model="name"
|
|
||||||
v-popover:iconPopover
|
|
||||||
placeholder="请选择"
|
|
||||||
@input="onUpdate"
|
|
||||||
></el-input>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { iconList } from "@/cool/modules/base";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "cl-menu-icons",
|
|
||||||
|
|
||||||
props: {
|
|
||||||
value: String
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
list: [],
|
|
||||||
name: ""
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
value: {
|
|
||||||
immediate: true,
|
|
||||||
handler(val) {
|
|
||||||
this.name = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
this.list = iconList();
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
onUpdate(icon) {
|
|
||||||
this.$emit("input", icon);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
.
|
|
||||||
<style lang="scss">
|
|
||||||
.popper-menu-icon {
|
|
||||||
width: 480px;
|
|
||||||
max-width: 90%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
.list {
|
|
||||||
height: 250px;
|
|
||||||
overflow-y: auto;
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-button {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
height: 40px;
|
|
||||||
width: 100%;
|
|
||||||
padding: 0;
|
|
||||||
|
|
||||||
.icon-svg {
|
|
||||||
font-size: 18px;
|
|
||||||
color: #444;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,97 +0,0 @@
|
|||||||
<template>
|
|
||||||
<el-cascader
|
|
||||||
:options="options"
|
|
||||||
:props="{ multiple: true }"
|
|
||||||
separator=":"
|
|
||||||
clearable
|
|
||||||
filterable
|
|
||||||
v-model="newValue"
|
|
||||||
@change="onChange"
|
|
||||||
></el-cascader>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: "cl-menu-perms",
|
|
||||||
|
|
||||||
props: {
|
|
||||||
value: [String, Number, Array]
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
options: [],
|
|
||||||
newValue: []
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
value() {
|
|
||||||
this.parse();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
|
||||||
let options = [];
|
|
||||||
let list = [];
|
|
||||||
|
|
||||||
const flat = obj => {
|
|
||||||
for (let i in obj) {
|
|
||||||
let { permission } = obj[i];
|
|
||||||
|
|
||||||
if (permission) {
|
|
||||||
list = [...list, Object.values(permission)].flat();
|
|
||||||
} else {
|
|
||||||
flat(obj[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
flat(this.$service);
|
|
||||||
|
|
||||||
list.filter(e => e.includes(":"))
|
|
||||||
.map(e => e.split(":"))
|
|
||||||
.forEach(arr => {
|
|
||||||
const col = (i, d) => {
|
|
||||||
let key = arr[i];
|
|
||||||
|
|
||||||
let index = d.findIndex(e => e.label == key);
|
|
||||||
|
|
||||||
if (index >= 0) {
|
|
||||||
col(i + 1, d[index].children);
|
|
||||||
} else {
|
|
||||||
let isLast = i == arr.length - 1;
|
|
||||||
|
|
||||||
d.push({
|
|
||||||
label: key,
|
|
||||||
value: key,
|
|
||||||
children: isLast ? null : []
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!isLast) {
|
|
||||||
col(i + 1, d[d.length - 1].children || []);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
col(0, options);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.options = options;
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
this.parse();
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
parse() {
|
|
||||||
this.newValue = this.value ? this.value.split(",").map(e => e.split(":")) : [];
|
|
||||||
},
|
|
||||||
|
|
||||||
onChange(row) {
|
|
||||||
this.$emit("input", row.map(e => e.join(":")).join(","));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
@ -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>
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@ -1,81 +0,0 @@
|
|||||||
.cl-slider-menu {
|
|
||||||
height: 100%;
|
|
||||||
overflow-y: auto;
|
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-menu {
|
|
||||||
border-right: 0;
|
|
||||||
|
|
||||||
.el-submenu__title,
|
|
||||||
&-item {
|
|
||||||
&.is-active,
|
|
||||||
&:hover {
|
|
||||||
background-color: $color-primary !important;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-submenu__title,
|
|
||||||
&-item,
|
|
||||||
&__title {
|
|
||||||
color: #eee;
|
|
||||||
letter-spacing: 0.5px;
|
|
||||||
height: 50px;
|
|
||||||
line-height: 50px;
|
|
||||||
|
|
||||||
.icon-svg {
|
|
||||||
font-size: 16px;
|
|
||||||
margin: 0 15px 0 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
span {
|
|
||||||
font-size: 12px;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&--collapse {
|
|
||||||
.el-submenu__title {
|
|
||||||
.icon-svg {
|
|
||||||
margin-left: 2px;
|
|
||||||
font-size: 19px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.cl-slider-menu__submenu {
|
|
||||||
background-color: #fff;
|
|
||||||
|
|
||||||
&.el-menu {
|
|
||||||
&--vertical {
|
|
||||||
.el-submenu {
|
|
||||||
&__title {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.icon-svg {
|
|
||||||
font-size: 18px;
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-menu-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.icon-svg {
|
|
||||||
font-size: 18px;
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,110 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="cl-menu-topbar">
|
|
||||||
<el-menu
|
|
||||||
:default-active="index"
|
|
||||||
mode="horizontal"
|
|
||||||
background-color="transparent"
|
|
||||||
@select="onSelect"
|
|
||||||
>
|
|
||||||
<el-menu-item v-for="(item, index) in list" :index="`${index}`" :key="index">
|
|
||||||
<icon-svg v-if="item.icon" :name="item.icon" :size="18"></icon-svg>
|
|
||||||
<span>{{ item.name }}</span>
|
|
||||||
</el-menu-item>
|
|
||||||
</el-menu>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { mapMutations } from "vuex";
|
|
||||||
import { firstMenu } from "../../utils";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "cl-menu-topbar",
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
index: "0"
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
list() {
|
|
||||||
return this.$store.getters.menuGroup.filter(e => e.isShow);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
const deep = (e, i) => {
|
|
||||||
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:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.list.forEach((e, i) => {
|
|
||||||
deep(e, i);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
...mapMutations(["SET_MENU_LIST"]),
|
|
||||||
|
|
||||||
onSelect(index) {
|
|
||||||
this.SET_MENU_LIST(index);
|
|
||||||
|
|
||||||
// 获取第一个菜单地址
|
|
||||||
const url = firstMenu(this.list[index].children);
|
|
||||||
this.$router.push(url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.cl-menu-topbar {
|
|
||||||
margin-right: 10px;
|
|
||||||
|
|
||||||
/deep/.el-menu {
|
|
||||||
height: 50px;
|
|
||||||
background: transparent;
|
|
||||||
border-bottom: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
.el-menu-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
height: 50px;
|
|
||||||
border-bottom: 0;
|
|
||||||
padding: 0 20px;
|
|
||||||
background: transparent;
|
|
||||||
|
|
||||||
span {
|
|
||||||
font-size: 12px;
|
|
||||||
margin-left: 3px;
|
|
||||||
line-height: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.is-active {
|
|
||||||
color: $color-primary;
|
|
||||||
}
|
|
||||||
|
|
||||||
/deep/.icon-svg {
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,108 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="cl-menu-tree">
|
|
||||||
<el-popover
|
|
||||||
ref="popover"
|
|
||||||
placement="bottom-start"
|
|
||||||
trigger="click"
|
|
||||||
popper-class="popper-menu-tree"
|
|
||||||
>
|
|
||||||
<el-input size="small" v-model="filterValue">
|
|
||||||
<i slot="prefix" class="el-input__icon el-icon-search"></i>
|
|
||||||
</el-input>
|
|
||||||
|
|
||||||
<el-tree
|
|
||||||
ref="tree"
|
|
||||||
node-key="menuId"
|
|
||||||
:data="treeList"
|
|
||||||
:props="props"
|
|
||||||
:highlight-current="true"
|
|
||||||
:expand-on-click-node="false"
|
|
||||||
:default-expanded-keys="expandedKeys"
|
|
||||||
:filter-node-method="filterNode"
|
|
||||||
@current-change="currentChange"
|
|
||||||
>
|
|
||||||
</el-tree>
|
|
||||||
</el-popover>
|
|
||||||
<el-input v-model="name" v-popover:popover readonly placeholder="请选择"></el-input>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { deepTree } from "cl-admin/utils";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "cl-menu-tree",
|
|
||||||
|
|
||||||
props: {
|
|
||||||
value: [Number, String]
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
filterValue: "",
|
|
||||||
list: [],
|
|
||||||
props: {
|
|
||||||
label: "name",
|
|
||||||
children: "children"
|
|
||||||
},
|
|
||||||
expandedKeys: []
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
filterValue(val) {
|
|
||||||
this.$refs.tree.filter(val);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
name() {
|
|
||||||
const item = this.list.find(e => e.id == this.value);
|
|
||||||
return item ? item.name : "一级菜单";
|
|
||||||
},
|
|
||||||
|
|
||||||
treeList() {
|
|
||||||
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: "一级菜单",
|
|
||||||
id: null
|
|
||||||
});
|
|
||||||
|
|
||||||
this.list = list;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
filterNode(value, data) {
|
|
||||||
if (!value) return true;
|
|
||||||
return data.name.indexOf(value) !== -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.popper-menu-tree {
|
|
||||||
width: 480px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
.el-input {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,211 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="app-process">
|
|
||||||
<div class="app-process__left hidden-xs-only" @click="toScroll(true)">
|
|
||||||
<i class="el-icon-arrow-left"></i>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="app-process__scroller" ref="scroller">
|
|
||||||
<div
|
|
||||||
class="app-process__item"
|
|
||||||
v-for="(item, index) in processList"
|
|
||||||
:key="index"
|
|
||||||
:ref="`item-${index}`"
|
|
||||||
:class="{ active: item.active }"
|
|
||||||
:data-index="index"
|
|
||||||
@click="onTap(item, index)"
|
|
||||||
@contextmenu.stop.prevent="openCM($event, item)"
|
|
||||||
>
|
|
||||||
<span>{{ item.label }}</span>
|
|
||||||
|
|
||||||
<i class="el-icon-close" v-if="index > 0" @click.stop="onDel(index)"></i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="app-process__right hidden-xs-only" @click="toScroll(false)">
|
|
||||||
<i class="el-icon-arrow-right"></i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { mapGetters, mapMutations } from "vuex";
|
|
||||||
import { ContextMenu } from "cl-admin-crud";
|
|
||||||
import { last } from "cl-admin/utils";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "cl-process",
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
...mapGetters(["processList"])
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
"$route.path"(val) {
|
|
||||||
this.adScroll(this.processList.findIndex(e => e.value === val) || 0);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
...mapMutations(["ADD_PROCESS", "DEL_PROCESS", "SET_PROCESS"]),
|
|
||||||
|
|
||||||
onTap(item, index) {
|
|
||||||
this.adScroll(index);
|
|
||||||
this.$router.push(item.value);
|
|
||||||
},
|
|
||||||
|
|
||||||
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) {
|
|
||||||
const next = last(this.processList);
|
|
||||||
this.$router.push(next ? next.value : "/");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
adScroll(index) {
|
|
||||||
if (index < 0) {
|
|
||||||
index = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const el = this.$refs[`item-${index}`][0];
|
|
||||||
|
|
||||||
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,
|
|
||||||
behavior: "smooth"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.app-process {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
height: 30px;
|
|
||||||
position: relative;
|
|
||||||
&__left,
|
|
||||||
&__right {
|
|
||||||
background-color: #fff;
|
|
||||||
height: 30px;
|
|
||||||
line-height: 30px;
|
|
||||||
padding: 0 2px;
|
|
||||||
border-radius: 3px;
|
|
||||||
cursor: pointer;
|
|
||||||
&:hover {
|
|
||||||
background-color: #eee;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&__left {
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
&__right {
|
|
||||||
margin-left: 10px;
|
|
||||||
}
|
|
||||||
&__scroller {
|
|
||||||
width: 100%;
|
|
||||||
flex: 1;
|
|
||||||
overflow-x: auto;
|
|
||||||
overflow-y: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
&::-webkit-scrollbar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&__item {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
border-radius: 3px;
|
|
||||||
height: 30px;
|
|
||||||
line-height: 30px;
|
|
||||||
padding: 0 10px;
|
|
||||||
background-color: #fff;
|
|
||||||
font-size: 12px;
|
|
||||||
margin-right: 10px;
|
|
||||||
color: #909399;
|
|
||||||
cursor: pointer;
|
|
||||||
&:last-child {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
i {
|
|
||||||
font-size: 14px;
|
|
||||||
width: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
transition: all 0.3s;
|
|
||||||
&:hover {
|
|
||||||
color: #fff;
|
|
||||||
background-color: $color-primary;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&:hover {
|
|
||||||
.el-icon-close {
|
|
||||||
width: 14px;
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.active {
|
|
||||||
span {
|
|
||||||
color: $color-primary;
|
|
||||||
}
|
|
||||||
i {
|
|
||||||
width: auto;
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
&:before {
|
|
||||||
background-color: $color-primary;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,128 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="cl-role-perms" v-loading="loading">
|
|
||||||
<p v-if="title">{{ title }}</p>
|
|
||||||
|
|
||||||
<el-input placeholder="输入关键字进行过滤" v-model="keyword" size="small"> </el-input>
|
|
||||||
|
|
||||||
<div class="scroller">
|
|
||||||
<el-tree
|
|
||||||
:data="list"
|
|
||||||
:props="props"
|
|
||||||
:default-checked-keys="checked"
|
|
||||||
:filter-node-method="filterNode"
|
|
||||||
highlight-current
|
|
||||||
node-key="id"
|
|
||||||
show-checkbox
|
|
||||||
ref="tree"
|
|
||||||
@check-change="save"
|
|
||||||
>
|
|
||||||
</el-tree>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { deepTree } from "cl-admin/utils";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "cl-role-perms",
|
|
||||||
|
|
||||||
props: {
|
|
||||||
value: Array,
|
|
||||||
title: String
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
list: [],
|
|
||||||
checked: [],
|
|
||||||
keyword: "",
|
|
||||||
props: {
|
|
||||||
label: "name",
|
|
||||||
children: "children"
|
|
||||||
},
|
|
||||||
loading: false
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
keyword(val) {
|
|
||||||
this.$refs["tree"].filter(val);
|
|
||||||
},
|
|
||||||
|
|
||||||
value(val) {
|
|
||||||
this.refreshTree(val);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
this.refresh();
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
refreshTree(val) {
|
|
||||||
if (!val) {
|
|
||||||
this.checked = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
let ids = [];
|
|
||||||
|
|
||||||
// 处理半选状态
|
|
||||||
let fn = list => {
|
|
||||||
list.forEach(e => {
|
|
||||||
if (e.children) {
|
|
||||||
fn(e.children);
|
|
||||||
} else {
|
|
||||||
ids.push(Number(e.id));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
fn(this.list);
|
|
||||||
|
|
||||||
this.checked = ids.filter(id => (val || []).find(e => e == id));
|
|
||||||
},
|
|
||||||
|
|
||||||
refresh() {
|
|
||||||
this.$service.system.menu
|
|
||||||
.list()
|
|
||||||
.then(res => {
|
|
||||||
this.list = deepTree(res);
|
|
||||||
|
|
||||||
this.refreshTree(this.value);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
this.$message.error(err);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
filterNode(val, data) {
|
|
||||||
if (!val) return true;
|
|
||||||
return data.name.includes(val);
|
|
||||||
},
|
|
||||||
|
|
||||||
save() {
|
|
||||||
const tree = this.$refs["tree"];
|
|
||||||
|
|
||||||
// 选中的节点
|
|
||||||
const checked = tree.getCheckedKeys();
|
|
||||||
// 半选中的节点
|
|
||||||
const halfChecked = tree.getHalfCheckedKeys();
|
|
||||||
|
|
||||||
this.$emit("input", [...checked, ...halfChecked]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.scroller {
|
|
||||||
border: 1px solid #dcdfe6;
|
|
||||||
margin-top: 5px;
|
|
||||||
border-radius: 3px;
|
|
||||||
max-height: 200px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
overflow-x: hidden;
|
|
||||||
padding: 5px 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,55 +0,0 @@
|
|||||||
<template>
|
|
||||||
<el-select v-model="newValue" v-bind="props" multiple @change="onChange">
|
|
||||||
<el-option
|
|
||||||
v-for="(item, index) in list"
|
|
||||||
:value="item.id"
|
|
||||||
:label="item.name"
|
|
||||||
:key="index"
|
|
||||||
></el-option>
|
|
||||||
</el-select>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: "cl-role-select",
|
|
||||||
|
|
||||||
props: {
|
|
||||||
value: [String, Number, Array],
|
|
||||||
props: Object
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
list: [],
|
|
||||||
newValue: undefined
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
value: {
|
|
||||||
immediate: true,
|
|
||||||
handler(val) {
|
|
||||||
let arr = [];
|
|
||||||
|
|
||||||
if (!(val instanceof Array)) {
|
|
||||||
arr = [val];
|
|
||||||
} else {
|
|
||||||
arr = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.newValue = arr.filter(Boolean);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async created() {
|
|
||||||
this.list = await this.$service.system.role.list();
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
onChange(val) {
|
|
||||||
this.$emit("input", val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
@ -1,99 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="cl-route-nav">
|
|
||||||
<p class="title" v-if="browser.isMini">
|
|
||||||
{{ lastName }}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<template v-else>
|
|
||||||
<el-breadcrumb>
|
|
||||||
<el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
|
|
||||||
<el-breadcrumb-item v-for="(item, index) in list" :key="index">{{
|
|
||||||
(item.meta && item.meta.label) || item.name
|
|
||||||
}}</el-breadcrumb-item>
|
|
||||||
</el-breadcrumb>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { mapGetters } from "vuex";
|
|
||||||
import _ from "lodash";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "cl-route-nav",
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
list: []
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
$route: {
|
|
||||||
immediate: true,
|
|
||||||
handler(route) {
|
|
||||||
const deep = item => {
|
|
||||||
if (route.path === "/") {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item.path == route.path) {
|
|
||||||
return item;
|
|
||||||
} else {
|
|
||||||
if (item.children) {
|
|
||||||
const ret = item.children.map(deep).find(Boolean);
|
|
||||||
|
|
||||||
if (ret) {
|
|
||||||
return [item, ret];
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.list = _(this.menuGroup)
|
|
||||||
.map(deep)
|
|
||||||
.filter(Boolean)
|
|
||||||
.flattenDeep()
|
|
||||||
.value();
|
|
||||||
|
|
||||||
if (this.list.length === 0) {
|
|
||||||
this.list.push(route);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
...mapGetters(["menuGroup", "browser"]),
|
|
||||||
|
|
||||||
lastName() {
|
|
||||||
return _.last(this.list).name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.cl-route-nav {
|
|
||||||
white-space: nowrap;
|
|
||||||
|
|
||||||
/deep/.el-breadcrumb {
|
|
||||||
&__inner {
|
|
||||||
font-size: 13px;
|
|
||||||
padding: 0 10px;
|
|
||||||
font-weight: normal;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,53 +0,0 @@
|
|||||||
<template>
|
|
||||||
<el-scrollbar
|
|
||||||
class="cl-scrollbar"
|
|
||||||
:view-style="[
|
|
||||||
{
|
|
||||||
'overflow-x': 'hidden',
|
|
||||||
width
|
|
||||||
},
|
|
||||||
viewStyle
|
|
||||||
]"
|
|
||||||
:native="native"
|
|
||||||
:wrap-style="wrapStyle"
|
|
||||||
:wrap-class="wrapClass"
|
|
||||||
:view-class="viewClass"
|
|
||||||
:noresize="noresize"
|
|
||||||
:tag="tag"
|
|
||||||
>
|
|
||||||
<slot></slot>
|
|
||||||
</el-scrollbar>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { getBrowser } from "cl-admin/utils";
|
|
||||||
|
|
||||||
const { plat } = getBrowser();
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "cl-scrollbar",
|
|
||||||
|
|
||||||
props: {
|
|
||||||
native: Boolean,
|
|
||||||
wrapStyle: Object,
|
|
||||||
wrapClass: Object,
|
|
||||||
viewClass: Object,
|
|
||||||
viewStyle: Object,
|
|
||||||
noresize: Boolean,
|
|
||||||
tag: {
|
|
||||||
type: String,
|
|
||||||
default: "div"
|
|
||||||
},
|
|
||||||
direction: {
|
|
||||||
type: String,
|
|
||||||
default: "vertical" // auto, vertical, horizontal
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
width() {
|
|
||||||
return `calc(100% - ${plat == "iphone" ? "10px" : "0px"})`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
import permission, { checkPerm } from "./permission";
|
|
||||||
|
|
||||||
export { checkPerm };
|
|
||||||
|
|
||||||
export default {
|
|
||||||
permission
|
|
||||||
};
|
|
||||||
@ -1,42 +0,0 @@
|
|||||||
import store from "@/store";
|
|
||||||
|
|
||||||
function change(el, binding) {
|
|
||||||
el.style.display = checkPerm(binding.value) ? el.getAttribute("_display") : "none";
|
|
||||||
}
|
|
||||||
|
|
||||||
function parse(value) {
|
|
||||||
const permission = store.getters.permission;
|
|
||||||
|
|
||||||
if (typeof value == "string") {
|
|
||||||
return value ? permission.some(e => e.includes(value.replace(/\s/g, ""))) : false;
|
|
||||||
} else {
|
|
||||||
return Boolean(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
|
||||||
inserted(el, binding) {
|
|
||||||
el.setAttribute("_display", el.style.display || "");
|
|
||||||
|
|
||||||
change(el, binding);
|
|
||||||
},
|
|
||||||
update: change
|
|
||||||
};
|
|
||||||
|
|
||||||
export const checkPerm = value => {
|
|
||||||
if (!value) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Object.prototype.toString.call(value) === "[object Object]") {
|
|
||||||
if (value.or) {
|
|
||||||
return value.or.some(parse);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.and) {
|
|
||||||
return value.and.some(e => !parse(e)) ? false : true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return parse(value);
|
|
||||||
};
|
|
||||||
@ -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,12 +0,0 @@
|
|||||||
import components from "./components";
|
|
||||||
import filters from "./filters";
|
|
||||||
import pages from "./pages";
|
|
||||||
import views from "./views";
|
|
||||||
import store from "./store";
|
|
||||||
import service from "./service";
|
|
||||||
import directives, { checkPerm } from "./directives";
|
|
||||||
import { iconList } from "./common";
|
|
||||||
import "./static/css/index.scss";
|
|
||||||
|
|
||||||
export { iconList, checkPerm };
|
|
||||||
export default { components, filters, pages, views, store, service, directives };
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
<template>
|
|
||||||
<error-page :code="403" desc="您无权访问此页面" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import ErrorPage from "./components/error-page";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
ErrorPage
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
<template>
|
|
||||||
<error-page :code="404" desc="找不到您要查找的页面"></error-page>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import ErrorPage from "./components/error-page";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
ErrorPage
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
<template>
|
|
||||||
<error-page :code="500" desc="糟糕,出了点问题"></error-page>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import ErrorPage from "./components/error-page";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
ErrorPage
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
<template>
|
|
||||||
<error-page :code="502" desc="马上回来"></error-page>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import ErrorPage from "./components/error-page";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
ErrorPage
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
@ -1,157 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="error-page">
|
|
||||||
<h1 class="code">{{ code }}</h1>
|
|
||||||
<p class="desc">{{ desc }}</p>
|
|
||||||
|
|
||||||
<template v-if="token || isLogout">
|
|
||||||
<div class="router">
|
|
||||||
<el-select size="medium" filterable prefix-icon="el-icon-search" v-model="url">
|
|
||||||
<el-option v-for="(item, index) in routes" :key="index" :value="item.path">
|
|
||||||
<span style="float: left">{{ item.name }}</span>
|
|
||||||
<span style="float: right">{{ item.path }}</span>
|
|
||||||
</el-option>
|
|
||||||
</el-select>
|
|
||||||
|
|
||||||
<el-button round @click="navTo">跳转</el-button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="link">
|
|
||||||
<el-link class="to-home" @click="home">回到首页</el-link>
|
|
||||||
<el-link class="to-back" @click="back">返回上一页</el-link>
|
|
||||||
<el-link class="to-login" @click="reLogin">重新登录</el-link>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-else>
|
|
||||||
<div class="router">
|
|
||||||
<el-button round @click="toLogin">返回登录页</el-button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<p class="copyright">Copyright © cool-admin-pro 2020</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { mapGetters } from "vuex";
|
|
||||||
import { href } from "cl-admin/utils";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
code: Number,
|
|
||||||
desc: String
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
url: "",
|
|
||||||
isLogout: false
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
...mapGetters(["routes", "token"])
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
navTo() {
|
|
||||||
this.$router.push(this.url);
|
|
||||||
},
|
|
||||||
|
|
||||||
toLogin() {
|
|
||||||
this.$router.push("/login");
|
|
||||||
},
|
|
||||||
|
|
||||||
reLogin() {
|
|
||||||
this.isLogout = true;
|
|
||||||
|
|
||||||
this.$store.dispatch("userLogout").done(() => {
|
|
||||||
href("/login");
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
back() {
|
|
||||||
history.back();
|
|
||||||
},
|
|
||||||
|
|
||||||
home() {
|
|
||||||
this.$router.push("/");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.error-page {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
height: 100%;
|
|
||||||
overflow-y: auto;
|
|
||||||
|
|
||||||
.code {
|
|
||||||
font-size: 120px;
|
|
||||||
font-weight: normal;
|
|
||||||
color: #6c757d;
|
|
||||||
font-family: "Segoe UI";
|
|
||||||
}
|
|
||||||
|
|
||||||
.desc {
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 400;
|
|
||||||
color: #34395e;
|
|
||||||
margin-top: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.router {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
margin-top: 50px;
|
|
||||||
max-width: 450px;
|
|
||||||
width: 90%;
|
|
||||||
|
|
||||||
.el-select {
|
|
||||||
font-size: 14px;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-button {
|
|
||||||
margin-left: 15px;
|
|
||||||
background-color: $color-primary;
|
|
||||||
border-color: $color-primary;
|
|
||||||
color: #fff;
|
|
||||||
padding: 0 30px;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
height: 36px;
|
|
||||||
line-height: 36px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.link {
|
|
||||||
margin-top: 40px;
|
|
||||||
|
|
||||||
a {
|
|
||||||
font-weight: 500;
|
|
||||||
transition: all 0.5s;
|
|
||||||
-webkit-transition: all 0.5s;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 14px;
|
|
||||||
margin: 0 15px;
|
|
||||||
padding-bottom: 2px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.copyright {
|
|
||||||
color: #6c757d;
|
|
||||||
font-size: 14px;
|
|
||||||
position: fixed;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
height: 50px;
|
|
||||||
line-height: 50px;
|
|
||||||
width: 100%;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,43 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="page-iframe" v-loading="loading" element-loading-text="拼命加载中">
|
|
||||||
<iframe :src="url" frameborder="0"></iframe>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
loading: false,
|
|
||||||
url: ""
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
$route: {
|
|
||||||
handler({ meta }) {
|
|
||||||
this.url = meta.iframeUrl;
|
|
||||||
},
|
|
||||||
immediate: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
const iframe = this.$el.querySelector("iframe");
|
|
||||||
this.loading = true;
|
|
||||||
|
|
||||||
iframe.onload = () => {
|
|
||||||
this.loading = false;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.page-iframe {
|
|
||||||
iframe {
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -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")
|
|
||||||
}
|
|
||||||
];
|
|
||||||
@ -1,64 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="common-captcha" @click="refresh">
|
|
||||||
<div class="svg" v-html="svg" v-if="svg"></div>
|
|
||||||
|
|
||||||
<img class="base64" :src="base64" alt="" v-else />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
svg: "",
|
|
||||||
base64: ""
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
this.refresh();
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
refresh() {
|
|
||||||
this.$service.open
|
|
||||||
.captcha({
|
|
||||||
height: 36,
|
|
||||||
width: 110
|
|
||||||
})
|
|
||||||
.then(({ captchaId, data }) => {
|
|
||||||
if (data.includes(";base64,")) {
|
|
||||||
this.base64 = data;
|
|
||||||
} else {
|
|
||||||
this.svg = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$emit("input", captchaId);
|
|
||||||
this.$emit("change", {
|
|
||||||
base64: this.base64,
|
|
||||||
svg: this.svg,
|
|
||||||
captchaId
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
this.$message.error(err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.common-captcha {
|
|
||||||
height: 36px;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
.svg {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.base64 {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,193 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="page-login">
|
|
||||||
<div class="box">
|
|
||||||
<img class="logo" src="../../static/images/logo.png" alt="" />
|
|
||||||
<p class="desc">COOL ADMIN是一款快速开发后台权限管理系统</p>
|
|
||||||
|
|
||||||
<el-form ref="form" class="form" size="medium" :disabled="saving">
|
|
||||||
<el-form-item label="用户名">
|
|
||||||
<el-input
|
|
||||||
placeholder="请输入用户名"
|
|
||||||
v-model="form.username"
|
|
||||||
maxlength="20"
|
|
||||||
auto-complete="off"
|
|
||||||
></el-input>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item label="密码">
|
|
||||||
<el-input
|
|
||||||
type="password"
|
|
||||||
placeholder="请输入密码"
|
|
||||||
v-model="form.password"
|
|
||||||
maxlength="20"
|
|
||||||
auto-complete="off"
|
|
||||||
></el-input>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item label="验证码" class="captcha">
|
|
||||||
<el-input
|
|
||||||
placeholder="请输入图片验证码"
|
|
||||||
maxlength="4"
|
|
||||||
v-model="form.verifyCode"
|
|
||||||
auto-complete="off"
|
|
||||||
@keyup.enter.native="next"
|
|
||||||
></el-input>
|
|
||||||
|
|
||||||
<captcha
|
|
||||||
ref="captcha"
|
|
||||||
class="value"
|
|
||||||
v-model="form.captchaId"
|
|
||||||
@change="captchaChange"
|
|
||||||
></captcha>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
|
|
||||||
<el-button round size="mini" class="submit-btn" @click="next" :loading="saving"
|
|
||||||
>登录</el-button
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import Captcha from "./components/captcha";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
Captcha
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
form: {
|
|
||||||
username: "",
|
|
||||||
password: "",
|
|
||||||
captchaId: "",
|
|
||||||
verifyCode: ""
|
|
||||||
},
|
|
||||||
saving: false
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
captchaChange() {
|
|
||||||
this.form.verifyCode = "";
|
|
||||||
},
|
|
||||||
|
|
||||||
async next() {
|
|
||||||
const { username, password, verifyCode } = this.form;
|
|
||||||
|
|
||||||
if (!username) {
|
|
||||||
return this.$message.warning("用户名不能为空");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!password) {
|
|
||||||
return this.$message.warning("密码不能为空");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!verifyCode) {
|
|
||||||
return this.$message.warning("图片验证码不能为空");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.saving = true;
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 登录
|
|
||||||
await this.$store.dispatch("userLogin", this.form);
|
|
||||||
|
|
||||||
// 用户信息
|
|
||||||
await this.$store.dispatch("userInfo");
|
|
||||||
|
|
||||||
// 权限菜单
|
|
||||||
let [first] = await this.$store.dispatch("permMenu");
|
|
||||||
|
|
||||||
if (!first) {
|
|
||||||
this.$message.error("该账号没有权限");
|
|
||||||
} else {
|
|
||||||
this.$router.push("/");
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
this.$message.error(err);
|
|
||||||
this.$refs.captcha.refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.saving = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.page-login {
|
|
||||||
height: 100vh;
|
|
||||||
width: 100vw;
|
|
||||||
position: relative;
|
|
||||||
background-color: #2f3447;
|
|
||||||
.box {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
height: 500px;
|
|
||||||
width: 500px;
|
|
||||||
position: absolute;
|
|
||||||
left: calc(50% - 250px);
|
|
||||||
top: calc(50% - 250px);
|
|
||||||
.logo {
|
|
||||||
height: 50px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
.desc {
|
|
||||||
color: #ccc;
|
|
||||||
font-size: 12px;
|
|
||||||
margin-bottom: 60px;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
}
|
|
||||||
/deep/.el-form {
|
|
||||||
width: 300px;
|
|
||||||
border-radius: 3px;
|
|
||||||
.el-form-item {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
&__label {
|
|
||||||
color: #ccc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.el-input {
|
|
||||||
.el-input__inner {
|
|
||||||
border: 0;
|
|
||||||
border-bottom: 0.5px solid #999;
|
|
||||||
border-radius: 0;
|
|
||||||
padding: 0 5px;
|
|
||||||
background-color: transparent;
|
|
||||||
color: #ccc;
|
|
||||||
transition: border-color 0.3s;
|
|
||||||
position: relative;
|
|
||||||
&:focus {
|
|
||||||
border-color: #fff;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
&:-webkit-autofill {
|
|
||||||
-webkit-text-fill-color: #fff !important;
|
|
||||||
-webkit-box-shadow: 0 0 0px 1000px transparent inset !important;
|
|
||||||
transition: background-color 50000s ease-in-out 0s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.captcha {
|
|
||||||
position: relative;
|
|
||||||
.value {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 1px;
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.submit-btn {
|
|
||||||
margin-top: 40px;
|
|
||||||
border-radius: 30px;
|
|
||||||
padding: 10px 40px;
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,80 +0,0 @@
|
|||||||
import { BaseService, Service } from "cl-admin";
|
|
||||||
|
|
||||||
@Service("base/comm")
|
|
||||||
class Common extends BaseService {
|
|
||||||
/**
|
|
||||||
* 文件上传模式
|
|
||||||
*/
|
|
||||||
uploadMode() {
|
|
||||||
return this.request({
|
|
||||||
url: "/uploadMode"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 文件上传,如果模式是 cloud,返回对应参数
|
|
||||||
*
|
|
||||||
* @returns
|
|
||||||
* @memberof CommonService
|
|
||||||
*/
|
|
||||||
upload(params) {
|
|
||||||
return this.request({
|
|
||||||
url: "/upload",
|
|
||||||
method: "POST",
|
|
||||||
params
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户退出
|
|
||||||
*/
|
|
||||||
userLogout() {
|
|
||||||
return this.request({
|
|
||||||
url: "/logout",
|
|
||||||
method: "POST"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户信息
|
|
||||||
*
|
|
||||||
* @returns
|
|
||||||
* @memberof CommonService
|
|
||||||
*/
|
|
||||||
userInfo() {
|
|
||||||
return this.request({
|
|
||||||
url: "/person"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户信息修改
|
|
||||||
*
|
|
||||||
* @param {*} params
|
|
||||||
* @returns
|
|
||||||
* @memberof CommonService
|
|
||||||
*/
|
|
||||||
userUpdate(params) {
|
|
||||||
return this.request({
|
|
||||||
url: "/personUpdate",
|
|
||||||
method: "POST",
|
|
||||||
data: {
|
|
||||||
...params
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 权限信息
|
|
||||||
*
|
|
||||||
* @returns
|
|
||||||
* @memberof CommonService
|
|
||||||
*/
|
|
||||||
permMenu() {
|
|
||||||
return this.request({
|
|
||||||
url: "/permmenu"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Common;
|
|
||||||
@ -1,25 +0,0 @@
|
|||||||
import Common from "./common";
|
|
||||||
import Open from "./open";
|
|
||||||
import SysUser from "./system/user";
|
|
||||||
import SysMenu from "./system/menu";
|
|
||||||
import SysRole from "./system/role";
|
|
||||||
import SysDept from "./system/dept";
|
|
||||||
import SysParam from "./system/param";
|
|
||||||
import SysLog from "./system/log";
|
|
||||||
import PluginInfo from "./plugin/info";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
common: new Common(),
|
|
||||||
open: new Open(),
|
|
||||||
system: {
|
|
||||||
user: new SysUser(),
|
|
||||||
menu: new SysMenu(),
|
|
||||||
role: new SysRole(),
|
|
||||||
dept: new SysDept(),
|
|
||||||
param: new SysParam(),
|
|
||||||
log: new SysLog()
|
|
||||||
},
|
|
||||||
plugin: {
|
|
||||||
info: new PluginInfo()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@ -1,56 +0,0 @@
|
|||||||
import { BaseService, Service } from "cl-admin";
|
|
||||||
|
|
||||||
@Service("base/open")
|
|
||||||
class Open extends BaseService {
|
|
||||||
/**
|
|
||||||
* 用户登录
|
|
||||||
*
|
|
||||||
* @param {*} { username, password, captchaId, verifyCode }
|
|
||||||
* @returns
|
|
||||||
* @memberof CommonService
|
|
||||||
*/
|
|
||||||
userLogin({ username, password, captchaId, verifyCode }) {
|
|
||||||
return this.request({
|
|
||||||
url: "/login",
|
|
||||||
method: "POST",
|
|
||||||
data: {
|
|
||||||
username,
|
|
||||||
password,
|
|
||||||
captchaId,
|
|
||||||
verifyCode
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 图片验证码 svg
|
|
||||||
*
|
|
||||||
* @param {*} { height, width }
|
|
||||||
* @returns
|
|
||||||
* @memberof CommonService
|
|
||||||
*/
|
|
||||||
captcha({ height, width }) {
|
|
||||||
return this.request({
|
|
||||||
url: "/captcha",
|
|
||||||
params: {
|
|
||||||
height,
|
|
||||||
width
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 刷新 token
|
|
||||||
* @param {string} token
|
|
||||||
*/
|
|
||||||
refreshToken(token) {
|
|
||||||
return this.request({
|
|
||||||
url: "/refreshToken",
|
|
||||||
params: {
|
|
||||||
refreshToken: token
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Open;
|
|
||||||
@ -1,32 +0,0 @@
|
|||||||
import { BaseService, Service, Permission } from "cl-admin";
|
|
||||||
|
|
||||||
@Service("base/plugin/info")
|
|
||||||
class PluginInfo extends BaseService {
|
|
||||||
@Permission("config")
|
|
||||||
config(data) {
|
|
||||||
return this.request({
|
|
||||||
url: "/config",
|
|
||||||
method: "POST",
|
|
||||||
data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Permission("getConfig")
|
|
||||||
getConfig(params) {
|
|
||||||
return this.request({
|
|
||||||
url: "/getConfig",
|
|
||||||
params
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Permission("enable")
|
|
||||||
enable(data) {
|
|
||||||
return this.request({
|
|
||||||
url: "/enable",
|
|
||||||
method: "POST",
|
|
||||||
data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default PluginInfo;
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
import { BaseService, Service, Permission } from "cl-admin";
|
|
||||||
|
|
||||||
@Service("base/sys/department")
|
|
||||||
class SysDepartment extends BaseService {
|
|
||||||
@Permission("order")
|
|
||||||
order(data) {
|
|
||||||
return this.request({
|
|
||||||
url: "/order",
|
|
||||||
method: "POST",
|
|
||||||
data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default SysDepartment;
|
|
||||||
@ -1,32 +0,0 @@
|
|||||||
import { BaseService, Service, Permission } from "cl-admin";
|
|
||||||
|
|
||||||
@Service("base/sys/log")
|
|
||||||
class SysLog extends BaseService {
|
|
||||||
@Permission("clear")
|
|
||||||
clear() {
|
|
||||||
return this.request({
|
|
||||||
url: "/clear",
|
|
||||||
method: "POST"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Permission("getKeep")
|
|
||||||
getKeep() {
|
|
||||||
return this.request({
|
|
||||||
url: "/getKeep"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Permission("setKeep")
|
|
||||||
setKeep(value) {
|
|
||||||
return this.request({
|
|
||||||
url: "/setKeep",
|
|
||||||
method: "POST",
|
|
||||||
data: {
|
|
||||||
value
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default SysLog;
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
import { BaseService, Service } from "cl-admin";
|
|
||||||
|
|
||||||
@Service("base/sys/menu")
|
|
||||||
class SysMenu extends BaseService {}
|
|
||||||
|
|
||||||
export default SysMenu;
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
import { BaseService, Service } from "cl-admin";
|
|
||||||
|
|
||||||
@Service("base/sys/param")
|
|
||||||
class SysParam extends BaseService {}
|
|
||||||
|
|
||||||
export default SysParam;
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
import { BaseService, Service } from "cl-admin";
|
|
||||||
|
|
||||||
@Service("base/sys/role")
|
|
||||||
class SysRole extends BaseService {}
|
|
||||||
|
|
||||||
export default SysRole;
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
import { BaseService, Service, Permission } from "cl-admin";
|
|
||||||
|
|
||||||
@Service("base/sys/user")
|
|
||||||
class SysUser extends BaseService {
|
|
||||||
@Permission("move")
|
|
||||||
move(data) {
|
|
||||||
return this.request({
|
|
||||||
url: "/move",
|
|
||||||
method: "POST",
|
|
||||||
data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default SysUser;
|
|
||||||
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