变量名称修改

用户增加部门字段
移除无用代码
修复OrgTree的一些问题
使用原本的el-select
master
toesbieya 6 years ago
parent 90097bfe0e
commit 0e8f4dc504
  1. 4
      vue/element-ui-personal/index.js
  2. 2
      vue/mock/controller/account/index.js
  3. 4
      vue/src/api/account.js
  4. 8
      vue/src/api/system/department.js
  5. 20
      vue/src/assets/styles/index.scss
  6. 127
      vue/src/components/OrgTree/OrgTreeNode.vue
  7. 266
      vue/src/components/OrgTree/index.vue
  8. 398
      vue/src/components/OrgTree/tree.scss
  9. 337
      vue/src/components/TreeSelect/index.vue
  10. 2
      vue/src/config/request.js
  11. 4
      vue/src/layout/components/Navbar/index.vue
  12. 2
      vue/src/mixins/bizDocumentTableMixin.js
  13. 4
      vue/src/store/modules/resource.js
  14. 2
      vue/src/store/modules/socket.js
  15. 15
      vue/src/store/modules/user.js
  16. 2
      vue/src/utils/auth.js
  17. 66
      vue/src/utils/index.js
  18. 178
      vue/src/views/app/login/LoginForm.vue
  19. 6
      vue/src/views/example/components/treeSelectExample.vue
  20. 4
      vue/src/views/purchase/inbound/components/OrderSelector.vue
  21. 6
      vue/src/views/purchase/inbound/detail.vue
  22. 6
      vue/src/views/purchase/inbound/index.vue
  23. 6
      vue/src/views/purchase/order/detail.vue
  24. 4
      vue/src/views/purchase/order/index.vue
  25. 2
      vue/src/views/sell/order/components/StockSelector.vue
  26. 26
      vue/src/views/sell/order/detail.vue
  27. 12
      vue/src/views/sell/order/index.vue
  28. 4
      vue/src/views/sell/outbound/components/OrderSelector.vue
  29. 14
      vue/src/views/sell/outbound/detail.vue
  30. 6
      vue/src/views/sell/outbound/index.vue
  31. 16
      vue/src/views/stock/current/index.vue
  32. 184
      vue/src/views/system/department/components/OrgTreeView.vue
  33. 315
      vue/src/views/system/department/index.vue
  34. 16
      vue/src/views/system/resource/EditDialog.vue
  35. 4
      vue/src/views/system/resource/index.vue
  36. 190
      vue/src/views/system/role/EditDialog.vue
  37. 158
      vue/src/views/system/role/components/EditDialog.vue
  38. 59
      vue/src/views/system/role/components/RoleResource.vue
  39. 215
      vue/src/views/system/role/index.vue
  40. 39
      vue/src/views/system/user/components/DepartmentSelector.vue
  41. 231
      vue/src/views/system/user/components/EditDialog.vue
  42. 9
      vue/src/views/system/user/components/RoleSelector.vue
  43. 299
      vue/src/views/system/user/index.vue
  44. 26
      vue/src/views/userCenter/components/Account.vue
  45. 77
      vue/src/views/userCenter/components/UserCard.vue
  46. 33
      vue/src/views/userCenter/index.vue

@ -1,9 +1,9 @@
import MenuItem from "./components/Menu/MenuItem"
import Submenu from "./components/Menu/Submenu"
import Message from "./components/Message"
import Select from './components/Select/Select'
//import Select from './components/Select/Select'
const components = [MenuItem, Submenu, Select]
const components = [MenuItem, Submenu]
export default function (Vue) {
components.forEach(component => {

@ -13,7 +13,7 @@ module.exports = [
else res.send(success({
id: 1,
name: 'admin',
admin: 1,
admin: true,
token: 'test'
}))
}

@ -26,6 +26,6 @@ export function validate(pwd) {
return request.get(`${baseUrl}/validate?pwd=${pwd}`).then(({data}) => data)
}
export function checkName(name) {
return request.get(`${baseUrl}/checkName?name=${name}`).then(({data}) => data)
export function checkName(name, id) {
return request.get(`${baseUrl}/checkName?name=${name}&id=${id}`).then(({data}) => data)
}

@ -2,12 +2,8 @@ import request from "@/config/request"
export const baseUrl = `/system/department`
export function getDepartments() {
return request.get(`${baseUrl}/get`).then(({data}) => data.data)
}
export function getAllDepartments() {
return request.get(`${baseUrl}/getAll`).then(({data}) => data.data)
export function getDepartments(all = true) {
return request.get(`${baseUrl}/get?all=${all}`).then(({data}) => data.data)
}
export function addDepartment(data) {

@ -165,22 +165,6 @@ a:hover {
}
}
//印章
.seal {
float: left;
border: solid .1em $--color-danger;
border-radius: .2em;
color: $--color-danger;
font-size: 23px;
font-weight: bold;
padding: .1em .5em;
bottom: 15px;
& + & {
margin-left: 20px;
}
}
.button-group {
.el-button {
margin-top: 10px;
@ -200,4 +184,8 @@ a:hover {
}
}
.el-form-item__content > .el-tree {
margin-top: 8px;
}

@ -1,10 +1,11 @@
<script type="text/jsx">
//
const isLeaf = (data, prop) => {
return !(Array.isArray(data[prop]) && data[prop].length > 0)
return !Array.isArray(data[prop]) || data[prop].length === 0
}
// node
export const renderNode = (h, data, context) => {
const renderNode = (h, data, context) => {
const {props} = context
const cls = ['org-tree-node']
const childNodes = []
@ -23,44 +24,11 @@ export const renderNode = (h, data, context) => {
childNodes.push(renderChildren(h, children, context))
}
return h('div', {
domProps: {
className: cls.join(' ')
}
}, childNodes)
}
//
export const renderBtn = (h, data, context) => {
const {props} = context
const expandHandler = context.listeners['on-expand']
let cls = ['org-tree-node-btn']
if (data[props.props.expand]) {
cls.push('expanded')
}
return h('span', {
'class': 'org-tree-button-wrapper',
on: {
click: e => {
e.stopPropagation()
expandHandler && expandHandler(data)
},
mousedown: e => e.stopPropagation()
}
}, [
props.buttonRender
? props.buttonRender(h, data)
: h('span', {
'class': cls.join(' ')
})
])
return <div class={cls.join(' ')}>{childNodes}</div>
}
// label
export const renderLabel = (h, data, context) => {
const renderLabel = (h, data, context) => {
const {props} = context
const label = data[props.props.label]
const nodeRender = props.nodeRender
@ -77,8 +45,7 @@ export const renderLabel = (h, data, context) => {
const childNodes = []
if (typeof nodeRender === 'function') {
let vnode = nodeRender(h, data)
vnode && childNodes.push(vnode)
childNodes.push(nodeRender(h, data))
}
else if (context.parent.$scopedSlots.default) {
childNodes.push(context.parent.$scopedSlots.default(data))
@ -99,44 +66,60 @@ export const renderLabel = (h, data, context) => {
}
labelClassName && cls.push(labelClassName)
return h('div', {
domProps: {
className: 'org-tree-node-label'
},
on: {
click: e => clickHandler && clickHandler(e, data),
mousedown: mousedownHandler,
mouseup: e => mouseupHandler && mouseupHandler(e, data),
touchstart: e => touchstartHandler && touchstartHandler(e, data),
touchleave: e => touchleaveHandler && touchleaveHandler(e, data)
}
}, [h('div', {
domProps: {
className: cls.join(' ')
},
style: {width: labelWidth}
}, childNodes)])
return (
<div
class="org-tree-node-label"
on-click={e => clickHandler && clickHandler(e, data)}
on-mousedown={mousedownHandler}
on-mouseup={e => mouseupHandler && mouseupHandler(e, data)}
on-touchstart={e => touchstartHandler && touchstartHandler(e, data)}
on-touchleave={e => touchleaveHandler && touchleaveHandler(e, data)}
>
<div class={cls.join(' ')} style={{width: labelWidth}}>
{childNodes}
</div>
</div>
)
}
//
const renderBtn = (h, data, context) => {
const {props} = context
const expandHandler = context.listeners['on-expand']
const onClock = e => {
e.stopPropagation()
expandHandler && expandHandler(data)
}
const onMousedown = e => e.stopPropagation()
const cls = ['org-tree-node-btn']
data[props.props.expand] && cls.push('expanded')
return (
<span class="org-tree-button-wrapper" on-click={onClock} on-mousedown={onMousedown}>
{props.buttonRender ? props.buttonRender(h, data) : <span class={cls.join(' ')}/>}
</span>
)
}
// node
export const renderChildren = (h, list, context) => {
const renderChildren = (h, list, context) => {
if (Array.isArray(list) && list.length) {
const children = list.map(item => {
return renderNode(h, item, context)
})
return h('div', {
domProps: {
className: 'org-tree-node-children'
}
}, children)
return (
<div class="org-tree-node-children">
{list.map(item => renderNode(h, item, context))}
</div>
)
}
return ''
}
export const render = (h, context) => {
const {props} = context
return renderNode(h, props.data, context)
}
export default {
name: "OrgTreeNode",
export default render
functional: true,
render(h, context) {
return renderNode(h, context.props.data, context)
}
}
</script>

@ -1,187 +1,143 @@
<template>
<div class="org-tree-container">
<div :class="{horizontal, collapsable}" class="org-tree">
<div class="org-tree" :class="{horizontal, collapsable}">
<org-tree-node
v-bind="$listeners"
:button-render="buttonRender"
:collapsable="collapsable"
:data="dataCloned"
:horizontal="horizontal"
:label-class-name="labelClassName"
:label-width="labelWidth"
:node-render="nodeRender"
:props="props"
@on-expand="handleExpand"
@on-node-click="handleNodeClick"
:data="dataCloned"
:props="props"
:horizontal="horizontal"
:label-width="labelWidth"
:collapsable="collapsable"
:label-class-name="labelClassName"
:node-render="nodeRender"
:button-render="buttonRender"
@on-expand="handleExpand"
@on-node-click="handleNodeClick"
/>
</div>
</div>
</template>
<script>
import render from './node'
import {deepClone as clonedeep} from '@/utils'
import OrgTreeNode from "./OrgTreeNode"
export default {
name: 'OrgTree',
export default {
name: 'OrgTree',
components: {
OrgTreeNode: {
render,
functional: true
}
},
components: {OrgTreeNode},
props: {
data: {required: true},
props: {
data: {
type: Object,
required: true
},
props: {
type: Object,
default: () => ({
id: 'id',
label: 'label',
expand: 'expand',
children: 'children'
})
},
horizontal: Boolean,
collapsable: Boolean,
nodeRender: Function,
buttonRender: Function,
labelWidth: [String, Number],
labelClassName: [Function, String],
expandAll: {
type: Boolean,
default: false
}
type: Object,
default: () => ({
id: 'id',
label: 'label',
expand: 'expand',
children: 'children'
})
},
horizontal: Boolean,
collapsable: Boolean,
nodeRender: Function,
buttonRender: Function,
labelWidth: [String, Number],
labelClassName: [Function, String],
expandAll: Boolean
},
data() {
return {
flatData: {},
dataCloned: {}
}
},
data() {
return {
flatData: {},
dataCloned: {}
}
},
watch: {
data(newData) {
this._handleData(newData)
this._mapData(this.dataCloned, item => {
const {expand} = this.flatData[item[this.prop_id]] || {}
if (expand) this.$set(item, this.prop_expand, true)
})
this._toggleExpand(this.dataCloned, this.expandAll)
},
expandAll(status) {
this._toggleExpand(this.dataCloned, status)
}
watch: {
data(newData) {
this._cloneData(newData)
this._mapData(this.dataCloned, item => {
const {expand} = this.flatData[item[this.props.id]] || {}
if (expand) this.$set(item, this.props.expand, true)
})
this._toggleExpand(this.dataCloned, this.expandAll)
},
expandAll(status) {
this._toggleExpand(this.dataCloned, status)
}
},
computed: {
prop_id() {
return this.props.id
},
prop_label() {
return this.props.label
},
prop_expand() {
return this.props.expand
},
prop_children() {
return this.props.children
}
methods: {
_cloneData(newData) {
this.dataCloned = JSON.parse(JSON.stringify(newData))
},
methods: {
_handleData(data) {
this._cloneData(data)
},
_cloneData(newData) {
this.dataCloned = clonedeep(newData)
},
_setFlatData(data) {
this.flatData[data[this.prop_id]] = data
},
/**
* @description 工具方法用于遍历树状数据的每个节点 fn为在该节点做的操作其有一个参数即当前节点数据
*/
_mapData(data, fn) {
fn(data)
const children = data[this.prop_children]
if (children) {
children.forEach(child => {
this._mapData(child, fn)
})
}
},
/**
* @description 用来便利所有节点数据将树状数据扁平化存放到flatData用于数据更新后展开状态的恢复
*/
_updateExpandStatus() {
this._mapData(this.dataCloned, this._setFlatData)
},
collapse(list) {
var _this = this
list.forEach(child => {
if (child[this.prop_expand]) {
child[this.prop_expand] = false
_setFlatData(data) {
this.flatData[data[this.props.id]] = data
},
/**
* @description 工具方法用于遍历树状数据的每个节点 fn为在该节点做的操作其有一个参数即当前节点数据
*/
_mapData(data, fn) {
fn(data)
const children = data[this.props.children]
children && children.forEach(child => this._mapData(child, fn))
},
/**
* @description 用来便利所有节点数据将树状数据扁平化存放到flatData用于数据更新后展开状态的恢复
*/
_updateExpandStatus() {
this._mapData(this.dataCloned, this._setFlatData)
},
_toggleExpand(data, status) {
if (Array.isArray(data)) {
data.forEach(item => {
this.$set(item, this.props.expand, status)
const children = item[this.props.children]
if (children) {
this._toggleExpand(children, status)
}
const children = child[this.prop_children]
children && _this.collapse(children)
})
},
handleExpand(data) {
if (this.prop_expand in data) {
data[this.prop_expand] = !data[this.prop_expand]
const children = data[this.prop_children]
if (!data[this.prop_expand] && children) {
this.collapse(children)
}
}
else {
this.$set(data, this.prop_expand, true)
}
else {
this.$set(data, this.props.expand, status)
const children = data[this.props.children]
if (children) {
this._toggleExpand(children, status)
}
this.$emit('on-expand', data, data[this.prop_expand])
this._updateExpandStatus()
},
_toggleExpand(data, status) {
var _this = this
if (Array.isArray(data)) {
data.forEach(item => {
_this.$set(item, this.prop_expand, status)
const children = item[this.prop_children]
if (children) {
_this._toggleExpand(children, status)
}
})
}
},
collapse(list) {
list.forEach(child => {
if (child[this.props.expand]) {
child[this.props.expand] = false
}
else {
_this.$set(data, this.prop_expand, status)
const children = data[this.prop_children]
if (children) {
_this._toggleExpand(children, status)
}
const children = child[this.props.children]
children && this.collapse(children)
})
},
handleExpand(data) {
if (this.props.expand in data) {
data[this.props.expand] = !data[this.props.expand]
const children = data[this.props.children]
if (!data[this.props.expand] && children) {
this.collapse(children)
}
},
handleNodeClick(e, data) {
this.$emit('on-node-click', e, data, () => {
this.handleExpand(data)
})
},
toggleExpand() {
this._toggleExpand(this.dataCloned, this.expandAll)
this._updateExpandStatus()
}
},
else this.$set(data, this.props.expand, true)
mounted() {
this._handleData(this.data)
this.$emit('on-expand', data, data[this.props.expand])
this._updateExpandStatus()
this._toggleExpand(this.dataCloned, this.expandAll)
},
handleNodeClick(e, data) {
this.$emit('on-node-click', e, data, () => this.handleExpand(data))
}
},
mounted() {
this._cloneData(this.data)
this._updateExpandStatus()
this._toggleExpand(this.dataCloned, this.expandAll)
}
}
</script>
<style lang="scss" src="./tree.scss"></style>

@ -1,261 +1,261 @@
.org-tree-container {
display: inline-block;
padding: 15px;
}
.org-tree {
display: table;
text-align: center;
&:before, &:after {
content: '';
.org-tree {
display: table;
}
&:after {
clear: both;
}
}
.org-tree-node,
.org-tree-node-children {
position: relative;
margin: 0;
padding: 0;
list-style-type: none;
&:before, &:after {
transition: all .35s;
}
}
.org-tree-node-label {
position: relative;
display: inline-block;
z-index: 1;
.org-tree-node-label-inner {
padding: 10px 15px;
text-align: center;
border-radius: 3px;
box-shadow: 0 1px 5px rgba(0, 0, 0, .15);
}
}
.org-tree-node-btn {
position: absolute;
top: 100%;
left: 50%;
width: 20px;
height: 20px;
z-index: 10;
margin-left: -10px;
margin-top: 10px;
background-color: #fff;
border: 1px solid #ccc;
border-radius: 50%;
box-shadow: 0 0 2px rgba(0, 0, 0, .15);
cursor: pointer;
transition: all .35s ease;
&:hover {
background-color: #e7e8e9;
transform: scale(1.15);
}
&:before, &:after {
content: '';
position: absolute;
}
&:before {
top: 50%;
left: 4px;
right: 4px;
height: 0;
border-top: 1px solid #ccc;
}
&:after {
top: 4px;
left: 50%;
bottom: 4px;
width: 0;
border-left: 1px solid #ccc;
}
&.expanded:after {
border: none;
}
}
.org-tree-node {
padding-top: 20px;
display: table-cell;
vertical-align: top;
&:before, &:after {
content: '';
display: table;
}
&.is-leaf, &.collapsed {
padding-left: 10px;
padding-right: 10px;
&:after {
clear: both;
}
}
&:before, &:after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 50%;
height: 19px;
}
.org-tree-node,
.org-tree-node-children {
position: relative;
margin: 0;
padding: 0;
list-style-type: none;
&:after {
left: 50%;
border-left: 1px solid #ddd;
&:before, &:after {
transition: all .35s;
}
}
&:not(:first-child):before,
&:not(:last-child):after {
border-top: 1px solid #ddd;
.org-tree-node-label {
position: relative;
display: inline-block;
z-index: 1;
.org-tree-node-label-inner {
padding: 10px 15px;
text-align: center;
border-radius: 3px;
box-shadow: 0 1px 5px rgba(0, 0, 0, .15);
}
}
}
.collapsable .org-tree-node.collapsed {
padding-bottom: 30px;
.org-tree-node-label:after {
content: '';
.org-tree-node-btn {
position: absolute;
top: 100%;
left: 0;
width: 50%;
left: 50%;
width: 20px;
height: 20px;
border-right: 1px solid #ddd;
}
}
.org-tree > .org-tree-node {
padding-top: 0;
z-index: 10;
margin-left: -10px;
margin-top: 10px;
background-color: #fff;
border: 1px solid #ccc;
border-radius: 50%;
box-shadow: 0 0 2px rgba(0, 0, 0, .15);
cursor: pointer;
transition: all .35s ease;
&:hover {
background-color: #e7e8e9;
transform: scale(1.15);
}
&:after {
border-left: 0;
}
}
&:before, &:after {
content: '';
position: absolute;
}
.org-tree-node-children {
padding-top: 20px;
display: table;
&:before {
top: 50%;
left: 4px;
right: 4px;
height: 0;
border-top: 1px solid #ccc;
}
&:before {
content: '';
position: absolute;
top: 0;
left: 50%;
width: 0;
height: 20px;
border-left: 1px solid #ddd;
}
&:after {
top: 4px;
left: 50%;
bottom: 4px;
width: 0;
border-left: 1px solid #ccc;
}
&:after {
content: '';
display: table;
clear: both;
&.expanded:after {
border: none;
}
}
}
.horizontal {
.org-tree-node {
padding-top: 20px;
display: table-cell;
float: none;
padding-top: 0;
padding-left: 20px;
vertical-align: top;
&.is-leaf, &.collapsed {
padding-top: 10px;
padding-bottom: 10px;
padding-left: 10px;
padding-right: 10px;
}
&:before, &:after {
width: 19px;
height: 50%;
}
&:after {
top: 50%;
content: '';
position: absolute;
top: 0;
left: 0;
border-left: 0;
width: 50%;
height: 19px;
}
&:only-child:before {
top: 1px;
border-bottom: 1px solid #ddd;
&:after {
left: 50%;
border-left: 1px solid #ddd;
}
&:not(:first-child):before,
&:not(:last-child):after {
border-top: 0;
border-left: 1px solid #ddd;
}
&:not(:only-child):after {
border-top: 1px solid #ddd;
}
.org-tree-node-inner {
display: table;
}
}
.org-tree-node-label {
display: table-cell;
vertical-align: middle;
}
&.collapsable .org-tree-node.collapsed {
padding-right: 30px;
.collapsable .org-tree-node.collapsed {
padding-bottom: 30px;
.org-tree-node-label:after {
top: 0;
left: 100%;
width: 20px;
height: 50%;
border-right: 0;
border-bottom: 1px solid #ddd;
content: '';
position: absolute;
top: 100%;
left: 0;
width: 50%;
height: 20px;
border-right: 1px solid #ddd;
}
}
.org-tree-node-btn {
top: 50%;
left: 100%;
margin-top: -11px;
margin-left: 9px;
}
.org-tree > .org-tree-node {
padding-top: 0;
& > .org-tree-node:only-child:before {
border-bottom: 0;
&:after {
border-left: 0;
}
}
.org-tree-node-children {
display: table-cell;
padding-top: 0;
padding-left: 20px;
padding-top: 20px;
display: table;
&:before {
top: 50%;
left: 0;
width: 20px;
height: 0;
border-left: 0;
border-top: 1px solid #ddd;
content: '';
position: absolute;
top: 0;
left: 50%;
width: 0;
height: 20px;
border-left: 1px solid #ddd;
}
&:after {
display: none;
content: '';
display: table;
clear: both;
}
}
.horizontal {
.org-tree-node {
display: table-cell;
float: none;
padding-top: 0;
padding-left: 20px;
&.is-leaf, &.collapsed {
padding-top: 10px;
padding-bottom: 10px;
}
&:before, &:after {
width: 19px;
height: 50%;
}
&:after {
top: 50%;
left: 0;
border-left: 0;
}
&:only-child:before {
top: 1px;
border-bottom: 1px solid #ddd;
}
&:not(:first-child):before,
&:not(:last-child):after {
border-top: 0;
border-left: 1px solid #ddd;
}
&:not(:only-child):after {
border-top: 1px solid #ddd;
}
.org-tree-node-inner {
display: table;
}
}
.org-tree-node-label {
display: table-cell;
vertical-align: middle;
}
&.collapsable .org-tree-node.collapsed {
padding-right: 30px;
.org-tree-node-label:after {
top: 0;
left: 100%;
width: 20px;
height: 50%;
border-right: 0;
border-bottom: 1px solid #ddd;
}
}
.org-tree-node-btn {
top: 50%;
left: 100%;
margin-top: -11px;
margin-left: 9px;
}
& > .org-tree-node:only-child:before {
border-bottom: 0;
}
& > .org-tree-node {
display: block;
.org-tree-node-children {
display: table-cell;
padding-top: 0;
padding-left: 20px;
&:before {
top: 50%;
left: 0;
width: 20px;
height: 0;
border-left: 0;
border-top: 1px solid #ddd;
}
&:after {
display: none;
}
& > .org-tree-node {
display: block;
}
}
}
}

@ -1,45 +1,45 @@
<template>
<el-select
ref="select"
:value="value"
:multiple="multiple"
:disabled="disabled"
:size="size"
:clearable="clearable"
:placeholder="placeholder"
:popper-append-to-body="popperAppendToBody"
:automatic-dropdown="automaticDropdown"
@input="$emit('input',$event)"
@change="$emit('change',$event)"
@remove-tag="removeTag"
ref="select"
:value="value"
:multiple="multiple"
:disabled="disabled"
:size="size"
:clearable="clearable"
:placeholder="placeholder"
:popper-append-to-body="popperAppendToBody"
:automatic-dropdown="automaticDropdown"
@input="e => $emit('input',e)"
@change="e => $emit('change',e)"
@remove-tag="removeTag"
>
<div class="tree-select-container" slot="empty">
<el-input
ref="search"
v-if="filterable && filterMethod"
v-model="search"
size="mini"
clearable
@input="onSearch"
ref="search"
v-if="filterable && filterMethod"
v-model="search"
size="mini"
clearable
@input="onSearch"
/>
<el-tree
ref="tree"
:data="data"
:node-key="nodeKey"
:props="props"
:show-checkbox="multiple"
:default-checked-keys="defaultCheckedKeys"
:current-node-key="multiple ? undefined : value"
:highlight-current="!multiple"
:expand-on-click-node="false"
:check-on-click-node="multiple"
:filter-node-method="filterMethod"
:accordion="accordion"
@node-click="nodeClick"
@check="check"
ref="tree"
:data="data"
:node-key="nodeKey"
:props="props"
:show-checkbox="multiple"
:default-checked-keys="defaultCheckedKeys"
:current-node-key="multiple ? undefined : value"
:highlight-current="!multiple"
:expand-on-click-node="false"
:check-on-click-node="multiple"
:filter-node-method="filterMethod"
:accordion="accordion"
@node-click="nodeClick"
@check="check"
>
<slot slot-scope="{node,data}">
<span :class="{'el-tree-node__label':true,'is-disabled':node.disabled}">{{node.label}}</span>
<span :class="{'el-tree-node__label':true,'is-disabled':node.disabled}">{{ node.label }}</span>
</slot>
</el-tree>
</div>
@ -47,168 +47,169 @@
</template>
<script>
import {debounce} from "@/utils"
import {flatTree} from "@/utils/tree"
export default {
name: "TreeSelect",
import {debounce} from "@/utils"
import {flatTree} from "@/utils/tree"
export default {
name: "TreeSelect",
props: {
value: {required: true},
data: {type: Array, default: () => []},
multiple: Boolean,
disabled: Boolean,
size: String,
clearable: {type: Boolean, default: true},
placeholder: String,
filterable: Boolean,
filterMethod: Function,
nodeKey: {type: String, default: 'id'},
props: {
value: {required: true},
data: {type: Array, default: () => []},
multiple: Boolean,
disabled: Boolean,
size: String,
clearable: {type: Boolean, default: true},
placeholder: String,
filterable: Boolean,
filterMethod: Function,
nodeKey: {type: String, default: 'id'},
props: {
default() {
return {
children: 'children',
label: 'label',
disabled: 'disabled'
}
default() {
return {
children: 'children',
label: 'label',
disabled: 'disabled'
}
},
accordion: {type: Boolean, default: true},
popperAppendToBody: Boolean,
automaticDropdown: Boolean
},
data() {
return {
currentNode: null,
search: ''
}
},
accordion: {type: Boolean, default: true},
popperAppendToBody: Boolean,
automaticDropdown: Boolean
},
data() {
return {
currentNode: null,
search: ''
}
},
computed: {
defaultCheckedKeys() {
return this.multiple ? this.value : []
}
computed: {
defaultCheckedKeys() {
return this.multiple ? this.value : []
}
},
watch: {
value(v) {
const method = this.multiple ? 'setCheckedKeys' : 'setCurrentKey'
this.$refs.tree[method](v || null)
if (!v && !this.multiple) this.currentNode = null
},
watch: {
value(v) {
const method = this.multiple ? 'setCheckedKeys' : 'setCurrentKey'
this.$refs.tree[method](v || null)
},
data() {
this.initSelectOptions()
}
},
methods: {
removeTag(v) {
const tree = this.$refs.tree
const value = [...this.value]
//ID
const parents = []
let removeNode = tree.getNode(v)
while (removeNode && removeNode.parent) {
parents.push(removeNode.parent.key)
removeNode = removeNode.parent
}
data() {
this.initSelectOptions()
//
for (let i = value.length - 1; i >= 0; i--) {
const isChild = this.getNodeParent(tree.getNode(value[i]), ({key}) => key === v)
if (isChild || parents.includes(value[i])) {
value.splice(i, 1)
}
}
value.length < this.value.length && this.$emit('input', value)
},
methods: {
removeTag(v) {
const tree = this.$refs.tree
const value = [...this.value]
//ID
const parents = []
let removeNode = tree.getNode(v)
while (removeNode && removeNode.parent) {
parents.push(removeNode.parent.key)
removeNode = removeNode.parent
}
onSearch(v) {
this.$refs.tree.filter(v)
},
//
for (let i = value.length - 1; i >= 0; i--) {
const isChild = this.getNodeParent(tree.getNode(value[i]), ({key}) => key === v)
if (isChild || parents.includes(value[i])) {
value.splice(i, 1)
}
}
nodeClick(data, node) {
if (this.multiple) return
value.length < this.value.length && this.$emit('input', value)
},
if (node.disabled) {
return this.$refs.tree.setCurrentKey(null)
}
onSearch(v) {
this.$refs.tree.filter(v)
},
if (this.currentNode === data) {
this.currentNode = null
this.$refs.tree.setCurrentKey(null)
}
else this.currentNode = data
nodeClick(data, node) {
if (this.multiple) return
this.$emit('input', this.currentNode && this.currentNode[this.nodeKey], this.currentNode)
this.$refs.select.blur()
},
if (node.disabled) {
return this.$refs.tree.setCurrentKey()
}
check(data, {checkedKeys}) {
this.multiple && this.$emit('input', checkedKeys)
},
if (this.currentNode === data) {
this.currentNode = null
this.$refs.tree.setCurrentKey()
}
else this.currentNode = data
this.$emit('input', this.currentNode && this.currentNode[this.nodeKey])
this.$refs.select.blur()
},
check(data, {checkedKeys}) {
this.multiple && this.$emit('input', checkedKeys)
},
getNodeParent(node, predicate) {
if (!node || predicate(node)) return node
return this.getNodeParent(node.parent, predicate)
},
initSelectOptions() {
this.$refs.select.cachedOptions =
flatTree(this.data, this.props.children)
.map(i => ({
value: i[this.nodeKey],
currentLabel: i[this.props.label]
}))
this.selectOptionsTrigger()
},
//el-selectoptionswatch
selectOptionsTrigger() {
const select = this.$refs.select
if (select.$isServer) return
this.$nextTick(() => {
select.broadcast('ElSelectDropdown', 'updatePopper')
})
if (select.multiple) {
select.resetInputHeight()
}
let inputs = select.$el.querySelectorAll('input')
if ([].indexOf.call(inputs, document.activeElement) === -1) {
select.setSelected()
}
if (select.defaultFirstOption && (select.filterable || select.remote) && select.filteredOptionsCount) {
select.checkDefaultFirstOption()
}
}
getNodeParent(node, predicate) {
if (!node || predicate(node)) return node
return this.getNodeParent(node.parent, predicate)
},
created() {
this.onSearch = debounce(this.onSearch)
initSelectOptions() {
this.$refs.select.cachedOptions =
flatTree(this.data, this.props.children)
.map(i => ({
value: i[this.nodeKey],
currentLabel: i[this.props.label]
}))
this.selectOptionsTrigger()
},
mounted() {
this.currentNode = this.$refs.tree.getCurrentNode()
this.initSelectOptions()
//el-selectoptionswatch
selectOptionsTrigger() {
const select = this.$refs.select
if (select.$isServer) return
this.$nextTick(() => {
select.broadcast('ElSelectDropdown', 'updatePopper')
})
if (select.multiple) {
select.resetInputHeight()
}
let inputs = select.$el.querySelectorAll('input')
if ([].indexOf.call(inputs, document.activeElement) === -1) {
select.setSelected()
}
if (select.defaultFirstOption && (select.filterable || select.remote) && select.filteredOptionsCount) {
select.checkDefaultFirstOption()
}
}
},
created() {
this.onSearch = debounce(this.onSearch)
},
mounted() {
this.currentNode = this.$refs.tree.getCurrentNode()
this.initSelectOptions()
}
}
</script>
<style lang="scss">
.tree-select-container {
padding: 8px;
.tree-select-container {
padding: 8px;
> .el-input {
margin-bottom: 8px;
}
> .el-input {
margin-bottom: 8px;
}
.el-tree-node__label.is-disabled {
background-color: #edf2fc;
border-color: #dcdfe6;
cursor: not-allowed;
}
.el-tree-node__label.is-disabled {
background-color: #edf2fc;
border-color: #dcdfe6;
cursor: not-allowed;
}
}
</style>

@ -44,7 +44,7 @@ service.interceptors.response.use(
//未登录
if (res.status === 401) {
if (store.state.user.prepare_logout) return Promise.reject()
if (store.state.user.prepareLogout) return Promise.reject()
return MessageBox.alert('请登录后重试', {
type: 'warning',
beforeClose: (action, instance, done) => {

@ -72,7 +72,7 @@
},
computed: {
...mapState('user', ['avatar', 'name', 'prepare_logout']),
...mapState('user', ['avatar', 'name', 'prepareLogout']),
...mapState('setting', ['sidebarCollapse', 'showBreadcrumb']),
@ -91,7 +91,7 @@
},
logout() {
if (this.prepare_logout) return
if (this.prepareLogout) return
elConfirm('确认退出?')
.then(() => this.$store.dispatch('user/logout'))
}

@ -38,7 +38,7 @@ export default {
data() {
return {
searchForm: {
id_fuzzy: null,
idFuzzy: null,
cname: null,
vname: null
},

@ -40,7 +40,7 @@ const mutations = {
map[item.url] = item.id
return map
}, {})
state.tree = createTree(data.filter(resource => resource.admin !== 1))
state.tree = createTree(data.filter(resource => resource.admin === false))
},
hasInitRoutes(state, sign) {
state.hasInitRoutes = sign
@ -114,7 +114,7 @@ function addFullPath(routes, basePath = '/') {
//获取经过权限控制后的路由
function getAuthorizedRoutes({resources, admin}) {
if (admin === 1) return finalAuthorityRoutes
if (admin === true) return finalAuthorityRoutes
if (!resources) return []
const arr = JSON.parse(JSON.stringify(finalAuthorityRoutes))
filter(arr, i => !needAuth(i) || i.fullPath in resources)

@ -74,7 +74,7 @@ function defaultEventBind(socket, {state, commit, dispatch}) {
function customEventBind(socket, {state, commit, dispatch, rootState}) {
socket.on('logout', msg => {
if (rootState.user.prepare_logout) return
if (rootState.user.prepareLogout) return
MessageBox.alert(msg || '你已被强制下线,请重新登陆', {
type: 'warning',
beforeClose: (action, instance, done) => {

@ -8,14 +8,15 @@ const user = getUser()
const state = {
//是否在退出的过程中,避免重复弹框
prepare_logout: '',
prepareLogout: '',
/*用户基本信息*/
id: emptyOrDefault(user.id, null),
name: emptyOrDefault(user.name),
role_name: emptyOrDefault(user.role_name),
roleName: emptyOrDefault(user.roleName),
deptName: emptyOrDefault(user.deptName),
avatar: emptyOrDefault(user.avatar),
admin: emptyOrDefault(user.admin),
admin: emptyOrDefault(user.admin, false),
token: emptyOrDefault(user.token),
resources: emptyOrDefault(user.resources, {})
}
@ -28,7 +29,7 @@ const actions = {
return new Promise((resolve, reject) => {
login({username: username.trim(), password})
.then(user => {
user.admin === 1 && (user.role_name = '超级管理员')
user.admin === true && (user.roleName = '超级管理员')
user.avatar = autoCompleteUrl(user.avatar)
commit('$all', user)
setUser(user)
@ -41,8 +42,8 @@ const actions = {
logout({commit, state, dispatch}) {
return new Promise((resolve, reject) => {
if (state.prepare_logout) return Promise.reject()
commit('prepare_logout', 'yes')
if (state.prepareLogout) return Promise.reject()
commit('prepareLogout', 'yes')
logout(state.token)
.then(() => {
commit('resource/hasInitRoutes', false, {root: true})
@ -57,7 +58,7 @@ const actions = {
window.location.reload()
})
.catch(error => reject(error))
.finally(() => commit('prepare_logout', ''))
.finally(() => commit('prepareLogout', ''))
})
},

@ -6,7 +6,7 @@ export function needAuth(route) {
}
export function auth(path) {
if (store.state.user.admin === 1) return true
if (store.state.user.admin === true) return true
const resourceMap = store.state.resource.dataMap
if (!(path in resourceMap)) return true

@ -173,69 +173,3 @@ export function delAllUrlParam() {
history.replaceState(null, null, [...href].join(''))
}
}
export function deepClone(data) {
if (data === null || data === undefined) {
return undefined
}
const dataType = getDataType(data)
if (dataType === "Date") {
let clonedDate = new Date()
clonedDate.setTime(data.getTime())
return clonedDate
}
if (dataType === "Object") {
if (isCyclic(data) === true) {
return data
}
let copiedObject = {}
Object.keys(data).forEach(key => copiedObject[key] = deepClone(data[key]))
return copiedObject
}
if (dataType === "Array") {
let copiedArray = []
for (let i = 0; i < data.length; i++) {
copiedArray.push(deepClone(data[i]))
}
return copiedArray
}
else return data
}
function getDataType(data) {
return Object.prototype.toString.call(data).slice(8, -1)
}
function isCyclic(data) {
let seenObjects = []
function detect(data) {
if (data && getDataType(data) === "Object") {
if (seenObjects.indexOf(data) !== -1) {
return true
}
seenObjects.push(data)
for (let key in data) {
if (data.hasOwnProperty(key) && detect(data[key])) {
return true
}
}
}
return false
}
return detect(data)
}

@ -6,12 +6,12 @@
</span>
<el-input
ref="username"
v-model="form.username"
:maxlength="20"
name="username"
placeholder="请输入用户名"
type="text"
ref="username"
v-model="form.username"
:maxlength="20"
name="username"
placeholder="请输入用户名"
type="text"
/>
</el-form-item>
@ -23,14 +23,14 @@
</el-tooltip>
<el-input
ref="password"
v-model="form.password"
:key="passwordType"
:type="passwordType"
:maxlength="20"
name="password"
placeholder="请输入密码"
@keyup.enter.native="login"
ref="password"
v-model="form.password"
:key="passwordType"
:type="passwordType"
:maxlength="20"
name="password"
placeholder="请输入密码"
@keyup.enter.native="login"
/>
<span @click="showPwd" class="show-pwd">
@ -38,19 +38,14 @@
</span>
</el-form-item>
<el-button
:loading="loading"
class="submit-btn"
type="primary"
@click="login"
>
{{loading ? '登 录 中...' : '登 录'}}
<el-button :loading="loading" class="submit-btn" type="primary" @click="login">
{{ loading ? '登 录 中...' : '登 录' }}
</el-button>
<div class="flex" style="margin-top: 20px">
<p class="other-ways">
其他方式登录
<svg-icon v-for="i in otherWays" :key="i" :icon="i" @click="thirdPartyLogin(i)"/>
<svg-icon v-for="i in otherWays" :key="i" :icon="i" @click="() => thirdPartyLogin(i)"/>
</p>
<el-button type="text" @click="register">注册账户</el-button>
@ -59,88 +54,85 @@
</template>
<script>
import md5 from "js-md5"
import {elSuccess} from "@/utils/message"
import {isEmpty} from "@/utils"
export default {
name: "LoginForm",
data() {
return {
loading: false,
form: {
username: '',
password: ''
},
rules: {
username: [{required: true, message: '请输入用户名', trigger: 'change'}],
password: [
{required: true, message: '请输入密码', trigger: 'change'},
{min: 6, max: 32, message: '请输入6-32位的密码', trigger: 'change'}
]
},
passwordType: 'password',
capsTooltip: false,
otherWays: ['qq']
}
},
methods: {
showPwd() {
this.passwordType = this.passwordType === 'password' ? '' : 'password'
this.$nextTick(() => this.$refs.password.focus())
import md5 from "js-md5"
import {elSuccess} from "@/utils/message"
import {isEmpty} from "@/utils"
export default {
name: "LoginForm",
data() {
return {
loading: false,
form: {
username: '',
password: ''
},
login() {
if (this.loading) return
this.$refs.form.validate(valid => {
if (!valid) return
this.loading = true
this.$puzzleVerify()
.then(() => this.$store.dispatch('user/login', {
...this.form,
password: md5(this.form.password)
}))
.then(() => this.success())
.catch(() => this.loading = false)
})
rules: {
username: [{required: true, message: '请输入用户名', trigger: 'change'}],
password: [{required: true, message: '请输入密码', trigger: 'change'}]
},
passwordType: 'password',
capsTooltip: false,
otherWays: ['qq']
}
},
register() {
!this.loading && this.$router.push('/register')
},
methods: {
showPwd() {
this.passwordType = this.passwordType === 'password' ? '' : 'password'
this.$nextTick(() => this.$refs.password.focus())
},
success() {
elSuccess('登陆成功')
const redirect = this.$route.query.redirect || '/'
//0.2s
setTimeout(() => this.$router.push(redirect), 200)
},
login() {
if (this.loading) return
this.$refs.form.validate(valid => {
if (!valid) return
this.loading = true
this.$puzzleVerify()
.then(() => this.$store.dispatch('user/login', {
...this.form,
password: md5(this.form.password)
}))
.then(() => this.success())
.catch(() => this.loading = false)
})
},
thirdPartyLogin(channel) {
this.$message.info('假装可以第三方登录')
},
register() {
!this.loading && this.$router.push('/register')
},
capsLockTip({keyCode}) {
if (keyCode === 20) this.capsTooltip = !this.capsTooltip
},
success() {
elSuccess('登陆成功')
const redirect = this.$route.query.redirect || '/'
//0.2s
setTimeout(() => this.$router.push(redirect), 200)
},
addCapsLockEvent() {
document.addEventListener('keyup', this.capsLockTip)
},
thirdPartyLogin(channel) {
this.$message.info('假装可以第三方登录')
},
removeEvent() {
document.removeEventListener('keyup', this.addCapsLockEvent)
}
capsLockTip({keyCode}) {
if (keyCode === 20) this.capsTooltip = !this.capsTooltip
},
mounted() {
this.addCapsLockEvent()
const key = isEmpty(this.form.username) ? 'username' : 'password'
this.$refs[key].focus()
addCapsLockEvent() {
document.addEventListener('keyup', this.capsLockTip)
},
this.$once('hook:beforeDestroy', this.removeEvent)
removeEvent() {
document.removeEventListener('keyup', this.addCapsLockEvent)
}
},
mounted() {
this.addCapsLockEvent()
const key = isEmpty(this.form.username) ? 'username' : 'password'
this.$refs[key].focus()
this.$once('hook:beforeDestroy', this.removeEvent)
}
}
</script>

@ -1,7 +1,7 @@
<template>
<div style="width: 300px">
<div class="tip-row">单选</div>
<tree-select v-model="single" :data="data" filterable :filter-method="filter"/>
<tree-select v-model="single" :data="data" filterable :filter-method="filter" @input="singleInput"/>
<div class="tip-row">多选</div>
<tree-select v-model="multiple" :data="data" multiple filterable :filter-method="filter"/>
@ -30,6 +30,10 @@
methods: {
filter(v, data) {
return data.label.includes(v)
},
singleInput(v,data) {
console.log(data)
}
}
}

@ -28,7 +28,7 @@
@selection-change="row._selection=$event"
>
<el-table-column
:selectable="p=>p.remain_num>0"
:selectable="p=>p.remainNum>0"
align="center"
type="selection"
width="60"
@ -36,7 +36,7 @@
<el-table-column align="center" label="#" type="index" width="80"/>
<el-table-column align="center" label="商品" prop="cname" show-overflow-tooltip/>
<el-table-column align="center" label="采购数量" prop="num"/>
<el-table-column align="center" label="未入库数量" prop="remain_num"/>
<el-table-column align="center" label="未入库数量" prop="remainNum"/>
</el-table>
<el-button
size="small"

@ -140,12 +140,12 @@
pid: this.form.id,
cid: i.cid,
cname: i.cname,
num: i.remain_num
num: i.remainNum
}))
},
changeInboundNum(nv, ov, row) {
let parentSub = this.parentSubList.find(i => i.cid === row.cid)
if (!parentSub || nv > parentSub.remain_num) {
if (!parentSub || nv > parentSub.remainNum) {
return elAlert(`${row.cname}的入库数量超出采购数量`, () => row.num = ov)
}
},
@ -171,7 +171,7 @@
if (!parentSub) {
return `${index}个商品不在采购订单中`
}
if (parentSub.remain_num < sub.num) {
if (parentSub.remainNum < sub.num) {
return `${index}个商品的入库数量超出采购数量`
}
index++

@ -2,10 +2,10 @@
<el-card v-loading="config.operating">
<search-form>
<search-form-item label="单 号:">
<el-input v-model="searchForm.id_fuzzy" clearable maxlength="50"/>
<el-input v-model="searchForm.idFuzzy" clearable maxlength="50"/>
</search-form-item>
<search-form-item label="采购订单:">
<el-input v-model="searchForm.pid_fuzzy" clearable maxlength="50"/>
<el-input v-model="searchForm.pidFuzzy" clearable maxlength="50"/>
</search-form-item>
<search-form-item label="创建人:">
<el-input v-model="searchForm.cname" clearable maxlength="50"/>
@ -113,7 +113,7 @@
baseUrl,
api: {search, del, getSubById},
searchForm: {
pid_fuzzy: null
pidFuzzy: null
},
excel: {
column: [

@ -59,11 +59,11 @@
<span v-else>{{row.price}}</span>
</template>
</el-table-column>
<el-table-column v-if="form.id" align="center" label="剩余未入库" prop="remain_num"/>
<el-table-column v-if="form.id" align="center" label="剩余未入库" prop="remainNum"/>
<el-table-column v-if="form.id" align="center" label="入库情况" width="120">
<template v-slot="{row}">
<span :class="{success:row.remain_num===0}" class="dot"/>
{{row.remain_num===0?'已全部入库':'未全部入库'}}
<span :class="{success:row.remainNum===0}" class="dot"/>
{{row.remainNum===0?'已全部入库':'未全部入库'}}
</template>
</el-table-column>
<el-table-column v-if="canSave" align="center" label="操作" width="140">

@ -2,7 +2,7 @@
<el-card v-loading="config.operating">
<search-form>
<search-form-item label="单 号:">
<el-input v-model="searchForm.id_fuzzy" clearable maxlength="50"/>
<el-input v-model="searchForm.idFuzzy" clearable maxlength="50"/>
</search-form-item>
<search-form-item label="供应商:">
<el-input v-model="searchForm.sname" clearable maxlength="50"/>
@ -80,7 +80,7 @@
<el-table-column align="center" label="商品" prop="cname" show-overflow-tooltip/>
<el-table-column align="center" label="采购数量" prop="num"/>
<el-table-column align="center" label="采购单价" prop="price"/>
<el-table-column align="center" label="未入库数量" prop="remain_num"/>
<el-table-column align="center" label="未入库数量" prop="remainNum"/>
</el-table>
</template>
</el-table-column>

@ -28,7 +28,7 @@
>
<el-table-column align="center" type="selection" reserve-selection width="80"/>
<el-table-column align="center" label="商品分类" prop="cname" show-overflow-tooltip/>
<el-table-column align="center" label="库存数量" prop="total_num" show-overflow-tooltip/>
<el-table-column align="center" label="库存数量" prop="totalNum" show-overflow-tooltip/>
</el-table>
<el-pagination

@ -13,11 +13,11 @@
</collapse-card>
<collapse-card header="基础信息">
<abstract-form-item label="客 户:" prop="customer_name">
<el-input v-if="canSave" :value="form.customer_name" readonly>
<abstract-form-item label="客 户:" prop="customerName">
<el-input v-if="canSave" :value="form.customerName" readonly>
<el-button slot="append" @click="customerDialog=true">选择</el-button>
</el-input>
<template v-else>{{form.customer_name}}</template>
<template v-else>{{form.customerName}}</template>
</abstract-form-item>
</collapse-card>
@ -49,11 +49,11 @@
<span v-else>{{row.price}}</span>
</template>
</el-table-column>
<el-table-column align="center" label="剩余未出库" prop="remain_num"/>
<el-table-column align="center" label="剩余未出库" prop="remainNum"/>
<el-table-column v-if="form.id" align="center" label="出库情况" width="120">
<template v-slot="{row}">
<span :class="{success:row.remain_num===0}" class="dot"/>
{{row.remain_num===0?'已全部出库':'未全部出库'}}
<span :class="{success:row.remainNum===0}" class="dot"/>
{{row.remainNum===0?'已全部出库':'未全部出库'}}
</template>
</el-table-column>
<div v-if="canSave" slot="append" class="table-add-btn">
@ -132,14 +132,14 @@
getById, add, update, commit, withdraw, pass, reject
},
form: {
customer_id: null,
customer_name: null,
customerId: null,
customerName: null,
finish: 0,
ftime: null,
total: 0
},
rules: {
customer_name: [{required: true, message: '客户不能为空', trigger: 'change'}]
customerName: [{required: true, message: '客户不能为空', trigger: 'change'}]
},
customerDialog: false,
stockDialog: false
@ -173,12 +173,12 @@
methods: {
selectCustomer(row) {
this.form.customer_id = row.id
this.form.customer_name = row.name
this.form.customerId = row.id
this.form.customerName = row.name
this.customerDialog = false
},
selectStock(stocks) {
this.form.data = stocks.map(i => ({cid: i.cid, cname: i.cname, num: i.total_num, price: 0}))
this.form.data = stocks.map(i => ({cid: i.cid, cname: i.cname, num: i.totalNum, price: 0}))
this.stockDialog = false
},
addSub() {
@ -187,7 +187,7 @@
cname: null,
num: 0,
price: 0,
remain_num: 0
remainNum: 0
})
},
delSub(row, index) {

@ -2,10 +2,10 @@
<el-card v-loading="config.operating">
<search-form>
<search-form-item label="单 号:">
<el-input v-model="searchForm.id_fuzzy" clearable maxlength="50"/>
<el-input v-model="searchForm.idFuzzy" clearable maxlength="50"/>
</search-form-item>
<search-form-item label="客 户:">
<el-input v-model="searchForm.customer_name" clearable maxlength="50"/>
<el-input v-model="searchForm.customerName" clearable maxlength="50"/>
</search-form-item>
<search-form-item label="创建人:">
<el-input v-model="searchForm.cname" clearable maxlength="50"/>
@ -83,13 +83,13 @@
<el-table-column align="center" label="商品" prop="cname" show-overflow-tooltip/>
<el-table-column align="center" label="销售数量" prop="num"/>
<el-table-column align="center" label="销售单价" prop="price"/>
<el-table-column align="center" label="未出库数量" prop="remain_num"/>
<el-table-column align="center" label="未出库数量" prop="remainNum"/>
</el-table>
</template>
</el-table-column>
<el-table-column align="center" label="#" type="index" width="80"/>
<el-table-column align="center" label="单 号" prop="id" width="160" show-overflow-tooltip/>
<el-table-column align="center" label="客 户" prop="customer_name" show-overflow-tooltip/>
<el-table-column align="center" label="客 户" prop="customerName" show-overflow-tooltip/>
<el-table-column align="center" label="创建人" prop="cname" show-overflow-tooltip/>
<el-table-column align="center" label="创建时间" width="150" show-overflow-tooltip>
<template v-slot="{row}">{{row.ctime | timestamp2Date}}</template>
@ -144,7 +144,7 @@
return {
baseUrl,
searchForm: {
customer_name: null
customerName: null
},
temp: {
finish: [],
@ -154,7 +154,7 @@
excel: {
column: [
{header: '序号', prop: 'id'},
{header: '客户', prop: 'customer_name', width: 30},
{header: '客户', prop: 'customerName', width: 30},
{header: '创建人', prop: 'cname'},
{header: '创建时间', prop: 'ctime'},
{header: '审核人', prop: 'vname'},

@ -28,7 +28,7 @@
@selection-change="row._selection=$event"
>
<el-table-column
:selectable="p=>p.remain_num>0"
:selectable="p=>p.remainNum>0"
align="center"
type="selection"
width="60"
@ -36,7 +36,7 @@
<el-table-column align="center" label="#" type="index" width="80"/>
<el-table-column align="center" label="商品" prop="cname" show-overflow-tooltip/>
<el-table-column align="center" label="销售数量" prop="num"/>
<el-table-column align="center" label="未出库数量" prop="remain_num"/>
<el-table-column align="center" label="未出库数量" prop="remainNum"/>
</el-table>
<el-button
size="small"

@ -52,7 +52,7 @@
<el-table-column align="center" label="#" type="index" width="80"/>
<el-table-column align="center" label="商 品" prop="cname"/>
<el-table-column align="center" label="出库数量" prop="num" width="150"/>
<el-table-column align="center" label="订单剩余未出库数量" prop="remain_num" width="250"/>
<el-table-column align="center" label="订单剩余未出库数量" prop="remainNum" width="250"/>
</abstract-table>
</collapse-card>
@ -203,19 +203,19 @@
selectStock(stocks) {
let res = []
let outboundNum = 0
let remain_num = this.row.remain_num
let remainNum = this.row.remainNum
for (let stock of stocks) {
if (remain_num <= 0) break
if (remainNum <= 0) break
let item = {
...stock,
sid: stock.id,
pid: this.form.id,
max_num: stock.num,
num: stock.num > remain_num ? remain_num : stock.num
num: stock.num > remainNum ? remainNum : stock.num
}
outboundNum = plus(outboundNum, item.num)
res.push(item)
remain_num -= stock.num
remainNum -= stock.num
}
this.row.num = outboundNum
this.row.data = res
@ -243,8 +243,8 @@
if (!outboundNum) return
if (outboundNum > displayTableRow.remain_num) {
let correctNum = sub(displayTableRow.remain_num, sub(outboundNum, nv))
if (outboundNum > displayTableRow.remainNum) {
let correctNum = sub(displayTableRow.remainNum, sub(outboundNum, nv))
if (correctNum < 0) correctNum = 0
else if (correctNum > row.max_num) correctNum = row.max_num
return elAlert(`${displayTableRow.cname}的出库数量超出销售数量`, () => row.num = ov)

@ -2,10 +2,10 @@
<el-card v-loading="config.operating">
<search-form>
<search-form-item label="单 号:">
<el-input v-model="searchForm.id_fuzzy" clearable maxlength="50"/>
<el-input v-model="searchForm.idFuzzy" clearable maxlength="50"/>
</search-form-item>
<search-form-item label="销售订单:">
<el-input v-model="searchForm.pid_fuzzy" clearable maxlength="50"/>
<el-input v-model="searchForm.pidFuzzy" clearable maxlength="50"/>
</search-form-item>
<search-form-item label="创建人:">
<el-input v-model="searchForm.cname" clearable maxlength="50"/>
@ -117,7 +117,7 @@
baseUrl,
api: {search, del, getSubById},
searchForm: {
pid_fuzzy: null
pidFuzzy: null
},
excel: {
column: [

@ -47,8 +47,8 @@
<el-table-column align="center" label="商品分类">
<el-link slot-scope="{row}" @click="more(row)">{{row.cname}}</el-link>
</el-table-column>
<el-table-column align="center" label="库存数量" prop="total_num" show-overflow-tooltip/>
<el-table-column align="center" label="总 值" prop="total_price" show-overflow-tooltip/>
<el-table-column align="center" label="库存数量" prop="totalNum" show-overflow-tooltip/>
<el-table-column align="center" label="总 值" prop="totalPrice" show-overflow-tooltip/>
</abstract-table>
<el-pagination
@ -104,14 +104,14 @@
columns: [
{header: '序号', prop: 'id', width: 20, merge: true},
{header: '分类名称', prop: 'cname', width: 20, merge: true},
{header: '库存总数', prop: 'total_num', width: 20, merge: true},
{header: '库存总值', prop: 'total_price', width: 20, merge: true},
{header: '库存总数', prop: 'totalNum', width: 20, merge: true},
{header: '库存总值', prop: 'totalPrice', width: 20, merge: true},
{header: '采购订单号', prop: 'cgddid', width: 20, merge: true},
{header: '采购单价', prop: 'cg_price', width: 20, merge: true},
{header: '采购数量', prop: 'cg_num', width: 20, merge: true},
{header: '采购单价', prop: 'cgPrice', width: 20, merge: true},
{header: '采购数量', prop: 'cgNum', width: 20, merge: true},
{header: '采购入库单号', prop: 'cgrkid', width: 20},
{header: '入库时间', prop: 'ctime', width: 20},
{header: '入库数量', prop: 'rk_num', width: 20},
{header: '入库数量', prop: 'rkNum', width: 20},
],
merge: {primaryKey: 'cid', orderKey: 'id'}
}
@ -132,7 +132,7 @@
summary({data}) {
let sum = ['合计', '', '', 0]
data.forEach(i => {
sum[3] = plus(sum[3], i.total_price)
sum[3] = plus(sum[3], i.totalPrice)
})
return sum
},

@ -1,125 +1,127 @@
<template>
<div :style="orgTreeStyle" class="org-tree-wrapper" @mousedown="mousedownView">
<org-tree :data="data" :horizontal="horizontal" collapsable expand-all>
<org-tree :data="data" :horizontal="horizontal" collapsable expand-all @on-node-click="nodeClick">
<div
slot-scope="data"
:class="{'has-children-label':data.children && data.children.length}"
class="custom-org-node"
@contextmenu.prevent="$emit('node-contextmenu',$event,data)"
slot-scope="data"
:class="{'has-children-label':data.children && data.children.length}"
class="custom-org-node"
@contextmenu.prevent="e => $emit('node-contextmenu',e,data)"
>
{{data.label}}
{{ data.label }}
</div>
</org-tree>
</div>
</template>
<script>
import OrgTree from '@/components/OrgTree'
import OrgTree from '@/components/OrgTree'
export default {
name: "OrgTreeView",
export default {
name: "OrgTreeView",
components: {OrgTree},
components: {OrgTree},
props: {
data: {type: Object, default: () => ({})},
horizontal: Boolean,
collapsable: Boolean,
zoom: {type: Number, default: 1},
},
props: {
data: {type: Object, default: () => ({})},
horizontal: Boolean,
collapsable: Boolean,
zoom: {type: Number, default: 1},
},
data() {
return {
orgTreeOffsetLeft: 0,
orgTreeOffsetTop: 0,
initPageX: 0,
initPageY: 0,
oldMarginLeft: 0,
oldMarginTop: 0,
canMove: false,
expandAll: true
}
},
data() {
computed: {
orgTreeStyle() {
return {
orgTreeOffsetLeft: 0,
orgTreeOffsetTop: 0,
initPageX: 0,
initPageY: 0,
oldMarginLeft: 0,
oldMarginTop: 0,
canMove: false,
expandAll: true
transform: `translate(-50%, -50%) scale(${this.zoom}, ${this.zoom})`,
marginLeft: `${this.orgTreeOffsetLeft}px`,
marginTop: `${this.orgTreeOffsetTop}px`
}
},
}
},
computed: {
orgTreeStyle() {
return {
transform: `translate(-50%, -50%) scale(${this.zoom}, ${this.zoom})`,
marginLeft: `${this.orgTreeOffsetLeft}px`,
marginTop: `${this.orgTreeOffsetTop}px`
}
}
methods: {
nodeClick(e, data, expand) {
expand()
},
methods: {
mousedownView(event) {
this.canMove = true
this.initPageX = event.pageX
this.initPageY = event.pageY
this.oldMarginLeft = this.orgTreeOffsetLeft
this.oldMarginTop = this.orgTreeOffsetTop
document.addEventListener('mousemove', this.mousemoveView)
document.addEventListener('mouseup', this.mouseupView)
},
mousemoveView(event) {
if (!this.canMove) return
const {pageX, pageY} = event
this.orgTreeOffsetLeft = this.oldMarginLeft + pageX - this.initPageX
this.orgTreeOffsetTop = this.oldMarginTop + pageY - this.initPageY
},
mouseupView() {
this.canMove = false
document.removeEventListener('mousemove', this.mousemoveView)
document.removeEventListener('mouseup', this.mouseupView)
}
mousedownView(event) {
this.canMove = true
this.initPageX = event.pageX
this.initPageY = event.pageY
this.oldMarginLeft = this.orgTreeOffsetLeft
this.oldMarginTop = this.orgTreeOffsetTop
document.addEventListener('mousemove', this.mousemoveView)
document.addEventListener('mouseup', this.mouseupView)
},
mousemoveView(event) {
if (!this.canMove) return
const {pageX, pageY} = event
this.orgTreeOffsetLeft = this.oldMarginLeft + pageX - this.initPageX
this.orgTreeOffsetTop = this.oldMarginTop + pageY - this.initPageY
},
mouseupView() {
this.canMove = false
document.removeEventListener('mousemove', this.mousemoveView)
document.removeEventListener('mouseup', this.mouseupView)
}
}
}
</script>
<style lang="scss">
.org-tree-wrapper {
cursor: move;
display: inline-block;
position: absolute;
left: 50%;
top: 50%;
transition: transform 0.2s ease-out;
z-index: 2;
.org-tree-wrapper {
cursor: move;
display: inline-block;
position: absolute;
left: 50%;
top: 50%;
transition: transform 0.2s ease-out;
z-index: 2;
.org-tree-node-label {
.org-tree-node-label-inner {
padding: 0;
.org-tree-node-label {
.org-tree-node-label-inner {
padding: 0;
.custom-org-node {
padding: 14px 41px;
background: #738699;
user-select: none;
word-wrap: normal;
white-space: nowrap;
border-radius: 4px;
color: #ffffff;
font-size: 14px;
font-weight: 500;
line-height: 20px;
transition: background 0.1s ease-in;
cursor: default;
.custom-org-node {
padding: 14px 41px;
background: #738699;
user-select: none;
word-wrap: normal;
white-space: nowrap;
border-radius: 4px;
color: #ffffff;
font-size: 14px;
font-weight: 500;
line-height: 20px;
transition: background 0.1s ease-in;
cursor: default;
&:hover {
background: #5d6c7b;
transition: background 0.1s ease-in;
}
&:hover {
background: #5d6c7b;
transition: background 0.1s ease-in;
}
&.has-children-label {
cursor: pointer;
}
&.has-children-label {
cursor: pointer;
}
}
}
}
.horizontal .custom-org-node {
padding: 14px 19px;
}
.horizontal .custom-org-node {
padding: 14px 19px;
}
}
</style>

@ -1,20 +1,14 @@
<template>
<el-card v-loading="loading" class="card-container max-view-height">
<div class="tip-row">
<a href="https://admin.iviewui.com/components/org_tree_page" target="_blank">
组织结构树演示详情见iview-admin
</a>
</div>
<div class="department-outer">
<div class="view-box">
<org-tree-view
:data="data"
:horizontal="horizontal"
:zoom="realZoom"
collapsable
@node-contextmenu="openMenu"
@wheel.native.prevent="wheel"
:data="data"
:horizontal="horizontal"
:zoom="realZoom"
collapsable
@node-contextmenu="openMenu"
@wheel.native.prevent="wheel"
/>
</div>
@ -25,13 +19,13 @@
</el-radio-group>
<el-button
circle
icon="el-icon-refresh"
size="small"
style="margin-right: 20px"
title="刷新"
type="info"
@click="search"
circle
icon="el-icon-refresh"
size="small"
style="margin-right: 20px"
title="刷新"
type="info"
@click="search"
/>
<zoom-control v-model="zoom" :max="200" :min="20"/>
@ -39,14 +33,14 @@
</div>
<context-menu
v-if="canAdd||canUpdate||canDel"
v-model="contextmenu.show"
:left="contextmenu.left"
:top="contextmenu.top"
v-if="canAdd || canUpdate || canDel"
v-model="contextmenu.show"
:left="contextmenu.left"
:top="contextmenu.top"
>
<context-menu-item v-show="canAdd" @click="add">添加部门</context-menu-item>
<context-menu-item v-show="canUpdate" @click="update">编辑部门</context-menu-item>
<context-menu-item v-show="canDel" @click="del">删除部门</context-menu-item>
<context-menu-item v-if="canAdd" @click="add">添加部门</context-menu-item>
<context-menu-item v-if="canUpdate" @click="update">编辑部门</context-menu-item>
<context-menu-item v-if="canDel" @click="del">删除部门</context-menu-item>
</context-menu>
<edit-dialog v-model="editDialog" :data="currentNode" :type="type" @success="search"/>
@ -54,159 +48,156 @@
</template>
<script>
import OrgTreeView from "./components/OrgTreeView"
import ZoomControl from '@/components/ZoomControl'
import ContextMenu from "@/components/ContextMenu"
import ContextMenuItem from "@/components/ContextMenu/ContextMenuItem"
import EditDialog from "./components/EditDialog"
import {auth} from "@/utils/auth"
import {createTreeByWorker} from "@/utils/tree"
import {isEmpty} from '@/utils'
import {elConfirm, elError, elSuccess} from "@/utils/message"
import {delDepartment, getAllDepartments} from "@/api/system/department"
const baseUrl = '/system/department'
export default {
name: "departmentManagement",
components: {OrgTreeView, ZoomControl, ContextMenu, ContextMenuItem, EditDialog},
data() {
return {
loading: false,
data: {},
zoom: 100,
horizontal: false,
currentNode: null,
type: 'add',
editDialog: false,
contextmenu: {
show: false,
left: 0,
top: 0
}
/**
* 详情见iview-admin
* https://admin.iviewui.com/components/org_tree_page
*/
import ContextMenu from "@/components/ContextMenu"
import ContextMenuItem from "@/components/ContextMenu/ContextMenuItem"
import EditDialog from "./components/EditDialog"
import OrgTreeView from "./components/OrgTreeView"
import ZoomControl from '@/components/ZoomControl'
import {baseUrl, delDepartment, getDepartments} from "@/api/system/department"
import {isEmpty} from '@/utils'
import {auth} from "@/utils/auth"
import {elConfirm, elError, elSuccess} from "@/utils/message"
import {createTreeByWorker} from "@/utils/tree"
export default {
name: "departmentManagement",
components: {ContextMenu, ContextMenuItem, EditDialog, OrgTreeView, ZoomControl},
data() {
return {
loading: false,
data: {},
zoom: 100,
horizontal: false,
currentNode: null,
type: 'add',
editDialog: false,
contextmenu: {
show: false,
left: 0,
top: 0
}
}
},
computed: {
realZoom() {
return this.zoom / 100
},
canAdd() {
return auth(`${baseUrl}/add`)
},
canUpdate() {
return auth(`${baseUrl}/update`)
},
canDel() {
return auth(`${baseUrl}/del`)
},
},
methods: {
//
completeDepartment(arr) {
arr.forEach(i => {
i.label = i.name
i.parentName = i.fullname
i.children && this.completeDepartment(i.children)
})
},
computed: {
realZoom() {
return this.zoom / 100
},
//
openMenu(e, obj) {
e.preventDefault()
this.currentNode = obj
const {left, top} = this.$el.getBoundingClientRect()
this.contextmenu.left = e.clientX - left + 15
this.contextmenu.top = e.clientY - top
this.contextmenu.show = true
},
canAdd() {
return auth(baseUrl + '/add')
},
//
wheel(e) {
const eventDelta = e.wheelDelta || -(e.detail || 0)
const zoom = this.zoom + eventDelta / 12 * 2
if (zoom >= 20 && zoom <= 200) {
this.zoom = zoom
}
},
canUpdate() {
return auth(baseUrl + '/update')
},
add() {
this.type = 'add'
this.editDialog = true
},
canDel() {
return auth(baseUrl + '/del')
},
update() {
this.type = 'edit'
this.editDialog = true
},
methods: {
openMenu(e, obj) {
e.preventDefault()
this.currentNode = obj
const {left, top} = this.$el.getBoundingClientRect()
this.contextmenu.left = e.clientX - left + 15
this.contextmenu.top = e.clientY - top
this.contextmenu.show = true
},
wheel(e) {
const eventDelta = e.wheelDelta || -(e.detail || 0)
const zoom = this.zoom + eventDelta / 12 * 2
if (zoom >= 20 && zoom <= 200) {
this.zoom = zoom
}
},
add() {
this.type = 'add'
this.editDialog = true
},
update() {
this.type = 'edit'
this.editDialog = true
},
del() {
if (this.loading || !this.currentNode) return
if (this.currentNode.id === 1) return elError('根节点不能删除')
if (this.currentNode.children && this.currentNode.children.length > 0) {
return elError('请先删除子部门')
}
elConfirm(`确定删除部门【${this.currentNode.name}】?`)
.then(() => {
this.loading = true
return delDepartment(this.currentNode)
})
.then(({msg}) => {
elSuccess(msg)
this.search()
})
.finally(() => this.loading = false)
},
search() {
this.currentNode = null
this.loading = true
getAllDepartments()
.then(data => createTreeByWorker(data))
.then(data => {
completeDepartment(data)
this.data = data[0]
})
.finally(() => this.loading = false)
del() {
if (this.loading || !this.currentNode) return
if (this.currentNode.id === 1) return elError('根节点不能删除')
if (this.currentNode.children && this.currentNode.children.length > 0) {
return elError('请先删除子部门')
}
elConfirm(`确定删除部门【${this.currentNode.name}】?`)
.then(() => {
this.loading = true
return delDepartment(this.currentNode)
})
.then(({msg}) => {
elSuccess(msg)
this.search()
})
.finally(() => this.loading = false)
},
mounted() {
this.search()
search() {
this.currentNode = null
this.loading = true
getDepartments()
.then(data => createTreeByWorker(data))
.then(data => {
this.completeDepartment(data)
this.data = data[0]
})
.finally(() => this.loading = false)
}
}
//
function completeDepartment(arr, name) {
arr.forEach(i => {
i.label = i.name
i.parentName = name
},
if (isEmpty(name)) i.fullname = i.name
else i.fullname = name + ' > ' + i.name
if (i.children) completeDepartment(i.children, i.fullname)
})
mounted() {
this.search()
}
}
</script>
<style lang="scss">
.department-outer {
width: 100%;
height: 100%;
overflow: hidden;
.tooltip-box {
position: absolute;
right: 30px;
bottom: 30px;
z-index: 2;
display: flex;
justify-content: flex-end;
align-items: center;
}
.department-outer {
width: 100%;
height: 100%;
overflow: hidden;
.tooltip-box {
position: absolute;
right: 30px;
bottom: 30px;
z-index: 2;
display: flex;
justify-content: flex-end;
align-items: center;
}
.view-box {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.view-box {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
}
</style>

@ -14,18 +14,18 @@
<el-form-item label="访问路径:" prop="url">
<el-input :value="form.url" readonly/>
</el-form-item>
<el-form-item label="总频率:" prop="total_rate">
<el-form-item label="总频率:" prop="totalRate">
<el-input-number
v-model="form.total_rate"
v-model="form.totalRate"
controls-position="right"
:min="1"
:step="1"
step-strictly size="small"
/>
</el-form-item>
<el-form-item label="单个IP频率:" prop="ip_rate">
<el-form-item label="单个IP频率:" prop="ipRate">
<el-input-number
v-model="form.ip_rate"
v-model="form.ipRate"
controls-position="right"
:min="1"
:step="1"
@ -66,12 +66,12 @@
id: null,
fullName: null,
url: null,
total_rate: 0,
ip_rate: 0
totalRate: 0,
ipRate: 0
},
rules: {
total_rate: [{required: true, message: '总频率不能为空', trigger: 'change'}],
ip_rate: [{required: true, message: '单个ip的频率不能为空', trigger: 'change'}],
totalRate: [{required: true, message: '总频率不能为空', trigger: 'change'}],
ipRate: [{required: true, message: '单个ip的频率不能为空', trigger: 'change'}],
}
}
},

@ -9,8 +9,8 @@
<abstract-table :data="tableData" @row-click="rowClick">
<el-table-column label="名称" prop="fullName" show-overflow-tooltip/>
<el-table-column label="访问路径" prop="url" show-overflow-tooltip/>
<el-table-column label="总频率" prop="total_rate" show-overflow-tooltip/>
<el-table-column label="单个IP频率" prop="ip_rate" show-overflow-tooltip/>
<el-table-column label="总频率" prop="totalRate" show-overflow-tooltip/>
<el-table-column label="单个IP频率" prop="ipRate" show-overflow-tooltip/>
</abstract-table>
</el-row>

@ -0,0 +1,190 @@
<template>
<form-dialog :loading="loading" :title="title" :value="value" width="40%" @close="cancel" @open="open">
<el-form
ref="form"
:model="form"
:rules="rules"
label-position="right"
label-width="100px"
status-icon
>
<el-form-item label="名 称:" prop="name">
<el-input v-model="form.name" maxlength="20"/>
</el-form-item>
<el-form-item label="状 态:" prop="status">
<el-radio-group v-model="form.status">
<el-radio :label="1">启用</el-radio>
<el-radio :label="0">禁用</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="数据范围:" prop="scope">
<el-select v-model="form.scope">
<el-option :value="1" label="全部"/>
<el-option :value="2" label="本部门"/>
<el-option :value="3" label="指定部门"/>
</el-select>
</el-form-item>
<el-form-item v-if="form.scope === 3" label="指定部门:">
<el-tree
ref="dept"
:data="departments"
:props="{label:'name'}"
:default-checked-keys="form.departmentId"
node-key="id"
show-checkbox
/>
</el-form-item>
<el-form-item label="权 限:">
<el-tree
ref="perm"
:data="resource"
:props="{label:'name'}"
node-key="id"
show-checkbox
/>
</el-form-item>
</el-form>
<template v-slot:footer>
<el-dropdown placement="top" style="float: left" @command="treeCommand">
<el-button plain size="small">
权限树操作
<i class="el-icon-arrow-up el-icon--right"/>
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item :command="{action:'expand',level:0}">展开全部</el-dropdown-item>
<el-dropdown-item :command="{action:'collapse',level:0}">收起全部</el-dropdown-item>
<el-dropdown-item :command="{action:'expand',level:1}">仅展开一级</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-button plain size="small" @click="closeDialog"> </el-button>
<el-button size="small" type="primary" @click="confirm"> </el-button>
</template>
</form-dialog>
</template>
<script>
import dialogMixin from "@/mixins/dialogMixin"
import FormDialog from '@/components/FormDialog'
import {getDepartments} from "@/api/system/department"
import {addRole, updateRole} from "@/api/system/role"
import {isEmpty, mergeObj} from '@/utils'
import {createTreeByWorker, elTreeControl} from '@/utils/tree'
export default {
name: "EditDialog",
mixins: [dialogMixin],
components: {FormDialog},
props: {
value: {type: Boolean, default: false},
type: {type: String, default: 'add'},
data: {type: Object, default: () => ({})},
},
data() {
return {
loading: false,
form: {
id: null,
name: null,
status: 1,
scope: 1,
departmentId: [],
resourceId: []
},
rules: {
name: [{required: true, message: '角色名称不能为空', trigger: 'change'}],
status: [{required: true, message: '角色状态不能为空', trigger: 'change'}],
scope: [{required: true, message: '数据范围不能为空', trigger: 'change'}],
},
departments: []
}
},
computed: {
title() {
if (isEmpty(this.type)) return ''
switch (this.type) {
case 'add':
return '添加角色'
case 'edit':
return '编辑角色'
}
},
confirmMessage() {
switch (this.type) {
case 'add':
return `确认添加新的角色【${this.form.name}】?`
case 'edit':
return `确认提交对【${this.data.name}】作出的修改?`
default:
return ''
}
},
resource() {
return this.$store.state.resource.tree
}
},
methods: {
treeCommand({action, level}) {
elTreeControl(this.$refs.perm, action, level)
},
open() {
if (this.type !== 'edit') return
this.$nextTick(() => {
mergeObj(this.form, this.data)
this.$refs.perm.setCheckedKeys(this.form.resourceId)
})
},
clearForm() {
this.form.id = null
this.form.name = null
this.form.status = 1
this.form.scope = 1
this.form.departmentId = []
this.form.resourceId = []
this.$refs.perm.setCheckedKeys([])
this.$refs.dept && this.$refs.dept.setCheckedKeys([])
this.$nextTick(() => this.$refs.form.clearValidate())
},
cancel() {
this.closeDialog()
this.clearForm()
},
confirm() {
if (this.loading) return
this.$refs.form.validate(v => {
if (!v) return
this.loading = true
const obj = {
...this.form,
departmentId: this.$refs.dept ? this.$refs.dept.getCheckedKeys().join(',') : [],
resourceId: this.$refs.perm.getCheckedKeys().join(',')
}
const promise = this.type === 'add' ? addRole(obj) : updateRole(obj)
promise
.then(({msg}) => this.$emit('success', msg))
.finally(() => this.loading = false)
})
}
},
mounted() {
getDepartments(false)
.then(data => createTreeByWorker(data))
.then(data => {
this.departments = data
})
}
}
</script>

@ -1,158 +0,0 @@
<template>
<form-dialog :loading="loading" :title="title" :value="value" @close="cancel" @open="open">
<el-form
ref="form"
:model="form"
:rules="rules"
label-position="right"
label-width="75px"
status-icon
>
<el-form-item label="名 称:" prop="name">
<el-input v-model="form.name" maxlength="20"/>
</el-form-item>
<el-form-item label="状 态:" prop="status">
<el-radio-group v-model="form.status">
<el-radio :label="1">启用</el-radio>
<el-radio :label="0">禁用</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="权 限:">
<el-tree
ref="tree"
:data="resource"
:expand-on-click-node="false"
:props="{label:'name'}"
node-key="id"
show-checkbox
/>
</el-form-item>
</el-form>
<template v-slot:footer>
<el-dropdown placement="top" style="float: left" @command="treeCommand">
<el-button plain size="small">
树形操作
<i class="el-icon-arrow-up el-icon--right"/>
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item :command="{action:'expand',level:0}">展开全部</el-dropdown-item>
<el-dropdown-item :command="{action:'collapse',level:0}">收起全部</el-dropdown-item>
<el-dropdown-item :command="{action:'expand',level:1}">仅展开一级</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-button plain size="small" @click="closeDialog"> </el-button>
<el-button size="small" type="primary" @click="confirm"> </el-button>
</template>
</form-dialog>
</template>
<script>
import FormDialog from '@/components/FormDialog'
import dialogMixin from "@/mixins/dialogMixin"
import {addRole, updateRole} from "@/api/system/role"
import {isEmpty, mergeObj} from '@/utils'
import {elTreeControl} from '@/utils/tree'
export default {
name: "EditDialog",
mixins: [dialogMixin],
components: {FormDialog},
props: {
value: {type: Boolean, default: false},
type: {type: String, default: 'add'},
data: {type: Object, default: () => ({})},
},
data() {
return {
loading: false,
form: {
id: null,
name: null,
status: 1,
resource_id: []
},
rules: {
name: [{required: true, message: '角色名称不能为空', trigger: 'change'}],
status: [{required: true, message: '角色状态不能为空', trigger: 'change'}],
}
}
},
computed: {
title() {
if (isEmpty(this.type)) return ''
switch (this.type) {
case 'add':
return '添加角色'
case 'edit':
return '编辑角色'
}
},
confirmMessage() {
switch (this.type) {
case 'add':
return `确认添加新的角色【${this.form.name}】?`
case 'edit':
return `确认提交对【${this.data.name}】作出的修改?`
default:
return ''
}
},
resource() {
return this.$store.state.resource.tree
}
},
methods: {
treeCommand({action, level}) {
elTreeControl(this.$refs.tree, action, level)
},
open() {
this.$nextTick(() => {
if (this.type === 'edit') {
mergeObj(this.form, this.data)
this.$refs.tree.setCheckedKeys(this.form.resource_id)
}
})
},
clearForm() {
this.form.id = null
this.form.name = null
this.form.status = 1
this.form.resource_id = []
this.$refs.tree.setCheckedKeys([])
this.$nextTick(() => this.$refs.form.clearValidate())
},
cancel() {
this.closeDialog()
this.clearForm()
},
confirm() {
if (this.loading) return
this.$refs.form.validate(v => {
if (!v) return
this.loading = true
let obj = {
...this.form,
resource_id: this.$refs.tree.getCheckedKeys().join(',')
}
let promise = this.type === 'add' ? addRole(obj) : updateRole(obj)
promise
.then(({msg}) => this.$emit('success', msg))
.finally(() => this.loading = false)
})
},
}
}
</script>

@ -1,59 +0,0 @@
<script type="text/jsx">
import {createTree} from "@/utils/tree"
export default {
name: "RoleResource",
functional: true,
props: {
ids: {type: Array, default: () => []},
map: {type: Object, default: () => ({})}
},
render(h, context) {
const {ids, map} = context.props
let tree = []
for (let id of ids) map[id] && tree.push(map[id])
tree = createTree(tree)
const nodes = []
const fun = (arr, left) => {
for (let r of arr) {
nodes.push(
<span style={'margin-left:' + left + 'px'}>
<svg-icon icon="check"/>
{r.name}
</span>
)
if (r.children.length > 0) {
if (r.children[0].children.length > 0) nodes.push(<p/>)
fun(r.children, left + 20)
}
}
nodes.push(<p/>)
}
fun(tree, 0)
return nodes
}
}
</script>
<style lang="scss" scoped>
@import "~@/assets/styles/variables.scss";
span {
.svg-icon {
color: $--color-primary
}
}
span + span {
margin-left: 10px;
}
</style>

@ -15,11 +15,11 @@
</search-form-item>
<search-form-item label="创建时间:">
<el-date-picker
v-model="temp.ctime"
format="yyyy-MM-dd"
range-separator="-"
type="daterange"
value-format="timestamp"
v-model="temp.ctime"
format="yyyy-MM-dd"
range-separator="-"
type="daterange"
value-format="timestamp"
/>
</search-form-item>
</search-form>
@ -33,30 +33,30 @@
<el-row v-loading="config.loading" class="table-container">
<abstract-table
:data="tableData"
@row-click="rowClick"
:data="tableData"
@row-click="rowClick"
>
<el-table-column align="center" label="#" type="index" width="80"/>
<el-table-column align="center" label="角色名" prop="name" show-overflow-tooltip/>
<el-table-column align="center" label="创建人" prop="cname" show-overflow-tooltip/>
<el-table-column align="center" label="创建时间" width="150" show-overflow-tooltip>
<template v-slot="{row}">{{row.ctime | timestamp2Date}}</template>
<template v-slot="{row}">{{ row.ctime | timestamp2Date }}</template>
</el-table-column>
<el-table-column align="center" label="状 态" width="120">
<template v-slot="{row}">
<span :class="row.status===1?'success':'error'" class="dot"/>
<span>{{row.status===1?'启用':'禁用'}}</span>
<span>{{ row.status === 1 ? '启用' : '禁用' }}</span>
</template>
</el-table-column>
</abstract-table>
<el-pagination
background
:current-page="searchForm.page"
:page-size="searchForm.pageSize"
:total="searchForm.total"
layout="total, prev, pager, next, jumper"
@current-change="pageChange"
background
:current-page="searchForm.page"
:page-size="searchForm.pageSize"
:total="searchForm.total"
layout="total, prev, pager, next, jumper"
@current-change="pageChange"
/>
</el-row>
@ -65,110 +65,113 @@
</template>
<script>
import SearchForm from "@/components/SearchForm"
import SearchFormItem from "@/components/SearchForm/SearchFormItem"
import EditDialog from './components/EditDialog'
import {delRole, searchRoles} from "@/api/system/role"
import {isEmpty} from '@/utils'
import {elConfirm, elError, elSuccess} from "@/utils/message"
import {auth} from "@/utils/auth"
import tableMixin from '@/mixins/tablePageMixin'
const baseUrl = '/system/role'
export default {
name: "roleManagement",
mixins: [tableMixin],
import tableMixin from '@/mixins/tablePageMixin'
import EditDialog from './EditDialog'
import SearchForm from "@/components/SearchForm"
import SearchFormItem from "@/components/SearchForm/SearchFormItem"
import {baseUrl, delRole, searchRoles} from "@/api/system/role"
import {isEmpty} from '@/utils'
import {auth} from "@/utils/auth"
import {elConfirm, elError, elSuccess} from "@/utils/message"
export default {
name: "roleManagement",
mixins: [tableMixin],
components: {EditDialog, SearchForm, SearchFormItem},
data() {
return {
searchForm: {
name: '',
cname: '',
status: null
},
temp: {
ctime: []
},
editDialog: false
}
},
components: {SearchForm, SearchFormItem, EditDialog},
computed: {
canAdd() {
return auth(`${baseUrl}/add`)
},
canUpdate() {
return auth(`${baseUrl}/update`)
},
canDel() {
return auth(`${baseUrl}/del`)
}
},
data() {
methods: {
mergeSearchForm() {
return {
searchForm: {
name: '',
cname: '',
status: null
},
temp: {
ctime: []
},
editDialog: false
...this.searchForm,
startTime: this.temp.ctime ? this.temp.ctime[0] : null,
endTime: this.temp.ctime ? this.temp.ctime[1] + 86400000 : null,
}
},
computed: {
canAdd() {
return auth(baseUrl + '/add')
},
canUpdate() {
return auth(baseUrl + '/update')
},
canDel() {
return auth(baseUrl + '/del')
}
str2intArray(str) {
if (isEmpty(str)) return []
return str.split(',').map(i => parseInt(i))
},
methods: {
mergeSearchForm() {
return {
...this.searchForm,
startTime: this.temp.ctime ? this.temp.ctime[0] : null,
endTime: this.temp.ctime ? this.temp.ctime[1] + 86400000 : null,
}
},
search() {
if (this.config.loading) return
this.config.loading = true
this.row = null
this.type = 'see'
searchRoles(this.mergeSearchForm())
.then(({list, total}) => {
list.forEach(i => {
i.resource_id = isEmpty(i.resource_id) ? [] : i.resource_id.split(',').map(i => parseInt(i))
})
this.searchForm.total = total
this.tableData = list
search() {
if (this.config.loading) return
this.config.loading = true
this.row = null
this.type = 'see'
searchRoles(this.mergeSearchForm())
.then(({list, total}) => {
list.forEach(i => {
i.departmentId = this.str2intArray(i.departmentId)
i.resourceId = this.str2intArray(i.resourceId)
})
.finally(() => this.config.loading = false)
},
this.searchForm.total = total
this.tableData = list
})
.finally(() => this.config.loading = false)
},
add() {
this.row = null
this.type = 'add'
this.editDialog = true
},
add() {
this.row = null
this.type = 'add'
this.editDialog = true
},
edit() {
if (!this.row) return elError('请选择要编辑的角色')
this.type = 'edit'
this.editDialog = true
},
edit() {
if (!this.row) return elError('请选择要编辑的角色')
this.type = 'edit'
this.editDialog = true
},
del() {
if (!this.row) return elError('请选择要删除的角色')
if (this.row.status === 1) return elError('不能删除已启用的角色')
if (this.config.operating) return
elConfirm(`确定删除角色【${this.row.name}】?`)
.then(() => {
this.config.operating = true
return delRole({id: this.row.id, name: this.row.name})
})
.then(() => {
elSuccess('删除成功')
this.search()
})
.finally(() => this.config.operating = false)
},
del() {
if (!this.row) return elError('请选择要删除的角色')
const {id, name, status} = this.row
if (status === 1) return elError('不能删除已启用的角色')
if (this.config.operating) return
elConfirm(`确定删除角色【${name}】?`)
.then(() => {
this.config.operating = true
return delRole({id, name})
})
.then(() => {
elSuccess('删除成功')
this.search()
})
.finally(() => this.config.operating = false)
},
success(msg) {
elSuccess(msg)
this.editDialog = false
this.search()
}
success(msg) {
elSuccess(msg)
this.editDialog = false
this.search()
}
}
}
</script>

@ -0,0 +1,39 @@
<template>
<tree-select :value="value" :data="data" :props="{label:'name'}" popper-append-to-body @input="input"/>
</template>
<script>
import TreeSelect from '@/components/TreeSelect'
import {getDepartments} from "@/api/system/department"
import {createTreeByWorker} from "@/utils/tree"
import {isEmpty} from "@/utils"
export default {
name: "DepartmentSelector",
components: {TreeSelect},
props: {value: Number},
data() {
return {
data: []
}
},
methods: {
input(v, data) {
this.$emit('input', v)
this.$emit('get-name', data && data.fullname)
},
},
mounted() {
getDepartments(false)
.then(data => createTreeByWorker(data))
.then(data => {
this.data = data
})
}
}
</script>

@ -1,22 +1,31 @@
<template>
<form-dialog :loading="loading" :title="title" :value="value" @close="cancel" @open="open">
<el-form
ref="form"
:model="form"
:rules="rules"
label-position="right"
label-width="75px"
status-icon
ref="form"
:model="form"
:rules="rules"
label-position="right"
label-width="75px"
status-icon
>
<el-form-item label="名 称:" prop="name">
<el-input v-model="form.name" :readonly="!canEdit" maxlength="20"/>
</el-form-item>
<el-form-item label="角 色:" prop="role">
<role-selector
v-model="form.role"
:disabled="!canEdit"
@get-name="e => form.role_name = e"
v-if="canEdit"
v-model="form.role"
@get-name="e => form.roleName = e"
/>
<template v-else>{{ form.roleName }}</template>
</el-form-item>
<el-form-item label="部 门:" prop="dept">
<department-selector
v-if="canEdit"
v-model="form.dept"
@get-name="e => form.deptName = e"
/>
<template v-else>{{ form.deptName }}</template>
</el-form-item>
<el-form-item label="状 态:" prop="status">
<el-radio-group v-model="form.status" :disabled="!canEdit">
@ -34,120 +43,126 @@
</template>
<script>
import FormDialog from '@/components/FormDialog'
import RoleSelector from './RoleSelector'
import dialogMixin from "@/mixins/dialogMixin"
import {checkName} from "@/api/account"
import {addUser, updateUser} from "@/api/system/user"
import {isEmpty} from '@/utils'
import {elConfirm} from "@/utils/message"
import dialogMixin from "@/mixins/dialogMixin"
import DepartmentSelector from "./DepartmentSelector"
import FormDialog from '@/components/FormDialog'
import RoleSelector from './RoleSelector'
import {checkName} from "@/api/account"
import {addUser, updateUser} from "@/api/system/user"
import {isEmpty} from '@/utils'
import {elConfirm} from "@/utils/message"
export default {
name: "EditDialog",
export default {
name: "EditDialog",
mixins: [dialogMixin],
mixins: [dialogMixin],
components: {FormDialog, RoleSelector},
components: {DepartmentSelector, FormDialog, RoleSelector},
props: {
value: {type: Boolean, default: false},
type: {type: String, default: 'see'},
data: {type: Object, default: () => ({})},
},
props: {
value: {type: Boolean, default: false},
type: {type: String, default: 'see'},
data: {type: Object, default: () => ({})},
},
data() {
const validateName = (r, v, c) => {
checkName(this.form.name, this.form.id)
.then(({msg}) => msg ? c(msg) : c())
.catch(e => c(e))
data() {
const validateName = (r, v, c) => {
checkName(this.form.name, this.form.id)
.then(({msg}) => msg ? c(msg) : c())
.catch(e => c(e))
}
return {
loading: false,
form: {
id: null,
name: null,
role: null,
roleName: null,
dept: null,
deptName: null,
status: 1,
},
rules: {
name: [
{required: true, message: '用户名称不能为空', trigger: 'change'},
{validator: validateName, trigger: 'change'}
],
role: [{required: true, message: '用户角色不能为空', trigger: 'change'}],
dept: [{required: true, message: '用户部门不能为空', trigger: 'change'}],
status: [{required: true, message: '用户状态不能为空', trigger: 'change'}],
}
return {
loading: false,
form: {
id: null,
name: null,
role: null,
role_name: null,
status: 1,
},
rules: {
name: [
{required: true, message: '用户名称不能为空', trigger: 'change'},
{validator: validateName, trigger: 'change'}
],
role: [{required: true, message: '用户角色不能为空', trigger: 'change'}],
status: [{required: true, message: '用户状态不能为空', trigger: 'change'}],
}
}
},
computed: {
title() {
if (isEmpty(this.type)) return ''
switch (this.type) {
case 'see':
return '用户信息'
case 'add':
return '添加用户'
case 'edit':
return '编辑用户'
}
},
computed: {
title() {
if (isEmpty(this.type)) return ''
switch (this.type) {
case 'see':
return '用户信息'
case 'add':
return '添加用户'
case 'edit':
return '编辑用户'
}
},
confirmMessage() {
switch (this.type) {
case 'add':
return `确认添加新的用户【${this.form.name}】?`
case 'edit':
return `确认提交对【${this.data.name}】作出的修改?`
default:
return ''
}
},
canEdit() {
return ['add', 'edit'].includes(this.type)
confirmMessage() {
switch (this.type) {
case 'add':
return `确认添加新的用户【${this.form.name}】?`
case 'edit':
return `确认提交对【${this.data.name}】作出的修改?`
default:
return ''
}
},
methods: {
open() {
this.$nextTick(() => {
if (this.type === 'edit') {
Object
.keys(this.data)
.forEach(key => key in this.form && (this.form[key] = this.data[key]))
}
})
},
canEdit() {
return ['add', 'edit'].includes(this.type)
}
},
clearForm() {
this.form.id = null
this.form.name = null
this.form.role = null
this.form.role_name = null
this.form.status = 1
this.$nextTick(() => this.$refs.form.clearValidate())
},
methods: {
open() {
this.$nextTick(() => {
if (this.type === 'edit') {
Object
.keys(this.data)
.forEach(key => key in this.form && (this.form[key] = this.data[key]))
}
})
},
cancel() {
this.closeDialog()
this.clearForm()
},
clearForm() {
this.form.id = null
this.form.name = null
this.form.role = null
this.form.roleName = null
this.form.dept = null
this.form.deptName = null
this.form.status = 1
this.$nextTick(() => this.$refs.form.clearValidate())
},
confirm() {
if (this.loading) return
this.$refs.form.validate(v => {
if (!v) return
elConfirm(this.confirmMessage)
.then(() => {
this.loading = true
return this.type === 'add' ? addUser(this.form) : updateUser(this.form)
})
.then(({msg}) => this.$emit('success', msg))
.finally(() => this.loading = false)
})
}
cancel() {
this.closeDialog()
this.clearForm()
},
confirm() {
if (this.loading) return
this.$refs.form.validate(v => {
if (!v) return
elConfirm(this.confirmMessage)
.then(() => {
this.loading = true
return this.type === 'add' ? addUser(this.form) : updateUser(this.form)
})
.then(({msg}) => this.$emit('success', msg))
.finally(() => this.loading = false)
})
}
}
}
</script>

@ -1,5 +1,5 @@
<template>
<el-select :disabled="disabled" :value="value" clearable @clear="emit(null)" @input="emit">
<el-select :value="value" clearable @clear="emit(null)" @input="emit">
<el-option
v-for="item in roles"
:key="item.id"
@ -15,12 +15,7 @@
export default {
name: "RoleSelector",
props: {
value: Number,
disabled: {
type: Boolean, default: false
}
},
props: {value: Number},
data() {
return {

@ -8,18 +8,18 @@
<role-selector v-model="searchForm.role"/>
</search-form-item>
<search-form-item label="状 态:">
<el-select v-model="searchForm.status" clearable @clear="searchForm.status=null">
<el-select v-model="searchForm.status" clearable @clear="searchForm.status = null">
<el-option :value="1" label="启用"/>
<el-option :value="0" label="禁用"/>
</el-select>
</search-form-item>
<search-form-item label="创建时间:">
<el-date-picker
v-model="temp.ctime"
format="yyyy-MM-dd"
range-separator="-"
type="daterange"
value-format="timestamp"
v-model="temp.ctime"
format="yyyy-MM-dd"
range-separator="-"
type="daterange"
value-format="timestamp"
/>
</search-form-item>
</search-form>
@ -40,43 +40,44 @@
<el-table-column align="center" label="#" type="index" width="80"/>
<el-table-column align="center" label="头像" width="100">
<img
slot-scope="{row}"
v-if="row.avatar"
:src="row.avatar"
class="table-image"
@click.stop="previewAvatar(row.avatar)"
slot-scope="{row}"
v-if="row.avatar"
:src="row.avatar"
class="table-image"
@click.stop="() => previewAvatar(row.avatar)"
>
</el-table-column>
<el-table-column align="center" label="用户名" prop="name" show-overflow-tooltip/>
<el-table-column align="center" label="角 色" show-overflow-tooltip>
<template v-slot="{row}">
{{row.admin===1?'超级管理员':row.role_name}}
{{ row.admin === true ? '超级管理员' : row.roleName }}
</template>
</el-table-column>
<el-table-column align="center" label="部 门" prop="deptName" show-overflow-tooltip/>
<el-table-column align="center" label="创建时间" width="150" show-overflow-tooltip>
<template v-slot="{row}">{{row.ctime | timestamp2Date}}</template>
<template v-slot="{row}">{{ row.ctime | timestamp2Date }}</template>
</el-table-column>
<el-table-column align="center" label="在线情况" width="120">
<template v-slot="{row}">
<span :class="row.online?'success':'error'" class="dot"/>
<span>{{row.online?'在线':'离线'}}</span>
<span :class="row.online ? 'success' : 'error'" class="dot"/>
<span>{{ row.online ? '在线' : '离线' }}</span>
</template>
</el-table-column>
<el-table-column align="center" label="状 态" width="120">
<template v-slot="{row}">
<span :class="row.status===1?'success':'error'" class="dot"/>
<span>{{row.status===1?'启用':'禁用'}}</span>
<span :class="row.status === 1 ? 'success' : 'error'" class="dot"/>
<span>{{ row.status === 1 ? '启用' : '禁用' }}</span>
</template>
</el-table-column>
</abstract-table>
<el-pagination
background
:current-page="searchForm.page"
:page-size="searchForm.pageSize"
:total="searchForm.total"
layout="total, prev, pager, next, jumper"
@current-change="pageChange"
background
:current-page="searchForm.page"
:page-size="searchForm.pageSize"
:total="searchForm.total"
layout="total, prev, pager, next, jumper"
@current-change="pageChange"
/>
</el-row>
@ -85,149 +86,149 @@
</template>
<script>
import SearchForm from "@/components/SearchForm"
import SearchFormItem from "@/components/SearchForm/SearchFormItem"
import RoleSelector from './components/RoleSelector'
import EditDialog from './components/EditDialog'
import {delUser, getUsers, kick, resetUserPwd} from "@/api/system/user"
import {isEmpty} from '@/utils'
import {autoCompleteUrl} from "@/utils/file"
import {elConfirm, elError, elSuccess} from "@/utils/message"
import {auth} from "@/utils/auth"
import tableMixin from '@/mixins/tablePageMixin'
import SearchForm from "@/components/SearchForm"
import SearchFormItem from "@/components/SearchForm/SearchFormItem"
import RoleSelector from './components/RoleSelector'
import EditDialog from './components/EditDialog'
import {delUser, getUsers, kick, resetUserPwd} from "@/api/system/user"
import {isEmpty} from '@/utils'
import {autoCompleteUrl} from "@/utils/file"
import {elConfirm, elError, elSuccess} from "@/utils/message"
import {auth} from "@/utils/auth"
import tableMixin from '@/mixins/tablePageMixin'
const baseUrl = '/system/user'
export default {
name: "userManagement",
mixins: [tableMixin],
components: {SearchForm, SearchFormItem, RoleSelector, EditDialog},
data() {
return {
searchForm: {
name: '',
role: null,
status: null
},
temp: {ctime: []},
editDialog: false
}
},
const baseUrl = '/system/user'
computed: {
canAdd() {
return auth(`${baseUrl}/add`)
},
export default {
name: "userManagement",
canUpdate() {
return auth(`${baseUrl}/update`)
},
mixins: [tableMixin],
canDel() {
return auth(`${baseUrl}/del`)
},
components: {SearchForm, SearchFormItem, RoleSelector, EditDialog},
canKick() {
return auth(`${baseUrl}/kick`)
},
canResetPwd() {
return auth(`${baseUrl}/resetPwd`)
},
},
data() {
methods: {
mergeSearchForm() {
return {
searchForm: {
name: '',
role: null,
status: null
},
temp: {ctime: []},
editDialog: false
...this.searchForm,
startTime: this.temp.ctime ? this.temp.ctime[0] : null,
endTime: this.temp.ctime ? this.temp.ctime[1] + 86400000 : null,
}
},
computed: {
canAdd() {
return auth(baseUrl + '/add')
},
canUpdate() {
return auth(baseUrl + '/update')
},
canDel() {
return auth(baseUrl + '/del')
},
canKick() {
return auth(baseUrl + '/kick')
},
canResetPwd() {
return auth(baseUrl + '/resetPwd')
},
search() {
if (this.config.loading) return
this.config.loading = true
this.row = null
this.type = 'see'
getUsers(this.mergeSearchForm())
.then(({list, total}) => {
list.forEach(u => u.avatar = autoCompleteUrl(u.avatar))
this.searchForm.total = total
this.tableData = list
})
.finally(() => this.config.loading = false)
},
methods: {
mergeSearchForm() {
return {
...this.searchForm,
startTime: this.temp.ctime ? this.temp.ctime[0] : null,
endTime: this.temp.ctime ? this.temp.ctime[1] + 86400000 : null,
}
},
search() {
if (this.config.loading) return
this.config.loading = true
this.row = null
this.type = 'see'
getUsers(this.mergeSearchForm())
.then(({list, total}) => {
list.forEach(u => u.avatar = autoCompleteUrl(u.avatar))
this.searchForm.total = total
this.tableData = list
})
.finally(() => this.config.loading = false)
},
add() {
this.type = 'add'
this.editDialog = true
},
add() {
this.type = 'add'
this.editDialog = true
},
edit() {
if (isEmpty(this.row)) return elError('请选择要编辑的用户')
this.type = 'edit'
this.editDialog = true
},
edit() {
if (isEmpty(this.row)) return elError('请选择要编辑的用户')
this.type = 'edit'
this.editDialog = true
},
resetPwd() {
if (isEmpty(this.row)) return elError('请选择要重置密码的用户')
if (this.config.operating) return
elConfirm(`确定重置用户【${this.row.name}】的密码?`)
.then(() => {
this.config.operating = true
return resetUserPwd({id: this.row.id, name: this.row.name})
})
.then(({msg}) => {
elSuccess(msg)
this.search()
})
.finally(() => this.config.operating = false)
},
resetPwd() {
if (isEmpty(this.row)) return elError('请选择要重置密码的用户')
if (this.config.operating) return
elConfirm(`确定重置用户【${this.row.name}】的密码?`)
.then(() => {
this.config.operating = true
return resetUserPwd({id: this.row.id, name: this.row.name})
})
.then(({msg}) => {
elSuccess(msg)
this.search()
})
.finally(() => this.config.operating = false)
},
del() {
if (isEmpty(this.row)) return elError('请选择要删除的用户')
if (this.config.operating) return
elConfirm(`确定删除用户【${this.row.name}】?`)
.then(() => {
this.config.operating = true
return delUser({id: this.row.id, name: this.row.name})
})
.then(({msg}) => {
elSuccess(msg)
this.search()
})
.finally(() => this.config.operating = false)
},
del() {
if (isEmpty(this.row)) return elError('请选择要删除的用户')
if (this.config.operating) return
elConfirm(`确定删除用户【${this.row.name}】?`)
.then(() => {
this.config.operating = true
return delUser({id: this.row.id, name: this.row.name})
})
.then(({msg}) => {
elSuccess(msg)
this.search()
})
.finally(() => this.config.operating = false)
},
kick() {
if (isEmpty(this.row)) return elError('请选择要踢出的用户')
if (!this.row.online) return elError('只有在线用户才可以踢出')
if (this.config.operating) return
elConfirm(`确定踢出用户【${this.row.name}】?`)
.then(() => {
this.config.operating = true
return kick([{id: this.row.id, name: this.row.name}])
})
.then(() => {
elSuccess('踢出成功')
this.search()
})
.finally(() => this.config.operating = false)
},
kick() {
if (isEmpty(this.row)) return elError('请选择要踢出的用户')
if (!this.row.online) return elError('只有在线用户才可以踢出')
if (this.config.operating) return
elConfirm(`确定踢出用户【${this.row.name}】?`)
.then(() => {
this.config.operating = true
return kick([{id: this.row.id, name: this.row.name}])
})
.then(() => {
elSuccess('踢出成功')
this.search()
})
.finally(() => this.config.operating = false)
},
success(msg) {
elSuccess(msg)
this.editDialog = false
this.search()
},
success(msg) {
elSuccess(msg)
this.editDialog = false
this.search()
},
previewAvatar(src) {
this.$image({index: 0, urlList: [src]})
}
previewAvatar(src) {
this.$image({index: 0, urlList: [src]})
}
}
}
</script>

@ -9,11 +9,11 @@
label-width="100px"
status-icon
>
<el-form-item label="旧密码:" prop="old_pwd">
<el-input v-model.trim="form.old_pwd" maxlength="100" type="password"/>
<el-form-item label="旧密码:" prop="oldPwd">
<el-input v-model.trim="form.oldPwd" maxlength="100" type="password"/>
</el-form-item>
<el-form-item label="新密码:" prop="new_pwd">
<el-input v-model.trim="form.new_pwd" maxlength="100" type="password"/>
<el-form-item label="新密码:" prop="newPwd">
<el-input v-model.trim="form.newPwd" maxlength="100" type="password"/>
</el-form-item>
</el-form>
@ -40,15 +40,15 @@
return {
loading: false,
form: {
old_pwd: null,
new_pwd: null
oldPwd: null,
newPwd: null
},
rules: {
old_pwd: [{required: true, message: '请输入原密码', trigger: 'change'}],
new_pwd: [
oldPwd: [{required: true, message: '请输入原密码', trigger: 'change'}],
newPwd: [
{required: true, message: '请输入新密码', trigger: 'change'},
{
validator: (r, v, c) => v !== this.form.old_pwd ? c() : c('新密码不得与旧密码相同'),
validator: (r, v, c) => v !== this.form.oldPwd ? c() : c('新密码不得与旧密码相同'),
trigger: 'change'
},
{
@ -68,8 +68,8 @@
methods: {
clearForm() {
this.form.old_pwd = null
this.form.new_pwd = null
this.form.oldPwd = null
this.form.newPwd = null
this.$nextTick(() => this.$refs.form.clearValidate())
},
@ -79,8 +79,8 @@
if (!v) return
this.loading = true
updateUserPwd({
old_pwd: md5(this.form.old_pwd),
new_pwd: md5(this.form.new_pwd),
oldPwd: md5(this.form.oldPwd),
newPwd: md5(this.form.newPwd),
})
.then(() => elSuccess('修改成功'))
.finally(() => {

@ -6,8 +6,11 @@
<li>登录账号
<div class="user-right">{{ name }}</div>
</li>
<li>所属部门
<div class="user-right">{{ deptName }}</div>
</li>
<li>用户角色
<div class="user-right">{{ role_name }}</div>
<div class="user-right">{{ roleName }}</div>
</li>
<li>安全设置
<div class="user-right"><a @click="modifyPasswordDialog=true">修改密码</a></div>
@ -21,56 +24,58 @@
</template>
<script>
import UserAccount from './Account'
import UploadAvatar from './Avatar'
import {mapState} from 'vuex'
import {mapState} from 'vuex'
import UploadAvatar from './Avatar'
import UserAccount from './Account'
export default {
components: {UserAccount, UploadAvatar},
export default {
name: 'UserCard',
data() {
return {
modifyPasswordDialog: false,
uploadAvatarDialog: false
}
},
components: {UploadAvatar, UserAccount},
computed: {
...mapState('user', ['name', 'avatar', 'role_name', 'admin'])
data() {
return {
modifyPasswordDialog: false,
uploadAvatarDialog: false
}
},
computed: {
...mapState('user', ['name', 'avatar', 'roleName', 'deptName', 'admin'])
}
}
</script>
<style lang="scss">
.user-card {
.el-avatar {
margin: 0 auto;
display: block;
cursor: pointer;
.user-card {
.el-avatar {
margin: 0 auto;
display: block;
cursor: pointer;
i {
line-height: 180px;
font-size: 150px;
}
i {
line-height: 180px;
font-size: 150px;
}
}
.user-info {
padding-left: 0;
list-style: none;
.user-info {
padding-left: 0;
list-style: none;
li {
border-bottom: 1px solid #F0F3F4;
padding: 11px 0;
font-size: 13px;
}
li {
border-bottom: 1px solid #F0F3F4;
padding: 11px 0;
font-size: 13px;
}
.user-right {
float: right;
.user-right {
float: right;
a {
color: #317EF3;
}
a {
color: #317EF3;
}
}
}
}
</style>

@ -9,7 +9,7 @@
<el-tabs v-model="activeTab" stretch>
<el-tab-pane v-for="i in tabs" v-once :key="i.label" :name="i.name">
<span slot="label">
{{i.label}}
{{ i.label }}
<el-tooltip :content="i.intro" placement="top">
<i v-if="i.intro" class="el-icon-question" style="color: #4AB7BD"/>
</el-tooltip>
@ -28,26 +28,23 @@
</template>
<script>
import UserCard from "./components/UserCard"
import LoginHistory from "./components/LoginHistory"
import LoginHistory from "./components/LoginHistory"
import UserAction from "./components/UserAction"
import UserCard from "./components/UserCard"
export default {
name: 'userCenter',
export default {
name: 'userCenter',
components: {
UserCard,
LoginHistory,
UserAction: () => import('./components/UserAction')
},
components: {UserCard, LoginHistory, UserAction},
data() {
return {
activeTab: 'login-history',
tabs: [
{label: '登录历史', name: 'login-history', intro: '只保留最近7天的记录'},
{label: '操作记录', name: 'user-action', intro: '只保留最近7天的记录'},
]
}
data() {
return {
activeTab: 'login-history',
tabs: [
{label: '登录历史', name: 'login-history', intro: '只保留最近7天的记录'},
{label: '操作记录', name: 'user-action', intro: '只保留最近7天的记录'},
]
}
}
}
</script>

Loading…
Cancel
Save