m0_70156489 10 часов назад
Родитель
Сommit
bd4d86f5db

+ 22 - 8
src/view/SalaryManage/ProcessProduction.vue

@@ -142,7 +142,12 @@
               </div>
             </template>
           </el-table-column>
-          <el-table-column label="数量" prop="数量" width="85" align="center" />
+          <el-table-column
+            :label="initialSysId || embeddedInTab ? '本组完成数' : '数量'"
+            prop="数量"
+            width="96"
+            align="center"
+          />
           <el-table-column label="累计完成数" prop="累计完成数" width="96" align="center" />
           <el-table-column label="未完成数" prop="未完成数" width="90" align="center" />
 
@@ -230,6 +235,8 @@ const props = defineProps({
   embeddedInTab: { type: Boolean, default: false },
   /** 可选的工序过滤(big_process),嵌入工分报工页时传入 */
   initialProcess: { type: String, default: '' },
+  /** 工分报工组别(sys_id),嵌入工分报工页时传入 add_formData['组别'] */
+  initialSysId: { type: String, default: '' },
 })
 
 /** 主页面表格高度:改此值即可 */
@@ -729,7 +736,7 @@ const loadByWorkorder = async (workorder, process) => {
   await executeProductionSearch(w, p)
 }
 
-const executeProductionSearch = async (orderNo, processFilter = '') => {
+const executeProductionSearch = async (orderNo, processFilter = '', sysIdFilter) => {
   const workorder = String(orderNo || '').trim()
   if (!workorder) {
     ElMessage.warning('请输入订单编号')
@@ -740,9 +747,16 @@ const executeProductionSearch = async (orderNo, processFilter = '') => {
   searched.value = true
   pageHint.value = ''
   try {
+    const process = processFilter || props.initialProcess || ''
+    const sys_id = String(
+      sysIdFilter !== undefined ? sysIdFilter : props.initialSysId || ''
+    ).trim()
+    const params = { workorder, process }
+    if (sys_id) params.sys_id = sys_id
+
     const [, res] = await Promise.all([
       loadOrderSummaryFromWorkOrderList(workorder),
-      checkProcessProduction({ workorder, process: processFilter || props.initialProcess || '' }),
+      checkProcessProduction(params),
     ])
 
     if (res?.code !== 0) {
@@ -779,12 +793,12 @@ watch(
   { immediate: true }
 )
 
-// 工序过滤变化时,如果已有工单号则重新查询
+// 工序 / 组别变化时,如果已有工单号则重新查询
 watch(
-  () => props.initialProcess,
-  (val) => {
-    if (val && props.initialWorkorder) {
-      loadByWorkorder(props.initialWorkorder, val)
+  () => [props.initialProcess, props.initialSysId],
+  () => {
+    if (props.initialWorkorder) {
+      loadByWorkorder(props.initialWorkorder, props.initialProcess)
     }
   }
 )

+ 256 - 38
src/view/SalaryManage/SalaryList.vue

@@ -10,10 +10,10 @@
             <el-form-item>
               <el-input
                 v-model="staffSearchInput"
-                placeholder="查询员工编号或员工姓名"
+                placeholder="查询订单编号、员工编号或员工姓名"
                 clearable
                 class="search"
-                style="width: 200px"
+                style="width: 260px"
               />
               <el-button type="primary" icon="search" class="bt" @click="applyStaffSearch">查询</el-button>
               <el-button type="primary" icon="search" class="bt" @click="openProcessProductionDialog">工序产量核查</el-button>
@@ -25,7 +25,7 @@
       <layout class="salary-list-body">
         <layout-sider :resize-directions="['right']" :width="190" class="salary-list-sider">
           <div v-loading="menuLoading" class="JKWTree-tree staff-tree-panel salary-tree-panel-fill">
-            <h3 class="salary-tree-h3">报工日期 / 工序 / 组别</h3>
+            <h3 class="salary-tree-h3">报工日期 / 生产工序 / 组别</h3>
             <div class="salary-tree-scroll">
               <el-tree
                 ref="menuTreeRef"
@@ -57,7 +57,7 @@
           <el-main class="yunyin-main-block">
             <div class="gva-table-box">
               <el-table
-                v-if="menuReady"
+                v-if="hasQueryCondition"
                 v-loading="listLoading"
                 :data="pagedPivotRows"
                 border
@@ -152,7 +152,7 @@
                   />
                 </template>
               </el-table>
-              <div v-if="menuReady && !listLoading" class="gva-pagination">
+              <div v-if="hasQueryCondition && !listLoading" class="gva-pagination">
                 <el-pagination
                   v-model:current-page="pivotPage"
                   v-model:page-size="pivotPageSize"
@@ -167,7 +167,7 @@
                 class="salary-main-placeholder"
               >
                 <el-empty
-                  description="请在左侧选择年、月、日期、工序或组别"
+                  description="暂无数据"
                   :image-size="120"
                 />
               </div>
@@ -245,7 +245,7 @@
             />
           </template>
         </el-table-column>
-        <el-table-column label="生产款号" prop="生产款号" width="200" align="center">
+        <el-table-column label="生产款号" prop="生产款号" width="220" align="center">
           <template #header>
             <TableColumnFilterHeader
               label="生产款号"
@@ -297,15 +297,11 @@
           </template>
         </el-table-column>
         <el-table-column label="数量" prop="数量" width="80" align="center" />
-        <el-table-column label="定额分" prop="标准分数" width="90" align="center" />
-        <el-table-column label="工资" prop="工资" width="90" align="center" />
-        <el-table-column label="标准工时(秒)" prop="标准工时" width="120" align="center" />
-        <el-table-column label="标准总工时(秒)" prop="生产工时" width="150" align="center" />
-        <!-- <el-table-column label="生产总工分" prop="生产分数" width="100" align="center" /> -->
-        <el-table-column label="日期" prop="日期" width="110" align="center">
+
+        <el-table-column label="报工时间" prop="sys_rq" width="160" align="center">
           <template #header>
             <TableColumnFilterHeader
-              label="日期"
+              label="报工时间"
               v-model="detailReportDateFilter"
               :options="detailReportDateFilterOptions"
               :active="isDetailReportDateFilterActive"
@@ -315,6 +311,17 @@
           </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="标准工时" width="120" align="center" />
+        <el-table-column label="标准总工时(秒)" prop="生产工时" width="150" align="center" />
+        <!-- <el-table-column label="生产总工分" prop="生产分数" width="100" align="center" /> -->
+        <el-table-column label="操作" width="90" align="center" fixed="right">
+          <template #default="{ row }">
+            <el-button type="primary" link size="small" @click="openEditQty(row)">修改</el-button>
+          </template>
+        </el-table-column>
+
           <template #empty>
             <el-empty
               v-if="!detailLoading"
@@ -325,6 +332,36 @@
         </el-table>
       </div>
     </el-dialog>
+
+    <!-- 修改报工数量 -->
+    <el-dialog
+      v-model="editQtyVisible"
+      title="修改数量"
+      width="420px"
+      append-to-body
+      destroy-on-close
+      :close-on-click-modal="false"
+    >
+      <el-form label-width="88px">
+        <el-form-item label="工序名称">
+          <el-input :model-value="editQtyForm.工序名称" disabled />
+        </el-form-item>
+        <el-form-item label="数量" required>
+          <el-input
+            v-model="editQtyForm.number"
+            type="number"
+            min="0"
+            step="1"
+            placeholder="请输入修正后的数量"
+            @keyup.enter="submitEditQty"
+          />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="editQtyVisible = false">取消</el-button>
+        <el-button type="primary" :loading="editQtyLoading" @click="submitEditQty">确定</el-button>
+      </template>
+    </el-dialog>
   </div>
 </template>
 
@@ -336,12 +373,16 @@ import {
   GetReportingWorkMonth,
   GetStaffSalaryList,
   GetStaffSalaryDetail,
+  UpdateReportingWork,
 } from '@/api/mes/job'
 import ProcessProduction from '@/view/SalaryManage/ProcessProduction.vue'
 import TableColumnFilterHeader from '@/view/SalaryManage/TableColumnFilterHeader.vue'
+import { useUserStore } from '@/pinia/modules/user'
 
 defineOptions({ name: 'SalaryList' })
 
+const userStore = useUserStore()
+
 const processProductionVisible = ref(false)
 const processProductionDialogKey = ref(0)
 
@@ -364,7 +405,17 @@ const listQuery = ref({
 })
 /** 月/日/工序/组别均可高亮选中 */
 const currentMenuKey = ref('')
-const menuReady = computed(() => !!String(listQuery.value.sys_rq || '').trim())
+
+const staffSearchInput = ref('')
+/** 点击「查询」后传给接口的 search */
+const searchKeywordApplied = ref('')
+
+/** 左侧树有日期,或顶部已点过查询(有 search)均可展示列表 */
+const hasQueryCondition = computed(
+  () =>
+    !!String(listQuery.value.sys_rq || '').trim() ||
+    !!String(searchKeywordApplied.value || '').trim()
+)
 
 /** 由 sys_rq 推导月份,供日列透视使用(年→当月,月→YYYY-MM,日→截 YYYY-MM) */
 function resolvePivotMonthYm(sysRq) {
@@ -380,16 +431,40 @@ function resolvePivotMonthYm(sysRq) {
   return ''
 }
 
-const selectedMonthYm = computed(() => resolvePivotMonthYm(listQuery.value.sys_rq))
+/** 仅 search 查询时,从返回 children 日期推断透视月份 */
+function inferMonthYmFromList(list) {
+  const counts = Object.create(null)
+  for (const item of list || []) {
+    for (const c of item.children || []) {
+      const ds = String(c['日期'] ?? '').trim()
+      const m = ds.match(/^(\d{4})[-/.年](\d{1,2})/)
+      if (!m) continue
+      const ym = `${m[1]}-${String(m[2]).padStart(2, '0')}`
+      counts[ym] = (counts[ym] || 0) + 1
+    }
+  }
+  let best = ''
+  let bestN = 0
+  for (const [ym, n] of Object.entries(counts)) {
+    if (n > bestN || (n === bestN && ym > best)) {
+      best = ym
+      bestN = n
+    }
+  }
+  if (best) return best
+  return getCurrentYearMonth().ym
+}
+
+/** 当前透视用的 YYYY-MM(树日期优先,否则取上次接口推断) */
+const pivotMonthYm = ref('')
+const selectedMonthYm = computed(
+  () => pivotMonthYm.value || resolvePivotMonthYm(listQuery.value.sys_rq)
+)
 
 const listLoading = ref(false)
-/** 员工为行、1~31 为列的透视表(接口全量,不含搜索) */
+/** 员工为行、1~31 为列的透视表(接口返回全量) */
 const pivotRowsAll = ref([])
 
-const staffSearchInput = ref('')
-/** 点击「查询」后生效的筛选关键字 */
-const searchKeywordApplied = ref('')
-
 const pivotPage = ref(1)
 const pivotPageSize = ref(50)
 
@@ -584,16 +659,7 @@ watch(detailRowsAll, () => {
   syncAllDetailFilters()
 })
 
-const pivotRowsFiltered = computed(() => {
-  let rows = applyPivotGroupFilter(pivotRowsAll.value)
-  const kw = (searchKeywordApplied.value || '').trim().toLowerCase()
-  if (!kw) return rows
-  return rows.filter((r) => {
-    const no = String(r['员工编号'] ?? '').toLowerCase()
-    const name = String(r['员工姓名'] ?? '').toLowerCase()
-    return no.includes(kw) || name.includes(kw)
-  })
-})
+const pivotRowsFiltered = computed(() => applyPivotGroupFilter(pivotRowsAll.value))
 
 const pagedPivotRows = computed(() => {
   const list = pivotRowsFiltered.value
@@ -803,6 +869,58 @@ function applyMenuDefaultExpand() {
   })
 }
 
+function getTodayDateStr() {
+  const t = new Date()
+  const y = t.getFullYear()
+  const m = String(t.getMonth() + 1).padStart(2, '0')
+  const d = String(t.getDate()).padStart(2, '0')
+  return `${y}-${m}-${d}`
+}
+
+/** 在菜单树中查找某日节点(如 d-2026-09-04) */
+function findDayNodeByDate(tree, dateStr) {
+  const targetId = `d-${dateStr}`
+  const walk = (nodes) => {
+    for (const n of nodes || []) {
+      if (n.id === targetId) return n
+      if (n.nodeType === 'day' && String(n.queryParams?.sys_rq || '') === dateStr) return n
+      const found = walk(n.children)
+      if (found) return found
+    }
+    return null
+  }
+  return walk(tree)
+}
+
+/** 进入页面:默认按当天日期查询,并高亮/展开对应树节点 */
+function selectTodayAndLoad() {
+  const today = getTodayDateStr()
+  const { year, month } = getCurrentYearMonth()
+  const dayNode = findDayNodeByDate(menuTreeData.value, today)
+
+  listQuery.value = {
+    sys_rq: today,
+    big_process: '',
+    group: '',
+  }
+  searchKeywordApplied.value = ''
+  currentMenuKey.value = dayNode?.id || `d-${today}`
+  pivotPage.value = 1
+  selectAllPivotGroupFilter()
+
+  const expandKeys = [`y-${year}`, `m-${year}-${month}`]
+  menuDefaultExpandedKeys.value = expandKeys
+  nextTick(() => {
+    try {
+      menuTreeRef.value?.setExpandedKeys?.(expandKeys)
+      menuTreeRef.value?.setCurrentKey?.(currentMenuKey.value || null)
+    } catch {
+      // ignore
+    }
+  })
+  loadStaffList()
+}
+
 function normalizeReportingMenuTree(nodes, depth = 0, ctx = {}) {
   if (!Array.isArray(nodes)) return []
   const out = []
@@ -911,6 +1029,8 @@ async function loadLeftMenu() {
       const raw = res.data
       menuTreeData.value = normalizeReportingMenuTree(Array.isArray(raw) ? raw : [])
       applyMenuDefaultExpand()
+      // 默认按当天日期拉列表
+      selectTodayAndLoad()
     } else {
       menuTreeData.value = []
     }
@@ -961,7 +1081,7 @@ function onMenuNodeClick(data, node) {
     big_process: String(qp.big_process || '').trim(),
     group: String(qp.group || '').trim(),
   }
-  staffSearchInput.value = ''
+  // 点树只按日期/工序/组别查,不带顶部 search
   searchKeywordApplied.value = ''
   pivotPage.value = 1
   selectAllPivotGroupFilter()
@@ -973,37 +1093,55 @@ async function loadStaffList() {
   const sys_rq = String(listQuery.value.sys_rq || '').trim()
   const big_process = String(listQuery.value.big_process || '').trim()
   const group = String(listQuery.value.group || '').trim()
-  const monthYm = resolvePivotMonthYm(sys_rq)
-  if (!sys_rq) {
+  const search = String(searchKeywordApplied.value || '').trim()
+  if (!sys_rq && !search) {
     pivotRowsAll.value = []
+    pivotMonthYm.value = ''
     return
   }
   listLoading.value = true
   try {
-    const params = { sys_rq }
+    const params = {}
+    if (sys_rq) params.sys_rq = sys_rq
+    if (search) params.search = search
     if (big_process) params.big_process = big_process
     if (group) params.group = group
     const res = await GetStaffSalaryList(params)
     if (res?.code === 0) {
       const raw = res.data
       const list = Array.isArray(raw) ? raw : raw?.list ?? []
+      let monthYm = resolvePivotMonthYm(sys_rq)
+      if (!monthYm) monthYm = inferMonthYmFromList(list)
+      pivotMonthYm.value = monthYm
       pivotRowsAll.value = buildPivotRows(list, monthYm, group)
       pivotPage.value = 1
     } else {
       pivotRowsAll.value = []
+      pivotMonthYm.value = resolvePivotMonthYm(sys_rq)
+      ElMessage.warning(res?.msg || '加载工资列表失败')
     }
   } catch (e) {
     console.error(e)
     pivotRowsAll.value = []
-    ElMessage.error('加载工资列表失败')
+    ElMessage.error(e?.response?.data?.msg || e?.message || '加载工资列表失败')
   } finally {
     listLoading.value = false
   }
 }
 
 function applyStaffSearch() {
-  searchKeywordApplied.value = (staffSearchInput.value || '').trim()
+  const search = (staffSearchInput.value || '').trim()
+  if (!search) {
+    ElMessage.warning('请输入订单编号/员工编号/姓名进行查询')
+    return
+  }
+  // 顶部搜索只传 search,不带左侧日期/工序/组别
+  listQuery.value = { sys_rq: '', big_process: '', group: '' }
+  currentMenuKey.value = ''
+  restoreMenuCurrentKey()
+  searchKeywordApplied.value = search
   pivotPage.value = 1
+  loadStaffList()
 }
 
 /** 与 renyuanjibenziliao 表头一致:紧凑;保留字号便于密列阅读 */
@@ -1130,8 +1268,86 @@ async function loadDetailRows() {
   }
 }
 
+const editQtyVisible = ref(false)
+const editQtyLoading = ref(false)
+const editQtyForm = ref({
+  id: '',
+  number: '',
+  工序名称: '',
+  标准工时: '',
+  标准分数: '',
+  生产工时: '',
+  生产分数: '',
+})
+
+function openEditQty(row) {
+  const id = row?.id ?? row?.ID
+  if (id === undefined || id === null || id === '') {
+    ElMessage.warning('缺少报工记录ID,无法修改')
+    return
+  }
+  editQtyForm.value = {
+    id,
+    number: String(row['数量'] ?? ''),
+    工序名称: String(row['工序名称'] ?? ''),
+    标准工时: row['标准工时'],
+    标准分数: row['标准分数'],
+    生产工时: row['生产工时'],
+    生产分数: row['生产分数'],
+  }
+  editQtyVisible.value = true
+}
+
+async function submitEditQty() {
+  const numberStr = String(editQtyForm.value.number ?? '').trim()
+  const qty = Number(numberStr)
+  if (!numberStr || Number.isNaN(qty) || qty <= 0) {
+    ElMessage.warning('请输入大于 0 的数量')
+    return
+  }
+
+  // 按定额重算工时/工分后一并提交;无定额时沿用原值
+  const stdHour = Number(editQtyForm.value.标准工时)
+  const stdScore = Number(editQtyForm.value.标准分数)
+  let production_hour = String(editQtyForm.value.生产工时 ?? '')
+  let production_score = String(editQtyForm.value.生产分数 ?? '')
+  if (!Number.isNaN(stdHour) && stdHour > 0) {
+    production_hour = (qty * stdHour).toFixed(2)
+  }
+  if (!Number.isNaN(stdScore) && stdScore > 0) {
+    production_score = (qty * stdScore).toFixed(2)
+  }
+
+  const mod_id = userStore.userInfo?.nickName || userStore.userInfo?.userName || ''
+
+  editQtyLoading.value = true
+  try {
+    const res = await UpdateReportingWork({
+      id: editQtyForm.value.id,
+      number: String(qty),
+      production_hour,
+      production_score,
+      mod_id,
+    })
+    if (res?.code === 0) {
+      ElMessage.success(res.msg || '修改成功')
+      editQtyVisible.value = false
+      await loadDetailRows()
+      if (hasQueryCondition.value) await loadStaffList()
+    } else {
+      ElMessage.error(res?.msg || '修改失败')
+    }
+  } catch (e) {
+    console.error(e)
+    ElMessage.error(e?.response?.data?.msg || e?.message || '修改失败')
+  } finally {
+    editQtyLoading.value = false
+  }
+}
+
 function onDetailClosed() {
   detailRowsAll.value = []
+  editQtyVisible.value = false
   detailOrderNoFilterCtrl.clear()
   detailStyleNoFilterCtrl.clear()
   detailProductNameFilterCtrl.clear()
@@ -1277,6 +1493,7 @@ onMounted(() => {
 .salary-list-page :deep(.arco-layout-content) {
   padding-top: 0 !important;
   padding-bottom: 0 !important;
+  padding-left: 0 !important;
   flex: 1;
   min-width: 0;
   min-height: 0;
@@ -1293,7 +1510,8 @@ onMounted(() => {
   display: flex;
   flex-direction: column;
   background: var(--el-bg-color, #fff);
-  --el-main-padding: 0 12px 12px 12px;
+  /* 去掉与左侧树之间的空隙 */
+  --el-main-padding: 0 12px 12px 0;
   padding: var(--el-main-padding) !important;
 }
 .salary-list-page .gva-table-box {

+ 12 - 2
src/view/performance/WorkScoreReporting/gongfenbaogong.vue

@@ -109,6 +109,7 @@
                         :key="`${currentOrderNo || 'empty'}-${processProductionRefreshKey}`"
                         :initial-workorder="currentOrderNo"
                         :initial-process="add_formData['工序']"
+                        :initial-sys-id="add_formData['组别']"
                         embedded-in-tab
                         hide-amount-columns
                       />
@@ -278,6 +279,7 @@
                               >
                                 <el-table-column prop="工艺编号" label="工序号" width="72" align="center" />
                                 <el-table-column prop="工艺名称" label="工序名称" min-width="240" />
+                                <el-table-column prop="本组完成数" label="本组完成数" min-width="88" align="center" />
                                 <el-table-column prop="累计完成数" label="累计完成数" min-width="60" align="center" />
                                 <el-table-column prop="未完成数" label="未完成数" min-width="60" align="center" />
                                 <el-table-column label="完工人员" width="120">
@@ -699,6 +701,9 @@ const normalizeWorkScoreProcessRow = (row) => {
     ...row,
     工艺编号,
     工艺名称,
+    本组完成数: row['本组完成数'] ?? row.group_complete ?? row['本组完成'] ?? '',
+    累计完成数: row['累计完成数'] ?? row.total_complete ?? '',
+    未完成数: row['未完成数'] ?? row.unfinished ?? '',
     标准分钟: row['标准分钟'] ?? row.standard_minutes ?? row['标准工时'] ?? row.standard_hour ?? '',
     标准工时: row['标准工时'] ?? row.standard_hour ?? row['标准分钟'] ?? row.standard_minutes ?? '',
     标准工分: row['标准工分'] ?? row.standard_score ?? row['标准公分'] ?? '',
@@ -908,11 +913,16 @@ const loadPartProcesses = async (index, partCode) => {
     return
   }
   try {
-    const response = await GetCarProcess({
+    const carParams = {
       workorder: getActiveOrderNo(),
       part_code: code,
       majorprocess: add_formData['工序'],
-    });
+    }
+    // 本组完成数由后端在 GetCarProcess 中按组别返回
+    const sysId = String(add_formData['组别'] || '').trim()
+    if (sysId) carParams.sys_id = sysId
+
+    const response = await GetCarProcess(carParams)
     
     if (response.code === 0 && response.data) {
       const processList = normalizeWorkScoreProcessList(response.data)

+ 409 - 183
src/view/yunyin/shengchanguanli/gongdanziliao.vue

@@ -59,13 +59,26 @@
 						  @row-click="tableRowClick" @row-dblclick="updateCompanyFunc"
 						  @selection-change="ddzlSelectionChange" :show-overflow-tooltip="true">
 				  <el-table-column align="center" type="selection" width="40"/>
-				  <el-table-column align="center"  label="附件"     	prop="status" 	width="60" />
-				  <el-table-column align="left" sortable label="订单编号"     	prop="订单编号" 	width="110" />
-				  <el-table-column align="left"  label="客户"      	prop="客户编号" 		width="70" />
-				  <el-table-column align="left"  label="款号"   	prop="生产款号"  width="200" />
-				  <el-table-column align="left"  label="款式"       	prop="款式"   	width="100"/> 
-				  <el-table-column align="left"  label="数量"    	prop="订单数量" 	width="60" />
-				  <el-table-column align="center" label="状态" prop="gd_statu" width="115" >
+				  <el-table-column align="center" prop="status" width="75">
+					<template #header>
+					  <el-tooltip
+						content='「技术附件」列显示 “*” 表示该订单尚未上传对应附件'
+						placement="top"
+						effect="dark"
+					>
+						<span class="fj-col-header">
+						  附件
+						  <el-icon class="fj-col-help"><QuestionFilled /></el-icon>
+						</span>
+					  </el-tooltip>
+					</template>
+				  </el-table-column>
+				  <el-table-column align="left"   label="订单编号" prop="订单编号" 	width="105" sortable/>
+				  <el-table-column align="left"   label="客户"     prop="客户编号" 		width="68" />
+				  <el-table-column align="left"   label="款号"     prop="生产款号"  width="200" />
+				  <el-table-column align="left"   label="款式"     prop="款式"   	width="100"/> 
+				  <el-table-column align="left"   label="数量"     prop="订单数量" 	width="60" />
+				  <el-table-column align="center" label="状态" 	   prop="gd_statu" width="85" sortable>
 				      <template #default="{ row }">
 				        <el-tag :type="row.gd_statu === '1-计划中' ? 'warning' : 
 				                       (row.gd_statu === '已裁剪' ? 'primary' : 
@@ -84,13 +97,13 @@
 				        </el-tag>
 				      </template>
 				  </el-table-column>
-				  <el-table-column align="left"  label="落货日期"   	prop="落货日期" 	width="100" />
-				  <el-table-column align="left"  label="船样"    	prop="船样合计" 	width="60" />
-				  <el-table-column align="left"  label="船样描述"    	prop="船样描述" 	width="110" />
-				  <el-table-column align="left"  label="单位"    		prop="单位" 		width="60" />
+				  <el-table-column align="left"  label="落货日期"   	prop="落货日期" 	width="98" />
+				  <el-table-column align="left"  label="船样"    		prop="船样合计" 	width="55" />
+				  <el-table-column align="left"  label="船样描述"    	prop="船样描述" 	width="130" />
+				  <el-table-column align="left"  label="单位"    		prop="单位" 		width="55" />
 				  <el-table-column align="left"  label="面料"       	prop="面料" 		width="180" />
 				  <el-table-column align="left"  label="备注"       	prop="要求" 		width="180" />
-				  <el-table-column align="left"  label="计划制造工分"        	prop="计划制造工分"		width="180" />
+				  <el-table-column align="left"  label="计划制造工分"    prop="计划制造工分"		width="180" />
 				  <el-table-column align="left"  label="粘衬"        	prop="粘衬"		width="180" />
 				  <el-table-column align="left"  label="箱唛要求"   	prop="箱唛要求" 	width="180" />
 				  <el-table-column align="left"  label="制单人"    	prop="Sys_id" 	width="160" />
@@ -182,14 +195,14 @@
 							:summary-method="gySummaries"
 							show-summary>
 					<!-- <el-table-column align="left" label="部件编号" prop="部件编号" width="80"/> -->
-					<el-table-column align="left" label="部件名称" prop="部件名称" width="150"/>
-					<el-table-column align="left" label="工序编号" prop="工艺编号" width="80"/>
+					<el-table-column align="center" label="部件名称" prop="部件名称" width="80"/>
+					<el-table-column align="center" label="工序编号" prop="工艺编号" width="75"/>
 					<el-table-column align="left" label="工序名称" prop="工艺名称" width="200"/>
-					<el-table-column align="left" label="生产工序" prop="大工艺" width="80"/>
+					<el-table-column align="center" label="生产工序" prop="大工艺" width="75"/>
 					<!-- <el-table-column align="left" label="秒" prop="标准工时" width="80"/> -->
-					<el-table-column align="left" label="工分" prop="标准公分" width="80"/>
+					<el-table-column align="center" label="工分" prop="标准公分" width="75"/>
 					<el-table-column align="left" label="备注" prop="备注" width="150"/>
-					<el-table-column align="left" label="难度系数" prop="系数" width="80"/>
+					<el-table-column align="center" label="难度系数" prop="系数" width="80"/>
 					<el-table-column align="left" label="录入人" prop="系统人" width="100"/>
 					<el-table-column align="left" label="录入时间" prop="系统时间" width="160"/>
 					<el-table-column align="left" label="修改人" prop="修改人" width="100"/>
@@ -376,7 +389,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>
+             <el-button type="success" v-if="!gyDetailReadonly" :loading="gyDetailSaving" @click="handleGyDetailSave">保存</el-button>
 		           </div>
 		           <el-button type="danger" class="gy-detail-dialog-exit" @click="closeGyDetailDialog">退出</el-button>
 		         </div>
@@ -475,7 +488,8 @@
 		           :height="gyDetailTableHeight"
 		           :min-width="gyDetailTableMinWidth"
 		           :data="gyDetailDisplayProcessList"
-		           :row-config="{ keyField: '_rowKey', isHover: true, isCurrent: true }"
+		           :row-config="{ keyField: '_rowKey', isHover: true, isCurrent: true, height: 24 }"
+		           :cell-config="{ height: 24, padding: false }"
 		           :scroll-y="{ enabled: true, gt: 30 }"
 		           :scroll-x="{ enabled: true }"
 		           :column-config="{ resizable: true, minWidth: 56 }"
@@ -486,9 +500,9 @@
 		           @checkbox-change="handleGyDetailCheckboxChange"
 		           @checkbox-all="handleGyDetailCheckboxChange"
 		         >
-		           <vxe-column type="checkbox" width="55" fixed="left" />
-		           <vxe-column field="部件编号" title="部件编号" width="90"/>
-		           <vxe-column field="部件名称" width="120">
+		           <vxe-column type="checkbox" width="55" fixed="left" align="center" header-align="center" />
+		           <vxe-column field="部件编号" title="部件编号" width="75" align="center" header-align="center" />
+		           <vxe-column field="部件名称" width="120" align="center" header-align="center">
 		             <template #header>
 		               <div class="gy-detail-col-header">
 		                 <span>部件名称</span>
@@ -520,8 +534,8 @@
                </template>
              </template>
            </vxe-column>
-           <vxe-column field="工序编号" title="工序编号" width="100"/>
-		           <vxe-column field="工序名称" title="工序名称" width="320">
+           <vxe-column field="工序编号" title="工序编号" width="100" align="center" header-align="center" />
+		           <vxe-column field="工序名称" title="工序名称" width="320" align="left" header-align="center">
 		             <template #default="{ row }">
                <span v-if="!isGyDetailCellEditable('工序名称') && !isGyDetailCellEditing(row, '工序名称')">{{ row.工序名称 }}</span>
                <template v-else>
@@ -530,7 +544,7 @@
                </template>
              </template>
 		           </vxe-column>
-		           <vxe-column field="大工序" width="110">
+		           <vxe-column field="大工序" width="110" align="center" header-align="center">
 		             <template #header>
 		               <div class="gy-detail-col-header">
 		                 <span>生产工序</span>
@@ -572,7 +586,7 @@
 		               </template>
 		             </template>
 		           </vxe-column>
-		           <vxe-column field="秒" title="秒" width="80">
+		           <vxe-column field="秒" title="秒" width="80" align="center" header-align="center">
 		             <template #default="{ row }">
 		               <span v-if="gyDetailReadonly">{{ row.秒 }}</span>
 		               <template v-else>
@@ -581,7 +595,7 @@
 		               </template>
 		             </template>
 		           </vxe-column>
-		           <vxe-column field="分" title="分" width="80">
+		           <vxe-column field="分" title="分" width="80" align="center" header-align="center">
 		             <template #default="{ row }">
 		               <span v-if="gyDetailReadonly">{{ row.分 }}</span>
 		               <template v-else>
@@ -590,7 +604,7 @@
 		               </template>
 		             </template>
 		           </vxe-column>
-		           <vxe-column field="定额分" title="工分" width="80">
+		           <vxe-column field="定额分" title="工分" width="80" align="center" header-align="center">
 		             <template #default="{ row }">
                <span v-if="!isGyDetailCellEditable('工分') && !isGyDetailCellEditing(row, '定额分')">{{ row.定额分 }}</span>
                <template v-else>
@@ -599,7 +613,7 @@
                </template>
              </template>
 		           </vxe-column>
-		           <vxe-column field="金额" title="金额" width="80">
+		           <vxe-column field="金额" title="金额" width="80" align="center" header-align="center">
 		             <template #default="{ row }">
 		               <span v-if="gyDetailReadonly">{{ row.金额 }}</span>
 		               <template v-else>
@@ -608,7 +622,7 @@
 		               </template>
 		             </template>
 		           </vxe-column>
-		           <vxe-column field="难度系数" width="110">
+		           <vxe-column field="难度系数" width="110" align="center" header-align="center">
 		             <template #header>
 		               <div class="gy-detail-col-header">
 		                 <span>难度系数</span>
@@ -649,7 +663,7 @@
 		               </template>
 		             </template>
 		           </vxe-column>
-		           <vxe-column field="备注" title="备注" width="120">
+		           <vxe-column field="备注" title="备注" width="120" align="center" header-align="center">
 		             <template #default="{ row }">
 		               <span v-if="gyDetailReadonly">{{ row.备注 }}</span>
 		               <template v-else>
@@ -658,7 +672,7 @@
 		               </template>
 		             </template>
 		           </vxe-column>
-		           <vxe-column field="状态" width="80">
+		           <vxe-column field="状态" width="80" align="center" header-align="center">
 		             <template #header>
 		               <div class="gy-detail-col-header">
 		                 <span>状态</span>
@@ -686,7 +700,7 @@
 		               <span>{{ formatGyDetailStatus(row.状态) }}</span>
 		             </template>
 		           </vxe-column>
-		           <vxe-column field="_gyAction" title="操作" width="100" fixed="right" align="center">
+		           <vxe-column field="_gyAction" title="操作" width="100" fixed="right" align="center" header-align="center">
 		             <template #default="{ row }">
 		               <el-button
 		                 v-if="isGyDetailRowSplit(row)"
@@ -1031,6 +1045,15 @@
 			</template>
 			
 		  </el-dialog>
+
+		  <!-- 订单图片页内放大预览 -->
+		  <el-image-viewer
+			v-if="imgPreviewVisible"
+			:url-list="imgPreviewList"
+			teleported
+			hide-on-click-modal
+			@close="closeImgPreview"
+		  />
   
 		  <!-- 关联订单面料弹窗 -->
 		  <el-dialog v-model="Related_fabricsVisible" :before-close="Related_fabricscloseDialog" title="关联订单面料" 
@@ -2482,7 +2505,11 @@
   import LuckyExcel from 'luckyexcel';
   import { toRaw } from 'vue';
   import { Layout, LayoutSider, LayoutContent } from '@arco-design/web-vue';
-  import { ElMessage, ElMessageBox,ElUpload, ElButton,ElLoading } from 'element-plus'
+  import { ElMessage, ElMessageBox, ElUpload, ElButton, ElLoading } from 'element-plus'
+  // 生产环境为按需引入,手动 import 组件不会带样式;补全 Upload,避免 picture-card 退化成原生「选择文件」
+  import 'element-plus/es/components/upload/style/css'
+  import 'element-plus/es/components/button/style/css'
+  import 'element-plus/es/components/image-viewer/style/css'
   import { ref, reactive, computed, nextTick, watch, onMounted, onBeforeUnmount, isRef } from 'vue'
   import axios from 'axios';
   import * as XLSX from 'xlsx';
@@ -2575,11 +2602,22 @@
 	});
   };
 
-  // 预览图片
+  // 预览图片(页内放大,不新开标签页)
+  const imgPreviewVisible = ref(false)
+  const imgPreviewList = ref([])
   const handlePreview = (file) => {
-	const previewUrl = file.url || buildFullImageUrl(file.response?.data?.data?.url);
-	if (previewUrl) window.open(previewUrl, '_blank');
-  };
+	const previewUrl = file.url || buildFullImageUrl(file.response?.data?.data?.url)
+	if (!previewUrl) {
+	  ElMessage.warning('暂无可预览的图片')
+	  return
+	}
+	imgPreviewList.value = [previewUrl]
+	imgPreviewVisible.value = true
+  }
+  const closeImgPreview = () => {
+	imgPreviewVisible.value = false
+	imgPreviewList.value = []
+  }
   // 移除图片
   const handleRemove = () => {
 	fileList.splice(0, fileList.length);
@@ -2835,15 +2873,25 @@
 	  syncUploadFileList(edit_ddformData.value.img);
   }
   
+  // Sys_id 形如「账号/角色」,安全取「/」后一段,避免空值或格式异常导致白屏
+  const getSysIdPart = (sysId) => {
+	  const parts = String(sysId ?? '').split('/')
+	  return parts.length > 1 ? parts[1] : ''
+  }
+
   //订单资料修改确认
   const dd_edit_Dialog = async () => {
-	  if(edit_ddformData.value.Sys_id.split('/')[1]===_username.value.split('/')[1]||_username.value.split('/')[1] === '超级管理员'){
-  //日期转换
-  let lhrq_formattedDeliveryDate = '';  
-	  if (edit_ddformData.value.落货日期) {  
-		  const date = new Date(edit_ddformData.value.落货日期);  
-		  lhrq_formattedDeliveryDate = `${date.getFullYear()}-${('0' + (date.getMonth() + 1)).slice(-2)}-${('0' + date.getDate()).slice(-2)}`;  
-	  }  
+	  const orderSysPart = getSysIdPart(edit_ddformData.value?.Sys_id)
+	  const userSysPart = getSysIdPart(_username.value)
+	  if (orderSysPart !== userSysPart && userSysPart !== '超级管理员') {
+		  return ElMessage({ type: 'error', message: '无法修改其他用户订单数据' })
+	  }
+	  //日期转换
+	  let lhrq_formattedDeliveryDate = '';
+	  if (edit_ddformData.value.落货日期) {
+		  const date = new Date(edit_ddformData.value.落货日期);
+		  lhrq_formattedDeliveryDate = `${date.getFullYear()}-${('0' + (date.getMonth() + 1)).slice(-2)}-${('0' + date.getDate()).slice(-2)}`;
+	  }
 	  let params = {};
 	  params.订单编号 = edit_ddformData.value.订单编号
 	  params.生产款号 = edit_ddformData.value.生产款号
@@ -2852,7 +2900,7 @@
 	  params.箱唛要求 = edit_ddformData.value.箱唛要求
 	  params.面料 = edit_ddformData.value.面料
 	  params.要求 = edit_ddformData.value.要求
-	  params.船样描述 = edit_ddformData.value.船样描述  
+	  params.船样描述 = edit_ddformData.value.船样描述
 	  params.船样合计 = edit_ddformData.value.船样合计
 	  params.粘衬 = edit_ddformData.value.粘衬
 	  params.订单数量 = edit_ddformData.value.订单数量
@@ -2863,21 +2911,20 @@
 	  params.id = edit_ddformData.value.Uniqid
 	  params.img = edit_ddformData.value.img
 	  console.log(params)
-	  // return;
-	  //订单资料修改接口
-	  const WorkOrderEdit_list = await WorkOrderEdit(params);
-	  if(WorkOrderEdit_list.code === 0){
-		  ddzl_dialogFormVisible.value = false;
-		  //修改成功调用接口更新表格数据
-		  _WorkList_page()
-		  ElMessage({type: 'success',message: '修改成功'})
-	  }else{
-		  ElMessage({type: 'error',message: '修改失败'})
-	  }
-	  }else{
-		  return ElMessage({type: 'error',message: '无法修改其他用户订单数据'})	
+	  try {
+		  const WorkOrderEdit_list = await WorkOrderEdit(params);
+		  if (WorkOrderEdit_list.code === 0) {
+			  ddzl_dialogFormVisible.value = false;
+			  _WorkList_page()
+			  ElMessage({ type: 'success', message: '修改成功' })
+		  } else {
+			  ElMessage({ type: 'error', message: WorkOrderEdit_list.msg || WorkOrderEdit_list.message || '修改失败' })
+		  }
+	  } catch (error) {
+		  console.error(error);
+		  const msg = error?.response?.data?.msg || error?.response?.data?.message || error?.message || '修改失败,请重试'
+		  ElMessage({ type: 'error', message: msg })
 	  }
-	  
   }
   
   //订单资料修改取消
@@ -3179,68 +3226,82 @@
   }
   const fullscreenLoading = ref(false); // 控制全屏加载
   const isClickable = ref(true); // 控制按钮是否可点击
+
+  const resetAddOrderForm = () => {
+	Object.assign(add_gdscgdformData, {
+	  订单编号: '',
+	  生产款号: '',
+	  款式: '',
+	  客户编号: '',
+	  落货日期: '',
+	  箱唛要求: '',
+	  面料: '',
+	  要求: '',
+	  备注: '',
+	  船样描述: '',
+	  船样合计: '',
+	  粘衬: '',
+	  订单数量: '',
+	  单位: '件',
+	  Sys_id: '',
+	  制单日期: '',
+	  审核日期: '',
+	  合并: false,
+	  img: '',
+	});
+	zsglddtableData.value = [];
+	syncUploadFileList('');
+  };
+
   //新增工单资料确定
   const add_gdenterDialog = async () => {
 	if (!isClickable.value) return;
-  
-	// 禁用按钮
+
+	// 先校验,通过后再 Loading,避免未填完就全屏卡住
+	const fields = [
+	  { key: '款式', message: '款式不能为空' },
+	  { key: '订单编号', message: '网络异常, 请重新打开' },
+	  { key: '客户编号', message: '网络异常, 请重新打开' },
+	  { key: '落货日期', message: '落货日期不能为空' },
+	  { key: '订单数量', message: '订单数量不能为空' },
+	  { key: '面料', message: '缺少面料信息,无法完成提交,请提供面料数据以便获取BOM资料数据' },
+	];
+	for (const field of fields) {
+	  if (!add_gdscgdformData[field.key]) {
+		ElMessage({ type: 'warning', message: field.message });
+		return;
+	  }
+	}
+	if (!add_gdscgdformData['制单日期']) {
+	  ElMessage({ type: 'warning', message: '制单日期不能为空' });
+	  return;
+	}
+
 	isClickable.value = false;
-  
-	// 开启全屏加载,并自定义文字和样式
 	const loadingInstance = ElLoading.service({
-	  lock: true, // 锁定屏幕
-	  text: '正在自动获取生成BOM面料资料,请稍候...', // 自定义加载文字
-	  background: 'rgba(0, 0, 0, 0.8)', // 背景颜色
-	  spinner: 'el-icon-loading', // 加载图标
+	  lock: true,
+	  text: '正在自动获取生成BOM面料资料,请稍候...',
+	  background: 'rgba(0, 0, 0, 0.8)',
+	  spinner: 'el-icon-loading',
 	});
-  
+
 	try {
-	  // 定义需要校验的字段和对应的提示信息
-	  const fields = [
-		{ key: '款式', message: '款式不能为空' },
-		{ key: '订单编号', message: '网络异常, 请重新打开' },
-		{ key: '客户编号', message: '网络异常, 请重新打开' },
-		{ key: '落货日期', message: '落货日期不能为空' },
-		{ key: '订单数量', message: '订单数量不能为空' },
-		{ key: '面料', message: '缺少面料信息,无法完成提交,请提供面料数据以便获取BOM资料数据' },
-	  ];
-  
-	  // 遍历每个字段,进行非空或未定义校验
-	  for (const field of fields) {
-		if (!add_gdscgdformData[field.key]) {
-		  ElMessage({ type: 'warning', message: field.message });
-		  return false;
-		}
-	  }
-  
-	  // 校验制单日期
-	  if (!add_gdscgdformData['制单日期']) {
-		ElMessage({ type: 'warning', message: '制单日期不能为空' });
-		return false;
-	  }
-  
-	  // 日期转换
 	  let lhrq_formattedDeliveryDate = '';
 	  if (add_gdscgdformData['落货日期']) {
 		const date = new Date(add_gdscgdformData['落货日期']);
 		lhrq_formattedDeliveryDate = `${date.getFullYear()}-${('0' + (date.getMonth() + 1)).slice(-2)}-${('0' + date.getDate()).slice(-2)}`;
 	  }
-  
-	  // 获取 zsglddtableData 中所有行的 UNIQID 和订单编号
+
 	  const ids = zsglddtableData.value.map(item => item.UNIQID);
 	  const dds = zsglddtableData.value.map(item => item.订单编号);
-  
-	  // 去重 UNIQID 和订单编号
 	  const uniqueIds = [...new Set(ids)];
 	  const uniqueDds = [...new Set(dds)];
-  
-	  // 用逗号连接 UNIQID 和订单编号
 	  const formattedIds = uniqueIds.join(',');
 	  const formattedDds = uniqueDds.join(',');
-  
+
 	  console.log('关联订单编号:', formattedDds);
 	  console.log('关联订单UNIQID:', formattedIds);
-  
+
 	  const formattedData = {
 		订单编号: add_gdscgdformData['订单编号'],
 		生产款号: add_gdscgdformData['生产款号'],
@@ -3261,35 +3322,33 @@
 		关联订单: formattedDds,
 		关联面料ID: formattedIds,
 	  };
-  
+
 	  console.log(formattedData);
-  
-	  // 调用新增订单接口
+
 	  const edit_gdformdata_list = await WorkOrderAdd(formattedData);
 	  if (edit_gdformdata_list.code === 0) {
+		resetAddOrderForm();
 		add_gddialogFormVisible.value = false;
 		ElMessage({ type: 'success', message: '新增成功' });
-  
-		// 新增后刷新表格,保证表格显示最新数据
 		const WorkListdata = await WorkOrderList({ search: _Gd_khdh.value, page: 1, limit: 50 });
 		tableData.value = WorkListdata.data.data;
 	  } else {
-		ElMessage({ type: 'error', message: '新增失败' });
+		ElMessage({ type: 'error', message: edit_gdformdata_list.msg || edit_gdformdata_list.message || '新增失败' });
 	  }
 	} catch (error) {
 	  console.error('Error:', error);
-	  ElMessage({ type: 'error', message: '操作失败,请重试' });
+	  const msg = error?.response?.data?.msg || error?.response?.data?.message || error?.message || '操作失败,请重试'
+	  ElMessage({ type: 'error', message: msg });
 	} finally {
-	  // 无论成功或失败,最终都要重新启用按钮并关闭加载
 	  isClickable.value = true;
-	  loadingInstance.close(); // 关闭加载
+	  loadingInstance.close();
 	}
   };
-  //新增工单资料取消
-  const add_gdcloseDialog = () => {
+  //新增工单资料取消 / 关闭
+  const add_gdcloseDialog = (done) => {
+	resetAddOrderForm();
 	add_gddialogFormVisible.value = false;
-	syncUploadFileList('');
-	add_gdscgdformData.img = '';
+	if (typeof done === 'function') done();
   }
   
   
@@ -3615,8 +3674,8 @@ function Input_handle() {
   add_gdscgdformData['订单数量'] = add_gdscgdformData['订单数量'].replace(/[^\d]/g, '')
   add_gdscgdformData['船样合计'] = add_gdscgdformData['船样合计'].replace(/[^\d]/g, '')
   
-  edit_ddformData['订单数量'] = edit_ddformData['订单数量'].replace(/[^\d]/g, '')
-  edit_ddformData['船样合计'] = edit_ddformData['船样合计'].replace(/[^\d]/g, '')
+  edit_ddformData.value['订单数量'] = String(edit_ddformData.value['订单数量'] ?? '').replace(/[^\d]/g, '')
+  edit_ddformData.value['船样合计'] = String(edit_ddformData.value['船样合计'] ?? '').replace(/[^\d]/g, '')
 }
 
 
@@ -5119,6 +5178,7 @@ const sortTableData = ref([])
   
   const gyDetailSelection = ref([])
   const gyDetailDeleting = ref(false)
+  const gyDetailSaving = ref(false)
   const gyDetailTableWrapRef = ref(null)
   const gyDetailTableRef = ref(null)
   const gyDetailTableHeight = ref(480)
@@ -5356,31 +5416,14 @@ const sortTableData = ref([])
       if (!buJianMingCheng) {
         row.部件编号 = ''
       } else {
-        // 查找是否有相同的部件名称
-        const existingRow = gyDetailFormData.工艺列表.find(
-          (r) => r !== row && String(r.部件名称 || '').trim() === buJianMingCheng && r.部件编号
-        )
-        if (existingRow) {
-          // 有相同的,使用相同的部件编号
-          row.部件编号 = existingRow.部件编号
-        } else {
-          // 没有相同的,找最大的部件编号+1
-          let maxCode = 0
-          gyDetailFormData.工艺列表.forEach((r) => {
-            const code = parseInt(String(r.部件编号 || ''), 10)
-            if (!isNaN(code) && code > maxCode) {
-              maxCode = code
-            }
-          })
-          row.部件编号 = String(maxCode + 1).padStart(2, '0')
-        }
+        ensureGyDetailPartCode(row)
       }
       
       // 自动填写生产工序
       const autoFillOptions = ['裁剪', '手工', '大烫', '总检', '包装']
       if (autoFillOptions.includes(buJianMingCheng)) {
         row.大工序 = buJianMingCheng
-      } else {
+      } else if (buJianMingCheng) {
         row.大工序 = '车缝'
       }
       
@@ -6131,20 +6174,85 @@ 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 handleGyDetailSave = async () => {
+    if (gyDetailReadonly.value || gyDetailSaving.value) return
+    gyDetailSaving.value = true
+    try {
+      // 先结束当前单元格编辑(原先保存按钮只 blur,没有真正提交)
+      let hadEditingCell = false
+      if (gyDetailEditingCell.value) {
+        hadEditingCell = true
+        const editing = gyDetailEditingCell.value
+        const editingRow = (gyDetailFormData.工艺列表 || []).find((r) => r._rowKey === editing.rowKey)
+        if (editingRow) {
+          await finishGyDetailCellEdit(editingRow)
+        } else {
+          clearGyDetailCellEdit()
+        }
+      }
+
+      const pendingRows = (gyDetailFormData.工艺列表 || []).filter((row) => {
+        if (!isGyDetailRowChanged(row)) return false
+        // 完全空白的新行不提交
+        if (row._gyDetailNew && !row.id) {
+          const filled =
+            String(row.部件名称 || '').trim() ||
+            String(row.工序名称 || '').trim() ||
+            String(row.定额分 || '').trim() ||
+            String(row.金额 || '').trim() ||
+            String(row.秒 || '').trim() ||
+            String(row.分 || '').trim()
+          if (!filled) return false
+        }
+        return true
+      })
+
+      if (!pendingRows.length) {
+        // 编辑中的格子已在 finish 时保存并提示过,这里不再重复弹
+        if (!hadEditingCell) {
+          ElMessage.success('没有需要保存的修改')
         }
+        return
+      }
+
+      let successCount = 0
+      let failCount = 0
+      let skipIncomplete = 0
+
+      for (const row of pendingRows) {
+        const requiredReady = !!(
+          String(row.部件名称 || '').trim() &&
+          String(row.工序名称 || '').trim() &&
+          String(row.大工序 || '').trim()
+        )
+        if (!row.id && !requiredReady) {
+          skipIncomplete++
+          continue
+        }
+        const ok = await handleUpdateProcess(row, { silent: true })
+        if (ok) successCount++
+        else failCount++
+      }
+
+      if (successCount > 0 && failCount === 0 && skipIncomplete === 0) {
+        ElMessage.success(successCount === 1 ? '保存成功' : `已保存 ${successCount} 条工序`)
+      } else if (successCount > 0) {
+        const parts = [`成功 ${successCount} 条`]
+        if (failCount) parts.push(`失败 ${failCount} 条`)
+        if (skipIncomplete) parts.push(`未填齐必填 ${skipIncomplete} 条`)
+        ElMessage.warning(parts.join(','))
+      } else if (skipIncomplete > 0 && failCount === 0) {
+        ElMessage.warning('请先填写部件名称、工序名称、生产工序后再保存')
+      } else {
+        ElMessage.error('保存失败')
       }
+
+      refreshGyDetailTableFooter()
+    } catch (e) {
+      console.error('工艺资料保存失败', e)
+      ElMessage.error('保存失败')
+    } finally {
+      gyDetailSaving.value = false
     }
   }
 
@@ -6506,13 +6614,47 @@ const sortTableData = ref([])
     }
   }
 
-  const handleUpdateProcess = async (row) => {
+  /**
+   * 按部件名称补齐部件编号:同名复用已有编号,否则取当前列表最大编号+1。
+   * 提交 Add/Update 前必须调用,避免后端报「部件编号不能为空」。
+   */
+  const ensureGyDetailPartCode = (row) => {
+    if (!row) return ''
+    const partName = String(row.部件名称 || '').trim()
+    if (!partName) {
+      row.部件编号 = ''
+      return ''
+    }
+    const currentCode = String(row.部件编号 || '').trim()
+    if (currentCode) return currentCode
+
+    const existingRow = (gyDetailFormData.工艺列表 || []).find(
+      (r) => r !== row && String(r.部件名称 || '').trim() === partName && String(r.部件编号 || '').trim()
+    )
+    if (existingRow) {
+      row.部件编号 = String(existingRow.部件编号).trim()
+      return row.部件编号
+    }
+
+    let maxCode = 0
+    ;(gyDetailFormData.工艺列表 || []).forEach((r) => {
+      const code = parseInt(String(r.部件编号 || ''), 10)
+      if (!isNaN(code) && code > maxCode) {
+        maxCode = code
+      }
+    })
+    row.部件编号 = String(maxCode + 1).padStart(2, '0')
+    return row.部件编号
+  }
+
+  const handleUpdateProcess = async (row, options = {}) => {
+    const silent = !!options.silent
     const orderNo = String(gyDetailFormData.订单编号 || '').trim()
-    if (!orderNo) return
-    if (!isGyDetailRowChanged(row)) return
+    if (!orderNo) return false
+    if (!isGyDetailRowChanged(row)) return false
     
     if (row._gyDetailSaving) {
-      return
+      return false
     }
 
     const buJianMingCheng = String(row.部件名称 || '').trim()
@@ -6520,7 +6662,17 @@ const sortTableData = ref([])
     const daGongXu = String(row.大工序 || '').trim()
     const requiredReady = !!(buJianMingCheng && gongXuMingCheng && daGongXu)
 
+    // 有部件名称时提交前补齐部件编号,避免偶发「部件编号不能为空」
+    if (buJianMingCheng) {
+      ensureGyDetailPartCode(row)
+    }
+
     const runUpdate = async () => {
+      const partCode = ensureGyDetailPartCode(row) || String(row.部件编号 || '').trim()
+      if (buJianMingCheng && !partCode) {
+        if (!silent) ElMessage.warning('部件编号不能为空,请重新填写部件名称')
+        return false
+      }
       const result = await UpdateProcess({
         id: row.id,
         process_name: row.工序名称 || '',
@@ -6532,17 +6684,17 @@ const sortTableData = ref([])
         coefficient: row.难度系数 || '',
         remark: row.备注 || '',
         mod_id: userStore.userInfo.nickName,
-        part_code: row.部件编号 || '',
+        part_code: partCode,
         part_name: row.部件名称 || '',
         process_code: row.工序编号 || '',
       })
       if (result.code === 0) {
         row._gyDetailNew = false
         row._gyDetailOrigin = snapshotGyDetailRow(row)
-        ElMessage.success('修改成功')
+        if (!silent) ElMessage.success('修改成功')
         return true
       }
-      ElMessage.error(result.msg || '修改失败')
+      if (!silent) ElMessage.error(result.msg || '修改失败')
       return false
     }
 
@@ -6551,29 +6703,36 @@ const sortTableData = ref([])
       if (row.id) {
         row._gyDetailSaving = true
         row._gyDetailNew = false
-        await runUpdate()
-        return
+        return await runUpdate()
       }
 
       // 无 id:需三个必填齐全才能新增
       if (!requiredReady) {
-        if (gyDetailLastEditingField.value === '工序名称' || gyDetailLastEditingField.value === '部件名称') {
+        if (!silent && (gyDetailLastEditingField.value === '工序名称' || gyDetailLastEditingField.value === '部件名称')) {
           ElMessage.warning('部件名称、工序名称、生产工序三个字段都必须填写')
         }
-        return
+        return false
+      }
+
+      const partCode = ensureGyDetailPartCode(row)
+      if (!partCode) {
+        if (!silent) ElMessage.warning('部件编号不能为空,请重新填写部件名称')
+        return false
       }
 
       row._gyDetailSaving = true
 
       const addRes = await AddProcess({
         workorder: orderNo,
-        part_code: row.部件编号 || '',
+        part_code: partCode,
         part_name: row.部件名称 || '',
         process_name: row.工序名称 || '',
         process_code: String(row.工序编号 ?? ''),
         big_process: row.大工序 || '',
         standard_hour: row.秒 || '',
+        standard_minutes: row.分 || '',
         standard_score: row.定额分 || '',
+        money: row.金额 || '',
         coefficient: row.难度系数 || '',
         remark: row.备注 || '',
         sys_id: userStore.userInfo?.nickName || '',
@@ -6586,22 +6745,22 @@ const sortTableData = ref([])
           await bindGyDetailRowIdByProcessCode(row)
         }
         row._gyDetailOrigin = snapshotGyDetailRow(row)
-        ElMessage.success('保存成功')
-        return
+        if (!silent) ElMessage.success('保存成功')
+        return true
       }
 
       // 工序已在库中(例如必填项保存时已新增):改为修改
       if (isGyProcessCodeExistsError(addRes)) {
         const bound = await bindGyDetailRowIdByProcessCode(row)
         if (bound && row.id) {
-          await runUpdate()
-          return
+          return await runUpdate()
         }
       }
 
-      ElMessage.error(addRes?.msg || '保存失败')
+      if (!silent) ElMessage.error(addRes?.msg || '保存失败')
+      return false
     } catch {
-      /* 忽略 */
+      return false
     } finally {
       row._gyDetailSaving = false
     }
@@ -7868,6 +8027,12 @@ const sortTableData = ref([])
 	text-align: center;
 	color: var(--el-text-color-regular);
   }
+  /* 去掉「按 Delete 键可删除」及角标关闭,仅保留 picture-card 悬停放大/垃圾桶 */
+  .add-order-upload :deep(.el-icon--close-tip),
+  .add-order-upload :deep(.el-upload-list__item .el-icon--close),
+  .add-order-upload :deep(.el-upload-list__item-status-label) {
+	display: none !important;
+  }
   .add-order-bottom-row {
 	margin-top: 4px;
   }
@@ -7966,6 +8131,23 @@ const sortTableData = ref([])
   .mab{
 	margin-bottom: 5px;
   }
+  .fj-col-header {
+	display: inline-flex;
+	align-items: center;
+	gap: 2px;
+	cursor: help;
+  }
+  .fj-col-help {
+	font-size: 14px;
+	color: #909399;
+  }
+  /* 上下表头统一浅色 */
+  :deep(.el-table) {
+	--el-table-header-bg-color: #fafafa;
+  }
+  :deep(.el-table__header th.el-table__cell) {
+	background-color: #fafafa !important;
+  }
   ::v-deep(.input_style .el-input__inner) {  
 	font-size: 18px; 
   }  
@@ -7974,8 +8156,9 @@ const sortTableData = ref([])
   }
   
   ::v-deep(.el-upload-list--picture-card) {  
-	  margin-left: 100px;
+	  margin-left: 0;
   }
+  ::v-deep(.add-order-upload .el-upload__input),
   ::v-deep(.import-process-upload .el-upload__input) {
     display: none !important;
   }
@@ -8445,20 +8628,42 @@ const sortTableData = ref([])
     --vxe-table-row-current-background-color: #ff80ff;
     --vxe-table-row-hover-current-background-color: #ff80ff;
     --vxe-ui-table-row-current-background-color: #ff80ff;
-    --vxe-table-row-height: 28px;
-    font-size: 14px;
+    --vxe-table-row-height: 24px;
+    --vxe-table-cell-padding-left: 4px;
+    --vxe-table-cell-padding-right: 4px;
+    font-size: 13px;
   }
 
   :deep(.gy-detail-vxe-table .vxe-table--body-wrapper .vxe-body--row),
-  :deep(.gy-detail-vxe-table .vxe-table--header-wrapper .vxe-header--row) {
-    height: 28px;
-    line-height: 28px;
+  :deep(.gy-detail-vxe-table .vxe-table--header-wrapper .vxe-header--row),
+  :deep(.gy-detail-vxe-table .vxe-table--footer-wrapper .vxe-footer--row) {
+    height: 24px !important;
+    line-height: 24px !important;
   }
 
+  :deep(.gy-detail-vxe-table .vxe-header--column),
+  :deep(.gy-detail-vxe-table .vxe-body--column),
+  :deep(.gy-detail-vxe-table .vxe-footer--column),
   :deep(.gy-detail-vxe-table .vxe-table--body-wrapper .vxe-body--cell),
-  :deep(.gy-detail-vxe-table .vxe-table--header-wrapper .vxe-header--cell) {
-    font-size: 14px;
-    padding: 0 8px;
+  :deep(.gy-detail-vxe-table .vxe-table--header-wrapper .vxe-header--cell),
+  :deep(.gy-detail-vxe-table .vxe-table--footer-wrapper .vxe-footer--cell) {
+    font-size: 13px;
+    height: 24px !important;
+    line-height: 24px !important;
+    padding: 0 4px !important;
+  }
+
+  :deep(.gy-detail-vxe-table .col--checkbox .vxe-cell),
+  :deep(.gy-detail-vxe-table .col--checkbox) {
+    text-align: center !important;
+    justify-content: center !important;
+  }
+
+  :deep(.gy-detail-vxe-table .col--checkbox .vxe-cell--checkbox),
+  :deep(.gy-detail-vxe-table .col--checkbox .vxe-checkbox--icon),
+  :deep(.gy-detail-vxe-table .col--checkbox .vxe-cell--title) {
+    margin-left: auto !important;
+    margin-right: auto !important;
   }
 
   .gy-detail-table-wrap ::-webkit-scrollbar,
@@ -8509,24 +8714,29 @@ const sortTableData = ref([])
   }
 
   .gy-detail-cell {
-    min-height: 24px;
-    line-height: 24px;
-    padding: 0 6px;
+    min-height: 22px;
+    height: 22px;
+    line-height: 22px;
+    padding: 0 4px;
     cursor: text;
     border-radius: 2px;
     overflow: hidden;
     text-overflow: ellipsis;
     white-space: nowrap;
+    text-align: center;
     color: var(--el-text-color-primary);
     box-sizing: border-box;
   }
 
   .gy-detail-cell--wide {
-    white-space: normal;
-    line-height: 1.4;
-    min-height: 28px;
+    white-space: nowrap;
+    line-height: 22px;
+    min-height: 22px;
+    height: 22px;
     display: flex;
     align-items: center;
+    justify-content: flex-start;
+    text-align: left;
   }
 
   .gy-detail-cell:hover {
@@ -8548,15 +8758,31 @@ const sortTableData = ref([])
   }
 
   .gy-detail-vxe-table .gy-detail-cell-select .el-select__wrapper {
-    padding: 0 6px;
+    min-height: 22px !important;
+    height: 22px !important;
+    padding: 0 4px;
     box-shadow: 0 0 0 1px var(--el-color-primary) inset;
   }
 
   .gy-detail-vxe-table .gy-detail-cell-input .el-input__wrapper {
-    padding: 0 6px;
+    min-height: 22px !important;
+    height: 22px !important;
+    padding: 0 4px;
     box-shadow: 0 0 0 1px var(--el-color-primary) inset;
   }
 
+  .gy-detail-vxe-table .gy-detail-cell-input .el-input__inner {
+    height: 22px !important;
+    line-height: 22px !important;
+  }
+
+  :deep(.gy-detail-vxe-table .el-button--small) {
+    --el-button-size: 20px;
+    height: 20px;
+    padding: 0 6px;
+    font-size: 12px;
+  }
+
   .gy-detail-vxe-table .vxe-body--row.row--current .gy-detail-cell:hover {
     background: rgba(255, 255, 255, 0.35);
   }

+ 57 - 2
src/view/yunyin/shengchanguanli/yangyipihe.vue

@@ -30,8 +30,34 @@
 							:show-overflow-tooltip="true"
 							style="width: 100%;height: 65vh" border  :data="tableData1" :row-key="orderTableRowKey" @row-click="tableRowClick" @selection-change="handleSelectionChange">
 						<el-table-column   align="center" type="selection" width="40"/>
-						<el-table-column   align="center" label="技术附件" prop="status"  width="85" />
-						<el-table-column   align="center" label="订单附件" prop="orderstatus"  width="85" />
+						<el-table-column align="center" prop="status" width="100">
+							<template #header>
+								<el-tooltip
+									content='「技术附件」列显示 “*” 表示该订单尚未上传对应附件'
+									placement="top"
+									effect="dark"
+								>
+									<span class="fj-col-header">
+										技术附件
+										<el-icon class="fj-col-help"><QuestionFilled /></el-icon>
+									</span>
+								</el-tooltip>
+							</template>
+						</el-table-column>
+						<el-table-column align="center" prop="orderstatus" width="100">
+							<template #header>
+								<el-tooltip
+									content='「订单附件」列显示 “*” 表示该订单尚未上传对应附件'
+									placement="top"
+									effect="dark"
+								>
+									<span class="fj-col-header">
+										订单附件
+										<el-icon class="fj-col-help"><QuestionFilled /></el-icon>
+									</span>
+								</el-tooltip>
+							</template>
+						</el-table-column>
 						<el-table-column   align="left" label="客户" prop="客户编号"  width="70" />
 						<el-table-column   align="left" label="订单编号" prop="订单编号"  width="115" />
 						<el-table-column   align="left" label="生产款号" prop="生产款号"  width="220" />
@@ -337,9 +363,28 @@
   }
   </script>
   <style scoped>
+  /* 表头/行底与订单资料页一致,去掉偏深的蓝灰底 */
+  :deep(.el-table) {
+	--el-table-header-bg-color: #fafafa;
+	--el-table-tr-bg-color: #ffffff;
+	--el-table-row-hover-bg-color: #f5f7fa;
+	--el-table-selected-row-bg-color: #ffffff;
+  }
+  :deep(.el-table__header th.el-table__cell) {
+	background-color: #fafafa !important;
+  }
+  :deep(.el-table__body tr > td.el-table__cell) {
+	background-color: #ffffff !important;
+  }
   :deep(.el-table__body tr.current-row) > td {
 	background: #ff80ff !important;
   }
+  :deep(.el-table__body tr.hover-row > td.el-table__cell) {
+	background-color: #f5f7fa !important;
+  }
+  :deep(.el-table__body tr.current-row.hover-row > td.el-table__cell) {
+	background: #ff80ff !important;
+  }
   .yangyipihe-attach-tip {
     font-size: 14px;
     height: auto;
@@ -368,6 +413,16 @@
 	padding: 3px !important;
 	font-size: 12px;
   }
+  .fj-col-header {
+	display: inline-flex;
+	align-items: center;
+	gap: 2px;
+	cursor: help;
+  }
+  .fj-col-help {
+	font-size: 14px;
+	color: #909399;
+  }
   .gva-table-box{
 	padding: 0px !important;
   }