Compare commits

..

No commits in common. "dev_1.0.3" and "master" have entirely different histories.

604 changed files with 2528 additions and 92585 deletions

View File

@ -1,139 +0,0 @@
你是一个资深 Java 后端工程师,请在当前 Spring BootJava8项目中实现一个完整的 MCP Server并加入“AI 自动选 ToolRAG + Tool Routing”能力。
# 一、基础 MCP 能力(必须实现)
1. 提供统一入口:
POST /mcp
2. 实现 JSON-RPC 2.0
* initialize
* tools/list
* tools/call
3. 返回格式:
{
"jsonrpc": "2.0",
"id": "...",
"result": ...
}
# 二、流式能力(必须)
1. 禁止使用 SseEmitter
2. 使用 HttpServletResponse + OutputStream
3. 实现 chunked streamingwrite + flush
# 三、Tool 注册中心(必须)
实现:
* McpToolRegistry
* register / get / list
Tool结构
* name
* description非常重要用于向量检索
* inputSchema
* handler
# 四、RAG Tool 检索模块(新增重点)
实现一个 ToolRetriever 组件:
1. 构建向量索引(简化实现):
* 使用内存存储 List<ToolEmbedding>
* ToolEmbedding:
* toolName
* description
* embedding(double[])
2. 提供方法:
List<McpTool> retrieve(String query, int topK)
3. embedding 实现(简化):
* 可使用:
* 简单 TF-IDF
* 或 mock embedding字符串相似度
* 不依赖外部服务(保证可运行)
# 五、Tool Router核心
实现 ToolRouter
流程:
1. 接收用户问题 query
2. 调用 ToolRetriever → 获取 topK tools
3. 构建 prompt只包含候选 tools
4. 返回候选 tools 给上层(或直接选)
要求:
* 不允许返回全部 tools
* 默认 topK = 3~5
# 六、tools/call 升级(关键)
当 method = tools/call 时:
1. 如果请求未指定 tool
* 自动触发 ToolRouter
* 选出最优 tool
2. 如果指定 tool
* 直接执行
# 七、默认内置工具
实现:
1. hello
输入name
输出hello xxx
2. meeting.summary
输入text
输出:模拟总结
# 八、架构分层(必须)
* controller
* service
* registry
* retriever新增
* router新增
# 九、扩展能力(必须预留)
1. 支持替换为真实向量数据库(如 Milvus / ES
2. 支持 embedding APIOpenAI / 本地模型)
3. 支持多 MCP Server未来扩展
# 十、输出要求
1. 输出完整代码:
* McpController
* McpService
* McpToolRegistry
* ToolRetriever
* ToolRouter
* ToolEmbedding
* ToolInitializer
* McpRequest
2. 代码必须:
* 可运行
* 无伪代码
* 包含必要注释
* 代码生成目录在ruoyi-sip/src/main/java/com/ruoyi/sip/llm
3. 不要解释,只输出代码

View File

@ -6,4 +6,3 @@ ENV = 'production'
# 若依管理系统/生产环境
VUE_APP_BASE_API = '/prod-api'
VUE_APP_SERVICE_HOST = 'wb.oms.unisspace.com'

View File

@ -28,20 +28,18 @@
"axios": "0.28.1",
"clipboard": "2.0.8",
"core-js": "3.37.1",
"decimal.js": "10.4.2",
"echarts": "5.4.0",
"element-ui": "2.15.14",
"file-saver": "2.0.5",
"fuse.js": "6.4.3",
"highlight.js": "9.18.5",
"html2canvas": "^1.4.1",
"js-beautify": "1.13.0",
"js-cookie": "3.0.1",
"jsencrypt": "3.0.0-rc.1",
"jspdf": "^2.5.1",
"jszip": "^3.10.1",
"nprogress": "0.2.0",
"quill": "2.0.2",
"html2canvas": "^1.4.1",
"jspdf": "^2.5.1",
"screenfull": "5.0.2",
"sortablejs": "1.10.2",
"splitpanes": "2.4.1",
@ -50,7 +48,8 @@
"vue-cropper": "0.5.5",
"vue-router": "3.4.9",
"vuedraggable": "2.24.3",
"vuex": "3.6.0"
"vuex": "3.6.0",
"decimal.js": "10.4.2"
},
"devDependencies": {
"@vue/cli-plugin-babel": "4.4.6",

View File

@ -1,29 +0,0 @@
import request from '@/utils/request'
// 查询审批任务列表
export function listApprovalTask(query) {
const { pageNum, pageSize, orderByColumn, isAsc, ...body } = query
return request({
url: '/approve/task/vue/list',
method: 'post',
params: { pageNum, pageSize, orderByColumn, isAsc },
data: body
})
}
// 获取审批任务数量统计
export function getApprovalTaskTotal() {
return request({
url: '/approve/task/vue/total',
method: 'get'
})
}
// 新增流程确认记录
export function addTodoConfirm(data) {
return request({
url: '/approve/task/vue/confirm',
method: 'post',
data
})
}

View File

@ -1,50 +0,0 @@
import request from '@/utils/request'
// 查询报价单列表
export function listQuotation(query) {
return request({
url: '/quotation/list',
method: 'get',
params: query
})
}
// 查询报价单详细
export function getQuotation(id) {
return request({
url: '/quotation/' + id,
method: 'get'
})
}
// 新增报价单
export function addQuotation(data) {
return request({
url: '/quotation/insert',
method: 'post',
data: data
})
}
// 修改报价单
export function updateQuotation(data) {
return request({
url: '/quotation/update',
method: 'put',
data: data
})
}
// 删除报价单
export function delQuotation(id) {
return request({
url: '/quotation/remove/batch/' + id,
method: 'delete'
})
}
export function exportSingleQuotation(id) {
return request({
url: '/quotation/export/single/' + id,
method: 'get'
})
}

View File

@ -1,5 +1,4 @@
import request from '@/utils/request'
import { tansParams } from '@/utils/ruoyi'
// 查询制造商信息列表
export function listVendor(query) {
@ -49,9 +48,7 @@ export function exportVendor(query) {
return request({
url: '/system/vendor/export',
method: 'post',
data: query,
transformRequest: [(params) => tansParams(params)],
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
params: query
})
}

View File

@ -1,10 +0,0 @@
import request from '@/utils/request'
// 按SN码查询库存数据
export function listInventoryInfoByProductSn(query) {
return request({
url: '/sip/dataProcess/inventoryInfoByProductSn',
method: 'get',
params: query
})
}

View File

@ -1,22 +0,0 @@
import request from '@/utils/request'
// 查询项目管理列表
export function listProject(query) {
return request({
url: '/sip/project/vue/list',
method: 'post',
data: query,
headers: { 'Content-Type': 'multipart/form-data' },
})
}
// 项目转移负责人
export function transferProject(data) {
return request({
url: '/sip/dataProcess/projectTransfer',
method: 'post',
data: data
})
}

View File

@ -1,72 +0,0 @@
import request from '@/utils/request'
// 查询财务计收列表
export function listCharge(query) {
return request({
url: '/finance/charge/list',
method: 'get',
params: query
})
}
// 查询财务计收详细
export function getCharge(id) {
return request({
url: '/finance/charge/' + id,
method: 'get'
})
}
// 新增财务计收
export function addCharge(data) {
return request({
url: '/finance/charge',
method: 'post',
data: data
})
}
// 修改财务计收
export function updateCharge(data) {
return request({
url: '/finance/charge',
method: 'put',
data: data
})
}
export function revokeCharge(data) {
return request({
url: '/finance/charge/revoke',
method: 'put',
data: data
})
}
// 删除财务计收
export function delCharge(id) {
return request({
url: '/finance/charge/' + id,
method: 'delete'
})
}
export function applyChargeBiz(data) {
return request({
url: '/finance/charge/applyBiz' ,
method: 'put',
data
})
}
export function returnApplyBiz(data) {
return request({
url: '/finance/charge/returnApplyBiz' ,
method: 'put',
data
})
}
export function applyCharge(data) {
return request({
url: '/finance/charge/apply' ,
method: 'put',
data
})
}

View File

@ -1,136 +0,0 @@
import request from '@/utils/request'
import {tansParams} from "@/utils/ruoyi"
// 查询销售收票单列表
export function listInvoice(query) {
return request({
url: '/finance/invoice/list',
method: 'post',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
params: query
})
}
// 查询销售收票单详细
export function getInvoice(id) {
return request({
url: '/finance/invoice/' + id,
method: 'get'
})
}
// 查询销售收票单附件
export function getInvoiceAttachments(id, params) {
return request({
url: `/finance/invoice/attachment/${id}`,
method: 'get',
params
})
}
// 上传销售收票单附件
export function uploadInvoiceAttachment(data) {
return request({
url: '/finance/invoice/uploadReceipt',
method: 'post',
headers: {
'Content-Type': 'multipart/form-data'
},
data: data,
needLoading: true
});
}
// 申请红冲
export function redRush(id) {
return request({
url: '/finance/invoice/applyRefund/' + id,
method: 'get'
})
}
// 申请红冲 (提交表单)
export function applyRefund(data) {
return request({
url: '/finance/invoice/applyRefund',
method: 'post',
data: data
})
}
// 退回销售收票单
export function returnInvoice(id) {
return request({
url: '/finance/invoice/return/' + id,
method: 'delete'
})
}
// 新增销售收票单
export function addInvoice(data) {
return request({
url: '/finance/receivable/mergeAndInitiateInvoice',
method: 'post',
data: data,
needLoading: true
})
}
// 查询销售收票单产品明细
export function getInvoiceProducts(code) {
return request({
url: '/finance/invoice/product/' + code,
method: 'get'
})
}
// 申请开票
export function applyInvoice(data) {
return request({
url: '/finance/invoice/apply',
method: 'post',
data: data
})
}
// 撤销销售收票单
export function revokeInvoice(id) {
return request({
url: '/finance/invoice/revoke/' + id,
method: 'delete'
})
}
// 查询开票审批列表
export function listInvoiceApprove(query) {
return request({
url: '/finance/invoice/approve/list',
method: 'post',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: query
})
}
// 查询已审批开票列表
export function listInvoiceApproved(query) {
return request({
url: '/finance/invoice/approved/list',
method: 'post',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: query
})
}
// 查询开票单详细
export function getInvoiceDetail(id) {
return request({
url: '/finance/invoice/' + id,
method: 'get'
})
}

View File

@ -1,36 +0,0 @@
import request from '@/utils/request'
// 查询收票待审批列表
export function listInvoiceReceiptApprove(query) {
return request({
url: '/finance/ticket/approve/list',
method: 'post',
data: query
})
}
// 查询收票已审批列表
export function listInvoiceReceiptApproved(query) {
return request({
url: '/finance/ticket/approved/list',
method: 'post',
data: query
})
}
// 查询收票详情
export function getInvoiceReceipt(id) {
return request({
url: '/finance/ticket/' + id,
method: 'get'
})
}
// 查询收票附件
export function getInvoiceReceiptAttachments(id,params) {
return request({
url: '/finance/ticket/attachment/' + id,
method: 'get',
params
})
}

View File

@ -1,26 +0,0 @@
import request from '@/utils/request'
// 查询核销记录列表
export function listInvoiceWriteOff(query) {
return request({
url: '/finance/invoice/writeoff/list',
method: 'get',
params: query
})
}
// 查询核销详情
export function getInvoiceWriteoff(id) {
return request({
url: '/finance/invoice/writeoff/' + id,
method: 'get'
})
}
// 删除核销记录
export function delInvoiceWriteoff(ids) {
return request({
url: '/finance/invoice/writeoff/' + ids,
method: 'delete'
})
}

View File

@ -1,81 +0,0 @@
import request from '@/utils/request'
// 查询采购应付单列表
export function listPayable(query) {
return request({
url: '/finance/payable/list',
method: 'post',
headers: { 'Content-Type': 'multipart/form-data' },
data: query
})
}
// 查询采购应付单详情
export function getPayable(id) {
return request({
url: '/finance/payable/' + id,
method: 'get'
})
}
// 查询付款计划列表
export function getPaymentPlan(payableBillId) {
return request({
url: `/finance/payable/plan/${payableBillId}`,
method: 'get'
})
}
// 更新付款计划
export function updatePaymentPlan(payableBillId, data) {
return request({
url: `/finance/payable/plan/${payableBillId}`,
method: 'post',
data: data
})
}
// 合并并发起付款
export function mergeAndInitiatePayment(data) {
return request({
url: '/finance/payable/mergeAndInitiatePayment',
method: 'post',
data: data,
needLoading: true
})
}
// 合并并发起收票
export function mergeAndInitiateReceipt(data) {
return request({
url: '/finance/payable/mergeAndInitiateReceipt',
method: 'post',
data: data,
needLoading: true
})
}
// [PLACEHOLDER] 查询收票计划列表 - Endpoint to be confirmed by user
export function getReceivingTicketPlan(payableBillId) {
return request({
url: `/finance/ticket/plan/${payableBillId}`,
method: 'get'
})
}
// [PLACEHOLDER] 更新收票计划 - Endpoint to be confirmed by user
export function updateReceivingTicketPlan(payableBillId, data) {
return request({
url: `/finance/ticket/plan/${payableBillId}`,
method: 'post',
data: data
})
}
// 同步付款计划至发票计划
export function syncToTicketPlan(payableBillId) {
return request({
url: `/finance/payable/plan/sync/${payableBillId}`,
method: 'post'
})
}

View File

@ -1,210 +0,0 @@
import request from '@/utils/request'
import { tansParams } from "@/utils/ruoyi"
// 查询付款单列表
export function listPayment(query) {
return request({
url: '/finance/payment/list',
method: 'post',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: tansParams(query)
})
}
export function exportPayment(query) {
return request({
url: '/finance/payment/export',
method: 'post',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: tansParams(query)
})
}
// 查询付款单详细
export function getPayment(id) {
return request({
url: '/finance/payment/' + id,
method: 'get'
})
}
// 查询付款单附件
export function getPaymentAttachments(id, params) {
return request({
url: `/finance/payment/attachment/${id}`,
method: 'get',
params
})
}
export function deleteFile(id) {
return request({
url: `/finance/payment/attachment/${id}`,
method: 'delete'
})
}
// 上传付款单附件
export function uploadPaymentAttachment(data) {
return request({
url: '/finance/payment/uploadReceipt',
method: 'post',
headers: {
'Content-Type': 'multipart/form-data'
},
data: data
});
}
// 退回付款单
export function returnPayment(id) {
return request({
url: '/finance/payment/returnPayment/' + id,
method: 'delete'
})
}
// 新增付款单
export function addPaymentFromPayable(data) {
return request({
url: '/finance/payable/mergeAndInitiatePayment',
method: 'post',
data: data,
needLoading: true
})
}
export function addPayment(data) {
return request({
url: '/finance/payment/add',
method: 'post',
data: data,
needLoading: true
})
}
export function handleRevoke(id) {
return request({
url: '/finance/payment/revoke',
method: 'post',
data: {id: id},
needLoading: true
})
}
// 申请付款
export function applyPaymentApi(data) {
return request({
url: '/finance/payment/applyPayment',
method: 'post',
data: data,
needLoading: true
})
}
// 申请退款
export function applyRefund(data) {
return request({
url: '/finance/payment/applyRefund',
method: 'post',
data: data,
needLoading: true
})
}
export function deletePayment(id) {
return request({
url: '/finance/payment/remove',
method: 'post',
params:{ids:id},
needLoading: true
})
}
export function applyRefundApprove(id) {
return request({
url: '/finance/payment/applyRefundApprove',
method: 'post',
data: {id: id},
needLoading: true
})
}
// 查询应付单列表 (用于新增付款单-非预付)
export function listPayableBills(query) {
return request({
url: 'finance/payable/list',
method: 'post',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: query
})
}
// 查询采购订单列表 (用于新增付款单-预付)
export function listOrders(query) {
return request({
url: '/project/order/list',
method: 'post',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: query
})
}
// 查询付款审批列表
export function listPaymentApprove(query) {
return request({
url: '/finance/payment/approve/list',
method: 'post',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: tansParams(query)
})
}
// 查询已审批付款列表
export function listPaymentApproved(query) {
return request({
url: '/finance/payment/approved/list',
method: 'post',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: tansParams(query)
})
}
// 查询付款单列表 (核销专用)
export function listPaymentForWriteOff(query) {
return request({
url: '/finance/payment/write-off/list',
method: 'post',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: tansParams(query)
})
}
// 手工匹配核销
export function manualWriteOff(data) {
return request({
url: '/finance/writeoff',
method: 'post',
data: data
})
}
// 新增付款单
export function updatePaymentBill(data) {
return request({
url: '/finance/payment/updatePaymentBill',
method: 'post',
data: data,
needLoading: true
})
}

View File

@ -1,34 +0,0 @@
import request from '@/utils/request'
import {tansParams} from "@/utils/ruoyi";
// 查询付款退款待审批列表
export function listPaymentRefundApprove(query) {
return request({
url: '/finance/payment/approve/list',
method: 'post',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: query
})
}
// 查询付款退款已审批列表
export function listPaymentRefundApproved(query) {
return request({
url: '/finance/payment/approved/list',
method: 'post',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
params: query
})
}
// 查询付款退款详情
export function getPaymentRefund(id) {
return request({
url: '/finance/paymentRefund/' + id,
method: 'get'
})
}

View File

@ -1,122 +0,0 @@
import request from '@/utils/request'
import {tansParams} from "@/utils/ruoyi"
// 查询收票单列表
export function listReceipt(query) {
return request({
url: '/finance/ticket/list',
method: 'post',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: query
})
}
// 查询收票单详细
export function getReceipt(id) {
return request({
url: '/finance/ticket/' + id,
method: 'get'
})
}
// 查询收票单附件
export function getReceiptAttachments(id, params) {
return request({
url: `/finance/ticket/attachment/${id}`,
method: 'get',
params
})
}
// 上传收票单附件
export function uploadReceiptAttachment(data) {
return request({
url: '/finance/ticket/uploadReceipt',
method: 'post',
headers: {
'Content-Type': 'multipart/form-data'
},
data: data,
needLoading: true
});
}
// 退回收票单
export function redRush(id) {
return request({
url: '/finance/ticket/applyRefund/' + id,
method: 'get'
})
}
export function returnReceipt(id) {
return request({
url: '/finance/ticket/return/' + id,
method: 'delete'
})
}
// 新增收票单
export function addReceipt(data) {
return request({
url: '/finance/payable/mergeAndInitiateReceipt',
method: 'post',
data: data,
needLoading: true
})
}
// 撤销收票单
export function revokeReceipt(id) {
return request({
url: '/finance/ticket/revoke/' + id,
method: 'put'
})
}
// 申请红冲(带附件)
export function applyRedRush(data) {
return request({
url: '/finance/ticket/applyRefund',
method: 'post',
headers: {
'Content-Type': 'multipart/form-data'
},
data: data,
needLoading: true
})
}
// 查询收款审批列表
export function listReceiptApprove(query) {
return request({
url: '/finance/receipt/approve/list',
method: 'post',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: query
})
}
// 查询已审批收款列表
export function listReceiptApproved(query) {
return request({
url: '/finance/receipt/approved/list',
method: 'post',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: query
})
}
// 查询收款单详细
export function getReceiptDetail(id) {
return request({
url: '/finance/receipt/' + id,
method: 'get'
})
}

View File

@ -1,81 +0,0 @@
import request from '@/utils/request'
// 查询销售应收单列表
export function listReceivable(query) {
return request({
url: '/finance/receivable/list',
method: 'post',
headers: { 'Content-Type': 'multipart/form-data' },
data: query
})
}
// 查询销售应收单详情
export function getReceivable(id) {
return request({
url: '/finance/receivable/' + id,
method: 'get'
})
}
// 查询收款计划列表
export function getReceiptPlan(receivableBillId) {
return request({
url: `/finance/receivable/plan/${receivableBillId}`,
method: 'get'
})
}
// 更新收款计划
export function updateReceiptPlan(receivableBillId, data) {
return request({
url: `/finance/receivable/plan/${receivableBillId}`,
method: 'post',
data: data
})
}
// 合并并发起收款
export function mergeAndInitiateReceipt(data) {
return request({
url: '/finance/receivable/mergeAndInitiateReceipt',
method: 'post',
data: data,
needLoading:true
})
}
// 合并并发起开票
export function mergeAndInitiateInvoice(data) {
return request({
url: '/finance/receivable/mergeAndInitiateInvoice',
method: 'post',
data: data,
needLoading:true
})
}
// 查询开票计划列表
export function getInvoicePlan(receivableBillId) {
return request({
url: `/finance/receivable/invoice/plan/${receivableBillId}`,
method: 'get'
})
}
// 更新开票计划
export function updateInvoicePlan(receivableBillId, data) {
return request({
url: `/finance/receivable/invoice/plan/${receivableBillId}`,
method: 'post',
data: data
})
}
// 同步收款计划至开票计划
export function syncToInvoicePlan(receivableBillId) {
return request({
url: `/finance/receivable/plan/sync/${receivableBillId}`,
method: 'post'
})
}

View File

@ -1,26 +0,0 @@
import request from '@/utils/request'
// 查询销售核销记录列表
export function listReceivableWriteOff(query) {
return request({
url: '/finance/receivable/writeoff/list',
method: 'get',
params: query
})
}
// 查询销售核销详情
export function getReceivableWriteOff(id) {
return request({
url: '/finance/receivable/writeoff/' + id,
method: 'get'
})
}
// 删除销售核销记录
export function delReceivableWriteOff(ids) {
return request({
url: '/finance/receivable/writeoff/' + ids,
method: 'delete'
})
}

View File

@ -1,130 +0,0 @@
import request from '@/utils/request'
import {tansParams} from "@/utils/ruoyi"
// 查询收款单列表
export function listReceive(query) {
return request({
url: '/finance/receipt/list',
method: 'post',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: query
})
}
// 查询收款单详细
export function getReceive(id) {
return request({
url: '/finance/receipt/' + id,
method: 'get'
})
}
// 查询收款单附件
export function getReceiveAttachments(id, params) {
return request({
url: `/finance/receipt/attachment/${id}`,
method: 'get',
params
})
}
// 上传收款单附件
export function uploadReceiveAttachment(data) {
return request({
url: '/finance/receipt/uploadReceipt',
method: 'post',
headers: {
'Content-Type': 'multipart/form-data'
},
data: data,
needLoading: true
});
}
// 申请红冲
export function redRush(id) {
return request({
url: '/finance/receipt/applyRefund/' + id,
method: 'get'
})
}
// 退回收款单
export function returnReceive(id) {
return request({
url: '/finance/receipt/returnReceivable/' + id,
method: 'delete'
})
}
// 新增收款单 (Calls Receivable Merge Logic)
export function mergeReceivable(data) {
return request({
url: '/finance/receivable/mergeAndInitiateReceipt',
method: 'post',
data: data,
needLoading: true
})
}
export function addReceipt(data) {
return request({
url: '/finance/receipt/insert',
method: 'post',
data: data,
needLoading: true
})
}
export function applyReceipt(data) {
return request({
url: '/finance/receipt/applyReceipt',
method: 'post',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: data,
needLoading: true
})
}
// 申请退款
export function submitRefund(data) {
return request({
url: '/finance/receipt/applyRefund',
method: 'post',
data: data,
needLoading: true
})
}
// 查询收款单列表 (核销专用)
export function listReceiptForWriteOff(query) {
return request({
url: '/finance/receipt/write-off/list',
method: 'post',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: tansParams(query)
})
}
// 手工匹配核销
export function manualReceiptWriteOff(data) {
return request({
url: '/finance/receivable/writeoff',
method: 'post',
data: data
})
}
// 撤销收款单
export function revokeReceipt(id) {
return request({
url: '/finance/receipt/revoke',
method: 'post',
data: {id: id},
needLoading: true
})
}

View File

@ -1,21 +0,0 @@
import request from '@/utils/request'
// 查询项目报表列表
export function listReport(query) {
return request({
url: '/finance/report/list',
method: 'post',
headers: { 'Content-Type': 'multipart/form-data' },
data: query
})
}
// 查询供货商维度报表列表
export function listSupplierReport(query) {
return request({
url: '/finance/report/listSupplier',
method: 'post',
headers: { 'Content-Type': 'multipart/form-data' },
data: query
})
}

View File

@ -1,45 +0,0 @@
import request from '@/utils/request'
// 查询核销记录列表
export function listWriteOff(query) {
return request({
url: '/finance/writeoff/list',
method: 'get',
params: query
})
}
export function listTicketWriteOff(query) {
return request({
url: '/finance/ticketWriteoff/list',
method: 'get',
params: query
})
}
// 查询核销详情
export function getWriteOff(id) {
return request({
url: '/finance/writeoff/' + id,
method: 'get'
})
}
export function getTicketWriteoff(id) {
return request({
url: '/finance/ticketWriteoff/' + id,
method: 'get'
})
}
// 删除核销记录
export function delWriteOff(ids) {
return request({
url: '/finance/writeoff/' + ids,
method: 'delete'
})
}
export function delTicketWriteoff(ids) {
return request({
url: '/finance/ticketWriteoff/' + ids,
method: 'delete'
})
}

View File

@ -17,14 +17,6 @@ export function listCompletedFlows(data) {
data: data
})
}
// 按业务单号查询待办记录(查看申请详情用)
export function listApplyFlows(data) {
return request({
url: '/flow/todo/apply-list',
method: 'post',
data: data
})
}
// 通用审批
export function approveTask(data) {
return request({

View File

@ -52,15 +52,6 @@ export function recallDelivery(id) {
})
}
// 提交出库撤回审批(跨天发货记录)
export function recallApply(id, reason, amountChanged) {
return request({
url: '/inventory/delivery/vue/recall/apply',
method: 'post',
data: {id, reason, amountChanged}
})
}
// 导出采购合同
export function exportDelivery(query) {
return request({

View File

@ -71,23 +71,6 @@ export function recallExecution(id) {
})
}
// 提交撤单审批申请
export function recallExecutionApply(id, reason, amountChanged) {
return request({
url: '/inventory/execution/vue/recall/apply',
method: 'post',
data: { id, reason, amountChanged }
})
}
export function exportExecution(data) {
return request({
url: `/inventory/execution/vue/export`,
method: 'post',
data: { data },
headers: { 'Content-Type': 'multipart/form-data' },
})
}
// 查询出库预览信息
export function getCheckOutPreview(data) {
return request({

View File

@ -85,30 +85,3 @@ export function getOuterLog(query) {
params: query
})
}
// 查询在途库存
export function getPurchaseStock(query) {
return request({
url: '/inventory/info/vue/purchase-stock',
method: 'get',
params: query
})
}
// 查询锁单
export function getBindOrder(query) {
return request({
url: '/inventory/info/vue/bind-order',
method: 'get',
params: query
})
}
// 查询在库现存
export function getInventoryInner(query) {
return request({
url: '/inventory/info/vue/inventory-inner',
method: 'get',
params: query
})
}

View File

@ -19,15 +19,6 @@ export function getInner(id) {
})
}
// 查询入库单产品信息列表
export function listInnerInventoryInfo(id, query) {
return request({
url: `${VUE_APP_API_URL}/${id}/inventory-info/list`,
method: 'get',
params: query
})
}
// 新增入库单信息
export function addInner(data) {
return request({

View File

@ -79,14 +79,6 @@ export function exportDownloadTemplate() {
method: 'get'
})
}
// 下载带列表数据的发货导入模板
export function exportDownloadTemplateData(data) {
return request({
url: '/inventory/outer/importTemplateData',
method: 'post',
data: data
})
}
export function importSnData(formData) {
return request({
url: '/inventory/outer/vue/importData',
@ -97,13 +89,3 @@ export function importSnData(formData) {
data: formData
})
}
export function assignDeliverySnData(formData) {
return request({
url: '/inventory/outer/vue/assignDeliveryData',
method: 'post',
headers: {
'Content-Type': 'multipart/form-data'
},
data: formData
})
}

View File

@ -1,11 +1,5 @@
import request from '@/utils/request'
function createLoginFormData(data) {
const formData = new FormData()
Object.keys(data).forEach(key => formData.append(key, data[key]))
return formData
}
// 登录方法 - 基于 Session 认证,使用 FormData 格式
export function login(username, password, code, rememberMe) {
const formData = new FormData()
@ -15,40 +9,6 @@ export function login(username, password, code, rememberMe) {
formData.append('rememberMe', rememberMe || false)
return request({
url: '/login',
headers: {
isToken: false,
'Content-Type': 'multipart/form-data',
repeatSubmit: false,
allowWarning: true
},
method: 'post',
data: formData
})
}
export function resetLoginPwd(data) {
const formData = createLoginFormData({
username: data.username,
newPassword: data.newPassword,
confirmPassword: data.confirmPassword,
emailCode: data.emailCode
})
return request({
url: '/login/resetPwd',
headers: {
isToken: false,
'Content-Type': 'multipart/form-data',
repeatSubmit: false
},
method: 'post',
data: formData
})
}
export function sendResetPwdEmailCode(username) {
const formData = createLoginFormData({ username })
return request({
url: '/login/sendResetPwdEmailCode',
headers: {
isToken: false,
'Content-Type': 'multipart/form-data',

View File

@ -49,7 +49,7 @@ export function delOrder(id) {
export function exportOrder(query) {
return request({
url: `${baseURL}/export`,
method: 'get',
method: 'post',
params: query
})
}

View File

@ -1,40 +1,28 @@
import request from '@/utils/request'
function normalizeSerialNumbers(input) {
const values = Array.isArray(input) ? input : [input]
return [...new Set(values
.map(item => (item || '').toString().trim())
.filter(Boolean))]
}
function buildQueryPayload(input) {
const serialNumbers = normalizeSerialNumbers(input)
return {
serialNumber: serialNumbers[0] || '',
serialNumbers
}
}
export function getProductInfo(serialNumberOrNumbers) {
// 查询产品信息
export function getProductInfo(serialNumber) {
return request({
url: '/manage/service/product',
method: 'post',
data: buildQueryPayload(serialNumberOrNumbers)
method: 'get',
params: { serialNumber }
})
}
export function getOrderInfo(serialNumberOrNumbers) {
// 查询相关合同信息
export function getOrderInfo(serialNumber) {
return request({
url: '/manage/service/order',
method: 'post',
data: buildQueryPayload(serialNumberOrNumbers)
method: 'get',
params: { serialNumber }
})
}
export function getWarrantyInfo(serialNumberOrNumbers) {
// 查询标准保修信息
export function getWarrantyInfo(serialNumber) {
return request({
url: '/manage/service/query',
method: 'post',
data: buildQueryPayload(serialNumberOrNumbers)
method: 'get',
params: { serialNumber }
})
}

View File

@ -1,14 +1,11 @@
import request from '@/utils/request'
import { tansParams } from '@/utils/ruoyi'
// 查询项目管理列表
export function listProject(query) {
return request({
url: '/sip/project/vue/list',
method: 'post',
data: query,
headers: { 'Content-Type': 'multipart/form-data' },
method: 'get',
params: query
})
}
@ -52,34 +49,6 @@ export function exportProject(query) {
return request({
url: '/sip/project/vue/export',
method: 'post',
data: query,
transformRequest: [(params) => tansParams(params)],
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
params: query
})
}
// 项目收藏
export function addCollect(data) {
return request({
url: '/project/collect/add',
method: 'post',
data: data,
needLoading:true
})
}
export function editJoinTrial(data) {
return request({
url: '/sip/project/vue/joinTrial',
method: 'put',
data: data,
needLoading:true
})
}
export function exportSingle(id) {
return request({
url: `/sip/project/vue/joinTrial/export/${id}`,
method: 'get',
needLoading:true
})
}

View File

@ -85,48 +85,3 @@ export function updateFinanceStatus(id, status) {
data: data
})
}
// 查询产品配货备货明细
export function productMatchList(orderCode) {
return request({
url: '/project/order/vue/productMatchList',
method: 'get',
params: { orderCode }
})
}
// 查询产品配备货绑定信息
export function productMatchBindList(orderCode, productCode, params = {}) {
return request({
url: '/project/order/vue/productMatchBindList',
method: 'get',
params: { orderCode, productCode, ...params }
})
}
// 保存订单关联采购单
export function savePurchaseOrderMap(data) {
return request({
url: '/project/order/vue/purchaseOrderMap',
method: 'post',
data
})
}
// 查询采购单SN码列表
export function purchaseSnList(purchaseNo, orderCode, productCode) {
return request({
url: '/project/order/vue/purchaseSnList',
method: 'get',
params: { purchaseNo, orderCode, productCode }
})
}
// 订单绑定SN码
export function bindOrderSnCodes(orderCode, productCode, purchaseNo, orderId, purchaseId, productSnList) {
return request({
url: '/project/order/vue/bindOrderSnCodes',
method: 'post',
data: { orderCode, productCode, purchaseNo, orderId, purchaseId, productSnList }
})
}

View File

@ -106,16 +106,6 @@ export function recallPurchaseorder(id) {
method: 'put'
})
}
export function exportPurchaseorder(data) {
return request({
url: '/sip/purchaseorder/export',
method: 'get',
params: data,
// headers: { 'Content-Type': 'multipart/form-data' },
needLoading: true
})
}
// 查询已审批采购单主表列表
export function listApprovedPurchaseorder(query) {

View File

@ -1,5 +1,4 @@
import request from '@/utils/request'
import { tansParams } from '@/utils/ruoyi'
// 查询办事处信息列表
export function listAgent(query) {
@ -49,8 +48,6 @@ export function exportAgent(query) {
return request({
url: '/system/agent/vue/export',
method: 'post',
data: query,
transformRequest: [(params) => tansParams(params)],
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
params: query
})
}

View File

@ -1,44 +0,0 @@
import request from '@/utils/request'
// 查询公司信息列表
export function listCompanyInfo(query) {
return request({
url: '/sip/companyInfo/list',
method: 'get',
params: query
})
}
// 查询公司信息详细
export function getCompanyInfo(id) {
return request({
url: '/sip/companyInfo/' + id,
method: 'get'
})
}
// 新增公司信息
export function addCompanyInfo(data) {
return request({
url: '/sip/companyInfo',
method: 'post',
data: data
})
}
// 修改公司信息
export function updateCompanyInfo(data) {
return request({
url: '/sip/companyInfo',
method: 'put',
data: data
})
}
// 删除公司信息
export function delCompanyInfo(id) {
return request({
url: '/sip/companyInfo/' + id,
method: 'delete'
})
}

View File

@ -1,5 +1,4 @@
import request from '@/utils/request'
import { tansParams } from '@/utils/ruoyi'
// 查询客户信息列表
export function listCustomer(query) {
@ -49,8 +48,6 @@ export function exportCustomer(query) {
return request({
url: '/system/customer/vue/export',
method: 'post',
data: query,
transformRequest: [(params) => tansParams(params)],
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
params: query
})
}

View File

@ -1,5 +1,4 @@
import request from '@/utils/request'
import { tansParams } from '@/utils/ruoyi'
// 查询产品管理列表
export function listProduct(query) {
@ -46,13 +45,11 @@ export function delProduct(id) {
// 导出产品管理
export function exportProduct(query) {
return request({
url: '/system/product/export',
method: 'post',
data: query,
transformRequest: [(params) => tansParams(params)],
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
})
return request({
url: '/system/product/export',
method: 'post',
params: query
})
}
// a new function that queries a product by its code

View File

@ -1,7 +1,7 @@
import request from '@/utils/request'
import { parseStrEmpty } from "@/utils/ruoyi";
// 鏌ヨ鐢ㄦ埛鍒楄〃
// 查询用户列表
export function listUser(query) {
return request({
url: '/system/user/list',
@ -10,7 +10,7 @@ export function listUser(query) {
})
}
// 鏌ヨ鐢ㄦ埛璇︾粏
// 查询用户详细
export function getUser(userId) {
return request({
url: '/system/user/' + parseStrEmpty(userId),
@ -18,7 +18,7 @@ export function getUser(userId) {
})
}
// 鏂板鐢ㄦ埛
// 新增用户
export function addUser(data) {
return request({
url: '/system/user/add',
@ -30,7 +30,7 @@ export function addUser(data) {
})
}
// 淇敼鐢ㄦ埛
// 修改用户
export function updateUser(data) {
return request({
url: '/system/user',
@ -39,7 +39,7 @@ export function updateUser(data) {
})
}
// 鍒犻櫎鐢ㄦ埛
// 删除用户
export function delUser(userId) {
return request({
url: '/system/user/' + userId,
@ -47,7 +47,7 @@ export function delUser(userId) {
})
}
// 鐢ㄦ埛瀵嗙爜閲嶇疆
// 用户密码重置
export function resetUserPwd(data) {
return request({
@ -60,7 +60,7 @@ export function resetUserPwd(data) {
})
}
// 鐢ㄦ埛鐘舵€佷慨鏀?
// 用户状态修改
export function changeUserStatus(userId, status) {
const data = {
userId,
@ -76,7 +76,7 @@ export function changeUserStatus(userId, status) {
})
}
// 鏌ヨ鐢ㄦ埛涓汉淇℃伅
// 查询用户个人信息
export function getUserProfile() {
return request({
url: '/system/user/profile/vue',
@ -84,7 +84,7 @@ export function getUserProfile() {
})
}
// 淇敼鐢ㄦ埛涓汉淇℃伅
// 修改用户个人信息
export function updateUserProfile(data) {
return request({
url: '/system/user/profile',
@ -93,7 +93,7 @@ export function updateUserProfile(data) {
})
}
// 鐢ㄦ埛瀵嗙爜閲嶇疆
// 用户密码重置
export function updateUserPwd(data) {
return request({
url: '/system/user/profile/resetPwd',
@ -103,7 +103,7 @@ export function updateUserPwd(data) {
})
}
// 鐢ㄦ埛澶村儚涓婁紶
// 用户头像上传
export function uploadAvatar(data) {
return request({
url: '/system/user/profile/updateAvatar',
@ -113,20 +113,7 @@ export function uploadAvatar(data) {
})
}
export function getUserBotProfile() {
return request({
url: '/system/user/profile/bot',
method: 'get'
})
}
export function generateUserBotProfile() {
return request({
url: '/system/user/profile/bot/generate',
method: 'post'
})
}
// 鏌ヨ鎺堟潈瑙掕壊
// 查询授权角色
export function getAuthRole(userId) {
return request({
url: '/system/user/vue/authRole/' + userId,
@ -134,7 +121,7 @@ export function getAuthRole(userId) {
})
}
// 淇濆瓨鎺堟潈瑙掕壊
// 保存授权角色
export function updateAuthRole(data) {
return request({
url: '/system/user/authRole',
@ -143,7 +130,7 @@ export function updateAuthRole(data) {
})
}
// 鏌ヨ閮ㄩ棬涓嬫媺鏍戠粨鏋?
// 查询部门下拉树结构
export function deptTreeSelect() {
return request({
url: '/system/user/deptTree',

View File

@ -1,5 +1,4 @@
import request from '@/utils/request'
import { tansParams } from '@/utils/ruoyi'
// 查询仓库基础信息列表
export function listInfo(query) {
@ -46,11 +45,9 @@ export function delInfo(id) {
// 导出仓库基础信息
export function exportInfo(query) {
return request({
url: '/warehouse/info/export',
method: 'post',
data: query,
transformRequest: [(params) => tansParams(params)],
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
})
}
return request({
url: '/warehouse/info/export',
method: 'post',
params: query
})
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

View File

@ -104,18 +104,6 @@
margin-left: 1px;
}
}
// + 穿
.el-table__fixed,
.el-table__fixed-right {
background-color: #fff;
z-index: 3;
}
.el-table__fixed .el-table__cell,
.el-table__fixed-right .el-table__cell {
background-color: #fff;
}
}
/** 表单布局 **/

View File

@ -1,64 +0,0 @@
<template>
<div>
<el-dialog :visible.sync="pdfPreviewVisible" width="80%" append-to-body top="5vh" title="PDF预览">
<iframe :src="currentPdfUrl" width="100%" height="600px" frameborder="0"></iframe>
</el-dialog>
<el-dialog :visible.sync="imagePreviewVisible" width="60%" append-to-body top="5vh" title="图片预览">
<img :src="currentImageUrl" style="width: 100%;max-height: 60vh" />
</el-dialog>
</div>
</template>
<script>
import request from '@/utils/request';
export default {
name: "GlobalFilePreview",
data() {
return {
pdfPreviewVisible: false,
currentPdfUrl: '',
imagePreviewVisible: false,
currentImageUrl: ''
};
},
methods: {
isPdf(filePath) {
return filePath && filePath.toLowerCase().endsWith('.pdf');
},
getImageUrl(resource) {
return process.env.VUE_APP_BASE_API + "/common/download/resource?resource=" + resource;
},
handlePreview(attachment) {
if (!attachment) return;
if (this.isPdf(attachment.filePath)) {
request({
url: '/common/download/resource',
method: 'get',
params: { resource: attachment.filePath },
responseType: 'blob'
}).then(res => {
const blob = new Blob([res.data], { type: 'application/pdf' });
this.currentPdfUrl = URL.createObjectURL(blob);
this.pdfPreviewVisible = true;
});
} else {
this.currentImageUrl = this.getImageUrl(attachment.filePath);
this.imagePreviewVisible = true;
}
},
downloadFile(attachment){
if (attachment){
const link = document.createElement('a');
link.href = process.env.VUE_APP_BASE_API + "/common/download/resource?resource=" +attachment.filePath;
link.download = attachment.fileName || 'file';
link.style.display = 'none';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
},
}
};
</script>

View File

@ -11,7 +11,7 @@
<el-button size="mini" circle icon="el-icon-menu" @click="showColumn()" v-if="showColumnsType == 'transfer'"/>
<el-dropdown trigger="click" :hide-on-click="false" style="padding-left: 12px" v-if="showColumnsType == 'checkbox'">
<el-button size="mini" circle icon="el-icon-menu" />
<el-dropdown-menu slot="dropdown" >
<el-dropdown-menu slot="dropdown">
<!-- 全选/反选 按钮 -->
<el-dropdown-item>
<el-checkbox :indeterminate="isIndeterminate" v-model="isChecked" @change="toggleCheckAll"> </el-checkbox>
@ -183,8 +183,4 @@ export default {
background-color: #ccc;
margin: 3px auto;
}
.el-dropdown-menu{
max-height: 400px;
overflow-y: auto;
}
</style>

View File

@ -3,7 +3,6 @@
<logo v-if="showLogo" :collapse="isCollapse" />
<el-scrollbar :class="settings.sideTheme" wrap-class="scrollbar-wrapper">
<el-menu
ref="sideMenu"
:default-active="activeMenu"
:collapse="isCollapse"
:background-color="settings.sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground"
@ -12,8 +11,6 @@
:active-text-color="settings.theme"
:collapse-transition="false"
mode="vertical"
@open="handleMenuOpen"
@close="handleMenuClose"
>
<sidebar-item
v-for="(route, index) in sidebarRouters"
@ -27,7 +24,6 @@
</template>
<script>
import pathLib from 'path'
import { mapGetters, mapState } from "vuex"
import Logo from "./Logo"
import SidebarItem from "./SidebarItem"
@ -56,55 +52,6 @@ export default {
isCollapse() {
return !this.sidebar.opened
}
},
methods: {
// """"
handleMenuClose(index) {
//
const closedRoute = this.sidebarRouters.find(route => route.path === index)
if (!closedRoute || !closedRoute.children || !closedRoute.children.length) return
// ""
const outerRoute = closedRoute.children.find(
child => !child.hidden && child.meta && child.meta.title === '出库管理'
)
if (!outerRoute) return
// "" el-menu index
const outerBasePath = pathLib.resolve(index, outerRoute.path)
const outerIndex = pathLib.resolve(outerBasePath, outerRoute.path)
if (this.$refs.sideMenu) {
this.$refs.sideMenu.close(outerIndex)
}
},
// ""
handleMenuOpen(index) {
//
const openedRoute = this.sidebarRouters.find(route => route.path === index)
if (!openedRoute || !openedRoute.children || !openedRoute.children.length) return
// ""
const outerRoute = openedRoute.children.find(
child => !child.hidden && child.meta && child.meta.title === '出库管理'
)
if (!outerRoute) return
// "" el-menu index
// SidebarItem
// basePath path.resolve(parentPath, child.path)
// basePath el-submenu :index = path.resolve(basePath, child.path)
// child.path resolve index
const outerBasePath = pathLib.resolve(index, outerRoute.path)
const outerIndex = pathLib.resolve(outerBasePath, outerRoute.path)
// 300ms el-submenu el-menu
setTimeout(() => {
if (this.$refs.sideMenu) {
this.$refs.sideMenu.open(outerIndex)
}
}, 300)
}
}
}
</script>

View File

@ -21,7 +21,6 @@ import './permission' // permission control
import { getDicts } from "@/api/system/dict/data"
import { getConfigKey } from "@/api/system/config"
import { parseTime, resetForm, addDateRange, selectDictLabel, selectDictLabels, handleTree } from "@/utils/ruoyi"
import { formatCurrency } from "@/utils"
// 分页组件
import Pagination from "@/components/Pagination"
// 自定义表格工具组件
@ -43,7 +42,6 @@ import DictData from '@/components/DictData'
Vue.prototype.getDicts = getDicts
Vue.prototype.getConfigKey = getConfigKey
Vue.prototype.parseTime = parseTime
Vue.prototype.formatCurrency = formatCurrency
Vue.prototype.resetForm = resetForm
Vue.prototype.addDateRange = addDateRange
Vue.prototype.selectDictLabel = selectDictLabel

View File

@ -5,16 +5,12 @@ import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
import { isPathMatch } from '@/utils/validate'
import { isRelogin } from '@/utils/request'
import { isServiceHost } from '@/utils/serviceHost'
NProgress.configure({ showSpinner: false })
const whiteList = ['/login', '/register', '/manage/service','/system/vendor/query','/system/partner/query']
const isWhiteList = (path) => {
if (path === '/' && isServiceHost()) {
return true
}
return whiteList.some(pattern => isPathMatch(pattern, path))
}

View File

@ -9,12 +9,13 @@ const baseURL = process.env.VUE_APP_BASE_API
let downloadLoadingInstance
export default {
download(name, isDelete = true) {
name(name, isDelete = true) {
var url = baseURL + "/common/download?fileName=" + encodeURIComponent(name) + "&delete=" + isDelete
axios({
method: 'get',
url: url,
responseType: 'blob',
headers: { 'Authorization': 'Bearer ' + getToken() }
}).then((res) => {
const isBlob = blobValidate(res.data)
if (isBlob) {

View File

@ -1,6 +1,5 @@
import Vue from 'vue'
import Router from 'vue-router'
import { isServiceHost } from '@/utils/serviceHost'
Vue.use(Router)
@ -63,10 +62,10 @@ export const constantRoutes = [
hidden: true
},
{
path: isServiceHost() ? '/admin-root' : '',
path: '',
component: Layout,
redirect: isServiceHost() ? undefined : 'index',
children: isServiceHost() ? [] : [
redirect: 'index',
children: [
{
path: 'index',
component: () => import('@/views/index'),
@ -95,46 +94,6 @@ export const constantRoutes = [
component: () => import('@/views/approve/approved_order/index'),
hidden: true
},
{
path: 'paymentLog',
component: () => import('@/views/approve/finance/payment/approved/index'),
hidden: true
},
{
path: 'paymentRedLog',
component: () => import('@/views/approve/finance/paymentRefund/approved/index'),
hidden: true
},
{
path: 'invoiceRedLog',
component: () => import('@/views/approve/finance/invoiceRed/approved/index'),
hidden: true
},
{
path: 'invoiceLog',
component: () => import('@/views/approve/finance/invoiceReceipt/approved/index'),
hidden: true
},
{
path: 'receiptLog',
component: () => import('@/views/approve/finance/receipt/approved/index.vue'),
hidden: true
},
{
path: 'receiptRefoundLog',
component: () => import('@/views/approve/finance/receiptRefound/approved/index.vue'),
hidden: true
},
{
path: 'receivableInvoiceLog',
component: () => import('@/views/approve/finance/receivableInvoice/approved/index.vue'),
hidden: true
},
{
path: 'receivableInvoiceRefundLog',
component: () => import('@/views/approve/finance/receivableInvoiceRefund/approved/index.vue'),
hidden: true
},
]
},
{
@ -153,11 +112,6 @@ export const constantRoutes = [
},
{
path: isServiceHost() ? '/' : '/service-host-root',
component: () => import('@/views/manage/service/index'),
hidden: true
},
{
path: '/manage/service',
component: () => import('@/views/manage/service/index'),
@ -210,31 +164,6 @@ export const dynamicRoutes = [
}
]
},
{
path: '/finance',
component: Layout,
redirect: 'noRedirect',
name: 'Finance',
meta: {
title: '财务管理',
icon: 'money'
},
children: [
{
path: 'payment',
component: () => import('@/views/finance/payment/index'),
name: 'Payment',
meta: { title: '付款单', icon: 'form' }
},
{
path: 'charge',
component: () => import('@/views/finance/charge/index'),
name: 'Charge',
meta: { title: '财务计收', icon: 'money' },
permissions: ['finance:charge:list']
}
]
},
{
path: '/inventory/execution',
component: Layout,

View File

@ -52,7 +52,7 @@ const user = {
const rememberMe = userInfo.rememberMe
return new Promise((resolve, reject) => {
login(username, password, code, rememberMe).then(res => {
resolve(res)
resolve()
}).catch(error => {
reject(error)
})
@ -60,16 +60,17 @@ const user = {
},
// 获取用户信息
GetInfo({ commit }) {
GetInfo({ commit, state }) {
return new Promise((resolve, reject) => {
getInfo().then(res => {
// 后端返回的数据在 res.data 中
const data = res.data || res
const user = data.user
let avatar = user.avatar ? '/common/download/resource?resource=' + encodeURIComponent(user.avatar) : ""
let avatar = user.avatar? '/common/download/resource?resource='+encodeURIComponent(user.avatar) : ""
if (!isHttp(avatar)) {
avatar = (isEmpty(avatar)) ? defAva : process.env.VUE_APP_BASE_API + avatar
}
if (data.roles && data.roles.length > 0) {
if (data.roles && data.roles.length > 0) { // 验证返回的roles是否是一个非空数组
commit('SET_ROLES', data.roles)
commit('SET_PERMISSIONS', data.permissions)
} else {
@ -81,13 +82,15 @@ const user = {
commit('SET_NAME', user.loginName)
commit('SET_NICK_NAME', user.userName)
commit('SET_AVATAR', avatar)
if (data.isDefaultModifyPwd) {
MessageBox.confirm('您的密码还是初始密码,请修改密码!', '安全提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => {
/* 初始密码提示 */
if(data.isDefaultModifyPwd) {
MessageBox.confirm('您的密码还是初始密码,请修改密码!', '安全提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => {
router.push({ name: 'Profile', params: { activeTab: 'resetPwd' } })
}).catch(() => {})
}
if (!data.isDefaultModifyPwd && data.isPasswordExpired) {
MessageBox.confirm('您的密码已过期,请尽快修改密码!', '安全提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => {
/* 过期密码提示 */
if(!data.isDefaultModifyPwd && data.isPasswordExpired) {
MessageBox.confirm('您的密码已过期,请尽快修改密码!', '安全提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => {
router.push({ name: 'Profile', params: { activeTab: 'resetPwd' } })
}).catch(() => {})
}
@ -99,7 +102,7 @@ const user = {
},
// 退出系统 - 基于 Session 认证
LogOut({ commit }) {
LogOut({ commit, state }) {
return new Promise((resolve, reject) => {
logout().then(() => {
commit('SET_ROLES', [])

View File

@ -19,17 +19,17 @@ export function toFixed(value, dp = DEFAULT_DP) {
// 加法
export function add(a, b, dp = DEFAULT_DP) {
return D(a).plus(D(b)).toDecimalPlaces(dp).toNumber()
return D(a).plus(b).toDecimalPlaces(dp).toNumber()
}
// 减法
export function sub(a, b, dp = DEFAULT_DP) {
return D(a).minus(D(b)).toDecimalPlaces(dp).toNumber()
return D(a).minus(b).toDecimalPlaces(dp).toNumber()
}
// 乘法
export function mul(a, b, dp = DEFAULT_DP) {
return D(a).times(D(b)).toDecimalPlaces(dp).toNumber()
return D(a).times(b).toDecimalPlaces(dp).toNumber()
}
// 除法

View File

@ -14,10 +14,7 @@ export function formatDate(cellValue) {
var seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds()
return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds
}
export function formatCurrency(value) {
if (value == null) return '0.00';
return Number(value).toLocaleString('en-US', {minimumFractionDigits: 2, maximumFractionDigits: 2});
}
/**
* @param {number} time
* @param {string} option

View File

@ -110,8 +110,6 @@ service.interceptors.response.use(res => {
} else if (code === 601) {
Message({ message: msg, type: 'warning' })
return Promise.reject('error')
} else if (code === 301 && res.config.headers && res.config.headers.allowWarning) {
return res.data
} else if (code !== 200) {
Notification.error({ title: msg })
return Promise.reject('error')

View File

@ -1,12 +0,0 @@
const serviceHosts = (process.env.VUE_APP_SERVICE_HOST || '')
.split(',')
.map(host => host.trim().toLowerCase())
.filter(Boolean)
export function isServiceHost(hostname) {
const currentHost = (hostname || (typeof window !== 'undefined' ? window.location.hostname : '') || '')
.trim()
.toLowerCase()
return currentHost !== '' && serviceHosts.includes(currentHost)
}

View File

@ -1,359 +0,0 @@
<template>
<el-dialog
title="订单撤回审批"
:visible="dialogVisible"
:close-on-click-modal="false"
width="80%"
top="5vh"
append-to-body
@update:visible="handleVisibleChange"
>
<div v-loading="loading" style="max-height: 70vh; overflow-y: auto;">
<div class="approve-container">
<ApproveLayout ref="approveLayout" title="紫光汇智信息技术有限公司">
<template #default>
<div style="display: flex;align-items: center;justify-content: center;">
<span style="margin-left: 10px;color: black;font-size: 30px">
紫光汇智信息技术有限公司订单撤回审批
</span>
</div>
<el-form ref="orderForm" :model="order" label-width="120px" class="mb20">
<h3 class="section-title">订单信息</h3>
<order-info-display
:order-data.sync="order"
/>
</el-form>
<h3 class="section-title">配置信息</h3>
<config-info
:order-data="order"
class="mb20"
/>
</template>
<template #footer>
<p>{{ order.projectCode }}-{{ order.orderCode }}-Rev.{{ order.versionCode }}</p>
</template>
</ApproveLayout>
<!-- 撤单信息 -->
<el-form label-width="120px" class="mb20">
<h3 class="section-title">撤单信息</h3>
<el-form-item label="撤单原因:">
<el-input :value="reason" readonly type="textarea" :rows="2" />
</el-form-item>
<el-form-item label="金额是否变化:">
<el-input :value="amountChanged || '—'" readonly />
</el-form-item>
<el-row>
<el-col :span="8">
<el-form-item label="出库状态:">
<dict-tag :options="dict.type.execution_outer_status" :value="order.outerStatus"/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="发货状态:">
<dict-tag :options="dict.type.execution_delivery_status" :value="order.deliveryStatus"/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="签收状态:">
<dict-tag :options="dict.type.execution_sign_status" :value="order.signStatus"/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<!-- 流转过程 -->
<el-tabs v-model="activeTab" class="approve-tabs">
<el-tab-pane label="流转过程" name="process">
<div class="process-container">
<el-tabs :value="activeVersionTab" @input="handleVersionTabChange" type="card" v-if="uniqueVersions.length > 0">
<el-tab-pane
v-for="version in uniqueVersions"
:key="version"
:label="'版本号Rev.' + version"
:name="version">
<el-timeline>
<el-timeline-item
v-for="log in groupedApproveLogs[version]"
:key="log.id"
:timestamp="log.approveTime"
placement="top">
<el-card>
<h4>{{ log.approveOpinion }}</h4>
<p><b>操作人:</b> {{ log.approveUserName }}</p>
<p><b>审批状态:</b> <el-tag :type="getStatusTagType(log.approveStatus)" size="small">{{ getStatusText(log.approveStatus) }}</el-tag></p>
</el-card>
</el-timeline-item>
</el-timeline>
</el-tab-pane>
</el-tabs>
<div v-else></div>
</div>
</el-tab-pane>
</el-tabs>
</div>
</div>
<span slot="footer" class="dialog-footer" v-if="showApprove && taskId">
<el-button @click="handleVisibleChange(false)"> </el-button>
<el-button type="danger" @click="openOpinionDialog('reject')"> </el-button>
<el-button type="primary" @click="openOpinionDialog('approve')"> </el-button>
</span>
<span slot="footer" class="dialog-footer" v-else>
<el-button @click="handleVisibleChange(false)"> </el-button>
</span>
<!-- 审批意见对话框 -->
<el-dialog :title="confirmDialogTitle" :visible.sync="opinionDialogVisible" width="500px" append-to-body :close-on-click-modal="false" :close-on-press-escape="false">
<el-form ref="opinionForm" :model="opinionForm" :rules="opinionRules" label-width="100px">
<el-form-item label="审批意见" prop="approveOpinion">
<el-input v-model="opinionForm.approveOpinion" type="textarea" :rows="4" placeholder="请输入审批意见" :disabled="submitLoading"/>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="opinionDialogVisible = false" :disabled="submitLoading"> </el-button>
<el-button type="primary" @click="showConfirmDialog" :loading="submitLoading"> </el-button>
</span>
</el-dialog>
</el-dialog>
</template>
<script>
import { getOrder } from '@/api/approve/order/index'
import { listCompletedFlows, listApplyFlows, approveTask } from '@/api/flow'
import ConfigInfo from '@/views/approve/order/ConfigInfo.vue'
import ApproveLayout from '@/views/approve/ApproveLayout.vue'
import OrderInfoDisplay from '@/views/project/order/components/OrderInfoDisplay.vue'
export default {
name: 'OrderRebackDetail',
dicts: ['execution_outer_status', 'execution_delivery_status', 'execution_sign_status'],
components: {
ApproveLayout,
OrderInfoDisplay,
ConfigInfo
},
props: {
visible: {
type: Boolean,
default: false
},
// id
orderId: {
type: [Number, String],
default: null
},
// businessKey
orderCode: {
type: String,
default: ''
},
processKey: {
type: String,
default: 'order_reback'
},
// id
taskId: {
type: [Number, String],
default: null
},
//
showApprove: {
type: Boolean,
default: false
}
},
data() {
return {
dialogVisible: false,
loading: false,
order: {},
logs: [],
reason: '',
amountChanged: '',
activeTab: 'process',
activeVersionTab: null,
confirmDialogTitle: '',
opinionDialogVisible: false,
currentApproveType: null,
submitLoading: false,
opinionForm: { approveOpinion: '' },
opinionRules: {
approveOpinion: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }]
}
}
},
computed: {
//
uniqueVersions() {
if (!this.logs || this.logs.length === 0) return []
const versions = [...new Set(this.logs.map(log => log.extendField1))].filter(v => v !== null && v !== undefined && v !== '')
return versions.sort((a, b) => b - a) //
},
//
groupedApproveLogs() {
if (!this.logs || this.logs.length === 0) return {}
return this.logs.reduce((acc, log) => {
const version = log.extendField1
if (!acc[version]) acc[version] = []
acc[version].push(log)
return acc
}, {})
}
},
watch: {
visible(val) {
this.dialogVisible = val
if (val) {
this.loadDetail()
}
}
},
methods: {
// prop
handleVisibleChange(val) {
this.dialogVisible = val
this.$emit('update:visible', val)
},
//
loadDetail() {
this.loading = true
this.order = {}
this.logs = []
this.reason = ''
this.amountChanged = ''
this.activeVersionTab = null
if (!this.orderId) {
this.loading = false
return
}
getOrder(this.orderId).then(response => {
const data = response.data || {}
this.order = data.projectOrderInfo || {}
const businessKey = this.order.orderCode || this.orderCode
this.loadFlows(businessKey)
}).catch(error => {
console.error('获取订单详情失败: ', error)
this.$modal.msgError('获取订单详情失败!')
this.loading = false
})
},
loadFlows(businessKey) {
const processKeyList = this.processKey ? [this.processKey] : ['order_reback']
listCompletedFlows({ businessKey: businessKey, processKeyList }).then(res => {
this.logs = res.data || []
// extendField2 extendField3extendField1
const applyLog = this.logs.find(log => log.extendField2)
if (applyLog) {
this.reason = applyLog.extendField2
this.amountChanged = applyLog.extendField3 || ''
} else {
//
listApplyFlows({ businessKey: businessKey, processKey: this.processKey }).then(todoRes => {
const todoList = todoRes.data || []
const apply = todoList.find(item => item.extendField2)
this.reason = apply ? apply.extendField2 : ''
this.amountChanged = apply ? (apply.extendField3 || '') : ''
}).catch(() => {})
}
if (this.uniqueVersions.length > 0) {
this.activeVersionTab = String(this.uniqueVersions[0])
}
this.loading = false
}).catch(() => {
this.logs = []
this.loading = false
})
},
openOpinionDialog(type) {
this.currentApproveType = type
this.confirmDialogTitle = type === 'approve' ? '同意审批' : '驳回审批'
this.opinionForm.approveOpinion = ''
this.opinionDialogVisible = true
this.$nextTick(() => {
this.$refs.opinionForm && this.$refs.opinionForm.clearValidate()
})
},
showConfirmDialog() {
this.$refs.opinionForm.validate(valid => {
if (valid) {
this.opinionDialogVisible = false
this.submitApproval()
}
})
},
submitApproval() {
this.submitLoading = true
const approveBtn = this.currentApproveType === 'approve' ? 1 : 0
const params = {
businessKey: this.order.orderCode || this.orderCode,
processKey: this.processKey,
taskId: this.taskId,
variables: {
comment: this.opinionForm.approveOpinion,
approveBtn: approveBtn
}
}
approveTask(params).then(() => {
this.submitLoading = false
this.$modal.msgSuccess(this.confirmDialogTitle + '成功')
this.handleVisibleChange(false)
this.$emit('success')
}).catch(error => {
this.submitLoading = false
console.error('审批失败: ', error)
this.$modal.msgError('审批失败!' + (error.msg || error.message))
})
},
getStatusTagType(status) {
const typeMap = {
'1': 'info', //
'2': 'danger', //
'3': 'success' //
}
return typeMap[String(status)] || 'info'
},
// Tab/
handleVersionTabChange(val) {
if (val !== null && val !== undefined && val !== '' && this.uniqueVersions.includes(val)) {
this.activeVersionTab = val
}
},
getStatusText(status) {
const textMap = { '1': '提交审批', '2': '驳回', '3': '批准' }
return textMap[String(status)] || '提交审批'
}
}
}
</script>
<style scoped>
.section-title {
font-size: 16px;
font-weight: bold;
color: #303133;
margin-bottom: 15px;
border-bottom: 1px solid #eee;
padding-bottom: 10px;
}
.mb20 {
margin-bottom: 20px;
}
/* Tab样式 */
.approve-tabs {
margin-top: 20px;
margin-left: 10px;
}
/* 流转过程容器 */
.process-container {
padding: 0;
}
/* 审批意见弹窗样式 */
::v-deep .el-dialog__body {
padding-top: 10px;
}
</style>

View File

@ -1,312 +0,0 @@
<template>
<el-dialog
title="出库撤回审批"
:visible="dialogVisible"
:close-on-click-modal="false"
width="80%"
top="5vh"
append-to-body
destroy-on-close
@update:visible="handleVisibleChange"
>
<div v-loading="loading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
<el-form label-width="110px">
<el-row>
<el-col :span="12">
<el-form-item label="出库单号:">
<el-input :value="form.outerCode" readonly />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="物流单号:">
<el-input :value="form.logisticsCode" readonly />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="物流公司:">
<el-input :value="form.logisticsCompany" readonly />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="仓库:">
<el-input :value="form.warehouseName" readonly />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="发货时间:">
<el-input :value="form.deliveryTime" readonly />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="发货数量:">
<el-input :value="form.quantity" readonly />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="收货地址:">
<el-input :value="form.notifierAddress" readonly />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="撤回原因:">
<el-input :value="reason" readonly type="textarea" :rows="2" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="金额是否变化:">
<el-input :value="amountChanged || '—'" readonly />
</el-form-item>
</el-col>
</el-row>
</el-form>
<el-divider content-position="left">SN码列表</el-divider>
<el-table v-loading="snLoading" :data="snList" border size="mini">
<el-table-column type="index" label="序号" align="center" width="60" />
<el-table-column label="SN码" align="center" prop="productSn"/>
<el-table-column label="授权码" align="center" prop="licenseKey"/>
<el-table-column label="产品编码" align="center" prop="productCode"/>
<el-table-column label="产品型号" align="center" prop="model"/>
<el-table-column label="描述" align="center" prop="productDesc"/>
<el-table-column label="仓库" align="center" prop="warehouseName"/>
</el-table>
<pagination v-show="snTotal>0" :total="snTotal" :page.sync="snQueryParams.pageNum" :limit.sync="snQueryParams.pageSize" @pagination="getSnList"/>
<el-divider content-position="left">流转意见</el-divider>
<div class="process-container">
<el-timeline>
<el-timeline-item v-for="log in logs" :key="log.id" :timestamp="log.approveTime" placement="top">
<el-card>
<h4>{{ log.approveOpinion }}</h4>
<p><b>操作人:</b> {{ log.approveUserName }}</p>
<p><b>审批状态:</b>
<el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag>
</p>
</el-card>
</el-timeline-item>
</el-timeline>
<div v-if="!logs || logs.length === 0"></div>
</div>
</div>
<span slot="footer" class="dialog-footer" v-if="showApprove && taskId">
<el-button @click="handleVisibleChange(false)"> </el-button>
<el-button type="danger" @click="openOpinionDialog('reject')"> </el-button>
<el-button type="primary" @click="openOpinionDialog('approve')"> </el-button>
</span>
<span slot="footer" class="dialog-footer" v-else>
<el-button @click="handleVisibleChange(false)"> </el-button>
</span>
<!-- 审批意见对话框 -->
<el-dialog :title="confirmDialogTitle" :visible.sync="opinionDialogVisible" width="30%" append-to-body>
<el-form ref="opinionForm" :model="opinionForm" :rules="opinionRules" label-width="100px">
<el-form-item label="审批意见" prop="approveOpinion">
<el-input v-model="opinionForm.approveOpinion" type="textarea" :rows="4" placeholder="请输入审批意见"/>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="opinionDialogVisible = false"> </el-button>
<el-button type="primary" @click="showConfirmDialog()"> </el-button>
</span>
</el-dialog>
</el-dialog>
</template>
<script>
import { getDelivery, listProductSn } from '@/api/inventory/delivery'
import { listCompletedFlows, listApplyFlows, approveTask } from '@/api/flow'
export default {
name: 'OuterRebackDetail',
props: {
visible: {
type: Boolean,
default: false
},
// idSN
deliveryId: {
type: [Number, String],
default: null
},
//
outerCode: {
type: String,
default: ''
},
processKey: {
type: String,
default: 'outer_reback'
},
// id
taskId: {
type: [Number, String],
default: null
},
//
showApprove: {
type: Boolean,
default: false
}
},
data() {
return {
dialogVisible: false,
loading: false,
form: {},
logs: [],
reason: '',
amountChanged: '',
// SN
snLoading: false,
snList: [],
snTotal: 0,
snQueryParams: {
pageNum: 1,
pageSize: 10,
deliveryId: null,
// /SNouter_code
inventoryStatus: null,
outerCode: null,
warehouseId: null,
productSnList: []
},
confirmDialogTitle: '',
opinionDialogVisible: false,
currentApproveType: null,
opinionForm: { approveOpinion: '' },
opinionRules: {
approveOpinion: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }]
}
}
},
watch: {
visible(val) {
this.dialogVisible = val
if (val) {
this.loadDetail()
}
}
},
methods: {
// prop
handleVisibleChange(val) {
this.dialogVisible = val
this.$emit('update:visible', val)
},
// SN
loadDetail() {
this.loading = true
this.form = {}
this.logs = []
this.reason = ''
this.amountChanged = ''
this.snList = []
this.snTotal = 0
if (!this.deliveryId) {
this.loading = false
return
}
getDelivery(this.deliveryId).then(response => {
this.form = response.data || {}
// SN outer_code
this.snQueryParams.deliveryId = this.deliveryId
this.snQueryParams.warehouseId = this.form.warehouseId
this.snQueryParams.productSnList = this.form.productSnList
this.getSnList()
const businessKey = this.form.outerCode || this.outerCode
this.loadFlows(businessKey)
}).catch(error => {
console.error('获取发货记录详情失败: ', error)
this.$modal.msgError('获取发货记录详情失败!')
this.loading = false
})
},
// SN
getSnList() {
this.snLoading = true
listProductSn(this.snQueryParams).then(res => {
this.snList = res.rows
this.snTotal = res.total
this.snLoading = false
}).catch(() => {
this.snLoading = false
})
},
loadFlows(businessKey) {
const processKeyList = this.processKey ? [this.processKey] : ['outer_reback']
listCompletedFlows({ businessKey: businessKey, processKeyList }).then(res => {
this.logs = res.data || []
// extend_field1 extend_field3
const applyLog = this.logs.find(log => log.extendField1)
if (applyLog) {
this.reason = applyLog.extendField1
this.amountChanged = applyLog.extendField3 || ''
} else {
//
listApplyFlows({ businessKey: businessKey, processKey: this.processKey }).then(todoRes => {
const todoList = todoRes.data || []
const apply = todoList.find(item => item.extendField1)
this.reason = apply ? apply.extendField1 : ''
this.amountChanged = apply ? (apply.extendField3 || '') : ''
}).catch(() => {})
}
this.loading = false
}).catch(() => {
this.logs = []
this.loading = false
})
},
openOpinionDialog(type) {
this.currentApproveType = type
this.confirmDialogTitle = type === 'approve' ? '同意审批' : '驳回审批'
this.opinionForm.approveOpinion = ''
this.opinionDialogVisible = true
this.$nextTick(() => {
this.$refs.opinionForm && this.$refs.opinionForm.clearValidate()
})
},
showConfirmDialog() {
this.$refs.opinionForm.validate(valid => {
if (valid) {
this.opinionDialogVisible = false
this.submitApproval()
}
})
},
submitApproval() {
const approveBtn = this.currentApproveType === 'approve' ? 1 : 0
const params = {
businessKey: this.form.outerCode || this.outerCode,
processKey: this.processKey,
taskId: this.taskId,
variables: {
comment: this.opinionForm.approveOpinion,
approveBtn: approveBtn
}
}
approveTask(params).then(() => {
this.$modal.msgSuccess(this.confirmDialogTitle + '成功')
this.handleVisibleChange(false)
this.$emit('success')
}).catch(error => {
console.error('审批失败: ', error)
this.$modal.msgError('审批失败!' + (error.msg || error.message))
})
},
getStatusText(status) {
const textMap = { '1': '提交审批', '2': '驳回', '3': '批准' }
return textMap[String(status)] || '提交审批'
}
}
}
</script>

File diff suppressed because it is too large Load Diff

View File

@ -49,22 +49,6 @@
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="产品编码" prop="productCode">
<el-input
v-model="queryParams.productCode"
placeholder="请输入产品编码"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="产品型号" prop="productModel">
<el-input
v-model="queryParams.productModel"
placeholder="请输入产品型号"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
@ -88,18 +72,13 @@
<el-table-column label="项目名称" align="center" prop="projectName" />
<el-table-column label="项目编号" align="center" prop="projectCode" />
<el-table-column label="客户名称" align="center" prop="customerName" />
<el-table-column label="订单金额" align="center" prop="actualPurchaseAmount" >
<template slot-scope="scope">
<span>{{ formatCurrency(scope.row.actualPurchaseAmount || scope.row.shipmentAmount) }}</span>
</template>
</el-table-column>
<el-table-column label="订单金额" align="center" prop="actualPurchaseAmount" />
<el-table-column label="汇智负责人" align="center" prop="dutyName" />
<el-table-column label="审批节点" align="center" prop="approveNode">
<template slot-scope="scope">
<span>{{ scope.row.approveNode || '-' }}</span>
</template>
</el-table-column>
<el-table-column label="提交时间" align="center" prop="approveTime" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button
@ -148,7 +127,6 @@
<script>
import { listOrder } from "@/api/approve/order/orderLog";
import ApproveDialog from '../order/Approve.vue';
import {formatCurrency} from "../../../utils";
export default {
name: "ApprovedOrder",
@ -175,12 +153,10 @@ export default {
orderCode: null,
projectName: null,
projectCode: null,
productCode: null,
productModel: null,
customerName: null,
dutyName: null,
approveNode: null,
orderByColumn:'t1.approveTime desc, t1.id',
orderByColumn:'t7.todo_approve_time',
isAsc: 'desc'
},
};
@ -189,7 +165,6 @@ export default {
this.getList();
},
methods: {
formatCurrency,
/** 查询订单列表 */
getList() {
this.loading = true;

View File

@ -1,259 +0,0 @@
function escapeHtml(value) {
return String(value == null ? "" : value)
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#39;");
}
function formatCurrency(value) {
const num = Number(value);
if (Number.isNaN(num)) {
return "0.00";
}
return num.toFixed(2);
}
function getProductTypeLabel(value) {
const map = {
"1": "软件",
"2": "电子计算机",
"3": "信息系统服务"
};
return map[String(value)] || "";
}
function convertCurrency(money) {
const cnNums = ["零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"];
const cnIntRadice = ["", "拾", "佰", "仟"];
const cnIntUnits = ["", "万", "亿", "兆"];
const cnDecUnits = ["角", "分", "毫", "厘"];
const cnInteger = "整";
const cnIntLast = "元";
let integerNum;
let decimalNum;
let chineseStr = "";
let parts;
if (money === "") return "";
money = parseFloat(money);
if (money >= 999999999999) return "";
if (money === 0) return cnNums[0] + cnIntLast + cnInteger;
let prefix = "";
if (money < 0) {
prefix = "(负数)";
money = Math.abs(money);
}
money = money.toString();
if (money.indexOf(".") === -1) {
integerNum = money;
decimalNum = "";
} else {
parts = money.split(".");
integerNum = parts[0];
decimalNum = parts[1].substr(0, 4);
}
if (parseInt(integerNum, 10) > 0) {
let zeroCount = 0;
const intLen = integerNum.length;
for (let i = 0; i < intLen; i++) {
const n = integerNum.substr(i, 1);
const p = intLen - i - 1;
const q = Math.floor(p / 4);
const m = p % 4;
if (n === "0") {
zeroCount++;
} else {
if (zeroCount > 0) chineseStr += cnNums[0];
zeroCount = 0;
chineseStr += cnNums[parseInt(n, 10)] + cnIntRadice[m];
}
if (m === 0 && zeroCount < 4) chineseStr += cnIntUnits[q];
}
chineseStr += cnIntLast;
}
if (decimalNum !== "") {
const decLen = decimalNum.length;
for (let i = 0; i < decLen; i++) {
const n = decimalNum.substr(i, 1);
if (n !== "0") chineseStr += cnNums[Number(n)] + cnDecUnits[i];
}
}
if (chineseStr === "") chineseStr += cnNums[0] + cnIntLast + cnInteger;
else if (decimalNum === "") chineseStr += cnInteger;
return prefix + chineseStr;
}
function getInvoiceTypeLabel(invoiceType) {
const map = {
"1": "增值税专用发票",
"2": "增值税普通发票",
"3": "电子发票"
};
return map[String(invoiceType)] || (invoiceType || "");
}
function buildItemRows(items) {
if (!items || items.length === 0) {
return `
<tr>
<td class="cell center" colspan="12" style="height: 28px;">暂无明细</td>
</tr>
`;
}
return items.map((item) => `
<tr>
<td class="cell center" align="center" style="text-align: center; mso-horizontal-align: center;" colspan="2">${escapeHtml(getProductTypeLabel(item.productType))}</td>
<td class="cell center" align="center" style="text-align: center; mso-horizontal-align: center;" colspan="2">${escapeHtml(item.productName)}</td>
<td class="cell center" align="center" style="text-align: center; mso-horizontal-align: center;">${escapeHtml(item.productModel)}</td>
<td class="cell center" align="center" style="text-align: center; mso-horizontal-align: center;">${escapeHtml(item.unit)}</td>
<td class="cell center" align="center" style="text-align: center; mso-horizontal-align: center;">${escapeHtml(item.quantity)}</td>
<td class="cell center" align="center" style="text-align: center; mso-horizontal-align: center;">${formatCurrency(item.price)}</td>
<td class="cell center" align="center" style="text-align: center; mso-horizontal-align: center;" colspan="2">${formatCurrency(item.allPrice)}</td>
<td class="cell center" align="center" style="text-align: center; mso-horizontal-align: center;">${escapeHtml(item.taxRate)}</td>
<td class="cell center" align="center" style="text-align: center; mso-horizontal-align: center;">${formatCurrency(item.taxAmount)}</td>
</tr>
`).join("");
}
function buildSummaryRow(items) {
let allPriceTotal = 0;
let taxTotal = 0;
(items || []).forEach((item) => {
allPriceTotal += Number(item.allPrice) || 0;
taxTotal += Number(item.taxAmount) || 0;
});
return `
<tr>
<td class="cell center bold" align="center" style="text-align: center; mso-horizontal-align: center;" colspan="8">合计</td>
<td class="cell center bold" align="center" style="text-align: center; mso-horizontal-align: center;" colspan="2">${allPriceTotal.toFixed(2)}</td>
<td class="cell center bold" align="center" style="text-align: center; mso-horizontal-align: center;">-</td>
<td class="cell center bold" align="center" style="text-align: center; mso-horizontal-align: center;">${taxTotal.toFixed(2)}</td>
</tr>
`;
}
function buildInvoiceExcelHtml(invoice) {
const detailItems = invoice.detailItemList || [];
const totalAmountNumber = detailItems.reduce((sum, item) => sum + (Number(item.allPrice) || 0), 0).toFixed(2);
const totalAmountChinese = convertCurrency(totalAmountNumber);
const invoiceTypeLabel = getInvoiceTypeLabel(invoice.invoiceType);
return `<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40">
<head>
<meta charset="UTF-8" />
<meta http-equiv="content-type" content="application/vnd.ms-excel; charset=UTF-8" />
<!--[if gte mso 9]><xml>
<x:ExcelWorkbook>
<x:ExcelWorksheets>
<x:ExcelWorksheet>
<x:Name>开票信息</x:Name>
<x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions>
</x:ExcelWorksheet>
</x:ExcelWorksheets>
</x:ExcelWorkbook>
</xml><![endif]-->
<style>
table { border-collapse: collapse; width: 1200px; font-family: "Microsoft YaHei", Arial, sans-serif; color: #8B4513; }
td { border: 1px solid #8B4513; padding: 6px; font-size: 12px; vertical-align: middle; mso-vertical-align: middle; mso-wrap-style: none; }
.no-border { border: none !important; }
.title { font-size: 26px; font-weight: bold; text-align: center; border: none !important; padding: 12px 0; }
.sub-title { font-size: 18px; font-weight: bold; text-align: center; border: none !important; padding-bottom: 8px; }
.label { width: 120px; font-weight: bold; text-align: center; }
.center { text-align: center; mso-horizontal-align: center; }
.right { text-align: right; mso-horizontal-align: right; }
.bold { font-weight: bold; }
.section-head { font-weight: bold; text-align: center; background: #f8f5f0; }
.cell { height: 26px; }
.total-main { font-weight: bold; }
</style>
</head>
<body>
<table>
<colgroup>
<col style="width:100px;" />
<col style="width:100px;" />
<col style="width:100px;" />
<col style="width:100px;" />
<col style="width:80px;" />
<col style="width:80px;" />
<col style="width:80px;" />
<col style="width:100px;" />
<col style="width:100px;" />
<col style="width:100px;" />
<col style="width:80px;" />
<col style="width:100px;" />
</colgroup>
<tr>
<td class="no-border" colspan="2"></td>
<td class="title" colspan="8">电子发票${escapeHtml(invoiceTypeLabel)}</td>
<td class="no-border" colspan="2" style="text-align: left; vertical-align: middle;">
<div><span class="bold">发票号码</span>----------</div>
<div><span class="bold">开票日期</span>----------</div>
</td>
</tr>
<tr>
<td class="section-head" colspan="2" rowspan="2">购买方信息</td>
<td class="label" colspan="4">名称${escapeHtml(invoice.buyerName)}</td>
<td class="section-head" colspan="2" rowspan="2">销售方信息</td>
<td class="label" colspan="4">名称${escapeHtml(invoice.sellerName)}</td>
</tr>
<tr>
<td class="label" colspan="4">纳税人识别号${escapeHtml(invoice.buyerCreditCode)}</td>
<td class="label" colspan="4">纳税人识别号${escapeHtml(invoice.sellerCreditCode)}</td>
</tr>
<tr>
<td class="section-head" colspan="2">税收大类</td>
<td class="section-head" colspan="2">项目名称</td>
<td class="section-head">规格型号</td>
<td class="section-head">单位</td>
<td class="section-head">数量</td>
<td class="section-head">单价</td>
<td class="section-head" colspan="2">金额</td>
<td class="section-head">税率%</td>
<td class="section-head">税额</td>
</tr>
${buildItemRows(detailItems)}
${buildSummaryRow(detailItems)}
<tr>
<td class="label total-main" align="center" style="text-align: center; mso-horizontal-align: center;" colspan="2">价税合计大写</td>
<td class="total-main center" align="center" style="text-align: center; mso-horizontal-align: center;" colspan="6"> ${escapeHtml(totalAmountChinese)}</td>
<td class="label total-main" align="center" style="text-align: center; mso-horizontal-align: center;" colspan="2">小写</td>
<td class="total-main center" align="center" style="text-align: center; mso-horizontal-align: center;" colspan="2">¥${formatCurrency(totalAmountNumber)}</td>
</tr>
<tr>
<td class="label" colspan="2">备注</td>
<td colspan="10">${escapeHtml(invoice.remark)}</td>
</tr>
<tr>
<td class="label" colspan="2">信息说明</td>
<td colspan="10">${escapeHtml(invoice.informationNote)}</td>
</tr>
</table>
</body>
</html>`;
}
export function exportInvoiceInfoToExcel(invoiceData, fileName) {
const content = buildInvoiceExcelHtml(invoiceData || {});
const blob = new Blob(["\ufeff", content], {
type: "application/vnd.ms-excel;charset=utf-8;"
});
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = fileName || "开票单.xls";
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
}

View File

@ -1,213 +0,0 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
<el-form-item label="收票编号" prop="receiptNo">
<el-input v-model="queryParams.receiptNo" placeholder="请输入收票编号" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="供应商" prop="vendorName">
<el-input v-model="queryParams.vendorName" placeholder="请输入供应商" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="项目名称" prop="projectName">
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="提交日期">
<el-date-picker
v-model="dateRange"
style="width: 240px"
value-format="yyyy-MM-dd"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
<el-table v-loading="loading" :data="invoiceReceiptList">
<el-table-column label="序号" type="index" width="50" align="center" />
<el-table-column label="收票编号" align="center" prop="ticketBillCode" />
<el-table-column label="供应商" align="center" prop="vendorName" />
<el-table-column label="金额" align="center" prop="totalPriceWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.applyTime) }}</span>
</template>
</el-table-column>
<el-table-column label="审批节点" align="center" prop="approveNode" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row)"></el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
<!-- 详情对话框 -->
<el-dialog title="收票单详情" :visible.sync="detailDialogVisible" width="80%" append-to-body>
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
<el-button
type="primary"
size="small"
icon="el-icon-download"
@click="exportPDF"
:loading="pdfExporting"
>导出PDF</el-button>
</div>
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
<ApproveLayout ref="approveLayout" title="收票单详情">
<invoice-receipt-detail :data="form"></invoice-receipt-detail>
<template #footer>
<span> {{ form.ticketBillCode }}</span>
</template>
</ApproveLayout>
</div>
<el-divider content-position="left">流转意见</el-divider>
<div class="process-container">
<el-timeline>
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
<el-card>
<h4>{{ log.approveOpinion }}</h4>
<p><b>操作人:</b> {{ log.approveUserName }} </p>
<p><b>审批状态:</b> <el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
</el-card>
</el-timeline-item>
</el-timeline>
<div v-if="!approveLogs || approveLogs.length === 0"></div>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="detailDialogVisible = false"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { listInvoiceReceiptApproved, getInvoiceReceipt } from "@/api/finance/invoiceReceipt";
import { listCompletedFlows } from "@/api/flow";
import InvoiceReceiptDetail from "../components/InvoiceReceiptDetail";
import ApproveLayout from "@/views/approve/ApproveLayout";
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
export default {
name: "InvoiceReceiptApproved",
components: { InvoiceReceiptDetail, ApproveLayout },
data() {
return {
loading: true,
showSearch: true,
total: 0,
invoiceReceiptList: [],
queryParams: {
pageNum: 1,
pageSize: 10,
receiptNo: null,
vendorName: null,
processKey: 'fianance_ticket',
projectName: null,
orderByColumn: 'applyTime',
isAsc: 'desc'
},
dateRange: [],
detailDialogVisible: false,
detailLoading: false,
form: {},
approveLogs: [],
currentInvoiceReceiptId: null,
pdfExporting: false
};
},
created() {
this.getList();
},
methods: {
getList() {
this.loading = true;
listInvoiceReceiptApproved(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
this.invoiceReceiptList = response.rows;
this.total = response.total;
this.loading = false;
});
},
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
resetQuery() {
this.dateRange = [];
this.resetForm("queryForm");
this.handleQuery();
},
handleView(row) {
this.form = {};
this.approveLogs = [];
this.currentInvoiceReceiptId = row.id;
this.detailLoading = true;
this.detailDialogVisible = true;
getInvoiceReceipt(this.currentInvoiceReceiptId).then(response => {
this.form = response.data;
this.loadApproveHistory(this.form.ticketBillCode);
this.detailLoading = false;
}).catch(() => {
this.detailLoading = false;
});
},
loadApproveHistory(businessKey) {
if (businessKey) {
listCompletedFlows({ businessKey: businessKey }).then(response => {
this.approveLogs = response.data;
});
}
},
getStatusText(status) {
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
return map[status] || '提交审批';
},
async exportPDF() {
this.pdfExporting = true;
try {
const element = this.$refs.approveLayout.$el;
const fileName = `收票单-${this.form.receiptBillCode || ''}.pdf`;
await exportElementToPDF(element, fileName);
this.$modal.msgSuccess('PDF导出成功');
} catch (error) {
console.error('PDF导出失败:', error);
this.$modal.msgError('PDF导出失败请稍后重试');
} finally {
this.pdfExporting = false;
}
}
}
};
</script>
<style scoped>
.process-container {
padding: 10px;
}
/* 导出PDF时的特殊样式 */
.approve-container.exporting-pdf ::v-deep .el-button--primary,
.approve-container.exporting-pdf ::v-deep .el-button--text {
display: none;
}
.approve-container.exporting-pdf ::v-deep .el-input__inner,
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
border: none !important;
box-shadow: none !important;
background-color: transparent !important;
resize: none !important;
padding: 0 !important;
}
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
display: none;
}
</style>

View File

@ -1,138 +0,0 @@
<template>
<div class="invoice-receipt-detail">
<div style="text-align: center;font-weight:bold;font-size: 25px;">收票申请单</div>
<el-descriptions title="收票单信息" :column="3" border>
<el-descriptions-item label="采购-收票单编号">{{ data.ticketBillCode }}</el-descriptions-item>
<el-descriptions-item label="制造商名称">{{ data.vendorName }}</el-descriptions-item>
<el-descriptions-item label="票据类型">
<dict-tag :options="dict.type.finance_invoice_type" :value="data.ticketType"/>
</el-descriptions-item>
<el-descriptions-item label="含税总价(元)">{{ formatCurrency(data.totalPriceWithTax) }}</el-descriptions-item>
<el-descriptions-item label="未税总价(元)">{{ formatCurrency(data.totalPriceWithoutTax) }}</el-descriptions-item>
<el-descriptions-item label="税额(元)">{{ formatCurrency(data.taxAmount) }}</el-descriptions-item>
<el-descriptions-item label="预计收票时间">{{ data.ticketTime }}</el-descriptions-item>
<el-descriptions-item label="制造商开票时间">{{ data.vendorTicketTime }}</el-descriptions-item>
<!-- <el-descriptions-item label="备注" :span="3">{{ data.remark }}</el-descriptions-item>-->
</el-descriptions>
<div class="section" style="margin-top: 20px;" v-show="data.detailList && data.detailList.length>0">
<div class="el-descriptions__title">发票明细列表</div>
<el-table :data="data.detailList" border style="width: 100%; margin-top: 10px;">
<el-table-column type="index" label="序号" width="50" align="center"></el-table-column>
<el-table-column prop="payableBillCode" label="采购-应付单编号" align="center"></el-table-column>
<el-table-column prop="projectName" label="项目名称" align="center"></el-table-column>
<el-table-column prop="productType" label="产品类型" align="center">
<template slot-scope="scope">
<dict-tag :options="dict.type.product_type" :value="scope.row.productType"/>
</template>
</el-table-column>
<el-table-column prop="totalPriceWithTax" label="含税总价" align="center" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"></el-table-column>
<el-table-column prop="paymentAmount" label="本次收票金额" align="center" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"></el-table-column>
</el-table>
</div>
<div class="section" style="margin-top: 20px;" v-if="attachments && attachments.length > 0">
<div class="el-descriptions__title">附件信息</div>
<el-table :data="attachments" border style="width: 100%; margin-top: 10px;">
<el-table-column prop="fileName" label="附件名称" align="center"></el-table-column>
<el-table-column prop="createByName" label="上传人" align="center"></el-table-column>
<el-table-column prop="createTime" label="上传时间" align="center"></el-table-column>
<el-table-column label="操作" align="center">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-view" @click="handlePreview(scope.row)"></el-button>
<el-button size="mini" type="text" icon="el-icon-download" @click="handleDownload(scope.row)"></el-button>
</template>
</el-table-column>
</el-table>
</div>
<el-dialog :visible.sync="pdfPreviewVisible" width="80%" append-to-body top="5vh" title="PDF预览">
<iframe :src="currentPdfUrl" width="100%" height="600px" frameborder="0"></iframe>
</el-dialog>
<el-dialog :visible.sync="imagePreviewVisible" width="60%" append-to-body top="5vh" title="图片预览">
<img :src="currentImageUrl" style="width: 100%;" />
</el-dialog>
</div>
</template>
<script>
import { getInvoiceReceiptAttachments } from "@/api/finance/invoiceReceipt";
import request from '@/utils/request';
export default {
name: "InvoiceReceiptDetail",
props: {
data: {
type: Object,
required: true,
default: () => ({})
}
},
dicts: ['finance_invoice_type', 'product_type'],
data() {
return {
attachments: [],
pdfPreviewVisible: false,
currentPdfUrl: '',
imagePreviewVisible: false,
currentImageUrl: ''
};
},
watch: {
'data.id': {
handler(val) {
if (val) {
this.fetchAttachments(val);
}
},
immediate: true
}
},
methods: {
fetchAttachments(id) {
getInvoiceReceiptAttachments(id,{ type: 'ticket' }).then(response => {
this.attachments = (response.data || []).filter(item => item.delFlag !== '2');
});
},
isPdf(filePath) {
return filePath && filePath.toLowerCase().endsWith('.pdf');
},
getImageUrl(resource) {
return process.env.VUE_APP_BASE_API + "/common/download/resource?resource=" + resource;
},
handlePreview(row) {
if (this.isPdf(row.filePath)) {
request({
url: '/common/download/resource',
method: 'get',
params: { resource: row.filePath },
responseType: 'blob'
}).then(res => {
const blob = new Blob([res.data], { type: 'application/pdf' });
this.currentPdfUrl = URL.createObjectURL(blob);
this.pdfPreviewVisible = true;
});
} else {
this.currentImageUrl = this.getImageUrl(row.filePath);
this.imagePreviewVisible = true;
}
},
handleDownload(row) {
const link = document.createElement('a');
link.href = this.getImageUrl(row.filePath);
link.download = row.fileName || 'attachment';
link.style.display = 'none';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
}
};
</script>
<style scoped>
.invoice-receipt-detail {
margin-bottom: 20px;
}
</style>

View File

@ -1,302 +0,0 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
<el-form-item label="收票编号" prop="receiptNo">
<el-input v-model="queryParams.receiptNo" placeholder="请输入收票编号" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="供应商" prop="vendorName">
<el-input v-model="queryParams.vendorName" placeholder="请输入供应商" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="项目名称" prop="projectName">
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="提交日期">
<el-date-picker
v-model="dateRange"
style="width: 240px"
value-format="yyyy-MM-dd"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
size="mini"
@click="toApproved()"
>审批历史</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="invoiceReceiptList">
<el-table-column label="序号" type="index" width="50" align="center" />
<el-table-column label="收票编号" align="center" prop="ticketBillCode" />
<el-table-column label="制造商" align="center" prop="vendorName" />
<!-- <el-table-column label="项目名称" align="center" prop="projectName" />-->
<el-table-column label="金额" align="center" prop="totalPriceWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
<!-- <el-table-column label="登记人" align="center" prop="createUserName" />-->
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.applyTime) }}</span>
</template>
</el-table-column>
<el-table-column label="审批节点" align="center" prop="approveNode" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleApprove(scope.row)"></el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
<!-- 审批详情主对话框 -->
<el-dialog title="收票单审批" :visible.sync="detailDialogVisible" width="80%" append-to-body>
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
<el-button
type="primary"
size="small"
icon="el-icon-download"
@click="exportPDF"
:loading="pdfExporting"
>导出PDF</el-button>
</div>
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
<ApproveLayout ref="approveLayout" title="收票单详情">
<invoice-receipt-detail :data="form"></invoice-receipt-detail>
<template #footer>
<span>{{ form.ticketBillCode }}</span>
</template>
</ApproveLayout>
</div>
<el-divider content-position="left">流转意见</el-divider>
<div class="process-container">
<el-timeline>
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
<el-card>
<h4>{{ log.approveOpinion }}</h4>
<p><b>操作人:</b> {{ log.approveUserName }} </p>
<p><b>审批状态:</b><el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
</el-card>
</el-timeline-item>
</el-timeline>
<div v-if="!approveLogs || approveLogs.length === 0"></div>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="openOpinionDialog('approve')"></el-button>
<el-button type="danger" @click="openOpinionDialog('reject')"></el-button>
<el-button @click="detailDialogVisible = false"> </el-button>
</span>
</el-dialog>
<!-- 审批意见对话框 -->
<el-dialog :title="confirmDialogTitle" :visible.sync="opinionDialogVisible" width="30%" append-to-body>
<el-form ref="opinionForm" :model="opinionForm" :rules="opinionRules" label-width="100px">
<el-form-item label="审批意见" prop="approveOpinion">
<el-input v-model="opinionForm.approveOpinion" type="textarea" :rows="4" placeholder="请输入审批意见"/>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="opinionDialogVisible = false"> </el-button>
<el-button type="primary" @click="showConfirmDialog()"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { listInvoiceReceiptApprove, getInvoiceReceipt } from "@/api/finance/invoiceReceipt";
import { approveTask, listCompletedFlows } from "@/api/flow";
import InvoiceReceiptDetail from "./components/InvoiceReceiptDetail";
import ApproveLayout from "@/views/approve/ApproveLayout";
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
export default {
name: "InvoiceReceiptApprove",
components: { InvoiceReceiptDetail, ApproveLayout },
data() {
return {
loading: true,
showSearch: true,
total: 0,
invoiceReceiptList: [],
queryParams: {
pageNum: 1,
pageSize: 10,
receiptNo: null,
vendorName: null,
processKey: 'fianance_ticket',
projectName: null,
orderByColumn: 'applyTime',
isAsc: 'desc'
},
dateRange: [],
detailDialogVisible: false,
detailLoading: false,
form: {},
approveLogs: [],
opinionDialogVisible: false,
confirmDialogTitle: '',
currentApproveType: '',
opinionForm: {
approveOpinion: ''
},
opinionRules: {
approveOpinion: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }],
},
processKey: 'fianance_ticket',
taskId: null,
currentInvoiceReceiptId: null,
pdfExporting: false
};
},
created() {
this.getList();
},
methods: {
getList() {
this.loading = true;
listInvoiceReceiptApprove(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
this.invoiceReceiptList = response.rows;
this.total = response.total;
this.loading = false;
});
},
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
resetQuery() {
this.dateRange = [];
this.resetForm("queryForm");
this.handleQuery();
},
toApproved() {
this.$router.push( '/approve/invoiceLog' )
},
handleApprove(row) {
this.resetDetailForm();
this.currentInvoiceReceiptId = row.id;
this.taskId = row.taskId;
this.detailLoading = true;
this.detailDialogVisible = true;
getInvoiceReceipt(this.currentInvoiceReceiptId).then(response => {
this.form = response.data;
this.loadApproveHistory(this.form.ticketBillCode);
this.detailLoading = false;
}).catch(() => {
this.detailLoading = false;
});
},
resetDetailForm() {
this.form = {};
this.approveLogs = [];
this.opinionForm.approveOpinion = '';
},
loadApproveHistory(businessKey) {
if (businessKey) {
let keys = [];
if(this.processKey) keys.push(this.processKey);
listCompletedFlows({ businessKey: businessKey, processKeyList: keys }).then(response => {
this.approveLogs = response.data;
});
}
},
openOpinionDialog(type) {
this.currentApproveType = type;
this.confirmDialogTitle = type === 'approve' ? '同意审批' : '驳回审批';
this.opinionDialogVisible = true;
this.opinionForm.approveOpinion = '';
this.$nextTick(() => {
if(this.$refs.opinionForm) this.$refs.opinionForm.clearValidate();
});
},
showConfirmDialog() {
this.$refs.opinionForm.validate(valid => {
if (valid) {
this.opinionDialogVisible = false;
this.submitApproval();
}
});
},
submitApproval() {
const approveBtn = this.currentApproveType === 'approve' ? 1 : 0;
const params = {
businessKey: this.form.ticketBillCode,
processKey: this.processKey,
taskId: this.taskId,
variables: {
comment: this.opinionForm.approveOpinion,
approveBtn: approveBtn
}
};
approveTask(params).then(() => {
this.$modal.msgSuccess(this.confirmDialogTitle + "成功");
this.detailDialogVisible = false;
this.getList();
});
},
getStatusText(status) {
if (!status) {
return '提交审批'
}
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
return map[status] || '提交审批';
},
async exportPDF() {
this.pdfExporting = true;
try {
const element = this.$refs.approveLayout.$el;
const fileName = `收票单-${this.form.ticketBillCode || ''}.pdf`;
await exportElementToPDF(element, fileName);
this.$modal.msgSuccess('PDF导出成功');
} catch (error) {
console.error('PDF导出失败:', error);
this.$modal.msgError('PDF导出失败请稍后重试');
} finally {
this.pdfExporting = false;
}
}
}
};
</script>
<style scoped>
.process-container {
padding: 10px;
}
/* 导出PDF时的特殊样式 */
.approve-container.exporting-pdf ::v-deep .el-button--primary,
.approve-container.exporting-pdf ::v-deep .el-button--text {
display: none;
}
.approve-container.exporting-pdf ::v-deep .el-input__inner,
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
border: none !important;
box-shadow: none !important;
background-color: transparent !important;
resize: none !important;
padding: 0 !important;
}
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
display: none;
}
</style>

View File

@ -1,214 +0,0 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
<el-form-item label="收票编号" prop="receiptNo">
<el-input v-model="queryParams.receiptNo" placeholder="请输入收票编号" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="供应商" prop="vendorName">
<el-input v-model="queryParams.vendorName" placeholder="请输入供应商" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="项目名称" prop="projectName">
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="提交日期">
<el-date-picker
v-model="dateRange"
style="width: 240px"
value-format="yyyy-MM-dd"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
<el-table v-loading="loading" :data="invoiceReceiptList">
<el-table-column label="序号" type="index" width="50" align="center" />
<el-table-column label="收票编号" align="center" prop="ticketBillCode" />
<el-table-column label="供应商" align="center" prop="vendorName" />
<el-table-column label="金额" align="center" prop="totalPriceWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.applyTime) }}</span>
</template>
</el-table-column>
<el-table-column label="审批节点" align="center" prop="approveNode" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row)"></el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
<!-- 详情对话框 -->
<el-dialog title="红冲发票详情" :visible.sync="detailDialogVisible" width="80%" append-to-body>
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
<el-button
type="primary"
size="small"
icon="el-icon-download"
@click="exportPDF"
:loading="pdfExporting"
>导出PDF</el-button>
</div>
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
<ApproveLayout ref="approveLayout" title="红冲发票详情">
<invoice-red-detail :data="form"></invoice-red-detail>
<template #footer>
<span> {{ form.ticketBillCode }}</span>
</template>
</ApproveLayout>
</div>
<el-divider content-position="left">流转意见</el-divider>
<div class="process-container">
<el-timeline>
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
<el-card>
<h4>{{ log.approveOpinion }}</h4>
<p><b>操作人:</b> {{ log.approveUserName }} </p>
<p><b>审批状态:</b> <el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
</el-card>
</el-timeline-item>
</el-timeline>
<div v-if="!approveLogs || approveLogs.length === 0"></div>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="detailDialogVisible = false"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { listInvoiceReceiptApproved, getInvoiceReceipt } from "@/api/finance/invoiceReceipt";
import { listCompletedFlows } from "@/api/flow";
import InvoiceRedDetail from "../components/InvoiceRedDetail";
import ApproveLayout from "@/views/approve/ApproveLayout";
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
export default {
name: "InvoiceRedApproved",
components: { InvoiceRedDetail, ApproveLayout },
data() {
return {
loading: true,
showSearch: true,
total: 0,
invoiceReceiptList: [],
queryParams: {
pageNum: 1,
pageSize: 10,
receiptNo: null,
vendorName: null,
processKey: 'finance_ticket_refound',
projectName: null,
orderByColumn: 'applyTime',
isAsc: 'desc'
},
dateRange: [],
detailDialogVisible: false,
detailLoading: false,
form: {},
approveLogs: [],
currentInvoiceReceiptId: null,
pdfExporting: false
};
},
created() {
this.getList();
},
methods: {
getList() {
this.loading = true;
listInvoiceReceiptApproved(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
this.invoiceReceiptList = response.rows;
this.total = response.total;
this.loading = false;
});
},
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
resetQuery() {
this.dateRange = [];
this.resetForm("queryForm");
this.handleQuery();
},
handleView(row) {
this.form = {};
this.approveLogs = [];
this.currentInvoiceReceiptId = row.id;
this.detailLoading = true;
this.detailDialogVisible = true;
getInvoiceReceipt(this.currentInvoiceReceiptId).then(response => {
this.form = response.data;
this.loadApproveHistory(this.form.ticketBillCode);
this.detailLoading = false;
}).catch(() => {
this.detailLoading = false;
});
},
loadApproveHistory(businessKey) {
if (businessKey) {
listCompletedFlows({ businessKey: businessKey }).then(response => {
this.approveLogs = response.data;
});
}
},
getStatusText(status) {
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
return map[status] || '提交审批';
},
async exportPDF() {
this.pdfExporting = true;
try {
const element = this.$refs.approveLayout.$el;
const fileName = `红冲发票-${this.form.receiptBillCode || ''}.pdf`;
await exportElementToPDF(element, fileName);
this.$modal.msgSuccess('PDF导出成功');
} catch (error) {
console.error('PDF导出失败:', error);
this.$modal.msgError('PDF导出失败请稍后重试');
} finally {
this.pdfExporting = false;
}
}
}
};
</script>
<style scoped>
.process-container {
padding: 10px;
}
/* 导出PDF时的特殊样式 */
.approve-container.exporting-pdf ::v-deep .el-button--primary,
.approve-container.exporting-pdf ::v-deep .el-button--text {
display: none;
}
.approve-container.exporting-pdf ::v-deep .el-input__inner,
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
border: none !important;
box-shadow: none !important;
background-color: transparent !important;
resize: none !important;
padding: 0 !important;
}
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
display: none;
}
</style>

View File

@ -1,139 +0,0 @@
<template>
<div class="invoice-red-detail">
<div style="text-align: center;font-weight:bold;font-size: 25px;">红冲收票申请单</div>
<el-descriptions title="红冲发票信息" :column="3" border>
<el-descriptions-item label="采购-收票单编号">{{ data.ticketBillCode }}</el-descriptions-item>
<el-descriptions-item label="制造商名称">{{ data.vendorName }}</el-descriptions-item>
<el-descriptions-item label="票据类型">
<dict-tag :options="dict.type.finance_invoice_type" :value="data.ticketType"/>
</el-descriptions-item>
<el-descriptions-item label="含税总价(元)"><span style="color: red">{{ formatCurrency(data.totalPriceWithTax) }} </span></el-descriptions-item>
<el-descriptions-item label="未税总价(元)"><span style="color: red">{{ formatCurrency(data.totalPriceWithTax) }}</span></el-descriptions-item>
<el-descriptions-item label="税额(元)"><span style="color: red">{{ formatCurrency(data.taxAmount) }} </span></el-descriptions-item>
<el-descriptions-item label="收票时间">{{ data.ticketTime }}</el-descriptions-item>
<el-descriptions-item label="制造商开票时间">{{ data.vendorTicketTime }}</el-descriptions-item>
<!-- <el-descriptions-item label="备注" :span="3">{{ data.remark }}</el-descriptions-item>-->
</el-descriptions>
<div class="section" style="margin-top: 20px;" v-show="data.detailList && data.detailList.length>0">
<div class="el-descriptions__title">发票明细列表</div>
<el-table :data="data.detailList" border style="width: 100%; margin-top: 10px;">
<el-table-column type="index" label="序号" width="50" align="center"></el-table-column>
<el-table-column prop="payableBillCode" label="采购-应付单编号" align="center"></el-table-column>
<el-table-column prop="projectName" label="项目名称" align="center"></el-table-column>
<el-table-column prop="productType" label="产品类型" align="center">
<template slot-scope="scope">
<dict-tag :options="dict.type.product_type" :value="scope.row.productType"/>
</template>
</el-table-column>
<el-table-column prop="totalPriceWithTax" label="含税总价" align="center" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"></el-table-column>
<el-table-column prop="paymentAmount" label="本次红冲金额" align="center" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"></el-table-column>
</el-table>
</div>
<div class="section" style="margin-top: 20px;" v-if="attachments && attachments.length > 0">
<div class="el-descriptions__title">附件信息</div>
<el-table :data="attachments" border style="width: 100%; margin-top: 10px;">
<el-table-column prop="fileName" label="附件名称" align="center"></el-table-column>
<el-table-column prop="createByName" label="上传人" align="center"></el-table-column>
<el-table-column prop="createTime" label="上传时间" align="center"></el-table-column>
<el-table-column label="操作" align="center">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-view" @click="handlePreview(scope.row)"></el-button>
<el-button size="mini" type="text" icon="el-icon-download" @click="handleDownload(scope.row)"></el-button>
</template>
</el-table-column>
</el-table>
</div>
<el-dialog :visible.sync="pdfPreviewVisible" width="80%" append-to-body top="5vh" title="PDF预览">
<iframe :src="currentPdfUrl" width="100%" height="600px" frameborder="0"></iframe>
</el-dialog>
<el-dialog :visible.sync="imagePreviewVisible" width="60%" append-to-body top="5vh" title="图片预览">
<img :src="currentImageUrl" style="width: 100%;" />
</el-dialog>
</div>
</template>
<script>
import { getInvoiceReceiptAttachments } from "@/api/finance/invoiceReceipt";
import request from '@/utils/request';
export default {
name: "InvoiceRedDetail",
props: {
data: {
type: Object,
required: true,
default: () => ({})
}
},
dicts: ['finance_invoice_type', 'product_type'],
data() {
return {
attachments: [],
pdfPreviewVisible: false,
currentPdfUrl: '',
imagePreviewVisible: false,
currentImageUrl: ''
};
},
watch: {
'data.id': {
handler(val) {
if (val) {
this.fetchAttachments(val);
}
},
immediate: true
}
},
methods: {
fetchAttachments(id) {
// invoiceReceipt
getInvoiceReceiptAttachments(id,{ type: 'ticket' }).then(response => {
this.attachments = (response.data || []).filter(item => item.delFlag !== '2');
});
},
isPdf(filePath) {
return filePath && filePath.toLowerCase().endsWith('.pdf');
},
getImageUrl(resource) {
return process.env.VUE_APP_BASE_API + "/common/download/resource?resource=" + resource;
},
handlePreview(row) {
if (this.isPdf(row.filePath)) {
request({
url: '/common/download/resource',
method: 'get',
params: { resource: row.filePath },
responseType: 'blob'
}).then(res => {
const blob = new Blob([res.data], { type: 'application/pdf' });
this.currentPdfUrl = URL.createObjectURL(blob);
this.pdfPreviewVisible = true;
});
} else {
this.currentImageUrl = this.getImageUrl(row.filePath);
this.imagePreviewVisible = true;
}
},
handleDownload(row) {
const link = document.createElement('a');
link.href = this.getImageUrl(row.filePath);
link.download = row.fileName || 'attachment';
link.style.display = 'none';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
}
};
</script>
<style scoped>
.invoice-red-detail {
margin-bottom: 20px;
}
</style>

View File

@ -1,304 +0,0 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
<el-form-item label="收票编号" prop="receiptNo">
<el-input v-model="queryParams.receiptNo" placeholder="请输入收票编号" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="供应商" prop="vendorName">
<el-input v-model="queryParams.vendorName" placeholder="请输入供应商" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="项目名称" prop="projectName">
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="提交日期">
<el-date-picker
v-model="dateRange"
style="width: 240px"
value-format="yyyy-MM-dd"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
size="mini"
@click="toApproved()"
>审批历史</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="invoiceReceiptList">
<el-table-column label="序号" type="index" width="50" align="center" />
<el-table-column label="收票编号" align="center" prop="ticketBillCode" />
<el-table-column label="供应商" align="center" prop="vendorName" />
<!-- <el-table-column label="项目名称" align="center" prop="projectName" />-->
<el-table-column label="金额" align="center" prop="totalPriceWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)">
</el-table-column>
<!-- <el-table-column label="登记人" align="center" prop="createUserName" />-->
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.applyTime) }}</span>
</template>
</el-table-column>
<el-table-column label="审批节点" align="center" prop="approveNode" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleApprove(scope.row)"></el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
<!-- 审批详情主对话框 -->
<el-dialog title="红冲发票审批" :visible.sync="detailDialogVisible" width="80%" append-to-body>
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
<el-button
type="primary"
size="small"
icon="el-icon-download"
@click="exportPDF"
:loading="pdfExporting"
>导出PDF</el-button>
</div>
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
<ApproveLayout ref="approveLayout" title="红冲发票详情">
<invoice-red-detail :data="form"></invoice-red-detail>
<template #footer>
<span> {{ form.ticketBillCode }}</span>
</template>
</ApproveLayout>
</div>
<el-divider content-position="left">流转意见</el-divider>
<div class="process-container">
<el-timeline>
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
<el-card>
<h4>{{ log.approveOpinion }}</h4>
<p><b>操作人:</b> {{ log.approveUserName }} </p>
<p><b>审批状态:</b> <el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
</el-card>
</el-timeline-item>
</el-timeline>
<div v-if="!approveLogs || approveLogs.length === 0"></div>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="openOpinionDialog('approve')"></el-button>
<el-button type="danger" @click="openOpinionDialog('reject')"></el-button>
<el-button @click="detailDialogVisible = false"> </el-button>
</span>
</el-dialog>
<!-- 审批意见对话框 -->
<el-dialog :title="confirmDialogTitle" :visible.sync="opinionDialogVisible" width="30%" append-to-body>
<el-form ref="opinionForm" :model="opinionForm" :rules="opinionRules" label-width="100px">
<el-form-item label="审批意见" prop="approveOpinion">
<el-input v-model="opinionForm.approveOpinion" type="textarea" :rows="4" placeholder="请输入审批意见"/>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="opinionDialogVisible = false"> </el-button>
<el-button type="primary" @click="showConfirmDialog()"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { listInvoiceReceiptApprove, getInvoiceReceipt } from "@/api/finance/invoiceReceipt";
import { approveTask, listCompletedFlows } from "@/api/flow";
import InvoiceRedDetail from "./components/InvoiceRedDetail";
import ApproveLayout from "@/views/approve/ApproveLayout";
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
export default {
name: "InvoiceRedApprove",
components: { InvoiceRedDetail, ApproveLayout },
data() {
return {
loading: true,
showSearch: true,
total: 0,
invoiceReceiptList: [],
queryParams: {
pageNum: 1,
pageSize: 10,
receiptNo: null,
vendorName: null,
processKey: 'finance_ticket_refound',
projectName: null,
orderByColumn: 'applyTime',
isAsc: 'desc'
},
dateRange: [],
detailDialogVisible: false,
detailLoading: false,
form: {},
approveLogs: [],
opinionDialogVisible: false,
confirmDialogTitle: '',
currentApproveType: '',
opinionForm: {
approveOpinion: ''
},
opinionRules: {
approveOpinion: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }],
},
processKey: 'finance_ticket_refound',
taskId: null,
currentInvoiceReceiptId: null,
pdfExporting: false
};
},
created() {
this.getList();
},
methods: {
getList() {
this.loading = true;
listInvoiceReceiptApprove(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
this.invoiceReceiptList = response.rows;
this.total = response.total;
this.loading = false;
});
},
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
resetQuery() {
this.dateRange = [];
this.resetForm("queryForm");
this.handleQuery();
},
toApproved() {
this.$router.push( '/approve/invoiceRedLog' )
},
handleApprove(row) {
this.resetDetailForm();
this.currentInvoiceReceiptId = row.id;
this.taskId = row.taskId;
this.detailLoading = true;
this.detailDialogVisible = true;
getInvoiceReceipt(this.currentInvoiceReceiptId).then(response => {
this.form = response.data;
this.loadApproveHistory(this.form.ticketBillCode);
this.detailLoading = false;
}).catch(() => {
this.detailLoading = false;
});
},
resetDetailForm() {
this.form = {};
this.approveLogs = [];
this.opinionForm.approveOpinion = '';
},
loadApproveHistory(businessKey) {
if (businessKey) {
let keys = [];
if(this.processKey) keys.push(this.processKey);
listCompletedFlows({ businessKey: businessKey, processKeyList: keys }).then(response => {
this.approveLogs = response.data;
});
}
},
openOpinionDialog(type) {
this.currentApproveType = type;
this.confirmDialogTitle = type === 'approve' ? '同意审批' : '驳回审批';
this.opinionDialogVisible = true;
this.opinionForm.approveOpinion = '';
this.$nextTick(() => {
if(this.$refs.opinionForm) this.$refs.opinionForm.clearValidate();
});
},
showConfirmDialog() {
this.$refs.opinionForm.validate(valid => {
if (valid) {
this.opinionDialogVisible = false;
this.submitApproval();
}
});
},
submitApproval() {
const approveBtn = this.currentApproveType === 'approve' ? 1 : 0;
const params = {
businessKey: this.form.ticketBillCode,
processKey: this.processKey,
taskId: this.taskId,
variables: {
comment: this.opinionForm.approveOpinion,
approveBtn: approveBtn
}
};
approveTask(params).then(() => {
this.$modal.msgSuccess(this.confirmDialogTitle + "成功");
this.detailDialogVisible = false;
this.getList();
});
},
getStatusText(status) {
if (!status) {
return '提交审批'
}
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
return map[status] || '提交审批';
},
async exportPDF() {
this.pdfExporting = true;
try {
const element = this.$refs.approveLayout.$el;
const fileName = `红冲发票-${this.form.ticketBillCode || ''}.pdf`;
await exportElementToPDF(element, fileName);
this.$modal.msgSuccess('PDF导出成功');
} catch (error) {
console.error('PDF导出失败:', error);
this.$modal.msgError('PDF导出失败请稍后重试');
} finally {
this.pdfExporting = false;
}
}
}
};
</script>
<style scoped>
.process-container {
padding: 10px;
}
/* 导出PDF时的特殊样式 */
.approve-container.exporting-pdf ::v-deep .el-button--primary,
.approve-container.exporting-pdf ::v-deep .el-button--text {
display: none;
}
.approve-container.exporting-pdf ::v-deep .el-input__inner,
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
border: none !important;
box-shadow: none !important;
background-color: transparent !important;
resize: none !important;
padding: 0 !important;
}
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
display: none;
}
</style>

View File

@ -1,215 +0,0 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
<el-form-item label="付款编号" prop="paymentNo">
<el-input v-model="queryParams.paymentNo" placeholder="请输入付款编号" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="制造商" prop="manufacturer">
<el-input v-model="queryParams.manufacturer" placeholder="请输入制造商" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="项目名称" prop="projectName">
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="提交日期">
<el-date-picker
v-model="dateRange"
style="width: 240px"
value-format="yyyy-MM-dd"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
<el-table v-loading="loading" :data="paymentList">
<el-table-column label="序号" type="index" width="50" align="center" />
<el-table-column label="付款编号" align="center" prop="paymentBillCode" />
<el-table-column label="制造商" align="center" prop="vendorName" />
<!-- <el-table-column label="项目名称" align="center" prop="projectName" />-->
<el-table-column label="金额" align="center" prop="totalPriceWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
<!-- <el-table-column label="汇智负责人" align="center" prop="hzUserName" />-->
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.applyTime) }}</span>
</template>
</el-table-column>
<el-table-column label="审批节点" align="center" prop="approveNode" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row)"></el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
<!-- 详情对话框 -->
<el-dialog title="付款单详情" :visible.sync="detailDialogVisible" width="80%" append-to-body>
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
<el-button
type="primary"
size="small"
icon="el-icon-download"
@click="exportPDF"
:loading="pdfExporting"
>导出PDF</el-button>
</div>
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
<ApproveLayout ref="approveLayout" title="付款单详情">
<payment-detail :data="form"></payment-detail>
<template #footer>
<span> {{ form.paymentBillCode }}</span>
</template>
</ApproveLayout>
</div>
<el-divider content-position="left">流转意见</el-divider>
<div class="process-container">
<el-timeline>
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
<el-card>
<h4>{{ log.approveOpinion }}</h4>
<p><b>操作人:</b> {{ log.approveUserName }} </p>
<p><b>审批状态:</b><el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
</el-card>
</el-timeline-item>
</el-timeline>
<div v-if="!approveLogs || approveLogs.length === 0"></div>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="detailDialogVisible = false"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { listPaymentApproved, getPayment } from "@/api/finance/payment";
import { listCompletedFlows } from "@/api/flow";
import PaymentDetail from "../components/PaymentDetail"; // Relative path adjustment
import ApproveLayout from "@/views/approve/ApproveLayout";
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
export default {
name: "PaymentApproved",
components: { PaymentDetail, ApproveLayout },
data() {
return {
loading: true,
showSearch: true,
total: 0,
paymentList: [],
queryParams: {
pageNum: 1,
pageSize: 10,
paymentNo: null,
manufacturer: null,
projectName: null,
processKey: 'finance_payment',
orderByColumn: 'applyTime',
isAsc: 'desc'
},
dateRange: [],
detailDialogVisible: false,
detailLoading: false,
form: {},
approveLogs: [],
currentPaymentId: null,
pdfExporting: false
};
},
created() {
this.getList();
},
methods: {
getList() {
this.loading = true;
listPaymentApproved(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
this.paymentList = response.rows;
this.total = response.total;
this.loading = false;
});
},
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
resetQuery() {
this.dateRange = [];
this.resetForm("queryForm");
this.handleQuery();
},
handleView(row) {
this.form = {};
this.approveLogs = [];
this.currentPaymentId = row.id;
this.detailLoading = true;
this.detailDialogVisible = true;
getPayment(this.currentPaymentId).then(response => {
this.form = response.data;
this.loadApproveHistory(this.form.paymentBillCode);
this.detailLoading = false;
}).catch(() => {
this.detailLoading = false;
});
},
loadApproveHistory(businessKey) {
if (businessKey) {
listCompletedFlows({ businessKey: businessKey }).then(response => {
this.approveLogs = response.data;
});
}
},
getStatusText(status) {
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
return map[status] || '提交审批';
},
async exportPDF() {
this.pdfExporting = true;
try {
const element = this.$refs.approveLayout.$el;
const fileName = `付款单-${this.form.paymentBillCode || ''}.pdf`;
await exportElementToPDF(element, fileName);
this.$modal.msgSuccess('PDF导出成功');
} catch (error) {
console.error('PDF导出失败:', error);
this.$modal.msgError('PDF导出失败请稍后重试');
} finally {
this.pdfExporting = false;
}
}
}
};
</script>
<style scoped>
.process-container {
padding: 10px;
}
/* 导出PDF时的特殊样式 */
.approve-container.exporting-pdf ::v-deep .el-button--primary,
.approve-container.exporting-pdf ::v-deep .el-button--text {
display: none;
}
.approve-container.exporting-pdf ::v-deep .el-input__inner,
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
border: none !important;
box-shadow: none !important;
background-color: transparent !important;
resize: none !important;
padding: 0 !important;
}
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
display: none;
}
</style>

View File

@ -1,284 +0,0 @@
<template>
<div class="payment-detail">
<div style="text-align: center;font-weight:bold;font-size: 25px;">付款申请单</div>
<el-descriptions title="付款单信息" :column="3" border>
<el-descriptions-item label="采购-付款单编号">{{ data.paymentBillCode }}</el-descriptions-item>
<el-descriptions-item label="制造商名称">{{ data.vendorName }}</el-descriptions-item>
<el-descriptions-item label="付款条件">
{{data.payType==='0'?'入库付款':'出库付款'}}
</el-descriptions-item>
<el-descriptions-item label="付款周期">
{{data.payConfigDay}}
</el-descriptions-item>
<el-descriptions-item label="含税总价(元)">{{ formatCurrency(data.totalPriceWithTax) }}</el-descriptions-item>
<!-- <el-descriptions-item label="未税总价(元)">{{ data.totalPriceWithoutTax }}</el-descriptions-item>-->
<!-- <el-descriptions-item label="税额(元)">{{ data.taxAmount }}</el-descriptions-item>-->
<el-descriptions-item label="支付方式">
<dict-tag :options="dict.type.payment_method" :value="data.paymentMethod"/>
</el-descriptions-item>
<el-descriptions-item label="银行账号">{{ data.payBankNumber }}</el-descriptions-item>
<el-descriptions-item label="账户名称">{{ data.payName }}</el-descriptions-item>
<el-descriptions-item label="银行开户行">{{ data.payBankOpenAddress }}</el-descriptions-item>
<el-descriptions-item label="银行行号">{{ data.bankNumber }}</el-descriptions-item>
<el-descriptions-item span="2"></el-descriptions-item>
<el-descriptions-item label="其它特别说明" :span="3">{{ data.remark }}</el-descriptions-item>
</el-descriptions>
<div class="section" style="margin-top: 20px;" >
<div class="el-descriptions__title">应付单信息</div>
<el-table :data="data.payableDetails" border style="width: 100%; margin-top: 10px;">
<el-table-column type="index" label="序号" width="50" align="center"></el-table-column>
<el-table-column label="采购单号" align="center">
<template slot-scope="scope">
<div class="purchase-no-cell">
<el-link
v-for="(purchaseItem, index) in getPurchaseNoList(scope.row.purchaseNo)"
:key="`${scope.$index}-${purchaseItem.purchaseNo}-${purchaseItem.purchaseId || ''}-${index}`"
type="primary"
:underline="false"
class="purchase-no-link"
@click="handleViewPruchaseDetail(scope.row, purchaseItem)"
>
{{ purchaseItem.purchaseNo }}
</el-link>
</div>
</template>
</el-table-column>
<el-table-column label="采购-应付单编号" align="center">
<template slot-scope="scope">
<el-button type="text" @click="handleViewPayable(scope.row)">{{ scope.row.payableBillCode }}</el-button>
</template>
</el-table-column>
<el-table-column label="项目名称" align="center">
<template slot-scope="scope">
<a @click="handleViewProject(scope.row)" class="link-type">{{ scope.row.projectName }}</a>
</template>
</el-table-column>
<el-table-column prop="productCode" label="产品编码" align="center"></el-table-column>
<el-table-column prop="model" label="产品型号" align="center"></el-table-column>
<el-table-column prop="productName" label="产品名称" align="center"></el-table-column>
</el-table>
</div>
<div class="section" style="margin-top: 20px;">
<div style="display: flex; justify-content: space-between; align-items: center;">
<div class="el-descriptions__title">附件信息</div>
<el-button type="success" size="mini" icon="el-icon-download" :loading="zipDownloading" @click="handleDownloadAllAttachments"></el-button>
</div>
<el-table :data="data.fileList" border style="width: 100%; margin-top: 10px;">
<el-table-column prop="fileName" label="附件名称" align="center"></el-table-column>
<el-table-column prop="createTime" label="上传时间" align="center"></el-table-column>
<el-table-column label="操作" align="center">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-view" @click="handlePreview(scope.row)"></el-button>
<el-button size="mini" type="text" icon="el-icon-download" @click="handleDownload(scope.row)"></el-button>
</template>
</el-table-column>
</el-table>
</div>
<el-dialog :visible.sync="pdfPreviewVisible" width="80%" append-to-body top="5vh" title="PDF预览">
<iframe :src="currentPdfUrl" width="100%" height="600px" frameborder="0"></iframe>
</el-dialog>
<el-dialog :visible.sync="imagePreviewVisible" width="60%" append-to-body top="5vh" title="图片预览">
<img :src="currentImageUrl" style="width: 100%;" />
</el-dialog>
<edit-form :visible.sync="payableVisible" :data="selectedPayableRow" :z-index="2000" @close="payableVisible = false" />
<project-detail-drawer :visible.sync="projectDrawerVisible" :project-id="currentProjectId" />
<el-drawer
title="采购单详情"
:visible.sync="showPruchaseDetailDrawer"
direction="rtl"
size="80%"
append-to-body
:modal-append-to-body="true"
:z-index="pruchaseDrawerZIndex"
>
<ApproveLayout title="采购单详情" v-if="showPruchaseDetailDrawer">
<purchase-order-detail-view
ref="pruchaseDetailView"
:order-data="pruchaseDetailOrderData"
@close="showPruchaseDetailDrawer = false"
>
</purchase-order-detail-view>
<template #footer>
<span>采购单编号: {{ pruchaseDetailOrderData ? pruchaseDetailOrderData.purchaseNo : '' }}</span>
<span v-if="pruchaseDetailOrderData"> | : {{ pruchaseDetailOrderData.version }}</span>
</template>
</ApproveLayout>
</el-drawer>
</div>
</template>
<script>
import request from '@/utils/request';
import JSZip from 'jszip';
import { saveAs } from 'file-saver';
import { getPurchaseorder } from "@/api/sip/purchaseorder";
import {formatCurrency} from "@/utils";
import EditForm from "@/views/finance/payable/components/EditForm.vue";
import ProjectDetailDrawer from "@/views/project/info/ProjectDetailDrawer.vue";
import ApproveLayout from "@/views/approve/ApproveLayout.vue";
import PurchaseOrderDetailView from "@/views/purchaseorder/components/PurchaseOrderDetailView.vue";
export default {
name: "PaymentDetail",
components: { EditForm, ProjectDetailDrawer, ApproveLayout, PurchaseOrderDetailView },
props: {
data: {
type: Object,
required: true,
default: () => ({})
}
},
dicts: ['payment_bill_type', 'product_type','payment_method'],
data() {
return {
attachments: [],
zipDownloading: false,
pdfPreviewVisible: false,
currentPdfUrl: '',
imagePreviewVisible: false,
currentImageUrl: '',
payableVisible: false,
selectedPayableRow: {},
projectDrawerVisible: false,
currentProjectId: null,
showPruchaseDetailDrawer: false,
pruchaseDetailOrderData: null,
pruchaseDrawerZIndex: 5000
};
},
watch: {
},
methods: {
formatCurrency,
isPdf(filePath) {
return filePath && filePath.toLowerCase().endsWith('.pdf');
},
getImageUrl(resource) {
return process.env.VUE_APP_BASE_API + "/common/download/resource?resource=" + resource;
},
handlePreview(row) {
if (this.isPdf(row.filePath)) {
// PDF Preview logic
request({
url: '/common/download/resource',
method: 'get',
params: { resource: row.filePath },
responseType: 'blob'
}).then(res => {
const blob = new Blob([res.data], { type: 'application/pdf' });
this.currentPdfUrl = URL.createObjectURL(blob);
this.pdfPreviewVisible = true;
});
} else {
// Image Preview
this.currentImageUrl = this.getImageUrl(row.filePath);
this.imagePreviewVisible = true;
}
},
handleDownload(row) {
const link = document.createElement('a');
link.href = this.getImageUrl(row.filePath);
link.download = row.fileName || 'attachment';
link.style.display = 'none';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
},
async handleDownloadAllAttachments() {
const files = Array.isArray(this.data.fileList) ? this.data.fileList : [];
if (files.length === 0) {
this.$modal.msgWarning('暂无可下载的附件');
return;
}
this.zipDownloading = true;
this.$modal.loading('正在打包附件,请稍候...');
try {
const zip = new JSZip();
for (const row of files) {
const blob = await request({
url: '/common/download/resource',
method: 'get',
params: { resource: row.filePath },
responseType: 'blob',
timeout: 0
}).then(res => res.data);
zip.file(row.fileName || 'file', blob);
}
const content = await zip.generateAsync({ type: 'blob' });
saveAs(content, (this.data.paymentBillCode || '付款申请单附件') + '.zip');
this.$modal.msgSuccess('下载成功');
} catch (error) {
console.error('附件打包下载失败', error);
this.$modal.msgError('附件下载失败,请稍后重试');
} finally {
this.zipDownloading = false;
this.$modal.closeLoading();
}
},
handleViewPayable(row) {
row.id = row.payableBillId;
this.selectedPayableRow = row;
this.payableVisible = true;
},
handleViewProject(row) {
this.currentProjectId = row.projectId;
this.projectDrawerVisible = true;
},
getPurchaseNoList(purchaseNo) {
return (purchaseNo || '')
.split(',')
.map(item => item.trim())
.filter(Boolean)
.map(item => {
const splitIndex = item.lastIndexOf('__');
if (splitIndex === -1) {
return { purchaseNo: item, purchaseId: null };
}
return {
purchaseNo: item.substring(0, splitIndex),
purchaseId: item.substring(splitIndex + 2)
};
});
},
handleViewPruchaseDetail(row, purchaseItem) {
this.pruchaseDrawerZIndex += 2;
const purchaseId = purchaseItem && purchaseItem.purchaseId ? purchaseItem.purchaseId : (row.purchaseId || row.purchaseOrderId);
if (purchaseId) {
getPurchaseorder(purchaseId).then(response => {
this.pruchaseDetailOrderData = response.data;
this.showPruchaseDetailDrawer = true;
});
return;
}
this.pruchaseDetailOrderData = {
purchaseNo: (purchaseItem && purchaseItem.purchaseNo) || row.purchaseNo || '',
version: row.version || ''
};
this.showPruchaseDetailDrawer = true;
},
}
};
</script>
<style scoped>
.payment-detail {
margin-bottom: 20px;
}
.purchase-no-cell {
word-break: break-all;
}
.purchase-no-link {
display: block;
}
</style>

View File

@ -1,314 +0,0 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
<el-form-item label="付款编号" prop="paymentNo">
<el-input v-model="queryParams.paymentNo" placeholder="请输入付款编号" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="制造商" prop="manufacturer">
<el-input v-model="queryParams.manufacturer" placeholder="请输入制造商" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="项目名称" prop="projectName">
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="提交日期">
<el-date-picker
v-model="dateRange"
style="width: 240px"
value-format="yyyy-MM-dd"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
size="mini"
@click="toApproved()"
>审批历史</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="paymentList">
<el-table-column label="序号" type="index" width="50" align="center" />
<el-table-column label="付款编号" align="center" prop="paymentBillCode" />
<el-table-column label="制造商" align="center" prop="vendorName" />
<!-- <el-table-column label="项目名称" align="center" prop="projectName" />-->
<el-table-column label="金额" align="center" prop="totalPriceWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)" />
<!-- <el-table-column label="汇智负责人" align="center" prop="hzUserName" />-->
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.applyTime) }}</span>
</template>
</el-table-column>
<el-table-column label="审批节点" align="center" prop="approveNode" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleApprove(scope.row)"></el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
<!-- 审批详情主对话框 -->
<el-dialog title="付款单审批" :visible.sync="detailDialogVisible" width="80%" append-to-body>
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
<el-button
type="primary"
size="small"
icon="el-icon-download"
@click="exportPDF"
:loading="pdfExporting"
>导出PDF</el-button>
</div>
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
<ApproveLayout ref="approveLayout" title="付款单详情">
<payment-detail :data="form"></payment-detail>
<template #footer>
<span>付款编号: {{ form.paymentBillCode }}</span>
</template>
</ApproveLayout>
</div>
<el-divider content-position="left">流转意见</el-divider>
<div class="process-container">
<el-timeline>
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
<el-card>
<h4>{{ log.approveOpinion }}</h4>
<p><b>操作人:</b> {{ log.approveUserName }} </p>
<p><b>审批状态:</b>
<el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
</el-card>
</el-timeline-item>
</el-timeline>
<div v-if="!approveLogs || approveLogs.length === 0"></div>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="openOpinionDialog('approve')"></el-button>
<el-button type="danger" @click="openOpinionDialog('reject')"></el-button>
<el-button @click="detailDialogVisible = false"> </el-button>
</span>
</el-dialog>
<!-- 审批意见对话框 -->
<el-dialog :title="confirmDialogTitle" :visible.sync="opinionDialogVisible" width="30%" append-to-body>
<el-form ref="opinionForm" :model="opinionForm" :rules="opinionRules" label-width="100px">
<el-form-item label="审批意见" prop="approveOpinion">
<el-input v-model="opinionForm.approveOpinion" type="textarea" :rows="4" placeholder="请输入审批意见"/>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="opinionDialogVisible = false"> </el-button>
<el-button type="primary" @click="showConfirmDialog()"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { listPaymentApprove, getPayment } from "@/api/finance/payment";
import { approveTask, listCompletedFlows } from "@/api/flow";
import PaymentDetail from "./components/PaymentDetail";
import ApproveLayout from "@/views/approve/ApproveLayout";
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
export default {
name: "PaymentApprove",
components: { PaymentDetail, ApproveLayout },
data() {
return {
loading: true,
showSearch: true,
total: 0,
paymentList: [],
queryParams: {
pageNum: 1,
pageSize: 10,
paymentNo: null,
manufacturer: null,
projectName: null,
processKey: 'finance_payment',
orderByColumn: 'applyTime',
isAsc: 'desc'
},
dateRange: [],
detailDialogVisible: false,
detailLoading: false,
form: {},
approveLogs: [],
opinionDialogVisible: false,
confirmDialogTitle: '',
currentApproveType: '',
opinionForm: {
approveOpinion: ''
},
opinionRules: {
approveOpinion: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }],
},
processKey: 'finance_payment',
taskId: null,
currentPaymentId: null,
pdfExporting: false
};
},
created() {
this.getList();
},
methods: {
getList() {
this.loading = true;
listPaymentApprove(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
this.paymentList = response.rows;
this.total = response.total;
this.loading = false;
});
},
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
resetQuery() {
this.dateRange = [];
this.resetForm("queryForm");
this.handleQuery();
},
toApproved() {
this.$router.push( '/approve/paymentLog' )
},
handleApprove(row) {
this.resetDetailForm();
this.currentPaymentId = row.id;
this.taskId = row.taskId;
this.detailLoading = true;
this.detailDialogVisible = true;
getPayment(this.currentPaymentId).then(response => {
this.form = response.data;
this.loadApproveHistory(this.form.paymentBillCode);
this.detailLoading = false;
}).catch(() => {
this.detailLoading = false;
});
},
resetDetailForm() {
this.form = {};
this.approveLogs = [];
this.opinionForm.approveOpinion = '';
},
loadApproveHistory(businessKey) {
if (businessKey) {
// Assuming processKeyList might be generic or specific, using generic fetch for now
// Usually need to know the specific process key. For payment, it might be 'payment_approval' etc.
// However, listCompletedFlows logic in reference used specific keys.
// If I don't know the key, maybe I can omit it or guess.
// The reference used `processKeyList: ['purchase_order_online']`.
// I will use a generic one or try to find it.
// If row.processKey is available I can use that.
let keys = [];
console.log(this.processKey)
if(this.processKey) keys.push(this.processKey);
listCompletedFlows({ businessKey: businessKey, processKeyList: keys }).then(response => {
this.approveLogs = response.data;
});
}
},
openOpinionDialog(type) {
this.currentApproveType = type;
this.confirmDialogTitle = type === 'approve' ? '同意审批' : '驳回审批';
this.opinionDialogVisible = true;
this.opinionForm.approveOpinion = '';
this.$nextTick(() => {
if(this.$refs.opinionForm) this.$refs.opinionForm.clearValidate();
});
},
showConfirmDialog() {
this.$refs.opinionForm.validate(valid => {
if (valid) {
this.opinionDialogVisible = false;
this.submitApproval();
}
});
},
submitApproval() {
const approveBtn = this.currentApproveType === 'approve' ? 1 : 0;
const params = {
businessKey: this.form.paymentBillCode,
processKey: this.processKey,
taskId: this.taskId,
variables: {
comment: this.opinionForm.approveOpinion,
approveBtn: approveBtn
}
};
approveTask(params).then(() => {
this.$modal.msgSuccess(this.confirmDialogTitle + "成功");
this.detailDialogVisible = false;
this.getList();
});
},
getStatusText(status) {
if (!status) {
return '提交审批'
}
// Map status codes to text
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
return map[status] || '提交审批';
},
async exportPDF() {
this.pdfExporting = true;
try {
const element = this.$refs.approveLayout.$el;
const fileName = `付款单-${this.form.paymentBillCode || ''}.pdf`;
await exportElementToPDF(element, fileName);
this.$modal.msgSuccess('PDF导出成功');
} catch (error) {
console.error('PDF导出失败:', error);
this.$modal.msgError('PDF导出失败请稍后重试');
} finally {
this.pdfExporting = false;
}
}
}
};
</script>
<style scoped>
.process-container {
padding: 10px;
}
/* 导出PDF时的特殊样式 */
.approve-container.exporting-pdf ::v-deep .el-button--primary,
.approve-container.exporting-pdf ::v-deep .el-button--text {
display: none;
}
.approve-container.exporting-pdf ::v-deep .el-input__inner,
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
border: none !important;
box-shadow: none !important;
background-color: transparent !important;
resize: none !important;
padding: 0 !important;
}
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
display: none;
}
</style>

View File

@ -1,219 +0,0 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
<el-form-item label="付款编号" prop="paymentNo">
<el-input v-model="queryParams.paymentNo" placeholder="请输入付款编号" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="制造商" prop="manufacturer">
<el-input v-model="queryParams.manufacturer" placeholder="请输入制造商" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="项目名称" prop="projectName">
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="提交日期">
<el-date-picker
v-model="dateRange"
style="width: 240px"
value-format="yyyy-MM-dd"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
<el-table v-loading="loading" :data="paymentList">
<el-table-column label="序号" type="index" width="50" align="center" />
<el-table-column label="付款编号" align="center" prop="paymentBillCode" />
<el-table-column label="制造商" align="center" prop="vendorName" />
<!-- <el-table-column label="项目名称" align="center" prop="projectName" />-->
<el-table-column label="金额" align="center" prop="totalPriceWithTax" >
<template slot-scope="scope">
<span style="color: red">{{ formatCurrency(scope.row.totalPriceWithTax) }}</span>
</template>
</el-table-column>
<!-- <el-table-column label="汇智负责人" align="center" prop="hzUserName" />-->
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.applyTime) }}</span>
</template>
</el-table-column>
<el-table-column label="审批节点" align="center" prop="approveNode" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row)"></el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
<!-- 详情对话框 -->
<el-dialog title="付款单详情" :visible.sync="detailDialogVisible" width="80%" append-to-body>
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
<el-button
type="primary"
size="small"
icon="el-icon-download"
@click="exportPDF"
:loading="pdfExporting"
>导出PDF</el-button>
</div>
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
<ApproveLayout ref="approveLayout" title="付款单详情">
<payment-detail :data="form"></payment-detail>
<template #footer>
<span> {{ form.paymentBillCode }}</span>
</template>
</ApproveLayout>
</div>
<el-divider content-position="left">流转意见</el-divider>
<div class="process-container">
<el-timeline>
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
<el-card>
<h4>{{ log.approveOpinion }}</h4>
<p><b>操作人:</b> {{ log.approveUserName }} </p>
<p><b>审批状态:</b> <el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
</el-card>
</el-timeline-item>
</el-timeline>
<div v-if="!approveLogs || approveLogs.length === 0"></div>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="detailDialogVisible = false"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { listPaymentApproved, getPayment } from "@/api/finance/payment";
import { listCompletedFlows } from "@/api/flow";
import PaymentDetail from "../components/PaymentRefundDetail.vue"; // Relative path adjustment
import ApproveLayout from "@/views/approve/ApproveLayout";
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
export default {
name: "PaymentApproved",
components: { PaymentDetail, ApproveLayout },
data() {
return {
loading: true,
showSearch: true,
total: 0,
paymentList: [],
queryParams: {
pageNum: 1,
pageSize: 10,
paymentNo: null,
manufacturer: null,
projectName: null,
processKey: 'finance_refund',
orderByColumn: 'applyTime',
isAsc: 'desc'
},
dateRange: [],
detailDialogVisible: false,
detailLoading: false,
form: {},
approveLogs: [],
currentPaymentId: null,
pdfExporting: false
};
},
created() {
this.getList();
},
methods: {
getList() {
this.loading = true;
listPaymentApproved(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
this.paymentList = response.rows;
this.total = response.total;
this.loading = false;
});
},
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
resetQuery() {
this.dateRange = [];
this.resetForm("queryForm");
this.handleQuery();
},
handleView(row) {
this.form = {};
this.approveLogs = [];
this.currentPaymentId = row.id;
this.detailLoading = true;
this.detailDialogVisible = true;
getPayment(this.currentPaymentId).then(response => {
this.form = response.data;
this.loadApproveHistory(this.form.paymentBillCode);
this.detailLoading = false;
}).catch(() => {
this.detailLoading = false;
});
},
loadApproveHistory(businessKey) {
if (businessKey) {
listCompletedFlows({ businessKey: businessKey }).then(response => {
this.approveLogs = response.data;
});
}
},
getStatusText(status) {
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
return map[status] || '提交审批';
},
async exportPDF() {
this.pdfExporting = true;
try {
const element = this.$refs.approveLayout.$el;
const fileName = `付款单-${this.form.paymentBillCode || ''}.pdf`;
await exportElementToPDF(element, fileName);
this.$modal.msgSuccess('PDF导出成功');
} catch (error) {
console.error('PDF导出失败:', error);
this.$modal.msgError('PDF导出失败请稍后重试');
} finally {
this.pdfExporting = false;
}
}
}
};
</script>
<style scoped>
.process-container {
padding: 10px;
}
/* 导出PDF时的特殊样式 */
.approve-container.exporting-pdf ::v-deep .el-button--primary,
.approve-container.exporting-pdf ::v-deep .el-button--text {
display: none;
}
.approve-container.exporting-pdf ::v-deep .el-input__inner,
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
border: none !important;
box-shadow: none !important;
background-color: transparent !important;
resize: none !important;
padding: 0 !important;
}
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
display: none;
}
</style>

View File

@ -1,67 +0,0 @@
<template>
<div class="payment-refund-detail">
<div style="text-align: center;font-weight:bold;font-size: 25px;">退款申请单</div>
<el-descriptions title="付款单信息" :column="3" border>
<el-descriptions-item label="采购付款单编号">{{ data.paymentBillCode }}</el-descriptions-item>
<el-descriptions-item label="制造商名称">{{ data.vendorName }}</el-descriptions-item>
<el-descriptions-item label="付款条件">
{{data.payType==='0'?'入库付款':'出库付款'}}
</el-descriptions-item>
<el-descriptions-item label="付款周期">
{{data.payConfigDay}}
</el-descriptions-item>
<el-descriptions-item label="含税总价(元)"><span style="color: red">{{ formatCurrency(data.totalPriceWithTax) }}</span></el-descriptions-item>
<!-- <el-descriptions-item label="未税总价(元)"><span style="color: red">{{ data.totalPriceWithoutTax }}</span></el-descriptions-item>-->
<!-- <el-descriptions-item label="税额(元)"><span style="color: red">{{ data.taxAmount }}</span></el-descriptions-item>-->
<el-descriptions-item label="支付方式">
<dict-tag :options="dict.type.payment_method" :value="data.paymentMethod"/>
</el-descriptions-item>
<el-descriptions-item label="银行账号">{{ data.payBankNumber }}</el-descriptions-item>
<el-descriptions-item label="账户名称">{{ data.payName }}</el-descriptions-item>
<el-descriptions-item label="银行开户行">{{ data.payBankOpenAddress }}</el-descriptions-item>
<el-descriptions-item label="银行行号" span="1">{{ data.bankNumber }}</el-descriptions-item>
<el-descriptions-item span="2"></el-descriptions-item>
<el-descriptions-item label="其它特别说明" span="3">{{ data.remark }}</el-descriptions-item>
</el-descriptions>
<div class="section" style="margin-top: 20px;" >
<div class="el-descriptions__title">应付单信息</div>
<el-table :data="data.payableDetails" border style="width: 100%; margin-top: 10px;">
<el-table-column type="index" label="序号" width="50" align="center"></el-table-column>
<el-table-column prop="payableBillCode" label="采购应付单编号" align="center"></el-table-column>
<el-table-column prop="projectName" label="项目名称" align="center"></el-table-column>
<el-table-column prop="productType" label="产品类型" align="center">
<template slot-scope="scope">
<dict-tag :options="dict.type.product_type" :value="scope.row.productType"/>
</template>
</el-table-column>
<!-- Note: Product Type is requested but not present in reference DetailDrawer.vue. Omitting for safety or need to ask. -->
<el-table-column prop="totalPriceWithTax" label="含税总价" align="center"></el-table-column>
<el-table-column prop="paymentAmount" label="本次付款金额" align="center"></el-table-column>
</el-table>
</div>
</div>
</template>
<script>
import {formatCurrency} from "@/utils";
export default {
name: "PaymentRefundDetail",
methods: {formatCurrency},
props: {
data: {
type: Object,
required: true,
default: () => ({})
}
},
dicts: ['payment_bill_type', 'payment_method','product_type']
};
</script>
<style scoped>
.payment-refund-detail {
margin-bottom: 20px;
}
</style>

View File

@ -1,315 +0,0 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
<el-form-item label="付款编号" prop="paymentNo">
<el-input v-model="queryParams.paymentNo" placeholder="请输入付款编号" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="制造商" prop="manufacturer">
<el-input v-model="queryParams.manufacturer" placeholder="请输入制造商" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="项目名称" prop="projectName">
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="提交日期">
<el-date-picker
v-model="dateRange"
style="width: 240px"
value-format="yyyy-MM-dd"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
size="mini"
@click="toApproved()"
>审批历史</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="paymentList">
<el-table-column label="序号" type="index" width="50" align="center" />
<el-table-column label="付款编号" align="center" prop="paymentBillCode" />
<el-table-column label="制造商" align="center" prop="vendorName" />
<!-- <el-table-column label="项目名称" align="center" prop="projectName" />-->
<el-table-column label="金额" align="center" prop="totalPriceWithTax" >
<template slot-scope="scope">
<span style="color: red">{{ formatCurrency(scope.row.totalPriceWithTax) }}</span>
</template>
</el-table-column>
<!-- <el-table-column label="汇智负责人" align="center" prop="hzUserName" />-->
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.applyTime) }}</span>
</template>
</el-table-column>
<el-table-column label="审批节点" align="center" prop="approveNode" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleApprove(scope.row)"></el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
<!-- 审批详情主对话框 -->
<el-dialog title="付款单审批" :visible.sync="detailDialogVisible" width="80%" append-to-body>
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
<el-button
type="primary"
size="small"
icon="el-icon-download"
@click="exportPDF"
:loading="pdfExporting"
>导出PDF</el-button>
</div>
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
<ApproveLayout ref="approveLayout" title="付款单详情">
<payment-detail :data="form"></payment-detail>
<template #footer>
<span>付款编号: {{ form.paymentBillCode }}</span>
</template>
</ApproveLayout>
</div>
<el-divider content-position="left">流转意见</el-divider>
<div class="process-container">
<el-timeline>
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
<el-card>
<h4>{{ log.approveOpinion }}</h4>
<p><b>操作人:</b> {{ log.approveUserName }} </p>
<p><b>审批状态:</b> <el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
</el-card>
</el-timeline-item>
</el-timeline>
<div v-if="!approveLogs || approveLogs.length === 0"></div>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="openOpinionDialog('approve')"></el-button>
<el-button type="danger" @click="openOpinionDialog('reject')"></el-button>
<el-button @click="detailDialogVisible = false"> </el-button>
</span>
</el-dialog>
<!-- 审批意见对话框 -->
<el-dialog :title="confirmDialogTitle" :visible.sync="opinionDialogVisible" width="30%" append-to-body>
<el-form ref="opinionForm" :model="opinionForm" :rules="opinionRules" label-width="100px">
<el-form-item label="审批意见" prop="approveOpinion">
<el-input v-model="opinionForm.approveOpinion" type="textarea" :rows="4" placeholder="请输入审批意见"/>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="opinionDialogVisible = false"> </el-button>
<el-button type="primary" @click="showConfirmDialog()"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { listPaymentApprove, getPayment } from "@/api/finance/payment";
import { approveTask, listCompletedFlows } from "@/api/flow";
import PaymentDetail from "./components/PaymentRefundDetail.vue";
import ApproveLayout from "@/views/approve/ApproveLayout";
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
export default {
name: "PaymentApprove",
components: { PaymentDetail, ApproveLayout },
data() {
return {
loading: true,
showSearch: true,
total: 0,
paymentList: [],
queryParams: {
pageNum: 1,
pageSize: 10,
paymentNo: null,
manufacturer: null,
projectName: null,
processKey: 'finance_refund',
orderByColumn: 'applyTime',
isAsc: 'desc'
},
dateRange: [],
detailDialogVisible: false,
detailLoading: false,
form: {},
approveLogs: [],
opinionDialogVisible: false,
confirmDialogTitle: '',
currentApproveType: '',
opinionForm: {
approveOpinion: ''
},
opinionRules: {
approveOpinion: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }],
},
processKey: 'finance_refund',
taskId: null,
currentPaymentId: null,
pdfExporting: false
};
},
created() {
this.getList();
},
methods: {
getList() {
this.loading = true;
listPaymentApprove(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
this.paymentList = response.rows;
this.total = response.total;
this.loading = false;
});
},
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
resetQuery() {
this.dateRange = [];
this.resetForm("queryForm");
this.handleQuery();
},
toApproved() {
this.$router.push( '/approve/paymentRedLog' )
},
handleApprove(row) {
this.resetDetailForm();
this.currentPaymentId = row.id;
this.taskId = row.taskId;
this.detailLoading = true;
this.detailDialogVisible = true;
getPayment(this.currentPaymentId).then(response => {
this.form = response.data;
this.loadApproveHistory(this.form.paymentBillCode);
this.detailLoading = false;
}).catch(() => {
this.detailLoading = false;
});
},
resetDetailForm() {
this.form = {};
this.approveLogs = [];
this.opinionForm.approveOpinion = '';
},
loadApproveHistory(businessKey) {
if (businessKey) {
// Assuming processKeyList might be generic or specific, using generic fetch for now
// Usually need to know the specific process key. For payment, it might be 'payment_approval' etc.
// However, listCompletedFlows logic in reference used specific keys.
// If I don't know the key, maybe I can omit it or guess.
// The reference used `processKeyList: ['purchase_order_online']`.
// I will use a generic one or try to find it.
// If row.processKey is available I can use that.
let keys = [];
console.log(this.processKey)
if(this.processKey) keys.push(this.processKey);
listCompletedFlows({ businessKey: businessKey, processKeyList: keys }).then(response => {
this.approveLogs = response.data;
});
}
},
openOpinionDialog(type) {
this.currentApproveType = type;
this.confirmDialogTitle = type === 'approve' ? '同意审批' : '驳回审批';
this.opinionDialogVisible = true;
this.opinionForm.approveOpinion = '';
this.$nextTick(() => {
if(this.$refs.opinionForm) this.$refs.opinionForm.clearValidate();
});
},
showConfirmDialog() {
this.$refs.opinionForm.validate(valid => {
if (valid) {
this.opinionDialogVisible = false;
this.submitApproval();
}
});
},
submitApproval() {
const approveBtn = this.currentApproveType === 'approve' ? 1 : 0;
const params = {
businessKey: this.form.paymentBillCode,
processKey: this.processKey,
taskId: this.taskId,
variables: {
comment: this.opinionForm.approveOpinion,
approveBtn: approveBtn
}
};
approveTask(params).then(() => {
this.$modal.msgSuccess(this.confirmDialogTitle + "成功");
this.detailDialogVisible = false;
this.getList();
});
},
getStatusText(status) {
if (!status) {
return '提交审批'
}
// Map status codes to text
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
return map[status] || '提交审批';
},
async exportPDF() {
this.pdfExporting = true;
try {
const element = this.$refs.approveLayout.$el;
const fileName = `付款单-${this.form.paymentBillCode || ''}.pdf`;
await exportElementToPDF(element, fileName);
this.$modal.msgSuccess('PDF导出成功');
} catch (error) {
console.error('PDF导出失败:', error);
this.$modal.msgError('PDF导出失败请稍后重试');
} finally {
this.pdfExporting = false;
}
}
}
};
</script>
<style scoped>
.process-container {
padding: 10px;
}
/* 导出PDF时的特殊样式 */
.approve-container.exporting-pdf ::v-deep .el-button--primary,
.approve-container.exporting-pdf ::v-deep .el-button--text {
display: none;
}
.approve-container.exporting-pdf ::v-deep .el-input__inner,
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
border: none !important;
box-shadow: none !important;
background-color: transparent !important;
resize: none !important;
padding: 0 !important;
}
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
display: none;
}
</style>

View File

@ -1,63 +0,0 @@
import html2canvas from 'html2canvas';
import jsPDF from 'jspdf';
/**
* 导出指定DOM元素为PDF
* @param {HTMLElement} element - 要导出的DOM元素
* @param {string} fileName - 导出的文件名
* @returns {Promise<void>}
*/
export async function exportElementToPDF(element, fileName) {
const disabledElements = [];
try {
// 移除所有输入框的 disabled 属性以便在PDF中正确显示
element.querySelectorAll('input:disabled, textarea:disabled').forEach(el => {
disabledElements.push(el);
el.disabled = false;
});
// 使用html2canvas捕获内容
const canvas = await html2canvas(element, {
scale: 2, // 提高清晰度
useCORS: true, // 允许跨域图片
logging: false, // 关闭日志
backgroundColor: '#F8F5F0' // 设置背景色
});
// 计算PDF页面尺寸
const imgWidth = 210; // A4纸宽度mm
const pageHeight = 297; // A4纸高度mm
const imgHeight = (canvas.height * imgWidth) / canvas.width;
let heightLeft = imgHeight;
// 创建PDF
const pdf = new jsPDF('p', 'mm', 'a4');
let position = 0;
// 将canvas转换为图片
const imgData = canvas.toDataURL('image/jpeg');
// 添加第一页
pdf.addImage(imgData, 'JPEG', 0, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
// 如果内容超过一页,添加更多页
while (heightLeft > 0) {
position = heightLeft - imgHeight;
pdf.addPage();
pdf.addImage(imgData, 'JPEG', 0, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
}
// 保存PDF
pdf.save(fileName);
} catch (error) {
console.error('PDF导出失败:', error);
throw error;
} finally {
// 恢复之前移除的 disabled 属性
disabledElements.forEach(el => {
el.disabled = true;
});
}
}

View File

@ -1,215 +0,0 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
<el-form-item label="收款编号" prop="receiptBillCode">
<el-input v-model="queryParams.receiptBillCode" placeholder="请输入收款编号" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="进货商" prop="partnerName">
<el-input v-model="queryParams.partnerName" placeholder="请输入进货商" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="项目名称" prop="projectName">
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="提交日期">
<el-date-picker
v-model="dateRange"
style="width: 240px"
value-format="yyyy-MM-dd"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
<el-table v-loading="loading" :data="receiptList">
<el-table-column label="序号" type="index" width="50" align="center" />
<el-table-column label="收款编号" align="center" prop="receiptBillCode" />
<el-table-column label="客户" align="center" prop="partnerName" />
<!-- <el-table-column label="项目名称" align="center" prop="projectName" />-->
<el-table-column label="金额" align="center" prop="totalPriceWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
<!-- <el-table-column label="汇智负责人" align="center" prop="hzUserName" />-->
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.applyTime) }}</span>
</template>
</el-table-column>
<el-table-column label="审批节点" align="center" prop="approveNode" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row)"></el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
<!-- 详情对话框 -->
<el-dialog title="收款单详情" :visible.sync="detailDialogVisible" width="80%" append-to-body>
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
<el-button
type="primary"
size="small"
icon="el-icon-download"
@click="exportPDF"
:loading="pdfExporting"
>导出PDF</el-button>
</div>
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
<ApproveLayout ref="approveLayout" title="收款单详情">
<receipt-detail :data="form"></receipt-detail>
<template #footer>
<span>{{ form.receiptBillCode }}</span>
</template>
</ApproveLayout>
</div>
<el-divider content-position="left">流转意见</el-divider>
<div class="process-container">
<el-timeline>
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
<el-card>
<h4>{{ log.approveOpinion }}</h4>
<p><b>操作人:</b> {{ log.approveUserName }} </p>
<p><b>审批状态:</b> <el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
</el-card>
</el-timeline-item>
</el-timeline>
<div v-if="!approveLogs || approveLogs.length === 0"></div>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="detailDialogVisible = false"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { listReceiptApproved, getReceiptDetail } from "@/api/finance/receipt";
import { listCompletedFlows } from "@/api/flow";
import ReceiptDetail from "../components/ReceiptDetail";
import ApproveLayout from "@/views/approve/ApproveLayout";
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
export default {
name: "ReceiptApproved",
components: { ReceiptDetail, ApproveLayout },
data() {
return {
loading: true,
showSearch: true,
total: 0,
receiptList: [],
queryParams: {
pageNum: 1,
pageSize: 10,
receiptBillCode: null,
partnerName: null,
projectName: null,
processKey: 'finance_receipt_approve',
orderByColumn: 'applyTime',
isAsc: 'desc'
},
dateRange: [],
detailDialogVisible: false,
detailLoading: false,
form: {},
approveLogs: [],
currentReceiptId: null,
pdfExporting: false
};
},
created() {
this.getList();
},
methods: {
getList() {
this.loading = true;
listReceiptApproved(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
this.receiptList = response.rows;
this.total = response.total;
this.loading = false;
});
},
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
resetQuery() {
this.dateRange = [];
this.resetForm("queryForm");
this.handleQuery();
},
handleView(row) {
this.form = {};
this.approveLogs = [];
this.currentReceiptId = row.id;
this.detailLoading = true;
this.detailDialogVisible = true;
getReceiptDetail(this.currentReceiptId).then(response => {
this.form = response.data;
this.loadApproveHistory(this.form.receiptBillCode);
this.detailLoading = false;
}).catch(() => {
this.detailLoading = false;
});
},
loadApproveHistory(businessKey) {
if (businessKey) {
listCompletedFlows({ businessKey: businessKey }).then(response => {
this.approveLogs = response.data;
});
}
},
getStatusText(status) {
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
return map[status] || '提交审批';
},
async exportPDF() {
this.pdfExporting = true;
try {
const element = this.$refs.approveLayout.$el;
const fileName = `收款单-${this.form.receiptBillCode || ''}.pdf`;
await exportElementToPDF(element, fileName);
this.$modal.msgSuccess('PDF导出成功');
} catch (error) {
console.error('PDF导出失败:', error);
this.$modal.msgError('PDF导出失败请稍后重试');
} finally {
this.pdfExporting = false;
}
}
}
};
</script>
<style scoped>
.process-container {
padding: 10px;
}
/* 导出PDF时的特殊样式 */
.approve-container.exporting-pdf ::v-deep .el-button--primary,
.approve-container.exporting-pdf ::v-deep .el-button--text {
display: none;
}
.approve-container.exporting-pdf ::v-deep .el-input__inner,
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
border: none !important;
box-shadow: none !important;
background-color: transparent !important;
resize: none !important;
padding: 0 !important;
}
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
display: none;
}
</style>

View File

@ -1,147 +0,0 @@
<template>
<div class="receipt-detail">
<div style="text-align: center;font-weight:bold;font-size: 25px;">收款申请单</div>
<el-descriptions title="收款单信息" :column="3" border>
<el-descriptions-item label="销售-收款单编号">{{ data.receiptBillCode }}</el-descriptions-item>
<el-descriptions-item :span="2" label="进货商名称">{{ data.partnerName }}</el-descriptions-item>
<!-- <el-descriptions-item label="收款单类型">-->
<!-- <dict-tag :options="dict.type.receipt_bill_type" :value="data.receiptBillType"/>-->
<!-- </el-descriptions-item>-->
<el-descriptions-item label="含税总价(元)">{{ formatCurrency(data.totalPriceWithTax) }}</el-descriptions-item>
<el-descriptions-item label="未税总价(元)">{{ formatCurrency(data.totalPriceWithoutTax) }}</el-descriptions-item>
<el-descriptions-item label="税额(元)">{{ formatCurrency(data.taxAmount) }}</el-descriptions-item>
<el-descriptions-item label="支付方式">
<dict-tag :options="dict.type.payment_method" :value="data.receiptMethod"/>
</el-descriptions-item>
<el-descriptions-item label="银行账号">{{ data.receiptBankNumber }}</el-descriptions-item>
<el-descriptions-item label="账户名称">{{ data.receiptAccountName }}</el-descriptions-item>
<el-descriptions-item label="银行开户行">{{ data.receiptBankOpenAddress }}</el-descriptions-item>
<el-descriptions-item label="银行行号">{{ data.bankNumber }}</el-descriptions-item>
<!-- <el-descriptions-item label="备注" :span="3">{{ data.remark }}</el-descriptions-item>-->
</el-descriptions>
<div class="section" style="margin-top: 20px;" v-show="data.detailDTOList && data.detailDTOList.length>0">
<div class="el-descriptions__title">应收单列表</div>
<el-table :data="data.detailDTOList" border style="width: 100%; margin-top: 10px;">
<el-table-column type="index" label="序号" width="50" align="center"></el-table-column>
<el-table-column prop="receivableBillCode" label="销售-应收单编号" align="center"></el-table-column>
<el-table-column prop="projectName" label="项目名称" align="center"></el-table-column>
<el-table-column prop="productType" label="产品类型" align="center">
<template slot-scope="scope">
<dict-tag :options="dict.type.product_type" :value="scope.row.productType"/>
</template>
</el-table-column>
<el-table-column prop="totalPriceWithTax" label="含税总价" align="center" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"></el-table-column>
<el-table-column prop="receiptAmount" label="本次收款金额" align="center" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"></el-table-column>
</el-table>
</div>
<div class="section" style="margin-top: 20px;" v-if="attachments && attachments.length > 0">
<div class="el-descriptions__title">附件信息</div>
<el-table :data="attachments" border style="width: 100%; margin-top: 10px;">
<el-table-column prop="fileName" label="附件名称" align="center"></el-table-column>
<el-table-column prop="createByName" label="上传人" align="center"></el-table-column>
<el-table-column prop="createTime" label="上传时间" align="center"></el-table-column>
<el-table-column label="操作" align="center">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-view" @click="handlePreview(scope.row)"></el-button>
<el-button size="mini" type="text" icon="el-icon-download" @click="handleDownload(scope.row)"></el-button>
</template>
</el-table-column>
</el-table>
</div>
<el-dialog :visible.sync="pdfPreviewVisible" width="80%" append-to-body top="5vh" title="PDF预览">
<iframe :src="currentPdfUrl" width="100%" height="600px" frameborder="0"></iframe>
</el-dialog>
<el-dialog :visible.sync="imagePreviewVisible" width="60%" append-to-body top="5vh" title="图片预览">
<img :src="currentImageUrl" style="width: 100%;" />
</el-dialog>
</div>
</template>
<script>
import request from '@/utils/request';
import {getInvoiceAttachments} from "@/api/finance/invoice";
import {getReceiptAttachments} from "@/api/finance/receipt";
export default {
name: "ReceiptDetail",
props: {
data: {
type: Object,
required: true,
default: () => ({})
}
},
dicts: ['receipt_bill_type', 'product_type','payment_method'],
data() {
return {
attachments: [],
pdfPreviewVisible: false,
currentPdfUrl: '',
imagePreviewVisible: false,
currentImageUrl: ''
};
},
watch: {
'data.id': {
handler(val) {
if (val) {
this.fetchAttachments(val);
}
},
immediate: true
}
},
methods: {
fetchAttachments(id) {
getReceiptAttachments(id,{type: "receipt"}).then(response => {
this.attachments = (response.data || []).filter(item => item.delFlag !== '2');
});
},
isPdf(filePath) {
return filePath && filePath.toLowerCase().endsWith('.pdf');
},
getImageUrl(resource) {
return process.env.VUE_APP_BASE_API + "/common/download/resource?resource=" + resource;
},
handlePreview(row) {
if (this.isPdf(row.filePath)) {
// PDF Preview logic
request({
url: '/common/download/resource',
method: 'get',
params: { resource: row.filePath },
responseType: 'blob'
}).then(res => {
const blob = new Blob([res.data], { type: 'application/pdf' });
this.currentPdfUrl = URL.createObjectURL(blob);
this.pdfPreviewVisible = true;
});
} else {
// Image Preview
this.currentImageUrl = this.getImageUrl(row.filePath);
this.imagePreviewVisible = true;
}
},
handleDownload(row) {
const link = document.createElement('a');
link.href = this.getImageUrl(row.filePath);
link.download = row.fileName || 'attachment';
link.style.display = 'none';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
}
};
</script>
<style scoped>
.receipt-detail {
margin-bottom: 20px;
}
</style>

View File

@ -1,302 +0,0 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
<el-form-item label="收款编号" prop="receiptBillCode">
<el-input v-model="queryParams.receiptBillCode" placeholder="请输入收款编号" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="进货商" prop="partnerName">
<el-input v-model="queryParams.partnerName" placeholder="请输入进货商名称" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="项目名称" prop="projectName">
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="提交日期">
<el-date-picker
v-model="dateRange"
style="width: 240px"
value-format="yyyy-MM-dd HH:mm:ss"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
size="mini"
@click="toApproved()"
>审批历史</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="receiptList">
<el-table-column label="序号" type="index" width="50" align="center" />
<el-table-column label="收款编号" align="center" prop="receiptBillCode" />
<el-table-column label="进货商" align="center" prop="partnerName" />
<!-- <el-table-column label="项目名称" align="center" prop="projectName" />-->
<el-table-column label="金额" align="center" prop="totalPriceWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
<!-- <el-table-column label="汇智负责人" align="center" prop="hzUserName" />-->
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.applyTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="审批节点" align="center" prop="approveNode" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleApprove(scope.row)"></el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
<!-- 审批详情主对话框 -->
<el-dialog title="收款单审批" :visible.sync="detailDialogVisible" width="80%" append-to-body>
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
<el-button
type="primary"
size="small"
icon="el-icon-download"
@click="exportPDF"
:loading="pdfExporting"
>导出PDF</el-button>
</div>
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
<ApproveLayout ref="approveLayout" title="收款单详情">
<receipt-detail :data="form"></receipt-detail>
<template #footer>
<span>{{ form.receiptBillCode }}</span>
</template>
</ApproveLayout>
</div>
<el-divider content-position="left">流转意见</el-divider>
<div class="process-container">
<el-timeline>
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
<el-card>
<h4>{{ log.approveOpinion }}</h4>
<p><b>操作人:</b> {{ log.approveUserName }} </p>
<p><b>审批状态:</b> <el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
</el-card>
</el-timeline-item>
</el-timeline>
<div v-if="!approveLogs || approveLogs.length === 0"></div>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="openOpinionDialog('approve')"></el-button>
<el-button type="danger" @click="openOpinionDialog('reject')"></el-button>
<el-button @click="detailDialogVisible = false"> </el-button>
</span>
</el-dialog>
<!-- 审批意见对话框 -->
<el-dialog :title="confirmDialogTitle" :visible.sync="opinionDialogVisible" width="30%" append-to-body>
<el-form ref="opinionForm" :model="opinionForm" :rules="opinionRules" label-width="100px">
<el-form-item label="审批意见" prop="approveOpinion">
<el-input v-model="opinionForm.approveOpinion" type="textarea" :rows="4" placeholder="请输入审批意见"/>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="opinionDialogVisible = false"> </el-button>
<el-button type="primary" @click="showConfirmDialog()"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { listReceiptApprove, getReceiptDetail } from "@/api/finance/receipt";
import { approveTask, listCompletedFlows } from "@/api/flow";
import ReceiptDetail from "./components/ReceiptDetail";
import ApproveLayout from "@/views/approve/ApproveLayout";
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
export default {
name: "ReceiptApprove",
components: { ReceiptDetail, ApproveLayout },
data() {
return {
loading: true,
showSearch: true,
total: 0,
receiptList: [],
queryParams: {
pageNum: 1,
pageSize: 10,
receiptBillCode: null,
partnerName: null,
projectName: null,
processKey: 'finance_receipt_approve',
orderByColumn: 'applyTime',
isAsc: 'desc'
},
dateRange: [],
detailDialogVisible: false,
detailLoading: false,
form: {},
approveLogs: [],
opinionDialogVisible: false,
confirmDialogTitle: '',
currentApproveType: '',
opinionForm: {
approveOpinion: ''
},
opinionRules: {
approveOpinion: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }],
},
processKey: 'finance_receipt_approve',
taskId: null,
currentReceiptId: null,
pdfExporting: false
};
},
created() {
this.getList();
},
methods: {
getList() {
this.loading = true;
listReceiptApprove(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
this.receiptList = response.rows;
this.total = response.total;
this.loading = false;
});
},
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
resetQuery() {
this.dateRange = [];
this.resetForm("queryForm");
this.handleQuery();
},
toApproved() {
this.$router.push( '/approve/receiptLog' )
},
handleApprove(row) {
this.resetDetailForm();
this.currentReceiptId = row.id;
this.taskId = row.taskId;
this.detailLoading = true;
this.detailDialogVisible = true;
getReceiptDetail(this.currentReceiptId).then(response => {
this.form = response.data;
this.loadApproveHistory(this.form.receiptBillCode);
this.detailLoading = false;
}).catch(() => {
this.detailLoading = false;
});
},
resetDetailForm() {
this.form = {};
this.approveLogs = [];
this.opinionForm.approveOpinion = '';
},
loadApproveHistory(businessKey) {
if (businessKey) {
let keys = [];
if(this.processKey) keys.push(this.processKey);
listCompletedFlows({ businessKey: businessKey, processKeyList: keys }).then(response => {
this.approveLogs = response.data;
});
}
},
openOpinionDialog(type) {
this.currentApproveType = type;
this.confirmDialogTitle = type === 'approve' ? '同意审批' : '驳回审批';
this.opinionDialogVisible = true;
this.opinionForm.approveOpinion = '';
this.$nextTick(() => {
if(this.$refs.opinionForm) this.$refs.opinionForm.clearValidate();
});
},
showConfirmDialog() {
this.$refs.opinionForm.validate(valid => {
if (valid) {
this.opinionDialogVisible = false;
this.submitApproval();
}
});
},
submitApproval() {
const approveBtn = this.currentApproveType === 'approve' ? 1 : 0;
const params = {
businessKey: this.form.receiptBillCode,
processKey: this.processKey,
taskId: this.taskId,
variables: {
comment: this.opinionForm.approveOpinion,
approveBtn: approveBtn
}
};
approveTask(params).then(() => {
this.$modal.msgSuccess(this.confirmDialogTitle + "成功");
this.detailDialogVisible = false;
this.getList();
});
},
getStatusText(status) {
if (!status) {
return '提交审批'
}
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
return map[status] || '提交审批';
},
async exportPDF() {
this.pdfExporting = true;
try {
const element = this.$refs.approveLayout.$el;
const fileName = `收款单-${this.form.receiptBillCode || ''}.pdf`;
await exportElementToPDF(element, fileName);
this.$modal.msgSuccess('PDF导出成功');
} catch (error) {
console.error('PDF导出失败:', error);
this.$modal.msgError('PDF导出失败请稍后重试');
} finally {
this.pdfExporting = false;
}
}
}
};
</script>
<style scoped>
.process-container {
padding: 10px;
}
/* 导出PDF时的特殊样式 */
.approve-container.exporting-pdf ::v-deep .el-button--primary,
.approve-container.exporting-pdf ::v-deep .el-button--text {
display: none;
}
.approve-container.exporting-pdf ::v-deep .el-input__inner,
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
border: none !important;
box-shadow: none !important;
background-color: transparent !important;
resize: none !important;
padding: 0 !important;
}
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
display: none;
}
</style>

View File

@ -1,215 +0,0 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
<el-form-item label="收款编号" prop="receiptBillCode">
<el-input v-model="queryParams.receiptBillCode" placeholder="请输入收款编号" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="进货商" prop="partnerName">
<el-input v-model="queryParams.partnerName" placeholder="请输入进货商" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="项目名称" prop="projectName">
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="提交日期">
<el-date-picker
v-model="dateRange"
style="width: 240px"
value-format="yyyy-MM-dd"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
<el-table v-loading="loading" :data="receiptList">
<el-table-column label="序号" type="index" width="50" align="center" />
<el-table-column label="收款编号" align="center" prop="receiptBillCode" />
<el-table-column label="客户" align="center" prop="partnerName" />
<!-- <el-table-column label="项目名称" align="center" prop="projectName" />-->
<el-table-column label="金额" align="center" prop="totalPriceWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)" />
<!-- <el-table-column label="汇智负责人" align="center" prop="hzUserName" />-->
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.applyTime) }}</span>
</template>
</el-table-column>
<el-table-column label="审批节点" align="center" prop="approveNode" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row)"></el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
<!-- 详情对话框 -->
<el-dialog title="收款单详情" :visible.sync="detailDialogVisible" width="80%" append-to-body>
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
<el-button
type="primary"
size="small"
icon="el-icon-download"
@click="exportPDF"
:loading="pdfExporting"
>导出PDF</el-button>
</div>
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
<ApproveLayout ref="approveLayout" title="收款单详情">
<receipt-detail :data="form"></receipt-detail>
<template #footer>
<span> {{ form.receiptBillCode }}</span>
</template>
</ApproveLayout>
</div>
<el-divider content-position="left">流转意见</el-divider>
<div class="process-container">
<el-timeline>
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
<el-card>
<h4>{{ log.approveOpinion }}</h4>
<p><b>操作人:</b> {{ log.approveUserName }} </p>
<p><b>审批状态:</b> <el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
</el-card>
</el-timeline-item>
</el-timeline>
<div v-if="!approveLogs || approveLogs.length === 0"></div>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="detailDialogVisible = false"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { listReceiptApproved, getReceiptDetail } from "@/api/finance/receipt";
import { listCompletedFlows } from "@/api/flow";
import ReceiptDetail from "../components/ReceiptDetail";
import ApproveLayout from "@/views/approve/ApproveLayout";
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
export default {
name: "ReceiptRefoundApproved",
components: { ReceiptDetail, ApproveLayout },
data() {
return {
loading: true,
showSearch: true,
total: 0,
receiptList: [],
queryParams: {
pageNum: 1,
pageSize: 10,
receiptBillCode: null,
partnerName: null,
projectName: null,
processKey: 'finance_receipt_refound',
orderByColumn: 'applyTime',
isAsc: 'desc'
},
dateRange: [],
detailDialogVisible: false,
detailLoading: false,
form: {},
approveLogs: [],
currentReceiptId: null,
pdfExporting: false
};
},
created() {
this.getList();
},
methods: {
getList() {
this.loading = true;
listReceiptApproved(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
this.receiptList = response.rows;
this.total = response.total;
this.loading = false;
});
},
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
resetQuery() {
this.dateRange = [];
this.resetForm("queryForm");
this.handleQuery();
},
handleView(row) {
this.form = {};
this.approveLogs = [];
this.currentReceiptId = row.id;
this.detailLoading = true;
this.detailDialogVisible = true;
getReceiptDetail(this.currentReceiptId).then(response => {
this.form = response.data;
this.loadApproveHistory(this.form.receiptBillCode);
this.detailLoading = false;
}).catch(() => {
this.detailLoading = false;
});
},
loadApproveHistory(businessKey) {
if (businessKey) {
listCompletedFlows({ businessKey: businessKey }).then(response => {
this.approveLogs = response.data;
});
}
},
getStatusText(status) {
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
return map[status] || '提交审批';
},
async exportPDF() {
this.pdfExporting = true;
try {
const element = this.$refs.approveLayout.$el;
const fileName = `收款单-${this.form.receiptBillCode || ''}.pdf`;
await exportElementToPDF(element, fileName);
this.$modal.msgSuccess('PDF导出成功');
} catch (error) {
console.error('PDF导出失败:', error);
this.$modal.msgError('PDF导出失败请稍后重试');
} finally {
this.pdfExporting = false;
}
}
}
};
</script>
<style scoped>
.process-container {
padding: 10px;
}
/* 导出PDF时的特殊样式 */
.approve-container.exporting-pdf ::v-deep .el-button--primary,
.approve-container.exporting-pdf ::v-deep .el-button--text {
display: none;
}
.approve-container.exporting-pdf ::v-deep .el-input__inner,
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
border: none !important;
box-shadow: none !important;
background-color: transparent !important;
resize: none !important;
padding: 0 !important;
}
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
display: none;
}
</style>

View File

@ -1,134 +0,0 @@
<template>
<div class="receipt-detail">
<div style="text-align: center;font-weight:bold;font-size: 25px;">退款申请单</div>
<el-descriptions title="收款单信息" :column="3" border>
<el-descriptions-item label="销售-收款单编号">{{ data.receiptBillCode }}</el-descriptions-item>
<el-descriptions-item :span="2" label="进货商名称">{{ data.partnerName }}</el-descriptions-item>
<!-- <el-descriptions-item label="收款单类型">-->
<!-- <dict-tag :options="dict.type.receipt_bill_type" :value="data.receiptBillType"/>-->
<!-- </el-descriptions-item>-->
<el-descriptions-item label="含税总价(元)"><span style="color: red">{{ formatCurrency(data.totalPriceWithTax) }}</span></el-descriptions-item>
<el-descriptions-item label="未税总价(元)"><span style="color: red">{{ formatCurrency(data.totalPriceWithoutTax) }}</span></el-descriptions-item>
<el-descriptions-item label="税额(元)"><span style="color: red">{{ formatCurrency(data.taxAmount) }}</span></el-descriptions-item>
<el-descriptions-item label="支付方式">
<dict-tag :options="dict.type.payment_method" :value="data.receiptMethod"/>
</el-descriptions-item>
<el-descriptions-item label="银行账号">{{ data.receiptBankNumber }}</el-descriptions-item>
<el-descriptions-item label="账户名称">{{ data.receiptAccountName }}</el-descriptions-item>
<el-descriptions-item label="银行开户行">{{ data.receiptBankOpenAddress }}</el-descriptions-item>
<el-descriptions-item label="银行行号">{{ data.bankNumber }}</el-descriptions-item>
<!-- <el-descriptions-item label="备注" :span="3">{{ data.remark }}</el-descriptions-item>-->
</el-descriptions>
<div class="section" style="margin-top: 20px;" v-show="data.detailDTOList && data.detailDTOList.length>0">
<div class="el-descriptions__title">应收单列表</div>
<el-table :data="data.detailDTOList" border style="width: 100%; margin-top: 10px;">
<el-table-column type="index" label="序号" width="50" align="center"></el-table-column>
<el-table-column prop="receivableBillCode" label="销售-应收单编号" align="center"></el-table-column>
<el-table-column prop="projectName" label="项目名称" align="center"></el-table-column>
<el-table-column prop="productType" label="产品类型" align="center">
<template slot-scope="scope">
<dict-tag :options="dict.type.product_type" :value="scope.row.productType"/>
</template>
</el-table-column>
<el-table-column prop="totalPriceWithTax" label="含税总价" align="center"></el-table-column>
<el-table-column prop="receiptAmount" label="本次收款金额" align="center"></el-table-column>
</el-table>
</div>
<div class="section" style="margin-top: 20px;" v-if="attachments && attachments.length > 0">
<div class="el-descriptions__title">附件信息</div>
<el-table :data="attachments" border style="width: 100%; margin-top: 10px;">
<el-table-column prop="fileName" label="附件名称" align="center"></el-table-column>
<el-table-column prop="createByName" label="上传人" align="center"></el-table-column>
<el-table-column prop="createTime" label="上传时间" align="center"></el-table-column>
<el-table-column label="操作" align="center">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-view" @click="handlePreview(scope.row)"></el-button>
<el-button size="mini" type="text" icon="el-icon-download" @click="handleDownload(scope.row)"></el-button>
</template>
</el-table-column>
</el-table>
</div>
<el-dialog :visible.sync="pdfPreviewVisible" width="80%" append-to-body top="5vh" title="PDF预览">
<iframe :src="currentPdfUrl" width="100%" height="600px" frameborder="0"></iframe>
</el-dialog>
<el-dialog :visible.sync="imagePreviewVisible" width="60%" append-to-body top="5vh" title="图片预览">
<img :src="currentImageUrl" style="width: 100%;" />
</el-dialog>
</div>
</template>
<script>
import request from '@/utils/request';
export default {
name: "ReceiptRefoundDetail",
props: {
data: {
type: Object,
required: true,
default: () => ({})
}
},
dicts: ['receipt_bill_type', 'product_type','payment_method'],
data() {
return {
attachments: [],
pdfPreviewVisible: false,
currentPdfUrl: '',
imagePreviewVisible: false,
currentImageUrl: ''
};
},
watch: {
},
methods: {
isPdf(filePath) {
return filePath && filePath.toLowerCase().endsWith('.pdf');
},
getImageUrl(resource) {
return process.env.VUE_APP_BASE_API + "/common/download/resource?resource=" + resource;
},
handlePreview(row) {
if (this.isPdf(row.filePath)) {
// PDF Preview logic
request({
url: '/common/download/resource',
method: 'get',
params: { resource: row.filePath },
responseType: 'blob'
}).then(res => {
const blob = new Blob([res.data], { type: 'application/pdf' });
this.currentPdfUrl = URL.createObjectURL(blob);
this.pdfPreviewVisible = true;
});
} else {
// Image Preview
this.currentImageUrl = this.getImageUrl(row.filePath);
this.imagePreviewVisible = true;
}
},
handleDownload(row) {
const link = document.createElement('a');
link.href = this.getImageUrl(row.filePath);
link.download = row.fileName || 'attachment';
link.style.display = 'none';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
}
};
</script>
<style scoped>
.receipt-detail {
margin-bottom: 20px;
}
</style>

View File

@ -1,306 +0,0 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
<el-form-item label="收款编号" prop="receiptBillCode">
<el-input v-model="queryParams.receiptBillCode" placeholder="请输入收款编号" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="进货商" prop="partnerName">
<el-input v-model="queryParams.partnerName" placeholder="请输入进货商名称" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="项目名称" prop="projectName">
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="提交日期">
<el-date-picker
v-model="dateRange"
style="width: 240px"
value-format="yyyy-MM-dd HH:mm:ss"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
size="mini"
@click="toApproved()"
>审批历史</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="receiptList">
<el-table-column label="序号" type="index" width="50" align="center" />
<el-table-column label="收款编号" align="center" prop="receiptBillCode" />
<el-table-column label="进货商" align="center" prop="partnerName" />
<!-- <el-table-column label="项目名称" align="center" prop="projectName" />-->
<el-table-column label="金额" align="center" prop="totalPriceWithTax" >
<template slot-scope="scope">
<span style="color: red">{{ formatCurrency(scope.row.totalPriceWithTax) }}</span>
</template>
</el-table-column>
<!-- <el-table-column label="汇智负责人" align="center" prop="hzUserName" />-->
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.applyTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="审批节点" align="center" prop="approveNode" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleApprove(scope.row)"></el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
<!-- 审批详情主对话框 -->
<el-dialog title="收款单审批" :visible.sync="detailDialogVisible" width="80%" append-to-body>
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
<el-button
type="primary"
size="small"
icon="el-icon-download"
@click="exportPDF"
:loading="pdfExporting"
>导出PDF</el-button>
</div>
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
<ApproveLayout ref="approveLayout" title="收款单详情">
<receipt-detail :data="form"></receipt-detail>
<template #footer>
<span>{{ form.receiptBillCode }}</span>
</template>
</ApproveLayout>
</div>
<el-divider content-position="left">流转意见</el-divider>
<div class="process-container">
<el-timeline>
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
<el-card>
<h4>{{ log.approveOpinion }}</h4>
<p><b>操作人:</b> {{ log.approveUserName }} </p>
<p><b>审批状态:</b><el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
</el-card>
</el-timeline-item>
</el-timeline>
<div v-if="!approveLogs || approveLogs.length === 0"></div>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="openOpinionDialog('approve')"></el-button>
<el-button type="danger" @click="openOpinionDialog('reject')"></el-button>
<el-button @click="detailDialogVisible = false"> </el-button>
</span>
</el-dialog>
<!-- 审批意见对话框 -->
<el-dialog :title="confirmDialogTitle" :visible.sync="opinionDialogVisible" width="30%" append-to-body>
<el-form ref="opinionForm" :model="opinionForm" :rules="opinionRules" label-width="100px">
<el-form-item label="审批意见" prop="approveOpinion">
<el-input v-model="opinionForm.approveOpinion" type="textarea" :rows="4" placeholder="请输入审批意见"/>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="opinionDialogVisible = false"> </el-button>
<el-button type="primary" @click="showConfirmDialog()"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { listReceiptApprove, getReceiptDetail } from "@/api/finance/receipt";
import { approveTask, listCompletedFlows } from "@/api/flow";
import ReceiptDetail from "./components/ReceiptDetail";
import ApproveLayout from "@/views/approve/ApproveLayout";
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
export default {
name: "ReceiptRefoundApprove",
components: { ReceiptDetail, ApproveLayout },
data() {
return {
loading: true,
showSearch: true,
total: 0,
receiptList: [],
queryParams: {
pageNum: 1,
pageSize: 10,
receiptBillCode: null,
partnerName: null,
projectName: null,
processKey: 'finance_receipt_refound',
orderByColumn: 'applyTime',
isAsc: 'desc'
},
dateRange: [],
detailDialogVisible: false,
detailLoading: false,
form: {},
approveLogs: [],
opinionDialogVisible: false,
confirmDialogTitle: '',
currentApproveType: '',
opinionForm: {
approveOpinion: ''
},
opinionRules: {
approveOpinion: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }],
},
processKey: 'finance_receipt_refound',
taskId: null,
currentReceiptId: null,
pdfExporting: false
};
},
created() {
this.getList();
},
methods: {
getList() {
this.loading = true;
listReceiptApprove(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
this.receiptList = response.rows;
this.total = response.total;
this.loading = false;
});
},
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
resetQuery() {
this.dateRange = [];
this.resetForm("queryForm");
this.handleQuery();
},
toApproved() {
this.$router.push( '/approve/receiptRefoundLog' )
},
handleApprove(row) {
this.resetDetailForm();
this.currentReceiptId = row.id;
this.taskId = row.taskId;
this.detailLoading = true;
this.detailDialogVisible = true;
getReceiptDetail(this.currentReceiptId).then(response => {
this.form = response.data;
this.loadApproveHistory(this.form.receiptBillCode);
this.detailLoading = false;
}).catch(() => {
this.detailLoading = false;
});
},
resetDetailForm() {
this.form = {};
this.approveLogs = [];
this.opinionForm.approveOpinion = '';
},
loadApproveHistory(businessKey) {
if (businessKey) {
let keys = [];
if(this.processKey) keys.push(this.processKey);
listCompletedFlows({ businessKey: businessKey, processKeyList: keys }).then(response => {
this.approveLogs = response.data;
});
}
},
openOpinionDialog(type) {
this.currentApproveType = type;
this.confirmDialogTitle = type === 'approve' ? '同意审批' : '驳回审批';
this.opinionDialogVisible = true;
this.opinionForm.approveOpinion = '';
this.$nextTick(() => {
if(this.$refs.opinionForm) this.$refs.opinionForm.clearValidate();
});
},
showConfirmDialog() {
this.$refs.opinionForm.validate(valid => {
if (valid) {
this.opinionDialogVisible = false;
this.submitApproval();
}
});
},
submitApproval() {
const approveBtn = this.currentApproveType === 'approve' ? 1 : 0;
const params = {
businessKey: this.form.receiptBillCode,
processKey: this.processKey,
taskId: this.taskId,
variables: {
comment: this.opinionForm.approveOpinion,
approveBtn: approveBtn
}
};
approveTask(params).then(() => {
this.$modal.msgSuccess(this.confirmDialogTitle + "成功");
this.detailDialogVisible = false;
this.getList();
});
},
getStatusText(status) {
if (!status) {
return '提交审批'
}
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
return map[status] || '提交审批';
},
async exportPDF() {
this.pdfExporting = true;
try {
const element = this.$refs.approveLayout.$el;
const fileName = `收款单-${this.form.receiptBillCode || ''}.pdf`;
await exportElementToPDF(element, fileName);
this.$modal.msgSuccess('PDF导出成功');
} catch (error) {
console.error('PDF导出失败:', error);
this.$modal.msgError('PDF导出失败请稍后重试');
} finally {
this.pdfExporting = false;
}
}
}
};
</script>
<style scoped>
.process-container {
padding: 10px;
}
/* 导出PDF时的特殊样式 */
.approve-container.exporting-pdf ::v-deep .el-button--primary,
.approve-container.exporting-pdf ::v-deep .el-button--text {
display: none;
}
.approve-container.exporting-pdf ::v-deep .el-input__inner,
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
border: none !important;
box-shadow: none !important;
background-color: transparent !important;
resize: none !important;
padding: 0 !important;
}
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
display: none;
}
</style>

View File

@ -1,240 +0,0 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
<el-form-item label="开票编号" prop="invoiceBillCode">
<el-input v-model="queryParams.invoiceBillCode" placeholder="请输入开票编号" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="客户" prop="partnerName">
<el-input v-model="queryParams.partnerName" placeholder="请输入客户" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="项目名称" prop="projectName">
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="提交日期">
<el-date-picker
v-model="dateRange"
style="width: 240px"
value-format="yyyy-MM-dd"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
<el-table v-loading="loading" :data="invoiceList">
<el-table-column label="序号" type="index" width="50" align="center" />
<el-table-column label="开票编号" align="center" prop="invoiceBillCode" />
<el-table-column label="客户" align="center" prop="partnerName" />
<el-table-column label="金额" align="center" prop="totalPriceWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)" />
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.applyTime) }}</span>
</template>
</el-table-column>
<el-table-column label="审批节点" align="center" prop="approveNode" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row)"></el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
<!-- 详情对话框 -->
<el-dialog title="开票单详情" :visible.sync="detailDialogVisible" width="80%" append-to-body>
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
<el-button
type="primary"
size="small"
icon="el-icon-download"
@click="exportPDF"
:loading="pdfExporting"
>导出PDF</el-button>
<el-button
type="primary"
size="small"
icon="el-icon-document"
style="margin-right: 10px;"
@click="exportExcel"
:loading="excelExporting"
>导出Excel</el-button>
</div>
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
<ApproveLayout ref="approveLayout" title="开票单详情">
<receivable-invoice-detail :data="form"></receivable-invoice-detail>
<template #footer>
<span>{{ form.invoiceBillCode }}</span>
</template>
</ApproveLayout>
</div>
<el-divider content-position="left">流转意见</el-divider>
<div class="process-container">
<el-timeline>
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
<el-card>
<h4>{{ log.approveOpinion }}</h4>
<p><b>操作人:</b> {{ log.approveUserName }} </p>
<p><b>审批状态:</b> <el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
</el-card>
</el-timeline-item>
</el-timeline>
<div v-if="!approveLogs || approveLogs.length === 0"></div>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="detailDialogVisible = false"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { listInvoiceApproved, getInvoiceDetail } from "@/api/finance/invoice";
import { listCompletedFlows } from "@/api/flow";
import ReceivableInvoiceDetail from "../components/ReceivableInvoiceDetail";
import ApproveLayout from "@/views/approve/ApproveLayout";
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
import { exportInvoiceInfoToExcel } from "@/views/approve/finance/invoiceExcelUtils";
export default {
name: "ReceivableInvoiceApproved",
components: { ReceivableInvoiceDetail, ApproveLayout },
data() {
return {
loading: true,
showSearch: true,
total: 0,
invoiceList: [],
queryParams: {
pageNum: 1,
pageSize: 10,
invoiceBillCode: null,
partnerName: null,
projectName: null,
processKey: 'finance_invoice_approve',
orderByColumn: 'applyTime',
isAsc: 'desc'
},
dateRange: [],
detailDialogVisible: false,
detailLoading: false,
form: {},
approveLogs: [],
currentInvoiceId: null,
pdfExporting: false,
excelExporting: false
};
},
created() {
this.getList();
},
methods: {
getList() {
this.loading = true;
listInvoiceApproved(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
this.invoiceList = response.rows;
this.total = response.total;
this.loading = false;
});
},
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
resetQuery() {
this.dateRange = [];
this.resetForm("queryForm");
this.handleQuery();
},
handleView(row) {
this.form = {};
this.approveLogs = [];
this.currentInvoiceId = row.id;
this.detailLoading = true;
this.detailDialogVisible = true;
getInvoiceDetail(this.currentInvoiceId).then(response => {
this.form = response.data;
this.loadApproveHistory(this.form.invoiceBillCode);
this.detailLoading = false;
}).catch(() => {
this.detailLoading = false;
});
},
loadApproveHistory(businessKey) {
if (businessKey) {
listCompletedFlows({ businessKey: businessKey }).then(response => {
this.approveLogs = response.data;
});
}
},
getStatusText(status) {
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
return map[status] || '提交审批';
},
async exportPDF() {
this.pdfExporting = true;
try {
const element = this.$refs.approveLayout.$el;
const fileName = `开票单-${this.form.invoiceBillCode || ''}.pdf`;
await exportElementToPDF(element, fileName);
this.$modal.msgSuccess('PDF导出成功');
} catch (error) {
console.error('PDF导出失败:', error);
this.$modal.msgError('PDF导出失败请稍后重试');
} finally {
this.pdfExporting = false;
}
},
exportExcel() {
if (!this.form || !this.form.invoiceBillCode) {
this.$modal.msgWarning("暂无可导出的开票信息");
return;
}
this.excelExporting = true;
try {
const fileName = `开票单-${this.form.invoiceBillCode || ""}.xls`;
exportInvoiceInfoToExcel(this.form, fileName);
this.$modal.msgSuccess("Excel导出成功");
} catch (error) {
console.error("Excel导出失败:", error);
this.$modal.msgError("Excel导出失败请稍后重试");
} finally {
this.excelExporting = false;
}
}
}
};
</script>
<style scoped>
.process-container {
padding: 10px;
}
/* 导出PDF时的特殊样式 */
.approve-container.exporting-pdf ::v-deep .el-button--primary,
.approve-container.exporting-pdf ::v-deep .el-button--text {
display: none;
}
.approve-container.exporting-pdf ::v-deep .el-input__inner,
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
border: none !important;
box-shadow: none !important;
background-color: transparent !important;
resize: none !important;
padding: 0 !important;
}
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
display: none;
}
</style>

View File

@ -1,170 +0,0 @@
<template>
<div class="invoice-detail">
<div style="text-align: center;font-weight:bold;font-size: 25px;">开票申请单</div>
<el-descriptions title="开票单信息" :column="3" border>
<el-descriptions-item label="销售-开票单编号">{{ data.invoiceBillCode }}</el-descriptions-item>
<el-descriptions-item :span="2" label="进货商">{{ data.partnerName }}</el-descriptions-item>
<el-descriptions-item label="含税总价(元)">{{ formatCurrency(data.totalPriceWithTax) }}</el-descriptions-item>
<el-descriptions-item label="未税总价(元)">{{ formatCurrency(data.totalPriceWithoutTax) }}</el-descriptions-item>
<el-descriptions-item label="税额(元)">{{ formatCurrency(data.taxAmount) }}</el-descriptions-item>
<!-- <el-descriptions-item label="银行账号">{{ data.buyerBankAccount }}</el-descriptions-item>-->
<!-- <el-descriptions-item label="账户名称">{{ data.buyerName }}</el-descriptions-item>-->
<!-- <el-descriptions-item label="银行开户行">{{ data.buyerBank }}</el-descriptions-item>-->
<!-- <el-descriptions-item label="银行行号">{{ data.bankNumber }}</el-descriptions-item> -->
</el-descriptions>
<div class="section" style="margin-top: 20px;">
<div class="el-descriptions__title">发票信息</div>
<invoice-info-view :data="data" />
</div>
<div class="section" style="margin-top: 20px;" v-show="data.detailDTOList && data.detailDTOList.length>0">
<div class="el-descriptions__title">应收单列表</div>
<el-table :data="data.detailDTOList" border style="width: 100%; margin-top: 10px;">
<el-table-column type="index" label="序号" width="50" align="center"></el-table-column>
<el-table-column prop="receivableBillCode" label="销售-应收单编号" align="center"></el-table-column>
<el-table-column prop="projectName" label="项目名称" align="center"></el-table-column>
<el-table-column prop="productType" label="产品类型" align="center">
<template slot-scope="scope">
<dict-tag :options="dict.type.product_type" :value="scope.row.productType"/>
</template>
</el-table-column>
<el-table-column prop="totalPriceWithTax" label="含税总价" align="center" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"></el-table-column>
<el-table-column prop="receiptAmount" label="本次开票金额" align="center" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"></el-table-column>
</el-table>
</div>
<div class="section" style="margin-top: 20px;" v-if="excelList && excelList.length > 0">
<div class="el-descriptions__title">附件信息</div>
<el-table :data="excelList" border style="width: 100%; margin-top: 10px;">
<el-table-column prop="fileName" label="附件名称" align="center"></el-table-column>
<el-table-column prop="createUserName" label="上传信息" align="center"></el-table-column>
<el-table-column prop="createTime" label="生成时间" align="center"></el-table-column>
<el-table-column label="操作" align="center">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-download" @click="handleDownload(scope.row)"></el-button>
</template>
</el-table-column>
</el-table>
</div>
<!-- <div class="section" style="margin-top: 20px;" v-if="attachments && attachments.length > 0">-->
<!-- <div class="el-descriptions__title">附件信息</div>-->
<!-- <el-table :data="attachments" border style="width: 100%; margin-top: 10px;">-->
<!-- <el-table-column prop="fileName" label="附件名称" align="center"></el-table-column>-->
<!-- <el-table-column prop="createUserName" label="上传人" align="center"></el-table-column>-->
<!-- <el-table-column prop="createTime" label="上传时间" align="center"></el-table-column>-->
<!-- <el-table-column label="操作" align="center">-->
<!-- <template slot-scope="scope">-->
<!-- <el-button size="mini" type="text" icon="el-icon-view" @click="handlePreview(scope.row)"></el-button>-->
<!-- <el-button size="mini" type="text" icon="el-icon-download" @click="handleDownload(scope.row)"></el-button>-->
<!-- </template>-->
<!-- </el-table-column>-->
<!-- </el-table>-->
<!-- </div>-->
<!-- <el-dialog :visible.sync="pdfPreviewVisible" width="80%" append-to-body top="5vh" title="PDF预览">-->
<!-- <iframe :src="currentPdfUrl" width="100%" height="600px" frameborder="0"></iframe>-->
<!-- </el-dialog>-->
<!-- <el-dialog :visible.sync="imagePreviewVisible" width="60%" append-to-body top="5vh" title="图片预览">-->
<!-- <img :src="currentImageUrl" style="width: 100%;" />-->
<!-- </el-dialog>-->
</div>
</template>
<script>
import { getInvoiceAttachments } from "@/api/finance/invoice";
import request from '@/utils/request';
import InvoiceInfoView from '@/views/finance/invoice/components/InvoiceInfoView';
export default {
name: "ReceivableInvoiceDetail",
components: { InvoiceInfoView },
props: {
data: {
type: Object,
required: true,
default: () => ({})
}
},
dicts: ['product_type'],
data() {
return {
attachments: [],
excelList: [],
pdfPreviewVisible: false,
currentPdfUrl: '',
imagePreviewVisible: false,
currentImageUrl: ''
};
},
watch: {
data: {
handler(val) {
if (val && val.updateTime) {
this.excelList = [{
fileName: '电子发票--购买方公司信息.xlsx',
createUserName: '系统生成',
createTime: val.updateTime,
isSystemGenerated: true
}];
}
},
immediate: true,
deep: true
}
},
methods: {
isPdf(filePath) {
return filePath && filePath.toLowerCase().endsWith('.pdf');
},
getImageUrl(resource) {
return process.env.VUE_APP_BASE_API + "/common/download/resource?resource=" + resource;
},
handlePreview(row) {
if (this.isPdf(row.filePath)) {
request({
url: '/common/download/resource',
method: 'get',
params: { resource: row.filePath },
responseType: 'blob'
}).then(res => {
const blob = new Blob([res.data], { type: 'application/pdf' });
this.currentPdfUrl = URL.createObjectURL(blob);
this.pdfPreviewVisible = true;
});
} else {
this.currentImageUrl = this.getImageUrl(row.filePath);
this.imagePreviewVisible = true;
}
},
handleDownload(row) {
if (row.isSystemGenerated) {
request({
url: '/finance/invoice/export/' + this.data.invoiceBillCode,
method: 'get'
}).then(res => {
window.location.href = process.env.VUE_APP_BASE_API + "/common/download?fileName=" + encodeURIComponent(res.msg) + "&delete=true";
});
return;
}
const link = document.createElement('a');
link.href = this.getImageUrl(row.filePath);
link.download = row.fileName || 'attachment';
link.style.display = 'none';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
}
};
</script>
<style scoped>
.invoice-detail {
margin-bottom: 20px;
}
</style>

View File

@ -1,327 +0,0 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
<el-form-item label="开票编号" prop="invoiceBillCode">
<el-input v-model="queryParams.invoiceBillCode" placeholder="请输入开票编号" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="进货商" prop="partnerName">
<el-input v-model="queryParams.partnerName" placeholder="请输入进货商" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="项目名称" prop="projectName">
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="提交日期">
<el-date-picker
v-model="dateRange"
style="width: 240px"
value-format="yyyy-MM-dd HH:mm:ss"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
size="mini"
@click="toApproved()"
>审批历史</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="invoiceList">
<el-table-column label="序号" type="index" width="50" align="center" />
<el-table-column label="开票编号" align="center" prop="invoiceBillCode" />
<el-table-column label="进货商" align="center" prop="partnerName" />
<el-table-column label="金额" align="center" prop="totalPriceWithTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.applyTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="审批节点" align="center" prop="approveNode" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleApprove(scope.row)"></el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
<!-- 审批详情主对话框 -->
<el-dialog title="开票单审批" :visible.sync="detailDialogVisible" width="80%" append-to-body>
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
<el-button
type="primary"
size="small"
icon="el-icon-download"
@click="exportPDF"
:loading="pdfExporting"
>导出PDF</el-button>
<el-button
type="primary"
size="small"
icon="el-icon-document"
style="margin-right: 10px;"
@click="exportExcel"
:loading="excelExporting"
>导出Excel</el-button>
</div>
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
<ApproveLayout ref="approveLayout" title="开票单详情">
<receivable-invoice-detail :data="form"></receivable-invoice-detail>
<template #footer>
<span>{{ form.invoiceBillCode }}</span>
</template>
</ApproveLayout>
</div>
<el-divider content-position="left">流转意见</el-divider>
<div class="process-container">
<el-timeline>
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
<el-card>
<h4>{{ log.approveOpinion }}</h4>
<p><b>操作人:</b> {{ log.approveUserName }} </p>
<p><b>审批状态:</b> <el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
</el-card>
</el-timeline-item>
</el-timeline>
<div v-if="!approveLogs || approveLogs.length === 0"></div>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="openOpinionDialog('approve')"></el-button>
<el-button type="danger" @click="openOpinionDialog('reject')"></el-button>
<el-button @click="detailDialogVisible = false"> </el-button>
</span>
</el-dialog>
<!-- 审批意见对话框 -->
<el-dialog :title="confirmDialogTitle" :visible.sync="opinionDialogVisible" width="30%" append-to-body>
<el-form ref="opinionForm" :model="opinionForm" :rules="opinionRules" label-width="100px">
<el-form-item label="审批意见" prop="approveOpinion">
<el-input v-model="opinionForm.approveOpinion" type="textarea" :rows="4" placeholder="请输入审批意见"/>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="opinionDialogVisible = false"> </el-button>
<el-button type="primary" @click="showConfirmDialog()"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { listInvoiceApprove, getInvoiceDetail } from "@/api/finance/invoice";
import { approveTask, listCompletedFlows } from "@/api/flow";
import ReceivableInvoiceDetail from "./components/ReceivableInvoiceDetail";
import ApproveLayout from "@/views/approve/ApproveLayout";
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
import { exportInvoiceInfoToExcel } from "@/views/approve/finance/invoiceExcelUtils";
export default {
name: "ReceivableInvoiceApprove",
components: { ReceivableInvoiceDetail, ApproveLayout },
data() {
return {
loading: true,
showSearch: true,
total: 0,
invoiceList: [],
queryParams: {
pageNum: 1,
pageSize: 10,
invoiceBillCode: null,
partnerName: null,
projectName: null,
processKey: 'finance_invoice_approve',
orderByColumn: 'applyTime',
isAsc: 'desc'
},
dateRange: [],
detailDialogVisible: false,
detailLoading: false,
form: {},
approveLogs: [],
opinionDialogVisible: false,
confirmDialogTitle: '',
currentApproveType: '',
opinionForm: {
approveOpinion: ''
},
opinionRules: {
approveOpinion: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }],
},
processKey: 'finance_invoice_approve',
taskId: null,
currentInvoiceId: null,
pdfExporting: false,
excelExporting: false
};
},
created() {
this.getList();
},
methods: {
getList() {
this.loading = true;
listInvoiceApprove(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
this.invoiceList = response.rows;
this.total = response.total;
this.loading = false;
});
},
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
resetQuery() {
this.dateRange = [];
this.resetForm("queryForm");
this.handleQuery();
},
toApproved() {
this.$router.push('/approve/receivableInvoiceLog')
},
handleApprove(row) {
this.resetDetailForm();
this.currentInvoiceId = row.id;
this.taskId = row.taskId;
this.detailLoading = true;
this.detailDialogVisible = true;
getInvoiceDetail(this.currentInvoiceId).then(response => {
this.form = response.data;
this.loadApproveHistory(this.form.invoiceBillCode);
this.detailLoading = false;
}).catch(() => {
this.detailLoading = false;
});
},
resetDetailForm() {
this.form = {};
this.approveLogs = [];
this.opinionForm.approveOpinion = '';
},
loadApproveHistory(businessKey) {
if (businessKey) {
let keys = [];
if(this.processKey) keys.push(this.processKey);
listCompletedFlows({ businessKey: businessKey, processKeyList: keys }).then(response => {
this.approveLogs = response.data;
});
}
},
openOpinionDialog(type) {
this.currentApproveType = type;
this.confirmDialogTitle = type === 'approve' ? '同意审批' : '驳回审批';
this.opinionDialogVisible = true;
this.opinionForm.approveOpinion = '';
this.$nextTick(() => {
if(this.$refs.opinionForm) this.$refs.opinionForm.clearValidate();
});
},
showConfirmDialog() {
this.$refs.opinionForm.validate(valid => {
if (valid) {
this.opinionDialogVisible = false;
this.submitApproval();
}
});
},
submitApproval() {
const approveBtn = this.currentApproveType === 'approve' ? 1 : 0;
const params = {
businessKey: this.form.invoiceBillCode,
processKey: this.processKey,
taskId: this.taskId,
variables: {
comment: this.opinionForm.approveOpinion,
approveBtn: approveBtn
}
};
approveTask(params).then(() => {
this.$modal.msgSuccess(this.confirmDialogTitle + "成功");
this.detailDialogVisible = false;
this.getList();
});
},
getStatusText(status) {
if (!status) {
return '提交审批'
}
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
return map[status] || '提交审批';
},
async exportPDF() {
this.pdfExporting = true;
try {
const element = this.$refs.approveLayout.$el;
const fileName = `开票单-${this.form.invoiceBillCode || ''}.pdf`;
await exportElementToPDF(element, fileName);
this.$modal.msgSuccess('PDF导出成功');
} catch (error) {
console.error('PDF导出失败:', error);
this.$modal.msgError('PDF导出失败请稍后重试');
} finally {
this.pdfExporting = false;
}
},
exportExcel() {
if (!this.form || !this.form.invoiceBillCode) {
this.$modal.msgWarning("暂无可导出的开票信息");
return;
}
this.excelExporting = true;
try {
const fileName = `开票单-${this.form.invoiceBillCode || ""}.xls`;
exportInvoiceInfoToExcel(this.form, fileName);
this.$modal.msgSuccess("Excel导出成功");
} catch (error) {
console.error("Excel导出失败:", error);
this.$modal.msgError("Excel导出失败请稍后重试");
} finally {
this.excelExporting = false;
}
}
}
};
</script>
<style scoped>
.process-container {
padding: 10px;
}
/* 导出PDF时的特殊样式 */
.approve-container.exporting-pdf ::v-deep .el-button--primary,
.approve-container.exporting-pdf ::v-deep .el-button--text {
display: none;
}
.approve-container.exporting-pdf ::v-deep .el-input__inner,
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
border: none !important;
box-shadow: none !important;
background-color: transparent !important;
resize: none !important;
padding: 0 !important;
}
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
display: none;
}
</style>

View File

@ -1,217 +0,0 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
<el-form-item label="开票编号" prop="invoiceBillCode">
<el-input v-model="queryParams.invoiceBillCode" placeholder="请输入开票编号" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="客户" prop="partnerName">
<el-input v-model="queryParams.partnerName" placeholder="请输入客户" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="项目名称" prop="projectName">
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="提交日期">
<el-date-picker
v-model="dateRange"
style="width: 240px"
value-format="yyyy-MM-dd"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
<el-table v-loading="loading" :data="invoiceList">
<el-table-column label="序号" type="index" width="50" align="center" />
<el-table-column label="开票编号" align="center" prop="invoiceBillCode" />
<el-table-column label="客户" align="center" prop="partnerName" />
<el-table-column label="金额" align="center" prop="totalPriceWithTax">
<template slot-scope="scope">
<span :style="scope.row.totalPriceWithTax < 0 ? 'color: red' : ''">{{ formatCurrency(scope.row.totalPriceWithTax) }}</span>
</template>
</el-table-column>
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.applyTime) }}</span>
</template>
</el-table-column>
<el-table-column label="审批节点" align="center" prop="approveNode" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row)"></el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
<!-- 详情对话框 -->
<el-dialog title="开票单详情" :visible.sync="detailDialogVisible" width="80%" append-to-body>
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
<el-button
type="primary"
size="small"
icon="el-icon-download"
@click="exportPDF"
:loading="pdfExporting"
>导出PDF</el-button>
</div>
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
<ApproveLayout ref="approveLayout" title="开票单详情">
<receivable-invoice-detail :data="form"></receivable-invoice-detail>
<template #footer>
<span>{{ form.invoiceBillCode }}</span>
</template>
</ApproveLayout>
</div>
<el-divider content-position="left">流转意见</el-divider>
<div class="process-container">
<el-timeline>
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
<el-card>
<h4>{{ log.approveOpinion }}</h4>
<p><b>操作人:</b> {{ log.approveUserName }} </p>
<p><b>审批状态:</b> <el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
</el-card>
</el-timeline-item>
</el-timeline>
<div v-if="!approveLogs || approveLogs.length === 0"></div>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="detailDialogVisible = false"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { listInvoiceApproved, getInvoiceDetail } from "@/api/finance/invoice";
import { listCompletedFlows } from "@/api/flow";
import ReceivableInvoiceDetail from "../components/ReceivableInvoiceDetail";
import ApproveLayout from "@/views/approve/ApproveLayout";
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
export default {
name: "ReceivableInvoiceRefundApproved",
components: { ReceivableInvoiceDetail, ApproveLayout },
data() {
return {
loading: true,
showSearch: true,
total: 0,
invoiceList: [],
queryParams: {
pageNum: 1,
pageSize: 10,
invoiceBillCode: null,
partnerName: null,
projectName: null,
processKey: 'finance_invoice_refound',
orderByColumn: 'applyTime',
isAsc: 'desc'
},
dateRange: [],
detailDialogVisible: false,
detailLoading: false,
form: {},
approveLogs: [],
currentInvoiceId: null,
pdfExporting: false
};
},
created() {
this.getList();
},
methods: {
getList() {
this.loading = true;
listInvoiceApproved(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
this.invoiceList = response.rows;
this.total = response.total;
this.loading = false;
});
},
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
resetQuery() {
this.dateRange = [];
this.resetForm("queryForm");
this.handleQuery();
},
handleView(row) {
this.form = {};
this.approveLogs = [];
this.currentInvoiceId = row.id;
this.detailLoading = true;
this.detailDialogVisible = true;
getInvoiceDetail(this.currentInvoiceId).then(response => {
this.form = response.data;
this.loadApproveHistory(this.form.invoiceBillCode);
this.detailLoading = false;
}).catch(() => {
this.detailLoading = false;
});
},
loadApproveHistory(businessKey) {
if (businessKey) {
listCompletedFlows({ businessKey: businessKey }).then(response => {
this.approveLogs = response.data;
});
}
},
getStatusText(status) {
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
return map[status] || '提交审批';
},
async exportPDF() {
this.pdfExporting = true;
try {
const element = this.$refs.approveLayout.$el;
const fileName = `开票单-${this.form.invoiceBillCode || ''}.pdf`;
await exportElementToPDF(element, fileName);
this.$modal.msgSuccess('PDF导出成功');
} catch (error) {
console.error('PDF导出失败:', error);
this.$modal.msgError('PDF导出失败请稍后重试');
} finally {
this.pdfExporting = false;
}
}
}
};
</script>
<style scoped>
.process-container {
padding: 10px;
}
/* 导出PDF时的特殊样式 */
.approve-container.exporting-pdf ::v-deep .el-button--primary,
.approve-container.exporting-pdf ::v-deep .el-button--text {
display: none;
}
.approve-container.exporting-pdf ::v-deep .el-input__inner,
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
border: none !important;
box-shadow: none !important;
background-color: transparent !important;
resize: none !important;
padding: 0 !important;
}
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
display: none;
}
</style>

View File

@ -1,183 +0,0 @@
<template>
<div class="invoice-detail">
<div style="text-align: center;font-weight:bold;font-size: 25px;">红冲开票申请单</div>
<el-descriptions title="开票单信息" :column="3" border>
<el-descriptions-item label="销售-开票单编号">{{ data.invoiceBillCode }}</el-descriptions-item>
<el-descriptions-item :span="2" label="进货商">{{ data.partnerName }}</el-descriptions-item>
<el-descriptions-item label="含税总价(元)">
<span :style="data.totalPriceWithTax < 0 ? 'color: red' : ''">{{ formatCurrency(data.totalPriceWithTax) }}</span>
</el-descriptions-item>
<el-descriptions-item label="未税总价(元)">
<span :style="data.totalPriceWithoutTax < 0 ? 'color: red' : ''">{{ formatCurrency(data.totalPriceWithoutTax) }}</span>
</el-descriptions-item>
<el-descriptions-item label="税额(元)">
<span :style="data.taxAmount < 0 ? 'color: red' : ''">{{ formatCurrency(data.taxAmount) }}</span>
</el-descriptions-item>
<!-- <el-descriptions-item label="银行账号">{{ data.buyerBankAccount }}</el-descriptions-item>-->
<!-- <el-descriptions-item label="账户名称">{{ data.buyerName }}</el-descriptions-item>-->
<!-- <el-descriptions-item label="银行开户行">{{ data.buyerBank }}</el-descriptions-item>-->
<!-- <el-descriptions-item label="银行行号">{{ data.bankNumber }}</el-descriptions-item> -->
</el-descriptions>
<div class="section" style="margin-top: 20px;">
<div class="el-descriptions__title">发票信息</div>
<invoice-info-view :data="data" />
</div>
<div class="section" style="margin-top: 20px;" v-show="data.detailDTOList && data.detailDTOList.length>0">
<div class="el-descriptions__title">应收单列表</div>
<el-table :data="data.detailDTOList" border style="width: 100%; margin-top: 10px;">
<el-table-column type="index" label="序号" width="50" align="center"></el-table-column>
<el-table-column prop="receivableBillCode" label="销售-应收单编号" align="center"></el-table-column>
<el-table-column prop="projectName" label="项目名称" align="center"></el-table-column>
<el-table-column prop="productType" label="产品类型" align="center">
<template slot-scope="scope">
<dict-tag :options="dict.type.product_type" :value="scope.row.productType"/>
</template>
</el-table-column>
<el-table-column prop="totalPriceWithTax" label="含税总价" align="center">
<template slot-scope="scope">
<span :style="scope.row.totalPriceWithTax < 0 ? 'color: red' : ''">{{ scope.row.totalPriceWithTax }}</span>
</template>
</el-table-column>
<el-table-column prop="receiptAmount" label="本次开票金额" align="center">
<template slot-scope="scope">
<span :style="scope.row.receiptAmount < 0 ? 'color: red' : ''">{{ scope.row.receiptAmount }}</span>
</template>
</el-table-column>
</el-table>
</div>
<div class="section" style="margin-top: 20px;" v-if="excelList && excelList.length > 0">
<div class="el-descriptions__title">附件信息</div>
<el-table :data="excelList" border style="width: 100%; margin-top: 10px;">
<el-table-column prop="fileName" label="附件名称" align="center"></el-table-column>
<el-table-column prop="createUserName" label="上传信息" align="center"></el-table-column>
<el-table-column prop="createTime" label="生成时间" align="center"></el-table-column>
<el-table-column label="操作" align="center">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-download" @click="handleDownload(scope.row)"></el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="section" style="margin-top: 20px;" v-if="attachments && attachments.length > 0">
<div class="el-descriptions__title">附件信息</div>
<el-table :data="attachments" border style="width: 100%; margin-top: 10px;">
<el-table-column prop="fileName" label="附件名称" align="center"></el-table-column>
<el-table-column prop="createUserName" label="上传人" align="center"></el-table-column>
<el-table-column prop="createTime" label="上传时间" align="center"></el-table-column>
<el-table-column label="操作" align="center">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-view" @click="handlePreview(scope.row)"></el-button>
<el-button size="mini" type="text" icon="el-icon-download" @click="handleDownload(scope.row)"></el-button>
</template>
</el-table-column>
</el-table>
</div>
<el-dialog :visible.sync="pdfPreviewVisible" width="80%" append-to-body top="5vh" title="PDF预览">
<iframe :src="currentPdfUrl" width="100%" height="600px" frameborder="0"></iframe>
</el-dialog>
<el-dialog :visible.sync="imagePreviewVisible" width="60%" append-to-body top="5vh" title="图片预览">
<img :src="currentImageUrl" style="width: 100%;" />
</el-dialog>
</div>
</template>
<script>
import { getInvoiceAttachments } from "@/api/finance/invoice";
import request from '@/utils/request';
import InvoiceInfoView from '@/views/finance/invoice/components/InvoiceInfoView';
export default {
name: "ReceivableInvoiceDetail",
components: { InvoiceInfoView },
props: {
data: {
type: Object,
required: true,
default: () => ({})
}
},
dicts: ['product_type'],
data() {
return {
attachments: [],
excelList: [],
pdfPreviewVisible: false,
currentPdfUrl: '',
imagePreviewVisible: false,
currentImageUrl: ''
};
},
watch: {
data: {
handler(val) {
if (val && val.updateTime) {
this.excelList = [{
fileName: '电子发票--购买方公司信息.xlsx',
createUserName: '系统生成',
createTime: val.updateTime,
isSystemGenerated: true
}];
}
},
immediate: true,
deep: true
}
},
methods: {
isPdf(filePath) {
return filePath && filePath.toLowerCase().endsWith('.pdf');
},
getImageUrl(resource) {
return process.env.VUE_APP_BASE_API + "/common/download/resource?resource=" + resource;
},
handlePreview(row) {
if (this.isPdf(row.filePath)) {
request({
url: '/common/download/resource',
method: 'get',
params: { resource: row.filePath },
responseType: 'blob'
}).then(res => {
const blob = new Blob([res.data], { type: 'application/pdf' });
this.currentPdfUrl = URL.createObjectURL(blob);
this.pdfPreviewVisible = true;
});
} else {
this.currentImageUrl = this.getImageUrl(row.filePath);
this.imagePreviewVisible = true;
}
},
handleDownload(row) {
if (row.isSystemGenerated) {
request({
url: '/finance/invoice/export/' + this.data.invoiceBillCode,
method: 'get'
}).then(res => {
window.location.href = process.env.VUE_APP_BASE_API + "/common/download?fileName=" + encodeURIComponent(res.msg) + "&delete=true";
});
return;
}
const link = document.createElement('a');
link.href = this.getImageUrl(row.filePath);
link.download = row.fileName || 'attachment';
link.style.display = 'none';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
}
};
</script>
<style scoped>
.invoice-detail {
margin-bottom: 20px;
}
</style>

View File

@ -1,304 +0,0 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
<el-form-item label="开票编号" prop="invoiceBillCode">
<el-input v-model="queryParams.invoiceBillCode" placeholder="请输入开票编号" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="进货商" prop="partnerName">
<el-input v-model="queryParams.partnerName" placeholder="请输入进货商" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="项目名称" prop="projectName">
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="提交日期">
<el-date-picker
v-model="dateRange"
style="width: 240px"
value-format="yyyy-MM-dd HH:mm:ss"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
size="mini"
@click="toApproved()"
>审批历史</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="invoiceList">
<el-table-column label="序号" type="index" width="50" align="center" />
<el-table-column label="开票编号" align="center" prop="invoiceBillCode" />
<el-table-column label="进货商" align="center" prop="partnerName" />
<el-table-column label="金额" align="center" prop="totalPriceWithTax">
<template slot-scope="scope">
<span :style="scope.row.totalPriceWithTax < 0 ? 'color: red' : ''">{{ formatCurrency(scope.row.totalPriceWithTax) }}</span>
</template>
</el-table-column>
<el-table-column label="提交日期" align="center" prop="applyTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.applyTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="审批节点" align="center" prop="approveNode" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleApprove(scope.row)"></el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
<!-- 审批详情主对话框 -->
<el-dialog title="开票单审批" :visible.sync="detailDialogVisible" width="80%" append-to-body>
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
<div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
<el-button
type="primary"
size="small"
icon="el-icon-download"
@click="exportPDF"
:loading="pdfExporting"
>导出PDF</el-button>
</div>
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
<ApproveLayout ref="approveLayout" title="开票单详情">
<receivable-invoice-detail :data="form"></receivable-invoice-detail>
<template #footer>
<span>{{ form.invoiceBillCode }}</span>
</template>
</ApproveLayout>
</div>
<el-divider content-position="left">流转意见</el-divider>
<div class="process-container">
<el-timeline>
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
<el-card>
<h4>{{ log.approveOpinion }}</h4>
<p><b>操作人:</b> {{ log.approveUserName }} </p>
<p><b>审批状态:</b> <el-tag :type="log.approveStatus == '3' ? 'success' : log.approveStatus == '2' ? 'danger' : 'info'">{{ getStatusText(log.approveStatus) }}</el-tag></p>
</el-card>
</el-timeline-item>
</el-timeline>
<div v-if="!approveLogs || approveLogs.length === 0"></div>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="openOpinionDialog('approve')"></el-button>
<el-button type="danger" @click="openOpinionDialog('reject')"></el-button>
<el-button @click="detailDialogVisible = false"> </el-button>
</span>
</el-dialog>
<!-- 审批意见对话框 -->
<el-dialog :title="confirmDialogTitle" :visible.sync="opinionDialogVisible" width="30%" append-to-body>
<el-form ref="opinionForm" :model="opinionForm" :rules="opinionRules" label-width="100px">
<el-form-item label="审批意见" prop="approveOpinion">
<el-input v-model="opinionForm.approveOpinion" type="textarea" :rows="4" placeholder="请输入审批意见"/>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="opinionDialogVisible = false"> </el-button>
<el-button type="primary" @click="showConfirmDialog()"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { listInvoiceApprove, getInvoiceDetail } from "@/api/finance/invoice";
import { approveTask, listCompletedFlows } from "@/api/flow";
import ReceivableInvoiceDetail from "./components/ReceivableInvoiceDetail";
import ApproveLayout from "@/views/approve/ApproveLayout";
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
export default {
name: "ReceivableInvoiceRefundApprove",
components: { ReceivableInvoiceDetail, ApproveLayout },
data() {
return {
loading: true,
showSearch: true,
total: 0,
invoiceList: [],
queryParams: {
pageNum: 1,
pageSize: 10,
invoiceBillCode: null,
partnerName: null,
projectName: null,
processKey: 'finance_invoice_refound',
orderByColumn: 'applyTime',
isAsc: 'desc'
},
dateRange: [],
detailDialogVisible: false,
detailLoading: false,
form: {},
approveLogs: [],
opinionDialogVisible: false,
confirmDialogTitle: '',
currentApproveType: '',
opinionForm: {
approveOpinion: ''
},
opinionRules: {
approveOpinion: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }],
},
processKey: 'finance_invoice_refound',
taskId: null,
currentInvoiceId: null,
pdfExporting: false
};
},
created() {
this.getList();
},
methods: {
getList() {
this.loading = true;
listInvoiceApprove(this.addDateRange(this.queryParams, this.dateRange, 'ApplyTime')).then(response => {
this.invoiceList = response.rows;
this.total = response.total;
this.loading = false;
});
},
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
resetQuery() {
this.dateRange = [];
this.resetForm("queryForm");
this.handleQuery();
},
toApproved() {
this.$router.push('/approve/receivableInvoiceRefundLog')
},
handleApprove(row) {
this.resetDetailForm();
this.currentInvoiceId = row.id;
this.taskId = row.taskId;
this.detailLoading = true;
this.detailDialogVisible = true;
getInvoiceDetail(this.currentInvoiceId).then(response => {
this.form = response.data;
this.loadApproveHistory(this.form.invoiceBillCode);
this.detailLoading = false;
}).catch(() => {
this.detailLoading = false;
});
},
resetDetailForm() {
this.form = {};
this.approveLogs = [];
this.opinionForm.approveOpinion = '';
},
loadApproveHistory(businessKey) {
if (businessKey) {
let keys = [];
if(this.processKey) keys.push(this.processKey);
listCompletedFlows({ businessKey: businessKey, processKeyList: keys }).then(response => {
this.approveLogs = response.data;
});
}
},
openOpinionDialog(type) {
this.currentApproveType = type;
this.confirmDialogTitle = type === 'approve' ? '同意审批' : '驳回审批';
this.opinionDialogVisible = true;
this.opinionForm.approveOpinion = '';
this.$nextTick(() => {
if(this.$refs.opinionForm) this.$refs.opinionForm.clearValidate();
});
},
showConfirmDialog() {
this.$refs.opinionForm.validate(valid => {
if (valid) {
this.opinionDialogVisible = false;
this.submitApproval();
}
});
},
submitApproval() {
const approveBtn = this.currentApproveType === 'approve' ? 1 : 0;
const params = {
businessKey: this.form.invoiceBillCode,
processKey: this.processKey,
taskId: this.taskId,
variables: {
comment: this.opinionForm.approveOpinion,
approveBtn: approveBtn
}
};
approveTask(params).then(() => {
this.$modal.msgSuccess(this.confirmDialogTitle + "成功");
this.detailDialogVisible = false;
this.getList();
});
},
getStatusText(status) {
if (!status) {
return '提交审批'
}
const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
return map[status] || '提交审批';
},
async exportPDF() {
this.pdfExporting = true;
try {
const element = this.$refs.approveLayout.$el;
const fileName = `开票单-${this.form.invoiceBillCode || ''}.pdf`;
await exportElementToPDF(element, fileName);
this.$modal.msgSuccess('PDF导出成功');
} catch (error) {
console.error('PDF导出失败:', error);
this.$modal.msgError('PDF导出失败请稍后重试');
} finally {
this.pdfExporting = false;
}
}
}
};
</script>
<style scoped>
.process-container {
padding: 10px;
}
/* 导出PDF时的特殊样式 */
.approve-container.exporting-pdf ::v-deep .el-button--primary,
.approve-container.exporting-pdf ::v-deep .el-button--text {
display: none;
}
.approve-container.exporting-pdf ::v-deep .el-input__inner,
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
border: none !important;
box-shadow: none !important;
background-color: transparent !important;
resize: none !important;
padding: 0 !important;
}
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
display: none;
}
</style>

View File

@ -135,7 +135,8 @@
import { approveOrder,getOrder } from "@/api/approve/order";
import ConfigInfo from './ConfigInfo.vue';
import ApproveLayout from '@/views/approve/ApproveLayout.vue';
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
import html2canvas from 'html2canvas';
import jsPDF from 'jspdf';
import OrderInfoDisplay from '@/views/project/order/components/OrderInfoDisplay.vue';
@ -467,21 +468,65 @@ export default {
// PDF
async exportPDF() {
this.pdfExporting = true;
const disabledElements = [];
try {
// ApproveLayoutDOM
const element = this.$refs.approveLayout.$el;
// disabled 便PDF
element.querySelectorAll('input:disabled, textarea:disabled').forEach(el => {
disabledElements.push(el);
el.disabled = false;
});
// 使html2canvas
const canvas = await html2canvas(element, {
scale: 2, //
useCORS: true, //
logging: false, //
backgroundColor: '#F8F5F0' //
});
// PDF
const imgWidth = 210; // A4mm
const pageHeight = 297; // A4mm
const imgHeight = (canvas.height * imgWidth) / canvas.width;
let heightLeft = imgHeight;
// PDF
const pdf = new jsPDF('p', 'mm', 'a4');
let position = 0;
// canvas
const imgData = canvas.toDataURL('image/jpeg');
//
pdf.addImage(imgData, 'JPEG', 0, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
//
while (heightLeft > 0) {
position = heightLeft - imgHeight;
pdf.addPage();
pdf.addImage(imgData, 'JPEG', 0, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
}
//
const fileName = `${this.order.projectCode || '订单'}-${this.order.orderCode || ''}-Rev.${this.order.versionCode || '1'}.pdf`;
// PDF
await exportElementToPDF(element, fileName);
// PDF
pdf.save(fileName);
this.$modal.msgSuccess('PDF导出成功');
} catch (error) {
console.error('PDF导出失败:', error);
this.$modal.msgError('PDF导出失败请稍后重试');
} finally {
// disabled
disabledElements.forEach(el => {
el.disabled = true;
});
this.pdfExporting = false;
}
}

View File

@ -30,7 +30,7 @@
<td v-if="!hidePrice">{{ product.discount ? (product.discount * 100).toFixed(2) + '%' : '-' }}</td>
<td>{{ formatCurrency(product.price) }}</td>
<td>{{ selectedDiscountLabel }}</td>
<td>{{ formatCurrency(getDisplayAllPrice(product, selectedDiscount)) }}</td>
<td>{{ formatCurrency(product.allPrice) }}</td>
<td>{{ formatCurrency(getDiscountedAllPrice(product, selectedDiscount)) }}</td>
<td>
<el-input
@ -86,7 +86,7 @@
<td v-if="!hidePrice">{{ product.discount ? (product.discount * 100).toFixed(2) + '%' : '-' }}</td>
<td>{{ formatCurrency(product.price) }}</td>
<td>{{ selectedDiscountLabel }}</td>
<td>{{ formatCurrency(getDisplayAllPrice(product, selectedDiscount)) }}</td>
<td>{{ formatCurrency(product.allPrice) }}</td>
<td>{{ formatCurrency(getDiscountedAllPrice(product, selectedDiscount)) }}</td>
<td>
<el-input
@ -142,7 +142,7 @@
<td v-if="!hidePrice">{{ product.discount ? (product.discount * 100).toFixed(2) + '%' : '-' }}</td>
<td>{{ formatCurrency(product.price) }}</td>
<td>{{ selectedDiscountLabel }}</td>
<td>{{ formatCurrency(getDisplayAllPrice(product, selectedDiscount)) }}</td>
<td>{{ formatCurrency(product.allPrice) }}</td>
<td>{{ formatCurrency(getDiscountedAllPrice(product, selectedDiscount)) }}</td>
<td>
<el-input
@ -178,7 +178,7 @@
</el-col>
</el-row>
<el-row type="flex" justify="end" align="middle" class="summary-row">
<el-col :span="orderData.orderStatus==='1'?18:24" style="text-align: center;">
<el-col :span="18" style="text-align: center;">
<span style="margin-right: 5px;">商业折扣</span>
<el-select
v-model="selectedDiscount"
@ -195,7 +195,7 @@
</el-option>
</el-select>
</el-col>
<el-col :span="6" v-if="orderData.orderStatus==='1'">
<el-col :span="6">
<span class="summary-label"> 折后总价合计</span> <span
class="summary-value-right"> {{ formatCurrency(finalTotal) }}</span>
</el-col>
@ -278,12 +278,7 @@ export default {
return this.calculateTotal(this.order.maintenanceProjectProductInfoList, this.selectedDiscount);
},
grandTotal() {
if (this.orderData.orderStatus === '1'){
return this.softwareTotal + this.hardwareTotal + this.maintenanceTotal;
}else{
return this.finalTotal;
}
},
finalTotal() {
return this.softwareDiscountedTotal + this.hardwareDiscountedTotal + this.maintenanceDiscountedTotal;
@ -302,20 +297,13 @@ export default {
//
this.$emit('tax-rate-change', product);
},
getDisplayAllPrice(product, discount) {
const allPrice = product.allPrice || 0;
if (this.order.orderStatus === '2' || this.order.orderStatus === 2) {
return this.$calc.div(allPrice, discount || 1);
}
return allPrice;
},
getDiscountedAllPrice(product, discount) {
const allPrice = product.allPrice || 0;
if (this.order.orderStatus === '2' || this.order.orderStatus === 2) {
return allPrice;
if (discount === 1) {
return product.allPrice;
}
const discountedPrice = this.$calc.mul(product.price || 0, discount == null ? 1 : discount, 2);
return this.$calc.mul(discountedPrice, product.quantity || 0, 2);
const roundedDiscountedUnitPrice = this.$calc.mul( product.price,discount);
return this.$calc.mul(roundedDiscountedUnitPrice , product.quantity);
},
calculateTotal(productList, discount) {
if (!productList) return 0;

View File

@ -49,28 +49,15 @@
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="产品编码" prop="productCode">
<el-input
v-model="queryParams.productCode"
placeholder="请输入产品编码"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="产品型号" prop="productModel">
<el-input
v-model="queryParams.productModel"
placeholder="请输入产品型号"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
@ -88,12 +75,7 @@
<el-table-column label="项目名称" align="center" prop="projectName" />
<el-table-column label="项目编号" align="center" prop="projectCode" />
<el-table-column label="客户名称" align="center" prop="customerName" />
<el-table-column label="订单金额" align="center" prop="actualPurchaseAmount" >
<template slot-scope="scope">
<span>{{ formatCurrency(scope.row.actualPurchaseAmount || scope.row.shipmentAmount) }}</span>
</template>
</el-table-column>
<el-table-column label="订单金额" align="center" prop="actualPurchaseAmount" />
<el-table-column label="汇智负责人" align="center" prop="dutyName" />
<el-table-column label="审批节点" align="center" prop="approveNode">
<template slot-scope="scope">
@ -124,7 +106,6 @@
<el-dialog
title="订单审批"
:visible.sync="approveDialogVisible"
:close-on-click-modal="false"
custom-class="approve-dialog"
width="80%"
top="5vh"
@ -149,7 +130,6 @@
<script>
import { listOrder } from "@/api/approve/order";
import ApproveDialog from './Approve.vue';
import {formatCurrency} from "../../../utils";
export default {
name: "Order",
@ -176,14 +156,9 @@ export default {
orderCode: null,
projectName: null,
projectCode: null,
productCode: null,
productModel: null,
customerName: null,
dutyName: null,
approveNode: null,
orderStatus: '1',
orderByColumn: 't2.id',
isAsc: 'desc',
},
};
},
@ -191,7 +166,6 @@ export default {
this.getList();
},
methods: {
formatCurrency,
toApproved(){
this.$router.push({
path: '/approve/orderLog',

View File

@ -23,22 +23,7 @@
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
<el-form-item label="产品编码" prop="productCode">
<el-input
v-model="queryParams.productCode"
placeholder="请输入产品编码"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="产品型号" prop="productModel">
<el-input
v-model="queryParams.productModel"
placeholder="请输入产品型号"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
@ -157,15 +142,12 @@ export default {
buyerName: null,
vendorName: null,
ownerName: null,
productCode: null,
productModel: null,
approveStatus: '1',
params:{
applyTimeStart: null,
applyTimeEnd: null,
},
orderByColumn: 'applyTime',
isAsc: 'desc'
}
},
//
dateRangeApplyTime: [],

View File

@ -23,22 +23,7 @@
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
<el-form-item label="产品编码" prop="productCode">
<el-input
v-model="queryParams.productCode"
placeholder="请输入产品编码"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="产品型号" prop="productModel">
<el-input
v-model="queryParams.productModel"
placeholder="请输入产品型号"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
@ -149,16 +134,12 @@ export default {
purchaseNo: null,
buyerName: null,
vendorName: null,
productCode: null,
productModel: null,
ownerName: null,
approveStatus: '3', // 3
params:{
applyTimeStart: null,
applyTimeEnd: null,
},
orderByColumn: 'applyTime',
isAsc: 'desc'
}
},
//
dateRangeApplyTime: [],

View File

@ -1,143 +0,0 @@
<template>
<el-drawer
title="报价单详情"
:visible.sync="visible"
direction="rtl"
size="80%"
:before-close="handleClose"
append-to-body
>
<div class="detail-container" v-loading="loading">
<!-- Basic Info -->
<el-divider content-position="left">基本信息</el-divider>
<el-descriptions :column="2" border size="medium">
<el-descriptions-item label="报价单号">{{ form.quotationCode }}</el-descriptions-item>
<el-descriptions-item label="报价单名称">{{ form.quotationName }}</el-descriptions-item>
<el-descriptions-item label="币种">
<dict-tag :options="dict.type.currency_type" :value="form.amountType"/>
</el-descriptions-item>
<el-descriptions-item label="代表处">{{ agentName }}</el-descriptions-item>
<el-descriptions-item label="客户名称">{{ form.customerName }}</el-descriptions-item>
<el-descriptions-item label="状态">{{ form.quotationStatus }}</el-descriptions-item>
<el-descriptions-item label="创建时间">{{ parseTime(form.createTime) }}</el-descriptions-item>
<el-descriptions-item label="备注" :span="2">{{ form.remark }}</el-descriptions-item>
</el-descriptions>
<div v-if="form.quotationCode || catalogueTotalPrice > 0 || discountedTotalPrice > 0" style="margin-top: 20px;">
<el-row type="flex" justify="space-between" style="margin-bottom: 20px; font-size: 14px;">
<el-col :span="24" style="text-align: right;">
<span v-if="catalogueTotalPrice > 0" style="margin-right: 20px;">
<span style="font-weight: bold;">目录总价</span>{{ formatAmount(catalogueTotalPrice) }}
</span>
<span v-if="discountedTotalPrice > 0">
<span style="font-weight: bold;">折后总价</span>{{ formatAmount(discountedTotalPrice) }}
</span>
</el-col>
</el-row>
</div>
<!-- Config Info -->
<product-config :value="form" readonly />
</div>
</el-drawer>
</template>
<script>
import { getQuotation } from "@/api/base/quotation";
import ProductConfig from "@/views/project/info/ProductConfig";
export default {
name: "QuotationDetail",
components: { ProductConfig },
dicts: ['currency_type'],
props: {
agentOptions: {
type: Array,
default: () => []
}
},
data() {
return {
visible: false,
loading: false,
form: {
softwareProjectProductInfoList: [],
hardwareProjectProductInfoList: [],
maintenanceProjectProductInfoList: []
}
};
},
computed: {
agentName() {
if (!this.form.agentCode || !this.agentOptions) return this.form.agentCode;
const agent = this.agentOptions.find(item => item.agentCode === this.form.agentCode);
return agent ? agent.agentName : this.form.agentCode;
},
catalogueTotalPrice() {
let total = 0;
const lists = [
this.form.softwareProjectProductInfoList,
this.form.hardwareProjectProductInfoList,
this.form.maintenanceProjectProductInfoList
];
lists.forEach(list => {
if (list && list.length > 0) {
list.forEach(item => {
total += Number(item.catalogueAllPrice) || 0;
});
}
});
return total;
},
discountedTotalPrice() {
let total = 0;
const lists = [
this.form.softwareProjectProductInfoList,
this.form.hardwareProjectProductInfoList,
this.form.maintenanceProjectProductInfoList
];
lists.forEach(list => {
if (list && list.length > 0) {
list.forEach(item => {
total += Number(item.allPrice) || 0;
});
}
});
return total;
}
},
methods: {
open(id) {
this.visible = true;
this.getDetail(id);
},
getDetail(id) {
this.loading = true;
getQuotation(id).then(response => {
this.form = response.data;
// Ensure lists are arrays
this.form.softwareProjectProductInfoList = this.form.softwareProjectProductInfoList || [];
this.form.hardwareProjectProductInfoList = this.form.hardwareProjectProductInfoList || [];
this.form.maintenanceProjectProductInfoList = this.form.maintenanceProjectProductInfoList || [];
this.loading = false;
});
},
handleClose(done) {
this.visible = false;
if (done) done();
},
formatAmount(value) {
if (value === null || value === undefined) return '';
return Number(value).toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
},
}
};
</script>
<style scoped>
.detail-container {
padding: 20px;
}
</style>

View File

@ -1,602 +0,0 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="报价单号" prop="quotationCode">
<el-input
v-model="queryParams.quotationCode"
placeholder="请输入报价单号"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="报价单" prop="quotationName">
<el-input
v-model="queryParams.quotationName"
placeholder="请输入报价单名称"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="状态" prop="quotationStatus">
<el-input
v-model="queryParams.quotationStatus"
placeholder="请输入状态"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="创建时间">
<el-date-picker
v-model="dateRange"
style="width: 240px"
value-format="yyyy-MM-dd HH:mm:ss"
type="datetimerange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-plus"
size="mini"
@click="handleAdd"
v-hasPermi="['sip:quotation:add']"
>新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="el-icon-edit"
size="mini"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['sip:quotation:update']"
>修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['sip:quotation:delete']"
>删除</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="quotationList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="报价单号" align="center" prop="quotationCode" />
<el-table-column label="报价单" align="center" prop="quotationName" />
<el-table-column label="项目编号" align="center" prop="projectCode" />
<!-- <el-table-column label="报价金额" align="center" prop="quotationAmount" />-->
<el-table-column label="报价金额(¥)" align="center" prop="discountAmount" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)" />
<el-table-column label="状态" align="center" prop="quotationStatus" >
<template slot-scope="scope">
<dict-tag :options="dict.type.quotation_status" :value="scope.row.quotationStatus"/>
</template>
</el-table-column>
<el-table-column label="创建人" align="center" prop="createByName" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-view"
@click="handleDetail(scope.row)"
>详情</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['sip:quotation:update']"
>修改</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-document-copy"
@click="handleCopy(scope.row)"
v-hasPermi="['sip:quotation:add']"
>复制创建</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
v-hasPermi="['sip:quotation:delete']"
>删除</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-download"
@click="handleExport(scope.row)"
v-hasPermi="['sip:quotation:export']"
>导出</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<!-- 添加或修改报价单对话框 -->
<el-dialog :title="title" :visible.sync="open" width="80%" append-to-body :close-on-click-modal="false" @close="cancel">
<el-steps :active="activeStep" simple finish-status="success" style="margin-bottom: 20px; cursor: pointer;">
<el-step icon="el-icon-caret-left" @click.native="handleStepClick(0)">
<template #title>
<i class="el-icon-caret-left"></i>
报价单列表
</template>
</el-step>
<el-step title="设置基本信息" @click.native="handleStepClick(1)"></el-step>
<el-step title="配置信息" @click.native="handleStepClick(2)"></el-step>
</el-steps>
<div v-if="form.quotationCode || catalogueTotalPrice > 0 || discountedTotalPrice > 0">
<el-divider></el-divider>
<el-row type="flex" justify="space-between" style="margin-bottom: 20px; font-size: 14px;">
<el-col :span="8">
<span v-if="form.quotationCode">
<span style="font-weight: bold;">报价单号</span>{{ form.quotationCode }}
</span>
</el-col>
<el-col :span="16" style="text-align: right;">
<span v-if="catalogueTotalPrice > 0" style="margin-right: 20px;">
<span style="font-weight: bold;">目录总价</span>{{ formatAmount(catalogueTotalPrice) }}
</span>
<span v-if="discountedTotalPrice > 0">
<span style="font-weight: bold;">折后总价</span>{{ formatAmount(discountedTotalPrice) }}
</span>
</el-col>
</el-row>
<el-divider></el-divider>
</div>
<el-form ref="form" :model="form" :rules="rules" label-width="100px" style="max-height: 50vh;overflow-y: auto;padding: 10px">
<!-- Step 1 Content: Basic Info -->
<div v-show="activeStep === 1">
<el-row>
<el-col :span="12">
<el-form-item label="报价单名称" prop="quotationName">
<el-input v-model="form.quotationName" placeholder="请输入报价单名称" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="币种" prop="amountType">
<el-select v-model="form.amountType" placeholder="请选择币种" style="width: 100%">
<el-option
v-for="dict in dict.type.currency_type"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="代表处" prop="agentCode">
<el-select v-model="form.agentCode" placeholder="请选择代表处" style="width: 100%">
<el-option
v-for="item in agentOptions"
:key="item.agentCode"
:label="item.agentName"
:value="item.agentCode"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="客户名称" prop="customerName">
<el-input v-model="form.customerName" placeholder="请输入客户名称" />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="报价单备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
</el-form-item>
</div>
<!-- Step 2 Content: Config Info -->
<div v-show="activeStep === 2">
<product-config :value="form" @input="updateProductConfig" />
</div>
</el-form>
<div slot="footer" class="dialog-footer">
<div v-if="activeStep === 1">
<el-button @click="cancel"> </el-button>
<el-button type="primary" @click="nextStep"></el-button>
</div>
<div v-if="activeStep === 2">
<el-button @click="prevStep"></el-button>
<el-button type="primary" @click="submitForm"> </el-button>
</div>
</div>
</el-dialog>
<quotation-detail ref="detail" :agent-options="agentOptions" />
</div>
</template>
<script>
import { listQuotation, getQuotation, delQuotation, addQuotation, updateQuotation,exportSingleQuotation } from "@/api/base/quotation";
import { listAgent } from "@/api/system/agent";
import ProductConfig from "@/views/project/info/ProductConfig";
import QuotationDetail from "./detail";
import {isEmpty} from "@/utils/validate";
export default {
name: "Quotation",
components: {
ProductConfig,
QuotationDetail
},
dicts: ['currency_type','quotation_status'],
data() {
return {
//
activeStep: 1,
//
loading: true,
//
ids: [],
//
single: true,
//
multiple: true,
//
showSearch: true,
//
total: 0,
//
quotationList: [],
//
dateRange: [],
//
agentOptions: [],
//
title: "",
//
open: false,
//
queryParams: {
pageNum: 1,
pageSize: 10,
quotationCode: null,
quotationName: null,
projectCode: null,
quotationStatus: null,
createTimeStart: null,
createTimeEnd: null,
isAsc : 'desc',
orderByColumn : 'createTime',
},
//
form: {
softwareProjectProductInfoList: [],
hardwareProjectProductInfoList: [],
maintenanceProjectProductInfoList: []
},
//
rules: {
quotationName: [
{ required: true, message: "报价单名称不能为空", trigger: "blur" }
],
amountType: [
{ required: true, message: "币种不能为空", trigger: "change" }
],
agentCode: [
{ required: true, message: "代表处不能为空", trigger: "change" }
],
customerName: [
{ required: true, message: "客户名称不能为空", trigger: "blur" }
],
}
};
},
computed: {
catalogueTotalPrice() {
let total = 0;
const lists = [
this.form.softwareProjectProductInfoList,
this.form.hardwareProjectProductInfoList,
this.form.maintenanceProjectProductInfoList
];
lists.forEach(list => {
if (list && list.length > 0) {
list.forEach(item => {
total += Number(item.catalogueAllPrice) || 0;
});
}
});
return total;
},
discountedTotalPrice() {
let total = 0;
const lists = [
this.form.softwareProjectProductInfoList,
this.form.hardwareProjectProductInfoList,
this.form.maintenanceProjectProductInfoList
];
lists.forEach(list => {
if (list && list.length > 0) {
list.forEach(item => {
total += Number(item.allPrice) || 0;
});
}
});
return total;
}
},
created() {
this.getList();
this.getAgentList();
},
methods: {
/** 查询报价单列表 */
getList() {
this.loading = true;
if (null != this.dateRange && '' != this.dateRange) {
this.queryParams.createTimeStart = this.dateRange[0];
this.queryParams.createTimeEnd = this.dateRange[1];
}
listQuotation(this.queryParams).then(response => {
this.quotationList = response.rows;
this.total = response.total;
this.loading = false;
});
},
handleDetail(row) {
this.$refs.detail.open(row.id);
},
handleExport(row){
this.$modal.confirm('是否确认导出已审批的采购数据项?').then(() => {
return exportSingleQuotation(row.id);
}).then(response => {
this.$download.download( response.msg)
})
},
/** 查询代表处列表 */
getAgentList() {
listAgent().then(response => {
this.agentOptions = response.rows;
});
},
//
cancel() {
this.open = false;
this.reset();
this.activeStep = 1;
},
//
reset() {
this.form = {
id: null,
quotationCode: null,
quotationName: null,
projectCode: null,
quotationAmount: null,
discountAmount: null,
quotationStatus: null,
createBy: null,
createTime: null,
updateBy: null,
updateTime: null,
remark: null,
agentCode: null,
amountType: null,
customerName: null,
softwareProjectProductInfoList: [],
hardwareProjectProductInfoList: [],
maintenanceProjectProductInfoList: []
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.dateRange = [];
this.resetForm("queryForm");
this.queryParams.createTimeStart = null;
this.queryParams.createTimeEnd = null;
this.handleQuery();
},
//
handleSelectionChange(selection) {
this.ids = selection.map(item => item.id)
this.single = selection.length!==1
this.multiple = !selection.length
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.activeStep = 1;
this.title = "添加报价单";
},
/** 复制创建 */
handleCopy(row) {
this.reset();
const id = row.id || this.ids
getQuotation(id).then(response => {
this.form = response.data;
// Reset ID and Code for new creation
this.form.id = null;
this.form.quotationCode = null;
this.form.quotationStatus = null;
this.form.createTime = null;
this.form.updateTime = null;
this.form.createBy = null;
this.form.updateBy = null;
// Ensure product lists are initialized if null
this.form.softwareProjectProductInfoList = this.form.softwareProjectProductInfoList || [];
this.form.hardwareProjectProductInfoList = this.form.hardwareProjectProductInfoList || [];
this.form.maintenanceProjectProductInfoList = this.form.maintenanceProjectProductInfoList || [];
// Clear IDs in configuration lists to ensure they are created as new records
const clearIds = (list) => {
if (list && list.length > 0) {
list.forEach(item => {
item.id = null;
item.quotationId = null;
});
}
};
clearIds(this.form.softwareProjectProductInfoList);
clearIds(this.form.hardwareProjectProductInfoList);
clearIds(this.form.maintenanceProjectProductInfoList);
this.open = true;
this.activeStep = 1;
this.title = "复制创建报价单";
});
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const id = row.id || this.ids
getQuotation(id).then(response => {
this.form = response.data;
// Ensure product lists are initialized if null
this.form.softwareProjectProductInfoList = this.form.softwareProjectProductInfoList || [];
this.form.hardwareProjectProductInfoList = this.form.hardwareProjectProductInfoList || [];
this.form.maintenanceProjectProductInfoList = this.form.maintenanceProjectProductInfoList || [];
this.open = true;
this.activeStep = 1;
this.title = "修改报价单";
});
},
/** 处理步骤点击 */
handleStepClick(stepIndex) {
if (stepIndex === 0) {
this.cancel();
} else if (stepIndex === 1) {
this.activeStep = 1;
} else if (stepIndex === 2) {
// Validate step 1 before going to step 2
this.$refs["form"].validateField(['quotationName', 'amountType', 'agentCode', 'customerName'], (errorMessage) => {
if (!errorMessage) {
this.activeStep = 2;
}
});
}
},
/** 下一步 */
nextStep() {
// Step 1
this.$refs["form"].validateField(['quotationName', 'amountType', 'agentCode', 'customerName'], (errorMessage) => {
if (!errorMessage) {
this.activeStep = 2;
}
});
},
/** 上一步 */
prevStep() {
this.activeStep = 1;
},
updateProductConfig(data) {
this.form.softwareProjectProductInfoList = data.softwareProjectProductInfoList;
this.form.hardwareProjectProductInfoList = data.hardwareProjectProductInfoList;
this.form.maintenanceProjectProductInfoList = data.maintenanceProjectProductInfoList;
},
formatAmount(value) {
if (value === null || value === undefined) return '';
return Number(value).toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (valid) {
const checkProduct=(list)=>isEmpty(list) ||( !isEmpty(list) && list.every(item => item.productBomCode!==''))
if (!checkProduct(this.form.softwareProjectProductInfoList) || !checkProduct(this.form.hardwareProjectProductInfoList) || !checkProduct(this.form.maintenanceProjectProductInfoList)) {
this.$modal.msgError("请完善产品信息");
return;
}
this.form.quotationAmount = this.catalogueTotalPrice;
this.form.discountAmount = this.discountedTotalPrice;
if (this.form.id != null) {
updateQuotation(this.form).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
} else {
addQuotation(this.form).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
}
}
});
},
/** 删除按钮操作 */
handleDelete(row) {
const ids = row.id || this.ids;
this.$modal.confirm('是否确认删除报价单?').then(function() {
return delQuotation(ids);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
}
}
};
</script>
<style scoped>
/* 只有第一步显示自定义图标,覆盖完成状态的对勾 */
::v-deep .el-step:first-child .el-step__icon-inner.is-status {
display: none;
}
::v-deep .el-step:first-child .el-step__icon-inner:not(.is-status) {
display: inline-block;
}
/* 当前激活步骤标题颜色鲜明 */
::v-deep .el-step__title.is-process {
color: #1890ff;
font-weight: 700;
}
/* 当前激活步骤图标颜色鲜明 */
::v-deep .el-step__head.is-process {
color: #1890ff;
border-color: #1890ff;
}
</style>

View File

@ -1,126 +0,0 @@
<template>
<el-dialog title="选择报价单" :visible.sync="visible" :close-on-click-modal="false" width="900px" append-to-body @close="handleClose">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" @submit.native.prevent>
<el-form-item label="报价单号" prop="quotationCode">
<el-input
v-model="queryParams.quotationCode"
placeholder="请输入报价单号"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="报价单名称" prop="quotationName">
<el-input
v-model="queryParams.quotationName"
placeholder="请输入报价单名称"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
<el-table v-loading="loading" :data="quotationList" @row-click="handleRowClick" highlight-current-row>
<el-table-column label="报价单号" align="center" prop="quotationCode" />
<el-table-column label="报价单名称" align="center" prop="quotationName" />
<el-table-column label="项目编号" align="center" prop="projectCode" />
<el-table-column label="报价金额" align="center" prop="discountAmount" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<div slot="footer" class="dialog-footer">
<el-button @click="handleClose"> </el-button>
</div>
</el-dialog>
</template>
<script>
import { listQuotation } from "@/api/base/quotation";
export default {
name: "SelectQuotation",
props: {
visible: {
type: Boolean,
default: false,
},
createBy: {
type: String,
default: "-1",
},
},
data() {
return {
//
loading: true,
//
total: 0,
//
quotationList: [],
//
queryParams: {
pageNum: 1,
pageSize: 10,
// createBy: this.createBy,
quotationCode: null,
quotationName: null,
orderByColumn: 'createTime',
isAsc: 'desc'
},
};
},
watch: {
visible(val) {
if (val) {
// this.queryParams.createBy = this.createBy||"-1";
this.getList();
}
},
},
methods: {
/** 查询报价单列表 */
getList() {
this.loading = true;
listQuotation(this.queryParams).then(response => {
this.quotationList = response.rows;
this.total = response.total;
this.loading = false;
});
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 行点击事件 */
handleRowClick(row) {
this.$emit("quotation-selected", row);
this.handleClose();
},
/** 关闭按钮 */
handleClose() {
this.$emit("update:visible", false);
},
},
};
</script>

View File

@ -9,17 +9,17 @@
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="制造商名称" prop="vendorAddress">
<el-form-item label="制造商名称" prop="vendorName">
<el-input
v-model="queryParams.vendorAddress"
v-model="queryParams.vendorName"
placeholder="请输入制造商名称"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="制造商全称" prop="vendorName">
<el-form-item label="制造商全称" prop="vendorAddress">
<el-input
v-model="queryParams.vendorName"
v-model="queryParams.vendorAddress"
placeholder="请输入制造商全称"
clearable
@keyup.enter.native="handleQuery"
@ -98,8 +98,8 @@
<el-table v-loading="loading" :data="vendorList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="制造商编码" align="center" prop="vendorCode" />
<el-table-column label="制造商名称" align="center" prop="vendorAddress" />
<el-table-column label="制造商全称" align="center" prop="vendorName" />
<el-table-column label="制造商名称" align="center" prop="vendorName" />
<el-table-column label="制造商全称" align="center" prop="vendorAddress" />
<el-table-column label="默认仓库" align="center" prop="warehouseName" />
<el-table-column label="联系人" align="center" prop="vendorUser" />
<el-table-column label="联系邮箱" align="center" prop="vendorEmail" />
@ -147,13 +147,13 @@
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="制造商名称" prop="vendorAddress">
<el-input v-model="form.vendorAddress" placeholder="请输入制造商名称" />
<el-form-item label="制造商名称" prop="vendorName">
<el-input v-model="form.vendorName" placeholder="请输入制造商名称" />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="制造商全称" prop="vendorName">
<el-input v-model="form.vendorName" placeholder="请输入制造商全称" />
<el-form-item label="制造商全称" prop="vendorAddress">
<el-input v-model="form.vendorAddress" placeholder="请输入制造商全称" />
</el-form-item>
<el-row>
<el-col :span="12">
@ -301,8 +301,6 @@ export default {
loading: true,
//
ids: [],
//
selectedVendorCodes: [],
//
single: true,
//
@ -332,8 +330,6 @@ export default {
vendorAddress: null,
vendorUser: null,
vendorStatus: null,
isAsc : 'desc',
orderByColumn : 'createTime',
},
//
form: {
@ -456,7 +452,6 @@ export default {
//
handleSelectionChange(selection) {
this.ids = selection.map(item => item.vendorId)
this.selectedVendorCodes = selection.map(item => item.vendorCode)
this.single = selection.length!==1
this.multiple = !selection.length
},
@ -529,12 +524,8 @@ export default {
},
/** 导出按钮操作 */
handleExport() {
const queryParams = {
...this.queryParams,
vendorCodeList: this.selectedVendorCodes.length ? this.selectedVendorCodes : undefined
};
const exportTip = this.selectedVendorCodes.length ? '是否确认导出选中的制造商信息数据项?' : '是否确认导出所有制造商信息数据项?';
this.$modal.confirm(exportTip).then(() => {
const queryParams = this.queryParams;
this.$modal.confirm('是否确认导出所有制造商信息数据项?').then(() => {
this.exportLoading = true;
return exportVendor(queryParams);
}).then(response => {

View File

@ -1,76 +0,0 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="80px">
<el-form-item label="SN码" prop="productSn">
<el-input
v-model="queryParams.productSn"
placeholder="请输入SN码"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="inventoryInfoList">
<el-table-column label="SN码" align="center" prop="productSn" />
<el-table-column label="入库单号" align="center" prop="innerCode" />
<el-table-column label="出库单号" align="center" prop="outerCode" />
<el-table-column label="订单编号" align="center" prop="orderCode" />
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
</div>
</template>
<script>
import { listInventoryInfoByProductSn } from '@/api/dataProcess/inventoryInfo'
export default {
name: 'DataProcessInventoryInfo',
data() {
return {
loading: false,
showSearch: true,
total: 0,
inventoryInfoList: [],
queryParams: {
pageNum: 1,
pageSize: 10,
productSn: null
}
}
},
methods: {
getList() {
this.loading = true
listInventoryInfoByProductSn(this.queryParams).then(response => {
this.inventoryInfoList = response.rows
this.total = response.total
this.loading = false
})
},
handleQuery() {
this.queryParams.pageNum = 1
this.getList()
},
resetQuery() {
this.resetForm('queryForm')
this.handleQuery()
}
}
}
</script>

View File

@ -1,301 +0,0 @@
<template xmlns="http://www.w3.org/1999/html">
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="100px">
<el-form-item label="项目编号" prop="projectCode">
<el-input
v-model="queryParams.projectCode"
placeholder="请输入项目编号"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="项目名称" prop="projectName">
<el-input
v-model="queryParams.projectName"
placeholder="请输入项目名称"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="代表处" prop="agentName">
<el-input
v-model="queryParams.agentName"
placeholder="请输入代表处"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="汇智负责人" prop="hzSupportUserName">
<el-input
v-model="queryParams.hzSupportUserName"
placeholder="请输入汇智负责人"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<br/>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-edit"
size="mini"
@click="handleTransfer"
v-hasPermi="['sip:project:add']"
>转移</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="projectList" @selection-change="handleSelectionChange" @sort-change="handleSortChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="项目编号" align="center" prop="projectCode" width="100" />
<el-table-column label="项目名称" align="center" prop="projectName" width="300">
<template slot-scope="scope">
<span>{{ scope.row.projectName }}</span>
</template>
</el-table-column>
<el-table-column label="最终客户" align="center" prop="customerName" width="200" />
<el-table-column label="BG" align="center" prop="bgProperty" width="70">
<template slot-scope="scope">
<dict-tag :options="dict.type.bg_type" :value="scope.row.bgProperty"/>
</template>
</el-table-column>
<el-table-column label="行业" align="center" prop="industryType" width="70">
<template slot-scope="scope">
<dict-tag v-if="scope.row.bgProperty === 'YYS'" :options="dict.type.bg_yys" :value="scope.row.industryType"/>
<dict-tag v-else :options="dict.type.bg_hysy" :value="scope.row.industryType"/>
</template>
</el-table-column>
<el-table-column label="代表处" align="center" prop="agentName" width="70" />
<el-table-column label="项目把握度" align="center" prop="projectGraspDegree" width="70" />
<el-table-column label="预计金额(元)" align="center" prop="estimatedAmount" width="120" sortable="custom" :sort-orders="['descending', 'ascending']">
<template slot-scope="scope">
<span>{{ formatAmountNumber(scope.row.estimatedAmount) }}</span>
</template>
</el-table-column>
<el-table-column label="项目阶段" align="center" prop="projectStage" width="160">
<template slot-scope="scope">
<dict-tag :options="dict.type.project_stage" :value="scope.row.projectStage"/>
</template>
</el-table-column>
<el-table-column label="建设类型" align="center" prop="constructionType" width="120" />
<el-table-column label="汇智负责人" align="center" prop="hzSupportUserName" width="100" />
<el-table-column label="POC" align="center" prop="poc" width="60">
<template slot-scope="scope">
<span>{{ scope.row.poc === '1' ? '是' : '否' }}</span>
</template>
</el-table-column>
<el-table-column label="预计下单时间" align="center" prop="estimatedOrderTime" width="140" sortable="custom" :sort-orders="['descending', 'ascending']">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.estimatedOrderTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="更新时间" align="center" prop="lastWorkUpdateTime" width="160" sortable="custom" :sort-orders="['descending', 'ascending']">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.lastWorkUpdateTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<el-table-column label="最后操作时间" align="center" prop="updateTime" width="160" sortable="custom" :sort-orders="['descending', 'ascending']">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.updateTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<el-dialog title="项目转移" :visible.sync="transferDialogVisible" width="500px" append-to-body>
<el-form ref="transferForm" :model="transferForm" :rules="transferRules" label-width="100px">
<el-form-item label="原汇智负责人" label-width="130px">
<el-input
v-model="transferForm.originalHzSupportUserName"
placeholder="原汇智负责人"
readonly
/>
</el-form-item>
<el-form-item label="新汇智负责人" prop="hzSupportUserName" label-width="130px">
<el-input
v-model="transferForm.hzSupportUserName"
placeholder="请选择新汇智负责人"
readonly
@click.native="openTransferUserSelect"
/>
<input type="hidden" v-model="transferForm.hzSupportUser" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="transferDialogVisible = false"> </el-button>
<el-button type="primary" @click="submitTransfer"> </el-button>
</div>
</el-dialog>
<select-user :visible.sync="selectUserVisible" @user-selected="handleUserSelected" />
</div>
</template>
<script>
import {listProject, transferProject} from "@/api/dataProcess/projectTransfer";
import SelectUser from "@/views/system/user/selectUser.vue";
export default {
name: "Project",
components: {
SelectUser
},
dicts: ['bg_type', 'bg_yys', 'bg_hysy', 'project_stage'],
data() {
return {
//
loading: true,
//
ids: [],
//
selectedRows: [],
//
showSearch: true,
//
total: 0,
//
projectList: [],
//
queryParams: {
pageNum: 1,
pageSize: 10,
projectCode: null,
projectName: null,
agentName: null,
hzSupportUserName: null,
isAsc: 'desc',
orderByColumn: 'createTime',
},
selectUserVisible: false,
transferDialogVisible: false,
transferForm: {
originalHzSupportUser: null,
originalHzSupportUserName: null,
hzSupportUser: null,
hzSupportUserName: null,
projectIdList: []
},
transferRules: {
hzSupportUserName: [{ required: true, message: "请选择汇智负责人", trigger: "blur" }]
}
};
},
created() {
this.getList();
},
methods: {
handleSortChange(column) {
this.queryParams.orderByColumn = column.prop;
if (column.order === 'ascending') {
this.queryParams.isAsc = 'asc';
} else if (column.order === 'descending') {
this.queryParams.isAsc = 'desc';
} else {
this.queryParams.isAsc = null;
this.queryParams.orderByColumn = null;
}
this.getList();
},
/** 查询项目管理列表 */
getList() {
this.loading = true;
listProject(this.queryParams).then(response => {
this.projectList = response.rows;
this.total = response.total;
this.loading = false;
});
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
//
handleSelectionChange(selection) {
this.ids = selection.map(item => item.id)
this.selectedRows = selection
},
handleTransfer() {
if (!this.selectedRows || this.selectedRows.length === 0) {
this.$modal.msgWarning("请至少选择一条数据");
return;
}
const hasEmptyHzSupportUser = this.selectedRows.some(item => !item.hzSupportUser);
if (hasEmptyHzSupportUser) {
this.$modal.msgWarning("选中数据存在无汇智负责人的项目,无法转移");
return;
}
const currentHzSupportUser = this.selectedRows[0].hzSupportUser;
const isSameHzSupportUser = this.selectedRows.every(item => item.hzSupportUser === currentHzSupportUser);
if (!isSameHzSupportUser) {
this.$modal.msgWarning("请选择同一个汇智负责人的项目进行转移");
return;
}
this.transferForm = {
originalHzSupportUser: currentHzSupportUser,
originalHzSupportUserName: this.selectedRows[0].hzSupportUserName,
hzSupportUser: null,
hzSupportUserName: null,
projectIdList: [...this.ids]
};
this.transferDialogVisible = true;
},
openTransferUserSelect() {
this.selectUserVisible = true;
},
handleUserSelected(user) {
this.transferForm.hzSupportUser = user.userId;
this.transferForm.hzSupportUserName = user.userName;
},
submitTransfer() {
this.$refs["transferForm"].validate(valid => {
if (!valid) {
return;
}
const params = {
originalHzSupportUser: this.transferForm.originalHzSupportUser,
originalHzSupportUserName: this.transferForm.originalHzSupportUserName,
hzSupportUser: this.transferForm.hzSupportUser,
hzSupportUserName: this.transferForm.hzSupportUserName,
projectIdList: this.transferForm.projectIdList
};
transferProject(params).then(() => {
this.$modal.msgSuccess("提交成功");
this.transferDialogVisible = false;
this.getList();
});
});
},
/** 格式化金额 */
formatAmountNumber(value) {
if (value) {
return Number(value).toLocaleString('en-US');
}
return '';
}
}
};
</script>

View File

@ -1,310 +0,0 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="100px">
<el-form-item label="项目编号" prop="projectCode">
<el-input
v-model="queryParams.projectCode"
placeholder="请输入项目编号"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="项目名称" prop="projectName">
<el-input
v-model="queryParams.projectName"
placeholder="请输入项目名称"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="进货商" prop="partnerName">
<el-input
v-model="queryParams.partnerName"
placeholder="请输入进货商"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<!-- <el-col :span="1.5">-->
<!-- <el-button-->
<!-- type="danger"-->
<!-- plain-->
<!-- icon="el-icon-delete"-->
<!-- size="mini"-->
<!-- :disabled="multiple"-->
<!-- @click="handleDelete"-->
<!-- v-hasPermi="['finance:charge:remove']"-->
<!-- >删除</el-button>-->
<!-- </el-col>-->
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-download"
size="mini"
@click="handleExport"
v-hasPermi="['finance:charge:export']"
>导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList" :columns="columns"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="chargeList" @selection-change="handleSelectionChange">
<!-- <el-table-column type="selection" width="55" align="center" />-->
<el-table-column label="项目编码" align="center" prop="projectCode" width="180" v-if="columns.projectCode.visible" key="projectCode"/>
<el-table-column label="项目名称" align="center" prop="projectName" width="240" v-if="columns.projectName.visible" key="projectName"/>
<el-table-column label="合同编号" align="center" prop="orderCode" width="180" v-if="columns.orderCode.visible" key="orderCode"/>
<el-table-column label="下单通路" align="center" prop="orderChannel" width="100" v-if="columns.orderChannel.visible" key="orderChannel">
<template slot-scope="scope">
<el-tag>{{ scope.row.orderChannel==='1'? "总代":"直签" }}</el-tag>
</template>
</el-table-column>
<el-table-column label="供货商" align="center" prop="supplier" width="180" v-if="columns.supplier.visible" key="supplier"/>
<el-table-column label="进货商" align="center" prop="partnerName" width="180" v-if="columns.partnerName.visible" key="partnerName"/>
<el-table-column label="业务侧可计收时间" align="center" prop="bizChargeDate" width="180" v-if="columns.bizChargeDate.visible" key="bizChargeDate">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.bizChargeDate, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="财务计收时间" align="center" prop="financeChargeDate" width="180" v-if="columns.financeChargeDate.visible" key="financeChargeDate">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.financeChargeDate, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="收入" align="center" v-if="columns.incomeWithTaxTotal.visible || columns.incomeWithoutTaxTotal.visible || columns.incomeTax.visible">
<el-table-column label="含税总价(元)" align="center" prop="incomeWithTaxTotal" width="180" v-if="columns.incomeWithTaxTotal.visible" key="incomeWithTaxTotal" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
<el-table-column label="未含税总价(元)" align="center" prop="incomeWithoutTaxTotal" width="180" v-if="columns.incomeWithoutTaxTotal.visible" key="incomeWithoutTaxTotal" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
<el-table-column label="税额(元)" align="center" prop="incomeTax" width="180" v-if="columns.incomeTax.visible" key="incomeTax">
<template slot-scope="scope">
<span>{{ formatCurrency($calc.sub(scope.row.incomeWithTaxTotal, scope.row.incomeWithoutTaxTotal)) }}</span>
</template>
</el-table-column>
</el-table-column>
<el-table-column label="成本" align="center" v-if="columns.costSoftwareWithoutTax.visible || columns.costHardwareWithoutTax.visible || columns.costSoftwareMaintWithoutTax.visible || columns.costHardwareMaintWithoutTax.visible || columns.costProvinceServiceWithoutTax.visible || columns.costOtherWithoutTax.visible || columns.allCostWithoutTax.visible">
<el-table-column label="软件折后未税小计" align="center" prop="costSoftwareWithoutTax" width="180" v-if="columns.costSoftwareWithoutTax.visible" key="costSoftwareWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
<el-table-column label="硬件折后未税小计" align="center" prop="costHardwareWithoutTax" width="180" v-if="columns.costHardwareWithoutTax.visible" key="costHardwareWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
<el-table-column label="软件维保折后未税小计" align="center" prop="costSoftwareMaintWithoutTax" width="180" v-if="columns.costSoftwareMaintWithoutTax.visible" key="costSoftwareMaintWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
<el-table-column label="硬件维保折后未税小计" align="center" prop="costHardwareMaintWithoutTax" width="180" v-if="columns.costHardwareMaintWithoutTax.visible" key="costHardwareMaintWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
<el-table-column label="省代服务折后未税小计" align="center" prop="costProvinceServiceWithoutTax" width="180" v-if="columns.costProvinceServiceWithoutTax.visible" key="costProvinceServiceWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
<el-table-column label="其它折后未税小计" align="center" prop="costOtherWithoutTax" width="180" v-if="columns.costOtherWithoutTax.visible" key="costOtherWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
<el-table-column label="成本未税合计" align="center" prop="allCostWithoutTax" width="180" v-if="columns.allCostWithoutTax.visible" key="allCostWithoutTax" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)">
</el-table-column>
</el-table-column>
<el-table-column label="毛利" align="center" prop="grossProfit" width="180" v-if="columns.grossProfit.visible" key="grossProfit" :formatter="(row, column, cellValue)=>formatCurrency(cellValue)"/>
<el-table-column label="毛利率" align="center" prop="grossProfitRate" width="180" v-if="columns.grossProfitRate.visible" key="grossProfitRate"/>
<el-table-column label="备注" align="center" prop="remark" width="180" v-if="columns.remark.visible" key="remark"/>
<el-table-column label="操作" align="center" width="180" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleRemark(scope.row)"
v-hasPermi="['finance:charge:edit']"
>编辑备注</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-refresh-left"
@click="handleRevoke(scope.row)"
v-hasPermi="['finance:charge:remove']"
>撤销</el-button>
<!-- <el-button-->
<!-- size="mini"-->
<!-- type="text"-->
<!-- icon="el-icon-delete"-->
<!-- @click="handleDelete(scope.row)"-->
<!-- v-hasPermi="['finance:charge:remove']"-->
<!-- >删除</el-button>-->
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<!-- 编辑备注对话框 -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" label-width="80px">
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入备注内容" :rows="4" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { listCharge, getCharge, delCharge, updateCharge, revokeCharge } from "@/api/finance/charge";
export default {
name: "Charge",
dicts: ['finance_charge_status'],
data() {
return {
//
loading: true,
//
ids: [],
//
single: true,
//
multiple: true,
//
showSearch: true,
//
total: 0,
//
chargeList: [],
//
title: "",
//
open: false,
//
queryParams: {
pageNum: 1,
pageSize: 10,
projectCode: null,
projectName: null,
partnerName: null,
orderByColumn:'createTime',
isAsc: 'desc'
},
//
columns: {
projectCode: { label: '项目编码', visible: true },
projectName: { label: '项目名称', visible: true },
orderCode: { label: '合同编号', visible: true },
orderChannel: { label: '下单通路', visible: true },
supplier: { label: '供货商', visible: true },
partnerName: { label: '进货商', visible: true },
bizChargeDate: { label: '业务侧可计收时间', visible: true },
financeChargeDate: { label: '财务计收时间', visible: true },
incomeWithTaxTotal: { label: '含税总价(元)', visible: true },
incomeWithoutTaxTotal: { label: '未含税总价(元)', visible: true },
incomeTax: { label: '税额(元)', visible: true },
costSoftwareWithoutTax: { label: '软件折后未税小计', visible: true },
costHardwareWithoutTax: { label: '硬件折后未税小计', visible: true },
costSoftwareMaintWithoutTax: { label: '软件维保折后未税小计', visible: true },
costHardwareMaintWithoutTax: { label: '硬件维保折后未税小计', visible: true },
costProvinceServiceWithoutTax: { label: '省代服务折后未税小计', visible: true },
costOtherWithoutTax: { label: '其它折后未税小计', visible: true },
allCostWithoutTax: { label: '成本未税合计', visible: true },
grossProfit: { label: '毛利', visible: true },
grossProfitRate: { label: '毛利率', visible: true },
remark: { label: '备注', visible: true }
},
//
form: {},
};
},
created() {
this.getList();
},
methods: {
/** 查询财务计收列表 */
getList() {
this.loading = true;
listCharge(this.queryParams).then(response => {
this.chargeList = response.rows;
this.total = response.total;
this.loading = false;
});
},
//
cancel() {
this.open = false;
this.reset();
},
//
reset() {
this.form = {
id: null,
remark: null,
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 多选框选中数据 */
handleSelectionChange(selection) {
this.ids = selection.map(item => item.id)
this.single = selection.length!==1
this.multiple = !selection.length
},
/** 编辑备注按钮操作 */
handleRemark(row) {
this.reset();
const id = row.id || this.ids
// 使 getCharge row
getCharge(id).then(response => {
this.form = {
id: response.data.id,
remark: response.data.remark
};
this.open = true;
this.title = "编辑备注";
});
},
/** 撤销按钮操作 */
handleRevoke(row) {
this.$modal.confirm('是否确认撤销该条数据?').then(function() {
return revokeCharge({id: row.id});
}).then(() => {
this.getList();
this.$modal.msgSuccess("撤销成功");
}).catch(() => {});
},
/** 提交按钮 */
submitForm() {
//
updateCharge({id:this.form.id,remark:this.form.remark}).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
},
/** 删除按钮操作 */
handleDelete(row) {
const ids = row.id || this.ids;
this.$modal.confirm('是否确认删除财务计收编号为"' + ids + '"的数据项?').then(function() {
return delCharge(ids);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
},
/** 导出按钮操作 */
handleExport() {
this.download('finance/charge/export', {
...this.queryParams
}, `charge_${new Date().getTime()}.xlsx`)
}
}
};
</script>

Some files were not shown because too many files have changed in this diff Show More