73bfd6a8 by lihua

数字合约功能迁移

1 parent 9324c603
......@@ -100,6 +100,9 @@ VITE_APP_DATA_DELIVERY = https://daop-jgjf-test.zgsjzc.com/
#数据服务接口地址
VITE_APP_SERVICE_BASEURL = ms-daop-trust-api-service
#数字合约接口
VITE_APP_DIGITAL_CONTRACT_URL = ms-daop-trust-data-space-service
# 本地访问地址
# VITE_API_CIRCULATION_URL = http://localhost:9000/circulation
......
......@@ -86,6 +86,9 @@ VITE_APP_PERSONAL_URL = ms-daop-personel-service
#数据服务接口地址
VITE_APP_SERVICE_BASEURL = ms-daop-trust-api-service
#数字合约接口
VITE_APP_DIGITAL_CONTRACT_URL = ms-daop-trust-data-space-service
#流通平台接口地址
VITE_APP_CIRCULATION = https://sz-lt.zgsjzc.com/
#数据加工交付
......
......@@ -35,6 +35,7 @@
"file-saver": "^2.0.5",
"hotkeys-js": "^3.10.2",
"html2canvas": "^1.4.1",
"html2pdf.js": "^0.12.1",
"insert-css": "^2.0.0",
"jquery": "^3.7.1",
"jsencrypt": "^3.3.2",
......
......@@ -65,6 +65,9 @@ dependencies:
html2canvas:
specifier: ^1.4.1
version: 1.4.1
html2pdf.js:
specifier: ^0.12.1
version: 0.12.1
insert-css:
specifier: ^2.0.0
version: 2.0.0
......@@ -966,6 +969,11 @@ packages:
regenerator-runtime: 0.14.1
dev: false
/@babel/runtime@7.28.4:
resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==}
engines: {node: '>=6.9.0'}
dev: false
/@babel/template@7.25.9:
resolution: {integrity: sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==}
engines: {node: '>=6.9.0'}
......@@ -1710,6 +1718,10 @@ packages:
resolution: {integrity: sha512-k7kRA033QNtC+gLc4VPlfnue58CM1iQLgn1IMAU8VPHGOj7oIHPp9UlhedEnD/Gl8evoCjwkZjlBORtZ3JByUA==}
dev: true
/@types/pako@2.0.4:
resolution: {integrity: sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw==}
dev: false
/@types/path-browserify@1.0.3:
resolution: {integrity: sha512-ZmHivEbNCBtAfcrFeBCiTjdIc2dey0l7oCGNGpSuRTy8jP6UVND7oUowlvDujBy8r2Hoa8bfFUOCiPWfmtkfxw==}
dev: true
......@@ -1718,6 +1730,12 @@ packages:
resolution: {integrity: sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ==}
dev: true
/@types/raf@3.4.3:
resolution: {integrity: sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==}
requiresBuild: true
dev: false
optional: true
/@types/semver@7.5.8:
resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==}
dev: true
......@@ -1734,6 +1752,12 @@ packages:
'@types/node': 22.9.3
dev: true
/@types/trusted-types@2.0.7:
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
requiresBuild: true
dev: false
optional: true
/@types/unist@2.0.11:
resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==}
dev: true
......@@ -2988,6 +3012,22 @@ packages:
resolution: {integrity: sha512-G1LRwLIQjBQoyq0ZJGqGIJUXzJ8irpbjHLpVRXDvBEScFJ9b17sgK6vlx0GAJFE21okD7zXl08rRRUfq6HdoEQ==}
dev: true
/canvg@3.0.11:
resolution: {integrity: sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==}
engines: {node: '>=10.0.0'}
requiresBuild: true
dependencies:
'@babel/runtime': 7.28.4
'@types/raf': 3.4.3
core-js: 3.39.0
raf: 3.4.1
regenerator-runtime: 0.13.11
rgbcolor: 1.0.1
stackblur-canvas: 2.7.0
svg-pathdata: 6.0.3
dev: false
optional: true
/capital-case@1.0.4:
resolution: {integrity: sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==}
dependencies:
......@@ -3959,6 +3999,14 @@ packages:
resolution: {integrity: sha512-m4yreHcUWHBncGVV7U+yQzc12vIlq0jMrtHZ5mW6dQMiL/7skSYNVX9wqKwOtyO9SGCgevrAFEgOCAHmamHTUA==}
dev: false
/dompurify@3.3.0:
resolution: {integrity: sha512-r+f6MYR1gGN1eJv0TVQbhA7if/U7P87cdPl3HN5rikqaBSBxLiCb/b9O+2eG0cxz0ghyU+mU1QkbsOwERMYlWQ==}
requiresBuild: true
optionalDependencies:
'@types/trusted-types': 2.0.7
dev: false
optional: true
/domutils@1.7.0:
resolution: {integrity: sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==}
dependencies:
......@@ -5108,6 +5156,14 @@ packages:
resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
dev: true
/fast-png@6.4.0:
resolution: {integrity: sha512-kAqZq1TlgBjZcLr5mcN6NP5Rv4V2f22z00c3g8vRrwkcqjerx7BEhPbOnWCPqaHUl2XWQBJQvOT/FQhdMT7X/Q==}
dependencies:
'@types/pako': 2.0.4
iobuffer: 5.4.0
pako: 2.1.0
dev: false
/fast-uri@3.0.3:
resolution: {integrity: sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==}
dev: true
......@@ -5131,6 +5187,10 @@ packages:
resolution: {integrity: sha512-Rr5QlUeGN1mbOHlaqcSYMKVpPbgLy0AWT/W0EHxA6NGI12yO1jpoui2zBBvU2G824ltM6Ut8BFgfHSBGfkmS0A==}
dev: false
/fflate@0.8.2:
resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==}
dev: false
/figures@3.2.0:
resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==}
engines: {node: '>=8'}
......@@ -5780,6 +5840,13 @@ packages:
text-segmentation: 1.0.3
dev: false
/html2pdf.js@0.12.1:
resolution: {integrity: sha512-3rBWQ96H5oOU9jtoz3MnE/epGi27ig9h8aonBk4JTpvUERM3lMRxhIRckhJZEi4wE0YfRINoYOIDY0hLY0CHgQ==}
dependencies:
html2canvas: 1.4.1
jspdf: 3.0.4
dev: false
/htmlparser2@3.10.1:
resolution: {integrity: sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==}
dependencies:
......@@ -5981,6 +6048,10 @@ packages:
engines: {node: '>= 0.10'}
dev: true
/iobuffer@5.4.0:
resolution: {integrity: sha512-DRebOWuqDvxunfkNJAlc3IzWIPD5xVxwUNbHr7xKB8E6aLJxIPfNX3CoMJghcFjpv6RWQsrcJbghtEwSPoJqMA==}
dev: false
/iota-array@1.0.0:
resolution: {integrity: sha512-pZ2xT+LOHckCatGQ3DcG/a+QuEqvoxqkiL7tvE8nn3uuu+f6i1TtpB5/FtWFbxUuVr5PZCx8KskuGatbJDXOWA==}
dev: true
......@@ -6512,6 +6583,19 @@ packages:
graceful-fs: 4.2.11
dev: true
/jspdf@3.0.4:
resolution: {integrity: sha512-dc6oQ8y37rRcHn316s4ngz/nOjayLF/FFxBF4V9zamQKRqXxyiH1zagkCdktdWhtoQId5K20xt1lB90XzkB+hQ==}
dependencies:
'@babel/runtime': 7.28.4
fast-png: 6.4.0
fflate: 0.8.2
optionalDependencies:
canvg: 3.0.11
core-js: 3.39.0
dompurify: 3.3.0
html2canvas: 1.4.1
dev: false
/jsprim@1.4.2:
resolution: {integrity: sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==}
engines: {node: '>=0.6.0'}
......@@ -7595,6 +7679,10 @@ packages:
engines: {node: '>=6'}
dev: true
/pako@2.1.0:
resolution: {integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==}
dev: false
/param-case@3.0.4:
resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==}
dependencies:
......@@ -7760,7 +7848,6 @@ packages:
/performance-now@2.1.0:
resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==}
dev: true
/picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
......@@ -8087,6 +8174,14 @@ packages:
engines: {node: '>=10'}
dev: true
/raf@3.4.1:
resolution: {integrity: sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==}
requiresBuild: true
dependencies:
performance-now: 2.1.0
dev: false
optional: true
/read-pkg-up@7.0.1:
resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==}
engines: {node: '>=8'}
......@@ -8224,6 +8319,12 @@ packages:
which-builtin-type: 1.2.0
dev: true
/regenerator-runtime@0.13.11:
resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==}
requiresBuild: true
dev: false
optional: true
/regenerator-runtime@0.14.1:
resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
dev: false
......@@ -8388,6 +8489,13 @@ packages:
resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
dev: true
/rgbcolor@1.0.1:
resolution: {integrity: sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==}
engines: {node: '>= 0.8.15'}
requiresBuild: true
dev: false
optional: true
/rimraf@3.0.2:
resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
deprecated: Rimraf versions prior to v4 are no longer supported
......@@ -8844,6 +8952,13 @@ packages:
deprecated: 'Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility'
dev: true
/stackblur-canvas@2.7.0:
resolution: {integrity: sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==}
engines: {node: '>=0.1.14'}
requiresBuild: true
dev: false
optional: true
/static-extend@0.1.2:
resolution: {integrity: sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==}
engines: {node: '>=0.10.0'}
......@@ -9271,6 +9386,13 @@ packages:
- supports-color
dev: true
/svg-pathdata@6.0.3:
resolution: {integrity: sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==}
engines: {node: '>=12.0.0'}
requiresBuild: true
dev: false
optional: true
/svg-tags@1.0.0:
resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==}
dev: true
......
import request from "@/utils/request";
/** 企业注册认证和连接器 */
/** 企业认证 **/
// 企业认证分页
export const getEnterpriseList = (params) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/enterprise/page-list`,
method: 'post',
data: params
})
// 企业认证详情
export const getEnterpriseDetail = (params) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/enterprise/detail`,
method: 'get',
params
})
// 企业认证新增
export const enterpriseSave = (params) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/enterprise/save`,
method: 'post',
data: params
})
// 企业认证修改
export const enterpriseUpdate = (params) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/enterprise/update`,
method: 'put',
data: params
})
// 企业认证删除
export const enterpriseDelete = (params) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/enterprise/delete`,
method: 'delete',
data: params
})
// 企业认证变更删除
export const enterpriseChangeDelete = (params) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/enterprise/change/delete`,
method: 'delete',
data: params
})
// 企业认证最后一级审批
export const enterpriseApprove = (params) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/enterprise/last-approve`,
method: 'post',
data: params
})
// 企业认证进度列表
export const getTaskGressList = (params) => request({
url: `${import.meta.env.VITE_APP_PERSONAL_URL}/pending-task/page-list`,
method: 'post',
data: params
})
// 企业认证重新发起
export const getTaskRestart = (params) => request({
url: `${import.meta.env.VITE_APP_PERSONAL_URL}/pending-task/restart`,
method: 'get',
params
})
// 企业认证执行日志
export const getTaskExecutionLog = (params) => request({
url: `${import.meta.env.VITE_APP_PERSONAL_URL}/pending-task/task-info`,
method: 'get',
params
})
// 获取企业认证流程列表
export const getFlowEnterpriseList = (params) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/enterprise/flow/page-list`,
method: 'post',
data: params
})
// 获取企业信息
export const getUserTenant = () => request({
// url: `http://localhost:9000/master/ms-daop-personel-service/tenant/get-current-user-tenant-from-cache`,
url: `https://sz-lt.zgsjzc.com/master/api/ms-daop-personel-service/tenant/get-current-user-tenant-from-cache`,
method: 'get'
})
// 获取企业信息
export const getEnterpriseData = (params) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/enterprise/detail-by-logonUser`,
method: 'get',
params
})
// 修改需求上架状态
export const updateDemandGrounding = (params) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/demand/update-grounding-pick`,
method: 'post',
data: params
})
/** 数字合约管理 */
import request from "@/utils/request";
export const contractStatusList = [{
value: '01',
label: '发起'
}, {
value: '02',
label: '协商'
}, {
value: '03',
label: '签订'
}, {
value: '0302',
label: '签订失败'
}, {
value: '05',
label: '履行中'
}, {
value: '06',
label: '终止'
}, {
value: '00',
label: '已撤回'
}];
/** 获取策略模板的列表 */
export const getPageList = (params) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/policy-template/page-list`,
method: 'post',
data: params
})
/** 新增策略模板 */
export const savePolicyTemplate = (params) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/policy-template/save`,
method: 'post',
data: params
});
/** 修改策略模板 */
export const updatePolicyTemplate = (params) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/policy-template/update`,
method: 'put',
data: params
});
/** 删除策略模板 */
export const deletePolicyTemplate = (params) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/policy-template/delete`,
method: 'delete',
data: params
});
/** 更新策略模板状态 */
export const updateTemplateState = (params) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/policy-template/update-status`,
method: 'put',
params
});
/** --------------------------- 合约模板管理 -------------------------- */
/** 获取策略模板的列表 */
export const getContractTemplatePageList = (params) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract-template/page-list`,
method: 'post',
data: params
})
/** 新增合约模板状态 */
export const saveContractTemplate = (params) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract-template/save`,
method: 'post',
data: params
});
/** 更新合约模板状态 */
export const updateContractTemplate = (params) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract-template/update`,
method: 'put',
data: params
});
/** 更新合约模板状态 */
export const updateContractTemplateState = (params) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract-template/update-status`,
method: 'put',
params
});
/** 删除策略模板 */
export const deleteContractTemplate = (params) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract-template/delete`,
method: 'delete',
data: params
});
/** 获取策略模板详情 */
export const getContractTemplateDetail = (params) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract-template/detail`,
method: 'get',
params
});
/** 复制合约模板 */
export const copyContractTemplate = (params) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract-template/copy`,
method: 'get',
params
});
/** 获取操作行为下拉列表 */
export const getActionPolicyList = () => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/policy-template/list-by-policy-type?policyType=CZ`,
method: 'get'
});
/** 获取约束条件下拉列表 */
export const getConstraintPolicyList = () => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/policy-template/list-by-policy-type?policyType=YS`,
method: 'get'
});
/** 根据状态获取可用的数字合约模板 */
export const getValidContractTemplateList = () => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract-template/list-by-biz-status`,
method: 'get'
});
/** --------------------- 合约管理 ----------------------- */
export const getContractPageList = (params) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract/page-list`,
method: 'post',
data: params
})
/** 合约备案列表 */
export const getContractOverviewPageList = (params) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract/overview-page-list`,
method: 'post',
data: params
})
/** 获取合约备案的发起主体下拉列表 */
export const getContractOverviewTenantList = () => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract/tenant-list`,
method: 'get',
});
/** 获取下拉数据产品列表 */
export const getContractDataProduct = () => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract/get-data-product`,
method: 'post'
})
/** 创建合约 */
export const saveContract = (params) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract/save`,
method: 'post',
data: params
})
/** 更新合约 */
export const updateContract = (params) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract/update`,
method: 'put',
data: params
})
export const deleteContract = (params) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract/delete`,
method: 'delete',
data: params
});
export const getContractDetail = (guid) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract/detail?guid=${guid}`,
method: 'get',
});
/** 查询协商信息详情 */
export const getContractNegotiate = (guid) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract/get-negotiate?guid=${guid}`,
method: 'post',
});
/** 根据版本获取协商信息 */
export const getContractNegoPlicyByVersion = (params) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract/get-policy-by-version?contractGuid=${params.guid}&version=${params.version}`,
method: 'get'
});
/** 拒绝本次合约 */
export const rejectContract = (guid) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract/reject?guid=${guid}`,
method: 'post',
});
/** 确认本次合约 */
export const confirmContract = (guid) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract/confirm?guid=${guid}`,
method: 'post',
});
/** 继续本地合约协商 */
export const continueContractNegotiate = (params) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract/continue-negotiate`,
method: 'post',
data: params
});
/** 获取可选的企业下拉列表,认证过之后带标识的 */
export const getContractTenantList = () => request({
url: `${import.meta.env.VITE_APP_PERSONAL_URL}/tenant/get-social-credit-code-tenant`,
method: 'post'
})
/** 撤回合约 */
export const cancelContract = (guid) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract/canal?guid=${guid}`,
method: 'post',
});
/** 签署合约 */
export const signContract = (params) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract-signature/save`,
method: 'post',
data: params
});
/** 获取签署合约详情 */
export const getSignListInfo = (guid) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract-signature/list-by-contract-guid?contractGuid=${guid}`,
method: 'post',
});
/** ----------------- 合约履约信息 ----------------- */
export const getContractProof = (guid) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/proof-execution/proof-by-contract-guid?contractGuid=${guid}`,
method: 'get'
});
/** 获取履约签名 */
export const getContractExecList = (params) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/proof-execution/execution-list-by-contract-guid`,
method: 'post',
data: params
});
/** 解除合同 */
export const terminateContract = (params) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract-terminate/save`,
method: 'post',
data: params
});
/** 获取终止合约信息 */
export const getTerminateDetailInfo = (guid) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract-terminate/detail?contractGuid=${guid}`,
method: 'get'
});
/** 日志管理-操作记录 */
export const getContractOperationLog = (params) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract-operation-log/page-list`,
method: 'post',
data: params
})
/** 操作记录详情 */
export const getContractOperationLogDetail = (guid) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract-operation-log/detail?guid=${guid}`,
method: 'get'
})
/** 日志管理-过程记录 */
export const getContractProcessLog = (params) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract-process-log/page-list`,
method: 'post',
data: params
})
export const getContractProcessLogDetail = (guid) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract-process-log/detail?guid=${guid}`,
method: 'get'
})
/** ------------ 合约统计记录 -------------- */
/** 获取备案统计 */
export const getContractStatis = () => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract/get-contract-statistics`,
method: 'get'
})
export const getContractMonthStatis = () => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract-signature/get-month-signature`,
method: 'get'
})
/** 获取合约履行异常预警记录 */
export const getLogTableList = (params) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract-process-log/page-abnormal-warning`,
method: 'post',
data: params
})
/** 生成数字签名文件 */
export const getSignatureFile = (params, data) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract-signature/get-signature-base?pageIndex=${data.pageIndex}&yPosition=${data.yPosition}`,
method: 'post',
data: params,
headers: {
'Content-Type': 'multipart/form-data'
}
})
......@@ -363,4 +363,31 @@ a {
.mb10 {
margin-bottom: 10px;
}
.el-tabs.log-tabs {
height: 100%;
.el-tabs__header {
margin-bottom: 0;
}
.el-tabs__item {
height: 32px;
padding: 0px;
width: 90px;
&:last-child {
width: 90px;
}
}
.el-tabs__content {
height: calc(100% - 32px);
}
.el-tab-pane {
padding: 0px 16px;
height: 100%;
}
}
\ No newline at end of file
......
......@@ -24,8 +24,7 @@ const props = defineProps({
const emits = defineEmits(["expand"]);
// const isExpanded = ref(true);
const isExpanded = ref(props.isExpand);
const isExpanded = ref(true);
watch(
() => props.isExpand,
......@@ -47,10 +46,14 @@ const expandSwicthHandler = () => {
</script>
<template>
<ElCard class="v-content-wrap" shadow="never" :body-style="{
padding: `0px`,
height: `${isExpanded ? contentHeight + 28 : 0}px`,
}">
<ElCard
class="v-content-wrap"
shadow="never"
:body-style="{
padding: `0px`,
height: `${isExpanded ? contentHeight + 28 : 0}px`,
}"
>
<template v-if="title" #header>
<div class="card-title" @click="expandSwicthHandler">
<span v-if="expandSwicth" style="padding-right: 5px; cursor: pointer">
......@@ -101,7 +104,6 @@ const expandSwicthHandler = () => {
color: var(--el-text-color-primary);
line-height: 21px;
font-weight: 600;
flex-shrink: 0;
}
.desc {
......
......@@ -9,6 +9,18 @@ export const useValidator = () => {
}
}
const requiredFiles = (message?: string) => {
return {
validator: (rule: any, value: any, callback: any) => {
if (!value?.length) {
callback(new Error('请上传文件'))
} else {
callback();
}
}, trigger: 'change'
}
}
// element scrollToError的优化
const scrollToError = () => {
nextTick(() => {
......@@ -175,6 +187,111 @@ export const useValidator = () => {
trigger: "blur",
};
}
/** 验证输入的多个IP地址 */
const validateIPList = (): FormItemRule => {
return {
validator: (rule: any, value: any, callback: any) => {
let ips = value?.split(",");
if (!ips) {
callback(new Error(`请填写合法的IP地址`));
return;
}
for (const ipV of ips) {
let ip = ipV.trim();
if (!ip || (!ipRegex.v4().test(ip) && !ipRegex.v6().test(ip))) {
callback(new Error(`请填写合法的IP地址`));
return;
}
}
callback();
},
trigger: "blur",
};
};
/** 验证输入的多个域名 */
const validateDomainList = (): FormItemRule => {
return {
validator: (rule: any, value: any, callback: any) => {
let domains = value?.split(",");
if (!domains) {
callback(new Error(`请填写合法的IP地址`));
return;
}
// 简单的域名验证正则表达式
const domainRegex =
/^(?!-)(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])\.?)+[a-zA-Z]{2,}$/;
// 更严格的正则,符合RFC标准
const strictDomainRegex =
/^((?!-)[A-Za-z0-9-]{1,63}(?<!-)\.)+[A-Za-z]{2,}$/;
// 国际化域名支持(Punycode编码)
const idnRegex =
/^(xn--[a-zA-Z0-9]+|(?!-)(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])\.?)+[a-zA-Z]{2,})$/;
for (const dv of domains) {
let domain = dv.trim();
if (!domain) {
callback(new Error("请填写合法的域名,不能包含连续的逗号"));
return;
}
// 检查长度限制
if (domain.length > 253) {
callback(new Error("单个域名长度不能超过253个字符"));
return;
}
// 检查是否以点开头或结尾
if (domain.startsWith(".") || domain.endsWith(".")) {
callback(new Error("域名不能以点开头或结尾"));
return;
}
// 检查连续的点
if (domain.includes("..")) {
callback(new Error("域名不能包含连续的点"));
return;
}
// 验证每个标签(以点分隔的部分)
const labels = domain.split(".");
const tld = labels[labels.length - 1];
// 检查顶级域名(TLD)不能全是数字
if (/^\d+$/.test(tld)) {
callback(new Error("顶级域名不能全是数字"));
return;
}
// 检查每个标签的长度和内容
for (let i = 0; i < labels.length; i++) {
const label = labels[i];
// 标签长度检查
if (label.length < 1 || label.length > 63) {
callback(new Error(`标签"${label}"长度必须在1-63个字符之间`));
return;
}
// 标签内容检查
if (!/^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/.test(label)) {
callback(new Error(`标签"${label}"格式不正确`));
return;
}
}
// 使用正则表达式验证
if (!strictDomainRegex.test(domain) && !idnRegex.test(domain)) {
callback(new Error(`域名格式不符合规范`));
return;
}
}
callback();
},
trigger: "blur",
};
};
return {
required,
......@@ -184,6 +301,9 @@ export const useValidator = () => {
description,
chOrEnPreffix,
scrollToError,
checkExistName
checkExistName,
requiredFiles,
validateIPList,
validateDomainList
}
}
......
import type { RouteRecordRaw } from 'vue-router'
function Layout() {
return import('@/layouts/index.vue')
}
const routes: RouteRecordRaw[] = [
{
path: '/data-smart-contract/strategy-management',
component: Layout,
meta: {
title: '策略管理',
icon: 'sidebar-videos',
},
children: [{
path: '',
name: 'strategyManagement',
component: () => import('@/views/data_smart_contract/strategyManagement.vue'),
meta: {
title: '',
sidebar: false,
breadcrumb: false,
cache: true,
editPage: true
},
}]
},
{
path: '/data-smart-contract/contract-template',
component: Layout,
meta: {
title: '合约模板',
icon: 'sidebar-videos',
},
children: [{
path: '',
name: 'contractTemplateManagement',
component: () => import('@/views/data_smart_contract/contractTemplateManagement.vue'),
meta: {
title: '',
sidebar: false,
breadcrumb: false,
cache: true
},
}, {
path: 'contract-template-create',
name: 'contractTemplateCreate',
component: () => import('@/views/data_smart_contract/contractTemplateCreate.vue'),
meta: {
title: '新建合约模板',
sidebar: false,
breadcrumb: false,
cache: true,
editPage: true,
reuse: true
},
beforeEnter: (to, from) => {
to.meta.title = !to.query.guid ? '新建合约模板' : `编辑-${to.query.name}`;
to.meta.editPage = true;
}
}]
},
{
path: '/data-smart-contract/contract-manage',
component: Layout,
meta: {
title: '合约管理',
icon: 'sidebar-videos',
},
children: [{
path: '',
name: 'smartContractManagement',
component: () => import('@/views/data_smart_contract/smartContractManagement.vue'),
meta: {
title: '',
sidebar: false,
breadcrumb: false,
cache: true,
editPage: true
},
}, {
path: 'samart-contract-create',
name: 'smartContractCreate',
component: () => import('@/views/data_smart_contract/smartContractCreate.vue'),
meta: {
title: '新建合约',
sidebar: false,
breadcrumb: false,
cache: true,
editPage: true,
reuse: true
},
beforeEnter: (to, from) => {
to.meta.title = !to.query.guid ? '新建合约' : `编辑合约-${to.query.name}`;
to.meta.editPage = true;
}
}, {
path: 'smart-contract-detail',
name: 'smartContractDetail',
component: () => import('@/views/data_smart_contract/smartContractDetail.vue'),
meta: {
title: '合约详情-',
sidebar: false,
breadcrumb: false,
cache: true,
editPage: true,
reuse: true
},
beforeEnter: (to, from) => {
if (to.query.type == 'consult') {
to.meta.title = `合约协商-${to.query.name}`;
} else if (to.query.type == 'sign') {
to.meta.title = `合约签署-${to.query.name}`;
} else if (to.query.type == 'reject') {
to.meta.title = `合约解除-${to.query.name}`;
} else {
to.meta.title = `合约详情-${to.query.name}`;
}
}
}]
},
{
path: '/data-smart-contract/contract-record-manage',
component: Layout,
meta: {
title: '合约备案',
icon: 'sidebar-videos',
},
children: [{
path: '',
name: 'contractRecordManage',
component: () => import('@/views/data_smart_contract/contractRecordManage.vue'),
meta: {
title: '',
sidebar: false,
breadcrumb: false,
cache: true,
editPage: true
},
}]
},
{
path: '/data-smart-contract/contract-log-manage',
component: Layout,
meta: {
title: '合约日志管理',
icon: 'sidebar-videos',
},
children: [{
path: '',
name: 'contractLogManage',
component: () => import('@/views/data_smart_contract/contractLogManage.vue'),
meta: {
title: '',
sidebar: false,
breadcrumb: false,
cache: true,
editPage: true
},
}]
},
{
path: '/data-smart-contract/exec-cnt-index',
component: Layout,
meta: {
title: '合约履行监测',
icon: 'sidebar-videos',
},
children: [{
path: '',
name: 'execCntIndex',
component: () => import('@/views/data_smart_contract/execCntIndex.vue'),
meta: {
title: '',
sidebar: false,
breadcrumb: false,
cache: true,
editPage: true
},
}]
},
]
export default routes
\ No newline at end of file
......@@ -2,8 +2,8 @@ import { setupLayouts } from 'virtual:meta-layouts'
import generatedRoutes from 'virtual:generated-pages'
import type { RouteRecordRaw } from 'vue-router'
import DataAssess from './modules/dataAsset';
import DataAssetRegistry from './modules/dataAssetRegistry';
import DataService from './modules/dataService';
import DataSmartContract from './modules/dataSmartContract';
import useSettingsStore from '@/store/modules/settings'
......@@ -92,6 +92,7 @@ const systemRoutes: RouteRecordRaw[] = [
const asyncRoutes: RouteRecordRaw[] = [
...DataAssess,
...DataService,
...DataSmartContract,
// ...DataAssetRegistry,
]
......
const useDataSmartContract = defineStore(
'isRefresh',
() => {
const isRefresh = ref<boolean>(false)
function set(v: boolean) {
isRefresh.value = v;
}
return {
isRefresh,
set,
}
},
)
export default useDataSmartContract
\ No newline at end of file
......@@ -5,7 +5,7 @@ import router from '@/router'
import { ElMessage } from 'element-plus'
import apiUser from '@/api/modules/user'
import { getCurrentTime } from '@/utils/common'
import { getSystemMenu, getUserInfo, getTokenByCode, loginOut, refreshToken, editPasswordInterface, getCurrentUserInfo } from '@/api/modules/queryService'
import { getSystemMenu, getUserInfo, getTokenByCode, loginOut, refreshToken, editPasswordInterface, getCurrentUserInfo, getTenantDetailInfo } from '@/api/modules/queryService'
const useUserStore = defineStore(
// 唯一ID
......@@ -57,6 +57,13 @@ const useUserStore = defineStore(
currentTenantGuid.value = res.data.tenantInfoList && res.data.tenantInfoList.length ? res.data.tenantInfoList[0].guid : '';
localStorage.setItem('currentTenantGuid', currentTenantGuid.value);
let currentTenant = res.data.tenantInfoList?.[0];
getTenantDetailInfo(currentTenantGuid.value).then((res: any) => {
if (res.code == '00000') {
localStorage.setItem('tenantInfo', JSON.stringify(res.data));
} else {
ElMessage.error(res.msg)
}
})
return getCurrentUserInfo({tenantGuid: currentTenantGuid.value}).then((res: any) => {
console.log(res, 'getCurrentUserInfo');
if (res.code == '00000') {
......
......@@ -565,7 +565,33 @@ export const tagType = (row, type): any => {
break
default:state = "info"
}
}else if(type=="releaseStatus") {
} else if (type == 'contractStatus') {
switch(row[type]) {
case '01':
state = 'primary'
break;
case '02':
state = 'primary'
break;
case '03':
state = 'primary'
break;
case '0301':
state = 'success'
break;
case '0302':
state = 'danger'
break;
case '05':
state = 'warning'
break;
case '06':
case '00':
state = 'info'
break;
default: state = 'info'
}
} else if(type=="releaseStatus") {
switch (row[type]) {
case 1:
state = 'info'; //待发布
......@@ -889,7 +915,35 @@ export const tagMethod = (row, type) => {
break;
default: tag = "待发布"
}
}else if (type == 'success') {
} else if (type == 'contractStatus') {
switch(row[type]) {
case '00':
tag = '已撤回'
break;
case '01':
tag = '发起'
break;
case '02':
tag = '协商'
break;
case '03':
tag = '签订'
break;
case '0301':
tag = '签订成功'
break;
case '0302':
tag = '签订失败'
break;
case '05':
tag = '履行中'
break;
case '06':
tag = '终止'
break;
default: tag = "发起"
}
} else if (type == 'success') {
switch (row[type]) {
case true:
tag = '成功'
......
......@@ -21,6 +21,7 @@ import {
} from "@/api/modules/dataService";
import useDataServiceStore from "@/store/modules/dataService";
import useUserStore from "@/store/modules/user";
import { changeNum } from '@/utils/common';
const router = useRouter();
const dataServiceStore = useDataServiceStore();
......@@ -162,17 +163,21 @@ const tableInfo = ref({
{
label: "平均响应时间(s)", field: "averageRespTime", width: 130, align: 'right', type: 'chnum'
},
{
label: "绑定连接器数", field: "bindingCount", width: 130, align: 'right', type: "text_btn", value: "detail", click: (scope) => {
if (!scope.row.bindingCount) {
proxy.$ElMessage.warning('当前没有绑定连接器');
return;
}
//TODO, 查看连接器信息。
}, getName: (scope) => {
return scope.row.bindingCount != null ? changeNum(scope.row.bindingCount ?? 0) : '--';
}
},
{ label: "API请求路径", field: "requestUrl", width: TableColumnWidth.DESCRIPTION },
{
label: '状态', field: 'apiState', type: 'switch', activeText: '启用', inactiveText: '停用', activeValue: 1, inactiveValue: 0, switchWidth: 56, width: 96, align: 'center'
// , isDisabled: (scope) => {
// if (scope.row.apiState == 1) {//被授权的不能禁用。
// return true;
// }
// return scope.row.approveState != 'Y';//正在审批中的不能停用。草稿中的不能启用。
// }
},
// { label: "审批状态", field: "approveState", type: "tag", width: TableColumnWidth.STATE, align: 'center' },
{ label: "API描述", field: "apiDescription", width: TableColumnWidth.DESCRIPTION },
{ label: "修改人", field: "updateUserName", width: TableColumnWidth.USERNAME },
{ label: "修改时间", field: "updateTime", width: 170 },
......
<script lang="ts" setup name="confirmDialog">
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
type: {
type: String,
default: 'success'
},
msg: {
type: String,
default: ''
}
});
const dialogVisible = computed(() => {
return props.visible;
});
const emits = defineEmits([
"btnClick",
]);
const cancelDialog = () => {
emits("btnClick", 'cancel');
}
const submit = () => {
emits("btnClick", 'submit');
}
const dialogClose = () => {
emits("btnClick", 'cancel');
}
const imgSrc = computed(() => {
return new URL(`../../../assets/images/confirmType/${props.type}.png`, import.meta.url).href;
})
</script>
<template>
<el-dialog v-model="dialogVisible" title="规则详情" width="460" :modal="true" :close-on-click-modal="true"
destroy-on-close align-center @close="dialogClose">
<div class="content">
<img class="header-img" :src="imgSrc"></img>
<div class="title" v-html="msg"></div>
</div>
<div class="dialog-footer">
<el-button @click="cancelDialog">取消</el-button>
<el-button @click="submit" type="primary" v-preReClick>确定</el-button>
</div>
</el-dialog>
</template>
<style lang="scss" scoped>
.content {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.header-img {
width: 126px;
height: 72px;
margin-bottom: 12px;
}
.title {
width: 364px;
font-size: 16px;
color: #212121;
text-align: center;
line-height: 24px;
font-weight: 400;
margin-bottom: 32px;
white-space: pre-line;
}
}
.dialog-footer {
width: 100%;
display: flex;
justify-content: center;
flex-direction: row;
margin-bottom: 12px;
}
</style>
\ No newline at end of file
<script lang="ts" setup name="showFile">
import { onUploadFilePreview, onUploadFileDownload } from '@/api/modules/common';
const props = defineProps({
file: {
type: Array<any>,
default: []
},
showRemove: {
type: Boolean,
default: false,
}
});
const emits = defineEmits(['deleteFile'])
const handleUploadFileRemove = (file) => {
emits("deleteFile");
}
</script>
<template>
<span v-for="(item) in (file || [])" class="item_value" :style="{ 'padding-left': '0px' }">
<div class="file-operate">
<template
v-if="item.name.substring(item.name.lastIndexOf('.') + 1).toLowerCase() == 'xls' || item.name.substring(item.name.lastIndexOf('.') + 1).toLowerCase() == 'xlsx' || item.name.substring(item.name.lastIndexOf('.') + 1).toLowerCase() == 'csv'">
<img class="file-img" src="../../../assets/images/excel.png" />
</template>
<template
v-else-if="item.name.substring(item.name.lastIndexOf('.') + 1).toLowerCase() == 'doc' || item.name.substring(item.name.lastIndexOf('.') + 1).toLowerCase() == 'docx'">
<img class="file-img" src="../../../assets/images/word.png" />
</template>
<template v-else-if="item.name.substring(item.name.lastIndexOf('.') + 1).toLowerCase() == 'zip'">
<img class="file-img" src="../../../assets/images/zip.png" />
</template>
<template v-else-if="item.name.substring(item.name.lastIndexOf('.') + 1).toLowerCase() == 'rar'">
<img class="file-img" src="../../../assets/images/RAR.png" />
</template>
<template v-else-if="item.name.substring(item.name.lastIndexOf('.') + 1).toLowerCase() == 'pdf'">
<img class="file-img" src="../../../assets/images/PDF.png" />
</template>
<template v-else-if="item.name.substring(item.name.lastIndexOf('.') + 1).toLowerCase() == 'png'">
<img class="file-img" src="../../../assets/images/png.png" />
</template>
<template
v-else-if="item.name.substring(item.name.lastIndexOf('.') + 1).toLowerCase() == 'jpg' || item.name.substring(item.name.lastIndexOf('.') + 1).toLowerCase() == 'jpeg'">
<img class="file-img" src="../../../assets/images/jpg.png" />
</template>
<div v-if="props.showRemove" :style="{ right: '72px' }" class="file-preview"
@click="handleUploadFileRemove(item)">删除
</div>
<div class="file-name" :style="{ width: showRemove ? 'calc(100% - 150px)' : 'calc(100% - 120px)' }"><ellipsis-tooltip :content="item.name ?? ''" class-name="w100f"
refName="tooltipOver"></ellipsis-tooltip></div>
<div :style="{ right: '36px' }"
v-if="item.name.substring(item.name.lastIndexOf('.') + 1).toLowerCase() == 'pdf' || item.name.substring(item.name.lastIndexOf('.') + 1).toLowerCase() == 'png' || item.name.substring(item.name.lastIndexOf('.') + 1).toLowerCase() == 'jpg' || item.name.substring(item.name.lastIndexOf('.') + 1).toLowerCase() == 'jpeg'"
class="file-preview" @click="onUploadFilePreview(item)">查看</div>
<div :style="{ right: '0px' }" class="file-preview" @click="onUploadFileDownload(item)">下载</div>
</div>
</span>
</template>
<style lang="scss" scoped>
.file-operate {
display: flex;
align-items: center;
position: relative;
.file-img {
width: 24px;
height: 24px;
}
&:hover {
background-color: #f5f5f5;
}
.file-name {
width: calc(100% - 120px);
color: var(--el-color-regular);
margin-left: 4px;
}
.file-preview {
position: absolute;
cursor: pointer;
color: var(--el-color-primary);
margin-right: 8px;
}
}
</style>
\ No newline at end of file
<template>
<div v-if="showTitle" class="title-row">
<div class="h-title">策略信息</div>
<el-button v-if="!isLook" plain @click="invokeTemplate" v-preReClick>合约模板调用</el-button>
</div>
<el-table class="strategyTable" ref="strategyTableRef" v-loading="strategyDataLoading" :data="strategyData"
:height="isReport ? 'auto' : '250px'" :highlight-current-row="true" stripe tooltip-effect="light" border :span-method="arraySpanMethod">
<el-table-column label="序号" width="56" align="center" fixed="left" :formatter="formatIndex" />
<el-table-column prop="action" label="操作行为" :width="isReport ? 'auto' : '150px'" :min-width="isReport ? '100px' : undefined" align="left" :show-overflow-tooltip="!isReport">
<template #header>
<span>操作行为</span>
<span style="color:red;margin-left: 2px;">*</span>
</template>
<template #default="scope">
<el-select v-if="!isLook" v-model="scope.row['action']" placeholder="请选择" :disabled="isLook"
@change="(val) => selectOperationChange(val, scope)" clearable filterable>
<el-option
v-for="opt in actionOptionsList.filter(a => scope.row.action == a.policyName || !strategyData.some(s => s.action == a.policyName))"
:key="opt['policyName']" :label="opt['policyName']" :value="opt['policyName']" />
</el-select>
<span v-else>{{ scope.row['action'] || '--' }}</span>
</template>
</el-table-column>
<el-table-column prop="actionEnName" label="操作行为英文名称" :width="isReport ? 'auto' : '150px'" :min-width="isReport ? '120px' : undefined" align="left" :show-overflow-tooltip="!isReport">
<template #default="scope">
<el-input v-if="!isLook" v-model="scope.row['actionEnName']" :disabled="true" placeholder="-"></el-input>
<span v-else>{{ scope.row['actionEnName'] || '--' }}</span>
</template>
</el-table-column>
<el-table-column prop="constraintName" label="约束条件" :width="isReport ? 'auto' : '150px'" :min-width="isReport ? '100px' : undefined" align="left" :show-overflow-tooltip="!isReport">
<template #header>
<span>约束条件</span>
<span style="color:red;margin-left: 2px;">*</span>
</template>
<template #default="scope">
<el-select v-if="!isLook" v-model="scope.row['constraintName']" placeholder="请选择" :disabled="isLook"
@change="(val) => selectConditionChange(val, scope)" clearable filterable>
<el-option v-for="opt in constraintOptionsList" :key="opt['policyName']" :label="opt['policyName']"
:value="opt['policyName']" />
</el-select>
<span v-else>{{ scope.row['constraintName'] || '--' }}</span>
</template>
</el-table-column>
<el-table-column prop="constraintEnName" label="约束条件英文名称" :width="isReport ? 'auto' : '150px'" :min-width="isReport ? '120px' : undefined" align="left" :show-overflow-tooltip="!isReport">
<template #default="scope">
<el-input v-if="!isLook" v-model="scope.row['constraintEnName']" :disabled="true" placeholder="-"></el-input>
<span v-else>{{ scope.row['constraintEnName'] || '--' }}</span>
</template>
</el-table-column>
<el-table-column prop="constraintOperatorCode" label="运算符" :width="isReport ? 'auto' : '150px'" :min-width="isReport ? '100px' : undefined" align="left" :show-overflow-tooltip="!isReport">
<template #header>
<span>运算符</span>
<span style="color:red;margin-left: 2px;">*</span>
</template>
<template #default="scope">
<el-select v-if="!isLook" v-model="scope.row['constraintOperatorCode']" placeholder="请选择" :disabled="isLook"
clearable filterable @change="(val) => handleOperatorSelectChange(val, scope, 'constraintOperatorCode')">
<el-option v-for="opt in getActualOperationList(scope)" :key="opt['value']" :label="opt['label']"
:value="opt['value']" />
</el-select>
<span v-else>{{ scope.row['constraintOperatorName'] || '--' }}</span>
</template>
</el-table-column>
<el-table-column prop="constraintValue" label="约束值" :width="isReport ? 'auto' : '240px'" :min-width="isReport ? '160px' : undefined" align="left" :show-overflow-tooltip="!isReport">
<template #header>
<span>约束值</span>
<span style="color:red;margin-left: 2px;">*</span>
</template>
<template #default="scope">
<el-input v-if="!isLook" v-model="scope.row['constraintValue']" placeholder="请输入" :maxlength="200"
@change="(val) => handleOperatorSelectChange(val, scope, 'constraintValue')" clearable></el-input>
<span v-else>{{ scope.row['constraintValue'] || '--' }}</span>
</template>
</el-table-column>
<el-table-column v-if="!isLook" label="操作" width="140px" align="left" fixed="right" show-overflow-tooltip>
<template #default="scope">
<span class="text_btn" @click="handleConditionClickAdd(scope)" v-preReClick>添加约束</span>
<el-divider direction="vertical" />
<span class="text_btn" @click="handleConditionDelete(scope)">删除</span>
</template>
</el-table-column>
</el-table>
<div v-if="!isLook" class="row-add-btn">
<el-button link @click="addStrategy" :icon="CirclePlus" v-preReClick>添加操作行为</el-button>
</div>
<!-- 选择合约模板对话框 -->
<Dialog_form :dialogConfigInfo="templateDialogInfo" />
</template>
<script lang="ts" setup name="strategyTable">
import { useValidator } from "@/hooks/useValidator";
import { CirclePlus } from "@element-plus/icons-vue";
import {
getValidContractTemplateList
} from "@/api/modules/dataSmartContract"
const { required } = useValidator();
const { proxy } = getCurrentInstance() as any;
const props = defineProps({
isLook: { //是否是查看页面,详情查看不需要展示输入框。
type: Boolean,
default: false
},
value: { // 是个表格数组
type: Array<any>,
default: <any>[]
},
showTitle: {
type: Boolean,
default: false
},
actionOptionsList: {//策略操作行为下拉列表
type: Array<any>,
default: []
},
constraintOptionsList: {//约束行为下拉列表
type: Array<any>,
default: []
},
operatorOptionList: {//运算符字典下拉列表
type: Array<any>,
default: []
},
isReport: {
type: Boolean,
default: false
}
})
const getActualOperationList = (scope) => {
let val = scope.row.constraintName;
let conditionItem = val && props.constraintOptionsList.find(c => c.policyName == val);
let constraintOperatorCodes = conditionItem?.constraintOperatorCodes || [];
if (!constraintOperatorCodes?.length) {
return props.operatorOptionList;
}
let list: any = constraintOperatorCodes?.map((c, index) => {
return {
value: c,
label: conditionItem?.constraintOperatorCodesName?.[index] || c
}
});
return list;
}
const strategyDataLoading = ref(false);
/** 策略信息结构最终值,带children层级结构 */
const strategyValueInfo: any = ref([{
index: 1,
action: '',
children: [{ childIndex: 1 }]
}]);
/** 将数据库存储的值转化为该组件接收的树形结构 */
const transferValueToNew = (val) => {
let detailPolicyListValue: any[] = []
val?.forEach((p, index) => {
let lastItem = detailPolicyListValue?.[detailPolicyListValue.length - 1];
let childInfo = Object.assign({}, p);
delete childInfo.children; //删掉冗余信息,否则后续修改会覆盖正确的值
delete childInfo.action;
delete childInfo.actionEnName;
if (!lastItem || lastItem.action != p.action) {
detailPolicyListValue.push(Object.assign({
index: detailPolicyListValue.length + 1,
}, p, {
children: [Object.assign({ childIndex: 1 }, childInfo)]
}))
} else {
lastItem.children.push(Object.assign({ childIndex: lastItem.children?.length + 1 }, childInfo));
}
})
return detailPolicyListValue;
}
watch(() => props.value, (val) => {
strategyValueInfo.value = transferValueToNew(val);
}, {
deep: true,
immediate: true
})
/** 策略信息表格数据 */
const strategyData = computed(() => {
let v: any = [];
strategyValueInfo.value.forEach(s => {
s.children?.forEach(sc => {
v.push(Object.assign({}, {
guid: sc.guid,
action: s.action,
actionEnName: s.actionEnName,
index: s.index,
orderNum: s.orderNum
}, sc));
})
})
return v;
});
/** 操作行为下拉框改变,带出英文名称等信息 */
const selectOperationChange = (val, scope) => {
let index = scope.row.index;
let sv = strategyValueInfo.value.find(s => s.index == index);
sv && (sv.actionEnName = props.actionOptionsList.find(o => o.policyName == val)?.policyEnName);
sv && (sv.action = val);
}
/** 约束条件下拉框改变,带出英文名称 */
const selectConditionChange = (val, scope) => {
let index = scope.row.index;
let sv = strategyValueInfo.value.find(s => s.index == index);
let svChild = sv.children?.find(c => c.childIndex == scope.row.childIndex);
svChild && (svChild.constraintEnName = props.constraintOptionsList.find(o => o.policyName == val)?.policyEnName);
svChild && (svChild.constraintName = val);
}
const handleOperatorSelectChange = (val, scope, field) => {
let index = scope.row.index;
let sv = strategyValueInfo.value.find(s => s.index == index);
let svChild = sv.children?.find(c => c.childIndex == scope.row.childIndex);
svChild && (svChild[field] = val);
}
/** 添加策略信息的操作行为行 */
const addStrategy = () => {
let len = strategyValueInfo.value.length;
if (len == props.actionOptionsList?.length) {
proxy.$ElMessage.warning('暂无可添加的操作行为!');
return;
}
let lastIndex = strategyValueInfo.value.at(-1)?.index || 0;
strategyValueInfo.value.push({
index: lastIndex + 1,
action: '',
children: [{ childIndex: 1 }]
})
}
/** 添加约束条件 */
const handleConditionClickAdd = (scope) => {
/** 根据index值进行添加删除 */
let index = scope.row.index;
let sv = strategyValueInfo.value.find(s => s.index == index);
if (sv) {
let childIndex = scope.row.childIndex;
let lastIndex = sv.children.at(-1)?.childIndex || 0;
sv.children.splice(childIndex, 0, { childIndex: lastIndex + 1 })
}
}
const handleConditionDelete = (scope) => {
let index = scope.row.index;
let sv = strategyValueInfo.value.find(s => s.index == index);
if (!sv) {
return;
}
if (sv?.children?.length > 1) {
//删除约束条件。
proxy.$openMessageBox("确定要删除该约束条件吗?", () => {
let childIndex = scope.row.childIndex;
let delIndex = sv.children.findIndex(c => c.childIndex == childIndex);
delIndex > -1 && sv.children.splice(delIndex, 1);
proxy.$ElMessage.success('删除成功');
}, () => {
proxy.$ElMessage.info("已取消删除");
})
} else {
//同步删除操作行为
proxy.$openMessageBox("确定要删除该操作行为和约束条件吗?", () => {
let index = scope.row.index;
strategyValueInfo.value.splice(index - 1, 1);
proxy.$ElMessage.success('删除成功');
}, () => {
proxy.$ElMessage.info("已取消删除");
})
}
}
// 计算合并后的序号
const mergedIndexes = computed(() => {
let index = 1;
const mergedIndexes: any[] = [];
for (let i = 0; i < strategyData.value.length; i++) {
if (i === 0 || strategyData.value[i - 1]['action'] != strategyData.value[i]['action'] || strategyData.value[i - 1]['index'] != strategyData.value[i]['index']) {
// 新合并组开始
mergedIndexes.push(index);
index++;
} else {
// 同一合并组
mergedIndexes.push(0); // 0表示不显示序号
}
}
return mergedIndexes;
});
// 序号格式化
const formatIndex = (row, column, cellValue, index) => {
return <string>mergedIndexes.value[index] || '';
};
const arraySpanMethod = ({ row, column, rowIndex, columnIndex }) => {
if (columnIndex > 2) {
return [1, 1];
}
// 查找当前字段值相同的连续行
let startRow = rowIndex;
let endRow = rowIndex;
let field = column.field;
// 向前查找
while (startRow > 0 && strategyData.value[startRow - 1][field] === strategyData.value[rowIndex][field] && strategyData.value[startRow - 1]['index'] == strategyData.value[rowIndex]['index']) {
startRow--;
}
// 向后查找
while (endRow < strategyData.value.length - 1 && strategyData.value[endRow + 1][field] === strategyData.value[rowIndex][field] && strategyData.value[endRow + 1]['index'] === strategyData.value[rowIndex]['index']) {
endRow++;
}
// 如果当前行不是相同值组的起始行,则不显示
if (startRow !== rowIndex) {
return [0, 0];
}
// 返回合并的行数
const rowspan = endRow - startRow + 1;
return [rowspan, 1];
}
/** 弹出模板选择对话框 */
const invokeTemplate = () => {
/** 调用获取启用的模板列表接口,如果返回null则提示没有可调用的模板。 */
getValidContractTemplateList().then((res: any) => {
if (res?.code == proxy.$passCode) {
const listData = res.data || [];
if (!listData?.length) {
proxy.$ElMessage.warning('当前没有可调用的合约模板');
return;
}
templateFormItems.value[0].options = listData;
templateFormItems.value[0].default = '';
templateDialogInfo.value.visible = true;
} else {
res?.msg && proxy.$ElMessage.error(res?.msg)
}
})
}
const templateFormItems = ref([{
type: 'select',
label: '合约模板名称',
field: 'guid',
default: '',
block: true,
placeholder: '请选择',
options: [],
props: {
value: 'guid',
label: 'templateName'
},
filterable: true,
clearable: true,
required: true
}]);
const templateFormRules = ref({
guid: [required("请选择合约模板名称")],
});
const templateDialogInfo = ref({
visible: false,
size: 480,
title: "合约模板调用",
type: 'edit',
formInfo: {
id: 'copy-form',
items: templateFormItems.value,
rules: templateFormRules.value
},
btns: {
submit: (btn, info) => {
templateDialogInfo.value.visible = false;
//将选择的值带入表格 TODO,跟经理确认是全量覆盖,还是保留操作行为不同的行。暂时先做成全量覆盖吧。
let item: any = templateFormItems.value[0].options?.find((i: any) => i.guid == info.guid);
strategyValueInfo.value = transferValueToNew(item?.policyRSVOS || []);
},
cancel: () => {
templateDialogInfo.value.visible = false;
}
},
submitBtnLoading: false
});
const validateValue = () => {
for (const d of strategyData.value) {
if (!d.action) {
proxy.$ElMessage.error('操作行为必填,请填写完整');
return false;
}
if (!d.constraintName) {
proxy.$ElMessage.error('约束条件必填,请填写完整');
return false;
}
if (!d.constraintOperatorCode) {
proxy.$ElMessage.error('运算符必填,请填写完整');
return false;
}
if (!d.constraintValue) {
proxy.$ElMessage.error('约束值必填,请填写完整');
return false;
}
}
return true;
}
defineExpose({
strategyData,
validateValue
})
</script>
<style lang="scss" scoped>
.row-add-btn {
.el-button--default {
padding: 4px 0px;
margin-top: 4px;
}
:deep(.el-icon) {
width: 16px;
height: 16px;
svg {
width: 16px;
height: 16px;
}
}
}
.title-row {
display: flex;
flex-direction: row;
height: 44px;
align-items: center;
.el-button {
color: var(--el-button-hover-text-color);
border-color: var(--el-button-hover-border-color);
}
}
.h-title {
font-size: 14px;
color: #212121;
font-weight: 600;
margin-right: 8px;
}
</style>
\ No newline at end of file
<route lang="yaml">
name: contractLogManage
</route>
<script lang="ts" setup name="contractLogManage">
import TableTools from "@/components/Tools/table_tools.vue";
import { commonPageConfig, TableColumnWidth } from "@/utils/enum";
import {
getContractOperationLog,
getContractOperationLogDetail,
getContractProcessLog,
getContractProcessLogDetail,
getContractNegoPlicyByVersion
} from "@/api/modules/dataSmartContract";
import StrategyTable from './components/strategyTable.vue';
const { proxy } = getCurrentInstance() as any;
const activeTabName = ref('operation');
/** 操作主体下拉列表 */
// const operationEntityListData: any = ref([]);
/** 操作记录筛选框 */
const opSearchItemList = ref([{
type: 'date-time',
label: '',
field: 'operatorTime',
default: [],
defaultStartTime: new Date(2000, 1, 1, 0, 0, 0),
defaultEndTime: new Date(2000, 1, 1, 23, 59, 59),
startPlaceholder: '操作开始时间',
endPlaceholder: '操作结束时间',
clearable: true
}, {
type: 'input',
label: '',
field: 'operationEntityName',
default: '',
placeholder: '操作主体',
// props: {
// value: 'tenantGuid',
// label: 'tenantName'
// },
// options: operationEntityListData.value,
//filterable: true,
maxlength: 50,
clearable: true
}, {
type: "input",
label: "",
field: "contractName",
default: "",
placeholder: "合约名称",
maxlength: 50,
clearable: true,
},]);
/** 过程记录筛选框 */
const processTableSearchItemList = ref([{
type: 'date-time',
label: '',
field: 'operatorTime',
default: [],
defaultStartTime: new Date(2000, 1, 1, 0, 0, 0),
defaultEndTime: new Date(2000, 1, 1, 23, 59, 59),
startPlaceholder: '操作开始时间',
endPlaceholder: '操作结束时间',
clearable: true
}, {
type: 'select',
label: '',
field: 'executionProcess',
default: '',
placeholder: '执行环节',
props: {
value: 'value',
label: 'label'
},
options: [{
value: '策略解析',
}, {
value: '行为校验',
}, {
value: '操作执行',
}],
filterable: true,
clearable: true
}, {
type: "input",
label: "",
field: "executionEntityName",
default: "",
placeholder: "执行节点名称",
maxlength: 50,
clearable: true,
},]);
/** ------------------ 操作记录 ----------------------- */
const opPage = ref({
...commonPageConfig,
contractName: '',
operatorTime: [],
operationEntityName: ''
});
const currTableData: any = ref({});
const opTableInfo = ref({
id: "op-table",
rowKey: 'guid',
fields: [
{ label: "序号", type: "index", width: TableColumnWidth.INDEX, align: "center" },
{ label: "操作编号", field: "operationId", width: 140 },
{ label: "操作时间", field: "operationTime", width: 170 },
{ label: "操作主体", field: "operationEntityName", width: 220 },
{ label: "操作合约名称", field: "contractName", width: 160 },
{ label: "操作合约标识", field: "contractId", width: 355 },
{ label: "操作类型", field: "operationType", width: 120 },
],
data: [],
showPage: true,
page: {
type: "normal",
rows: 0,
...opPage.value,
},
actionInfo: {
label: "操作",
type: "btn",
width: 80,
btns: [{
value: 'view', label: '查看', click: (scope) => {
currTableData.value = scope.row;
detailDialogInfo.value.visible = true;
detailDialogInfo.value.contentLoading = true;
getContractOperationLogDetail(scope.row.guid).then((res: any) => {
detailDialogInfo.value.contentLoading = false;
if (res?.code == proxy.$passCode) {
opDetailInfo.value = res.data || {};
} else {
res?.msg && proxy.$ElMessage.error(res?.msg)
}
})
}
}]
},
loading: false
});
const toTableSearch = (val: any, clear: boolean = false) => {
if (clear) {
opSearchItemList.value.map((item) => (item.default = ""));
opPage.value.contractName = '';
opPage.value.operationEntityName = '';
opPage.value.operatorTime = [];
} else {
opPage.value.contractName = val.contractName;
opPage.value.operationEntityName = val.operationEntityName;
opPage.value.operatorTime = val.operatorTime;
}
getOpTableData();
};
const getOpTableData = () => {
opTableInfo.value.loading = true
getContractOperationLog({
pageIndex: opPage.value.curr,
pageSize: opPage.value.limit,
contractName: opPage.value.contractName,
operationEntityName: opPage.value.operationEntityName,
operationTimeStart: opPage.value.operatorTime?.[0],
operationTimeEnd: opPage.value.operatorTime?.[1]
}).then((res: any) => {
opTableInfo.value.data = [];
if (res?.code == proxy.$passCode) {
const data = res.data || {};
opTableInfo.value.loading = false
opTableInfo.value.data = data.records || []
opTableInfo.value.page.limit = data.pageSize
opTableInfo.value.page.curr = data.pageIndex
opTableInfo.value.page.rows = data.totalRows
} else {
res?.msg && proxy.$ElMessage.error(res?.msg)
opTableInfo.value.loading = false
}
}).catch(() => {
opTableInfo.value.loading = false
})
}
const opTablePageChange = (info) => {
opPage.value.curr = Number(info.curr);
opPage.value.limit = Number(info.limit);
opTableInfo.value.page.curr = opPage.value.curr;
opTableInfo.value.page.limit = opPage.value.limit;
getOpTableData();
};
/** ------------------ 过程记录 ----------------------- */
const processPage = ref({
...commonPageConfig,
executionEntityName: '',
operatorTime: [],
executionProcess: ''
});
const processTableInfo = ref({
id: "process-table",
rowKey: 'guid',
fields: [
{ label: "序号", type: "index", width: TableColumnWidth.INDEX, align: "center" },
{ label: "执行编号", field: "operationId", width: 140 },
{ label: "执行时间", field: "operationTime", width: 170 },
{ label: "执行节点名称", field: "executionEntityName", width: 220 },
{ label: "执行节点标识", field: "executionEntityId", width: 175 },
{ label: "执行合约名称", field: "contractName", width: 160 },
{ label: "执行环节", field: "executionProcess", width: 120 },
],
data: [],
showPage: true,
page: {
type: "normal",
rows: 0,
...processPage.value,
},
actionInfo: {
label: "操作",
type: "btn",
width: 80,
btns: [{
value: 'view', label: '查看', click: (scope) => {
currTableData.value = scope.row;
processDetailDialogInfo.value.visible = true;
processDetailDialogInfo.value.contentLoading = true;
getContractProcessLogDetail(scope.row.guid).then((res: any) => {
processDetailDialogInfo.value.contentLoading = false;
if (res?.code == proxy.$passCode) {
processDetailInfo.value = res.data || {};
execContractTableInfo.value.data = processDetailInfo.value.policys || [];
} else {
res?.msg && proxy.$ElMessage.error(res?.msg)
}
})
}
}]
},
loading: false
});
const toProcessTableSearch = (val: any, clear: boolean = false) => {
if (clear) {
processTableSearchItemList.value.map((item) => (item.default = ""));
processPage.value.executionEntityName = '';
processPage.value.executionProcess = '';
processPage.value.operatorTime = [];
} else {
processPage.value.executionEntityName = val.executionEntityName;
processPage.value.executionProcess = val.executionProcess;
processPage.value.operatorTime = val.operatorTime;
}
getProcessTableData();
};
const getProcessTableData = () => {
processTableInfo.value.loading = true
getContractProcessLog({
pageIndex: processPage.value.curr,
pageSize: processPage.value.limit,
executionEntityName: processPage.value.executionEntityName,
executionProcess: processPage.value.executionProcess,
operationTimeStart: processPage.value.operatorTime?.[0],
operationTimeEnd: processPage.value.operatorTime?.[1]
}).then((res: any) => {
processTableInfo.value.data = [];
if (res?.code == proxy.$passCode) {
const data = res.data || {};
processTableInfo.value.loading = false
processTableInfo.value.data = data.records || []
processTableInfo.value.page.limit = data.pageSize
processTableInfo.value.page.curr = data.pageIndex
processTableInfo.value.page.rows = data.totalRows
} else {
res?.msg && proxy.$ElMessage.error(res?.msg)
processTableInfo.value.loading = false
}
}).catch(() => {
processTableInfo.value.loading = false
})
}
const processTablePageChange = (info) => {
processPage.value.curr = Number(info.curr);
processPage.value.limit = Number(info.limit);
processTableInfo.value.page.curr = processPage.value.curr;
processTableInfo.value.page.limit = processPage.value.limit;
getProcessTableData();
};
onBeforeMount(() => {
toTableSearch({});
toProcessTableSearch({});
})
/** 操作记录详情对话框 */
const detailDialogInfo = ref({
visible: false,
size: 855,
direction: "column",
header: {
title: "查看操作记录",
},
type: '',
contents: [],
footer: {
show: false
},
contentLoading: false,
});
/** 查看操作记录详情 */
const opDetailInfo: any = ref({});
const handleDialogBtnClick = (btn) => {
if (btn.value == 'cancel') {
detailDialogInfo.value.visible = false;
}
}
/** 查看操作记录详情对话框 */
const versionDetailDialogInfo = ref({
visible: false,
size: 800,
direction: "column",
header: {
title: "查看版本详情",
},
type: '',
contents: [],
footer: {
show: false
},
contentLoading: false,
});
/** 查看协商策略版本详情 */
const versionDetail: any = ref([]);
const handleVersionDialogBtnClick = (btn) => {
if (btn.value == 'cancel') {
versionDetailDialogInfo.value.visible = false;
}
}
/** 店家查看协商版本 */
const clickVersion = (child) => {
versionDetailDialogInfo.value.visible = true;
versionDetailDialogInfo.value.header.title = '查看版本详情' + '(' + child.version + ')';
versionDetailDialogInfo.value.contentLoading = true;
getContractNegoPlicyByVersion({
guid: currTableData.value.contractGuid,
version: child.version
}).then((res: any) => {
versionDetailDialogInfo.value.contentLoading = false;
if (res?.code == proxy.$passCode) {
const data = res.data || [];
versionDetail.value = data;
} else {
res?.msg && proxy.$ElMessage.error(res?.msg);
}
})
}
/** 操作记录详情对话框 */
const processDetailDialogInfo = ref({
visible: false,
size: 855,
direction: "column",
header: {
title: "查看过程记录",
},
type: '',
contents: [],
footer: {
show: false
},
contentLoading: false,
});
/** 查看操作记录详情 */
const processDetailInfo: any = ref({});
const handleProcessDialogBtnClick = (btn) => {
if (btn.value == 'cancel') {
processDetailDialogInfo.value.visible = false;
}
}
const execContractTableInfo = ref({
id: "exec-contract-table",
height: '214px',
fields: <any[]>[
{ label: "序号", type: "index", width: TableColumnWidth.INDEX, align: "center" },
{ label: "策略id", field: "strategyId", width: 260 },
{ label: "操作行为", field: "action", width: 120 },
{ label: "操作行为英文名称", field: "actionEnName", width: 140 },
{ label: "约束条件", field: "constraintName", width: 120 },
{ label: "约束条件英文名称", field: "constraintEnName", width: 140 },
{ label: "约束条件运算符", field: "constraintOperatorName", width: 140 },
{ label: "约束条件值", field: "constraintValue", width: 150 },
{ label: "执行结果", field: "result", width: 130 },
// { label: "上报时间", field: "reportingTime", width: 170 },
],
data: [],
showPage: false,
actionInfo: {
show: false
},
loading: false
});
</script>
<template>
<el-tabs v-model="activeTabName" class="log-tabs">
<el-tab-pane label="操作记录" name="operation">
<div class="table_tool_wrap">
<TableTools :searchItems="opSearchItemList" :init="false" searchId="op-table-search" @search="toTableSearch" />
</div>
<div class="table_panel_wrap">
<Table :tableInfo="opTableInfo" @tablePageChange="opTablePageChange" />
</div>
</el-tab-pane>
<el-tab-pane label="过程记录" name="process">
<div class="table_tool_wrap">
<TableTools :searchItems="processTableSearchItemList" :init="false" searchId="process-table-search"
@search="toProcessTableSearch" />
</div>
<div class="table_panel_wrap">
<Table :tableInfo="processTableInfo" @tablePageChange="processTablePageChange" />
</div>
</el-tab-pane>
<Dialog ref="dialogRef" :dialogInfo="detailDialogInfo" class="log-table-detail" @btnClick="handleDialogBtnClick">
<template #extra-content>
<div class="main-content" v-loading="detailDialogInfo.contentLoading">
<div class="list_panel">
<div class="list_item is_block">
<span class="item_label">操作类型:</span>
<span class="item_value">{{ opDetailInfo?.operationType || '--' }}</span>
</div>
<div class="list_item">
<span class="item_label">合约标识:</span>
<span class="item_value"><ellipsis-tooltip :content="opDetailInfo?.contractId || '--'"
class-name="w100f mr8-i" :refName="'tooltipOver' + 'contractId'"></ellipsis-tooltip></span>
</div>
<div class="list_item">
<span class="item_label">合约名称:</span>
<span class="item_value"><ellipsis-tooltip :content="opDetailInfo?.contractName || '--'"
class-name="w100f mr8-i" :refName="'tooltipOver' + 'contractName'"></ellipsis-tooltip></span>
</div>
<div class="list_item">
<span class="item_label">操作主体标识:</span>
<span class="item_value"><ellipsis-tooltip :content="opDetailInfo?.operationEntityId || '--'"
class-name="w100f mr8-i" :refName="'tooltipOver' + 'operationEntityId'"></ellipsis-tooltip></span>
</div>
<div class="list_item">
<span class="item_label">操作主体名称:</span>
<span class="item_value"><ellipsis-tooltip :content="opDetailInfo?.operationEntityName || '--'"
class-name="w100f mr8-i" :refName="'tooltipOver' + 'operationEntityName'"></ellipsis-tooltip></span>
</div>
<div class="list_item is_block">
<span class="item_label">操作时间:</span>
<span class="item_value">{{ opDetailInfo?.operationTime || '--' }}</span>
</div>
<div class="list_item is_block">
<span class="item_label">操作内容:</span>
<span class="item_value">修改前<span class="link"
@click="clickVersion(opDetailInfo.operationContent?.[0])">{{
opDetailInfo.operationContent?.[0]?.version || '--' }}</span>
<span class="item_value" style="display: block;padding-left: 0px;">修改后<span class="link"
@click="clickVersion(opDetailInfo.operationContent?.[1])">{{
opDetailInfo.operationContent?.[1]?.version || '--' }}</span></span></span>
</div>
</div>
</div>
</template>
</Dialog>
<Dialog ref="processDialogRef" :dialogInfo="processDetailDialogInfo" class="log-process-detail"
@btnClick="handleProcessDialogBtnClick">
<template #extra-content>
<div class="main-content" v-loading="processDetailDialogInfo.contentLoading">
<div class="list_panel">
<div class="list_item">
<span class="item_label">执行时间:</span>
<span class="item_value">{{ processDetailInfo?.operationTime || '--' }}</span>
</div>
<div class="list_item">
<span class="item_label">执行结果:</span>
<span class="item_value"><ellipsis-tooltip :content="processDetailInfo?.executionResult || '--'"
class-name="w100f mr8-i" :refName="'tooltipOver' + 'executionResult'"></ellipsis-tooltip></span>
</div>
<div class="list_item">
<span class="item_label">执行环节:</span>
<span class="item_value"><ellipsis-tooltip :content="processDetailInfo?.executionProcess || '--'"
class-name="w100f mr8-i" :refName="'tooltipOver' + 'executionProcess'"></ellipsis-tooltip></span>
</div>
<div class="list_item">
<span class="item_label">合约名称:</span>
<span class="item_value"><ellipsis-tooltip :content="processDetailInfo?.contractName || '--'"
class-name="w100f mr8-i" :refName="'tooltipOver' + 'contractName'"></ellipsis-tooltip></span>
</div>
<div class="list_item">
<span class="item_label">执行节点标识:</span>
<span class="item_value"><ellipsis-tooltip :content="processDetailInfo?.executionEntityId || '--'"
class-name="w100f mr8-i" :refName="'tooltipOver' + 'executionEntityId'"></ellipsis-tooltip></span>
</div>
<div class="list_item">
<span class="item_label">执行节点名称:</span>
<span class="item_value"><ellipsis-tooltip :content="processDetailInfo?.executionEntityName || '--'"
class-name="w100f mr8-i" :refName="'tooltipOver' + 'executionEntityName'"></ellipsis-tooltip></span>
</div>
<div class="h-title">策略信息</div>
<Table :table-info="execContractTableInfo"></Table>
</div>
</div>
</template>
</Dialog>
<Dialog ref="versionDialogRef" :dialogInfo="versionDetailDialogInfo" class="policy-table-detail"
@btnClick="handleVersionDialogBtnClick">
<template #extra-content>
<div v-loading="versionDetailDialogInfo.contentLoading">
<StrategyTable ref="strategyTableDetailRef" :show-title="true" :is-look="true" :value="versionDetail">
</StrategyTable>
</div>
</template>
</Dialog>
</el-tabs>
</template>
<style lang="scss" scoped>
.table_panel_wrap {
height: calc(100% - 44px);
}
.main-content {
margin: 20px;
}
.list_panel {
display: flex;
flex-wrap: wrap;
display: flex;
align-items: flex-start;
&.main {
.list_item {
width: 25%;
}
}
.list_item {
width: 50%;
line-height: 32px;
font-size: 14px;
color: var(--el-text-color-regular);
display: flex;
justify-content: space-between;
min-width: 120px;
.item_label {
text-align: left;
}
.item_value {
color: var(--el-color-regular);
padding: 0 4px 0 0;
flex: 1;
text-align: justify;
min-width: 0;
.link {
color: var(--el-color-primary);
cursor: pointer;
margin-left: 4px;
}
}
&.is_block {
width: 100%;
.item_value {
white-space: pre-wrap;
}
}
}
}
:deep(.policy-table-detail) {
.dialog_content {
padding: 0px 20px 20px;
}
}
.h-title {
font-size: 14px;
color: #212121;
font-weight: 600;
margin-right: 8px;
margin-bottom: 8px;
}
</style>
\ No newline at end of file
<route lang="yaml">
name: contractRecordManage
</route>
<script lang="ts" setup name="contractRecordManage">
import { ref } from 'vue';
import TableTools from "@/components/Tools/table_tools.vue";
import {
getContractOverviewPageList,
getContractOverviewTenantList
} from "@/api/modules/dataSmartContract"
import useDataSmartContract from "@/store/modules/dataSmartContract";
import { commonPageConfig } from '@/utils/enum';
const userData = JSON.parse(localStorage.userData);
const router = useRouter();
const route = useRoute();
const { proxy } = getCurrentInstance() as any;
const dataSmartContractStore = useDataSmartContract();
/** 发起主体下拉列表数据 */
const initiatorListData: any = ref([]);
const searchItemList = ref([
{
type: "input",
label: "",
field: "contractName",
default: "",
placeholder: "合约名称",
maxlength: 50,
clearable: true,
},
{
type: 'select',
label: '',
field: 'initiatorGuid',
default: '',
placeholder: '发起主体',
props: {
value: 'tenantGuid',
label: 'tenantName'
},
options: initiatorListData.value,
filterable: true,
clearable: true
},
{
type: 'date-time',
label: '',
field: 'signatureTime',
default: [],
defaultStartTime: new Date(2000, 1, 1, 0, 0, 0),
defaultEndTime: new Date(2000, 1, 1, 23, 59, 59),
startPlaceholder: '签署开始时间',
endPlaceholder: '签署结束时间',
clearable: true
},
]);
const tableFields = ref([
{ label: "序号", type: "index", width: 56, align: "center" },
{ label: "合约名称", field: "contractName", width: 160, },
{ label: "签署方式", field: "signModeName", width: 120 },
{ label: "产品名称", field: "productName", width: 180 },
{ label: "合约编号", field: "contractId", width: 355 },
{ label: "发起主体", field: "tenantName", width: 205 },
{ label: "签署时间", field: "signatureTime", width: 170 }
]);
const page = ref({
...commonPageConfig,
contractName: '',
initiatorGuid: '',
signatureTime: [],
});
const currTableData: any = ref({});
const tableInfo = ref({
id: 'contract-table',
rowKey: 'guid',
loading: false,
fields: tableFields.value,
data: [],
page: {
type: "normal",
rows: 0,
...page.value,
},
actionInfo: {
label: "操作",
type: "btn",
width: 80,
btns: (scope) => {
let row = scope.row;
let btns: any = [];
btns.push({
value: 'view', label: '查看', click: () => {
if (scope.row.contractStatus == '05') {
router.push({
name: 'smartContractDetail',
query: {
guid: scope.row.guid,
name: scope.row.contractName,
type: 'keepAgree',
isDetail: 'Y'
}
});
} else {
router.push({
name: 'smartContractDetail',
query: {
guid: scope.row.guid,
name: scope.row.contractName,
isDetail: 'Y'
}
});
}
}
});
return btns;
}
}
});
const toSearch = (val: any, clear: boolean = false) => {
if (clear) {
searchItemList.value.map((item) => (item.default = ""));
page.value.contractName = '';
page.value.initiatorGuid = '';
page.value.signatureTime = [];
} else {
page.value.contractName = val.contractName;
page.value.initiatorGuid = val.initiatorGuid;
page.value.signatureTime = val.signatureTime;
}
getTableData();
};
const getTableData = () => {
getContractOverviewTenantList().then((res: any) => {
if (res?.code == proxy.$passCode) {
initiatorListData.value = res.data || [];
searchItemList.value[1].options = initiatorListData.value;
} else {
res?.msg && proxy.$ElMessage.error(res?.msg)
}
})
tableInfo.value.loading = true
getContractOverviewPageList({
pageIndex: page.value.curr,
pageSize: page.value.limit,
contractName: page.value.contractName,
initiatorGuid: page.value.initiatorGuid,
signatureStartTime: page.value.signatureTime?.[0],
signatureEndTime: page.value.signatureTime?.[1]
}).then((res: any) => {
tableInfo.value.data = [];
if (res?.code == proxy.$passCode) {
const data = res.data || {};
tableInfo.value.loading = false
tableInfo.value.data = data.records || []
tableInfo.value.page.limit = data.pageSize
tableInfo.value.page.curr = data.pageIndex
tableInfo.value.page.rows = data.totalRows
} else {
res?.msg && proxy.$ElMessage.error(res?.msg)
tableInfo.value.loading = false
}
}).catch(() => {
tableInfo.value.loading = false
})
};
const tablePageChange = (info) => {
page.value.curr = Number(info.curr);
page.value.limit = Number(info.limit);
tableInfo.value.page.curr = page.value.curr;
tableInfo.value.page.limit = page.value.limit;
getTableData();
};
onActivated(() => {
if (dataSmartContractStore.isRefresh) {//如果是首次加载,则不需要调用
page.value.curr = 1;
getTableData();
dataSmartContractStore.set(false);
}
})
onBeforeMount(() => {
!dataSmartContractStore.isRefresh && toSearch({})
})
</script>
<template>
<div class="container_wrap">
<div class="table_tool_wrap">
<TableTools :searchItems="searchItemList" :searchId="'contract-search'" @search="toSearch" :init="false" />
</div>
<div class="table_panel_wrap">
<Table :tableInfo="tableInfo" @tablePageChange="tablePageChange" />
</div>
</div>
</template>
<style lang="scss" scoped>
.container_wrap {
padding: 0px 16px;
.table_panel_wrap {
height: calc(100% - 44px);
}
}
:deep(.el-tag.el-tag--primary) {
color: #0E5FD8;
background: #F2F9FF;
border: 1px solid rgba(224, 239, 255, 1);
}
</style>
\ No newline at end of file
<route lang="yaml">
name: contractTemplateCreate
</route>
<script lang="ts" setup name="contractTemplateCreate">
import { useValidator } from "@/hooks/useValidator";
import useUserStore from "@/store/modules/user";
import useDataSmartContract from "@/store/modules/dataSmartContract";
import {
getContractTemplateDetail,
getActionPolicyList,
getConstraintPolicyList,
updateContractTemplate,
saveContractTemplate,
} from "@/api/modules/dataSmartContract"
import {
getParamsList
} from "@/api/modules/queryService";
import StrategyTable from "./components/strategyTable.vue";
const { required } = useValidator();
const router = useRouter();
const route = useRoute();
const { proxy } = getCurrentInstance() as any;
const userStore = useUserStore();
const dataSmartContract = useDataSmartContract();
const fullPath = route.fullPath;
const fullscreenLoading = ref(false);
const expandBase = ref(false);
const expandInfo = ref(false);
const baseInfoFormRef = ref();
const baseInfoFormItems = ref([{
type: 'input',
label: '合约模板名称',
field: 'templateName',
default: '',
placeholder: '请输入',
maxlength: 50,
clearable: true,
required: true
}, {
type: 'input',
label: '版本',
field: 'version',
default: 'v1.0.0',
placeholder: '-',
disabled: true,
clearable: true,
required: false
}, {
type: 'input',
label: '合约模板编号',
field: 'templateId',
default: '',
placeholder: '自动生成',
disabled: true,
clearable: true,
required: false
}, {
label: '启用状态',
type: 'switch',
field: 'bizStatus',
default: 'Y',
placeholder: '请选择',
activeValue: 'Y',
inactiveValue: 'N',
switchWidth: 32,
}, {
label: '模板描述',
type: 'textarea',
placeholder: '请输入',
field: 'description',
default: '',
block: true,
maxlength: 500,
clearable: true,
required: false,
}]);
const baseInfoFormRules = ref({
templateName: [required("请填写合约模板名称")],
});
const strategyTableRef = ref();
const cancel = () => {
proxy.$openMessageBox(
"当前页面尚未保存,确定放弃修改吗?",
() => {
userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath));
router.push({
name: "contractTemplateManagement",
});
},
() => {
proxy.$ElMessage.info("已取消");
}
);
}
const submit = () => {
baseInfoFormRef.value.ruleFormRef?.validate((valid, errorItem) => {
if (valid) {
let validate = strategyTableRef.value?.validateValue();
if (!validate) {
expandInfo.value = true;
return;
}
let params = { ...baseInfoFormRef.value.formInline };
params.policyRQVOS = strategyTableRef.value.strategyData?.map((d, index) => {
return Object.assign({}, d, { orderNum: index + 1 })
});
if (route.query.guid) {
params.guid = route.query.guid;
fullscreenLoading.value = true;
updateContractTemplate(params).then((res: any) => {
fullscreenLoading.value = false;
if (res?.code == proxy.$passCode) {
proxy.$ElMessage.success('编辑合约模板成功');
userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath));
router.push({
name: "contractTemplateManagement",
});
dataSmartContract.set(true);
} else {
res?.msg && proxy.$ElMessage.error(res?.msg);
}
}).catch(() => {
fullscreenLoading.value = false;
});
} else {
fullscreenLoading.value = true;
delete params.version;
saveContractTemplate(params).then((res: any) => {
fullscreenLoading.value = false;
if (res?.code == proxy.$passCode) {
proxy.$ElMessage.success('新建合约模板成功');
userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath));
router.push({
name: "contractTemplateManagement",
});
dataSmartContract.set(true);
} else {
res?.msg && proxy.$ElMessage.error(res?.msg);
}
}).catch(() => {
fullscreenLoading.value = false;
});
}
} else {
expandBase.value = true;
var obj = Object.keys(errorItem);
baseInfoFormRef.value.ruleFormRef?.scrollToField(obj[0]);
}
})
}
const detailInfo: any = ref({});
/** 约束运算符字典下拉 */
const operatorOptionList: any = ref([]);
/** 约束行为下拉列表 */
const constraintOptionsList: any = ref([]);
/** 策略操作行为下拉列表 */
const actionOptionsList: any = ref([]);
onBeforeMount(() => {
if (route.query.guid) {
fullscreenLoading.value = true;
getContractTemplateDetail({ guid: route.query.guid }).then((res: any) => {
fullscreenLoading.value = false;
if (res?.code == proxy.$passCode) {
detailInfo.value = res.data || {};
baseInfoFormItems.value.forEach(item => {
item.default = detailInfo.value[item.field] || "";
});
} else {
res?.msg && proxy.$ElMessage.error(res?.msg);
}
});
}
getParamsList({ dictType: '约束运算符' }).then((res: any) => {
if (res?.code == proxy.$passCode) {
const data = res.data || [];
operatorOptionList.value = data;
} else {
res?.msg && proxy.$ElMessage.error(res?.msg);
}
})
getActionPolicyList().then((res: any) => {
if (res?.code == proxy.$passCode) {
const data = res.data || [];
actionOptionsList.value = data;
} else {
res?.msg && proxy.$ElMessage.error(res?.msg);
}
})
getConstraintPolicyList().then((res: any) => {
if (res?.code == proxy.$passCode) {
const data = res.data || [];
constraintOptionsList.value = data;
} else {
res?.msg && proxy.$ElMessage.error(res?.msg);
}
})
})
</script>
<template>
<div class="container_wrap full" v-loading="fullscreenLoading">
<div class="content_main panel">
<ContentWrap title="模板信息" expandSwicth style="margin-top: 15px" :isExpand="expandBase"
@expand="(v) => (expandBase = v)" description="">
<Form ref="baseInfoFormRef" formId="base-info-form" :itemList="baseInfoFormItems" :rules="baseInfoFormRules"
col="col3" />
</ContentWrap>
<ContentWrap title="策略信息" expandSwicth style="margin-top: 15px" :isExpand="expandInfo"
@expand="(v) => (expandInfo = v)" description="">
<StrategyTable ref="strategyTableRef" :value="detailInfo.policyRSVOS || (route.query.guid ? [] : [{
index: 1,
action: '',
children: [{ childIndex: 1 }]
}])" :operatorOptionList="operatorOptionList" :actionOptionsList="actionOptionsList"
:constraintOptionsList="constraintOptionsList"></StrategyTable>
</ContentWrap>
</div>
<div class="tool_btns">
<div class="btns">
<el-button @click="cancel">取消</el-button>
<el-button type="primary" @click="submit">确定</el-button>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.container_wrap {
overflow: hidden;
.content_main {
height: calc(100% - 45px);
overflow: hidden auto;
&.panel {
padding: 0 16px 16px;
}
}
}
.tool_btns {
height: 44px;
margin: 0 -8px;
display: flex;
justify-content: center;
align-items: center;
border-top: 1px solid #d9d9d9;
}
:deep(.strategyTable.el-table) {
.cell {
padding: 0px 10px;
}
& td.el-table__cell {
padding: 2px 0px;
height: 36px;
}
}
</style>
<route lang="yaml">
name: contractTemplateManagement
</route>
<script lang="ts" setup name="contractTemplateManagement">
import { ref } from 'vue';
import TableTools from "@/components/Tools/table_tools.vue";
import {
getContractTemplatePageList,
copyContractTemplate,
deleteContractTemplate,
updateContractTemplateState
} from "@/api/modules/dataSmartContract"
import useDataSmartContract from "@/store/modules/dataSmartContract";
import { commonPageConfig } from '@/utils/enum';
import { useValidator } from "@/hooks/useValidator";
const router = useRouter();
const route = useRoute();
const { proxy } = getCurrentInstance() as any;
const dataSmartContractStore = useDataSmartContract();
const { required } = useValidator();
const searchItemList = ref([
{
type: "input",
label: "",
field: "templateName",
default: "",
placeholder: "合约模板名称",
maxlength: 50,
clearable: true,
},
{
type: 'select',
label: '',
field: 'bizStatus',
default: '',
placeholder: '启用状态',
options: [
{ label: '启用', value: 'Y' },
{ label: '停用', value: 'N' },
],
filterable: true,
clearable: true
}
]);
const tableFields = ref([
{ label: "序号", type: "index", width: 56, align: "center" },
{ label: "合约模板名称", field: "templateName", width: 220, },
{ label: "版本号", field: "version", width: 100 },
{ label: '启用状态', width: 96, field: 'bizStatus', type: 'switch', activeText: '启用', inactiveText: '停用', activeValue: 'Y', inactiveValue: 'N', align: 'center' },
{ label: "合约模板编号", field: "templateId", width: 190 },
{ label: "修改人", field: "updateUserName", width: 130 },
{ label: "修改时间", field: "updateTime", width: 170 },
]);
const page = ref({
...commonPageConfig,
templateName: '',
bizStatus: ''
});
const currTableData: any = ref({});
const tableInfo = ref({
id: 'contract-template-table',
rowKey: 'guid',
loading: false,
fields: tableFields.value,
data: [],
page: {
type: "normal",
rows: 0,
...page.value,
},
actionInfo: {
label: "操作",
type: "btn",
width: 140,
btns: (scope) => {
let row = scope.row;
return [{
value: 'edit', label: '编辑', click: (scope) => {
router.push({
name: 'contractTemplateCreate',
query: {
guid: scope.row.guid,
name: scope.row.templateName
}
});
}
}, {
value: 'copy', label: '复制', click: copyTemplate
}, {
value: 'delete', label: '删除', click: (scope) => {
proxy.$openMessageBox("此操作将永久删除, 是否继续?", () => {
deleteContractTemplate([scope.row.guid]).then((res: any) => {
if (res?.code == proxy.$passCode) {
page.value.curr = 1;
getTableData();
proxy.$ElMessage.success('删除成功');
} else {
res?.msg && proxy.$ElMessage.error(res?.msg);
}
});
}, () => {
proxy.$ElMessage.info("已取消");
})
}
}]
}
}
});
const tableSwitchBeforeChange = (scope, field, callback) => {
const msg = `确定${scope.row[field] == 'Y' ? '停用' : '启用'}${scope.row.templateName}】?`
proxy.$openMessageBox(msg, () => {
const state = scope.row[field] == 'Y' ? 'N' : 'Y'
const result = tableSwitchChange(state, scope, field)
callback(result)
}, () => {
callback(false)
});
}
const tableSwitchChange = (val, scope, field) => {
return new Promise((resolve, reject) => {
let params = {
guid: scope.row.guid,
bizStatus: val
}
updateContractTemplateState(params).then((res: any) => {
if (res?.code == proxy.$passCode && res.data) {
getTableData();
proxy.$ElMessage({
type: "success",
message: `【${scope.row.templateName}${val == 'Y' ? '启用' : '停用'}成功`,
});
resolve(true)
} else {
res?.msg && proxy.$ElMessage.error(res?.msg)
reject(false)
}
}).catch(() => {
reject(false)
})
})
}
const copyTemplate = (scope) => {
currTableData.value = scope.row;
copyFormItems.value[0].default = scope.row.templateName + '_copy';
copyDialogInfo.value.formInfo.items = copyFormItems.value;
copyDialogInfo.value.visible = true;
}
const toSearch = (val: any, clear: boolean = false) => {
if (clear) {
searchItemList.value.map((item) => (item.default = ""));
page.value.templateName = '';
page.value.bizStatus = "";
} else {
page.value.templateName = val.templateName;
page.value.bizStatus = val.bizStatus;
}
getTableData();
};
const getTableData = () => {
tableInfo.value.loading = true
getContractTemplatePageList({
pageIndex: page.value.curr,
pageSize: page.value.limit,
templateName: page.value.templateName,
bizStatus: page.value.bizStatus
}).then((res: any) => {
tableInfo.value.loading = false
if (res?.code == proxy.$passCode) {
const data = res.data || {}
tableInfo.value.data = data.records || []
tableInfo.value.page.limit = data.pageSize
tableInfo.value.page.curr = data.pageIndex
tableInfo.value.page.rows = data.totalRows
} else {
res?.msg && proxy.$ElMessage.error(res?.msg)
}
}).catch(() => {
tableInfo.value.loading = false
})
};
const tablePageChange = (info) => {
page.value.curr = Number(info.curr);
page.value.limit = Number(info.limit);
tableInfo.value.page.curr = page.value.curr;
tableInfo.value.page.limit = page.value.limit;
getTableData();
};
const newCreate = () => {
router.push({
name: 'contractTemplateCreate'
});
}
onActivated(() => {
if (dataSmartContractStore.isRefresh) {//如果是首次加载,则不需要调用
page.value.curr = 1;
getTableData();
dataSmartContractStore.set(false);
}
})
onBeforeMount(() => {
!dataSmartContractStore.isRefresh && toSearch({})
})
/** -------------- 复制功能 ------------------- */
const copyFormItems = ref([{
type: 'input',
label: '合约模板名称',
field: 'templateName',
default: '',
block: true,
placeholder: '请输入',
maxlength: 50,
clearable: true,
required: true
}]);
const copyFormRules = ref({
templateName: [required("请填写合约模板名称")],
});
const copyDialogInfo = ref({
visible: false,
size: 480,
title: "复制合约模板",
type: 'add',
formInfo: {
id: 'copy-form',
items: copyFormItems.value,
rules: copyFormRules.value
},
btns: {
submit: (btn, info) => {
info.guid = currTableData.value.guid;
copyDialogInfo.value.submitBtnLoading = true;
copyContractTemplate(info).then((res: any) => {
copyDialogInfo.value.submitBtnLoading = false;
if (res.code == proxy.$passCode) {
page.value.curr = 1;
getTableData();
proxy.$ElMessage({
type: 'success',
message: `【${info.templateName}】复制成功`
})
copyDialogInfo.value.visible = false;
} else {
proxy.$ElMessage.error(res.msg);
}
})
},
cancel: () => {
copyDialogInfo.value.visible = false;
}
},
submitBtnLoading: false
});
</script>
<template>
<div class="container_wrap">
<div class="table_tool_wrap">
<TableTools :searchItems="searchItemList" :searchId="'settle-asset-search'" @search="toSearch" :init="false" />
<div class="tools_btns">
<el-button type="primary" @click="newCreate">新增</el-button>
</div>
</div>
<div class="table_panel_wrap">
<Table :tableInfo="tableInfo" @tablePageChange="tablePageChange"
@tableSwitchBeforeChange="tableSwitchBeforeChange" />
</div>
<!-- 复制对话框 -->
<Dialog_form :dialogConfigInfo="copyDialogInfo" />
</div>
</template>
<style lang="scss" scoped>
.container_wrap {
padding: 0px 16px;
}
</style>
\ No newline at end of file
<route lang="yaml">
name: execCntIndex
</route>
<script lang="ts" setup name="execCntIndex">
import { changeNum } from '@/utils/common'
import {
getContractStatis,
getContractMonthStatis,
getLogTableList
} from "@/api/modules/dataSmartContract";
import * as echarts from 'echarts';
import { commonPageConfig } from '@/utils/enum';
const { proxy } = getCurrentInstance() as any;
const detailInfo: any = ref({});
const logTableInfo: any = ref({
id: "plan-detail-table",
loading: false,
// height: 'auto',
// minPanelHeight: '60px',
// minHeight: '60px',
// maxHeight: '250px',
fields: [
{ label: "序号", type: "index", width: 56, align: "center", fixed: true },
{ label: "合约标识", field: "contractId", width: 355 },
{ label: "合约名称", field: "contractName", width: 140 },
{ label: "执行策略id", field: "strategyId", width: 260 },
{ label: "执行时间", field: "operationTime", width: 170 },
{ label: "执行节点标识", field: "executionEntityId", width: 180 },
{ label: "上报时间", field: "operationTime", width: 170 },
{ label: "异常类型", field: "executionResult", width: 120 },
],
data: [],
page: {
type: "normal",
rows: 0,
...commonPageConfig,
},
actionInfo: {
show: false
}
});
const pageChange = (info) => {
logTableInfo.value.page.curr = Number(info.curr);
logTableInfo.value.page.limit = Number(info.limit);
getLogTableListData(true);
};
const getLogTableListData = (isPage = false) => {
isPage && (logTableInfo.value.loading = true);
return getLogTableList({
pageIndex: logTableInfo.value.page.curr,
pageSize: logTableInfo.value.page.limit,
}).then((res: any) => {
logTableInfo.value.loading = false;
if (res?.code == proxy.$passCode) {
const data = res.data || {};
logTableInfo.value.data = data.records || []
logTableInfo.value.page.limit = data.pageSize
logTableInfo.value.page.curr = data.pageIndex
logTableInfo.value.page.rows = data.totalRows
} else {
res?.msg && proxy.$ElMessage.error(res?.msg)
}
})
}
let barChart: any = null;
const barChartData: any = ref([]);
onMounted(() => {
barChart = echarts.init(document.getElementById('bar'));
setBarChartOption(barChart);
})
watch(() => barChartData.value, (val) => {
setBarChartOption(barChart);
}, {
deep: true
})
/** 设置柱形图option,默认只 展示12次的 */
const setBarChartOption = (barChart) => {
return new Promise((resolve, reject) => {
if (!barChartData.value || !Object.keys(barChartData.value).length) {
let option1 = {
title: [
{
text: "",
left: "30px",
top: "30px",
},
{
text: "暂无数据",
left: "center",
top: "center",
textStyle: {
fontFamily: 'SimSun',
fontStyle: "normal",
fontWeight: "400",
fontSize: 18,
color: '#999'
},
},
],
};
barChart.setOption(option1, true);
window.addEventListener("resize", () => {
barChart.resize();
});
return;
}
let getLast12Months = () => {
const months: any = [];
const currentDate = new Date();
for (let i = 11; i >= 0; i--) {
const targetDate = new Date(currentDate);
targetDate.setMonth(targetDate.getMonth() - i);
const year = targetDate.getFullYear();
const month = String(targetDate.getMonth() + 1).padStart(2, '0');
months.push(`${year}-${month}`);
}
return months;
}
// 使用示例
const last12Months = getLast12Months();
let itemXAxisData: any = last12Months;
let itemYAxisData: any = last12Months.map(v => {
return barChartData.value[v] || 0;
});
let option = {
textStyle: {
fontFamily: 'SimSun'
},
color: ['#5B8FF9', '#FF4E00', '#867EEC', '#FDBC3E', '#F48A64', '#276FF5', '#46D0B5'],
title: {
show: false,
left: '30px',
top: '30px',
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
crossStyle: {
color: '#999'
}
},
textStyle: {
align: 'left'
},
},
legend: {
show: false,
right: 0,
textStyle: {
fontSize: 14
},
},
grid: {
left: 30,
right: 60,
bottom: 5,
containLabel: true
},
xAxis: {
type: 'category',
boundaryGap: false,
nameTextStyle: {
color: '#000000'
},
axisLabel: {
interval: 'auto',
textStyle: {
color: '#000000'
},
},
axisTick: {
show: false,
},
axisLine: {
lineStyle: {
color: '#d9d9d9'
}
},
data: itemXAxisData
},
yAxis: {
type: 'value',
name: '',
min: 0,
minInterval: 1,
nameTextStyle: {
color: '#000000'
},
axisLabel: {
textStyle: {
color: '#000000'
}
},
axisLine: {
//y轴
show: false
}
},
series: [{
name: '已签署合约数',
type: 'line',
data: itemYAxisData,
label: {
show: false,
},
tooltip: {
valueFormatter: function (value) {
return changeNum(value, 0);
}
},
yAxisIndex: 0
}]
};
option && barChart.setOption(option, true);
barChart.on('finished', () => {
resolve(true);
});
window.addEventListener('resize', () => {
barChart.resize();
});
});
}
const fullscreenloading = ref(false);
onBeforeMount(() => {
fullscreenloading.value = true;
let ps1 = getContractStatis().then((res: any) => {
if (res?.code == proxy.$passCode) {
detailInfo.value = res.data || {};
} else {
res?.msg && proxy.$ElMessage.error(res?.msg)
}
})
let ps2 = getContractMonthStatis().then((res: any) => {
if (res?.code == proxy.$passCode) {
barChartData.value = res.data || {};
} else {
res?.msg && proxy.$ElMessage.error(res?.msg)
}
})
let ps3 = getLogTableListData();
Promise.all([ps1, ps2, ps3]).then(() => {
fullscreenloading.value = false;
}).catch(() => {
fullscreenloading.value = false;
})
})
</script>
<template>
<div class="content-main" v-loading="fullscreenloading">
<div class="title">合约备案执行统计</div>
<div class="kpi-content">
<div class="border-content">
<span class="text">备案合约数</span>
<span class="number num-color">{{ detailInfo.registerNum != null ? changeNum(detailInfo.registerNum ?? 0) : '--'
}}<span class="unit" v-show="detailInfo.registerNum != null"></span></span>
</div>
<div class="border-content ml16">
<span class="text">备案策略数</span>
<span class="number num-color">{{ detailInfo.registerPolicyNum != null ? changeNum(detailInfo.registerPolicyNum
?? 0) : '--'
}}<span class="unit" v-show="detailInfo.registerPolicyNum != null"></span></span>
</div>
<div class="border-content ml16">
<span class="text">策略执行数</span>
<span class="number num-color">{{ detailInfo.registerPolicyExecutionNum != null ?
(changeNum(detailInfo.registerPolicyExecutionNum ?? 0)) :
'--'
}}<span class="unit" v-show="detailInfo.registerPolicyExecutionNum != null"></span></span>
</div>
<div class="border-content ml16">
<span class="text">签署合约主体数</span>
<span class="number num-color">{{ detailInfo.registerSignatureNum != null ?
changeNum(detailInfo.registerSignatureNum ?? 0) : '--'
}}<span class="unit" v-show="detailInfo.registerSignatureNum != null"></span></span>
</div>
<div class="border-content ml16">
<span class="text">策略执行节点数</span>
<span class="number num-color">{{ detailInfo.registerExecutionNodeNum != null ?
changeNum(detailInfo.registerExecutionNodeNum ?? 0) : '--'
}}<span class="unit" v-show="detailInfo.registerExecutionNodeNum != null"></span></span>
</div>
</div>
<div class="title">合约履行异常预警记录</div>
<Table class="table-h" :tableInfo="logTableInfo" @tablePageChange="pageChange" />
<div class="title">近12个月备案合约趋势</div>
<div class="content-chart-bar" id="bar">
</div>
</div>
</template>
<style lang="scss" scoped>
.content-main {
height: 100%;
overflow-y: auto;
overflow-x: hidden;
padding: 0px 16px 16px;
}
.title {
line-height: 24px;
font-size: 16px;
color: #212121;
font-weight: 600;
margin-top: 12px;
margin-bottom: 8px;
}
.kpi-content {
display: flex;
flex-direction: row;
}
.border-content {
height: 76px;
display: flex;
flex-direction: column;
align-items: flex-start;
padding-left: 16px;
justify-content: center;
border: 1px solid #d9d9d9;
//width: 160px;
flex: 1;
min-width: 150px;
border-radius: 2px;
.number {
line-height: 30px;
font-weight: 700;
font-size: 20px;
.unit {
font-size: 18px;
font-weight: 400;
}
}
.num-color {
color: #212121;
}
.text {
font-size: 14px;
color: #666666;
display: flex;
.el-icon {
color: #b2b2b2;
}
}
}
.ml16 {
margin-left: 16px;
}
.content-chart-bar {
height: 300px;
border: 1px solid #d9d9d9;
padding: 8px;
}
.table-h {
height: 300px !important;
}
</style>
\ No newline at end of file
<route lang="yaml">
name: smartContractCreate
</route>
<script lang="ts" setup name="smartContractCreate">
import { useValidator } from "@/hooks/useValidator";
import useUserStore from "@/store/modules/user";
import {
getContractDetail,
getActionPolicyList,
getConstraintPolicyList,
getContractDataProduct,
getContractTenantList,
saveContract,
updateContract,
} from "@/api/modules/dataSmartContract"
import StrategyTable from "./components/strategyTable.vue";
import { CirclePlus } from "@element-plus/icons-vue";
import { scrollLastRowToView } from "@/utils/common";
import { TableColumnWidth } from "@/utils/enum";
import {
getParamsList,
} from "@/api/modules/queryService";
import useDataSmartContract from "@/store/modules/dataSmartContract";
import { getEnterpriseData } from "@/api/modules/dataIdentify";
const { required } = useValidator();
const userStore = useUserStore();
const userData = JSON.parse(localStorage.userData);
const tenantData = JSON.parse(localStorage.tenantInfo);
const router = useRouter();
const route = useRoute();
const fullscreenLoading = ref(false);
const expandBase = ref(false);
const expandInfo = ref(false);
const expandPolicy = ref(false);
const fullPath = route.fullPath;
const dataSmartContract = useDataSmartContract();
const { proxy } = getCurrentInstance() as any;
const signModeList = ref([]);
const baseInfoFormRef = ref();
const baseInfoFormItems = ref([{
label: '合约名称',
type: 'input',
placeholder: '请输入',
field: 'contractName',
default: '',
maxlength: 50,
clearable: true,
required: true
}, {
label: '签署模式',
type: 'select',
placeholder: '请选择',
field: 'signModeCode',
default: '01',
options: signModeList.value,
props: {
value: 'value',
label: 'label'
},
disabled: false,
required: true,
visible: true
}, {
label: "终止时间",
type: "datetime",
field: "endTime",
defaultTime: new Date(2000, 1, 1, 23, 59, 59),
default: null,
placeholder: "请选择",
clearable: true,
disabledDate: (date) => {
const today = new Date();
// 将 today 设置为 00:00:00,只比较日期部分(本地时间)
const todayStart = new Date(
today.getFullYear(),
today.getMonth(),
today.getDate()
);
// 将 date 也转换为本地日期的开始时间(避免 UTC 问题)
const dateStart = new Date(
date.getFullYear(),
date.getMonth(),
date.getDate()
);
// 禁用 dateStart < todayStart 的日期(即今天之前)
return dateStart.getTime() < todayStart.getTime();
},
required: true,
}, {
label: '合约简介',
type: 'textarea',
focusValue: false,
placeholder: '请输入',
field: 'contractAbstract',
default: '',
clearable: true,
required: false,
block: true,
maxlength: 500
}]);
const baseInfoFormRules = ref({
contractName: [required('请填写合约名称')],
signModeCode: [required('请选择签署模式')],
endTime: [required('请选择终止时间')],
});
const handleFormSelectChange = (val, row, info) => {
if (row.field == 'signModeCode') { //签署模式改变会影响下面的合约策略执行者的显示
let nodeInfo = nodeInfoFormRef.value.formInline;
nodeInfoFormItems.value.forEach((item, index) => {
item.default = nodeInfo[item.field];
if (index == 1) {
item.default = val == '01' ? userData.tenantGuid : '';
} else if (index == 2) {
item.default = val == '01' ? tenantList.value.find(t => t.guid == userData.tenantGuid)?.socialCreditCode : '';
}
})
productList.value = productList.value?.map(d => {
d.disabled = val == '01' && d.enterpriseName != userData.tenantName; //点对点,只能选择自己公司所属产品
return d;
}) || [];
productTableInfo.value.editInfo.dataProductId.options = productList.value;
}
}
/** ----------------- 扩展字段相关信息 ----------------------------- */
const extendTableRef = ref();
const extendData: any = ref([]);
/** 合约扩展字段类型 */
const dataTypeList: any = ref([]);
/** 基本信息的入参定义表格配置 */
const extendTableInfo = ref({
id: "input-extend-table",
height: '214px',
fields: [
{ label: "序号", type: "index", width: TableColumnWidth.INDEX, align: "center" },
{ label: "扩展字段名称", field: "expansionFieldName", width: 160, required: true, columClass: 'edit-colum', type: 'edit' },
{ label: "字段类型", field: "fieldType", width: 140, required: true, columClass: 'edit-colum', type: 'edit' },
{ label: "输入内容", field: "content", width: 300, required: false, columClass: 'edit-colum', type: 'edit' },
],
editInfo: {
expansionFieldName: {
label: '',
type: 'input',
field: 'expansionFieldName',
default: '',
maxlength: 50,
placeholder: '请输入',
clearable: true,
},
fieldType: {
label: '',
type: 'select',
field: 'fieldType',
default: '',
options: dataTypeList.value,
props: {
label: 'label',
value: 'value'
},
placeholder: '请选择',
clearable: false,
filterable: true
},
content: {
label: '',
type: 'input',
field: 'content',
default: '',
maxlength: 200,
placeholder: '请输入',
clearable: true,
}
},
STATUS: 'edit',
data: extendData.value,
showPage: false,
actionInfo: {
show: true,
label: "操作",
type: "btn",
width: 60,
fixed: 'right',
btns: [
{
label: "删除", value: "remove", click: (scope) => {
let index = scope.$index;
proxy.$openMessageBox("此操作将永久删除, 是否继续?", () => {
extendData.value.splice(index, 1);
extendTableInfo.value.data = extendData.value;
proxy.$ElMessage.success('扩展字段删除成功');
}, () => {
proxy.$ElMessage.info("已取消");
});
}
},
]
},
loading: false
});
/** 给表格添加一行入参定义。 */
const addExtend = () => {
extendData.value.push({ expansionFieldName: '', fieldType: '', content: '' });
extendTableInfo.value.data = extendData.value;
nextTick(() => {
scrollLastRowToView(extendTableRef.value?.tableRef, extendData.value.length);
})
}
/** ----------------- 数据产品表格相关信息 ----------------------------- */
const productTableRef = ref();
const productData: any = ref([]);
const productList: any = ref([]);
/** 基本信息的入参定义表格配置 */
const productTableInfo = ref({
id: "input-product-table",
height: '214px',
fields: [
{ label: "序号", type: "index", width: TableColumnWidth.INDEX, align: "center" },
{ label: "数据产品", field: "dataProductId", width: 180, required: true, columClass: 'edit-colum', type: 'edit' },
{ label: "数据产品编码", field: "dataProductId", width: 261 },
{ label: "所属主体名称", field: "dataProductEntityName", width: 200 },
{ label: "产品简介", field: "dataProductAbstract", width: 240 },
],
editInfo: {
dataProductId: {
label: '',
type: 'select',
field: 'dataProductId',
default: '',
options: productList.value,
props: {
label: 'productNameDesc',
value: 'productId',
disabled: 'disabled'
},
placeholder: '请选择',
clearable: false,
filterable: true
},
},
STATUS: 'edit',
data: productData.value,
showPage: false,
actionInfo: {
show: true,
label: "操作",
type: "btn",
width: 60,
fixed: 'right',
btns: [
{
label: "删除", value: "remove", click: (scope) => {
let index = scope.$index;
proxy.$openMessageBox("此操作将永久删除, 是否继续?", () => {
productData.value.splice(index, 1);
productTableInfo.value.data = productData.value;
proxy.$ElMessage.success('产品删除成功');
}, () => {
proxy.$ElMessage.info("已取消");
});
}
},
]
},
loading: false
});
/** 给表格添加一行入参定义。 */
const addProduct = () => {
productData.value.push({ dataProductId: '' });
productTableInfo.value.data = productData.value;
nextTick(() => {
scrollLastRowToView(productTableRef.value?.tableRef, productData.value.length);
})
}
/** 根据下拉选择的产品带出产品相关信息 */
const hanldeTableSelectChange = (val, scope, item) => {
let productItem = val && productList.value.find(p => p.productId == val);
scope.row.dataProductId = val;
scope.row.dataProductCode = val;
scope.row.dataProductName = productItem?.productName;
scope.row.dataProductEntityId = productItem?.enterpriseCode;
scope.row.dataProductAbstract = productItem?.description;
scope.row.dataProductEntityName = productItem?.enterpriseName;
let nodeInfo = nodeInfoFormRef.value.formInline;
if (scope.$index == 0) {
nodeInfoFormItems.value.forEach((item, index) => {
item.default = nodeInfo[item.field];
if (index == 1) {
item.default = tenantList.value.find(t => t.socialCreditCode == scope.row.dataProductEntityId)?.guid || scope.row.dataProductEntityName;
} else if (index == 2) {
item.default = scope.row.dataProductEntityId || '';
}
})
}
}
/** ---------------- 合约策略执行者相关表单信息 ----------------------------------- */
/** 策略执行者下拉列表 */
const tenantList: any = ref([]);
const nodeInfoFormRef = ref();
// 策略执行者类型列表
const executorTypeList: any = ref([]);
const nodeInfoFormItems = ref([{
label: '策略执行者类型',
type: 'input',
placeholder: '-',
field: 'dataProviderType',
default: '',
disabled: true,
visible: true
}, {
label: '策略执行者名称',
type: 'select',
placeholder: '请选择',
field: 'dataProviderGuid',
default: '',
options: tenantList.value, //先禁用,选择产品后自动带出
props: {
value: 'guid',
label: 'tenantName'
},
filterable: true,
clearable: true,
disabled: true,
required: true,
visible: true
}, {
label: '策略执行者标识',
type: 'input',
placeholder: '-',
field: 'dataProviderId',
default: '',
disabled: true,
visible: true
}, {
label: '策略执行者类型',
type: 'input',
placeholder: '-',
field: 'dataUserType',
default: '',
disabled: true,
visible: true
}, {
label: '策略执行者名称',
type: 'select',
placeholder: '请选择',
field: 'dataUserGuid',
default: '',
options: tenantList.value,
props: {
value: 'guid',
label: 'tenantName'
},
filterable: true,
clearable: true,
required: true,
visible: true
}, {
label: '策略执行者标识',
type: 'input',
placeholder: '-',
field: 'dataUserId',
default: '',
disabled: true,
visible: true
}])
const nodeInfoFormRules = ref({
// dataProviderGuid: [required('请选择数据供给方策略执行者名称')],
dataUserGuid: [required('请选择数据使用方策略执行者名称')],
});
const handleNodeSelectChange = (val, row, info) => {
if (row.field == 'dataUserGuid') {
let tenantItem = tenantList.value.find(t => t.guid == val);
info.dataUserId = tenantItem?.socialCreditCode || '';
nodeInfoFormItems.value.forEach(item => {
item.default = info[item.field];
});
}
}
const strategyTableRef = ref();
/** 约束运算符字典下拉 */
const operatorOptionList: any = ref([]);
/** 约束行为下拉列表 */
const constraintOptionsList: any = ref([]);
/** 策略操作行为下拉列表 */
const actionOptionsList: any = ref([]);
const getSubmitInfo = () => {
let baseInfo = baseInfoFormRef.value.formInline;
let nodeInfo = nodeInfoFormRef.value.formInline;
let params = { ...baseInfo };
params.contractExpansions = extendData.value;
params.contractSubjects = productData.value;
params.executionNodes = [{
executionerTypeCode: executorTypeList.value[0]?.value,
executionerGuid: nodeInfo.dataProviderGuid,
executionerId: nodeInfo.dataProviderId,
executionerName: tenantList.value.find(t => t.guid == nodeInfo.dataProviderGuid)?.tenantName
}, {
executionerTypeCode: executorTypeList.value[1]?.value,
executionerGuid: nodeInfo.dataUserGuid,
executionerId: nodeInfo.dataUserId,
executionerName: tenantList.value.find(t => t.guid == nodeInfo.dataUserGuid)?.tenantName
}];
params.policys = strategyTableRef.value.strategyData?.map((d, index) => {
return Object.assign({}, d, { orderNum: index + 1 })
}) || [];
return params;
}
const saveDraft = () => {
let params: any = getSubmitInfo();
params.isResubmit = false;
fullscreenLoading.value = true;
if (!route.query.guid) {
saveContract(params).then((res: any) => {
fullscreenLoading.value = false;
if (res?.code == proxy.$passCode) {
proxy.$ElMessage.success('合约保存成功');
userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath));
router.push({
name: "smartContractManagement",
});
dataSmartContract.set(true);
} else {
res?.msg && proxy.$ElMessage.error(res?.msg)
}
})
} else {
params.guid = route.query.guid;
params.isResubmit = false;
updateContract(params).then((res: any) => {
fullscreenLoading.value = false;
if (res?.code == proxy.$passCode) {
proxy.$ElMessage.success('合约编辑保存成功');
userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath));
router.push({
name: "smartContractManagement",
});
dataSmartContract.set(true);
} else {
res?.msg && proxy.$ElMessage.error(res?.msg)
}
})
}
}
const submit = () => {
baseInfoFormRef.value.ruleFormRef?.validate((valid, errorItem) => {
if (valid) {
for (const data of extendData.value) {
if (!data.expansionFieldName) {
proxy.$ElMessage.error('扩展信息中的扩展字段名称不能为空');
handleLeft('base-info');
expandBase.value = true;
return;
}
if (!data.fieldType) {
proxy.$ElMessage.error('扩展信息中的字段类型不能为空');
handleLeft('base-info');
expandBase.value = true;
return;
}
}
if (!productData.value?.length) {
proxy.$ElMessage.error('合约标的的数据产品不能为空');
expandInfo.value = true;
handleLeft('product-info');
return;
}
let index = 0;
for (const pd of productData.value) {
if (!pd.dataProductId) {
proxy.$ElMessage.error('合约标的的数据产品不能为空');
expandInfo.value = true;
handleLeft('product-info');
return;
}
if (userData.superTubeFlag == 'Y') { //只有主平台模式下需判断
if (baseInfoFormRef.value.formInline.signModeCode == '02' && index > 0 && productData.value[index].dataProductEntityId != pd.dataProductEntityId) {
proxy.$ElMessage.error('合约标的的数据产品不能来自不同的企业主体');
expandInfo.value = true;
handleLeft('product-info');
return;
} else {
if (pd.dataProductEntityId != nodeInfoFormRef.value?.formInline?.dataProviderId) {
proxy.$ElMessage.error('合约标的的数据产品所属主体与数据提供方的策略执行者不一致,请检查!');
expandInfo.value = true;
handleLeft('product-info');
return;
}
}
}
index++;
}
nodeInfoFormRef.value.ruleFormRef?.validate((valid1, errorItem1) => {
if (valid1) {
let nodeInfo = nodeInfoFormRef.value.formInline;
if (nodeInfo.dataProviderGuid == nodeInfo.dataUserGuid) {
proxy.$ElMessage.error(`${executorTypeList.value[0].label}${executorTypeList.value[1].label}的策略执行者不能选择同一个`);
expandPolicy.value = true;
handleLeft('policy-info');
return;
}
if (!strategyTableRef.value.validateValue()) {
expandPolicy.value = true;
handleLeft('policy-info');
return;
}
let params: any = getSubmitInfo();
params.isResubmit = detailInfo.value?.contractStatus == '00';
params.contractStatus = '02';
fullscreenLoading.value = true;
if (!route.query.guid) {
saveContract(params).then((res: any) => {
fullscreenLoading.value = false;
if (res?.code == proxy.$passCode) {
proxy.$ElMessage.success('合约协商成功');
userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath));
router.push({
name: "smartContractManagement",
});
dataSmartContract.set(true);
} else {
res?.msg && proxy.$ElMessage.error(res?.msg)
}
})
} else {
params.guid = route.query.guid;
updateContract(params).then((res: any) => {
fullscreenLoading.value = false;
if (res?.code == proxy.$passCode) {
proxy.$ElMessage.success('合约协商成功');
userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath));
router.push({
name: "smartContractManagement",
});
dataSmartContract.set(true);
} else {
res?.msg && proxy.$ElMessage.error(res?.msg)
}
})
}
} else {
expandPolicy.value = true;
var obj = Object.keys(errorItem1);
nodeInfoFormRef.value.ruleFormRef?.scrollToField(obj[0]);
}
})
} else {
expandBase.value = true;
var obj = Object.keys(errorItem);
baseInfoFormRef.value.ruleFormRef?.scrollToField(obj[0]);
nodeInfoFormRef.value.validate();
}
});
}
const handleLeft = (id) => {
let dom = document.getElementById(id);
dom && dom.scrollIntoView({
behavior: "smooth", // 平滑过渡
block: "start", // 上边框与视窗顶部平齐。默认值
});
}
const cancel = () => {
proxy.$openMessageBox("当前页面尚未保存,确定放弃修改吗?", () => {
userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath));
router.push({
name: "smartContractManagement",
});
}, () => {
proxy.$ElMessage.info("已取消");
});
}
const detailInfo: any = ref({});
/** 当前会员是否认证信息 */
const currTenantDetailInfo: any = ref({ });
const psLogon = ref();
onBeforeMount(() => {
let exec = () => {
if (userData.superTubeFlag != 'Y') {
baseInfoFormItems.value[1].disabled = true;
baseInfoFormItems.value[1].default = '01';
productTableInfo.value.editInfo.dataProductId.props.label = 'productName';
} else { //可以选点对点。未认证时只能选择中介参与
baseInfoFormItems.value[1].disabled = !currTenantDetailInfo.value.trustedIdentityCredential;
baseInfoFormItems.value[1].default = !currTenantDetailInfo.value.trustedIdentityCredential ? '02' : '01';
productTableInfo.value.editInfo.dataProductId.props.label = 'productNameDesc';
}
if (route.query.guid) {
fullscreenLoading.value = true;
getContractDetail(route.query.guid).then((res: any) => {
fullscreenLoading.value = false;
if (res?.code == proxy.$passCode) {
detailInfo.value = res.data || {};
baseInfoFormItems.value.forEach(item => {
item.default = detailInfo.value[item.field];
});
// if (baseInfoFormItems.value[1].default == '01') {
// // 默认点对点,数据使用方可以选择
// nodeInfoFormItems.value[1].disabled = true;
// } else {
// nodeInfoFormItems.value[1].disabled = false;
// }
// nodeInfoFormItems.value[1].disabled = detailInfo.value.signModeCode == '01';
let executionNodes = detailInfo.value.executionNodes || [];
let provider = executionNodes.find(e => e.executionerTypeCode == '01');
let user = executionNodes.find(e => e.executionerTypeCode == '02');
nodeInfoFormItems.value[1].default = provider.executionerGuid;
nodeInfoFormItems.value[2].default = provider.executionerId;
nodeInfoFormItems.value[4].default = user.executionerGuid;
nodeInfoFormItems.value[5].default = user.executionerId;
if (baseInfoFormItems.value[1].default == '01' && userData.superTubeFlag == 'Y') {
productList.value = res.data?.map(d => {
d.disabled = d.enterpriseName != userData.tenantName; //点对点,只能选择自己公司所属产品
return d;
}) || [];
}
productTableInfo.value.editInfo.dataProductId.options = productList.value;
extendData.value = detailInfo.value.contractExpansions || [];
extendTableInfo.value.data = extendData.value;
productData.value = detailInfo.value.contractSubjects || [];
productTableInfo.value.data = productData.value;
} else {
res?.msg && proxy.$ElMessage.error(res?.msg)
}
})
} else {
if (baseInfoFormItems.value[1].default == '01') {
// 默认点对点,数据使用方可以选择
// nodeInfoFormItems.value[1].disabled = true;
nodeInfoFormItems.value[1].default = userData.tenantGuid;
if (tenantList.value.length) {
let item = tenantList.value.find(t => t.guid == userData.tenantGuid);
nodeInfoFormItems.value[2].default = item?.socialCreditCode;
}
} else {
// nodeInfoFormItems.value[1].disabled = false;
nodeInfoFormItems.value[1].default = ''; //如果是中介的话,不能选择自己吧。
}
}
getContractDataProduct().then((res: any) => {
if (res?.code == proxy.$passCode) {
productList.value = res.data?.map(d => {
d.productNameDesc = d.productName + '(' + d.enterpriseName + ')';
d.disabled = userData.superTubeFlag == 'Y' && baseInfoFormItems.value[1].default == '01' && d.enterpriseName != userData.tenantName; //点对点,只能选择自己公司所属产品
return d;
}) || [];
productTableInfo.value.editInfo.dataProductId.options = productList.value;
} else {
res?.msg && proxy.$ElMessage.error(res?.msg);
}
})
}
fullscreenLoading.value = true;
psLogon.value = getEnterpriseData({
logonUser: userData.tenantName == "非认证会员" ? userData.logonUser : tenantData.logonUser
}).then((res: any) => {
psLogon.value = null;
fullscreenLoading.value = false;
if (res?.code == proxy.$passCode) {
currTenantDetailInfo.value = res.data || {};
exec();
} else {
res?.msg && proxy.$ElMessage.error(res?.msg);
exec();
}
}).catch(() => {
psLogon.value = null;
fullscreenLoading.value = false;
exec();
});
getParamsList({ dictType: '合约签署模式' }).then((res: any) => {
if (res?.code == proxy.$passCode) {
const data = res.data || [];
signModeList.value = data;
baseInfoFormItems.value[1].options = signModeList.value;
} else {
res?.msg && proxy.$ElMessage.error(res?.msg);
}
})
getParamsList({ dictType: '合约扩展字段类型' }).then((res: any) => {
if (res?.code == proxy.$passCode) {
const data = res.data || [];
dataTypeList.value = data;
extendTableInfo.value.editInfo.fieldType.options = dataTypeList.value;
} else {
res?.msg && proxy.$ElMessage.error(res?.msg);
}
})
getParamsList({ dictType: '约束运算符' }).then((res: any) => {
if (res?.code == proxy.$passCode) {
const data = res.data || [];
operatorOptionList.value = data;
} else {
res?.msg && proxy.$ElMessage.error(res?.msg);
}
})
getActionPolicyList().then((res: any) => {
if (res?.code == proxy.$passCode) {
const data = res.data || [];
actionOptionsList.value = data;
} else {
res?.msg && proxy.$ElMessage.error(res?.msg);
}
})
getConstraintPolicyList().then((res: any) => {
if (res?.code == proxy.$passCode) {
const data = res.data || [];
constraintOptionsList.value = data;
} else {
res?.msg && proxy.$ElMessage.error(res?.msg);
}
})
getParamsList({ dictType: '策略执行者类型' }).then((res: any) => {
if (res?.code == proxy.$passCode) {
const data = res.data || [];
executorTypeList.value = data;
nodeInfoFormItems.value[0].default = executorTypeList.value[0]?.label;
nodeInfoFormItems.value[3].default = executorTypeList.value[1]?.label;
} else {
res?.msg && proxy.$ElMessage.error(res?.msg);
}
})
getContractTenantList().then((res: any) => {
if (res.code == proxy.$passCode) {
tenantList.value = res.data || [];
nodeInfoFormItems.value[1].options = tenantList.value;
nodeInfoFormItems.value[4].options = tenantList.value;
if (nodeInfoFormItems.value[1].default) {
let item = tenantList.value.find(t => t.guid == nodeInfoFormItems.value[1].default);
nodeInfoFormItems.value[2].default = item?.socialCreditCode;
}
} else {
res?.msg && proxy.$ElMessage.error(res.msg);
}
})
});
onActivated(() => {
if (route.query.guid) {
let tab: any = userStore.tabbar.find((tab: any) => tab.fullPath === fullPath);
if (tab) {
document.title = `编辑合约-${route.query.name}`;
tab.meta.title = `编辑合约-${route.query.name}`;
}
}
})
</script>
<template>
<div class="container_wrap full" v-loading="fullscreenLoading">
<div class="content_main panel">
<ContentWrap id="base-info" title="合约信息" expandSwicth style="margin-top: 15px" :isExpand="expandBase"
@expand="(v) => (expandBase = v)" description="">
<Form ref="baseInfoFormRef" formId="base-info-form" :itemList="baseInfoFormItems" :rules="baseInfoFormRules"
col="col3" @select-change="handleFormSelectChange" />
<Table ref="extendTableRef" :tableInfo="extendTableInfo" class="fiveRow-table" />
<div class="row-add-btn">
<el-button link @click="addExtend" :icon="CirclePlus" v-preReClick>添加扩展信息</el-button>
</div>
</ContentWrap>
<ContentWrap id="product-info" title="合约标的" expandSwicth style="margin-top: 15px" :isExpand="expandInfo"
@expand="(v) => (expandInfo = v)" description="">
<Table ref="productTableRef" :tableInfo="productTableInfo" class="fiveRow-table"
@table-select-change="hanldeTableSelectChange" />
<div class="row-add-btn">
<el-button link @click="addProduct" :icon="CirclePlus" v-preReClick>添加产品</el-button>
</div>
</ContentWrap>
<ContentWrap id="policy-info" title="合约策略" expandSwicth style="margin-top: 15px" :isExpand="expandPolicy"
@expand="(v) => (expandPolicy = v)" description="">
<div class="h-title">策略执行节点</div>
<Form ref="nodeInfoFormRef" formId="node-info-form" :itemList="nodeInfoFormItems" :rules="nodeInfoFormRules"
@select-change="handleNodeSelectChange" col="col3" />
<StrategyTable ref="strategyTableRef" :show-title="true" :value="detailInfo.policys || []"
:operatorOptionList="operatorOptionList" :actionOptionsList="actionOptionsList"
:constraintOptionsList="constraintOptionsList"></StrategyTable>
</ContentWrap>
</div>
<div class="tool_btns">
<div class="btns">
<el-button @click="cancel">取消</el-button>
<el-button @click="saveDraft" v-show="detailInfo?.contractStatus != '00'">保存</el-button>
<el-button type="primary" @click="submit">合约协商</el-button>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.container_wrap {
overflow: hidden;
.content_main {
height: calc(100% - 45px);
overflow: hidden auto;
&.panel {
padding: 0 16px 16px;
}
}
}
.tool_btns {
height: 44px;
margin: 0 -8px;
display: flex;
justify-content: center;
align-items: center;
border-top: 1px solid #d9d9d9;
}
.row-add-btn {
.el-button--default {
padding: 4px 0px;
margin-top: 4px;
}
:deep(.el-icon) {
width: 16px;
height: 16px;
svg {
width: 16px;
height: 16px;
}
}
}
.h-title {
font-size: 14px;
color: #212121;
font-weight: 600;
margin-bottom: 4px;
}
</style>
<route lang="yaml">
name: smartContractDetail
</route>
<script lang="ts" setup name="smartContractDetail">
import {
getContractDetail,
getActionPolicyList,
getConstraintPolicyList,
getContractNegotiate,
getContractNegoPlicyByVersion,
continueContractNegotiate,
rejectContract,
confirmContract,
signContract,
getSignListInfo,
getContractExecList,
terminateContract,
getTerminateDetailInfo,
getSignatureFile
} from "@/api/modules/dataSmartContract";
import {
tagMethod,
tagType
} from "@/utils/common";
import {
getParamsList,
} from "@/api/modules/queryService";
import { commonPageConfig, TableColumnWidth } from "@/utils/enum";
import StrategyTable from './components/strategyTable.vue';
import { onUploadFilePreview, onUploadFileDownload } from '@/api/modules/common';
import { useValidator } from "@/hooks/useValidator";
import useUserStore from "@/store/modules/user";
import useDataSmartContract from "@/store/modules/dataSmartContract";
import ConfirmDialog from "./components/confirmDialog.vue";
import { getEnterpriseData } from "@/api/modules/dataIdentify";
import showFile from "./components/showFile.vue";
// @ts-ignore
import html2pdf from 'html2pdf.js';
import { getPathUrl, getUpFileSignByUrl, obsUploadRequest } from "@/api/modules/obsService";
import { ElMessage } from "element-plus";
const { required, requiredFiles } = useValidator();
const userStore = useUserStore();
const userData = JSON.parse(localStorage.userData)
const tenantData = JSON.parse(localStorage.tenantInfo);
const dataSmartContract = useDataSmartContract();
const router = useRouter();
const route = useRoute();
const fullPath = route.fullPath;
const fullscreenLoading = ref(false);
const expandBase = ref(false);
const expandInfo = ref(false);
const expandPolicy = ref(false);
const expandPolicyConsult = ref(false);
const expandConsultHistory = ref(false);
const expandSign = ref(false);//合约签署
const expandKeep = ref(false);//合约履约执行信息
const expandReject = ref(false);//合约解除
const { proxy } = getCurrentInstance() as any;
const detailInfo: any = ref({});
const detailType = ref(route.query.type); //为null表示第一步查看,其余协整和签署等需要传递type
const isDetail = ref(route.query.isDetail == 'Y');
const toolBtns: any = computed(() => {
let btnsArr: any = [{
label: isDetail.value ? "关闭" : '取消', value: "cancel", plain: true
}];
let contractStatus = detailInfo.value.contractStatus;
if ((!detailType.value || currentStep.value == 1) && contractStatus == '01') {
btnsArr.push({
label: "合约协商", value: "consult", type: 'primary'
});
}
if (detailType.value == 'consult') {
btnsArr.push({
label: "拒绝本次合约", value: "refuse", plain: true
});
btnsArr.push({
label: "确认本次合约", value: "confirm", type: 'primary', disabled: disableConfirmBtn.value
});
btnsArr.push({
label: "继续协商", value: "continue", type: 'primary', disabled: !disableConfirmBtn.value
});
} else if (detailType.value == 'sign') {
btnsArr.push({
label: "确定签署", value: "confirmSign", type: 'primary', disabled: !confirmAuth.value
});
} else if (detailType.value == 'reject' && (!currentStep.value || currentStep.value == 5)) {
// btnsArr.push({
// label: "上一步", value: "lastStep", plain: true
// });
btnsArr.push({
label: "确认提交", value: "confirmReject", type: 'primary'
});
}
return btnsArr;
});
const btnHandles = ref({
'cancel': () => {
userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath));
router.push({
name: "smartContractManagement",
});
},
'consult': () => {
if (!detailType.value) {
router.push({
name: "smartContractManagement",
});
return;
}
if (detailType.value == 'consult') {
currentStep.value = 2;
}
},
'refuse': () => {
confirmDialogInfo.value.type = 'fail';
confirmDialogInfo.value.dialogVisible = true;
},
'continue': () => { //继续协商
if (!strategyTableEditRef.value.validateValue()) {
return;
}
fullscreenLoading.value = true;
continueContractNegotiate({
contractGuid: route.query.guid,
policys: strategyTableEditRef.value.strategyData?.map((d, index) => {
return Object.assign({}, d, { orderNum: index + 1 })
})
}).then((res: any) => {
fullscreenLoading.value = false;
if (res?.code == proxy.$passCode) {
proxy.$ElMessage.success('合约继续协商提交成功');
userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath));
router.push({
name: "smartContractManagement",
});
dataSmartContract.set(true);
} else {
res?.msg && proxy.$ElMessage.error(res?.msg)
}
})
},
'confirm': () => { //确认本次合约。如果还需另一方确认,就需要清空confirmGuids。
confirmDialogInfo.value.type = detailInfo.value.signModeCode == '01' ? 'success' : (!detailInfo.value.confirmGuids?.length ? 'warning' : 'success');
confirmDialogInfo.value.dialogVisible = true;
},
'confirmSign': () => {
signInfoFormRef.value?.ruleFormRef?.validate((valid, errorItem) => {
if (valid) {
if (!signFileUrlInfo.value?.url) {
proxy.$ElMessage.error('签署签名文件不能为空!');
return;
}
let signFormInline = signInfoFormRef.value.formInline;
let params: any = {
contractGuid: route.query.guid,
signatureEntityGuid: userData.tenantGuid,
signatureEntityId: signTenantDetailInfo.value.socialCreditCode,
signatureEntityName: userData.tenantName,
identityCertificate: { url: signTenantDetailInfo.value.trustedIdentityCredential },
signature: signFileUrlInfo.value?.url ? {
name: signFileUrlInfo.value.name,
url: signFileUrlInfo.value.url,
} : {},
attachment: signFormInline.file?.length ? {
name: signFormInline.file[0].name,
url: signFormInline.file[0].url,
} : {},
};
fullscreenLoading.value = true;
signContract(params).then((res: any) => {
fullscreenLoading.value = false;
if (res?.code == proxy.$passCode) {
proxy.$ElMessage.success('合约确定签署成功');
userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath));
router.push({
name: "smartContractManagement",
});
dataSmartContract.set(true);
} else {
res?.msg && proxy.$ElMessage.error(res?.msg)
}
})
} else {
expandSign.value = true;
var obj = Object.keys(errorItem);
signInfoFormRef.value.ruleFormRef?.scrollToField(obj[0]);
}
})
},
'lastStep': () => {
if (detailType.value == 'reject' && !currentStep.value) {
currentStep.value = 4;
} else {
currentStep.value--;
}
},
'confirmReject': () => { //确认解除合约
//后端需要检验选择的合同约定终止时间不能大于合同生效时间
rejectInfoFormRef.value.ruleFormRef?.validate((valid, errorItem) => {
if (valid) {
proxy.$openMessageBox('确认提交合约解除信息吗?', () => {
let params = { ...rejectInfoFormRef.value.formInline };
let file = params.dataProcessingProof || [];
params.dataProcessingProof = {
name: file?.[0]?.name,
url: file?.[0]?.url
}
params.contractGuid = route.query.guid;
fullscreenLoading.value = true;
terminateContract(params).then((res: any) => {
fullscreenLoading.value = false;
if (res?.code == proxy.$passCode) {
proxy.$ElMessage.success('合约解除信息提交成功');
userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath));
router.push({
name: "smartContractManagement",
});
dataSmartContract.set(true);
} else {
res?.msg && proxy.$ElMessage.error(res?.msg)
}
})
}, () => {
proxy.$ElMessage.info("已取消");
});
} else {
var obj = Object.keys(errorItem);
rejectInfoFormRef.value.ruleFormRef.scrollToField(obj[0])
}
})
}
});
const extendTableInfo = ref({
id: "input-extend-table",
height: '214px',
fields: [
{ label: "序号", type: "index", width: TableColumnWidth.INDEX, align: "center" },
{ label: "扩展字段名称", field: "expansionFieldName", width: 160 },
{ label: "字段类型", field: "fieldTypeName", width: 140 },
{ label: "输入内容", field: "content", width: 300 },
],
data: [],
showPage: false,
actionInfo: {
show: false
},
loading: false
});
const exportExtendTableInfo = computed(() => {
return {
id: "export-input-extend-table",
height: 'auto',
minPanelHeight: '60px',
minHeight: '60px',
fields: [
{ label: "序号", type: "index", width: TableColumnWidth.INDEX, align: "center", showTooltip: false },
{ label: "扩展字段名称", field: "expansionFieldName", width: "auto", minWidth: 140, showTooltip: false },
{ label: "字段类型", field: "fieldTypeName", width: "auto", minWidth: 100, showTooltip: false },
{ label: "输入内容", field: "content", width: "auto", minWidth: 250, showTooltip: false },
],
data: extendTableInfo.value.data,
showPage: false,
actionInfo: {
show: false
},
loading: false
}
});
const productTableInfo = ref({
id: "input-product-table",
height: '214px',
fields: [
{ label: "序号", type: "index", width: TableColumnWidth.INDEX, align: "center" },
{ label: "数据产品", field: "dataProductName", width: 180 },
{ label: "数据产品编码", field: "dataProductId", width: 261 },
{ label: "所属主体名称", field: "dataProductEntityName", width: 240 },
{ label: "产品简介", field: "dataProductAbstract", width: 240 },
],
data: [],
showPage: false,
actionInfo: {
show: false
},
loading: false
});
const exportProductTableInfo = computed(() => {
return {
id: "export-input-product-table",
height: 'auto',
minPanelHeight: '60px',
minHeight: '60px',
fields: [
{ label: "序号", type: "index", width: TableColumnWidth.INDEX, align: "center", showTooltip: false },
{ label: "数据产品", field: "dataProductName", minWidth: 120, showTooltip: false, width: "auto" },
{ label: "数据产品编码", field: "dataProductId", width: "auto", minWidth: 140, showTooltip: false },
{ label: "所属主体名称", field: "dataProductEntityName", width: "auto", minWidth: 150, showTooltip: false },
{ label: "产品简介", field: "dataProductAbstract", width: "auto", minWidth: 160, showTooltip: false },
],
data: productTableInfo.value.data,
showPage: false,
actionInfo: {
show: false
},
loading: false
}
});
const nodeInfoFormRef = ref();
const nodeInfoFormItems = ref([{
label: '策略执行者类型',
type: 'input',
placeholder: '请选择',
field: 'dataProviderType',
default: '数据供给方',
disabled: true,
visible: true
}, {
label: '策略执行者名称',
type: 'input',
placeholder: '请选择',
field: 'dataProviderName',
default: '',
options: [],
props: {
value: 'value',
label: 'label'
},
filterable: true,
clearable: true,
required: true,
disabled: true,
visible: true
}, {
label: '策略执行者标识',
type: 'input',
placeholder: '请输入',
field: 'dataProviderId',
default: '',
disabled: true,
visible: true
}, {
label: '策略执行者类型',
type: 'input',
placeholder: '请选择',
field: 'dataUserType',
default: '数据使用方',
disabled: true,
visible: true
}, {
label: '策略执行者名称',
type: 'input',
placeholder: '请选择',
field: 'dataUserName',
default: '',
options: [],
props: {
value: 'value',
label: 'label'
},
filterable: true,
clearable: true,
required: true,
disabled: true,
visible: true
}, {
label: '策略执行者标识',
type: 'input',
placeholder: '请输入',
field: 'dataUserId',
default: '',
disabled: true,
visible: true
}])
const currentStep = ref(0);
const handleClickStep = (number) => {
currentStep.value = number;
}
/** ---------------------- 合约协商 ------------------------- */
const strategyTableEditRef = ref();
/** 时间线列表 */
const activities: any = ref([]);
const detailDialogInfo = ref({
visible: false,
size: 800,
direction: "column",
header: {
title: "查看版本详情",
},
type: '',//标识是否是重新提交
contents: [],
footer: {
show: false
},
contentLoading: false,
});
const versionDetail: any = ref([]);
const clickVersion = (child) => {
detailDialogInfo.value.visible = true;
detailDialogInfo.value.header.title = '查看版本详情' + '(' + child.version + ')';
detailDialogInfo.value.contentLoading = true;
getContractNegoPlicyByVersion({
guid: route.query.guid,
version: child.version
}).then((res: any) => {
detailDialogInfo.value.contentLoading = false;
if (res?.code == proxy.$passCode) {
const data = res.data || [];
versionDetail.value = data;
} else {
res?.msg && proxy.$ElMessage.error(res?.msg);
}
})
}
const handleVersionDialogBtnClick = (btn) => {
if (btn.value == 'cancel') {
detailDialogInfo.value.visible = false;
}
}
const disableConfirmBtn = computed(() => {
let newValue = strategyTableEditRef.value?.strategyData || [];
let originValue = consultDetailInfo.value.policys || [];
if (newValue.length != originValue.length) {
return true;
}
let index = 0;
for (const newItem of newValue) {
let oldItem = originValue[index];
if (newItem.action != oldItem.action || newItem.constraintEnName != oldItem.constraintEnName ||
newItem.constraintOperatorCode != oldItem.constraintOperatorCode || newItem.constraintValue != oldItem.constraintValue) {
return true;
}
index++;
}
})
const confirmDialogInfo = ref({
dialogVisible: false,
type: 'success',
msg: {
'success': '确认合约表示您已仔细查阅并接受本次合约所有相关信息,确定提交后将为您跳转合约签署环节。\n如有疑问可取消操作。',
'warning': '确认合约表示您已仔细查阅并接受本次合约所有相关信息,确定提交后待对方确认合约后再进入签署环节。',
'fail': '拒绝后将终止本次合约协商流程,请谨慎操作。\n如有疑问可联络合约发起方详细咨询。'
},
});
const handleConfirmDialog = (btn) => {
if (btn == 'submit') {
confirmDialogInfo.value.dialogVisible = false;
if (confirmDialogInfo.value.type == 'fail') {
fullscreenLoading.value = true;
rejectContract(route.query.guid).then((res: any) => {
fullscreenLoading.value = false;
if (res?.code == proxy.$passCode) {
proxy.$ElMessage.success('拒绝本次合约提交成功');
userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath));
router.push({
name: "smartContractManagement",
});
dataSmartContract.set(true);
} else {
res?.msg && proxy.$ElMessage.error(res?.msg)
}
})
} else {
fullscreenLoading.value = true;
confirmContract(route.query.guid).then((res: any) => {
fullscreenLoading.value = false;
if (res?.code == proxy.$passCode) {
proxy.$ElMessage.success('确认本次合约成功');
userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath));
router.push({
name: "smartContractManagement",
});
dataSmartContract.set(true);
} else {
res?.msg && proxy.$ElMessage.error(res?.msg)
}
})
}
} else {
confirmDialogInfo.value.dialogVisible = false
}
}
/** ------------------------- 合约签署 -------------------------- */
const confirmAuth = ref(false); //是否勾选确权
const signInfoFormRef = ref();
const signInfoFormItems = ref([{
label: '附件上传',
tip: '文件大小不超过20MB',
type: 'upload-file',
limitSize: 20,
required: false,
default: [],
block: true,
field: 'file',
},
// {
// label: '签署签名',
// tip: '文件大小不超过20MB',
// type: 'upload-file',
// limitSize: 20,
// limit: 1,
// required: true,
// default: [],
// block: false,
// field: 'signFile',
// },
// {
// label: '签署时间',
// type: 'input',
// placeholder: '确定签署提交的时间',
// field: 'submitTime',
// default: '',
// maxlength: 50,
// disabled: true
// },
]);
const signInfoFormRules = ref({
// signFile: [{
// validator: (rule: any, value: any, callback: any) => {
// if (!value?.length) {
// callback(new Error('请上传文件'))
// } else {
// callback();
// }
// }, trigger: 'change'
// }],
});
const viewResultVoucherFile = (url) => {
if (!url) {
proxy.$ElMessage.error('暂无可查看的身份凭证证书');
return;
}
onUploadFilePreview(url);
}
const signFileLoading = ref(false);
const signFileUrlInfo: any = ref({ name: '', url: '' });
function getElementPdfPosition(element, options = {}) {
const { scale = 2, pageHeightPt = 842 } = options;
// 1. 获取元素距离容器顶部的像素距离
const container = document.getElementById('pdf-container'); // 你的根容器
const rect = element.getBoundingClientRect();
const containerRect = container.getBoundingClientRect();
const topPx = rect.top - containerRect.top + window.scrollY;
// 2. html2canvas 默认使用 devicePixelRatio,但 html2pdf 通常固定 scale
// html2pdf 内部会将 canvas 高度按比例缩放到 PDF 页面宽度
// 简化模型:假设 PDF 宽度 = 595pt (A4),对应 canvas.width / scale px
const pdfPageWidthPt = 595; // A4 width in pt
const containerWidthPx = container.offsetWidth;
const pxToPtRatio = pdfPageWidthPt / containerWidthPx; // 每像素多少 pt
const topPt = topPx * pxToPtRatio;
// 3. 计算落在第几页 & 页内 Y 坐标
const page = Math.floor(topPt / pageHeightPt) + 1;
const yInPage = topPt % pageHeightPt;
return {
page,
x: rect.left * pxToPtRatio, // 可选 X 坐标
y: yInPage,
totalY: topPt
};
}
const geneSignFile = async () => {
const element = document.getElementById('pdf-content'); // 指定要转PDF的DOM
if (!element) {
proxy.$ElMessage.error('找不到生成pdf文件的网页内容');
return;
}
signFileLoading.value = true;
element.style.display = '';
await nextTick();
setTimeout(async () => {
let fileName = detailInfo.value.contractName + '_' + tenantData.abbreviation + '.pdf';
const options = {
margin: [10, 3, 10, 3],
filename: fileName,
image: {
type: 'jpeg',
quality: 0.9
},
html2canvas: {
scale: 2,
useCORS: true,
allowTaint: false,
logging: false,
backgroundColor: '#ffffff'
},
jsPDF: {
unit: 'pt',
format: 'a4',
orientation: 'portrait'
},
// 防止文字被切断的关键配置
pagebreak: {
mode: ['css', 'avoid-all', 'legacy'],
avoid: ['.avoid-break', '.el-card__header']
}
};
// 方法1: 直接生成并下载
try {
html2pdf().from(element).set(options).toPdf().get('pdf').then(pdf => {
let totalPages = pdf.internal.getNumberOfPages();
// 获取最后一页的结束位置
for (let i = 1; i <= totalPages; i++) {
pdf.setPage(i);
}
const pageInfo = pdf.internal.getCurrentPageInfo();
const pdfPageWidthPt = parseInt(pageInfo.pageContext?.mediaBox?.topRightX); // A4 width in pt
const pageHeightPt = parseInt(pageInfo.pageContext?.mediaBox?.topRightY);
const containerWidthPx = element.offsetWidth;
let lastThree: any = element.children[3];//产品
let lastCont: any = element.children[4];
let lastEle: any = element.children[5];
const pxToPtRatio = pdfPageWidthPt / containerWidthPx; // 每像素多少 pt
const topPt = element.offsetHeight * pxToPtRatio;
let yInPage: any = 0;
if (totalPages == 1) {
yInPage = pageHeightPt - 20;
} else if (totalPages == 2) {
if ((element.children[0].offsetHeight + element.children[2].offsetHeight + lastThree.offsetHeight) * pxToPtRatio > (pageHeightPt + 25) ) {
//第一页装不下产品。
yInPage = (Math.ceil((lastThree.offsetHeight + lastCont.offsetHeight) * pxToPtRatio) + 150)
} else if ((element.offsetHeight - lastEle.offsetHeight) * pxToPtRatio > (pageHeightPt + 25)) { //装不下策略信息
yInPage = (Math.ceil(lastCont.offsetHeight * pxToPtRatio) + 150);
} else {
yInPage = 70;
}
} else if (totalPages == 3) {
yInPage = Math.ceil(pageHeightPt / 2);
}
let blob = pdf.output('blob');
const formData = new FormData();
formData.append('image', blob, fileName); // 可以指定文件名
getSignatureFile(formData, { pageIndex: totalPages, yPosition: yInPage }).then((res: any) => {
if (res?.code == proxy.$passCode) {
if (!res.data) {
signFileLoading.value = false;
proxy.$ElMessage.error('生成的签名文件为空');
return;
}
// 将 base64 转换为二进制数据
const byteCharacters = atob(res.data);
const byteArrays: any = [];
for (let offset = 0; offset < byteCharacters.length; offset += 512) {
const slice = byteCharacters.slice(offset, offset + 512);
const byteNumbers = new Array(slice.length);
for (let i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
// 创建 Blob 然后转换为 File
const blob = new Blob(byteArrays, { type: 'application/pdf' });
let fileObject = new File([blob], fileName, { type: 'application/pdf' });
getUpFileSignByUrl({ fileName: fileName })
.then((res: any) => {
obsUploadRequest({
signedUrl: res.data.signedUrl,
file: fileObject,
actualSignedRequestHeaders: res.data.actualSignedRequestHeaders
}).then(() => {
signFileLoading.value = false;
if (res.code == '00000') {
const fileUrl = res.data.signedUrl ? getPathUrl(res.data.signedUrl) : '';
signFileUrlInfo.value = {
name: fileName,
url: fileUrl
};
ElMessage.success('生成成功');
} else {
signFileLoading.value = false;
ElMessage.error('文件生成失败!');
}
})
})
.catch(() => {
signFileLoading.value = false;
ElMessage.error('文件生成失败');
});
// downloadPdf(res.data || '')
} else {
signFileLoading.value = false;
res?.msg && proxy.$ElMessage.error(res?.msg)
}
})
})
} catch {
signFileLoading.value = false;
}
}, 50)
}
const deleteSignFile = () => {
proxy.$openMessageBox('确定要删除该签名文件吗?', () => {
signFileUrlInfo.value = {};
proxy.$ElMessage({
type: "success",
message: "删除成功",
});
}, () => {
proxy.$ElMessage.info("已取消删除");
})
}
/** ------------------------- 履约信息 ------------------------- */
const execContractTableInfo = ref({
id: "exec-contract-table",
height: '214px',
fields: <any[]>[
{ label: "序号", type: "index", width: TableColumnWidth.INDEX, align: "center" },
{ label: "策略id", field: "strategyId", width: 260 },
{ label: "操作行为", field: "action", width: 120 },
{ label: "操作行为英文名称", field: "actionEnName", width: 140 },
{ label: "约束条件", field: "constraintName", width: 120 },
{ label: "约束条件英文名称", field: "constraintEnName", width: 140 },
{ label: "约束条件运算符", field: "constraintOperatorCodeName", width: 140 },
{ label: "约束条件值", field: "constraintValue", width: 150 },
{ label: "执行结果", field: "result", width: 130 },
{ label: "上报时间", field: "reportingTime", width: 170 },
],
data: [],
showPage: true,
page: {
type: "normal",
rows: 0,
...commonPageConfig,
},
actionInfo: {
show: false
},
loading: false
});
const pageChange = (info) => {
execContractTableInfo.value.page.curr = Number(info.curr);
execContractTableInfo.value.page.limit = Number(info.limit);
getStepFourExecInfo();
};
/** ------------------ 合约解除 ----------------------- */
const rejectInfoFormRef = ref();
const terminateTypeList: any = ref([]);
const rejectInfoFormItems = ref([{
label: '解除类型', //合约解除类型
type: 'select',
placeholder: '请选择',
field: 'terminateTypeCode',
default: 'TQ',
options: terminateTypeList.value,
filterable: true,
required: true,
visible: true,
disabled: false
}, {
label: '合约生效时间',
type: 'input',
placeholder: '-',
field: 'activationTime',
default: '',
disabled: true,
visible: true
}, {
label: '合约终止时间',
type: 'input',
placeholder: '-',
field: 'scheduledEndTime',
default: '',
disabled: true,
visible: true
}, {
label: "合同约定终止时间",
type: "datetime",
field: "agreedEndTime",
defaultTime: new Date(2000, 1, 1, 23, 59, 59),
default: null,
placeholder: "请选择",
clearable: true,
// disabledDate: (date) => {
// const today = new Date();
// // 将 today 设置为 00:00:00,只比较日期部分(本地时间)
// const todayStart = new Date(
// today.getFullYear(),
// today.getMonth(),
// today.getDate()
// );
// // 将 date 也转换为本地日期的开始时间(避免 UTC 问题)
// const dateStart = new Date(
// date.getFullYear(),
// date.getMonth(),
// date.getDate()
// );
// // 禁用 dateStart < todayStart 的日期(即今天之前)
// return dateStart.getTime() < todayStart.getTime();
// },
visible: true, //提前解除才展示
required: true,
}, {
label: '合约解除原因',
type: 'textarea',
placeholder: '请输入',
field: 'issueReason',
default: '',
rows: 2,
clearable: true,
required: true,
block: true,
visible: true, //提前解除才展示
maxlength: 200
}, {
label: '数据处理方式',
type: 'textarea',
placeholder: '请输入',
field: 'dataProcessingMethod',
default: '',
clearable: true,
required: true,
rows: 2,
block: true,
visible: true,
maxlength: 200
}, {
label: '数据处理指令',
type: 'textarea',
placeholder: '请输入',
field: 'dataProcessingInstructions',
default: '',
clearable: true,
required: true,
rows: 2,
block: true,
visible: true,
maxlength: 200
}, {
label: "数据处理证明",
tip: "单个文件不得大于20M",
type: "upload-file",
limitSize: 20,
limit: 1,
default: [],
field: "dataProcessingProof",
required: true,
},]);
const rejectInfoFormRules = ref({
terminateTypeCode: [required('请选择解除类型')],
agreedEndTime: [required('请选择合同约定终止时间')],
issueReason: [required('请填写合同解除原因')],
dataProcessingMethod: [required('请填写数据处理方式')],
dataProcessingInstructions: [required('请填写数据处理指令')],
dataProcessingProof: [requiredFiles()],
});
const handleRejectFormSelectChange = (val, row, info) => {
if (row.field == 'terminateTypeCode') {
rejectInfoFormItems.value[3].visible = val == 'TQ';
rejectInfoFormItems.value[4].visible = val == 'TQ';
rejectInfoFormItems.value.forEach(item => {
item.default = info[item.field];
})
}
}
/** --- 合约协商信息 -- */
const consultDetailInfo: any = ref({});
/** 约束运算符字典下拉 */
const operatorOptionList: any = ref([]);
/** 约束行为下拉列表 */
const constraintOptionsList: any = ref([]);
/** 策略操作行为下拉列表 */
const actionOptionsList: any = ref([]);
/** 获取第二步合约协商信息 */
const getStepTwoNegotiateInfo = (loading = true) => {
fullscreenLoading.value = true;
getContractNegotiate(route.query.guid).then((res: any) => {
loading && (fullscreenLoading.value = false);
if (res?.code == proxy.$passCode) {
consultDetailInfo.value = res.data || {};
let negotiateRecord = consultDetailInfo.value.negotiateRecord || {};
activities.value = [];
for (const key in negotiateRecord) {
activities.value.push({
timestamp: key,
type: 'primary',
hollow: true,
children: negotiateRecord[key] || []
})
}
} else {
res?.msg && proxy.$ElMessage.error(res?.msg)
}
})
}
/** 已签署过的详情信息 */
const signDetailInfo: any = ref([]);
const getStepThreeSignInfo = () => {
fullscreenLoading.value = true;
return getSignListInfo(route.query.guid).then((res: any) => {
// fullscreenLoading.value = false;
if (res?.code == proxy.$passCode) {
signDetailInfo.value = res.data || [];
} else {
res?.msg && proxy.$ElMessage.error(res?.msg)
}
})
}
/** 履约证明信息 */
// const execProofDetailInfo: any = ref({});
/** 获取履约执行信息和证明信息 */
const getStepFourExecInfo = () => {
// getContractProof(route.query.guid).then((res: any) => {
// if (res?.code == proxy.$passCode) {
// execProofDetailInfo.value = res.data || {};
// } else {
// res?.msg && proxy.$ElMessage.error(res?.msg)
// }
// })
execContractTableInfo.value.loading = true;
getContractExecList({
contractGuid: route.query.guid,
pageIndex: execContractTableInfo.value.page.curr,
pageSize: execContractTableInfo.value.page.limit,
}).then((res: any) => {
execContractTableInfo.value.loading = false;
if (res?.code == proxy.$passCode) {
const data = res.data || {};
execContractTableInfo.value.data = data.records || []
execContractTableInfo.value.page.limit = data.pageSize
execContractTableInfo.value.page.curr = data.pageIndex
execContractTableInfo.value.page.rows = data.totalRows
} else {
res?.msg && proxy.$ElMessage.error(res?.msg)
}
})
}
/** 终止合约信息详情 */
const terminateDetailInfo: any = ref({});
/** 获取第五步终止合约信息详情 */
const getStepTerminateInfo = () => {
fullscreenLoading.value = true;
getTerminateDetailInfo(route.query.guid).then((res: any) => {
fullscreenLoading.value = false;
if (res?.code == proxy.$passCode) {
terminateDetailInfo.value = res.data || {};
} else {
res?.msg && proxy.$ElMessage.error(res?.msg)
}
})
}
/** 企业注册认证信息详情,供签署时显示 */
const signTenantDetailInfo: any = ref({});
onBeforeMount(() => {
if (isDetail.value) { //若是查看详情,默认都展示第一步。
currentStep.value = 1;
}
fullscreenLoading.value = true;
let detailPs = getContractDetail(route.query.guid).then((res: any) => {
detailType.value != 'sign' && (fullscreenLoading.value = false);
if (res?.code == proxy.$passCode) {
detailInfo.value = res.data || {};
if (isDetail.value || detailType.value == 'reject') {
detailInfo.value.negotiateEntityGuid && getStepTwoNegotiateInfo();
detailInfo.value.signatureList?.length && getStepThreeSignInfo();
detailInfo.value.contractStatus == '05' && getStepFourExecInfo();
detailInfo.value.contractStatus == '06' && getStepFourExecInfo();
detailInfo.value.contractStatus == '06' && getStepTerminateInfo();
}
let executionNodes = detailInfo.value.executionNodes || [];
let provider = executionNodes.find(e => e.executionerTypeCode == '01');
let user = executionNodes.find(e => e.executionerTypeCode == '02');
nodeInfoFormItems.value[0].default = provider.executionerTypeName;
nodeInfoFormItems.value[1].default = provider.executionerName;
nodeInfoFormItems.value[2].default = provider.executionerId;
nodeInfoFormItems.value[3].default = user.executionerTypeName;
nodeInfoFormItems.value[4].default = user.executionerName;
nodeInfoFormItems.value[5].default = user.executionerId;
extendTableInfo.value.data = detailInfo.value.contractExpansions || [];
productTableInfo.value.data = detailInfo.value.contractSubjects || [];
} else {
res?.msg && proxy.$ElMessage.error(res?.msg)
}
})
// 企业来做协商处理,先调用详情接口。
if (detailType.value == 'consult') {
getStepTwoNegotiateInfo();
getParamsList({ dictType: '约束运算符' }).then((res: any) => {
if (res?.code == proxy.$passCode) {
const data = res.data || [];
operatorOptionList.value = data;
} else {
res?.msg && proxy.$ElMessage.error(res?.msg);
}
})
getActionPolicyList().then((res: any) => {
if (res?.code == proxy.$passCode) {
const data = res.data || [];
actionOptionsList.value = data;
} else {
res?.msg && proxy.$ElMessage.error(res?.msg);
}
})
getConstraintPolicyList().then((res: any) => {
if (res?.code == proxy.$passCode) {
const data = res.data || [];
constraintOptionsList.value = data;
} else {
res?.msg && proxy.$ElMessage.error(res?.msg);
}
})
} else if (detailType.value == 'sign') {
getStepTwoNegotiateInfo(false);
fullscreenLoading.value = true;
detailPs.then(() => {
fullscreenLoading.value = true;
let psSign = getStepThreeSignInfo();
let psLogon = getEnterpriseData({
logonUser: userData.tenantName == "非认证会员" ? userData.logonUser : tenantData.logonUser
}).then((res: any) => {
if (res?.code == proxy.$passCode) {
signTenantDetailInfo.value = res.data || {};
} else {
res?.msg && proxy.$ElMessage.error(res?.msg);
}
});
Promise.all([psSign, psLogon]).then(() => {
fullscreenLoading.value = false;
}).catch(() => {
fullscreenLoading.value = false;
})
})
} else if (detailType.value == 'reject') {
let psType = getParamsList({ dictType: '合约解除类型' }).then((res: any) => {
if (res?.code == proxy.$passCode) {
const data = res.data || [];
terminateTypeList.value = data;
rejectInfoFormItems.value[0].options = terminateTypeList.value;
} else {
res?.msg && proxy.$ElMessage.error(res?.msg);
}
})
// 详情跟类型都返回之后再设置选项
Promise.all([detailPs, psType]).then((res: any) => {
//选项待定,只有未到合同终止时间才可以选择提前解除。
let endTime = detailInfo.value.endTime;
let currDate = new Date();
let endDate = new Date(endTime);
if (currDate > endDate) { //终止时间大于当前时间,可以选择全部包括提前解除和到期解除
rejectInfoFormItems.value[0].options = terminateTypeList.value.filter(t => t.value == 'DQ');
rejectInfoFormItems.value[0].default = 'DQ'
} else {
rejectInfoFormItems.value[0].options = terminateTypeList.value.filter(t => t.value == 'TQ');
rejectInfoFormItems.value[0].default = 'TQ'
}
rejectInfoFormItems.value[1].default = detailInfo.value.signatureTime;
rejectInfoFormItems.value[2].default = detailInfo.value.endTime;
rejectInfoFormItems.value[3].visible = rejectInfoFormItems.value[0].default == 'TQ';
rejectInfoFormItems.value[4].visible = rejectInfoFormItems.value[0].default == 'TQ';
})
}
});
onActivated(() => {
let tab: any = userStore.tabbar.find((tab: any) => tab.fullPath === fullPath);
if (tab) {
if (detailType.value == 'consult') {
document.title = `合约协商-${route.query.name}`;
tab.meta.title = `合约协商-${route.query.name}`;
} else if (detailType.value == 'sign') {
document.title = `合约签署-${route.query.name}`;
tab.meta.title = `合约签署-${route.query.name}`;
} else if (detailType.value == 'reject') {
document.title = `合约解除-${route.query.name}`;
tab.meta.title = `合约解除-${route.query.name}`;
} else {
document.title = `合约详情-${route.query.name}`;
tab.meta.title = `合约详情-${route.query.name}`;
}
}
})
</script>
<template>
<div class="container_wrap full" v-loading="fullscreenLoading">
<div class="content_main panel">
<div class="first-title-row">
<div class="title-row">
<div class="title"><ellipsis-tooltip :content="detailInfo?.contractName || '--'" class-name="w100f"
:refName="'tooltipOver' + 'contractName'"></ellipsis-tooltip></div>
<el-tag :type="tagType(detailInfo, 'contractStatus')">{{ tagMethod(detailInfo, 'contractStatus') }}</el-tag>
</div>
<div class="list_panel mt6">
<div class="list_item">
<span class="item_label">合约编号:</span>
<span class="item_value"><ellipsis-tooltip :content="detailInfo?.contractId || '--'"
class-name="w100f mr8-i" :refName="'tooltipOver' + 'contractId'"></ellipsis-tooltip></span>
</div>
<div class="list_item">
<span class="item_label">合约版本:</span>
<span class="item_value">{{ detailInfo?.policys?.at(-1)?.version || 'v1.0.0' }}</span>
</div>
<div class="list_item">
<span class="item_label">创建时间:</span>
<span class="item_value">{{ detailInfo.createTime || '--' }}</span>
</div>
<div class="list_item">
<span class="item_label">发起主体:</span>
<span class="item_value"><ellipsis-tooltip :content="detailInfo?.tenantName || '--'"
class-name="w100f mr8-i" :refName="'tooltipOver' + 'tenantName'"></ellipsis-tooltip></span>
</div>
<div class="list_item">
<span class="item_label">签署时间:</span>
<span class="item_value">{{ detailInfo.signatureTime || '--' }}</span>
</div>
<div class="list_item">
<span class="item_label">有效期:</span>
<span class="item_value">{{ !detailInfo.signatureTime ? '--' : (detailInfo.signatureTime + '至' +
detailInfo.endTime) }}</span>
</div>
</div>
</div>
<div class="empty-div"></div>
<div class="progress">
<div :class="{ 'active': true, 'no-select': detailType && currentStep != 1 || (isDetail && currentStep > 1) }"
style="cursor: pointer;" @click="handleClickStep(1)">1、合约信息</div>
<div
:class="{ 'bg-gray': true, 'active': !!detailType || (isDetail && detailInfo?.negotiateEntityGuid), 'cursor-pointer': !!detailType || (isDetail && detailInfo?.negotiateEntityGuid), 'no-select': (currentStep && currentStep != 2) || (detailType != 'consult' && !currentStep) }"
@click="(!!detailType || (isDetail && detailInfo?.negotiateEntityGuid)) ? handleClickStep(2) : () => { }">
<div class="triangle-box">
<div
:class="{ 'triangle': true, 'active': true, 'no-select': detailType && currentStep != 1 || (isDetail && currentStep > 1) }">
</div>
</div>
2、合约协商
</div>
<div :class="{
'bg-gray': true, 'active': detailType == 'sign' || detailType == 'keepAgree' || detailType == 'reject' || (isDetail && detailInfo.signatureList?.length), 'cursor-pointer': detailType == 'sign' || detailType == 'keepAgree' || detailType == 'reject' || (isDetail && detailInfo.signatureList?.length),
'no-select': (currentStep && currentStep != 3) || (detailType != 'sign' && !currentStep)
}"
@click="(detailType == 'sign' || detailType == 'keepAgree' || detailType == 'reject' || (isDetail && detailInfo.signatureList?.length)) ? handleClickStep(3) : () => { }">
<div class="triangle-box">
<div
:class="{ 'triangle': true, 'active': !!detailType || (isDetail && detailInfo?.negotiateEntityGuid), 'no-select': (currentStep && currentStep != 2) || (detailType != 'consult' && !currentStep) }">
</div>
</div>
3、合约签署
</div>
<div :class="{
'bg-gray': true, 'active': detailType == 'keepAgree' || detailType == 'reject' || (isDetail && detailInfo.contractStatus == '06'), 'cursor-pointer': detailType == 'keepAgree' || detailType == 'reject' || (isDetail && detailInfo.contractStatus == '06'),
'no-select': (currentStep && currentStep != 4) || (detailType != 'keepAgree' && !currentStep)
}"
@click="(detailType == 'keepAgree' || detailType == 'reject' || (isDetail && detailInfo.contractStatus == '06')) ? handleClickStep(4) : () => { }">
<div class="triangle-box">
<div :class="{
'triangle': true, 'active': detailType == 'sign' || detailType == 'keepAgree' || detailType == 'reject' || (isDetail && detailInfo.signatureList?.length),
'no-select': (currentStep && currentStep != 3) || (detailType != 'sign' && !currentStep)
}">
</div>
</div>
4、合约履行
</div>
<div v-show="detailType == 'reject' || (isDetail && detailInfo.contractStatus == '06')" :class="{
'bg-gray': true, 'active': detailType == 'reject' || (isDetail && detailInfo.contractStatus == '06'), 'cursor-pointer': detailType == 'reject' || (isDetail && detailInfo.contractStatus == '06'),
'no-select': (currentStep && currentStep != 5) || (detailType != 'reject' && !currentStep)
}"
@click="(detailType == 'reject' || (isDetail && detailInfo.contractStatus == '06')) ? handleClickStep(5) : () => { }">
<div class="triangle-box">
<div
:class="{ 'triangle': true, 'active': detailType == 'keepAgree' || detailType == 'reject' || (isDetail && detailInfo.contractStatus == '06'), 'no-select': (currentStep && currentStep != 4) || (detailType != 'keepAgree' && !currentStep) }">
</div>
</div>
5、合约解除
</div>
</div>
<ContentWrap v-show="currentStep == 1" id="base-info" title="合约信息" expandSwicth style="margin-top: 15px"
:isExpand="expandBase" @expand="(v) => (expandBase = v)" description="">
<div class="list_panel">
<div class="list_item">
<span class="item_label">合约名称:</span>
<span class="item_value"><ellipsis-tooltip :content="detailInfo?.contractName || '--'"
class-name="w100f mr8-i" :refName="'tooltipOver' + 'contractName1'"></ellipsis-tooltip></span>
</div>
<div class="list_item">
<span class="item_label">签署模式:</span>
<span class="item_value"><ellipsis-tooltip :content="detailInfo?.signModeName || '--'"
class-name="w100f mr8-i" :refName="'tooltipOver' + 'signModeName'"></ellipsis-tooltip></span>
</div>
<div class="list_item">
<span class="item_label">终止时间:</span>
<span class="item_value"><ellipsis-tooltip :content="detailInfo?.endTime || '--'" class-name="w100f mr8-i"
:refName="'tooltipOver' + 'endTime'"></ellipsis-tooltip></span>
</div>
<div class="list_item is_block">
<span class="item_label">合约简介:</span>
<span class="item_value">{{ detailInfo.contractAbstract || '--' }}</span>
</div>
</div>
<Table ref="extendTableRef" :tableInfo="extendTableInfo" class="fiveRow-table" />
</ContentWrap>
<ContentWrap v-show="currentStep == 1" id="product-info" title="合约标的" expandSwicth style="margin-top: 15px"
:isExpand="expandInfo" @expand="(v) => (expandInfo = v)" description="">
<Table ref="productTableRef" :tableInfo="productTableInfo" class="fiveRow-table" />
</ContentWrap>
<ContentWrap v-show="currentStep == 1" id="policy-info" title="合约策略" expandSwicth style="margin-top: 15px"
:isExpand="expandPolicy" @expand="(v) => (expandPolicy = v)" description="">
<div class="h-title">策略执行节点</div>
<Form ref="nodeInfoFormRef" formId="node-info-form" :itemList="nodeInfoFormItems" col="col3" />
<StrategyTable ref="strategyTableRef" :show-title="true" :is-look="true" :value="detailInfo.policys || []">
</StrategyTable>
</ContentWrap>
<ContentWrap v-show="detailType == 'consult' && currentStep != 1" id="policy-info-consult" title="合约策略"
expandSwicth style="margin-top: 15px" :isExpand="expandPolicyConsult" @expand="(v) => (expandPolicyConsult = v)"
description="">
<StrategyTable ref="strategyTableEditRef" :show-title="true" :value="consultDetailInfo?.policys"
:operatorOptionList="operatorOptionList" :actionOptionsList="actionOptionsList"
:constraintOptionsList="constraintOptionsList"></StrategyTable>
</ContentWrap>
<ContentWrap v-show="detailType == 'consult' || currentStep == 2" id="history-info-consult" title="协商记录"
expandSwicth style="margin-top: 15px" :isExpand="expandConsultHistory"
@expand="(v) => (expandConsultHistory = v)" description="">
<el-timeline style="max-width: 900px">
<el-timeline-item v-for="(activity, index) in activities" :key="index" :timestamp="activity.timestamp"
:hollow="activity.hollow" :type="activity.type" placement="top">
<div v-for="(child, childIndex) of activity.children" class="row-per"
:style="{ 'margin-top': childIndex > 0 ? '8px' : '0px' }">
<div
:class="child.negotiateEntityType == '数据提供方' ? 'label' : (child.negotiateEntityType == '数据服务方' ? 'label2' : 'label1')">
{{ child.negotiateEntityType }}
</div>
<div style="width: 350px;" class="desc"> <ellipsis-tooltip
:content="'协商主体:' + (child?.negotiateEntityName ?? '--')" class-name="w100f"
:refName="'tooltipOverTenant'"></ellipsis-tooltip></div>
<div style="width: 200px;" class="desc">版本号:<span class="main" @click="clickVersion(child)">{{
child.version }}</span></div>
<div style="width: 250px;" class="desc">{{ '提交时间:' + child.submitTime }}</div>
</div>
</el-timeline-item>
</el-timeline>
</ContentWrap>
<ContentWrap v-show="!currentStep && detailType == 'sign' || currentStep == 3" id="sign-info" title="合约签署"
expandSwicth style="margin-top: 15px" :isExpand="expandSign" @expand="(v) => (expandSign = v)" description="">
<template v-for="(item, index) in signDetailInfo">
<div :class="{ 'h-title': true, 'mt6': index > 0 }">{{ item.executionerTypeName }}</div>
<div class="list_panel">
<div class="list_item wrap">
<span class="item_label">签署主体标识</span>
<span class="item_value"><ellipsis-tooltip :content="item?.signatureEntityId || '--'"
class-name="w100f mr8-i" :refName="'tooltipOver' + 'tenantCode'"></ellipsis-tooltip></span>
</div>
<div class="list_item wrap">
<span class="item_label">签署主体名称</span>
<span class="item_value row"><ellipsis-tooltip :content="item?.signatureEntityName || '--'"
class-name="w100f mr8-i custom" :refName="'tooltipOver' + 'signatureEntityName'"></ellipsis-tooltip>
<div :class="item.executionerTypeCode == '02' ? 'label1' : 'label'">{{ item.executionerTypeName }}</div>
</span>
</div>
<div class="list_item isFile">
<span class="item_label">身份凭证证书</span>
<template v-if="item?.identityCertificate?.url">
<span class="item_value link" @click="viewResultVoucherFile(item?.identityCertificate?.url)">查看</span>
</template>
<span v-else class="item_value">--</span>
</div>
<!-- 没有上传就不显示 -->
<div class="list_item isFile">
<span class="item_label">附件</span>
<template v-if="item?.attachment?.url">
<showFile :file="[item.attachment]"></showFile>
</template>
<span v-else class="item_value">--</span>
</div>
<div class="list_item isFile">
<span class="item_label">签署签名</span>
<template v-if="item?.signature?.url">
<showFile :file="[item.signature]"></showFile>
</template>
<span v-else class="item_value">--</span>
</div>
<div class="list_item wrap">
<span class="item_label">签署时间</span>
<span class="item_value"><ellipsis-tooltip :content="item?.signatureTime || '--'" class-name="w100f mr8-i"
:refName="'tooltipOver' + 'signatureTime'"></ellipsis-tooltip></span>
</div>
</div>
</template>
<template v-if="detailType == 'sign'">
<div class="h-title mt6">{{ userData.tenantGuid == detailInfo.dataUserGuid ? '数据使用方' : '数据提供方' }}</div>
<div class="list_panel">
<div class="list_item wrap">
<span class="item_label">签署主体标识</span>
<span class="item_value"><ellipsis-tooltip :content="signTenantDetailInfo?.socialCreditCode || '--'"
class-name="w100f mr8-i" :refName="'tooltipOver' + 'tenantCode'"></ellipsis-tooltip></span>
</div>
<div class="list_item wrap">
<span class="item_label">签署主体名称</span>
<span class="item_value row"><ellipsis-tooltip :content="userData?.tenantName || '--'"
class-name="w100f mr8-i custom" :refName="'tooltipOver' + 'tenantName'"></ellipsis-tooltip>
<div :class="userData.tenantGuid == detailInfo.dataUserGuid ? 'label1' : 'label'">{{ userData.tenantGuid
== detailInfo.dataUserGuid ? '数据使用方' : '数据提供方' }}</div>
</span>
</div>
<div class="list_item isFile">
<span class="item_label">身份凭证证书</span>
<template v-if="signTenantDetailInfo?.trustedIdentityCredential">
<span class="item_value link"
@click="viewResultVoucherFile(signTenantDetailInfo?.trustedIdentityCredential)">查看</span>
</template>
<span v-else class="item_value">--</span>
</div>
</div>
<div style="display: flex">
<Form class="mt6" ref="signInfoFormRef" formId="sign-info-form" :itemList="signInfoFormItems"
:rules="signInfoFormRules" col="col3" style="width: 33.33%;" />
<div class="sign-main" style="width: 33.33%;">
<div class="item-label" style="margin-bottom: 2px;">签署签名<span class="required">*</span></div>
<el-button v-show="!signFileUrlInfo?.url" type="primary" :loading="signFileLoading"
@click="geneSignFile">生成签名文件</el-button>
<template v-if="signFileUrlInfo?.url">
<showFile :file="[signFileUrlInfo]" :show-remove="true" @deleteFile="deleteSignFile"></showFile>
</template>
</div>
</div>
</template>
</ContentWrap>
<el-checkbox style="margin-top: 4px;" v-show="detailType == 'sign' && (!currentStep || currentStep == 3)"
v-model="confirmAuth">确认提交代表您已知晓本次签署行为所包含的责任义务,并授权允许平台使用本次操作的系统日志作为纠纷处理证明。</el-checkbox>
<ContentWrap v-show="!currentStep && detailType == 'keepAgree' || currentStep == 4" id="keep-info"
title="合约执行履约信息" expandSwicth style="margin-top: 15px" :isExpand="expandKeep" @expand="(v) => (expandKeep = v)"
description="">
<template v-if="execContractTableInfo.data?.length">
<!-- <div class="h-title">执行信息</div> -->
<el-table ref="execContractTableRef" :data="execContractTableInfo.data" :highlight-current-row="true" stripe
v-loading="execContractTableInfo.loading" border height="100%" tooltip-effect="light" row-key="guid" :style="{
width: '100%',
height: '300px',
display: 'inline-block',
}">
<el-table-column type="expand">
<template #default="scope">
<div class="h-title mt6">履约证明</div>
<div class="list_panel" style="width: 1000px;">
<div class="list_item">
<span class="item_label">日志摘要:</span>
<span class="item_value"><ellipsis-tooltip :content="scope.row?.contractProof?.logHash || '--'"
class-name="w100f mr8-i" :refName="'tooltipOver' + 'logHash'"></ellipsis-tooltip></span>
</div>
<div class="list_item">
<span class="item_label">区块链交易ID:</span>
<span class="item_value row"><ellipsis-tooltip
:content="scope.row?.contractProof?.blockchainTx || '--'" class-name="w100f mr8-i"
:refName="'tooltipOver' + 'blockchainTx'"></ellipsis-tooltip>
</span>
</div>
<div class="list_item">
<span class="item_label">发送时间:</span>
<span class="item_value"><ellipsis-tooltip :content="scope.row?.contractProof?.sendTime || '--'"
class-name="w100f mr8-i" :refName="'tooltipOver' + 'sendTime'"></ellipsis-tooltip></span>
</div>
<div class="list_item" style="width: 50%;">
<span class="item_label">履约连接器签名:</span>
<!-- <template v-if="scope.row?.signature?.url">
<showFile :file="[execProofDetailInfo.signature]"></showFile>
</template> -->
<span class="item_value">{{ scope.row?.contractProof?.signature || '--' }}</span>
</div>
</div>
</template>
</el-table-column>
<el-table-column v-for="(item, i) in execContractTableInfo.fields" :label="item.label" :width="item.width"
:align="item.align" :prop="item.field" show-overflow-tooltip>
<template #default="scope">
<span v-if="item.type == 'index'">
{{
execContractTableInfo.page.curr !== undefined
? (execContractTableInfo.page.curr - 1) * execContractTableInfo.page.limit + scope.$index + 1
: scope.$index + 1
}}
</span>
<!-- <el-tag v-else-if="item.type == 'tag'"
:type="item.tagType ? item.tagType(scope) : tagType(scope.row, item.field)">{{
item.getName ? item.getName(scope) : tagMethod(scope.row, item.field)
}}</el-tag> -->
<span v-else>
{{ item.getName ? item.getName(scope) : scope.row[item.field] !== 0 && !scope.row[item.field]
?
"--" : scope.row[item.field] }}
</span>
</template>
</el-table-column>
</el-table>
<PageNav :class="[execContractTableInfo.page.type]" :pageInfo="execContractTableInfo.page"
@pageChange="pageChange" />
<!-- <Table ref="execContractTableRef" :tableInfo="execContractTableInfo" class="fiveRow-table" /> -->
</template>
<div class="empty-content" v-if="!execContractTableInfo.data?.length">
<img src="../../assets/images/empty-data.png" :style="{ width: '168px', height: '96px' }" />
<div class="empty-text">暂无履约数据</div>
</div>
</ContentWrap>
<ContentWrap v-show="!isDetail && (!currentStep && detailType == 'reject' || currentStep == 5)" id="reject-info"
title="合约解除信息" expandSwicth style="margin-top: 15px" :isExpand="expandReject"
@expand="(v) => (expandReject = v)" description="">
<Form ref="rejectInfoFormRef" formId="reject-info-form" :itemList="rejectInfoFormItems"
:rules="rejectInfoFormRules" col="col3" @select-change="handleRejectFormSelectChange" />
</ContentWrap>
<ContentWrap v-show="isDetail && currentStep == 5" id="reject-info" title="合约解除信息" expandSwicth
style="margin-top: 15px" :isExpand="expandReject" @expand="(v) => (expandReject = v)" description="">
<div class="list_panel">
<div class="list_item">
<span class="item_label">解除类型:</span>
<span class="item_value"><ellipsis-tooltip :content="terminateDetailInfo?.terminateTypeCodeName || '--'"
class-name="w100f mr8-i" :refName="'tooltipOver' + 'terminateTypeCodeName'"></ellipsis-tooltip></span>
</div>
<div class="list_item">
<span class="item_label">合同生效时间:</span>
<span class="item_value row">{{ terminateDetailInfo?.activationTime || '--' }}
</span>
</div>
<div class="list_item">
<span class="item_label">合同终止时间:</span>
<span class="item_value row">{{ terminateDetailInfo?.scheduledEndTime || '--' }}
</span>
</div>
<div class="list_item" v-if="terminateDetailInfo.terminateTypeCode == 'TQ'">
<span class="item_label">合同约定终止时间:</span>
<span class="item_value row">{{ terminateDetailInfo?.agreedEndTime || '--' }}
</span>
</div>
<div class="list_item">
<span class="item_label">合同解除时间:</span>
<span class="item_value row">{{ terminateDetailInfo?.issueTime || '--' }}
</span>
</div>
<div class="list_item is_block" v-if="terminateDetailInfo.terminateTypeCode == 'TQ'">
<span class="item_label">合约解除原因:</span>
<span class="item_value row">{{ terminateDetailInfo?.issueReason || '--' }}
</span>
</div>
<div class="list_item is_block">
<span class="item_label">数据处理方式:</span>
<span class="item_value row">{{ terminateDetailInfo?.dataProcessingMethod || '--' }}
</span>
</div>
<div class="list_item is_block">
<span class="item_label">数据处理指令:</span>
<span class="item_value row">{{ terminateDetailInfo?.dataProcessingInstructions || '--' }}
</span>
</div>
<div class="list_item isFile">
<span class="item_label">数据处理证明:</span>
<template v-if="terminateDetailInfo?.dataProcessingProof?.url">
<showFile :file="[terminateDetailInfo?.dataProcessingProof]"></showFile>
</template>
<span v-else class="item_value">--</span>
</div>
</div>
</ContentWrap>
</div>
<div class="tool_btns">
<div class="btns">
<el-button v-for="btn in toolBtns" :type="btn.type" :disabled="btn.disabled" :plain="btn.plain"
@click="btnHandles[btn.value]">{{
btn.label
}}</el-button>
</div>
</div>
<Dialog ref="dialogRef" :dialogInfo="detailDialogInfo" class="policy-table-detail"
@btnClick="handleVersionDialogBtnClick">
<template #extra-content>
<div v-loading="detailDialogInfo.contentLoading">
<StrategyTable ref="strategyTableDetailRef" :show-title="true" :is-look="true" :value="versionDetail">
</StrategyTable>
</div>
</template>
</Dialog>
<ConfirmDialog :visible="confirmDialogInfo.dialogVisible" :type="confirmDialogInfo.type"
:msg="confirmDialogInfo.msg[confirmDialogInfo.type]" @btn-click="handleConfirmDialog"></ConfirmDialog>
<div id="pdf-content" style="display: none;">
<div class="first-title-row" style="padding: 0px;">
<div class="title-row" style="align-items: flex-start;">
<div class="title" style="white-space: pre-wrap;word-break: break-all;">{{ detailInfo?.contractName || '--' }}
</div>
<!-- <el-tag :type="tagType(detailInfo, 'contractStatus')">{{ tagMethod(detailInfo, 'contractStatus') }}</el-tag> -->
</div>
<div class="list_panel two-per">
<div class="list_item" style=" width: 55%;">
<span class="item_label">合约编号:</span>
<span class="item_value"><ellipsis-tooltip :content="detailInfo?.contractId || '--'"
class-name="w100f mr8-i" :refName="'tooltipOver' + 'contractId'"></ellipsis-tooltip></span>
</div>
<div class="list_item" style=" width: 45%;">
<span class="item_label">合约版本:</span>
<span class="item_value">{{ detailInfo?.policys?.at(-1)?.version || 'v1.0.0' }}</span>
</div>
<div class="list_item" style=" width: 55%;">
<span class="item_label">发起主体:</span>
<span class="item_value">{{ detailInfo?.tenantName || '--' }}</span>
</div>
<div class="list_item" style=" width: 45%;">
<span class="item_label">创建时间:</span>
<span class="item_value">{{ detailInfo.createTime || '--' }}</span>
</div>
<div class="list_item" style=" width: 55%;">
<span class="item_label">有效期:</span>
<span class="item_value">{{ !detailInfo.signatureTime ? '--' : (detailInfo.signatureTime + '至' +
detailInfo.endTime) }}</span>
</div>
<div class="list_item" style=" width: 45%;">
<span class="item_label">签署时间:</span>
<span class="item_value">{{ detailInfo.signatureTime || '--' }}</span>
</div>
</div>
</div>
<div class="empty-div"></div>
<ContentWrap id="base-info" title="合约信息" style="margin-top: 12px" :isExpand="expandBase"
@expand="(v) => (expandBase = v)" description="">
<div class="list_panel two-per">
<div class="list_item is_block">
<span class="item_label">合约名称:</span>
<span class="item_value">{{ detailInfo?.contractName || '--' }}</span>
</div>
<div class="list_item">
<span class="item_label">签署模式:</span>
<span class="item_value"><ellipsis-tooltip :content="detailInfo?.signModeName || '--'"
class-name="w100f mr8-i" :refName="'tooltipOver' + 'signModeName'"></ellipsis-tooltip></span>
</div>
<div class="list_item">
<span class="item_label">终止时间:</span>
<span class="item_value"><ellipsis-tooltip :content="detailInfo?.endTime || '--'" class-name="w100f mr8-i"
:refName="'tooltipOver' + 'endTime'"></ellipsis-tooltip></span>
</div>
<div class="list_item is_block">
<span class="item_label">合约简介:</span>
<span class="item_value">{{ detailInfo.contractAbstract || '--' }}</span>
</div>
</div>
<Table v-if="exportExtendTableInfo.data?.length" ref="extendTableRef" :tableInfo="exportExtendTableInfo" />
</ContentWrap>
<ContentWrap id="product-info" title="合约标的" style="margin-top: 12px;" :isExpand="expandInfo"
@expand="(v) => (expandInfo = v)" description="">
<Table ref="productTableRef" :tableInfo="exportProductTableInfo" />
</ContentWrap>
<ContentWrap id="policy-info" title="合约策略" style="margin-top: 12px;" :isExpand="expandPolicy"
@expand="(v) => (expandPolicy = v)" description="">
<div class="h-title">策略执行节点</div>
<!-- <Form ref="nodeInfoFormRef" formId="node-info-form" :itemList="nodeInfoFormItems" col="col3" /> -->
<div class="list_panel">
<div class="list_item wrap" style="width: 20%;">
<span class="item_label">策略执行者类型</span>
<span class="item_value"><ellipsis-tooltip :content="nodeInfoFormItems[0].default || '--'"
class-name="w100f mr8-i" :refName="'tooltipOver1'"></ellipsis-tooltip></span>
</div>
<div class="list_item wrap" style="width: 40%;">
<span class="item_label">策略执行者名称</span>
<span class="item_value"><ellipsis-tooltip :content="nodeInfoFormItems[1].default || '--'"
class-name="w100f mr8-i custom" :refName="'tooltipOver2'"></ellipsis-tooltip>
</span>
</div>
<div class="list_item wrap" style="width: 40%;">
<span class="item_label">策略执行者标识</span>
<span class="item_value"><ellipsis-tooltip :content="nodeInfoFormItems[2].default || '--'"
class-name="w100f mr8-i custom" :refName="'tooltipOver3'"></ellipsis-tooltip>
</span>
</div>
<div class="list_item wrap" style="width: 20%;">
<span class="item_label">策略执行者类型</span>
<span class="item_value"><ellipsis-tooltip :content="nodeInfoFormItems[3].default || '--'"
class-name="w100f mr8-i" :refName="'tooltipOver4'"></ellipsis-tooltip></span>
</div>
<div class="list_item wrap" style="width: 40%;">
<span class="item_label">策略执行者名称</span>
<span class="item_value"><ellipsis-tooltip :content="nodeInfoFormItems[4].default || '--'"
class-name="w100f mr8-i custom" :refName="'tooltipOver5'"></ellipsis-tooltip>
</span>
</div>
<div class="list_item wrap" style="width: 40%;">
<span class="item_label">策略执行者标识</span>
<span class="item_value"><ellipsis-tooltip :content="nodeInfoFormItems[5].default || '--'"
class-name="w100f mr8-i custom" :refName="'tooltipOver6'"></ellipsis-tooltip>
</span>
</div>
</div>
<StrategyTable ref="strategyTableRef" :show-title="true" :is-look="true" :isReport="true"
:value="detailInfo.policys || []">
</StrategyTable>
</ContentWrap>
<div class="sign-file-main">
<div class="title-desc">{{ userData.tenantGuid == detailInfo.dataUserGuid ? '数据使用方' : '数据提供方' }}签章:</div>
<div class="title-desc">法人签名:</div>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.container_wrap {
overflow: hidden;
.content_main {
height: calc(100% - 45px);
overflow: hidden auto;
&.panel {
padding: 0 16px 16px;
}
}
}
.first-title-row {
background-color: #fff;
padding: 14px 0px 0px 0px;
.title-row {
display: flex;
flex-direction: row;
align-items: center;
.title {
font-size: 18px;
color: #212121;
line-height: 27px;
font-weight: 600;
max-width: calc(100% - 120px);
margin-right: 8px;
}
}
}
.list_panel {
display: flex;
flex-wrap: wrap;
display: flex;
align-items: flex-start;
&.main {
.list_item {
width: 25%;
}
}
&.two-per {
.list_item {
width: 50%;
}
}
.list_item {
width: 33.33%;
line-height: 32px;
font-size: 14px;
color: var(--el-text-color-regular);
display: flex;
justify-content: space-between;
min-width: 120px;
&.wrap {
flex-direction: column;
.item_value {
padding: 0px 4px 0px 0px;
}
}
.item_label {
text-align: left;
}
.item_value {
color: var(--el-color-regular);
padding: 0 4px;
flex: 1;
text-align: justify;
min-width: 0;
&.link {
color: var(--el-color-primary);
cursor: pointer;
}
&.row {
display: flex;
justify-content: flex-start;
align-items: center;
flex-direction: row;
:deep(.custom) {
width: auto;
max-width: calc(100% - 75px);
}
}
}
&.is_block {
width: 100%;
.item_value {
white-space: pre-wrap;
}
}
&.isFile {
// width: 50%;
display: flex;
flex-direction: column;
justify-content: space-between;
.item_label {
// width: 100px;
// text-align: right;
flex-shrink: 0;
}
.item_value {
flex: 1 1 0%;
min-width: 100px;
white-space: pre-wrap;
}
}
}
}
.empty-div {
height: 4px;
background: #E2E7EF;
margin: 12px -16px 0px;
}
.mt6 {
margin-top: 6px;
}
.tool_btns {
height: 44px;
margin: 0 -8px;
display: flex;
justify-content: center;
align-items: center;
border-top: 1px solid #d9d9d9;
}
.h-title {
font-size: 14px;
color: #212121;
font-weight: 600;
margin-bottom: 4px;
}
:deep(.el-tag.el-tag--primary) {
color: #0E5FD8;
background: #F2F9FF;
border: 1px solid rgba(224, 239, 255, 1);
min-width: 56px;
}
.progress {
width: 100%;
display: flex;
justify-content: start;
margin-top: 12px;
margin-bottom: 12px;
}
.progress>div {
width: 25%;
height: 28px;
line-height: 28px;
color: white;
position: relative;
text-align: center;
&.bg-gray {
color: #666;
background-color: #F2F2F2;
}
&.active {
background-color: #5797FF;
color: #fff;
&.no-select {
background-color: #85B8FF;
}
}
}
/* 三角形 */
.triangle-box {
display: inline-block;
width: 30px;
height: 28px;
overflow: hidden;
position: absolute;
left: 0;
}
.triangle {
transform: rotate(45deg);
transform-origin: left top;
position: absolute;
top: -3px;
left: 0px;
width: 24px;
height: 28px;
border: 3px solid white;
&.active {
background-color: #5797FF;
&.no-select {
background-color: #85B8FF;
}
}
}
.cursor-pointer {
cursor: pointer;
}
:deep(.el-timeline) {
margin-left: -16px;
.el-timeline-item__tail {
height: calc(100% - 18px);
top: 14px;
}
.el-timeline-item__timestamp {
color: #212121;
}
.time-title {
color: #212121;
}
.row-per {
display: flex;
flex-direction: row;
align-items: center;
.desc {
color: #999;
.main {
color: var(--el-color-primary);
cursor: pointer;
}
}
}
}
.label {
width: 70px;
background: #FF854D;
border-radius: 2px;
font-size: 12px;
color: #FFFFFF;
text-align: center;
line-height: 20px;
font-weight: 400;
margin-right: 4px;
white-space: pre;
}
.label1 {
width: 70px;
background: #5797FF;
border-radius: 2px;
font-size: 12px;
color: #FFFFFF;
text-align: center;
line-height: 20px;
font-weight: 400;
margin-right: 4px;
white-space: pre;
}
.label2 {
width: 70px;
background: #40BD6E;
border-radius: 2px;
font-size: 12px;
color: #FFFFFF;
text-align: center;
line-height: 20px;
font-weight: 400;
margin-right: 4px;
white-space: pre;
}
:deep(.policy-table-detail) {
.dialog_content {
padding: 0px 20px 20px;
}
}
.empty-content {
display: flex;
align-items: center;
justify-content: center;
height: 250px;
width: 100%;
flex-direction: column;
.empty-text {
font-size: 14px;
color: #b2b2b2;
}
}
:deep(.dialog-form-inline.col3) {
align-items: flex-start;
}
:deep(.el-table) {
.el-table__expanded-cell {
padding: 16px;
}
}
.sign-main {
.item-label {
line-height: 21px;
color: #666;
}
.required {
color: #fb2323;
margin-left: 2px;
}
}
#pdf-content {
padding: 12px;
width: 780px;
:deep(.el-table .cell) {
white-space: normal;
word-break: break-word;
}
}
.sign-file-main {
display: flex;
flex-direction: row;
width: 100%;
margin-top: 24px;
.title-desc {
width: 50%;
font-size: 18px;
color: #212121;
font-weight: 600;
}
}
.avoid-break {
page-break-inside: avoid;
break-inside: avoid;
}
</style>
\ No newline at end of file
<route lang="yaml">
name: smartContractManagement
</route>
<script lang="ts" setup name="smartContractManagement">
import { ref } from 'vue';
import TableTools from "@/components/Tools/table_tools.vue";
import {
getContractPageList,
contractStatusList,
deleteContract,
cancelContract,
} from "@/api/modules/dataSmartContract"
import useDataSmartContract from "@/store/modules/dataSmartContract";
import { commonPageConfig } from '@/utils/enum';
import { getEnterpriseData } from "@/api/modules/dataIdentify";
const userData = JSON.parse(localStorage.userData);
const tenantData = JSON.parse(localStorage.tenantInfo);
const router = useRouter();
const route = useRoute();
const { proxy } = getCurrentInstance() as any;
const dataSmartContractStore = useDataSmartContract();
const searchItemList = ref([
{
type: "input",
label: "",
field: "contractName",
default: "",
placeholder: "合约名称",
maxlength: 50,
clearable: true,
},
{
type: 'select',
label: '',
field: 'contractStatus',
default: '',
placeholder: '合约状态',
options: contractStatusList,
filterable: true,
clearable: true
}
]);
const tableFields = ref([
{ label: "序号", type: "index", width: 56, align: "center" },
{ label: "合约名称", field: "contractName", width: 160, },
{ label: "签署方式", field: "signModeName", width: 120 },
{ label: "合约状态", field: "contractStatus", type: "tag", width: 96, align: 'center' },
{ label: "合约编号", field: "contractId", width: 355 },
{ label: "发起主体", field: "tenantName", width: 205 },
{ label: "创建人", field: "createUserName", width: 130 },
{ label: "创建时间", field: "createTime", width: 170 },
{ label: "提交企业", field: "submitTenantName", width: 200 },
{ label: "提交时间", field: "submitTime", width: 170 },
]);
const page = ref({
...commonPageConfig,
contractName: '',
contractStatus: ''
});
const currTableData: any = ref({});
const tableInfo = ref({
id: 'contract-table',
rowKey: 'guid',
loading: false,
fields: tableFields.value,
data: [],
page: {
type: "normal",
rows: 0,
...page.value,
},
actionInfo: {
label: "操作",
type: "btn",
width: 160,
btns: (scope) => {
let row = scope.row;
let btns: any = [];
let contractStatus = row.contractStatus;
if (contractStatus == '00') { //撤回状态,如果是合约发起方有重新提交按钮,否则只能查看
if (userData.tenantGuid == row.tenantGuid) { //是本人发起的。
btns.push({ value: 'view', label: '查看', click: () => btnHandles['view'](scope) });
row.isDisable != 'Y' && btns.push({ value: 'edit', label: '重新提交', click: () => btnHandles['edit'](scope) });
btns.push({ value: 'delete', label: '删除', click: () => btnHandles['delete'](scope) })
} else {
btns.push({ value: 'view', label: '查看', click: () => btnHandles['view'](scope) });
}
} else if (contractStatus == '01') { //发起状态
btns.push({ value: 'edit', label: '编辑', click: () => btnHandles['edit'](scope) });
btns.push({ value: 'delete', label: '删除', click: () => btnHandles['delete'](scope) })
} else if (contractStatus == '02') { // 协商
btns.push({ value: 'view', label: '查看', click: () => btnHandles['view'](scope) });
/**
* 1. 中介签署,只需要两方都协商,中介不显示按钮。
* 2. 点对点的话,只有对方需要协商。只要有继续协商变动之后,就需要对方确认。发起方提交后,如果对方还没协商过,发起方就没有协商按钮。
*/
if (row.signModeCode == '01') {
if (row.negotiateEntityGuid == row.dataProviderGuid && userData.tenantGuid == row.dataUserGuid || (row.negotiateEntityGuid == row.dataUserGuid && userData.tenantGuid == row.dataProviderGuid)) {
btns.push({ value: 'consult', label: '协商', click: () => btnHandles['consult'](scope) });
}
} else { //中介协商。
if (!row.confirmGuids?.length) { //没有确认guids。
if (row.negotiateEntityGuid != row.dataProviderGuid && row.negotiateEntityGuid != row.dataUserGuid) { //都不是,是中介。
if ((userData.tenantGuid == row.dataUserGuid || userData.tenantGuid == row.dataProviderGuid)) { //中介新建,两方都有。
btns.push({ value: 'consult', label: '协商', click: () => btnHandles['consult'](scope) });
}
}
} else { //其中一方继续协商或确认都有confirmGuids
// 其中一方确认。协商guid还是上次的。
if (!row.confirmGuids.includes(userData.tenantGuid) && (userData.tenantGuid == row.dataUserGuid || userData.tenantGuid == row.dataProviderGuid)) {
btns.push({ value: 'consult', label: '协商', click: () => btnHandles['consult'](scope) });
}
}
}
// 合约发起方才有撤回按钮
if (userData.tenantGuid == row.tenantGuid) {
btns.push({ value: 'revoke', label: '撤回', click: () => btnHandles['revoke'](scope) });
}
} else if (contractStatus == '03') { //签署,签署过的一方不需要显示签署。
btns.push({ value: 'view', label: '查看', click: () => btnHandles['view'](scope) });
if (row.signModeCode == '01') {
if (!row.signatureList?.includes(userData.tenantGuid)) {
btns.push({ value: 'sign', label: '签署', click: () => btnHandles['sign'](scope) });
}
} else { //中介
if (!row.signatureList?.includes(userData.tenantGuid) && (userData.tenantGuid == row.dataUserGuid || userData.tenantGuid == row.dataProviderGuid)) {
btns.push({ value: 'sign', label: '签署', click: () => btnHandles['sign'](scope) });
}
}
// 合约发起方才有撤回按钮
if (userData.tenantGuid == row.tenantGuid) {
btns.push({ value: 'revoke', label: '撤回', click: () => btnHandles['revoke'](scope) });
}
} else if (contractStatus == '05') { //合约履行中
btns.push({ value: 'view', label: '查看', click: () => btnHandles['view'](scope) });
//合约发起方才有合约解除
if (userData.tenantGuid == row.tenantGuid) {
btns.push({ value: 'termination', label: '解除', click: () => btnHandles['termination'](scope) });
}
} else if (contractStatus == '06' || contractStatus == '0302') {
btns.push({ value: 'view', label: '查看', click: () => btnHandles['view'](scope) });
}
return btns;
}
}
});
const btnHandles = {
edit: (scope) => {
router.push({
name: 'smartContractCreate',
query: {
guid: scope.row.guid,
name: scope.row.contractName
}
});
},
delete: (scope) => {
proxy.$openMessageBox("此操作将永久删除, 是否继续?", () => {
deleteContract([scope.row.guid]).then((res: any) => {
if (res?.code == proxy.$passCode) {
page.value.curr = 1;
getTableData();
proxy.$ElMessage.success('删除成功');
} else {
res?.msg && proxy.$ElMessage.error(res?.msg);
}
});
}, () => {
proxy.$ElMessage.info("已取消");
})
},
consult: (scope) => {
router.push({
name: 'smartContractDetail',
query: {
guid: scope.row.guid,
name: scope.row.contractName,
type: 'consult'
}
});
},
sign: (scope) => {
router.push({
name: 'smartContractDetail',
query: {
guid: scope.row.guid,
name: scope.row.contractName,
type: 'sign'
}
});
},
revoke: (scope) => {
proxy.$openMessageBox("确定撤回该连合约吗?", () => {
tableInfo.value.loading = true;
cancelContract(scope.row.guid).then((res: any) => {
tableInfo.value.loading = false;
if (res.code == proxy.$passCode) {
proxy.$ElMessage.success('合约撤回成功');
getTableData();
} else {
proxy.$ElMessage.error(res.msg);
}
});
}, () => {
proxy.$ElMessage.info("已取消撤回");
})
},
termination: (scope) => {//解除合同
router.push({
name: 'smartContractDetail',
query: {
guid: scope.row.guid,
name: scope.row.contractName,
type: 'reject'
}
});
},
view: (scope) => {//查看
if (scope.row.contractStatus == '05') {
router.push({
name: 'smartContractDetail',
query: {
guid: scope.row.guid,
name: scope.row.contractName,
type: 'keepAgree',
isDetail: 'Y'
}
});
} else {
router.push({
name: 'smartContractDetail',
query: {
guid: scope.row.guid,
name: scope.row.contractName,
isDetail: 'Y'
}
});
}
}
}
const toSearch = (val: any, clear: boolean = false) => {
if (clear) {
searchItemList.value.map((item) => (item.default = ""));
page.value.contractName = '';
page.value.contractStatus = "";
} else {
page.value.contractName = val.contractName;
page.value.contractStatus = val.contractStatus;
}
getTableData();
};
const getTableData = () => {
tableInfo.value.loading = true
getContractPageList({
pageIndex: page.value.curr,
pageSize: page.value.limit,
contractName: page.value.contractName,
contractStatus: page.value.contractStatus
}).then((res: any) => {
tableInfo.value.loading = false
if (res?.code == proxy.$passCode) {
const data = res.data || {}
tableInfo.value.data = data.records || []
tableInfo.value.page.limit = data.pageSize
tableInfo.value.page.curr = data.pageIndex
tableInfo.value.page.rows = data.totalRows
} else {
res?.msg && proxy.$ElMessage.error(res?.msg)
}
}).catch(() => {
tableInfo.value.loading = false
})
};
const tablePageChange = (info) => {
page.value.curr = Number(info.curr);
page.value.limit = Number(info.limit);
tableInfo.value.page.curr = page.value.curr;
tableInfo.value.page.limit = page.value.limit;
getTableData();
};
const newCreate = () => {
let exec = () => {
if (tenantData.isCertification != 'Y' && !currTenantDetailInfo.value.trustedIdentityCredential) { //认证过的
if (userData.superTubeFlag != 'Y') {//平台用户
proxy.$ElMessage.error('请先完成企业认证后再进行创建');
return;
}
}
router.push({
name: 'smartContractCreate'
});
}
if (psLogon.value) {
psLogon.value.then(() => {
exec();
})
} else {
exec();
}
}
onActivated(() => {
if (dataSmartContractStore.isRefresh) {//如果是首次加载,则不需要调用
page.value.curr = 1;
getTableData();
dataSmartContractStore.set(false);
}
})
/** 当前会员是否认证信息 */
const currTenantDetailInfo: any = ref({});
const psLogon = ref();
onBeforeMount(() => {
!dataSmartContractStore.isRefresh && toSearch({})
if (tenantData.isCertification != 'Y') {
psLogon.value = getEnterpriseData({
logonUser: userData.tenantName == "非认证会员" ? userData.logonUser : tenantData.logonUser
}).then((res: any) => {
psLogon.value = null;
if (res?.code == proxy.$passCode) {
currTenantDetailInfo.value = res.data || {};
} else {
res?.msg && proxy.$ElMessage.error(res?.msg);
}
}).catch(() => {
psLogon.value = null;
});
}
})
</script>
<template>
<div class="container_wrap">
<div class="table_tool_wrap">
<TableTools :searchItems="searchItemList" :searchId="'contract-search'" @search="toSearch" :init="false" />
<div class="tools_btns">
<el-button type="primary" @click="newCreate">新增</el-button>
</div>
</div>
<div class="table_panel_wrap">
<Table :tableInfo="tableInfo" @tablePageChange="tablePageChange" />
</div>
</div>
</template>
<style lang="scss" scoped>
.container_wrap {
padding: 0px 16px;
}
:deep(.el-tag.el-tag--primary) {
color: #0E5FD8;
background: #F2F9FF;
border: 1px solid rgba(224, 239, 255, 1);
}
</style>
\ No newline at end of file
<route lang="yaml">
name: strategyManagement
</route>
<script lang="ts" setup name="strategyManagement">
import { ref } from 'vue';
import TableTools from "@/components/Tools/table_tools.vue";
import {
getPageList,
updateTemplateState,
savePolicyTemplate,
updatePolicyTemplate,
deletePolicyTemplate
} from "@/api/modules/dataSmartContract";
import {
getParamsList
} from "@/api/modules/queryService";
import { useValidator } from '@/hooks/useValidator';
const { required, regexpValidate } = useValidator();
const router = useRouter();
const route = useRoute();
const { proxy } = getCurrentInstance() as any;
const policyTypeList: any = ref([]);
/** 约束运算符字典下拉 */
const operatorOptionList: any = ref([]);
const searchItemList = ref([
{
type: "input",
label: "",
field: "policyName",
default: "",
placeholder: "策略名称",
maxlength: 50,
clearable: true,
},
{
type: 'tree-select',
label: '',
field: 'policyTypeCode',
default: '',
placeholder: '维度类型',
options: policyTypeList.value,
filterable: true,
clearable: true,
checkStrictly: true,//只能选择叶子节点。
lazy: false,
multiple: false,
collapseTagsTooltip: true,
collapseTags: true,
props: {
label: "label",
value: "value",
children: 'childDictList'
},
showCheckbox: false
}
]);
const tableFields = ref([
{ label: "序号", type: "index", width: 56, align: "center" },
{ label: "策略id", field: "policyId", width: 205, },
{ label: "策略名称", field: "policyName", width: 130 },
{ label: "策略英文名", field: "policyEnName", width: 140 },
{ label: "维度类型", field: "policyTypeCodeName", width: 100 },
{ label: '启用状态', width: 96, field: 'bizStatus', type: 'switch', activeText: '启用', inactiveText: '停用', activeValue: 'Y', inactiveValue: 'N', align: 'center' },
{ label: "描述", field: "description", width: 200 },
{ label: "修改人", field: "updateUserName", width: 130 },
{ label: "修改时间", field: "updateTime", width: 170 },
]);
const page = ref({
limit: 50,
curr: 1,
sizes: [
{ label: "10", value: 10 },
{ label: "50", value: 50 },
{ label: "100", value: 100 },
{ label: "150", value: 150 },
{ label: "200", value: 200 },
],
policyName: '',
policyTypeCode: ''
});
const currTableData: any = ref({});
const tableInfo = ref({
id: 'value-asset-table',
rowKey: 'guid',
loading: false,
fields: tableFields.value,
data: [],
page: {
type: "normal",
rows: 0,
...page.value,
},
actionInfo: {
label: "操作",
type: "btn",
width: 100,
btns: (scope) => {
let row = scope.row;
if (row.bizStatus == 'Y') {
return [{
label: "查看", value: "view", click: (scope) => {
drawerInfo.value.visible = true;
drawerInfo.value.type = 'view';
drawerInfo.value.header.title = '查看策略';
drawerInfo.value.footer.visible = false;
classEditFormItems.value.forEach(item => {
item.default = scope.row[item.field] || '';
item.disabled = true;
});
let val = scope.row.policyTypeCode;
let selectItem = val && policyTypeList.value.find(p => p.childDictList?.some(c => c.value == val));
classEditFormItems.value.at(-2).visible = selectItem?.value == 'YS';
}
}]
} else {
return [{
label: "编辑", value: "edit", click: (scope) => {
currTableData.value = scope.row;
drawerInfo.value.visible = true;
drawerInfo.value.type = 'edit';
drawerInfo.value.header.title = '编辑策略';
drawerInfo.value.footer.visible = true;
classEditFormItems.value.forEach(item => {
item.default = scope.row[item.field] || '';
item.disabled = false;
});
let val = scope.row.policyTypeCode;
let selectItem = val && policyTypeList.value.find(p => p.childDictList?.some(c => c.value == val));
classEditFormItems.value.at(-2).visible = selectItem?.value == 'YS';
}
}, {
label: "删除", value: "delete", click: (scope) => {
proxy.$openMessageBox("此操作将永久删除, 是否继续?", () => {
deletePolicyTemplate([scope.row.guid]).then((res: any) => {
if (res?.code == proxy.$passCode) {
page.value.curr = 1;
getTableData();
proxy.$ElMessage.success('删除成功');
} else {
res?.msg && proxy.$ElMessage.error(res?.msg);
}
});
}, () => {
proxy.$ElMessage.info("已取消");
})
}
}]
}
}
}
});
const tableSwitchBeforeChange = (scope, field, callback) => {
const msg = `确定${scope.row[field] == 'Y' ? '停用' : '启用'}${scope.row.policyName}】策略?`
proxy.$openMessageBox(msg, () => {
const state = scope.row[field] == 'Y' ? 'N' : 'Y'
const result = tableSwitchChange(state, scope, field)
callback(result)
}, () => {
callback(false)
});
}
const tableSwitchChange = (val, scope, field) => {
return new Promise((resolve, reject) => {
let params = {
guid: scope.row.guid,
bizStatus: val
}
updateTemplateState(params).then((res: any) => {
if (res?.code == proxy.$passCode && res?.data) {
getTableData();
proxy.$ElMessage({
type: "success",
message: `【${scope.row.policyName}】策略${val == 'Y' ? '启用' : '停用'}成功`,
});
resolve(true)
} else {
proxy.$ElMessage({
type: "error",
message: res.msg,
});
reject(false)
}
}).catch(() => {
reject(false)
})
})
}
const toSearch = (val: any, clear: boolean = false) => {
if (clear) {
searchItemList.value.map((item) => (item.default = ""));
page.value.policyName = '';
page.value.policyTypeCode = "";
} else {
page.value.policyName = val.policyName;
page.value.policyTypeCode = val.policyTypeCode;
}
getTableData();
};
const getTableData = () => {
tableInfo.value.loading = true
getPageList({
pageIndex: page.value.curr,
pageSize: page.value.limit,
policyName: page.value.policyName,
policyTypeCode: page.value.policyTypeCode
}).then((res: any) => {
tableInfo.value.loading = false
if (res?.code == proxy.$passCode) {
const data = res.data || {}
tableInfo.value.data = data.records || []
tableInfo.value.page.limit = data.pageSize
tableInfo.value.page.curr = data.pageIndex
tableInfo.value.page.rows = data.totalRows
} else {
proxy.$ElMessage({
type: 'error',
message: res.msg,
})
}
}).catch(() => {
tableInfo.value.loading = false
})
};
const tablePageChange = (info) => {
page.value.curr = Number(info.curr);
page.value.limit = Number(info.limit);
tableInfo.value.page.curr = page.value.curr;
tableInfo.value.page.limit = page.value.limit;
getTableData();
};
const defaultValueInfo = ref({ bizStatus: 'Y' });
const newCreate = () => {
drawerInfo.value.visible = true;
drawerInfo.value.type = 'add';
drawerInfo.value.header.title = '新增策略';
drawerInfo.value.footer.visible = true;
classEditFormItems.value.forEach(item => {
item.default = defaultValueInfo.value[item.field] || '';
item.disabled = false;
});
classEditFormItems.value.at(-2).visible = false;
}
onActivated(() => {
})
onBeforeMount(() => {
toSearch({});
getParamsList({ dictType: '维度类型' }).then((res: any) => {
if (res?.code == proxy.$passCode) {
const data = res.data || [];
policyTypeList.value = data;
searchItemList.value[1].options = policyTypeList.value;
classEditFormItems.value[0].options = policyTypeList.value;
} else {
res?.msg && proxy.$ElMessage.error(res?.msg);
}
})
getParamsList({ dictType: '约束运算符' }).then((res: any) => {
if (res?.code == proxy.$passCode) {
const data = res.data || [];
operatorOptionList.value = data;
if (classEditFormItems.value.at(-2)) {
classEditFormItems.value.at(-2).options = operatorOptionList.value;
}
} else {
res?.msg && proxy.$ElMessage.error(res?.msg);
}
})
})
const classEditFormItems: any = ref([{
label: '维度类型',
type: 'tree-select',
placeholder: '请选择',
field: 'policyTypeCode',
default: '',
options: policyTypeList.value,
filterable: true,
clearable: true,
required: true,
showAllLevels: false,
checkStrictly: false,//只能选择叶子节点。
lazy: false,
props: {
value: 'value',
label: 'label',
children: 'childDictList'
},
disabled: false,
}, {
label: '策略名称',
type: 'input',
field: 'policyName',
default: '',
maxlength: 50,
placeholder: '请输入',
required: true,
clearable: true,
}, {
label: '策略英文名',
type: 'input',
field: 'policyEnName',
default: '',
maxlength: 50,
regexp: /[^A-Za-z0-9_]/g,
placeholder: '请输入',
required: true,
clearable: true,
}, {
label: '启用状态',
type: 'switch',
field: 'bizStatus',
default: 'Y',
placeholder: '请选择',
activeValue: 'Y',
inactiveValue: 'N',
switchWidth: 32,
}, {
label: '运算符',
type: 'select',
multiple: true,
collapse: true,
tagsTooltip: true,
placeholder: '默认全部',
field: 'constraintOperatorCodes',
default: '',
options: operatorOptionList.value,
filterable: true,
clearable: true,
required: false,
visible: false,
disabled: false,
}, {
label: '策略描述',
type: 'textarea',
placeholder: '请输入',
field: 'description',
default: '',
block: true,
maxlength: 500,
clearable: true,
required: true,
}]);
const classEditFormRules = ref({
policyTypeCode: [required('请选择维度类型')],
policyName: [required('请填写策略名称')],
policyEnName: [required('请填写策略英文名')],
description: [required('请填写策略描述')]
});
/** 新增分类的form */
const classEditFormInfo = ref({
type: "form",
title: "",
col: "span",
formInfo: {
id: "add-class-form",
readonly: false,
items: classEditFormItems.value,
rules: classEditFormRules.value,
},
});
/** 新增编辑分类。 */
const drawerInfo = ref({
visible: false,
direction: 'rtl',
size: 600,
header: {
title: '新增策略',
},
type: '',
container: {
contents: [classEditFormInfo.value],
},
footer: {
visible: true,
btns: [
{ type: 'default', label: '取消', value: 'cancel' },
{ type: 'primary', label: '确定', value: 'save', loading: false },
]
}
})
const handlerDrawerSelectChange = (val, row, info) => {
if (row.field == 'policyTypeCode') {
let selectItem = val && policyTypeList.value.find(p => p.childDictList?.some(c => c.value == val));
classEditFormItems.value.at(-2).visible = selectItem?.value == 'YS';
classEditFormItems.value.forEach(item => {
item.default = info[item.field];
});
}
}
const drawerBtnClick = async (btn, info) => {
if (btn.value == 'cancel') {
drawerInfo.value.visible = false;
} else {
let constraintOperatorCodes = info.constraintOperatorCodes || [];
if (constraintOperatorCodes?.length) {
info.constraintOperatorCodes = constraintOperatorCodes.map(c => {
return operatorOptionList.value.find(o => o.value == c)
}).sort((a, b) => {
return a.orderNum - b.orderNum;
}).map(v => {
return v.value;
})
}
drawerInfo.value.footer.btns[1].loading = true;
if (drawerInfo.value.type == 'edit') {
info.guid = currTableData.value.guid;
info.policyId = currTableData.value.policyId;
updatePolicyTemplate(info).then((res: any) => {
drawerInfo.value.footer.btns[1].loading = false;
if (res?.code == proxy.$passCode) {
drawerInfo.value.visible = false;
getTableData();
proxy.$ElMessage.success('编辑策略成功');
} else {
res?.msg && proxy.$ElMessage.error(res?.msg);
}
});
} else {
savePolicyTemplate(info).then((res: any) => {
drawerInfo.value.footer.btns[1].loading = false;
if (res?.code == proxy.$passCode) {
drawerInfo.value.visible = false;
page.value.curr = 1;
getTableData();
proxy.$ElMessage.success('新增策略成功');
} else {
res?.msg && proxy.$ElMessage.error(res?.msg);
}
});
}
}
}
</script>
<template>
<div class="container_wrap">
<div class="table_tool_wrap">
<TableTools :searchItems="searchItemList" :searchId="'settle-asset-search'" @search="toSearch" :init="false" />
<div class="tools_btns">
<el-button type="primary" @click="newCreate">新增</el-button>
</div>
</div>
<div class="table_panel_wrap">
<Table :tableInfo="tableInfo" @tablePageChange="tablePageChange"
@tableSwitchBeforeChange="tableSwitchBeforeChange" />
</div>
<Drawer :drawerInfo="drawerInfo" @drawerSelectChange="handlerDrawerSelectChange" @drawerBtnClick="drawerBtnClick"
ref="drawerRef"></Drawer>
</div>
</template>
<style lang="scss" scoped>
.container_wrap {
padding: 0px 16px;
}
</style>
\ No newline at end of file
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!