增加树选择组件

单据流程条修改
自定义的el-select移除collapse-tag选项
master
toesbieya 6 years ago
parent e7aebfc71a
commit aa66f586cf
  1. 55
      vue/element-ui-personal/components/Select/Select.vue
  2. 4
      vue/src/bizComponents/document/DocSteps.vue
  3. 6
      vue/src/components/RegionSelector/Tree/TreeDialog.vue
  4. 214
      vue/src/components/TreeSelect/index.vue
  5. 6
      vue/src/router/constant/modules/example.js
  6. 2
      vue/src/router/util.js
  7. 2
      vue/src/views/example/components/regionSelectorExample.vue
  8. 31
      vue/src/views/example/components/treeSelectExample.vue
  9. 57
      vue/src/views/purchase/order/components/CategorySelector.vue

@ -12,28 +12,7 @@
:style="{ 'max-width': inputWidth - 32 + 'px', width: '100%' }"
>
<span v-if="collapseTags && selected.length">
<el-tag
:closable="!selectDisabled"
:size="collapseTagSize"
:hit="selected[0].hitState"
type="info"
@close="deleteTag($event, selected[0])"
disable-transitions
>
<span class="el-select__tags-text">{{ selected[0].currentLabel }}</span>
</el-tag>
<el-tag
v-if="selected.length > 1"
:closable="false"
:size="collapseTagSize"
type="info"
disable-transitions
>
<span class="el-select__tags-text">+ {{ selected.length - 1 }}</span>
</el-tag>
</span>
<transition-group @after-leave="resetInputHeight" v-if="!collapseTags">
<transition-group @after-leave="resetInputHeight">
<el-tag
v-for="item in selected"
:key="getValueKey(item)"
@ -204,7 +183,6 @@
defaultFirstOption: Boolean,
reserveKeyword: Boolean,
valueKey: {type: String, default: 'value'},
collapseTags: Boolean,
popperAppendToBody: Boolean
},
@ -439,8 +417,7 @@
this.hoverIndex = -1
if (this.multiple && this.filterable) {
this.$nextTick(() => {
const length = this.$refs.input.value.length * 15 + 20
this.inputLength = this.collapseTags ? Math.min(50, length) : length
this.inputLength = this.$refs.input.value.length * 15 + 20
this.managePlaceholder()
this.resetInputHeight()
})
@ -483,25 +460,21 @@
},
getOption(value) {
let option
const isObject = Object.prototype.toString.call(value).toLowerCase() === '[object object]'
const isNull = Object.prototype.toString.call(value).toLowerCase() === '[object null]'
const isUndefined = Object.prototype.toString.call(value).toLowerCase() === '[object undefined]'
const type = v => Object.prototype.toString.call(v).toLowerCase()
const isObject = type(value) === '[object object]'
const isNull = type(value) === '[object null]'
const isUndefined = type(value) === '[object undefined]'
for (let i = this.cachedOptions.length - 1; i >= 0; i--) {
const cachedOption = this.cachedOptions[i]
const isEqual = isObject
? getValueByPath(cachedOption.value, this.valueKey) === getValueByPath(value, this.valueKey)
: cachedOption.value === value
if (isEqual) {
option = cachedOption
break
if (isEqual) return cachedOption
}
}
if (option) return option
const label = (!isObject && !isNull && !isUndefined)
? value : ''
let newOption = {
const label = (!isObject && !isNull && !isUndefined) ? value : ''
const newOption = {
value: value,
currentLabel: label
}
@ -533,9 +506,7 @@
})
}
this.selected = result
this.$nextTick(() => {
this.resetInputHeight()
})
this.$nextTick(() => this.resetInputHeight())
},
handleFocus(event) {
@ -618,7 +589,7 @@
},
resetInputHeight() {
if (this.collapseTags && !this.filterable) return
if (!this.filterable) return
this.$nextTick(() => {
if (!this.$refs.reference) return
let inputChildNodes = this.$refs.reference.$el.childNodes
@ -747,7 +718,7 @@
},
deleteTag(event, tag) {
let index = this.selected.indexOf(tag)
const index = this.selected.indexOf(tag)
if (index > -1 && !this.selectDisabled) {
const value = this.value.slice()
value.splice(index, 1)

@ -11,9 +11,9 @@
render(h, context) {
const {status, finish} = context.props
const active = status === 2 && (finish === undefined || finish === 1) ? 3 : status
const active = status === 2 && (finish === undefined || finish === 2) ? 4 : status
return (
<el-steps align-center active={active + 1}>
<el-steps align-center active={active}>
<el-step title="填写单据"/>
<el-step title="提交单据,等待审核"/>
<el-step title="审核通过"/>

@ -1,5 +1,5 @@
<script type="text/jsx">
import {store,init} from '../store'
import {store, init} from '../store'
import FormDialog from "@/components/FormDialog"
import {createLimitTree, getNodeId} from "@/utils/tree"
@ -56,7 +56,7 @@
render() {
return (
<dialog-form
<form-dialog
v-model={this.visible}
directives={[{name: 'drag-dialog'}]}
class="tree-dialog"
@ -77,7 +77,7 @@
</span>
)}
</el-tree>
</dialog-form>
</form-dialog>
)
}
}

@ -0,0 +1,214 @@
<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"
>
<div class="tree-select-container" slot="empty">
<el-input
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"
>
<slot slot-scope="{node,data}">
<span :class="{'el-tree-node__label':true,'is-disabled':node.disabled}">{{node.label}}</span>
</slot>
</el-tree>
</div>
</el-select>
</template>
<script>
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: {
default() {
return {
children: 'children',
label: 'label',
disabled: 'disabled'
}
}
},
accordion: {type: Boolean, default: true},
popperAppendToBody: Boolean,
automaticDropdown: Boolean
},
data() {
return {
currentNode: null,
search: ''
}
},
computed: {
defaultCheckedKeys() {
return this.multiple ? this.value : []
}
},
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
}
//
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)
},
onSearch(v) {
this.$refs.tree.filter(v)
},
nodeClick(data, node) {
if (this.multiple) return
if (node.disabled) {
return this.$refs.tree.setCurrentKey()
}
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()
}
}
},
created() {
this.onSearch = debounce(this.onSearch)
},
mounted() {
this.currentNode = this.$refs.tree.getCurrentNode()
this.initSelectOptions()
}
}
</script>
<style lang="scss">
.tree-select-container {
padding: 8px;
> .el-input {
margin-bottom: 8px;
}
.el-tree-node__label.is-disabled {
background-color: #edf2fc;
border-color: #dcdfe6;
cursor: not-allowed;
}
}
</style>

@ -77,6 +77,12 @@ const router = {
name: 'regionSelectorExample',
component: lazyLoadView(import('@/views/example/components/regionSelectorExample')),
meta: {title: '行政区划选择'}
},
{
path: 'treeSelectExample',
name: 'treeSelectExample',
component: lazyLoadView(import('@/views/example/components/treeSelectExample')),
meta: {title: '树选择'}
}
]
},

@ -6,7 +6,7 @@ import PageSkeleton from "@/components/Skeleton/PageSkeleton"
//拼接页面标题
export function setPageTitle(route) {
const pageTitle = route.meta.title
document.title= pageTitle ? `${pageTitle} - ${title}` : title
document.title = pageTitle ? `${pageTitle} - ${title}` : title
}
//确定路由的标题

@ -1,5 +1,5 @@
<template>
<div style="width: 500px">
<div style="width: 300px">
<div class="tip-row">
行政区划选择<a href="https://github.com/modood/Administrative-divisions-of-China" target="_blank">数据由此获取</a>
</div>

@ -0,0 +1,31 @@
<template>
<div>
<tree-select v-model="value" :data="data" multiple filterable :filter-method="filter"/>
</div>
</template>
<script>
import TreeSelect from '@/components/TreeSelect'
export default {
name: "treeSelectExample",
components: {TreeSelect},
data() {
return {
value: [],
data: [
{id: 1, label: '一级 1', children: [{id: 2, label: '二级 1-1'}, {id: 3, label: '二级 1-2'}]},
{id: 4, label: '一级 2', children: [{id: 5, label: '二级 2-1'}]},
],
}
},
methods: {
filter(v, data) {
return data.label.includes(v)
}
}
}
</script>

@ -1,54 +1,81 @@
<template>
<el-select :value="value" :disabled="disabled" size="small" popper-append-to-body @change="change" @input="emit">
<el-option
v-for="item in selectableCategories"
:key="item.id"
:label="item.name"
:value="item.id"
<tree-select
:value="value"
:data="tree"
:disabled="disabled"
:props="props"
size="small"
popper-append-to-body
@change="change"
@input="emit"
/>
</el-select>
</template>
<script>
import TreeSelect from '@/components/TreeSelect'
import {getAll} from "@/api/system/category"
import {isEmpty} from "@/utils"
import {elError} from "@/utils/message"
export default {
name: "CategorySelector",
components: {TreeSelect},
props: {
value: Number,
value: {required: true},
disabled: Boolean,
selected: {type: Array, default: () => []}
},
data() {
return {
props: {
label: 'name',
children: 'children',
disabled: '_disabled'
}
}
},
computed: {
tree() {
return this.$store.state.dataCache.categoryTree
},
selectableCategories() {
const cache = this.$store.state.dataCache
if (!cache.categories) return []
return cache.categories.filter(i => i.type === 1)
return this.$store.state.dataCache.categories.filter(i => i.type === 1)
},
},
methods: {
init() {
if (this.selectableCategories.length > 0) return
if (this.tree.length === 0) {
getAll()
.then(data => this.$store.commit('dataCache/categories', data))
.then(data => {
data.forEach(i => i._disabled = i.type === 0)
this.$store.commit('dataCache/categories', data)
})
}
},
change(v) {
if (!v || this.selected.length <= 0) return
if (isEmpty(v) || this.selected.length <= 0) return
if (this.selected.includes(v)) {
elError('该分类已选择')
this.emit(null)
}
},
emit(v) {
this.$emit('input', v)
if (v === 0 || v) {
if (!isEmpty(v)) {
let item = this.selectableCategories.find(i => i.id === v)
this.$emit('get-name', item ? item.name : null)
}
else this.$emit('get-name', null)
}
},
mounted() {
this.init()
}

Loading…
Cancel
Save