liuhairui 2 tygodni temu
rodzic
commit
2422b014d0

+ 78 - 16
src/api/yunyin/product.js

@@ -59,10 +59,79 @@ export const ProductPartList = (params) => {
     })
 }
 
-//============产品部件库管理=================
+/** 产品明细 */
+export const ProductDetail = (params) => {
+    return service({
+        url: '/mes_server/Product/ProductDetail',
+        method: 'get',
+        params
+    })
+}
+
+//============产品资料管理--产品下的部件资料=============
+//新增部件资料
+export const PartAdd = (data) => {
+    return service({
+        url: '/mes_server/Product/PartAdd',
+        method: 'post',
+        data
+    })
+}
+//修改部件资料
+export const PartEdit = (data) => {
+    return service({
+        url: '/mes_server/Product/PartEdit',
+        method: 'post',
+        data
+    })
+}
+//删除部件资料
+export const PartDelete = (data) => {
+    return service({
+        url: '/mes_server/Product/PartDelete',
+        method: 'post',
+        data
+    })
+}
+
+//============产品资料管理-产品下的工艺资料=============
+//新增工艺资料
+export const ProcessAdd = (data) => {
+    return service({
+        url: '/mes_server/Product/ProcessAdd',
+        method: 'post',
+        data
+    })
+}
+//修改工艺资料
+export const ProcessEdit = (data) => {
+    return service({
+        url: '/mes_server/Product/ProcessEdit',
+        method: 'post',
+        data
+    })
+}
+//删除工艺资料
+export const ProcessDelete = (data) => {
+    return service({
+        url: '/mes_server/Product/ProcessDelete',
+        method: 'post',
+        data
+    })
+}
+
+//============新增部件工艺【Excel导入】=================
+export const ProcessExcel = (data) => {
+    return service({
+        url: '/mes_server/Product/ProcessExcel',
+        method: 'post',
+        data
+    })
+}
+
+//============产品部件库(Part_Lib,下拉/部件库页 ProductPar)=============
 
-//新增产品部件库
-export const ParAdd = (data) => {
+export const ParLibAdd = (data) => {
     return service({
         url: '/mes_server/Part_Lib/ParAdd',
         method: 'post',
@@ -70,7 +139,6 @@ export const ParAdd = (data) => {
     })
 }
 
-//获取产品部件库
 export const ParList = (params) => {
     return service({
         url: '/mes_server/Part_Lib/ParList',
@@ -79,8 +147,7 @@ export const ParList = (params) => {
     })
 }
 
-//修改产品部件库
-export const ParEdit = (data) => {
+export const ParLibEdit = (data) => {
     return service({
         url: '/mes_server/Part_Lib/ParEdit',
         method: 'post',
@@ -88,8 +155,7 @@ export const ParEdit = (data) => {
     })
 }
 
-//删除产品部件库
-export const ParDelete = (data) => {
+export const ParLibDelete = (data) => {
     return service({
         url: '/mes_server/Part_Lib/ParDelete',
         method: 'post',
@@ -97,10 +163,9 @@ export const ParDelete = (data) => {
     })
 }
 
-//============产品工艺库管理=================
+//============产品工艺库(Process_Lib,下拉/工艺库页 ProductProcess)=============
 
-//新增产品工艺库
-export const ProcessAdd = (data) => {
+export const ProcessLibAdd = (data) => {
     return service({
         url: '/mes_server/Process_Lib/ProcessAdd',
         method: 'post',
@@ -108,7 +173,6 @@ export const ProcessAdd = (data) => {
     })
 }
 
-//获取产品工艺库
 export const ProcessList = (params) => {
     return service({
         url: '/mes_server/Process_Lib/ProcessList',
@@ -117,8 +181,7 @@ export const ProcessList = (params) => {
     })
 }
 
-//修改产品工艺库
-export const ProcessEdit = (data) => {
+export const ProcessLibEdit = (data) => {
     return service({
         url: '/mes_server/Process_Lib/ProcessEdit',
         method: 'post',
@@ -126,8 +189,7 @@ export const ProcessEdit = (data) => {
     })
 }
 
-//删除产品工艺库
-export const ProcessDelete = (data) => {
+export const ProcessLibDelete = (data) => {
     return service({
         url: '/mes_server/Process_Lib/ProcessDelete',
         method: 'post',

+ 386 - 75
src/view/yunyin/product/ProductPar.vue

@@ -1,28 +1,34 @@
 <template>
-  <div>
+  <div class="yunyin-hr-split">
     <layout>
-      <layout-header>
-        <div class="product-header">
-          <el-input
-            v-model="searchKeyword"
-            placeholder="搜索部件代号或名称"
-            clearable
-            style="width: 220px; margin: 5px"
-            @keyup.enter="onSearch"
-            @clear="onSearch"
-          />
-          <el-button type="primary" icon="Search" style="margin: 5px" @click="onSearch">查询</el-button>
-          <el-button type="primary" icon="Plus" style="margin: 5px" @click="onAdd">新增</el-button>
+      <layout-header class="yunyin-page-header">
+        <div>
+          <el-form class="demo-form-inline" @keyup.enter="onSearch">
+            <el-form-item>
+              <el-input
+                v-model="searchKeyword"
+                placeholder="搜索部件名称"
+                clearable
+                class="search"
+                style="width: 220px"
+                @keyup.enter="onSearch"
+                @clear="onSearch"
+              />
+              <el-button type="primary" icon="Search" class="bt" @click="onSearch">查询</el-button>
+              <el-button type="primary" icon="Plus" class="bt" @click="onAdd">新增</el-button>
+            </el-form-item>
+          </el-form>
         </div>
       </layout-header>
 
-      <layout-content>
-        <el-main class="product-main">
+      <layout-content >
+        <el-main class="yunyin-main-block">
           <div class="gva-table-box">
             <el-table
+              class="product-par-main-table"
               :data="tableData"
               border
-              size="small"
+              tooltip-effect="dark"
               style="width: 100%; height: 60vh"
               :row-style="{ height: '20px' }"
               :header-cell-style="{ padding: '0px' }"
@@ -30,19 +36,21 @@
               :header-row-style="{ height: '20px' }"
               row-key="id"
               :show-overflow-tooltip="true"
+              highlight-current-row
+              @row-dblclick="onTableRowDblclick"
             >
-              <el-table-column align="center" label="部件编码" prop="部件编码" width="90" />
+              <el-table-column align="center" label="序号" prop="id" width="100" />
               <el-table-column align="left" label="部件名称" prop="部件名称" width="100" show-overflow-tooltip />
               <el-table-column align="center" label="操作人" prop="sys_id" width="100" />
-              <el-table-column align="center" label="创建时间" prop="创建时间" width="115" />
-              <el-table-column align="center" label="修改时间" prop="修改时间" width="120" />
-              <el-table-column align="center" label="操作" width="140">
+              <el-table-column align="center" label="创建时间" prop="创建时间" width="160" />
+              <el-table-column align="center" label="修改时间" prop="修改时间" width="160" />
+              <el-table-column align="center" label="操作" width="160">
                 <template #default="{ row }">
                   <el-button type="primary" size="small" @click="onEdit(row)">编辑</el-button>
                   <el-button type="danger" size="small" @click="onDelete(row)">删除</el-button>
                 </template>
               </el-table-column>
-            </el-table> 
+            </el-table>
             <div class="gva-pagination">
               <el-pagination
                 v-model:current-page="page"
@@ -61,44 +69,86 @@
       </layout-content>
     </layout>
 
-    <!-- 新增弹窗 -->
     <el-dialog
       v-model="dialogVisible"
-      title="新增部件"
-      width="400px"
+      :title="isEdit ? '修改部件' : '新增部件'"
+      :width="isEdit ? '440px' : PART_ADD_DIALOG_WIDTH"
+      align-center
+      destroy-on-close
+      append-to-body
+      :class="{ 'par-part-unified-dialog': !isEdit, 'par-part-batch-dialog': !isEdit }"
+      @closed="resetForm"
     >
-      <el-form :model="formData" label-width="80px">
-        <el-form-item label="部件信息">
-          <el-input
-            v-model="formData.par"
-            placeholder="请输入部件信息"
-            style="width: 100%"
-          />
+      <!-- 修改:单字段表单 -->
+      <el-form
+        v-if="isEdit"
+        ref="formRef"
+        :model="formData"
+        :rules="formRules"
+        label-width="100px"
+        class="par-dialog-form"
+      >
+        <el-form-item label="部件名称" prop="部件名称">
+          <el-input v-model="formData['部件名称']" placeholder="请输入部件名称" clearable />
         </el-form-item>
       </el-form>
+
+      <!-- 新增:仅手输部件名称,批量 ParLibAdd;无下拉、无编码列 -->
+      <div v-else class="par-part-add-batch">
+        <p class="par-part-hint">输入时自动增行;按回车跳到下一行输入。</p>
+        <el-table
+          :data="partAddRows"
+          border
+          class="par-part-add-table"
+          :height="partBatchTableHeight"
+          :row-key="(row) => row._key"
+        >
+          <el-table-column label="部件名称" min-width="420">
+            <template #default="{ row, $index }">
+              <el-input
+                :ref="(el) => setPartNameInputRef(el, row._key)"
+                v-model="row['部件名称']"
+                class="par-part-name-input"
+                placeholder="请输入部件名称"
+                clearable
+                maxlength="128"
+                @input="tryAppendPartLibRow($index)"
+                @keydown.enter.prevent="onPartNameEnter($index)"
+              />
+            </template>
+          </el-table-column>
+          <el-table-column label="操作" width="88" align="center" fixed="right">
+            <template #default="{ row, $index }">
+              <el-button type="danger" link size="small" @click="removePartLibRow(row, $index)">移除</el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+        <div class="par-part-meta">将提交 {{ partLibAddSubmitCount }} 条(已去重)</div>
+      </div>
+
       <template #footer>
-        <span class="dialog-footer">
-          <el-button @click="dialogVisible = false">取消</el-button>
-          <el-button type="primary" @click="handleAdd">确定</el-button>
-        </span>
+        <el-button @click="dialogVisible = false">取消</el-button>
+        <el-button type="primary" :loading="submitLoading" @click="submitForm">确定</el-button>
       </template>
     </el-dialog>
   </div>
 </template>
 
 <script setup>
-import { Layout, LayoutContent } from '@arco-design/web-vue'
-import { ref, onMounted } from 'vue'
-import { ParList, ParDelete, ParAdd } from '@/api/yunyin/product'
- import { useUserStore } from '@/pinia/modules/user';
+import { Layout, LayoutHeader, LayoutContent } from '@arco-design/web-vue'
+import { ref, reactive, computed, onMounted, nextTick } from 'vue'
+import { ParList, ParLibDelete, ParLibAdd, ParLibEdit } from '@/api/yunyin/product'
+import { useUserStore } from '@/pinia/modules/user'
 import { ElMessage, ElMessageBox } from 'element-plus'
 
 defineOptions({ name: 'ProductPar' })
-//获取登录用户信息
+
+/** 部件库:列表查询、批量手输名称新增(ParLibAdd)、编辑、删除 */
+
 const userStore = useUserStore()
-const _username = ref('')
-_username.value = userStore.userInfo.userName + '/' + userStore.userInfo.nickName
-console.log('获取用户名称',_username.value)
+
+const PART_ADD_DIALOG_WIDTH = 'min(720px, 94vw)'
+const partBatchTableHeight = 480
 
 const tableData = ref([])
 const total = ref(0)
@@ -106,12 +156,133 @@ const page = ref(1)
 const pageSize = ref(30)
 const searchKeyword = ref('')
 
-// 弹窗相关
 const dialogVisible = ref(false)
-const formData = ref({
-  par: ''
+const isEdit = ref(false)
+const submitLoading = ref(false)
+const formRef = ref(null)
+
+const MAX_BATCH = 300
+
+let partLibRowSeed = 0
+function newPartLibRow() {
+  partLibRowSeed += 1
+  return {
+    _key: `p_${Date.now()}_${partLibRowSeed}`,
+    部件名称: '',
+  }
+}
+
+const partAddRows = ref([newPartLibRow()])
+
+const partLibAddSubmitCount = computed(() => {
+  const seen = new Set()
+  let n = 0
+  for (const r of partAddRows.value) {
+    const name = String(r['部件名称'] ?? '').trim()
+    if (!name || seen.has(name)) continue
+    seen.add(name)
+    n += 1
+  }
+  return n
 })
 
+function collectPartLibBatchItems() {
+  const seen = new Set()
+  const list = []
+  for (const r of partAddRows.value) {
+    const name = String(r['部件名称'] ?? '').trim()
+    if (!name || seen.has(name)) continue
+    seen.add(name)
+    list.push({ part_name: name })
+  }
+  return list
+}
+
+/** el-input 实例,按 row._key 存,便于回车聚焦下一行 */
+const partNameInputRefMap = new Map()
+function setPartNameInputRef(el, key) {
+  if (!key) return
+  if (el) partNameInputRefMap.set(key, el)
+  else partNameInputRefMap.delete(key)
+}
+
+function focusPartNameInputAtIndex(index) {
+  const row = partAddRows.value[index]
+  if (!row) return
+  nextTick(() => {
+    const inst = partNameInputRefMap.get(row._key)
+    if (inst && typeof inst.focus === 'function') inst.focus()
+  })
+}
+
+/** 回车:跳到下一行;若无下一行则先增行再聚焦(与输入自动增行配合) */
+function onPartNameEnter(rowIndex) {
+  tryAppendPartLibRow(rowIndex)
+  if (rowIndex + 1 < partAddRows.value.length) {
+    focusPartNameInputAtIndex(rowIndex + 1)
+    return
+  }
+  addPartLibRow()
+  focusPartNameInputAtIndex(partAddRows.value.length - 1)
+}
+
+function addPartLibRow() {
+  partAddRows.value.push(newPartLibRow())
+}
+
+function tryAppendPartLibRow(rowIndex) {
+  if (rowIndex !== partAddRows.value.length - 1) return
+  const row = partAddRows.value[rowIndex]
+  if (!row) return
+  if (!String(row['部件名称'] ?? '').trim()) return
+  addPartLibRow()
+}
+
+function removePartLibRow(row, index) {
+  const label = String(row?.['部件名称'] ?? '').trim() || '该行'
+  if (partAddRows.value.length <= 1) {
+    ElMessage.warning('至少保留一行')
+    return
+  }
+  ElMessageBox.confirm(`确定要删除【${label}】吗?`, '提示', {
+    confirmButtonText: '确定',
+    cancelButtonText: '取消',
+    type: 'warning',
+  })
+    .then(() => {
+      partAddRows.value.splice(index, 1)
+    })
+    .catch(() => {})
+}
+
+const formData = reactive({
+  id: undefined,
+  部件名称: '',
+})
+
+const formRules = {
+  部件名称: [{ required: true, message: '请输入部件名称', trigger: 'blur' }],
+}
+
+const resetForm = () => {
+  formData.id = undefined
+  formData['部件名称'] = ''
+  isEdit.value = false
+  partAddRows.value = [newPartLibRow()]
+  formRef.value?.clearValidate?.()
+}
+
+/** 编辑:part_name + id;新增走 submitBatch,不用此项 */
+const buildEditPayload = () => {
+  const sysId = userStore.userInfo?.nickName || userStore.userInfo?.userName || ''
+  const part_name = String(formData['部件名称'] ?? '').trim()
+  return {
+    id: formData.id,
+    part_name,
+    sys_id: sysId,
+  }
+}
+
 const fetchData = async () => {
   try {
     const params = {
@@ -125,7 +296,7 @@ const fetchData = async () => {
 
     const res = await ParList(params)
     if (res?.code !== 0) {
-      ElMessage.error(res?.msg || '获取部件资料失败')
+      ElMessage.error(res?.msg || '加载部件列表失败')
       tableData.value = []
       total.value = 0
       return
@@ -138,7 +309,7 @@ const fetchData = async () => {
     console.error(e)
     tableData.value = []
     total.value = 0
-    ElMessage.error('获取部件资料失败')
+    ElMessage.error('加载部件列表失败')
   }
 }
 
@@ -153,53 +324,98 @@ const onPageSizeChange = () => {
 }
 
 const onAdd = () => {
-  formData.value = {
-    par: ''
-  }
+  resetForm()
   dialogVisible.value = true
 }
 
-const handleAdd = async () => {
-  try {
-    const par = formData.value.par.trim()
-    if (!par) {
-      ElMessage.error('请输入部件信息')
-      return
-    }
+const onEdit = (row) => {
+  isEdit.value = true
+  formData.id = row.id
+  formData['部件名称'] = row['部件名称'] ?? row.part_name ?? ''
+  dialogVisible.value = true
+}
 
-    const params = {
-      par: par,
-      sys_id: userStore.userInfo.nickName
-    }
+const onTableRowDblclick = (row) => {
+  if (!row) return
+  onEdit(row)
+}
 
-    const res = await ParAdd(params)
+const submitBatch = async () => {
+  const part_list = collectPartLibBatchItems()
+  if (part_list.length === 0) {
+    ElMessage.warning('请至少输入一行部件名称')
+    return
+  }
+  if (part_list.length > MAX_BATCH) {
+    ElMessage.warning(`单次最多提交 ${MAX_BATCH} 条,请分批添加`)
+    return
+  }
+  const sysId = userStore.userInfo?.nickName || userStore.userInfo?.userName || ''
+  submitLoading.value = true
+  try {
+    const res = await ParLibAdd({ sys_id: sysId, part_list })
     if (res?.code !== 0) {
-      ElMessage.error(res?.msg || '新增失败')
+      ElMessage.error(res?.msg || '批量新增失败')
       return
     }
-
-    ElMessage.success('新增成功')
+    ElMessage.success(`已提交 ${part_list.length} 条`)
     dialogVisible.value = false
     fetchData()
   } catch (e) {
     console.error(e)
-    ElMessage.error('新增失败')
+    ElMessage.error('批量新增请求失败')
+  } finally {
+    submitLoading.value = false
   }
 }
 
-const onEdit = (row) => {
-  ElMessage.info('编辑功能待开发')
+const submitForm = async () => {
+  if (isEdit.value) {
+    const form = formRef.value
+    if (!form) return
+    try {
+      await form.validate()
+    } catch {
+      return
+    }
+    const payload = buildEditPayload()
+    if (!payload.part_name) {
+      ElMessage.warning('请输入部件名称')
+      return
+    }
+    submitLoading.value = true
+    try {
+      const res = await ParLibEdit(payload)
+      if (res?.code !== 0) {
+        ElMessage.error(res?.msg || '修改失败')
+        return
+      }
+      ElMessage.success('修改成功')
+      dialogVisible.value = false
+      fetchData()
+    } catch (e) {
+      console.error(e)
+      ElMessage.error('修改失败')
+    } finally {
+      submitLoading.value = false
+    }
+    return
+  }
+
+  await submitBatch()
 }
 
 const onDelete = async (row) => {
+  const partLabel =
+    String(row?.['部件名称'] ?? row?.part_name ?? '').trim()
   try {
-    await ElMessageBox.confirm('确定要删除该部件资料吗?', '提示', {
+    await ElMessageBox.confirm(`确定要删除【${partLabel}】吗?`, '提示', {
       confirmButtonText: '确定',
       cancelButtonText: '取消',
       type: 'warning',
     })
 
-    const res = await ParDelete({ id: row.id })
+    const res = await ParLibDelete({ id: row.id })
     if (res?.code !== 0) {
       ElMessage.error(res?.msg || '删除失败')
       return
@@ -220,15 +436,110 @@ onMounted(() => {
 </script>
 
 <style scoped>
-.product-header {
-  padding: 4px 0;
+.yunyin-hr-split :deep(.arco-layout-content) {
+  padding-top: 0 !important;
+  padding-bottom: 0 !important;
 }
-.product-main {
-  padding-bottom: 8px;
+.yunyin-main-block {
+  padding: 10px 8px 8px 8px !important;
+  box-sizing: border-box;
+}
+.yunyin-page-header :deep(.arco-layout-header) {
+  padding: 12px 10px;
+  margin-bottom: 12px;
+  box-sizing: border-box;
+}
+.search {
+  margin-left: 0 !important;
+  margin-right: 10px !important;
+}
+.bt {
+  margin-left: 2px !important;
+  padding: 3px !important;
+  font-size: 12px;
+}
+.gva-table-box {
+  padding: 0 !important;
+}
+:deep(.el-table td .cell) {
+  line-height: 20px !important;
 }
 .gva-pagination {
   margin-top: 5px;
   display: flex;
   justify-content: flex-end;
 }
+.par-dialog-form :deep(.el-input) {
+  width: 100%;
+}
+/* 新增部件批量:仅名称列,弹窗略窄 */
+.par-part-batch-dialog :deep(.el-dialog__body) {
+  padding-top: 12px;
+  padding-bottom: 8px;
+}
+.par-part-unified-dialog.par-part-batch-dialog :deep(.el-dialog) {
+  width: min(720px, 94vw) !important;
+  min-width: min(720px, 94vw) !important;
+  max-width: min(720px, 94vw) !important;
+  height: min(560px, 85vh) !important;
+  min-height: min(560px, 85vh) !important;
+  max-height: min(560px, 85vh) !important;
+  box-sizing: border-box !important;
+  display: flex !important;
+  flex-direction: column !important;
+}
+.par-part-unified-dialog :deep(.el-dialog__header) {
+  flex-shrink: 0;
+}
+.par-part-unified-dialog :deep(.el-dialog__body) {
+  flex: 1;
+  min-height: 0;
+  overflow: hidden;
+  display: flex;
+  flex-direction: column;
+}
+.par-part-unified-dialog :deep(.el-dialog__footer) {
+  flex-shrink: 0;
+}
+.par-part-add-batch {
+  margin-top: 0;
+  flex: 1;
+  min-height: 0;
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+}
+.par-part-hint {
+  margin: 0 0 10px;
+  font-size: 15px;
+  line-height: 1.55;
+  color: var(--el-color-danger);
+}
+.par-part-meta {
+  margin-top: 8px;
+  font-size: 12px;
+  color: var(--el-text-color-secondary);
+}
+.par-part-add-table :deep(.par-part-name-input) {
+  width: 100%;
+}
+/* 选中行高亮:需 highlight-current-row;用官方变量覆盖(与 tr.current-row > td.el-table__cell 一致) */
+.product-par-main-table {
+  --el-table-current-row-bg-color: #ff80ff;
+}
+/*
+ * 悬停时颜色被盖住的原因:
+ * 1) tr.hover-row.current-row 使用 table-row-hover-bg-color
+ * 2) enable-row-hover 下 tr:hover 也会铺悬停底色,且可能后加载、盖过 current-row
+ * 以下统一强制:当前行在鼠标移入时仍用选中色(含主表体与固定列副本)
+ */
+.product-par-main-table :deep(.el-table__body tr.el-table__row.current-row:hover > td.el-table__cell),
+.product-par-main-table :deep(.el-table__body tr.el-table__row.hover-row.current-row > td.el-table__cell),
+.product-par-main-table :deep(.el-table__body tr.el-table__row--striped.current-row:hover > td.el-table__cell),
+.product-par-main-table :deep(.el-table__body tr.el-table__row--striped.hover-row.current-row > td.el-table__cell),
+.product-par-main-table :deep(.el-table__fixed-body-wrapper .el-table__body tr.el-table__row.current-row:hover > td.el-table__cell),
+.product-par-main-table :deep(.el-table__fixed-body-wrapper .el-table__body tr.el-table__row.hover-row.current-row > td.el-table__cell) {
+  background-color: #ff80ff !important;
+}
+
 </style>

+ 705 - 82
src/view/yunyin/product/ProductProcess.vue

@@ -1,25 +1,31 @@
 <template>
-  <div>
+  <div ref="processPageRootRef" class="yunyin-hr-split product-process-page">
     <layout>
-      <layout-header>
-        <div class="product-header">
-          <el-input
-            v-model="searchKeyword"
-            placeholder="搜索工艺编码或名称"
-            clearable
-            style="width: 220px; margin: 5px"
-            @keyup.enter="onSearch"
-            @clear="onSearch"
-          />
-          <el-button type="primary" icon="Search" style="margin: 5px" @click="onSearch">查询</el-button>
-          <el-button type="primary" icon="Plus" style="margin: 5px" @click="onAdd">新增</el-button>
+      <layout-header class="yunyin-page-header">
+        <div>
+          <el-form class="demo-form-inline" @keyup.enter="onSearch">
+            <el-form-item>
+              <el-input
+                v-model="searchKeyword"
+                placeholder="搜索工艺名称"
+                clearable
+                class="search"
+                style="width: 220px"
+                @keyup.enter="onSearch"
+                @clear="onSearch"
+              />
+              <el-button type="primary" icon="Search" class="bt" @click="onSearch">查询</el-button>
+              <el-button type="primary" icon="Plus" class="bt" @click="onAdd">新增</el-button>
+            </el-form-item>
+          </el-form>
         </div>
       </layout-header>
 
       <layout>
-        <layout-sider :resize-directions="['right']" :width="200" style="margin-right: 10px">
-          <div class="product-menu-wrap">
-            <h3 class="product-menu-title">工序</h3>
+        <!-- 左侧树(与 gongdanziliao:侧栏 margin:0) -->
+        <layout-sider :resize-directions="['right']" :width="200" style="margin: 0px">
+          <div class="product-menu-wrap jkw-tree-like">
+            <h3 class="product-menu-title">工序分类</h3>
             <el-tree
               ref="menuTreeRef"
               :data="menuTreeData"
@@ -37,37 +43,44 @@
           </div>
         </layout-sider>
 
-        <layout-content>
-          <el-main class="product-main">
-            <div class="gva-table-box">
-              <el-table
+        <!-- 右侧区域 -->
+        <layout-content >
+          <el-main class="yunyin-main-block">
+            <div class="gva-table-box process-table-panel">
+              <div ref="listTableWrapRef" class="process-list-table-wrap">
+                <el-table
+                class="product-process-main-table"
                 :data="tableData"
                 border
-                size="small"
-                style="width: 100%; height: 60vh"
+                tooltip-effect="dark"
+                :height="listTableHeight"
+                style="width: 100%"
                 :row-style="{ height: '20px' }"
+                @row-dblclick="onTableRowDblclick"
                 :header-cell-style="{ padding: '0px' }"
                 :cell-style="{ padding: '0px' }"
                 :header-row-style="{ height: '20px' }"
                 row-key="id"
                 :show-overflow-tooltip="true"
+                highlight-current-row
               >
-                <el-table-column align="center" label="工艺编码" prop="工艺编码" width="100" />
-                <el-table-column align="left" label="工艺名称" prop="工艺名称" width="160" show-overflow-tooltip />
-                <el-table-column align="center" label="生产工序" prop="生产工序" width="90" />
-                <el-table-column align="center" label="标准工时" prop="标准工时" width="80" />
-                <el-table-column align="center" label="标准工分" prop="标准工分" width="80" />
-                <el-table-column align="center" label="操作人" prop="sys_id" width="90" />
-                <el-table-column align="center" label="创建时间" prop="创建时间" width="115" />
-                <el-table-column align="center" label="修改时间" prop="修改时间" width="115" />
-                <el-table-column align="center" label="操作" width="130">
+                <el-table-column align="center" label="序号" prop="id" width="100" />
+                <el-table-column align="left" label="工艺名称" prop="gy_name" width="300" show-overflow-tooltip />
+                <el-table-column align="center" label="生产工序" prop="big_process" width="110" />
+                <el-table-column align="center" label="标准工时" prop="standard_hour" width="110" />
+                <el-table-column align="center" label="标准工价" prop="standard_score" width="110" />
+                <el-table-column align="center" label="操作人" prop="sys_id" width="110" />
+                <el-table-column align="center" label="创建时间" prop="创建时间" width="160" />
+                <el-table-column align="center" label="修改时间" prop="修改时间" width="160" />
+                <el-table-column align="center" label="操作" width="160">
                   <template #default="{ row }">
                     <el-button type="primary" size="small" @click="onEdit(row)">编辑</el-button>
                     <el-button type="danger" size="small" @click="onDelete(row)">删除</el-button>
                   </template>
                 </el-table-column>
               </el-table>
-              <div class="gva-pagination">
+              </div>
+              <div class="gva-pagination process-pagination-bar">
                 <el-pagination
                   v-model:current-page="page"
                   v-model:page-size="pageSize"
@@ -85,57 +98,154 @@
         </layout-content>
       </layout>
     </layout>
+
+    <el-dialog
+      v-model="dialogVisible"
+      :title="isEdit ? '修改工艺' : '新增工艺'"
+      :width="isEdit ? '560px' : 'min(1200px, 96vw)'"
+      align-center
+      destroy-on-close
+      append-to-body
+      :class="{ 'process-add-dialog': !isEdit }"
+      @closed="resetForm"
+    >
+      <el-form
+        v-if="isEdit"
+        ref="formRef"
+        :model="formData"
+        :rules="formRules"
+        label-width="100px"
+        class="process-dialog-form"
+      >
+        <el-form-item label="工艺名称" prop="工艺名称">
+          <el-input v-model="formData['工艺名称']" placeholder="请输入工艺名称" clearable />
+        </el-form-item>
+        <el-form-item label="生产工序" prop="生产工序">
+          <el-select v-model="formData['生产工序']" placeholder="请选择生产工序" clearable filterable style="width: 100%">
+            <el-option v-for="opt in processSelectOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="标准工时" prop="标准工时">
+          <el-input-number v-model="formData['标准工时']" :min="0" :precision="2" :step="0.5" controls-position="right" style="width: 100%" />
+        </el-form-item>
+        <el-form-item label="标准工价" prop="标准工价">
+          <el-input-number v-model="formData['标准工价']" :min="0" :precision="2" :step="0.5" controls-position="right" style="width: 100%" />
+        </el-form-item>
+      </el-form>
+
+      <!-- 新增 -->
+      <div v-else class="process-add-batch">
+        <p class="process-add-hint">
+          一行一条工艺
+        </p>
+        <div class="process-add-toolbar">
+          <el-button type="primary" size="small" @click="addProcessRow">新增一行</el-button>
+        </div>
+        <el-table
+          :data="addRows"
+          border
+          class="process-add-table"
+          :height="addProcessTableHeight"
+          :row-key="(row) => row._key"
+        >
+          <el-table-column label="工艺名称" min-width="400">
+            <template #default="{ row, $index }">
+              <el-input
+                v-model="row['工艺名称']"
+                class="process-add-field-full"
+                placeholder="请输入工艺名称"
+                clearable
+                maxlength="128"
+                style="width: 100%"
+                @blur="onProcessNameBlur($index)"
+                @keydown.enter.prevent="onProcessNameEnter($index)"
+              />
+            </template>
+          </el-table-column>
+          <el-table-column label="生产工序" width="168">
+            <template #default="{ row }">
+              <el-select
+                v-model="row['生产工序']"
+                class="process-add-field-full"
+                placeholder="请选择"
+                clearable
+                filterable
+                style="width: 100%"
+              >
+                <el-option v-for="opt in processSelectOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
+              </el-select>
+            </template>
+          </el-table-column>
+          <el-table-column label="标准工时" width="140">
+            <template #default="{ row }">
+              <el-input-number
+                v-model="row['标准工时']"
+                class="process-add-field-full"
+                :min="0"
+                :precision="2"
+                :step="0.5"
+                controls-position="right"
+                style="width: 100%"
+              />
+            </template>
+          </el-table-column>
+          <el-table-column label="标准工价" width="140">
+            <template #default="{ row }">
+              <el-input-number
+                v-model="row['标准工价']"
+                class="process-add-field-full"
+                :min="0"
+                :precision="2"
+                :step="0.5"
+                controls-position="right"
+                style="width: 100%"
+              />
+            </template>
+          </el-table-column>
+          <el-table-column label="操作" width="88" align="center" fixed="right">
+            <template #default="{ row, $index }">
+              <el-button type="danger" link size="small" @click="removeProcessRow(row, $index)">移除</el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+        <div class="process-add-meta">将提交 {{ addSubmitCount }} 条</div>
+      </div>
+
+      <template #footer>
+        <el-button @click="dialogVisible = false">取消</el-button>
+        <el-button type="primary" :loading="submitLoading" @click="submitForm">确定</el-button>
+      </template>
+    </el-dialog>
   </div>
 </template>
 
 <script setup>
-import { Layout, LayoutSider, LayoutContent } from '@arco-design/web-vue'
-import { ref, onMounted, nextTick } from 'vue'
-import { ProcessList, ProcessDelete } from '@/api/yunyin/product'
+import { Layout, LayoutHeader, LayoutSider, LayoutContent } from '@arco-design/web-vue'
+import { ref, reactive, computed, onMounted, onUnmounted, onActivated, onDeactivated, nextTick } from 'vue'
+import { ProcessList, ProcessLibDelete, ProcessLibAdd, ProcessLibEdit } from '@/api/yunyin/product'
+import { useUserStore } from '@/pinia/modules/user'
 import { ElMessage, ElMessageBox } from 'element-plus'
 
-defineOptions({ name: 'ProductProcess' }) 
+defineOptions({ name: 'ProductProcess' })
+
+const userStore = useUserStore()
 
 const menuTreeRef = ref(null)
 const treeProps = { label: 'label', children: 'children' }
 const menuTreeData = ref([
-  {
-    id: 'process-all',
-    label: '全部工序',
-    isAll: true
-  },
-  {
-    id: 'process-裁剪',
-    label: '裁剪',
-    code: '裁剪'
-  },
-  {
-    id: 'process-车缝',
-    label: '车缝',
-    code: '车缝'
-  },
-  {
-    id: 'process-手工',
-    label: '手工',
-    code: '手工'
-  },
-  {
-    id: 'process-大烫',
-    label: '大烫',
-    code: '大烫'
-  },
-  {
-    id: 'process-总检',
-    label: '总检',
-    code: '总检'
-  },
-  {
-    id: 'process-包装',
-    label: '包装',
-    code: '包装'
-  }
+  { id: 'process-all', label: '全部工序', isAll: true },
+  { id: 'process-裁剪', label: '裁剪', code: '裁剪' },
+  { id: 'process-车缝', label: '车缝', code: '车缝' },
+  { id: 'process-手工', label: '手工', code: '手工' },
+  { id: 'process-大烫', label: '大烫', code: '大烫' },
+  { id: 'process-总检', label: '总检', code: '总检' },
+  { id: 'process-包装', label: '包装', code: '包装' },
 ])
 
+const processSelectOptions = computed(() =>
+  menuTreeData.value.filter((d) => !d.isAll && d.code).map((d) => ({ label: d.label, value: d.code })),
+)
+
 const tableData = ref([])
 const total = ref(0)
 const page = ref(1)
@@ -143,6 +253,207 @@ const pageSize = ref(30)
 const searchKeyword = ref('')
 const selectedCode = ref(null)
 
+const dialogVisible = ref(false)
+const isEdit = ref(false)
+const submitLoading = ref(false)
+const formRef = ref(null)
+
+/** 页面根节点:高度对齐 layout 里 el-main 的可视高度,避免用 100vh 与嵌套区域不一致导致整页滚动 */
+const processPageRootRef = ref(null)
+/** 主列表表格可视高度(随剩余空间变化,仅表格内部滚动) */
+const listTableWrapRef = ref(null)
+const listTableHeight = ref(480)
+let listTableResizeObserver = null
+let mainElResizeObserver = null
+/** 进入本页时暂存 el-main 的 overflow,离开时还原(禁止主区域出现纵向滚动条) */
+let layoutMainEl = null
+let layoutMainOverflowSaved = ''
+
+/** 本页根高度 = el-main 底边到本根顶边的距离(不再用 min(clientHeight, geom),避免算大撑出滚动) */
+function syncProcessPageRootHeight() {
+  const root = processPageRootRef.value
+  if (!root) return
+  const main = root.closest('.el-main')
+  if (!main) return
+  const mainBr = main.getBoundingClientRect()
+  const rootBr = root.getBoundingClientRect()
+  let h = Math.floor(mainBr.bottom - rootBr.top - 10)
+  if (!Number.isFinite(h) || h < 120) {
+    const cs = getComputedStyle(main)
+    const padY = (parseFloat(cs.paddingTop) || 0) + (parseFloat(cs.paddingBottom) || 0)
+    h = Math.max(120, main.clientHeight - padY - 16)
+  }
+  root.style.height = `${h}px`
+  root.style.maxHeight = `${h}px`
+  root.style.minHeight = '0'
+  root.style.overflow = 'hidden'
+  nextTick(() => updateListTableHeight())
+}
+
+function updateListTableHeight() {
+  const el = listTableWrapRef.value
+  if (!el) return
+  const h = Math.floor(el.getBoundingClientRect().height)
+  if (h >= 120) listTableHeight.value = h
+}
+
+const formData = reactive({
+  id: undefined,
+  工艺名称: '',
+  生产工序: '',
+  标准工时: undefined,
+  标准工价: undefined,
+})
+
+const formRules = {
+  工艺名称: [{ required: true, message: '请输入工艺名称', trigger: 'blur' }],
+  生产工序: [{ required: true, message: '请选择生产工序', trigger: 'change' }],
+}
+
+const numOrNull = (v) => {
+  if (v === undefined || v === null || v === '') return undefined
+  const n = Number(v)
+  return Number.isFinite(n) ? n : undefined
+}
+
+/** 列表接口若返回 gy_code / gy_name 等,统一映射到表格用的中文字段 */
+function normalizeProcessListItem(row) {
+  if (!row || typeof row !== 'object') return row
+  return {
+    ...row,
+    工艺名称: row.gy_name ?? row['工艺名称'] ?? '',
+    生产工序: row.big_process ?? row['生产工序'] ?? '',
+    标准工时: row.standard_hour ?? row['标准工时'],
+    标准工价: row.standard_score ?? row['标准工价'] ?? row['标准工分'],
+  }
+}
+
+const MAX_PROCESS_BATCH = 300
+
+let processRowKeySeed = 0
+function newProcessRow() {
+  processRowKeySeed += 1
+  return {
+    _key: `gx_${Date.now()}_${processRowKeySeed}`,
+    工艺名称: '',
+    生产工序: selectedCode.value || '',
+    标准工时: undefined,
+    标准工价: undefined,
+  }
+}
+
+const addRows = ref([newProcessRow()])
+
+/** 新增弹窗内表格必须用 height(非 max-height),行数少时也会占满固定可视区域 */
+const addProcessTableHeight = ref(600)
+
+function syncAddProcessTableHeight() {
+  if (typeof window === 'undefined') return
+  addProcessTableHeight.value = Math.min(
+    780,
+    Math.max(420, Math.round(window.innerHeight * 0.52)),
+  )
+}
+
+const addSubmitCount = computed(() => {
+  let n = 0
+  for (const r of addRows.value) {
+    const name = String(r['工艺名称'] ?? '').trim()
+    const gx = String(r['生产工序'] ?? '').trim()
+    const empty = !name && !gx && r['标准工时'] == null && r['标准工价'] == null
+    if (empty) continue
+    if (name && gx) n += 1
+  }
+  return n
+})
+
+function addProcessRow() {
+  addRows.value.push(newProcessRow())
+}
+
+/** 最后一行且工艺名称非空时,自动追加一行空行(失焦或回车触发) */
+function tryAppendRowAfterNameInput(rowIndex) {
+  if (rowIndex !== addRows.value.length - 1) return
+  const row = addRows.value[rowIndex]
+  if (!row) return
+  if (!String(row['工艺名称'] ?? '').trim()) return
+  addProcessRow()
+}
+
+function onProcessNameBlur(rowIndex) {
+  tryAppendRowAfterNameInput(rowIndex)
+}
+
+function onProcessNameEnter(rowIndex) {
+  tryAppendRowAfterNameInput(rowIndex)
+}
+
+function removeProcessRow(row, index) {
+  const label =
+    String(row?.['工艺名称'] ?? '').trim() ||
+    String(row?.['生产工序'] ?? '').trim() ||
+    '该行'
+  if (addRows.value.length <= 1) {
+    ElMessage.warning('至少保留一行')
+    return
+  }
+  ElMessageBox.confirm(`确定要删除【${label}】吗?删除不可恢复`, '提示', {
+    confirmButtonText: '确定',
+    cancelButtonText: '取消',
+    type: 'warning',
+  })
+    .then(() => {
+      addRows.value.splice(index, 1)
+    })
+    .catch(() => {})
+}
+
+function collectProcessBatchList() {
+  const list = []
+  for (let i = 0; i < addRows.value.length; i++) {
+    const r = addRows.value[i]
+    const name = String(r['工艺名称'] ?? '').trim()
+    const gx = String(r['生产工序'] ?? '').trim()
+    const empty = !name && !gx && r['标准工时'] == null && r['标准工价'] == null
+    if (empty) continue
+    if (!name || !gx) {
+      return {
+        error: `第 ${i + 1} 行请同时填写工艺名称与生产工序,或清空该行`,
+      }
+    }
+    list.push({
+      gy_name: name,
+      big_process: gx,
+      standard_hour: numOrNull(r['标准工时']),
+      standard_score: numOrNull(r['标准工价']),
+    })
+  }
+  return { list }
+}
+
+const resetForm = () => {
+  formData.id = undefined
+  formData['工艺名称'] = ''
+  formData['生产工序'] = selectedCode.value || ''
+  formData['标准工时'] = undefined
+  formData['标准工价'] = undefined
+  isEdit.value = false
+  addRows.value = [newProcessRow()]
+  formRef.value?.clearValidate?.()
+}
+
+const buildPayload = () => {
+  const sysId = userStore.userInfo?.nickName || userStore.userInfo?.userName || ''
+  return {
+    id: formData.id,
+    gy_name: String(formData['工艺名称'] ?? '').trim(),
+    big_process: String(formData['生产工序'] ?? '').trim(),
+    standard_hour: numOrNull(formData['标准工时']),
+    standard_score: numOrNull(formData['标准工价']),
+    sys_id: sysId,
+  }
+}
+
 const fetchData = async () => {
   try {
     const params = {
@@ -165,9 +476,13 @@ const fetchData = async () => {
       return
     }
     const payload = res.data || {}
-    tableData.value = Array.isArray(payload.list) ? payload.list : []
+    const rawList = Array.isArray(payload.list) ? payload.list : []
+    tableData.value = rawList.map(normalizeProcessListItem)
     const c = payload.count ?? payload.total
     total.value = c != null ? Number(c) : tableData.value.length
+    await nextTick()
+    syncProcessPageRootHeight()
+    updateListTableHeight()
   } catch (e) {
     console.error(e)
     tableData.value = []
@@ -197,22 +512,111 @@ const onPageSizeChange = () => {
 }
 
 const onAdd = () => {
-  ElMessage.info('新增功能待开发')
+  isEdit.value = false
+  resetForm()
+  syncAddProcessTableHeight()
+  dialogVisible.value = true
+}
+
+const parseNum = (val) => {
+  if (val === undefined || val === null || val === '') return undefined
+  const n = Number(val)
+  return Number.isFinite(n) ? n : undefined
 }
 
 const onEdit = (row) => {
-  ElMessage.info('编辑功能待开发')
+  isEdit.value = true
+  formData.id = row.id
+  formData['工艺名称'] = row.gy_name ?? row['工艺名称'] ?? ''
+  formData['生产工序'] = row.big_process ?? row['生产工序'] ?? ''
+  formData['标准工时'] = parseNum(row.standard_hour ?? row['标准工时'])
+  formData['标准工价'] = parseNum(row.standard_score ?? row['标准工价'] ?? row['标准工分'])
+  dialogVisible.value = true
+}
+
+const onTableRowDblclick = (row) => {
+  if (!row) return
+  onEdit(row)
+}
+
+const submitForm = async () => {
+  if (isEdit.value) {
+    const form = formRef.value
+    if (!form) return
+    try {
+      await form.validate()
+    } catch {
+      return
+    }
+    const payload = buildPayload()
+    if (!payload.gy_code || !payload.gy_name || !payload.big_process) {
+      ElMessage.warning('请填写工艺名称并选择生产工序')
+      return
+    }
+    submitLoading.value = true
+    try {
+      const res = await ProcessLibEdit(payload)
+      if (res?.code !== 0) {
+        ElMessage.error(res?.msg || '修改失败')
+        return
+      }
+      ElMessage.success('修改成功')
+      dialogVisible.value = false
+      fetchData()
+    } catch (e) {
+      console.error(e)
+      ElMessage.error('修改失败')
+    } finally {
+      submitLoading.value = false
+    }
+    return
+  }
+
+  const batch = collectProcessBatchList()
+  if (batch.error) {
+    ElMessage.warning(batch.error)
+    return
+  }
+  const { list } = batch
+  if (!list.length) {
+    ElMessage.warning('请至少填写一行完整的工艺名称与生产工序')
+    return
+  }
+  if (list.length > MAX_PROCESS_BATCH) {
+    ElMessage.warning(`单次最多提交 ${MAX_PROCESS_BATCH} 条,请分批添加`)
+    return
+  }
+  const sysId = userStore.userInfo?.nickName || userStore.userInfo?.userName || ''
+  submitLoading.value = true
+  try {
+    const res = await ProcessAdd({ sys_id: sysId, process_list: list })
+    if (res?.code !== 0) {
+      ElMessage.error(res?.msg || '批量新增失败')
+      return
+    }
+    ElMessage.success(`已提交 ${list.length} 条`)
+    dialogVisible.value = false
+    fetchData()
+  } catch (e) {
+    console.error(e)
+    ElMessage.error('批量新增请求失败')
+  } finally {
+    submitLoading.value = false
+  }
 }
 
 const onDelete = async (row) => {
+  const delLabel =
+    String(row?.gy_name ?? row?.['工艺名称'] ?? '').trim()
+   
   try {
-    await ElMessageBox.confirm('确定要删除该工艺资料吗?', '提示', {
+    await ElMessageBox.confirm(`确定要删除【${delLabel}】吗?删除不可恢复`, '提示', {
       confirmButtonText: '确定',
       cancelButtonText: '取消',
       type: 'warning',
     })
 
-    const res = await ProcessDelete({ id: row.id })
+    const res = await ProcessLibDelete({ id: row.id })
     if (res?.code !== 0) {
       ElMessage.error(res?.msg || '删除失败')
       return
@@ -227,28 +631,183 @@ const onDelete = async (row) => {
   }
 }
 
+function onProcessWindowResize() {
+  syncProcessPageRootHeight()
+  syncAddProcessTableHeight()
+  updateListTableHeight()
+}
+
 onMounted(async () => {
+  syncAddProcessTableHeight()
+  window.addEventListener('resize', onProcessWindowResize)
   await nextTick()
+  lockLayoutMainOverflow()
   menuTreeRef.value?.setCurrentKey('process-all')
   fetchData()
+  await nextTick()
+  syncProcessPageRootHeight()
+  requestAnimationFrame(() => {
+    syncProcessPageRootHeight()
+    requestAnimationFrame(() => syncProcessPageRootHeight())
+  })
+  updateListTableHeight()
+  const mainEl = processPageRootRef.value?.closest?.('.el-main')
+  if (mainEl) {
+    mainElResizeObserver = new ResizeObserver(() => syncProcessPageRootHeight())
+    mainElResizeObserver.observe(mainEl)
+  }
+  if (listTableWrapRef.value) {
+    listTableResizeObserver = new ResizeObserver(() => updateListTableHeight())
+    listTableResizeObserver.observe(listTableWrapRef.value)
+  }
+})
+
+function restoreLayoutMainOverflow() {
+  if (layoutMainEl) {
+    layoutMainEl.style.overflow = layoutMainOverflowSaved
+  }
+}
+
+function lockLayoutMainOverflow() {
+  const main = processPageRootRef.value?.closest?.('.el-main')
+  if (!main) return
+  if (layoutMainEl !== main) {
+    layoutMainOverflowSaved = main.style.overflow || ''
+  }
+  layoutMainEl = main
+  main.style.overflow = 'hidden'
+}
+
+onUnmounted(() => {
+  window.removeEventListener('resize', onProcessWindowResize)
+  mainElResizeObserver?.disconnect()
+  mainElResizeObserver = null
+  listTableResizeObserver?.disconnect()
+  listTableResizeObserver = null
+  restoreLayoutMainOverflow()
+  layoutMainEl = null
+})
+
+onDeactivated(() => {
+  restoreLayoutMainOverflow()
+})
+
+onActivated(() => {
+  nextTick(() => {
+    lockLayoutMainOverflow()
+    syncProcessPageRootHeight()
+  })
 })
 </script>
 
 <style scoped>
-.product-header {
-  padding: 4px 0;
+/* 根节点必须是纵向 flex + 隐藏溢出,否则子级会按内容无限增高,el-main 出现整页滚动 */
+.yunyin-hr-split.product-process-page,
+.product-process-page.admin-box {
+  display: flex !important;
+  flex-direction: column !important;
+  box-sizing: border-box !important;
+  width: 100%;
+  max-width: 100%;
+  overflow: hidden !important;
+}
+.product-process-page > :deep(.arco-layout) {
+  flex: 1;
+  min-height: 0;
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+}
+.product-process-page :deep(.arco-layout-has-sider) {
+  flex: 1;
+  min-height: 0;
+  overflow: hidden;
+}
+.product-process-page :deep(.arco-layout-content) {
+  flex: 1;
+  min-height: 0;
+  overflow: hidden;
+  display: flex;
+  flex-direction: column;
+}
+.product-process-page .yunyin-main-block {
+  flex: 1;
+  min-height: 0;
+  overflow: hidden;
+  display: flex;
+  flex-direction: column;
+  padding: 0 !important;
+}
+.product-process-page .process-table-panel {
+  flex: 1;
+  min-height: 0;
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+}
+.product-process-page .process-list-table-wrap {
+  flex: 1;
+  min-height: 0;
+  overflow: hidden;
+}
+.product-process-page .process-pagination-bar {
+  flex-shrink: 0;
+}
+.product-process-page .product-menu-wrap {
+  min-height: 0 !important;
+  height: 100% !important;
+  max-height: 100% !important;
+  overflow-x: hidden;
+  overflow-y: auto;
+}
+.product-process-page .yunyin-page-header :deep(.arco-layout-header) {
+  margin-bottom: 8px;
+}
+.yunyin-hr-split :deep(.arco-layout-sider-children) {
+  padding-top: 0 !important;
+  padding-bottom: 10px !important;
+  box-sizing: border-box;
+}
+.yunyin-hr-split :deep(.arco-layout-content) {
+  padding-top: 0 !important;
+  padding-bottom: 0 !important;
+}
+.yunyin-main-block {
+  box-sizing: border-box;
+}
+.yunyin-page-header :deep(.arco-layout-header) {
+  padding: 12px 10px;
+  margin-bottom: 12px;
+  box-sizing: border-box;
+}
+.search {
+  margin-left: 0 !important;
+  margin-right: 10px !important;
+}
+.bt {
+  margin-left: 2px !important;
+  padding: 3px !important;
+  font-size: 12px;
+}
+.product-menu-wrap.jkw-tree-like {
+  margin-right: 20px;
 }
 .product-menu-wrap {
   background: #fff;
   padding: 10px;
+  box-sizing: border-box;
   min-height: 260px;
-  max-height: calc(100vh - 140px);
-  overflow: auto;
+  height: min(calc(80vh + 20px), calc(100vh - 200px));
+  max-height: min(calc(80vh + 20px), calc(100vh - 200px));
+  overflow-x: hidden;
+  overflow-y: auto;
 }
 .product-menu-title {
   font-size: 15px;
   font-weight: 700;
+  line-height: 1.2;
   margin: 0 0 10px;
+  padding: 0;
 }
 .tree-node-text {
   font-size: 13px;
@@ -260,12 +819,76 @@ onMounted(async () => {
   color: red;
   font-weight: 600;
 }
-.product-main {
-  padding-bottom: 8px;
+.gva-table-box {
+  padding: 0 !important;
+}
+:deep(.el-table td .cell) {
+  line-height: 20px !important;
 }
 .gva-pagination {
   margin-top: 5px;
   display: flex;
   justify-content: flex-end;
 }
+.process-dialog-form :deep(.el-input),
+.process-dialog-form :deep(.el-select) {
+  width: 100%;
+}
+.process-dialog-form :deep(.el-input-number) {
+  width: 100%;
+}
+/* 新增工艺:弹窗主体略放宽,避免表格固定高度时显得挤 */
+.process-add-dialog :deep(.el-dialog__body) {
+  padding-top: 12px;
+  padding-bottom: 8px;
+}
+.process-add-batch {
+  margin-top: 0;
+}
+.process-add-hint {
+  margin: 0 0 10px;
+  font-size: 13px;
+  line-height: 1.5;
+  color: var(--el-text-color-regular);
+}
+.process-add-toolbar {
+  margin-bottom: 10px;
+}
+.process-add-table :deep(.el-input),
+.process-add-table :deep(.el-select),
+.process-add-table :deep(.el-input-number) {
+  width: 100%;
+  max-width: 100%;
+}
+.process-add-field-full {
+  width: 100% !important;
+  max-width: 100%;
+  box-sizing: border-box;
+}
+.process-add-table :deep(.process-add-field-full .el-input__wrapper) {
+  width: 100%;
+  box-sizing: border-box;
+}
+.process-add-table :deep(.el-table__cell) {
+  padding: 8px 10px;
+}
+.process-add-meta {
+  margin-top: 8px;
+  font-size: 12px;
+  color: var(--el-text-color-secondary);
+}
+/* 选中行高亮:需 highlight-current-row;用官方变量覆盖 */
+.product-process-main-table {
+  --el-table-current-row-bg-color: #ff80ff;
+}
+/* 当前行 + 鼠标悬停:覆盖 EP 的 tr:hover / hover-row.current-row 悬停底色 */
+.product-process-main-table :deep(.el-table__body tr.el-table__row.current-row:hover > td.el-table__cell),
+.product-process-main-table :deep(.el-table__body tr.el-table__row.hover-row.current-row > td.el-table__cell),
+.product-process-main-table :deep(.el-table__body tr.el-table__row--striped.current-row:hover > td.el-table__cell),
+.product-process-main-table :deep(.el-table__body tr.el-table__row--striped.hover-row.current-row > td.el-table__cell),
+.product-process-main-table :deep(.el-table__fixed-body-wrapper .el-table__body tr.el-table__row.current-row:hover > td.el-table__cell),
+.product-process-main-table :deep(.el-table__fixed-body-wrapper .el-table__body tr.el-table__row.hover-row.current-row > td.el-table__cell) {
+  background-color: #ff80ff !important;
+}
+
 </style>

Plik diff jest za duży
+ 2253 - 163
src/view/yunyin/product/list.vue


Plik diff jest za duży
+ 860 - 141
src/view/yunyin/renliziyuan/GroupManagement.vue


+ 142 - 409
src/view/yunyin/renliziyuan/renyuanjibenziliao.vue

@@ -1,23 +1,22 @@
 <template>
-  <div>
+  <div class="yunyin-hr-split">
     <layout>
-      <layout-header>
-        <div class="">
-          <!--按钮部分-->
+      <layout-header class="yunyin-page-header">
+        <div>
           <el-form ref="elSearchFormRef" class="demo-form-inline" @keyup.enter="onSubmit">
             <el-form-item>
-              <el-input v-model="searchInfo" placeholder="搜索员工编号或员工姓名" clearable style="width: 200px;margin: 5px"></el-input>
-              <el-button type="primary" icon="search" @click="onSubmit" style="margin: 5px">查询</el-button>
-              <el-button type="primary" icon="Plus" @click="openAddDialog" style="margin: 5px">新增</el-button>
+              <el-input v-model="searchInfo" placeholder="查询员工编号或员工姓名" clearable class="search" style="width: 200px"></el-input>
+              <el-button type="primary" icon="search" class="bt" @click="onSubmit">查询</el-button>
+              <el-button type="primary" icon="Plus" class="bt" @click="openAddDialog">新增</el-button>
             </el-form-item>
           </el-form>
         </div>
       </layout-header>
 
       <layout>
-        <!-- 左侧树侧形结构-->
-        <layout-sider :resize-directions="['right']" :width="190" style="margin-right: 10px;">
-          <div class="JKWTree-tree" style="min-height: 260px; max-height: 420px; overflow: auto">
+        <!-- 左侧树(与 gongdanziliao:侧栏 margin:0) -->
+        <layout-sider :resize-directions="['right']" :width="190" style="margin: 0px">
+          <div class="JKWTree-tree staff-tree-panel">
             <h3>组织架构</h3>
             <el-tree
               ref="orgTreeRef"
@@ -40,28 +39,30 @@
 
         <!-- 右侧区域 -->
         <layout-content >
-        <el-main>
+        <el-main class="yunyin-main-block">
           <div class="gva-table-box">
             <!-- 表格数据 -->
-            <el-table ref="multipleTableRef" style="width: 100%;height: 65vh" tooltip-effect="dark"
-                      :row-style="{ height: '20px' }"  :header-cell-style="{ padding: '0px' }"
+            <el-table ref="multipleTableRef" style="width: 100%;height: 72vh" tooltip-effect="dark"
+                      :row-style="{ height: '30px' }" :header-cell-style="{ padding: '0px' }"
                       :cell-style="{ padding: '0px' }" :header-row-style="{ height: '20px' }"
-                      :data="tableData"  border row-key="员工编号"
-                      size="small"
+                      :data="tableData" border row-key="员工编号"
                       :highlight-current-row="true" @row-dblclick="updateCompanyFunc"
                       @row-click="tableRowClick" :show-overflow-tooltip="true"
                       >
-              <el-table-column  sortable align="left" label="员工编号" prop="员工编号"  width="100" />
-              <el-table-column  sortable align="left" label="员工姓名" prop="员工姓名"  width="100"/>
-              <el-table-column   align="left" label="性别" prop="性别"  width="80"/>
-              <el-table-column  sortable align="left" label="所在部门" prop="所在部门"  width="100"/>
-              <el-table-column  sortable align="left" label="所在小组" prop="所在小组"  width="100"/>
-              <el-table-column  sortable align="left" label="生产工序" prop="生产工序"  width="100"/>
-              <el-table-column  sortable align="left" label="职称职务" prop="职称职务"  width="100"/>
-              <el-table-column label="操作" width="140" align="center" fixed="right">
+              <el-table-column  sortable align="left" label="员工编号" prop="员工编号"  width="110" />
+              <el-table-column  sortable align="left" label="员工姓名" prop="员工姓名"  width="110"/>
+              <el-table-column  sortable align="left" label="性别" prop="性别"  width="80"/>
+              <!-- <el-table-column  sortable align="left" label="所在部门" prop="所在部门"  width="110"/> -->
+              <el-table-column  sortable align="left" label="生产工序" prop="生产工序"  width="110"/>
+              <el-table-column  sortable align="left" label="创建人员" prop="创建人员"  width="120"/>
+              <el-table-column  sortable align="left" label="创建日期" prop="创建日期"  width="160"/>
+              
+              <el-table-column label="操作" width="168" align="center" fixed="right">
                 <template #default="{ row }">
-                  <el-button type="primary" link size="small" @click.stop="onViewStaff(row)">查看</el-button>
-                  <el-button type="danger" link size="small" @click.stop="onDeleteStaff(row)">删除</el-button>
+                  <div class="table-op-btns">
+                    <el-button type="primary" size="small" @click.stop="onViewStaff(row)">编辑</el-button>
+                    <el-button type="danger" size="small" @click.stop="onDeleteStaff(row)">删除</el-button>
+                  </div>
                 </template>
               </el-table-column>
             </el-table>
@@ -81,144 +82,47 @@
             </div>
           </div>
 
-          <!-- 弹窗:新增与修改为同一套双栏(资料+已选工序 | 工序库) -->
+          <!-- 弹窗:新增/修改仅基础资料表单 -->
           <el-dialog
             v-model="dialogFormVisible"
             :before-close="closeDialog"
             :title="isAddMode ? '新增员工基本资料' : '修改员工基本资料'"
-            width="1120px"
+            width="520px"
             align-center
             destroy-on-close
             append-to-body
-            class="staff-dialog-wrap staff-dialog-wrap--add"
+            class="staff-dialog-wrap staff-dialog-form-simple"
           >
-              <el-row :gutter="16" class="add-staff-layout add-staff-layout--fixed">
-                <el-col :span="11">
-                  <el-form :model="formDataTest" label-position="right" label-width="100px" class="add-staff-form">
-                    <el-form-item label="员工编号" required>
-                      <el-input v-model="formDataTest['员工编号']" placeholder="员工编号" clearable />
-                    </el-form-item>
-					<el-form-item label="员工姓名" required>
-					  <el-input v-model="formDataTest['员工姓名']" placeholder="员工姓名" clearable />
-					</el-form-item>
-                    <el-form-item label="性别">
-                      <el-select v-model="formDataTest['性别']" placeholder="请选择性别" class="team-select-full">
-                        <el-option v-for="g in genderOptions" :key="g" :label="g" :value="g" />
-                      </el-select>
-                    </el-form-item>
-                    <!-- <el-form-item label="所在部门">
-                      <el-input v-model="formDataTest['所在部门']" placeholder="所在部门" clearable />
-                    </el-form-item> -->
-                    <el-form-item label="所在小组">
-                      <div class="team-and-process-row">
-                        <div class="team-select-wrap">
-                          <el-select
-                            v-model="formDataTest['所在小组']"
-                            placeholder="请选择所在小组"
-                            clearable
-                            filterable
-                            class="team-select-full"
-                            @change="onTeamChange"
-                          >
-                            <el-option
-                              v-for="(item, idx) in deviceTeamList"
-                              :key="'team-' + idx + '-' + item['设备编组']"
-                              :label="formatTeamOptionLabel(item)"
-                              :value="item['设备编组']"
-                            />
-                          </el-select>
-                        </div>
-                        <el-input
-                          :model-value="formDataTest['生产工序'] ?? ''"
-                          readonly
-                          placeholder="生产工序"
-                          class="team-process-display-input"
-                        />
-                      </div>
-                    </el-form-item>
-                    <el-form-item label="职务">
-                      <el-select v-model="formDataTest['职称职务']" placeholder="请选择职务" clearable class="team-select-full">
-                        <el-option v-for="p in positionOptions" :key="p" :label="p" :value="p" />
-                      </el-select>
-                    </el-form-item>
-                  </el-form>
-                  <div class="add-staff-selected-title">
-                    可做 {{ addSelectedProcesses.length }} 道工序
-                  </div>
-                  <el-table
-                    :data="addSelectedProcesses"
-                    border
-                    size="small"
-                    height="320"
-                    row-key="id"
-                    class="add-staff-selected-table"
-                    empty-text="在右侧勾选工序,将显示在此处"
-                  >
-                    <el-table-column prop="id" label="工序ID" width="70" align="center" show-overflow-tooltip />
-                    <el-table-column prop="工序编码" label="工序编码" min-width="96" show-overflow-tooltip />
-                    <el-table-column prop="工序名称" label="工序名称" min-width="120" show-overflow-tooltip />
-                    <el-table-column prop="生产工序" label="生产工序" width="80" show-overflow-tooltip />
-                    <el-table-column label="操作" width="72" align="center" fixed="right">
-                      <template #default="{ row }">
-                        <el-button type="danger" link size="small" @click="removeAddProcess(row)">移除</el-button>
-                      </template>
-                    </el-table-column>
-                  </el-table>
-                </el-col>
-                <el-col :span="13">
-                  <div class="add-staff-lib-head">
-                    <span class="add-staff-lib-title">工序库</span>
-                    <div class="add-staff-lib-search">
-                      <el-input
-                        v-model="processSearchKeyword"
-                        placeholder="搜索工序名称、代号、工序"
-                        clearable
-                        size="small"
-                        style="width: 220px"
-                        @keyup.enter="onProcessSearch"
-                        @clear="onProcessSearch"
-                      />
-                      <el-button type="primary" size="small" @click="onProcessSearch">搜索</el-button>
-                    </div>
-                  </div>
-                  <el-table
-                    :data="processLibraryList"
-                    border
-                    size="small"
-                    height="480"
-                    row-key="id"
-                    class="add-staff-process-table"
-                  >
-                    <el-table-column width="48" align="center">
-                      <template #default="{ row }">
-                        <el-checkbox
-                          :model-value="isProcessRowSelected(row)"
-                          @change="(val) => toggleAddProcess(row, val)"
-                        />
-                      </template>
-                    </el-table-column>
-                    <el-table-column prop="id" label="工序ID" width="70" align="center" show-overflow-tooltip />
-                    <el-table-column prop="工序编码" label="工序编码" width="100" show-overflow-tooltip />
-                    <el-table-column prop="工序名称" label="工序名称" min-width="120" show-overflow-tooltip />
-                    <el-table-column prop="生产工序" label="生产工序" width="80" show-overflow-tooltip />
-                  </el-table>
-                  <div class="add-staff-lib-pagination">
-                    <el-pagination
-                      :current-page="processLibraryPage"
-                      :page-size="processLibraryPageSize"
-                      :page-sizes="[10,15,20,30,50]"
-                      :total="processLibraryTotal"
-                      :pager-count="5"
-                      small
-                      background
-                      layout="total, sizes, prev, pager, next"
-                      @current-change="handleProcessLibPageChange"
-                      @size-change="handleProcessLibSizeChange"
-                    />
-                  </div>
-                </el-col>
-              </el-row>
-            <div style="text-align: right; margin-top: 12px">
+            <el-form :model="formDataTest" label-position="right" label-width="100px" class="staff-basic-form">
+              <el-form-item label="员工编号" required>
+                <el-input
+                  v-model="formDataTest['员工编号']"
+                  placeholder="员工编号"
+                  :clearable="isAddMode"
+                  :disabled="!isAddMode"
+                />
+              </el-form-item>
+              <el-form-item label="员工姓名" required>
+                <el-input v-model="formDataTest['员工姓名']" placeholder="员工姓名" clearable />
+              </el-form-item>
+              <el-form-item label="性别">
+                <el-select v-model="formDataTest['性别']" placeholder="请选择性别" class="team-select-full">
+                  <el-option v-for="g in genderOptions" :key="g" :label="g" :value="g" />
+                </el-select>
+              </el-form-item>
+              <el-form-item label="所在工序">
+                <el-select
+                  v-model="formDataTest['生产工序']"
+                  placeholder="请选择生产工序"
+                  clearable
+                  filterable
+                  class="team-select-full"
+                >
+                  <el-option v-for="gx in processOptions" :key="gx" :label="gx" :value="gx" />
+                </el-select>
+              </el-form-item>
+            </el-form>
+            <div class="staff-dialog-footer">
               <el-button @click="closeDialog">取消</el-button>
               <el-button type="primary" @click="enterDialog">确定</el-button>
             </div>
@@ -232,9 +136,9 @@
 </template>
 <script setup>
 // 全量引入格式化工具 请按需保留
-import { Layout, LayoutSider, LayoutContent } from '@arco-design/web-vue';
+import { Layout, LayoutHeader, LayoutSider, LayoutContent } from '@arco-design/web-vue';
 
-import { ref, reactive, nextTick } from 'vue'
+import { ref, reactive, nextTick, computed } from 'vue'
 import {
   getDepartment,
   getStaffList,
@@ -242,16 +146,20 @@ import {
   PostStaffAdd,
   PostStaffEdit,
   PostStaffDelete,
-  GetProcessesList,
   GetDeviceNameList,
 } from '@/api/yunyin/yunying'
 import { ElMessage, ElMessageBox } from 'element-plus'
+import { useUserStore } from '@/pinia/modules/user';
 defineOptions({name: 'Company'})
 
+ //获取登录用户信息
+  const userStore = useUserStore()
+  const _username = ref('')
+  _username.value = userStore.userInfo.userName + '/' + userStore.userInfo.nickName
+  console.log('获取用户名称',_username.value)
+
 /** 性别下拉,默认女 */
 const genderOptions = ['女', '男']
-/** 职务下拉 */
-const positionOptions = ['组长', '人员']
 
 // =========== 获取左侧树侧形结构 ===========
 const treeData = ref([])
@@ -378,23 +286,40 @@ const isAddMode = ref(false)
 //修改
 const formDataTest = ref({})
 
-const processLibraryList = ref([])
-const processLibraryPage = ref(1)
-const processLibraryPageSize = ref(30)
-const processLibraryTotal = ref(0)
-const processSearchKeyword = ref('')
-const addSelectedProcesses = ref([])
-/**设备编组列表,用于「所在小组」下拉 */
+/** GetDeviceNameList 原始列表,用于推导「生产工序」下拉选项 */
 const deviceTeamList = ref([])
 
-const formatTeamOptionLabel = (item) => {
-  const name = item['设备编组'] ?? ''
-  const gx = item['工序']
-  if (gx !== undefined && gx !== null && gx !== '') {
-    return `${name}`
+/** 去重后的生产工序选项 */
+/** 生产工序下拉:按接口「工序」字段数值从小到大排序(同工序多行取最小序号) */
+const processOptions = computed(() => {
+  const list = deviceTeamList.value
+  if (!Array.isArray(list) || list.length === 0) return []
+  const gxToMinProc = new Map()
+  for (const item of list) {
+    const gx = String(item['生产工序'] ?? '').trim()
+    if (!gx) continue
+    const n = Number(item['工序'])
+    const prev = gxToMinProc.get(gx)
+    if (!Number.isNaN(n)) {
+      if (prev === undefined || Number.isNaN(prev) || n < prev) {
+        gxToMinProc.set(gx, n)
+      }
+    } else if (prev === undefined) {
+      gxToMinProc.set(gx, NaN)
+    }
   }
-  return name
-}
+  const keys = Array.from(gxToMinProc.keys())
+  return keys.sort((a, b) => {
+    const pa = gxToMinProc.get(a)
+    const pb = gxToMinProc.get(b)
+    const na = Number(pa)
+    const nb = Number(pb)
+    if (!Number.isNaN(na) && !Number.isNaN(nb) && na !== nb) return na - nb
+    if (!Number.isNaN(na) && Number.isNaN(nb)) return -1
+    if (Number.isNaN(na) && !Number.isNaN(nb)) return 1
+    return a.localeCompare(b, 'zh-CN')
+  })
+})
 
 const loadDeviceTeamList = async () => {
   try {
@@ -412,21 +337,6 @@ const loadDeviceTeamList = async () => {
   }
 }
 
-/** 根据 GetDeviceNameList 返回项中的「设备编组」→「生产工序」,在选择所在小组时写入表单(无下拉) */
-const syncProductionProcessFromTeam = (teamValue) => {
-  if (teamValue === undefined || teamValue === null || teamValue === '') {
-    formDataTest.value['生产工序'] = ''
-    return
-  }
-  const row = deviceTeamList.value.find((item) => item['设备编组'] === teamValue)
-  const gx = row && row['生产工序']
-  formDataTest.value['生产工序'] = gx != null && gx !== '' ? gx : ''
-}
-
-const onTeamChange = (val) => {
-  syncProductionProcessFromTeam(val)
-}
-
 /** getStaffInfo 的 data 可能为 [{...}] 单元素数组 */
 const normalizeStaffInfoPayload = (raw) => {
   if (Array.isArray(raw)) {
@@ -438,126 +348,11 @@ const normalizeStaffInfoPayload = (raw) => {
   return null
 }
 
-/**
- * getStaffInfo 的 process_list 可能为:
- * - 旧:{ "1": "工序名", ... }
- * - 新:{ "1": { 工序ID, 工序编码, 工序名称, 生产工序 }, ... }
- * - 或数组:[{ ... }, ...]
- */
-const normalizeProcessRowFromStaffInfo = (raw, fallbackKey) => {
-  if (raw == null) return null
-  if (typeof raw === 'string') {
-    const numId = fallbackKey != null ? Number(fallbackKey) : NaN
-    const id = !Number.isNaN(numId) ? numId : fallbackKey
-    return {
-      id,
-      工序编码: '',
-      工序名称: raw,
-      生产工序: '',
-    }
-  }
-  if (typeof raw === 'object' && !Array.isArray(raw)) {
-    const id =
-      raw['工序ID'] ?? raw['工序Id'] ?? raw.id ?? raw.ID ?? (fallbackKey != null ? Number(fallbackKey) || fallbackKey : undefined)
-    return {
-      id: id != null && id !== '' ? id : fallbackKey,
-      工序编码: raw['工序编码'] ?? '',
-      工序名称: raw['工序名称'] ?? '',
-      生产工序: raw['生产工序'] ?? '',
-    }
-  }
-  return {
-    id: fallbackKey != null ? Number(fallbackKey) || fallbackKey : undefined,
-    工序编码: '',
-    工序名称: String(raw),
-    生产工序: '',
-  }
-}
-
-const processListToSelectedRows = (processList) => {
-  if (!processList) return []
-  if (Array.isArray(processList)) {
-    return processList
-      .map((item, idx) => normalizeProcessRowFromStaffInfo(item, idx))
-      .filter(Boolean)
-  }
-  if (typeof processList === 'object') {
-    return Object.keys(processList)
-      .sort((a, b) => Number(a) - Number(b))
-      .map((key) => normalizeProcessRowFromStaffInfo(processList[key], key))
-      .filter(Boolean)
-  }
-  return []
-}
-
-const loadProcessLibrary = async () => {
-  try {
-    const res = await GetProcessesList({
-      page: processLibraryPage.value,
-      limit: processLibraryPageSize.value,
-      search: processSearchKeyword.value.trim(),
-    })
-    if (res?.code === 0 && res.data) {
-      const list = res.data.list || []
-      processLibraryList.value = list
-      const rawTotal = res.data.total ?? res.data.count
-      if (rawTotal != null && rawTotal !== '') {
-        processLibraryTotal.value = Number(rawTotal)
-      } else {
-        processLibraryTotal.value = list.length
-      }
-    } else {
-      processLibraryList.value = []
-      processLibraryTotal.value = 0
-    }
-  } catch (e) {
-    console.error(e)
-    processLibraryList.value = []
-    processLibraryTotal.value = 0
-    ElMessage.error('工序库加载失败')
-  }
-}
-
-const onProcessSearch = () => {
-  processLibraryPage.value = 1
-  loadProcessLibrary()
-}
-
-const handleProcessLibPageChange = (p) => {
-  processLibraryPage.value = p
-  loadProcessLibrary()
-}
-
-const handleProcessLibSizeChange = (size) => {
-  processLibraryPageSize.value = size
-  processLibraryPage.value = 1
-  loadProcessLibrary()
-}
-
-const isProcessRowSelected = (row) => addSelectedProcesses.value.some((p) => p.id === row.id)
-
-const toggleAddProcess = (row, checked) => {
-  if (checked) {
-    // 按勾选顺序追加在末尾,序号自上而下为 1、2、3…(先选在上)
-    addSelectedProcesses.value = [...addSelectedProcesses.value.filter((p) => p.id !== row.id), row]
-  } else {
-    addSelectedProcesses.value = addSelectedProcesses.value.filter((p) => p.id !== row.id)
-  }
-}
-
-const removeAddProcess = (row) => {
-  addSelectedProcesses.value = addSelectedProcesses.value.filter((p) => p.id !== row.id)
-}
-
 const openAddDialog = async () => {
   isAddMode.value = true
   formDataTest.value = { 性别: '女' }
-  addSelectedProcesses.value = []
-  processSearchKeyword.value = ''
-  processLibraryPage.value = 1
-  processLibraryPageSize.value = 15
   dialogFormVisible.value = true
-  await Promise.all([loadProcessLibrary(), loadDeviceTeamList()])
+  await loadDeviceTeamList()
 }
 
 const updateCompanyFunc = async (row) => {
@@ -574,15 +369,11 @@ const updateCompanyFunc = async (row) => {
       ElMessage.error('未获取到员工资料')
       return
     }
-    const { process_list, ...rest } = payload
+    const { process_list: _processList, ...rest } = payload
     if (!rest['性别']) rest['性别'] = '女'
     formDataTest.value = rest
-    addSelectedProcesses.value = processListToSelectedRows(process_list)
-    processSearchKeyword.value = ''
-    processLibraryPage.value = 1
     dialogFormVisible.value = true
-    await Promise.all([loadProcessLibrary(), loadDeviceTeamList()])
-    syncProductionProcessFromTeam(formDataTest.value['所在小组'])
+    await loadDeviceTeamList()
   } catch (e) {
     console.error(e)
   }
@@ -630,8 +421,6 @@ const onDeleteStaff = async (row) => {
 const closeDialog = () => {
   dialogFormVisible.value = false
   isAddMode.value = false
-  addSelectedProcesses.value = []
-  processSearchKeyword.value = ''
 }
 
 // 弹窗确定
@@ -652,22 +441,17 @@ const buildStaffPayload = () => ({
   员工姓名: formDataTest.value['员工姓名'],
   性别: formDataTest.value['性别'],
   生产工序: formDataTest.value['生产工序'],
-  职称职务: formDataTest.value['职称职务'],
   所在部门: formDataTest.value['所在部门'],
-  所在小组: formDataTest.value['所在小组'],
-  gx_ids: addSelectedProcesses.value.map((p) => p.id),
+  sys_id: userStore.userInfo.nickName,
 })
 
-/** 新增:仅提交弹窗内字段 + 已选工序 id(与修改接口字段可不同) */
 const buildAddPayload = () => ({
   员工编号: formDataTest.value['员工编号'],
   员工姓名: formDataTest.value['员工姓名'],
   性别: formDataTest.value['性别'],
   生产工序: formDataTest.value['生产工序'],
-  职称职务: formDataTest.value['职称职务'],
   所在部门: formDataTest.value['所在部门'],
-  所在小组: formDataTest.value['所在小组'],
-  gx_ids: addSelectedProcesses.value.map((p) => p.id),
+  sys_id: userStore.userInfo.nickName,
 })
 
 const _ygjbzladd = async () => {
@@ -676,8 +460,6 @@ const _ygjbzladd = async () => {
     if (res.code === 0) {
       dialogFormVisible.value = false
       isAddMode.value = false
-      addSelectedProcesses.value = []
-      processSearchKeyword.value = ''
       ElMessage({ type: 'success', message: '新增成功' })
       resetToFirstPage()
       _getStaffList()
@@ -746,7 +528,9 @@ const _ygjbzledit = async ()=>{
 .JKWTree-tree h3 {
   font-size: 15px;
   font-weight: 700;
-  margin: 10px 0;
+  line-height: 1.2;
+  margin: 0 0 10px;
+  padding: 0;
 }
 .tree-node-label {
   margin-right: 4px;
@@ -762,105 +546,35 @@ const _ygjbzledit = async ()=>{
 .JKWTree-content {
   flex: 1;
 }
-/* 新增员工:双栏工序选择 */
-.add-staff-layout {
-  min-height: 400px;
-}
-.add-staff-form :deep(.el-form-item) {
-  margin-bottom: 12px;
-}
-.add-staff-form :deep(.el-input) {
-  width: 100%;
-}
 .team-select-full {
   width: 100%;
 }
-/* 所在小组 + 生产工序只读展示 */
-.team-and-process-row {
-  display: flex;
-  align-items: center;
-  gap: 8px;
-  width: 100%;
-}
-.team-and-process-row .team-select-wrap {
-  flex: 1;
-  min-width: 0;
-}
-.team-and-process-row .team-process-display-input {
-  flex: 0 0 120px;
-  width: 120px;
-}
-.team-and-process-row .team-process-display-input :deep(.el-input__inner) {
-  cursor: default;
-}
-.add-staff-selected-title {
-  font-size: 13px;
-  font-weight: 600;
-  margin: 14px 0 8px;
+.staff-basic-form :deep(.el-form-item) {
+  margin-bottom: 14px;
 }
-.add-staff-selected-table {
+.staff-basic-form :deep(.el-input),
+.staff-basic-form :deep(.el-select) {
   width: 100%;
 }
-.add-staff-form :deep(.el-select) {
-  width: 100%;
-}
-/* 弹窗:新增/修改同一套双栏,固定宽高 */
-.staff-dialog-wrap--add :deep(.el-dialog) {
-  width: 1120px !important;
-  max-width: min(1120px, 96vw) !important;
-  min-height: 720px;
-  margin-bottom: 5vh;
-  display: flex;
-  flex-direction: column;
-}
-.staff-dialog-wrap--add :deep(.el-dialog__body) {
-  flex: 1;
-  min-height: 580px;
-  box-sizing: border-box;
-  overflow: hidden;
-}
-.staff-dialog-wrap :deep(.el-dialog__body) {
+.staff-dialog-footer {
+  text-align: right;
+  margin-top: 8px;
   padding-top: 8px;
 }
-.add-staff-layout--fixed {
-  min-height: 580px;
-}
-.add-staff-layout--fixed .el-col:last-child {
-  display: flex;
-  flex-direction: column;
-}
-.add-staff-lib-head {
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  margin-bottom: 8px;
-  flex-wrap: wrap;
-  gap: 8px;
-}
-.add-staff-lib-search {
-  display: flex;
+/* 表格操作列:实心按钮,与顶部「新增」风格一致 */
+.table-op-btns {
+  display: inline-flex;
   align-items: center;
+  justify-content: center;
   gap: 8px;
   flex-wrap: wrap;
 }
-.add-staff-lib-title {
-  font-weight: 600;
-  font-size: 14px;
-}
-.add-staff-process-table {
-  width: 100%;
-}
-.add-staff-lib-pagination {
-  display: flex;
-  justify-content: flex-end;
-  align-items: center;
-  flex-shrink: 0;
-  min-height: 36px;
-  margin-top: 10px;
-  flex-wrap: wrap;
+.staff-dialog-form-simple :deep(.el-dialog) {
+  max-width: min(520px, 96vw);
+  margin-bottom: 5vh;
 }
-.add-staff-lib-pagination :deep(.el-pagination) {
-  justify-content: flex-end;
+.staff-dialog-wrap :deep(.el-dialog__body) {
+  padding-top: 8px;
 }
 /* 图片上传 */
 .upload-box {
@@ -916,6 +630,25 @@ const _ygjbzledit = async ()=>{
 .gva-table-box{
   padding: 0px !important;
 }
+.yunyin-hr-split :deep(.arco-layout-sider-children) {
+  padding-top: 0 !important;
+  padding-bottom: 0 !important;
+}
+.yunyin-hr-split :deep(.arco-layout-content) {
+  padding-top: 0 !important;
+}
+.yunyin-main-block {
+  box-sizing: border-box;
+}
+.staff-tree-panel {
+  min-height: 260px;
+  max-height: 420px;
+  overflow: auto;
+}
+.yunyin-page-header :deep(.arco-layout-header) {
+  padding: 12px 10px;
+  box-sizing: border-box;
+}
 .mab{
   margin-bottom: 5px;
 }

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików