123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338 |
- <template>
- <a-card :class="[{ [$style.treewrap]: !hideLeftBtn }, { [$style.collapse]: fold }]">
- <a-input-search
- v-if="treeData.length > 0 && !hideSearch"
- placeholder="请输入"
- allow-clear
- @change="onSearchChange"
- />
- <a-spin :spinning="spinning" :class="$style.spin" />
- <a-empty v-if="empty" />
- <a-tree
- v-if="treeData.length > 0"
- show-icon
- :expanded-keys.sync="expandedKeys"
- :auto-expand-parent="autoExpandParent"
- :tree-data="treeData"
- :replace-fields="replacefields"
- :selected-keys.sync="selectedkey"
- :load-data="loadData"
- @select="onSelect"
- @expand="onExpand"
- >
- <template v-slot:title="{ text, props }">
- <span v-if="text.indexOf(searchValue) > -1">
- {{ text.substr(0, text.indexOf(searchValue))
- }}<span :class="$style.searchColor">{{ searchValue }}</span
- >{{ text.substr(text.indexOf(searchValue) + searchValue.length) }}</span
- ><span v-else>{{ text }}</span
- ><span v-if="props.count > 0 && optionCount !== undefined"
- >({{ props[optionCount] }})</span
- >
- </template>
- </a-tree>
- <a-button v-if="!hideLeftBtn" type="primary" :class="$style.fold" @click="foldClick">
- <a-icon :type="icontype" />
- </a-button>
- </a-card>
- </template>
- <script>
- import { Message } from 'ant-design-vue'
- import components from './_import-components/km-tree-async-import'
- export default {
- name: 'KmTreeAsync',
- components,
- props: {
- loadTreeData: {
- type: Function,
- required: true,
- },
- hideLeftBtn: {
- type: Boolean,
- default: false,
- },
- hideSearch: {
- type: Boolean,
- default: false,
- },
- optionName: {
- type: String,
- default: 'text',
- },
- optionValue: {
- type: String,
- default: 'id',
- },
- optionCount: {
- type: String,
- default: undefined,
- },
- },
- data() {
- return {
- searchValue: '',
- dataList: [], // 数组dataList,搜索要用
- spinning: true,
- empty: false,
- treeData: [],
- expandedKeys: [],
- autoExpandParent: true,
- replacefields: {
- title: 'text',
- key: 'id',
- },
- selectedkey: [], // 选中的节点
- defaultselectedkey: undefined,
- icontype: 'left',
- fold: false,
- }
- },
- created() {
- this.loadDataForRoot()
- },
- methods: {
- transformData(data) {
- return data.map((d) => {
- const { children, checkable, selectable, leaf, ...rest } = d
- return {
- ...rest,
- checkable: checkable ?? false,
- selectable: selectable ?? true,
- isLeaf: leaf ?? true,
- key: d[this.optionValue],
- title: d[this.optionName],
- children: children && this.transformData(children),
- scopedSlots: { title: 'title' },
- originalValue: rest,
- }
- })
- },
- loadDataForRoot() {
- Promise.resolve(this.loadTreeData())
- .then((data) => {
- this.spinning = false
- this.treeData = this.transformData(data)
- // 生成搜索要用的数组
- this.generateList(this.treeData)
- if (this.defaultselectedkey) {
- this.selectedkey = this.defaultselectedkey
- } else {
- // 没传展开的keys,根节点只有一个的话,展开它
- if (this.treeData.length === 1) {
- this.expandedKeys = [this.treeData[0].key]
- this.selectedkey = [this.treeData[0].key]
- this.$emit('treeSelectd', this.treeData[0])
- }
- }
- })
- .catch(() => {
- this.spinning = false
- this.empty = true
- const data = {
- id: -1,
- text: '获取分类失败,请联系管理员',
- }
- this.$emit('treeSelectd', data)
- Message.error({ content: '获取分类失败,请联系管理员' })
- })
- },
- loadData(treeNode) {
- return new Promise((resolve) => {
- if (treeNode.dataRef.children) {
- // 有值了直接渲染
- resolve()
- return
- }
- Promise.resolve(this.loadTreeData(treeNode.dataRef.key)).then((data) => {
- treeNode.dataRef.children = this.transformData(data)
- this.treeData = [...this.treeData]
- // 生成搜索要用的数组
- this.generateList(this.treeData)
- resolve()
- })
- })
- },
- // 刷新树
- refresh() {
- this.defaultselectedkey = this.selectedkey
- this.treeData = []
- this.spinning = true
- this.loadDataForRoot()
- },
- // 搜索相关
- onExpand(expandedKeys) {
- this.expandedKeys = expandedKeys
- this.autoExpandParent = false
- },
- getParentKey(key, tree) {
- let parentKey
- for (let i = 0; i < tree.length; i++) {
- const node = tree[i]
- if (node.children) {
- if (node.children.some((item) => item.id === key)) {
- parentKey = node.id
- } else if (this.getParentKey(key, node.children)) {
- parentKey = this.getParentKey(key, node.children)
- }
- }
- }
- return parentKey
- },
- // 获取当前选中节点的根节点
- getRootKey(key) {
- let rootKey
- const strKey = key.toString()
- if (this.treeData.length === 1) {
- rootKey = this.treeData[0].id
- } else {
- for (let index = 0; index < this.treeData.length; index++) {
- var item = this.treeData[index]
- // 先判断选中的是不是根节点
- if (item.id === strKey) {
- rootKey = key
- break
- } else {
- if (this.getParentKey(strKey, [item])) {
- // 说明选中的是这个根节点下的分类
- rootKey = item.id
- break
- }
- }
- }
- }
- return parseInt(rootKey)
- },
- onSearchChange(e) {
- const value = e.target.value
- const expandedKeys = this.dataList
- .map((item) => {
- if (item.title.indexOf(value) > -1) {
- return this.getParentKey(item.key, this.treeData)
- }
- return null
- })
- .filter((item, i, self) => item && self.indexOf(item) === i)
- Object.assign(this, {
- expandedKeys,
- searchValue: value,
- autoExpandParent: true,
- })
- },
- // 处理搜索用的dataList
- generateList(data) {
- for (let i = 0; i < data.length; i++) {
- const node = data[i]
- const key = node.id
- const title = node.text
- const props = node.props
- this.dataList.push({ key, id: key, title: title, props })
- if (node.children) {
- this.generateList(node.children)
- }
- }
- },
- // 获取被选中的节点
- fnGetNodeItem(data, key) {
- return new Promise((resolve, reject) => {
- for (var i in data) {
- if (data[i].id === key) {
- resolve(data[i])
- break
- } else {
- if (data[i].children) {
- this.fnGetNodeItem(data[i].children, key).then((data) => {
- resolve(data)
- })
- }
- }
- }
- })
- },
- // 树选中调用
- onSelect(selectedKeys, info) {
- this.selectedkey = selectedKeys
- this.$emit('treeSelectd', info)
- },
- // 展示/隐藏树的小箭头点击事件
- foldClick() {
- this.fold = !this.fold
- if (this.fold) {
- this.icontype = 'right'
- } else {
- this.icontype = 'left'
- }
- },
- },
- }
- </script>
- <style module lang="scss">
- @use '@/common/design' as *;
- .spin {
- width: 100%;
- line-height: 30;
- }
- .treewrap {
- :global(.ant-input-search) {
- margin: 8px 0;
- overflow: hidden;
- }
- position: relative;
- display: flex;
- flex-direction: column;
- width: 20%;
- min-height: 100%;
- margin-right: $padding-lg;
- transition: width 0.2s;
- .fold {
- position: absolute;
- top: calc(50% - 30px);
- right: -15px;
- z-index: 2;
- width: 15px;
- height: 75px;
- padding: 0;
- border-radius: 0 10px 10px 0;
- }
- :global(.ant-tree) {
- overflow-x: auto;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
- :global(.ant-card-body) {
- background: $white;
- }
- }
- .collapse {
- width: 0;
- :global(.ant-card-body) {
- background: transparent;
- :global(.ant-empty) {
- display: none;
- }
- }
- }
- .search-color {
- color: $primary-color;
- }
- </style>
|