diff --git a/src/views/repair/tickets/Components/TicketTable.vue b/src/views/repair/tickets/Components/TicketTable.vue index a67e84c..c4fe83d 100644 --- a/src/views/repair/tickets/Components/TicketTable.vue +++ b/src/views/repair/tickets/Components/TicketTable.vue @@ -98,6 +98,7 @@ + @@ -459,16 +545,20 @@ import { setTicketsSettlement, settlementReview, getSettlement, - payConfirm + payConfirm, + setTicketsAntiSettlement } from '@/api/repair/tickets/Tickets' import TicketsShow from "@/views/repair/tickets/Components/TicketsShow.vue"; import {getByNameAndMobile} from "@/api/base/customer"; import EditTickets from "@/views/repair/tickets/form/EditTickets.vue"; import { getChargeCompanyList, addChargeCompany } from '@/views/base/chargeCompany/api/chargeCompanyApi' +import { getAccounts } from '@/views/company/account/api/accountApi' +import { getAccessToken } from "@/utils/auth" +import ImagePreview from '@/components/ImagePreview'; export default { name: "TicketTable", - components: {EditTickets, TicketsShow}, + components: {EditTickets, TicketsShow, ImagePreview}, props: { TicketType: { type: String, @@ -490,48 +580,66 @@ export default { data() { return { loading: false, - showLoading: false, // 添加加载状态 + showLoading: false, formData: { id: null, ticketsStatus: null, billingRemark: null, payType: null, isPaid: '1', - remark: '' + remark: '', + receivablesAccount: null }, + accountList: [], + accountLoading: false, settlementFormData: { actualMoney: 0, discountType: this.defaultDiscountType, discount: 0, - payType: 'xj', // 默认现结 + settlementType: 'xj', chargeCompanyId: null }, - chargeCompanyList: [], // 挂账单位列表 - searchLoading: false, // 搜索加载状态 - // 新增挂账单位表单 + chargeCompanyList: [], + searchLoading: false, chargeCompanyForm: { companyName: '', contactPerson: '', contactPhone: '', address: '', - status: false, // 默认为禁用状态 + status: false, systemCode: 'repair', - remark: '' + remark: '', + file: '' }, + uploadFileList: [], + viewFileUrl: process.env.VUE_APP_FILE_API, + uploadFileUrl: process.env.VUE_APP_BASE_API + "/admin-api/infra/file/upload", + headers: { Authorization: "Bearer " + getAccessToken() }, + uploadingCount: 0, + isShowFile: false, + selectFile: { + fileName: '', + filePath: '', + isImage: false + }, + fileUrl: '', + isSyncingFromParent: false, chargeCompanyRules: { companyName: [{ required: true, message: '单位名称不能为空', trigger: 'blur' }], contactPerson: [{ required: true, message: '联系人不能为空', trigger: 'blur' }], contactPhone: [{ required: true, message: '联系电话不能为空', trigger: 'blur' }] }, - chargeCompanyDialogVisible: false, // 新增挂账单位弹窗显示状态 + chargeCompanyDialogVisible: false, formRules: { payType: [{required: true, message: '支付方式不能为空', trigger: 'blur'}], isPaid: [{required: true, message: '请选择是否支付', trigger: 'blur'}], + receivablesAccount: [{required: true, message: '请选择收款账号', trigger: 'blur'}], remark: [{required: false, message: '收款备注不能为空', trigger: 'blur'}] }, settlementFormRules: { discountType: [{required: true, message: '优惠类型不能为空', trigger: 'blur'}], - discount: [{required: true, message: '优惠不能为空', trigger: 'blur'}] + discount: [{required: true, message: '优惠不能为空', trigger: 'blur'}], + settlementType: [{required: true, message: '结算方式不能为空', trigger: 'blur'}] }, dialogVisible: false, dialogVisibleSettlement: false, @@ -547,7 +655,6 @@ export default { value: '2' } ], - // 确认收款相关数据 confirmPaymentVisible: false, confirmPaymentLoading: false, confirmPaymentSubmitLoading: false, @@ -561,14 +668,14 @@ export default { paidTime: '', payTypeLabel: '', payConfirm: '', - payConfirmRemark: '' + payConfirmRemark: '', + payTime: '' }, confirmPaymentRules: { payConfirm: [ { required: true, message: '请选择确认状态', trigger: 'change' } ] }, - // 确认状态选项 confirmStatusOptions: [ { value: '1', @@ -581,8 +688,29 @@ export default { ] } }, + computed: { + isImageType() { + if (!this.selectFile.fileName) return false; + const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp']; + const extension = this.selectFile.fileName.substring(this.selectFile.fileName.lastIndexOf('.')).toLowerCase(); + return imageExtensions.includes(extension); + }, + isAudioType() { + if (!this.selectFile.fileName) return false; + const audioExtensions = ['.mp3', '.wav', '.ogg', '.flac', '.aac']; + const extension = this.selectFile.fileName.substring(this.selectFile.fileName.lastIndexOf('.')).toLowerCase(); + return audioExtensions.includes(extension); + }, + isPdfType() { + if (!this.selectFile.fileName) return false; + return this.selectFile.fileName.toLowerCase().endsWith('.pdf'); + }, + isTxtType() { + if (!this.selectFile.fileName) return false; + return this.selectFile.fileName.toLowerCase().endsWith('.txt'); + } + }, watch: { - // 监听是否支付状态变化 'formData.isPaid': { handler(newVal) { this.updateFormRules(newVal); @@ -593,14 +721,10 @@ export default { methods: { handleShow(row) { - // 显示加载动画 this.showLoading = true; - // 调用子组件的open方法 this.$refs.ticketsShow.open(row).then(() => { - // 加载完成后隐藏加载动画 this.showLoading = false; }).catch(() => { - // 出错时也隐藏加载动画 this.showLoading = false; }); }, @@ -624,9 +748,26 @@ export default { } catch { } }, - /** - * 打印 - */ + async handleAntiSettlement(row) { + this.$prompt('反结算备注', '提示', { + confirmButtonText: '确定', + cancelButtonText: '取消', + }).then(({value}) => { + this.formData.id = row.id + this.formData['remark'] = value + this.formData.ticketsStatus = "03" + this.doAntiSettlement() + }).catch(() => { + }) + }, + async doAntiSettlement() { + try { + await setTicketsAntiSettlement(this.formData) + this.$modal.msgSuccess("反结算成功") + this.$emit("setAntiSettlement") + } catch { + } + }, async handlePrint(row) { window.open(process.env.VUE_APP_BASE_API + '/admin-api/repair/tickets/print/' + row.id) }, @@ -640,21 +781,17 @@ export default { } }, jisuan() { - // 根据优惠类型计算 if (this.settlementFormData.discountType === '1') { this.settlementFormData.actualMoney = this.settlementFormData.money - this.settlementFormData.discount } else if (this.settlementFormData.discountType === '2') { - // 保留小数点后两位 this.settlementFormData.actualMoney = this.settlementFormData.money * (1 - this.settlementFormData.discount / 100) this.settlementFormData.actualMoney = parseFloat(this.settlementFormData.actualMoney).toFixed(2) } console.log(this.settlementFormData) }, - // 处理支付方式变化 async handlePayTypeChange(value) { if (value === 'gz') { - // 获取启用状态的挂账单位(systemCode为repair) try { const res = await getChargeCompanyList({ status: 1, @@ -668,7 +805,6 @@ export default { } }, - // 搜索挂账单位 async searchChargeCompany(query) { if (query !== '') { this.searchLoading = true; @@ -686,7 +822,6 @@ export default { this.searchLoading = false; } } else { - // 如果搜索关键词为空,重新加载所有启用的挂账单位 try { const res = await getChargeCompanyList({ status: 1, @@ -700,7 +835,6 @@ export default { } }, - //收款 handlePaid(row) { this.formData = { id: null, @@ -708,16 +842,33 @@ export default { billingRemark: null, payType: null, isPaid: '1', - remark: '' + remark: '', + receivablesAccount: null } this.formData['id'] = row.id this.formData['ticketsStatus'] = '02' this.dialogVisible = true this.checkIsHangAccount(row) this.getSettlement(row) + this.loadAccountList() + }, + + async loadAccountList() { + this.accountLoading = true; + try { + const res = await getAccounts({ + status: 1, + systemCode: 'repair' + }); + this.accountList = res.data.records || []; + } catch (err) { + console.error('获取收款账号列表失败:', err); + this.$modal.msgError('获取收款账号列表失败'); + } finally { + this.accountLoading = false; + } }, - //结算 handleSettlement(row, type) { this.settlementType = type if (type == 'js') { @@ -725,8 +876,8 @@ export default { ticketId: row.id, money: row.totalPrice, actualMoney: row.totalPrice, - discountType: this.defaultDiscountType, // 加上这一行 - discount: 0 // 加上这一行 + discountType: this.defaultDiscountType, + discount: 0 } } else if (type === 'jssh') { this.settlementFormData = row.settlement @@ -774,8 +925,8 @@ export default { try { await this.$refs['formRefSettlement'].validate() - if (this.settlementType === 'jssh') { + console.log('执行借宿那审核'); await settlementReview(this.settlementFormData) } else { await setTicketsSettlement(this.settlementFormData) @@ -783,7 +934,8 @@ export default { this.$modal.msgSuccess("提交成功") this.dialogVisibleSettlement = false this.$emit("setVoid") - } catch { + } catch (error) { + console.error('Error in settlement process:', error); } }, getDictDataByCode(code) { @@ -813,7 +965,6 @@ export default { } }, - // 确认收款相关方法 handleConfirmPayment(row) { this.resetConfirmPaymentForm() this.confirmPaymentForm = { @@ -822,17 +973,16 @@ export default { userName: row.userName, carNo: row.carNo, totalPrice: row.totalPrice, - paidAmount: row.paidAmount || row.totalPrice, // 实收金额,默认为应收金额 - paidTime: row.paidTime || '', // 收款时间 - payTypeLabel: this.getPayTypeLabel(row.payType), // 收款方式标签 - payConfirm: row.payConfirm || '', // 确认状态 - payConfirmRemark: row.payConfirmRemark || '', // 确认备注 - payTime: row.payTime || '', // 付款时间 + paidAmount: row.paidAmount || row.totalPrice, + paidTime: row.paidTime || '', + payTypeLabel: this.getPayTypeLabel(row.payType), + payConfirm: row.payConfirm || '', + payConfirmRemark: row.payConfirmRemark || '', + payTime: row.payTime || '', } this.confirmPaymentVisible = true }, - // 获取支付方式标签 getPayTypeLabel(payType) { if (!payType) return '' const payTypes = this.getDictDataByCode(this.DICT_TYPE.REPAIR_PAY_TYPE) @@ -840,55 +990,47 @@ export default { return payTypeItem ? payTypeItem.label : '' }, - // 提交确认收款 async submitConfirmPayment() { this.$refs['confirmPaymentForm'].validate(async valid => { if (valid) { this.confirmPaymentSubmitLoading = true - // 构造提交数据 const data = { id: this.confirmPaymentForm.id, payConfirm: this.confirmPaymentForm.payConfirm, payConfirmRemark: this.confirmPaymentForm.payConfirmRemark } - // 注意:这里需要后端提供对应的API接口 - // 由于没有看到具体的API,暂时使用模拟方式 await payConfirm(data) this.$modal.msgSuccess("确认收款成功") this.confirmPaymentSubmitLoading = false this.confirmPaymentVisible = false - this.$emit("setVoid") // 刷新列表 + this.$emit("setVoid") } }) }, - // 取消确认收款 cancelConfirmPayment() { this.confirmPaymentVisible = false this.$refs['confirmPaymentForm'].resetFields() }, - // 重置确认收款表单 resetConfirmPaymentForm() { this.confirmPaymentSubmitLoading = false this.$refs['confirmPaymentForm'] && this.$refs['confirmPaymentForm'].resetFields() }, - // 根据是否支付状态更新表单验证规则 updateFormRules(isPaid) { if (isPaid === '0') { - // 未支付:收款方式可选,收款备注必填 this.formRules.payType = [{required: false, message: '支付方式不能为空', trigger: 'blur'}]; + this.formRules.receivablesAccount = [{required: false, message: '请选择收款账号', trigger: 'blur'}]; this.formRules.remark = [{required: true, message: '收款备注不能为空', trigger: 'blur'}]; } else { - // 已支付:收款方式必填,收款备注可选 this.formRules.payType = [{required: true, message: '支付方式不能为空', trigger: 'blur'}]; + this.formRules.receivablesAccount = [{required: true, message: '请选择收款账号', trigger: 'blur'}]; this.formRules.remark = [{required: false, message: '收款备注不能为空', trigger: 'blur'}]; } - // 重新设置表单验证规则 this.$nextTick(() => { if (this.$refs.formRef) { this.$refs.formRef.clearValidate(); @@ -896,32 +1038,33 @@ export default { }); }, - // 处理新增挂账单位按钮点击 handleAddChargeCompany() { - // 重置表单 this.chargeCompanyForm = { companyName: '', contactPerson: '', contactPhone: '', address: '', - status: false, // 默认为禁用状态 + status: false, systemCode: 'repair', - remark: '' + remark: '', + file: '' }; + this.uploadFileList = []; this.chargeCompanyDialogVisible = true; }, - // 提交新增挂账单位 async submitChargeCompany() { this.$refs.chargeCompanyFormRef.validate(async (valid) => { if (valid) { try { + const fileField = this.ensureFileField(); + console.log('提交时的file字段值:', fileField); + await addChargeCompany(this.chargeCompanyForm); this.$modal.msgSuccess('挂账单位申请成功,请等待审核'); this.chargeCompanyDialogVisible = false; - // 如果当前是挂账方式,则重新加载挂账单位列表 - if (this.settlementFormData.payType === 'gz') { - await this.searchChargeCompany(''); // 重新加载列表 + if (this.settlementFormData.settlementType === 'gz') { + await this.searchChargeCompany(''); } } catch (err) { console.error('新增挂账单位失败:', err); @@ -931,10 +1074,157 @@ export default { }); }, - // 取消新增挂账单位 cancelChargeCompany() { this.chargeCompanyDialogVisible = false; this.$refs.chargeCompanyFormRef && this.$refs.chargeCompanyFormRef.resetFields(); + this.uploadFileList = []; + }, + + beforeUpload(file) { + const isLt20M = file.size / 1024 / 1024 < 20 + if (!isLt20M) { + this.$message.error('上传文件大小不能超过 20MB!') + return false + } + + if (this.uploadingCount === 0) { + this.$modal.loading("正在上传文件,请稍候...") + } + this.uploadingCount++ + return true + }, + + handleUploadSuccess(res, file, fileList) { + this.uploadingCount-- + + console.log('上传成功响应:', res); + + const originalFileName = file.name || '未知文件名'; + + const index = this.uploadFileList.findIndex(item => item.uid === file.uid) + if (index > -1) { + const filePath = res && res.code === 0 && res.data ? res.data : file.name + + this.uploadFileList[index].url = filePath + this.uploadFileList[index].status = 'success' + this.uploadFileList[index].name = originalFileName + this.uploadFileList[index].fileName = originalFileName + this.uploadFileList[index].filePath = filePath + this.uploadFileList[index].isImage = this.isImageExtension(originalFileName) + } else { + const filePath = res && res.code === 0 && res.data ? res.data : file.name + this.uploadFileList.push({ + uid: file.uid, + name: originalFileName, + fileName: originalFileName, + url: filePath, + filePath: filePath, + status: 'success', + isImage: this.isImageExtension(originalFileName) + }) + } + + this.updateFilePaths() + + if (this.uploadingCount === 0) { + this.$modal.closeLoading() + } + }, + + handleUploadError(err, file, fileList) { + this.uploadingCount-- + this.$message.error('文件上传失败,请重试') + + if (this.uploadingCount === 0) { + this.$modal.closeLoading() + } + }, + + handleRemove(file, fileList) { + this.uploadFileList = fileList + this.updateFilePaths() + }, + + updateFilePaths() { + const paths = this.uploadFileList + .filter(file => file && (file.status === 'success' || file.url)) + .map(file => { + const filePath = file.url || file.name || '' + + if (filePath && this.viewFileUrl && filePath.includes(this.viewFileUrl)) { + return filePath.replace(this.viewFileUrl, '') + } + return filePath + }) + + const filePathStr = paths.join(',') || '' + this.chargeCompanyForm.file = filePathStr + + console.log('更新后的file字段值:', filePathStr) + }, + + isImageExtension(fileName) { + if (!fileName) return false; + const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp']; + const extension = fileName.substring(fileName.lastIndexOf('.')).toLowerCase(); + return imageExtensions.includes(extension); + }, + + getPreviewFilePath(file) { + if (!file || !file.filePath && !file.url) return ''; + + const filePath = file.filePath || file.url || ''; + if (filePath.startsWith('http://') || filePath.startsWith('https://')) { + return filePath; + } + return `${this.viewFileUrl || process.env.VUE_APP_BASE_API}${filePath}`; + }, + + previewFile(file) { + console.log('预览文件:', file); + this.selectFile = { + fileName: file.name || file.fileName || '未知文件', + filePath: file.url || file.filePath || '', + isImage: file.isImage || this.isImageExtension(file.name || file.fileName || '') + }; + + if (!this.isImageExtension(this.selectFile.fileName) && !this.isPdfType && !this.isTxtType && !this.isAudioType) { + const fileUrl = this.getPreviewFilePath(this.selectFile); + this.fileUrl = `https://view.officeapps.live.com/op/view.aspx?src=${encodeURIComponent(fileUrl)}`; + } + + this.isShowFile = true; + }, + + toggleFullScreen() { + const container = this.$refs.previewContainer; + if (!container) return; + + if (!document.fullscreenElement) { + container.requestFullscreen().catch(err => { + console.error(`全屏切换失败: ${err.message}`); + }); + } else { + if (document.exitFullscreen) { + document.exitFullscreen(); + } + } + }, + + extractFileName(path) { + if (!path) return '未知文件'; + let cleanPath = path; + if (cleanPath.includes('http://') || cleanPath.includes('https://')) { + const urlObj = new URL(cleanPath); + cleanPath = urlObj.pathname; + } + const parts = cleanPath.split('/'); + return parts[parts.length - 1] || '未知文件'; + }, + + ensureFileField() { + this.updateFilePaths() + return this.chargeCompanyForm.file } } } @@ -966,4 +1256,28 @@ export default { color: #409EFF; margin-bottom: 10px; } - + /* 文件预览样式 */ + .preview-container { + position: relative; + width: 100%; + height: 600px; + overflow: auto; + } + + .preview-iframe { + width: 100%; + height: 100%; + border: none; + } + + .fullscreen-btn { + background: #409EFF; + color: white; + border: none; + } + + .fullscreen-btn:hover { + background: #66b1ff; + color: white; + } + \ No newline at end of file