From 3471290659516cf21db3211a9053daff5f283e03 Mon Sep 17 00:00:00 2001
From: zhuguifei <312353457@qq.com>
Date: 星期五, 20 三月 2026 15:50:18 +0800
Subject: [PATCH] feat: 基础数据仪器管理、判定依据、判定依据明细
---
ruoyi-plus-soybean/src/views/qm/std/modules/std-sub-table.vue | 408 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 408 insertions(+), 0 deletions(-)
diff --git a/ruoyi-plus-soybean/src/views/qm/std/modules/std-sub-table.vue b/ruoyi-plus-soybean/src/views/qm/std/modules/std-sub-table.vue
new file mode 100755
index 0000000..8aad960
--- /dev/null
+++ b/ruoyi-plus-soybean/src/views/qm/std/modules/std-sub-table.vue
@@ -0,0 +1,408 @@
+<script setup lang="tsx">
+import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue';
+import type { DataTableColumns, DataTableRowKey } from 'naive-ui';
+import { NDivider } from 'naive-ui';
+import { useLoading } from '@sa/hooks';
+import { jsonClone } from '@sa/utils';
+import { fetchBatchDeleteCheckitem, fetchGetCheckitemTree } from '@/service/api/qm/checkitem';
+import { useAuth } from '@/hooks/business/auth';
+import { $t } from '@/locales';
+import ButtonIcon from '@/components/custom/button-icon.vue';
+import CheckitemOperateDrawer from '@/views/qm/checkitem/modules/checkitem-operate-drawer.vue';
+
+defineOptions({
+ name: 'StdSubTable'
+});
+
+interface Props {
+ stdId?: CommonType.IdType | null;
+ stdCode?: string | '';
+}
+
+const props = withDefaults(defineProps<Props>(), {
+ stdId: null,
+ stdCode: ''
+});
+
+interface StdSubRow extends Api.Qm.Checkitem {
+ children?: StdSubRow[];
+}
+
+const { loading, startLoading, endLoading } = useLoading();
+const { hasAuth } = useAuth();
+const rows = ref<StdSubRow[]>([]);
+const checkedRowKeys = ref<CommonType.IdType[]>([]);
+const drawerVisible = ref(false);
+const operateType = ref<NaiveUI.TableOperateType>('add');
+const editingData = ref<Api.Qm.Checkitem | null>(null);
+const showFullscreen = ref(false);
+const fullscreenStyle = ref<Record<string, string>>({});
+const onFullscreenKeydown = (event: KeyboardEvent) => {
+ if (event.key === 'Escape') {
+ showFullscreen.value = false;
+ }
+};
+
+const cardTitle = computed(() => `瑙勭▼鏄庣粏${props.stdCode || ''}`);
+
+async function getSubList() {
+ if (!props.stdId && !props.stdCode) {
+ rows.value = [];
+ return;
+ }
+
+ startLoading();
+ try {
+ const { data, error } = await fetchGetCheckitemTree({
+ pageNum: 1,
+ pageSize: 9999,
+ stdCode: props.stdId != null ? String(props.stdId) : String(props.stdCode)
+ });
+ if (error) {
+ rows.value = [];
+ return;
+ }
+
+ rows.value = data.rows;
+ } finally {
+ endLoading();
+ }
+}
+
+watch(
+ () => [props.stdId, props.stdCode],
+ async () => {
+ checkedRowKeys.value = [];
+ await getSubList();
+ },
+ { immediate: true }
+);
+
+watch(
+ showFullscreen,
+ visible => {
+ if (visible) {
+ updateFullscreenStyle();
+ window.addEventListener('keydown', onFullscreenKeydown);
+ window.addEventListener('resize', updateFullscreenStyle);
+ window.addEventListener('scroll', updateFullscreenStyle, true);
+ return;
+ }
+ window.removeEventListener('keydown', onFullscreenKeydown);
+ window.removeEventListener('resize', updateFullscreenStyle);
+ window.removeEventListener('scroll', updateFullscreenStyle, true);
+ },
+ { immediate: true }
+);
+
+onBeforeUnmount(() => {
+ window.removeEventListener('keydown', onFullscreenKeydown);
+ window.removeEventListener('resize', updateFullscreenStyle);
+ window.removeEventListener('scroll', updateFullscreenStyle, true);
+});
+
+onMounted(() => {
+ updateFullscreenStyle();
+});
+
+function updateFullscreenStyle() {
+ const container = document.querySelector<HTMLElement>('.std-content-area');
+ if (!container) {
+ fullscreenStyle.value = {
+ position: 'fixed',
+ inset: '0',
+ zIndex: '20'
+ };
+ return;
+ }
+ const rect = container.getBoundingClientRect();
+ fullscreenStyle.value = {
+ position: 'fixed',
+ left: `${rect.left}px`,
+ top: `${rect.top}px`,
+ width: `${rect.width}px`,
+ height: `${rect.height}px`,
+ zIndex: '20'
+ };
+}
+
+function handleAdd() {
+ if (!props.stdId && !props.stdCode) {
+ window.$message?.warning('璇峰厛閫夋嫨瑙勭▼');
+ return;
+ }
+ operateType.value = 'add';
+ editingData.value = null;
+ drawerVisible.value = true;
+}
+
+function handleEdit(row: StdSubRow) {
+ operateType.value = 'edit';
+ editingData.value = jsonClone(row);
+ drawerVisible.value = true;
+}
+
+async function handleBatchDelete() {
+ if (checkedRowKeys.value.length === 0) return;
+ const { error } = await fetchBatchDeleteCheckitem(checkedRowKeys.value);
+ if (error) return;
+ window.$message?.success($t('common.deleteSuccess'));
+ checkedRowKeys.value = [];
+ await getSubList();
+}
+
+async function handleDelete(id: CommonType.IdType) {
+ const { error } = await fetchBatchDeleteCheckitem([id]);
+ if (error) return;
+ window.$message?.success($t('common.deleteSuccess'));
+ await getSubList();
+}
+
+const columns = computed<DataTableColumns<StdSubRow>>(() => [
+ {
+ type: 'selection',
+ align: 'center',
+ width: 48
+ },
+ {
+ key: 'index',
+ title: $t('common.index'),
+ align: 'center',
+ width: 56,
+ render: (_: any, index: number) => index + 1
+ },
+ {
+ key: 'itemName',
+ title: '椤圭洰鍚嶇О',
+ align: 'left',
+ width: 400,
+ tree: true
+ } as any,
+ {
+ key: 'itemCode',
+ title: '椤圭洰浠g爜',
+ align: 'center',
+ width: 180
+ },
+ {
+ key: 'unit',
+ title: '鍗曚綅',
+ align: 'center',
+ width: 80
+ },
+ {
+ key: 'enable',
+ title: '鍚敤',
+ align: 'center',
+ width: 72,
+ render: row => (String(row.enable) === '1' ? '鍚敤' : '鍋滅敤')
+ },
+ {
+ key: 'score',
+ title: '鍒嗗��',
+ align: 'center',
+ width: 80
+ },
+ {
+ key: 'itemDes',
+ title: '鎻忚堪',
+ align: 'center',
+ minWidth: 160
+ },
+ {
+ key: 'operate',
+ title: $t('common.operate'),
+ align: 'center',
+ width: 130,
+ render: row => {
+ const divider = () => {
+ if (!hasAuth('qm:checkitem:edit') || !hasAuth('qm:checkitem:remove')) {
+ return null;
+ }
+ return <NDivider vertical />;
+ };
+
+ const editBtn = () => {
+ if (!hasAuth('qm:checkitem:edit')) {
+ return null;
+ }
+ return (
+ <ButtonIcon
+ text
+ type="primary"
+ icon="material-symbols:drive-file-rename-outline-outline"
+ tooltipContent={$t('common.edit')}
+ onClick={() => handleEdit(row)}
+ />
+ );
+ };
+
+ const deleteBtn = () => {
+ if (!hasAuth('qm:checkitem:remove')) {
+ return null;
+ }
+ return (
+ <ButtonIcon
+ text
+ type="error"
+ icon="material-symbols:delete-outline"
+ tooltipContent={$t('common.delete')}
+ popconfirmContent={$t('common.confirmDelete')}
+ onPositiveClick={() => handleDelete(row.id)}
+ />
+ );
+ };
+
+ return (
+ <div class="flex-center gap-8px">
+ {editBtn()}
+ {divider()}
+ {deleteBtn()}
+ </div>
+ );
+ }
+ }
+]);
+
+function rowKey(row: StdSubRow): DataTableRowKey {
+ return String(row.id);
+}
+</script>
+
+<template>
+ <NCard
+ :title="cardTitle"
+ :bordered="false"
+ size="small"
+ class="flex-col-stretch card-wrapper"
+ :content-style="{ flex: 1, overflow: 'hidden', display: 'flex', flexDirection: 'column' }"
+ >
+ <template #header-extra>
+ <NSpace align="center">
+ <TableHeaderOperation
+ :disabled-delete="checkedRowKeys.length === 0"
+ :loading="loading"
+ :show-add="hasAuth('qm:checkitem:add')"
+ :show-delete="hasAuth('qm:checkitem:remove')"
+ :show-export="false"
+ @add="handleAdd"
+ @delete="handleBatchDelete"
+ @refresh="getSubList"
+ />
+ <NButton size="small" @click="showFullscreen = true">
+ <template #icon>
+ <icon-mdi-fullscreen class="text-icon" />
+ </template>
+ 鍏ㄥ睆
+ </NButton>
+ </NSpace>
+ </template>
+
+ <NSpin :show="loading" class="h-full" content-class="h-full">
+ <div v-if="!props.stdId && !props.stdCode" class="h-full flex-center text-gray-400">
+ 璇风偣鍑讳笂鏂硅〃鏍艰鏌ョ湅鏄庣粏
+ </div>
+ <NDataTable
+ v-else
+ v-model:checked-row-keys="checkedRowKeys"
+ :columns="columns as any"
+ :data="rows"
+ size="small"
+ flex-height
+ children-key="children"
+ :row-key="rowKey"
+ default-expand-all
+ striped
+ style="height: 200px"
+ />
+ </NSpin>
+ </NCard>
+
+ <Teleport to="body">
+ <div v-if="showFullscreen" class="fullscreen-mask" :style="fullscreenStyle" @click.self="showFullscreen = false">
+ <NCard
+ :title="cardTitle"
+ :bordered="false"
+ size="small"
+ class="flex-col-stretch fullscreen-card"
+ :content-style="{ flex: 1, overflow: 'hidden', display: 'flex', flexDirection: 'column' }"
+ >
+ <template #header-extra>
+ <NSpace align="center">
+ <TableHeaderOperation
+ :disabled-delete="checkedRowKeys.length === 0"
+ :loading="loading"
+ :show-add="hasAuth('qm:checkitem:add')"
+ :show-delete="hasAuth('qm:checkitem:remove')"
+ :show-export="false"
+ @add="handleAdd"
+ @delete="handleBatchDelete"
+ @refresh="getSubList"
+ />
+ <NButton size="small" ghost type="error" @click="showFullscreen = false">
+ <template #icon>
+ <icon-mdi-close class="text-icon" />
+ </template>
+ 鍏抽棴
+ </NButton>
+ </NSpace>
+ </template>
+
+ <NSpin :show="loading" class="h-full" content-class="h-full">
+ <div v-if="!props.stdId && !props.stdCode" class="h-full flex-center text-gray-400">
+ 璇风偣鍑讳笂鏂硅〃鏍艰鏌ョ湅鏄庣粏
+ </div>
+ <NDataTable
+ v-else
+ v-model:checked-row-keys="checkedRowKeys"
+ :columns="columns as any"
+ :data="rows"
+ size="small"
+ flex-height
+ children-key="children"
+ :row-key="rowKey"
+ striped
+ class="fullscreen-table"
+ />
+ </NSpin>
+ </NCard>
+ </div>
+ </Teleport>
+
+ <CheckitemOperateDrawer
+ v-model:visible="drawerVisible"
+ :operate-type="operateType"
+ :row-data="editingData"
+ :std-code="props.stdId"
+ @submitted="getSubList"
+ />
+</template>
+
+<style scoped>
+:deep(.n-card__content) {
+ padding: 8px 12px;
+}
+
+:deep(.n-data-table-th),
+:deep(.n-data-table-td) {
+ padding: 4px 6px;
+}
+
+.fullscreen-mask {
+ position: absolute;
+ inset: 0;
+ z-index: 20;
+ background: #fff;
+ padding: 0;
+ display: flex;
+}
+
+.fullscreen-card {
+ width: 100%;
+ height: 100%;
+}
+
+.fullscreen-table {
+ height: 100%;
+}
+</style>
--
Gitblit v1.9.3