功能变化: 优化用户管理中部门下拉无数据bug

pull/78/head
李强 2022-11-04 15:51:11 +08:00
parent ccee4ada56
commit b308877a14
8 changed files with 479 additions and 5 deletions

View File

@ -9,4 +9,5 @@ Vue.component('d2-icon-svg', () => import('./d2-icon-svg/index.vue'))
Vue.component('importExcel', () => import('./importExcel/index.vue'))
Vue.component('foreignKey', () => import('./foreign-key/index.vue'))
Vue.component('manyToMany', () => import('./many-to-many/index.vue'))
Vue.component('d2p-tree-selector', () => import('./tree-selector/lib/tree-selector.vue'))
Vue.component('dept-format', () => import('./dept-format/lib/dept-format.vue'))

View File

@ -0,0 +1,27 @@
## 树形选择组件
示例地址http://preview.d2-crud-plus.docmirror.cn/D2CrudPlusExample/index.html#/demo/form/area
### 1. 引入
```javascript
// 先引入d2-crud,d2-crud-plus
// 参考 https://github.com/greper/d2-crud-plus/blob/master/packages/d2-crud-plus-example/src/business/lib/index.js
// 然后引入树形组件
import { D2pTreeSelector } from 'd2p-extends'
Vue.use(D2pTreeSelector)
```
### 2. crud
```javascript
export const crudOptions = {
columns: [
{
title: '树形选择',
key: 'pcaTree',
type: 'tree-selector',
dict: {url:'xxx',isTree:true}
}
]
}
```
### 3. 效果
http://preview.d2-crud-plus.docmirror.cn/D2CrudPlusExample/index.html#/demo/form/area

View File

@ -0,0 +1,13 @@
import { d2CrudPlus } from 'd2-crud-plus'
import types from './types'
function install (Vue) {
Vue.component('d2p-tree-selector', () => import('./lib/tree-selector'))
if (d2CrudPlus != null) {
d2CrudPlus.util.columnResolve.addTypes(types)
}
}
export default {
install
}

View File

@ -0,0 +1,424 @@
<template>
<div class="d2p-tree-selector">
<div class="el-cascader el-cascader--default" :class="{'is-disabled':disabled}" @click="openDialog">
<div class="el-input el-input--default el-input--suffix" :class="{'is-disabled':disabled}" >
<el-input ref="reference" :disabled="disabled" :placeholder="value ? '' : placeholder"/>
<span class="el-input__suffix">
<span class="el-input__suffix-inner">
<i class="el-input__icon el-icon-arrow-down" @click="openDialog"/>
</span>
</span>
</div>
<div class="el-cascader__tags" ref="tags">
<transition-group @after-leave="resetInputHeight" >
<el-tag
v-for="item in selected"
:key="getValueKey(item)"
:closable="clearable"
:size="collapseTagSize"
:hit="false"
type="info"
@close="itemClosed(item)"
disable-transitions>
<span class="el-select__tags-text">{{ getValueLabel(item) }}</span>
</el-tag>
</transition-group>
</div>
</div>
<el-dialog custom-class="d2p-tree-selector-dialog"
:title="dialogTitle"
:visible.sync="dialogVisible"
width="30%" append-to-body>
<div class="tree-wrapper">
<div v-if="treeFilter" class="filter-bar" style="padding-bottom: 20px">
<el-input
prefix-icon="el-icon-search"
:placeholder="filterPlaceholder"
v-model="filterText" size="small" >
</el-input>
</div>
<div class="tree-body">
<el-tree
:data="_options"
@check-change="handleCheckChange"
@current-change="handleCurrentChange"
:filterNodeMethod="filterNode"
v-bind="_elProps"
ref="elTree">
</el-tree>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">{{cancelText}}</el-button>
<el-button type="primary" @click="selectSubmit">{{confirmText}}</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import lodash from 'lodash'
import { d2CrudPlus } from 'd2-crud-plus'
import log from 'd2p-extends/src/utils/util.log'
// import {D2pTreeSelector} from 'd2p-extends'
export default {
name: 'd2p-tree-selector',
mixins: [d2CrudPlus.input, d2CrudPlus.inputDict],
props: {
//
value: {
type: [Number, String, Boolean, Array, Object]
},
// valuenodes nodes
filter: {
type: Function,
require: false
},
//
placeholder: {
type: String,
required: false,
default: '请选择'
},
// placeholder
filterPlaceholder: {
type: String,
default: '输入关键字进行过滤'
},
dialogTitle: {
type: String,
default: '选择'
},
cancelText: {
type: String,
default: '取消'
},
confirmText: {
type: String,
default: '确定'
},
// elProps.filterNodeMethod
treeFilter: {
type: Boolean,
require: false,
default: true
},
// false
multiple: {
type: Boolean,
default: true
},
//
ignoreFullCheckedChildren: { type: Boolean, default: true },
//
leafOnly: { type: Boolean, default: false },
//
includeHalfChecked: { type: Boolean, default: false },
// el-tree
elProps: {
type: Object
},
/**
* 是否可以清除
*/
clearable: {
type: Boolean,
default: true
},
//
dict: {
type: Object,
require: false
}
},
data () {
return {
currentValue: undefined,
collapseTags: false,
selected: [],
dialogVisible: false,
filterText: undefined
}
},
created () {
// if (this.dict) {
// this.dict = d2CrudPlus.util.dict.mergeDefault(this.dict, true)
// }
// this.initData()
},
computed: {
_elProps () {
const defaultElProps = {
showCheckbox: this.multiple,
highlightCurrent: !this.multiple,
props: {}
}
if (this.dict != null) {
if (this.dict.label != null) { defaultElProps.props.label = this.dict.label }
if (this.dict.value != null) { defaultElProps.props.value = this.dict.value }
if (this.dict.children != null) { defaultElProps.props.children = this.dict.children }
}
defaultElProps.nodeKey = defaultElProps.props.value
lodash.merge(defaultElProps, this.elProps)
return defaultElProps
},
collapseTagSize () {
return ['small', 'mini'].indexOf(this.selectSize) > -1
? 'mini'
: 'small'
}
},
watch: {
filterText (val) {
this.$refs.elTree.filter(val)
}
},
methods: {
// initData () {
// d2CrudPlus.util.dict.get(this.dict).then(ret => {
// this.$set(this, 'data', ret)
// this.setValue(this.value)
// })
// },
onDictLoaded () {
log.debug('onDictLoaded', this.dict, this.value)
this.setValue(this.value)
},
setValue (value) {
log.debug('setValue:', this.currentValue, this.value, this._options)
if (this.currentValue === this.value) {
return
}
let arrValue = value
if (value == null) {
this.selected = []
}
if (!(arrValue instanceof Array)) {
arrValue = [arrValue]
}
if (this.dict && this.dict.getNodes) {
log.debug('getNodes:', arrValue)
this.dict.getNodes(arrValue).then(nodes => {
this.selectedNodes(nodes, value)
})
} else {
const nodes = []
if (this._options == null || this._options.length === 0) {
return
}
for (const item of arrValue) {
const data = this._options
const node = d2CrudPlus.util.dict.getByValue(item, data, this.dict)
if (node != null) {
nodes.push(node)
}
}
this.selectedNodes(nodes, value)
}
},
selectedNodes (nodes, value) {
const selected = []
for (const node of nodes) {
node.id = node[this.dict.value]
selected.push(node)
}
log.debug('selected:', selected)
this.$set(this, 'selected', selected)
this.resetInputHeight()
},
handleCheckChange (event) {
this.$emit('check-change', event)
},
handleCurrentChange (event) {
this.$emit('current-change', event)
},
openDialog () {
if (this.disabled) {
return
}
this.dialogVisible = true
setTimeout(() => {
if (this.selected != null) {
const ids = this.selected.map(item => item[this._elProps.props.value])
ids.forEach(id => {
const current = this.$refs.elTree.store.nodesMap[id]
if (current != null) {
this.doExpandParent(current)
}
})
this.$nextTick(() => {
if (this.multiple) {
this.$refs.elTree.setCheckedKeys(ids, this.leafOnly)
} else if (ids.length > 0) {
this.$refs.elTree.setCurrentKey(ids[0])
}
})
}
}, 1)
},
doExpandParent (node) {
if (node.parent != null) {
this.doExpandParent(node.parent)
}
node.expanded = true
},
getValueKey (item) {
if (this._elProps.props.value != null) {
return item[this._elProps.props.value]
} else {
return item.value
}
},
getValueLabel (item) {
if (this._elProps.props.label != null) {
return item[this._elProps.props.label]
} else {
return item.label
}
},
getValueChildren (item) {
let children = 'children'
if (this._elProps.props.children != null) {
children = this._elProps.props.children
}
return item[children]
},
selectSubmit () {
const nodes = this.refreshSelected()
this.dialogVisible = false
this.doValueInputChanged(nodes)
},
doValueInputChanged (nodes) {
let values = this.formatValue(nodes)
this.resetInputHeight()
if (!this.multiple) {
values = values && values.length > 0 ? values[0] : undefined
}
this.currentValue = values
if (this.dispatch) {
this.dispatch('ElFormItem', 'el.form.blur')
}
this.$emit('input', values)
},
itemClosed (item) {
const newNodes = lodash.without(this.selected, item)
console.log('new value', item, newNodes)
this.$set(this, 'selected', newNodes)
this.doValueInputChanged(newNodes)
},
refreshSelected () {
let nodes = null
if (this.multiple) {
nodes = this.$refs.elTree.getCheckedNodes(this.leafOnly, this.includeHalfChecked)
} else {
const node = this.$refs.elTree.getCurrentNode()
if (node == null) {
nodes = []
} else {
nodes = [node]
}
}
if (this.ignoreFullCheckedChildren) {
nodes = this.filterFullCheckedChildren(nodes)
}
if (this.filter != null) {
nodes = this.filter(nodes)
}
log.debug('selected', this.selected)
this.$set(this, 'selected', nodes)
return nodes
},
resetInputHeight () {
if (this.collapseTags && !this.filterable) return
this.$nextTick(() => {
if (!this.$refs.reference) return
const inputChildNodes = this.$refs.reference.$el.childNodes
const input = [].filter.call(inputChildNodes, item => item.tagName === 'INPUT')[0]
const tags = this.$refs.tags
const sizeInMap = this.initialInputHeight || 40
const height = this.selected.length === 0
? sizeInMap + 'px'
: Math.max(
tags ? (tags.clientHeight + (tags.clientHeight > sizeInMap ? 6 : 0)) : 0,
sizeInMap
) + 'px'
input.style.height = height
if (this.visible && this.emptyText !== false) {
this.broadcast('ElSelectDropdown', 'updatePopper')
}
})
},
filterFullCheckedChildren (nodes) {
const ignored = new Set()
for (const item of nodes) {
const children = this.getValueChildren(item)
if (children != null) {
for (const child of children) {
ignored.add(this.getValueKey(child))
}
}
}
const values = []
for (const item of nodes) {
const key = this.getValueKey(item)
if (!ignored.has(key)) {
values.push(item)
}
}
return values
},
formatValue (nodes) {
const values = []
for (const item of nodes) {
values.push(this.getValueKey(item))
}
return values
},
filterNode (value, data) {
if (!value) return true
return this.getValueLabel(data).indexOf(value) !== -1
},
onChange (value) {
this.$emit('change', value)
if (this.dispatch) {
this.dispatch('ElFormItem', 'el.form.blur')
}
}
}
}
</script>
<style lang="scss">
.d2p-tree-selector{
width: 100%;
.el-cascader{
width: 100%;
}
.is-disabled .el-tag__close.el-icon-close {
display: none;
}
}
.d2p-tree-selector-dialog{
&.el-dialog{
max-height:70vh;
display: flex;
flex-direction: column;
.el-dialog__body{
flex:1;
overflow-y: auto;
}
.el-dialog__header{
padding: 20px 20px 20px;
border-bottom: 1px solid #eee;
}
.el-dialog__footer {
padding: 10px 20px 10px;
border-top: 1px solid #eee;
}
}
}
</style>

View File

@ -0,0 +1,6 @@
export default {
'tree-selector': {
form: { component: { name: 'd2p-tree-selector', props: { } } },
component: { name: 'values-format', props: {} }
}
}

View File

@ -147,5 +147,9 @@ export default {
})
}
}
},
'tree-selector': {
form: { component: { name: 'd2p-tree-selector', props: { } } },
component: { name: 'values-format', props: {} }
}
}

View File

@ -8,7 +8,6 @@ import {
D2pFileUploader,
D2pFullEditor,
D2pIconSelector,
D2pTreeSelector,
D2pUploader
} from 'd2p-extends' // 源码方式引入上传组件支持懒加载
// http请求
@ -51,9 +50,9 @@ Vue.use(d2CrudPlus, {
method: 'get'
}).then(ret => {
if (dict.isTree) {
return XEUtils.toArrayTree(ret.data.data, { parentKey: 'parent', strict: true })
return XEUtils.toArrayTree(ret.data.data || ret.data, { parentKey: 'parent', strict: false })
} else {
return ret.data.data
return ret.data.data || ret.data
}
})
},
@ -101,7 +100,7 @@ Vue.use(d2CrudPlus, {
})
// 安装扩展插件
Vue.use(D2pTreeSelector)
// Vue.use(D2pTreeSelector)
Vue.use(D2pAreaSelector)
Vue.use(D2pIconSelector)
Vue.use(D2pFullEditor, {

View File

@ -179,7 +179,7 @@ export const crudOptions = (vm) => {
minWidth: 140,
type: 'tree-selector',
dict: {
cache: false,
cache: true,
isTree: true,
url: '/api/system/dept/all_dept/',
value: 'id', // 数据字典中value字段的属性名