| | |
| | | }); |
| | | }; |
| | | |
| | | // è·åç©è设å¤å®æ¶æ°éæ°æ® |
| | | export const getIotRealtimeData = (id) => { |
| | | return request({ |
| | | url: `/stockInventory/iotRealtime/${id}`, |
| | | method: "get", |
| | | }); |
| | | }; |
| | | |
| | | // æ¹éè·åç©è设å¤å®æ¶æ°éæ°æ®ï¼POST请æ±ï¼ä¼ é设å¤IDæ°ç»ï¼ |
| | | export const batchGetIotRealtimeData = (deviceIds) => { |
| | | return request({ |
| | | url: `/stockInventory/iotRealtime`, |
| | | method: "post", |
| | | data: deviceIds, |
| | | }); |
| | | }; |
| | | |
| | |
| | | > |
| | | <el-option |
| | | v-for="person in employees" |
| | | :key="person.id" |
| | | :label="`${person.staffName}${person.postName ? ` (${person.postName})` : ''}`" |
| | | :value="person.id" |
| | | :key="person.userId" |
| | | :label="`${person.nickName || person.userName}${person.dept?.deptName ? ` (${person.dept.deptName})` : ''}`" |
| | | :value="person.userId" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | |
| | | import {ElMessage} from 'element-plus' |
| | | import {Plus, Document, Promotion, Bell} from '@element-plus/icons-vue' |
| | | import {getRoomEnum, saveMeetingApplication} from '@/api/collaborativeApproval/meeting.js' |
| | | import {staffOnJobListPage} from "@/api/personnelManagement/staffOnJob.js"; |
| | | import {userListNoPageByTenantId} from "@/api/system/user.js"; |
| | | |
| | | // å½åç³è¯·ç±»å |
| | | const currentType = ref('department') // approval: å®¡æ¹æµç¨, department: é¨é¨çº§, notification: éç¥åå¸ |
| | |
| | | getRoomEnum().then(res => { |
| | | meetingRooms.value = res.data |
| | | }) |
| | | staffOnJobListPage({ |
| | | current: -1, |
| | | size: -1, |
| | | staffState: 1 |
| | | }).then(res => { |
| | | employees.value = res.data.records.sort((a, b) => (a.postName || '').localeCompare(b.postName || '')) |
| | | userListNoPageByTenantId().then(res => { |
| | | employees.value = res.data || [] |
| | | }) |
| | | }) |
| | | </script> |
| | |
| | | placeholder="éæ©çææ¶é´" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | <el-form-item label="éç¨èå´" |
| | | prop="scope"> |
| | | <el-checkbox-group v-model="regulationForm.scope"> |
| | | <el-checkbox label="all">å
¨ä½åå·¥</el-checkbox> |
| | | <el-checkbox label="manager">管çå±</el-checkbox> |
| | | <el-checkbox label="hr">人äºé¨é¨</el-checkbox> |
| | | <el-checkbox label="finance">è´¢å¡é¨é¨</el-checkbox> |
| | | <el-checkbox label="tech">ææ¯é¨é¨</el-checkbox> |
| | | </el-checkbox-group> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="æ¯å¦éè¦ç¡®è®¤" |
| | | prop="requireConfirm"> |
| | | <el-switch v-model="regulationForm.requireConfirm" /> |
| | | <span class="ml-10">å¼å¯ååå·¥éè¦é
读确认</span> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="éä»¶ä¸ä¼ "> |
| | | <AttachmentUploadFile |
| | | v-model:fileList="regulationForm.storageBlobDTOs" |
| | | :limit="10" |
| | | :fileSize="50" |
| | | buttonText="ç¹å»ä¸ä¼ éä»¶" |
| | | /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | |
| | | addRuleFile, |
| | | } from "@/api/collaborativeApproval/rulesRegulationsManagementFile.js"; |
| | | import PIMTable from "@/components/PIMTable/PIMTable.vue"; |
| | | import AttachmentUploadFile from "@/components/AttachmentUpload/file/index.vue"; |
| | | |
| | | // ååºå¼æ°æ® |
| | | const operationType = ref("add"); |
| | |
| | | status: "active", |
| | | readCount: 0, |
| | | effectiveTime: "", |
| | | scope: [], |
| | | requireConfirm: false, |
| | | storageBlobDTOs: [] |
| | | }); |
| | | |
| | | const readStatus = ref({ |
| | |
| | | effectiveTime: [ |
| | | { required: true, message: "è¯·éæ©çææ¶é´", trigger: "change" }, |
| | | ], |
| | | scope: [{ required: true, message: "è¯·éæ©éç¨èå´", trigger: "change" }], |
| | | |
| | | }; |
| | | |
| | | const regulationSearchForm = reactive({ |
| | |
| | | status: "active", |
| | | readCount: 0, |
| | | effectiveTime: "", |
| | | scope: [], |
| | | requireConfirm: false, |
| | | storageBlobDTOs: [] |
| | | }); |
| | | }; |
| | | |
| | |
| | | <el-dialog title="æ¥çéä»¶" |
| | | v-model="dialogVisitable" width="800px" @close="cancel"> |
| | | <div class="upload-container"> |
| | | <!-- ç产å --> |
| | | <!-- ç产å¼å¸¸ --> |
| | | <div class="form-container"> |
| | | <div class="title">ç产å</div> |
| | | |
| | | <div class="title">ç产å¼å¸¸</div> |
| | | |
| | | <!-- å¾çå表 --> |
| | | <div style="display: flex; flex-wrap: wrap;"> |
| | | <img v-for="(item, index) in beforeProductionImgs" :key="index" |
| | | @click="showMedia(beforeProductionImgs, index, 'image')" |
| | | <img v-for="(item, index) in productionAbnormalImgs" :key="index" |
| | | @click="showMedia(productionAbnormalImgs, index, 'image')" |
| | | :src="item" style="max-width: 100px; height: 100px; margin: 5px;" alt=""> |
| | | </div> |
| | | |
| | | |
| | | <!-- è§é¢å表 --> |
| | | <div style="display: flex; flex-wrap: wrap;"> |
| | | <div |
| | | v-for="(videoUrl, index) in beforeProductionVideos" |
| | | v-for="(videoUrl, index) in productionAbnormalVideos" |
| | | :key="index" |
| | | @click="showMedia(beforeProductionVideos, index, 'video')" |
| | | style="position: relative; margin: 10px; cursor: pointer;" |
| | | > |
| | | <div style="width: 160px; height: 90px; background-color: #333; display: flex; align-items: center; justify-content: center;"> |
| | | <img src="@/assets/images/video.png" alt="ææ¾" style="width: 30px; height: 30px; opacity: 0.8;" /> |
| | | </div> |
| | | <div style="text-align: center; font-size: 12px; color: #666;">ç¹å»ææ¾</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- çäº§ä¸ --> |
| | | <div class="form-container"> |
| | | <div class="title">ç产ä¸</div> |
| | | |
| | | <!-- å¾çå表 --> |
| | | <div style="display: flex; flex-wrap: wrap;"> |
| | | <img v-for="(item, index) in productionIssuesImgs" :key="index" |
| | | @click="showMedia(productionIssuesImgs, index, 'image')" |
| | | :src="item" style="max-width: 100px; height: 100px; margin: 5px;" alt=""> |
| | | </div> |
| | | |
| | | <!-- è§é¢å表 --> |
| | | <div style="display: flex; flex-wrap: wrap;"> |
| | | <div |
| | | v-for="(videoUrl, index) in productionIssuesVideos" |
| | | :key="index" |
| | | @click="showMedia(productionIssuesVideos, index, 'video')" |
| | | style="position: relative; margin: 10px; cursor: pointer;" |
| | | > |
| | | <div style="width: 160px; height: 90px; background-color: #333; display: flex; align-items: center; justify-content: center;"> |
| | | <img src="@/assets/images/video.png" alt="ææ¾" style="width: 30px; height: 30px; opacity: 0.8;" /> |
| | | </div> |
| | | <div style="text-align: center; font-size: 12px; color: #666;">ç¹å»ææ¾</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- ç产å --> |
| | | <div class="form-container"> |
| | | <div class="title">ç产å</div> |
| | | |
| | | <!-- å¾çå表 --> |
| | | <div style="display: flex; flex-wrap: wrap;"> |
| | | <img v-for="(item, index) in afterProductionImgs" :key="index" |
| | | @click="showMedia(afterProductionImgs, index, 'image')" |
| | | :src="item" style="max-width: 100px; height: 100px; margin: 5px;" alt=""> |
| | | </div> |
| | | |
| | | <!-- è§é¢å表 --> |
| | | <div style="display: flex; flex-wrap: wrap;"> |
| | | <div |
| | | v-for="(videoUrl, index) in afterProductionVideos" |
| | | :key="index" |
| | | @click="showMedia(afterProductionVideos, index, 'video')" |
| | | @click="showMedia(productionAbnormalVideos, index, 'video')" |
| | | style="position: relative; margin: 10px; cursor: pointer;" |
| | | > |
| | | <div style="width: 160px; height: 90px; background-color: #333; display: flex; align-items: center; justify-content: center;"> |
| | |
| | | </div> |
| | | </div> |
| | | </el-dialog> |
| | | |
| | | |
| | | <!-- ç»ä¸åªä½æ¥çå¨ --> |
| | | <div v-if="isMediaViewerVisible" class="media-viewer-overlay" @click.self="closeMediaViewer"> |
| | | <div class="media-viewer-content" @click.stop> |
| | |
| | | :index="currentMediaIndex" |
| | | @hide="closeMediaViewer" |
| | | ></vue-easy-lightbox> |
| | | |
| | | |
| | | <!-- è§é¢ --> |
| | | <div v-else-if="mediaType === 'video'" style="position: relative;"> |
| | | <video |
| | |
| | | </div> |
| | | </template> |
| | | <script setup> |
| | | import { ref } from 'vue'; |
| | | import { ref, getCurrentInstance } from 'vue'; |
| | | import VueEasyLightbox from 'vue-easy-lightbox'; |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | |
| | | const dialogVisitable = ref(false); |
| | | |
| | | // å¾çæ°ç» |
| | | const beforeProductionImgs = ref([]); |
| | | const afterProductionImgs = ref([]); |
| | | const productionIssuesImgs = ref([]); |
| | | const productionAbnormalImgs = ref([]); |
| | | |
| | | // è§é¢æ°ç» |
| | | const beforeProductionVideos = ref([]); |
| | | const afterProductionVideos = ref([]); |
| | | const productionIssuesVideos = ref([]); |
| | | const productionAbnormalVideos = ref([]); |
| | | |
| | | // åªä½æ¥çå¨ç¶æ |
| | | const isMediaViewerVisible = ref(false); |
| | |
| | | // å¤ç URLï¼å° Windows è·¯å¾è½¬æ¢ä¸ºå¯è®¿é®ç URL |
| | | function processFileUrl(fileUrl) { |
| | | if (!fileUrl) return ''; |
| | | |
| | | |
| | | // 妿 URL æ¯ Windows è·¯å¾æ ¼å¼ï¼å
å«åææ ï¼ï¼éè¦è½¬æ¢ |
| | | if (fileUrl && fileUrl.indexOf('\\') > -1) { |
| | | // æ¥æ¾ uploads å
³é®åçä½ç½®ï¼ä»é£éå¼å§æåç¸å¯¹è·¯å¾ |
| | |
| | | fileUrl = '/uploads/' + fileName; |
| | | } |
| | | } |
| | | |
| | | |
| | | // ç¡®ä¿ææé http å¼å¤´ç URL 齿¼æ¥ baseUrl |
| | | if (fileUrl && !fileUrl.startsWith('http')) { |
| | | // ç¡®ä¿è·¯å¾ä»¥ / å¼å¤´ |
| | |
| | | // æ¼æ¥ baseUrl |
| | | fileUrl = javaApi + fileUrl; |
| | | } |
| | | |
| | | |
| | | return fileUrl; |
| | | } |
| | | |
| | |
| | | function processItems(items) { |
| | | const images = []; |
| | | const videos = []; |
| | | |
| | | |
| | | // æ£æ¥ items æ¯å¦åå¨ä¸ä¸ºæ°ç» |
| | | if (!items || !Array.isArray(items)) { |
| | | return { images, videos }; |
| | | } |
| | | |
| | | |
| | | items.forEach(item => { |
| | | if (!item || !item.url) return; |
| | | |
| | | |
| | | // å¤çæä»¶ URL |
| | | const fileUrl = processFileUrl(item.url); |
| | | |
| | | |
| | | // æ ¹æ®æä»¶æ©å±å夿æ¯å¾çè¿æ¯è§é¢ |
| | | const urlLower = fileUrl.toLowerCase(); |
| | | if (urlLower.match(/\.(jpg|jpeg|png|gif|bmp|webp)$/)) { |
| | |
| | | } |
| | | } |
| | | }); |
| | | |
| | | |
| | | return { images, videos }; |
| | | } |
| | | |
| | | // æå¼å¼¹çªå¹¶å è½½æ°æ® |
| | | const openDialog = async (row) => { |
| | | // ä½¿ç¨æ£ç¡®çåæ®µåï¼commonFileListBefore, commonFileListAfter |
| | | // productionIssues å¯è½ä¸åå¨ï¼ä½¿ç¨ç©ºæ°ç» |
| | | const { images: beforeImgs, videos: beforeVids } = processItems(row.commonFileListBefore || []); |
| | | const { images: afterImgs, videos: afterVids } = processItems(row.commonFileListAfter || []); |
| | | const { images: issueImgs, videos: issueVids } = processItems(row.productionIssues || []); |
| | | |
| | | beforeProductionImgs.value = beforeImgs; |
| | | beforeProductionVideos.value = beforeVids; |
| | | |
| | | afterProductionImgs.value = afterImgs; |
| | | afterProductionVideos.value = afterVids; |
| | | |
| | | productionIssuesImgs.value = issueImgs; |
| | | productionIssuesVideos.value = issueVids; |
| | | |
| | | // ä½¿ç¨ commonFileListBeforeDTO æ commonFileListBeforeVO åæ®µ |
| | | const fileList = row.commonFileListBeforeDTO || row.commonFileListBeforeVO || []; |
| | | const { images, videos } = processItems(fileList); |
| | | |
| | | productionAbnormalImgs.value = images; |
| | | productionAbnormalVideos.value = videos; |
| | | |
| | | dialogVisitable.value = true; |
| | | }; |
| | | |
| | |
| | | padding: 20px; |
| | | border: 1px solid #dcdfe6; |
| | | box-sizing: border-box; |
| | | |
| | | |
| | | .form-container { |
| | | flex: 1; |
| | | width: 100%; |
| | |
| | | padding-left: 10px; |
| | | position: relative; |
| | | margin: 6px 0; |
| | | |
| | | |
| | | &::before { |
| | | content: ""; |
| | | position: absolute; |
| | |
| | | max-height: 90vh; |
| | | overflow: hidden; |
| | | } |
| | | </style> |
| | | </style> |
| | |
| | | total, |
| | | layout: 'total, sizes, prev, pager, next, jumper', |
| | | }" |
| | | :table-style="{ width: '100%', height: 'calc(100vh - 23em)' }" |
| | | @selection-change="handleSelectionChange" |
| | | @pagination="handlePagination" |
| | | > |
| | |
| | | monthSaleHaveMoney: 0, |
| | | monthSaleMoney: 0, |
| | | todayInventoryNum: 0, |
| | | lastMonthSaleMoney: 0, |
| | | lastMonthPurchaseMoney: 0, |
| | | }); |
| | | |
| | | const qualityStatisticsObject = ref({ |
| | |
| | | }); |
| | | }); |
| | | |
| | | // 计ç®ç¯æ¯å¢é¿ç |
| | | const calculateGrowthRate = (current, last) => { |
| | | const currentVal = parseFloat(current) || 0; |
| | | const lastVal = parseFloat(last) || 0; |
| | | if (lastVal === 0) return currentVal > 0 ? 100 : 0; |
| | | return ((currentVal - lastVal) / lastVal * 100).toFixed(1); |
| | | }; |
| | | |
| | | const dashboardCards = computed(() => [ |
| | | { |
| | | key: "sales", |
| | | title: "é宿°æ®", |
| | | desc: "æ¬æéå®é¢(å
)", |
| | | value: formatNumber(businessInfo.value.monthSaleMoney), |
| | | subLabel: "æªå¼ç¥¨éé¢", |
| | | subValue: formatNumber(businessInfo.value.monthSaleHaveMoney), |
| | | trend: `å æ¯ ${ratioText(businessInfo.value.monthSaleHaveMoney, businessInfo.value.monthSaleMoney)}`, |
| | | subLabel: "䏿éå®", |
| | | subValue: formatNumber(businessInfo.value.lastMonthSaleMoney), |
| | | trend: (() => { |
| | | const rate = calculateGrowthRate(businessInfo.value.monthSaleMoney, businessInfo.value.lastMonthSaleMoney); |
| | | const num = parseFloat(rate); |
| | | if (num > 0) return `ç¯æ¯ â ${rate}%`; |
| | | if (num < 0) return `ç¯æ¯ â ${Math.abs(num)}%`; |
| | | return "ç¯æ¯æå¹³"; |
| | | })(), |
| | | icon: DataLine, |
| | | visible: visibleModules.value.sales, |
| | | }, |
| | |
| | | title: "éè´æ°æ®", |
| | | desc: "æ¬æéè´é¢(å
)", |
| | | value: formatNumber(businessInfo.value.monthPurchaseMoney), |
| | | subLabel: "å¾
仿¬¾éé¢", |
| | | subValue: formatNumber(businessInfo.value.monthPurchaseHaveMoney), |
| | | trend: `å æ¯ ${ratioText( |
| | | businessInfo.value.monthPurchaseHaveMoney, |
| | | businessInfo.value.monthPurchaseMoney |
| | | )}`, |
| | | subLabel: "䏿éè´", |
| | | subValue: formatNumber(businessInfo.value.lastMonthPurchaseMoney), |
| | | trend: (() => { |
| | | const rate = calculateGrowthRate(businessInfo.value.monthPurchaseMoney, businessInfo.value.lastMonthPurchaseMoney); |
| | | const num = parseFloat(rate); |
| | | if (num > 0) return `ç¯æ¯ â ${rate}%`; |
| | | if (num < 0) return `ç¯æ¯ â ${Math.abs(num)}%`; |
| | | return "ç¯æ¯æå¹³"; |
| | | })(), |
| | | icon: ShoppingCartFull, |
| | | visible: visibleModules.value.procurement, |
| | | }, |
| | |
| | | <template> |
| | | <div> |
| | | <div class="search_form" |
| | | style="margin-bottom: 10px"> |
| | | <el-form ref="searchFormRef" |
| | | :model="searchForm" |
| | | class="demo-form-inline"> |
| | | <div class="search_form" style="margin-bottom: 10px"> |
| | | <el-form ref="searchFormRef" :model="searchForm" class="demo-form-inline"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="4"> |
| | | <el-form-item label="åºåºæ¥æ" |
| | | prop="timeStr"> |
| | | <el-date-picker v-model="searchForm.timeStr" |
| | | type="date" |
| | | placeholder="è¯·éæ©æ¥æ" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | clearable /> |
| | | <el-form-item label="åºåºæ¥æ" prop="timeStr"> |
| | | <el-date-picker |
| | | v-model="searchForm.timeStr" |
| | | type="date" |
| | | placeholder="è¯·éæ©æ¥æ" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-form-item label="产å大类" |
| | | prop="productName"> |
| | | <el-input v-model="searchForm.productName" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥" |
| | | clearable /> |
| | | <el-form-item label="产å大类" prop="productName"> |
| | | <el-input |
| | | v-model="searchForm.productName" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-form-item label="è§æ ¼åå·" |
| | | prop="model"> |
| | | <el-input v-model="searchForm.model" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥" |
| | | clearable /> |
| | | <el-form-item label="è§æ ¼åå·" prop="model"> |
| | | <el-input |
| | | v-model="searchForm.model" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-form-item label="æ¹å·" |
| | | prop="batchNo"> |
| | | <el-input v-model="searchForm.batchNo" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥" |
| | | clearable /> |
| | | <el-form-item label="æ¹å·" prop="batchNo"> |
| | | <el-input |
| | | v-model="searchForm.batchNo" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-form-item label="æ¥æº" |
| | | prop="recordType"> |
| | | <el-select v-model="searchForm.recordType" |
| | | style="width: 240px" |
| | | placeholder="è¯·éæ©" |
| | | clearable> |
| | | <el-option v-for="item in stockRecordTypeOptions" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" /> |
| | | <el-form-item label="æ¥æº" prop="recordType"> |
| | | <el-select |
| | | v-model="searchForm.recordType" |
| | | style="width: 240px" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | > |
| | | <el-option |
| | | v-for="item in stockRecordTypeOptions" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <!-- æé® --> |
| | | <el-col :span="4"> |
| | | <el-form-item> |
| | | <el-button type="primary" |
| | | @click="getList"> |
| | | æç´¢ |
| | | </el-button> |
| | | <el-button @click="resetSearch"> |
| | | éç½® |
| | | </el-button> |
| | | <el-button type="primary" @click="getList"> æç´¢ </el-button> |
| | | <el-button @click="resetSearch"> éç½® </el-button> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | </div> |
| | | <div class="actions"> |
| | | <el-button type="primary" |
| | | @click="handleBatchApprove">审æ¹</el-button> |
| | | <el-button type="primary" @click="handleBatchApprove">审æ¹</el-button> |
| | | <el-button @click="handleOut">导åº</el-button> |
| | | <el-button type="danger" |
| | | plain |
| | | @click="handleDelete">å é¤</el-button> |
| | | <el-button type="primary" |
| | | plain |
| | | @click="handlePrint">æå°</el-button> |
| | | <el-button type="danger" plain @click="handleDelete">å é¤</el-button> |
| | | <el-button type="primary" plain @click="handlePrint">æå°</el-button> |
| | | </div> |
| | | <div class="table_list"> |
| | | <el-table :data="tableData" |
| | | border |
| | | v-loading="tableLoading" |
| | | @selection-change="handleSelectionChange" |
| | | :expand-row-keys="expandedRowKeys" |
| | | :row-key="(row) => row.id" |
| | | style="width: 100%" |
| | | height="calc(100vh - 18.5em)"> |
| | | <el-table-column align="center" |
| | | type="selection" |
| | | width="55" /> |
| | | <el-table-column align="center" |
| | | label="åºå·" |
| | | type="index" |
| | | width="60" /> |
| | | <el-table-column label="åºåºæ¹æ¬¡" |
| | | prop="outboundBatches" |
| | | min-width="100" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="åºåºæ¥æ" |
| | | prop="createTime" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="产å大类" |
| | | prop="productName" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="è§æ ¼åå·" |
| | | prop="model" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="æ¹å·" |
| | | prop="batchNo" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="åä½" |
| | | prop="unit" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="åºåºæ°é" |
| | | prop="stockOutNum" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="åºåºäºº" |
| | | prop="createBy" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="æ¥æº" |
| | | prop="recordType" |
| | | show-overflow-tooltip> |
| | | <el-table |
| | | :data="tableData" |
| | | border |
| | | v-loading="tableLoading" |
| | | @selection-change="handleSelectionChange" |
| | | :expand-row-keys="expandedRowKeys" |
| | | :row-key="(row) => row.id" |
| | | style="width: 100%" |
| | | height="calc(100vh - 18.5em)" |
| | | > |
| | | <el-table-column align="center" type="selection" width="55" /> |
| | | <el-table-column align="center" label="åºå·" type="index" width="60" /> |
| | | <el-table-column |
| | | label="åºåºæ¹æ¬¡" |
| | | prop="outboundBatches" |
| | | min-width="100" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | label="åºåºæ¥æ" |
| | | prop="updateTime" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | label="产å大类" |
| | | prop="productName" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column label="è§æ ¼åå·" prop="model" show-overflow-tooltip /> |
| | | <el-table-column label="æ¹å·" prop="batchNo" show-overflow-tooltip /> |
| | | <el-table-column label="åä½" prop="unit" show-overflow-tooltip /> |
| | | <el-table-column |
| | | label="åºåºæ°é" |
| | | prop="stockOutNum" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column label="åºåºäºº" prop="createBy" show-overflow-tooltip /> |
| | | <el-table-column label="æ¥æº" prop="recordType" show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | {{ getRecordType(scope.row.recordType) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="审æ¹ç¶æ" |
| | | prop="approvalStatus" |
| | | show-overflow-tooltip> |
| | | <el-table-column |
| | | label="审æ¹ç¶æ" |
| | | prop="approvalStatus" |
| | | show-overflow-tooltip |
| | | > |
| | | <template #default="scope"> |
| | | <el-tag :type="getApprovalStatusTagType(scope.row.approvalStatus)" |
| | | size="small"> |
| | | <el-tag |
| | | :type="getApprovalStatusTagType(scope.row.approvalStatus)" |
| | | size="small" |
| | | > |
| | | {{ getApprovalStatusLabel(scope.row.approvalStatus) }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <pagination v-show="total > 0" |
| | | :total="total" |
| | | layout="total, sizes, prev, pager, next, jumper" |
| | | :page="page.current" |
| | | :limit="page.size" |
| | | @pagination="paginationChange" /> |
| | | <pagination |
| | | v-show="total > 0" |
| | | :total="total" |
| | | layout="total, sizes, prev, pager, next, jumper" |
| | | :page="page.current" |
| | | :limit="page.size" |
| | | @pagination="paginationChange" |
| | | /> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import pagination from "@/components/PIMTable/Pagination.vue"; |
| | | import { ref } from "vue"; |
| | | import { ElMessageBox } from "element-plus"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import { getCurrentDate } from "@/utils/index.js"; |
| | | import { |
| | | getStockOutPage, |
| | | delPendingStockOut, |
| | | batchApproveStockOutRecords, |
| | | } from "@/api/inventoryManagement/stockOut.js"; |
| | | import { |
| | | findAllQualifiedStockOutRecordTypeOptions, |
| | | findAllUnQualifiedStockOutRecordTypeOptions, |
| | | } from "@/api/basicData/enum.js"; |
| | | import pagination from "@/components/PIMTable/Pagination.vue"; |
| | | import { ref } from "vue"; |
| | | import { ElMessageBox } from "element-plus"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import { getCurrentDate } from "@/utils/index.js"; |
| | | import { |
| | | getStockOutPage, |
| | | delPendingStockOut, |
| | | batchApproveStockOutRecords, |
| | | } from "@/api/inventoryManagement/stockOut.js"; |
| | | import { |
| | | findAllQualifiedStockOutRecordTypeOptions, |
| | | findAllUnQualifiedStockOutRecordTypeOptions, |
| | | } from "@/api/basicData/enum.js"; |
| | | |
| | | const userStore = useUserStore(); |
| | | const { proxy } = getCurrentInstance(); |
| | | const tableData = ref([]); |
| | | const selectedRows = ref([]); |
| | | const tableLoading = ref(false); |
| | | // æ¥æºç±»åé项 |
| | | const stockRecordTypeOptions = ref([]); |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | | }); |
| | | const total = ref(0); |
| | | const userStore = useUserStore(); |
| | | const { proxy } = getCurrentInstance(); |
| | | const tableData = ref([]); |
| | | const selectedRows = ref([]); |
| | | const tableLoading = ref(false); |
| | | // æ¥æºç±»åé项 |
| | | const stockRecordTypeOptions = ref([]); |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | | }); |
| | | const total = ref(0); |
| | | |
| | | const props = defineProps({ |
| | | type: { |
| | | type: String, |
| | | required: true, |
| | | default: "0", |
| | | }, |
| | | topParentProductId: { |
| | | type: [String, Number], |
| | | default: undefined, |
| | | }, |
| | | }); |
| | | const props = defineProps({ |
| | | type: { |
| | | type: String, |
| | | required: true, |
| | | default: "0", |
| | | }, |
| | | topParentProductId: { |
| | | type: [String, Number], |
| | | default: undefined, |
| | | }, |
| | | }); |
| | | |
| | | // æå°ç¸å
³ |
| | | const printPreviewVisible = ref(false); |
| | | const printData = ref([]); |
| | | // æå°ç¸å
³ |
| | | const printPreviewVisible = ref(false); |
| | | const printData = ref([]); |
| | | |
| | | // ç¨æ·ä¿¡æ¯è¡¨åå¼¹æ¡æ°æ® |
| | | const data = reactive({ |
| | | searchForm: { |
| | | supplierName: "", |
| | | timeStr: "", |
| | | recordType: "", |
| | | }, |
| | | }); |
| | | const { searchForm } = toRefs(data); |
| | | // ç¨æ·ä¿¡æ¯è¡¨åå¼¹æ¡æ°æ® |
| | | const data = reactive({ |
| | | searchForm: { |
| | | supplierName: "", |
| | | timeStr: "", |
| | | recordType: "", |
| | | }, |
| | | }); |
| | | const { searchForm } = toRefs(data); |
| | | |
| | | const searchFormRef = ref(null); |
| | | const searchFormRef = ref(null); |
| | | |
| | | const resetSearch = () => { |
| | | searchFormRef.value?.resetFields(); |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | const resetSearch = () => { |
| | | searchFormRef.value?.resetFields(); |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | |
| | | const paginationChange = obj => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList(); |
| | | }; |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | getStockOutPage({ |
| | | ...searchForm.value, |
| | | ...page, |
| | | topParentProductId: props.topParentProductId, |
| | | const paginationChange = (obj) => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList(); |
| | | }; |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | getStockOutPage({ |
| | | ...searchForm.value, |
| | | ...page, |
| | | topParentProductId: props.topParentProductId, |
| | | }) |
| | | .then((res) => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records; |
| | | tableData.value.map((item) => { |
| | | item.children = []; |
| | | }); |
| | | total.value = res.data.total; |
| | | }) |
| | | .then(res => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records; |
| | | tableData.value.map(item => { |
| | | item.children = []; |
| | | }); |
| | | total.value = res.data.total; |
| | | }) |
| | | .catch(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | .catch(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | const getRecordType = recordType => { |
| | | return ( |
| | | stockRecordTypeOptions.value.find(item => item.value === recordType) |
| | | ?.label || "" |
| | | ); |
| | | }; |
| | | const getRecordType = (recordType) => { |
| | | return ( |
| | | stockRecordTypeOptions.value.find((item) => item.value === recordType) |
| | | ?.label || "" |
| | | ); |
| | | }; |
| | | |
| | | const approvalStatusLabelMap = { |
| | | 0: "å¾
审æ¹", |
| | | 1: "éè¿", |
| | | 2: "驳å", |
| | | 3: "å¾
确认", |
| | | pending: "å¾
审æ¹", |
| | | approved: "éè¿", |
| | | rejected: "驳å", |
| | | PENDING: "å¾
审æ¹", |
| | | APPROVED: "éè¿", |
| | | REJECTED: "驳å", |
| | | }; |
| | | const approvalStatusLabelMap = { |
| | | 0: "å¾
审æ¹", |
| | | 1: "éè¿", |
| | | 2: "驳å", |
| | | 3: "å¾
确认", |
| | | pending: "å¾
审æ¹", |
| | | approved: "éè¿", |
| | | rejected: "驳å", |
| | | PENDING: "å¾
审æ¹", |
| | | APPROVED: "éè¿", |
| | | REJECTED: "驳å", |
| | | }; |
| | | |
| | | const getApprovalStatusLabel = status => { |
| | | if (status === null || status === undefined || status === "") { |
| | | return "å¾
审æ¹"; |
| | | } |
| | | return approvalStatusLabelMap[status] || "å¾
审æ¹"; |
| | | }; |
| | | const getApprovalStatusLabel = (status) => { |
| | | if (status === null || status === undefined || status === "") { |
| | | return "å¾
审æ¹"; |
| | | } |
| | | return approvalStatusLabelMap[status] || "å¾
审æ¹"; |
| | | }; |
| | | |
| | | // éè¿/驳ååºå®è²ï¼å
¶ä½ï¼å«å¾
审æ¹ã空å¼ãæªæ å°ä½ææ¡ä¸ºå¾
审æ¹ï¼ç»ä¸ç¨ warning é¢è¦è² |
| | | const getApprovalStatusTagType = status => { |
| | | if ( |
| | | status === 1 || |
| | | status === "1" || |
| | | status === "approved" || |
| | | status === "APPROVED" |
| | | ) |
| | | return "success"; |
| | | if ( |
| | | status === 2 || |
| | | status === "2" || |
| | | status === "rejected" || |
| | | status === "REJECTED" |
| | | ) |
| | | return "danger"; |
| | | return "warning"; |
| | | }; |
| | | // éè¿/驳ååºå®è²ï¼å
¶ä½ï¼å«å¾
审æ¹ã空å¼ãæªæ å°ä½ææ¡ä¸ºå¾
审æ¹ï¼ç»ä¸ç¨ warning é¢è¦è² |
| | | const getApprovalStatusTagType = (status) => { |
| | | if ( |
| | | status === 1 || |
| | | status === "1" || |
| | | status === "approved" || |
| | | status === "APPROVED" |
| | | ) |
| | | return "success"; |
| | | if ( |
| | | status === 2 || |
| | | status === "2" || |
| | | status === "rejected" || |
| | | status === "REJECTED" |
| | | ) |
| | | return "danger"; |
| | | return "warning"; |
| | | }; |
| | | |
| | | // è·åæ¥æºç±»åé项 |
| | | const fetchStockRecordTypeOptions = () => { |
| | | if (props.type === "0") { |
| | | findAllQualifiedStockOutRecordTypeOptions().then(res => { |
| | | stockRecordTypeOptions.value = res.data; |
| | | }); |
| | | return; |
| | | } |
| | | findAllUnQualifiedStockOutRecordTypeOptions().then(res => { |
| | | // è·åæ¥æºç±»åé项 |
| | | const fetchStockRecordTypeOptions = () => { |
| | | if (props.type === "0") { |
| | | findAllQualifiedStockOutRecordTypeOptions().then((res) => { |
| | | stockRecordTypeOptions.value = res.data; |
| | | }); |
| | | }; |
| | | return; |
| | | } |
| | | findAllUnQualifiedStockOutRecordTypeOptions().then((res) => { |
| | | stockRecordTypeOptions.value = res.data; |
| | | }); |
| | | }; |
| | | |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | | const handleSelectionChange = selection => { |
| | | // è¿æ»¤æåæ°æ® |
| | | selectedRows.value = selection.filter(item => item.id); |
| | | console.log("selection", selectedRows.value); |
| | | }; |
| | | const expandedRowKeys = ref([]); |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | | const handleSelectionChange = (selection) => { |
| | | // è¿æ»¤æåæ°æ® |
| | | selectedRows.value = selection.filter((item) => item.id); |
| | | console.log("selection", selectedRows.value); |
| | | }; |
| | | const expandedRowKeys = ref([]); |
| | | |
| | | const handleBatchApprove = () => { |
| | | if (selectedRows.value.length === 0) { |
| | | proxy.$modal.msgWarning("è¯·éæ©æ°æ®"); |
| | | return; |
| | | } |
| | | const ids = selectedRows.value.map(item => item.id); |
| | | ElMessageBox.confirm("è¯·éæ©å®¡æ¹ç»æ", "审æ¹", { |
| | | confirmButtonText: "éè¿", |
| | | cancelButtonText: "驳å", |
| | | type: "warning", |
| | | distinguishCancelAndClose: true, |
| | | const handleBatchApprove = () => { |
| | | if (selectedRows.value.length === 0) { |
| | | proxy.$modal.msgWarning("è¯·éæ©æ°æ®"); |
| | | return; |
| | | } |
| | | const ids = selectedRows.value.map((item) => item.id); |
| | | ElMessageBox.confirm("è¯·éæ©å®¡æ¹ç»æ", "审æ¹", { |
| | | confirmButtonText: "éè¿", |
| | | cancelButtonText: "驳å", |
| | | type: "warning", |
| | | distinguishCancelAndClose: true, |
| | | }) |
| | | .then(() => { |
| | | batchApproveStockOutRecords({ ids, approvalStatus: 1 }) |
| | | .then(() => { |
| | | proxy.$modal.msgSuccess("审æ¹éè¿æå"); |
| | | getList(); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msgError("审æ¹éè¿å¤±è´¥"); |
| | | }); |
| | | }) |
| | | .then(() => { |
| | | batchApproveStockOutRecords({ ids, approvalStatus: 1 }) |
| | | .catch((action) => { |
| | | if (action === "cancel") { |
| | | batchApproveStockOutRecords({ ids, approvalStatus: 2 }) |
| | | .then(() => { |
| | | proxy.$modal.msgSuccess("审æ¹éè¿æå"); |
| | | proxy.$modal.msgSuccess("审æ¹é©³åæå"); |
| | | getList(); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msgError("审æ¹éè¿å¤±è´¥"); |
| | | proxy.$modal.msgError("审æ¹é©³å失败"); |
| | | }); |
| | | }) |
| | | .catch(action => { |
| | | if (action === "cancel") { |
| | | batchApproveStockOutRecords({ ids, approvalStatus: 2 }) |
| | | .then(() => { |
| | | proxy.$modal.msgSuccess("审æ¹é©³åæå"); |
| | | getList(); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msgError("审æ¹é©³å失败"); |
| | | }); |
| | | return; |
| | | } |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | }; |
| | | return; |
| | | } |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | }; |
| | | |
| | | // å¯¼åº |
| | | const handleOut = () => { |
| | | ElMessageBox.confirm("æ¯å¦ç¡®è®¤å¯¼åºï¼", "导åº", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | // å¯¼åº |
| | | const handleOut = () => { |
| | | ElMessageBox.confirm("æ¯å¦ç¡®è®¤å¯¼åºï¼", "导åº", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | proxy.download( |
| | | "/stockOutRecord/exportStockOutRecord", |
| | | { type: props.type }, |
| | | props.type === "0" ? "åæ ¼åºåºå°è´¦.xlsx" : "ä¸åæ ¼åºåºå°è´¦.xlsx" |
| | | ); |
| | | }) |
| | | .then(() => { |
| | | proxy.download( |
| | | "/stockOutRecord/exportStockOutRecord", |
| | | { type: props.type }, |
| | | props.type === "0" ? "åæ ¼åºåºå°è´¦.xlsx" : "ä¸åæ ¼åºåºå°è´¦.xlsx" |
| | | ); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | }; |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | }; |
| | | |
| | | // å é¤ |
| | | const handleDelete = () => { |
| | | let ids = []; |
| | | if (selectedRows.value.length > 0) { |
| | | ids = selectedRows.value.map(item => item.id); |
| | | } else { |
| | | proxy.$modal.msgWarning("è¯·éæ©æ°æ®"); |
| | | return; |
| | | } |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«å é¤ï¼æ¯å¦ç¡®è®¤å é¤ï¼", "导åº", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | // å é¤ |
| | | const handleDelete = () => { |
| | | let ids = []; |
| | | if (selectedRows.value.length > 0) { |
| | | ids = selectedRows.value.map((item) => item.id); |
| | | } else { |
| | | proxy.$modal.msgWarning("è¯·éæ©æ°æ®"); |
| | | return; |
| | | } |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«å é¤ï¼æ¯å¦ç¡®è®¤å é¤ï¼", "导åº", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | delPendingStockOut(ids).then((res) => { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | getList(); |
| | | }); |
| | | }) |
| | | .then(() => { |
| | | delPendingStockOut(ids).then(res => { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | getList(); |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | }; |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | }; |
| | | |
| | | // æå°åè½ |
| | | const handlePrint = () => { |
| | | if (selectedRows.value.length === 0) { |
| | | proxy.$modal.msgWarning("è¯·éæ©è¦æå°çæ°æ®"); |
| | | return; |
| | | } |
| | | printData.value = [...selectedRows.value]; |
| | | console.log("æå°æ°æ®:", printData.value); |
| | | printPreviewVisible.value = true; |
| | | }; |
| | | // æå°åè½ |
| | | const handlePrint = () => { |
| | | if (selectedRows.value.length === 0) { |
| | | proxy.$modal.msgWarning("è¯·éæ©è¦æå°çæ°æ®"); |
| | | return; |
| | | } |
| | | printData.value = [...selectedRows.value]; |
| | | console.log("æå°æ°æ®:", printData.value); |
| | | printPreviewVisible.value = true; |
| | | }; |
| | | |
| | | // æ§è¡æå° |
| | | const executePrint = () => { |
| | | console.log("å¼å§æ§è¡æå°ï¼æ°æ®æ¡æ°:", printData.value.length); |
| | | console.log("æå°æ°æ®:", printData.value); |
| | | // æ§è¡æå° |
| | | const executePrint = () => { |
| | | console.log("å¼å§æ§è¡æå°ï¼æ°æ®æ¡æ°:", printData.value.length); |
| | | console.log("æå°æ°æ®:", printData.value); |
| | | |
| | | // å建ä¸ä¸ªæ°çæå°çªå£ |
| | | const printWindow = window.open("", "_blank", "width=800,height=600"); |
| | | // å建ä¸ä¸ªæ°çæå°çªå£ |
| | | const printWindow = window.open("", "_blank", "width=800,height=600"); |
| | | |
| | | // æå»ºæå°å
容 |
| | | let printContent = ` |
| | | // æå»ºæå°å
容 |
| | | let printContent = ` |
| | | <!DOCTYPE html> |
| | | <html> |
| | | <head> |
| | |
| | | <body> |
| | | `; |
| | | |
| | | // ä¸ºæ¯æ¡æ°æ®çææå°é¡µé¢ |
| | | printData.value.forEach((item, index) => { |
| | | printContent += ` |
| | | // ä¸ºæ¯æ¡æ°æ®çææå°é¡µé¢ |
| | | printData.value.forEach((item, index) => { |
| | | printContent += ` |
| | | <div class="print-page"> |
| | | <div class="delivery-note"> |
| | | <div class="header"> |
| | |
| | | </div> |
| | | </div> |
| | | `; |
| | | }); |
| | | }); |
| | | |
| | | printContent += ` |
| | | printContent += ` |
| | | </body> |
| | | </html> |
| | | `; |
| | | |
| | | // åå
¥å
容尿°çªå£ |
| | | printWindow.document.write(printContent); |
| | | printWindow.document.close(); |
| | | // åå
¥å
容尿°çªå£ |
| | | printWindow.document.write(printContent); |
| | | printWindow.document.close(); |
| | | |
| | | // çå¾
å
容å è½½å®æåæå° |
| | | printWindow.onload = () => { |
| | | setTimeout(() => { |
| | | printWindow.print(); |
| | | printWindow.close(); |
| | | printPreviewVisible.value = false; |
| | | }, 500); |
| | | }; |
| | | // çå¾
å
容å è½½å®æåæå° |
| | | printWindow.onload = () => { |
| | | setTimeout(() => { |
| | | printWindow.print(); |
| | | printWindow.close(); |
| | | printPreviewVisible.value = false; |
| | | }, 500); |
| | | }; |
| | | }; |
| | | |
| | | // æ ¼å¼åæ¥æ |
| | | const formatDate = dateString => { |
| | | if (!dateString) return getCurrentDate(); |
| | | const date = new Date(dateString); |
| | | const year = date.getFullYear(); |
| | | const month = String(date.getMonth() + 1).padStart(2, "0"); |
| | | const day = String(date.getDate()).padStart(2, "0"); |
| | | return `${year}/${month}/${day}`; |
| | | }; |
| | | // æ ¼å¼åæ¥æ |
| | | const formatDate = (dateString) => { |
| | | if (!dateString) return getCurrentDate(); |
| | | const date = new Date(dateString); |
| | | const year = date.getFullYear(); |
| | | const month = String(date.getMonth() + 1).padStart(2, "0"); |
| | | const day = String(date.getDate()).padStart(2, "0"); |
| | | return `${year}/${month}/${day}`; |
| | | }; |
| | | |
| | | // æ ¼å¼åæ¥ææ¶é´ |
| | | const formatDateTime = date => { |
| | | const year = date.getFullYear(); |
| | | const month = String(date.getMonth() + 1).padStart(2, "0"); |
| | | const day = String(date.getDate()).padStart(2, "0"); |
| | | const hours = String(date.getHours()).padStart(2, "0"); |
| | | const minutes = String(date.getMinutes()).padStart(2, "0"); |
| | | const seconds = String(date.getSeconds()).padStart(2, "0"); |
| | | return `${year}/${month}/${day} ${hours}:${minutes}:${seconds}`; |
| | | }; |
| | | onMounted(() => { |
| | | // æ ¼å¼åæ¥ææ¶é´ |
| | | const formatDateTime = (date) => { |
| | | const year = date.getFullYear(); |
| | | const month = String(date.getMonth() + 1).padStart(2, "0"); |
| | | const day = String(date.getDate()).padStart(2, "0"); |
| | | const hours = String(date.getHours()).padStart(2, "0"); |
| | | const minutes = String(date.getMinutes()).padStart(2, "0"); |
| | | const seconds = String(date.getSeconds()).padStart(2, "0"); |
| | | return `${year}/${month}/${day} ${hours}:${minutes}:${seconds}`; |
| | | }; |
| | | onMounted(() => { |
| | | getList(); |
| | | fetchStockRecordTypeOptions(); |
| | | }); |
| | | |
| | | watch( |
| | | () => props.topParentProductId, |
| | | () => { |
| | | page.current = 1; |
| | | getList(); |
| | | fetchStockRecordTypeOptions(); |
| | | }); |
| | | |
| | | watch( |
| | | () => props.topParentProductId, |
| | | () => { |
| | | page.current = 1; |
| | | getList(); |
| | | } |
| | | ); |
| | | } |
| | | ); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .print-preview-dialog { |
| | | .el-dialog__body { |
| | | padding: 0; |
| | | max-height: 80vh; |
| | | overflow-y: auto; |
| | | } |
| | | .print-preview-dialog { |
| | | .el-dialog__body { |
| | | padding: 0; |
| | | max-height: 80vh; |
| | | overflow-y: auto; |
| | | } |
| | | } |
| | | |
| | | .print-preview-container { |
| | | .print-preview-header { |
| | | padding: 15px; |
| | | border-bottom: 1px solid #e4e7ed; |
| | | text-align: center; |
| | | |
| | | .el-button { |
| | | margin: 0 10px; |
| | | } |
| | | } |
| | | |
| | | .print-preview-content { |
| | | padding: 20px; |
| | | background-color: #f5f5f5; |
| | | min-height: 400px; |
| | | } |
| | | } |
| | | |
| | | .print-page { |
| | | width: 220mm; |
| | | height: 90mm; |
| | | padding: 10mm; |
| | | margin: 0 auto; |
| | | background: white; |
| | | box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); |
| | | margin-bottom: 10px; |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | .delivery-note { |
| | | width: 100%; |
| | | height: 100%; |
| | | font-family: "SimSun", serif; |
| | | font-size: 10px; |
| | | line-height: 1.2; |
| | | display: flex; |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .header { |
| | | .print-preview-container { |
| | | .print-preview-header { |
| | | padding: 15px; |
| | | border-bottom: 1px solid #e4e7ed; |
| | | text-align: center; |
| | | margin-bottom: 8px; |
| | | |
| | | .company-name { |
| | | font-size: 18px; |
| | | font-weight: bold; |
| | | margin-bottom: 4px; |
| | | } |
| | | |
| | | .document-title { |
| | | font-size: 16px; |
| | | font-weight: bold; |
| | | .el-button { |
| | | margin: 0 10px; |
| | | } |
| | | } |
| | | |
| | | .info-section { |
| | | margin-bottom: 8px; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | .print-preview-content { |
| | | padding: 20px; |
| | | background-color: #f5f5f5; |
| | | min-height: 400px; |
| | | } |
| | | } |
| | | |
| | | .info-row { |
| | | line-height: 20px; |
| | | .print-page { |
| | | width: 220mm; |
| | | height: 90mm; |
| | | padding: 10mm; |
| | | margin: 0 auto; |
| | | background: white; |
| | | box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); |
| | | margin-bottom: 10px; |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | .delivery-note { |
| | | width: 100%; |
| | | height: 100%; |
| | | font-family: "SimSun", serif; |
| | | font-size: 10px; |
| | | line-height: 1.2; |
| | | display: flex; |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .header { |
| | | text-align: center; |
| | | margin-bottom: 8px; |
| | | |
| | | .company-name { |
| | | font-size: 18px; |
| | | font-weight: bold; |
| | | margin-bottom: 4px; |
| | | } |
| | | |
| | | .document-title { |
| | | font-size: 16px; |
| | | font-weight: bold; |
| | | } |
| | | } |
| | | |
| | | .info-section { |
| | | margin-bottom: 8px; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | |
| | | .info-row { |
| | | line-height: 20px; |
| | | |
| | | .label { |
| | | font-weight: bold; |
| | | width: 60px; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .value { |
| | | margin-right: 20px; |
| | | min-width: 80px; |
| | | font-size: 14px; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .table-section { |
| | | margin-bottom: 4px; |
| | | flex: 1; |
| | | |
| | | .product-table { |
| | | width: 100%; |
| | | border-collapse: collapse; |
| | | border: 1px solid #000; |
| | | |
| | | th, |
| | | td { |
| | | border: 1px solid #000; |
| | | padding: 6px; |
| | | text-align: center; |
| | | font-size: 14px; |
| | | line-height: 1.4; |
| | | } |
| | | |
| | | th { |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .total-label { |
| | | text-align: right; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .total-value { |
| | | font-weight: bold; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .footer-section { |
| | | .footer-row { |
| | | display: flex; |
| | | margin-bottom: 3px; |
| | | line-height: 20px; |
| | | justify-content: space-between; |
| | | |
| | | .footer-item { |
| | | display: flex; |
| | | margin-right: 20px; |
| | | |
| | | .label { |
| | | font-weight: bold; |
| | | width: 60px; |
| | | width: 80px; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .value { |
| | | margin-right: 20px; |
| | | min-width: 80px; |
| | | font-size: 14px; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .table-section { |
| | | margin-bottom: 4px; |
| | | flex: 1; |
| | | |
| | | .product-table { |
| | | width: 100%; |
| | | border-collapse: collapse; |
| | | border: 1px solid #000; |
| | | |
| | | th, |
| | | td { |
| | | border: 1px solid #000; |
| | | padding: 6px; |
| | | text-align: center; |
| | | font-size: 14px; |
| | | line-height: 1.4; |
| | | } |
| | | |
| | | th { |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .total-label { |
| | | text-align: right; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .total-value { |
| | | font-weight: bold; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .footer-section { |
| | | .footer-row { |
| | | display: flex; |
| | | margin-bottom: 3px; |
| | | line-height: 20px; |
| | | justify-content: space-between; |
| | | |
| | | .footer-item { |
| | | display: flex; |
| | | margin-right: 20px; |
| | | |
| | | .label { |
| | | font-weight: bold; |
| | | width: 80px; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .value { |
| | | min-width: 80px; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | &.address-item { |
| | | .address-value { |
| | | min-width: 200px; |
| | | } |
| | | &.address-item { |
| | | .address-value { |
| | | min-width: 200px; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | @media print { |
| | | .app-container { |
| | | display: none; |
| | | } |
| | | |
| | | .print-page { |
| | | box-shadow: none; |
| | | margin: 0; |
| | | padding: 10mm; |
| | | padding-left: 20mm; |
| | | page-break-inside: avoid; |
| | | page-break-after: always; |
| | | } |
| | | .print-page:last-child { |
| | | page-break-after: avoid; |
| | | } |
| | | @media print { |
| | | .app-container { |
| | | display: none; |
| | | } |
| | | |
| | | .actions { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | margin-bottom: 10px; |
| | | .print-page { |
| | | box-shadow: none; |
| | | margin: 0; |
| | | padding: 10mm; |
| | | padding-left: 20mm; |
| | | page-break-inside: avoid; |
| | | page-break-after: always; |
| | | } |
| | | .print-page:last-child { |
| | | page-break-after: avoid; |
| | | } |
| | | } |
| | | |
| | | .actions { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | margin-bottom: 10px; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div> |
| | | <el-dialog v-model="isShow" |
| | | title="ç»å®ç©è设å¤" |
| | | width="600" |
| | | @close="closeModal"> |
| | | <el-form label-width="100px" |
| | | :model="formState" |
| | | ref="formRef"> |
| | | <el-form-item label="产ååç§°"> |
| | | <el-input v-model="props.record.productName" disabled /> |
| | | </el-form-item> |
| | | <el-form-item label="è§æ ¼åå·"> |
| | | <el-input v-model="props.record.model" disabled /> |
| | | </el-form-item> |
| | | <el-form-item label="æ¹å·"> |
| | | <el-input v-model="props.record.batchNo" disabled /> |
| | | </el-form-item> |
| | | <el-form-item label="ç©è设å¤" |
| | | prop="deviceIds" |
| | | :rules="[ |
| | | { |
| | | required: false, |
| | | message: 'è¯·éæ©ç©è设å¤', |
| | | trigger: 'change', |
| | | } |
| | | ]"> |
| | | <el-select v-model="formState.deviceIds" |
| | | multiple |
| | | filterable |
| | | placeholder="è¯·éæ©ç©è设å¤" |
| | | style="width: 100%"> |
| | | <el-option v-for="item in deviceOptions" |
| | | :key="item.id" |
| | | :label="`${item.deviceName} (${item.deviceModel})`" |
| | | :value="item.id"> |
| | | <span style="float: left">{{ item.deviceName }}</span> |
| | | <span style="float: right; color: #8492a6; font-size: 13px">{{ item.deviceModel }}</span> |
| | | </el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="handleSubmit" :loading="submitLoading">确认</el-button> |
| | | <el-button @click="closeModal">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, computed, onMounted, getCurrentInstance } from "vue"; |
| | | import { getLedgerPage } from "@/api/equipmentManagement/ledger.js"; |
| | | import { createStockInventory } from "@/api/inventoryManagement/stockInventory.js"; |
| | | |
| | | const props = defineProps({ |
| | | visible: { |
| | | type: Boolean, |
| | | required: true, |
| | | }, |
| | | record: { |
| | | type: Object, |
| | | default: () => ({}), |
| | | }, |
| | | }); |
| | | |
| | | const emit = defineEmits(["update:visible", "completed"]); |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | | const isShow = computed({ |
| | | get() { |
| | | return props.visible; |
| | | }, |
| | | set(val) { |
| | | emit("update:visible", val); |
| | | }, |
| | | }); |
| | | |
| | | const formRef = ref(null); |
| | | const submitLoading = ref(false); |
| | | const deviceOptions = ref([]); |
| | | |
| | | const formState = ref({ |
| | | deviceIds: [], |
| | | }); |
| | | |
| | | // è·åç©è设å¤å表 |
| | | const getDeviceOptions = async () => { |
| | | try { |
| | | const res = await getLedgerPage({ |
| | | isIotDevice: 1, |
| | | page: 1, |
| | | size: 999, |
| | | }); |
| | | if (res.data && res.data.records) { |
| | | deviceOptions.value = res.data.records; |
| | | } |
| | | } catch (error) { |
| | | console.error("è·åç©è设å¤å表失败:", error); |
| | | proxy.$modal.msgError("è·åç©è设å¤å表失败"); |
| | | } |
| | | }; |
| | | |
| | | // åå§åå·²ç»å®è®¾å¤ |
| | | const initSelectedDevices = () => { |
| | | if (props.record.warehouse) { |
| | | // warehouse åæ®µåå¨çæ¯éå·åéç设å¤ID |
| | | const deviceIds = props.record.warehouse.split(",").map(id => Number(id.trim())).filter(id => !isNaN(id)); |
| | | formState.value.deviceIds = deviceIds; |
| | | } |
| | | }; |
| | | |
| | | const closeModal = () => { |
| | | formState.value.deviceIds = []; |
| | | isShow.value = false; |
| | | }; |
| | | |
| | | const handleSubmit = () => { |
| | | formRef.value.validate(valid => { |
| | | if (valid) { |
| | | submitLoading.value = true; |
| | | // å°è®¾å¤IDæ°ç»è½¬æ¢ä¸ºéå·åéçå符串 |
| | | const warehouse = formState.value.deviceIds.join(","); |
| | | // 使ç¨åºåæ°å¢/ç¼è¾æ¥å£ï¼warehouse åæ®µä¼ 设å¤ID |
| | | const submitData = { |
| | | id: props.record.id, |
| | | productModelId: props.record.productModelId, |
| | | batchNo: props.record.batchNo, |
| | | qualitity: props.record.qualitity, |
| | | warehouse: warehouse, |
| | | }; |
| | | createStockInventory(submitData) |
| | | .then(res => { |
| | | submitLoading.value = false; |
| | | proxy.$modal.msgSuccess("ç»å®æå"); |
| | | closeModal(); |
| | | emit("completed"); |
| | | }) |
| | | .catch(() => { |
| | | submitLoading.value = false; |
| | | }); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getDeviceOptions(); |
| | | initSelectedDevices(); |
| | | }); |
| | | </script> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div> |
| | | <el-dialog v-model="isShow" |
| | | title="ç©è设å¤å®æ¶æ°é" |
| | | width="800" |
| | | @close="closeModal"> |
| | | <div v-if="loading" v-loading="loading" element-loading-text="å è½½ä¸..." style="min-height: 200px;"></div> |
| | | <div v-else-if="!hasDevices" class="empty-state"> |
| | | <el-empty description="ææ ç»å®çç©è设å¤"> |
| | | <el-button type="primary" @click="closeModal">å»ç»å®</el-button> |
| | | </el-empty> |
| | | </div> |
| | | <div v-else> |
| | | <div class="device-header"> |
| | | <div class="refresh-switch"> |
| | | <el-switch v-model="autoRefresh" active-text="èªå¨å·æ°(30s)" /> |
| | | </div> |
| | | <el-button type="primary" size="small" @click="fetchData" :loading="loading"> |
| | | <el-icon><Refresh /></el-icon>å·æ° |
| | | </el-button> |
| | | </div> |
| | | <div class="devices-container"> |
| | | <el-card v-for="device in deviceData.devices" :key="device.deviceId" class="device-card"> |
| | | <template #header> |
| | | <div class="device-header-info"> |
| | | <div class="device-title"> |
| | | <span class="device-name">{{ device.deviceName }}</span> |
| | | <el-tag size="small" type="info">{{ device.deviceModel }}</el-tag> |
| | | </div> |
| | | <div class="device-status"> |
| | | <span class="status-dot" :class="getStatusClass(device.status)"></span> |
| | | <span :class="getStatusTextClass(device.status)">{{ getStatusLabel(device.status) }}</span> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | <div v-if="device.status === 'å¨çº¿'" class="device-data"> |
| | | <el-row :gutter="10"> |
| | | <el-col :span="8" v-if="device.temperature"> |
| | | <div class="data-item"> |
| | | <el-icon class="data-icon"><Sunny /></el-icon> |
| | | <div class="data-info"> |
| | | <div class="data-label">温度</div> |
| | | <div class="data-value">{{ device.temperature }}</div> |
| | | </div> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="8" v-if="device.humidity"> |
| | | <div class="data-item"> |
| | | <el-icon class="data-icon"><Drizzling /></el-icon> |
| | | <div class="data-info"> |
| | | <div class="data-label">湿度</div> |
| | | <div class="data-value">{{ device.humidity }}</div> |
| | | </div> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="8" v-if="device.co2"> |
| | | <div class="data-item"> |
| | | <el-icon class="data-icon"><WindPower /></el-icon> |
| | | <div class="data-info"> |
| | | <div class="data-label">CO2</div> |
| | | <div class="data-value">{{ device.co2 }}</div> |
| | | </div> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="8" v-if="device.light"> |
| | | <div class="data-item"> |
| | | <el-icon class="data-icon"><Sunrise /></el-icon> |
| | | <div class="data-info"> |
| | | <div class="data-label">å
ç
§</div> |
| | | <div class="data-value">{{ device.light }}</div> |
| | | </div> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="8" v-if="device.battery"> |
| | | <div class="data-item"> |
| | | <el-icon class="data-icon"><Lightning /></el-icon> |
| | | <div class="data-info"> |
| | | <div class="data-label">çµé</div> |
| | | <div class="data-value">{{ device.battery }}</div> |
| | | </div> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | <div v-else class="device-offline"> |
| | | <el-alert :title="device.statusMessage || (device.status === 'error' ? '设å¤å¼å¸¸' : '设å¤ç¦»çº¿')" :type="device.status === 'error' ? 'error' : 'warning'" :closable="false" show-icon /> |
| | | </div> |
| | | </el-card> |
| | | </div> |
| | | </div> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="closeModal">å
³é</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, computed, onMounted, onUnmounted, watch } from "vue"; |
| | | import { batchGetIotRealtimeData } from "@/api/inventoryManagement/stockInventory.js"; |
| | | import { Refresh, Sunny, Drizzling, WindPower, Sunrise, Lightning } from "@element-plus/icons-vue"; |
| | | |
| | | const props = defineProps({ |
| | | visible: { |
| | | type: Boolean, |
| | | required: true, |
| | | }, |
| | | record: { |
| | | type: Object, |
| | | default: () => ({}), |
| | | }, |
| | | }); |
| | | |
| | | const emit = defineEmits(["update:visible"]); |
| | | |
| | | const isShow = computed({ |
| | | get() { |
| | | return props.visible; |
| | | }, |
| | | set(val) { |
| | | emit("update:visible", val); |
| | | }, |
| | | }); |
| | | |
| | | const loading = ref(false); |
| | | const deviceData = ref({ |
| | | inventoryId: null, |
| | | iotDeviceIds: "", |
| | | devices: [], |
| | | }); |
| | | const autoRefresh = ref(false); |
| | | let refreshTimer = null; |
| | | |
| | | const hasDevices = computed(() => { |
| | | return deviceData.value.devices && deviceData.value.devices.length > 0; |
| | | }); |
| | | |
| | | const getStatusClass = (status) => { |
| | | switch (status) { |
| | | case "å¨çº¿": |
| | | return "status-online"; |
| | | case "offline": |
| | | return "status-offline"; |
| | | case "error": |
| | | return "status-error"; |
| | | default: |
| | | return "status-offline"; |
| | | } |
| | | }; |
| | | |
| | | const getStatusTextClass = (status) => { |
| | | switch (status) { |
| | | case "å¨çº¿": |
| | | return "text-online"; |
| | | case "offline": |
| | | return "text-offline"; |
| | | case "error": |
| | | return "text-error"; |
| | | default: |
| | | return "text-offline"; |
| | | } |
| | | }; |
| | | |
| | | const getStatusLabel = (status) => { |
| | | switch (status) { |
| | | case "å¨çº¿": |
| | | return "å¨çº¿"; |
| | | case "offline": |
| | | return "离线"; |
| | | case "error": |
| | | return "å¼å¸¸"; |
| | | default: |
| | | return "离线"; |
| | | } |
| | | }; |
| | | |
| | | const fetchData = async () => { |
| | | const deviceIdsStr = props.record?.warehouse; |
| | | console.log("fetchData called, warehouse:", deviceIdsStr); |
| | | if (!deviceIdsStr) { |
| | | console.warn("warehouse is empty, skip fetch"); |
| | | deviceData.value = { inventoryId: null, iotDeviceIds: "", devices: [] }; |
| | | return; |
| | | } |
| | | loading.value = true; |
| | | try { |
| | | const deviceIds = deviceIdsStr.split(",").map(id => id.trim()).filter(id => id); |
| | | const res = await batchGetIotRealtimeData(deviceIds); |
| | | |
| | | if (res.code === 200 && res.data) { |
| | | deviceData.value = res.data; |
| | | } else { |
| | | deviceData.value = { inventoryId: null, iotDeviceIds: deviceIdsStr, devices: [] }; |
| | | } |
| | | } catch (error) { |
| | | console.error("è·åç©èè®¾å¤æ°æ®å¤±è´¥:", error); |
| | | } finally { |
| | | loading.value = false; |
| | | } |
| | | }; |
| | | |
| | | const closeModal = () => { |
| | | autoRefresh.value = false; |
| | | isShow.value = false; |
| | | }; |
| | | |
| | | // çå¬å¼¹çªæ¾ç¤ºç¶æï¼æ¯æ¬¡æå¼æ¶è·åæ°æ® |
| | | watch(() => props.visible, (val) => { |
| | | console.log("visible changed:", val, "record:", props.record); |
| | | if (val) { |
| | | fetchData(); |
| | | } else { |
| | | // å
³éæ¶åæ¢èªå¨å·æ° |
| | | autoRefresh.value = false; |
| | | } |
| | | }, { immediate: true }); |
| | | |
| | | // èªå¨å·æ° |
| | | watch(autoRefresh, (val) => { |
| | | if (val) { |
| | | refreshTimer = setInterval(() => { |
| | | fetchData(); |
| | | }, 30000); |
| | | } else { |
| | | if (refreshTimer) { |
| | | clearInterval(refreshTimer); |
| | | refreshTimer = null; |
| | | } |
| | | } |
| | | }); |
| | | |
| | | onUnmounted(() => { |
| | | if (refreshTimer) { |
| | | clearInterval(refreshTimer); |
| | | } |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .empty-state { |
| | | padding: 40px 0; |
| | | } |
| | | |
| | | .device-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .refresh-switch { |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .devices-container { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 15px; |
| | | } |
| | | |
| | | .device-card { |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .device-header-info { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | } |
| | | |
| | | .device-title { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .device-name { |
| | | font-weight: bold; |
| | | font-size: 16px; |
| | | } |
| | | |
| | | .device-status { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 5px; |
| | | } |
| | | |
| | | .status-dot { |
| | | width: 8px; |
| | | height: 8px; |
| | | border-radius: 50%; |
| | | display: inline-block; |
| | | } |
| | | |
| | | .status-online { |
| | | background-color: #67c23a; |
| | | } |
| | | |
| | | .status-offline { |
| | | background-color: #909399; |
| | | } |
| | | |
| | | .status-error { |
| | | background-color: #f56c6c; |
| | | } |
| | | |
| | | .text-online { |
| | | color: #67c23a; |
| | | } |
| | | |
| | | .text-offline { |
| | | color: #909399; |
| | | } |
| | | |
| | | .text-error { |
| | | color: #f56c6c; |
| | | } |
| | | |
| | | .device-data { |
| | | padding: 10px 0; |
| | | } |
| | | |
| | | .data-item { |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 10px; |
| | | background-color: #f5f7fa; |
| | | border-radius: 4px; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .data-icon { |
| | | font-size: 24px; |
| | | color: #409eff; |
| | | margin-right: 10px; |
| | | } |
| | | |
| | | .data-info { |
| | | flex: 1; |
| | | } |
| | | |
| | | .data-label { |
| | | font-size: 12px; |
| | | color: #909399; |
| | | margin-bottom: 2px; |
| | | } |
| | | |
| | | .data-value { |
| | | font-size: 16px; |
| | | font-weight: bold; |
| | | color: #303133; |
| | | } |
| | | |
| | | .device-offline { |
| | | padding: 20px 0; |
| | | } |
| | | </style> |
| | |
| | | handleSubmit, |
| | | isShow, |
| | | }); |
| | | </script> |
| | | </script> |
| | |
| | | <el-table-column label="æ¹å·" |
| | | prop="batchNo" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="ç©è设å¤" |
| | | prop="warehouse" |
| | | show-overflow-tooltip |
| | | min-width="150"> |
| | | <template #default="scope"> |
| | | {{ getDeviceNames(scope.row.warehouse) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="åæ ¼åºåæ°é" |
| | | prop="qualifiedQuantity" |
| | | show-overflow-tooltip /> |
| | |
| | | show-overflow-tooltip /> |
| | | <el-table-column fixed="right" |
| | | label="æä½" |
| | | min-width="80" |
| | | width="210" |
| | | align="center"> |
| | | <template #default="scope"> |
| | | <el-button link |
| | | type="primary" |
| | | @click="showDetailModal(scope.row)">详æ
</el-button> |
| | | <el-button link |
| | | type="success" |
| | | @click="showBindDeviceModal(scope.row)">ç»å®è®¾å¤</el-button> |
| | | <el-button link |
| | | type="info" |
| | | @click="showIotDataModal(scope.row)">æ¥çæ°é</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | |
| | | :operation-type="operationType" |
| | | :type="record.stockType" |
| | | @completed="handleQuery" /> |
| | | <!-- ç»å®ç©èè®¾å¤ --> |
| | | <bind-device-dialog v-if="isShowBindDeviceModal" |
| | | v-model:visible="isShowBindDeviceModal" |
| | | :record="record" |
| | | @completed="handleQuery" /> |
| | | <!-- æ¥çç©èè®¾å¤æ°é --> |
| | | <iot-data-dialog v-if="isShowIotDataModal" |
| | | v-model:visible="isShowIotDataModal" |
| | | :record="record" /> |
| | | </div> |
| | | </template> |
| | | |
| | |
| | | import { ref, reactive, toRefs, onMounted, getCurrentInstance } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import { getStockInventoryListPageCombined } from "@/api/inventoryManagement/stockInventory.js"; |
| | | import { getLedgerPage } from "@/api/equipmentManagement/ledger.js"; |
| | | const props = defineProps({ |
| | | productId: { |
| | | type: Number, |
| | |
| | | const BatchNoQtyDetail = defineAsyncComponent(() => |
| | | import("@/views/inventoryManagement/stockManagement/BatchNoQtyDetail.vue") |
| | | ); |
| | | const BindDeviceDialog = defineAsyncComponent(() => |
| | | import("@/views/inventoryManagement/stockManagement/BindDeviceDialog.vue") |
| | | ); |
| | | const IotDataDialog = defineAsyncComponent(() => |
| | | import("@/views/inventoryManagement/stockManagement/IotDataDialog.vue") |
| | | ); |
| | | const { proxy } = getCurrentInstance(); |
| | | const tableData = ref([]); |
| | | const selectedRows = ref([]); |
| | |
| | | const operationType = ref("frozen"); |
| | | // æ¯å¦æ¾ç¤ºå¯¼å
¥å¼¹æ¡ |
| | | const isShowImportModal = ref(false); |
| | | // æ¯å¦æ¾ç¤ºç»å®è®¾å¤å¼¹æ¡ |
| | | const isShowBindDeviceModal = ref(false); |
| | | // æ¯å¦æ¾ç¤ºç©èæ°éå¼¹æ¡ |
| | | const isShowIotDataModal = ref(false); |
| | | // 设å¤å表ï¼ç¨äºæ¾ç¤ºè®¾å¤åç§°ï¼ |
| | | const deviceList = ref([]); |
| | | const data = reactive({ |
| | | searchForm: { |
| | | productName: "", |
| | |
| | | operationType.value = "thaw"; |
| | | }; |
| | | |
| | | // ç¹å»ç»å®è®¾å¤ |
| | | const showBindDeviceModal = row => { |
| | | record.value = row; |
| | | isShowBindDeviceModal.value = true; |
| | | }; |
| | | |
| | | // ç¹å»æ¥çæ°é |
| | | const showIotDataModal = row => { |
| | | record.value = row; |
| | | isShowIotDataModal.value = true; |
| | | }; |
| | | |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | | const handleSelectionChange = selection => { |
| | | // è¿æ»¤æåæ°æ® |
| | |
| | | }); |
| | | }; |
| | | |
| | | // è·å设å¤å表 |
| | | const getDeviceList = async () => { |
| | | try { |
| | | const res = await getLedgerPage({ |
| | | isIotDevice: 1, |
| | | page: 1, |
| | | size: 999, |
| | | }); |
| | | if (res.data && res.data.records) { |
| | | deviceList.value = res.data.records; |
| | | } |
| | | } catch (error) { |
| | | console.error("è·å设å¤å表失败:", error); |
| | | } |
| | | }; |
| | | |
| | | // æ ¹æ®è®¾å¤IDè·å设å¤åç§° |
| | | const getDeviceNames = (warehouse) => { |
| | | if (!warehouse) return "-"; |
| | | const deviceIds = warehouse.split(",").map(id => id.trim()); |
| | | const names = deviceIds |
| | | .map(id => { |
| | | const device = deviceList.value.find(d => String(d.id) === id); |
| | | return device ? device.deviceName : id; |
| | | }) |
| | | .filter(name => name); |
| | | return names.length > 0 ? names.join(", ") : warehouse; |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getList(); |
| | | getDeviceList(); |
| | | }); |
| | | </script> |
| | | |
| | |
| | | <el-input-number v-model="formState.qualitity" :step="1" :min="1" :max="maxQuality" style="width: 100%" /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="åºåºæ¹æ¬¡" prop="outboundBatches"> |
| | | <el-input |
| | | v-model="formState.outboundBatches" |
| | | placeholder="ç空èªå¨çæ" |
| | | maxlength="100" |
| | | show-word-limit |
| | | /> |
| | | <div class="form-tip">ä¸å¡«åèªå¨çæï¼æ ¼å¼ï¼CKå¹´ææ¥-åºå·</div> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="夿³¨" prop="remark"> |
| | | <el-input v-model="formState.remark" type="textarea" /> |
| | | </el-form-item> |
| | |
| | | model: "", |
| | | unit: "", |
| | | qualitity: 0, |
| | | outboundBatches: "", |
| | | remark: '', |
| | | }); |
| | | |
| | |
| | | productModelId: undefined, |
| | | productName: "", |
| | | productModelName: "", |
| | | outboundBatches: "", |
| | | description: '', |
| | | }; |
| | | isShow.value = false; |
| | |
| | | handleSubmit, |
| | | isShow, |
| | | }); |
| | | </script> |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .form-tip { |
| | | font-size: 12px; |
| | | color: #909399; |
| | | margin-top: 4px; |
| | | } |
| | | </style> |
| | |
| | | purchaseContractNumber: row.purchaseContractNumber || "", |
| | | quotationNo: row.quotationNo || "", |
| | | shippingNo: row.shippingNo || "", |
| | | warehouse: row.warehouse || "", |
| | | }; |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | /** ç»è£
å®¡æ¹æäº¤ DTO */ |
| | | export function buildApproveInstanceDto(row, uiResult, comment) { |
| | | export function buildApproveInstanceDto(row, uiResult, comment, warehouse, extraData = {}) { |
| | | const opinion = (comment || "").trim(); |
| | | return { |
| | | id: row?.id, |
| | | approveAction: mapApproveActionToApi(uiResult), |
| | | approveComment: opinion || (uiResult === "approved" ? "åæ" : ""), |
| | | warehouse: warehouse || row?.warehouse || "", |
| | | ...extraData, |
| | | }; |
| | | } |
| | | |
| | |
| | | export function initTemplateAttachmentsFromSnapshot(templateSnapshot) { |
| | | const list = templateSnapshot?.storageBlobDTOs; |
| | | return list?.length ? JSON.parse(JSON.stringify(list)) : []; |
| | | } |
| | | } |
| | |
| | | <div class="approve-detail-panel"> |
| | | <div class="detail-block"> |
| | | <div class="detail-block-title">åºæ¬ä¿¡æ¯</div> |
| | | <el-descriptions :column="2" |
| | | border> |
| | | <el-descriptions-item label="ä¸å¡åå·">{{ row.bizId || row.id || "â" }}</el-descriptions-item> |
| | | <el-descriptions :column="2" border> |
| | | <el-descriptions-item label="ä¸å¡åå·">{{ |
| | | row.bizId || row.id || "â" |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="审æ¹ç¶æ"> |
| | | <el-tag :type="approvalStatusTagType(row.approvalStatus)" |
| | | size="small" |
| | | effect="plain"> |
| | | <el-tag |
| | | :type="approvalStatusTagType(row.approvalStatus)" |
| | | size="small" |
| | | effect="plain" |
| | | > |
| | | {{ approvalStatusLabel(row.approvalStatus) }} |
| | | </el-tag> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="审æ¹ç±»å"> |
| | | <span class="approve-type-cell" |
| | | :style="approvalTypeStyle(row.approvalType)"> |
| | | <span |
| | | class="approve-type-cell" |
| | | :style="approvalTypeStyle(row.approvalType)" |
| | | > |
| | | {{ approvalTypeLabel(row.approvalType) }} |
| | | </span> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="ç³è¯·äººç¼å·">{{ row.applicantNo || "â" }}</el-descriptions-item> |
| | | <el-descriptions-item label="ç³è¯·äººåç§°">{{ row.applicantName || "â" }}</el-descriptions-item> |
| | | <el-descriptions-item label="ç³è¯·æè¦">{{ row.summary || "â" }}</el-descriptions-item> |
| | | <el-descriptions-item v-if="row.rejectReason" |
| | | label="驳ååå " |
| | | :span="2"> |
| | | <el-descriptions-item label="ç³è¯·äººç¼å·">{{ |
| | | row.applicantNo || "â" |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="ç³è¯·äººåç§°">{{ |
| | | row.applicantName || "â" |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="ç³è¯·æè¦">{{ |
| | | row.summary || "â" |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item |
| | | v-if="row.rejectReason" |
| | | label="驳ååå " |
| | | :span="2" |
| | | > |
| | | <span class="reject-text">{{ row.rejectReason }}</span> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="å建æ¶é´" |
| | | :span="2"> |
| | | <el-descriptions-item label="å建æ¶é´" :span="2"> |
| | | {{ formatDisplayTime(row.createTime) }} |
| | | </el-descriptions-item> |
| | | </el-descriptions> |
| | |
| | | <div class="detail-block"> |
| | | <div class="detail-block-title">å¡«æ¥å
容</div> |
| | | <!-- é»è®¤è¡¨åå±ç¤º --> |
| | | <FormPayloadFields v-if="!isSpecialApprovalType" |
| | | :fields="formResolved.fields" |
| | | :form-payload="formResolved.formPayload" |
| | | readonly /> |
| | | |
| | | <FormPayloadFields |
| | | v-if="!isSpecialApprovalType" |
| | | :fields="formResolved.fields" |
| | | :form-payload="formResolved.formPayload" |
| | | readonly |
| | | /> |
| | | |
| | | <!-- å货审æ¹è¯¦æ
--> |
| | | <template v-else-if="row.businessType === 7"> |
| | | <div v-if="detailData.shippingInfo" class="shipment-detail"> |
| | | <el-divider content-position="left">å货详æ
</el-divider> |
| | | <el-descriptions :column="2" border> |
| | | <el-descriptions-item label="éå®è®¢å">{{ detailData.shippingInfo.salesContractNo || "--" }}</el-descriptions-item> |
| | | <el-descriptions-item label="å货订åå·">{{ detailData.shippingInfo.shippingNo || "--" }}</el-descriptions-item> |
| | | <el-descriptions-item label="客æ·åç§°">{{ detailData.shippingInfo.customerName || "--" }}</el-descriptions-item> |
| | | <el-descriptions-item label="åè´§ç±»å">{{ detailData.shippingInfo.type || "--" }}</el-descriptions-item> |
| | | <el-descriptions-item label="åè´§æ¥æ">{{ detailData.shippingInfo.shippingDateDate || "--" }}</el-descriptions-item> |
| | | <el-descriptions-item label="å®¡æ ¸ç¶æ">{{ detailData.shippingInfo.status || "--" }}</el-descriptions-item> |
| | | <el-descriptions-item label="å货车çå·">{{ detailData.shippingInfo.shippingCarNumber || "--" }}</el-descriptions-item> |
| | | <el-descriptions-item label="å¿«éå
¬å¸">{{ detailData.shippingInfo.expressCompany || "--" }}</el-descriptions-item> |
| | | <el-descriptions-item label="å¿«éåå·" :span="2">{{ detailData.shippingInfo.expressNumber || "--" }}</el-descriptions-item> |
| | | <el-descriptions-item label="éå®è®¢å">{{ |
| | | detailData.shippingInfo.salesContractNo || "--" |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="åºåºæ¹å·">{{ |
| | | detailData.shippingInfo.outboundBatches || "--" |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="客æ·åç§°">{{ |
| | | detailData.shippingInfo.customerName || "--" |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="åè´§ç±»å">{{ |
| | | detailData.shippingInfo.type || "--" |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="åè´§æ¥æ">{{ |
| | | detailData.shippingInfo.shippingDateDate || "--" |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="å®¡æ ¸ç¶æ">{{ |
| | | detailData.shippingInfo.status || "--" |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="å货车çå·">{{ |
| | | detailData.shippingInfo.shippingCarNumber || "--" |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="å¿«éå
¬å¸">{{ |
| | | detailData.shippingInfo.expressCompany || "--" |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="å¿«éåå·" :span="2">{{ |
| | | detailData.shippingInfo.expressNumber || "--" |
| | | }}</el-descriptions-item> |
| | | </el-descriptions> |
| | | <div v-if="detailData.shippingProductDetailDtoList.length" style="margin-top: 20px;"> |
| | | <div |
| | | v-if="detailData.shippingProductDetailDtoList.length" |
| | | style="margin-top: 20px" |
| | | > |
| | | <h4>产åæç»</h4> |
| | | <el-table :data="detailData.shippingProductDetailDtoList" |
| | | border |
| | | size="small" |
| | | style="width: 100%"> |
| | | <el-table-column label="æ¹å·" prop="batchNo" min-width="160" show-overflow-tooltip /> |
| | | <el-table-column label="产ååç§°" prop="productName" min-width="160" show-overflow-tooltip /> |
| | | <el-table-column label="è§æ ¼åå·" prop="specificationModel" min-width="160" show-overflow-tooltip /> |
| | | <el-table-column label="åè´§æ°é" prop="deliveryQuantity" min-width="120" align="center" /> |
| | | <el-table |
| | | :data="detailData.shippingProductDetailDtoList" |
| | | border |
| | | size="small" |
| | | style="width: 100%" |
| | | > |
| | | <el-table-column |
| | | label="æ¹å·" |
| | | prop="batchNo" |
| | | min-width="160" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | label="产ååç§°" |
| | | prop="productName" |
| | | min-width="160" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | label="è§æ ¼åå·" |
| | | prop="specificationModel" |
| | | min-width="160" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | label="åè´§æ°é" |
| | | prop="deliveryQuantity" |
| | | min-width="120" |
| | | align="center" |
| | | /> |
| | | </el-table> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | |
| | | <!-- éè´å®¡æ¹è¯¦æ
--> |
| | | <template v-else-if="row.businessType === 5"> |
| | | <div v-if="detailData" class="procurement-detail"> |
| | | <el-divider content-position="left">éè´è¯¦æ
</el-divider> |
| | | <el-descriptions :column="2" border> |
| | | <el-descriptions-item label="éè´ååå·">{{ detailData.purchaseContractNumber || "--" }}</el-descriptions-item> |
| | | <el-descriptions-item label="ä¾åºååç§°">{{ detailData.supplierName || "--" }}</el-descriptions-item> |
| | | <el-descriptions-item label="项ç®åç§°">{{ detailData.projectName || "--" }}</el-descriptions-item> |
| | | <el-descriptions-item label="éå®ååå·">{{ detailData.salesContractNo || "--" }}</el-descriptions-item> |
| | | <el-descriptions-item label="ç¾è®¢æ¥æ">{{ detailData.executionDate || "--" }}</el-descriptions-item> |
| | | <el-descriptions-item label="å½å
¥æ¥æ">{{ detailData.entryDate || "--" }}</el-descriptions-item> |
| | | <el-descriptions-item label="仿¬¾æ¹å¼">{{ detailData.paymentMethod || "--" }}</el-descriptions-item> |
| | | <el-descriptions-item label="éè´ååå·">{{ |
| | | detailData.purchaseContractNumber || "--" |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="ä¾åºååç§°">{{ |
| | | detailData.supplierName || "--" |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="项ç®åç§°">{{ |
| | | detailData.projectName || "--" |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="éå®ååå·">{{ |
| | | detailData.salesContractNo || "--" |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="ç¾è®¢æ¥æ">{{ |
| | | detailData.executionDate || "--" |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="å½å
¥æ¥æ">{{ |
| | | detailData.entryDate || "--" |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="仿¬¾æ¹å¼">{{ |
| | | detailData.paymentMethod || "--" |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="ååéé¢" :span="2"> |
| | | <span style="font-size: 18px; color: #e6a23c; font-weight: bold;"> |
| | | <span style="font-size: 18px; color: #e6a23c; font-weight: bold"> |
| | | ¥{{ Number(detailData.contractAmount ?? 0).toFixed(2) }} |
| | | </span> |
| | | </el-descriptions-item> |
| | | </el-descriptions> |
| | | <div v-if="detailData.productData.length" style="margin-top: 20px;"> |
| | | <div v-if="detailData.productData.length" style="margin-top: 20px"> |
| | | <h4>产åæç»</h4> |
| | | <el-table :data="detailData.productData" |
| | | border |
| | | style="width: 100%"> |
| | | <el-table :data="detailData.productData" border style="width: 100%"> |
| | | <el-table-column prop="productCategory" label="产ååç§°" /> |
| | | <el-table-column prop="specificationModel" label="è§æ ¼åå·" /> |
| | | <el-table-column prop="unit" label="åä½" /> |
| | | <el-table-column prop="quantity" label="æ°é" /> |
| | | <el-table-column prop="taxInclusiveUnitPrice" label="å«ç¨åä»·"> |
| | | <template #default="scope">Â¥{{ Number(scope.row.taxInclusiveUnitPrice ?? 0).toFixed(2) }}</template> |
| | | <template #default="scope" |
| | | >Â¥{{ |
| | | Number(scope.row.taxInclusiveUnitPrice ?? 0).toFixed(2) |
| | | }}</template |
| | | > |
| | | </el-table-column> |
| | | <el-table-column prop="taxInclusiveTotalPrice" label="å«ç¨æ»ä»·"> |
| | | <template #default="scope">Â¥{{ Number(scope.row.taxInclusiveTotalPrice ?? 0).toFixed(2) }}</template> |
| | | <template #default="scope" |
| | | >Â¥{{ |
| | | Number(scope.row.taxInclusiveTotalPrice ?? 0).toFixed(2) |
| | | }}</template |
| | | > |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | |
| | | <!-- æ¥ä»·å®¡æ¹è¯¦æ
--> |
| | | <template v-else-if="row.businessType === 6"> |
| | | <div v-if="detailData" class="quotation-detail"> |
| | | <el-divider content-position="left">æ¥ä»·è¯¦æ
</el-divider> |
| | | <el-descriptions :column="2" border> |
| | | <el-descriptions-item label="æ¥ä»·åå·">{{ detailData.quotationNo || "--" }}</el-descriptions-item> |
| | | <el-descriptions-item label="客æ·åç§°">{{ detailData.customer || "--" }}</el-descriptions-item> |
| | | <el-descriptions-item label="ä¸å¡å">{{ detailData.salesperson || "--" }}</el-descriptions-item> |
| | | <el-descriptions-item label="æ¥ä»·æ¥æ">{{ detailData.quotationDate || "--" }}</el-descriptions-item> |
| | | <el-descriptions-item label="æææè³">{{ detailData.validDate || "--" }}</el-descriptions-item> |
| | | <el-descriptions-item label="仿¬¾æ¹å¼">{{ detailData.paymentMethod || "--" }}</el-descriptions-item> |
| | | <el-descriptions-item label="æ¥ä»·åå·">{{ |
| | | detailData.quotationNo || "--" |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="客æ·åç§°">{{ |
| | | detailData.customer || "--" |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="ä¸å¡å">{{ |
| | | detailData.salesperson || "--" |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="æ¥ä»·æ¥æ">{{ |
| | | detailData.quotationDate || "--" |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="æææè³">{{ |
| | | detailData.validDate || "--" |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="仿¬¾æ¹å¼">{{ |
| | | detailData.paymentMethod || "--" |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="æ¥ä»·æ»é¢" :span="2"> |
| | | <span style="font-size: 18px; color: #e6a23c; font-weight: bold;"> |
| | | <span style="font-size: 18px; color: #e6a23c; font-weight: bold"> |
| | | ¥{{ Number(detailData.totalAmount ?? 0).toFixed(2) }} |
| | | </span> |
| | | </el-descriptions-item> |
| | | </el-descriptions> |
| | | <div v-if="detailData.products?.length" style="margin-top: 20px;"> |
| | | <div v-if="detailData.products?.length" style="margin-top: 20px"> |
| | | <h4>产åæç»</h4> |
| | | <el-table :data="detailData.products" |
| | | border |
| | | style="width: 100%"> |
| | | <el-table :data="detailData.products" border style="width: 100%"> |
| | | <el-table-column prop="product" label="产ååç§°" /> |
| | | <el-table-column prop="specification" label="è§æ ¼åå·" /> |
| | | <el-table-column prop="unit" label="åä½" /> |
| | | <el-table-column prop="unitPrice" label="åä»·"> |
| | | <template #default="scope">Â¥{{ Number(scope.row.unitPrice ?? 0).toFixed(2) }}</template> |
| | | <template #default="scope" |
| | | >Â¥{{ Number(scope.row.unitPrice ?? 0).toFixed(2) }}</template |
| | | > |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | <div v-if="detailData.remark" style="margin-top: 20px;"> |
| | | <div v-if="detailData.remark" style="margin-top: 20px"> |
| | | <h4>夿³¨</h4> |
| | | <p>{{ detailData.remark }}</p> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | </div> |
| | | <div v-if="attachmentList.length" |
| | | class="detail-block"> |
| | | <div v-if="attachmentList.length" class="detail-block"> |
| | | <div class="detail-block-title">éä»¶å表</div> |
| | | <div class="attachment-list"> |
| | | <div v-for="file in attachmentList" |
| | | :key="file.id" |
| | | class="attachment-item"> |
| | | <div |
| | | v-for="file in attachmentList" |
| | | :key="file.id" |
| | | class="attachment-item" |
| | | > |
| | | <el-icon class="file-icon"> |
| | | <Paperclip /> |
| | | </el-icon> |
| | | <span class="file-name" |
| | | :title="file.name || file.originalFilename"> |
| | | <span class="file-name" :title="file.name || file.originalFilename"> |
| | | {{ file.name || file.originalFilename }} |
| | | </span> |
| | | <div class="file-actions"> |
| | | <el-link v-if="file.previewURL || file.url" |
| | | type="primary" |
| | | :underline="false" |
| | | @click="openFile(file.previewURL || file.url)">é¢è§</el-link> |
| | | <el-divider v-if="(file.previewURL || file.url) && file.downloadURL" |
| | | direction="vertical" /> |
| | | <el-link v-if="file.downloadURL" |
| | | type="primary" |
| | | :underline="false" |
| | | @click="openFile(file.downloadURL)">ä¸è½½</el-link> |
| | | <el-link |
| | | v-if="file.previewURL || file.url" |
| | | type="primary" |
| | | :underline="false" |
| | | @click="openFile(file.previewURL || file.url)" |
| | | >é¢è§</el-link |
| | | > |
| | | <el-divider |
| | | v-if="(file.previewURL || file.url) && file.downloadURL" |
| | | direction="vertical" |
| | | /> |
| | | <el-link |
| | | v-if="file.downloadURL" |
| | | type="primary" |
| | | :underline="false" |
| | | @click="openFile(file.downloadURL)" |
| | | >ä¸è½½</el-link |
| | | > |
| | | </div> |
| | | </div> |
| | | </div> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { computed } from "vue"; |
| | | import { Paperclip } from "@element-plus/icons-vue"; |
| | | import { formatDisplayTime } from "../../approve-template/approveTemplateConstants.js"; |
| | | import { |
| | | approvalTypeLabel, |
| | | approvalTypeStyle, |
| | | approvalStatusLabel, |
| | | approvalStatusTagType, |
| | | resolveInstanceFormFields, |
| | | } from "../approveListConstants.js"; |
| | | import FormPayloadFields from "./FormPayloadFields.vue"; |
| | | import { computed } from "vue"; |
| | | import { Paperclip } from "@element-plus/icons-vue"; |
| | | import { formatDisplayTime } from "../../approve-template/approveTemplateConstants.js"; |
| | | import { |
| | | approvalTypeLabel, |
| | | approvalTypeStyle, |
| | | approvalStatusLabel, |
| | | approvalStatusTagType, |
| | | resolveInstanceFormFields, |
| | | } from "../approveListConstants.js"; |
| | | import FormPayloadFields from "./FormPayloadFields.vue"; |
| | | |
| | | const props = defineProps({ |
| | | row: { type: Object, default: () => ({}) }, |
| | | detailData: { type: Object, default: () => ({}) }, |
| | | }); |
| | | const props = defineProps({ |
| | | row: { type: Object, default: () => ({}) }, |
| | | detailData: { type: Object, default: () => ({}) }, |
| | | }); |
| | | |
| | | const formResolved = computed(() => resolveInstanceFormFields(props.row)); |
| | | const formResolved = computed(() => resolveInstanceFormFields(props.row)); |
| | | |
| | | // æ¯å¦ä¸ºç¹æ®å®¡æ¹ç±»åï¼éè´ãåè´§ãæ¥ä»·ï¼ |
| | | const isSpecialApprovalType = computed(() => { |
| | | return [5, 7, 6].includes(props.row.businessType); |
| | | }); |
| | | |
| | | // æ¯å¦ä¸ºç¹æ®å®¡æ¹ç±»åï¼éè´ãåè´§ãæ¥ä»·ï¼ |
| | | const isSpecialApprovalType = computed(() => { |
| | | return [5, 7, 6].includes(props.row.businessType); |
| | | }); |
| | | // 详æ
æ°æ®ï¼ç´æ¥ä½¿ç¨ä¼ å
¥ç detail-data åæ°ï¼ |
| | | const detailData = computed(() => { |
| | | return props.detailData || {}; |
| | | }); |
| | | |
| | | // 详æ
æ°æ®ï¼ç´æ¥ä½¿ç¨ä¼ å
¥ç detail-data åæ°ï¼ |
| | | const detailData = computed(() => { |
| | | return props.detailData || {}; |
| | | }); |
| | | const attachmentList = computed(() => { |
| | | const list = props.row.storageBlobVOList || props.row.storageBlobDTOs || []; |
| | | return Array.isArray(list) ? list : []; |
| | | }); |
| | | |
| | | const attachmentList = computed(() => { |
| | | const list = props.row.storageBlobVOList || props.row.storageBlobDTOs || []; |
| | | return Array.isArray(list) ? list : []; |
| | | }); |
| | | |
| | | function openFile(url) { |
| | | if (!url) return; |
| | | window.open(url, "_blank"); |
| | | } |
| | | function openFile(url) { |
| | | if (!url) return; |
| | | window.open(url, "_blank"); |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .approve-detail-panel { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 20px; |
| | | } |
| | | .detail-block-title { |
| | | font-size: 14px; |
| | | font-weight: 600; |
| | | color: var(--el-text-color-primary); |
| | | margin: 0 0 12px; |
| | | padding-left: 10px; |
| | | border-left: 3px solid var(--el-color-primary); |
| | | line-height: 1.4; |
| | | } |
| | | .approve-type-cell { |
| | | display: inline-block; |
| | | padding: 2px 10px; |
| | | border-radius: 4px; |
| | | font-size: 13px; |
| | | line-height: 1.5; |
| | | } |
| | | .reject-text { |
| | | color: var(--el-color-danger); |
| | | } |
| | | .approve-detail-panel { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 20px; |
| | | } |
| | | .detail-block-title { |
| | | font-size: 14px; |
| | | font-weight: 600; |
| | | color: var(--el-text-color-primary); |
| | | margin: 0 0 12px; |
| | | padding-left: 10px; |
| | | border-left: 3px solid var(--el-color-primary); |
| | | line-height: 1.4; |
| | | } |
| | | .approve-type-cell { |
| | | display: inline-block; |
| | | padding: 2px 10px; |
| | | border-radius: 4px; |
| | | font-size: 13px; |
| | | line-height: 1.5; |
| | | } |
| | | .reject-text { |
| | | color: var(--el-color-danger); |
| | | } |
| | | |
| | | .attachment-list { |
| | | display: grid; |
| | | grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); |
| | | gap: 12px; |
| | | } |
| | | .attachment-item { |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 10px 12px; |
| | | background-color: var(--el-fill-color-light); |
| | | border-radius: 6px; |
| | | border: 1px solid var(--el-border-color-lighter); |
| | | transition: all 0.3s; |
| | | } |
| | | .attachment-item:hover { |
| | | border-color: var(--el-color-primary-light-5); |
| | | background-color: var(--el-color-primary-light-9); |
| | | } |
| | | .file-icon { |
| | | font-size: 18px; |
| | | color: var(--el-text-color-secondary); |
| | | margin-right: 10px; |
| | | } |
| | | .file-name { |
| | | flex: 1; |
| | | font-size: 13px; |
| | | color: var(--el-text-color-primary); |
| | | white-space: nowrap; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | margin-right: 12px; |
| | | } |
| | | .file-actions { |
| | | display: flex; |
| | | align-items: center; |
| | | flex-shrink: 0; |
| | | } |
| | | </style> |
| | | .attachment-list { |
| | | display: grid; |
| | | grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); |
| | | gap: 12px; |
| | | } |
| | | .attachment-item { |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 10px 12px; |
| | | background-color: var(--el-fill-color-light); |
| | | border-radius: 6px; |
| | | border: 1px solid var(--el-border-color-lighter); |
| | | transition: all 0.3s; |
| | | } |
| | | .attachment-item:hover { |
| | | border-color: var(--el-color-primary-light-5); |
| | | background-color: var(--el-color-primary-light-9); |
| | | } |
| | | .file-icon { |
| | | font-size: 18px; |
| | | color: var(--el-text-color-secondary); |
| | | margin-right: 10px; |
| | | } |
| | | .file-name { |
| | | flex: 1; |
| | | font-size: 13px; |
| | | color: var(--el-text-color-primary); |
| | | white-space: nowrap; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | margin-right: 12px; |
| | | } |
| | | .file-actions { |
| | | display: flex; |
| | | align-items: center; |
| | | flex-shrink: 0; |
| | | } |
| | | </style> |
| | |
| | | <InstanceFlowDisplay :tasks="approveDialog.row?.tasks" :nodes="approveDialog.row?.flowNodes" /> |
| | | </div> |
| | | <el-form label-width="100px" class="mt16"> |
| | | <el-form-item label="ä»åº" v-if="isPurchaseApproval"> |
| | | <el-select |
| | | v-model="approveDialog.warehouse" |
| | | placeholder="è¯·éæ©ä»åº" |
| | | clearable |
| | | > |
| | | <el-option |
| | | v-for="item in warehouseOptions" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="å®¡æ¹æè§" required> |
| | | <el-input |
| | | v-model="approveOpinion" |
| | |
| | | <script setup> |
| | | import { Plus, RefreshRight } from "@element-plus/icons-vue"; |
| | | import { ElMessage } from "element-plus"; |
| | | import { computed, onMounted, ref } from "vue"; |
| | | import { computed, onMounted, ref, reactive } from "vue"; |
| | | import { getDicts } from "@/api/system/dict/data"; |
| | | import { APPROVAL_MODULE_KEYS } from "../approve-shared/approvalModuleRegistry.js"; |
| | | import FinReimburseApprovePanel from "../../ReimburseManage/shared/components/FinReimburseApprovePanel.vue"; |
| | | import ApprovalTemplateFormSection from "../approve-shared/components/ApprovalTemplateFormSection.vue"; |
| | |
| | | openDetail, |
| | | openApprove, |
| | | } = al; |
| | | |
| | | // ä»åºé项 |
| | | const warehouseOptions = ref([]); |
| | | |
| | | // è·åä»åºåå
¸æ°æ® |
| | | const loadWarehouseOptions = async () => { |
| | | const res = await getDicts("warehouse"); |
| | | if (res.code === 200) { |
| | | warehouseOptions.value = res.data.map((item) => ({ |
| | | label: item.dictLabel, |
| | | value: item.dictValue, |
| | | })); |
| | | } |
| | | }; |
| | | |
| | | // æ¯å¦ä¸ºéè´å®¡æ¹ |
| | | const isPurchaseApproval = computed(() => { |
| | | return Number(approveDialog.row?.businessType) === 5; |
| | | }); |
| | | |
| | | const { flowUserOptions, loadFlowUsers } = useFlowUserOptions(); |
| | | |
| | |
| | | onMounted(() => { |
| | | loadFlowUsers(); |
| | | loadSearchBusinessTypeOptions(); |
| | | loadWarehouseOptions(); |
| | | handleQuery(); |
| | | }); |
| | | </script> |
| | |
| | | const detailRow = ref({}); |
| | | const detailData = ref({}); |
| | | |
| | | const approveDialog = reactive({ visible: false, row: null }); |
| | | const approveDialog = reactive({ visible: false, row: null, warehouse: ''}); |
| | | const approveOpinion = ref(""); |
| | | const approveSubmitting = ref(false); |
| | | |
| | |
| | | } |
| | | approveDialog.row = { ...row }; |
| | | approveOpinion.value = ""; |
| | | approveDialog.warehouse = row?.warehouse || ''; |
| | | const bizType = Number(row.businessType); |
| | | // æ¥ä»·å®¡æ¹ï¼ç¨å®¡æ¹äºç±å段æ¿è½½ç"æ¥ä»·åå·"廿¥æ¥ä»·å表 |
| | | if (bizType === 6) { |
| | |
| | | approveSubmitting.value = true; |
| | | try { |
| | | await approveApprovalInstance( |
| | | buildApproveInstanceDto(row, result, approveOpinion.value) |
| | | buildApproveInstanceDto(row, result, approveOpinion.value, approveDialog.warehouse) |
| | | ); |
| | | reimburseDialog.visible = false; |
| | | await fetchApprovalList(); |
| | |
| | | if (approveSubmitting.value) return { ok: false }; |
| | | approveSubmitting.value = true; |
| | | try { |
| | | // åè´§å®¡æ¹æ¶ä¼ éåºåºæ¹å· |
| | | const extraData = {}; |
| | | if (Number(row.businessType) === 7 && detailData.value?.shippingInfo?.outboundBatches) { |
| | | extraData.outboundBatches = detailData.value.shippingInfo.outboundBatches; |
| | | } |
| | | await approveApprovalInstance( |
| | | buildApproveInstanceDto(row, result, approveOpinion.value) |
| | | buildApproveInstanceDto(row, result, approveOpinion.value, approveDialog.warehouse, extraData) |
| | | ); |
| | | approveDialog.visible = false; |
| | | await fetchApprovalList(); |
| | |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="å
¥åºç¶æ" |
| | | prop="stockInStatus" |
| | | width="100" |
| | | show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | <el-tag :type="getStockInStatusType(scope.row.stockInStatus)" |
| | | size="small"> |
| | | {{ scope.row.stockInStatus || '--' }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <!-- <el-table-column label="å
¥åºç¶æ"--> |
| | | <!-- prop="stockInStatus"--> |
| | | <!-- width="100"--> |
| | | <!-- show-overflow-tooltip>--> |
| | | <!-- <template #default="scope">--> |
| | | <!-- <el-tag :type="getStockInStatusType(scope.row.stockInStatus)"--> |
| | | <!-- size="small">--> |
| | | <!-- {{ scope.row.stockInStatus || '--' }}--> |
| | | <!-- </el-tag>--> |
| | | <!-- </template>--> |
| | | <!-- </el-table-column>--> |
| | | <el-table-column label="ç¾è®¢æ¥æ" |
| | | prop="executionDate" |
| | | width="100" |
| | |
| | | import {userListNoPage} from "@/api/system/user.js"; |
| | | import {qualityInspectDetailByProductId, getQualityTestStandardParamByTestStandardId} from "@/api/qualityManagement/metricMaintenance.js"; |
| | | import {qualityInspectParamInfo} from "@/api/qualityManagement/qualityInspectParam.js"; |
| | | import {ledgerListNoPage} from "@/api/salesManagement/salesLedger.js"; |
| | | import {ledgerListNoPage, productList} from "@/api/salesManagement/salesLedger.js"; |
| | | const { proxy } = getCurrentInstance() |
| | | const emit = defineEmits(['close']) |
| | | |
| | |
| | | salesContractNo: "", |
| | | }, |
| | | rules: { |
| | | checkTime: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | process: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | checkTime: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | process: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | checkName: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | productId: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | productModelId: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | testStandardId: [{ required: false, message: "è¯·éæ©ææ ", trigger: "change" }], |
| | | unit: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | quantity: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | qualifiedQuantity: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | unqualifiedQuantity: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | quantity: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | qualifiedQuantity: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | unqualifiedQuantity: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | checkCompany: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | checkResult: [{ required: true, message: "请è¾å
¥", trigger: "change" }], |
| | | checkResult: [{ required: false, message: "请è¾å
¥", trigger: "change" }], |
| | | }, |
| | | }); |
| | | const { form, rules } = toRefs(data); |
| | |
| | | form.value = {} |
| | | testStandardOptions.value = []; |
| | | tableData.value = []; |
| | | |
| | | // æ°å¢æ¨¡å¼ä¸ï¼é»è®¤è®¾ç½®äº§åå称为"马éè¯éªè±ç²"(id=320) |
| | | if (operationType.value === 'add') { |
| | | const defaultProductId = 320; |
| | | form.value.productId = defaultProductId; |
| | | currentProductId.value = defaultProductId; |
| | | form.value.productName = '马éè¯éªè±ç²'; |
| | | // å è½½è§æ ¼åå·å表 |
| | | try { |
| | | const modelRes = await modelList({ id: defaultProductId }); |
| | | modelOptions.value = modelRes || []; |
| | | // é»è®¤éä¸ç¬¬ä¸ä¸ªè§æ ¼åå· |
| | | if (modelOptions.value.length > 0) { |
| | | const firstModel = modelOptions.value[0]; |
| | | form.value.productModelId = firstModel.id; |
| | | form.value.model = firstModel.model || ''; |
| | | form.value.unit = firstModel.unit || ''; |
| | | } |
| | | // å è½½ææ é项 |
| | | getList(); |
| | | } catch (e) { |
| | | console.error('å è½½é»è®¤äº§åè§æ ¼åå·å¤±è´¥', e); |
| | | } |
| | | } |
| | | |
| | | if (operationType.value === 'edit' || operationType.value === 'view') { |
| | | // å
ä¿å testStandardIdï¼é¿å
被æ¸
空 |
| | |
| | | } |
| | | |
| | | // éå®è®¢åéæ©ååå¤ç |
| | | const handleSalesLedgerChange = (value) => { |
| | | const handleSalesLedgerChange = async (value) => { |
| | | const selectedItem = salesLedgerOptions.value.find(item => item.id == value); |
| | | if (selectedItem) { |
| | | form.value.salesContractNo = selectedItem.salesContractNo || ''; |
| | | // æ ¹æ®éå®è®¢åIDæ¥è¯¢äº§åä¿¡æ¯ï¼åªèµå¼æ°é |
| | | try { |
| | | const res = await productList({ salesLedgerId: value, type: 1 }); |
| | | const productData = res.data && res.data.length > 0 ? res.data[0] : null; |
| | | if (productData) { |
| | | // åªèªå¨å¸¦å
¥æ°é |
| | | form.value.quantity = productData.quantity || 0; |
| | | } |
| | | } catch (e) { |
| | | console.error('æ¥è¯¢éå®è®¢å产åä¿¡æ¯å¤±è´¥', e); |
| | | proxy.$modal.msgError('æ¥è¯¢éå®è®¢å产åä¿¡æ¯å¤±è´¥'); |
| | | } |
| | | } else { |
| | | form.value.salesContractNo = ''; |
| | | form.value.quantity = undefined; |
| | | } |
| | | } |
| | | |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div> |
| | | <el-dialog |
| | | v-model="dialogVisible" |
| | | title="å¿«éæ£éª" |
| | | width="70%" |
| | | @close="closeDialog" |
| | | > |
| | | <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef"> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ£æµç»æï¼" prop="checkResult"> |
| | | <el-select v-model="form.checkResult" placeholder="è¯·éæ©æ£æµç»æ" style="width: 100%" @change="handleCheckResultChange"> |
| | | <el-option label="åæ ¼" value="åæ ¼" /> |
| | | <el-option label="ä¸åæ ¼" value="ä¸åæ ¼" /> |
| | | <el-option label="é¨ååæ ¼" value="é¨ååæ ¼" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ææ éæ©ï¼" prop="testStandardId"> |
| | | <el-select v-model="form.testStandardId" placeholder="è¯·éæ©ææ " style="width: 100%" @change="handleTestStandardChange"> |
| | | <el-option |
| | | v-for="item in testStandardOptions" |
| | | :key="item.id" |
| | | :label="item.standardName || item.standardNo" |
| | | :value="item.id" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <template v-if="form.checkResult"> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ°éï¼" prop="quantity"> |
| | | <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="form.quantity" placeholder="请è¾å
¥æ°é" clearable :precision="2" @change="handleQuantityChange"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ£æµåä½ï¼" prop="checkCompany"> |
| | | <el-input v-model="form.checkCompany" placeholder="请è¾å
¥æ£æµåä½" clearable/> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="åæ ¼æ°éï¼" prop="qualifiedQuantity"> |
| | | <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="form.qualifiedQuantity" placeholder="请è¾å
¥åæ ¼æ°é" clearable :precision="2" @change="handleQualifiedQuantityChange"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ä¸åæ ¼æ°éï¼" prop="unqualifiedQuantity"> |
| | | <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="form.unqualifiedQuantity" placeholder="请è¾å
¥ä¸åæ ¼æ°é" clearable :precision="2" @change="handleUnqualifiedQuantityChange"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ£éªåï¼" prop="checkName"> |
| | | <el-select v-model="form.checkName" placeholder="è¯·éæ©æ£éªå" clearable style="width: 100%"> |
| | | <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName" :value="item.nickName"/> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ£æµæ¥æï¼" prop="checkTime"> |
| | | <el-date-picker |
| | | v-model="form.checkTime" |
| | | type="date" |
| | | placeholder="è¯·éæ©æ£æµæ¥æ" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | clearable |
| | | style="width: 100%" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </template> |
| | | </el-form> |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :tableLoading="tableLoading" |
| | | height="400" |
| | | > |
| | | <template #slot="{ row }"> |
| | | <el-input v-model="row.testValue" clearable/> |
| | | </template> |
| | | </PIMTable> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitForm">确认</el-button> |
| | | <el-button @click="closeDialog">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, toRefs, getCurrentInstance, nextTick } from "vue"; |
| | | import { userListNoPage } from "@/api/system/user.js"; |
| | | import { batchQuickInspect } from "@/api/qualityManagement/rawMaterialInspection.js"; |
| | | import { qualityInspectDetailByProductId, getQualityTestStandardParamByTestStandardId } from "@/api/qualityManagement/metricMaintenance.js"; |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | const emit = defineEmits(['close', 'success']); |
| | | |
| | | const dialogVisible = ref(false); |
| | | const userList = ref([]); |
| | | const selectedIds = ref([]); |
| | | const selectedRows = ref([]); |
| | | const testStandardOptions = ref([]); |
| | | const inspectType = ref(2); // åºåæ£éªç±»å |
| | | |
| | | const data = reactive({ |
| | | form: { |
| | | checkResult: '', |
| | | testStandardId: '', |
| | | quantity: undefined, |
| | | qualifiedQuantity: undefined, |
| | | unqualifiedQuantity: undefined, |
| | | checkCompany: '', |
| | | checkName: '', |
| | | checkTime: '', |
| | | }, |
| | | rules: { |
| | | checkResult: [{ required: true, message: "è¯·éæ©æ£æµç»æ", trigger: "change" }], |
| | | testStandardId: [{ required: true, message: "è¯·éæ©ææ ", trigger: "change" }], |
| | | quantity: [{ required: true, message: "请è¾å
¥æ°é", trigger: "blur" }], |
| | | qualifiedQuantity: [{ required: true, message: "请è¾å
¥åæ ¼æ°é", trigger: "blur" }], |
| | | unqualifiedQuantity: [{ required: true, message: "请è¾å
¥ä¸åæ ¼æ°é", trigger: "blur" }], |
| | | checkCompany: [{ required: true, message: "请è¾å
¥æ£æµåä½", trigger: "blur" }], |
| | | checkName: [{ required: true, message: "è¯·éæ©æ£éªå", trigger: "change" }], |
| | | checkTime: [{ required: true, message: "è¯·éæ©æ£æµæ¥æ", trigger: "change" }], |
| | | }, |
| | | }); |
| | | const { form, rules } = toRefs(data); |
| | | |
| | | const tableColumn = ref([ |
| | | { |
| | | label: "ææ ", |
| | | prop: "parameterItem", |
| | | }, |
| | | { |
| | | label: "åä½", |
| | | prop: "unit", |
| | | }, |
| | | { |
| | | label: "æ åå¼", |
| | | prop: "standardValue", |
| | | }, |
| | | { |
| | | label: "å
æ§å¼", |
| | | prop: "controlValue", |
| | | }, |
| | | { |
| | | label: "æ£éªå¼", |
| | | prop: "testValue", |
| | | dataType: 'slot', |
| | | slot: 'slot', |
| | | }, |
| | | ]); |
| | | const tableData = ref([]); |
| | | const tableLoading = ref(false); |
| | | |
| | | // æå¼å¼¹æ¡ |
| | | const openDialog = async (ids, rows) => { |
| | | selectedIds.value = ids; |
| | | selectedRows.value = rows; |
| | | dialogVisible.value = true; |
| | | |
| | | // 计ç®éä¸è¡çæ»æ°é |
| | | const totalQuantity = rows.reduce((sum, row) => sum + (Number(row.quantity) || 0), 0); |
| | | |
| | | // å è½½ç¨æ·å表 |
| | | const userListsRes = await userListNoPage(); |
| | | userList.value = userListsRes.data; |
| | | |
| | | // å è½½ææ éé¡¹ï¼æ ¹æ®ç¬¬ä¸ä¸ªéä¸è¡ç产åIDï¼ |
| | | if (rows && rows.length > 0 && rows[0].productId) { |
| | | const params = { |
| | | productId: rows[0].productId, |
| | | inspectType: 2 |
| | | }; |
| | | const res = await qualityInspectDetailByProductId(params); |
| | | testStandardOptions.value = res.data || []; |
| | | } else { |
| | | testStandardOptions.value = []; |
| | | } |
| | | |
| | | // é置表å |
| | | form.value = { |
| | | checkResult: '', |
| | | testStandardId: '', |
| | | quantity: totalQuantity, |
| | | qualifiedQuantity: totalQuantity, |
| | | unqualifiedQuantity: 0, |
| | | checkCompany: '', |
| | | checkName: '', |
| | | checkTime: '', |
| | | }; |
| | | tableData.value = []; |
| | | |
| | | await nextTick(); |
| | | proxy.$refs.formRef?.clearValidate(); |
| | | }; |
| | | |
| | | // ææ éæ©ååå¤ç |
| | | const handleTestStandardChange = async (testStandardId) => { |
| | | if (!testStandardId) { |
| | | tableData.value = []; |
| | | return; |
| | | } |
| | | tableLoading.value = true; |
| | | try { |
| | | const res = await getQualityTestStandardParamByTestStandardId(testStandardId); |
| | | tableData.value = (res.data || []).map(item => ({ |
| | | ...item, |
| | | id: null, |
| | | testValue: '' |
| | | })); |
| | | } catch (error) { |
| | | console.error('è·åæ ååæ°å¤±è´¥:', error); |
| | | tableData.value = []; |
| | | } finally { |
| | | tableLoading.value = false; |
| | | } |
| | | }; |
| | | |
| | | // æ£æµç»æååå¤ç |
| | | const handleCheckResultChange = (value) => { |
| | | if (value === 'åæ ¼') { |
| | | // åæ ¼æ¶ï¼åæ ¼æ°éçäºæ°éï¼ä¸åæ ¼æ°é为0 |
| | | form.value.qualifiedQuantity = form.value.quantity || 0; |
| | | form.value.unqualifiedQuantity = 0; |
| | | } else if (value === 'ä¸åæ ¼') { |
| | | // ä¸åæ ¼æ¶ï¼åæ ¼æ°é为0ï¼ä¸åæ ¼æ°éçäºæ°é |
| | | form.value.qualifiedQuantity = 0; |
| | | form.value.unqualifiedQuantity = form.value.quantity || 0; |
| | | } |
| | | }; |
| | | |
| | | // æ°éååå¤ç |
| | | const handleQuantityChange = (value) => { |
| | | if (form.value.checkResult === 'åæ ¼') { |
| | | form.value.qualifiedQuantity = value || 0; |
| | | form.value.unqualifiedQuantity = 0; |
| | | } else if (form.value.checkResult === 'ä¸åæ ¼') { |
| | | form.value.qualifiedQuantity = 0; |
| | | form.value.unqualifiedQuantity = value || 0; |
| | | } |
| | | }; |
| | | |
| | | // åæ ¼æ°éååå¤ç |
| | | const handleQualifiedQuantityChange = (value) => { |
| | | const quantity = form.value.quantity || 0; |
| | | if (value > quantity) { |
| | | proxy.$modal.msgWarning("åæ ¼æ°éä¸è½å¤§äºæ»æ°é"); |
| | | form.value.qualifiedQuantity = quantity; |
| | | form.value.unqualifiedQuantity = 0; |
| | | } else { |
| | | form.value.unqualifiedQuantity = Number((quantity - value).toFixed(2)); |
| | | } |
| | | updateCheckResult(); |
| | | }; |
| | | |
| | | // ä¸åæ ¼æ°éååå¤ç |
| | | const handleUnqualifiedQuantityChange = (value) => { |
| | | const quantity = form.value.quantity || 0; |
| | | if (value > quantity) { |
| | | proxy.$modal.msgWarning("ä¸åæ ¼æ°éä¸è½å¤§äºæ»æ°é"); |
| | | form.value.unqualifiedQuantity = quantity; |
| | | form.value.qualifiedQuantity = 0; |
| | | } else { |
| | | form.value.qualifiedQuantity = Number((quantity - value).toFixed(2)); |
| | | } |
| | | updateCheckResult(); |
| | | }; |
| | | |
| | | // æ ¹æ®åæ ¼/ä¸åæ ¼æ°éæ´æ°æ£æµç»æ |
| | | const updateCheckResult = () => { |
| | | const qualified = form.value.qualifiedQuantity || 0; |
| | | const unqualified = form.value.unqualifiedQuantity || 0; |
| | | const quantity = form.value.quantity || 0; |
| | | |
| | | if (qualified === quantity && unqualified === 0) { |
| | | form.value.checkResult = 'åæ ¼'; |
| | | } else if (unqualified === quantity && qualified === 0) { |
| | | form.value.checkResult = 'ä¸åæ ¼'; |
| | | } else if (qualified > 0 && unqualified > 0) { |
| | | form.value.checkResult = 'é¨ååæ ¼'; |
| | | } |
| | | }; |
| | | |
| | | // æäº¤è¡¨å |
| | | const submitForm = () => { |
| | | proxy.$refs.formRef.validate((valid) => { |
| | | if (valid) { |
| | | const data = { |
| | | ids: selectedIds.value, |
| | | inspectType: inspectType.value, |
| | | ...form.value, |
| | | paramList: tableData.value |
| | | }; |
| | | batchQuickInspect(data).then(res => { |
| | | proxy.$modal.msgSuccess(res.msg || "å¿«éæ£éªå®æ"); |
| | | emit('success'); |
| | | closeDialog(); |
| | | }); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // å
³éå¼¹æ¡ |
| | | const closeDialog = () => { |
| | | dialogVisible.value = false; |
| | | emit('close'); |
| | | }; |
| | | |
| | | defineExpose({ |
| | | openDialog, |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | </style> |
| | |
| | | @close="handleQuery"></FormDia> |
| | | <files-dia ref="filesDia" |
| | | @close="handleQuery"></files-dia> |
| | | <QuickCheckDia ref="quickCheckDia" |
| | | @close="handleQuery" |
| | | @success="getList"></QuickCheckDia> |
| | | <el-dialog v-model="dialogFormVisible" |
| | | title="ç¼è¾æ£éªå" |
| | | width="30%" |
| | |
| | | } from "vue"; |
| | | import InspectionFormDia from "@/views/qualityManagement/finalInspection/components/inspectionFormDia.vue"; |
| | | import FormDia from "@/views/qualityManagement/finalInspection/components/formDia.vue"; |
| | | import QuickCheckDia from "@/views/qualityManagement/finalInspection/components/quickCheckDia.vue"; |
| | | import { ElMessageBox } from "element-plus"; |
| | | import { |
| | | downloadQualityInspect, |
| | |
| | | qualityInspectListPage, |
| | | qualityInspectUpdate, |
| | | submitQualityInspect, |
| | | batchQuickInspect, |
| | | } from "@/api/qualityManagement/rawMaterialInspection.js"; |
| | | import FilesDia from "@/views/qualityManagement/finalInspection/components/filesDia.vue"; |
| | | import dayjs from "dayjs"; |
| | |
| | | const formDia = ref(); |
| | | const filesDia = ref(); |
| | | const inspectionFormDia = ref(); |
| | | const quickCheckDia = ref(); |
| | | const { proxy } = getCurrentInstance(); |
| | | const userStore = useUserStore(); |
| | | const userList = ref([]); |
| | |
| | | return; |
| | | } |
| | | |
| | | const totalCount = selectedRows.value.length; |
| | | const submittedCount = totalCount - unSubmittedRows.length; |
| | | |
| | | let confirmMessage = `已鿩 ${totalCount} æ¡æ£éªå`; |
| | | if (submittedCount > 0) { |
| | | confirmMessage += `ï¼å
¶ä¸ ${submittedCount} æ¡å·²æäº¤ï¼å°èªå¨è·³è¿ï¼`; |
| | | } |
| | | confirmMessage += `\n\n确认åå°èªå¨ï¼\n· æ£éªç»æè®¾ä¸º"åæ ¼"\n· åæ ¼æ°éè®¾ä¸ºæ»æ°\n· ä¸åæ ¼æ°é设为 0\n· æäº¤å¹¶å
¥åº`; |
| | | |
| | | ElMessageBox.confirm(confirmMessage, "å¿«éæ£éª", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | dangerouslyUseHTMLString: false, |
| | | }) |
| | | .then(() => { |
| | | // è°ç¨æ¹éå¿«éæ£éªæ¥å£ |
| | | const ids = unSubmittedRows.map(item => item.id); |
| | | batchQuickInspect(ids).then(res => { |
| | | proxy.$modal.msgSuccess(res.msg || "å¿«éæ£éªå®æ"); |
| | | getList(); |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | const ids = unSubmittedRows.map(item => item.id); |
| | | quickCheckDia.value?.openDialog(ids, unSubmittedRows); |
| | | }; |
| | | |
| | | // æå¼æ°å¢æ£éªå¼¹æ¡ |
| | |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="closeBindingDialog">åæ¶</el-button> |
| | | <el-button type="primary" @click="submitBinding">ç¡®å®</el-button> |
| | | <el-button @click="closeBindingDialog">åæ¶</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | |
| | | formatData: (val) => { |
| | | const map = { 0: 'åæææ£éª', 1: 'è¿ç¨æ£éª', 2: 'åºåæ£éª' } |
| | | return map[val] || val |
| | | } |
| | | }, |
| | | { |
| | | label: 'å·¥åº', |
| | | prop: 'processId', |
| | | align: 'center', |
| | | dataType: 'tag', |
| | | formatData: (val) => { |
| | | const target = processOptions.value.find( |
| | | (item) => String(item.value) === String(val) |
| | | ) |
| | | return target?.label || val |
| | | } |
| | | }, |
| | | { |
| | |
| | | <el-option label="åºåæ£éª" value="2" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="å·¥åº" prop="processId"> |
| | | <el-select v-model="form.processId" placeholder="è¯·éæ©å·¥åº" style="width: 100%"> |
| | | <el-option |
| | | v-for="item in processOptions" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="ç¶æ" prop="state"> |
| | | <el-select v-model="form.state" placeholder="è¯·éæ©ç¶æ" style="width: 100%"> |
| | | <el-option label="è稿" value="0" /> |
| | |
| | | |
| | | const formRef = ref(null) |
| | | |
| | | const computedTitle = computed(() => { |
| | | const computedTitle = computed(() => { |
| | | if (props.operationType === 'edit') return 'ç¼è¾æ£æµæ å' |
| | | if (props.operationType === 'copy') return 'å¤å¶æ£æµæ å' |
| | | return 'æ°å¢æ£æµæ å' |
| | |
| | | } |
| | | }, |
| | | { |
| | | label: 'å·¥åº', |
| | | prop: 'processId', |
| | | align: 'center', |
| | | dataType: 'tag', |
| | | formatData: (val) => { |
| | | const target = processOptions.value.find( |
| | | (item) => String(item.value) === String(val) |
| | | ) |
| | | return target?.label || val |
| | | } |
| | | }, |
| | | { |
| | | label: 'ç¶æ', |
| | | prop: 'state', |
| | | headerSlot: 'stateHeader', |
| | |
| | | ref="formRef"> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å·¥åºï¼" |
| | | prop="process"> |
| | | <el-select v-model="form.process" |
| | | placeholder="è¯·éæ©å·¥åº" |
| | | <el-form-item label="éè´ååå·ï¼" |
| | | prop="purchaseContractNo"> |
| | | <el-select v-model="form.purchaseContractNo" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | :disabled="isViewMode || processQuantityDisabled" |
| | | filterable |
| | | :disabled="isViewMode" |
| | | style="width: 100%"> |
| | | <el-option v-for="item in processList" |
| | | :key="item.name" |
| | | :label="item.name" |
| | | :value="item.name" /> |
| | | <el-option v-for="item in purchaseContractList" |
| | | :key="item.id" |
| | | :label="item.purchaseContractNumber + ' - ' + item.supplierName" |
| | | :value="item.purchaseContractNumber" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | |
| | | getCurrentInstance, |
| | | nextTick, |
| | | } from "vue"; |
| | | import { getOptions } from "@/api/procurementManagement/procurementLedger.js"; |
| | | import { getOptions, purchaseListPage } from "@/api/procurementManagement/procurementLedger.js"; |
| | | import { modelList, productTreeList } from "@/api/basicData/product.js"; |
| | | import { |
| | | qualityInspectAdd, |
| | |
| | | } from "@/api/qualityManagement/metricMaintenance.js"; |
| | | import { userListNoPage } from "@/api/system/user.js"; |
| | | import { qualityInspectParamInfo } from "@/api/qualityManagement/qualityInspectParam.js"; |
| | | import { list } from "@/api/productionManagement/productionProcess"; |
| | | |
| | | import qualified from "@/views/inventoryManagement/stockManagement/Qualified.vue"; |
| | | const { proxy } = getCurrentInstance(); |
| | | const emit = defineEmits(["close"]); |
| | |
| | | const data = reactive({ |
| | | form: { |
| | | checkTime: "", |
| | | process: "", |
| | | checkName: "", |
| | | productName: "", |
| | | productId: "", |
| | |
| | | unqualifiedQuantity: "", |
| | | checkCompany: "", |
| | | checkResult: "", |
| | | purchaseContractNo: "", |
| | | }, |
| | | rules: { |
| | | checkTime: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | process: [{ required: true, message: "è¯·éæ©å·¥åº", trigger: "change" }], |
| | | checkTime: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | checkName: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | productId: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | productModelId: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | testStandardId: [{ required: false, message: "è¯·éæ©ææ ", trigger: "change" }], |
| | | unit: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | quantity: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | qualifiedQuantity: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | unqualifiedQuantity: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | quantity: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | qualifiedQuantity: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | unqualifiedQuantity: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | checkCompany: [{ required: false, message: "请è¾å
¥", trigger: "blur" }], |
| | | checkResult: [{ required: true, message: "请è¾å
¥", trigger: "change" }], |
| | | purchaseContractNo: [{ required: false, message: "è¯·éæ©", trigger: "change" }], |
| | | checkResult: [{ required: false, message: "请è¾å
¥", trigger: "change" }], |
| | | }, |
| | | }); |
| | | const userList = ref([]); |
| | |
| | | const v = form.value || {}; |
| | | return !!(v.productMainId != null || v.purchaseLedgerId != null); |
| | | }); |
| | | const processList = ref([]); // å·¥åºä¸æå表ï¼å·¥åºåç§° nameï¼ |
| | | |
| | | const supplierList = ref([]); |
| | | const productOptions = ref([]); |
| | | const purchaseContractList = ref([]); // éè´ååå·å表 |
| | | const tableColumn = ref([ |
| | | { |
| | | label: "ææ ", |
| | |
| | | getOptions().then(res => { |
| | | supplierList.value = res.data; |
| | | }); |
| | | // å 载工åºä¸æå表 |
| | | try { |
| | | const res = await list({ size: -1, current: -1 }); |
| | | processList.value = res.data.records || []; |
| | | } catch (e) { |
| | | console.error("å 载工åºå表失败", e); |
| | | processList.value = []; |
| | | } |
| | | let userLists = await userListNoPage(); |
| | | userList.value = userLists.data; |
| | | // å
éç½®è¡¨åæ°æ®ï¼ä¿æåæ®µå®æ´ï¼é¿å
å¼¹çªé¦æ¬¡æ¸²ææ¶è§¦åå¿
填红æ¡âéªä¸ä¸âï¼ |
| | | // å è½½éè´ååå·å表 |
| | | try { |
| | | const contractRes = await purchaseListPage({ pageNum: -1, pageSize: -1 }); |
| | | purchaseContractList.value = contractRes.data?.records || []; |
| | | } catch (e) { |
| | | console.error("å è½½éè´ååå·å¤±è´¥", e); |
| | | purchaseContractList.value = []; |
| | | } |
| | | // å
éç½®è¡¨åæ°æ®ï¼ä¿æåæ®µå®æ´ï¼é¿å
å¼¹çªé¦æ¬¡æ¸²ææ¶è§¦åå¿
填红æ¡"éªä¸ä¸"ï¼ |
| | | form.value = { |
| | | checkTime: "", |
| | | process: "", |
| | | checkName: "", |
| | | productName: "", |
| | | productId: "", |
| | |
| | | quantity: "", |
| | | checkCompany: "", |
| | | checkResult: "", |
| | | purchaseContractNo: "", |
| | | }; |
| | | testStandardOptions.value = []; |
| | | tableData.value = []; |
| | |
| | | let params = { |
| | | productId: currentProductId.value, |
| | | inspectType: 1, |
| | | process: form.value.process || "", |
| | | }; |
| | | qualityInspectDetailByProductId(params).then(res => { |
| | | testStandardOptions.value = res.data || []; |
| | |
| | | return; |
| | | } |
| | | |
| | | // æ ¹æ®éè´ååå·æ¥æ¾å¯¹åºç purchaseLedgerId |
| | | const selectedContract = purchaseContractList.value.find( |
| | | item => item.purchaseContractNumber === form.value.purchaseContractNo |
| | | ); |
| | | const purchaseLedgerId = selectedContract ? selectedContract.id : null; |
| | | |
| | | const data = { |
| | | ...form.value, |
| | | process: processName, // ä¿ç process åæ®µä»¥å
¼å®¹å端 |
| | | purchaseLedgerId: purchaseLedgerId, // æäº¤ purchaseLedgerId |
| | | qualityInspectParams: tableData.value, |
| | | }; |
| | | if (operationType.value === "add") { |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div> |
| | | <el-dialog |
| | | v-model="dialogVisible" |
| | | title="å¿«éæ£éª" |
| | | width="70%" |
| | | @close="closeDialog" |
| | | > |
| | | <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef"> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ£æµç»æï¼" prop="checkResult"> |
| | | <el-select v-model="form.checkResult" placeholder="è¯·éæ©æ£æµç»æ" style="width: 100%" @change="handleCheckResultChange"> |
| | | <el-option label="åæ ¼" value="åæ ¼" /> |
| | | <el-option label="ä¸åæ ¼" value="ä¸åæ ¼" /> |
| | | <el-option label="é¨ååæ ¼" value="é¨ååæ ¼" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ææ éæ©ï¼" prop="testStandardId"> |
| | | <el-select v-model="form.testStandardId" placeholder="è¯·éæ©ææ " style="width: 100%" @change="handleTestStandardChange"> |
| | | <el-option |
| | | v-for="item in testStandardOptions" |
| | | :key="item.id" |
| | | :label="item.standardName || item.standardNo" |
| | | :value="item.id" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <template v-if="form.checkResult"> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ°éï¼" prop="quantity"> |
| | | <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="form.quantity" placeholder="请è¾å
¥æ°é" clearable :precision="2" @change="handleQuantityChange"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ£æµåä½ï¼" prop="checkCompany"> |
| | | <el-input v-model="form.checkCompany" placeholder="请è¾å
¥æ£æµåä½" clearable/> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="åæ ¼æ°éï¼" prop="qualifiedQuantity"> |
| | | <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="form.qualifiedQuantity" placeholder="请è¾å
¥åæ ¼æ°é" clearable :precision="2" @change="handleQualifiedQuantityChange"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ä¸åæ ¼æ°éï¼" prop="unqualifiedQuantity"> |
| | | <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="form.unqualifiedQuantity" placeholder="请è¾å
¥ä¸åæ ¼æ°é" clearable :precision="2" @change="handleUnqualifiedQuantityChange"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ£éªåï¼" prop="checkName"> |
| | | <el-select v-model="form.checkName" placeholder="è¯·éæ©æ£éªå" clearable style="width: 100%"> |
| | | <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName" :value="item.nickName"/> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ£æµæ¥æï¼" prop="checkTime"> |
| | | <el-date-picker |
| | | v-model="form.checkTime" |
| | | type="date" |
| | | placeholder="è¯·éæ©æ£æµæ¥æ" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | clearable |
| | | style="width: 100%" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </template> |
| | | </el-form> |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :tableLoading="tableLoading" |
| | | height="400" |
| | | > |
| | | <template #slot="{ row }"> |
| | | <el-input v-model="row.testValue" clearable/> |
| | | </template> |
| | | </PIMTable> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitForm">确认</el-button> |
| | | <el-button @click="closeDialog">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, toRefs, getCurrentInstance, nextTick } from "vue"; |
| | | import { userListNoPage } from "@/api/system/user.js"; |
| | | import { batchQuickInspect } from "@/api/qualityManagement/rawMaterialInspection.js"; |
| | | import { qualityInspectDetailByProductId, getQualityTestStandardParamByTestStandardId } from "@/api/qualityManagement/metricMaintenance.js"; |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | const emit = defineEmits(['close', 'success']); |
| | | |
| | | const dialogVisible = ref(false); |
| | | const userList = ref([]); |
| | | const selectedIds = ref([]); |
| | | const selectedRows = ref([]); |
| | | const testStandardOptions = ref([]); |
| | | const inspectType = ref(1); // è¿ç¨æ£éªç±»å |
| | | |
| | | const data = reactive({ |
| | | form: { |
| | | checkResult: '', |
| | | testStandardId: '', |
| | | quantity: undefined, |
| | | qualifiedQuantity: undefined, |
| | | unqualifiedQuantity: undefined, |
| | | checkCompany: '', |
| | | checkName: '', |
| | | checkTime: '', |
| | | }, |
| | | rules: { |
| | | checkResult: [{ required: true, message: "è¯·éæ©æ£æµç»æ", trigger: "change" }], |
| | | testStandardId: [{ required: true, message: "è¯·éæ©ææ ", trigger: "change" }], |
| | | quantity: [{ required: true, message: "请è¾å
¥æ°é", trigger: "blur" }], |
| | | qualifiedQuantity: [{ required: true, message: "请è¾å
¥åæ ¼æ°é", trigger: "blur" }], |
| | | unqualifiedQuantity: [{ required: true, message: "请è¾å
¥ä¸åæ ¼æ°é", trigger: "blur" }], |
| | | checkCompany: [{ required: true, message: "请è¾å
¥æ£æµåä½", trigger: "blur" }], |
| | | checkName: [{ required: true, message: "è¯·éæ©æ£éªå", trigger: "change" }], |
| | | checkTime: [{ required: true, message: "è¯·éæ©æ£æµæ¥æ", trigger: "change" }], |
| | | }, |
| | | }); |
| | | const { form, rules } = toRefs(data); |
| | | |
| | | const tableColumn = ref([ |
| | | { |
| | | label: "ææ ", |
| | | prop: "parameterItem", |
| | | }, |
| | | { |
| | | label: "åä½", |
| | | prop: "unit", |
| | | }, |
| | | { |
| | | label: "æ åå¼", |
| | | prop: "standardValue", |
| | | }, |
| | | { |
| | | label: "å
æ§å¼", |
| | | prop: "controlValue", |
| | | }, |
| | | { |
| | | label: "æ£éªå¼", |
| | | prop: "testValue", |
| | | dataType: 'slot', |
| | | slot: 'slot', |
| | | }, |
| | | ]); |
| | | const tableData = ref([]); |
| | | const tableLoading = ref(false); |
| | | |
| | | // æå¼å¼¹æ¡ |
| | | const openDialog = async (ids, rows) => { |
| | | selectedIds.value = ids; |
| | | selectedRows.value = rows; |
| | | dialogVisible.value = true; |
| | | |
| | | // å è½½ç¨æ·å表 |
| | | const userListsRes = await userListNoPage(); |
| | | userList.value = userListsRes.data; |
| | | |
| | | // å è½½ææ éé¡¹ï¼æ ¹æ®ç¬¬ä¸ä¸ªéä¸è¡ç产åIDï¼ |
| | | if (rows && rows.length > 0 && rows[0].productId) { |
| | | const params = { |
| | | productId: rows[0].productId, |
| | | inspectType: 1 |
| | | }; |
| | | const res = await qualityInspectDetailByProductId(params); |
| | | testStandardOptions.value = res.data || []; |
| | | } else { |
| | | testStandardOptions.value = []; |
| | | } |
| | | |
| | | // é置表å |
| | | form.value = { |
| | | checkResult: '', |
| | | testStandardId: '', |
| | | quantity: undefined, |
| | | qualifiedQuantity: undefined, |
| | | unqualifiedQuantity: undefined, |
| | | checkCompany: '', |
| | | checkName: '', |
| | | checkTime: '', |
| | | }; |
| | | tableData.value = []; |
| | | |
| | | await nextTick(); |
| | | proxy.$refs.formRef?.clearValidate(); |
| | | }; |
| | | |
| | | // ææ éæ©ååå¤ç |
| | | const handleTestStandardChange = async (testStandardId) => { |
| | | if (!testStandardId) { |
| | | tableData.value = []; |
| | | return; |
| | | } |
| | | tableLoading.value = true; |
| | | try { |
| | | const res = await getQualityTestStandardParamByTestStandardId(testStandardId); |
| | | tableData.value = (res.data || []).map(item => ({ |
| | | ...item, |
| | | id: null, |
| | | testValue: '' |
| | | })); |
| | | } catch (error) { |
| | | console.error('è·åæ ååæ°å¤±è´¥:', error); |
| | | tableData.value = []; |
| | | } finally { |
| | | tableLoading.value = false; |
| | | } |
| | | }; |
| | | |
| | | // æ£æµç»æååå¤ç |
| | | const handleCheckResultChange = (value) => { |
| | | if (value === 'åæ ¼') { |
| | | // åæ ¼æ¶ï¼åæ ¼æ°éçäºæ°éï¼ä¸åæ ¼æ°é为0 |
| | | form.value.qualifiedQuantity = form.value.quantity || 0; |
| | | form.value.unqualifiedQuantity = 0; |
| | | } else if (value === 'ä¸åæ ¼') { |
| | | // ä¸åæ ¼æ¶ï¼åæ ¼æ°é为0ï¼ä¸åæ ¼æ°éçäºæ°é |
| | | form.value.qualifiedQuantity = 0; |
| | | form.value.unqualifiedQuantity = form.value.quantity || 0; |
| | | } |
| | | }; |
| | | |
| | | // æ°éååå¤ç |
| | | const handleQuantityChange = (value) => { |
| | | if (form.value.checkResult === 'åæ ¼') { |
| | | form.value.qualifiedQuantity = value || 0; |
| | | form.value.unqualifiedQuantity = 0; |
| | | } else if (form.value.checkResult === 'ä¸åæ ¼') { |
| | | form.value.qualifiedQuantity = 0; |
| | | form.value.unqualifiedQuantity = value || 0; |
| | | } |
| | | }; |
| | | |
| | | // åæ ¼æ°éååå¤ç |
| | | const handleQualifiedQuantityChange = (value) => { |
| | | const quantity = form.value.quantity || 0; |
| | | if (value > quantity) { |
| | | proxy.$modal.msgWarning("åæ ¼æ°éä¸è½å¤§äºæ»æ°é"); |
| | | form.value.qualifiedQuantity = quantity; |
| | | form.value.unqualifiedQuantity = 0; |
| | | } else { |
| | | form.value.unqualifiedQuantity = Number((quantity - value).toFixed(2)); |
| | | } |
| | | updateCheckResult(); |
| | | }; |
| | | |
| | | // ä¸åæ ¼æ°éååå¤ç |
| | | const handleUnqualifiedQuantityChange = (value) => { |
| | | const quantity = form.value.quantity || 0; |
| | | if (value > quantity) { |
| | | proxy.$modal.msgWarning("ä¸åæ ¼æ°éä¸è½å¤§äºæ»æ°é"); |
| | | form.value.unqualifiedQuantity = quantity; |
| | | form.value.qualifiedQuantity = 0; |
| | | } else { |
| | | form.value.qualifiedQuantity = Number((quantity - value).toFixed(2)); |
| | | } |
| | | updateCheckResult(); |
| | | }; |
| | | |
| | | // æ ¹æ®åæ ¼/ä¸åæ ¼æ°éæ´æ°æ£æµç»æ |
| | | const updateCheckResult = () => { |
| | | const qualified = form.value.qualifiedQuantity || 0; |
| | | const unqualified = form.value.unqualifiedQuantity || 0; |
| | | const quantity = form.value.quantity || 0; |
| | | |
| | | if (qualified === quantity && unqualified === 0) { |
| | | form.value.checkResult = 'åæ ¼'; |
| | | } else if (unqualified === quantity && qualified === 0) { |
| | | form.value.checkResult = 'ä¸åæ ¼'; |
| | | } else if (qualified > 0 && unqualified > 0) { |
| | | form.value.checkResult = 'é¨ååæ ¼'; |
| | | } |
| | | }; |
| | | |
| | | // æäº¤è¡¨å |
| | | const submitForm = () => { |
| | | proxy.$refs.formRef.validate((valid) => { |
| | | if (valid) { |
| | | const data = { |
| | | ids: selectedIds.value, |
| | | inspectType: inspectType.value, |
| | | ...form.value, |
| | | paramList: tableData.value |
| | | }; |
| | | batchQuickInspect(data).then(res => { |
| | | proxy.$modal.msgSuccess(res.msg || "å¿«éæ£éªå®æ"); |
| | | emit('success'); |
| | | closeDialog(); |
| | | }); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // å
³éå¼¹æ¡ |
| | | const closeDialog = () => { |
| | | dialogVisible.value = false; |
| | | emit('close'); |
| | | }; |
| | | |
| | | defineExpose({ |
| | | openDialog, |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | </style> |
| | |
| | | <div class="app-container"> |
| | | <div class="search_form mb20"> |
| | | <div> |
| | | <span class="search_title">å·¥åºï¼</span> |
| | | <el-input v-model="searchForm.process" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥å·¥åºæç´¢" |
| | | @change="handleQuery" |
| | | clearable |
| | | :prefix-icon="Search" /> |
| | | <span style="margin-left: 10px" |
| | | class="search_title">æ£æµæ¥æï¼</span> |
| | | <el-date-picker v-model="searchForm.entryDate" |
| | |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | @change="changeDaterange" /> |
| | | <span style="margin-left: 10px" |
| | | class="search_title">ç产工åå·ï¼</span> |
| | | <el-input v-model="searchForm.workOrderNo" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥ç产工åå·æç´¢" |
| | | @change="handleQuery" |
| | | clearable |
| | | :prefix-icon="Search" /> |
| | | <el-button type="primary" |
| | | @click="handleQuery" |
| | | style="margin-left: 10px">æç´¢</el-button> |
| | |
| | | @close="handleQuery"></FormDia> |
| | | <files-dia ref="filesDia" |
| | | @close="handleQuery"></files-dia> |
| | | <QuickCheckDia ref="quickCheckDia" |
| | | @close="handleQuery" |
| | | @success="getList"></QuickCheckDia> |
| | | <el-dialog v-model="dialogFormVisible" |
| | | title="ç¼è¾æ£éªå" |
| | | width="30%" |
| | |
| | | } from "vue"; |
| | | import InspectionFormDia from "@/views/qualityManagement/processInspection/components/inspectionFormDia.vue"; |
| | | import FormDia from "@/views/qualityManagement/processInspection/components/formDia.vue"; |
| | | import QuickCheckDia from "@/views/qualityManagement/processInspection/components/quickCheckDia.vue"; |
| | | import { ElMessageBox } from "element-plus"; |
| | | import { |
| | | downloadQualityInspect, |
| | |
| | | qualityInspectListPage, |
| | | qualityInspectUpdate, |
| | | submitQualityInspect, |
| | | batchQuickInspect, |
| | | } from "@/api/qualityManagement/rawMaterialInspection.js"; |
| | | import FilesDia from "@/views/qualityManagement/processInspection/components/filesDia.vue"; |
| | | import dayjs from "dayjs"; |
| | |
| | | |
| | | const data = reactive({ |
| | | searchForm: { |
| | | process: "", |
| | | entryDate: undefined, // å½å
¥æ¥æ |
| | | workOrderNo: "", |
| | | entryDateStart: undefined, |
| | |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: "å·¥åº", |
| | | prop: "process", |
| | | width: 230, |
| | | }, |
| | | { |
| | | label: "æ£éªå", |
| | | prop: "checkName", |
| | | }, |
| | | { |
| | | label: "éè´ååå·", |
| | | prop: "purchaseContractNo", |
| | | width: 150, |
| | | }, |
| | | { |
| | | label: "产ååç§°", |
| | |
| | | const formDia = ref(); |
| | | const filesDia = ref(); |
| | | const inspectionFormDia = ref(); |
| | | const quickCheckDia = ref(); |
| | | const { proxy } = getCurrentInstance(); |
| | | const userStore = useUserStore(); |
| | | const changeDaterange = value => { |
| | |
| | | return; |
| | | } |
| | | |
| | | const totalCount = selectedRows.value.length; |
| | | const submittedCount = totalCount - unSubmittedRows.length; |
| | | |
| | | let confirmMessage = `已鿩 ${totalCount} æ¡æ£éªå`; |
| | | if (submittedCount > 0) { |
| | | confirmMessage += `ï¼å
¶ä¸ ${submittedCount} æ¡å·²æäº¤ï¼å°èªå¨è·³è¿ï¼`; |
| | | } |
| | | confirmMessage += `\n\n确认åå°èªå¨ï¼\n· æ£éªç»æè®¾ä¸º"åæ ¼"\n· åæ ¼æ°éè®¾ä¸ºæ»æ°\n· ä¸åæ ¼æ°é设为 0\n· æäº¤å¹¶å
¥åº`; |
| | | |
| | | ElMessageBox.confirm(confirmMessage, "å¿«éæ£éª", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | dangerouslyUseHTMLString: false, |
| | | }) |
| | | .then(() => { |
| | | // è°ç¨æ¹éå¿«éæ£éªæ¥å£ |
| | | const ids = unSubmittedRows.map(item => item.id); |
| | | batchQuickInspect(ids).then(res => { |
| | | proxy.$modal.msgSuccess(res.msg || "å¿«éæ£éªå®æ"); |
| | | getList(); |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | const ids = unSubmittedRows.map(item => item.id); |
| | | quickCheckDia.value?.openDialog(ids, unSubmittedRows); |
| | | }; |
| | | |
| | | // æå¼æ°å¢æ£éªå¼¹æ¡ |
| | |
| | | checkResult: "", |
| | | }, |
| | | rules: { |
| | | checkTime: [{required: true, message: "请è¾å
¥", trigger: "blur"},], |
| | | checkTime: [{required: false, message: "请è¾å
¥", trigger: "blur"},], |
| | | supplier: [{required: true, message: "请è¾å
¥", trigger: "blur"}], |
| | | checkName: [{required: false, message: "请è¾å
¥", trigger: "blur"}], |
| | | productId: [{required: true, message: "请è¾å
¥", trigger: "blur"}], |
| | | productModelId: [{required: true, message: "è¯·éæ©äº§ååå·", trigger: "change"}], |
| | | testStandardId: [{required: false, message: "è¯·éæ©ææ ", trigger: "change"}], |
| | | unit: [{required: false, message: "请è¾å
¥", trigger: "blur"}], |
| | | quantity: [{required: true, message: "请è¾å
¥", trigger: "blur"}], |
| | | qualifiedQuantity: [{required: true, message: "请è¾å
¥", trigger: "blur"}], |
| | | unqualifiedQuantity: [{required: true, message: "请è¾å
¥", trigger: "blur"}], |
| | | quantity: [{required: false, message: "请è¾å
¥", trigger: "blur"}], |
| | | qualifiedQuantity: [{required: false, message: "请è¾å
¥", trigger: "blur"}], |
| | | unqualifiedQuantity: [{required: false, message: "请è¾å
¥", trigger: "blur"}], |
| | | checkCompany: [{required: false, message: "请è¾å
¥", trigger: "blur"}], |
| | | checkResult: [{required: true, message: "è¯·éæ©æ£æµç»æ", trigger: "change"}], |
| | | checkResult: [{required: false, message: "è¯·éæ©æ£æµç»æ", trigger: "change"}], |
| | | }, |
| | | }); |
| | | const tableColumn = ref([ |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div> |
| | | <el-dialog |
| | | v-model="dialogVisible" |
| | | title="å¿«éæ£éª" |
| | | width="70%" |
| | | @close="closeDialog" |
| | | > |
| | | <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef"> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ£æµç»æï¼" prop="checkResult"> |
| | | <el-select v-model="form.checkResult" placeholder="è¯·éæ©æ£æµç»æ" style="width: 100%" @change="handleCheckResultChange"> |
| | | <el-option label="åæ ¼" value="åæ ¼" /> |
| | | <el-option label="ä¸åæ ¼" value="ä¸åæ ¼" /> |
| | | <el-option label="é¨ååæ ¼" value="é¨ååæ ¼" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ææ éæ©ï¼" prop="testStandardId"> |
| | | <el-select v-model="form.testStandardId" placeholder="è¯·éæ©ææ " style="width: 100%" @change="handleTestStandardChange"> |
| | | <el-option |
| | | v-for="item in testStandardOptions" |
| | | :key="item.id" |
| | | :label="item.standardName || item.standardNo" |
| | | :value="item.id" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <template v-if="form.checkResult"> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ°éï¼" prop="quantity"> |
| | | <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="form.quantity" placeholder="请è¾å
¥æ°é" clearable :precision="2" @change="handleQuantityChange"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ£æµåä½ï¼" prop="checkCompany"> |
| | | <el-input v-model="form.checkCompany" placeholder="请è¾å
¥æ£æµåä½" clearable/> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="åæ ¼æ°éï¼" prop="qualifiedQuantity"> |
| | | <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="form.qualifiedQuantity" placeholder="请è¾å
¥åæ ¼æ°é" clearable :precision="2" @change="handleQualifiedQuantityChange"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ä¸åæ ¼æ°éï¼" prop="unqualifiedQuantity"> |
| | | <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="form.unqualifiedQuantity" placeholder="请è¾å
¥ä¸åæ ¼æ°é" clearable :precision="2" @change="handleUnqualifiedQuantityChange"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ£éªåï¼" prop="checkName"> |
| | | <el-select v-model="form.checkName" placeholder="è¯·éæ©æ£éªå" clearable style="width: 100%"> |
| | | <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName" :value="item.nickName"/> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ£æµæ¥æï¼" prop="checkTime"> |
| | | <el-date-picker |
| | | v-model="form.checkTime" |
| | | type="date" |
| | | placeholder="è¯·éæ©æ£æµæ¥æ" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | clearable |
| | | style="width: 100%" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </template> |
| | | </el-form> |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :tableLoading="tableLoading" |
| | | height="400" |
| | | > |
| | | <template #slot="{ row }"> |
| | | <el-input v-model="row.testValue" clearable/> |
| | | </template> |
| | | </PIMTable> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitForm">确认</el-button> |
| | | <el-button @click="closeDialog">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, toRefs, getCurrentInstance, nextTick } from "vue"; |
| | | import { userListNoPage } from "@/api/system/user.js"; |
| | | import { batchQuickInspect } from "@/api/qualityManagement/rawMaterialInspection.js"; |
| | | import { qualityInspectDetailByProductId, getQualityTestStandardParamByTestStandardId } from "@/api/qualityManagement/metricMaintenance.js"; |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | const emit = defineEmits(['close', 'success']); |
| | | |
| | | const dialogVisible = ref(false); |
| | | const userList = ref([]); |
| | | const selectedIds = ref([]); |
| | | const selectedRows = ref([]); |
| | | const testStandardOptions = ref([]); |
| | | const inspectType = ref(0); // åæææ£éªç±»å |
| | | |
| | | const data = reactive({ |
| | | form: { |
| | | checkResult: '', |
| | | testStandardId: '', |
| | | quantity: undefined, |
| | | qualifiedQuantity: undefined, |
| | | unqualifiedQuantity: undefined, |
| | | checkCompany: '', |
| | | checkName: '', |
| | | checkTime: '', |
| | | }, |
| | | rules: { |
| | | checkResult: [{ required: true, message: "è¯·éæ©æ£æµç»æ", trigger: "change" }], |
| | | testStandardId: [{ required: true, message: "è¯·éæ©ææ ", trigger: "change" }], |
| | | quantity: [{ required: true, message: "请è¾å
¥æ°é", trigger: "blur" }], |
| | | qualifiedQuantity: [{ required: true, message: "请è¾å
¥åæ ¼æ°é", trigger: "blur" }], |
| | | unqualifiedQuantity: [{ required: true, message: "请è¾å
¥ä¸åæ ¼æ°é", trigger: "blur" }], |
| | | checkCompany: [{ required: true, message: "请è¾å
¥æ£æµåä½", trigger: "blur" }], |
| | | checkName: [{ required: true, message: "è¯·éæ©æ£éªå", trigger: "change" }], |
| | | checkTime: [{ required: true, message: "è¯·éæ©æ£æµæ¥æ", trigger: "change" }], |
| | | }, |
| | | }); |
| | | const { form, rules } = toRefs(data); |
| | | |
| | | const tableColumn = ref([ |
| | | { |
| | | label: "ææ ", |
| | | prop: "parameterItem", |
| | | }, |
| | | { |
| | | label: "åä½", |
| | | prop: "unit", |
| | | }, |
| | | { |
| | | label: "æ åå¼", |
| | | prop: "standardValue", |
| | | }, |
| | | { |
| | | label: "å
æ§å¼", |
| | | prop: "controlValue", |
| | | }, |
| | | { |
| | | label: "æ£éªå¼", |
| | | prop: "testValue", |
| | | dataType: 'slot', |
| | | slot: 'slot', |
| | | }, |
| | | ]); |
| | | const tableData = ref([]); |
| | | const tableLoading = ref(false); |
| | | |
| | | // æå¼å¼¹æ¡ |
| | | const openDialog = async (ids, rows) => { |
| | | selectedIds.value = ids; |
| | | selectedRows.value = rows; |
| | | dialogVisible.value = true; |
| | | |
| | | // å è½½ç¨æ·å表 |
| | | const userListsRes = await userListNoPage(); |
| | | userList.value = userListsRes.data; |
| | | |
| | | // å è½½ææ éé¡¹ï¼æ ¹æ®ç¬¬ä¸ä¸ªéä¸è¡ç产åIDï¼ |
| | | if (rows && rows.length > 0 && rows[0].productId) { |
| | | const params = { |
| | | productId: rows[0].productId, |
| | | inspectType: 0 |
| | | }; |
| | | const res = await qualityInspectDetailByProductId(params); |
| | | testStandardOptions.value = res.data || []; |
| | | } else { |
| | | testStandardOptions.value = []; |
| | | } |
| | | |
| | | // é置表å |
| | | form.value = { |
| | | checkResult: '', |
| | | testStandardId: '', |
| | | quantity: undefined, |
| | | qualifiedQuantity: undefined, |
| | | unqualifiedQuantity: undefined, |
| | | checkCompany: '', |
| | | checkName: '', |
| | | checkTime: '', |
| | | }; |
| | | tableData.value = []; |
| | | |
| | | await nextTick(); |
| | | proxy.$refs.formRef?.clearValidate(); |
| | | }; |
| | | |
| | | // ææ éæ©ååå¤ç |
| | | const handleTestStandardChange = async (testStandardId) => { |
| | | if (!testStandardId) { |
| | | tableData.value = []; |
| | | return; |
| | | } |
| | | tableLoading.value = true; |
| | | try { |
| | | const res = await getQualityTestStandardParamByTestStandardId(testStandardId); |
| | | tableData.value = (res.data || []).map(item => ({ |
| | | ...item, |
| | | id: null, |
| | | testValue: '' |
| | | })); |
| | | } catch (error) { |
| | | console.error('è·åæ ååæ°å¤±è´¥:', error); |
| | | tableData.value = []; |
| | | } finally { |
| | | tableLoading.value = false; |
| | | } |
| | | }; |
| | | |
| | | // æ£æµç»æååå¤ç |
| | | const handleCheckResultChange = (value) => { |
| | | if (value === 'åæ ¼') { |
| | | // åæ ¼æ¶ï¼åæ ¼æ°éçäºæ°éï¼ä¸åæ ¼æ°é为0 |
| | | form.value.qualifiedQuantity = form.value.quantity || 0; |
| | | form.value.unqualifiedQuantity = 0; |
| | | } else if (value === 'ä¸åæ ¼') { |
| | | // ä¸åæ ¼æ¶ï¼åæ ¼æ°é为0ï¼ä¸åæ ¼æ°éçäºæ°é |
| | | form.value.qualifiedQuantity = 0; |
| | | form.value.unqualifiedQuantity = form.value.quantity || 0; |
| | | } |
| | | }; |
| | | |
| | | // æ°éååå¤ç |
| | | const handleQuantityChange = (value) => { |
| | | if (form.value.checkResult === 'åæ ¼') { |
| | | form.value.qualifiedQuantity = value || 0; |
| | | form.value.unqualifiedQuantity = 0; |
| | | } else if (form.value.checkResult === 'ä¸åæ ¼') { |
| | | form.value.qualifiedQuantity = 0; |
| | | form.value.unqualifiedQuantity = value || 0; |
| | | } |
| | | }; |
| | | |
| | | // åæ ¼æ°éååå¤ç |
| | | const handleQualifiedQuantityChange = (value) => { |
| | | const quantity = form.value.quantity || 0; |
| | | if (value > quantity) { |
| | | proxy.$modal.msgWarning("åæ ¼æ°éä¸è½å¤§äºæ»æ°é"); |
| | | form.value.qualifiedQuantity = quantity; |
| | | form.value.unqualifiedQuantity = 0; |
| | | } else { |
| | | form.value.unqualifiedQuantity = Number((quantity - value).toFixed(2)); |
| | | } |
| | | updateCheckResult(); |
| | | }; |
| | | |
| | | // ä¸åæ ¼æ°éååå¤ç |
| | | const handleUnqualifiedQuantityChange = (value) => { |
| | | const quantity = form.value.quantity || 0; |
| | | if (value > quantity) { |
| | | proxy.$modal.msgWarning("ä¸åæ ¼æ°éä¸è½å¤§äºæ»æ°é"); |
| | | form.value.unqualifiedQuantity = quantity; |
| | | form.value.qualifiedQuantity = 0; |
| | | } else { |
| | | form.value.qualifiedQuantity = Number((quantity - value).toFixed(2)); |
| | | } |
| | | updateCheckResult(); |
| | | }; |
| | | |
| | | // æ ¹æ®åæ ¼/ä¸åæ ¼æ°éæ´æ°æ£æµç»æ |
| | | const updateCheckResult = () => { |
| | | const qualified = form.value.qualifiedQuantity || 0; |
| | | const unqualified = form.value.unqualifiedQuantity || 0; |
| | | const quantity = form.value.quantity || 0; |
| | | |
| | | if (qualified === quantity && unqualified === 0) { |
| | | form.value.checkResult = 'åæ ¼'; |
| | | } else if (unqualified === quantity && qualified === 0) { |
| | | form.value.checkResult = 'ä¸åæ ¼'; |
| | | } else if (qualified > 0 && unqualified > 0) { |
| | | form.value.checkResult = 'é¨ååæ ¼'; |
| | | } |
| | | }; |
| | | |
| | | // æäº¤è¡¨å |
| | | const submitForm = () => { |
| | | proxy.$refs.formRef.validate((valid) => { |
| | | if (valid) { |
| | | const data = { |
| | | ids: selectedIds.value, |
| | | inspectType: inspectType.value, |
| | | ...form.value, |
| | | paramList: tableData.value |
| | | }; |
| | | batchQuickInspect(data).then(res => { |
| | | proxy.$modal.msgSuccess(res.msg || "å¿«éæ£éªå®æ"); |
| | | emit('success'); |
| | | closeDialog(); |
| | | }); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // å
³éå¼¹æ¡ |
| | | const closeDialog = () => { |
| | | dialogVisible.value = false; |
| | | emit('close'); |
| | | }; |
| | | |
| | | defineExpose({ |
| | | openDialog, |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | </style> |
| | |
| | | @close="handleQuery"></FormDia> |
| | | <files-dia ref="filesDia" |
| | | @close="handleQuery"></files-dia> |
| | | <QuickCheckDia ref="quickCheckDia" |
| | | @close="handleQuery" |
| | | @success="getList"></QuickCheckDia> |
| | | <el-dialog v-model="dialogFormVisible" |
| | | title="ç¼è¾æ£éªå" |
| | | width="30%" |
| | |
| | | } from "vue"; |
| | | import InspectionFormDia from "@/views/qualityManagement/rawMaterialInspection/components/inspectionFormDia.vue"; |
| | | import FormDia from "@/views/qualityManagement/rawMaterialInspection/components/formDia.vue"; |
| | | import QuickCheckDia from "@/views/qualityManagement/rawMaterialInspection/components/quickCheckDia.vue"; |
| | | import { ElMessageBox } from "element-plus"; |
| | | import { |
| | | downloadQualityInspect, |
| | |
| | | qualityInspectListPage, |
| | | qualityInspectUpdate, |
| | | submitQualityInspect, |
| | | batchQuickInspect, |
| | | } from "@/api/qualityManagement/rawMaterialInspection.js"; |
| | | import FilesDia from "@/views/qualityManagement/rawMaterialInspection/components/filesDia.vue"; |
| | | import dayjs from "dayjs"; |
| | |
| | | const formDia = ref(); |
| | | const filesDia = ref(); |
| | | const inspectionFormDia = ref(); |
| | | const quickCheckDia = ref(); |
| | | const { proxy } = getCurrentInstance(); |
| | | const userStore = useUserStore(); |
| | | const changeDaterange = value => { |
| | |
| | | return; |
| | | } |
| | | |
| | | const totalCount = selectedRows.value.length; |
| | | const unSubmittedCount = unSubmittedRows.length; |
| | | const submittedCount = totalCount - unSubmittedCount; |
| | | |
| | | let confirmMessage = `已鿩 ${totalCount} æ¡æ£éªå`; |
| | | if (submittedCount > 0) { |
| | | confirmMessage += `ï¼å
¶ä¸ ${submittedCount} æ¡å·²æäº¤ï¼å°èªå¨è·³è¿ï¼`; |
| | | } |
| | | confirmMessage += `\n\n确认åå°èªå¨ï¼\n· æ£éªç»æè®¾ä¸º"åæ ¼"\n· åæ ¼æ°éè®¾ä¸ºæ»æ°\n· ä¸åæ ¼æ°é设为 0\n· æäº¤å¹¶å
¥åº`; |
| | | |
| | | ElMessageBox.confirm(confirmMessage, "å¿«éæ£éª", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | dangerouslyUseHTMLString: false, |
| | | }) |
| | | .then(() => { |
| | | // è°ç¨æ¹éå¿«éæ£éªæ¥å£ |
| | | const ids = unSubmittedRows.map(item => item.id); |
| | | batchQuickInspect(ids).then(res => { |
| | | proxy.$modal.msgSuccess(res.msg || "å¿«éæ£éªå®æ"); |
| | | getList(); |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | const ids = unSubmittedRows.map(item => item.id); |
| | | quickCheckDia.value?.openDialog(ids, unSubmittedRows); |
| | | }; |
| | | // æå¼éä»¶å¼¹æ¡ |
| | | const openFilesFormDia = (type, row) => { |
| | |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | label="åºåºæ¹æ¬¡" |
| | | prop="outboundBatches" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | label="åè´§æ¶é´" |
| | | prop="shippingDate" |
| | | show-overflow-tooltip |
| | |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="24" v-if="deliveryForm.type === '货车'"> |
| | | <el-form-item label="åºåºæ¹å·ï¼" prop="outboundBatches"> |
| | | <el-input |
| | | v-model="deliveryForm.outboundBatches" |
| | | placeholder="请è¾å
¥åºåºæ¹å·" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="24" v-else> |
| | | <el-form-item label="å¿«éå
¬å¸ï¼" prop="expressCompany"> |
| | | <el-input |
| | |
| | | expressCompany: "", |
| | | expressNumber: "", |
| | | type: "货车", // 货车, å¿«é |
| | | outboundBatches: "", |
| | | }, |
| | | deliveryRules: { |
| | | shippingCarNumber: [ |
| | |
| | | type: "货车", |
| | | batchNo: [], |
| | | batchNoList, |
| | | outboundBatches: "", |
| | | }; |
| | | deliveryFileList.value = []; |
| | | deliveryFormVisible.value = true; |
| | |
| | | deliveryForm.value.type === "货车" |
| | | ? deliveryForm.value.shippingCarNumber |
| | | : "", |
| | | outboundBatches: |
| | | deliveryForm.value.type === "货车" |
| | | ? deliveryForm.value.outboundBatches |
| | | : "", |
| | | expressCompany: |
| | | deliveryForm.value.type === "å¿«é" |
| | | ? deliveryForm.value.expressCompany |
| | |
| | | </div> |
| | | </section> |
| | | |
| | | <!-- ç产模åå·²éè |
| | | <section class="section-card section-card--bottom"> |
| | | <article class="module-title module-title--produce"> |
| | | <div class="module-title__icon"> |
| | |
| | | </template> |
| | | </div> |
| | | </section> |
| | | --> |
| | | |
| | | <section class="section-card section-card--bottom"> |
| | | <article class="module-title module-title--store"> |