m0_70156489 13 hours ago
parent
commit
408e0770e8

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

@@ -681,6 +681,7 @@ export const orderBomList = (params) => {
     params
   })
 }
+
 // 入库、出库、退还单号列表
 export const FabricDetail = (params) => {
   return service({
@@ -1902,3 +1903,70 @@ export const getLogs = (params) => {
   })
 }
 
+// 小组工分上报 - 左侧菜单
+// 大工序工分报工产量跟踪 - 左侧菜单树(支持按大工序筛选)
+export const WorkpointsOutput = (params) => {
+  return service({
+    url: '/mes_server/reporting_work/WorkpointsOutput',
+    method: 'get',
+    params
+  })
+}
+
+// 小组工分上报 - 列表数据
+export const WorkpointsOutputList = (params) => {
+  return service({
+    url: '/mes_server/reporting_work/WorkpointsOutputList',
+    method: 'get',
+    params
+  })
+}
+
+// ==================== 报工相关 ====================
+
+// 获取订单工艺数据
+export const GetOrderProcess = (params) => {
+  return service({
+    url: '/mes_server/reporting_work/GetOrderProcess',
+    method: 'get',
+    params
+  })
+}
+
+// 获取车缝工序详情
+export const GetCarProcess = (params) => {
+  return service({
+    url: '/mes_server/reporting_work/GetCarProcess',
+    method: 'get',
+    params
+  })
+}
+
+// ==================== 手工新增工分报工 ====================
+// 手工新增工分报工 - 左侧菜单
+export const GetManualReportingLeft = (params) => {
+  return service({
+    url: '/mes_server/reporting_work/GetManualReportingLeft',
+    method: 'get',
+    params
+  })
+}
+
+// 手工新增工分报工 - 列表
+export const GetManualReportingList = (params) => {
+  return service({
+    url: '/mes_server/reporting_work/GetManualReportingList',
+    method: 'get',
+    params
+  })
+}
+
+// 手工新增工分报工 - 新增
+export const AddManualReporting = (data) => {
+  return service({
+    url: '/mes_server/reporting_work/AddManualReporting',
+    method: 'post',
+    data
+  })
+}
+

+ 541 - 0
src/view/SalaryManage/GroupWorkpointsOutput.vue

@@ -0,0 +1,541 @@
+<template>
+  <div class="yunyin-hr-split salary-list-page">
+    <layout>
+      <layout-header class="yunyin-page-header">
+        <div>
+          <el-form
+            class="demo-form-inline"
+            @keyup.enter="applySearch"
+          >
+            <el-form-item>
+              <el-input
+                v-model="searchInput"
+                placeholder="查询订单编号、员工编号或员工姓名"
+                clearable
+                class="search"
+                style="width: 260px"
+              />
+              <el-button type="primary" icon="Search" class="bt" @click="applySearch">查询</el-button>
+            </el-form-item>
+          </el-form>
+        </div>
+      </layout-header>
+
+      <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>
+            <div class="salary-tree-scroll">
+              <el-tree
+                ref="menuTreeRef"
+                class="salary-menu-tree"
+                :data="menuTreeData"
+                :props="treeProps"
+                node-key="id"
+                highlight-current
+                :default-expanded-keys="menuDefaultExpandedKeys"
+                :current-node-key="currentMenuKey"
+                :expand-on-click-node="true"
+                @node-click="onMenuNodeClick"
+              >
+                <template #default="{ data }">
+                  <span class="tree-node-label">{{ data.label }}</span>
+                </template>
+              </el-tree>
+            </div>
+            <el-empty
+              v-if="!menuLoading && !menuTreeData.length"
+              class="salary-tree-empty"
+              description="暂无报工菜单数据"
+              :image-size="72"
+            />
+          </div>
+        </layout-sider>
+
+        <layout-content>
+          <el-main class="yunyin-main-block">
+            <div class="gva-table-box">
+              <el-table
+                v-if="hasQueryCondition"
+                v-loading="listLoading"
+                :data="pagedRows"
+                border
+                row-key="rowKey"
+                stripe
+                class="salary-pivot-table"
+                style="width: 100%; height: calc(100vh - 240px)"
+                :row-style="{ height: '30px' }"
+                :header-row-style="{ height: '28px' }"
+                :header-cell-style="{ textAlign: 'center', padding: '2px 0' }"
+                :cell-style="{ textAlign: 'center', padding: '2px 0' }"
+                :show-overflow-tooltip="true"
+                show-summary
+                :summary-method="getSummaries"
+              >
+                <el-table-column label="订单编号" prop="work_order" width="110" align="center" fixed="left" show-overflow-tooltip />
+                <el-table-column label="生产款号" prop="款号" width="160" align="center" show-overflow-tooltip />
+                <el-table-column label="款式" prop="款式" width="110" align="center" show-overflow-tooltip />
+                <el-table-column label="部件编号" prop="部件编号" width="90" align="center" show-overflow-tooltip />
+                <el-table-column label="部件名称" prop="部件名称" width="90" align="center" show-overflow-tooltip />
+                <el-table-column label="小组" prop="sys_id" width="100" align="center" show-overflow-tooltip />
+                <el-table-column label="员工编号" prop="员工编号" width="90" align="center" show-overflow-tooltip />
+                <el-table-column label="员工姓名" prop="staff_name" width="90" align="center" show-overflow-tooltip />
+                <el-table-column label="工序号" prop="process_code" width="70" align="center" show-overflow-tooltip />
+                <el-table-column label="工序名称" prop="process_name" width="140" align="center" show-overflow-tooltip />
+                <el-table-column label="工分" prop="工分" width="80" align="center" show-overflow-tooltip />
+                <el-table-column label="工资" prop="工资" width="80" align="center" show-overflow-tooltip />
+                <el-table-column label="报工数量" prop="number" width="90" align="center" show-overflow-tooltip />
+                <el-table-column label="报工日期" prop="sys_rq" width="160" align="center" show-overflow-tooltip />
+                <el-table-column label="标准工时" prop="标准工时" width="90" align="center" show-overflow-tooltip />
+                <el-table-column label="标准总工时" prop="标准总工时" width="100" align="center" show-overflow-tooltip />
+                <el-table-column label="设备编号" prop="设备编号" width="90" align="center" show-overflow-tooltip />
+                <template #empty>
+                  <el-empty v-if="!listLoading" description="暂无数据" :image-size="100" />
+                </template>
+              </el-table>
+              <div v-if="hasQueryCondition && !listLoading" class="gva-pagination">
+                <el-pagination
+                  v-model:current-page="page"
+                  v-model:page-size="pageSize"
+                  :page-sizes="[20, 50, 100, 200]"
+                  :total="total"
+                  layout="total, sizes, prev, pager, next, jumper"
+                  background
+                />
+              </div>
+              <div v-else-if="!listLoading" class="salary-main-placeholder">
+                <el-empty description="请从左侧选择大工序、机台编号和报工日期" :image-size="120" />
+              </div>
+            </div>
+          </el-main>
+        </layout-content>
+      </layout>
+    </layout>
+  </div>
+</template>
+
+<script setup>
+import { Layout, LayoutHeader, LayoutSider, LayoutContent } from '@arco-design/web-vue'
+import { ref, computed, onMounted, watch, nextTick } from 'vue'
+import { ElMessage } from 'element-plus'
+import { WorkpointsOutput, WorkpointsOutputList } from '@/api/mes/job'
+
+defineOptions({ name: 'GroupWorkpointsOutput' })
+
+// 生产工序排序
+const treeProps = { label: 'label', children: 'children' }
+const menuTreeRef = ref(null)
+const menuLoading = ref(false)
+const menuTreeData = ref([])
+const menuDefaultExpandedKeys = ref([])
+const currentMenuKey = ref('')
+
+const listQuery = ref({ majorprocess: '', machine: '', sys_rq: '' })
+const searchInput = ref('')
+const searchKeywordApplied = ref('')
+
+const hasQueryCondition = computed(
+  () =>
+    !!String(listQuery.value.majorprocess || '').trim() ||
+    !!String(listQuery.value.machine || '').trim() ||
+    !!String(listQuery.value.sys_rq || '').trim() ||
+    !!String(searchKeywordApplied.value || '').trim()
+)
+
+const listLoading = ref(false)
+const tableData = ref([])
+const page = ref(1)
+const pageSize = ref(50)
+const total = ref(0)
+
+const pagedRows = computed(() => {
+  const start = (page.value - 1) * pageSize.value
+  return tableData.value.slice(start, start + pageSize.value)
+})
+
+// 报工数量合计
+const totalReportQty = computed(() => {
+  const sum = tableData.value.reduce((acc, row) => {
+    const v = Number(row['报工数量'])
+    return acc + (isNaN(v) ? 0 : v)
+  }, 0)
+  return Number(sum).toFixed(2)
+})
+
+// 工分合计
+const totalWorkPoints = computed(() => {
+  const sum = tableData.value.reduce((acc, row) => {
+    const v = Number(row['工分'])
+    return acc + (isNaN(v) ? 0 : v)
+  }, 0)
+  return Number(sum).toFixed(2)
+})
+
+// 工资合计
+const totalSalary = computed(() => {
+  const sum = tableData.value.reduce((acc, row) => {
+    const v = Number(row['工资'])
+    return acc + (isNaN(v) ? 0 : v)
+  }, 0)
+  return Number(sum).toFixed(2)
+})
+
+// 标准工时合计
+const totalStandardHours = computed(() => {
+  const sum = tableData.value.reduce((acc, row) => {
+    const v = Number(row['标准工时'])
+    return acc + (isNaN(v) ? 0 : v)
+  }, 0)
+  return Number(sum).toFixed(2)
+})
+
+// 标准总工时合计
+const totalProductionHours = computed(() => {
+  const sum = tableData.value.reduce((acc, row) => {
+    const v = Number(row['标准总工时'])
+    return acc + (isNaN(v) ? 0 : v)
+  }, 0)
+  return Number(sum).toFixed(2)
+})
+
+// 表格合计行:只在报工数量列下方显示合计
+const getSummaries = (param) => {
+  const { columns, data } = param
+  const sums = []
+  columns.forEach((column, index) => {
+    if (index === 0) {
+      sums[index] = '合计'
+      return
+    }
+    if (column.property === 'number') {
+      const sum = data.reduce((acc, row) => {
+        const v = Number(row['number'] ?? row['报工数量'] ?? 0)
+        return acc + (isNaN(v) ? 0 : v)
+      }, 0)
+      sums[index] = Number(sum).toFixed(2)
+    } else {
+      sums[index] = ''
+    }
+  })
+  return sums
+}
+
+watch([tableData, pageSize], () => {
+  const n = tableData.value.length
+  const maxPage = n === 0 ? 1 : Math.max(1, Math.ceil(n / pageSize.value))
+  if (page.value > maxPage) page.value = maxPage
+})
+watch(pageSize, () => { page.value = 1 })
+// 监听查询条件变化,自动重新加载列表
+watch([listQuery, searchKeywordApplied], () => {
+  if (!hasQueryCondition.value) {
+    tableData.value = []
+    total.value = 0
+    return
+  }
+  page.value = 1
+  loadList()
+}, { deep: true })
+
+// 加载三级菜单树:大工序 → 机台编号 → 上报日期
+async function loadLeftMenu() {
+  menuLoading.value = true
+  try {
+    const res = await WorkpointsOutput()
+    console.log('[GroupWorkpointsOutput] WorkpointsOutput 返回:', res)
+    if (res?.code === 0) {
+      const data = res.data
+      // 处理对象结构 {车缝: [...], 裁剪: [...]} 转为数组
+      if (Array.isArray(data)) {
+        menuTreeData.value = data
+      } else if (data && typeof data === 'object') {
+        // 转为 [{label: '车缝', children: [...]}, ...]
+        menuTreeData.value = Object.entries(data).map(([label, children]) => ({
+          label,
+          id: `proc-${label}`,
+          children: children || [],
+        }))
+      } else {
+        menuTreeData.value = []
+      }
+      // 默认展开第一级
+      if (menuTreeData.value.length > 0) {
+        menuDefaultExpandedKeys.value = menuTreeData.value.map(n => n.id)
+      }
+    } else {
+      menuTreeData.value = []
+    }
+  } catch (e) {
+    console.error('[GroupWorkpointsOutput] WorkpointsOutput 异常:', e)
+    ElMessage.error('加载左侧菜单失败')
+    menuTreeData.value = []
+  } finally {
+    menuLoading.value = false
+  }
+}
+
+// 点击节点查询列表(仅叶子节点日期可查询)
+function onMenuNodeClick(data, node) {
+  // 必须是叶子节点(无children)且有sys_rq才查询
+  if (data.children?.length || !data.sys_rq) return
+
+  // node.parent.data 是机台节点,node.parent.parent.data 是一级工序节点
+  const parentNode = node?.parent?.data
+  const grandParentNode = node?.parent?.parent?.data
+
+  const majorprocess = grandParentNode?.label || parentNode?.majorprocess || ''
+  const machine = parentNode?.sys_sbID || parentNode?.machine || ''
+
+  currentMenuKey.value = data.id
+  listQuery.value = {
+    majorprocess,
+    machine,
+    sys_rq: data.sys_rq || '',
+  }
+  // page.value = 1 由 watch listQuery 自动处理
+}
+
+// 关键词搜索
+const applySearch = () => {
+  if (!searchInput.value?.trim()) {
+    ElMessage.warning('请输入查询关键词')
+    return
+  }
+  searchKeywordApplied.value = searchInput.value
+  listQuery.value = { majorprocess: '', machine: '', sys_rq: '' }
+  currentMenuKey.value = ''
+  // loadList() 由 watch(searchKeywordApplied) 自动处理
+}
+
+async function loadList() {
+  if (!hasQueryCondition.value) {
+    tableData.value = []
+    total.value = 0
+    return
+  }
+  listLoading.value = true
+  try {
+    const params = {
+      page: page.value,
+      limit: pageSize.value,
+    }
+
+    if (searchKeywordApplied.value) {
+      // 关键词搜索模式(后端需支持 search 参数)
+      params.search = searchKeywordApplied.value
+    } else {
+      // 菜单选择模式
+      params.majorprocess = listQuery.value.majorprocess
+      params.machine = listQuery.value.machine
+      params.date = listQuery.value.sys_rq
+    }
+
+    console.log('[GroupWorkpointsOutput] loadList 参数:', params)
+    const res = await WorkpointsOutputList(params)
+    console.log('[GroupWorkpointsOutput] WorkpointsOutputList 返回:', res)
+    if (res?.code === 0) {
+      const list = res.data?.list || res.data || []
+      tableData.value = list.map((item, index) => ({
+        ...item,
+        rowKey: item.id || `row-${index}`,
+      }))
+      total.value = Number(res.data?.total ?? 0) || 0
+    } else {
+      tableData.value = []
+      total.value = 0
+    }
+  } catch (error) {
+    console.error('[GroupWorkpointsOutput] loadList 异常:', error)
+    tableData.value = []
+    total.value = 0
+  } finally {
+    listLoading.value = false
+  }
+}
+
+onMounted(() => {
+  loadLeftMenu()
+})
+</script>
+
+<style scoped>
+.yunyin-hr-split.salary-list-page {
+  display: flex !important;
+  flex-direction: column !important;
+  box-sizing: border-box !important;
+  width: 100%;
+  max-width: 100%;
+  height: calc(100vh - 140px);
+  overflow: hidden !important;
+  background: var(--el-bg-color, #fff);
+}
+.salary-list-page > :deep(.arco-layout) {
+  flex: 1;
+  min-height: 0;
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+}
+.salary-list-page :deep(.arco-layout-has-sider) {
+  flex: 1;
+  min-height: 0;
+  overflow: hidden;
+  align-items: stretch !important;
+}
+.yunyin-page-header {
+  padding: 6px 16px;
+  background: #fff;
+  border-bottom: 1px solid var(--el-border-color-lighter, #e4e7ed);
+  display: flex;
+  align-items: center;
+  flex-shrink: 0;
+}
+.salary-list-body {
+  flex: 1;
+  min-height: 0;
+  overflow: hidden;
+}
+.salary-list-page :deep(.arco-layout-sider) {
+  background: #fff !important;
+  border-right: 1px solid var(--el-border-color-lighter);
+  align-self: stretch !important;
+  height: auto !important;
+  min-height: 100%;
+  margin: 0 !important;
+  display: flex;
+  flex-direction: column;
+}
+.salary-list-page :deep(.salary-list-sider.arco-layout-sider) {
+  padding-bottom: 0 !important;
+}
+.salary-list-page :deep(.arco-layout-sider-children) {
+  padding: 0 !important;
+  height: 100% !important;
+  min-height: 100%;
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+  box-sizing: border-box;
+}
+.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;
+  overflow: hidden;
+  display: flex;
+  flex-direction: column;
+  background: var(--el-bg-color, #fff);
+}
+.salary-list-page .staff-tree-panel.salary-tree-panel-fill {
+  min-height: 100% !important;
+  max-height: none !important;
+  height: 100%;
+  flex: 1 1 auto;
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+  box-sizing: border-box;
+  padding: 10px 6px 10px 10px;
+  background: #fff;
+}
+.salary-tree-panel-fill {
+  min-height: 100% !important;
+  height: 100%;
+}
+.salary-tree-scroll {
+  flex: 1;
+  overflow-y: auto;
+  min-height: 0;
+}
+.salary-tree-h3 {
+  font-size: 13px;
+  font-weight: 700;
+  margin: 0 0 6px;
+  color: var(--el-text-color-primary, #303133);
+  padding: 0 4px;
+}
+.salary-tree-empty {
+  padding: 12px 0;
+}
+.yunyin-main-block {
+  box-sizing: border-box;
+  flex: 1;
+  min-height: 0;
+  overflow: hidden;
+  display: flex;
+  flex-direction: column;
+  background: var(--el-bg-color, #fff);
+  --el-main-padding: 0 12px 12px 0;
+  padding: var(--el-main-padding) !important;
+}
+.gva-table-box {
+  flex: 1;
+  min-height: 0;
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+}
+.gva-pagination {
+  flex-shrink: 0;
+  margin-top: 8px;
+  display: flex;
+  justify-content: flex-end;
+}
+.salary-main-placeholder {
+  flex: 1 1 0;
+  min-height: 200px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  background: var(--el-bg-color, #fff);
+}
+:deep(.salary-menu-tree .el-tree-node__content) {
+  color: var(--el-text-color-regular, #606266);
+  font-weight: normal;
+}
+:deep(.salary-menu-tree .el-tree-node__content:hover) {
+  background-color: var(--el-fill-color, #f5f7fa) !important;
+}
+:deep(.salary-menu-tree .el-tree-node.is-current > .el-tree-node__content) {
+  color: var(--el-color-danger, #f56c6c) !important;
+  font-weight: 600;
+}
+:deep(.salary-menu-tree .el-tree-node.is-current > .el-tree-node__content .tree-node-label) {
+  color: var(--el-color-danger, #f56c6c) !important;
+}
+.salary-pivot-table {
+  --el-table-current-row-bg-color: #ff80ff;
+}
+.salary-pivot-table :deep(.el-table__body tr.current-row) > td {
+  background: #ff80ff !important;
+}
+.salary-pivot-table :deep(.el-table__body tr.el-table__row.current-row:hover > td.el-table__cell) {
+  background: #ff80ff !important;
+}
+.salary-pivot-table :deep(.el-table__footer .cell) {
+  font-size: 14px;
+  color: var(--el-text-color-regular);
+}
+.salary-pivot-table :deep(.el-table__footer .cell strong) {
+  color: #f56c6c;
+  font-weight: 600;
+}
+.salary-table-footer {
+  display: flex;
+  justify-content: space-around;
+  padding: 8px 16px;
+  font-size: 14px;
+  color: var(--el-text-color-regular);
+  background: #f5f7fa;
+  border: 1px solid var(--el-border-color);
+  border-top: none;
+}
+.salary-table-footer strong {
+  color: #f56c6c;
+  font-weight: 600;
+  margin-left: 4px;
+}
+</style>

+ 781 - 0
src/view/SalaryManage/ManualReporting.vue

@@ -0,0 +1,781 @@
+<template>
+  <div class="manual-reporting-page">
+    <layout>
+      <layout-header class="page-header">
+        <div class="header-left">
+          <el-form @keyup.enter="handleSearch">
+            <el-form-item>
+              <el-input
+                v-model="searchKeyword"
+                placeholder="搜索订单/款号/姓名/工号"
+                clearable
+                style="width: 200px"
+                @clear="handleSearch"
+              />
+              <el-button type="primary" icon="Search" @click="handleSearch">查询</el-button>
+              <el-button type="success" icon="Plus" @click="handleAdd">新增报工</el-button>
+            </el-form-item>
+          </el-form>
+        </div>
+      </layout-header>
+
+      <layout class="page-body">
+        <layout-sider :resize-directions="['right']" :width="220" class="left-sider">
+          <div v-loading="menuLoading" class="menu-tree-container">
+            <h3 class="tree-title">报工日期</h3>
+            <div class="tree-scroll">
+              <el-tree
+                ref="menuTreeRef"
+                class="menu-tree"
+                :data="menuTreeData"
+                :props="treeProps"
+                node-key="id"
+                highlight-current
+                :default-expanded-keys="defaultExpandedKeys"
+                :expand-on-click-node="false"
+                @node-click="onMenuNodeClick"
+              >
+                <template #default="{ data }">
+                  <span class="tree-node-label">{{ data.label }}</span>
+                </template>
+              </el-tree>
+            </div>
+            <el-empty
+              v-if="!menuLoading && !menuTreeData.length"
+              description="暂无数据"
+              :image-size="60"
+            />
+          </div>
+        </layout-sider>
+
+        <layout-content>
+          <el-main class="main-content">
+            <div class="table-box">
+              <el-table
+                v-loading="listLoading"
+                :data="tableData"
+                border
+                stripe
+                style="width: 100%; height: calc(100vh - 200px)"
+                :header-cell-style="{ textAlign: 'center', background: '#f5f7fa' }"
+                :cell-style="{ textAlign: 'center' }"
+                show-overflow-tooltip
+              >
+                <el-table-column label="订单编号" prop="work_order" width="130" fixed="left" />
+                <el-table-column label="款号" prop="款号" width="100" />
+                <el-table-column label="款式" prop="款式" width="100" />
+                <el-table-column label="部件" prop="部件编号" width="80" />
+                <el-table-column label="员工编号" prop="员工编号" width="90" />
+                <el-table-column label="员工姓名" prop="staff_name" width="90" />
+                <el-table-column label="工序号" prop="process_code" width="70" />
+                <el-table-column label="工序名称" prop="process_name" width="120" />
+                <el-table-column label="工分" prop="工分" width="70" />
+                <el-table-column label="报工数量" prop="number" width="70" />
+                <el-table-column label="标准工时" prop="标准工时" width="90" />
+                <el-table-column label="总工时" prop="标准总工时" width="90" />
+                <el-table-column label="工资" prop="工资" width="80" />
+                <el-table-column label="报工日期" prop="date" width="110" />
+                <el-table-column label="系统时间" prop="sys_rq" width="160" />
+              </el-table>
+
+              <div v-if="tableData.length" class="pagination">
+                <el-pagination
+                  v-model:current-page="page"
+                  v-model:page-size="pageSize"
+                  :page-sizes="[20, 50, 100, 200]"
+                  :total="total"
+                  layout="total, sizes, prev, pager, next, jumper"
+                  background
+                />
+              </div>
+
+              <el-empty v-if="!listLoading && !tableData.length && hasCondition" description="暂无数据" :image-size="80" />
+              <el-empty v-if="!hasCondition" description="请从左侧选择日期查看数据" :image-size="80" />
+            </div>
+          </el-main>
+        </layout-content>
+      </layout>
+    </layout>
+
+    <!-- 新增报工弹窗 -->
+    <el-dialog
+      v-model="addDialogVisible"
+      title="手工新增工分报工(批量)"
+      width="1200px"
+      :close-on-click-modal="false"
+    >
+      <div class="dialog-body">
+        <!-- 顶部筛选区 -->
+        <div class="batch-add-header">
+          <el-row :gutter="20">
+            <el-col :span="8">
+              <el-form-item label="订单编号">
+                <el-select
+                  v-model="batchWorkOrder"
+                  filterable
+                  remote
+                  reserve-keyword
+                  placeholder="输入订单编号或款号搜索"
+                  style="width: 100%"
+                  :remote-method="remoteSearchWorkOrder"
+                  :loading="workOrderLoading"
+                  @change="onBatchWorkOrderChange"
+                >
+                  <el-option
+                    v-for="wo in workOrderOptions"
+                    :key="wo.订单编号"
+                    :label="wo.订单编号 + ' / ' + wo.生产款号 + (wo.款式 ? ' - ' + wo.款式 : '')"
+                    :value="wo.订单编号"
+                  />
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="报工日期">
+                <el-date-picker
+                  v-model="batchDate"
+                  type="date"
+                  placeholder="选择日期"
+                  format="YYYY-MM-DD"
+                  value-format="YYYY-MM-DD"
+                  style="width: 100%"
+                />
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="小组">
+                <el-input v-model="batchSysId" placeholder="小组名称(可选)" />
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </div>
+
+        <!-- 批量表格 -->
+        <div class="batch-table-wrap">
+          <el-table :data="batchTableData" border stripe max-height="400">
+            <el-table-column type="index" label="序号" width="60" />
+            <el-table-column label="员工姓名" width="180">
+              <template #default="{ row }">
+                <el-select
+                  v-model="row.staff_no"
+                  filterable
+                  style="width: 100%"
+                  :filter-method="(q) => filterStaffOptions(q, row)"
+                  @change="(val) => onBatchStaffChange(val, row)"
+                >
+                  <el-option
+                    v-for="staff in row._staffOptions || staffList"
+                    :key="staff.staff_no"
+                    :label="staff.staff_name"
+                    :value="staff.staff_no"
+                  >
+                    <span style="float: left">{{ staff.staff_name }}</span>
+                    <span style="float: right; color: #8492a6; font-size: 12px">{{ staff.staff_no }}</span>
+                  </el-option>
+                </el-select>
+              </template>
+            </el-table-column>
+            <el-table-column label="员工编号" width="100">
+              <template #default="{ row }">
+                <span>{{ row.staff_no }}</span>
+              </template>
+            </el-table-column>
+            <el-table-column label="大工序" width="120">
+              <template #default="{ row }">
+                <el-select v-model="row.majorprocess" style="width: 100%" @change="() => onBatchMajorProcessChange(row)">
+                  <el-option
+                    v-for="proc in majorProcessList"
+                    :key="proc"
+                    :label="proc"
+                    :value="proc"
+                  />
+                </el-select>
+              </template>
+            </el-table-column>
+            <el-table-column label="部件" width="150">
+              <template #default="{ row }">
+                <el-select
+                  v-model="row.part_code"
+                  style="width: 100%"
+                  :disabled="!batchWorkOrder || !row.majorprocess"
+                  @change="() => onBatchPartChange(row)"
+                >
+                  <el-option
+                    v-for="part in partList"
+                    :key="part.部件编号"
+                    :label="part.部件编号 + ' - ' + part.部件名称"
+                    :value="part.部件编号"
+                  />
+                </el-select>
+              </template>
+            </el-table-column>
+            <el-table-column label="工序名称" width="150">
+              <template #default="{ row }">
+                <el-select
+                  v-model="row.process_code"
+                  filterable
+                  clearable
+                  style="width: 100%"
+                  :disabled="!row.part_code"
+                >
+                  <el-option
+                    v-for="proc in processList"
+                    :key="proc.工艺编号"
+                    :label="proc.工艺编号 + ' - ' + proc.工艺名称"
+                    :value="proc.工艺编号"
+                  />
+                </el-select>
+              </template>
+            </el-table-column>
+            <el-table-column label="报工数量" width="100">
+              <template #default="{ row, $index }">
+                <el-input
+                  v-model="row.number"
+                  style="width: 100%"
+                  @input="() => onNumberInput(row, $index)"
+                />
+              </template>
+            </el-table-column>
+            <el-table-column label="备注" min-width="120">
+              <template #default="{ row }">
+                <el-input v-model="row.remark" placeholder="备注" />
+              </template>
+            </el-table-column>
+            <el-table-column label="操作" width="80" fixed="right">
+              <template #default="{ $index }">
+                <el-button type="danger" link @click="removeBatchRow($index)">移除</el-button>
+              </template>
+            </el-table-column>
+          </el-table>
+        </div>
+
+        <div class="batch-add-footer">
+          <span style="color: #909399;">共 {{ batchTableData.length }} 条</span>
+        </div>
+      </div>
+      <template #footer>
+        <el-button @click="addDialogVisible = false">取消</el-button>
+        <el-button type="primary" :loading="submitLoading" @click="submitBatchForm">确认报工</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import { Layout, LayoutHeader, LayoutSider, LayoutContent } from '@arco-design/web-vue'
+import { ref, computed, onMounted, watch } from 'vue'
+import { ElMessage } from 'element-plus'
+import {
+  GetManualReportingLeft,
+  GetManualReportingList,
+  AddManualReporting,
+  WorkOrderList,
+  GetOrderProcess,
+  GetCarProcess
+} from '@/api/mes/job'
+import { getStaffList } from '@/api/yunyin/yunying'
+
+defineOptions({ name: 'ManualReporting' })
+
+const treeProps = { label: 'label', children: 'children' }
+const menuTreeRef = ref(null)
+const menuLoading = ref(false)
+const menuTreeData = ref([])
+const defaultExpandedKeys = ref([])
+
+const searchKeyword = ref('')
+const listLoading = ref(false)
+const tableData = ref([])
+const page = ref(1)
+const pageSize = ref(50)
+const total = ref(0)
+
+const currentDate = ref('')
+const currentStaffNo = ref('')
+
+const hasCondition = computed(() => {
+  return !!currentDate.value || !!searchKeyword.value
+})
+
+// 分页数据
+const pagedData = computed(() => {
+  const start = (page.value - 1) * pageSize.value
+  return tableData.value.slice(start, start + pageSize.value)
+})
+
+watch([currentDate, currentStaffNo, pageSize], () => {
+  page.value = 1
+  loadList()
+})
+
+watch(searchKeyword, () => {
+  page.value = 1
+  if (searchKeyword.value) {
+    loadSearchList()
+  }
+})
+
+// 加载左侧菜单
+async function loadMenu() {
+  menuLoading.value = true
+  try {
+    const res = await GetManualReportingLeft()
+    if (res?.code === 0) {
+      menuTreeData.value = res.data || []
+      if (menuTreeData.value.length > 0) {
+        defaultExpandedKeys.value = menuTreeData.value.map(n => n.id)
+      }
+    }
+  } catch (e) {
+    console.error('加载菜单失败', e)
+  } finally {
+    menuLoading.value = false
+  }
+}
+
+// 点击菜单节点
+function onMenuNodeClick(data) {
+  if (data.children?.length) return // 一级节点不查询
+  currentDate.value = data.sys_rq || ''
+  currentStaffNo.value = data.staff_no || ''
+  searchKeyword.value = ''
+}
+
+// 加载列表
+async function loadList() {
+  if (!currentDate.value) {
+    tableData.value = []
+    total.value = 0
+    return
+  }
+  listLoading.value = true
+  try {
+    const params = { date: currentDate.value }
+    if (currentStaffNo.value) params.staff_no = currentStaffNo.value
+    const res = await GetManualReportingList(params)
+    if (res?.code === 0) {
+      tableData.value = res.data?.list || []
+      total.value = res.data?.total || 0
+    }
+  } catch (e) {
+    console.error('加载列表失败', e)
+  } finally {
+    listLoading.value = false
+  }
+}
+
+// 关键词搜索
+async function loadSearchList() {
+  if (!searchKeyword.value.trim()) return
+  listLoading.value = true
+  try {
+    const res = await GetManualReportingList({ search: searchKeyword.value })
+    if (res?.code === 0) {
+      tableData.value = res.data?.list || []
+      total.value = res.data?.total || 0
+    }
+  } catch (e) {
+    console.error('搜索失败', e)
+  } finally {
+    listLoading.value = false
+  }
+}
+
+function handleSearch() {
+  if (!searchKeyword.value.trim()) {
+    ElMessage.warning('请输入搜索关键词')
+    return
+  }
+  currentDate.value = ''
+  currentStaffNo.value = ''
+  loadSearchList()
+}
+
+// ==================== 新增报工 ====================
+const addDialogVisible = ref(false)
+const addFormRef = ref(null)
+const addForm = ref({
+    staff_name: '',
+    staff_no: '',
+    work_order: '',
+    majorprocess: '',
+    part_code: '',
+    process_code: '',
+    number: '',
+    date: new Date().toISOString().slice(0, 10),
+    sys_id: '',
+    remark: ''
+  })
+
+const addFormRules = {
+  staff_name: [{ required: true, message: '请选择员工', trigger: 'change' }],
+  work_order: [{ required: true, message: '请选择订单', trigger: 'change' }],
+  majorprocess: [{ required: true, message: '请选择大工序', trigger: 'change' }],
+  process_code: [{ required: true, message: '请选择工序', trigger: 'change' }],
+  number: [{ required: true, message: '请输入产量', trigger: 'blur' }],
+  date: [{ required: true, message: '请选择日期', trigger: 'change' }]
+}
+
+// ========== 批量新增相关 ==========
+const submitLoading = ref(false)
+const batchWorkOrder = ref('')
+const batchDate = ref(new Date().toISOString().slice(0, 10))
+const batchSysId = ref('')
+const batchTableData = ref([])
+
+// 员工列表
+const staffList = ref([])
+
+function createEmptyRow() {
+  return {
+    staff_name: '',
+    staff_no: '',
+    majorprocess: '',
+    part_code: '',
+    process_code: '',
+    number: '',
+    remark: '',
+    _staffOptions: null
+  }
+}
+
+function addBatchRow() {
+  batchTableData.value.push(createEmptyRow())
+}
+
+function removeBatchRow(index) {
+  batchTableData.value.splice(index, 1)
+}
+
+function onBatchStaffChange(val, row) {
+  const staff = staffList.value.find(s => s.staff_no === val)
+  if (staff) {
+    row.staff_no = staff.staff_no
+    row.staff_name = staff.staff_name
+  }
+}
+
+// 员工下拉搜索:姓名与编号一起匹配,显示时只显示姓名
+function filterStaffOptions(query, row) {
+  const kw = (query || '').trim().toLowerCase()
+  if (!kw) {
+    row._staffOptions = staffList.value
+    return
+  }
+  row._staffOptions = staffList.value.filter(
+    s => String(s.staff_name || '').toLowerCase().includes(kw) || String(s.staff_no || '').toLowerCase().includes(kw)
+  )
+}
+
+async function onBatchMajorProcessChange(row) {
+  row.part_code = ''
+  row.process_code = ''
+  if (!batchWorkOrder.value || !row.majorprocess) {
+    partList.value = []
+    return
+  }
+  try {
+    const res = await GetOrderProcess({
+      workorder: batchWorkOrder.value,
+      majorprocess: row.majorprocess
+    })
+    if (res?.code === 0) {
+      partList.value = res.data || []
+    }
+  } catch (e) {}
+}
+
+function onBatchWorkOrderChange() {
+  batchTableData.value.forEach(row => {
+    row.part_code = ''
+    row.process_code = ''
+  })
+  partList.value = []
+  processList.value = []
+}
+
+async function onBatchPartChange(row) {
+  row.process_code = ''
+  processList.value = []
+  if (!batchWorkOrder.value || !row.part_code) return
+  try {
+    const res = await GetCarProcess({
+      workorder: batchWorkOrder.value,
+      part_code: row.part_code
+    })
+    if (res?.code === 0) {
+      processList.value = res.data || []
+    }
+  } catch (e) {}
+}
+
+function onNumberInput(row, index) {
+  // 如果是最后一行,输入产量后自动添加一行
+  if (index === batchTableData.value.length - 1 && Number(row.number) > 0) {
+    batchTableData.value.push(createEmptyRow())
+  }
+}
+
+async function submitBatchForm() {
+  // 验证必填字段
+  const validData = batchTableData.value.filter(row =>
+    row.staff_name && row.majorprocess && row.part_code && row.process_code && Number(row.number) > 0
+  )
+
+  if (validData.length === 0) {
+    ElMessage.warning('请至少填写一条完整的数据')
+    return
+  }
+
+  submitLoading.value = true
+  try {
+    const submitData = validData.map(row => ({
+      staff_no: row.staff_no,
+      staff_name: row.staff_name,
+      work_order: batchWorkOrder.value,
+      majorprocess: row.majorprocess,
+      part_code: row.part_code || null,
+      process_code: row.process_code,
+      number: Number(row.number),
+      date: batchDate.value,
+      sys_id: batchSysId.value,
+      remark: row.remark || ''
+    }))
+
+    const res = await AddManualReporting(submitData)
+    if (res?.code === 0) {
+      ElMessage.success(`报工成功,共 ${submitData.length} 条`)
+      addDialogVisible.value = false
+      batchTableData.value = []
+      batchWorkOrder.value = ''
+      batchSysId.value = ''
+      loadMenu()
+      loadList()
+    } else {
+      ElMessage.error(res?.msg || '报工失败')
+    }
+  } catch (e) {
+    ElMessage.error('报工失败')
+  } finally {
+    submitLoading.value = false
+  }
+}
+
+// ========== 原有方法 ==========
+
+// 下拉数据
+const workOrderOptions = ref([])
+const workOrderLoading = ref(false)
+const majorProcessList = ref(['裁剪', '车缝', '后道', '整理', '包装'])
+const partList = ref([])
+const processList = ref([])
+
+// 远程搜索订单
+let workOrderTimer = null
+const remoteSearchWorkOrder = (query) => {
+  clearTimeout(workOrderTimer)
+  if (!query) {
+    workOrderOptions.value = []
+    return
+  }
+  workOrderTimer = setTimeout(async () => {
+    workOrderLoading.value = true
+    try {
+      const res = await WorkOrderList({ search: query, page: 1, limit: 20 })
+      if (res?.code === 0) {
+        workOrderOptions.value = res.data?.data || res.data?.list || []
+      }
+    } catch (e) {
+      console.error('搜索订单失败', e)
+    } finally {
+      workOrderLoading.value = false
+    }
+  }, 300)
+}
+
+async function handleAdd() {
+  addDialogVisible.value = true
+  // 重置批量数据 - 默认10行
+  batchWorkOrder.value = ''
+  batchDate.value = new Date().toISOString().slice(0, 10)
+  batchSysId.value = ''
+  batchTableData.value = Array.from({ length: 10 }, () => createEmptyRow())
+  // 加载员工列表(全部在职员工,用于下拉搜索)
+  loadStaffList()
+}
+
+// 加载员工列表
+async function loadStaffList() {
+  try {
+    const res = await getStaffList({ page: 1, limit: 1000 })
+    if (res?.code === 0) {
+      const list = res.data?.list || []
+      staffList.value = list.map(item => ({
+        staff_no: item.员工编号,
+        staff_name: item.员工姓名
+      }))
+    }
+  } catch (e) {
+    console.error('加载员工列表失败', e)
+  }
+}
+
+function onStaffChange(val) {
+  const staff = staffList.value.find(s => s.staff_name === val)
+  addForm.value.staff_no = staff?.staff_no || ''
+}
+
+async function onWorkOrderChange() {
+  addForm.value.part_code = ''
+  addForm.value.process_code = ''
+  partList.value = []
+  processList.value = []
+  if (!addForm.value.work_order || !addForm.value.majorprocess) return
+  try {
+    const res = await GetOrderProcess({
+      workorder: addForm.value.work_order,
+      majorprocess: addForm.value.majorprocess
+    })
+    if (res?.code === 0) {
+      partList.value = res.data || []
+    }
+  } catch (e) {}
+}
+
+function onPartChange() {
+  addForm.value.process_code = ''
+  processList.value = []
+  if (!addForm.value.work_order || !addForm.value.part_code) return
+  GetCarProcess({
+    workorder: addForm.value.work_order,
+    part_code: addForm.value.part_code
+  }).then(res => {
+    if (res?.code === 0) {
+      processList.value = res.data || []
+    }
+  })
+}
+
+watch(() => addForm.value.majorprocess, () => {
+  addForm.value.part_code = ''
+  addForm.value.process_code = ''
+  partList.value = []
+  processList.value = []
+  if (addForm.value.work_order) onWorkOrderChange()
+})
+
+async function submitAddForm() {
+  try {
+    await addFormRef.value.validate()
+  } catch {
+    return
+  }
+
+  try {
+    const res = await AddManualReporting([{
+      staff_no: addForm.value.staff_no,
+      staff_name: addForm.value.staff_name,
+      work_order: addForm.value.work_order,
+      majorprocess: addForm.value.majorprocess,
+      part_code: addForm.value.part_code || null,
+      process_code: addForm.value.process_code,
+      number: Number(addForm.value.number),
+      date: addForm.value.date,
+      sys_id: addForm.value.sys_id,
+      remark: addForm.value.remark
+    }])
+    if (res?.code === 0) {
+      ElMessage.success('报工成功')
+      addDialogVisible.value = false
+      loadMenu()
+      loadList()
+    } else {
+      ElMessage.error(res?.msg || '报工失败')
+    }
+  } catch (e) {
+    ElMessage.error('报工失败')
+  }
+}
+
+onMounted(() => {
+  loadMenu()
+})
+</script>
+
+<style scoped>
+.manual-reporting-page {
+  display: flex;
+  flex-direction: column;
+  height: calc(100vh - 120px);
+  background: #fff;
+}
+
+.page-header {
+  padding: 8px 16px;
+  background: #fff;
+  border-bottom: 1px solid #e4e7ed;
+  display: flex;
+  align-items: center;
+}
+
+.page-body {
+  flex: 1;
+  min-height: 0;
+  overflow: hidden;
+}
+
+.left-sider {
+  background: #fff;
+  border-right: 1px solid #e4e7ed;
+}
+
+.menu-tree-container {
+  padding: 10px;
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+}
+
+.tree-title {
+  font-size: 13px;
+  font-weight: 600;
+  margin: 0 0 8px;
+  color: #303133;
+}
+
+.tree-scroll {
+  flex: 1;
+  overflow-y: auto;
+}
+
+:deep(.menu-tree .el-tree-node__content) {
+  height: 32px;
+}
+
+:deep(.menu-tree .el-tree-node.is-current > .el-tree-node__content) {
+  background: #f56c6c20;
+  color: #f56c6c;
+}
+
+.main-content {
+  padding: 12px;
+}
+
+.table-box {
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+}
+
+.pagination {
+  margin-top: 12px;
+  display: flex;
+  justify-content: flex-end;
+}
+
+.dialog-body {
+  max-height: 60vh;
+  overflow-y: auto;
+  padding-right: 8px;
+}
+</style>

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

@@ -38,6 +38,22 @@
           <el-button type="primary" icon="search" :loading="workOrderSuggestLoading" @click="handleQueryClick">
             查询
           </el-button>
+          <span class="process-production-search-label" style="margin-left: 16px; margin-right: 8px; color: #606266; font-size: 14px;">选择报工月份:</span>
+          <el-select
+            v-model="searchMonth"
+            placeholder="请选择"
+            style="width: 160px"
+            :clearable="true"
+            :loading="monthOptionsLoading"
+            @change="onSearchMonthChange"
+          >
+            <el-option
+              v-for="m in reportedMonths"
+              :key="m"
+              :label="m"
+              :value="m"
+            />
+          </el-select>
       </el-form>
 
       <el-alert
@@ -49,6 +65,10 @@
         :closable="false"
       />
 
+      <div v-if="false" style="margin-bottom: 8px; color: #909399; font-size: 13px;">
+        当前筛选:{{ searchMonth }} 月
+      </div>
+
       <!-- 订单摘要:新增字段只在此处数组里加一行,key 与 WorkOrderList 返回字段一致 -->
       <el-descriptions
         v-if="!embeddedInTab"
@@ -323,6 +343,16 @@ const emptyDescription = computed(() => {
 })
 
 const searchKeyword = ref('')
+const currentWorkorder = ref('')   // 当前已选中的工单号(月份变化时用它重新查)
+const searchMonth = ref('')
+const reportedMonths = ref([])
+const monthOptionsLoading = ref(false)
+
+/** 月份筛选变化时,用当前已选工单重新查询 */
+const onSearchMonthChange = () => {
+  const w = String(currentWorkorder.value || '').trim()
+  if (w) executeProductionSearch(w)
+}
 const searchWrapRef = ref(null)
 const workOrderDropdownVisible = ref(false)
 
@@ -694,6 +724,13 @@ onMounted(() => {
   document.addEventListener('click', onDocClick)
 })
 
+/** 从 checkProcessProduction 返回中提取已报工月份列表 */
+const fetchReportedMonths = (resData) => {
+  reportedMonths.value = Array.isArray(resData?.已报工月份列表)
+    ? resData.已报工月份列表
+    : []
+}
+
 onUnmounted(() => {
   document.removeEventListener('click', onDocClick)
   clearTimeout(searchInputTimer)
@@ -726,8 +763,9 @@ const onPickWorkOrder = async (item) => {
   if (!no) return
   applyOrderSummaryFromWorkOrder(item, no)
   workOrderDropdownVisible.value = false
-  await executeProductionSearch(no)
+  const res = await executeProductionSearch(no)
   searchKeyword.value = ''
+  fetchReportedMonths(res)
 }
 
 /** 点击查询 / 回车:仅 1 条则直接查,多条则展开下拉;查询前不清空输入框 */
@@ -755,7 +793,8 @@ const loadByWorkorder = async (workorder, process) => {
   if (!w) return
   searchKeyword.value = w
   const p = process !== undefined ? process : props.initialProcess
-  await executeProductionSearch(w, p)
+  const res = await executeProductionSearch(w, p)
+  fetchReportedMonths(res)
 }
 
 const executeProductionSearch = async (orderNo, processFilter = '', sysIdFilter) => {
@@ -775,6 +814,7 @@ const executeProductionSearch = async (orderNo, processFilter = '', sysIdFilter)
     ).trim()
     const params = { workorder, process }
     if (sys_id) params.sys_id = sys_id
+    if (searchMonth.value) params.month = searchMonth.value
 
     const [, res] = await Promise.all([
       loadOrderSummaryFromWorkOrderList(workorder),
@@ -783,25 +823,28 @@ const executeProductionSearch = async (orderNo, processFilter = '', sysIdFilter)
 
     if (res?.code !== 0) {
       processListRaw.value = []
-      pageHint.value = res?.msg || '未找到报工数据'
-      return
+    pageHint.value = res?.msg || '未找到报工数据'
+    return
     }
 
     const data = res.data || {}
+    currentWorkorder.value = workorder  // 记住当前工单号,供月份筛选重查用
     // 与 WorkOrderList 摘要合并:核查接口通常不含客户编号,不能整表覆盖
     applyOrderSummaryFromWorkOrder(data, workorder, { merge: true })
     processListRaw.value = data.工序列表 || []
-    resetAllColumnFilters()
+resetAllColumnFilters()
     activeProcessKey.value = ''
     if (!processListRaw.value.length) {
       pageHint.value = '未找到报工数据'
     }
+    return data
   } catch (error) {
     console.error(error)
     pageHint.value = ''
     ElMessage.error('查询失败,请稍后重试')
     processListRaw.value = []
     resetOrderSummary(workorder)
+    return null
   } finally {
     loading.value = false
   }

+ 389 - 394
src/view/layout/index.vue

@@ -1,394 +1,389 @@
-<template>
-  <el-container class="layout-cont">
-    <el-container :class="[isSider?'openside':'hideside',isMobile ? 'mobile': '']" >
-      <el-row
-        :class="[isShadowBg && isMobile?'bg-black opacity-30 w-full h-full absolute top-0 left-0 z-[1001]':'']"
-        @click="changeShadow()"
-      />
-      <el-aside
-        class="main-cont gva-aside"
-        :style="{width:asideWidth()}"
-		v-if="showLayout=== true"
-      >
-        <div
-          class="min-h-[60px] text-center transition-all duration-300 flex items-center justify-center gap-2"
-          :style="{background: backgroundColor}"
-        >
-          <!-- <img
-            alt
-            class="w-9 h-9 p-1 bg-white rounded-full"
-            :src="`https://www.reachingkayo.com/Uploads/62f88d57efb9d749.jpg`"
-          > -->
-          <img class="w-9 h-9 p-1 bg-white rounded-full" :src="`/src/assets/dacheng.jpg`" alt width="400px" height="400px" />
-             
-          <div
-            v-if="isSider"
-            class="inline-flex font-bold text-1xl"
-            :style="{color:textColor}"
-          >智能服装MES生产管理系统</div>
-        </div>
-        <Aside class="aside" />
-      </el-aside>
-      <!-- 分块滑动功能 -->
-      <el-main :class="{ 'main-cont main-right': yourCondition }">
-        <transition
-          :duration="{ enter: 800, leave: 100 }"
-          mode="out-in"
-          name="el-fade-in-linear"
-        >
-          <div
-            :style="{width: `calc(100% - ${getAsideWidth()})`}"
-            class="fixed top-0 box-border z-50"
-          >
-            <el-row>
-              <el-col>
-                <el-header class="header-cont" v-if="showLayout=== true">
-                  <el-row class="p-0 h-full">
-                    <el-col
-                      :xs="2"
-                      :lg="1"
-                      :md="1"
-                      :sm="1"
-                      :xl="1"
-                      class="z-50 flex items-center pl-3"
-                    >
-                      <div
-                        class="text-black cursor-pointer text-lg leading-5"
-                        @click="totalCollapse"
-                      >
-                        <div
-                          v-if="isCollapse"
-                          class="gvaIcon gvaIcon-arrow-double-right"
-                        />
-                        <div
-                          v-else
-                          class="gvaIcon gvaIcon-arrow-double-left"
-                        />
-                      </div>
-                    </el-col>
-                    <el-col
-                      :xs="10"
-                      :lg="14"
-                      :md="14"
-                      :sm="9"
-                      :xl="14"
-                      :pull="1"
-                      class="flex items-center"
-                    >
-                      <!-- 修改为手机端不显示顶部标签 -->
-                      <el-breadcrumb
-                        v-show="!isMobile"
-                        class="breadcrumb"
-                      >
-                        <el-breadcrumb-item
-                          v-for="item in matched.slice(1,matched.length)"
-                          :key="item.path"
-                        >{{ fmtTitle(item.meta.title,route) }}</el-breadcrumb-item>
-                      </el-breadcrumb>
-                    </el-col>
-                    <el-col
-                      :xs="12"
-                      :lg="9"
-                      :md="9"
-                      :sm="14"
-                      :xl="9"
-                      class="flex items-center justify-end"
-                    >
-                      <div class="mr-1.5 flex items-center">
-                        <Search />
-                        <el-dropdown>
-                          <div class="flex justify-center items-center h-full w-full">
-                            <span class="cursor-pointer flex justify-center items-center">
-                              <CustomPic />
-                              <span
-                                v-show="!isMobile"
-                                style="margin-left: 5px"
-                              >{{ userStore.userInfo.nickName }}</span>
-                              <el-icon>
-                                <arrow-down />
-                              </el-icon>
-                            </span>
-                          </div>
-                          <template #dropdown>
-                            <el-dropdown-menu>
-                              <el-dropdown-item>
-                                <span class="font-bold">
-                                  当前角色:{{ userStore.userInfo.authority.authorityName }}
-                                </span>
-                              </el-dropdown-item>
-                              <template v-if="userStore.userInfo.authorities">
-                                <el-dropdown-item
-                                  v-for="item in userStore.userInfo.authorities.filter(i=>i.authorityId!==userStore.userInfo.authorityId)"
-                                  :key="item.authorityId"
-                                  @click="changeUserAuth(item.authorityId)"
-                                >
-                                  <span>
-                                    切换为:{{ item.authorityName }}
-                                  </span>
-                                </el-dropdown-item>
-                              </template>
-                              <el-dropdown-item icon="avatar">
-                                <div
-                                  class="command-box"
-                                  style="display: flex"
-                                  @click="handleCommand"
-                                >
-                                  <div>指令菜单</div>
-                                  <div style="margin-left: 8px">
-                                    <span class="button">{{ first }}</span>
-                                    +
-                                    <span class="button">K</span>
-                                  </div>
-                                </div>
-                              </el-dropdown-item>
-                              <el-dropdown-item
-                                icon="avatar"
-                                @click="toPerson"
-                              >个人信息</el-dropdown-item>
-                              <el-dropdown-item
-                                icon="reading-lamp"
-                                @click="userStore.LoginOut"
-                              >登 出</el-dropdown-item>
-                            </el-dropdown-menu>
-                          </template>
-                        </el-dropdown>
-                      </div>
-                    </el-col>
-                  </el-row>
-                </el-header>
-              </el-col>
-            </el-row>
-            <!-- 当前面包屑用路由自动生成可根据需求修改 -->
-            <!--
-            :to="{ path: item.path }" 暂时注释不用-->
-            <HistoryComponent ref="layoutHistoryComponent" />
-          </div>
-        </transition>
-        <router-view
-          v-if="reloadFlag"
-          v-slot="{ Component }"
-		 :class="{ 'admin-box': yourCondition }"
-        >
-          <div
-            v-loading="loadingFlag"
-            element-loading-text="正在加载中"
-          >
-            <transition
-              mode="out-in"
-              name="el-fade-in-linear"
-            >
-              <keep-alive :include="routerStore.keepAliveRouters">
-                <component :is="Component" />
-              </keep-alive>
-            </transition>
-          </div>
-        </router-view>
-        <BottomInfo />
-        <setting />
-        <CommandMenu ref="command" />
-      </el-main>
-    </el-container>
-  </el-container>
-</template>
-
-<script setup>
-import Aside from '@/view/layout/aside/index.vue'
-import HistoryComponent from '@/view/layout/aside/historyComponent/history.vue'
-import Search from '@/view/layout/search/search.vue'
-import BottomInfo from '@/view/layout/bottomInfo/bottomInfo.vue'
-import CustomPic from '@/components/customPic/index.vue'
-import CommandMenu from '@/components/commandMenu/index.vue'
-import Setting from './setting/index.vue'
-import { setUserAuthority } from '@/api/user'
-import { emitter } from '@/utils/bus.js'
-import { computed, ref, onMounted, nextTick } from 'vue'
-import { useRouter, useRoute } from 'vue-router'
-import { useRouterStore } from '@/pinia/modules/router'
-import { fmtTitle } from '@/utils/fmtRouterTitle'
-import { useUserStore } from '@/pinia/modules/user'
-defineOptions({
-  name: 'Layout',
-})
-
-const router = useRouter()
-const route = useRoute()
-const routerStore = useRouterStore()
-// 三种窗口适配
-const isCollapse = ref(false)
-const isSider = ref(true)
-const isMobile = ref(false)
-
-const first = ref('')
-const dialogVisible = ref(false)
-const initPage = () => {
-  // 判断当前用户的操作系统
-  if (window.localStorage.getItem('osType') === 'WIN') {
-    first.value = 'Ctrl'
-  } else {
-    first.value = '⌘'
-  }
-  // 当用户同时按下ctrl和k键的时候
-  const handleKeyDown = (e) => {
-    if (e.ctrlKey && e.key === 'k') {
-      // 阻止浏览器默认事件
-      e.preventDefault()
-      handleCommand()
-    }
-  }
-  window.addEventListener('keydown', handleKeyDown)
-
-  const screenWidth = document.body.clientWidth
-  if (screenWidth < 1000) {
-    isMobile.value = true
-    isSider.value = false
-    isCollapse.value = true
-  } else if (screenWidth >= 1000 && screenWidth < 1200) {
-    isMobile.value = false
-    isSider.value = false
-    isCollapse.value = true
-  } else {
-    isMobile.value = false
-    isSider.value = true
-    isCollapse.value = false
-  }
-}
-
-initPage()
-
-const command = ref()
-const handleCommand = () => {
-  command.value.open()
-}
-
-const loadingFlag = ref(false)
-onMounted(() => {
-  // 挂载一些通用的事件
-  emitter.emit('collapse', isCollapse.value)
-  emitter.emit('mobile', isMobile.value)
-  emitter.on('reload', reload)
-  emitter.on('showLoading', () => {
-    loadingFlag.value = true
-  })
-  emitter.on('closeLoading', () => {
-    loadingFlag.value = false
-  })
-  window.onresize = () => {
-    return (() => {
-      initPage()
-      emitter.emit('collapse', isCollapse.value)
-      emitter.emit('mobile', isMobile.value)
-    })()
-  }
-  if (userStore.loadingInstance) {
-    userStore.loadingInstance.close()
-  }
-})
-const showLayout = ref(true)
-const yourCondition = ref(true)
-
-const userStore = useUserStore()
-console.log(userStore.userInfo.authorityId)
-if(userStore.userInfo.authorityId==100002){
-	showLayout.value=false
-	yourCondition.value=false
-}else{
-	yourCondition.value=true
-}
-	
-// console.log(userStore.userInfo.nickName )
-const asideWidth = () => {
-  if (isMobile.value) {
-    return isCollapse.value ? '0px' : '220px'
-  }
-  return isCollapse.value ? '54px' : '220px'
-}
-
-const getAsideWidth = () => {
-  if (isMobile.value) return '0px'
-  return isCollapse.value ? '54px' : '220px'
-}
-
-const textColor = computed(() => {
-  if (userStore.sideMode === 'dark') {
-    return '#fff'
-  } else if (userStore.sideMode === 'light') {
-    return '#191a23'
-  } else {
-    return userStore.baseColor
-  }
-})
-
-const backgroundColor = computed(() => {
-  if (userStore.sideMode === 'dark') {
-    return '#191a23'
-  } else if (userStore.sideMode === 'light') {
-    return '#fff'
-  } else {
-    return userStore.sideMode
-  }
-})
-
-const matched = computed(() => route.meta.matched)
-
-const changeUserAuth = async(id) => {
-  const res = await setUserAuthority({
-    authorityId: id
-  })
-  if (res.code === 0) {
-    window.sessionStorage.setItem('needCloseAll', 'true')
-    window.location.reload()
-  }
-}
-
-const reloadFlag = ref(true)
-let reloadTimer = null
-const reload = async() => {
-  if (reloadTimer) {
-    window.clearTimeout(reloadTimer)
-  }
-  reloadTimer = window.setTimeout(async() => {
-    if (route.meta.keepAlive) {
-      reloadFlag.value = false
-      await nextTick()
-      reloadFlag.value = true
-    } else {
-      const title = route.meta.title
-      router.push({ name: 'Reload', params: { title }})
-    }
-  }, 400)
-}
-
-const isShadowBg = ref(false)
-const totalCollapse = () => {
-  isCollapse.value = !isCollapse.value
-  isSider.value = !isCollapse.value
-  isShadowBg.value = !isCollapse.value
-  emitter.emit('collapse', isCollapse.value)
-}
-
-const toPerson = () => {
-  router.push({ name: 'person' })
-}
-const changeShadow = () => {
-  isShadowBg.value = !isShadowBg.value
-  isSider.value = !!isCollapse.value
-  totalCollapse()
-}
-</script>
-
-<style lang="scss">
-.button {
-  font-size: 12px;
-  color: #666;
-  background:	rgb(250,250,250);
-  width: 25px!important;
-  padding: 4px 8px !important;
-  border: 1px solid #eaeaea;
-  margin-right: 4px;
-  border-radius: 4px;
-}
-:deep .el-overlay {
-  background-color: hsla(0,0%,100%,.9) !important;
-}
-
-</style>
+<template>
+  <el-container class="layout-cont">
+    <el-container :class="[isSider?'openside':'hideside',isMobile ? 'mobile': '']" >
+      <el-row
+        :class="[isShadowBg && isMobile?'bg-black opacity-30 w-full h-full absolute top-0 left-0 z-[1001]':'']"
+        @click="changeShadow()"
+      />
+      <el-aside
+        class="main-cont gva-aside"
+        :style="{width:asideWidth()}"
+		v-if="showLayout=== true"
+      >
+        <div
+          class="min-h-[60px] text-center transition-all duration-300 flex items-center justify-center gap-2"
+          :style="{background: backgroundColor}"
+        >
+          <!-- <img
+            alt
+            class="w-9 h-9 p-1 bg-white rounded-full"
+            :src="`https://www.reachingkayo.com/Uploads/62f88d57efb9d749.jpg`"
+          > -->
+          <img class="w-9 h-9 p-1 bg-white rounded-full" :src="`/src/assets/dacheng.jpg`" alt width="400px" height="400px" />
+             
+          <div
+            v-if="isSider"
+            class="inline-flex font-bold text-1xl"
+            :style="{color:textColor}"
+          >智能服装MES生产管理系统</div>
+        </div>
+        <Aside class="aside" />
+      </el-aside>
+      <!-- 分块滑动功能 -->
+      <el-main :class="{ 'main-cont main-right': yourCondition }">
+        <transition
+          :duration="{ enter: 800, leave: 100 }"
+          mode="out-in"
+          name="el-fade-in-linear"
+        >
+          <div
+            :style="{width: `calc(100% - ${getAsideWidth()})`}"
+            class="fixed top-0 box-border z-50"
+          >
+            <el-row>
+              <el-col>
+                <el-header class="header-cont" v-if="showLayout=== true">
+                  <el-row class="p-0 h-full">
+                    <el-col
+                      :xs="2"
+                      :lg="1"
+                      :md="1"
+                      :sm="1"
+                      :xl="1"
+                      class="z-50 flex items-center pl-3"
+                    >
+                      <div
+                        class="text-black cursor-pointer text-lg leading-5"
+                        @click="totalCollapse"
+                      >
+                        <div
+                          v-if="isCollapse"
+                          class="gvaIcon gvaIcon-arrow-double-right"
+                        />
+                        <div
+                          v-else
+                          class="gvaIcon gvaIcon-arrow-double-left"
+                        />
+                      </div>
+                    </el-col>
+                    <el-col
+                      :xs="10"
+                      :lg="14"
+                      :md="14"
+                      :sm="9"
+                      :xl="14"
+                      :pull="1"
+                      class="flex items-center"
+                    >
+                      <!-- 修改为手机端不显示顶部标签 -->
+                      <el-breadcrumb
+                        v-show="!isMobile"
+                        class="breadcrumb"
+                      >
+                        <el-breadcrumb-item
+                          v-for="item in matched.slice(1,matched.length)"
+                          :key="item.path"
+                        >{{ fmtTitle(item.meta.title,route) }}</el-breadcrumb-item>
+                      </el-breadcrumb>
+                    </el-col>
+                    <el-col
+                      :xs="12"
+                      :lg="9"
+                      :md="9"
+                      :sm="14"
+                      :xl="9"
+                      class="flex items-center justify-end"
+                    >
+                      <div class="mr-1.5 flex items-center">
+                        <Search />
+                        <el-dropdown>
+                          <div class="flex justify-center items-center h-full w-full">
+                            <span class="cursor-pointer flex justify-center items-center">
+                              <CustomPic />
+                              <span
+                                v-show="!isMobile"
+                                style="margin-left: 5px"
+                              >{{ userStore.userInfo.nickName }}</span>
+                              <el-icon>
+                                <arrow-down />
+                              </el-icon>
+                            </span>
+                          </div>
+                          <template #dropdown>
+                            <el-dropdown-menu>
+                              <el-dropdown-item>
+                                <span class="font-bold">
+                                  当前角色:{{ userStore.userInfo.authority.authorityName }}
+                                </span>
+                              </el-dropdown-item>
+                              <template v-if="userStore.userInfo.authorities">
+                                <el-dropdown-item
+                                  v-for="item in userStore.userInfo.authorities.filter(i=>i.authorityId!==userStore.userInfo.authorityId)"
+                                  :key="item.authorityId"
+                                  @click="changeUserAuth(item.authorityId)"
+                                >
+                                  <span>
+                                    切换为:{{ item.authorityName }}
+                                  </span>
+                                </el-dropdown-item>
+                              </template>
+                              <el-dropdown-item icon="avatar">
+                                <div
+                                  class="command-box"
+                                  style="display: flex"
+                                  @click="handleCommand"
+                                >
+                                  <div>指令菜单</div>
+                                  <div style="margin-left: 8px">
+                                    <span class="button">{{ first }}</span>
+                                    +
+                                    <span class="button">K</span>
+                                  </div>
+                                </div>
+                              </el-dropdown-item>
+                              <el-dropdown-item
+                                icon="avatar"
+                                @click="toPerson"
+                              >个人信息</el-dropdown-item>
+                              <el-dropdown-item
+                                icon="reading-lamp"
+                                @click="userStore.LoginOut"
+                              >登 出</el-dropdown-item>
+                            </el-dropdown-menu>
+                          </template>
+                        </el-dropdown>
+                      </div>
+                    </el-col>
+                  </el-row>
+                </el-header>
+              </el-col>
+            </el-row>
+            <!-- 当前面包屑用路由自动生成可根据需求修改 -->
+            <!--
+            :to="{ path: item.path }" 暂时注释不用-->
+            <HistoryComponent ref="layoutHistoryComponent" />
+          </div>
+        </transition>
+        <router-view
+          v-if="reloadFlag"
+          v-slot="{ Component }"
+		 :class="{ 'admin-box': yourCondition }"
+        >
+            <transition
+              mode="out-in"
+              name="el-fade-in-linear"
+            >
+              <keep-alive :include="routerStore.keepAliveRouters">
+                <component :is="Component" />
+              </keep-alive>
+            </transition>
+        </router-view>
+        <BottomInfo />
+        <setting />
+        <CommandMenu ref="command" />
+      </el-main>
+    </el-container>
+  </el-container>
+</template>
+
+<script setup>
+import Aside from '@/view/layout/aside/index.vue'
+import HistoryComponent from '@/view/layout/aside/historyComponent/history.vue'
+import Search from '@/view/layout/search/search.vue'
+import BottomInfo from '@/view/layout/bottomInfo/bottomInfo.vue'
+import CustomPic from '@/components/customPic/index.vue'
+import CommandMenu from '@/components/commandMenu/index.vue'
+import Setting from './setting/index.vue'
+import { setUserAuthority } from '@/api/user'
+import { emitter } from '@/utils/bus.js'
+import { computed, ref, onMounted, nextTick } from 'vue'
+import { useRouter, useRoute } from 'vue-router'
+import { useRouterStore } from '@/pinia/modules/router'
+import { fmtTitle } from '@/utils/fmtRouterTitle'
+import { useUserStore } from '@/pinia/modules/user'
+defineOptions({
+  name: 'Layout',
+})
+
+const router = useRouter()
+const route = useRoute()
+const routerStore = useRouterStore()
+// 三种窗口适配
+const isCollapse = ref(false)
+const isSider = ref(true)
+const isMobile = ref(false)
+
+const first = ref('')
+const dialogVisible = ref(false)
+const initPage = () => {
+  // 判断当前用户的操作系统
+  if (window.localStorage.getItem('osType') === 'WIN') {
+    first.value = 'Ctrl'
+  } else {
+    first.value = '⌘'
+  }
+  // 当用户同时按下ctrl和k键的时候
+  const handleKeyDown = (e) => {
+    if (e.ctrlKey && e.key === 'k') {
+      // 阻止浏览器默认事件
+      e.preventDefault()
+      handleCommand()
+    }
+  }
+  window.addEventListener('keydown', handleKeyDown)
+
+  const screenWidth = document.body.clientWidth
+  if (screenWidth < 1000) {
+    isMobile.value = true
+    isSider.value = false
+    isCollapse.value = true
+  } else if (screenWidth >= 1000 && screenWidth < 1200) {
+    isMobile.value = false
+    isSider.value = false
+    isCollapse.value = true
+  } else {
+    isMobile.value = false
+    isSider.value = true
+    isCollapse.value = false
+  }
+}
+
+initPage()
+
+const command = ref()
+const handleCommand = () => {
+  command.value.open()
+}
+
+const loadingFlag = ref(false)
+onMounted(() => {
+  // 挂载一些通用的事件
+  emitter.emit('collapse', isCollapse.value)
+  emitter.emit('mobile', isMobile.value)
+  emitter.on('reload', reload)
+  emitter.on('showLoading', () => {
+    loadingFlag.value = true
+  })
+  emitter.on('closeLoading', () => {
+    loadingFlag.value = false
+  })
+  window.onresize = () => {
+    return (() => {
+      initPage()
+      emitter.emit('collapse', isCollapse.value)
+      emitter.emit('mobile', isMobile.value)
+    })()
+  }
+  if (userStore.loadingInstance) {
+    userStore.loadingInstance.close()
+  }
+})
+const showLayout = ref(true)
+const yourCondition = ref(true)
+
+const userStore = useUserStore()
+console.log(userStore.userInfo.authorityId)
+if(userStore.userInfo.authorityId==100002){
+	showLayout.value=false
+	yourCondition.value=false
+}else{
+	yourCondition.value=true
+}
+	
+// console.log(userStore.userInfo.nickName )
+const asideWidth = () => {
+  if (isMobile.value) {
+    return isCollapse.value ? '0px' : '220px'
+  }
+  return isCollapse.value ? '54px' : '220px'
+}
+
+const getAsideWidth = () => {
+  if (isMobile.value) return '0px'
+  return isCollapse.value ? '54px' : '220px'
+}
+
+const textColor = computed(() => {
+  if (userStore.sideMode === 'dark') {
+    return '#fff'
+  } else if (userStore.sideMode === 'light') {
+    return '#191a23'
+  } else {
+    return userStore.baseColor
+  }
+})
+
+const backgroundColor = computed(() => {
+  if (userStore.sideMode === 'dark') {
+    return '#191a23'
+  } else if (userStore.sideMode === 'light') {
+    return '#fff'
+  } else {
+    return userStore.sideMode
+  }
+})
+
+const matched = computed(() => route.meta.matched)
+
+const changeUserAuth = async(id) => {
+  const res = await setUserAuthority({
+    authorityId: id
+  })
+  if (res.code === 0) {
+    window.sessionStorage.setItem('needCloseAll', 'true')
+    window.location.reload()
+  }
+}
+
+const reloadFlag = ref(true)
+let reloadTimer = null
+const reload = async() => {
+  if (reloadTimer) {
+    window.clearTimeout(reloadTimer)
+  }
+  reloadTimer = window.setTimeout(async() => {
+    if (route.meta.keepAlive) {
+      reloadFlag.value = false
+      await nextTick()
+      reloadFlag.value = true
+    } else {
+      const title = route.meta.title
+      router.push({ name: 'Reload', params: { title }})
+    }
+  }, 400)
+}
+
+const isShadowBg = ref(false)
+const totalCollapse = () => {
+  isCollapse.value = !isCollapse.value
+  isSider.value = !isCollapse.value
+  isShadowBg.value = !isCollapse.value
+  emitter.emit('collapse', isCollapse.value)
+}
+
+const toPerson = () => {
+  router.push({ name: 'person' })
+}
+const changeShadow = () => {
+  isShadowBg.value = !isShadowBg.value
+  isSider.value = !!isCollapse.value
+  totalCollapse()
+}
+</script>
+
+<style lang="scss">
+.button {
+  font-size: 12px;
+  color: #666;
+  background:	rgb(250,250,250);
+  width: 25px!important;
+  padding: 4px 8px !important;
+  border: 1px solid #eaeaea;
+  margin-right: 4px;
+  border-radius: 4px;
+}
+:deep .el-overlay {
+  background-color: hsla(0,0%,100%,.9) !important;
+}
+
+</style>

+ 15 - 0
src/view/performance/WorkScoreReporting/gongfenbaogong.vue

@@ -67,6 +67,11 @@
                         >
                         <el-table-column align="center" label="小组" prop="sys_id" :width="90" show-overflow-tooltip />
                         <el-table-column align="center" label="订单编号" prop="work_order" :width="100" />
+						<el-table-column align="center" label="生产款号" prop="生产款号" :width="100">
+							<template #default="{ row }">
+								<span class="gongfen-cell-strong">{{ row['生产款号'] }}</span>
+							</template>
+						</el-table-column>
                         <el-table-column align="center" label="工序号" prop="process_code" :width="90" />
                                   <el-table-column align="left" label="工序名称" prop="process_name" min-width="200" show-overflow-tooltip />
                                   <el-table-column align="center" label="员工姓名" prop="staff_name" :width="120">
@@ -3002,6 +3007,16 @@ const yellowTopTip = (text) => {
   width: 100%;
 }
 
+.gongfen-header-strong {
+  color: #f56c6c;
+  font-weight: bold;
+}
+
+.gongfen-cell-strong {
+  color: #f56c6c;
+  font-weight: bold;
+}
+
 .gongfen-history-table :deep(.el-table__header .cell),
 .gongfen-history-table :deep(.el-table__body .cell),
 .gongfen-history-table :deep(.el-table__footer .cell) {

+ 354 - 142
src/view/yunyin/shengchanguanli/gongdanziliao.vue

@@ -49,10 +49,6 @@
 						  :row-style="{ height: '20px' }" :header-cell-style="{ padding: '0px' }"
 						  :cell-style="{ padding: '0px' }" :header-row-style="{ height: '20px' }"
 						  border tooltip-effect="dark" 
-						  v-loading="loading" 
-						  element-loading-text="拼命加载中"
-						  element-loading-spinner="el-icon-loading"
-						  element-loading-background="rgba(0, 0, 0, 0.8)"
 						  :data="tableData" :row-key="ddzlTableRowKey"
 						  highlight-current-row="true"
 						  :cell-class-name="tableDataCellClass"
@@ -139,7 +135,7 @@
 					<el-table-column type="selection" width="30" />
 					<el-table-column align="left" sortable label="子订单编号" prop="子订单编号"  width="140"/>
 					<el-table-column align="left" sortable label="PO号	" prop="款号"  width="180"/>
-					<el-table-column align="left" sortable label="颜色备注" prop="颜色备注"  width="110"/>
+					<el-table-column align="left" sortable label="颜色备注" prop="颜色备注"  width="150"/>
 					<el-table-column align="left" sortable label="色系名称" prop="颜色"  width="110"/>
 					<el-table-column align="left" sortable label="船样" prop="船样"  width="80"/>
 					<!-- 尺码列表 -->
@@ -315,7 +311,11 @@
   
   
 			<el-tab-pane label="BOM资料"  name="six">
-				  <el-table ref="multipleTable"
+				<div v-if="bomLoading" class="bom-loading-hint">
+				  <el-icon class="is-loading"><Loading /></el-icon>
+				  <span style="margin-left: 8px;">BOM资料生成中,请稍候...</span>
+				</div>
+				<el-table ref="bomTableRef"
 							:row-style="{ height: '20px' }"
 							:cell-style="{ padding: '0px' }" :header-row-style="{ height: '20px' }"
 							:header-cell-style="{ padding: '0px' }"
@@ -761,120 +761,119 @@
 		   <el-dialog v-model="dialogFormVisible" :before-close="edit_closeDialog" 
 		   style="width: 80%; max-height: 80%; overflow-y: auto; margin-top: 4%;"
 		   title="颜色资料修改" destroy-on-close >
-			   <el-row :gutter="20" class="form-row">
-				   <el-col :span="10">
-					   <div style="display: flex; gap: 0px;">
-						   <h3 style="color:red;font-size: 24px;margin:-5px 0px 8px 12px;">订单编号&nbsp;&nbsp;&nbsp;{{_ddhval}}</h3>
-					   </div>
-				   </el-col>
-				   <el-col :span="10">
-					   <div style="display: flex; gap: 0px;">
-						   <el-form-item label="订单子编号:" class="mab" prop="keyOrder" label-width='110'>
-							   <el-input v-model="edit_formData['子订单编号']" disabled style="width: 250px;" id="订单子编号" />
-						   </el-form-item>
-					   </div>
-				   </el-col>
+		   <el-form label-width="100px">
+		   <el-row :gutter="24" class="form-row">
+			   <el-col :span="10">
+				   <div style="display: flex; gap: 0px;">
+					   <h3 style="color:red;font-size: 24px;margin:-5px 0px 8px 12px;">订单编号&nbsp;&nbsp;&nbsp;{{_ddhval}}</h3>
+				   </div>
+			   </el-col>
+			   <el-col :span="10">
+				   <el-form-item label="订单子编号:" class="mab" prop="keyOrder" id='订单子编号' label-width='110'>
+					   <el-input v-model="edit_formData['子订单编号']" disabled style="width: 250px;" />
+				   </el-form-item>
+			   </el-col>
 			   </el-row>
-		   
-			   <el-row :gutter="20">
+		
+			   <el-row :gutter="24" class="form-row">
 				  <el-col :span="10">
-					   <div style="display: flex; gap: 0px;">
-						   <el-form-item label="色系名称:" class="mab" prop="keyOrder" label-width='110'>
-							  <el-select
-							  v-model=" edit_formData['颜色']" style="width: 150px;" id="色系名称" 
-							  @keydown="add_gdzlent($event, '', '色系名称', '款号:')">
-							  <el-option 
-								v-for="color in getSuborderscolor.colorlist" 
-								:key="color.id" 
-								:label="color.colorname" 
-								:value="color.colorname"  
-								@click="xg_sxhandleSizeChange(color.colorname,color.colorcode)">
-							  </el-option>
-							  </el-select>
-						   </el-form-item>
-					   </div>
+					   <el-form-item label="色系名称:" class="mab" prop="keyOrder" label-width='110'>
+						  <el-select
+						  v-model=" edit_formData['颜色']" style="width: 150px;" id="色系名称" 
+						  @keydown="add_gdzlent($event, '', '色系名称', '款号:')">
+						  <el-option 
+						  v-for="color in getSuborderscolor.colorlist" 
+						  :key="color.id" 
+						  :label="color.colorname" 
+							:value="color.colorname"  
+							@click="xg_sxhandleSizeChange(color.colorname,color.colorcode)">
+						  </el-option>
+						  </el-select>
+					   </el-form-item>
 				   </el-col>
 				   
-				   <el-col :span="10">
-					   <el-form-item label="PO号:" prop="deliveryDate" class="mab" label-width="110px" style="">
-						   <el-input v-model="edit_formData['款号']" style="width: 250px;" id="生产款号" />
+				<el-col :span="10">
+					   <el-form-item label="PO号:" class="mab" prop="keyOrder" label-width='110'>
+						   <el-autocomplete
+						   v-model="edit_formData['款号']"
+						   style="width: 250px;"
+						   :fetch-suggestions="queryPoSearch"
+						   placeholder="请输入或选择PO号"
+						   @select="poSelect"
+						   />
 					   </el-form-item>
 				   </el-col>
 			   </el-row>
-		   
-			   <el-row :gutter="20">
+		
+			   <el-row :gutter="24" class="form-row">
 				   <el-col :span="8">
 					 <el-form-item label="颜色备注:" prop="joinDate" label-width='110'>
 					   <el-input v-model="edit_formData['颜色备注']" style="width: 250px;" id ='颜色备注' @keydown="add_gdzlent($event, '款号:', '颜色备注', '是否船样')"/>
 					 </el-form-item>
 				   </el-col>
-				  <el-col :span="10">
-					   <div style="display: flex; gap: 0px;">
-						  <el-form-item label="是否船样:" prop="joinDate" label-width='200'>
-							   <el-select v-model="edit_formData['船样']" style="width: 150px;" id="是否船样" @keydown="add_gdzlent($event, '尺码规格', '是否船样', 'zds1')">
-								   <el-option label="是" value="是" @click="edit_cyChange('是')"></el-option>
-								   <el-option label="否" value="否" @click="edit_cyChange('否')"></el-option>
-							   </el-select>
-						   </el-form-item>
-					   </div>
-				   </el-col>
+				  
+				  <el-col :span="8">
+					<el-form-item label="是否船样:" prop="joinDate" label-width='230'>
+					   <el-select v-model="edit_formData['船样']" style="width: 150px;" id ='是否船样' @keydown="add_gdzlent($event, '颜色备注', '是否船样', 'zds1')">
+						   <el-option label="是" value="是" @click="edit_cyChange('是')"></el-option>
+						   <el-option label="否" value="否" @click="edit_cyChange('否')"></el-option>
+					   </el-select>
+					</el-form-item>
+				  </el-col>
 			   </el-row>
-		   
-			   <el-row class="form-row">
-				   <el-col :span="20">
-					   <div style="display: flex; gap: 0px;">
-						   <el-form-item label="尺码:" class="mab" prop="keyOrder" label-width='110'>
-							   <el-input v-model="edit_cmformdata.cm1" readonly style="width: 80px;" />
-							   <el-input v-model="edit_cmformdata.cm2" readonly style="width: 80px;" />
-							   <el-input v-model="edit_cmformdata.cm3" readonly style="width: 80px;" />
-							   <el-input v-model="edit_cmformdata.cm4" readonly style="width: 80px;" />
-							   <el-input v-model="edit_cmformdata.cm5" readonly style="width: 80px;" />
-							   <el-input v-model="edit_cmformdata.cm6" readonly style="width: 80px;" />
-							   <el-input v-model="edit_cmformdata.cm7" readonly style="width: 80px;" />
-							   <el-input v-model="edit_cmformdata.cm8" readonly style="width: 80px;" />
-							   <el-input v-model="edit_cmformdata.cm9" readonly style="width: 80px;" />
-							   <el-input v-model="edit_cmformdata.cm10" readonly style="width: 80px;" />
-							   <el-input v-model="edit_cmformdata.cm11" readonly style="width: 80px;" />
-							   <el-input v-model="edit_cmformdata.cm12" readonly style="width: 80px;" />
-							   <el-input v-model="edit_cmformdata.cm13" readonly style="width: 80px;" />
-							   <el-input v-model="edit_cmformdata.cm14" readonly style="width: 80px;" />
-							   <el-input v-model="edit_cmformdata.sctotal" readonly style="width: 80px;" />
-						   </el-form-item>
-					   </div>
+		
+			   <el-row :gutter="24" class="form-row">
+				   <el-col :span="24">
+					   <el-form-item label="尺码:" class="mab" prop="keyOrder" label-width="110">
+						   <el-input v-model="edit_cmformdata.cm1" readonly style="width: 80px;" />
+						   <el-input v-model="edit_cmformdata.cm2" readonly style="width: 80px;" />
+						   <el-input v-model="edit_cmformdata.cm3" readonly style="width: 80px;" />
+						   <el-input v-model="edit_cmformdata.cm4" readonly style="width: 80px;" />
+						   <el-input v-model="edit_cmformdata.cm5" readonly style="width: 80px;" />
+						   <el-input v-model="edit_cmformdata.cm6" readonly style="width: 80px;" />
+						   <el-input v-model="edit_cmformdata.cm7" readonly style="width: 80px;" />
+						   <el-input v-model="edit_cmformdata.cm8" readonly style="width: 80px;" />
+						   <el-input v-model="edit_cmformdata.cm9" readonly style="width: 80px;" />
+						   <el-input v-model="edit_cmformdata.cm10" readonly style="width: 80px;" />
+						   <el-input v-model="edit_cmformdata.cm11" readonly style="width: 80px;" />
+						   <el-input v-model="edit_cmformdata.cm12" readonly style="width: 80px;" />
+						   <el-input v-model="edit_cmformdata.cm13" readonly style="width: 80px;" />
+						   <el-input v-model="edit_cmformdata.cm14" readonly style="width: 80px;" />
+						   <el-input v-model="edit_cmformdata.sctotal" readonly style="width: 80px;" />
+					   </el-form-item>
 				   </el-col>
 			   </el-row>
-		   
-			   <el-row class="form-row">
-				   <el-col :span="20">
-					   <div style="display: flex; gap: 0px;">
-						   <el-form-item label="制单数:" class="mab" prop="keyOrder" label-width='110'>
-							   <el-input v-model="edit_zdsformdata.zds1" @input="updateTotal" style="width: 80px;" id ='zds1'  @keydown="add_gdzlent($event, 'cm14', 'zds1', 'zds2')"/>
-							   <el-input v-model="edit_zdsformdata.zds2" @input="updateTotal" style="width: 80px;" id ='zds2'  @keydown="add_gdzlent($event, 'zds1', 'zds2', 'zds3')"/>
-							   <el-input v-model="edit_zdsformdata.zds3" @input="updateTotal" style="width: 80px;" id ='zds3'  @keydown="add_gdzlent($event, 'zds2', 'zds3', 'zds4')"/>
-							   <el-input v-model="edit_zdsformdata.zds4" @input="updateTotal" style="width: 80px;" id ='zds4'  @keydown="add_gdzlent($event, 'zds3', 'zds4', 'zds5')"/>
-							   <el-input v-model="edit_zdsformdata.zds5" @input="updateTotal" style="width: 80px;" id ='zds5'  @keydown="add_gdzlent($event, 'zds4', 'zds5', 'zds6')"/>
-							   <el-input v-model="edit_zdsformdata.zds6" @input="updateTotal" style="width: 80px;" id ='zds6'  @keydown="add_gdzlent($event, 'zds5', 'zds6', 'zds7')"/>
-							   <el-input v-model="edit_zdsformdata.zds7" @input="updateTotal" style="width: 80px;" id ='zds7'  @keydown="add_gdzlent($event, 'zds6', 'zds7', 'zds8')"/>
-							   <el-input v-model="edit_zdsformdata.zds8" @input="updateTotal" style="width: 80px;" id ='zds8'  @keydown="add_gdzlent($event, 'zds7', 'zds8', 'zds9')"/>
-							   <el-input v-model="edit_zdsformdata.zds9" @input="updateTotal" style="width: 80px;" id ='zds9'  @keydown="add_gdzlent($event, 'zds8', 'zds9', 'zds10')"/>
-							   <el-input v-model="edit_zdsformdata.zds10" @input="updateTotal" style="width: 80px;" id ='zds10'  @keydown="add_gdzlent($event, 'zds9', 'zds10', 'zds11')"/>
-							   <el-input v-model="edit_zdsformdata.zds11" @input="updateTotal" style="width: 80px;" id ='zds11'  @keydown="add_gdzlent($event, 'zds10', 'zds11', 'zds12')"/>
-							   <el-input v-model="edit_zdsformdata.zds12" @input="updateTotal" style="width: 80px;" id ='zds12'  @keydown="add_gdzlent($event, 'zds11', 'zds12', 'zds13')"/>
-							   <el-input v-model="edit_zdsformdata.zds13" @input="updateTotal" style="width: 80px;" id ='zds13'  @keydown="add_gdzlent($event, 'zds12', 'zds13', 'zds14')"/>
-							   <el-input v-model="edit_zdsformdata.zds14" @input="updateTotal" style="width: 80px;" id ='zds14'  @keydown="add_gdzlent($event, 'zds13', 'zds14', '色系名称')"/>
-							   <el-input v-model="edit_zdsformdata.zdtotal" readonly style="width: 80px;" />
-						   </el-form-item>
-					   </div>
+		
+			   <el-row :gutter="24" class="form-row">
+				   <el-col :span="24">
+					   <el-form-item label="制单数:" class="mab" prop="keyOrder" label-width='110'>
+						   <el-input v-model="edit_zdsformdata.zds1" @input="updateTotal" style="width: 80px;" id ='zds1'  @keydown="add_gdzlent($event, 'cm14', 'zds1', 'zds2')"/>
+						   <el-input v-model="edit_zdsformdata.zds2" @input="updateTotal" style="width: 80px;" id ='zds2'  @keydown="add_gdzlent($event, 'zds1', 'zds2', 'zds3')"/>
+						   <el-input v-model="edit_zdsformdata.zds3" @input="updateTotal" style="width: 80px;" id ='zds3'  @keydown="add_gdzlent($event, 'zds2', 'zds3', 'zds4')"/>
+						   <el-input v-model="edit_zdsformdata.zds4" @input="updateTotal" style="width: 80px;" id ='zds4'  @keydown="add_gdzlent($event, 'zds3', 'zds4', 'zds5')"/>
+						   <el-input v-model="edit_zdsformdata.zds5" @input="updateTotal" style="width: 80px;" id ='zds5'  @keydown="add_gdzlent($event, 'zds4', 'zds5', 'zds6')"/>
+						   <el-input v-model="edit_zdsformdata.zds6" @input="updateTotal" style="width: 80px;" id ='zds6'  @keydown="add_gdzlent($event, 'zds5', 'zds6', 'zds7')"/>
+						   <el-input v-model="edit_zdsformdata.zds7" @input="updateTotal" style="width: 80px;" id ='zds7'  @keydown="add_gdzlent($event, 'zds6', 'zds7', 'zds8')"/>
+						   <el-input v-model="edit_zdsformdata.zds8" @input="updateTotal" style="width: 80px;" id ='zds8'  @keydown="add_gdzlent($event, 'zds7', 'zds8', 'zds9')"/>
+						   <el-input v-model="edit_zdsformdata.zds9" @input="updateTotal" style="width: 80px;" id ='zds9'  @keydown="add_gdzlent($event, 'zds8', 'zds9', 'zds10')"/>
+						   <el-input v-model="edit_zdsformdata.zds10" @input="updateTotal" style="width: 80px;" id ='zds10'  @keydown="add_gdzlent($event, 'zds9', 'zds10', 'zds11')"/>
+						   <el-input v-model="edit_zdsformdata.zds11" @input="updateTotal" style="width: 80px;" id ='zds11'  @keydown="add_gdzlent($event, 'zds10', 'zds11', 'zds12')"/>
+						   <el-input v-model="edit_zdsformdata.zds12" @input="updateTotal" style="width: 80px;" id ='zds12'  @keydown="add_gdzlent($event, 'zds11', 'zds12', 'zds13')"/>
+						   <el-input v-model="edit_zdsformdata.zds13" @input="updateTotal" style="width: 80px;" id ='zds13'  @keydown="add_gdzlent($event, 'zds12', 'zds13', 'zds14')"/>
+						   <el-input v-model="edit_zdsformdata.zds14" @input="updateTotal" style="width: 80px;" id ='zds14'  @keydown="add_gdzlent($event, 'zds13', 'zds14', '色系名称')"/>
+						   <el-input v-model="edit_zdsformdata.zdtotal" readonly style="width: 80px;" />
+					   </el-form-item>
 				   </el-col>
 			   </el-row>
-			   
+			   </el-form>
+		
 			   <template #footer>
 				   <div class="dialog-footer" style="display: flex; justify-content: flex-end; margin-top: 20px;">
 					   <el-button @click="ys_edit_closeDialog" style="margin-left: 10px;">取消</el-button>
+					   <el-button @click="openSizePreview('edit')" style="margin-left: 10px;">预览尺码</el-button>
 					   <el-button type="primary" @click="ys_edit_Dialog" style="margin-left: 10px;">确认</el-button>
 				   </div>
 			   </template>
-  
 		   </el-dialog>
 		
 			  
@@ -1478,7 +1477,13 @@
 				  </el-col>
 				  <el-col :span="10">
 					<el-form-item label="PO号:" class="mab" prop="keyOrder" label-width="110">
-					  <el-input v-model="add_yszlformdata.kh" style="width: 250px;" id ='款号:' @keydown="add_gdzlent($event, '色系名称', '款号:', '颜色备注')"/>
+					  <el-autocomplete
+					  v-model="add_yszlformdata.kh"
+					  style="width: 250px;"
+					  :fetch-suggestions="queryPoSearch"
+					  placeholder="请输入或选择PO号"
+					  @select="poSelect"
+					  />
 					</el-form-item>
 				  </el-col>
 				</el-row>
@@ -1637,13 +1642,42 @@
 		  
 			  </el-form>
 		  
-			  <template #footer>
+		  <template #footer>
 				<div class="dialog-footer" style="display: flex; justify-content: flex-end; padding: 10px;">
-				  <el-button type="primary" @click="add_dyjcloseDialog">取消</el-button>
+				  <el-button @click="add_dyjcloseDialog">取消</el-button>
+				  <el-button @click="openSizePreview">预览尺码</el-button>
 				  <el-button type="primary" @click="add_gdyjenterDialog">确定</el-button>
 				</div>
+		  </template>
+		  
+			</el-dialog>
+
+			<!-- 尺码数量预览弹窗 -->
+			<el-dialog
+			  v-model="sizePreviewVisible"
+			  title="尺码数量预览"
+			  width="46%"
+			  append-to-body
+			  destroy-on-close>
+			<div v-loading="sizePreviewLoading">
+				<table class="size-preview-h-table">
+				  <tr>
+					<th class="size-preview-h-label-th">尺码</th>
+					<th v-for="s in sizePreviewSizes" :key="'h-'+s">{{ s }}</th>
+					<th class="size-preview-h-total-th">合计(件)</th>
+				  </tr>
+				  <tr>
+					<td class="size-preview-h-label-td">制单数</td>
+					<td v-for="(q, idx) in sizePreviewQtys" :key="'q-'+idx">{{ q }}</td>
+					<td class="size-preview-h-total-td">{{ sizePreviewCurrentTotal }}</td>
+				  </tr>
+				</table>
+			  </div>
+			  <template #footer>
+				<div class="dialog-footer" style="text-align: right;">
+				  <el-button type="primary" @click="sizePreviewVisible = false">关闭</el-button>
+				</div>
 			  </template>
-			  
 			</el-dialog>
 			  
 		  <!-- 新增工序资料-->
@@ -2559,12 +2593,12 @@
   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 } 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 { Plus } from '@element-plus/icons-vue'
+  import { Plus, Loading } from '@element-plus/icons-vue'
   import { ref, reactive, computed, nextTick, watch, onMounted, onBeforeUnmount, isRef } from 'vue'
   import axios from 'axios';
   import * as XLSX from 'xlsx';
@@ -3047,6 +3081,10 @@
 	  //获取色系名称列表
 	  const getSuborders = await getSuborder({order:_Gd_gdbh.value,cy:'否',colorname:edit_formData['颜色']});
 	  getSuborderscolor.colorlist = getSuborders.data.colorlist;
+	  // 更新PO号下拉选项
+	  if (getSuborders.data.po) {
+		poOptions.value = getSuborders.data.po;
+	  }
 	  let index = 1;  
 	  // 创建一个函数来构建尺码和数量的映射  
 	  function buildSizeQuantitiesMap(sizes, formData) {  
@@ -3091,9 +3129,17 @@
 	if (value === '是') {
 		  const getSuborders = await getSuborder({order:_Gd_gdbh.value,cy:'是',colorname:edit_formData['颜色']});
 		  edit_formData['子订单编号'] = getSuborders.data;
+		  // 更新PO号下拉选项
+		  if (getSuborders.data.po) {
+			poOptions.value = getSuborders.data.po;
+		  }
 	  }else if(value === '否'){
 		  const getSuborders = await getSuborder({order:_Gd_gdbh.value,cy:'否',colorname:edit_formData['颜色']});
 		  edit_formData['子订单编号'] = getSuborders.data.order;
+		  // 更新PO号下拉选项
+		  if (getSuborders.data.po) {
+			poOptions.value = getSuborders.data.po;
+		  }
 	}
   };
   
@@ -3228,7 +3274,7 @@
 	  ElMessage.error('获取款式列表失败');
 	}
   };
-  
+
   // =========== 新增订单资料 ===========
   const add_gdscgdformData = reactive({
 	订单编号: '',
@@ -3336,12 +3382,7 @@
 	}
 
 	isClickable.value = false;
-	const loadingInstance = ElLoading.service({
-	  lock: true,
-	  text: '正在自动获取生成BOM面料资料,请稍候...',
-	  background: 'rgba(0, 0, 0, 0.8)',
-	  spinner: 'el-icon-loading',
-	});
+	ElMessage({ type: 'info', message: '正在自动获取生成BOM面料资料,请稍候...', duration: 0 });
 
 	try {
 	  let lhrq_formattedDeliveryDate = '';
@@ -3390,6 +3431,12 @@
 		ElMessage({ type: 'success', message: '新增成功' });
 		const WorkListdata = await WorkOrderList({ search: _Gd_khdh.value, page: 1, limit: 50 });
 		tableData.value = WorkListdata.data.data;
+		
+		// 找到新创建的订单并触发加载 BOM 数据
+		const newOrder = tableData.value.find(item => item.订单编号 === formattedData.订单编号);
+		if (newOrder) {
+		  await tableRowClick(newOrder);
+		}
 	  } else {
 		ElMessage({ type: 'error', message: edit_gdformdata_list.msg || edit_gdformdata_list.message || '新增失败' });
 	  }
@@ -3399,7 +3446,6 @@
 	  ElMessage({ type: 'error', message: msg });
 	} finally {
 	  isClickable.value = true;
-	  loadingInstance.close();
 	}
   };
   //新增工单资料取消 / 关闭
@@ -3541,14 +3587,62 @@
   });
   //记录色系编号
   const add_colorcode = ref('')
+
+  // =========== 尺码数量预览 ===========
+  const sizePreviewVisible = ref(false)
+  const sizePreviewLoading = ref(false)
+  const sizePreviewSizes = ref([])
+  const sizePreviewQtys = ref([])
+  const sizePreviewCurrentTotal = ref(0)
+
+  // 打开尺码数量预览:照抄弹窗中填写的尺码与制单数,用于核对
+  // source: 'add' = 新增颜色资料弹窗, 'edit' = 颜色资料修改弹窗
+  const openSizePreview = (source = 'add') => {
+    sizePreviewVisible.value = true
+    sizePreviewLoading.value = false
+    const cmSource = source === 'edit' ? edit_cmformdata : cmformdata
+    const zdsSource = source === 'edit' ? edit_zdsformdata : zdsformdata
+    const sizes = []
+    const qtys = []
+    for (let i = 1; i <= 14; i++) {
+      const size = String(cmSource[`cm${i}`] ?? '').trim()
+      if (!size) continue
+      sizes.push(size)
+      qtys.push(parseInt(String(zdsSource[`zds${i}`] ?? '').replace(/[^\d]/g, ''), 10) || 0)
+    }
+    sizePreviewSizes.value = sizes
+    sizePreviewQtys.value = qtys
+    sizePreviewCurrentTotal.value = qtys.reduce((sum, v) => sum + v, 0)
+  }
   
   //选择色系名称获取子订单编号
+  //把后端返回的尺码数据填充到新增弹窗的尺码输入框
+  // 后端返回的尺码来自"当前订单下未删除的颜色资料",删除后重新新增不会带上旧尺码
+  const applyCmToAddForm = (cm) => {
+	// 先清空,避免后端没返回尺码时残留上一次的值
+	for (let i = 1; i <= 14; i++) {
+	  cmformdata[`cm${i}`] = '';
+	}
+	cmformdata.sctotal = '合计';
+	if (cm) {
+	  for (let i = 1; i <= 14; i++) {
+		cmformdata[`cm${i}`] = cm[`cm${i}`] || '';
+	  }
+	}
+  }
+
   const xz_sxhandleSizeChange =  async  (value,color_id) => {
 	  
 	  //获取子订单编号
 	  const getPonumberdata = await getSuborder({colorname:value,order:_ddhval.value,cy:add_yszlformdata.sfcy});
 	  console.log("获取子订单编号",getPonumberdata)
 	  add_yszlformdata.ddzbh = getPonumberdata.data.order ? getPonumberdata.data.order : getPonumberdata.data;
+	  // 更新PO号下拉选项
+	  if (getPonumberdata.data.po) {
+		poOptions.value = getPonumberdata.data.po;
+	  }
+	  // 切换色系时同步刷新尺码
+	  applyCmToAddForm(getPonumberdata.data.cm);
 	  
 	if (value === '红色系') {
 	  add_yszlformdata.sxmc = value
@@ -3592,7 +3686,11 @@
   const xg_sxhandleSizeChange =  async (value,color_id) => {
 	  
 	  const getPonumberdata = await getSuborder({colorname:value,order:_ddhval.value,cy:edit_formData['船样']});
-	  console.log(edit_formData.value)	
+	  console.log(edit_formData.value)
+	  // 更新PO号下拉选项
+	  if (getPonumberdata.data.po) {
+		poOptions.value = getPonumberdata.data.po;
+	  }
 	  if(edit_formData.value['颜色'] === edit_formData['颜色']){
 		  edit_formData['子订单编号'] = edit_formData.value['子订单编号']
 	  }else{
@@ -3643,15 +3741,57 @@
 		  add_yszlformdata.ddzbh = _Gd_gdbh.value + '-99'
 		  const getSuborders = await getSuborder({order:_Gd_gdbh.value,cy:'是',colorname:add_yszlformdata['sxmc']});
 		  add_yszlformdata['ddzbh'] = getSuborders.data;
+		  // 更新PO号下拉选项
+		  if (getSuborders.data.po) {
+			poOptions.value = getSuborders.data.po;
+		  }
+		  // 切换船样时清空尺码(船样分支后端不返回尺码数据)
+		  applyCmToAddForm(getSuborders.data.cm);
 	  }else if(value === '否'){
 		  const getSuborders = await getSuborder({order:_Gd_gdbh.value,cy:'否',colorname:add_yszlformdata['sxmc']});
 		  add_yszlformdata['ddzbh'] = getSuborders.data.order;
+		  // 更新PO号下拉选项
+		  if (getSuborders.data.po) {
+			poOptions.value = getSuborders.data.po;
+		  }
+		  // 切换船样时同步刷新尺码
+		  applyCmToAddForm(getSuborders.data.cm);
 	}
   };
   
   const getSuborderscolor = reactive({
 		colorlist: []
   });
+
+  // PO号下拉选项
+  const poOptions = ref([]);
+
+  // PO号搜索建议
+  const queryPoSearch = (queryString, cb) => {
+    if (!poOptions.value || poOptions.value.length === 0) {
+      // 没有下拉数据时,输入什么就返回什么(直接使用输入值)
+      if (queryString) {
+        cb([{ value: queryString }]);
+      } else {
+        cb([]);
+      }
+      return;
+    }
+    const results = queryString
+      ? poOptions.value.filter(createFilter(queryString))
+      : poOptions.value.map(po => ({ value: po }));
+    cb(results);
+  };
+
+  // PO号过滤函数
+  const createFilter = (queryString) => {
+    return (po) => String(po).toLowerCase().indexOf(queryString.toLowerCase()) === 0;
+  };
+
+  // PO号选择回调
+  const poSelect = (item) => {
+    console.log('选中PO号:', item.value);
+  };
 	  
   //新增颜色资料按钮
   const addgdyj_onclick = async  () => {
@@ -3666,27 +3806,16 @@
 	  
 	  
 	  getSuborderscolor.colorlist = getSuborders.data.colorlist;
-			  
+
+	  // 更新PO号下拉选项
+	  if (getSuborders.data.po) {
+		poOptions.value = getSuborders.data.po;
+	  }
+
 	  add_yszlformdata['ddzbh'] = getSuborders.data.order;
 	  // 假设获取的数据是 getSuborders.data
-	  if (getSuborders.data.cm) {
-		// 判断并赋值
-		cmformdata.cm1 = getSuborders.data.cm.cm1 || '';
-		cmformdata.cm2 = getSuborders.data.cm.cm2 || '';
-		cmformdata.cm3 = getSuborders.data.cm.cm3 || '';
-		cmformdata.cm4 = getSuborders.data.cm.cm4 || '';
-		cmformdata.cm5 = getSuborders.data.cm.cm5 || '';
-		cmformdata.cm6 = getSuborders.data.cm.cm6 || '';
-		cmformdata.cm7 = getSuborders.data.cm.cm7 || '';
-		cmformdata.cm8 = getSuborders.data.cm.cm8 || '';
-		cmformdata.cm9 = getSuborders.data.cm.cm9 || '';
-		cmformdata.cm10 = getSuborders.data.cm.cm10 || '';
-		cmformdata.cm11 = getSuborders.data.cm.cm11 || '';
-		cmformdata.cm12 = getSuborders.data.cm.cm12 || '';
-		cmformdata.cm13 = getSuborders.data.cm.cm13 || '';
-		cmformdata.cm14 = getSuborders.data.cm.cm14 || '';
-		cmformdata.sctotal = '合计';
-	  }
+	  // 无论后端是否返回尺码,都要先清空,避免上一次打开弹窗/切换船样后的残留值
+	  applyCmToAddForm(getSuborders.data.cm);
   
 	  add_yszlformdata['ddh'] = _Gd_gdbh.value;
 	  add_yszlformdata['ys'] = '';
@@ -3830,9 +3959,26 @@ function Input_handle() {
 		  sizeDatas.splice(0,PrintListDatas.data.型号.length,...PrintListDatas.data.型号);
 		  ystableData.splice(0,PrintListDatas.data.列表.length,...PrintListDatas.data.列表);
 		  
-		  const orderBomListdata = await orderBomList({order:_Gd_gdbh.value});
-		  bomtableData.value = orderBomListdata.data
-		  bomtableData.splice(0,orderBomListdata.data.length,...orderBomListdata.data);//bom表格数据
+		  // BOM 数据:查询时若无数据后端会自动触发生成,需轮询等待
+		  const fetchBomData = async () => {
+			const bomRes = await orderBomList({ order: _Gd_gdbh.value });
+			if (bomRes?.data && bomRes.data.length > 0) {
+			  bomtableData.splice(0, bomtableData.length, ...bomRes.data);
+			  return true; // 有数据了,停止轮询
+			}
+			return false; // 还需继续轮询
+		  };
+		  
+		  // 等待 BOM 数据(最多等待 30 秒,每 2 秒轮询一次)
+		  bomLoading.value = true; // 开始加载
+		  let bomLoaded = await fetchBomData();
+		  let waitCount = 0;
+		  while (!bomLoaded && waitCount < 15) {
+			await new Promise(resolve => setTimeout(resolve, 2000));
+			bomLoaded = await fetchBomData();
+			waitCount++;
+		  }
+		  bomLoading.value = false; // 加载完成
 		  
 		  const fabricListdata = await fabricList({ order: _Gd_gdbh.value });
 		  console.log("fabricListdata:", fabricListdata); // 打印接口返回的数据
@@ -5890,6 +6036,10 @@ const sortTableData = ref([])
   onBeforeUnmount(() => {
     gyDetailResizeObserver?.disconnect()
     gyDetailResizeObserver = null
+    if (bomPollingTimer) {
+      clearInterval(bomPollingTimer);
+      bomPollingTimer = null;
+    }
   })
 
   const clearGyDetailTableSelection = async () => {
@@ -7240,6 +7390,7 @@ const sortTableData = ref([])
   const fhdtableData = reactive([])
   //BOM附件
   const bomtableData = reactive([])
+  const bomLoading = ref(false) // BOM 表格加载状态
   //面料附件
   const mltableData = reactive([])
   
@@ -7427,15 +7578,27 @@ const sortTableData = ref([])
 	  const jsOrderAttachments = await OrderAttachments({order:_Gd_gdbh.value,desc:'技术附件'})
 	  const ddOrderAttachments = await OrderAttachments({order:_Gd_gdbh.value,desc:'订单资料附件'})
 	  const fhdOrderAttachments = await OrderAttachments({order:_Gd_gdbh.value,desc:'发货单附件'})
-	  
-	  const orderBomListdata = await orderBomList({ order: _Gd_gdbh.value });
-	  // console.log("orderBomListdata:", orderBomListdata); // 打印接口返回的数据
-	  // 确保 orderBomListdata.data 是数组,避免 undefined 错误
-	  if (orderBomListdata?.data && Array.isArray(orderBomListdata.data)) {
-		  bomtableData.splice(0, bomtableData.length, ...orderBomListdata.data);
-	  } else {
-		  bomtableData.splice(0, bomtableData.length); // 如果数据无效,清空表格
+
+	  // BOM 数据:查询时若无数据后端会自动触发生成,需轮询等待
+	  bomtableData.splice(0, bomtableData.length);
+	  bomLoading.value = true; // 开始加载
+	  const fetchBomData = async () => {
+		const bomRes = await orderBomList({ order: _Gd_gdbh.value });
+		if (bomRes?.data && bomRes.data.length > 0) {
+		  bomtableData.splice(0, bomtableData.length, ...bomRes.data);
+		  return true;
+		}
+		return false;
+	  };
+	  // 等待 BOM 数据(最多等待 30 秒,每 2 秒轮询一次)
+	  let bomLoaded = await fetchBomData();
+	  let waitCount = 0;
+	  while (!bomLoaded && waitCount < 15) {
+		await new Promise(resolve => setTimeout(resolve, 2000));
+		bomLoaded = await fetchBomData();
+		waitCount++;
 	  }
+	  bomLoading.value = false; // 加载完成
 	  
 	  // 获取部件资料
 	const partListData = await PartList({ workorder: _Gd_gdbh.value });
@@ -9087,6 +9250,44 @@ const sortTableData = ref([])
     opacity: 0.85;
   }
 
+  /* 尺码数量预览 */
+  .size-preview-h-table {
+    width: 100%;
+    border-collapse: collapse;
+    table-layout: fixed;
+    font-size: 13px;
+    text-align: center;
+  }
+
+  .size-preview-h-table th,
+  .size-preview-h-table td {
+    border: 1px solid #dcdfe6;
+    padding: 8px 4px;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+  }
+
+  .size-preview-h-table th {
+    background: #f5f7fa;
+    color: #303133;
+    font-weight: bold;
+  }
+
+  .size-preview-h-label-th,
+  .size-preview-h-label-td {
+    width: 90px;
+    background: #fafafa;
+    color: #303133;
+    font-weight: bold;
+  }
+
+  .size-preview-h-total-th,
+  .size-preview-h-total-td {
+    color: #303133;
+    font-weight: bold;
+  }
+
 </style>
 
 <style>
@@ -9206,5 +9407,16 @@ const sortTableData = ref([])
     color: #f56c6c;
     font-weight: 600;
   }
+
+  /* BOM 加载提示 */
+  .bom-loading-hint {
+    display: flex;
+    align-items: center;
+    padding: 12px 16px;
+    color: #909399;
+    font-size: 14px;
+    background: #f5f7fa;
+    border-bottom: 1px solid #ebeef5;
+  }
 </style>
   

+ 135 - 130
vite.config.js

@@ -1,130 +1,135 @@
-import legacyPlugin from '@vitejs/plugin-legacy'
-import AutoImport from 'unplugin-auto-import/vite'
-import Components from 'unplugin-vue-components/vite'
-import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
-import { viteLogo } from './src/core/config'
-import Banner from 'vite-plugin-banner'
-import * as path from 'path'
-import * as dotenv from 'dotenv'
-import * as fs from 'fs'
-import vuePlugin from '@vitejs/plugin-vue'
-import GvaPosition from './vitePlugin/gvaPosition'
-import GvaPositionServer from './vitePlugin/codeServer'
-import fullImportPlugin from './vitePlugin/fullImport/fullImport.js'
-
-
-
-// @see https://cn.vitejs.dev/config/
-export default ({
-  command,
-  mode
-}) => {
-  const NODE_ENV = mode || 'development'
-  const envFiles = [
-    `.env.${NODE_ENV}`
-  ]
-  for (const file of envFiles) {
-    const envConfig = dotenv.parse(fs.readFileSync(file))
-    for (const k in envConfig) {
-      process.env[k] = envConfig[k]
-    }
-  }
-
-  viteLogo(process.env)
-
-  const timestamp = Date.parse(new Date())
-
-  const optimizeDeps = {}
-
-  const alias = {
-    '@': path.resolve(__dirname, './src'),
-    'vue$': 'vue/dist/vue.runtime.esm-bundler.js',
-  }
-
-  const esbuild = {}
-
-  // const rollupOptions = {
-  //   output: {
-  //     entryFileNames: 'js/087AC4D233B64EB0[name].js',
-  //     chunkFileNames: 'js/087AC4D233B64EB0[name].js',
-  //     assetFileNames: 'assets/087AC4D233B64EB0[name].[ext]',
-  //   },
-  // }
-  const rollupOptions = {
-      output: {
-        entryFileNames: 'js/[name].[hash].js', // 添加哈希值
-        chunkFileNames: 'js/[name].[hash].js', // 添加哈希值
-        assetFileNames: 'assets/[name].[hash].[ext]', // 添加哈希值
-      },
-    }
-
-  const config = {
-    base: './', // index.html文件所在位置
-    root: './', // js导入的资源路径,src
-    resolve: {
-      alias,
-    },
-    define: {
-      'process.env': {}
-    },
-    server: {
-      // 如果使用docker-compose开发模式,设置为false
-      open: true,
-      port: process.env.VITE_CLI_PORT,
-      proxy: {
-        // 把key的路径代理到target位置
-        // detail: https://cli.vuejs.org/config/#devserver-proxy
-        [process.env.VITE_BASE_API]: { // 需要代理的路径   例如 '/api'
-          target: `${process.env.VITE_BASE_PATH}:${process.env.VITE_SERVER_PORT}/`, // 代理到 目标路径
-          changeOrigin: true,
-          rewrite: path => path.replace(new RegExp('^' + process.env.VITE_BASE_API), ''),
-        },
-		[process.env.VITE_UPLOADS_API]: { // 需要代理的路径   例如 '/api'
-		  target: `${process.env.VITE_BASE_PATH}:${process.env.VITE_UPLOADS_PORT}/`, // 代理到 目标路径
-		  changeOrigin: true
-		},
-      },
-    },
-    build: {
-      minify: 'terser', // 是否进行压缩,boolean | 'terser' | 'esbuild',默认使用terser
-      manifest: false, // 是否产出manifest.json
-      sourcemap: false, // 是否产出sourcemap.json
-      outDir: 'dist', // 产出目录
-      rollupOptions,
-    },
-    esbuild,
-    optimizeDeps,
-    plugins: [
-      GvaPositionServer(),
-      GvaPosition(),
-      legacyPlugin({
-        targets: ['Android > 39', 'Chrome >= 60', 'Safari >= 10.1', 'iOS >= 10.3', 'Firefox >= 54', 'Edge >= 15'],
-      }),
-      vuePlugin(),
-      [Banner(`\n Build based on gin-vue-admin \n Time : ${timestamp}`)]
-    ],
-    css: {
-      preprocessorOptions: {
-        scss: {
-          additionalData: `@use "@/style/element/index.scss" as *;`,
-        }
-      }
-    },
-  }
-
-  if (NODE_ENV === 'development') {
-    config.plugins.push(
-      fullImportPlugin()
-    )
-  } else {
-    config.plugins.push(AutoImport({
-      resolvers: [ElementPlusResolver()]
-    }),
-    Components({
-      resolvers: [ElementPlusResolver({
-        importStyle: 'sass'
-      })]
-    }))
-  }
-  return config
-}
+import legacyPlugin from '@vitejs/plugin-legacy'
+import AutoImport from 'unplugin-auto-import/vite'
+import Components from 'unplugin-vue-components/vite'
+import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
+import { viteLogo } from './src/core/config'
+import Banner from 'vite-plugin-banner'
+import * as path from 'path'
+import * as dotenv from 'dotenv'
+import * as fs from 'fs'
+import vuePlugin from '@vitejs/plugin-vue'
+import GvaPosition from './vitePlugin/gvaPosition'
+import GvaPositionServer from './vitePlugin/codeServer'
+import fullImportPlugin from './vitePlugin/fullImport/fullImport.js'
+
+
+
+// @see https://cn.vitejs.dev/config/
+export default ({
+  command,
+  mode
+}) => {
+  const NODE_ENV = mode || 'development'
+  const envFiles = [
+    `.env.${NODE_ENV}`
+  ]
+  for (const file of envFiles) {
+    const envConfig = dotenv.parse(fs.readFileSync(file))
+    for (const k in envConfig) {
+      process.env[k] = envConfig[k]
+    }
+  }
+
+  viteLogo(process.env)
+
+  const timestamp = Date.parse(new Date())
+
+  const optimizeDeps = {}
+
+  const alias = {
+    '@': path.resolve(__dirname, './src'),
+    'vue$': 'vue/dist/vue.runtime.esm-bundler.js',
+  }
+
+  const esbuild = {}
+
+  // const rollupOptions = {
+  //   output: {
+  //     entryFileNames: 'js/087AC4D233B64EB0[name].js',
+  //     chunkFileNames: 'js/087AC4D233B64EB0[name].js',
+  //     assetFileNames: 'assets/087AC4D233B64EB0[name].[ext]',
+  //   },
+  // }
+  const rollupOptions = {
+      output: {
+        entryFileNames: 'js/[name].[hash].js', // 添加哈希值
+        chunkFileNames: 'js/[name].[hash].js', // 添加哈希值
+        assetFileNames: 'assets/[name].[hash].[ext]', // 添加哈希值
+      },
+    }
+
+  const config = {
+    base: './', // index.html文件所在位置
+    root: './', // js导入的资源路径,src
+    resolve: {
+      alias,
+    },
+    define: {
+      'process.env': {}
+    },
+    server: {
+      // 如果使用docker-compose开发模式,设置为false
+      open: true,
+      port: process.env.VITE_CLI_PORT,
+      proxy: {
+        // 把key的路径代理到target位置
+        // detail: https://cli.vuejs.org/config/#devserver-proxy
+        [process.env.VITE_BASE_API]: { // 需要代理的路径   例如 '/api'
+          target: `${process.env.VITE_BASE_PATH}:${process.env.VITE_SERVER_PORT}/`, // 代理到 目标路径
+          changeOrigin: true,
+          rewrite: path => path.replace(new RegExp('^' + process.env.VITE_BASE_API), ''),
+        },
+		[process.env.VITE_UPLOADS_API]: { // 需要代理的路径   例如 '/api'
+		  target: `${process.env.VITE_BASE_PATH}:${process.env.VITE_UPLOADS_PORT}/`, // 代理到 目标路径
+		  changeOrigin: true
+		},
+        '/mes_server': { // 代理到 PHP 后端 (mes-dacheng-server-api)
+          target: `${process.env.VITE_BASE_PATH_PHP}/`, // 去掉 /mes_server 前缀,转发到 PHP 后端根路径
+          changeOrigin: true,
+          rewrite: path => path.replace(/^\/mes_server/, ''),
+        },
+      },
+    },
+    build: {
+      minify: 'terser', // 是否进行压缩,boolean | 'terser' | 'esbuild',默认使用terser
+      manifest: false, // 是否产出manifest.json
+      sourcemap: false, // 是否产出sourcemap.json
+      outDir: 'dist', // 产出目录
+      rollupOptions,
+    },
+    esbuild,
+    optimizeDeps,
+    plugins: [
+      GvaPositionServer(),
+      GvaPosition(),
+      legacyPlugin({
+        targets: ['Android > 39', 'Chrome >= 60', 'Safari >= 10.1', 'iOS >= 10.3', 'Firefox >= 54', 'Edge >= 15'],
+      }),
+      vuePlugin(),
+      [Banner(`\n Build based on gin-vue-admin \n Time : ${timestamp}`)]
+    ],
+    css: {
+      preprocessorOptions: {
+        scss: {
+          additionalData: `@use "@/style/element/index.scss" as *;`,
+        }
+      }
+    },
+  }
+
+  if (NODE_ENV === 'development') {
+    config.plugins.push(
+      fullImportPlugin()
+    )
+  } else {
+    config.plugins.push(AutoImport({
+      resolvers: [ElementPlusResolver()]
+    }),
+    Components({
+      resolvers: [ElementPlusResolver({
+        importStyle: 'sass'
+      })]
+    }))
+  }
+  return config
+}