From ec226b03fbd26bc178137ad20d7bd030ef2c5c8b Mon Sep 17 00:00:00 2001
From: liding <756868258@qq.com>
Date: 星期三, 24 六月 2026 15:01:40 +0800
Subject: [PATCH] Merge branch 'refs/heads/dev_NEW_pro' into dev_宁夏_铭远环保

---
 src/views/productionManagement/productStructure/DetailNew/MaterialCard.vue |  236 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 236 insertions(+), 0 deletions(-)

diff --git a/src/views/productionManagement/productStructure/DetailNew/MaterialCard.vue b/src/views/productionManagement/productStructure/DetailNew/MaterialCard.vue
new file mode 100644
index 0000000..f0ac5a6
--- /dev/null
+++ b/src/views/productionManagement/productStructure/DetailNew/MaterialCard.vue
@@ -0,0 +1,236 @@
+<template>
+  <div class="material-node">
+    <!-- 褰撳墠鑺傜偣鍗$墖 -->
+    <div :class="['node-card', isRoot ? 'root-card' : (row.nodeType === 'semiFinished' ? 'semi-finished-card' : 'child-card')]">
+      <div class="node-header">
+        <div class="node-label">
+          <el-tag :type="isRoot ? '' : (row.nodeType === 'semiFinished' ? 'warning' : 'success')" size="small" effect="dark">
+            {{ isRoot ? '鎴愬搧' : (row.nodeType === 'semiFinished' ? '鍗婃垚鍝�' : '鍘熸枡') }}
+          </el-tag>
+          <span class="node-title">{{ row.productName || '鏈�夋嫨浜у搧' }}</span>
+          <span v-if="row.model" class="node-sub">瑙勬牸: {{ row.model }}</span>
+          <span v-if="row.unit" class="node-sub">鍗曚綅: {{ row.unit }}</span>
+        </div>
+        <div class="node-actions">
+          <template v-if="editable && (isRoot || row.nodeType === 'semiFinished')">
+            <el-button type="primary"
+                       text
+                       size="small"
+                       @click="handleAdd('semiFinished')">
+              + 娣诲姞鍗婃垚鍝�
+            </el-button>
+            <el-button type="primary"
+                       text
+                       size="small"
+                       @click="handleAdd('rawMaterial')">
+              + 娣诲姞鍘熸枡
+            </el-button>
+          </template>
+          <el-button v-if="editable"
+                     type="danger"
+                     text
+                     size="small"
+                     @click="$emit('remove', row.tempId)">
+            鍒犻櫎
+          </el-button>
+        </div>
+      </div>
+
+      <!-- 缂栬緫妯″紡涓嬬殑琛ㄥ崟 -->
+      <div v-if="editable" class="node-body">
+        <el-row :gutter="12">
+          <el-col :span="7">
+            <el-form-item label="浜у搧" :rules="[{ required: true, message: '璇烽�夋嫨浜у搧' }]" style="margin:0">
+              <el-input :model-value="row.productName || ''"
+                        readonly
+                        placeholder="鐐瑰嚮閫夋嫨浜у搧"
+                        @click="openSelect"
+                        style="width:100%">
+                <template #suffix>
+                  <el-icon><component :is="SearchIcon" /></el-icon>
+                </template>
+              </el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="5">
+            <el-form-item label="瑙勬牸" style="margin:0">
+              <el-select v-model="row.model"
+                         placeholder="璇烽�夋嫨瑙勬牸"
+                         clearable
+                         style="width:100%"
+                         @visible-change="(v:boolean) => { if (v) openSelect() }">
+                <el-option v-if="row.model" :label="row.model" :value="row.model" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col v-if="!isRoot" :span="5">
+            <el-form-item label="宸ュ簭" :rules="[{ required: true, message: '璇烽�夋嫨宸ュ簭' }]" style="margin:0">
+              <el-select v-model="row.processId"
+                         placeholder="璇烽�夋嫨"
+                         filterable
+                         clearable
+                         style="width:100%"
+                         @change="(v:any) => $emit('processChange', row, v)">
+                <el-option v-for="item in processOptions"
+                           :key="item.id"
+                           :label="item.name"
+                           :value="item.id" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="4">
+            <el-form-item label="鏁伴噺" :rules="[{ required: true, message: '璇峰~鍐欐暟閲�' }]" style="margin:0">
+              <el-input-number v-model="row.unitQuantity"
+                               :min="0"
+                               :precision="2"
+                               :step="1"
+                               controls-position="right"
+                               style="width:100%"
+                               @change="$emit('quantityChange')" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="3">
+            <el-form-item label="鍗曚綅" style="margin:0">
+              <el-input v-model="row.unit" placeholder="鍗曚綅" clearable style="width:100%" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </div>
+
+      <!-- 闈炵紪杈戞ā寮忥細绠�娲佹樉绀� -->
+      <div v-else class="node-view">
+        <span v-if="!isRoot && row.processName">宸ュ簭: {{ row.processName }} | </span>
+        <span>鏁伴噺: {{ row.unitQuantity || '-' }}</span>
+      </div>
+    </div>
+
+    <!-- 閫掑綊娓叉煋瀛愯妭鐐� -->
+    <div v-if="row.children && row.children.length > 0" class="node-children">
+      <MaterialCard
+        v-for="child in row.children"
+        :key="child.tempId"
+        :row="child"
+        :depth="depth + 1"
+        :editable="editable"
+        :process-options="processOptions"
+        @remove="(id:string) => $emit('remove', id)"
+        @add="(id:string, nodeType:string) => $emit('add', id, nodeType)"
+        @select-product="(tempId: string, data: any) => $emit('selectProduct', tempId, data)"
+        @process-change="(row: any, v: any) => $emit('processChange', row, v)"
+        @quantity-change="$emit('quantityChange')"
+      />
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { computed } from 'vue'
+import { Search } from '@element-plus/icons-vue'
+
+const SearchIcon = Search
+
+const props = defineProps<{
+  row: any
+  depth: number
+  editable: boolean
+  processOptions: any[]
+}>()
+
+const emit = defineEmits<{
+  remove: [tempId: string]
+  add: [tempId: string, nodeType: string]
+  selectProduct: [tempId: string, data: any]
+  processChange: [row: any, value: any]
+  quantityChange: []
+}>()
+
+const isRoot = computed(() => props.depth === 0)
+
+const openSelect = () => {
+  emit('selectProduct', props.row.tempId, null)
+}
+
+const handleAdd = (nodeType: string) => {
+  emit('add', props.row.tempId, nodeType)
+}
+</script>
+
+<script lang="ts">
+export default { name: 'MaterialCard' }
+</script>
+
+<style scoped>
+.material-node {
+  margin: 4px 0;
+}
+
+.node-card {
+  border: 1px solid #e4e7ed;
+  border-radius: 8px;
+  overflow: hidden;
+}
+
+.root-card {
+  border-color: #409eff;
+  border-left: 4px solid #409eff;
+  background-color: #f0f5ff;
+}
+
+.child-card {
+  border-left: 4px solid #67c23a;
+  background-color: #f0f9eb;
+}
+
+.semi-finished-card {
+  border-left: 4px solid #e6a23c;
+  background-color: #fdf6ec;
+}
+
+.node-header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: 8px 12px;
+  background-color: rgba(0,0,0,0.03);
+  flex-wrap: wrap;
+  gap: 4px;
+}
+
+.node-label {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  flex-wrap: wrap;
+}
+
+.node-title {
+  font-weight: 600;
+  color: #303133;
+}
+
+.node-sub {
+  font-size: 12px;
+  color: #909399;
+}
+
+.node-actions {
+  display: flex;
+  gap: 4px;
+}
+
+.node-body {
+  padding: 10px 12px;
+}
+
+.node-view {
+  padding: 6px 12px;
+  font-size: 13px;
+  color: #606266;
+}
+
+.node-children {
+  margin-left: 36px;
+  padding-left: 16px;
+  border-left: 2px dashed #dcdfe6;
+}
+</style>

--
Gitblit v1.9.3