| src/api/productionManagement/productionPlan.js | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages.json | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/inventoryManagement/stockManagement/selectProductModel.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/productionManagement/mainProductionPlan/edit.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/productionManagement/mainProductionPlan/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/productionManagement/mainProductionPlan/issue.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
src/api/productionManagement/productionPlan.js
@@ -27,3 +27,48 @@ params: query, }); } // ç产计å-æ°å¢ export function productionPlanAdd(data) { return request({ url: "/productionPlan/addProductionPlan", method: "post", data, }); } // ç产计å-ä¿®æ¹ export function productionPlanUpdate(data) { return request({ url: "/productionPlan/updateProductionPlan", method: "put", data, }); } // ç产计å-å é¤ export function productionPlanDelete(data) { return request({ url: "/productionPlan/deleteProductionPlan", method: "delete", data, }); } // åå¹¶ä¸å export function productionPlanCombine(data) { return request({ url: "/productionPlan/combine", method: "post", data, }); } // 追踪è¿åº¦ export function trackProgressByNo(query) { return request({ url: "/track/trackProgressByNo", method: "get", params: query, }); } src/pages.json
@@ -880,6 +880,20 @@ } }, { "path": "pages/productionManagement/mainProductionPlan/edit", "style": { "navigationBarTitleText": "ç产计å", "navigationStyle": "custom" } }, { "path": "pages/productionManagement/mainProductionPlan/issue", "style": { "navigationBarTitleText": "ä¸å", "navigationStyle": "custom" } }, { "path": "pages/productionManagement/productionScheduling/index", "style": { "navigationBarTitleText": "ç产æäº§", src/pages/inventoryManagement/stockManagement/selectProductModel.vue
@@ -93,7 +93,11 @@ .then(res => { const records = res?.records || res?.data?.records || res?.data || []; const rows = Array.isArray(records) ? records : []; list.value = page.current === 1 ? rows : [...list.value, ...rows]; const normalized = rows.map(r => { const modelId = r?.id ?? r?.productModelId ?? r?.materialModelId; return modelId == null ? r : { ...r, id: modelId }; }); list.value = page.current === 1 ? normalized : [...list.value, ...normalized]; total.value = Number(res?.total ?? res?.data?.total ?? list.value.length); loadStatus.value = list.value.length >= total.value ? "nomore" : "loadmore"; }) src/pages/productionManagement/mainProductionPlan/edit.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,275 @@ <template> <view class="account-detail"> <PageHeader :title="pageTitle" @back="goBack" /> <up-form ref="formRef" :model="form" :rules="rules" label-width="110"> <up-form-item label="主ç产计åå·" prop="mpsNo"> <up-input v-model="form.mpsNo" placeholder="æ°å¢åèªå¨çæ" disabled /> </up-form-item> <up-form-item label="产å" prop="productModelId" required> <up-input v-model="productText" placeholder="è¯·éæ©" readonly @click="goSelectProductModel" /> <template #right> <up-icon name="arrow-right" @click="goSelectProductModel"></up-icon> </template> </up-form-item> <up-form-item label="æéæ°é" prop="qtyRequired" required> <up-input v-model="form.qtyRequired" type="number" placeholder="请è¾å ¥" clearable /> </up-form-item> <up-form-item label="åä½" prop="unit"> <up-input v-model="form.unit" placeholder="èªå¨å¡«å " disabled /> </up-form-item> <up-form-item label="éæ±æ¥æ" prop="requiredDate" required> <up-input v-model="form.requiredDate" placeholder="è¯·éæ©" readonly @click="showRequiredDatePicker = true" /> <template #right> <up-icon name="arrow-right" @click="showRequiredDatePicker = true"></up-icon> </template> </up-form-item> <up-form-item label="æ¿è¯ºæ¥æ" prop="promisedDeliveryDate"> <up-input v-model="form.promisedDeliveryDate" placeholder="è¯·éæ©" readonly @click="showPromisedDatePicker = true" /> <template #right> <up-icon name="arrow-right" @click="showPromisedDatePicker = true"></up-icon> </template> </up-form-item> <up-form-item label="夿³¨" prop="remark"> <up-textarea v-model="form.remark" placeholder="请è¾å ¥" auto-height /> </up-form-item> </up-form> <FooterButtons :loading="loading" confirmText="ä¿å" @cancel="goBack" @confirm="handleSubmit" /> <up-datetime-picker :show="showRequiredDatePicker" v-model="requiredDateValue" mode="date" @confirm="onRequiredDateConfirm" @cancel="showRequiredDatePicker = false" /> <up-datetime-picker :show="showPromisedDatePicker" v-model="promisedDateValue" mode="date" @confirm="onPromisedDateConfirm" @cancel="showPromisedDatePicker = false" /> </view> </template> <script setup> import { computed, onMounted, ref } from "vue"; import { onLoad } from "@dcloudio/uni-app"; import FooterButtons from "@/components/FooterButtons.vue"; import PageHeader from "@/components/PageHeader.vue"; import { formatDateToYMD } from "@/utils/ruoyi"; import { productionPlanAdd, productionPlanUpdate, } from "@/api/productionManagement/productionPlan"; const formRef = ref(); const loading = ref(false); const editId = ref(undefined); const readonly = ref(false); const selectedProductRow = ref(null); const form = ref({ id: undefined, mpsNo: "", productId: undefined, productModelId: undefined, productName: "", model: "", qtyRequired: "", unit: "", requiredDate: "", promisedDeliveryDate: "", remark: "", }); const rules = { qtyRequired: [{ required: true, message: "请è¾å ¥æ°é", trigger: "blur" }], requiredDate: [ { required: true, message: "è¯·éæ©éæ±æ¥æ", trigger: "change" }, ], }; const pageTitle = computed(() => editId.value ? "ç¼è¾ç产计å" : "æ°å¢ç产计å" ); const productText = computed(() => { const name = form.value.productName || ""; const model = form.value.model || ""; if (!name && !model) return ""; return model ? `${name} / ${model}` : name; }); const showRequiredDatePicker = ref(false); const showPromisedDatePicker = ref(false); const requiredDateValue = ref(Date.now()); const promisedDateValue = ref(Date.now()); const goBack = () => { uni.navigateBack(); }; const goSelectProductModel = () => { if (readonly.value) return; const onSelected = row => { selectedProductRow.value = row || null; const productId = row?.productId ?? row?.productMainId ?? row?.productID; const productModelId = row?.id ?? row?.productModelId ?? row?.productModelID ?? row?.materialModelId ?? row?.materialModelID ?? row?.modelId ?? row?.modelID; form.value.productId = productId ?? undefined; form.value.productModelId = productModelId ?? undefined; form.value.productName = row.productName || ""; form.value.model = row.model || ""; form.value.unit = row.unit || "æ¹"; }; uni.$once("stockManagement:selectedProductModel", onSelected); uni.navigateTo({ url: "/pages/inventoryManagement/stockManagement/selectProductModel", events: { selected: onSelected, }, }); }; const onRequiredDateConfirm = e => { const value = e?.value ?? requiredDateValue.value; form.value.requiredDate = formatDateToYMD(value); showRequiredDatePicker.value = false; }; const onPromisedDateConfirm = e => { const value = e?.value ?? promisedDateValue.value; form.value.promisedDeliveryDate = formatDateToYMD(value); showPromisedDatePicker.value = false; }; const handleSubmit = async () => { if (readonly.value) return; if (!form.value.productModelId && selectedProductRow.value) { const row = selectedProductRow.value; const fallback = row?.id ?? row?.productModelId ?? row?.productModelID ?? row?.materialModelId ?? row?.materialModelID ?? row?.modelId ?? row?.modelID; if (fallback != null) form.value.productModelId = fallback; } if (!form.value.productModelId) { uni.showToast({ title: "è¯·éæ©äº§å", icon: "none" }); return; } const valid = await formRef.value.validate().catch(() => false); if (!valid) return; const qty = Number(form.value.qtyRequired); if (!qty || qty <= 0) { uni.showToast({ title: "æ°éå¿ é¡»å¤§äº0", icon: "none" }); return; } loading.value = true; const payload = { ...form.value, qtyRequired: qty }; const action = editId.value ? productionPlanUpdate : productionPlanAdd; if (!editId.value) payload.id = null; action(payload) .then(() => { uni.showToast({ title: "ä¿åæå", icon: "success" }); uni.$emit("mainProductionPlan:refresh"); goBack(); }) .catch(err => { const msg = err?.msg || err?.message || err?.data?.msg || err?.data?.message; uni.showToast({ title: msg ? String(msg) : "ä¿å失败", icon: "none" }); }) .finally(() => { loading.value = false; }); }; const initFromRow = row => { if (!row || typeof row !== "object") return; editId.value = row.id; readonly.value = Boolean(row.source === "éå®" || String(row.status) !== "0"); form.value = { ...form.value, id: row.id, mpsNo: row.mpsNo || "", productId: row.productId ?? undefined, productModelId: row.productModelId ?? undefined, productName: row.productName || "", model: row.model || "", qtyRequired: String(row.qtyRequired ?? ""), unit: row.unit || "æ¹", requiredDate: row.requiredDate ? formatDateToYMD(row.requiredDate) : "", promisedDeliveryDate: row.promisedDeliveryDate ? formatDateToYMD(row.promisedDeliveryDate) : "", remark: row.remark || "", }; }; onLoad(options => { if (options?.data) { try { const row = JSON.parse(decodeURIComponent(options.data)); initFromRow(row); } catch { uni.showToast({ title: "æ°æ®å 载失败", icon: "none" }); } } }); onMounted(() => { if (!editId.value) { form.value.requiredDate = formatDateToYMD(Date.now()); } }); </script> <style scoped lang="scss"> @import "@/static/scss/form-common.scss"; </style> src/pages/productionManagement/mainProductionPlan/index.vue
@@ -1,43 +1,102 @@ <template> <view class="main-production-plan"> <!-- 使ç¨éç¨é¡µé¢å¤´é¨ç»ä»¶ --> <PageHeader title="主ç产计å" @back="goBack" /> <PageHeader title="主ç产计å" @back="goBack"> <!-- <template #right> <view class="header-right"> <u-button v-if="!selectMode" size="mini" @click="enterSelectMode">åå¹¶ä¸å</u-button> <u-button v-else size="mini" @click="exitSelectMode">åæ¶</u-button> </view> </template> --> </PageHeader> <!-- æç´¢åºå --> <view class="search-section"> <view class="search-bar"> <view class="search-input"> <up-input class="search-text" <up-input class="search-text" placeholder="请è¾å ¥è®¡åå·æäº§ååç§°" v-model="searchForm.keyword" @change="handleQuery" clearable /> clearable /> </view> <view class="filter-button" @click="handleQuery"> <up-icon name="search" size="24" color="#999"></up-icon> <view class="filter-button" @click="handleQuery"> <up-icon name="search" size="24" color="#999"></up-icon> </view> </view> </view> <!-- å表åºå --> <scroll-view scroll-y class="list-container" v-if="tableData.length > 0" @scrolltolower="loadMore"> <view v-for="(item, index) in tableData" :key="item.id || index" @click="goDetail(item)"> <scroll-view scroll-y class="list-container" v-if="tableData.length > 0"> <up-checkbox-group v-if="selectMode" v-model="selectedIds" placement="column"> <view v-for="(item, index) in tableData" :key="item.id || index" @click="toggleSelect(item)"> <view class="ledger-item"> <view class="item-header"> <view class="item-left"> <view class="document-icon"> <up-icon name="file-text" size="16" color="#ffffff"></up-icon> <up-icon name="file-text" size="16" color="#ffffff"></up-icon> </view> <text class="item-id">{{ item.mpsNo }}</text> </view> <view class="item-right"> <up-tag :text="getStatusText(item.status)" :type="getStatusType(item.status)" size="mini" /> <up-checkbox :name="String(item.id)" :label="''" @click.stop></up-checkbox> </view> </view> <up-divider></up-divider> <view class="item-details"> <view class="detail-row"> <text class="detail-label">产ååç§°</text> <text class="detail-value">{{ item.productName || '-' }}</text> </view> <view class="detail-row"> <text class="detail-label">è§æ ¼åå·</text> <text class="detail-value">{{ item.model || '-' }}</text> </view> <view class="detail-row"> <text class="detail-label">å¯ä¸åæ°é</text> <text class="detail-value highlight">{{ getRemainingQty(item) }} {{ item.unit || 'æ¹' }}</text> </view> </view> </view> </view> </up-checkbox-group> <view v-else> <view v-for="(item, index) in tableData" :key="item.id || index" @click="goDetail(item)"> <view class="ledger-item"> <view class="item-header"> <view class="item-left"> <view class="document-icon"> <up-icon name="file-text" size="16" color="#ffffff"></up-icon> </view> <text class="item-id">{{ item.mpsNo }}</text> </view> <view class="item-right"> <up-tag :text="getStatusText(item.status)" :type="getStatusType(item.status)" size="mini" /> </view> </view> <up-divider></up-divider> <view class="item-details"> <view class="detail-row"> <text class="detail-label">产ååç§°</text> @@ -52,6 +111,10 @@ <text class="detail-value highlight">{{ item.qtyRequired || 0 }} {{ item.unit || 'æ¹' }}</text> </view> <view class="detail-row"> <text class="detail-label">å·²ä¸åæ°é</text> <text class="detail-value">{{ item.quantityIssued || 0 }} {{ item.unit || 'æ¹' }}</text> </view> <view class="detail-row"> <text class="detail-label">éæ±æ¥æ</text> <text class="detail-value">{{ formatDate(item.requiredDate) }}</text> </view> @@ -60,41 +123,65 @@ <text class="detail-value">{{ item.source === 'éå®' ? 'éå®' : 'å é¨' }}</text> </view> </view> <view class="item-footer"> <text class="more-detail">æ¥ç详æ </text> <up-icon name="arrow-right" size="14" color="#999"></up-icon> <view class="action-buttons"> <u-button v-if="canEdit(item)" size="small" class="action-btn" @click.stop="goEdit(item)">ç¼è¾</u-button> <u-button v-if="canIssue(item)" size="small" class="action-btn" type="primary" @click.stop="goIssue([item])">ä¸å</u-button> <u-button v-if="canDelete(item)" size="small" class="action-btn" type="error" @click.stop="handleDelete(item)">å é¤</u-button> </view> </view> </view> <up-loadmore :status="loadStatus" v-if="tableData.length >= page.size" /> </view> </scroll-view> <view v-else class="no-data"> <up-empty mode="data" text="ææ ä¸»çäº§è®¡åæ°æ®"></up-empty> <view v-else class="no-data"> <up-empty mode="data" text="ææ ä¸»çäº§è®¡åæ°æ®"></up-empty> </view> <FooterButtons v-if="selectMode" cancelText="åæ¶" confirmText="ä¸å" :confirmDisabled="selectedIds.length === 0" @cancel="exitSelectMode" @confirm="goIssueSelected" /> <view class="fab-button" @click="goAdd"> <up-icon name="plus" size="24" color="#ffffff"></up-icon> </view> </view> </template> <script setup> import { ref, reactive, toRefs, getCurrentInstance } from "vue"; import { onShow } from '@dcloudio/uni-app'; import { ref, reactive, toRefs } from "vue"; import { onShow } from "@dcloudio/uni-app"; import dayjs from "dayjs"; import { productionPlanListPage } from "@/api/productionManagement/productionPlan.js"; import { productionPlanDelete, productionPlanListPage, } from "@/api/productionManagement/productionPlan.js"; import PageHeader from "@/components/PageHeader.vue"; const { proxy } = getCurrentInstance(); import FooterButtons from "@/components/FooterButtons.vue"; // å è½½ç¶æ const loading = ref(false); const loadStatus = ref('loadmore'); // åè¡¨æ°æ® const tableData = ref([]); // å页é ç½® const page = reactive({ current: 1, size: 10, total: 0, current: -1, size: -1, }); // æç´¢è¡¨åæ°æ® @@ -102,10 +189,13 @@ searchForm: { keyword: "", mpsNo: "", productName: "" productName: "", }, }); const { searchForm } = toRefs(data); const selectMode = ref(false); const selectedIds = ref([]); // è¿åä¸ä¸é¡µ const goBack = () => { @@ -113,12 +203,12 @@ }; // æ ¼å¼åæ¥æ const formatDate = (date) => { return date ? dayjs(date).format('YYYY-MM-DD') : '-'; const formatDate = date => { return date ? dayjs(date).format("YYYY-MM-DD") : "-"; }; // è·åç¶æææ¬ const getStatusText = (status) => { const getStatusText = status => { const statusMap = { 0: "å¾ ä¸å", 1: "é¨åä¸å", @@ -128,7 +218,7 @@ }; // è·åç¶æç±»å (uView tag type) const getStatusType = (status) => { const getStatusType = status => { const typeMap = { 0: "warning", 1: "primary", @@ -137,63 +227,172 @@ return typeMap[status] || "info"; }; // æ¥è¯¢å表 const handleQuery = () => { page.current = 1; tableData.value = []; getList(); const getRemainingQty = row => { const required = Number(row?.qtyRequired || 0); const issued = Number(row?.quantityIssued || 0); const remaining = required - issued; return Number((remaining > 0 ? remaining : 0).toFixed(4)); }; // å è½½æ´å¤ const loadMore = () => { if (loadStatus.value === 'nomore' || loading.value) return; page.current++; const canEdit = row => { return String(row?.status) === "0" && row?.source !== "éå®"; }; const canDelete = row => { return String(row?.status) === "0"; }; const canIssue = row => { return String(row?.status) !== "2" && getRemainingQty(row) > 0; }; const enterSelectMode = () => { selectMode.value = true; selectedIds.value = []; }; const exitSelectMode = () => { selectMode.value = false; selectedIds.value = []; }; const toggleSelect = item => { if (!item?.id) return; const id = String(item.id); const idx = selectedIds.value.indexOf(id); if (idx >= 0) selectedIds.value.splice(idx, 1); else selectedIds.value.push(id); }; // æ¥è¯¢å表 const handleQuery = () => { getList(); }; // è·ååè¡¨æ°æ® const getList = () => { loading.value = true; loadStatus.value = 'loading'; exitSelectMode(); // æé 请æ±åæ° // PC端æ¥å£æ¯æ mpsNo, productName çï¼è¿éç®åå¤çï¼å¦æ keyword åå¨ï¼åå°è¯å¹é const params = { current: page.current, size: page.size, mpsNo: searchForm.value.keyword, // ç®åå¤çï¼æç´¢å· productName: searchForm.value.keyword // ç®åå¤çï¼æç´¢åç§° productName: searchForm.value.keyword, // ç®åå¤çï¼æç´¢åç§° }; productionPlanListPage(params).then((res) => { productionPlanListPage(params) .then(res => { loading.value = false; const records = res.data.records || []; if (page.current === 1) { tableData.value = records; const payload = res?.data; if (Array.isArray(payload)) { tableData.value = payload; } else if (payload && typeof payload === "object") { tableData.value = payload.records || payload.rows || payload.data || []; } else { tableData.value = [...tableData.value, ...records]; tableData.value = []; } if (records.length < page.size) { loadStatus.value = 'nomore'; } else { loadStatus.value = 'loadmore'; } page.total = res.data.total || 0; }).catch(() => { }) .catch(() => { loading.value = false; loadStatus.value = 'loadmore'; uni.showToast({ title: 'å 载失败', icon: 'error' title: "å 载失败", icon: "error", }); }); }; // 跳转详æ const goDetail = (item) => { const goDetail = item => { if (selectMode.value) return; uni.navigateTo({ url: `/pages/productionManagement/mainProductionPlan/detail?data=${encodeURIComponent(JSON.stringify(item))}` url: `/pages/productionManagement/mainProductionPlan/detail?data=${encodeURIComponent( JSON.stringify(item) )}`, }); }; const goAdd = () => { uni.navigateTo({ url: "/pages/productionManagement/mainProductionPlan/edit", }); }; const goEdit = item => { uni.navigateTo({ url: `/pages/productionManagement/mainProductionPlan/edit?data=${encodeURIComponent( JSON.stringify(item) )}`, }); }; const goIssue = rows => { const list = Array.isArray(rows) ? rows : []; if (list.length === 0) return; const first = list[0]; const sumRemaining = Number( list.reduce((sum, r) => sum + getRemainingQty(r), 0).toFixed(4) ); const payload = { productName: first.productName || "", model: first.model || "", productId: first.productId ?? undefined, ids: list.map(r => r.id), planCompleteTime: formatDate(first.requiredDate) === "-" ? "" : formatDate(first.requiredDate), totalAssignedQuantity: sumRemaining, maxQuantity: sumRemaining, }; uni.navigateTo({ url: `/pages/productionManagement/mainProductionPlan/issue?data=${encodeURIComponent( JSON.stringify(payload) )}`, }); }; const goIssueSelected = () => { const rows = tableData.value.filter(r => selectedIds.value.includes(String(r.id)) ); if (rows.length === 0) { uni.showToast({ title: "è¯·éæ©è¦ä¸åç计å", icon: "none" }); return; } const first = rows[0]; const modelId = first.productModelId; const invalid = rows.some(r => r.productModelId !== modelId || !canIssue(r)); if (invalid) { uni.showToast({ title: "åªè½åå¹¶ä¸åç¸åè§æ ¼ä¸å¯ä¸åç计å", icon: "none", }); return; } goIssue(rows); }; const handleDelete = item => { if (!item?.id) return; uni.showModal({ title: "å é¤æç¤º", content: "确认å é¤è¯¥ç产计åï¼å é¤åæ æ³æ¢å¤", success: res => { if (!res.confirm) return; uni.showLoading({ title: "å é¤ä¸...", mask: true }); productionPlanDelete([item.id]) .then(() => { uni.showToast({ title: "å 餿å", icon: "success" }); getList(); }) .catch(() => { uni.showToast({ title: "å é¤å¤±è´¥", icon: "error" }); }) .finally(() => { uni.hideLoading(); }); }, }); }; @@ -204,7 +403,7 @@ </script> <style scoped lang="scss"> @import '@/styles/sales-common.scss'; @import "@/styles/sales-common.scss"; .main-production-plan { min-height: 100vh; @@ -216,6 +415,13 @@ .list-container { flex: 1; height: 0; padding-bottom: 140rpx; } .header-right { display: flex; align-items: center; padding-right: 12rpx; } .ledger-item { @@ -254,6 +460,15 @@ } } .action-buttons { display: flex; justify-content: flex-end; align-items: center; gap: 16rpx; padding-top: 16rpx; border-top: 1rpx solid #f0f0f0; } .item-details { padding: 10rpx 0; @@ -279,19 +494,24 @@ } } .item-footer { display: flex; justify-content: flex-end; align-items: center; padding-top: 16rpx; border-top: 1rpx solid #f0f0f0; .item-right :deep(.up-checkbox__label) { display: none; } } .more-detail { font-size: 24rpx; color: #999; margin-right: 8rpx; } } .fab-button { position: fixed; right: 30rpx; bottom: 140rpx; width: 100rpx; height: 100rpx; background: #3c9cff; border-radius: 50%; display: flex; align-items: center; justify-content: center; box-shadow: 0 8rpx 24rpx rgba(60, 156, 255, 0.3); z-index: 1001; } .no-data { src/pages/productionManagement/mainProductionPlan/issue.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,142 @@ <template> <view class="account-detail"> <PageHeader title="ä¸å" @back="goBack" /> <up-form ref="formRef" :model="form" :rules="rules" label-width="110"> <up-form-item label="产ååç§°"> <up-input v-model="form.productName" disabled placeholder="èªå¨å¡«å " /> </up-form-item> <up-form-item label="è§æ ¼åå·"> <up-input v-model="form.model" disabled placeholder="èªå¨å¡«å " /> </up-form-item> <up-form-item label="计å宿æ¶é´" prop="planCompleteTime" required> <up-input v-model="form.planCompleteTime" placeholder="è¯·éæ©" readonly @click="showPlanDatePicker = true" /> <template #right> <up-icon name="arrow-right" @click="showPlanDatePicker = true"></up-icon> </template> </up-form-item> <up-form-item label="ç产æ°é" prop="totalAssignedQuantity" required> <up-input v-model="form.totalAssignedQuantity" type="number" placeholder="请è¾å ¥" clearable /> </up-form-item> <up-form-item label="å¯ä¸åæ°é"> <up-input :model-value="String(maxQuantity)" disabled placeholder="èªå¨è®¡ç®" /> </up-form-item> </up-form> <FooterButtons :loading="loading" confirmText="ç¡®å®ä¸å" @cancel="goBack" @confirm="handleSubmit" /> <up-datetime-picker :show="showPlanDatePicker" v-model="planDateValue" mode="date" @confirm="onPlanDateConfirm" @cancel="showPlanDatePicker = false" /> </view> </template> <script setup> import { ref } from "vue"; import { onLoad } from "@dcloudio/uni-app"; import FooterButtons from "@/components/FooterButtons.vue"; import PageHeader from "@/components/PageHeader.vue"; import { formatDateToYMD } from "@/utils/ruoyi"; import { productionPlanCombine } from "@/api/productionManagement/productionPlan"; const formRef = ref(); const loading = ref(false); const maxQuantity = ref(0); const showPlanDatePicker = ref(false); const planDateValue = ref(Date.now()); const form = ref({ productName: "", model: "", totalAssignedQuantity: "", planCompleteTime: "", productId: undefined, ids: [], }); const rules = { planCompleteTime: [{ required: true, message: "è¯·éæ©è®¡å宿æ¶é´", trigger: "change" }], totalAssignedQuantity: [{ required: true, message: "请è¾å ¥ç产æ°é", trigger: "blur" }], }; const goBack = () => { uni.navigateBack(); }; const onPlanDateConfirm = e => { const value = e?.value ?? planDateValue.value; form.value.planCompleteTime = formatDateToYMD(value); showPlanDatePicker.value = false; }; const handleSubmit = async () => { const valid = await formRef.value.validate().catch(() => false); if (!valid) return; const qty = Number(form.value.totalAssignedQuantity); if (!qty || qty <= 0) { uni.showToast({ title: "ç产æ°éå¿ é¡»å¤§äº0", icon: "none" }); return; } if (qty > Number(maxQuantity.value || 0)) { uni.showToast({ title: "ç产æ°éä¸è½å¤§äºå¯ä¸åæ°é", icon: "none" }); return; } if (!Array.isArray(form.value.ids) || form.value.ids.length === 0) { uni.showToast({ title: "è¯·éæ©è¦ä¸åç计å", icon: "none" }); return; } loading.value = true; const payload = { ...form.value, totalAssignedQuantity: Number(qty.toFixed(4)), }; productionPlanCombine(payload) .then(res => { if (res?.code && Number(res.code) !== 200) { const msg = res?.msg || res?.message || "ä¸å失败"; uni.showToast({ title: String(msg), icon: "none" }); return; } uni.showToast({ title: "ä¸åæå", icon: "success" }); uni.$emit("mainProductionPlan:refresh"); goBack(); }) .catch(err => { const msg = err?.msg || err?.message || err?.data?.msg || err?.data?.message; uni.showToast({ title: msg ? String(msg) : "ä¸å失败", icon: "none" }); }) .finally(() => { loading.value = false; }); }; onLoad(options => { if (!options?.data) return; try { const payload = JSON.parse(decodeURIComponent(options.data)); form.value.productName = payload.productName || ""; form.value.model = payload.model || ""; form.value.productId = payload.productId ?? undefined; form.value.ids = Array.isArray(payload.ids) ? payload.ids : []; form.value.planCompleteTime = payload.planCompleteTime || ""; form.value.totalAssignedQuantity = String(payload.totalAssignedQuantity ?? ""); maxQuantity.value = Number(payload.maxQuantity ?? 0); if (!form.value.planCompleteTime) { form.value.planCompleteTime = formatDateToYMD(Date.now()); } } catch { uni.showToast({ title: "æ°æ®å 载失败", icon: "none" }); } }); </script> <style scoped lang="scss"> @import "@/static/scss/form-common.scss"; </style>