xuhuajiao
10 months ago
13 changed files with 1000 additions and 889 deletions
-
15.env.development
-
230package.json
-
32src/views/archivesManage/archivesList/index.vue
-
2src/views/archivesManage/archivesList/mixins/archives.js
-
106src/views/archivesManage/fileImport/dataImport/index.vue
-
23src/views/archivesManage/fileImport/importLog/index.vue
-
92src/views/archivesManage/fileImport/module/detail.vue
-
22src/views/archivesManage/outInStorage/inStorage/index.vue
-
1src/views/archivesManage/outInStorage/outInHistory/index.vue
-
2src/views/system/logManage/loginLog/index.vue
-
530src/views/system/menu/index.vue
-
762src/views/system/role/index.vue
-
72src/views/system/user/index.vue
@ -1,115 +1,115 @@ |
|||
{ |
|||
"name": "yxk-storeroom-system", |
|||
"version": "1.0.0", |
|||
"description": "智能库房综合管理系统", |
|||
"author": "刘力", |
|||
"license": "", |
|||
"scripts": { |
|||
"dev": "vue-cli-service serve", |
|||
"build:prod": "vue-cli-service build", |
|||
"build:stage": "vue-cli-service build --mode staging", |
|||
"preview": "node build/index.js --preview", |
|||
"lint": "eslint --ext .js,.vue src", |
|||
"test:unit": "jest --clearCache && vue-cli-service test:unit", |
|||
"svgo": "svgo -f src/assets/icons/svg --config=src/assets/icons/svgo.yml", |
|||
"new": "plop" |
|||
}, |
|||
"husky": { |
|||
"hooks": { |
|||
"pre-commit": "lint-staged" |
|||
} |
|||
}, |
|||
"lint-staged": { |
|||
"src/**/*.{js,vue}": [ |
|||
"eslint --fix", |
|||
"git add" |
|||
] |
|||
}, |
|||
"dependencies": { |
|||
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", |
|||
"@babel/plugin-proposal-optional-chaining": "^7.18.9", |
|||
"@riophae/vue-treeselect": "^0.4.0", |
|||
"axios": "^0.21.1", |
|||
"clipboard": "2.0.4", |
|||
"codemirror": "^5.49.2", |
|||
"connect": "3.6.6", |
|||
"core-js": "^2.6.12", |
|||
"echarts": "^4.2.1", |
|||
"echarts-gl": "^1.1.1", |
|||
"echarts-wordcloud": "^1.1.3", |
|||
"element-ui": "^2.15.6", |
|||
"file-saver": "1.3.8", |
|||
"fuse.js": "3.4.4", |
|||
"highlight.js": "^11.5.1", |
|||
"html2canvas": "^1.4.1", |
|||
"js-beautify": "^1.10.2", |
|||
"js-cookie": "2.2.0", |
|||
"jsencrypt": "^3.0.0-rc.1", |
|||
"jszip": "^3.7.1", |
|||
"mavon-editor": "^2.9.1", |
|||
"normalize.css": "7.0.0", |
|||
"nprogress": "0.2.0", |
|||
"path-to-regexp": "2.4.0", |
|||
"print-js": "^1.6.0", |
|||
"qs": "^6.10.1", |
|||
"screenfull": "4.2.0", |
|||
"sortablejs": "1.8.4", |
|||
"vkbeautify": "^0.99.3", |
|||
"vue": "^2.6.14", |
|||
"vue-count-to": "^1.0.13", |
|||
"vue-cropper": "0.4.9", |
|||
"vue-echarts": "^5.0.0-beta.0", |
|||
"vue-highlightjs": "^1.3.3", |
|||
"vue-image-crop-upload": "^2.5.0", |
|||
"vue-router": "3.0.2", |
|||
"vue-splitpane": "1.0.4", |
|||
"vuedraggable": "2.20.0", |
|||
"vuex": "3.1.0", |
|||
"wangeditor": "^4.7.11", |
|||
"x2js": "^3.4.3", |
|||
"xlsx": "^0.17.4" |
|||
}, |
|||
"devDependencies": { |
|||
"@babel/parser": "^7.7.4", |
|||
"@babel/register": "7.0.0", |
|||
"@vue/babel-plugin-transform-vue-jsx": "^1.2.1", |
|||
"@vue/cli-plugin-babel": "3.5.3", |
|||
"@vue/cli-plugin-eslint": "^3.9.1", |
|||
"@vue/cli-plugin-unit-jest": "3.5.3", |
|||
"@vue/cli-service": "3.5.3", |
|||
"@vue/test-utils": "1.0.0-beta.29", |
|||
"autoprefixer": "^9.5.1", |
|||
"babel-core": "7.0.0-bridge.0", |
|||
"babel-eslint": "10.0.1", |
|||
"babel-jest": "23.6.0", |
|||
"babel-plugin-dynamic-import-node": "2.3.0", |
|||
"babel-plugin-transform-remove-console": "^6.9.4", |
|||
"chalk": "2.4.2", |
|||
"chokidar": "2.1.5", |
|||
"connect": "3.6.6", |
|||
"eslint": "5.15.3", |
|||
"eslint-plugin-vue": "5.2.2", |
|||
"html-webpack-plugin": "3.2.0", |
|||
"http-proxy-middleware": "^0.19.1", |
|||
"husky": "1.3.1", |
|||
"lint-staged": "8.1.5", |
|||
"plop": "2.3.0", |
|||
"sass": "1.32.13", |
|||
"sass-loader": "10.2.0", |
|||
"script-ext-html-webpack-plugin": "2.1.3", |
|||
"script-loader": "0.7.2", |
|||
"serve-static": "^1.13.2", |
|||
"svg-sprite-loader": "4.1.3", |
|||
"svgo": "1.2.0", |
|||
"tasksfile": "^5.1.1", |
|||
"vue-template-compiler": "2.6.14" |
|||
}, |
|||
"engines": { |
|||
"node": ">=8.9", |
|||
"npm": ">= 3.0.0" |
|||
}, |
|||
"browserslist": [ |
|||
"> 1%", |
|||
"last 2 versions" |
|||
] |
|||
} |
|||
{ |
|||
"name": "yxk-storeroom-system", |
|||
"version": "1.0.0", |
|||
"description": "智能库房综合管理系统", |
|||
"author": "刘力", |
|||
"license": "", |
|||
"scripts": { |
|||
"dev": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve", |
|||
"build:prod": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service build", |
|||
"build:stage": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service build --mode staging", |
|||
"preview": "node build/index.js --preview", |
|||
"lint": "eslint --ext .js,.vue src", |
|||
"test:unit": "jest --clearCache && vue-cli-service test:unit", |
|||
"svgo": "svgo -f src/assets/icons/svg --config=src/assets/icons/svgo.yml", |
|||
"new": "plop" |
|||
}, |
|||
"husky": { |
|||
"hooks": { |
|||
"pre-commit": "lint-staged" |
|||
} |
|||
}, |
|||
"lint-staged": { |
|||
"src/**/*.{js,vue}": [ |
|||
"eslint --fix", |
|||
"git add" |
|||
] |
|||
}, |
|||
"dependencies": { |
|||
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", |
|||
"@babel/plugin-proposal-optional-chaining": "^7.18.9", |
|||
"@riophae/vue-treeselect": "^0.4.0", |
|||
"axios": "^0.21.1", |
|||
"clipboard": "2.0.4", |
|||
"codemirror": "^5.49.2", |
|||
"connect": "3.6.6", |
|||
"core-js": "^2.6.12", |
|||
"echarts": "^4.2.1", |
|||
"echarts-gl": "^1.1.1", |
|||
"echarts-wordcloud": "^1.1.3", |
|||
"element-ui": "^2.15.6", |
|||
"file-saver": "1.3.8", |
|||
"fuse.js": "3.4.4", |
|||
"highlight.js": "^11.5.1", |
|||
"html2canvas": "^1.4.1", |
|||
"js-beautify": "^1.10.2", |
|||
"js-cookie": "2.2.0", |
|||
"jsencrypt": "^3.0.0-rc.1", |
|||
"jszip": "^3.7.1", |
|||
"mavon-editor": "^2.9.1", |
|||
"normalize.css": "7.0.0", |
|||
"nprogress": "0.2.0", |
|||
"path-to-regexp": "2.4.0", |
|||
"print-js": "^1.6.0", |
|||
"qs": "^6.10.1", |
|||
"screenfull": "4.2.0", |
|||
"sortablejs": "1.8.4", |
|||
"vkbeautify": "^0.99.3", |
|||
"vue": "^2.6.14", |
|||
"vue-count-to": "^1.0.13", |
|||
"vue-cropper": "0.4.9", |
|||
"vue-echarts": "^5.0.0-beta.0", |
|||
"vue-highlightjs": "^1.3.3", |
|||
"vue-image-crop-upload": "^2.5.0", |
|||
"vue-router": "3.0.2", |
|||
"vue-splitpane": "1.0.4", |
|||
"vuedraggable": "2.20.0", |
|||
"vuex": "3.1.0", |
|||
"wangeditor": "^4.7.11", |
|||
"x2js": "^3.4.3", |
|||
"xlsx": "^0.17.4" |
|||
}, |
|||
"devDependencies": { |
|||
"@babel/parser": "^7.7.4", |
|||
"@babel/register": "7.0.0", |
|||
"@vue/babel-plugin-transform-vue-jsx": "^1.2.1", |
|||
"@vue/cli-plugin-babel": "3.5.3", |
|||
"@vue/cli-plugin-eslint": "^3.9.1", |
|||
"@vue/cli-plugin-unit-jest": "3.5.3", |
|||
"@vue/cli-service": "3.5.3", |
|||
"@vue/test-utils": "1.0.0-beta.29", |
|||
"autoprefixer": "^9.5.1", |
|||
"babel-core": "7.0.0-bridge.0", |
|||
"babel-eslint": "10.0.1", |
|||
"babel-jest": "23.6.0", |
|||
"babel-plugin-dynamic-import-node": "2.3.0", |
|||
"babel-plugin-transform-remove-console": "^6.9.4", |
|||
"chalk": "2.4.2", |
|||
"chokidar": "2.1.5", |
|||
"connect": "3.6.6", |
|||
"eslint": "5.15.3", |
|||
"eslint-plugin-vue": "5.2.2", |
|||
"html-webpack-plugin": "3.2.0", |
|||
"http-proxy-middleware": "^0.19.1", |
|||
"husky": "1.3.1", |
|||
"lint-staged": "8.1.5", |
|||
"plop": "2.3.0", |
|||
"sass": "1.32.13", |
|||
"sass-loader": "10.2.0", |
|||
"script-ext-html-webpack-plugin": "2.1.3", |
|||
"script-loader": "0.7.2", |
|||
"serve-static": "^1.13.2", |
|||
"svg-sprite-loader": "4.1.3", |
|||
"svgo": "1.2.0", |
|||
"tasksfile": "^5.1.1", |
|||
"vue-template-compiler": "2.6.14" |
|||
}, |
|||
"engines": { |
|||
"node": ">=8.9", |
|||
"npm": ">= 3.0.0" |
|||
}, |
|||
"browserslist": [ |
|||
"> 1%", |
|||
"last 2 versions" |
|||
] |
|||
} |
@ -1,265 +1,265 @@ |
|||
<template> |
|||
<div> |
|||
|
|||
<!--工具栏--> |
|||
<div class="head-container"> |
|||
<div v-if="crud.props.searchToggle" class="head-search"> |
|||
<!-- 搜索 --> |
|||
<el-input v-model="query.blurry" clearable size="small" placeholder="模糊搜索" prefix-icon="el-icon-search" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" /> |
|||
<date-range-picker v-model="query.createTime" class="date-item" /> |
|||
<rrOperation /> |
|||
</div> |
|||
<crudOperation :permission="permission" /> |
|||
</div> |
|||
<!--表单渲染--> |
|||
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title"> |
|||
<span class="dialog-right-top" /> |
|||
<span class="dialog-left-bottom" /> |
|||
<div class="setting-dialog"> |
|||
<el-form ref="form" :inline="true" :model="form" :rules="rules" size="small" label-width="80px"> |
|||
<el-form-item label="菜单类型" prop="type"> |
|||
<el-radio-group v-model="form.type" size="mini" style="width: 178px"> |
|||
<el-radio-button label="0">目录</el-radio-button> |
|||
<el-radio-button label="1">菜单</el-radio-button> |
|||
<el-radio-button label="2">按钮</el-radio-button> |
|||
</el-radio-group> |
|||
</el-form-item> |
|||
<el-form-item v-show="form.type.toString() !== '2'" label="菜单图标" prop="icon"> |
|||
<el-popover |
|||
placement="bottom-start" |
|||
width="450" |
|||
trigger="click" |
|||
@show="$refs['iconSelect'].reset()" |
|||
> |
|||
<IconSelect ref="iconSelect" @selected="selected" /> |
|||
<el-input slot="reference" v-model="form.icon" style="width: 450px;" placeholder="点击选择图标" readonly> |
|||
<svg-icon v-if="form.icon" slot="prefix" :icon-class="form.icon" class="el-input__icon" style="height: 30px;width: 16px;" /> |
|||
<i v-else slot="prefix" class="el-icon-search el-input__icon" /> |
|||
</el-input> |
|||
</el-popover> |
|||
</el-form-item> |
|||
<el-form-item v-show="form.type.toString() !== '2'" label="外链菜单" prop="iFrame"> |
|||
<el-radio-group v-model="form.iFrame" size="mini"> |
|||
<el-radio-button label="true">是</el-radio-button> |
|||
<el-radio-button label="false">否</el-radio-button> |
|||
</el-radio-group> |
|||
</el-form-item> |
|||
<el-form-item v-show="form.type.toString() === '1'" label="菜单缓存" prop="cache"> |
|||
<el-radio-group v-model="form.cache" size="mini"> |
|||
<el-radio-button label="true">是</el-radio-button> |
|||
<el-radio-button label="false">否</el-radio-button> |
|||
</el-radio-group> |
|||
</el-form-item> |
|||
<el-form-item v-show="form.type.toString() !== '2'" label="菜单可见" prop="hidden"> |
|||
<el-radio-group v-model="form.hidden" size="mini"> |
|||
<el-radio-button label="false">是</el-radio-button> |
|||
<el-radio-button label="true">否</el-radio-button> |
|||
</el-radio-group> |
|||
</el-form-item> |
|||
<el-form-item v-if="form.type.toString() !== '2'" label="菜单标题" prop="title"> |
|||
<el-input v-model="form.title" :style=" form.type.toString() === '0' ? 'width: 450px' : 'width: 178px'" placeholder="菜单标题" /> |
|||
</el-form-item> |
|||
<el-form-item v-if="form.type.toString() === '2'" label="按钮名称" prop="title"> |
|||
<el-input v-model="form.title" placeholder="按钮名称" style="width: 178px;" /> |
|||
</el-form-item> |
|||
<el-form-item v-show="form.type.toString() !== '0'" label="权限标识" prop="permission"> |
|||
<el-input v-model="form.permission" :disabled="form.iFrame.toString() === 'true'" placeholder="权限标识" style="width: 178px;" /> |
|||
</el-form-item> |
|||
<el-form-item v-if="form.type.toString() !== '2'" label="路由地址" prop="path"> |
|||
<el-input v-model="form.path" placeholder="路由地址" style="width: 178px;" /> |
|||
</el-form-item> |
|||
<el-form-item label="菜单排序" prop="menuSort"> |
|||
<el-input-number v-model.number="form.menuSort" :min="0" :max="999" controls-position="right" style="width: 178px;" /> |
|||
</el-form-item> |
|||
<el-form-item v-show="form.iFrame.toString() !== 'true' && form.type.toString() === '1'" label="组件名称" prop="componentName"> |
|||
<el-input v-model="form.componentName" style="width: 178px;" placeholder="匹配组件内Name字段" /> |
|||
</el-form-item> |
|||
<el-form-item v-show="form.iFrame.toString() !== 'true' && form.type.toString() === '1'" label="组件路径" prop="component"> |
|||
<el-input v-model="form.component" style="width: 178px;" placeholder="组件路径" /> |
|||
</el-form-item> |
|||
<el-form-item label="上级类目" prop="pid"> |
|||
<treeselect |
|||
v-model="form.pid" |
|||
:options="menus" |
|||
:load-options="loadMenus" |
|||
style="width: 450px;" |
|||
placeholder="选择上级类目" |
|||
/> |
|||
</el-form-item> |
|||
</el-form> |
|||
<div slot="footer" class="dialog-footer"> |
|||
<el-button type="text" @click="crud.cancelCU">取消</el-button> |
|||
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button> |
|||
</div> |
|||
</div> |
|||
</el-dialog> |
|||
<!--表格渲染--> |
|||
<div class="app-container container-wrap" style="margin-top:0;min-height: calc(100vh - 292px);"> |
|||
<span class="right-top-line" /> |
|||
<span class="left-bottom-line" /> |
|||
<el-table |
|||
ref="table" |
|||
v-loading="crud.loading" |
|||
lazy |
|||
:load="getMenus" |
|||
:data="crud.data" |
|||
:tree-props="{children: 'children', hasChildren: 'hasChildren'}" |
|||
row-key="id" |
|||
@select="crud.selectChange" |
|||
@select-all="crud.selectAllChange" |
|||
@selection-change="crud.selectionChangeHandler" |
|||
> |
|||
<el-table-column type="selection" align="center" width="55" /> |
|||
<el-table-column :show-overflow-tooltip="true" label="菜单标题" width="125px" prop="title" /> |
|||
<el-table-column prop="icon" label="图标" align="center" width="60px"> |
|||
<template slot-scope="scope"> |
|||
<svg-icon :icon-class="scope.row.icon ? scope.row.icon : ''" /> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column prop="menuSort" align="center" label="排序"> |
|||
<template slot-scope="scope"> |
|||
{{ scope.row.menuSort }} |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column :show-overflow-tooltip="true" prop="permission" label="权限标识" /> |
|||
<el-table-column :show-overflow-tooltip="true" prop="component" label="组件路径" /> |
|||
<el-table-column prop="iFrame" label="外链" width="75px"> |
|||
<template slot-scope="scope"> |
|||
<span v-if="scope.row.iFrame">是</span> |
|||
<span v-else>否</span> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column prop="cache" label="缓存" width="75px"> |
|||
<template slot-scope="scope"> |
|||
<span v-if="scope.row.cache">是</span> |
|||
<span v-else>否</span> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column prop="hidden" label="可见" width="75px"> |
|||
<template slot-scope="scope"> |
|||
<span v-if="scope.row.hidden">否</span> |
|||
<span v-else>是</span> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column prop="createTime" label="创建日期" width="150px"> |
|||
<template slot-scope="scope"> |
|||
<div>{{ scope.row.createTime | parseTime }}</div> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column v-if="checkPer(['admin','menu:edit','menu:del'])" label="操作" width="130px" align="center" fixed="right"> |
|||
<template slot-scope="scope"> |
|||
<udOperation |
|||
:data="scope.row" |
|||
:permission="permission" |
|||
msg="确定删除吗,如果存在下级节点则一并删除,此操作不能撤销!" |
|||
/> |
|||
</template> |
|||
</el-table-column> |
|||
</el-table> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import crudMenu from '@/api/system/menu' |
|||
import IconSelect from '@/components/IconSelect' |
|||
import Treeselect from '@riophae/vue-treeselect' |
|||
import '@riophae/vue-treeselect/dist/vue-treeselect.css' |
|||
import { LOAD_CHILDREN_OPTIONS } from '@riophae/vue-treeselect' |
|||
import CRUD, { presenter, header, form, crud } from '@crud/crud' |
|||
import rrOperation from '@crud/RR.operation' |
|||
import crudOperation from '@crud/CRUD.operation' |
|||
import udOperation from '@crud/UD.operation' |
|||
import DateRangePicker from '@/components/DateRangePicker' |
|||
|
|||
// crud交由presenter持有 |
|||
const defaultForm = { id: null, title: null, menuSort: 999, path: null, component: null, componentName: null, iFrame: false, roles: [], pid: 0, icon: null, cache: false, hidden: false, type: 0, permission: null } |
|||
export default { |
|||
name: 'Menu', |
|||
components: { Treeselect, IconSelect, crudOperation, rrOperation, udOperation, DateRangePicker }, |
|||
cruds() { |
|||
return CRUD({ title: '菜单', url: 'api/menus', crudMethod: { ...crudMenu }}) |
|||
}, |
|||
mixins: [presenter(), header(), form(defaultForm), crud()], |
|||
data() { |
|||
return { |
|||
menus: [], |
|||
permission: { |
|||
add: ['admin', 'menu:add'], |
|||
edit: ['admin', 'menu:edit'], |
|||
del: ['admin', 'menu:del'] |
|||
}, |
|||
rules: { |
|||
title: [ |
|||
{ required: true, message: '请输入标题', trigger: 'blur' } |
|||
], |
|||
path: [ |
|||
{ required: true, message: '请输入地址', trigger: 'blur' } |
|||
] |
|||
} |
|||
} |
|||
}, |
|||
methods: { |
|||
// 新增与编辑前做的操作 |
|||
[CRUD.HOOK.afterToCU](crud, form) { |
|||
this.menus = [] |
|||
if (form.id != null) { |
|||
if (form.pid === null) { |
|||
form.pid = 0 |
|||
} |
|||
this.getSupDepts(form.id) |
|||
} else { |
|||
this.menus.push({ id: 0, label: '顶级类目', children: null }) |
|||
} |
|||
}, |
|||
getMenus(tree, treeNode, resolve) { |
|||
const params = { pid: tree.id } |
|||
setTimeout(() => { |
|||
crudMenu.getMenus(params).then(res => { |
|||
resolve(res.content) |
|||
}) |
|||
}, 100) |
|||
}, |
|||
getSupDepts(id) { |
|||
crudMenu.getMenuSuperior(id).then(res => { |
|||
const children = res.map(function(obj) { |
|||
if (!obj.leaf && !obj.children) { |
|||
obj.children = null |
|||
} |
|||
return obj |
|||
}) |
|||
this.menus = [{ id: 0, label: '顶级类目', children: children }] |
|||
}) |
|||
}, |
|||
loadMenus({ action, parentNode, callback }) { |
|||
if (action === LOAD_CHILDREN_OPTIONS) { |
|||
crudMenu.getMenusTree(parentNode.id).then(res => { |
|||
parentNode.children = res.map(function(obj) { |
|||
if (!obj.leaf) { |
|||
obj.children = null |
|||
} |
|||
return obj |
|||
}) |
|||
setTimeout(() => { |
|||
callback() |
|||
}, 100) |
|||
}) |
|||
} |
|||
}, |
|||
// 选中图标 |
|||
selected(name) { |
|||
this.form.icon = name |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style rel="stylesheet/scss" lang="scss" scoped> |
|||
::v-deep .el-input-number .el-input__inner { |
|||
text-align: left; |
|||
} |
|||
// ::v-deep .vue-treeselect__control,::v-deep .vue-treeselect__placeholder,::v-deep .vue-treeselect__single-value { |
|||
// height: 30px; |
|||
// line-height: 30px; |
|||
// } |
|||
</style> |
|||
<template> |
|||
<div> |
|||
|
|||
<!--工具栏--> |
|||
<div class="head-container"> |
|||
<div v-if="crud.props.searchToggle" class="head-search"> |
|||
<!-- 搜索 --> |
|||
<el-input v-model="query.blurry" clearable size="small" placeholder="模糊搜索" prefix-icon="el-icon-search" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" /> |
|||
<date-range-picker v-model="query.createTime" class="date-item" /> |
|||
<rrOperation /> |
|||
</div> |
|||
<crudOperation :permission="permission" /> |
|||
</div> |
|||
<!--表单渲染--> |
|||
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title"> |
|||
<span class="dialog-right-top" /> |
|||
<span class="dialog-left-bottom" /> |
|||
<div class="setting-dialog"> |
|||
<el-form ref="form" :inline="true" :model="form" :rules="rules" size="small" label-width="80px"> |
|||
<el-form-item label="菜单类型" prop="type"> |
|||
<el-radio-group v-model="form.type" size="mini" style="width: 178px"> |
|||
<el-radio-button label="0">目录</el-radio-button> |
|||
<el-radio-button label="1">菜单</el-radio-button> |
|||
<el-radio-button label="2">按钮</el-radio-button> |
|||
</el-radio-group> |
|||
</el-form-item> |
|||
<el-form-item v-show="form.type.toString() !== '2'" label="菜单图标" prop="icon"> |
|||
<el-popover |
|||
placement="bottom-start" |
|||
width="450" |
|||
trigger="click" |
|||
@show="$refs['iconSelect'].reset()" |
|||
> |
|||
<IconSelect ref="iconSelect" @selected="selected" /> |
|||
<el-input slot="reference" v-model="form.icon" style="width: 450px;" placeholder="点击选择图标" readonly> |
|||
<svg-icon v-if="form.icon" slot="prefix" :icon-class="form.icon" class="el-input__icon" style="height: 30px;width: 16px;" /> |
|||
<i v-else slot="prefix" class="el-icon-search el-input__icon" /> |
|||
</el-input> |
|||
</el-popover> |
|||
</el-form-item> |
|||
<el-form-item v-show="form.type.toString() !== '2'" label="外链菜单" prop="iFrame"> |
|||
<el-radio-group v-model="form.iFrame" size="mini"> |
|||
<el-radio-button label="true">是</el-radio-button> |
|||
<el-radio-button label="false">否</el-radio-button> |
|||
</el-radio-group> |
|||
</el-form-item> |
|||
<el-form-item v-show="form.type.toString() === '1'" label="菜单缓存" prop="cache"> |
|||
<el-radio-group v-model="form.cache" size="mini"> |
|||
<el-radio-button label="true">是</el-radio-button> |
|||
<el-radio-button label="false">否</el-radio-button> |
|||
</el-radio-group> |
|||
</el-form-item> |
|||
<el-form-item v-show="form.type.toString() !== '2'" label="菜单可见" prop="hidden"> |
|||
<el-radio-group v-model="form.hidden" size="mini"> |
|||
<el-radio-button label="false">是</el-radio-button> |
|||
<el-radio-button label="true">否</el-radio-button> |
|||
</el-radio-group> |
|||
</el-form-item> |
|||
<el-form-item v-if="form.type.toString() !== '2'" label="菜单标题" prop="title"> |
|||
<el-input v-model="form.title" :style=" form.type.toString() === '0' ? 'width: 450px' : 'width: 178px'" placeholder="菜单标题" /> |
|||
</el-form-item> |
|||
<el-form-item v-if="form.type.toString() === '2'" label="按钮名称" prop="title"> |
|||
<el-input v-model="form.title" placeholder="按钮名称" style="width: 178px;" /> |
|||
</el-form-item> |
|||
<el-form-item v-show="form.type.toString() !== '0'" label="权限标识" prop="permission"> |
|||
<el-input v-model="form.permission" :disabled="form.iFrame.toString() === 'true'" placeholder="权限标识" style="width: 178px;" /> |
|||
</el-form-item> |
|||
<el-form-item v-if="form.type.toString() !== '2'" label="路由地址" prop="path"> |
|||
<el-input v-model="form.path" placeholder="路由地址" style="width: 178px;" /> |
|||
</el-form-item> |
|||
<el-form-item label="菜单排序" prop="menuSort"> |
|||
<el-input-number v-model.number="form.menuSort" :min="0" :max="999" controls-position="right" style="width: 178px;" /> |
|||
</el-form-item> |
|||
<el-form-item v-show="form.iFrame.toString() !== 'true' && form.type.toString() === '1'" label="组件名称" prop="componentName"> |
|||
<el-input v-model="form.componentName" style="width: 178px;" placeholder="匹配组件内Name字段" /> |
|||
</el-form-item> |
|||
<el-form-item v-show="form.iFrame.toString() !== 'true' && form.type.toString() === '1'" label="组件路径" prop="component"> |
|||
<el-input v-model="form.component" style="width: 178px;" placeholder="组件路径" /> |
|||
</el-form-item> |
|||
<el-form-item label="上级类目" prop="pid"> |
|||
<treeselect |
|||
v-model="form.pid" |
|||
:options="menus" |
|||
:load-options="loadMenus" |
|||
style="width: 450px;" |
|||
placeholder="选择上级类目" |
|||
/> |
|||
</el-form-item> |
|||
</el-form> |
|||
<div slot="footer" class="dialog-footer"> |
|||
<el-button type="text" @click="crud.cancelCU">取消</el-button> |
|||
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button> |
|||
</div> |
|||
</div> |
|||
</el-dialog> |
|||
<!--表格渲染--> |
|||
<div class="app-container container-wrap" style="margin-top:0;min-height: calc(100vh - 292px);"> |
|||
<span class="right-top-line" /> |
|||
<span class="left-bottom-line" /> |
|||
<el-table |
|||
ref="table" |
|||
v-loading="crud.loading" |
|||
lazy |
|||
:load="getMenus" |
|||
:data="crud.data" |
|||
:tree-props="{children: 'children', hasChildren: 'hasChildren'}" |
|||
row-key="id" |
|||
@select="crud.selectChange" |
|||
@select-all="crud.selectAllChange" |
|||
@selection-change="crud.selectionChangeHandler" |
|||
> |
|||
<el-table-column type="selection" align="center" width="55" /> |
|||
<el-table-column :show-overflow-tooltip="true" label="菜单标题" min-width="100px" prop="title" /> |
|||
<el-table-column prop="icon" label="图标" align="center" width="60px"> |
|||
<template slot-scope="scope"> |
|||
<svg-icon :icon-class="scope.row.icon ? scope.row.icon : ''" /> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column prop="menuSort" align="center" label="排序" width="60px"> |
|||
<template slot-scope="scope"> |
|||
{{ scope.row.menuSort }} |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column :show-overflow-tooltip="true" prop="permission" label="权限标识" /> |
|||
<el-table-column :show-overflow-tooltip="true" prop="component" label="组件路径" /> |
|||
<el-table-column prop="iFrame" label="外链" width="75px"> |
|||
<template slot-scope="scope"> |
|||
<span v-if="scope.row.iFrame">是</span> |
|||
<span v-else>否</span> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column prop="cache" label="缓存" width="75px"> |
|||
<template slot-scope="scope"> |
|||
<span v-if="scope.row.cache">是</span> |
|||
<span v-else>否</span> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column prop="hidden" label="可见" width="75px"> |
|||
<template slot-scope="scope"> |
|||
<span v-if="scope.row.hidden">否</span> |
|||
<span v-else>是</span> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column prop="createTime" label="创建日期" width="150px"> |
|||
<template slot-scope="scope"> |
|||
<div>{{ scope.row.createTime | parseTime }}</div> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column v-if="checkPer(['admin','menu:edit','menu:del'])" label="操作" width="130px" align="center" fixed="right"> |
|||
<template slot-scope="scope"> |
|||
<udOperation |
|||
:data="scope.row" |
|||
:permission="permission" |
|||
msg="确定删除吗,如果存在下级节点则一并删除,此操作不能撤销!" |
|||
/> |
|||
</template> |
|||
</el-table-column> |
|||
</el-table> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import crudMenu from '@/api/system/menu' |
|||
import IconSelect from '@/components/IconSelect' |
|||
import Treeselect from '@riophae/vue-treeselect' |
|||
import '@riophae/vue-treeselect/dist/vue-treeselect.css' |
|||
import { LOAD_CHILDREN_OPTIONS } from '@riophae/vue-treeselect' |
|||
import CRUD, { presenter, header, form, crud } from '@crud/crud' |
|||
import rrOperation from '@crud/RR.operation' |
|||
import crudOperation from '@crud/CRUD.operation' |
|||
import udOperation from '@crud/UD.operation' |
|||
import DateRangePicker from '@/components/DateRangePicker' |
|||
|
|||
// crud交由presenter持有 |
|||
const defaultForm = { id: null, title: null, menuSort: 999, path: null, component: null, componentName: null, iFrame: false, roles: [], pid: 0, icon: null, cache: false, hidden: false, type: 0, permission: null } |
|||
export default { |
|||
name: 'Menu', |
|||
components: { Treeselect, IconSelect, crudOperation, rrOperation, udOperation, DateRangePicker }, |
|||
cruds() { |
|||
return CRUD({ title: '菜单', url: 'api/menus', crudMethod: { ...crudMenu }}) |
|||
}, |
|||
mixins: [presenter(), header(), form(defaultForm), crud()], |
|||
data() { |
|||
return { |
|||
menus: [], |
|||
permission: { |
|||
add: ['admin', 'menu:add'], |
|||
edit: ['admin', 'menu:edit'], |
|||
del: ['admin', 'menu:del'] |
|||
}, |
|||
rules: { |
|||
title: [ |
|||
{ required: true, message: '请输入标题', trigger: 'blur' } |
|||
], |
|||
path: [ |
|||
{ required: true, message: '请输入地址', trigger: 'blur' } |
|||
] |
|||
} |
|||
} |
|||
}, |
|||
methods: { |
|||
// 新增与编辑前做的操作 |
|||
[CRUD.HOOK.afterToCU](crud, form) { |
|||
this.menus = [] |
|||
if (form.id != null) { |
|||
if (form.pid === null) { |
|||
form.pid = 0 |
|||
} |
|||
this.getSupDepts(form.id) |
|||
} else { |
|||
this.menus.push({ id: 0, label: '顶级类目', children: null }) |
|||
} |
|||
}, |
|||
getMenus(tree, treeNode, resolve) { |
|||
const params = { pid: tree.id } |
|||
setTimeout(() => { |
|||
crudMenu.getMenus(params).then(res => { |
|||
resolve(res.content) |
|||
}) |
|||
}, 100) |
|||
}, |
|||
getSupDepts(id) { |
|||
crudMenu.getMenuSuperior(id).then(res => { |
|||
const children = res.map(function(obj) { |
|||
if (!obj.leaf && !obj.children) { |
|||
obj.children = null |
|||
} |
|||
return obj |
|||
}) |
|||
this.menus = [{ id: 0, label: '顶级类目', children: children }] |
|||
}) |
|||
}, |
|||
loadMenus({ action, parentNode, callback }) { |
|||
if (action === LOAD_CHILDREN_OPTIONS) { |
|||
crudMenu.getMenusTree(parentNode.id).then(res => { |
|||
parentNode.children = res.map(function(obj) { |
|||
if (!obj.leaf) { |
|||
obj.children = null |
|||
} |
|||
return obj |
|||
}) |
|||
setTimeout(() => { |
|||
callback() |
|||
}, 100) |
|||
}) |
|||
} |
|||
}, |
|||
// 选中图标 |
|||
selected(name) { |
|||
this.form.icon = name |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style rel="stylesheet/scss" lang="scss" scoped> |
|||
::v-deep .el-input-number .el-input__inner { |
|||
text-align: left; |
|||
} |
|||
// ::v-deep .vue-treeselect__control,::v-deep .vue-treeselect__placeholder,::v-deep .vue-treeselect__single-value { |
|||
// height: 30px; |
|||
// line-height: 30px; |
|||
// } |
|||
</style> |
@ -1,381 +1,381 @@ |
|||
<template> |
|||
<div class="app-container"> |
|||
<!--工具栏--> |
|||
<div class="head-container"> |
|||
<div v-if="crud.props.searchToggle" class="head-search"> |
|||
<!-- 搜索 --> |
|||
<el-input v-model="query.blurry" size="small" clearable placeholder="输入名称或者描述搜索" prefix-icon="el-icon-search" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" /> |
|||
<date-range-picker v-model="query.createTime" class="date-item" /> |
|||
<rrOperation /> |
|||
</div> |
|||
<crudOperation :permission="permission" /> |
|||
</div> |
|||
<!-- 表单渲染 --> |
|||
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title"> |
|||
<span class="dialog-right-top" /> |
|||
<span class="dialog-left-bottom" /> |
|||
<div class="setting-dialog"> |
|||
<el-form ref="form" :inline="true" :model="form" :rules="rules" size="small" label-width="80px"> |
|||
<el-form-item label="角色名称" prop="name"> |
|||
<el-input v-model="form.name" style="width: 380px;" /> |
|||
</el-form-item> |
|||
<el-form-item label="角色级别" prop="level"> |
|||
<el-input-number v-model.number="form.level" :min="1" controls-position="right" style="width: 145px;" /> |
|||
</el-form-item> |
|||
<el-form-item label="数据范围" prop="dataScope"> |
|||
<el-select v-model="form.dataScope" style="width: 140px" placeholder="请选择数据范围" @change="changeScope"> |
|||
<el-option |
|||
v-for="item in dateScopes" |
|||
:key="item" |
|||
:label="item" |
|||
:value="item" |
|||
/> |
|||
</el-select> |
|||
</el-form-item> |
|||
<el-form-item v-if="form.dataScope === '自定义'" label="数据权限" prop="depts"> |
|||
<treeselect |
|||
v-model="deptDatas" |
|||
:load-options="loadDepts" |
|||
:options="depts" |
|||
multiple |
|||
style="width: 380px" |
|||
placeholder="请选择" |
|||
/> |
|||
</el-form-item> |
|||
<el-form-item label="描述信息" prop="description"> |
|||
<el-input v-model="form.description" style="width: 380px;" rows="5" type="textarea" /> |
|||
</el-form-item> |
|||
</el-form> |
|||
<div slot="footer" class="dialog-footer"> |
|||
<el-button type="text" @click="crud.cancelCU">取消</el-button> |
|||
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button> |
|||
</div> |
|||
</div> |
|||
</el-dialog> |
|||
<el-row :gutter="15"> |
|||
<!--角色管理--> |
|||
<el-col :xs="15" :sm="17" :md="18" :lg="19" :xl="19"> |
|||
<el-card class="box-card" shadow="never"> |
|||
<span class="right-top-line" /> |
|||
<span class="left-bottom-line" /> |
|||
<div slot="header" class="clearfix"> |
|||
<span class="role-span">角色列表</span> |
|||
</div> |
|||
<el-table ref="table" v-loading="crud.loading" highlight-current-row style="width: 100%;" height="calc(100vh - 400px)" :data="crud.data" @selection-change="crud.selectionChangeHandler" @current-change="handleCurrentChange"> |
|||
<el-table-column :selectable="checkboxT" type="selection" align="center" width="55" /> |
|||
<el-table-column prop="name" label="名称" /> |
|||
<el-table-column prop="dataScope" label="数据权限" /> |
|||
<el-table-column prop="level" label="角色级别" /> |
|||
<el-table-column :show-overflow-tooltip="true" prop="description" label="描述" /> |
|||
<el-table-column :show-overflow-tooltip="true" width="135px" prop="createTime" label="创建日期"> |
|||
<template slot-scope="scope"> |
|||
<div>{{ scope.row.createTime | parseTime }}</div> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column v-if="checkPer(['admin','roles:edit','roles:del'])" label="操作" width="130px" align="center" fixed="right"> |
|||
<template slot-scope="scope"> |
|||
<udOperation |
|||
v-if="scope.row.level >= level" |
|||
:data="scope.row" |
|||
:permission="permission" |
|||
/> |
|||
</template> |
|||
</el-table-column> |
|||
</el-table> |
|||
<!--分页组件--> |
|||
<pagination /> |
|||
</el-card> |
|||
</el-col> |
|||
<!-- 菜单授权 --> |
|||
<el-col :xs="9" :sm="7" :md="6" :lg="5" :xl="5"> |
|||
<el-card class="box-card" shadow="never" style="min-height:calc(100vh - 292px)"> |
|||
<span class="right-top-line" /> |
|||
<span class="left-bottom-line" /> |
|||
<div slot="header" class="clearfix"> |
|||
<el-tooltip class="item" effect="dark" content="选择指定角色分配菜单" placement="top"> |
|||
<span class="role-span">菜单分配</span> |
|||
</el-tooltip> |
|||
<el-button |
|||
v-permission="['admin','roles:edit']" |
|||
:disabled="!showButton" |
|||
:loading="menuLoading" |
|||
icon="el-icon-check" |
|||
size="mini" |
|||
style="float: right; padding: 6px 9px" |
|||
@click="saveMenu" |
|||
>保存</el-button> |
|||
</div> |
|||
<el-tree |
|||
ref="menu" |
|||
lazy |
|||
:data="menus" |
|||
:default-checked-keys="menuIds" |
|||
:load="getMenuDatas" |
|||
:props="defaultProps" |
|||
check-strictly |
|||
accordion |
|||
show-checkbox |
|||
node-key="id" |
|||
@check="menuChange" |
|||
/> |
|||
</el-card> |
|||
</el-col> |
|||
</el-row> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import crudRoles from '@/api/system/role' |
|||
import { getDepts, getDeptSuperior } from '@/api/system/dept' |
|||
import { getMenusTree, getChild } from '@/api/system/menu' |
|||
import CRUD, { presenter, header, form, crud } from '@crud/crud' |
|||
import rrOperation from '@crud/RR.operation' |
|||
import crudOperation from '@crud/CRUD.operation' |
|||
import udOperation from '@crud/UD.operation' |
|||
import pagination from '@crud/Pagination' |
|||
import Treeselect from '@riophae/vue-treeselect' |
|||
import '@riophae/vue-treeselect/dist/vue-treeselect.css' |
|||
import { LOAD_CHILDREN_OPTIONS } from '@riophae/vue-treeselect' |
|||
import DateRangePicker from '@/components/DateRangePicker' |
|||
|
|||
const defaultForm = { id: null, name: null, depts: [], description: null, dataScope: '全部', level: 3 } |
|||
export default { |
|||
name: 'Role', |
|||
components: { Treeselect, pagination, crudOperation, rrOperation, udOperation, DateRangePicker }, |
|||
cruds() { |
|||
return CRUD({ title: '角色', url: 'api/roles', sort: 'level,asc', crudMethod: { ...crudRoles }}) |
|||
}, |
|||
mixins: [presenter(), header(), form(defaultForm), crud()], |
|||
data() { |
|||
return { |
|||
defaultProps: { children: 'children', label: 'label', isLeaf: 'leaf' }, |
|||
dateScopes: ['全部', '本级', '自定义'], level: 3, |
|||
currentId: 0, menuLoading: false, showButton: false, |
|||
menus: [], menuIds: [], depts: [], deptDatas: [], // 多选时使用 |
|||
permission: { |
|||
add: ['admin', 'roles:add'], |
|||
edit: ['admin', 'roles:edit'], |
|||
del: ['admin', 'roles:del'] |
|||
}, |
|||
rules: { |
|||
name: [ |
|||
{ required: true, message: '请输入名称', trigger: 'blur' } |
|||
], |
|||
permission: [ |
|||
{ required: true, message: '请输入权限', trigger: 'blur' } |
|||
] |
|||
} |
|||
} |
|||
}, |
|||
created() { |
|||
crudRoles.getLevel().then(data => { |
|||
this.level = data.level |
|||
}) |
|||
}, |
|||
methods: { |
|||
getMenuDatas(node, resolve) { |
|||
setTimeout(() => { |
|||
getMenusTree(node.data.id ? node.data.id : 0).then(res => { |
|||
resolve(res) |
|||
}) |
|||
}, 100) |
|||
}, |
|||
[CRUD.HOOK.afterRefresh]() { |
|||
this.$refs.menu.setCheckedKeys([]) |
|||
}, |
|||
// 新增前初始化部门信息 |
|||
[CRUD.HOOK.beforeToAdd](crud, form) { |
|||
this.deptDatas = [] |
|||
form.menus = null |
|||
}, |
|||
// 编辑前初始化自定义数据权限的部门信息 |
|||
[CRUD.HOOK.beforeToEdit](crud, form) { |
|||
this.deptDatas = [] |
|||
if (form.dataScope === '自定义') { |
|||
this.getSupDepts(form.depts) |
|||
} |
|||
const _this = this |
|||
form.depts.forEach(function(dept) { |
|||
_this.deptDatas.push(dept.id) |
|||
}) |
|||
// 将角色的菜单清空,避免日志入库数据过长 |
|||
form.menus = null |
|||
}, |
|||
// 提交前做的操作 |
|||
[CRUD.HOOK.afterValidateCU](crud) { |
|||
if (crud.form.dataScope === '自定义' && this.deptDatas.length === 0) { |
|||
this.$message({ |
|||
message: '自定义数据权限不能为空', |
|||
type: 'warning' |
|||
}) |
|||
return false |
|||
} else if (crud.form.dataScope === '自定义') { |
|||
const depts = [] |
|||
this.deptDatas.forEach(function(data) { |
|||
const dept = { id: data } |
|||
depts.push(dept) |
|||
}) |
|||
crud.form.depts = depts |
|||
} else { |
|||
crud.form.depts = [] |
|||
} |
|||
return true |
|||
}, |
|||
// 触发单选 |
|||
handleCurrentChange(val) { |
|||
if (val) { |
|||
const _this = this |
|||
// 清空菜单的选中 |
|||
this.$refs.menu.setCheckedKeys([]) |
|||
// 保存当前的角色id |
|||
this.currentId = val.id |
|||
// 初始化默认选中的key |
|||
this.menuIds = [] |
|||
val.menus.forEach(function(data) { |
|||
_this.menuIds.push(data.id) |
|||
}) |
|||
this.showButton = true |
|||
} |
|||
}, |
|||
menuChange(menu) { |
|||
// 获取该节点的所有子节点,id 包含自身 |
|||
getChild(menu.id).then(childIds => { |
|||
// 判断是否在 menuIds 中,如果存在则删除,否则添加 |
|||
if (this.menuIds.indexOf(menu.id) !== -1) { |
|||
for (let i = 0; i < childIds.length; i++) { |
|||
const index = this.menuIds.indexOf(childIds[i]) |
|||
if (index !== -1) { |
|||
this.menuIds.splice(index, 1) |
|||
} |
|||
} |
|||
} else { |
|||
for (let i = 0; i < childIds.length; i++) { |
|||
const index = this.menuIds.indexOf(childIds[i]) |
|||
if (index === -1) { |
|||
this.menuIds.push(childIds[i]) |
|||
} |
|||
} |
|||
} |
|||
this.$refs.menu.setCheckedKeys(this.menuIds) |
|||
}) |
|||
}, |
|||
// 保存菜单 |
|||
saveMenu() { |
|||
this.menuLoading = true |
|||
const role = { id: this.currentId, menus: [] } |
|||
// 得到已选中的 key 值 |
|||
this.menuIds.forEach(function(id) { |
|||
const menu = { id: id } |
|||
role.menus.push(menu) |
|||
}) |
|||
crudRoles.editMenu(role).then(() => { |
|||
this.crud.notify('保存成功', CRUD.NOTIFICATION_TYPE.SUCCESS) |
|||
this.menuLoading = false |
|||
this.update() |
|||
}).catch(err => { |
|||
this.menuLoading = false |
|||
console.log(err.response.data.message) |
|||
}) |
|||
}, |
|||
// 改变数据 |
|||
update() { |
|||
// 无刷新更新 表格数据 |
|||
crudRoles.get(this.currentId).then(res => { |
|||
for (let i = 0; i < this.crud.data.length; i++) { |
|||
if (res.id === this.crud.data[i].id) { |
|||
this.crud.data[i] = res |
|||
break |
|||
} |
|||
} |
|||
}) |
|||
}, |
|||
// 获取部门数据 |
|||
getDepts() { |
|||
getDepts({ enabled: true }).then(res => { |
|||
this.depts = res.content.map(function(obj) { |
|||
if (obj.hasChildren) { |
|||
obj.children = null |
|||
} |
|||
return obj |
|||
}) |
|||
}) |
|||
}, |
|||
getSupDepts(depts) { |
|||
const ids = [] |
|||
depts.forEach(dept => { |
|||
ids.push(dept.id) |
|||
}) |
|||
getDeptSuperior(ids).then(res => { |
|||
const date = res.content |
|||
this.buildDepts(date) |
|||
this.depts = date |
|||
}) |
|||
}, |
|||
buildDepts(depts) { |
|||
depts.forEach(data => { |
|||
if (data.children) { |
|||
this.buildDepts(data.children) |
|||
} |
|||
if (data.hasChildren && !data.children) { |
|||
data.children = null |
|||
} |
|||
}) |
|||
}, |
|||
// 获取弹窗内部门数据 |
|||
loadDepts({ action, parentNode, callback }) { |
|||
if (action === LOAD_CHILDREN_OPTIONS) { |
|||
getDepts({ enabled: true, pid: parentNode.id }).then(res => { |
|||
parentNode.children = res.content.map(function(obj) { |
|||
if (obj.hasChildren) { |
|||
obj.children = null |
|||
} |
|||
return obj |
|||
}) |
|||
setTimeout(() => { |
|||
callback() |
|||
}, 200) |
|||
}) |
|||
} |
|||
}, |
|||
// 如果数据权限为自定义则获取部门数据 |
|||
changeScope() { |
|||
if (this.form.dataScope === '自定义') { |
|||
this.getDepts() |
|||
} |
|||
}, |
|||
checkboxT(row) { |
|||
return row.level >= this.level |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style rel="stylesheet/scss" lang="scss"> |
|||
.role-span { |
|||
font-weight: bold;color: #303133; |
|||
font-size: 15px; |
|||
} |
|||
</style> |
|||
|
|||
<style rel="stylesheet/scss" lang="scss" scoped> |
|||
.head-container{ |
|||
padding: 0 0 20px 0; |
|||
} |
|||
::v-deep .el-input-number .el-input__inner { |
|||
text-align: left; |
|||
} |
|||
::v-deep .vue-treeselect__multi-value{ |
|||
margin-bottom: 0; |
|||
} |
|||
::v-deep .vue-treeselect__multi-value-item{ |
|||
border: 0; |
|||
padding: 0; |
|||
} |
|||
::v-deep .el-table__body tr.current-row > td{ |
|||
color: #fff; |
|||
background-color: #13439E; |
|||
} |
|||
::v-deep .el-table__header{ |
|||
border-top: 1px solid #113D72; |
|||
} |
|||
</style> |
|||
<template> |
|||
<div class="app-container"> |
|||
<!--工具栏--> |
|||
<div class="head-container"> |
|||
<div v-if="crud.props.searchToggle" class="head-search"> |
|||
<!-- 搜索 --> |
|||
<el-input v-model="query.blurry" size="small" clearable placeholder="输入名称或者描述搜索" prefix-icon="el-icon-search" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" /> |
|||
<date-range-picker v-model="query.createTime" class="date-item" /> |
|||
<rrOperation /> |
|||
</div> |
|||
<crudOperation :permission="permission" /> |
|||
</div> |
|||
<!-- 表单渲染 --> |
|||
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title"> |
|||
<span class="dialog-right-top" /> |
|||
<span class="dialog-left-bottom" /> |
|||
<div class="setting-dialog"> |
|||
<el-form ref="form" :inline="true" :model="form" :rules="rules" size="small" label-width="80px"> |
|||
<el-form-item label="角色名称" prop="name"> |
|||
<el-input v-model="form.name" style="width: 380px;" /> |
|||
</el-form-item> |
|||
<!-- <el-form-item label="角色级别" prop="level"> |
|||
<el-input-number v-model.number="form.level" :min="1" controls-position="right" style="width: 145px;" /> |
|||
</el-form-item> |
|||
<el-form-item label="数据范围" prop="dataScope"> |
|||
<el-select v-model="form.dataScope" style="width: 140px" placeholder="请选择数据范围" @change="changeScope"> |
|||
<el-option |
|||
v-for="item in dateScopes" |
|||
:key="item" |
|||
:label="item" |
|||
:value="item" |
|||
/> |
|||
</el-select> |
|||
</el-form-item> --> |
|||
<el-form-item v-if="form.dataScope === '自定义'" label="数据权限" prop="depts"> |
|||
<treeselect |
|||
v-model="deptDatas" |
|||
:load-options="loadDepts" |
|||
:options="depts" |
|||
multiple |
|||
style="width: 380px" |
|||
placeholder="请选择" |
|||
/> |
|||
</el-form-item> |
|||
<el-form-item label="描述信息" prop="description"> |
|||
<el-input v-model="form.description" style="width: 380px;" rows="5" type="textarea" /> |
|||
</el-form-item> |
|||
</el-form> |
|||
<div slot="footer" class="dialog-footer"> |
|||
<el-button type="text" @click="crud.cancelCU">取消</el-button> |
|||
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button> |
|||
</div> |
|||
</div> |
|||
</el-dialog> |
|||
<el-row :gutter="15"> |
|||
<!--角色管理--> |
|||
<el-col :xs="15" :sm="17" :md="18" :lg="19" :xl="19"> |
|||
<el-card class="box-card" shadow="never"> |
|||
<span class="right-top-line" /> |
|||
<span class="left-bottom-line" /> |
|||
<div slot="header" class="clearfix"> |
|||
<span class="role-span">角色列表</span> |
|||
</div> |
|||
<el-table ref="table" v-loading="crud.loading" highlight-current-row style="width: 100%;" height="calc(100vh - 400px)" :data="crud.data" @selection-change="crud.selectionChangeHandler" @current-change="handleCurrentChange"> |
|||
<el-table-column :selectable="checkboxT" type="selection" align="center" width="55" /> |
|||
<el-table-column prop="name" label="名称" /> |
|||
<el-table-column prop="dataScope" label="数据权限" /> |
|||
<el-table-column prop="level" label="角色级别" /> |
|||
<el-table-column :show-overflow-tooltip="true" prop="description" label="描述" /> |
|||
<el-table-column :show-overflow-tooltip="true" width="135px" prop="createTime" label="创建日期"> |
|||
<template slot-scope="scope"> |
|||
<div>{{ scope.row.createTime | parseTime }}</div> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column v-if="checkPer(['admin','roles:edit','roles:del'])" label="操作" width="130px" align="center" fixed="right"> |
|||
<template slot-scope="scope"> |
|||
<udOperation |
|||
v-if="scope.row.level >= level" |
|||
:data="scope.row" |
|||
:permission="permission" |
|||
/> |
|||
</template> |
|||
</el-table-column> |
|||
</el-table> |
|||
<!--分页组件--> |
|||
<pagination /> |
|||
</el-card> |
|||
</el-col> |
|||
<!-- 菜单授权 --> |
|||
<el-col :xs="9" :sm="7" :md="6" :lg="5" :xl="5"> |
|||
<el-card class="box-card" shadow="never" style="min-height:calc(100vh - 292px)"> |
|||
<span class="right-top-line" /> |
|||
<span class="left-bottom-line" /> |
|||
<div slot="header" class="clearfix"> |
|||
<el-tooltip class="item" effect="dark" content="选择指定角色分配菜单" placement="top"> |
|||
<span class="role-span">菜单分配</span> |
|||
</el-tooltip> |
|||
<el-button |
|||
v-permission="['admin','roles:edit']" |
|||
:disabled="!showButton" |
|||
:loading="menuLoading" |
|||
icon="el-icon-check" |
|||
size="mini" |
|||
style="float: right; padding: 6px 9px" |
|||
@click="saveMenu" |
|||
>保存</el-button> |
|||
</div> |
|||
<el-tree |
|||
ref="menu" |
|||
lazy |
|||
:data="menus" |
|||
:default-checked-keys="menuIds" |
|||
:load="getMenuDatas" |
|||
:props="defaultProps" |
|||
check-strictly |
|||
accordion |
|||
show-checkbox |
|||
node-key="id" |
|||
@check="menuChange" |
|||
/> |
|||
</el-card> |
|||
</el-col> |
|||
</el-row> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import crudRoles from '@/api/system/role' |
|||
import { getDepts, getDeptSuperior } from '@/api/system/dept' |
|||
import { getMenusTree, getChild } from '@/api/system/menu' |
|||
import CRUD, { presenter, header, form, crud } from '@crud/crud' |
|||
import rrOperation from '@crud/RR.operation' |
|||
import crudOperation from '@crud/CRUD.operation' |
|||
import udOperation from '@crud/UD.operation' |
|||
import pagination from '@crud/Pagination' |
|||
import Treeselect from '@riophae/vue-treeselect' |
|||
import '@riophae/vue-treeselect/dist/vue-treeselect.css' |
|||
import { LOAD_CHILDREN_OPTIONS } from '@riophae/vue-treeselect' |
|||
import DateRangePicker from '@/components/DateRangePicker' |
|||
|
|||
const defaultForm = { id: null, name: null, depts: [], description: null, dataScope: '全部', level: 3 } |
|||
export default { |
|||
name: 'Role', |
|||
components: { Treeselect, pagination, crudOperation, rrOperation, udOperation, DateRangePicker }, |
|||
cruds() { |
|||
return CRUD({ title: '角色', url: 'api/roles', sort: 'level,asc', crudMethod: { ...crudRoles }}) |
|||
}, |
|||
mixins: [presenter(), header(), form(defaultForm), crud()], |
|||
data() { |
|||
return { |
|||
defaultProps: { children: 'children', label: 'label', isLeaf: 'leaf' }, |
|||
dateScopes: ['全部', '本级', '自定义'], level: 3, |
|||
currentId: 0, menuLoading: false, showButton: false, |
|||
menus: [], menuIds: [], depts: [], deptDatas: [], // 多选时使用 |
|||
permission: { |
|||
add: ['admin', 'roles:add'], |
|||
edit: ['admin', 'roles:edit'], |
|||
del: ['admin', 'roles:del'] |
|||
}, |
|||
rules: { |
|||
name: [ |
|||
{ required: true, message: '请输入名称', trigger: 'blur' } |
|||
], |
|||
permission: [ |
|||
{ required: true, message: '请输入权限', trigger: 'blur' } |
|||
] |
|||
} |
|||
} |
|||
}, |
|||
created() { |
|||
crudRoles.getLevel().then(data => { |
|||
this.level = data.level |
|||
}) |
|||
}, |
|||
methods: { |
|||
getMenuDatas(node, resolve) { |
|||
setTimeout(() => { |
|||
getMenusTree(node.data.id ? node.data.id : 0).then(res => { |
|||
resolve(res) |
|||
}) |
|||
}, 100) |
|||
}, |
|||
[CRUD.HOOK.afterRefresh]() { |
|||
this.$refs.menu.setCheckedKeys([]) |
|||
}, |
|||
// 新增前初始化部门信息 |
|||
[CRUD.HOOK.beforeToAdd](crud, form) { |
|||
this.deptDatas = [] |
|||
form.menus = null |
|||
}, |
|||
// 编辑前初始化自定义数据权限的部门信息 |
|||
[CRUD.HOOK.beforeToEdit](crud, form) { |
|||
this.deptDatas = [] |
|||
if (form.dataScope === '自定义') { |
|||
this.getSupDepts(form.depts) |
|||
} |
|||
const _this = this |
|||
form.depts.forEach(function(dept) { |
|||
_this.deptDatas.push(dept.id) |
|||
}) |
|||
// 将角色的菜单清空,避免日志入库数据过长 |
|||
form.menus = null |
|||
}, |
|||
// 提交前做的操作 |
|||
[CRUD.HOOK.afterValidateCU](crud) { |
|||
if (crud.form.dataScope === '自定义' && this.deptDatas.length === 0) { |
|||
this.$message({ |
|||
message: '自定义数据权限不能为空', |
|||
type: 'warning' |
|||
}) |
|||
return false |
|||
} else if (crud.form.dataScope === '自定义') { |
|||
const depts = [] |
|||
this.deptDatas.forEach(function(data) { |
|||
const dept = { id: data } |
|||
depts.push(dept) |
|||
}) |
|||
crud.form.depts = depts |
|||
} else { |
|||
crud.form.depts = [] |
|||
} |
|||
return true |
|||
}, |
|||
// 触发单选 |
|||
handleCurrentChange(val) { |
|||
if (val) { |
|||
const _this = this |
|||
// 清空菜单的选中 |
|||
this.$refs.menu.setCheckedKeys([]) |
|||
// 保存当前的角色id |
|||
this.currentId = val.id |
|||
// 初始化默认选中的key |
|||
this.menuIds = [] |
|||
val.menus.forEach(function(data) { |
|||
_this.menuIds.push(data.id) |
|||
}) |
|||
this.showButton = true |
|||
} |
|||
}, |
|||
menuChange(menu) { |
|||
// 获取该节点的所有子节点,id 包含自身 |
|||
getChild(menu.id).then(childIds => { |
|||
// 判断是否在 menuIds 中,如果存在则删除,否则添加 |
|||
if (this.menuIds.indexOf(menu.id) !== -1) { |
|||
for (let i = 0; i < childIds.length; i++) { |
|||
const index = this.menuIds.indexOf(childIds[i]) |
|||
if (index !== -1) { |
|||
this.menuIds.splice(index, 1) |
|||
} |
|||
} |
|||
} else { |
|||
for (let i = 0; i < childIds.length; i++) { |
|||
const index = this.menuIds.indexOf(childIds[i]) |
|||
if (index === -1) { |
|||
this.menuIds.push(childIds[i]) |
|||
} |
|||
} |
|||
} |
|||
this.$refs.menu.setCheckedKeys(this.menuIds) |
|||
}) |
|||
}, |
|||
// 保存菜单 |
|||
saveMenu() { |
|||
this.menuLoading = true |
|||
const role = { id: this.currentId, menus: [] } |
|||
// 得到已选中的 key 值 |
|||
this.menuIds.forEach(function(id) { |
|||
const menu = { id: id } |
|||
role.menus.push(menu) |
|||
}) |
|||
crudRoles.editMenu(role).then(() => { |
|||
this.crud.notify('保存成功', CRUD.NOTIFICATION_TYPE.SUCCESS) |
|||
this.menuLoading = false |
|||
this.update() |
|||
}).catch(err => { |
|||
this.menuLoading = false |
|||
console.log(err.response.data.message) |
|||
}) |
|||
}, |
|||
// 改变数据 |
|||
update() { |
|||
// 无刷新更新 表格数据 |
|||
crudRoles.get(this.currentId).then(res => { |
|||
for (let i = 0; i < this.crud.data.length; i++) { |
|||
if (res.id === this.crud.data[i].id) { |
|||
this.crud.data[i] = res |
|||
break |
|||
} |
|||
} |
|||
}) |
|||
}, |
|||
// 获取部门数据 |
|||
getDepts() { |
|||
getDepts({ enabled: true }).then(res => { |
|||
this.depts = res.content.map(function(obj) { |
|||
if (obj.hasChildren) { |
|||
obj.children = null |
|||
} |
|||
return obj |
|||
}) |
|||
}) |
|||
}, |
|||
getSupDepts(depts) { |
|||
const ids = [] |
|||
depts.forEach(dept => { |
|||
ids.push(dept.id) |
|||
}) |
|||
getDeptSuperior(ids).then(res => { |
|||
const date = res.content |
|||
this.buildDepts(date) |
|||
this.depts = date |
|||
}) |
|||
}, |
|||
buildDepts(depts) { |
|||
depts.forEach(data => { |
|||
if (data.children) { |
|||
this.buildDepts(data.children) |
|||
} |
|||
if (data.hasChildren && !data.children) { |
|||
data.children = null |
|||
} |
|||
}) |
|||
}, |
|||
// 获取弹窗内部门数据 |
|||
loadDepts({ action, parentNode, callback }) { |
|||
if (action === LOAD_CHILDREN_OPTIONS) { |
|||
getDepts({ enabled: true, pid: parentNode.id }).then(res => { |
|||
parentNode.children = res.content.map(function(obj) { |
|||
if (obj.hasChildren) { |
|||
obj.children = null |
|||
} |
|||
return obj |
|||
}) |
|||
setTimeout(() => { |
|||
callback() |
|||
}, 200) |
|||
}) |
|||
} |
|||
}, |
|||
// 如果数据权限为自定义则获取部门数据 |
|||
changeScope() { |
|||
if (this.form.dataScope === '自定义') { |
|||
this.getDepts() |
|||
} |
|||
}, |
|||
checkboxT(row) { |
|||
return row.level >= this.level |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style rel="stylesheet/scss" lang="scss"> |
|||
.role-span { |
|||
font-weight: bold;color: #303133; |
|||
font-size: 15px; |
|||
} |
|||
</style> |
|||
|
|||
<style rel="stylesheet/scss" lang="scss" scoped> |
|||
.head-container{ |
|||
padding: 0 0 20px 0; |
|||
} |
|||
::v-deep .el-input-number .el-input__inner { |
|||
text-align: left; |
|||
} |
|||
::v-deep .vue-treeselect__multi-value{ |
|||
margin-bottom: 0; |
|||
} |
|||
::v-deep .vue-treeselect__multi-value-item{ |
|||
border: 0; |
|||
padding: 0; |
|||
} |
|||
::v-deep .el-table__body tr.current-row > td{ |
|||
color: #fff; |
|||
background-color: #13439E; |
|||
} |
|||
::v-deep .el-table__header{ |
|||
border-top: 1px solid #113D72; |
|||
} |
|||
</style> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue