以下是使用 TypeScript 优化后的代码:
import { ref, reactive, toRefs, computed, watch, nextTick, defineEmits, defineExpose } from 'vue';
import { storeToRefs } from 'pinia';
import { ElTree } from 'element-plus';
import { useCommon } from '@/compositions/common';
import { useDictStore } from '@/stores/dict';
interface Props {
selectedItemProps: {
id: number;
[key: string]: any;
};
allLabelList: Array<any>;
}
interface LabelItem {
id: number;
labelName: string;
disabled?: boolean;
labelPermission?: number;
labelPermissionText?: string;
childLabelList?: Array<LabelItem>;
}
interface FieldItem {
fieldKey: string;
}
interface ShareField {
labelId: number;
fieldList: Array<string>;
}
interface LeftTreeInfo {
loading: boolean;
checkedKeys: Array<number | string>;
list: Array<LabelItem>;
flatList: Array<[LabelItem, Array<[LabelItem, ...any[]]>]>;
selectedId?: number | null;
}
interface RightContentInfo {
fieldKeyList: Array<string>;
checkAll: boolean;
}
interface DataInfo {
loading:boolean
}
export default function useShareLabelTree(props: Props) {
const { proxy } = useCommon();
const useDict = useDictStore(); // 字典
const { labelPermissionList } = storeToRefs(useDict);
const { selectedItemProps } = toRefs(props);
const emit = defineEmits(['on-detail']);
// 设置数组扁平化
function setFlat(list:Array<LabelItem>) :Array<[LabelItem, Array<[LabelItem,...any[]]>]>{
return list.map((item) => {
item.disabled = item.labelPermission == 0;
item.labelPermissionText = proxy.$utils.findIdName(labelPermissionList.value, item.labelPermission);
return [item, setFlat(item.childLabelList ?? [])];
}).flat(Infinity);
}
// 左侧tree树信息
const treeRef = ref<InstanceType<typeof ElTree>>();
const leftTreeInfo: LeftTreeInfo = reactive({
loading: false,
checkedKeys: [],
list: props.allLabelList,
get flatList() {
return setFlat(this.list);
},
async init() {
if (!selectedItemProps.value?.id) {
this.list[0] && this.nodeClick(this.list[0]);
} else {
await dataInfo.getDetail();
}
},
filterNode(value: string, data: any) {
if (!value) { return true; }
return data.labelName.includes(value);
},
selectedId: null,
get selectedItem() {
return this.flatList.find(obj => obj.id === this.selectedId)?.[0];
},
get selectedItemFieldkeyList() {
return (this.selectedItem?.fieldList || []).map(obj => obj.fieldKey);
},
async nodeClick(data:any) {
const { id } = data;
if (!id) { return; }
leftTreeInfo.selectedId = id;
nextTick(() => {
rightContentInfo.setCheckAllStatus(data);
});
},
checkChange(checkedNodes:any, isChecked:boolean) {
},
async check(checkedNodes:any, checkedKeysFamiy:Array<number|string>, halfCheckedNodes:any, halfCheckedKeys:any){
await leftTreeInfo.nodeClick(checkedNodes);
nextTick(() => {
const isChecked = checkedKeysFamiy.includes(checkedNodes.id);
rightContentInfo.changeCheckAll(isChecked);
});
}
});
const filterText = ref('');
watch(filterText, (val) => {
treeRef?.value!.filter(val);
});
// ---------------------------------------------------
// 右侧信息
const rightContentInfo: RightContentInfo = reactive({
get fieldKeyList() {
return (leftTreeInfo.selectedItem.fieldList || []).map(v => v.fieldKey);
},
checkAll: false,
setCheckAllStatus(data:any) {
const allNum = (data.fieldList || []).length;
const curNum = (data?.$savedFieldKeyList || []).length;
const num = allNum - curNum;
this.checkAll = num == 0;
this.isIndeterminate = num < allNum && num !== 0;
},
changeCheckAll: (isChecked: boolean) => {
leftTreeInfo.selectedItem.$savedFieldKeyList = isChecked ? (leftTreeInfo.selectedItemFieldkeyList) : [];
rightContentInfo.checkAll = isChecked;
rightContentInfo.isIndeterminate = false;
},
changeFiled(keys:Array<string>) {
const checkedCount:number= keys.length, fieldKeyCount:number= leftTreeInfo.selectedItemFieldkeyList.length;
rightContentInfo.checkAll = checkedCount === fieldKeyCount;
rightContentInfo.isIndeterminate = checkedCount > 0 && checkedCount < fieldKeyCount;
}
});
// ---------------------------------------------------
// 数据信息
const dataInfo: DataInfo= reactive({
loading:false,
async getDetail(){
this.loading=true;
try{
const data :any= await proxy.$requests.Label.getLabelShareById.request({ id: selectedItemProps.value.id });
const values:any = proxy.$utils.getObjectValues(data, `tenantOpenId,tenantName,allowModify,shareField`, {
more: initData => {
return { tenantOpenId: initData.tenantId };
},
});
nextTick(() => {
emit('on-detail', values);
const shareFields:Array<ShareField> = values.shareField || [];
const keys:Array<number|string> = shareFields.map(item => item.labelId);
treeRef.value!.setCheckedKeys(keys);
leftTreeInfo.selectedId = leftTreeInfo.list[0]?.id;
this.setLeftInfoFiledList(leftTreeInfo.flatList, shareFields || []);
rightContentInfo.setCheckAllStatus(leftTreeInfo.selectedItem);
});
}finally{
this.loading=false
}
},
setLeftInfoFiledList(list:any, arr:Array<ShareField>) {
list.forEach((item:any, i:number) => {
for (let v of arr) {
if (item.id == v.labelId) {
list[i].$savedFieldKeyList = [...new Set(v.fieldList.filter(v => v && v.trim()))];
return;
}
}
});
},
resetValues() {
leftTreeInfo.selectedId = null;
treeRef.value && treeRef.value!.setCheckedKeys([]);
filterText.value = '';
(leftTreeInfo.list || []).forEach((item:any) => {
item.$savedFieldKeyList = [];
});
rightContentInfo.checkAll = false;
rightContentInfo.isIndeterminate = false;
},
});
defineExpose({ treeRef, leftTreeInfo, rightContentInfo, dataInfo });
return {treeRef,leftTreeInfo,rightContentInfo,dataInf};
}
优化建议:
将各个接口的参数和返回值类型进行明确的声明,提高代码可读性和可维护性。
根据单一职责原则将函数拆分成多个小函数,提高代码的复用性和可测试性。
使用 TypeScript 的类型推导功能减少变量或参数的重复定义。
使用
defineExpose
将需要暴露给外部使用的变量、方法等进行统一管理,增加代码可读性。