| src/components/Dialog/ImportDialog.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/productionManagement/productionProcess/Edit.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/productionManagement/productionProcess/New.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/productionManagement/productionProcess/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/productionManagement/workOrderEdit/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
src/components/Dialog/ImportDialog.vue
@@ -39,8 +39,8 @@ </el-upload> <template #footer> <div class="dialog-footer"> <el-button type="primary" @click="handleConfirm">确 定</el-button> <el-button @click="handleCancel">取 消</el-button> <el-button type="primary" :loading="loading" @click="handleConfirm">确 定</el-button> <el-button :disabled="loading" @click="handleCancel">取 消</el-button> </div> </template> </el-dialog> @@ -118,6 +118,10 @@ onChange: { type: Function, default: null }, loading: { type: Boolean, default: false } }) src/views/productionManagement/productionProcess/Edit.vue
@@ -3,29 +3,64 @@ <el-dialog v-model="isShow" title="编辑部件" width="400" width="760" @close="closeModal" > <el-form label-width="140px" :model="formState" label-position="top" ref="formRef"> <el-form-item label="部件:" prop="name" <el-row :gutter="16"> <el-col :span="12"> <el-form-item label="产品名称:" prop="productId" :rules="[ { required: true, message: '请输入部件', message: '请选择产品名称', }, { max: 100, message: '最多100个字符', } ]"> <el-input v-model="formState.name" /> </el-form-item> <el-form-item label="部件编号" prop="no"> <el-input v-model="formState.no" /> </el-form-item> <el-form-item <el-tree-select v-model="formState.productId" placeholder="请选择产品名称" clearable filterable check-strictly :data="productCategoryOptions" :render-after-expand="false" style="width: 100%" @change="handleProductChange" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="产品规格:" prop="productModelId" :rules="[ { required: true, message: '请选择产品规格', }, ]"> <el-select v-model="formState.productModelId" placeholder="请选择产品规格" clearable filterable :disabled="!formState.productId" style="width: 100%"> <el-option v-for="item in modelOptions" :key="item.id" :label="item.model" :value="item.id" /> </el-select> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="部件编号" prop="no"> <el-input v-model="formState.no" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="部件类型" prop="type" :rules="[ @@ -34,25 +69,58 @@ message: '请选择部件类型', } ]" > <el-select v-model="formState.type" placeholder="请选择部件类型"> <el-option label="加工" :value="1" /> <el-option label="刮板冷芯制作" :value="2" /> <el-option label="管路组对" :value="3" /> <el-option label="罐体连接及调试" :value="4" /> <el-option label="测试打压" :value="5" /> <el-option label="其他" :value="6" /> </el-select> </el-form-item> <el-form-item label="工资定额" prop="salaryQuota"> <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-form-item> <el-form-item label="备注" prop="remark"> <el-input v-model="formState.remark" type="textarea" /> </el-form-item> > <el-select v-model="formState.type" placeholder="请选择部件类型"> <el-option label="加工" :value="1" /> <el-option label="刮板冷芯制作" :value="2" /> <el-option label="管路组对" :value="3" /> <el-option label="罐体连接及调试" :value="4" /> <el-option label="测试打压" :value="5" /> <el-option label="其他" :value="6" /> </el-select> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="计划工时(小时)" prop="salaryQuota" :rules="[ { validator: validateNonNegativeSalaryQuota, trigger: ['blur', 'change'] } ]" > <el-input v-model="formState.salaryQuota" type="number" :step="0.001" :min="0" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="计划人员" prop="plannerId"> <el-select v-model="formState.plannerId" placeholder="请选择计划人员" clearable filterable style="width: 100%" @change="handlePlannerChange" > <el-option v-for="item in plannerOptions" :key="item.userId" :label="item.nickName" :value="item.userId" /> </el-select> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="是否质检" prop="isQuality"> <el-switch v-model="formState.isQuality" :active-value="true" inactive-value="false"/> </el-form-item> </el-col> <el-col :span="24"> <el-form-item label="备注" prop="remark"> <el-input v-model="formState.remark" type="textarea" /> </el-form-item> </el-col> </el-row> </el-form> <template #footer> <div class="dialog-footer"> @@ -65,8 +133,10 @@ </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 { modelListPage, productTreeList } from "@/api/basicData/product"; import { userListNoPageByTenantId } from "@/api/system/user.js"; const props = defineProps({ visible: { @@ -86,12 +156,19 @@ const formState = ref({ id: props.record.id, name: props.record.name, productId: props.record.productId, productModelId: props.record.productModelId, type: props.record.type, no: props.record.no, remark: props.record.remark, salaryQuota: props.record.salaryQuota, plannerId: props.record.plannerId, plannerName: props.record.plannerName, isQuality: props.record.isQuality, }); const productCategoryOptions = ref([]); const modelOptions = ref([]); const plannerOptions = ref([]); const isShow = computed({ get() { @@ -108,10 +185,14 @@ formState.value = { id: newRecord.id, name: newRecord.name || '', productId: newRecord.productId, productModelId: newRecord.productModelId, no: newRecord.no || '', type: newRecord.type, remark: newRecord.remark || '', salaryQuota: newRecord.salaryQuota || '', plannerId: newRecord.plannerId, plannerName: newRecord.plannerName || '', isQuality: props.record.isQuality, }; } @@ -123,16 +204,129 @@ formState.value = { id: props.record.id, name: props.record.name || '', productId: props.record.productId, productModelId: props.record.productModelId, no: props.record.no || '', type: props.record.type, remark: props.record.remark || '', salaryQuota: props.record.salaryQuota || '', plannerId: props.record.plannerId, plannerName: props.record.plannerName || '', isQuality: props.record.isQuality, }; } }); let { proxy } = getCurrentInstance() const validateNonNegativeSalaryQuota = (rule, value, callback) => { if (value === '' || value === null || value === undefined) { callback(new Error('请输入计划工时')); return; } const num = Number(value); if (Number.isNaN(num) || num < 0) { callback(new Error('计划工时不能小于0')); return; } callback(); }; const convertProductTree = list => { return (list || []).map(item => { const children = convertProductTree(item.children || item.childList || []); return { ...item, value: item.id, label: item.name || item.label, children, disabled: children.length > 0, }; }); }; const findNodeById = (nodes, targetId) => { for (const node of nodes || []) { if (String(node.value) === String(targetId)) { return node; } if (node.children && node.children.length > 0) { const found = findNodeById(node.children, targetId); if (found) return found; } } return null; }; const findNodeIdByLabel = (nodes, targetLabel) => { for (const node of nodes || []) { if (node.label === targetLabel) { return node.value; } if (node.children && node.children.length > 0) { const found = findNodeIdByLabel(node.children, targetLabel); if (found !== null && found !== undefined) return found; } } return undefined; }; const getProductCategoryOptions = async () => { try { const res = await productTreeList(); const list = Array.isArray(res) ? res : res?.data || []; productCategoryOptions.value = convertProductTree(list); if (!formState.value.productId && formState.value.name) { formState.value.productId = findNodeIdByLabel(productCategoryOptions.value, formState.value.name); } await getModelOptions(formState.value.productId); } catch (e) { productCategoryOptions.value = []; } }; const getModelOptions = async productId => { if (!productId) { modelOptions.value = []; return; } try { const res = await modelListPage({ id: productId, current: 1, size: 999, }); const records = res?.records || res?.data?.records || []; modelOptions.value = records; } catch (e) { modelOptions.value = []; } }; const getPlannerOptions = async () => { try { const res = await userListNoPageByTenantId(); plannerOptions.value = res?.data || []; if (!formState.value.plannerId && formState.value.plannerName) { const selectedUser = plannerOptions.value.find(item => item.nickName === formState.value.plannerName); formState.value.plannerId = selectedUser?.userId; } } catch (e) { plannerOptions.value = []; } }; const handlePlannerChange = value => { const selectedUser = plannerOptions.value.find(item => String(item.userId) === String(value)); formState.value.plannerName = selectedUser?.nickName || ''; }; const handleProductChange = async value => { const selectedNode = findNodeById(productCategoryOptions.value, value); formState.value.name = selectedNode?.label || ''; formState.value.productModelId = undefined; await getModelOptions(value); }; const closeModal = () => { isShow.value = false; @@ -152,6 +346,11 @@ }) }; onMounted(() => { getProductCategoryOptions(); getPlannerOptions(); }); defineExpose({ closeModal, handleSubmit, src/views/productionManagement/productionProcess/New.vue
@@ -3,29 +3,64 @@ <el-dialog v-model="isShow" title="新增部件" width="400" width="760" @close="closeModal" > <el-form label-width="140px" :model="formState" label-position="top" ref="formRef"> <el-form-item label="部件:" prop="name" <el-row :gutter="16"> <el-col :span="12"> <el-form-item label="产品名称:" prop="productId" :rules="[ { required: true, message: '请输入部件', message: '请选择产品名称', }, { max: 100, message: '最多100个字符', } ]"> <el-input v-model="formState.name" /> </el-form-item> <el-form-item label="部件编号" prop="no"> <el-input v-model="formState.no" /> </el-form-item> <el-form-item <el-tree-select v-model="formState.productId" placeholder="请选择产品名称" clearable filterable check-strictly :data="productCategoryOptions" :render-after-expand="false" style="width: 100%" @change="handleProductChange" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="产品规格:" prop="productModelId" :rules="[ { required: true, message: '请选择产品规格', }, ]"> <el-select v-model="formState.productModelId" placeholder="请选择产品规格" clearable filterable :disabled="!formState.productId" style="width: 100%"> <el-option v-for="item in modelOptions" :key="item.id" :label="item.model" :value="item.id" /> </el-select> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="部件编号" prop="no"> <el-input v-model="formState.no" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="部件类型" prop="type" :rules="[ @@ -34,27 +69,60 @@ message: '请选择部件类型', } ]" > <el-select v-model="formState.type" placeholder="请选择部件类型"> <el-option label="加工" :value="1" /> <el-option label="刮板冷芯制作" :value="2" /> <el-option label="管路组对" :value="3" /> <el-option label="罐体连接及调试" :value="4" /> <el-option label="测试打压" :value="5" /> <el-option label="其他" :value="6" /> </el-select> </el-form-item> <el-form-item label="工资定额" prop="salaryQuota"> <el-input v-model="formState.salaryQuota" type="number" :step="0.001"> <template #append>元</template> </el-input> </el-form-item> <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="remark"> <el-input v-model="formState.remark" type="textarea" /> </el-form-item> > <el-select v-model="formState.type" placeholder="请选择部件类型"> <el-option label="加工" :value="1" /> <el-option label="刮板冷芯制作" :value="2" /> <el-option label="管路组对" :value="3" /> <el-option label="罐体连接及调试" :value="4" /> <el-option label="测试打压" :value="5" /> <el-option label="其他" :value="6" /> </el-select> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="计划工时(小时)" prop="salaryQuota" :rules="[ { validator: validateNonNegativeSalaryQuota, trigger: ['blur', 'change'] } ]" > <el-input v-model="formState.salaryQuota" type="number" :step="0.001" :min="0"> <template #append>小时</template> </el-input> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="计划人员" prop="plannerId"> <el-select v-model="formState.plannerId" placeholder="请选择计划人员" clearable filterable style="width: 100%" @change="handlePlannerChange" > <el-option v-for="item in plannerOptions" :key="item.userId" :label="item.nickName" :value="item.userId" /> </el-select> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="是否质检" prop="isQuality"> <el-switch v-model="formState.isQuality" :active-value="true" inactive-value="false"/> </el-form-item> </el-col> <el-col :span="24"> <el-form-item label="备注" prop="remark"> <el-input v-model="formState.remark" type="textarea" /> </el-form-item> </el-col> </el-row> </el-form> <template #footer> <div class="dialog-footer"> @@ -67,8 +135,10 @@ </template> <script setup> import { ref, computed, getCurrentInstance } from "vue"; import { ref, computed, getCurrentInstance, onMounted } from "vue"; import {add} from "@/api/productionManagement/productionProcess.js"; import { modelListPage, productTreeList } from "@/api/basicData/product"; import { userListNoPageByTenantId } from "@/api/system/user.js"; const props = defineProps({ visible: { @@ -82,11 +152,18 @@ // 响应式数据(替代选项式的 data) const formState = ref({ name: '', productId: undefined, productModelId: undefined, type: undefined, remark: '', salaryQuota: '', plannerId: undefined, plannerName: '', isQuality: false, }); const productCategoryOptions = ref([]); const modelOptions = ref([]); const plannerOptions = ref([]); const isShow = computed({ get() { @@ -98,6 +175,94 @@ }); let { proxy } = getCurrentInstance() const validateNonNegativeSalaryQuota = (rule, value, callback) => { if (value === '' || value === null || value === undefined) { callback(new Error('请输入计划工时')); return; } const num = Number(value); if (Number.isNaN(num) || num < 0) { callback(new Error('计划工时不能小于0')); return; } callback(); }; const convertProductTree = list => { return (list || []).map(item => { const children = convertProductTree(item.children || item.childList || []); return { ...item, value: item.id, label: item.name || item.label, children, disabled: children.length > 0, }; }); }; const findNodeById = (nodes, targetId) => { for (const node of nodes || []) { if (String(node.value) === String(targetId)) { return node; } if (node.children && node.children.length > 0) { const found = findNodeById(node.children, targetId); if (found) return found; } } return null; }; const getProductCategoryOptions = async () => { try { const res = await productTreeList(); const list = Array.isArray(res) ? res : res?.data || []; productCategoryOptions.value = convertProductTree(list); } catch (e) { productCategoryOptions.value = []; } }; const getModelOptions = async productId => { if (!productId) { modelOptions.value = []; return; } try { const res = await modelListPage({ id: productId, current: 1, size: 999, }); const records = res?.records || res?.data?.records || []; modelOptions.value = records; } catch (e) { modelOptions.value = []; } }; const getPlannerOptions = async () => { try { const res = await userListNoPageByTenantId(); plannerOptions.value = res?.data || []; } catch (e) { plannerOptions.value = []; } }; const handlePlannerChange = value => { const selectedUser = plannerOptions.value.find(item => String(item.userId) === String(value)); formState.value.plannerName = selectedUser?.nickName || ''; }; const handleProductChange = async value => { const selectedNode = findNodeById(productCategoryOptions.value, value); formState.value.name = selectedNode?.label || ''; formState.value.productModelId = undefined; await getModelOptions(value); }; const closeModal = () => { isShow.value = false; @@ -117,6 +282,11 @@ }) }; onMounted(() => { getProductCategoryOptions(); getPlannerOptions(); }); defineExpose({ closeModal, handleSubmit, src/views/productionManagement/productionProcess/index.vue
@@ -3,13 +3,33 @@ <div class="search_form"> <el-form :model="searchForm" :inline="true"> <el-form-item label="部件:"> <el-form-item label="产品名称:"> <el-input v-model="searchForm.name" placeholder="请输入" placeholder="请输入产品名称" clearable prefix-icon="Search" style="width: 200px;" @change="handleQuery" /> </el-form-item> <el-form-item label="部件类型:"> <el-select v-model="searchForm.type" placeholder="请选择" clearable style="width: 200px;" @change="handleQuery"> <el-option label="加工" :value="1" /> <el-option label="刮板冷芯制作" :value="2" /> <el-option label="管路组对" :value="3" /> <el-option label="罐体连接及调试" :value="4" /> <el-option label="测试打压" :value="5" /> <el-option label="其他" :value="6" /> </el-select> </el-form-item> <el-form-item label="部件编号:"> <el-input v-model="searchForm.no" @@ -60,7 +80,10 @@ title="导入部件" :action="importAction" :headers="importHeaders" :loading="importLoading" :disabled="importLoading" :auto-upload="false" :on-progress="handleImportProgress" :on-success="handleImportSuccess" :on-error="handleImportError" @confirm="handleImportConfirm" @@ -85,26 +108,36 @@ const data = reactive({ searchForm: { name: "", type: undefined, no: "", }, }); const { searchForm } = toRefs(data); const tableColumn = ref([ { label: "产品名称", prop: "name", }, { label: "产品规格", prop: "productModel", }, { label: "部件编号", prop: "no", }, { label: "部件", prop: "name", }, { label: "部件类型", prop: "typeText", }, { label: "工资定额", label: "计划工时(小时)", prop: "salaryQuota", }, { label: "计划人员", prop: "plannerName", }, { label: "是否质检", @@ -145,6 +178,7 @@ const isShowEditModal = ref(false); const record = ref({}); const importDialogVisible = ref(false); const importLoading = ref(false); const importDialogRef = ref(null); const page = reactive({ current: 1, @@ -152,6 +186,7 @@ total: 0, }); const { proxy } = getCurrentInstance(); // 导入相关配置 const importAction = @@ -251,8 +286,14 @@ } }; // 导入中 const handleImportProgress = () => { importLoading.value = true; }; // 导入成功 const handleImportSuccess = response => { importLoading.value = false; if (response.code === 200) { proxy.$modal.msgSuccess("导入成功"); importDialogVisible.value = false; @@ -267,11 +308,13 @@ // 导入失败 const handleImportError = error => { importLoading.value = false; proxy.$modal.msgError("导入失败:" + (error.message || "未知错误")); }; // 关闭导入弹窗 const handleImportClose = () => { importLoading.value = false; if (importDialogRef.value) { importDialogRef.value.clearFiles(); } src/views/productionManagement/workOrderEdit/index.vue
@@ -40,17 +40,33 @@ label-width="120px"> <el-form-item label="计划开始时间"> <el-date-picker v-model="editrow.planStartTime" type="date" type="datetime" placeholder="请选择" value-format="YYYY-MM-DD" format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss" style="width: 300px" /> </el-form-item> <el-form-item label="计划结束时间"> <el-date-picker v-model="editrow.planEndTime" type="date" type="datetime" placeholder="请选择" value-format="YYYY-MM-DD" format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss" style="width: 300px" /> </el-form-item> <el-form-item label="报工人"> <el-select v-model="editrow.reportWorkUserIds" multiple filterable collapse-tags collapse-tags-tooltip placeholder="请选择报工人" style="width: 300px"> <el-option v-for="user in userOptions" :key="user.userId" :label="user.nickName" :value="user.userId" /> </el-select> </el-form-item> </el-form> <template #footer> @@ -72,6 +88,7 @@ productWorkOrderPage, updateProductWorkOrder, } from "@/api/productionManagement/workOrder.js"; import { userListNoPageByTenantId } from "@/api/system/user.js"; import { getCurrentInstance, reactive, toRefs } from "vue"; const { proxy } = getCurrentInstance(); @@ -163,6 +180,7 @@ ]); const tableData = ref([]); const userOptions = ref([]); const tableLoading = ref(false); const editDialogVisible = ref(false); let editrow = ref(null); @@ -210,7 +228,11 @@ productWorkOrderPage(params) .then(res => { tableLoading.value = false; tableData.value = res.data.records; tableData.value = (res.data.records || []).map(item => ({ ...item, planStartTime: formatDateTime(item.planStartTime), planEndTime: formatDateTime(item.planEndTime), })); page.total = res.data.total; }) .catch(() => { @@ -220,11 +242,45 @@ const handleEdit = row => { editrow.value = JSON.parse(JSON.stringify(row)); if (typeof editrow.value.reportWorkUserIds === "string") { editrow.value.reportWorkUserIds = editrow.value.reportWorkUserIds .split(",") .map(v => Number(v)) .filter(v => Number.isFinite(v)); } else if (!Array.isArray(editrow.value.reportWorkUserIds)) { editrow.value.reportWorkUserIds = []; } editDialogVisible.value = true; }; const formatDateTime = value => { if (!value) return ""; const date = dayjs(value); return date.isValid() ? date.format("YYYY-MM-DD HH:mm:ss") : value; }; const getUserList = () => { userListNoPageByTenantId() .then(res => { if (res.code === 200) { userOptions.value = res.data || []; } }) .catch(() => { userOptions.value = []; }); }; const handleUpdate = () => { updateProductWorkOrder(editrow.value) const selectedUsers = userOptions.value.filter(user => (editrow.value.reportWorkUserIds || []).includes(user.userId) ); const submitData = { ...editrow.value, reportWorkUserIds: editrow.value.reportWorkUserIds || [], reportWork: selectedUsers.map(user => user.nickName).join(","), }; updateProductWorkOrder(submitData) .then(res => { proxy.$modal.msgSuccess("提交成功"); editDialogVisible.value = false; @@ -239,6 +295,7 @@ onMounted(() => { getList(); getUserList(); }); </script>