| 10 天以前 | zhangwencui | ![]() |
| 10 天以前 | zhangwencui | ![]() |
| 10 天以前 | gaoluyang | ![]() |
| 10 天以前 | zhangwencui | ![]() |
| 10 天以前 | zhangwencui | ![]() |
| 10 天以前 | gaoluyang | ![]() |
| 10 天以前 | gaoluyang | ![]() |
| 10 天以前 | zhangwencui | ![]() |
| 10 天以前 | gaoluyang | ![]() |
multiple/assets/favicon/DHDCico.icomultiple/assets/logo/DHDCLogo.pngmultiple/config.json
@@ -129,8 +129,8 @@ }, "DHDC": { "env": { "VITE_APP_TITLE": "æ¦ç é¼è¯ä¿¡æ¯ç®¡çç³»ç»", "VITE_BASE_API": "http://114.132.189.42:9032", "VITE_APP_TITLE": "æ¦ç å¸é¼åçå®ä¸ä¿¡æ¯ç®¡çç³»ç»", "VITE_BASE_API": "http://114.132.189.42:9018", "VITE_JAVA_API": "http://114.132.189.42:9033" }, "screen": "screen/DHDCView.png", src/api/equipmentManagement/calibration.js
@@ -24,12 +24,4 @@ method: "post", data: query, }); } // å é¤è®°å½ export function ledgerRecordDelete(ids) { return request({ url: "/measuringInstrumentLedgerRecord/delete", method: "delete", data: ids, }); } src/api/equipmentManagement/maintenanceTaskFile.js
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,28 @@ import request from "@/utils/request"; // æ¥è¯¢ä¿å »ä»»å¡éä»¶å表 export function listMaintenanceTaskFiles(query) { return request({ url: "/maintenanceTaskFile/listPage", method: "get", params: query, }); } // æ°å¢ä¿å »ä»»å¡éä»¶ export function addMaintenanceTaskFile(data) { return request({ url: "/maintenanceTaskFile/add", method: "post", data, }); } // å é¤ä¿å »ä»»å¡éä»¶ export function delMaintenanceTaskFile(id) { return request({ url: "/maintenanceTaskFile/del", method: "delete", data: Array.isArray(id) ? id : [id], }); } src/api/equipmentManagement/repair.js
@@ -70,3 +70,19 @@ data, }); }; // æ¥è¯¢è®¾å¤çæ¥ä¿®éé¢-æ¯æ export const monthlyAmount = (query) => { return request({ url: `/device/repair/monthlyAmount`, method: "get", params: query, }); }; // æ¥è¯¢è®¾å¤çæ¥ä¿®éé¢-æ¯å¹´ export const yearlyAmount = (query) => { return request({ url: `/device/repair/yearlyAmount`, method: "get", params: query, }); }; src/api/equipmentManagement/upkeep.js
@@ -102,3 +102,20 @@ data: params, }); }; // æ¥è¯¢è®¾å¤çæ¥ä¿®éé¢-æ¯æ export const monthlyAmount = (query) => { return request({ url: `/device/maintenance/monthlyAmount`, method: "get", params: query, }); }; // æ¥è¯¢è®¾å¤çæ¥ä¿®éé¢-æ¯å¹´ export const yearlyAmount = (query) => { return request({ url: `/device/maintenance/yearlyAmount`, method: "get", params: query, }); }; src/api/inventoryManagement/stockIn.js
@@ -72,3 +72,12 @@ }) } // æ°å¢ååå ¥åºä¿¡æ¯ export function addProduct(data) { return request({ url: '/stockin/addProduct', method: 'post', data: data }) } src/components/Dialog/FileListDialog.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,328 @@ <template> <el-dialog v-model="dialogVisible" :title="title" :width="width" :before-close="handleClose"> <div class="file-list-toolbar" v-if="showToolbar"> <template v-if="useBuiltInUpload"> <el-upload v-model:file-list="uploadFileList" class="upload-demo" :action="uploadAction" :headers="uploadHeaders" :show-file-list="false" :on-success="handleDefaultUploadSuccess" :on-error="handleDefaultUploadError"> <el-button v-if="showUploadButton" type="primary" size="small"> ä¸ä¼ éä»¶ </el-button> </el-upload> </template> <template v-else> <el-button v-if="showUploadButton" type="primary" size="small" @click="handleUpload"> æ°å¢éä»¶ </el-button> </template> </div> <el-table :data="tableData" border :height="tableHeight"> <el-table-column :label="nameColumnLabel" :prop="nameColumnProp" :min-width="nameColumnMinWidth" show-overflow-tooltip /> <el-table-column v-if="showActions" fixed="right" label="æä½" :width="actionColumnWidth" align="center"> <template #default="scope"> <el-button v-if="showDownload" link type="primary" size="small" @click="handleDownload(scope.row)"> ä¸è½½ </el-button> <el-button v-if="showPreview" link type="primary" size="small" @click="handlePreview(scope.row)"> é¢è§ </el-button> <el-button v-if="showDeleteButton" link type="danger" size="small" @click="handleDelete(scope.row, scope.$index)"> å é¤ </el-button> <slot name="actions" :row="scope.row"></slot> </template> </el-table-column> <slot name="columns"></slot> </el-table> <pagination v-if="isShowPagination" style="margin-bottom: 20px;" :total="page.total" :page="page.current" :limit="page.size" @pagination="paginationSearch" @change="handleChange" /> </el-dialog> <filePreview v-if="showPreview" ref="filePreviewRef" /> </template> <script setup> import { ref, computed, getCurrentInstance } from "vue"; import pagination from "@/components/Pagination/index.vue"; import { ElMessage } from "element-plus"; import filePreview from "@/components/filePreview/index.vue"; import { getToken } from "@/utils/auth"; const props = defineProps({ modelValue: { type: Boolean, default: false, }, title: { type: String, default: "éä»¶", }, width: { type: String, default: "40%", }, tableHeight: { type: String, default: "40vh", }, nameColumnLabel: { type: String, default: "éä»¶åç§°", }, nameColumnProp: { type: String, default: "name", }, nameColumnMinWidth: { type: [String, Number], default: 400, }, actionColumnWidth: { type: [String, Number], default: 160, }, showActions: { type: Boolean, default: true, }, showDownload: { type: Boolean, default: true, }, showPreview: { type: Boolean, default: true, }, showUploadButton: { type: Boolean, default: false, }, showDeleteButton: { type: Boolean, default: false, }, urlField: { type: String, default: "url", }, downloadMethod: { type: Function, default: null, }, previewMethod: { type: Function, default: null, }, uploadMethod: { type: Function, default: null, }, deleteMethod: { type: Function, default: null, }, rulesRegulationsManagementId: { type: [String, Number], default: "", }, uploadUrl: { type: String, default: `${import.meta.env.VITE_APP_BASE_API}/file/upload`, }, isShowPagination: { type: Boolean, default: false, }, page: { type: Object, default: () => ({ current: 1, size: 10, total: 0, }), }, }); const emit = defineEmits([ "update:modelValue", "close", "download", "preview", "upload", "delete", ]); const { proxy } = getCurrentInstance(); const filePreviewRef = ref(null); const uploadFileList = ref([]); const dialogVisible = computed({ get: () => props.modelValue, set: val => emit("update:modelValue", val), }); const tableData = ref([]); const showToolbar = computed(() => props.showUploadButton); const useBuiltInUpload = computed(() => !props.uploadMethod); const uploadAction = computed(() => props.uploadUrl); const uploadHeaders = computed(() => ({ Authorization: `Bearer ${getToken()}`, })); const handleClose = () => { emit("close"); dialogVisible.value = false; }; const handleDownload = row => { if (props.downloadMethod) { props.downloadMethod(row); } else { // é»è®¤ä¸è½½æ¹æ³ proxy.$download.name(row[props.urlField]); } emit("download", row); }; const handlePreview = row => { if (props.previewMethod) { props.previewMethod(row); } else { // é»è®¤é¢è§æ¹æ³ if (filePreviewRef.value) { filePreviewRef.value.open(row[props.urlField]); } } emit("preview", row); }; const paginationSearch = page => { props.page.current = page.page; props.page.size = page.limit; emit("pagination", page.page, page.limit); }; const open = list => { dialogVisible.value = true; tableData.value = list || []; }; const handleUpload = async () => { if (props.uploadMethod) { // 妿æä¾äºèªå®ä¹ä¸ä¼ æ¹æ³ï¼ç±ç¶ç»ä»¶è´è´£æ´æ°å表ï¼éè¿ setListï¼ // è¿éä¸åèªå¨æ·»å ï¼é¿å ä¸ç¶ç»ä»¶ç setList éå¤ await props.uploadMethod(); } emit("upload"); }; const handleDelete = async (row, index) => { if (props.deleteMethod) { const result = await props.deleteMethod(row, index); if (result === false) { return; } // 妿æä¾äº deleteMethodï¼ç±ç¶ç»ä»¶è´è´£å·æ°å表ï¼ä¸å¨è¿éå é¤ } else { // å¦ææ²¡ææä¾ deleteMethodï¼æå¨ç»ä»¶å é¨å é¤ removeAttachment(index); } emit("delete", row); }; const addAttachment = item => { tableData.value = [...tableData.value, item]; }; const handleDefaultUploadSuccess = async (res, file) => { if (res?.code !== 200) { ElMessage.error(res?.msg || "æä»¶ä¸ä¼ 失败"); return; } if (!props.rulesRegulationsManagementId) { ElMessage.error("缺å°è§ç« å¶åº¦IDï¼æ æ³ä¿åéä»¶"); return; } const fileName = res?.data?.originalName || file?.name; const fileUrl = res?.data?.tempPath || res?.data?.url; const payload = { fileName, fileUrl, rulesRegulationsManagementId: props.rulesRegulationsManagementId, raw: res?.data || {}, }; emit("upload", payload); }; const handleDefaultUploadError = () => { ElMessage.error("æä»¶ä¸ä¼ 失败"); }; const removeAttachment = index => { if (index > -1 && index < tableData.value.length) { const newList = [...tableData.value]; newList.splice(index, 1); tableData.value = newList; } }; const setList = list => { tableData.value = list || []; }; defineExpose({ open, addAttachment, removeAttachment, setList, handleUpload, handleDelete, }); </script> <style scoped> .file-list-toolbar { margin-bottom: 8px; text-align: right; } </style> src/components/Dialog/FormDialog.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,73 @@ <template> <el-dialog v-model="dialogVisible" :title="computedTitle" :width="width" @close="handleClose" > <slot></slot> <template #footer> <div class="dialog-footer"> <el-button type="primary" @click="handleConfirm">确认</el-button> <el-button @click="handleCancel">åæ¶</el-button> </div> </template> </el-dialog> </template> <script setup> import { computed } from 'vue' const props = defineProps({ modelValue: { type: Boolean, default: false }, title: { type: [String, Function], default: '' }, operationType: { type: String, default: '' }, width: { type: String, default: '70%' } }) const emit = defineEmits(['update:modelValue', 'close', 'confirm', 'cancel']) const dialogVisible = computed({ get: () => props.modelValue, set: (val) => emit('update:modelValue', val) }) const computedTitle = computed(() => { if (typeof props.title === 'function') { return props.title(props.operationType) } return props.title }) const handleClose = () => { emit('close') } const handleConfirm = () => { emit('confirm') } const handleCancel = () => { emit('cancel') dialogVisible.value = false } </script> <style scoped> .dialog-footer { text-align: center; } </style> src/views/basicData/enterpriseInfo/index.vue
@@ -4,45 +4,74 @@ <div class="page-header"> <h2>ä¼ä¸é¨æ·</h2> <div class="header-actions"> <el-button @click="handlePreview" :icon="View" type="info" plain>é¢è§</el-button> <el-button @click="toggleEdit" :icon="isEdit ? 'Close' : 'Edit'" :type="isEdit ? 'default' : 'primary'"> <el-button @click="handlePreview" :icon="View" type="info" plain>é¢è§</el-button> <el-button @click="toggleEdit" :icon="isEdit ? 'Close' : 'Edit'" :type="isEdit ? 'default' : 'primary'"> {{ isEdit ? 'åæ¶ç¼è¾' : 'ç¼è¾' }} </el-button> </div> </div> <!-- ä¼ä¸ä¿¡æ¯å¡ç --> <div class="enterprise-info-card" v-loading="loading"> <div class="enterprise-info-card" v-loading="loading"> <!-- åºæ¬ä¿¡æ¯åºå --> <div class="info-section"> <div class="section-header"> <h3>åºæ¬ä¿¡æ¯</h3> </div> <el-descriptions :column="2" border class="info-descriptions"> <el-descriptions :column="2" border class="info-descriptions"> <el-descriptions-item label="å ¬å¸åç§°"> <el-input v-if="isEdit" v-model="form.companyName" placeholder="请è¾å ¥å ¬å¸åç§°" clearable /> <span v-else class="content-text">{{ form.companyName || '-' }}</span> <el-input v-if="isEdit" v-model="form.companyName" placeholder="请è¾å ¥å ¬å¸åç§°" clearable /> <span v-else class="content-text">{{ form.companyName || '-' }}</span> </el-descriptions-item> <el-descriptions-item label="è系人"> <el-input v-if="isEdit" v-model="form.contactPerson" placeholder="请è¾å ¥è系人" clearable /> <span v-else class="content-text">{{ form.contactPerson || '-' }}</span> <el-input v-if="isEdit" v-model="form.contactPerson" placeholder="请è¾å ¥è系人" clearable /> <span v-else class="content-text">{{ form.contactPerson || '-' }}</span> </el-descriptions-item> <el-descriptions-item label="èç³»çµè¯"> <el-input v-if="isEdit" v-model="form.contactPhone" placeholder="请è¾å ¥èç³»çµè¯" clearable /> <span v-else class="content-text">{{ form.contactPhone || '-' }}</span> <el-input v-if="isEdit" v-model="form.contactPhone" placeholder="请è¾å ¥èç³»çµè¯" clearable /> <span v-else class="content-text">{{ form.contactPhone || '-' }}</span> </el-descriptions-item> <el-descriptions-item label="å ¬å¸å°å"> <el-input v-if="isEdit" v-model="form.companyAddress" placeholder="请è¾å ¥å ¬å¸å°å" clearable /> <span v-else class="content-text">{{ form.companyAddress || '-' }}</span> <el-input v-if="isEdit" v-model="form.companyAddress" placeholder="请è¾å ¥å ¬å¸å°å" clearable /> <span v-else class="content-text">{{ form.companyAddress || '-' }}</span> </el-descriptions-item> <el-descriptions-item label="å ¬å¸ç½ç«"> <el-input v-if="isEdit" v-model="form.website" placeholder="请è¾å ¥å ¬å¸ç½ç«" clearable /> <a v-else-if="form.website" :href="form.website" target="_blank" class="link-text">{{ form.website }}</a> <span v-else class="content-text">-</span> <el-input v-if="isEdit" v-model="form.website" placeholder="请è¾å ¥å ¬å¸ç½ç«" clearable /> <a v-else-if="form.website" :href="form.website" target="_blank" class="link-text">{{ form.website }}</a> <span v-else class="content-text">-</span> </el-descriptions-item> </el-descriptions> </div> <!-- Logoåäºç»´ç åºå --> <div class="info-section"> <div class="section-header"> @@ -53,135 +82,148 @@ <div class="upload-item"> <span class="upload-label">å ¬å¸Logo</span> <div class="upload-wrapper"> <el-upload v-if="isEdit" class="logo-uploader" :show-file-list="false" :before-upload="(file) => beforeLogoUpload(file, 'companyLogo')" action="#"> <img v-if="form.companyLogo" :src="form.companyLogo" class="uploaded-image" alt="Image Preview" /> <div v-else class="upload-placeholder"> <el-icon class="upload-icon"><Plus /></el-icon> <el-upload v-if="isEdit" class="logo-uploader" :show-file-list="false" :before-upload="(file) => beforeLogoUpload(file, 'companyLogo')" action="#"> <img v-if="form.companyLogo" :src="base + form.companyLogo" class="uploaded-image" alt="Image Preview" /> <div v-else class="upload-placeholder"> <el-icon class="upload-icon"> <Plus /> </el-icon> <span class="upload-text">ä¸ä¼ Logo</span> </div> </el-upload> <img v-else-if="form.companyLogo" :src="form.companyLogo" class="display-image" alt="Image Preview" /> <div v-else class="empty-placeholder"> <el-icon :size="40"><Picture /></el-icon> <img v-else-if="form.companyLogo" :src="base + form.companyLogo" class="display-image" alt="Image Preview" /> <div v-else class="empty-placeholder"> <el-icon :size="40"> <Picture /> </el-icon> <span>ææ Logo</span> </div> </div> </div> <!-- äºç»´ç --> <div class="upload-item"> <span class="upload-label">äºç»´ç </span> <div class="upload-wrapper"> <el-upload v-if="isEdit" class="qr-uploader" :show-file-list="false" :before-upload="(file) => beforeLogoUpload(file, 'qrCode')" action="#"> <img v-if="form.qrCode" :src="form.qrCode" class="uploaded-image" alt="Image Preview" /> <div v-else class="upload-placeholder"> <el-icon class="upload-icon"><Plus /></el-icon> <el-upload v-if="isEdit" class="qr-uploader" :show-file-list="false" :before-upload="(file) => beforeLogoUpload(file, 'qrCode')" action="#"> <img v-if="form.qrCode" :src="base + form.qrCode" class="uploaded-image" alt="Image Preview" /> <div v-else class="upload-placeholder"> <el-icon class="upload-icon"> <Plus /> </el-icon> <span class="upload-text">ä¸ä¼ äºç»´ç </span> </div> </el-upload> <img v-else-if="form.qrCode" :src="form.qrCode" class="display-image" alt="Image Preview" /> <div v-else class="empty-placeholder"> <el-icon :size="40"><Picture /></el-icon> <img v-else-if="form.qrCode" :src="base + form.qrCode" class="display-image" alt="Image Preview" /> <div v-else class="empty-placeholder"> <el-icon :size="40"> <Picture /> </el-icon> <span>ææ äºç»´ç </span> </div> </div> </div> </div> </div> <!-- å ¬å¸ç®ä» --> <div class="info-section"> <div class="section-header"> <h3>å ¬å¸ç®ä»</h3> </div> <div class="content-editor"> <el-input v-if="isEdit" v-model="form.companyIntro" type="textarea" :rows="6" maxlength="2000" show-word-limit placeholder="请è¾å ¥å ¬å¸ç®ä»..." /> <div v-else class="content-display" v-html="form.companyIntro || '<span class=\'empty-text\'>ææ å ¬å¸ç®ä»</span>'"></div> <el-input v-if="isEdit" v-model="form.companyIntro" type="textarea" :rows="6" maxlength="2000" show-word-limit placeholder="请è¾å ¥å ¬å¸ç®ä»..." /> <div v-else class="content-display" v-html="form.companyIntro || '<span class=\'empty-text\'>ææ å ¬å¸ç®ä»</span>'"></div> </div> </div> <!-- 产åä»ç» --> <div class="info-section"> <div class="section-header"> <h3>产åä»ç»</h3> </div> <div class="content-editor"> <el-input v-if="isEdit" v-model="form.productIntro" type="textarea" :rows="6" maxlength="2000" show-word-limit placeholder="请è¾å ¥äº§åä»ç»..." /> <div v-else class="content-display" v-html="form.productIntro || '<span class=\'empty-text\'>ææ äº§åä»ç»</span>'"></div> <el-input v-if="isEdit" v-model="form.productIntro" type="textarea" :rows="6" maxlength="2000" show-word-limit placeholder="请è¾å ¥äº§åä»ç»..." /> <div v-else class="content-display" v-html="form.productIntro || '<span class=\'empty-text\'>ææ äº§åä»ç»</span>'"></div> </div> </div> <!-- 设å¤ä»ç» --> <div class="info-section"> <div class="section-header"> <h3>设å¤ä»ç»</h3> </div> <div class="content-editor"> <el-input v-if="isEdit" v-model="form.equipmentIntro" type="textarea" :rows="6" maxlength="2000" show-word-limit placeholder="请è¾å ¥è®¾å¤ä»ç»..." /> <div v-else class="content-display" v-html="form.equipmentIntro || '<span class=\'empty-text\'>ææ è®¾å¤ä»ç»</span>'"></div> <el-input v-if="isEdit" v-model="form.equipmentIntro" type="textarea" :rows="6" maxlength="2000" show-word-limit placeholder="请è¾å ¥è®¾å¤ä»ç»..." /> <div v-else class="content-display" v-html="form.equipmentIntro || '<span class=\'empty-text\'>ææ è®¾å¤ä»ç»</span>'"></div> </div> </div> <!-- æä½æé® --> <div v-if="isEdit" class="form-actions"> <el-button type="primary" @click="handleSave" :loading="saving" size="large">ä¿å</el-button> <el-button @click="handleCancel" size="large">åæ¶</el-button> <div v-if="isEdit" class="form-actions"> <el-button type="primary" @click="handleSave" :loading="saving" size="large">ä¿å</el-button> <el-button @click="handleCancel" size="large">åæ¶</el-button> </div> </div> <!-- é¢è§å¼¹çª --> <el-dialog v-model="previewVisible" title="ä¼ä¸ä¿¡æ¯é¢è§" width="70%" :destroy-on-close="true"> <el-dialog v-model="previewVisible" title="ä¼ä¸ä¿¡æ¯é¢è§" width="70%" :destroy-on-close="true"> <div class="preview-content"> <div class="preview-header"> <img v-if="form.companyLogo" :src="form.companyLogo" class="preview-logo" alt="Image Preview" /> <img v-if="form.companyLogo" :src="form.companyLogo" class="preview-logo" alt="Image Preview" /> <div class="preview-title"> <h1>{{ form.companyName || 'å ¬å¸åç§°' }}</h1> <p v-if="form.website">{{ form.website }}</p> @@ -206,9 +248,12 @@ <h4>设å¤ä»ç»</h4> <div v-html="form.equipmentIntro || 'ææ ä»ç»'"></div> </div> <div v-if="form.qrCode" class="preview-section preview-qr"> <div v-if="form.qrCode" class="preview-section preview-qr"> <h4>æ«ç å ³æ³¨</h4> <img :src="form.qrCode" class="qr-image" alt="Image Preview" /> <img :src="form.qrCode" class="qr-image" alt="Image Preview" /> </div> </div> </el-dialog> @@ -216,449 +261,463 @@ </template> <script setup> import { ref, reactive, onMounted } from 'vue' import { ElMessage } from 'element-plus' import { Plus, Picture, View } from '@element-plus/icons-vue' import { getEnterpriseInfo, saveEnterpriseInfo, uploadLogo, uploadQrCode } from '@/api/basicData/enterpriseInfo.js' import { ref, reactive, onMounted } from "vue"; import { ElMessage } from "element-plus"; import { Plus, Picture, View } from "@element-plus/icons-vue"; import { getEnterpriseInfo, saveEnterpriseInfo, uploadLogo, uploadQrCode, } from "@/api/basicData/enterpriseInfo.js"; const { proxy } = getCurrentInstance() const { proxy } = getCurrentInstance(); const loading = ref(false) const saving = ref(false) const isEdit = ref(false) const previewVisible = ref(false) const uploadType = ref('') const loading = ref(false); const saving = ref(false); const isEdit = ref(false); const previewVisible = ref(false); const uploadType = ref(""); const form = reactive({ id: null, companyName: '', companyLogo: '', companyIntro: '', productIntro: '', equipmentIntro: '', contactPerson: '', contactPhone: '', companyAddress: '', website: '', qrCode: '' }) companyName: "", companyLogo: "", companyIntro: "", productIntro: "", equipmentIntro: "", contactPerson: "", contactPhone: "", companyAddress: "", website: "", qrCode: "", }); // æ·±æ·è´åå§æ°æ®ï¼ç¨äºåæ¶æ¶æ¢å¤ let originalForm = {} let originalForm = {}; const base = window.location.protocol + "//" + window.location.host; // è·åä¼ä¸ä¿¡æ¯ const fetchInfo = () => { loading.value = true getEnterpriseInfo().then(res => { if (res.code === 200 && res.data) { // å°å¾çè·¯å¾æ¼æ¥ä¸ºå®æ´å°å const base = window.location.protocol + '//' + window.location.host if (res.data.companyLogo) { res.data.companyLogo = base + res.data.companyLogo loading.value = true; getEnterpriseInfo() .then(res => { if (res.code === 200 && res.data) { // å°å¾çè·¯å¾æ¼æ¥ä¸ºå®æ´å°å if (res.data.companyLogo) { res.data.companyLogo = res.data.companyLogo; } if (res.data.qrCode) { res.data.qrCode = res.data.qrCode; } Object.assign(form, res.data); originalForm = JSON.parse(JSON.stringify(res.data)); } if (res.data.qrCode) { res.data.qrCode = base + res.data.qrCode } Object.assign(form, res.data) originalForm = JSON.parse(JSON.stringify(res.data)) } }).finally(() => { loading.value = false }) } }) .finally(() => { loading.value = false; }); }; // 忢ç¼è¾æ¨¡å¼ const toggleEdit = () => { if (isEdit.value) { // åæ¶ç¼è¾ï¼æ¢å¤åå§æ°æ® Object.assign(form, originalForm) isEdit.value = false Object.assign(form, originalForm); isEdit.value = false; } else { // è¿å ¥ç¼è¾æ¨¡å¼ originalForm = JSON.parse(JSON.stringify(form)) isEdit.value = true originalForm = JSON.parse(JSON.stringify(form)); isEdit.value = true; } } }; // é¢è§ const handlePreview = () => { previewVisible.value = true } previewVisible.value = true; }; // æä»¶ä¸ä¼ åæ ¡éª const beforeLogoUpload = (file, type) => { const isImage = file.type.startsWith('image/') const isLt2M = file.size / 1024 / 1024 < 2 const isImage = file.type.startsWith("image/"); const isLt2M = file.size / 1024 / 1024 < 2; if (!isImage) { ElMessage.error('åªè½ä¸ä¼ å¾çæä»¶ï¼') return false ElMessage.error("åªè½ä¸ä¼ å¾çæä»¶ï¼"); return false; } if (!isLt2M) { ElMessage.error('å¾ç大å°ä¸è½è¶ è¿ 2MBï¼') return false ElMessage.error("å¾ç大å°ä¸è½è¶ è¿ 2MBï¼"); return false; } uploadType.value = type uploadFileReq(file) return false } uploadType.value = type; uploadFileReq(file); return false; }; // ä¸ä¼ æä»¶è¯·æ± const uploadFileReq = (file) => { const uploadFn = uploadType.value === 'companyLogo' ? uploadLogo : uploadQrCode uploadFn(file).then(res => { if (res.code === 200) { const path = res.data.tempPath if (uploadType.value === 'companyLogo') { form.companyLogo = path const uploadFileReq = file => { const uploadFn = uploadType.value === "companyLogo" ? uploadLogo : uploadQrCode; uploadFn(file) .then(res => { if (res.code === 200) { const path = res.data.tempPath; if (uploadType.value === "companyLogo") { form.companyLogo = path; } else { form.qrCode = path; } ElMessage.success("ä¸ä¼ æå"); } else { form.qrCode = path ElMessage.error(res.msg || "ä¸ä¼ 失败"); } ElMessage.success('ä¸ä¼ æå') } else { ElMessage.error(res.msg || 'ä¸ä¼ 失败') } }).catch(() => { ElMessage.error('ä¸ä¼ 失败') }) } }) .catch(() => { ElMessage.error("ä¸ä¼ 失败"); }); }; // ä¿å const handleSave = () => { saving.value = true saveEnterpriseInfo(form).then(res => { if (res.code === 200) { ElMessage.success('ä¿åæå') isEdit.value = false fetchInfo() } else { ElMessage.error(res.msg || 'ä¿å失败') } }).finally(() => { saving.value = false }) } saving.value = true; console.log(form, "form"); saveEnterpriseInfo(form) .then(res => { if (res.code === 200) { ElMessage.success("ä¿åæå"); isEdit.value = false; fetchInfo(); } else { ElMessage.error(res.msg || "ä¿å失败"); } }) .finally(() => { saving.value = false; }); }; // åæ¶ const handleCancel = () => { Object.assign(form, originalForm) isEdit.value = false } Object.assign(form, originalForm); isEdit.value = false; }; onMounted(() => { fetchInfo() }) fetchInfo(); }); </script> <style scoped lang="scss"> .page-header { display: flex; justify-content: space-between; align-items: center; padding: 16px 24px; background: #fff; border-radius: 8px; margin-bottom: 16px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); h2 { margin: 0; font-size: 20px; font-weight: 600; color: #303133; } .header-actions { .page-header { display: flex; gap: 10px; } } .enterprise-info-card { background: #fff; padding: 24px; border-radius: 8px; box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06); } .info-section { margin-bottom: 32px; &:last-of-type { margin-bottom: 0; } .section-header { justify-content: space-between; align-items: center; padding: 16px 24px; background: #fff; border-radius: 8px; margin-bottom: 16px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); h3 { h2 { margin: 0; font-size: 16px; font-size: 20px; font-weight: 600; color: #303133; padding-bottom: 10px; border-bottom: 2px solid #409eff; display: inline-block; position: relative; } &::after { content: ''; position: absolute; left: 0; bottom: -2px; width: 60px; height: 2px; background-color: #409eff; .header-actions { display: flex; gap: 10px; } } .enterprise-info-card { background: #fff; padding: 24px; border-radius: 8px; box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06); } .info-section { margin-bottom: 32px; &:last-of-type { margin-bottom: 0; } .section-header { margin-bottom: 16px; h3 { margin: 0; font-size: 16px; font-weight: 600; color: #303133; padding-bottom: 10px; border-bottom: 2px solid #409eff; display: inline-block; position: relative; &::after { content: ""; position: absolute; left: 0; bottom: -2px; width: 60px; height: 2px; background-color: #409eff; } } } } } .info-descriptions { :deep(.el-descriptions__label) { width: 120px; background-color: #f5f7fa; font-weight: 500; .info-descriptions { :deep(.el-descriptions__label) { width: 120px; background-color: #f5f7fa; font-weight: 500; color: #606266; } :deep(.el-descriptions__content) { color: #303133; } } .content-text { color: #606266; } :deep(.el-descriptions__content) { color: #303133; } } .link-text { color: #409eff; text-decoration: none; .content-text { color: #606266; } .link-text { color: #409eff; text-decoration: none; &:hover { text-decoration: underline; } } .logo-qr-container { display: flex; gap: 40px; } .upload-item { display: flex; flex-direction: column; align-items: center; .upload-label { font-size: 14px; color: #606266; margin-bottom: 12px; font-weight: 500; &:hover { text-decoration: underline; } } .upload-wrapper { .logo-qr-container { display: flex; gap: 40px; } .upload-item { display: flex; flex-direction: column; align-items: center; .upload-label { font-size: 14px; color: #606266; margin-bottom: 12px; font-weight: 500; } .upload-wrapper { width: 140px; height: 140px; } } /* el-upload æ ¹èç¹æ¯å¤å± divï¼çæ£è§¦ååºå¨å å± .el-uploadï¼é让å å±éºæ»¡å¤å±èçº¿æ¡ */ .logo-uploader, .qr-uploader { display: flex; flex-direction: column; box-sizing: border-box; width: 140px; height: 140px; } } border: 1px dashed #d9d9d9; border-radius: 8px; cursor: pointer; transition: all 0.3s; overflow: hidden; /* el-upload æ ¹èç¹æ¯å¤å± divï¼çæ£è§¦ååºå¨å å± .el-uploadï¼é让å å±éºæ»¡å¤å±èçº¿æ¡ */ .logo-uploader, .qr-uploader { display: flex; flex-direction: column; box-sizing: border-box; width: 140px; height: 140px; border: 1px dashed #d9d9d9; border-radius: 8px; cursor: pointer; transition: all 0.3s; overflow: hidden; :deep(.el-upload) { flex: 1; min-height: 0; width: 100%; display: flex !important; flex-direction: column; align-items: stretch; box-sizing: border-box; border: none; outline: none; } :deep(.el-upload) { flex: 1; min-height: 0; width: 100%; display: flex !important; flex-direction: column; align-items: stretch; box-sizing: border-box; border: none; outline: none; :deep(.uploaded-image), :deep(.upload-placeholder) { flex: 1 1 auto; min-height: 0; min-width: 0; width: 100%; } &:hover { border-color: #409eff; } :deep(.uploaded-image) { object-fit: contain; } :deep(.upload-placeholder) { display: flex; flex-direction: column; justify-content: center; align-items: center; background: #fafafa; color: #8c939d; .upload-icon { font-size: 32px; margin-bottom: 8px; } .upload-text { font-size: 12px; } } } :deep(.uploaded-image), :deep(.upload-placeholder) { flex: 1 1 auto; min-height: 0; min-width: 0; width: 100%; } &:hover { border-color: #409eff; } :deep(.uploaded-image) { .display-image { width: 140px; height: 140px; object-fit: contain; border-radius: 8px; border: 1px solid #e4e7ed; } :deep(.upload-placeholder) { .empty-placeholder { width: 140px; height: 140px; display: flex; flex-direction: column; justify-content: center; align-items: center; background: #fafafa; color: #8c939d; border-radius: 8px; border: 1px dashed #e4e7ed; color: #c0c4cc; .upload-icon { font-size: 32px; margin-bottom: 8px; } .upload-text { span { margin-top: 8px; font-size: 12px; } } } .display-image { width: 140px; height: 140px; object-fit: contain; border-radius: 8px; border: 1px solid #e4e7ed; } .empty-placeholder { width: 140px; height: 140px; display: flex; flex-direction: column; justify-content: center; align-items: center; background: #fafafa; border-radius: 8px; border: 1px dashed #e4e7ed; color: #c0c4cc; span { margin-top: 8px; font-size: 12px; .content-editor { :deep(.el-textarea__inner) { border-radius: 6px; font-size: 14px; line-height: 1.8; } } } .content-editor { :deep(.el-textarea__inner) { .content-display { padding: 16px 20px; background: #fafafa; border-radius: 6px; font-size: 14px; border: 1px solid #ebeef5; line-height: 1.8; color: #606266; min-height: 120px; white-space: pre-wrap; :deep(.empty-text) { color: #c0c4cc; font-style: italic; } } } .content-display { padding: 16px 20px; background: #fafafa; border-radius: 6px; border: 1px solid #ebeef5; line-height: 1.8; color: #606266; min-height: 120px; white-space: pre-wrap; :deep(.empty-text) { color: #c0c4cc; font-style: italic; } } .form-actions { display: flex; justify-content: center; gap: 16px; padding-top: 24px; margin-top: 16px; border-top: 1px solid #ebeef5; } /* é¢è§å¼¹çªæ ·å¼ */ .preview-content { .preview-header { .form-actions { display: flex; align-items: center; gap: 20px; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 8px; color: #fff; margin-bottom: 20px; justify-content: center; gap: 16px; padding-top: 24px; margin-top: 16px; border-top: 1px solid #ebeef5; } .preview-logo { width: 80px; height: 80px; object-fit: contain; background: #fff; /* é¢è§å¼¹çªæ ·å¼ */ .preview-content { .preview-header { display: flex; align-items: center; gap: 20px; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 8px; padding: 8px; color: #fff; margin-bottom: 20px; .preview-logo { width: 80px; height: 80px; object-fit: contain; background: #fff; border-radius: 8px; padding: 8px; } .preview-title { h1 { margin: 0 0 8px 0; font-size: 28px; font-weight: 600; } p { margin: 0; opacity: 0.9; font-size: 14px; } } } .preview-title { h1 { margin: 0 0 8px 0; font-size: 28px; .preview-section { margin-bottom: 24px; h4 { font-size: 16px; font-weight: 600; color: #303133; margin: 0 0 12px 0; padding-bottom: 8px; border-bottom: 1px solid #ebeef5; } p { margin: 0; opacity: 0.9; font-size: 14px; margin: 8px 0; color: #606266; line-height: 1.6; } :deep(div) { line-height: 1.8; color: #606266; white-space: pre-wrap; } } .preview-qr { text-align: center; .qr-image { width: 150px; height: 150px; margin-top: 12px; border-radius: 8px; border: 1px solid #ebeef5; } } } .preview-section { margin-bottom: 24px; h4 { font-size: 16px; font-weight: 600; color: #303133; margin: 0 0 12px 0; padding-bottom: 8px; border-bottom: 1px solid #ebeef5; } p { margin: 8px 0; color: #606266; line-height: 1.6; } :deep(div) { line-height: 1.8; color: #606266; white-space: pre-wrap; } :deep(.el-divider) { margin: 16px 0; } .preview-qr { text-align: center; .qr-image { width: 150px; height: 150px; margin-top: 12px; border-radius: 8px; border: 1px solid #ebeef5; } } } :deep(.el-divider) { margin: 16px 0; } </style> src/views/equipmentManagement/amountSummary/index.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,180 @@ <template> <div class="app-container"> <el-tabs v-model="activeTab"> <el-tab-pane label="æ¥ä¿®" name="repair"> <el-form :inline="true" :model="filtersRepair"> <el-form-item label="æ¥ç模å¼"> <el-radio-group v-model="modeRepair" @change="buildColumnsRepair"> <el-radio-button :value="'month'">ææ</el-radio-button> <el-radio-button :value="'year'">æå¹´</el-radio-button> </el-radio-group> </el-form-item> <el-form-item v-if="modeRepair === 'month' || modeRepair === 'year'" label="年份"> <el-date-picker v-model="yearRepair" type="year" value-format="YYYY" format="YYYY" placeholder="è¯·éæ©å¹´ä»½" @change="refreshRepair" /> </el-form-item> <el-form-item> <el-button type="primary" @click="refreshRepair">æ¥è¯¢</el-button> <el-button @click="resetRepair">éç½®</el-button> </el-form-item> </el-form> <div class="table_list"> <PIMTable rowKey="deviceId" :column="columnsRepair" :tableData="tableDataRepair" :page="{ current: 1, size: tableDataRepair.length || 10, total: tableDataRepair.length }" /> </div> </el-tab-pane> <el-tab-pane label="ä¿å »" name="maintain"> <el-form :inline="true" :model="filtersMaintain"> <el-form-item label="æ¥ç模å¼"> <el-radio-group v-model="modeMaintain" @change="buildColumnsMaintain"> <el-radio-button :value="'month'">ææ</el-radio-button> <el-radio-button :value="'year'">æå¹´</el-radio-button> </el-radio-group> </el-form-item> <el-form-item v-if="modeMaintain === 'month' || modeMaintain === 'year'" label="年份"> <el-date-picker v-model="yearMaintain" type="year" value-format="YYYY" format="YYYY" placeholder="è¯·éæ©å¹´ä»½" @change="refreshMaintain" /> </el-form-item> <el-form-item> <el-button type="primary" @click="refreshMaintain">æ¥è¯¢</el-button> <el-button @click="resetMaintain">éç½®</el-button> </el-form-item> </el-form> <div class="table_list"> <PIMTable rowKey="deviceId" :column="columnsMaintain" :tableData="tableDataMaintain" :page="{ current: 1, size: tableDataMaintain.length || 10, total: tableDataMaintain.length }" /> </div> </el-tab-pane> </el-tabs> </div> </template> <script setup> import { ref } from "vue"; import { monthlyAmount, yearlyAmount } from "@/api/equipmentManagement/repair"; import { monthlyAmount as monthlyAmountMaintain, yearlyAmount as yearlyAmountMaintain } from "@/api/equipmentManagement/upkeep"; defineOptions({ name: "é颿±æ»" }); const activeTab = ref("repair"); // æ¥ä¿® const modeRepair = ref("month"); const yearRepair = ref(new Date().getFullYear().toString()); const filtersRepair = ref({}); const columnsRepair = ref([]); const tableDataRepair = ref([]); const fetchRepairData = async () => { try { const query = { year: yearRepair.value }; const res = modeRepair.value === "month" ? await monthlyAmount(query) : await yearlyAmount(query); const list = res?.records || res?.data || res || []; tableDataRepair.value = Array.isArray(list) ? list : []; } catch (e) { tableDataRepair.value = []; } }; const buildColumnsRepair = async () => { const base = [{ label: "设å¤åç§°", align: "center", prop: "deviceName", width: 180 }]; if (modeRepair.value === "month") { const monthCols = Array.from({ length: 12 }, (_, i) => ({ label: `${i + 1}æ`, align: "center", prop: `month${i + 1}`, })); columnsRepair.value = [ ...base, ...monthCols, { label: "æ»è®¡", align: "center", prop: "total" }, ]; } else { columnsRepair.value = [ ...base, { label: "éé¢", align: "center", prop: "totalRepairPrice" }, ]; } await fetchRepairData(); }; const refreshRepair = async () => { await buildColumnsRepair(); }; const resetRepair = () => { modeRepair.value = "month"; yearRepair.value = new Date().getFullYear().toString(); refreshRepair(); }; // ä¿å » const modeMaintain = ref("month"); const yearMaintain = ref(new Date().getFullYear().toString()); const filtersMaintain = ref({}); const columnsMaintain = ref([]); const tableDataMaintain = ref([]); const fetchMaintainData = async () => { try { const query = { year: yearMaintain.value }; const res = modeMaintain.value === "month" ? await monthlyAmountMaintain(query) : await yearlyAmountMaintain(query); const list = res?.records || res?.data || res || []; tableDataMaintain.value = Array.isArray(list) ? list : []; } catch (e) { tableDataMaintain.value = []; } }; const buildColumnsMaintain = async () => { const base = [{ label: "设å¤åç§°", align: "center", prop: "deviceName", width: 180 }]; if (modeMaintain.value === "month") { const monthCols = Array.from({ length: 12 }, (_, i) => ({ label: `${i + 1}æ`, align: "center", prop: `month${i + 1}`, })); columnsMaintain.value = [ ...base, ...monthCols, { label: "æ»è®¡", align: "center", prop: "total" }, ]; } else { columnsMaintain.value = [ ...base, { label: "éé¢", align: "center", prop: "totalRepairPrice" }, ]; } await fetchMaintainData(); }; const refreshMaintain = async () => { await buildColumnsMaintain(); }; const resetMaintain = () => { modeMaintain.value = "month"; yearMaintain.value = new Date().getFullYear().toString(); refreshMaintain(); }; buildColumnsRepair(); refreshRepair(); buildColumnsMaintain(); refreshMaintain(); </script> <style scoped> .table_list { margin-top: 10px; } </style> src/views/equipmentManagement/calibration/index.vue
@@ -57,10 +57,10 @@ <script setup> import {onMounted, ref} from "vue"; import {ElMessageBox, ElMessage} from "element-plus"; import {ElMessageBox} from "element-plus"; import useUserStore from "@/store/modules/user.js"; import CalibrationDia from "@/views/equipmentManagement/measurementEquipment/components/calibrationDia.vue"; import {ledgerRecordListPage, ledgerRecordDelete} from "@/api/equipmentManagement/calibration.js"; import {ledgerRecordListPage} from "@/api/equipmentManagement/calibration.js"; const { proxy } = getCurrentInstance(); const userStore = useUserStore() @@ -134,7 +134,6 @@ { dataType: "action", label: "æä½", width: 140, align: "center", fixed: 'right', operation: [ @@ -144,16 +143,9 @@ clickFun: (row) => { openCalibrationDia("edit", row); }, }, { name: "å é¤", type: "text", style: { color: "#F56C6C" }, clickFun: (row) => { handleDelete(row); }, disabled: (row) => { return row.userId !== userStore.id } }, ], }, @@ -201,26 +193,6 @@ calibrationDia.value?.openDialog(type, row) }) } // å é¤è®°å½ const handleDelete = (row) => { ElMessageBox.confirm(`确认å é¤è®¡éå¨å ·ç¼å·ä¸º"${row.code}"çæ£å®è®°å½åï¼`, "å é¤ç¡®è®¤", { confirmButtonText: "确认", cancelButtonText: "åæ¶", type: "warning", }) .then(() => { ledgerRecordDelete([row.id]).then(() => { ElMessage.success("å 餿å"); getList(); }).catch(() => { ElMessage.error("å é¤å¤±è´¥"); }); }) .catch(() => { proxy.$modal.msg("已忶å é¤"); }); }; // å¯¼åº const handleOut = () => { src/views/equipmentManagement/defectManagement/index.vue
@@ -43,7 +43,7 @@ <el-dialog title="ç»è®°è®¾å¤ç¼ºé·" v-model="showRegisterDialog" width="50%"> <el-form :model="defectForm" :rules="defectRules" ref="defectFormRef" label-width="100px"> <el-form-item label="设å¤åç§°" prop="deviceName"> <el-select v-model="defectForm.deviceLedgerId" @change="setDeviceModel"> <el-select v-model="defectForm.deviceLedgerId" @change="setDeviceModel" filterable> <el-option v-for="(item, index) in deviceOptions" :key="index" src/views/equipmentManagement/inspectionManagement/components/formDia.vue
@@ -1,116 +1,102 @@ <template> <div> <el-dialog :title="operationType === 'add' ? 'æ°å¢å·¡æ£ä»»å¡' : 'ç¼è¾å·¡æ£ä»»å¡'" v-model="dialogVisitable" width="800px" @close="cancel"> <el-form ref="formRef" :model="form" :rules="rules" label-width="120px"> <el-row> <el-col :span="12"> <el-form-item label="设å¤åç§°" prop="taskId"> <el-select v-model="form.taskId" @change="setDeviceModel" filterable> <el-option v-for="(item, index) in deviceOptions" :key="index" :label="item.deviceName" :value="item.id" ></el-option> </el-select> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="å·¡æ£äºº" prop="inspector"> <el-select v-model="form.inspector" filterable default-first-option :reserve-keyword="false" 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-row> <el-row> <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-col :span="12"> <el-form-item label="ç»è®°æ¶é´" prop="dateStr"> <el-date-picker v-model="form.dateStr" type="date" placeholder="éæ©ç»è®°æ¥æ" format="YYYY-MM-DD" value-format="YYYY-MM-DD" style="width: 100%" /> </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> <div> <el-dialog :title="operationType === 'add' ? 'æ°å¢å·¡æ£ä»»å¡' : 'ç¼è¾å·¡æ£ä»»å¡'" v-model="dialogVisitable" width="800px" @close="cancel"> <el-form ref="formRef" :model="form" :rules="rules" label-width="120px"> <el-row> <el-col :span="12"> <el-form-item label="设å¤åç§°" prop="taskIds"> <el-select v-model="form.taskIds" @change="setDeviceModel" multiple filterable> <el-option v-for="(item, index) in deviceOptions" :key="index" :label="item.deviceName" :value="item.id" ></el-option> </el-select> </el-form-item> </el-col> <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-row> <el-row> <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 type="primary" @click="submitForm">ä¿å</el-button> <el-button @click="cancel">åæ¶</el-button> </div> </template> </el-dialog> </div> </template> <script setup> @@ -127,117 +113,128 @@ const operationType = ref('add'); const deviceOptions = ref([]); const data = reactive({ form: { taskId: undefined, taskName: undefined, inspector: '', inspectorIds: '', remarks: '', frequencyType: '', frequencyDetail: '', week: '', time: '', dateStr: '' }, rules: { taskId: [{ required: true, message: "è¯·éæ©è®¾å¤", trigger: "change" },], inspector: [{ required: true, message: "请è¾å ¥å·¡æ£äºº", trigger: "blur" },], dateStr: [{ required: true, message: "è¯·éæ©ç»è®°æ¶é´", trigger: "change" }] } form: { taskIds: [], taskName: undefined, inspector: '', inspectorIds: '', remarks: '', frequencyType: '', frequencyDetail: '', week: '', time: '' }, rules: { taskIds: [{ required: true, message: "è¯·éæ©è®¾å¤", trigger: "change" },], inspector: [{ required: true, message: "请è¾å ¥å·¡æ£äºº", trigger: "blur" },], } }) const { form, rules } = toRefs(data) const userList = ref([]) const loadDeviceName = async () => { const { data } = await getDeviceLedger(); deviceOptions.value = data; const { data } = await getDeviceLedger(); deviceOptions.value = data; }; const setDeviceModel = (id) => { const option = deviceOptions.value.find((item) => item.id === id); if (option) { form.value.taskName = option.deviceName; } const setDeviceModel = (ids) => { if (!ids || ids.length === 0) { form.value.taskIds = [] form.value.taskName = undefined return } const selectedDevices = deviceOptions.value.filter((item) => ids.includes(item.id)) if (selectedDevices.length > 0) { form.value.taskIds = ids form.value.taskName = selectedDevices.map(d => d.deviceName).join(',') } } // æå¼å¼¹æ¡ const openDialog = async (type, row) => { dialogVisitable.value = true operationType.value = type // é置表å resetForm(); // å è½½ç¨æ·å表 userListNoPageByTenantId().then((res) => { userList.value = res.data; }); // å 载设å¤å表 await loadDeviceName(); if (type === 'edit' && row) { form.value = {...row} form.value.inspector = form.value.inspectorIds.split(',').map(Number) // 妿æè®¾å¤IDï¼èªå¨è®¾ç½®è®¾å¤ä¿¡æ¯ if (form.value.taskId) { setDeviceModel(form.value.taskId); } } dialogVisitable.value = true operationType.value = type // é置表å resetForm(); // å è½½ç¨æ·å表 userListNoPageByTenantId().then((res) => { userList.value = res.data; }); // å 载设å¤å表 await loadDeviceName(); if (type === 'edit' && row) { form.value = {...row} form.value.inspector = form.value.inspectorIds.split(',').map(Number) // 妿æè®¾å¤IDæ°ç»ï¼è½¬æ¢ä¸ºæ°ç»å¹¶è®¾ç½®è®¾å¤ä¿¡æ¯ if (row.taskIds) { form.value.taskIds = row.taskIds.split(',').map(id => parseInt(id.trim())) setDeviceModel(form.value.taskIds) } } } // å ³éå¯¹è¯æ¡ const cancel = () => { resetForm() dialogVisitable.value = false emit('closeDia') resetForm() dialogVisitable.value = false emit('closeDia') } // é置表å彿° const resetForm = () => { if (proxy.$refs.formRef) { proxy.$refs.formRef.resetFields() } // éç½®è¡¨åæ°æ®ç¡®ä¿è®¾å¤ä¿¡æ¯æ£ç¡®éç½® form.value = { taskId: undefined, taskName: undefined, inspector: '', inspectorIds: '', remarks: '', frequencyType: '', frequencyDetail: '', week: '', time: '' } if (proxy.$refs.formRef) { proxy.$refs.formRef.resetFields() } // éç½®è¡¨åæ°æ®ç¡®ä¿è®¾å¤ä¿¡æ¯æ£ç¡®éç½® form.value = { taskIds: [], taskName: undefined, inspector: '', inspectorIds: '', remarks: '', frequencyType: '', frequencyDetail: '', week: '', time: '' } } // æäº¤è¡¨å const submitForm = () => { proxy.$refs["formRef"].validate(async valid => { if (valid) { try { 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 await addOrEditTimingTask(form.value) cancel() proxy.$modal.msgSuccess('æäº¤æå') } catch (error) { proxy.$modal.msgError('æäº¤å¤±è´¥ï¼è¯·éè¯') } } }) proxy.$refs["formRef"].validate(async valid => { if (valid) { try { form.value.inspectorIds = form.value.inspector.join(',') delete form.value.inspector // å¤ç taskIds å taskName if (form.value.taskIds && Array.isArray(form.value.taskIds)) { form.value.taskIds = form.value.taskIds.join(',') } 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 await addOrEditTimingTask(form.value) cancel() proxy.$modal.msgSuccess('æäº¤æå') } catch (error) { proxy.$modal.msgError('æäº¤å¤±è´¥ï¼è¯·éè¯') } } }) } defineExpose({ openDialog }) </script> src/views/equipmentManagement/inspectionManagement/components/viewFiles.vue
@@ -32,7 +32,7 @@ <!-- ç产å --> <div class="form-container"> <div class="title">ç产ä¸</div> <div class="title">ç产å</div> <!-- å¾çå表 --> <div style="display: flex; flex-wrap: wrap;"> @@ -59,7 +59,7 @@ <!-- ç产é®é¢ --> <div class="form-container"> <div class="title">ç产å</div> <div class="title">ç产é®é¢</div> <!-- å¾çå表 --> <div style="display: flex; flex-wrap: wrap;"> @@ -100,7 +100,7 @@ <!-- è§é¢ --> <div v-else-if="mediaType === 'video'" style="position: relative;"> <Video <video :src="mediaList[currentMediaIndex]" autoplay controls @@ -114,6 +114,7 @@ <script setup> import { ref } from 'vue'; import VueEasyLightbox from 'vue-easy-lightbox'; const { proxy } = getCurrentInstance(); // æ§å¶å¼¹çªæ¾ç¤º const dialogVisitable = ref(false); @@ -133,26 +134,83 @@ const currentMediaIndex = ref(0); const mediaList = ref([]); // åå¨å½åè¦æ¥ççåªä½å表ï¼å«å¾çåè§é¢å¯¹è±¡ï¼ const mediaType = ref('image'); // image | video const javaApi = proxy.javaApi; // å¤ç URLï¼å° Windows è·¯å¾è½¬æ¢ä¸ºå¯è®¿é®ç URL function processFileUrl(fileUrl) { if (!fileUrl) return ''; // 妿 URL æ¯ Windows è·¯å¾æ ¼å¼ï¼å å«åææ ï¼ï¼éè¦è½¬æ¢ if (fileUrl && fileUrl.indexOf('\\') > -1) { // æ¥æ¾ uploads å ³é®åçä½ç½®ï¼ä»é£éå¼å§æåç¸å¯¹è·¯å¾ const uploadsIndex = fileUrl.toLowerCase().indexOf('uploads'); if (uploadsIndex > -1) { // ä» uploads å¼å§æåè·¯å¾ï¼å¹¶å°åææ æ¿æ¢ä¸ºæ£ææ const relativePath = fileUrl.substring(uploadsIndex).replace(/\\/g, '/'); fileUrl = '/' + relativePath; } else { // å¦ææ²¡ææ¾å° uploadsï¼æåæåä¸ä¸ªç®å½åæä»¶å const parts = fileUrl.split('\\'); const fileName = parts[parts.length - 1]; fileUrl = '/uploads/' + fileName; } } // ç¡®ä¿ææé http å¼å¤´ç URL 齿¼æ¥ baseUrl if (fileUrl && !fileUrl.startsWith('http')) { // ç¡®ä¿è·¯å¾ä»¥ / å¼å¤´ if (!fileUrl.startsWith('/')) { fileUrl = '/' + fileUrl; } // æ¼æ¥ 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.contentType?.startsWith('image/')) { images.push(item.url); } else if (item.contentType?.startsWith('video/')) { videos.push(item.url); if (!item || !item.url) return; // å¤çæä»¶ URL const fileUrl = processFileUrl(item.url); // æ ¹æ®æä»¶æ©å±å夿æ¯å¾çè¿æ¯è§é¢ const urlLower = fileUrl.toLowerCase(); if (urlLower.match(/\.(jpg|jpeg|png|gif|bmp|webp)$/)) { images.push(fileUrl); } else if (urlLower.match(/\.(mp4|avi|mov|wmv|flv|mkv|webm)$/)) { videos.push(fileUrl); } else if (item.contentType) { // 妿æ contentTypeï¼ä½¿ç¨ contentType 夿 if (item.contentType.startsWith('image/')) { images.push(fileUrl); } else if (item.contentType.startsWith('video/')) { videos.push(fileUrl); } } }); 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); // ä½¿ç¨æ£ç¡®çåæ®µåï¼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; src/views/equipmentManagement/inspectionManagement/index.vue
@@ -1,78 +1,77 @@ <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> <div style="display: flex;flex-direction: row;justify-content: space-between;margin-bottom: 10px;"> <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 @click="handleOut">导åº</el-button> </el-space> <el-space v-else> <el-button @click="handleOut">导åº</el-button> </el-space> </div> <div> <div> <PIMTable :table-loading="tableLoading" :table-data="tableData" :column="tableColumns" @selection-change="handleSelectionChange" :is-selection="true" :border="true" :table-style="{ width: '100%', height: 'calc(100vh - 23em)' }" :page="{ current: pageNum, size: pageSize, total: total, }" @pagination="pagination" > <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> </PIMTable> </div> </div> </el-card> <form-dia ref="formDia" @closeDia="handleQuery"></form-dia> <view-files ref="viewFiles"></view-files> </div> <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> <div style="display: flex;flex-direction: row;justify-content: space-between;margin-bottom: 10px;"> <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 @click="handleOut">导åº</el-button> </el-space> <el-space v-else> <el-button @click="handleOut">导åº</el-button> </el-space> </div> <div> <PIMTable :table-loading="tableLoading" :table-data="tableData" :column="tableColumns" @selection-change="handleSelectionChange" @pagination="handlePagination" :is-selection="true" :border="true" :page="{ current: pageNum, size: pageSize, total: total, layout: 'total, sizes, prev, pager, next, jumper' }" :table-style="{ width: '100%', height: 'calc(100vh - 23em)' }" > <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> </PIMTable> </div> </el-card> <form-dia ref="formDia" @closeDia="handleQuery"></form-dia> <view-files ref="viewFiles"></view-files> </div> </template> <script setup> @@ -81,16 +80,15 @@ import { ElMessageBox } from "element-plus"; // ç»ä»¶å¼å ¥ import Pagination from "@/components/Pagination/index.vue"; import PIMTable from "@/components/PIMTable/PIMTable.vue"; import FormDia from "@/views/equipmentManagement/inspectionManagement/components/formDia.vue"; import ViewFiles from "@/views/equipmentManagement/inspectionManagement/components/viewFiles.vue"; // æ¥å£å¼å ¥ import { delTimingTask, inspectionTaskList, timingTaskList delTimingTask, inspectionTaskList, timingTaskList } from "@/api/inspectionManagement/index.js"; // å ¨å±åé @@ -100,14 +98,14 @@ // æ¥è¯¢åæ° const queryParams = reactive({ searchAll: "", searchAll: "", }); // åéæ¡é ç½® const activeRadio = ref("taskManage"); const radios = reactive([ { name: "taskManage", label: "宿¶ä»»å¡ç®¡ç" }, { name: "task", label: "宿¶ä»»å¡è®°å½" }, { name: "taskManage", label: "宿¶ä»»å¡ç®¡ç" }, { name: "task", label: "宿¶ä»»å¡è®°å½" }, ]); // è¡¨æ ¼æ°æ® @@ -122,233 +120,244 @@ // åé ç½® const columns = ref([ { prop: "taskName", label: "å·¡æ£ä»»å¡åç§°", minWidth: 160 }, { prop: "remarks", label: "夿³¨", minWidth: 150 }, { prop: "inspector", label: "æ§è¡å·¡æ£äºº", minWidth: 150, slot: "inspector" }, { prop: "frequencyType", label: "颿¬¡", minWidth: 150, formatData: (cell) => ({ DAILY: "æ¯æ¥", WEEKLY: "æ¯å¨", MONTHLY: "æ¯æ", QUARTERLY: "å£åº¦" }[cell] || "") }, { 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: "dateStr", label: "ç»è®°æ¥æ", minWidth: 100 }, { prop: "taskName", label: "å·¡æ£ä»»å¡åç§°", minWidth: 160 }, { 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 getOperationColumn = (operations) => { if (!operations || operations.length === 0) return null; const operationConfig = { label: "æä½", width: 130, fixed: "right", dataType: "action", operation: operations.map(op => { switch (op) { case 'edit': return { name: "ç¼è¾", clickFun: handleAdd, color: "#409EFF" }; case 'viewFile': return { name: "æ¥çéä»¶", clickFun: viewFile, color: "#67C23A" }; default: return null; } }).filter(Boolean) }; return operationConfig; if (!operations || operations.length === 0) return null; const operationConfig = { label: "æä½", width: 130, fixed: "right", dataType: "action", operation: operations.map(op => { switch (op) { case 'edit': return { name: "ç¼è¾", clickFun: handleAdd, color: "#409EFF" }; case 'viewFile': return { name: "æ¥çéä»¶", clickFun: viewFile, color: "#67C23A" }; default: return null; } }).filter(Boolean) }; return operationConfig; }; onMounted(() => { radioChange('taskManage'); radioChange('taskManage'); }); // åéåå const radioChange = (value) => { if (value === "taskManage") { const operationColumn = getOperationColumn(['edit']); tableColumns.value = [...columns.value, ...(operationColumn ? [operationColumn] : [])]; operationsArr.value = ['edit']; } else if (value === "task") { const operationColumn = getOperationColumn(['viewFile']); tableColumns.value = [...columns.value, ...(operationColumn ? [operationColumn] : [])]; operationsArr.value = ['viewFile']; } pageNum.value = 1; pageSize.value = 10; getList(); if (value === "taskManage") { const operationColumn = getOperationColumn(['edit']); tableColumns.value = [...columns.value, ...(operationColumn ? [operationColumn] : [])]; operationsArr.value = ['edit']; } else if (value === "task") { const operationColumn = getOperationColumn(['viewFile']); const statusColumn = { prop: "status", label: "ä»»å¡ç¶æ", minWidth: 100, dataType: "tag", formatType: (row) => { if (row.status === '已巡æ£') return 'success'; return 'warning'; } }; tableColumns.value = [...columns.value, statusColumn, ...(operationColumn ? [operationColumn] : [])]; operationsArr.value = ['viewFile']; } pageNum.value = 1; pageSize.value = 10; getList(); }; // æ¥è¯¢æä½ const handleQuery = () => { pageNum.value = 1; pageSize.value = 10; getList(); pageNum.value = 1; pageSize.value = 10; getList(); }; const pagination = (obj) => { pageNum.value = obj.page; pageSize.value = obj.limit; // å页å¤ç 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 (activeRadio.value === "task") { apiCall = inspectionTaskList(params); } else { apiCall = timingTaskList(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; }); tableLoading.value = true; const params = { ...queryParams, size: pageSize.value, current: pageNum.value }; let apiCall; if (activeRadio.value === "task") { apiCall = inspectionTaskList(params); } else { apiCall = timingTaskList(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(); for (const key in queryParams) { if (!["pageNum", "pageSize"].includes(key)) { queryParams[key] = ""; } } handleQuery(); }; // æ°å¢ / ç¼è¾ const handleAdd = (row) => { const type = row ? 'edit' : 'add'; nextTick(() => { formDia.value?.openDialog(type, row); }); const type = row ? 'edit' : 'add'; nextTick(() => { formDia.value?.openDialog(type, row); }); }; // æ¥çéä»¶ const viewFile = (row) => { nextTick(() => { viewFiles.value?.openDialog(row); }); nextTick(() => { viewFiles.value?.openDialog(row); }); }; // å é¤æä½ const handleDelete = () => { if (!selectedRows.value.length) { proxy.$modal.msgWarning("è¯·éæ©è¦å é¤çæ°æ®"); return; } const deleteIds = selectedRows.value.map(item => item.id); proxy.$modal.confirm('æ¯å¦ç¡®è®¤å 餿鿰æ®é¡¹ï¼').then(() => { return delTimingTask(deleteIds); }).then(() => { proxy.$modal.msgSuccess("å 餿å"); handleQuery(); }).catch(() => {}); if (!selectedRows.value.length) { proxy.$modal.msgWarning("è¯·éæ©è¦å é¤çæ°æ®"); return; } const deleteIds = selectedRows.value.map(item => item.id); proxy.$modal.confirm('æ¯å¦ç¡®è®¤å 餿鿰æ®é¡¹ï¼').then(() => { return delTimingTask(deleteIds); }).then(() => { proxy.$modal.msgSuccess("å 餿å"); handleQuery(); }).catch(() => {}); }; // å¤éåæ´ const handleSelectionChange = (selection) => { selectedRows.value = selection; selectedRows.value = selection; }; // å¯¼åº const handleOut = () => { ElMessageBox.confirm("éä¸çå 容å°è¢«å¯¼åºï¼æ¯å¦ç¡®è®¤å¯¼åºï¼", "导åº", { confirmButtonText: "确认", cancelButtonText: "åæ¶", type: "warning", }) .then(() => { // æ ¹æ®å½åéä¸çæ ç¾é¡µè°ç¨ä¸åçå¯¼åºæ¥å£ if (activeRadio.value === "taskManage") { // 宿¶ä»»å¡ç®¡ç proxy.download("/timingTask/export", {}, "宿¶ä»»å¡ç®¡ç.xlsx"); } else if (activeRadio.value === "task") { // 宿¶ä»»å¡è®°å½ proxy.download("/inspectionTask/export", {}, "宿¶ä»»å¡è®°å½.xlsx"); } }) .catch(() => { proxy.$modal.msg("已忶"); }); ElMessageBox.confirm("éä¸çå 容å°è¢«å¯¼åºï¼æ¯å¦ç¡®è®¤å¯¼åºï¼", "导åº", { confirmButtonText: "确认", cancelButtonText: "åæ¶", type: "warning", }) .then(() => { // æ ¹æ®å½åéä¸çæ ç¾é¡µè°ç¨ä¸åçå¯¼åºæ¥å£ if (activeRadio.value === "taskManage") { // 宿¶ä»»å¡ç®¡ç proxy.download("/timingTask/export", {}, "宿¶ä»»å¡ç®¡ç.xlsx"); } else if (activeRadio.value === "task") { // 宿¶ä»»å¡è®°å½ proxy.download("/inspectionTask/export", {}, "宿¶ä»»å¡è®°å½.xlsx"); } }) .catch(() => { proxy.$modal.msg("已忶"); }); }; </script> <style scoped> .person-tags { display: flex; flex-wrap: wrap; gap: 4px; display: flex; flex-wrap: wrap; gap: 4px; } .person-tag { margin-right: 4px; margin-bottom: 2px; margin-right: 4px; margin-bottom: 2px; } .no-data { color: #909399; font-size: 14px; color: #909399; font-size: 14px; } </style> src/views/equipmentManagement/ledger/Form.vue
@@ -32,71 +32,29 @@ </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="å¯ç¨ææ§" prop="enableDepreciation"> <el-switch v-model="form.enableDepreciation" :active-value="true" :inactive-value="false" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="æ°é" prop="number"> <el-input-number :min="1" style="width: 100%" v-model="form.number" disabled disabled placeholder="请è¾å ¥æ°é" @change="mathNum" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="å«ç¨åä»·" prop="taxIncludingPriceUnit"> <el-input-number :step="0.01" :min="0" style="width: 100%" <el-form-item label="èµäº§åå¼" prop="taxIncludingPriceUnit"> <el-input-number :min="0" style="width: 100%" :precision="2" v-model="form.taxIncludingPriceUnit" placeholder="请è¾å ¥å«ç¨åä»·" placeholder="请è¾å ¥èµäº§åå¼" maxlength="10" @change="mathNum" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="å«ç¨æ»ä»·" prop="taxIncludingPriceTotal"> <el-input v-model="form.taxIncludingPriceTotal" placeholder="èªå¨çæ" type="number" disabled /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="ç¨ç(%)" prop="taxRate"> <!-- <el-input v-model="form.taxRate" placeholder="请è¾å ¥ç¨ç" type="number" > <template #append> % </template> </el-input> --> <el-select v-model="form.taxRate" placeholder="è¯·éæ©" clearable @change="mathNum" > <el-option label="1" :value="1" /> <el-option label="6" :value="6" /> <el-option label="13" :value="13" /> </el-select> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="ä¸å«ç¨æ»ä»·" prop="unTaxIncludingPriceTotal"> <el-input v-model="form.unTaxIncludingPriceTotal" placeholder="èªå¨çæ" type="number" disabled /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="å¯ç¨ææ§" prop="enableDepreciation"> <el-switch v-model="form.enableDepreciation" :active-value="true" :inactive-value="false" /> </el-form-item> </el-col> <!-- <el-col :span="12"> <el-form-item label="å½å ¥äºº" prop="createUser"> <el-input v-model="form.createUser" placeholder="请è¾å ¥å½å ¥äºº" /> @@ -137,11 +95,6 @@ // import useUserStore from "@/store/modules/user"; import { getLedgerById } from "@/api/equipmentManagement/ledger"; import dayjs from "dayjs"; import { calculateTaxIncludeTotalPrice, calculateTaxExclusiveTotalPrice, } from "@/utils/summarizeTable"; import { ElMessage } from "element-plus"; import {ref} from "vue"; defineOptions({ @@ -151,13 +104,25 @@ const operationType = ref(''); const formRules = { deviceName: [{ required: true, trigger: "blur", message: "请è¾å ¥" }], deviceModel: [{ required: true, trigger: "blur", message: "请è¾å ¥" }], supplierName: [{ required: true, trigger: "blur", message: "请è¾å ¥" }], unit: [{ required: true, trigger: "blur", message: "请è¾å ¥" }], number: [{ required: true, trigger: "blur", message: "请è¾å ¥" }], taxIncludingPriceUnit: [{ required: true, trigger: "blur", message: "请è¾å ¥" }], taxRate: [{ required: true, trigger: "change", message: "请è¾å ¥" }], planRuntimeTime: [{ required: true, trigger: "change", message: "è¯·éæ©" }], deviceModel: [{ trigger: "blur", message: "请è¾å ¥" }], supplierName: [{ trigger: "blur", message: "请è¾å ¥" }], unit: [{ trigger: "blur", message: "请è¾å ¥" }], taxIncludingPriceUnit: [ { required: true, trigger: "blur", validator: (rule, value, callback) => { if (value === undefined || value === null || value === '') { callback(new Error("请è¾å ¥èµäº§åå¼")); } else if (typeof value === 'number' && value >= 0) { callback(); } else { callback(new Error("请è¾å ¥ææçèµäº§åå¼")); } } } ], planRuntimeTime: [{ trigger: "change", message: "è¯·éæ©" }], } const { form, resetForm } = useFormData({ @@ -169,10 +134,7 @@ enableDepreciation: false, // æ¯å¦å¯ç¨ææ§ unit: undefined, // åä½ number: 1, // æ°é taxIncludingPriceUnit: undefined, // å«ç¨åä»· taxIncludingPriceTotal: undefined, // å«ç¨æ»ä»· taxRate: undefined, // ç¨ç unTaxIncludingPriceTotal: undefined, // ä¸å«ç¨æ»ä»· taxIncludingPriceUnit: undefined, // èµäº§åå¼ // createUser: useUserStore().nickName, // å½å ¥äºº createTime: dayjs().format("YYYY-MM-DD HH:mm:ss"), // å½å ¥æ¥æ planRuntimeTime: dayjs().format("YYYY-MM-DD"), // å½å ¥æ¥æ @@ -193,29 +155,11 @@ form.unit = data.unit; form.number = 1; form.taxIncludingPriceUnit = data.taxIncludingPriceUnit; form.taxIncludingPriceTotal = data.taxIncludingPriceTotal; form.taxRate = data.taxRate; form.unTaxIncludingPriceTotal = data.unTaxIncludingPriceTotal; form.createTime = data.createTime; } }; const mathNum = () => { if (!form.taxIncludingPriceUnit) { ElMessage.error("请è¾å ¥åä»·"); return; } form.taxIncludingPriceTotal = calculateTaxIncludeTotalPrice( form.taxIncludingPriceUnit, form.number ); if (form.taxRate) { form.unTaxIncludingPriceTotal = calculateTaxExclusiveTotalPrice( form.taxIncludingPriceTotal, form.taxRate ); } }; // æ¸ é¤è¡¨åæ ¡éªç¶æ const clearValidate = () => { src/views/equipmentManagement/ledger/Modal.vue
@@ -1,5 +1,5 @@ <template> <el-dialog :title="modalOptions.title" v-model="visible" @close="close" draggable> <el-dialog :title="modalOptions.title" v-model="visible" @close="close"> <Form ref="formRef"></Form> <template #footer> <el-button type="primary" @click="sendForm" :loading="loading"> src/views/equipmentManagement/ledger/index.vue
@@ -55,6 +55,7 @@ <div></div> <div> <el-button type="primary" @click="add" icon="Plus"> æ°å¢ </el-button> <el-button plain icon="Upload" @click="handleImport">å¯¼å ¥</el-button> <el-button @click="handleOut" icon="download">导åº</el-button> <el-button type="danger" @@ -82,13 +83,58 @@ </PIMTable> </div> <Modal ref="modalRef" @success="getTableData"></Modal> <el-dialog v-model="qrDialogVisible" title="äºç»´ç " width="300px" draggable> <el-dialog v-model="qrDialogVisible" title="äºç»´ç " width="300px"> <div style="text-align:center;"> <img :src="qrCodeUrl" alt="äºç»´ç " style="width:200px;height:200px;" /> <div style="margin-top:6px;font-size:14px;color:#333;">{{ qrRowData?.deviceName }}</div> <div style="margin:10px 0;"> <el-button type="primary" @click="downloadQRCode">ä¸è½½äºç»´ç å¾ç</el-button> </div> </div> </el-dialog> <!-- ç¨æ·å¯¼å ¥å¯¹è¯æ¡ --> <el-dialog :title="upload.title" v-model="upload.open" width="400px" append-to-body > <el-upload ref="uploadRef" :limit="1" accept=".xlsx, .xls" :headers="upload.headers" :action="upload.url + '?updateSupport=' + upload.updateSupport" :disabled="upload.isUploading" :before-upload="upload.beforeUpload" :on-progress="upload.onProgress" :on-success="upload.onSuccess" :on-error="upload.onError" :on-change="upload.onChange" :auto-upload="false" drag > <el-icon class="el-icon--upload"><upload-filled /></el-icon> <div class="el-upload__text">å°æä»¶æå°æ¤å¤ï¼æ<em>ç¹å»ä¸ä¼ </em></div> <template #tip> <div class="el-upload__tip text-center"> <span>ä» å è®¸å¯¼å ¥xlsãxlsxæ ¼å¼æä»¶ã</span> <el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline" @click="importTemplate" >ä¸è½½æ¨¡æ¿</el-link > </div> </template> </el-upload> <template #footer> <div class="dialog-footer"> <el-button type="primary" @click="submitFileForm">ç¡® å®</el-button> <el-button @click="upload.open = false">å æ¶</el-button> </div> </template> </el-dialog> </div> </template> @@ -103,6 +149,7 @@ import dayjs from "dayjs"; import QRCode from "qrcode"; import { ref } from "vue"; import { getToken } from "@/utils/auth.js"; defineOptions({ name: "设å¤å°è´¦", @@ -171,24 +218,9 @@ prop: "number", }, { label: "å«ç¨åä»·", label: "èµäº§åå¼", align: "center", prop: "taxIncludingPriceUnit", }, { label: "å«ç¨æ»ä»·", align: "center", prop: "taxIncludingPriceTotal", }, { label: "ç¨ç", align: "center", prop: "taxRate", }, { label: "ä¸å«ç¨æ»ä»·", align: "center", prop: "unTaxIncludingPriceTotal", }, { label: "å¯ç¨ææ§", @@ -231,6 +263,69 @@ }, ] ); const upload = reactive({ // æ¯å¦æ¾ç¤ºå¼¹åºå±ï¼å®¢æ·å¯¼å ¥ï¼ open: false, // å¼¹åºå±æ é¢ï¼å®¢æ·å¯¼å ¥ï¼ title: "", // æ¯å¦ç¦ç¨ä¸ä¼ isUploading: false, // 设置ä¸ä¼ ç请æ±å¤´é¨ headers: { Authorization: "Bearer " + getToken() }, // ä¸ä¼ çå°å url: import.meta.env.VITE_APP_BASE_API + "/device/ledger/import", // æä»¶ä¸ä¼ åçåè° beforeUpload: (file) => { console.log('æä»¶å³å°ä¸ä¼ ', file); // å¯ä»¥å¨æ¤å¤åæä»¶ç±»åæå¤§å°æ ¡éª const isValid = file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' || file.name.endsWith('.xlsx') || file.name.endsWith('.xls'); if (!isValid) { proxy.$modal.msgError("åªè½ä¸ä¼ Excel æä»¶"); } return isValid; }, // æä»¶ç¶ææ¹åæ¶çåè° onChange: (file, fileList) => { console.log('æä»¶ç¶ææ¹å', file, fileList); }, // æä»¶ä¸ä¼ æåæ¶çåè° onSuccess: (response, file, fileList) => { console.log('ä¸ä¼ æå', response, file, fileList); upload.isUploading = false; if(response.code === 200){ proxy.$modal.msgSuccess("æä»¶ä¸ä¼ æå"); upload.open = false; proxy.$refs["uploadRef"].clearFiles(); getTableData(); }else if(response.code === 500){ proxy.$modal.msgError(response.msg); }else{ proxy.$modal.msgWarning(response.msg); } }, // æä»¶ä¸ä¼ 失败æ¶çåè° onError: (error, file, fileList) => { console.error('ä¸ä¼ 失败', error, file, fileList); upload.isUploading = false; proxy.$modal.msgError("æä»¶ä¸ä¼ 失败"); }, // æä»¶ä¸ä¼ è¿åº¦åè° onProgress: (event, file, fileList) => { console.log('ä¸ä¼ ä¸...', event.percent); } }); /** æäº¤ä¸ä¼ æä»¶ */ const submitFileForm = () => { upload.isUploading = true; proxy.$refs["uploadRef"].submit(); } /** ä¸è½½æ¨¡æ¿ */ const importTemplate = () => { proxy.download("/device/ledger/downloadTemplate", {}, "设å¤å°è´¦æ¨¡æ¿.xlsx"); } // å¤éååä»ä¹ const handleSelectionChange = (selectionList) => { @@ -275,6 +370,11 @@ } getTableData(); }; /** å¯¼å ¥æé®æä½ */ const handleImport = () => { upload.title = "设å¤å°è´¦å¯¼å ¥"; upload.open = true; } const handleOut = () => { ElMessageBox.confirm("éä¸çå 容å°è¢«å¯¼åºï¼æ¯å¦ç¡®è®¤å¯¼åºï¼", "导åº", { @@ -299,10 +399,44 @@ }; const downloadQRCode = () => { const a = document.createElement("a"); a.href = qrCodeUrl.value; a.download = `${qrRowData.value.deviceName || "äºç»´ç "}.png`; a.click(); const name = qrRowData.value?.deviceName || "äºç»´ç "; const img = new Image(); img.src = qrCodeUrl.value; img.onload = () => { const padding = 10; const qrSize = 200; const textHeight = 24; // space for text const width = qrSize + padding * 2; const height = qrSize + padding * 2 + textHeight; const canvas = document.createElement("canvas"); canvas.width = width; canvas.height = height; const ctx = canvas.getContext("2d"); // background ctx.fillStyle = "#ffffff"; ctx.fillRect(0, 0, width, height); // draw QR centered ctx.drawImage(img, padding, padding, qrSize, qrSize); // draw name centered below ctx.fillStyle = "#333"; ctx.font = "14px Arial"; ctx.textAlign = "center"; ctx.textBaseline = "middle"; const maxTextWidth = width - padding * 2; let displayName = name; // ellipsis if too long while (ctx.measureText(displayName).width > maxTextWidth && displayName.length > 0) { displayName = displayName.slice(0, -1); } if (displayName !== name) displayName = displayName + "â¦"; ctx.fillText(displayName, width / 2, qrSize + padding + textHeight / 2); const dataUrl = canvas.toDataURL("image/png"); const a = document.createElement("a"); a.href = dataUrl; a.download = `${name}.png`; a.click(); }; }; onMounted(() => { src/views/equipmentManagement/measurementEquipment/components/calibrationDia.vue
@@ -4,7 +4,6 @@ v-model="dialogFormVisible" title="计éå¨å ·" width="50%" draggable @close="closeDia" > <el-form @@ -68,9 +67,7 @@ <el-select v-model="form.userId" placeholder="è¯·éæ©" filterable default-first-option :reserve-keyword="false" disabled clearable > <el-option @@ -132,7 +129,6 @@ import {getToken} from "@/utils/auth.js"; import {ledgerRecordUpdate, ledgerRecordVerifying} from "@/api/equipmentManagement/calibration.js"; import {delLedgerFile} from "@/api/salesManagement/salesLedger.js"; import { getCurrentDate } from "@/utils/index.js"; const { proxy } = getCurrentInstance() const emit = defineEmits(['close']) const dialogFormVisible = ref(false); @@ -252,6 +248,14 @@ dialogFormVisible.value = false; emit('close') }; // è·åå½åæ¥æå¹¶æ ¼å¼å为 YYYY-MM-DD function getCurrentDate() { const today = new Date(); const year = today.getFullYear(); const month = String(today.getMonth() + 1).padStart(2, "0"); // æä»½ä»0å¼å§ const day = String(today.getDate()).padStart(2, "0"); return `${year}-${month}-${day}`; } defineExpose({ openDialog, }); src/views/equipmentManagement/measurementEquipment/components/formDia.vue
@@ -4,7 +4,6 @@ v-model="dialogFormVisible" title="计éå¨å ·" width="50%" draggable @close="closeDia" > <el-form @@ -65,9 +64,7 @@ v-model="form.userId" placeholder="è¯·éæ©" clearable filterable default-first-option :reserve-keyword="false" disabled > <el-option v-for="item in userList" @@ -127,7 +124,6 @@ import {afterSalesServiceAdd, afterSalesServiceUpdate} from "@/api/customerService/index.js"; import {getToken} from "@/utils/auth.js"; import {measuringInstrumentAdd, measuringInstrumentUpdate} from "@/api/equipmentManagement/measurementEquipment.js"; import { getCurrentDate } from "@/utils/index.js"; const { proxy } = getCurrentInstance() const emit = defineEmits(['close']) const dialogFormVisible = ref(false); @@ -238,6 +234,14 @@ dialogFormVisible.value = false; emit('close') }; // è·åå½åæ¥æå¹¶æ ¼å¼å为 YYYY-MM-DD function getCurrentDate() { const today = new Date(); const year = today.getFullYear(); const month = String(today.getMonth() + 1).padStart(2, "0"); // æä»½ä»0å¼å§ const day = String(today.getDate()).padStart(2, "0"); return `${year}-${month}-${day}`; } defineExpose({ openDialog, }); src/views/equipmentManagement/measurementEquipment/index.vue
@@ -148,13 +148,13 @@ openCalibrationDia("verifying", row); }, }, // { // name: "éä»¶", // type: "text", // clickFun: (row) => { // openFilesFormDia(row); // }, // }, { name: "éä»¶", type: "text", clickFun: (row) => { openFilesFormDia(row); }, }, ], }, ]); @@ -221,6 +221,12 @@ const handleDelete = () => { let ids = []; if (selectedRows.value.length > 0) { // æ£æ¥æ¯å¦æä»äººç»´æ¤çæ°æ® const unauthorizedData = selectedRows.value.filter(item => item.userId !== userStore.id); if (unauthorizedData.length > 0) { proxy.$modal.msgWarning("ä¸å¯å é¤ä»äººç»´æ¤çæ°æ®"); return; } ids = selectedRows.value.map((item) => item.id); } else { proxy.$modal.msgWarning("è¯·éæ©æ°æ®"); src/views/equipmentManagement/repair/Form/MaintainForm.vue
ÎļþÒÑɾ³ý src/views/equipmentManagement/repair/Form/RepairForm.vue
ÎļþÒÑɾ³ý src/views/equipmentManagement/repair/Modal/ApproveModal.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,142 @@ <template> <FormDialog v-model="visible" title="æ¥ä¿®å®¡æ¹" width="800px" @confirm="handleSubmit" @cancel="handleClose" @close="handleClose" > <el-descriptions :column="2" border> <el-descriptions-item label="设å¤åç§°"> {{ detail.deviceName || "-" }} </el-descriptions-item> <el-descriptions-item label="è§æ ¼åå·"> {{ detail.deviceModel || "-" }} </el-descriptions-item> <el-descriptions-item label="æ¥ä¿®æ¥æ"> {{ detail.repairTime || "-" }} </el-descriptions-item> <el-descriptions-item label="æ¥ä¿®äºº"> {{ detail.repairName || "-" }} </el-descriptions-item> <el-descriptions-item label="审æ¹äºº"> {{ detail.auditName || "-" }} </el-descriptions-item> <el-descriptions-item label="å½åç¶æ"> {{ statusText(detail.status) }} </el-descriptions-item> <el-descriptions-item label="æ éç°è±¡" :span="2"> {{ detail.remark || "-" }} </el-descriptions-item> </el-descriptions> <div style="margin-top: 16px"> <el-form ref="formRef" :model="form" :rules="rules" label-width="100px"> <el-form-item label="审æ¹ç»æ" prop="decision"> <el-radio-group v-model="form.decision"> <el-radio :value="0">éè¿</el-radio> <el-radio :value="3">ä¸éè¿</el-radio> </el-radio-group> </el-form-item> <el-form-item label="çç£äºº" prop="supervisoryName"> <el-input v-model="form.supervisoryName" placeholder="请è¾å ¥çç£äºº" clearable style="width: 100%" /> </el-form-item> </el-form> </div> </FormDialog> </template> <script setup> import { nextTick, ref } from "vue"; import { ElMessage, ElMessageBox } from "element-plus"; import FormDialog from "@/components/Dialog/FormDialog.vue"; import { editRepair, getRepairById } from "@/api/equipmentManagement/repair"; defineOptions({ name: "æ¥ä¿®å®¡æ¹å¼¹çª", }); const emits = defineEmits(["ok"]); const visible = ref(false); const loading = ref(false); const id = ref(); const detail = ref({}); const formRef = ref(); const form = ref({ decision: undefined, // 0 éè¿ 3 ä¸éè¿ supervisoryName: undefined, // çç£äºº }); const rules = { decision: [{ required: true, message: "è¯·éæ©å®¡æ¹ç»æ", trigger: "change" }], supervisoryName: [{ required: true, message: "è¯·éæ©çç£äºº", trigger: "change" }], }; const statusText = (status) => { const map = { 0: "å¾ ç»´ä¿®", 1: "å®ç»", 2: "å¾ å®¡æ ¸", 3: "å®¡æ ¸ä¸éè¿", }; return map[status] ?? "-"; }; const loadDetail = async (repairId) => { const { data } = await getRepairById(repairId); detail.value = data ?? {}; }; const open = async (repairId) => { id.value = repairId; visible.value = true; await nextTick(); await loadDetail(repairId); form.value.decision = undefined; form.value.supervisoryName = undefined; }; const handleClose = () => { visible.value = false; id.value = undefined; detail.value = {}; form.value.decision = undefined; form.value.supervisoryName = undefined; }; const updateStatus = async (status) => { loading.value = true; try { const { code } = await editRepair({ id: id.value, status, supervisoryName: form.value.supervisoryName }); if (code === 200) { ElMessage.success("å®¡æ¹æå"); emits("ok"); handleClose(); } } finally { loading.value = false; } }; const handleSubmit = async () => { if (detail.value?.status !== 2) { ElMessage.warning("ä» å¾ å®¡æ ¸ç¶æå¯å®¡æ¹"); return; } await formRef.value?.validate(async (valid) => { if (!valid) return; const isApprove = form.value.decision === 0; ElMessageBox.confirm(`确认审æ¹${isApprove ? "éè¿" : "ä¸éè¿"}ï¼`, "æç¤º", { confirmButtonText: "ç¡®å®", cancelButtonText: "åæ¶", type: "warning", }).then(() => updateStatus(form.value.decision)); }); }; defineExpose({ open }); </script> <style scoped></style> src/views/equipmentManagement/repair/Modal/MaintainModal.vue
@@ -1,53 +1,116 @@ <template> <el-dialog v-model="visible" :title="modalOptions.title" direction="ltr" draggable> <MaintainForm ref="maintainFormRef" /> <template #footer> <el-button type="primary" @click="sendForm" :loading="loading"> {{ modalOptions.confirmText }} </el-button> <el-button @click="closeModal">{{ modalOptions.cancelText }}</el-button> </template> </el-dialog> <FormDialog v-model="visible" :title="'设å¤ç»´ä¿®'" width="500px" @confirm="sendForm" @cancel="handleCancel" @close="handleClose" > <el-form :model="form" :rules="rules" label-width="110px" ref="formRef"> <el-form-item label="维修人" prop="maintenanceName"> <el-input v-model="form.maintenanceName" placeholder="请è¾å ¥ç»´ä¿®äºº" /> </el-form-item> <el-form-item label="ç»´ä¿®ç»æ" prop="maintenanceResult"> <el-input v-model="form.maintenanceResult" placeholder="请è¾å ¥ç»´ä¿®ç»æ" /> </el-form-item> <el-form-item label="æ¬æ¬¡ç»´ä¿®éé¢" prop="repairPrice"> <el-input-number v-model="form.repairPrice" :min="0" :precision="2" style="width: 100%" /> </el-form-item> <el-form-item label="ç»´ä¿®æ¥æ" prop="maintenanceTime"> <el-date-picker v-model="form.maintenanceTime" placeholder="è¯·éæ©ç»´ä¿®æ¥æ" format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss" type="datetime" clearable style="width: 100%" /> </el-form-item> </el-form> </FormDialog> </template> <script setup> import { useModal } from "@/hooks/useModal"; import MaintainForm from "../Form/MaintainForm.vue"; import FormDialog from "@/components/Dialog/FormDialog.vue"; import { addMaintain } from "@/api/equipmentManagement/repair"; import useFormData from "@/hooks/useFormData"; import useUserStore from "@/store/modules/user"; import dayjs from "dayjs"; import { ElMessage } from "element-plus"; defineOptions({ name: "ç»´ä¿®æ¨¡ææ¡", }); const maintainFormRef = ref(); const emits = defineEmits(["ok"]); const { id, visible, loading, openModal, modalOptions, handleConfirm, closeModal, } = useModal({ title: "设å¤ç»´ä¿®" }); // ä¿åæ¥ä¿®è®°å½çid const repairId = ref(); const visible = ref(false); const loading = ref(false); const formRef = ref(); const userStore = useUserStore(); const { form, resetForm } = useFormData({ maintenanceName: undefined, // ç»´ä¿®åç§° maintenanceResult: undefined, // ç»´ä¿®ç»æ maintenanceTime: undefined, // ç»´ä¿®æ¥æ repairPrice: undefined, // ç»´ä¿®éé¢ status: 0, }); const rules = { maintenanceName: [{ required: true, message: "请è¾å ¥ç»´ä¿®äºº", trigger: "blur" }], maintenanceResult: [{ required: true, message: "请è¾å ¥ç»´ä¿®ç»æ", trigger: "blur" }], repairPrice: [{ required: true, message: "请è¾å ¥æ¬æ¬¡ç»´ä¿®éé¢", trigger: "change" }], maintenanceTime: [{ required: true, message: "è¯·éæ©ç»´ä¿®æ¥æ", trigger: "change" }], }; const setForm = (data) => { form.maintenanceName = data.maintenanceName ?? userStore.nickName; form.maintenanceResult = data.maintenanceResult; form.maintenanceTime = data.maintenanceTime ? dayjs(data.maintenanceTime).format("YYYY-MM-DD HH:mm:ss") : dayjs().format("YYYY-MM-DD HH:mm:ss"); form.status = 2; // ç»´ä¿®ååºå®è¿å ¥å¾ å®¡æ ¸ï¼ä¸å¨çé¢å±ç¤ºï¼ }; const sendForm = async () => { loading.value = true; const form = await maintainFormRef.value.getForm(); const { code } = await addMaintain({ id: id.value, ...form }); if (code == 200) { emits("ok"); maintainFormRef.value.resetForm(); closeModal(); } loading.value = false; await formRef.value.validate(async (valid) => { if (!valid) return; loading.value = true; try { const { code } = await addMaintain({ id: repairId.value, ...form }); if (code == 200) { ElMessage.success("ç»´ä¿®æå"); emits("ok"); resetForm(); visible.value = false; } } finally { loading.value = false; } }) }; const handleCancel = () => { resetForm(); visible.value = false; }; const handleClose = () => { resetForm(); visible.value = false; }; const open = async (id, row) => { openModal(id); repairId.value = id; // ä¿åæ¥ä¿®è®°å½çid visible.value = true; await nextTick(); maintainFormRef.value.setForm(row); setForm(row); }; defineExpose({ src/views/equipmentManagement/repair/Modal/RepairModal.vue
@@ -1,24 +1,113 @@ <template> <el-dialog v-model="visible" :title="modalOptions.title" @close="close" draggable> <RepairForm ref="repairFormRef" :id="id" /> <template #footer> <el-button type="primary" @click="sendForm" :loading="loading"> {{ modalOptions.confirmText }} </el-button> <el-button @click="closeModal">{{ modalOptions.cancelText }}</el-button> </template> </el-dialog> <FormDialog v-model="visible" :title="id ? 'ç¼è¾è®¾å¤æ¥ä¿®' : 'æ°å¢è®¾å¤æ¥ä¿®'" width="800px" @confirm="sendForm" @cancel="handleCancel" @close="handleClose" > <el-form :model="form" :rules="rules" label-width="100px" ref="formRef"> <el-row> <el-col :span="12"> <el-form-item label="设å¤åç§°"> <el-select v-model="form.deviceLedgerId" @change="setDeviceModel" filterable> <el-option v-for="(item, index) in deviceOptions" :key="index" :label="item.deviceName" :value="item.id" ></el-option> </el-select> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="è§æ ¼åå·"> <el-input v-model="form.deviceModel" placeholder="请è¾å ¥è§æ ¼åå·" disabled /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="æ¥ä¿®æ¥æ"> <el-date-picker v-model="form.repairTime" placeholder="è¯·éæ©æ¥ä¿®æ¥æ" format="YYYY-MM-DD" value-format="YYYY-MM-DD" type="date" clearable style="width: 100%" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="æ¥ä¿®äºº"> <el-input v-model="form.repairName" placeholder="请è¾å ¥æ¥ä¿®äºº" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="审æ¹äºº" prop="auditName"> <el-select v-model="form.auditName" placeholder="è¯·éæ©å®¡æ¹äºº" filterable clearable style="width: 100%" > <el-option v-for="user in userOptions" :key="user.userId" :label="user.nickName" :value="user.nickName" /> </el-select> </el-form-item> </el-col> </el-row> <el-row v-if="id"> <el-col :span="12"> <el-form-item label="æ¥ä¿®ç¶æ"> <el-select v-model="form.status"> <el-option label="å¾ ç»´ä¿®" :value="0"></el-option> <el-option label="å®ç»" :value="1"></el-option> <el-option label="å¾ å®¡æ ¸" :value="2"></el-option> <el-option label="å®¡æ ¸ä¸éè¿" :value="3"></el-option> </el-select> </el-form-item> </el-col> </el-row> <el-row> <el-col :span="24"> <el-form-item label="æ éç°è±¡"> <el-input v-model="form.remark" :rows="2" type="textarea" placeholder="请è¾å ¥æ éç°è±¡" /> </el-form-item> </el-col> </el-row> </el-form> </FormDialog> </template> <script setup> import { useModal } from "@/hooks/useModal"; import RepairForm from "../Form/RepairForm.vue"; import FormDialog from "@/components/Dialog/FormDialog.vue"; import { addRepair, editRepair, getRepairById, } from "@/api/equipmentManagement/repair"; import { ElMessage } from "element-plus"; import dayjs from "dayjs"; import useFormData from "@/hooks/useFormData"; import { getDeviceLedger } from "@/api/equipmentManagement/ledger"; import useUserStore from "@/store/modules/user"; import { userListNoPageByTenantId } from "@/api/system/user"; defineOptions({ name: "è®¾å¤æ¥ä¿®å¼¹çª", @@ -26,63 +115,101 @@ const emits = defineEmits(["ok"]); const repairFormRef = ref(); const { id, visible, loading, openModal, modalOptions, handleConfirm, closeModal, } = useModal({ title: "è®¾å¤æ¥ä¿®" }); const id = ref(); const visible = ref(false); const loading = ref(false); const formRef = ref(); const userStore = useUserStore(); const deviceOptions = ref([]); const userOptions = ref([]); const loadDeviceName = async () => { const { data } = await getDeviceLedger(); deviceOptions.value = data; }; const loadUserOptions = async () => { const res = await userListNoPageByTenantId(); userOptions.value = res?.data ?? []; }; const { form, resetForm } = useFormData({ deviceLedgerId: undefined, // 设å¤Id deviceName: undefined, // 设å¤åç§° deviceModel: undefined, // è§æ ¼åå· repairTime: dayjs().format("YYYY-MM-DD"), // æ¥ä¿®æ¥æï¼é»è®¤å½å¤© repairName: userStore.nickName, // æ¥ä¿®äºº auditName: undefined, // 审æ¹äºº remark: undefined, // æ éç°è±¡ status: 0, // æ¥ä¿®ç¶æ }); const rules = { auditName: [ { required: true, message: "è¯·éæ©å®¡æ¹äºº", trigger: "change" }, ], }; const setDeviceModel = (deviceId) => { const option = deviceOptions.value.find((item) => item.id === deviceId); form.deviceModel = option.deviceModel; }; const setForm = (data) => { form.deviceLedgerId = data.deviceLedgerId; form.deviceName = data.deviceName; form.deviceModel = data.deviceModel; form.repairTime = data.repairTime; form.repairName = data.repairName; form.auditName = data.auditName; form.remark = data.remark; form.status = data.status; }; const sendForm = async () => { try { // å¼å§å è½½ await formRef.value?.validate(async (valid) => { if (!valid) return; loading.value = true; // æäº¤è¡¨åå¹¶è·åæ ¡éªç»æ const submitStatus = await repairFormRef.value.submitForm(); if (!submitStatus) { // å¦æè¡¨åéªè¯å¤±è´¥ï¼åæ¶å è½½ç¶æ loading.value = false; return; } // è·åè¡¨åæ°æ® const form = await repairFormRef.value.getForm(); // æ ¹æ®æ¯å¦æIDå³å®æ¯ç¼è¾è¿æ¯æ°å¢ const { code } = id.value try { const { code } = id.value ? await editRepair({ id: unref(id), ...form }) : await addRepair(form); if (code === 200) { ElMessage.success(`${id ? "ç¼è¾" : "æ°å¢"}æ¥ä¿®æå`); emits("ok"); if (code == 200) { ElMessage.success(`${id.value ? "ç¼è¾" : "æ°å¢"}æ¥ä¿®æå`); visible.value = false; emits("ok"); } } finally { loading.value = false; } } catch (error) { } finally { // æ 论æåè¿æ¯å¤±è´¥ï¼é½åæ¶å è½½ç¶æ loading.value = false; closeModal(); } }); }; const handleCancel = () => { resetForm(); visible.value = false; }; const handleClose = () => { resetForm(); visible.value = false; }; const openAdd = async () => { openModal(); id.value = undefined; visible.value = true; await nextTick(); await repairFormRef.value.loadDeviceName(); await Promise.all([loadDeviceName(), loadUserOptions()]); }; const openEdit = async (id) => { const { data } = await getRepairById(id); openModal(id); const openEdit = async (editId) => { const { data } = await getRepairById(editId); id.value = editId; visible.value = true; await nextTick(); await repairFormRef.value.loadDeviceName(); await repairFormRef.value.setForm(data); }; const close = () => { repairFormRef.value.resetForm(); closeModal(); await Promise.all([loadDeviceName(), loadUserOptions()]); setForm(data); }; defineExpose({ @@ -90,3 +217,5 @@ openEdit, }); </script> <style lang="scss" scoped></style> src/views/equipmentManagement/repair/index.vue
@@ -7,7 +7,6 @@ style="width: 240px" placeholder="请è¾å ¥è®¾å¤åç§°" clearable :prefix-icon="Search" @change="getTableData" /> </el-form-item> @@ -15,9 +14,8 @@ <el-input v-model="filters.deviceModel" style="width: 240px" placeholder="è¯·éæ©è§æ ¼åå·" placeholder="请è¾å ¥è§æ ¼åå·" clearable :prefix-icon="Search" @change="getTableData" /> </el-form-item> @@ -27,7 +25,6 @@ style="width: 240px" placeholder="请è¾å ¥æ éç°è±¡" clearable :prefix-icon="Search" @change="getTableData" /> </el-form-item> @@ -37,7 +34,6 @@ style="width: 240px" placeholder="请è¾å ¥ç»´ä¿®äºº" clearable :prefix-icon="Search" @change="getTableData" /> </el-form-item> @@ -68,15 +64,6 @@ <div class="actions"> <el-text class="mx-1" size="large">è®¾å¤æ¥ä¿®</el-text> <div> <el-button type="primary" icon="Plus" :disabled="multipleList.length !== 1 || multipleList[0]?.status !== 1" @click="addMaintain" > æ°å¢ç»´ä¿® </el-button> <el-button type="success" icon="Van" @click="addRepair"> æ°å¢æ¥ä¿® </el-button> @@ -86,79 +73,94 @@ <el-button type="danger" icon="Delete" :disabled="multipleList.length <= 0" @click="delRepairByIds(multipleList)" :disabled="multipleList.length <= 0 || hasFinishedStatus" @click="delRepairByIds(multipleList.map((item) => item.id))" > æ¹éå é¤ </el-button> </div> </div> <PIMTable rowKey="id" isSelection :column="columns" :tableData="dataList" :page="{ rowKey="id" isSelection :column="columns" :tableData="dataList" :page="{ current: pagination.currentPage, size: pagination.pageSize, total: pagination.total, }" @selection-change="handleSelectionChange" @pagination="changePage" @selection-change="handleSelectionChange" @pagination="changePage" > <template #statusRef="{ row }"> <el-tag v-if="row.status === 5" type="danger">维修失败</el-tag> <el-tag v-if="row.status === 4" type="danger">ç»´ä¿®æå</el-tag> <el-tag v-if="row.status === 3" type="danger">ç»´ä¿®ä¸</el-tag> <el-tag v-if="row.status === 2" type="danger">å®¡æ ¸å¤±è´¥</el-tag> <el-tag v-if="row.status === 1" type="success">å®¡æ ¸éè¿</el-tag> <el-tag v-if="row.status === 0" type="warning">å®¡æ ¸ä¸</el-tag> <el-tag v-if="row.status === 0" type="warning">å¾ ç»´ä¿®</el-tag> <el-tag v-else-if="row.status === 1" type="success">å®ç»</el-tag> <el-tag v-else-if="row.status === 2" type="info">å¾ å®¡æ ¸</el-tag> <el-tag v-else-if="row.status === 3" type="danger">å®¡æ ¸ä¸éè¿</el-tag> </template> <template #operation="{ row }"> <el-button type="primary" text icon="editPen" link :disabled="row.status === 1" @click="editRepair(row.id)" :disabled="row.status !== 0" > ç¼è¾ </el-button> <el-button type="danger" text icon="delete" @click="delRepairByIds(row)" type="warning" link :disabled="row.status !== 2" @click="openApprove(row.id)" > å®¡æ¹ </el-button> <el-button type="success" link :disabled="row.status !== 0" @click="addMaintain(row)" > ç»´ä¿® </el-button> <el-button type="danger" link :disabled="row.status === 1" @click="delRepairByIds(row.id)" > å é¤ </el-button> </template> </PIMTable> </div> <RepairModal ref="repairModalRef" @ok="getTableData" /> <MaintainModal ref="maintainModalRef" @ok="getTableData" /> <RepairModal ref="repairModalRef" @ok="getTableData"/> <MaintainModal ref="maintainModalRef" @ok="getTableData"/> <ApproveModal ref="approveModalRef" @ok="getTableData"/> </div> </template> <script setup> import { usePaginationApi } from "@/hooks/usePaginationApi"; import { getRepairPage, delRepair } from "@/api/equipmentManagement/repair"; import { onMounted, getCurrentInstance } from "vue"; import { onMounted, getCurrentInstance, computed } from "vue"; import {usePaginationApi} from "@/hooks/usePaginationApi"; import {getRepairPage, delRepair} from "@/api/equipmentManagement/repair"; import RepairModal from "./Modal/RepairModal.vue"; import { ElMessageBox, ElMessage } from "element-plus"; import {ElMessageBox, ElMessage} from "element-plus"; import dayjs from "dayjs"; import MaintainModal from "./Modal/MaintainModal.vue"; import ApproveModal from "./Modal/ApproveModal.vue"; defineOptions({ name: "è®¾å¤æ¥ä¿®", }); const { proxy } = getCurrentInstance(); const {proxy} = getCurrentInstance(); // æ¨¡ææ¡å®ä¾ const repairModalRef = ref(); const maintainModalRef = ref(); const approveModalRef = ref(); // è¡¨æ ¼å¤éæ¡éä¸é¡¹ const multipleList = ref([]); @@ -173,85 +175,87 @@ resetFilters, onCurrentChange, } = usePaginationApi( getRepairPage, { deviceName: undefined, deviceModel: undefined, remark: undefined, maintenanceName: undefined, repairTimeStr: undefined, maintenanceTimeStr: undefined, }, [ getRepairPage, { label: "设å¤åç§°", align: "center", prop: "deviceName", deviceName: undefined, deviceModel: undefined, remark: undefined, maintenanceName: undefined, repairTimeStr: undefined, maintenanceTimeStr: undefined, }, { label: "è§æ ¼åå·", align: "center", prop: "deviceModel", }, { label: "æ¥ä¿®æ¥æ", align: "center", prop: "repairTime", formatData: (cell) => dayjs(cell).format("YYYY-MM-DD"), }, { label: "æ¥ä¿®äºº", align: "center", prop: "repairName", }, { label: "æ éç°è±¡", align: "center", prop: "remark", }, { label: "维修人", align: "center", prop: "maintenanceName", }, { label: "ç»´ä¿®ç»æ", align: "center", prop: "maintenanceResult", }, { label: "ç»´ä¿®æ¥æ", align: "center", prop: "maintenanceTime", formatData: (cell) => (cell ? dayjs(cell).format("YYYY-MM-DD") : ""), }, { label: "ç¶æ", align: "center", prop: "status", dataType: "slot", slot: "statusRef", }, { fixed: "right", label: "æä½", dataType: "slot", slot: "operation", align: "center", width: "200px", }, ] [ { label: "设å¤åç§°", align: "center", prop: "deviceName", }, { label: "è§æ ¼åå·", align: "center", prop: "deviceModel", }, { label: "æ¥ä¿®æ¥æ", align: "center", prop: "repairTime", formatData: (cell) => dayjs(cell).format("YYYY-MM-DD"), }, { label: "æ¥ä¿®äºº", align: "center", prop: "repairName", }, { label: "æ éç°è±¡", align: "center", prop: "remark", }, { label: "维修人", align: "center", prop: "maintenanceName", }, { label: "ç»´ä¿®ç»æ", align: "center", prop: "maintenanceResult", }, { label: "ç»´ä¿®æ¥æ", align: "center", prop: "maintenanceTime", formatData: (cell) => (cell ? dayjs(cell).format("YYYY-MM-DD") : ""), }, { prop: "auditName", label: "å®¡æ ¸äºº", width: 120 }, { prop: "supervisoryName", label: "çç£äºº", width: 120 }, { label: "ç¶æ", align: "center", prop: "status", dataType: "slot", slot: "statusRef", }, { fixed: "right", label: "æä½", dataType: "slot", slot: "operation", align: "center", width: "300px", }, ] ); // type === 1 ç»´ä¿® 2æ¥ä¿®é´ const handleDateChange = (value,type) => { const handleDateChange = (value, type) => { filters.maintenanceTimeStr = null filters.c = null if(type === 1){ if (type === 1) { if (value) { filters.maintenanceTimeStr = dayjs(value).format("YYYY-MM-DD"); } }else{ } else { if (value) { filters.repairTimeStr = dayjs(value).format("YYYY-MM-DD"); } @@ -264,6 +268,11 @@ multipleList.value = selectionList; }; // æ£æ¥éä¸çè®°å½ä¸æ¯å¦æå®ç»ç¶æç const hasFinishedStatus = computed(() => { return multipleList.value.some(item => item.status === 1) }) // æ°å¢æ¥ä¿® const addRepair = () => { repairModalRef.value.openAdd(); @@ -275,36 +284,33 @@ }; // æ°å¢ç»´ä¿® const addMaintain = () => { const row = multipleList.value[0]; const addMaintain = (row) => { maintainModalRef.value.open(row.id, row); }; const changePage = ({ page, limit }) => { pagination.currentPage = page; pagination.pageSize = limit; onCurrentChange(page); // å®¡æ¹ const openApprove = (id) => { approveModalRef.value.open(id); }; const changePage = ({page, limit}) => { pagination.currentPage = page; pagination.pageSize = limit; onCurrentChange(page); }; // åè¡å é¤ const delRepairByIds = async (ids) => { let isDel = false if(Array.isArray(ids)){ ids.forEach((item)=>{ if(item.status !== 0){ isDel = true } }) }else{ if(ids.status !== 0){ isDel = true } } // æ£æ¥æ¯å¦æå®ç»ç¶æçè®°å½ const idsArray = Array.isArray(ids) ? ids : [ids]; const hasFinished = idsArray.some(id => { const record = dataList.value.find(item => item.id === id); return record && record.status === 1; }); if(isDel){ ElMessage.warning("åªè½å é¤å®¡æ ¸ä¸çæ¥ä¿®æ°æ®"); return if (hasFinished) { ElMessage.warning('ä¸è½å é¤ç¶æä¸ºå®ç»çè®°å½'); return; } ElMessageBox.confirm("确认å 餿¥ä¿®æ°æ®, æ¤æä½ä¸å¯é?", "è¦å", { @@ -312,17 +318,10 @@ cancelButtonText: "åæ¶", type: "warning", }).then(async () => { let idsList = "" if(Array.isArray(ids)){ idsList = multipleList.value.map((item) => item.id); console.log(idsList) }else{ idsList = ids.id } const { code } = await delRepair(idsList); const {code} = await delRepair(ids); if (code === 200) { ElMessage.success("å 餿å"); await getTableData(); getTableData(); } }); }; @@ -334,12 +333,12 @@ cancelButtonText: "åæ¶", type: "warning", }) .then(() => { proxy.download("/device/repair/export", {}, "è®¾å¤æ¥ä¿®.xlsx"); }) .catch(() => { ElMessage.info("已忶"); }); .then(() => { proxy.download("/device/repair/export", {}, "è®¾å¤æ¥ä¿®.xlsx"); }) .catch(() => { ElMessage.info("已忶"); }); }; onMounted(() => { @@ -351,6 +350,7 @@ .table_list { margin-top: unset; } .actions { display: flex; justify-content: space-between; src/views/equipmentManagement/spareParts/index.vue
@@ -19,47 +19,21 @@ <el-button type="primary" @click="addCategory" >æ°å¢</el-button> </div> </div> <PIMTable rowKey="id" :column="columns" :tableData="renderTableData" :tableLoading="loading" :page="pagination" :isShowPagination="true" @pagination="handleSizeChange" > <template #status="{ row }"> <el-tag type="success" size="small">{{ row.status }}</el-tag> </template> </PIMTable> <div class="table_list"> <el-table v-loading="loading" :data="renderTableData" style="width: 100%; margin-top: 10px;" border row-key="id" > <el-table-column prop="deviceNameStr" label="设å¤åç§°" width="300"></el-table-column> <el-table-column prop="name" label="å¤ä»¶åç§°" width="200"></el-table-column> <el-table-column prop="sparePartsNo" label="å¤ä»¶ç¼å·" width="200"></el-table-column> <el-table-column prop="status" label="ç¶æ" width="100"> <template #default="{ row }"> <el-tag type="success" size="small">{{ row.status }}</el-tag> </template> </el-table-column> <el-table-column prop="price" label="ä»·æ ¼" width="140"></el-table-column> <el-table-column prop="description" label="æè¿°" width="150"></el-table-column> <el-table-column label="æä½" width="150" fixed="right" align="center"> <template #default="{ row }"> <el-button link type="primary" @click="() => editCategory(row)" :disabled="loading" > ç¼è¾ </el-button> <el-button link @click="() => deleteCategory(row.id)" style="color: #f56c6c;" :disabled="loading" > å é¤ </el-button> </template> </el-table-column> </el-table> </div> <el-dialog title="å类管ç" v-model="dialogVisible" width="60%"> <el-form :model="form" :rules="rules" ref="formRef" label-width="100px"> <el-form-item label="设å¤" prop="deviceLedgerIds"> @@ -85,6 +59,9 @@ </el-form-item> <el-form-item label="å¤ä»¶ç¼å·" prop="sparePartsNo"> <el-input v-model="form.sparePartsNo"></el-input> </el-form-item> <el-form-item label="æ°é" prop="quantity"> <el-input type="number" v-model="form.quantity"></el-input> </el-form-item> <el-form-item label="ç¶æ" prop="status"> <el-select v-model="form.status" placeholder="è¯·éæ©ç¶æ"> @@ -143,6 +120,66 @@ const queryParams = reactive({ name: '' }); // å页忰 const pagination = reactive({ current: 1, size: 10, total: 0 }); const columns = ref([ { label: "设å¤åç§°", prop: "deviceNameStr", }, { label: "å¤ä»¶åç§°", prop: "name", }, { label: "å¤ä»¶ç¼å·", prop: "sparePartsNo", }, { label: "ç¶æ", prop: "status", slot: "status", dataType: "slot", }, { label: "ä»·æ ¼", prop: "price", }, { label: "æ°é", prop: "quantity", }, { label: "æè¿°", prop: "description", }, { label: "æä½", prop: "operation", width: 150, fixed: 'right', align: "center", dataType: "action", operation: [ { name: "ç¼è¾", clickFun: (row) => { editCategory(row) }, }, { name: "å é¤", clickFun: (row) => { deleteCategory(row.id) }, }, ], }, ]); // è¡¨åæ°æ® const form = reactive({ id:'', @@ -161,6 +198,9 @@ ], sparePartsNo: [ { required: true, message: '请è¾å ¥å¤ä»¶ç¼å·', trigger: 'blur' } ], quantity:[ { required: true, message: '请è¾å ¥æ°é', trigger: 'blur' } ], status: [ { required: true, message: 'è¯·éæ©ç¶æ', trigger: 'change' } @@ -208,7 +248,10 @@ const fetchListData = async () => { loading.value = true; try { const params = {}; const params = { current: pagination.current, size: pagination.size }; if (queryParams.name) { params.name = queryParams.name; } @@ -216,6 +259,7 @@ if (res.code === 200) { renderTableData.value = res.data.records || []; categories.value = res.data.records || []; pagination.total = res.data.total || 0; } } catch (error) { loading.value = false; @@ -226,12 +270,27 @@ // æ¥è¯¢ const handleQuery = () => { pagination.current = 1; fetchListData(); } // éç½®æ¥è¯¢ const resetQuery = () => { queryParams.name = ''; pagination.current = 1; fetchListData(); } // å页大尿¹å const handleSizeChange = (size) => { pagination.size = size; pagination.current = 1; fetchListData(); } // å½å页æ¹å const handleCurrentChange = (current) => { pagination.current = current; fetchListData(); } @@ -254,6 +313,7 @@ form.status = ''; form.description = ''; form.deviceLedgerIds = []; form.quantity = undefined; form.price = null; operationType.value = 'add' dialogVisible.value = true; @@ -366,6 +426,13 @@ margin-top: unset; } .pagination-container { margin-top: 20px; display: flex; justify-content: flex-end; padding: 16px 0; } .el-table__header-wrapper th { background-color: #f5f7fa; font-weight: 600; src/views/equipmentManagement/upkeep/Form/ApproveModal.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,144 @@ <template> <FormDialog v-model="visible" title="宿¶ä»»å¡å®¡æ¹" width="800px" @confirm="handleSubmit" @cancel="handleClose" @close="handleClose" > <el-descriptions :column="2" border> <el-descriptions-item label="ä»»å¡åç§°"> {{ detail.taskName || "-" }} </el-descriptions-item> <el-descriptions-item label="è§æ ¼åå·"> {{ detail.deviceModel || "-" }} </el-descriptions-item> <el-descriptions-item label="颿¬¡"> {{ frequencyText(detail.frequencyType) }} </el-descriptions-item> <el-descriptions-item label="å¼å§æ¥æä¸æ¶é´"> {{ detail.frequencyDetail || "-" }} </el-descriptions-item> <el-descriptions-item label="ç»è®°äºº"> {{ detail.registrant || "-" }} </el-descriptions-item> <el-descriptions-item label="ç»è®°æ¥æ"> {{ detail.registrationDate || "-" }} </el-descriptions-item> <el-descriptions-item label="å½åç¶æ"> {{ statusText(detail.status) }} </el-descriptions-item> <el-descriptions-item label="夿³¨" :span="2"> {{ detail.remarks || "-" }} </el-descriptions-item> </el-descriptions> <div style="margin-top: 16px"> <el-form ref="formRef" :model="form" :rules="rules" label-width="100px"> <el-form-item label="审æ¹ç»æ" prop="decision"> <el-radio-group v-model="form.decision"> <el-radio label="å®¡æ ¸éè¿">å®¡æ ¸éè¿</el-radio> <el-radio label="å®¡æ ¸ä¸éè¿">å®¡æ ¸ä¸éè¿</el-radio> </el-radio-group> </el-form-item> <el-form-item label="çç£äºº" prop="supervisoryName"> <el-input v-model="form.supervisoryName" placeholder="请è¾å ¥çç£äºº" clearable style="width: 100%" /> </el-form-item> </el-form> </div> </FormDialog> </template> <script setup> import { nextTick, ref } from "vue"; import { ElMessage, ElMessageBox } from "element-plus"; import FormDialog from "@/components/Dialog/FormDialog.vue"; import { deviceMaintenanceTaskEdit } from "@/api/equipmentManagement/upkeep"; defineOptions({ name: "宿¶ä»»å¡å®¡æ¹å¼¹çª", }); const emits = defineEmits(["ok"]); const visible = ref(false); const loading = ref(false); const detail = ref({}); const formRef = ref(); const form = ref({ decision: undefined, // å®¡æ ¸éè¿ / å®¡æ ¸ä¸éè¿ supervisoryName: undefined, // çç£äºº }); const rules = { decision: [{ required: true, message: "è¯·éæ©å®¡æ¹ç»æ", trigger: "change" }], supervisoryName: [{ required: true, message: "è¯·éæ©çç£äºº", trigger: "change" }], }; const statusText = (status) => status || "-"; const frequencyText = (type) => { const map = { DAILY: "æ¯æ¥", WEEKLY: "æ¯å¨", MONTHLY: "æ¯æ", QUARTERLY: "å£åº¦", }; return map[type] ?? "-"; }; const open = async (row) => { detail.value = { ...(row || {}) }; visible.value = true; await nextTick(); form.value.decision = undefined; form.value.supervisoryName = undefined; }; const handleClose = () => { visible.value = false; detail.value = {}; form.value.decision = undefined; form.value.supervisoryName = undefined; }; const updateStatus = async (status) => { loading.value = true; try { const payload = { ...(detail.value || {}), status, supervisoryName: form.value.supervisoryName }; const { code } = await deviceMaintenanceTaskEdit(payload); if (code === 200) { ElMessage.success("å®¡æ¹æå"); emits("ok"); handleClose(); } } finally { loading.value = false; } }; const handleSubmit = async () => { if (detail.value?.status === "å®¡æ ¸éè¿") { ElMessage.warning("å®¡æ ¸éè¿åä¸å¯å次审æ¹"); return; } await formRef.value?.validate(async (valid) => { if (!valid) return; const isApprove = form.value.decision === "å®¡æ ¸éè¿"; ElMessageBox.confirm( `确认审æ¹${isApprove ? "éè¿" : "ä¸éè¿"}ï¼`, "æç¤º", { confirmButtonText: "ç¡®å®", cancelButtonText: "åæ¶", type: "warning", } ).then(() => updateStatus(form.value.decision)); }); }; defineExpose({ open }); </script> <style scoped></style> src/views/equipmentManagement/upkeep/Form/MaintenanceForm.vue
ÎļþÒÑɾ³ý src/views/equipmentManagement/upkeep/Form/MaintenanceModal.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,146 @@ <template> <FormDialog v-model="visible" :title="'设å¤ä¿å »'" width="500px" @confirm="sendForm" @cancel="handleCancel" @close="handleClose" > <el-form :model="form" :rules="rules" label-width="120px" ref="formRef"> <el-form-item label="å®é ä¿å »äºº" prop="maintenanceActuallyName"> <el-input v-model="form.maintenanceActuallyName" placeholder="请è¾å ¥å®é ä¿å »äºº" ></el-input> </el-form-item> <el-form-item label="å®é ä¿å »æ¥æ" prop="maintenanceActuallyTime"> <el-date-picker v-model="form.maintenanceActuallyTime" placeholder="è¯·éæ©å®é ä¿å »æ¥æ" format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss" type="datetime" clearable style="width: 100%" /> </el-form-item> <el-form-item label="ä¿å »ç¶æ" prop="status"> <el-select v-model="form.status"> <el-option label="å¾ ä¿å »" :value="0"></el-option> <el-option label="å®ç»" :value="1"></el-option> <el-option label="失败" :value="2"></el-option> </el-select> </el-form-item> <el-form-item label="ä¿å »ç»æ" prop="maintenanceResult"> <el-input v-model="form.maintenanceResult" placeholder="请è¾å ¥ä¿å »ç»æ" type="text" /> </el-form-item> <el-form-item label="æ¬æ¬¡ä¿å »éé¢" prop="maintenancePrice"> <el-input-number v-model="form.maintenancePrice" :min="0" :precision="2" style="width: 100%" /> </el-form-item> </el-form> </FormDialog> </template> <script setup> import FormDialog from "@/components/Dialog/FormDialog.vue"; import { addMaintenance } from "@/api/equipmentManagement/upkeep"; import useFormData from "@/hooks/useFormData"; import dayjs from "dayjs"; import useUserStore from "@/store/modules/user"; import { ElMessage } from "element-plus"; defineOptions({ name: "ä¿å »æ¨¡ææ¡", }); const emits = defineEmits(["ok"]); // ä¿å计åä¿å »è®°å½çid const planId = ref(); const visible = ref(false); const loading = ref(false); const formRef = ref(); const userStore = useUserStore(); const { form, resetForm } = useFormData({ maintenanceActuallyName: undefined, // å®é ä¿å »äºº maintenanceActuallyTime: undefined, // å®é ä¿å »æ¥æ maintenanceResult: undefined, // ä¿å »ç»æ maintenancePrice: undefined, // ä¿å »éé¢ status: 0, // ä¿å »ç¶æ }); const rules = { maintenanceActuallyName: [ { required: true, message: "请è¾å ¥å®é ä¿å »äºº", trigger: "blur" }, ], maintenanceActuallyTime: [ { required: true, message: "è¯·éæ©å®é ä¿å »æ¥æ", trigger: "change" }, ], maintenanceResult: [ { required: true, message: "请è¾å ¥ä¿å »ç»æ", trigger: "blur" }, ], maintenancePrice: [ { required: true, message: "请è¾å ¥æ¬æ¬¡ä¿å »éé¢", trigger: "change" }, ], }; const setForm = (data) => { form.maintenanceActuallyName = data.maintenanceActuallyName ?? userStore.nickName; form.maintenanceActuallyTime = data.maintenanceActuallyTime ? dayjs(data.maintenanceActuallyTime).format("YYYY-MM-DD HH:mm:ss") : dayjs().format("YYYY-MM-DD HH:mm:ss"); form.maintenanceResult = data.maintenanceResult; form.status = 1; // é»è®¤ç¶æä¸ºå®ç» }; /** * @desc ä¿åä¿å » */ const sendForm = async () => { await formRef.value?.validate(async (valid) => { if (!valid) return; loading.value = true; try { const { code } = await addMaintenance({ id: planId.value, ...form }); if (code == 200) { ElMessage.success("ä¿å »æå"); emits("ok"); resetForm(); visible.value = false; } } finally { loading.value = false; } }); }; const handleCancel = () => { resetForm(); visible.value = false; }; const handleClose = () => { resetForm(); visible.value = false; }; const open = async (id, row) => { planId.value = id; // ä¿å计åä¿å »è®°å½çid visible.value = true; await nextTick(); setForm(row); }; defineExpose({ open, }); </script> <style lang="scss" scoped></style> src/views/equipmentManagement/upkeep/Form/PlanForm.vue
ÎļþÒÑɾ³ý src/views/equipmentManagement/upkeep/Form/PlanModal.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,188 @@ <template> <FormDialog v-model="visible" :title="id ? 'ç¼è¾è®¾å¤ä¿å »è®¡å' : 'æ°å¢è®¾å¤ä¿å »è®¡å'" width="500px" @confirm="sendForm" @cancel="handleCancel" @close="handleClose" > <el-form :model="form" label-width="100px"> <el-form-item label="设å¤åç§°"> <el-select v-model="form.deviceLedgerId" @change="setDeviceModel" placeholder="è¯·éæ©è®¾å¤" filterable default-first-option :reserve-keyword="false" > <el-option v-for="(item, index) in deviceOptions" :key="index" :label="item.deviceName" :value="item.id" ></el-option> </el-select> </el-form-item> <el-form-item label="è§æ ¼åå·"> <el-input v-model="form.deviceModel" placeholder="请è¾å ¥è§æ ¼åå·" disabled /> </el-form-item> <el-form-item label="å½å ¥äºº"> <el-select v-model="form.createUser" placeholder="è¯·éæ©" filterable default-first-option :reserve-keyword="false" clearable > <el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.userId" /> </el-select> </el-form-item> <el-form-item v-if="id" label="ä¿ä¿®ç¶æ"> <el-select v-model="form.status"> <el-option label="å¾ ä¿ä¿®" :value="0"></el-option> <el-option label="å®ç»" :value="1"></el-option> <el-option label="失败" :value="2"></el-option> </el-select> </el-form-item> <el-form-item label="计åä¿å »æ¥æ"> <el-date-picker style="width: 100%" v-model="form.maintenancePlanTime" format="YYYY-MM-DD" value-format="YYYY-MM-DD HH:mm:ss" type="date" placeholder="è¯·éæ©è®¡åä¿å »æ¥ææ¥æ" clearable /> </el-form-item> </el-form> </FormDialog> </template> <script setup> import FormDialog from "@/components/Dialog/FormDialog.vue"; import { addUpkeep, editUpkeep, getUpkeepById, } from "@/api/equipmentManagement/upkeep"; import { ElMessage } from "element-plus"; import useFormData from "@/hooks/useFormData"; import { getDeviceLedger } from "@/api/equipmentManagement/ledger"; import { onMounted } from "vue"; import dayjs from "dayjs"; import { userListNoPage } from "@/api/system/user.js"; defineOptions({ name: "设å¤ä¿å »æ°å¢è®¡å", }); const emits = defineEmits(["ok"]); const id = ref(); const visible = ref(false); const loading = ref(false); const deviceOptions = ref([]); const loadDeviceName = async () => { const { data } = await getDeviceLedger(); deviceOptions.value = data; }; const { form, resetForm } = useFormData({ deviceLedgerId: undefined, // 设å¤Id deviceName: undefined, // 设å¤åç§° deviceModel: undefined, // è§æ ¼åå· maintenancePlanTime: undefined, // 计åä¿å »æ¥æ createUser: undefined, // å½å ¥äºº status: 0, //ä¿ä¿®ç¶æ }); const setDeviceModel = (deviceId) => { const option = deviceOptions.value.find((item) => item.id === deviceId); form.deviceModel = option.deviceModel; }; /** * @desc 设置表åå 容 * @param data 设å¤ä¿¡æ¯ */ const setForm = (data) => { form.deviceLedgerId = data.deviceLedgerId; form.deviceName = data.deviceName; form.deviceModel = data.deviceModel; form.createUser = Number(data.createUser); form.status = data.status; form.maintenancePlanTime = dayjs(data.maintenancePlanTime).format( "YYYY-MM-DD HH:mm:ss" ); }; // ç¨æ·å表 const userList = ref([]); onMounted(() => { loadDeviceName(); userListNoPage().then((res) => { userList.value = res.data; }); }); const openEdit = async (editId) => { const { data } = await getUpkeepById(editId); id.value = editId; visible.value = true; await nextTick(); setForm(data); }; const sendForm = async () => { loading.value = true; try { const { code } = id.value ? await editUpkeep({ id: unref(id), ...form }) : await addUpkeep(form); if (code == 200) { ElMessage.success(`${id.value ? "ç¼è¾" : "æ°å¢"}计åæå`); visible.value = false; emits("ok"); } } finally { loading.value = false; } }; const handleCancel = () => { resetForm(); visible.value = false; }; const handleClose = () => { resetForm(); visible.value = false; }; const openModal = () => { id.value = undefined; visible.value = true; }; defineExpose({ openModal, openEdit, }); </script> <style lang="scss" scoped></style> src/views/equipmentManagement/upkeep/Form/formDia.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,337 @@ <template> <FormDialog v-model="dialogVisitable" :title="operationType === 'add' ? 'æ°å¢ä¿å »ä»»å¡' : 'ç¼è¾ä¿å »ä»»å¡'" width="800px" :operation-type="operationType" @confirm="submitForm" @cancel="cancel" @close="cancel" > <el-form ref="formRef" :model="form" :rules="rules" label-width="120px"> <el-row> <el-col :span="12"> <el-form-item label="设å¤åç§°" prop="taskIds"> <el-select v-model="form.taskIds" @change="setDeviceModel" multiple filterable> <el-option v-for="(item, index) in deviceOptions" :key="index" :label="item.deviceName" :value="item.id" ></el-option> </el-select> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="è§æ ¼åå·"> <el-input v-model="form.deviceModel" placeholder="请è¾å ¥è§æ ¼åå·" disabled /> </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" filterable default-first-option :reserve-keyword="false" placeholder="è¯·éæ©" 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="registrationDate"> <el-date-picker v-model="form.registrationDate" type="date" placeholder="éæ©ç»è®°æ¥æ" format="YYYY-MM-DD" value-format="YYYY-MM-DD" style="width: 100%" /> </el-form-item> </el-col> </el-row> <el-row> <el-col :span="12"> <el-form-item label="审æ¹äºº" prop="auditName"> <el-select v-model="form.auditName" filterable placeholder="è¯·éæ©å®¡æ¹äºº" clearable > <el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.nickName" /> </el-select> </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-row> <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-form> </FormDialog> </template> <script setup> import FormDialog from "@/components/Dialog/FormDialog.vue"; import { reactive, ref, getCurrentInstance, toRefs } from "vue"; import {userListNoPageByTenantId} from "@/api/system/user.js"; import { getDeviceLedger } from "@/api/equipmentManagement/ledger"; import { deviceMaintenanceTaskAdd, deviceMaintenanceTaskEdit } from "@/api/equipmentManagement/upkeep"; import { getCurrentDate } from "@/utils/index.js"; import useUserStore from "@/store/modules/user.js"; const { proxy } = getCurrentInstance() const emit = defineEmits() const dialogVisitable = ref(false); const operationType = ref('add'); const deviceOptions = ref([]); const userStore = useUserStore(); const data = reactive({ form: { taskIds: [], taskName: undefined, // å½å ¥äººï¼åéä¸ä¸ªç¨æ· id inspector: undefined, auditName: undefined, remarks: '', frequencyType: '', frequencyDetail: '', week: '', time: '', deviceModel: undefined, // è§æ ¼åå· registrationDate: '' }, rules: { taskIds: [{ required: true, message: "è¯·éæ©è®¾å¤", trigger: "change" },], inspector: [{ required: true, message: "è¯·éæ©å½å ¥äºº", trigger: "blur" },], registrationDate: [{ required: true, message: "è¯·éæ©ç»è®°æ¶é´", trigger: "change" }], auditName: [{ required: true, message: "è¯·éæ©å®¡æ¹äºº", trigger: "change" }], } }) const { form, rules } = toRefs(data) const userList = ref([]) const loadDeviceName = async () => { const { data } = await getDeviceLedger(); deviceOptions.value = data; }; // éæ©è®¾å¤æ¶ï¼å填设å¤åç§°(taskName)åè§æ ¼åå·(deviceModel) const setDeviceModel = (ids) => { if (!ids || ids.length === 0) { form.value.taskIds = [] form.value.taskName = undefined form.value.deviceModel = undefined return } const selectedDevices = deviceOptions.value.filter((item) => ids.includes(item.id)) if (selectedDevices.length > 0) { form.value.taskIds = ids form.value.taskName = selectedDevices.map(d => d.deviceName).join(',') form.value.deviceModel = selectedDevices.map(d => d.deviceModel || '-').join(',') } } // æå¼å¼¹æ¡ const openDialog = async (type, row) => { dialogVisitable.value = true operationType.value = type // é置表å resetForm(); // å è½½ç¨æ·å表 userListNoPageByTenantId().then((res) => { userList.value = res.data; }); // å 载设å¤å表 await loadDeviceName(); if (type === 'edit' && row) { form.value = { ...row } // ç¼è¾æ¶ç¨æ¥å£è¿åç registrantId åæ¾å½å ¥äºº if (row.registrantId) { form.value.inspector = row.registrantId } // 妿æè®¾å¤IDæ°ç»ï¼è½¬æ¢ä¸ºæ°ç»å¹¶è®¾ç½®è®¾å¤ä¿¡æ¯ if (row.taskIds) { form.value.taskIds = row.taskIds.split(',').map(id => parseInt(id.trim())) setDeviceModel(form.value.taskIds) } } else if (type === 'add') { // æ°å¢æ¶è®¾ç½®ç»è®°æ¥æä¸ºå½å¤© form.value.registrationDate = getCurrentDate(); // æ°å¢æ¶è®¾ç½®å½å ¥äººä¸ºå½åç»å½è´¦æ· form.value.inspector = userStore.id; } } // å ³éå¯¹è¯æ¡ const cancel = () => { resetForm() dialogVisitable.value = false emit('closeDia') } // é置表å彿° const resetForm = () => { if (proxy.$refs.formRef) { proxy.$refs.formRef.resetFields() } // éç½®è¡¨åæ°æ®ç¡®ä¿è®¾å¤ä¿¡æ¯æ£ç¡®éç½® form.value = { taskIds: [], taskName: undefined, inspector: undefined, auditName: undefined, remarks: '', frequencyType: '', frequencyDetail: '', week: '', time: '', deviceModel: undefined, registrationDate: '' } } // æäº¤è¡¨å const submitForm = () => { proxy.$refs["formRef"].validate(async valid => { if (valid) { try { const payload = { ...form.value } // ä¸åååç«¯ä¼ ä¿å »äººå段ï¼ä» ä½¿ç¨æ¥å£è¦æ±ç registrant / registrantId // æ ¹æ®éæ©ç"å½å ¥äºº"设置 registrant / registrantId if (payload.inspector) { const selectedUser = userList.value.find( (u) => String(u.userId) === String(payload.inspector) ) if (selectedUser) { payload.registrantId = selectedUser.userId payload.registrant = selectedUser.nickName } } delete payload.inspector delete payload.inspectorIds // å¤ç taskIds å taskName if (payload.taskIds && Array.isArray(payload.taskIds)) { payload.taskIds = payload.taskIds.join(',') } if (payload.frequencyType === 'WEEKLY') { let frequencyDetail = '' frequencyDetail = payload.week + ',' + payload.time payload.frequencyDetail = frequencyDetail } // å½å ¥æ¥æï¼ç´æ¥ä½¿ç¨è¡¨åéç registrationDate åæ®µ // ä¸äºé»è®¤ç¶æå段 if (payload.status === undefined || payload.status === null || payload.status === '') { payload.status = 'å¾ å®¡æ ¸' // é»è®¤ç¶æï¼å¾ å®¡æ ¸ } payload.active = true payload.deleted = 0 if (operationType.value === 'edit') { await deviceMaintenanceTaskEdit(payload) } else { await deviceMaintenanceTaskAdd(payload) } cancel() proxy.$modal.msgSuccess('æäº¤æå') } catch (error) { proxy.$modal.msgError('æäº¤å¤±è´¥ï¼è¯·éè¯') } } }) } defineExpose({ openDialog }) </script> <style scoped> </style> src/views/equipmentManagement/upkeep/Modal/MaintenanceModal.vue
ÎļþÒÑɾ³ý src/views/equipmentManagement/upkeep/Modal/PlanModal.vue
ÎļþÒÑɾ³ý src/views/equipmentManagement/upkeep/Modal/formDia.vue
ÎļþÒÑɾ³ý src/views/equipmentManagement/upkeep/index.vue
@@ -17,8 +17,9 @@ </el-form-item> <el-form-item label="ä»»å¡ç¶æ"> <el-select v-model="scheduledFilters.status" placeholder="è¯·éæ©ä»»å¡ç¶æ" clearable style="width: 200px"> <el-option label="å¯ç¨" value="1" /> <el-option label="åç¨" value="0" /> <el-option label="å¾ å®¡æ ¸" value="å¾ å®¡æ ¸" /> <el-option label="å®¡æ ¸éè¿" value="å®¡æ ¸éè¿" /> <el-option label="å®¡æ ¸ä¸éè¿" value="å®¡æ ¸ä¸éè¿" /> </el-select> </el-form-item> <el-form-item> @@ -58,22 +59,30 @@ @pagination="changeScheduledPage" > <template #statusRef="{ row }"> <el-tag v-if="row.status === 1" type="success">å¯ç¨</el-tag> <el-tag v-if="row.status === 0" type="danger">åç¨</el-tag> <el-tag v-if="row.status === 'å¾ å®¡æ ¸'" type="warning">å¾ å®¡æ ¸</el-tag> <el-tag v-else-if="row.status === 'å®¡æ ¸éè¿'" type="success">å®¡æ ¸éè¿</el-tag> <el-tag v-else-if="row.status === 'å®¡æ ¸ä¸éè¿'" type="danger">å®¡æ ¸ä¸éè¿</el-tag> <span v-else>{{ row.status }}</span> </template> <template #operation="{ row }"> <el-button type="primary" text icon="editPen" link @click="editScheduledTask(row)" > ç¼è¾ </el-button> <el-button type="warning" link :disabled="row.status === 'å®¡æ ¸éè¿'" @click="openScheduledApprove(row)" > å®¡æ¹ </el-button> <el-button type="danger" text icon="delete" link @click="delScheduledTaskByIds(row.id)" > å é¤ @@ -135,24 +144,13 @@ <div class="actions"> <el-text class="mx-1" size="large">ä»»å¡è®°å½</el-text> <div> <el-button type="primary" icon="Plus" :disabled="multipleList.length !== 1" @click="addMaintain" > æ°å¢ä¿å » </el-button> <el-button type="success" icon="Van" @click="addPlan"> æ°å¢è®¡å </el-button> <el-button @click="handleOut"> å¯¼åº </el-button> <el-button type="danger" icon="Delete" :disabled="multipleList.length <= 0" :disabled="multipleList.length <= 0 || hasFinishedStatus" @click="delRepairByIds(multipleList.map((item) => item.id))" > æ¹éå é¤ @@ -181,21 +179,44 @@ <el-tag v-if="row.status === 0" type="warning">å¾ ä¿å »</el-tag> </template> <template #operation="{ row }"> <!-- è¿ä¸ªåè½è·æ°å¢ä¿å »åè½ä¸æ¨¡ä¸æ ·ï¼æå¥æä¹ï¼ --> <!-- <el-button type="primary" text @click="addMaintain(row)" > æ°å¢ä¿å » </el-button> --> <el-button type="primary" text icon="editPen" link :disabled="row.status === 1" @click="editPlan(row.id)" > ç¼è¾ </el-button> <el-button type="success" link :disabled="row.status === 1" @click="addMaintain(row)" > ä¿å » </el-button> <el-button type="danger" text icon="delete" link :disabled="row.status === 1" @click="delRepairByIds(row.id)" > å é¤ </el-button> <el-button type="primary" link @click="openFileDialog(row)" > éä»¶ </el-button> </template> </PIMTable> @@ -203,24 +224,41 @@ </el-tab-pane> </el-tabs> <PlanModal ref="planModalRef" @ok="getTableData" /> <MaintenanceModal ref="maintainModalRef" @ok="getTableData" /> <FormDia ref="formDiaRef" @closeDia="getScheduledTableData" /> <MaintenanceModal ref="maintainModalRef" @ok="getTableData" /> <FormDia ref="formDiaRef" @closeDia="getScheduledTableData" /> <ApproveModal ref="approveModalRef" @ok="getScheduledTableData" /> <FileListDialog ref="fileListDialogRef" v-model="fileDialogVisible" :show-upload-button="true" :show-delete-button="true" :delete-method="handleAttachmentDelete" :name-column-label="'éä»¶åç§°'" :rulesRegulationsManagementId="currentMaintenanceTaskId" @upload="handleAttachmentUpload" /> </div> </template> <script setup> import { ref, onMounted, reactive, getCurrentInstance, nextTick } from 'vue' import { ref, onMounted, reactive, getCurrentInstance, nextTick, computed } from 'vue' import { Search } from '@element-plus/icons-vue' import { ElMessage, ElMessageBox } from 'element-plus' import PlanModal from './Modal/PlanModal.vue' import MaintenanceModal from './Modal/MaintenanceModal.vue' import FormDia from './Modal/formDia.vue' import PlanModal from './Form/PlanModal.vue' import MaintenanceModal from './Form/MaintenanceModal.vue' import FormDia from './Form/formDia.vue' import ApproveModal from './Form/ApproveModal.vue' import FileListDialog from '@/components/Dialog/FileListDialog.vue' import { getUpkeepPage, delUpkeep, deviceMaintenanceTaskList, deviceMaintenanceTaskDel, } from '@/api/equipmentManagement/upkeep' import { listMaintenanceTaskFiles, addMaintenanceTaskFile, delMaintenanceTaskFile, } from '@/api/equipmentManagement/maintenanceTaskFile' import dayjs from 'dayjs' const { proxy } = getCurrentInstance() @@ -234,6 +272,12 @@ const maintainModalRef = ref() // 宿¶ä»»å¡å¼¹çªæ§å¶å¨ const formDiaRef = ref() // ä¿å »å®¡æ¹å¼¹çª const approveModalRef = ref() // éä»¶å¼¹çª const fileListDialogRef = ref(null) const fileDialogVisible = ref(false) const currentMaintenanceTaskId = ref(null) // ä»»å¡è®°å½tabï¼å设å¤ä¿å »é¡µé¢ï¼ç¸å ³åé const filters = reactive({ @@ -307,6 +351,15 @@ }, { prop: "registrant", label: "ç»è®°äºº", minWidth: 100 }, { prop: "registrationDate", label: "ç»è®°æ¥æ", minWidth: 100 }, { prop: "auditName", label: "å®¡æ ¸äºº", width: 120 }, { prop: "supervisoryName", label: "çç£äºº", width: 120 }, { label: "ç¶æ", align: "center", prop: "status", dataType: "slot", slot: "statusRef", }, { fixed: "right", label: "æä½", @@ -379,7 +432,7 @@ dataType: "slot", slot: "operation", align: "center", width: "200px", width: "350px", }, ]) @@ -494,15 +547,24 @@ multipleList.value = selection } // æ£æ¥éä¸çè®°å½ä¸æ¯å¦æå®ç»ç¶æç const hasFinishedStatus = computed(() => { return multipleList.value.some(item => item.status === 1) }) const changePage = (page) => { pagination.value.currentPage = page.page pagination.value.pageSize = page.limit getTableData() } const addMaintain = () => { const row = multipleList.value[0] const addMaintain = (row) => { maintainModalRef.value.open(row.id, row) } // 宿¶ä»»å¡å®¡æ¹ const openScheduledApprove = (row) => { approveModalRef.value.open(row) } const addPlan = () => { @@ -514,6 +576,13 @@ } const delRepairByIds = async (ids) => { // æ£æ¥æ¯å¦æå®ç»ç¶æçè®°å½ const hasFinished = multipleList.value.some(item => item.status === 1) if (hasFinished) { ElMessage.warning('ä¸è½å é¤ç¶æä¸ºå®ç»çè®°å½') return } try { await ElMessageBox.confirm('确认å é¤ä¿å »æ°æ®, æ¤æä½ä¸å¯é?', 'è¦å', { confirmButtonText: 'ç¡®å®', @@ -554,6 +623,79 @@ getTableData() } // éä»¶ç¸å ³æ¹æ³ // æ¥è¯¢éä»¶å表 const fetchMaintenanceTaskFiles = async (deviceMaintenanceId) => { try { const params = { current: 1, size: 100, deviceMaintenanceId, rulesRegulationsManagementId:deviceMaintenanceId } const res = await listMaintenanceTaskFiles(params) const records = res?.data?.records || [] const mapped = records.map(item => ({ id: item.id, name: item.fileName || item.name, url: item.fileUrl || item.url, raw: item, })) fileListDialogRef.value?.setList(mapped) } catch (error) { ElMessage.error('è·åéä»¶å表失败') } } // æå¼éä»¶å¼¹çª const openFileDialog = async (row) => { currentMaintenanceTaskId.value = row.id fileDialogVisible.value = true await fetchMaintenanceTaskFiles(row.id) } // å·æ°éä»¶å表 const refreshFileList = async () => { if (!currentMaintenanceTaskId.value) return await fetchMaintenanceTaskFiles(currentMaintenanceTaskId.value) } // ä¸ä¼ éä»¶ const handleAttachmentUpload = async (filePayload) => { if (!currentMaintenanceTaskId.value) return try { const payload = { name: filePayload?.fileName || filePayload?.name, url: filePayload?.fileUrl || filePayload?.url, deviceMaintenanceId: currentMaintenanceTaskId.value, } await addMaintenanceTaskFile(payload) ElMessage.success('æä»¶ä¸ä¼ æå') await refreshFileList() } catch (error) { ElMessage.error('æä»¶ä¸ä¼ 失败') } } // å é¤éä»¶ const handleAttachmentDelete = async (row) => { if (!row?.id) return false try { await ElMessageBox.confirm('确认å é¤è¯¥éä»¶ï¼', 'æç¤º', { type: 'warning' }) } catch { return false } try { await delMaintenanceTaskFile(row.id) ElMessage.success('å 餿å') await refreshFileList() return true } catch (error) { ElMessage.error('å é¤å¤±è´¥') return false } } onMounted(() => { // æ ¹æ®é»è®¤æ¿æ´»ç Tab è°ç¨å¯¹åºçæ¥è¯¢æ¥å£ if (activeTab.value === 'scheduled') { src/views/inventoryManagement/receiptManagement/index.vue
@@ -23,7 +23,9 @@ </div> <div> <el-button type="primary" @click="openForm('add')">æ°å¢å ¥åº</el-button> @click="openFormadd('add')">订åå ¥åº</el-button> <el-button type="primary" @click="openDirectForm('add')">æ°å¢å ¥åº</el-button> <el-button @click="handleOut">导åº</el-button> <el-button type="danger" plain @@ -231,6 +233,72 @@ </div> </template> </el-dialog> <!-- ç´æ¥æ°å¢å ¥åºå¯¹è¯æ¡ --> <el-dialog v-model="directDialogVisible" title="æ°å¢å ¥åº" width="70%" @close="closeDirectDia"> <el-form :model="directForm" label-width="140px" label-position="top" :rules="directRules" ref="directFormRef"> <el-form :model="directQuery" class="mb-2"> <el-form-item label="产å大类"> <el-select v-model="directQuery.productCategory" placeholder="è¯·éæ©äº§å大类" clearable filterable @change="handleProductCategoryChange"> <el-option v-for="item in productList" :key="item.id" :label="item.productName" :value="item.productName" /> </el-select> </el-form-item> <el-form-item label="è§æ ¼åå·"> <el-select v-model="directQuery.productModelId" placeholder="请å éæ©äº§å大类" clearable filterable :disabled="!directQuery.productCategory"> <el-option v-for="item in productModelList" :key="item.id" :label="item.model" :value="item.id" /> </el-select> </el-form-item> <el-form-item label="å ¥åºæ°é"> <el-input v-model="directQuery.inboundQuantity" placeholder="è¾å ¥å ¥åºæ°é" clearable /> </el-form-item> <el-form-item label="é¢è¦æ°é"> <el-input v-model="directQuery.warnNum" placeholder="è¾å ¥é¢è¦æ°é" clearable /> </el-form-item> <el-form-item label="缺货æ°é"> <el-input v-model="directQuery.outStockQuantity" placeholder="è¾å ¥ç¼ºè´§æ°é" clearable /> </el-form-item> <el-form-item label="缺货æ åµ"> <el-input v-model="directQuery.shortageDescription" placeholder="è¾å ¥ç¼ºè´§æ åµ" clearable /> </el-form-item> </el-form> </el-form> <template #footer> <div class="dialog-footer"> <el-button type="primary" @click="submitDirectForm">确认</el-button> <el-button @click="closeDirectDia">åæ¶</el-button> </div> </template> </el-dialog> </div> </template> @@ -245,8 +313,10 @@ addSutockIn, delStockIn, selectProductRecordListByPuechaserId, addProduct, } from "@/api/inventoryManagement/stockIn.js"; import { purchaseListPage } from "@/api/procurementManagement/procurementLedger.js"; import { productTreeList, modelList } from "@/api/basicData/product.js"; const userStore = useUserStore(); const { proxy } = getCurrentInstance(); @@ -304,8 +374,56 @@ { required: true, message: "请è¾å ¥å ¥åºæ¹æ¬¡", trigger: "blur" }, ], }, // ç´æ¥æ°å¢å ¥åºç¸å ³ directDialogVisible: false, directForm: { id: null, productId: null, productModel: "", inboundQuantity: 0, warnNum: 0, outStockQuantity: 0, shortageDescription: "", }, directRules: { supplierId: [ { required: true, message: "è¯·éæ©ä¾åºå", trigger: "change" }, ], inboundTime: [ { required: true, message: "è¯·éæ©å ¥åºæ¶é´", trigger: "change" }, ], inboundBatch: [ { required: true, message: "请è¾å ¥å ¥åºæ¹æ¬¡", trigger: "blur" }, ], }, directQuery: { productId: null, productModelId: null, inboundQuantity: 0, warnNum: 0, outStockQuantity: 0, shortageDescription: "", }, directProductList: [], directLoading: false, directSelectedRows: [], // 产åç¸å ³ productList: [], productModelList: [], }); const { searchForm, form, rules } = toRefs(data); const { searchForm, form, rules, directDialogVisible, directForm, directRules, directQuery, directProductList, directLoading, directSelectedRows, productModelList, } = toRefs(data); const formatPurchaseOption = (item = {}) => { const contract = item.purchaseContractNumber || "--"; @@ -441,9 +559,7 @@ loadingProducts.value = false; } }; // æå¼å¼¹æ¡ const openForm = async (type, row) => { const openFormadd = async (type, row) => { operationType.value = type; dialogFormVisible.value = true; selectedRows.value = []; @@ -491,6 +607,215 @@ loadingProducts.value = false; } } }; // æå¼è®¢åå ¥åºå¼¹æ¡ const openForm = async (type, row) => { if (row.salesLedgerProductId) { operationType.value = type; dialogFormVisible.value = true; selectedRows.value = []; await loadPurchaseOptions(); if (type === "add") { // æ°å¢æ¶åå§å表å form.value = { id: null, purchaseContractNumber: "", supplierId: null, supplierName: "", inboundTime: "", inboundBatch: "", recorderId: userStore.userId, recorderName: userStore.name, entryDate: getCurrentDate(), remark: "", }; productList.value = []; // æ¸ ç©ºäº§åå表 } else { form.value = JSON.parse(JSON.stringify(row)); try { loadingProducts.value = true; // æ ¹æ®ååå·å 载对åºç产åå表ï¼å设 getProductByContract æ¯å¯ç¨æ¥å£ï¼ const res = await selectProductRecordListByPuechaserId({ purchaseContractNumber: form.value.purchaseContractNumber, id: row.id, }); productList.value = res.data.map(item => ({ ...item, quantityStock: Number( item.quantityStock ?? item.inboundQuantity ?? row.inboundNum ?? 0 ), originalQuantityStock: Number( item.quantityStock ?? item.inboundQuantity ?? row.inboundNum ?? 0 ), })); selectedRows.value = productList.value; } catch (error) { console.error("å 载产å失败:", error); proxy.$modal.msgError("å 载产å失败"); productList.value = []; } finally { loadingProducts.value = false; } } } else { // ç´æ¥æ°å¢å ¥åº openDirectForm(type, row); } }; // æå¼ç´æ¥æ°å¢å ¥åºå¼¹æ¡ const openDirectForm = (type, row) => { directDialogVisible.value = true; directSelectedRows.value = []; directQuery.value = { productId: null, productModelId: null, inboundQuantity: 0, warnNum: 0, outStockQuantity: 0, shortageDescription: "", }; directProductList.value = []; directForm.value = { id: null, supplierId: null, supplierName: "", inboundTime: "", inboundBatch: "", recorderId: userStore.userId, recorderName: userStore.name, entryDate: getCurrentDate(), remark: "", }; if (type === "edit" && row) { // ç¼è¾æ¨¡å¼ï¼åæ¾æ°æ® directForm.value = { ...row }; // åæ¾äº§å大类 const selectedProduct = productList.value.find( item => item.productName === row.productCategory ); if (selectedProduct) { directQuery.value.productId = selectedProduct.id; // æ ¹æ®äº§å大类å è½½è§æ ¼åå·åè¡¨å¹¶åæ¾ loadProductModelList(selectedProduct.id).then(() => { // åæ¾è§æ ¼åå· directQuery.value.productCategory = row.productCategory || ""; directQuery.value.productModelId = row.productModelId; // åæ¾å ¶ä»å段 directQuery.value.inboundQuantity = row.inboundNum || row.inboundQuantity; directQuery.value.warnNum = row.warnNum || 0; directQuery.value.outStockQuantity = row.outStockQuantity || 0; directQuery.value.shortageDescription = row.shortageDescription || ""; }); } } }; // å 载产å大类å表 const loadProductList = async () => { try { const res = await productTreeList(); productList.value = res; } catch (error) { console.error("å 载产å大类失败:", error); proxy.$modal.msgError("å 载产å大类失败"); } }; // å¤ç产å大类åå const handleProductCategoryChange = value => { directQuery.value.specificationModel = ""; productModelList.value = []; if (value) { // æ ¹æ®äº§å大类å è½½è§æ ¼åå·å表 const selectedProduct = productList.value.find( item => item.productName === value ); if (selectedProduct) { loadProductModelList(selectedProduct.id); } } }; // å 载产ååå·å表 const loadProductModelList = async productId => { try { const res = await modelList({ id: productId }); productModelList.value = res; } catch (error) { console.error("å è½½è§æ ¼åå·å¤±è´¥:", error); proxy.$modal.msgError("å è½½è§æ ¼åå·å¤±è´¥"); } }; // ç´æ¥æ°å¢å ¥åºæç´¢äº§å const onDirectSearch = async () => { try { directLoading.value = true; // è¿ééè¦è°ç¨äº§åæç´¢APIï¼ææ¶æ¨¡ææ°æ® // å®é 项ç®ä¸åºè¯¥è°ç¨çå®çäº§åæ¥è¯¢æ¥å£ directProductList.value = [ { id: 1, productCategory: "çµå产å", specificationModel: "A-100", unit: "个", quantityStock: 0, outStockQuantity: 0, shortageDescription: "", taxRate: 13, taxInclusiveUnitPrice: 100, }, { id: 2, productCategory: "çµå产å", specificationModel: "A-200", unit: "个", quantityStock: 0, outStockQuantity: 0, shortageDescription: "", taxRate: 13, taxInclusiveUnitPrice: 200, }, { id: 3, productCategory: "åå ¬ç¨å", specificationModel: "B-100", unit: "å¥", quantityStock: 0, outStockQuantity: 0, shortageDescription: "", taxRate: 13, taxInclusiveUnitPrice: 50, }, ]; } catch (error) { console.error("æç´¢äº§å失败:", error); proxy.$modal.msgError("æç´¢äº§å失败"); directProductList.value = []; } finally { directLoading.value = false; } }; // ç´æ¥æ°å¢å ¥åºéç½®æç´¢ const onDirectReset = () => { directQuery.value = { productId: null, productModelId: null, inboundQuantity: 0, warnNum: 0, outStockQuantity: 0, shortageDescription: "", }; directProductList.value = []; }; // å¤çç´æ¥æ°å¢å ¥åºäº§åéæ© const handleDirectSelectionChange = selection => { directSelectedRows.value = selection.filter(item => item.id); }; const updatePro = async () => { @@ -583,10 +908,50 @@ } }; // å ³éå¼¹æ¡ // å ³é订åå ¥åºå¼¹æ¡ const closeDia = () => { proxy.$refs.formRef.resetFields(); dialogFormVisible.value = false; }; // å ³éç´æ¥æ°å¢å ¥åºå¼¹æ¡ const closeDirectDia = () => { if (proxy.$refs.directFormRef) { proxy.$refs.directFormRef.resetFields(); } directDialogVisible.value = false; }; // æäº¤ç´æ¥æ°å¢å ¥åºè¡¨å const submitDirectForm = async () => { // éªè¯è³å°éæ©äºä¸ä¸ªäº§å console.log(directQuery.value, "directQuery.value"); if (!directQuery.value.productModelId) { proxy.$modal.msgWarning("请å éæ©äº§ååè§æ ¼åå·"); return; } await proxy.$refs.directFormRef.validate(); if (directQuery.value.inboundQuantity <= 0) { proxy.$modal.msgError("æ¬æ¬¡å ¥åºæ°éé大äº0"); return; } // åå¤æäº¤æ°æ® const submitData = { ...directQuery.value, // 妿æ¯ç¼è¾æ¨¡å¼ï¼æ·»å id ...(directForm.value.id && { id: directForm.value.id }), }; addProduct(submitData).then(res => { if (res.code === 200) { proxy.$modal.msgSuccess("æä½æå"); closeDirectDia(); getList(); // å·æ°å表 } else { proxy.$modal.msgError(res.msg || "æä½å¤±è´¥"); } }); }; // è¡¨æ ¼éæ©æ°æ® const handleSelectionChange = selection => { @@ -679,6 +1044,7 @@ onMounted(() => { getList(); loadProductList(); }); </script> src/views/inventoryManagement/stockManagement/index.vue
@@ -3,67 +3,148 @@ <div class="search_form"> <div> <span class="search_title">ä¾åºååç§°ï¼</span> <el-input v-model="searchForm.supplierName" style="width: 240px" placeholder="请è¾å ¥" @change="handleQuery" clearable prefix-icon="Search" /> <span class="search_title ml10">å ¥åºæ¥æï¼</span> <el-date-picker v-model="searchForm.timeStr" type="date" placeholder="è¯·éæ©æ¥æ" value-format="YYYY-MM-DD" format="YYYY-MM-DD" clearable @change="handleQuery" /> <el-button type="primary" @click="handleQuery" style="margin-left: 10px">æç´¢</el-button> <el-input v-model="searchForm.supplierName" style="width: 240px" placeholder="请è¾å ¥" @change="handleQuery" clearable prefix-icon="Search" /> <span class="search_title ml10">å ¥åºæ¥æï¼</span> <el-date-picker v-model="searchForm.timeStr" type="date" placeholder="è¯·éæ©æ¥æ" value-format="YYYY-MM-DD" format="YYYY-MM-DD" clearable @change="handleQuery" /> <el-button type="primary" @click="handleQuery" style="margin-left: 10px">æç´¢</el-button> </div> <div> <!-- <el-button type="primary" @click="openForm('add')">æ°å¢</el-button> --> <el-button @click="handleOut">导åº</el-button> <el-button type="danger" plain @click="handleDelete">å é¤</el-button> <el-button type="danger" plain @click="handleDelete">å é¤</el-button> </div> </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" show-summary style="width: 100%" :row-class-name="tableRowClassName" :summary-method="summarizeMainTable" 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="createTime" width="100" show-overflow-tooltip /> <el-table-column label="ä¾åºååç§°" prop="supplierName" width="240" show-overflow-tooltip /> <el-table-column label="产å大类" prop="productCategory" width="100" show-overflow-tooltip /> <el-table-column label="è§æ ¼åå·" prop="specificationModel" width="200" show-overflow-tooltip /> <el-table-column label="åä½" prop="unit" width="80" show-overflow-tooltip /> <el-table-column label="åºåæ°é" prop="inboundNum0" width="100" show-overflow-tooltip /> <el-table-column label="åºåé¢è¦æ°é" prop="warnNum" width="130" show-overflow-tooltip /> <el-table-column label="å«ç¨åä»·" prop="taxInclusiveUnitPrice" width="100" show-overflow-tooltip /> <el-table-column label="å«ç¨æ»ä»·" prop="taxInclusiveTotalPrice" width="100" show-overflow-tooltip /> <el-table-column label="ç¨ç(%)" prop="taxRate" width="100" show-overflow-tooltip /> <el-table-column label="ä¸å«ç¨æ»ä»·" prop="taxExclusiveTotalPrice" width="100" show-overflow-tooltip /> <el-table-column label="å ¥åºäºº" prop="createBy" width="80" show-overflow-tooltip /> <el-table-column fixed="right" label="æä½" min-width="60" align="center"> <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange" :expand-row-keys="expandedRowKeys" :row-key="row => row.id" show-summary style="width: 100%" :row-class-name="tableRowClassName" :summary-method="summarizeMainTable" 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="createTime" width="100" show-overflow-tooltip /> <el-table-column label="ä¾åºååç§°" prop="supplierName" width="240" show-overflow-tooltip /> <el-table-column label="产å大类" prop="productCategory" width="100" show-overflow-tooltip /> <el-table-column label="è§æ ¼åå·" prop="specificationModel" width="200" show-overflow-tooltip /> <el-table-column label="åä½" prop="unit" width="80" show-overflow-tooltip /> <el-table-column label="åºåæ°é" prop="inboundNum0" width="100" show-overflow-tooltip /> <el-table-column label="åºåé¢è¦æ°é" prop="warnNum" width="130" show-overflow-tooltip /> <el-table-column label="å«ç¨åä»·" prop="taxInclusiveUnitPrice" width="100" show-overflow-tooltip /> <el-table-column label="å«ç¨æ»ä»·" prop="taxInclusiveTotalPrice" width="100" show-overflow-tooltip /> <el-table-column label="ç¨ç(%)" prop="taxRate" width="100" show-overflow-tooltip /> <el-table-column label="ä¸å«ç¨æ»ä»·" prop="taxExclusiveTotalPrice" width="100" show-overflow-tooltip /> <el-table-column label="å ¥åºäºº" prop="createBy" width="80" show-overflow-tooltip /> <el-table-column fixed="right" label="æä½" min-width="60" align="center"> <template #default="scope"> <el-button link type="primary" size="small" @click="openForm('edit', scope.row);">ç¼è¾</el-button> <el-button link type="primary" size="small" @click="openForm('edit', scope.row);">ç¼è¾</el-button> </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> <el-dialog v-model="dialogFormVisible" :title="operationType === 'add' ? 'æ°å¢åºå' : 'ç¼è¾åºå'" width="70%" @close="closeDia"> <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef"> <el-dialog v-model="dialogFormVisible" :title="operationType === 'add' ? 'æ°å¢åºå' : 'ç¼è¾åºå'" width="70%" @close="closeDia"> <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="supplierName"> <el-input disabled v-model="form.supplierName" placeholder="请è¾å ¥" clearable /> <el-form-item label="ä¾åºååç§°ï¼" prop="supplierName"> <el-input disabled v-model="form.supplierName" placeholder="请è¾å ¥" clearable /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="产å大类ï¼" prop="productId"> <el-select disabled v-model="form.productCategory" placeholder="è¯·éæ©" clearable filterable> <el-option v-for="item in productList" :key="item.id" :label="item.productName" <el-form-item label="产å大类ï¼" prop="productId"> <el-select disabled v-model="form.productCategory" placeholder="è¯·éæ©" clearable filterable> <el-option v-for="item in productList" :key="item.id" :label="item.productName" :value="item.productName" /> </el-select> </el-form-item> @@ -71,77 +152,122 @@ </el-row> <el-row :gutter="30"> <el-col :span="12"> <el-form-item label="è§æ ¼åå·ï¼" prop="productManageId"> <el-select disabled v-model="form.specificationModel" placeholder="请å éæ©äº§å大类" clearable filterable :disabled="!form.productCategory"> <el-option v-for="item in productModelList" :key="item.id" :label="item.model" <el-form-item label="è§æ ¼åå·ï¼" prop="productManageId"> <el-select disabled v-model="form.specificationModel" placeholder="请å éæ©äº§å大类" clearable filterable :disabled="!form.productCategory"> <el-option v-for="item in productModelList" :key="item.id" :label="item.model" :value="item.id" /> </el-select> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="åä½ï¼" prop="customerId"> <el-input disabled v-model="form.unit" placeholder="请è¾å ¥" clearable /> <el-form-item label="åä½ï¼" prop="customerId"> <el-input disabled v-model="form.unit" placeholder="请è¾å ¥" clearable /> </el-form-item> </el-col> </el-row> <el-row :gutter="30"> <el-col :span="12"> <el-form-item label="åºåæ¶é´ï¼" prop="projectName"> <el-date-picker style="width: 100%" v-model="form.updateTime" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="date" placeholder="è¯·éæ©" clearable /> <el-form-item label="åºåæ¶é´ï¼" prop="projectName"> <el-date-picker style="width: 100%" v-model="form.updateTime" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="date" placeholder="è¯·éæ©" clearable /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="å ¥åºæ¶é´ï¼" prop="projectName"> <el-date-picker style="width: 100%" v-model="form.createTime" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="date" placeholder="è¯·éæ©" clearable /> </el-form-item> </el-col> </el-row> <el-row :gutter="30"> <el-col :span="12"> <el-form-item label="å«ç¨åä»·ï¼" prop="customerId"> <el-input disabled v-model="form.taxInclusiveUnitPrice" placeholder="请è¾å ¥" clearable /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="å«ç¨æ»ä»·ï¼" prop="customerContractNo"> <el-input disabled v-model="form.taxInclusiveTotalPrice" placeholder="请è¾å ¥" clearable /> </el-form-item> </el-col> </el-row> <el-row :gutter="30"> <el-col :span="12"> <el-form-item label="ç¨çï¼" prop="customerId"> <el-input disabled v-model="form.taxRate" placeholder="请è¾å ¥" clearable /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="ä¸å«ç¨æ»ä»·ï¼" prop="entryDate"> <el-input disabled v-model="form.taxExclusiveTotalPrice" placeholder="请è¾å ¥" clearable /> <el-form-item label="å ¥åºæ¶é´ï¼" prop="projectName"> <el-date-picker style="width: 100%" v-model="form.createTime" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="date" placeholder="è¯·éæ©" clearable /> </el-form-item> </el-col> </el-row> <el-row :gutter="30"> <el-col :span="12"> <el-form-item label="åºåºäººï¼" prop="entryPerson"> <el-select v-model="form.createUser" placeholder="è¯·éæ©" clearable> <el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.userId" /> <el-form-item label="å«ç¨åä»·ï¼" prop="customerId"> <el-input disabled v-model="form.taxInclusiveUnitPrice" placeholder="请è¾å ¥" clearable /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="å«ç¨æ»ä»·ï¼" prop="customerContractNo"> <el-input disabled v-model="form.taxInclusiveTotalPrice" placeholder="请è¾å ¥" clearable /> </el-form-item> </el-col> </el-row> <el-row :gutter="30"> <el-col :span="12"> <el-form-item label="ç¨çï¼" prop="customerId"> <el-input disabled v-model="form.taxRate" placeholder="请è¾å ¥" clearable /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="ä¸å«ç¨æ»ä»·ï¼" prop="entryDate"> <el-input disabled v-model="form.taxExclusiveTotalPrice" placeholder="请è¾å ¥" clearable /> </el-form-item> </el-col> </el-row> <el-row :gutter="30"> <el-col :span="12"> <el-form-item label="åºåºäººï¼" prop="entryPerson"> <el-select v-model="form.createUser" placeholder="è¯·éæ©" clearable> <el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.userId" /> </el-select> </el-form-item> </el-col> <!-- <el-col :span="12">--> <!-- <el-form-item label="åºåé¢è¦æ°éï¼" prop="warnNum">--> <!-- <el-input v-model="form.warnNum" placeholder="请è¾å ¥æä½åºå" clearable />--> <!-- </el-form-item>--> <!-- </el-col>--> <!-- <el-col :span="12">--> <!-- <el-form-item label="åºåé¢è¦æ°éï¼" prop="warnNum">--> <!-- <el-input v-model="form.warnNum" placeholder="请è¾å ¥æä½åºå" clearable />--> <!-- </el-form-item>--> <!-- </el-col>--> </el-row> </el-form> <template #footer> <div class="dialog-footer"> <el-button type="primary" @click="submitForm">确认</el-button> <el-button type="primary" @click="submitForm">确认</el-button> <el-button @click="closeDia">åæ¶</el-button> </div> </template> @@ -150,277 +276,298 @@ </template> <script setup> import pagination from '@/components/PIMTable/Pagination.vue' import { ref, reactive, toRefs, onMounted, getCurrentInstance } from 'vue' import { ElMessageBox } from "element-plus"; import useUserStore from '@/store/modules/user' import { userListNoPageByTenantId } from "@/api/system/user.js"; import { productTreeList,modelList } from "@/api/basicData/product.js" import { getStockManagePage, delStockManage, } from "@/api/inventoryManagement/stockManage.js"; import { updateManagement,updateStockIn } from "@/api/inventoryManagement/stockIn.js"; import pagination from "@/components/PIMTable/Pagination.vue"; import { ref, reactive, toRefs, onMounted, getCurrentInstance } from "vue"; import { ElMessageBox } from "element-plus"; import useUserStore from "@/store/modules/user"; import { userListNoPageByTenantId } from "@/api/system/user.js"; import { productTreeList, modelList } from "@/api/basicData/product.js"; import { getStockManagePage, delStockManage, } from "@/api/inventoryManagement/stockManage.js"; import { updateManagement, updateStockIn, } from "@/api/inventoryManagement/stockIn.js"; const userStore = useUserStore(); const { proxy } = getCurrentInstance(); const tableData = ref([]); const productData = ref([]); const selectedRows = ref([]); const userList = ref([]); const productList = ref([]); const productModelList = ref([]); // const customerOption = ref([]) const tableLoading = ref(false); const page = reactive({ current: 1, size: 100, }); const total = ref(0); const fileList = ref([]); const loading = ref(false); // ç¨æ·ä¿¡æ¯è¡¨åå¼¹æ¡æ°æ® const operationType = ref(""); const dialogFormVisible = ref(false); const data = reactive({ searchForm: { supplierName: "", timeStr: "", }, form: { supplierId: null, supplierName: "", productId: null, productName: "", userId: userStore.userId, nickName: "", productModelId: null, model: "", unit: "", productrecordId: null, taxInclusiveUnitPrice: "", taxInclusiveTotalPrice: "", taxRate: "", taxExclusiveTotalPrice: "", inboundTime: "", inboundBatch: "", stockQuantity: "", boundTime: "", warnNum: "", // æ°å¢æä½åºååæ®µ salesLedgerProductId: null, }, rules: { // supplierName: [{ required: true, message: '请è¾å ¥ä¾åºååç§°', trigger: 'blur' }], productCategory: [ { required: true, message: "è¯·éæ©äº§å大类", trigger: "change" }, ], specificationModel: [ { required: true, message: "请è¾å ¥è§æ ¼åå·", trigger: "blur" }, ], unit: [{ required: true, message: "请è¾å ¥åä½", trigger: "blur" }], stockQuantity: [ { required: true, message: "请è¾å ¥åºåºæ°é", trigger: "blur" }, ], taxInclusiveUnitPrice: [ { required: true, message: "请è¾å ¥å«ç¨åä»·", trigger: "blur" }, ], taxInclusiveTotalPrice: [ { required: true, message: "请è¾å ¥å«ç¨æ»ä»·", trigger: "blur" }, ], taxRate: [{ required: true, message: "请è¾å ¥ç¨ç", trigger: "blur" }], taxExclusiveTotalPrice: [ { required: true, message: "请è¾å ¥ä¸å«ç¨æ»ä»·", trigger: "blur" }, ], boundTime: [ { required: true, message: "è¯·éæ©åºåæ¶é´", trigger: "change" }, ], inboundTime: [ { required: true, message: "è¯·éæ©å ¥åºæ¶é´", trigger: "change" }, ], inboundPerson: [ { required: true, message: "è¯·éæ©åºåºäºº", trigger: "change" }, ], warnNum: [{ required: true, message: "请è¾å ¥æä½åºå", trigger: "blur" }], }, }); const { searchForm, form, rules } = toRefs(data); const userStore = useUserStore() const { proxy } = getCurrentInstance() const tableData = ref([]) const productData = ref([]) const selectedRows = ref([]) const userList = ref([]) const productList = ref([]) const productModelList = ref([]) // const customerOption = ref([]) const tableLoading = ref(false) const page = reactive({ current: 1, size: 100, }) const total = ref(0) const fileList = ref([]) const loading = ref(false); // ç¨æ·ä¿¡æ¯è¡¨åå¼¹æ¡æ°æ® const operationType = ref('') const dialogFormVisible = ref(false) const data = reactive({ searchForm: { supplierName: '', timeStr: '', }, form: { supplierId: null, supplierName: '', productId: null, productName: '', userId: userStore.userId, nickName: '', productModelId: null, model: '', unit: '', productrecordId: null, taxInclusiveUnitPrice: '', taxInclusiveTotalPrice: '', taxRate: '', taxExclusiveTotalPrice: '', inboundTime: '', inboundBatch: '', stockQuantity: '', boundTime: '', warnNum: '', // æ°å¢æä½åºååæ®µ salesLedgerProductId: null, }, rules: { supplierName: [{ required: true, message: '请è¾å ¥ä¾åºååç§°', trigger: 'blur' }], productCategory: [{ required: true, message: 'è¯·éæ©äº§å大类', trigger: 'change' }], specificationModel: [{ required: true, message: '请è¾å ¥è§æ ¼åå·', trigger: 'blur' }], unit: [{ required: true, message: '请è¾å ¥åä½', trigger: 'blur' }], stockQuantity: [{ required: true, message: '请è¾å ¥åºåºæ°é', trigger: 'blur' }], taxInclusiveUnitPrice: [{ required: true, message: '请è¾å ¥å«ç¨åä»·', trigger: 'blur' }], taxInclusiveTotalPrice: [{ required: true, message: '请è¾å ¥å«ç¨æ»ä»·', trigger: 'blur' }], taxRate: [{ required: true, message: '请è¾å ¥ç¨ç', trigger: 'blur' }], taxExclusiveTotalPrice: [{ required: true, message: '请è¾å ¥ä¸å«ç¨æ»ä»·', trigger: 'blur' }], boundTime: [{ required: true, message: 'è¯·éæ©åºåæ¶é´', trigger: 'change' }], inboundTime: [{ required: true, message: 'è¯·éæ©å ¥åºæ¶é´', trigger: 'change' }], inboundPerson: [{ required: true, message: 'è¯·éæ©åºåºäºº', trigger: 'change' }], warnNum: [{ required: true, message: '请è¾å ¥æä½åºå', trigger: 'blur' }], } }) const { searchForm, form, rules } = toRefs(data) // æ¥è¯¢å表 /** æç´¢æé®æä½ */ const handleQuery = () => { page.current = 1 getList() } const paginationChange = (obj) => { page.current = obj.page; page.size = obj.limit; getList() } const getList = () => { tableLoading.value = true getStockManagePage({ ...searchForm.value, ...page }).then(res => { tableLoading.value = false tableData.value = res.data.records total.value = res.data.total // æ°æ®å è½½å®æåæ£æ¥åºå // checkStockAndCreatePurchase(); }).catch(() => { tableLoading.value = false }) } // è¡¨æ ¼éæ©æ°æ® const handleSelectionChange = (selection) => { // è¿æ»¤æåæ°æ® selectedRows.value = selection.filter(item => item.id); console.log('selection', selectedRows.value) } const expandedRowKeys = ref([]) // 主表åè®¡æ¹æ³ const summarizeMainTable = (param) => { return proxy.summarizeTable(param, ['contractAmount', 'taxInclusiveTotalPrice', 'taxExclusiveTotalPrice']); }; // è¡¨æ ¼è¡ç±»å const tableRowClassName = ({ row }) => { const stock = Number(row?.inboundNum0 ?? 0); const warn = Number(row?.warnNum ?? 0); if (!Number.isFinite(stock) || !Number.isFinite(warn)) { return ''; } return stock < warn ? 'row-low-stock' : ''; }; // æå¼å¼¹æ¡ const openForm = async (type, row) => { operationType.value = type form.value = {} productData.value = [] let userLists = await userListNoPageByTenantId() userList.value = userLists.data if (type === 'edit') { form.value = { ...row } productTreeList().then(res =>{ productList.value = res productList.value.forEach(i =>{ if (i.label === row.productCategory) { modelList({ id: i.id }).then((res) => { productModelList.value = res; }); } }) }) } form.value.entryDate = getCurrentDate() // 设置é»è®¤å½å ¥æ¥æä¸ºå½åæ¥æ dialogFormVisible.value = true } // æäº¤è¡¨å const submitForm = () => { console.log(form.value) proxy.$refs["formRef"].validate(valid => { if (valid) { updateManagement(form.value).then(res => { proxy.$modal.msgSuccess("æäº¤æå") closeDia() getList() // æäº¤åæ£æ¥åºåå¹¶å°è¯å建请è´å // æ¥è¯¢å表 /** æç´¢æé®æä½ */ const handleQuery = () => { page.current = 1; getList(); }; const paginationChange = obj => { page.current = obj.page; page.size = obj.limit; getList(); }; const getList = () => { tableLoading.value = true; getStockManagePage({ ...searchForm.value, ...page }) .then(res => { tableLoading.value = false; tableData.value = res.data.records; total.value = res.data.total; // æ°æ®å è½½å®æåæ£æ¥åºå // checkStockAndCreatePurchase(); }) } }) } // æ£æ¥åºåå¹¶å建请è´å // const checkStockAndCreatePurchase = async () => { // const stockList = tableData.value; // // handList() // for (const item of stockList) { // if (item.inboundNum0 < item.warnNum) { // try { // const stockInData = { // id: item.id, // quantityStock: item.warnNum + item.totalInboundNum,// ä½¿ç¨æ°æ ¼å¼å彿° // }; // loading.value = true // await updateStockIn(stockInData) // proxy.$modal.msgSuccess(`产å ${item.productCategory} ä¿®æ¹å ¥åºæå`) // loading.value = false // } catch (error) { // proxy.$modal.msgError(`产å ${item.productCategory} çæè¯·è´å失败ï¼è¯·æå¨å¤ç`); // // } // } // } // }; // å ³éå¼¹æ¡ const closeDia = () => { proxy.resetForm("formRef") dialogFormVisible.value = false } .catch(() => { tableLoading.value = false; }); }; // å¯¼åº const handleOut = () => { ElMessageBox.confirm( 'æ¯å¦ç¡®è®¤å¯¼åºï¼', '导åº', { confirmButtonText: '确认', cancelButtonText: 'åæ¶', type: 'warning', } ).then(() => { proxy.download("/stockin/exportCopy", {}, 'åºåä¿¡æ¯.xlsx') }).catch(() => { proxy.$modal.msg("已忶") }) } // å é¤ const handleDelete = () => { let ids = [] if (selectedRows.value.length > 0) { // æ£æ¥æ¯å¦æä»äººç»´æ¤çæ°æ® const unauthorizedData = selectedRows.value.filter(item => item.createUser !== userStore.id); if (unauthorizedData.length > 0) { proxy.$modal.msgWarning("ä¸å¯å é¤ä»äººç»´æ¤çæ°æ®"); return; } ids = selectedRows.value.map(item => item.id); } else { proxy.$modal.msgWarning('è¯·éæ©æ°æ®') return } ElMessageBox.confirm( 'éä¸çå 容å°è¢«å é¤ï¼æ¯å¦ç¡®è®¤å é¤ï¼', '导åº', { confirmButtonText: '确认', cancelButtonText: 'åæ¶', type: 'warning', } ).then(() => { delStockManage({ids:ids}).then(res => { proxy.$modal.msgSuccess("å 餿å") getList() // è¡¨æ ¼éæ©æ°æ® const handleSelectionChange = selection => { // è¿æ»¤æåæ°æ® selectedRows.value = selection.filter(item => item.id); console.log("selection", selectedRows.value); }; const expandedRowKeys = ref([]); // 主表åè®¡æ¹æ³ const summarizeMainTable = param => { return proxy.summarizeTable(param, [ "contractAmount", "taxInclusiveTotalPrice", "taxExclusiveTotalPrice", ]); }; // è¡¨æ ¼è¡ç±»å const tableRowClassName = ({ row }) => { const stock = Number(row?.inboundNum0 ?? 0); const warn = Number(row?.warnNum ?? 0); if (!Number.isFinite(stock) || !Number.isFinite(warn)) { return ""; } return stock < warn ? "row-low-stock" : ""; }; // æå¼å¼¹æ¡ const openForm = async (type, row) => { operationType.value = type; form.value = {}; productData.value = []; let userLists = await userListNoPageByTenantId(); userList.value = userLists.data; if (type === "edit") { form.value = { ...row }; productTreeList().then(res => { productList.value = res; productList.value.forEach(i => { if (i.label === row.productCategory) { modelList({ id: i.id }).then(res => { productModelList.value = res; }); } }); }); } form.value.entryDate = getCurrentDate(); // 设置é»è®¤å½å ¥æ¥æä¸ºå½åæ¥æ dialogFormVisible.value = true; }; // æäº¤è¡¨å const submitForm = () => { console.log(form.value); proxy.$refs["formRef"].validate(valid => { if (valid) { updateManagement(form.value).then(res => { proxy.$modal.msgSuccess("æäº¤æå"); closeDia(); getList(); // æäº¤åæ£æ¥åºåå¹¶å°è¯å建请è´å // checkStockAndCreatePurchase(); }); } }); }; // æ£æ¥åºåå¹¶å建请è´å // const checkStockAndCreatePurchase = async () => { // const stockList = tableData.value; // // handList() // for (const item of stockList) { // if (item.inboundNum0 < item.warnNum) { // try { // const stockInData = { // id: item.id, // quantityStock: item.warnNum + item.totalInboundNum,// ä½¿ç¨æ°æ ¼å¼å彿° // }; // loading.value = true // await updateStockIn(stockInData) // proxy.$modal.msgSuccess(`产å ${item.productCategory} ä¿®æ¹å ¥åºæå`) // loading.value = false // } catch (error) { // proxy.$modal.msgError(`产å ${item.productCategory} çæè¯·è´å失败ï¼è¯·æå¨å¤ç`); // // } // } // } // }; // å ³éå¼¹æ¡ const closeDia = () => { proxy.resetForm("formRef"); dialogFormVisible.value = false; }; // å¯¼åº const handleOut = () => { ElMessageBox.confirm("æ¯å¦ç¡®è®¤å¯¼åºï¼", "导åº", { confirmButtonText: "确认", cancelButtonText: "åæ¶", type: "warning", }) }).catch(() => { proxy.$modal.msg("已忶") }) } // è·åå½åæ¥æå¹¶æ ¼å¼å为 YYYY-MM-DD function getCurrentDate() { const today = new Date(); const year = today.getFullYear(); const month = String(today.getMonth() + 1).padStart(2, '0'); // æä»½ä»0å¼å§ const day = String(today.getDate()).padStart(2, '0'); return `${year}-${month}-${day}`; } onMounted(() => { getList() // checkStockAndCreatePurchase(); .then(() => { proxy.download("/stockin/exportCopy", {}, "åºåä¿¡æ¯.xlsx"); }) .catch(() => { proxy.$modal.msg("已忶"); }); }; // å é¤ const handleDelete = () => { let ids = []; if (selectedRows.value.length > 0) { // æ£æ¥æ¯å¦æä»äººç»´æ¤çæ°æ® const unauthorizedData = selectedRows.value.filter( item => item.createUser !== userStore.id ); if (unauthorizedData.length > 0) { proxy.$modal.msgWarning("ä¸å¯å é¤ä»äººç»´æ¤çæ°æ®"); return; } ids = selectedRows.value.map(item => item.id); } else { proxy.$modal.msgWarning("è¯·éæ©æ°æ®"); return; } ElMessageBox.confirm("éä¸çå 容å°è¢«å é¤ï¼æ¯å¦ç¡®è®¤å é¤ï¼", "导åº", { confirmButtonText: "确认", cancelButtonText: "åæ¶", type: "warning", }) .then(() => { delStockManage({ ids: ids }).then(res => { proxy.$modal.msgSuccess("å 餿å"); getList(); }); }) .catch(() => { proxy.$modal.msg("已忶"); }); }; // è·åå½åæ¥æå¹¶æ ¼å¼å为 YYYY-MM-DD function getCurrentDate() { const today = new Date(); const year = today.getFullYear(); const month = String(today.getMonth() + 1).padStart(2, "0"); // æä»½ä»0å¼å§ const day = String(today.getDate()).padStart(2, "0"); return `${year}-${month}-${day}`; } onMounted(() => { getList(); // checkStockAndCreatePurchase(); // æ¯å°æ¶æ£æ¥ä¸æ¬¡åºå // const intervalId = setInterval(checkStockAndCreatePurchase, 60 * 60 * 1000); // onUnmounted(() => { // // ç»ä»¶å¸è½½æ¶æ¸ é¤å®æ¶å¨ // clearInterval(intervalId); // }); }) // onUnmounted(() => { // // ç»ä»¶å¸è½½æ¶æ¸ é¤å®æ¶å¨ // clearInterval(intervalId); // }); }); </script> <style scoped lang="scss"> :deep(.row-low-stock td) { background-color: #fde2e2; color: #c45656; } :deep(.row-low-stock td) { background-color: #fde2e2; color: #c45656; } :deep(.row-low-stock:hover > td) { background-color: #fcd4d4; } :deep(.row-low-stock:hover > td) { background-color: #fcd4d4; } </style> src/views/salesManagement/salesLedger/index.vue
@@ -1,29 +1,48 @@ <template> <div class="app-container"> <div class="search_form"> <el-form :model="searchForm" :inline="true"> <el-form :model="searchForm" :inline="true"> <el-form-item label="客æ·åç§°ï¼"> <el-input v-model="searchForm.customerName" placeholder="请è¾å ¥" clearable prefix-icon="Search" @change="handleQuery" /> <el-input v-model="searchForm.customerName" placeholder="请è¾å ¥" clearable prefix-icon="Search" @change="handleQuery" /> </el-form-item> <el-form-item label="客æ·ååå·ï¼"> <el-input v-model="searchForm.customerContractNo" placeholder="请è¾å ¥" clearable prefix-icon="Search" @change="handleQuery" /> <el-input v-model="searchForm.customerContractNo" placeholder="请è¾å ¥" clearable prefix-icon="Search" @change="handleQuery" /> </el-form-item> <el-form-item label="éå®ååå·ï¼"> <el-input v-model="searchForm.salesContractNo" placeholder="请è¾å ¥" clearable prefix-icon="Search" @change="handleQuery" /> <el-input v-model="searchForm.salesContractNo" placeholder="请è¾å ¥" clearable prefix-icon="Search" @change="handleQuery" /> </el-form-item> <el-form-item label="项ç®åç§°ï¼"> <el-input v-model="searchForm.projectName" placeholder="请è¾å ¥" clearable prefix-icon="Search" @change="handleQuery" /> <el-input v-model="searchForm.projectName" placeholder="请è¾å ¥" clearable prefix-icon="Search" @change="handleQuery" /> </el-form-item> <el-form-item label="å½å ¥æ¥æï¼"> <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange" placeholder="è¯·éæ©" clearable @change="changeDaterange" /> <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange" placeholder="è¯·éæ©" clearable @change="changeDaterange" /> </el-form-item> <el-form-item> <el-button type="primary" @click="handleQuery"> æç´¢ </el-button> <el-button type="primary" @click="handleQuery"> æç´¢ </el-button> </el-form-item> </el-form> </div> @@ -31,125 +50,225 @@ <div class="actions"> <div></div> <div> <el-button type="primary" @click="openForm('add')"> <el-button type="primary" @click="openForm('add')"> æ°å¢å°è´¦ </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> <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange" :expand-row-keys="expandedRowKeys" :row-key="(row) => row.id" show-summary style="width: 100%" :summary-method="summarizeMainTable" @expand-change="expandChange" height="calc(100vh - 18.5em)"> <el-table-column align="center" type="selection" width="55" /> <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange" :expand-row-keys="expandedRowKeys" :row-key="(row) => row.id" show-summary style="width: 100%" :summary-method="summarizeMainTable" @expand-change="expandChange" height="calc(100vh - 18.5em)"> <el-table-column align="center" type="selection" width="55" /> <el-table-column type="expand"> <template #default="props"> <el-table :data="props.row.children" border show-summary :summary-method="summarizeChildrenTable" style="min-width: 1200px;" <el-table :data="props.row.children" border show-summary :summary-method="summarizeChildrenTable" style="min-width: 1200px;" scrollbar-always-on> <el-table-column align="center" label="åºå·" type="index" width="70" /> <el-table-column label="产å大类" prop="productCategory" width="160" /> <el-table-column label="è§æ ¼åå·" prop="specificationModel" width="220" /> <el-table-column label="åä½" prop="unit" width="100" /> <el-table-column label="æ°é" prop="quantity" width="120" /> <el-table-column label="ç¨ç(%)" prop="taxRate" width="120" /> <el-table-column label="å«ç¨åä»·(å )" prop="taxInclusiveUnitPrice" :formatter="formattedNumber" width="160" /> <el-table-column label="å«ç¨æ»ä»·(å )" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" width="180" /> <el-table-column label="ä¸å«ç¨æ»ä»·(å )" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" width="180" /> <!-- <el-table-column fixed="right" label="æä½" width="150" align="center">--> <!-- <template #default="scope">--> <!-- <el-button link type="primary" size="small" @click="openDeliveryForm(props.row, scope.row)">åè´§</el-button>--> <!-- </template>--> <!-- </el-table-column>--> <el-table-column align="center" label="åºå·" type="index" width="70" /> <el-table-column label="产å大类" prop="productCategory" /> <el-table-column label="è§æ ¼åå·" prop="specificationModel" /> <el-table-column label="åä½" prop="unit" /> <el-table-column label="æ°é" prop="quantity" /> <el-table-column label="ç¨ç(%)" prop="taxRate" /> <el-table-column label="å«ç¨åä»·(å )" prop="taxInclusiveUnitPrice" :formatter="formattedNumber" /> <el-table-column label="å«ç¨æ»ä»·(å )" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" /> <el-table-column label="ä¸å«ç¨æ»ä»·(å )" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" /> <el-table-column fixed="right" label="æä½" width="150" align="center"> <template #default="scope"> <el-button link type="primary" size="small" @click="openDeliveryForm(props.row, scope.row)">åè´§</el-button> </template> </el-table-column> </el-table> </template> </el-table-column> <el-table-column align="center" label="åºå·" type="index" width="60" /> <el-table-column label="éå®ååå·" prop="salesContractNo" width="180" show-overflow-tooltip /> <el-table-column label="客æ·ååå·" prop="customerContractNo" width="180" show-overflow-tooltip /> <el-table-column label="客æ·åç§°" prop="customerName" width="300" show-overflow-tooltip /> <el-table-column label="ä¸å¡å" prop="salesman" width="100" show-overflow-tooltip /> <el-table-column label="项ç®åç§°" prop="projectName" width="180" show-overflow-tooltip /> <el-table-column label="å®¡æ ¸ç¶æ" width="140"> <el-table-column align="center" label="åºå·" type="index" width="60" /> <el-table-column label="éå®ååå·" prop="salesContractNo" width="180" show-overflow-tooltip /> <el-table-column label="客æ·ååå·" prop="customerContractNo" width="180" show-overflow-tooltip /> <el-table-column label="客æ·åç§°" prop="customerName" width="300" show-overflow-tooltip /> <el-table-column label="ä¸å¡å" prop="salesman" width="100" show-overflow-tooltip /> <el-table-column label="项ç®åç§°" prop="projectName" width="180" show-overflow-tooltip /> <el-table-column label="å®¡æ ¸ç¶æ" width="140"> <template #default="scope"> <el-tag v-if="(scope.row.approveStatus ?? scope.row.approvalStatus) == 0" type="warning" >å¾ å®¡æ ¸</el-tag> <el-tag v-else-if="(scope.row.approveStatus ?? scope.row.approvalStatus) == 1" type="primary" >å®¡æ ¸ä¸</el-tag> <el-tag v-else-if="(scope.row.approveStatus ?? scope.row.approvalStatus) == 2" type="success" >å®¡æ ¸å®æ</el-tag> <el-tag v-else-if="(scope.row.approveStatus ?? scope.row.approvalStatus) == 3" type="danger" >å®¡æ ¸æªéè¿</el-tag> <el-tag v-else-if="(scope.row.approveStatus ?? scope.row.approvalStatus) == 4" type="info" >已鿰æäº¤</el-tag> <el-tag v-else type="info">-</el-tag> <el-tag v-if="(scope.row.approveStatus ?? scope.row.approvalStatus) == 0" type="warning">å¾ å®¡æ ¸</el-tag> <el-tag v-else-if="(scope.row.approveStatus ?? scope.row.approvalStatus) == 1" type="primary">å®¡æ ¸ä¸</el-tag> <el-tag v-else-if="(scope.row.approveStatus ?? scope.row.approvalStatus) == 2" type="success">å®¡æ ¸å®æ</el-tag> <el-tag v-else-if="(scope.row.approveStatus ?? scope.row.approvalStatus) == 3" type="danger">å®¡æ ¸æªéè¿</el-tag> <el-tag v-else-if="(scope.row.approveStatus ?? scope.row.approvalStatus) == 4" type="info">已鿰æäº¤</el-tag> <el-tag v-else type="info">-</el-tag> </template> </el-table-column> <el-table-column label="éå®ç±»å" width="120"> <el-table-column label="éå®ç±»å" width="120"> <template #default="scope"> <el-tag :type="scope.row.salesType === 'ç´§æ¥' ? 'danger' : 'info'" >{{ scope.row.salesType || '-' }}</el-tag> <el-tag :type="scope.row.salesType === 'ç´§æ¥' ? 'danger' : 'info'">{{ scope.row.salesType || '-' }}</el-tag> </template> </el-table-column> <el-table-column label="仿¬¾æ¹å¼" prop="paymentMethod" show-overflow-tooltip /> <el-table-column label="ååéé¢(å )" prop="contractAmount" width="220" show-overflow-tooltip :formatter="formattedNumber" /> <el-table-column label="å½å ¥äºº" prop="entryPersonName" width="100" show-overflow-tooltip /> <el-table-column label="å½å ¥æ¥æ" prop="entryDate" width="120" show-overflow-tooltip /> <el-table-column label="ç¾è®¢æ¥æ" prop="executionDate" width="120" show-overflow-tooltip /> <el-table-column fixed="right" label="æä½" min-width="150" align="center"> <el-table-column label="仿¬¾æ¹å¼" prop="paymentMethod" show-overflow-tooltip /> <el-table-column label="ååéé¢(å )" prop="contractAmount" width="220" show-overflow-tooltip :formatter="formattedNumber" /> <el-table-column label="å½å ¥äºº" prop="entryPersonName" width="100" show-overflow-tooltip /> <el-table-column label="å½å ¥æ¥æ" prop="entryDate" width="120" show-overflow-tooltip /> <el-table-column label="ç¾è®¢æ¥æ" prop="executionDate" width="120" show-overflow-tooltip /> <el-table-column fixed="right" label="æä½" min-width="150" align="center"> <template #default="scope"> <el-button link type="primary" size="small" @click="openForm('edit', scope.row)">ç¼è¾</el-button> <!-- <el-button link type="primary" size="small" @click="openForm('view', scope.row)">详æ </el-button>--> <el-button link type="primary" size="small" @click="downLoadFile(scope.row)">éä»¶</el-button> <el-button link type="primary" size="small" @click="openDeliveryForm(scope.row)">åè´§</el-button> <el-button link type="primary" size="small" @click="openForm('edit', scope.row)">ç¼è¾</el-button> <!-- <el-button link type="primary" size="small" @click="openForm('view', scope.row)">详æ </el-button>--> <el-button link type="primary" size="small" @click="downLoadFile(scope.row)">éä»¶</el-button> <!-- <el-button link type="primary" size="small" @click="openDeliveryForm(scope.row)">åè´§</el-button> --> </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> <el-dialog v-model="dialogFormVisible" :title="operationType === 'add' ? 'æ°å¢éå®å°è´¦é¡µé¢' : 'ç¼è¾éå®å°è´¦é¡µé¢'" width="70%" @close="closeDia"> <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef"> <el-dialog v-model="dialogFormVisible" :title="operationType === 'add' ? 'æ°å¢éå®å°è´¦é¡µé¢' : 'ç¼è¾éå®å°è´¦é¡µé¢'" width="70%" @close="closeDia"> <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="salesContractNo"> <el-input v-model="form.salesContractNo" placeholder="èªå¨çæ" clearable disabled /> <el-form-item label="éå®ååå·ï¼" prop="salesContractNo"> <el-input v-model="form.salesContractNo" placeholder="èªå¨çæ" clearable disabled /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="ä¸å¡åï¼" prop="salesman"> <el-select v-model="form.salesman" placeholder="è¯·éæ©" clearable :disabled="operationType === 'view'"> <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName" :value="item.nickName" /> <el-form-item label="ä¸å¡åï¼" prop="salesman"> <el-select v-model="form.salesman" placeholder="è¯·éæ©" clearable :disabled="operationType === 'view'"> <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName" :value="item.nickName" /> </el-select> </el-form-item> </el-col> </el-row> <el-row :gutter="30"> <el-col :span="12"> <el-form-item label="客æ·ååå·ï¼" prop="customerContractNo"> <el-input v-model="form.customerContractNo" placeholder="请è¾å ¥" clearable :disabled="operationType === 'view'"/> <el-form-item label="客æ·ååå·ï¼" prop="customerContractNo"> <el-input v-model="form.customerContractNo" placeholder="请è¾å ¥" clearable :disabled="operationType === 'view'" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="客æ·åç§°ï¼" prop="customerId"> <el-select v-model="form.customerId" placeholder="è¯·éæ©" clearable :disabled="operationType === 'view'"> <el-option v-for="item in customerOption" :key="item.id" :label="item.customerName" :value="item.id"> <el-form-item label="客æ·åç§°ï¼" prop="customerId"> <el-select v-model="form.customerId" placeholder="è¯·éæ©" clearable :disabled="operationType === 'view'"> <el-option v-for="item in customerOption" :key="item.id" :label="item.customerName" :value="item.id"> {{ item.customerName + "ââ" + item.taxpayerIdentificationNumber }} @@ -160,49 +279,78 @@ </el-row> <el-row :gutter="30"> <el-col :span="12"> <el-form-item label="项ç®åç§°ï¼" prop="projectName"> <el-input v-model="form.projectName" placeholder="请è¾å ¥" clearable :disabled="operationType === 'view'" /> <el-form-item label="项ç®åç§°ï¼" prop="projectName"> <el-input v-model="form.projectName" placeholder="请è¾å ¥" clearable :disabled="operationType === 'view'" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="ç¾è®¢æ¥æï¼" prop="executionDate"> <el-date-picker style="width: 100%" v-model="form.executionDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="date" placeholder="è¯·éæ©" clearable :disabled="operationType === 'view'" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="ç¾è®¢æ¥æï¼" prop="executionDate"> <el-date-picker style="width: 100%" v-model="form.executionDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="date" placeholder="è¯·éæ©" clearable :disabled="operationType === 'view'" /> </el-form-item> </el-col> </el-row> <el-row :gutter="30"> <el-col :span="12"> <el-form-item label="å½å ¥äººï¼" prop="entryPerson"> <el-select v-model="form.entryPerson" placeholder="è¯·éæ©" clearable @change="changs" disabled> <el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.userId" /> </el-select> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="å½å ¥æ¥æï¼" prop="entryDate"> <el-date-picker style="width: 100%" v-model="form.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="date" placeholder="è¯·éæ©" clearable /> <el-form-item label="å½å ¥äººï¼" prop="entryPerson"> <el-select v-model="form.entryPerson" placeholder="è¯·éæ©" clearable @change="changs" disabled> <el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.userId" /> </el-select> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="å½å ¥æ¥æï¼" prop="entryDate"> <el-date-picker style="width: 100%" v-model="form.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="date" placeholder="è¯·éæ©" clearable /> </el-form-item> </el-col> </el-row> <el-row :gutter="30"> <el-col :span="12"> <el-form-item label="仿¬¾æ¹å¼"> <el-input v-model="form.paymentMethod" placeholder="请è¾å ¥" clearable :disabled="operationType === 'view'" /> <el-input v-model="form.paymentMethod" placeholder="请è¾å ¥" clearable :disabled="operationType === 'view'" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="éå®ç±»åï¼" prop="salesType"> <el-select v-model="form.salesType" placeholder="è¯·éæ©" clearable :disabled="operationType === 'view'" style="width: 100%" > <el-option label="æ®é" value="æ®é" /> <el-option label="ç´§æ¥" value="ç´§æ¥" /> <el-form-item label="éå®ç±»åï¼" prop="salesType"> <el-select v-model="form.salesType" placeholder="è¯·éæ©" clearable :disabled="operationType === 'view'" style="width: 100%"> <el-option label="æ®é" value="æ®é" /> <el-option label="ç´§æ¥" value="ç´§æ¥" /> </el-select> </el-form-item> </el-col> @@ -212,37 +360,31 @@ <el-form-item v-if="operationType !== 'view'"> <template #label> <span>审æ¹äººéæ©ï¼</span> <el-button type="primary" @click="addApproverNode" style="margin-left: 8px;">æ°å¢èç¹</el-button> <el-button type="primary" @click="addApproverNode" style="margin-left: 8px;">æ°å¢èç¹</el-button> </template> <div style="display: flex; align-items: flex-end; flex-wrap: wrap;"> <div v-for="(node, index) in approverNodes" :key="node.id" style="margin-right: 30px; text-align: center; margin-bottom: 10px;" > <div v-for="(node, index) in approverNodes" :key="node.id" style="margin-right: 30px; text-align: center; margin-bottom: 10px;"> <div> <span>审æ¹äºº</span> â </div> <el-select v-model="node.userId" placeholder="éæ©äººå" style="width: 140px; margin-bottom: 8px;" > <el-option v-for="user in userList" :key="user.userId" :label="user.nickName" :value="user.userId" /> <el-select v-model="node.userId" placeholder="éæ©äººå" style="width: 140px; margin-bottom: 8px;"> <el-option v-for="user in userList" :key="user.userId" :label="user.nickName" :value="user.userId" /> </el-select> <div> <el-button type="danger" size="small" @click="removeApproverNode(index)" v-if="approverNodes.length > 1" >å é¤</el-button> <el-button type="danger" size="small" @click="removeApproverNode(index)" v-if="approverNodes.length > 1">å é¤</el-button> </div> </div> </div> @@ -250,44 +392,93 @@ </el-col> </el-row> <el-row> <el-form-item label="产åä¿¡æ¯ï¼" prop="entryDate"> <el-button v-if="operationType !== 'view'" type="primary" @click="openProductForm('add')">æ·»å </el-button> <el-button v-if="operationType !== 'view'" plain type="danger" @click="deleteProduct" >å é¤</el-button> <el-form-item label="产åä¿¡æ¯ï¼" prop="entryDate"> <el-button v-if="operationType !== 'view'" type="primary" @click="openProductForm('add')">æ·»å </el-button> <el-button v-if="operationType !== 'view'" plain type="danger" @click="deleteProduct">å é¤</el-button> </el-form-item> </el-row> <el-table :data="productData" border @selection-change="productSelected" show-summary :summary-method="summarizeMainTable"> <el-table-column align="center" type="selection" width="55" v-if="operationType !== 'view'" /> <el-table-column align="center" label="åºå·" type="index" width="60" /> <el-table-column label="产å大类" prop="productCategory" /> <el-table-column label="è§æ ¼åå·" prop="specificationModel" /> <el-table-column label="åä½" prop="unit" /> <el-table-column label="æ°é" prop="quantity" /> <el-table-column label="ç¨ç(%)" prop="taxRate" /> <el-table-column label="å«ç¨åä»·(å )" prop="taxInclusiveUnitPrice" :formatter="formattedNumber" /> <el-table-column label="å«ç¨æ»ä»·(å )" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" /> <el-table-column label="ä¸å«ç¨æ»ä»·(å )" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" /> <el-table-column fixed="right" label="æä½" min-width="60" align="center" v-if="operationType !== 'view'"> <el-table :data="productData" border @selection-change="productSelected" show-summary :summary-method="summarizeMainTable"> <el-table-column align="center" type="selection" width="55" v-if="operationType !== 'view'" /> <el-table-column align="center" label="åºå·" type="index" width="60" /> <el-table-column label="产å大类" prop="productCategory" /> <el-table-column label="è§æ ¼åå·" prop="specificationModel" /> <el-table-column label="åä½" prop="unit" /> <el-table-column label="æ°é" prop="quantity" /> <el-table-column label="ç¨ç(%)" prop="taxRate" /> <el-table-column label="å«ç¨åä»·(å )" prop="taxInclusiveUnitPrice" :formatter="formattedNumber" /> <el-table-column label="å«ç¨æ»ä»·(å )" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" /> <el-table-column label="ä¸å«ç¨æ»ä»·(å )" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" /> <el-table-column fixed="right" label="æä½" min-width="60" align="center" v-if="operationType !== 'view'"> <template #default="scope"> <el-button link type="primary" size="small" @click="openProductForm('edit', scope.row,scope.$index)">ç¼è¾</el-button> <el-button link type="primary" size="small" @click="openProductForm('edit', scope.row,scope.$index)">ç¼è¾</el-button> </template> </el-table-column> </el-table> <el-row :gutter="30"> <el-col :span="24"> <el-form-item label="夿³¨Â·ï¼" prop="remark"> <el-input v-model="form.remark" placeholder="请è¾å ¥" clearable type="textarea" :rows="2" :disabled="operationType === 'view'" /> <el-form-item label="夿³¨Â·ï¼" prop="remark"> <el-input v-model="form.remark" placeholder="请è¾å ¥" clearable type="textarea" :rows="2" :disabled="operationType === 'view'" /> </el-form-item> </el-col> </el-row> <el-row :gutter="30"> <el-col :span="24"> <el-form-item label="éä»¶ææï¼" prop="remark"> <el-upload v-model:file-list="fileList" :action="upload.url" multiple ref="fileUpload" auto-upload :headers="upload.headers" :before-upload="handleBeforeUpload" :on-error="handleUploadError" :on-success="handleUploadSuccess" :on-remove="handleRemove"> <el-button type="primary" v-if="operationType !== 'view'">ä¸ä¼ </el-button> <template #tip v-if="operationType !== 'view'"> <el-form-item label="éä»¶ææï¼" prop="remark"> <el-upload v-model:file-list="fileList" :action="upload.url" multiple ref="fileUpload" auto-upload :headers="upload.headers" :before-upload="handleBeforeUpload" :on-error="handleUploadError" :on-success="handleUploadSuccess" :on-remove="handleRemove"> <el-button type="primary" v-if="operationType !== 'view'">ä¸ä¼ </el-button> <template #tip v-if="operationType !== 'view'"> <div class="el-upload__tip"> æä»¶æ ¼å¼æ¯æ docï¼docxï¼xlsï¼xlsxï¼pptï¼pptxï¼pdfï¼txtï¼xmlï¼jpgï¼jpegï¼pngï¼gifï¼bmpï¼rarï¼zipï¼7z @@ -300,84 +491,140 @@ </el-form> <template #footer> <div class="dialog-footer"> <el-button type="primary" @click="submitForm">确认</el-button> <el-button type="primary" @click="submitForm">确认</el-button> <el-button @click="closeDia">åæ¶</el-button> </div> </template> </el-dialog> <el-dialog v-model="productFormVisible" :title="productOperationType === 'add' ? 'æ°å¢äº§å' : 'ç¼è¾äº§å'" width="40%" @close="closeProductDia"> <el-form :model="productForm" label-width="140px" label-position="top" :rules="productRules" ref="productFormRef"> <el-dialog v-model="productFormVisible" :title="productOperationType === 'add' ? 'æ°å¢äº§å' : 'ç¼è¾äº§å'" width="40%" @close="closeProductDia"> <el-form :model="productForm" label-width="140px" label-position="top" :rules="productRules" ref="productFormRef"> <el-row :gutter="30"> <el-col :span="24"> <el-form-item label="产å大类ï¼" prop="productCategory"> <el-form-item label="产å大类ï¼" prop="productCategory"> <!-- <el-select v-model="productForm.productCategory" placeholder="è¯·éæ©" clearable> <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName" :value="item.nickName"/> </el-select> --> <el-tree-select v-model="productForm.productCategory" placeholder="è¯·éæ©" clearable check-strictly @change="getModels" :data="productOptions" :render-after-expand="false" style="width: 100%" /> <el-tree-select v-model="productForm.productCategory" placeholder="è¯·éæ©" clearable check-strictly @change="getModels" :data="productOptions" :render-after-expand="false" style="width: 100%" /> </el-form-item> </el-col> </el-row> <el-row :gutter="30"> <el-col :span="24"> <el-form-item label="è§æ ¼åå·ï¼" prop="productModelId"> <el-select v-model="productForm.productModelId" placeholder="è¯·éæ©" clearable @change="getProductModel"> <el-option v-for="item in modelOptions" :key="item.id" :label="item.model" :value="item.id" /> <el-form-item label="è§æ ¼åå·ï¼" prop="productModelId"> <el-select v-model="productForm.productModelId" placeholder="è¯·éæ©" clearable @change="getProductModel"> <el-option v-for="item in modelOptions" :key="item.id" :label="item.model" :value="item.id" /> </el-select> </el-form-item> </el-col> </el-row> <el-row :gutter="30"> <el-col :span="12"> <el-form-item label="åä½ï¼" prop="unit"> <el-input v-model="productForm.unit" placeholder="请è¾å ¥" clearable /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="ç¨ç(%)ï¼" prop="taxRate"> <el-select v-model="productForm.taxRate" placeholder="è¯·éæ©" clearable @change="calculateFromTaxRate"> <el-option label="1" value="1" /> <el-option label="6" value="6" /> <el-option label="13" value="13" /> </el-select> </el-form-item> </el-col> </el-row> <el-row :gutter="30"> <el-col :span="12"> <el-form-item label="å«ç¨åä»·(å )ï¼" prop="taxInclusiveUnitPrice"> <el-input-number :step="0.01" :min="0" v-model="productForm.taxInclusiveUnitPrice" style="width: 100%" :precision="2" placeholder="请è¾å ¥" clearable @change="calculateFromUnitPrice" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="æ°éï¼" prop="quantity"> <el-input-number :step="0.1" :min="0" v-model="productForm.quantity" placeholder="请è¾å ¥" clearable :precision="2" @change="calculateFromQuantity" style="width: 100%" /> </el-form-item> </el-col> </el-row> <el-row :gutter="30"> <el-col :span="12"> <el-form-item label="å«ç¨æ»ä»·(å )ï¼" prop="taxInclusiveTotalPrice"> <el-input v-model="productForm.taxInclusiveTotalPrice" placeholder="请è¾å ¥" clearable @change="calculateFromTotalPrice" /> <el-form-item label="åä½ï¼" prop="unit"> <el-input v-model="productForm.unit" placeholder="请è¾å ¥" clearable /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="ä¸å«ç¨æ»ä»·(å )ï¼" prop="taxExclusiveTotalPrice"> <el-input v-model="productForm.taxExclusiveTotalPrice" placeholder="请è¾å ¥" clearable @change="calculateFromExclusiveTotalPrice" /> <el-form-item label="ç¨ç(%)ï¼" prop="taxRate"> <el-select v-model="productForm.taxRate" placeholder="è¯·éæ©" clearable @change="calculateFromTaxRate"> <el-option label="1" value="1" /> <el-option label="6" value="6" /> <el-option label="13" value="13" /> </el-select> </el-form-item> </el-col> </el-row> <el-row :gutter="30"> <el-col :span="12"> <el-form-item label="å票类åï¼" prop="invoiceType"> <el-select v-model="productForm.invoiceType" placeholder="è¯·éæ©" clearable> <el-option label="墿®ç¥¨" value="墿®ç¥¨" /> <el-option label="å¢ä¸ç¥¨" value="å¢ä¸ç¥¨" /> <el-form-item label="å«ç¨åä»·(å )ï¼" prop="taxInclusiveUnitPrice"> <el-input-number :step="0.01" :min="0" v-model="productForm.taxInclusiveUnitPrice" style="width: 100%" :precision="2" placeholder="请è¾å ¥" clearable @change="calculateFromUnitPrice" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="æ°éï¼" prop="quantity"> <el-input-number :step="0.1" :min="0" v-model="productForm.quantity" placeholder="请è¾å ¥" clearable :precision="2" @change="calculateFromQuantity" style="width: 100%" /> </el-form-item> </el-col> </el-row> <el-row :gutter="30"> <el-col :span="12"> <el-form-item label="å«ç¨æ»ä»·(å )ï¼" prop="taxInclusiveTotalPrice"> <el-input v-model="productForm.taxInclusiveTotalPrice" placeholder="请è¾å ¥" clearable @change="calculateFromTotalPrice" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="ä¸å«ç¨æ»ä»·(å )ï¼" prop="taxExclusiveTotalPrice"> <el-input v-model="productForm.taxExclusiveTotalPrice" placeholder="请è¾å ¥" clearable @change="calculateFromExclusiveTotalPrice" /> </el-form-item> </el-col> </el-row> <el-row :gutter="30"> <el-col :span="12"> <el-form-item label="å票类åï¼" prop="invoiceType"> <el-select v-model="productForm.invoiceType" placeholder="è¯·éæ©" clearable> <el-option label="墿®ç¥¨" value="墿®ç¥¨" /> <el-option label="å¢ä¸ç¥¨" value="å¢ä¸ç¥¨" /> </el-select> </el-form-item> </el-col> @@ -385,661 +632,719 @@ </el-form> <template #footer> <div class="dialog-footer"> <el-button type="primary" @click="submitProduct">确认</el-button> <el-button type="primary" @click="submitProduct">确认</el-button> <el-button @click="closeProductDia">åæ¶</el-button> </div> </template> </el-dialog> <!-- æå°é¢è§å¼¹çª --> <el-dialog v-model="printPreviewVisible" title="æå°é¢è§" width="90%" :close-on-click-modal="false" class="print-preview-dialog" > <div class="print-preview-container"> <div class="print-preview-header"> <el-button type="primary" @click="executePrint">æ§è¡æå°</el-button> <el-button @click="printPreviewVisible = false">å ³éé¢è§</el-button> </div> <div class="print-preview-content"> <div v-if="printData.length === 0" style="text-align: center; padding: 50px; color: #999;"> ææ æå°æ°æ® </div> <div v-else style="text-align: center; padding: 10px; color: #666; font-size: 14px; background: #e8f4fd; margin-bottom: 10px;"> å ± {{ printData.length }} æ¡æ°æ®å¾ æå° </div> <div v-for="(item, index) in printData" :key="index" class="print-page"> <div class="delivery-note"> <div class="header"> <div class="company-name">é¼è¯çå®ä¸æéè´£ä»»å ¬å¸</div> <div class="document-title">é¶å®åè´§å</div> </div> <div class="info-section"> <div class="info-row"> <div> <span class="label">åè´§æ¥æï¼</span> <span class="value">{{ formatDate(item.createTime) }}</span> </div> <div> <span class="label">客æ·åç§°ï¼</span> <span class="value">{{ item.customerName || 'å¼ ç±æ' }}</span> </div> </div> <div class="info-row"> <span class="label">åå·ï¼</span> <span class="value">{{ item.salesContractNo }}</span> </div> </div> <div class="table-section"> <table class="product-table"> <thead> <tr> <th>产ååç§°</th> <th>è§æ ¼åå·</th> <th>åä½</th> <th>åä»·</th> <th>é¶å®æ°é</th> <th>é¶å®éé¢</th> </tr> </thead> <tbody> <tr v-for="product in item.products" :key="product.id"> <td>{{ product.productCategory || '' }}</td> <td>{{ product.specificationModel || '' }}</td> <td>{{ product.unit || '' }}</td> <td>{{ product.taxInclusiveUnitPrice || '0' }}</td> <td>{{ product.quantity || '0' }}</td> <td>{{ product.taxInclusiveTotalPrice || '0' }}</td> </tr> <tr v-if="!item.products || item.products.length === 0"> <td colspan="6" style="text-align: center; color: #999;">ææ äº§åæ°æ®</td> </tr> </tbody> <tfoot> <tr> <td class="label">å计</td> <td class="total-value"></td> <td class="total-value"></td> <td class="total-value"></td> <td class="total-value">{{ getTotalQuantity(item.products) }}</td> <td class="total-value">{{ getTotalAmount(item.products) }}</td> </tr> </tfoot> </table> </div> <div class="footer-section"> <div class="footer-row"> <div class="footer-item"> <span class="label">æ¶è´§çµè¯ï¼</span> <span class="value"></span> </div> <div class="footer-item"> <span class="label">æ¶è´§äººï¼</span> <span class="value"></span> </div> <div class="footer-item address-item"> <span class="label">æ¶è´§å°åï¼</span> <span class="value address-value"></span> </div> </div> <div class="footer-row"> <div class="footer-item"> <span class="label">æä½åï¼</span> <span class="value">{{ userStore.nickName || 'æå¼å' }}</span> </div> <div class="footer-item"> <span class="label">æå°æ¥æï¼</span> <span class="value">{{ formatDateTime(new Date()) }}</span> </div> </div> </div> </div> </div> </div> </div> </el-dialog> <!-- åè´§å¼¹æ¡ --> <el-dialog v-model="deliveryFormVisible" title="åè´§ä¿¡æ¯" width="40%" @close="closeDeliveryDia" > <el-form :model="deliveryForm" label-width="120px" label-position="top" :rules="deliveryRules" ref="deliveryFormRef"> <el-row :gutter="30"> <el-col :span="24"> <el-form-item label="åè´§æ¥æï¼" prop="shippingDate"> <el-date-picker style="width: 100%" v-model="deliveryForm.shippingDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="date" placeholder="è¯·éæ©åè´§æ¥æ" clearable /> </el-form-item> </el-col> </el-row> <el-row :gutter="30"> <el-col :span="24"> <el-form-item label="å货车çå·ï¼" prop="shippingCarNumber"> <el-input v-model="deliveryForm.shippingCarNumber" placeholder="请è¾å ¥å货车çå·" clearable /> </el-form-item> </el-col> </el-row> </el-form> <template #footer> <div class="dialog-footer"> <el-button type="primary" @click="submitDelivery">确认åè´§</el-button> <el-button @click="closeDeliveryDia">åæ¶</el-button> </div> </template> </el-dialog> <!-- æå°é¢è§å¼¹çª --> <el-dialog v-model="printPreviewVisible" title="æå°é¢è§" width="90%" :close-on-click-modal="false" class="print-preview-dialog"> <div class="print-preview-container"> <div class="print-preview-header"> <el-button type="primary" @click="executePrint">æ§è¡æå°</el-button> <el-button @click="printPreviewVisible = false">å ³éé¢è§</el-button> </div> <div class="print-preview-content"> <div v-if="printData.length === 0" style="text-align: center; padding: 50px; color: #999;"> ææ æå°æ°æ® </div> <div v-else style="text-align: center; padding: 10px; color: #666; font-size: 14px; background: #e8f4fd; margin-bottom: 10px;"> å ± {{ printData.length }} æ¡æ°æ®å¾ æå° </div> <div v-for="(item, index) in printData" :key="index" class="print-page"> <div class="delivery-note"> <div class="header"> <div class="company-name">é¼è¯çå®ä¸æéè´£ä»»å ¬å¸</div> <div class="document-title">é¶å®åè´§å</div> </div> <div class="info-section"> <div class="info-row"> <div> <span class="label">åè´§æ¥æï¼</span> <span class="value">{{ formatDate(item.createTime) }}</span> </div> <div> <span class="label">客æ·åç§°ï¼</span> <span class="value">{{ item.customerName || 'å¼ ç±æ' }}</span> </div> </div> <div class="info-row"> <span class="label">åå·ï¼</span> <span class="value">{{ item.salesContractNo }}</span> </div> </div> <div class="table-section"> <table class="product-table"> <thead> <tr> <th>产ååç§°</th> <th>è§æ ¼åå·</th> <th>åä½</th> <th>åä»·</th> <th>é¶å®æ°é</th> <th>é¶å®éé¢</th> </tr> </thead> <tbody> <tr v-for="product in item.products" :key="product.id"> <td>{{ product.productCategory || '' }}</td> <td>{{ product.specificationModel || '' }}</td> <td>{{ product.unit || '' }}</td> <td>{{ product.taxInclusiveUnitPrice || '0' }}</td> <td>{{ product.quantity || '0' }}</td> <td>{{ product.taxInclusiveTotalPrice || '0' }}</td> </tr> <tr v-if="!item.products || item.products.length === 0"> <td colspan="6" style="text-align: center; color: #999;">ææ äº§åæ°æ®</td> </tr> </tbody> <tfoot> <tr> <td class="label">å计</td> <td class="total-value"></td> <td class="total-value"></td> <td class="total-value"></td> <td class="total-value">{{ getTotalQuantity(item.products) }}</td> <td class="total-value">{{ getTotalAmount(item.products) }}</td> </tr> </tfoot> </table> </div> <div class="footer-section"> <div class="footer-row"> <div class="footer-item"> <span class="label">æ¶è´§çµè¯ï¼</span> <span class="value"></span> </div> <div class="footer-item"> <span class="label">æ¶è´§äººï¼</span> <span class="value"></span> </div> <div class="footer-item address-item"> <span class="label">æ¶è´§å°åï¼</span> <span class="value address-value"></span> </div> </div> <div class="footer-row"> <div class="footer-item"> <span class="label">æä½åï¼</span> <span class="value">{{ userStore.nickName || 'æå¼å' }}</span> </div> <div class="footer-item"> <span class="label">æå°æ¥æï¼</span> <span class="value">{{ formatDateTime(new Date()) }}</span> </div> </div> </div> </div> </div> </div> </div> </el-dialog> <!-- åè´§å¼¹æ¡ --> <el-dialog v-model="deliveryFormVisible" title="åè´§ä¿¡æ¯" width="40%" @close="closeDeliveryDia"> <el-form :model="deliveryForm" label-width="120px" label-position="top" :rules="deliveryRules" ref="deliveryFormRef"> <el-row :gutter="30"> <el-col :span="24"> <el-form-item label="åè´§æ¥æï¼" prop="shippingDate"> <el-date-picker style="width: 100%" v-model="deliveryForm.shippingDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="date" placeholder="è¯·éæ©åè´§æ¥æ" clearable /> </el-form-item> </el-col> </el-row> <el-row :gutter="30"> <el-col :span="24"> <el-form-item label="å货车çå·ï¼" prop="shippingCarNumber"> <el-input v-model="deliveryForm.shippingCarNumber" placeholder="请è¾å ¥å货车çå·" clearable /> </el-form-item> </el-col> </el-row> </el-form> <template #footer> <div class="dialog-footer"> <el-button type="primary" @click="submitDelivery">确认åè´§</el-button> <el-button @click="closeDeliveryDia">åæ¶</el-button> </div> </template> </el-dialog> <FileList ref="fileListRef" /> </div> </template> <script setup> import { getToken } from "@/utils/auth"; import pagination from "@/components/PIMTable/Pagination.vue"; import {onMounted, ref} from "vue"; import { addShippingInfo } from "@/api/salesManagement/deliveryLedger.js"; import {ElMessage, ElMessageBox} from "element-plus"; import useUserStore from "@/store/modules/user"; import { userListNoPage } from "@/api/system/user.js"; import FileList from "./fileList.vue"; import { ledgerListPage, productList, customerList, addOrUpdateSalesLedger, getSalesLedgerWithProducts, delLedger, addOrUpdateSalesLedgerProduct, delProduct, delLedgerFile, getProductInventory, } from "@/api/salesManagement/salesLedger.js"; import { modelList, productTreeList } from "@/api/basicData/product.js"; import useFormData from "@/hooks/useFormData.js"; import dayjs from "dayjs"; import { getStockInPage } from "@/api/inventoryManagement/stockIn.js"; import { getToken } from "@/utils/auth"; import pagination from "@/components/PIMTable/Pagination.vue"; import { onMounted, ref } from "vue"; import { addShippingInfo } from "@/api/salesManagement/deliveryLedger.js"; import { ElMessage, ElMessageBox } from "element-plus"; import useUserStore from "@/store/modules/user"; import { userListNoPage } from "@/api/system/user.js"; import FileList from "./fileList.vue"; import { ledgerListPage, productList, customerList, addOrUpdateSalesLedger, getSalesLedgerWithProducts, delLedger, addOrUpdateSalesLedgerProduct, delProduct, delLedgerFile, getProductInventory, } from "@/api/salesManagement/salesLedger.js"; import { modelList, productTreeList } from "@/api/basicData/product.js"; import useFormData from "@/hooks/useFormData.js"; import dayjs from "dayjs"; import { getStockInPage } from "@/api/inventoryManagement/stockIn.js"; const userStore = useUserStore(); const { proxy } = getCurrentInstance(); const tableData = ref([]); const productData = ref([]); const selectedRows = ref([]); const productSelectedRows = ref([]); const userList = ref([]); const customerOption = ref([]); const productOptions = ref([]); const modelOptions = ref([]); const tableLoading = ref(false); const page = reactive({ current: 1, size: 100, }); const total = ref(0); const fileList = ref([]); const approverNodes = ref([{ id: 1, userId: null }]); let nextApproverId = 2; const userStore = useUserStore(); const { proxy } = getCurrentInstance(); const tableData = ref([]); const productData = ref([]); const selectedRows = ref([]); const productSelectedRows = ref([]); const userList = ref([]); const customerOption = ref([]); const productOptions = ref([]); const modelOptions = ref([]); const tableLoading = ref(false); const page = reactive({ current: 1, size: 100, }); const total = ref(0); const fileList = ref([]); const approverNodes = ref([{ id: 1, userId: null }]); let nextApproverId = 2; // ç¨æ·ä¿¡æ¯è¡¨åå¼¹æ¡æ°æ® const operationType = ref(""); const dialogFormVisible = ref(false); const data = reactive({ searchForm: { customerName: "", // 客æ·åç§° customerContractNo: "", // 客æ·ååç¼å· salesContractNo: "", // éå®ååç¼å· projectName: "", // 项ç®åç§° entryDate: null, // å½å ¥æ¥æ entryDateStart: undefined, entryDateEnd: undefined, }, form: { salesContractNo: "", salesman: "", customerContractNo: "", customerId: "", projectName: "", entryPerson: "", entryDate: "", maintenanceTime: "", productData: [], executionDate: "", paymentMethod: "", salesType: "æ®é", }, rules: { salesType: [ { required: true, message: "è¯·éæ©éå®ç±»å", trigger: "change" }, ], salesman: [{ required: true, message: "è¯·éæ©", trigger: "change" }], customerContractNo: [ { required: true, message: "请è¾å ¥", trigger: "blur" }, ], customerId: [{ required: true, message: "è¯·éæ©", trigger: "change" }], projectName: [{ required: true, message: "请è¾å ¥", trigger: "blur" }], entryPerson: [{ required: true, message: "è¯·éæ©", trigger: "change" }], entryDate: [{ required: true, message: "è¯·éæ©", trigger: "change" }], executionDate: [{ required: true, message: "è¯·éæ©", trigger: "change" }], }, }); const { form, rules } = toRefs(data); const { form: searchForm } = useFormData(data.searchForm); const addApproverNode = () => { approverNodes.value.push({ id: nextApproverId++, userId: null }); }; const removeApproverNode = index => { approverNodes.value.splice(index, 1); }; // 产å表åå¼¹æ¡æ°æ® const productFormVisible = ref(false); const productOperationType = ref(""); const currentId = ref(""); const productFormData = reactive({ productForm: { productCategory: "", specificationModel: "", unit: "", quantity: "", taxInclusiveUnitPrice: "", taxRate: "", taxInclusiveTotalPrice: "", taxExclusiveTotalPrice: "", invoiceType: "", }, productRules: { productCategory: [{ required: true, message: "è¯·éæ©", trigger: "change" }], productModelId: [{ required: true, message: "è¯·éæ©", trigger: "change" }], specificationModel: [ { required: true, message: "è¯·éæ©", trigger: "change" }, ], unit: [{ required: true, message: "请è¾å ¥", trigger: "blur" }], quantity: [{ required: true, message: "请è¾å ¥", trigger: "blur" }], taxInclusiveUnitPrice: [ { required: true, message: "请è¾å ¥", trigger: "blur" }, ], taxRate: [{ required: true, message: "è¯·éæ©", trigger: "change" }], taxInclusiveTotalPrice: [ { required: true, message: "请è¾å ¥", trigger: "blur" }, ], taxExclusiveTotalPrice: [ { required: true, message: "请è¾å ¥", trigger: "blur" }, ], invoiceType: [{ required: true, message: "è¯·éæ©", trigger: "change" }], }, }); const { productForm, productRules } = toRefs(productFormData); // 鲿¢å¾ªç¯è®¡ç®çæ å¿ const isCalculating = ref(false); const upload = reactive({ // ä¸ä¼ çå°å url: import.meta.env.VITE_APP_BASE_API + "/file/upload", // 设置ä¸ä¼ ç请æ±å¤´é¨ headers: { Authorization: "Bearer " + getToken() }, }); // æå°ç¸å ³ const printPreviewVisible = ref(false); const printData = ref([]); // ç¨æ·ä¿¡æ¯è¡¨åå¼¹æ¡æ°æ® const operationType = ref(""); const dialogFormVisible = ref(false); const data = reactive({ searchForm: { customerName: "", // 客æ·åç§° customerContractNo: "", // 客æ·ååç¼å· salesContractNo: "", // éå®ååç¼å· projectName: "", // 项ç®åç§° entryDate: null, // å½å ¥æ¥æ entryDateStart: undefined, entryDateEnd: undefined, }, form: { salesContractNo: "", salesman: "", customerContractNo: "", customerId: "", projectName: "", entryPerson: "", entryDate: "", maintenanceTime: "", productData: [], executionDate: "", paymentMethod: "", salesType: "æ®é", }, rules: { salesType: [{ required: true, message: "è¯·éæ©éå®ç±»å", trigger: "change" }], salesman: [{ required: true, message: "è¯·éæ©", trigger: "change" }], customerContractNo: [ { required: true, message: "请è¾å ¥", trigger: "blur" }, ], customerId: [{ required: true, message: "è¯·éæ©", trigger: "change" }], projectName: [{ required: true, message: "请è¾å ¥", trigger: "blur" }], entryPerson: [{ required: true, message: "è¯·éæ©", trigger: "change" }], entryDate: [{ required: true, message: "è¯·éæ©", trigger: "change" }], executionDate: [{ required: true, message: "è¯·éæ©", trigger: "change" }], }, }); const { form, rules } = toRefs(data); const { form: searchForm } = useFormData(data.searchForm); const addApproverNode = () => { approverNodes.value.push({ id: nextApproverId++, userId: null }); }; const removeApproverNode = (index) => { approverNodes.value.splice(index, 1); }; // 产å表åå¼¹æ¡æ°æ® const productFormVisible = ref(false); const productOperationType = ref(""); const currentId = ref(""); const productFormData = reactive({ productForm: { productCategory: "", specificationModel: "", unit: "", quantity: "", taxInclusiveUnitPrice: "", taxRate: "", taxInclusiveTotalPrice: "", taxExclusiveTotalPrice: "", invoiceType: "", }, productRules: { productCategory: [{ required: true, message: "è¯·éæ©", trigger: "change" }], productModelId: [{ required: true, message: "è¯·éæ©", trigger: "change" }], specificationModel: [ { required: true, message: "è¯·éæ©", trigger: "change" }, ], unit: [{ required: true, message: "请è¾å ¥", trigger: "blur" }], quantity: [{ required: true, message: "请è¾å ¥", trigger: "blur" }], taxInclusiveUnitPrice: [ { required: true, message: "请è¾å ¥", trigger: "blur" }, ], taxRate: [{ required: true, message: "è¯·éæ©", trigger: "change" }], taxInclusiveTotalPrice: [ { required: true, message: "请è¾å ¥", trigger: "blur" }, ], taxExclusiveTotalPrice: [ { required: true, message: "请è¾å ¥", trigger: "blur" }, ], invoiceType: [{ required: true, message: "è¯·éæ©", trigger: "change" }], }, }); const { productForm, productRules } = toRefs(productFormData); // 鲿¢å¾ªç¯è®¡ç®çæ å¿ const isCalculating = ref(false); const upload = reactive({ // ä¸ä¼ çå°å url: import.meta.env.VITE_APP_BASE_API + "/file/upload", // 设置ä¸ä¼ ç请æ±å¤´é¨ headers: { Authorization: "Bearer " + getToken() }, }); // æå°ç¸å ³ const printPreviewVisible = ref(false); const printData = ref([]); // åè´§ç¸å ³ const deliveryFormVisible = ref(false); const currentDeliveryContext = ref(null); const deliveryFormData = reactive({ deliveryForm: { shippingDate: "", shippingCarNumber: "", }, deliveryRules: { shippingDate: [ { required: true, message: "è¯·éæ©åè´§æ¥æ", trigger: "change" }, ], shippingCarNumber: [ { required: true, message: "请è¾å ¥å货车çå·", trigger: "blur" }, ], }, }); const { deliveryForm, deliveryRules } = toRefs(deliveryFormData); // åè´§ç¸å ³ const deliveryFormVisible = ref(false); const currentDeliveryContext = ref(null); const deliveryFormData = reactive({ deliveryForm: { shippingDate: "", shippingCarNumber: "", }, deliveryRules: { shippingDate: [ { required: true, message: "è¯·éæ©åè´§æ¥æ", trigger: "change" } ], shippingCarNumber: [ { required: true, message: "请è¾å ¥å货车çå·", trigger: "blur" } ], }, }); const { deliveryForm, deliveryRules } = toRefs(deliveryFormData); const changeDaterange = value => { if (value) { searchForm.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD"); searchForm.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD"); } else { searchForm.entryDateStart = undefined; searchForm.entryDateEnd = undefined; } handleQuery(); }; const changeDaterange = (value) => { if (value) { searchForm.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD"); searchForm.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD"); } else { searchForm.entryDateStart = undefined; searchForm.entryDateEnd = undefined; } handleQuery(); }; // æ¥è¯¢å表 /** æç´¢æé®æä½ */ const handleQuery = () => { page.current = 1; expandedRowKeys.value = []; getList(); }; const paginationChange = (obj) => { page.current = obj.page; page.size = obj.limit; getList(); }; const getList = () => { tableLoading.value = true; const { entryDate, ...rest } = searchForm; ledgerListPage({ ...rest, ...page }) .then((res) => { tableLoading.value = false; tableData.value = res.records; tableData.value.map((item) => { item.children = []; // æ¥è¯¢å表 /** æç´¢æé®æä½ */ const handleQuery = () => { page.current = 1; expandedRowKeys.value = []; getList(); }; const paginationChange = obj => { page.current = obj.page; page.size = obj.limit; getList(); }; const getList = () => { tableLoading.value = true; const { entryDate, ...rest } = searchForm; ledgerListPage({ ...rest, ...page }) .then(res => { tableLoading.value = false; tableData.value = res.records; tableData.value.map(item => { item.children = []; }); total.value = res.total; }) .catch(() => { tableLoading.value = false; }); total.value = res.total; }) .catch(() => { tableLoading.value = false; }; // è·å产å大类treeæ°æ® const getProductOptions = () => { productTreeList().then(res => { productOptions.value = convertIdToValue(res); }); }; // è·å产å大类treeæ°æ® const getProductOptions = () => { productTreeList().then((res) => { productOptions.value = convertIdToValue(res); }); }; const formattedNumber = (row, column, cellValue) => { return parseFloat(cellValue).toFixed(2); }; // è·åtreeåæ°æ® const getModels = (value) => { productForm.value.productCategory = findNodeById(productOptions.value, value); modelList({ id: value }).then((res) => { modelOptions.value = res; }); }; const getProductModel = (value) => { console.log("value", value); const index = modelOptions.value.findIndex((item) => item.id === value); if (index !== -1) { productForm.value.specificationModel = modelOptions.value[index].model; productForm.value.unit = modelOptions.value[index].unit; } else { productForm.value.specificationModel = null; productForm.value.unit = null; } }; const findNodeById = (nodes, productId) => { for (let i = 0; i < nodes.length; i++) { if (nodes[i].value === productId) { return nodes[i].label; // æ¾å°èç¹ï¼è¿å该èç¹ }; const formattedNumber = (row, column, cellValue) => { return parseFloat(cellValue).toFixed(2); }; // è·åtreeåæ°æ® const getModels = value => { productForm.value.productCategory = findNodeById(productOptions.value, value); modelList({ id: value }).then(res => { modelOptions.value = res; }); }; const getProductModel = value => { console.log("value", value); const index = modelOptions.value.findIndex(item => item.id === value); if (index !== -1) { productForm.value.specificationModel = modelOptions.value[index].model; productForm.value.unit = modelOptions.value[index].unit; } else { productForm.value.specificationModel = null; productForm.value.unit = null; } if (nodes[i].children && nodes[i].children.length > 0) { const foundNode = findNodeById(nodes[i].children, productId); if (foundNode) { return foundNode; // å¨åèç¹ä¸æ¾å°ï¼è¿å该èç¹ }; const findNodeById = (nodes, productId) => { for (let i = 0; i < nodes.length; i++) { if (nodes[i].value === productId) { return nodes[i].label; // æ¾å°èç¹ï¼è¿å该èç¹ } } } return null; // æ²¡ææ¾å°èç¹ï¼è¿ånull }; function convertIdToValue(data) { return data.map((item) => { const { id, children, ...rest } = item; const newItem = { ...rest, value: id, // å° id æ¹ä¸º value }; if (children && children.length > 0) { newItem.children = convertIdToValue(children); } return newItem; }); } // è¡¨æ ¼éæ©æ°æ® const handleSelectionChange = (selection) => { // è¿æ»¤æåæ°æ® selectedRows.value = selection.filter((item) => item.children !== undefined); console.log("selection", selectedRows.value); }; const productSelected = (selectedRows) => { productSelectedRows.value = selectedRows; }; const expandedRowKeys = ref([]); // å±å¼è¡ const expandChange = (row, expandedRows) => { if (expandedRows.length > 0) { expandedRowKeys.value = []; try { productList({ salesLedgerId: row.id, type: 1 }).then((res) => { const index = tableData.value.findIndex((item) => item.id === row.id); if (index > -1) { tableData.value[index].children = res.data; } expandedRowKeys.value.push(row.id); }); } catch (error) { console.log(error); } } else { expandedRowKeys.value = []; } }; // 主表åè®¡æ¹æ³ const summarizeMainTable = (param) => { return proxy.summarizeTable(param, [ "contractAmount", "taxInclusiveTotalPrice", "taxExclusiveTotalPrice", ]); }; // å表åè®¡æ¹æ³ const summarizeChildrenTable = (param) => { return proxy.summarizeTable(param, [ "taxInclusiveUnitPrice", "taxInclusiveTotalPrice", "taxExclusiveTotalPrice", ]); }; // æå¼å¼¹æ¡ const openForm = async (type, row) => { operationType.value = type; form.value = {}; productData.value = []; approverNodes.value = [{ id: 1, userId: null }]; nextApproverId = 2; let userLists = await userListNoPage(); userList.value = userLists.data; customerList().then((res) => { customerOption.value = res; }); form.value.entryPerson = userStore.id; if (type !== "add") { currentId.value = row.id; getSalesLedgerWithProducts({ id: row.id, type: 1 }).then((res) => { form.value = { ...res }; form.value.entryPerson = Number(res.entryPerson); if (!form.value.salesType) { form.value.salesType = "æ®é"; } productData.value = form.value.productData; fileList.value = form.value.salesLedgerFiles; const approveUserIds = form.value.approveUserIds || form.value.approverIds; if (approveUserIds) { const ids = String(approveUserIds) .split(",") .map((id) => Number(id.trim())) .filter((id) => !Number.isNaN(id)); if (ids.length > 0) { approverNodes.value = ids.map((id, idx) => ({ id: idx + 1, userId: id })); nextApproverId = ids.length + 1; if (nodes[i].children && nodes[i].children.length > 0) { const foundNode = findNodeById(nodes[i].children, productId); if (foundNode) { return foundNode; // å¨åèç¹ä¸æ¾å°ï¼è¿å该èç¹ } } } return null; // æ²¡ææ¾å°èç¹ï¼è¿ånull }; function convertIdToValue(data) { return data.map(item => { const { id, children, ...rest } = item; const newItem = { ...rest, value: id, // å° id æ¹ä¸º value }; if (children && children.length > 0) { newItem.children = convertIdToValue(children); } return newItem; }); } // let userAll = await userStore.getInfo() // userList.value.forEach(element => { // if(userAll.user.nickName === element.nickName && userAll.user.userName === element.userName) { // form.value.entryPerson = userAll.user.userId // 设置é»è®¤ä¸å¡å为å½åç¨æ· // } // }); form.value.entryDate = getCurrentDate(); // 设置é»è®¤å½å ¥æ¥æä¸ºå½åæ¥æ if (type === "add") { form.value.salesType = "æ®é"; } dialogFormVisible.value = true; }; function changs(val) { console.log(val); } // ä¸ä¼ åæ ¡æ£ function handleBeforeUpload(file) { // æ ¡æ£æä»¶å¤§å° // if (file.size > 1024 * 1024 * 10) { // proxy.$modal.msgError("ä¸ä¼ æä»¶å¤§å°ä¸è½è¶ è¿10MB!"); // return false; // } proxy.$modal.loading("æ£å¨ä¸ä¼ æä»¶ï¼è¯·ç¨å..."); return true; } // ä¸ä¼ 失败 function handleUploadError(err) { proxy.$modal.msgError("ä¸ä¼ æä»¶å¤±è´¥"); proxy.$modal.closeLoading(); } // ä¸ä¼ æååè° function handleUploadSuccess(res, file, uploadFiles) { proxy.$modal.closeLoading(); if (res.code === 200) { file.tempId = res.data.tempId; proxy.$modal.msgSuccess("ä¸ä¼ æå"); } else { proxy.$modal.msgError(res.msg); proxy.$refs.fileUpload.handleRemove(file); } } // ç§»é¤æä»¶ function handleRemove(file) { if (operationType.value === "edit") { let ids = []; ids.push(file.id); delLedgerFile(ids).then((res) => { proxy.$modal.msgSuccess("å 餿å"); // è¡¨æ ¼éæ©æ°æ® const handleSelectionChange = selection => { // è¿æ»¤æåæ°æ® selectedRows.value = selection.filter(item => item.children !== undefined); console.log("selection", selectedRows.value); }; const productSelected = selectedRows => { productSelectedRows.value = selectedRows; }; const expandedRowKeys = ref([]); // å±å¼è¡ const expandChange = (row, expandedRows) => { if (expandedRows.length > 0) { expandedRowKeys.value = []; try { productList({ salesLedgerId: row.id, type: 1 }).then(res => { const index = tableData.value.findIndex(item => item.id === row.id); if (index > -1) { tableData.value[index].children = res.data; } expandedRowKeys.value.push(row.id); }); } catch (error) { console.log(error); } } else { expandedRowKeys.value = []; } }; // 主表åè®¡æ¹æ³ const summarizeMainTable = param => { return proxy.summarizeTable(param, [ "contractAmount", "taxInclusiveTotalPrice", "taxExclusiveTotalPrice", ]); }; // å表åè®¡æ¹æ³ const summarizeChildrenTable = param => { return proxy.summarizeTable(param, [ "taxInclusiveUnitPrice", "taxInclusiveTotalPrice", "taxExclusiveTotalPrice", ]); }; // æå¼å¼¹æ¡ const openForm = async (type, row) => { operationType.value = type; form.value = {}; productData.value = []; approverNodes.value = [{ id: 1, userId: null }]; nextApproverId = 2; let userLists = await userListNoPage(); userList.value = userLists.data; customerList().then(res => { customerOption.value = res; }); form.value.entryPerson = userStore.id; if (type !== "add") { currentId.value = row.id; getSalesLedgerWithProducts({ id: row.id, type: 1 }).then(res => { form.value = { ...res }; form.value.entryPerson = Number(res.entryPerson); if (!form.value.salesType) { form.value.salesType = "æ®é"; } productData.value = form.value.productData; fileList.value = form.value.salesLedgerFiles; const approveUserIds = form.value.approveUserIds || form.value.approverIds; if (approveUserIds) { const ids = String(approveUserIds) .split(",") .map(id => Number(id.trim())) .filter(id => !Number.isNaN(id)); if (ids.length > 0) { approverNodes.value = ids.map((id, idx) => ({ id: idx + 1, userId: id, })); nextApproverId = ids.length + 1; } } }); } // let userAll = await userStore.getInfo() // userList.value.forEach(element => { // if(userAll.user.nickName === element.nickName && userAll.user.userName === element.userName) { // form.value.entryPerson = userAll.user.userId // 设置é»è®¤ä¸å¡å为å½åç¨æ· // } // }); form.value.entryDate = getCurrentDate(); // 设置é»è®¤å½å ¥æ¥æä¸ºå½åæ¥æ if (type === "add") { form.value.salesType = "æ®é"; } dialogFormVisible.value = true; }; function changs(val) { console.log(val); } } // æäº¤è¡¨å const submitForm = () => { proxy.$refs["formRef"].validate((valid) => { if (valid) { if (operationType.value !== "view") { const hasEmptyApprover = approverNodes.value.some((node) => !node.userId); if (hasEmptyApprover) { proxy.$modal.msgWarning("请为ææå®¡æ¹èç¹éæ©å®¡æ¹äºº"); // ä¸ä¼ åæ ¡æ£ function handleBeforeUpload(file) { // æ ¡æ£æä»¶å¤§å° // if (file.size > 1024 * 1024 * 10) { // proxy.$modal.msgError("ä¸ä¼ æä»¶å¤§å°ä¸è½è¶ è¿10MB!"); // return false; // } proxy.$modal.loading("æ£å¨ä¸ä¼ æä»¶ï¼è¯·ç¨å..."); return true; } // ä¸ä¼ 失败 function handleUploadError(err) { proxy.$modal.msgError("ä¸ä¼ æä»¶å¤±è´¥"); proxy.$modal.closeLoading(); } // ä¸ä¼ æååè° function handleUploadSuccess(res, file, uploadFiles) { proxy.$modal.closeLoading(); if (res.code === 200) { file.tempId = res.data.tempId; proxy.$modal.msgSuccess("ä¸ä¼ æå"); } else { proxy.$modal.msgError(res.msg); proxy.$refs.fileUpload.handleRemove(file); } } // ç§»é¤æä»¶ function handleRemove(file) { if (operationType.value === "edit") { let ids = []; ids.push(file.id); delLedgerFile(ids).then(res => { proxy.$modal.msgSuccess("å 餿å"); }); } } // æäº¤è¡¨å const submitForm = () => { proxy.$refs["formRef"].validate(valid => { if (valid) { if (operationType.value !== "view") { const hasEmptyApprover = approverNodes.value.some(node => !node.userId); if (hasEmptyApprover) { proxy.$modal.msgWarning("请为ææå®¡æ¹èç¹éæ©å®¡æ¹äºº"); return; } form.value.approveUserIds = approverNodes.value .map(node => node.userId) .join(","); } console.log("productData.value--", productData.value); if (productData.value !== null && productData.value.length > 0) { form.value.productData = proxy.HaveJson(productData.value); } else { proxy.$modal.msgWarning("请添å 产åä¿¡æ¯"); return; } form.value.approveUserIds = approverNodes.value.map((node) => node.userId).join(","); } console.log('productData.value--', productData.value) if (productData.value !== null && productData.value.length > 0) { form.value.productData = proxy.HaveJson(productData.value); } else { proxy.$modal.msgWarning("请添å 产åä¿¡æ¯"); return; } let tempFileIds = []; if (fileList.value !== null && fileList.value.length > 0) { tempFileIds = fileList.value.map((item) => item.tempId); } form.value.tempFileIds = tempFileIds; form.value.type = 1; addOrUpdateSalesLedger(form.value).then((res) => { proxy.$modal.msgSuccess("æäº¤æå"); closeDia(); getList(); }); } }); }; // å ³éå¼¹æ¡ const closeDia = () => { proxy.resetForm("formRef"); dialogFormVisible.value = false; }; const productIndex = ref(0); // æå¼äº§åå¼¹æ¡ const openProductForm = (type, row,index) => { productOperationType.value = type; productForm.value = {}; proxy.resetForm("productFormRef"); if (type === "edit") { productForm.value = { ...row }; productIndex.value = index; } productFormVisible.value = true; getProductOptions(); }; // æäº¤äº§å表å const submitProduct = () => { proxy.$refs["productFormRef"].validate((valid) => { if (valid) { if (operationType.value === "edit") { submitProductEdit(); } else { if(productOperationType.value === "add"){ productData.value.push({ ...productForm.value }); }else{ productData.value[productIndex.value] = { ...productForm.value } let tempFileIds = []; if (fileList.value !== null && fileList.value.length > 0) { tempFileIds = fileList.value.map(item => item.tempId); } closeProductDia(); form.value.tempFileIds = tempFileIds; form.value.type = 1; addOrUpdateSalesLedger(form.value).then(res => { proxy.$modal.msgSuccess("æäº¤æå"); closeDia(); getList(); }); } }); }; // å ³éå¼¹æ¡ const closeDia = () => { proxy.resetForm("formRef"); dialogFormVisible.value = false; }; const productIndex = ref(0); // æå¼äº§åå¼¹æ¡ const openProductForm = (type, row, index) => { productOperationType.value = type; productForm.value = {}; proxy.resetForm("productFormRef"); if (type === "edit") { productForm.value = { ...row }; productIndex.value = index; } }); }; const submitProductEdit = () => { productForm.value.salesLedgerId = currentId.value; productForm.value.type = 1 addOrUpdateSalesLedgerProduct(productForm.value).then((res) => { proxy.$modal.msgSuccess("æäº¤æå"); closeProductDia(); getSalesLedgerWithProducts({ id: currentId.value, type: 1 }).then((res) => { productData.value = res.productData; }); }); }; // å é¤äº§å const deleteProduct = () => { if (productSelectedRows.value.length === 0) { proxy.$modal.msgWarning("è¯·éæ©æ°æ®"); return; } if (operationType.value === "add") { productSelectedRows.value.forEach((selectedRow) => { const index = productData.value.findIndex( (product) => product.id === selectedRow.id ); if (index !== -1) { productData.value.splice(index, 1); productFormVisible.value = true; getProductOptions(); }; // æäº¤äº§å表å const submitProduct = () => { proxy.$refs["productFormRef"].validate(valid => { if (valid) { if (operationType.value === "edit") { submitProductEdit(); } else { if (productOperationType.value === "add") { productData.value.push({ ...productForm.value }); } else { productData.value[productIndex.value] = { ...productForm.value }; } closeProductDia(); } } }); } else { }; const submitProductEdit = () => { productForm.value.salesLedgerId = currentId.value; productForm.value.type = 1; addOrUpdateSalesLedgerProduct(productForm.value).then(res => { proxy.$modal.msgSuccess("æäº¤æå"); closeProductDia(); getSalesLedgerWithProducts({ id: currentId.value, type: 1 }).then(res => { productData.value = res.productData; }); }); }; // å é¤äº§å const deleteProduct = () => { if (productSelectedRows.value.length === 0) { proxy.$modal.msgWarning("è¯·éæ©æ°æ®"); return; } if (operationType.value === "add") { productSelectedRows.value.forEach(selectedRow => { const index = productData.value.findIndex( product => product.id === selectedRow.id ); if (index !== -1) { productData.value.splice(index, 1); } }); } else { let ids = []; if (productSelectedRows.value.length > 0) { ids = productSelectedRows.value.map(item => item.id); } ElMessageBox.confirm("éä¸çå 容å°è¢«å é¤ï¼æ¯å¦ç¡®è®¤å é¤ï¼", "导åº", { confirmButtonText: "确认", cancelButtonText: "åæ¶", type: "warning", }) .then(() => { delProduct(ids).then(res => { proxy.$modal.msgSuccess("å 餿å"); closeProductDia(); getSalesLedgerWithProducts({ id: currentId.value, type: 1 }).then( res => { productData.value = res.productData; } ); }); }) .catch(() => { proxy.$modal.msg("已忶"); }); } }; // å ³é产åå¼¹æ¡ const closeProductDia = () => { proxy.resetForm("productFormRef"); productFormVisible.value = false; }; // å¯¼åº const handleOut = () => { ElMessageBox.confirm("éä¸çå 容å°è¢«å¯¼åºï¼æ¯å¦ç¡®è®¤å¯¼åºï¼", "导åº", { confirmButtonText: "确认", cancelButtonText: "åæ¶", type: "warning", }) .then(() => { proxy.download("/sales/ledger/export", {}, "éå®å°è´¦.xlsx"); }) .catch(() => { proxy.$modal.msg("已忶"); }); }; // å é¤ const handleDelete = () => { let ids = []; if (productSelectedRows.value.length > 0) { ids = productSelectedRows.value.map((item) => item.id); if (selectedRows.value.length > 0) { ids = selectedRows.value.map(item => item.id); } else { proxy.$modal.msgWarning("è¯·éæ©æ°æ®"); return; } ElMessageBox.confirm("éä¸çå 容å°è¢«å é¤ï¼æ¯å¦ç¡®è®¤å é¤ï¼", "导åº", { confirmButtonText: "确认", @@ -1047,843 +1352,831 @@ type: "warning", }) .then(() => { delProduct(ids).then((res) => { delLedger(ids).then(res => { proxy.$modal.msgSuccess("å 餿å"); closeProductDia(); getSalesLedgerWithProducts({ id: currentId.value, type: 1 }).then( (res) => { productData.value = res.productData; } ); getList(); }); }) .catch(() => { proxy.$modal.msg("已忶"); }); } }; // å ³é产åå¼¹æ¡ const closeProductDia = () => { proxy.resetForm("productFormRef"); productFormVisible.value = false; }; // å¯¼åº const handleOut = () => { ElMessageBox.confirm("éä¸çå 容å°è¢«å¯¼åºï¼æ¯å¦ç¡®è®¤å¯¼åºï¼", "导åº", { confirmButtonText: "确认", cancelButtonText: "åæ¶", type: "warning", }) .then(() => { proxy.download("/sales/ledger/export", {}, "éå®å°è´¦.xlsx"); }) .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", }) .then(() => { delLedger(ids).then((res) => { proxy.$modal.msgSuccess("å 餿å"); getList(); }); }) .catch(() => { proxy.$modal.msg("已忶"); }); }; }; // æå°åè½ const handlePrint = async () => { if (selectedRows.value.length === 0) { proxy.$modal.msgWarning("è¯·éæ©è¦æå°çæ°æ®"); return; } // æ¾ç¤ºå è½½ç¶æ proxy.$modal.loading("æ£å¨è·åäº§åæ°æ®ï¼è¯·ç¨å..."); try { // 为æ¯ä¸ªéä¸çéå®å°è´¦è®°å½æ¥è¯¢å¯¹åºçäº§åæ°æ® const printDataWithProducts = []; for (const row of selectedRows.value) { try { // è°ç¨productListæ¥å£æ¥è¯¢äº§åæ°æ® const productRes = await productList({ salesLedgerId: row.id, type: 1 }); // å°äº§åæ°æ®æ´åå°éå®å°è´¦è®°å½ä¸ const rowWithProducts = { ...row, products: productRes.data || [] }; printDataWithProducts.push(rowWithProducts); } catch (error) { console.error(`è·åéå®å°è´¦ ${row.id} çäº§åæ°æ®å¤±è´¥:`, error); // å³ä½¿æä¸ªè®°å½çäº§åæ°æ®è·å失败ï¼ä¹è¦å å«è¯¥è®°å½ printDataWithProducts.push({ ...row, products: [] }); } } printData.value = printDataWithProducts; console.log('æå°æ°æ®ï¼å å«äº§åï¼:', printData.value); printPreviewVisible.value = true; } catch (error) { console.error('è·åäº§åæ°æ®å¤±è´¥:', error); proxy.$modal.msgError("è·åäº§åæ°æ®å¤±è´¥ï¼è¯·éè¯"); } finally { proxy.$modal.closeLoading(); } }; // æ§è¡æå° const executePrint = () => { console.log('å¼å§æ§è¡æå°ï¼æ°æ®æ¡æ°:', printData.value.length); console.log('æå°æ°æ®:', printData.value); // å建ä¸ä¸ªæ°çæå°çªå£ const printWindow = window.open('', '_blank', 'width=800,height=600'); // æå»ºæå°å 容 let printContent = ` <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>æå°é¢è§</title> <style> body { margin: 0; padding: 0; font-family: "SimSun", serif; background: white; } .print-page { width: 200mm; height: 75mm; padding: 10mm; padding-left: 20mm; background: white; box-sizing: border-box; page-break-after: always; page-break-inside: avoid; } .print-page:last-child { page-break-after: avoid; } .delivery-note { width: 100%; height: 100%; font-size: 12px; line-height: 1.2; display: flex; flex-direction: column; color: #000; } .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: 12px; } .value { margin-right: 20px; min-width: 80px; font-size: 12px; } .table-section { margin-bottom: 40px; // flex: 0.6; } .product-table { width: 100%; border-collapse: collapse; border: 1px solid #000; } .product-table th, .product-table td { border: 1px solid #000; padding: 6px; text-align: center; font-size: 12px; line-height: 1.4; } .product-table th { font-weight: bold; } .total-value { font-weight: bold; } .footer-section { margin-top: auto; } .footer-row { display: flex; margin-bottom: 3px; line-height: 22px; justify-content: space-between; } .footer-item { display: flex; margin-right: 20px; } .footer-item .label { font-weight: bold; width: 80px; font-size: 12px; } .footer-item .value { min-width: 80px; font-size: 12px; } .address-item .address-value { min-width: 200px; } @media print { body { margin: 0; padding: 0; } .print-page { margin: 0; padding: 10mm; /* padding-left: 20mm; */ page-break-inside: avoid; page-break-after: always; } .print-page:last-child { page-break-after: avoid; } } </style> </head> <body> `; // ä¸ºæ¯æ¡æ°æ®çææå°é¡µé¢ printData.value.forEach((item, index) => { printContent += ` <div class="print-page"> <div class="delivery-note"> <div class="header"> <div class="company-name">é¼è¯çå®ä¸æéè´£ä»»å ¬å¸</div> <div class="document-title">é¶å®åè´§å</div> </div> <div class="info-section"> <div class="info-row"> <div> <span class="label">åè´§æ¥æï¼</span> <span class="value">${formatDate(item.createTime)}</span> </div> <div> <span class="label">客æ·åç§°ï¼</span> <span class="value">${item.customerName || 'å¼ ç±æ'}</span> </div> </div> <div class="info-row"> <span class="label">åå·ï¼</span> <span class="value">${item.salesContractNo || ''}</span> </div> </div> <div class="table-section"> <table class="product-table"> <thead> <tr> <th>产ååç§°</th> <th>è§æ ¼åå·</th> <th>åä½</th> <th>åä»·</th> <th>é¶å®æ°é</th> <th>é¶å®éé¢</th> </tr> </thead> <tbody> ${item.products && item.products.length > 0 ? item.products.map(product => ` <tr> <td>${product.productCategory || ''}</td> <td>${product.specificationModel || ''}</td> <td>${product.unit || ''}</td> <td>${product.taxInclusiveUnitPrice || '0'}</td> <td>${product.quantity || '0'}</td> <td>${product.taxInclusiveTotalPrice || '0'}</td> </tr> `).join('') : '<tr><td colspan="6" style="text-align: center; color: #999;">ææ äº§åæ°æ®</td></tr>' } </tbody> <tfoot> <tr> <td class="label">å计</td> <td class="total-value"></td> <td class="total-value"></td> <td class="total-value"></td> <td class="total-value">${getTotalQuantityForPrint(item.products)}</td> <td class="total-value">${getTotalAmountForPrint(item.products)}</td> </tr> </tfoot> </table> </div> <div class="footer-section"> <div class="footer-row"> <div class="footer-item"> <span class="label">æ¶è´§çµè¯ï¼</span> <span class="value"></span> </div> <div class="footer-item"> <span class="label">æ¶è´§äººï¼</span> <span class="value"></span> </div> <div class="footer-item address-item"> <span class="label">æ¶è´§å°åï¼</span> <span class="value address-value"></span> </div> </div> <div class="footer-row"> <div class="footer-item"> <span class="label">æä½åï¼</span> <span class="value">${userStore.nickName || 'æå¼å'}</span> </div> <div class="footer-item"> <span class="label">æå°æ¥æï¼</span> <span class="value">${formatDateTime(new Date())}</span> </div> </div> </div> </div> </div> `; }); printContent += ` </body> </html> `; // åå ¥å 容尿°çªå£ printWindow.document.write(printContent); printWindow.document.close(); // çå¾ å 容å è½½å®æåæå° 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 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}`; }; // è·åå½åæ¥æå¹¶æ ¼å¼å为 YYYY-MM-DD function getCurrentDate() { const today = new Date(); const year = today.getFullYear(); const month = String(today.getMonth() + 1).padStart(2, "0"); // æä»½ä»0å¼å§ const day = String(today.getDate()).padStart(2, "0"); return `${year}-${month}-${day}`; } // 计ç®äº§åæ»æ°é const getTotalQuantity = (products) => { if (!products || products.length === 0) return '0'; const total = products.reduce((sum, product) => { return sum + (parseFloat(product.quantity) || 0); }, 0); return total.toFixed(2); }; // 计ç®äº§åæ»éé¢ const getTotalAmount = (products) => { if (!products || products.length === 0) return '0'; const total = products.reduce((sum, product) => { return sum + (parseFloat(product.taxInclusiveTotalPrice) || 0); }, 0); return total.toFixed(2); }; // ç¨äºæå°ç计ç®å½æ° const getTotalQuantityForPrint = (products) => { if (!products || products.length === 0) return '0'; const total = products.reduce((sum, product) => { return sum + (parseFloat(product.quantity) || 0); }, 0); return total.toFixed(2); }; const getTotalAmountForPrint = (products) => { if (!products || products.length === 0) return '0'; const total = products.reduce((sum, product) => { return sum + (parseFloat(product.taxInclusiveTotalPrice) || 0); }, 0); return total.toFixed(2); }; const mathNum = () => { console.log("productForm.value", productForm.value); if (!productForm.value.taxInclusiveUnitPrice) { return; } if (!productForm.value.quantity) { return; } // å«ç¨æ»ä»·è®¡ç® productForm.value.taxInclusiveTotalPrice = proxy.calculateTaxIncludeTotalPrice( productForm.value.taxInclusiveUnitPrice, productForm.value.quantity ); if (productForm.value.taxRate) { // ä¸å«ç¨æ»ä»·è®¡ç® productForm.value.taxExclusiveTotalPrice = proxy.calculateTaxExclusiveTotalPrice( productForm.value.taxInclusiveTotalPrice, productForm.value.taxRate ); } }; // æ ¹æ®å«ç¨æ»ä»·è®¡ç®å«ç¨åä»·åæ°é const calculateFromTotalPrice = () => { if (isCalculating.value) return; const totalPrice = parseFloat(productForm.value.taxInclusiveTotalPrice); const quantity = parseFloat(productForm.value.quantity); if (!totalPrice || !quantity || quantity <= 0) { return; } isCalculating.value = true; // 计ç®å«ç¨åä»· = å«ç¨æ»ä»· / æ°é productForm.value.taxInclusiveUnitPrice = (totalPrice / quantity).toFixed(2); // 妿æç¨çï¼è®¡ç®ä¸å«ç¨æ»ä»· if (productForm.value.taxRate) { productForm.value.taxExclusiveTotalPrice = proxy.calculateTaxExclusiveTotalPrice( totalPrice, productForm.value.taxRate ); } isCalculating.value = false; }; // æ ¹æ®ä¸å«ç¨æ»ä»·è®¡ç®å«ç¨åä»·åæ°é const calculateFromExclusiveTotalPrice = () => { if (!productForm.value.taxRate) { proxy.$modal.msgWarning("请å éæ©ç¨ç"); return; } if (isCalculating.value) return; const exclusiveTotalPrice = parseFloat(productForm.value.taxExclusiveTotalPrice); const quantity = parseFloat(productForm.value.quantity); const taxRate = parseFloat(productForm.value.taxRate); if (!exclusiveTotalPrice || !quantity || quantity <= 0 || !taxRate) { return; } isCalculating.value = true; // å 计ç®å«ç¨æ»ä»· = ä¸å«ç¨æ»ä»· / (1 - ç¨ç/100) const taxRateDecimal = taxRate / 100; const inclusiveTotalPrice = exclusiveTotalPrice / (1 - taxRateDecimal); productForm.value.taxInclusiveTotalPrice = inclusiveTotalPrice.toFixed(2); // 计ç®å«ç¨åä»· = å«ç¨æ»ä»· / æ°é productForm.value.taxInclusiveUnitPrice = (inclusiveTotalPrice / quantity).toFixed(2); isCalculating.value = false; }; // æ ¹æ®æ°éååè®¡ç®æ»ä»· const calculateFromQuantity = () => { if (!productForm.value.taxRate) { proxy.$modal.msgWarning("请å éæ©ç¨ç"); return; } if (isCalculating.value) return; const quantity = parseFloat(productForm.value.quantity); const unitPrice = parseFloat(productForm.value.taxInclusiveUnitPrice); if (!quantity || quantity <= 0 || !unitPrice) { return; } isCalculating.value = true; // 计ç®å«ç¨æ»ä»· productForm.value.taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2); // 妿æç¨çï¼è®¡ç®ä¸å«ç¨æ»ä»· if (productForm.value.taxRate) { productForm.value.taxExclusiveTotalPrice = proxy.calculateTaxExclusiveTotalPrice( productForm.value.taxInclusiveTotalPrice, productForm.value.taxRate ); } isCalculating.value = false; }; // æ ¹æ®å«ç¨åä»·ååè®¡ç®æ»ä»· const calculateFromUnitPrice = () => { if (!productForm.value.taxRate) { proxy.$modal.msgWarning("请å éæ©ç¨ç"); return; } if (isCalculating.value) return; const quantity = parseFloat(productForm.value.quantity); const unitPrice = parseFloat(productForm.value.taxInclusiveUnitPrice); if (!quantity || quantity <= 0 || !unitPrice) { return; } isCalculating.value = true; // 计ç®å«ç¨æ»ä»· productForm.value.taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2); // 妿æç¨çï¼è®¡ç®ä¸å«ç¨æ»ä»· if (productForm.value.taxRate) { productForm.value.taxExclusiveTotalPrice = proxy.calculateTaxExclusiveTotalPrice( productForm.value.taxInclusiveTotalPrice, productForm.value.taxRate ); } isCalculating.value = false; }; // æ ¹æ®ç¨çåå计ç®ä¸å«ç¨æ»ä»· const calculateFromTaxRate = () => { if (!productForm.value.taxRate) { proxy.$modal.msgWarning("请å éæ©ç¨ç"); return; } if (isCalculating.value) return; const inclusiveTotalPrice = parseFloat(productForm.value.taxInclusiveTotalPrice); const taxRate = parseFloat(productForm.value.taxRate); if (!inclusiveTotalPrice || !taxRate) { return; } isCalculating.value = true; // 计ç®ä¸å«ç¨æ»ä»· productForm.value.taxExclusiveTotalPrice = proxy.calculateTaxExclusiveTotalPrice( inclusiveTotalPrice, taxRate ); isCalculating.value = false; }; /** * ä¸è½½æä»¶ * * @param row ä¸è½½æä»¶çç¸å ³ä¿¡æ¯å¯¹è±¡ */ const fileListRef = ref(null) const downLoadFile = (row) => { getSalesLedgerWithProducts({ id: row.id, type: 1 }).then((res) => { fileListRef.value.open(res.salesLedgerFiles) }); } // æå¼åè´§å¼¹æ¡ const openDeliveryForm = (row) => { getProductInventory({ salesLedgerId: row.id, type:1 }).then((res) => { currentDeliveryRow.value = row; deliveryForm.value = { shippingDate: getCurrentDate(), shippingCarNumber: "", }; deliveryFormVisible.value = true; }).catch(err => { // ElMessage.error(err); }); }; // æäº¤å货表å const submitDelivery = () => { proxy.$refs["deliveryFormRef"].validate((valid) => { if (valid) { addShippingInfo({ salesLedgerId: currentDeliveryContext.value.parentRow.id, salesLedgerProductId: currentDeliveryContext.value.productRow.id, shippingDate: deliveryForm.value.shippingDate, shippingCarNumber: deliveryForm.value.shippingCarNumber, }) .then(() => { proxy.$modal.msgSuccess("åè´§æå"); closeDeliveryDia(); }) .catch(() => { proxy.$modal.msgError("å货失败ï¼è¯·éè¯"); }); // æå°åè½ const handlePrint = async () => { if (selectedRows.value.length === 0) { proxy.$modal.msgWarning("è¯·éæ©è¦æå°çæ°æ®"); return; } // æ¾ç¤ºå è½½ç¶æ proxy.$modal.loading("æ£å¨è·åäº§åæ°æ®ï¼è¯·ç¨å..."); try { // 为æ¯ä¸ªéä¸çéå®å°è´¦è®°å½æ¥è¯¢å¯¹åºçäº§åæ°æ® const printDataWithProducts = []; for (const row of selectedRows.value) { try { // è°ç¨productListæ¥å£æ¥è¯¢äº§åæ°æ® const productRes = await productList({ salesLedgerId: row.id, type: 1, }); // å°äº§åæ°æ®æ´åå°éå®å°è´¦è®°å½ä¸ const rowWithProducts = { ...row, products: productRes.data || [], }; printDataWithProducts.push(rowWithProducts); } catch (error) { console.error(`è·åéå®å°è´¦ ${row.id} çäº§åæ°æ®å¤±è´¥:`, error); // å³ä½¿æä¸ªè®°å½çäº§åæ°æ®è·å失败ï¼ä¹è¦å å«è¯¥è®°å½ printDataWithProducts.push({ ...row, products: [], }); } } printData.value = printDataWithProducts; console.log("æå°æ°æ®ï¼å å«äº§åï¼:", printData.value); printPreviewVisible.value = true; } catch (error) { console.error("è·åäº§åæ°æ®å¤±è´¥:", error); proxy.$modal.msgError("è·åäº§åæ°æ®å¤±è´¥ï¼è¯·éè¯"); } finally { proxy.$modal.closeLoading(); } }; // æ§è¡æå° const executePrint = () => { console.log("å¼å§æ§è¡æå°ï¼æ°æ®æ¡æ°:", printData.value.length); console.log("æå°æ°æ®:", printData.value); // å建ä¸ä¸ªæ°çæå°çªå£ const printWindow = window.open("", "_blank", "width=800,height=600"); // æå»ºæå°å 容 let printContent = ` <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>æå°é¢è§</title> <style> body { margin: 0; padding: 0; font-family: "SimSun", serif; background: white; } .print-page { width: 200mm; height: 75mm; padding: 10mm; padding-left: 20mm; background: white; box-sizing: border-box; page-break-after: always; page-break-inside: avoid; } .print-page:last-child { page-break-after: avoid; } .delivery-note { width: 100%; height: 100%; font-size: 12px; line-height: 1.2; display: flex; flex-direction: column; color: #000; } .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: 12px; } .value { margin-right: 20px; min-width: 80px; font-size: 12px; } .table-section { margin-bottom: 40px; // flex: 0.6; } .product-table { width: 100%; border-collapse: collapse; border: 1px solid #000; } .product-table th, .product-table td { border: 1px solid #000; padding: 6px; text-align: center; font-size: 12px; line-height: 1.4; } .product-table th { font-weight: bold; } .total-value { font-weight: bold; } .footer-section { margin-top: auto; } .footer-row { display: flex; margin-bottom: 3px; line-height: 22px; justify-content: space-between; } .footer-item { display: flex; margin-right: 20px; } .footer-item .label { font-weight: bold; width: 80px; font-size: 12px; } .footer-item .value { min-width: 80px; font-size: 12px; } .address-item .address-value { min-width: 200px; } @media print { body { margin: 0; padding: 0; } .print-page { margin: 0; padding: 10mm; /* padding-left: 20mm; */ page-break-inside: avoid; page-break-after: always; } .print-page:last-child { page-break-after: avoid; } } </style> </head> <body> `; // ä¸ºæ¯æ¡æ°æ®çææå°é¡µé¢ printData.value.forEach((item, index) => { printContent += ` <div class="print-page"> <div class="delivery-note"> <div class="header"> <div class="company-name">é¼è¯çå®ä¸æéè´£ä»»å ¬å¸</div> <div class="document-title">é¶å®åè´§å</div> </div> <div class="info-section"> <div class="info-row"> <div> <span class="label">åè´§æ¥æï¼</span> <span class="value">${formatDate( item.createTime )}</span> </div> <div> <span class="label">客æ·åç§°ï¼</span> <span class="value">${ item.customerName || "å¼ ç±æ" }</span> </div> </div> <div class="info-row"> <span class="label">åå·ï¼</span> <span class="value">${ item.salesContractNo || "" }</span> </div> </div> <div class="table-section"> <table class="product-table"> <thead> <tr> <th>产ååç§°</th> <th>è§æ ¼åå·</th> <th>åä½</th> <th>åä»·</th> <th>é¶å®æ°é</th> <th>é¶å®éé¢</th> </tr> </thead> <tbody> ${ item.products && item.products.length > 0 ? item.products .map( product => ` <tr> <td>${product.productCategory || ""}</td> <td>${ product.specificationModel || "" }</td> <td>${product.unit || ""}</td> <td>${ product.taxInclusiveUnitPrice || "0" }</td> <td>${product.quantity || "0"}</td> <td>${ product.taxInclusiveTotalPrice || "0" }</td> </tr> ` ) .join("") : '<tr><td colspan="6" style="text-align: center; color: #999;">ææ äº§åæ°æ®</td></tr>' } </tbody> <tfoot> <tr> <td class="label">å计</td> <td class="total-value"></td> <td class="total-value"></td> <td class="total-value"></td> <td class="total-value">${getTotalQuantityForPrint( item.products )}</td> <td class="total-value">${getTotalAmountForPrint( item.products )}</td> </tr> </tfoot> </table> </div> <div class="footer-section"> <div class="footer-row"> <div class="footer-item"> <span class="label">æ¶è´§çµè¯ï¼</span> <span class="value"></span> </div> <div class="footer-item"> <span class="label">æ¶è´§äººï¼</span> <span class="value"></span> </div> <div class="footer-item address-item"> <span class="label">æ¶è´§å°åï¼</span> <span class="value address-value"></span> </div> </div> <div class="footer-row"> <div class="footer-item"> <span class="label">æä½åï¼</span> <span class="value">${ userStore.nickName || "æå¼å" }</span> </div> <div class="footer-item"> <span class="label">æå°æ¥æï¼</span> <span class="value">${formatDateTime( new Date() )}</span> </div> </div> </div> </div> </div> `; }); printContent += ` </body> </html> `; // åå ¥å 容尿°çªå£ printWindow.document.write(printContent); printWindow.document.close(); // çå¾ å 容å è½½å®æåæå° 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 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}`; }; // è·åå½åæ¥æå¹¶æ ¼å¼å为 YYYY-MM-DD function getCurrentDate() { const today = new Date(); const year = today.getFullYear(); const month = String(today.getMonth() + 1).padStart(2, "0"); // æä»½ä»0å¼å§ const day = String(today.getDate()).padStart(2, "0"); return `${year}-${month}-${day}`; } // 计ç®äº§åæ»æ°é const getTotalQuantity = products => { if (!products || products.length === 0) return "0"; const total = products.reduce((sum, product) => { return sum + (parseFloat(product.quantity) || 0); }, 0); return total.toFixed(2); }; // 计ç®äº§åæ»éé¢ const getTotalAmount = products => { if (!products || products.length === 0) return "0"; const total = products.reduce((sum, product) => { return sum + (parseFloat(product.taxInclusiveTotalPrice) || 0); }, 0); return total.toFixed(2); }; // ç¨äºæå°ç计ç®å½æ° const getTotalQuantityForPrint = products => { if (!products || products.length === 0) return "0"; const total = products.reduce((sum, product) => { return sum + (parseFloat(product.quantity) || 0); }, 0); return total.toFixed(2); }; const getTotalAmountForPrint = products => { if (!products || products.length === 0) return "0"; const total = products.reduce((sum, product) => { return sum + (parseFloat(product.taxInclusiveTotalPrice) || 0); }, 0); return total.toFixed(2); }; const mathNum = () => { console.log("productForm.value", productForm.value); if (!productForm.value.taxInclusiveUnitPrice) { return; } if (!productForm.value.quantity) { return; } // å«ç¨æ»ä»·è®¡ç® productForm.value.taxInclusiveTotalPrice = proxy.calculateTaxIncludeTotalPrice( productForm.value.taxInclusiveUnitPrice, productForm.value.quantity ); if (productForm.value.taxRate) { // ä¸å«ç¨æ»ä»·è®¡ç® productForm.value.taxExclusiveTotalPrice = proxy.calculateTaxExclusiveTotalPrice( productForm.value.taxInclusiveTotalPrice, productForm.value.taxRate ); } }; // æ ¹æ®å«ç¨æ»ä»·è®¡ç®å«ç¨åä»·åæ°é const calculateFromTotalPrice = () => { if (isCalculating.value) return; const totalPrice = parseFloat(productForm.value.taxInclusiveTotalPrice); const quantity = parseFloat(productForm.value.quantity); if (!totalPrice || !quantity || quantity <= 0) { return; } isCalculating.value = true; // 计ç®å«ç¨åä»· = å«ç¨æ»ä»· / æ°é productForm.value.taxInclusiveUnitPrice = (totalPrice / quantity).toFixed(2); // 妿æç¨çï¼è®¡ç®ä¸å«ç¨æ»ä»· if (productForm.value.taxRate) { productForm.value.taxExclusiveTotalPrice = proxy.calculateTaxExclusiveTotalPrice( totalPrice, productForm.value.taxRate ); } isCalculating.value = false; }; // æ ¹æ®ä¸å«ç¨æ»ä»·è®¡ç®å«ç¨åä»·åæ°é const calculateFromExclusiveTotalPrice = () => { if (!productForm.value.taxRate) { proxy.$modal.msgWarning("请å éæ©ç¨ç"); return; } if (isCalculating.value) return; const exclusiveTotalPrice = parseFloat( productForm.value.taxExclusiveTotalPrice ); const quantity = parseFloat(productForm.value.quantity); const taxRate = parseFloat(productForm.value.taxRate); if (!exclusiveTotalPrice || !quantity || quantity <= 0 || !taxRate) { return; } isCalculating.value = true; // å 计ç®å«ç¨æ»ä»· = ä¸å«ç¨æ»ä»· / (1 - ç¨ç/100) const taxRateDecimal = taxRate / 100; const inclusiveTotalPrice = exclusiveTotalPrice / (1 - taxRateDecimal); productForm.value.taxInclusiveTotalPrice = inclusiveTotalPrice.toFixed(2); // 计ç®å«ç¨åä»· = å«ç¨æ»ä»· / æ°é productForm.value.taxInclusiveUnitPrice = ( inclusiveTotalPrice / quantity ).toFixed(2); isCalculating.value = false; }; // æ ¹æ®æ°éååè®¡ç®æ»ä»· const calculateFromQuantity = () => { if (!productForm.value.taxRate) { proxy.$modal.msgWarning("请å éæ©ç¨ç"); return; } if (isCalculating.value) return; const quantity = parseFloat(productForm.value.quantity); const unitPrice = parseFloat(productForm.value.taxInclusiveUnitPrice); if (!quantity || quantity <= 0 || !unitPrice) { return; } isCalculating.value = true; // 计ç®å«ç¨æ»ä»· productForm.value.taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2); // 妿æç¨çï¼è®¡ç®ä¸å«ç¨æ»ä»· if (productForm.value.taxRate) { productForm.value.taxExclusiveTotalPrice = proxy.calculateTaxExclusiveTotalPrice( productForm.value.taxInclusiveTotalPrice, productForm.value.taxRate ); } isCalculating.value = false; }; // æ ¹æ®å«ç¨åä»·ååè®¡ç®æ»ä»· const calculateFromUnitPrice = () => { if (!productForm.value.taxRate) { proxy.$modal.msgWarning("请å éæ©ç¨ç"); return; } if (isCalculating.value) return; const quantity = parseFloat(productForm.value.quantity); const unitPrice = parseFloat(productForm.value.taxInclusiveUnitPrice); if (!quantity || quantity <= 0 || !unitPrice) { return; } isCalculating.value = true; // 计ç®å«ç¨æ»ä»· productForm.value.taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2); // 妿æç¨çï¼è®¡ç®ä¸å«ç¨æ»ä»· if (productForm.value.taxRate) { productForm.value.taxExclusiveTotalPrice = proxy.calculateTaxExclusiveTotalPrice( productForm.value.taxInclusiveTotalPrice, productForm.value.taxRate ); } isCalculating.value = false; }; // æ ¹æ®ç¨çåå计ç®ä¸å«ç¨æ»ä»· const calculateFromTaxRate = () => { if (!productForm.value.taxRate) { proxy.$modal.msgWarning("请å éæ©ç¨ç"); return; } if (isCalculating.value) return; const inclusiveTotalPrice = parseFloat( productForm.value.taxInclusiveTotalPrice ); const taxRate = parseFloat(productForm.value.taxRate); if (!inclusiveTotalPrice || !taxRate) { return; } isCalculating.value = true; // 计ç®ä¸å«ç¨æ»ä»· productForm.value.taxExclusiveTotalPrice = proxy.calculateTaxExclusiveTotalPrice(inclusiveTotalPrice, taxRate); isCalculating.value = false; }; /** * ä¸è½½æä»¶ * * @param row ä¸è½½æä»¶çç¸å ³ä¿¡æ¯å¯¹è±¡ */ const fileListRef = ref(null); const downLoadFile = row => { getSalesLedgerWithProducts({ id: row.id, type: 1 }).then(res => { fileListRef.value.open(res.salesLedgerFiles); }); }; // æå¼åè´§å¼¹æ¡ const openDeliveryForm = (props, scope) => { getProductInventory({ salesLedgerId: props.id, type: 1 }) .then(res => { currentDeliveryContext.value = { id: props.id, productId: scope.id, }; deliveryForm.value = { shippingDate: getCurrentDate(), shippingCarNumber: "", }; // currentDeliveryRow.value = props.row; deliveryFormVisible.value = true; }) .catch(err => { // ElMessage.error(err); }); }; // æäº¤å货表å const submitDelivery = () => { proxy.$refs["deliveryFormRef"].validate(valid => { if (valid) { addShippingInfo({ salesLedgerId: currentDeliveryContext.value.id, salesLedgerProductId: currentDeliveryContext.value.productId, shippingDate: deliveryForm.value.shippingDate, shippingCarNumber: deliveryForm.value.shippingCarNumber, }) .then(() => { proxy.$modal.msgSuccess("åè´§æå"); closeDeliveryDia(); }) .catch(() => { proxy.$modal.msgError("å货失败ï¼è¯·éè¯"); }); } }); }; // å ³éåè´§å¼¹æ¡ const closeDeliveryDia = () => { proxy.resetForm("deliveryFormRef"); deliveryFormVisible.value = false; currentDeliveryContext.value = null; }; onMounted(() => { getList(); }); }; // å ³éåè´§å¼¹æ¡ const closeDeliveryDia = () => { proxy.resetForm("deliveryFormRef"); deliveryFormVisible.value = false; currentDeliveryContext.value = null; }; onMounted(() => { getList(); }); </script> <style scoped lang="scss"> .ml-10 { margin-left: 10px; } .ml-10 { margin-left: 10px; } .table_list { margin-top: unset; } .table_list { margin-top: unset; } .actions { display: flex; justify-content: space-between; margin-bottom: 10px; } .print-preview-dialog { .el-dialog__body { padding: 0; max-height: 80vh; overflow-y: auto; } } .actions { display: flex; justify-content: space-between; margin-bottom: 10px; } .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; .print-preview-container { .print-preview-header { padding: 15px; border-bottom: 1px solid #e4e7ed; text-align: center; .el-button { margin: 0 10px; } } .el-button { margin: 0 10px; } } .print-preview-content { padding: 20px; background-color: #f5f5f5; min-height: 400px; } } .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; } .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; } .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; .header { text-align: center; margin-bottom: 8px; .company-name { font-size: 18px; font-weight: bold; margin-bottom: 4px; } .company-name { font-size: 18px; font-weight: bold; margin-bottom: 4px; } .document-title { font-size: 16px; font-weight: bold; } } .document-title { font-size: 16px; font-weight: bold; } } .info-section { margin-bottom: 8px; display: flex; justify-content: space-between; align-items: center; .info-section { margin-bottom: 8px; display: flex; justify-content: space-between; align-items: center; .info-row { line-height: 20px; .info-row { line-height: 20px; .label { font-weight: bold; width: 60px; font-size: 14px; } .label { font-weight: bold; width: 60px; font-size: 14px; } .value { margin-right: 20px; min-width: 80px; font-size: 14px; } } } .value { margin-right: 20px; min-width: 80px; font-size: 14px; } } } .table-section { margin-bottom: 4px; flex: 1; .table-section { margin-bottom: 4px; flex: 1; .product-table { width: 100%; border-collapse: collapse; border: 1px solid #000; .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, td { border: 1px solid #000; padding: 6px; text-align: center; font-size: 14px; line-height: 1.4; } th { font-weight: bold; } th { font-weight: bold; } .total-label { text-align: right; font-weight: bold; } .total-label { text-align: right; font-weight: bold; } .total-value { 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-section { .footer-row { display: flex; margin-bottom: 3px; line-height: 20px; justify-content: space-between; .footer-item { display: flex; margin-right: 20px; .footer-item { display: flex; margin-right: 20px; .label { font-weight: bold; width: 80px; font-size: 14px; } .label { font-weight: bold; width: 80px; font-size: 14px; } .value { min-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; } @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; } } .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; } } </style>