From 71a260afa397035d3844ab13f2fea5669f7b46ab Mon Sep 17 00:00:00 2001
From: 张诺 <zhang_12370@163.com>
Date: 星期二, 28 四月 2026 14:35:51 +0800
Subject: [PATCH] feat(productionManagement): 新增工单管理功能模块
---
src/views/productionManagement/productionProcess/Edit.vue | 321 +++++++++++++++++++++++++++++++++++++++++++++++++----
1 files changed, 297 insertions(+), 24 deletions(-)
diff --git a/src/views/productionManagement/productionProcess/Edit.vue b/src/views/productionManagement/productionProcess/Edit.vue
index 8e92403..35b05a3 100644
--- a/src/views/productionManagement/productionProcess/Edit.vue
+++ b/src/views/productionManagement/productionProcess/Edit.vue
@@ -8,6 +8,21 @@
>
<el-form label-width="140px" :model="formState" label-position="top" ref="formRef">
<el-form-item
+ label="宸ュ簭缂栧彿锛�"
+ prop="no"
+ :rules="[
+ {
+ required: true,
+ message: '璇疯緭鍏ュ伐搴忕紪鍙�',
+ },
+ {
+ max: 100,
+ message: '鏈�澶�100涓瓧绗�',
+ }
+ ]">
+ <el-input v-model="formState.no" placeholder="璇疯緭鍏ュ伐搴忓悕绉�" />
+ </el-form-item>
+ <el-form-item
label="宸ュ簭鍚嶇О锛�"
prop="name"
:rules="[
@@ -22,12 +37,90 @@
]">
<el-input v-model="formState.name" />
</el-form-item>
- <el-form-item label="宸ュ簭缂栧彿" prop="no">
+
+ <!-- <el-form-item label="宸ュ簭缂栧彿" prop="no">
<el-input v-model="formState.no" />
+ </el-form-item> -->
+ <el-form-item
+ label="宸ュ簭鏈哄彴"
+ prop="deviceId"
+ :rules="[
+ {
+ required: true,
+ message: '璇烽�夋嫨宸ュ簭绫诲瀷',
+ }
+ ]"
+ >
+ <el-select
+ v-model="formState.deviceId"
+ placeholder="璇烽�夋嫨宸ュ簭鏈哄彴"
+ filterable
+ remote
+ clearable
+ reserve-keyword
+ :remote-method="handleDeviceRemoteSearch"
+ :loading="deviceLoading"
+ @clear="handleDeviceClear"
+ @change="handleDeviceChange"
+ @visible-change="handleDeviceDropdownVisible"
+ popper-class="device-select-popper"
+ >
+ <el-option v-for="item in equipmentList" :key="item.id" :label="item.deviceName" :value="item.id" />
+ <el-option
+ v-if="equipmentList.length > 0 && deviceHasMore"
+ :value="__deviceLoadMoreSentinel"
+ label="鍔犺浇鏇村鈥�"
+ disabled
+ />
+ </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
+ label="鎶ュ伐浜�"
+ prop="ids"
+ :rules="[
+ {
+ required: true,
+ message: '璇烽�夋嫨鎶ュ伐浜�',
+ trigger: 'change',
+ }
+ ]"
+ >
+ <el-select
+ v-model="formState.ids"
+ multiple
+ filterable
+ clearable
+ collapse-tags
+ collapse-tags-tooltip
+ placeholder="璇烽�夋嫨鎶ュ伐浜�"
+ @change="handleReportUsersChange"
+ >
+ <el-option
+ v-for="item in userOptions"
+ :key="item.userId"
+ :label="item.nickName"
+ :value="item.userId"
+ />
+ </el-select>
</el-form-item>
+<!-- <el-form-item-->
+<!-- label="宸ュ簭绫诲瀷"-->
+<!-- prop="type"-->
+<!-- :rules="[-->
+<!-- {-->
+<!-- required: true,-->
+<!-- message: '璇烽�夋嫨宸ュ簭绫诲瀷',-->
+<!-- }-->
+<!-- ]"-->
+<!-- >-->
+<!-- <el-select v-model="formState.type" placeholder="璇烽�夋嫨宸ュ簭绫诲瀷">-->
+<!-- <el-option label="璁℃椂" :value="0" />-->
+<!-- <el-option label="璁′欢" :value="1" />-->
+<!-- </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>
@@ -46,8 +139,10 @@
</template>
<script setup>
-import { ref, computed, getCurrentInstance, watch } from "vue";
-import {update} from "@/api/productionManagement/productionProcess.js";
+import { ref, computed, getCurrentInstance, watch, reactive, nextTick, onBeforeUnmount, onMounted } from "vue";
+import { update } from "@/api/productionManagement/productionProcess.js";
+import { getLedgerPage } from "@/api/equipmentManagement/ledger.js";
+import { userListNoPageByTenantId } from "@/api/system/user.js";
const props = defineProps({
visible: {
@@ -67,11 +162,71 @@
const formState = ref({
id: props.record.id,
name: props.record.name,
+ type: props.record.type,
no: props.record.no,
+ deviceId: props.record.deviceId,
+ deviceName: props.record.deviceName,
+ ids: [],
+ reportWorkerList: [],
remark: props.record.remark,
salaryQuota: props.record.salaryQuota,
isQuality: props.record.isQuality,
});
+
+const page = reactive({
+ current: 1,
+ size: 10,
+ total: 0,
+});
+
+const equipmentList = ref([]);
+const deviceLoading = ref(false);
+const deviceQuery = ref("");
+const deviceScrollWrap = ref(null);
+const __deviceLoadMoreSentinel = "__deviceLoadMoreSentinel";
+const userOptions = ref([]);
+
+const deviceHasMore = computed(() => {
+ const total = Number(page.total ?? 0);
+ if (!total) {
+ return false;
+ }
+ return equipmentList.value.length < total;
+});
+
+const handleDeviceChange = (val) => {
+ formState.value.deviceName = equipmentList.value.find(item => item.id === val)?.deviceName || "";
+};
+
+const normalizeReportUserIds = (record) => {
+ const raw = record?.ids;
+ if (Array.isArray(raw)) {
+ return raw.map(item => item?.userId ?? item).filter(Boolean);
+ }
+ if (typeof raw === "string") {
+ return raw.split(/[,锛�;锛沑s]+/g).map(item => item.trim()).filter(Boolean);
+ }
+ if (Array.isArray(record?.reportWorkerList)) {
+ return record.reportWorkerList.map(item => item?.userId).filter(Boolean);
+ }
+ return [];
+};
+
+const handleReportUsersChange = (val) => {
+ const userMap = new Map(userOptions.value.map(item => [item.userId, item.nickName]));
+ formState.value.reportWorkerList = (val || []).map(userId => ({
+ userId,
+ userName: userMap.get(userId) || "",
+ }));
+};
+
+const getUserOptions = async () => {
+ const res = await userListNoPageByTenantId();
+ userOptions.value = Array.isArray(res?.data) ? res.data : [];
+ if (formState.value.ids?.length) {
+ handleReportUsersChange(formState.value.ids);
+ }
+};
const isShow = computed({
get() {
@@ -82,35 +237,147 @@
},
});
-// 鐩戝惉 record 鍙樺寲锛屾洿鏂拌〃鍗曟暟鎹�
+const ensureSelectedDeviceOption = () => {
+ const currentDeviceId = formState.value.deviceId;
+ if (!currentDeviceId || !props.record?.deviceName) {
+ return;
+ }
+ const exists = equipmentList.value.some(item => item.id === currentDeviceId);
+ if (!exists) {
+ equipmentList.value.unshift({
+ id: currentDeviceId,
+ deviceName: props.record.deviceName,
+ });
+ }
+};
+
+const getLedgerPageS = async ({ reset = false } = {}) => {
+ if (deviceLoading.value) return;
+ deviceLoading.value = true;
+ try {
+ const res = await getLedgerPage({
+ current: page.current,
+ size: page.size,
+ deviceName: deviceQuery.value ? deviceQuery.value : undefined,
+ });
+ const data = res?.data || {};
+ const records = Array.isArray(data.records) ? data.records : [];
+ page.total = Number(data.total ?? page.total ?? 0);
+ page.current = Number(data.current ?? page.current);
+ page.size = Number(data.size ?? page.size);
+ equipmentList.value = reset ? records : [...equipmentList.value, ...records];
+ ensureSelectedDeviceOption();
+ } finally {
+ deviceLoading.value = false;
+ }
+};
+
+const resetDeviceOptions = async () => {
+ page.current = 1;
+ page.size = 10;
+ page.total = 0;
+ equipmentList.value = [];
+ await getLedgerPageS({ reset: true });
+};
+
+const loadMoreDevices = async () => {
+ if (deviceLoading.value) return;
+ if (!deviceHasMore.value) return;
+ page.current += 1;
+ await getLedgerPageS();
+};
+
+let remoteTimer = null;
+const handleDeviceRemoteSearch = (query) => {
+ const nextQuery = (query ?? "").trim();
+ deviceQuery.value = nextQuery;
+ if (remoteTimer) clearTimeout(remoteTimer);
+ remoteTimer = setTimeout(() => {
+ resetDeviceOptions();
+ }, 300);
+};
+
+const handleDeviceClear = () => {
+ deviceQuery.value = "";
+ resetDeviceOptions();
+};
+
+const onDeviceDropdownScroll = (e) => {
+ const el = e.target;
+ if (!el) return;
+ if (el.scrollHeight - el.scrollTop - el.clientHeight <= 20) {
+ loadMoreDevices();
+ }
+};
+
+const unbindDeviceDropdownScroll = () => {
+ if (deviceScrollWrap.value) {
+ deviceScrollWrap.value.removeEventListener("scroll", onDeviceDropdownScroll);
+ deviceScrollWrap.value = null;
+ }
+};
+
+const bindDeviceDropdownScroll = () => {
+ unbindDeviceDropdownScroll();
+ const wrap =
+ document.querySelector(".device-select-popper .el-scrollbar__wrap") ||
+ document.querySelector(".device-select-popper .el-select-dropdown__wrap");
+ if (wrap) {
+ deviceScrollWrap.value = wrap;
+ wrap.addEventListener("scroll", onDeviceDropdownScroll);
+ }
+};
+
+const handleDeviceDropdownVisible = async (visible) => {
+ if (!visible) {
+ unbindDeviceDropdownScroll();
+ return;
+ }
+ if (equipmentList.value.length === 0) {
+ await resetDeviceOptions();
+ }
+ await nextTick();
+ bindDeviceDropdownScroll();
+};
+
+const applyRecordToForm = (record) => {
+ const ids = normalizeReportUserIds(record);
+ formState.value = {
+ id: record.id,
+ name: record.name || "",
+ no: record.no || "",
+ type: record.type,
+ deviceId: record.deviceId,
+ deviceName: record.deviceName || "",
+ ids,
+ reportWorkerList: Array.isArray(record.reportWorkerList) ? record.reportWorkerList : [],
+ remark: record.remark || "",
+ salaryQuota: record.salaryQuota || "",
+ isQuality: record.isQuality,
+ };
+ if (!formState.value.reportWorkerList.length && ids.length) {
+ handleReportUsersChange(ids);
+ }
+ ensureSelectedDeviceOption();
+};
+
watch(() => props.record, (newRecord) => {
if (newRecord && isShow.value) {
- formState.value = {
- id: newRecord.id,
- name: newRecord.name || '',
- no: newRecord.no || '',
- remark: newRecord.remark || '',
- salaryQuota: newRecord.salaryQuota || '',
- isQuality: props.record.isQuality,
- };
+ applyRecordToForm(newRecord);
}
}, { 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,
- };
+ applyRecordToForm(props.record);
}
});
let { proxy } = getCurrentInstance()
+
+onMounted(() => {
+ getUserOptions();
+});
const closeModal = () => {
isShow.value = false;
@@ -119,7 +386,8 @@
const handleSubmit = () => {
proxy.$refs["formRef"].validate(valid => {
if (valid) {
- update(formState.value).then(res => {
+ handleReportUsersChange(formState.value.ids);
+ update(formState.value).then(() => {
// 鍏抽棴妯℃�佹
isShow.value = false;
// 鍛婄煡鐖剁粍浠跺凡瀹屾垚
@@ -135,4 +403,9 @@
handleSubmit,
isShow,
});
+
+onBeforeUnmount(() => {
+ unbindDeviceDropdownScroll();
+ if (remoteTimer) clearTimeout(remoteTimer);
+});
</script>
--
Gitblit v1.9.3