Преглед изворни кода

[Feat 0000] 矿区级联选择器升级至全局存储,支持搜索并优化交互

houzekong пре 4 месеци
родитељ
комит
3d8594d2c9

+ 7 - 8
src/api/sys/menu.ts

@@ -47,11 +47,11 @@ export const getMenuList: () => Promise<getMenuListResultModel> = () => {
     defHttp.get({
       url: Api.getEnfMineTreeData,
     }),
-  ]).then(([res, rawlist]) => {
-    const tree = listToTree(rawlist, {
+  ]).then(([res, raw]) => {
+    const tree = listToTree(raw, {
+      id: 'id',
       pid: 'parentId',
       children: 'childDepart',
-      id: 'id',
     });
     return {
       allAuth: res.allAuth || [],
@@ -79,7 +79,6 @@ export const getMenuList: () => Promise<getMenuListResultModel> = () => {
  */
 export function getBackMenuAndPerms() {
   return getMenuList().catch((e) => {
-    console.log('接口 getBackMenuAndPerms 异常错误信息:', e);
     // Token过期失效,直接跳转登录页面 2025-09-08 scott
     if (e && (e.message.includes('timeout') || e.message.includes('401') || e.message.includes('500'))) {
       const userStore = useUserStoreWithOut();
@@ -107,9 +106,9 @@ export function getBackMenuAndPerms() {
 
 export function getEnfMineTree() {
   return defHttp.get<EnfMineTreeItem[]>(
-    { url: Api.getEnfMineTreeData },
-    {
-      isTransformResponse: false,
-    }
+    { url: Api.getEnfMineTreeData }
+    // {
+    //   isTransformResponse: false,
+    // }
   );
 }

+ 2 - 0
src/api/sys/model/menuModel.ts

@@ -31,4 +31,6 @@ export interface EnfMineTreeItem {
   childDepart: EnfMineTreeItem[];
   /** 部门排序 */
   departOrder?: number;
+  /** 是否为叶节点,前端添加 */
+  isLeaf?: boolean;
 }

+ 33 - 50
src/components/Form/src/jeecg/components/MineCascader/MineCascader.vue

@@ -1,94 +1,77 @@
 <template>
   <a-cascader
-    v-model:value="innerValue"
-    :options="options"
-    :load-data="loadData"
+    :value="innerValue"
+    :options="getMineTree"
     :placeholder="placeholder"
     :field-names="{
       label: 'departName',
       value: 'id',
-      children: 'children',
+      children: 'childDepart',
     }"
+    show-search
     @change="handleChange"
   >
+    <template #displayRender="{ labels }">
+      {{ labels[labels.length - 1] }}
+    </template>
   </a-cascader>
   <!-- <a-input>{{ shownText }}</a-input> -->
 </template>
 
 <script lang="ts">
-  import { CascaderProps } from 'ant-design-vue';
-  import { last } from 'lodash';
-  import { defineComponent, ref, onMounted, triggerRef } from 'vue';
-  import { getSjmbStructData, getUserMinePermissionData } from './mineData.api';
+  import { get, last } from 'lodash';
+  import { defineComponent, onMounted, computed } from 'vue';
   // import { useMessage } from '/@/hooks/web/useMessage';
   import { propTypes } from '/@/utils/propTypes';
+  import { useMineStore } from '/@/store/modules/mine';
+  import { storeToRefs } from 'pinia';
 
   export default defineComponent({
     name: 'MineCascader',
     props: {
       value: propTypes.string.def(''),
       placeholder: propTypes.string.def('请选择矿名'),
-      // level: propTypes.number.def(3)
+      /** 选择器选择深度,1即执法处,2即区县,3即矿区 */
+      level: propTypes.number.def(3),
       /** 最后一级矿名选择时传递出去的值对应的字段 */
-      valueField: propTypes.string.def('fax'),
+      valueField: propTypes.string,
     },
     emits: ['change', 'update:value'],
     setup(props, { emit }) {
       // const { createMessage } = useMessage();
+      const mineStore = useMineStore();
+      const { getMine, getMineCode, getMinePath, getMineTree } = storeToRefs(mineStore);
 
-      const shownText = ref('');
-      const innerValue = ref<string[]>([]);
-      const options = ref<CascaderProps['options']>([]);
-
-      const loadData: CascaderProps['loadData'] = async (selectedOptions) => {
-        const targetOption = last(selectedOptions);
-        if (!targetOption) return;
-        if (targetOption.children) return;
-
-        targetOption.loading = true;
-
-        // load options lazily
-        try {
-          if (selectedOptions.length === 2) {
-            const res = await getUserMinePermissionData({ departId: targetOption.id });
-            res.forEach((r) => {
-              r.id = r[props.valueField];
-            });
-            targetOption.children = res;
-          } else {
-            const res = await getSjmbStructData({ orgType: '3', parentId: targetOption.id });
-            targetOption.children = res;
-            targetOption.disabled = !res.length;
-          }
-        } finally {
-          targetOption.loading = false;
-          // trigger watcher
-          triggerRef(options);
-          // options.value = [...options.value];
-        }
-      };
+      // const shownText = computed(() => getMine.value.departName || '-');
+      const innerValue = computed(() => getMinePath.value.map((e) => e.id));
+      const options = getMineTree;
 
       /**
        * change事件
        * @param e
        */
-      function handleChange(value, selectedOptions: CascaderProps['options']) {
-        value = last(value);
-        shownText.value = last(selectedOptions)?.label;
-        emit('update:value', value);
-        emit('change', value);
+      function handleChange(value: any[]) {
+        mineStore.setMineById(last(value));
+
+        let val;
+        if (props.valueField) {
+          val = get(getMine.value, props.valueField);
+        } else {
+          val = getMineCode.value;
+        }
+        emit('update:value', val);
+        emit('change', val);
       }
 
       onMounted(() => {
-        getSjmbStructData({ orgType: '2' }).then((res) => {
-          options.value = res;
-        });
+        mineStore.fetchMineTree();
+        // mineStore.fetchMineTree(props.level);
       });
 
       return {
         innerValue,
         options,
-        loadData,
+        getMineTree,
         handleChange,
       };
     },

+ 87 - 35
src/store/modules/mine.ts

@@ -1,56 +1,108 @@
-import type { ErrorLogInfo } from '/#/store';
-
 import { defineStore } from 'pinia';
-import { store } from '/@/store';
-
-import { flatMap } from 'lodash-es';
-
-import { formatToDateTime } from '/@/utils/dateUtil';
-import projectSetting from '/@/settings/projectSetting';
 
-import { ErrorTypeEnum } from '/@/enums/exceptionEnum';
 import { getEnfMineTree } from '/@/api/sys/menu';
+import { getUserMinePermissionData } from '/@/components/Form/src/jeecg/components/MineCascader/mineData.api';
+import { findNode, findPath, listToTree } from '/@/utils/helper/treeHelper';
+import { isEmpty, isNil } from 'lodash';
+
+export interface MineDepartment {
+  /** 唯一标识 */
+  id: string;
+  /** 部门名称 */
+  departName: string;
+  /** 父部门ID */
+  parentId: string | null;
+  /** 是否为叶子节点 */
+  isLeaf: boolean;
+  /** 子部门列表 */
+  childDepart: MineDepartment[];
+  /** 传真,用作矿井编号 */
+  fax: string | null;
+}
 
 export interface MineState {
-  errorLogInfoList: Nullable<ErrorLogInfo[]>;
-  errorLogListCount: number;
+  mine?: MineDepartment;
+  /** 当前选中的矿井ID,可能为空 */
+  mineId?: MineDepartment['id'];
+  /** 当前选中的矿井编号,可能为空 */
+  mineCode?: MineDepartment['fax'];
+  /** 当前选中的矿井在组织树中的访问路径 */
+  minePath: MineDepartment[];
+  /** 矿井组织树 */
+  mineTree: MineDepartment[];
 }
 
-export const useErrorLogStore = defineStore({
+const DEFAULT_CONFIG = {
+  id: 'id',
+  pid: 'parentId',
+  children: 'childDepart',
+};
+
+export const useMineStore = defineStore({
   id: 'mine-store',
   state: (): MineState => ({
-    defaultCode: null,
+    mine: undefined,
+    mineId: undefined,
+    mineCode: undefined,
+    minePath: [],
     mineTree: [],
   }),
   getters: {
-    getMineTree(): any {
+    getMine(): MineState['mine'] {
+      return this.mine;
+    },
+    getMineId(): MineState['mineId'] {
+      return this.getMine?.id;
+    },
+    getMineCode(): MineState['mineCode'] {
+      return this.getMine?.fax;
+    },
+    getMineTree(): MineState['mineTree'] {
       return this.mineTree;
     },
-    getDefaultCode(): any {
-      return this.defaultCode;
+    getMinePath(): MineState['minePath'] {
+      return this.minePath;
     },
   },
   actions: {
+    /** 设置当前选中的矿井,并更新path项 */
+    setMine(mine?: MineDepartment) {
+      if (isNil(mine) || isEmpty(mine)) return;
+      this.mine = mine;
+      const path = findPath(this.getMineTree, (item) => item.id === mine.id, DEFAULT_CONFIG);
+      this.setMinePath(path);
+    },
+    /** 根据传入的id设置当前选中的矿井,更新id、code、path三项 */
+    setMineById(id: string = '') {
+      const node = findNode(this.getMineTree, (item) => item.id === id, DEFAULT_CONFIG);
+      this.setMine(node);
+    },
+    /** 根据传入的code设置当前选中的矿井,更新id、code、path三项 */
+    setMineByCode(code: string = '') {
+      const node = findNode(this.getMineTree, (item) => item.fax === code, DEFAULT_CONFIG);
+      this.setMine(node);
+    },
+    setMinePath(path: any[] = []) {
+      this.minePath = path;
+    },
+    setMineTree(tree: any[] = []) {
+      this.mineTree = tree;
+    },
     async fetchMineTree() {
-      const topLevelData = getEnfMineTree().then((r) => {
-        // const transkey =
-        return r.reduce((arr, ele) => {
-          if (ele.childDepart) {
-            arr.push(...ele.childDepart);
-            delete ele.childDepart;
-            arr.push(ele);
-          } else {
-            arr.push(ele);
-          }
-
-          return arr;
-        }, []);
-      });
+      const r1 = await getEnfMineTree();
+      const r2 = await getUserMinePermissionData({});
+      r1.forEach((e) => (e.isLeaf = false));
+      r2.forEach((e) => (e.isLeaf = true));
+      const tree = listToTree([...r1, ...r2], DEFAULT_CONFIG);
+      // 使用查找方法而不是选中r2中第一个“叶节点”是因为:
+      // r2返回的矿名不具备权限控制,r1返回的父节点才有权限控制
+
+      this.setMineTree(tree);
+
+      if (this.getMine) return;
+
+      const leaf = findNode(tree, (item) => item.isLeaf, DEFAULT_CONFIG);
+      this.setMine(leaf);
     },
   },
 });
-
-// Need to be used outside the setup
-export function useErrorLogStoreWithOut() {
-  return useErrorLogStore(store);
-}