增加tree-select组件

This commit is contained in:
zhigang.li 2019-01-24 16:29:14 +08:00
parent 07726d1ec8
commit 65da8286d7
15 changed files with 436 additions and 31 deletions

52
package-lock.json generated
View File

@ -1680,9 +1680,9 @@
"dev": true
},
"async-validator": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/async-validator/-/async-validator-1.10.0.tgz",
"integrity": "sha512-tjkUJ3OXURZbm1nrlU2QtH0XJe4YvhN1J9AYiKFN9ODBqt0AFIE6YZdZByrWG2SidPUOOK5KIAsqskqFj/43ZQ==",
"version": "1.10.1",
"resolved": "http://bnpm.byted.org/async-validator/download/async-validator-1.10.1.tgz",
"integrity": "sha1-QemwufjnGebt+UY3LwGKlYwucPM=",
"requires": {
"babel-runtime": "6.x"
}
@ -1961,7 +1961,7 @@
},
"batch-processor": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/batch-processor/-/batch-processor-1.0.0.tgz",
"resolved": "http://bnpm.byted.org/batch-processor/download/batch-processor-1.0.0.tgz",
"integrity": "sha1-dclcMrdI4IUNEMKxaPa9vpiRrOg="
},
"bcrypt-pbkdf": {
@ -3528,8 +3528,8 @@
},
"deepmerge": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz",
"integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA=="
"resolved": "http://bnpm.byted.org/deepmerge/download/deepmerge-2.2.1.tgz",
"integrity": "sha1-XT/yKgHAD2RUBaL7wX0HeKGAEXA="
},
"default-gateway": {
"version": "2.7.2",
@ -3910,8 +3910,8 @@
},
"element-resize-detector": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/element-resize-detector/-/element-resize-detector-1.2.0.tgz",
"integrity": "sha512-UmhNB8sIJVZeg56gEjgmMd6p37sCg8j8trVW0LZM7Wzv+kxQ5CnRHcgRKBTB/kFUSn3e7UP59kl2V2U8Du1hmg==",
"resolved": "http://bnpm.byted.org/element-resize-detector/download/element-resize-detector-1.2.0.tgz",
"integrity": "sha1-YzRP1vTl7P9vAY0Cfheygf1Pozg=",
"requires": {
"batch-processor": "1.0.0"
}
@ -6724,18 +6724,18 @@
"dev": true
},
"iview": {
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/iview/-/iview-3.1.5.tgz",
"integrity": "sha512-lFom+Do/mJ4ejdu/+o/kCMWVGBpe85pIho7AsrOge/7IPuP5UbdSPu720qaEe4lC2T1AFczD74LlzqZ/mik0FA==",
"version": "3.2.2",
"resolved": "http://bnpm.byted.org/iview/download/iview-3.2.2.tgz",
"integrity": "sha1-W32NtdHqcRzDn6rmBFeqA1TLUsg=",
"requires": {
"async-validator": "^1.8.2",
"deepmerge": "^2.1.0",
"element-resize-detector": "^1.1.14",
"async-validator": "^1.10.0",
"deepmerge": "^2.2.1",
"element-resize-detector": "^1.2.0",
"js-calendar": "^1.2.3",
"lodash.throttle": "^4.1.1",
"popper.js": "^1.14.1",
"popper.js": "^1.14.6",
"tinycolor2": "^1.4.1",
"v-click-outside-x": "^3.5.0"
"v-click-outside-x": "^3.5.6"
}
},
"iview-area": {
@ -6785,8 +6785,8 @@
},
"js-calendar": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/js-calendar/-/js-calendar-1.2.3.tgz",
"integrity": "sha512-dAA1/Zbp4+c5E+ARCVTIuKepXsNLzSYfzvOimiYD4S5eeP9QuplSHLcdhfqFSwyM1o1u6ku6RRRCyaZ0YAjiBw=="
"resolved": "http://bnpm.byted.org/js-calendar/download/js-calendar-1.2.3.tgz",
"integrity": "sha1-pYOwZEtOaVujlPNE0QPbzHp6fT4="
},
"js-cookie": {
"version": "2.2.0",
@ -7742,7 +7742,7 @@
},
"lodash.throttle": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
"resolved": "http://bnpm.byted.org/lodash.throttle/download/lodash.throttle-4.1.1.tgz",
"integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ="
},
"lodash.transform": {
@ -8988,8 +8988,8 @@
},
"popper.js": {
"version": "1.14.6",
"resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.14.6.tgz",
"integrity": "sha512-AGwHGQBKumlk/MDfrSOf0JHhJCImdDMcGNoqKmKkU+68GFazv3CQ6q9r7Ja1sKDZmYWTckY/uLyEznheTDycnA=="
"resolved": "http://bnpm.byted.org/popper.js/download/popper.js-1.14.6.tgz",
"integrity": "sha1-qyDdTt+SiLiztlMcR8NhEHtgtLA="
},
"portfinder": {
"version": "1.0.20",
@ -11458,7 +11458,7 @@
},
"tinycolor2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz",
"resolved": "http://bnpm.byted.org/tinycolor2/download/tinycolor2-1.4.1.tgz",
"integrity": "sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g="
},
"tmp": {
@ -11969,16 +11969,16 @@
"dev": true
},
"v-click-outside-x": {
"version": "3.5.6",
"resolved": "https://registry.npmjs.org/v-click-outside-x/-/v-click-outside-x-3.5.6.tgz",
"integrity": "sha512-41j5mdZ8NzGJM5Or0BTADKU1B0vBi5a29g9ieVNTorg3FTiep6zOXjZeLpMmSH/koon1JP6S5uvwI2OfSVCgSA=="
"version": "3.7.1",
"resolved": "http://bnpm.byted.org/v-click-outside-x/download/v-click-outside-x-3.7.1.tgz",
"integrity": "sha1-qgPqoOQeRMtSB9z4bC2fDdZAhME="
},
"v-org-tree": {
"version": "1.0.6",
"resolved": "http://registry.npm.taobao.org/v-org-tree/download/v-org-tree-1.0.6.tgz",
"integrity": "sha1-uh4EL9bExFb9/FmFtlvClFpAaWc=",
"requires": {
"clonedeep": "2.0.0"
"clonedeep": "^2.0.0"
}
},
"validate-npm-package-license": {

View File

@ -19,7 +19,7 @@
"dayjs": "^1.7.7",
"echarts": "^4.0.4",
"html2canvas": "^1.0.0-alpha.12",
"iview": "^3.1.3",
"iview": "^3.2.2",
"iview-area": "^1.5.17",
"js-cookie": "^2.2.0",
"simplemde": "^1.11.2",

View File

@ -42,3 +42,10 @@ export const getOrgData = () => {
method: 'get'
})
}
export const getTreeSelectData = () => {
return axios.request({
url: 'get_tree_select_data',
method: 'get'
})
}

View File

@ -0,0 +1 @@
export { default } from './tree-select.vue'

View File

@ -0,0 +1,135 @@
<template>
<Tree
:data="data"
@on-check-change="handleCheckSelect"
v-on="parent.$listeners"
v-bind="parent.$attrs"
:load-data="loadDataCallback"
show-checkbox
></Tree>
</template>
<script>
import Emitter from 'iview/src/mixins/emitter.js'
const arrayEqual = (arr1, arr2) => {
//
if (arr1.length !== arr2.length) {
return false
} else {
//
for (let i = 0; i < arr1.length; i++) {
if (arr1[i] !== arr2[i]) {
return false
}
}
return true
}
}
export default {
name: 'TreeSelectTree',
mixins: [Emitter],
props: {
data: {
type: Array,
default: () => []
},
selectedArray: {
type: Array,
default: () => []
},
loadData: Function
},
data () {
return {
flatDic: {},
checkedArray: []
}
},
inject: ['parent'],
computed: {
expandAll () {
return this.parent.$attrs['expand-all']
}
},
watch: {
data (newData, oldVal) {
this.updateFlagDic(newData)
let selectArray = []
this.selectedArray.forEach(id => {
if (id in this.flatDic) selectArray.push(id)
})
this.$emit('on-check', selectArray.map(id => this.flatDic[id]))
if (this.expandAll) this.checkData(newData, false, true)
},
selectedArray (newVal, oldVal) {
if (arrayEqual(newVal, oldVal)) return
const filtedNewVal = newVal.filter(id => id in this.flatDic)
this.$emit('on-check', filtedNewVal.map(id => this.flatDic[id]))
this.$emit('on-clear')
this.$nextTick(() => {
this.checkData(this.data, true)
})
}
},
methods: {
checkEmit (value, label) {
this.dispatch('iSelect', 'on-select-selected', {
value,
label
})
this.$emit('on-select-selected', {
value,
label
})
},
updateFlagDic (newData) {
let newFlagDic = {}
this.setFlagDic(newData, item => {
newFlagDic[item.id] = item
})
this.flatDic = newFlagDic
},
setFlagDic (data, callback) {
data.forEach(item => {
if (item.children && item.children.length) { this.setFlagDic(item.children, callback) }
callback(item)
})
},
handleCheckSelect (selectArray, selectItem) {
this.$emit('on-check', selectArray)
this.parent.$emit('on-change', selectArray)
},
checkData (data, emit, expandAll) {
data.forEach(item => {
if (this.selectedArray.includes(item.id)) {
this.$set(item, 'checked', true)
this.checkedArray.push(item)
if (emit) this.checkEmit(item.id, item.title)
} else this.$set(item, 'checked', false)
if (item.children && item.children.length) {
if (this.expandAll && expandAll) this.$set(item, 'expand', true)
this.checkData(item.children, emit, expandAll)
}
})
},
loadDataCallback (item, callback) {
this.loadData(item, data => {
return (() => {
callback(data)
this.updateFlagDic(this.data)
})(data)
})
}
},
mounted () {
this.checkData(this.data, false, true)
this.$nextTick(() => {
this.$emit('on-check', this.checkedArray)
})
}
}
</script>
<style></style>

View File

@ -0,0 +1,72 @@
<template>
<Select
ref="select"
class="tree-select"
v-bind="$attrs"
@on-change="handleChange"
multiple
>
<tree-select-tree-item
:selectedArray="value"
:data="data"
@on-clear="handleClear"
:load-data="loadData"
@on-check="handleTreeCheck"
></tree-select-tree-item>
</Select>
</template>
<script>
import Emitter from 'iview/src/mixins/emitter'
import TreeSelectTreeItem from './tree-select-tree.vue'
export default {
name: 'TreeSelect',
mixins: [Emitter],
components: {
TreeSelectTreeItem
},
props: {
value: {
type: Array,
default: () => []
},
data: {
type: Array,
default: () => []
},
loadData: Function
},
data () {
return {
isChangedByTree: true,
isInit: true
}
},
provide () {
return {
parent: this
}
},
methods: {
handleChange (selected) {
if (!this.isChangedByTree) this.$emit('input', selected)
this.isChangedByTree = false
},
handleTreeCheck (selectedArray) {
this.isChangedByTree = true
this.$emit('input', selectedArray.map(item => item.id))
},
handleClear () {
this.$refs.select.reset()
}
}
}
</script>
<style lang="less">
.tree-select {
.ivu-select-dropdown {
padding: 0 6px;
}
}
</style>

View File

@ -40,5 +40,6 @@ export default {
message_page: 'Message Center',
tree_table_page: 'Tree Table',
org_tree_page: 'Org Tree',
drag_drawer_page: 'Draggable Drawer'
drag_drawer_page: 'Draggable Drawer',
tree_select_page: 'Tree Selector'
}

View File

@ -40,5 +40,6 @@ export default {
message_page: '消息中心',
tree_table_page: '树状表格',
org_tree_page: '组织结构树',
drag_drawer_page: '可拖动抽屉'
drag_drawer_page: '可拖动抽屉',
tree_select_page: '树状下拉选择器'
}

View File

@ -40,5 +40,6 @@ export default {
message_page: '消息中心',
tree_table_page: '樹狀表格',
org_tree_page: '組織結構樹',
drag_drawer_page: '可拖動抽屜'
drag_drawer_page: '可拖動抽屜',
tree_select_page: '樹狀下拉選擇器'
}

View File

@ -1,6 +1,7 @@
import Mock from 'mockjs'
import { doCustomTimes } from '@/libs/util'
import orgData from './data/org-data'
import { treeData } from './data/tree-select'
const Random = Mock.Random
export const getTableData = req => {
@ -33,3 +34,7 @@ export const uploadImage = req => {
export const getOrgData = req => {
return orgData
}
export const getTreeSelectData = req => {
return treeData
}

View File

@ -0,0 +1,82 @@
export const treeData = [
{
id: 1,
title: '1',
children: [
{
id: 11,
title: '1-1',
loading: false,
children: [
// {
// id: 111,
// title: '1-1-1'
// },
// {
// id: 112,
// title: '1-1-2'
// },
// {
// id: 113,
// title: '1-1-3'
// },
// {
// id: 114,
// title: '1-1-4'
// }
]
},
{
id: 12,
title: '1-2',
children: [
{
id: 121,
title: '1-2-1'
}
]
}
]
}
]
export const newTreeData = [
{
id: 'a',
title: 'a',
children: [
{
id: 'a1',
title: 'a-1',
children: [
{
id: 112,
title: '1-1-2'
},
{
id: 'a12',
title: 'a-1-2'
},
{
id: 'a13',
title: 'a-1-3'
},
{
id: 'a14',
title: 'a-1-4'
}
]
},
{
id: 'a2',
title: 'a-2',
children: [
{
id: 'a21',
title: 'b-2-1'
}
]
}
]
}
]

View File

@ -1,6 +1,6 @@
import Mock from 'mockjs'
import { login, logout, getUserInfo } from './login'
import { getTableData, getDragList, uploadImage, getOrgData } from './data'
import { getTableData, getDragList, uploadImage, getOrgData, getTreeSelectData } from './data'
import { getMessageInit, getContentByMsgId, hasRead, removeReaded, restoreTrash, messageCount } from './user'
// 配置Ajax请求延时可用来测试网络延迟大时项目中一些效果
@ -23,5 +23,6 @@ Mock.mock(/\/message\/remove_readed/, removeReaded)
Mock.mock(/\/message\/restore/, restoreTrash)
Mock.mock(/\/message\/count/, messageCount)
Mock.mock(/\/get_org_data/, getOrgData)
Mock.mock(/\/get_tree_select_data/, getTreeSelectData)
export default Mock

View File

@ -107,6 +107,15 @@ export default [
},
component: Main,
children: [
{
path: 'tree_select_page',
name: 'tree_select_page',
meta: {
icon: 'md-arrow-dropdown-circle',
title: '树状下拉选择器'
},
component: () => import('@/view/components/tree-select/index.vue')
},
{
path: 'count_to_page',
name: 'count_to_page',

0
src/test.js Normal file
View File

View File

@ -0,0 +1,90 @@
<template>
<div>
<tree-select
v-model="treeSelected"
style="width: 300px;"
check-strictly
:expand-all="true"
:load-data="loadData"
@on-change="handleTreeSelectChange"
@on-toggle-expand="handleTreeSelectExpand"
@on-check-change="handleTreeSelectCheckChange"
@on-select-change="handleTreeSelectClick"
:data="treeData"
></tree-select>
<Button @click="changeTreeSelectData">更新选中数据</Button>
<Button @click="changeTreeData">更新树数据</Button>
</div>
</template>
<script>
import TreeSelect from '_c/tree-select'
import { newTreeData } from '@/mock/data/tree-select'
import { getTreeSelectData } from '@/api/data'
export default {
name: 'tree_select_page',
components: {
TreeSelect
},
data () {
return {
treeSelected: [112, 113],
treeData: []
}
},
mounted () {
getTreeSelectData().then(res => {
const { data } = res
this.treeData = data
})
},
methods: {
changeTreeSelectData () {
this.treeSelected = [111, 114]
},
changeTreeData () {
this.treeData = newTreeData
// this.treeSelected = [];
},
handleTreeSelectChange (list) {
// console.log('=-========', list);
},
handleTreeSelectExpand (item) {
// console.log('toggle expand', item);
},
handleTreeSelectCheckChange (selectedArray, item) {
// console.log(selectedArray, item);
},
handleTreeSelectClick (selectArray, item) {
// console.log(selectArray, item);
},
loadData (item, callback) {
setTimeout(() => {
let data = [
{
id: 111,
title: '1-1-1'
},
{
id: 112,
title: '1-1-2'
},
{
id: 113,
title: '1-1-3'
},
{
id: 114,
title: '1-1-4'
}
]
callback(data)
}, 1000)
}
}
}
</script>
<style>
</style>