<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: '项目代码',
|
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="fullscreen-card flex-col-stretch"
|
: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>
|