From 4fbdf4c6b13d19b9be54900b5dcff29e2ca7ef01 Mon Sep 17 00:00:00 2001 From: riku <risaku@163.com> Date: 星期二, 24 六月 2025 17:31:45 +0800 Subject: [PATCH] 巡查单据自动下载功能(待完成) --- src/utils/doc.js | 25 ++ public/餐饮巡查单据模板.doc | 0 src/views/fysp/check/components/ArbitraryPhoto.vue | 36 ++++ src/views/fysp/task/components/CompMonitorObjEdit.vue | 2 src/components/FYImageSelectDialog.vue | 1 src/views/fysp/check/ProCheckProxy.js | 18 +- src/views/fysp/check/components/CompSubTaskStatistic.vue | 42 ++++- src/components.d.ts | 1 public/工地巡查单据模板.docx | 0 src/views/fysp/scene/SceneInspectFile.vue | 252 +++++++++++++++++++++++++++++++ src/views/fysp/task/components/CompSubTaskList.vue | 40 +++- src/views/fysp/task/TaskManage.vue | 12 + 12 files changed, 388 insertions(+), 41 deletions(-) diff --git "a/public/\345\267\245\345\234\260\345\267\241\346\237\245\345\215\225\346\215\256\346\250\241\346\235\277.docx" "b/public/\345\267\245\345\234\260\345\267\241\346\237\245\345\215\225\346\215\256\346\250\241\346\235\277.docx" new file mode 100644 index 0000000..c1a8470 --- /dev/null +++ "b/public/\345\267\245\345\234\260\345\267\241\346\237\245\345\215\225\346\215\256\346\250\241\346\235\277.docx" Binary files differ diff --git "a/public/\351\244\220\351\245\256\345\267\241\346\237\245\345\215\225\346\215\256\346\250\241\346\235\277.doc" "b/public/\351\244\220\351\245\256\345\267\241\346\237\245\345\215\225\346\215\256\346\250\241\346\235\277.doc" new file mode 100644 index 0000000..b65f9c3 --- /dev/null +++ "b/public/\351\244\220\351\245\256\345\267\241\346\237\245\345\215\225\346\215\256\346\250\241\346\235\277.doc" Binary files differ diff --git a/src/components.d.ts b/src/components.d.ts index d9c2fb8..2b04de3 100644 --- a/src/components.d.ts +++ b/src/components.d.ts @@ -25,6 +25,7 @@ ElCard: typeof import('element-plus/es')['ElCard'] ElCascader: typeof import('element-plus/es')['ElCascader'] ElCheckbox: typeof import('element-plus/es')['ElCheckbox'] + ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup'] ElCol: typeof import('element-plus/es')['ElCol'] ElCollapse: typeof import('element-plus/es')['ElCollapse'] ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem'] diff --git a/src/components/FYImageSelectDialog.vue b/src/components/FYImageSelectDialog.vue index 6e283c3..4d2602a 100644 --- a/src/components/FYImageSelectDialog.vue +++ b/src/components/FYImageSelectDialog.vue @@ -56,6 +56,7 @@ :preview-src-list=" readonly ? typeImgMap.get(activeId).map((v) => v.url) : [] " + crossOrigin="Anonymous" :initial-index="i" @contextmenu="(e) => showContextMenu(e, i)" @click="onSelect(img, i)" diff --git a/src/utils/doc.js b/src/utils/doc.js index 25115f2..a757cfc 100644 --- a/src/utils/doc.js +++ b/src/utils/doc.js @@ -81,10 +81,20 @@ let width = image.width; let height = image.height; - if (width > height && horizontalHeight && height > horizontalHeight && scale) { + if ( + width > height && + horizontalHeight && + height > horizontalHeight && + scale + ) { height = horizontalHeight; width = horizontalHeight * scale; - } else if (width <= height && verticalWidth && width > verticalWidth && scale) { + } else if ( + width <= height && + verticalWidth && + width > verticalWidth && + scale + ) { width = verticalWidth; height = verticalWidth * scale; } @@ -144,12 +154,15 @@ throw error; } const zip = new Pizzip(content); - const imageOptions = getImageOptions(imageSize); let doc = new docxtemplater() .setOptions({ paragraphLoop: true }) - .loadZip(zip) - .attachModule(new ImageModule(imageOptions)) - .compile(); + .loadZip(zip); + + if (imageSize) { + const imageOptions = getImageOptions(imageSize); + doc.attachModule(new ImageModule(imageOptions)); + } + doc.compile(); doc.resolveData(data).then(() => { try { doc.render(); diff --git a/src/views/fysp/check/ProCheckProxy.js b/src/views/fysp/check/ProCheckProxy.js index 938422f..013e0c9 100644 --- a/src/views/fysp/check/ProCheckProxy.js +++ b/src/views/fysp/check/ProCheckProxy.js @@ -29,7 +29,7 @@ //鏈�氳繃鏁伴噺 unpassNum: 0, //鏁存敼鐜� - changePer: '0%', + changePer: '--', //閫氳繃鐜� passPer: '0%', //瀹℃牳鐜� @@ -49,10 +49,10 @@ status.uncheckNum++; else status.passNum++; - if (p.extension3 == proStatus.pass) { + if (p.extension3 != proStatus.unCheck) { status.proCheckedNum++; } - if (p.extension3 == proStatus.change_pass) { + if (p.extension3 == proStatus.change_pass || p.extension3 == proStatus.change_fail) { status.changeCheckedNum++; } @@ -234,18 +234,18 @@ else if (s.proCheckedNum < s.proNum) { type = 2; } - // 鏈暣鏀� - else if (s.changeNum < s.proNum) { - type = 3; - } // 鏁存敼鏈鏍� - else if (s.changeCheckedNum == 0) { + else if (s.changeNum > 0 && s.changeCheckedNum == 0) { type = 4; } // 鏁存敼閮ㄥ垎瀹℃牳 - else if (s.changeCheckedNum < s.changeNum) { + else if (s.changeNum > 0 && s.changeCheckedNum < s.changeNum) { type = 5; } + // 鏈暣鏀� + else if (s.changeNum < s.proNum) { + type = 3; + } // 瀹屽叏瀹℃牳 else { type = 6; diff --git a/src/views/fysp/check/components/ArbitraryPhoto.vue b/src/views/fysp/check/components/ArbitraryPhoto.vue index 55b11a5..703ceda 100644 --- a/src/views/fysp/check/components/ArbitraryPhoto.vue +++ b/src/views/fysp/check/components/ArbitraryPhoto.vue @@ -74,6 +74,7 @@ // ], closeContextMenuListenr: undefined, // 鍙抽敭閫変腑鐨勫浘鐗� + selectedFileElement: undefined, selectedFile: undefined, selectedIndex: undefined, selectedTypeId: undefined @@ -94,7 +95,7 @@ }; }); return [ - // { label: '澶嶅埗鍥剧墖', action: 'copy' }, + // { label: '澶嶅埗鍒板壀璐存澘', action: 'copy' }, { label: '绉诲姩鍒�', children: items } ]; } @@ -158,6 +159,7 @@ left: `${event.clientX}px`, top: `${event.clientY}px` }; + this.selectedFileElement = event.target; this.selectedTypeId = typeId; this.selectedIndex = index; this.selectedFile = this.typesMap.get(typeId)[index]; @@ -168,6 +170,7 @@ handleMenuItem(item) { switch (item.action) { case 'copy': + this.drawImageToCanvas(); break; case 'move': this.selectedFile.data.businesstypeid = item.value; @@ -217,6 +220,37 @@ // } // this.selectedFile.loading = false; // }, 2000); + }, + drawImageToCanvas() { + const canvas = document.createElement('canvas'); + const ctx = canvas.getContext('2d'); + const img = this.selectedFileElement; // 鑾峰彇DOM寮曠敤 + // img.crossOrigin = 'Anonymous'; + canvas.width = img.naturalWidth; + canvas.height = img.naturalHeight; + ctx.drawImage(img, 0, 0, canvas.width, canvas.height); + // this.copyCanvasToClipboard(canvas); + const dataUrl = canvas.toDataURL('image/png'); // 鍙互閫夋嫨鍏朵粬鏍煎紡濡�'image/jpeg' + // 鍒涘缓涓�涓复鏃剁殑textarea鍏冪礌鏉ュ鍒舵枃鏈� + const tempTextArea = document.createElement('textarea'); + document.body.appendChild(tempTextArea); + tempTextArea.value = dataUrl; + tempTextArea.select(); + document.execCommand('copy'); + document.body.removeChild(tempTextArea); + }, + copyCanvasToClipboard(canvas) { + canvas.toBlob((blob) => { + const item = new ClipboardItem({ 'image/png': blob }); // 浣犲彲浠ユ牴鎹渶瑕佹敼鍙樻牸寮忥紝濡� "image/jpeg" + navigator.clipboard.write([item]).then( + () => { + console.log('Image copied to clipboard'); + }, + (err) => { + console.error('Could not copy image: ', err); + } + ); + }, 'image/png'); // 鍚屾牱锛岃繖閲屼篃鍙互鎸囧畾鍏朵粬鏍煎紡锛屽 'image/jpeg' } } }; diff --git a/src/views/fysp/check/components/CompSubTaskStatistic.vue b/src/views/fysp/check/components/CompSubTaskStatistic.vue index 7f8d3e9..7e30a44 100644 --- a/src/views/fysp/check/components/CompSubTaskStatistic.vue +++ b/src/views/fysp/check/components/CompSubTaskStatistic.vue @@ -269,17 +269,39 @@ completedTimes: 0, reviewTimes: 0 }; - this.monitorObjList.forEach((m) => { - if (this.sceneType == undefined || m.sceneTypeId == this.sceneType) { - _res.total++; - const times = parseInt(m.extension1); - if (times) { - _res.completedScenes++; - _res.completedTimes += times; - _res.reviewTimes += times - 1; - } + + // Fixme 2025.5.30: 鐩墠姝ゅ鐨勭洃绠$増鏈俊鎭挓璁板綍鐨勪换鍔″畬鎴愭鏁版湁鍋忓樊锛屾墍浠ユ殏鏃跺厛鏀逛负閫氳繃宸℃煡浠诲姟鏈韩杩涜缁熻 + /**************************************************************************************/ + // this.monitorObjList.forEach((m) => { + // if (this.sceneType == undefined || m.sceneTypeId == this.sceneType) { + // _res.total++; + // const times = parseInt(m.extension1); + // if (times) { + // _res.completedScenes++; + // _res.completedTimes += times; + // _res.reviewTimes += times - 1; + // } + // } + // }); + /**************************************************************************************/ + + /**************************************************************************************/ + _res.total = this.monitorObjList.filter( + (m) => this.sceneType == undefined || m.sceneTypeId == this.sceneType + ).length; + _res.completedTimes = this.subtasks.length; + const map = new Map(); + const uniqueArr = []; + for (let item of this.subtasks) { + if (!map.has(item.data.sceneId)) { + map.set(item.data.sceneId, true); + uniqueArr.push(item); } - }); + } + _res.completedScenes = uniqueArr.length; + _res.reviewTimes = _res.completedTimes - _res.completedScenes; + + /**************************************************************************************/ return _res; } }, diff --git a/src/views/fysp/scene/SceneInspectFile.vue b/src/views/fysp/scene/SceneInspectFile.vue new file mode 100644 index 0000000..ddd17c8 --- /dev/null +++ b/src/views/fysp/scene/SceneInspectFile.vue @@ -0,0 +1,252 @@ +<template> + <el-dialog + :model-value="modelValue" + @update:model-value="handleDialogChange" + title="宸℃煡鍗曟嵁涓嬭浇" + class="dialog-wrapper" + v-loading="loading" + > + <el-scrollbar + ref="scrollbarRef" + height="50vh" + v-loading="loading" + :always="true" + > + <el-checkbox-group v-model="checkList"> + <el-space direction="vertical" alignment="flex-start" fill> + <el-checkbox + v-for="(item, index) in sceneInfoList" + :key="item.scense.guid" + :value="index" + :class="(item.invalid ? 'checkbox-invalid' : '') + ' checkbox'" + > + <div> + <el-text size="large">{{ item.scense.name }}</el-text> + </div> + <div class="m-t-4"> + <el-text size="small">{{ + '鍦板潃锛�' + item.scense.location + }}</el-text> + </div> + <el-space class="m-t-4"> + <el-tag> + {{ + item.scense.cityname + + item.scense.districtname + + item.scense.townname + }} + <!-- {{ item.scense.districtname }} + {{ item.scense.townname }} --> + </el-tag> + <el-tag>{{ item.scense.type }}</el-tag> + + <!-- {{ item.scense.contacts }} + {{ item.scense.contactst }} --> + </el-space> + </el-checkbox> + </el-space> + </el-checkbox-group> + </el-scrollbar> + <template #footer> + <div class="dialog-footer"> + <el-button type="danger" @click="cancel">鍙栨秷</el-button> + <el-button type="primary" :loading="docLoading" @click="handelDownload"> + 涓嬭浇 + </el-button> + <el-button type="default" :loading="docLoading" @click="handelPrint"> + 鎵撳嵃 + </el-button> + </div> + </template> + </el-dialog> +</template> +<script setup> +/** + * 鍦烘櫙宸℃煡鍗曟嵁鑷姩涓嬭浇 + */ +import { ref, watch } from 'vue'; +import { exportDocx } from '@/utils/doc'; +import sceneApi from '@/api/fysp/sceneApi'; + +const props = defineProps({ + // 瀵硅瘽妗嗗紑鍏� + modelValue: Boolean, + // 鍦烘櫙鍩虹淇℃伅鏁扮粍 + value: Array +}); + +const emits = defineEmits(['update:modelValue']); + +const loading = ref(false); +const scrollbarRef = ref(); +const sceneInfoList = ref([]); +const checkList = ref([]); +const docLoading = ref(false); + +watch( + () => [props.modelValue, props.value], + (nV, oV) => { + if (nV[0] && nV[1] && nV[1] != oV[1]) { + fetchSceneInfo(nV[1]); + } + } +); + +function fetchSceneInfo(sceneIdList) { + loading.value = true; + sceneInfoList.value = []; + checkList.value = []; + sceneIdList.forEach((sid) => { + sceneApi + .getSceneDetail(sid) + .then((res) => { + sceneInfoList.value.push(res.data); + checkList.value.push(sceneInfoList.value.length - 1); + + //鍦烘櫙 + // if (res.data.scense) sceneInfoList.value = res.data.scense; + // if (res.data.subScene) { + // formSubScene.value = res.data.subScene; + // } else { + // formSubScene.value = { + // sGuid: formScene.value.guid + // }; + // } + }) + .finally(() => { + loading.value = false; + scrollbarRef.value.setScrollTop(0); + }); + }); +} + +function handleDialogChange(value) { + emits('update:modelValue', value); +} + +// 鏍煎紡鍖栧満鏅俊鎭紝鐢熸垚鍙傛暟缁撴瀯 +function parseParam() { + const selected = sceneInfoList.value.filter((v, i) => { + return checkList.value.indexOf(i) != -1; + }); + const param = selected.map((v) => { + switch (v.scense.typeid) { + // 宸ュ湴 + case 1: + return { + type: v.scense.typeid, + params: { + district: v.scense.districtname, + name: v.scense.name, + employerUnit: v.scense.csEmployerUnit, + constructionUnit: v.subScene.csConstructionUnit, + timeRange: v.subScene.csStartTime + ? `${v.subScene.csStartTime}鑷�${v.subScene.csEndTime}` + : undefined, + stage: v.subScene.siExtension1, + contacts: v.subScene.csConstructionContacts, + contactsTel: v.subScene.csConstructionContactsTel, + location: v.scense.location + } + }; + // 椁愰ギ + case 5: + return { + type: v.scense.typeid, + params: { + district: v.scense.districtname, + location: v.scense.location, + name: v.scense.name, + contacts: v.scense.contacts, + contactsTel: v.scense.contactst + } + }; + // default: + // v.invalid = true; + // return undefined; + } + }); + + param.forEach((p) => { + for (const key in p.params) { + let value = p.params[key]; + if (value == undefined) { + // 鑻ュ睘鎬х己澶憋紝鍒欐敼涓�20涓┖鏍肩锛屽搴攚ord涓�10涓腑鏂囧瓧绗︾殑闀垮害 + p.params[key] = ' '; + } + } + }); + + return param; +} + +// 鏍规嵁鍦烘櫙绫诲瀷锛岀敓鎴愬搴旂殑word鏂囨。 +function generateDoc(param) { + param.forEach((p) => { + let template, _param; + switch (p.type) { + // 宸ュ湴 + case 1: + template = '/宸ュ湴宸℃煡鍗曟嵁妯℃澘.docx'; + _param = p.params; + break; + // 椁愰ギ + case 5: + break; + default: + break; + } + + exportDocx(template, _param, `${_param.name}宸℃煡鍗曟嵁.docx`).finally( + () => (docLoading.value = false) + ); + }); +} + +// 涓嬭浇word鏂囨。 +function download(file) {} + +// 鎵撳嵃word鏂囨。 +function print(file) {} + +function filePrepare() { + const param = parseParam(); + if (param) { + return generateDoc(param); + } +} + +// 鐐瑰嚮涓嬭浇鎸夐挳鎿嶄綔 +function handelDownload() { + const file = filePrepare(); + if (file) { + download(file); + } +} + +// 鐐瑰嚮鎵撳嵃鎸夐挳鎿嶄綔 +function handelPrint() { + const file = filePrepare(); + if (file) { + print(file); + } +} + +// 鍙栨秷鎿嶄綔 +function cancel() { + // 鍏抽棴瀵硅瘽妗� + handleDialogChange(false); +} +</script> +<style scoped> +.checkbox { + border: var(--el-border); + padding: 8px; +} +.checkbox-invalid { + border-color: var(--el-color-error); +} +:deep(.el-checkbox) { + height: auto; +} +</style> diff --git a/src/views/fysp/task/TaskManage.vue b/src/views/fysp/task/TaskManage.vue index f844b5e..24129b6 100644 --- a/src/views/fysp/task/TaskManage.vue +++ b/src/views/fysp/task/TaskManage.vue @@ -202,10 +202,16 @@ computed: { // 鎬讳换鍔$姸鎬佺粺璁� taskStatus() { + let total = 0, + inspected = 0; + this.curMonitorObjList.forEach((obj) => { + total += parseInt(obj.monitornum); + inspected += obj.extension1 ? parseInt(obj.extension1) : 0; + }); return [ - { name: '鍦烘櫙鏁�', value: 100 }, - { name: '鏈贰鏌�', value: 0 }, - { name: '宸插贰鏌�', value: 0 } + { name: '鍦烘櫙鏁�', value: total }, + { name: '鏈贰鏌�', value: total - inspected }, + { name: '宸插贰鏌�', value: inspected } ]; } }, diff --git a/src/views/fysp/task/components/CompMonitorObjEdit.vue b/src/views/fysp/task/components/CompMonitorObjEdit.vue index 517f03b..cccd5ea 100644 --- a/src/views/fysp/task/components/CompMonitorObjEdit.vue +++ b/src/views/fysp/task/components/CompMonitorObjEdit.vue @@ -274,7 +274,7 @@ }); }, deleteMov(item) { - if (item.extension1) { + if (!(item.extension1 == null || item.extension1 == undefined || item.extension1 == '0')) { ElMessage({ message: '宸茬洃绠″満鏅棤娉曠Щ闄�', type: 'error' diff --git a/src/views/fysp/task/components/CompSubTaskList.vue b/src/views/fysp/task/components/CompSubTaskList.vue index 03ef062..2ff7199 100644 --- a/src/views/fysp/task/components/CompSubTaskList.vue +++ b/src/views/fysp/task/components/CompSubTaskList.vue @@ -1,13 +1,22 @@ <template> <el-row justify="space-between"> <el-text>鍗曟棩璁″垝</el-text> - <el-button - v-show="create && data && data.length > 0" - type="success" - size="small" - @click="add" - >浠诲姟璋冩暣</el-button - > + <div> + <el-button + type="success" + size="small" + plain + @click="handleInspectFileDownload" + >鍗曟嵁涓嬭浇</el-button + > + <el-button + v-show="create && data && data.length > 0" + type="success" + size="small" + @click="add" + >浠诲姟璋冩暣</el-button + > + </div> </el-row> <el-divider /> <div> @@ -70,11 +79,13 @@ @cancel="dialogVisible = false" ></CompSubTaskEdit> </el-dialog> + <SceneInspectFile v-model="downloadDialog" :value="downloadSceneList"></SceneInspectFile> </template> <script setup> import { ref, computed, watch, onMounted, onUnmounted } from 'vue'; import { ElMessageBox, ElNotification, ElMessage } from 'element-plus'; import CompSubTaskEdit from './CompSubTaskEdit.vue'; +import SceneInspectFile from "@/views/fysp/scene/SceneInspectFile.vue"; import subtaskApi from '@/api/fysp/subtaskApi'; const props = defineProps({ @@ -86,12 +97,14 @@ // 鏄惁鏄剧ず娣诲姞浠诲姟鎸夐挳 create: Boolean, loading: Boolean, - createLoading:Boolean, + createLoading: Boolean }); const dialogVisible = ref(false); const activeItem = ref(null); const data = computed(() => props.modelValue); +const downloadDialog = ref(false); +const downloadSceneList = ref([]) const emit = defineEmits(['submit', 'add', 'remove', 'update:modelValue']); @@ -102,7 +115,7 @@ cancelButtonText: '鍙栨秷', type: 'warning' }).then(() => { - return subtaskApi.deleteSubtask(item.stguid).then(res=>{ + return subtaskApi.deleteSubtask(item.stguid).then((res) => { if (res == 1) { const index = data.value.indexOf(item); data.value.splice(index, 1); @@ -110,9 +123,9 @@ emit('update:modelValue', data.value); emit('remove', item); } else { - Promise.reject('鍒犻櫎宸℃煡浠诲姟澶辫触') + Promise.reject('鍒犻櫎宸℃煡浠诲姟澶辫触'); } - }) + }); }); } } @@ -139,4 +152,9 @@ onUnmounted(() => { dialogVisible.value = false; }); + +function handleInspectFileDownload() { + downloadSceneList.value = data.value.map(v=>v.scenseid) + downloadDialog.value = true +} </script> -- Gitblit v1.9.3