Browse Source

打印模板实现多级表头

ljb 11 months ago
parent
commit
b57e1ab823

+ 1 - 1
src/components/print-table-btn/index.vue

@@ -8,7 +8,7 @@
     >
     >
       打印
       打印
     </el-button>
     </el-button>
-    <el-checkbox v-model="landscape" style="display: inline-block; vertical-align: bottom;">横向打印</el-checkbox>
+    <!-- <el-checkbox v-model="landscape" style="display: inline-block; vertical-align: bottom;">横向打印</el-checkbox> -->
   </span>
   </span>
 </template>
 </template>
 
 

+ 113 - 4
src/components/print-wide-table/index.vue

@@ -67,7 +67,7 @@
           <div class="print-meta">打印时间: {{ new Date().toLocaleString() }}</div>
           <div class="print-meta">打印时间: {{ new Date().toLocaleString() }}</div>
         </div>
         </div>
         <table class="wide-table">
         <table class="wide-table">
-          <thead>
+          <!-- <thead>
             <tr>
             <tr>
               <th v-for="(col, index) in columns" :key="index" 
               <th v-for="(col, index) in columns" :key="index" 
                   :style="getColumnStyle(col)"
                   :style="getColumnStyle(col)"
@@ -75,10 +75,23 @@
                 {{ col.label }}
                 {{ col.label }}
               </th>
               </th>
             </tr>
             </tr>
+          </thead> -->
+           <thead>
+            <tr v-for="(row, rowIndex) in headerRows" :key="'row-' + rowIndex">
+              <th 
+                v-for="(col, colIndex) in row" 
+                :key="'col-' + colIndex"
+                :rowspan="col.rowspan"
+                :colspan="col.colspan"
+                :style="{ width: col.width + 'px' }"
+              >
+                {{ col.label }}
+              </th>
+            </tr>
           </thead>
           </thead>
           <tbody>
           <tbody>
             <tr v-for="(row, rowIndex) in getPageData(page)" :key="rowIndex">
             <tr v-for="(row, rowIndex) in getPageData(page)" :key="rowIndex">
-              <td v-for="(col, colIndex) in columns" :key="colIndex" 
+              <td v-for="(col, colIndex) in tableFields" :key="colIndex" 
                   :style="getColumnStyle(col)"
                   :style="getColumnStyle(col)"
                   :class="{
                   :class="{
                     'fixed-column': col.fixed,
                     'fixed-column': col.fixed,
@@ -139,7 +152,12 @@ export default {
       isPreviewing: false,
       isPreviewing: false,
       currentPage: 1,
       currentPage: 1,
       columnWidths: {},
       columnWidths: {},
-      tableWidth: 0
+      tableWidth: 0,
+
+      // 计算后的表头行数据
+      headerRows: [],
+      // 扁平化的字段配置
+      tableFields: []
     }
     }
   },
   },
   computed: {
   computed: {
@@ -174,6 +192,90 @@ export default {
     window.removeEventListener('resize', this.calculateColumnWidths)
     window.removeEventListener('resize', this.calculateColumnWidths)
   },
   },
   methods: {
   methods: {
+    // 处理表头配置,生成表头行数据和扁平字段
+    processHeaderConfig(headerConfig) {
+      // 1. 扁平化字段配置
+      this.flattenFields(headerConfig)
+      
+      // 2. 生成表头行数据
+      const maxDepth = this.getMaxDepth(headerConfig)
+      this.headerRows = Array(maxDepth).fill().map(() => [])
+      
+      this.buildHeaderRows(headerConfig, 0, 0)
+    },
+    
+    // 扁平化字段配置
+    flattenFields(config, parentPath = '') {
+      config.forEach(item => {
+        if (item.prop) {
+          this.tableFields.push({
+            prop: item.prop,
+            label: item.label,
+            width: item.width
+          })
+        }
+        
+        if (item.children) {
+          this.flattenFields(item.children, parentPath + item.label + '/')
+        }
+      })
+    },
+    
+    // 获取表头最大深度
+    getMaxDepth(config) {
+      let maxDepth = 1
+      config.forEach(item => {
+        if (item.children) {
+          const depth = 1 + this.getMaxDepth(item.children)
+          if (depth > maxDepth) {
+            maxDepth = depth
+          }
+        }
+      })
+      return maxDepth
+    },
+    
+    // 构建表头行数据
+    buildHeaderRows(config, currentRow, currentCol) {
+      config.forEach(item => {
+        // 计算当前项跨越的行数和列数
+        const rowspan = item.rowspan || 1
+        const colspan = item.colspan || this.calculateColspan(item)
+        
+        // 在当前行和后续行中添加表头单元格
+        for (let i = 0; i < rowspan; i++) {
+          if (currentRow + i < this.headerRows.length) {
+            if (i === 0) {
+              // 主单元格
+              this.headerRows[currentRow + i].push({
+                label: item.label,
+                rowspan,
+                colspan,
+                width: item.width
+              })
+            } else {
+              // 被合并的行,添加空单元格占位
+              this.headerRows[currentRow + i].push(null)
+            }
+          }
+        }
+        
+        // 如果有子项,递归处理
+        if (item.children) {
+          this.buildHeaderRows(item.children, currentRow + 1, currentCol)
+        }
+        
+        currentCol += colspan
+      })
+    },
+    
+    // 计算列跨度
+    calculateColspan(item) {
+      if (item.children) {
+        return item.children.reduce((sum, child) => sum + this.calculateColspan(child), 0)
+      }
+      return 1
+    },
     getColumnStyle(col) {
     getColumnStyle(col) {
       const style = {}
       const style = {}
       if (this.columnWidths[col.prop]) {
       if (this.columnWidths[col.prop]) {
@@ -251,7 +353,6 @@ export default {
       //   landscape = isLandscape;
       //   landscape = isLandscape;
       // }
       // }
       let landscape = isLandscape;
       let landscape = isLandscape;
-      console.log(landscape)
       
       
       printWindow.document.open()
       printWindow.document.open()
       printWindow.document.write(`
       printWindow.document.write(`
@@ -366,6 +467,14 @@ export default {
       this.$nextTick(() => {
       this.$nextTick(() => {
         this.calculateColumnWidths()
         this.calculateColumnWidths()
       })
       })
+    },
+    columns: {
+      handler(newVal) {
+        this.tableFields = [];
+        this.processHeaderConfig(newVal);
+      },
+      deep: true,
+      immediate: true
     }
     }
   }
   }
 }
 }

+ 34 - 0
src/views/tech-person/cy-person-roster.vue

@@ -16,6 +16,7 @@
         >
         >
           导出
           导出
         </el-button>
         </el-button>
+        <print-table-btn @click="printTable" />
         <div>
         <div>
           <year-month-select v-model="params.yearAndMonth" :showMonth="false"></year-month-select>
           <year-month-select v-model="params.yearAndMonth" :showMonth="false"></year-month-select>
         </div>
         </div>
@@ -30,6 +31,16 @@
       </template>
       </template>
       
       
     </avue-crud>
     </avue-crud>
+
+    <WideTablePrinter
+      ref="printWideTable"
+      v-if="wideTableColumns.length"
+      :columns="wideTableColumns"
+      :data="data"
+      :print-title="printTitle"
+      :rows-per-page="30"
+      :default-landscape="true"
+    />
   </basic-container>
   </basic-container>
 </template>
 </template>
 
 
@@ -52,6 +63,8 @@ export default window.$crudCommon({
       params: {
       params: {
         yearAndMonth: moment(new Date()).format('YYYY'),
         yearAndMonth: moment(new Date()).format('YYYY'),
       },
       },
+      wideTableColumns: [],
+      printTitle: "",
     };
     };
   },
   },
   watch: {
   watch: {
@@ -74,6 +87,27 @@ export default window.$crudCommon({
         })
         })
       });
       });
     },
     },
+    filterHidden(items) {
+      if (!items) return null;
+      return items
+        .filter(item => !item.hide)
+        .map(item => ({
+          ...item,
+          children: item.children ? this.filterHidden(item.children) : undefined
+        }))
+        .filter(item => Object.keys(item).length > 0); // 移除空对象
+    },
+    /**
+     * 打印表格
+     * @param isLandscape 是否横向打印
+     */
+    printTable(isLandscape) {
+      this.wideTableColumns = this.filterHidden(this.$refs.crud.columnOption);
+      this.printTitle = `参研人员花名册${this.params.yearAndMonth}`;
+      this.$nextTick(() => {
+        this.$refs.printWideTable.printTable(isLandscape);
+      })
+    },
   },
   },
 }, {
 }, {
   // 模块路径
   // 模块路径