<template>
|
<div class="matnr-group-page art-full-height">
|
<ArtSearchBar
|
v-model="searchForm"
|
:items="searchItems"
|
:show-expand="false"
|
@search="handleSearch"
|
@reset="handleReset"
|
/>
|
|
<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>
|
|
<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"
|
/>
|
|
<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>
|
</template>
|
|
<script setup>
|
import { computed, nextTick, onMounted, reactive, ref } from 'vue'
|
import { ElMessage } from 'element-plus'
|
import { useUserStore } from '@/store/modules/user'
|
import { useAuth } from '@/hooks/core/useAuth'
|
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 { guardRequestWithMessage } from '@/utils/sys/requestGuard'
|
import {
|
fetchDeleteMatnrGroup,
|
fetchExportMatnrGroupReport,
|
fetchGetMatnrGroupDetail,
|
fetchGetMatnrGroupMany,
|
fetchMatnrGroupTree,
|
fetchSaveMatnrGroup,
|
fetchUpdateMatnrGroup
|
} from '@/api/matnr-group'
|
import MatnrGroupDialog from './modules/matnr-group-dialog.vue'
|
import MatnrGroupDetailDrawer from './modules/matnr-group-detail-drawer.vue'
|
import { createMatnrGroupTableColumns } from './matnrGroupTable.columns'
|
import {
|
MATNR_GROUP_REPORT_STYLE,
|
MATNR_GROUP_REPORT_TITLE,
|
buildMatnrGroupDialogModel,
|
buildMatnrGroupPrintRows,
|
buildMatnrGroupReportMeta,
|
buildMatnrGroupSavePayload,
|
buildMatnrGroupTreeLookupMap,
|
buildMatnrGroupTreeQueryParams,
|
createMatnrGroupSearchState,
|
createMatnrGroupTreeSelectOptions,
|
normalizeMatnrGroupDetailRecord,
|
normalizeMatnrGroupTreeRows
|
} from './matnrGroupPage.helpers'
|
|
defineOptions({ name: 'MatnrGroup' })
|
|
const { hasAuth } = useAuth()
|
const userStore = useUserStore()
|
|
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 initialSearchState = createMatnrGroupSearchState()
|
const searchForm = reactive({ ...initialSearchState })
|
|
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(() => [
|
{
|
label: '关键字',
|
key: 'condition',
|
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 || ''
|
}
|
|
function resolveGroupLabel(id) {
|
const node = groupLookupMap.value.get(Number(id))
|
if (!node) {
|
return ''
|
}
|
return node.displayLabel || node.label || [node.name, node.code].filter(Boolean).join(' · ')
|
}
|
|
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 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)
|
}
|
})
|
}
|
|
toggleRows(tableData.value)
|
}
|
|
async function loadGroupTree() {
|
loading.value = true
|
try {
|
const records = await guardRequestWithMessage(
|
fetchMatnrGroupTree(buildMatnrGroupTreeQueryParams(searchForm)),
|
[],
|
{ timeoutMessage: '物料分组加载超时,已停止等待' }
|
)
|
tableData.value = normalizeMatnrGroupTreeRows(Array.isArray(records) ? records : [])
|
await syncTreeExpandState()
|
} catch (error) {
|
tableData.value = []
|
ElMessage.error(error?.message || '物料分组加载失败')
|
} finally {
|
loading.value = false
|
}
|
}
|
|
async function loadGroupDetail(id) {
|
return guardRequestWithMessage(fetchGetMatnrGroupDetail(id), {}, {
|
timeoutMessage: '物料分组详情加载超时,已停止等待'
|
})
|
}
|
|
const {
|
dialogVisible,
|
dialogType,
|
currentRecord: currentGroupData,
|
selectedRows,
|
handleSelectionChange,
|
showDialog,
|
handleDialogSubmit,
|
handleDelete,
|
handleBatchDelete
|
} = useCrudPage({
|
createEmptyModel: () =>
|
buildMatnrGroupDialogModel({}, {
|
parentId: 0,
|
parCode: '',
|
resolveParentCode: resolveGroupCode
|
}),
|
buildEditModel: (record) => buildMatnrGroupDialogModel(record, { resolveParentCode: resolveGroupCode }),
|
buildSavePayload: (formData) => buildMatnrGroupSavePayload(formData),
|
saveRequest: (payload) => {
|
if (payload.id !== void 0 && payload.id !== null && Number(payload.parentId) === Number(payload.id)) {
|
throw new Error('上级分组不能选择当前分组')
|
}
|
return fetchSaveMatnrGroup(payload)
|
},
|
updateRequest: (payload) => {
|
if (payload.id !== void 0 && payload.id !== null && Number(payload.parentId) === Number(payload.id)) {
|
throw new Error('上级分组不能选择当前分组')
|
}
|
return fetchUpdateMatnrGroup(payload)
|
},
|
deleteRequest: fetchDeleteMatnrGroup,
|
entityName: '物料分组',
|
resolveRecordLabel: (record) => record?.name || record?.code || record?.id,
|
refreshCreate: loadGroupTree,
|
refreshUpdate: loadGroupTree,
|
refreshRemove: loadGroupTree
|
})
|
handleDeleteAction = handleDelete
|
|
const buildPreviewMeta = (rows) => {
|
const now = new Date()
|
return {
|
reportDate: now.toLocaleDateString('zh-CN'),
|
printedAt: now.toLocaleString('zh-CN', { hour12: false }),
|
operator: userStore.getUserInfo?.name || userStore.getUserInfo?.username || '',
|
count: rows.length,
|
reportStyle: { ...MATNR_GROUP_REPORT_STYLE }
|
}
|
}
|
|
const resolvePrintRecords = async (payload) => {
|
if (Array.isArray(payload?.ids) && payload.ids.length > 0) {
|
return await fetchGetMatnrGroupMany(payload.ids)
|
}
|
return flattenedTableRows.value
|
}
|
|
const {
|
previewVisible,
|
previewRows,
|
previewMeta,
|
handlePreviewVisibleChange,
|
handleExport,
|
handlePrint
|
} = usePrintExportPage({
|
downloadFileName: 'matnr-group.xlsx',
|
requestExport: (payload) =>
|
fetchExportMatnrGroupReport(payload, {
|
headers: {
|
Authorization: userStore.accessToken || ''
|
}
|
}),
|
resolvePrintRecords,
|
buildPreviewRows: (records) => buildMatnrGroupPrintRows(records, resolveGroupLabel),
|
buildPreviewMeta
|
})
|
|
const resolvedPreviewMeta = computed(() =>
|
buildMatnrGroupReportMeta({
|
previewMeta: previewMeta.value,
|
count: previewRows.value.length,
|
orientation: previewMeta.value?.reportStyle?.orientation || MATNR_GROUP_REPORT_STYLE.orientation
|
})
|
)
|
|
function handleSearch() {
|
loadGroupTree()
|
}
|
|
function handleReset() {
|
Object.assign(searchForm, { ...initialSearchState })
|
loadGroupTree()
|
}
|
|
function handleRefresh() {
|
loadGroupTree()
|
}
|
|
async function openDetail(row) {
|
detailDrawerVisible.value = true
|
detailLoading.value = true
|
try {
|
const detail = await loadGroupDetail(row.id)
|
detailData.value = normalizeMatnrGroupDetailRecord(detail, resolveGroupLabel)
|
} catch (error) {
|
detailDrawerVisible.value = false
|
detailData.value = {}
|
ElMessage.error(error?.message || '获取物料分组详情失败')
|
} finally {
|
detailLoading.value = false
|
}
|
}
|
|
async function openEditDialog(row) {
|
try {
|
const detail = await loadGroupDetail(row.id)
|
showDialog('edit', detail)
|
} catch (error) {
|
ElMessage.error(error?.message || '获取物料分组详情失败')
|
}
|
}
|
|
function toggleExpand() {
|
isExpanded.value = !isExpanded.value
|
syncTreeExpandState()
|
}
|
|
onMounted(() => {
|
loadGroupTree()
|
})
|
</script>
|