功能变化: 优化用户管理中部门下拉无数据bug
parent
ccee4ada56
commit
b308877a14
|
@ -9,4 +9,5 @@ Vue.component('d2-icon-svg', () => import('./d2-icon-svg/index.vue'))
|
||||||
Vue.component('importExcel', () => import('./importExcel/index.vue'))
|
Vue.component('importExcel', () => import('./importExcel/index.vue'))
|
||||||
Vue.component('foreignKey', () => import('./foreign-key/index.vue'))
|
Vue.component('foreignKey', () => import('./foreign-key/index.vue'))
|
||||||
Vue.component('manyToMany', () => import('./many-to-many/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'))
|
Vue.component('dept-format', () => import('./dept-format/lib/dept-format.vue'))
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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]
|
||||||
|
},
|
||||||
|
// 过滤,value中的nodes过滤方法 参数为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>
|
|
@ -0,0 +1,6 @@
|
||||||
|
export default {
|
||||||
|
'tree-selector': {
|
||||||
|
form: { component: { name: 'd2p-tree-selector', props: { } } },
|
||||||
|
component: { name: 'values-format', props: {} }
|
||||||
|
}
|
||||||
|
}
|
|
@ -147,5 +147,9 @@ export default {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
'tree-selector': {
|
||||||
|
form: { component: { name: 'd2p-tree-selector', props: { } } },
|
||||||
|
component: { name: 'values-format', props: {} }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ import {
|
||||||
D2pFileUploader,
|
D2pFileUploader,
|
||||||
D2pFullEditor,
|
D2pFullEditor,
|
||||||
D2pIconSelector,
|
D2pIconSelector,
|
||||||
D2pTreeSelector,
|
|
||||||
D2pUploader
|
D2pUploader
|
||||||
} from 'd2p-extends' // 源码方式引入,上传组件支持懒加载
|
} from 'd2p-extends' // 源码方式引入,上传组件支持懒加载
|
||||||
// http请求
|
// http请求
|
||||||
|
@ -51,9 +50,9 @@ Vue.use(d2CrudPlus, {
|
||||||
method: 'get'
|
method: 'get'
|
||||||
}).then(ret => {
|
}).then(ret => {
|
||||||
if (dict.isTree) {
|
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 {
|
} 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(D2pAreaSelector)
|
||||||
Vue.use(D2pIconSelector)
|
Vue.use(D2pIconSelector)
|
||||||
Vue.use(D2pFullEditor, {
|
Vue.use(D2pFullEditor, {
|
||||||
|
|
|
@ -179,7 +179,7 @@ export const crudOptions = (vm) => {
|
||||||
minWidth: 140,
|
minWidth: 140,
|
||||||
type: 'tree-selector',
|
type: 'tree-selector',
|
||||||
dict: {
|
dict: {
|
||||||
cache: false,
|
cache: true,
|
||||||
isTree: true,
|
isTree: true,
|
||||||
url: '/api/system/dept/all_dept/',
|
url: '/api/system/dept/all_dept/',
|
||||||
value: 'id', // 数据字典中value字段的属性名
|
value: 'id', // 数据字典中value字段的属性名
|
||||||
|
|
Loading…
Reference in New Issue