AntDesign中a-tree使用案例
<template> <a-modal :visible='true' :title='数据授权' @cancel='handleClose()' @ok='onSubmit' :width='600' :maskClosable='false'> <div class='tree-box'> <a-input-search style='margin-bottom: 8px' placeholder='请输入关键字搜索' @change='onChange'/> <a-tree v-if='treeData.length > 0' checkable :tree-data='treeData' @check='onCheck' :replace-fields='replaceFields' :multiple='true' :checkStrictly='false' :defaultExpandAll='true' :default-checked-keys='checkedKeys'> <a-tree> </div> </a-modal> </template> <script setup lang='ts'> import {getOrgList} from '@/api/system.js' import {authUserDataScope, queryUserDataScope, authRoleDataScope, queryRoleDataScope} from '@/api/dataAuthorization.js' import {ref, reactive} from "vue" export default { props: { id: String, type: String }, data() { return { replaceFields: { title: 'label', key: 'id', }, keyWorld: '', treeData: [], checkedKeys: [], } }, created() { }, methods() { // 获取组织树结构 onLoadTreeData() { getOrgList().then((res) => { if(res.status === 0) { this.treeData = res.data; } }) }, // ★★★★★★ 获取数据授权 getDataScope() { let requestFunc = ''; let param = ''; // ★ 通过方法引用充分减少代码冗余 if(this.type === 'role') { requestFunc = queryRoleDataScope param = 'roleId' }else { requestFunc = queryUserDataScope param = 'userId' } func({[param]: this.id}).then((res) => { this.checkedKeys = res.data; message.info('授权成功!') }) }, onSubmit() { // !this.checkedKeys.length = true 等价于 this.checkedKeys.length = 0 if(!this.checkedKeys.length) { message.warn('请选择需要授权的数据!'); return; }else { this.getDataScope(); } }, onCheck(checkedKeys) { this.checkedKeys = checkedKeys || []; }, onChange(e) { this.keyWord = e.target.value; }, handleClose() { this.$emit('handle') } } } </script> <style lang="less" scoped> .tree-box > .ant-tree { height: 600px; overflow-y: auto; } </style>
AntDesign Vue中:checkStrictly的作用
在 Ant Design Vue 中,checkStrictly 是 Tree(树形控件)组件的一个属性。它用于指定是否严格遵守父子节点间的勾选关联规则。
当 checkStrictly 属性设置为 true 时,父节点和子节点之间的勾选状态不会相互影响。也就是说,勾选或取消勾选父节点不会影响子节点的勾选状态,同时子节点的勾选状态也不会影响父节点> 的勾选状态。这意味着父节点和子节点之间的勾选状态是相互独立的。
当 checkStrictly 属性设置为 false 时,父节点和子节点之间的勾选状态是关联的。如果一个父节点被勾选或取消勾选,其所有子节点也会相应地被勾选或取消勾选。同样地,如果一个子节点> > 被勾选或取消勾选,它的父节点也会根据子节点的状态相应地被勾选或取消勾选。
通过设置 checkStrictly 属性,您可以根据自己的需求来控制树形控件中勾选状态的关联性。
Example Usage
<template> <div> <a-input-search style="margin-bottom: 8px" placeholder="请输入关键字搜索" @change="onChange" /> <div class="allnode" :class="{'active': selectId === ''}"> 全部 <a-icon type="plus-circle" @click.stop="nodeHandle({}, true)" /> </div> <a-tree ref="nodeTree" class="group-tree" :tree-data="nodeData" :selectedKeys="selectedKeys" :replace-fields="replaceFields" :expanded-keys="expandedKeys" :auto-expand-parent="autoExpandParent" default-expand-all show-icon blockNode @select="onSelect" @expand="onExpand" > <template slot="title" slot-scope="{ groupName }"> <span v-if="groupName.indexOf(searchValue) > -1"> {{ groupName.substr(0, groupName.indexOf(searchValue)) }} <span style="color: #f50">{{ searchValue }}</span> {{ groupName.substr(groupName.indexOf(searchValue) + searchValue.length) }} </span> <span v-else>{{ groupName }}</span> </template> <!-- 新增、修改、删除图标 --> <template slot="plus" slot-scope="record"> <!-- <a-popover placement="bottom"> <template slot="content"> --> <!-- <p @click.stop="nodeHandle(record, false)">编辑</p> <p @click.stop="nodeDel(record)">删除</p> --> <!-- </template> --> <!-- <img src="../../../assets/dict/more.png" class="more-btn" /> --> <!-- </a-popover> --> <a-icon type="edit" @click.stop="nodeHandle(record, false)" /> <a-icon type="delete" @click.stop="nodeDel(record)" /> </template> </a-tree> <a-modal v-model="visible" :title="title" destroyOnClose @ok="handleSubmit"> <AddForm ref="addFormRef" :editData="editData" @setVisibleHide="setVisibleHide" /> </a-modal> </div> </template> <script> import { getGroupList, existPerson,deleteGroupData } from '@/api/group.js' import AddForm from './modal/addGroupForm.vue' // 数组对象中查找符合目标的对象 const parseArray = function (objArray, key, value) { for (let i in objArray) { let element = objArray[i] if (typeof element === 'object') { let result = parseArray(element, key, value) if (result) return result } else { if (i === key) { if (element === value) return objArray } } } } export default { name: 'treeComponent', data() { return { replaceFields: { title: 'groupName', key: 'groupCode', }, nodeData: [], groupName: '', selectedKeys: null, filterResource: null, expandedKeys: [], searchValue: '', autoExpandParent: true, dataList: [], visible: false, editData: {}, title: '新增', selectId: '', } }, components: { AddForm, }, mounted() { this.GetLimsConfigNode() }, methods: { onExpand(expandedKeys) { this.expandedKeys = expandedKeys; this.autoExpandParent = false; }, // 获取全部节点树 async GetLimsConfigNode() { getGroupList({keyWord: ''}).then((res) => { this.nodeData = res.data; this.generateList(this.nodeData); }) }, generateList(data) { for (let i = 0; i < data.length; i++) { const node = data[i]; this.dataList.push({ groupCode: node.groupCode, groupName: node.groupName }); if (node.children) { this.generateList(node.children); } } }, // 新增、修改节点操作,此处为打开新增修改界面,调用实际后端接口进行节点的处理操作 async nodeHandle(record, isAdd) { this.visible = true; this.title = isAdd ? '新增' : '编辑'; this.editData = record; }, setVisibleHide() { this.visible = false; this.GetLimsConfigNode() }, nodeDel(record) { this.$confirm({ title: `您确定要删除该节点:${record.groupName}?`, okText: '删除', cancelText: '取消', onOk: async () => { existPerson({id: record.id}).then((res) => { if (res.status === 0) { if(!res.data) { delGroup(record.id) } else { this.$message.warning('请先删除组内人员!') } } }) }, }) }, delGroup(id) { deleteGroupData({id}).then((res) => { if (res.status === 0) { this.$message.success('删除成功!') this.GetLimsConfigNode() } else { this.$message.warning(res.msg) } }) }, async onSelect(params, e) { if (this.filterResource) { this.$set(this.filterResource, 'scopedSlots', {}) } if (e.selected) { let groupName = e.selectedNodes[0].data.props.groupName let arr = groupName.split('-') this.filterResource = parseArray(this.nodeData, 'groupCode', params[0]) let iconStr if (arr.length < 4) { iconStr = 'plus' } else { iconStr = 'edit' } this.$set(this.filterResource, 'scopedSlots', { icon: iconStr }) this.selectedKeys = params this.groupName = groupName this.selectId = e.selectedNodes[0].data.props.id this.$emit('getCurrentGroupId', this.selectId) } else { this.selectedKeys = null this.groupName = '' this.selectId = ''; this.$emit('getCurrentGroupId', this.selectId) } }, onChange(e) { const value = e.target.value; const expandedKeys = this.dataList .map(item => { if (item.groupName.indexOf(value) > -1) { return this.getParentKey(item.groupCode, this.nodeData); } return null; }) .filter((item, i, self) => item && self.indexOf(item) === i); Object.assign(this, { expandedKeys, searchValue: value, autoExpandParent: true, }); }, getParentKey(groupCode, tree) { let parentKey; for (let i = 0; i < tree.length; i++) { const node = tree[i]; if (node.children) { if (node.children.some(item => item.groupCode === groupCode)) { parentKey = node.groupCode; } else if (this.getParentKey(groupCode, node.children)) { parentKey = this.getParentKey(groupCode, node.children); } } } return parentKey; }, handleSubmit() { this.$refs.addFormRef.onSubmit(); }, }, } </script> <style lang="scss"> .ant-tree.group-tree { li { position: relative; padding: 6px 0; ul { padding: 0; margin-left: -30px; li { padding-left: 40px; .ant-tree-switcher { display: none; } } } .ant-tree-node-content-wrapper.ant-tree-node-selected { background: transparent; } .ant-tree-switcher_close,.ant-tree-switcher_open { position: absolute; left: 10px; z-index: 9; background: url('../../../assets/dict/organization.png') no-repeat; background-size: 16px 16px; background-position: center; i { display: none !important; } } .ant-tree-node-content-wrapper:hover { background-color: transparent; } span.ant-tree-iconEle { position: absolute; right: 10px; width: auto; .anticon { margin: 0 5px; } } &:hover { background: rgba(15, 34, 67, 0.05); } } li.ant-tree-treenode-selected { background: rgba(43, 121, 255, 0.1); border-radius: 4px; .ant-tree-title { color: #1F71FF; } ul { li { .ant-tree-title { color: rgba(0, 0, 0, 0.65); } } } &:before { content: ''; position: absolute; width: 2px; height: 8px; left: 0px; top: calc(50% - 8px/2); background: #1F71FF; border-radius: 0px 3px 3px 0px; } } } .ant-tree li span.ant-tree-switcher.ant-tree-switcher-noop { width: 10px; } .allnode { padding: 6px 16px; position: relative; .anticon { float: right; line-height: 24px; } } .allnode.active { background: rgba(43, 121, 255, 0.1); &:before { content: ''; position: absolute; width: 2px; height: 8px; left: 0px; top: calc(50% - 8px/2); background: #1F71FF; border-radius: 0px 3px 3px 0px; } } .ant-popover { padding-top: 0px; margin-top: 5px; .ant-popover-arrow { display: none; } .ant-popover-inner { box-shadow: 0px 16px 32px -6px rgba(15, 34, 67, 0.18), 0px 3px 8px -2px rgba(15, 34, 67, 0.16), 0px 0px 1px rgba(15, 34, 67, 0.16); border-radius: 8px; .ant-popover-inner-content { padding: 8px; p { padding: 5px 16px; margin: 0; cursor: pointer; &:hover { background: rgba(15, 34, 67, 0.05); border-radius: 6px; } } } } } </style>
学而不思则罔,思而不学则殆!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具