| src/assets/BI/biaoti.png | 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/store/modules/user.js | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/inspectionManagement/components/formDia.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/inspectionManagement/components/qrCodeDia.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/inspectionManagement/components/viewFiles.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/inspectionManagement/components/viewQrCodeFiles.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/inspectionManagement/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/reportAnalysis/dataDashboard/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
src/assets/BI/biaoti.pngsrc/store/modules/user.js
@@ -2,6 +2,7 @@ import { getToken, setToken, removeToken } from '@/utils/auth' import { isHttp, isEmpty } from "@/utils/validate" import defAva from '@/assets/images/profile.jpg' import { defineStore } from 'pinia' const useUserStore = defineStore( 'user', src/views/inspectionManagement/components/formDia.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,174 @@ <template> <div> <el-dialog :title="operationType === 'add' ? 'æ°å¢å·¡æ£ä»»å¡' : 'ç¼è¾å·¡æ£ä»»å¡'" v-model="dialogVisitable" width="800px" @close="cancel"> <el-form :model="form" :rules="rules" ref="formRef" label-width="120px"> <el-row> <el-col :span="12"> <el-form-item label="ä»»å¡åç§°" prop="taskName"> <el-input v-model="form.taskName" placeholder="请è¾å ¥ä»»å¡åç§°" maxlength="30" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="å°ç¹" prop="inspectionLocation"> <el-input v-model="form.inspectionLocation" placeholder="请è¾å ¥å°ç¹" maxlength="30" /> </el-form-item> </el-col> </el-row> <el-row> <el-col :span="12"> <el-form-item label="å·¡æ£äºº" prop="inspector"> <el-select v-model="form.inspector" placeholder="è¯·éæ©" multiple clearable> <el-option v-for="item in userList" :label="item.nickName" :value="item.userId" :key="item.userId"/> </el-select> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="夿³¨" prop="remarks"> <el-input v-model="form.remarks" placeholder="请è¾å ¥å¤æ³¨" type="textarea" /> </el-form-item> </el-col> </el-row> <el-row> <el-col :span="12"> <el-form-item label="ä»»å¡é¢ç" prop="frequencyType"> <el-select v-model="form.frequencyType" placeholder="è¯·éæ©" clearable> <el-option label="æ¯æ¥" value="DAILY"/> <el-option label="æ¯å¨" value="WEEKLY"/> <el-option label="æ¯æ" value="MONTHLY"/> <el-option label="å£åº¦" value="QUARTERLY"/> </el-select> </el-form-item> </el-col> <el-col :span="12" v-if="form.frequencyType === 'DAILY' && form.frequencyType"> <el-form-item label="æ¥æ" prop="frequencyDetail"> <el-time-picker v-model="form.frequencyDetail" placeholder="éæ©æ¶é´" format="HH:mm" value-format="HH:mm" /> </el-form-item> </el-col> <el-col :span="12" v-if="form.frequencyType === 'WEEKLY' && form.frequencyType"> <el-form-item label="æ¥æ" prop="frequencyDetail"> <el-select v-model="form.week" placeholder="è¯·éæ©" clearable style="width: 50%"> <el-option label="å¨ä¸" value="MON"/> <el-option label="å¨äº" value="TUE"/> <el-option label="å¨ä¸" value="WED"/> <el-option label="å¨å" value="THU"/> <el-option label="å¨äº" value="FRI"/> <el-option label="å¨å " value="SAT"/> <el-option label="卿¥" value="SUN"/> </el-select> <el-time-picker v-model="form.time" placeholder="éæ©æ¶é´" format="HH:mm" value-format="HH:mm" style="width: 50%"/> </el-form-item> </el-col> <el-col :span="12" v-if="form.frequencyType === 'MONTHLY' && form.frequencyType"> <el-form-item label="æ¥æ" prop="frequencyDetail"> <el-date-picker v-model="form.frequencyDetail" type="datetime" clearable placeholder="éæ©å¼å§æ¥æ" format="DD,HH:mm" value-format="DD,HH:mm" /> </el-form-item> </el-col> <el-col :span="12" v-if="form.frequencyType === 'QUARTERLY' && form.frequencyType"> <el-form-item label="æ¥æ" prop="frequencyDetail"> <el-date-picker v-model="form.frequencyDetail" type="datetime" clearable placeholder="éæ©å¼å§æ¥æ" format="MM,DD,HH:mm" value-format="MM,DD,HH:mm" /> </el-form-item> </el-col> </el-row> </el-form> <template #footer> <div class="dialog-footer"> <el-button @click="cancel">åæ¶</el-button> <el-button type="primary" @click="submitForm">ä¿å</el-button> </div> </template> </el-dialog> </div> </template> <script setup> import {reactive, ref} from "vue"; import useUserStore from '@/store/modules/user' import {addOrEditTimingTask} from "@/api/inspectionManagement/index.js"; import {userListAll} from "@/api/publicApi/index.js"; const { proxy } = getCurrentInstance() const emit = defineEmits() const userStore = useUserStore() const dialogVisitable = ref(false); const operationType = ref('add'); const data = reactive({ form: { taskName: '', inspectionLocation: '', inspector: '', inspectorIds: '', remarks: '', frequencyType: '', frequencyDetail: '', }, rules: { taskName: [{ required: true, message: "请è¾å ¥ä»»å¡åç§°", trigger: "blur" },], inspectionLocation: [{ required: true, message: "请è¾å ¥å°ç¹", trigger: "blur" },], inspector: [{ required: true, message: "请è¾å ¥å·¡æ£äºº", trigger: "blur" },], } }) const { form, rules } = toRefs(data) const userList = ref([]) // æå¼å¼¹æ¡ const openDialog = async (type, row) => { dialogVisitable.value = true userListAll().then(res => { userList.value = res.data }) if (type === 'edit') { form.value = {...row} form.value.inspector = form.value.inspectorIds.split(',').map(Number) } } // å ³éå并表å const cancel = () => { proxy.resetForm("formRef") dialogVisitable.value = false emit('closeDia') } // æäº¤å并表å const submitForm = () => { proxy.$refs["formRef"].validate(async valid => { if (valid) { form.value.inspectorIds = form.value.inspector.join(',') delete form.value.inspector if (form.value.frequencyType === 'WEEKLY') { let frequencyDetail = '' frequencyDetail = form.value.week + ',' + form.value.time form.value.frequencyDetail = frequencyDetail } let res = await userStore.getInfo() form.value.registrantId = res.user.userId addOrEditTimingTask(form.value).then(() => { cancel() proxy.$modal.msgSuccess('æäº¤æå') }) } }) } defineExpose({ openDialog }) </script> <style scoped> </style> src/views/inspectionManagement/components/qrCodeDia.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,132 @@ <template> <div> <el-dialog :title="operationType === 'add' ? 'æ°å¢äºç»´ç ' : 'ç¼è¾äºç»´ç '" v-model="dialogVisitable" width="500px" @close="cancel"> <el-form :model="form" :rules="rules" ref="formRef" label-width="120px"> <el-row> <el-col :span="24"> <el-form-item label="设å¤åç§°" prop="deviceName"> <el-input v-model="form.deviceName" placeholder="请è¾å ¥è®¾å¤åç§°" maxlength="30" /> </el-form-item> </el-col> </el-row> <el-row> <el-col :span="24"> <el-form-item label="æå¨ä½ç½®æè¿°" prop="location"> <el-input v-model="form.location" placeholder="请è¾å ¥æå¨ä½ç½®æè¿°" maxlength="30"/> </el-form-item> </el-col> </el-row> </el-form> <div> <el-button type="primary" @click="submitForm">çæå¹¶æå°äºç»´ç </el-button> </div> <div v-if="isShowQrCode" class="print-section" ref="qrCodeContainer" id="qrCodeContainer"> <vue-qrcode :value="qrCodeValue" :width="qrCodeSize"></vue-qrcode> </div> </el-dialog> </div> </template> <script setup> import useUserStore from "@/store/modules/user.js"; import {reactive, ref} from "vue"; import printJS from 'print-js'; import {addOrEditQrCode} from "@/api/inspectionUpload/index.js"; const { proxy } = getCurrentInstance() const emit = defineEmits() const userStore = useUserStore() const dialogVisitable = ref(false); const isShowQrCode = ref(false); const operationType = ref('add'); const qrCodeValue = ref(''); const qrCodeSize = ref(100); const data = reactive({ form: { deviceName: '', location: '', qrCodeId: '', id: '' }, rules: { deviceName: [{ required: true, message: '请è¾å ¥è®¾å¤åç§°', trigger: 'blur' }], location: [{ required: true, message: '请è¾å ¥å°ç¹', trigger: 'blur' }] } }) const { form, rules } = toRefs(data) // æå¼å¼¹æ¡ const openDialog = async (type, row) => { dialogVisitable.value = true qrCodeValue.value = '' isShowQrCode.value = false; if (type === 'edit') { form.value.id = row.id form.value.qrCodeId = row.id form.value.deviceName = row.deviceName form.value.location = row.location // å°è¡¨åæ°æ®è½¬ä¸º JSON å符串ä½ä¸ºäºç»´ç å 容 qrCodeValue.value = JSON.stringify(form.value); isShowQrCode.value = true; } } // æäº¤å并表å const submitForm = () => { proxy.$refs["formRef"].validate(valid => { if (valid) { addOrEditQrCode(form.value).then((res) => { form.value.qrCodeId = res.data }) // å°è¡¨åæ°æ®è½¬ä¸º JSON å符串ä½ä¸ºäºç»´ç å 容 qrCodeValue.value = JSON.stringify(form.value); isShowQrCode.value = true; showQrCode() } }) } const showQrCode = () => { // å»¶è¿æ§è¡æå°ï¼é¿å DOM æ´æ°åå°±è°ç¨æå° setTimeout(() => { printJS({ printable: 'qrCodeContainer',//é¡µé¢ type: "html",//ææ¡£ç±»å maxWidth: 360, style: `@page { margin:0; size: 400px 75px collapse; margin-top:3px; &:first-of-type{ margin-top:0 !important; } } html{ zoom:100%; } @media print{ width: 400px; height: 75px; margin:0; }`, targetStyles: ["*"], // 使ç¨domçæææ ·å¼ï¼å¾éè¦ font_size: '0.20cm', }); }, 300); } // å ³éå并表å const cancel = () => { proxy.resetForm("formRef") dialogVisitable.value = false emit('closeDia') } defineExpose({ openDialog }) </script> <style scoped> .print-section { text-align: center; margin-top: 30px; } </style> src/views/inspectionManagement/components/viewFiles.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,246 @@ <template> <div> <el-dialog title="æ¥çéä»¶" v-model="dialogVisitable" width="800px" @close="cancel"> <div class="upload-container"> <!-- ç产å --> <div class="form-container"> <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')" :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" :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 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')" 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> </el-dialog> <!-- ç»ä¸åªä½æ¥çå¨ --> <div v-if="isMediaViewerVisible" class="media-viewer-overlay" @click.self="closeMediaViewer"> <div class="media-viewer-content" @click.stop> <!-- å¾ç --> <vue-easy-lightbox v-if="mediaType === 'image'" :visible="isMediaViewerVisible" :imgs="mediaList" :index="currentMediaIndex" @hide="closeMediaViewer" ></vue-easy-lightbox> <!-- è§é¢ --> <div v-else-if="mediaType === 'video'" style="position: relative;"> <Video :src="mediaList[currentMediaIndex]" autoplay controls style="max-width: 90vw; max-height: 80vh;" /> </div> </div> </div> </div> </template> <script setup> import { ref } from 'vue'; import VueEasyLightbox from 'vue-easy-lightbox'; // æ§å¶å¼¹çªæ¾ç¤º const dialogVisitable = ref(false); // å¾çæ°ç» const beforeProductionImgs = ref([]); const afterProductionImgs = ref([]); const productionIssuesImgs = ref([]); // è§é¢æ°ç» const beforeProductionVideos = ref([]); const afterProductionVideos = ref([]); const productionIssuesVideos = ref([]); // åªä½æ¥çå¨ç¶æ const isMediaViewerVisible = ref(false); const currentMediaIndex = ref(0); const mediaList = ref([]); // åå¨å½åè¦æ¥ççåªä½å表ï¼å«å¾çåè§é¢å¯¹è±¡ï¼ const mediaType = ref('image'); // image | video // å¤çæ¯ä¸ç±»æ°æ®ï¼å离å¾çåè§é¢ function processItems(items) { const images = []; const videos = []; items.forEach(item => { if (item.contentType?.startsWith('image/')) { images.push(item.url); } else if (item.contentType?.startsWith('video/')) { videos.push(item.url); } }); return { images, videos }; } // æå¼å¼¹çªå¹¶å è½½æ°æ® const openDialog = async (row) => { const { images: beforeImgs, videos: beforeVids } = processItems(row.beforeProduction); const { images: afterImgs, videos: afterVids } = processItems(row.afterProduction); 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; dialogVisitable.value = true; }; // æ¾ç¤ºåªä½ï¼å¾ç or è§é¢ï¼ function showMedia(mediaArray, index, type) { mediaList.value = mediaArray; currentMediaIndex.value = index; mediaType.value = type; isMediaViewerVisible.value = true; } // å ³éåªä½æ¥çå¨ function closeMediaViewer() { isMediaViewerVisible.value = false; mediaList.value = []; mediaType.value = 'image'; } // 表åå ³éæ¹æ³ const cancel = () => { dialogVisitable.value = false; }; defineExpose({ openDialog }); </script> <style scoped lang="scss"> .upload-container { display: flex; flex-direction: column; align-items: center; padding: 20px; border: 1px solid #dcdfe6; box-sizing: border-box; .form-container { flex: 1; width: 100%; margin-bottom: 20px; } } .title { font-size: 14px; color: #165dff; line-height: 20px; font-weight: 600; padding-left: 10px; position: relative; margin: 6px 0; &::before { content: ""; position: absolute; left: 0; top: 3px; width: 4px; height: 14px; background-color: #165dff; } } .media-viewer-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0, 0, 0, 0.8); z-index: 9999; display: flex; align-items: center; justify-content: center; } .media-viewer-content { position: relative; max-width: 90vw; max-height: 90vh; overflow: hidden; } </style> src/views/inspectionManagement/components/viewQrCodeFiles.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,169 @@ <template> <div> <el-dialog title="æ¥çéä»¶" v-model="dialogVisitable" width="800px" @close="cancel"> <div class="upload-container"> <div class="form-container"> <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')" :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" :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> </el-dialog> <!-- ç»ä¸åªä½æ¥çå¨ --> <div v-if="isMediaViewerVisible" class="media-viewer-overlay" @click.self="closeMediaViewer"> <div class="media-viewer-content" @click.stop> <!-- å¾ç --> <vue-easy-lightbox v-if="mediaType === 'image'" :visible="isMediaViewerVisible" :imgs="mediaList" :index="currentMediaIndex" @hide="closeMediaViewer" ></vue-easy-lightbox> <!-- è§é¢ --> <div v-else-if="mediaType === 'video'" style="position: relative;"> <Video :src="mediaList[currentMediaIndex]" autoplay controls style="max-width: 90vw; max-height: 80vh;" /> </div> </div> </div> </div> </template> <script setup> // æ§å¶å¼¹çªæ¾ç¤º import VueEasyLightbox from "vue-easy-lightbox"; const dialogVisitable = ref(false); // å¾çæ°ç» const beforeProductionImgs = ref([]); // è§é¢æ°ç» const beforeProductionVideos = ref([]); // åªä½æ¥çå¨ç¶æ const isMediaViewerVisible = ref(false); const currentMediaIndex = ref(0); const mediaList = ref([]); // åå¨å½åè¦æ¥ççåªä½å表ï¼å«å¾çåè§é¢å¯¹è±¡ï¼ const mediaType = ref('image'); // image | video // æå¼å¼¹çªå¹¶å è½½æ°æ® const openDialog = async (row) => { const { images: beforeImgs, videos: beforeVids } = processItems(row.storageBlobDTO); beforeProductionImgs.value = beforeImgs; beforeProductionVideos.value = beforeVids; dialogVisitable.value = true; }; // æ¾ç¤ºåªä½ï¼å¾ç or è§é¢ï¼ function showMedia(mediaArray, index, type) { mediaList.value = mediaArray; currentMediaIndex.value = index; mediaType.value = type; isMediaViewerVisible.value = true; } // å ³éåªä½æ¥çå¨ function closeMediaViewer() { isMediaViewerVisible.value = false; mediaList.value = []; mediaType.value = 'image'; } // 表åå ³éæ¹æ³ const cancel = () => { dialogVisitable.value = false; }; // å¤çæ¯ä¸ç±»æ°æ®ï¼å离å¾çåè§é¢ function processItems(items) { const images = []; const videos = []; items.forEach(item => { if (item.contentType?.startsWith('image/')) { images.push(item.url); } else if (item.contentType?.startsWith('video/')) { videos.push(item.url); } }); return { images, videos }; } defineExpose({ openDialog }); </script> <style scoped lang="scss"> .upload-container { display: flex; flex-direction: column; align-items: center; padding: 20px; border: 1px solid #dcdfe6; box-sizing: border-box; .form-container { flex: 1; width: 100%; margin-bottom: 20px; } } .title { font-size: 14px; color: #165dff; line-height: 20px; font-weight: 600; padding-left: 10px; position: relative; margin: 6px 0; &::before { content: ""; position: absolute; left: 0; top: 3px; width: 4px; height: 14px; background-color: #165dff; } } .media-viewer-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0, 0, 0, 0.8); z-index: 9999; display: flex; align-items: center; justify-content: center; } .media-viewer-content { position: relative; max-width: 90vw; max-height: 90vh; overflow: hidden; } </style> src/views/inspectionManagement/index.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,391 @@ <template> <div class="app-container"> <el-form :inline="true" :model="queryParams" class="search-form"> <el-form-item label="æç´¢"> <el-input v-model="queryParams.searchAll" placeholder="请è¾å ¥å ³é®å" clearable :style="{ width: '100%' }" /> </el-form-item> <el-form-item> <el-button type="primary" @click="handleQuery">æ¥è¯¢</el-button> <el-button @click="resetQuery">éç½®</el-button> </el-form-item> </el-form> <el-card> <!-- æ ç¾é¡µ --> <el-tabs v-model="activeTab" class="info-tabs" @tab-click="handleTabClick" > <el-tab-pane v-for="tab in tabs" :key="tab.name" :label="tab.label" :name="tab.name" /> </el-tabs> <div style="display: flex;flex-direction: row;justify-content: space-between;" v-if="tabName === 'task'"> <el-radio-group v-model="activeRadio" @change="radioChange"> <el-radio-button v-for="tab in radios" :key="tab.name" :label="tab.label" :value="tab.name"/> </el-radio-group> <!-- æä½æé®åº --> <el-space v-if="activeRadio !== 'task'"> <el-button type="primary" :icon="Plus" @click="handleAdd(undefined)">æ°å»º</el-button> <el-button type="danger" :icon="Delete" @click="handleDelete">å é¤</el-button> <!-- <el-button type="info" plain :icon="Download">导åº</el-button> --> </el-space> </div> <div> <div> <ETable :loading="tableLoading" :table-data="tableData" :columns="tableColumns" @selection-change="handleSelectionChange" :show-selection="true" :border="true" style="width: 100%;height: calc(100vh - 30em)" operationsWidth="130" :operations="operationsArr" @edit="handleAdd" @viewFile="viewFile" v-if="tabName === 'task'" > <template #inspector="{ row }"> <div class="person-tags"> <!-- è°è¯ä¿¡æ¯ï¼ä¸çº¿æ¶å é¤ --> <!-- {{ console.log('inspector data:', row.inspector) }} --> <template v-if="row.inspector && row.inspector.length > 0"> <el-tag v-for="(person, index) in row.inspector" :key="index" size="small" type="primary" class="person-tag" > {{ person }} </el-tag> </template> <span v-else class="no-data">--</span> </div> </template> </ETable> <el-table ref="table" :data="tableData" height="480" v-loading="tableLoading" border v-else style="width: 100%;height: calc(100vh - 25em)"> <el-table-column label="åºå·" type="index" width="60" align="center" /> <el-table-column prop="deviceName" label="设å¤åç§°" :show-overflow-tooltip="true"> <template #default="scope"> {{scope.row.qrCode.deviceName}} </template> </el-table-column> <el-table-column prop="location" label="æå¨ä½ç½®æè¿°" :show-overflow-tooltip="true"> <template #default="scope"> {{scope.row.qrCode.location}} </template> </el-table-column> <el-table-column prop="scanner" label="å·¡æ£äºº"></el-table-column> <el-table-column prop="scanTime" label="å·¡æ£æ¶é´"></el-table-column> <el-table-column fixed="right" label="æä½"> <template #default="scope"> <el-button link type="primary" @click="handleAdd(scope.row)">æ¥çéä»¶</el-button> </template> </el-table-column> </el-table> </div> <pagination v-if="total>0" :page="pageNum" :limit="pageSize" :total="total" @pagination="handlePagination" :layout="'total, prev, pager, next, jumper'" /> </div> </el-card> <form-dia ref="formDia" @closeDia="handleQuery"></form-dia> <qr-code-dia ref="qrCodeDia" @closeDia="handleQuery"></qr-code-dia> <view-files ref="viewFiles"></view-files> <view-qr-code-files ref="viewQrCodeFiles"></view-qr-code-files> </div> </template> <script setup> import { Delete, Plus } from "@element-plus/icons-vue"; import { onMounted, ref, reactive, getCurrentInstance, nextTick } from "vue"; // ç»ä»¶å¼å ¥ import Pagination from "@/components/Pagination/index.vue"; import ETable from "@/components/Table/ETable.vue"; import FormDia from "@/views/inspectionManagement/components/formDia.vue"; import QrCodeDia from "@/views/inspectionManagement/components/qrCodeDia.vue"; import ViewFiles from "@/views/inspectionManagement/components/viewFiles.vue"; import ViewQrCodeFiles from "@/views/inspectionManagement/components/viewQrCodeFiles.vue"; // æ¥å£å¼å ¥ import { delTimingTask, inspectionTaskList, timingTaskList } from "@/api/inspectionManagement/index.js"; import { delQrCode, qrCodeList, qrCodeScanRecordList } from "@/api/inspectionUpload/index.js"; // å ¨å±åé const { proxy } = getCurrentInstance(); const formDia = ref(); const qrCodeDia = ref(); const viewFiles = ref(); const viewQrCodeFiles = ref(); // æ¥è¯¢åæ° const queryParams = reactive({ searchAll: "", }); // æ ç¾é¡µé ç½® const activeTab = ref("task"); const tabName = ref("task"); const tabs = reactive([ { name: "task", label: "ç产巡æ£" }, { name: "qrCodeScanRecord", label: "ç°åºå·¡æ£è®°å½" }, ]); // åéæ¡é ç½® const activeRadio = ref("taskManage"); const radios = reactive([ { name: "taskManage", label: "宿¶ä»»å¡ç®¡ç" }, { name: "task", label: "宿¶ä»»å¡è®°å½" }, { name: "qrCode", label: "äºç»´ç 管ç" }, ]); // è¡¨æ ¼æ°æ® const selectedRows = ref([]); const tableData = ref([]); const operationsArr = ref([]); const tableColumns = ref([]); const tableLoading = ref(false); const total = ref(0); const pageNum = ref(1); const pageSize = ref(10); // åé ç½® const columns = ref([ { prop: "taskName", label: "å·¡æ£ä»»å¡åç§°", minWidth: 160 }, { prop: "inspectionLocation", label: "å°ç¹", minWidth: 120 }, { prop: "remarks", label: "夿³¨", minWidth: 150 }, { prop: "inspector", label: "æ§è¡å·¡æ£äºº", minWidth: 150, slot: "inspector" }, { prop: "frequencyType", label: "颿¬¡", minWidth: 150, formatter: (_, __, val) => ({ DAILY: "æ¯æ¥", WEEKLY: "æ¯å¨", MONTHLY: "æ¯æ", QUARTERLY: "å£åº¦" }[val] || "") }, { prop: "frequencyDetail", label: "å¼å§æ¥æä¸æ¶é´", minWidth: 150, formatter: (row, column, cellValue) => { // å 夿æ¯å¦æ¯å符串 if (typeof cellValue !== 'string') return ''; let val = cellValue; const replacements = { MON: 'å¨ä¸', TUE: 'å¨äº', WED: 'å¨ä¸', THU: 'å¨å', FRI: 'å¨äº', SAT: 'å¨å ', SUN: '卿¥' }; // ä½¿ç¨æ£å䏿¬¡æ§æ¿æ¢ææå¹é 项 return val.replace(/MON|TUE|WED|THU|FRI|SAT|SUN/g, match => replacements[match]); } }, { prop: "registrant", label: "ç»è®°äºº", minWidth: 100 }, { prop: "createTime", label: "ç»è®°æ¥æ", minWidth: 100 }, ]); const columns1 = ref([ { prop: "deviceName", label: "设å¤åç§°", minWidth: 160 }, { prop: "location", label: "æå¨ä½ç½®æè¿°", minWidth: 120 }, { prop: "createBy", label: "å建è ", minWidth: 100 }, { prop: "createTime", label: "å建æ¶é´", minWidth: 100 }, ]); onMounted(() => { radioChange('taskManage'); }); // æ ç¾é¡µç¹å»äºä»¶ const handleTabClick = (tab) => { tabName.value = tab.props.name; tableData.value = []; getList(); }; // åéåå const radioChange = (value) => { if (value === "taskManage") { tableColumns.value = columns.value; operationsArr.value = ['edit']; } else if (value === "task") { tableColumns.value = columns.value; operationsArr.value = ['viewFile']; } else { tableColumns.value = columns1.value; operationsArr.value = ['edit']; } pageNum.value = 1; pageSize.value = 10; getList(); }; // æ¥è¯¢æä½ const handleQuery = () => { pageNum.value = 1; pageSize.value = 10; getList(); }; // å页å¤ç const handlePagination = (val) => { pageNum.value = val.page; pageSize.value = val.limit; getList(); }; // è·ååè¡¨æ°æ® const getList = () => { tableLoading.value = true; const params = { ...queryParams, size: pageSize.value, current: pageNum.value }; let apiCall; if (tabName.value === 'task') { switch (activeRadio.value) { case "task": apiCall = inspectionTaskList(params); break; case "qrCode": apiCall = qrCodeList(params); break; default: apiCall = timingTaskList(params); } } else { apiCall = qrCodeScanRecordList(params); } apiCall.then(res => { const rawData = res.data.records || []; // å¤ç inspector åæ®µï¼å°å符串转æ¢ä¸ºæ°ç»ï¼éç¨äºæææ åµï¼ tableData.value = rawData.map(item => { const processedItem = { ...item }; // å¤ç inspector åæ®µ if (processedItem.inspector) { if (typeof processedItem.inspector === 'string') { // å符串æéå·åå² processedItem.inspector = processedItem.inspector.split(',').map(s => s.trim()).filter(s => s); } else if (!Array.isArray(processedItem.inspector)) { // éæ°ç»è½¬ä¸ºæ°ç» processedItem.inspector = [processedItem.inspector]; } } else { // 空å¼è®¾ä¸ºç©ºæ°ç» processedItem.inspector = []; } return processedItem; }); total.value = res.data.total || 0; }).finally(() => { tableLoading.value = false; }); }; // éç½®æ¥è¯¢ const resetQuery = () => { for (const key in queryParams) { if (!["pageNum", "pageSize"].includes(key)) { queryParams[key] = ""; } } handleQuery(); }; // æ°å¢ / ç¼è¾ const handleAdd = (row) => { const type = row ? 'edit' : 'add'; nextTick(() => { if (tabName.value === 'task') { if (activeRadio.value === "taskManage") { formDia.value?.openDialog(type, row); } else if (activeRadio.value === "qrCode") { qrCodeDia.value?.openDialog(type, row); } } else { viewQrCodeFiles.value?.openDialog(row); } }); }; // æ¥çéä»¶ const viewFile = (row) => { nextTick(() => { viewFiles.value?.openDialog(row); }); }; // å é¤æä½ const handleDelete = () => { if (!selectedRows.value.length) { proxy.$modal.msgWarning("è¯·éæ©è¦å é¤çæ°æ®"); return; } const deleteIds = selectedRows.value.map(item => item.id); const api = activeRadio.value === "taskManage" ? delTimingTask : delQrCode; proxy.$modal.confirm('æ¯å¦ç¡®è®¤å 餿鿰æ®é¡¹ï¼').then(() => { return api(deleteIds); }).then(() => { proxy.$modal.msgSuccess("å 餿å"); handleQuery(); }).catch(() => {}); }; // å¤éåæ´ const handleSelectionChange = (selection) => { selectedRows.value = selection; }; </script> <style scoped> .person-tags { display: flex; flex-wrap: wrap; gap: 4px; } .person-tag { margin-right: 4px; margin-bottom: 2px; } .no-data { color: #909399; font-size: 14px; } </style> src/views/reportAnalysis/dataDashboard/index.vue
@@ -12,6 +12,7 @@ <!-- 顶鍿 颿 --> <div class="dashboard-header"> <div class="factory-name">{{ userStore.currentFactoryName }}</div> </div> <!-- 主è¦å 容åºå --> @@ -241,6 +242,7 @@ import { ref, onMounted, onBeforeUnmount, nextTick } from 'vue' import autofit from 'autofit.js' import Echarts from "@/components/Echarts/echarts.vue"; import useUserStore from '@/store/modules/user' import { analysisCustomerContractAmounts, getAmountHalfYear, homeTodos, @@ -258,6 +260,9 @@ // å ¨å±ç¸å ³ç¶æ const isFullscreen = ref(false); // ç¨æ·store const userStore = useUserStore() // ååºå¼æ°æ® const currentTime = ref('') @@ -915,7 +920,7 @@ // 使ç¨nextTickç¡®ä¿DOMå®å ¨æ¸²æåååå§åå¾è¡¨ nextTick(() => { // åå§åautofitèªéåº autofit.init({ dh: 1440, dw: 2560, el: '.data-dashboard', resize: true }, false) autofit.init({ dh: 1080, dw: 1920, el: '.data-dashboard', resize: true }, false) // æ·»å èªå¨æ»å¨å¨ç»ææ - 客æ·ä¿¡æ¯å表 const contractList = refContractList.value @@ -1044,7 +1049,6 @@ position: relative; width: 100%; height: 100%; overflow: hidden; background-image: url("@/assets/BI/backImage@2x.png"); background-size: cover; background-position: center; @@ -1090,6 +1094,17 @@ background-size: cover; background-position: center; background-repeat: no-repeat; display: flex; align-items: center; justify-content: center; } .factory-name { font-weight: 600; font-size: 52px; color: #FFFFFF; top: 32px; position: absolute; } .fullscreen-btn {