Compare commits

..

No commits in common. 'master' and 'V1.0.1' have entirely different histories.

  1. 7
      .env
  2. 2
      .env.production
  3. 5
      .eslintrc.js
  4. 9
      index.html
  5. 344
      package-lock.json
  6. 8
      package.json
  7. 462
      public/tinymce/langs/zh_CN.js
  8. 419
      public/tinymce/langs/zh_TW.js
  9. 7
      public/tinymce/skins/content/dark/content.min.css
  10. 7
      public/tinymce/skins/content/default/content.min.css
  11. 7
      public/tinymce/skins/content/document/content.min.css
  12. 7
      public/tinymce/skins/content/writer/content.min.css
  13. 7
      public/tinymce/skins/ui/oxide-dark/content.inline.min.css
  14. 7
      public/tinymce/skins/ui/oxide-dark/content.min.css
  15. 7
      public/tinymce/skins/ui/oxide-dark/content.mobile.min.css
  16. BIN
      public/tinymce/skins/ui/oxide-dark/fonts/tinymce-mobile.woff
  17. 7
      public/tinymce/skins/ui/oxide-dark/skin.min.css
  18. 7
      public/tinymce/skins/ui/oxide-dark/skin.mobile.min.css
  19. 7
      public/tinymce/skins/ui/oxide-dark/skin.shadowdom.min.css
  20. 7
      public/tinymce/skins/ui/oxide/content.inline.min.css
  21. 7
      public/tinymce/skins/ui/oxide/content.min.css
  22. 7
      public/tinymce/skins/ui/oxide/content.mobile.min.css
  23. BIN
      public/tinymce/skins/ui/oxide/fonts/tinymce-mobile.woff
  24. 7
      public/tinymce/skins/ui/oxide/skin.min.css
  25. 7
      public/tinymce/skins/ui/oxide/skin.mobile.min.css
  26. 7
      public/tinymce/skins/ui/oxide/skin.shadowdom.min.css
  27. 5
      src/api/bank.ts
  28. 4
      src/api/config.ts
  29. 7
      src/api/judgment.ts
  30. 225
      src/api/model.ts
  31. 2
      src/api/system.ts
  32. 31
      src/api/user.ts
  33. 1
      src/assets/svgs/preview.svg
  34. 2
      src/components/AliOss/upload.ts
  35. 10
      src/components/Back.vue
  36. 258
      src/components/DialogForm.vue
  37. 30
      src/components/HelloI18n.vue
  38. 17
      src/components/LabelTip.vue
  39. 27
      src/components/ListMove.vue
  40. 555
      src/components/Panel/index.vue
  41. 59
      src/components/QueryForm/QueryForm.vue
  42. 16
      src/components/QueryForm/QueryInput.vue
  43. 64
      src/components/QueryForm/QueryItem.vue
  44. 2
      src/components/QueryForm/index.ts
  45. 9
      src/components/Search.vue
  46. 34
      src/components/StrategyConfirm.vue
  47. 45
      src/components/TableList/ColumnList.vue
  48. 40
      src/components/TableList/ColumnSetting.vue
  49. 2
      src/components/TableList/index.ts
  50. 55
      src/components/TableList/useColumns.ts
  51. 11
      src/components/Tinymce/index.vue
  52. 118
      src/components/Upload/BaseUpload.vue
  53. 136
      src/components/Upload/FileListUpload.vue
  54. 84
      src/components/Upload/ImageCropper.vue
  55. 157
      src/components/Upload/ImageListUpload.vue
  56. 149
      src/components/Upload/ImageUpload.vue
  57. 4
      src/components/Upload/index.ts
  58. 335
      src/data.ts
  59. 35
      src/layout/components/AppHeader.vue
  60. 3
      src/layout/components/AppMain.vue
  61. 172
      src/layout/components/AppSidebar/Menu.vue
  62. 46
      src/layout/components/AppSidebar/index.vue
  63. 8
      src/layout/components/Logo.vue
  64. 22
      src/layout/index.vue
  65. 5
      src/permission.ts
  66. 2
      src/router/index.ts
  67. 10
      src/settings.ts
  68. 2
      src/store/useCurrentUser.ts
  69. 23
      src/store/useProduct.ts
  70. 26
      src/styles/form.scss
  71. 53
      src/styles/index.scss
  72. 1
      src/utils/auth.ts
  73. 32
      src/utils/common.ts
  74. 6
      src/utils/getPageTitle.ts
  75. 41
      src/utils/request.ts
  76. 9
      src/views/403.vue
  77. 11
      src/views/404.vue
  78. 82
      src/views/Home.vue
  79. 98
      src/views/Role.vue
  80. 42
      src/views/bankProduct/index.vue
  81. 140
      src/views/config/level/Index.vue
  82. 110
      src/views/config/param/Buyer.vue
  83. 18
      src/views/config/param/Financial.vue
  84. 15
      src/views/config/param/Index.vue
  85. 27
      src/views/finance/Account.vue
  86. 79
      src/views/finance/Bank.vue
  87. 95
      src/views/finance/BankDetail.vue
  88. 98
      src/views/finance/Fund.vue
  89. 72
      src/views/finance/Insurance.vue
  90. 52
      src/views/finance/Order.vue
  91. 76
      src/views/finance/Publish.vue
  92. 174
      src/views/product/afterLoan/1029.vue
  93. 194
      src/views/product/afterLoan/1029/Detail.vue
  94. 157
      src/views/product/afterLoan/1029/Index.vue
  95. 187
      src/views/product/afterLoan/1030.vue
  96. 219
      src/views/product/afterLoan/1030/Detail.vue
  97. 157
      src/views/product/afterLoan/1030/Index.vue
  98. 347
      src/views/product/afterLoan/1031.vue
  99. 336
      src/views/product/afterLoan/1031/Detail.vue
  100. 157
      src/views/product/afterLoan/1031/Index.vue
  101. Some files were not shown because too many files have changed in this diff Show More

@ -1,9 +1,8 @@
VITE_APP_TITLE=金融产品设计及数字化营销沙盘 VITE_APP_TITLE=金融产品设计及数字化营销沙盘
VITE_PORT=9520 VITE_PORT=9520
# VITE_PROXY=http://192.168.31.125:8080 VITE_PROXY=http://192.168.31.125:8080
VITE_PUBLIC_PATH=./ VITE_PUBLIC_PATH=./
# VITE_BASE_API=http://192.168.31.217:9000 VITE_BASE_API=http://192.168.31.217:9000
VITE_BASE_API=http://121.37.12.51 # VITE_BASE_API=http://121.37.12.51
# VITE_BASE_API=https://www.occupationlab.com
VITE_I18N_LOCALE=zh-cn VITE_I18N_LOCALE=zh-cn
VITE_I18N_FALLBACK_LOCALE=zh-cn VITE_I18N_FALLBACK_LOCALE=zh-cn

@ -1 +1 @@
VITE_BASE_API=https://izhixinyun.com VITE_BASE_API=https://www.occupationlab.com

@ -17,11 +17,6 @@ module.exports = {
// '@typescript-eslint/camelcase': 'off', // '@typescript-eslint/camelcase': 'off',
'import/extensions': 'off', 'import/extensions': 'off',
'no-nested-ternary': 'off',
'no-use-before-define': 'off',
'no-unused-expressions': 'off',
'no-empty': 'off',
'no-console': 'off'
}, },
settings: { settings: {
'import/resolver': { 'import/resolver': {

@ -4,15 +4,6 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>金融产品设计及数字化营销沙盘</title> <title>金融产品设计及数字化营销沙盘</title>
<script>
var _hmt = _hmt || [];
(function () {
var hm = document.createElement('script');
hm.src = 'https://hm.baidu.com/hm.js?f2fcf78da19a1a04b48b45a07549d1d7';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(hm, s);
})();
</script>
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>

344
package-lock.json generated

@ -52,9 +52,9 @@
} }
}, },
"@ctrl/tinycolor": { "@ctrl/tinycolor": {
"version": "3.6.1", "version": "3.4.0",
"resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz", "resolved": "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-3.4.0.tgz",
"integrity": "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==" "integrity": "sha512-JZButFdZ1+/xAfpguQHoabIXkcqRRKpMrWKBkpEZZyxfY9C1DpADFB8PEqGSTeFr135SaTRfKqGKx5xSCLI7ZQ=="
}, },
"@element-plus/icons-vue": { "@element-plus/icons-vue": {
"version": "0.2.7", "version": "0.2.7",
@ -85,28 +85,6 @@
"integrity": "sha512-8HqW8EVqjnCmWXVpqAOZf+EGESdkR27odcMMMGefgKXtar00SoYNSryGv//TELI4T3QFsECo78p+0lmalk/CFA==", "integrity": "sha512-8HqW8EVqjnCmWXVpqAOZf+EGESdkR27odcMMMGefgKXtar00SoYNSryGv//TELI4T3QFsECo78p+0lmalk/CFA==",
"dev": true "dev": true
}, },
"@floating-ui/core": {
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.1.tgz",
"integrity": "sha512-42UH54oPZHPdRHdw6BgoBD6cg/eVTmVrFcgeRDM3jbO7uxSoipVcmcIGFcA5jmOHO5apcyvBhkSKES3fQJnu7A==",
"requires": {
"@floating-ui/utils": "^0.2.0"
}
},
"@floating-ui/dom": {
"version": "1.6.5",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.5.tgz",
"integrity": "sha512-Nsdud2X65Dz+1RHjAIP0t8z5e2ff/IRbei6BqFrl1urT8sDVzM1HMQ+R0XcU5ceRfyO3I6ayeqIfh+6Wb8LGTw==",
"requires": {
"@floating-ui/core": "^1.0.0",
"@floating-ui/utils": "^0.2.0"
}
},
"@floating-ui/utils": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.2.tgz",
"integrity": "sha512-J4yDIIthosAsRZ5CPYP/jQvUAQtlZTTD/4suA08/FEnlxqW3sKS9iAhgsa9VYLZ6vDHn/ixJgIqRQPotoBjxIw=="
},
"@intlify/bundle-utils": { "@intlify/bundle-utils": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmmirror.com/@intlify/bundle-utils/-/bundle-utils-2.2.0.tgz", "resolved": "https://registry.npmmirror.com/@intlify/bundle-utils/-/bundle-utils-2.2.0.tgz",
@ -229,9 +207,9 @@
} }
}, },
"@popperjs/core": { "@popperjs/core": {
"version": "npm:@sxzz/popperjs-es@2.11.7", "version": "2.11.2",
"resolved": "https://registry.npmjs.org/@sxzz/popperjs-es/-/popperjs-es-2.11.7.tgz", "resolved": "https://registry.npmmirror.com/@popperjs/core/-/core-2.11.2.tgz",
"integrity": "sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==" "integrity": "sha512-92FRmppjjqz29VMJ2dn+xdyXZBrMlE42AV6Kq6BwjWV7CNUW1hs2FtxSNLQE+gJhaZ6AAmYuO9y8dshhcBl7vA=="
}, },
"@rollup/pluginutils": { "@rollup/pluginutils": {
"version": "4.1.2", "version": "4.1.2",
@ -307,15 +285,8 @@
"@types/lodash": { "@types/lodash": {
"version": "4.14.178", "version": "4.14.178",
"resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.14.178.tgz", "resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.14.178.tgz",
"integrity": "sha512-0d5Wd09ItQWH1qFbEyQ7oTQ3GZrMfth5JkbN3EvTKLXcHLRDSXeLnlvlOn0wvxVIwK5o2M8JzP/OWz7T3NRsbw==" "integrity": "sha512-0d5Wd09ItQWH1qFbEyQ7oTQ3GZrMfth5JkbN3EvTKLXcHLRDSXeLnlvlOn0wvxVIwK5o2M8JzP/OWz7T3NRsbw==",
}, "dev": true
"@types/lodash-es": {
"version": "4.17.12",
"resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz",
"integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==",
"requires": {
"@types/lodash": "*"
}
}, },
"@types/node": { "@types/node": {
"version": "17.0.17", "version": "17.0.17",
@ -344,11 +315,6 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"@types/web-bluetooth": {
"version": "0.0.16",
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz",
"integrity": "sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ=="
},
"@typescript-eslint/eslint-plugin": { "@typescript-eslint/eslint-plugin": {
"version": "4.33.0", "version": "4.33.0",
"resolved": "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz", "resolved": "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz",
@ -616,16 +582,16 @@
} }
}, },
"@vue/compiler-sfc": { "@vue/compiler-sfc": {
"version": "3.2.34", "version": "3.2.25",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.34.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.25.tgz",
"integrity": "sha512-I+vT4soKJtdsoREBDYAcz56+yGpZ5T3GUigvBFgC2yTeTtBtREOPzYw8kZyMuD2ZlryPYBkbV8D9xxcvU0j/aw==", "integrity": "sha512-PminuOYIcFI7UZn+mdy2OPbogyAb0IHkVuqwmLDJiSRFhc/QAXQnO9KdS4nez3bQ9XlQmoAveQzcZuekHzdb5w==",
"requires": { "requires": {
"@babel/parser": "^7.16.4", "@babel/parser": "^7.16.4",
"@vue/compiler-core": "3.2.34", "@vue/compiler-core": "3.2.25",
"@vue/compiler-dom": "3.2.34", "@vue/compiler-dom": "3.2.25",
"@vue/compiler-ssr": "3.2.34", "@vue/compiler-ssr": "3.2.25",
"@vue/reactivity-transform": "3.2.34", "@vue/reactivity-transform": "3.2.25",
"@vue/shared": "3.2.34", "@vue/shared": "3.2.25",
"estree-walker": "^2.0.2", "estree-walker": "^2.0.2",
"magic-string": "^0.25.7", "magic-string": "^0.25.7",
"postcss": "^8.1.10", "postcss": "^8.1.10",
@ -633,65 +599,65 @@
}, },
"dependencies": { "dependencies": {
"@vue/compiler-core": { "@vue/compiler-core": {
"version": "3.2.34", "version": "3.2.25",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.34.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.25.tgz",
"integrity": "sha512-Y53lv04ZhDfqflhk4yEgBZrCL1RipbxqmqJFfl1PRkjOzt0bvJpf1sCNN81QNfXohVwFGf+Hng2ztwLwOZgbuA==", "integrity": "sha512-FlffKezIqztTCTyG0klkYRwhdyL6b1PTTCIerPb4p2R9qQaczccTX5g9ysi9w6tpLQ48a1WiXnFDJhWD7XoqwA==",
"requires": { "requires": {
"@babel/parser": "^7.16.4", "@babel/parser": "^7.16.4",
"@vue/shared": "3.2.34", "@vue/shared": "3.2.25",
"estree-walker": "^2.0.2", "estree-walker": "^2.0.2",
"source-map": "^0.6.1" "source-map": "^0.6.1"
} }
}, },
"@vue/compiler-dom": { "@vue/compiler-dom": {
"version": "3.2.34", "version": "3.2.25",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.34.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.25.tgz",
"integrity": "sha512-MFLUYDgy0aES9x1goU/pgxpzgT9IZOndO8qwQVSyVfUvl/CywEBtfBi5+8fsiBDhoGIT7g8qcsUUF1NYViU2vQ==", "integrity": "sha512-4JrburkRg4VWbc8AKpzKFWbNY4MDXshqjFl53+vINq7zaw3Z7aSqnLv0EkKh8B8ynf/MYsAdygGutyVbEWYxOw==",
"requires": { "requires": {
"@vue/compiler-core": "3.2.34", "@vue/compiler-core": "3.2.25",
"@vue/shared": "3.2.34" "@vue/shared": "3.2.25"
} }
}, },
"@vue/shared": { "@vue/shared": {
"version": "3.2.34", "version": "3.2.25",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.34.tgz", "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.25.tgz",
"integrity": "sha512-zhEeB8TrFmTXmTXmu/wcjEhgrjO4xqdDQrCdPhjX7NxfoLqoBVKguOm8qyihWNLbP+41svYY4za9mqXyqFLzNg==" "integrity": "sha512-DkHJFV2gw9WBRmUCa21eyG0WvlF0l1QFOgTkWj29O4mt2Tv3BSE5PQOKhUruZIym4bBYCqx9ZGtoD1WohDprow=="
} }
} }
}, },
"@vue/compiler-ssr": { "@vue/compiler-ssr": {
"version": "3.2.34", "version": "3.2.25",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.34.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.25.tgz",
"integrity": "sha512-zyaMdGJhxoA34ibWsXF7VH1PO5yrNB1MZg/ByRfXGM8JefGQaz+PpHvBy/5OI0ehEyhAyCb7279JdhYHacMZbw==", "integrity": "sha512-+BAl8U5D3JkGR6086PFx1BQQ5km3z9fT88hy/7lzf8i3vEDdPQodadnX2t6tndFjIux05MEKg43DeocOojT0mw==",
"requires": { "requires": {
"@vue/compiler-dom": "3.2.34", "@vue/compiler-dom": "3.2.25",
"@vue/shared": "3.2.34" "@vue/shared": "3.2.25"
}, },
"dependencies": { "dependencies": {
"@vue/compiler-core": { "@vue/compiler-core": {
"version": "3.2.34", "version": "3.2.25",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.34.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.25.tgz",
"integrity": "sha512-Y53lv04ZhDfqflhk4yEgBZrCL1RipbxqmqJFfl1PRkjOzt0bvJpf1sCNN81QNfXohVwFGf+Hng2ztwLwOZgbuA==", "integrity": "sha512-FlffKezIqztTCTyG0klkYRwhdyL6b1PTTCIerPb4p2R9qQaczccTX5g9ysi9w6tpLQ48a1WiXnFDJhWD7XoqwA==",
"requires": { "requires": {
"@babel/parser": "^7.16.4", "@babel/parser": "^7.16.4",
"@vue/shared": "3.2.34", "@vue/shared": "3.2.25",
"estree-walker": "^2.0.2", "estree-walker": "^2.0.2",
"source-map": "^0.6.1" "source-map": "^0.6.1"
} }
}, },
"@vue/compiler-dom": { "@vue/compiler-dom": {
"version": "3.2.34", "version": "3.2.25",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.34.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.25.tgz",
"integrity": "sha512-MFLUYDgy0aES9x1goU/pgxpzgT9IZOndO8qwQVSyVfUvl/CywEBtfBi5+8fsiBDhoGIT7g8qcsUUF1NYViU2vQ==", "integrity": "sha512-4JrburkRg4VWbc8AKpzKFWbNY4MDXshqjFl53+vINq7zaw3Z7aSqnLv0EkKh8B8ynf/MYsAdygGutyVbEWYxOw==",
"requires": { "requires": {
"@vue/compiler-core": "3.2.34", "@vue/compiler-core": "3.2.25",
"@vue/shared": "3.2.34" "@vue/shared": "3.2.25"
} }
}, },
"@vue/shared": { "@vue/shared": {
"version": "3.2.34", "version": "3.2.25",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.34.tgz", "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.25.tgz",
"integrity": "sha512-zhEeB8TrFmTXmTXmu/wcjEhgrjO4xqdDQrCdPhjX7NxfoLqoBVKguOm8qyihWNLbP+41svYY4za9mqXyqFLzNg==" "integrity": "sha512-DkHJFV2gw9WBRmUCa21eyG0WvlF0l1QFOgTkWj29O4mt2Tv3BSE5PQOKhUruZIym4bBYCqx9ZGtoD1WohDprow=="
} }
} }
}, },
@ -728,89 +694,89 @@
} }
}, },
"@vue/reactivity-transform": { "@vue/reactivity-transform": {
"version": "3.2.34", "version": "3.2.25",
"resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.34.tgz", "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.25.tgz",
"integrity": "sha512-OtsrL4/i6Md279pMhZ8wRijeDhPSdnXrH9wmqAcKDhVcp1L2kSWlgVVLa1jGIyyFYE806YiJNJiGBvXPGXMzxw==", "integrity": "sha512-fOiW67PUalicMfMr4Sc9l8mUtkN7ZD+G1/zJV8blzQ8GEZSeRcJm11gqve6Ps623ju5YORu7V/Q1gZoOJ9WO4g==",
"requires": { "requires": {
"@babel/parser": "^7.16.4", "@babel/parser": "^7.16.4",
"@vue/compiler-core": "3.2.34", "@vue/compiler-core": "3.2.25",
"@vue/shared": "3.2.34", "@vue/shared": "3.2.25",
"estree-walker": "^2.0.2", "estree-walker": "^2.0.2",
"magic-string": "^0.25.7" "magic-string": "^0.25.7"
}, },
"dependencies": { "dependencies": {
"@vue/compiler-core": { "@vue/compiler-core": {
"version": "3.2.34", "version": "3.2.25",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.34.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.25.tgz",
"integrity": "sha512-Y53lv04ZhDfqflhk4yEgBZrCL1RipbxqmqJFfl1PRkjOzt0bvJpf1sCNN81QNfXohVwFGf+Hng2ztwLwOZgbuA==", "integrity": "sha512-FlffKezIqztTCTyG0klkYRwhdyL6b1PTTCIerPb4p2R9qQaczccTX5g9ysi9w6tpLQ48a1WiXnFDJhWD7XoqwA==",
"requires": { "requires": {
"@babel/parser": "^7.16.4", "@babel/parser": "^7.16.4",
"@vue/shared": "3.2.34", "@vue/shared": "3.2.25",
"estree-walker": "^2.0.2", "estree-walker": "^2.0.2",
"source-map": "^0.6.1" "source-map": "^0.6.1"
} }
}, },
"@vue/shared": { "@vue/shared": {
"version": "3.2.34", "version": "3.2.25",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.34.tgz", "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.25.tgz",
"integrity": "sha512-zhEeB8TrFmTXmTXmu/wcjEhgrjO4xqdDQrCdPhjX7NxfoLqoBVKguOm8qyihWNLbP+41svYY4za9mqXyqFLzNg==" "integrity": "sha512-DkHJFV2gw9WBRmUCa21eyG0WvlF0l1QFOgTkWj29O4mt2Tv3BSE5PQOKhUruZIym4bBYCqx9ZGtoD1WohDprow=="
} }
} }
}, },
"@vue/runtime-core": { "@vue/runtime-core": {
"version": "3.2.34", "version": "3.2.25",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.34.tgz", "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.25.tgz",
"integrity": "sha512-GtaHqYiuEb56OA0cbMh20UPpDiXGRX+NS1buKif4OL341JJ3NtmNOIchCzknaN76oN6KqrLiO82/+TEZXl2Xtw==", "integrity": "sha512-2+fo5+lofT4xr8W2rtjyz+AM+UB1U/UNLH6ISFdHWNWuveSWxF+vkCQaATmhp6O3XA7QJAbHoRqIZor20EWSfQ==",
"requires": { "requires": {
"@vue/reactivity": "3.2.34", "@vue/reactivity": "3.2.25",
"@vue/shared": "3.2.34" "@vue/shared": "3.2.25"
}, },
"dependencies": { "dependencies": {
"@vue/reactivity": { "@vue/reactivity": {
"version": "3.2.34", "version": "3.2.25",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.34.tgz", "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.25.tgz",
"integrity": "sha512-xbRIOPqxdNOr0zS47moRS6zf4BKd0z+55R85UJlo4r5ezqCktk6fYy1atY4tGzo7Maqh6QoKw3LtIKvpz8d7WA==", "integrity": "sha512-Dxc/u/dxoneIDqyfmuwPVBR0G3OQJqe3Dtz4z3NGt+CGj4UuOZQfN5raJPmp6xGYgrtC6PAWoCgHhyrgr1qCtg==",
"requires": { "requires": {
"@vue/shared": "3.2.34" "@vue/shared": "3.2.25"
} }
}, },
"@vue/shared": { "@vue/shared": {
"version": "3.2.34", "version": "3.2.25",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.34.tgz", "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.25.tgz",
"integrity": "sha512-zhEeB8TrFmTXmTXmu/wcjEhgrjO4xqdDQrCdPhjX7NxfoLqoBVKguOm8qyihWNLbP+41svYY4za9mqXyqFLzNg==" "integrity": "sha512-DkHJFV2gw9WBRmUCa21eyG0WvlF0l1QFOgTkWj29O4mt2Tv3BSE5PQOKhUruZIym4bBYCqx9ZGtoD1WohDprow=="
} }
} }
}, },
"@vue/runtime-dom": { "@vue/runtime-dom": {
"version": "3.2.34", "version": "3.2.25",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.34.tgz", "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.25.tgz",
"integrity": "sha512-uqizbaJqmNH3O4TRr+8cM1tid5ODWHyQYZ3CLWcjn3dLkf0N7wvNuhUELQUZU/wQLvVMhJUQNrmOqckHLm6Xpw==", "integrity": "sha512-3gGeyHnygn4yG6bssRKhQIxnE8vgB8FtYUUwoYoA/Pm0vZ+bGPoZax4TbtZD9eW9rvs8CY8boNp4t/sJaPJrRQ==",
"requires": { "requires": {
"@vue/runtime-core": "3.2.34", "@vue/runtime-core": "3.2.25",
"@vue/shared": "3.2.34", "@vue/shared": "3.2.25",
"csstype": "^2.6.8" "csstype": "^2.6.8"
}, },
"dependencies": { "dependencies": {
"@vue/shared": { "@vue/shared": {
"version": "3.2.34", "version": "3.2.25",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.34.tgz", "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.25.tgz",
"integrity": "sha512-zhEeB8TrFmTXmTXmu/wcjEhgrjO4xqdDQrCdPhjX7NxfoLqoBVKguOm8qyihWNLbP+41svYY4za9mqXyqFLzNg==" "integrity": "sha512-DkHJFV2gw9WBRmUCa21eyG0WvlF0l1QFOgTkWj29O4mt2Tv3BSE5PQOKhUruZIym4bBYCqx9ZGtoD1WohDprow=="
} }
} }
}, },
"@vue/server-renderer": { "@vue/server-renderer": {
"version": "3.2.34", "version": "3.2.25",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.34.tgz", "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.25.tgz",
"integrity": "sha512-PMnBAq1BexPFXBxuLngp4lQvc0XQD1CBDIHtEsG0pRusGWVJddBUKlR/EnnSvGaJ34YmKkAl9kdvczOz0kddew==", "integrity": "sha512-qFRmcyeyyhWbnTPn6cbCZ4bjeuPLSkUpFa98p4LEJtFBFbxjGnrHXHOjYxCY3Lznmxe0kMM3qG4t3GnjcXP12w==",
"requires": { "requires": {
"@vue/compiler-ssr": "3.2.34", "@vue/compiler-ssr": "3.2.25",
"@vue/shared": "3.2.34" "@vue/shared": "3.2.25"
}, },
"dependencies": { "dependencies": {
"@vue/shared": { "@vue/shared": {
"version": "3.2.34", "version": "3.2.25",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.34.tgz", "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.25.tgz",
"integrity": "sha512-zhEeB8TrFmTXmTXmu/wcjEhgrjO4xqdDQrCdPhjX7NxfoLqoBVKguOm8qyihWNLbP+41svYY4za9mqXyqFLzNg==" "integrity": "sha512-DkHJFV2gw9WBRmUCa21eyG0WvlF0l1QFOgTkWj29O4mt2Tv3BSE5PQOKhUruZIym4bBYCqx9ZGtoD1WohDprow=="
} }
} }
}, },
@ -830,25 +796,18 @@
} }
}, },
"@vueuse/core": { "@vueuse/core": {
"version": "9.13.0", "version": "7.6.1",
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-9.13.0.tgz", "resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-7.6.1.tgz",
"integrity": "sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==", "integrity": "sha512-492y7R9HRu6TXzcGBMVG5qg5o9CHjrWLfOHh+TEknJeLe3LIYHsIBi1IlUN5s/yP3OHlBynjrzMMUm4gEyBmQg==",
"requires": { "requires": {
"@types/web-bluetooth": "^0.0.16", "@vueuse/shared": "7.6.1",
"@vueuse/metadata": "9.13.0",
"@vueuse/shared": "9.13.0",
"vue-demi": "*" "vue-demi": "*"
} }
}, },
"@vueuse/metadata": {
"version": "9.13.0",
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-9.13.0.tgz",
"integrity": "sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ=="
},
"@vueuse/shared": { "@vueuse/shared": {
"version": "9.13.0", "version": "7.6.1",
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-9.13.0.tgz", "resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-7.6.1.tgz",
"integrity": "sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==", "integrity": "sha512-VhURBjuyELYLW94TLqwyM+tUZ0uyWAOjp8zDnJts5wwyHZlGt/yabLbuEl70cKmt0zR9psVyAyHC+LTgRrA1Zw==",
"requires": { "requires": {
"vue-demi": "*" "vue-demi": "*"
} }
@ -1080,9 +1039,9 @@
"dev": true "dev": true
}, },
"async-validator": { "async-validator": {
"version": "4.2.5", "version": "4.0.7",
"resolved": "https://registry.npmjs.org/async-validator/-/async-validator-4.2.5.tgz", "resolved": "https://registry.npmmirror.com/async-validator/-/async-validator-4.0.7.tgz",
"integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==" "integrity": "sha512-Pj2IR7u8hmUEDOwB++su6baaRi+QvsgajuFB9j95foM1N2gy5HM4z60hfusIO0fBPG5uLAEl6yCJr1jNSVugEQ=="
}, },
"autoprefixer": { "autoprefixer": {
"version": "10.4.2", "version": "10.4.2",
@ -1236,9 +1195,9 @@
"dev": true "dev": true
}, },
"caniuse-lite": { "caniuse-lite": {
"version": "1.0.30001620", "version": "1.0.30001311",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001620.tgz", "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001311.tgz",
"integrity": "sha512-WJvYsOjd1/BYUY6SNGUosK9DUidBPDTnOARHp3fSmFO1ekdxaY6nKRttEVrfMmYi80ctS0kz1wiWmm14fVc3ew==", "integrity": "sha512-mleTFtFKfykEeW34EyfhGIFjGCqzhh38Y0LhdQ9aWF+HorZTtdgKV/1hEE0NlFkG2ubvisPV6l400tlbPys98A==",
"dev": true "dev": true
}, },
"capital-case": { "capital-case": {
@ -1761,42 +1720,21 @@
"dev": true "dev": true
}, },
"element-plus": { "element-plus": {
"version": "2.4.3", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/element-plus/-/element-plus-2.4.3.tgz", "resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.0.2.tgz",
"integrity": "sha512-b3q26j+lM4SBqiyzw8HybybGnP2pk4MWgrnzzzYW5qKQUgV6EG1Zg7nMCfgCVccI8tNvZoTiUHb2mFaiB9qT8w==", "integrity": "sha512-URjC0HwwiqtlLxqTmHXQ31WXrdAq4ChWyyn52OcQs3PRsnMPfahGVq2AWnfzzlzlhVeI5lY3HQiuB1zDathS+g==",
"requires": { "requires": {
"@ctrl/tinycolor": "^3.4.1", "@ctrl/tinycolor": "^3.4.0",
"@element-plus/icons-vue": "^2.3.1", "@element-plus/icons-vue": "^0.2.6",
"@floating-ui/dom": "^1.0.1", "@popperjs/core": "^2.11.2",
"@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.7", "@vueuse/core": "^7.6.0",
"@types/lodash": "^4.14.182", "async-validator": "^4.0.7",
"@types/lodash-es": "^4.17.6", "dayjs": "^1.10.7",
"@vueuse/core": "^9.1.0",
"async-validator": "^4.2.5",
"dayjs": "^1.11.3",
"escape-html": "^1.0.3",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"lodash-unified": "^1.0.2", "lodash-unified": "^1.0.1",
"memoize-one": "^6.0.0", "memoize-one": "^6.0.0",
"normalize-wheel-es": "^1.2.0" "normalize-wheel-es": "^1.1.1"
},
"dependencies": {
"@element-plus/icons-vue": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-2.3.1.tgz",
"integrity": "sha512-XxVUZv48RZAd87ucGS48jPf6pKu0yV5UCg9f4FFwtrYxXOwWuVJo6wOvSLKEoMQKjv8GsX/mhP6UsC1lRwbUWg=="
},
"@types/lodash": {
"version": "4.17.1",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.1.tgz",
"integrity": "sha512-X+2qazGS3jxLAIz5JDXDzglAF3KpijdhFxlf/V1+hEsOUc+HnWi81L/uv/EvGuV90WY+7mPGFCUDGfQC3Gj95Q=="
},
"dayjs": {
"version": "1.11.11",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.11.tgz",
"integrity": "sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg=="
}
} }
}, },
"emmet": { "emmet": {
@ -3431,13 +3369,13 @@
}, },
"lodash-es": { "lodash-es": {
"version": "4.17.21", "version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", "resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz",
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
}, },
"lodash-unified": { "lodash-unified": {
"version": "1.0.3", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/lodash-unified/-/lodash-unified-1.0.3.tgz", "resolved": "https://registry.npmmirror.com/lodash-unified/-/lodash-unified-1.0.1.tgz",
"integrity": "sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==" "integrity": "sha512-Py+twfpWn+2dFQWCuGcp21WiQRwZwnm1cyE3piSt/VtBVKVyxlR58WgOVRzXtmdmDRGJKH8F8GPaA29WK/yK8g=="
}, },
"lodash.clonedeep": { "lodash.clonedeep": {
"version": "4.5.0", "version": "4.5.0",
@ -3575,7 +3513,7 @@
}, },
"memoize-one": { "memoize-one": {
"version": "6.0.0", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", "resolved": "https://registry.npmmirror.com/memoize-one/-/memoize-one-6.0.0.tgz",
"integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==" "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="
}, },
"merge-descriptors": { "merge-descriptors": {
@ -3880,9 +3818,9 @@
"dev": true "dev": true
}, },
"normalize-wheel-es": { "normalize-wheel-es": {
"version": "1.2.0", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/normalize-wheel-es/-/normalize-wheel-es-1.2.0.tgz", "resolved": "https://registry.npmmirror.com/normalize-wheel-es/-/normalize-wheel-es-1.1.1.tgz",
"integrity": "sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==" "integrity": "sha512-157VNH4CngrcsvF8xOVOe22cwniIR3nxSltdctvQeHZj8JttEeOXffK28jucWfWBXs0QNetAumjc1GiInnwX4w=="
}, },
"nprogress": { "nprogress": {
"version": "0.2.0", "version": "0.2.0",
@ -5800,48 +5738,48 @@
} }
}, },
"vue": { "vue": {
"version": "3.2.34", "version": "3.2.25",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.2.34.tgz", "resolved": "https://registry.npmjs.org/vue/-/vue-3.2.25.tgz",
"integrity": "sha512-gXRg5v8OSmGT4ZiQ/X/Pcz6Fr2igHQx/wvRH/pLnt0VvjfGGqrwhnwjYZilLP4HBcO211rMD9PpU6lfWfIv3wg==", "integrity": "sha512-jU3t7fyQDHoCWCqhmRrnSmYZvHC35tOJTP704di7HGfq5EcFA1cU/1ZPjUV1eCxJev65Khjyfni+vk9oa+eTtw==",
"requires": { "requires": {
"@vue/compiler-dom": "3.2.34", "@vue/compiler-dom": "3.2.25",
"@vue/compiler-sfc": "3.2.34", "@vue/compiler-sfc": "3.2.25",
"@vue/runtime-dom": "3.2.34", "@vue/runtime-dom": "3.2.25",
"@vue/server-renderer": "3.2.34", "@vue/server-renderer": "3.2.25",
"@vue/shared": "3.2.34" "@vue/shared": "3.2.25"
}, },
"dependencies": { "dependencies": {
"@vue/compiler-core": { "@vue/compiler-core": {
"version": "3.2.34", "version": "3.2.25",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.34.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.25.tgz",
"integrity": "sha512-Y53lv04ZhDfqflhk4yEgBZrCL1RipbxqmqJFfl1PRkjOzt0bvJpf1sCNN81QNfXohVwFGf+Hng2ztwLwOZgbuA==", "integrity": "sha512-FlffKezIqztTCTyG0klkYRwhdyL6b1PTTCIerPb4p2R9qQaczccTX5g9ysi9w6tpLQ48a1WiXnFDJhWD7XoqwA==",
"requires": { "requires": {
"@babel/parser": "^7.16.4", "@babel/parser": "^7.16.4",
"@vue/shared": "3.2.34", "@vue/shared": "3.2.25",
"estree-walker": "^2.0.2", "estree-walker": "^2.0.2",
"source-map": "^0.6.1" "source-map": "^0.6.1"
} }
}, },
"@vue/compiler-dom": { "@vue/compiler-dom": {
"version": "3.2.34", "version": "3.2.25",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.34.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.25.tgz",
"integrity": "sha512-MFLUYDgy0aES9x1goU/pgxpzgT9IZOndO8qwQVSyVfUvl/CywEBtfBi5+8fsiBDhoGIT7g8qcsUUF1NYViU2vQ==", "integrity": "sha512-4JrburkRg4VWbc8AKpzKFWbNY4MDXshqjFl53+vINq7zaw3Z7aSqnLv0EkKh8B8ynf/MYsAdygGutyVbEWYxOw==",
"requires": { "requires": {
"@vue/compiler-core": "3.2.34", "@vue/compiler-core": "3.2.25",
"@vue/shared": "3.2.34" "@vue/shared": "3.2.25"
} }
}, },
"@vue/shared": { "@vue/shared": {
"version": "3.2.34", "version": "3.2.25",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.34.tgz", "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.25.tgz",
"integrity": "sha512-zhEeB8TrFmTXmTXmu/wcjEhgrjO4xqdDQrCdPhjX7NxfoLqoBVKguOm8qyihWNLbP+41svYY4za9mqXyqFLzNg==" "integrity": "sha512-DkHJFV2gw9WBRmUCa21eyG0WvlF0l1QFOgTkWj29O4mt2Tv3BSE5PQOKhUruZIym4bBYCqx9ZGtoD1WohDprow=="
} }
} }
}, },
"vue-demi": { "vue-demi": {
"version": "0.14.7", "version": "0.12.1",
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.7.tgz", "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.12.1.tgz",
"integrity": "sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==" "integrity": "sha512-QL3ny+wX8c6Xm1/EZylbgzdoDolye+VpCXRhI2hug9dJTP3OUJ3lmiKN3CsVV3mOJKwFi0nsstbgob0vG7aoIw=="
}, },
"vue-eslint-parser": { "vue-eslint-parser": {
"version": "7.11.0", "version": "7.11.0",

@ -6,8 +6,8 @@
"dev": "vite", "dev": "vite",
"test": "vite build --mode test", "test": "vite build --mode test",
"build": "vite build", "build": "vite build",
"lint": "eslint --ext .vue,.ts src/", "preview": "vite preview",
"lint:fix": "eslint --ext .vue,.ts src/ --fix" "plop": "plop"
}, },
"dependencies": { "dependencies": {
"@element-plus/icons-vue": "^0.2.7", "@element-plus/icons-vue": "^0.2.7",
@ -17,14 +17,14 @@
"core-js": "^3.21.0", "core-js": "^3.21.0",
"cropperjs": "^1.5.12", "cropperjs": "^1.5.12",
"dayjs": "^1.10.7", "dayjs": "^1.10.7",
"element-plus": "^2.4.3", "element-plus": "^2.0.2",
"js-cookie": "^3.0.1", "js-cookie": "^3.0.1",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"mavon-editor": "^2.10.4", "mavon-editor": "^2.10.4",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"path-to-regexp": "^6.2.0", "path-to-regexp": "^6.2.0",
"tinymce": "^5.9.2", "tinymce": "^5.9.2",
"vue": "^3.2.34", "vue": "^3.2.25",
"vue-i18n": "^9.2.0-beta.30", "vue-i18n": "^9.2.0-beta.30",
"vue-router": "^4.0.12", "vue-router": "^4.0.12",
"vuedraggable": "^4.1.0" "vuedraggable": "^4.1.0"

@ -0,0 +1,462 @@
tinymce.addI18n('zh_CN',{
"Redo": "\u91cd\u505a",
"Undo": "\u64a4\u9500",
"Cut": "\u526a\u5207",
"Copy": "\u590d\u5236",
"Paste": "\u7c98\u8d34",
"Select all": "\u5168\u9009",
"New document": "\u65b0\u6587\u4ef6",
"Ok": "\u786e\u5b9a",
"Cancel": "\u53d6\u6d88",
"Visual aids": "\u7f51\u683c\u7ebf",
"Bold": "\u7c97\u4f53",
"Italic": "\u659c\u4f53",
"Underline": "\u4e0b\u5212\u7ebf",
"Strikethrough": "\u5220\u9664\u7ebf",
"Superscript": "\u4e0a\u6807",
"Subscript": "\u4e0b\u6807",
"Clear formatting": "\u6e05\u9664\u683c\u5f0f",
"Align left": "\u5de6\u8fb9\u5bf9\u9f50",
"Align center": "\u4e2d\u95f4\u5bf9\u9f50",
"Align right": "\u53f3\u8fb9\u5bf9\u9f50",
"Justify": "\u4e24\u7aef\u5bf9\u9f50",
"Bullet list": "\u9879\u76ee\u7b26\u53f7",
"Numbered list": "\u7f16\u53f7\u5217\u8868",
"Decrease indent": "\u51cf\u5c11\u7f29\u8fdb",
"Increase indent": "\u589e\u52a0\u7f29\u8fdb",
"Close": "\u5173\u95ed",
"Formats": "\u683c\u5f0f",
"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "\u4f60\u7684\u6d4f\u89c8\u5668\u4e0d\u652f\u6301\u6253\u5f00\u526a\u8d34\u677f\uff0c\u8bf7\u4f7f\u7528Ctrl+X\/C\/V\u7b49\u5feb\u6377\u952e\u3002",
"Headers": "\u6807\u9898",
"Header 1": "\u6807\u98981",
"Header 2": "\u6807\u98982",
"Header 3": "\u6807\u98983",
"Header 4": "\u6807\u98984",
"Header 5": "\u6807\u98985",
"Header 6": "\u6807\u98986",
"Headings": "\u6807\u9898",
"Heading 1": "\u6807\u98981",
"Heading 2": "\u6807\u98982",
"Heading 3": "\u6807\u98983",
"Heading 4": "\u6807\u98984",
"Heading 5": "\u6807\u98985",
"Heading 6": "\u6807\u98986",
"Preformatted": "\u9884\u5148\u683c\u5f0f\u5316\u7684",
"Div": "Div",
"Pre": "Pre",
"Code": "\u4ee3\u7801",
"Paragraph": "\u6bb5\u843d",
"Blockquote": "\u5f15\u6587\u533a\u5757",
"Inline": "\u6587\u672c",
"Blocks": "\u57fa\u5757",
"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\u5f53\u524d\u4e3a\u7eaf\u6587\u672c\u7c98\u8d34\u6a21\u5f0f\uff0c\u518d\u6b21\u70b9\u51fb\u53ef\u4ee5\u56de\u5230\u666e\u901a\u7c98\u8d34\u6a21\u5f0f\u3002",
"Fonts": "\u5b57\u4f53",
"Font Sizes": "\u5b57\u53f7",
"Class": "\u7c7b\u578b",
"Browse for an image": "\u6d4f\u89c8\u56fe\u50cf",
"OR": "\u6216",
"Drop an image here": "\u62d6\u653e\u4e00\u5f20\u56fe\u50cf\u81f3\u6b64",
"Upload": "\u4e0a\u4f20",
"Block": "\u5757",
"Align": "\u5bf9\u9f50",
"Default": "\u9ed8\u8ba4",
"Circle": "\u7a7a\u5fc3\u5706",
"Disc": "\u5b9e\u5fc3\u5706",
"Square": "\u65b9\u5757",
"Lower Alpha": "\u5c0f\u5199\u82f1\u6587\u5b57\u6bcd",
"Lower Greek": "\u5c0f\u5199\u5e0c\u814a\u5b57\u6bcd",
"Lower Roman": "\u5c0f\u5199\u7f57\u9a6c\u5b57\u6bcd",
"Upper Alpha": "\u5927\u5199\u82f1\u6587\u5b57\u6bcd",
"Upper Roman": "\u5927\u5199\u7f57\u9a6c\u5b57\u6bcd",
"Anchor...": "\u951a\u70b9...",
"Name": "\u540d\u79f0",
"Id": "\u6807\u8bc6\u7b26",
"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "\u6807\u8bc6\u7b26\u5e94\u8be5\u4ee5\u5b57\u6bcd\u5f00\u5934\uff0c\u540e\u8ddf\u5b57\u6bcd\u3001\u6570\u5b57\u3001\u7834\u6298\u53f7\u3001\u70b9\u3001\u5192\u53f7\u6216\u4e0b\u5212\u7ebf\u3002",
"You have unsaved changes are you sure you want to navigate away?": "\u4f60\u8fd8\u6709\u6587\u6863\u5c1a\u672a\u4fdd\u5b58\uff0c\u786e\u5b9a\u8981\u79bb\u5f00\uff1f",
"Restore last draft": "\u6062\u590d\u4e0a\u6b21\u7684\u8349\u7a3f",
"Special character...": "\u7279\u6b8a\u5b57\u7b26...",
"Source code": "\u6e90\u4ee3\u7801",
"Insert\/Edit code sample": "\u63d2\u5165\/\u7f16\u8f91\u4ee3\u7801\u793a\u4f8b",
"Language": "\u8bed\u8a00",
"Code sample...": "\u793a\u4f8b\u4ee3\u7801...",
"Color Picker": "\u9009\u8272\u5668",
"R": "R",
"G": "G",
"B": "B",
"Left to right": "\u4ece\u5de6\u5230\u53f3",
"Right to left": "\u4ece\u53f3\u5230\u5de6",
"Emoticons": "\u8868\u60c5",
"Emoticons...": "\u8868\u60c5\u7b26\u53f7...",
"Metadata and Document Properties": "\u5143\u6570\u636e\u548c\u6587\u6863\u5c5e\u6027",
"Title": "\u6807\u9898",
"Keywords": "\u5173\u952e\u8bcd",
"Description": "\u63cf\u8ff0",
"Robots": "\u673a\u5668\u4eba",
"Author": "\u4f5c\u8005",
"Encoding": "\u7f16\u7801",
"Fullscreen": "\u5168\u5c4f",
"Action": "\u64cd\u4f5c",
"Shortcut": "\u5feb\u6377\u952e",
"Help": "\u5e2e\u52a9",
"Address": "\u5730\u5740",
"Focus to menubar": "\u79fb\u52a8\u7126\u70b9\u5230\u83dc\u5355\u680f",
"Focus to toolbar": "\u79fb\u52a8\u7126\u70b9\u5230\u5de5\u5177\u680f",
"Focus to element path": "\u79fb\u52a8\u7126\u70b9\u5230\u5143\u7d20\u8def\u5f84",
"Focus to contextual toolbar": "\u79fb\u52a8\u7126\u70b9\u5230\u4e0a\u4e0b\u6587\u83dc\u5355",
"Insert link (if link plugin activated)": "\u63d2\u5165\u94fe\u63a5 (\u5982\u679c\u94fe\u63a5\u63d2\u4ef6\u5df2\u6fc0\u6d3b)",
"Save (if save plugin activated)": "\u4fdd\u5b58(\u5982\u679c\u4fdd\u5b58\u63d2\u4ef6\u5df2\u6fc0\u6d3b)",
"Find (if searchreplace plugin activated)": "\u67e5\u627e(\u5982\u679c\u67e5\u627e\u66ff\u6362\u63d2\u4ef6\u5df2\u6fc0\u6d3b)",
"Plugins installed ({0}):": "\u5df2\u5b89\u88c5\u63d2\u4ef6 ({0}):",
"Premium plugins:": "\u4f18\u79c0\u63d2\u4ef6\uff1a",
"Learn more...": "\u4e86\u89e3\u66f4\u591a...",
"You are using {0}": "\u4f60\u6b63\u5728\u4f7f\u7528 {0}",
"Plugins": "\u63d2\u4ef6",
"Handy Shortcuts": "\u5feb\u6377\u952e",
"Horizontal line": "\u6c34\u5e73\u5206\u5272\u7ebf",
"Insert\/edit image": "\u63d2\u5165\/\u7f16\u8f91\u56fe\u7247",
"Alternative description": "\u66ff\u4ee3\u63cf\u8ff0",
"Accessibility": "\u8f85\u52a9\u529f\u80fd",
"Image is decorative": "\u56fe\u50cf\u662f\u88c5\u9970\u6027\u7684",
"Source": "\u5730\u5740",
"Dimensions": "\u5927\u5c0f",
"Constrain proportions": "\u4fdd\u6301\u7eb5\u6a2a\u6bd4",
"General": "\u666e\u901a",
"Advanced": "\u9ad8\u7ea7",
"Style": "\u6837\u5f0f",
"Vertical space": "\u5782\u76f4\u8fb9\u8ddd",
"Horizontal space": "\u6c34\u5e73\u8fb9\u8ddd",
"Border": "\u8fb9\u6846",
"Insert image": "\u63d2\u5165\u56fe\u7247",
"Image...": "\u56fe\u7247...",
"Image list": "\u56fe\u7247\u5217\u8868",
"Rotate counterclockwise": "\u9006\u65f6\u9488\u65cb\u8f6c",
"Rotate clockwise": "\u987a\u65f6\u9488\u65cb\u8f6c",
"Flip vertically": "\u5782\u76f4\u7ffb\u8f6c",
"Flip horizontally": "\u6c34\u5e73\u7ffb\u8f6c",
"Edit image": "\u7f16\u8f91\u56fe\u7247",
"Image options": "\u56fe\u7247\u9009\u9879",
"Zoom in": "\u653e\u5927",
"Zoom out": "\u7f29\u5c0f",
"Crop": "\u88c1\u526a",
"Resize": "\u8c03\u6574\u5927\u5c0f",
"Orientation": "\u65b9\u5411",
"Brightness": "\u4eae\u5ea6",
"Sharpen": "\u9510\u5316",
"Contrast": "\u5bf9\u6bd4\u5ea6",
"Color levels": "\u989c\u8272\u5c42\u6b21",
"Gamma": "\u4f3d\u9a6c\u503c",
"Invert": "\u53cd\u8f6c",
"Apply": "\u5e94\u7528",
"Back": "\u540e\u9000",
"Insert date\/time": "\u63d2\u5165\u65e5\u671f\/\u65f6\u95f4",
"Date\/time": "\u65e5\u671f\/\u65f6\u95f4",
"Insert\/edit link": "\u63d2\u5165\/\u7f16\u8f91\u94fe\u63a5",
"Text to display": "\u663e\u793a\u6587\u5b57",
"Url": "\u5730\u5740",
"Open link in...": "\u94fe\u63a5\u6253\u5f00\u4f4d\u7f6e...",
"Current window": "\u5f53\u524d\u7a97\u53e3",
"None": "\u65e0",
"New window": "\u5728\u65b0\u7a97\u53e3\u6253\u5f00",
"Open link": "\u6253\u5f00\u94fe\u63a5",
"Remove link": "\u5220\u9664\u94fe\u63a5",
"Anchors": "\u951a\u70b9",
"Link...": "\u94fe\u63a5...",
"Paste or type a link": "\u7c98\u8d34\u6216\u8f93\u5165\u94fe\u63a5",
"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "\u4f60\u6240\u586b\u5199\u7684URL\u5730\u5740\u4e3a\u90ae\u4ef6\u5730\u5740\uff0c\u9700\u8981\u52a0\u4e0amailto:\u524d\u7f00\u5417\uff1f",
"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "\u4f60\u6240\u586b\u5199\u7684URL\u5730\u5740\u5c5e\u4e8e\u5916\u90e8\u94fe\u63a5\uff0c\u9700\u8981\u52a0\u4e0ahttp:\/\/:\u524d\u7f00\u5417\uff1f",
"The URL you entered seems to be an external link. Do you want to add the required https:\/\/ prefix?": "\u60a8\u8f93\u5165\u7684 URL \u4f3c\u4e4e\u662f\u4e00\u4e2a\u5916\u90e8\u94fe\u63a5\u3002\u60a8\u60f3\u6dfb\u52a0\u6240\u9700\u7684 https:\/\/ \u524d\u7f00\u5417\uff1f",
"Link list": "\u94fe\u63a5\u5217\u8868",
"Insert video": "\u63d2\u5165\u89c6\u9891",
"Insert\/edit video": "\u63d2\u5165\/\u7f16\u8f91\u89c6\u9891",
"Insert\/edit media": "\u63d2\u5165\/\u7f16\u8f91\u5a92\u4f53",
"Alternative source": "\u955c\u50cf",
"Alternative source URL": "\u66ff\u4ee3\u6765\u6e90\u7f51\u5740",
"Media poster (Image URL)": "\u5c01\u9762(\u56fe\u7247\u5730\u5740)",
"Paste your embed code below:": "\u5c06\u5185\u5d4c\u4ee3\u7801\u7c98\u8d34\u5728\u4e0b\u9762:",
"Embed": "\u5185\u5d4c",
"Media...": "\u591a\u5a92\u4f53...",
"Nonbreaking space": "\u4e0d\u95f4\u65ad\u7a7a\u683c",
"Page break": "\u5206\u9875\u7b26",
"Paste as text": "\u7c98\u8d34\u4e3a\u6587\u672c",
"Preview": "\u9884\u89c8",
"Print...": "\u6253\u5370...",
"Save": "\u4fdd\u5b58",
"Find": "\u67e5\u627e",
"Replace with": "\u66ff\u6362\u4e3a",
"Replace": "\u66ff\u6362",
"Replace all": "\u5168\u90e8\u66ff\u6362",
"Previous": "\u4e0a\u4e00\u4e2a",
"Next": "\u4e0b\u4e00\u4e2a",
"Find and Replace": "\u67e5\u627e\u548c\u66ff\u6362",
"Find and replace...": "\u67e5\u627e\u5e76\u66ff\u6362...",
"Could not find the specified string.": "\u672a\u627e\u5230\u641c\u7d22\u5185\u5bb9.",
"Match case": "\u533a\u5206\u5927\u5c0f\u5199",
"Find whole words only": "\u5168\u5b57\u5339\u914d",
"Find in selection": "\u5728\u9009\u533a\u4e2d\u67e5\u627e",
"Spellcheck": "\u62fc\u5199\u68c0\u67e5",
"Spellcheck Language": "\u62fc\u5199\u68c0\u67e5\u8bed\u8a00",
"No misspellings found.": "\u6ca1\u6709\u53d1\u73b0\u62fc\u5199\u9519\u8bef",
"Ignore": "\u5ffd\u7565",
"Ignore all": "\u5168\u90e8\u5ffd\u7565",
"Finish": "\u5b8c\u6210",
"Add to Dictionary": "\u6dfb\u52a0\u5230\u5b57\u5178",
"Insert table": "\u63d2\u5165\u8868\u683c",
"Table properties": "\u8868\u683c\u5c5e\u6027",
"Delete table": "\u5220\u9664\u8868\u683c",
"Cell": "\u5355\u5143\u683c",
"Row": "\u884c",
"Column": "\u5217",
"Cell properties": "\u5355\u5143\u683c\u5c5e\u6027",
"Merge cells": "\u5408\u5e76\u5355\u5143\u683c",
"Split cell": "\u62c6\u5206\u5355\u5143\u683c",
"Insert row before": "\u5728\u4e0a\u65b9\u63d2\u5165",
"Insert row after": "\u5728\u4e0b\u65b9\u63d2\u5165",
"Delete row": "\u5220\u9664\u884c",
"Row properties": "\u884c\u5c5e\u6027",
"Cut row": "\u526a\u5207\u884c",
"Copy row": "\u590d\u5236\u884c",
"Paste row before": "\u7c98\u8d34\u5230\u4e0a\u65b9",
"Paste row after": "\u7c98\u8d34\u5230\u4e0b\u65b9",
"Insert column before": "\u5728\u5de6\u4fa7\u63d2\u5165",
"Insert column after": "\u5728\u53f3\u4fa7\u63d2\u5165",
"Delete column": "\u5220\u9664\u5217",
"Cols": "\u5217",
"Rows": "\u884c",
"Width": "\u5bbd",
"Height": "\u9ad8",
"Cell spacing": "\u5355\u5143\u683c\u5916\u95f4\u8ddd",
"Cell padding": "\u5355\u5143\u683c\u5185\u8fb9\u8ddd",
"Caption": "\u6807\u9898",
"Show caption": "\u663e\u793a\u6807\u9898",
"Left": "\u5de6\u5bf9\u9f50",
"Center": "\u5c45\u4e2d",
"Right": "\u53f3\u5bf9\u9f50",
"Cell type": "\u5355\u5143\u683c\u7c7b\u578b",
"Scope": "\u8303\u56f4",
"Alignment": "\u5bf9\u9f50\u65b9\u5f0f",
"H Align": "\u6c34\u5e73\u5bf9\u9f50",
"V Align": "\u5782\u76f4\u5bf9\u9f50",
"Top": "\u9876\u90e8\u5bf9\u9f50",
"Middle": "\u5782\u76f4\u5c45\u4e2d",
"Bottom": "\u5e95\u90e8\u5bf9\u9f50",
"Header cell": "\u8868\u5934\u5355\u5143\u683c",
"Row group": "\u884c\u7ec4",
"Column group": "\u5217\u7ec4",
"Row type": "\u884c\u7c7b\u578b",
"Header": "\u8868\u5934",
"Body": "\u8868\u4f53",
"Footer": "\u8868\u5c3e",
"Border color": "\u8fb9\u6846\u989c\u8272",
"Insert template...": "\u63d2\u5165\u6a21\u677f...",
"Templates": "\u6a21\u677f",
"Template": "\u6a21\u677f",
"Text color": "\u6587\u5b57\u989c\u8272",
"Background color": "\u80cc\u666f\u8272",
"Custom...": "\u81ea\u5b9a\u4e49...",
"Custom color": "\u81ea\u5b9a\u4e49\u989c\u8272",
"No color": "\u65e0",
"Remove color": "\u79fb\u9664\u989c\u8272",
"Table of Contents": "\u5185\u5bb9\u5217\u8868",
"Show blocks": "\u663e\u793a\u533a\u5757\u8fb9\u6846",
"Show invisible characters": "\u663e\u793a\u4e0d\u53ef\u89c1\u5b57\u7b26",
"Word count": "\u5b57\u6570",
"Count": "\u8ba1\u6570",
"Document": "\u6587\u6863",
"Selection": "\u9009\u62e9",
"Words": "\u5355\u8bcd",
"Words: {0}": "\u5b57\u6570\uff1a{0}",
"{0} words": "{0} \u5b57",
"File": "\u6587\u4ef6",
"Edit": "\u7f16\u8f91",
"Insert": "\u63d2\u5165",
"View": "\u89c6\u56fe",
"Format": "\u683c\u5f0f",
"Table": "\u8868\u683c",
"Tools": "\u5de5\u5177",
"Powered by {0}": "\u7531{0}\u9a71\u52a8",
"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u5728\u7f16\u8f91\u533a\u6309ALT-F9\u6253\u5f00\u83dc\u5355\uff0c\u6309ALT-F10\u6253\u5f00\u5de5\u5177\u680f\uff0c\u6309ALT-0\u67e5\u770b\u5e2e\u52a9",
"Image title": "\u56fe\u7247\u6807\u9898",
"Border width": "\u8fb9\u6846\u5bbd\u5ea6",
"Border style": "\u8fb9\u6846\u6837\u5f0f",
"Error": "\u9519\u8bef",
"Warn": "\u8b66\u544a",
"Valid": "\u6709\u6548",
"To open the popup, press Shift+Enter": "\u6309Shitf+Enter\u952e\u6253\u5f00\u5bf9\u8bdd\u6846",
"Rich Text Area. Press ALT-0 for help.": "\u7f16\u8f91\u533a\u3002\u6309Alt+0\u952e\u6253\u5f00\u5e2e\u52a9\u3002",
"System Font": "\u7cfb\u7edf\u5b57\u4f53",
"Failed to upload image: {0}": "\u56fe\u7247\u4e0a\u4f20\u5931\u8d25: {0}",
"Failed to load plugin: {0} from url {1}": "\u63d2\u4ef6\u52a0\u8f7d\u5931\u8d25: {0} \u6765\u81ea\u94fe\u63a5 {1}",
"Failed to load plugin url: {0}": "\u63d2\u4ef6\u52a0\u8f7d\u5931\u8d25 \u94fe\u63a5: {0}",
"Failed to initialize plugin: {0}": "\u63d2\u4ef6\u521d\u59cb\u5316\u5931\u8d25: {0}",
"example": "\u793a\u4f8b",
"Search": "\u641c\u7d22",
"All": "\u5168\u90e8",
"Currency": "\u8d27\u5e01",
"Text": "\u6587\u5b57",
"Quotations": "\u5f15\u7528",
"Mathematical": "\u6570\u5b66",
"Extended Latin": "\u62c9\u4e01\u8bed\u6269\u5145",
"Symbols": "\u7b26\u53f7",
"Arrows": "\u7bad\u5934",
"User Defined": "\u81ea\u5b9a\u4e49",
"dollar sign": "\u7f8e\u5143\u7b26\u53f7",
"currency sign": "\u8d27\u5e01\u7b26\u53f7",
"euro-currency sign": "\u6b27\u5143\u7b26\u53f7",
"colon sign": "\u5192\u53f7",
"cruzeiro sign": "\u514b\u9c81\u8d5b\u7f57\u5e01\u7b26\u53f7",
"french franc sign": "\u6cd5\u90ce\u7b26\u53f7",
"lira sign": "\u91cc\u62c9\u7b26\u53f7",
"mill sign": "\u5bc6\u5c14\u7b26\u53f7",
"naira sign": "\u5948\u62c9\u7b26\u53f7",
"peseta sign": "\u6bd4\u585e\u5854\u7b26\u53f7",
"rupee sign": "\u5362\u6bd4\u7b26\u53f7",
"won sign": "\u97e9\u5143\u7b26\u53f7",
"new sheqel sign": "\u65b0\u8c22\u514b\u5c14\u7b26\u53f7",
"dong sign": "\u8d8a\u5357\u76fe\u7b26\u53f7",
"kip sign": "\u8001\u631d\u57fa\u666e\u7b26\u53f7",
"tugrik sign": "\u56fe\u683c\u91cc\u514b\u7b26\u53f7",
"drachma sign": "\u5fb7\u62c9\u514b\u9a6c\u7b26\u53f7",
"german penny symbol": "\u5fb7\u56fd\u4fbf\u58eb\u7b26\u53f7",
"peso sign": "\u6bd4\u7d22\u7b26\u53f7",
"guarani sign": "\u74dc\u62c9\u5c3c\u7b26\u53f7",
"austral sign": "\u6fb3\u5143\u7b26\u53f7",
"hryvnia sign": "\u683c\u91cc\u592b\u5c3c\u4e9a\u7b26\u53f7",
"cedi sign": "\u585e\u5730\u7b26\u53f7",
"livre tournois sign": "\u91cc\u5f17\u5f17\u5c14\u7b26\u53f7",
"spesmilo sign": "spesmilo\u7b26\u53f7",
"tenge sign": "\u575a\u6208\u7b26\u53f7",
"indian rupee sign": "\u5370\u5ea6\u5362\u6bd4",
"turkish lira sign": "\u571f\u8033\u5176\u91cc\u62c9",
"nordic mark sign": "\u5317\u6b27\u9a6c\u514b",
"manat sign": "\u9a6c\u7eb3\u7279\u7b26\u53f7",
"ruble sign": "\u5362\u5e03\u7b26\u53f7",
"yen character": "\u65e5\u5143\u5b57\u6837",
"yuan character": "\u4eba\u6c11\u5e01\u5143\u5b57\u6837",
"yuan character, in hong kong and taiwan": "\u5143\u5b57\u6837\uff08\u6e2f\u53f0\u5730\u533a\uff09",
"yen\/yuan character variant one": "\u5143\u5b57\u6837\uff08\u5927\u5199\uff09",
"Loading emoticons...": "\u52a0\u8f7d\u8868\u60c5\u7b26\u53f7...",
"Could not load emoticons": "\u4e0d\u80fd\u52a0\u8f7d\u8868\u60c5\u7b26\u53f7",
"People": "\u4eba\u7c7b",
"Animals and Nature": "\u52a8\u7269\u548c\u81ea\u7136",
"Food and Drink": "\u98df\u7269\u548c\u996e\u54c1",
"Activity": "\u6d3b\u52a8",
"Travel and Places": "\u65c5\u6e38\u548c\u5730\u70b9",
"Objects": "\u7269\u4ef6",
"Flags": "\u65d7\u5e1c",
"Characters": "\u5b57\u7b26",
"Characters (no spaces)": "\u5b57\u7b26(\u65e0\u7a7a\u683c)",
"{0} characters": "{0} \u4e2a\u5b57\u7b26",
"Error: Form submit field collision.": "\u9519\u8bef: \u8868\u5355\u63d0\u4ea4\u5b57\u6bb5\u51b2\u7a81\u3002",
"Error: No form element found.": "\u9519\u8bef: \u6ca1\u6709\u8868\u5355\u63a7\u4ef6\u3002",
"Update": "\u66f4\u65b0",
"Color swatch": "\u989c\u8272\u6837\u672c",
"Turquoise": "\u9752\u7eff\u8272",
"Green": "\u7eff\u8272",
"Blue": "\u84dd\u8272",
"Purple": "\u7d2b\u8272",
"Navy Blue": "\u6d77\u519b\u84dd",
"Dark Turquoise": "\u6df1\u84dd\u7eff\u8272",
"Dark Green": "\u6df1\u7eff\u8272",
"Medium Blue": "\u4e2d\u84dd\u8272",
"Medium Purple": "\u4e2d\u7d2b\u8272",
"Midnight Blue": "\u6df1\u84dd\u8272",
"Yellow": "\u9ec4\u8272",
"Orange": "\u6a59\u8272",
"Red": "\u7ea2\u8272",
"Light Gray": "\u6d45\u7070\u8272",
"Gray": "\u7070\u8272",
"Dark Yellow": "\u6697\u9ec4\u8272",
"Dark Orange": "\u6df1\u6a59\u8272",
"Dark Red": "\u6df1\u7ea2\u8272",
"Medium Gray": "\u4e2d\u7070\u8272",
"Dark Gray": "\u6df1\u7070\u8272",
"Light Green": "\u6d45\u7eff\u8272",
"Light Yellow": "\u6d45\u9ec4\u8272",
"Light Red": "\u6d45\u7ea2\u8272",
"Light Purple": "\u6d45\u7d2b\u8272",
"Light Blue": "\u6d45\u84dd\u8272",
"Dark Purple": "\u6df1\u7d2b\u8272",
"Dark Blue": "\u6df1\u84dd\u8272",
"Black": "\u9ed1\u8272",
"White": "\u767d\u8272",
"Switch to or from fullscreen mode": "\u5207\u6362\u5168\u5c4f\u6a21\u5f0f",
"Open help dialog": "\u6253\u5f00\u5e2e\u52a9\u5bf9\u8bdd\u6846",
"history": "\u5386\u53f2",
"styles": "\u6837\u5f0f",
"formatting": "\u683c\u5f0f\u5316",
"alignment": "\u5bf9\u9f50",
"indentation": "\u7f29\u8fdb",
"Font": "\u5b57\u4f53",
"Size": "\u5b57\u53f7",
"More...": "\u66f4\u591a...",
"Select...": "\u9009\u62e9...",
"Preferences": "\u9996\u9009\u9879",
"Yes": "\u662f",
"No": "\u5426",
"Keyboard Navigation": "\u952e\u76d8\u6307\u5f15",
"Version": "\u7248\u672c",
"Code view": "\u4ee3\u7801\u89c6\u56fe",
"Open popup menu for split buttons": "\u6253\u5f00\u5f39\u51fa\u5f0f\u83dc\u5355\uff0c\u7528\u4e8e\u62c6\u5206\u6309\u94ae",
"List Properties": "\u5217\u8868\u5c5e\u6027",
"List properties...": "\u6807\u9898\u5b57\u4f53\u5c5e\u6027",
"Start list at number": "\u4ee5\u6570\u5b57\u5f00\u59cb\u5217\u8868",
"Line height": "\u884c\u9ad8",
"comments": "\u5907\u6ce8",
"Format Painter": "\u683c\u5f0f\u5237",
"Insert\/edit iframe": "\u63d2\u5165\/\u7f16\u8f91\u6846\u67b6",
"Capitalization": "\u5927\u5199",
"lowercase": "\u5c0f\u5199",
"UPPERCASE": "\u5927\u5199",
"Title Case": "\u9996\u5b57\u6bcd\u5927\u5199",
"permanent pen": "\u8bb0\u53f7\u7b14",
"Permanent Pen Properties": "\u6c38\u4e45\u7b14\u5c5e\u6027",
"Permanent pen properties...": "\u6c38\u4e45\u7b14\u5c5e\u6027...",
"case change": "\u6848\u4f8b\u66f4\u6539",
"page embed": "\u9875\u9762\u5d4c\u5165",
"Advanced sort...": "\u9ad8\u7ea7\u6392\u5e8f...",
"Advanced Sort": "\u9ad8\u7ea7\u6392\u5e8f",
"Sort table by column ascending": "\u6309\u5217\u5347\u5e8f\u8868",
"Sort table by column descending": "\u6309\u5217\u964d\u5e8f\u8868",
"Sort": "\u6392\u5e8f",
"Order": "\u6392\u5e8f",
"Sort by": "\u6392\u5e8f\u65b9\u5f0f",
"Ascending": "\u5347\u5e8f",
"Descending": "\u964d\u5e8f",
"Column {0}": "\u5217{0}",
"Row {0}": "\u884c{0}",
"Spellcheck...": "\u62fc\u5199\u68c0\u67e5...",
"Misspelled word": "\u62fc\u5199\u9519\u8bef\u7684\u5355\u8bcd",
"Suggestions": "\u5efa\u8bae",
"Change": "\u66f4\u6539",
"Finding word suggestions": "\u67e5\u627e\u5355\u8bcd\u5efa\u8bae",
"Success": "\u6210\u529f",
"Repair": "\u4fee\u590d",
"Issue {0} of {1}": "\u5171\u8ba1{1}\u95ee\u9898{0}",
"Images must be marked as decorative or have an alternative text description": "\u56fe\u50cf\u5fc5\u987b\u6807\u8bb0\u4e3a\u88c5\u9970\u6027\u6216\u5177\u6709\u66ff\u4ee3\u6587\u672c\u63cf\u8ff0",
"Images must have an alternative text description. Decorative images are not allowed.": "\u56fe\u50cf\u5fc5\u987b\u5177\u6709\u66ff\u4ee3\u6587\u672c\u63cf\u8ff0\u3002\u4e0d\u5141\u8bb8\u4f7f\u7528\u88c5\u9970\u56fe\u50cf\u3002",
"Or provide alternative text:": "\u6216\u63d0\u4f9b\u5907\u9009\u6587\u672c\uff1a",
"Make image decorative:": "\u4f7f\u56fe\u50cf\u88c5\u9970\uff1a",
"ID attribute must be unique": "ID \u5c5e\u6027\u5fc5\u987b\u662f\u552f\u4e00\u7684",
"Make ID unique": "\u4f7f ID \u72ec\u4e00\u65e0\u4e8c",
"Keep this ID and remove all others": "\u4fdd\u7559\u6b64 ID \u5e76\u5220\u9664\u6240\u6709\u5176\u4ed6",
"Remove this ID": "\u5220\u9664\u6b64 ID",
"Remove all IDs": "\u6e05\u9664\u5168\u90e8IDs",
"Checklist": "\u6e05\u5355",
"Anchor": "\u951a\u70b9",
"Special character": "\u7279\u6b8a\u7b26\u53f7",
"Code sample": "\u4ee3\u7801\u793a\u4f8b",
"Color": "\u989c\u8272",
"Document properties": "\u6587\u6863\u5c5e\u6027",
"Image description": "\u56fe\u7247\u63cf\u8ff0",
"Image": "\u56fe\u7247",
"Insert link": "\u63d2\u5165\u94fe\u63a5",
"Target": "\u6253\u5f00\u65b9\u5f0f",
"Link": "\u94fe\u63a5",
"Poster": "\u5c01\u9762",
"Media": "\u5a92\u4f53",
"Print": "\u6253\u5370",
"Prev": "\u4e0a\u4e00\u4e2a",
"Find and replace": "\u67e5\u627e\u548c\u66ff\u6362",
"Whole words": "\u5168\u5b57\u5339\u914d",
"Insert template": "\u63d2\u5165\u6a21\u677f"
});

@ -0,0 +1,419 @@
tinymce.addI18n('zh_TW',{
"Redo": "\u91cd\u505a",
"Undo": "\u64a4\u92b7",
"Cut": "\u526a\u4e0b",
"Copy": "\u8907\u88fd",
"Paste": "\u8cbc\u4e0a",
"Select all": "\u5168\u9078",
"New document": "\u65b0\u6587\u4ef6",
"Ok": "\u78ba\u5b9a",
"Cancel": "\u53d6\u6d88",
"Visual aids": "\u5c0f\u5e6b\u624b",
"Bold": "\u7c97\u9ad4",
"Italic": "\u659c\u9ad4",
"Underline": "\u4e0b\u5283\u7dda",
"Strikethrough": "\u522a\u9664\u7dda",
"Superscript": "\u4e0a\u6a19",
"Subscript": "\u4e0b\u6a19",
"Clear formatting": "\u6e05\u9664\u683c\u5f0f",
"Align left": "\u5de6\u908a\u5c0d\u9f4a",
"Align center": "\u4e2d\u9593\u5c0d\u9f4a",
"Align right": "\u53f3\u908a\u5c0d\u9f4a",
"Justify": "\u5de6\u53f3\u5c0d\u9f4a",
"Bullet list": "\u9805\u76ee\u6e05\u55ae",
"Numbered list": "\u6578\u5b57\u6e05\u55ae",
"Decrease indent": "\u6e1b\u5c11\u7e2e\u6392",
"Increase indent": "\u589e\u52a0\u7e2e\u6392",
"Close": "\u95dc\u9589",
"Formats": "\u683c\u5f0f",
"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "\u60a8\u7684\u700f\u89bd\u5668\u4e0d\u652f\u63f4\u5b58\u53d6\u526a\u8cbc\u7c3f\uff0c\u53ef\u4ee5\u4f7f\u7528\u5feb\u901f\u9375 Ctrl + X\/C\/V \u4ee3\u66ff\u526a\u4e0b\u3001\u8907\u88fd\u8207\u8cbc\u4e0a\u3002",
"Headers": "\u6a19\u984c",
"Header 1": "\u6a19\u984c 1",
"Header 2": "\u6a19\u984c 2",
"Header 3": "\u6a19\u984c 3",
"Header 4": "\u6a19\u984c 4",
"Header 5": "\u6a19\u984c 5",
"Header 6": "\u6a19\u984c 6",
"Headings": "\u6a19\u984c",
"Heading 1": "\u6a19\u984c1",
"Heading 2": "\u6a19\u984c2",
"Heading 3": "\u6a19\u984c3",
"Heading 4": "\u6a19\u984c4",
"Heading 5": "\u6a19\u984c5",
"Heading 6": "\u6a19\u984c6",
"Preformatted": "\u9810\u5148\u683c\u5f0f\u5316\u7684",
"Div": "Div",
"Pre": "Pre",
"Code": "\u4ee3\u78bc",
"Paragraph": "\u6bb5\u843d",
"Blockquote": "\u5f15\u6587\u5340\u584a",
"Inline": "\u5167\u806f",
"Blocks": "\u57fa\u584a",
"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\u76ee\u524d\u5c07\u4ee5\u7d14\u6587\u5b57\u7684\u6a21\u5f0f\u8cbc\u4e0a\uff0c\u60a8\u53ef\u4ee5\u518d\u9ede\u9078\u4e00\u6b21\u53d6\u6d88\u3002",
"Fonts": "\u5b57\u578b",
"Font Sizes": "\u5b57\u578b\u5927\u5c0f",
"Class": "\u985e\u578b",
"Browse for an image": "\u5f9e\u5716\u7247\u4e2d\u700f\u89bd",
"OR": "\u6216",
"Drop an image here": "\u62d6\u66f3\u5716\u7247\u81f3\u6b64",
"Upload": "\u4e0a\u50b3",
"Block": "\u5340\u584a",
"Align": "\u5c0d\u9f4a",
"Default": "\u9810\u8a2d",
"Circle": "\u7a7a\u5fc3\u5713",
"Disc": "\u5be6\u5fc3\u5713",
"Square": "\u6b63\u65b9\u5f62",
"Lower Alpha": "\u5c0f\u5beb\u82f1\u6587\u5b57\u6bcd",
"Lower Greek": "\u5e0c\u81d8\u5b57\u6bcd",
"Lower Roman": "\u5c0f\u5beb\u7f85\u99ac\u6578\u5b57",
"Upper Alpha": "\u5927\u5beb\u82f1\u6587\u5b57\u6bcd",
"Upper Roman": "\u5927\u5beb\u7f85\u99ac\u6578\u5b57",
"Anchor...": "\u9328\u9ede...",
"Name": "\u540d\u7a31",
"Id": "Id",
"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "Id\u61c9\u4ee5\u5b57\u6bcd\u958b\u982d\uff0c\u5f8c\u9762\u63a5\u8457\u5b57\u6bcd\uff0c\u6578\u5b57\uff0c\u7834\u6298\u865f\uff0c\u9ede\u6578\uff0c\u5192\u865f\u6216\u4e0b\u5283\u7dda\u3002",
"You have unsaved changes are you sure you want to navigate away?": "\u7de8\u8f2f\u5c1a\u672a\u88ab\u5132\u5b58\uff0c\u4f60\u78ba\u5b9a\u8981\u96e2\u958b\uff1f",
"Restore last draft": "\u8f09\u5165\u4e0a\u4e00\u6b21\u7de8\u8f2f\u7684\u8349\u7a3f",
"Special character...": "\u7279\u6b8a\u5b57\u5143......",
"Source code": "\u539f\u59cb\u78bc",
"Insert\/Edit code sample": "\u63d2\u5165\/\u7de8\u8f2f \u7a0b\u5f0f\u78bc\u7bc4\u4f8b",
"Language": "\u8a9e\u8a00",
"Code sample...": "\u7a0b\u5f0f\u78bc\u7bc4\u4f8b...",
"Color Picker": "\u9078\u8272\u5668",
"R": "\u7d05",
"G": "\u7da0",
"B": "\u85cd",
"Left to right": "\u5f9e\u5de6\u5230\u53f3",
"Right to left": "\u5f9e\u53f3\u5230\u5de6",
"Emoticons...": "\u8868\u60c5\u7b26\u865f\u2026",
"Metadata and Document Properties": "\u5f8c\u8a2d\u8cc7\u6599\u8207\u6587\u4ef6\u5c6c\u6027",
"Title": "\u6a19\u984c",
"Keywords": "\u95dc\u9375\u5b57",
"Description": "\u63cf\u8ff0",
"Robots": "\u6a5f\u5668\u4eba",
"Author": "\u4f5c\u8005",
"Encoding": "\u7de8\u78bc",
"Fullscreen": "\u5168\u87a2\u5e55",
"Action": "\u52d5\u4f5c",
"Shortcut": "\u5feb\u901f\u9375",
"Help": "\u5e6b\u52a9",
"Address": "\u5730\u5740",
"Focus to menubar": "\u8df3\u81f3\u9078\u55ae\u5217",
"Focus to toolbar": "\u8df3\u81f3\u5de5\u5177\u5217",
"Focus to element path": "\u8df3\u81f3HTML\u5143\u7d20\u5217",
"Focus to contextual toolbar": "\u8df3\u81f3\u5feb\u6377\u9078\u55ae",
"Insert link (if link plugin activated)": "\u65b0\u589e\u6377\u5f91 (\u6377\u5f91\u5916\u639b\u555f\u7528\u6642)",
"Save (if save plugin activated)": "\u5132\u5b58 (\u5132\u5b58\u5916\u639b\u555f\u7528\u6642)",
"Find (if searchreplace plugin activated)": "\u5c0b\u627e (\u5c0b\u627e\u53d6\u4ee3\u5916\u639b\u555f\u7528\u6642)",
"Plugins installed ({0}):": "({0}) \u500b\u5916\u639b\u5df2\u5b89\u88dd\uff1a",
"Premium plugins:": "\u52a0\u503c\u5916\u639b\uff1a",
"Learn more...": "\u4e86\u89e3\u66f4\u591a...",
"You are using {0}": "\u60a8\u6b63\u5728\u4f7f\u7528 {0}",
"Plugins": "\u5916\u639b",
"Handy Shortcuts": "\u5feb\u901f\u9375",
"Horizontal line": "\u6c34\u5e73\u7dda",
"Insert\/edit image": "\u63d2\u5165\/\u7de8\u8f2f \u5716\u7247",
"Image description": "\u5716\u7247\u63cf\u8ff0",
"Source": "\u5716\u7247\u7db2\u5740",
"Dimensions": "\u5c3a\u5bf8",
"Constrain proportions": "\u7b49\u6bd4\u4f8b\u7e2e\u653e",
"General": "\u4e00\u822c",
"Advanced": "\u9032\u968e",
"Style": "\u6a23\u5f0f",
"Vertical space": "\u9ad8\u5ea6",
"Horizontal space": "\u5bec\u5ea6",
"Border": "\u908a\u6846",
"Insert image": "\u63d2\u5165\u5716\u7247",
"Image...": "\u5716\u7247......",
"Image list": "\u5716\u7247\u6e05\u55ae",
"Rotate counterclockwise": "\u9006\u6642\u91dd\u65cb\u8f49",
"Rotate clockwise": "\u9806\u6642\u91dd\u65cb\u8f49",
"Flip vertically": "\u5782\u76f4\u7ffb\u8f49",
"Flip horizontally": "\u6c34\u5e73\u7ffb\u8f49",
"Edit image": "\u7de8\u8f2f\u5716\u7247",
"Image options": "\u5716\u7247\u9078\u9805",
"Zoom in": "\u653e\u5927",
"Zoom out": "\u7e2e\u5c0f",
"Crop": "\u88c1\u526a",
"Resize": "\u8abf\u6574\u5927\u5c0f",
"Orientation": "\u65b9\u5411",
"Brightness": "\u4eae\u5ea6",
"Sharpen": "\u92b3\u5316",
"Contrast": "\u5c0d\u6bd4",
"Color levels": "\u984f\u8272\u5c64\u6b21",
"Gamma": "\u4f3d\u99ac\u503c",
"Invert": "\u53cd\u8f49",
"Apply": "\u61c9\u7528",
"Back": "\u5f8c\u9000",
"Insert date\/time": "\u63d2\u5165 \u65e5\u671f\/\u6642\u9593",
"Date\/time": "\u65e5\u671f\/\u6642\u9593",
"Insert\/Edit Link": "\u63d2\u5165\/\u7de8\u8f2f\u9023\u7d50",
"Insert\/edit link": "\u63d2\u5165\/\u7de8\u8f2f\u9023\u7d50",
"Text to display": "\u986f\u793a\u6587\u5b57",
"Url": "\u7db2\u5740",
"Open link in...": "\u958b\u555f\u9023\u7d50\u65bc...",
"Current window": "\u76ee\u524d\u8996\u7a97",
"None": "\u7121",
"New window": "\u53e6\u958b\u8996\u7a97",
"Remove link": "\u79fb\u9664\u9023\u7d50",
"Anchors": "\u52a0\u5165\u9328\u9ede",
"Link...": "\u9023\u7d50...",
"Paste or type a link": "\u8cbc\u4e0a\u6216\u8f38\u5165\u9023\u7d50",
"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "\u4f60\u6240\u586b\u5beb\u7684URL\u70ba\u96fb\u5b50\u90f5\u4ef6\uff0c\u9700\u8981\u52a0\u4e0amailto:\u524d\u7db4\u55ce\uff1f",
"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "\u4f60\u6240\u586b\u5beb\u7684URL\u5c6c\u65bc\u5916\u90e8\u93c8\u63a5\uff0c\u9700\u8981\u52a0\u4e0ahttp:\/\/:\u524d\u7db4\u55ce\uff1f",
"Link list": "\u9023\u7d50\u6e05\u55ae",
"Insert video": "\u63d2\u5165\u5f71\u97f3",
"Insert\/edit video": "\u63d2\u4ef6\/\u7de8\u8f2f \u5f71\u97f3",
"Insert\/edit media": "\u63d2\u5165\/\u7de8\u8f2f \u5a92\u9ad4",
"Alternative source": "\u66ff\u4ee3\u5f71\u97f3",
"Alternative source URL": "\u66ff\u4ee3\u4f86\u6e90URL",
"Media poster (Image URL)": "\u5a92\u9ad4\u6d77\u5831\uff08\u5f71\u50cfImage URL\uff09",
"Paste your embed code below:": "\u8acb\u5c07\u60a8\u7684\u5d4c\u5165\u5f0f\u7a0b\u5f0f\u78bc\u8cbc\u5728\u4e0b\u9762:",
"Embed": "\u5d4c\u5165\u78bc",
"Media...": "\u5a92\u9ad4...",
"Nonbreaking space": "\u4e0d\u5206\u884c\u7684\u7a7a\u683c",
"Page break": "\u5206\u9801",
"Paste as text": "\u4ee5\u7d14\u6587\u5b57\u8cbc\u4e0a",
"Preview": "\u9810\u89bd",
"Print...": "\u5217\u5370...",
"Save": "\u5132\u5b58",
"Find": "\u641c\u5c0b",
"Replace with": "\u66f4\u63db",
"Replace": "\u66ff\u63db",
"Replace all": "\u66ff\u63db\u5168\u90e8",
"Previous": "\u4e0a\u4e00\u500b",
"Next": "\u4e0b\u4e00\u500b",
"Find and replace...": "\u5c0b\u627e\u53ca\u53d6\u4ee3...",
"Could not find the specified string.": "\u7121\u6cd5\u67e5\u8a62\u5230\u6b64\u7279\u5b9a\u5b57\u4e32",
"Match case": "\u76f8\u5339\u914d\u6848\u4ef6",
"Find whole words only": "\u50c5\u627e\u51fa\u5b8c\u6574\u5b57\u532f",
"Spell check": "\u62fc\u5beb\u6aa2\u67e5",
"Ignore": "\u5ffd\u7565",
"Ignore all": "\u5ffd\u7565\u6240\u6709",
"Finish": "\u5b8c\u6210",
"Add to Dictionary": "\u52a0\u5165\u5b57\u5178\u4e2d",
"Insert table": "\u63d2\u5165\u8868\u683c",
"Table properties": "\u8868\u683c\u5c6c\u6027",
"Delete table": "\u522a\u9664\u8868\u683c",
"Cell": "\u5132\u5b58\u683c",
"Row": "\u5217",
"Column": "\u884c",
"Cell properties": "\u5132\u5b58\u683c\u5c6c\u6027",
"Merge cells": "\u5408\u4f75\u5132\u5b58\u683c",
"Split cell": "\u5206\u5272\u5132\u5b58\u683c",
"Insert row before": "\u63d2\u5165\u5217\u5728...\u4e4b\u524d",
"Insert row after": "\u63d2\u5165\u5217\u5728...\u4e4b\u5f8c",
"Delete row": "\u522a\u9664\u5217",
"Row properties": "\u5217\u5c6c\u6027",
"Cut row": "\u526a\u4e0b\u5217",
"Copy row": "\u8907\u88fd\u5217",
"Paste row before": "\u8cbc\u4e0a\u5217\u5728...\u4e4b\u524d",
"Paste row after": "\u8cbc\u4e0a\u5217\u5728...\u4e4b\u5f8c",
"Insert column before": "\u63d2\u5165\u6b04\u4f4d\u5728...\u4e4b\u524d",
"Insert column after": "\u63d2\u5165\u6b04\u4f4d\u5728...\u4e4b\u5f8c",
"Delete column": "\u522a\u9664\u884c",
"Cols": "\u6b04\u4f4d\u6bb5",
"Rows": "\u5217",
"Width": "\u5bec\u5ea6",
"Height": "\u9ad8\u5ea6",
"Cell spacing": "\u5132\u5b58\u683c\u5f97\u9593\u8ddd",
"Cell padding": "\u5132\u5b58\u683c\u7684\u908a\u8ddd",
"Show caption": "\u986f\u793a\u6a19\u984c",
"Left": "\u5de6\u908a",
"Center": "\u4e2d\u9593",
"Right": "\u53f3\u908a",
"Cell type": "\u5132\u5b58\u683c\u7684\u985e\u578b",
"Scope": "\u7bc4\u570d",
"Alignment": "\u5c0d\u9f4a",
"H Align": "\u6c34\u5e73\u4f4d\u7f6e",
"V Align": "\u5782\u76f4\u4f4d\u7f6e",
"Top": "\u7f6e\u9802",
"Middle": "\u7f6e\u4e2d",
"Bottom": "\u7f6e\u5e95",
"Header cell": "\u6a19\u982d\u5132\u5b58\u683c",
"Row group": "\u5217\u7fa4\u7d44",
"Column group": "\u6b04\u4f4d\u7fa4\u7d44",
"Row type": "\u884c\u7684\u985e\u578b",
"Header": "\u6a19\u982d",
"Body": "\u4e3b\u9ad4",
"Footer": "\u9801\u5c3e",
"Border color": "\u908a\u6846\u984f\u8272",
"Insert template...": "\u63d2\u5165\u6a23\u7248...",
"Templates": "\u6a23\u7248",
"Template": "\u6a23\u677f",
"Text color": "\u6587\u5b57\u984f\u8272",
"Background color": "\u80cc\u666f\u984f\u8272",
"Custom...": "\u81ea\u8a02",
"Custom color": "\u81ea\u8a02\u984f\u8272",
"No color": "No color",
"Remove color": "\u79fb\u9664\u984f\u8272",
"Table of Contents": "\u76ee\u9304",
"Show blocks": "\u986f\u793a\u5340\u584a\u8cc7\u8a0a",
"Show invisible characters": "\u986f\u793a\u96b1\u85cf\u5b57\u5143",
"Word count": "\u8a08\u7b97\u5b57\u6578",
"Count": "\u8a08\u7b97",
"Document": "\u6587\u4ef6",
"Selection": "\u9078\u9805",
"Words": "\u5b57\u6578",
"Words: {0}": "\u5b57\u6578\uff1a{0}",
"{0} words": "{0} \u5b57\u5143",
"File": "\u6a94\u6848",
"Edit": "\u7de8\u8f2f",
"Insert": "\u63d2\u5165",
"View": "\u6aa2\u8996",
"Format": "\u683c\u5f0f",
"Table": "\u8868\u683c",
"Tools": "\u5de5\u5177",
"Powered by {0}": "\u7531 {0} \u63d0\u4f9b",
"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u8c50\u5bcc\u7684\u6587\u672c\u5340\u57df\u3002\u6309ALT-F9\u524d\u5f80\u4e3b\u9078\u55ae\u3002\u6309ALT-F10\u547c\u53eb\u5de5\u5177\u6b04\u3002\u6309ALT-0\u5c0b\u6c42\u5e6b\u52a9",
"Image title": "\u5716\u7247\u6a19\u984c",
"Border width": "\u6846\u7dda\u5bec\u5ea6",
"Border style": "\u6846\u7dda\u6a23\u5f0f",
"Error": "\u932f\u8aa4",
"Warn": "\u8b66\u544a",
"Valid": "\u6709\u6548",
"To open the popup, press Shift+Enter": "\u8981\u958b\u555f\u5f48\u51fa\u8996\u7a97\uff0c\u8acb\u6309Shift+Enter",
"Rich Text Area. Press ALT-0 for help.": "\u5bcc\u6587\u672c\u5340\u57df\u3002\u8acb\u6309ALT-0\u5c0b\u6c42\u5354\u52a9\u3002",
"System Font": "\u7cfb\u7d71\u5b57\u578b",
"Failed to upload image: {0}": "\u7121\u6cd5\u4e0a\u50b3\u5f71\u50cf\uff1a{0}",
"Failed to load plugin: {0} from url {1}": "\u7121\u6cd5\u4e0a\u50b3\u63d2\u4ef6\uff1a{0}\u81eaurl{1}",
"Failed to load plugin url: {0}": "\u7121\u6cd5\u4e0a\u50b3\u63d2\u4ef6\uff1a{0}",
"Failed to initialize plugin: {0}": "\u7121\u6cd5\u555f\u52d5\u63d2\u4ef6\uff1a{0}",
"example": "\u7bc4\u4f8b",
"Search": "\u641c\u7d22",
"All": "\u5168\u90e8",
"Currency": "\u8ca8\u5e63",
"Text": "\u6587\u672c",
"Quotations": "\u5f15\u7528",
"Mathematical": "\u6578\u5b78",
"Extended Latin": "\u62c9\u4e01\u5b57\u6bcd\u64f4\u5145",
"Symbols": "\u7b26\u865f",
"Arrows": "\u7bad\u982d",
"User Defined": "\u4f7f\u7528\u8005\u5df2\u5b9a\u7fa9",
"dollar sign": "\u7f8e\u5143\u7b26\u865f",
"currency sign": "\u8ca8\u5e63\u7b26\u865f",
"euro-currency sign": "\u6b50\u5143\u7b26\u865f",
"colon sign": "\u79d1\u6717\u7b26\u865f",
"cruzeiro sign": "\u514b\u9b6f\u8cfd\u7f85\u7b26\u865f",
"french franc sign": "\u6cd5\u6717\u7b26\u865f",
"lira sign": "\u91cc\u62c9\u7b26\u865f",
"mill sign": "\u6587\u7b26\u865f",
"naira sign": "\u5948\u62c9\u7b26\u865f",
"peseta sign": "\u6bd4\u585e\u5854\u7b26\u865f",
"rupee sign": "\u76e7\u6bd4\u7b26\u865f",
"won sign": "\u97d3\u571c\u7b26\u865f",
"new sheqel sign": "\u65b0\u8b1d\u514b\u723e\u7b26\u865f",
"dong sign": "\u8d8a\u5357\u76fe\u7b26\u865f",
"kip sign": "\u8001\u64be\u5e63\u7b26\u865f",
"tugrik sign": "\u8499\u53e4\u5e63\u7b26\u865f",
"drachma sign": "\u5fb7\u514b\u62c9\u99ac\u7b26\u865f",
"german penny symbol": "\u5fb7\u570b\u5206\u7b26\u865f",
"peso sign": "\u62ab\u7d22\u7b26\u865f",
"guarani sign": "\u5df4\u62c9\u572d\u5e63\u7b26\u865f",
"austral sign": "\u963f\u6839\u5ef7\u5e63\u7b26\u865f",
"hryvnia sign": "\u70cf\u514b\u862d\u5e63\u7b26\u865f",
"cedi sign": "\u8fe6\u7d0d\u5e63\u7b26\u865f",
"livre tournois sign": "\u91cc\u5f17\u723e\u7b26\u865f",
"spesmilo sign": "\u570b\u969b\u5e63\u7b26\u865f",
"tenge sign": "\u54c8\u85a9\u514b\u5e63\u7b26\u865f",
"indian rupee sign": "\u5370\u5ea6\u76e7\u6bd4\u7b26\u865f",
"turkish lira sign": "\u571f\u8033\u5176\u91cc\u62c9\u7b26\u865f",
"nordic mark sign": "\u5317\u6b50\u99ac\u514b\u7b26\u865f",
"manat sign": "\u4e9e\u585e\u62dc\u7136\u5e63\u7b26\u865f",
"ruble sign": "\u76e7\u5e03\u7b26\u865f",
"yen character": "\u65e5\u5713\u7b26\u865f",
"yuan character": "\u4eba\u6c11\u5e63\u7b26\u865f",
"yuan character, in hong kong and taiwan": "\u6e2f\u5143\u8207\u53f0\u5e63\u7b26\u865f",
"yen\/yuan character variant one": "\u65e5\u5713\/\u4eba\u6c11\u5e63\u7b26\u865f\u8b8a\u5316\u578b",
"Loading emoticons...": "\u8f09\u5165\u8868\u60c5\u7b26\u865f\u2026",
"Could not load emoticons": "\u7121\u6cd5\u8f09\u5165\u8868\u60c5\u7b26\u865f",
"People": "\u4eba",
"Animals and Nature": "\u52d5\u7269\u8207\u81ea\u7136",
"Food and Drink": "\u98f2\u98df",
"Activity": "\u6d3b\u52d5",
"Travel and Places": "\u65c5\u884c\u8207\u5730\u9ede",
"Objects": "\u7269\u4ef6",
"Flags": "\u65d7\u6a19",
"Characters": "\u5b57\u5143",
"Characters (no spaces)": "\u5b57\u5143\uff08\u7121\u7a7a\u683c\uff09",
"{0} characters": "{0}\u5b57\u5143",
"Error: Form submit field collision.": "\u932f\u8aa4\uff1a\u8868\u683c\u905e\u4ea4\u6b04\u4f4d\u885d\u7a81\u3002",
"Error: No form element found.": "\u932f\u8aa4\uff1a\u627e\u4e0d\u5230\u8868\u683c\u5143\u7d20\u3002",
"Update": "\u66f4\u65b0",
"Color swatch": "\u8272\u5f69\u6a23\u672c",
"Turquoise": "\u571f\u8033\u5176\u85cd",
"Green": "\u7da0\u8272",
"Blue": "\u85cd\u8272",
"Purple": "\u7d2b\u8272",
"Navy Blue": "\u6df1\u85cd\u8272",
"Dark Turquoise": "\u6df1\u571f\u8033\u5176\u85cd",
"Dark Green": "\u6df1\u7da0\u8272",
"Medium Blue": "\u4e2d\u85cd\u8272",
"Medium Purple": "\u4e2d\u7d2b\u8272",
"Midnight Blue": "\u9ed1\u85cd\u8272",
"Yellow": "\u9ec3\u8272",
"Orange": "\u6a59\u8272",
"Red": "\u7d05\u8272",
"Light Gray": "\u6dfa\u7070\u8272",
"Gray": "\u7070\u8272",
"Dark Yellow": "\u6df1\u9ec3\u8272",
"Dark Orange": "\u6df1\u6a59\u8272",
"Dark Red": "\u6697\u7d05\u8272",
"Medium Gray": "\u4e2d\u7070\u8272",
"Dark Gray": "\u6df1\u7070\u8272",
"Light Green": "\u6de1\u7da0\u8272",
"Light Yellow": "\u6dfa\u9ec3\u8272",
"Light Red": "\u6dfa\u7d05\u8272",
"Light Purple": "\u6dfa\u7d2b\u8272",
"Light Blue": "\u6dfa\u85cd\u8272",
"Dark Purple": "\u6df1\u7d2b\u8272",
"Dark Blue": "\u6df1\u85cd\u8272",
"Black": "\u9ed1\u8272",
"White": "\u767d\u8272",
"Switch to or from fullscreen mode": "\u8f49\u63db\u81ea\/\u81f3\u5168\u87a2\u5e55\u6a21\u5f0f",
"Open help dialog": "\u958b\u555f\u5354\u52a9\u5c0d\u8a71",
"history": "\u6b77\u53f2",
"styles": "\u6a23\u5f0f",
"formatting": "\u683c\u5f0f",
"alignment": "\u5c0d\u9f4a",
"indentation": "\u7e2e\u6392",
"permanent pen": "\u6c38\u4e45\u6027\u7b46",
"comments": "\u8a3b\u89e3",
"Format Painter": "\u8907\u88fd\u683c\u5f0f",
"Insert\/edit iframe": "\u63d2\u5165\/\u7de8\u8f2fiframe",
"Capitalization": "\u5927\u5beb",
"lowercase": "\u5c0f\u5beb",
"UPPERCASE": "\u5927\u5beb",
"Title Case": "\u5b57\u9996\u5927\u5beb",
"Permanent Pen Properties": "\u6c38\u4e45\u6a19\u8a18\u5c6c\u6027",
"Permanent pen properties...": "\u6c38\u4e45\u6a19\u8a18\u5c6c\u6027......",
"Font": "\u5b57\u578b",
"Size": "\u5b57\u5f62\u5927\u5c0f",
"More...": "\u66f4\u591a\u8cc7\u8a0a......",
"Spellcheck Language": "\u62fc\u5beb\u8a9e\u8a00",
"Select...": "\u9078\u64c7......",
"Preferences": "\u9996\u9078\u9805",
"Yes": "\u662f",
"No": "\u5426",
"Keyboard Navigation": "\u9375\u76e4\u5c0e\u822a",
"Version": "\u7248\u672c",
"Anchor": "\u52a0\u5165\u9328\u9ede",
"Special character": "\u7279\u6b8a\u5b57\u5143",
"Code sample": "\u7a0b\u5f0f\u78bc\u7bc4\u4f8b",
"Color": "\u984f\u8272",
"Emoticons": "\u8868\u60c5",
"Document properties": "\u6587\u4ef6\u7684\u5c6c\u6027",
"Image": "\u5716\u7247",
"Insert link": "\u63d2\u5165\u9023\u7d50",
"Target": "\u958b\u555f\u65b9\u5f0f",
"Link": "\u9023\u7d50",
"Poster": "\u9810\u89bd\u5716\u7247",
"Media": "\u5a92\u9ad4",
"Print": "\u5217\u5370",
"Prev": "\u4e0a\u4e00\u500b",
"Find and replace": "\u5c0b\u627e\u53ca\u53d6\u4ee3",
"Whole words": "\u6574\u500b\u55ae\u5b57",
"Spellcheck": "\u62fc\u5b57\u6aa2\u67e5",
"Caption": "\u8868\u683c\u6a19\u984c",
"Insert template": "\u63d2\u5165\u6a23\u7248"
});

@ -0,0 +1,7 @@
/**
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
* Licensed under the LGPL or a commercial license.
* For LGPL see License.txt in the project root for license information.
* For commercial licenses see https://www.tiny.cloud/
*/
body{background-color:#2f3742;color:#dfe0e4;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif;line-height:1.4;margin:1rem}a{color:#4099ff}table{border-collapse:collapse}table:not([cellpadding]) td,table:not([cellpadding]) th{padding:.4rem}table[border]:not([border="0"]):not([style*=border-width]) td,table[border]:not([border="0"]):not([style*=border-width]) th{border-width:1px}table[border]:not([border="0"]):not([style*=border-style]) td,table[border]:not([border="0"]):not([style*=border-style]) th{border-style:solid}table[border]:not([border="0"]):not([style*=border-color]) td,table[border]:not([border="0"]):not([style*=border-color]) th{border-color:#6d737b}figure{display:table;margin:1rem auto}figure figcaption{color:#8a8f97;display:block;margin-top:.25rem;text-align:center}hr{border-color:#6d737b;border-style:solid;border-width:1px 0 0 0}code{background-color:#6d737b;border-radius:3px;padding:.1rem .2rem}.mce-content-body:not([dir=rtl]) blockquote{border-left:2px solid #6d737b;margin-left:1.5rem;padding-left:1rem}.mce-content-body[dir=rtl] blockquote{border-right:2px solid #6d737b;margin-right:1.5rem;padding-right:1rem}

@ -0,0 +1,7 @@
/**
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
* Licensed under the LGPL or a commercial license.
* For LGPL see License.txt in the project root for license information.
* For commercial licenses see https://www.tiny.cloud/
*/
body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif;line-height:1.4;margin:1rem}table{border-collapse:collapse}table:not([cellpadding]) td,table:not([cellpadding]) th{padding:.4rem}table[border]:not([border="0"]):not([style*=border-width]) td,table[border]:not([border="0"]):not([style*=border-width]) th{border-width:1px}table[border]:not([border="0"]):not([style*=border-style]) td,table[border]:not([border="0"]):not([style*=border-style]) th{border-style:solid}table[border]:not([border="0"]):not([style*=border-color]) td,table[border]:not([border="0"]):not([style*=border-color]) th{border-color:#ccc}figure{display:table;margin:1rem auto}figure figcaption{color:#999;display:block;margin-top:.25rem;text-align:center}hr{border-color:#ccc;border-style:solid;border-width:1px 0 0 0}code{background-color:#e8e8e8;border-radius:3px;padding:.1rem .2rem}.mce-content-body:not([dir=rtl]) blockquote{border-left:2px solid #ccc;margin-left:1.5rem;padding-left:1rem}.mce-content-body[dir=rtl] blockquote{border-right:2px solid #ccc;margin-right:1.5rem;padding-right:1rem}

@ -0,0 +1,7 @@
/**
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
* Licensed under the LGPL or a commercial license.
* For LGPL see License.txt in the project root for license information.
* For commercial licenses see https://www.tiny.cloud/
*/
@media screen{html{background:#f4f4f4;min-height:100%}}body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif}@media screen{body{background-color:#fff;box-shadow:0 0 4px rgba(0,0,0,.15);box-sizing:border-box;margin:1rem auto 0;max-width:820px;min-height:calc(100vh - 1rem);padding:4rem 6rem 6rem 6rem}}table{border-collapse:collapse}table:not([cellpadding]) td,table:not([cellpadding]) th{padding:.4rem}table[border]:not([border="0"]):not([style*=border-width]) td,table[border]:not([border="0"]):not([style*=border-width]) th{border-width:1px}table[border]:not([border="0"]):not([style*=border-style]) td,table[border]:not([border="0"]):not([style*=border-style]) th{border-style:solid}table[border]:not([border="0"]):not([style*=border-color]) td,table[border]:not([border="0"]):not([style*=border-color]) th{border-color:#ccc}figure figcaption{color:#999;margin-top:.25rem;text-align:center}hr{border-color:#ccc;border-style:solid;border-width:1px 0 0 0}.mce-content-body:not([dir=rtl]) blockquote{border-left:2px solid #ccc;margin-left:1.5rem;padding-left:1rem}.mce-content-body[dir=rtl] blockquote{border-right:2px solid #ccc;margin-right:1.5rem;padding-right:1rem}

@ -0,0 +1,7 @@
/**
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
* Licensed under the LGPL or a commercial license.
* For LGPL see License.txt in the project root for license information.
* For commercial licenses see https://www.tiny.cloud/
*/
body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif;line-height:1.4;margin:1rem auto;max-width:900px}table{border-collapse:collapse}table:not([cellpadding]) td,table:not([cellpadding]) th{padding:.4rem}table[border]:not([border="0"]):not([style*=border-width]) td,table[border]:not([border="0"]):not([style*=border-width]) th{border-width:1px}table[border]:not([border="0"]):not([style*=border-style]) td,table[border]:not([border="0"]):not([style*=border-style]) th{border-style:solid}table[border]:not([border="0"]):not([style*=border-color]) td,table[border]:not([border="0"]):not([style*=border-color]) th{border-color:#ccc}figure{display:table;margin:1rem auto}figure figcaption{color:#999;display:block;margin-top:.25rem;text-align:center}hr{border-color:#ccc;border-style:solid;border-width:1px 0 0 0}code{background-color:#e8e8e8;border-radius:3px;padding:.1rem .2rem}.mce-content-body:not([dir=rtl]) blockquote{border-left:2px solid #ccc;margin-left:1.5rem;padding-left:1rem}.mce-content-body[dir=rtl] blockquote{border-right:2px solid #ccc;margin-right:1.5rem;padding-right:1rem}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1,7 @@
/**
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
* Licensed under the LGPL or a commercial license.
* For LGPL see License.txt in the project root for license information.
* For commercial licenses see https://www.tiny.cloud/
*/
.tinymce-mobile-unfocused-selections .tinymce-mobile-unfocused-selection{background-color:green;display:inline-block;opacity:.5;position:absolute}body{-webkit-text-size-adjust:none}body img{max-width:96vw}body table img{max-width:95%}body{font-family:sans-serif}table{border-collapse:collapse}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1,7 @@
/**
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
* Licensed under the LGPL or a commercial license.
* For LGPL see License.txt in the project root for license information.
* For commercial licenses see https://www.tiny.cloud/
*/
body.tox-dialog__disable-scroll{overflow:hidden}.tox-fullscreen{border:0;height:100%;left:0;margin:0;overflow:hidden;-ms-scroll-chaining:none;overscroll-behavior:none;padding:0;position:fixed;top:0;touch-action:pinch-zoom;width:100%}.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle{display:none}.tox.tox-tinymce.tox-fullscreen{background-color:transparent;z-index:1200}.tox-shadowhost.tox-fullscreen{z-index:1200}.tox-fullscreen .tox.tox-tinymce-aux,.tox-fullscreen~.tox.tox-tinymce-aux{z-index:1201}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1,7 @@
/**
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
* Licensed under the LGPL or a commercial license.
* For LGPL see License.txt in the project root for license information.
* For commercial licenses see https://www.tiny.cloud/
*/
.tinymce-mobile-unfocused-selections .tinymce-mobile-unfocused-selection{background-color:green;display:inline-block;opacity:.5;position:absolute}body{-webkit-text-size-adjust:none}body img{max-width:96vw}body table img{max-width:95%}body{font-family:sans-serif}table{border-collapse:collapse}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1,7 @@
/**
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
* Licensed under the LGPL or a commercial license.
* For LGPL see License.txt in the project root for license information.
* For commercial licenses see https://www.tiny.cloud/
*/
body.tox-dialog__disable-scroll{overflow:hidden}.tox-fullscreen{border:0;height:100%;left:0;margin:0;overflow:hidden;-ms-scroll-chaining:none;overscroll-behavior:none;padding:0;position:fixed;top:0;touch-action:pinch-zoom;width:100%}.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle{display:none}.tox.tox-tinymce.tox-fullscreen{background-color:transparent;z-index:1200}.tox-shadowhost.tox-fullscreen{z-index:1200}.tox-fullscreen .tox.tox-tinymce-aux,.tox-fullscreen~.tox.tox-tinymce-aux{z-index:1201}

@ -11,12 +11,10 @@ export const personalRiskControlConfigurationField = async (): Promise<any> =>
(await axios.post('/product/riskControlConfigurationField/personalRiskControlConfigurationField')).data; (await axios.post('/product/riskControlConfigurationField/personalRiskControlConfigurationField')).data;
export const productElement = async (id: number): Promise<any> => (await axios.post(`/product/product/bank/products/productElement?id=${id}`)).data; export const productElement = async (id: number): Promise<any> => (await axios.post(`/product/product/bank/products/productElement?id=${id}`)).data;
export const findById = async (id: number): Promise<any> => (await axios.post(`/product/product/bank/products/findById?id=${id}`)).data; export const findById = async (id: number): Promise<any> => (await axios.post(`/product/product/bank/products/findById?id=${id}`)).data;
export const elementDetail = async (id: number): Promise<any> => (await axios.post(`/product/product/bank/products/elementDetail?id=${id}`)).data;
export const save = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/product/bank/products/save`, data)).data; export const save = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/product/bank/products/save`, data)).data;
export const riskSave = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/managerOfRiskControl/bankRiskControlAllocation/save`, data)).data; export const riskSave = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/managerOfRiskControl/bankRiskControlAllocation/save`, data)).data;
export const riskUpdate = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/managerOfRiskControl/bankRiskControlAllocation/update`, data)).data; export const riskUpdate = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/managerOfRiskControl/bankRiskControlAllocation/update`, data)).data;
export const riskControlDetailsAreDisplayed = async (id: number): Promise<any> => export const riskById = async (id: number): Promise<any> => (await axios.post(`/product/managerOfRiskControl/bankRiskControlAllocation/findById?id=${id}`)).data;
(await axios.post(`/product/managerOfRiskControl/bankRiskControlAllocation/riskControlDetailsAreDisplayed?id=${id}`)).data;
export const examineAndApprove = async (id: number | string, opinionDescription: string, status: number, approvalTime: string): Promise<any> => export const examineAndApprove = async (id: number | string, opinionDescription: string, status: number, approvalTime: string): Promise<any> =>
(await axios.post(`/product/product/bank/products/examineAndApprove?id=${id}&opinionDescription=${opinionDescription}&status=${status}&approvalTime=${approvalTime}`)).data; (await axios.post(`/product/product/bank/products/examineAndApprove?id=${id}&opinionDescription=${opinionDescription}&status=${status}&approvalTime=${approvalTime}`)).data;
export const update = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/product/bank/products/update`, data)).data; export const update = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/product/bank/products/update`, data)).data;
@ -24,4 +22,3 @@ export const cancelCollection = async (id: number): Promise<any> => (await axios
export const collect = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/checkPointCollect/collect`, data)).data; export const collect = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/checkPointCollect/collect`, data)).data;
export const submitOpe = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/product/bank/operation/submit`, data)).data; export const submitOpe = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/product/bank/operation/submit`, data)).data;
export const statistics = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/product/bank/products/statistics`, data)).data; export const statistics = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/product/bank/products/statistics`, data)).data;
export const approvalRecord = async (params: Record<string, any>): Promise<any> => (await axios.post(`/product/product/bank/products/approvalRecord`, {}, { params })).data;

@ -20,6 +20,6 @@ export const checkPointListByStu = async (projectId: number, displayCollection?:
export const getTheCurrentUserName = async (): Promise<any> => (await axios.post('/product/product/bank/products/getTheCurrentUserName')).data; export const getTheCurrentUserName = async (): Promise<any> => (await axios.post('/product/product/bank/products/getTheCurrentUserName')).data;
export const getOperationTime = async (checkpointId: number, projectId: number): Promise<any> => export const getOperationTime = async (checkpointId: number, projectId: number): Promise<any> =>
(await axios.post(`/product/product/bank/products/getOperationTime?checkpointId=${checkpointId}&projectId=${projectId}`)).data; (await axios.post(`/product/product/bank/products/getOperationTime?checkPointId=${checkpointId}&projectId=${projectId}`)).data;
export const saveOperationTime = async (checkpointId: number, projectId: number, operationTime: string): Promise<any> => export const saveOperationTime = async (checkpointId: number, projectId: number, operationTime: string): Promise<any> =>
(await axios.post(`/product/product/bank/products/saveOperationTime?checkpointId=${checkpointId}&projectId=${projectId}&operationTime=${operationTime}`)).data; (await axios.post(`/product/product/bank/products/saveOperationTime?checkPointId=${checkpointId}&projectId=${projectId}&operationTime=${operationTime}`)).data;

@ -14,10 +14,3 @@ export const deleteCache = async (data: Record<string, any>): Promise<any> =>
) )
).data; ).data;
export const getOperation = async (params?: Record<string, any>): Promise<any> => (await axios.get('/product/product/bank/operation/getOperation', { params })).data; export const getOperation = async (params?: Record<string, any>): Promise<any> => (await axios.get('/product/product/bank/operation/getOperation', { params })).data;
export const getSandTableLastCache = async (data: Record<string, any>): Promise<any> =>
(
await axios.post(
`/product/product/bank/operation/getSandTableLastCache?cid=${data.cid}&projectId=${data.projectId}&assessmentId=${data.assessmentId}&competitionId=${data.competitionId}`,
)
).data;
export const deleteOperationData = async (data: Record<string, any>): Promise<any> => (await axios.post('/product/product/bank/operation/deleteOperationData', data)).data;

@ -1,213 +1,84 @@
import axios from '@/utils/request'; import axios from '@/utils/request';
import { getIds } from '@/utils/common';
export const accessStrategyGovernmentBlacklistFind = async (checkpointId: number, projectId: number): Promise<any> =>
const host = `http://192.168.31.51:9000`; (await axios.post(`/product/accessStrategyGovernmentBlacklist/details?checkpointId=${checkpointId}&projectId=${projectId}`)).data;
export const accessStrategyGovernmentBlacklistList = async (data: Record<string, any>): Promise<any> =>
(
await axios.post(`/product/accessStrategyGovernmentBlacklist/list`, {
...getIds(),
...data,
})
).data;
export const accessStrategyGovernmentBlacklistFind = async (params: Record<string, any>): Promise<any> =>
(await axios.post(`/product/accessStrategyGovernmentBlacklist/details`, {}, { params })).data;
export const accessStrategyGovernmentBlacklistSave = async (data: Record<string, any>): Promise<any> => export const accessStrategyGovernmentBlacklistSave = async (data: Record<string, any>): Promise<any> =>
(await axios.post(`/product/accessStrategyGovernmentBlacklist/saveOrUpdate`, data)).data; (await axios.post(`/product/accessStrategyGovernmentBlacklist/saveOrUpdate`, data)).data;
export const accessStrategyGovernmentBlacklistDel = async (data: Record<string, any>): Promise<any> =>
(await axios.post(`/product/accessStrategyGovernmentBlacklist/delete`, data)).data; export const accessStrategyEnterpriseBlacklistFind = async (checkpointId: number, projectId: number): Promise<any> =>
(await axios.post(`/product/accessStrategyEnterpriseBlacklist/details?checkpointId=${checkpointId}&projectId=${projectId}`)).data;
export const accessStrategyEnterpriseBlacklist = async (data: Record<string, any>): Promise<any> =>
(
await axios.post(`/product/accessStrategyEnterpriseBlacklist/list`, {
...getIds(),
...data,
})
).data;
export const accessStrategyEnterpriseBlacklistFind = async (params: Record<string, any>): Promise<any> =>
(await axios.post(`/product/accessStrategyEnterpriseBlacklist/details`, {}, { params })).data;
export const accessStrategyEnterpriseBlacklistSave = async (data: Record<string, any>): Promise<any> => export const accessStrategyEnterpriseBlacklistSave = async (data: Record<string, any>): Promise<any> =>
(await axios.post(`/product/accessStrategyEnterpriseBlacklist/saveOrUpdate`, data)).data; (await axios.post(`/product/accessStrategyEnterpriseBlacklist/saveOrUpdate`, data)).data;
export const accessStrategyEnterpriseBlacklistDel = async (data: Record<string, any>): Promise<any> =>
(await axios.post(`/product/accessStrategyEnterpriseBlacklist/delete`, data)).data; export const accessStrategyAntiFraudStrategyFind = async (checkpointId: number, projectId: number): Promise<any> =>
(await axios.post(`/product/accessStrategyAntiFraudStrategy/details?checkpointId=${checkpointId}&projectId=${projectId}`)).data;
export const accessStrategyAntiFraudStrategy = async (data: Record<string, any>): Promise<any> =>
(
await axios.post(`/product/accessStrategyAntiFraudStrategy/list`, {
...getIds(),
...data,
})
).data;
export const accessStrategyAntiFraudStrategyFind = async (params: Record<string, any>): Promise<any> =>
(await axios.post(`/product/accessStrategyAntiFraudStrategy/details`, {}, { params })).data;
export const accessStrategyAntiFraudStrategySave = async (data: Record<string, any>): Promise<any> => export const accessStrategyAntiFraudStrategySave = async (data: Record<string, any>): Promise<any> =>
(await axios.post(`/product/accessStrategyAntiFraudStrategy/saveOrUpdate`, data)).data; (await axios.post(`/product/accessStrategyAntiFraudStrategy/saveOrUpdate`, data)).data;
export const accessStrategyAntiFraudStrategyDel = async (data: Record<string, any>): Promise<any> =>
(await axios.post(`/product/accessStrategyAntiFraudStrategy/delete`, data)).data; export const accessStrategyBusinessBlacklistFind = async (checkpointId: number, projectId: number): Promise<any> =>
(await axios.post(`/product/accessStrategyBusinessBlacklist/details?checkpointId=${checkpointId}&projectId=${projectId}`)).data;
export const accessStrategyBusinessBlacklist = async (data: Record<string, any>): Promise<any> =>
(
await axios.post(`/product/accessStrategyBusinessBlacklist/list`, {
...getIds(),
...data,
})
).data;
export const accessStrategyBusinessBlacklistFind = async (params: Record<string, any>): Promise<any> =>
(await axios.post(`/product/accessStrategyBusinessBlacklist/details`, {}, { params })).data;
export const accessStrategyBusinessBlacklistSave = async (data: Record<string, any>): Promise<any> => export const accessStrategyBusinessBlacklistSave = async (data: Record<string, any>): Promise<any> =>
(await axios.post(`/product/accessStrategyBusinessBlacklist/saveOrUpdate`, data)).data; (await axios.post(`/product/accessStrategyBusinessBlacklist/saveOrUpdate`, data)).data;
export const accessStrategyBusinessBlacklistDel = async (data: Record<string, any>): Promise<any> =>
(await axios.post(`/product/accessStrategyBusinessBlacklist/delete`, data)).data; export const accessStrategyCreditBlacklistFind = async (checkpointId: number, projectId: number): Promise<any> =>
(await axios.post(`/product/accessStrategyCreditBlacklist/details?checkpointId=${checkpointId}&projectId=${projectId}`)).data;
export const accessStrategyCreditBlacklist = async (data: Record<string, any>): Promise<any> =>
(
await axios.post(`/product/accessStrategyCreditBlacklist/list`, {
...getIds(),
...data,
})
).data;
export const accessStrategyCreditBlacklistFind = async (params: Record<string, any>): Promise<any> =>
(await axios.post(`/product/accessStrategyCreditBlacklist/details`, {}, { params })).data;
export const accessStrategyCreditBlacklistSave = async (data: Record<string, any>): Promise<any> => export const accessStrategyCreditBlacklistSave = async (data: Record<string, any>): Promise<any> =>
(await axios.post(`/product/accessStrategyCreditBlacklist/saveOrUpdate`, data)).data; (await axios.post(`/product/accessStrategyCreditBlacklist/saveOrUpdate`, data)).data;
export const accessStrategyCreditBlacklistDel = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/accessStrategyCreditBlacklist/delete`, data)).data;
export const accessStrategyInlineBlacklistFind = async (checkpointId: number, projectId: number): Promise<any> =>
export const accessStrategyInlineBlacklist = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/accessStrategyInlineBlacklist/details?checkpointId=${checkpointId}&projectId=${projectId}`)).data;
(
await axios.post(`/product/accessStrategyInlineBlacklist/list`, {
...getIds(),
...data,
})
).data;
export const accessStrategyInlineBlacklistFind = async (params: Record<string, any>): Promise<any> =>
(await axios.post(`/product/accessStrategyInlineBlacklist/details`, {}, { params })).data;
export const accessStrategyInlineBlacklistSave = async (data: Record<string, any>): Promise<any> => export const accessStrategyInlineBlacklistSave = async (data: Record<string, any>): Promise<any> =>
(await axios.post(`/product/accessStrategyInlineBlacklist/saveOrUpdate`, data)).data; (await axios.post(`/product/accessStrategyInlineBlacklist/saveOrUpdate`, data)).data;
export const accessStrategyInlineBlacklistDel = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/accessStrategyInlineBlacklist/delete`, data)).data;
export const accessStrategyNegativeIndustryStrategyFind = async (checkpointId: number, projectId: number): Promise<any> =>
export const accessStrategyNegativeIndustryStrategy = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/accessStrategyNegativeIndustryStrategy/details?checkpointId=${checkpointId}&projectId=${projectId}`)).data;
(
await axios.post(`/product/accessStrategyNegativeIndustryStrategy/list`, {
...getIds(),
...data,
})
).data;
export const accessStrategyNegativeIndustryStrategyFind = async (params: Record<string, any>): Promise<any> =>
(await axios.post(`/product/accessStrategyNegativeIndustryStrategy/details`, {}, { params })).data;
export const accessStrategyNegativeIndustryStrategySave = async (data: Record<string, any>): Promise<any> => export const accessStrategyNegativeIndustryStrategySave = async (data: Record<string, any>): Promise<any> =>
(await axios.post(`/product/accessStrategyNegativeIndustryStrategy/saveOrUpdate`, data)).data; (await axios.post(`/product/accessStrategyNegativeIndustryStrategy/saveOrUpdate`, data)).data;
export const accessStrategyNegativeIndustryStrategyDel = async (data: Record<string, any>): Promise<any> =>
(await axios.post(`/product/accessStrategyNegativeIndustryStrategy/delete`, data)).data;
export const delCredit = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/creditScoringStrategy/delete`, data)).data; export const delCredit = async (id: number): Promise<any> => (await axios.post(`/product/creditScoringStrategy/delete?strategyId=${id}`)).data;
export const findCredit = async (id: number): Promise<any> => (await axios.post(`/product/creditScoringStrategy/details?strategyId=${id}`)).data; export const findCredit = async (id: number): Promise<any> => (await axios.post(`/product/creditScoringStrategy/details?strategyId=${id}`)).data;
export const listCredit = async (data: Record<string, any>): Promise<any> => export const listCredit = async (data: Record<string, any>): Promise<any> =>
( (await axios.post(`/product/creditScoringStrategy/list?checkpointId=${data.checkpointId}&projectId=${data.projectId}&pageNum=${data.pageNum}&pageSize=${data.pageSize}`)).data;
await axios.post(`/product/creditScoringStrategy/list`, {
...data,
...getIds(),
})
).data;
export const saveCredit = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/creditScoringStrategy/saveOrUpdate`, data)).data; export const saveCredit = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/creditScoringStrategy/saveOrUpdate`, data)).data;
export const detailRick = async (type: number): Promise<any> => export const detailRick = async (data: Record<string, any>): Promise<any> =>
( (await axios.post(`/product/riskDegreeStrategy/details?checkpointId=${data.checkpointId}&projectId=${data.projectId}&type=${data.type}`)).data;
await axios.post(`/product/riskDegreeStrategy/details`, {
...getIds(),
type,
})
).data;
export const saveRick = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/riskDegreeStrategy/saveOrUpdate`, data)).data; export const saveRick = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/riskDegreeStrategy/saveOrUpdate`, data)).data;
export const interestRateModelList = async (data: Record<string, any>): Promise<any> => export const businessInterestRateDetails = async (checkpointId: number, projectId: number): Promise<any> =>
( (await axios.post(`/product/interestRateModel/businessInterestRateDetails?checkpointId=${checkpointId}&projectId=${projectId}`)).data;
await axios.post(`/product/interestRateModel/list`, {
...getIds(),
...data,
})
).data;
export const businessInterestRateDetails = async (params: Record<string, any>): Promise<any> =>
(await axios.post(`/product/interestRateModel/businessInterestRateDetails`, {}, { params })).data;
export const businessInterestRateSaveOrUpdate = async (data: Record<string, any>): Promise<any> => export const businessInterestRateSaveOrUpdate = async (data: Record<string, any>): Promise<any> =>
(await axios.post(`/product/interestRateModel/businessInterestRateSaveOrUpdate`, data)).data; (await axios.post(`/product/interestRateModel/businessInterestRateSaveOrUpdate`, data)).data;
export const personalInterestRateDetails = async (params: Record<string, any>): Promise<any> => export const personalInterestRateDetails = async (checkpointId: number, projectId: number): Promise<any> =>
(await axios.post(`/product/interestRateModel/personalInterestRateDetails`, {}, { params })).data; (await axios.post(`/product/interestRateModel/personalInterestRateDetails?checkpointId=${checkpointId}&projectId=${projectId}`)).data;
export const personalInterestRateSaveOrUpdate = async (data: Record<string, any>): Promise<any> => export const personalInterestRateSaveOrUpdate = async (data: Record<string, any>): Promise<any> =>
(await axios.post(`/product/interestRateModel/personalInterestRateSaveOrUpdate`, data)).data; (await axios.post(`/product/interestRateModel/personalInterestRateSaveOrUpdate`, data)).data;
export const deleteBusinessInterestRate = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/interestRateModel/deleteBusinessInterestRate`, data)).data;
export const deletePersonalInterestRate = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/interestRateModel/deletePersonalInterestRate`, data)).data; export const businessQuotaModelDetails = async (checkpointId: number, projectId: number): Promise<any> =>
(await axios.post(`/product/quotaModel/businessQuotaModelDetails?checkpointId=${checkpointId}&projectId=${projectId}`)).data;
export const quotaModelList = async (data: Record<string, any>): Promise<any> =>
(
await axios.post(`/product/quotaModel/list`, {
...getIds(),
...data,
})
).data;
export const quotaModelDel = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/quotaModel/delete`, data)).data;
export const businessQuotaModelDetails = async (): Promise<any> => (await axios.post(`/product/quotaModel/businessQuotaModelDetails`, getIds())).data;
export const businessQuotaModelSaveOrUpdate = async (data: Record<string, any>): Promise<any> => export const businessQuotaModelSaveOrUpdate = async (data: Record<string, any>): Promise<any> =>
(await axios.post(`/product/quotaModel/businessQuotaModelSaveOrUpdate`, data)).data; (await axios.post(`/product/quotaModel/businessQuotaModelSaveOrUpdate`, data)).data;
export const personalCreditModelDetails = async (params: Record<string, any>): Promise<any> => export const personalCreditModelDetails = async (checkpointId: number, projectId: number): Promise<any> =>
(await axios.post(`/product/quotaModel/personalCreditModelDetails`, {}, { params })).data; (await axios.post(`/product/quotaModel/personalCreditModelDetails?checkpointId=${checkpointId}&projectId=${projectId}`)).data;
export const personalCreditModelSaveOrUpdate = async (data: Record<string, any>): Promise<any> => export const personalCreditModelSaveOrUpdate = async (data: Record<string, any>): Promise<any> =>
(await axios.post(`/product/quotaModel/personalCreditModelSaveOrUpdate`, data)).data; (await axios.post(`/product/quotaModel/personalCreditModelSaveOrUpdate`, data)).data;
export const fiveLevelClassification = async (data: Record<string, any>): Promise<any> => export const fiveLevelClassificationDetails = async (checkpointId: number, projectId: number): Promise<any> =>
( (await axios.post(`/product/fiveLevelClassification/details?checkpointId=${checkpointId}&projectId=${projectId}`)).data;
await axios.post(`/product/fiveLevelClassification/list`, {
...getIds(),
...data,
})
).data;
export const fiveLevelClassificationDetails = async (params: Record<string, any>): Promise<any> =>
(await axios.post(`/product/fiveLevelClassification/details`, {}, { params })).data;
export const fiveLevelClassificationSave = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/fiveLevelClassification/saveOrUpdate`, data)).data; export const fiveLevelClassificationSave = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/fiveLevelClassification/saveOrUpdate`, data)).data;
export const fiveLevelClassificationDel = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/fiveLevelClassification/delete`, data)).data;
export const postLoanInspectionDetails = async (checkpointId: number, projectId: number): Promise<any> =>
export const postLoanInspection = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/postLoanInspection/details?checkpointId=${checkpointId}&projectId=${projectId}`)).data;
(
await axios.post(`/product/postLoanInspection/list`, {
...getIds(),
...data,
})
).data;
export const postLoanInspectionDetails = async (params: Record<string, any>): Promise<any> => (await axios.post(`/product/postLoanInspection/details`, {}, { params })).data;
export const postLoanInspectionSave = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/postLoanInspection/saveOrUpdate`, data)).data; export const postLoanInspectionSave = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/postLoanInspection/saveOrUpdate`, data)).data;
export const postLoanInspectionDel = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/postLoanInspection/delete`, data)).data;
export const postCreditScoreDetails = async (checkpointId: number, projectId: number): Promise<any> =>
export const postCreditScore = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/postCreditScore/details?checkpointId=${checkpointId}&projectId=${projectId}`)).data;
(
await axios.post(`/product/postCreditScore/list`, {
...getIds(),
...data,
})
).data;
export const postCreditScoreDetails = async (params: Record<string, any>): Promise<any> => (await axios.post(`/product/postCreditScore/details`, {}, { params })).data;
export const postCreditScoreSave = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/postCreditScore/saveOrUpdate`, data)).data; export const postCreditScoreSave = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/postCreditScore/saveOrUpdate`, data)).data;
export const postCreditScoreDel = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/postCreditScore/delete`, data)).data;
export const postLoanWarningDetails = async (checkpointId: number, projectId: number): Promise<any> =>
export const postLoanWarning = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/postLoanWarning/details?checkpointId=${checkpointId}&projectId=${projectId}`)).data;
(
await axios.post(`/product/postLoanWarning/list`, {
...getIds(),
...data,
})
).data;
export const postLoanWarningDetails = async (params: Record<string, any>): Promise<any> => (await axios.post(`/product/postLoanWarning/details`, {}, { params })).data;
export const postLoanWarningSave = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/postLoanWarning/saveOrUpdate`, data)).data; export const postLoanWarningSave = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/postLoanWarning/saveOrUpdate`, data)).data;
export const postLoanWarningDel = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/postLoanWarning/delete`, data)).data;
export const collectionAfterLoan = async (data: Record<string, any>): Promise<any> =>
(
await axios.post(`/product/collectionAfterLoan/list`, {
...getIds(),
...data,
})
).data;
export const collectionAfterLoanDetails = async (params: Record<string, any>): Promise<any> => (await axios.post(`/product/collectionAfterLoan/details`, {}, { params })).data;
export const collectionAfterLoanSave = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/collectionAfterLoan/saveOrUpdate`, data)).data;
export const collectionAfterLoanDel = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/collectionAfterLoan/delete`, data)).data;
export const isTheStrategyRelatedToTheProduct = async (data: Record<string, any>): Promise<any> => export const collectionAfterLoanDetails = async (checkpointId: number, projectId: number): Promise<any> =>
(await axios.post(`/product/managerOfRiskControl/bankRiskControlAllocation/isTheStrategyRelatedToTheProduct`, data)).data; (await axios.post(`/product/collectionAfterLoan/details?checkpointId=${checkpointId}&projectId=${projectId}`)).data;
export const collectionAfterLoanSave = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/collectionAfterLoan/saveOrUpdate`, data)).data;

@ -16,5 +16,3 @@ export const editExperimentalData = async (data: Record<string, any>): Promise<a
(await axios.post('/occupationlab/occupationlab/experimentalReport/editExperimentalData', data)).data; (await axios.post('/occupationlab/occupationlab/experimentalReport/editExperimentalData', data)).data;
export const getStartTime = async (params: Record<string, any>): Promise<any> => (await axios.get('/python/python/getStartTime', { params })).data; export const getStartTime = async (params: Record<string, any>): Promise<any> => (await axios.get('/python/python/getStartTime', { params })).data;
export const getCurrentTime = async (): Promise<any> => (await axios.get('/competition/competition/management/getCurrentTime')).data; export const getCurrentTime = async (): Promise<any> => (await axios.get('/competition/competition/management/getCurrentTime')).data;
export const heartbeatDetection = async (): Promise<any> => (await axios.get('/nakadai/message/heartbeatDetection')).data;
export const initData = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/product/bank/operation/initData`, data)).data;

@ -0,0 +1,31 @@
import axios from '@/utils/request';
export const queryOrgList = async (params?: Record<string, any>): Promise<any> => (await axios.get('/backend/core/org', { params })).data;
export const queryOrg = async (id: number): Promise<any> => (await axios.get(`/backend/core/org/${id}`)).data;
export const createOrg = async (data: Record<string, any>): Promise<any> => (await axios.post('/backend/core/org', data)).data;
export const updateOrg = async (data: Record<string, any>): Promise<any> => (await axios.post('/backend/core/org?_method=put', data)).data;
export const updateOrgOrder = async (data: number[]): Promise<any> => (await axios.post('/backend/core/org/order?_method=put', data)).data;
export const deleteOrg = async (data: number[]): Promise<any> => (await axios.post('/backend/core/org?_method=delete', data)).data;
export const queryRoleList = async (params?: Record<string, any>): Promise<any> => (await axios.get('/backend/core/role', { params })).data;
export const queryRole = async (id: number): Promise<any> => (await axios.get(`/backend/core/role/${id}`)).data;
export const createRole = async (data: Record<string, any>): Promise<any> => (await axios.post('/backend/core/role', data)).data;
export const updateRole = async (data: Record<string, any>): Promise<any> => (await axios.post('/backend/core/role?_method=put', data)).data;
export const updateRoleOrder = async (data: number[]): Promise<any> => (await axios.post('/backend/core/role/order?_method=put', data)).data;
export const deleteRole = async (data: number[]): Promise<any> => (await axios.post('/backend/core/role?_method=delete', data)).data;
export const queryGroupList = async (params?: Record<string, any>): Promise<any> => (await axios.get('/backend/core/group', { params })).data;
export const queryGroup = async (id: number): Promise<any> => (await axios.get(`/backend/core/group/${id}`)).data;
export const createGroup = async (data: Record<string, any>): Promise<any> => (await axios.post('/backend/core/group', data)).data;
export const updateGroup = async (data: Record<string, any>): Promise<any> => (await axios.post('/backend/core/group?_method=put', data)).data;
export const updateGroupOrder = async (data: number[]): Promise<any> => (await axios.post('/backend/core/group/order?_method=put', data)).data;
export const deleteGroup = async (data: number[]): Promise<any> => (await axios.post('/backend/core/group?_method=delete', data)).data;
export const queryUserPage = async (params?: Record<string, any>): Promise<any> => (await axios.get('/backend/core/user', { params })).data;
export const queryUser = async (id: number): Promise<any> => (await axios.get(`/backend/core/user/${id}`)).data;
export const createUser = async (data: Record<string, any>): Promise<any> => (await axios.post('/backend/core/user', data)).data;
export const updateUser = async (data: Record<string, any>): Promise<any> => (await axios.post('/backend/core/user?_method=put', data)).data;
export const deleteUser = async (data: number[]): Promise<any> => (await axios.post('/backend/core/user?_method=delete', data)).data;
export const usernameValidation = async (username?: string): Promise<any> => (await axios.get('/backend/core/user/username-validation', { params: { username } })).data;
export const emailValidation = async (email?: string): Promise<any> => (await axios.get('/backend/core/user/email-validation', { params: { email } })).data;
export const mobileValidation = async (mobile?: string): Promise<any> => (await axios.get('/backend/core/user/mobile-validation', { params: { mobile } })).data;

@ -1 +0,0 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1713852488689" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2030" width="20" height="20" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M900.896 240.832c-21.344-29.024-51.04-63.04-83.584-95.552s-66.528-62.304-95.552-83.552c-49.44-36.256-73.376-40.384-87.136-40.384l-475.296 0c-42.336 0-76.704 34.336-76.704 76.672l0 827.968c0 42.336 34.368 76.704 76.704 76.704l705.248 0c42.208 0 76.768-34.336 76.768-76.704l0-597.952c-0.032-13.792-4.256-37.792-40.48-87.168l0 0 0 0zM773.856 188.672c29.504 29.472 52.544 55.968 69.568 77.984l-147.552 0 0-147.552c22.016 17.056 48.544 40.16 77.984 69.536l0 0 0 0 0 0zM880.032 925.952c0 8.384-7.008 15.392-15.392 15.392l-705.248 0c-8.384 0-15.392-7.008-15.392-15.392l0-827.936c0-8.288 7.008-15.328 15.392-15.328l475.296 0 0 214.688c0 16.864 13.728 30.688 30.688 30.688l214.688 0 0.032 597.92zM779.872 823.296l-172.928-172.96c29.024-36.384 46.432-82.464 46.432-132.704 0-117.6-95.328-212.896-212.896-212.896-117.632 0-212.896 95.296-212.896 212.896s95.328 212.896 212.896 212.896c46.304 0 83.744-9.344 118.688-34.496l173.984 173.984c6.56 6.56 17.088 6.56 23.584 0l23.136-23.072c6.432-6.528 6.432-17.12 0-23.648l0 0 0 0zM440.448 656.832c-76.896 0-139.232-62.336-139.232-139.232s62.336-139.232 139.232-139.232 139.232 62.336 139.232 139.232-62.304 139.232-139.232 139.232l0 0 0 0z" fill="#006bff" p-id="2031"></path></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

@ -17,7 +17,7 @@ export default {
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
try { try {
// 上传到阿里云oss // 上传到阿里云oss
const res = await client.multipartUpload(`${Date.now()}.${getFileExt(file.name)}`, file); const res = await client.multipartUpload(Date.now() + '.' + getFileExt(file.name), file);
resolve({ resolve({
format: getFileExt(file.name), format: getFileExt(file.name),
name: file.name, name: file.name,

@ -1,7 +1,10 @@
<template> <template>
<div class="flex items-center p-3 mb-5 bg-white rounded-[10px]"> <div class="flex items-center p-3 mb-5 bg-white rounded-[10px]">
<div class="inline-flex items-center cursor-pointer" @click="back"> <div class="inline-flex items-center cursor-pointer"
<img src="@/assets/images/back.png" alt="" class="" /> @click="back">
<img src="@/assets/images/back.png"
alt=""
class="" />
<span class="ml-[6px] text-sm text-[#3C65FF]">返回</span> <span class="ml-[6px] text-sm text-[#3C65FF]">返回</span>
</div> </div>
<span class="mx-5 text-sm text-[#999]">|</span> <span class="mx-5 text-sm text-[#999]">|</span>
@ -9,7 +12,8 @@
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { useRouter } from 'vue-router'; import { onMounted, ref } from 'vue';
import { useRouter, useRoute } from 'vue-router';
import { logout } from '@/store/useCurrentUser'; import { logout } from '@/store/useCurrentUser';
const props = defineProps({ const props = defineProps({

@ -0,0 +1,258 @@
<template>
<el-dialog
:title="title"
:close-on-click-modal="!unsaved"
:model-value="modelValue"
@update:model-value="$emit('update:modelValue', $event)"
@opened="!isEdit && focus?.focus()"
:width="large ? '98%' : '683px'"
:top="large ? '16px' : undefined"
>
<div>
<el-button v-if="isEdit && addable" :disabled="perm(`${perms}:create`)" type="primary" :icon="Plus" @click="handleAdd">{{ $t('add') }}</el-button>
<el-popconfirm v-if="isEdit" :title="$t('confirmDelete')" @confirm="handleDelete">
<template #reference>
<el-button :loading="buttonLoading" :disabled="disableDelete?.(bean) || perm(`${perms}:delete`)" :icon="Delete">{{ $t('delete') }}</el-button>
</template>
</el-popconfirm>
<el-button v-if="isEdit" @click="handlePrev" :disabled="!hasPrev">{{ $t('form.prev') }}</el-button>
<el-button v-if="isEdit" @click="handleNext" :disabled="!hasNext">{{ $t('form.next') }}</el-button>
<el-button @click="handleCancel" type="primary">{{ $t('back') }}</el-button>
<el-tooltip :content="$t('form.continuous')" placement="top">
<el-switch v-model="continuous" size="small" class="ml-2"></el-switch>
</el-tooltip>
<el-tag v-if="unsaved" type="danger" class="ml-2">{{ $t('form.unsaved') }}</el-tag>
<slot name="header" :values="values" :bean="bean" :isEdit="isEdit"></slot>
</div>
<el-form
:class="['mt-5', 'pr-5']"
ref="form"
v-loading="loading"
:model="values"
:disabled="!editable"
:label-width="labelWidth ?? '150px'"
:label-position="labelPosition ?? 'right'"
>
<slot :values="values" :bean="bean" :isEdit="isEdit"></slot>
<div v-if="editable">
<el-button :disabled="perm(isEdit ? `${perms}:update` : `${perms}:create`)" :loading="buttonLoading" @click.prevent="handleSubmit" type="primary" native-type="submit">
{{ $t('submit') }}
</el-button>
</div>
</el-form>
</el-dialog>
</template>
<script lang="ts">
import { computed, defineComponent, PropType, ref, toRefs, watch } from 'vue';
import { ElMessage } from 'element-plus';
import { Plus, Delete } from '@element-plus/icons-vue';
import { useI18n } from 'vue-i18n';
import _ from 'lodash';
import { perm } from '@/store/useCurrentUser';
const CONTINUOUS_SETTINGS = 'ujcms_continuous_settings';
function fetchContinuous(): Record<string, boolean> {
const settings = localStorage.getItem(CONTINUOUS_SETTINGS);
return settings ? JSON.parse(settings) : {};
}
function storeContinuous(settings: Record<string, boolean>) {
localStorage.setItem(CONTINUOUS_SETTINGS, JSON.stringify(settings));
}
function getContinuous(name: string) {
const settings = fetchContinuous();
return settings[name] ?? false;
}
function setContinuous(name: string, continuous: boolean) {
const settings = fetchContinuous();
settings[name] = continuous;
storeContinuous(settings);
}
export default defineComponent({
name: 'DialogForm',
props: {
modelValue: { type: Boolean, required: true },
name: { type: String, required: true },
beanId: { required: true },
beanIds: { type: Array, required: true },
initValues: { type: Function as PropType<(bean?: any) => any>, required: true },
toValues: { type: Function as PropType<(bean: any) => any>, required: true },
queryBean: { type: Function as PropType<(id: any) => Promise<any>>, required: true },
createBean: { type: Function as PropType<(bean: any) => Promise<any>>, required: true },
updateBean: { type: Function as PropType<(bean: any) => Promise<any>>, required: true },
deleteBean: { type: Function as PropType<(ids: any[]) => Promise<any>>, required: true },
disableDelete: { type: Function as PropType<(bean: any) => boolean> },
addable: { type: Boolean, default: true },
editable: { type: Boolean, default: true },
perms: String,
focus: Object,
large: Boolean,
labelPosition: String,
labelWidth: String,
},
emits: {
'update:modelValue': null,
finished: null,
beanChange: null,
beforeSubmit: null,
},
setup(props, { emit }) {
const { name, beanId, beanIds, focus, modelValue: visible } = toRefs(props);
const { t } = useI18n();
const loading = ref<boolean>(false);
const buttonLoading = ref<boolean>(false);
const continuous = ref<boolean>(getContinuous(name.value));
const reseted = ref<boolean>(false);
const unsaved = ref<boolean>(false);
const form = ref<any>();
const values = ref<any>(props.initValues());
const bean = ref<any>({});
const id = ref<any>();
const ids = ref<Array<any>>([]);
const isEdit = computed(() => id.value != null);
const title = computed(() => `${name.value} - ${isEdit.value ? `${t('edit')} (ID: ${id.value})` : `${t('add')}`}`);
const idChanged = async () => {
loading.value = true;
unsaved.value = false;
reseted.value = true;
try {
bean.value = id.value != null ? await props.queryBean(id.value) : {};
values.value = id.value != null ? props.toValues(bean.value) : props.initValues(values.value);
emit('beanChange', bean.value);
form.value.resetFields();
} finally {
loading.value = false;
}
};
watch(visible, () => {
if (visible.value) {
ids.value = beanIds.value;
if (id.value !== beanId.value) {
id.value = beanId.value;
} else if (id.value != null) {
idChanged();
}
if (id.value == null) {
reseted.value = true;
values.value = props.initValues();
}
}
});
watch(id, () => {
idChanged();
});
watch(
// 使 lodash
// https://v3.vuejs.org/guide/reactivity-computed-watchers.html#watching-reactive-objects
() => _.cloneDeep(values.value),
(curr, prev) => {
// bean
if (reseted.value) {
reseted.value = false;
} else if (JSON.stringify(curr) !== JSON.stringify(prev)) {
// values.customsundefined
// watchjsonjsonunsaved
unsaved.value = true;
}
},
{ deep: true },
);
watch(continuous, () => setContinuous(name.value, continuous.value));
const index = computed(() => ids.value.indexOf(id.value));
const hasPrev = computed(() => index.value > 0);
const hasNext = computed(() => index.value < ids.value.length - 1);
const handlePrev = () => {
if (hasPrev.value) {
id.value = ids.value[index.value - 1];
}
};
const handleNext = () => {
if (hasNext.value) {
id.value = ids.value[index.value + 1];
}
};
const handleAdd = () => {
// eslint-disable-next-line no-unused-expressions
focus?.value?.focus?.();
id.value = undefined;
};
const handleCancel = () => {
emit('update:modelValue', false);
};
const handleSubmit = () => {
form.value.validate(async (valid: boolean) => {
if (!valid) return;
buttonLoading.value = true;
try {
emit('beforeSubmit', values.value);
if (isEdit.value) {
await props.updateBean(values.value);
unsaved.value = false;
} else {
await props.createBean(values.value);
// eslint-disable-next-line no-unused-expressions
focus?.value?.focus?.();
unsaved.value = false;
reseted.value = true;
values.value = props.initValues(values.value);
form.value.resetFields();
}
ElMessage.success(t('success'));
if (!continuous.value) emit('update:modelValue', false);
emit('finished', bean.value);
} finally {
buttonLoading.value = false;
}
});
};
const handleDelete = async () => {
buttonLoading.value = true;
try {
await props.deleteBean([id.value]);
if (!continuous.value) emit('update:modelValue', false);
if (hasNext.value) {
handleNext();
ids.value.splice(index.value - 1, 1);
} else if (hasPrev.value) {
handlePrev();
ids.value.splice(index.value + 1, 1);
} else {
emit('update:modelValue', false);
}
ElMessage.success(t('success'));
emit('finished');
} finally {
buttonLoading.value = false;
}
};
const setUnsaved = (bool: boolean) => {
unsaved.value = bool;
};
return {
perm,
handleAdd,
handleDelete,
handleSubmit,
handleCancel,
handlePrev,
handleNext,
hasPrev,
hasNext,
loading,
buttonLoading,
continuous,
unsaved,
isEdit,
form,
values,
bean,
id,
title,
setUnsaved,
Plus,
Delete,
};
},
});
</script>

@ -0,0 +1,30 @@
<template>
<p>{{ t('hello') }}</p>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { useI18n } from 'vue-i18n';
export default defineComponent({
name: 'HelloI18n',
setup() {
const { t } = useI18n({
inheritLocale: true,
useScope: 'local',
});
// Something todo ..
return { t };
},
});
</script>
<i18n>
{
"en": {
"hello": "Hello i18n in SFC!"
}
}
</i18n>

@ -0,0 +1,17 @@
<template>
{{ label ?? $t(message) }}
<el-tooltip :content="tooltip ?? $t(message + '.tooltip')"
placement="top">
<el-icon class="text-base align-text-top"><question-filled /></el-icon>
</el-tooltip>
</template>
<script setup lang="ts">
import { QuestionFilled } from '@element-plus/icons-vue';
defineProps({
label: { type: String },
tooltip: { type: String },
message: { type: String, required: true },
});
</script>

@ -0,0 +1,27 @@
<template>
<el-button-group>
<el-button :disabled="disabled"
:icon="Top"
@click="$emit('move', 'top')">{{ $t('moveTop') }}</el-button>
<el-button :disabled="disabled"
:icon="ArrowUp"
@click="$emit('move', 'up')">{{ $t('moveUp') }}</el-button>
<el-button :disabled="disabled"
:icon="ArrowDown"
@click="$emit('move', 'down')">{{ $t('moveDown') }}</el-button>
<el-button :disabled="disabled"
:icon="Bottom"
@click="$emit('move', 'bottom')">{{ $t('moveBottom') }}</el-button>
</el-button-group>
</template>
<script setup lang="ts">
import { Top, Bottom, ArrowUp, ArrowDown } from '@element-plus/icons-vue';
defineProps({
disabled: { type: Boolean, required: true },
});
defineEmits({
move: null,
});
</script>

@ -1,48 +1,83 @@
<template> <template>
<div v-if="!hidePanel" :class="['panel', { active: visible }]" id="panel" ref="container" :style="style" v-loading="loading"> <div v-if="!hidePanel"
<el-container class="scrollbar" id="container" v-show="visible"> :class="['panel', { active: visible }]"
<el-header class="panel-header" id="panelHeader"> id="panel"
ref="container"
:style="style">
<el-container class="scrollbar"
id="container"
v-show="visible">
<el-header id="header">
<div class="panel-header"
id="panelHeader">
<div class="project"> <div class="project">
<div class="inline-flex items-center"> <div class="inline-flex items-center">
<p class="whitespace-nowrap">实训项目</p> <p>实训项目</p>
<el-tooltip effect="dark" content="点击右侧“下三角”按钮可切换实验项目" placement="bottom"> <el-tooltip effect="dark"
<i class="info el-icon-warning" style="margin-left: 10px"></i> content="点击右侧“下三角”按钮可切换实验项目"
placement="bottom">
<i class="info el-icon-warning"
style="margin-left: 10px"></i>
</el-tooltip> </el-tooltip>
</div> </div>
<el-select v-model="param.projectId" placeholder="请选择" class="select" :disabled="per != 0" @change="getCache(1)"> <el-select v-model="param.projectId"
<el-option v-for="(item, i) in projectList" :key="item.projectId" :label="i + 1 + '. ' + item.projectName" :value="item.projectId"></el-option> placeholder="请选择"
class="select"
:disabled="per != 0"
@change="selectProject">
<el-option v-for="(item, i) in projectList"
:key="item.projectId"
:label="i + 1 + '. ' + item.projectName"
:value="item.projectId"></el-option>
</el-select> </el-select>
</div> </div>
<div class="item"> <div class="item">
<div class="count"> <div class="count">
实训{{ text }}时间 <span>{{ day }}</span 实训{{ text }}时间 <span>{{ day }}</span> <span>{{ hour }}</span>小时 <span>{{ minutes }}</span> <span>{{ seconds }}</span>
> <span>{{ hour }}</span
>小时 <span>{{ minutes }}</span
> <span>{{ seconds }}</span
>
</div> </div>
</div> </div>
<div v-if="per !== 2" class="item"> <div class="item">
<div>
总得分 总得分
<span class="total-score">{{ grade }}</span> <span class="total-score">{{ grade }}</span>
</div> </div>
<div class="actions"> </div>
<el-button class="h-[40px]" @click="toReport" v-if="isSubmit">查看实验报告</el-button> <div>
<el-button class="reload h-[40px]" @click="reloadConfirm" v-show="per == 0">重新开始</el-button> <el-button class="h-[40px]"
<el-button type="primary" class="submit btn h-[40px]" :loading="submiting" @click="confirmSubmit" :disabled="isSubmit || !projectList.length">提交</el-button> @click="toReport"
v-if="isSubmit">查看实验报告</el-button>
<el-button class="reload h-[40px]"
@click="reload"
v-show="per == 0">重新开始</el-button>
<el-button type="primary"
class="submit btn h-[40px]"
:loading="submiting"
@click="submit"
:disabled="isSubmit || !projectList.length">提交</el-button>
</div>
</div> </div>
</el-header> </el-header>
<el-container id="infoContainer"> <el-container id="infoContainer">
<el-aside id="aside" width="30%"> <el-aside id="aside"
width="30%">
<div class="aside-header"> <div class="aside-header">
<div class="p-title"> <div class="p-title">
<i class="el-icon-s-order"></i> <i class="el-icon-s-order"></i>
<p>实验目标</p> <p>实验目标</p>
</div> </div>
<div class="goal"> <div class="goal">
<div v-if="pd.experimentTargetType == 0 || !pd.experimentTargetType" class="ql-editor" v-html="pd.experimentTarget"></div> <div v-if="pd.experimentTargetType == 0 || !pd.experimentTargetType"
class="ql-editor"
v-html="pd.experimentTarget"></div>
<mavon-editor v-else class="md" v-model="pd.experimentTarget" :ishljs="true" :subfield="false" :editable="false" :toolbarsFlag="false" :boxShadowStyle="none" /> <mavon-editor v-else
class="md"
v-model="pd.experimentTarget"
:ishljs="true"
:subfield="false"
:editable="false"
:toolbarsFlag="false"
:boxShadowStyle="none" />
</div> </div>
</div> </div>
<div class="aside-footer"> <div class="aside-footer">
@ -53,20 +88,35 @@
<div> <div>
<el-row> <el-row>
<el-col :span="24"> <el-col :span="24">
<el-card shadow="never" :border="false"> <el-card shadow="never"
<el-table class="task-table" :data="taskList" :stripe="true"> :border="false">
<el-table class="task-table"
:data="taskList"
:stripe="true">
<el-table-column type="index"></el-table-column> <el-table-column type="index"></el-table-column>
<el-table-column prop="name" label="判分点" align="center"></el-table-column> <el-table-column prop="name"
<el-table-column prop="score" label="分值" width="60" align="center"></el-table-column> label="判分点"
align="center"></el-table-column>
<el-table-column prop="score"
label="分值"
width="60"
align="center"></el-table-column>
<template v-if="!param.competitionId"> <template v-if="!param.competitionId">
<el-table-column label="结果" width="60" align="center"> <el-table-column label="结果"
<template #default="{ row }"> width="60"
align="center">
<template v-slot="scope">
<template v-if="isSubmit"> <template v-if="isSubmit">
<div v-if="!param.competitionId" class="flex justify-center items-center"> <div v-if="!param.competitionId"
<el-icon v-if="row.finishedResult" color="#15d500" :size="16"> class="flex justify-center items-center">
<el-icon v-if="scope.row.finishedResult"
color="#15d500"
:size="16">
<Check /> <Check />
</el-icon> </el-icon>
<el-icon v-else color="#f00" :size="16"> <el-icon v-else
color="#f00"
:size="16">
<Close /> <Close />
</el-icon> </el-icon>
</div> </div>
@ -74,9 +124,12 @@
</template> </template>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="得分" width="60" align="center"> <el-table-column prop="score"
<template #default="{ row }"> label="得分"
<template v-if="isSubmit">{{ param.competitionId ? '-' : row.examScore }}</template> width="60"
align="center">
<template v-slot="scope">
<template v-if="isSubmit">{{ param.competitionId ? '-' : scope.row.examScore }}</template>
</template> </template>
</el-table-column> </el-table-column>
</template> </template>
@ -88,38 +141,65 @@
</div> </div>
</el-aside> </el-aside>
<el-main id="main"> <el-main id="main">
<el-tabs class="info-tab" v-model="pannelTab" type="card"> <el-tabs class="info-tab"
<el-tab-pane label="项目背景" name="first"> v-model="pannelTab"
<div v-if="pd.experimentDescriptionType == 0 || !pd.experimentDescriptionType" class="ql-editor" v-html="pd.experimentDescription"></div> type="card">
<el-tab-pane label="项目背景"
name="first">
<div v-if="pd.experimentDescriptionType == 0 || !pd.experimentDescriptionType"
class="ql-editor"
v-html="pd.experimentDescription"></div>
<mavon-editor v-else class="md" v-model="pd.experimentDescription" :ishljs="true" :subfield="false" :editable="false" :toolbarsFlag="false" :boxShadowStyle="none" /> <mavon-editor v-else
class="md"
v-model="pd.experimentDescription"
:ishljs="true"
:subfield="false"
:editable="false"
:toolbarsFlag="false"
:boxShadowStyle="none" />
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="实验要求" name="second"> <el-tab-pane label="实验要求"
name="second">
<el-collapse v-model="curReq"> <el-collapse v-model="curReq">
<el-collapse-item v-for="item in points" :name="item.judgmentId" :key="item.judgmentId"> <el-collapse-item v-for="item in points"
:name="item.judgmentId"
:key="item.judgmentId">
<template v-slot:title> <template v-slot:title>
<i class="el-icon-s-ticket"></i> <i class="el-icon-s-ticket"></i>
<div class="break-all des" v-html="item.name"></div> <div class="break-all des"
v-html="item.name"></div>
</template> </template>
<div v-if="item.experimentalRequirementsType == 0 || !item.experimentalRequirementsType" class="ql-editor" v-html="item.experimentalRequirements"></div> <div v-if="item.experimentalRequirementsType == 0 || !item.experimentalRequirementsType"
class="ql-editor"
v-html="item.experimentalRequirements"></div>
<mavon-editor <mavon-editor v-else
v-else
class="md" class="md"
v-model="item.experimentalRequirements" v-model="item.experimentalRequirements"
:ishljs="true" :ishljs="true"
:subfield="false" :subfield="false"
:editable="false" :editable="false"
:toolbarsFlag="false" :toolbarsFlag="false"
:boxShadowStyle="none" :boxShadowStyle="none" />
/>
</el-collapse-item> </el-collapse-item>
</el-collapse> </el-collapse>
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="实验提示" name="third" v-if="hintOpen"> <el-tab-pane label="实验提示"
<div v-if="pd.experimentHintType == 0 || !pd.experimentHintType" class="ql-editor" v-html="pd.experimentHint"></div> name="third"
v-if="hintOpen">
<div v-if="pd.experimentHintType == 0 || !pd.experimentHintType"
class="ql-editor"
v-html="pd.experimentHint"></div>
<mavon-editor v-else class="md" v-model="pd.experimentHint" :ishljs="true" :subfield="false" :editable="false" :toolbarsFlag="false" :boxShadowStyle="none" /> <mavon-editor v-else
class="md"
v-model="pd.experimentHint"
:ishljs="true"
:subfield="false"
:editable="false"
:toolbarsFlag="false"
:boxShadowStyle="none" />
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
</el-main> </el-main>
@ -127,21 +207,27 @@
</el-container> </el-container>
<div :class="['toggle absolute top-[200px] text-center', visible ? 'top-[35%] left-[100%]' : '']"> <div :class="['toggle absolute top-[200px] text-center', visible ? 'top-[35%] left-[100%]' : '']">
<el-icon class="cursor-pointer" color="#f1772b" :size="24"> <el-icon class="cursor-pointer"
color="#f1772b"
:size="24">
<Rank id="toggle" /> <Rank id="toggle" />
</el-icon> </el-icon>
<div class="toggle-panel w-[40px] h-[175px] bg-[length:100%_100%] bg-no-repeat cursor-pointer" :class="{ active: visible }" ref="handle" @click="pannelToggle"></div> <div class="toggle-panel w-[40px] h-[175px] bg-[length:100%_100%] bg-no-repeat cursor-pointer"
:class="{ active: visible }"
ref="handle"
@click="visible = !visible"></div>
</div> </div>
</div> </div>
<div v-if="isSubmit && !isReport" class="z-[199] fixed top-[64px] right-0 bottom-0 left-0 bg-[rgba(0,0,0,.3)]"></div> <div v-if="isSubmit && !isReport"
class="z-[199] fixed top-[64px] right-0 bottom-0 left-0 bg-[rgba(0,0,0,.3)]"></div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, onMounted, inject, computed, watch, onUnmounted } from 'vue'; import { ref, reactive, onMounted, inject, computed, watch } from 'vue';
import { submitOpe } from '@/api/bank'; import { submitOpe } from '@/api/bank';
import { getSandTableLastCache, deleteOperationData } from '@/api/judgment'; import { deleteCache } from '@/api/judgment';
import { getProjectBySystemId, getProjectDetail, getDetailById, getCompetition, getStartTime, heartbeatDetection, initData } from '@/api/system'; import { pageStuAssessment, getProjectBySystemId, getProjectDetail, getDetailById, getCompetition, getStartTime } from '@/api/system';
import Settings from '@/settings'; import Settings from '@/settings';
import { useRouter, useRoute, beforeRouteLeave } from 'vue-router'; import { useRouter, useRoute } from 'vue-router';
import type { Action } from 'element-plus'; import type { Action } from 'element-plus';
import { ElMessage, ElMessageBox } from 'element-plus'; import { ElMessage, ElMessageBox } from 'element-plus';
import { Close, Check, Rank } from '@element-plus/icons-vue'; import { Close, Check, Rank } from '@element-plus/icons-vue';
@ -150,9 +236,9 @@ import Cookies from 'js-cookie';
import { mavonEditor } from 'mavon-editor'; import { mavonEditor } from 'mavon-editor';
import 'mavon-editor/dist/css/index.css'; import 'mavon-editor/dist/css/index.css';
import '@vueup/vue-quill/dist/vue-quill.snow.css'; import '@vueup/vue-quill/dist/vue-quill.snow.css';
import { useDraggable, useWindowSize, Position } from '@vueuse/core'; import { useDraggable } from '@vueuse/core';
import { logout } from '@/store/useCurrentUser'; import { logout } from '@/store/useCurrentUser';
import { getNow } from '@/utils/common'; import { getIds, getNow } from '@/utils/common';
const router = useRouter(); const router = useRouter();
const route = useRoute(); const route = useRoute();
@ -167,7 +253,6 @@ const isReport = ref<boolean>(true);
const grade = ref<string | number>('00'); const grade = ref<string | number>('00');
const text = ref<string>(''); // const text = ref<string>(''); //
const counterTimer = ref<any>(null); const counterTimer = ref<any>(null);
const heartBeatTimer = ref<any>(null);
const day = ref<number | string>(0); const day = ref<number | string>(0);
const seconds = ref<number | string>(0); const seconds = ref<number | string>(0);
const minutes = ref<number | string>(0); const minutes = ref<number | string>(0);
@ -180,23 +265,20 @@ const judgmentId = ref<string | number>('');
const curReq = ref<Record<string, any>[]>([]); const curReq = ref<Record<string, any>[]>([]);
const taskList = ref<Record<string, any>[]>([]); const taskList = ref<Record<string, any>[]>([]);
const pannelTab = ref<string>('first'); const pannelTab = ref<string>('first');
const loading = ref<boolean>(false);
const submiting = ref<boolean>(false); const submiting = ref<boolean>(false);
const reportId = ref<string | number>(''); const reportId = ref<string | number>('');
const countVal = ref<any>(''); const countVal = ref<any>('');
const getLevel = ref(); const getLevel = ref();
const container = ref<HTMLElement | null>(null); const container = ref<HTMLElement | null>(null);
const handle = ref<HTMLElement | null>(null); const handle = ref<HTMLElement | null>(null);
const { width, height } = useWindowSize();
// //
const { x, y, style } = useDraggable(container, { const { x, y, style } = useDraggable(container, {
initialValue: { x: 0, y: 200 }, initialValue: { x: 0, y: 200 },
stopPropagation: true, stopPropagation: true,
handle: handle.value, handle: handle.value,
onStart: (p: Position, e: PointerEvent): boolean => { onStart(position, e) {
const { id } = e.target; const { id } = e.target;
if (id !== 'panelHeader' && id !== 'toggle') return false; if (id !== 'panelHeader' && id !== 'toggle') return false;
return true;
}, },
}); });
@ -223,7 +305,6 @@ if (param.token) {
Cookies.set('sand-account', param.account ?? ''); Cookies.set('sand-account', param.account ?? '');
Cookies.set('sand-admin', param.admin ?? ''); // Cookies.set('sand-admin', param.admin ?? ''); //
Cookies.remove('sand-submit'); Cookies.remove('sand-submit');
Cookies.remove('sand-loaded');
router.replace(route.path); router.replace(route.path);
} else { } else {
param.systemId = Cookies.get('sand-systemId'); param.systemId = Cookies.get('sand-systemId');
@ -259,28 +340,21 @@ watch(
); );
// //
let getSumTime = (reset?: number): Promise<any> => { const getSumTime = (): Promise<any> => {
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
const res = await getStartTime({ const res = await getStartTime({
permissions: per.value, permissions: per.value,
projectId: param.projectId, projectId: param.projectId,
reset,
}); });
resolve(res.startTime ? new Date(res.startTime) : ''); resolve(res.startTime ? new Date(res.startTime) : '');
}); });
}; };
//
let getEntryTime = async (resetTime?: number) => {
let now = await getSumTime(resetTime); //
if (!now) now = await getNow();
entryTime.value = now;
};
// //
const timeFormat = (num: number): string | number => { const timeFormat = (num: number): string | number => {
return num < 10 ? `0${num}` : num; return num < 10 ? `0${num}` : num;
}; };
// //
let reloadCount = () => { const reloadCount = () => {
clearInterval(counterTimer.value); clearInterval(counterTimer.value);
countVal.value = ''; countVal.value = '';
day.value = '00'; day.value = '00';
@ -289,26 +363,7 @@ let reloadCount = () => {
hour.value = '00'; hour.value = '00';
}; };
// //
let counter = async (counterTime: number) => { const counter = (counterTime: number) => {
if (counterTime <= 0) {
if (per.value) {
clearInterval(counterTimer.value);
// /
if (!Cookies.get('sand-submit')) {
Cookies.set('sand-submit', 'true');
await submit();
submiting.value = true;
ElMessageBox.alert(`${per.value === 1 ? '考核' : '竞赛'}时间已到,系统已自动交卷`, '提示', {
confirmButtonText: '确定',
closeOnClickModal: false,
showClose: false,
callback: (action: Action) => {
logout();
},
});
}
}
} else {
const leave1 = counterTime % (24 * 3600); // const leave1 = counterTime % (24 * 3600); //
const leave2 = leave1 % 3600; // const leave2 = leave1 % 3600; //
const leave3 = leave2 % 60; // const leave3 = leave2 % 60; //
@ -317,27 +372,40 @@ let counter = async (counterTime: number) => {
hour.value = timeFormat(Math.floor(leave1 / 3600)); hour.value = timeFormat(Math.floor(leave1 / 3600));
minutes.value = timeFormat(Math.floor(leave2 / 60)); minutes.value = timeFormat(Math.floor(leave2 / 60));
seconds.value = timeFormat(Math.round(leave3)); seconds.value = timeFormat(Math.round(leave3));
}
}; };
// //
let startCount = () => { const startCount = () => {
clearInterval(counterTimer.value); clearInterval(counterTimer.value);
counterTimer.value = setInterval(() => { counterTimer.value = setInterval(() => {
counter(per.value ? countVal.value-- : countVal.value++); counter(per.value ? countVal.value-- : countVal.value++);
}, 1000); }, 1000);
}; };
// //
let setSubmit = (val: boolean) => { const setSubmit = (val: boolean) => {
isSubmit.value = val; isSubmit.value = val;
val ? Cookies.set('sand-submit', val) : Cookies.remove('sand-submit'); Cookies.set('sand-submit', val);
}; };
// //
let getAssList = async () => { const getAssList = async () => {
await getAssStatus(); // const { list } = await pageStuAssessment({
await getProDetail(); // pageNum: 1,
// pageSize: 10000,
// });
// let done = false;
// // reportIdclassIdclassId
// if (list.find((e) => e.assessmentId == param.assessmentId && e.reportId && e.classId == param.classId)) {
// done = true;
// setSubmit(true);
// ElMessage.error('');
// setTimeout((_) => {
// window.history.back(); //
// }, 1500);
// }
getProDetail();
getAssStatus();
}; };
// //
let getAssStatus = async () => { const getAssStatus = async () => {
// //
if (!isSubmit.value) { if (!isSubmit.value) {
const { data } = await getDetailById(param.assessmentId); const { data } = await getDetailById(param.assessmentId);
@ -347,8 +415,6 @@ let getAssStatus = async () => {
submit(); submit();
ElMessageBox.alert(`考核时间已到,系统已自动交卷`, '提示', { ElMessageBox.alert(`考核时间已到,系统已自动交卷`, '提示', {
confirmButtonText: '确定', confirmButtonText: '确定',
closeOnClickModal: false,
showClose: false,
callback: (action: Action) => { callback: (action: Action) => {
logout(); logout();
}, },
@ -357,8 +423,8 @@ let getAssStatus = async () => {
} }
}; };
// //
let getCompetitionStatus = async () => { const getCompetitionStatus = async () => {
// //
if (!isSubmit.value) { if (!isSubmit.value) {
const { competition } = await getCompetition(param.competitionId); const { competition } = await getCompetition(param.competitionId);
@ -369,15 +435,13 @@ let getCompetitionStatus = async () => {
const now = await getNow(); const now = await getNow();
// //
if (now >= endTime) { if (now >= endTime) {
submit();
ElMessageBox.alert(`竞赛时间已到,系统已自动交卷`, '提示', { ElMessageBox.alert(`竞赛时间已到,系统已自动交卷`, '提示', {
confirmButtonText: '确定', confirmButtonText: '确定',
closeOnClickModal: false,
showClose: false,
callback: (action: Action) => { callback: (action: Action) => {
logout(); logout();
}, },
}); });
submit();
} else { } else {
// //
countVal.value = (endTime - now) / 1000; countVal.value = (endTime - now) / 1000;
@ -386,145 +450,49 @@ let getCompetitionStatus = async () => {
} }
} }
}; };
// //
let delCache = async () => { const selectProject = () => {
await deleteOperationData({
cid: param.cid,
projectId: param.projectId,
assessmentId: param.assessmentId,
competitionId: param.competitionId,
});
};
//
let setNewProject = (reloadPage?: number) => {
Cookies.set('sand-projectId', param.projectId); Cookies.set('sand-projectId', param.projectId);
getProDetail(); getProDetail();
setSubmit(false);
countVal.value = 0;
grade.value = '00';
pannelTab.value = 'first';
reload(); reload();
getEntryTime();
if (reloadPage) {
// //
if (route.path === '/') { if (route.path === '/') {
location.reload(); location.reload();
} else { } else {
getLevel.value && getLevel.value(); getLevel.value && getLevel.value();
} }
}
};
//
const initBuiltInData = async () => {
await initData({
assessmentId: param.assessmentId,
cid: param.cid,
competitionId: param.competitionId,
projectId: param.projectId,
});
};
//
let getCache = async (reloadPage?: number) => {
initBuiltInData();
//
const res = await getSandTableLastCache({
cid: param.cid,
projectId: param.projectId,
assessmentId: param.assessmentId || '',
competitionId: param.competitionId || '',
});
//
if (res.getLastCache) {
ElMessageBox.confirm('是否要继续上次的操作记录?', '提示', {
confirmButtonText: '是',
cancelButtonText: '否',
type: 'success',
closeOnClickModal: false,
showClose: false,
})
.then(async () => {
// id
if (res.checkpointId) {
Cookies.set('sand-level', res.checkpointId);
}
setNewProject(reloadPage);
})
.catch(async () => {
Cookies.remove('sand-level');
setNewProject();
delCache();
//
if (reloadPage) {
if (route.path === '/') {
location.reload();
} else {
getLevel.value && getLevel.value();
}
}
});
} else {
Cookies.remove('sand-level');
setNewProject(reloadPage);
}
};
let handleCache = () => {
//
if (!isSubmit.value) {
param.cid && getEntryTime();
visible.value && !Cookies.get('sand-loaded') && getCache(0); //
}
}; };
// //
const toReport = () => { const toReport = () => {
router.replace('/report'); router.push('/report');
}; };
// //
let reload = async () => { const reload = async () => {
if (!per.value) { await deleteCache(getIds());
reloadCount(); reloadCount();
startCount();
grade.value = '00'; grade.value = '00';
pannelTab.value = 'first';
setSubmit(false); setSubmit(false);
startCount();
router.push('/'); router.push('/');
}
}; };
// //
let reloadConfirm = async () => { const submit = async () => {
if (isSubmit.value) { if (isSubmit.value) return false;
reload(); ElMessageBox.confirm('此操作将视为结束考试,是否继续?', '提示', {
} else {
ElMessageBox.confirm('<p class="text-danger">点击重新开始,之前操作会清空。</p><p>确定重新开始吗?</p>', '提示', {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning', type: 'warning',
closeOnClickModal: false,
dangerouslyUseHTMLString: true,
}) })
.then(() => { .then(async () => {
getEntryTime(1);
delCache().then(); //
Cookies.remove('sand-level');
reload();
})
.catch(() => {
submiting.value = false;
});
}
};
let pannelToggle = () => {
visible.value = !visible.value;
x.value = 0;
y.value = 200;
};
//
let submit = async () => {
if (!submiting.value) {
submiting.value = true; submiting.value = true;
const checkpointId = Cookies.get('sand-level') ?? ''; const date = new Date();
const date = await getNow();
const timeSum = Math.ceil((date.getTime() - entryTime.value.getTime()) / 60000); // const timeSum = Math.ceil((date.getTime() - entryTime.value.getTime()) / 60000); //
const submitTime = dayjs(date).format('YYYY-MM-DD HH:mm:ss'); const submitTime = dayjs(date).format('YYYY-MM-DD HH:mm:ss');
reloadCount(); reloadCount();
try {
const { retMap } = await submitOpe({ const { retMap } = await submitOpe({
classId: param.classId ? param.classId : '', classId: param.classId ? param.classId : '',
className: param.className ? param.className : '', className: param.className ? param.className : '',
@ -533,7 +501,7 @@ let submit = async () => {
endTime: per.value ? param.stopTime : submitTime, // endTime: per.value ? param.stopTime : submitTime, //
submitTime, // 3 submitTime, // 3
timeSum, timeSum,
checkpointId, checkpointId: Cookies.get('sand-level') ?? '',
projectId: param.projectId, projectId: param.projectId,
projectName: projectList.value.find((e) => e.projectId == param.projectId)?.projectName, projectName: projectList.value.find((e) => e.projectId == param.projectId)?.projectName,
lcId: curReq.value, lcId: curReq.value,
@ -547,6 +515,7 @@ let submit = async () => {
}); });
setSubmit(true); setSubmit(true);
let score = 0;
// //
taskList.value.map((e) => { taskList.value.map((e) => {
const item = retMap?.scoreInfo.find((n) => n.lcId === e.judgmentId); const item = retMap?.scoreInfo.find((n) => n.lcId === e.judgmentId);
@ -554,59 +523,39 @@ let submit = async () => {
if (item) { if (item) {
e.examScore = item.questionScore; e.examScore = item.questionScore;
e.finishedResult = item.calculate; // 12 e.finishedResult = item.calculate; // 12
score += item.questionScore; //
} else { } else {
e.examScore = 0; e.examScore = 0;
} }
} catch (e) {} } catch (e) {}
}); });
const score = retMap.totalScore; grade.value = score < 10 ? '0' + score : score;
grade.value = score < 10 && !(score % 1) ? `0${score}` : score;
reportId.value = retMap.reportId; reportId.value = retMap.reportId;
Cookies.set('sand-reportId', retMap.reportId); Cookies.set('sand-reportId', retMap.reportId);
Cookies.set('sand-score', grade.value);
localStorage.setItem('sand-taskList', JSON.stringify(taskList.value)); localStorage.setItem('sand-taskList', JSON.stringify(taskList.value));
delCache(); await deleteCache(getIds());
submiting.value = false; submiting.value = false;
// //
if (per.value) { per.value &&
const time = param.resultAnnouncementTime; ElMessageBox.alert(
const msg = `提交成功${param.resultsDetails == 0 && param.resultAnnouncementTime != 0 ? ',成绩将在' + param.resultAnnouncementTime + '小时后发布,请去参赛信息模块查看' : ''}`,
time === '0' ? '提交成功!成绩将在比赛结束后公布,请前往参赛信息模块查看' : time > 0 ? `提交成功!成绩将在比赛结束后${time}小时公布,请前往参赛信息模块查看` : '提交成功'; '提示',
ElMessageBox.alert(msg, '提示', { {
confirmButtonText: '确定', confirmButtonText: '确定',
closeOnClickModal: false,
showClose: false,
callback: (action: Action) => { callback: (action: Action) => {
logout(); logout();
}, },
}); },
} );
} catch (e) {
submiting.value = false;
}
}
};
//
let confirmSubmit = () => {
if (isSubmit.value) return false;
if (!Cookies.get('sand-level')) return ElMessage.error('请选择关卡');
ElMessageBox.confirm('此操作将视为结束考试,是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
closeOnClickModal: false,
}) })
.then(submit)
.catch(() => { .catch(() => {
submiting.value = false; submiting.value = false;
}); });
}; };
// //
let getProDetail = async () => { const getProDetail = async () => {
loading.value = true;
try {
const res = await getProjectDetail({ const res = await getProjectDetail({
projectId: param.projectId, projectId: param.projectId,
stuAssessent: 1, stuAssessent: 1,
@ -626,7 +575,6 @@ let getProDetail = async () => {
curReq.value = pointsList.map((e) => e.judgmentId); // judgmentIditem curReq.value = pointsList.map((e) => e.judgmentId); // judgmentIditem
points.value = pointsList; points.value = pointsList;
taskList.value = isSubmit.value ? JSON.parse(localStorage.getItem('sand-taskList')) : pointsList; // taskList.value = isSubmit.value ? JSON.parse(localStorage.getItem('sand-taskList')) : pointsList; //
grade.value = isSubmit.value ? Cookies.get('sand-score') : '00';
judgmentId.value = pointsList[0].judgmentId; // judgmentId.value = pointsList[0].judgmentId; //
pd.value = project; pd.value = project;
curSystemId.value = project.systemId; curSystemId.value = project.systemId;
@ -639,9 +587,6 @@ let getProDetail = async () => {
countVal.value = (isPrac ? now - entryTime.value : new Date(param.stopTime).getTime() - now) / 1000; // countVal.value = (isPrac ? now - entryTime.value : new Date(param.stopTime).getTime() - now) / 1000; //
startCount(); startCount();
} }
} finally {
loading.value = false;
}
}; };
// //
@ -656,6 +601,12 @@ const getList = async () => {
if (!per.value && !param.projectId) param.projectId = projects[0]?.projectId ?? 0; // if (!per.value && !param.projectId) param.projectId = projects[0]?.projectId ?? 0; //
getProDetail(); getProDetail();
}; };
//
const getEntryTime = async () => {
let now = await getSumTime(); //
if (!now) now = await getNow();
entryTime.value = now;
};
// websocket // websocket
// socket // socket
@ -672,9 +623,9 @@ const getMessage = (msg) => {
console.log(JSON.parse(msg.data)); console.log(JSON.parse(msg.data));
const { content } = JSON.parse(msg.data); const { content } = JSON.parse(msg.data);
// 1234-id // 1234-id
if (content == 1 && per.value === 2) { if (content == 1) {
getCompetitionStatus(); getCompetitionStatus();
} else if (content.includes('3-') && per.value === 1) { } else if (content.includes('3-')) {
// 3-id // 3-id
getAssStatus(); getAssStatus();
} }
@ -695,80 +646,41 @@ const initSocket = () => {
// socket // socket
socket.onmessage = getMessage; socket.onmessage = getMessage;
}; };
//
let setHeartbeatDetection = () => { onMounted(() => {
heartBeatTimer.value = setInterval(async () => { getLevel.value = inject('getLevel'); //
await heartbeatDetection();
}, 58 * 1000);
};
//
const init = async () => {
console.log('init');
per.value = param.assessmentId ? 1 : param.competitionId ? 2 : 0; per.value = param.assessmentId ? 1 : param.competitionId ? 2 : 0;
param.cid && getEntryTime();
if (param.assessmentId) { if (param.assessmentId) {
// getAssList();
await getAssList();
handleCache();
initSocket(); initSocket();
setHeartbeatDetection();
} else if (param.competitionId) {
//
getCompetitionStatus();
handleCache();
initSocket();
setHeartbeatDetection();
} else { } else {
//
param.cid && getList(); param.cid && getList();
handleCache(); if (param.competitionId) {
getCompetitionStatus();
initSocket();
} }
if (!Cookies.get('sand-loaded')) {
Cookies.set('sand-loaded', 1); //
} else if (per.value) {
// /
getProDetail();
} }
};
onMounted(init);
onUnmounted(() => {
counter = null;
submit = null;
pannelToggle = null;
setHeartbeatDetection = null;
getProDetail = null;
getCache = null;
getAssList = null;
getAssStatus = null;
getCompetitionStatus = null;
confirmSubmit = null;
handleCache = null;
getSumTime = null;
getEntryTime = null;
reloadCount = null;
delCache = null;
setNewProject = null;
reload = null;
reloadConfirm = null;
startCount = null;
setSubmit = null;
clearInterval(counterTimer.value);
clearInterval(heartBeatTimer.value);
console.log('onUnmounted');
}); });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.el-main { .el-main {
@apply w-[60%] p-0 mr-5 mb-2 ml-2 text-[#333] text-base whitespace-pre-wrap bg-white overflow-hidden; width: 60%;
background-color: #fff;
color: #333;
padding: 0;
font-size: 16px;
margin: 0px 20px 10px 10px;
white-space: pre-wrap;
overflow: hidden;
} }
.panel-header { .panel-header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
flex-wrap: wrap;
height: auto !important;
.project { .project {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
@ -909,7 +821,7 @@ onUnmounted(() => {
font-size: 14px; font-size: 14px;
} }
:deep(.select) { :deep(.select) {
@apply flex-1 min-w-[180px]; @apply flex-1;
.el-select__caret:before { .el-select__caret:before {
// content: '\e78f'; // content: '\e78f';
padding: 3px; padding: 3px;
@ -924,7 +836,7 @@ onUnmounted(() => {
.el-input { .el-input {
padding: 10px 0; padding: 10px 0;
} }
.el-input--suffix .el-input__wrapper { .el-input--suffix .el-input__inner {
height: 40px !important; height: 40px !important;
padding-right: 50px; padding-right: 50px;
margin-left: 15px; margin-left: 15px;
@ -936,7 +848,6 @@ onUnmounted(() => {
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
box-shadow: none;
} }
} }
.panel { .panel {
@ -983,14 +894,4 @@ onUnmounted(() => {
.ql-editor { .ql-editor {
@apply text-sm; @apply text-sm;
} }
@media (max-width: 1450px) {
.panel-header {
.actions {
z-index: 10;
position: absolute;
top: 70px;
right: 30px;
}
}
}
</style> </style>

@ -0,0 +1,59 @@
<template>
<form class="flex">
<div class="space-y-1">
<div v-for="(name, index) in names" :key="name" class="flex">
<el-button :icon="index == 0 ? Plus : Minus" @click="handelRow(index)" :disabled="index <= 0 && remains.length <= 0" circle></el-button>
<el-select v-model="names[index]" @change="clearParams()" class="w-36">
<el-option v-for="item in data.filter((it) => it.name === names[index] || remains.includes(it))" :key="item.name" :label="item.label" :value="item.name"></el-option>
</el-select>
<query-input :inputs="inputs" :name="names[index]"></query-input>
</div>
</div>
<div>
<el-button-group class="ml-2">
<el-button native-type="submit" :icon="Search" @click.prevent="$emit('search')">{{ $t('search') }}</el-button>
<el-button :icon="Refresh" @click="$emit('reset')">{{ $t('reset') }}</el-button>
</el-button-group>
</div>
</form>
</template>
<script setup lang="ts">
import { defineEmits, useSlots, provide, computed, ref, toRefs } from 'vue';
import { Plus, Minus, Search, Refresh } from '@element-plus/icons-vue';
import QueryInput from './QueryInput.vue';
const props = defineProps({ params: { type: Object, required: true } });
const { params } = toRefs(props);
const slots = useSlots();
provide('params', params);
defineEmits({
search: null,
reset: null,
});
const data = ref<any[]>([]);
const inputs = ref<any[]>([]);
inputs.value = slots.default?.() ?? [];
data.value = inputs.value.map((item) => ({ label: item.props?.label, name: item.props?.name }));
const [first] = data.value;
const names = ref<string[]>([first.name]);
const remains = computed(() => data.value.filter((it) => !names.value.includes(it.name)));
const clearParams = () => {
Object.keys(params.value).forEach((key) => {
if (!names.value.includes(key) && names.value.findIndex((item) => item.split(',').includes(key)) === -1) {
delete params.value[key];
}
});
};
const handelRow = (index: number) => {
if (index === 0) {
const [item] = remains.value;
names.value[names.value.length] = item.name;
} else {
names.value.splice(index, 1);
clearParams();
}
};
</script>

@ -0,0 +1,16 @@
<script lang="ts">
import { defineComponent, computed, toRefs } from 'vue';
export default defineComponent({
name: 'QueryInput',
props: { inputs: { type: Array, required: true }, name: { type: String, required: true } },
setup(props) {
const { inputs, name } = toRefs(props);
const input = computed(() => inputs.value.find((item: any) => item.props.name === name.value));
return { input };
},
render() {
return this.input;
},
});
</script>

@ -0,0 +1,64 @@
<template>
<slot>
<div v-if="type === 'number'"
class="inline-block">
<el-input-number v-model="params[first]"
:placeholder="$t('begin.number')"
class="w-48"></el-input-number>
<el-input-number v-model="params[second]"
:placeholder="$t('end.number')"
class="w-48"></el-input-number>
</div>
<el-date-picker v-else-if="type === 'date'"
v-model="params[name]"
type="daterange"
:start-placeholder="$t('begin.date')"
:end-placeholder="$t('end.date')"
class="w-96"></el-date-picker>
<el-date-picker v-else-if="type === 'datetime'"
v-model="params[name]"
type="datetimerange"
:start-placeholder="$t('begin.date')"
:end-placeholder="$t('end.date')"
class="w-96">
</el-date-picker>
<!--
<div v-else-if="type === 'date'" class="inline-block">
<el-date-picker v-model="params[first]" type="date" :placeholder="$t('begin.date')" class="w-48"></el-date-picker>
<el-date-picker v-model="params[second]" type="date" :placeholder="$t('end.date')" class="w-48"></el-date-picker>
</div>
<div v-else-if="type === 'datetime'" class="inline-block">
<el-date-picker v-model="params[first]" type="datetime" class="w-48"></el-date-picker>
<el-date-picker v-model="params[second]" type="datetime" class="w-48"></el-date-picker>
</div>
-->
<el-select v-else-if="options"
v-model="params[name]"
multiple
class="w-96">
<el-option v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"></el-option>
</el-select>
<el-input v-else
v-model="params[name]"
class="w-96"></el-input>
</slot>
</template>
<script setup lang="ts">
import { inject, PropType, ref, toRefs } from 'vue';
const props = defineProps({
label: { type: String, required: true },
name: { type: String, required: true },
// 'string' | 'date' | 'datetime' | 'number'
type: { type: String },
options: { type: Object as PropType<Array<{ label: string; value: string | number }>> },
});
const params = inject<any>('params');
const { name } = toRefs(props);
const [firstName, secondName] = name.value.split(',');
const first = ref<string>(firstName);
const second = ref<string>(secondName);
</script>

@ -0,0 +1,2 @@
export { default as QueryForm } from './QueryForm.vue';
export { default as QueryItem } from './QueryItem.vue';

@ -1,7 +1,12 @@
<template> <template>
<div class="search"> <div class="search">
<input type="text" placeholder="搜索" maxlength="20" v-model="val" /> <input type="text"
<img src="@/assets/images/search.png" alt="" class="icon" /> placeholder="搜索"
maxlength="20"
v-model="val" />
<img src="@/assets/images/search.png"
alt=""
class="icon" />
</div> </div>
</template> </template>

@ -1,34 +0,0 @@
<template>
<!-- 保存策略时的confirm询问 -->
<el-dialog v-model="visible" width="500px">
<p>修改后的策略同步已关联这条策略的贷款产品会导致关联产品下架并重新配置风控</p>
<p class="my-3 text-[#006BFF]">确定修改这条策略吗</p>
<el-checkbox v-model="syncCheck" label="策略修改同步之前关联的产品" />
<template #footer>
<div class="dialog-footer">
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="submit">确定</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue';
const props = defineProps({
modelValue: { type: Boolean, default: false },
});
const emit = defineEmits(['update:modelValue', 'submit']);
const visible = computed({
get: () => props.modelValue,
set: (value) => {
emit('update:modelValue', value);
},
});
const syncCheck = ref<boolean>(false);
const submit = () => {
visible.value = false;
emit('submit', +syncCheck.value);
};
</script>

@ -0,0 +1,45 @@
<script lang="ts">
import { computed, defineComponent, toRefs } from 'vue';
import { useI18n } from 'vue-i18n';
import { ColumnState, getColumnSettings, setColumnOrigins } from './useColumns';
export default defineComponent({
name: 'ColumnList',
props: { name: { type: String, required: true } },
setup(props, { slots }) {
const { name } = toRefs(props);
const { t } = useI18n();
const slotColumns = slots.default?.() ?? [];
//
const getColumnTitle = (columnProps: any) => {
// checkbox
if (columnProps?.type === 'selection') return t('table.selection');
return columnProps?.label;
};
// el-table-column
const origins: ColumnState[] = slotColumns.map((column) => ({ title: getColumnTitle(column.props), display: column.props?.display !== 'none' }));
setColumnOrigins(name.value, origins);
const settings = getColumnSettings(name.value);
const columns = computed(() =>
slotColumns
.filter((column) => {
const matched = settings.value.find((item) => getColumnTitle(column.props) === item.title);
return !matched || matched.display;
})
.map((column) => ({ ...column, key: getColumnTitle(column.props) }))
.sort((a, b) => {
let indexA = settings.value.findIndex((item) => item.title === getColumnTitle(a));
if (indexA < 0) indexA = slotColumns.findIndex((item) => getColumnTitle(item) === getColumnTitle(a));
let indexB = settings.value.findIndex((item) => item.title === getColumnTitle(b));
if (indexB < 0) indexB = slotColumns.findIndex((item) => getColumnTitle(item) === getColumnTitle(b));
return indexA - indexB;
}),
);
return { columns };
},
render() {
return this.columns;
},
});
</script>

@ -0,0 +1,40 @@
<template>
<el-dropdown class="align-middle"
trigger="click"
:hide-on-click="false">
<el-tooltip :content="$t('table.columnsSetting')"
placement="top">
<el-icon class="text-base">
<setting />
</el-icon>
</el-tooltip>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>
<el-button @click="resetColumns"
type="text">{{ $t('table.columnsReset') }}</el-button>
</el-dropdown-item>
<el-dropdown-item v-for="(column, index) in settings"
:key="column.title"
:divided="index === 0">
<el-checkbox v-model="column.display">{{ column.title }}</el-checkbox>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
<script setup lang="ts">
import { toRefs, watch } from 'vue';
import { Setting } from '@element-plus/icons-vue';
import { getColumnOrigins, getColumnSettings, mergeColumns, storeColumnSettings } from './useColumns';
const props = defineProps({ name: { type: String, required: true } });
const { name } = toRefs(props);
const settings = getColumnSettings(name.value);
const origins = getColumnOrigins(name.value);
watch(settings, () => storeColumnSettings(), { deep: true });
const resetColumns = () => {
settings.value = mergeColumns([], origins.value);
};
</script>

@ -0,0 +1,2 @@
export { default as ColumnSetting } from './ColumnSetting.vue';
export { default as ColumnList } from './ColumnList.vue';

@ -0,0 +1,55 @@
import { reactive, toRef } from 'vue';
export interface ColumnState {
title: string;
display: boolean;
}
const COLUMN_SETTINGS = 'ujcms_column_settings';
function fetchColumnSettings(): Record<string, ColumnState[]> {
const settings = localStorage.getItem(COLUMN_SETTINGS);
return settings ? JSON.parse(settings) : {};
}
const originStore: Record<string, ColumnState[]> = reactive({});
const settingStore: Record<string, ColumnState[]> = reactive(fetchColumnSettings());
export function storeColumnSettings() {
localStorage.setItem(COLUMN_SETTINGS, JSON.stringify(settingStore));
}
export const getColumnOrigins = (name: string) => {
if (!originStore[name]) originStore[name] = [];
return toRef(originStore, name);
};
export const mergeColumns = (settings: ColumnState[], origins: ColumnState[]) => {
// 去除不存在的列
for (let i = 0, len = settings.length; i < len; ) {
if (origins.findIndex((column) => column.title === settings[i].title) === -1) {
settings.splice(i, 1);
len -= 1;
} else {
i += 1;
}
}
// 增加未记录的列
origins.forEach((column) => {
if (settings.findIndex((item) => item.title === column.title) === -1) {
settings.push({ ...column });
}
});
return settings;
};
export const setColumnOrigins = (name: string, origins: ColumnState[]) => {
originStore[name] = origins;
if (!settingStore[name]) settingStore[name] = [];
const settings = settingStore[name];
mergeColumns(settings, origins);
};
export const getColumnSettings = (name: string) => {
if (!settingStore[name]) settingStore[name] = [];
return toRef(settingStore, name);
};
// export const setColumnSettings = (name: string, settings: ColumnState[]) => {
// settingStore[name] = settings;
// };

@ -1,11 +1,13 @@
<template> <template>
<div> <div>
<textarea :id="elementId" ref="element"></textarea> <textarea :id="elementId"
ref="element"></textarea>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, ref, toRefs, watch, onMounted, onBeforeUnmount, onActivated, onDeactivated, PropType } from 'vue'; import { defineComponent, ref, toRefs, watch, onMounted, onBeforeUnmount, onActivated, onDeactivated, PropType } from 'vue';
import { useI18n } from 'vue-i18n';
import { getAuthHeaders } from '@/utils/auth'; import { getAuthHeaders } from '@/utils/auth';
import { imageUploadUrl } from '@/api/system'; import { imageUploadUrl } from '@/api/system';
@ -54,8 +56,8 @@ import 'tinymce/plugins/visualblocks';
import 'tinymce/plugins/visualchars'; import 'tinymce/plugins/visualchars';
// import 'tinymce/plugins/wordcount'; // import 'tinymce/plugins/wordcount';
import Oss from '@/components/AliOss/upload.ts';
import { isTextarea, uuid, initEditor } from './utils'; import { isTextarea, uuid, initEditor } from './utils';
import Oss from '@/components/AliOss/upload.ts';
export default defineComponent({ export default defineComponent({
name: 'Tinymce', name: 'Tinymce',
@ -76,6 +78,7 @@ export default defineComponent({
}, },
setup(props, ctx) { setup(props, ctx) {
const { disabled, modelValue } = toRefs(props); const { disabled, modelValue } = toRefs(props);
const { t } = useI18n();
const element = ref<any>(); const element = ref<any>();
let vueEditor: any = null; let vueEditor: any = null;
const elementId: string = props.id || uuid('tiny-vue'); const elementId: string = props.id || uuid('tiny-vue');
@ -124,7 +127,7 @@ export default defineComponent({
images_upload_handler(blobInfo: any, success: any, failure: any, progress: any) { images_upload_handler(blobInfo: any, success: any, failure: any, progress: any) {
const fileSizeLimitByte = 0; const fileSizeLimitByte = 0;
if (fileSizeLimitByte > 0 && blobInfo.blob().size > fileSizeLimitByte) { if (fileSizeLimitByte > 0 && blobInfo.blob().size > fileSizeLimitByte) {
failure(`文件大小不能超过 ${`${fileSizeLimitByte / 1024 / 1024}MB`}`, { remove: true }); failure(t('error.fileMaxSize', { size: `${fileSizeLimitByte / 1024 / 1024}MB` }), { remove: true });
return; return;
} }
@ -207,7 +210,7 @@ export default defineComponent({
const file = files?.item(0); const file = files?.item(0);
if (!file) return; if (!file) return;
if (fileSizeLimtByte > 0 && file.size > fileSizeLimtByte) { if (fileSizeLimtByte > 0 && file.size > fileSizeLimtByte) {
tinymce.activeEditor.windowManager.alert(`文件大小不能超过 ${`${fileSizeLimtByte / 1024 / 1024}MB`}`); tinymce.activeEditor.windowManager.alert(t('error.fileMaxSize', { size: `${fileSizeLimtByte / 1024 / 1024}MB` }));
return; return;
} }
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();

@ -0,0 +1,118 @@
<template>
<el-upload
:action="action"
:headers="{ ...getAuthHeaders(), ...getSiteHeaders() }"
:accept="accept"
:before-upload="beforeUpload"
:on-progress="(event, file) => (progressFile = file)"
:show-file-list="false"
:disabled="disabled"
:multiple="multiple"
>
<!--
//
action="https://jsonplaceholder.typicode.com/posts/"
-->
<el-button type="primary">{{ $t('clickToUpload') }}</el-button>
</el-upload>
<el-progress v-if="progressFile.status === 'uploading'" :percentage="parseInt(progressFile.percentage, 10)"></el-progress>
</template>
<script lang="ts">
import { defineComponent, onMounted, ref, toRefs, computed } from 'vue';
import { ElMessage } from 'element-plus';
import { useI18n } from 'vue-i18n';
import { getAuthHeaders } from '@/utils/auth';
import { getSiteHeaders } from '@/utils/common';
import { imageUploadUrl, videoUploadUrl, docUploadUrl, fileUploadUrl, queryGlobalSettings } from '@/api/config';
export default defineComponent({
name: 'BaseUpload',
props: {
type: {
type: String,
default: 'file',
validator: (value: string) => ['image', 'video', 'doc', 'file'].includes(value),
},
uploadAction: { type: String },
fileAccept: { type: String },
fileMaxSize: { type: Number },
multiple: { type: Boolean },
disabled: { type: Boolean, default: false },
'on-success': { type: Function },
},
setup(props) {
const { type, uploadAction, fileAccept, fileMaxSize } = toRefs(props);
const { t } = useI18n();
const progressFile = ref<any>({});
const global = ref<any>();
const fetchGlobalSettings = async () => {
global.value = await queryGlobalSettings();
};
onMounted(() => {
fetchGlobalSettings();
});
const action = computed(() => {
if (uploadAction?.value != null) {
return uploadAction.value;
}
switch (type.value) {
case 'image':
return imageUploadUrl;
case 'video':
return videoUploadUrl;
case 'doc':
return docUploadUrl;
case 'file':
return fileUploadUrl;
default:
throw new Error(`Type not support: ${type.value}`);
}
});
const accept = computed(() => {
if (fileAccept?.value != null) {
return fileAccept.value;
}
switch (type.value) {
case 'image':
return global?.value?.upload?.imageInputAccept ?? '.jpg,.jpeg,.png,.gif';
case 'video':
return global?.value?.upload?.videoInputAccept ?? '.mp4,.m3u8';
case 'doc':
return global?.value?.upload?.docInputAccept ?? '.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx';
case 'file':
return global?.value?.upload?.fileInputAccept ?? '.zip,.7z,.gz,.bz2,.iso,.rar,.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.jpg,.jpeg,.png,.gif,.mp4,.m3u8,.mp3,.ogg';
default:
throw new Error(`Type not support: ${type.value}`);
}
});
const maxSize = computed(() => {
if (fileMaxSize?.value != null) {
return fileMaxSize.value;
}
switch (type.value) {
case 'image':
return global?.value?.upload?.imageLimitByte ?? 0;
case 'video':
return global?.value?.upload?.videoLimitByte ?? 0;
case 'doc':
return global?.value?.upload?.docLimitByte ?? 0;
case 'file':
return global?.value?.upload?.fileLimitByte ?? 0;
default:
throw new Error(`Type not support: ${type.value}`);
}
});
const beforeUpload = (file: any) => {
if (maxSize.value > 0 && file.size > maxSize.value) {
ElMessage.error(t('error.fileMaxSize', { size: `${maxSize.value / 1024 / 1024}MB` }));
return false;
}
return true;
};
return { progressFile, getAuthHeaders, getSiteHeaders, action, accept, beforeUpload };
},
});
</script>
<style lang="scss" scoped></style>

@ -0,0 +1,136 @@
<template>
<div class="w-full">
<el-upload :action="fileUploadUrl"
:headers="{ ...getAuthHeaders(), ...getSiteHeaders() }"
:accept="accept"
:before-upload="beforeUpload"
:on-success="(res) => fileList.push({ name: res.name, url: res.url, length: res.size })"
:on-progress="(event, file) => (progressFile = file)"
:show-file-list="false"
multiple>
<!--
action="https://jsonplaceholder.typicode.com/posts/"
-->
<el-button type="primary">{{ $t('clickToUpload') }}</el-button>
</el-upload>
<el-progress v-if="progressFile.status === 'uploading'"
:percentage="parseInt(progressFile.percentage, 10)"></el-progress>
<transition-group tag="ul"
:class="['el-upload-list', 'el-upload-list--text', { 'is-disabled': disabled }]"
name="el-list">
<li v-for="file in fileList"
:key="file.url"
class="el-upload-list__item is-success">
<a class="el-upload-list__item-name"
@click="handlePreview(file)">
<el-icon class="el-icon--document">
<Document />
</el-icon>{{ file.name }}
</a>
<label class="el-upload-list__item-status-label">
<el-icon class="el-icon--upload-success el-icon--circle-check">
<CircleCheck />
</el-icon>
</label>
<el-icon v-if="!disabled"
class="el-icon--close"
@click="fileList.splice(fileList.indexOf(file), 1)">
<Close />
</el-icon>
</li>
</transition-group>
<el-dialog :title="$t('article.fileList.attribute')"
v-model="previewVisible"
top="5vh"
:width="768"
append-to-body>
<el-form ref="form"
:model="previewFile"
label-width="150px">
<el-form-item prop="name"
:label="$t('name')"
:rules="{ required: true, message: () => $t('v.required') }">
<el-input v-model="previewFile.name"
maxlength="100"></el-input>
</el-form-item>
<el-form-item prop="length"
:label="$t('size')"
:rules="{ required: true, message: () => $t('v.required') }">
<el-input v-model="previewFile.length"
maxlength="19">
<template #append>Byte</template>
</el-input>
</el-form-item>
<el-form-item prop="url"
label="URL"
:rules="{ required: true, message: () => $t('v.required') }">
<el-input v-model="previewFile.url"
maxlength="255"></el-input>
</el-form-item>
<el-button @click.prevent="handleSubmit()"
type="primary"
native-type="submit">{{ $t('submit') }}</el-button>
</el-form>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref, toRefs, computed } from 'vue';
import { ElMessage } from 'element-plus';
import { Close, Document, CircleCheck } from '@element-plus/icons-vue';
import { useI18n } from 'vue-i18n';
import { getAuthHeaders } from '@/utils/auth';
import { getSiteHeaders } from '@/utils/common';
import { fileUploadUrl, queryGlobalSettings } from '@/api/config';
const props = defineProps({
modelValue: { type: Array, default: () => [] },
fileAccept: { type: String },
fileMaxSize: { type: Number },
disabled: { type: Boolean, default: false },
});
const emit = defineEmits({ 'update:modelValue': null });
const { fileAccept, fileMaxSize } = toRefs(props);
const { t } = useI18n();
const { modelValue } = toRefs(props);
const progressFile = ref<any>({});
const fileList = computed({
get: (): any[] => modelValue.value,
set: (val) => emit('update:modelValue', val),
});
const previewVisible = ref<boolean>(false);
const previewFile = ref<any>({});
const form = ref<any>();
const handlePreview = (file: any) => {
previewFile.value = file;
previewVisible.value = true;
};
const handleSubmit = () => {
form.value.validate(async (valid: boolean) => {
if (!valid) return;
previewVisible.value = false;
});
};
const global = ref<any>();
const fetchGlobalSettings = async () => {
global.value = await queryGlobalSettings();
};
onMounted(() => {
fetchGlobalSettings();
});
const defaultAccept = '.zip,.7z,.gz,.bz2,.iso,.rar,.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.jpg,.jpeg,.png,.gif,.mp4,.m3u8,.mp3,.ogg';
const accept = computed(() => fileAccept?.value ?? global?.value?.upload?.fileInputAccept ?? defaultAccept);
const maxSize = computed(() => fileMaxSize?.value ?? global?.value?.upload?.fileLimitByte ?? 0);
const beforeUpload = (file: any) => {
if (maxSize.value > 0 && file.size > maxSize.value) {
ElMessage.error(t('error.fileMaxSize', { size: `${maxSize.value / 1024 / 1024}MB` }));
return false;
}
return true;
};
</script>
<style lang="scss" scoped></style>

@ -0,0 +1,84 @@
<template>
<el-dialog :title="$t('imageCrop')" v-model="visible" @closed="destroyCropper()" top="5vh" :width="768" destroy-on-close append-to-body>
<div class="text-center">
<img ref="imgRef" @load="initCropper()" :src="src" alt="" class="inline" style="max-height:410px" />
</div>
<div class="text-right">
<el-button @click.prevent="handleSubmit()" type="primary" native-type="submit" class="mt-4">{{ $t('submit') }}</el-button>
</div>
</el-dialog>
</template>
<script lang="ts">
import { computed, defineComponent, ref, toRefs } from 'vue';
import Cropper from 'cropperjs';
import 'cropperjs/dist/cropper.css';
import { cropImage } from '@/api/config';
export default defineComponent({
name: 'ImageCropper',
props: {
modelValue: { type: Boolean, required: true },
src: { type: String, default: null },
width: { type: Number },
height: { type: Number },
thumbnailWidth: { type: Number },
thumbnailHeight: { type: Number },
},
emits: { 'update:modelValue': null, success: null },
setup(props, { emit }) {
const { modelValue, src, width, height, thumbnailWidth, thumbnailHeight } = toRefs(props);
const visible = computed({
get: () => modelValue.value,
set: (val) => emit('update:modelValue', val),
});
const imgRef = ref<any>();
const cropper = ref<any>();
const cropParam = ref<any>({});
const initCropper = () => {
if (imgRef.value) {
cropper.value = new Cropper(imgRef.value, {
aspectRatio: width?.value && height?.value ? width.value / height.value : NaN,
autoCropArea: width?.value && height?.value ? 1 : 0.8,
viewMode: 1,
minCropBoxWidth: width?.value ?? 16,
minCropBoxHeight: height?.value ?? 16,
zoomable: false,
crop(event) {
cropParam.value.url = src.value;
cropParam.value.x = Math.floor(event.detail.x);
cropParam.value.y = Math.floor(event.detail.y);
cropParam.value.width = Math.floor(event.detail.width);
cropParam.value.height = Math.floor(event.detail.height);
cropParam.value.maxWidth = width?.value;
cropParam.value.maxHeight = height?.value;
cropParam.value.thumbnailWidth = thumbnailWidth?.value;
cropParam.value.thumbnailHeight = thumbnailHeight?.value;
},
});
}
};
const destroyCropper = () => {
if (cropper.value) {
cropper.value.destroy();
}
};
const handleSubmit = async () => {
visible.value = false;
emit('success', (await cropImage(cropParam.value)).url);
};
return { imgRef, visible, initCropper, destroyCropper, handleSubmit };
},
});
</script>
<style lang="scss" scoped>
/* Ensure the size of the image fit the container perfectly */
:deep(img) {
display: block;
/* This rule is very important, please don't ignore this */
max-width: 100%;
}
</style>

@ -0,0 +1,157 @@
<template>
<div>
<!-- <transition-group tag="ul" :class="['el-upload-list', 'el-upload-list--picture-card', { 'is-disabled': disabled }]" name="el-list"> -->
<ul :class="['el-upload-list', 'el-upload-list--picture-card', { 'is-disabled': disabled }]">
<li v-for="file in fileList"
:key="file.url"
class="el-upload-list__item is-success">
<div class="w-full h-full bg-gray-50 flex justify-center items-center">
<img class="max-w-full max-h-full block"
:src="file.url"
alt="" />
<div class="full-flex-center absolute rounded-md cursor-default bg-black bg-opacity-50 opacity-0 hover:opacity-100 space-x-4"
@click.stop>
<el-icon class="image-action"
@click="(cropperVisible = true), (currentFile = file)"
:title="$t('cropImage')">
<Crop />
</el-icon>
<el-icon class="image-action"
@click="handlePreview(file)"
:title="$t('previewImage')">
<View />
</el-icon>
<el-icon class="image-action"
@click="fileList.splice(fileList.indexOf(file), 1)"
:title="$t('deleteImage')">
<Delete />
</el-icon>
</div>
</div>
</li>
</ul>
<!-- </transition-group> -->
<el-upload :action="imageUploadUrl"
:headers="{ ...getAuthHeaders(), ...getSiteHeaders() }"
:data="getData()"
:accept="accept"
:before-upload="beforeUpload"
:on-success="(res, file) => fileList.push({ name: res.name, url: res.url })"
:on-progress="(event, file) => (progressFile = file)"
:show-file-list="false"
multiple
class="inline-block">
<el-progress v-if="progressFile.status === 'uploading'"
type="circle"
:percentage="parseInt(progressFile.percentage, 10)" />
<div v-else
class="el-upload--picture-card">
<el-icon>
<Plus />
</el-icon>
</div>
</el-upload>
<div>
<el-dialog v-model="previewVisible"
top="5vh"
:width="768">
<el-input v-model="previewFile.url"
maxlength="255">
<template #prepend>URL</template>
</el-input>
<el-input v-model="previewFile.description"
type="textarea"
:rows="2"
:placeholder="$t('article.imageList.description')"
class="mt-1"></el-input>
<img :src="previewFile.url"
alt=""
class="mt-1 border border-gray-300" />
</el-dialog>
</div>
<image-cropper v-model="cropperVisible"
:src="currentFile.url"
:thumbnailWidth="thumbnailWidth"
:thumbnailHeight="thumbnailHeight"
@success="(url) => (currentFile.url = url)"></image-cropper>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref, toRefs, computed } from 'vue';
import { ElMessage } from 'element-plus';
import { Plus, Crop, View, Delete } from '@element-plus/icons-vue';
import { useI18n } from 'vue-i18n';
import { getAuthHeaders } from '@/utils/auth';
import { getSiteHeaders } from '@/utils/common';
import { imageUploadUrl, queryGlobalSettings } from '@/api/config';
import ImageCropper from './ImageCropper.vue';
const props = defineProps({
modelValue: { type: Array, default: () => [] },
fileAccept: { type: String },
fileMaxSize: { type: Number },
maxWidth: { type: Number },
maxHeight: { type: Number },
disabled: { type: Boolean, default: false },
});
const emit = defineEmits({ 'update:modelValue': null });
const { modelValue, maxWidth, maxHeight, fileAccept, fileMaxSize } = toRefs(props);
const { t } = useI18n();
const progressFile = ref<any>({});
const currentFile = ref<any>({});
const previewVisible = ref<boolean>(false);
const cropperVisible = ref<boolean>(false);
const previewFile = ref<any>({ src: 'data:;base64,=' });
const fileList = computed({
get: (): any => modelValue.value,
set: (val) => emit('update:modelValue', val),
});
const handlePreview = (file: any) => {
previewFile.value = file;
previewVisible.value = true;
};
const thumbnailWidth = 300;
const thumbnailHeight = 300;
const getData = () => {
const data: any = { isWatermark: true, thumbnailWidth, thumbnailHeight };
if (maxWidth?.value != null) {
data.maxWidth = maxWidth.value;
}
if (maxHeight?.value != null) {
data.maxHeight = maxHeight.value;
}
return data;
};
const global = ref<any>();
const fetchGlobalSettings = async () => {
global.value = await queryGlobalSettings();
};
onMounted(() => {
fetchGlobalSettings();
});
const defaultAccept = 'image/jpg,image/jpeg,image/png,image/gif';
const accept = computed(() => fileAccept?.value ?? global?.value?.upload?.imageInputAccept ?? defaultAccept);
const maxSize = computed(() => fileMaxSize?.value ?? global?.value?.upload?.imageLimitByte ?? 0);
const beforeUpload = (file: any) => {
if (maxSize.value > 0 && file.size > maxSize.value) {
ElMessage.error(t('error.fileMaxSize', { size: `${maxSize.value / 1024 / 1024}MB` }));
return false;
}
return true;
};
</script>
<style lang="scss" scoped>
:deep(.el-dialog__headerbtn) {
top: 4px;
}
.full-flex-center {
@apply w-full h-full flex justify-center items-center;
}
.image-action {
@apply cursor-pointer text-xl text-white;
}
</style>

@ -0,0 +1,149 @@
<template>
<el-upload :action="imageUploadUrl"
:headers="{ ...getAuthHeaders(), ...getSiteHeaders() }"
:accept="accept"
:before-upload="beforeUpload"
:data="data"
:show-file-list="false"
:on-success="(res) => ((src = res.url), (cropperVisible = mode === 'manual'))"
:on-progress="(event, file) => (progressFile = file)">
<!--
//
action="https://jsonplaceholder.typicode.com/posts/"
-->
<div v-if="src"
class="full-flex-center rounded-border relative hover:border-opacity-0">
<img :src="src"
class="max-w-full max-h-full block" />
<div class="full-flex-center absolute rounded-md cursor-default bg-black bg-opacity-50 opacity-0 hover:opacity-100 space-x-4"
@click.stop>
<el-icon class="image-action"
@click="cropperVisible = true"
:title="$t('cropImage')">
<Crop />
</el-icon>
<el-icon class="image-action"
@click="previewVisible = true"
:title="$t('previewImage')">
<View />
</el-icon>
<el-icon class="image-action"
@click="src = undefined"
:title="$t('deleteImage')">
<Delete />
</el-icon>
</div>
</div>
<el-progress v-else-if="progressFile.status === 'uploading'"
type="circle"
:percentage="parseInt(progressFile.percentage, 10)" />
<div v-else
class="el-upload--picture-card">
<el-icon>
<plus />
</el-icon>
</div>
</el-upload>
<div>
<el-dialog v-model="previewVisible"
top="5vh"
:width="768"
append-to-body
destroy-on-close>
<el-input v-model="src">
<template #prepend>URL</template>
</el-input>
<img :src="src"
alt=""
class="mt-1 border border-gray-300" />
</el-dialog>
</div>
<image-cropper v-model="cropperVisible"
:src="src"
:width="width"
:height="height"
@success="(url) => (src = url)"></image-cropper>
</template>
<script setup lang="ts">
import { computed, onMounted, ref, toRefs } from 'vue';
import { ElMessage } from 'element-plus';
import { Plus, Crop, View, Delete } from '@element-plus/icons-vue';
import { useI18n } from 'vue-i18n';
import { getAuthHeaders } from '@/utils/auth';
import { getSiteHeaders } from '@/utils/common';
import { imageUploadUrl, queryGlobalSettings } from '@/api/config';
import ImageCropper from './ImageCropper.vue';
// 'image/jpg,image/jpeg,image/png,image/gif'
const props = defineProps({
modelValue: { type: String, default: null },
fileAccept: { type: String },
fileMaxSize: { type: Number },
width: { type: Number },
height: { type: Number },
mode: { type: String, default: 'none' },
});
const emit = defineEmits({ 'update:modelValue': null });
const { modelValue, width, height, mode, fileAccept, fileMaxSize } = toRefs(props);
const { t } = useI18n();
const progressFile = ref<any>({});
const previewVisible = ref<boolean>(false);
const cropperVisible = ref<boolean>(false);
const src = computed({
get: (): string | undefined => modelValue.value,
set: (val: string | undefined) => emit('update:modelValue', val),
});
const resizable = computed(() => ['cut', 'resize'].includes(mode.value));
const data = computed(() => {
const params: any = { resizeMode: mode.value === 'cut' ? 'cut' : 'normal' };
if (width?.value != null) {
// 0
params.maxWidth = resizable.value ? width.value : 0;
}
if (height?.value != null) {
// 0
params.maxHeight = resizable.value ? height.value : 0;
}
return params;
});
const global = ref<any>();
const fetchGlobalSettings = async () => {
global.value = await queryGlobalSettings();
};
onMounted(() => {
fetchGlobalSettings();
});
const accept = computed(() => fileAccept?.value ?? global?.value?.upload?.imageInputAccept ?? 'image/jpg,image/jpeg,image/png,image/gif');
const maxSize = computed(() => fileMaxSize?.value ?? global?.value?.upload?.imageLimitByte ?? 0);
const beforeUpload = (file: any) => {
if (maxSize.value > 0 && file.size > maxSize.value) {
ElMessage.error(t('error.fileMaxSize', { size: `${maxSize.value / 1024 / 1024}MB` }));
return false;
}
return true;
};
</script>
<style lang="scss" scoped>
:deep(.el-dialog__headerbtn) {
top: 4px;
}
:deep(.el-upload) {
width: 148px;
height: 148px;
}
.full-flex-center {
@apply w-full h-full flex justify-center items-center;
}
.rounded-border {
border: 1px solid #c0ccda;
@apply rounded-md bg-gray-50;
}
.image-action {
@apply cursor-pointer text-xl text-white;
}
</style>

@ -0,0 +1,4 @@
export { default as ImageUpload } from './ImageUpload.vue';
export { default as ImageListUpload } from './ImageListUpload.vue';
export { default as FileListUpload } from './FileListUpload.vue';
export { default as BaseUpload } from './BaseUpload.vue';

@ -0,0 +1,335 @@
import i18n from '@/i18n';
export function getPermsTreeData(): any[] {
const {
global: { t },
} = i18n;
return [
{
label: t('menu.home'),
key: 'home',
perms: ['auth'],
children: [
{
label: t('menu.personal'),
key: 'personal',
children: [
{
label: t('menu.personal.password'),
key: 'password:update',
perms: ['password:update'],
},
],
},
{
label: t('menu.content'),
key: 'content',
children: [
{
label: t('menu.content.article'),
key: 'article',
perms: ['article:page', 'article:list', 'channel:list', 'dict:list', 'model:list'],
children: [
{ label: t('list'), key: 'article:page' },
{ label: t('add'), key: 'article:create', perms: ['article:create'] },
{ label: t('edit'), key: 'article:update', perms: ['article:update', 'article:show'] },
{ label: t('delete'), key: 'article:delete', perms: ['article:delete'] },
],
},
{
label: t('menu.content.channel'),
key: 'channel',
perms: ['channel:page', 'channel:list'],
children: [
{ label: t('list'), key: 'channel:page' },
{ label: t('add'), key: 'channel:create', perms: ['channel:create'] },
{ label: t('edit'), key: 'channel:update', perms: ['channel:update', 'channel:show'] },
{ label: t('delete'), key: 'channel:delete', perms: ['channel:delete'] },
],
},
{
label: t('menu.content.blockItem'),
key: 'blockItem',
perms: ['blockItem:page', 'blockItem:list', 'block:list'],
children: [
{ label: t('list'), key: 'blockItem:page' },
{ label: t('add'), key: 'blockItem:create', perms: ['blockItem:create'] },
{ label: t('edit'), key: 'blockItem:update', perms: ['blockItem:update', 'blockItem:show'] },
{ label: t('delete'), key: 'blockItem:delete', perms: ['blockItem:delete'] },
],
},
{
label: t('menu.content.attachment'),
key: 'attachment',
perms: ['attachment:page', 'attachment:list'],
children: [
{ label: t('list'), key: 'attachment:page' },
{ label: t('add'), key: 'attachment:create', perms: ['attachment:create'] },
{ label: t('edit'), key: 'attachment:update', perms: ['attachment:update', 'attachment:show'] },
{ label: t('delete'), key: 'attachment:delete', perms: ['attachment:delete'] },
],
},
{
label: t('menu.content.generator'),
key: 'generator',
perms: ['generator:show', 'siteSettings:html:show', 'task:list', 'task:show', 'task:delete'],
children: [
{ label: t('generator.op.fulltext.reindexAll'), key: 'generator:fulltext:reindexAll', perms: ['generator:fulltext:reindexAll'] },
{ label: t('generator.op.fulltext.reindexSite'), key: 'generator:fulltext:reindexSite', perms: ['generator:fulltext:reindexSite'] },
{ label: t('generator.html'), key: 'generator:html', perms: ['generator:html'] },
{ label: t('site.settings.html'), key: 'siteSettings:html:update', perms: ['siteSettings:html:update', 'generator:html'] },
],
},
],
},
{
label: t('menu.config'),
key: 'config',
children: [
{
label: t('menu.config.globalSettings'),
key: 'globalSettings',
perms: ['globalSettings:show'],
children: [
{ label: t('global.settings.base'), key: 'globalSettings:base:update', perms: ['globalSettings:base:update'] },
{ label: t('global.settings.upload'), key: 'globalSettings:upload:update', perms: ['globalSettings:upload:update'] },
{ label: t('global.settings.customs'), key: 'globalSettings:customs:update', perms: ['globalSettings:customs:update'] },
],
},
{
label: t('menu.config.siteSettings'),
key: 'siteSettings',
perms: ['siteSettings:show'],
children: [
{ label: t('site.settings.base'), key: 'siteSettings:base:update', perms: ['siteSettings:base:update'] },
{ label: t('site.settings.watermark'), key: 'siteSettings:watermark:update', perms: ['siteSettings:watermark:update'] },
{ label: t('site.settings.customs'), key: 'siteSettings:customs:update', perms: ['siteSettings:customs:update'] },
],
},
{
label: t('menu.config.model'),
key: 'model',
perms: ['model:page', 'model:list'],
children: [
{ label: t('list'), key: 'model:page' },
{ label: t('add'), key: 'model:create', perms: ['model:create'] },
{ label: t('edit'), key: 'model:update', perms: ['model:update', 'model:show'] },
{ label: t('delete'), key: 'model:delete', perms: ['model:delete'] },
],
},
{
label: t('menu.config.block'),
key: 'block',
perms: ['block:page', 'block:list'],
children: [
{ label: t('list'), key: 'block:page' },
{ label: t('add'), key: 'block:create', perms: ['block:create'] },
{ label: t('edit'), key: 'block:update', perms: ['block:update', 'block:show'] },
{ label: t('delete'), key: 'block:delete', perms: ['block:delete'] },
],
},
{
label: t('menu.config.dictType'),
key: 'dictType',
perms: ['dictType:page', 'dictType:list'],
children: [
{ label: t('list'), key: 'dictType:page' },
{ label: t('add'), key: 'dictType:create', perms: ['dictType:create'] },
{ label: t('edit'), key: 'dictType:update', perms: ['dictType:update', 'dictType:show'] },
{ label: t('delete'), key: 'dictType:delete', perms: ['dictType:delete'] },
],
},
{
label: t('menu.config.dict'),
key: 'dict',
perms: ['dict:page', 'dict:list', 'dictType:list'],
children: [
{ label: t('list'), key: 'dict:page' },
{ label: t('add'), key: 'dict:create', perms: ['dict:create'] },
{ label: t('edit'), key: 'dict:update', perms: ['dict:update', 'dict:show'] },
{ label: t('delete'), key: 'dict:delete', perms: ['dict:delete'] },
],
},
],
},
{
label: t('menu.user'),
key: 'user',
children: [
{
label: t('menu.user.user'),
key: 'user',
perms: ['user:page', 'user:list', 'group:list', 'org:list'],
children: [
{ label: t('list'), key: 'user:page' },
{ label: t('add'), key: 'user:create', perms: ['user:create'] },
{ label: t('edit'), key: 'user:update', perms: ['user:update', 'user:show'] },
{ label: t('delete'), key: 'user:delete', perms: ['user:delete'] },
],
},
{
label: t('menu.user.role'),
key: 'role',
perms: ['role:page', 'role:list'],
children: [
{ label: t('list'), key: 'role:page' },
{ label: t('add'), key: 'role:create', perms: ['role:create'] },
{ label: t('edit'), key: 'role:update', perms: ['role:update', 'role:show'] },
{ label: t('delete'), key: 'role:delete', perms: ['role:delete'] },
],
},
{
label: t('menu.user.group'),
key: 'group',
perms: ['group:page', 'group:list'],
children: [
{ label: t('list'), key: 'group:page' },
{ label: t('add'), key: 'group:create', perms: ['group:create'] },
{ label: t('edit'), key: 'group:update', perms: ['group:update', 'group:show'] },
{ label: t('delete'), key: 'group:delete', perms: ['group:delete'] },
],
},
{
// label: t('menu.user.org'),
key: 'org',
perms: ['org:page', 'org:list'],
children: [
{ label: t('list'), key: 'org:page' },
{ label: t('add'), key: 'org:create', perms: ['org:create'] },
{ label: t('edit'), key: 'org:update', perms: ['org:update', 'org:show'] },
{ label: t('delete'), key: 'org:delete', perms: ['org:delete'] },
],
},
],
},
{
label: t('menu.system'),
key: 'system',
children: [
{
// label: t('menu.system.site'),
key: 'site',
perms: ['site:page', 'site:list', 'org:list', 'model:list', 'storage:list'],
children: [
{ label: t('list'), key: 'site:page' },
{ label: t('add'), key: 'site:create', perms: ['site:create'] },
{ label: t('edit'), key: 'site:update', perms: ['site:update', 'site:show'] },
{ label: t('delete'), key: 'site:delete', perms: ['site:delete'] },
],
},
{
label: t('menu.system.storage'),
key: 'storage',
perms: ['storage:page', 'storage:list'],
children: [
{ label: t('list'), key: 'storage:page' },
{ label: t('add'), key: 'storage:create', perms: ['storage:create'] },
{ label: t('edit'), key: 'storage:update', perms: ['storage:update', 'storage:show'] },
{ label: t('delete'), key: 'storage:delete', perms: ['storage:delete'] },
],
},
// {
// label: t('menu.system.task'),
// key: 'task',
// perms: ['task:page', 'task:list'],
// children: [
// { label: t('list'), key: 'task:page' },
// { label: t('add'), key: 'task:create', perms: ['task:create'] },
// { label: t('edit'), key: 'task:update', perms: ['task:update', 'task:show'] },
// { label: t('delete'), key: 'task:delete', perms: ['task:delete'] },
// ],
// },
],
},
],
},
];
}
export function getModelData(): any {
return {
article: {
mains: [
{ code: 'title', must: true, show: true, double: false, required: true },
{ code: 'subtitle', must: false, show: false, double: false, required: false },
{ code: 'fullTitle', must: false, show: false, double: false, required: false },
{ code: 'linkUrl', must: false, show: true, double: false, required: false },
{ code: 'seoKeywords', must: false, show: false, double: false, required: false },
{ code: 'seoDescription', must: false, show: true, double: false, required: false },
{ code: 'author', must: false, show: false, double: true, required: false },
{ code: 'editor', must: false, show: false, double: true, required: false },
{ code: 'image', must: false, show: true, double: false, required: false, type: 'image', imageWidth: 300, imageHeight: 200, imageMode: 'manual' },
{ code: 'file', must: false, show: false, double: false, required: false },
{ code: 'video', must: false, show: false, double: false, required: false },
{ code: 'doc', must: false, show: false, double: false, required: false },
{ code: 'imageList', must: false, show: false, double: false, required: false, type: 'imageList', imageMaxWidth: 1920, imageMaxHeight: 1920 },
{ code: 'fileList', must: false, show: false, double: false, required: false },
{ code: 'text', must: false, show: true, double: false, required: true },
],
asides: [
{ code: 'channel', must: true, show: true, required: true },
{ code: 'org', must: false, show: true, required: true },
{ code: 'publishDate', must: true, show: true, required: true },
// { code: 'offlineDate', must: false, show: true, required: false },
{ code: 'source', must: false, show: true, required: false },
{ code: 'articleTemplate', must: false, show: true, required: false },
{ code: 'allowComment', must: false, show: true, required: true },
{ code: 'user', must: false, show: false, required: true },
{ code: 'created', must: false, show: false, required: true },
{ code: 'modifiedUser', must: false, show: false, required: false },
{ code: 'modified', must: false, show: false, required: false },
],
},
channel: {
mains: [
{ code: 'name', must: true, show: true, double: true, required: true },
{ code: 'alias', must: true, show: true, double: true, required: true },
{ code: 'linkUrl', must: true, show: true, double: false, required: true },
{ code: 'seoTitle', must: false, show: true, double: true, required: false },
{ code: 'seoKeywords', must: false, show: true, double: true, required: false },
{ code: 'seoDescription', must: false, show: true, double: false, required: false },
{ code: 'channelTemplate', must: false, show: true, double: true, required: true },
{ code: 'articleTemplate', must: false, show: true, double: true, required: true },
{ code: 'channelModel', must: true, show: true, double: true, required: true },
{ code: 'articleModel', must: true, show: true, double: true, required: true },
{ code: 'group', must: false, show: true, double: false, required: true },
{ code: 'nav', must: false, show: true, double: true, required: true },
{ code: 'allowComment', must: false, show: true, double: true, required: true },
{ code: 'allowContribute', must: false, show: true, double: true, required: true },
{ code: 'allowSearch', must: false, show: true, double: true, required: true },
{ code: 'text', must: false, show: false, double: false, required: false },
],
asides: [
{ code: 'type', must: true, show: true, required: true },
{ code: 'parent', must: true, show: true, required: false },
{ code: 'pageSize', must: true, show: true, required: true },
],
},
};
}
export function mergeModelFields(defaultFields: any[], s: string | null | undefined, type: string): any[] {
const fields = JSON.parse(s || '[]');
const defaults = defaultFields.map((item: any) => ({ ...item, label: `${type}.${item.code}` }));
// 去除默认字段中不存在的字段
fields.filter((field: any) => defaults.findIndex((item) => item.code === field.code) !== -1);
defaults.forEach((item) => {
const index = fields.findIndex((it: any) => it.code === item.code);
if (index !== -1) {
// 加上缺失属性,覆盖不可改属性
fields[index] = { ...item, ...fields[index], must: item.must, label: item.label, type: item.type };
} else {
// 加上没有的字段
fields.push(item);
}
});
return fields;
}
export function arr2obj(arr: any[]) {
const obj: Record<string, any> = {};
arr.forEach((item: any) => {
obj[item.code] = item;
});
return obj;
}

@ -1,12 +1,31 @@
<template> <template>
<div class="width-[100%] flex justify-between items-center w-full py-6 lg:py-4 px-5 overflow-hidden bg-transparent"> <div class="width-[100%] flex justify-between items-center w-full py-6 px-5 overflow-hidden bg-transparent">
<logo /> <logo :collapse="collapse" />
<div v-if="!hidePanel" class="inline-flex items-center"> <div v-if="!hidePanel"
<el-tooltip effect="light" content="退出实训" placement="bottom"> class="inline-flex items-center">
<img class="mr-3 cursor-pointer" src="@/assets/images/11.png" alt="" @click="logout" /> <el-tooltip effect="light"
content="退出实训"
placement="bottom">
<img class="mr-3 cursor-pointer"
src="@/assets/images/11.png"
alt=""
@click="logout" />
</el-tooltip> </el-tooltip>
<el-tooltip effect="light" content="返回选择角色" placement="bottom"> <el-tooltip effect="light"
<img class="cursor-pointer" src="@/assets/images/4.png" alt="" @click="toRole" /> content="返回关卡"
placement="bottom">
<img class="mr-3 cursor-pointer"
src="@/assets/images/2.png"
alt=""
@click="toLevel" />
</el-tooltip>
<el-tooltip effect="light"
content="返回选择角色"
placement="bottom">
<img class="cursor-pointer"
src="@/assets/images/4.png"
alt=""
@click="toRole" />
</el-tooltip> </el-tooltip>
</div> </div>
</div> </div>
@ -16,7 +35,7 @@
import { ref, onMounted, computed } from 'vue'; import { ref, onMounted, computed } from 'vue';
import { useRouter, useRoute } from 'vue-router'; import { useRouter, useRoute } from 'vue-router';
import Settings from '@/settings'; import Settings from '@/settings';
import { logout } from '@/store/useCurrentUser'; import { currentUser, perm, logout } from '@/store/useCurrentUser';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import Logo from './Logo.vue'; import Logo from './Logo.vue';

@ -1,7 +1,8 @@
<template> <template>
<section class="px-3"> <section class="px-3">
<router-view v-slot="{ Component }"> <router-view v-slot="{ Component }">
<transition name="fade-transform" mode="out-in"> <transition name="fade-transform"
mode="out-in">
<component :is="Component" /> <component :is="Component" />
</transition> </transition>
</router-view> </router-view>

@ -1,75 +1,125 @@
<template> <template>
<ul class="switch px-7 lg:px-2"> <ul class="switch px-7">
<!-- 产品经理 --> <!-- 产品经理 -->
<template v-if="role == 41"> <template v-if="role == 41">
<el-badge :value="productState.stat1"> <el-badge :value="num1">
<li :class="{ active: active == 1 }" @click="toPage('/product/bank?type=0&i=1&role=41')"> <li :class="{ active: active == 1 }"
<img class="icon" src="@/assets/images/icon5.png" alt="" /> @click="toPage('/product/bank?type=0&i=1&role=41')">
<img class="icon-1" src="@/assets/images/icon5-1.png" alt="" /> <img class="icon"
src="@/assets/images/icon5.png"
alt="" />
<img class="icon-1"
src="@/assets/images/icon5-1.png"
alt="" />
<p class="text">个人产品</p> <p class="text">个人产品</p>
</li> </li>
</el-badge> </el-badge>
<el-badge :value="productState.stat2"> <el-badge :value="num2">
<li :class="{ active: active == 2 }" @click="toPage('/product/bank?type=1&i=2&role=41')"> <li :class="{ active: active == 2 }"
<img class="icon" src="@/assets/images/icon6.png" alt="" /> @click="toPage('/product/bank?type=1&i=2&role=41')">
<img class="icon-1" src="@/assets/images/icon6-1.png" alt="" /> <img class="icon"
src="@/assets/images/icon6.png"
alt="" />
<img class="icon-1"
src="@/assets/images/icon6-1.png"
alt="" />
<p class="text">企业产品</p> <p class="text">企业产品</p>
</li> </li>
</el-badge> </el-badge>
</template> </template>
<!-- 风控经理 --> <!-- 风控经理 -->
<template v-else-if="role == 42"> <template v-else-if="role == 42">
<el-badge :value="productState.stat1"> <el-badge :value="num1">
<li :class="{ active: active == 1 }" @click="toPage('/product/bank?type=&i=1&role=42')"> <li :class="{ active: active == 1 }"
<img class="icon" src="@/assets/images/icon1.png" alt="" /> @click="toPage('/product/bank?type=&i=1&role=42')">
<img class="icon-1" src="@/assets/images/icon1-1.png" alt="" /> <img class="icon"
src="@/assets/images/icon1.png"
alt="" />
<img class="icon-1"
src="@/assets/images/icon1-1.png"
alt="" />
<p class="text">产品风控配置</p> <p class="text">产品风控配置</p>
</li> </li>
</el-badge> </el-badge>
<li :class="{ active: active == 2 }" @click="toPage('/product/strategy?i=2&role=42&id=150')"> <li :class="{ active: active == 2 }"
<img class="icon" src="@/assets/images/icon2.png" alt="" /> @click="toPage('/product/strategy?i=2&role=42&id=150')">
<img class="icon-1" src="@/assets/images/icon2-1.png" alt="" /> <img class="icon"
src="@/assets/images/icon2.png"
alt="" />
<img class="icon-1"
src="@/assets/images/icon2-1.png"
alt="" />
<p class="text">贷前准入模型</p> <p class="text">贷前准入模型</p>
</li> </li>
<li :class="{ active: active == 3 }" @click="toPage('/product/interestRate?&i=3&role=42&id=772')"> <li :class="{ active: active == 3 }"
<img class="icon" src="@/assets/images/icon3.png" alt="" /> @click="toPage('/product/interestRate/tab1?&i=3&role=42&id=772')">
<img class="icon-1" src="@/assets/images/icon3-1.png" alt="" /> <img class="icon"
src="@/assets/images/icon3.png"
alt="" />
<img class="icon-1"
src="@/assets/images/icon3-1.png"
alt="" />
<p class="text">利率定价模型</p> <p class="text">利率定价模型</p>
</li> </li>
<li :class="{ active: active == 4 }" @click="toPage('/product/afterLoan?&i=4&role=42&id=1029')"> <li :class="{ active: active == 4 }"
<img class="icon" src="@/assets/images/icon4.png" alt="" /> @click="toPage('/product/afterLoan?&i=4&role=42&id=1029')">
<img class="icon-1" src="@/assets/images/icon4-1.png" alt="" /> <img class="icon"
src="@/assets/images/icon4.png"
alt="" />
<img class="icon-1"
src="@/assets/images/icon4-1.png"
alt="" />
<p class="text">贷后管理模型</p> <p class="text">贷后管理模型</p>
</li> </li>
</template> </template>
<!-- 专家委员会 --> <!-- 专家委员会 -->
<el-badge v-else-if="role == 43" :value="productState.stat1"> <el-badge v-else-if="role == 43"
:value="num1">
<li class="active"> <li class="active">
<img class="icon-1" src="@/assets/images/icon7-1.png" alt="" /> <img class="icon-1"
src="@/assets/images/icon7-1.png"
alt="" />
<p class="text">审批产品</p> <p class="text">审批产品</p>
</li> </li>
</el-badge> </el-badge>
<!-- 保险 --> <!-- 保险 -->
<li v-else-if="route.path.includes('insurance')" class="active"> <li v-else-if="route.path.includes('insurance')"
<img class="icon-1" src="@/assets/images/icon4-1.png" alt="" /> class="active">
<img class="icon-1"
src="@/assets/images/icon4-1.png"
alt="" />
<p class="text">保险产品</p> <p class="text">保险产品</p>
</li> </li>
<!-- 基金 --> <!-- 基金 -->
<li v-else-if="route.path.includes('fund')" class="active"> <li v-else-if="route.path.includes('fund')"
<img class="icon-1" src="@/assets/images/icon3-1.png" alt="" /> class="active">
<img class="icon-1"
src="@/assets/images/icon3-1.png"
alt="" />
<p class="text">基金产品</p> <p class="text">基金产品</p>
</li> </li>
<!-- 配置 --> <!-- 配置 -->
<template v-else> <template v-else>
<li :class="{ active: route.path === '/config/index' }" @click="toPage('/config/index')"> <li :class="{ active: route.path === '/config/index' }"
<img class="icon" src="@/assets/images/icon4.png" alt="" /> @click="toPage('/config/index')">
<img class="icon-1" src="@/assets/images/icon4-1.png" alt="" /> <img class="icon"
src="@/assets/images/icon4.png"
alt="" />
<img class="icon-1"
src="@/assets/images/icon4-1.png"
alt="" />
<p class="text">参数配置</p> <p class="text">参数配置</p>
</li> </li>
<!-- 中台进来的才需要显示关卡配置 --> <!-- 中台进来的才需要显示关卡配置 -->
<li v-if="isAdmin" :class="{ active: route.path === '/config/level' }" @click="toPage('/config/level')"> <li v-if="isAdmin"
<img class="icon" src="@/assets/images/icon1.png" alt="" /> :class="{ active: route.path === '/config/level' }"
<img class="icon-1" src="@/assets/images/icon1-1.png" alt="" /> @click="toPage('/config/level')">
<img class="icon"
src="@/assets/images/icon1.png"
alt="" />
<img class="icon-1"
src="@/assets/images/icon1-1.png"
alt="" />
<p class="text">关卡配置</p> <p class="text">关卡配置</p>
</li> </li>
</template> </template>
@ -80,59 +130,85 @@
import { onMounted, computed, ref } from 'vue'; import { onMounted, computed, ref } from 'vue';
import { useRouter, useRoute } from 'vue-router'; import { useRouter, useRoute } from 'vue-router';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import { productState, getStat } from '@/store/useProduct'; import { statistics } from '@/api/bank';
const router = useRouter(); const router = useRouter();
const route = useRoute(); const route = useRoute();
const active = computed(() => route.query.i); const active = computed(() => route.query.i);
const role = computed(() => route.query.role); const role = computed(() => route.query.role);
const projectId = +Cookies.get('sand-projectId');
const levelId = +Cookies.get('sand-level');
const isAdmin = ref<number | string>(route.query.admin ?? Cookies.get('sand-admin')); // const isAdmin = ref<number | string>(route.query.admin ?? Cookies.get('sand-admin')); //
const num1 = ref<number | string>('');
const num2 = ref<number>(0);
// //
const toPage = (path: string) => { const toPage = (path: string) => {
router.push(path); router.push(path);
}; };
//
const getNum = async (status?: number, type?: number) => {
const { data } = await statistics({
checkPointId: levelId,
projectId,
status,
productType: type,
});
if (type === 1) {
// -
num2.value = data || '';
} else {
num1.value = data || '';
}
};
onMounted(() => { onMounted(() => {
const status = role.value === '41' ? 299 : role.value === '42' ? 295 : role.value === '43' ? 296 : ''; // (1- 2-3-4-5- const status = role.value === '41' ? 299 : role.value === '42' ? 295 : role.value === '43' ? 296 : ''; // (1- 2-3-4-5-
if (status) { if (status) {
getStat(status, role.value === '41' ? 0 : ''); getNum(status, role.value === '41' ? 0 : '');
role.value === '41' && getStat(status, 1); role.value === '41' && getNum(status, 1);
} }
}); });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.switch { .switch {
:deep(.el-badge) {
@apply w-full;
}
li { li {
@apply flex flex-col justify-center items-center h-[102px] mb-5 bg-white rounded-[10px] cursor-pointer; display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 122px;
height: 102px;
margin-bottom: 20px;
background: #ffffff;
box-shadow: 0px 0px 8px 0px rgba(162, 199, 246, 0.16); box-shadow: 0px 0px 8px 0px rgba(162, 199, 246, 0.16);
border-radius: 10px;
cursor: pointer;
&:hover, &:hover,
&.active { &.active {
background: linear-gradient(-36deg, #3c65ff, #33d1ff); background: linear-gradient(-36deg, #3c65ff, #33d1ff);
box-shadow: 0px 6px 16px 0px rgba(96, 155, 255, 0.56); box-shadow: 0px 6px 16px 0px rgba(96, 155, 255, 0.56);
.icon { .icon {
@apply hidden; display: none;
} }
.icon-1 { .icon-1 {
@apply inline-block; display: inline-block;
} }
.text { .text {
@apply text-white; color: #fff;
} }
} }
} }
img { img {
@apply mx-auto; margin: 0 auto;
} }
.icon-1 { .icon-1 {
@apply hidden; display: none;
} }
.text { .text {
@apply mt-[10px] font-[MiSans] font-semibold text-base lg:text-sm; margin-top: 10px;
font-family: MiSans;
font-weight: 600;
} }
} }
</style> </style>

@ -1,11 +1,14 @@
<template> <template>
<div> <div>
<el-scrollbar wrap-style="height: calc(100% - 85px)"> <el-scrollbar wrap-style="height: calc(100% - 85px)">
<div v-if="!isConfig" class="avatar py-3 mx-auto mb-5 text-center"> <div v-if="!isConfig"
<img class="mx-auto" src="@/assets/images/6.png" alt="" /> class="avatar py-3 mx-auto mb-5 text-center">
<p class="text-white text-base lg:text-sm">{{ roleName }}</p> <img class="mx-auto"
<p class="my-2 text-white text-sm lg:text-xs">产品部门</p> src="@/assets/images/6.png"
<div class="flex justify-center items-center text-white text-xs lg:text-[10px]"> alt="" />
<p class="text-white text-md">{{ roleName }}</p>
<p class="my-2 text-white text-sm">产品部门</p>
<div class="flex justify-center items-center text-white text-xs">
虚拟时间{{ date }} 虚拟时间{{ date }}
<!-- <img class="ml-2 cursor-pointer" <!-- <img class="ml-2 cursor-pointer"
src="@/assets/images/date.png" src="@/assets/images/date.png"
@ -16,14 +19,22 @@
<menus></menus> <menus></menus>
</el-scrollbar> </el-scrollbar>
<el-dialog v-model="dateVisible" title="选择交易日期" width="400px" center> <el-dialog v-model="dateVisible"
title="选择交易日期"
width="400px"
center>
<div class="text-center"> <div class="text-center">
<el-date-picker v-model="diaDate" format="YYYY/MM/DD" value-format="YYYY-MM-DD" type="date" /> <el-date-picker v-model="diaDate"
format="YYYY/MM/DD"
value-format="YYYY-MM-DD"
type="date" />
</div> </div>
<template #footer> <template #footer>
<span class="flex justify-center"> <span class="flex justify-center">
<div class="dia-btn cancel" @click="dateVisible = false">取消</div> <div class="dia-btn mr-3 cancel"
<div class="dia-btn" @click="submitDate">确定</div> @click="dateVisible = false">取消</div>
<div class="dia-btn"
@click="submitDate">确定</div>
</span> </span>
</template> </template>
</el-dialog> </el-dialog>
@ -33,11 +44,13 @@
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, ref, computed } from 'vue'; import { onMounted, ref, computed } from 'vue';
import { useRouter, useRoute } from 'vue-router'; import { useRouter, useRoute } from 'vue-router';
import Menus from './Menu.vue';
import Settings from '@/settings';
import { getOperationTime, saveOperationTime } from '@/api/config'; import { getOperationTime, saveOperationTime } from '@/api/config';
import { appState } from '@/store/useAppState';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { getNow } from '@/utils/common'; import { getNow } from '@/utils/common';
import Menus from './Menu.vue';
const router = useRouter(); const router = useRouter();
const route = useRoute(); const route = useRoute();
@ -47,20 +60,13 @@ const levelId = +Cookies.get('sand-level');
const date = ref<string>(dayjs(new Date()).format('YYYY-MM-DD')); const date = ref<string>(dayjs(new Date()).format('YYYY-MM-DD'));
const diaDate = ref<string>(dayjs(new Date()).format('YYYY-M-D')); const diaDate = ref<string>(dayjs(new Date()).format('YYYY-M-D'));
const dateVisible = ref<boolean>(false); const dateVisible = ref<boolean>(false);
const roleIds = {
41: '产品经理',
42: '风控经理',
43: '专家委员会',
};
const roleName = computed(() => { const roleName = computed(() => {
if (route.query.role) { if (route.query.role) {
return roleIds[+route.query.role]; return Settings.roleIds[+route.query.role];
} } else if (route.path.includes('insurance')) {
if (route.path.includes('insurance')) {
return '保险产品经理'; return '保险产品经理';
} } else if (route.path.includes('fund')) {
if (route.path.includes('fund')) {
return '基金产品经理'; return '基金产品经理';
} }
}); });

@ -1,7 +1,7 @@
<template> <template>
<div class="flex items-center justify-center overflow-hidden"> <div class="flex items-center justify-center overflow-hidden">
<router-link class="whitespace-nowrap text-center" to="/"> <router-link class="whitespace-nowrap text-center" to="/">
<h1 class="ml-1 text-[22px] lg:text-lg leading-[1] font-bold text-[#333]">{{ title }}</h1> <h1 v-if="!collapse" class="ml-1 text-[22px] leading-[1] font-bold text-[#333]">{{ title }}</h1>
</router-link> </router-link>
</div> </div>
</template> </template>
@ -12,6 +12,12 @@ import Settings from '@/settings';
export default defineComponent({ export default defineComponent({
name: 'SidebarLogo', name: 'SidebarLogo',
props: {
collapse: {
type: Boolean,
required: true,
},
},
data() { data() {
return { return {
title: Settings.title, title: Settings.title,

@ -1,9 +1,15 @@
<template> <template>
<div class="min-h-full bg-[url('@/assets/images/1.png')] bg-[length:100%_100%] bg-no-repeat"> <div class="min-h-full bg-[url('@/assets/images/1.png')] bg-[length:100%_100%] bg-no-repeat">
<app-header /> <app-header />
<Back v-if="hidePanel" class="mx-3" name="金融产品设计及数字化营销沙盘系统后台管理系统" :isLogout="true" /> <Back v-if="hidePanel"
<app-sidebar v-if="!hideNav" class="sidebar fixed h-full px-5 overflow-hidden transition-width duration-300" /> class="mx-3"
<div class="main h-[calc(100vh-86px)] transition-margin duration-300 overflow-auto" :class="{ ml: !hideNav }" id="appMain"> name="金融产品设计及数字化营销沙盘系统后台管理系统"
:isLogout="true" />
<app-sidebar v-if="!hideNav"
class="sidebar fixed w-sidebar h-full px-5 overflow-hidden transition-width duration-300 z-40" />
<div class="main h-[calc(100vh-86px)] transition-margin duration-300 overflow-auto"
:class="{ 'ml-sidebar': !hideNav }"
id="appMain">
<app-main /> <app-main />
</div> </div>
<Panel /> <Panel />
@ -14,10 +20,10 @@
import { defineComponent, computed } from 'vue'; import { defineComponent, computed } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import Settings from '@/settings'; import Settings from '@/settings';
import Panel from '@/components/Panel/index.vue';
import Back from '@/components/Back.vue';
import { AppSidebar, AppHeader, AppMain } from './components'; import { AppSidebar, AppHeader, AppMain } from './components';
import useResizeHandler from './composables/useResizeHandler'; import useResizeHandler from './composables/useResizeHandler';
import Panel from '@/components/Panel/index.vue';
import Back from '@/components/Back.vue';
export default defineComponent({ export default defineComponent({
name: 'Layout', name: 'Layout',
@ -50,10 +56,4 @@ export default defineComponent({
background: url(../assets/images/1.png) 0 0/100% auto no-repeat; background: url(../assets/images/1.png) 0 0/100% auto no-repeat;
@apply bg-gray-100; @apply bg-gray-100;
} }
.sidebar {
@apply w-sidebar lg:w-[160px];
}
.ml {
@apply ml-sidebar lg:ml-[144px];
}
</style> </style>

@ -2,11 +2,14 @@ import NProgress from 'nprogress'; // progress bar
import 'nprogress/nprogress.css'; // progress bar style import 'nprogress/nprogress.css'; // progress bar style
import { RouteLocationNormalized } from 'vue-router'; import { RouteLocationNormalized } from 'vue-router';
import getPageTitle from '@/utils/getPageTitle'; import getPageTitle from '@/utils/getPageTitle';
import { getAccessToken } from '@/utils/auth'; // get token from cookie
import router from './router'; import router from './router';
NProgress.configure({ showSpinner: false }); // NProgress Configuration NProgress.configure({ showSpinner: false }); // NProgress Configuration
router.beforeEach(async () => { const LOGIN_PATH = '/login';
router.beforeEach(async (to: RouteLocationNormalized) => {
NProgress.start(); NProgress.start();
return true; return true;
}); });

@ -37,7 +37,7 @@ export const routes: Array<RouteRecordRaw> = [
{ path: 'strategy', component: () => import('@/views/product/strategy/CardList.vue'), meta: { title: '产品列表' } }, { path: 'strategy', component: () => import('@/views/product/strategy/CardList.vue'), meta: { title: '产品列表' } },
{ path: 'insurance', component: () => import('@/views/product/insurance/List.vue'), meta: { title: '保险产品' } }, { path: 'insurance', component: () => import('@/views/product/insurance/List.vue'), meta: { title: '保险产品' } },
{ path: 'insurance/:action', component: () => import('@/views/product/insurance/CardList.vue'), meta: { title: '保险产品' } }, { path: 'insurance/:action', component: () => import('@/views/product/insurance/CardList.vue'), meta: { title: '保险产品' } },
{ path: 'interestRate', component: () => import('@/views/product/interestRate/CardList.vue'), meta: { title: '利率定价模型' } }, { path: 'interestRate/:action', component: () => import('@/views/product/interestRate/CardList.vue'), meta: { title: '利率定价模型' } },
{ path: 'fund', component: () => import('@/views/product/fund/List.vue'), meta: { title: '基金产品' } }, { path: 'fund', component: () => import('@/views/product/fund/List.vue'), meta: { title: '基金产品' } },
{ path: 'fund/:action', component: () => import('@/views/product/fund/CardList.vue'), meta: { title: '基金产品' } }, { path: 'fund/:action', component: () => import('@/views/product/fund/CardList.vue'), meta: { title: '基金产品' } },
{ path: 'afterLoan', component: () => import('@/views/product/afterLoan/CardList.vue'), meta: { title: '贷后管理' } }, { path: 'afterLoan', component: () => import('@/views/product/afterLoan/CardList.vue'), meta: { title: '贷后管理' } },

@ -1,5 +1,15 @@
export default { export default {
title: import.meta.env.VITE_APP_TITLE || '金融产品设计及数字化营销沙盘', title: import.meta.env.VITE_APP_TITLE || '金融产品设计及数字化营销沙盘',
showSettings: true,
/**
* @type {object}
* @description id
*/
roleIds: {
41: '产品经理',
42: '风控经理',
43: '专家委员会',
},
/** /**
* @type {array} * @type {array}
* @description * @description

@ -1,4 +1,4 @@
import { reactive } from 'vue'; import { reactive, readonly } from 'vue';
import { RouteRecordRaw } from 'vue-router'; import { RouteRecordRaw } from 'vue-router';
import { removeAccessToken, removeParam } from '@/utils/auth'; import { removeAccessToken, removeParam } from '@/utils/auth';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';

@ -1,11 +1,8 @@
import { reactive, readonly } from 'vue'; import { reactive, readonly } from 'vue';
import { statistics } from '@/api/bank';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
export interface Product { export interface Product {
status?: Record<string, any>; status?: Record<string, any>;
stat1?: number | string;
stat2?: number | string;
} }
const state = reactive<Product>({ const state = reactive<Product>({
@ -36,12 +33,9 @@ const state = reactive<Product>({
name: '审批打回', name: '审批打回',
}, },
], ],
// 左侧菜单项的右上角badge统计
stat1: '',
stat2: '',
}); });
export const productState = state; export const productState = readonly(state);
// 专家委员会产品进度(去除配置风控) // 专家委员会产品进度(去除配置风控)
export const getExpertStatus = (): Record<string, any>[] => { export const getExpertStatus = (): Record<string, any>[] => {
const newStatus = JSON.parse(JSON.stringify(state.status)); const newStatus = JSON.parse(JSON.stringify(state.status));
@ -52,18 +46,3 @@ export const getExpertStatus = (): Record<string, any>[] => {
export const getStatus = (id: number | string): string => { export const getStatus = (id: number | string): string => {
return state.status.find((e) => e.id === id)?.name; return state.status.find((e) => e.id === id)?.name;
}; };
// 获取产品数量
export const getStat = async (status?: number, type?: number): Promise<void> => {
const { data } = await statistics({
checkpointId: Cookies.get('sand-level'),
projectId: Cookies.get('sand-projectId'),
status,
productType: type,
});
if (type === 1) {
// 产品经理-企业
state.stat2 = data || '';
} else {
state.stat1 = data || '';
}
};

@ -32,14 +32,14 @@
} }
:deep(.el-form-item__label) { :deep(.el-form-item__label) {
@apply font-semibold text-sm lg:text-[12px] leading-[32px] text-black; @apply font-semibold text-sm leading-[32px] text-black;
} }
.field-name { .field-name {
@apply mb-3 text-sm lg:text-[12px] font-semibold leading-[32px] text-[#333]; @apply mb-3 text-sm font-semibold leading-[32px] text-[#333];
} }
.tips { .tips {
@apply text-sm lg:text-xs text-[#686868]; @apply text-sm text-[#686868];
} }
.num-inputs { .num-inputs {
@apply flex items-center; @apply flex items-center;
@ -53,27 +53,9 @@
@apply ml-2.5 text-sm text-[#333]; @apply ml-2.5 text-sm text-[#333];
} }
} }
.radio-wrap {
@apply flex items-center;
.label {
@apply mr-10 lg:text-xs;
}
.whether {
@apply mr-5 text-xs text-[#666];
}
.el-radio {
margin-right: 15px;
}
}
:deep(.el-input),
:deep(.el-textarea),
:deep(.el-radio__label),
:deep(.el-checkbox__label) {
@apply lg:text-xs;
}
} }
.submit { .submit {
@apply py-[15px] px-[22px] lg:py-[10px] lg:px-[15px] mt-5 text-sm lg:text-xs leading-none text-white bg-[#006bff] rounded-xl cursor-pointer; @apply py-[15px] px-[22px] mt-5 text-sm leading-none text-white bg-[#006bff] rounded-xl cursor-pointer;
&:hover { &:hover {
@apply opacity-90; @apply opacity-90;
} }

@ -49,7 +49,9 @@ body {
// global css // global css
.block { .block {
@apply p-5 rounded-lg bg-white; @apply p-5;
@apply rounded-lg;
@apply bg-white;
} }
.inline-form .el-form-item { .inline-form .el-form-item {
@ -72,12 +74,12 @@ body {
.card-list { .card-list {
.left { .left {
@apply w-[241px] min-w-[241px] lg:w-[180px] lg:min-w-[180px] pr-5 py-4 border-r border-solid border-[#e9eff2]; @apply w-[241px] min-w-[241px] pr-5 py-4 border-r border-solid border-[#e9eff2];
} }
.products { .products {
@apply max-h-[calc(100vh-150px)] pr-1 overflow-auto; @apply max-h-[calc(100vh-150px)] pr-1 overflow-auto;
li { li {
@apply relative p-5 pt-7 lg:p-4 mb-5 rounded-[10px] cursor-pointer border border-solid border-[transparent] bg-[url('@/assets/images/10.png')] bg-[length:100%_100%] bg-no-repeat; @apply relative p-5 pt-7 mb-5 rounded-[10px] cursor-pointer border border-solid border-[transparent] bg-[url('@/assets/images/10.png')] bg-[length:100%_100%] bg-no-repeat;
&.active { &.active {
@apply border-[#519aff]; @apply border-[#519aff];
} }
@ -86,46 +88,32 @@ body {
@apply absolute top-0 right-0; @apply absolute top-0 right-0;
} }
h6 { h6 {
@apply text-[#14436b] font-semibold lg:text-[15px]; @apply text-[#14436b] font-semibold;
} }
.type, .type,
.status { .status {
@apply my-[15px] lg:mt-2 text-sm lg:text-xs text-[#333]; @apply my-[15px] text-sm text-[#333];
} }
.meta { .meta {
@apply mt-[15px] lg:mt-2 text-sm lg:text-xs text-[#8798a9]; @apply mt-[15px] text-sm text-[#8798a9];
} }
} }
.right { .right {
@apply flex-1 w-[calc(100vw-533px)] max-h-[calc(100vh-100px)] overflow-auto px-5 pt-2; @apply flex-1 w-[calc(100vw-533px)] max-h-[calc(100vh-100px)] overflow-auto px-5 pt-2;
} }
} }
.menu-card {
@apply flex;
.left {
@apply mr-4 rounded-lg border-r-0;
}
.right {
@apply flex-1 w-[calc(100vw-533px)] max-h-[calc(100vh-100px)] overflow-auto p-4 bg-white rounded-lg;
}
}
// .c-auto { // .c-auto {
// @apply w-[calc(100vw-533px)] max-h-[calc(100vh-160px)] overflow-auto; // @apply w-[calc(100vw-533px)] max-h-[calc(100vh-160px)] overflow-auto;
// } // }
.c-table { .c-table {
@apply rounded-[10px]; @apply rounded-[10px];
th.el-table__cell { th.el-table__cell {
@apply bg-[#F8FBFC]; @apply bg-[#F8FBFC];
} }
th.el-table__cell > .cell { th.el-table__cell > .cell {
@apply text-[#006BFF]; @apply text-[#006BFF];
} }
.cell, .el-input__inner {
.el-input {
@apply lg:text-xs;
}
.el-input__wrapper {
@apply px-2; @apply px-2;
} }
} }
@ -138,7 +126,7 @@ body {
} }
.el-select { .el-select {
@apply w-[170px]; @apply w-[170px];
.el-input__wrapper { .el-input__inner {
@apply pl-[41px] rounded-[18px] border-[#dfe9f8]; @apply pl-[41px] rounded-[18px] border-[#dfe9f8];
} }
} }
@ -157,21 +145,16 @@ body {
} }
} }
.dia-btn { .dia-btn {
@apply py-[11px] px-[26px] text-sm leading-[1] text-white rounded-[18px] cursor-pointer; padding: 11px 26px;
font-size: 14px;
line-height: 1;
color: #fff;
background: linear-gradient(-36deg, #006bff, #2ab1ff); background: linear-gradient(-36deg, #006bff, #2ab1ff);
border-radius: 18px;
cursor: pointer;
&.cancel { &.cancel {
@apply mr-3 font-semibold text-[#333]; font-size: 600;
color: #333;
background: #f4f8fc; background: #f4f8fc;
} }
} }
.model-drawer {
.el-drawer__header > :first-child {
@apply text-[#333] text-xl font-semibold text-center;
}
}
@media screen and (-webkit-min-device-pixel-ratio: 1.5), screen and (min-resolution: 144dpi) {
body {
zoom: 0.9;
}
}

@ -24,7 +24,6 @@ export const removeParam = (): void => {
Cookies.remove('sand-systemId'); Cookies.remove('sand-systemId');
Cookies.remove('sand-level'); Cookies.remove('sand-level');
Cookies.remove('sand-submit'); Cookies.remove('sand-submit');
Cookies.remove('sand-loaded');
}; };
export const getAuthHeaders = (): any => { export const getAuthHeaders = (): any => {

@ -1,6 +1,5 @@
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import { getCurrentTime } from '@/api/system'; import { getCurrentTime } from '@/api/system';
import { getTheCurrentUserName } from '@/api/config';
const UJCMS_LOCALE = 'ujcms-locale'; const UJCMS_LOCALE = 'ujcms-locale';
const UJCMS_SITE_ID = 'ujcms-site-id'; const UJCMS_SITE_ID = 'ujcms-site-id';
@ -237,7 +236,6 @@ export const getFileExt = (fileName: string): string => {
*/ */
export const getIds = (): Record<string, any> => { export const getIds = (): Record<string, any> => {
return { return {
cid: +Cookies.get('sand-cid'),
checkpointId: +Cookies.get('sand-level') || '', checkpointId: +Cookies.get('sand-level') || '',
projectId: +Cookies.get('sand-projectId') || '', projectId: +Cookies.get('sand-projectId') || '',
assessmentId: +Cookies.get('sand-assessmentId') || '', assessmentId: +Cookies.get('sand-assessmentId') || '',
@ -254,33 +252,3 @@ export const getNow = (): Promise<any> => {
resolve(new Date(res.currentTime)); resolve(new Date(res.currentTime));
}); });
}; };
/**
*
*/
export const getUsername = async (): string => {
const res = await getTheCurrentUserName();
return res.userName;
};
export const whethers = [
{
id: 795,
name: '启用',
},
{
id: 796,
name: '不启用',
},
];
export const opt1 = [
{
id: 345,
name: '是',
},
{
id: 346,
name: '否',
},
];

@ -1,10 +1,14 @@
import defaultSettings from '@/settings'; import defaultSettings from '@/settings';
import i18n from '@/i18n';
const { title } = defaultSettings; const { title } = defaultSettings;
export default function getPageTitle(pageTitle: string | undefined): string { export default function getPageTitle(pageTitle: string | undefined): string {
if (pageTitle) { if (pageTitle) {
return `${pageTitle} - ${title}`; const {
global: { t },
} = i18n;
return `${t(pageTitle)} - ${title}`;
} }
return `${title}`; return `${title}`;
} }

@ -1,13 +1,15 @@
import { h } from 'vue';
import axios from 'axios'; import axios from 'axios';
import { ElMessageBox, ElMessage } from 'element-plus'; import { ElMessageBox, ElMessage } from 'element-plus';
import { getAuthHeaders } from '@/utils/auth'; import { getAuthHeaders } from '@/utils/auth';
import i18n from '@/i18n';
import { logout } from '@/store/useCurrentUser'; import { logout } from '@/store/useCurrentUser';
const service = axios.create({ const service = axios.create({
baseURL: import.meta.env.VITE_BASE_API, baseURL: import.meta.env.VITE_BASE_API,
timeout: 60 * 1000, timeout: 10000,
}); });
let logouted = 0;
service.interceptors.request.use( service.interceptors.request.use(
(config) => { (config) => {
config.headers = { ...config.headers, ...getAuthHeaders() }; config.headers = { ...config.headers, ...getAuthHeaders() };
@ -18,27 +20,12 @@ service.interceptors.request.use(
service.interceptors.response.use( service.interceptors.response.use(
(res) => { (res) => {
const { message, status, code, msg } = res.data; const { message, status } = res.data;
if (status) { if (status) {
if (status === 200) { if (status === 200) {
return res; return res;
} }
message === '无缓存' || ElMessage.error(message);
ElMessage.error(message);
} else if (code === 401) {
// 账号互踢
if (!logouted) {
ElMessageBox.alert(msg.includes('顶') ? '您的账号已在其他设备登录,您已被迫下线!' : '登录过期,请重新登录!', {
confirmButtonText: '重新登录',
type: 'warning',
closeOnClickModal: false,
showClose: false,
}).then(() => {
// 未登录
logout();
});
logouted = 1;
}
} else { } else {
return res; return res;
} }
@ -46,18 +33,24 @@ service.interceptors.response.use(
(e) => { (e) => {
const { const {
response: { response: {
data: { message, error }, data: { timestamp, message, path, error, exception, trace },
status, status,
}, },
} = e; } = e;
const {
global: { t },
} = i18n;
if (status === 401) { if (status === 401) {
logouted || ElMessageBox.confirm(t('confirmLogin'), { confirmButtonText: t('loginAgain'), type: 'warning' }).then(() => {
ElMessageBox.alert('登录状态已过期,请重新登录', { confirmButtonText: '重新登录', type: 'warning', closeOnClickModal: false, showClose: false }).then(() => {
// 未登录 // 未登录
logout(); logout();
}); });
logouted = 1; } else if (status === 403) {
} else if (message) { ElMessageBox({
title: status,
message: h('div', null, [h('p', { class: 'text-lg' }, t('error.forbidden')), h('p', { class: 'mt-2' }, message)]),
});
} else {
ElMessage.error(message); ElMessage.error(message);
} }
return Promise.reject(error); return Promise.reject(error);

@ -2,9 +2,9 @@
<div class="h-full p-4 bg-gray-200"> <div class="h-full p-4 bg-gray-200">
<div class="p-4 rounded shadow bg-white"> <div class="p-4 rounded shadow bg-white">
<h1 class="font-bold text-3xl">403</h1> <h1 class="font-bold text-3xl">403</h1>
<p class="mt-4">对不起您没有该页面的访问权限</p> <p class="mt-4">{{ message }}</p>
<p class="mt-4"> <p class="mt-4">
<el-button type="primary" @click="handleLogout()" plain>退出</el-button> <el-button type="primary" @click="handleLogout()" plain>{{ $t('logout') }}</el-button>
</p> </p>
</div> </div>
</div> </div>
@ -13,17 +13,20 @@
<script lang="ts"> <script lang="ts">
import { defineComponent, ref } from 'vue'; import { defineComponent, ref } from 'vue';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { logout } from '@/store/useCurrentUser'; import { logout } from '@/store/useCurrentUser';
export default defineComponent({ export default defineComponent({
name: 'Page403', name: 'Page403',
setup() { setup() {
const { t } = useI18n();
const router = useRouter(); const router = useRouter();
const message = ref(t('error.forbidden'));
const handleLogout = () => { const handleLogout = () => {
logout(); logout();
router.push('/login'); router.push('/login');
}; };
return { handleLogout }; return { message, handleLogout };
}, },
}); });
</script> </script>

@ -2,15 +2,14 @@
<div class="wscn-http404-container"> <div class="wscn-http404-container">
<div class="wscn-http404"> <div class="wscn-http404">
<div class="pic-404"> <div class="pic-404">
<img class="pic-404__parent" src="@/assets/404_images/404.png" alt="404" /> <img class="pic-404__parent" src="@/assets/404_images/404.png" alt="404">
<img class="pic-404__child left" src="@/assets/404_images/404_cloud.png" alt="404" /> <img class="pic-404__child left" src="@/assets/404_images/404_cloud.png" alt="404">
<img class="pic-404__child mid" src="@/assets/404_images/404_cloud.png" alt="404" /> <img class="pic-404__child mid" src="@/assets/404_images/404_cloud.png" alt="404">
<img class="pic-404__child right" src="@/assets/404_images/404_cloud.png" alt="404" /> <img class="pic-404__child right" src="@/assets/404_images/404_cloud.png" alt="404">
</div> </div>
<div class="bullshit"> <div class="bullshit">
<div class="bullshit__oops">OOPS!</div> <div class="bullshit__oops">OOPS!</div>
<div class="bullshit__info"> <div class="bullshit__info">All rights reserved
All rights reserved
<a style="color:#20a0ff" href="https://wallstreetcn.com" target="_blank">wallstreetcn</a> <a style="color:#20a0ff" href="https://wallstreetcn.com" target="_blank">wallstreetcn</a>
</div> </div>
<div class="bullshit__headline">{{ message }}</div> <div class="bullshit__headline">{{ message }}</div>

@ -2,40 +2,64 @@
<div class="flex justify-between items-center h-[64px] px-5 bg-white"> <div class="flex justify-between items-center h-[64px] px-5 bg-white">
<h1>金融产品设计及数字化营销沙盘系统</h1> <h1>金融产品设计及数字化营销沙盘系统</h1>
<div class="inline-flex items-center"> <div class="inline-flex items-center">
<el-tooltip effect="light" content="退出实训" placement="bottom"> <el-tooltip effect="light"
<img class="mr-3 cursor-pointer" src="@/assets/images/11.png" alt="" @click="logout" /> content="退出实训"
placement="bottom">
<img class="mr-3 cursor-pointer"
src="@/assets/images/11.png"
alt=""
@click="logout" />
</el-tooltip> </el-tooltip>
</div> </div>
</div> </div>
<div class="relative h-[calc(100vh-64px)] pt-5 pl-5 bg-[url('@/assets/images/level/4.png')] bg-[length:100%_100%] bg-no-repeat bg-fixed overflow-auto" id="wrap"> <div class="relative h-[calc(100vh-64px)] pt-5 pl-5 bg-[url('@/assets/images/level/4.png')] bg-[length:100%_100%] bg-no-repeat bg-fixed overflow-auto"
id="wrap">
<div class="fixed z-10"> <div class="fixed z-10">
<div class="w-[354px] h-[68px] bg-[url('@/assets/images/level/5.png')] bg-[length:100%_100%] bg-no-repeat"></div> <div class="w-[354px] h-[68px] bg-[url('@/assets/images/level/5.png')] bg-[length:100%_100%] bg-no-repeat"></div>
<div class="absolute top-5 left-40 flex items-center cursor-pointer" @click="getLevel(1)"> <div class="absolute top-5 left-40 flex items-center cursor-pointer"
<img v-if="collected" src="@/assets/images/level/7.png" alt="" /> @click="getLevel(1)">
<img v-else src="@/assets/images/level/6.png" alt="" /> <img v-if="collected"
src="@/assets/images/level/7.png"
alt="" />
<img v-else
src="@/assets/images/level/6.png"
alt="" />
<span class="ml-2 text-sm text-[#999]">仅显示已收藏的关卡</span> <span class="ml-2 text-sm text-[#999]">仅显示已收藏的关卡</span>
</div> </div>
</div> </div>
<div class="relative mt-20"> <div class="relative mt-20">
<div v-for="(item, i) in levels" :key="i" :class="['item', { active: curLevel === item.checkpointId, disabled: !item.enableOrNot }]" @click="selecLevel(item)"> <div v-for="(item, i) in levels"
<span class="num">LV.{{ i + 1 }}</span> :key="i"
:class="['item', { active: curLevel === item.checkpointId }]"
@click="selecLevel(item)">
<span class="num">LV.{{ item.serialNumber }}</span>
<div class="texts"> <div class="texts">
<h6>{{ numToChinese(i + 1) }}</h6> <h6>{{ numToChinese(item.serialNumber) }}</h6>
<p class="des mul-ellipsis2">{{ item.customsPassName }}</p> <p class="des mul-ellipsis2">{{ item.customsPassName }}</p>
<img v-if="item.collect" class="icon" src="@/assets/images/level/star2.png" alt="" @click.stop="collectItem(item)" /> <img v-if="item.collect"
<img v-else class="icon" src="@/assets/images/level/star1.png" alt="" @click.stop="collectItem(item)" /> class="icon"
src="@/assets/images/level/star2.png"
alt=""
@click.stop="collectItem(item)" />
<img v-else
class="icon"
src="@/assets/images/level/star1.png"
alt=""
@click.stop="collectItem(item)" />
</div> </div>
</div> </div>
</div> </div>
<div class="arrow top-[68px] left-[50%] translate-x-[-50%] w-[64px] h-[64px] bg-[url('@/assets/images/level/arrow-up.png')]" @click="move('up')"></div> <div class="arrow top-[68px] left-[50%] translate-x-[-50%] w-[64px] h-[64px] bg-[url('@/assets/images/level/arrow-up.png')]"
<div class="arrow right-0 top-[50%] translate-y-[-50%] w-[64px] h-[64px] bg-[url('@/assets/images/level/arrow-right.png')]" @click="move('right')"></div> @click="move('up')"></div>
<div class="arrow bottom-0 left-[50%] translate-x-[-50%] w-[64px] h-[64px] bg-[url('@/assets/images/level/arrow-down.png')]" @click="move('down')"></div> <div class="arrow right-0 top-[50%] translate-y-[-50%] w-[64px] h-[64px] bg-[url('@/assets/images/level/arrow-right.png')]"
<div class="arrow left-0 top-[50%] translate-y-[-50%] w-[64px] h-[64px] bg-[url('@/assets/images/level/arrow-left.png')]" @click="move('left')"></div> @click="move('right')"></div>
<div <div class="arrow bottom-0 left-[50%] translate-x-[-50%] w-[64px] h-[64px] bg-[url('@/assets/images/level/arrow-down.png')]"
class="fixed bottom-2 right-1 w-[262px] h-[74px] bg-[url('@/assets/images/level/submit.png')] bg-[length:100%_100%] bg-no-repeat cursor-pointer hover:bg-[url('@/assets/images/level/submit-hover.png')]" @click="move('down')"></div>
@click="toRole" <div class="arrow left-0 top-[50%] translate-y-[-50%] w-[64px] h-[64px] bg-[url('@/assets/images/level/arrow-left.png')]"
></div> @click="move('left')"></div>
<div class="fixed bottom-2 right-1 w-[262px] h-[74px] bg-[url('@/assets/images/level/submit.png')] bg-[length:100%_100%] bg-no-repeat cursor-pointer hover:bg-[url('@/assets/images/level/submit-hover.png')]"
@click="toRole"></div>
</div> </div>
<Panel /> <Panel />
</template> </template>
@ -66,21 +90,19 @@ const getLevel = async (only: any = '') => {
provide('getLevel', getLevel); provide('getLevel', getLevel);
// //
const selecLevel = (item: Record<string, any>) => { const selecLevel = (item: Record<string, any>) => {
if (item.enableOrNot) curLevel.value = item.checkpointId; curLevel.value = item.checkpointId;
}; };
// //
const collectItem = async (item: Record<string, any>) => { const collectItem = async (item: Record<string, any>) => {
if (item.enableOrNot) {
if (item.collect) { if (item.collect) {
await cancelCollection(item.favoriteId); await cancelCollection(item.favoriteId);
} else { } else {
await collect({ await collect({
checkpointId: item.checkpointId, checkPointId: item.checkpointId,
projectId, projectId,
}); });
} }
getLevel(); getLevel();
}
}; };
// //
const toRole = () => { const toRole = () => {
@ -105,7 +127,9 @@ const move = (dir: string) => {
if (dir === 'up' || dir === 'down') param.top = el.scrollTop + (dir === 'up' ? -150 : 150); if (dir === 'up' || dir === 'down') param.top = el.scrollTop + (dir === 'up' ? -150 : 150);
el.scrollTo(param); el.scrollTo(param);
}; };
onMounted(getLevel); onMounted(() => {
getLevel();
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -161,9 +185,6 @@ onMounted(getLevel);
-webkit-text-fill-color: #fff; -webkit-text-fill-color: #fff;
} }
} }
&.disabled {
@apply opacity-30 cursor-not-allowed;
}
&:nth-child(2) { &:nth-child(2) {
@apply top-[var(--line2)] left-[160px]; @apply top-[var(--line2)] left-[160px];
@ -390,10 +411,3 @@ onMounted(getLevel);
} }
} }
</style> </style>
<style>
@media screen and (-webkit-min-device-pixel-ratio: 1.5), screen and (min-resolution: 144dpi) {
body {
zoom: 1;
}
}
</style>

@ -41,50 +41,49 @@
<p class="text">今日渠道订单</p> <p class="text">今日渠道订单</p>
</div> </div>
</div> </div>
<el-tooltip effect="light" content="退出实训" placement="bottom"> <el-tooltip effect="light"
<img class="mr-3 cursor-pointer" src="@/assets/images/11.png" alt="" @click="logout" /> content="退出实训"
placement="bottom">
<img class="mr-3 cursor-pointer"
src="@/assets/images/11.png"
alt=""
@click="logout" />
</el-tooltip> </el-tooltip>
<el-tooltip effect="light" content="返回关卡" placement="bottom"> <el-tooltip effect="light"
<img class="mr-3 cursor-pointer" src="@/assets/images/2.png" alt="" @click="toLevel" /> content="返回关卡"
placement="bottom">
<img class="mr-3 cursor-pointer"
src="@/assets/images/2.png"
alt=""
@click="toLevel" />
</el-tooltip> </el-tooltip>
</div> </div>
</div> </div>
<div class="relative h-[calc(100vh-64px)] overflow-hidden bg-[#e5eafe]"> <div class="relative h-[calc(100vh-64px)] overflow-hidden bg-[#e5eafe]">
<img class="w-full h-full" src="@/assets/images/role/bg.png" alt="" /> <img class="w-full h-full"
src="@/assets/images/role/bg.png"
alt="">
<div class="absolute top-5 left-[18px] w-[204px] h-[68px] bg-[url('@/assets/images/role/2.png')] bg-[length:100%_100%] bg-no-repeat"></div> <div class="absolute top-5 left-[18px] w-[204px] h-[68px] bg-[url('@/assets/images/role/2.png')] bg-[length:100%_100%] bg-no-repeat"></div>
<div class="date absolute top-[70px] left-[54%] w-[198px] h-[46px] pt-[10px] pl-[56px] text-base text-white bg-[url('@/assets/images/role/date.png')] bg-no-repeat transition"> <div class="date absolute top-[70px] left-[54%] w-[198px] h-[46px] pt-[10px] pl-[56px] text-base text-white bg-[url('@/assets/images/role/date.png')] bg-no-repeat transition">
{{ date }} {{ date }}
</div> </div>
<div <div class="role top-[30%] left-[17%] xl:top-[31%] lg:top-[32%] lg:left-[15%] bg-[url('@/assets/images/role/product.png')] hover:bg-[url('@/assets/images/role/product1.png')]"
class="role top-[30%] left-[17%] xl:top-[31%] lg:top-[32%] lg:left-[15%] bg-[url('@/assets/images/role/product.png')] hover:bg-[url('@/assets/images/role/product1.png')]" @click="selectRole(41)"></div>
@click="selectRole(41)" <div class="role top-[22%] left-[25%] xl:top-[24%] lg:top-[25%] lg:left-[23%] bg-[url('@/assets/images/role/committee.png')] hover:bg-[url('@/assets/images/role/committee1.png')]"
></div> @click="selectRole(43)"></div>
<div <div class="role top-[17%] left-[31.5%] xl:top-[16%] xl:left-[31%] lg:left-[30%] bg-[url('@/assets/images/role/riskControl.png')] hover:bg-[url('@/assets/images/role/riskControl1.png')]"
class="role top-[22%] left-[25%] xl:top-[24%] lg:top-[25%] lg:left-[23%] bg-[url('@/assets/images/role/committee.png')] hover:bg-[url('@/assets/images/role/committee1.png')]" @click="selectRole(42)"></div>
@click="selectRole(43)" <div class="role bottom-[200px] left-[100px] xl:bottom-[150px] lg:bottom-[120px] lg1:bottom-[20px] bg-[url('@/assets/images/role/insurance.png')] hover:bg-[url('@/assets/images/role/insurance1.png')]"
></div> @click="selectRole(275)"></div>
<div <div class="role relative bottom-[70px] left-[25%] bg-[url('@/assets/images/role/fund.png')] hover:bg-[url('@/assets/images/role/fund1.png')]"
class="role top-[17%] left-[31.5%] xl:top-[16%] xl:left-[31%] lg:left-[30%] bg-[url('@/assets/images/role/riskControl.png')] hover:bg-[url('@/assets/images/role/riskControl1.png')]" @click="selectRole(1161)"></div>
@click="selectRole(42)" <div class="role relative bottom-[220px] left-[41%] bg-[url('@/assets/images/role/market-bank.png')] hover:bg-[url('@/assets/images/role/market-bank1.png')]"
></div> @click="selectRole('bank')"></div>
<div <div class="role relative bottom-[360px] left-[52%] bg-[url('@/assets/images/role/market-insurance.png')] hover:bg-[url('@/assets/images/role/market-insurance1.png')]"
class="role bottom-[200px] left-[100px] xl:bottom-[150px] lg:bottom-[120px] bg-[url('@/assets/images/role/insurance.png')] hover:bg-[url('@/assets/images/role/insurance1.png')]" @click="selectRole('insurance')"></div>
@click="selectRole(275)" <div class="role relative bottom-[490px] left-[63%] bg-[url('@/assets/images/role/market-fund.png')] hover:bg-[url('@/assets/images/role/market-fund1.png')]"
></div> @click="selectRole('fund')"></div>
<div class="role relative bottom-[70px] left-[25%] bg-[url('@/assets/images/role/fund.png')] hover:bg-[url('@/assets/images/role/fund1.png')]" @click="selectRole(1161)"></div>
<div
class="role relative bottom-[220px] left-[41%] bg-[url('@/assets/images/role/market-bank.png')] hover:bg-[url('@/assets/images/role/market-bank1.png')]"
@click="selectRole('bank')"
></div>
<div
class="role relative bottom-[360px] left-[52%] bg-[url('@/assets/images/role/market-insurance.png')] hover:bg-[url('@/assets/images/role/market-insurance1.png')]"
@click="selectRole('insurance')"
></div>
<div
class="role relative bottom-[490px] left-[63%] bg-[url('@/assets/images/role/market-fund.png')] hover:bg-[url('@/assets/images/role/market-fund1.png')]"
@click="selectRole('fund')"
></div>
</div> </div>
<!-- <div class="fixed top-[80px] right-[80px]"> <!-- <div class="fixed top-[80px] right-[80px]">
<div class="flex items-center h-[60px] px-4 rounded-tl-[20px] rounded-tr-[20px]" <div class="flex items-center h-[60px] px-4 rounded-tl-[20px] rounded-tr-[20px]"
@ -125,14 +124,20 @@
<Panel /> <Panel />
<el-dialog v-model="dateVisible" title="选择交易日期" width="400px" center> <el-dialog v-model="dateVisible"
title="选择交易日期"
width="400px"
center>
<div class="text-center"> <div class="text-center">
<el-date-picker v-model="diaDate" type="date" /> <el-date-picker v-model="diaDate"
type="date" />
</div> </div>
<template #footer> <template #footer>
<span class="flex justify-center"> <span class="flex justify-center">
<div class="dia-btn cancel" @click="dateVisible = false">取消</div> <div class="dia-btn mr-3 cancel"
<div class="dia-btn" @click="submitDate">确定</div> @click="dateVisible = false">取消</div>
<div class="dia-btn"
@click="submitDate">确定</div>
</span> </span>
</template> </template>
</el-dialog> </el-dialog>
@ -150,8 +155,11 @@ import dayjs from 'dayjs';
import { getNow } from '@/utils/common'; import { getNow } from '@/utils/common';
const router = useRouter(); const router = useRouter();
const route = useRoute();
const projectId = +Cookies.get('sand-projectId'); const projectId = +Cookies.get('sand-projectId');
const levelId = +Cookies.get('sand-level'); const levelId = +Cookies.get('sand-level');
const collected = ref<boolean>(false);
const curLevel = ref<number | string>('');
const dateVisible = ref<boolean>(false); const dateVisible = ref<boolean>(false);
const date = ref<string>(dayjs(new Date()).format('YYYY-MM-DD')); const date = ref<string>(dayjs(new Date()).format('YYYY-MM-DD'));
const diaDate = ref<string>(dayjs(new Date()).format('YYYY-M-D')); const diaDate = ref<string>(dayjs(new Date()).format('YYYY-M-D'));
@ -220,7 +228,11 @@ const ranks = ref<Record<string, any>[]>([
money: '沙盘名称', money: '沙盘名称',
}, },
]); ]);
//
const getLevel = async () => {
const { data } = await checkPointList(1);
levels.value = data;
};
// //
const selectRole = (id: number) => { const selectRole = (id: number) => {
let path = `/product/bank?type=0&i=1&role=${id}`; let path = `/product/bank?type=0&i=1&role=${id}`;
@ -320,11 +332,3 @@ onMounted(async () => {
} }
} }
</style> </style>
<style>
@media screen and (-webkit-min-device-pixel-ratio: 1.5), screen and (min-resolution: 144dpi) {
body {
zoom: 1;
}
}
</style>

@ -4,27 +4,45 @@
<dl> <dl>
<dt>担保方式</dt> <dt>担保方式</dt>
<div class="vals"> <div class="vals">
<dd v-for="(item, i) in methods" :key="i" :class="{ active: method === item.id }" @click="filterClick(item, 'method')">{{ item.name }}</dd> <dd v-for="(item, i) in methods"
:key="i"
:class="{ active: method === item.id }"
@click="filterClick(item, 'method')">{{ item.name }}</dd>
</div> </div>
</dl> </dl>
<dl> <dl>
<dt>产品进度</dt> <dt>产品进度</dt>
<div class="vals"> <div class="vals">
<dd v-for="(item, i) in methods" :key="i" :class="{ active: method === item.id }" @click="filterClick(item, 'method')">{{ item.name }}</dd> <dd v-for="(item, i) in methods"
:key="i"
:class="{ active: method === item.id }"
@click="filterClick(item, 'method')">{{ item.name }}</dd>
</div> </div>
</dl> </dl>
</div> </div>
<div class="block mt-3"> <div class="block mt-3">
<div class="search"> <div class="search">
<input type="text" placeholder="搜索" maxlength="20" /> <input type="text"
<img src="@/assets/images/search.png" alt="" class="icon" /> placeholder="搜索"
maxlength="20" />
<img src="@/assets/images/search.png"
alt=""
class="icon" />
</div> </div>
<el-table ref="table" v-loading="loading" :data="data" @selection-change="(rows) => (selection = rows)" @sort-change="handleSort"> <el-table ref="table"
<el-table-column type="selection" :selectable="deletable" width="50"></el-table-column> v-loading="loading"
<el-table-column property="id" label="ID" width="64" sortable="custom"></el-table-column> :data="data"
@selection-change="(rows) => (selection = rows)"
@sort-change="handleSort">
<el-table-column type="selection"
:selectable="deletable"
width="50"></el-table-column>
<el-table-column property="id"
label="ID"
width="64"
sortable="custom"></el-table-column>
</el-table> </el-table>
<el-pagination <el-pagination v-model:currentPage="currentPage"
v-model:currentPage="currentPage"
v-model:pageSize="pageSize" v-model:pageSize="pageSize"
:total="total" :total="total"
:page-sizes="pageSizes" :page-sizes="pageSizes"
@ -33,8 +51,7 @@
@current-change="fetchData()" @current-change="fetchData()"
small small
background background
class="px-3 py-2 justify-end" class="px-3 py-2 justify-end"></el-pagination>
></el-pagination>
</div> </div>
</div> </div>
</template> </template>
@ -43,11 +60,13 @@
import { computed, onMounted, ref } from 'vue'; import { computed, onMounted, ref } from 'vue';
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';
import { Search } from '@element-plus/icons-vue'; import { Search } from '@element-plus/icons-vue';
import { useI18n } from 'vue-i18n';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { perm } from '@/store/useCurrentUser'; import { perm } from '@/store/useCurrentUser';
import { pageSizes, pageLayout, toParams, resetParams } from '@/utils/common'; import { pageSizes, pageLayout, toParams, resetParams } from '@/utils/common';
import { deleteUser, queryUserPage } from '@/api/user'; import { deleteUser, queryUserPage } from '@/api/user';
const { t } = useI18n();
const params = ref<any>({}); const params = ref<any>({});
const sort = ref<any>(); const sort = ref<any>();
const currentPage = ref<number>(1); const currentPage = ref<number>(1);
@ -110,6 +129,7 @@ const handleEdit = (id: number) => {
const handleDelete = async (ids: number[]) => { const handleDelete = async (ids: number[]) => {
await deleteUser(ids); await deleteUser(ids);
fetchData(); fetchData();
ElMessage.success(t('success'));
}; };
const deletable = (bean: any) => bean.id > 1; const deletable = (bean: any) => bean.id > 1;
</script> </script>

@ -1,18 +1,30 @@
<template> <template>
<div class="block"> <div class="block">
<div class="flex justify-between items-center mb-5"> <div class="flex justify-between items-center mb-5">
<search v-model="params.customsPassName" @change="getList"></search> <search v-model="params.customsPassName"
@change="getList"></search>
<div class="filter"> <div class="filter">
<div class="select"> <div class="select">
<el-select v-model="params.isEnable" placeholder="启用状态" size="large" clearable> <el-select v-model="params.isEnable"
<el-option v-for="item in enables" :key="item.id" :label="item.name" :value="item.id" /> placeholder="启用状态"
size="large"
clearable>
<el-option v-for="item in enables"
:key="item.id"
:label="item.name"
:value="item.id" />
</el-select> </el-select>
<img src="@/assets/images/7.png" alt="" class="icon" /> <img src="@/assets/images/7.png"
alt=""
class="icon" />
</div> </div>
<el-popconfirm :title="delTitle" :disabled="!multipleSelection.length" @confirm.stop="delAll"> <el-popconfirm :title="delTitle"
:disabled="!multipleSelection.length"
@confirm.stop="delAll">
<template #reference> <template #reference>
<div :class="['add-btn mr-2', {'cursor-not-allowed': !multipleSelection.length}]"> <div :class="['add-btn mr-2', {'cursor-not-allowed': !multipleSelection.length}]">
<el-icon :size="24" color="#fff"> <el-icon :size="24"
color="#fff">
<Delete /> <Delete />
</el-icon> </el-icon>
删除关卡 删除关卡
@ -20,73 +32,119 @@
</template> </template>
</el-popconfirm> </el-popconfirm>
<div class="add-btn" @click="toAdd"> <div class="add-btn"
<img src="@/assets/images/plus.png" alt="" class="icon" /> @click="toAdd">
<img src="@/assets/images/plus.png"
alt=""
class="icon" />
新增关卡 新增关卡
</div> </div>
</div> </div>
</div> </div>
<div class="h-[calc(100vh-310px)] overflow-auto" id="tableWrap"> <div class="h-[calc(100vh-310px)] overflow-auto"
<el-table v-loading="loading" :data="list" @sort-change="handleSort" @selection-change="handleSelectionChange"> id="tableWrap">
<el-table-column label="移动" width="80"> <el-table v-loading="loading"
:data="list"
@selection-change="handleSelectionChange">
<el-table-column label="移动"
width="80">
<template #default="{ row, $index }"> <template #default="{ row, $index }">
<el-popover placement="right" trigger="hover"> <el-popover placement="right"
trigger="hover">
<template #reference> <template #reference>
<el-icon class="rotate-90 cursor-pointer" :size="16" color="#877c7c"> <el-icon class="rotate-90 cursor-pointer"
:size="16"
color="#877c7c">
<MoreFilled /> <MoreFilled />
</el-icon> </el-icon>
</template> </template>
<div class="flex items-center mb-2"> <div class="flex items-center mb-2">
<el-input class="w-[80px]" placeholder="请输入" v-model.number="row.serial"></el-input> <el-input class="w-[80px]"
<el-icon class="ml-2 cursor-pointer" :size="16" color="#877c7c" @click.stop="submitSerial(row, $index, row.serial)"> placeholder="请输入"
v-model.number="row.serial"></el-input>
<el-icon class="ml-2 cursor-pointer"
:size="16"
color="#877c7c"
@click.stop="submitSerial(row, $index, row.serial)">
<Check /> <Check />
</el-icon> </el-icon>
</div> </div>
<p class="my-2 cursor-pointer" @click="submitSerial(row, $index, 1)">置顶</p> <p class="my-2 cursor-pointer"
<p class="cursor-pointer" @click="submitSerial(row, $index, list.length)">置底</p> @click="submitSerial(row, $index, 1)">置顶</p>
<p class="cursor-pointer"
@click="submitSerial(row, $index, list.length)">置底</p>
</el-popover> </el-popover>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column type="selection" width="55" /> <el-table-column type="selection"
<el-table-column label="序号" width="80" align="center"> width="55" />
<el-table-column label="序号"
width="80"
align="center">
<template #default="{ row, $index }"> <template #default="{ row, $index }">
{{ $index + 1 }} {{ $index + 1 }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="customsPassName" min-width="200" label="关卡名称"> <el-table-column prop="customsPassName"
min-width="200"
label="关卡名称">
<template #default="{ row }"> <template #default="{ row }">
<div class="flex items-center"> <div class="flex items-center">
<el-input v-if="row.editing" class="w-[250px]" placeholder="请输入关卡名称" v-model="row.customsPassName"></el-input> <el-input v-if="row.editing"
class="w-[250px]"
placeholder="请输入关卡名称"
v-model="row.customsPassName"></el-input>
<span v-else>{{ row.customsPassName }}</span> <span v-else>{{ row.customsPassName }}</span>
<el-icon v-if="!row.editing" class="mx-2 cursor-pointer" :size="16" color="#877c7c" @click="edit(row)"> <el-icon v-if="!row.editing"
class="mx-2 cursor-pointer"
:size="16"
color="#877c7c"
@click="edit(row)">
<Edit /> <Edit />
</el-icon> </el-icon>
<el-icon v-if="row.editing" class="ml-2 cursor-pointer" :size="16" color="#877c7c" @click="cancel(row)"> <el-icon v-if="row.editing"
class="ml-2 cursor-pointer"
:size="16"
color="#877c7c"
@click="cancel(row)">
<Close /> <Close />
</el-icon> </el-icon>
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="creator" min-width="170" label="创建人"></el-table-column> <el-table-column prop="creator"
<el-table-column prop="createTime" label="创建时间" width="180" sortable="custom"></el-table-column> min-width="170"
<el-table-column label="启用关卡" width="140" align="center"> label="创建人"></el-table-column>
<el-table-column prop="createTime"
label="创建时间"
width="180"
sortable="custom"></el-table-column>
<el-table-column label="启用关卡"
width="140"
align="center">
<template #default="{ row }"> <template #default="{ row }">
<el-popconfirm <el-popconfirm v-if="row.checkpointId"
v-if="row.checkpointId"
:title="row.isEnable ? '禁用关卡后,已关联该关卡的判分点将被禁用,确定禁用吗?' : '启用关卡后,已关联该关卡的判分点将被启用,确定启用吗?'" :title="row.isEnable ? '禁用关卡后,已关联该关卡的判分点将被禁用,确定禁用吗?' : '启用关卡后,已关联该关卡的判分点将被启用,确定启用吗?'"
@confirm.stop="enableSubmit(row)" @confirm.stop="enableSubmit(row)">
>
<template #reference> <template #reference>
<el-switch v-if="row.checkpointId" v-model="row.isEnable" :active-value="1" :inactive-value="0" @change="enableChange(row)" /> <el-switch v-if="row.checkpointId"
v-model="row.isEnable"
:active-value="1"
:inactive-value="0"
@change="enableChange(row)" />
</template> </template>
</el-popconfirm> </el-popconfirm>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" width="120"> <el-table-column label="操作"
width="120">
<template #default="{ row }"> <template #default="{ row }">
<el-popconfirm v-if="row.checkpointId" :title="delTitle" @confirm.stop="handleDelete([row])"> <el-popconfirm v-if="row.checkpointId"
:title="delTitle"
@confirm.stop="handleDelete([row])">
<template #reference> <template #reference>
<el-button type="text" size="small">删除</el-button> <el-button type="text"
size="small">删除</el-button>
</template> </template>
</el-popconfirm> </el-popconfirm>
</template> </template>
@ -94,7 +152,11 @@
</el-table> </el-table>
</div> </div>
<div class="filter flex justify-end mt-3"> <div class="filter flex justify-end mt-3">
<div :class="['add-btn', { 'not-allow': !hadChange }]" @click="save" v-loading="saveLoading">保存</div> <div :class="['add-btn', {'not-allow': !hadChange}]"
@click="save"
v-loading="saveLoading">
保存
</div>
</div> </div>
</div> </div>
</template> </template>
@ -113,9 +175,8 @@ const route = useRoute();
const projectId = +Cookies.get('sand-projectId'); const projectId = +Cookies.get('sand-projectId');
const levelId = +Cookies.get('sand-level'); const levelId = +Cookies.get('sand-level');
const hadChange = ref<number>(0); const hadChange = ref<number>(0);
const delTitle = '删除关卡后,已关联该关卡的判分点将被禁用,确定删除吗?'; const delTitle: string = '删除关卡后,已关联该关卡的判分点将被禁用,确定删除吗?';
const params = reactive({ const params = reactive({
createDateSort: '',
platformId: 3, platformId: 3,
customsPassName: '', customsPassName: '',
isEnable: '', isEnable: '',
@ -152,9 +213,6 @@ onMounted(() => {
}); });
watch([params, () => route.query], getList); watch([params, () => route.query], getList);
const handleSort = ({ column, prop, order }: { column: any; prop: string; order: string }) => {
params.createDateSort = order === 'descending' ? 'desc' : order === 'ascending' ? 'asc' : '';
};
// //
const save = async () => { const save = async () => {
@ -176,7 +234,7 @@ const save = async () => {
await updatePass(param); await updatePass(param);
// //
const addList = list.value.filter((e) => !e.checkpointId && e.customsPassName); const addList = list.value.filter((e) => !e.checkpointId && e.customsPassName);
if (addList.length) await savePass(addList); await savePass(addList);
getList(); getList();
ElMessage.success('保存成功!'); ElMessage.success('保存成功!');
saveLoading.value = false; saveLoading.value = false;

@ -1,71 +1,123 @@
<template> <template>
<div class="max-h-[calc(100vh-290px)] overflow-auto"> <div class="max-h-[calc(100vh-290px)] overflow-auto">
<h6 class="title">城市人口及年龄参数</h6> <h6 class="title">城市人口及年龄参数</h6>
<el-table class="c-table" :data="form.ageAduRatioList" :span-method="ageSpan" border> <el-table class="c-table"
<el-table-column prop="productName" label="城市总人口 (人)" align="center"> :data="form.ageAduRatioList"
:span-method="ageSpan"
border>
<el-table-column prop="productName"
label="城市总人口 (人)"
align="center">
<template #default="{ row }"> <template #default="{ row }">
<el-input v-model.number="row.totalPopulation" placeholder="请输入" type="number" min="0"></el-input> <el-input v-model.number="row.totalPopulation"
placeholder="请输入"
type="number"
min="0"></el-input>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="ageStage" label="年龄 (岁)" align="center"></el-table-column> <el-table-column prop="ageStage"
<el-table-column prop="ageRatio" label="年龄占比 (%)" align="center"> label="年龄 (岁)"
align="center"></el-table-column>
<el-table-column prop="ageRatio"
label="年龄占比 (%)"
align="center">
<template #default="{ row }"> <template #default="{ row }">
<el-input v-model="row.ageRatio" placeholder="请输入"></el-input> <el-input v-model="row.ageRatio"
placeholder="请输入"></el-input>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<h6 class="title mt-7">学历参数</h6> <h6 class="title mt-7">学历参数</h6>
<el-table class="c-table" :data="edus" :span-method="eduSpan" border> <el-table class="c-table"
<el-table-column prop="ageStage" label="年龄 (岁)" align="center"> </el-table-column> :data="edus"
<el-table-column prop="education" label="学历" align="center"></el-table-column> :span-method="eduSpan"
<el-table-column prop="parentIds" label="学历占比 (%)" align="center"> border>
<el-table-column prop="ageStage"
label="年龄 (岁)"
align="center"> </el-table-column>
<el-table-column prop="education"
label="学历"
align="center"></el-table-column>
<el-table-column prop="parentIds"
label="学历占比 (%)"
align="center">
<template #default="{ row }"> <template #default="{ row }">
<el-input v-model="row.eduRatio" placeholder="请输入"></el-input> <el-input v-model="row.eduRatio"
placeholder="请输入"></el-input>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<h6 class="title mt-7">企业数量配置</h6> <h6 class="title mt-7">企业数量配置</h6>
<el-table class="c-table" :data="form.enterpriseNumList" border> <el-table class="c-table"
<el-table-column prop="productName" label="企业类型" align="center"> :data="form.enterpriseNumList"
border>
<el-table-column prop="productName"
label="企业类型"
align="center">
<template #default="{ row }"> <template #default="{ row }">
{{ row.enterpriseType == 1 ? '小微企业' : '创业企业' }} {{ row.enterpriseType == 1 ? '小微企业' : '创业企业' }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="parentIds" label="企业数量(家)" align="center"> <el-table-column prop="parentIds"
label="企业数量(家)"
align="center">
<template #default="{ row }"> <template #default="{ row }">
<el-input v-model.number="row.number" placeholder="请输入" maxlength="3" type="number" min="0"></el-input> <el-input v-model.number="row.number"
placeholder="请输入"
maxlength="3"
type="number"
min="0"></el-input>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<h6 class="title mt-7">单个商品每日需求量</h6> <h6 class="title mt-7">单个商品每日需求量</h6>
<el-table class="c-table" :data="form.commodityDemandList" border> <el-table class="c-table"
<el-table-column prop="typeName" label="买家类型" min-width="100" align="center"> </el-table-column> :data="form.commodityDemandList"
<el-table-column prop="parentIds" label="买家总占比 (%)" min-width="100" align="center"> border>
<el-table-column prop="typeName"
label="买家类型"
min-width="100"
align="center"> </el-table-column>
<el-table-column prop="parentIds"
label="买家总占比 (%)"
min-width="100"
align="center">
<template #default="{ row }"> <template #default="{ row }">
<el-input v-model="row.buyerRatio" placeholder="请输入"></el-input> <el-input v-model="row.buyerRatio"
placeholder="请输入"></el-input>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="parentIds" label="每次需求人数占比 (%)" min-width="130" align="center"> <el-table-column prop="parentIds"
label="每次需求人数占比 (%)"
min-width="130"
align="center">
<template #default="{ row }"> <template #default="{ row }">
<el-input v-model="row.peopleNumRatio" placeholder="请输入"></el-input> <el-input v-model="row.peopleNumRatio"
placeholder="请输入"></el-input>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="parentIds" label="单个买家单次需求" min-width="200" align="center"> <el-table-column prop="parentIds"
label="单个买家单次需求"
min-width="200"
align="center">
<template #default="{ row }"> <template #default="{ row }">
<div class="flex items-center"> <div class="flex items-center">
<template v-if="row.typeId == 7 || row.typeId == 8"> <template v-if="row.typeId == 7 || row.typeId == 8">
<span class="mr-2 whitespace-nowrap">一个保险产品一次最多购买</span> <span class="mr-2 whitespace-nowrap">一个保险产品一次最多购买</span>
<el-input v-model="row.buyerSingleDemand" placeholder="请输入"></el-input> <el-input v-model="row.buyerSingleDemand"
placeholder="请输入"></el-input>
<span class="ml-2"></span> <span class="ml-2"></span>
</template> </template>
<template v-else> <template v-else>
<span class="mr-2 whitespace-nowrap">{{ row.typeId == 9 ? '投入' : '贷款' }}</span> <span class="mr-2 whitespace-nowrap">{{ row.typeId == 9 ? '投入' : '贷款' }}</span>
<el-input v-model="row.buyerSingleDemandMin" placeholder="请输入"></el-input> <el-input v-model="row.buyerSingleDemandMin"
placeholder="请输入"></el-input>
<span class="mx-2">~</span> <span class="mx-2">~</span>
<el-input v-model="row.buyerSingleDemandMax" placeholder="请输入"></el-input> <el-input v-model="row.buyerSingleDemandMax"
placeholder="请输入"></el-input>
<span class="ml-2 whitespace-nowrap">万元</span> <span class="ml-2 whitespace-nowrap">万元</span>
</template> </template>
</div> </div>
@ -74,7 +126,10 @@
</el-table> </el-table>
<h6 class="title mt-7">系统账户起始金额</h6> <h6 class="title mt-7">系统账户起始金额</h6>
<el-form v-if="form.moneyAllocationList.length" class="w-[300px]" label-width="120px" label-suffix=":"> <el-form v-if="form.moneyAllocationList.length"
class="w-[300px]"
label-width="120px"
label-suffix=":">
<el-form-item label="银行账户"> <el-form-item label="银行账户">
<el-input v-model="form.moneyAllocationList[0].startingAmount"> <el-input v-model="form.moneyAllocationList[0].startingAmount">
<template #suffix> 万元 </template> <template #suffix> 万元 </template>
@ -94,7 +149,8 @@
</div> </div>
<div class="flex justify-end"> <div class="flex justify-end">
<div class="submit" @click="submit">保存修改</div> <div class="submit"
@click="submit">保存修改</div>
</div> </div>
</template> </template>

@ -1,6 +1,10 @@
<template> <template>
<div class="max-h-[calc(100vh-290px)] overflow-auto"> <div class="max-h-[calc(100vh-290px)] overflow-auto">
<el-form ref="formRef" label-width="100px" label-suffix="" class="form" status-icon> <el-form ref="formRef"
label-width="100px"
label-suffix=":"
class="form"
status-icon>
<h6 class="title">单个银行账户</h6> <h6 class="title">单个银行账户</h6>
<el-form-item label="收益计算"> <el-form-item label="收益计算">
<div> <div>
@ -13,7 +17,8 @@
<div class="flex-1"> <div class="flex-1">
<div class="flex items-center"> <div class="flex items-center">
<span class="text">起始金额</span> <span class="text">起始金额</span>
<el-input class="w-[140px] mx-2" v-model="form[0].startingAmount"></el-input> <el-input class="w-[140px] mx-2"
v-model="form[0].startingAmount"></el-input>
<span class="text">万元</span> <span class="text">万元</span>
</div> </div>
<p class="text mt-4">银行账户金额 = 起始金额 + 累计收益</p> <p class="text mt-4">银行账户金额 = 起始金额 + 累计收益</p>
@ -32,7 +37,8 @@
<div class="flex-1"> <div class="flex-1">
<div class="flex items-center"> <div class="flex items-center">
<span class="text">起始金额</span> <span class="text">起始金额</span>
<el-input class="w-[140px] mx-2" v-model="form[1].startingAmount"></el-input> <el-input class="w-[140px] mx-2"
v-model="form[1].startingAmount"></el-input>
<span class="text">万元</span> <span class="text">万元</span>
</div> </div>
<p class="text mt-4">保险账户金额 = 起始金额 + 累计收益</p> <p class="text mt-4">保险账户金额 = 起始金额 + 累计收益</p>
@ -57,7 +63,8 @@
<div class="flex-1"> <div class="flex-1">
<div class="flex items-center"> <div class="flex items-center">
<span class="text">起始金额</span> <span class="text">起始金额</span>
<el-input class="w-[140px] mx-2" v-model="form[2].startingAmount"></el-input> <el-input class="w-[140px] mx-2"
v-model="form[2].startingAmount"></el-input>
<span class="text">万元</span> <span class="text">万元</span>
</div> </div>
<p class="text mt-4">基金账户金额 = 所有基金资产净值+起始金额 + 累计收益 - 购买渠道金额</p> <p class="text mt-4">基金账户金额 = 所有基金资产净值+起始金额 + 累计收益 - 购买渠道金额</p>
@ -67,7 +74,8 @@
</div> </div>
<div class="flex justify-end"> <div class="flex justify-end">
<div class="submit" @click="submit">保存修改</div> <div class="submit"
@click="submit">保存修改</div>
</div> </div>
</template> </template>

@ -1,14 +1,19 @@
<template> <template>
<div class="block"> <div class="block">
<el-tabs v-model="curTab" @tab-click="tabChange"> <el-tabs v-model="curTab"
<el-tab-pane label="系统买方" name="tab1"> @tab-click="tabChange">
<el-tab-pane label="系统买方"
name="tab1">
<buyer /> <buyer />
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="金融市场" name="tab2"> <el-tab-pane label="金融市场"
name="tab2">
<Financial /> <Financial />
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="扫单配置" name="tab3"> </el-tab-pane> <el-tab-pane label="扫单配置"
<el-tab-pane label="渠道广告" name="tab4"> </el-tab-pane> name="tab3"> </el-tab-pane>
<el-tab-pane label="渠道广告"
name="tab4"> </el-tab-pane>
</el-tabs> </el-tabs>
</div> </div>
</template> </template>

@ -9,17 +9,23 @@
<li> <li>
<h6 class="title mb-[10px]">收入总额</h6> <h6 class="title mb-[10px]">收入总额</h6>
<p class="text-lg text-[#F08B14]">696,652,660,000.00</p> <p class="text-lg text-[#F08B14]">696,652,660,000.00</p>
<img src="@/assets/images/account/1.png" alt="" class="bg" /> <img src="@/assets/images/account/1.png"
alt=""
class="bg" />
</li> </li>
<li> <li>
<h6 class="title mb-[10px]">支出总额</h6> <h6 class="title mb-[10px]">支出总额</h6>
<p class="text-lg text-[#4488F4]">696,652,660,000.00</p> <p class="text-lg text-[#4488F4]">696,652,660,000.00</p>
<img src="@/assets/images/account/2.png" alt="" class="bg" /> <img src="@/assets/images/account/2.png"
alt=""
class="bg" />
</li> </li>
<li> <li>
<h6 class="title mb-[10px]">累计收益</h6> <h6 class="title mb-[10px]">累计收益</h6>
<p class="text-lg text-[#FE4953]">696,652,660,000.00</p> <p class="text-lg text-[#FE4953]">696,652,660,000.00</p>
<img src="@/assets/images/account/3.png" alt="" class="bg" /> <img src="@/assets/images/account/3.png"
alt=""
class="bg" />
</li> </li>
</ul> </ul>
</div> </div>
@ -34,7 +40,8 @@
<div> <div>
<div class="flex items-center mb-[75px]"> <div class="flex items-center mb-[75px]">
<div class="icon mr-2"> <div class="icon mr-2">
<img src="@/assets/images/account/6.png" alt="" /> <img src="@/assets/images/account/6.png"
alt="" />
</div> </div>
<div class=""> <div class="">
<h6 class="mb-2 text-base text-[#333]">银行账户</h6> <h6 class="mb-2 text-base text-[#333]">银行账户</h6>
@ -43,7 +50,8 @@
</div> </div>
<div class="flex items-center mb-[75px]"> <div class="flex items-center mb-[75px]">
<div class="icon mr-2"> <div class="icon mr-2">
<img src="@/assets/images/account/7.png" alt="" /> <img src="@/assets/images/account/7.png"
alt="" />
</div> </div>
<div class=""> <div class="">
@ -53,7 +61,8 @@
</div> </div>
<div class="flex items-center"> <div class="flex items-center">
<div class="icon mr-2"> <div class="icon mr-2">
<img src="@/assets/images/account/8.png" alt="" /> <img src="@/assets/images/account/8.png"
alt="" />
</div> </div>
<div class=""> <div class="">
<h6 class="mb-2 text-base text-[#333]">基金账户</h6> <h6 class="mb-2 text-base text-[#333]">基金账户</h6>
@ -72,7 +81,8 @@
<p class="text-[#448ACA] font-semibold">3656631.00</p> <p class="text-[#448ACA] font-semibold">3656631.00</p>
</div> </div>
<div class="icon blue ml-2"> <div class="icon blue ml-2">
<img src="@/assets/images/account/9.png" alt="" /> <img src="@/assets/images/account/9.png"
alt="" />
</div> </div>
</div> </div>
<div class="flex items-center"> <div class="flex items-center">
@ -81,7 +91,8 @@
<p class="text-[#448ACA] font-semibold">3656631.00</p> <p class="text-[#448ACA] font-semibold">3656631.00</p>
</div> </div>
<div class="icon blue ml-2"> <div class="icon blue ml-2">
<img src="@/assets/images/account/10.png" alt="" /> <img src="@/assets/images/account/10.png"
alt="" />
</div> </div>
</div> </div>
</div> </div>

@ -5,42 +5,77 @@
<dl> <dl>
<dt>担保方式</dt> <dt>担保方式</dt>
<div class="vals"> <div class="vals">
<dd v-for="(item, i) in productTypes" :key="i" :class="{ active: params.productType === item.id }" @click="filterClick(item, 'productType')">{{ item.name }}</dd> <dd v-for="(item, i) in productTypes"
:key="i"
:class="{ active: params.productType === item.id }"
@click="filterClick(item, 'productType')">{{ item.name }}</dd>
</div> </div>
</dl> </dl>
<dl> <dl>
<dt>担保方式</dt> <dt>担保方式</dt>
<div class="vals"> <div class="vals">
<dd v-for="(item, i) in guarantees" :key="i" :class="{ active: params.guarantyStyleId === item.id }" @click="filterClick(item, 'guarantyStyleId')">{{ item.name }}</dd> <dd v-for="(item, i) in guarantees"
:key="i"
:class="{ active: params.guarantyStyleId === item.id }"
@click="filterClick(item, 'guarantyStyleId')">{{ item.name }}</dd>
</div> </div>
</dl> </dl>
<dl> <dl>
<dt>贷款期限</dt> <dt>贷款期限</dt>
<div class="vals"> <div class="vals">
<dd v-for="(item, i) in times" :key="i" :class="{ active: params.loanPeriod === item.name }" @click="filterClick(item, 'loanPeriod')">{{ item.name }}</dd> <dd v-for="(item, i) in times"
:key="i"
:class="{ active: params.loanPeriod === item.name }"
@click="filterClick(item, 'loanPeriod')">{{ item.name }}</dd>
</div> </div>
</dl> </dl>
<dl> <dl>
<dt>贷款额度</dt> <dt>贷款额度</dt>
<div class="vals"> <div class="vals">
<dd v-for="(item, i) in moneys" :key="i" :class="{ active: params.loanLimit === item.name }" @click="filterClick(item, 'loanLimit')">{{ item.name }}</dd> <dd v-for="(item, i) in moneys"
:key="i"
:class="{ active: params.loanLimit === item.name }"
@click="filterClick(item, 'loanLimit')">{{ item.name }}</dd>
</div> </div>
</dl> </dl>
</div> </div>
<div class="block mt-3"> <div class="block mt-3">
<div class="search mb-2"> <div class="search mb-2">
<input type="text" v-model="params.keyWord" placeholder="搜索" maxlength="20" /> <input type="text"
<img src="@/assets/images/search.png" alt="" class="icon" /> v-model="params.keyWord"
placeholder="搜索"
maxlength="20" />
<img src="@/assets/images/search.png"
alt=""
class="icon" />
</div> </div>
<el-table ref="table" v-loading="loading" :data="list"> <el-table ref="table"
<el-table-column prop="productName" label="贷款名称" min-width="110"></el-table-column> v-loading="loading"
<el-table-column prop="productTypeText" label="贷款对象" min-width="80"></el-table-column> :data="list">
<el-table-column prop="guarantyStyle" label="担保方式" min-width="80"></el-table-column> <el-table-column prop="productName"
<el-table-column prop="loanPeriod" label="贷款期限" min-width="80"></el-table-column> label="贷款名称"
<el-table-column prop="loanLimit" label="贷款额度" min-width="80"></el-table-column> min-width="110"></el-table-column>
<el-table-column prop="rateStandard" label="参考利率范围" min-width="80"></el-table-column> <el-table-column prop="productTypeText"
<el-table-column prop="userName" label="产品经理" min-width="80"></el-table-column> label="贷款对象"
<el-table-column prop="operationTime" label="发布日期" min-width="80"></el-table-column> min-width="80"></el-table-column>
<el-table-column prop="guarantyStyle"
label="担保方式"
min-width="80"></el-table-column>
<el-table-column prop="loanPeriod"
label="贷款期限"
min-width="80"></el-table-column>
<el-table-column prop="loanLimit"
label="贷款额度"
min-width="80"></el-table-column>
<el-table-column prop="rateStandard"
label="参考利率范围"
min-width="80"></el-table-column>
<el-table-column prop="userName"
label="产品经理"
min-width="80"></el-table-column>
<el-table-column prop="operationTime"
label="发布日期"
min-width="80"></el-table-column>
<!-- <el-table-column prop="id" <!-- <el-table-column prop="id"
label="操作" label="操作"
width="100"> width="100">
@ -50,8 +85,7 @@
@click="toDetail(row)">查看详情</el-button> @click="toDetail(row)">查看详情</el-button>
</template></el-table-column> --> </template></el-table-column> -->
</el-table> </el-table>
<el-pagination <el-pagination v-model:currentPage="currentPage"
v-model:currentPage="currentPage"
v-model:pageSize="pageSize" v-model:pageSize="pageSize"
:total="total" :total="total"
:page-sizes="pageSizes" :page-sizes="pageSizes"
@ -60,18 +94,18 @@
@current-change="getList()" @current-change="getList()"
small small
background background
class="px-3 py-2 justify-end" class="px-3 py-2 justify-end"></el-pagination>
></el-pagination>
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, ref, reactive, watch } from 'vue'; import { onMounted, ref, reactive, watch } from 'vue';
import { pageSizes, pageLayout, toParams, getIds } from '@/utils/common'; import { pageSizes, pageLayout, toParams } from '@/utils/common';
import { getProcessInformationBasedOnRoles } from '@/api/judgment'; import { getProcessInformationBasedOnRoles } from '@/api/judgment';
import { bankingProductMarketsList } from '@/api/finance'; import { bankingProductMarketsList } from '@/api/finance';
import { useRouter, useRoute } from 'vue-router'; import { useRouter, useRoute } from 'vue-router';
import Cookies from 'js-cookie';
const router = useRouter(); const router = useRouter();
const route = useRoute(); const route = useRoute();
@ -137,8 +171,9 @@ const moneys = ref<Record<string, any>[]>([
name: '>1000万', name: '>1000万',
}, },
]); ]);
const projectId = +Cookies.get('sand-projectId');
const levelId = +Cookies.get('sand-level');
const params = reactive({ const params = reactive({
...getIds(),
createDateSort: '', createDateSort: '',
guarantyStyleId: '', guarantyStyleId: '',
keyWord: '', keyWord: '',
@ -146,6 +181,8 @@ const params = reactive({
status: '', status: '',
loanPeriod: '不限', loanPeriod: '不限',
loanLimit: '不限', loanLimit: '不限',
checkPointId: levelId,
projectId,
}); });
const currentPage = ref<number>(1); const currentPage = ref<number>(1);
const pageSize = ref<number>(10); const pageSize = ref<number>(10);

@ -49,8 +49,7 @@
</div> </div>
<div class="line"> <div class="line">
<span class="label">贷款用途</span> <span class="label">贷款用途</span>
<div class="val"> <div class="val">{{
{{
info.loanPurpose === '购房' info.loanPurpose === '购房'
? '可用于住房按揭贷款、二手房住房按揭贷款。' ? '可用于住房按揭贷款、二手房住房按揭贷款。'
: info.loanPurpose === '消费' : info.loanPurpose === '消费'
@ -62,8 +61,7 @@
: info.loanPurpose === '经营' && info.productType : info.loanPurpose === '经营' && info.productType
? '用于短期生产经营周转的可循环的人民币信用贷款业务。' ? '用于短期生产经营周转的可循环的人民币信用贷款业务。'
: info.otherPurposesOfLoan : info.otherPurposesOfLoan
}} }}</div>
</div>
</div> </div>
<div class="line"> <div class="line">
<span class="label">还款方式</span> <span class="label">还款方式</span>
@ -83,43 +81,71 @@
<span class="label">材料要求</span> <span class="label">材料要求</span>
<div class="val"> <div class="val">
<p class="text">办理账户-提供材料{{ riskInfo?.accountMaterials }}</p> <p class="text">办理账户-提供材料{{ riskInfo?.accountMaterials }}</p>
<p v-if="riskInfo?.sendingAccount" class="text">办理账户-发放账户借记卡或放款专户</p> <p v-if="riskInfo?.sendingAccount"
<p v-if="riskInfo?.loanApplicationMethod" class="text">贷款申请-申请方式{{ riskInfo?.loanApplicationMethod }}</p> class="text">办理账户-发放账户借记卡或放款专户</p>
<p v-if="riskInfo?.borrowerMaterial" class="text">贷款申请-提供材料-借款人材料{{ riskInfo?.borrowerMaterial }}</p> <p v-if="riskInfo?.loanApplicationMethod"
class="text">贷款申请-申请方式{{ riskInfo?.loanApplicationMethod }}</p>
<p v-if="riskInfo?.borrowerMaterial"
class="text">贷款申请-提供材料-借款人材料{{ riskInfo?.borrowerMaterial }}</p>
<template v-if="info.productType"> <template v-if="info.productType">
<p v-if="riskInfo?.enterpriseMaterial" class="text">贷款申请-提供材料-企业材料{{ riskInfo?.enterpriseMaterial }}</p> <p v-if="riskInfo?.enterpriseMaterial"
<p v-if="riskInfo?.collateral" class="text">贷款申请-提供材料-抵押物{{ riskInfo?.collateral }}</p> class="text">贷款申请-提供材料-企业材料{{ riskInfo?.enterpriseMaterial }}</p>
<p v-if="riskInfo?.collateral"
class="text">贷款申请-提供材料-抵押物{{ riskInfo?.collateral }}</p>
</template> </template>
<template v-else> <template v-else>
<p v-if="riskInfo?.mateMaterial" class="text">贷款申请-提供材料-配偶材料{{ riskInfo?.mateMaterial }}</p> <p v-if="riskInfo?.mateMaterial"
<p v-if="riskInfo?.businessMaterials" class="text">贷款申请-提供材料-经营类材料{{ riskInfo?.businessMaterials }}</p> class="text">贷款申请-提供材料-配偶材料{{ riskInfo?.mateMaterial }}</p>
<p v-if="riskInfo?.businessMaterials"
class="text">贷款申请-提供材料-经营类材料{{ riskInfo?.businessMaterials }}</p>
</template> </template>
<p v-if="riskInfo?.supplementaryMaterials" class="text">贷款申请-提供材料-补充材料{{ riskInfo?.supplementaryMaterials }}</p> <p v-if="riskInfo?.supplementaryMaterials"
<p v-if="riskInfo?.runBatchObject" class="text">系统跑批准入风控策略-跑批对象{{ riskInfo?.runBatchObject }}</p> class="text">贷款申请-提供材料-补充材料{{ riskInfo?.supplementaryMaterials }}</p>
<p v-if="riskInfo?.accessStrategy" class="text">系统跑批准入风控策略-准入策略{{ riskInfo?.accessStrategy }}</p> <p v-if="riskInfo?.runBatchObject"
<p v-if="riskInfo?.personalCreditScoringStrategies" class="text">系统跑批准入风控策略-信用评分策略{{ riskInfo?.personalCreditScoringStrategies }}</p> class="text">系统跑批准入风控策略-跑批对象{{ riskInfo?.runBatchObject }}</p>
<p v-if="riskInfo?.riskDegreeStrategy" class="text">系统跑批准入风控策略-风险度策略{{ riskInfo?.riskDegreeStrategy }}</p> <p v-if="riskInfo?.accessStrategy"
<p v-if="riskInfo?.interestRatePricingModel" class="text">系统跑批准入风控策略-利率定价模型-个人额度模型{{ riskInfo?.interestRatePricingModel }}</p> class="text">系统跑批准入风控策略-准入策略{{ riskInfo?.accessStrategy }}</p>
<p v-if="riskInfo?.individualInterestRateModel" class="text">系统跑批准入风控策略-利率定价模型-个人利率模型</p> <p v-if="riskInfo?.personalCreditScoringStrategies"
<p v-if="riskInfo?.dueDiligenceMode" class="text">尽职调查-尽调方式{{ riskInfo?.dueDiligenceMode }}</p> class="text">系统跑批准入风控策略-信用评分策略{{ riskInfo?.personalCreditScoringStrategies }}</p>
<p v-if="riskInfo?.dueDiligenceContent" class="text">尽职调查-尽调内容{{ riskInfo?.dueDiligenceContent }}</p> <p v-if="riskInfo?.riskDegreeStrategy"
<p v-if="riskInfo?.reviewContent" class="text">贷款审查-审查内容{{ riskInfo?.reviewContent }}</p> class="text">系统跑批准入风控策略-风险度策略{{ riskInfo?.riskDegreeStrategy }}</p>
<p v-if="riskInfo?.reviewSignature" class="text">贷款审查-审查签字{{ riskInfo?.reviewSignature }}</p> <p v-if="riskInfo?.interestRatePricingModel"
<p v-if="riskInfo?.reviewApproveContent" class="text">贷款审批-审批内容{{ riskInfo?.reviewApproveContent }}</p> class="text">系统跑批准入风控策略-利率定价模型-个人额度模型{{ riskInfo?.interestRatePricingModel }}</p>
<p v-if="riskInfo?.approvalSignature" class="text">贷款审批-审批签字{{ riskInfo?.approvalSignature }}</p> <p v-if="riskInfo?.individualInterestRateModel"
<p v-if="riskInfo?.contractMaterials" class="text">签订合同-提供的材料{{ riskInfo?.contractMaterials }}</p> class="text">系统跑批准入风控策略-利率定价模型-个人利率模型</p>
<p v-if="riskInfo?.loanContract" class="text">签订合同-合同模板-借贷合同{{ riskInfo?.loanContract }}</p> <p v-if="riskInfo?.dueDiligenceMode"
<p v-if="riskInfo?.mortgageContract" class="text">签订合同-合同模板-抵押合同{{ riskInfo?.mortgageContract }}</p> class="text">尽职调查-尽调方式{{ riskInfo?.dueDiligenceMode }}</p>
<p v-if="riskInfo?.pledgeContract" class="text">签订合同-合同模板-质押合同{{ riskInfo?.pledgeContract }}</p> <p v-if="riskInfo?.dueDiligenceContent"
<p v-if="riskInfo?.guaranteeContract" class="text">签订合同-合同模板-担保合同{{ riskInfo?.guaranteeContract }}</p> class="text">尽职调查-尽调内容{{ riskInfo?.dueDiligenceContent }}</p>
<p v-if="riskInfo?.selectionStrategy" class="text">贷后管理-选择策略{{ riskInfo?.selectionStrategy }}</p> <p v-if="riskInfo?.reviewContent"
class="text">贷款审查-审查内容{{ riskInfo?.reviewContent }}</p>
<p v-if="riskInfo?.reviewSignature"
class="text">贷款审查-审查签字{{ riskInfo?.reviewSignature }}</p>
<p v-if="riskInfo?.reviewApproveContent"
class="text">贷款审批-审批内容{{ riskInfo?.reviewApproveContent }}</p>
<p v-if="riskInfo?.approvalSignature"
class="text">贷款审批-审批签字{{ riskInfo?.approvalSignature }}</p>
<p v-if="riskInfo?.contractMaterials"
class="text">签订合同-提供的材料{{ riskInfo?.contractMaterials }}</p>
<p v-if="riskInfo?.loanContract"
class="text">签订合同-合同模板-借贷合同{{ riskInfo?.loanContract }}</p>
<p v-if="riskInfo?.mortgageContract"
class="text">签订合同-合同模板-抵押合同{{ riskInfo?.mortgageContract }}</p>
<p v-if="riskInfo?.pledgeContract"
class="text">签订合同-合同模板-质押合同{{ riskInfo?.pledgeContract }}</p>
<p v-if="riskInfo?.guaranteeContract"
class="text">签订合同-合同模板-担保合同{{ riskInfo?.guaranteeContract }}</p>
<p v-if="riskInfo?.selectionStrategy"
class="text">贷后管理-选择策略{{ riskInfo?.selectionStrategy }}</p>
</div> </div>
</div> </div>
</div> </div>
<div class="user m-4"> <div class="user m-4">
<h6 class="mb-4 text-lg font-semibold text-[#01305B]">产品经理</h6> <h6 class="mb-4 text-lg font-semibold text-[#01305B]">产品经理</h6>
<div class="text-center"> <div class="text-center">
<img src="@/assets/images/bankDetail/4.png" alt="" class="mx-auto" /> <img src="@/assets/images/bankDetail/4.png"
alt=""
class="mx-auto" />
<p class="mt-2 font-semibold text-[#114575]">{{ userName }}</p> <p class="mt-2 font-semibold text-[#114575]">{{ userName }}</p>
</div> </div>
<p class="mt-2 text-[#114575]">财经学院 金融2班</p> <p class="mt-2 text-[#114575]">财经学院 金融2班</p>
@ -131,6 +157,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, watch, onMounted } from 'vue'; import { ref, computed, watch, onMounted } from 'vue';
import { findById } from '@/api/bank'; import { findById } from '@/api/bank';
import { getTheCurrentUserName } from '@/api/config';
import { useRouter, useRoute } from 'vue-router'; import { useRouter, useRoute } from 'vue-router';
import Back from '@/components/Back.vue'; import Back from '@/components/Back.vue';
@ -152,8 +179,14 @@ const getDetail = async () => {
} }
} }
}; };
//
const getName = async () => {
const res = await getTheCurrentUserName();
userName.value = res.userName;
};
onMounted(() => { onMounted(() => {
getDetail(); getDetail();
getName();
}); });
</script> </script>

@ -5,41 +5,77 @@
<dl> <dl>
<dt>申购费率</dt> <dt>申购费率</dt>
<div class="vals"> <div class="vals">
<dd v-for="(item, i) in rates" :key="i" :class="{ active: params.subscriptionRate === item.id }" @click="filterClick(item, 'subscriptionRate')">{{ item.name }}</dd> <dd v-for="(item, i) in rates"
:key="i"
:class="{ active: params.subscriptionRate === item.name }"
@click="filterClick(item, 'subscriptionRate')">{{ item.name }}</dd>
</div> </div>
</dl> </dl>
<dl> <dl>
<dt>成立年限</dt> <dt>成立年限</dt>
<div class="vals"> <div class="vals">
<dd v-for="(item, i) in times" :key="i" :class="{ active: params.establishmentPeriod === item.name }" @click="filterClick(item, 'establishmentPeriod')"> <dd v-for="(item, i) in times"
{{ item.name }} :key="i"
</dd> :class="{ active: params.establishmentPeriod === item.name }"
@click="filterClick(item, 'establishmentPeriod')">{{ item.name }}</dd>
</div> </div>
</dl> </dl>
<dl> <dl>
<dt>基金规模</dt> <dt>基金规模</dt>
<div class="vals"> <div class="vals">
<dd v-for="(item, i) in scales" :key="i" :class="{ active: params.fundraisingScale === item.name }" @click="filterClick(item, 'fundraisingScale')">{{ item.name }}</dd> <dd v-for="(item, i) in scales"
:key="i"
:class="{ active: params.fundraisingScale === item.name }"
@click="filterClick(item, 'fundraisingScale')">{{ item.name }}</dd>
</div> </div>
</dl> </dl>
</div> </div>
<div class="block mt-3"> <div class="block mt-3">
<div class="search mb-2"> <div class="search mb-2">
<input type="text" v-model="params.fundName" placeholder="搜索" maxlength="20" /> <input type="text"
<img src="@/assets/images/search.png" alt="" class="icon" /> v-model="params.fundName"
placeholder="搜索"
maxlength="20" />
<img src="@/assets/images/search.png"
alt=""
class="icon" />
</div> </div>
<el-table ref="table" v-loading="loading" :data="list"> <el-table ref="table"
<el-table-column prop="fundCode" label="基金代码" min-width="80"></el-table-column> v-loading="loading"
<el-table-column prop="fundName" label="基金名称" min-width="110"></el-table-column> :data="list">
<el-table-column prop="dailyNetAssetValue" label="最新单位净值" min-width="80"></el-table-column> <el-table-column prop="fundCode"
<el-table-column prop="dailyGrowthRate" label="日增长率(%)" min-width="80"></el-table-column> label="基金代码"
<el-table-column prop="weeklyGrowthRate" label="近1周(%)" min-width="80"></el-table-column> min-width="80"></el-table-column>
<el-table-column prop="monthlyGrowthRate" label="近1月(%)" min-width="80"></el-table-column> <el-table-column prop="fundName"
<el-table-column prop="growthRateInRecentMarch" label="近3月(%)" min-width="80"></el-table-column> label="基金名称"
<el-table-column prop="growthRateSinceInception" label="成立来(%)" min-width="80"></el-table-column> min-width="110"></el-table-column>
<el-table-column prop="fundraisingScale" label="基金规模(万元)" min-width="80"></el-table-column> <el-table-column prop="dailyNetAssetValue"
<el-table-column prop="userName" label="产品经理" min-width="80"></el-table-column> label="现净值"
<el-table-column prop="operationTime" label="发布日期" min-width="80"></el-table-column> min-width="80"></el-table-column>
<el-table-column prop="dailyGrowthRate"
label="日增长率(%)"
min-width="80"></el-table-column>
<el-table-column prop="weeklyGrowthRate"
label="近1周(%)"
min-width="80"></el-table-column>
<el-table-column prop="monthlyGrowthRate"
label="近1月(%)"
min-width="80"></el-table-column>
<el-table-column prop="growthRateInRecentMarch"
label="近3月(%)"
min-width="80"></el-table-column>
<el-table-column prop="growthRateSinceInception"
label="成立来(%)"
min-width="80"></el-table-column>
<el-table-column prop="fundraisingScale"
label="基金规模(万元)"
min-width="80"></el-table-column>
<el-table-column prop="userName"
label="产品经理"
min-width="80"></el-table-column>
<el-table-column prop="operationTime"
label="发布日期"
min-width="80"></el-table-column>
<!-- <el-table-column prop="id" <!-- <el-table-column prop="id"
label="操作" label="操作"
width="100"> width="100">
@ -49,8 +85,7 @@
@click="toDetail(row)">查看详情</el-button> @click="toDetail(row)">查看详情</el-button>
</template></el-table-column> --> </template></el-table-column> -->
</el-table> </el-table>
<el-pagination <el-pagination v-model:currentPage="currentPage"
v-model:currentPage="currentPage"
v-model:pageSize="pageSize" v-model:pageSize="pageSize"
:total="total" :total="total"
:page-sizes="pageSizes" :page-sizes="pageSizes"
@ -59,8 +94,7 @@
@current-change="getList()" @current-change="getList()"
small small
background background
class="px-3 py-2 justify-end" class="px-3 py-2 justify-end"></el-pagination>
></el-pagination>
</div> </div>
</div> </div>
</template> </template>
@ -68,7 +102,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, ref, reactive, watch } from 'vue'; import { onMounted, ref, reactive, watch } from 'vue';
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';
import { pageSizes, pageLayout, toParams, getIds } from '@/utils/common'; import { pageSizes, pageLayout, toParams } from '@/utils/common';
import { fundProductMarketList } from '@/api/finance'; import { fundProductMarketList } from '@/api/finance';
import { useRouter, useRoute } from 'vue-router'; import { useRouter, useRoute } from 'vue-router';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
@ -78,11 +112,12 @@ const route = useRoute();
const projectId = +Cookies.get('sand-projectId'); const projectId = +Cookies.get('sand-projectId');
const levelId = +Cookies.get('sand-level'); const levelId = +Cookies.get('sand-level');
const params = reactive({ const params = reactive({
...getIds(),
establishmentPeriod: '不限', establishmentPeriod: '不限',
fundraisingScale: '不限', fundraisingScale: '不限',
subscriptionRate: '不限', subscriptionRate: '不限',
fundName: '', fundName: '',
checkPointId: levelId,
projectId,
}); });
const currentPage = ref<number>(1); const currentPage = ref<number>(1);
const pageSize = ref<number>(10); const pageSize = ref<number>(10);
@ -90,19 +125,15 @@ const total = ref<number>(0);
const table = ref<any>(); const table = ref<any>();
const rates = ref<Record<string, any>[]>([ const rates = ref<Record<string, any>[]>([
{ {
id: '不限',
name: '不限', name: '不限',
}, },
{ {
id: 0,
name: '0', name: '0',
}, },
{ {
id: 1,
name: '0-0.5%', name: '0-0.5%',
}, },
{ {
id: 2,
name: '>0.5%', name: '>0.5%',
}, },
]); ]);
@ -166,9 +197,14 @@ onMounted(() => {
watch([params, () => route.query], initList); watch([params, () => route.query], initList);
// //
const filterClick = (item: Record<string, any>, key: string) => { const filterClick = (item: Record<string, any>, key: string) => {
params[key] = item[key === 'subscriptionRate' ? 'id' : 'name']; params[key] = item.name;
};
//
const toAdd = () => {
router.push({
path: `/product/fund/add`,
});
}; };
// //
const toDetail = async (path: string, id: number) => { const toDetail = async (path: string, id: number) => {
router.push(`${path}?id=${id}&name=${params.fundName}`); router.push(`${path}?id=${id}&name=${params.fundName}`);

@ -5,39 +5,71 @@
<dl> <dl>
<dt>险种分类</dt> <dt>险种分类</dt>
<div class="vals"> <div class="vals">
<dd v-for="(item, i) in insuranceTypes" :key="i" :class="{ active: params.insuranceType === item.id }" @click="filterClick(item, 'insuranceType')">{{ item.name }}</dd> <dd v-for="(item, i) in insuranceTypes"
:key="i"
:class="{ active: params.insuranceType === item.id }"
@click="filterClick(item, 'insuranceType')">{{ item.name }}</dd>
</div> </div>
</dl> </dl>
<dl v-if="params.insuranceType !== 311"> <dl v-if="params.insuranceType !== 311">
<dt>承保年龄</dt> <dt>承保年龄</dt>
<div class="vals"> <div class="vals">
<dd v-for="(item, i) in ages" :key="i" :class="{ active: params.coverageAge === item.id }" @click="filterClick(item, 'coverageAge')">{{ item.id }}</dd> <dd v-for="(item, i) in ages"
:key="i"
:class="{ active: params.coverageAge === item.id }"
@click="filterClick(item, 'coverageAge')">{{ item.id }}</dd>
</div> </div>
</dl> </dl>
<dl> <dl>
<dt>保险期限</dt> <dt>保险期限</dt>
<div class="vals"> <div class="vals">
<dd v-for="(item, i) in times" :key="i" :class="{ active: params.protectionPeriod === item.id }" @click="filterClick(item, 'protectionPeriod')">{{ item.id }}</dd> <dd v-for="(item, i) in times"
:key="i"
:class="{ active: params.protectionPeriod === item.id }"
@click="filterClick(item, 'protectionPeriod')">{{ item.id }}</dd>
</div> </div>
</dl> </dl>
</div> </div>
<div class="block mt-3"> <div class="block mt-3">
<div class="search mb-2"> <div class="search mb-2">
<input type="text" v-model="params.insuranceName" placeholder="搜索" maxlength="20" /> <input type="text"
<img src="@/assets/images/search.png" alt="" class="icon" /> v-model="params.insuranceName"
placeholder="搜索"
maxlength="20" />
<img src="@/assets/images/search.png"
alt=""
class="icon" />
</div> </div>
<el-table ref="table" v-loading="loading" :data="list"> <el-table ref="table"
<el-table-column prop="insuranceName" label="保险名称" min-width="110"></el-table-column> v-loading="loading"
<el-table-column prop="insuranceTypeText" label="险种分类" min-width="80"></el-table-column> :data="list">
<el-table-column v-if="params.insuranceType !== 311" prop="guarantyStyle" label="承保年龄" min-width="80"> <el-table-column prop="insuranceName"
label="保险名称"
min-width="110"></el-table-column>
<el-table-column prop="insuranceTypeText"
label="险种分类"
min-width="80"></el-table-column>
<el-table-column v-if="params.insuranceType !== 311"
prop="guarantyStyle"
label="承保年龄"
min-width="80">
<template #default="{ row }"> <template #default="{ row }">
{{ row.minimumAge ? row.minimumAge + '-' + row.maximumAge + '周岁' : '-' }} {{ row.minimumAge ? row.minimumAge + '-' + row.maximumAge + '周岁' : '-' }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="insuranceDeadline" label="保险期限" min-width="80"></el-table-column> <el-table-column prop="insuranceDeadline"
<el-table-column prop="premiumAmount" label="保费(元)" min-width="80"></el-table-column> label="保险期限"
<el-table-column prop="userName" label="产品经理" min-width="80"></el-table-column> min-width="80"></el-table-column>
<el-table-column prop="operationTime" label="发布日期" min-width="80"></el-table-column> <el-table-column prop="premiumAmount"
label="保费(元)"
min-width="80"></el-table-column>
<el-table-column prop="userName"
label="产品经理"
min-width="80"></el-table-column>
<el-table-column prop="operationTime"
label="发布日期"
min-width="80"></el-table-column>
<!-- <el-table-column prop="id" <!-- <el-table-column prop="id"
label="操作" label="操作"
width="100"> width="100">
@ -47,8 +79,7 @@
@click="toDetail(row)">查看详情</el-button> @click="toDetail(row)">查看详情</el-button>
</template></el-table-column> --> </template></el-table-column> -->
</el-table> </el-table>
<el-pagination <el-pagination v-model:currentPage="currentPage"
v-model:currentPage="currentPage"
v-model:pageSize="pageSize" v-model:pageSize="pageSize"
:total="total" :total="total"
:page-sizes="pageSizes" :page-sizes="pageSizes"
@ -57,26 +88,29 @@
@current-change="getList()" @current-change="getList()"
small small
background background
class="px-3 py-2 justify-end" class="px-3 py-2 justify-end"></el-pagination>
></el-pagination>
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, ref, reactive, watch } from 'vue'; import { onMounted, ref, reactive, watch } from 'vue';
import { pageSizes, pageLayout, toParams, getIds } from '@/utils/common'; import { pageSizes, pageLayout, toParams } from '@/utils/common';
import { insuranceMarketList } from '@/api/finance'; import { insuranceMarketList } from '@/api/finance';
import { useRouter, useRoute } from 'vue-router'; import { useRouter, useRoute } from 'vue-router';
import Cookies from 'js-cookie';
const router = useRouter(); const router = useRouter();
const route = useRoute(); const route = useRoute();
const projectId = +Cookies.get('sand-projectId');
const levelId = +Cookies.get('sand-level');
const params = reactive({ const params = reactive({
...getIds(),
insuranceName: '', insuranceName: '',
insuranceType: '', insuranceType: '',
coverageAge: '不限', coverageAge: '不限',
protectionPeriod: '不限', protectionPeriod: '不限',
checkPointId: levelId,
projectId,
}); });
const currentPage = ref<number>(1); const currentPage = ref<number>(1);
const pageSize = ref<number>(10); const pageSize = ref<number>(10);

@ -8,27 +8,46 @@
<h6 class="title mb-5">订单列表</h6> <h6 class="title mb-5">订单列表</h6>
<div class="flex justify-between mb-5"> <div class="flex justify-between mb-5">
<div class="search"> <div class="search">
<input type="text" placeholder="搜索" maxlength="20" /> <input type="text"
<img src="@/assets/images/search.png" alt="" class="icon" /> placeholder="搜索"
maxlength="20" />
<img src="@/assets/images/search.png"
alt=""
class="icon" />
</div> </div>
<div> <div>
<el-select v-model="form.status" placeholder="订单类型" size="large"> <el-select v-model="form.status"
<el-option v-for="item in paces" :key="item.id" :label="item.name" :value="item.id" /> placeholder="订单类型"
size="large">
<el-option v-for="item in paces"
:key="item.id"
:label="item.name"
:value="item.id" />
</el-select> </el-select>
</div> </div>
</div> </div>
<el-table ref="table" v-loading="loading" :data="data"> <el-table ref="table"
<el-table-column type="selection" :selectable="deletable" width="50"></el-table-column> v-loading="loading"
<el-table-column prop="parentIds" label="订单号"></el-table-column> :data="data">
<el-table-column prop="parentIds" label="产品名称"></el-table-column> <el-table-column type="selection"
<el-table-column prop="parentIds" label="订单类型"></el-table-column> :selectable="deletable"
<el-table-column prop="parentIds" label="成交情况"></el-table-column> width="50"></el-table-column>
<el-table-column prop="parentIds" label="成交日期"></el-table-column> <el-table-column prop="parentIds"
<el-table-column prop="parentIds" label="订单情况 "></el-table-column> label="订单号"></el-table-column>
<el-table-column prop="parentIds" label="备注"></el-table-column> <el-table-column prop="parentIds"
label="产品名称"></el-table-column>
<el-table-column prop="parentIds"
label="订单类型"></el-table-column>
<el-table-column prop="parentIds"
label="成交情况"></el-table-column>
<el-table-column prop="parentIds"
label="成交日期"></el-table-column>
<el-table-column prop="parentIds"
label="订单情况 "></el-table-column>
<el-table-column prop="parentIds"
label="备注"></el-table-column>
</el-table> </el-table>
<el-pagination <el-pagination v-model:currentPage="currentPage"
v-model:currentPage="currentPage"
v-model:pageSize="pageSize" v-model:pageSize="pageSize"
:total="total" :total="total"
:page-sizes="pageSizes" :page-sizes="pageSizes"
@ -37,8 +56,7 @@
@current-change="fetchData()" @current-change="fetchData()"
small small
background background
class="px-3 py-2 justify-end" class="px-3 py-2 justify-end"></el-pagination>
></el-pagination>
</div> </div>
</div> </div>
</template> </template>

@ -6,25 +6,34 @@
<div class="line"> <div class="line">
<label class="label">产品类型</label> <label class="label">产品类型</label>
<div class="fields"> <div class="fields">
<el-input value="银行产品" disabled></el-input> <el-input value="银行产品"
disabled></el-input>
</div> </div>
</div> </div>
<div class="line"> <div class="line">
<label class="label">选择产品</label> <label class="label">选择产品</label>
<div class="fields flex items-center"> <div class="fields flex items-center">
<el-input class="mr-5" placeholder="点击选择需要发布的产品"></el-input> <el-input class="mr-5"
<div class="btn" @click="showProduct">新增</div> placeholder="点击选择需要发布的产品"></el-input>
<div class="btn"
@click="showProduct">新增</div>
</div> </div>
</div> </div>
<div class="line"> <div class="line">
<label class="label">产品图标</label> <label class="label">产品图标</label>
<div class="fields"> <div class="fields">
<p class="tips">上传产品LOGO图片</p> <p class="tips">上传产品LOGO图片</p>
<div class="avatar-uploader" action="https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15" :show-file-list="false"> <div class="avatar-uploader"
<img v-if="imageUrl" :src="imageUrl" class="avatar" /> action="https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15"
<div v-else class="upload"> :show-file-list="false">
<img v-if="imageUrl"
:src="imageUrl"
class="avatar" />
<div v-else
class="upload">
<el-icon :size="20"> <el-icon :size="20">
<Plus :size="20" color="#DFE9F8" /> <Plus :size="20"
color="#DFE9F8" />
</el-icon> </el-icon>
</div> </div>
</div> </div>
@ -43,11 +52,15 @@
<p class="tips">选择需要购买的投放渠道</p> <p class="tips">选择需要购买的投放渠道</p>
<div class="route"> <div class="route">
<p class="text">{百度广告类型}单次广告价格为{}适用于{}次扫单</p> <p class="text">{百度广告类型}单次广告价格为{}适用于{}次扫单</p>
<el-input-number v-model="num" :min="1" :max="10" /> <el-input-number v-model="num"
:min="1"
:max="10" />
</div> </div>
<div class="route"> <div class="route">
<p class="text">{百度广告类型}单次广告价格为{}适用于{}次扫单</p> <p class="text">{百度广告类型}单次广告价格为{}适用于{}次扫单</p>
<el-input-number v-model="num" :min="1" :max="10" /> <el-input-number v-model="num"
:min="1"
:max="10" />
</div> </div>
</div> </div>
</div> </div>
@ -64,23 +77,35 @@
</div> </div>
</div> </div>
<el-dialog v-model="productVisible" title="选择需要发布的银行产品" width="1000px" center> <el-dialog v-model="productVisible"
title="选择需要发布的银行产品"
width="1000px"
center>
<div class="flex justify-between mb-4"> <div class="flex justify-between mb-4">
<search v-model="keyword"></search> <search v-model="keyword"></search>
<el-checkbox label="仅查看未发布的产品" /> <el-checkbox label="仅查看未发布的产品" />
</div> </div>
<el-table ref="table" v-loading="loading" :data="list"> <el-table ref="table"
<el-table-column prop="productNumber" label="产品名称"></el-table-column> v-loading="loading"
<el-table-column prop="productNumber" label="产品编号"></el-table-column> :data="list">
<el-table-column prop="productNumber" label="产品对象"></el-table-column> <el-table-column prop="productNumber"
<el-table-column prop="parentIds" label="担保方式"></el-table-column> label="产品名称"></el-table-column>
<el-table-column prop="parentIds" label="贷款用途"></el-table-column> <el-table-column prop="productNumber"
<el-table-column prop="loanCeiling" label="最高额度/年利率/期限"></el-table-column> label="产品编号"></el-table-column>
<el-table-column prop="createTime" label="创建日期"></el-table-column> <el-table-column prop="productNumber"
<el-table-column prop="status" label="状态"></el-table-column> label="产品对象"></el-table-column>
<el-table-column prop="parentIds"
label="担保方式"></el-table-column>
<el-table-column prop="parentIds"
label="贷款用途"></el-table-column>
<el-table-column prop="loanCeiling"
label="最高额度/年利率/期限"></el-table-column>
<el-table-column prop="createTime"
label="创建日期"></el-table-column>
<el-table-column prop="status"
label="状态"></el-table-column>
</el-table> </el-table>
<el-pagination <el-pagination v-model:currentPage="currentPage"
v-model:currentPage="currentPage"
v-model:pageSize="pageSize" v-model:pageSize="pageSize"
:total="total" :total="total"
:page-sizes="pageSizes" :page-sizes="pageSizes"
@ -89,12 +114,13 @@
@current-change="fetchData()" @current-change="fetchData()"
small small
background background
class="px-3 py-2 justify-end" class="px-3 py-2 justify-end"></el-pagination>
></el-pagination>
<template #footer> <template #footer>
<span class="flex justify-center"> <span class="flex justify-center">
<div class="dia-btn cancel" @click="productVisible = false">取消</div> <div class="dia-btn mr-3 cancel"
<div class="dia-btn" @click="productVisible = false">确定</div> @click="productVisible = false">取消</div>
<div class="dia-btn"
@click="productVisible = false">确定</div>
</span> </span>
</template> </template>
</el-dialog> </el-dialog>

@ -0,0 +1,174 @@
<template>
<!-- 五级分类 -->
<el-table class="c-table"
:data="form"
:span-method="span"
:cell-style="{background:'#fff'}"
border>
<el-table-column prop="recordName"
label="产品类别"
min-width="150"
align="center">
</el-table-column>
<el-table-column label="逾期时间"
align="center">
<el-table-column label="未逾期"
align="center">
<template #default="{ row }">
<el-select clearable
v-model="row.notOverdue">
<el-option v-for="item in row.recordChildren[0].subject.itemList"
:key="item"
:label="item.options"
:value="item.itemId" />
</el-select>
</template>
</el-table-column>
<el-table-column label="1~30天"
align="center">
<template #default="{ row }">
<el-select clearable
v-model="row.thirtyDays">
<el-option v-for="item in row.recordChildren[0].subject.itemList"
:key="item"
:label="item.options"
:value="item.itemId" />
</el-select>
</template></el-table-column>
<el-table-column label="31~90天"
align="center">
<template #default="{ row }">
<el-select clearable
v-model="row.ninetyDays">
<el-option v-for="item in row.recordChildren[0].subject.itemList"
:key="item"
:label="item.options"
:value="item.itemId" />
</el-select>
</template></el-table-column>
<el-table-column label="91~180天"
align="center">
<template #default="{ row }">
<el-select clearable
v-model="row.oneHundredAndEightyDays">
<el-option v-for="item in row.recordChildren[0].subject.itemList"
:key="item"
:label="item.options"
:value="item.itemId" />
</el-select>
</template></el-table-column>
<el-table-column label="181~360天"
align="center">
<template #default="{ row }">
<el-select clearable
v-model="row.threeHundredAndSixtyDays">
<el-option v-for="item in row.recordChildren[0].subject.itemList"
:key="item"
:label="item.options"
:value="item.itemId" />
</el-select>
</template></el-table-column>
<el-table-column label="360天以上"
align="center">
<template #default="{ row }">
<el-select clearable
v-model="row.threeHundredAndSixtyDaysAbove">
<el-option v-for="item in row.recordChildren[0].subject.itemList"
:key="item"
:label="item.options"
:value="item.itemId" />
</el-select>
</template></el-table-column>
</el-table-column>
</el-table>
<div class="flex justify-end">
<div class="submit"
@click="submit">确认完成配置</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { ElMessage } from 'element-plus';
import { fiveLevelClassificationDetails, fiveLevelClassificationSave } from '@/api/model';
import { getProcessInformationBasedOnRoles, addOperation } from '@/api/judgment';
import { handleId, getIds } from '@/utils/common';
import Cookies from 'js-cookie';
const projectId = +Cookies.get('sand-projectId');
const levelId = +Cookies.get('sand-level');
const form = ref<Record<string, any>[]>([]);
const info = ref<Record<string, any>[]>([]);
//
const getConfig = async () => {
const { process } = await getProcessInformationBasedOnRoles(1029);
const result = [];
process.map((e, i) => {
const cur = info.value.length ? info.value[i] : {};
result.push({
checkpointId: levelId,
projectId,
recordName: e.name,
recordChildren: e.recordChildren,
ninetyDays: +cur.ninetyDays || '',
notOverdue: +cur.notOverdue || '',
oneHundredAndEightyDays: +cur.oneHundredAndEightyDays || '',
thirtyDays: +cur.thirtyDays || '',
threeHundredAndSixtyDays: +cur.threeHundredAndSixtyDays || '',
threeHundredAndSixtyDaysAbove: +cur.threeHundredAndSixtyDaysAbove || '',
id: cur.id || '',
stRecordId: e.id,
});
});
form.value = result;
};
//
const getDetail = async () => {
try {
const { data } = await fiveLevelClassificationDetails(levelId, projectId);
info.value = data;
getConfig();
} finally {
}
};
const fieldKeys = ['notOverdue', 'thirtyDays', 'ninetyDays', 'oneHundredAndEightyDays', 'threeHundredAndSixtyDays', 'threeHundredAndSixtyDaysAbove'];
//
const addRecord = async (data: Record<string, any>) => {
const preIds = `1,${levelId},42,69,1029`; // 1id
const rule = [];
data.map((e) => {
e.recordChildren.forEach((n, i) => {
rule.push(handleId(n.id, n.subjectId, e[fieldKeys[i]], `${preIds},${e.stRecordId},${n.id}`, 1));
});
});
await addOperation({
...getIds(),
parentId: preIds,
lcJudgmentRuleReq: rule,
});
};
//
const submit = async () => {
let param = JSON.parse(JSON.stringify(form.value));
const recordParam = JSON.parse(JSON.stringify(param));
param.map((e) => {
delete e.recordChildren;
});
await fiveLevelClassificationSave({ fiveLevelClassificationList: param });
addRecord(recordParam);
getDetail();
ElMessage.success('提交成功!');
};
onMounted(() => {
getDetail();
});
</script>
<style lang="scss" scoped>
@import url(../../../styles/form.scss);
</style>

@ -1,194 +0,0 @@
<template>
<!-- 五级分类 -->
<el-form label-width="90px" label-suffix="" class="form" :disabled="disabled" v-loading="loading">
<el-form-item label="策略名称">
<div>
<el-input class="w-[320px]" placeholder="请输入20以内字符" maxlength="20" clearable v-model="strategyName" />
<p v-if="bankIds.length" class="text-danger">不同步已关联产品请修改策略名称</p>
</div>
</el-form-item>
<el-form-item label="策略规则">
<el-table class="c-table" :data="form" :cell-style="{ background: '#fff' }" border>
<el-table-column prop="recordName" label="产品类别" min-width="150" align="center"> </el-table-column>
<el-table-column label="逾期时间" align="center">
<el-table-column label="未逾期" align="center">
<template #default="{ row }">
<el-select clearable v-model="row.notOverdue">
<el-option v-for="item in row.recordChildren[0].subject.itemList" :key="item" :label="item.options" :value="item.itemId" />
</el-select>
</template>
</el-table-column>
<el-table-column label="1~30天" align="center">
<template #default="{ row }">
<el-select clearable v-model="row.thirtyDays">
<el-option v-for="item in row.recordChildren[0].subject.itemList" :key="item" :label="item.options" :value="item.itemId" />
</el-select> </template
></el-table-column>
<el-table-column label="31~90天" align="center">
<template #default="{ row }">
<el-select clearable v-model="row.ninetyDays">
<el-option v-for="item in row.recordChildren[0].subject.itemList" :key="item" :label="item.options" :value="item.itemId" />
</el-select> </template
></el-table-column>
<el-table-column label="91~180天" align="center">
<template #default="{ row }">
<el-select clearable v-model="row.oneHundredAndEightyDays">
<el-option v-for="item in row.recordChildren[0].subject.itemList" :key="item" :label="item.options" :value="item.itemId" />
</el-select> </template
></el-table-column>
<el-table-column label="181~360天" align="center">
<template #default="{ row }">
<el-select clearable v-model="row.threeHundredAndSixtyDays">
<el-option v-for="item in row.recordChildren[0].subject.itemList" :key="item" :label="item.options" :value="item.itemId" />
</el-select> </template
></el-table-column>
<el-table-column label="360天以上" align="center">
<template #default="{ row }">
<el-select clearable v-model="row.threeHundredAndSixtyDaysAbove">
<el-option v-for="item in row.recordChildren[0].subject.itemList" :key="item" :label="item.options" :value="item.itemId" />
</el-select> </template
></el-table-column>
</el-table-column>
</el-table>
</el-form-item>
<div v-if="!disabled" class="flex justify-end mt-3">
<div class="dia-btn cancel" @click="emit('close')">取消</div>
<div class="dia-btn" @click="confirmSubmit">确定</div>
</div>
</el-form>
<Confirm v-model="syncVisible" @submit="submit" />
</template>
<script setup lang="ts">
import { ref, defineAsyncComponent, onMounted, toRefs } from 'vue';
import { ElMessage } from 'element-plus';
import { fiveLevelClassificationDetails, fiveLevelClassificationSave, isTheStrategyRelatedToTheProduct } from '@/api/model';
import { getProcessInformationBasedOnRoles, addOperation } from '@/api/judgment';
import { handleId, getIds } from '@/utils/common';
import Cookies from 'js-cookie';
import { getStat } from '@/store/useProduct';
const Confirm = defineAsyncComponent(() => import('@/components/StrategyConfirm.vue'));
const props = defineProps({
disabled: { type: Boolean, default: false },
row: { type: Object },
});
const emit = defineEmits(['close']);
const { strategyId, strategyName } = toRefs(props.row);
const form = ref<Record<string, any>[]>([]);
const info = ref<Record<string, any>[]>([]);
const loading = ref<boolean>(false);
const syncVisible = ref<boolean>(false);
const bankIds = ref<Record<string, any>[]>([]);
//
const getConfig = async () => {
const { process } = await getProcessInformationBasedOnRoles(1029);
const result = [];
process.slice(1).map((e, i) => {
const cur = info.value.length ? info.value[i] : {};
result.push({
...getIds(),
recordName: e.name,
recordChildren: e.recordChildren,
ninetyDays: +cur.ninetyDays || '',
notOverdue: +cur.notOverdue || '',
oneHundredAndEightyDays: +cur.oneHundredAndEightyDays || '',
thirtyDays: +cur.thirtyDays || '',
threeHundredAndSixtyDays: +cur.threeHundredAndSixtyDays || '',
threeHundredAndSixtyDaysAbove: +cur.threeHundredAndSixtyDaysAbove || '',
id: cur.id || '',
stRecordId: e.id,
});
});
form.value = result;
loading.value = false;
};
//
const getDetail = async () => {
loading.value = true;
try {
if (strategyId.value) {
const { data } = await fiveLevelClassificationDetails({
strategyId: strategyId.value,
});
info.value = data;
}
getConfig();
//
if (strategyId.value && !props.disabled) {
const res = await isTheStrategyRelatedToTheProduct({
tacticsId: strategyId.value,
strategyType: 14,
});
if (res.isRelated) bankIds.value = res.bankIds;
}
} finally {
}
};
onMounted(getDetail);
const fieldKeys = ['notOverdue', 'thirtyDays', 'ninetyDays', 'oneHundredAndEightyDays', 'threeHundredAndSixtyDays', 'threeHundredAndSixtyDaysAbove'];
//
const addRecord = async (data: Record<string, any>) => {
const preIds = `1,${Cookies.get('sand-level')},42,69,1029`; // 1id
const rule = [handleId(140, 28, strategyName.value, `${preIds},140`, 3)];
data.map((e) => {
e.recordChildren.forEach((n, i) => {
e[fieldKeys[i]] && rule.push(handleId(n.id, n.subjectId, e[fieldKeys[i]], `${preIds},${e.stRecordId},${n.id}`, 1));
});
});
await addOperation({
...getIds(),
parentId: preIds,
lcJudgmentRuleReq: rule,
});
loading.value = false;
ElMessage.success('提交成功!');
syncVisible.value = false;
emit('close', 1);
};
//
const submit = async (synchronizeUpdate?: number) => {
loading.value = true;
try {
const param = JSON.parse(JSON.stringify(form.value));
const recordParam = JSON.parse(JSON.stringify(param));
param.map((e) => {
delete e.recordChildren;
});
await fiveLevelClassificationSave({
...getIds(),
strategyId: strategyId.value,
strategyName: strategyName.value,
synchronizeUpdate,
fiveLevelClassificationList: param,
bankIds: bankIds.value,
});
getStat(295);
addRecord(recordParam);
} catch (e) {
loading.value = false;
}
};
const confirmSubmit = async () => {
if (!strategyName.value) return ElMessage.error('请输入策略名称!');
//
if (bankIds.value.length) {
syncVisible.value = true;
} else {
submit();
}
};
</script>
<style lang="scss" scoped>
@import url(../../../../styles/form.scss);
</style>

@ -1,157 +0,0 @@
<template>
<div class="block">
<div class="flex justify-between items-center mb-5">
<search v-model="keyWord" @change="initList"></search>
<div class="filter">
<el-popconfirm title="确定删除选中策略吗?" :disabled="!multipleSelection.length" @confirm.stop="delAll">
<template #reference>
<div :class="['add-btn mr-2', { 'cursor-not-allowed': !multipleSelection.length }]">
<el-icon :size="24" color="#fff">
<Delete />
</el-icon>
批量删除
</div>
</template>
</el-popconfirm>
<div class="add-btn" @click="toAdd">
<img src="@/assets/images/plus.png" alt="" class="icon" />
新增
</div>
</div>
</div>
<el-table ref="table" v-loading="loading" :data="list" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" :selectable="handleDisable" />
<el-table-column label="序号" type="index" width="60" align="center" />
<el-table-column prop="strategyName" label="五级分类策略名称" min-width="180" />
<el-table-column prop="createTime" label="新增日期" min-width="140" />
<el-table-column label="操作" width="140">
<template #default="{ row }">
<el-button type="primary" link @click="toDetail(row, true)" size="small">查看</el-button>
<template v-if="!row.builtIn">
<el-button type="primary" link @click="toDetail(row)" size="small">编辑</el-button>
<el-popconfirm title="确定删除这条策略吗?" @confirm.stop="handleDelete([row.strategyId])">
<template #reference>
<el-button type="primary" link size="small">删除</el-button>
</template>
</el-popconfirm>
</template>
</template>
</el-table-column>
</el-table>
<el-pagination
v-model:currentPage="currentPage"
v-model:pageSize="pageSize"
:total="total"
:page-sizes="pageSizes"
:layout="pageLayout"
@size-change="getList()"
@current-change="getList()"
small
background
class="px-3 py-2 justify-end"
></el-pagination>
<el-drawer v-model="visible" :title="(isDetail ? '查看' : curRow.strategyId ? '编辑' : '新增') + '五级分类策略'" size="100%" :z-index="10" class="model-drawer">
<Detail v-model:row="curRow" :disabled="isDetail" :key="i" @close="closeDrawer" />
</el-drawer>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref, watch, defineAsyncComponent } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { Delete } from '@element-plus/icons-vue';
import { pageSizes, pageLayout } from '@/utils/common';
import { fiveLevelClassification, fiveLevelClassificationDel } from '@/api/model';
import Search from '@/components/Search.vue';
const Detail = defineAsyncComponent(() => import('./Detail.vue'));
const keyWord = ref<string>();
const currentPage = ref<number>(1);
const pageSize = ref<number>(10);
const total = ref<number>(0);
const table = ref<any>();
const multipleSelection = ref<Record<string, any>[]>([]);
const list = ref<Record<string, any>[]>([]);
const loading = ref<boolean>(false);
const visible = ref<boolean>(false);
const curRow = ref<Record<string, any>>({
strategyId: '',
strategyName: '',
});
const isDetail = ref<boolean>(false);
const i = ref<number>(0);
//
const getList = async () => {
loading.value = true;
try {
const { page } = await fiveLevelClassification({ pageNum: currentPage.value, pageSize: pageSize.value, keyWord: keyWord.value });
list.value = page.records;
total.value = page.total;
} finally {
loading.value = false;
}
};
//
const initList = async () => {
currentPage.value = 1;
getList();
};
watch(keyWord, initList);
onMounted(getList);
//
const handleDisable = (row: Record<string, any>) => {
//
if (row.builtIn) return false;
return true;
};
//
const handleSelectionChange = (val: Record<string, any>[]) => {
multipleSelection.value = val;
};
const handleDelete = async (ids: number[]) => {
try {
const res = await fiveLevelClassificationDel({ ids });
//
if (res.tip) {
ElMessageBox.alert(res.message, '提示', {
confirmButtonText: '确定',
});
} else {
getList();
ElMessage.success('删除成功!');
}
} catch (e) {}
};
//
const delAll = async () => {
handleDelete(multipleSelection.value.map((e) => e.strategyId));
};
//
const toAdd = () => {
i.value++;
isDetail.value = false;
curRow.value = {
strategyId: '',
strategyName: '',
};
visible.value = true;
};
//
const toDetail = async (row: Record<string, any>, detail = false) => {
i.value++;
isDetail.value = detail;
curRow.value = row;
visible.value = true;
};
//
const closeDrawer = (refresh?: number) => {
visible.value = false;
refresh && initList();
};
</script>

@ -0,0 +1,187 @@
<template>
<!-- 贷后检查 -->
<div class="c-auto">
<el-table class="c-table"
:data="form"
:cell-style="{background:'#fff'}"
border>
<el-table-column prop="name"
label="选用"
width="100"
align="center">
<template #default="{ row }">
<el-checkbox v-model="row.isChoose"></el-checkbox>
</template>
</el-table-column>
<el-table-column prop="recordName"
label="检查方式"
min-width="150"
align="center"></el-table-column>
<el-table-column label="检查对象"
min-width="200"
align="center">
<template #default="{ row }">
<el-select v-if="row.recordChildren"
clearable
v-model="row.checkObject">
<el-option v-for="item in row?.recordChildren[1].subject.itemList"
:key="item"
:label="item.options"
:value="item.itemId" />
</el-select>
</template>
</el-table-column>
<el-table-column label="检查时间"
min-width="270"
align="center">
<template #default="{ row, $index }">
<span v-if="$index === 4">点击后触发</span>
<div v-else
class="flex items-center">
<el-select v-if="row.recordChildren"
clearable
v-model="row.checkTimeType">
<el-option v-for="item in row?.recordChildren[2].recordChildren[0].subject.itemList"
:key="item"
:label="item.options"
:value="item.itemId" />
</el-select>
<el-input class="w-[100px] mx-2"
placeholder="请输入"
v-model="row.timeDays"></el-input>
</div>
</template>
</el-table-column>
<el-table-column label="检查内容"
min-width="230"
align="center">
<template #default="{ row }">
<el-checkbox v-model="row.governmentData">政务数据</el-checkbox>
<el-checkbox v-model="row.creditData">征信数据</el-checkbox>
</template>
</el-table-column>
</el-table>
</div>
<div class="flex justify-end">
<div class="submit"
@click="submit">确认完成配置</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { ElMessage } from 'element-plus';
import { postLoanInspectionDetails, postLoanInspectionSave } from '@/api/model';
import { getProcessInformationBasedOnRoles, addOperation } from '@/api/judgment';
import type { TableColumnCtx } from 'element-plus';
import { handleId, getIds } from '@/utils/common';
import Cookies from 'js-cookie';
const projectId = +Cookies.get('sand-projectId');
const levelId = +Cookies.get('sand-level');
const form = ref<Record<string, any>[]>([]);
const info = ref<Record<string, any>[]>([]);
//
const getConfig = async () => {
const { process } = await getProcessInformationBasedOnRoles(1030);
const result = [];
process.map((e, i) => {
const cur = info.value.length ? info.value[i] : {};
let temp = {
checkpointId: levelId,
projectId,
recordName: e.name,
recordChildren: e.recordChildren,
checkObject: +cur.checkObject || '',
checkTimeType: +cur.checkTimeType || '',
creditData: info.value.length ? !!cur.creditData : false,
governmentData: info.value.length ? !!cur.governmentData : false,
isChoose: info.value.length ? !!cur.isChoose : false,
timeDays: cur.timeDays || '',
id: cur.id || '',
stRecordId: e.id,
};
result.push(temp);
});
form.value = result;
};
//
const getDetail = async () => {
try {
const { data } = await postLoanInspectionDetails(levelId, projectId);
info.value = data;
getConfig();
} finally {
}
};
interface SpanMethodProps {
row: Record<string, any>;
column: TableColumnCtx<Record<string, any>>;
rowIndex: number;
columnIndex: number;
}
//
const span = ({ row, column, rowIndex, columnIndex }: SpanMethodProps) => {
if (columnIndex < 2) {
if (rowIndex === 1) {
return {
rowspan: 2,
colspan: 1,
};
} else if (rowIndex === 2) {
return {
rowspan: 0,
colspan: 0,
};
}
}
};
//
const addRecord = async (data: Record<string, any>) => {
const preIds = `1,${Cookies.get('sand-level')},42,69,1030`; // 1id
const rule = [];
data.forEach((e, i) => {
rule.push(handleId(1052, '', '', preIds + ',' + e.stRecordId + ',1052', ''), handleId(1053, 282, e.checkObject, preIds + ',' + e.stRecordId + ',1053', 1));
i !== 4 && rule.push(handleId(1054, 283, e.checkTimeType, preIds + ',' + e.stRecordId + ',1054,1056', 1));
e.timeDays && rule.push(handleId(1057, 284, e.timeDays, preIds + ',' + e.stRecordId + ',1054,1057', 3));
const ids = [];
e.governmentData && ids.push(778);
e.creditData && ids.push(793);
e.governmentData && rule.push(handleId(1055, 323, ids.join(), preIds + ',' + e.stRecordId + ',1055', 1));
});
await addOperation({
...getIds(),
parentId: preIds,
lcJudgmentRuleReq: rule,
});
};
//
const submit = async () => {
let param = JSON.parse(JSON.stringify(form.value));
param.map((e, i) => {
e.creditData = +e.creditData;
e.governmentData = +e.governmentData;
e.isChoose = +e.isChoose;
});
const recordParam = JSON.parse(JSON.stringify(param));
param.map((e) => {
delete e.recordChildren;
});
await postLoanInspectionSave({ postLoanInspectionList: param });
addRecord(recordParam);
getDetail();
ElMessage.success('提交成功!');
};
onMounted(() => {
getDetail();
});
</script>
<style lang="scss" scoped>
@import url(../../../styles/form.scss);
</style>

@ -1,219 +0,0 @@
<template>
<!-- 贷后检查 -->
<el-form label-width="90px" label-suffix="" class="form" :disabled="disabled" v-loading="loading">
<el-form-item label="策略名称">
<div>
<el-input class="w-[320px]" placeholder="请输入20以内字符" maxlength="20" clearable v-model="strategyName" />
<p v-if="bankIds.length" class="text-danger">不同步已关联产品请修改策略名称</p>
</div>
</el-form-item>
<el-form-item label="策略规则">
<el-table class="c-table" :data="form" :cell-style="{ background: '#fff' }" border>
<el-table-column prop="name" label="选用" width="130" align="center">
<template #default="{ row }">
<el-radio-group v-model="row.isChoose">
<el-radio v-for="(item, i) in opt1" :key="i" :label="item.id">{{ item.name }}</el-radio>
</el-radio-group>
</template>
</el-table-column>
<el-table-column prop="recordName" label="检查方式" min-width="150" align="center"></el-table-column>
<el-table-column label="检查对象" min-width="200" align="center">
<template #default="{ row }">
<el-select v-if="row.recordChildren" clearable v-model="row.checkObject">
<el-option v-for="item in row?.recordChildren[1].subject.itemList" :key="item" :label="item.options" :value="item.itemId" />
</el-select>
</template>
</el-table-column>
<el-table-column label="检查时间" min-width="270" align="center">
<template #default="{ row, $index }">
<span v-if="$index === 4">点击后触发</span>
<div v-else class="flex items-center">
<el-select v-if="row.recordChildren" clearable v-model="row.checkTimeType">
<el-option v-for="item in row?.recordChildren[2].recordChildren[0].subject.itemList" :key="item" :label="item.options" :value="item.itemId" />
</el-select>
<el-input class="w-[100px] mx-2" placeholder="请输入" v-model="row.timeDays"></el-input>
</div>
</template>
</el-table-column>
<el-table-column label="检查内容" min-width="230" align="center">
<template #default="{ row }">
<el-checkbox v-model="row.governmentData">政务数据</el-checkbox>
<el-checkbox v-model="row.creditData">征信数据</el-checkbox>
</template>
</el-table-column>
</el-table>
</el-form-item>
<div v-if="!disabled" class="flex justify-end mt-3">
<div class="dia-btn cancel" @click="emit('close')">取消</div>
<div class="dia-btn" @click="confirmSubmit">确定</div>
</div>
</el-form>
<Confirm v-model="syncVisible" @submit="submit" />
</template>
<script setup lang="ts">
import { ref, defineAsyncComponent, onMounted, toRefs } from 'vue';
import { ElMessage } from 'element-plus';
import { postLoanInspectionDetails, postLoanInspectionSave, isTheStrategyRelatedToTheProduct } from '@/api/model';
import { getProcessInformationBasedOnRoles, addOperation } from '@/api/judgment';
import type { TableColumnCtx } from 'element-plus';
import { handleId, getIds, opt1 } from '@/utils/common';
import Cookies from 'js-cookie';
import { getStat } from '@/store/useProduct';
const Confirm = defineAsyncComponent(() => import('@/components/StrategyConfirm.vue'));
const props = defineProps({
disabled: { type: Boolean, default: false },
row: { type: Object },
});
const emit = defineEmits(['close']);
const { strategyId, strategyName } = toRefs(props.row);
const form = ref<Record<string, any>[]>([]);
const info = ref<Record<string, any>[]>([]);
const loading = ref<boolean>(false);
const syncVisible = ref<boolean>(false);
const bankIds = ref<Record<string, any>[]>([]);
//
const getConfig = async () => {
const { process } = await getProcessInformationBasedOnRoles(1030);
const result = [];
process.slice(1).map((e, i) => {
const cur = info.value.length ? info.value[i] : {};
const temp = {
...getIds(),
recordName: e.name,
recordChildren: e.recordChildren,
checkObject: +cur.checkObject || '',
checkTimeType: +cur.checkTimeType || '',
creditData: info.value.length ? !!cur.creditData : false,
governmentData: info.value.length ? !!cur.governmentData : false,
isChoose: info.value.length ? cur.isChoose : '',
timeDays: cur.timeDays || '',
id: cur.id || '',
stRecordId: e.id,
};
result.push(temp);
});
form.value = result;
loading.value = false;
};
//
const getDetail = async () => {
loading.value = true;
try {
if (strategyId.value) {
const { data } = await postLoanInspectionDetails({
strategyId: strategyId.value,
});
info.value = data;
}
getConfig();
//
if (strategyId.value && !props.disabled) {
const res = await isTheStrategyRelatedToTheProduct({
tacticsId: strategyId.value,
strategyType: 15,
});
if (res.isRelated) bankIds.value = res.bankIds;
}
} finally {
}
};
onMounted(getDetail);
interface SpanMethodProps {
row: Record<string, any>;
column: TableColumnCtx<Record<string, any>>;
rowIndex: number;
columnIndex: number;
}
//
const span = ({ row, column, rowIndex, columnIndex }: SpanMethodProps) => {
if (columnIndex < 2) {
if (rowIndex === 1) {
return {
rowspan: 2,
colspan: 1,
};
}
if (rowIndex === 2) {
return {
rowspan: 0,
colspan: 0,
};
}
}
};
//
const addRecord = async (data: Record<string, any>) => {
const preIds = `1,${Cookies.get('sand-level')},42,69,1030`; // 1id
const rule = [handleId(140, 28, strategyName.value, `${preIds},140`, 3)];
data.forEach((e, i) => {
e.isChoose && rule.push(handleId(1052, 140, e.isChoose, `${preIds},${e.stRecordId},1052`, 1));
e.checkObject && rule.push(handleId(1053, 282, e.checkObject, `${preIds},${e.stRecordId},1053`, 1));
i !== 4 && e.checkTimeType && rule.push(handleId(1054, 283, e.checkTimeType, `${preIds},${e.stRecordId},1054,1056`, 1));
e.timeDays && rule.push(handleId(1057, 284, e.timeDays, `${preIds},${e.stRecordId},1054,1057`, 3));
const ids = [];
e.governmentData && ids.push(778);
e.creditData && ids.push(793);
e.governmentData && rule.push(handleId(1055, 323, ids.join(), `${preIds},${e.stRecordId},1055`, 1));
});
await addOperation({
...getIds(),
parentId: preIds,
lcJudgmentRuleReq: rule,
});
loading.value = false;
ElMessage.success('提交成功!');
syncVisible.value = false;
emit('close', 1);
};
//
const submit = async (synchronizeUpdate?: number) => {
loading.value = true;
try {
const param = JSON.parse(JSON.stringify(form.value));
param.map((e, i) => {
e.creditData = +e.creditData;
e.governmentData = +e.governmentData;
});
const recordParam = JSON.parse(JSON.stringify(param));
param.map((e) => {
delete e.recordChildren;
});
await postLoanInspectionSave({
...getIds(),
strategyId: strategyId.value,
strategyName: strategyName.value,
synchronizeUpdate,
postLoanInspectionList: param,
bankIds: bankIds.value,
});
getStat(295);
addRecord(recordParam);
} catch (e) {
loading.value = false;
}
};
const confirmSubmit = async () => {
if (!strategyName.value) return ElMessage.error('请输入策略名称!');
//
if (bankIds.value.length) {
syncVisible.value = true;
} else {
submit();
}
};
</script>
<style lang="scss" scoped>
@import url(../../../../styles/form.scss);
</style>

@ -1,157 +0,0 @@
<template>
<div class="block">
<div class="flex justify-between items-center mb-5">
<search v-model="keyWord" @change="initList"></search>
<div class="filter">
<el-popconfirm title="确定删除选中策略吗?" :disabled="!multipleSelection.length" @confirm.stop="delAll">
<template #reference>
<div :class="['add-btn mr-2', { 'cursor-not-allowed': !multipleSelection.length }]">
<el-icon :size="24" color="#fff">
<Delete />
</el-icon>
批量删除
</div>
</template>
</el-popconfirm>
<div class="add-btn" @click="toAdd">
<img src="@/assets/images/plus.png" alt="" class="icon" />
新增
</div>
</div>
</div>
<el-table ref="table" v-loading="loading" :data="list" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" :selectable="handleDisable" />
<el-table-column label="序号" type="index" width="60" align="center" />
<el-table-column prop="strategyName" label="贷后检查策略名称" min-width="180" />
<el-table-column prop="createTime" label="新增日期" min-width="140" />
<el-table-column label="操作" width="140">
<template #default="{ row }">
<el-button type="primary" link @click="toDetail(row, true)" size="small">查看</el-button>
<template v-if="!row.builtIn">
<el-button type="primary" link @click="toDetail(row)" size="small">编辑</el-button>
<el-popconfirm title="确定删除这条策略吗?" @confirm.stop="handleDelete([row.strategyId])">
<template #reference>
<el-button type="primary" link size="small">删除</el-button>
</template>
</el-popconfirm>
</template>
</template>
</el-table-column>
</el-table>
<el-pagination
v-model:currentPage="currentPage"
v-model:pageSize="pageSize"
:total="total"
:page-sizes="pageSizes"
:layout="pageLayout"
@size-change="getList()"
@current-change="getList()"
small
background
class="px-3 py-2 justify-end"
></el-pagination>
<el-drawer v-model="visible" :title="(isDetail ? '查看' : curRow.strategyId ? '编辑' : '新增') + '贷后检查策略'" size="100%" :z-index="10" class="model-drawer">
<Detail v-model:row="curRow" :disabled="isDetail" :key="i" @close="closeDrawer" />
</el-drawer>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref, watch, defineAsyncComponent } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { Delete } from '@element-plus/icons-vue';
import { pageSizes, pageLayout } from '@/utils/common';
import { postLoanInspection, postLoanInspectionDel } from '@/api/model';
import Search from '@/components/Search.vue';
const Detail = defineAsyncComponent(() => import('./Detail.vue'));
const keyWord = ref<string>();
const currentPage = ref<number>(1);
const pageSize = ref<number>(10);
const total = ref<number>(0);
const table = ref<any>();
const multipleSelection = ref<Record<string, any>[]>([]);
const list = ref<Record<string, any>[]>([]);
const loading = ref<boolean>(false);
const visible = ref<boolean>(false);
const curRow = ref<Record<string, any>>({
strategyId: '',
strategyName: '',
});
const isDetail = ref<boolean>(false);
const i = ref<number>(0);
//
const getList = async () => {
loading.value = true;
try {
const { page } = await postLoanInspection({ pageNum: currentPage.value, pageSize: pageSize.value, keyWord: keyWord.value });
list.value = page.records;
total.value = page.total;
} finally {
loading.value = false;
}
};
//
const initList = async () => {
currentPage.value = 1;
getList();
};
watch(keyWord, initList);
onMounted(getList);
//
const handleDisable = (row: Record<string, any>) => {
//
if (row.builtIn) return false;
return true;
};
//
const handleSelectionChange = (val: Record<string, any>[]) => {
multipleSelection.value = val;
};
const handleDelete = async (ids: number[]) => {
try {
const res = await postLoanInspectionDel({ ids });
//
if (res.tip) {
ElMessageBox.alert(res.message, '提示', {
confirmButtonText: '确定',
});
} else {
getList();
ElMessage.success('删除成功!');
}
} catch (e) {}
};
//
const delAll = async () => {
handleDelete(multipleSelection.value.map((e) => e.strategyId));
};
//
const toAdd = () => {
i.value++;
isDetail.value = false;
curRow.value = {
strategyId: '',
strategyName: '',
};
visible.value = true;
};
//
const toDetail = async (row: Record<string, any>, detail = false) => {
i.value++;
isDetail.value = detail;
curRow.value = row;
visible.value = true;
};
//
const closeDrawer = (refresh?: number) => {
visible.value = false;
refresh && initList();
};
</script>

@ -0,0 +1,347 @@
<template>
<!-- 贷后评分 -->
<el-table class="c-table"
:data="form"
:span-method="span"
:cell-style="{background:'#fff'}"
border>
<el-table-column prop="recordName"
label="指标"
min-width="80"
align="center"></el-table-column>
<el-table-column prop="recordName"
label="公式/取值"
min-width="200"
align="center">
<template #default="{ row, $index }">
<div class="flex items-center">
<template v-if="$index === 1">
存贷比 =
<div class="inline-flex flex-col justify-center mx-2">
<el-select v-if="row.recordChildren"
class="w-[140px]"
clearable
v-model="row.formulaOne">
<el-option v-for="item in row?.recordChildren[0].subject.itemList"
:key="item"
:label="item.options"
:value="item.itemId" />
</el-select>
<p class="h-[1px] my-2 bg-[#cdcdcd]"></p>
<el-select v-if="row.recordChildren"
class="w-[140px]"
clearable
v-model="row.formulaTwo">
<el-option v-for="item in row?.recordChildren[1].subject.itemList"
:key="item"
:label="item.options"
:value="item.itemId" />
</el-select>
</div>
x 100%
</template>
<template v-if="$index === 6">
房屋净值 =
<el-select v-if="row.recordChildren"
class="w-[140px] mx-2"
clearable
v-model="row.formulaOne">
<el-option v-for="item in row?.recordChildren[0].subject.itemList"
:key="item"
:label="item.options"
:value="item.itemId" />
</el-select>
-
<el-select v-if="row.recordChildren"
class="w-[140px] ml-2"
clearable
v-model="row.formulaTwo">
<el-option v-for="item in row?.recordChildren[1].subject.itemList"
:key="item"
:label="item.options"
:value="item.itemId" />
</el-select>
</template>
<template v-if="$index === 9">
<el-input class="w-[80px] mx-2"
placeholder="请输入"
v-model="row.formulaOne"></el-input>
<el-select v-if="row.recordChildren"
class="w-[140px] mr-2"
clearable
v-model="row.formulaTwo">
<el-option v-for="item in row?.recordChildren[1].subject.itemList"
:key="item"
:label="item.options"
:value="item.itemId" />
</el-select>
内逾期次数
</template>
<template v-if="$index === 13">
<el-select v-if="row.recordChildren"
class="w-[140px] mx-2"
clearable
v-model="row.formulaOne">
<el-option v-for="item in row?.recordChildren[0].subject.itemList"
:key="item"
:label="item.options"
:value="item.itemId" />
</el-select>
开始算还款当天不计算利息
</template>
<template v-if="$index === 17">当前尚未偿还的贷款总额</template>
<template v-if="$index === 20">
平均额度使用率 =
<div class="inline-flex flex-col justify-center mx-2">
<el-select v-if="row.recordChildren"
class="w-[140px]"
clearable
v-model="row.formulaOne">
<el-option v-for="item in row?.recordChildren[0].subject.itemList"
:key="item"
:label="item.options"
:value="item.itemId" />
</el-select>
<p class="h-[1px] my-2 bg-[#cdcdcd]"></p>
<el-select v-if="row.recordChildren"
class="w-[140px]"
clearable
v-model="row.formulaTwo">
<el-option v-for="item in row?.recordChildren[1].subject.itemList"
:key="item"
:label="item.options"
:value="item.itemId" />
</el-select>
</div>
x 100%
</template>
<template v-if="$index === 24">
最大用信率 =
<div class="inline-flex flex-col justify-center mx-2">
<el-select v-if="row.recordChildren"
class="w-[140px]"
clearable
v-model="row.formulaOne">
<el-option v-for="item in row?.recordChildren[0].subject.itemList"
:key="item"
:label="item.options"
:value="item.itemId" />
</el-select>
<p class="h-[1px] my-2 bg-[#cdcdcd]"></p>
<el-select v-if="row.recordChildren"
class="w-[140px]"
clearable
v-model="row.formulaTwo">
<el-option v-for="item in row?.recordChildren[1].subject.itemList"
:key="item"
:label="item.options"
:value="item.itemId" />
</el-select>
</div>
x 100%
</template>
<template v-if="$index === 28">到目前为止使用额度的次数</template>
<template v-if="$index === 32">到目前为止客户可以使用的最大额度</template>
<template v-if="$index === 37">客户贷记卡已经逾期的总月数</template>
<template v-if="$index === 39">客户在行所有贷款已经逾期的总月数</template>
</div>
</template>
</el-table-column>
<el-table-column prop="ruleName"
label="取值"
min-width="100"
align="center"></el-table-column>
<el-table-column label="分数"
min-width="80"
align="center">
<template #default="{ row }">
<el-select v-if="row.subject"
clearable
v-model="row.score">
<el-option v-for="item in row.subject.itemList.sort((a, b) => +a.options - +b.options)"
:key="item"
:label="item.options"
:value="item.itemId" />
</el-select>
<span v-else>600</span>
</template>
</el-table-column>
</el-table>
<div class="flex justify-end">
<div class="submit"
@click="submit">确认完成配置</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { ElMessage } from 'element-plus';
import { postCreditScoreDetails, postCreditScoreSave } from '@/api/model';
import { getProcessInformationBasedOnRoles, addOperation } from '@/api/judgment';
import type { TableColumnCtx } from 'element-plus';
import { handleId, getIds } from '@/utils/common';
import Cookies from 'js-cookie';
const projectId = +Cookies.get('sand-projectId');
const levelId = +Cookies.get('sand-level');
const form = ref<Record<string, any>[]>([]);
const info = ref<Record<string, any>[]>([]);
//
const getConfig = async () => {
const { process } = await getProcessInformationBasedOnRoles(1031);
const result = [
{
recordName: '基准分',
},
];
process.map((e, i) => {
let temp = {
checkpointId: levelId,
projectId,
recordName: e.name,
recordChildren: e.recordChildren,
formulaOne: '',
formulaTwo: '',
score: '',
id: '',
stRecordId: e.id,
middleId: e.recordChildren[e.recordChildren.length - 1]?.id,
};
//
e.recordChildren[e.recordChildren.length - 1]?.recordChildren?.map((n, j) => {
temp = JSON.parse(JSON.stringify(temp));
temp.index = j;
temp.ruleName = n.name;
temp.subject = n.subject;
temp.ruleId = n.id;
result.push(temp);
});
});
if (info.value.length) {
result.forEach((e, i) => {
if (i) {
e.formulaOne = info.value[i - 1].formulaOne ? +info.value[i - 1].formulaOne : '';
e.indexId = info.value[i - 1].indexId;
e.formulaTwo = info.value[i - 1].formulaTwo ? +info.value[i - 1].formulaTwo : '';
e.score = info.value[i - 1].score ? +info.value[i - 1].score : '';
}
});
}
form.value = result;
console.log('🚀 ~ file: 1031.vue:83 ~ getConfig ~ result:', result);
};
//
const getDetail = async () => {
try {
const { data } = await postCreditScoreDetails(levelId, projectId);
info.value = data;
getConfig();
} finally {
}
};
interface SpanMethodProps {
row: Record<string, any>;
column: TableColumnCtx<Record<string, any>>;
rowIndex: number;
columnIndex: number;
}
const rowMerge1 = [1, 32];
const rowMerge2 = [6, 17];
const rowMerge3 = [9, 13, 20, 24, 28];
const rowMerge4 = [37, 39];
//
const span = ({ row, column, rowIndex, columnIndex }: SpanMethodProps) => {
if (!rowIndex) {
if (!columnIndex) {
return {
rowspan: 1,
colspan: 3,
};
} else if (columnIndex === 3) {
return {
rowspan: 1,
colspan: 1,
};
} else {
return {
rowspan: 0,
colspan: 0,
};
}
}
if (columnIndex < 2) {
if (rowMerge1.includes(rowIndex)) {
return {
rowspan: 5,
colspan: 1,
};
} else if (rowMerge2.includes(rowIndex)) {
return {
rowspan: 3,
colspan: 1,
};
} else if (rowMerge3.includes(rowIndex)) {
return {
rowspan: 4,
colspan: 1,
};
} else if (rowMerge4.includes(rowIndex)) {
return {
rowspan: 2,
colspan: 1,
};
} else {
return {
rowspan: 0,
colspan: 0,
};
}
}
};
//
const addRecord = async (data: Record<string, any>) => {
const preIds = `1,${Cookies.get('sand-level')},42,69,1031`; // 1id
const rule = [];
data.forEach((e, i) => {
e.score && rule.push(handleId(e.ruleId, e.subject.subjectId, e.score, `${preIds},${e.stRecordId},${e.middleId},${e.ruleId}`, 1));
if (i === 1 || i === 6 || i === 20 || i === 24) {
e.formulaOne && rule.push(handleId(1061, 285, e.formulaOne, `${preIds},${e.stRecordId},1061`, 1));
e.formulaTwo && rule.push(handleId(1062, 285, e.formulaTwo, `${preIds},${e.stRecordId},1062`, 1));
}
});
data[9].formulaOne && rule.push(handleId(1075, 288, data[9].formulaOne, `${preIds},1074,1075`, 3));
data[9].formulaTwo && rule.push(handleId(1076, 289, data[9].formulaTwo, `${preIds},1074,1076`, 1));
data[13].formulaOne && rule.push(handleId(1083, 291, data[13].formulaOne, `${preIds},1082,1083`, 1));
await addOperation({
...getIds(),
parentId: preIds,
lcJudgmentRuleReq: rule,
});
};
//
const submit = async () => {
let param = JSON.parse(JSON.stringify(form.value));
const recordParam = JSON.parse(JSON.stringify(param));
param.map((e) => {
delete e.recordChildren;
});
await postCreditScoreSave({ postCreditScoreList: param });
addRecord(recordParam);
getDetail();
ElMessage.success('提交成功!');
};
onMounted(() => {
getDetail();
});
</script>
<style lang="scss" scoped>
@import url(../../../styles/form.scss);
</style>

@ -1,336 +0,0 @@
<template>
<!-- 贷后评分 -->
<el-form label-width="90px" label-suffix="" class="form" :disabled="disabled" v-loading="loading">
<el-form-item label="策略名称">
<div>
<el-input class="w-[320px]" placeholder="请输入20以内字符" maxlength="20" clearable v-model="strategyName" />
<p v-if="bankIds.length" class="text-danger">不同步已关联产品请修改策略名称</p>
</div>
</el-form-item>
<el-form-item label="策略规则">
<el-table class="c-table" :data="form" :span-method="span" :cell-style="{ background: '#fff' }" border>
<el-table-column prop="name" label="选用" width="130" align="center">
<template #default="{ row, $index }">
<el-radio-group v-if="$index" v-model="row.isChoose">
<el-radio v-for="(item, i) in opt1" :key="i" :label="item.id">{{ item.name }}</el-radio>
</el-radio-group>
<span v-else>基准分</span>
</template>
</el-table-column>
<el-table-column prop="recordName" label="指标" min-width="80" align="center"></el-table-column>
<el-table-column prop="recordName" label="公式/取值" min-width="200" align="center">
<template #default="{ row, $index }">
<div class="flex items-center">
<template v-if="$index === 1">
存贷比 =
<div class="inline-flex flex-col justify-center mx-2">
<el-select v-if="row.recordChildren" class="w-[140px]" clearable v-model="row.formulaOne" disabled>
<el-option v-for="item in row?.recordChildren[1].subject.itemList" :key="item" :label="item.options" :value="item.itemId" />
</el-select>
<p class="h-[1px] my-2 bg-[#cdcdcd]"></p>
<el-select v-if="row.recordChildren" class="w-[140px]" clearable v-model="row.formulaTwo" disabled>
<el-option v-for="item in row?.recordChildren[2].subject.itemList" :key="item" :label="item.options" :value="item.itemId" />
</el-select>
</div>
x 100%
</template>
<template v-if="$index === 6">
房屋净值 =
<el-select v-if="row.recordChildren" class="w-[140px] mx-2" clearable v-model="row.formulaOne" disabled>
<el-option v-for="item in row?.recordChildren[1].subject.itemList" :key="item" :label="item.options" :value="item.itemId" />
</el-select>
-
<el-select v-if="row.recordChildren" class="w-[140px] ml-2" clearable v-model="row.formulaTwo" disabled>
<el-option v-for="item in row?.recordChildren[2].subject.itemList" :key="item" :label="item.options" :value="item.itemId" />
</el-select>
</template>
<template v-if="$index === 9">
<el-input class="w-[80px] mx-2" placeholder="请输入" v-model="row.formulaOne" disabled></el-input>
<el-select v-if="row.recordChildren" class="w-[140px] mr-2" clearable v-model="row.formulaTwo" disabled>
<el-option v-for="item in row?.recordChildren[2].subject.itemList" :key="item" :label="item.options" :value="item.itemId" />
</el-select>
内逾期次数
</template>
<template v-if="$index === 12">
<el-select v-if="row.recordChildren" class="w-[140px] mx-2" clearable v-model="row.formulaOne" disabled>
<el-option v-for="item in row?.recordChildren[1].subject.itemList" :key="item" :label="item.options" :value="item.itemId" />
</el-select>
开始算还款当天不计算利息
</template>
<template v-if="$index === 15">当前尚未偿还的贷款总额</template>
<template v-if="$index === 18">
平均额度使用率 =
<div class="inline-flex flex-col justify-center mx-2">
<el-select v-if="row.recordChildren" class="w-[140px]" clearable v-model="row.formulaOne" disabled>
<el-option v-for="item in row?.recordChildren[1].subject.itemList" :key="item" :label="item.options" :value="item.itemId" />
</el-select>
<p class="h-[1px] my-2 bg-[#cdcdcd]"></p>
<el-select v-if="row.recordChildren" class="w-[140px]" clearable v-model="row.formulaTwo" disabled>
<el-option v-for="item in row?.recordChildren[2].subject.itemList" :key="item" :label="item.options" :value="item.itemId" />
</el-select>
</div>
x 100%
</template>
<template v-if="$index === 22">
最大用信率 =
<div class="inline-flex flex-col justify-center mx-2">
<el-select v-if="row.recordChildren" class="w-[140px]" clearable v-model="row.formulaOne" disabled>
<el-option v-for="item in row?.recordChildren[1].subject.itemList" :key="item" :label="item.options" :value="item.itemId" />
</el-select>
<p class="h-[1px] my-2 bg-[#cdcdcd]"></p>
<el-select v-if="row.recordChildren" class="w-[140px]" clearable v-model="row.formulaTwo" disabled>
<el-option v-for="item in row?.recordChildren[2].subject.itemList" :key="item" :label="item.options" :value="item.itemId" />
</el-select>
</div>
x 100%
</template>
<template v-if="$index === 26">到目前为止使用额度的次数</template>
<template v-if="$index === 30">到目前为止客户可以使用的最大额度</template>
<template v-if="$index === 35">客户贷记卡已经逾期的总月数</template>
<template v-if="$index === 37">客户在行所有贷款已经逾期的总月数</template>
</div>
</template>
</el-table-column>
<el-table-column prop="ruleName" label="取值" min-width="100" align="center"></el-table-column>
<el-table-column label="分数" min-width="80" align="center">
<template #default="{ row }">
<el-select v-if="row.subject" clearable v-model="row.score">
<el-option v-for="item in row.subject.itemList.sort((a, b) => +a.options - +b.options)" :key="item" :label="item.options" :value="item.itemId" />
</el-select>
<span v-else>600</span>
</template>
</el-table-column>
</el-table>
</el-form-item>
<div v-if="!disabled" class="flex justify-end mt-3">
<div class="dia-btn cancel" @click="emit('close')">取消</div>
<div class="dia-btn" @click="confirmSubmit">确定</div>
</div>
</el-form>
<Confirm v-model="syncVisible" @submit="submit" />
</template>
<script setup lang="ts">
import { ref, defineAsyncComponent, onMounted, toRefs } from 'vue';
import { ElMessage } from 'element-plus';
import { postCreditScoreDetails, postCreditScoreSave, isTheStrategyRelatedToTheProduct } from '@/api/model';
import { getProcessInformationBasedOnRoles, addOperation } from '@/api/judgment';
import type { TableColumnCtx } from 'element-plus';
import { handleId, getIds, opt1 } from '@/utils/common';
import Cookies from 'js-cookie';
import { getStat } from '@/store/useProduct';
const Confirm = defineAsyncComponent(() => import('@/components/StrategyConfirm.vue'));
const props = defineProps({
disabled: { type: Boolean, default: false },
row: { type: Object },
});
const emit = defineEmits(['close']);
const { strategyId, strategyName } = toRefs(props.row);
const form = ref<Record<string, any>[]>([]);
const info = ref<Record<string, any>[]>([]);
//
const answer = [[697, 693], [698, 691], [1, 707], [716], [], [695, 696], [696, 694]];
const loading = ref<boolean>(false);
const syncVisible = ref<boolean>(false);
const bankIds = ref<Record<string, any>[]>([]);
//
const getConfig = async () => {
const { process } = await getProcessInformationBasedOnRoles(1031);
const result = [
{
recordName: '基准分',
},
];
process.slice(1).map((e, i) => {
let temp = {
...getIds(),
recordName: e.name,
recordChildren: e.recordChildren,
formulaOne: i < 7 ? answer[i][0] || '' : '',
formulaTwo: i < 7 ? answer[i][1] || '' : '',
score: '',
id: '',
stRecordId: e.id,
middleId: e.recordChildren[e.recordChildren.length - 1]?.id,
};
//
e.recordChildren[e.recordChildren.length - 1]?.recordChildren?.map((n, j) => {
temp = JSON.parse(JSON.stringify(temp));
temp.index = j;
temp.ruleName = n.name;
temp.subject = n.subject;
temp.ruleId = n.id;
temp.isChoose = '';
result.push(temp);
});
});
if (info.value.length) {
result.forEach((e, i) => {
if (i) {
e.indexId = info.value[i - 1].indexId;
e.isChoose = info.value[i - 1].isChoose;
e.score = info.value[i - 1].score ? +info.value[i - 1].score : '';
}
});
}
form.value = result;
loading.value = false;
};
//
const getDetail = async () => {
loading.value = true;
try {
if (strategyId.value) {
const { data } = await postCreditScoreDetails({
strategyId: strategyId.value,
});
info.value = data.slice(1);
}
getConfig();
//
if (strategyId.value && !props.disabled) {
const res = await isTheStrategyRelatedToTheProduct({
tacticsId: strategyId.value,
strategyType: 16,
});
if (res.isRelated) bankIds.value = res.bankIds;
}
} finally {
}
};
onMounted(getDetail);
interface SpanMethodProps {
row: Record<string, any>;
column: TableColumnCtx<Record<string, any>>;
rowIndex: number;
columnIndex: number;
}
const rowMerge1 = [1, 30];
const rowMerge2 = [6, 9, 12, 15];
const rowMerge3 = [9, 18, 22, 26];
const rowMerge4 = [35, 37];
//
const span = ({ row, column, rowIndex, columnIndex }: SpanMethodProps) => {
if (!rowIndex) {
if (!columnIndex) {
return {
rowspan: 1,
colspan: 4,
};
}
if (columnIndex === 4) {
return {
rowspan: 1,
colspan: 1,
};
}
return {
rowspan: 0,
colspan: 0,
};
}
if (columnIndex < 3) {
if (rowMerge1.includes(rowIndex)) {
return {
rowspan: 5,
colspan: 1,
};
}
if (rowMerge2.includes(rowIndex)) {
return {
rowspan: 3,
colspan: 1,
};
}
if (rowMerge3.includes(rowIndex)) {
return {
rowspan: 4,
colspan: 1,
};
}
if (rowMerge4.includes(rowIndex)) {
return {
rowspan: 2,
colspan: 1,
};
}
return {
rowspan: 0,
colspan: 0,
};
}
};
//
const addRecord = async (data: Record<string, any>) => {
const preIds = `1,${Cookies.get('sand-level')},42,69,1031`; // 1id
const rule = [handleId(140, 28, strategyName.value, `${preIds},140`, 3)];
data.forEach((e, i) => {
e.isChoose && rule.push(handleId(1052, 140, e.isChoose, `${preIds},${e.stRecordId},1052`, 1));
e.score && rule.push(handleId(e.ruleId, e.subject.subjectId, e.score, `${preIds},${e.stRecordId},${e.middleId},${e.ruleId}`, 1));
if (i === 1 || i === 6 || i === 18 || i === 22) {
e.formulaOne && rule.push(handleId(1061, 285, e.formulaOne, `${preIds},${e.stRecordId},1061`, 1));
e.formulaTwo && rule.push(handleId(1062, 285, e.formulaTwo, `${preIds},${e.stRecordId},1062`, 1));
}
});
data[9].formulaOne && rule.push(handleId(1075, 288, data[9].formulaOne, `${preIds},1074,1075`, 3));
data[9].formulaTwo && rule.push(handleId(1076, 289, data[9].formulaTwo, `${preIds},1074,1076`, 1));
data[12].formulaOne && rule.push(handleId(1083, 291, data[12].formulaOne, `${preIds},1082,1083`, 1));
await addOperation({
...getIds(),
parentId: preIds,
lcJudgmentRuleReq: rule,
});
loading.value = false;
ElMessage.success('提交成功!');
syncVisible.value = false;
emit('close', 1);
};
//
const submit = async (synchronizeUpdate?: number) => {
loading.value = true;
try {
const param = JSON.parse(JSON.stringify(form.value));
const recordParam = JSON.parse(JSON.stringify(param));
param.map((e) => {
delete e.recordChildren;
});
await postCreditScoreSave({
...getIds(),
strategyId: strategyId.value,
strategyName: strategyName.value,
synchronizeUpdate,
postCreditScoreList: param,
bankIds: bankIds.value,
});
getStat(295);
addRecord(recordParam);
} catch (e) {
loading.value = false;
}
};
const confirmSubmit = async () => {
if (!strategyName.value) return ElMessage.error('请输入策略名称!');
//
if (bankIds.value.length) {
syncVisible.value = true;
} else {
submit();
}
};
</script>
<style lang="scss" scoped>
@import url(../../../../styles/form.scss);
</style>

@ -1,157 +0,0 @@
<template>
<div class="block">
<div class="flex justify-between items-center mb-5">
<search v-model="keyWord" @change="initList"></search>
<div class="filter">
<el-popconfirm title="确定删除选中策略吗?" :disabled="!multipleSelection.length" @confirm.stop="delAll">
<template #reference>
<div :class="['add-btn mr-2', { 'cursor-not-allowed': !multipleSelection.length }]">
<el-icon :size="24" color="#fff">
<Delete />
</el-icon>
批量删除
</div>
</template>
</el-popconfirm>
<div class="add-btn" @click="toAdd">
<img src="@/assets/images/plus.png" alt="" class="icon" />
新增
</div>
</div>
</div>
<el-table ref="table" v-loading="loading" :data="list" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" :selectable="handleDisable" />
<el-table-column label="序号" type="index" width="60" align="center" />
<el-table-column prop="strategyName" label="贷后评分策略名称" min-width="180" />
<el-table-column prop="createTime" label="新增日期" min-width="140" />
<el-table-column label="操作" width="140">
<template #default="{ row }">
<el-button type="primary" link @click="toDetail(row, true)" size="small">查看</el-button>
<template v-if="!row.builtIn">
<el-button type="primary" link @click="toDetail(row)" size="small">编辑</el-button>
<el-popconfirm title="确定删除这条策略吗?" @confirm.stop="handleDelete([row.strategyId])">
<template #reference>
<el-button type="primary" link size="small">删除</el-button>
</template>
</el-popconfirm>
</template>
</template>
</el-table-column>
</el-table>
<el-pagination
v-model:currentPage="currentPage"
v-model:pageSize="pageSize"
:total="total"
:page-sizes="pageSizes"
:layout="pageLayout"
@size-change="getList()"
@current-change="getList()"
small
background
class="px-3 py-2 justify-end"
></el-pagination>
<el-drawer v-model="visible" :title="(isDetail ? '查看' : curRow.strategyId ? '编辑' : '新增') + '贷后评分策略'" size="100%" :z-index="10" class="model-drawer">
<Detail v-model:row="curRow" :disabled="isDetail" :key="i" @close="closeDrawer" />
</el-drawer>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref, watch, defineAsyncComponent } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { Delete } from '@element-plus/icons-vue';
import { pageSizes, pageLayout } from '@/utils/common';
import { postCreditScore, postCreditScoreDel } from '@/api/model';
import Search from '@/components/Search.vue';
const Detail = defineAsyncComponent(() => import('./Detail.vue'));
const keyWord = ref<string>();
const currentPage = ref<number>(1);
const pageSize = ref<number>(10);
const total = ref<number>(0);
const table = ref<any>();
const multipleSelection = ref<Record<string, any>[]>([]);
const list = ref<Record<string, any>[]>([]);
const loading = ref<boolean>(false);
const visible = ref<boolean>(false);
const curRow = ref<Record<string, any>>({
strategyId: '',
strategyName: '',
});
const isDetail = ref<boolean>(false);
const i = ref<number>(0);
//
const getList = async () => {
loading.value = true;
try {
const { page } = await postCreditScore({ pageNum: currentPage.value, pageSize: pageSize.value, keyWord: keyWord.value });
list.value = page.records;
total.value = page.total;
} finally {
loading.value = false;
}
};
//
const initList = async () => {
currentPage.value = 1;
getList();
};
watch(keyWord, initList);
onMounted(getList);
//
const handleDisable = (row: Record<string, any>) => {
//
if (row.builtIn) return false;
return true;
};
//
const handleSelectionChange = (val: Record<string, any>[]) => {
multipleSelection.value = val;
};
const handleDelete = async (ids: number[]) => {
try {
const res = await postCreditScoreDel({ ids });
//
if (res.tip) {
ElMessageBox.alert(res.message, '提示', {
confirmButtonText: '确定',
});
} else {
getList();
ElMessage.success('删除成功!');
}
} catch (e) {}
};
//
const delAll = async () => {
handleDelete(multipleSelection.value.map((e) => e.strategyId));
};
//
const toAdd = () => {
i.value++;
isDetail.value = false;
curRow.value = {
strategyId: '',
strategyName: '',
};
visible.value = true;
};
//
const toDetail = async (row: Record<string, any>, detail = false) => {
i.value++;
isDetail.value = detail;
curRow.value = row;
visible.value = true;
};
//
const closeDrawer = (refresh?: number) => {
visible.value = false;
refresh && initList();
};
</script>

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

Loading…
Cancel
Save