| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267 |
- <template>
- <view class="da-tree" :style="{ '--theme-color': themeColor }">
- <scroll-view class="da-tree-scroll" :scroll-y="true" :scroll-x="false">
- <view
- class="da-tree-item"
- :class="{ 'is-show': item.show }"
- :style="{ paddingLeft: item.level * indent + 'rpx' }"
- v-for="item in datalist"
- :key="item.key">
- <view
- v-if="item.showArrow && !filterValue"
- class="da-tree-item__icon"
- @click="handleExpandedChange(item)">
- <view
- :class="['da-tree-item__icon--arr', 'is-loading']"
- v-if="loadLoading && item.loading"></view>
- <view
- :class="[
- 'da-tree-item__icon--arr',
- 'is-expand',
- { 'is-right': !item.expand },
- ]"
- v-else></view>
- </view>
- <view v-else class="da-tree-item__icon"></view>
- <view
- class="da-tree-item__checkbox"
- :class="[
- `da-tree-item__checkbox--${checkboxPlacement}`,
- { 'is--disabled': item.disabled },
- ]"
- v-if="showCheckbox"
- @click="handleCheckChange(item)">
- <view
- class="da-tree-item__checkbox--icon da-tree-checkbox-checked"
- v-if="item.checkedStatus === isCheckedStatus"></view>
- <view
- class="da-tree-item__checkbox--icon da-tree-checkbox-indeterminate"
- v-else-if="item.checkedStatus === halfCheckedStatus"></view>
- <view
- class="da-tree-item__checkbox--icon da-tree-checkbox-outline"
- v-else></view>
- </view>
- <view
- class="da-tree-item__checkbox"
- :class="[
- `da-tree-item__checkbox--${checkboxPlacement}`,
- { 'is--disabled': item.disabled },
- ]"
- v-if="!showCheckbox && showRadioIcon"
- @click="handleRadioChange(item)">
- <view
- class="da-tree-item__checkbox--icon da-tree-radio-checked"
- v-if="item.checkedStatus === isCheckedStatus"></view>
- <view
- class="da-tree-item__checkbox--icon da-tree-radio-indeterminate"
- v-else-if="item.checkedStatus === halfCheckedStatus"></view>
- <view
- class="da-tree-item__checkbox--icon da-tree-radio-outline"
- v-else></view>
- </view>
- <view
- class="da-tree-item__label"
- :class="'da-tree-item__label--' + item.checkedStatus"
- @click="handleLabelClick(item)"
- >{{ item.label }}
- <text class="da-tree-item__label--append" v-if="item.append">{{
- item.append
- }}</text></view
- >
- </view>
- </scroll-view>
- </view>
- </template>
- <script>
- import { defineComponent, ref, unref, watch } from "vue";
- import {
- unCheckedStatus,
- halfCheckedStatus,
- isCheckedStatus,
- deepClone,
- getAllNodeKeys,
- getAllNodes,
- logError,
- isArray,
- isString,
- isNumber,
- isFunction,
- } from "./utils";
- import basicProps from "./props";
- export default defineComponent({
- name: "DaTree",
- props: basicProps,
- emits: ["change", "expand"],
- setup(props, { emit }) {
- /** 原始的树数据 */
- const dataRef = ref([]);
- /** 处理后的一维树项数据 */
- const datalist = ref([]);
- /** 处理后的以key为键值的树项数据 */
- const datamap = ref({});
- /** 默认的展开数据 */
- const expandedKeys = ref([]);
- /** 默认的已选数据 */
- const checkedKeys = ref(null);
- /** 加载状态 */
- const loadLoading = ref(false);
- let fieldMap = {
- value: "value",
- label: "label",
- children: "children",
- disabled: "disabled",
- append: "append",
- leaf: "leaf",
- sort: "sort",
- };
- /**
- * 初始化数据结构
- */
- function initData() {
- fieldMap = {
- value:
- props.field?.key || props.field?.value || props.valueField || "value",
- label: props.field?.label || props.labelField || "label",
- children: props.field?.children || props.childrenField || "children",
- disabled: props.field?.disabled || props.disabledField || "disabled",
- append: props.field?.append || props.appendField || "append",
- leaf: props.field?.leaf || props.leafField || "leaf",
- sort: props.field?.sort || props.sortField || "sort",
- };
- const data = deepClone(dataRef.value);
- datalist.value = [];
- datamap.value = {};
- // clean tree
- handleTreeData(data);
- // flat tree
- datalist.value = checkInitData(datalist.value);
- // console.log('init datalist', datalist.value)
- // console.log('init datamap', datamap.value)
- }
- /**
- * 转换为节点数据
- * @param data
- * @param parent
- * @param level
- */
- function handleTreeData(
- data = [],
- parent = null,
- level = 0,
- insertIndex = -1
- ) {
- return data.reduce((prev, cur, index) => {
- const key = cur[fieldMap.value];
- const children = cur[fieldMap.children] || null;
- const newItem = createNewItem(cur, index, parent, level);
- if (insertIndex > -1) {
- // 插入子项尾部
- const index = (parent.childrenKeys?.length || 0) + insertIndex + 1;
- if (!parent?.childrenKeys?.includes(key)) {
- datamap.value[key] = newItem;
- datalist.value.splice(index, 0, newItem);
- parent.children.push(newItem);
- if (newItem.parentKeys?.length) {
- newItem.parentKeys.forEach((k) => {
- datamap.value[k].childrenKeys = [
- ...datamap.value[k].childrenKeys,
- newItem.key,
- ];
- });
- }
- }
- } else {
- datamap.value[key] = newItem;
- datalist.value.push(newItem);
- }
- const hasChildren = children && children.length > 0;
- if (hasChildren) {
- const childrenData = handleTreeData(children, newItem, level + 1);
- // childrenData.sort((a, b) => a.sort - b.sort)
- newItem.children = childrenData;
- const childrenKeys = childrenData.reduce((p, k) => {
- const keys = k.childrenKeys;
- p.push(...keys, k.key);
- return p;
- }, []);
- newItem.childrenKeys = childrenKeys;
- }
- prev.push(newItem);
- return prev;
- }, []);
- }
- /**
- * 创建节点
- * @param item
- * @param index
- * @param parent
- * @param level
- */
- function createNewItem(item, index, parent, level) {
- const key = item[fieldMap.value];
- const label = item[fieldMap.label];
- const sort = item[fieldMap.sort] || 0;
- const children = item[fieldMap.children] || null;
- const append = item[fieldMap.append] || null;
- // 1. 获取自身真实的 disabled 状态(兼容后端返回字符串 "false" 的情况)
- let selfDisabled = item[fieldMap.disabled];
- if (selfDisabled === "false") selfDisabled = false;
- selfDisabled = !!selfDisabled;
- // 2. 【核心修改:父子解绑】直接使用节点自身的状态,不继承 parent.disabled
- let disabled = selfDisabled;
- let isLeaf = isFunction(props.isLeafFn)
- ? props.isLeafFn(item)
- : item[fieldMap.leaf] || false;
- const isEmptyChildren = children && children.length === 0;
- let showArrow = true;
- let expand = props.defaultExpandAll || false;
- const isLoadMode = props.loadMode && isFunction(props.loadApi);
- if (!children || isEmptyChildren) {
- expand = false;
- if (isLoadMode) {
- showArrow = true;
- } else {
- isLeaf = true;
- showArrow = false;
- }
- }
- if (isLeaf) {
- showArrow = false;
- expand = false;
- } else {
- showArrow = true;
- }
- // 3. 修复 onlyRadioLeaf 单选逻辑(同样保持父子解绑)
- if (!props.showCheckbox) {
- if (props.onlyRadioLeaf) {
- if (!isLeaf) {
- // 单选模式下,如果规定“只能选叶子节点”,那么非叶子节点必须被禁用
- disabled = true;
- } else {
- // 叶子节点只看自己的状态,不去拿 parent 的状态
- disabled = selfDisabled;
- }
- }
- }
- if (disabled) {
- if (isLeaf || !children || isEmptyChildren) {
- expand = false;
- showArrow = false;
- }
- }
- const parentKey = parent ? parent.key : null;
- const show = props.defaultExpandAll || level === 0;
- const newItem = {
- key,
- parentKey,
- label,
- append,
- isLeaf,
- showArrow,
- level,
- expand,
- show,
- sort,
- disabled,
- loaded: false,
- loading: false,
- indexs: [index],
- checkedStatus: unCheckedStatus,
- parentKeys: [],
- childrenKeys: [],
- children: [],
- originItem: item,
- };
- if (parent) {
- newItem.parentKeys = [parent.key, ...parent.parentKeys];
- newItem.indexs = [...parent.indexs, index];
- }
- return newItem;
- }
- /**
- * 处理初始化内容
- * @param list
- */
- function checkInitData(list) {
- let checkedKeyList = null;
- let expandedKeyList = [];
- if (props.showCheckbox) {
- checkedKeyList = [...new Set(checkedKeys.value || [])];
- expandedKeyList = props.expandChecked
- ? [...(checkedKeys.value || []), ...(expandedKeys.value || [])]
- : expandedKeys.value;
- } else {
- checkedKeyList = checkedKeys.value || null;
- expandedKeyList =
- props.expandChecked && checkedKeys.value
- ? [checkedKeys.value, ...(expandedKeys.value || [])]
- : expandedKeys.value;
- }
- handleCheckState(list, checkedKeyList, true);
- // 处理初始展开
- expandedKeyList = [...new Set(expandedKeyList)];
- if (!props.defaultExpandAll) {
- handleExpandState(list, expandedKeyList, true);
- }
- list.sort((a, b) => {
- if (a.sort === 0 && b.sort === 0) {
- return 0;
- }
- if (a.parentKey === b.parentKey) {
- if (a.sort - b.sort > 0) {
- return 1;
- } else {
- return -1;
- }
- }
- return 0;
- });
- return list;
- }
- /**
- * 处理选中
- * @param list
- * @param checkedKeyList
- */
- function handleCheckState(list, checkedKeyList, checked = true) {
- // 多选
- if (props.showCheckbox) {
- if (checkedKeyList?.length) {
- checkedKeyList.forEach((k) => {
- const item = datamap.value[k];
- if (item) {
- checkTheChecked(item, checked);
- }
- });
- }
- return;
- }
- // 单选
- for (let i = 0; i < list.length; i++) {
- const item = list[i];
- if (item.key === checkedKeyList) {
- checkTheRadio(item, checked);
- break;
- }
- }
- }
- /**
- * 校验多选节点
- * @param item
- * @param checked
- */
- function checkTheChecked(item, checked = true) {
- const { childrenKeys, parentKeys, disabled = false } = item;
- if (!props.checkedDisabled && disabled) return;
- // 当前
- item.checkedStatus = checked ? isCheckedStatus : unCheckedStatus;
- if (!props.checkStrictly) {
- // 子类
- childrenKeys.forEach((k) => {
- const childrenItem = unref(datamap)[k];
- childrenItem.checkedStatus =
- !props.checkedDisabled && childrenItem.disabled
- ? childrenItem.checkedStatus
- : item.checkedStatus;
- });
- // 父类
- parentKeys.forEach((k) => {
- const parentItem = datamap.value[k];
- parentItem.checkedStatus = getParentCheckedStatus(parentItem);
- });
- }
- }
- /**
- * 校验单选节点
- * @param item
- */
- function checkTheRadio(item, checked) {
- const { parentKeys, isLeaf, disabled = false } = item;
- if (!props.checkedDisabled && disabled) return;
- // 限制末节点选中,但当前非末节点
- if (props.onlyRadioLeaf && !isLeaf) {
- logError(`限制了末节点选中,当前[${item.label}]非末节点`);
- return;
- }
- if (datalist.value?.length) {
- datalist.value.forEach((k) => {
- k.checkedStatus = unCheckedStatus;
- });
- }
- parentKeys.forEach((k) => {
- const parentItem = datamap.value[k];
- parentItem.checkedStatus = checked
- ? getParentCheckedStatus(parentItem)
- : unCheckedStatus;
- });
- // 当前
- item.checkedStatus = checked ? isCheckedStatus : unCheckedStatus;
- }
- /**
- * 处理父节点展开
- * @param item
- * @param expand
- */
- // function handleExpandParentNode(item, expand = true) {
- // if (!expand) return
- // if (item?.parentKeys?.length) {
- // item.parentKeys.forEach(pk => {
- // if (!datamap.value[pk].expand) {
- // datamap.value[pk].expand = true
- // }
- // })
- // }
- // }
- /**
- * 处理节点展开
- * @param list
- * @param expandedKeyList
- * @param expand
- */
- function handleExpandState(list, expandedKeyList, expand = true) {
- // 收起
- if (expand === false) {
- for (let i = 0; i < list.length; i++) {
- const item = list[i];
- if (expandedKeyList?.includes(item.key)) {
- item.expand = false;
- if (item.childrenKeys?.length) {
- item.childrenKeys.forEach((ck) => {
- datamap.value[ck].expand = false;
- datamap.value[ck].show = false;
- });
- }
- }
- }
- return;
- }
- // 展开
- for (let i = 0; i < list.length; i++) {
- const item = list[i];
- // 处理展开
- if (expandedKeyList?.includes(item.key)) {
- // 父子
- item.expand = true;
- if (item.children?.length) {
- item.children.forEach((k) => {
- const kItem = unref(datamap)[k.key];
- kItem.show = true;
- });
- }
- // 族系
- if (item.parentKeys?.length) {
- item.parentKeys.forEach((k) => {
- const kItem = unref(datamap)[k];
- kItem.expand = true;
- if (kItem.children?.length) {
- kItem.children.forEach((k) => {
- const skItem = unref(datamap)[k.key];
- skItem.show = true;
- });
- }
- });
- }
- }
- }
- }
- /**
- * 点击选框
- * @param item
- */
- function handleCheckChange(item) {
- const {
- childrenKeys,
- parentKeys,
- checkedStatus,
- isLeaf,
- disabled = false,
- } = item;
- if (!props.showCheckbox) return;
- if (disabled) return;
- // 当前
- item.checkedStatus =
- checkedStatus === isCheckedStatus ? unCheckedStatus : isCheckedStatus;
- // 子类
- if (!props.checkStrictly) {
- if (props.expandChecked) {
- item.show = true;
- item.expand = childrenKeys?.length > 0 || isLeaf;
- }
- childrenKeys.forEach((k) => {
- const childrenItem = unref(datamap)[k];
- childrenItem.checkedStatus = childrenItem.disabled
- ? childrenItem.checkedStatus
- : item.checkedStatus;
- if (props.expandChecked) {
- childrenItem.show = true;
- childrenItem.expand =
- childrenItem?.childrenKeys?.length > 0 || childrenItem.isLeaf;
- }
- });
- } else {
- if (props.expandChecked) {
- logError(
- `多选时,当 checkStrictly 为 true 时,不支持选择自动展开子节点属性(expandChecked)`
- );
- }
- }
- // 父类
- if (!props.checkStrictly) {
- parentKeys.forEach((k) => {
- const parentItem = datamap.value[k];
- parentItem.checkedStatus = getParentCheckedStatus(parentItem);
- });
- }
- const hasCheckedKeys = [];
- for (let i = 0; i < datalist.value.length; i++) {
- const k = datalist.value[i];
- if (k.checkedStatus === isCheckedStatus) {
- if ((props.packDisabledkey && k.disabled) || !k.disabled) {
- hasCheckedKeys.push(k.key);
- }
- }
- }
- checkedKeys.value = [...hasCheckedKeys];
- emit("change", hasCheckedKeys, item);
- }
- /**
- * 点击单选
- * @param item
- */
- function handleRadioChange(item) {
- const { parentKeys, checkedStatus, key, disabled = false, isLeaf } = item;
- if (props.showCheckbox) return;
- if (props.onlyRadioLeaf && !isLeaf) handleExpandedChange(item);
- if (disabled) return;
- // 重置所有选择
- if (datalist.value?.length) {
- for (let i = 0; i < datalist.value.length; i++) {
- const k = datalist.value[i];
- k.checkedStatus = unCheckedStatus;
- }
- }
- parentKeys.forEach((k) => {
- const parentItem = datamap.value[k];
- parentItem.checkedStatus = getParentCheckedStatus(parentItem);
- });
- // 当前
- item.checkedStatus =
- checkedStatus === isCheckedStatus ? unCheckedStatus : isCheckedStatus;
- checkedKeys.value = key;
- emit("change", key, item);
- }
- /**
- * 点击标签
- */
- function handleLabelClick(item) {
- if (props.showCheckbox) {
- handleCheckChange(item);
- } else {
- handleRadioChange(item);
- }
- }
- /**
- * 点击展开收起
- * @param item
- */
- async function handleExpandedChange(item) {
- if (props.filterValue) return;
- const { expand, loading = false, disabled } = item;
- if (loadLoading.value && loading) return;
- checkExpandedChange(item);
- // 异步
- item.expand = !expand;
- let currentItem = null;
- if (!disabled) {
- if (!props.showCheckbox && props.onlyRadioLeaf && props.loadMode) {
- logError(`单选时,当 onlyRadioLeaf 为 true 时不支持动态数据`);
- } else {
- currentItem = await loadExpandNode(item);
- }
- }
- emit("expand", !expand, currentItem || item || null);
- }
- /**
- * 检查展开状态
- * @param item
- */
- function checkExpandedChange(item) {
- const { expand, childrenKeys, children = null } = item;
- if (expand) {
- if (childrenKeys?.length) {
- childrenKeys.forEach((k) => {
- if (unref(datamap)[k]) {
- unref(datamap)[k].show = false;
- unref(datamap)[k].expand = false;
- }
- });
- }
- } else {
- if (children?.length) {
- const childrenKeys = children.map((k) => k.key);
- childrenKeys.forEach((k) => {
- if (unref(datamap)[k]) {
- unref(datamap)[k].show = true;
- }
- });
- }
- }
- }
- /**
- * 加载异步数据
- * @param item
- */
- async function loadExpandNode(item) {
- const { expand, key, loaded, children } = item;
- if (children?.length && !props.alwaysFirstLoad) {
- return item;
- }
- if (expand && props.loadMode && !loaded) {
- if (isFunction(props.loadApi)) {
- expandedKeys.value.push(key);
- loadLoading.value = true;
- item.loading = true;
- const currentNode = deepClone(item);
- const apiRes = await props.loadApi(currentNode);
- // 新增子项
- let newChildren = [
- ...(item.originItem?.children || []),
- ...(apiRes || []),
- ];
- const newChildrenObj = {};
- newChildren = newChildren.reduce((total, next) => {
- newChildrenObj[next[fieldMap.value]]
- ? ""
- : (newChildrenObj[next[fieldMap.value]] =
- true && total.push(next));
- return total;
- }, []);
- item.originItem.children = newChildren || null;
- if (apiRes?.length) {
- const insertIndex = datalist.value.findIndex(
- (k) => k.key === item.key
- );
- handleTreeData(apiRes, item, item.level + 1, insertIndex);
- datalist.value = checkInitData(datalist.value);
- } else {
- // 加载后无数据就移除展开图标
- item.expand = false;
- item.isLeaf = true;
- item.showArrow = false;
- }
- loadLoading.value = false;
- item.loading = false;
- item.loaded = true;
- }
- } else {
- const eki = expandedKeys.value.findIndex((k) => k === key);
- if (eki >= 0) {
- expandedKeys.value.splice(eki, 1);
- }
- }
- return item;
- }
- /**
- * 获取父类的选中状态
- * @param item
- */
- function getParentCheckedStatus(item) {
- if (!item) {
- return unCheckedStatus;
- }
- if (!props.checkedDisabled && item.disabled) {
- return item.checkedStatus || unCheckedStatus;
- }
- // 单选时,父类永远为半选
- if (!props.showCheckbox) {
- return halfCheckedStatus;
- }
- const { children } = item;
- // 子类全选中
- const childrenCheckedAll = children.every(
- (k) => k.checkedStatus === isCheckedStatus
- );
- if (childrenCheckedAll) {
- return isCheckedStatus;
- }
- // 子类全不选中
- const childrenUncheckedAll = children.every(
- (k) => k.checkedStatus === unCheckedStatus
- );
- if (childrenUncheckedAll) {
- return unCheckedStatus;
- }
- return halfCheckedStatus;
- }
- function filterData() {
- if (props.filterValue === "") {
- datalist.value.forEach((k) => {
- k.show = true;
- });
- return;
- }
- datalist.value.forEach((k) => {
- if (k.label.indexOf(props.filterValue) > -1) {
- k.show = true;
- k.parentKeys.forEach((k) => {
- datamap.value[k].show = true;
- });
- } else {
- k.show = false;
- }
- });
- datalist.value.forEach((k) => {
- if (k.show) {
- k.parentKeys.forEach((k) => {
- datamap.value[k].show = true;
- });
- }
- });
- }
- /**
- * 返回已选的 key
- */
- const getCheckedKeys = () =>
- getAllNodeKeys(
- datalist.value,
- "checkedStatus",
- isCheckedStatus,
- props.packDisabledkey
- );
- /**
- * 根据key设置已选
- * @param keys 单选时为数字或者字符串,多选时为数组
- * @param checked 多选时为key的数组,单选时为key
- */
- function setCheckedKeys(keys, checked = true) {
- // 多选
- if (props.showCheckbox) {
- if (!isArray(keys)) {
- logError(`setCheckedKeys 第一个参数非数组,传入的是[${keys}]`);
- return;
- }
- const list = datalist.value;
- // 取消选择
- if (checked === false) {
- let newCheckedKeys = [];
- for (let i = 0; i < checkedKeys.value.length; i++) {
- const ck = checkedKeys.value[i];
- if (!keys.includes(ck)) {
- newCheckedKeys.push(ck);
- }
- }
- newCheckedKeys = [...new Set(newCheckedKeys)];
- checkedKeys.value = newCheckedKeys;
- handleCheckState(list, keys, false);
- return;
- }
- // 选择
- const newCheckedKeys = [...checkedKeys.value, ...keys];
- checkedKeys.value = [...new Set(newCheckedKeys)];
- handleCheckState(list, checkedKeys.value, true);
- if (props.expandChecked && checked) {
- expandedKeys.value = [
- ...new Set([...(checkedKeys.value || []), ...(keys || [])]),
- ];
- handleExpandState(list, keys, true);
- }
- return;
- }
- // 单选
- // 如果为数组则拿第一个
- if (isArray(keys)) {
- keys = keys[0];
- }
- if (!isString(keys) && !isNumber(keys)) {
- logError("setCheckedKeys 第一个参数字符串或数字,传入的是==>", keys);
- return;
- }
- const list = datalist.value;
- checkedKeys.value = checked ? keys : null;
- if (props.expandChecked && checked) {
- handleExpandState(list, [keys], true);
- }
- handleCheckState(list, keys, !!checked);
- }
- /**
- * 返回半选的 key
- */
- const getHalfCheckedKeys = () =>
- getAllNodeKeys(
- datalist.value,
- "checkedStatus",
- halfCheckedStatus,
- props.packDisabledkey
- );
- /**
- * 返回未选的 key
- */
- const getUncheckedKeys = () =>
- getAllNodeKeys(
- datalist.value,
- "checkedStatus",
- unCheckedStatus,
- props.packDisabledkey
- );
- /**
- * 返回已展开的 key
- */
- const getExpandedKeys = () =>
- getAllNodeKeys(datalist.value, "expand", true);
- /**
- * 返回未展开的 key
- */
- const getUnexpandedKeys = () =>
- getAllNodeKeys(datalist.value, "expand", false);
- /**
- * 根据key展开/收起
- *
- * @param keys 数组,或字符串 all
- * @param expand true为展开/false为收起
- */
- function setExpandedKeys(keys, expand = true) {
- if (!Array.isArray(keys) && keys !== "all") {
- logError("setExpandedKeys 第一个参数非数组,传入的是===>", keys);
- return;
- }
- const list = datalist.value;
- // 展开/收起全部
- if (keys === "all") {
- list.forEach((k) => {
- k.expand = expand;
- if (k.level > 0) {
- k.show = expand;
- }
- });
- return;
- }
- // 收起
- if (expand === false) {
- const newExpandedKeys = [];
- for (let i = 0; i < expandedKeys.value.length; i++) {
- const ek = expandedKeys.value[i];
- if (!keys.includes(ek)) {
- newExpandedKeys.push(ek);
- }
- }
- expandedKeys.value = [...new Set(newExpandedKeys)];
- handleExpandState(list, keys, false);
- return;
- }
- // 展开
- const newExpandedKeys = [];
- for (let i = 0; i < list.length; i++) {
- if (keys.includes(list[i].key)) {
- newExpandedKeys.push(list[i].key);
- }
- }
- expandedKeys.value = [...new Set(newExpandedKeys)];
- handleExpandState(list, newExpandedKeys, true);
- }
- /**
- * 返回已选的节点
- */
- const getCheckedNodes = () =>
- getAllNodes(
- datalist.value,
- "checkedStatus",
- isCheckedStatus,
- props.packDisabledkey
- );
- /**
- * 返回半选的节点
- */
- const getHalfCheckedNodes = () =>
- getAllNodes(
- datalist.value,
- "checkedStatus",
- halfCheckedStatus,
- props.packDisabledkey
- );
- /**
- * 返回未选的节点
- */
- const getUncheckedNodes = () =>
- getAllNodes(
- datalist.value,
- "checkedStatus",
- unCheckedStatus,
- props.packDisabledkey
- );
- /**
- * 返回已展开的节点
- */
- const getExpandedNodes = () => getAllNodes(datalist.value, "expand", true);
- /**
- * 返回未展开的节点
- */
- const getUnexpandedNodes = () =>
- getAllNodes(datalist.value, "expand", false);
- watch(
- () => props.defaultExpandedKeys,
- (v) => {
- if (v?.length) {
- expandedKeys.value = v;
- } else {
- expandedKeys.value = [];
- }
- // if (v) checkInitData(datalist.value)
- },
- { immediate: true }
- );
- watch(
- () => props.defaultCheckedKeys,
- (v) => {
- if (props.showCheckbox) {
- if (v?.length) {
- checkedKeys.value = v;
- } else {
- checkedKeys.value = [];
- }
- } else {
- if (v || v === 0) {
- checkedKeys.value = v;
- } else {
- checkedKeys.value = null;
- }
- }
- // checkInitData(datalist.value)
- },
- { immediate: true }
- );
- watch(
- () => props.data,
- (v) => {
- dataRef.value = deepClone(v);
- setTimeout(() => {
- initData();
- }, 36);
- },
- { immediate: true, deep: true }
- );
- watch(
- () => props.filterValue,
- () => {
- filterData();
- }
- );
- return {
- datalist,
- unCheckedStatus,
- halfCheckedStatus,
- isCheckedStatus,
- handleCheckChange,
- handleRadioChange,
- handleLabelClick,
- handleExpandedChange,
- loadLoading,
- // updateChildrenByKey: () => {},
- // insertBeforeByKey: () => {},
- // insertAfterByKey: () => {},
- getCheckedKeys,
- setCheckedKeys,
- getHalfCheckedKeys,
- getUncheckedKeys,
- getExpandedKeys,
- getUnexpandedKeys,
- setExpandedKeys,
- getCheckedNodes,
- getHalfCheckedNodes,
- getUncheckedNodes,
- getExpandedNodes,
- getUnexpandedNodes,
- };
- },
- });
- </script>
- <style lang="scss" scoped>
- @font-face {
- font-family: "da-tree-iconfont"; /* Project id */
- src: url("data:application/octet-stream;base64,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")
- format("truetype");
- }
- .da-tree {
- width: 100%;
- height: 100%;
- &-scroll {
- width: 100%;
- height: 100%;
- }
- &-item {
- display: flex;
- align-items: center;
- height: 0;
- padding: 0;
- overflow: hidden;
- font-size: 28rpx;
- line-height: 1;
- visibility: hidden;
- opacity: 0;
- transition: opacity 0.2s linear;
- &.is-show {
- height: auto;
- padding: 12rpx 24rpx;
- visibility: visible;
- opacity: 1;
- }
- &__icon {
- display: flex;
- align-items: center;
- justify-content: center;
- width: 40rpx;
- height: 40rpx;
- overflow: hidden;
- &--arr {
- position: relative;
- display: flex;
- align-items: center;
- justify-content: center;
- width: 32rpx;
- height: 32rpx;
- &::after {
- position: relative;
- z-index: 1;
- overflow: hidden;
- /* stylelint-disable-next-line font-family-no-missing-generic-family-keyword */
- font-family: "da-tree-iconfont" !important;
- font-size: 32rpx;
- font-style: normal;
- color: #999;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
- }
- &.is-expand {
- &::after {
- content: "\e604";
- }
- }
- &.is-right {
- transform: rotate(-90deg);
- }
- &.is-loading {
- animation: IconLoading 1s linear 0s infinite;
- &::after {
- content: "\e7f1";
- }
- }
- }
- }
- &__checkbox {
- width: 40rpx;
- height: 40rpx;
- overflow: hidden;
- &--left {
- order: 0;
- }
- &--right {
- order: 1;
- }
- &--icon {
- position: relative;
- display: flex;
- align-items: center;
- justify-content: center;
- width: 40rpx;
- height: 40rpx;
- &::after {
- position: relative;
- top: 0;
- left: 0;
- z-index: 1;
- overflow: hidden;
- /* stylelint-disable-next-line font-family-no-missing-generic-family-keyword */
- font-family: "da-tree-iconfont" !important;
- font-size: 32rpx;
- font-style: normal;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
- }
- &.da-tree-checkbox-outline::after {
- color: #bbb;
- content: "\ead5";
- }
- &.da-tree-checkbox-checked::after {
- color: var(--theme-color, #007aff);
- content: "\ead4";
- }
- &.da-tree-checkbox-indeterminate::after {
- color: var(--theme-color, #007aff);
- content: "\ebce";
- }
- &.da-tree-radio-outline::after {
- color: #bbb;
- content: "\ecc5";
- }
- &.da-tree-radio-checked::after {
- color: var(--theme-color, #007aff);
- content: "\ecc4";
- }
- &.da-tree-radio-indeterminate::after {
- color: var(--theme-color, #007aff);
- content: "\ea4f";
- }
- }
- &.is--disabled {
- cursor: not-allowed;
- opacity: 0.35;
- }
- }
- &__label {
- flex: 1;
- margin-left: 4rpx;
- color: #555;
- &--2 {
- color: var(--theme-color, #007aff);
- }
- &--append {
- font-size: 60%;
- opacity: 0.6;
- }
- }
- }
- }
- @keyframes IconLoading {
- 0% {
- transform: rotate(0deg);
- }
- 100% {
- transform: rotate(360deg);
- }
- }
- </style>
|