From f3f5a0c0db0b782401a9a0fcf6176312f420d941 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期三, 25 三月 2026 14:01:19 +0800
Subject: [PATCH] 军泰伟业 1.不要树形结构。根据图纸编号为唯一索引添加产品并可以导入 2.要求可以上传图纸 3.产品可以绑定工艺路线 4.工艺路线改成由工序组成,新增编辑是工序可以挪动顺序 5.工序添加报工权限字段,可以多选人员
---
src/views/productionManagement/processRoute/Form.vue | 376 +++++++++++++++++
src/views/productionManagement/productionProcess/index.vue | 5
/dev/null | 194 --------
src/views/productionManagement/processRoute/index.vue | 142 +++---
src/views/basicData/product/index.vue | 180 +++++++
src/views/productionManagement/processRoute/processRouteItem/index.vue | 122 +++++
src/api/basicData/productProcess.js | 8
src/views/productionManagement/productionProcess/Edit.vue | 138 +++++
src/views/productionManagement/productionProcess/New.vue | 117 +++++
9 files changed, 975 insertions(+), 307 deletions(-)
diff --git a/src/api/basicData/productProcess.js b/src/api/basicData/productProcess.js
index e0208fa..d2b23b6 100644
--- a/src/api/basicData/productProcess.js
+++ b/src/api/basicData/productProcess.js
@@ -8,3 +8,11 @@
params: query
})
}
+
+// 鑾峰彇閮ㄩ棬鐢ㄦ埛鏍�
+export function listDeptUserTree() {
+ return request({
+ url: '/productProcess/listDeptUserTree',
+ method: 'get'
+ })
+}
diff --git a/src/views/basicData/product/index.vue b/src/views/basicData/product/index.vue
index 24a4659..48d2011 100644
--- a/src/views/basicData/product/index.vue
+++ b/src/views/basicData/product/index.vue
@@ -96,19 +96,17 @@
clearable
/>
</el-form-item>
- <el-form-item label="瑙勬牸鍨嬪彿" prop="drawingNumber">
- <el-input
- v-model="modelForm.drawingNumber"
- placeholder="璇疯緭鍏ヨ鏍煎瀷鍙�"
- clearable
- />
- </el-form-item>
<el-form-item label="鍗曚綅" prop="unit">
- <el-input
+ <el-select
v-model="modelForm.unit"
- placeholder="璇疯緭鍏ュ崟浣�"
+ placeholder="璇烽�夋嫨鍗曚綅"
clearable
- />
+ style="width: 100%"
+ >
+ <el-option label="浠�" value="浠�" />
+ <el-option label="濂�" value="濂�" />
+ <el-option label="鍙�" value="鍙�" />
+ </el-select>
</el-form-item>
<el-form-item label="浜у搧灞炴��" prop="productType">
<el-select
@@ -121,6 +119,43 @@
<el-option label="澶栬喘" :value="2" />
<el-option label="濮斿" :value="3" />
</el-select>
+ </el-form-item>
+ <el-form-item label="宸ヨ壓璺嚎" prop="routeId">
+ <el-select
+ v-model="modelForm.routeId"
+ placeholder="璇烽�夋嫨宸ヨ壓璺嚎"
+ clearable
+ style="width: 100%"
+ filterable
+ >
+ <el-option
+ v-for="item in processRouteList"
+ :key="item.id"
+ :label="item.processRouteName"
+ :value="item.id"
+ />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="涓婁紶鍥剧焊" prop="drawingFile">
+ <el-upload
+ v-model:file-list="drawingFileList"
+ :action="upload.url"
+ :headers="upload.headers"
+ :data="upload.data"
+ :on-success="handleDrawingUploadSuccess"
+ :on-remove="handleDrawingRemove"
+ :before-upload="handleDrawingBeforeUpload"
+ :limit="1"
+ accept=".pdf,.jpg,.jpeg,.png,.dwg"
+ list-type="picture-card"
+ >
+ <el-icon class="avatar-uploader-icon"><Plus /></el-icon>
+ <template #tip>
+ <div class="el-upload__tip">
+ 鏀寔 pdf銆乯pg銆乯peg銆乸ng銆乨wg 鏍煎紡锛屽ぇ灏忎笉瓒呰繃 10MB
+ </div>
+ </template>
+ </el-upload>
</el-form-item>
</el-form>
</FormDialog>
@@ -162,10 +197,10 @@
</template>
<script setup>
-import { ref, reactive } from "vue";
+import { ref, reactive, onMounted } from "vue";
import { ElMessageBox } from "element-plus";
+import { Plus } from "@element-plus/icons-vue";
import { getToken } from "@/utils/auth.js";
-import { FileUpload } from "@/components/Upload";
import FormDialog from "@/components/Dialog/FormDialog.vue";
import {
addOrEditProductModel,
@@ -173,6 +208,7 @@
productListPage,
downloadTemplate,
} from "@/api/basicData/product.js";
+import { listPage as getProcessRouteList } from "@/api/productionManagement/processRoute.js";
import ImportExcel from "./ImportExcel/index.vue";
const { proxy } = getCurrentInstance();
@@ -185,6 +221,14 @@
const tableLoading = ref(false);
const selectedRows = ref([]);
const modelFormRef = ref();
+const processRouteList = ref([]);
+const drawingFileList = ref([]);
+
+const upload = reactive({
+ url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
+ headers: { Authorization: "Bearer " + getToken() },
+ data: { type: 13 },
+});
const queryForm = reactive({
productName: "",
@@ -210,8 +254,8 @@
minWidth: 150,
},
{
- label: "瑙勬牸鍨嬪彿",
- prop: "drawingNumber",
+ label: "宸ヨ壓璺嚎",
+ prop: "routeName",
minWidth: 150,
},
{
@@ -252,8 +296,11 @@
productName: "",
model: "",
unit: "",
- drawingNumber: "",
productType: null,
+ routeId: null,
+ drawingFile: "",
+ tempFileIds: [],
+ salesLedgerFiles: [],
},
modelRules: {
productName: [
@@ -261,8 +308,7 @@
{ max: 50, message: "浜у搧鍚嶇О涓嶈兘瓒呰繃50涓瓧绗�", trigger: "blur" },
],
model: [{ required: true, message: "璇疯緭鍏ュ浘绾哥紪鍙�", trigger: "blur" }],
- unit: [{ required: true, message: "璇疯緭鍏ュ崟浣�", trigger: "blur" }],
- drawingNumber: [],
+ unit: [{ required: true, message: "璇烽�夋嫨鍗曚綅", trigger: "change" }],
productType: [{ required: true, message: "璇烽�夋嫨浜у搧灞炴��", trigger: "change" }],
},
});
@@ -334,10 +380,27 @@
modelForm.value.model = "";
modelForm.value.id = "";
modelForm.value.unit = "";
- modelForm.value.drawingNumber = "";
modelForm.value.productType = null;
+ modelForm.value.routeId = null;
+ modelForm.value.drawingFile = "";
+ modelForm.value.tempFileIds = [];
+ modelForm.value.salesLedgerFiles = [];
+ drawingFileList.value = [];
if (type === "edit") {
modelForm.value = { ...data };
+ modelForm.value.tempFileIds = data.tempFileIds || [];
+ modelForm.value.salesLedgerFiles = data.salesLedgerFiles || [];
+ if (data.salesLedgerFiles && data.salesLedgerFiles.length > 0) {
+ drawingFileList.value = data.salesLedgerFiles.map(file => ({
+ name: file.name,
+ url: file.url
+ }));
+ } else if (data.drawingFile) {
+ drawingFileList.value = [{
+ name: data.drawingFile.split('/').pop(),
+ url: data.drawingFile
+ }];
+ }
}
};
@@ -429,7 +492,61 @@
proxy.download("/basic/product/downloadTemplate", {}, "浜у搧瀵煎叆妯℃澘.xlsx");
};
-getModelList();
+const getProcessRouteListData = () => {
+ getProcessRouteList({ current: 1, size: 1000 }).then((res) => {
+ processRouteList.value = res.data.records || [];
+ }).catch(() => {
+ processRouteList.value = [];
+ });
+};
+
+const handleDrawingBeforeUpload = (file) => {
+ const isAllowed = [
+ 'application/pdf',
+ 'image/jpeg',
+ 'image/jpg',
+ 'image/png',
+ 'application/dwg'
+ ].includes(file.type) || file.name.endsWith('.dwg');
+ const isLt10M = file.size / 1024 / 1024 < 10;
+
+ if (!isAllowed) {
+ proxy.$modal.msgError("鍙兘涓婁紶 pdf銆乯pg銆乯peg銆乸ng銆乨wg 鏍煎紡鐨勬枃浠讹紒");
+ return false;
+ }
+ if (!isLt10M) {
+ proxy.$modal.msgError("涓婁紶鏂囦欢澶у皬涓嶈兘瓒呰繃 10MB锛�");
+ return false;
+ }
+ return true;
+};
+
+const handleDrawingUploadSuccess = (response, file, fileList) => {
+ console.log('涓婁紶鎴愬姛鍝嶅簲', response);
+ console.log('response.data', response.data);
+ if (response.code === 200) {
+ modelForm.value.tempFileIds = [response.data?.tempId];
+ modelForm.value.salesLedgerFiles = [{
+ tempId: response.data?.tempId,
+ originalName: response.data?.originalName || file.name,
+ tempPath: response.data?.tempPath,
+ type: response.data?.type || 13
+ }];
+ proxy.$modal.msgSuccess("涓婁紶鎴愬姛");
+ } else {
+ proxy.$modal.msgError(response.msg || "涓婁紶澶辫触");
+ }
+};
+
+const handleDrawingRemove = (file) => {
+ modelForm.value.tempFileIds = [];
+ modelForm.value.salesLedgerFiles = [];
+};
+
+onMounted(() => {
+ getModelList();
+ getProcessRouteListData();
+});
</script>
<style scoped>
@@ -469,6 +586,31 @@
margin-top: 16px;
}
+.avatar-uploader-icon {
+ font-size: 28px;
+ color: #8c939d;
+ width: 148px;
+ height: 148px;
+ text-align: center;
+ line-height: 148px;
+}
+
+:deep(.el-upload--picture-card) {
+ width: 148px;
+ height: 148px;
+}
+
+:deep(.el-upload-list__item) {
+ width: 148px;
+ height: 148px;
+}
+
+:deep(.el-upload__tip) {
+ font-size: 12px;
+ color: #909399;
+ margin-top: 8px;
+}
+
:deep(.el-dialog__body) {
padding: 20px 24px;
}
diff --git a/src/views/productionManagement/processRoute/Edit.vue b/src/views/productionManagement/processRoute/Edit.vue
deleted file mode 100644
index 0c0fe0f..0000000
--- a/src/views/productionManagement/processRoute/Edit.vue
+++ /dev/null
@@ -1,252 +0,0 @@
-<template>
- <div>
- <el-dialog
- v-model="isShow"
- title="缂栬緫宸ヨ壓璺嚎"
- width="400"
- @close="closeModal"
- >
- <el-form label-width="140px" :model="formState" label-position="top" ref="formRef">
- <el-form-item
- label="浜у搧鍚嶇О"
- prop="productModelId"
- :rules="[
- {
- required: true,
- message: '璇烽�夋嫨浜у搧',
- trigger: 'change',
- }
- ]"
- >
- <el-button type="primary" @click="showProductSelectDialog = true">
- {{ formState.productName && formState.productModelName
- ? `${formState.productName} - ${formState.productModelName}`
- : '閫夋嫨浜у搧' }}
- </el-button>
- </el-form-item>
-
- <el-form-item
- label="BOM"
- prop="bomId"
- :rules="[
- {
- required: true,
- message: '璇烽�夋嫨BOM',
- trigger: 'change',
- }
- ]"
- >
- <el-select
- v-model="formState.bomId"
- placeholder="璇烽�夋嫨BOM"
- clearable
- :disabled="!formState.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="description">
- <el-input v-model="formState.description" type="textarea" />
- </el-form-item>
- </el-form>
-
- <!-- 浜у搧閫夋嫨寮圭獥 -->
- <ProductSelectDialog
- v-model="showProductSelectDialog"
- @confirm="handleProductSelect"
- single
- />
- <template #footer>
- <div class="dialog-footer">
- <el-button type="primary" @click="handleSubmit">纭</el-button>
- <el-button @click="closeModal">鍙栨秷</el-button>
- </div>
- </template>
- </el-dialog>
- </div>
-</template>
-
-<script setup>
-import {ref, computed, getCurrentInstance, onMounted, nextTick, watch} from "vue";
-import {update} from "@/api/productionManagement/processRoute.js";
-import {getByModel} from "@/api/productionManagement/productBom.js";
-import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
-
-const props = defineProps({
- visible: {
- type: Boolean,
- required: true,
- },
-
- record: {
- type: Object,
- required: true,
- }
-});
-
-const emit = defineEmits(['update:visible', 'completed']);
-
-// 鍝嶅簲寮忔暟鎹紙鏇夸唬閫夐」寮忕殑 data锛�
-const formState = ref({
- productId: undefined,
- productModelId: undefined,
- productName: "",
- productModelName: "",
- bomId: undefined,
- description: '',
-});
-
-const isShow = computed({
- get() {
- return props.visible;
- },
- set(val) {
- emit('update:visible', val);
- },
-});
-
-const showProductSelectDialog = ref(false);
-const bomOptions = ref([]);
-
-let { proxy } = getCurrentInstance()
-
-const closeModal = () => {
- isShow.value = false;
-};
-
-// 璁剧疆琛ㄥ崟鏁版嵁
-const setFormData = () => {
- if (props.record) {
- formState.value = {
- ...props.record,
- productId: props.record.productId,
- productModelId: props.record.productModelId,
- productName: props.record.productName || "",
- // 娉ㄦ剰锛歳ecord涓殑瀛楁鏄痬odel锛岄渶瑕佹槧灏勫埌productModelName
- productModelName: props.record.model || props.record.productModelName || "",
- bomId: props.record.bomId,
- description: props.record.description || '',
- };
- // 濡傛灉鏈変骇鍝佸瀷鍙稩D锛屽姞杞紹OM鍒楄〃
- if (props.record.productModelId) {
- loadBomList(props.record.productModelId);
- }
- }
-}
-
-// 鍔犺浇BOM鍒楄〃
-const loadBomList = async (productModelId) => {
- if (!productModelId) {
- bomOptions.value = [];
- return;
- }
- try {
- const res = await getByModel(productModelId);
- // 澶勭悊杩斿洖鐨凚OM鏁版嵁锛氬彲鑳芥槸鏁扮粍銆佸璞℃垨鍖呭惈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];
- }
- bomOptions.value = bomList;
- } catch (error) {
- console.error("鍔犺浇BOM鍒楄〃澶辫触锛�", error);
- bomOptions.value = [];
- }
-};
-
-// 浜у搧閫夋嫨澶勭悊
-const handleProductSelect = async (products) => {
- if (products && products.length > 0) {
- const product = products[0];
- // 鍏堟煡璇OM鍒楄〃锛堝繀閫夛級
- try {
- const res = await getByModel(product.id);
- // 澶勭悊杩斿洖鐨凚OM鏁版嵁锛氬彲鑳芥槸鏁扮粍銆佸璞℃垨鍖呭惈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) {
- formState.value.productModelId = product.id;
- formState.value.productName = product.productName;
- formState.value.productModelName = product.model;
- // 濡傛灉褰撳墠閫夋嫨鐨凚OM涓嶅湪鏂板垪琛ㄤ腑锛屽垯閲嶇疆BOM閫夋嫨
- const currentBomExists = bomList.some(bom => bom.id === formState.value.bomId);
- if (!currentBomExists) {
- formState.value.bomId = undefined;
- }
- bomOptions.value = bomList;
- showProductSelectDialog.value = false;
- // 瑙﹀彂琛ㄥ崟楠岃瘉鏇存柊
- proxy.$refs["formRef"]?.validateField('productModelId');
- } else {
- proxy.$modal.msgError("璇ヤ骇鍝佹病鏈塀OM锛岃鍏堝垱寤築OM");
- }
- } catch (error) {
- // 濡傛灉鎺ュ彛杩斿洖404鎴栧叾浠栭敊璇紝璇存槑娌℃湁BOM
- proxy.$modal.msgError("璇ヤ骇鍝佹病鏈塀OM锛岃鍏堝垱寤築OM");
- }
- }
-};
-
-const handleSubmit = () => {
- proxy.$refs["formRef"].validate(valid => {
- if (valid) {
- // 楠岃瘉鏄惁閫夋嫨浜嗕骇鍝佸拰BOM
- if (!formState.value.productModelId) {
- proxy.$modal.msgError("璇烽�夋嫨浜у搧");
- return;
- }
- if (!formState.value.bomId) {
- proxy.$modal.msgError("璇烽�夋嫨BOM");
- return;
- }
- update(formState.value).then(res => {
- // 鍏抽棴妯℃�佹
- isShow.value = false;
- // 鍛婄煡鐖剁粍浠跺凡瀹屾垚
- emit('completed');
- proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
- })
- }
- })
-};
-
-defineExpose({
- closeModal,
- handleSubmit,
- isShow,
-});
-
-
-// 鐩戝惉寮圭獥鎵撳紑锛屽垵濮嬪寲琛ㄥ崟鏁版嵁
-watch(() => props.visible, (visible) => {
- if (visible && props.record) {
- nextTick(() => {
- setFormData();
- });
- }
-}, { immediate: true });
-
-onMounted(() => {
- if (props.visible && props.record) {
- setFormData();
- }
-});
-</script>
diff --git a/src/views/productionManagement/processRoute/Form.vue b/src/views/productionManagement/processRoute/Form.vue
new file mode 100644
index 0000000..65fba7e
--- /dev/null
+++ b/src/views/productionManagement/processRoute/Form.vue
@@ -0,0 +1,376 @@
+<template>
+ <div>
+ <el-dialog
+ v-model="isShow"
+ :title="isEdit ? '缂栬緫宸ヨ壓璺嚎' : '鍒涘缓宸ヨ壓璺嚎'"
+ width="900px"
+ @close="closeModal"
+ >
+ <el-form label-width="140px" :model="formState" label-position="top" ref="formRef">
+ <el-form-item label="宸ヨ壓璺嚎缂栧彿">
+ <el-input v-model="formState.processRouteCode" placeholder="璇疯緭鍏ワ紝蹇界暐灏嗚嚜鍔ㄧ敓鎴�" clearable />
+ </el-form-item>
+
+ <el-form-item
+ label="宸ヨ壓璺嚎鍚嶇О"
+ prop="processRouteName"
+ :rules="[
+ {
+ required: true,
+ message: '璇疯緭鍏ュ伐鑹鸿矾绾垮悕绉�',
+ trigger: 'blur',
+ }
+ ]"
+ >
+ <el-input v-model="formState.processRouteName" placeholder="璇峰~鍐�" clearable />
+ </el-form-item>
+
+ <div class="section-title">宸ュ簭鍒楄〃</div>
+
+ <div class="table-actions">
+ <el-button type="primary" link @click="addRow">
+ <el-icon><Plus /></el-icon> 娣诲姞涓�琛�
+ </el-button>
+ </div>
+
+ <el-table
+ ref="tableRef"
+ :data="formState.processRouteItems"
+ border
+ style="width: 100%"
+ class="process-table"
+ row-key="tempId"
+ >
+ <el-table-column label="鎷栨嫿" width="60" align="center">
+ <template #default>
+ <el-icon class="drag-handle"><Rank /></el-icon>
+ </template>
+ </el-table-column>
+ <el-table-column label="宸ュ簭" prop="processId" min-width="200">
+ <template #header>
+ <span class="required">宸ュ簭</span>
+ </template>
+ <template #default="scope">
+ <el-select
+ v-model="scope.row.processId"
+ placeholder="璇烽�夋嫨"
+ clearable
+ style="width: 100%"
+ >
+ <el-option
+ v-for="item in processOptions"
+ :key="item.id"
+ :label="item.name"
+ :value="item.id"
+ />
+ </el-select>
+ </template>
+ </el-table-column>
+ <el-table-column label="鏄惁璐ㄦ" prop="isQuality" width="100" align="center">
+ <template #default="scope">
+ <el-switch v-model="scope.row.isQuality" />
+ </template>
+ </el-table-column>
+ <el-table-column label="鎿嶄綔" width="80" align="center" fixed="right">
+ <template #default="scope">
+ <el-button type="danger" link @click="deleteRow(scope.$index)">
+ <el-icon><Delete /></el-icon>
+ </el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+
+ <el-empty v-if="formState.processRouteItems.length === 0" description="鏆傛棤鏁版嵁" />
+ </el-form>
+
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button type="primary" @click="handleSubmit">纭</el-button>
+ <el-button @click="closeModal">鍙栨秷</el-button>
+ </div>
+ </template>
+ </el-dialog>
+ </div>
+</template>
+
+<script setup>
+import {ref, computed, getCurrentInstance, onMounted, nextTick, watch, onUnmounted} from "vue";
+import {add, update} from "@/api/productionManagement/processRoute.js";
+import {findProcessRouteItemList} from "@/api/productionManagement/processRouteItem.js";
+import {processList} from "@/api/productionManagement/productionProcess.js";
+import {Plus, Delete, Rank} from '@element-plus/icons-vue';
+import Sortable from 'sortablejs';
+
+const props = defineProps({
+ visible: {
+ type: Boolean,
+ required: true,
+ }
+});
+
+const emit = defineEmits(['update:visible', 'completed']);
+
+const isEdit = computed(() => {
+ return formState.value && formState.value.id;
+});
+
+const isShow = computed({
+ get() {
+ return props.visible;
+ },
+ set(val) {
+ emit('update:visible', val);
+ },
+});
+
+const processOptions = ref([]);
+
+let { proxy } = getCurrentInstance();
+
+const tableRef = ref(null);
+let sortable = null;
+
+let tempIdCounter = 0;
+
+const formState = ref({
+ id: undefined,
+ processRouteCode: '',
+ processRouteName: '',
+ processRouteItems: [],
+});
+
+const initSortable = () => {
+ if (sortable) {
+ sortable.destroy();
+ sortable = null;
+ }
+
+ nextTick(() => {
+ if (tableRef.value) {
+ const table = tableRef.value.$el.querySelector('.el-table__body-wrapper tbody');
+ if (table) {
+ sortable = Sortable.create(table, {
+ animation: 150,
+ handle: '.drag-handle',
+ ghostClass: 'sortable-ghost',
+ onEnd: (evt) => {
+ const { oldIndex, newIndex } = evt;
+ if (oldIndex !== undefined && newIndex !== undefined && oldIndex !== newIndex) {
+ const item = formState.value.processRouteItems.splice(oldIndex, 1)[0];
+ formState.value.processRouteItems.splice(newIndex, 0, item);
+ }
+ }
+ });
+ }
+ }
+ });
+};
+
+const getProcessList = () => {
+ processList({}).then(res => {
+ processOptions.value = res.data || [];
+ }).catch(err => {
+ console.error("鑾峰彇宸ュ簭鍒楄〃澶辫触锛�", err);
+ });
+};
+
+const closeModal = () => {
+ formState.value = {
+ id: undefined,
+ processRouteCode: '',
+ processRouteName: '',
+ processRouteItems: [],
+ };
+ isShow.value = false;
+};
+
+const setFormData = async () => {
+ if (isEdit.value) {
+ formState.value = {
+ id: props.record.id,
+ processRouteCode: props.record.processRouteCode || '',
+ processRouteName: props.record.processRouteName || '',
+ processRouteItems: (props.record.processRouteItems || []).map((item, index) => ({
+ tempId: item.id || `temp_${tempIdCounter++}`,
+ processId: item.processId,
+ id: item.id,
+ isQuality: item.isQuality !== undefined ? item.isQuality : false,
+ dragSort: index + 1,
+ })),
+ };
+ } else {
+ formState.value = {
+ id: undefined,
+ processRouteCode: '',
+ processRouteName: '',
+ processRouteItems: [],
+ };
+ }
+}
+
+const addRow = () => {
+ formState.value.processRouteItems.push({
+ tempId: `temp_${tempIdCounter++}`,
+ processId: undefined,
+ isQuality: false,
+ });
+ nextTick(() => {
+ initSortable();
+ });
+};
+
+const deleteRow = (index) => {
+ formState.value.processRouteItems.splice(index, 1);
+ nextTick(() => {
+ initSortable();
+ });
+};
+
+const handleSubmit = () => {
+ proxy.$refs["formRef"].validate(valid => {
+ if (valid) {
+ if (formState.value.processRouteItems.length === 0) {
+ proxy.$modal.msgError("璇疯嚦灏戞坊鍔犱竴涓伐搴�");
+ return;
+ }
+
+ for (let i = 0; i < formState.value.processRouteItems.length; i++) {
+ const row = formState.value.processRouteItems[i];
+ if (!row.processId) {
+ proxy.$modal.msgError(`绗�${i + 1}琛岋細璇烽�夋嫨宸ュ簭`);
+ return;
+ }
+ }
+
+ const submitData = {
+ id: formState.value.id,
+ processRouteCode: formState.value.processRouteCode,
+ processRouteName: formState.value.processRouteName,
+ processRouteItems: formState.value.processRouteItems.map((item, index) => ({
+ id: item.id,
+ routeId: formState.value.id,
+ processId: item.processId,
+ isQuality: item.isQuality,
+ dragSort: index + 1,
+ })),
+ };
+
+ const apiCall = isEdit.value ? update(submitData) : add(submitData);
+
+ apiCall.then(res => {
+ isShow.value = false;
+ emit('completed');
+ proxy.$modal.msgSuccess(isEdit.value ? "缂栬緫鎴愬姛" : "鏂板鎴愬姛");
+ });
+ }
+ });
+};
+
+// 鐩戝惉 visible 鍙樺寲
+watch(() => props.visible, (visible) => {
+ if (visible) {
+ nextTick(() => {
+ initSortable();
+ });
+ }
+});
+
+const setData = async (row) => {
+ if (row) {
+ formState.value = {
+ id: row.id,
+ processRouteCode: row.processRouteCode || '',
+ processRouteName: row.processRouteName || '',
+ processRouteItems: [],
+ };
+
+ const res = await findProcessRouteItemList({ routeId: row.id });
+ if (res.data && Array.isArray(res.data)) {
+ formState.value.processRouteItems = res.data.map((item, index) => ({
+ tempId: item.id || `temp_${tempIdCounter++}`,
+ processId: item.processId,
+ id: item.id,
+ isQuality: item.isQuality !== undefined ? item.isQuality : false,
+ dragSort: index + 1,
+ }));
+ }
+
+ nextTick(() => {
+ initSortable();
+ });
+ } else {
+ formState.value = {
+ id: undefined,
+ processRouteCode: '',
+ processRouteName: '',
+ processRouteItems: [],
+ };
+
+ nextTick(() => {
+ initSortable();
+ });
+ }
+};
+
+onMounted(() => {
+ getProcessList();
+});
+
+onUnmounted(() => {
+ if (sortable) {
+ sortable.destroy();
+ sortable = null;
+ }
+});
+
+defineExpose({
+ closeModal,
+ handleSubmit,
+ isShow,
+ setData,
+});
+</script>
+
+<style scoped>
+.section-title {
+ font-size: 14px;
+ font-weight: bold;
+ margin: 20px 0 10px 0;
+ color: #333;
+}
+
+.table-actions {
+ display: flex;
+ gap: 16px;
+ margin-bottom: 10px;
+}
+
+.process-table {
+ margin-bottom: 20px;
+}
+
+.required::before {
+ content: '*';
+ color: #f56c6c;
+ margin-right: 4px;
+}
+
+:deep(.el-dialog__body) {
+ padding-top: 10px;
+}
+
+.sortable-ghost {
+ opacity: 0.4;
+ background-color: #f5f7fa;
+}
+
+.drag-handle {
+ cursor: move;
+ font-size: 18px;
+ color: #909399;
+}
+
+.drag-handle:hover {
+ color: #409eff;
+}
+</style>
diff --git a/src/views/productionManagement/processRoute/New.vue b/src/views/productionManagement/processRoute/New.vue
deleted file mode 100644
index 62c6873..0000000
--- a/src/views/productionManagement/processRoute/New.vue
+++ /dev/null
@@ -1,194 +0,0 @@
-<template>
- <div>
- <el-dialog
- v-model="isShow"
- title="鏂板宸ヨ壓璺嚎"
- width="400"
- @close="closeModal"
- >
- <el-form label-width="140px" :model="formState" label-position="top" ref="formRef">
- <el-form-item
- label="浜у搧鍚嶇О"
- prop="productModelId"
- :rules="[
- {
- required: true,
- message: '璇烽�夋嫨浜у搧',
- trigger: 'change',
- }
- ]"
- >
- <el-button type="primary" @click="showProductSelectDialog = true">
- {{ formState.productName && formState.productModelName
- ? `${formState.productName} - ${formState.productModelName}`
- : '閫夋嫨浜у搧' }}
- </el-button>
- </el-form-item>
-
- <el-form-item
- label="BOM"
- prop="bomId"
- :rules="[
- {
- required: true,
- message: '璇烽�夋嫨BOM',
- trigger: 'change',
- }
- ]"
- >
- <el-select
- v-model="formState.bomId"
- placeholder="璇烽�夋嫨BOM"
- clearable
- :disabled="!formState.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="description">
- <el-input v-model="formState.description" type="textarea" />
- </el-form-item>
- </el-form>
-
- <!-- 浜у搧閫夋嫨寮圭獥 -->
- <ProductSelectDialog
- v-model="showProductSelectDialog"
- @confirm="handleProductSelect"
- single
- />
- <template #footer>
- <div class="dialog-footer">
- <el-button type="primary" @click="handleSubmit">纭</el-button>
- <el-button @click="closeModal">鍙栨秷</el-button>
- </div>
- </template>
- </el-dialog>
- </div>
-</template>
-
-<script setup>
-import {ref, computed, getCurrentInstance} from "vue";
-import {add} from "@/api/productionManagement/processRoute.js";
-import {getByModel} from "@/api/productionManagement/productBom.js";
-import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
-
-const props = defineProps({
- visible: {
- type: Boolean,
- required: true,
- },
-});
-
-const emit = defineEmits(['update:visible', 'completed']);
-
-// 鍝嶅簲寮忔暟鎹紙鏇夸唬閫夐」寮忕殑 data锛�
-const formState = ref({
- productId: undefined,
- productModelId: undefined,
- productName: "",
- productModelName: "",
- bomId: undefined,
- description: '',
-});
-
-const isShow = computed({
- get() {
- return props.visible;
- },
- set(val) {
- emit('update:visible', val);
- },
-});
-
-const showProductSelectDialog = ref(false);
-const bomOptions = ref([]);
-
-let { proxy } = getCurrentInstance()
-
-const closeModal = () => {
- // 閲嶇疆琛ㄥ崟鏁版嵁
- formState.value = {
- productId: undefined,
- productModelId: undefined,
- productName: "",
- productModelName: "",
- bomId: undefined,
- description: '',
- };
- bomOptions.value = [];
- isShow.value = false;
-};
-
-// 浜у搧閫夋嫨澶勭悊
-const handleProductSelect = async (products) => {
- if (products && products.length > 0) {
- const product = products[0];
- // 鍏堟煡璇OM鍒楄〃锛堝繀閫夛級
- try {
- const res = await getByModel(product.id);
- // 澶勭悊杩斿洖鐨凚OM鏁版嵁锛氬彲鑳芥槸鏁扮粍銆佸璞℃垨鍖呭惈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) {
- formState.value.productModelId = product.id;
- formState.value.productName = product.productName;
- formState.value.productModelName = product.model;
- formState.value.bomId = undefined; // 閲嶇疆BOM閫夋嫨
- bomOptions.value = bomList;
- showProductSelectDialog.value = false;
- // 瑙﹀彂琛ㄥ崟楠岃瘉鏇存柊
- proxy.$refs["formRef"]?.validateField('productModelId');
- } else {
- proxy.$modal.msgError("璇ヤ骇鍝佹病鏈塀OM锛岃鍏堝垱寤築OM");
- }
- } catch (error) {
- // 濡傛灉鎺ュ彛杩斿洖404鎴栧叾浠栭敊璇紝璇存槑娌℃湁BOM
- proxy.$modal.msgError("璇ヤ骇鍝佹病鏈塀OM锛岃鍏堝垱寤築OM");
- }
- }
-};
-
-const handleSubmit = () => {
- proxy.$refs["formRef"].validate(valid => {
- if (valid) {
- // 楠岃瘉鏄惁閫夋嫨浜嗕骇鍝佸拰BOM
- if (!formState.value.productModelId) {
- proxy.$modal.msgError("璇烽�夋嫨浜у搧");
- return;
- }
- if (!formState.value.bomId) {
- proxy.$modal.msgError("璇烽�夋嫨BOM");
- return;
- }
- add(formState.value).then(res => {
- // 鍏抽棴妯℃�佹
- isShow.value = false;
- // 鍛婄煡鐖剁粍浠跺凡瀹屾垚
- emit('completed');
- proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
- })
- }
- })
-};
-
-
-defineExpose({
- closeModal,
- handleSubmit,
- isShow,
-});
-</script>
diff --git a/src/views/productionManagement/processRoute/index.vue b/src/views/productionManagement/processRoute/index.vue
index 9a4950d..a4a9d3f 100644
--- a/src/views/productionManagement/processRoute/index.vue
+++ b/src/views/productionManagement/processRoute/index.vue
@@ -2,13 +2,25 @@
<div class="app-container">
<div class="search_form">
<el-form :model="searchForm" :inline="true">
- <el-form-item label="瑙勬牸鍚嶇О:">
- <el-input v-model="searchForm.model" placeholder="璇疯緭鍏�" clearable prefix-icon="Search"
- style="width: 200px;"
- @change="handleQuery" />
+ <el-form-item label="宸ヨ壓璺嚎缂栧彿">
+ <el-input v-model="searchForm.processRouteCode" placeholder="璇疯緭鍏ュ伐鑹鸿矾绾跨紪鍙�" clearable style="width: 200px;" />
+ </el-form-item>
+ <el-form-item label="宸ヨ壓璺嚎鍚嶇О">
+ <el-input v-model="searchForm.processRouteName" placeholder="璇疯緭鍏ュ伐鑹鸿矾绾垮悕绉�" clearable style="width: 200px;" />
+ </el-form-item>
+ <el-form-item label="宸ュ簭鍚嶇О">
+ <el-select v-model="searchForm.processName" placeholder="璇烽�夋嫨宸ュ簭" clearable style="width: 200px;">
+ <el-option
+ v-for="item in processOptions"
+ :key="item.id"
+ :label="item.name"
+ :value="item.name"
+ />
+ </el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleQuery">鎼滅储</el-button>
+ <el-button @click="resetQuery">閲嶇疆</el-button>
</el-form-item>
</el-form>
</div>
@@ -29,87 +41,65 @@
:total="page.total"
/>
</div>
- <new-process
- v-if="isShowNewModal"
- v-model:visible="isShowNewModal"
- @completed="getList"
- />
-
- <edit-process
- v-if="isShowEditModal"
- v-model:visible="isShowEditModal"
- :record="record"
- @completed="getList"
- />
-
- <route-item-form
- v-if="isShowItemModal"
- v-model:visible="isShowItemModal"
- :record="record"
+ <process-form
+ v-if="isShowFormModal"
+ ref="formRef"
+ v-model:visible="isShowFormModal"
@completed="getList"
/>
</div>
</template>
<script setup>
-import {onMounted, ref} from "vue";
-import NewProcess from "@/views/productionManagement/processRoute/New.vue";
-import EditProcess from "@/views/productionManagement/processRoute/Edit.vue";
-import RouteItemForm from "@/views/productionManagement/processRoute/ItemsForm.vue";
+import {onMounted, ref, nextTick} from "vue";
+import ProcessForm from "@/views/productionManagement/processRoute/Form.vue";
import {listPage, del} from "@/api/productionManagement/processRoute.js";
-import { useRouter } from 'vue-router'
+import {processList} from "@/api/productionManagement/productionProcess.js";
-const router = useRouter()
const data = reactive({
searchForm: {
- model: "",
+ processRouteCode: '',
+ processRouteName: '',
+ processName: undefined,
},
});
const { searchForm } = toRefs(data);
+
+const processOptions = ref([]);
const tableColumn = ref([
{
label: "宸ヨ壓璺嚎缂栧彿",
prop: "processRouteCode",
},
{
- label: "浜у搧鍚嶇О",
- prop: "productName",
- },
- {
- label: "鍥剧焊缂栧彿",
- prop: "model",
- },
- {
- label: "瑙勬牸鍚嶇О",
- prop: "drawingNumber",
+ label: "宸ヨ壓璺嚎鍚嶇О",
+ prop: "processRouteName",
},
{
- label: "BOM缂栧彿",
- prop: "bomNo",
+ label: "宸ュ簭鍒楄〃",
+ prop: "processName",
+ minWidth: 300,
},
{
- label: "鎻忚堪",
- prop: "description",
+ label: "鍒涘缓浜�",
+ prop: "createBy",
+ },
+ {
+ label: "鍒涘缓鏃堕棿",
+ prop: "createTime",
},
{
dataType: "action",
label: "鎿嶄綔",
align: "center",
fixed: "right",
- width: 280,
+ width: 150,
operation: [
{
name: "缂栬緫",
type: "text",
clickFun: (row) => {
showEditModal(row);
- }
- },
- {
- name: "璺嚎椤圭洰",
- type: "text",
- clickFun: (row) => {
- showItemModal(row);
}
}
]
@@ -118,10 +108,9 @@
const tableData = ref([]);
const selectedRows = ref([]);
const tableLoading = ref(false);
-const isShowNewModal = ref(false);
-const isShowEditModal = ref(false);
-const isShowItemModal = ref(false);
+const isShowFormModal = ref(false);
const record = ref({});
+const formRef = ref(null);
const page = reactive({
current: 1,
size: 100,
@@ -132,6 +121,17 @@
// 鏌ヨ鍒楄〃
/** 鎼滅储鎸夐挳鎿嶄綔 */
const handleQuery = () => {
+ page.current = 1;
+ getList();
+};
+
+/** 閲嶇疆鎸夐挳鎿嶄綔 */
+const resetQuery = () => {
+ searchForm.value = {
+ processRouteCode: '',
+ processRouteName: '',
+ processName: undefined,
+ };
page.current = 1;
getList();
};
@@ -162,28 +162,14 @@
// 鎵撳紑鏂板寮规
const showNewModal = () => {
- isShowNewModal.value = true
+ isShowFormModal.value = true;
+ record.value = {};
};
-const showEditModal = (row) => {
- isShowEditModal.value = true
- record.value = row
-};
-
-const showItemModal = (row) => {
- router.push({
- path: '/productionManagement/processRouteItem',
- query: {
- id: row.id,
- processRouteCode: row.processRouteCode || '',
- drawingNumber: row.drawingNumber || "",
- productName: row.productName || '',
- model: row.model || '',
- bomNo: row.bomNo || '',
- description: row.description || '',
- type: 'route',
- }
- })
+const showEditModal = async (row) => {
+ isShowFormModal.value = true;
+ await nextTick();
+ await formRef.value && formRef.value.setData({ id: row.id, processRouteCode: row.processRouteCode, processRouteName: row.processRouteName });
};
// 鍒犻櫎
@@ -202,8 +188,18 @@
}
onMounted(() => {
+ getProcessList();
getList();
});
+
+// 鑾峰彇宸ュ簭鍒楄〃
+const getProcessList = () => {
+ processList({}).then(res => {
+ processOptions.value = res.data || [];
+ }).catch(err => {
+ console.error("鑾峰彇宸ュ簭鍒楄〃澶辫触锛�", err);
+ });
+};
</script>
<style scoped></style>
diff --git a/src/views/productionManagement/processRoute/processRouteItem/index.vue b/src/views/productionManagement/processRoute/processRouteItem/index.vue
index 0f0be43..65035ec 100644
--- a/src/views/productionManagement/processRoute/processRouteItem/index.vue
+++ b/src/views/productionManagement/processRoute/processRouteItem/index.vue
@@ -96,6 +96,11 @@
{{scope.row.isQuality ? "鏄�" : "鍚�"}}
</template>
</el-table-column>
+ <el-table-column label="鎶ュ伐鏉冮檺" prop="userPower" min-width="200">
+ <template #default="scope">
+ {{ scope.row.userPower || '-' }}
+ </template>
+ </el-table-column>
<el-table-column label="鎿嶄綔" align="center" fixed="right" width="150">
<template #default="scope">
<el-button type="primary" link size="small" @click="handleEdit(scope.row)" :disabled="scope.row.isComplete">缂栬緫</el-button>
@@ -207,7 +212,22 @@
</el-form-item>
<el-form-item label="鏄惁璐ㄦ" prop="isQuality">
- <el-switch v-model="form.isQuality" :active-value="true" inactive-value="false"/>
+ <el-switch v-model="form.isQuality" :active-value="true" :inactive-value="false"/>
+ </el-form-item>
+ <el-form-item label="鎶ュ伐鏉冮檺" prop="userPower" :rules="[{ required: true, message: '璇烽�夋嫨鎶ュ伐鏉冮檺', trigger: 'change' }]">
+ <el-tree-select
+ v-model="form.userPower"
+ :data="staffList"
+ :props="treeProps"
+ placeholder="璇烽�夋嫨浜哄憳"
+ multiple
+ show-checkbox
+ collapse-tags
+ collapse-tags-tooltip
+ style="width: 100%"
+ node-key="id"
+ :render-after-expand="false"
+ />
</el-form-item>
</el-form>
@@ -232,6 +252,7 @@
import { findProcessRouteItemList, addOrUpdateProcessRouteItem, sortProcessRouteItem, batchDeleteProcessRouteItem } from "@/api/productionManagement/processRouteItem.js";
import { findProductProcessRouteItemList, deleteRouteItem, addRouteItem, addOrUpdateProductProcessRouteItem, sortRouteItem } from "@/api/productionManagement/productProcessRoute.js";
import { processList } from "@/api/productionManagement/productionProcess.js";
+import { listDeptUserTree } from "@/api/basicData/productProcess.js";
import { useRoute } from 'vue-router'
import { ElMessageBox } from 'element-plus'
import Sortable from 'sortablejs'
@@ -263,6 +284,13 @@
const processOptions = ref([]);
const showProductSelectDialog = ref(false);
+const staffList = ref([]);
+
+const treeProps = {
+ label: 'label',
+ children: 'children',
+};
+
let tableSortable = null;
let cardSortable = null;
@@ -284,6 +312,7 @@
model: "",
unit: "",
isQuality: false,
+ userPower: [],
});
const rules = {
@@ -356,6 +385,12 @@
// 缂栬緫
const handleEdit = (row) => {
operationType.value = 'edit';
+ const userPowerNames = row.userPower ? row.userPower.split(',') : [];
+ const userPowerIds = userPowerNames.map(name => {
+ const user = findUserByName(name);
+ return user ? user.id : null;
+ }).filter(id => id !== null);
+
form.value = {
id: row.id,
routeId: routeId.value,
@@ -365,8 +400,25 @@
model: row.model || "",
unit: row.unit || "",
isQuality: row.isQuality,
+ userPower: userPowerIds,
};
dialogVisible.value = true;
+};
+
+const findUserByName = (name) => {
+ const findInTree = (nodes) => {
+ for (const node of nodes) {
+ if (node.isUser && node.label === name) {
+ return node;
+ }
+ if (node.children && node.children.length > 0) {
+ const found = findInTree(node.children);
+ if (found) return found;
+ }
+ }
+ return null;
+ };
+ return findInTree(staffList.value);
};
// 鍒犻櫎
@@ -415,6 +467,8 @@
if (valid) {
submitLoading.value = true;
+ const userPowerNames = getAllUserNamesFromSelection(form.value.userPower);
+
if (operationType.value === 'add') {
// 鏂板锛氫紶鍗曚釜瀵硅薄锛屽寘鍚玠ragSort瀛楁
// dragSort = 褰撳墠鍒楄〃闀垮害 + 1锛岃〃绀烘柊澧炶褰曟帓鍦ㄦ渶鍚�
@@ -428,6 +482,7 @@
processId: form.value.processId,
productModelId: form.value.productModelId,
isQuality: form.value.isQuality,
+ userPower: userPowerNames.join(','),
dragSort,
})
: addOrUpdateProcessRouteItem({
@@ -435,6 +490,7 @@
processId: form.value.processId,
productModelId: form.value.productModelId,
isQuality: form.value.isQuality,
+ userPower: userPowerNames.join(','),
dragSort,
});
@@ -460,6 +516,7 @@
processId: form.value.processId,
productModelId: form.value.productModelId,
isQuality: form.value.isQuality,
+ userPower: userPowerNames.join(','),
})
: addOrUpdateProcessRouteItem({
routeId: routeId.value,
@@ -467,6 +524,7 @@
productModelId: form.value.productModelId,
id: form.value.id,
isQuality: form.value.isQuality,
+ userPower: userPowerNames.join(','),
});
updatePromise
@@ -640,8 +698,70 @@
getRouteInfo();
getList();
getProcessList();
+ getStaffList();
});
+const getStaffList = () => {
+ listDeptUserTree().then(res => {
+ const buildTree = (nodes) => {
+ return nodes.map(node => {
+ const deptNode = {
+ id: `dept_${node.deptId}`,
+ label: node.deptName,
+ isUser: false,
+ children: []
+ };
+
+ if (node.userList && node.userList.length > 0) {
+ node.userList.forEach(user => {
+ deptNode.children.push({
+ id: user.userId,
+ label: user.nickName || user.userName,
+ isUser: true,
+ userName: user.userName,
+ nickName: user.nickName
+ });
+ });
+ }
+
+ if (node.childrenList && node.childrenList.length > 0) {
+ const childNodes = buildTree(node.childrenList);
+ deptNode.children = deptNode.children.concat(childNodes);
+ }
+
+ return deptNode;
+ });
+ };
+ staffList.value = buildTree(res.data || []);
+ }).catch(() => {
+ staffList.value = [];
+ });
+};
+
+const getAllUserNamesFromSelection = (selectedIds) => {
+ const names = [];
+ const processNode = (node) => {
+ if (selectedIds.includes(node.id)) {
+ if (node.isUser) {
+ names.push(node.label);
+ } else {
+ if (node.children && node.children.length > 0) {
+ node.children.forEach(child => {
+ if (child.isUser) {
+ names.push(child.label);
+ }
+ });
+ }
+ }
+ }
+ if (node.children && node.children.length > 0) {
+ node.children.forEach(child => processNode(child));
+ }
+ };
+ staffList.value.forEach(node => processNode(node));
+ return [...new Set(names)];
+};
+
onUnmounted(() => {
destroySortable();
});
diff --git a/src/views/productionManagement/productionProcess/Edit.vue b/src/views/productionManagement/productionProcess/Edit.vue
index a9b266d..0920e4f 100644
--- a/src/views/productionManagement/productionProcess/Edit.vue
+++ b/src/views/productionManagement/productionProcess/Edit.vue
@@ -31,6 +31,21 @@
<el-form-item label="鏄惁璐ㄦ" prop="isQuality">
<el-switch v-model="formState.isQuality" :active-value="true" inactive-value="false"/>
</el-form-item>
+ <el-form-item label="鎶ュ伐鏉冮檺" prop="userPower" :rules="[{ required: true, message: '璇烽�夋嫨鎶ュ伐鏉冮檺', trigger: 'change' }]">
+ <el-tree-select
+ v-model="formState.userPower"
+ :data="staffList"
+ :props="treeProps"
+ placeholder="璇烽�夋嫨浜哄憳"
+ multiple
+ show-checkbox
+ collapse-tags
+ collapse-tags-tooltip
+ style="width: 100%"
+ node-key="id"
+ :render-after-expand="false"
+ />
+ </el-form-item>
<el-form-item label="澶囨敞" prop="remark">
<el-input v-model="formState.remark" type="textarea" />
</el-form-item>
@@ -46,8 +61,9 @@
</template>
<script setup>
-import { ref, computed, getCurrentInstance, watch } from "vue";
+import { ref, computed, getCurrentInstance, watch, onMounted } from "vue";
import {update} from "@/api/productionManagement/productionProcess.js";
+import { listDeptUserTree } from "@/api/basicData/productProcess.js";
const props = defineProps({
visible: {
@@ -71,7 +87,15 @@
remark: props.record.remark,
salaryQuota: props.record.salaryQuota,
isQuality: props.record.isQuality,
+ userPower: props.record.userPower ? props.record.userPower.split(',') : [],
});
+
+const staffList = ref([]);
+
+const treeProps = {
+ label: 'label',
+ children: 'children',
+};
const isShow = computed({
get() {
@@ -85,30 +109,23 @@
// 鐩戝惉 record 鍙樺寲锛屾洿鏂拌〃鍗曟暟鎹�
watch(() => props.record, (newRecord) => {
if (newRecord && isShow.value) {
+ const userPowerNames = newRecord.userPower ? newRecord.userPower.split(',') : [];
+ const userPowerIds = userPowerNames.map(name => {
+ const user = findUserByName(name);
+ return user ? user.id : null;
+ }).filter(id => id !== null);
+
formState.value = {
id: newRecord.id,
name: newRecord.name || '',
no: newRecord.no || '',
remark: newRecord.remark || '',
salaryQuota: newRecord.salaryQuota || '',
- isQuality: props.record.isQuality,
+ isQuality: newRecord.isQuality,
+ userPower: userPowerIds,
};
}
}, { immediate: true, deep: true });
-
-// 鐩戝惉寮圭獥鎵撳紑锛岄噸鏂板垵濮嬪寲琛ㄥ崟鏁版嵁
-watch(() => props.visible, (visible) => {
- if (visible && props.record) {
- formState.value = {
- id: props.record.id,
- name: props.record.name || '',
- no: props.record.no || '',
- remark: props.record.remark || '',
- salaryQuota: props.record.salaryQuota || '',
- isQuality: props.record.isQuality,
- };
- }
-});
let { proxy } = getCurrentInstance()
@@ -119,7 +136,13 @@
const handleSubmit = () => {
proxy.$refs["formRef"].validate(valid => {
if (valid) {
- update(formState.value).then(res => {
+ const userPowerNames = getAllUserNamesFromSelection(formState.value.userPower);
+
+ const submitData = {
+ ...formState.value,
+ userPower: userPowerNames.join(',')
+ };
+ update(submitData).then(res => {
// 鍏抽棴妯℃�佹
isShow.value = false;
// 鍛婄煡鐖剁粍浠跺凡瀹屾垚
@@ -130,6 +153,87 @@
})
};
+const findUserById = (userId) => {
+ const findInTree = (nodes) => {
+ for (const node of nodes) {
+ if (node.id === userId) {
+ return node;
+ }
+ if (node.children && node.children.length > 0) {
+ const found = findInTree(node.children);
+ if (found) return found;
+ }
+ }
+ return null;
+ };
+ return findInTree(staffList.value);
+};
+
+const getStaffList = () => {
+ listDeptUserTree().then(res => {
+ const buildTree = (nodes) => {
+ return nodes.map(node => {
+ const deptNode = {
+ id: `dept_${node.deptId}`,
+ label: node.deptName,
+ isUser: false,
+ children: []
+ };
+
+ if (node.userList && node.userList.length > 0) {
+ node.userList.forEach(user => {
+ deptNode.children.push({
+ id: user.userId,
+ label: user.nickName || user.userName,
+ isUser: true,
+ userName: user.userName,
+ nickName: user.nickName
+ });
+ });
+ }
+
+ if (node.childrenList && node.childrenList.length > 0) {
+ const childNodes = buildTree(node.childrenList);
+ deptNode.children = deptNode.children.concat(childNodes);
+ }
+
+ return deptNode;
+ });
+ };
+ staffList.value = buildTree(res.data || []);
+ }).catch(() => {
+ staffList.value = [];
+ });
+};
+
+const getAllUserNamesFromSelection = (selectedIds) => {
+ const names = [];
+ const processNode = (node) => {
+ if (selectedIds.includes(node.id)) {
+ if (node.isUser) {
+ names.push(node.label);
+ } else {
+ if (node.children && node.children.length > 0) {
+ node.children.forEach(child => {
+ if (child.isUser) {
+ names.push(child.label);
+ }
+ });
+ }
+ }
+ }
+ if (node.children && node.children.length > 0) {
+ node.children.forEach(child => processNode(child));
+ }
+ };
+ staffList.value.forEach(node => processNode(node));
+ return [...new Set(names)];
+};
+
+onMounted(() => {
+ getStaffList();
+});
+
defineExpose({
closeModal,
handleSubmit,
diff --git a/src/views/productionManagement/productionProcess/New.vue b/src/views/productionManagement/productionProcess/New.vue
index a5924e5..db69fbc 100644
--- a/src/views/productionManagement/productionProcess/New.vue
+++ b/src/views/productionManagement/productionProcess/New.vue
@@ -29,7 +29,22 @@
<!-- <el-input v-model="formState.salaryQuota" type="number" :step="0.001" />-->
<!-- </el-form-item>-->
<el-form-item label="鏄惁璐ㄦ" prop="isQuality">
- <el-switch v-model="formState.isQuality" :active-value="true" inactive-value="false"/>
+ <el-switch v-model="formState.isQuality" :active-value="true" :inactive-value="false"/>
+ </el-form-item>
+ <el-form-item label="鎶ュ伐鏉冮檺" prop="userPower" :rules="[{ required: true, message: '璇烽�夋嫨鎶ュ伐鏉冮檺', trigger: 'change' }]">
+ <el-tree-select
+ v-model="formState.userPower"
+ :data="staffList"
+ :props="treeProps"
+ placeholder="璇烽�夋嫨浜哄憳"
+ multiple
+ show-checkbox
+ collapse-tags
+ collapse-tags-tooltip
+ style="width: 100%"
+ node-key="id"
+ :render-after-expand="false"
+ />
</el-form-item>
<el-form-item label="澶囨敞" prop="remark">
<el-input v-model="formState.remark" type="textarea" />
@@ -46,8 +61,9 @@
</template>
<script setup>
-import { ref, computed, getCurrentInstance } from "vue";
+import { ref, computed, getCurrentInstance, onMounted } from "vue";
import {add} from "@/api/productionManagement/productionProcess.js";
+import { listDeptUserTree } from "@/api/basicData/productProcess.js";
const props = defineProps({
visible: {
@@ -64,7 +80,15 @@
remark: '',
salaryQuota: '',
isQuality: false,
+ userPower: [],
});
+
+const staffList = ref([]);
+
+const treeProps = {
+ label: 'label',
+ children: 'children',
+};
const isShow = computed({
get() {
@@ -84,7 +108,13 @@
const handleSubmit = () => {
proxy.$refs["formRef"].validate(valid => {
if (valid) {
- add(formState.value).then(res => {
+ const userPowerNames = getAllUserNamesFromSelection(formState.value.userPower);
+
+ const submitData = {
+ ...formState.value,
+ userPower: userPowerNames.join(',')
+ };
+ add(submitData).then(res => {
// 鍏抽棴妯℃�佹
isShow.value = false;
// 鍛婄煡鐖剁粍浠跺凡瀹屾垚
@@ -95,6 +125,87 @@
})
};
+const findUserById = (userId) => {
+ const findInTree = (nodes) => {
+ for (const node of nodes) {
+ if (node.id === userId) {
+ return node;
+ }
+ if (node.children && node.children.length > 0) {
+ const found = findInTree(node.children);
+ if (found) return found;
+ }
+ }
+ return null;
+ };
+ return findInTree(staffList.value);
+};
+
+const getStaffList = () => {
+ listDeptUserTree().then(res => {
+ const buildTree = (nodes) => {
+ return nodes.map(node => {
+ const deptNode = {
+ id: `dept_${node.deptId}`,
+ label: node.deptName,
+ isUser: false,
+ children: []
+ };
+
+ if (node.userList && node.userList.length > 0) {
+ node.userList.forEach(user => {
+ deptNode.children.push({
+ id: user.userId,
+ label: user.nickName || user.userName,
+ isUser: true,
+ userName: user.userName,
+ nickName: user.nickName
+ });
+ });
+ }
+
+ if (node.childrenList && node.childrenList.length > 0) {
+ const childNodes = buildTree(node.childrenList);
+ deptNode.children = deptNode.children.concat(childNodes);
+ }
+
+ return deptNode;
+ });
+ };
+ staffList.value = buildTree(res.data || []);
+ }).catch(() => {
+ staffList.value = [];
+ });
+};
+
+const getAllUserNamesFromSelection = (selectedIds) => {
+ const names = [];
+ const processNode = (node) => {
+ if (selectedIds.includes(node.id)) {
+ if (node.isUser) {
+ names.push(node.label);
+ } else {
+ if (node.children && node.children.length > 0) {
+ node.children.forEach(child => {
+ if (child.isUser) {
+ names.push(child.label);
+ }
+ });
+ }
+ }
+ }
+ if (node.children && node.children.length > 0) {
+ node.children.forEach(child => processNode(child));
+ }
+ };
+ staffList.value.forEach(node => processNode(node));
+ return [...new Set(names)];
+};
+
+onMounted(() => {
+ getStaffList();
+});
+
defineExpose({
closeModal,
handleSubmit,
diff --git a/src/views/productionManagement/productionProcess/index.vue b/src/views/productionManagement/productionProcess/index.vue
index f8521c6..bdbebff 100644
--- a/src/views/productionManagement/productionProcess/index.vue
+++ b/src/views/productionManagement/productionProcess/index.vue
@@ -98,6 +98,11 @@
label: "宸ュ簭鍚嶇О",
prop: "name",
},
+ {
+ label: "鎶ュ伐鏉冮檺",
+ prop: "userPower",
+ width: 200,
+ },
// {
// label: "宸ヨ祫瀹氶",
// prop: "salaryQuota",
--
Gitblit v1.9.3