Compare commits
275 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
73cacea552 | |
|
|
a249239e77 | |
|
|
415b9b3e46 | |
|
|
625519c2c2 | |
|
|
6e6d29511c | |
|
|
43fc48a3a5 | |
|
|
87a3901694 | |
|
|
2d5cffb78e | |
|
|
0b693760df | |
|
|
a9ed35793a | |
|
|
0106da0c29 | |
|
|
2c6547f5ec | |
|
|
4e4792459c | |
|
|
aba73af268 | |
|
|
34e9047763 | |
|
|
e3d77525dd | |
|
|
818454311f | |
|
|
4d911c93ae | |
|
|
934b2990f1 | |
|
|
6d4923ac8b | |
|
|
2913740e36 | |
|
|
3883b75b3b | |
|
|
300c1f63b2 | |
|
|
4b2e8538b1 | |
|
|
cabdd4c531 | |
|
|
3d94027618 | |
|
|
972d121580 | |
|
|
239009a59b | |
|
|
4bd4fae390 | |
|
|
40f40baf5e | |
|
|
c10a81f271 | |
|
|
f3f7a99b10 | |
|
|
09fb371292 | |
|
|
61ba3c9680 | |
|
|
d0d6919b11 | |
|
|
9ee56a83a7 | |
|
|
954be37aa4 | |
|
|
79010a0f36 | |
|
|
134ca89b9a | |
|
|
eb1f4fb7b3 | |
|
|
348eb137a8 | |
|
|
c440cb0357 | |
|
|
a5b4446202 | |
|
|
d55fc11e24 | |
|
|
3b84d8d87c | |
|
|
1545771196 | |
|
|
2aaccc1193 | |
|
|
0c451fcbed | |
|
|
3b41b6b3d3 | |
|
|
015ba6da70 | |
|
|
c246975914 | |
|
|
8c2ecc89c8 | |
|
|
5fb2faecdc | |
|
|
671bbd5692 | |
|
|
8acc68141d | |
|
|
71e8eb1e37 | |
|
|
7597a444b9 | |
|
|
d01a1faa1a | |
|
|
c27bfd26cf | |
|
|
11cdcdb83c | |
|
|
802b169818 | |
|
|
341000a3b0 | |
|
|
a1748ed05b | |
|
|
1c601a908e | |
|
|
8bab6d7d17 | |
|
|
02f4c47383 | |
|
|
ea52bec18d | |
|
|
3584c72284 | |
|
|
ee84487fe0 | |
|
|
8aa2b92fa6 | |
|
|
6bb9738dc5 | |
|
|
e992a62a5b | |
|
|
5749d20c80 | |
|
|
a7e4d9c1f4 | |
|
|
f68242d2e7 | |
|
|
98b9097ff2 | |
|
|
475229295a | |
|
|
73a7f6d6ca | |
|
|
2fdbafa0c3 | |
|
|
d9effe68c0 | |
|
|
00f91b394a | |
|
|
1e96076576 | |
|
|
66d315069c | |
|
|
af74c4033d | |
|
|
1aa1e592af | |
|
|
cca2215a60 | |
|
|
626d1f1b09 | |
|
|
d6b41e7cb2 | |
|
|
8be71600a2 | |
|
|
1735901ffe | |
|
|
27ac07ad0a | |
|
|
c2bbeb7cdc | |
|
|
4bdbc1daf7 | |
|
|
ae727d0fdd | |
|
|
515abca80f | |
|
|
773f400add | |
|
|
b274c5e4af | |
|
|
af6d748b86 | |
|
|
fd5a2e60e4 | |
|
|
ce4044bf8c | |
|
|
53b84e0c9b | |
|
|
c410d4e2e3 | |
|
|
f7516a9ffe | |
|
|
778f855ee9 | |
|
|
feb99bb10e | |
|
|
b7067082ed | |
|
|
9a8fe13f28 | |
|
|
4ac353a83a | |
|
|
7f39e3029f | |
|
|
086af70f22 | |
|
|
0e96989aaa | |
|
|
9fd0d3caf4 | |
|
|
6328264f90 | |
|
|
2ebd64ab41 | |
|
|
6a26d412c4 | |
|
|
504379f3e3 | |
|
|
81c561d28a | |
|
|
8784a143f1 | |
|
|
9439b82081 | |
|
|
e036fe7e10 | |
|
|
7ffd5135cc | |
|
|
f03fc06949 | |
|
|
4d8e4fa01d | |
|
|
980a8147ce | |
|
|
60a9bdab2e | |
|
|
e9c73d1925 | |
|
|
6f3b51f461 | |
|
|
5eec808514 | |
|
|
9e8edf01b8 | |
|
|
baae63153a | |
|
|
9a491ab1c7 | |
|
|
1f86f382df | |
|
|
7a7b549de3 | |
|
|
9b90d44e34 | |
|
|
c66bdbcc74 | |
|
|
4b7021b45e | |
|
|
3d2266f4a1 | |
|
|
2fae492f2b | |
|
|
5ac798c295 | |
|
|
61143a39f7 | |
|
|
9d067ab748 | |
|
|
1860ac322e | |
|
|
50855a68ce | |
|
|
53d900ca22 | |
|
|
6280850e30 | |
|
|
96ede9b575 | |
|
|
a7e0598951 | |
|
|
487bf5e0c6 | |
|
|
93053025a2 | |
|
|
5409a3c225 | |
|
|
c39ed025b9 | |
|
|
af221e4486 | |
|
|
0f656cfed6 | |
|
|
6b62162e8d | |
|
|
2373b13fc6 | |
|
|
4cc420799e | |
|
|
48bad8a9a6 | |
|
|
adae1ab595 | |
|
|
7bcc08ff0e | |
|
|
6b9263ca1b | |
|
|
8abca803f9 | |
|
|
983780b73b | |
|
|
04b59e281a | |
|
|
9f11895847 | |
|
|
d1910e2497 | |
|
|
268c56e7ce | |
|
|
e251f3fccb | |
|
|
abcd3c1c6f | |
|
|
4b8cd8b9e8 | |
|
|
aff3016e4f | |
|
|
115394106a | |
|
|
ddcde9e1da | |
|
|
6f072b97c9 | |
|
|
ab03f70fd6 | |
|
|
c759c8bc55 | |
|
|
e298748d45 | |
|
|
e04b80ff25 | |
|
|
830fde4bc0 | |
|
|
7ff9552621 | |
|
|
6776fe3f0d | |
|
|
e2f9e0e393 | |
|
|
7d8ad420e4 | |
|
|
356fc1995e | |
|
|
d9da314968 | |
|
|
3951ef325d | |
|
|
a7f7a29d74 | |
|
|
50ee54d6ef | |
|
|
701d90779a | |
|
|
61b10eba26 | |
|
|
45f69d527e | |
|
|
a56d750f2d | |
|
|
aa2efbd42e | |
|
|
6bed84cf3e | |
|
|
330f2f7d54 | |
|
|
b96d006f9c | |
|
|
baa4b52553 | |
|
|
dd2a7d99b6 | |
|
|
e1643a6331 | |
|
|
5f6f014226 | |
|
|
b3e5568216 | |
|
|
cc29006013 | |
|
|
db343353cc | |
|
|
01cd03213e | |
|
|
31a8de5569 | |
|
|
ec3e34ad21 | |
|
|
0d14477705 | |
|
|
74ed165f71 | |
|
|
f2cbf8f9cb | |
|
|
5485a827be | |
|
|
0dfdeee19d | |
|
|
d1e23c58ef | |
|
|
7a6abccf2f | |
|
|
602d06aafc | |
|
|
59b045d457 | |
|
|
97bcc1ac0a | |
|
|
b1cbbec237 | |
|
|
4ce68abe84 | |
|
|
3102a2c0a0 | |
|
|
357410c3e7 | |
|
|
a4ce1ba45a | |
|
|
7db1b6ed37 | |
|
|
4238f345cb | |
|
|
caec1d4bd0 | |
|
|
695c77ca60 | |
|
|
f0294212b2 | |
|
|
f992b2b29e | |
|
|
5583bb0f11 | |
|
|
47879d029a | |
|
|
5ead2c13b2 | |
|
|
389deac133 | |
|
|
b21c54a901 | |
|
|
e767c6aa50 | |
|
|
8ea80a4dbe | |
|
|
fb3f3c721e | |
|
|
d3bbf2da62 | |
|
|
693899f45c | |
|
|
fe2791f815 | |
|
|
54e7d394bb | |
|
|
6ec773ce3d | |
|
|
0774e81fd0 | |
|
|
1111d285c5 | |
|
|
c257cdc5cd | |
|
|
1d85e92997 | |
|
|
e7ed169f94 | |
|
|
9be3b62aaf | |
|
|
6c3f344b92 | |
|
|
ed90f7ce87 | |
|
|
f79150abc9 | |
|
|
8d6ca0b64f | |
|
|
6613b3612c | |
|
|
af12674d7b | |
|
|
7c46ae5db4 | |
|
|
56d79e96b6 | |
|
|
995f7f8b03 | |
|
|
2cf2fdff08 | |
|
|
dc1f5f7302 | |
|
|
49cd27c221 | |
|
|
a4a93ee484 | |
|
|
c940880a9d | |
|
|
3e45254fc1 | |
|
|
a19909d1bf | |
|
|
2f51b56298 | |
|
|
4cc2d3beb1 | |
|
|
ed9fda7fe3 | |
|
|
bd830115d4 | |
|
|
a9142c0e1b | |
|
|
d3c4776bab | |
|
|
a1ea52a934 | |
|
|
05865dc2ef | |
|
|
30d551fe59 | |
|
|
333b5e1a8e | |
|
|
94348aebc4 | |
|
|
1d842d3bf2 | |
|
|
1bdc88cd7c | |
|
|
1a4f64865a |
|
|
@ -0,0 +1,139 @@
|
|||
你是一个资深 Java 后端工程师,请在当前 Spring Boot(Java8)项目中实现一个完整的 MCP Server,并加入“AI 自动选 Tool(RAG + 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 streaming(write + 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 API(OpenAI / 本地模型)
|
||||
3. 支持多 MCP Server(未来扩展)
|
||||
|
||||
# 十、输出要求
|
||||
|
||||
1. 输出完整代码:
|
||||
|
||||
* McpController
|
||||
* McpService
|
||||
* McpToolRegistry
|
||||
* ToolRetriever
|
||||
* ToolRouter
|
||||
* ToolEmbedding
|
||||
* ToolInitializer
|
||||
* McpRequest
|
||||
|
||||
2. 代码必须:
|
||||
|
||||
* 可运行
|
||||
* 无伪代码
|
||||
* 包含必要注释
|
||||
* 代码生成目录在ruoyi-sip/src/main/java/com/ruoyi/sip/llm
|
||||
3. 不要解释,只输出代码
|
||||
|
|
@ -6,3 +6,4 @@ ENV = 'production'
|
|||
|
||||
# 若依管理系统/生产环境
|
||||
VUE_APP_BASE_API = '/prod-api'
|
||||
VUE_APP_SERVICE_HOST = 'wb.oms.unisspace.com'
|
||||
|
|
|
|||
|
|
@ -28,18 +28,20 @@
|
|||
"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",
|
||||
|
|
@ -48,8 +50,7 @@
|
|||
"vue-cropper": "0.5.5",
|
||||
"vue-router": "3.4.9",
|
||||
"vuedraggable": "2.24.3",
|
||||
"vuex": "3.6.0",
|
||||
"decimal.js": "10.4.2"
|
||||
"vuex": "3.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "4.4.6",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
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
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
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'
|
||||
})
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import request from '@/utils/request'
|
||||
import { tansParams } from '@/utils/ruoyi'
|
||||
|
||||
// 查询制造商信息列表
|
||||
export function listVendor(query) {
|
||||
|
|
@ -48,7 +49,9 @@ export function exportVendor(query) {
|
|||
return request({
|
||||
url: '/system/vendor/export',
|
||||
method: 'post',
|
||||
params: query
|
||||
data: query,
|
||||
transformRequest: [(params) => tansParams(params)],
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 按SN码查询库存数据
|
||||
export function listInventoryInfoByProductSn(query) {
|
||||
return request({
|
||||
url: '/sip/dataProcess/inventoryInfoByProductSn',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
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
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
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
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
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'
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
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
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
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'
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
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'
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,210 @@
|
|||
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
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
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'
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
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'
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
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'
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
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'
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
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
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
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
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
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'
|
||||
})
|
||||
}
|
||||
|
|
@ -17,6 +17,14 @@ 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({
|
||||
|
|
|
|||
|
|
@ -52,6 +52,15 @@ 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({
|
||||
|
|
|
|||
|
|
@ -71,6 +71,23 @@ 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({
|
||||
|
|
|
|||
|
|
@ -85,3 +85,30 @@ 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
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,15 @@ 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({
|
||||
|
|
|
|||
|
|
@ -79,6 +79,14 @@ 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',
|
||||
|
|
@ -89,3 +97,13 @@ 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
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
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()
|
||||
|
|
@ -9,6 +15,40 @@ 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',
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ export function delOrder(id) {
|
|||
export function exportOrder(query) {
|
||||
return request({
|
||||
url: `${baseURL}/export`,
|
||||
method: 'post',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,28 +1,40 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询产品信息
|
||||
export function getProductInfo(serialNumber) {
|
||||
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) {
|
||||
return request({
|
||||
url: '/manage/service/product',
|
||||
method: 'get',
|
||||
params: { serialNumber }
|
||||
method: 'post',
|
||||
data: buildQueryPayload(serialNumberOrNumbers)
|
||||
})
|
||||
}
|
||||
|
||||
// 查询相关合同信息
|
||||
export function getOrderInfo(serialNumber) {
|
||||
export function getOrderInfo(serialNumberOrNumbers) {
|
||||
return request({
|
||||
url: '/manage/service/order',
|
||||
method: 'get',
|
||||
params: { serialNumber }
|
||||
method: 'post',
|
||||
data: buildQueryPayload(serialNumberOrNumbers)
|
||||
})
|
||||
}
|
||||
|
||||
// 查询标准保修信息
|
||||
export function getWarrantyInfo(serialNumber) {
|
||||
export function getWarrantyInfo(serialNumberOrNumbers) {
|
||||
return request({
|
||||
url: '/manage/service/query',
|
||||
method: 'get',
|
||||
params: { serialNumber }
|
||||
method: 'post',
|
||||
data: buildQueryPayload(serialNumberOrNumbers)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,14 @@
|
|||
import request from '@/utils/request'
|
||||
import { tansParams } from '@/utils/ruoyi'
|
||||
|
||||
// 查询项目管理列表
|
||||
export function listProject(query) {
|
||||
return request({
|
||||
url: '/sip/project/vue/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
method: 'post',
|
||||
data: query,
|
||||
headers: { 'Content-Type': 'multipart/form-data' },
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -49,6 +52,34 @@ export function exportProject(query) {
|
|||
return request({
|
||||
url: '/sip/project/vue/export',
|
||||
method: 'post',
|
||||
params: query
|
||||
data: query,
|
||||
transformRequest: [(params) => tansParams(params)],
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
|
||||
})
|
||||
}
|
||||
|
||||
// 项目收藏
|
||||
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
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -85,3 +85,48 @@ 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 }
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -106,6 +106,16 @@ 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) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import request from '@/utils/request'
|
||||
import { tansParams } from '@/utils/ruoyi'
|
||||
|
||||
// 查询办事处信息列表
|
||||
export function listAgent(query) {
|
||||
|
|
@ -48,6 +49,8 @@ export function exportAgent(query) {
|
|||
return request({
|
||||
url: '/system/agent/vue/export',
|
||||
method: 'post',
|
||||
params: query
|
||||
data: query,
|
||||
transformRequest: [(params) => tansParams(params)],
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
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'
|
||||
})
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import request from '@/utils/request'
|
||||
import { tansParams } from '@/utils/ruoyi'
|
||||
|
||||
// 查询客户信息列表
|
||||
export function listCustomer(query) {
|
||||
|
|
@ -48,6 +49,8 @@ export function exportCustomer(query) {
|
|||
return request({
|
||||
url: '/system/customer/vue/export',
|
||||
method: 'post',
|
||||
params: query
|
||||
data: query,
|
||||
transformRequest: [(params) => tansParams(params)],
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import request from '@/utils/request'
|
||||
import { tansParams } from '@/utils/ruoyi'
|
||||
|
||||
// 查询产品管理列表
|
||||
export function listProduct(query) {
|
||||
|
|
@ -45,11 +46,13 @@ export function delProduct(id) {
|
|||
|
||||
// 导出产品管理
|
||||
export function exportProduct(query) {
|
||||
return request({
|
||||
url: '/system/product/export',
|
||||
method: 'post',
|
||||
params: query
|
||||
})
|
||||
return request({
|
||||
url: '/system/product/export',
|
||||
method: 'post',
|
||||
data: query,
|
||||
transformRequest: [(params) => tansParams(params)],
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
|
||||
})
|
||||
}
|
||||
|
||||
// a new function that queries a product by its code
|
||||
|
|
|
|||
|
|
@ -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,7 +113,20 @@ 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,
|
||||
|
|
@ -121,7 +134,7 @@ export function getAuthRole(userId) {
|
|||
})
|
||||
}
|
||||
|
||||
// 保存授权角色
|
||||
// 淇濆瓨鎺堟潈瑙掕壊
|
||||
export function updateAuthRole(data) {
|
||||
return request({
|
||||
url: '/system/user/authRole',
|
||||
|
|
@ -130,7 +143,7 @@ export function updateAuthRole(data) {
|
|||
})
|
||||
}
|
||||
|
||||
// 查询部门下拉树结构
|
||||
// 鏌ヨ閮ㄩ棬涓嬫媺鏍戠粨鏋?
|
||||
export function deptTreeSelect() {
|
||||
return request({
|
||||
url: '/system/user/deptTree',
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import request from '@/utils/request'
|
||||
import { tansParams } from '@/utils/ruoyi'
|
||||
|
||||
// 查询仓库基础信息列表
|
||||
export function listInfo(query) {
|
||||
|
|
@ -45,9 +46,11 @@ export function delInfo(id) {
|
|||
|
||||
// 导出仓库基础信息
|
||||
export function exportInfo(query) {
|
||||
return request({
|
||||
url: '/warehouse/info/export',
|
||||
method: 'post',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
return request({
|
||||
url: '/warehouse/info/export',
|
||||
method: 'post',
|
||||
data: query,
|
||||
transformRequest: [(params) => tansParams(params)],
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 8.6 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 7.5 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 152 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
|
|
@ -104,6 +104,18 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/** 表单布局 **/
|
||||
|
|
|
|||
|
|
@ -0,0 +1,64 @@
|
|||
<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>
|
||||
|
|
@ -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,4 +183,8 @@ export default {
|
|||
background-color: #ccc;
|
||||
margin: 3px auto;
|
||||
}
|
||||
.el-dropdown-menu{
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
<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"
|
||||
|
|
@ -11,6 +12,8 @@
|
|||
:active-text-color="settings.theme"
|
||||
:collapse-transition="false"
|
||||
mode="vertical"
|
||||
@open="handleMenuOpen"
|
||||
@close="handleMenuClose"
|
||||
>
|
||||
<sidebar-item
|
||||
v-for="(route, index) in sidebarRouters"
|
||||
|
|
@ -24,6 +27,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import pathLib from 'path'
|
||||
import { mapGetters, mapState } from "vuex"
|
||||
import Logo from "./Logo"
|
||||
import SidebarItem from "./SidebarItem"
|
||||
|
|
@ -52,6 +56,55 @@ 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>
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ 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"
|
||||
// 自定义表格工具组件
|
||||
|
|
@ -42,6 +43,7 @@ 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
|
||||
|
|
|
|||
|
|
@ -5,12 +5,16 @@ 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))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,13 +9,12 @@ const baseURL = process.env.VUE_APP_BASE_API
|
|||
let downloadLoadingInstance
|
||||
|
||||
export default {
|
||||
name(name, isDelete = true) {
|
||||
download(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) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import Vue from 'vue'
|
||||
import Router from 'vue-router'
|
||||
import { isServiceHost } from '@/utils/serviceHost'
|
||||
|
||||
Vue.use(Router)
|
||||
|
||||
|
|
@ -62,10 +63,10 @@ export const constantRoutes = [
|
|||
hidden: true
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
path: isServiceHost() ? '/admin-root' : '',
|
||||
component: Layout,
|
||||
redirect: 'index',
|
||||
children: [
|
||||
redirect: isServiceHost() ? undefined : 'index',
|
||||
children: isServiceHost() ? [] : [
|
||||
{
|
||||
path: 'index',
|
||||
component: () => import('@/views/index'),
|
||||
|
|
@ -94,6 +95,46 @@ 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
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
@ -112,6 +153,11 @@ 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'),
|
||||
|
|
@ -164,6 +210,31 @@ 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,
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ const user = {
|
|||
const rememberMe = userInfo.rememberMe
|
||||
return new Promise((resolve, reject) => {
|
||||
login(username, password, code, rememberMe).then(res => {
|
||||
resolve()
|
||||
resolve(res)
|
||||
}).catch(error => {
|
||||
reject(error)
|
||||
})
|
||||
|
|
@ -60,17 +60,16 @@ const user = {
|
|||
},
|
||||
|
||||
// 获取用户信息
|
||||
GetInfo({ commit, state }) {
|
||||
GetInfo({ commit }) {
|
||||
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) { // 验证返回的roles是否是一个非空数组
|
||||
if (data.roles && data.roles.length > 0) {
|
||||
commit('SET_ROLES', data.roles)
|
||||
commit('SET_PERMISSIONS', data.permissions)
|
||||
} else {
|
||||
|
|
@ -82,15 +81,13 @@ 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(() => {})
|
||||
}
|
||||
|
|
@ -102,7 +99,7 @@ const user = {
|
|||
},
|
||||
|
||||
// 退出系统 - 基于 Session 认证
|
||||
LogOut({ commit, state }) {
|
||||
LogOut({ commit }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
logout().then(() => {
|
||||
commit('SET_ROLES', [])
|
||||
|
|
|
|||
|
|
@ -19,17 +19,17 @@ export function toFixed(value, dp = DEFAULT_DP) {
|
|||
|
||||
// 加法
|
||||
export function add(a, b, dp = DEFAULT_DP) {
|
||||
return D(a).plus(b).toDecimalPlaces(dp).toNumber()
|
||||
return D(a).plus(D(b)).toDecimalPlaces(dp).toNumber()
|
||||
}
|
||||
|
||||
// 减法
|
||||
export function sub(a, b, dp = DEFAULT_DP) {
|
||||
return D(a).minus(b).toDecimalPlaces(dp).toNumber()
|
||||
return D(a).minus(D(b)).toDecimalPlaces(dp).toNumber()
|
||||
}
|
||||
|
||||
// 乘法
|
||||
export function mul(a, b, dp = DEFAULT_DP) {
|
||||
return D(a).times(b).toDecimalPlaces(dp).toNumber()
|
||||
return D(a).times(D(b)).toDecimalPlaces(dp).toNumber()
|
||||
}
|
||||
|
||||
// 除法
|
||||
|
|
|
|||
|
|
@ -14,7 +14,10 @@ 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
|
||||
|
|
|
|||
|
|
@ -110,6 +110,8 @@ 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')
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
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)
|
||||
}
|
||||
|
|
@ -0,0 +1,359 @@
|
|||
<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,金额是否变化存储于 extendField3(extendField1 为订单版本号)
|
||||
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>
|
||||
|
|
@ -0,0 +1,312 @@
|
|||
<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
|
||||
},
|
||||
// 发货记录id,用于获取发货详情与SN码列表
|
||||
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,
|
||||
// 不按状态/出库单过滤:撤回后的SN已还原为入库状态且outer_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
|
|
@ -49,6 +49,22 @@
|
|||
@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>
|
||||
|
|
@ -72,13 +88,18 @@
|
|||
<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" />
|
||||
<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="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
|
||||
|
|
@ -127,6 +148,7 @@
|
|||
<script>
|
||||
import { listOrder } from "@/api/approve/order/orderLog";
|
||||
import ApproveDialog from '../order/Approve.vue';
|
||||
import {formatCurrency} from "../../../utils";
|
||||
|
||||
export default {
|
||||
name: "ApprovedOrder",
|
||||
|
|
@ -153,10 +175,12 @@ export default {
|
|||
orderCode: null,
|
||||
projectName: null,
|
||||
projectCode: null,
|
||||
productCode: null,
|
||||
productModel: null,
|
||||
customerName: null,
|
||||
dutyName: null,
|
||||
approveNode: null,
|
||||
orderByColumn:'t7.todo_approve_time',
|
||||
orderByColumn:'t1.approveTime desc, t1.id',
|
||||
isAsc: 'desc'
|
||||
},
|
||||
};
|
||||
|
|
@ -165,6 +189,7 @@ export default {
|
|||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
formatCurrency,
|
||||
/** 查询订单列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,259 @@
|
|||
function escapeHtml(value) {
|
||||
return String(value == null ? "" : value)
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'");
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
@ -0,0 +1,213 @@
|
|||
<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>
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
<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>
|
||||
|
|
@ -0,0 +1,302 @@
|
|||
<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>
|
||||
|
|
@ -0,0 +1,214 @@
|
|||
<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>
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
<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>
|
||||
|
|
@ -0,0 +1,304 @@
|
|||
<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>
|
||||
|
|
@ -0,0 +1,215 @@
|
|||
<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>
|
||||
|
|
@ -0,0 +1,284 @@
|
|||
<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>
|
||||
|
|
@ -0,0 +1,314 @@
|
|||
<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>
|
||||
|
|
@ -0,0 +1,219 @@
|
|||
<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>
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
<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>
|
||||
|
|
@ -0,0 +1,315 @@
|
|||
<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>
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
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;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,215 @@
|
|||
<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>
|
||||
|
|
@ -0,0 +1,147 @@
|
|||
<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>
|
||||
|
|
@ -0,0 +1,302 @@
|
|||
<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>
|
||||
|
|
@ -0,0 +1,215 @@
|
|||
<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>
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
<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>
|
||||
|
|
@ -0,0 +1,306 @@
|
|||
<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>
|
||||
|
|
@ -0,0 +1,240 @@
|
|||
<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>
|
||||
|
|
@ -0,0 +1,170 @@
|
|||
<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>
|
||||
|
|
@ -0,0 +1,327 @@
|
|||
<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>
|
||||
|
|
@ -0,0 +1,217 @@
|
|||
<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>
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
<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>
|
||||
|
|
@ -0,0 +1,304 @@
|
|||
<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>
|
||||
|
|
@ -135,8 +135,7 @@
|
|||
import { approveOrder,getOrder } from "@/api/approve/order";
|
||||
import ConfigInfo from './ConfigInfo.vue';
|
||||
import ApproveLayout from '@/views/approve/ApproveLayout.vue';
|
||||
import html2canvas from 'html2canvas';
|
||||
import jsPDF from 'jspdf';
|
||||
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
|
||||
|
||||
import OrderInfoDisplay from '@/views/project/order/components/OrderInfoDisplay.vue';
|
||||
|
||||
|
|
@ -468,65 +467,21 @@ export default {
|
|||
// 导出PDF
|
||||
async exportPDF() {
|
||||
this.pdfExporting = true;
|
||||
const disabledElements = [];
|
||||
try {
|
||||
// 获取ApproveLayout组件的DOM元素
|
||||
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; // 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;
|
||||
}
|
||||
|
||||
// 生成文件名
|
||||
const fileName = `${this.order.projectCode || '订单'}-${this.order.orderCode || ''}-Rev.${this.order.versionCode || '1'}.pdf`;
|
||||
|
||||
// 保存PDF
|
||||
pdf.save(fileName);
|
||||
// 调用通用方法导出PDF
|
||||
await exportElementToPDF(element, 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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(product.allPrice) }}</td>
|
||||
<td>{{ formatCurrency(getDisplayAllPrice(product, selectedDiscount)) }}</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(product.allPrice) }}</td>
|
||||
<td>{{ formatCurrency(getDisplayAllPrice(product, selectedDiscount)) }}</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(product.allPrice) }}</td>
|
||||
<td>{{ formatCurrency(getDisplayAllPrice(product, selectedDiscount)) }}</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="18" style="text-align: center;">
|
||||
<el-col :span="orderData.orderStatus==='1'?18:24" 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">
|
||||
<el-col :span="6" v-if="orderData.orderStatus==='1'">
|
||||
<span class="summary-label"> 折后总价合计</span> <span
|
||||
class="summary-value-right"> {{ formatCurrency(finalTotal) }}</span>
|
||||
</el-col>
|
||||
|
|
@ -278,7 +278,12 @@ 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;
|
||||
|
|
@ -297,13 +302,20 @@ export default {
|
|||
// 税率变化时通知父组件
|
||||
this.$emit('tax-rate-change', product);
|
||||
},
|
||||
getDiscountedAllPrice(product, discount) {
|
||||
if (discount === 1) {
|
||||
return product.allPrice;
|
||||
getDisplayAllPrice(product, discount) {
|
||||
const allPrice = product.allPrice || 0;
|
||||
if (this.order.orderStatus === '2' || this.order.orderStatus === 2) {
|
||||
return this.$calc.div(allPrice, discount || 1);
|
||||
}
|
||||
const roundedDiscountedUnitPrice = this.$calc.mul( product.price,discount);
|
||||
|
||||
return this.$calc.mul(roundedDiscountedUnitPrice , product.quantity);
|
||||
return allPrice;
|
||||
},
|
||||
getDiscountedAllPrice(product, discount) {
|
||||
const allPrice = product.allPrice || 0;
|
||||
if (this.order.orderStatus === '2' || this.order.orderStatus === 2) {
|
||||
return allPrice;
|
||||
}
|
||||
const discountedPrice = this.$calc.mul(product.price || 0, discount == null ? 1 : discount, 2);
|
||||
return this.$calc.mul(discountedPrice, product.quantity || 0, 2);
|
||||
},
|
||||
calculateTotal(productList, discount) {
|
||||
if (!productList) return 0;
|
||||
|
|
|
|||
|
|
@ -49,15 +49,28 @@
|
|||
@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
|
||||
|
|
@ -75,7 +88,12 @@
|
|||
<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" />
|
||||
|
||||
<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="dutyName" />
|
||||
<el-table-column label="审批节点" align="center" prop="approveNode">
|
||||
<template slot-scope="scope">
|
||||
|
|
@ -106,6 +124,7 @@
|
|||
<el-dialog
|
||||
title="订单审批"
|
||||
:visible.sync="approveDialogVisible"
|
||||
:close-on-click-modal="false"
|
||||
custom-class="approve-dialog"
|
||||
width="80%"
|
||||
top="5vh"
|
||||
|
|
@ -130,6 +149,7 @@
|
|||
<script>
|
||||
import { listOrder } from "@/api/approve/order";
|
||||
import ApproveDialog from './Approve.vue';
|
||||
import {formatCurrency} from "../../../utils";
|
||||
|
||||
export default {
|
||||
name: "Order",
|
||||
|
|
@ -156,9 +176,14 @@ 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',
|
||||
},
|
||||
};
|
||||
},
|
||||
|
|
@ -166,6 +191,7 @@ export default {
|
|||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
formatCurrency,
|
||||
toApproved(){
|
||||
this.$router.push({
|
||||
path: '/approve/orderLog',
|
||||
|
|
|
|||
|
|
@ -23,7 +23,22 @@
|
|||
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>
|
||||
|
|
@ -142,12 +157,15 @@ export default {
|
|||
buyerName: null,
|
||||
vendorName: null,
|
||||
ownerName: null,
|
||||
productCode: null,
|
||||
productModel: null,
|
||||
approveStatus: '1',
|
||||
params:{
|
||||
applyTimeStart: null,
|
||||
applyTimeEnd: null,
|
||||
}
|
||||
|
||||
},
|
||||
orderByColumn: 'applyTime',
|
||||
isAsc: 'desc'
|
||||
},
|
||||
// 时间范围选择器
|
||||
dateRangeApplyTime: [],
|
||||
|
|
|
|||
|
|
@ -23,7 +23,22 @@
|
|||
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>
|
||||
|
|
@ -134,12 +149,16 @@ 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: [],
|
||||
|
|
|
|||
|
|
@ -0,0 +1,143 @@
|
|||
<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>
|
||||
|
|
@ -0,0 +1,602 @@
|
|||
<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>
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
<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>
|
||||
|
|
@ -9,17 +9,17 @@
|
|||
@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"
|
||||
/>
|
||||
</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"
|
||||
|
|
@ -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="vendorName" />
|
||||
<el-table-column label="制造商全称" align="center" prop="vendorAddress" />
|
||||
<el-table-column label="制造商名称" align="center" prop="vendorAddress" />
|
||||
<el-table-column label="制造商全称" align="center" prop="vendorName" />
|
||||
<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="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-col>
|
||||
</el-row>
|
||||
<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-row>
|
||||
<el-col :span="12">
|
||||
|
|
@ -301,6 +301,8 @@ export default {
|
|||
loading: true,
|
||||
// 选中数组
|
||||
ids: [],
|
||||
// 选中制造商编码
|
||||
selectedVendorCodes: [],
|
||||
// 非单个禁用
|
||||
single: true,
|
||||
// 非多个禁用
|
||||
|
|
@ -330,6 +332,8 @@ export default {
|
|||
vendorAddress: null,
|
||||
vendorUser: null,
|
||||
vendorStatus: null,
|
||||
isAsc : 'desc',
|
||||
orderByColumn : 'createTime',
|
||||
},
|
||||
// 表单参数
|
||||
form: {
|
||||
|
|
@ -452,6 +456,7 @@ 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
|
||||
},
|
||||
|
|
@ -524,8 +529,12 @@ export default {
|
|||
},
|
||||
/** 导出按钮操作 */
|
||||
handleExport() {
|
||||
const queryParams = this.queryParams;
|
||||
this.$modal.confirm('是否确认导出所有制造商信息数据项?').then(() => {
|
||||
const queryParams = {
|
||||
...this.queryParams,
|
||||
vendorCodeList: this.selectedVendorCodes.length ? this.selectedVendorCodes : undefined
|
||||
};
|
||||
const exportTip = this.selectedVendorCodes.length ? '是否确认导出选中的制造商信息数据项?' : '是否确认导出所有制造商信息数据项?';
|
||||
this.$modal.confirm(exportTip).then(() => {
|
||||
this.exportLoading = true;
|
||||
return exportVendor(queryParams);
|
||||
}).then(response => {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,76 @@
|
|||
<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>
|
||||
|
|
@ -0,0 +1,301 @@
|
|||
<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>
|
||||
|
|
@ -0,0 +1,310 @@
|
|||
<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
Loading…
Reference in New Issue