|
|
@@ -1,251 +0,0 @@
|
|
|
-<template>
|
|
|
- <div class="el-table-page">
|
|
|
- <el-table :data="TASK_TEMPLATE_HEADER" :show-header="false" border>
|
|
|
- <el-table-column prop="title" align="center" width="60" />
|
|
|
- <el-table-column prop="content" align="center" />
|
|
|
- </el-table>
|
|
|
- <el-table
|
|
|
- v-bind="$attrs"
|
|
|
- :data="mergedTableData"
|
|
|
- :span-method="handleSpanMethod"
|
|
|
- border
|
|
|
- class="custom-table"
|
|
|
- :row-class-name="tableRowClassName"
|
|
|
- >
|
|
|
- <el-table-column v-for="column in TASK_TEMPLATE_MAIN_COLUMNS" :key="column.prop" v-bind="column" align="center">
|
|
|
- <template #default="scope">
|
|
|
- <!-- 底部表格内容 - 第一列 -->
|
|
|
- <template v-if="scope.row.rowType === 'footer' && column.prop === 'inspectItem'">
|
|
|
- <div v-if="scope.row.footerType === 'inspectionAndSelftest'" class="inspection-result--div">
|
|
|
- <span>检查结论及存在问题:</span>
|
|
|
- <el-input
|
|
|
- v-model="scope.row.inspectionResult"
|
|
|
- maxlength="200"
|
|
|
- style="width: 100%"
|
|
|
- placeholder="必填,输入检查结论及存在问题,1-200字"
|
|
|
- type="textarea"
|
|
|
- :rows="2"
|
|
|
- v-show="operationType === 'edit'"
|
|
|
- />
|
|
|
- </div>
|
|
|
- </template>
|
|
|
-
|
|
|
- <!-- 底部表格内容 - 第二列 -->
|
|
|
- <template v-else-if="scope.row.rowType === 'footer' && column.prop === 'inspectStandard'">
|
|
|
- <div v-if="scope.row.footerType === 'inspectionAndSelftest'" class="inspection-result--div">
|
|
|
- <span>被检查(自查)单位意见:</span>
|
|
|
- <el-input
|
|
|
- v-model="scope.row.reviewResult"
|
|
|
- maxlength="200"
|
|
|
- style="width: 100%"
|
|
|
- placeholder="必填,输入被检查(自查)单位意见,1-200字"
|
|
|
- type="textarea"
|
|
|
- :rows="2"
|
|
|
- v-show="operationType === 'edit'"
|
|
|
- />
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
-
|
|
|
- <!-- 自检检查结果列 -->
|
|
|
- <el-table-column label="自检检查结果" align="center" fixed="right">
|
|
|
- <el-table-column prop="compliance" label="符合" align="center" width="150">
|
|
|
- <template #default="scope">
|
|
|
- <Compliance :radio-value="scope.row.compliance" :disabled="operationType === 'view'" />
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column prop="actualSituation" label="实际情况" align="center" width="410">
|
|
|
- <template #default="scope">
|
|
|
- <ActualSituation v-show="operationType === 'edit'" :actual-situation="scope.row.actualSituation" />
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- </el-table-column>
|
|
|
- </el-table>
|
|
|
- </div>
|
|
|
-</template>
|
|
|
-
|
|
|
-<script lang="ts" setup>
|
|
|
- import { ref, computed, onMounted } from 'vue';
|
|
|
- import type { SpanTableData, MainRow, MergedRow } from '../type';
|
|
|
- import { formatSpanTableData } from '../utils/format-data';
|
|
|
- import {
|
|
|
- TASK_TEMPLATE_HEADER,
|
|
|
- TASK_TEMPLATE_FOOTER,
|
|
|
- TASK_TEMPLATE_MAIN_COLUMNS,
|
|
|
- MERGE_FIELDS,
|
|
|
- } from '../constants/template-detail';
|
|
|
- import Compliance from './Compliance.vue';
|
|
|
- import ActualSituation from './ActualSituation.vue';
|
|
|
-
|
|
|
- const props = defineProps<{
|
|
|
- operationType: 'view' | 'edit';
|
|
|
- mainTable: SpanTableData[];
|
|
|
- }>();
|
|
|
-
|
|
|
- // 创建一个合并的数据源
|
|
|
- const mergedTableData = computed<MergedRow[]>(() => {
|
|
|
- const formattedMainData = formatSpanTableData(props.mainTable).map((item) => ({
|
|
|
- ...item,
|
|
|
- rowType: 'main' as const,
|
|
|
- }));
|
|
|
- // 为底部添加类型标记
|
|
|
- const footerData = [
|
|
|
- {
|
|
|
- ...TASK_TEMPLATE_FOOTER[0],
|
|
|
- rowType: 'footer' as const,
|
|
|
- footerType: 'inspectionAndSelftest' as const, // 合并为一行
|
|
|
- },
|
|
|
- ];
|
|
|
-
|
|
|
- return [...formattedMainData, ...footerData];
|
|
|
- });
|
|
|
-
|
|
|
- // 存储每列的合并状态
|
|
|
- const spanMaps = ref<{ [key: string]: number[][] }>({});
|
|
|
-
|
|
|
- // 根据列索引获取对应的属性名
|
|
|
- const getPropNameByColumnIndex = (columnIndex: number) => {
|
|
|
- return TASK_TEMPLATE_MAIN_COLUMNS[columnIndex]?.prop || '';
|
|
|
- };
|
|
|
-
|
|
|
- // 初始化合并信息
|
|
|
- const initSpanMaps = () => {
|
|
|
- const mainData = mergedTableData.value.filter((item): item is MainRow => item.rowType === 'main');
|
|
|
- if (!mainData.length) return;
|
|
|
-
|
|
|
- MERGE_FIELDS.forEach((field) => {
|
|
|
- // 每个字段用于存储 [行的起始位置, 合并的行数] 的二维数组
|
|
|
- const cellSpans: number[][] = [];
|
|
|
- let currentValue = mainData[0][field];
|
|
|
- let currentIndex = mainData[0].index;
|
|
|
- let startIndex = 0;
|
|
|
- let spanCount = 1;
|
|
|
-
|
|
|
- for (let i = 1; i < mainData.length; i++) {
|
|
|
- // 只有当当前值与前一个值相同,且index也相同时才合并
|
|
|
- if (mainData[i][field] === currentValue && mainData[i].index === currentIndex) {
|
|
|
- spanCount++;
|
|
|
- } else {
|
|
|
- cellSpans.push([startIndex, spanCount]);
|
|
|
- currentValue = mainData[i][field];
|
|
|
- currentIndex = mainData[i].index;
|
|
|
- startIndex = i;
|
|
|
- spanCount = 1;
|
|
|
- }
|
|
|
- }
|
|
|
- // 处理最后一组数据
|
|
|
- cellSpans.push([startIndex, spanCount]);
|
|
|
- spanMaps.value[field] = cellSpans;
|
|
|
- });
|
|
|
- };
|
|
|
-
|
|
|
- // 自定义合并方法
|
|
|
- const handleSpanMethod = ({
|
|
|
- row,
|
|
|
- column,
|
|
|
- rowIndex,
|
|
|
- columnIndex,
|
|
|
- }: {
|
|
|
- row: MergedRow;
|
|
|
- column: { property: string };
|
|
|
- rowIndex: number;
|
|
|
- columnIndex: number;
|
|
|
- }) => {
|
|
|
- // 处理底部行
|
|
|
- if (row.rowType === 'footer') {
|
|
|
- if (column.property === 'inspectItem') {
|
|
|
- if (row.footerType === 'inspectionAndSelftest') {
|
|
|
- // 第一个单元格占据左半部分
|
|
|
- return { rowspan: 1, colspan: 3 };
|
|
|
- }
|
|
|
- } else if (column.property === 'inspectStandard' && row.footerType === 'inspectionAndSelftest') {
|
|
|
- // 第二个单元格占据右半部分
|
|
|
- return { rowspan: 1, colspan: 3 };
|
|
|
- }
|
|
|
- // 隐藏其余单元格
|
|
|
- return { rowspan: 0, colspan: 0 };
|
|
|
- }
|
|
|
-
|
|
|
- // 处理主表数据的合并
|
|
|
- if (row.rowType === 'main') {
|
|
|
- const mainDataStartIndex = mergedTableData.value.findIndex((item) => item.rowType === 'main');
|
|
|
- const adjustedRowIndex = rowIndex - mainDataStartIndex;
|
|
|
-
|
|
|
- const propName = getPropNameByColumnIndex(columnIndex);
|
|
|
-
|
|
|
- // 如果不在需要合并的字段列表中,直接返回不合并
|
|
|
- if (!MERGE_FIELDS.includes(propName)) {
|
|
|
- return { rowspan: 1, colspan: 1 };
|
|
|
- }
|
|
|
-
|
|
|
- // 获取当前字段的合并信息
|
|
|
- const cellSpans = spanMaps.value[propName];
|
|
|
- if (!cellSpans) return { rowspan: 1, colspan: 1 };
|
|
|
-
|
|
|
- // 查找当前行是否是需要合并的起始行
|
|
|
- for (const [startIndex, spanCount] of cellSpans) {
|
|
|
- if (adjustedRowIndex === startIndex) {
|
|
|
- // 是起始行,显示并合并
|
|
|
- return { rowspan: spanCount, colspan: 1 };
|
|
|
- } else if (adjustedRowIndex > startIndex && adjustedRowIndex < startIndex + spanCount) {
|
|
|
- // 不是起始行,隐藏
|
|
|
- return { rowspan: 0, colspan: 0 };
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return { rowspan: 1, colspan: 1 };
|
|
|
- };
|
|
|
-
|
|
|
- // 新增:底部行粘性定位 class
|
|
|
- const tableRowClassName = ({ row }: { row: MergedRow }) => {
|
|
|
- if (row.rowType !== 'footer') return;
|
|
|
- if (row.footerType === 'inspectionAndSelftest') {
|
|
|
- return 'sticky-footer-inspection';
|
|
|
- }
|
|
|
- return;
|
|
|
- };
|
|
|
-
|
|
|
- // 监听数据变化,重新计算合并状态
|
|
|
- onMounted(() => {
|
|
|
- initSpanMaps();
|
|
|
- });
|
|
|
-</script>
|
|
|
-
|
|
|
-<style lang="scss" scoped>
|
|
|
- .el-table-page {
|
|
|
- width: 100%;
|
|
|
- height: 100%;
|
|
|
- }
|
|
|
-
|
|
|
- .inspection-result--div {
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- align-items: flex-start;
|
|
|
- gap: 5cpx;
|
|
|
- padding: 5cpx;
|
|
|
- height: 90cpx;
|
|
|
- }
|
|
|
-
|
|
|
- :deep(.custom-table .el-table__inner-wrapper:after) {
|
|
|
- height: 0;
|
|
|
- }
|
|
|
-
|
|
|
- :deep(.custom-table th) {
|
|
|
- background-color: $white-color !important;
|
|
|
- color: #606266 !important;
|
|
|
- font-weight: 500 !important;
|
|
|
- }
|
|
|
-
|
|
|
- .sticky-footer {
|
|
|
- position: sticky;
|
|
|
- z-index: 2;
|
|
|
- }
|
|
|
-
|
|
|
- :deep(.sticky-footer-inspection) {
|
|
|
- @extend .sticky-footer;
|
|
|
- bottom: 0;
|
|
|
- box-shadow: 0 -2px 5px rgba(0, 0, 0, 0.1);
|
|
|
- }
|
|
|
-</style>
|