| | |
| | | </div> |
| | | </div> |
| | | <div class="card-body"> |
| | | <div class="route-desc">{{ route.routeDesc || '暂无描述' }}</div> |
| | | <el-button type="primary" |
| | | link |
| | | @click="toggleExpand(route)"> |
| | | {{ route.expanded ? '收起' : '展开工序路线' }} |
| | | <el-icon class="expand-icon"> |
| | | <component :is="route.expanded ? 'ArrowUp' : 'ArrowDown'" /> |
| | | </el-icon> |
| | | </el-button> |
| | | <div class="route-meta"> |
| | | <span class="meta-item"> |
| | | <el-icon> |
| | | <Box /> |
| | | </el-icon> |
| | | <span class="meta-label">产品:</span> |
| | | <span class="meta-value">{{ route.productName }} - {{ route.productModelName }}</span> |
| | | </span> |
| | | <span class="meta-item"> |
| | | <el-icon> |
| | | <Document /> |
| | | </el-icon> |
| | | <span class="meta-label">BOM:</span> |
| | | <span class="meta-value">{{ route.bomId || '-' }}</span> |
| | | </span> |
| | | <span class="meta-item"> |
| | | <el-icon> |
| | | <Document /> |
| | | </el-icon> |
| | | <span class="meta-label">路线描述:</span> |
| | | <span class="meta-value">{{ route.routeDesc || '暂无描述' }}</span> |
| | | </span> |
| | | </div> |
| | | <div class="expand-btn-wrapper"> |
| | | <el-button class="expand-btn" |
| | | :class="{ expanded: route.expanded }" |
| | | type="primary" |
| | | text |
| | | @click="toggleExpand(route)"> |
| | | <span class="btn-text">{{ route.expanded ? '收起工序路线' : '展开工序路线' }}</span> |
| | | <el-icon class="expand-icon"> |
| | | <component :is="route.expanded ? 'ArrowUp' : 'ArrowDown'" /> |
| | | </el-icon> |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | <div v-if="route.expanded" |
| | | class="process-route"> |
| | |
| | | :rules="routeRules" |
| | | ref="routeFormRef" |
| | | label-width="120px"> |
| | | <el-form-item label="产品名称" |
| | | prop="productModelId"> |
| | | <el-button type="primary" |
| | | @click="showProductSelectDialog = true"> |
| | | {{ routeForm.productName && routeForm.productModelName |
| | | ? `${routeForm.productName} - ${routeForm.productModelName}` |
| | | : '选择产品' }} |
| | | </el-button> |
| | | </el-form-item> |
| | | <el-form-item label="BOM" |
| | | prop="bomId"> |
| | | <el-select v-model="routeForm.bomId" |
| | | placeholder="请选择BOM" |
| | | clearable |
| | | :disabled="!routeForm.productModelId || bomOptions.length === 0" |
| | | style="width: 100%"> |
| | | <el-option v-for="item in bomOptions" |
| | | :key="item.id" |
| | | :label="item.bomNo || `BOM-${item.id}`" |
| | | :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="路线编码" |
| | | prop="routeCode"> |
| | | <el-input v-model="routeForm.routeCode" |
| | |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | <!-- 产品选择弹窗 --> |
| | | <ProductSelectDialog v-model="showProductSelectDialog" |
| | | @confirm="handleProductSelect" |
| | | single /> |
| | | <!-- 工序新增/编辑对话框 --> |
| | | <el-dialog v-model="processDialogVisible" |
| | | :title="isProcessEdit ? '编辑工序' : '新增工序'" |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive } from "vue"; |
| | | import { ref, reactive, getCurrentInstance } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import { |
| | | Plus, |
| | |
| | | Search, |
| | | Check, |
| | | Close, |
| | | Box, |
| | | Document, |
| | | } from "@element-plus/icons-vue"; |
| | | import { listType } from "@/api/system/dict/type"; |
| | | import { getByModel } from "@/api/productionManagement/productBom.js"; |
| | | import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue"; |
| | | |
| | | // 工艺路线列表 |
| | | const routeList = ref([]); |
| | | const dictTypes = ref([]); |
| | | |
| | | // 获取全局实例 |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | | // 产品选择和BOM相关 |
| | | const showProductSelectDialog = ref(false); |
| | | const bomOptions = ref([]); |
| | | |
| | | // 工艺路线对话框 |
| | | const routeDialogVisible = ref(false); |
| | |
| | | const routeFormRef = ref(null); |
| | | const routeForm = reactive({ |
| | | id: null, |
| | | productModelId: null, |
| | | productName: "", |
| | | productModelName: "", |
| | | bomId: null, |
| | | routeCode: "", |
| | | routeName: "", |
| | | routeDesc: "", |
| | | status: "1", |
| | | }); |
| | | const routeRules = { |
| | | productModelId: [ |
| | | { required: true, message: "请选择产品", trigger: "change" }, |
| | | ], |
| | | bomId: [{ required: true, message: "请选择BOM", trigger: "change" }], |
| | | routeCode: [{ required: true, message: "请输入路线编码", trigger: "blur" }], |
| | | routeName: [{ required: true, message: "请输入路线名称", trigger: "blur" }], |
| | | }; |
| | |
| | | routeList.value = [ |
| | | { |
| | | id: 1, |
| | | productModelId: 1, |
| | | productName: "标准砌块", |
| | | productModelName: "3.5型", |
| | | bomId: 1, |
| | | routeCode: "ROUTE001", |
| | | routeName: "标准砌块生产线", |
| | | routeDesc: "标准砌块生产流程", |
| | |
| | | }, |
| | | { |
| | | id: 2, |
| | | productModelId: 2, |
| | | productName: "板材", |
| | | productModelName: "5.0型", |
| | | bomId: 2, |
| | | routeCode: "ROUTE002", |
| | | routeName: "板材生产线", |
| | | routeDesc: "板材生产流程", |
| | |
| | | const handleAddRoute = () => { |
| | | isRouteEdit.value = false; |
| | | routeForm.id = null; |
| | | routeForm.productModelId = null; |
| | | routeForm.productName = ""; |
| | | routeForm.productModelName = ""; |
| | | routeForm.bomId = null; |
| | | routeForm.routeCode = ""; |
| | | routeForm.routeName = ""; |
| | | routeForm.routeDesc = ""; |
| | | routeForm.status = "1"; |
| | | bomOptions.value = []; |
| | | routeDialogVisible.value = true; |
| | | }; |
| | | |
| | | const handleEditRoute = route => { |
| | | isRouteEdit.value = true; |
| | | routeForm.id = route.id; |
| | | routeForm.productModelId = route.productModelId; |
| | | routeForm.productName = route.productName; |
| | | routeForm.productModelName = route.productModelName; |
| | | routeForm.bomId = route.bomId; |
| | | routeForm.routeCode = route.routeCode; |
| | | routeForm.routeName = route.routeName; |
| | | routeForm.routeDesc = route.routeDesc; |
| | |
| | | getRouteList(); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // 产品选择处理 |
| | | const handleProductSelect = async products => { |
| | | if (products && products.length > 0) { |
| | | const product = products[0]; |
| | | // 先查询BOM列表(必选) |
| | | try { |
| | | const res = await getByModel(product.id); |
| | | // 处理返回的BOM数据:可能是数组、对象或包含data字段 |
| | | let bomList = []; |
| | | if (Array.isArray(res)) { |
| | | bomList = res; |
| | | } else if (res && res.data) { |
| | | bomList = Array.isArray(res.data) ? res.data : [res.data]; |
| | | } else if (res && typeof res === "object") { |
| | | bomList = [res]; |
| | | } |
| | | |
| | | if (bomList.length > 0) { |
| | | routeForm.productModelId = product.id; |
| | | routeForm.productName = product.productName; |
| | | routeForm.productModelName = product.model; |
| | | routeForm.bomId = undefined; // 重置BOM选择 |
| | | bomOptions.value = bomList; |
| | | showProductSelectDialog.value = false; |
| | | // 触发表单验证更新 |
| | | proxy.$refs["routeFormRef"]?.validateField("productModelId"); |
| | | } else { |
| | | proxy.$modal.msgError("该产品没有BOM,请先创建BOM"); |
| | | } |
| | | } catch (error) { |
| | | // 如果接口返回404或其他错误,说明没有BOM |
| | | proxy.$modal.msgError("该产品没有BOM,请先创建BOM"); |
| | | } |
| | | } |
| | | }; |
| | | |
| | | const handleApproveRoute = route => { |
| | |
| | | margin-bottom: 12px; |
| | | } |
| | | |
| | | .expand-icon { |
| | | margin-left: 4px; |
| | | .route-meta { |
| | | display: flex; |
| | | gap: 24px; |
| | | margin-bottom: 12px; |
| | | padding: 10px 14px; |
| | | background: linear-gradient(135deg, #f5f7fa 0%, #e8ecf1 100%); |
| | | border-radius: 8px; |
| | | border-left: 3px solid #409eff; |
| | | |
| | | .meta-item { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 6px; |
| | | font-size: 13px; |
| | | margin-right: 40px; |
| | | |
| | | .el-icon { |
| | | font-size: 14px; |
| | | color: #409eff; |
| | | } |
| | | |
| | | .meta-label { |
| | | color: #909399; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .meta-value { |
| | | color: #303133; |
| | | font-weight: 600; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .expand-btn-wrapper { |
| | | display: flex; |
| | | justify-content: center; |
| | | margin-top: 8px; |
| | | |
| | | .expand-btn { |
| | | padding: 8px 20px; |
| | | border-radius: 20px; |
| | | background: linear-gradient(135deg, #ecf5ff 0%, #d9ecff 100%); |
| | | border: 1px solid #b3d8ff; |
| | | transition: all 0.3s ease; |
| | | |
| | | .btn-text { |
| | | font-size: 13px; |
| | | font-weight: 500; |
| | | color: #409eff; |
| | | margin-right: 6px; |
| | | } |
| | | |
| | | .expand-icon { |
| | | font-size: 14px; |
| | | color: #409eff; |
| | | transition: transform 0.3s ease; |
| | | } |
| | | |
| | | &:hover { |
| | | background: linear-gradient(135deg, #409eff 0%, #66b1ff 100%); |
| | | border-color: #409eff; |
| | | box-shadow: 0 4px 12px rgba(64, 158, 255, 0.3); |
| | | |
| | | .btn-text, |
| | | .expand-icon { |
| | | color: #fff; |
| | | } |
| | | } |
| | | |
| | | &.expanded { |
| | | background: linear-gradient(135deg, #f0f9eb 0%, #e1f3d8 100%); |
| | | border-color: #a5d69a; |
| | | |
| | | .btn-text, |
| | | .expand-icon { |
| | | color: #67c23a; |
| | | } |
| | | |
| | | &:hover { |
| | | background: linear-gradient(135deg, #67c23a 0%, #85ce61 100%); |
| | | border-color: #67c23a; |
| | | box-shadow: 0 4px 12px rgba(103, 194, 58, 0.3); |
| | | |
| | | .btn-text, |
| | | .expand-icon { |
| | | color: #fff; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |