zck hai 1 mes
pai
achega
e2e9c18ac0

+ 19 - 0
src/api/mes/job.js

@@ -26,6 +26,25 @@ export const Get_lwPrintFields = (params) => {
   })
 }
 
+//获取工商学生打印左侧数据
+export const Get_studentPrintFields = (params) => {
+  return service({
+    url: '/mes_server/work_order/Get_studentPrintFields',
+    method: 'get',
+    params
+  })
+}
+
+
+//获取打码卡片打印左侧数据
+export const Get_printCardPrintFields = (params) => {
+  return service({
+    url: '/mes_server/work_order/Get_printCardPrintFields',
+    method: 'get',
+    params
+  })
+}
+
 
 // export const getDepartment = (data) => {
 //   return service({

+ 723 - 0
src/view/dm/printcard.vue

@@ -0,0 +1,723 @@
+<template>
+    <layout>
+      <layout-header>
+        <div class="top-action-bar no-print">
+          <el-form class="demo-form-inline">
+            <el-form-item>
+              <div class="action-buttons">
+                <!-- 打印按钮 -->
+                <el-button type="primary" @click="printAllLabels" style="margin: 5px">
+                  <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16" style="margin-right: 5px">
+                    <path d="M5 1a2 2 0 0 0-2 2v1h10V3a2 2 0 0 0-2-2H5zm6 8H5a1 1 0 0 0-1 1v3a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1v-3a1 1 0 0 0-1-1z"/>
+                    <path d="M0 7a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2h-1v-2a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v2H2a2 2 0 0 1-2-2V7zm2.5 1a.5.5 0 1 0 0-1 .5.5 0 0 0 0 1z"/>
+                  </svg>
+                  打印标签
+                </el-button>
+              </div>
+            </el-form-item>
+          </el-form>
+        </div>
+      </layout-header>
+    </layout>
+<layout>
+ 
+        <!-- 左侧树状图区域 -->
+<layout-sider :resize-directions="['right']" :width="300" :height="600" v-if="showTree">
+  <div class="tree-section">
+    <div class="tree-header">
+      <h3>资产选择</h3>
+    </div>
+    <div class="tree-wrapper">
+      <el-tree
+        ref="treeRef"
+        :data="treeData"
+        node-key="id"
+        :props="treeProps"
+        @node-click="handleNodeClick"
+        @check-change="handleCheckChange"
+        show-checkbox
+        highlight-current
+        :default-expand-all="false"
+      >
+        <template #default="{ node, data }">
+          <span class="tree-node">
+            <span>{{ data.label }}</span>
+            <span class="tree-code" v-if="data.code">{{ data.code }}</span>
+            <span class="tree-count" v-if="data.count">({{ data.count }})</span>
+          </span>
+        </template>
+      </el-tree>
+    </div>
+    <div class="selected-info" v-if="selectedAsset">
+      已选择:{{ selectedAsset['物品名称'] }} ({{ selectedAsset['编码'] }})
+    </div>
+  </div>
+</layout-sider>
+        <!-- 右侧标签预览区域 -->
+        <layout-content>
+            <div class="label-wrapper" ref="labelWrapper">
+              <!-- 标签预览 - 只显示一个 -->
+              <div class="asset-label" v-if="selectedAssets.length > 0">
+                <div class="label-header">
+                  <h3>河南金芒果印刷有限公司低值易耗品</h3>
+                </div>
+                <div class="label-content" >
+                  <div class="label-field">
+                    <span class="field-label">资产名称:</span>
+                    <span class="field-value">{{ selectedAssets[0]['物品名称'] }}</span>
+                  </div>
+                  <div class="label-field">
+                    <span class="field-label">使用部门:</span>
+                    <span class="field-value">{{ selectedAssets[0]['使用部门'] }}</span>
+                  </div>
+                  <div class="label-field">
+                    <span class="field-label">存放地点:</span>
+                    <span class="field-value">{{ selectedAssets[0]['存放地点'] }}</span>
+                  </div>
+                  <div class="label-barcode-section">
+                    <div class="label-barcode">
+                      <canvas :ref="el => barcodeCanvas = el" class="barcode"></canvas>
+                      <div class="code-text">{{ selectedAssets[0]['编码'] }}</div>
+                    </div>
+                    <div class="label-logo">
+                      <img src="http://20.0.16.87:9094/uploads/certificate/中国烟草.png" alt="中国烟草" class="post-logo">
+                    </div>
+                  </div>
+                </div>
+              </div>
+              <div v-else class="no-data">
+                暂无数据
+              </div>
+            </div>
+        </layout-content>
+        </layout>
+</template>
+
+<script setup>
+import { ref, reactive, computed, onMounted, onBeforeUnmount, watch, nextTick } from 'vue'
+import { Layout, LayoutSider, LayoutContent, LayoutHeader } from '@arco-design/web-vue'
+import { ElTree, ElButton, ElForm, ElFormItem, ElMessage } from 'element-plus'
+import { Get_printCardPrintFields } from '@/api/mes/job'
+import JsBarcode from 'jsbarcode'
+
+// 树状图相关
+const showTree = ref(true)
+const treeRef = ref(null)
+const treeData = ref([])
+const selectedAsset = ref(null) // 存储单个选中的节点
+const selectedAssets = ref([]) // 存储所有资产数据
+const checkedAssets = ref([]) // 存储勾选的资产数据
+const treeProps = {
+  label: 'label',
+  children: 'children'
+}
+
+// 响应式数据
+const labelWrapper = ref(null)
+const barcodeCanvas = ref(null)
+
+// 获取打印数据字段
+const getPrintDataFields = async () => {
+  try {
+    const response = await Get_printCardPrintFields({})
+    if (response.code === 0) {
+      // response.data 是一个对象,键为物品名称,值为该物品的数组
+      const groupedData = response.data
+      
+      // 转换成树形数据结构,符合Element Plus Tree组件要求
+      const transformedTreeData = Object.keys(groupedData).map(groupName => ({
+        id: groupName,
+        label: `${groupName} (${groupedData[groupName].length}个)`,
+        value: groupName,
+        children: groupedData[groupName].map(item => ({
+          id: item['编码'],
+          label: `${item['物品名称']} - ${item['编码']}`,
+          value: item['编码'],
+          originalData: item  // 保存原始数据以便后续使用
+        }))
+      }))
+      
+      treeData.value = transformedTreeData
+      
+      // 展平的资产列表用于其他操作
+      const flatAssets = Object.values(groupedData).flat()
+      selectedAssets.value = flatAssets
+      
+      console.log('分组后的树形数据:', transformedTreeData)
+      console.log('展平的资产列表:', flatAssets)
+      
+      // 生成第一个资产的条码
+      if (flatAssets.length > 0) {
+        nextTick(() => {
+          generateBarcode(flatAssets[0]['编码'])
+        })
+      }
+    } else {
+      console.error('获取打印数据字段失败:', response.msg)
+      // 可以添加错误提示
+      ElMessage.error(response.msg || '获取数据失败')
+    }
+  } catch (error) {
+    console.error('获取数据失败:', error)
+    ElMessage.error('网络请求失败,请稍后重试')
+  }
+}
+
+// 生成条码(调整高度匹配示例图)
+const generateBarcode = (code) => {
+  if (barcodeCanvas.value) {
+    JsBarcode(barcodeCanvas.value, code, {
+      format: 'CODE128',
+      width: 2,
+      height: 300, // 调整高度匹配示例图
+      displayValue: false,
+      margin: 5 // 缩小边距
+    })
+  }
+}
+
+// 处理树状图节点点击
+const handleNodeClick = (data) => {
+  // 检查是否是叶子节点(有originalData)
+  if (data.originalData) {
+    // 只显示当前选中的资产
+    selectedAssets.value = [data.originalData]
+    selectedAsset.value = data.originalData
+    
+    // 生成条码
+    nextTick(() => {
+      generateBarcode(data.originalData['编码'])
+    })
+  }
+}
+
+// 处理树节点勾选
+const handleCheckChange = (data, checked, indeterminate) => {
+  // 递归处理子节点
+  const processChildren = (node, isChecked) => {
+    if (node.children && node.children.length > 0) {
+      node.children.forEach(child => {
+        if (child.originalData) {
+          if (isChecked) {
+            // 添加到勾选列表
+            if (!checkedAssets.value.some(item => item['编码'] === child.originalData['编码'])) {
+              checkedAssets.value.push(child.originalData)
+            }
+          } else {
+            // 从勾选列表移除
+            checkedAssets.value = checkedAssets.value.filter(item => item['编码'] !== child.originalData['编码'])
+          }
+        }
+        // 递归处理子节点
+        processChildren(child, isChecked)
+      })
+    }
+  }
+  
+  // 处理当前节点
+  if (data.originalData) {
+    if (checked) {
+      // 添加到勾选列表
+      if (!checkedAssets.value.some(item => item['编码'] === data.originalData['编码'])) {
+        checkedAssets.value.push(data.originalData)
+      }
+    } else {
+      // 从勾选列表移除
+      checkedAssets.value = checkedAssets.value.filter(item => item['编码'] !== data.originalData['编码'])
+    }
+  } else {
+    // 处理一级节点
+    processChildren(data, checked)
+  }
+  
+  console.log('勾选的资产:', checkedAssets.value)
+}
+
+// 打印所有标签
+const printAllLabels = async () => {
+  // 使用勾选的数据,如果没有勾选则使用所有数据
+  const assetsToPrint = checkedAssets.value.length > 0 ? checkedAssets.value : selectedAssets.value;
+  
+  if (assetsToPrint.length === 0) {
+    console.error('没有可打印的标签数据')
+    return
+  }
+  
+  const originalBodyStyle = document.body.style.cssText;
+  const originalBodyClass = document.body.className;
+  const originalHtmlStyle = document.documentElement.style.cssText;
+  
+  try {
+    // 创建打印结构
+    const iframe = document.createElement('iframe');
+    iframe.style.position = 'fixed';
+    iframe.style.right = '0';
+    iframe.style.bottom = '0';
+    iframe.style.width = '0';
+    iframe.style.height = '0';
+    iframe.style.border = 'none';
+    iframe.style.visibility = 'hidden';
+    document.body.appendChild(iframe);
+    
+    await new Promise(resolve => {
+      iframe.onload = resolve;
+      iframe.src = 'about:blank';
+    });
+    
+    const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
+    iframeDoc.open();
+    
+    // 构建打印内容
+    let labelsHtml = '';
+    assetsToPrint.forEach((asset, index) => {
+      labelsHtml += `
+        <div class="label-print">
+          <div class="label-header-print">
+            <h3>河南金芒果印刷有限公司低值易耗品</h3>
+          </div>
+          <div class="label-content-print">
+            <div class="label-field-print">
+              <span class="field-label-print">资产名称:</span>
+              <span class="field-value-print">${asset['物品名称']}</span>
+            </div>
+            <div class="label-field-print">
+              <span class="field-label-print">使用部门:</span>
+              <span class="field-value-print">${asset['使用部门']}</span>
+            </div>
+            <div class="label-field-print">
+              <span class="field-label-print">存放地点:</span>
+              <span class="field-value-print">${asset['存放地点']}</span>
+            </div>
+            <div class="label-barcode-section-print">
+              <div class="label-barcode-print">
+                <canvas id="barcode-${index}" class="barcode-canvas-print"></canvas>
+                <div class="code-text-print">${asset['编码']}</div>
+              </div>
+              <div class="label-logo-print">
+                <img src="http://20.0.16.87:9094/uploads/certificate/中国烟草.png" alt="中国烟草" class="post-logo-print">
+              </div>
+            </div>
+          </div>
+        </div>
+      `;
+    });
+    
+    // 构建完整的HTML内容 —— 核心修复:</style> 改为 '<\/style>'
+    const htmlContent = `<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="UTF-8">
+  <title>资产标签打印</title>
+  <script src="/luckysheet/JsBarcode.all.min.js"><\/script>
+  <style>
+    @page {
+      size: 100mm 60mm;
+      margin: 0;
+    }
+    body {
+      margin: 0;
+      padding: 0;
+      background: #fff;
+      -webkit-print-color-adjust: exact;
+      print-color-adjust: exact;
+      overflow: hidden;
+      font-family: 'Source Han Sans CN', Arial, sans-serif;
+    }
+    
+    .label-print {
+      width: 100mm;
+      height: 60mm;
+      position: relative;
+      margin: 0 auto;
+      background: #fff;
+      padding: 3mm;
+      box-sizing: border-box;
+      page-break-inside: avoid;
+      display: flex;
+      flex-direction: column;
+    }
+    
+    .label-header-print {
+      text-align: left;
+      margin-bottom: 2mm;
+    }
+    
+    .label-header-print h3 {
+      margin: 0;
+      font-size: 11pt;
+      font-weight: bold;
+      letter-spacing: 1px;
+    }
+    
+    .label-content-print {
+      font-size: 10pt;
+      flex: 1;
+      display: flex;
+      flex-direction: column;
+    }
+    
+    .label-field-print {
+      margin-bottom: 1mm;
+      line-height: 1.4;
+      display: flex;
+      align-items: center;
+    }
+    
+    .field-label-print {
+      font-weight: normal;
+      font-size: 11pt;
+      width: 80px;
+      flex-shrink: 0;
+    }
+    
+    .field-value-print {
+      font-size: 11pt;
+      flex: 1;
+    }
+    
+    .label-barcode-section-print {
+      display: flex;
+      align-items: flex-end;
+      margin-top: 2mm;
+      gap: 3mm;
+      position: relative;
+      padding-bottom: 1mm;
+    }
+    
+    .label-barcode-print {
+      flex: 1;
+      display: flex;
+      flex-direction: column;
+      position: relative;
+    }
+    
+    .barcode-canvas-print {
+      width: 100%;
+      height: 55px;
+    }
+    
+    .code-text-print {
+      font-size: 10pt;
+      text-align: left;
+      margin-top: 0.5mm;
+      font-family: 'Courier New', monospace;
+      letter-spacing: 1px;
+      position: absolute;
+      bottom: -5mm;
+    }
+    
+    .label-logo-print {
+      flex-shrink: 0;
+      display: flex;
+      align-items: center;
+      height: 55px;
+    }
+    
+    .post-logo-print {
+      width: 20mm;
+      height: auto;
+    }
+    
+    * {
+      box-sizing: border-box;
+    }
+  <\/style>
+</head>
+<body>
+  ${labelsHtml}
+  <script>
+    // 生成条码
+    const assets = ${JSON.stringify(assetsToPrint)};
+    assets.forEach((asset, index) => {
+      const canvas = document.getElementById('barcode-' + index);
+      if (canvas) {
+        JsBarcode(canvas, asset['编码'], {
+          format: 'CODE128',
+          width: 2,
+          height: 45,
+          displayValue: false,
+          margin: 5
+        });
+      }
+    });
+  <\/script>
+</body>
+</html>`;
+    
+    // 写入iframe
+    iframeDoc.write(htmlContent);
+    iframeDoc.close();
+    
+    // 等待iframe渲染完成后执行打印
+    await new Promise(resolve => setTimeout(resolve, 500));
+    
+    // 打印调试信息
+    console.log('打印标签数量:', assetsToPrint.length);
+    
+    iframe.contentWindow.print();
+    
+    // 打印后移除iframe
+    setTimeout(() => {
+      document.body.removeChild(iframe);
+      // 恢复原页面样式
+      document.body.style.cssText = originalBodyStyle;
+      document.body.className = originalBodyClass;
+      document.documentElement.style.cssText = originalHtmlStyle;
+    }, 1000);
+    
+  } catch (error) {
+    console.error('打印失败:', error);
+    // 异常时恢复样式
+    document.body.style.cssText = originalBodyStyle;
+    document.body.className = originalBodyClass;
+    document.documentElement.style.cssText = originalHtmlStyle;
+  }
+};
+
+// 生命周期
+onMounted(() => {
+  getPrintDataFields()
+})
+</script>
+
+<style scoped>
+
+/* 基本样式 */
+
+.certificate-content {
+  padding: 20px;
+  background-color: #f9f9f9;
+  min-height: calc(100vh - 100px);
+  font-family: 'Source Han Sans CN', Arial, sans-serif;
+}
+
+/* 右侧内容区域 */
+.layout-content {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  min-height: 600px;
+  background-color: #f9f9f9;
+}
+
+/* 顶部操作栏 */
+.top-action-bar {
+  padding: 10px 20px;
+  background: #fff;
+  border-bottom: 1px solid #e8e8e8;
+}
+
+.action-buttons {
+  display: flex;
+  gap: 10px;
+  flex-wrap: wrap;
+}
+
+/* 树状图区域 */
+.tree-section {
+  height: 100%;
+  padding: 10px;
+  background: #fff;
+  border-right: 1px solid #e8e8e8;
+}
+
+.tree-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 15px;
+  padding-bottom: 10px;
+  border-bottom: 1px solid #e4e7ed;
+}
+
+.tree-header h3 {
+  margin: 0;
+  font-size: 16px;
+  color: #303133;
+}
+
+.tree-wrapper {
+  flex: 1;
+  overflow-y: auto;
+  max-height: calc(100vh - 400px);
+}
+
+.tree-node {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  width: 100%;
+}
+
+.tree-code {
+  font-size: 12px;
+  color: #909399;
+  margin-left: 10px;
+}
+
+.selected-info {
+  margin-top: 15px;
+  padding: 10px;
+  background: #f5f7fa;
+  border-radius: 4px;
+  text-align: center;
+  font-size: 14px;
+  color: #606266;
+}
+
+/* 标签容器 */
+.label-wrapper {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 20px;
+  justify-content: center;
+  align-items: center;
+  padding: 20px;
+  width: 100%;
+  min-height: 600px;
+}
+
+/* 资产标签样式(完全对齐示例图) */
+.asset-label {
+  width: 100mm;
+  height: 60mm;
+  background: #fff;
+  border: 1px solid #000;
+  padding: 5mm;
+  box-sizing: border-box;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+}
+
+.label-header {
+  text-align: center;
+  margin-bottom: 3mm;
+}
+
+.label-header h3 {
+  margin: 0;
+  font-size: 10pt;
+  font-weight: bold;
+}
+
+.label-content {
+  font-size: 8pt;
+}
+
+.label-field {
+  margin-bottom: 2mm;
+  line-height: 1.2;
+}
+
+.field-label {
+  font-weight: bold;
+  font-size: 10pt;
+}
+
+.field-value {
+  margin-left: 5px;
+  font-size: 10pt;
+}
+
+.label-barcode-section {
+  display: flex;
+  align-items: flex-end;
+  margin-top: 2mm;
+  gap: 3mm;
+  position: relative;
+  padding-bottom: 1mm;
+}
+
+.label-barcode {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  position: relative;
+}
+
+.barcode {
+  width: 100%;
+  height: 55px;
+}
+
+.code-text {
+  font-size: 10pt;
+  text-align: left;
+  margin-top: 0.5mm;
+  font-family: 'Courier New', monospace;
+  letter-spacing: 1px;
+  position: absolute;
+  bottom: -5mm;
+}
+
+.label-logo {
+  flex-shrink: 0;
+  display: flex;
+  align-items: center;
+  height: 55px;
+}
+
+.post-logo {
+  width: 20mm;
+  height: auto;
+}
+
+/* 响应式设计 */
+@media (max-width: 1200px) {
+  .label-wrapper {
+    padding: 10px;
+  }
+  
+  .asset-label {
+    width: 95mm;
+    height: 55mm;
+  }
+}
+
+@media (max-width: 768px) {
+  .label-wrapper {
+    flex-direction: column;
+    align-items: center;
+  }
+  
+  .asset-label {
+    width: 90mm;
+    height: 50mm;
+  }
+}
+
+/* 打印样式 */
+@media print {
+  .no-print {
+    display: none !important;
+  }
+  
+  body {
+    margin: 0;
+    padding: 0;
+  }
+  
+  .label-wrapper {
+    display: block;
+    padding: 0;
+  }
+  
+  .asset-label {
+    page-break-inside: avoid;
+    margin: 0;
+    box-shadow: none;
+  }
+}
+
+/* 暂无数据样式 */
+.no-data {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  width: 100mm;
+  height: 60mm;
+  background: #fff;
+  border: 1px solid #e8e8e8;
+  color: #909399;
+  font-size: 14px;
+}
+</style>

+ 6 - 6
src/view/zs/laowozhengshudayin.vue

@@ -1071,7 +1071,7 @@ const exportPDF = async () => {
     
     /* 开始日期 */
     .startdate {
-      top: 560px;
+      top: 565px;
       left: 580px;
       font-size: 13px;
       font-weight: 400;
@@ -1079,7 +1079,7 @@ const exportPDF = async () => {
     
     /* 结束日期 */
     .enddate {
-      top: 560px;
+      top: 565px;
       left: 730px;
       font-size: 13px;
       font-weight: 400;
@@ -1087,7 +1087,7 @@ const exportPDF = async () => {
     
     /* 下方开始日期 */
     .startdate-bottom {
-      top: 580px;
+      top: 585px;
       left: 450px;
       font-size: 13px;
       font-weight: 400;
@@ -1095,7 +1095,7 @@ const exportPDF = async () => {
     
     /* 下方结束日期 */
     .enddate-bottom {
-      top: 580px;
+      top: 585px;
       left: 690px;
       font-size: 13px;
       font-weight: 400;
@@ -1427,13 +1427,13 @@ watch(() => editableData.value.enddate, () => {
 
 /* 下方横线上的日期字段 */
 .startdate-bottom-container {
-  top: 570px;
+  top: 575px;
   left: 425px;
   width: 100px;
 }
 
 .enddate-bottom-container {
-  top: 570px;
+  top: 575px;
   left: 670px;
   width: 100px;
 }

+ 835 - 0
src/view/zs/zhengshudayinstudent.vue

@@ -0,0 +1,835 @@
+<template>
+  <div>
+    <layout>
+      <layout-header>
+        <div class="top-action-bar no-print">
+          <el-form class="demo-form-inline">
+            <el-form-item>
+              <div class="action-buttons">
+                <!-- 打印按钮 -->
+                <el-button type="primary" @click="printCertificate" style="margin: 5px">
+                  <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16" style="margin-right: 5px">
+                    <path d="M5 1a2 2 0 0 0-2 2v1h10V3a2 2 0 0 0-2-2H5zm6 8H5a1 1 0 0 0-1 1v3a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1v-3a1 1 0 0 0-1-1z"/>
+                    <path d="M0 7a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2h-1v-2a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v2H2a2 2 0 0 1-2-2V7zm2.5 1a.5.5 0 1 0 0-1 .5.5 0 0 0 0 1z"/>
+                  </svg>
+                  打印证书
+                </el-button>
+                
+                <!-- 编辑按钮 -->
+                <el-button type="primary" @click="toggleEditMode" style="margin: 5px">
+                  <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16" style="margin-right: 5px">
+                    <path d="M12.854.146a.5.5 0 0 0-.707 0L10.5 1.793 14.207 5.5l1.647-1.646a.5.5 0 0 0 0-.708l-3-3zm.646 6.061L9.793 2.5 3.293 9H3.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.207l6.5-6.5zm-7.468 7.468A.5.5 0 0 1 6 13.5V13h-.5a.5.5 0 0 1-.5-.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.5-.5V10h-.5a.499.499 0 0 1-.175-.032l-.179.178a.5.5 0 0 0-.11.168l-2 5a.5.5 0 0 0 .65.65l5-2a.5.5 0 0 0 .168-.11l.178-.178z"/>
+                  </svg>
+                  {{ isEditing ? '完成编辑' : '编辑证书' }}
+                </el-button>
+              </div>
+            </el-form-item>
+          </el-form>
+        </div>
+      </layout-header>
+
+      <layout>
+        <!-- 左侧树状图区域 -->
+        <layout-sider :resize-directions="['right']" :width="300" v-if="showTree">
+          <div class="tree-section">
+            <div class="tree-header">
+              <h3>学生选择</h3>
+            </div>
+            <div class="tree-wrapper">
+              <el-tree
+                ref="treeRef"
+                :data="treeData"
+                node-key="id"
+                :props="defaultProps"
+                @node-click="handleNodeClick"
+                highlight-current
+                :default-expand-all="true"
+              >
+                <template #default="{ node, data }">
+                  <span class="tree-node">
+                    <span>{{ data.name }}</span>
+                    <span v-if="data.college" class="tree-college">{{ data.college }}</span>
+                  </span>
+                </template>
+              </el-tree>
+            </div>
+            <div class="selected-info" v-if="selectedStudent">
+              已选择:{{ selectedStudent.name }} ({{ selectedStudent.college }})
+            </div>
+          </div>
+        </layout-sider>
+
+        <!-- 右侧证书区域 -->
+        <layout-content>
+          <div class="certificate-content">
+            <div class="cert-wrapper" ref="certificateWrapper">
+              <!-- 学生证书样式 -->
+              <div class="student-certificate">
+                <!-- 证书背景图片 -->
+                <div class="certificate-background">
+                  <img src="http://20.0.16.87:9094/uploads/certificate/student.png" alt="学生证书背景" class="bg-image">
+                </div>
+                
+                <!-- 可编辑字段 -->
+                <div class="certificate-fields">
+                  <!-- Name -->
+                  <div class="field-container name-container">
+                    <span 
+                      v-if="!isEditing || editingField !== 'name'"
+                      class="field name-field"
+                      @click="startEditing('name')"
+                      :style="{ width: nameWidth }"
+                      ref="nameRef"
+                    >
+                      {{ editableData.name }}
+                    </span>
+                    <input 
+                      v-else
+                      v-model="editableData.name"
+                      class="field-input name-input"
+                      type="text"
+                      @blur="blurField"
+                      @keyup.enter="blurField"
+                      ref="nameInput"
+                      :style="{ width: nameInputWidth }"
+                    />
+                  </div>
+                  
+                  <!-- College -->
+                  <div class="field-container college-container">
+                    <span 
+                      v-if="!isEditing || editingField !== 'college'"
+                      class="field college-field"
+                      @click="startEditing('college')"
+                      :style="{ width: collegeWidth }"
+                      ref="collegeRef"
+                    >
+                      {{ editableData.college }}
+                    </span>
+                    <input 
+                      v-else
+                      v-model="editableData.college"
+                      class="field-input college-input"
+                      type="text"
+                      @blur="blurField"
+                      @keyup.enter="blurField"
+                      ref="collegeInput"
+                      :style="{ width: collegeInputWidth }"
+                    />
+                  </div>
+                  
+                  <!-- Department -->
+                  <div class="field-container department-container">
+                    <span 
+                      v-if="!isEditing || editingField !== 'department'"
+                      class="field department-field"
+                      @click="startEditing('department')"
+                      :style="{ width: departmentWidth }"
+                      ref="departmentRef"
+                    >
+                      {{ editableData.department }}
+                    </span>
+                    <input 
+                      v-else
+                      v-model="editableData.department"
+                      class="field-input department-input"
+                      type="text"
+                      @blur="blurField"
+                      @keyup.enter="blurField"
+                      ref="departmentInput"
+                      :style="{ width: departmentInputWidth }"
+                    />
+                  </div>
+                </div>
+              </div>
+            </div>
+          </div>
+        </layout-content>
+      </layout>
+    </layout>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, computed, onMounted, onBeforeUnmount, watch, nextTick } from 'vue'
+import { Layout, LayoutSider, LayoutContent, LayoutHeader } from '@arco-design/web-vue'
+import { ElTree, ElButton, ElForm, ElFormItem } from 'element-plus'
+import { Get_studentPrintFields } from '@/api/mes/job'
+
+// 树状图相关
+const showTree = ref(true)
+const treeRef = ref(null)
+const treeData = ref([])
+const selectedStudent = ref(null) // 存储单个选中的节点
+const defaultProps = {
+  label: 'name'
+}
+
+
+// 证书数据
+const props = defineProps({
+  certificateData: {
+    type: Object,
+    default: () => ({
+      name: '陈冰婕',
+      college: '艺术设计学院',
+      department: '视传2401'
+    })
+  }
+})
+
+// 响应式数据
+const certificateWrapper = ref(null)
+const isEditing = ref(false)
+const editingField = ref(null)
+
+// 宽度相关
+const nameWidth = ref('200px')
+const collegeWidth = ref('200px')
+const departmentWidth = ref('200px')
+const nameInputWidth = ref('200px')
+const collegeInputWidth = ref('200px')
+const departmentInputWidth = ref('200px')
+
+// 可编辑数据
+const editableData = ref({
+  name: props.certificateData.name,
+  college: props.certificateData.college,
+  department: props.certificateData.department
+})
+
+// 添加监听器,确保当 props.certificateData 变化时更新显示
+watch(() => props.certificateData, (newData) => {
+  console.log('🔔 父组件数据更新:', newData)
+  // 更新本地响应式数据
+  Object.assign(editableData.value, newData)
+  // 强制更新一次字段宽度
+  nextTick(() => {
+    adjustFieldWidths()
+  })
+}, { deep: true })
+
+// 获取打印数据字段
+const getPrintDataFields = async () => {
+  try {
+    const response = await Get_studentPrintFields({})
+    if (response.code === 0) {
+      // 转换接口数据为树状图数据,处理字段名和ID
+      treeData.value = response.data.map(item => ({
+        id: item.$treeNodeId,
+        name: item.Name,
+        college: item.College,
+        department: item.Department
+      }))
+      console.log('转换后的数据:', treeData.value)
+    } else {
+      console.error('获取打印数据字段失败:', response.msg)
+    }
+  } catch (error) {
+    console.error('获取数据失败:', error)
+  }
+}
+
+// 处理树状图节点点击 - 单选逻辑
+const handleNodeClick = (nodeData) => {
+  selectedStudent.value = nodeData
+  console.log('✅ 选中节点:', nodeData)
+  updateCertificateFromStudent(nodeData)
+}
+
+// 更新证书数据方法
+const updateCertificateFromStudent = (studentData) => {
+  console.log('从选中学生更新证书数据:', studentData)
+  
+  // 创建一个完整的更新对象,包含所有字段
+  const updatedData = {
+    name: studentData.name || '陈冰婕',
+    college: studentData.college || '艺术设计学院',
+    department: studentData.department || '视传2401'
+  }
+  
+  console.log('📤 要更新的数据:', updatedData)
+  
+  // 直接更新本地响应式数据
+  Object.assign(editableData.value, updatedData)
+  
+  // 发送更新到父组件
+  emit('update:certificate-data', updatedData)
+  
+  // 强制重新计算并显示
+  nextTick(() => {
+    console.log('当前显示的数据:', {
+      姓名: editableData.value.name,
+      学院: editableData.value.college,
+      系部: editableData.value.department
+    })
+    adjustFieldWidths()
+  })
+}
+
+// 调整字段宽度
+const adjustFieldWidths = () => {
+  const adjustField = (fieldRef, minWidth = 100) => {
+    const element = certificateWrapper.value?.querySelector(`[ref="${fieldRef}"]`);
+    if (element) {
+      const textLength = element.textContent?.length || 0;
+      const newWidth = Math.max(minWidth, textLength * 12);
+      return newWidth + 'px';
+    }
+    return minWidth + 'px';
+  }
+  
+  nameWidth.value = adjustField('nameRef', 200);
+  collegeWidth.value = adjustField('collegeRef', 200);
+  departmentWidth.value = adjustField('departmentRef', 200);
+}
+
+const adjustInputWidth = (fieldName) => {
+  const minWidthMap = {
+    name: 200,
+    college: 200,
+    department: 200
+  }
+  
+  const widthPropMap = {
+    name: 'nameInputWidth',
+    college: 'collegeInputWidth',
+    department: 'departmentInputWidth'
+  }
+  
+  const value = editableData.value[fieldName]
+  const minWidth = minWidthMap[fieldName] || 100
+  const textLength = value ? value.length : 0
+  const newWidth = Math.max(minWidth, textLength * 12 + 20)
+  
+  const propName = widthPropMap[fieldName]
+  if (propName) {
+    if (fieldName === 'name') nameInputWidth.value = newWidth + 'px'
+    else if (fieldName === 'college') collegeInputWidth.value = newWidth + 'px'
+    else if (fieldName === 'department') departmentInputWidth.value = newWidth + 'px'
+  }
+}
+
+const toggleEditMode = () => {
+  if (isEditing.value) {
+    saveAllChanges();
+    isEditing.value = false;
+    editingField.value = null;
+    
+    nextTick(() => {
+      adjustFieldWidths();
+    });
+  } else {
+    isEditing.value = true;
+    editingField.value = null;
+  }
+}
+
+const startEditing = (fieldName) => {
+  if (!isEditing.value) {
+    isEditing.value = true;
+  }
+  editingField.value = fieldName;
+  
+  nextTick(() => {
+    const inputRefMap = {
+      name: 'nameInput',
+      college: 'collegeInput',
+      department: 'departmentInput'
+    };
+    
+    const refName = inputRefMap[fieldName];
+    if (refName) {
+      const element = certificateWrapper.value?.querySelector(`[ref="${refName}"]`);
+      if (element) {
+        element.focus();
+        element.select();
+      }
+    }
+  });
+}
+
+const blurField = () => {
+  editingField.value = null
+}
+
+const saveAllChanges = () => {
+  emit('update:certificate-data', {
+    name: editableData.value.name,
+    college: editableData.value.college,
+    department: editableData.value.department
+  });
+  
+  emit('certificate-updated', {
+    data: { ...editableData.value }
+  });
+}
+
+// 打印证书方法
+const printCertificate = async () => {
+  // 如果正在编辑,先保存再打印
+  if (isEditing.value) {
+    await saveAllChanges();
+    isEditing.value = false;
+    editingField.value = null;
+    await nextTick();
+  }
+  
+  const originalBodyStyle = document.body.style.cssText;
+  const originalBodyClass = document.body.className;
+  const originalHtmlStyle = document.documentElement.style.cssText;
+  
+  try {
+    // 创建打印结构
+    const iframe = document.createElement('iframe');
+    iframe.style.position = 'fixed';
+    iframe.style.right = '0';
+    iframe.style.bottom = '0';
+    iframe.style.width = '0';
+    iframe.style.height = '0';
+    iframe.style.border = 'none';
+    iframe.style.visibility = 'hidden';
+    document.body.appendChild(iframe);
+    
+    await new Promise(resolve => {
+      iframe.onload = resolve;
+      iframe.src = 'about:blank';
+    });
+    
+    const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
+    iframeDoc.open();
+    
+    // 获取当前数据
+    const currentData = editableData.value;
+    
+    // 学生证书打印配置
+    const printConfig = {
+      fields: {
+        name: {
+          top: 315,
+          left: 340,
+          width: 200
+        },
+        college: {
+          top: 400,
+          left: 150,
+          width: 200
+        },
+        department: {
+          top: 400,
+          left: 520,
+          width: 200
+        }
+      }
+    };
+    
+    iframeDoc.write(`
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="UTF-8">
+  <title>学生证书打印</title>
+  <style>
+    @page {
+      size: A4 landscape;
+      margin: 0;
+    }
+    body {
+      margin: 0;
+      padding: 0;
+      background: #fff;
+      -webkit-print-color-adjust: exact;
+      print-color-adjust: exact;
+      overflow: hidden;
+      font-family: 'Source Han Sans CN', Arial, sans-serif;
+    }
+    
+    .cert-print-container {
+      width: 297mm;
+      height: 210mm;
+      position: relative;
+      margin: 0 auto;
+      background: #fff;
+    }
+    
+    .student-cert-print {
+      width: 100%;
+      height: 100%;
+      position: relative;
+      padding: 40px;
+      box-sizing: border-box;
+    }
+    
+    .field-section-print {
+      position: absolute;
+      text-align: center;
+    }
+    
+    .field-value-print {
+      font-size: 18px;
+      font-weight: normal;
+    }
+    
+    .name-field-print {
+      top: ${printConfig.fields.name.top}px;
+      left: ${printConfig.fields.name.left}px;
+      width: ${printConfig.fields.name.width}px;
+    }
+    
+    .name-field-print .field-value-print {
+      font-size: 30px;
+      font-weight: 300;
+      letter-spacing: 12px;
+      white-space: nowrap;
+    }
+    
+    .college-field-print {
+      top: ${printConfig.fields.college.top}px;
+      left: ${printConfig.fields.college.left}px;
+      width: ${printConfig.fields.college.width}px;
+    }
+    
+    .department-field-print {
+      top: ${printConfig.fields.department.top}px;
+      left: ${printConfig.fields.department.left}px;
+      width: ${printConfig.fields.department.width}px;
+    }
+    
+    * {
+      box-sizing: border-box;
+    }
+  </style>
+</head>
+<body>
+  <div class="cert-print-container">
+    <div class="student-cert-print">
+      <!-- 只打印需要的字段,不包含背景和头部 -->
+      
+      <!-- 姓名 -->
+      <div class="field-section-print name-field-print">
+        <div class="field-value-print">${currentData.name}</div>
+      </div>
+      
+      <!-- 学院 -->
+      <div class="field-section-print college-field-print">
+        <div class="field-value-print">${currentData.college}</div>
+      </div>
+      
+      <!-- 系部 -->
+      <div class="field-section-print department-field-print">
+        <div class="field-value-print">${currentData.department}</div>
+      </div>
+    </div>
+  </div>
+</body>
+</html>
+`);
+    iframeDoc.close();
+    
+    // 等待iframe渲染完成后执行打印
+    await new Promise(resolve => setTimeout(resolve, 500));
+    
+    // 打印调试信息
+    console.log('打印字段位置信息:', {
+      姓名: currentData.name,
+      学院: currentData.college,
+      系部: currentData.department
+    });
+    
+    iframe.contentWindow.print();
+    
+    // 打印后移除iframe
+    setTimeout(() => {
+      document.body.removeChild(iframe);
+      // 恢复原页面样式
+      document.body.style.cssText = originalBodyStyle;
+      document.body.className = originalBodyClass;
+      document.documentElement.style.cssText = originalHtmlStyle;
+    }, 1000);
+    
+  } catch (error) {
+    console.error('打印失败:', error);
+    // 异常时恢复样式
+    document.body.style.cssText = originalBodyStyle;
+    document.body.className = originalBodyClass;
+    document.documentElement.style.cssText = originalHtmlStyle;
+  }
+};
+
+
+
+// 定义emit事件
+const emit = defineEmits(['update:certificate-data', 'certificate-updated'])
+
+// 生命周期
+onMounted(() => {
+  adjustFieldWidths()
+  window.addEventListener('resize', adjustFieldWidths)
+  getPrintDataFields()
+})
+
+onBeforeUnmount(() => {
+  window.removeEventListener('resize', adjustFieldWidths)
+})
+
+// 监听器
+watch(() => editableData.value.name, () => {
+  nextTick(() => {
+    adjustFieldWidths()
+    adjustInputWidth('name')
+  })
+})
+
+watch(() => editableData.value.college, () => {
+  nextTick(() => {
+    adjustFieldWidths()
+    adjustInputWidth('college')
+  })
+})
+
+watch(() => editableData.value.department, () => {
+  nextTick(() => {
+    adjustFieldWidths()
+    adjustInputWidth('department')
+  })
+})
+</script>
+
+<style scoped>
+/* 基本样式 */
+.certificate-content {
+  padding: 0;
+  background-color: #f9f9f9;
+  min-height: calc(100vh - 100px);
+  font-family: "SimSun", "思源黑体", "STSong", serif;
+  font-size: 15pt;
+  font-weight: normal;
+}
+
+/* 顶部操作栏 */
+.top-action-bar {
+  padding: 10px 20px;
+  background: #fff;
+  border-bottom: 1px solid #e8e8e8;
+}
+
+.action-buttons {
+  display: flex;
+  gap: 10px;
+  flex-wrap: wrap;
+}
+
+/* 树状图区域 */
+.tree-section {
+  height: 100%;
+  padding: 10px;
+  background: #fff;
+  border-right: 1px solid #e8e8e8;
+}
+
+.tree-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 15px;
+  padding-bottom: 10px;
+  border-bottom: 1px solid #e4e7ed;
+}
+
+.tree-header h3 {
+  margin: 0;
+  font-size: 16px;
+  color: #303133;
+}
+
+.tree-wrapper {
+  flex: 1;
+  overflow-y: auto;
+  max-height: calc(100vh - 200px);
+}
+
+.tree-node {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  width: 100%;
+}
+
+.tree-college {
+  font-size: 12px;
+  color: #909399;
+  margin-left: 10px;
+}
+
+.selected-info {
+  margin-top: 15px;
+  padding: 10px;
+  background: #f5f7fa;
+  border-radius: 4px;
+  text-align: center;
+  font-size: 14px;
+  color: #606266;
+}
+
+/* 证书容器 */
+.certificate-content {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  padding: 0px;
+}
+
+.cert-wrapper {
+  width: 297mm;
+  height: 210mm;
+  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);
+  background: #fff;
+  margin: 0 auto;
+  position: relative;
+  padding: 0;
+  overflow: hidden;
+}
+
+/* 学生证书样式 */
+.student-certificate {
+  width: 100%;
+  height: 100%;
+  position: relative;
+  padding: 0;
+}
+
+.certificate-background {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  z-index: 1;
+}
+
+.bg-image {
+  width: 100%;
+  height: 100%;
+  object-fit: cover;
+}
+
+.certificate-fields {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  z-index: 2;
+  padding: 60px;
+  box-sizing: border-box;
+}
+
+.field-container {
+  position: absolute;
+  text-align: center;
+}
+
+/* 字段定位 - 适应横向证书 */
+.name-container {
+  top: 370px;
+  left: 350px;
+  width: 200px;
+}
+
+.college-container {
+  top: 440px;
+  left: 160px;
+  width: 200px;
+}
+
+.department-container {
+  top: 440px;
+  left: 530px;
+  width: 200px;
+}
+
+/* 字段样式 */
+.field {
+  display: inline-block;
+  border: 2px solid transparent;
+  text-align: center;
+  box-sizing: border-box;
+  vertical-align: middle;
+  position: relative;
+  min-height: 30px;
+  line-height: 30px;
+  padding: 0 10px;
+  font-size: 20px;
+  font-weight: 300;
+  cursor: pointer;
+  transition: all 0.3s ease;
+}
+
+.name-field {
+  font-size: 30px;
+  font-weight: 300;
+  letter-spacing: 12px;
+  white-space: nowrap;
+}
+
+.field:hover {
+  background: rgba(0, 102, 204, 0.1);
+  border-bottom-color: #0066cc;
+}
+
+.field-input {
+  display: inline-block;
+  border: 2px solid #0066cc;
+  background: rgba(255, 255, 255, 0.9);
+  text-align: center;
+  box-sizing: border-box;
+  vertical-align: middle;
+  position: relative;
+  min-height: 30px;
+  line-height: 30px;
+  padding: 0 10px;
+  font-size: 16px;
+  font-weight: bold;
+  outline: none;
+  border-radius: 4px;
+  transition: all 0.3s ease;
+}
+
+.field-input:focus {
+  background: rgba(0, 102, 204, 0.1);
+  box-shadow: 0 0 0 2px rgba(0, 102, 204, 0.2);
+}
+
+/* 响应式设计 */
+@media (max-width: 1200px) {
+  .cert-wrapper {
+    width: 95%;
+    padding: 30px;
+  }
+  
+  .student-certificate {
+    padding: 40px;
+  }
+  
+  .field, .field-input {
+    font-size: 14px;
+  }
+}
+
+@media (max-width: 768px) {
+  .cert-wrapper {
+    width: 100%;
+    padding: 20px;
+  }
+  
+  .student-certificate {
+    padding: 30px;
+    background-image: none;
+    border: 1px solid #e8e8e8;
+  }
+  
+  .field, .field-input {
+    font-size: 12px;
+  }
+}
+</style>