zck 1 개월 전
부모
커밋
c92c3bb4a4

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

@@ -1720,7 +1720,65 @@ export const checkProcessProduction = (params) => {
   })
 }
 
+//完工工单列表
+export const getCompletedWorkOrderList = (params) => {
+  return service({
+    url: '/mes_server/synchronization/getCompletedWorkOrderList',
+    method: 'get',
+    params,
+    donNotShowError: true,
+  })
+}
+
+//数据同步
+export const syncWorkOrder = (data) => {
+  return service({
+    url: '/mes_server/synchronization/syncWorkOrder',
+    method: 'post',
+    data,
+    donNotShowError: true,
+  })
+}
 
+//重试同步
+export const retrySyncWorkOrder = (data) => {
+  return service({
+    url: '/mes_server/synchronization/retrySyncWorkOrder',
+    method: 'post',
+    data,
+    donNotShowError: true,
+  })
+}
+
+//撤回同步
+export const cancelSyncWorkOrder = (data) => {
+  return service({
+    url: '/mes_server/synchronization/cancelSyncWorkOrder',
+    method: 'post',
+    data,
+    donNotShowError: true,
+  })
+}
+
+//同步日志列表
+export const getSyncLogList = (params) => {
+  return service({
+    url: '/mes_server/synchronization/getSyncLogList',
+    method: 'get',
+    params,
+    donNotShowError: true,
+  })
+}
+
+//操作过程列表
+export const getOperationLogList = (params) => {
+  return service({
+    url: '/mes_server/synchronization/getOperationLogList',
+    method: 'get',
+    params,
+    donNotShowError: true,
+  })
+}
 
 /*设备工分报工产量跟踪*/
 
@@ -1788,3 +1846,59 @@ export const GetStaffSalaryDetail = (params) => {
   })
 }
 
+
+/*月度样衣进度*/
+//左侧菜单
+export const getDateTree = (params) => {
+  return service({
+    url: '/mes_server/monthly_garments/getDateTree',
+    method: 'get',
+    params
+  })
+}
+
+//列表查询
+export const getMonthlyGarmentsList = (params) => {
+  return service({
+    url: '/mes_server/monthly_garments/getMonthlyGarmentsList',
+    method: 'get',
+    params
+  })
+}
+
+//保存新增
+export const saveMonthlyGarments = (data) => {
+  return service({
+    url: '/mes_server/monthly_garments/saveMonthlyGarments',
+    method: 'post',
+    data
+  })
+}
+
+//修改数据
+export const updateMonthlyGarments = (data) => {
+  return service({
+    url: '/mes_server/monthly_garments/updateMonthlyGarments',
+    method: 'post',
+    data
+  })
+}
+
+//删除数据
+export const deleteMonthlyGarments = (data) => {
+  return service({
+    url: '/mes_server/monthly_garments/deleteMonthlyGarments',
+    method: 'post',
+    data
+  })
+}
+
+//查询日志
+export const getLogs = (params) => {
+  return service({
+    url: '/mes_server/monthly_garments/getLogs',
+    method: 'get',
+    params
+  })
+}
+

+ 403 - 0
src/view/SalaryManage/ERPDataSynchronization.vue

@@ -0,0 +1,403 @@
+<template>
+    <div>
+      <layout>
+        <layout-header>
+          <div class="">
+            <!--          按钮部分-->
+            <el-form ref="elSearchFormRef"   class="demo-form-inline" :rules="searchRule" >
+              <el-form-item>
+                <el-input v-model="searchInfo" placeholder="搜索订单编号" clearable style="width: 200px;margin: 5px"></el-input>
+                <el-button type="primary" icon="search" class="bt" @click="OrderSearch">查询</el-button>
+                <el-button type="primary" class="bt" icon="download" @click="synchronizationData">数据同步</el-button>
+                <el-button type="primary" class="bt" icon="download" @click="retrySynchronization">重试同步</el-button>
+                <el-button type="primary" class="bt" icon="download" @click="withdrawSynchronization">撤回同步</el-button>
+                <!-- <el-button type="primary" class="bt" icon="download" @click="mxToExcel">导出到Excel(明细)</el-button> -->
+              </el-form-item>
+            </el-form>
+  
+        
+  
+          </div>
+        </layout-header>
+  
+        <layout>
+          <!--    左侧树侧形结构(暂时注释)-->
+          <!-- <layout-sider :resize-directions="['right']" :width="190" style="margin-right: 10px;">
+            <div class="JKWTree-tree" style="height: 200px">
+              <h3>生产进程查询</h3>
+              <el-tree :data="treeData"  :props="defaultProps" @node-click="handleNodeClick" @node-expand="handleNodeExpand">
+              </el-tree>
+            </div>
+          </layout-sider> -->
+
+          <!-- 右侧区域 -->
+          <layout-content >
+          <el-main>
+            <div class="gva-table-box">
+              <!-- 表格数据 -->
+              <el-table ref="multipleTable" style="width: 100%;height: 33vh" tooltip-effect="dark"
+                        :row-style="{ height: '25px' }"  :header-cell-style="{ padding: '0px' }"
+                        :cell-style="{ padding: '0px' }" :header-row-style="{ height: '20px' }"
+                        :data="hztableData"  border row-key="ID"
+                        size="small"
+                        :cell-class-name="gxbgCellClass"
+                        highlight-current-row="true" @row-dblclick="updateCompanyFunc"
+                        @row-click="tableRowClick" :show-overflow-tooltip="true"
+                        @selection-change="handleSelectionChange">
+               <el-table-column  sortable align="center" label="订单编号" prop="订单编号"  width="110" />
+               <el-table-column  sortable align="center" label="生产款号" prop="生产款号"  width="130" />
+               <el-table-column   align="center" label="款式" prop="款式"  width="130" />
+               <el-table-column   align="center" label="客户编号" prop="客户编号"  width="100" />
+               <el-table-column   align="center" label="工单完工日期" prop="工单完工日期"  width="160" />
+               <el-table-column   align="center" label="工单状态" prop="gd_statu"  width="100" />
+               <el-table-column   align="center" label="备注" prop="remark"  width="200" />
+               <el-table-column   align="center" label="同步状态" prop="同步状态"  width="130" />
+               <el-table-column   align="center" label="最后同步时间" prop="last_sync_time"  width="160" />
+               <el-table-column   align="center" label="最后同步结果" prop="last_sync_msg"  width="130" />
+              </el-table>
+  
+              <!-- 分页 -->
+              <div class="gva-pagination">
+                <el-pagination layout="total" :current-page="page" :page-size="pageSize"
+                 :total="total" @current-change="handleCurrentChange" @size-change="handleSizeChange" />
+              </div>
+            </div>
+
+
+            <el-tabs v-model="activeTab" type="border-card">
+              <el-tab-pane label="同步日志" name="sync">
+                <div class="gva-table-box">
+                  <el-table ref="syncTable" style="width: 100%;height: 45vh" tooltip-effect="dark"
+                            :row-style="{ height: '25px' }" :header-cell-style="{ padding: '0px' }"
+                            :cell-style="{ padding: '0px' }" :header-row-style="{ height: '20px' }"
+                            :data="syncLogData" border size="small"
+                            :show-overflow-tooltip="true">
+                    <!-- <el-table-column align="center" label="ID" prop="id" width="80" /> -->
+                    <el-table-column align="center" label="订单编号" prop="work_order_no" width="120" />
+                    <el-table-column align="center" label="款式" prop="style_no" width="120" />
+                    <el-table-column align="center" label="完工日期" prop="complete_date" width="120" />
+                    <el-table-column align="center" label="操作人" prop="operator" width="100" />
+                    <el-table-column align="center" label="同步时间" prop="sync_time" width="160" />
+                    <el-table-column align="center" label="报工起始" prop="report_start_seq" width="100" />
+                    <el-table-column align="center" label="报工结束" prop="report_end_seq" width="100" />
+                    <el-table-column align="center" label="同步状态" prop="sync_status_text" width="100" />
+                    <el-table-column align="center" label="同步方式" prop="sync_module_text" width="100" />
+                    <el-table-column align="center" label="同步结果" prop="sync_msg" width="200" />
+                  </el-table>
+                </div>
+              </el-tab-pane>
+              <el-tab-pane label="操作日志" name="operation">
+                <div class="gva-table-box">
+                  <el-table ref="operationTable" style="width: 100%;height: 45vh" tooltip-effect="dark"
+                            :row-style="{ height: '25px' }" :header-cell-style="{ padding: '0px' }"
+                            :cell-style="{ padding: '0px' }" :header-row-style="{ height: '20px' }"
+                            :data="operationLogData" border size="small"
+                            :show-overflow-tooltip="true">
+                    <!-- <el-table-column align="center" label="ID" prop="id" width="80" /> -->
+                    <el-table-column align="center" label="订单编号" prop="work_order_no" width="120"/>
+                    <el-table-column align="center" label="款式" prop="style_no" width="120" />
+                    <el-table-column align="center" label="操作类型" prop="operate_type" width="100" />
+                    <el-table-column align="center" label="操作人" prop="operate_user_name" width="100" />
+                    <el-table-column align="center" label="操作时间" prop="operate_time" width="160" />
+                    <el-table-column align="center" label="操作内容" prop="operate_content" width="300" />
+                    <el-table-column align="center" label="操作状态" prop="operate_status_text" width="100" />
+                    <!-- <el-table-column align="center" label="操作来源" prop="operate_source" width="100" /> -->
+                  </el-table>
+                </div>
+              </el-tab-pane>
+            </el-tabs>
+  
+          </el-main>
+        </layout-content>
+        </layout>
+      </layout>
+
+      <el-dialog title="数据同步" v-model="dialogVisible" width="400px" :close-on-click-modal="false">
+        <el-form :model="syncForm" label-width="100px">
+          <el-form-item label="订单编号">
+            <el-input v-model="syncForm.orderNo" placeholder="请输入订单编号"></el-input>
+          </el-form-item>
+          <el-form-item label="同步方式">
+            <el-radio-group v-model="syncForm.syncType">
+              <el-radio :label="1">工单</el-radio>
+              <el-radio :label="2">工艺</el-radio>
+              <el-radio :label="3">报工</el-radio>
+              <el-radio :label="4">全量</el-radio>
+            </el-radio-group>
+          </el-form-item>
+        </el-form>
+        <template #footer>
+          <el-button @click="closeDialog">取消</el-button>
+          <el-button type="primary" @click="confirmSync">确认</el-button>
+        </template>
+      </el-dialog>
+
+      <el-dialog title="撤回同步" v-model="withdrawDialogVisible" width="400px" :close-on-click-modal="false">
+        <el-form :model="withdrawForm" label-width="100px">
+          <el-form-item label="订单编号">
+            <el-input v-model="withdrawForm.orderNo" disabled></el-input>
+          </el-form-item>
+          <el-form-item label="撤回原因">
+            <el-input v-model="withdrawForm.reason" placeholder="请输入撤回原因"></el-input>
+          </el-form-item>
+        </el-form>
+        <template #footer>
+          <el-button @click="closeWithdrawDialog">取消</el-button>
+          <el-button type="primary" @click="confirmWithdraw">确认</el-button>
+        </template>
+      </el-dialog>
+    </div>
+  </template>
+  <script setup>
+  // 全量引入格式化工具 请按需保留
+  import { Layout, LayoutSider, LayoutContent } from '@arco-design/web-vue';
+  import { getCompletedWorkOrderList,syncWorkOrder,retrySyncWorkOrder,cancelSyncWorkOrder,getSyncLogList,getOperationLogList } from '@/api/mes/job'
+  import {ref, reactive} from 'vue'
+  import { exportExcelFile } from '@/utils/excel'
+  import {ElMessage} from "element-plus";
+  import { useUserStore } from '@/pinia/modules/user';
+  defineOptions({name: 'Company'})
+  //获取登录用户信息
+  const userStore = useUserStore()
+  const _username = ref('')
+  _username.value = userStore.userInfo.userName + '/' + userStore.userInfo.nickName
+  
+  const dialogVisible = ref(false)
+  const syncForm = reactive({
+    orderNo: '',
+    syncType: 4
+  })
+
+  const synchronizationData = () => {
+    syncForm.orderNo = selectedRow.value['订单编号']
+    dialogVisible.value = true
+  }
+
+  const selectedRow = ref(null)
+  const activeTab = ref('sync')
+  const syncLogData = ref([])
+  const operationLogData = ref([])
+
+  const tableRowClick = async (row) => {
+    selectedRow.value = row
+    const orderNo = row['订单编号']
+    const [syncRes, operationRes] = await Promise.all([
+      getSyncLogList({ work_order_no: orderNo }),
+      getOperationLogList({ work_order_no: orderNo })
+    ])
+    if (syncRes.code === 0) {
+      syncLogData.value = syncRes.data
+    } else {
+      syncLogData.value = []
+    }
+    if (operationRes.code === 0) {
+      operationLogData.value = operationRes.data
+    } else {
+      operationLogData.value = []
+    }
+  }
+
+  const withdrawDialogVisible = ref(false)
+  const withdrawForm = reactive({
+    orderNo: '',
+    reason: ''
+  })
+
+  const withdrawSynchronization = () => {
+    if (!selectedRow.value) {
+      ElMessage.warning('请先选择一条订单')
+      return
+    }
+    withdrawForm.orderNo = selectedRow.value['订单编号']
+    withdrawForm.reason = ''
+    withdrawDialogVisible.value = true
+  }
+
+  const closeWithdrawDialog = () => {
+    withdrawDialogVisible.value = false
+    withdrawForm.orderNo = ''
+    withdrawForm.reason = ''
+  }
+
+  const confirmWithdraw = async () => {
+    if (!withdrawForm.reason) {
+      ElMessage.warning('请输入撤回原因')
+      return
+    }
+    const res = await cancelSyncWorkOrder({
+      work_order_no: withdrawForm.orderNo,
+      operator: userStore.userInfo.nickName,
+      reason: withdrawForm.reason
+    })
+    if (res.code === 0) {
+      ElMessage.success('撤回请求已提交')
+      closeWithdrawDialog()
+      OrderSearch()
+    } else {
+      ElMessage.error(res.msg || '撤回失败')
+    }
+  }
+
+  const closeDialog = () => {
+    dialogVisible.value = false
+    syncForm.orderNo = ''
+    syncForm.syncType = 4
+  }
+
+  const confirmSync = async () => {
+    if (!syncForm.orderNo) {
+      ElMessage.warning('请输入订单编号')
+      return
+    }
+    const res = await syncWorkOrder({
+      work_order_no: syncForm.orderNo,
+      sync_module: syncForm.syncType,
+      operator: userStore.userInfo.nickName,
+      operate_source: 'backend',
+      operate_type: 'manual'
+    })
+    if (res.code === 0) {
+      ElMessage.success('同步请求已提交')
+      closeDialog()
+      OrderSearch()
+    } else {
+      ElMessage.error(res.msg || '同步失败')
+    }
+  }
+
+  const retrySynchronization = async () => {
+    if (!selectedRow.value) {
+      ElMessage.warning('请先选择一条订单')
+      return
+    }
+    const res = await retrySyncWorkOrder({
+      work_order_no: selectedRow.value['订单编号']
+    })
+    if (res.code === 0) {
+      ElMessage.success('重试同步请求已提交')
+      OrderSearch()
+    } else {
+      ElMessage.error(res.msg || '重试同步失败')
+    }
+  }
+  // =========== 获取左侧树侧形结构 ===========
+  
+
+  
+  
+
+  // =========== 获取完工工单列表 ===========
+  const hztableData = ref([]);
+  const total = ref(0);
+  const page = ref(1);
+  const pageSize = ref(10);
+  const searchInfo = ref('');
+  const OrderSearch = async () => {
+    const res = await getCompletedWorkOrderList({
+      page: page.value,
+      pageSize: pageSize.value,
+      search: searchInfo.value,
+    })
+    if(res.code === 0){
+      hztableData.value = res.data.list;
+      total.value = res.data.total;
+    }
+  }
+
+  
+
+  
+
+  
+  
+  
+  
+  
+  </script>
+  
+  <style scoped>
+  .form-container {
+    display: flex;
+    flex-wrap: wrap;
+  }
+  
+  .form-column {
+    /*flex: 1;*/
+    margin-right: 15px; /* 调整列之间的间距 */
+  }
+  
+  /* 左侧输入框宽度调整 */
+  .form-column .el-form-item .el-input {
+    width: 150px; /* 调整左侧输入框的宽度 */
+  }
+  
+  :deep(.hui-plan-usage-lows  div) {
+    color: #8c939d !important;
+  }
+  :deep(.lan-plan-usage-lows  div) {
+    color: blue !important;
+    font-weight: bold;
+  }
+  
+
+  /* 媒体查询,根据需要调整断点 */
+  @media screen and (max-width: 768px) {
+    .form-column {
+      flex: 1 0 100%; /* 在小屏幕下变成单列布局 */
+      margin-right: 0;
+    }
+  }
+  /*:deep(.el-table td .cell) {*/
+  /*  line-height: 30px !important;*/
+  /*}*/
+  .JKWTree-container {
+    display: flex;
+  }
+  .JKWTree-tree {
+    /*width: 300px;*/
+    background-color: #fff;
+    padding: 10px;
+    margin-right: 20px;
+  }
+  .JKWTree-tree h3 {
+    font-size: 15px;
+    font-weight: 700;
+    margin: 10px 0;
+  }
+  .JKWTree-content {
+    flex: 1;
+  }
+
+  
+  /* 选中某行时的背景色 */
+  :deep(.el-table__body tr.current-row) > td {
+    background: #ff80ff !important;
+  }
+  </style>
+  <style scoped>
+  :deep(.el-table td .cell) {
+    line-height: 20px !important;
+  }
+  :deep(.el-tabs__header){
+    margin-bottom: 0;
+  }
+  .search{
+    margin-left: 0px !important;
+    margin-right: 10px !important;
+  }
+  .bt{
+    margin-left: 2px !important;
+    padding: 3px !important;
+    font-size: 12px;
+  }
+  .el-tabs__header{
+    margin: 0px !important;
+  }
+  .gva-table-box{
+    padding: 0px !important;
+  }
+  .mab{
+    margin-bottom: 5px;
+  }
+  </style>
+  

+ 21 - 5
src/view/SalaryManage/ProcessProduction.vue

@@ -117,6 +117,7 @@
               </div>
             </template>
           </el-table-column>
+          <el-table-column label="部件名称" prop="部件名称" width="90" align="center"  />
           <el-table-column label="工序编号" prop="工序号" width="90" align="center"  />
           <el-table-column label="工序名称" prop="工序名称" min-width="280" align="left"  show-overflow-tooltip />
           <el-table-column label="员工姓名" prop="员工姓名" width="110" align="left">
@@ -227,6 +228,8 @@ const props = defineProps({
   hideAmountColumns: { type: Boolean, default: false },
   /** 嵌入工分报工页 Tab:隐藏搜索栏与订单摘要,固定表格高度 */
   embeddedInTab: { type: Boolean, default: false },
+  /** 可选的工序过滤(big_process),嵌入工分报工页时传入 */
+  initialProcess: { type: String, default: '' },
 })
 
 /** 主页面表格高度:改此值即可 */
@@ -327,6 +330,7 @@ const flattenProcessList = (processList = []) => {
       rows.push({
         rowKey: `${proc.工序号}-empty`,
         小组: '',
+        部件名称: proc.部件名称 ?? '',
         工序号: proc.工序号 ?? '',
         工序名称: proc.工序名称 ?? '',
         员工姓名: '',
@@ -345,6 +349,7 @@ const flattenProcessList = (processList = []) => {
       rows.push({
         rowKey: `${proc.工序号}-${index}-${staff.员工姓名 || ''}`,
         小组: pickGroupFromStaff(staff),
+        部件名称: proc.部件名称 ?? '',
         工序号: proc.工序号 ?? '',
         工序名称: proc.工序名称 ?? '',
         员工姓名: staff.员工姓名 ?? '',
@@ -696,14 +701,15 @@ const handleQueryClick = async () => {
   workOrderDropdownVisible.value = true
 }
 
-const loadByWorkorder = async (workorder) => {
+const loadByWorkorder = async (workorder, process) => {
   const w = String(workorder || '').trim()
   if (!w) return
   searchKeyword.value = w
-  await executeProductionSearch(w)
+  const p = process !== undefined ? process : props.initialProcess
+  await executeProductionSearch(w, p)
 }
 
-const executeProductionSearch = async (orderNo) => {
+const executeProductionSearch = async (orderNo, processFilter = '') => {
   const workorder = String(orderNo || '').trim()
   if (!workorder) {
     ElMessage.warning('请输入订单编号')
@@ -716,7 +722,7 @@ const executeProductionSearch = async (orderNo) => {
   try {
     const [, res] = await Promise.all([
       loadOrderSummaryFromWorkOrderList(workorder),
-      checkProcessProduction({ workorder }),
+      checkProcessProduction({ workorder, process: processFilter || props.initialProcess || '' }),
     ])
 
     if (res?.code !== 0) {
@@ -748,11 +754,21 @@ const executeProductionSearch = async (orderNo) => {
 watch(
   () => props.initialWorkorder,
   (val) => {
-    if (val) loadByWorkorder(val)
+    if (val) loadByWorkorder(val, props.initialProcess)
   },
   { immediate: true }
 )
 
+// 工序过滤变化时,如果已有工单号则重新查询
+watch(
+  () => props.initialProcess,
+  (val) => {
+    if (val && props.initialWorkorder) {
+      loadByWorkorder(props.initialWorkorder, val)
+    }
+  }
+)
+
 defineExpose({ loadByWorkorder })
 </script>
 

+ 20 - 19
src/view/performance/WorkScoreReporting/gongfenbaogong.vue

@@ -108,6 +108,7 @@
                         ref="processProductionRef"
                         :key="`${currentOrderNo || 'empty'}-${processProductionRefreshKey}`"
                         :initial-workorder="currentOrderNo"
+                        :initial-process="add_formData['工序']"
                         embedded-in-tab
                         hide-amount-columns
                       />
@@ -613,7 +614,7 @@ const getMachineMacdata = async () => {
         // 	message: '未获取到机台编号请确认MAC地址或与管理员联系'
         // })
         add_formData['机台号'] = '测试机台'
-        add_formData['工序'] = _code
+        add_formData['工序'] = _code.value
         add_formData['组别'] = "测试"
         add_formData['人员'] = "测试"
         return;
@@ -1421,7 +1422,7 @@ const refreshProcessProduction = async () => {
   if (!orderNo) return
   await nextTick()
   if (processProductionRef.value?.loadByWorkorder) {
-    await processProductionRef.value.loadByWorkorder(orderNo)
+    await processProductionRef.value.loadByWorkorder(orderNo, add_formData['工序'])
   } else {
     processProductionRefreshKey.value += 1
   }
@@ -2083,13 +2084,13 @@ const warningres = (arr) => {
   box-shadow: none;
 }
 .work-score-employee-section .employee-avatar {
-  width: 36px;
-  height: 36px;
-  font-size: 16px;
+  width: 30px;
+  height: 30px;
+  font-size: 18px;
   margin-bottom: 4px;
 }
 .work-score-employee-section .employee-name {
-  font-size: 15px;
+  font-size: 18px;
   font-weight: 600;
   line-height: 1.2;
   margin-bottom: 2px;
@@ -2100,7 +2101,7 @@ const warningres = (arr) => {
   text-align: center;
 }
 .work-score-employee-section .employee-id {
-  font-size: 11px;
+  font-size: 14px;
   line-height: 1.2;
   white-space: nowrap;
   overflow: hidden;
@@ -2220,10 +2221,10 @@ const warningres = (arr) => {
   text-align: center;
 }
 .work-score-process-table-wrap :deep(.work-score-process-table .cell) {
-  font-size: 14px;
-  line-height: 1.2;
-  padding-top: 4px;
-  padding-bottom: 4px;
+  font-size: 15px;
+  line-height: 1.4;
+  padding-top: 0px;
+  padding-bottom: 0px;
 }
 .work-score-row-actions {
   display: flex;
@@ -2622,13 +2623,13 @@ const warningres = (arr) => {
 /* 员工网格样式 */
 .employee-grid {
   display: grid;
-  grid-template-columns: repeat(8, 1fr);
-  gap: 10px;
+  grid-template-columns: repeat(12, 1fr);
+  gap: 8px;
 }
 
 .employee-card {
   border: 1px solid #e0e0e0;
-  border-radius: 8px;
+  border-radius: 12px;
   padding: 10px;
   text-align: center;
   cursor: pointer;
@@ -2646,26 +2647,26 @@ const warningres = (arr) => {
 }
 
 .employee-avatar {
-  width: 40px;
-  height: 40px;
+  width: 30px;
+  height: 30px;
   border-radius: 50%;
   display: flex;
   align-items: center;
   justify-content: center;
   margin: 0 auto 8px;
-  font-size: 18px;
+  font-size: 20px;
   font-weight: bold;
   color: white;
 }
 
 .employee-name {
-  font-size: 14px;
+  font-size: 20px;
   font-weight: 500;
   margin-bottom: 3px;
 }
 
 .employee-id {
-  font-size: 12px;
+  font-size: 16px;
   color: #999;
 }
 

+ 682 - 0
src/view/yunyin/shengchanguanli/MonthlySampleGarments.vue

@@ -0,0 +1,682 @@
+<template>
+    <div>
+      <layout>
+        <layout-header>
+          <div class="">
+            <!--          按钮部分-->
+            <el-form ref="elSearchFormRef"   class="demo-form-inline" :rules="searchRule" >
+              <el-form-item>
+                <!-- <el-input v-model="searchInfo" placeholder="搜索工单编号" clearable style="width: 200px;margin: 5px" @keyup.enter="onSubmit"></el-input>
+                <el-button type="primary" class="bt" icon="download" @click="hzToExcel" >导出到Excel(汇总)</el-button>
+                <el-button type="primary" class="bt" icon="download" @click="mxToExcel" >导出到Excel(明细)</el-button> -->
+              </el-form-item>
+            </el-form>
+  
+        
+  
+          </div>
+        </layout-header>
+  
+        <layout>
+          <!--    左侧树侧形结构-->
+          <layout-sider :resize-directions="['right']" :width="190" style="margin-right: 10px;">
+            <div class="JKWTree-tree" style="height: 200px">
+              <h3>样衣生产月份</h3>
+              <el-tree :data="treeData"  :props="defaultProps" @node-click="handleNodeClick" class="treecolor">
+              </el-tree>
+            </div>
+          </layout-sider>
+  
+          <!-- 右侧区域 -->
+          <layout-content >
+          <el-main>
+            <div class="gva-table-box">
+              <!-- <div style="margin-bottom: 10px; text-align: right;">
+                <el-button size="small" type="primary" @click="addFirstRow">新增一行</el-button>
+              </div> -->
+              <!-- 表格数据 -->
+              <el-table ref="multipleTable" style="width: 100%;height: 70vh" tooltip-effect="dark"
+                        :row-style="{ height: '25px' }"  :header-cell-style="{ padding: '0px' }"
+                        :cell-style="{ padding: '0px' }" :header-row-style="{ height: '20px' }"
+                        :data="tableData"  border row-key="ID"
+                        size="small"
+                        :cell-class-name="gxbgCellClass"
+                        highlight-current-row="true"
+                        @row-click="tableRowClick" :show-overflow-tooltip="true"
+                        @selection-change="handleSelectionChange"
+                        @cell-click="handleTableCellClick"
+                        @row-dblclick="handleRowDblclick">
+               <el-table-column sortable align="center" label="客户编号" prop="客户编号" width="120">
+                 <template #default="{ row }">
+                   <el-input v-if="editingCell.rowId === (row.UniqId || row.tempId) && editingCell.prop === '客户编号'"
+                             v-model="row.客户编号"
+                             @blur="handleCellBlur(row, '客户编号')"
+                             @keyup.enter="handleCellBlur(row, '客户编号')"
+                             :ref="el => { if (el) el.focus() }" />
+                   <span v-else @click="handleCellClick(row, '客户编号')" class="cell-span">{{ row.客户编号 }}</span>
+                 </template>
+               </el-table-column>
+               <el-table-column sortable align="center" label="款号" prop="款号" width="120">
+                 <template #default="{ row }">
+                   <el-input v-if="editingCell.rowId === (row.UniqId || row.tempId) && editingCell.prop === '款号'"
+                             v-model="row.款号"
+                             @blur="handleCellBlur(row, '款号')"
+                             @keyup.enter="handleCellBlur(row, '款号')"
+                             :ref="el => { if (el) el.focus() }" />
+                   <span v-else @click="handleCellClick(row, '款号')" class="cell-span">{{ row.款号 }}</span>
+                 </template>
+               </el-table-column>
+               <el-table-column sortable align="center" label="款式" prop="款式" width="120">
+                 <template #default="{ row }">
+                   <el-input v-if="editingCell.rowId === (row.UniqId || row.tempId) && editingCell.prop === '款式'"
+                             v-model="row.款式"
+                             @blur="handleCellBlur(row, '款式')"
+                             @keyup.enter="handleCellBlur(row, '款式')"
+                             :ref="el => { if (el) el.focus() }" />
+                   <span v-else @click="handleCellClick(row, '款式')" class="cell-span">{{ row.款式 }}</span>
+                 </template>
+               </el-table-column>
+               <el-table-column sortable align="center" label="样办种类" prop="样办种类" width="120">
+                 <template #default="{ row }">
+                   <el-input v-if="editingCell.rowId === (row.UniqId || row.tempId) && editingCell.prop === '样办种类'"
+                             v-model="row.样办种类"
+                             @blur="handleCellBlur(row, '样办种类')"
+                             @keyup.enter="handleCellBlur(row, '样办种类')"
+                             :ref="el => { if (el) el.focus() }" />
+                   <span v-else @click="handleCellClick(row, '样办种类')" class="cell-span">{{ row.样办种类 }}</span>
+                 </template>
+               </el-table-column>
+               <el-table-column sortable align="center" label="做办部门" prop="做办部门" width="120">
+                 <template #default="{ row }">
+                   <el-input v-if="editingCell.rowId === (row.UniqId || row.tempId) && editingCell.prop === '做办部门'"
+                             v-model="row.做办部门"
+                             @blur="handleCellBlur(row, '做办部门')"
+                             @keyup.enter="handleCellBlur(row, '做办部门')"
+                             :ref="el => { if (el) el.focus() }" />
+                   <span v-else @click="handleCellClick(row, '做办部门')" class="cell-span">{{ row.做办部门 }}</span>
+                 </template>
+               </el-table-column>
+               <el-table-column sortable align="center" label="数量" prop="数量" width="100">
+                 <template #default="{ row }">
+                   <el-input v-if="editingCell.rowId === (row.UniqId || row.tempId) && editingCell.prop === '数量'"
+                             v-model="row.数量"
+                             @blur="handleCellBlur(row, '数量')"
+                             @keyup.enter="handleCellBlur(row, '数量')"
+                             :ref="el => { if (el) el.focus() }" />
+                   <span v-else @click="handleCellClick(row, '数量')" class="cell-span">{{ row.数量 }}</span>
+                 </template>
+               </el-table-column>
+               <el-table-column sortable align="center" label="下单日期" prop="下单日期" width="120">
+                 <template #default="{ row }">
+                   <el-date-picker v-if="editingCell.rowId === (row.UniqId || row.tempId) && editingCell.prop === '下单日期'"
+                                   v-model="row.下单日期"
+                                   type="date"
+                                   value-format="YYYY-MM-DD"
+                                   placeholder="选择日期"
+                                   style="width: 100%"
+                                   @blur="handleCellBlur(row, '下单日期')" />
+                   <span v-else @click="handleCellClick(row, '下单日期')" class="cell-span">{{ row.下单日期 }}</span>
+                 </template>
+               </el-table-column>
+               <el-table-column sortable align="center" label="面料进度" prop="面料进度" width="120">
+                 <template #default="{ row }">
+                   <el-input v-if="editingCell.rowId === (row.UniqId || row.tempId) && editingCell.prop === '面料进度'"
+                             v-model="row.面料进度"
+                             @blur="handleCellBlur(row, '面料进度')"
+                             @keyup.enter="handleCellBlur(row, '面料进度')"
+                             :ref="el => { if (el) el.focus() }" />
+                   <span v-else @click="handleCellClick(row, '面料进度')" class="cell-span">{{ row.面料进度 }}</span>
+                 </template>
+               </el-table-column>
+               <el-table-column sortable align="center" label="辅料进度" prop="辅料进度" width="120">
+                 <template #default="{ row }">
+                   <el-input v-if="editingCell.rowId === (row.UniqId || row.tempId) && editingCell.prop === '辅料进度'"
+                             v-model="row.辅料进度"
+                             @blur="handleCellBlur(row, '辅料进度')"
+                             @keyup.enter="handleCellBlur(row, '辅料进度')"
+                             :ref="el => { if (el) el.focus() }" />
+                   <span v-else @click="handleCellClick(row, '辅料进度')" class="cell-span">{{ row.辅料进度 }}</span>
+                 </template>
+               </el-table-column>
+               <el-table-column sortable align="center" label="计划交办日期" prop="计划交办日期" width="140">
+                 <template #default="{ row }">
+                   <el-date-picker v-if="editingCell.rowId === (row.UniqId || row.tempId) && editingCell.prop === '计划交办日期'"
+                                   v-model="row.计划交办日期"
+                                   type="date"
+                                   value-format="YYYY-MM-DD"
+                                   placeholder="选择日期"
+                                   style="width: 100%"
+                                   @blur="handleCellBlur(row, '计划交办日期')" />
+                   <span v-else @click="handleCellClick(row, '计划交办日期')" class="cell-span">{{ row.计划交办日期 }}</span>
+                 </template>
+               </el-table-column>
+               <el-table-column sortable align="center" label="实际交办日期" prop="实际交办日期" width="140">
+                 <template #default="{ row }">
+                   <el-date-picker v-if="editingCell.rowId === (row.UniqId || row.tempId) && editingCell.prop === '实际交办日期'"
+                                   v-model="row.实际交办日期"
+                                   type="date"
+                                   value-format="YYYY-MM-DD"
+                                   placeholder="选择日期"
+                                   style="width: 100%"
+                                   @blur="handleCellBlur(row, '实际交办日期')" />
+                   <span v-else @click="handleCellClick(row, '实际交办日期')" class="cell-span">{{ row.实际交办日期 }}</span>
+                 </template>
+               </el-table-column>
+               <el-table-column sortable align="center" label="备注" prop="备注" width="150">
+                 <template #default="{ row }">
+                   <el-input v-if="editingCell.rowId === (row.UniqId || row.tempId) && editingCell.prop === '备注'"
+                             v-model="row.备注"
+                             @blur="handleCellBlur(row, '备注')"
+                             @keyup.enter="handleCellBlur(row, '备注')"
+                             :ref="el => { if (el) el.focus() }" />
+                   <span v-else @click="handleCellClick(row, '备注')" class="cell-span">{{ row.备注 }}</span>
+                 </template>
+               </el-table-column>
+               <el-table-column align="center" label="操作" width="230" fixed="right">
+                 <template #default="{ row, $index }">
+                   <el-button size="small" type="primary" @click="addRow($index)">新增一行</el-button>
+                   <el-button size="small" type="warning" @click="handleRowDblclick(row)">详情</el-button>
+                   <el-button size="small" type="danger" @click="deleteRow(row, $index)">删除</el-button>
+                 </template>
+               </el-table-column>
+              </el-table>
+
+              <!-- 分页 -->
+              <div class="gva-pagination">
+                <el-pagination layout="total" :current-page="page" :page-size="pageSize"
+                 :total="total" @current-change="handleCurrentChange" @size-change="handleSizeChange" />
+              </div>
+
+              <!-- 日志弹窗 -->
+              <el-dialog title="修改记录" v-model="logDialogVisible" width="800px">
+                <el-table :data="logTableData" border size="small">
+                  <el-table-column prop="ModifyField" label="修改字段" width="120" />
+                  <el-table-column prop="OriginalValue" label="原值" width="150" />
+                  <el-table-column prop="Modified" label="新值" width="150" />
+                  <el-table-column prop="修改人" label="修改人" width="100" />
+                  <el-table-column prop="修改日期" label="修改日期" width="180" />
+                </el-table>
+              </el-dialog>
+            </div>
+
+          </el-main>
+        </layout-content>
+        </layout>
+      </layout>
+    </div>
+  </template>
+  <script setup>
+  import { Layout, LayoutSider, LayoutContent } from '@arco-design/web-vue';
+  import * as XLSX from 'xlsx'
+  import FileSaver from 'file-saver'
+  import {ref, reactive} from 'vue'
+  import { exportExcelFile } from '@/utils/excel'
+  import {ElMessage} from "element-plus";
+  import { getDateTree, getMonthlyGarmentsList, saveMonthlyGarments, updateMonthlyGarments, deleteMonthlyGarments, getLogs, } from '@/api/mes/job'
+  import { useUserStore } from '@/pinia/modules/user';
+  //获取登录用户信息
+    const userStore = useUserStore()
+    const _username = ref('')
+    _username.value = userStore.userInfo.userName + '/' + userStore.userInfo.nickName
+  defineOptions({name: 'Company'})
+
+  const treeData = ref([])
+  const defaultProps = {
+    children: 'children',
+    label: 'label'
+  }
+
+  const getTree = async () => {
+    try {
+      const response = await getDateTree() 
+      const data = response.data || {}
+      
+      const sortedYearMonths = Object.keys(data).sort((a, b) => {
+        const yearMonthA = parseInt(a);
+        const yearMonthB = parseInt(b);
+        return yearMonthB - yearMonthA;
+      });
+      
+      treeData.value = sortedYearMonths.map(yearMonth => {
+        return {
+          label: `${yearMonth}`,
+          children: data[yearMonth].map(customer => {
+            return {
+              label: `${customer.客户编号} (${customer.total}条)`,
+              yearMonth: yearMonth,
+              customerCode: customer.客户编号,
+              total: customer.total,
+            }
+          })
+        }
+      })
+
+      if (treeData.value.length === 0 && tableData.value.length === 0) {
+        tableData.value.push({
+          tempId: 'empty_row_' + Date.now(),
+          '客户编号': '',
+          '款号': '',
+          '款式': '',
+          '样办种类': '',
+          '做办部门': '',
+          '数量': '',
+          '下单日期': '',
+          '面料进度': '',
+          '辅料进度': '',
+          '计划交办日期': '',
+          '实际交办日期': '',
+          '备注': ''
+        })
+      }
+    } catch (error) {
+      ElMessage.error('获取数据失败')
+      console.error('获取树形数据失败:', error)
+    }
+  }
+  getTree()
+
+  const tableData = ref([])
+  const _noderq = ref('')
+  const _nodecode = ref('')
+  const page = ref(1)
+  const pageSize = ref(20)
+  const total = ref(0)
+  const editingCell = reactive({ rowId: null, prop: null })
+  const editingOriginalValue = ref('')
+  const logDialogVisible = ref(false)
+  const logTableData = ref([])
+
+  const handleNodeClick = async (node) => {
+    const allNodes = document.querySelectorAll('.treecolor .el-tree-node');
+    allNodes.forEach(node => {
+      node.querySelector('.el-tree-node__label').style.color = '';
+    });
+    
+    const clickedNodeId = node['$treeNodeId'];
+    const clickedNode = document.querySelector(`.treecolor .el-tree-node[data-key="${clickedNodeId}"]`);
+    if (clickedNode) {
+      clickedNode.querySelector('.el-tree-node__label').style.color = 'red';
+    }
+    
+    _noderq.value = node.yearMonth
+    _nodecode.value = node.customerCode
+    
+    if (!node.children) {
+        const res = await getMonthlyGarmentsList({sys_rq: node.yearMonth, customer: node.customerCode})
+        if(res.code === 0){
+          tableData.value = res.data.result || []
+          total.value = res.data.total || res.data.result.length
+        }
+        if (tableData.value.length === 0) {
+          tableData.value.push({
+            tempId: 'empty_row_' + Date.now(),
+            '客户编号': '',
+            '款号': '',
+            '款式': '',
+            '样办种类': '',
+            '做办部门': '',
+            '数量': '',
+            '下单日期': '',
+            '面料进度': '',
+            '辅料进度': '',
+            '计划交办日期': '',
+            '实际交办日期': '',
+            '备注': ''
+          })
+        }
+      }
+  }
+
+  const searchInfo = ref('')
+  const searchRule = {}
+  const elSearchFormRef = ref()
+
+  const onSubmit = async () => {
+    if (!searchInfo.value) {
+      ElMessage.warning('请输入搜索内容');
+      return;
+    } else {
+      const res = await getMonthlyGarmentsList({search: searchInfo.value})
+      if(res.code === 0){
+        tableData.value = res.data.result
+        total.value = res.data.total || res.data.result.length
+      }
+    }
+  }
+
+  const tableRowClick = (row) => {
+    console.log('当前行数据:', row)
+  }
+
+  const handleSelectionChange = (selectedItems) => {
+    console.log('选中项:', selectedItems)
+  }
+
+  const handleCurrentChange = (val) => {
+    page.value = val
+  }
+
+  const handleSizeChange = (val) => {
+    pageSize.value = val
+  }
+
+  const updateCompanyFunc = (row) => {
+    console.log('双击行:', row)
+  }
+
+  const gxbgCellClass = ({row, column, rowIndex, columnIndex}) => {
+    return ''
+  }
+
+  const addRow = (index) => {
+    const newRow = {
+      tempId: 'temp_' + Date.now(),
+      '客户编号': '',
+      '款号': '',
+      '款式': '',
+      '样办种类': '',
+      '做办部门': '',
+      '数量': '',
+      '下单日期': '',
+      '面料进度': '',
+      '辅料进度': '',
+      '计划交办日期': '',
+      '实际交办日期': '',
+      '备注': ''
+    }
+    tableData.value.splice(index + 1, 0, newRow)
+  }
+
+  const addFirstRow = () => {
+    const newRow = {
+      tempId: 'temp_' + Date.now(),
+      '客户编号': '',
+      '款号': '',
+      '款式': '',
+      '样办种类': '',
+      '做办部门': '',
+      '数量': '',
+      '下单日期': '',
+      '面料进度': '',
+      '辅料进度': '',
+      '计划交办日期': '',
+      '实际交办日期': '',
+      '备注': ''
+    }
+    tableData.value.push(newRow)
+  }
+
+  const handleRowDblclick = async (row) => {
+    if (!row.UniqId) return
+    const res = await getLogs({ UniqId: row.UniqId })
+    if (res.code === 0) {
+      logTableData.value = res.data.result || []
+      logDialogVisible.value = true
+    } else {
+      ElMessage.error('获取日志失败')
+    }
+  }
+
+  const deleteRow = async (row, index) => {
+    try {
+      if (row.UniqId) {
+        const res = await deleteMonthlyGarments({ UniqId: row.UniqId, nickName: userStore.userInfo.nickName })
+        if(res.code !== 0){
+          ElMessage.error('删除失败')
+          return
+        }
+      }
+      tableData.value.splice(index, 1)
+      ElMessage.success('删除成功')
+      await refreshData()
+    } catch (error) {
+      ElMessage.error('删除失败')
+      console.error('删除失败:', error)
+    }
+  }
+
+  const handleCellClick = (row, prop) => {
+    editingCell.rowId = row.UniqId || row.tempId
+    editingCell.prop = prop
+    editingOriginalValue.value = row[prop]
+  }
+
+  const handleTableCellClick = (row, column, cell, event) => {
+    console.log('cell-click triggered:', { rowId: row.UniqId || row.tempId, columnLabel: column.label, columnProp: column.prop, columnIndex: column.index })
+    if (column.prop && column.prop !== '') {
+      handleCellClick(row, column.prop)
+      console.log('editingCell after click:', editingCell)
+    }
+  }
+
+  const refreshData = async () => {
+    await getTree()
+    if (_noderq.value && _nodecode.value) {
+      const res = await getMonthlyGarmentsList({sys_rq: _noderq.value, customer: _nodecode.value})
+      if(res.code === 0){
+        tableData.value = res.data.result || []
+        total.value = res.data.total || res.data.result.length
+        if (tableData.value.length === 0) {
+          tableData.value.push({
+            tempId: 'empty_row_' + Date.now(),
+            '客户编号': '',
+            '款号': '',
+            '款式': '',
+            '样办种类': '',
+            '做办部门': '',
+            '数量': '',
+            '下单日期': '',
+            '面料进度': '',
+            '辅料进度': '',
+            '计划交办日期': '',
+            '实际交办日期': '',
+            '备注': ''
+          })
+        }
+      }
+    }
+  }
+
+  const handleCellBlur = async (row, prop) => {
+    try {
+      if (row.UniqId) {
+        if (String(row[prop]) === String(editingOriginalValue.value)) {
+          editingCell.rowId = null
+          editingCell.prop = null
+          return
+        }
+        const res = await updateMonthlyGarments({
+          UniqId: row.UniqId,
+          nickName: userStore.userInfo.nickName,
+          '客户编号': row['客户编号'],
+          '款号': row['款号'],
+          '款式': row['款式'],
+          '样办种类': row['样办种类'],
+          '做办部门': row['做办部门'],
+          '数量': row['数量'],
+          '下单日期': row['下单日期'],
+          '面料进度': row['面料进度'],
+          '辅料进度': row['辅料进度'],
+          '计划交办日期': row['计划交办日期'],
+          '实际交办日期': row['实际交办日期'],
+          '备注': row['备注']
+        })
+        if(res.code !== 0){
+          ElMessage.error('修改失败')
+          editingCell.rowId = null
+          editingCell.prop = null
+          return
+        }
+        await refreshData()
+      } else if (row.tempId) {
+        const res = await saveMonthlyGarments({
+          '客户编号': row['客户编号'],
+          '款号': row['款号'],
+          '款式': row['款式'],
+          '样办种类': row['样办种类'],
+          '做办部门': row['做办部门'],
+          '数量': row['数量'],
+          '下单日期': row['下单日期'],
+          '面料进度': row['面料进度'],
+          '辅料进度': row['辅料进度'],
+          '计划交办日期': row['计划交办日期'],
+          '实际交办日期': row['实际交办日期'],
+          '备注': row['备注']
+        })
+        if(res.code === 0){
+          row.UniqId = parseInt(res.data.id)
+          delete row.tempId
+          await refreshData()
+        } else {
+          ElMessage.error('保存失败')
+          editingCell.rowId = null
+          editingCell.prop = null
+          return
+        }
+      }
+      editingCell.rowId = null
+      editingCell.prop = null
+      ElMessage.success('保存成功')
+    } catch (error) {
+      ElMessage.error('保存失败')
+      console.error('保存失败:', error)
+      editingCell.rowId = null
+      editingCell.prop = null
+    }
+  }
+
+  const exportToExcel = (data, filename = '导出数据.xlsx', sheetName = 'Sheet1') => {
+    try {
+      const wb = XLSX.utils.book_new();
+      
+      if (typeof data === 'object' && data.nodeType === 1) {
+        const worksheet = XLSX.utils.table_to_sheet(data, { raw: true });
+        XLSX.utils.book_append_sheet(wb, worksheet, sheetName);
+      } else if (Array.isArray(data)) {
+        const worksheet = XLSX.utils.json_to_sheet(data);
+        XLSX.utils.book_append_sheet(wb, worksheet, sheetName);
+      } else if (data['!ref']) {
+        XLSX.utils.book_append_sheet(wb, data, sheetName);
+      } else {
+        throw new Error('不支持的导出数据类型');
+      }
+      
+      XLSX.writeFile(wb, filename);
+      return true;
+    } catch (error) {
+      console.error('导出Excel失败:', error);
+      ElMessage.error('导出失败: ' + error.message);
+      return false;
+    }
+  };
+
+  const hzToExcel = () => {
+    if (tableData.value.length === 0) {
+      ElMessage.warning('暂无数据可导出');
+      return;
+    }
+    exportToExcel(tableData.value, '月份样衣生产进度表.xlsx', '月份样衣生产进度表');
+  }
+
+  const mxToExcel = () => {
+    if (tableData.value.length === 0) {
+      ElMessage.warning('暂无数据可导出');
+      return;
+    }
+    exportToExcel(tableData.value, '月份样衣生产进度明细表.xlsx', '月份样衣生产进度明细表');
+  }
+  </script>
+  
+  <style scoped>
+  .form-container {
+    display: flex;
+    flex-wrap: wrap;
+  }
+  
+  .form-column {
+    /*flex: 1;*/
+    margin-right: 15px; /* 调整列之间的间距 */
+  }
+  
+  /* 左侧输入框宽度调整 */
+  .form-column .el-form-item .el-input {
+    width: 150px; /* 调整左侧输入框的宽度 */
+  }
+  
+  :deep(.hui-plan-usage-lows  div) {
+    color: #8c939d !important;
+  }
+  :deep(.lan-plan-usage-lows  div) {
+    color: blue !important;
+    font-weight: bold;
+  }
+  
+
+  /* 媒体查询,根据需要调整断点 */
+  @media screen and (max-width: 768px) {
+    .form-column {
+      flex: 1 0 100%; /* 在小屏幕下变成单列布局 */
+      margin-right: 0;
+    }
+  }
+  /*:deep(.el-table td .cell) {*/
+  /*  line-height: 30px !important;*/
+  /*}*/
+  .JKWTree-container {
+    display: flex;
+  }
+  .JKWTree-tree {
+    /*width: 300px;*/
+    background-color: #fff;
+    padding: 10px;
+    margin-right: 20px;
+  }
+  .JKWTree-tree h3 {
+    font-size: 15px;
+    font-weight: 700;
+    margin: 10px 0;
+  }
+  .JKWTree-content {
+    flex: 1;
+  }
+
+  
+  /* 选中某行时的背景色 */
+  :deep(.el-table__body tr.current-row) > td {
+    background: #ff80ff !important;
+  }
+  </style>
+  <style scoped>
+  :deep(.el-table td .cell) {
+    line-height: 20px !important;
+  }
+  :deep(.el-tabs__header){
+    margin-bottom: 0;
+  }
+  .search{
+    margin-left: 0px !important;
+    margin-right: 10px !important;
+  }
+  .bt{
+    margin-left: 2px !important;
+    padding: 3px !important;
+    font-size: 12px;
+  }
+  .el-tabs__header{
+    margin: 0px !important;
+  }
+  .gva-table-box{
+    padding: 0px !important;
+  }
+  .mab{
+    margin-bottom: 5px;
+  }
+  .cell-span {
+    display: inline-block;
+    width: 100%;
+    height: 100%;
+    cursor: pointer;
+    padding: 4px 0;
+  }
+  </style>
+  

+ 28 - 3
src/view/yunyin/shengchanguanli/gongdanziliao.vue

@@ -376,6 +376,7 @@
 		             <el-button type="primary" @click="openGyDetailProcessAdd">增加</el-button>
 		             <el-button type="danger" :disabled="!gyDetailSelection.length || gyDetailDeleting" :loading="gyDetailDeleting" @click="handleGyDetailBatchDelete">删除</el-button>
 		             <el-button type="primary" @click="handleGyDetailPrintPreview">打印预览</el-button>
+             <el-button type="success" v-if="!gyDetailReadonly" @click="handleGyDetailSave">保存</el-button>
 		           </div>
 		           <el-button type="danger" class="gy-detail-dialog-exit" @click="closeGyDetailDialog">退出</el-button>
 		         </div>
@@ -5323,8 +5324,15 @@ const sortTableData = ref([])
       } else {
         row.大工序 = '车缝'
       }
-      // 编辑部件名称时,不调用handleUpdateProcess,不新增
-      return
+      
+      // 新行编辑部件名称时,检查工序名称是否已填写
+      if (row._gyDetailNew) {
+        const gongXuMingCheng = String(row.工序名称 || '').trim()
+        if (!gongXuMingCheng) {
+          ElMessage.warning('请先填写工序名称')
+          return
+        }
+      }
     }
     
     if (editingField === '定额分') {
@@ -6037,6 +6045,23 @@ const sortTableData = ref([])
     }
   }
 
+  const handleGyDetailSave = () => {
+    if (gyDetailCellInputRef.value) {
+      const comp = gyDetailCellInputRef.value
+      const selectEl = comp.$el?.querySelector('.el-select')
+      if (selectEl) {
+        comp.blur?.()
+      } else {
+        const inputEl = comp.$el?.querySelector('input') || 
+                        comp.$el?.querySelector('textarea') ||
+                        comp.$el
+        if (inputEl) {
+          inputEl.blur()
+        }
+      }
+    }
+  }
+
   const handleGyDetailPrintPreview = async () => {
     if (!gyDetailFormData.工艺列表?.length) {
       ElMessage.warning('暂无工艺数据,无法打印预览')
@@ -8182,7 +8207,7 @@ const sortTableData = ref([])
   .sort-drag-handle {
     color: var(--el-text-color-secondary);
     cursor: move;
-  }
+  }	
   .list-lib-name-select {
     width: 100%;
   }