parent
90097bfe0e
commit
0e8f4dc504
@ -1,187 +1,143 @@ |
|||||||
<template> |
<template> |
||||||
<div class="org-tree-container"> |
<div class="org-tree-container"> |
||||||
<div :class="{horizontal, collapsable}" class="org-tree"> |
<div class="org-tree" :class="{horizontal, collapsable}"> |
||||||
<org-tree-node |
<org-tree-node |
||||||
v-bind="$listeners" |
:data="dataCloned" |
||||||
:button-render="buttonRender" |
:props="props" |
||||||
:collapsable="collapsable" |
:horizontal="horizontal" |
||||||
:data="dataCloned" |
:label-width="labelWidth" |
||||||
:horizontal="horizontal" |
:collapsable="collapsable" |
||||||
:label-class-name="labelClassName" |
:label-class-name="labelClassName" |
||||||
:label-width="labelWidth" |
:node-render="nodeRender" |
||||||
:node-render="nodeRender" |
:button-render="buttonRender" |
||||||
:props="props" |
@on-expand="handleExpand" |
||||||
@on-expand="handleExpand" |
@on-node-click="handleNodeClick" |
||||||
@on-node-click="handleNodeClick" |
|
||||||
/> |
/> |
||||||
</div> |
</div> |
||||||
</div> |
</div> |
||||||
</template> |
</template> |
||||||
|
|
||||||
<script> |
<script> |
||||||
import render from './node' |
import OrgTreeNode from "./OrgTreeNode" |
||||||
import {deepClone as clonedeep} from '@/utils' |
|
||||||
|
|
||||||
export default { |
export default { |
||||||
name: 'OrgTree', |
name: 'OrgTree', |
||||||
|
|
||||||
components: { |
components: {OrgTreeNode}, |
||||||
OrgTreeNode: { |
|
||||||
render, |
|
||||||
functional: true |
|
||||||
} |
|
||||||
}, |
|
||||||
|
|
||||||
|
props: { |
||||||
|
data: {required: true}, |
||||||
props: { |
props: { |
||||||
data: { |
type: Object, |
||||||
type: Object, |
default: () => ({ |
||||||
required: true |
id: 'id', |
||||||
}, |
label: 'label', |
||||||
props: { |
expand: 'expand', |
||||||
type: Object, |
children: 'children' |
||||||
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 |
|
||||||
} |
|
||||||
}, |
}, |
||||||
|
horizontal: Boolean, |
||||||
|
collapsable: Boolean, |
||||||
|
nodeRender: Function, |
||||||
|
buttonRender: Function, |
||||||
|
labelWidth: [String, Number], |
||||||
|
labelClassName: [Function, String], |
||||||
|
expandAll: Boolean |
||||||
|
}, |
||||||
|
|
||||||
data() { |
data() { |
||||||
return { |
return { |
||||||
flatData: {}, |
flatData: {}, |
||||||
dataCloned: {} |
dataCloned: {} |
||||||
} |
} |
||||||
}, |
}, |
||||||
|
|
||||||
watch: { |
watch: { |
||||||
data(newData) { |
data(newData) { |
||||||
this._handleData(newData) |
this._cloneData(newData) |
||||||
this._mapData(this.dataCloned, item => { |
this._mapData(this.dataCloned, item => { |
||||||
const {expand} = this.flatData[item[this.prop_id]] || {} |
const {expand} = this.flatData[item[this.props.id]] || {} |
||||||
if (expand) this.$set(item, this.prop_expand, true) |
if (expand) this.$set(item, this.props.expand, true) |
||||||
}) |
}) |
||||||
this._toggleExpand(this.dataCloned, this.expandAll) |
this._toggleExpand(this.dataCloned, this.expandAll) |
||||||
}, |
|
||||||
expandAll(status) { |
|
||||||
this._toggleExpand(this.dataCloned, status) |
|
||||||
} |
|
||||||
}, |
}, |
||||||
|
expandAll(status) { |
||||||
|
this._toggleExpand(this.dataCloned, status) |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
computed: { |
methods: { |
||||||
prop_id() { |
_cloneData(newData) { |
||||||
return this.props.id |
this.dataCloned = JSON.parse(JSON.stringify(newData)) |
||||||
}, |
|
||||||
prop_label() { |
|
||||||
return this.props.label |
|
||||||
}, |
|
||||||
prop_expand() { |
|
||||||
return this.props.expand |
|
||||||
}, |
|
||||||
prop_children() { |
|
||||||
return this.props.children |
|
||||||
} |
|
||||||
}, |
}, |
||||||
|
_setFlatData(data) { |
||||||
methods: { |
this.flatData[data[this.props.id]] = data |
||||||
_handleData(data) { |
}, |
||||||
this._cloneData(data) |
/** |
||||||
}, |
* @description 工具方法,用于遍历树状数据的每个节点, fn为在该节点做的操作,其有一个参数即当前节点数据 |
||||||
_cloneData(newData) { |
*/ |
||||||
this.dataCloned = clonedeep(newData) |
_mapData(data, fn) { |
||||||
}, |
fn(data) |
||||||
_setFlatData(data) { |
const children = data[this.props.children] |
||||||
this.flatData[data[this.prop_id]] = data |
children && children.forEach(child => this._mapData(child, fn)) |
||||||
}, |
}, |
||||||
/** |
/** |
||||||
* @description 工具方法,用于遍历树状数据的每个节点, fn为在该节点做的操作,其有一个参数即当前节点数据 |
* @description 用来便利所有节点数据,将树状数据扁平化存放到flatData,用于数据更新后展开状态的恢复 |
||||||
*/ |
*/ |
||||||
_mapData(data, fn) { |
_updateExpandStatus() { |
||||||
fn(data) |
this._mapData(this.dataCloned, this._setFlatData) |
||||||
const children = data[this.prop_children] |
}, |
||||||
if (children) { |
_toggleExpand(data, status) { |
||||||
children.forEach(child => { |
if (Array.isArray(data)) { |
||||||
this._mapData(child, fn) |
data.forEach(item => { |
||||||
}) |
this.$set(item, this.props.expand, status) |
||||||
} |
const children = item[this.props.children] |
||||||
}, |
if (children) { |
||||||
/** |
this._toggleExpand(children, status) |
||||||
* @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 |
|
||||||
} |
} |
||||||
const children = child[this.prop_children] |
|
||||||
children && _this.collapse(children) |
|
||||||
}) |
}) |
||||||
}, |
} |
||||||
handleExpand(data) { |
else { |
||||||
if (this.prop_expand in data) { |
this.$set(data, this.props.expand, status) |
||||||
data[this.prop_expand] = !data[this.prop_expand] |
const children = data[this.props.children] |
||||||
const children = data[this.prop_children] |
if (children) { |
||||||
if (!data[this.prop_expand] && children) { |
this._toggleExpand(children, status) |
||||||
this.collapse(children) |
|
||||||
} |
|
||||||
} |
|
||||||
else { |
|
||||||
this.$set(data, this.prop_expand, true) |
|
||||||
} |
} |
||||||
this.$emit('on-expand', data, data[this.prop_expand]) |
} |
||||||
this._updateExpandStatus() |
}, |
||||||
}, |
collapse(list) { |
||||||
_toggleExpand(data, status) { |
list.forEach(child => { |
||||||
var _this = this |
if (child[this.props.expand]) { |
||||||
if (Array.isArray(data)) { |
child[this.props.expand] = false |
||||||
data.forEach(item => { |
|
||||||
_this.$set(item, this.prop_expand, status) |
|
||||||
const children = item[this.prop_children] |
|
||||||
if (children) { |
|
||||||
_this._toggleExpand(children, status) |
|
||||||
} |
|
||||||
}) |
|
||||||
} |
} |
||||||
else { |
const children = child[this.props.children] |
||||||
_this.$set(data, this.prop_expand, status) |
children && this.collapse(children) |
||||||
const children = data[this.prop_children] |
}) |
||||||
if (children) { |
}, |
||||||
_this._toggleExpand(children, status) |
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.$emit('on-expand', data, data[this.props.expand]) |
||||||
this._handleData(this.data) |
|
||||||
this._updateExpandStatus() |
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> |
</script> |
||||||
|
|
||||||
<style lang="scss" src="./tree.scss"></style> |
<style lang="scss" src="./tree.scss"></style> |
||||||
|
|||||||
@ -1,261 +1,261 @@ |
|||||||
.org-tree-container { |
.org-tree-container { |
||||||
display: inline-block; |
display: inline-block; |
||||||
padding: 15px; |
padding: 15px; |
||||||
} |
|
||||||
|
|
||||||
.org-tree { |
|
||||||
display: table; |
|
||||||
text-align: center; |
|
||||||
|
|
||||||
&:before, &:after { |
.org-tree { |
||||||
content: ''; |
|
||||||
display: table; |
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; |
text-align: center; |
||||||
border-radius: 3px; |
|
||||||
box-shadow: 0 1px 5px rgba(0, 0, 0, .15); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
.org-tree-node-btn { |
&:before, &:after { |
||||||
position: absolute; |
content: ''; |
||||||
top: 100%; |
display: table; |
||||||
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; |
|
||||||
|
|
||||||
&.is-leaf, &.collapsed { |
&:after { |
||||||
padding-left: 10px; |
clear: both; |
||||||
padding-right: 10px; |
} |
||||||
} |
} |
||||||
|
|
||||||
&:before, &:after { |
.org-tree-node, |
||||||
content: ''; |
.org-tree-node-children { |
||||||
position: absolute; |
position: relative; |
||||||
top: 0; |
margin: 0; |
||||||
left: 0; |
padding: 0; |
||||||
width: 50%; |
list-style-type: none; |
||||||
height: 19px; |
|
||||||
} |
|
||||||
|
|
||||||
&:after { |
&:before, &:after { |
||||||
left: 50%; |
transition: all .35s; |
||||||
border-left: 1px solid #ddd; |
} |
||||||
} |
} |
||||||
|
|
||||||
&:not(:first-child):before, |
.org-tree-node-label { |
||||||
&:not(:last-child):after { |
position: relative; |
||||||
border-top: 1px solid #ddd; |
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 { |
||||||
|
|
||||||
.collapsable .org-tree-node.collapsed { |
|
||||||
padding-bottom: 30px; |
|
||||||
|
|
||||||
.org-tree-node-label:after { |
|
||||||
content: ''; |
|
||||||
position: absolute; |
position: absolute; |
||||||
top: 100%; |
top: 100%; |
||||||
left: 0; |
left: 50%; |
||||||
width: 50%; |
width: 20px; |
||||||
height: 20px; |
height: 20px; |
||||||
border-right: 1px solid #ddd; |
z-index: 10; |
||||||
} |
margin-left: -10px; |
||||||
} |
margin-top: 10px; |
||||||
|
background-color: #fff; |
||||||
.org-tree > .org-tree-node { |
border: 1px solid #ccc; |
||||||
padding-top: 0; |
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 { |
&:before, &:after { |
||||||
border-left: 0; |
content: ''; |
||||||
} |
position: absolute; |
||||||
} |
} |
||||||
|
|
||||||
.org-tree-node-children { |
&:before { |
||||||
padding-top: 20px; |
top: 50%; |
||||||
display: table; |
left: 4px; |
||||||
|
right: 4px; |
||||||
|
height: 0; |
||||||
|
border-top: 1px solid #ccc; |
||||||
|
} |
||||||
|
|
||||||
&:before { |
&:after { |
||||||
content: ''; |
top: 4px; |
||||||
position: absolute; |
left: 50%; |
||||||
top: 0; |
bottom: 4px; |
||||||
left: 50%; |
width: 0; |
||||||
width: 0; |
border-left: 1px solid #ccc; |
||||||
height: 20px; |
} |
||||||
border-left: 1px solid #ddd; |
|
||||||
} |
|
||||||
|
|
||||||
&:after { |
&.expanded:after { |
||||||
content: ''; |
border: none; |
||||||
display: table; |
} |
||||||
clear: both; |
|
||||||
} |
} |
||||||
} |
|
||||||
|
|
||||||
.horizontal { |
|
||||||
.org-tree-node { |
.org-tree-node { |
||||||
|
padding-top: 20px; |
||||||
display: table-cell; |
display: table-cell; |
||||||
float: none; |
vertical-align: top; |
||||||
padding-top: 0; |
|
||||||
padding-left: 20px; |
|
||||||
|
|
||||||
&.is-leaf, &.collapsed { |
&.is-leaf, &.collapsed { |
||||||
padding-top: 10px; |
padding-left: 10px; |
||||||
padding-bottom: 10px; |
padding-right: 10px; |
||||||
} |
} |
||||||
|
|
||||||
&:before, &:after { |
&:before, &:after { |
||||||
width: 19px; |
content: ''; |
||||||
height: 50%; |
position: absolute; |
||||||
} |
top: 0; |
||||||
|
|
||||||
&:after { |
|
||||||
top: 50%; |
|
||||||
left: 0; |
left: 0; |
||||||
border-left: 0; |
width: 50%; |
||||||
|
height: 19px; |
||||||
} |
} |
||||||
|
|
||||||
&:only-child:before { |
&:after { |
||||||
top: 1px; |
left: 50%; |
||||||
border-bottom: 1px solid #ddd; |
border-left: 1px solid #ddd; |
||||||
} |
} |
||||||
|
|
||||||
&:not(:first-child):before, |
&:not(:first-child):before, |
||||||
&:not(:last-child):after { |
&:not(:last-child):after { |
||||||
border-top: 0; |
|
||||||
border-left: 1px solid #ddd; |
|
||||||
} |
|
||||||
|
|
||||||
&:not(:only-child):after { |
|
||||||
border-top: 1px solid #ddd; |
border-top: 1px solid #ddd; |
||||||
} |
} |
||||||
|
|
||||||
.org-tree-node-inner { |
|
||||||
display: table; |
|
||||||
} |
|
||||||
|
|
||||||
} |
} |
||||||
|
|
||||||
.org-tree-node-label { |
.collapsable .org-tree-node.collapsed { |
||||||
display: table-cell; |
padding-bottom: 30px; |
||||||
vertical-align: middle; |
|
||||||
} |
|
||||||
|
|
||||||
&.collapsable .org-tree-node.collapsed { |
|
||||||
padding-right: 30px; |
|
||||||
|
|
||||||
.org-tree-node-label:after { |
.org-tree-node-label:after { |
||||||
top: 0; |
content: ''; |
||||||
left: 100%; |
position: absolute; |
||||||
width: 20px; |
top: 100%; |
||||||
height: 50%; |
left: 0; |
||||||
border-right: 0; |
width: 50%; |
||||||
border-bottom: 1px solid #ddd; |
height: 20px; |
||||||
|
border-right: 1px solid #ddd; |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
.org-tree-node-btn { |
.org-tree > .org-tree-node { |
||||||
top: 50%; |
padding-top: 0; |
||||||
left: 100%; |
|
||||||
margin-top: -11px; |
|
||||||
margin-left: 9px; |
|
||||||
} |
|
||||||
|
|
||||||
& > .org-tree-node:only-child:before { |
&:after { |
||||||
border-bottom: 0; |
border-left: 0; |
||||||
|
} |
||||||
} |
} |
||||||
|
|
||||||
.org-tree-node-children { |
.org-tree-node-children { |
||||||
display: table-cell; |
padding-top: 20px; |
||||||
padding-top: 0; |
display: table; |
||||||
padding-left: 20px; |
|
||||||
|
|
||||||
&:before { |
&:before { |
||||||
top: 50%; |
content: ''; |
||||||
left: 0; |
position: absolute; |
||||||
width: 20px; |
top: 0; |
||||||
height: 0; |
left: 50%; |
||||||
border-left: 0; |
width: 0; |
||||||
border-top: 1px solid #ddd; |
height: 20px; |
||||||
|
border-left: 1px solid #ddd; |
||||||
} |
} |
||||||
|
|
||||||
&:after { |
&: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 { |
.org-tree-node-children { |
||||||
display: block; |
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,125 +1,127 @@ |
|||||||
<template> |
<template> |
||||||
<div :style="orgTreeStyle" class="org-tree-wrapper" @mousedown="mousedownView"> |
<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 |
<div |
||||||
slot-scope="data" |
slot-scope="data" |
||||||
:class="{'has-children-label':data.children && data.children.length}" |
:class="{'has-children-label':data.children && data.children.length}" |
||||||
class="custom-org-node" |
class="custom-org-node" |
||||||
@contextmenu.prevent="$emit('node-contextmenu',$event,data)" |
@contextmenu.prevent="e => $emit('node-contextmenu',e,data)" |
||||||
> |
> |
||||||
{{data.label}} |
{{ data.label }} |
||||||
</div> |
</div> |
||||||
</org-tree> |
</org-tree> |
||||||
</div> |
</div> |
||||||
</template> |
</template> |
||||||
|
|
||||||
<script> |
<script> |
||||||
import OrgTree from '@/components/OrgTree' |
import OrgTree from '@/components/OrgTree' |
||||||
|
|
||||||
export default { |
export default { |
||||||
name: "OrgTreeView", |
name: "OrgTreeView", |
||||||
|
|
||||||
components: {OrgTree}, |
components: {OrgTree}, |
||||||
|
|
||||||
props: { |
props: { |
||||||
data: {type: Object, default: () => ({})}, |
data: {type: Object, default: () => ({})}, |
||||||
horizontal: Boolean, |
horizontal: Boolean, |
||||||
collapsable: Boolean, |
collapsable: Boolean, |
||||||
zoom: {type: Number, default: 1}, |
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 { |
return { |
||||||
orgTreeOffsetLeft: 0, |
transform: `translate(-50%, -50%) scale(${this.zoom}, ${this.zoom})`, |
||||||
orgTreeOffsetTop: 0, |
marginLeft: `${this.orgTreeOffsetLeft}px`, |
||||||
initPageX: 0, |
marginTop: `${this.orgTreeOffsetTop}px` |
||||||
initPageY: 0, |
|
||||||
oldMarginLeft: 0, |
|
||||||
oldMarginTop: 0, |
|
||||||
canMove: false, |
|
||||||
expandAll: true |
|
||||||
} |
} |
||||||
}, |
} |
||||||
|
}, |
||||||
|
|
||||||
computed: { |
methods: { |
||||||
orgTreeStyle() { |
nodeClick(e, data, expand) { |
||||||
return { |
expand() |
||||||
transform: `translate(-50%, -50%) scale(${this.zoom}, ${this.zoom})`, |
|
||||||
marginLeft: `${this.orgTreeOffsetLeft}px`, |
|
||||||
marginTop: `${this.orgTreeOffsetTop}px` |
|
||||||
} |
|
||||||
} |
|
||||||
}, |
}, |
||||||
|
|
||||||
methods: { |
mousedownView(event) { |
||||||
mousedownView(event) { |
this.canMove = true |
||||||
this.canMove = true |
this.initPageX = event.pageX |
||||||
this.initPageX = event.pageX |
this.initPageY = event.pageY |
||||||
this.initPageY = event.pageY |
this.oldMarginLeft = this.orgTreeOffsetLeft |
||||||
this.oldMarginLeft = this.orgTreeOffsetLeft |
this.oldMarginTop = this.orgTreeOffsetTop |
||||||
this.oldMarginTop = this.orgTreeOffsetTop |
document.addEventListener('mousemove', this.mousemoveView) |
||||||
document.addEventListener('mousemove', this.mousemoveView) |
document.addEventListener('mouseup', this.mouseupView) |
||||||
document.addEventListener('mouseup', this.mouseupView) |
}, |
||||||
}, |
mousemoveView(event) { |
||||||
|
if (!this.canMove) return |
||||||
mousemoveView(event) { |
const {pageX, pageY} = event |
||||||
if (!this.canMove) return |
this.orgTreeOffsetLeft = this.oldMarginLeft + pageX - this.initPageX |
||||||
const {pageX, pageY} = event |
this.orgTreeOffsetTop = this.oldMarginTop + pageY - this.initPageY |
||||||
this.orgTreeOffsetLeft = this.oldMarginLeft + pageX - this.initPageX |
}, |
||||||
this.orgTreeOffsetTop = this.oldMarginTop + pageY - this.initPageY |
mouseupView() { |
||||||
}, |
this.canMove = false |
||||||
|
document.removeEventListener('mousemove', this.mousemoveView) |
||||||
mouseupView() { |
document.removeEventListener('mouseup', this.mouseupView) |
||||||
this.canMove = false |
|
||||||
document.removeEventListener('mousemove', this.mousemoveView) |
|
||||||
document.removeEventListener('mouseup', this.mouseupView) |
|
||||||
} |
|
||||||
} |
} |
||||||
} |
} |
||||||
|
} |
||||||
</script> |
</script> |
||||||
|
|
||||||
<style lang="scss"> |
<style lang="scss"> |
||||||
.org-tree-wrapper { |
.org-tree-wrapper { |
||||||
cursor: move; |
cursor: move; |
||||||
display: inline-block; |
display: inline-block; |
||||||
position: absolute; |
position: absolute; |
||||||
left: 50%; |
left: 50%; |
||||||
top: 50%; |
top: 50%; |
||||||
transition: transform 0.2s ease-out; |
transition: transform 0.2s ease-out; |
||||||
z-index: 2; |
z-index: 2; |
||||||
|
|
||||||
.org-tree-node-label { |
.org-tree-node-label { |
||||||
.org-tree-node-label-inner { |
.org-tree-node-label-inner { |
||||||
padding: 0; |
padding: 0; |
||||||
|
|
||||||
.custom-org-node { |
.custom-org-node { |
||||||
padding: 14px 41px; |
padding: 14px 41px; |
||||||
background: #738699; |
background: #738699; |
||||||
user-select: none; |
user-select: none; |
||||||
word-wrap: normal; |
word-wrap: normal; |
||||||
white-space: nowrap; |
white-space: nowrap; |
||||||
border-radius: 4px; |
border-radius: 4px; |
||||||
color: #ffffff; |
color: #ffffff; |
||||||
font-size: 14px; |
font-size: 14px; |
||||||
font-weight: 500; |
font-weight: 500; |
||||||
line-height: 20px; |
line-height: 20px; |
||||||
transition: background 0.1s ease-in; |
transition: background 0.1s ease-in; |
||||||
cursor: default; |
cursor: default; |
||||||
|
|
||||||
&:hover { |
&:hover { |
||||||
background: #5d6c7b; |
background: #5d6c7b; |
||||||
transition: background 0.1s ease-in; |
transition: background 0.1s ease-in; |
||||||
} |
} |
||||||
|
|
||||||
&.has-children-label { |
&.has-children-label { |
||||||
cursor: pointer; |
cursor: pointer; |
||||||
} |
|
||||||
} |
} |
||||||
} |
} |
||||||
} |
} |
||||||
|
} |
||||||
|
|
||||||
.horizontal .custom-org-node { |
.horizontal .custom-org-node { |
||||||
padding: 14px 19px; |
padding: 14px 19px; |
||||||
} |
|
||||||
} |
} |
||||||
|
} |
||||||
</style> |
</style> |
||||||
|
|||||||
@ -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> |
|
||||||
@ -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> |
||||||
Loading…
Reference in new issue