73bfd6a8 by lihua

数字合约功能迁移

1 parent 9324c603
...@@ -100,6 +100,9 @@ VITE_APP_DATA_DELIVERY = https://daop-jgjf-test.zgsjzc.com/ ...@@ -100,6 +100,9 @@ VITE_APP_DATA_DELIVERY = https://daop-jgjf-test.zgsjzc.com/
100 #数据服务接口地址 100 #数据服务接口地址
101 VITE_APP_SERVICE_BASEURL = ms-daop-trust-api-service 101 VITE_APP_SERVICE_BASEURL = ms-daop-trust-api-service
102 102
103 #数字合约接口
104 VITE_APP_DIGITAL_CONTRACT_URL = ms-daop-trust-data-space-service
105
103 # 本地访问地址 106 # 本地访问地址
104 # VITE_API_CIRCULATION_URL = http://localhost:9000/circulation 107 # VITE_API_CIRCULATION_URL = http://localhost:9000/circulation
105 108
......
...@@ -86,6 +86,9 @@ VITE_APP_PERSONAL_URL = ms-daop-personel-service ...@@ -86,6 +86,9 @@ VITE_APP_PERSONAL_URL = ms-daop-personel-service
86 #数据服务接口地址 86 #数据服务接口地址
87 VITE_APP_SERVICE_BASEURL = ms-daop-trust-api-service 87 VITE_APP_SERVICE_BASEURL = ms-daop-trust-api-service
88 88
89 #数字合约接口
90 VITE_APP_DIGITAL_CONTRACT_URL = ms-daop-trust-data-space-service
91
89 #流通平台接口地址 92 #流通平台接口地址
90 VITE_APP_CIRCULATION = https://sz-lt.zgsjzc.com/ 93 VITE_APP_CIRCULATION = https://sz-lt.zgsjzc.com/
91 #数据加工交付 94 #数据加工交付
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
35 "file-saver": "^2.0.5", 35 "file-saver": "^2.0.5",
36 "hotkeys-js": "^3.10.2", 36 "hotkeys-js": "^3.10.2",
37 "html2canvas": "^1.4.1", 37 "html2canvas": "^1.4.1",
38 "html2pdf.js": "^0.12.1",
38 "insert-css": "^2.0.0", 39 "insert-css": "^2.0.0",
39 "jquery": "^3.7.1", 40 "jquery": "^3.7.1",
40 "jsencrypt": "^3.3.2", 41 "jsencrypt": "^3.3.2",
......
...@@ -65,6 +65,9 @@ dependencies: ...@@ -65,6 +65,9 @@ dependencies:
65 html2canvas: 65 html2canvas:
66 specifier: ^1.4.1 66 specifier: ^1.4.1
67 version: 1.4.1 67 version: 1.4.1
68 html2pdf.js:
69 specifier: ^0.12.1
70 version: 0.12.1
68 insert-css: 71 insert-css:
69 specifier: ^2.0.0 72 specifier: ^2.0.0
70 version: 2.0.0 73 version: 2.0.0
...@@ -966,6 +969,11 @@ packages: ...@@ -966,6 +969,11 @@ packages:
966 regenerator-runtime: 0.14.1 969 regenerator-runtime: 0.14.1
967 dev: false 970 dev: false
968 971
972 /@babel/runtime@7.28.4:
973 resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==}
974 engines: {node: '>=6.9.0'}
975 dev: false
976
969 /@babel/template@7.25.9: 977 /@babel/template@7.25.9:
970 resolution: {integrity: sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==} 978 resolution: {integrity: sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==}
971 engines: {node: '>=6.9.0'} 979 engines: {node: '>=6.9.0'}
...@@ -1710,6 +1718,10 @@ packages: ...@@ -1710,6 +1718,10 @@ packages:
1710 resolution: {integrity: sha512-k7kRA033QNtC+gLc4VPlfnue58CM1iQLgn1IMAU8VPHGOj7oIHPp9UlhedEnD/Gl8evoCjwkZjlBORtZ3JByUA==} 1718 resolution: {integrity: sha512-k7kRA033QNtC+gLc4VPlfnue58CM1iQLgn1IMAU8VPHGOj7oIHPp9UlhedEnD/Gl8evoCjwkZjlBORtZ3JByUA==}
1711 dev: true 1719 dev: true
1712 1720
1721 /@types/pako@2.0.4:
1722 resolution: {integrity: sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw==}
1723 dev: false
1724
1713 /@types/path-browserify@1.0.3: 1725 /@types/path-browserify@1.0.3:
1714 resolution: {integrity: sha512-ZmHivEbNCBtAfcrFeBCiTjdIc2dey0l7oCGNGpSuRTy8jP6UVND7oUowlvDujBy8r2Hoa8bfFUOCiPWfmtkfxw==} 1726 resolution: {integrity: sha512-ZmHivEbNCBtAfcrFeBCiTjdIc2dey0l7oCGNGpSuRTy8jP6UVND7oUowlvDujBy8r2Hoa8bfFUOCiPWfmtkfxw==}
1715 dev: true 1727 dev: true
...@@ -1718,6 +1730,12 @@ packages: ...@@ -1718,6 +1730,12 @@ packages:
1718 resolution: {integrity: sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ==} 1730 resolution: {integrity: sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ==}
1719 dev: true 1731 dev: true
1720 1732
1733 /@types/raf@3.4.3:
1734 resolution: {integrity: sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==}
1735 requiresBuild: true
1736 dev: false
1737 optional: true
1738
1721 /@types/semver@7.5.8: 1739 /@types/semver@7.5.8:
1722 resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} 1740 resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==}
1723 dev: true 1741 dev: true
...@@ -1734,6 +1752,12 @@ packages: ...@@ -1734,6 +1752,12 @@ packages:
1734 '@types/node': 22.9.3 1752 '@types/node': 22.9.3
1735 dev: true 1753 dev: true
1736 1754
1755 /@types/trusted-types@2.0.7:
1756 resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
1757 requiresBuild: true
1758 dev: false
1759 optional: true
1760
1737 /@types/unist@2.0.11: 1761 /@types/unist@2.0.11:
1738 resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} 1762 resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==}
1739 dev: true 1763 dev: true
...@@ -2988,6 +3012,22 @@ packages: ...@@ -2988,6 +3012,22 @@ packages:
2988 resolution: {integrity: sha512-G1LRwLIQjBQoyq0ZJGqGIJUXzJ8irpbjHLpVRXDvBEScFJ9b17sgK6vlx0GAJFE21okD7zXl08rRRUfq6HdoEQ==} 3012 resolution: {integrity: sha512-G1LRwLIQjBQoyq0ZJGqGIJUXzJ8irpbjHLpVRXDvBEScFJ9b17sgK6vlx0GAJFE21okD7zXl08rRRUfq6HdoEQ==}
2989 dev: true 3013 dev: true
2990 3014
3015 /canvg@3.0.11:
3016 resolution: {integrity: sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==}
3017 engines: {node: '>=10.0.0'}
3018 requiresBuild: true
3019 dependencies:
3020 '@babel/runtime': 7.28.4
3021 '@types/raf': 3.4.3
3022 core-js: 3.39.0
3023 raf: 3.4.1
3024 regenerator-runtime: 0.13.11
3025 rgbcolor: 1.0.1
3026 stackblur-canvas: 2.7.0
3027 svg-pathdata: 6.0.3
3028 dev: false
3029 optional: true
3030
2991 /capital-case@1.0.4: 3031 /capital-case@1.0.4:
2992 resolution: {integrity: sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==} 3032 resolution: {integrity: sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==}
2993 dependencies: 3033 dependencies:
...@@ -3959,6 +3999,14 @@ packages: ...@@ -3959,6 +3999,14 @@ packages:
3959 resolution: {integrity: sha512-m4yreHcUWHBncGVV7U+yQzc12vIlq0jMrtHZ5mW6dQMiL/7skSYNVX9wqKwOtyO9SGCgevrAFEgOCAHmamHTUA==} 3999 resolution: {integrity: sha512-m4yreHcUWHBncGVV7U+yQzc12vIlq0jMrtHZ5mW6dQMiL/7skSYNVX9wqKwOtyO9SGCgevrAFEgOCAHmamHTUA==}
3960 dev: false 4000 dev: false
3961 4001
4002 /dompurify@3.3.0:
4003 resolution: {integrity: sha512-r+f6MYR1gGN1eJv0TVQbhA7if/U7P87cdPl3HN5rikqaBSBxLiCb/b9O+2eG0cxz0ghyU+mU1QkbsOwERMYlWQ==}
4004 requiresBuild: true
4005 optionalDependencies:
4006 '@types/trusted-types': 2.0.7
4007 dev: false
4008 optional: true
4009
3962 /domutils@1.7.0: 4010 /domutils@1.7.0:
3963 resolution: {integrity: sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==} 4011 resolution: {integrity: sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==}
3964 dependencies: 4012 dependencies:
...@@ -5108,6 +5156,14 @@ packages: ...@@ -5108,6 +5156,14 @@ packages:
5108 resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} 5156 resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
5109 dev: true 5157 dev: true
5110 5158
5159 /fast-png@6.4.0:
5160 resolution: {integrity: sha512-kAqZq1TlgBjZcLr5mcN6NP5Rv4V2f22z00c3g8vRrwkcqjerx7BEhPbOnWCPqaHUl2XWQBJQvOT/FQhdMT7X/Q==}
5161 dependencies:
5162 '@types/pako': 2.0.4
5163 iobuffer: 5.4.0
5164 pako: 2.1.0
5165 dev: false
5166
5111 /fast-uri@3.0.3: 5167 /fast-uri@3.0.3:
5112 resolution: {integrity: sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==} 5168 resolution: {integrity: sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==}
5113 dev: true 5169 dev: true
...@@ -5131,6 +5187,10 @@ packages: ...@@ -5131,6 +5187,10 @@ packages:
5131 resolution: {integrity: sha512-Rr5QlUeGN1mbOHlaqcSYMKVpPbgLy0AWT/W0EHxA6NGI12yO1jpoui2zBBvU2G824ltM6Ut8BFgfHSBGfkmS0A==} 5187 resolution: {integrity: sha512-Rr5QlUeGN1mbOHlaqcSYMKVpPbgLy0AWT/W0EHxA6NGI12yO1jpoui2zBBvU2G824ltM6Ut8BFgfHSBGfkmS0A==}
5132 dev: false 5188 dev: false
5133 5189
5190 /fflate@0.8.2:
5191 resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==}
5192 dev: false
5193
5134 /figures@3.2.0: 5194 /figures@3.2.0:
5135 resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} 5195 resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==}
5136 engines: {node: '>=8'} 5196 engines: {node: '>=8'}
...@@ -5780,6 +5840,13 @@ packages: ...@@ -5780,6 +5840,13 @@ packages:
5780 text-segmentation: 1.0.3 5840 text-segmentation: 1.0.3
5781 dev: false 5841 dev: false
5782 5842
5843 /html2pdf.js@0.12.1:
5844 resolution: {integrity: sha512-3rBWQ96H5oOU9jtoz3MnE/epGi27ig9h8aonBk4JTpvUERM3lMRxhIRckhJZEi4wE0YfRINoYOIDY0hLY0CHgQ==}
5845 dependencies:
5846 html2canvas: 1.4.1
5847 jspdf: 3.0.4
5848 dev: false
5849
5783 /htmlparser2@3.10.1: 5850 /htmlparser2@3.10.1:
5784 resolution: {integrity: sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==} 5851 resolution: {integrity: sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==}
5785 dependencies: 5852 dependencies:
...@@ -5981,6 +6048,10 @@ packages: ...@@ -5981,6 +6048,10 @@ packages:
5981 engines: {node: '>= 0.10'} 6048 engines: {node: '>= 0.10'}
5982 dev: true 6049 dev: true
5983 6050
6051 /iobuffer@5.4.0:
6052 resolution: {integrity: sha512-DRebOWuqDvxunfkNJAlc3IzWIPD5xVxwUNbHr7xKB8E6aLJxIPfNX3CoMJghcFjpv6RWQsrcJbghtEwSPoJqMA==}
6053 dev: false
6054
5984 /iota-array@1.0.0: 6055 /iota-array@1.0.0:
5985 resolution: {integrity: sha512-pZ2xT+LOHckCatGQ3DcG/a+QuEqvoxqkiL7tvE8nn3uuu+f6i1TtpB5/FtWFbxUuVr5PZCx8KskuGatbJDXOWA==} 6056 resolution: {integrity: sha512-pZ2xT+LOHckCatGQ3DcG/a+QuEqvoxqkiL7tvE8nn3uuu+f6i1TtpB5/FtWFbxUuVr5PZCx8KskuGatbJDXOWA==}
5986 dev: true 6057 dev: true
...@@ -6512,6 +6583,19 @@ packages: ...@@ -6512,6 +6583,19 @@ packages:
6512 graceful-fs: 4.2.11 6583 graceful-fs: 4.2.11
6513 dev: true 6584 dev: true
6514 6585
6586 /jspdf@3.0.4:
6587 resolution: {integrity: sha512-dc6oQ8y37rRcHn316s4ngz/nOjayLF/FFxBF4V9zamQKRqXxyiH1zagkCdktdWhtoQId5K20xt1lB90XzkB+hQ==}
6588 dependencies:
6589 '@babel/runtime': 7.28.4
6590 fast-png: 6.4.0
6591 fflate: 0.8.2
6592 optionalDependencies:
6593 canvg: 3.0.11
6594 core-js: 3.39.0
6595 dompurify: 3.3.0
6596 html2canvas: 1.4.1
6597 dev: false
6598
6515 /jsprim@1.4.2: 6599 /jsprim@1.4.2:
6516 resolution: {integrity: sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==} 6600 resolution: {integrity: sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==}
6517 engines: {node: '>=0.6.0'} 6601 engines: {node: '>=0.6.0'}
...@@ -7595,6 +7679,10 @@ packages: ...@@ -7595,6 +7679,10 @@ packages:
7595 engines: {node: '>=6'} 7679 engines: {node: '>=6'}
7596 dev: true 7680 dev: true
7597 7681
7682 /pako@2.1.0:
7683 resolution: {integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==}
7684 dev: false
7685
7598 /param-case@3.0.4: 7686 /param-case@3.0.4:
7599 resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} 7687 resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==}
7600 dependencies: 7688 dependencies:
...@@ -7760,7 +7848,6 @@ packages: ...@@ -7760,7 +7848,6 @@ packages:
7760 7848
7761 /performance-now@2.1.0: 7849 /performance-now@2.1.0:
7762 resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} 7850 resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==}
7763 dev: true
7764 7851
7765 /picocolors@1.1.1: 7852 /picocolors@1.1.1:
7766 resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} 7853 resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
...@@ -8087,6 +8174,14 @@ packages: ...@@ -8087,6 +8174,14 @@ packages:
8087 engines: {node: '>=10'} 8174 engines: {node: '>=10'}
8088 dev: true 8175 dev: true
8089 8176
8177 /raf@3.4.1:
8178 resolution: {integrity: sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==}
8179 requiresBuild: true
8180 dependencies:
8181 performance-now: 2.1.0
8182 dev: false
8183 optional: true
8184
8090 /read-pkg-up@7.0.1: 8185 /read-pkg-up@7.0.1:
8091 resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} 8186 resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==}
8092 engines: {node: '>=8'} 8187 engines: {node: '>=8'}
...@@ -8224,6 +8319,12 @@ packages: ...@@ -8224,6 +8319,12 @@ packages:
8224 which-builtin-type: 1.2.0 8319 which-builtin-type: 1.2.0
8225 dev: true 8320 dev: true
8226 8321
8322 /regenerator-runtime@0.13.11:
8323 resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==}
8324 requiresBuild: true
8325 dev: false
8326 optional: true
8327
8227 /regenerator-runtime@0.14.1: 8328 /regenerator-runtime@0.14.1:
8228 resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} 8329 resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
8229 dev: false 8330 dev: false
...@@ -8388,6 +8489,13 @@ packages: ...@@ -8388,6 +8489,13 @@ packages:
8388 resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} 8489 resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
8389 dev: true 8490 dev: true
8390 8491
8492 /rgbcolor@1.0.1:
8493 resolution: {integrity: sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==}
8494 engines: {node: '>= 0.8.15'}
8495 requiresBuild: true
8496 dev: false
8497 optional: true
8498
8391 /rimraf@3.0.2: 8499 /rimraf@3.0.2:
8392 resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} 8500 resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
8393 deprecated: Rimraf versions prior to v4 are no longer supported 8501 deprecated: Rimraf versions prior to v4 are no longer supported
...@@ -8844,6 +8952,13 @@ packages: ...@@ -8844,6 +8952,13 @@ packages:
8844 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' 8952 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'
8845 dev: true 8953 dev: true
8846 8954
8955 /stackblur-canvas@2.7.0:
8956 resolution: {integrity: sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==}
8957 engines: {node: '>=0.1.14'}
8958 requiresBuild: true
8959 dev: false
8960 optional: true
8961
8847 /static-extend@0.1.2: 8962 /static-extend@0.1.2:
8848 resolution: {integrity: sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==} 8963 resolution: {integrity: sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==}
8849 engines: {node: '>=0.10.0'} 8964 engines: {node: '>=0.10.0'}
...@@ -9271,6 +9386,13 @@ packages: ...@@ -9271,6 +9386,13 @@ packages:
9271 - supports-color 9386 - supports-color
9272 dev: true 9387 dev: true
9273 9388
9389 /svg-pathdata@6.0.3:
9390 resolution: {integrity: sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==}
9391 engines: {node: '>=12.0.0'}
9392 requiresBuild: true
9393 dev: false
9394 optional: true
9395
9274 /svg-tags@1.0.0: 9396 /svg-tags@1.0.0:
9275 resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==} 9397 resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==}
9276 dev: true 9398 dev: true
......
1 import request from "@/utils/request";
2
3 /** 企业注册认证和连接器 */
4
5 /** 企业认证 **/
6 // 企业认证分页
7 export const getEnterpriseList = (params) => request({
8 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/enterprise/page-list`,
9 method: 'post',
10 data: params
11 })
12
13 // 企业认证详情
14 export const getEnterpriseDetail = (params) => request({
15 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/enterprise/detail`,
16 method: 'get',
17 params
18 })
19
20 // 企业认证新增
21 export const enterpriseSave = (params) => request({
22 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/enterprise/save`,
23 method: 'post',
24 data: params
25 })
26
27 // 企业认证修改
28 export const enterpriseUpdate = (params) => request({
29 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/enterprise/update`,
30 method: 'put',
31 data: params
32 })
33
34 // 企业认证删除
35 export const enterpriseDelete = (params) => request({
36 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/enterprise/delete`,
37 method: 'delete',
38 data: params
39 })
40
41 // 企业认证变更删除
42 export const enterpriseChangeDelete = (params) => request({
43 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/enterprise/change/delete`,
44 method: 'delete',
45 data: params
46 })
47
48 // 企业认证最后一级审批
49 export const enterpriseApprove = (params) => request({
50 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/enterprise/last-approve`,
51 method: 'post',
52 data: params
53 })
54
55 // 企业认证进度列表
56 export const getTaskGressList = (params) => request({
57 url: `${import.meta.env.VITE_APP_PERSONAL_URL}/pending-task/page-list`,
58 method: 'post',
59 data: params
60 })
61
62 // 企业认证重新发起
63 export const getTaskRestart = (params) => request({
64 url: `${import.meta.env.VITE_APP_PERSONAL_URL}/pending-task/restart`,
65 method: 'get',
66 params
67 })
68
69 // 企业认证执行日志
70 export const getTaskExecutionLog = (params) => request({
71 url: `${import.meta.env.VITE_APP_PERSONAL_URL}/pending-task/task-info`,
72 method: 'get',
73 params
74 })
75
76 // 获取企业认证流程列表
77 export const getFlowEnterpriseList = (params) => request({
78 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/enterprise/flow/page-list`,
79 method: 'post',
80 data: params
81 })
82
83 // 获取企业信息
84 export const getUserTenant = () => request({
85 // url: `http://localhost:9000/master/ms-daop-personel-service/tenant/get-current-user-tenant-from-cache`,
86 url: `https://sz-lt.zgsjzc.com/master/api/ms-daop-personel-service/tenant/get-current-user-tenant-from-cache`,
87 method: 'get'
88 })
89
90 // 获取企业信息
91 export const getEnterpriseData = (params) => request({
92 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/enterprise/detail-by-logonUser`,
93 method: 'get',
94 params
95 })
96
97 // 修改需求上架状态
98 export const updateDemandGrounding = (params) => request({
99 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/demand/update-grounding-pick`,
100 method: 'post',
101 data: params
102 })
1 /** 数字合约管理 */
2
3 import request from "@/utils/request";
4
5 export const contractStatusList = [{
6 value: '01',
7 label: '发起'
8 }, {
9 value: '02',
10 label: '协商'
11 }, {
12 value: '03',
13 label: '签订'
14 }, {
15 value: '0302',
16 label: '签订失败'
17 }, {
18 value: '05',
19 label: '履行中'
20 }, {
21 value: '06',
22 label: '终止'
23 }, {
24 value: '00',
25 label: '已撤回'
26 }];
27
28 /** 获取策略模板的列表 */
29 export const getPageList = (params) => request({
30 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/policy-template/page-list`,
31 method: 'post',
32 data: params
33 })
34
35 /** 新增策略模板 */
36 export const savePolicyTemplate = (params) => request({
37 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/policy-template/save`,
38 method: 'post',
39 data: params
40 });
41
42 /** 修改策略模板 */
43 export const updatePolicyTemplate = (params) => request({
44 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/policy-template/update`,
45 method: 'put',
46 data: params
47 });
48
49 /** 删除策略模板 */
50 export const deletePolicyTemplate = (params) => request({
51 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/policy-template/delete`,
52 method: 'delete',
53 data: params
54 });
55
56 /** 更新策略模板状态 */
57 export const updateTemplateState = (params) => request({
58 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/policy-template/update-status`,
59 method: 'put',
60 params
61 });
62
63 /** --------------------------- 合约模板管理 -------------------------- */
64 /** 获取策略模板的列表 */
65 export const getContractTemplatePageList = (params) => request({
66 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract-template/page-list`,
67 method: 'post',
68 data: params
69 })
70
71 /** 新增合约模板状态 */
72 export const saveContractTemplate = (params) => request({
73 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract-template/save`,
74 method: 'post',
75 data: params
76 });
77
78 /** 更新合约模板状态 */
79 export const updateContractTemplate = (params) => request({
80 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract-template/update`,
81 method: 'put',
82 data: params
83 });
84
85 /** 更新合约模板状态 */
86 export const updateContractTemplateState = (params) => request({
87 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract-template/update-status`,
88 method: 'put',
89 params
90 });
91
92 /** 删除策略模板 */
93 export const deleteContractTemplate = (params) => request({
94 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract-template/delete`,
95 method: 'delete',
96 data: params
97 });
98
99 /** 获取策略模板详情 */
100 export const getContractTemplateDetail = (params) => request({
101 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract-template/detail`,
102 method: 'get',
103 params
104 });
105
106 /** 复制合约模板 */
107 export const copyContractTemplate = (params) => request({
108 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract-template/copy`,
109 method: 'get',
110 params
111 });
112
113 /** 获取操作行为下拉列表 */
114 export const getActionPolicyList = () => request({
115 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/policy-template/list-by-policy-type?policyType=CZ`,
116 method: 'get'
117 });
118
119 /** 获取约束条件下拉列表 */
120 export const getConstraintPolicyList = () => request({
121 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/policy-template/list-by-policy-type?policyType=YS`,
122 method: 'get'
123 });
124
125 /** 根据状态获取可用的数字合约模板 */
126 export const getValidContractTemplateList = () => request({
127 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract-template/list-by-biz-status`,
128 method: 'get'
129 });
130
131 /** --------------------- 合约管理 ----------------------- */
132 export const getContractPageList = (params) => request({
133 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract/page-list`,
134 method: 'post',
135 data: params
136 })
137
138 /** 合约备案列表 */
139 export const getContractOverviewPageList = (params) => request({
140 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract/overview-page-list`,
141 method: 'post',
142 data: params
143 })
144
145 /** 获取合约备案的发起主体下拉列表 */
146 export const getContractOverviewTenantList = () => request({
147 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract/tenant-list`,
148 method: 'get',
149 });
150
151 /** 获取下拉数据产品列表 */
152 export const getContractDataProduct = () => request({
153 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract/get-data-product`,
154 method: 'post'
155 })
156
157 /** 创建合约 */
158 export const saveContract = (params) => request({
159 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract/save`,
160 method: 'post',
161 data: params
162 })
163
164 /** 更新合约 */
165 export const updateContract = (params) => request({
166 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract/update`,
167 method: 'put',
168 data: params
169 })
170
171 export const deleteContract = (params) => request({
172 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract/delete`,
173 method: 'delete',
174 data: params
175 });
176
177 export const getContractDetail = (guid) => request({
178 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract/detail?guid=${guid}`,
179 method: 'get',
180 });
181
182 /** 查询协商信息详情 */
183 export const getContractNegotiate = (guid) => request({
184 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract/get-negotiate?guid=${guid}`,
185 method: 'post',
186 });
187
188 /** 根据版本获取协商信息 */
189 export const getContractNegoPlicyByVersion = (params) => request({
190 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract/get-policy-by-version?contractGuid=${params.guid}&version=${params.version}`,
191 method: 'get'
192 });
193
194 /** 拒绝本次合约 */
195 export const rejectContract = (guid) => request({
196 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract/reject?guid=${guid}`,
197 method: 'post',
198 });
199
200 /** 确认本次合约 */
201 export const confirmContract = (guid) => request({
202 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract/confirm?guid=${guid}`,
203 method: 'post',
204 });
205
206 /** 继续本地合约协商 */
207 export const continueContractNegotiate = (params) => request({
208 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract/continue-negotiate`,
209 method: 'post',
210 data: params
211 });
212
213 /** 获取可选的企业下拉列表,认证过之后带标识的 */
214 export const getContractTenantList = () => request({
215 url: `${import.meta.env.VITE_APP_PERSONAL_URL}/tenant/get-social-credit-code-tenant`,
216 method: 'post'
217 })
218
219 /** 撤回合约 */
220 export const cancelContract = (guid) => request({
221 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract/canal?guid=${guid}`,
222 method: 'post',
223 });
224
225 /** 签署合约 */
226 export const signContract = (params) => request({
227 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract-signature/save`,
228 method: 'post',
229 data: params
230 });
231
232 /** 获取签署合约详情 */
233 export const getSignListInfo = (guid) => request({
234 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract-signature/list-by-contract-guid?contractGuid=${guid}`,
235 method: 'post',
236 });
237
238 /** ----------------- 合约履约信息 ----------------- */
239 export const getContractProof = (guid) => request({
240 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/proof-execution/proof-by-contract-guid?contractGuid=${guid}`,
241 method: 'get'
242 });
243
244 /** 获取履约签名 */
245 export const getContractExecList = (params) => request({
246 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/proof-execution/execution-list-by-contract-guid`,
247 method: 'post',
248 data: params
249 });
250
251 /** 解除合同 */
252 export const terminateContract = (params) => request({
253 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract-terminate/save`,
254 method: 'post',
255 data: params
256 });
257
258 /** 获取终止合约信息 */
259 export const getTerminateDetailInfo = (guid) => request({
260 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract-terminate/detail?contractGuid=${guid}`,
261 method: 'get'
262 });
263
264 /** 日志管理-操作记录 */
265 export const getContractOperationLog = (params) => request({
266 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract-operation-log/page-list`,
267 method: 'post',
268 data: params
269 })
270
271 /** 操作记录详情 */
272 export const getContractOperationLogDetail = (guid) => request({
273 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract-operation-log/detail?guid=${guid}`,
274 method: 'get'
275 })
276
277 /** 日志管理-过程记录 */
278 export const getContractProcessLog = (params) => request({
279 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract-process-log/page-list`,
280 method: 'post',
281 data: params
282 })
283
284 export const getContractProcessLogDetail = (guid) => request({
285 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract-process-log/detail?guid=${guid}`,
286 method: 'get'
287 })
288
289 /** ------------ 合约统计记录 -------------- */
290
291 /** 获取备案统计 */
292 export const getContractStatis = () => request({
293 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract/get-contract-statistics`,
294 method: 'get'
295 })
296
297 export const getContractMonthStatis = () => request({
298 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract-signature/get-month-signature`,
299 method: 'get'
300 })
301
302 /** 获取合约履行异常预警记录 */
303 export const getLogTableList = (params) => request({
304 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract-process-log/page-abnormal-warning`,
305 method: 'post',
306 data: params
307 })
308
309 /** 生成数字签名文件 */
310 export const getSignatureFile = (params, data) => request({
311 url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/contract-signature/get-signature-base?pageIndex=${data.pageIndex}&yPosition=${data.yPosition}`,
312 method: 'post',
313 data: params,
314 headers: {
315 'Content-Type': 'multipart/form-data'
316 }
317 })
...@@ -364,3 +364,30 @@ a { ...@@ -364,3 +364,30 @@ a {
364 .mb10 { 364 .mb10 {
365 margin-bottom: 10px; 365 margin-bottom: 10px;
366 } 366 }
367
368 .el-tabs.log-tabs {
369 height: 100%;
370
371 .el-tabs__header {
372 margin-bottom: 0;
373 }
374
375 .el-tabs__item {
376 height: 32px;
377 padding: 0px;
378 width: 90px;
379
380 &:last-child {
381 width: 90px;
382 }
383 }
384
385 .el-tabs__content {
386 height: calc(100% - 32px);
387 }
388
389 .el-tab-pane {
390 padding: 0px 16px;
391 height: 100%;
392 }
393 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -24,8 +24,7 @@ const props = defineProps({ ...@@ -24,8 +24,7 @@ const props = defineProps({
24 24
25 const emits = defineEmits(["expand"]); 25 const emits = defineEmits(["expand"]);
26 26
27 // const isExpanded = ref(true); 27 const isExpanded = ref(true);
28 const isExpanded = ref(props.isExpand);
29 28
30 watch( 29 watch(
31 () => props.isExpand, 30 () => props.isExpand,
...@@ -47,10 +46,14 @@ const expandSwicthHandler = () => { ...@@ -47,10 +46,14 @@ const expandSwicthHandler = () => {
47 </script> 46 </script>
48 47
49 <template> 48 <template>
50 <ElCard class="v-content-wrap" shadow="never" :body-style="{ 49 <ElCard
50 class="v-content-wrap"
51 shadow="never"
52 :body-style="{
51 padding: `0px`, 53 padding: `0px`,
52 height: `${isExpanded ? contentHeight + 28 : 0}px`, 54 height: `${isExpanded ? contentHeight + 28 : 0}px`,
53 }"> 55 }"
56 >
54 <template v-if="title" #header> 57 <template v-if="title" #header>
55 <div class="card-title" @click="expandSwicthHandler"> 58 <div class="card-title" @click="expandSwicthHandler">
56 <span v-if="expandSwicth" style="padding-right: 5px; cursor: pointer"> 59 <span v-if="expandSwicth" style="padding-right: 5px; cursor: pointer">
...@@ -101,7 +104,6 @@ const expandSwicthHandler = () => { ...@@ -101,7 +104,6 @@ const expandSwicthHandler = () => {
101 color: var(--el-text-color-primary); 104 color: var(--el-text-color-primary);
102 line-height: 21px; 105 line-height: 21px;
103 font-weight: 600; 106 font-weight: 600;
104 flex-shrink: 0;
105 } 107 }
106 108
107 .desc { 109 .desc {
......
...@@ -9,6 +9,18 @@ export const useValidator = () => { ...@@ -9,6 +9,18 @@ export const useValidator = () => {
9 } 9 }
10 } 10 }
11 11
12 const requiredFiles = (message?: string) => {
13 return {
14 validator: (rule: any, value: any, callback: any) => {
15 if (!value?.length) {
16 callback(new Error('请上传文件'))
17 } else {
18 callback();
19 }
20 }, trigger: 'change'
21 }
22 }
23
12 // element scrollToError的优化 24 // element scrollToError的优化
13 const scrollToError = () => { 25 const scrollToError = () => {
14 nextTick(() => { 26 nextTick(() => {
...@@ -175,6 +187,111 @@ export const useValidator = () => { ...@@ -175,6 +187,111 @@ export const useValidator = () => {
175 trigger: "blur", 187 trigger: "blur",
176 }; 188 };
177 } 189 }
190 /** 验证输入的多个IP地址 */
191 const validateIPList = (): FormItemRule => {
192 return {
193 validator: (rule: any, value: any, callback: any) => {
194 let ips = value?.split(",");
195 if (!ips) {
196 callback(new Error(`请填写合法的IP地址`));
197 return;
198 }
199 for (const ipV of ips) {
200 let ip = ipV.trim();
201 if (!ip || (!ipRegex.v4().test(ip) && !ipRegex.v6().test(ip))) {
202 callback(new Error(`请填写合法的IP地址`));
203 return;
204 }
205 }
206 callback();
207 },
208 trigger: "blur",
209 };
210 };
211
212 /** 验证输入的多个域名 */
213 const validateDomainList = (): FormItemRule => {
214 return {
215 validator: (rule: any, value: any, callback: any) => {
216 let domains = value?.split(",");
217 if (!domains) {
218 callback(new Error(`请填写合法的IP地址`));
219 return;
220 }
221 // 简单的域名验证正则表达式
222 const domainRegex =
223 /^(?!-)(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])\.?)+[a-zA-Z]{2,}$/;
224
225 // 更严格的正则,符合RFC标准
226 const strictDomainRegex =
227 /^((?!-)[A-Za-z0-9-]{1,63}(?<!-)\.)+[A-Za-z]{2,}$/;
228
229 // 国际化域名支持(Punycode编码)
230 const idnRegex =
231 /^(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,})$/;
232 for (const dv of domains) {
233 let domain = dv.trim();
234 if (!domain) {
235 callback(new Error("请填写合法的域名,不能包含连续的逗号"));
236 return;
237 }
238
239 // 检查长度限制
240 if (domain.length > 253) {
241 callback(new Error("单个域名长度不能超过253个字符"));
242 return;
243 }
244
245 // 检查是否以点开头或结尾
246 if (domain.startsWith(".") || domain.endsWith(".")) {
247 callback(new Error("域名不能以点开头或结尾"));
248 return;
249 }
250
251 // 检查连续的点
252 if (domain.includes("..")) {
253 callback(new Error("域名不能包含连续的点"));
254 return;
255 }
256
257 // 验证每个标签(以点分隔的部分)
258 const labels = domain.split(".");
259 const tld = labels[labels.length - 1];
260
261 // 检查顶级域名(TLD)不能全是数字
262 if (/^\d+$/.test(tld)) {
263 callback(new Error("顶级域名不能全是数字"));
264 return;
265 }
266
267 // 检查每个标签的长度和内容
268 for (let i = 0; i < labels.length; i++) {
269 const label = labels[i];
270
271 // 标签长度检查
272 if (label.length < 1 || label.length > 63) {
273 callback(new Error(`标签"${label}"长度必须在1-63个字符之间`));
274 return;
275 }
276
277 // 标签内容检查
278 if (!/^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/.test(label)) {
279 callback(new Error(`标签"${label}"格式不正确`));
280 return;
281 }
282 }
283
284 // 使用正则表达式验证
285 if (!strictDomainRegex.test(domain) && !idnRegex.test(domain)) {
286 callback(new Error(`域名格式不符合规范`));
287 return;
288 }
289 }
290 callback();
291 },
292 trigger: "blur",
293 };
294 };
178 295
179 return { 296 return {
180 required, 297 required,
...@@ -184,6 +301,9 @@ export const useValidator = () => { ...@@ -184,6 +301,9 @@ export const useValidator = () => {
184 description, 301 description,
185 chOrEnPreffix, 302 chOrEnPreffix,
186 scrollToError, 303 scrollToError,
187 checkExistName 304 checkExistName,
305 requiredFiles,
306 validateIPList,
307 validateDomainList
188 } 308 }
189 } 309 }
......
1 import type { RouteRecordRaw } from 'vue-router'
2 function Layout() {
3 return import('@/layouts/index.vue')
4 }
5 const routes: RouteRecordRaw[] = [
6 {
7 path: '/data-smart-contract/strategy-management',
8 component: Layout,
9 meta: {
10 title: '策略管理',
11 icon: 'sidebar-videos',
12 },
13 children: [{
14 path: '',
15 name: 'strategyManagement',
16 component: () => import('@/views/data_smart_contract/strategyManagement.vue'),
17 meta: {
18 title: '',
19 sidebar: false,
20 breadcrumb: false,
21 cache: true,
22 editPage: true
23 },
24 }]
25 },
26 {
27 path: '/data-smart-contract/contract-template',
28 component: Layout,
29 meta: {
30 title: '合约模板',
31 icon: 'sidebar-videos',
32 },
33 children: [{
34 path: '',
35 name: 'contractTemplateManagement',
36 component: () => import('@/views/data_smart_contract/contractTemplateManagement.vue'),
37 meta: {
38 title: '',
39 sidebar: false,
40 breadcrumb: false,
41 cache: true
42 },
43 }, {
44 path: 'contract-template-create',
45 name: 'contractTemplateCreate',
46 component: () => import('@/views/data_smart_contract/contractTemplateCreate.vue'),
47 meta: {
48 title: '新建合约模板',
49 sidebar: false,
50 breadcrumb: false,
51 cache: true,
52 editPage: true,
53 reuse: true
54 },
55 beforeEnter: (to, from) => {
56 to.meta.title = !to.query.guid ? '新建合约模板' : `编辑-${to.query.name}`;
57 to.meta.editPage = true;
58 }
59 }]
60 },
61 {
62 path: '/data-smart-contract/contract-manage',
63 component: Layout,
64 meta: {
65 title: '合约管理',
66 icon: 'sidebar-videos',
67 },
68 children: [{
69 path: '',
70 name: 'smartContractManagement',
71 component: () => import('@/views/data_smart_contract/smartContractManagement.vue'),
72 meta: {
73 title: '',
74 sidebar: false,
75 breadcrumb: false,
76 cache: true,
77 editPage: true
78 },
79 }, {
80 path: 'samart-contract-create',
81 name: 'smartContractCreate',
82 component: () => import('@/views/data_smart_contract/smartContractCreate.vue'),
83 meta: {
84 title: '新建合约',
85 sidebar: false,
86 breadcrumb: false,
87 cache: true,
88 editPage: true,
89 reuse: true
90 },
91 beforeEnter: (to, from) => {
92 to.meta.title = !to.query.guid ? '新建合约' : `编辑合约-${to.query.name}`;
93 to.meta.editPage = true;
94 }
95 }, {
96 path: 'smart-contract-detail',
97 name: 'smartContractDetail',
98 component: () => import('@/views/data_smart_contract/smartContractDetail.vue'),
99 meta: {
100 title: '合约详情-',
101 sidebar: false,
102 breadcrumb: false,
103 cache: true,
104 editPage: true,
105 reuse: true
106 },
107 beforeEnter: (to, from) => {
108 if (to.query.type == 'consult') {
109 to.meta.title = `合约协商-${to.query.name}`;
110 } else if (to.query.type == 'sign') {
111 to.meta.title = `合约签署-${to.query.name}`;
112 } else if (to.query.type == 'reject') {
113 to.meta.title = `合约解除-${to.query.name}`;
114 } else {
115 to.meta.title = `合约详情-${to.query.name}`;
116 }
117 }
118 }]
119 },
120 {
121 path: '/data-smart-contract/contract-record-manage',
122 component: Layout,
123 meta: {
124 title: '合约备案',
125 icon: 'sidebar-videos',
126 },
127 children: [{
128 path: '',
129 name: 'contractRecordManage',
130 component: () => import('@/views/data_smart_contract/contractRecordManage.vue'),
131 meta: {
132 title: '',
133 sidebar: false,
134 breadcrumb: false,
135 cache: true,
136 editPage: true
137 },
138 }]
139 },
140 {
141 path: '/data-smart-contract/contract-log-manage',
142 component: Layout,
143 meta: {
144 title: '合约日志管理',
145 icon: 'sidebar-videos',
146 },
147 children: [{
148 path: '',
149 name: 'contractLogManage',
150 component: () => import('@/views/data_smart_contract/contractLogManage.vue'),
151 meta: {
152 title: '',
153 sidebar: false,
154 breadcrumb: false,
155 cache: true,
156 editPage: true
157 },
158 }]
159 },
160 {
161 path: '/data-smart-contract/exec-cnt-index',
162 component: Layout,
163 meta: {
164 title: '合约履行监测',
165 icon: 'sidebar-videos',
166 },
167 children: [{
168 path: '',
169 name: 'execCntIndex',
170 component: () => import('@/views/data_smart_contract/execCntIndex.vue'),
171 meta: {
172 title: '',
173 sidebar: false,
174 breadcrumb: false,
175 cache: true,
176 editPage: true
177 },
178 }]
179 },
180 ]
181
182 export default routes
...\ No newline at end of file ...\ No newline at end of file
...@@ -2,8 +2,8 @@ import { setupLayouts } from 'virtual:meta-layouts' ...@@ -2,8 +2,8 @@ import { setupLayouts } from 'virtual:meta-layouts'
2 import generatedRoutes from 'virtual:generated-pages' 2 import generatedRoutes from 'virtual:generated-pages'
3 import type { RouteRecordRaw } from 'vue-router' 3 import type { RouteRecordRaw } from 'vue-router'
4 import DataAssess from './modules/dataAsset'; 4 import DataAssess from './modules/dataAsset';
5 import DataAssetRegistry from './modules/dataAssetRegistry';
6 import DataService from './modules/dataService'; 5 import DataService from './modules/dataService';
6 import DataSmartContract from './modules/dataSmartContract';
7 7
8 import useSettingsStore from '@/store/modules/settings' 8 import useSettingsStore from '@/store/modules/settings'
9 9
...@@ -92,6 +92,7 @@ const systemRoutes: RouteRecordRaw[] = [ ...@@ -92,6 +92,7 @@ const systemRoutes: RouteRecordRaw[] = [
92 const asyncRoutes: RouteRecordRaw[] = [ 92 const asyncRoutes: RouteRecordRaw[] = [
93 ...DataAssess, 93 ...DataAssess,
94 ...DataService, 94 ...DataService,
95 ...DataSmartContract,
95 // ...DataAssetRegistry, 96 // ...DataAssetRegistry,
96 ] 97 ]
97 98
......
1 const useDataSmartContract = defineStore(
2 'isRefresh',
3 () => {
4 const isRefresh = ref<boolean>(false)
5
6 function set(v: boolean) {
7 isRefresh.value = v;
8 }
9
10 return {
11 isRefresh,
12 set,
13 }
14 },
15 )
16
17 export default useDataSmartContract
...\ No newline at end of file ...\ No newline at end of file
...@@ -5,7 +5,7 @@ import router from '@/router' ...@@ -5,7 +5,7 @@ import router from '@/router'
5 import { ElMessage } from 'element-plus' 5 import { ElMessage } from 'element-plus'
6 import apiUser from '@/api/modules/user' 6 import apiUser from '@/api/modules/user'
7 import { getCurrentTime } from '@/utils/common' 7 import { getCurrentTime } from '@/utils/common'
8 import { getSystemMenu, getUserInfo, getTokenByCode, loginOut, refreshToken, editPasswordInterface, getCurrentUserInfo } from '@/api/modules/queryService' 8 import { getSystemMenu, getUserInfo, getTokenByCode, loginOut, refreshToken, editPasswordInterface, getCurrentUserInfo, getTenantDetailInfo } from '@/api/modules/queryService'
9 9
10 const useUserStore = defineStore( 10 const useUserStore = defineStore(
11 // 唯一ID 11 // 唯一ID
...@@ -57,6 +57,13 @@ const useUserStore = defineStore( ...@@ -57,6 +57,13 @@ const useUserStore = defineStore(
57 currentTenantGuid.value = res.data.tenantInfoList && res.data.tenantInfoList.length ? res.data.tenantInfoList[0].guid : ''; 57 currentTenantGuid.value = res.data.tenantInfoList && res.data.tenantInfoList.length ? res.data.tenantInfoList[0].guid : '';
58 localStorage.setItem('currentTenantGuid', currentTenantGuid.value); 58 localStorage.setItem('currentTenantGuid', currentTenantGuid.value);
59 let currentTenant = res.data.tenantInfoList?.[0]; 59 let currentTenant = res.data.tenantInfoList?.[0];
60 getTenantDetailInfo(currentTenantGuid.value).then((res: any) => {
61 if (res.code == '00000') {
62 localStorage.setItem('tenantInfo', JSON.stringify(res.data));
63 } else {
64 ElMessage.error(res.msg)
65 }
66 })
60 return getCurrentUserInfo({tenantGuid: currentTenantGuid.value}).then((res: any) => { 67 return getCurrentUserInfo({tenantGuid: currentTenantGuid.value}).then((res: any) => {
61 console.log(res, 'getCurrentUserInfo'); 68 console.log(res, 'getCurrentUserInfo');
62 if (res.code == '00000') { 69 if (res.code == '00000') {
......
...@@ -565,7 +565,33 @@ export const tagType = (row, type): any => { ...@@ -565,7 +565,33 @@ export const tagType = (row, type): any => {
565 break 565 break
566 default:state = "info" 566 default:state = "info"
567 } 567 }
568 }else if(type=="releaseStatus") { 568 } else if (type == 'contractStatus') {
569 switch(row[type]) {
570 case '01':
571 state = 'primary'
572 break;
573 case '02':
574 state = 'primary'
575 break;
576 case '03':
577 state = 'primary'
578 break;
579 case '0301':
580 state = 'success'
581 break;
582 case '0302':
583 state = 'danger'
584 break;
585 case '05':
586 state = 'warning'
587 break;
588 case '06':
589 case '00':
590 state = 'info'
591 break;
592 default: state = 'info'
593 }
594 } else if(type=="releaseStatus") {
569 switch (row[type]) { 595 switch (row[type]) {
570 case 1: 596 case 1:
571 state = 'info'; //待发布 597 state = 'info'; //待发布
...@@ -889,7 +915,35 @@ export const tagMethod = (row, type) => { ...@@ -889,7 +915,35 @@ export const tagMethod = (row, type) => {
889 break; 915 break;
890 default: tag = "待发布" 916 default: tag = "待发布"
891 } 917 }
892 }else if (type == 'success') { 918 } else if (type == 'contractStatus') {
919 switch(row[type]) {
920 case '00':
921 tag = '已撤回'
922 break;
923 case '01':
924 tag = '发起'
925 break;
926 case '02':
927 tag = '协商'
928 break;
929 case '03':
930 tag = '签订'
931 break;
932 case '0301':
933 tag = '签订成功'
934 break;
935 case '0302':
936 tag = '签订失败'
937 break;
938 case '05':
939 tag = '履行中'
940 break;
941 case '06':
942 tag = '终止'
943 break;
944 default: tag = "发起"
945 }
946 } else if (type == 'success') {
893 switch (row[type]) { 947 switch (row[type]) {
894 case true: 948 case true:
895 tag = '成功' 949 tag = '成功'
......
...@@ -21,6 +21,7 @@ import { ...@@ -21,6 +21,7 @@ import {
21 } from "@/api/modules/dataService"; 21 } from "@/api/modules/dataService";
22 import useDataServiceStore from "@/store/modules/dataService"; 22 import useDataServiceStore from "@/store/modules/dataService";
23 import useUserStore from "@/store/modules/user"; 23 import useUserStore from "@/store/modules/user";
24 import { changeNum } from '@/utils/common';
24 25
25 const router = useRouter(); 26 const router = useRouter();
26 const dataServiceStore = useDataServiceStore(); 27 const dataServiceStore = useDataServiceStore();
...@@ -162,17 +163,21 @@ const tableInfo = ref({ ...@@ -162,17 +163,21 @@ const tableInfo = ref({
162 { 163 {
163 label: "平均响应时间(s)", field: "averageRespTime", width: 130, align: 'right', type: 'chnum' 164 label: "平均响应时间(s)", field: "averageRespTime", width: 130, align: 'right', type: 'chnum'
164 }, 165 },
166 {
167 label: "绑定连接器数", field: "bindingCount", width: 130, align: 'right', type: "text_btn", value: "detail", click: (scope) => {
168 if (!scope.row.bindingCount) {
169 proxy.$ElMessage.warning('当前没有绑定连接器');
170 return;
171 }
172 //TODO, 查看连接器信息。
173 }, getName: (scope) => {
174 return scope.row.bindingCount != null ? changeNum(scope.row.bindingCount ?? 0) : '--';
175 }
176 },
165 { label: "API请求路径", field: "requestUrl", width: TableColumnWidth.DESCRIPTION }, 177 { label: "API请求路径", field: "requestUrl", width: TableColumnWidth.DESCRIPTION },
166 { 178 {
167 label: '状态', field: 'apiState', type: 'switch', activeText: '启用', inactiveText: '停用', activeValue: 1, inactiveValue: 0, switchWidth: 56, width: 96, align: 'center' 179 label: '状态', field: 'apiState', type: 'switch', activeText: '启用', inactiveText: '停用', activeValue: 1, inactiveValue: 0, switchWidth: 56, width: 96, align: 'center'
168 // , isDisabled: (scope) => {
169 // if (scope.row.apiState == 1) {//被授权的不能禁用。
170 // return true;
171 // }
172 // return scope.row.approveState != 'Y';//正在审批中的不能停用。草稿中的不能启用。
173 // }
174 }, 180 },
175 // { label: "审批状态", field: "approveState", type: "tag", width: TableColumnWidth.STATE, align: 'center' },
176 { label: "API描述", field: "apiDescription", width: TableColumnWidth.DESCRIPTION }, 181 { label: "API描述", field: "apiDescription", width: TableColumnWidth.DESCRIPTION },
177 { label: "修改人", field: "updateUserName", width: TableColumnWidth.USERNAME }, 182 { label: "修改人", field: "updateUserName", width: TableColumnWidth.USERNAME },
178 { label: "修改时间", field: "updateTime", width: 170 }, 183 { label: "修改时间", field: "updateTime", width: 170 },
......
1 <script lang="ts" setup name="confirmDialog">
2
3 const props = defineProps({
4 visible: {
5 type: Boolean,
6 default: false,
7 },
8 type: {
9 type: String,
10 default: 'success'
11 },
12 msg: {
13 type: String,
14 default: ''
15 }
16 });
17
18 const dialogVisible = computed(() => {
19 return props.visible;
20 });
21
22 const emits = defineEmits([
23 "btnClick",
24 ]);
25
26 const cancelDialog = () => {
27 emits("btnClick", 'cancel');
28 }
29
30 const submit = () => {
31 emits("btnClick", 'submit');
32 }
33
34 const dialogClose = () => {
35 emits("btnClick", 'cancel');
36 }
37
38 const imgSrc = computed(() => {
39 return new URL(`../../../assets/images/confirmType/${props.type}.png`, import.meta.url).href;
40 })
41
42 </script>
43
44 <template>
45 <el-dialog v-model="dialogVisible" title="规则详情" width="460" :modal="true" :close-on-click-modal="true"
46 destroy-on-close align-center @close="dialogClose">
47 <div class="content">
48 <img class="header-img" :src="imgSrc"></img>
49 <div class="title" v-html="msg"></div>
50 </div>
51 <div class="dialog-footer">
52 <el-button @click="cancelDialog">取消</el-button>
53 <el-button @click="submit" type="primary" v-preReClick>确定</el-button>
54 </div>
55 </el-dialog>
56 </template>
57
58 <style lang="scss" scoped>
59 .content {
60 display: flex;
61 flex-direction: column;
62 justify-content: center;
63 align-items: center;
64
65 .header-img {
66 width: 126px;
67 height: 72px;
68 margin-bottom: 12px;
69 }
70
71 .title {
72 width: 364px;
73 font-size: 16px;
74 color: #212121;
75 text-align: center;
76 line-height: 24px;
77 font-weight: 400;
78 margin-bottom: 32px;
79 white-space: pre-line;
80 }
81 }
82
83 .dialog-footer {
84 width: 100%;
85 display: flex;
86 justify-content: center;
87 flex-direction: row;
88 margin-bottom: 12px;
89 }
90 </style>
...\ No newline at end of file ...\ No newline at end of file
1 <script lang="ts" setup name="showFile">
2 import { onUploadFilePreview, onUploadFileDownload } from '@/api/modules/common';
3
4 const props = defineProps({
5 file: {
6 type: Array<any>,
7 default: []
8 },
9 showRemove: {
10 type: Boolean,
11 default: false,
12 }
13 });
14
15 const emits = defineEmits(['deleteFile'])
16
17 const handleUploadFileRemove = (file) => {
18 emits("deleteFile");
19 }
20
21 </script>
22
23 <template>
24 <span v-for="(item) in (file || [])" class="item_value" :style="{ 'padding-left': '0px' }">
25 <div class="file-operate">
26 <template
27 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'">
28 <img class="file-img" src="../../../assets/images/excel.png" />
29 </template>
30 <template
31 v-else-if="item.name.substring(item.name.lastIndexOf('.') + 1).toLowerCase() == 'doc' || item.name.substring(item.name.lastIndexOf('.') + 1).toLowerCase() == 'docx'">
32 <img class="file-img" src="../../../assets/images/word.png" />
33 </template>
34 <template v-else-if="item.name.substring(item.name.lastIndexOf('.') + 1).toLowerCase() == 'zip'">
35 <img class="file-img" src="../../../assets/images/zip.png" />
36 </template>
37 <template v-else-if="item.name.substring(item.name.lastIndexOf('.') + 1).toLowerCase() == 'rar'">
38 <img class="file-img" src="../../../assets/images/RAR.png" />
39 </template>
40 <template v-else-if="item.name.substring(item.name.lastIndexOf('.') + 1).toLowerCase() == 'pdf'">
41 <img class="file-img" src="../../../assets/images/PDF.png" />
42 </template>
43 <template v-else-if="item.name.substring(item.name.lastIndexOf('.') + 1).toLowerCase() == 'png'">
44 <img class="file-img" src="../../../assets/images/png.png" />
45 </template>
46 <template
47 v-else-if="item.name.substring(item.name.lastIndexOf('.') + 1).toLowerCase() == 'jpg' || item.name.substring(item.name.lastIndexOf('.') + 1).toLowerCase() == 'jpeg'">
48 <img class="file-img" src="../../../assets/images/jpg.png" />
49 </template>
50 <div v-if="props.showRemove" :style="{ right: '72px' }" class="file-preview"
51 @click="handleUploadFileRemove(item)">删除
52 </div>
53 <div class="file-name" :style="{ width: showRemove ? 'calc(100% - 150px)' : 'calc(100% - 120px)' }"><ellipsis-tooltip :content="item.name ?? ''" class-name="w100f"
54 refName="tooltipOver"></ellipsis-tooltip></div>
55 <div :style="{ right: '36px' }"
56 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'"
57 class="file-preview" @click="onUploadFilePreview(item)">查看</div>
58 <div :style="{ right: '0px' }" class="file-preview" @click="onUploadFileDownload(item)">下载</div>
59 </div>
60 </span>
61 </template>
62
63 <style lang="scss" scoped>
64 .file-operate {
65 display: flex;
66 align-items: center;
67 position: relative;
68
69 .file-img {
70 width: 24px;
71 height: 24px;
72 }
73
74 &:hover {
75 background-color: #f5f5f5;
76 }
77
78 .file-name {
79 width: calc(100% - 120px);
80 color: var(--el-color-regular);
81 margin-left: 4px;
82 }
83
84 .file-preview {
85 position: absolute;
86 cursor: pointer;
87 color: var(--el-color-primary);
88 margin-right: 8px;
89 }
90 }
91 </style>
...\ No newline at end of file ...\ No newline at end of file
1 <template>
2 <div v-if="showTitle" class="title-row">
3 <div class="h-title">策略信息</div>
4 <el-button v-if="!isLook" plain @click="invokeTemplate" v-preReClick>合约模板调用</el-button>
5 </div>
6 <el-table class="strategyTable" ref="strategyTableRef" v-loading="strategyDataLoading" :data="strategyData"
7 :height="isReport ? 'auto' : '250px'" :highlight-current-row="true" stripe tooltip-effect="light" border :span-method="arraySpanMethod">
8 <el-table-column label="序号" width="56" align="center" fixed="left" :formatter="formatIndex" />
9 <el-table-column prop="action" label="操作行为" :width="isReport ? 'auto' : '150px'" :min-width="isReport ? '100px' : undefined" align="left" :show-overflow-tooltip="!isReport">
10 <template #header>
11 <span>操作行为</span>
12 <span style="color:red;margin-left: 2px;">*</span>
13 </template>
14 <template #default="scope">
15 <el-select v-if="!isLook" v-model="scope.row['action']" placeholder="请选择" :disabled="isLook"
16 @change="(val) => selectOperationChange(val, scope)" clearable filterable>
17 <el-option
18 v-for="opt in actionOptionsList.filter(a => scope.row.action == a.policyName || !strategyData.some(s => s.action == a.policyName))"
19 :key="opt['policyName']" :label="opt['policyName']" :value="opt['policyName']" />
20 </el-select>
21 <span v-else>{{ scope.row['action'] || '--' }}</span>
22 </template>
23 </el-table-column>
24 <el-table-column prop="actionEnName" label="操作行为英文名称" :width="isReport ? 'auto' : '150px'" :min-width="isReport ? '120px' : undefined" align="left" :show-overflow-tooltip="!isReport">
25 <template #default="scope">
26 <el-input v-if="!isLook" v-model="scope.row['actionEnName']" :disabled="true" placeholder="-"></el-input>
27 <span v-else>{{ scope.row['actionEnName'] || '--' }}</span>
28 </template>
29 </el-table-column>
30 <el-table-column prop="constraintName" label="约束条件" :width="isReport ? 'auto' : '150px'" :min-width="isReport ? '100px' : undefined" align="left" :show-overflow-tooltip="!isReport">
31 <template #header>
32 <span>约束条件</span>
33 <span style="color:red;margin-left: 2px;">*</span>
34 </template>
35 <template #default="scope">
36 <el-select v-if="!isLook" v-model="scope.row['constraintName']" placeholder="请选择" :disabled="isLook"
37 @change="(val) => selectConditionChange(val, scope)" clearable filterable>
38 <el-option v-for="opt in constraintOptionsList" :key="opt['policyName']" :label="opt['policyName']"
39 :value="opt['policyName']" />
40 </el-select>
41 <span v-else>{{ scope.row['constraintName'] || '--' }}</span>
42 </template>
43 </el-table-column>
44 <el-table-column prop="constraintEnName" label="约束条件英文名称" :width="isReport ? 'auto' : '150px'" :min-width="isReport ? '120px' : undefined" align="left" :show-overflow-tooltip="!isReport">
45 <template #default="scope">
46 <el-input v-if="!isLook" v-model="scope.row['constraintEnName']" :disabled="true" placeholder="-"></el-input>
47 <span v-else>{{ scope.row['constraintEnName'] || '--' }}</span>
48 </template>
49 </el-table-column>
50 <el-table-column prop="constraintOperatorCode" label="运算符" :width="isReport ? 'auto' : '150px'" :min-width="isReport ? '100px' : undefined" align="left" :show-overflow-tooltip="!isReport">
51 <template #header>
52 <span>运算符</span>
53 <span style="color:red;margin-left: 2px;">*</span>
54 </template>
55 <template #default="scope">
56 <el-select v-if="!isLook" v-model="scope.row['constraintOperatorCode']" placeholder="请选择" :disabled="isLook"
57 clearable filterable @change="(val) => handleOperatorSelectChange(val, scope, 'constraintOperatorCode')">
58 <el-option v-for="opt in getActualOperationList(scope)" :key="opt['value']" :label="opt['label']"
59 :value="opt['value']" />
60 </el-select>
61 <span v-else>{{ scope.row['constraintOperatorName'] || '--' }}</span>
62 </template>
63 </el-table-column>
64 <el-table-column prop="constraintValue" label="约束值" :width="isReport ? 'auto' : '240px'" :min-width="isReport ? '160px' : undefined" align="left" :show-overflow-tooltip="!isReport">
65 <template #header>
66 <span>约束值</span>
67 <span style="color:red;margin-left: 2px;">*</span>
68 </template>
69 <template #default="scope">
70 <el-input v-if="!isLook" v-model="scope.row['constraintValue']" placeholder="请输入" :maxlength="200"
71 @change="(val) => handleOperatorSelectChange(val, scope, 'constraintValue')" clearable></el-input>
72 <span v-else>{{ scope.row['constraintValue'] || '--' }}</span>
73 </template>
74 </el-table-column>
75 <el-table-column v-if="!isLook" label="操作" width="140px" align="left" fixed="right" show-overflow-tooltip>
76 <template #default="scope">
77 <span class="text_btn" @click="handleConditionClickAdd(scope)" v-preReClick>添加约束</span>
78 <el-divider direction="vertical" />
79 <span class="text_btn" @click="handleConditionDelete(scope)">删除</span>
80 </template>
81 </el-table-column>
82 </el-table>
83 <div v-if="!isLook" class="row-add-btn">
84 <el-button link @click="addStrategy" :icon="CirclePlus" v-preReClick>添加操作行为</el-button>
85 </div>
86 <!-- 选择合约模板对话框 -->
87 <Dialog_form :dialogConfigInfo="templateDialogInfo" />
88 </template>
89
90 <script lang="ts" setup name="strategyTable">
91 import { useValidator } from "@/hooks/useValidator";
92 import { CirclePlus } from "@element-plus/icons-vue";
93 import {
94 getValidContractTemplateList
95 } from "@/api/modules/dataSmartContract"
96
97 const { required } = useValidator();
98 const { proxy } = getCurrentInstance() as any;
99
100 const props = defineProps({
101 isLook: { //是否是查看页面,详情查看不需要展示输入框。
102 type: Boolean,
103 default: false
104 },
105 value: { // 是个表格数组
106 type: Array<any>,
107 default: <any>[]
108 },
109 showTitle: {
110 type: Boolean,
111 default: false
112 },
113 actionOptionsList: {//策略操作行为下拉列表
114 type: Array<any>,
115 default: []
116 },
117 constraintOptionsList: {//约束行为下拉列表
118 type: Array<any>,
119 default: []
120 },
121 operatorOptionList: {//运算符字典下拉列表
122 type: Array<any>,
123 default: []
124 },
125 isReport: {
126 type: Boolean,
127 default: false
128 }
129 })
130
131 const getActualOperationList = (scope) => {
132 let val = scope.row.constraintName;
133 let conditionItem = val && props.constraintOptionsList.find(c => c.policyName == val);
134 let constraintOperatorCodes = conditionItem?.constraintOperatorCodes || [];
135 if (!constraintOperatorCodes?.length) {
136 return props.operatorOptionList;
137 }
138 let list: any = constraintOperatorCodes?.map((c, index) => {
139 return {
140 value: c,
141 label: conditionItem?.constraintOperatorCodesName?.[index] || c
142 }
143 });
144 return list;
145 }
146
147 const strategyDataLoading = ref(false);
148
149 /** 策略信息结构最终值,带children层级结构 */
150 const strategyValueInfo: any = ref([{
151 index: 1,
152 action: '',
153 children: [{ childIndex: 1 }]
154 }]);
155
156 /** 将数据库存储的值转化为该组件接收的树形结构 */
157 const transferValueToNew = (val) => {
158 let detailPolicyListValue: any[] = []
159 val?.forEach((p, index) => {
160 let lastItem = detailPolicyListValue?.[detailPolicyListValue.length - 1];
161 let childInfo = Object.assign({}, p);
162 delete childInfo.children; //删掉冗余信息,否则后续修改会覆盖正确的值
163 delete childInfo.action;
164 delete childInfo.actionEnName;
165 if (!lastItem || lastItem.action != p.action) {
166 detailPolicyListValue.push(Object.assign({
167 index: detailPolicyListValue.length + 1,
168 }, p, {
169 children: [Object.assign({ childIndex: 1 }, childInfo)]
170 }))
171 } else {
172 lastItem.children.push(Object.assign({ childIndex: lastItem.children?.length + 1 }, childInfo));
173 }
174 })
175 return detailPolicyListValue;
176 }
177
178 watch(() => props.value, (val) => {
179 strategyValueInfo.value = transferValueToNew(val);
180 }, {
181 deep: true,
182 immediate: true
183 })
184
185 /** 策略信息表格数据 */
186 const strategyData = computed(() => {
187 let v: any = [];
188 strategyValueInfo.value.forEach(s => {
189 s.children?.forEach(sc => {
190 v.push(Object.assign({}, {
191 guid: sc.guid,
192 action: s.action,
193 actionEnName: s.actionEnName,
194 index: s.index,
195 orderNum: s.orderNum
196 }, sc));
197 })
198 })
199 return v;
200 });
201
202 /** 操作行为下拉框改变,带出英文名称等信息 */
203 const selectOperationChange = (val, scope) => {
204 let index = scope.row.index;
205 let sv = strategyValueInfo.value.find(s => s.index == index);
206 sv && (sv.actionEnName = props.actionOptionsList.find(o => o.policyName == val)?.policyEnName);
207 sv && (sv.action = val);
208 }
209
210 /** 约束条件下拉框改变,带出英文名称 */
211 const selectConditionChange = (val, scope) => {
212 let index = scope.row.index;
213 let sv = strategyValueInfo.value.find(s => s.index == index);
214 let svChild = sv.children?.find(c => c.childIndex == scope.row.childIndex);
215 svChild && (svChild.constraintEnName = props.constraintOptionsList.find(o => o.policyName == val)?.policyEnName);
216 svChild && (svChild.constraintName = val);
217 }
218
219 const handleOperatorSelectChange = (val, scope, field) => {
220 let index = scope.row.index;
221 let sv = strategyValueInfo.value.find(s => s.index == index);
222 let svChild = sv.children?.find(c => c.childIndex == scope.row.childIndex);
223 svChild && (svChild[field] = val);
224 }
225
226 /** 添加策略信息的操作行为行 */
227 const addStrategy = () => {
228 let len = strategyValueInfo.value.length;
229 if (len == props.actionOptionsList?.length) {
230 proxy.$ElMessage.warning('暂无可添加的操作行为!');
231 return;
232 }
233 let lastIndex = strategyValueInfo.value.at(-1)?.index || 0;
234 strategyValueInfo.value.push({
235 index: lastIndex + 1,
236 action: '',
237 children: [{ childIndex: 1 }]
238 })
239 }
240
241 /** 添加约束条件 */
242 const handleConditionClickAdd = (scope) => {
243 /** 根据index值进行添加删除 */
244 let index = scope.row.index;
245 let sv = strategyValueInfo.value.find(s => s.index == index);
246 if (sv) {
247 let childIndex = scope.row.childIndex;
248 let lastIndex = sv.children.at(-1)?.childIndex || 0;
249 sv.children.splice(childIndex, 0, { childIndex: lastIndex + 1 })
250 }
251 }
252
253 const handleConditionDelete = (scope) => {
254 let index = scope.row.index;
255 let sv = strategyValueInfo.value.find(s => s.index == index);
256 if (!sv) {
257 return;
258 }
259 if (sv?.children?.length > 1) {
260 //删除约束条件。
261 proxy.$openMessageBox("确定要删除该约束条件吗?", () => {
262 let childIndex = scope.row.childIndex;
263 let delIndex = sv.children.findIndex(c => c.childIndex == childIndex);
264 delIndex > -1 && sv.children.splice(delIndex, 1);
265 proxy.$ElMessage.success('删除成功');
266 }, () => {
267 proxy.$ElMessage.info("已取消删除");
268 })
269 } else {
270 //同步删除操作行为
271 proxy.$openMessageBox("确定要删除该操作行为和约束条件吗?", () => {
272 let index = scope.row.index;
273 strategyValueInfo.value.splice(index - 1, 1);
274 proxy.$ElMessage.success('删除成功');
275 }, () => {
276 proxy.$ElMessage.info("已取消删除");
277 })
278 }
279 }
280
281 // 计算合并后的序号
282 const mergedIndexes = computed(() => {
283 let index = 1;
284 const mergedIndexes: any[] = [];
285
286 for (let i = 0; i < strategyData.value.length; i++) {
287 if (i === 0 || strategyData.value[i - 1]['action'] != strategyData.value[i]['action'] || strategyData.value[i - 1]['index'] != strategyData.value[i]['index']) {
288 // 新合并组开始
289 mergedIndexes.push(index);
290 index++;
291 } else {
292 // 同一合并组
293 mergedIndexes.push(0); // 0表示不显示序号
294 }
295 }
296
297 return mergedIndexes;
298 });
299
300 // 序号格式化
301 const formatIndex = (row, column, cellValue, index) => {
302 return <string>mergedIndexes.value[index] || '';
303 };
304
305 const arraySpanMethod = ({ row, column, rowIndex, columnIndex }) => {
306 if (columnIndex > 2) {
307 return [1, 1];
308 }
309
310 // 查找当前字段值相同的连续行
311 let startRow = rowIndex;
312 let endRow = rowIndex;
313 let field = column.field;
314 // 向前查找
315 while (startRow > 0 && strategyData.value[startRow - 1][field] === strategyData.value[rowIndex][field] && strategyData.value[startRow - 1]['index'] == strategyData.value[rowIndex]['index']) {
316 startRow--;
317 }
318
319 // 向后查找
320 while (endRow < strategyData.value.length - 1 && strategyData.value[endRow + 1][field] === strategyData.value[rowIndex][field] && strategyData.value[endRow + 1]['index'] === strategyData.value[rowIndex]['index']) {
321 endRow++;
322 }
323
324 // 如果当前行不是相同值组的起始行,则不显示
325 if (startRow !== rowIndex) {
326 return [0, 0];
327 }
328
329 // 返回合并的行数
330 const rowspan = endRow - startRow + 1;
331 return [rowspan, 1];
332 }
333
334 /** 弹出模板选择对话框 */
335 const invokeTemplate = () => {
336 /** 调用获取启用的模板列表接口,如果返回null则提示没有可调用的模板。 */
337 getValidContractTemplateList().then((res: any) => {
338 if (res?.code == proxy.$passCode) {
339 const listData = res.data || [];
340 if (!listData?.length) {
341 proxy.$ElMessage.warning('当前没有可调用的合约模板');
342 return;
343 }
344 templateFormItems.value[0].options = listData;
345 templateFormItems.value[0].default = '';
346 templateDialogInfo.value.visible = true;
347 } else {
348 res?.msg && proxy.$ElMessage.error(res?.msg)
349 }
350 })
351 }
352
353 const templateFormItems = ref([{
354 type: 'select',
355 label: '合约模板名称',
356 field: 'guid',
357 default: '',
358 block: true,
359 placeholder: '请选择',
360 options: [],
361 props: {
362 value: 'guid',
363 label: 'templateName'
364 },
365 filterable: true,
366 clearable: true,
367 required: true
368 }]);
369
370 const templateFormRules = ref({
371 guid: [required("请选择合约模板名称")],
372 });
373
374 const templateDialogInfo = ref({
375 visible: false,
376 size: 480,
377 title: "合约模板调用",
378 type: 'edit',
379 formInfo: {
380 id: 'copy-form',
381 items: templateFormItems.value,
382 rules: templateFormRules.value
383 },
384 btns: {
385 submit: (btn, info) => {
386 templateDialogInfo.value.visible = false;
387 //将选择的值带入表格 TODO,跟经理确认是全量覆盖,还是保留操作行为不同的行。暂时先做成全量覆盖吧。
388 let item: any = templateFormItems.value[0].options?.find((i: any) => i.guid == info.guid);
389 strategyValueInfo.value = transferValueToNew(item?.policyRSVOS || []);
390 },
391 cancel: () => {
392 templateDialogInfo.value.visible = false;
393 }
394 },
395 submitBtnLoading: false
396 });
397
398 const validateValue = () => {
399 for (const d of strategyData.value) {
400 if (!d.action) {
401 proxy.$ElMessage.error('操作行为必填,请填写完整');
402 return false;
403 }
404 if (!d.constraintName) {
405 proxy.$ElMessage.error('约束条件必填,请填写完整');
406 return false;
407 }
408 if (!d.constraintOperatorCode) {
409 proxy.$ElMessage.error('运算符必填,请填写完整');
410 return false;
411 }
412 if (!d.constraintValue) {
413 proxy.$ElMessage.error('约束值必填,请填写完整');
414 return false;
415 }
416 }
417 return true;
418 }
419
420 defineExpose({
421 strategyData,
422 validateValue
423 })
424 </script>
425
426 <style lang="scss" scoped>
427 .row-add-btn {
428 .el-button--default {
429 padding: 4px 0px;
430 margin-top: 4px;
431 }
432
433 :deep(.el-icon) {
434 width: 16px;
435 height: 16px;
436
437 svg {
438 width: 16px;
439 height: 16px;
440 }
441 }
442 }
443
444 .title-row {
445 display: flex;
446 flex-direction: row;
447 height: 44px;
448 align-items: center;
449
450 .el-button {
451 color: var(--el-button-hover-text-color);
452 border-color: var(--el-button-hover-border-color);
453 }
454 }
455
456 .h-title {
457 font-size: 14px;
458 color: #212121;
459 font-weight: 600;
460 margin-right: 8px;
461 }
462 </style>
...\ No newline at end of file ...\ No newline at end of file
1 <route lang="yaml">
2 name: contractLogManage
3 </route>
4
5 <script lang="ts" setup name="contractLogManage">
6 import TableTools from "@/components/Tools/table_tools.vue";
7 import { commonPageConfig, TableColumnWidth } from "@/utils/enum";
8 import {
9 getContractOperationLog,
10 getContractOperationLogDetail,
11 getContractProcessLog,
12 getContractProcessLogDetail,
13 getContractNegoPlicyByVersion
14 } from "@/api/modules/dataSmartContract";
15 import StrategyTable from './components/strategyTable.vue';
16
17 const { proxy } = getCurrentInstance() as any;
18
19 const activeTabName = ref('operation');
20
21 /** 操作主体下拉列表 */
22 // const operationEntityListData: any = ref([]);
23
24 /** 操作记录筛选框 */
25 const opSearchItemList = ref([{
26 type: 'date-time',
27 label: '',
28 field: 'operatorTime',
29 default: [],
30 defaultStartTime: new Date(2000, 1, 1, 0, 0, 0),
31 defaultEndTime: new Date(2000, 1, 1, 23, 59, 59),
32 startPlaceholder: '操作开始时间',
33 endPlaceholder: '操作结束时间',
34 clearable: true
35 }, {
36 type: 'input',
37 label: '',
38 field: 'operationEntityName',
39 default: '',
40 placeholder: '操作主体',
41 // props: {
42 // value: 'tenantGuid',
43 // label: 'tenantName'
44 // },
45 // options: operationEntityListData.value,
46 //filterable: true,
47 maxlength: 50,
48 clearable: true
49 }, {
50 type: "input",
51 label: "",
52 field: "contractName",
53 default: "",
54 placeholder: "合约名称",
55 maxlength: 50,
56 clearable: true,
57 },]);
58
59 /** 过程记录筛选框 */
60 const processTableSearchItemList = ref([{
61 type: 'date-time',
62 label: '',
63 field: 'operatorTime',
64 default: [],
65 defaultStartTime: new Date(2000, 1, 1, 0, 0, 0),
66 defaultEndTime: new Date(2000, 1, 1, 23, 59, 59),
67 startPlaceholder: '操作开始时间',
68 endPlaceholder: '操作结束时间',
69 clearable: true
70 }, {
71 type: 'select',
72 label: '',
73 field: 'executionProcess',
74 default: '',
75 placeholder: '执行环节',
76 props: {
77 value: 'value',
78 label: 'label'
79 },
80 options: [{
81 value: '策略解析',
82 }, {
83 value: '行为校验',
84 }, {
85 value: '操作执行',
86 }],
87 filterable: true,
88 clearable: true
89 }, {
90 type: "input",
91 label: "",
92 field: "executionEntityName",
93 default: "",
94 placeholder: "执行节点名称",
95 maxlength: 50,
96 clearable: true,
97 },]);
98
99 /** ------------------ 操作记录 ----------------------- */
100 const opPage = ref({
101 ...commonPageConfig,
102 contractName: '',
103 operatorTime: [],
104 operationEntityName: ''
105 });
106
107 const currTableData: any = ref({});
108
109 const opTableInfo = ref({
110 id: "op-table",
111 rowKey: 'guid',
112 fields: [
113 { label: "序号", type: "index", width: TableColumnWidth.INDEX, align: "center" },
114 { label: "操作编号", field: "operationId", width: 140 },
115 { label: "操作时间", field: "operationTime", width: 170 },
116 { label: "操作主体", field: "operationEntityName", width: 220 },
117 { label: "操作合约名称", field: "contractName", width: 160 },
118 { label: "操作合约标识", field: "contractId", width: 355 },
119 { label: "操作类型", field: "operationType", width: 120 },
120 ],
121 data: [],
122 showPage: true,
123 page: {
124 type: "normal",
125 rows: 0,
126 ...opPage.value,
127 },
128 actionInfo: {
129 label: "操作",
130 type: "btn",
131 width: 80,
132 btns: [{
133 value: 'view', label: '查看', click: (scope) => {
134 currTableData.value = scope.row;
135 detailDialogInfo.value.visible = true;
136 detailDialogInfo.value.contentLoading = true;
137 getContractOperationLogDetail(scope.row.guid).then((res: any) => {
138 detailDialogInfo.value.contentLoading = false;
139 if (res?.code == proxy.$passCode) {
140 opDetailInfo.value = res.data || {};
141 } else {
142 res?.msg && proxy.$ElMessage.error(res?.msg)
143 }
144 })
145 }
146 }]
147 },
148 loading: false
149 });
150
151 const toTableSearch = (val: any, clear: boolean = false) => {
152 if (clear) {
153 opSearchItemList.value.map((item) => (item.default = ""));
154 opPage.value.contractName = '';
155 opPage.value.operationEntityName = '';
156 opPage.value.operatorTime = [];
157 } else {
158 opPage.value.contractName = val.contractName;
159 opPage.value.operationEntityName = val.operationEntityName;
160 opPage.value.operatorTime = val.operatorTime;
161 }
162 getOpTableData();
163 };
164
165 const getOpTableData = () => {
166 opTableInfo.value.loading = true
167 getContractOperationLog({
168 pageIndex: opPage.value.curr,
169 pageSize: opPage.value.limit,
170 contractName: opPage.value.contractName,
171 operationEntityName: opPage.value.operationEntityName,
172 operationTimeStart: opPage.value.operatorTime?.[0],
173 operationTimeEnd: opPage.value.operatorTime?.[1]
174 }).then((res: any) => {
175 opTableInfo.value.data = [];
176 if (res?.code == proxy.$passCode) {
177 const data = res.data || {};
178 opTableInfo.value.loading = false
179 opTableInfo.value.data = data.records || []
180 opTableInfo.value.page.limit = data.pageSize
181 opTableInfo.value.page.curr = data.pageIndex
182 opTableInfo.value.page.rows = data.totalRows
183 } else {
184 res?.msg && proxy.$ElMessage.error(res?.msg)
185 opTableInfo.value.loading = false
186 }
187 }).catch(() => {
188 opTableInfo.value.loading = false
189 })
190 }
191
192 const opTablePageChange = (info) => {
193 opPage.value.curr = Number(info.curr);
194 opPage.value.limit = Number(info.limit);
195 opTableInfo.value.page.curr = opPage.value.curr;
196 opTableInfo.value.page.limit = opPage.value.limit;
197 getOpTableData();
198 };
199
200 /** ------------------ 过程记录 ----------------------- */
201 const processPage = ref({
202 ...commonPageConfig,
203 executionEntityName: '',
204 operatorTime: [],
205 executionProcess: ''
206 });
207
208 const processTableInfo = ref({
209 id: "process-table",
210 rowKey: 'guid',
211 fields: [
212 { label: "序号", type: "index", width: TableColumnWidth.INDEX, align: "center" },
213 { label: "执行编号", field: "operationId", width: 140 },
214 { label: "执行时间", field: "operationTime", width: 170 },
215 { label: "执行节点名称", field: "executionEntityName", width: 220 },
216 { label: "执行节点标识", field: "executionEntityId", width: 175 },
217 { label: "执行合约名称", field: "contractName", width: 160 },
218 { label: "执行环节", field: "executionProcess", width: 120 },
219 ],
220 data: [],
221 showPage: true,
222 page: {
223 type: "normal",
224 rows: 0,
225 ...processPage.value,
226 },
227 actionInfo: {
228 label: "操作",
229 type: "btn",
230 width: 80,
231 btns: [{
232 value: 'view', label: '查看', click: (scope) => {
233 currTableData.value = scope.row;
234 processDetailDialogInfo.value.visible = true;
235 processDetailDialogInfo.value.contentLoading = true;
236 getContractProcessLogDetail(scope.row.guid).then((res: any) => {
237 processDetailDialogInfo.value.contentLoading = false;
238 if (res?.code == proxy.$passCode) {
239 processDetailInfo.value = res.data || {};
240 execContractTableInfo.value.data = processDetailInfo.value.policys || [];
241 } else {
242 res?.msg && proxy.$ElMessage.error(res?.msg)
243 }
244 })
245 }
246 }]
247 },
248 loading: false
249 });
250
251 const toProcessTableSearch = (val: any, clear: boolean = false) => {
252 if (clear) {
253 processTableSearchItemList.value.map((item) => (item.default = ""));
254 processPage.value.executionEntityName = '';
255 processPage.value.executionProcess = '';
256 processPage.value.operatorTime = [];
257 } else {
258 processPage.value.executionEntityName = val.executionEntityName;
259 processPage.value.executionProcess = val.executionProcess;
260 processPage.value.operatorTime = val.operatorTime;
261 }
262 getProcessTableData();
263 };
264
265 const getProcessTableData = () => {
266 processTableInfo.value.loading = true
267 getContractProcessLog({
268 pageIndex: processPage.value.curr,
269 pageSize: processPage.value.limit,
270 executionEntityName: processPage.value.executionEntityName,
271 executionProcess: processPage.value.executionProcess,
272 operationTimeStart: processPage.value.operatorTime?.[0],
273 operationTimeEnd: processPage.value.operatorTime?.[1]
274 }).then((res: any) => {
275 processTableInfo.value.data = [];
276 if (res?.code == proxy.$passCode) {
277 const data = res.data || {};
278 processTableInfo.value.loading = false
279 processTableInfo.value.data = data.records || []
280 processTableInfo.value.page.limit = data.pageSize
281 processTableInfo.value.page.curr = data.pageIndex
282 processTableInfo.value.page.rows = data.totalRows
283 } else {
284 res?.msg && proxy.$ElMessage.error(res?.msg)
285 processTableInfo.value.loading = false
286 }
287 }).catch(() => {
288 processTableInfo.value.loading = false
289 })
290 }
291
292 const processTablePageChange = (info) => {
293 processPage.value.curr = Number(info.curr);
294 processPage.value.limit = Number(info.limit);
295 processTableInfo.value.page.curr = processPage.value.curr;
296 processTableInfo.value.page.limit = processPage.value.limit;
297 getProcessTableData();
298 };
299
300 onBeforeMount(() => {
301 toTableSearch({});
302 toProcessTableSearch({});
303 })
304
305 /** 操作记录详情对话框 */
306 const detailDialogInfo = ref({
307 visible: false,
308 size: 855,
309 direction: "column",
310 header: {
311 title: "查看操作记录",
312 },
313 type: '',
314 contents: [],
315 footer: {
316 show: false
317 },
318 contentLoading: false,
319 });
320
321 /** 查看操作记录详情 */
322 const opDetailInfo: any = ref({});
323
324 const handleDialogBtnClick = (btn) => {
325 if (btn.value == 'cancel') {
326 detailDialogInfo.value.visible = false;
327 }
328 }
329
330 /** 查看操作记录详情对话框 */
331 const versionDetailDialogInfo = ref({
332 visible: false,
333 size: 800,
334 direction: "column",
335 header: {
336 title: "查看版本详情",
337 },
338 type: '',
339 contents: [],
340 footer: {
341 show: false
342 },
343 contentLoading: false,
344 });
345
346 /** 查看协商策略版本详情 */
347 const versionDetail: any = ref([]);
348
349 const handleVersionDialogBtnClick = (btn) => {
350 if (btn.value == 'cancel') {
351 versionDetailDialogInfo.value.visible = false;
352 }
353 }
354
355 /** 店家查看协商版本 */
356 const clickVersion = (child) => {
357 versionDetailDialogInfo.value.visible = true;
358 versionDetailDialogInfo.value.header.title = '查看版本详情' + '(' + child.version + ')';
359 versionDetailDialogInfo.value.contentLoading = true;
360 getContractNegoPlicyByVersion({
361 guid: currTableData.value.contractGuid,
362 version: child.version
363 }).then((res: any) => {
364 versionDetailDialogInfo.value.contentLoading = false;
365 if (res?.code == proxy.$passCode) {
366 const data = res.data || [];
367 versionDetail.value = data;
368 } else {
369 res?.msg && proxy.$ElMessage.error(res?.msg);
370 }
371 })
372 }
373
374 /** 操作记录详情对话框 */
375 const processDetailDialogInfo = ref({
376 visible: false,
377 size: 855,
378 direction: "column",
379 header: {
380 title: "查看过程记录",
381 },
382 type: '',
383 contents: [],
384 footer: {
385 show: false
386 },
387 contentLoading: false,
388 });
389
390 /** 查看操作记录详情 */
391 const processDetailInfo: any = ref({});
392
393 const handleProcessDialogBtnClick = (btn) => {
394 if (btn.value == 'cancel') {
395 processDetailDialogInfo.value.visible = false;
396 }
397 }
398
399 const execContractTableInfo = ref({
400 id: "exec-contract-table",
401 height: '214px',
402 fields: <any[]>[
403 { label: "序号", type: "index", width: TableColumnWidth.INDEX, align: "center" },
404 { label: "策略id", field: "strategyId", width: 260 },
405 { label: "操作行为", field: "action", width: 120 },
406 { label: "操作行为英文名称", field: "actionEnName", width: 140 },
407 { label: "约束条件", field: "constraintName", width: 120 },
408 { label: "约束条件英文名称", field: "constraintEnName", width: 140 },
409 { label: "约束条件运算符", field: "constraintOperatorName", width: 140 },
410 { label: "约束条件值", field: "constraintValue", width: 150 },
411 { label: "执行结果", field: "result", width: 130 },
412 // { label: "上报时间", field: "reportingTime", width: 170 },
413 ],
414 data: [],
415 showPage: false,
416 actionInfo: {
417 show: false
418 },
419 loading: false
420 });
421
422 </script>
423
424 <template>
425 <el-tabs v-model="activeTabName" class="log-tabs">
426 <el-tab-pane label="操作记录" name="operation">
427 <div class="table_tool_wrap">
428 <TableTools :searchItems="opSearchItemList" :init="false" searchId="op-table-search" @search="toTableSearch" />
429 </div>
430 <div class="table_panel_wrap">
431 <Table :tableInfo="opTableInfo" @tablePageChange="opTablePageChange" />
432 </div>
433 </el-tab-pane>
434 <el-tab-pane label="过程记录" name="process">
435 <div class="table_tool_wrap">
436 <TableTools :searchItems="processTableSearchItemList" :init="false" searchId="process-table-search"
437 @search="toProcessTableSearch" />
438 </div>
439 <div class="table_panel_wrap">
440 <Table :tableInfo="processTableInfo" @tablePageChange="processTablePageChange" />
441 </div>
442 </el-tab-pane>
443 <Dialog ref="dialogRef" :dialogInfo="detailDialogInfo" class="log-table-detail" @btnClick="handleDialogBtnClick">
444 <template #extra-content>
445 <div class="main-content" v-loading="detailDialogInfo.contentLoading">
446 <div class="list_panel">
447 <div class="list_item is_block">
448 <span class="item_label">操作类型:</span>
449 <span class="item_value">{{ opDetailInfo?.operationType || '--' }}</span>
450 </div>
451 <div class="list_item">
452 <span class="item_label">合约标识:</span>
453 <span class="item_value"><ellipsis-tooltip :content="opDetailInfo?.contractId || '--'"
454 class-name="w100f mr8-i" :refName="'tooltipOver' + 'contractId'"></ellipsis-tooltip></span>
455 </div>
456 <div class="list_item">
457 <span class="item_label">合约名称:</span>
458 <span class="item_value"><ellipsis-tooltip :content="opDetailInfo?.contractName || '--'"
459 class-name="w100f mr8-i" :refName="'tooltipOver' + 'contractName'"></ellipsis-tooltip></span>
460 </div>
461 <div class="list_item">
462 <span class="item_label">操作主体标识:</span>
463 <span class="item_value"><ellipsis-tooltip :content="opDetailInfo?.operationEntityId || '--'"
464 class-name="w100f mr8-i" :refName="'tooltipOver' + 'operationEntityId'"></ellipsis-tooltip></span>
465 </div>
466 <div class="list_item">
467 <span class="item_label">操作主体名称:</span>
468 <span class="item_value"><ellipsis-tooltip :content="opDetailInfo?.operationEntityName || '--'"
469 class-name="w100f mr8-i" :refName="'tooltipOver' + 'operationEntityName'"></ellipsis-tooltip></span>
470 </div>
471 <div class="list_item is_block">
472 <span class="item_label">操作时间:</span>
473 <span class="item_value">{{ opDetailInfo?.operationTime || '--' }}</span>
474 </div>
475 <div class="list_item is_block">
476 <span class="item_label">操作内容:</span>
477 <span class="item_value">修改前<span class="link"
478 @click="clickVersion(opDetailInfo.operationContent?.[0])">{{
479 opDetailInfo.operationContent?.[0]?.version || '--' }}</span>
480 <span class="item_value" style="display: block;padding-left: 0px;">修改后<span class="link"
481 @click="clickVersion(opDetailInfo.operationContent?.[1])">{{
482 opDetailInfo.operationContent?.[1]?.version || '--' }}</span></span></span>
483 </div>
484 </div>
485 </div>
486 </template>
487 </Dialog>
488
489 <Dialog ref="processDialogRef" :dialogInfo="processDetailDialogInfo" class="log-process-detail"
490 @btnClick="handleProcessDialogBtnClick">
491 <template #extra-content>
492 <div class="main-content" v-loading="processDetailDialogInfo.contentLoading">
493 <div class="list_panel">
494 <div class="list_item">
495 <span class="item_label">执行时间:</span>
496 <span class="item_value">{{ processDetailInfo?.operationTime || '--' }}</span>
497 </div>
498 <div class="list_item">
499 <span class="item_label">执行结果:</span>
500 <span class="item_value"><ellipsis-tooltip :content="processDetailInfo?.executionResult || '--'"
501 class-name="w100f mr8-i" :refName="'tooltipOver' + 'executionResult'"></ellipsis-tooltip></span>
502 </div>
503 <div class="list_item">
504 <span class="item_label">执行环节:</span>
505 <span class="item_value"><ellipsis-tooltip :content="processDetailInfo?.executionProcess || '--'"
506 class-name="w100f mr8-i" :refName="'tooltipOver' + 'executionProcess'"></ellipsis-tooltip></span>
507 </div>
508 <div class="list_item">
509 <span class="item_label">合约名称:</span>
510 <span class="item_value"><ellipsis-tooltip :content="processDetailInfo?.contractName || '--'"
511 class-name="w100f mr8-i" :refName="'tooltipOver' + 'contractName'"></ellipsis-tooltip></span>
512 </div>
513 <div class="list_item">
514 <span class="item_label">执行节点标识:</span>
515 <span class="item_value"><ellipsis-tooltip :content="processDetailInfo?.executionEntityId || '--'"
516 class-name="w100f mr8-i" :refName="'tooltipOver' + 'executionEntityId'"></ellipsis-tooltip></span>
517 </div>
518 <div class="list_item">
519 <span class="item_label">执行节点名称:</span>
520 <span class="item_value"><ellipsis-tooltip :content="processDetailInfo?.executionEntityName || '--'"
521 class-name="w100f mr8-i" :refName="'tooltipOver' + 'executionEntityName'"></ellipsis-tooltip></span>
522 </div>
523 <div class="h-title">策略信息</div>
524 <Table :table-info="execContractTableInfo"></Table>
525 </div>
526 </div>
527 </template>
528 </Dialog>
529 <Dialog ref="versionDialogRef" :dialogInfo="versionDetailDialogInfo" class="policy-table-detail"
530 @btnClick="handleVersionDialogBtnClick">
531 <template #extra-content>
532 <div v-loading="versionDetailDialogInfo.contentLoading">
533 <StrategyTable ref="strategyTableDetailRef" :show-title="true" :is-look="true" :value="versionDetail">
534 </StrategyTable>
535 </div>
536 </template>
537 </Dialog>
538 </el-tabs>
539 </template>
540
541 <style lang="scss" scoped>
542 .table_panel_wrap {
543 height: calc(100% - 44px);
544 }
545
546 .main-content {
547 margin: 20px;
548 }
549
550 .list_panel {
551 display: flex;
552 flex-wrap: wrap;
553 display: flex;
554 align-items: flex-start;
555
556 &.main {
557 .list_item {
558 width: 25%;
559 }
560 }
561
562 .list_item {
563 width: 50%;
564 line-height: 32px;
565 font-size: 14px;
566 color: var(--el-text-color-regular);
567 display: flex;
568 justify-content: space-between;
569 min-width: 120px;
570
571 .item_label {
572 text-align: left;
573 }
574
575 .item_value {
576 color: var(--el-color-regular);
577 padding: 0 4px 0 0;
578 flex: 1;
579 text-align: justify;
580 min-width: 0;
581
582 .link {
583 color: var(--el-color-primary);
584 cursor: pointer;
585 margin-left: 4px;
586 }
587
588 }
589
590 &.is_block {
591 width: 100%;
592
593 .item_value {
594 white-space: pre-wrap;
595 }
596 }
597
598 }
599 }
600
601 :deep(.policy-table-detail) {
602 .dialog_content {
603 padding: 0px 20px 20px;
604 }
605 }
606
607 .h-title {
608 font-size: 14px;
609 color: #212121;
610 font-weight: 600;
611 margin-right: 8px;
612 margin-bottom: 8px;
613 }
614 </style>
...\ No newline at end of file ...\ No newline at end of file
1 <route lang="yaml">
2 name: contractRecordManage
3 </route>
4
5 <script lang="ts" setup name="contractRecordManage">
6 import { ref } from 'vue';
7 import TableTools from "@/components/Tools/table_tools.vue";
8 import {
9 getContractOverviewPageList,
10 getContractOverviewTenantList
11 } from "@/api/modules/dataSmartContract"
12 import useDataSmartContract from "@/store/modules/dataSmartContract";
13 import { commonPageConfig } from '@/utils/enum';
14
15 const userData = JSON.parse(localStorage.userData);
16 const router = useRouter();
17 const route = useRoute();
18 const { proxy } = getCurrentInstance() as any;
19 const dataSmartContractStore = useDataSmartContract();
20
21 /** 发起主体下拉列表数据 */
22 const initiatorListData: any = ref([]);
23
24 const searchItemList = ref([
25 {
26 type: "input",
27 label: "",
28 field: "contractName",
29 default: "",
30 placeholder: "合约名称",
31 maxlength: 50,
32 clearable: true,
33 },
34 {
35 type: 'select',
36 label: '',
37 field: 'initiatorGuid',
38 default: '',
39 placeholder: '发起主体',
40 props: {
41 value: 'tenantGuid',
42 label: 'tenantName'
43 },
44 options: initiatorListData.value,
45 filterable: true,
46 clearable: true
47 },
48 {
49 type: 'date-time',
50 label: '',
51 field: 'signatureTime',
52 default: [],
53 defaultStartTime: new Date(2000, 1, 1, 0, 0, 0),
54 defaultEndTime: new Date(2000, 1, 1, 23, 59, 59),
55 startPlaceholder: '签署开始时间',
56 endPlaceholder: '签署结束时间',
57 clearable: true
58 },
59 ]);
60
61 const tableFields = ref([
62 { label: "序号", type: "index", width: 56, align: "center" },
63 { label: "合约名称", field: "contractName", width: 160, },
64 { label: "签署方式", field: "signModeName", width: 120 },
65 { label: "产品名称", field: "productName", width: 180 },
66 { label: "合约编号", field: "contractId", width: 355 },
67 { label: "发起主体", field: "tenantName", width: 205 },
68 { label: "签署时间", field: "signatureTime", width: 170 }
69 ]);
70
71 const page = ref({
72 ...commonPageConfig,
73 contractName: '',
74 initiatorGuid: '',
75 signatureTime: [],
76 });
77
78 const currTableData: any = ref({});
79 const tableInfo = ref({
80 id: 'contract-table',
81 rowKey: 'guid',
82 loading: false,
83 fields: tableFields.value,
84 data: [],
85 page: {
86 type: "normal",
87 rows: 0,
88 ...page.value,
89 },
90 actionInfo: {
91 label: "操作",
92 type: "btn",
93 width: 80,
94 btns: (scope) => {
95 let row = scope.row;
96 let btns: any = [];
97 btns.push({
98 value: 'view', label: '查看', click: () => {
99 if (scope.row.contractStatus == '05') {
100 router.push({
101 name: 'smartContractDetail',
102 query: {
103 guid: scope.row.guid,
104 name: scope.row.contractName,
105 type: 'keepAgree',
106 isDetail: 'Y'
107 }
108 });
109 } else {
110 router.push({
111 name: 'smartContractDetail',
112 query: {
113 guid: scope.row.guid,
114 name: scope.row.contractName,
115 isDetail: 'Y'
116 }
117 });
118 }
119 }
120 });
121 return btns;
122 }
123 }
124 });
125
126 const toSearch = (val: any, clear: boolean = false) => {
127 if (clear) {
128 searchItemList.value.map((item) => (item.default = ""));
129 page.value.contractName = '';
130 page.value.initiatorGuid = '';
131 page.value.signatureTime = [];
132 } else {
133 page.value.contractName = val.contractName;
134 page.value.initiatorGuid = val.initiatorGuid;
135 page.value.signatureTime = val.signatureTime;
136 }
137 getTableData();
138 };
139
140 const getTableData = () => {
141 getContractOverviewTenantList().then((res: any) => {
142 if (res?.code == proxy.$passCode) {
143 initiatorListData.value = res.data || [];
144 searchItemList.value[1].options = initiatorListData.value;
145 } else {
146 res?.msg && proxy.$ElMessage.error(res?.msg)
147 }
148 })
149 tableInfo.value.loading = true
150 getContractOverviewPageList({
151 pageIndex: page.value.curr,
152 pageSize: page.value.limit,
153 contractName: page.value.contractName,
154 initiatorGuid: page.value.initiatorGuid,
155 signatureStartTime: page.value.signatureTime?.[0],
156 signatureEndTime: page.value.signatureTime?.[1]
157 }).then((res: any) => {
158 tableInfo.value.data = [];
159 if (res?.code == proxy.$passCode) {
160 const data = res.data || {};
161 tableInfo.value.loading = false
162 tableInfo.value.data = data.records || []
163 tableInfo.value.page.limit = data.pageSize
164 tableInfo.value.page.curr = data.pageIndex
165 tableInfo.value.page.rows = data.totalRows
166 } else {
167 res?.msg && proxy.$ElMessage.error(res?.msg)
168 tableInfo.value.loading = false
169 }
170 }).catch(() => {
171 tableInfo.value.loading = false
172 })
173 };
174
175 const tablePageChange = (info) => {
176 page.value.curr = Number(info.curr);
177 page.value.limit = Number(info.limit);
178 tableInfo.value.page.curr = page.value.curr;
179 tableInfo.value.page.limit = page.value.limit;
180 getTableData();
181 };
182
183 onActivated(() => {
184 if (dataSmartContractStore.isRefresh) {//如果是首次加载,则不需要调用
185 page.value.curr = 1;
186 getTableData();
187 dataSmartContractStore.set(false);
188 }
189 })
190
191 onBeforeMount(() => {
192 !dataSmartContractStore.isRefresh && toSearch({})
193 })
194
195 </script>
196
197 <template>
198 <div class="container_wrap">
199 <div class="table_tool_wrap">
200 <TableTools :searchItems="searchItemList" :searchId="'contract-search'" @search="toSearch" :init="false" />
201 </div>
202 <div class="table_panel_wrap">
203 <Table :tableInfo="tableInfo" @tablePageChange="tablePageChange" />
204 </div>
205 </div>
206 </template>
207
208 <style lang="scss" scoped>
209 .container_wrap {
210 padding: 0px 16px;
211
212 .table_panel_wrap {
213 height: calc(100% - 44px);
214 }
215 }
216
217 :deep(.el-tag.el-tag--primary) {
218 color: #0E5FD8;
219 background: #F2F9FF;
220 border: 1px solid rgba(224, 239, 255, 1);
221 }
222 </style>
...\ No newline at end of file ...\ No newline at end of file
1 <route lang="yaml">
2 name: contractTemplateCreate
3 </route>
4
5 <script lang="ts" setup name="contractTemplateCreate">
6 import { useValidator } from "@/hooks/useValidator";
7 import useUserStore from "@/store/modules/user";
8 import useDataSmartContract from "@/store/modules/dataSmartContract";
9 import {
10 getContractTemplateDetail,
11 getActionPolicyList,
12 getConstraintPolicyList,
13 updateContractTemplate,
14 saveContractTemplate,
15 } from "@/api/modules/dataSmartContract"
16 import {
17 getParamsList
18 } from "@/api/modules/queryService";
19 import StrategyTable from "./components/strategyTable.vue";
20
21 const { required } = useValidator();
22 const router = useRouter();
23 const route = useRoute();
24 const { proxy } = getCurrentInstance() as any;
25 const userStore = useUserStore();
26 const dataSmartContract = useDataSmartContract();
27 const fullPath = route.fullPath;
28 const fullscreenLoading = ref(false);
29 const expandBase = ref(false);
30 const expandInfo = ref(false);
31
32 const baseInfoFormRef = ref();
33 const baseInfoFormItems = ref([{
34 type: 'input',
35 label: '合约模板名称',
36 field: 'templateName',
37 default: '',
38 placeholder: '请输入',
39 maxlength: 50,
40 clearable: true,
41 required: true
42 }, {
43 type: 'input',
44 label: '版本',
45 field: 'version',
46 default: 'v1.0.0',
47 placeholder: '-',
48 disabled: true,
49 clearable: true,
50 required: false
51 }, {
52 type: 'input',
53 label: '合约模板编号',
54 field: 'templateId',
55 default: '',
56 placeholder: '自动生成',
57 disabled: true,
58 clearable: true,
59 required: false
60 }, {
61 label: '启用状态',
62 type: 'switch',
63 field: 'bizStatus',
64 default: 'Y',
65 placeholder: '请选择',
66 activeValue: 'Y',
67 inactiveValue: 'N',
68 switchWidth: 32,
69 }, {
70 label: '模板描述',
71 type: 'textarea',
72 placeholder: '请输入',
73 field: 'description',
74 default: '',
75 block: true,
76 maxlength: 500,
77 clearable: true,
78 required: false,
79 }]);
80
81 const baseInfoFormRules = ref({
82 templateName: [required("请填写合约模板名称")],
83 });
84
85 const strategyTableRef = ref();
86
87 const cancel = () => {
88 proxy.$openMessageBox(
89 "当前页面尚未保存,确定放弃修改吗?",
90 () => {
91 userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath));
92 router.push({
93 name: "contractTemplateManagement",
94 });
95 },
96 () => {
97 proxy.$ElMessage.info("已取消");
98 }
99 );
100 }
101
102 const submit = () => {
103 baseInfoFormRef.value.ruleFormRef?.validate((valid, errorItem) => {
104 if (valid) {
105 let validate = strategyTableRef.value?.validateValue();
106 if (!validate) {
107 expandInfo.value = true;
108 return;
109 }
110 let params = { ...baseInfoFormRef.value.formInline };
111 params.policyRQVOS = strategyTableRef.value.strategyData?.map((d, index) => {
112 return Object.assign({}, d, { orderNum: index + 1 })
113 });
114 if (route.query.guid) {
115 params.guid = route.query.guid;
116 fullscreenLoading.value = true;
117 updateContractTemplate(params).then((res: any) => {
118 fullscreenLoading.value = false;
119 if (res?.code == proxy.$passCode) {
120 proxy.$ElMessage.success('编辑合约模板成功');
121 userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath));
122 router.push({
123 name: "contractTemplateManagement",
124 });
125 dataSmartContract.set(true);
126 } else {
127 res?.msg && proxy.$ElMessage.error(res?.msg);
128 }
129 }).catch(() => {
130 fullscreenLoading.value = false;
131 });
132 } else {
133 fullscreenLoading.value = true;
134 delete params.version;
135 saveContractTemplate(params).then((res: any) => {
136 fullscreenLoading.value = false;
137 if (res?.code == proxy.$passCode) {
138 proxy.$ElMessage.success('新建合约模板成功');
139 userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath));
140 router.push({
141 name: "contractTemplateManagement",
142 });
143 dataSmartContract.set(true);
144 } else {
145 res?.msg && proxy.$ElMessage.error(res?.msg);
146 }
147 }).catch(() => {
148 fullscreenLoading.value = false;
149 });
150 }
151 } else {
152 expandBase.value = true;
153 var obj = Object.keys(errorItem);
154 baseInfoFormRef.value.ruleFormRef?.scrollToField(obj[0]);
155 }
156 })
157 }
158
159 const detailInfo: any = ref({});
160 /** 约束运算符字典下拉 */
161 const operatorOptionList: any = ref([]);
162
163 /** 约束行为下拉列表 */
164 const constraintOptionsList: any = ref([]);
165
166 /** 策略操作行为下拉列表 */
167 const actionOptionsList: any = ref([]);
168
169 onBeforeMount(() => {
170 if (route.query.guid) {
171 fullscreenLoading.value = true;
172 getContractTemplateDetail({ guid: route.query.guid }).then((res: any) => {
173 fullscreenLoading.value = false;
174 if (res?.code == proxy.$passCode) {
175 detailInfo.value = res.data || {};
176 baseInfoFormItems.value.forEach(item => {
177 item.default = detailInfo.value[item.field] || "";
178 });
179 } else {
180 res?.msg && proxy.$ElMessage.error(res?.msg);
181 }
182 });
183 }
184 getParamsList({ dictType: '约束运算符' }).then((res: any) => {
185 if (res?.code == proxy.$passCode) {
186 const data = res.data || [];
187 operatorOptionList.value = data;
188 } else {
189 res?.msg && proxy.$ElMessage.error(res?.msg);
190 }
191 })
192 getActionPolicyList().then((res: any) => {
193 if (res?.code == proxy.$passCode) {
194 const data = res.data || [];
195 actionOptionsList.value = data;
196 } else {
197 res?.msg && proxy.$ElMessage.error(res?.msg);
198 }
199 })
200 getConstraintPolicyList().then((res: any) => {
201 if (res?.code == proxy.$passCode) {
202 const data = res.data || [];
203 constraintOptionsList.value = data;
204 } else {
205 res?.msg && proxy.$ElMessage.error(res?.msg);
206 }
207 })
208 })
209
210 </script>
211
212 <template>
213 <div class="container_wrap full" v-loading="fullscreenLoading">
214 <div class="content_main panel">
215 <ContentWrap title="模板信息" expandSwicth style="margin-top: 15px" :isExpand="expandBase"
216 @expand="(v) => (expandBase = v)" description="">
217 <Form ref="baseInfoFormRef" formId="base-info-form" :itemList="baseInfoFormItems" :rules="baseInfoFormRules"
218 col="col3" />
219 </ContentWrap>
220 <ContentWrap title="策略信息" expandSwicth style="margin-top: 15px" :isExpand="expandInfo"
221 @expand="(v) => (expandInfo = v)" description="">
222 <StrategyTable ref="strategyTableRef" :value="detailInfo.policyRSVOS || (route.query.guid ? [] : [{
223 index: 1,
224 action: '',
225 children: [{ childIndex: 1 }]
226 }])" :operatorOptionList="operatorOptionList" :actionOptionsList="actionOptionsList"
227 :constraintOptionsList="constraintOptionsList"></StrategyTable>
228 </ContentWrap>
229 </div>
230 <div class="tool_btns">
231 <div class="btns">
232 <el-button @click="cancel">取消</el-button>
233 <el-button type="primary" @click="submit">确定</el-button>
234 </div>
235 </div>
236 </div>
237 </template>
238
239 <style lang="scss" scoped>
240 .container_wrap {
241 overflow: hidden;
242
243 .content_main {
244 height: calc(100% - 45px);
245 overflow: hidden auto;
246
247 &.panel {
248 padding: 0 16px 16px;
249 }
250 }
251 }
252
253 .tool_btns {
254 height: 44px;
255 margin: 0 -8px;
256 display: flex;
257 justify-content: center;
258 align-items: center;
259 border-top: 1px solid #d9d9d9;
260 }
261
262 :deep(.strategyTable.el-table) {
263 .cell {
264 padding: 0px 10px;
265 }
266
267 & td.el-table__cell {
268 padding: 2px 0px;
269 height: 36px;
270 }
271 }
272 </style>
1 <route lang="yaml">
2 name: contractTemplateManagement
3 </route>
4
5 <script lang="ts" setup name="contractTemplateManagement">
6 import { ref } from 'vue';
7 import TableTools from "@/components/Tools/table_tools.vue";
8 import {
9 getContractTemplatePageList,
10 copyContractTemplate,
11 deleteContractTemplate,
12 updateContractTemplateState
13 } from "@/api/modules/dataSmartContract"
14 import useDataSmartContract from "@/store/modules/dataSmartContract";
15 import { commonPageConfig } from '@/utils/enum';
16 import { useValidator } from "@/hooks/useValidator";
17
18 const router = useRouter();
19 const route = useRoute();
20 const { proxy } = getCurrentInstance() as any;
21 const dataSmartContractStore = useDataSmartContract();
22 const { required } = useValidator();
23
24 const searchItemList = ref([
25 {
26 type: "input",
27 label: "",
28 field: "templateName",
29 default: "",
30 placeholder: "合约模板名称",
31 maxlength: 50,
32 clearable: true,
33 },
34 {
35 type: 'select',
36 label: '',
37 field: 'bizStatus',
38 default: '',
39 placeholder: '启用状态',
40 options: [
41 { label: '启用', value: 'Y' },
42 { label: '停用', value: 'N' },
43 ],
44 filterable: true,
45 clearable: true
46 }
47 ]);
48
49 const tableFields = ref([
50 { label: "序号", type: "index", width: 56, align: "center" },
51 { label: "合约模板名称", field: "templateName", width: 220, },
52 { label: "版本号", field: "version", width: 100 },
53 { label: '启用状态', width: 96, field: 'bizStatus', type: 'switch', activeText: '启用', inactiveText: '停用', activeValue: 'Y', inactiveValue: 'N', align: 'center' },
54 { label: "合约模板编号", field: "templateId", width: 190 },
55 { label: "修改人", field: "updateUserName", width: 130 },
56 { label: "修改时间", field: "updateTime", width: 170 },
57 ]);
58
59 const page = ref({
60 ...commonPageConfig,
61 templateName: '',
62 bizStatus: ''
63 });
64 const currTableData: any = ref({});
65 const tableInfo = ref({
66 id: 'contract-template-table',
67 rowKey: 'guid',
68 loading: false,
69 fields: tableFields.value,
70 data: [],
71 page: {
72 type: "normal",
73 rows: 0,
74 ...page.value,
75 },
76 actionInfo: {
77 label: "操作",
78 type: "btn",
79 width: 140,
80 btns: (scope) => {
81 let row = scope.row;
82 return [{
83 value: 'edit', label: '编辑', click: (scope) => {
84 router.push({
85 name: 'contractTemplateCreate',
86 query: {
87 guid: scope.row.guid,
88 name: scope.row.templateName
89 }
90 });
91 }
92 }, {
93 value: 'copy', label: '复制', click: copyTemplate
94 }, {
95 value: 'delete', label: '删除', click: (scope) => {
96 proxy.$openMessageBox("此操作将永久删除, 是否继续?", () => {
97 deleteContractTemplate([scope.row.guid]).then((res: any) => {
98 if (res?.code == proxy.$passCode) {
99 page.value.curr = 1;
100 getTableData();
101 proxy.$ElMessage.success('删除成功');
102 } else {
103 res?.msg && proxy.$ElMessage.error(res?.msg);
104 }
105 });
106 }, () => {
107 proxy.$ElMessage.info("已取消");
108 })
109 }
110 }]
111 }
112 }
113 });
114
115 const tableSwitchBeforeChange = (scope, field, callback) => {
116 const msg = `确定${scope.row[field] == 'Y' ? '停用' : '启用'}${scope.row.templateName}】?`
117 proxy.$openMessageBox(msg, () => {
118 const state = scope.row[field] == 'Y' ? 'N' : 'Y'
119 const result = tableSwitchChange(state, scope, field)
120 callback(result)
121 }, () => {
122 callback(false)
123 });
124 }
125
126 const tableSwitchChange = (val, scope, field) => {
127 return new Promise((resolve, reject) => {
128 let params = {
129 guid: scope.row.guid,
130 bizStatus: val
131 }
132 updateContractTemplateState(params).then((res: any) => {
133 if (res?.code == proxy.$passCode && res.data) {
134 getTableData();
135 proxy.$ElMessage({
136 type: "success",
137 message: `【${scope.row.templateName}${val == 'Y' ? '启用' : '停用'}成功`,
138 });
139 resolve(true)
140 } else {
141 res?.msg && proxy.$ElMessage.error(res?.msg)
142 reject(false)
143 }
144 }).catch(() => {
145 reject(false)
146 })
147 })
148 }
149
150
151 const copyTemplate = (scope) => {
152 currTableData.value = scope.row;
153 copyFormItems.value[0].default = scope.row.templateName + '_copy';
154 copyDialogInfo.value.formInfo.items = copyFormItems.value;
155 copyDialogInfo.value.visible = true;
156 }
157
158 const toSearch = (val: any, clear: boolean = false) => {
159 if (clear) {
160 searchItemList.value.map((item) => (item.default = ""));
161 page.value.templateName = '';
162 page.value.bizStatus = "";
163 } else {
164 page.value.templateName = val.templateName;
165 page.value.bizStatus = val.bizStatus;
166 }
167 getTableData();
168 };
169
170 const getTableData = () => {
171 tableInfo.value.loading = true
172 getContractTemplatePageList({
173 pageIndex: page.value.curr,
174 pageSize: page.value.limit,
175 templateName: page.value.templateName,
176 bizStatus: page.value.bizStatus
177 }).then((res: any) => {
178 tableInfo.value.loading = false
179 if (res?.code == proxy.$passCode) {
180 const data = res.data || {}
181 tableInfo.value.data = data.records || []
182 tableInfo.value.page.limit = data.pageSize
183 tableInfo.value.page.curr = data.pageIndex
184 tableInfo.value.page.rows = data.totalRows
185 } else {
186 res?.msg && proxy.$ElMessage.error(res?.msg)
187 }
188 }).catch(() => {
189 tableInfo.value.loading = false
190 })
191 };
192
193 const tablePageChange = (info) => {
194 page.value.curr = Number(info.curr);
195 page.value.limit = Number(info.limit);
196 tableInfo.value.page.curr = page.value.curr;
197 tableInfo.value.page.limit = page.value.limit;
198 getTableData();
199 };
200
201 const newCreate = () => {
202 router.push({
203 name: 'contractTemplateCreate'
204 });
205 }
206
207 onActivated(() => {
208 if (dataSmartContractStore.isRefresh) {//如果是首次加载,则不需要调用
209 page.value.curr = 1;
210 getTableData();
211 dataSmartContractStore.set(false);
212 }
213 })
214
215 onBeforeMount(() => {
216 !dataSmartContractStore.isRefresh && toSearch({})
217 })
218
219 /** -------------- 复制功能 ------------------- */
220 const copyFormItems = ref([{
221 type: 'input',
222 label: '合约模板名称',
223 field: 'templateName',
224 default: '',
225 block: true,
226 placeholder: '请输入',
227 maxlength: 50,
228 clearable: true,
229 required: true
230 }]);
231
232 const copyFormRules = ref({
233 templateName: [required("请填写合约模板名称")],
234 });
235
236 const copyDialogInfo = ref({
237 visible: false,
238 size: 480,
239 title: "复制合约模板",
240 type: 'add',
241 formInfo: {
242 id: 'copy-form',
243 items: copyFormItems.value,
244 rules: copyFormRules.value
245 },
246 btns: {
247 submit: (btn, info) => {
248 info.guid = currTableData.value.guid;
249 copyDialogInfo.value.submitBtnLoading = true;
250 copyContractTemplate(info).then((res: any) => {
251 copyDialogInfo.value.submitBtnLoading = false;
252 if (res.code == proxy.$passCode) {
253 page.value.curr = 1;
254 getTableData();
255 proxy.$ElMessage({
256 type: 'success',
257 message: `【${info.templateName}】复制成功`
258 })
259 copyDialogInfo.value.visible = false;
260 } else {
261 proxy.$ElMessage.error(res.msg);
262 }
263 })
264 },
265 cancel: () => {
266 copyDialogInfo.value.visible = false;
267 }
268 },
269 submitBtnLoading: false
270 });
271
272
273 </script>
274
275 <template>
276 <div class="container_wrap">
277 <div class="table_tool_wrap">
278 <TableTools :searchItems="searchItemList" :searchId="'settle-asset-search'" @search="toSearch" :init="false" />
279 <div class="tools_btns">
280 <el-button type="primary" @click="newCreate">新增</el-button>
281 </div>
282 </div>
283 <div class="table_panel_wrap">
284 <Table :tableInfo="tableInfo" @tablePageChange="tablePageChange"
285 @tableSwitchBeforeChange="tableSwitchBeforeChange" />
286 </div>
287 <!-- 复制对话框 -->
288 <Dialog_form :dialogConfigInfo="copyDialogInfo" />
289 </div>
290 </template>
291
292 <style lang="scss" scoped>
293 .container_wrap {
294 padding: 0px 16px;
295 }
296 </style>
...\ No newline at end of file ...\ No newline at end of file
1 <route lang="yaml">
2 name: execCntIndex
3 </route>
4
5 <script lang="ts" setup name="execCntIndex">
6 import { changeNum } from '@/utils/common'
7 import {
8 getContractStatis,
9 getContractMonthStatis,
10 getLogTableList
11 } from "@/api/modules/dataSmartContract";
12 import * as echarts from 'echarts';
13 import { commonPageConfig } from '@/utils/enum';
14
15 const { proxy } = getCurrentInstance() as any;
16 const detailInfo: any = ref({});
17
18 const logTableInfo: any = ref({
19 id: "plan-detail-table",
20 loading: false,
21 // height: 'auto',
22 // minPanelHeight: '60px',
23 // minHeight: '60px',
24 // maxHeight: '250px',
25 fields: [
26 { label: "序号", type: "index", width: 56, align: "center", fixed: true },
27 { label: "合约标识", field: "contractId", width: 355 },
28 { label: "合约名称", field: "contractName", width: 140 },
29 { label: "执行策略id", field: "strategyId", width: 260 },
30 { label: "执行时间", field: "operationTime", width: 170 },
31 { label: "执行节点标识", field: "executionEntityId", width: 180 },
32 { label: "上报时间", field: "operationTime", width: 170 },
33 { label: "异常类型", field: "executionResult", width: 120 },
34 ],
35 data: [],
36 page: {
37 type: "normal",
38 rows: 0,
39 ...commonPageConfig,
40 },
41 actionInfo: {
42 show: false
43 }
44 });
45
46 const pageChange = (info) => {
47 logTableInfo.value.page.curr = Number(info.curr);
48 logTableInfo.value.page.limit = Number(info.limit);
49 getLogTableListData(true);
50 };
51
52 const getLogTableListData = (isPage = false) => {
53 isPage && (logTableInfo.value.loading = true);
54 return getLogTableList({
55 pageIndex: logTableInfo.value.page.curr,
56 pageSize: logTableInfo.value.page.limit,
57 }).then((res: any) => {
58 logTableInfo.value.loading = false;
59 if (res?.code == proxy.$passCode) {
60 const data = res.data || {};
61 logTableInfo.value.data = data.records || []
62 logTableInfo.value.page.limit = data.pageSize
63 logTableInfo.value.page.curr = data.pageIndex
64 logTableInfo.value.page.rows = data.totalRows
65 } else {
66 res?.msg && proxy.$ElMessage.error(res?.msg)
67 }
68 })
69 }
70
71 let barChart: any = null;
72
73 const barChartData: any = ref([]);
74
75 onMounted(() => {
76 barChart = echarts.init(document.getElementById('bar'));
77 setBarChartOption(barChart);
78 })
79
80 watch(() => barChartData.value, (val) => {
81 setBarChartOption(barChart);
82 }, {
83 deep: true
84 })
85
86 /** 设置柱形图option,默认只 展示12次的 */
87 const setBarChartOption = (barChart) => {
88 return new Promise((resolve, reject) => {
89 if (!barChartData.value || !Object.keys(barChartData.value).length) {
90 let option1 = {
91 title: [
92 {
93 text: "",
94 left: "30px",
95 top: "30px",
96 },
97 {
98 text: "暂无数据",
99 left: "center",
100 top: "center",
101 textStyle: {
102 fontFamily: 'SimSun',
103 fontStyle: "normal",
104 fontWeight: "400",
105 fontSize: 18,
106 color: '#999'
107 },
108 },
109 ],
110 };
111 barChart.setOption(option1, true);
112 window.addEventListener("resize", () => {
113 barChart.resize();
114 });
115 return;
116 }
117 let getLast12Months = () => {
118 const months: any = [];
119 const currentDate = new Date();
120
121 for (let i = 11; i >= 0; i--) {
122 const targetDate = new Date(currentDate);
123 targetDate.setMonth(targetDate.getMonth() - i);
124
125 const year = targetDate.getFullYear();
126 const month = String(targetDate.getMonth() + 1).padStart(2, '0');
127
128 months.push(`${year}-${month}`);
129 }
130
131 return months;
132 }
133
134 // 使用示例
135 const last12Months = getLast12Months();
136
137 let itemXAxisData: any = last12Months;
138 let itemYAxisData: any = last12Months.map(v => {
139 return barChartData.value[v] || 0;
140 });
141 let option = {
142 textStyle: {
143 fontFamily: 'SimSun'
144 },
145 color: ['#5B8FF9', '#FF4E00', '#867EEC', '#FDBC3E', '#F48A64', '#276FF5', '#46D0B5'],
146 title: {
147 show: false,
148 left: '30px',
149 top: '30px',
150 },
151 tooltip: {
152 trigger: 'axis',
153 axisPointer: {
154 type: 'cross',
155 crossStyle: {
156 color: '#999'
157 }
158 },
159 textStyle: {
160 align: 'left'
161 },
162 },
163 legend: {
164 show: false,
165 right: 0,
166 textStyle: {
167 fontSize: 14
168 },
169 },
170 grid: {
171 left: 30,
172 right: 60,
173 bottom: 5,
174 containLabel: true
175 },
176 xAxis: {
177 type: 'category',
178 boundaryGap: false,
179 nameTextStyle: {
180 color: '#000000'
181 },
182 axisLabel: {
183 interval: 'auto',
184 textStyle: {
185 color: '#000000'
186 },
187 },
188 axisTick: {
189 show: false,
190 },
191 axisLine: {
192 lineStyle: {
193 color: '#d9d9d9'
194 }
195 },
196 data: itemXAxisData
197 },
198 yAxis: {
199 type: 'value',
200 name: '',
201 min: 0,
202 minInterval: 1,
203 nameTextStyle: {
204 color: '#000000'
205 },
206 axisLabel: {
207 textStyle: {
208 color: '#000000'
209 }
210 },
211 axisLine: {
212 //y轴
213 show: false
214 }
215 },
216 series: [{
217 name: '已签署合约数',
218 type: 'line',
219 data: itemYAxisData,
220 label: {
221 show: false,
222 },
223 tooltip: {
224 valueFormatter: function (value) {
225 return changeNum(value, 0);
226 }
227 },
228 yAxisIndex: 0
229 }]
230 };
231 option && barChart.setOption(option, true);
232 barChart.on('finished', () => {
233 resolve(true);
234 });
235 window.addEventListener('resize', () => {
236 barChart.resize();
237 });
238 });
239 }
240
241 const fullscreenloading = ref(false);
242
243 onBeforeMount(() => {
244 fullscreenloading.value = true;
245 let ps1 = getContractStatis().then((res: any) => {
246 if (res?.code == proxy.$passCode) {
247 detailInfo.value = res.data || {};
248 } else {
249 res?.msg && proxy.$ElMessage.error(res?.msg)
250 }
251 })
252 let ps2 = getContractMonthStatis().then((res: any) => {
253 if (res?.code == proxy.$passCode) {
254 barChartData.value = res.data || {};
255 } else {
256 res?.msg && proxy.$ElMessage.error(res?.msg)
257 }
258 })
259 let ps3 = getLogTableListData();
260 Promise.all([ps1, ps2, ps3]).then(() => {
261 fullscreenloading.value = false;
262 }).catch(() => {
263 fullscreenloading.value = false;
264 })
265 })
266
267 </script>
268
269 <template>
270 <div class="content-main" v-loading="fullscreenloading">
271 <div class="title">合约备案执行统计</div>
272 <div class="kpi-content">
273 <div class="border-content">
274 <span class="text">备案合约数</span>
275 <span class="number num-color">{{ detailInfo.registerNum != null ? changeNum(detailInfo.registerNum ?? 0) : '--'
276 }}<span class="unit" v-show="detailInfo.registerNum != null"></span></span>
277 </div>
278 <div class="border-content ml16">
279 <span class="text">备案策略数</span>
280 <span class="number num-color">{{ detailInfo.registerPolicyNum != null ? changeNum(detailInfo.registerPolicyNum
281 ?? 0) : '--'
282 }}<span class="unit" v-show="detailInfo.registerPolicyNum != null"></span></span>
283 </div>
284 <div class="border-content ml16">
285 <span class="text">策略执行数</span>
286 <span class="number num-color">{{ detailInfo.registerPolicyExecutionNum != null ?
287 (changeNum(detailInfo.registerPolicyExecutionNum ?? 0)) :
288 '--'
289 }}<span class="unit" v-show="detailInfo.registerPolicyExecutionNum != null"></span></span>
290 </div>
291 <div class="border-content ml16">
292 <span class="text">签署合约主体数</span>
293 <span class="number num-color">{{ detailInfo.registerSignatureNum != null ?
294 changeNum(detailInfo.registerSignatureNum ?? 0) : '--'
295 }}<span class="unit" v-show="detailInfo.registerSignatureNum != null"></span></span>
296 </div>
297 <div class="border-content ml16">
298 <span class="text">策略执行节点数</span>
299 <span class="number num-color">{{ detailInfo.registerExecutionNodeNum != null ?
300 changeNum(detailInfo.registerExecutionNodeNum ?? 0) : '--'
301 }}<span class="unit" v-show="detailInfo.registerExecutionNodeNum != null"></span></span>
302 </div>
303 </div>
304 <div class="title">合约履行异常预警记录</div>
305 <Table class="table-h" :tableInfo="logTableInfo" @tablePageChange="pageChange" />
306 <div class="title">近12个月备案合约趋势</div>
307 <div class="content-chart-bar" id="bar">
308 </div>
309 </div>
310 </template>
311
312 <style lang="scss" scoped>
313 .content-main {
314 height: 100%;
315 overflow-y: auto;
316 overflow-x: hidden;
317 padding: 0px 16px 16px;
318 }
319
320 .title {
321 line-height: 24px;
322 font-size: 16px;
323 color: #212121;
324 font-weight: 600;
325 margin-top: 12px;
326 margin-bottom: 8px;
327 }
328
329 .kpi-content {
330 display: flex;
331 flex-direction: row;
332 }
333
334 .border-content {
335 height: 76px;
336 display: flex;
337 flex-direction: column;
338 align-items: flex-start;
339 padding-left: 16px;
340 justify-content: center;
341 border: 1px solid #d9d9d9;
342 //width: 160px;
343 flex: 1;
344 min-width: 150px;
345 border-radius: 2px;
346
347 .number {
348 line-height: 30px;
349 font-weight: 700;
350 font-size: 20px;
351
352 .unit {
353 font-size: 18px;
354 font-weight: 400;
355 }
356 }
357
358 .num-color {
359 color: #212121;
360 }
361
362 .text {
363 font-size: 14px;
364 color: #666666;
365 display: flex;
366
367 .el-icon {
368 color: #b2b2b2;
369 }
370 }
371 }
372
373 .ml16 {
374 margin-left: 16px;
375 }
376
377 .content-chart-bar {
378 height: 300px;
379 border: 1px solid #d9d9d9;
380 padding: 8px;
381 }
382
383 .table-h {
384 height: 300px !important;
385 }
386 </style>
...\ No newline at end of file ...\ No newline at end of file
1 <route lang="yaml">
2 name: smartContractCreate
3 </route>
4
5 <script lang="ts" setup name="smartContractCreate">
6 import { useValidator } from "@/hooks/useValidator";
7 import useUserStore from "@/store/modules/user";
8 import {
9 getContractDetail,
10 getActionPolicyList,
11 getConstraintPolicyList,
12 getContractDataProduct,
13 getContractTenantList,
14 saveContract,
15 updateContract,
16 } from "@/api/modules/dataSmartContract"
17 import StrategyTable from "./components/strategyTable.vue";
18 import { CirclePlus } from "@element-plus/icons-vue";
19 import { scrollLastRowToView } from "@/utils/common";
20 import { TableColumnWidth } from "@/utils/enum";
21 import {
22 getParamsList,
23 } from "@/api/modules/queryService";
24 import useDataSmartContract from "@/store/modules/dataSmartContract";
25 import { getEnterpriseData } from "@/api/modules/dataIdentify";
26
27 const { required } = useValidator();
28 const userStore = useUserStore();
29 const userData = JSON.parse(localStorage.userData);
30 const tenantData = JSON.parse(localStorage.tenantInfo);
31 const router = useRouter();
32 const route = useRoute();
33 const fullscreenLoading = ref(false);
34 const expandBase = ref(false);
35 const expandInfo = ref(false);
36 const expandPolicy = ref(false);
37 const fullPath = route.fullPath;
38 const dataSmartContract = useDataSmartContract();
39 const { proxy } = getCurrentInstance() as any;
40
41 const signModeList = ref([]);
42
43 const baseInfoFormRef = ref();
44
45 const baseInfoFormItems = ref([{
46 label: '合约名称',
47 type: 'input',
48 placeholder: '请输入',
49 field: 'contractName',
50 default: '',
51 maxlength: 50,
52 clearable: true,
53 required: true
54 }, {
55 label: '签署模式',
56 type: 'select',
57 placeholder: '请选择',
58 field: 'signModeCode',
59 default: '01',
60 options: signModeList.value,
61 props: {
62 value: 'value',
63 label: 'label'
64 },
65 disabled: false,
66 required: true,
67 visible: true
68 }, {
69 label: "终止时间",
70 type: "datetime",
71 field: "endTime",
72 defaultTime: new Date(2000, 1, 1, 23, 59, 59),
73 default: null,
74 placeholder: "请选择",
75 clearable: true,
76 disabledDate: (date) => {
77 const today = new Date();
78 // 将 today 设置为 00:00:00,只比较日期部分(本地时间)
79 const todayStart = new Date(
80 today.getFullYear(),
81 today.getMonth(),
82 today.getDate()
83 );
84
85 // 将 date 也转换为本地日期的开始时间(避免 UTC 问题)
86 const dateStart = new Date(
87 date.getFullYear(),
88 date.getMonth(),
89 date.getDate()
90 );
91
92 // 禁用 dateStart < todayStart 的日期(即今天之前)
93 return dateStart.getTime() < todayStart.getTime();
94 },
95 required: true,
96 }, {
97 label: '合约简介',
98 type: 'textarea',
99 focusValue: false,
100 placeholder: '请输入',
101 field: 'contractAbstract',
102 default: '',
103 clearable: true,
104 required: false,
105 block: true,
106 maxlength: 500
107 }]);
108
109 const baseInfoFormRules = ref({
110 contractName: [required('请填写合约名称')],
111 signModeCode: [required('请选择签署模式')],
112 endTime: [required('请选择终止时间')],
113 });
114
115 const handleFormSelectChange = (val, row, info) => {
116 if (row.field == 'signModeCode') { //签署模式改变会影响下面的合约策略执行者的显示
117 let nodeInfo = nodeInfoFormRef.value.formInline;
118 nodeInfoFormItems.value.forEach((item, index) => {
119 item.default = nodeInfo[item.field];
120 if (index == 1) {
121 item.default = val == '01' ? userData.tenantGuid : '';
122 } else if (index == 2) {
123 item.default = val == '01' ? tenantList.value.find(t => t.guid == userData.tenantGuid)?.socialCreditCode : '';
124 }
125 })
126 productList.value = productList.value?.map(d => {
127 d.disabled = val == '01' && d.enterpriseName != userData.tenantName; //点对点,只能选择自己公司所属产品
128 return d;
129 }) || [];
130 productTableInfo.value.editInfo.dataProductId.options = productList.value;
131 }
132 }
133
134 /** ----------------- 扩展字段相关信息 ----------------------------- */
135 const extendTableRef = ref();
136 const extendData: any = ref([]);
137
138 /** 合约扩展字段类型 */
139 const dataTypeList: any = ref([]);
140
141 /** 基本信息的入参定义表格配置 */
142 const extendTableInfo = ref({
143 id: "input-extend-table",
144 height: '214px',
145 fields: [
146 { label: "序号", type: "index", width: TableColumnWidth.INDEX, align: "center" },
147 { label: "扩展字段名称", field: "expansionFieldName", width: 160, required: true, columClass: 'edit-colum', type: 'edit' },
148 { label: "字段类型", field: "fieldType", width: 140, required: true, columClass: 'edit-colum', type: 'edit' },
149 { label: "输入内容", field: "content", width: 300, required: false, columClass: 'edit-colum', type: 'edit' },
150 ],
151 editInfo: {
152 expansionFieldName: {
153 label: '',
154 type: 'input',
155 field: 'expansionFieldName',
156 default: '',
157 maxlength: 50,
158 placeholder: '请输入',
159 clearable: true,
160 },
161 fieldType: {
162 label: '',
163 type: 'select',
164 field: 'fieldType',
165 default: '',
166 options: dataTypeList.value,
167 props: {
168 label: 'label',
169 value: 'value'
170 },
171 placeholder: '请选择',
172 clearable: false,
173 filterable: true
174 },
175 content: {
176 label: '',
177 type: 'input',
178 field: 'content',
179 default: '',
180 maxlength: 200,
181 placeholder: '请输入',
182 clearable: true,
183 }
184 },
185 STATUS: 'edit',
186 data: extendData.value,
187 showPage: false,
188 actionInfo: {
189 show: true,
190 label: "操作",
191 type: "btn",
192 width: 60,
193 fixed: 'right',
194 btns: [
195 {
196 label: "删除", value: "remove", click: (scope) => {
197 let index = scope.$index;
198 proxy.$openMessageBox("此操作将永久删除, 是否继续?", () => {
199 extendData.value.splice(index, 1);
200 extendTableInfo.value.data = extendData.value;
201 proxy.$ElMessage.success('扩展字段删除成功');
202 }, () => {
203 proxy.$ElMessage.info("已取消");
204 });
205 }
206 },
207 ]
208 },
209 loading: false
210 });
211
212 /** 给表格添加一行入参定义。 */
213 const addExtend = () => {
214 extendData.value.push({ expansionFieldName: '', fieldType: '', content: '' });
215 extendTableInfo.value.data = extendData.value;
216 nextTick(() => {
217 scrollLastRowToView(extendTableRef.value?.tableRef, extendData.value.length);
218 })
219 }
220
221 /** ----------------- 数据产品表格相关信息 ----------------------------- */
222 const productTableRef = ref();
223 const productData: any = ref([]);
224
225 const productList: any = ref([]);
226
227 /** 基本信息的入参定义表格配置 */
228 const productTableInfo = ref({
229 id: "input-product-table",
230 height: '214px',
231 fields: [
232 { label: "序号", type: "index", width: TableColumnWidth.INDEX, align: "center" },
233 { label: "数据产品", field: "dataProductId", width: 180, required: true, columClass: 'edit-colum', type: 'edit' },
234 { label: "数据产品编码", field: "dataProductId", width: 261 },
235 { label: "所属主体名称", field: "dataProductEntityName", width: 200 },
236 { label: "产品简介", field: "dataProductAbstract", width: 240 },
237 ],
238 editInfo: {
239 dataProductId: {
240 label: '',
241 type: 'select',
242 field: 'dataProductId',
243 default: '',
244 options: productList.value,
245 props: {
246 label: 'productNameDesc',
247 value: 'productId',
248 disabled: 'disabled'
249 },
250 placeholder: '请选择',
251 clearable: false,
252 filterable: true
253 },
254 },
255 STATUS: 'edit',
256 data: productData.value,
257 showPage: false,
258 actionInfo: {
259 show: true,
260 label: "操作",
261 type: "btn",
262 width: 60,
263 fixed: 'right',
264 btns: [
265 {
266 label: "删除", value: "remove", click: (scope) => {
267 let index = scope.$index;
268 proxy.$openMessageBox("此操作将永久删除, 是否继续?", () => {
269 productData.value.splice(index, 1);
270 productTableInfo.value.data = productData.value;
271 proxy.$ElMessage.success('产品删除成功');
272 }, () => {
273 proxy.$ElMessage.info("已取消");
274 });
275 }
276 },
277 ]
278 },
279 loading: false
280 });
281
282 /** 给表格添加一行入参定义。 */
283 const addProduct = () => {
284 productData.value.push({ dataProductId: '' });
285 productTableInfo.value.data = productData.value;
286 nextTick(() => {
287 scrollLastRowToView(productTableRef.value?.tableRef, productData.value.length);
288 })
289 }
290
291 /** 根据下拉选择的产品带出产品相关信息 */
292 const hanldeTableSelectChange = (val, scope, item) => {
293 let productItem = val && productList.value.find(p => p.productId == val);
294 scope.row.dataProductId = val;
295 scope.row.dataProductCode = val;
296 scope.row.dataProductName = productItem?.productName;
297 scope.row.dataProductEntityId = productItem?.enterpriseCode;
298 scope.row.dataProductAbstract = productItem?.description;
299 scope.row.dataProductEntityName = productItem?.enterpriseName;
300 let nodeInfo = nodeInfoFormRef.value.formInline;
301 if (scope.$index == 0) {
302 nodeInfoFormItems.value.forEach((item, index) => {
303 item.default = nodeInfo[item.field];
304 if (index == 1) {
305 item.default = tenantList.value.find(t => t.socialCreditCode == scope.row.dataProductEntityId)?.guid || scope.row.dataProductEntityName;
306 } else if (index == 2) {
307 item.default = scope.row.dataProductEntityId || '';
308 }
309 })
310 }
311 }
312
313 /** ---------------- 合约策略执行者相关表单信息 ----------------------------------- */
314
315 /** 策略执行者下拉列表 */
316 const tenantList: any = ref([]);
317
318 const nodeInfoFormRef = ref();
319
320 // 策略执行者类型列表
321 const executorTypeList: any = ref([]);
322
323 const nodeInfoFormItems = ref([{
324 label: '策略执行者类型',
325 type: 'input',
326 placeholder: '-',
327 field: 'dataProviderType',
328 default: '',
329 disabled: true,
330 visible: true
331 }, {
332 label: '策略执行者名称',
333 type: 'select',
334 placeholder: '请选择',
335 field: 'dataProviderGuid',
336 default: '',
337 options: tenantList.value, //先禁用,选择产品后自动带出
338 props: {
339 value: 'guid',
340 label: 'tenantName'
341 },
342 filterable: true,
343 clearable: true,
344 disabled: true,
345 required: true,
346 visible: true
347 }, {
348 label: '策略执行者标识',
349 type: 'input',
350 placeholder: '-',
351 field: 'dataProviderId',
352 default: '',
353 disabled: true,
354 visible: true
355 }, {
356 label: '策略执行者类型',
357 type: 'input',
358 placeholder: '-',
359 field: 'dataUserType',
360 default: '',
361 disabled: true,
362 visible: true
363 }, {
364 label: '策略执行者名称',
365 type: 'select',
366 placeholder: '请选择',
367 field: 'dataUserGuid',
368 default: '',
369 options: tenantList.value,
370 props: {
371 value: 'guid',
372 label: 'tenantName'
373 },
374 filterable: true,
375 clearable: true,
376 required: true,
377 visible: true
378 }, {
379 label: '策略执行者标识',
380 type: 'input',
381 placeholder: '-',
382 field: 'dataUserId',
383 default: '',
384 disabled: true,
385 visible: true
386 }])
387
388 const nodeInfoFormRules = ref({
389 // dataProviderGuid: [required('请选择数据供给方策略执行者名称')],
390 dataUserGuid: [required('请选择数据使用方策略执行者名称')],
391 });
392
393 const handleNodeSelectChange = (val, row, info) => {
394 if (row.field == 'dataUserGuid') {
395 let tenantItem = tenantList.value.find(t => t.guid == val);
396 info.dataUserId = tenantItem?.socialCreditCode || '';
397 nodeInfoFormItems.value.forEach(item => {
398 item.default = info[item.field];
399 });
400 }
401 }
402
403 const strategyTableRef = ref();
404
405 /** 约束运算符字典下拉 */
406 const operatorOptionList: any = ref([]);
407
408 /** 约束行为下拉列表 */
409 const constraintOptionsList: any = ref([]);
410
411 /** 策略操作行为下拉列表 */
412 const actionOptionsList: any = ref([]);
413
414 const getSubmitInfo = () => {
415 let baseInfo = baseInfoFormRef.value.formInline;
416 let nodeInfo = nodeInfoFormRef.value.formInline;
417 let params = { ...baseInfo };
418 params.contractExpansions = extendData.value;
419 params.contractSubjects = productData.value;
420 params.executionNodes = [{
421 executionerTypeCode: executorTypeList.value[0]?.value,
422 executionerGuid: nodeInfo.dataProviderGuid,
423 executionerId: nodeInfo.dataProviderId,
424 executionerName: tenantList.value.find(t => t.guid == nodeInfo.dataProviderGuid)?.tenantName
425 }, {
426 executionerTypeCode: executorTypeList.value[1]?.value,
427 executionerGuid: nodeInfo.dataUserGuid,
428 executionerId: nodeInfo.dataUserId,
429 executionerName: tenantList.value.find(t => t.guid == nodeInfo.dataUserGuid)?.tenantName
430 }];
431 params.policys = strategyTableRef.value.strategyData?.map((d, index) => {
432 return Object.assign({}, d, { orderNum: index + 1 })
433 }) || [];
434 return params;
435 }
436
437 const saveDraft = () => {
438 let params: any = getSubmitInfo();
439 params.isResubmit = false;
440 fullscreenLoading.value = true;
441 if (!route.query.guid) {
442 saveContract(params).then((res: any) => {
443 fullscreenLoading.value = false;
444 if (res?.code == proxy.$passCode) {
445 proxy.$ElMessage.success('合约保存成功');
446 userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath));
447 router.push({
448 name: "smartContractManagement",
449 });
450 dataSmartContract.set(true);
451 } else {
452 res?.msg && proxy.$ElMessage.error(res?.msg)
453 }
454 })
455 } else {
456 params.guid = route.query.guid;
457 params.isResubmit = false;
458 updateContract(params).then((res: any) => {
459 fullscreenLoading.value = false;
460 if (res?.code == proxy.$passCode) {
461 proxy.$ElMessage.success('合约编辑保存成功');
462 userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath));
463 router.push({
464 name: "smartContractManagement",
465 });
466 dataSmartContract.set(true);
467 } else {
468 res?.msg && proxy.$ElMessage.error(res?.msg)
469 }
470 })
471 }
472 }
473
474 const submit = () => {
475 baseInfoFormRef.value.ruleFormRef?.validate((valid, errorItem) => {
476 if (valid) {
477 for (const data of extendData.value) {
478 if (!data.expansionFieldName) {
479 proxy.$ElMessage.error('扩展信息中的扩展字段名称不能为空');
480 handleLeft('base-info');
481 expandBase.value = true;
482 return;
483 }
484 if (!data.fieldType) {
485 proxy.$ElMessage.error('扩展信息中的字段类型不能为空');
486 handleLeft('base-info');
487 expandBase.value = true;
488 return;
489 }
490 }
491 if (!productData.value?.length) {
492 proxy.$ElMessage.error('合约标的的数据产品不能为空');
493 expandInfo.value = true;
494 handleLeft('product-info');
495 return;
496 }
497 let index = 0;
498 for (const pd of productData.value) {
499 if (!pd.dataProductId) {
500 proxy.$ElMessage.error('合约标的的数据产品不能为空');
501 expandInfo.value = true;
502 handleLeft('product-info');
503 return;
504 }
505 if (userData.superTubeFlag == 'Y') { //只有主平台模式下需判断
506 if (baseInfoFormRef.value.formInline.signModeCode == '02' && index > 0 && productData.value[index].dataProductEntityId != pd.dataProductEntityId) {
507 proxy.$ElMessage.error('合约标的的数据产品不能来自不同的企业主体');
508 expandInfo.value = true;
509 handleLeft('product-info');
510 return;
511 } else {
512 if (pd.dataProductEntityId != nodeInfoFormRef.value?.formInline?.dataProviderId) {
513 proxy.$ElMessage.error('合约标的的数据产品所属主体与数据提供方的策略执行者不一致,请检查!');
514 expandInfo.value = true;
515 handleLeft('product-info');
516 return;
517 }
518 }
519 }
520 index++;
521 }
522 nodeInfoFormRef.value.ruleFormRef?.validate((valid1, errorItem1) => {
523 if (valid1) {
524 let nodeInfo = nodeInfoFormRef.value.formInline;
525 if (nodeInfo.dataProviderGuid == nodeInfo.dataUserGuid) {
526 proxy.$ElMessage.error(`${executorTypeList.value[0].label}${executorTypeList.value[1].label}的策略执行者不能选择同一个`);
527 expandPolicy.value = true;
528 handleLeft('policy-info');
529 return;
530 }
531 if (!strategyTableRef.value.validateValue()) {
532 expandPolicy.value = true;
533 handleLeft('policy-info');
534 return;
535 }
536 let params: any = getSubmitInfo();
537 params.isResubmit = detailInfo.value?.contractStatus == '00';
538 params.contractStatus = '02';
539 fullscreenLoading.value = true;
540 if (!route.query.guid) {
541 saveContract(params).then((res: any) => {
542 fullscreenLoading.value = false;
543 if (res?.code == proxy.$passCode) {
544 proxy.$ElMessage.success('合约协商成功');
545 userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath));
546 router.push({
547 name: "smartContractManagement",
548 });
549 dataSmartContract.set(true);
550 } else {
551 res?.msg && proxy.$ElMessage.error(res?.msg)
552 }
553 })
554 } else {
555 params.guid = route.query.guid;
556 updateContract(params).then((res: any) => {
557 fullscreenLoading.value = false;
558 if (res?.code == proxy.$passCode) {
559 proxy.$ElMessage.success('合约协商成功');
560 userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath));
561 router.push({
562 name: "smartContractManagement",
563 });
564 dataSmartContract.set(true);
565 } else {
566 res?.msg && proxy.$ElMessage.error(res?.msg)
567 }
568 })
569 }
570 } else {
571 expandPolicy.value = true;
572 var obj = Object.keys(errorItem1);
573 nodeInfoFormRef.value.ruleFormRef?.scrollToField(obj[0]);
574 }
575 })
576 } else {
577 expandBase.value = true;
578 var obj = Object.keys(errorItem);
579 baseInfoFormRef.value.ruleFormRef?.scrollToField(obj[0]);
580 nodeInfoFormRef.value.validate();
581 }
582 });
583 }
584
585 const handleLeft = (id) => {
586 let dom = document.getElementById(id);
587 dom && dom.scrollIntoView({
588 behavior: "smooth", // 平滑过渡
589 block: "start", // 上边框与视窗顶部平齐。默认值
590 });
591 }
592
593 const cancel = () => {
594 proxy.$openMessageBox("当前页面尚未保存,确定放弃修改吗?", () => {
595 userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath));
596 router.push({
597 name: "smartContractManagement",
598 });
599 }, () => {
600 proxy.$ElMessage.info("已取消");
601 });
602 }
603
604 const detailInfo: any = ref({});
605
606 /** 当前会员是否认证信息 */
607 const currTenantDetailInfo: any = ref({ });
608
609 const psLogon = ref();
610
611 onBeforeMount(() => {
612 let exec = () => {
613 if (userData.superTubeFlag != 'Y') {
614 baseInfoFormItems.value[1].disabled = true;
615 baseInfoFormItems.value[1].default = '01';
616 productTableInfo.value.editInfo.dataProductId.props.label = 'productName';
617 } else { //可以选点对点。未认证时只能选择中介参与
618 baseInfoFormItems.value[1].disabled = !currTenantDetailInfo.value.trustedIdentityCredential;
619 baseInfoFormItems.value[1].default = !currTenantDetailInfo.value.trustedIdentityCredential ? '02' : '01';
620 productTableInfo.value.editInfo.dataProductId.props.label = 'productNameDesc';
621 }
622 if (route.query.guid) {
623 fullscreenLoading.value = true;
624 getContractDetail(route.query.guid).then((res: any) => {
625 fullscreenLoading.value = false;
626 if (res?.code == proxy.$passCode) {
627 detailInfo.value = res.data || {};
628 baseInfoFormItems.value.forEach(item => {
629 item.default = detailInfo.value[item.field];
630 });
631 // if (baseInfoFormItems.value[1].default == '01') {
632 // // 默认点对点,数据使用方可以选择
633 // nodeInfoFormItems.value[1].disabled = true;
634 // } else {
635 // nodeInfoFormItems.value[1].disabled = false;
636 // }
637 // nodeInfoFormItems.value[1].disabled = detailInfo.value.signModeCode == '01';
638 let executionNodes = detailInfo.value.executionNodes || [];
639 let provider = executionNodes.find(e => e.executionerTypeCode == '01');
640 let user = executionNodes.find(e => e.executionerTypeCode == '02');
641 nodeInfoFormItems.value[1].default = provider.executionerGuid;
642 nodeInfoFormItems.value[2].default = provider.executionerId;
643 nodeInfoFormItems.value[4].default = user.executionerGuid;
644 nodeInfoFormItems.value[5].default = user.executionerId;
645 if (baseInfoFormItems.value[1].default == '01' && userData.superTubeFlag == 'Y') {
646 productList.value = res.data?.map(d => {
647 d.disabled = d.enterpriseName != userData.tenantName; //点对点,只能选择自己公司所属产品
648 return d;
649 }) || [];
650 }
651 productTableInfo.value.editInfo.dataProductId.options = productList.value;
652 extendData.value = detailInfo.value.contractExpansions || [];
653 extendTableInfo.value.data = extendData.value;
654 productData.value = detailInfo.value.contractSubjects || [];
655 productTableInfo.value.data = productData.value;
656 } else {
657 res?.msg && proxy.$ElMessage.error(res?.msg)
658 }
659 })
660 } else {
661 if (baseInfoFormItems.value[1].default == '01') {
662 // 默认点对点,数据使用方可以选择
663 // nodeInfoFormItems.value[1].disabled = true;
664 nodeInfoFormItems.value[1].default = userData.tenantGuid;
665 if (tenantList.value.length) {
666 let item = tenantList.value.find(t => t.guid == userData.tenantGuid);
667 nodeInfoFormItems.value[2].default = item?.socialCreditCode;
668 }
669 } else {
670 // nodeInfoFormItems.value[1].disabled = false;
671 nodeInfoFormItems.value[1].default = ''; //如果是中介的话,不能选择自己吧。
672 }
673 }
674 getContractDataProduct().then((res: any) => {
675 if (res?.code == proxy.$passCode) {
676 productList.value = res.data?.map(d => {
677 d.productNameDesc = d.productName + '(' + d.enterpriseName + ')';
678 d.disabled = userData.superTubeFlag == 'Y' && baseInfoFormItems.value[1].default == '01' && d.enterpriseName != userData.tenantName; //点对点,只能选择自己公司所属产品
679 return d;
680 }) || [];
681 productTableInfo.value.editInfo.dataProductId.options = productList.value;
682 } else {
683 res?.msg && proxy.$ElMessage.error(res?.msg);
684 }
685 })
686 }
687 fullscreenLoading.value = true;
688 psLogon.value = getEnterpriseData({
689 logonUser: userData.tenantName == "非认证会员" ? userData.logonUser : tenantData.logonUser
690 }).then((res: any) => {
691 psLogon.value = null;
692 fullscreenLoading.value = false;
693 if (res?.code == proxy.$passCode) {
694 currTenantDetailInfo.value = res.data || {};
695 exec();
696 } else {
697 res?.msg && proxy.$ElMessage.error(res?.msg);
698 exec();
699 }
700 }).catch(() => {
701 psLogon.value = null;
702 fullscreenLoading.value = false;
703 exec();
704 });
705 getParamsList({ dictType: '合约签署模式' }).then((res: any) => {
706 if (res?.code == proxy.$passCode) {
707 const data = res.data || [];
708 signModeList.value = data;
709 baseInfoFormItems.value[1].options = signModeList.value;
710 } else {
711 res?.msg && proxy.$ElMessage.error(res?.msg);
712 }
713 })
714 getParamsList({ dictType: '合约扩展字段类型' }).then((res: any) => {
715 if (res?.code == proxy.$passCode) {
716 const data = res.data || [];
717 dataTypeList.value = data;
718 extendTableInfo.value.editInfo.fieldType.options = dataTypeList.value;
719 } else {
720 res?.msg && proxy.$ElMessage.error(res?.msg);
721 }
722 })
723 getParamsList({ dictType: '约束运算符' }).then((res: any) => {
724 if (res?.code == proxy.$passCode) {
725 const data = res.data || [];
726 operatorOptionList.value = data;
727 } else {
728 res?.msg && proxy.$ElMessage.error(res?.msg);
729 }
730 })
731 getActionPolicyList().then((res: any) => {
732 if (res?.code == proxy.$passCode) {
733 const data = res.data || [];
734 actionOptionsList.value = data;
735 } else {
736 res?.msg && proxy.$ElMessage.error(res?.msg);
737 }
738 })
739 getConstraintPolicyList().then((res: any) => {
740 if (res?.code == proxy.$passCode) {
741 const data = res.data || [];
742 constraintOptionsList.value = data;
743 } else {
744 res?.msg && proxy.$ElMessage.error(res?.msg);
745 }
746 })
747 getParamsList({ dictType: '策略执行者类型' }).then((res: any) => {
748 if (res?.code == proxy.$passCode) {
749 const data = res.data || [];
750 executorTypeList.value = data;
751 nodeInfoFormItems.value[0].default = executorTypeList.value[0]?.label;
752 nodeInfoFormItems.value[3].default = executorTypeList.value[1]?.label;
753 } else {
754 res?.msg && proxy.$ElMessage.error(res?.msg);
755 }
756 })
757 getContractTenantList().then((res: any) => {
758 if (res.code == proxy.$passCode) {
759 tenantList.value = res.data || [];
760 nodeInfoFormItems.value[1].options = tenantList.value;
761 nodeInfoFormItems.value[4].options = tenantList.value;
762 if (nodeInfoFormItems.value[1].default) {
763 let item = tenantList.value.find(t => t.guid == nodeInfoFormItems.value[1].default);
764 nodeInfoFormItems.value[2].default = item?.socialCreditCode;
765 }
766 } else {
767 res?.msg && proxy.$ElMessage.error(res.msg);
768 }
769 })
770 });
771
772 onActivated(() => {
773 if (route.query.guid) {
774 let tab: any = userStore.tabbar.find((tab: any) => tab.fullPath === fullPath);
775 if (tab) {
776 document.title = `编辑合约-${route.query.name}`;
777 tab.meta.title = `编辑合约-${route.query.name}`;
778 }
779 }
780 })
781
782 </script>
783
784 <template>
785 <div class="container_wrap full" v-loading="fullscreenLoading">
786 <div class="content_main panel">
787 <ContentWrap id="base-info" title="合约信息" expandSwicth style="margin-top: 15px" :isExpand="expandBase"
788 @expand="(v) => (expandBase = v)" description="">
789 <Form ref="baseInfoFormRef" formId="base-info-form" :itemList="baseInfoFormItems" :rules="baseInfoFormRules"
790 col="col3" @select-change="handleFormSelectChange" />
791 <Table ref="extendTableRef" :tableInfo="extendTableInfo" class="fiveRow-table" />
792 <div class="row-add-btn">
793 <el-button link @click="addExtend" :icon="CirclePlus" v-preReClick>添加扩展信息</el-button>
794 </div>
795 </ContentWrap>
796 <ContentWrap id="product-info" title="合约标的" expandSwicth style="margin-top: 15px" :isExpand="expandInfo"
797 @expand="(v) => (expandInfo = v)" description="">
798 <Table ref="productTableRef" :tableInfo="productTableInfo" class="fiveRow-table"
799 @table-select-change="hanldeTableSelectChange" />
800 <div class="row-add-btn">
801 <el-button link @click="addProduct" :icon="CirclePlus" v-preReClick>添加产品</el-button>
802 </div>
803 </ContentWrap>
804 <ContentWrap id="policy-info" title="合约策略" expandSwicth style="margin-top: 15px" :isExpand="expandPolicy"
805 @expand="(v) => (expandPolicy = v)" description="">
806 <div class="h-title">策略执行节点</div>
807 <Form ref="nodeInfoFormRef" formId="node-info-form" :itemList="nodeInfoFormItems" :rules="nodeInfoFormRules"
808 @select-change="handleNodeSelectChange" col="col3" />
809 <StrategyTable ref="strategyTableRef" :show-title="true" :value="detailInfo.policys || []"
810 :operatorOptionList="operatorOptionList" :actionOptionsList="actionOptionsList"
811 :constraintOptionsList="constraintOptionsList"></StrategyTable>
812 </ContentWrap>
813 </div>
814 <div class="tool_btns">
815 <div class="btns">
816 <el-button @click="cancel">取消</el-button>
817 <el-button @click="saveDraft" v-show="detailInfo?.contractStatus != '00'">保存</el-button>
818 <el-button type="primary" @click="submit">合约协商</el-button>
819 </div>
820 </div>
821 </div>
822 </template>
823
824 <style lang="scss" scoped>
825 .container_wrap {
826 overflow: hidden;
827
828 .content_main {
829 height: calc(100% - 45px);
830 overflow: hidden auto;
831
832 &.panel {
833 padding: 0 16px 16px;
834 }
835 }
836 }
837
838 .tool_btns {
839 height: 44px;
840 margin: 0 -8px;
841 display: flex;
842 justify-content: center;
843 align-items: center;
844 border-top: 1px solid #d9d9d9;
845 }
846
847 .row-add-btn {
848 .el-button--default {
849 padding: 4px 0px;
850 margin-top: 4px;
851 }
852
853 :deep(.el-icon) {
854 width: 16px;
855 height: 16px;
856
857 svg {
858 width: 16px;
859 height: 16px;
860 }
861 }
862 }
863
864 .h-title {
865 font-size: 14px;
866 color: #212121;
867 font-weight: 600;
868 margin-bottom: 4px;
869 }
870 </style>
1 <route lang="yaml">
2 name: smartContractDetail
3 </route>
4
5 <script lang="ts" setup name="smartContractDetail">
6 import {
7 getContractDetail,
8 getActionPolicyList,
9 getConstraintPolicyList,
10 getContractNegotiate,
11 getContractNegoPlicyByVersion,
12 continueContractNegotiate,
13 rejectContract,
14 confirmContract,
15 signContract,
16 getSignListInfo,
17 getContractExecList,
18 terminateContract,
19 getTerminateDetailInfo,
20 getSignatureFile
21 } from "@/api/modules/dataSmartContract";
22 import {
23 tagMethod,
24 tagType
25 } from "@/utils/common";
26 import {
27 getParamsList,
28 } from "@/api/modules/queryService";
29 import { commonPageConfig, TableColumnWidth } from "@/utils/enum";
30 import StrategyTable from './components/strategyTable.vue';
31 import { onUploadFilePreview, onUploadFileDownload } from '@/api/modules/common';
32 import { useValidator } from "@/hooks/useValidator";
33 import useUserStore from "@/store/modules/user";
34 import useDataSmartContract from "@/store/modules/dataSmartContract";
35 import ConfirmDialog from "./components/confirmDialog.vue";
36 import { getEnterpriseData } from "@/api/modules/dataIdentify";
37 import showFile from "./components/showFile.vue";
38 // @ts-ignore
39 import html2pdf from 'html2pdf.js';
40 import { getPathUrl, getUpFileSignByUrl, obsUploadRequest } from "@/api/modules/obsService";
41 import { ElMessage } from "element-plus";
42
43 const { required, requiredFiles } = useValidator();
44 const userStore = useUserStore();
45 const userData = JSON.parse(localStorage.userData)
46 const tenantData = JSON.parse(localStorage.tenantInfo);
47 const dataSmartContract = useDataSmartContract();
48 const router = useRouter();
49 const route = useRoute();
50 const fullPath = route.fullPath;
51 const fullscreenLoading = ref(false);
52 const expandBase = ref(false);
53 const expandInfo = ref(false);
54 const expandPolicy = ref(false);
55 const expandPolicyConsult = ref(false);
56 const expandConsultHistory = ref(false);
57 const expandSign = ref(false);//合约签署
58 const expandKeep = ref(false);//合约履约执行信息
59 const expandReject = ref(false);//合约解除
60 const { proxy } = getCurrentInstance() as any;
61 const detailInfo: any = ref({});
62 const detailType = ref(route.query.type); //为null表示第一步查看,其余协整和签署等需要传递type
63 const isDetail = ref(route.query.isDetail == 'Y');
64
65 const toolBtns: any = computed(() => {
66 let btnsArr: any = [{
67 label: isDetail.value ? "关闭" : '取消', value: "cancel", plain: true
68 }];
69 let contractStatus = detailInfo.value.contractStatus;
70 if ((!detailType.value || currentStep.value == 1) && contractStatus == '01') {
71 btnsArr.push({
72 label: "合约协商", value: "consult", type: 'primary'
73 });
74 }
75 if (detailType.value == 'consult') {
76 btnsArr.push({
77 label: "拒绝本次合约", value: "refuse", plain: true
78 });
79 btnsArr.push({
80 label: "确认本次合约", value: "confirm", type: 'primary', disabled: disableConfirmBtn.value
81 });
82 btnsArr.push({
83 label: "继续协商", value: "continue", type: 'primary', disabled: !disableConfirmBtn.value
84 });
85 } else if (detailType.value == 'sign') {
86 btnsArr.push({
87 label: "确定签署", value: "confirmSign", type: 'primary', disabled: !confirmAuth.value
88 });
89 } else if (detailType.value == 'reject' && (!currentStep.value || currentStep.value == 5)) {
90 // btnsArr.push({
91 // label: "上一步", value: "lastStep", plain: true
92 // });
93 btnsArr.push({
94 label: "确认提交", value: "confirmReject", type: 'primary'
95 });
96 }
97 return btnsArr;
98 });
99
100 const btnHandles = ref({
101 'cancel': () => {
102 userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath));
103 router.push({
104 name: "smartContractManagement",
105 });
106 },
107 'consult': () => {
108 if (!detailType.value) {
109 router.push({
110 name: "smartContractManagement",
111 });
112 return;
113 }
114 if (detailType.value == 'consult') {
115 currentStep.value = 2;
116 }
117 },
118 'refuse': () => {
119 confirmDialogInfo.value.type = 'fail';
120 confirmDialogInfo.value.dialogVisible = true;
121 },
122 'continue': () => { //继续协商
123 if (!strategyTableEditRef.value.validateValue()) {
124 return;
125 }
126 fullscreenLoading.value = true;
127 continueContractNegotiate({
128 contractGuid: route.query.guid,
129 policys: strategyTableEditRef.value.strategyData?.map((d, index) => {
130 return Object.assign({}, d, { orderNum: index + 1 })
131 })
132 }).then((res: any) => {
133 fullscreenLoading.value = false;
134 if (res?.code == proxy.$passCode) {
135 proxy.$ElMessage.success('合约继续协商提交成功');
136 userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath));
137 router.push({
138 name: "smartContractManagement",
139 });
140 dataSmartContract.set(true);
141 } else {
142 res?.msg && proxy.$ElMessage.error(res?.msg)
143 }
144 })
145 },
146 'confirm': () => { //确认本次合约。如果还需另一方确认,就需要清空confirmGuids。
147 confirmDialogInfo.value.type = detailInfo.value.signModeCode == '01' ? 'success' : (!detailInfo.value.confirmGuids?.length ? 'warning' : 'success');
148 confirmDialogInfo.value.dialogVisible = true;
149 },
150 'confirmSign': () => {
151 signInfoFormRef.value?.ruleFormRef?.validate((valid, errorItem) => {
152 if (valid) {
153 if (!signFileUrlInfo.value?.url) {
154 proxy.$ElMessage.error('签署签名文件不能为空!');
155 return;
156 }
157 let signFormInline = signInfoFormRef.value.formInline;
158 let params: any = {
159 contractGuid: route.query.guid,
160 signatureEntityGuid: userData.tenantGuid,
161 signatureEntityId: signTenantDetailInfo.value.socialCreditCode,
162 signatureEntityName: userData.tenantName,
163 identityCertificate: { url: signTenantDetailInfo.value.trustedIdentityCredential },
164 signature: signFileUrlInfo.value?.url ? {
165 name: signFileUrlInfo.value.name,
166 url: signFileUrlInfo.value.url,
167 } : {},
168 attachment: signFormInline.file?.length ? {
169 name: signFormInline.file[0].name,
170 url: signFormInline.file[0].url,
171 } : {},
172 };
173 fullscreenLoading.value = true;
174 signContract(params).then((res: any) => {
175 fullscreenLoading.value = false;
176 if (res?.code == proxy.$passCode) {
177 proxy.$ElMessage.success('合约确定签署成功');
178 userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath));
179 router.push({
180 name: "smartContractManagement",
181 });
182 dataSmartContract.set(true);
183 } else {
184 res?.msg && proxy.$ElMessage.error(res?.msg)
185 }
186 })
187 } else {
188 expandSign.value = true;
189 var obj = Object.keys(errorItem);
190 signInfoFormRef.value.ruleFormRef?.scrollToField(obj[0]);
191 }
192 })
193 },
194 'lastStep': () => {
195 if (detailType.value == 'reject' && !currentStep.value) {
196 currentStep.value = 4;
197 } else {
198 currentStep.value--;
199 }
200 },
201 'confirmReject': () => { //确认解除合约
202 //后端需要检验选择的合同约定终止时间不能大于合同生效时间
203 rejectInfoFormRef.value.ruleFormRef?.validate((valid, errorItem) => {
204 if (valid) {
205 proxy.$openMessageBox('确认提交合约解除信息吗?', () => {
206 let params = { ...rejectInfoFormRef.value.formInline };
207 let file = params.dataProcessingProof || [];
208 params.dataProcessingProof = {
209 name: file?.[0]?.name,
210 url: file?.[0]?.url
211 }
212 params.contractGuid = route.query.guid;
213 fullscreenLoading.value = true;
214 terminateContract(params).then((res: any) => {
215 fullscreenLoading.value = false;
216 if (res?.code == proxy.$passCode) {
217 proxy.$ElMessage.success('合约解除信息提交成功');
218 userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath));
219 router.push({
220 name: "smartContractManagement",
221 });
222 dataSmartContract.set(true);
223 } else {
224 res?.msg && proxy.$ElMessage.error(res?.msg)
225 }
226 })
227 }, () => {
228 proxy.$ElMessage.info("已取消");
229 });
230 } else {
231 var obj = Object.keys(errorItem);
232 rejectInfoFormRef.value.ruleFormRef.scrollToField(obj[0])
233 }
234 })
235 }
236 });
237
238 const extendTableInfo = ref({
239 id: "input-extend-table",
240 height: '214px',
241 fields: [
242 { label: "序号", type: "index", width: TableColumnWidth.INDEX, align: "center" },
243 { label: "扩展字段名称", field: "expansionFieldName", width: 160 },
244 { label: "字段类型", field: "fieldTypeName", width: 140 },
245 { label: "输入内容", field: "content", width: 300 },
246 ],
247 data: [],
248 showPage: false,
249 actionInfo: {
250 show: false
251 },
252 loading: false
253 });
254
255 const exportExtendTableInfo = computed(() => {
256 return {
257 id: "export-input-extend-table",
258 height: 'auto',
259 minPanelHeight: '60px',
260 minHeight: '60px',
261 fields: [
262 { label: "序号", type: "index", width: TableColumnWidth.INDEX, align: "center", showTooltip: false },
263 { label: "扩展字段名称", field: "expansionFieldName", width: "auto", minWidth: 140, showTooltip: false },
264 { label: "字段类型", field: "fieldTypeName", width: "auto", minWidth: 100, showTooltip: false },
265 { label: "输入内容", field: "content", width: "auto", minWidth: 250, showTooltip: false },
266 ],
267 data: extendTableInfo.value.data,
268 showPage: false,
269 actionInfo: {
270 show: false
271 },
272 loading: false
273 }
274 });
275
276 const productTableInfo = ref({
277 id: "input-product-table",
278 height: '214px',
279 fields: [
280 { label: "序号", type: "index", width: TableColumnWidth.INDEX, align: "center" },
281 { label: "数据产品", field: "dataProductName", width: 180 },
282 { label: "数据产品编码", field: "dataProductId", width: 261 },
283 { label: "所属主体名称", field: "dataProductEntityName", width: 240 },
284 { label: "产品简介", field: "dataProductAbstract", width: 240 },
285 ],
286 data: [],
287 showPage: false,
288 actionInfo: {
289 show: false
290 },
291 loading: false
292 });
293
294
295 const exportProductTableInfo = computed(() => {
296 return {
297 id: "export-input-product-table",
298 height: 'auto',
299 minPanelHeight: '60px',
300 minHeight: '60px',
301 fields: [
302 { label: "序号", type: "index", width: TableColumnWidth.INDEX, align: "center", showTooltip: false },
303 { label: "数据产品", field: "dataProductName", minWidth: 120, showTooltip: false, width: "auto" },
304 { label: "数据产品编码", field: "dataProductId", width: "auto", minWidth: 140, showTooltip: false },
305 { label: "所属主体名称", field: "dataProductEntityName", width: "auto", minWidth: 150, showTooltip: false },
306 { label: "产品简介", field: "dataProductAbstract", width: "auto", minWidth: 160, showTooltip: false },
307 ],
308 data: productTableInfo.value.data,
309 showPage: false,
310 actionInfo: {
311 show: false
312 },
313 loading: false
314 }
315 });
316
317
318 const nodeInfoFormRef = ref();
319
320 const nodeInfoFormItems = ref([{
321 label: '策略执行者类型',
322 type: 'input',
323 placeholder: '请选择',
324 field: 'dataProviderType',
325 default: '数据供给方',
326 disabled: true,
327 visible: true
328 }, {
329 label: '策略执行者名称',
330 type: 'input',
331 placeholder: '请选择',
332 field: 'dataProviderName',
333 default: '',
334 options: [],
335 props: {
336 value: 'value',
337 label: 'label'
338 },
339 filterable: true,
340 clearable: true,
341 required: true,
342 disabled: true,
343 visible: true
344 }, {
345 label: '策略执行者标识',
346 type: 'input',
347 placeholder: '请输入',
348 field: 'dataProviderId',
349 default: '',
350 disabled: true,
351 visible: true
352 }, {
353 label: '策略执行者类型',
354 type: 'input',
355 placeholder: '请选择',
356 field: 'dataUserType',
357 default: '数据使用方',
358 disabled: true,
359 visible: true
360 }, {
361 label: '策略执行者名称',
362 type: 'input',
363 placeholder: '请选择',
364 field: 'dataUserName',
365 default: '',
366 options: [],
367 props: {
368 value: 'value',
369 label: 'label'
370 },
371 filterable: true,
372 clearable: true,
373 required: true,
374 disabled: true,
375 visible: true
376 }, {
377 label: '策略执行者标识',
378 type: 'input',
379 placeholder: '请输入',
380 field: 'dataUserId',
381 default: '',
382 disabled: true,
383 visible: true
384 }])
385
386 const currentStep = ref(0);
387
388 const handleClickStep = (number) => {
389 currentStep.value = number;
390 }
391
392 /** ---------------------- 合约协商 ------------------------- */
393 const strategyTableEditRef = ref();
394
395 /** 时间线列表 */
396 const activities: any = ref([]);
397
398 const detailDialogInfo = ref({
399 visible: false,
400 size: 800,
401 direction: "column",
402 header: {
403 title: "查看版本详情",
404 },
405 type: '',//标识是否是重新提交
406 contents: [],
407 footer: {
408 show: false
409 },
410 contentLoading: false,
411 });
412
413 const versionDetail: any = ref([]);
414
415 const clickVersion = (child) => {
416 detailDialogInfo.value.visible = true;
417 detailDialogInfo.value.header.title = '查看版本详情' + '(' + child.version + ')';
418 detailDialogInfo.value.contentLoading = true;
419 getContractNegoPlicyByVersion({
420 guid: route.query.guid,
421 version: child.version
422 }).then((res: any) => {
423 detailDialogInfo.value.contentLoading = false;
424 if (res?.code == proxy.$passCode) {
425 const data = res.data || [];
426 versionDetail.value = data;
427 } else {
428 res?.msg && proxy.$ElMessage.error(res?.msg);
429 }
430 })
431 }
432
433 const handleVersionDialogBtnClick = (btn) => {
434 if (btn.value == 'cancel') {
435 detailDialogInfo.value.visible = false;
436 }
437 }
438
439 const disableConfirmBtn = computed(() => {
440 let newValue = strategyTableEditRef.value?.strategyData || [];
441 let originValue = consultDetailInfo.value.policys || [];
442 if (newValue.length != originValue.length) {
443 return true;
444 }
445 let index = 0;
446 for (const newItem of newValue) {
447 let oldItem = originValue[index];
448 if (newItem.action != oldItem.action || newItem.constraintEnName != oldItem.constraintEnName ||
449 newItem.constraintOperatorCode != oldItem.constraintOperatorCode || newItem.constraintValue != oldItem.constraintValue) {
450 return true;
451 }
452 index++;
453 }
454 })
455
456 const confirmDialogInfo = ref({
457 dialogVisible: false,
458 type: 'success',
459 msg: {
460 'success': '确认合约表示您已仔细查阅并接受本次合约所有相关信息,确定提交后将为您跳转合约签署环节。\n如有疑问可取消操作。',
461 'warning': '确认合约表示您已仔细查阅并接受本次合约所有相关信息,确定提交后待对方确认合约后再进入签署环节。',
462 'fail': '拒绝后将终止本次合约协商流程,请谨慎操作。\n如有疑问可联络合约发起方详细咨询。'
463 },
464 });
465
466 const handleConfirmDialog = (btn) => {
467 if (btn == 'submit') {
468 confirmDialogInfo.value.dialogVisible = false;
469 if (confirmDialogInfo.value.type == 'fail') {
470 fullscreenLoading.value = true;
471 rejectContract(route.query.guid).then((res: any) => {
472 fullscreenLoading.value = false;
473 if (res?.code == proxy.$passCode) {
474 proxy.$ElMessage.success('拒绝本次合约提交成功');
475 userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath));
476 router.push({
477 name: "smartContractManagement",
478 });
479 dataSmartContract.set(true);
480 } else {
481 res?.msg && proxy.$ElMessage.error(res?.msg)
482 }
483 })
484 } else {
485 fullscreenLoading.value = true;
486 confirmContract(route.query.guid).then((res: any) => {
487 fullscreenLoading.value = false;
488 if (res?.code == proxy.$passCode) {
489 proxy.$ElMessage.success('确认本次合约成功');
490 userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath));
491 router.push({
492 name: "smartContractManagement",
493 });
494 dataSmartContract.set(true);
495 } else {
496 res?.msg && proxy.$ElMessage.error(res?.msg)
497 }
498 })
499 }
500 } else {
501 confirmDialogInfo.value.dialogVisible = false
502 }
503 }
504
505 /** ------------------------- 合约签署 -------------------------- */
506 const confirmAuth = ref(false); //是否勾选确权
507
508 const signInfoFormRef = ref();
509
510 const signInfoFormItems = ref([{
511 label: '附件上传',
512 tip: '文件大小不超过20MB',
513 type: 'upload-file',
514 limitSize: 20,
515 required: false,
516 default: [],
517 block: true,
518 field: 'file',
519 },
520 // {
521 // label: '签署签名',
522 // tip: '文件大小不超过20MB',
523 // type: 'upload-file',
524 // limitSize: 20,
525 // limit: 1,
526 // required: true,
527 // default: [],
528 // block: false,
529 // field: 'signFile',
530 // },
531 // {
532 // label: '签署时间',
533 // type: 'input',
534 // placeholder: '确定签署提交的时间',
535 // field: 'submitTime',
536 // default: '',
537 // maxlength: 50,
538 // disabled: true
539 // },
540 ]);
541
542 const signInfoFormRules = ref({
543 // signFile: [{
544 // validator: (rule: any, value: any, callback: any) => {
545 // if (!value?.length) {
546 // callback(new Error('请上传文件'))
547 // } else {
548 // callback();
549 // }
550 // }, trigger: 'change'
551 // }],
552 });
553
554 const viewResultVoucherFile = (url) => {
555 if (!url) {
556 proxy.$ElMessage.error('暂无可查看的身份凭证证书');
557 return;
558 }
559 onUploadFilePreview(url);
560 }
561
562 const signFileLoading = ref(false);
563
564 const signFileUrlInfo: any = ref({ name: '', url: '' });
565
566 function getElementPdfPosition(element, options = {}) {
567 const { scale = 2, pageHeightPt = 842 } = options;
568
569 // 1. 获取元素距离容器顶部的像素距离
570 const container = document.getElementById('pdf-container'); // 你的根容器
571 const rect = element.getBoundingClientRect();
572 const containerRect = container.getBoundingClientRect();
573 const topPx = rect.top - containerRect.top + window.scrollY;
574
575 // 2. html2canvas 默认使用 devicePixelRatio,但 html2pdf 通常固定 scale
576 // html2pdf 内部会将 canvas 高度按比例缩放到 PDF 页面宽度
577 // 简化模型:假设 PDF 宽度 = 595pt (A4),对应 canvas.width / scale px
578
579 const pdfPageWidthPt = 595; // A4 width in pt
580 const containerWidthPx = container.offsetWidth;
581 const pxToPtRatio = pdfPageWidthPt / containerWidthPx; // 每像素多少 pt
582
583 const topPt = topPx * pxToPtRatio;
584
585 // 3. 计算落在第几页 & 页内 Y 坐标
586 const page = Math.floor(topPt / pageHeightPt) + 1;
587 const yInPage = topPt % pageHeightPt;
588
589 return {
590 page,
591 x: rect.left * pxToPtRatio, // 可选 X 坐标
592 y: yInPage,
593 totalY: topPt
594 };
595 }
596
597
598 const geneSignFile = async () => {
599 const element = document.getElementById('pdf-content'); // 指定要转PDF的DOM
600 if (!element) {
601 proxy.$ElMessage.error('找不到生成pdf文件的网页内容');
602 return;
603 }
604 signFileLoading.value = true;
605 element.style.display = '';
606 await nextTick();
607 setTimeout(async () => {
608 let fileName = detailInfo.value.contractName + '_' + tenantData.abbreviation + '.pdf';
609 const options = {
610 margin: [10, 3, 10, 3],
611 filename: fileName,
612 image: {
613 type: 'jpeg',
614 quality: 0.9
615 },
616 html2canvas: {
617 scale: 2,
618 useCORS: true,
619 allowTaint: false,
620 logging: false,
621 backgroundColor: '#ffffff'
622 },
623 jsPDF: {
624 unit: 'pt',
625 format: 'a4',
626 orientation: 'portrait'
627 },
628 // 防止文字被切断的关键配置
629 pagebreak: {
630 mode: ['css', 'avoid-all', 'legacy'],
631 avoid: ['.avoid-break', '.el-card__header']
632 }
633 };
634
635 // 方法1: 直接生成并下载
636 try {
637 html2pdf().from(element).set(options).toPdf().get('pdf').then(pdf => {
638 let totalPages = pdf.internal.getNumberOfPages();
639
640 // 获取最后一页的结束位置
641 for (let i = 1; i <= totalPages; i++) {
642 pdf.setPage(i);
643 }
644
645 const pageInfo = pdf.internal.getCurrentPageInfo();
646
647 const pdfPageWidthPt = parseInt(pageInfo.pageContext?.mediaBox?.topRightX); // A4 width in pt
648 const pageHeightPt = parseInt(pageInfo.pageContext?.mediaBox?.topRightY);
649 const containerWidthPx = element.offsetWidth;
650 let lastThree: any = element.children[3];//产品
651 let lastCont: any = element.children[4];
652 let lastEle: any = element.children[5];
653 const pxToPtRatio = pdfPageWidthPt / containerWidthPx; // 每像素多少 pt
654 const topPt = element.offsetHeight * pxToPtRatio;
655 let yInPage: any = 0;
656 if (totalPages == 1) {
657 yInPage = pageHeightPt - 20;
658 } else if (totalPages == 2) {
659 if ((element.children[0].offsetHeight + element.children[2].offsetHeight + lastThree.offsetHeight) * pxToPtRatio > (pageHeightPt + 25) ) {
660 //第一页装不下产品。
661 yInPage = (Math.ceil((lastThree.offsetHeight + lastCont.offsetHeight) * pxToPtRatio) + 150)
662 } else if ((element.offsetHeight - lastEle.offsetHeight) * pxToPtRatio > (pageHeightPt + 25)) { //装不下策略信息
663 yInPage = (Math.ceil(lastCont.offsetHeight * pxToPtRatio) + 150);
664 } else {
665 yInPage = 70;
666 }
667 } else if (totalPages == 3) {
668 yInPage = Math.ceil(pageHeightPt / 2);
669 }
670
671 let blob = pdf.output('blob');
672 const formData = new FormData();
673 formData.append('image', blob, fileName); // 可以指定文件名
674 getSignatureFile(formData, { pageIndex: totalPages, yPosition: yInPage }).then((res: any) => {
675 if (res?.code == proxy.$passCode) {
676 if (!res.data) {
677 signFileLoading.value = false;
678 proxy.$ElMessage.error('生成的签名文件为空');
679 return;
680 }
681 // 将 base64 转换为二进制数据
682 const byteCharacters = atob(res.data);
683 const byteArrays: any = [];
684
685 for (let offset = 0; offset < byteCharacters.length; offset += 512) {
686 const slice = byteCharacters.slice(offset, offset + 512);
687 const byteNumbers = new Array(slice.length);
688
689 for (let i = 0; i < slice.length; i++) {
690 byteNumbers[i] = slice.charCodeAt(i);
691 }
692
693 const byteArray = new Uint8Array(byteNumbers);
694 byteArrays.push(byteArray);
695 }
696
697 // 创建 Blob 然后转换为 File
698 const blob = new Blob(byteArrays, { type: 'application/pdf' });
699 let fileObject = new File([blob], fileName, { type: 'application/pdf' });
700 getUpFileSignByUrl({ fileName: fileName })
701 .then((res: any) => {
702 obsUploadRequest({
703 signedUrl: res.data.signedUrl,
704 file: fileObject,
705 actualSignedRequestHeaders: res.data.actualSignedRequestHeaders
706 }).then(() => {
707 signFileLoading.value = false;
708 if (res.code == '00000') {
709 const fileUrl = res.data.signedUrl ? getPathUrl(res.data.signedUrl) : '';
710 signFileUrlInfo.value = {
711 name: fileName,
712 url: fileUrl
713 };
714 ElMessage.success('生成成功');
715 } else {
716 signFileLoading.value = false;
717 ElMessage.error('文件生成失败!');
718 }
719 })
720 })
721 .catch(() => {
722 signFileLoading.value = false;
723 ElMessage.error('文件生成失败');
724 });
725 // downloadPdf(res.data || '')
726 } else {
727 signFileLoading.value = false;
728 res?.msg && proxy.$ElMessage.error(res?.msg)
729 }
730 })
731 })
732 } catch {
733 signFileLoading.value = false;
734 }
735 }, 50)
736 }
737
738 const deleteSignFile = () => {
739 proxy.$openMessageBox('确定要删除该签名文件吗?', () => {
740 signFileUrlInfo.value = {};
741 proxy.$ElMessage({
742 type: "success",
743 message: "删除成功",
744 });
745 }, () => {
746 proxy.$ElMessage.info("已取消删除");
747 })
748 }
749
750 /** ------------------------- 履约信息 ------------------------- */
751
752 const execContractTableInfo = ref({
753 id: "exec-contract-table",
754 height: '214px',
755 fields: <any[]>[
756 { label: "序号", type: "index", width: TableColumnWidth.INDEX, align: "center" },
757 { label: "策略id", field: "strategyId", width: 260 },
758 { label: "操作行为", field: "action", width: 120 },
759 { label: "操作行为英文名称", field: "actionEnName", width: 140 },
760 { label: "约束条件", field: "constraintName", width: 120 },
761 { label: "约束条件英文名称", field: "constraintEnName", width: 140 },
762 { label: "约束条件运算符", field: "constraintOperatorCodeName", width: 140 },
763 { label: "约束条件值", field: "constraintValue", width: 150 },
764 { label: "执行结果", field: "result", width: 130 },
765 { label: "上报时间", field: "reportingTime", width: 170 },
766 ],
767 data: [],
768 showPage: true,
769 page: {
770 type: "normal",
771 rows: 0,
772 ...commonPageConfig,
773 },
774 actionInfo: {
775 show: false
776 },
777 loading: false
778 });
779
780 const pageChange = (info) => {
781 execContractTableInfo.value.page.curr = Number(info.curr);
782 execContractTableInfo.value.page.limit = Number(info.limit);
783 getStepFourExecInfo();
784 };
785
786 /** ------------------ 合约解除 ----------------------- */
787 const rejectInfoFormRef = ref();
788 const terminateTypeList: any = ref([]);
789
790 const rejectInfoFormItems = ref([{
791 label: '解除类型', //合约解除类型
792 type: 'select',
793 placeholder: '请选择',
794 field: 'terminateTypeCode',
795 default: 'TQ',
796 options: terminateTypeList.value,
797 filterable: true,
798 required: true,
799 visible: true,
800 disabled: false
801 }, {
802 label: '合约生效时间',
803 type: 'input',
804 placeholder: '-',
805 field: 'activationTime',
806 default: '',
807 disabled: true,
808 visible: true
809 }, {
810 label: '合约终止时间',
811 type: 'input',
812 placeholder: '-',
813 field: 'scheduledEndTime',
814 default: '',
815 disabled: true,
816 visible: true
817 }, {
818 label: "合同约定终止时间",
819 type: "datetime",
820 field: "agreedEndTime",
821 defaultTime: new Date(2000, 1, 1, 23, 59, 59),
822 default: null,
823 placeholder: "请选择",
824 clearable: true,
825 // disabledDate: (date) => {
826 // const today = new Date();
827 // // 将 today 设置为 00:00:00,只比较日期部分(本地时间)
828 // const todayStart = new Date(
829 // today.getFullYear(),
830 // today.getMonth(),
831 // today.getDate()
832 // );
833
834 // // 将 date 也转换为本地日期的开始时间(避免 UTC 问题)
835 // const dateStart = new Date(
836 // date.getFullYear(),
837 // date.getMonth(),
838 // date.getDate()
839 // );
840
841 // // 禁用 dateStart < todayStart 的日期(即今天之前)
842 // return dateStart.getTime() < todayStart.getTime();
843 // },
844 visible: true, //提前解除才展示
845 required: true,
846 }, {
847 label: '合约解除原因',
848 type: 'textarea',
849 placeholder: '请输入',
850 field: 'issueReason',
851 default: '',
852 rows: 2,
853 clearable: true,
854 required: true,
855 block: true,
856 visible: true, //提前解除才展示
857 maxlength: 200
858 }, {
859 label: '数据处理方式',
860 type: 'textarea',
861 placeholder: '请输入',
862 field: 'dataProcessingMethod',
863 default: '',
864 clearable: true,
865 required: true,
866 rows: 2,
867 block: true,
868 visible: true,
869 maxlength: 200
870 }, {
871 label: '数据处理指令',
872 type: 'textarea',
873 placeholder: '请输入',
874 field: 'dataProcessingInstructions',
875 default: '',
876 clearable: true,
877 required: true,
878 rows: 2,
879 block: true,
880 visible: true,
881 maxlength: 200
882 }, {
883 label: "数据处理证明",
884 tip: "单个文件不得大于20M",
885 type: "upload-file",
886 limitSize: 20,
887 limit: 1,
888 default: [],
889 field: "dataProcessingProof",
890 required: true,
891 },]);
892
893 const rejectInfoFormRules = ref({
894 terminateTypeCode: [required('请选择解除类型')],
895 agreedEndTime: [required('请选择合同约定终止时间')],
896 issueReason: [required('请填写合同解除原因')],
897 dataProcessingMethod: [required('请填写数据处理方式')],
898 dataProcessingInstructions: [required('请填写数据处理指令')],
899 dataProcessingProof: [requiredFiles()],
900 });
901
902 const handleRejectFormSelectChange = (val, row, info) => {
903 if (row.field == 'terminateTypeCode') {
904 rejectInfoFormItems.value[3].visible = val == 'TQ';
905 rejectInfoFormItems.value[4].visible = val == 'TQ';
906 rejectInfoFormItems.value.forEach(item => {
907 item.default = info[item.field];
908 })
909 }
910 }
911
912 /** --- 合约协商信息 -- */
913 const consultDetailInfo: any = ref({});
914
915 /** 约束运算符字典下拉 */
916 const operatorOptionList: any = ref([]);
917
918 /** 约束行为下拉列表 */
919 const constraintOptionsList: any = ref([]);
920
921 /** 策略操作行为下拉列表 */
922 const actionOptionsList: any = ref([]);
923
924 /** 获取第二步合约协商信息 */
925 const getStepTwoNegotiateInfo = (loading = true) => {
926 fullscreenLoading.value = true;
927 getContractNegotiate(route.query.guid).then((res: any) => {
928 loading && (fullscreenLoading.value = false);
929 if (res?.code == proxy.$passCode) {
930 consultDetailInfo.value = res.data || {};
931 let negotiateRecord = consultDetailInfo.value.negotiateRecord || {};
932 activities.value = [];
933 for (const key in negotiateRecord) {
934 activities.value.push({
935 timestamp: key,
936 type: 'primary',
937 hollow: true,
938 children: negotiateRecord[key] || []
939 })
940 }
941 } else {
942 res?.msg && proxy.$ElMessage.error(res?.msg)
943 }
944 })
945 }
946
947 /** 已签署过的详情信息 */
948 const signDetailInfo: any = ref([]);
949
950 const getStepThreeSignInfo = () => {
951 fullscreenLoading.value = true;
952 return getSignListInfo(route.query.guid).then((res: any) => {
953 // fullscreenLoading.value = false;
954 if (res?.code == proxy.$passCode) {
955 signDetailInfo.value = res.data || [];
956 } else {
957 res?.msg && proxy.$ElMessage.error(res?.msg)
958 }
959 })
960 }
961
962 /** 履约证明信息 */
963 // const execProofDetailInfo: any = ref({});
964
965 /** 获取履约执行信息和证明信息 */
966 const getStepFourExecInfo = () => {
967 // getContractProof(route.query.guid).then((res: any) => {
968 // if (res?.code == proxy.$passCode) {
969 // execProofDetailInfo.value = res.data || {};
970 // } else {
971 // res?.msg && proxy.$ElMessage.error(res?.msg)
972 // }
973 // })
974 execContractTableInfo.value.loading = true;
975 getContractExecList({
976 contractGuid: route.query.guid,
977 pageIndex: execContractTableInfo.value.page.curr,
978 pageSize: execContractTableInfo.value.page.limit,
979 }).then((res: any) => {
980 execContractTableInfo.value.loading = false;
981 if (res?.code == proxy.$passCode) {
982 const data = res.data || {};
983 execContractTableInfo.value.data = data.records || []
984 execContractTableInfo.value.page.limit = data.pageSize
985 execContractTableInfo.value.page.curr = data.pageIndex
986 execContractTableInfo.value.page.rows = data.totalRows
987 } else {
988 res?.msg && proxy.$ElMessage.error(res?.msg)
989 }
990 })
991 }
992
993 /** 终止合约信息详情 */
994 const terminateDetailInfo: any = ref({});
995
996 /** 获取第五步终止合约信息详情 */
997 const getStepTerminateInfo = () => {
998 fullscreenLoading.value = true;
999 getTerminateDetailInfo(route.query.guid).then((res: any) => {
1000 fullscreenLoading.value = false;
1001 if (res?.code == proxy.$passCode) {
1002 terminateDetailInfo.value = res.data || {};
1003 } else {
1004 res?.msg && proxy.$ElMessage.error(res?.msg)
1005 }
1006 })
1007 }
1008
1009 /** 企业注册认证信息详情,供签署时显示 */
1010 const signTenantDetailInfo: any = ref({});
1011
1012 onBeforeMount(() => {
1013 if (isDetail.value) { //若是查看详情,默认都展示第一步。
1014 currentStep.value = 1;
1015 }
1016 fullscreenLoading.value = true;
1017 let detailPs = getContractDetail(route.query.guid).then((res: any) => {
1018 detailType.value != 'sign' && (fullscreenLoading.value = false);
1019 if (res?.code == proxy.$passCode) {
1020 detailInfo.value = res.data || {};
1021 if (isDetail.value || detailType.value == 'reject') {
1022 detailInfo.value.negotiateEntityGuid && getStepTwoNegotiateInfo();
1023 detailInfo.value.signatureList?.length && getStepThreeSignInfo();
1024 detailInfo.value.contractStatus == '05' && getStepFourExecInfo();
1025 detailInfo.value.contractStatus == '06' && getStepFourExecInfo();
1026 detailInfo.value.contractStatus == '06' && getStepTerminateInfo();
1027 }
1028 let executionNodes = detailInfo.value.executionNodes || [];
1029 let provider = executionNodes.find(e => e.executionerTypeCode == '01');
1030 let user = executionNodes.find(e => e.executionerTypeCode == '02');
1031 nodeInfoFormItems.value[0].default = provider.executionerTypeName;
1032 nodeInfoFormItems.value[1].default = provider.executionerName;
1033 nodeInfoFormItems.value[2].default = provider.executionerId;
1034 nodeInfoFormItems.value[3].default = user.executionerTypeName;
1035 nodeInfoFormItems.value[4].default = user.executionerName;
1036 nodeInfoFormItems.value[5].default = user.executionerId;
1037 extendTableInfo.value.data = detailInfo.value.contractExpansions || [];
1038 productTableInfo.value.data = detailInfo.value.contractSubjects || [];
1039 } else {
1040 res?.msg && proxy.$ElMessage.error(res?.msg)
1041 }
1042 })
1043 // 企业来做协商处理,先调用详情接口。
1044 if (detailType.value == 'consult') {
1045 getStepTwoNegotiateInfo();
1046 getParamsList({ dictType: '约束运算符' }).then((res: any) => {
1047 if (res?.code == proxy.$passCode) {
1048 const data = res.data || [];
1049 operatorOptionList.value = data;
1050 } else {
1051 res?.msg && proxy.$ElMessage.error(res?.msg);
1052 }
1053 })
1054 getActionPolicyList().then((res: any) => {
1055 if (res?.code == proxy.$passCode) {
1056 const data = res.data || [];
1057 actionOptionsList.value = data;
1058 } else {
1059 res?.msg && proxy.$ElMessage.error(res?.msg);
1060 }
1061 })
1062 getConstraintPolicyList().then((res: any) => {
1063 if (res?.code == proxy.$passCode) {
1064 const data = res.data || [];
1065 constraintOptionsList.value = data;
1066 } else {
1067 res?.msg && proxy.$ElMessage.error(res?.msg);
1068 }
1069 })
1070 } else if (detailType.value == 'sign') {
1071 getStepTwoNegotiateInfo(false);
1072 fullscreenLoading.value = true;
1073 detailPs.then(() => {
1074 fullscreenLoading.value = true;
1075 let psSign = getStepThreeSignInfo();
1076 let psLogon = getEnterpriseData({
1077 logonUser: userData.tenantName == "非认证会员" ? userData.logonUser : tenantData.logonUser
1078 }).then((res: any) => {
1079 if (res?.code == proxy.$passCode) {
1080 signTenantDetailInfo.value = res.data || {};
1081 } else {
1082 res?.msg && proxy.$ElMessage.error(res?.msg);
1083 }
1084 });
1085 Promise.all([psSign, psLogon]).then(() => {
1086 fullscreenLoading.value = false;
1087 }).catch(() => {
1088 fullscreenLoading.value = false;
1089 })
1090 })
1091 } else if (detailType.value == 'reject') {
1092 let psType = getParamsList({ dictType: '合约解除类型' }).then((res: any) => {
1093 if (res?.code == proxy.$passCode) {
1094 const data = res.data || [];
1095 terminateTypeList.value = data;
1096 rejectInfoFormItems.value[0].options = terminateTypeList.value;
1097 } else {
1098 res?.msg && proxy.$ElMessage.error(res?.msg);
1099 }
1100 })
1101 // 详情跟类型都返回之后再设置选项
1102 Promise.all([detailPs, psType]).then((res: any) => {
1103 //选项待定,只有未到合同终止时间才可以选择提前解除。
1104 let endTime = detailInfo.value.endTime;
1105 let currDate = new Date();
1106 let endDate = new Date(endTime);
1107 if (currDate > endDate) { //终止时间大于当前时间,可以选择全部包括提前解除和到期解除
1108 rejectInfoFormItems.value[0].options = terminateTypeList.value.filter(t => t.value == 'DQ');
1109 rejectInfoFormItems.value[0].default = 'DQ'
1110 } else {
1111 rejectInfoFormItems.value[0].options = terminateTypeList.value.filter(t => t.value == 'TQ');
1112 rejectInfoFormItems.value[0].default = 'TQ'
1113 }
1114 rejectInfoFormItems.value[1].default = detailInfo.value.signatureTime;
1115 rejectInfoFormItems.value[2].default = detailInfo.value.endTime;
1116 rejectInfoFormItems.value[3].visible = rejectInfoFormItems.value[0].default == 'TQ';
1117 rejectInfoFormItems.value[4].visible = rejectInfoFormItems.value[0].default == 'TQ';
1118 })
1119 }
1120 });
1121
1122 onActivated(() => {
1123 let tab: any = userStore.tabbar.find((tab: any) => tab.fullPath === fullPath);
1124 if (tab) {
1125 if (detailType.value == 'consult') {
1126 document.title = `合约协商-${route.query.name}`;
1127 tab.meta.title = `合约协商-${route.query.name}`;
1128 } else if (detailType.value == 'sign') {
1129 document.title = `合约签署-${route.query.name}`;
1130 tab.meta.title = `合约签署-${route.query.name}`;
1131 } else if (detailType.value == 'reject') {
1132 document.title = `合约解除-${route.query.name}`;
1133 tab.meta.title = `合约解除-${route.query.name}`;
1134 } else {
1135 document.title = `合约详情-${route.query.name}`;
1136 tab.meta.title = `合约详情-${route.query.name}`;
1137 }
1138 }
1139 })
1140
1141 </script>
1142
1143 <template>
1144 <div class="container_wrap full" v-loading="fullscreenLoading">
1145 <div class="content_main panel">
1146 <div class="first-title-row">
1147 <div class="title-row">
1148 <div class="title"><ellipsis-tooltip :content="detailInfo?.contractName || '--'" class-name="w100f"
1149 :refName="'tooltipOver' + 'contractName'"></ellipsis-tooltip></div>
1150 <el-tag :type="tagType(detailInfo, 'contractStatus')">{{ tagMethod(detailInfo, 'contractStatus') }}</el-tag>
1151 </div>
1152 <div class="list_panel mt6">
1153 <div class="list_item">
1154 <span class="item_label">合约编号:</span>
1155 <span class="item_value"><ellipsis-tooltip :content="detailInfo?.contractId || '--'"
1156 class-name="w100f mr8-i" :refName="'tooltipOver' + 'contractId'"></ellipsis-tooltip></span>
1157 </div>
1158 <div class="list_item">
1159 <span class="item_label">合约版本:</span>
1160 <span class="item_value">{{ detailInfo?.policys?.at(-1)?.version || 'v1.0.0' }}</span>
1161 </div>
1162 <div class="list_item">
1163 <span class="item_label">创建时间:</span>
1164 <span class="item_value">{{ detailInfo.createTime || '--' }}</span>
1165 </div>
1166 <div class="list_item">
1167 <span class="item_label">发起主体:</span>
1168 <span class="item_value"><ellipsis-tooltip :content="detailInfo?.tenantName || '--'"
1169 class-name="w100f mr8-i" :refName="'tooltipOver' + 'tenantName'"></ellipsis-tooltip></span>
1170 </div>
1171 <div class="list_item">
1172 <span class="item_label">签署时间:</span>
1173 <span class="item_value">{{ detailInfo.signatureTime || '--' }}</span>
1174 </div>
1175 <div class="list_item">
1176 <span class="item_label">有效期:</span>
1177 <span class="item_value">{{ !detailInfo.signatureTime ? '--' : (detailInfo.signatureTime + '至' +
1178 detailInfo.endTime) }}</span>
1179 </div>
1180 </div>
1181 </div>
1182 <div class="empty-div"></div>
1183 <div class="progress">
1184 <div :class="{ 'active': true, 'no-select': detailType && currentStep != 1 || (isDetail && currentStep > 1) }"
1185 style="cursor: pointer;" @click="handleClickStep(1)">1、合约信息</div>
1186 <div
1187 :class="{ 'bg-gray': true, 'active': !!detailType || (isDetail && detailInfo?.negotiateEntityGuid), 'cursor-pointer': !!detailType || (isDetail && detailInfo?.negotiateEntityGuid), 'no-select': (currentStep && currentStep != 2) || (detailType != 'consult' && !currentStep) }"
1188 @click="(!!detailType || (isDetail && detailInfo?.negotiateEntityGuid)) ? handleClickStep(2) : () => { }">
1189 <div class="triangle-box">
1190 <div
1191 :class="{ 'triangle': true, 'active': true, 'no-select': detailType && currentStep != 1 || (isDetail && currentStep > 1) }">
1192 </div>
1193 </div>
1194 2、合约协商
1195 </div>
1196 <div :class="{
1197 '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),
1198 'no-select': (currentStep && currentStep != 3) || (detailType != 'sign' && !currentStep)
1199 }"
1200 @click="(detailType == 'sign' || detailType == 'keepAgree' || detailType == 'reject' || (isDetail && detailInfo.signatureList?.length)) ? handleClickStep(3) : () => { }">
1201 <div class="triangle-box">
1202 <div
1203 :class="{ 'triangle': true, 'active': !!detailType || (isDetail && detailInfo?.negotiateEntityGuid), 'no-select': (currentStep && currentStep != 2) || (detailType != 'consult' && !currentStep) }">
1204 </div>
1205 </div>
1206 3、合约签署
1207 </div>
1208 <div :class="{
1209 'bg-gray': true, 'active': detailType == 'keepAgree' || detailType == 'reject' || (isDetail && detailInfo.contractStatus == '06'), 'cursor-pointer': detailType == 'keepAgree' || detailType == 'reject' || (isDetail && detailInfo.contractStatus == '06'),
1210 'no-select': (currentStep && currentStep != 4) || (detailType != 'keepAgree' && !currentStep)
1211 }"
1212 @click="(detailType == 'keepAgree' || detailType == 'reject' || (isDetail && detailInfo.contractStatus == '06')) ? handleClickStep(4) : () => { }">
1213 <div class="triangle-box">
1214 <div :class="{
1215 'triangle': true, 'active': detailType == 'sign' || detailType == 'keepAgree' || detailType == 'reject' || (isDetail && detailInfo.signatureList?.length),
1216 'no-select': (currentStep && currentStep != 3) || (detailType != 'sign' && !currentStep)
1217 }">
1218 </div>
1219 </div>
1220 4、合约履行
1221 </div>
1222 <div v-show="detailType == 'reject' || (isDetail && detailInfo.contractStatus == '06')" :class="{
1223 'bg-gray': true, 'active': detailType == 'reject' || (isDetail && detailInfo.contractStatus == '06'), 'cursor-pointer': detailType == 'reject' || (isDetail && detailInfo.contractStatus == '06'),
1224 'no-select': (currentStep && currentStep != 5) || (detailType != 'reject' && !currentStep)
1225 }"
1226 @click="(detailType == 'reject' || (isDetail && detailInfo.contractStatus == '06')) ? handleClickStep(5) : () => { }">
1227 <div class="triangle-box">
1228 <div
1229 :class="{ 'triangle': true, 'active': detailType == 'keepAgree' || detailType == 'reject' || (isDetail && detailInfo.contractStatus == '06'), 'no-select': (currentStep && currentStep != 4) || (detailType != 'keepAgree' && !currentStep) }">
1230 </div>
1231 </div>
1232 5、合约解除
1233 </div>
1234 </div>
1235 <ContentWrap v-show="currentStep == 1" id="base-info" title="合约信息" expandSwicth style="margin-top: 15px"
1236 :isExpand="expandBase" @expand="(v) => (expandBase = v)" description="">
1237 <div class="list_panel">
1238 <div class="list_item">
1239 <span class="item_label">合约名称:</span>
1240 <span class="item_value"><ellipsis-tooltip :content="detailInfo?.contractName || '--'"
1241 class-name="w100f mr8-i" :refName="'tooltipOver' + 'contractName1'"></ellipsis-tooltip></span>
1242 </div>
1243 <div class="list_item">
1244 <span class="item_label">签署模式:</span>
1245 <span class="item_value"><ellipsis-tooltip :content="detailInfo?.signModeName || '--'"
1246 class-name="w100f mr8-i" :refName="'tooltipOver' + 'signModeName'"></ellipsis-tooltip></span>
1247 </div>
1248 <div class="list_item">
1249 <span class="item_label">终止时间:</span>
1250 <span class="item_value"><ellipsis-tooltip :content="detailInfo?.endTime || '--'" class-name="w100f mr8-i"
1251 :refName="'tooltipOver' + 'endTime'"></ellipsis-tooltip></span>
1252 </div>
1253 <div class="list_item is_block">
1254 <span class="item_label">合约简介:</span>
1255 <span class="item_value">{{ detailInfo.contractAbstract || '--' }}</span>
1256 </div>
1257 </div>
1258 <Table ref="extendTableRef" :tableInfo="extendTableInfo" class="fiveRow-table" />
1259 </ContentWrap>
1260 <ContentWrap v-show="currentStep == 1" id="product-info" title="合约标的" expandSwicth style="margin-top: 15px"
1261 :isExpand="expandInfo" @expand="(v) => (expandInfo = v)" description="">
1262 <Table ref="productTableRef" :tableInfo="productTableInfo" class="fiveRow-table" />
1263 </ContentWrap>
1264 <ContentWrap v-show="currentStep == 1" id="policy-info" title="合约策略" expandSwicth style="margin-top: 15px"
1265 :isExpand="expandPolicy" @expand="(v) => (expandPolicy = v)" description="">
1266 <div class="h-title">策略执行节点</div>
1267 <Form ref="nodeInfoFormRef" formId="node-info-form" :itemList="nodeInfoFormItems" col="col3" />
1268 <StrategyTable ref="strategyTableRef" :show-title="true" :is-look="true" :value="detailInfo.policys || []">
1269 </StrategyTable>
1270 </ContentWrap>
1271 <ContentWrap v-show="detailType == 'consult' && currentStep != 1" id="policy-info-consult" title="合约策略"
1272 expandSwicth style="margin-top: 15px" :isExpand="expandPolicyConsult" @expand="(v) => (expandPolicyConsult = v)"
1273 description="">
1274 <StrategyTable ref="strategyTableEditRef" :show-title="true" :value="consultDetailInfo?.policys"
1275 :operatorOptionList="operatorOptionList" :actionOptionsList="actionOptionsList"
1276 :constraintOptionsList="constraintOptionsList"></StrategyTable>
1277 </ContentWrap>
1278 <ContentWrap v-show="detailType == 'consult' || currentStep == 2" id="history-info-consult" title="协商记录"
1279 expandSwicth style="margin-top: 15px" :isExpand="expandConsultHistory"
1280 @expand="(v) => (expandConsultHistory = v)" description="">
1281 <el-timeline style="max-width: 900px">
1282 <el-timeline-item v-for="(activity, index) in activities" :key="index" :timestamp="activity.timestamp"
1283 :hollow="activity.hollow" :type="activity.type" placement="top">
1284 <div v-for="(child, childIndex) of activity.children" class="row-per"
1285 :style="{ 'margin-top': childIndex > 0 ? '8px' : '0px' }">
1286 <div
1287 :class="child.negotiateEntityType == '数据提供方' ? 'label' : (child.negotiateEntityType == '数据服务方' ? 'label2' : 'label1')">
1288 {{ child.negotiateEntityType }}
1289 </div>
1290 <div style="width: 350px;" class="desc"> <ellipsis-tooltip
1291 :content="'协商主体:' + (child?.negotiateEntityName ?? '--')" class-name="w100f"
1292 :refName="'tooltipOverTenant'"></ellipsis-tooltip></div>
1293 <div style="width: 200px;" class="desc">版本号:<span class="main" @click="clickVersion(child)">{{
1294 child.version }}</span></div>
1295 <div style="width: 250px;" class="desc">{{ '提交时间:' + child.submitTime }}</div>
1296 </div>
1297 </el-timeline-item>
1298 </el-timeline>
1299 </ContentWrap>
1300 <ContentWrap v-show="!currentStep && detailType == 'sign' || currentStep == 3" id="sign-info" title="合约签署"
1301 expandSwicth style="margin-top: 15px" :isExpand="expandSign" @expand="(v) => (expandSign = v)" description="">
1302 <template v-for="(item, index) in signDetailInfo">
1303 <div :class="{ 'h-title': true, 'mt6': index > 0 }">{{ item.executionerTypeName }}</div>
1304 <div class="list_panel">
1305 <div class="list_item wrap">
1306 <span class="item_label">签署主体标识</span>
1307 <span class="item_value"><ellipsis-tooltip :content="item?.signatureEntityId || '--'"
1308 class-name="w100f mr8-i" :refName="'tooltipOver' + 'tenantCode'"></ellipsis-tooltip></span>
1309 </div>
1310 <div class="list_item wrap">
1311 <span class="item_label">签署主体名称</span>
1312 <span class="item_value row"><ellipsis-tooltip :content="item?.signatureEntityName || '--'"
1313 class-name="w100f mr8-i custom" :refName="'tooltipOver' + 'signatureEntityName'"></ellipsis-tooltip>
1314 <div :class="item.executionerTypeCode == '02' ? 'label1' : 'label'">{{ item.executionerTypeName }}</div>
1315 </span>
1316 </div>
1317 <div class="list_item isFile">
1318 <span class="item_label">身份凭证证书</span>
1319 <template v-if="item?.identityCertificate?.url">
1320 <span class="item_value link" @click="viewResultVoucherFile(item?.identityCertificate?.url)">查看</span>
1321 </template>
1322 <span v-else class="item_value">--</span>
1323 </div>
1324 <!-- 没有上传就不显示 -->
1325 <div class="list_item isFile">
1326 <span class="item_label">附件</span>
1327 <template v-if="item?.attachment?.url">
1328 <showFile :file="[item.attachment]"></showFile>
1329 </template>
1330 <span v-else class="item_value">--</span>
1331 </div>
1332 <div class="list_item isFile">
1333 <span class="item_label">签署签名</span>
1334 <template v-if="item?.signature?.url">
1335 <showFile :file="[item.signature]"></showFile>
1336 </template>
1337 <span v-else class="item_value">--</span>
1338 </div>
1339 <div class="list_item wrap">
1340 <span class="item_label">签署时间</span>
1341 <span class="item_value"><ellipsis-tooltip :content="item?.signatureTime || '--'" class-name="w100f mr8-i"
1342 :refName="'tooltipOver' + 'signatureTime'"></ellipsis-tooltip></span>
1343 </div>
1344 </div>
1345 </template>
1346 <template v-if="detailType == 'sign'">
1347 <div class="h-title mt6">{{ userData.tenantGuid == detailInfo.dataUserGuid ? '数据使用方' : '数据提供方' }}</div>
1348 <div class="list_panel">
1349 <div class="list_item wrap">
1350 <span class="item_label">签署主体标识</span>
1351 <span class="item_value"><ellipsis-tooltip :content="signTenantDetailInfo?.socialCreditCode || '--'"
1352 class-name="w100f mr8-i" :refName="'tooltipOver' + 'tenantCode'"></ellipsis-tooltip></span>
1353 </div>
1354 <div class="list_item wrap">
1355 <span class="item_label">签署主体名称</span>
1356 <span class="item_value row"><ellipsis-tooltip :content="userData?.tenantName || '--'"
1357 class-name="w100f mr8-i custom" :refName="'tooltipOver' + 'tenantName'"></ellipsis-tooltip>
1358 <div :class="userData.tenantGuid == detailInfo.dataUserGuid ? 'label1' : 'label'">{{ userData.tenantGuid
1359 == detailInfo.dataUserGuid ? '数据使用方' : '数据提供方' }}</div>
1360 </span>
1361 </div>
1362 <div class="list_item isFile">
1363 <span class="item_label">身份凭证证书</span>
1364 <template v-if="signTenantDetailInfo?.trustedIdentityCredential">
1365 <span class="item_value link"
1366 @click="viewResultVoucherFile(signTenantDetailInfo?.trustedIdentityCredential)">查看</span>
1367 </template>
1368 <span v-else class="item_value">--</span>
1369 </div>
1370 </div>
1371 <div style="display: flex">
1372 <Form class="mt6" ref="signInfoFormRef" formId="sign-info-form" :itemList="signInfoFormItems"
1373 :rules="signInfoFormRules" col="col3" style="width: 33.33%;" />
1374 <div class="sign-main" style="width: 33.33%;">
1375 <div class="item-label" style="margin-bottom: 2px;">签署签名<span class="required">*</span></div>
1376 <el-button v-show="!signFileUrlInfo?.url" type="primary" :loading="signFileLoading"
1377 @click="geneSignFile">生成签名文件</el-button>
1378 <template v-if="signFileUrlInfo?.url">
1379 <showFile :file="[signFileUrlInfo]" :show-remove="true" @deleteFile="deleteSignFile"></showFile>
1380 </template>
1381 </div>
1382 </div>
1383 </template>
1384 </ContentWrap>
1385 <el-checkbox style="margin-top: 4px;" v-show="detailType == 'sign' && (!currentStep || currentStep == 3)"
1386 v-model="confirmAuth">确认提交代表您已知晓本次签署行为所包含的责任义务,并授权允许平台使用本次操作的系统日志作为纠纷处理证明。</el-checkbox>
1387 <ContentWrap v-show="!currentStep && detailType == 'keepAgree' || currentStep == 4" id="keep-info"
1388 title="合约执行履约信息" expandSwicth style="margin-top: 15px" :isExpand="expandKeep" @expand="(v) => (expandKeep = v)"
1389 description="">
1390 <template v-if="execContractTableInfo.data?.length">
1391 <!-- <div class="h-title">执行信息</div> -->
1392 <el-table ref="execContractTableRef" :data="execContractTableInfo.data" :highlight-current-row="true" stripe
1393 v-loading="execContractTableInfo.loading" border height="100%" tooltip-effect="light" row-key="guid" :style="{
1394 width: '100%',
1395 height: '300px',
1396 display: 'inline-block',
1397 }">
1398 <el-table-column type="expand">
1399 <template #default="scope">
1400 <div class="h-title mt6">履约证明</div>
1401 <div class="list_panel" style="width: 1000px;">
1402 <div class="list_item">
1403 <span class="item_label">日志摘要:</span>
1404 <span class="item_value"><ellipsis-tooltip :content="scope.row?.contractProof?.logHash || '--'"
1405 class-name="w100f mr8-i" :refName="'tooltipOver' + 'logHash'"></ellipsis-tooltip></span>
1406 </div>
1407 <div class="list_item">
1408 <span class="item_label">区块链交易ID:</span>
1409 <span class="item_value row"><ellipsis-tooltip
1410 :content="scope.row?.contractProof?.blockchainTx || '--'" class-name="w100f mr8-i"
1411 :refName="'tooltipOver' + 'blockchainTx'"></ellipsis-tooltip>
1412 </span>
1413 </div>
1414 <div class="list_item">
1415 <span class="item_label">发送时间:</span>
1416 <span class="item_value"><ellipsis-tooltip :content="scope.row?.contractProof?.sendTime || '--'"
1417 class-name="w100f mr8-i" :refName="'tooltipOver' + 'sendTime'"></ellipsis-tooltip></span>
1418 </div>
1419 <div class="list_item" style="width: 50%;">
1420 <span class="item_label">履约连接器签名:</span>
1421 <!-- <template v-if="scope.row?.signature?.url">
1422 <showFile :file="[execProofDetailInfo.signature]"></showFile>
1423 </template> -->
1424 <span class="item_value">{{ scope.row?.contractProof?.signature || '--' }}</span>
1425 </div>
1426 </div>
1427 </template>
1428 </el-table-column>
1429 <el-table-column v-for="(item, i) in execContractTableInfo.fields" :label="item.label" :width="item.width"
1430 :align="item.align" :prop="item.field" show-overflow-tooltip>
1431 <template #default="scope">
1432 <span v-if="item.type == 'index'">
1433 {{
1434 execContractTableInfo.page.curr !== undefined
1435 ? (execContractTableInfo.page.curr - 1) * execContractTableInfo.page.limit + scope.$index + 1
1436 : scope.$index + 1
1437 }}
1438 </span>
1439 <!-- <el-tag v-else-if="item.type == 'tag'"
1440 :type="item.tagType ? item.tagType(scope) : tagType(scope.row, item.field)">{{
1441 item.getName ? item.getName(scope) : tagMethod(scope.row, item.field)
1442 }}</el-tag> -->
1443 <span v-else>
1444 {{ item.getName ? item.getName(scope) : scope.row[item.field] !== 0 && !scope.row[item.field]
1445 ?
1446 "--" : scope.row[item.field] }}
1447 </span>
1448 </template>
1449 </el-table-column>
1450 </el-table>
1451 <PageNav :class="[execContractTableInfo.page.type]" :pageInfo="execContractTableInfo.page"
1452 @pageChange="pageChange" />
1453 <!-- <Table ref="execContractTableRef" :tableInfo="execContractTableInfo" class="fiveRow-table" /> -->
1454 </template>
1455 <div class="empty-content" v-if="!execContractTableInfo.data?.length">
1456 <img src="../../assets/images/empty-data.png" :style="{ width: '168px', height: '96px' }" />
1457 <div class="empty-text">暂无履约数据</div>
1458 </div>
1459 </ContentWrap>
1460 <ContentWrap v-show="!isDetail && (!currentStep && detailType == 'reject' || currentStep == 5)" id="reject-info"
1461 title="合约解除信息" expandSwicth style="margin-top: 15px" :isExpand="expandReject"
1462 @expand="(v) => (expandReject = v)" description="">
1463 <Form ref="rejectInfoFormRef" formId="reject-info-form" :itemList="rejectInfoFormItems"
1464 :rules="rejectInfoFormRules" col="col3" @select-change="handleRejectFormSelectChange" />
1465 </ContentWrap>
1466 <ContentWrap v-show="isDetail && currentStep == 5" id="reject-info" title="合约解除信息" expandSwicth
1467 style="margin-top: 15px" :isExpand="expandReject" @expand="(v) => (expandReject = v)" description="">
1468 <div class="list_panel">
1469 <div class="list_item">
1470 <span class="item_label">解除类型:</span>
1471 <span class="item_value"><ellipsis-tooltip :content="terminateDetailInfo?.terminateTypeCodeName || '--'"
1472 class-name="w100f mr8-i" :refName="'tooltipOver' + 'terminateTypeCodeName'"></ellipsis-tooltip></span>
1473 </div>
1474 <div class="list_item">
1475 <span class="item_label">合同生效时间:</span>
1476 <span class="item_value row">{{ terminateDetailInfo?.activationTime || '--' }}
1477 </span>
1478 </div>
1479 <div class="list_item">
1480 <span class="item_label">合同终止时间:</span>
1481 <span class="item_value row">{{ terminateDetailInfo?.scheduledEndTime || '--' }}
1482 </span>
1483 </div>
1484 <div class="list_item" v-if="terminateDetailInfo.terminateTypeCode == 'TQ'">
1485 <span class="item_label">合同约定终止时间:</span>
1486 <span class="item_value row">{{ terminateDetailInfo?.agreedEndTime || '--' }}
1487 </span>
1488 </div>
1489 <div class="list_item">
1490 <span class="item_label">合同解除时间:</span>
1491 <span class="item_value row">{{ terminateDetailInfo?.issueTime || '--' }}
1492 </span>
1493 </div>
1494 <div class="list_item is_block" v-if="terminateDetailInfo.terminateTypeCode == 'TQ'">
1495 <span class="item_label">合约解除原因:</span>
1496 <span class="item_value row">{{ terminateDetailInfo?.issueReason || '--' }}
1497 </span>
1498 </div>
1499 <div class="list_item is_block">
1500 <span class="item_label">数据处理方式:</span>
1501 <span class="item_value row">{{ terminateDetailInfo?.dataProcessingMethod || '--' }}
1502 </span>
1503 </div>
1504 <div class="list_item is_block">
1505 <span class="item_label">数据处理指令:</span>
1506 <span class="item_value row">{{ terminateDetailInfo?.dataProcessingInstructions || '--' }}
1507 </span>
1508 </div>
1509 <div class="list_item isFile">
1510 <span class="item_label">数据处理证明:</span>
1511 <template v-if="terminateDetailInfo?.dataProcessingProof?.url">
1512 <showFile :file="[terminateDetailInfo?.dataProcessingProof]"></showFile>
1513 </template>
1514 <span v-else class="item_value">--</span>
1515 </div>
1516 </div>
1517 </ContentWrap>
1518 </div>
1519 <div class="tool_btns">
1520 <div class="btns">
1521 <el-button v-for="btn in toolBtns" :type="btn.type" :disabled="btn.disabled" :plain="btn.plain"
1522 @click="btnHandles[btn.value]">{{
1523 btn.label
1524 }}</el-button>
1525 </div>
1526 </div>
1527 <Dialog ref="dialogRef" :dialogInfo="detailDialogInfo" class="policy-table-detail"
1528 @btnClick="handleVersionDialogBtnClick">
1529 <template #extra-content>
1530 <div v-loading="detailDialogInfo.contentLoading">
1531 <StrategyTable ref="strategyTableDetailRef" :show-title="true" :is-look="true" :value="versionDetail">
1532 </StrategyTable>
1533 </div>
1534 </template>
1535 </Dialog>
1536 <ConfirmDialog :visible="confirmDialogInfo.dialogVisible" :type="confirmDialogInfo.type"
1537 :msg="confirmDialogInfo.msg[confirmDialogInfo.type]" @btn-click="handleConfirmDialog"></ConfirmDialog>
1538 <div id="pdf-content" style="display: none;">
1539 <div class="first-title-row" style="padding: 0px;">
1540 <div class="title-row" style="align-items: flex-start;">
1541 <div class="title" style="white-space: pre-wrap;word-break: break-all;">{{ detailInfo?.contractName || '--' }}
1542 </div>
1543 <!-- <el-tag :type="tagType(detailInfo, 'contractStatus')">{{ tagMethod(detailInfo, 'contractStatus') }}</el-tag> -->
1544 </div>
1545 <div class="list_panel two-per">
1546 <div class="list_item" style=" width: 55%;">
1547 <span class="item_label">合约编号:</span>
1548 <span class="item_value"><ellipsis-tooltip :content="detailInfo?.contractId || '--'"
1549 class-name="w100f mr8-i" :refName="'tooltipOver' + 'contractId'"></ellipsis-tooltip></span>
1550 </div>
1551 <div class="list_item" style=" width: 45%;">
1552 <span class="item_label">合约版本:</span>
1553 <span class="item_value">{{ detailInfo?.policys?.at(-1)?.version || 'v1.0.0' }}</span>
1554 </div>
1555 <div class="list_item" style=" width: 55%;">
1556 <span class="item_label">发起主体:</span>
1557 <span class="item_value">{{ detailInfo?.tenantName || '--' }}</span>
1558 </div>
1559 <div class="list_item" style=" width: 45%;">
1560 <span class="item_label">创建时间:</span>
1561 <span class="item_value">{{ detailInfo.createTime || '--' }}</span>
1562 </div>
1563 <div class="list_item" style=" width: 55%;">
1564 <span class="item_label">有效期:</span>
1565 <span class="item_value">{{ !detailInfo.signatureTime ? '--' : (detailInfo.signatureTime + '至' +
1566 detailInfo.endTime) }}</span>
1567 </div>
1568 <div class="list_item" style=" width: 45%;">
1569 <span class="item_label">签署时间:</span>
1570 <span class="item_value">{{ detailInfo.signatureTime || '--' }}</span>
1571 </div>
1572 </div>
1573 </div>
1574 <div class="empty-div"></div>
1575 <ContentWrap id="base-info" title="合约信息" style="margin-top: 12px" :isExpand="expandBase"
1576 @expand="(v) => (expandBase = v)" description="">
1577 <div class="list_panel two-per">
1578 <div class="list_item is_block">
1579 <span class="item_label">合约名称:</span>
1580 <span class="item_value">{{ detailInfo?.contractName || '--' }}</span>
1581 </div>
1582 <div class="list_item">
1583 <span class="item_label">签署模式:</span>
1584 <span class="item_value"><ellipsis-tooltip :content="detailInfo?.signModeName || '--'"
1585 class-name="w100f mr8-i" :refName="'tooltipOver' + 'signModeName'"></ellipsis-tooltip></span>
1586 </div>
1587 <div class="list_item">
1588 <span class="item_label">终止时间:</span>
1589 <span class="item_value"><ellipsis-tooltip :content="detailInfo?.endTime || '--'" class-name="w100f mr8-i"
1590 :refName="'tooltipOver' + 'endTime'"></ellipsis-tooltip></span>
1591 </div>
1592 <div class="list_item is_block">
1593 <span class="item_label">合约简介:</span>
1594 <span class="item_value">{{ detailInfo.contractAbstract || '--' }}</span>
1595 </div>
1596 </div>
1597 <Table v-if="exportExtendTableInfo.data?.length" ref="extendTableRef" :tableInfo="exportExtendTableInfo" />
1598 </ContentWrap>
1599 <ContentWrap id="product-info" title="合约标的" style="margin-top: 12px;" :isExpand="expandInfo"
1600 @expand="(v) => (expandInfo = v)" description="">
1601 <Table ref="productTableRef" :tableInfo="exportProductTableInfo" />
1602 </ContentWrap>
1603 <ContentWrap id="policy-info" title="合约策略" style="margin-top: 12px;" :isExpand="expandPolicy"
1604 @expand="(v) => (expandPolicy = v)" description="">
1605 <div class="h-title">策略执行节点</div>
1606 <!-- <Form ref="nodeInfoFormRef" formId="node-info-form" :itemList="nodeInfoFormItems" col="col3" /> -->
1607 <div class="list_panel">
1608 <div class="list_item wrap" style="width: 20%;">
1609 <span class="item_label">策略执行者类型</span>
1610 <span class="item_value"><ellipsis-tooltip :content="nodeInfoFormItems[0].default || '--'"
1611 class-name="w100f mr8-i" :refName="'tooltipOver1'"></ellipsis-tooltip></span>
1612 </div>
1613 <div class="list_item wrap" style="width: 40%;">
1614 <span class="item_label">策略执行者名称</span>
1615 <span class="item_value"><ellipsis-tooltip :content="nodeInfoFormItems[1].default || '--'"
1616 class-name="w100f mr8-i custom" :refName="'tooltipOver2'"></ellipsis-tooltip>
1617 </span>
1618 </div>
1619 <div class="list_item wrap" style="width: 40%;">
1620 <span class="item_label">策略执行者标识</span>
1621 <span class="item_value"><ellipsis-tooltip :content="nodeInfoFormItems[2].default || '--'"
1622 class-name="w100f mr8-i custom" :refName="'tooltipOver3'"></ellipsis-tooltip>
1623 </span>
1624 </div>
1625 <div class="list_item wrap" style="width: 20%;">
1626 <span class="item_label">策略执行者类型</span>
1627 <span class="item_value"><ellipsis-tooltip :content="nodeInfoFormItems[3].default || '--'"
1628 class-name="w100f mr8-i" :refName="'tooltipOver4'"></ellipsis-tooltip></span>
1629 </div>
1630 <div class="list_item wrap" style="width: 40%;">
1631 <span class="item_label">策略执行者名称</span>
1632 <span class="item_value"><ellipsis-tooltip :content="nodeInfoFormItems[4].default || '--'"
1633 class-name="w100f mr8-i custom" :refName="'tooltipOver5'"></ellipsis-tooltip>
1634 </span>
1635 </div>
1636 <div class="list_item wrap" style="width: 40%;">
1637 <span class="item_label">策略执行者标识</span>
1638 <span class="item_value"><ellipsis-tooltip :content="nodeInfoFormItems[5].default || '--'"
1639 class-name="w100f mr8-i custom" :refName="'tooltipOver6'"></ellipsis-tooltip>
1640 </span>
1641 </div>
1642 </div>
1643 <StrategyTable ref="strategyTableRef" :show-title="true" :is-look="true" :isReport="true"
1644 :value="detailInfo.policys || []">
1645 </StrategyTable>
1646 </ContentWrap>
1647 <div class="sign-file-main">
1648 <div class="title-desc">{{ userData.tenantGuid == detailInfo.dataUserGuid ? '数据使用方' : '数据提供方' }}签章:</div>
1649 <div class="title-desc">法人签名:</div>
1650 </div>
1651 </div>
1652 </div>
1653 </template>
1654
1655 <style lang="scss" scoped>
1656 .container_wrap {
1657 overflow: hidden;
1658
1659 .content_main {
1660 height: calc(100% - 45px);
1661 overflow: hidden auto;
1662
1663 &.panel {
1664 padding: 0 16px 16px;
1665 }
1666 }
1667 }
1668
1669 .first-title-row {
1670 background-color: #fff;
1671 padding: 14px 0px 0px 0px;
1672
1673 .title-row {
1674 display: flex;
1675 flex-direction: row;
1676 align-items: center;
1677
1678 .title {
1679 font-size: 18px;
1680 color: #212121;
1681 line-height: 27px;
1682 font-weight: 600;
1683 max-width: calc(100% - 120px);
1684 margin-right: 8px;
1685 }
1686 }
1687 }
1688
1689 .list_panel {
1690 display: flex;
1691 flex-wrap: wrap;
1692 display: flex;
1693 align-items: flex-start;
1694
1695 &.main {
1696 .list_item {
1697 width: 25%;
1698 }
1699 }
1700
1701 &.two-per {
1702 .list_item {
1703 width: 50%;
1704 }
1705 }
1706
1707 .list_item {
1708 width: 33.33%;
1709 line-height: 32px;
1710 font-size: 14px;
1711 color: var(--el-text-color-regular);
1712 display: flex;
1713 justify-content: space-between;
1714 min-width: 120px;
1715
1716 &.wrap {
1717 flex-direction: column;
1718
1719 .item_value {
1720 padding: 0px 4px 0px 0px;
1721 }
1722 }
1723
1724 .item_label {
1725 text-align: left;
1726 }
1727
1728 .item_value {
1729 color: var(--el-color-regular);
1730 padding: 0 4px;
1731 flex: 1;
1732 text-align: justify;
1733 min-width: 0;
1734
1735 &.link {
1736 color: var(--el-color-primary);
1737 cursor: pointer;
1738 }
1739
1740 &.row {
1741 display: flex;
1742 justify-content: flex-start;
1743 align-items: center;
1744 flex-direction: row;
1745
1746 :deep(.custom) {
1747 width: auto;
1748 max-width: calc(100% - 75px);
1749 }
1750 }
1751 }
1752
1753 &.is_block {
1754 width: 100%;
1755
1756 .item_value {
1757 white-space: pre-wrap;
1758 }
1759 }
1760
1761 &.isFile {
1762 // width: 50%;
1763 display: flex;
1764 flex-direction: column;
1765 justify-content: space-between;
1766
1767 .item_label {
1768 // width: 100px;
1769 // text-align: right;
1770 flex-shrink: 0;
1771 }
1772
1773 .item_value {
1774 flex: 1 1 0%;
1775 min-width: 100px;
1776 white-space: pre-wrap;
1777 }
1778 }
1779 }
1780 }
1781
1782 .empty-div {
1783 height: 4px;
1784 background: #E2E7EF;
1785 margin: 12px -16px 0px;
1786 }
1787
1788 .mt6 {
1789 margin-top: 6px;
1790 }
1791
1792 .tool_btns {
1793 height: 44px;
1794 margin: 0 -8px;
1795 display: flex;
1796 justify-content: center;
1797 align-items: center;
1798 border-top: 1px solid #d9d9d9;
1799 }
1800
1801 .h-title {
1802 font-size: 14px;
1803 color: #212121;
1804 font-weight: 600;
1805 margin-bottom: 4px;
1806 }
1807
1808 :deep(.el-tag.el-tag--primary) {
1809 color: #0E5FD8;
1810 background: #F2F9FF;
1811 border: 1px solid rgba(224, 239, 255, 1);
1812 min-width: 56px;
1813 }
1814
1815 .progress {
1816 width: 100%;
1817 display: flex;
1818 justify-content: start;
1819 margin-top: 12px;
1820 margin-bottom: 12px;
1821 }
1822
1823 .progress>div {
1824 width: 25%;
1825 height: 28px;
1826 line-height: 28px;
1827 color: white;
1828 position: relative;
1829 text-align: center;
1830
1831 &.bg-gray {
1832 color: #666;
1833 background-color: #F2F2F2;
1834 }
1835
1836 &.active {
1837 background-color: #5797FF;
1838 color: #fff;
1839
1840 &.no-select {
1841 background-color: #85B8FF;
1842 }
1843 }
1844
1845 }
1846
1847 /* 三角形 */
1848 .triangle-box {
1849 display: inline-block;
1850 width: 30px;
1851 height: 28px;
1852 overflow: hidden;
1853 position: absolute;
1854 left: 0;
1855 }
1856
1857 .triangle {
1858 transform: rotate(45deg);
1859 transform-origin: left top;
1860 position: absolute;
1861 top: -3px;
1862 left: 0px;
1863 width: 24px;
1864 height: 28px;
1865 border: 3px solid white;
1866
1867 &.active {
1868 background-color: #5797FF;
1869
1870 &.no-select {
1871 background-color: #85B8FF;
1872 }
1873 }
1874 }
1875
1876 .cursor-pointer {
1877 cursor: pointer;
1878 }
1879
1880 :deep(.el-timeline) {
1881 margin-left: -16px;
1882
1883 .el-timeline-item__tail {
1884 height: calc(100% - 18px);
1885 top: 14px;
1886 }
1887
1888 .el-timeline-item__timestamp {
1889 color: #212121;
1890 }
1891
1892 .time-title {
1893 color: #212121;
1894 }
1895
1896 .row-per {
1897 display: flex;
1898 flex-direction: row;
1899 align-items: center;
1900
1901 .desc {
1902 color: #999;
1903
1904 .main {
1905 color: var(--el-color-primary);
1906 cursor: pointer;
1907 }
1908 }
1909
1910 }
1911 }
1912
1913 .label {
1914 width: 70px;
1915 background: #FF854D;
1916 border-radius: 2px;
1917 font-size: 12px;
1918 color: #FFFFFF;
1919 text-align: center;
1920 line-height: 20px;
1921 font-weight: 400;
1922 margin-right: 4px;
1923 white-space: pre;
1924 }
1925
1926 .label1 {
1927 width: 70px;
1928 background: #5797FF;
1929 border-radius: 2px;
1930 font-size: 12px;
1931 color: #FFFFFF;
1932 text-align: center;
1933 line-height: 20px;
1934 font-weight: 400;
1935 margin-right: 4px;
1936 white-space: pre;
1937 }
1938
1939 .label2 {
1940 width: 70px;
1941 background: #40BD6E;
1942 border-radius: 2px;
1943 font-size: 12px;
1944 color: #FFFFFF;
1945 text-align: center;
1946 line-height: 20px;
1947 font-weight: 400;
1948 margin-right: 4px;
1949 white-space: pre;
1950 }
1951
1952 :deep(.policy-table-detail) {
1953 .dialog_content {
1954 padding: 0px 20px 20px;
1955 }
1956 }
1957
1958 .empty-content {
1959 display: flex;
1960 align-items: center;
1961 justify-content: center;
1962 height: 250px;
1963 width: 100%;
1964 flex-direction: column;
1965
1966 .empty-text {
1967 font-size: 14px;
1968 color: #b2b2b2;
1969 }
1970 }
1971
1972 :deep(.dialog-form-inline.col3) {
1973 align-items: flex-start;
1974 }
1975
1976 :deep(.el-table) {
1977 .el-table__expanded-cell {
1978 padding: 16px;
1979 }
1980 }
1981
1982 .sign-main {
1983 .item-label {
1984 line-height: 21px;
1985 color: #666;
1986 }
1987
1988 .required {
1989 color: #fb2323;
1990 margin-left: 2px;
1991 }
1992 }
1993
1994 #pdf-content {
1995 padding: 12px;
1996 width: 780px;
1997
1998 :deep(.el-table .cell) {
1999 white-space: normal;
2000 word-break: break-word;
2001 }
2002 }
2003
2004 .sign-file-main {
2005 display: flex;
2006 flex-direction: row;
2007 width: 100%;
2008 margin-top: 24px;
2009
2010 .title-desc {
2011 width: 50%;
2012 font-size: 18px;
2013 color: #212121;
2014 font-weight: 600;
2015 }
2016
2017 }
2018
2019 .avoid-break {
2020 page-break-inside: avoid;
2021 break-inside: avoid;
2022 }
2023 </style>
...\ No newline at end of file ...\ No newline at end of file
1 <route lang="yaml">
2 name: smartContractManagement
3 </route>
4
5 <script lang="ts" setup name="smartContractManagement">
6 import { ref } from 'vue';
7 import TableTools from "@/components/Tools/table_tools.vue";
8 import {
9 getContractPageList,
10 contractStatusList,
11 deleteContract,
12 cancelContract,
13 } from "@/api/modules/dataSmartContract"
14 import useDataSmartContract from "@/store/modules/dataSmartContract";
15 import { commonPageConfig } from '@/utils/enum';
16 import { getEnterpriseData } from "@/api/modules/dataIdentify";
17
18 const userData = JSON.parse(localStorage.userData);
19 const tenantData = JSON.parse(localStorage.tenantInfo);
20 const router = useRouter();
21 const route = useRoute();
22 const { proxy } = getCurrentInstance() as any;
23 const dataSmartContractStore = useDataSmartContract();
24
25 const searchItemList = ref([
26 {
27 type: "input",
28 label: "",
29 field: "contractName",
30 default: "",
31 placeholder: "合约名称",
32 maxlength: 50,
33 clearable: true,
34 },
35 {
36 type: 'select',
37 label: '',
38 field: 'contractStatus',
39 default: '',
40 placeholder: '合约状态',
41 options: contractStatusList,
42 filterable: true,
43 clearable: true
44 }
45 ]);
46
47 const tableFields = ref([
48 { label: "序号", type: "index", width: 56, align: "center" },
49 { label: "合约名称", field: "contractName", width: 160, },
50 { label: "签署方式", field: "signModeName", width: 120 },
51 { label: "合约状态", field: "contractStatus", type: "tag", width: 96, align: 'center' },
52 { label: "合约编号", field: "contractId", width: 355 },
53 { label: "发起主体", field: "tenantName", width: 205 },
54 { label: "创建人", field: "createUserName", width: 130 },
55 { label: "创建时间", field: "createTime", width: 170 },
56 { label: "提交企业", field: "submitTenantName", width: 200 },
57 { label: "提交时间", field: "submitTime", width: 170 },
58 ]);
59
60 const page = ref({
61 ...commonPageConfig,
62 contractName: '',
63 contractStatus: ''
64 });
65
66 const currTableData: any = ref({});
67 const tableInfo = ref({
68 id: 'contract-table',
69 rowKey: 'guid',
70 loading: false,
71 fields: tableFields.value,
72 data: [],
73 page: {
74 type: "normal",
75 rows: 0,
76 ...page.value,
77 },
78 actionInfo: {
79 label: "操作",
80 type: "btn",
81 width: 160,
82 btns: (scope) => {
83 let row = scope.row;
84 let btns: any = [];
85 let contractStatus = row.contractStatus;
86 if (contractStatus == '00') { //撤回状态,如果是合约发起方有重新提交按钮,否则只能查看
87 if (userData.tenantGuid == row.tenantGuid) { //是本人发起的。
88 btns.push({ value: 'view', label: '查看', click: () => btnHandles['view'](scope) });
89 row.isDisable != 'Y' && btns.push({ value: 'edit', label: '重新提交', click: () => btnHandles['edit'](scope) });
90 btns.push({ value: 'delete', label: '删除', click: () => btnHandles['delete'](scope) })
91 } else {
92 btns.push({ value: 'view', label: '查看', click: () => btnHandles['view'](scope) });
93 }
94 } else if (contractStatus == '01') { //发起状态
95 btns.push({ value: 'edit', label: '编辑', click: () => btnHandles['edit'](scope) });
96 btns.push({ value: 'delete', label: '删除', click: () => btnHandles['delete'](scope) })
97 } else if (contractStatus == '02') { // 协商
98 btns.push({ value: 'view', label: '查看', click: () => btnHandles['view'](scope) });
99 /**
100 * 1. 中介签署,只需要两方都协商,中介不显示按钮。
101 * 2. 点对点的话,只有对方需要协商。只要有继续协商变动之后,就需要对方确认。发起方提交后,如果对方还没协商过,发起方就没有协商按钮。
102 */
103 if (row.signModeCode == '01') {
104 if (row.negotiateEntityGuid == row.dataProviderGuid && userData.tenantGuid == row.dataUserGuid || (row.negotiateEntityGuid == row.dataUserGuid && userData.tenantGuid == row.dataProviderGuid)) {
105 btns.push({ value: 'consult', label: '协商', click: () => btnHandles['consult'](scope) });
106 }
107 } else { //中介协商。
108 if (!row.confirmGuids?.length) { //没有确认guids。
109 if (row.negotiateEntityGuid != row.dataProviderGuid && row.negotiateEntityGuid != row.dataUserGuid) { //都不是,是中介。
110 if ((userData.tenantGuid == row.dataUserGuid || userData.tenantGuid == row.dataProviderGuid)) { //中介新建,两方都有。
111 btns.push({ value: 'consult', label: '协商', click: () => btnHandles['consult'](scope) });
112 }
113 }
114 } else { //其中一方继续协商或确认都有confirmGuids
115 // 其中一方确认。协商guid还是上次的。
116 if (!row.confirmGuids.includes(userData.tenantGuid) && (userData.tenantGuid == row.dataUserGuid || userData.tenantGuid == row.dataProviderGuid)) {
117 btns.push({ value: 'consult', label: '协商', click: () => btnHandles['consult'](scope) });
118 }
119 }
120 }
121 // 合约发起方才有撤回按钮
122 if (userData.tenantGuid == row.tenantGuid) {
123 btns.push({ value: 'revoke', label: '撤回', click: () => btnHandles['revoke'](scope) });
124 }
125 } else if (contractStatus == '03') { //签署,签署过的一方不需要显示签署。
126 btns.push({ value: 'view', label: '查看', click: () => btnHandles['view'](scope) });
127 if (row.signModeCode == '01') {
128 if (!row.signatureList?.includes(userData.tenantGuid)) {
129 btns.push({ value: 'sign', label: '签署', click: () => btnHandles['sign'](scope) });
130 }
131 } else { //中介
132 if (!row.signatureList?.includes(userData.tenantGuid) && (userData.tenantGuid == row.dataUserGuid || userData.tenantGuid == row.dataProviderGuid)) {
133 btns.push({ value: 'sign', label: '签署', click: () => btnHandles['sign'](scope) });
134 }
135 }
136 // 合约发起方才有撤回按钮
137 if (userData.tenantGuid == row.tenantGuid) {
138 btns.push({ value: 'revoke', label: '撤回', click: () => btnHandles['revoke'](scope) });
139 }
140 } else if (contractStatus == '05') { //合约履行中
141 btns.push({ value: 'view', label: '查看', click: () => btnHandles['view'](scope) });
142 //合约发起方才有合约解除
143 if (userData.tenantGuid == row.tenantGuid) {
144 btns.push({ value: 'termination', label: '解除', click: () => btnHandles['termination'](scope) });
145 }
146 } else if (contractStatus == '06' || contractStatus == '0302') {
147 btns.push({ value: 'view', label: '查看', click: () => btnHandles['view'](scope) });
148 }
149 return btns;
150 }
151 }
152 });
153
154 const btnHandles = {
155 edit: (scope) => {
156 router.push({
157 name: 'smartContractCreate',
158 query: {
159 guid: scope.row.guid,
160 name: scope.row.contractName
161 }
162 });
163 },
164 delete: (scope) => {
165 proxy.$openMessageBox("此操作将永久删除, 是否继续?", () => {
166 deleteContract([scope.row.guid]).then((res: any) => {
167 if (res?.code == proxy.$passCode) {
168 page.value.curr = 1;
169 getTableData();
170 proxy.$ElMessage.success('删除成功');
171 } else {
172 res?.msg && proxy.$ElMessage.error(res?.msg);
173 }
174 });
175 }, () => {
176 proxy.$ElMessage.info("已取消");
177 })
178 },
179 consult: (scope) => {
180 router.push({
181 name: 'smartContractDetail',
182 query: {
183 guid: scope.row.guid,
184 name: scope.row.contractName,
185 type: 'consult'
186 }
187 });
188 },
189 sign: (scope) => {
190 router.push({
191 name: 'smartContractDetail',
192 query: {
193 guid: scope.row.guid,
194 name: scope.row.contractName,
195 type: 'sign'
196 }
197 });
198 },
199 revoke: (scope) => {
200 proxy.$openMessageBox("确定撤回该连合约吗?", () => {
201 tableInfo.value.loading = true;
202 cancelContract(scope.row.guid).then((res: any) => {
203 tableInfo.value.loading = false;
204 if (res.code == proxy.$passCode) {
205 proxy.$ElMessage.success('合约撤回成功');
206 getTableData();
207 } else {
208 proxy.$ElMessage.error(res.msg);
209 }
210 });
211 }, () => {
212 proxy.$ElMessage.info("已取消撤回");
213 })
214 },
215 termination: (scope) => {//解除合同
216 router.push({
217 name: 'smartContractDetail',
218 query: {
219 guid: scope.row.guid,
220 name: scope.row.contractName,
221 type: 'reject'
222 }
223 });
224 },
225 view: (scope) => {//查看
226 if (scope.row.contractStatus == '05') {
227 router.push({
228 name: 'smartContractDetail',
229 query: {
230 guid: scope.row.guid,
231 name: scope.row.contractName,
232 type: 'keepAgree',
233 isDetail: 'Y'
234 }
235 });
236 } else {
237 router.push({
238 name: 'smartContractDetail',
239 query: {
240 guid: scope.row.guid,
241 name: scope.row.contractName,
242 isDetail: 'Y'
243 }
244 });
245 }
246 }
247 }
248
249 const toSearch = (val: any, clear: boolean = false) => {
250 if (clear) {
251 searchItemList.value.map((item) => (item.default = ""));
252 page.value.contractName = '';
253 page.value.contractStatus = "";
254 } else {
255 page.value.contractName = val.contractName;
256 page.value.contractStatus = val.contractStatus;
257 }
258 getTableData();
259 };
260
261 const getTableData = () => {
262 tableInfo.value.loading = true
263 getContractPageList({
264 pageIndex: page.value.curr,
265 pageSize: page.value.limit,
266 contractName: page.value.contractName,
267 contractStatus: page.value.contractStatus
268 }).then((res: any) => {
269 tableInfo.value.loading = false
270 if (res?.code == proxy.$passCode) {
271 const data = res.data || {}
272 tableInfo.value.data = data.records || []
273 tableInfo.value.page.limit = data.pageSize
274 tableInfo.value.page.curr = data.pageIndex
275 tableInfo.value.page.rows = data.totalRows
276 } else {
277 res?.msg && proxy.$ElMessage.error(res?.msg)
278 }
279 }).catch(() => {
280 tableInfo.value.loading = false
281 })
282 };
283
284 const tablePageChange = (info) => {
285 page.value.curr = Number(info.curr);
286 page.value.limit = Number(info.limit);
287 tableInfo.value.page.curr = page.value.curr;
288 tableInfo.value.page.limit = page.value.limit;
289 getTableData();
290 };
291
292 const newCreate = () => {
293 let exec = () => {
294 if (tenantData.isCertification != 'Y' && !currTenantDetailInfo.value.trustedIdentityCredential) { //认证过的
295 if (userData.superTubeFlag != 'Y') {//平台用户
296 proxy.$ElMessage.error('请先完成企业认证后再进行创建');
297 return;
298 }
299 }
300 router.push({
301 name: 'smartContractCreate'
302 });
303 }
304 if (psLogon.value) {
305 psLogon.value.then(() => {
306 exec();
307 })
308 } else {
309 exec();
310 }
311 }
312
313 onActivated(() => {
314 if (dataSmartContractStore.isRefresh) {//如果是首次加载,则不需要调用
315 page.value.curr = 1;
316 getTableData();
317 dataSmartContractStore.set(false);
318 }
319 })
320
321 /** 当前会员是否认证信息 */
322 const currTenantDetailInfo: any = ref({});
323
324 const psLogon = ref();
325
326 onBeforeMount(() => {
327 !dataSmartContractStore.isRefresh && toSearch({})
328 if (tenantData.isCertification != 'Y') {
329 psLogon.value = getEnterpriseData({
330 logonUser: userData.tenantName == "非认证会员" ? userData.logonUser : tenantData.logonUser
331 }).then((res: any) => {
332 psLogon.value = null;
333 if (res?.code == proxy.$passCode) {
334 currTenantDetailInfo.value = res.data || {};
335 } else {
336 res?.msg && proxy.$ElMessage.error(res?.msg);
337 }
338 }).catch(() => {
339 psLogon.value = null;
340 });
341 }
342 })
343
344 </script>
345
346 <template>
347 <div class="container_wrap">
348 <div class="table_tool_wrap">
349 <TableTools :searchItems="searchItemList" :searchId="'contract-search'" @search="toSearch" :init="false" />
350 <div class="tools_btns">
351 <el-button type="primary" @click="newCreate">新增</el-button>
352 </div>
353 </div>
354 <div class="table_panel_wrap">
355 <Table :tableInfo="tableInfo" @tablePageChange="tablePageChange" />
356 </div>
357 </div>
358 </template>
359
360 <style lang="scss" scoped>
361 .container_wrap {
362 padding: 0px 16px;
363 }
364
365 :deep(.el-tag.el-tag--primary) {
366 color: #0E5FD8;
367 background: #F2F9FF;
368 border: 1px solid rgba(224, 239, 255, 1);
369 }
370 </style>
...\ No newline at end of file ...\ No newline at end of file
1 <route lang="yaml">
2 name: strategyManagement
3 </route>
4
5 <script lang="ts" setup name="strategyManagement">
6 import { ref } from 'vue';
7 import TableTools from "@/components/Tools/table_tools.vue";
8 import {
9 getPageList,
10 updateTemplateState,
11 savePolicyTemplate,
12 updatePolicyTemplate,
13 deletePolicyTemplate
14 } from "@/api/modules/dataSmartContract";
15 import {
16 getParamsList
17 } from "@/api/modules/queryService";
18 import { useValidator } from '@/hooks/useValidator';
19
20 const { required, regexpValidate } = useValidator();
21 const router = useRouter();
22 const route = useRoute();
23 const { proxy } = getCurrentInstance() as any;
24
25 const policyTypeList: any = ref([]);
26
27 /** 约束运算符字典下拉 */
28 const operatorOptionList: any = ref([]);
29
30 const searchItemList = ref([
31 {
32 type: "input",
33 label: "",
34 field: "policyName",
35 default: "",
36 placeholder: "策略名称",
37 maxlength: 50,
38 clearable: true,
39 },
40 {
41 type: 'tree-select',
42 label: '',
43 field: 'policyTypeCode',
44 default: '',
45 placeholder: '维度类型',
46 options: policyTypeList.value,
47 filterable: true,
48 clearable: true,
49 checkStrictly: true,//只能选择叶子节点。
50 lazy: false,
51 multiple: false,
52 collapseTagsTooltip: true,
53 collapseTags: true,
54 props: {
55 label: "label",
56 value: "value",
57 children: 'childDictList'
58 },
59 showCheckbox: false
60 }
61 ]);
62
63 const tableFields = ref([
64 { label: "序号", type: "index", width: 56, align: "center" },
65 { label: "策略id", field: "policyId", width: 205, },
66 { label: "策略名称", field: "policyName", width: 130 },
67 { label: "策略英文名", field: "policyEnName", width: 140 },
68 { label: "维度类型", field: "policyTypeCodeName", width: 100 },
69 { label: '启用状态', width: 96, field: 'bizStatus', type: 'switch', activeText: '启用', inactiveText: '停用', activeValue: 'Y', inactiveValue: 'N', align: 'center' },
70 { label: "描述", field: "description", width: 200 },
71 { label: "修改人", field: "updateUserName", width: 130 },
72 { label: "修改时间", field: "updateTime", width: 170 },
73 ]);
74
75 const page = ref({
76 limit: 50,
77 curr: 1,
78 sizes: [
79 { label: "10", value: 10 },
80 { label: "50", value: 50 },
81 { label: "100", value: 100 },
82 { label: "150", value: 150 },
83 { label: "200", value: 200 },
84 ],
85 policyName: '',
86 policyTypeCode: ''
87 });
88 const currTableData: any = ref({});
89 const tableInfo = ref({
90 id: 'value-asset-table',
91 rowKey: 'guid',
92 loading: false,
93 fields: tableFields.value,
94 data: [],
95 page: {
96 type: "normal",
97 rows: 0,
98 ...page.value,
99 },
100 actionInfo: {
101 label: "操作",
102 type: "btn",
103 width: 100,
104 btns: (scope) => {
105 let row = scope.row;
106 if (row.bizStatus == 'Y') {
107 return [{
108 label: "查看", value: "view", click: (scope) => {
109 drawerInfo.value.visible = true;
110 drawerInfo.value.type = 'view';
111 drawerInfo.value.header.title = '查看策略';
112 drawerInfo.value.footer.visible = false;
113 classEditFormItems.value.forEach(item => {
114 item.default = scope.row[item.field] || '';
115 item.disabled = true;
116 });
117 let val = scope.row.policyTypeCode;
118 let selectItem = val && policyTypeList.value.find(p => p.childDictList?.some(c => c.value == val));
119 classEditFormItems.value.at(-2).visible = selectItem?.value == 'YS';
120 }
121 }]
122 } else {
123 return [{
124 label: "编辑", value: "edit", click: (scope) => {
125 currTableData.value = scope.row;
126 drawerInfo.value.visible = true;
127 drawerInfo.value.type = 'edit';
128 drawerInfo.value.header.title = '编辑策略';
129 drawerInfo.value.footer.visible = true;
130 classEditFormItems.value.forEach(item => {
131 item.default = scope.row[item.field] || '';
132 item.disabled = false;
133 });
134 let val = scope.row.policyTypeCode;
135 let selectItem = val && policyTypeList.value.find(p => p.childDictList?.some(c => c.value == val));
136 classEditFormItems.value.at(-2).visible = selectItem?.value == 'YS';
137 }
138 }, {
139 label: "删除", value: "delete", click: (scope) => {
140 proxy.$openMessageBox("此操作将永久删除, 是否继续?", () => {
141 deletePolicyTemplate([scope.row.guid]).then((res: any) => {
142 if (res?.code == proxy.$passCode) {
143 page.value.curr = 1;
144 getTableData();
145 proxy.$ElMessage.success('删除成功');
146 } else {
147 res?.msg && proxy.$ElMessage.error(res?.msg);
148 }
149 });
150 }, () => {
151 proxy.$ElMessage.info("已取消");
152 })
153 }
154 }]
155 }
156 }
157 }
158 });
159
160 const tableSwitchBeforeChange = (scope, field, callback) => {
161 const msg = `确定${scope.row[field] == 'Y' ? '停用' : '启用'}${scope.row.policyName}】策略?`
162 proxy.$openMessageBox(msg, () => {
163 const state = scope.row[field] == 'Y' ? 'N' : 'Y'
164 const result = tableSwitchChange(state, scope, field)
165 callback(result)
166 }, () => {
167 callback(false)
168 });
169 }
170
171 const tableSwitchChange = (val, scope, field) => {
172 return new Promise((resolve, reject) => {
173 let params = {
174 guid: scope.row.guid,
175 bizStatus: val
176 }
177 updateTemplateState(params).then((res: any) => {
178 if (res?.code == proxy.$passCode && res?.data) {
179 getTableData();
180 proxy.$ElMessage({
181 type: "success",
182 message: `【${scope.row.policyName}】策略${val == 'Y' ? '启用' : '停用'}成功`,
183 });
184 resolve(true)
185 } else {
186 proxy.$ElMessage({
187 type: "error",
188 message: res.msg,
189 });
190 reject(false)
191 }
192 }).catch(() => {
193 reject(false)
194 })
195 })
196 }
197
198 const toSearch = (val: any, clear: boolean = false) => {
199 if (clear) {
200 searchItemList.value.map((item) => (item.default = ""));
201 page.value.policyName = '';
202 page.value.policyTypeCode = "";
203 } else {
204 page.value.policyName = val.policyName;
205 page.value.policyTypeCode = val.policyTypeCode;
206 }
207 getTableData();
208 };
209
210 const getTableData = () => {
211 tableInfo.value.loading = true
212 getPageList({
213 pageIndex: page.value.curr,
214 pageSize: page.value.limit,
215 policyName: page.value.policyName,
216 policyTypeCode: page.value.policyTypeCode
217 }).then((res: any) => {
218 tableInfo.value.loading = false
219 if (res?.code == proxy.$passCode) {
220 const data = res.data || {}
221 tableInfo.value.data = data.records || []
222 tableInfo.value.page.limit = data.pageSize
223 tableInfo.value.page.curr = data.pageIndex
224 tableInfo.value.page.rows = data.totalRows
225 } else {
226 proxy.$ElMessage({
227 type: 'error',
228 message: res.msg,
229 })
230 }
231 }).catch(() => {
232 tableInfo.value.loading = false
233 })
234 };
235
236 const tablePageChange = (info) => {
237 page.value.curr = Number(info.curr);
238 page.value.limit = Number(info.limit);
239 tableInfo.value.page.curr = page.value.curr;
240 tableInfo.value.page.limit = page.value.limit;
241 getTableData();
242 };
243
244 const defaultValueInfo = ref({ bizStatus: 'Y' });
245
246 const newCreate = () => {
247 drawerInfo.value.visible = true;
248 drawerInfo.value.type = 'add';
249 drawerInfo.value.header.title = '新增策略';
250 drawerInfo.value.footer.visible = true;
251 classEditFormItems.value.forEach(item => {
252 item.default = defaultValueInfo.value[item.field] || '';
253 item.disabled = false;
254 });
255 classEditFormItems.value.at(-2).visible = false;
256 }
257
258 onActivated(() => {
259 })
260
261 onBeforeMount(() => {
262 toSearch({});
263 getParamsList({ dictType: '维度类型' }).then((res: any) => {
264 if (res?.code == proxy.$passCode) {
265 const data = res.data || [];
266 policyTypeList.value = data;
267 searchItemList.value[1].options = policyTypeList.value;
268 classEditFormItems.value[0].options = policyTypeList.value;
269 } else {
270 res?.msg && proxy.$ElMessage.error(res?.msg);
271 }
272 })
273 getParamsList({ dictType: '约束运算符' }).then((res: any) => {
274 if (res?.code == proxy.$passCode) {
275 const data = res.data || [];
276 operatorOptionList.value = data;
277 if (classEditFormItems.value.at(-2)) {
278 classEditFormItems.value.at(-2).options = operatorOptionList.value;
279 }
280 } else {
281 res?.msg && proxy.$ElMessage.error(res?.msg);
282 }
283 })
284 })
285
286 const classEditFormItems: any = ref([{
287 label: '维度类型',
288 type: 'tree-select',
289 placeholder: '请选择',
290 field: 'policyTypeCode',
291 default: '',
292 options: policyTypeList.value,
293 filterable: true,
294 clearable: true,
295 required: true,
296 showAllLevels: false,
297 checkStrictly: false,//只能选择叶子节点。
298 lazy: false,
299 props: {
300 value: 'value',
301 label: 'label',
302 children: 'childDictList'
303 },
304 disabled: false,
305 }, {
306 label: '策略名称',
307 type: 'input',
308 field: 'policyName',
309 default: '',
310 maxlength: 50,
311 placeholder: '请输入',
312 required: true,
313 clearable: true,
314 }, {
315 label: '策略英文名',
316 type: 'input',
317 field: 'policyEnName',
318 default: '',
319 maxlength: 50,
320 regexp: /[^A-Za-z0-9_]/g,
321 placeholder: '请输入',
322 required: true,
323 clearable: true,
324 }, {
325 label: '启用状态',
326 type: 'switch',
327 field: 'bizStatus',
328 default: 'Y',
329 placeholder: '请选择',
330 activeValue: 'Y',
331 inactiveValue: 'N',
332 switchWidth: 32,
333 }, {
334 label: '运算符',
335 type: 'select',
336 multiple: true,
337 collapse: true,
338 tagsTooltip: true,
339 placeholder: '默认全部',
340 field: 'constraintOperatorCodes',
341 default: '',
342 options: operatorOptionList.value,
343 filterable: true,
344 clearable: true,
345 required: false,
346 visible: false,
347 disabled: false,
348 }, {
349 label: '策略描述',
350 type: 'textarea',
351 placeholder: '请输入',
352 field: 'description',
353 default: '',
354 block: true,
355 maxlength: 500,
356 clearable: true,
357 required: true,
358 }]);
359
360 const classEditFormRules = ref({
361 policyTypeCode: [required('请选择维度类型')],
362 policyName: [required('请填写策略名称')],
363 policyEnName: [required('请填写策略英文名')],
364 description: [required('请填写策略描述')]
365 });
366
367 /** 新增分类的form */
368 const classEditFormInfo = ref({
369 type: "form",
370 title: "",
371 col: "span",
372 formInfo: {
373 id: "add-class-form",
374 readonly: false,
375 items: classEditFormItems.value,
376 rules: classEditFormRules.value,
377 },
378 });
379
380 /** 新增编辑分类。 */
381 const drawerInfo = ref({
382 visible: false,
383 direction: 'rtl',
384 size: 600,
385 header: {
386 title: '新增策略',
387 },
388 type: '',
389 container: {
390 contents: [classEditFormInfo.value],
391 },
392 footer: {
393 visible: true,
394 btns: [
395 { type: 'default', label: '取消', value: 'cancel' },
396 { type: 'primary', label: '确定', value: 'save', loading: false },
397 ]
398 }
399 })
400
401 const handlerDrawerSelectChange = (val, row, info) => {
402 if (row.field == 'policyTypeCode') {
403 let selectItem = val && policyTypeList.value.find(p => p.childDictList?.some(c => c.value == val));
404 classEditFormItems.value.at(-2).visible = selectItem?.value == 'YS';
405 classEditFormItems.value.forEach(item => {
406 item.default = info[item.field];
407 });
408 }
409 }
410
411 const drawerBtnClick = async (btn, info) => {
412 if (btn.value == 'cancel') {
413 drawerInfo.value.visible = false;
414 } else {
415 let constraintOperatorCodes = info.constraintOperatorCodes || [];
416 if (constraintOperatorCodes?.length) {
417 info.constraintOperatorCodes = constraintOperatorCodes.map(c => {
418 return operatorOptionList.value.find(o => o.value == c)
419 }).sort((a, b) => {
420 return a.orderNum - b.orderNum;
421 }).map(v => {
422 return v.value;
423 })
424 }
425 drawerInfo.value.footer.btns[1].loading = true;
426 if (drawerInfo.value.type == 'edit') {
427 info.guid = currTableData.value.guid;
428 info.policyId = currTableData.value.policyId;
429 updatePolicyTemplate(info).then((res: any) => {
430 drawerInfo.value.footer.btns[1].loading = false;
431 if (res?.code == proxy.$passCode) {
432 drawerInfo.value.visible = false;
433 getTableData();
434 proxy.$ElMessage.success('编辑策略成功');
435 } else {
436 res?.msg && proxy.$ElMessage.error(res?.msg);
437 }
438 });
439 } else {
440 savePolicyTemplate(info).then((res: any) => {
441 drawerInfo.value.footer.btns[1].loading = false;
442 if (res?.code == proxy.$passCode) {
443 drawerInfo.value.visible = false;
444 page.value.curr = 1;
445 getTableData();
446 proxy.$ElMessage.success('新增策略成功');
447 } else {
448 res?.msg && proxy.$ElMessage.error(res?.msg);
449 }
450 });
451 }
452 }
453 }
454
455 </script>
456
457 <template>
458 <div class="container_wrap">
459 <div class="table_tool_wrap">
460 <TableTools :searchItems="searchItemList" :searchId="'settle-asset-search'" @search="toSearch" :init="false" />
461 <div class="tools_btns">
462 <el-button type="primary" @click="newCreate">新增</el-button>
463 </div>
464 </div>
465 <div class="table_panel_wrap">
466 <Table :tableInfo="tableInfo" @tablePageChange="tablePageChange"
467 @tableSwitchBeforeChange="tableSwitchBeforeChange" />
468 </div>
469 <Drawer :drawerInfo="drawerInfo" @drawerSelectChange="handlerDrawerSelectChange" @drawerBtnClick="drawerBtnClick"
470 ref="drawerRef"></Drawer>
471 </div>
472 </template>
473
474 <style lang="scss" scoped>
475 .container_wrap {
476 padding: 0px 16px;
477 }
478 </style>
...\ No newline at end of file ...\ 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!