From 1553782fd262f97a336fecc8b38f8f309fc08ae6 Mon Sep 17 00:00:00 2001
From: zhou zhou <3272660260@qq.com>
Date: 星期三, 01 四月 2026 11:07:31 +0800
Subject: [PATCH] #前端

---
 rsf-design/src/views/basic-info/matnr-group/index.vue |  430 +++++++++++++++++-----------------------------------
 1 files changed, 143 insertions(+), 287 deletions(-)

diff --git a/rsf-design/src/views/basic-info/matnr-group/index.vue b/rsf-design/src/views/basic-info/matnr-group/index.vue
index dadb999..8475421 100644
--- a/rsf-design/src/views/basic-info/matnr-group/index.vue
+++ b/rsf-design/src/views/basic-info/matnr-group/index.vue
@@ -1,141 +1,99 @@
 <template>
-  <div class="matnr-group-page art-full-height flex flex-col gap-4 xl:flex-row">
-    <ElCard class="w-full shrink-0 xl:w-[320px]">
-      <div class="mb-3 flex items-center justify-between gap-3">
-        <div>
-          <div class="text-base font-medium text-[var(--art-text-primary)]">鐗╂枡鍒嗙粍</div>
-          <div class="text-xs text-[var(--art-text-secondary)]">
-            {{ selectedGroupLabel }}
-          </div>
-        </div>
-        <ElButton text @click="handleResetGroup">鍏ㄩ儴</ElButton>
-      </div>
+  <div class="matnr-group-page art-full-height">
+    <ArtSearchBar
+      v-model="searchForm"
+      :items="searchItems"
+      :show-expand="false"
+      @search="handleSearch"
+      @reset="handleReset"
+    />
 
-      <div class="mb-3 flex items-center gap-2">
-        <ElInput
-          v-model.trim="groupSearch"
-          clearable
-          placeholder="鎼滅储鐗╂枡鍒嗙粍"
-          @clear="handleGroupSearch"
-          @keyup.enter="handleGroupSearch"
-        />
-        <ElButton @click="handleGroupSearch">鎼滅储</ElButton>
-      </div>
+    <ElCard class="art-table-card">
+      <ArtTableHeader
+        :loading="loading"
+        :show-zebra="false"
+        v-model:columns="columnChecks"
+        @refresh="handleRefresh"
+      >
+        <template #left>
+          <ElSpace wrap>
+            <ElButton v-auth="'add'" @click="showDialog('add')" v-ripple>鏂板鍒嗙粍</ElButton>
+            <ElButton
+              v-auth="'delete'"
+              type="danger"
+              :disabled="selectedRows.length === 0"
+              @click="handleBatchDelete"
+              v-ripple
+            >
+              鎵归噺鍒犻櫎
+            </ElButton>
+            <ElButton @click="toggleExpand" v-ripple>
+              {{ isExpanded ? '鏀惰捣' : '灞曞紑' }}
+            </ElButton>
+            <ListExportPrint
+              class="inline-flex"
+              :preview-visible="previewVisible"
+              @update:previewVisible="handlePreviewVisibleChange"
+              :report-title="reportTitle"
+              :selected-rows="selectedRows"
+              :query-params="reportQueryParams"
+              :columns="columns"
+              :preview-rows="previewRows"
+              :preview-meta="resolvedPreviewMeta"
+              :total="flattenedTableRows.length"
+              :disabled="loading"
+              @export="handleExport"
+              @print="handlePrint"
+            />
+          </ElSpace>
+        </template>
+      </ArtTableHeader>
 
-      <ElScrollbar class="h-[calc(100vh-260px)] pr-1">
-        <div v-if="groupTreeLoading" class="py-6">
-          <ElSkeleton :rows="10" animated />
-        </div>
-        <ElEmpty v-else-if="!groupTreeData.length" description="鏆傛棤鐗╂枡鍒嗙粍" />
-        <ElTree
-          v-else
-          :data="groupTreeData"
-          :props="treeProps"
-          node-key="id"
-          highlight-current
-          default-expand-all
-          :current-node-key="selectedGroupId"
-          @node-click="handleGroupNodeClick"
-        >
-          <template #default="{ data }">
-            <div class="flex items-center gap-2">
-              <span class="font-medium">{{ data.name || '--' }}</span>
-              <span class="text-xs text-[var(--art-text-secondary)]">{{ data.code || '--' }}</span>
-            </div>
-          </template>
-        </ElTree>
-      </ElScrollbar>
-    </ElCard>
-
-    <div class="min-w-0 flex-1 space-y-4">
-      <ArtSearchBar
-        v-model="searchForm"
-        :items="searchItems"
-        :showExpand="true"
-        @search="handleSearch"
-        @reset="handleReset"
+      <ArtTable
+        ref="tableRef"
+        rowKey="id"
+        :loading="loading"
+        :columns="columns"
+        :data="tableData"
+        :stripe="false"
+        :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
+        :default-expand-all="true"
+        @selection-change="handleSelectionChange"
       />
 
-      <ElCard class="art-table-card">
-        <ArtTableHeader :loading="loading" v-model:columns="columnChecks" @refresh="refreshData">
-          <template #left>
-            <ElSpace wrap>
-              <ElButton v-auth="'add'" @click="showDialog('add')" v-ripple>鏂板鍒嗙粍</ElButton>
-              <ElButton
-                v-auth="'delete'"
-                type="danger"
-                :disabled="selectedRows.length === 0"
-                @click="handleBatchDelete"
-                v-ripple
-              >
-                鎵归噺鍒犻櫎
-              </ElButton>
-              <ListExportPrint
-                class="inline-flex"
-                :preview-visible="previewVisible"
-                @update:previewVisible="handlePreviewVisibleChange"
-                :report-title="reportTitle"
-                :selected-rows="selectedRows"
-                :query-params="reportQueryParams"
-                :columns="columns"
-                :preview-rows="previewRows"
-                :preview-meta="resolvedPreviewMeta"
-                :total="pagination.total"
-                :disabled="loading"
-                @export="handleExport"
-                @print="handlePrint"
-              />
-            </ElSpace>
-          </template>
-        </ArtTableHeader>
+      <MatnrGroupDialog
+        v-model:visible="dialogVisible"
+        :dialog-type="dialogType"
+        :group-data="currentGroupData"
+        :parent-group-options="parentGroupOptions"
+        :resolve-parent-code="resolveGroupCode"
+        @submit="handleDialogSubmit"
+      />
 
-        <ArtTable
-          :loading="loading"
-          :data="data"
-          :columns="columns"
-          :pagination="pagination"
-          @selection-change="handleSelectionChange"
-          @pagination:size-change="handleSizeChange"
-          @pagination:current-change="handleCurrentChange"
-        />
-
-        <MatnrGroupDialog
-          v-model:visible="dialogVisible"
-          :dialog-type="dialogType"
-          :group-data="currentGroupData"
-          :parent-group-options="parentGroupOptions"
-          :resolve-parent-code="resolveGroupCode"
-          @submit="handleDialogSubmit"
-        />
-
-        <MatnrGroupDetailDrawer
-          v-model:visible="detailDrawerVisible"
-          :loading="detailLoading"
-          :detail="detailData"
-        />
-      </ElCard>
-    </div>
+      <MatnrGroupDetailDrawer
+        v-model:visible="detailDrawerVisible"
+        :loading="detailLoading"
+        :detail="detailData"
+      />
+    </ElCard>
   </div>
 </template>
 
 <script setup>
+  import { computed, nextTick, onMounted, reactive, ref } from 'vue'
   import { ElMessage } from 'element-plus'
-  import { computed, onMounted, ref } from 'vue'
   import { useUserStore } from '@/store/modules/user'
   import { useAuth } from '@/hooks/core/useAuth'
-  import { useTable } from '@/hooks/core/useTable'
   import { useTableColumns } from '@/hooks/core/useTableColumns'
   import { useCrudPage } from '@/views/system/common/useCrudPage'
   import { usePrintExportPage } from '@/views/system/common/usePrintExportPage'
   import ListExportPrint from '@/components/biz/list-export-print/index.vue'
-  import { defaultResponseAdapter } from '@/utils/table/tableUtils'
   import { guardRequestWithMessage } from '@/utils/sys/requestGuard'
   import {
     fetchDeleteMatnrGroup,
     fetchExportMatnrGroupReport,
     fetchGetMatnrGroupDetail,
     fetchGetMatnrGroupMany,
-    fetchMatnrGroupPage,
     fetchMatnrGroupTree,
     fetchSaveMatnrGroup,
     fetchUpdateMatnrGroup
@@ -147,7 +105,6 @@
     MATNR_GROUP_REPORT_STYLE,
     MATNR_GROUP_REPORT_TITLE,
     buildMatnrGroupDialogModel,
-    buildMatnrGroupPageQueryParams,
     buildMatnrGroupPrintRows,
     buildMatnrGroupReportMeta,
     buildMatnrGroupSavePayload,
@@ -155,11 +112,8 @@
     buildMatnrGroupTreeQueryParams,
     createMatnrGroupSearchState,
     createMatnrGroupTreeSelectOptions,
-    getMatnrGroupPaginationKey,
     normalizeMatnrGroupDetailRecord,
-    normalizeMatnrGroupListRow,
-    normalizeMatnrGroupTreeRows,
-    resolveMatnrGroupTreeNodeLabel
+    normalizeMatnrGroupTreeRows
   } from './matnrGroupPage.helpers'
 
   defineOptions({ name: 'MatnrGroup' })
@@ -167,31 +121,23 @@
   const { hasAuth } = useAuth()
   const userStore = useUserStore()
 
-  const searchForm = ref(createMatnrGroupSearchState())
-  const groupSearch = ref('')
-  const groupTreeLoading = ref(false)
-  const groupTreeData = ref([])
-  const selectedGroupId = ref(null)
+  const loading = ref(false)
+  const isExpanded = ref(true)
+  const tableRef = ref()
   const detailDrawerVisible = ref(false)
   const detailLoading = ref(false)
   const detailData = ref({})
+  const tableData = ref([])
   let handleDeleteAction = null
 
   const reportTitle = MATNR_GROUP_REPORT_TITLE
-  const reportQueryParams = computed(() =>
-    buildMatnrGroupPageQueryParams({
-      ...searchForm.value,
-      parentId: selectedGroupId.value ?? ''
-    })
-  )
+  const initialSearchState = createMatnrGroupSearchState()
+  const searchForm = reactive({ ...initialSearchState })
 
-  const groupLookupMap = computed(() => buildMatnrGroupTreeLookupMap(groupTreeData.value))
-  const parentGroupOptions = computed(() => createMatnrGroupTreeSelectOptions(groupTreeData.value))
-
-  const treeProps = {
-    label: 'displayLabel',
-    children: 'children'
-  }
+  const reportQueryParams = computed(() => buildMatnrGroupTreeQueryParams(searchForm))
+  const groupLookupMap = computed(() => buildMatnrGroupTreeLookupMap(tableData.value))
+  const parentGroupOptions = computed(() => createMatnrGroupTreeSelectOptions(tableData.value))
+  const flattenedTableRows = computed(() => flattenMatnrGroupRows(tableData.value))
 
   const searchItems = computed(() => [
     {
@@ -202,56 +148,16 @@
         clearable: true,
         placeholder: '璇疯緭鍏ュ垎缁勭紪鐮�/鍚嶇О/澶囨敞'
       }
-    },
-    {
-      label: '鍒嗙粍缂栫爜',
-      key: 'code',
-      type: 'input',
-      props: {
-        clearable: true,
-        placeholder: '璇疯緭鍏ュ垎缁勭紪鐮�'
-      }
-    },
-    {
-      label: '鍒嗙粍鍚嶇О',
-      key: 'name',
-      type: 'input',
-      props: {
-        clearable: true,
-        placeholder: '璇疯緭鍏ュ垎缁勫悕绉�'
-      }
-    },
-    {
-      label: '涓婄骇缂栫爜',
-      key: 'parCode',
-      type: 'input',
-      props: {
-        clearable: true,
-        placeholder: '璇疯緭鍏ヤ笂绾х紪鐮�'
-      }
-    },
-    {
-      label: '鐘舵��',
-      key: 'status',
-      type: 'select',
-      props: {
-        clearable: true,
-        options: [
-          { label: '姝e父', value: 1 },
-          { label: '鍐荤粨', value: 0 }
-        ]
-      }
-    },
-    {
-      label: '澶囨敞',
-      key: 'memo',
-      type: 'input',
-      props: {
-        clearable: true,
-        placeholder: '璇疯緭鍏ュ娉�'
-      }
     }
   ])
+
+  function flattenMatnrGroupRows(records = []) {
+    if (!Array.isArray(records)) {
+      return []
+    }
+
+    return records.flatMap((item) => [item, ...flattenMatnrGroupRows(item?.children || [])])
+  }
 
   function resolveGroupCode(id) {
     return groupLookupMap.value.get(Number(id))?.code || ''
@@ -262,51 +168,53 @@
     if (!node) {
       return ''
     }
-    return node.displayLabel || resolveMatnrGroupTreeNodeLabel(node)
+    return node.displayLabel || node.label || [node.name, node.code].filter(Boolean).join(' 路 ')
   }
 
-  const selectedGroupLabel = computed(() => {
-    if (selectedGroupId.value === null || selectedGroupId.value === undefined) {
-      return '鍏ㄩ儴鍒嗙粍'
-    }
-    return resolveGroupLabel(selectedGroupId.value) || '鍏ㄩ儴鍒嗙粍'
-  })
+  const { columnChecks, columns } = useTableColumns(() =>
+    createMatnrGroupTableColumns({
+      handleView: openDetail,
+      handleEdit: hasAuth('update') ? openEditDialog : null,
+      handleDelete: hasAuth('delete') ? (row) => handleDeleteAction?.(row) : null,
+      canEdit: hasAuth('update'),
+      canDelete: hasAuth('delete')
+    })
+  )
 
-  async function applyTableFilters() {
-    replaceSearchParams(
-      buildMatnrGroupPageQueryParams({
-        ...searchForm.value,
-        parentId: selectedGroupId.value ?? ''
+  async function syncTreeExpandState() {
+    await nextTick()
+    if (!tableRef.value?.elTableRef) {
+      return
+    }
+
+    const toggleRows = (rows = []) => {
+      rows.forEach((row) => {
+        if (Array.isArray(row.children) && row.children.length) {
+          tableRef.value.elTableRef.toggleRowExpansion(row, isExpanded.value)
+          toggleRows(row.children)
+        }
       })
-    )
-    await getData()
+    }
+
+    toggleRows(tableData.value)
   }
 
   async function loadGroupTree() {
-    groupTreeLoading.value = true
-    let selectionCleared = false
+    loading.value = true
     try {
       const records = await guardRequestWithMessage(
-        fetchMatnrGroupTree(buildMatnrGroupTreeQueryParams({ condition: groupSearch.value })),
+        fetchMatnrGroupTree(buildMatnrGroupTreeQueryParams(searchForm)),
         [],
         { timeoutMessage: '鐗╂枡鍒嗙粍鍔犺浇瓒呮椂锛屽凡鍋滄绛夊緟' }
       )
-      const normalizedTree = normalizeMatnrGroupTreeRows(Array.isArray(records) ? records : [])
-      groupTreeData.value = normalizedTree
-      if (selectedGroupId.value && !groupLookupMap.value.get(Number(selectedGroupId.value))) {
-        selectedGroupId.value = null
-        selectionCleared = true
-      }
-      if (selectionCleared) {
-        await applyTableFilters()
-      }
+      tableData.value = normalizeMatnrGroupTreeRows(Array.isArray(records) ? records : [])
+      await syncTreeExpandState()
     } catch (error) {
-      groupTreeData.value = []
+      tableData.value = []
       ElMessage.error(error?.message || '鐗╂枡鍒嗙粍鍔犺浇澶辫触')
     } finally {
-      groupTreeLoading.value = false
+      loading.value = false
     }
-    return selectionCleared
   }
 
   async function loadGroupDetail(id) {
@@ -314,32 +222,6 @@
       timeoutMessage: '鐗╂枡鍒嗙粍璇︽儏鍔犺浇瓒呮椂锛屽凡鍋滄绛夊緟'
     })
   }
-
-  const { columns, columnChecks, data, loading, pagination, getData, replaceSearchParams, resetSearchParams, handleSizeChange, handleCurrentChange, refreshData, refreshCreate, refreshUpdate, refreshRemove } =
-    useTable({
-      core: {
-        apiFn: fetchMatnrGroupPage,
-        apiParams: buildMatnrGroupPageQueryParams(reportQueryParams.value),
-        paginationKey: getMatnrGroupPaginationKey(),
-        columnsFactory: () =>
-          createMatnrGroupTableColumns({
-            handleView: openDetail,
-            handleEdit: hasAuth('update') ? openEditDialog : null,
-            handleDelete: hasAuth('delete') ? (row) => handleDeleteAction?.(row) : null,
-            resolveParentLabel: resolveGroupLabel,
-            canEdit: hasAuth('update'),
-            canDelete: hasAuth('delete')
-          })
-      },
-      transform: {
-        dataTransformer: (records) => {
-          if (!Array.isArray(records)) {
-            return []
-          }
-          return records.map((item) => normalizeMatnrGroupListRow(item, resolveGroupLabel))
-        }
-      }
-    })
 
   const {
     dialogVisible,
@@ -354,8 +236,8 @@
   } = useCrudPage({
     createEmptyModel: () =>
       buildMatnrGroupDialogModel({}, {
-        parentId: selectedGroupId.value ?? 0,
-        parCode: resolveGroupCode(selectedGroupId.value ?? 0),
+        parentId: 0,
+        parCode: '',
         resolveParentCode: resolveGroupCode
       }),
     buildEditModel: (record) => buildMatnrGroupDialogModel(record, { resolveParentCode: resolveGroupCode }),
@@ -375,18 +257,11 @@
     deleteRequest: fetchDeleteMatnrGroup,
     entityName: '鐗╂枡鍒嗙粍',
     resolveRecordLabel: (record) => record?.name || record?.code || record?.id,
-    refreshCreate: refreshTreeAndTable,
-    refreshUpdate: refreshTreeAndTable,
-    refreshRemove: refreshTreeAndTable
+    refreshCreate: loadGroupTree,
+    refreshUpdate: loadGroupTree,
+    refreshRemove: loadGroupTree
   })
   handleDeleteAction = handleDelete
-
-  async function refreshTreeAndTable() {
-    const selectionCleared = await loadGroupTree()
-    if (!selectionCleared) {
-      await refreshData()
-    }
-  }
 
   const buildPreviewMeta = (rows) => {
     const now = new Date()
@@ -401,15 +276,9 @@
 
   const resolvePrintRecords = async (payload) => {
     if (Array.isArray(payload?.ids) && payload.ids.length > 0) {
-      return defaultResponseAdapter(await fetchGetMatnrGroupMany(payload.ids)).records
+      return await fetchGetMatnrGroupMany(payload.ids)
     }
-    return defaultResponseAdapter(
-      await fetchMatnrGroupPage({
-        ...reportQueryParams.value,
-        current: 1,
-        pageSize: Number(pagination.total) > 0 ? Number(pagination.total) : Number(payload?.pageSize) || 20
-      })
-    ).records
+    return flattenedTableRows.value
   }
 
   const {
@@ -440,34 +309,17 @@
     })
   )
 
-  function handleSearch(params) {
-    replaceSearchParams(
-      buildMatnrGroupPageQueryParams({
-        ...params,
-        parentId: selectedGroupId.value ?? ''
-      })
-    )
-    getData()
+  function handleSearch() {
+    loadGroupTree()
   }
 
   function handleReset() {
-    Object.assign(searchForm.value, createMatnrGroupSearchState())
-    selectedGroupId.value = null
-    resetSearchParams()
+    Object.assign(searchForm, { ...initialSearchState })
+    loadGroupTree()
   }
 
-  async function handleGroupSearch() {
-    await loadGroupTree()
-  }
-
-  async function handleGroupNodeClick(node) {
-    selectedGroupId.value = Number(node?.id || 0) || null
-    await applyTableFilters()
-  }
-
-  async function handleResetGroup() {
-    selectedGroupId.value = null
-    await applyTableFilters()
+  function handleRefresh() {
+    loadGroupTree()
   }
 
   async function openDetail(row) {
@@ -494,8 +346,12 @@
     }
   }
 
-  onMounted(async () => {
-    await loadGroupTree()
-    await getData()
+  function toggleExpand() {
+    isExpanded.value = !isExpanded.value
+    syncTreeExpandState()
+  }
+
+  onMounted(() => {
+    loadGroupTree()
   })
 </script>

--
Gitblit v1.9.1