Browse Source

update

main
wangshaoping 10 months ago
parent
commit
bb1d326402
  1. 2
      build.gradle
  2. 4
      erm.frontend/package.json
  3. 4
      gradle.properties
  4. 4
      io.sc.engine.mv.frontend/package.json
  5. 10
      io.sc.engine.mv.frontend/src/i18n/messages.json
  6. 6
      io.sc.engine.mv.frontend/src/i18n/messages_tw_CN.json
  7. 6
      io.sc.engine.mv.frontend/src/i18n/messages_zh_CN.json
  8. 4
      io.sc.engine.rule.frontend/package.json
  9. 33
      io.sc.engine.rule.frontend/src/i18n/messages.json
  10. 33
      io.sc.engine.rule.frontend/src/i18n/messages_tw_CN.json
  11. 8
      io.sc.engine.rule.frontend/src/i18n/messages_zh_CN.json
  12. 2
      io.sc.engine.rule.frontend/src/views/migration/Migration.vue
  13. 2
      io.sc.platform.attachment/src/main/java/io/sc/platform/attachment/service/impl/AttachmentServiceImpl.java
  14. 2
      io.sc.platform.attachment/src/main/resources/META-INF/platform/plugins/liquibase.json
  15. 102
      io.sc.platform.core.frontend/package copy.json
  16. 2
      io.sc.platform.core.frontend/package.json
  17. 2
      io.sc.platform.core.frontend/src/components/index.ts
  18. 4
      io.sc.platform.core.frontend/src/platform/utils/Tools.ts
  19. 2
      io.sc.platform.core.frontend/src/routes/routes.json
  20. 179
      io.sc.platform.core.frontend/src/views/FormElements.vue
  21. 70
      io.sc.platform.core.frontend/src/views/Standard.vue
  22. 109
      io.sc.platform.core.frontend/src/views/standard/AdminDivision.vue
  23. 79
      io.sc.platform.core.frontend/src/views/standard/Counterparty.vue
  24. 257
      io.sc.platform.core.frontend/src/views/standard/Country.vue
  25. 72
      io.sc.platform.core.frontend/src/views/standard/Currency.vue
  26. 109
      io.sc.platform.core.frontend/src/views/standard/Industry.vue
  27. 79
      io.sc.platform.core.frontend/src/views/standard/PerformanceStatus.vue
  28. 4
      io.sc.platform.core.frontend/template-project/package.json
  29. 2
      io.sc.platform.core.frontend/template-project/src/components/index.ts
  30. 2
      io.sc.platform.core.frontend/template-project/src/routes/routes.json
  31. 179
      io.sc.platform.core.frontend/template-project/src/views/FormElements.vue
  32. 70
      io.sc.platform.core.frontend/template-project/src/views/Standard.vue
  33. 109
      io.sc.platform.core.frontend/template-project/src/views/standard/AdminDivision.vue
  34. 79
      io.sc.platform.core.frontend/template-project/src/views/standard/Counterparty.vue
  35. 257
      io.sc.platform.core.frontend/template-project/src/views/standard/Country.vue
  36. 72
      io.sc.platform.core.frontend/template-project/src/views/standard/Currency.vue
  37. 109
      io.sc.platform.core.frontend/template-project/src/views/standard/Industry.vue
  38. 79
      io.sc.platform.core.frontend/template-project/src/views/standard/PerformanceStatus.vue
  39. 2
      io.sc.platform.core/src/main/java/io/sc/platform/core/bean/GlobalExceptionHandler.java
  40. 7
      io.sc.platform.core/src/main/java/io/sc/platform/core/bean/RestControllerReturnValueAdviceBean.java
  41. 3
      io.sc.platform.core/src/main/resources/io/sc/platform/core/i18n/words.properties
  42. 3
      io.sc.platform.core/src/main/resources/io/sc/platform/core/i18n/words_tw_CN.properties
  43. 3
      io.sc.platform.core/src/main/resources/io/sc/platform/core/i18n/words_zh_CN.properties
  44. 6
      io.sc.platform.developer.doc/asciidoc/frontend/api/api.adoc
  45. 4
      io.sc.platform.developer.frontend/package.json
  46. 2
      io.sc.platform.gradle/templates/pgp/setup/build.gradle.txt
  47. 4
      io.sc.platform.gradle/templates/pgp/setup/gradle.properties
  48. 2
      io.sc.platform.jdbc/src/main/java/io/sc/platform/jdbc/service/impl/SqlExecutorServiceImpl.java
  49. 4
      io.sc.platform.lcdp.frontend/package.json
  50. 4
      io.sc.platform.mvc.frontend/package.json
  51. 2
      io.sc.platform.mvc/src/main/java/io/sc/platform/mvc/controller/DownloaderController.java
  52. 2
      io.sc.platform.mvc/src/main/java/io/sc/platform/mvc/service/impl/FrontEndModuleServiceImpl.java
  53. 6
      io.sc.platform.mvc/src/main/resources/META-INF/platform/plugins/application-properties.json
  54. 4
      io.sc.platform.security.frontend/package.json
  55. 27
      io.sc.platform.security/src/main/resources/liquibase/io.sc.platform.security_8.0.0_20220606__Security Database Schema DDL.xml
  56. 69
      io.sc.platform.system.api/src/main/java/io/sc/platform/system/api/application/ApplicationVo.java
  57. 4
      io.sc.platform.system.frontend/package.json
  58. 2
      io.sc.platform.system.frontend/src/components/index.ts
  59. 15
      io.sc.platform.system.frontend/src/i18n/messages.json
  60. 14
      io.sc.platform.system.frontend/src/i18n/messages_tw_CN.json
  61. 14
      io.sc.platform.system.frontend/src/i18n/messages_zh_CN.json
  62. 6
      io.sc.platform.system.frontend/src/menus/menus.json
  63. 15
      io.sc.platform.system.frontend/src/routes/routes.json
  64. 127
      io.sc.platform.system.frontend/src/views/application/Application.vue
  65. 82
      io.sc.platform.system.frontend/src/views/menu/Menu.vue
  66. 9
      io.sc.platform.system.frontend/src/views/parameter/Parameter.vue
  67. 102
      io.sc.platform.system.frontend/src/views/shared/SelectApplicationDialog.vue
  68. 141
      io.sc.platform.system.frontend/src/views/shared/SelectApplicationGrid.vue
  69. 110
      io.sc.platform.system/src/main/java/io/sc/platform/system/application/controller/ApplicationWebController.java
  70. 169
      io.sc.platform.system/src/main/java/io/sc/platform/system/application/jpa/entity/ApplicationEntity.java
  71. 15
      io.sc.platform.system/src/main/java/io/sc/platform/system/application/jpa/repository/ApplicationRepository.java
  72. 77
      io.sc.platform.system/src/main/java/io/sc/platform/system/application/service/ApplicationService.java
  73. 146
      io.sc.platform.system/src/main/java/io/sc/platform/system/application/service/impl/ApplicationServiceImpl.java
  74. 11
      io.sc.platform.system/src/main/java/io/sc/platform/system/application/support/ApplicationComparator.java
  75. 26
      io.sc.platform.system/src/main/java/io/sc/platform/system/application/support/ApplicationEntityNameComparator.java
  76. 26
      io.sc.platform.system/src/main/java/io/sc/platform/system/application/support/ApplicationVoCodeComparator.java
  77. 26
      io.sc.platform.system/src/main/java/io/sc/platform/system/application/support/ApplicationVoNameComparator.java
  78. 40
      io.sc.platform.system/src/main/java/io/sc/platform/system/menu/controller/MenuWebController.java
  79. 54
      io.sc.platform.system/src/main/java/io/sc/platform/system/menu/jpa/entity/MenuEntity.java
  80. 46
      io.sc.platform.system/src/main/java/io/sc/platform/system/menu/service/MenuService.java
  81. 123
      io.sc.platform.system/src/main/java/io/sc/platform/system/menu/service/impl/MenuServiceImpl.java
  82. 6
      io.sc.platform.system/src/main/java/io/sc/platform/system/role/jpa/entity/RoleEntity.java
  83. 3
      io.sc.platform.system/src/main/resources/META-INF/platform/plugins/components.json
  84. 3
      io.sc.platform.system/src/main/resources/META-INF/platform/plugins/repositories.json
  85. 59
      io.sc.platform.system/src/main/resources/liquibase/io.sc.platform.system_8.0.0_20220606__System Database Schema DDL.xml
  86. 4
      io.sc.standard.frontend/package.json
  87. 102
      io.sc.standard.frontend/src/views/standard/Country.vue

2
build.gradle

@ -654,7 +654,7 @@ subprojects {
// : -D
// gradle bootwar -Dfrontend=dev # pnpm build
// gradle bootwar -Dfrontend=prod # pnpm prod
// gradle bootwar -Dfrontend # ,
// gradle bootwar -Dfrontend=none # ,
def isFrontend =System.getProperty("frontend")?:"prod";
if(isFrontend=="dev"){
frontendGenerateThymeleafTemplate.dependsOn(frontendNpmBuild);

4
erm.frontend/package.json

@ -1,6 +1,6 @@
{
"name": "erm.frontend",
"version": "8.1.38",
"version": "8.1.40",
"description": "",
"private": false,
"keywords": [],
@ -92,7 +92,7 @@
"luckyexcel": "1.0.1",
"mockjs": "1.1.0",
"pinia": "2.1.7",
"platform-core": "8.1.190",
"platform-core": "8.1.193",
"quasar": "2.15.3",
"tailwindcss": "3.4.3",
"vue": "3.4.24",

4
gradle.properties

@ -36,9 +36,9 @@ application_version=1.0.0
# platform
###########################################################
platform_group=io.sc
platform_version=8.1.38
platform_version=8.1.40
platform_plugin_version=8.1.13
platform_core_frontend_version=8.1.190
platform_core_frontend_version=8.1.193
###########################################################
# dependencies version

4
io.sc.engine.mv.frontend/package.json

@ -1,6 +1,6 @@
{
"name": "io.sc.engine.mv.frontend",
"version": "8.1.38",
"version": "8.1.40",
"description": "",
"private": false,
"keywords": [],
@ -92,7 +92,7 @@
"luckyexcel": "1.0.1",
"mockjs": "1.1.0",
"pinia": "2.1.7",
"platform-core": "8.1.190",
"platform-core": "8.1.193",
"quasar": "2.15.3",
"tailwindcss": "3.4.3",
"vue": "3.4.24",

10
io.sc.engine.mv.frontend/src/i18n/messages.json

@ -1,5 +1,5 @@
{
"menu.engine.mv" : "Model Validator",
"menu.engine.mv" : "Model Validator Tools",
"menu.engine.mv.config" : "Configuration",
"menu.engine.mv.config.cutOffPoint" : "Score Cut Off Point",
"menu.engine.mv.config.threshold" : "Threshold",
@ -43,7 +43,7 @@
"io.sc.engine.mv.config.binomial.grid.entity.confidenceLevel" : "Confidence Level",
"io.sc.engine.mv.config.binomial.grid.entity.zUpper" : "Z Upper",
"io.sc.engine.mv.config.binomial.grid.entity.zLower" : "Z Lower",
"io.sc.engine.mv.config.chiSquare.grid.title" : "Const of Chi-square List",
"io.sc.engine.mv.config.chiSquare.grid.entity.dof" : "DOF",
"io.sc.engine.mv.config.chiSquare.grid.entity.significanceLevel" : "Significance Level",
@ -117,7 +117,7 @@
"io.sc.engine.mv.sample.defaultRecord.grid.title" : "Default Record List",
"io.sc.engine.mv.sample.defaultRecord.grid.entity.customId" : "Customer ID",
"io.sc.engine.mv.sample.defaultRecord.grid.entity.defaultConfirmDate" : "Default Confirm Date",
"io.sc.engine.mv.result.task.progress" : "Current Running Task:",
"io.sc.engine.mv.result.grid.title" : "Validate Result List",
@ -201,7 +201,7 @@
"io.sc.engine.mv.result.chiSquare.count" : "Total Count",
"io.sc.engine.mv.result.chiSquare.defaultCount" : "Default Count",
"io.sc.engine.mv.result.chiSquare.chiSquare" : "ChiSquare Value",
"io.sc.engine.mv.result.binomial.grid.title" : "Binomial Test",
"io.sc.engine.mv.result.binomial.level" : "Level",
"io.sc.engine.mv.result.binomial.pd" : "PD",
@ -225,4 +225,4 @@
"io.sc.engine.mv.executorDialog.form.entity.binomialSignificanceLevel" : "Binomial Significance Level",
"io.sc.engine.mv.executorDialog.form.entity.chiSquareSignificanceLevel" : "ChiSquare Significance Level",
"io.sc.engine.mv.executorDialog.form.action.execute" : "Execute Now"
}
}

6
io.sc.engine.mv.frontend/src/i18n/messages_tw_CN.json

@ -1,5 +1,5 @@
{
"menu.engine.mv" : "模型驗證",
"menu.engine.mv" : "模型驗證工具",
"menu.engine.mv.config" : "配置",
"menu.engine.mv.config.cutOffPoint" : "評分截斷點",
"menu.engine.mv.config.threshold" : "預警閾值",
@ -188,7 +188,7 @@
"io.sc.engine.mv.result.chiSquare.count" : "樣本總數",
"io.sc.engine.mv.result.chiSquare.defaultCount" : "事實違約樣本個數",
"io.sc.engine.mv.result.chiSquare.chiSquare" : "卡方检验值",
"io.sc.engine.mv.result.binomial.grid.title" : "二項檢驗結果",
"io.sc.engine.mv.result.binomial.level" : "評分等級",
"io.sc.engine.mv.result.binomial.pd" : "違約概率",
@ -212,4 +212,4 @@
"io.sc.engine.mv.executorDialog.form.entity.binomialSignificanceLevel" : "二項檢驗採用的顯著水平",
"io.sc.engine.mv.executorDialog.form.entity.chiSquareSignificanceLevel" : "卡方檢驗採用的顯著水平",
"io.sc.engine.mv.executorDialog.form.action.execute" : "立即執行"
}
}

6
io.sc.engine.mv.frontend/src/i18n/messages_zh_CN.json

@ -1,5 +1,5 @@
{
"menu.engine.mv" : "模型验证",
"menu.engine.mv" : "模型验证工具",
"menu.engine.mv.config" : "配置",
"menu.engine.mv.config.cutOffPoint" : "评分截断点",
"menu.engine.mv.config.threshold" : "预警阈值",
@ -202,7 +202,7 @@
"io.sc.engine.mv.result.chiSquare.count" : "样本总数",
"io.sc.engine.mv.result.chiSquare.defaultCount" : "事实违约样本个数",
"io.sc.engine.mv.result.chiSquare.chiSquare" : "卡方检验值",
"io.sc.engine.mv.result.binomial.grid.title" : "二项检验结果",
"io.sc.engine.mv.result.binomial.level" : "评分等级",
"io.sc.engine.mv.result.binomial.pd" : "违约概率",
@ -226,4 +226,4 @@
"io.sc.engine.mv.executorDialog.form.entity.binomialSignificanceLevel" : "二项检验采用的显著水平",
"io.sc.engine.mv.executorDialog.form.entity.chiSquareSignificanceLevel" : "卡方检验采用的显著水平",
"io.sc.engine.mv.executorDialog.form.action.execute" : "立即执行"
}
}

4
io.sc.engine.rule.frontend/package.json

@ -1,6 +1,6 @@
{
"name": "io.sc.engine.rule.frontend",
"version": "8.1.38",
"version": "8.1.40",
"description": "",
"private": false,
"keywords": [],
@ -92,7 +92,7 @@
"luckyexcel": "1.0.1",
"mockjs": "1.1.0",
"pinia": "2.1.7",
"platform-core": "8.1.190",
"platform-core": "8.1.193",
"quasar": "2.15.3",
"tailwindcss": "3.4.3",
"vue": "3.4.24",

33
io.sc.engine.rule.frontend/src/i18n/messages.json

@ -172,13 +172,13 @@
"re.lib.tab.indicator.title": "Feature",
"re.lib.tab.testcase.title": "Test Case",
"re.migration.import.title":"Import ( From The File Uploaded )",
"re.migration.import.subTitle":"",
"re.migration.import.action":"Import",
"re.migration.importFromServer.title":"Import ( From Server File)",
"re.migration.importFromServer.subTitle":"",
"re.migration.importFromServer.serverPath.label":"Please input the path of file in the server",
"re.migration.importFromServer.action":"Import",
"re.migration.export.title":"Export",
@ -189,4 +189,35 @@
"re.migration.remove.subTitle":"Warning: It will remove all data in the engine, and can NOT undo!",
"re.migration.remove.action":"Remove",
"re.migration.remove.action.tip":"Are you sure to remove the all data?",
"re.workflow.task.grid.title":"Task List",
"re.workflow.task.grid.toolbar.viewResource":"View Resource",
"re.workflow.task.grid.toolbar.viewAttachment":"View Attachment",
"re.workflow.task.grid.toolbar.claim":"Claim Task",
"re.workflow.task.grid.toolbar.claim.tip":"Are you sure to claim the task?",
"re.workflow.task.grid.toolbar.unclaim":"Unclaim Task",
"re.workflow.task.grid.toolbar.unclaim.tip":"Are you sure to unclaim the task?",
"re.workflow.task.grid.toolbar.complete":"Complete Task",
"re.workflow.task.grid.toolbar.terminate":"Terminate Task",
"re.workflow.task.grid.toolbar.terminate.tip":"Are you sure to terminate the task?",
"re.workflow.task.grid.entity.processDefinitionId":"Process Definition ID",
"re.workflow.task.grid.entity.processInstanceId":"Process Instance ID",
"re.workflow.task.grid.entity.taskDefinitionId":"Task Definition ID",
"re.workflow.task.grid.entity.taskId":"Task ID",
"re.workflow.task.grid.entity.taskName":"Task Name",
"re.workflow.task.grid.entity.taskAssignee":"Task Assignee",
"re.workflow.task.grid.entity.taskCreateTime":"Task Create Date",
"re.workflow.task.grid.entity.taskEndTime":"Task Complete Date",
"re.workflow.task.grid.entity.taskClaimTime":"Task Claim Date",
"re.workflow.task.grid.entity.taskTreatment":"Task Treatment",
"re.workflow.task.grid.entity.resourceType":"Resource Type",
"re.workflow.task.grid.entity.resourceId":"Resource ID",
"re.workflow.task.grid.entity.resourceCode":"Resource Code",
"re.workflow.task.grid.entity.resourceName":"Resource Name",
"re.workflow.task.grid.entity.resourceVersion":"Resource Version",
"re.workflow.task.grid.entity.resourceStatus":"Resource Status",
"re.workflow.task.grid.entity.attachments":"Attachments",
"re.workflow.historyTask.grid.title":"历史任务列表"
}

33
io.sc.engine.rule.frontend/src/i18n/messages_tw_CN.json

@ -172,13 +172,13 @@
"re.lib.tab.indicator.title": "特征",
"re.lib.tab.testcase.title": "測試用例",
"re.migration.import.title":"導入數據 (通過上傳文件導入)",
"re.migration.import.subTitle":"",
"re.migration.import.action":"導入數據",
"re.migration.importFromServer.title":"導入數據 (從服務器文件導入)",
"re.migration.importFromServer.subTitle":"",
"re.migration.importFromServer.serverPath.label":"請輸入服務器上導入文件的路徑",
"re.migration.importFromServer.action":"導入數據",
"re.migration.export.title":"導出所有數據",
@ -190,4 +190,35 @@
"re.migration.remove.action":"刪除數據",
"re.migration.remove.action.tip":"您確定要刪除所有數據嗎?",
"re.workflow.task.grid.title":"任務列表",
"re.workflow.task.grid.toolbar.viewResource":"查看資源",
"re.workflow.task.grid.toolbar.viewAttachment":"查看附件",
"re.workflow.task.grid.toolbar.claim":"領取任務",
"re.workflow.task.grid.toolbar.claim.tip":"您確定要領取任務嗎?",
"re.workflow.task.grid.toolbar.unclaim":"歸還任務",
"re.workflow.task.grid.toolbar.unclaim.tip":"您確定要歸還任務嗎?",
"re.workflow.task.grid.toolbar.complete":"審批任務",
"re.workflow.task.grid.toolbar.terminate":"終止任務",
"re.workflow.task.grid.toolbar.terminate.tip":"您確定要終止任務嗎?",
"re.workflow.task.grid.entity.processDefinitionId":"流程定義ID",
"re.workflow.task.grid.entity.processInstanceId":"流程實例ID",
"re.workflow.task.grid.entity.taskDefinitionId":"任務定義ID",
"re.workflow.task.grid.entity.taskId":"任務ID",
"re.workflow.task.grid.entity.taskName":"任務名稱",
"re.workflow.task.grid.entity.taskAssignee":"任務處理人",
"re.workflow.task.grid.entity.taskCreateTime":"任務創建日期",
"re.workflow.task.grid.entity.taskEndTime":"任務完成日期",
"re.workflow.task.grid.entity.taskClaimTime":"任務領取日期",
"re.workflow.task.grid.entity.taskTreatment":"審批意見",
"re.workflow.task.grid.entity.resourceType":"資源類型",
"re.workflow.task.grid.entity.resourceId":"資源ID",
"re.workflow.task.grid.entity.resourceCode":"資源代碼",
"re.workflow.task.grid.entity.resourceName":"資源名稱",
"re.workflow.task.grid.entity.resourceVersion":"資源版本",
"re.workflow.task.grid.entity.resourceStatus":"資源狀態",
"re.workflow.task.grid.entity.attachments":"資源附件",
"re.workflow.historyTask.grid.title":"歷史任務列表"
}

8
io.sc.engine.rule.frontend/src/i18n/messages_zh_CN.json

@ -172,13 +172,13 @@
"re.lib.tab.indicator.title": "特征",
"re.lib.tab.testcase.title": "测试用例",
"re.migration.import.title":"导入数据 (通过上传文件导入)",
"re.migration.import.subTitle":"",
"re.migration.import.action":"导入数据",
"re.migration.importFromServer.title":"导入数据 (从服务器文件导入)",
"re.migration.importFromServer.subTitle":"",
"re.migration.importFromServer.serverPath.label":"请输入服务器上导入文件的路径",
"re.migration.importFromServer.action":"导入数据",
"re.migration.export.title":"导出所有数据",
@ -219,9 +219,5 @@
"re.workflow.task.grid.entity.resourceStatus":"资源状态",
"re.workflow.task.grid.entity.attachments":"资源附件",
"re.workflow.historyTask.grid.title":"历史任务列表",
"re.workflow.historyTask.grid.title":"历史任务列表"
}

2
io.sc.engine.rule.frontend/src/views/migration/Migration.vue

@ -83,7 +83,7 @@
<q-separator />
<q-card-section>
<q-input v-model="serverFilePathRef" outlined dense></q-input>
<q-input v-model="serverFilePathRef" :label="$t('re.migration.importFromServer.serverPath.label')" outlined dense></q-input>
</q-card-section>
<q-card-actions align="right">

2
io.sc.platform.attachment/src/main/java/io/sc/platform/attachment/service/impl/AttachmentServiceImpl.java

@ -21,7 +21,7 @@ import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.util.*;
@Service("frAttachmentService")
@Service
public class AttachmentServiceImpl extends DaoServiceImpl<AttachmentEntity, String, AttachmentRepository> implements AttachmentService {
@Autowired private SystemParameterService systemParameterService;

2
io.sc.platform.attachment/src/main/resources/META-INF/platform/plugins/liquibase.json

@ -2,7 +2,7 @@
{
"category" : "install",
"order" : 200,
"description":"系统管理",
"description":"附件管理",
"locations":[
"liquibase/io.sc.platform.attachment_8.0.0_20220606__Attachment Database Schema DDL.xml"
]

102
io.sc.platform.core.frontend/package copy.json

@ -1,102 +0,0 @@
{
"name": "io.sc.platform.system.frontend",
"version": "8.1.38",
"description": "",
"private": false,
"keywords": [],
"author": "",
"license": "ISC",
"scripts": {
"dev": "nodemon",
"serve": "node ./util-components-generator.cjs && cross-env NODE_ENV=development webpack serve --config webpack.env.serve.cjs",
"build": "node ./util-components-generator.cjs && cross-env NODE_ENV=development webpack --config webpack.env.build.cjs",
"prod": "node ./util-components-generator.cjs && cross-env NODE_ENV=production webpack --config webpack.env.prod.cjs",
"sync": "platform sync",
"clean": "rm -rf ./node_modules && rm -rf pnpm-lock.yaml"
},
"engines": {
"node": ">=18",
"pnpm": ">=7"
},
"publishConfig": {
"registry": "http://nexus.sc.io:8000/repository/npm-releases/",
"access": "public"
},
"devDependencies": {
"@babel/core": "7.23.7",
"@babel/preset-env": "7.23.7",
"@babel/preset-typescript": "7.23.3",
"@babel/plugin-transform-class-properties": "7.23.3",
"@babel/plugin-transform-object-rest-spread": "7.23.4",
"@quasar/app-webpack": "3.12.1",
"@quasar/cli": "2.3.0",
"@types/mockjs": "1.0.10",
"@types/node": "20.10.6",
"@typescript-eslint/eslint-plugin": "6.17.0",
"@typescript-eslint/parser": "6.17.0",
"@vue/compiler-sfc": "3.4.3",
"@webpack-cli/serve": "2.0.5",
"autoprefixer": "10.4.16",
"babel-loader": "9.1.3",
"clean-webpack-plugin": "4.0.0",
"copy-webpack-plugin": "11.0.0",
"cross-env": "7.0.3",
"css-loader": "6.8.1",
"eslint": "8.56.0",
"eslint-config-prettier": "9.1.0",
"eslint-plugin-prettier": "5.1.2",
"eslint-plugin-vue": "9.19.2",
"eslint-webpack-plugin": "4.0.1",
"html-webpack-plugin": "5.6.0",
"json5": "2.2.3",
"mini-css-extract-plugin": "2.7.6",
"nodemon": "3.0.2",
"postcss": "8.4.32",
"postcss-import": "16.0.0",
"postcss-loader": "7.3.4",
"postcss-preset-env": "9.3.0",
"prettier": "3.1.1",
"sass": "1.69.7",
"sass-loader": "13.3.3",
"typescript": "5.3.3",
"vue-loader": "17.4.2",
"webpack": "5.89.0",
"webpack-bundle-analyzer": "4.10.1",
"webpack-cli": "5.1.4",
"webpack-dev-server": "4.15.1",
"webpack-merge": "5.10.0",
"@vue/babel-plugin-jsx": "1.1.5"
},
"dependencies": {
"@codemirror/autocomplete": "6.11.1",
"@codemirror/commands": "6.3.3",
"@codemirror/lang-html": "6.4.7",
"@codemirror/lang-java": "6.0.1",
"@codemirror/lang-javascript": "6.2.1",
"@codemirror/lang-json": "6.0.1",
"@codemirror/lang-sql": "6.5.4",
"@codemirror/lang-xml": "6.0.2",
"@codemirror/language": "6.10.0",
"@codemirror/search": "6.5.5",
"@codemirror/state": "6.4.0",
"@codemirror/view": "6.23.0",
"@maxgraph/core": "0.9.0",
"@quasar/extras": "1.16.9",
"@vueuse/core": "10.7.1",
"axios": "1.6.3",
"codemirror": "6.0.1",
"dayjs": "1.11.10",
"echarts": "5.4.3",
"exceljs": "4.4.0",
"file-saver": "2.0.5",
"luckyexcel": "1.0.1",
"mockjs": "1.1.0",
"pinia": "2.1.7",
"quasar": "2.14.5",
"tailwindcss": "3.4.0",
"vue": "3.4.3",
"vue-dompurify-html": "5.0.1",
"vue-i18n": "9.8.0",
"vue-router": "4.2.5"
}
}

2
io.sc.platform.core.frontend/package.json

@ -1,6 +1,6 @@
{
"name": "platform-core",
"version": "8.1.191",
"version": "8.1.193",
"description": "前端核心包,用于快速构建前端的脚手架",
"//main": "库的主文件",
"main": "dist/platform-core.js",

2
io.sc.platform.core.frontend/src/components/index.ts

@ -2,7 +2,7 @@
* ,
*/
import component_testcase_formElements from '@/views/FormElements.vue';
import component_testcase_formElements from '@/views/Standard.vue';
import component_testcase_likmDialog from '@/views/likm/Dialog.vue';
import component_testcase_likmDrawer from '@/views/likm/Drawer.vue';
import component_testcase_likmForm from '@/views/likm/Form.vue';

4
io.sc.platform.core.frontend/src/platform/utils/Tools.ts

@ -850,7 +850,7 @@ class Tools {
* @param maxIncluded
* @return
*/
public static generateIntervalRange(minIncluded: boolean, min: any, max: any, maxIncluded: boolean) : string {
public static generateIntervalRange(minIncluded: boolean, min: any, max: any, maxIncluded: boolean): string {
if (Tools.isUndefinedOrNull(min) && Tools.isUndefinedOrNull(max)) {
return '';
}
@ -882,7 +882,7 @@ class Tools {
* @param propertyName
* @returns
*/
public static hasOwnProperty(obj: object, propertyName: string) : boolean {
public static hasOwnProperty(obj: object, propertyName: string): boolean {
return Object.prototype.hasOwnProperty.call(obj, propertyName);
}
}

2
io.sc.platform.core.frontend/src/routes/routes.json

@ -5,7 +5,7 @@
"parent": "/",
"priority": 0,
"component": "component.testcase.formElements",
"componentPath": "@/views/FormElements.vue",
"componentPath": "@/views/Standard.vue",
"redirect": null,
"meta": {
"permissions": ["/testcase/formElements/**/*"]

179
io.sc.platform.core.frontend/src/views/FormElements.vue

@ -1,160 +1,41 @@
<template>
<div>
<div class="row justify-end q-gutter-md q-py-md">
<q-select
v-model="selectedTheme"
:label="$t('theme')"
:options="themeOptions"
option-label="name"
option-value="id"
emit-value
map-options
dense
outlined
style="min-width: 200px"
@update:model-value="themeChanged"
>
<template #option="scope">
<q-item v-bind="scope.itemProps">
<q-item-section>
<q-item-label>{{ scope.opt.name }}</q-item-label>
</q-item-section>
<q-item-section v-if="scope.opt.active" avatar>
<q-icon name="bi-check" />
</q-item-section>
<q-item-section v-else avatar>
<w-icon-empty></w-icon-empty>
</q-item-section>
</q-item>
</template>
</q-select>
<q-btn :label="$t('theme.action.setDefaultTheme')" icon="bi-heart" no-caps outline dense padding="2px 10px" @click="defaultTheme" />
<q-btn :label="$t('add')" icon="add" no-caps outline dense padding="2px 10px" @click="prompt = true" />
<q-btn :label="$t('delete')" icon="delete" no-caps outline dense padding="2px 10px" @click="removeTheme" />
<q-btn :label="$t('save')" icon="save" no-caps outline dense padding="2px 10px" @click="saveTheme" />
<div class="fit row wrap justify-center items-start content-start q-pt-md q-gutter-md">
<q-input v-model="frontendWebContextPath" :label="$t('lcdp.frontend.export.frontendWebContextPath')" outlined dense style="width: 300px" />
</div>
<div class="fit row wrap justify-center items-start content-start q-pt-md q-gutter-md">
<q-input v-model="backendApiWebContextPath" :label="$t('lcdp.frontend.export.backendApiWebContextPath')" outlined dense style="width: 300px" />
</div>
<div class="fit row wrap justify-center items-start content-start q-pt-md q-gutter-md">
<q-btn :loading="loading" outlined :label="$t('lcdp.frontend.export')" @click="exportFrontend" />
</div>
<q-dialog v-model="prompt" persistent>
<q-card style="min-width: 350px">
<q-card-section class="q-pt-md">
<q-input v-model="configureName" :label="$t('name')" outlined dense autofocus @keyup.enter="prompt = false" />
</q-card-section>
<q-card-actions align="right" class="text-primary">
<q-btn v-close-popup flat :label="$t('cancel')" />
<q-btn v-close-popup flat :label="$t('confirm')" @click="newTheme" />
</q-card-actions>
</q-card>
</q-dialog>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useQuasar } from 'quasar';
import { Environment, axios, Tools, NotifyManager } from '@/platform';
const { t } = useI18n();
const quasar = useQuasar();
const selectedTab = ref('brand');
const selectedTheme = ref('');
const themeOptions = ref([]);
const prompt = ref(false);
const configureName = ref('');
let configureMap = new Map<string, object>();
const newTheme = () => {
axios.post(Environment.apiContextPath('/api/lcdp/configure'), { name: configureName.value }).then((response) => {
NotifyManager.info(t('success'));
refresh(() => {
selectedTheme.value = response.data.id;
configureName.value = '';
});
});
};
const themeChanged = (value) => {
const option = configureMap.get(value);
if (option) {
Tools.mergeObject(Environment.getConfigure(), option);
}
};
const defaultTheme = () => {
quasar
.dialog({
title: t('confirm'),
message: t('theme.action.setDefaultTheme.confirm', { themeName: configureMap.get(selectedTheme.value).name }),
cancel: true,
persistent: true,
import { ref, reactive } from 'vue';
import { axios, Environment, Downloader } from '@/platform';
const frontendWebContextPath = ref('/');
const backendApiWebContextPath = ref('/');
const loading = ref(false);
const exportFileInfo = reactive({ filePath: '', exportName: '' });
const exportFrontend = () => {
loading.value = true;
axios
.post(Environment.apiContextPath('/api/mvc/frontend/export'), {
frontendWebContextPath: frontendWebContextPath.value,
backendApiWebContextPath: backendApiWebContextPath.value,
})
.onOk(() => {
axios.post(Environment.apiContextPath('/api/lcdp/configure/activeConfigure/') + selectedTheme.value).then((response) => {
NotifyManager.info(t('success'));
refresh();
});
});
};
const removeTheme = () => {
quasar
.dialog({
title: t('confirm'),
message: t('theme.action.delete.confirm', { themeName: configureMap.get(selectedTheme.value).name }),
cancel: true,
persistent: true,
})
.onOk(() => {
axios.delete(Environment.apiContextPath('/api/lcdp/configure/') + selectedTheme.value).then((response) => {
NotifyManager.info(t('success'));
refresh();
selectedTheme.value = '';
});
});
};
const saveTheme = () => {
quasar
.dialog({
title: t('confirm'),
message: t('theme.action.save.confirm', { themeName: configureMap.get(selectedTheme.value).name }),
cancel: true,
persistent: true,
.then((response) => {
exportFileInfo.filePath = response.data.filePath;
exportFileInfo.exportName = response.data.exportName;
let url = Environment.apiContextPath('/api/mvc/download?');
url += 'filePath=' + encodeURIComponent(exportFileInfo.filePath);
url += '&exportName=' + encodeURIComponent(exportFileInfo.exportName);
Downloader.get(url);
})
.onOk(() => {
const data = {};
Tools.mergeObject(data, configureMap.get(selectedTheme.value));
data.theme = Environment.getConfigure().theme;
axios.put(Environment.apiContextPath('/api/lcdp/configure/') + selectedTheme.value, data).then((response) => {
NotifyManager.info(t('success'));
});
.finally(() => {
loading.value = false;
});
};
const refresh = (callback?: any) => {
axios.get(Environment.apiContextPath('/api/lcdp/configure')).then((response) => {
configureMap.clear();
const options = [];
selectedTheme.value = '';
const items = response.data.content;
if (items && items.length > 0) {
for (const item of items) {
options.push(item);
configureMap.set(item.id, item);
if (item.active) {
selectedTheme.value = item.id;
}
}
}
themeOptions.value = options;
if (callback) {
callback();
}
});
};
refresh();
</script>

70
io.sc.platform.core.frontend/src/views/Standard.vue

@ -0,0 +1,70 @@
<template>
<div style="height: 100%">
<q-btn-dropdown dense outline>
<template #label><div>更多</div></template>
</q-btn-dropdown>
<q-splitter v-model="splitterModelRef" style="height: 100%">
<template #before>
<q-tabs v-model="selectedTabRef" vertical>
<q-tab name="Industry" no-caps>
<div class="text-left" style="width: 200px" :title="$t('standard.industry.grid.title')">{{ $t('standard.industry.grid.title') }}</div>
</q-tab>
<q-tab name="AdminDivision" no-caps>
<div class="text-left" style="width: 200px" :title="$t('standard.adminDivision.grid.title')">{{ $t('standard.adminDivision.grid.title') }}</div>
</q-tab>
<q-tab name="PerformanceStatus" no-caps>
<div class="text-left" style="width: 200px" :title="$t('standard.performanceStatus.grid.title')">
{{ $t('standard.performanceStatus.grid.title') }}
</div>
</q-tab>
<q-tab name="Counterparty" no-caps>
<div class="text-left" style="width: 200px" :title="$t('standard.counterparty.grid.title')">{{ $t('standard.counterparty.grid.title') }}</div>
</q-tab>
<q-tab name="Country" no-caps>
<div class="text-left" style="width: 200px" :title="$t('standard.country.grid.title')">{{ $t('standard.country.grid.title') }}</div>
</q-tab>
<q-tab name="Currency" no-caps>
<div class="text-left" style="width: 200px" :title="$t('standard.currency.grid.title')">{{ $t('standard.currency.grid.title') }}</div>
</q-tab>
</q-tabs>
</template>
<template #after>
<div class="pl-1" style="height: 100%">
<q-tab-panels v-model="selectedTabRef" animated swipeable style="height: 100%">
<q-tab-panel name="Industry" class="px-0 pb-0" style="height: 100%; padding-left: 0px; padding-right: 0px; padding-bottom: 0px">
<Industry></Industry>
</q-tab-panel>
<q-tab-panel name="AdminDivision" class="px-0 pb-0" style="height: 100%; padding-left: 0px; padding-right: 0px; padding-bottom: 0px">
<AdminDivision></AdminDivision>
</q-tab-panel>
<q-tab-panel name="PerformanceStatus" class="px-0 pb-0" style="height: 100%; padding-left: 0px; padding-right: 0px; padding-bottom: 0px">
<PerformanceStatus></PerformanceStatus>
</q-tab-panel>
<q-tab-panel name="Counterparty" class="px-0 pb-0" style="height: 100%; padding-left: 0px; padding-right: 0px; padding-bottom: 0px">
<Counterparty></Counterparty>
</q-tab-panel>
<q-tab-panel name="Country" class="px-0 pb-0" style="height: 100%; padding-left: 0px; padding-right: 0px; padding-bottom: 0px">
<Country></Country>
</q-tab-panel>
<q-tab-panel name="Currency" class="px-0 pb-0" style="height: 100%; padding-left: 0px; padding-right: 0px; padding-bottom: 0px">
<Currency></Currency>
</q-tab-panel>
</q-tab-panels>
</div>
</template>
</q-splitter>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import Industry from './standard/Industry.vue';
import AdminDivision from './standard/AdminDivision.vue';
import PerformanceStatus from './standard/PerformanceStatus.vue';
import Counterparty from './standard/Counterparty.vue';
import Country from './standard/Country.vue';
import Currency from './standard/Currency.vue';
const splitterModelRef = ref(15);
const selectedTabRef = ref('Industry');
</script>

109
io.sc.platform.core.frontend/src/views/standard/AdminDivision.vue

@ -0,0 +1,109 @@
<template>
<w-grid
ref="gridRef"
:title="$t('standard.adminDivision.grid.title')"
hide-bottom
:config-button="true"
selection="multiple"
:checkbox-selection="true"
dense
:tree="true"
:tree-icon="
(row) => {
return { name: 'folder', color: 'amber' };
}
"
primary-key="code"
:data-url="Environment.apiContextPath('/api/standard/adminDivision')"
:pageable="false"
:pagination="{
sortBy: 'code',
descending: false,
}"
:toolbar-configure="{ noIcon: false }"
:toolbar-actions="[
'refresh',
'separator',
[
{
extend: 'add',
click: undefined,
},
{
extend: 'addTop',
label: $t('standard.adminDivision.grid.toolbar.addTop'),
},
{
extend: 'addChild',
label: $t('standard.adminDivision.grid.toolbar.addChild'),
},
],
'edit',
'remove',
'separator',
'view',
'separator',
'export',
]"
:columns="[
{
name: 'target',
label: $t('standard.entity.target'),
columns: [
{ width: 300, name: 'name', label: $t('name') },
{ width: 100, name: 'code', label: $t('code') },
{ width: 100, name: 'description', label: $t('description') },
],
},
{
name: 'source',
label: $t('standard.entity.source'),
columns: [
{ width: 300, name: 'mappingName', label: $t('name') },
{ width: 100, name: 'mappingCode', label: $t('code') },
{ width: 100, name: 'mappingDescription', label: $t('description') },
],
},
{ width: 100, name: 'lastModifier', label: $t('lastModifier') },
{ width: 120, name: 'lastModifyDate', label: $t('lastModifyDate'), format: Formater.dateOnly() },
]"
:editor="{
dialog: {
width: '600px',
},
form: {
colsNum: 1,
fields: [
{ name: 'code', label: $t('standard.entity.code'), type: 'text', required: true },
{ name: 'name', label: $t('standard.entity.name'), type: 'text', required: true },
{ name: 'description', label: $t('standard.entity.description'), type: 'textarea', rows: 3 },
{ name: 'mappingCode', label: $t('standard.entity.mappingCode'), type: 'text', required: true },
{ name: 'mappingName', label: $t('standard.entity.mappingName'), type: 'text' },
{ name: 'mappingDescription', label: $t('standard.entity.mappingDescription'), type: 'textarea', rows: 3 },
],
},
}"
:viewer="{
panel: {
columnNum: 1,
fields: [
{ name: 'code', label: $t('standard.entity.code') },
{ name: 'name', label: $t('standard.entity.name') },
{ name: 'description', label: $t('standard.entity.description') },
{ name: 'mappingCode', label: $t('standard.entity.mappingCode') },
{ name: 'mappingName', label: $t('standard.entity.mappingName') },
{ name: 'mappingDescription', label: $t('standard.entity.mappingDescription') },
{ name: 'dataComeFrom', label: $t('dataComeFrom') },
{ name: 'creator', label: $t('creator') },
{ name: 'createDate', label: $t('createDate') },
{ name: 'lastModifier', label: $t('lastModifier') },
{ name: 'lastModifyDate', label: $t('lastModifyDate'), format: Formater.none() },
],
},
}"
></w-grid>
</template>
<script setup lang="ts">
import { Environment, Formater } from '@/platform';
</script>

79
io.sc.platform.core.frontend/src/views/standard/Counterparty.vue

@ -0,0 +1,79 @@
<template>
<w-grid
ref="gridRef"
:title="$t('standard.counterparty.grid.title')"
:config-button="true"
selection="multiple"
:checkbox-selection="true"
:tree="false"
primary-key="code"
:data-url="Environment.apiContextPath('/api/standard/counterparty')"
:pageable="true"
:pagination="{
sortBy: 'code',
descending: false,
}"
:toolbar-configure="{ noIcon: false }"
:toolbar-actions="['refresh', 'separator', 'add', 'edit', 'remove', 'separator', 'view', 'separator', 'export']"
:columns="[
{
name: 'target',
label: $t('standard.entity.target'),
columns: [
{ width: 150, name: 'code', label: $t('code') },
{ width: 200, name: 'name', label: $t('name') },
{ width: 100, name: 'description', label: $t('description') },
],
},
{
name: 'source',
label: $t('standard.entity.source'),
columns: [
{ width: 150, name: 'mappingCode', label: $t('code') },
{ width: 200, name: 'mappingName', label: $t('name') },
{ width: 100, name: 'mappingDescription', label: $t('description') },
],
},
{ width: 100, name: 'lastModifier', label: $t('lastModifier') },
{ width: 120, name: 'lastModifyDate', label: $t('lastModifyDate'), format: Formater.dateOnly() },
]"
:editor="{
dialog: {
width: '600px',
},
form: {
colsNum: 1,
fields: [
{ name: 'code', label: $t('standard.entity.code'), type: 'text', required: true },
{ name: 'name', label: $t('standard.entity.name'), type: 'text', required: true },
{ name: 'description', label: $t('standard.entity.description'), type: 'textarea', rows: 3 },
{ name: 'mappingCode', label: $t('standard.entity.mappingCode'), type: 'text', required: true },
{ name: 'mappingName', label: $t('standard.entity.mappingName'), type: 'text' },
{ name: 'mappingDescription', label: $t('standard.entity.mappingDescription'), type: 'textarea', rows: 3 },
],
},
}"
:viewer="{
panel: {
columnNum: 1,
fields: [
{ name: 'code', label: $t('standard.entity.code') },
{ name: 'name', label: $t('standard.entity.name') },
{ name: 'description', label: $t('standard.entity.description') },
{ name: 'mappingCode', label: $t('standard.entity.mappingCode') },
{ name: 'mappingName', label: $t('standard.entity.mappingName') },
{ name: 'mappingDescription', label: $t('standard.entity.mappingDescription') },
{ name: 'dataComeFrom', label: $t('dataComeFrom') },
{ name: 'creator', label: $t('creator') },
{ name: 'createDate', label: $t('createDate') },
{ name: 'lastModifier', label: $t('lastModifier') },
{ name: 'lastModifyDate', label: $t('lastModifyDate'), format: Formater.none() },
],
},
}"
></w-grid>
</template>
<script setup lang="ts">
import { Environment, Formater } from '@/platform';
</script>

257
io.sc.platform.core.frontend/src/views/standard/Country.vue

@ -0,0 +1,257 @@
<template>
<w-grid
ref="gridRef"
:title="$t('standard.country.grid.title')"
:hide-bottom="false"
:config-button="true"
selection="multiple"
:checkbox-selection="true"
:tree="false"
primary-key="code"
:data-url="Environment.apiContextPath('/api/standard/country')"
:pageable="false"
:sort-by="['codeLatin3']"
:toolbar-configure="{ noIcon: false }"
:toolbar-actions="['query', 'refresh', 'separator', 'add', 'edit', 'remove', 'separator', 'view', 'separator', 'export']"
:query-form-cols-num="5"
:query-form-fields="[
{ name: 'code', label: $t('standard.country.entity.code'), type: 'text', clearable: true },
{ name: 'codeLatin3', label: $t('standard.country.entity.codeLatin3'), type: 'text', clearable: true },
{ name: 'nameChinese', label: $t('standard.country.entity.nameChinese'), type: 'text', clearable: true },
{ name: 'nameEnglish', label: $t('standard.country.entity.nameEnglish'), type: 'text', clearable: true },
]"
:columns="columnsComputed"
:editor="{
dialog: {
width: '800px',
},
form: {
colsNum: 3,
fields: [
{ name: 'code', label: $t('standard.country.entity.code'), type: 'text', required: true },
{ name: 'codeLatin2', label: $t('standard.country.entity.codeLatin2'), type: 'text', required: true },
{ name: 'codeLatin3', label: $t('standard.country.entity.codeLatin3'), type: 'text', required: true },
{ name: 'nameChinese', label: $t('standard.country.entity.nameChinese'), type: 'text', required: true },
{ name: 'nameChineseFull', label: $t('standard.country.entity.nameChineseFull'), type: 'text', colSpan: 2 },
{ name: 'remarkChinese', label: $t('standard.country.entity.remarkChinese'), type: 'text', colSpan: 3 },
{ name: 'nameEnglish', label: $t('standard.country.entity.nameEnglish'), type: 'text', required: true },
{ name: 'nameEnglishFull', label: $t('standard.country.entity.nameEnglishFull'), type: 'text', colSpan: 2 },
{ name: 'remarkEnglish', label: $t('standard.country.entity.remarkEnglish'), type: 'text', colSpan: 3 },
{ name: 'nameLocalShort', label: $t('standard.country.entity.nameLocalShort'), type: 'text', colSpan: 2 },
{ name: 'isIndependent', label: $t('standard.country.entity.isIndependent'), type: 'select', options: Options.yesNo() },
{
name: 'currency',
label: $t('standard.country.entity.currency'),
type: 'select',
options: currencyOptionsRef,
colSpan: 2,
useInput: true,
hideSelected: true,
fillInput: true,
onFilter: (val, update) => {
if (val) {
update(() => {
const input = val.toUpperCase();
const currencyOptions = [];
for (const currency of currencyList) {
if (
currency.code?.indexOf(input) > -1 ||
currency.codeLatin?.indexOf(input) > -1 ||
currency.nameChinese?.indexOf(input) > -1 ||
currency.nameEnglish?.indexOf(input) > -1
) {
const item = createCurrencyOptionItem(currency);
if (item) {
currencyOptions.push(item);
}
}
}
currencyOptionsRef = currencyOptions;
});
} else {
update(() => {
const currencyOptions = [];
for (const currency of currencyList) {
const item = createCurrencyOptionItem(currency);
if (item) {
currencyOptions.push(item);
}
}
currencyOptionsRef = currencyOptions;
});
}
},
},
{ name: 'languageCode2', label: $t('standard.country.entity.languageCode2'), type: 'text', colsFirst: true },
{ name: 'languageCode3', label: $t('standard.country.entity.languageCode3'), type: 'text' },
],
},
}"
:viewer="{
panel: {
columnNum: 1,
fields: [
{ name: 'code', label: $t('standard.country.entity.code') },
{ name: 'codeLatin2', label: $t('standard.country.entity.codeLatin2') },
{ name: 'codeLatin3', label: $t('standard.country.entity.codeLatin3') },
{ name: 'nameChinese', label: $t('standard.country.entity.nameChinese') },
{ name: 'nameChineseFull', label: $t('standard.country.entity.nameChineseFull') },
{ name: 'nameEnglish', label: $t('standard.country.entity.nameEnglish') },
{ name: 'nameEnglishFull', label: $t('standard.country.entity.nameEnglishFull') },
{ name: 'nameLocalShort', label: $t('standard.country.entity.nameLocalShort') },
{ name: 'remarkChinese', label: $t('standard.country.entity.remarkChinese') },
{ name: 'remarkEnglish', label: $t('standard.country.entity.remarkEnglish') },
{ name: 'languageCode2', label: $t('standard.country.entity.languageCode2') },
{ name: 'languageCode3', label: $t('standard.country.entity.languageCode3') },
{ name: 'isIndependent', label: $t('standard.country.entity.isIndependent') },
{
name: 'currency',
label: $t('standard.country.entity.currency'),
format: (value, row) => {
return getCurrencyLabel(currencyMap[row.currency]);
},
},
{ name: 'dataComeFrom', label: $t('dataComeFrom') },
{ name: 'creator', label: $t('creator') },
{ name: 'createDate', label: $t('createDate') },
{ name: 'lastModifier', label: $t('lastModifier') },
{ name: 'lastModifyDate', label: $t('lastModifyDate'), format: Formater.none() },
],
},
}"
></w-grid>
</template>
<script setup lang="ts">
import { ref, onMounted, computed } from 'vue';
import { useI18n } from 'vue-i18n';
import { axios, Environment, Formater, Options, eventBus } from '@/platform';
let currentLocale = Environment.getConfigure().setting.i18n.locale;
let currencyMap = {};
let currencyList = [];
const { t } = useI18n();
const currencyOptionsRef = ref([]);
const columnsComputed = computed(() => {
const result = [
{ width: 70, name: 'code', label: t('code') },
{ width: 70, name: 'codeLatin3', label: t('code') + '3' },
{
width: 150,
name: 'nameChinese',
label: t('name'),
format: (value, row) => {
if (row) {
return getCountryName(row);
}
},
},
{
name: 'currency',
label: t('standard.entity.currency'),
columns: [
{
width: 70,
name: 'currencyCode',
label: t('code'),
sortable: false,
format: (value, row) => {
if (row.currency) {
return currencyMap[row.currency]?.code;
}
},
},
{
width: 70,
name: 'currencyCodeLatin',
label: t('code') + '2',
sortable: false,
format: (value, row) => {
if (row.currency) {
return currencyMap[row.currency]?.codeLatin;
}
},
},
{
width: 150,
name: 'currencyname',
label: t('name'),
sortable: false,
format: (value, row) => {
if (row.currency) {
return getCurrencyName(currencyMap[row.currency]);
}
},
},
],
},
{
name: 'language',
label: t('standard.entity.language'),
columns: [{ width: 100, name: 'languageCode3', label: t('code') }],
},
{ width: 100, name: 'lastModifier', label: t('lastModifier') },
{ width: 120, name: 'lastModifyDate', label: t('lastModifyDate'), format: Formater.dateOnly() },
];
currentLocale = Environment.getConfigure().setting.i18n.locale;
if (currentLocale.endsWith('CN')) {
result[2].name = 'nameChinese';
} else {
result[2].name = 'nameEnglish';
}
return result;
});
onMounted(() => {
axios.get(Environment.apiContextPath('/api/standard/currency?pageable=false&sortBy=code')).then((response) => {
currencyMap = {};
currencyList = [];
const currencyOptions = [];
if (response?.data?.content) {
for (const currency of response.data.content) {
currencyMap[currency.code] = currency;
currencyList.push(currency);
}
for (const currency of currencyList) {
const item = createCurrencyOptionItem(currency);
if (item) {
currencyOptions.push(item);
}
}
}
currencyOptionsRef.value = currencyOptions;
});
});
const getCurrencyLabel = (currency) => {
return currency.code + ' ' + currency.codeLatin + ' ' + (currentLocale.endsWith('CN') ? currency.nameChinese : currency.nameEnglish);
};
const createCurrencyOptionItem = (currency) => {
if (currency) {
return {
value: currency.code,
label: getCurrencyLabel(currency),
};
}
return null;
};
const getCountryName = (country) => {
if (country) {
return currentLocale.endsWith('CN') ? country.nameChinese : country.nameEnglish;
}
return null;
};
const getCurrencyName = (currency) => {
if (currency) {
return currentLocale.endsWith('CN') ? currency.nameChinese : currency.nameEnglish;
}
return null;
};
</script>

72
io.sc.platform.core.frontend/src/views/standard/Currency.vue

@ -0,0 +1,72 @@
<template>
<w-grid
ref="gridRef"
:title="$t('standard.currency.grid.title')"
:hide-bottom="false"
:config-button="true"
selection="multiple"
:checkbox-selection="true"
:tree="false"
primary-key="code"
:data-url="Environment.apiContextPath('/api/standard/currency')"
:pageable="false"
:pagination="{
sortBy: 'codeLatin',
descending: false,
}"
:toolbar-configure="{ noIcon: false }"
:toolbar-actions="['query', 'refresh', 'separator', 'add', 'edit', 'remove', 'separator', 'view', 'separator', 'export']"
:query-form-cols-num="4"
:query-form-fields="[
{ name: 'code', label: $t('standard.currency.entity.code'), type: 'text', clearable: true },
{ name: 'codeLatin', label: $t('standard.currency.entity.codeLatin'), type: 'text', clearable: true },
{ name: 'nameChinese', label: $t('standard.country.entity.nameChinese'), type: 'text', clearable: true },
{ name: 'nameEnglish', label: $t('standard.country.entity.nameEnglish'), type: 'text', clearable: true },
]"
:columns="[
{ width: 80, name: 'code', label: $t('standard.currency.entity.code') },
{ width: 80, name: 'codeLatin', label: $t('standard.currency.entity.codeLatin') },
{ width: 150, name: 'nameChinese', label: $t('standard.currency.entity.nameChinese') },
{ width: 150, name: 'nameEnglish', label: $t('standard.currency.entity.nameEnglish') },
{ width: 100, name: 'precision', label: $t('standard.currency.entity.precision') },
{ width: 100, name: 'lastModifier', label: $t('lastModifier') },
{ width: 120, name: 'lastModifyDate', label: $t('lastModifyDate'), format: Formater.dateOnly() },
]"
:editor="{
dialog: {
width: '600px',
},
form: {
colsNum: 1,
fields: [
{ name: 'code', label: $t('standard.currency.entity.code'), type: 'text', required: true },
{ name: 'codeLatin', label: $t('standard.currency.entity.codeLatin'), type: 'text', required: true },
{ name: 'nameChinese', label: $t('standard.country.entity.nameChinese'), type: 'text', required: true },
{ name: 'nameEnglish', label: $t('standard.country.entity.nameEnglish'), type: 'text' },
{ name: 'precision', label: $t('standard.currency.entity.precision'), type: 'text', required: true, defaultValue: 2 },
],
},
}"
:viewer="{
panel: {
columnNum: 1,
fields: [
{ name: 'code', label: $t('standard.currency.entity.code') },
{ name: 'codeLatin', label: $t('standard.currency.entity.codeLatin') },
{ name: 'nameChinese', label: $t('standard.country.entity.nameChinese') },
{ name: 'nameEnglish', label: $t('standard.country.entity.nameEnglish') },
{ name: 'precision', label: $t('standard.currency.entity.precision') },
{ name: 'dataComeFrom', label: $t('dataComeFrom') },
{ name: 'creator', label: $t('creator') },
{ name: 'createDate', label: $t('createDate') },
{ name: 'lastModifier', label: $t('lastModifier') },
{ name: 'lastModifyDate', label: $t('lastModifyDate'), format: Formater.none() },
],
},
}"
></w-grid>
</template>
<script setup lang="ts">
import { Environment, Formater, Options } from '@/platform';
</script>

109
io.sc.platform.core.frontend/src/views/standard/Industry.vue

@ -0,0 +1,109 @@
<template>
<w-grid
ref="treeGridRef"
:title="$t('standard.industry.grid.title')"
hide-bottom
:config-button="true"
selection="multiple"
:checkbox-selection="true"
dense
:tree="true"
:tree-icon="
(row) => {
return { name: 'folder', color: 'amber' };
}
"
primary-key="code"
:data-url="Environment.apiContextPath('/api/standard/industry')"
:pageable="false"
:pagination="{
sortBy: 'code',
descending: false,
}"
:toolbar-configure="{ noIcon: false }"
:toolbar-actions="[
'refresh',
'separator',
[
{
extend: 'add',
click: undefined,
},
{
extend: 'addTop',
label: $t('standard.industry.grid.toolbar.addTop'),
},
{
extend: 'addChild',
label: $t('standard.industry.grid.toolbar.addChild'),
},
],
'edit',
'remove',
'separator',
'view',
'separator',
'export',
]"
:columns="[
{
name: 'target',
label: $t('standard.entity.target'),
columns: [
{ width: 300, name: 'name', label: $t('name') },
{ width: 100, name: 'code', label: $t('code') },
{ width: 100, name: 'description', label: $t('description') },
],
},
{
name: 'source',
label: $t('standard.entity.source'),
columns: [
{ width: 300, name: 'mappingName', label: $t('name') },
{ width: 100, name: 'mappingCode', label: $t('code') },
{ width: 100, name: 'mappingDescription', label: $t('description') },
],
},
{ width: 100, name: 'lastModifier', label: $t('lastModifier') },
{ width: 120, name: 'lastModifyDate', label: $t('lastModifyDate'), format: Formater.dateOnly() },
]"
:editor="{
dialog: {
width: '600px',
},
form: {
colsNum: 1,
fields: [
{ name: 'code', label: $t('standard.entity.code'), type: 'text', required: true },
{ name: 'name', label: $t('standard.entity.name'), type: 'text', required: true },
{ name: 'description', label: $t('standard.entity.description'), type: 'textarea', rows: 3 },
{ name: 'mappingCode', label: $t('standard.entity.mappingCode'), type: 'text', required: true },
{ name: 'mappingName', label: $t('standard.entity.mappingName'), type: 'text' },
{ name: 'mappingDescription', label: $t('standard.entity.mappingDescription'), type: 'textarea', rows: 3 },
],
},
}"
:viewer="{
panel: {
columnNum: 1,
fields: [
{ name: 'code', label: $t('standard.entity.code') },
{ name: 'name', label: $t('standard.entity.name') },
{ name: 'description', label: $t('standard.entity.description') },
{ name: 'mappingCode', label: $t('standard.entity.mappingCode') },
{ name: 'mappingName', label: $t('standard.entity.mappingName') },
{ name: 'mappingDescription', label: $t('standard.entity.mappingDescription') },
{ name: 'dataComeFrom', label: $t('dataComeFrom') },
{ name: 'creator', label: $t('creator') },
{ name: 'createDate', label: $t('createDate') },
{ name: 'lastModifier', label: $t('lastModifier') },
{ name: 'lastModifyDate', label: $t('lastModifyDate'), format: Formater.none() },
],
},
}"
></w-grid>
</template>
<script setup lang="ts">
import { Environment, Formater } from '@/platform';
</script>

79
io.sc.platform.core.frontend/src/views/standard/PerformanceStatus.vue

@ -0,0 +1,79 @@
<template>
<w-grid
ref="gridRef"
:title="$t('standard.performanceStatus.grid.title')"
:config-button="true"
selection="multiple"
:checkbox-selection="true"
:tree="false"
primary-key="code"
:data-url="Environment.apiContextPath('/api/standard/performanceStatus')"
:pageable="true"
:pagination="{
sortBy: 'code',
descending: false,
}"
:toolbar-configure="{ noIcon: false }"
:toolbar-actions="['refresh', 'separator', 'add', 'edit', 'remove', 'separator', 'view', 'separator', 'export']"
:columns="[
{
name: 'target',
label: $t('standard.entity.target'),
columns: [
{ width: 100, name: 'code', label: $t('code') },
{ width: 100, name: 'name', label: $t('name') },
{ width: 100, name: 'description', label: $t('description') },
],
},
{
name: 'source',
label: $t('standard.entity.source'),
columns: [
{ width: 100, name: 'mappingCode', label: $t('code') },
{ width: 100, name: 'mappingName', label: $t('name') },
{ width: 100, name: 'mappingDescription', label: $t('description') },
],
},
{ width: 100, name: 'lastModifier', label: $t('lastModifier') },
{ width: 120, name: 'lastModifyDate', label: $t('lastModifyDate'), format: Formater.dateOnly() },
]"
:editor="{
dialog: {
width: '600px',
},
form: {
colsNum: 1,
fields: [
{ name: 'code', label: $t('standard.entity.code'), type: 'text', required: true },
{ name: 'name', label: $t('standard.entity.name'), type: 'text', required: true },
{ name: 'description', label: $t('standard.entity.description'), type: 'textarea', rows: 3 },
{ name: 'mappingCode', label: $t('standard.entity.mappingCode'), type: 'text', required: true },
{ name: 'mappingName', label: $t('standard.entity.mappingName'), type: 'text' },
{ name: 'mappingDescription', label: $t('standard.entity.mappingDescription'), type: 'textarea', rows: 3 },
],
},
}"
:viewer="{
panel: {
columnNum: 1,
fields: [
{ name: 'code', label: $t('standard.entity.code') },
{ name: 'name', label: $t('standard.entity.name') },
{ name: 'description', label: $t('standard.entity.description') },
{ name: 'mappingCode', label: $t('standard.entity.mappingCode') },
{ name: 'mappingName', label: $t('standard.entity.mappingName') },
{ name: 'mappingDescription', label: $t('standard.entity.mappingDescription') },
{ name: 'dataComeFrom', label: $t('dataComeFrom') },
{ name: 'creator', label: $t('creator') },
{ name: 'createDate', label: $t('createDate') },
{ name: 'lastModifier', label: $t('lastModifier') },
{ name: 'lastModifyDate', label: $t('lastModifyDate'), format: Formater.none() },
],
},
}"
></w-grid>
</template>
<script setup lang="ts">
import { Environment, Formater } from '@/platform';
</script>

4
io.sc.platform.core.frontend/template-project/package.json

@ -1,6 +1,6 @@
{
"name": "platform-core",
"version": "8.1.190",
"version": "8.1.193",
"description": "前端核心包,用于快速构建前端的脚手架",
"private": false,
"keywords": [],
@ -93,7 +93,7 @@
"luckyexcel": "1.0.1",
"mockjs": "1.1.0",
"pinia": "2.1.7",
"platform-core": "8.1.190",
"platform-core": "8.1.193",
"quasar": "2.15.3",
"tailwindcss": "3.4.3",
"vue": "3.4.24",

2
io.sc.platform.core.frontend/template-project/src/components/index.ts

@ -2,7 +2,7 @@
* ,
*/
import component_testcase_formElements from '@/views/FormElements.vue';
import component_testcase_formElements from '@/views/Standard.vue';
import component_testcase_likmDialog from '@/views/likm/Dialog.vue';
import component_testcase_likmDrawer from '@/views/likm/Drawer.vue';
import component_testcase_likmForm from '@/views/likm/Form.vue';

2
io.sc.platform.core.frontend/template-project/src/routes/routes.json

@ -5,7 +5,7 @@
"parent": "/",
"priority": 0,
"component": "component.testcase.formElements",
"componentPath": "@/views/FormElements.vue",
"componentPath": "@/views/Standard.vue",
"redirect": null,
"meta": {
"permissions": ["/testcase/formElements/**/*"]

179
io.sc.platform.core.frontend/template-project/src/views/FormElements.vue

@ -1,160 +1,41 @@
<template>
<div>
<div class="row justify-end q-gutter-md q-py-md">
<q-select
v-model="selectedTheme"
:label="$t('theme')"
:options="themeOptions"
option-label="name"
option-value="id"
emit-value
map-options
dense
outlined
style="min-width: 200px"
@update:model-value="themeChanged"
>
<template #option="scope">
<q-item v-bind="scope.itemProps">
<q-item-section>
<q-item-label>{{ scope.opt.name }}</q-item-label>
</q-item-section>
<q-item-section v-if="scope.opt.active" avatar>
<q-icon name="bi-check" />
</q-item-section>
<q-item-section v-else avatar>
<w-icon-empty></w-icon-empty>
</q-item-section>
</q-item>
</template>
</q-select>
<q-btn :label="$t('theme.action.setDefaultTheme')" icon="bi-heart" no-caps outline dense padding="2px 10px" @click="defaultTheme" />
<q-btn :label="$t('add')" icon="add" no-caps outline dense padding="2px 10px" @click="prompt = true" />
<q-btn :label="$t('delete')" icon="delete" no-caps outline dense padding="2px 10px" @click="removeTheme" />
<q-btn :label="$t('save')" icon="save" no-caps outline dense padding="2px 10px" @click="saveTheme" />
<div class="fit row wrap justify-center items-start content-start q-pt-md q-gutter-md">
<q-input v-model="frontendWebContextPath" :label="$t('lcdp.frontend.export.frontendWebContextPath')" outlined dense style="width: 300px" />
</div>
<div class="fit row wrap justify-center items-start content-start q-pt-md q-gutter-md">
<q-input v-model="backendApiWebContextPath" :label="$t('lcdp.frontend.export.backendApiWebContextPath')" outlined dense style="width: 300px" />
</div>
<div class="fit row wrap justify-center items-start content-start q-pt-md q-gutter-md">
<q-btn :loading="loading" outlined :label="$t('lcdp.frontend.export')" @click="exportFrontend" />
</div>
<q-dialog v-model="prompt" persistent>
<q-card style="min-width: 350px">
<q-card-section class="q-pt-md">
<q-input v-model="configureName" :label="$t('name')" outlined dense autofocus @keyup.enter="prompt = false" />
</q-card-section>
<q-card-actions align="right" class="text-primary">
<q-btn v-close-popup flat :label="$t('cancel')" />
<q-btn v-close-popup flat :label="$t('confirm')" @click="newTheme" />
</q-card-actions>
</q-card>
</q-dialog>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useQuasar } from 'quasar';
import { Environment, axios, Tools, NotifyManager } from '@/platform';
const { t } = useI18n();
const quasar = useQuasar();
const selectedTab = ref('brand');
const selectedTheme = ref('');
const themeOptions = ref([]);
const prompt = ref(false);
const configureName = ref('');
let configureMap = new Map<string, object>();
const newTheme = () => {
axios.post(Environment.apiContextPath('/api/lcdp/configure'), { name: configureName.value }).then((response) => {
NotifyManager.info(t('success'));
refresh(() => {
selectedTheme.value = response.data.id;
configureName.value = '';
});
});
};
const themeChanged = (value) => {
const option = configureMap.get(value);
if (option) {
Tools.mergeObject(Environment.getConfigure(), option);
}
};
const defaultTheme = () => {
quasar
.dialog({
title: t('confirm'),
message: t('theme.action.setDefaultTheme.confirm', { themeName: configureMap.get(selectedTheme.value).name }),
cancel: true,
persistent: true,
import { ref, reactive } from 'vue';
import { axios, Environment, Downloader } from '@/platform';
const frontendWebContextPath = ref('/');
const backendApiWebContextPath = ref('/');
const loading = ref(false);
const exportFileInfo = reactive({ filePath: '', exportName: '' });
const exportFrontend = () => {
loading.value = true;
axios
.post(Environment.apiContextPath('/api/mvc/frontend/export'), {
frontendWebContextPath: frontendWebContextPath.value,
backendApiWebContextPath: backendApiWebContextPath.value,
})
.onOk(() => {
axios.post(Environment.apiContextPath('/api/lcdp/configure/activeConfigure/') + selectedTheme.value).then((response) => {
NotifyManager.info(t('success'));
refresh();
});
});
};
const removeTheme = () => {
quasar
.dialog({
title: t('confirm'),
message: t('theme.action.delete.confirm', { themeName: configureMap.get(selectedTheme.value).name }),
cancel: true,
persistent: true,
})
.onOk(() => {
axios.delete(Environment.apiContextPath('/api/lcdp/configure/') + selectedTheme.value).then((response) => {
NotifyManager.info(t('success'));
refresh();
selectedTheme.value = '';
});
});
};
const saveTheme = () => {
quasar
.dialog({
title: t('confirm'),
message: t('theme.action.save.confirm', { themeName: configureMap.get(selectedTheme.value).name }),
cancel: true,
persistent: true,
.then((response) => {
exportFileInfo.filePath = response.data.filePath;
exportFileInfo.exportName = response.data.exportName;
let url = Environment.apiContextPath('/api/mvc/download?');
url += 'filePath=' + encodeURIComponent(exportFileInfo.filePath);
url += '&exportName=' + encodeURIComponent(exportFileInfo.exportName);
Downloader.get(url);
})
.onOk(() => {
const data = {};
Tools.mergeObject(data, configureMap.get(selectedTheme.value));
data.theme = Environment.getConfigure().theme;
axios.put(Environment.apiContextPath('/api/lcdp/configure/') + selectedTheme.value, data).then((response) => {
NotifyManager.info(t('success'));
});
.finally(() => {
loading.value = false;
});
};
const refresh = (callback?: any) => {
axios.get(Environment.apiContextPath('/api/lcdp/configure')).then((response) => {
configureMap.clear();
const options = [];
selectedTheme.value = '';
const items = response.data.content;
if (items && items.length > 0) {
for (const item of items) {
options.push(item);
configureMap.set(item.id, item);
if (item.active) {
selectedTheme.value = item.id;
}
}
}
themeOptions.value = options;
if (callback) {
callback();
}
});
};
refresh();
</script>

70
io.sc.platform.core.frontend/template-project/src/views/Standard.vue

@ -0,0 +1,70 @@
<template>
<div style="height: 100%">
<q-btn-dropdown dense outline>
<template #label><div>更多</div></template>
</q-btn-dropdown>
<q-splitter v-model="splitterModelRef" style="height: 100%">
<template #before>
<q-tabs v-model="selectedTabRef" vertical>
<q-tab name="Industry" no-caps>
<div class="text-left" style="width: 200px" :title="$t('standard.industry.grid.title')">{{ $t('standard.industry.grid.title') }}</div>
</q-tab>
<q-tab name="AdminDivision" no-caps>
<div class="text-left" style="width: 200px" :title="$t('standard.adminDivision.grid.title')">{{ $t('standard.adminDivision.grid.title') }}</div>
</q-tab>
<q-tab name="PerformanceStatus" no-caps>
<div class="text-left" style="width: 200px" :title="$t('standard.performanceStatus.grid.title')">
{{ $t('standard.performanceStatus.grid.title') }}
</div>
</q-tab>
<q-tab name="Counterparty" no-caps>
<div class="text-left" style="width: 200px" :title="$t('standard.counterparty.grid.title')">{{ $t('standard.counterparty.grid.title') }}</div>
</q-tab>
<q-tab name="Country" no-caps>
<div class="text-left" style="width: 200px" :title="$t('standard.country.grid.title')">{{ $t('standard.country.grid.title') }}</div>
</q-tab>
<q-tab name="Currency" no-caps>
<div class="text-left" style="width: 200px" :title="$t('standard.currency.grid.title')">{{ $t('standard.currency.grid.title') }}</div>
</q-tab>
</q-tabs>
</template>
<template #after>
<div class="pl-1" style="height: 100%">
<q-tab-panels v-model="selectedTabRef" animated swipeable style="height: 100%">
<q-tab-panel name="Industry" class="px-0 pb-0" style="height: 100%; padding-left: 0px; padding-right: 0px; padding-bottom: 0px">
<Industry></Industry>
</q-tab-panel>
<q-tab-panel name="AdminDivision" class="px-0 pb-0" style="height: 100%; padding-left: 0px; padding-right: 0px; padding-bottom: 0px">
<AdminDivision></AdminDivision>
</q-tab-panel>
<q-tab-panel name="PerformanceStatus" class="px-0 pb-0" style="height: 100%; padding-left: 0px; padding-right: 0px; padding-bottom: 0px">
<PerformanceStatus></PerformanceStatus>
</q-tab-panel>
<q-tab-panel name="Counterparty" class="px-0 pb-0" style="height: 100%; padding-left: 0px; padding-right: 0px; padding-bottom: 0px">
<Counterparty></Counterparty>
</q-tab-panel>
<q-tab-panel name="Country" class="px-0 pb-0" style="height: 100%; padding-left: 0px; padding-right: 0px; padding-bottom: 0px">
<Country></Country>
</q-tab-panel>
<q-tab-panel name="Currency" class="px-0 pb-0" style="height: 100%; padding-left: 0px; padding-right: 0px; padding-bottom: 0px">
<Currency></Currency>
</q-tab-panel>
</q-tab-panels>
</div>
</template>
</q-splitter>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import Industry from './standard/Industry.vue';
import AdminDivision from './standard/AdminDivision.vue';
import PerformanceStatus from './standard/PerformanceStatus.vue';
import Counterparty from './standard/Counterparty.vue';
import Country from './standard/Country.vue';
import Currency from './standard/Currency.vue';
const splitterModelRef = ref(15);
const selectedTabRef = ref('Industry');
</script>

109
io.sc.platform.core.frontend/template-project/src/views/standard/AdminDivision.vue

@ -0,0 +1,109 @@
<template>
<w-grid
ref="gridRef"
:title="$t('standard.adminDivision.grid.title')"
hide-bottom
:config-button="true"
selection="multiple"
:checkbox-selection="true"
dense
:tree="true"
:tree-icon="
(row) => {
return { name: 'folder', color: 'amber' };
}
"
primary-key="code"
:data-url="Environment.apiContextPath('/api/standard/adminDivision')"
:pageable="false"
:pagination="{
sortBy: 'code',
descending: false,
}"
:toolbar-configure="{ noIcon: false }"
:toolbar-actions="[
'refresh',
'separator',
[
{
extend: 'add',
click: undefined,
},
{
extend: 'addTop',
label: $t('standard.adminDivision.grid.toolbar.addTop'),
},
{
extend: 'addChild',
label: $t('standard.adminDivision.grid.toolbar.addChild'),
},
],
'edit',
'remove',
'separator',
'view',
'separator',
'export',
]"
:columns="[
{
name: 'target',
label: $t('standard.entity.target'),
columns: [
{ width: 300, name: 'name', label: $t('name') },
{ width: 100, name: 'code', label: $t('code') },
{ width: 100, name: 'description', label: $t('description') },
],
},
{
name: 'source',
label: $t('standard.entity.source'),
columns: [
{ width: 300, name: 'mappingName', label: $t('name') },
{ width: 100, name: 'mappingCode', label: $t('code') },
{ width: 100, name: 'mappingDescription', label: $t('description') },
],
},
{ width: 100, name: 'lastModifier', label: $t('lastModifier') },
{ width: 120, name: 'lastModifyDate', label: $t('lastModifyDate'), format: Formater.dateOnly() },
]"
:editor="{
dialog: {
width: '600px',
},
form: {
colsNum: 1,
fields: [
{ name: 'code', label: $t('standard.entity.code'), type: 'text', required: true },
{ name: 'name', label: $t('standard.entity.name'), type: 'text', required: true },
{ name: 'description', label: $t('standard.entity.description'), type: 'textarea', rows: 3 },
{ name: 'mappingCode', label: $t('standard.entity.mappingCode'), type: 'text', required: true },
{ name: 'mappingName', label: $t('standard.entity.mappingName'), type: 'text' },
{ name: 'mappingDescription', label: $t('standard.entity.mappingDescription'), type: 'textarea', rows: 3 },
],
},
}"
:viewer="{
panel: {
columnNum: 1,
fields: [
{ name: 'code', label: $t('standard.entity.code') },
{ name: 'name', label: $t('standard.entity.name') },
{ name: 'description', label: $t('standard.entity.description') },
{ name: 'mappingCode', label: $t('standard.entity.mappingCode') },
{ name: 'mappingName', label: $t('standard.entity.mappingName') },
{ name: 'mappingDescription', label: $t('standard.entity.mappingDescription') },
{ name: 'dataComeFrom', label: $t('dataComeFrom') },
{ name: 'creator', label: $t('creator') },
{ name: 'createDate', label: $t('createDate') },
{ name: 'lastModifier', label: $t('lastModifier') },
{ name: 'lastModifyDate', label: $t('lastModifyDate'), format: Formater.none() },
],
},
}"
></w-grid>
</template>
<script setup lang="ts">
import { Environment, Formater } from '@/platform';
</script>

79
io.sc.platform.core.frontend/template-project/src/views/standard/Counterparty.vue

@ -0,0 +1,79 @@
<template>
<w-grid
ref="gridRef"
:title="$t('standard.counterparty.grid.title')"
:config-button="true"
selection="multiple"
:checkbox-selection="true"
:tree="false"
primary-key="code"
:data-url="Environment.apiContextPath('/api/standard/counterparty')"
:pageable="true"
:pagination="{
sortBy: 'code',
descending: false,
}"
:toolbar-configure="{ noIcon: false }"
:toolbar-actions="['refresh', 'separator', 'add', 'edit', 'remove', 'separator', 'view', 'separator', 'export']"
:columns="[
{
name: 'target',
label: $t('standard.entity.target'),
columns: [
{ width: 150, name: 'code', label: $t('code') },
{ width: 200, name: 'name', label: $t('name') },
{ width: 100, name: 'description', label: $t('description') },
],
},
{
name: 'source',
label: $t('standard.entity.source'),
columns: [
{ width: 150, name: 'mappingCode', label: $t('code') },
{ width: 200, name: 'mappingName', label: $t('name') },
{ width: 100, name: 'mappingDescription', label: $t('description') },
],
},
{ width: 100, name: 'lastModifier', label: $t('lastModifier') },
{ width: 120, name: 'lastModifyDate', label: $t('lastModifyDate'), format: Formater.dateOnly() },
]"
:editor="{
dialog: {
width: '600px',
},
form: {
colsNum: 1,
fields: [
{ name: 'code', label: $t('standard.entity.code'), type: 'text', required: true },
{ name: 'name', label: $t('standard.entity.name'), type: 'text', required: true },
{ name: 'description', label: $t('standard.entity.description'), type: 'textarea', rows: 3 },
{ name: 'mappingCode', label: $t('standard.entity.mappingCode'), type: 'text', required: true },
{ name: 'mappingName', label: $t('standard.entity.mappingName'), type: 'text' },
{ name: 'mappingDescription', label: $t('standard.entity.mappingDescription'), type: 'textarea', rows: 3 },
],
},
}"
:viewer="{
panel: {
columnNum: 1,
fields: [
{ name: 'code', label: $t('standard.entity.code') },
{ name: 'name', label: $t('standard.entity.name') },
{ name: 'description', label: $t('standard.entity.description') },
{ name: 'mappingCode', label: $t('standard.entity.mappingCode') },
{ name: 'mappingName', label: $t('standard.entity.mappingName') },
{ name: 'mappingDescription', label: $t('standard.entity.mappingDescription') },
{ name: 'dataComeFrom', label: $t('dataComeFrom') },
{ name: 'creator', label: $t('creator') },
{ name: 'createDate', label: $t('createDate') },
{ name: 'lastModifier', label: $t('lastModifier') },
{ name: 'lastModifyDate', label: $t('lastModifyDate'), format: Formater.none() },
],
},
}"
></w-grid>
</template>
<script setup lang="ts">
import { Environment, Formater } from '@/platform';
</script>

257
io.sc.platform.core.frontend/template-project/src/views/standard/Country.vue

@ -0,0 +1,257 @@
<template>
<w-grid
ref="gridRef"
:title="$t('standard.country.grid.title')"
:hide-bottom="false"
:config-button="true"
selection="multiple"
:checkbox-selection="true"
:tree="false"
primary-key="code"
:data-url="Environment.apiContextPath('/api/standard/country')"
:pageable="false"
:sort-by="['codeLatin3']"
:toolbar-configure="{ noIcon: false }"
:toolbar-actions="['query', 'refresh', 'separator', 'add', 'edit', 'remove', 'separator', 'view', 'separator', 'export']"
:query-form-cols-num="5"
:query-form-fields="[
{ name: 'code', label: $t('standard.country.entity.code'), type: 'text', clearable: true },
{ name: 'codeLatin3', label: $t('standard.country.entity.codeLatin3'), type: 'text', clearable: true },
{ name: 'nameChinese', label: $t('standard.country.entity.nameChinese'), type: 'text', clearable: true },
{ name: 'nameEnglish', label: $t('standard.country.entity.nameEnglish'), type: 'text', clearable: true },
]"
:columns="columnsComputed"
:editor="{
dialog: {
width: '800px',
},
form: {
colsNum: 3,
fields: [
{ name: 'code', label: $t('standard.country.entity.code'), type: 'text', required: true },
{ name: 'codeLatin2', label: $t('standard.country.entity.codeLatin2'), type: 'text', required: true },
{ name: 'codeLatin3', label: $t('standard.country.entity.codeLatin3'), type: 'text', required: true },
{ name: 'nameChinese', label: $t('standard.country.entity.nameChinese'), type: 'text', required: true },
{ name: 'nameChineseFull', label: $t('standard.country.entity.nameChineseFull'), type: 'text', colSpan: 2 },
{ name: 'remarkChinese', label: $t('standard.country.entity.remarkChinese'), type: 'text', colSpan: 3 },
{ name: 'nameEnglish', label: $t('standard.country.entity.nameEnglish'), type: 'text', required: true },
{ name: 'nameEnglishFull', label: $t('standard.country.entity.nameEnglishFull'), type: 'text', colSpan: 2 },
{ name: 'remarkEnglish', label: $t('standard.country.entity.remarkEnglish'), type: 'text', colSpan: 3 },
{ name: 'nameLocalShort', label: $t('standard.country.entity.nameLocalShort'), type: 'text', colSpan: 2 },
{ name: 'isIndependent', label: $t('standard.country.entity.isIndependent'), type: 'select', options: Options.yesNo() },
{
name: 'currency',
label: $t('standard.country.entity.currency'),
type: 'select',
options: currencyOptionsRef,
colSpan: 2,
useInput: true,
hideSelected: true,
fillInput: true,
onFilter: (val, update) => {
if (val) {
update(() => {
const input = val.toUpperCase();
const currencyOptions = [];
for (const currency of currencyList) {
if (
currency.code?.indexOf(input) > -1 ||
currency.codeLatin?.indexOf(input) > -1 ||
currency.nameChinese?.indexOf(input) > -1 ||
currency.nameEnglish?.indexOf(input) > -1
) {
const item = createCurrencyOptionItem(currency);
if (item) {
currencyOptions.push(item);
}
}
}
currencyOptionsRef = currencyOptions;
});
} else {
update(() => {
const currencyOptions = [];
for (const currency of currencyList) {
const item = createCurrencyOptionItem(currency);
if (item) {
currencyOptions.push(item);
}
}
currencyOptionsRef = currencyOptions;
});
}
},
},
{ name: 'languageCode2', label: $t('standard.country.entity.languageCode2'), type: 'text', colsFirst: true },
{ name: 'languageCode3', label: $t('standard.country.entity.languageCode3'), type: 'text' },
],
},
}"
:viewer="{
panel: {
columnNum: 1,
fields: [
{ name: 'code', label: $t('standard.country.entity.code') },
{ name: 'codeLatin2', label: $t('standard.country.entity.codeLatin2') },
{ name: 'codeLatin3', label: $t('standard.country.entity.codeLatin3') },
{ name: 'nameChinese', label: $t('standard.country.entity.nameChinese') },
{ name: 'nameChineseFull', label: $t('standard.country.entity.nameChineseFull') },
{ name: 'nameEnglish', label: $t('standard.country.entity.nameEnglish') },
{ name: 'nameEnglishFull', label: $t('standard.country.entity.nameEnglishFull') },
{ name: 'nameLocalShort', label: $t('standard.country.entity.nameLocalShort') },
{ name: 'remarkChinese', label: $t('standard.country.entity.remarkChinese') },
{ name: 'remarkEnglish', label: $t('standard.country.entity.remarkEnglish') },
{ name: 'languageCode2', label: $t('standard.country.entity.languageCode2') },
{ name: 'languageCode3', label: $t('standard.country.entity.languageCode3') },
{ name: 'isIndependent', label: $t('standard.country.entity.isIndependent') },
{
name: 'currency',
label: $t('standard.country.entity.currency'),
format: (value, row) => {
return getCurrencyLabel(currencyMap[row.currency]);
},
},
{ name: 'dataComeFrom', label: $t('dataComeFrom') },
{ name: 'creator', label: $t('creator') },
{ name: 'createDate', label: $t('createDate') },
{ name: 'lastModifier', label: $t('lastModifier') },
{ name: 'lastModifyDate', label: $t('lastModifyDate'), format: Formater.none() },
],
},
}"
></w-grid>
</template>
<script setup lang="ts">
import { ref, onMounted, computed } from 'vue';
import { useI18n } from 'vue-i18n';
import { axios, Environment, Formater, Options, eventBus } from '@/platform';
let currentLocale = Environment.getConfigure().setting.i18n.locale;
let currencyMap = {};
let currencyList = [];
const { t } = useI18n();
const currencyOptionsRef = ref([]);
const columnsComputed = computed(() => {
const result = [
{ width: 70, name: 'code', label: t('code') },
{ width: 70, name: 'codeLatin3', label: t('code') + '3' },
{
width: 150,
name: 'nameChinese',
label: t('name'),
format: (value, row) => {
if (row) {
return getCountryName(row);
}
},
},
{
name: 'currency',
label: t('standard.entity.currency'),
columns: [
{
width: 70,
name: 'currencyCode',
label: t('code'),
sortable: false,
format: (value, row) => {
if (row.currency) {
return currencyMap[row.currency]?.code;
}
},
},
{
width: 70,
name: 'currencyCodeLatin',
label: t('code') + '2',
sortable: false,
format: (value, row) => {
if (row.currency) {
return currencyMap[row.currency]?.codeLatin;
}
},
},
{
width: 150,
name: 'currencyname',
label: t('name'),
sortable: false,
format: (value, row) => {
if (row.currency) {
return getCurrencyName(currencyMap[row.currency]);
}
},
},
],
},
{
name: 'language',
label: t('standard.entity.language'),
columns: [{ width: 100, name: 'languageCode3', label: t('code') }],
},
{ width: 100, name: 'lastModifier', label: t('lastModifier') },
{ width: 120, name: 'lastModifyDate', label: t('lastModifyDate'), format: Formater.dateOnly() },
];
currentLocale = Environment.getConfigure().setting.i18n.locale;
if (currentLocale.endsWith('CN')) {
result[2].name = 'nameChinese';
} else {
result[2].name = 'nameEnglish';
}
return result;
});
onMounted(() => {
axios.get(Environment.apiContextPath('/api/standard/currency?pageable=false&sortBy=code')).then((response) => {
currencyMap = {};
currencyList = [];
const currencyOptions = [];
if (response?.data?.content) {
for (const currency of response.data.content) {
currencyMap[currency.code] = currency;
currencyList.push(currency);
}
for (const currency of currencyList) {
const item = createCurrencyOptionItem(currency);
if (item) {
currencyOptions.push(item);
}
}
}
currencyOptionsRef.value = currencyOptions;
});
});
const getCurrencyLabel = (currency) => {
return currency.code + ' ' + currency.codeLatin + ' ' + (currentLocale.endsWith('CN') ? currency.nameChinese : currency.nameEnglish);
};
const createCurrencyOptionItem = (currency) => {
if (currency) {
return {
value: currency.code,
label: getCurrencyLabel(currency),
};
}
return null;
};
const getCountryName = (country) => {
if (country) {
return currentLocale.endsWith('CN') ? country.nameChinese : country.nameEnglish;
}
return null;
};
const getCurrencyName = (currency) => {
if (currency) {
return currentLocale.endsWith('CN') ? currency.nameChinese : currency.nameEnglish;
}
return null;
};
</script>

72
io.sc.platform.core.frontend/template-project/src/views/standard/Currency.vue

@ -0,0 +1,72 @@
<template>
<w-grid
ref="gridRef"
:title="$t('standard.currency.grid.title')"
:hide-bottom="false"
:config-button="true"
selection="multiple"
:checkbox-selection="true"
:tree="false"
primary-key="code"
:data-url="Environment.apiContextPath('/api/standard/currency')"
:pageable="false"
:pagination="{
sortBy: 'codeLatin',
descending: false,
}"
:toolbar-configure="{ noIcon: false }"
:toolbar-actions="['query', 'refresh', 'separator', 'add', 'edit', 'remove', 'separator', 'view', 'separator', 'export']"
:query-form-cols-num="4"
:query-form-fields="[
{ name: 'code', label: $t('standard.currency.entity.code'), type: 'text', clearable: true },
{ name: 'codeLatin', label: $t('standard.currency.entity.codeLatin'), type: 'text', clearable: true },
{ name: 'nameChinese', label: $t('standard.country.entity.nameChinese'), type: 'text', clearable: true },
{ name: 'nameEnglish', label: $t('standard.country.entity.nameEnglish'), type: 'text', clearable: true },
]"
:columns="[
{ width: 80, name: 'code', label: $t('standard.currency.entity.code') },
{ width: 80, name: 'codeLatin', label: $t('standard.currency.entity.codeLatin') },
{ width: 150, name: 'nameChinese', label: $t('standard.currency.entity.nameChinese') },
{ width: 150, name: 'nameEnglish', label: $t('standard.currency.entity.nameEnglish') },
{ width: 100, name: 'precision', label: $t('standard.currency.entity.precision') },
{ width: 100, name: 'lastModifier', label: $t('lastModifier') },
{ width: 120, name: 'lastModifyDate', label: $t('lastModifyDate'), format: Formater.dateOnly() },
]"
:editor="{
dialog: {
width: '600px',
},
form: {
colsNum: 1,
fields: [
{ name: 'code', label: $t('standard.currency.entity.code'), type: 'text', required: true },
{ name: 'codeLatin', label: $t('standard.currency.entity.codeLatin'), type: 'text', required: true },
{ name: 'nameChinese', label: $t('standard.country.entity.nameChinese'), type: 'text', required: true },
{ name: 'nameEnglish', label: $t('standard.country.entity.nameEnglish'), type: 'text' },
{ name: 'precision', label: $t('standard.currency.entity.precision'), type: 'text', required: true, defaultValue: 2 },
],
},
}"
:viewer="{
panel: {
columnNum: 1,
fields: [
{ name: 'code', label: $t('standard.currency.entity.code') },
{ name: 'codeLatin', label: $t('standard.currency.entity.codeLatin') },
{ name: 'nameChinese', label: $t('standard.country.entity.nameChinese') },
{ name: 'nameEnglish', label: $t('standard.country.entity.nameEnglish') },
{ name: 'precision', label: $t('standard.currency.entity.precision') },
{ name: 'dataComeFrom', label: $t('dataComeFrom') },
{ name: 'creator', label: $t('creator') },
{ name: 'createDate', label: $t('createDate') },
{ name: 'lastModifier', label: $t('lastModifier') },
{ name: 'lastModifyDate', label: $t('lastModifyDate'), format: Formater.none() },
],
},
}"
></w-grid>
</template>
<script setup lang="ts">
import { Environment, Formater, Options } from '@/platform';
</script>

109
io.sc.platform.core.frontend/template-project/src/views/standard/Industry.vue

@ -0,0 +1,109 @@
<template>
<w-grid
ref="treeGridRef"
:title="$t('standard.industry.grid.title')"
hide-bottom
:config-button="true"
selection="multiple"
:checkbox-selection="true"
dense
:tree="true"
:tree-icon="
(row) => {
return { name: 'folder', color: 'amber' };
}
"
primary-key="code"
:data-url="Environment.apiContextPath('/api/standard/industry')"
:pageable="false"
:pagination="{
sortBy: 'code',
descending: false,
}"
:toolbar-configure="{ noIcon: false }"
:toolbar-actions="[
'refresh',
'separator',
[
{
extend: 'add',
click: undefined,
},
{
extend: 'addTop',
label: $t('standard.industry.grid.toolbar.addTop'),
},
{
extend: 'addChild',
label: $t('standard.industry.grid.toolbar.addChild'),
},
],
'edit',
'remove',
'separator',
'view',
'separator',
'export',
]"
:columns="[
{
name: 'target',
label: $t('standard.entity.target'),
columns: [
{ width: 300, name: 'name', label: $t('name') },
{ width: 100, name: 'code', label: $t('code') },
{ width: 100, name: 'description', label: $t('description') },
],
},
{
name: 'source',
label: $t('standard.entity.source'),
columns: [
{ width: 300, name: 'mappingName', label: $t('name') },
{ width: 100, name: 'mappingCode', label: $t('code') },
{ width: 100, name: 'mappingDescription', label: $t('description') },
],
},
{ width: 100, name: 'lastModifier', label: $t('lastModifier') },
{ width: 120, name: 'lastModifyDate', label: $t('lastModifyDate'), format: Formater.dateOnly() },
]"
:editor="{
dialog: {
width: '600px',
},
form: {
colsNum: 1,
fields: [
{ name: 'code', label: $t('standard.entity.code'), type: 'text', required: true },
{ name: 'name', label: $t('standard.entity.name'), type: 'text', required: true },
{ name: 'description', label: $t('standard.entity.description'), type: 'textarea', rows: 3 },
{ name: 'mappingCode', label: $t('standard.entity.mappingCode'), type: 'text', required: true },
{ name: 'mappingName', label: $t('standard.entity.mappingName'), type: 'text' },
{ name: 'mappingDescription', label: $t('standard.entity.mappingDescription'), type: 'textarea', rows: 3 },
],
},
}"
:viewer="{
panel: {
columnNum: 1,
fields: [
{ name: 'code', label: $t('standard.entity.code') },
{ name: 'name', label: $t('standard.entity.name') },
{ name: 'description', label: $t('standard.entity.description') },
{ name: 'mappingCode', label: $t('standard.entity.mappingCode') },
{ name: 'mappingName', label: $t('standard.entity.mappingName') },
{ name: 'mappingDescription', label: $t('standard.entity.mappingDescription') },
{ name: 'dataComeFrom', label: $t('dataComeFrom') },
{ name: 'creator', label: $t('creator') },
{ name: 'createDate', label: $t('createDate') },
{ name: 'lastModifier', label: $t('lastModifier') },
{ name: 'lastModifyDate', label: $t('lastModifyDate'), format: Formater.none() },
],
},
}"
></w-grid>
</template>
<script setup lang="ts">
import { Environment, Formater } from '@/platform';
</script>

79
io.sc.platform.core.frontend/template-project/src/views/standard/PerformanceStatus.vue

@ -0,0 +1,79 @@
<template>
<w-grid
ref="gridRef"
:title="$t('standard.performanceStatus.grid.title')"
:config-button="true"
selection="multiple"
:checkbox-selection="true"
:tree="false"
primary-key="code"
:data-url="Environment.apiContextPath('/api/standard/performanceStatus')"
:pageable="true"
:pagination="{
sortBy: 'code',
descending: false,
}"
:toolbar-configure="{ noIcon: false }"
:toolbar-actions="['refresh', 'separator', 'add', 'edit', 'remove', 'separator', 'view', 'separator', 'export']"
:columns="[
{
name: 'target',
label: $t('standard.entity.target'),
columns: [
{ width: 100, name: 'code', label: $t('code') },
{ width: 100, name: 'name', label: $t('name') },
{ width: 100, name: 'description', label: $t('description') },
],
},
{
name: 'source',
label: $t('standard.entity.source'),
columns: [
{ width: 100, name: 'mappingCode', label: $t('code') },
{ width: 100, name: 'mappingName', label: $t('name') },
{ width: 100, name: 'mappingDescription', label: $t('description') },
],
},
{ width: 100, name: 'lastModifier', label: $t('lastModifier') },
{ width: 120, name: 'lastModifyDate', label: $t('lastModifyDate'), format: Formater.dateOnly() },
]"
:editor="{
dialog: {
width: '600px',
},
form: {
colsNum: 1,
fields: [
{ name: 'code', label: $t('standard.entity.code'), type: 'text', required: true },
{ name: 'name', label: $t('standard.entity.name'), type: 'text', required: true },
{ name: 'description', label: $t('standard.entity.description'), type: 'textarea', rows: 3 },
{ name: 'mappingCode', label: $t('standard.entity.mappingCode'), type: 'text', required: true },
{ name: 'mappingName', label: $t('standard.entity.mappingName'), type: 'text' },
{ name: 'mappingDescription', label: $t('standard.entity.mappingDescription'), type: 'textarea', rows: 3 },
],
},
}"
:viewer="{
panel: {
columnNum: 1,
fields: [
{ name: 'code', label: $t('standard.entity.code') },
{ name: 'name', label: $t('standard.entity.name') },
{ name: 'description', label: $t('standard.entity.description') },
{ name: 'mappingCode', label: $t('standard.entity.mappingCode') },
{ name: 'mappingName', label: $t('standard.entity.mappingName') },
{ name: 'mappingDescription', label: $t('standard.entity.mappingDescription') },
{ name: 'dataComeFrom', label: $t('dataComeFrom') },
{ name: 'creator', label: $t('creator') },
{ name: 'createDate', label: $t('createDate') },
{ name: 'lastModifier', label: $t('lastModifier') },
{ name: 'lastModifyDate', label: $t('lastModifyDate'), format: Formater.none() },
],
},
}"
></w-grid>
</template>
<script setup lang="ts">
import { Environment, Formater } from '@/platform';
</script>

2
io.sc.platform.core/src/main/java/io/sc/platform/core/bean/GlobalExceptionHandler.java

@ -26,7 +26,7 @@ public class GlobalExceptionHandler {
@ExceptionHandler(value =Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ResponseBody
public ResponseWrapper exceptionHandler(HttpServletRequest request, Exception e){
public ResponseWrapper exceptionHandler(Exception e){
log.error("",e);
if(e instanceof ValidateException){
ValidateException exception =(ValidateException)e;

7
io.sc.platform.core/src/main/java/io/sc/platform/core/bean/RestControllerReturnValueAdviceBean.java

@ -14,13 +14,11 @@ import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
/**
@ -79,9 +77,4 @@ public class RestControllerReturnValueAdviceBean implements ResponseBodyAdvice<O
return ResponseWrapperBuilder.success(body);
}
}
@ExceptionHandler(Exception.class)
public ResponseWrapper handleException(HttpServletRequest request, Exception e){
return ResponseWrapperBuilder.error(e);
}
}

3
io.sc.platform.core/src/main/resources/io/sc/platform/core/i18n/words.properties

@ -211,4 +211,5 @@ executeAll=Execute All
moreQueryCondition=More Query Condition
valueType=Value Type
source=Source
target=Target
target=Target
application=Application

3
io.sc.platform.core/src/main/resources/io/sc/platform/core/i18n/words_tw_CN.properties

@ -211,4 +211,5 @@ executeAll=\u57F7\u884C\u6240\u6709
moreQueryCondition=\u66F4\u591A\u67E5\u8A62\u689D\u4EF6
valueType=\u503C\u985E\u578B
source=\u6E90
target=\u76EE\u6A19
target=\u76EE\u6A19
application=\u61C9\u7528

3
io.sc.platform.core/src/main/resources/io/sc/platform/core/i18n/words_zh_CN.properties

@ -211,4 +211,5 @@ executeAll=\u6267\u884C\u6240\u6709
moreQueryCondition=\u66F4\u591A\u67E5\u8BE2\u6761\u4EF6
valueType=\u503C\u7C7B\u578B
source=\u6E90
target=\u76EE\u6807
target=\u76EE\u6807
application=\u5E94\u7528

6
io.sc.platform.developer.doc/asciidoc/frontend/api/api.adoc

@ -1,5 +1,5 @@
= API
== Tools
== Tools
工具类, 使用方法:
[source,javaScript]
@ -79,8 +79,8 @@ Tools.xxx(y1,y2);
import { eventBus } from 'platform-core';
// 监听全局事件
eventBus.on('onLocaleChanged',(locale:string)=>{});
eventBus.on('onWindowResize',()=>{});
eventBus.on('onLocaleChanged',(locale:string)=>{}); // 切换语言
eventBus.on('onWindowResize',()=>{}); // 窗口尺寸变化
// 发送全局事件
eventBus.emit('eventName');

4
io.sc.platform.developer.frontend/package.json

@ -1,6 +1,6 @@
{
"name": "io.sc.platform.developer.frontend",
"version": "8.1.38",
"version": "8.1.40",
"description": "",
"private": false,
"keywords": [],
@ -92,7 +92,7 @@
"luckyexcel": "1.0.1",
"mockjs": "1.1.0",
"pinia": "2.1.7",
"platform-core": "8.1.190",
"platform-core": "8.1.193",
"quasar": "2.15.3",
"tailwindcss": "3.4.3",
"vue": "3.4.24",

2
io.sc.platform.gradle/templates/pgp/setup/build.gradle.txt

@ -654,7 +654,7 @@ subprojects {
// 设置方式: 通过命令行 -D 传入目标环境参数
// gradle bootwar -Dfrontend=dev # 采用 pnpm build 构建前端
// gradle bootwar -Dfrontend=prod # 采用 pnpm prod 构建前端
// gradle bootwar -Dfrontend # 不构建前端, 仅生成后端需要的文件
// gradle bootwar -Dfrontend=none # 不构建前端, 仅生成后端需要的文件
def isFrontend =System.getProperty("frontend")?:"prod";
if(isFrontend=="dev"){
frontendGenerateThymeleafTemplate.dependsOn(frontendNpmBuild);

4
io.sc.platform.gradle/templates/pgp/setup/gradle.properties

@ -36,9 +36,9 @@ application_version=1.0.0
# platform
###########################################################
platform_group=io.sc
platform_version=8.1.38
platform_version=8.1.40
platform_plugin_version=8.1.13
platform_core_frontend_version=8.1.188
platform_core_frontend_version=8.1.193
###########################################################
# dependencies version

2
io.sc.platform.jdbc/src/main/java/io/sc/platform/jdbc/service/impl/SqlExecutorServiceImpl.java

@ -20,7 +20,7 @@ import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
@Service("frSqlExecutorService")
@Service
public class SqlExecutorServiceImpl implements SqlExecutorService{
private static final Logger log = LoggerFactory.getLogger(SqlExecutorServiceImpl.class);

4
io.sc.platform.lcdp.frontend/package.json

@ -1,6 +1,6 @@
{
"name": "io.sc.platform.lcdp.frontend",
"version": "8.1.38",
"version": "8.1.40",
"description": "",
"private": false,
"keywords": [],
@ -92,7 +92,7 @@
"luckyexcel": "1.0.1",
"mockjs": "1.1.0",
"pinia": "2.1.7",
"platform-core": "8.1.190",
"platform-core": "8.1.193",
"quasar": "2.15.3",
"tailwindcss": "3.4.3",
"vue": "3.4.24",

4
io.sc.platform.mvc.frontend/package.json

@ -1,6 +1,6 @@
{
"name": "io.sc.platform.mvc.frontend",
"version": "8.1.38",
"version": "8.1.40",
"description": "",
"private": false,
"keywords": [],
@ -92,7 +92,7 @@
"luckyexcel": "1.0.1",
"mockjs": "1.1.0",
"pinia": "2.1.7",
"platform-core": "8.1.190",
"platform-core": "8.1.193",
"quasar": "2.15.3",
"tailwindcss": "3.4.3",
"vue": "3.4.24",

2
io.sc.platform.mvc/src/main/java/io/sc/platform/mvc/controller/DownloaderController.java

@ -18,7 +18,7 @@ import java.io.FileInputStream;
@RequestMapping("/api/mvc")
public class DownloaderController {
@RequestMapping(value="download",method=RequestMethod.GET)
public void downloadLogFile(@RequestParam("filePath") String filePath,@RequestParam(value = "exportName",required = false) String exportName,HttpServletRequest request,HttpServletResponse response) throws Exception{
public void download(@RequestParam("filePath") String filePath,@RequestParam(value = "exportName",required = false) String exportName,HttpServletRequest request,HttpServletResponse response) throws Exception{
File file =new File(filePath);
if(file.isFile() && file.exists()) {
if(StringUtils.hasText(exportName)) {

2
io.sc.platform.mvc/src/main/java/io/sc/platform/mvc/service/impl/FrontEndModuleServiceImpl.java

@ -13,7 +13,7 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@Service("frFrontEndComponentService")
@Service
public class FrontEndModuleServiceImpl implements FrontEndModuleService {
/**
* 本地前端模块 Map

6
io.sc.platform.mvc/src/main/resources/META-INF/platform/plugins/application-properties.json

@ -87,10 +87,10 @@
"description": "servlet multipart configuration",
"properties": [
"spring.servlet.multipart.enabled = true",
"spring.servlet.multipart.file-size-threshold = 100MB",
"spring.servlet.multipart.file-size-threshold = 1GB",
"spring.servlet.multipart.location = ${application.home.dir}/work/web/upload",
"spring.servlet.multipart.max-file-size = 100MB",
"spring.servlet.multipart.max-request-size = 200MB",
"spring.servlet.multipart.max-file-size = 1GB",
"spring.servlet.multipart.max-request-size = 1GB",
"spring.servlet.multipart.resolve-lazily = false"
]
}

4
io.sc.platform.security.frontend/package.json

@ -1,6 +1,6 @@
{
"name": "io.sc.platform.security.frontend",
"version": "8.1.38",
"version": "8.1.40",
"description": "",
"private": false,
"keywords": [
@ -93,7 +93,7 @@
"luckyexcel": "1.0.1",
"mockjs": "1.1.0",
"pinia": "2.1.7",
"platform-core": "8.1.190",
"platform-core": "8.1.193",
"quasar": "2.14.5",
"tailwindcss": "3.4.0",
"vue": "3.4.3",

27
io.sc.platform.security/src/main/resources/liquibase/io.sc.platform.security_8.0.0_20220606__Security Database Schema DDL.xml

@ -43,33 +43,6 @@
referencedTableName="SYS_CORPORATION"
referencedColumnNames="ID_" onDelete="CASCADE"/>
<!-- 应用表 -->
<createTable tableName="SYS_APPLICATION" remarks="应用表">
<column name="ID_" type="NVARCHAR(36)" remarks="ID">
<constraints primaryKey="true"/>
</column>
<column name="CODE_" type="NVARCHAR(255)" remarks="应用代码"/>
<column name="NAME_" type="NVARCHAR(255)" remarks="应用名称"></column>
<column name="DESCRIPTION_" type="NVARCHAR(255)" remarks="描述"></column>
<column name="ENABLE_" type="SMALLINT" remarks="是否可用(0:不可用,1:可用)"></column>
<column name="JPA_VERSION_" type="INTEGER" remarks="JPA乐观锁版本"/>
<column name="DATA_COME_FROM_" type="NVARCHAR(10)" remarks="数据来源(INPUT:手工录入,IMPORT:系统自动导入)"/>
<column name="CREATOR_" type="NVARCHAR(255)" remarks="创建人"/>
<column name="CREATE_DATE_" type="DATETIME" remarks="创建日期"/>
<column name="LAST_MODIFIER_" type="NVARCHAR(255)" remarks="最后修改人"/>
<column name="LAST_MODIFYDATE_" type="DATETIME" remarks="最后修改日期"/>
<column name="CORP_CODE_" type="NVARCHAR(255)" remarks="所属法人代码"/>
</createTable>
<addUniqueConstraint tableName="SYS_APPLICATION" columnNames="CODE_,CORP_CODE_"></addUniqueConstraint>
<addNotNullConstraint columnName="CODE_" columnDataType="NVARCHAR(255)" tableName="SYS_APPLICATION" constraintName="CONST_SYS_APPLICATION_CODE"/>
<addNotNullConstraint columnName="NAME_" columnDataType="NVARCHAR(255)" tableName="SYS_APPLICATION" constraintName="CONST_SYS_APPLICATION_VALUE"/>
<addNotNullConstraint columnName="ENABLE_" columnDataType="SMALLINT" tableName="SYS_APPLICATION" constraintName="CONST_SYS_APPLICATION_ENABLE"/>
<addNotNullConstraint columnName="DATA_COME_FROM_" columnDataType="NVARCHAR(10)" tableName="SYS_APPLICATION" constraintName="CONST_SYS_APPLICATION_DCF"/>
<addNotNullConstraint columnName="CORP_CODE_" columnDataType="NVARCHAR(255)" tableName="SYS_APPLICATION" constraintName="CONST_SYS_APPLICATION_CORP"/>
<addDefaultValue columnName="ENABLE_" columnDataType="SMALLINT" tableName="SYS_APPLICATION" defaultValueNumeric="1"/>
<addDefaultValue columnName="DATA_COME_FROM_" columnDataType="NVARCHAR(10)" tableName="SYS_APPLICATION" defaultValue="INPUT"/>
<addDefaultValue columnName="CORP_CODE_" columnDataType="NVARCHAR(255)" tableName="SYS_APPLICATION" defaultValue="_PRIMARY_"/>
<!-- 用户表 -->
<createTable tableName="SYS_USER" remarks="用户表">
<column name="ID_" type="NVARCHAR(36)" remarks="ID">

69
io.sc.platform.system.api/src/main/java/io/sc/platform/system/api/application/ApplicationVo.java

@ -0,0 +1,69 @@
package io.sc.platform.system.api.application;
import io.sc.platform.orm.api.vo.CorporationAuditorVo;
public class ApplicationVo extends CorporationAuditorVo {
private String id;
private String code;
private String name;
private String description;
private Boolean enable;
private Integer order;
private boolean selected;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Boolean getEnable() {
return enable;
}
public void setEnable(Boolean enable) {
this.enable = enable;
}
public Integer getOrder() {
return order;
}
public void setOrder(Integer order) {
this.order = order;
}
public boolean getSelected() {
return selected;
}
public void setSelected(boolean selected) {
this.selected = selected;
}
}

4
io.sc.platform.system.frontend/package.json

@ -1,6 +1,6 @@
{
"name": "io.sc.platform.system.frontend",
"version": "8.1.38",
"version": "8.1.40",
"description": "",
"private": false,
"keywords": [],
@ -92,7 +92,7 @@
"luckyexcel": "1.0.1",
"mockjs": "1.1.0",
"pinia": "2.1.7",
"platform-core": "8.1.190",
"platform-core": "8.1.193",
"quasar": "2.15.3",
"tailwindcss": "3.4.3",
"vue": "3.4.24",

2
io.sc.platform.system.frontend/src/components/index.ts

@ -3,6 +3,7 @@
*/
import component_system_License from '@/views/license/License.vue';
import component_system_application from '@/views/application/Application.vue';
import component_system_Corporation from '@/views/corporation/Corporation.vue';
import component_system_User from '@/views/user/User.vue';
import component_system_Role from '@/views/role/Role.vue';
@ -21,6 +22,7 @@ import component_system_monitor_Resources from '@/views/monitor/Resources.vue';
const localComponents = {
'component.system.License': component_system_License,
'component.system.application': component_system_application,
'component.system.Corporation': component_system_Corporation,
'component.system.User': component_system_User,
'component.system.Role': component_system_Role,

15
io.sc.platform.system.frontend/src/i18n/messages.json

@ -1,6 +1,7 @@
{
"menu.system" : "System Manager",
"menu.system.license" : "License",
"menu.system.application" : "Application",
"menu.system.corporation" : "Corporation",
"menu.system.user" : "User",
"menu.system.role" : "Role",
@ -43,11 +44,25 @@
"system.shared.selectOrg.grid.title": "Organization Tree",
"system.shared.selectOrg.grid.toolbar.save.tip":"Are you sure to update organizations?",
"system.shared.selectApplication.grid.title": "Included Application List",
"system.shared.selectApplication.grid.toolbar.selectIn":"Select In",
"system.shared.selectApplication.grid.toolbar.selectOut":"Select Out",
"system.shared.selectApplication.grid.toolbar.selectOut.tip":"Are you sure to select out the applications?",
"system.shared.selectApplication.grid.toolbar.selectAllIn":"Select All In",
"system.shared.selectApplication.grid.toolbar.selectAllIn.tip":"Are you sure to select in ALL applications?",
"system.shared.selectApplication.grid.toolbar.selectAllOut":"Select All Out",
"system.shared.selectApplication.grid.toolbar.selectAllOut.tip":"Are you sure to select out ALL applications?",
"system.shared.selectApplication.dialog.title": "Select Applications",
"system.shared.selectApplication.dialog.grid.title": "Available Application List",
"system.shared.selectMenu.grid.title": "Menu Tree",
"system.shared.selectMenu.grid.toolbar.save.tip":"Are you sure to update menus?",
"system.shared.importMenuPlugin.dialog.title": "Import Menu Plugins",
"system.application.grid.title":"Application List",
"system.corporation.grid.title":"Corporation Tree",
"system.corporation.grid.toolbar.addTop":"Add Top Corporation",
"system.corporation.grid.toolbar.addChild":"Add Child Corporation",

14
io.sc.platform.system.frontend/src/i18n/messages_tw_CN.json

@ -1,6 +1,7 @@
{
"menu.system" : "系統管理",
"menu.system.license" : "許可證管理",
"menu.system.application" : "應用管理",
"menu.system.corporation" : "法人管理",
"menu.system.user" : "用戶管理",
"menu.system.role" : "角色管理",
@ -43,11 +44,24 @@
"system.shared.selectOrg.grid.title": "機構樹",
"system.shared.selectOrg.grid.toolbar.save.tip":"您确定要更新機構吗?",
"system.shared.selectApplication.grid.title": "已包含應用列表",
"system.shared.selectApplication.grid.toolbar.selectIn":"選入",
"system.shared.selectApplication.grid.toolbar.selectOut":"選出",
"system.shared.selectApplication.grid.toolbar.selectOut.tip":"您確定要選出應用嗎?",
"system.shared.selectApplication.grid.toolbar.selectAllIn":"選入所有應用",
"system.shared.selectApplication.grid.toolbar.selectAllIn.tip":"您確定要選入全部應用嗎?",
"system.shared.selectApplication.grid.toolbar.selectAllOut":"選出所有應用",
"system.shared.selectApplication.grid.toolbar.selectAllOut.tip":"您確定要選出全部應用嗎?",
"system.shared.selectApplication.dialog.title": "選擇應用",
"system.shared.selectApplication.dialog.grid.title": "可選應用列表",
"system.shared.selectMenu.grid.title": "菜單树",
"system.shared.selectMenu.grid.toolbar.save.tip":"您确定要更新菜單吗?",
"system.shared.importMenuPlugin.dialog.title": "導入菜單插件",
"system.application.grid.title":"應用列表",
"system.corporation.grid.title":"法人樹",
"system.corporation.grid.toolbar.addTop":"新增頂級法人",
"system.corporation.grid.toolbar.addChild":"新增子法人",

14
io.sc.platform.system.frontend/src/i18n/messages_zh_CN.json

@ -1,6 +1,7 @@
{
"menu.system" : "系统管理",
"menu.system.license" : "许可证管理",
"menu.system.application" : "应用管理",
"menu.system.corporation" : "法人管理",
"menu.system.user" : "用户管理",
"menu.system.role" : "角色管理",
@ -43,11 +44,24 @@
"system.shared.selectOrg.grid.title": "机构树",
"system.shared.selectOrg.grid.toolbar.save.tip":"您确定要更新机构吗?",
"system.shared.selectApplication.grid.title": "已包含应用列表",
"system.shared.selectApplication.grid.toolbar.selectIn":"选入",
"system.shared.selectApplication.grid.toolbar.selectOut":"选出",
"system.shared.selectApplication.grid.toolbar.selectOut.tip":"您确定要选出应用吗?",
"system.shared.selectApplication.grid.toolbar.selectAllIn":"选入所有应用",
"system.shared.selectApplication.grid.toolbar.selectAllIn.tip":"您确定要选入全部应用吗?",
"system.shared.selectApplication.grid.toolbar.selectAllOut":"选出所有应用",
"system.shared.selectApplication.grid.toolbar.selectAllOut.tip":"您确定要选出全部应用吗?",
"system.shared.selectApplication.dialog.title": "选择应用",
"system.shared.selectApplication.dialog.grid.title": "可选应用列表",
"system.shared.selectMenu.grid.title": "菜单树",
"system.shared.selectMenu.grid.toolbar.save.tip":"您确定要更新菜单吗?",
"system.shared.importMenuPlugin.dialog.title": "导入菜单插件",
"system.application.grid.title":"应用列表",
"system.corporation.grid.title":"法人树",
"system.corporation.grid.toolbar.addTop":"新增顶级法人",
"system.corporation.grid.toolbar.addChild":"新增子法人",

6
io.sc.platform.system.frontend/src/menus/menus.json

@ -17,9 +17,11 @@
*/
[
{"type":"GROUP", "order":8000, "id":"menu.system", "titleI18nKey":"menu.system", "icon":"bi-gear"},
{"type":"GROUP", "order":8000, "id":"menu.system", "titleI18nKey":"menu.system", "icon":"bi-gear"},
/*/*/
{"type":"ROUTE", "order":100,"parentId":"menu.system", "id":"menu.system.license", "titleI18nKey":"menu.system.license", "icon":"bi-c-circle", "routeName":"route.system.license"},
{"type":"ROUTE", "order":100,"parentId":"menu.system", "id":"menu.system.license", "titleI18nKey":"menu.system.license", "icon":"bi-c-circle", "routeName":"route.system.license"},
/*/*/
{"type":"ROUTE", "order":150,"parentId":"menu.system", "id":"menu.system.application", "titleI18nKey":"menu.system.application", "icon":"bi-app-indicator", "routeName":"route.system.application"},
/*/*/
{"type":"SEPARATOR", "order":200, "parentId":"menu.system"},
/*/*/

15
io.sc.platform.system.frontend/src/routes/routes.json

@ -14,6 +14,21 @@
]
}
},
{
"parent": "/",
"name": "route.system.application",
"path": "system/application",
"priority": 0,
"module": "io.sc.platform.system.frontend",
"component": "component.system.application",
"componentPath":"@/views/application/Application.vue",
"redirect": null,
"meta": {
"permissions": [
"/system/application/**/*"
]
}
},
{
"parent": "/",
"name": "route.system.corporation",

127
io.sc.platform.system.frontend/src/views/application/Application.vue

@ -0,0 +1,127 @@
<template>
<q-splitter :model-value="60" class="w-full" style="height: 100%">
<template #before>
<div class="pr-1" style="height: 100%">
<w-grid
ref="applicationGridRef"
:title="$t('system.application.grid.title')"
:config-button="true"
selection="multiple"
:checkbox-selection="true"
:data-url="Environment.apiContextPath('/api/system/application')"
:sort-by="['order']"
:query-form-cols-num="3"
:query-form-fields="[
{ name: 'code', label: $t('code'), type: 'text' },
{ name: 'name', label: $t('name'), type: 'text' },
{ name: 'enable', label: $t('isEnable'), type: 'select', options: Options.yesNo() },
]"
:toolbar-configure="{ noIcon: false }"
:toolbar-actions="['query', 'refresh', 'separator', 'add', 'clone', 'edit', 'remove', 'separator', 'view', 'separator', 'export']"
:columns="[
{ width: 60, name: 'order', label: $t('order') },
{ width: 100, name: 'code', label: $t('code') },
{ width: '100%', name: 'name', label: $t('name') },
{ width: 80, name: 'enable', label: $t('status'), format: Formater.enableTag() },
{ width: 120, name: 'lastModifier', label: $t('lastModifier') },
{ width: 120, name: 'lastModifyDate', label: $t('lastModifyDate'), format: Formater.dateOnly() },
]"
:editor="{
dialog: {
width: '600px',
height: '300px',
},
form: {
colsNum: 1,
fields: [
{ name: 'code', label: $t('code'), type: 'text', required: true },
{ name: 'name', label: $t('name'), type: 'text', required: true },
{ name: 'description', label: $t('description'), type: 'textarea', rows: 1 },
{ name: 'order', label: $t('order'), type: 'number' },
{ name: 'enable', label: $t('enable'), type: 'checkbox', defaultValue: true },
],
},
}"
:viewer="{
panel: {
columnNum: 1,
fields: [
{ name: 'id', label: $t('id') },
{ name: 'code', label: $t('code') },
{ name: 'name', label: $t('name') },
{ name: 'description', label: $t('description') },
{ name: 'enable', label: $t('enable'), format: Formater.none() },
{ name: 'order', label: $t('order') },
{ name: 'dataComeFrom', label: $t('dataComeFrom') },
{ name: 'creator', label: $t('creator') },
{ name: 'createDate', label: $t('createDate') },
{ name: 'lastModifier', label: $t('lastModifier') },
{ name: 'lastModifyDate', label: $t('lastModifyDate'), format: Formater.none() },
{ name: 'corporationCode', label: $t('corporationCode') },
],
},
}"
@row-click="
(evt, row, index) => {
currentSelectedRoleId = row.id;
menuTreeGridRef?.refresh();
userGridRef?.refresh();
}
"
@before-request-data="
() => {
currentSelectedRoleId = '';
menuTreeGridRef?.refresh();
userGridRef?.refresh();
}
"
>
</w-grid>
</div>
</template>
<template #after>
<div class="pl-1" style="height: 100%">
<q-tabs v-model="selectedTabRef" inline-label align="left" :breakpoint="0" no-caps>
<q-tab name="menu" icon="bi-menu-app" :label="$t('menu')" />
</q-tabs>
<q-tab-panels v-model="selectedTabRef" animated swipeable keep-alive style="height: calc(100% - 48px)">
<q-tab-panel name="menu" class="px-0 pb-0" style="height: 100%; padding-left: 0px; padding-right: 0px; padding-bottom: 0px">
<SelectMenuTreeGrid
ref="menuTreeGridRef"
:fetch-data-url="Environment.apiContextPath('/api/system/menu/listAllMenusWithSelectedStatusByRole')"
foreign-key="roleId"
:foreign-value="currentSelectedRoleId"
@update="update"
></SelectMenuTreeGrid>
</q-tab-panel>
</q-tab-panels>
</div>
</template>
</q-splitter>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { Environment, axios, EnumTools, Options, Formater } from 'platform-core';
import SelectMenuTreeGrid from '../shared/SelectMenuTreeGrid.vue';
const DataComeFromEnum = await EnumTools.fetch('io.sc.platform.orm.api.enums.DataComeFrom');
const applicationGridRef = ref();
const userGridRef = ref();
const menuTreeGridRef = ref();
const selectedTabRef = ref('menu');
const currentSelectedRoleId = ref('');
const update = (ids, gridComponent) => {
axios
.post(Environment.apiContextPath('/api/system/application/updateMenus'), {
one: applicationGridRef.value.getSelectedRows()[0].id,
many: ids,
})
.then(() => {
gridComponent.refresh();
});
};
</script>

82
io.sc.platform.system.frontend/src/views/menu/Menu.vue

@ -57,7 +57,7 @@
{
width: '100%',
name: 'titleI18nKey',
label: $t('system.menu.grid.entity.titleI18nKey'),
label: $t('name'),
sortable: false,
format: (value, row) => {
if (row.type === 'SEPARATOR') {
@ -187,9 +187,10 @@
</template>
<template #after>
<div class="pl-1" style="height: 100%">
<q-tabs v-model="selectedTabRef" inline-label align="left" :breakpoint="0">
<q-tabs v-model="selectedTabRef" no-caps inline-label align="left" :breakpoint="0">
<q-tab name="role" icon="bi-diagram-3" :label="$t('role')" />
<q-tab name="org" icon="bi-people" :label="$t('org')" />
<q-tab name="application" icon="bi-app-indicator" :label="$t('application')" />
</q-tabs>
<q-tab-panels v-model="selectedTabRef" animated swipeable keep-alive style="height: calc(100% - 48px)">
@ -200,10 +201,10 @@
:fetch-other-data-url="Environment.apiContextPath('/api/system/role/queryOtherRolesByMenu')"
foreign-key="menuId"
:foreign-value="currentSelectedMenuId"
@select-in="selectIn"
@select-out="selectOut"
@select-all-in="selectAllIn"
@select-all-out="selectAllOut"
@select-in="selectRoleIn"
@select-out="selectRoleOut"
@select-all-in="selectRoleAllIn"
@select-all-out="selectRoleAllOut"
></SelectRoleGrid>
</q-tab-panel>
@ -216,6 +217,20 @@
@update="update"
></SelectOrgTreeGrid>
</q-tab-panel>
<q-tab-panel name="application" class="px-0 pb-0" style="height: 100%; padding-left: 0px; padding-right: 0px; padding-bottom: 0px">
<SelectApplicationGrid
ref="applicationGridRef"
:fetch-data-url="Environment.apiContextPath('/api/system/application/queryApplicationsByMenu')"
:fetch-other-data-url="Environment.apiContextPath('/api/system/application/queryOtherApplicationsByMenu')"
foreign-key="menuId"
:foreign-value="currentSelectedMenuId"
@select-in="selectApplicationIn"
@select-out="selectApplicationOut"
@select-all-in="selectApplicationAllIn"
@select-all-out="selectApplicationAllOut"
></SelectApplicationGrid>
</q-tab-panel>
</q-tab-panels>
</div>
</template>
@ -227,6 +242,7 @@ import { ref } from 'vue';
import { Environment, axios, Tools, EnumTools, Formater, Options } from 'platform-core';
import SelectRoleGrid from '../shared/SelectRoleGrid.vue';
import SelectOrgTreeGrid from '../shared/SelectOrgTreeGrid.vue';
import SelectApplicationGrid from '../shared/SelectApplicationGrid.vue';
import ImportMenuPluginDialog from './ImportMenuPluginDialog.vue';
const MenuTypeEnum = await EnumTools.fetch('io.sc.platform.system.enums.MenuType');
@ -235,12 +251,13 @@ const UrlOpenTypeEnum = await EnumTools.fetch('io.sc.platform.system.enums.UrlOp
const menuTreeGridRef = ref();
const roleGridRef = ref();
const orgTreeGridRef = ref();
const applicationGridRef = ref();
const importMenuPluginDialogRef = ref();
const selectedTabRef = ref('role');
const currentSelectedMenuId = ref('');
const selectIn = (ids: string[], gridComponent, dialogComponent) => {
const selectRoleIn = (ids: string[], gridComponent, dialogComponent) => {
axios
.post(Environment.apiContextPath('/api/system/menu/addRoles'), {
one: menuTreeGridRef.value.getSelectedRows()[0].id,
@ -252,7 +269,7 @@ const selectIn = (ids: string[], gridComponent, dialogComponent) => {
});
};
const selectOut = (ids, gridComponent) => {
const selectRoleOut = (ids, gridComponent) => {
axios
.post(Environment.apiContextPath('/api/system/menu/removeRoles'), {
one: menuTreeGridRef.value.getSelectedRows()[0].id,
@ -263,7 +280,7 @@ const selectOut = (ids, gridComponent) => {
});
};
const selectAllIn = (gridComponent) => {
const selectRoleAllIn = (gridComponent) => {
axios
.post(Environment.apiContextPath('/api/system/menu/addAllRoles'), {
one: menuTreeGridRef.value.getSelectedRows()[0].id,
@ -274,7 +291,7 @@ const selectAllIn = (gridComponent) => {
});
};
const selectAllOut = (gridComponent) => {
const selectRoleAllOut = (gridComponent) => {
axios
.post(Environment.apiContextPath('/api/system/menu/removeAllRoles'), {
one: menuTreeGridRef.value.getSelectedRows()[0].id,
@ -285,6 +302,51 @@ const selectAllOut = (gridComponent) => {
});
};
const selectApplicationIn = (ids: string[], gridComponent, dialogComponent) => {
axios
.post(Environment.apiContextPath('/api/system/menu/addApplications'), {
one: menuTreeGridRef.value.getSelectedRows()[0].id,
many: ids,
})
.then(() => {
gridComponent?.refresh();
dialogComponent?.close();
});
};
const selectApplicationOut = (ids, gridComponent) => {
axios
.post(Environment.apiContextPath('/api/system/menu/removeApplications'), {
one: menuTreeGridRef.value.getSelectedRows()[0].id,
many: ids,
})
.then(() => {
gridComponent?.refresh();
});
};
const selectApplicationAllIn = (gridComponent) => {
axios
.post(Environment.apiContextPath('/api/system/menu/addAllApplications'), {
one: menuTreeGridRef.value.getSelectedRows()[0].id,
many: [],
})
.then(() => {
gridComponent?.refresh();
});
};
const selectApplicationAllOut = (gridComponent) => {
axios
.post(Environment.apiContextPath('/api/system/menu/removeAllApplications'), {
one: menuTreeGridRef.value.getSelectedRows()[0].id,
many: [],
})
.then(() => {
gridComponent?.refresh();
});
};
const update = (ids, gridComponent) => {
axios
.post(Environment.apiContextPath('/api/system/menu/updateOrgs'), {

9
io.sc.platform.system.frontend/src/views/parameter/Parameter.vue

@ -88,7 +88,14 @@
return true;
},
},
{ name: 'code', label: $t('code'), type: 'text' },
{
name: 'code',
label: $t('code'),
type: 'text',
readOnlyIf: () => {
return true;
},
},
{
name: 'valueInput',
label: $t('value'),

102
io.sc.platform.system.frontend/src/views/shared/SelectApplicationDialog.vue

@ -0,0 +1,102 @@
<template>
<w-dialog
ref="dialogRef"
:title="$t('system.shared.selectApplication.dialog.title')"
width="800px"
height="500px"
:can-maximize="false"
:buttons="[
{
label: $t('confirm'),
click: () => {
const ids = Tools.extractProperties(gridRef.getSelectedRows(), 'id');
emit('afterSelected', ids, dialogRef);
},
},
]"
>
<div class="px-2" style="height: 100%">
<w-grid
ref="gridRef"
:title="$t('system.shared.selectApplication.dialog.grid.title')"
selection="multiple"
:full-screen-button="false"
:toolbar-configure="{ noIcon: false }"
:toolbar-actions="['query', 'refresh']"
:query-form-fields="[
{ name: 'code', label: $t('code'), type: 'text' },
{ name: 'name', label: $t('name'), type: 'text' },
{
name: 'enable',
label: $t('enable'),
type: 'select',
options: Options.yesNo(),
queryOperator: 'equals',
},
{
name: 'dataComeFrom',
label: $t('dataComeFrom'),
type: 'select',
options: Options.enum(DataComeFromEnum),
queryOperator: 'equals',
},
]"
:auto-fetch-data="false"
:fetch-data-url="fetchDataUrl + '?' + foreignKey + '=' + foreignValue"
:columns="[
{ name: 'code', label: $t('code') },
{ name: 'name', label: $t('name') },
{
name: 'status',
label: t('status'),
format: Formater.enableTag(),
},
{ name: 'lastModifier', label: t('lastModifier') },
{ name: 'lastModifyDate', label: t('lastModifyDate') },
]"
></w-grid>
</div>
</w-dialog>
</template>
<script setup lang="ts">
import { ref, nextTick } from 'vue';
import { useI18n } from 'vue-i18n';
import { Environment, Tools, EnumTools, Options, Formater } from 'platform-core';
const props = defineProps({
opener: { type: Object, default: undefined },
fetchDataUrl: { type: String, default: '' },
foreignKey: { type: String, default: '' },
foreignValue: { type: String, default: '' },
});
const emit = defineEmits<{
(e: 'afterSelected', ids: string[], dialogComponent: any): void;
}>();
const { t } = useI18n();
const dialogRef = ref();
const gridRef = ref();
const foreignKeyRef = ref();
const open = (foreignKey: string) => {
foreignKeyRef.value = foreignKey;
dialogRef.value.show();
nextTick(() => {
gridRef.value.refresh();
});
};
const close = () => {
dialogRef.value.hide();
};
defineExpose({
open,
close,
});
const DataComeFromEnum = await EnumTools.fetch('io.sc.platform.orm.api.enums.DataComeFrom');
</script>

141
io.sc.platform.system.frontend/src/views/shared/SelectApplicationGrid.vue

@ -0,0 +1,141 @@
<template>
<w-grid
ref="gridRef"
:title="$t('system.shared.selectApplication.grid.title')"
:config-button="false"
selection="multiple"
:checkbox-selection="true"
:fetch-data-url="fetchDataUrl + '?' + foreignKey + '=' + foreignValue"
:auto-fetch-data="false"
:toolbar-configure="{ noIcon: true }"
:toolbar-actions="[
'refresh',
'separator',
{
name: 'selectIn',
label: $t('system.shared.selectApplication.grid.toolbar.selectIn'),
enableIf: () => {
return foreignValue ? true : false;
},
click: () => {
dialogRef.open(foreignValue);
},
},
{
name: 'selectOut',
label: $t('system.shared.selectApplication.grid.toolbar.selectOut'),
enableIf: () => {
return foreignValue && gridRef?.getSelectedRows()?.length > 0;
},
click: (arg) => {
const ids = Tools.extractProperties(arg.selecteds, 'id');
DialogManager.confirm($t('system.shared.selectApplication.grid.toolbar.selectOut.tip'), () => {
emit('selectOut', ids, gridRef);
});
},
},
'separator',
{
name: 'selectAllIn',
label: $t('system.shared.selectApplication.grid.toolbar.selectAllIn'),
enableIf: () => {
return foreignValue ? true : false;
},
click: () => {
DialogManager.confirm($t('system.shared.selectApplication.grid.toolbar.selectAllIn.tip'), () => {
emit('selectAllIn', gridRef);
});
},
},
{
name: 'selectAllOut',
label: $t('system.shared.selectApplication.grid.toolbar.selectAllOut'),
enableIf: () => {
return foreignValue && gridRef?.getRows()?.length > 0;
},
click: () => {
DialogManager.confirm($t('system.shared.selectApplication.grid.toolbar.selectAllOut.tip'), () => {
emit('selectAllOut', gridRef);
});
},
},
'separator',
'view',
]"
:columns="[
{ width: 150, name: 'code', label: $t('code') },
{ width: '100%', name: 'name', label: $t('name') },
{
width: 80,
name: 'enable',
label: $t('status'),
format: Formater.enableTag(),
},
]"
:viewer="{
panel: {
columnNum: 1,
fields: [
{ name: 'id', label: $t('id') },
{ name: 'code', label: $t('code') },
{ name: 'name', label: $t('name') },
{ name: 'description', label: $t('description') },
{ name: 'enable', label: $t('enable') },
{ name: 'dataComeFrom', label: $t('dataComeFrom') },
{ name: 'creator', label: $t('creator') },
{ name: 'createDate', label: $t('createDate') },
{ name: 'lastModifier', label: $t('lastModifier') },
{ name: 'lastModifyDate', label: $t('lastModifyDate') },
{ name: 'corporationCode', label: $t('corporationCode') },
],
},
}"
></w-grid>
<SelectApplicationDialog
ref="dialogRef"
:opener="gridRef"
:fetch-data-url="fetchOtherDataUrl"
:foreign-key="foreignKey"
:foreign-value="foreignValue"
@after-selected="
(ids: string[]) => {
emit('selectIn', ids, gridRef, dialogRef);
}
"
></SelectApplicationDialog>
</template>
<script setup lang="ts">
import { ref, onUpdated } from 'vue';
import { EnumTools, DialogManager, Formater, Tools } from 'platform-core';
import SelectApplicationDialog from './SelectApplicationDialog.vue';
const props = defineProps({
fetchDataUrl: { type: String, default: '' },
fetchOtherDataUrl: { type: String, default: '' },
foreignKey: { type: String, default: '' },
foreignValue: { type: String, default: '' },
});
const emit = defineEmits<{
(e: 'selectIn', ids: string[], gridComponent: any, dialogComponent: any): void;
(e: 'selectOut', ids: string[], gridComponent: any): void;
(e: 'selectAllIn', gridComponent: any): void;
(e: 'selectAllOut', gridComponent: any): void;
}>();
const gridRef = ref();
const dialogRef = ref();
const refresh = () => {
gridRef.value.refresh();
};
onUpdated(() => {
gridRef.value.refresh();
});
defineExpose({
refresh,
});
</script>

110
io.sc.platform.system/src/main/java/io/sc/platform/system/application/controller/ApplicationWebController.java

@ -0,0 +1,110 @@
package io.sc.platform.system.application.controller;
import io.sc.platform.mvc.controller.support.RestCrudController;
import io.sc.platform.mvc.support.One2Many;
import io.sc.platform.orm.service.support.QueryParameter;
import io.sc.platform.orm.service.support.QueryResult;
import io.sc.platform.orm.util.EntityVoUtil;
import io.sc.platform.system.api.application.ApplicationVo;
import io.sc.platform.system.application.jpa.entity.ApplicationEntity;
import io.sc.platform.system.application.jpa.repository.ApplicationRepository;
import io.sc.platform.system.application.service.ApplicationService;
import org.springframework.data.domain.Page;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/system/application")
public class ApplicationWebController extends RestCrudController<ApplicationVo,ApplicationEntity,String,ApplicationRepository, ApplicationService> {
/**
* 查询菜单所属的应用
* @param menuId 菜单ID
* @param queryParameter 查询参数
* @return 菜单所属的应用
* @throws Exception 违例
*/
@GetMapping("queryApplicationsByMenu")
public Page<ApplicationVo> queryApplicationsByMenu(String menuId,QueryParameter queryParameter) throws Exception{
if(StringUtils.hasText(menuId)){
Page<ApplicationEntity> result =service.queryApplicationsByMenu(menuId,queryParameter);
return EntityVoUtil.toVo(result);
}
return QueryResult.emptyPage();
}
/**
* 查询菜单不属于的应用
* @param menuId 菜单ID
* @param queryParameter 查询参数
* @return 菜单不属于的应用
* @throws Exception 违例
*/
@GetMapping("queryOtherApplicationsByMenu")
public Page<ApplicationVo> queryOtherApplicationsByMenu(@RequestParam(name="menuId",required=false) String menuId, QueryParameter queryParameter) throws Exception{
if(StringUtils.hasText(menuId)){
Page<ApplicationEntity> result =service.queryOtherApplicationsByMenu(menuId,queryParameter);
return EntityVoUtil.toVo(result);
}
return QueryResult.emptyPage();
}
/**
* 给应用添加菜单
* @param wrapper 一对多关系(ID关系)封装器, one: 代表应用名, many: 代表菜单ID集合
* @throws Exception 违例
*/
@PostMapping("addMenus")
public void addMenus(@RequestBody One2Many<String,String> wrapper) throws Exception{
if(wrapper!=null){
service.addMenus(wrapper.getOne(), wrapper.getMany());
}
}
/**
* 给应用添加所有菜单
* @param wrapper 一对多关系(ID关系)封装器, one: 代表应用名
* @throws Exception 违例
*/
@PostMapping("addAllMenus")
public void addAllMenus(@RequestBody One2Many<String,String> wrapper) throws Exception{
if(wrapper!=null){
service.addAllMenus(wrapper.getOne());
}
}
/**
* 移除应用包含的菜单
* @param wrapper 一对多关系(ID关系)封装器, one: 代表应用名, many: 代表菜单ID集合
* @throws Exception 违例
*/
@PostMapping("removeMenus")
public void removeMenus(@RequestBody One2Many<String,String> wrapper) throws Exception{
if(wrapper!=null){
service.removeMenus(wrapper.getOne(), wrapper.getMany());
}
}
/**
* 移除应用包含的所有菜单
* @param wrapper 一对多关系(ID关系)封装器, one: 代表应用名
* @throws Exception 违例
*/
@PostMapping("removeAllMenus")
public void removeAllMenus(@RequestBody One2Many<String,String> wrapper) throws Exception{
if(wrapper!=null){
service.removeAllMenus(wrapper.getOne());
}
}
/**
* 更新应用包含的菜单
* @param wrapper 一对多关系(ID关系)封装器, one: 代表应用名, many: 代表菜单ID集合
* @throws Exception 违例
*/
@PostMapping("updateMenus")
public void updateMenus(@RequestBody One2Many<String,String> wrapper) throws Exception{
if(wrapper!=null){
service.updateMenus(wrapper.getOne(),wrapper.getMany());
}
}
}

169
io.sc.platform.system/src/main/java/io/sc/platform/system/application/jpa/entity/ApplicationEntity.java

@ -0,0 +1,169 @@
package io.sc.platform.system.application.jpa.entity;
import io.sc.platform.orm.converter.NumericBooleanConverter;
import io.sc.platform.orm.entity.CorporationAuditorEntity;
import io.sc.platform.system.api.application.ApplicationVo;
import io.sc.platform.system.menu.jpa.entity.MenuEntity;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.*;
import javax.validation.constraints.Size;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@Entity
@Table(name="SYS_APPLICATION")
public class ApplicationEntity extends CorporationAuditorEntity<ApplicationVo> {
//主键
@Id
@GeneratedValue(generator = "system-uuid")
@GenericGenerator(name = "system-uuid", strategy = "uuid2")
@Column(name="ID_", length=36)
@Size(max=36)
private String id;
//代码
@Column(name="CODE_",nullable=false,length=254)
@Size(max=254)
private String code;
//名称
@Column(name="NAME_", length=254)
@Size(min=1,max=255)
private String name;
//描述
@Column(name="DESCRIPTION_", length=254)
@Size(max=254)
private String description;
//是否可用
@Column(name="ENABLE_")
@Convert(converter= NumericBooleanConverter.class)
private Boolean enable;
//顺序
@Column(name="ORDER_")
private Integer order;
@ManyToMany(mappedBy="applications",fetch=FetchType.LAZY)
private List<MenuEntity> menus =new ArrayList<MenuEntity>();
@Override
public ApplicationVo toVo() {
ApplicationVo vo =new ApplicationVo();
super.toVo(vo);
vo.setId(this.getId());
vo.setCode(this.getCode());
vo.setName(this.getName());
vo.setDescription(this.getDescription());
vo.setEnable(this.getEnable());
vo.setOrder(this.getOrder());
return vo;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Boolean getEnable() {
return enable;
}
public void setEnable(Boolean enable) {
this.enable = enable;
}
public Integer getOrder() {
return order;
}
public void setOrder(Integer order) {
this.order = order;
}
public List<MenuEntity> getMenus() {
return menus;
}
public void setMenus(List<MenuEntity> menus) {
this.menus = menus;
}
/**
* 增加菜单
* @param menus 菜单
*/
public void addMenus(MenuEntity... menus){
if(menus!=null && menus.length>0){
for(MenuEntity menu : menus){
if(menu.getApplications()!=null && !menu.getApplications().contains(this)){
menu.getApplications().add(this);
}
if(this.getMenus()!=null && !this.getMenus().contains(menu)){
this.getMenus().add(menu);
}
}
}
}
/**
* 移除菜单
* @param menus 菜单
*/
public void removeMenus(MenuEntity...menus){
if(menus!=null && menus.length>0){
for(MenuEntity menu : menus){
if(menu.getApplications()!=null && menu.getApplications().contains(this)){
menu.getApplications().remove(this);
}
if(this.getMenus()!=null && this.getMenus().contains(menu)){
this.getMenus().remove(menu);
}
}
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
ApplicationEntity that = (ApplicationEntity) o;
return Objects.equals(id, that.id);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), id);
}
}

15
io.sc.platform.system/src/main/java/io/sc/platform/system/application/jpa/repository/ApplicationRepository.java

@ -0,0 +1,15 @@
package io.sc.platform.system.application.jpa.repository;
import io.sc.platform.orm.repository.DaoRepository;
import io.sc.platform.system.application.jpa.entity.ApplicationEntity;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
public interface ApplicationRepository extends DaoRepository<ApplicationEntity,String> {
@Query("select max(e.order)+1 from io.sc.platform.system.application.jpa.entity.ApplicationEntity e")
public Integer getNextOrder();
public ApplicationEntity findByCode(String code);
public List<ApplicationEntity> findAllByOrderByName();
}

77
io.sc.platform.system/src/main/java/io/sc/platform/system/application/service/ApplicationService.java

@ -0,0 +1,77 @@
package io.sc.platform.system.application.service;
import io.sc.platform.orm.service.DaoService;
import io.sc.platform.orm.service.support.QueryParameter;
import io.sc.platform.system.application.jpa.entity.ApplicationEntity;
import io.sc.platform.system.application.jpa.repository.ApplicationRepository;
import org.springframework.data.domain.Page;
import java.util.List;
import java.util.Set;
public interface ApplicationService extends DaoService<ApplicationEntity, String, ApplicationRepository> {
/**
* 查询应用列表
* @param filter 过滤文本
* @param queryParameter 查询参数
* @return 匹配的应用
* @throws Exception 违例
*/
public Page<ApplicationEntity> queryApplicationsByCodeOrName(String filter, QueryParameter queryParameter) throws Exception;
/**
* 查询菜单所属的应用
* @param menuId 菜单ID
* @param queryParameter 查询参数
* @return 菜单所属的应用
* @throws Exception 违例
*/
public Page<ApplicationEntity> queryApplicationsByMenu(String menuId,QueryParameter queryParameter) throws Exception;
/**
* 查询菜单不属于的应用
* @param menuId 菜单ID
* @param queryParameter 查询参数
* @return 菜单不属于的应用
* @throws Exception 违例
*/
public Page<ApplicationEntity> queryOtherApplicationsByMenu(String menuId, QueryParameter queryParameter) throws Exception;
/**
* 给应用添加菜单
* @param ApplicationId 应用ID
* @param menuIds 需要添加的菜单ID列表
* @throws Exception 违例
*/
public void addMenus(String ApplicationId,Set<String> menuIds) throws Exception;
/**
* 给应用添加所有菜单
* @param ApplicationId 应用ID
* @throws Exception 违例
*/
public void addAllMenus(String ApplicationId) throws Exception;
/**
* 移除应用包含的菜单
* @param ApplicationId 应用ID
* @param menuIds 需要移除的菜单ID列表
* @throws Exception 违例
*/
public void removeMenus(String ApplicationId,Set<String> menuIds) throws Exception;
/**
* 移除应用包含的所有菜单
* @param ApplicationId 应用ID
* @throws Exception 违例
*/
public void removeAllMenus(String ApplicationId) throws Exception;
/**
* 更新应用包含的菜单
* @param ApplicationId 应用ID
* @param menuIds 需要更新的菜单ID列表
* @throws Exception 违例
*/
public void updateMenus(String ApplicationId,Set<String> menuIds) throws Exception;
}

146
io.sc.platform.system/src/main/java/io/sc/platform/system/application/service/impl/ApplicationServiceImpl.java

@ -0,0 +1,146 @@
package io.sc.platform.system.application.service.impl;
import io.sc.platform.jdbc.util.SqlBatcher;
import io.sc.platform.orm.entity.support.EntityChangedEventType;
import io.sc.platform.orm.service.impl.DaoServiceImpl;
import io.sc.platform.orm.service.support.QueryParameter;
import io.sc.platform.orm.service.support.QueryResult;
import io.sc.platform.system.application.jpa.entity.ApplicationEntity;
import io.sc.platform.system.application.jpa.repository.ApplicationRepository;
import io.sc.platform.system.application.service.ApplicationService;
import io.sc.platform.system.menu.jpa.entity.MenuEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.Subquery;
import javax.transaction.Transactional;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Set;
@Service
public class ApplicationServiceImpl extends DaoServiceImpl<ApplicationEntity, String, ApplicationRepository> implements ApplicationService {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public ApplicationEntity add(ApplicationEntity entity) throws Exception {
Integer nextOrder =repository.getNextOrder();
entity.setOrder(nextOrder==null?1:nextOrder);
return super.add(entity);
}
@Override
public Page<ApplicationEntity> queryApplicationsByCodeOrName(String filter, QueryParameter queryParameter) throws Exception {
if(StringUtils.hasText(filter)) {
Specification<ApplicationEntity> specification = (root, query, criteriaBuilder) -> {
return criteriaBuilder.or(
criteriaBuilder.like(root.get("code"),filter),
criteriaBuilder.like(root.get("name"),filter)
);
};
return this.query(specification, queryParameter);
}
return QueryResult.emptyPage();
}
@Override
public Page<ApplicationEntity> queryApplicationsByMenu(String menuId, QueryParameter queryParameter) throws Exception {
if(StringUtils.hasText(menuId)) {
Specification<ApplicationEntity> specification = (root, query, criteriaBuilder) -> {
Subquery<Integer> subquery =query.subquery(Integer.class);
Join<ApplicationEntity,MenuEntity> join = subquery.correlate(root).join("menus");
subquery.select(criteriaBuilder.literal(1));
subquery.where(criteriaBuilder.equal(join.get("id"), menuId));
return criteriaBuilder.exists(subquery);
};
return this.query(specification, queryParameter);
}
return QueryResult.emptyPage();
}
@Override
public Page<ApplicationEntity> queryOtherApplicationsByMenu(String menuId, QueryParameter queryParameter) throws Exception {
if(StringUtils.hasText(menuId)) {
Specification<ApplicationEntity> baseSpecification = (root, query, criteriaBuilder) -> {
Subquery<Integer> subquery =query.subquery(Integer.class);
Join<ApplicationEntity,MenuEntity> join = subquery.correlate(root).join("menus");
subquery.select(criteriaBuilder.literal(1));
subquery.where(criteriaBuilder.equal(join.get("id"), menuId));
return criteriaBuilder.not(criteriaBuilder.exists(subquery));
};
Specification<ApplicationEntity> specification =buildSpecification(queryParameter);
return this.query(baseSpecification.and(specification), queryParameter);
}
return QueryResult.emptyPage();
}
@Override
@Transactional
public void addMenus(String applicationId, Set<String> menuIds) throws Exception {
if(StringUtils.hasText(applicationId) && menuIds!=null && !menuIds.isEmpty()){
SqlBatcher sqlBatcher =new SqlBatcher("insert into SYS_APPLICATION_MENU(MENU_ID_,APPLICATION_ID_) values(?,?)");
for(String menuId : menuIds){
sqlBatcher.addArg(new Object[]{menuId,applicationId});
}
sqlBatcher.execute(jdbcTemplate);
}
}
@Override
@Transactional
public void addAllMenus(final String applicationId) throws Exception {
if(StringUtils.hasText(applicationId)){
removeAllMenus(applicationId);
ApplicationEntity application =repository.getOne(applicationId);
if(application!=null){
final SqlBatcher sqlBatcher =new SqlBatcher("insert into SYS_APPLICATION_MENU(MENU_ID_,APPLICATION_ID_) values(?,?)");
String query ="select ID_ from SYS_MENU";
jdbcTemplate.query(query, new RowCallbackHandler() {
@Override
public void processRow(ResultSet rs) throws SQLException {
sqlBatcher.addArg(new Object[]{rs.getString("ID_"),applicationId});
}
});
sqlBatcher.execute(jdbcTemplate);
}
}
}
@Override
@Transactional
public void removeMenus(String applicationId, Set<String> menuIds) throws Exception {
if(StringUtils.hasText(applicationId) && menuIds!=null && !menuIds.isEmpty()){
SqlBatcher sqlBatcher =new SqlBatcher("delete from SYS_APPLICATION_MENU where MENU_ID_=? and APPLICATION_ID_=?");
for(String menuId : menuIds){
sqlBatcher.addArg(new Object[]{menuId,applicationId});
}
sqlBatcher.execute(jdbcTemplate);
}
}
@Override
@Transactional
public void removeAllMenus(String applicationId) throws Exception {
jdbcTemplate.update("delete from SYS_APPLICATION_MENU where APPLICATION_ID_=?", applicationId);
}
@Override
@Transactional
public void updateMenus(String applicationId, Set<String> menuIds) throws Exception {
removeAllMenus(applicationId);
addMenus(applicationId,menuIds);
}
}

11
io.sc.platform.system/src/main/java/io/sc/platform/system/application/support/ApplicationComparator.java

@ -0,0 +1,11 @@
package io.sc.platform.system.application.support;
public class ApplicationComparator {
public static final ApplicationVoNameComparator voName =new ApplicationVoNameComparator(true);
public static final ApplicationVoNameComparator voNameDesc =new ApplicationVoNameComparator(false);
public static final ApplicationVoCodeComparator voCode =new ApplicationVoCodeComparator(true);
public static final ApplicationVoCodeComparator voCodeDesc =new ApplicationVoCodeComparator(false);
public static final ApplicationEntityNameComparator entityName =new ApplicationEntityNameComparator();
public static final ApplicationEntityNameComparator entityNameDesc =new ApplicationEntityNameComparator();
}

26
io.sc.platform.system/src/main/java/io/sc/platform/system/application/support/ApplicationEntityNameComparator.java

@ -0,0 +1,26 @@
package io.sc.platform.system.application.support;
import io.sc.platform.core.util.PinyinUtil;
import io.sc.platform.system.application.jpa.entity.ApplicationEntity;
import io.sc.platform.system.org.jpa.entity.OrgEntity;
import java.util.Comparator;
public class ApplicationEntityNameComparator implements Comparator<ApplicationEntity> {
private boolean asc =true;
public ApplicationEntityNameComparator(){}
public ApplicationEntityNameComparator(boolean asc){
this.asc =asc;
}
@Override
public int compare(ApplicationEntity o1, ApplicationEntity o2) {
if(asc) {
return PinyinUtil.compare(o1.getName(), o2.getName());
}else{
return PinyinUtil.compare(o2.getName(), o1.getName());
}
}
}

26
io.sc.platform.system/src/main/java/io/sc/platform/system/application/support/ApplicationVoCodeComparator.java

@ -0,0 +1,26 @@
package io.sc.platform.system.application.support;
import io.sc.platform.core.util.PinyinUtil;
import io.sc.platform.system.api.application.ApplicationVo;
import io.sc.platform.system.api.org.OrgVo;
import java.util.Comparator;
public class ApplicationVoCodeComparator implements Comparator<ApplicationVo> {
private boolean asc =true;
public ApplicationVoCodeComparator(){}
public ApplicationVoCodeComparator(boolean asc){
this.asc =asc;
}
@Override
public int compare(ApplicationVo o1, ApplicationVo o2) {
if(asc) {
return PinyinUtil.compare(o1.getCode(), o2.getCode());
}else{
return PinyinUtil.compare(o2.getCode(), o1.getCode());
}
}
}

26
io.sc.platform.system/src/main/java/io/sc/platform/system/application/support/ApplicationVoNameComparator.java

@ -0,0 +1,26 @@
package io.sc.platform.system.application.support;
import io.sc.platform.core.util.PinyinUtil;
import io.sc.platform.system.api.application.ApplicationVo;
import io.sc.platform.system.api.org.OrgVo;
import java.util.Comparator;
public class ApplicationVoNameComparator implements Comparator<ApplicationVo> {
private boolean asc =true;
public ApplicationVoNameComparator(){}
public ApplicationVoNameComparator(boolean asc){
this.asc =asc;
}
@Override
public int compare(ApplicationVo o1, ApplicationVo o2) {
if(asc) {
return PinyinUtil.compare(o1.getName(), o2.getName());
}else{
return PinyinUtil.compare(o2.getName(), o1.getName());
}
}
}

40
io.sc.platform.system/src/main/java/io/sc/platform/system/menu/controller/MenuWebController.java

@ -148,6 +148,46 @@ public class MenuWebController extends RestCrudController<MenuVo,MenuEntity,Stri
}
}
/**
* 给菜单添加应用
* @param wrapper 具有级联关系的菜单和应用关系封装器, 由于菜单具有级联关系(如果某个应用拥有一个子菜单的访问权限,那么该应用同时拥有该子菜单所有父菜单分类的访问权限)
* @throws Exception 违例
*/
@PostMapping("addApplications")
public void addApplications(@RequestBody CascadeMany2Many<String,String> wrapper) throws Exception{
service.addApplications(wrapper);
}
/**
* 给菜单添加所有应用
* @param wrapper 具有级联关系的菜单和应用关系封装器
* @throws Exception 违例
*/
@PostMapping("addAllApplications")
public void addAllApplications(@RequestBody CascadeMany2Many<String,String> wrapper) throws Exception{
service.addAllApplications(wrapper);
}
/**
* 移除菜单包含的应用
* @param wrapper 具有级联关系的菜单和应用关系封装器
* @throws Exception 违例
*/
@PostMapping("removeApplications")
public void removeApplications(@RequestBody CascadeMany2Many<String,String> wrapper) throws Exception{
service.removeApplications(wrapper);
}
/**
* 移除菜单包含的所有应用
* @param wrapper 具有级联关系的菜单和应用关系封装器
* @throws Exception 违例
*/
@PostMapping("removeAllApplications")
public void removeAllApplications(@RequestBody CascadeMany2Many<String,String> wrapper) throws Exception{
service.removeAllApplications(wrapper);
}
@GetMapping("getAllMenuPlugins")
public List<MenuItem> getAllMenuPlugins() throws Exception{
return service.getAllMenuPlugins();

54
io.sc.platform.system/src/main/java/io/sc/platform/system/menu/jpa/entity/MenuEntity.java

@ -8,6 +8,7 @@ import io.sc.platform.orm.api.vo.CorporationAuditorVo;
import io.sc.platform.orm.converter.NumericBooleanConverter;
import io.sc.platform.orm.entity.CorporationAuditorEntity;
import io.sc.platform.system.api.menu.MenuVo;
import io.sc.platform.system.application.jpa.entity.ApplicationEntity;
import io.sc.platform.system.enums.MenuType;
import io.sc.platform.system.org.jpa.entity.OrgEntity;
import io.sc.platform.system.role.jpa.entity.RoleEntity;
@ -96,6 +97,15 @@ public class MenuEntity extends CorporationAuditorEntity<MenuVo> implements IdCl
)
protected List<OrgEntity> orgs =new ArrayList<OrgEntity>();
//包含的应用集合
@ManyToMany(fetch=FetchType.LAZY)
@JoinTable(
name = "SYS_APPLICATION_MENU",
joinColumns = {@JoinColumn(name = "MENU_ID_", nullable = false,referencedColumnName="ID_")},
inverseJoinColumns = {@JoinColumn(name = "APPLICATION_ID_",nullable = false,referencedColumnName="ID_")}
)
protected List<ApplicationEntity> applications =new ArrayList<>();
//父
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="PARENT_ID_")
@ -197,6 +207,14 @@ public class MenuEntity extends CorporationAuditorEntity<MenuVo> implements IdCl
this.orgs = orgs;
}
public List<ApplicationEntity> getApplications() {
return applications;
}
public void setApplications(List<ApplicationEntity> applications) {
this.applications = applications;
}
public MenuGroupEntity getParent() {
return parent;
}
@ -277,6 +295,42 @@ public class MenuEntity extends CorporationAuditorEntity<MenuVo> implements IdCl
}
}
/**
* 添加应用
* @param applications 应用
*/
public void addApplications(ApplicationEntity...applications){
if(applications!=null && applications.length>0){
for(ApplicationEntity application : applications){
List<MenuEntity> menus =application.getMenus();
if(menus!=null && !menus.contains(this)){
menus.add(this);
}
if(this.applications!=null && !this.applications.contains(application)){
this.applications.add(application);
}
}
}
}
/**
* 移除应用
* @param applications 应用
*/
public void removeApplications(ApplicationEntity...applications){
if(applications!=null && applications.length>0){
for(ApplicationEntity application : applications){
List<MenuEntity> menus =application.getMenus();
if(menus!=null && menus.contains(this)){
menus.remove(this);
}
if(this.applications!=null && this.applications.contains(application)){
this.applications.remove(application);
}
}
}
}
/**
* 重新设置父,并建立父子关系
* @param parent 新父

46
io.sc.platform.system/src/main/java/io/sc/platform/system/menu/service/MenuService.java

@ -34,6 +34,15 @@ public interface MenuService extends DaoService<MenuEntity, String, MenuReposito
*/
public List<MenuVo> listAllMenusWithSelectedStatusByOrg(String orgId, QueryParameter queryParameter) throws Exception;
/**
* 列出所有菜单树,并且将应用所拥有的菜单列表作出标记
* @param applicationId 应用ID
* @param queryParameter 查询参数
* @return 所有菜单树,并且将应用所拥有的菜单列表作出标记
* @throws Exception 违例
*/
public List<MenuVo> listAllMenusWithSelectedStatusByApplication(String applicationId, QueryParameter queryParameter) throws Exception;
/**
* 查询角色所拥有的菜单
* @param roleId 角色ID
@ -52,6 +61,15 @@ public interface MenuService extends DaoService<MenuEntity, String, MenuReposito
*/
public List<MenuEntity> listMenusByOrg(String orgId, QueryParameter queryParameter) throws Exception;
/**
* 查询应用所拥有的菜单
* @param applicationId 应用ID
* @param queryParameter 查询参数
* @return 应用所拥有的菜单
* @throws Exception 违例
*/
public List<MenuEntity> listMenusByApplication(String applicationId, QueryParameter queryParameter) throws Exception;
/**
* 给菜单添加角色
* @param wrapper 具有级联关系的菜单和角色多对多关系 Wrapper
@ -87,6 +105,34 @@ public interface MenuService extends DaoService<MenuEntity, String, MenuReposito
*/
public void updateOrgs(CascadeMany2Many<String,String> wrapper) throws Exception;
/**
* 给菜单添加应用
* @param wrapper 具有级联关系的菜单和应用多对多关系 Wrapper
* @throws Exception 违例
*/
public void addApplications(CascadeMany2Many<String,String> wrapper) throws Exception;
/**
* 给菜单添加所有应用
* @param wrapper 具有级联关系的菜单和应用多对多关系 Wrapper
* @throws Exception 违例
*/
public void addAllApplications(CascadeMany2Many<String,String> wrapper) throws Exception;
/**
* 移除菜单包含应用
* @param wrapper 具有级联关系的菜单和应用多对多关系 Wrapper
* @throws Exception 违例
*/
public void removeApplications(CascadeMany2Many<String,String> wrapper) throws Exception;
/**
* 移除菜单包含的所有应用
* @param wrapper 具有级联关系的菜单和应用多对多关系 Wrapper
* @throws Exception 违例
*/
public void removeAllApplications(CascadeMany2Many<String,String> wrapper) throws Exception;
public List<MenuItem> getAllMenuPlugins() throws Exception;
/**
* 导入菜单插件

123
io.sc.platform.system/src/main/java/io/sc/platform/system/menu/service/impl/MenuServiceImpl.java

@ -10,6 +10,7 @@ import io.sc.platform.orm.service.support.QueryParameter;
import io.sc.platform.orm.service.support.QueryResult;
import io.sc.platform.orm.util.EntityVoUtil;
import io.sc.platform.system.api.menu.MenuVo;
import io.sc.platform.system.application.jpa.entity.ApplicationEntity;
import io.sc.platform.system.menu.convertor.MenuEntityPluginConvertor;
import io.sc.platform.system.menu.jpa.entity.MenuEntity;
import io.sc.platform.system.menu.jpa.entity.MenuGroupEntity;
@ -139,6 +140,31 @@ public class MenuServiceImpl extends DaoServiceImpl<MenuEntity, String, MenuRepo
return Collections.emptyList();
}
@Override
public List<MenuVo> listAllMenusWithSelectedStatusByApplication(String applicationId, QueryParameter queryParameter) throws Exception {
Sort sort =queryParameter.getSort();
if(sort==null){
queryParameter.addSortBy("order");
}
if(StringUtils.hasText(applicationId)) {
List<MenuEntity> allMenuEntities = repository.findAll();
List<MenuEntity> selectedMenuEntities = listMenusByApplication(applicationId, queryParameter);
List<MenuVo> allMenus = EntityVoUtil.toVo(allMenuEntities);
List<MenuVo> selectedMenus = EntityVoUtil.toVo(selectedMenuEntities);
Set<String> selectedMenuIds = new HashSet<>();
for (MenuVo selectedMenu : selectedMenus) {
selectedMenuIds.add(selectedMenu.getId());
}
for (MenuVo allMenu : allMenus) {
if (selectedMenuIds.contains(allMenu.getId())) {
allMenu.setSelected(true);
}
}
return allMenus;
}
return Collections.emptyList();
}
@Override
public List<MenuEntity> listMenusByRole(String roleId, QueryParameter queryParameter) throws Exception {
if(StringUtils.hasText(roleId)) {
@ -163,6 +189,18 @@ public class MenuServiceImpl extends DaoServiceImpl<MenuEntity, String, MenuRepo
return QueryResult.emptyList();
}
@Override
public List<MenuEntity> listMenusByApplication(String applicationId, QueryParameter queryParameter) throws Exception {
if(StringUtils.hasText(applicationId)) {
Specification<MenuEntity> specification = (root, query, criteriaBuilder) -> {
Join<ApplicationEntity,MenuEntity> join = root.join("applications");
return criteriaBuilder.equal(join.get("id"), applicationId);
};
return this.list(specification, queryParameter);
}
return QueryResult.emptyList();
}
@Override
@Transactional
public void addRoles(CascadeMany2Many<String,String> wrapper) throws Exception {
@ -280,6 +318,91 @@ public class MenuServiceImpl extends DaoServiceImpl<MenuEntity, String, MenuRepo
}
}
@Override
@Transactional
public void addApplications(CascadeMany2Many<String, String> wrapper) throws Exception {
if(wrapper==null){return;}
if(wrapper.getTargets()!=null && !wrapper.getTargets().isEmpty()){
Set<String> sourceIds =new HashSet<>();
sourceIds.add(wrapper.getSource());
sourceIds.addAll(wrapper.getSourceParents());
sourceIds.addAll(wrapper.getSourceChildren());
SqlBatcher sqlBatcher =new SqlBatcher("insert into SYS_APPLICATION_MENU(APPLICATION_ID_,MENU_ID_) values(?,?)");
for(String applicationId : wrapper.getTargets()){
for(String menuId : sourceIds){
sqlBatcher.addArg(new Object[]{applicationId,menuId});
}
}
sqlBatcher.execute(jdbcTemplate);
standardMenuAndRoleTable();
}
}
@Override
@Transactional
public void addAllApplications(CascadeMany2Many<String, String> wrapper) throws Exception {
if(wrapper==null){return;}
List<Map<String,Object>> applications =jdbcTemplate.queryForList("select ID_ from SYS_APPLICATION");
List<String> applicationIds =new ArrayList<String>();
if(applications!=null && applications.size()>0){
for(Map<String,Object> row : applications){
applicationIds.add((String)row.get("ID_"));
}
}
if(applicationIds!=null && !applicationIds.isEmpty()){
Set<String> sourceIds =new HashSet<>();
sourceIds.add(wrapper.getSource());
sourceIds.addAll(wrapper.getSourceParents());
sourceIds.addAll(wrapper.getSourceChildren());
SqlBatcher sqlBatcher =new SqlBatcher("insert into SYS_APPLICATION_MENU(APPLICATION_ID_,MENU_ID_) values(?,?)");
for(String applicationId : applicationIds){
for(String menuId : sourceIds){
sqlBatcher.addArg(new Object[]{applicationId,menuId});
}
}
sqlBatcher.execute(jdbcTemplate);
standardMenuAndRoleTable();
}
}
@Override
@Transactional
public void removeApplications(CascadeMany2Many<String, String> wrapper) throws Exception {
if(wrapper==null){return;}
if(wrapper.getTargets()!=null && wrapper.getTargets().size()>0){
SqlBatcher sqlBatcher =new SqlBatcher("delete from SYS_APPLICATION_MENU where MENU_ID_=? and APPLICATION_ID_=?");
for(String roleId : wrapper.getTargets()){
Set<String> sourceIds =new HashSet<>();
sourceIds.add(wrapper.getSource());
sourceIds.addAll(wrapper.getSourceChildren());
for(String menuId : sourceIds){
if(StringUtils.hasText(menuId) && StringUtils.hasText(roleId)){
sqlBatcher.addArg(new Object[]{menuId,roleId});
}
}
}
sqlBatcher.execute(jdbcTemplate);
standardMenuAndRoleTable();
}
}
@Override
@Transactional
public void removeAllApplications(CascadeMany2Many<String, String> wrapper) throws Exception {
if(wrapper==null){return;}
SqlBatcher sqlBatcher =new SqlBatcher("delete from SYS_APPLICATION_MENU where MENU_ID_=?");
Set<String> sourceIds =new HashSet<>();
sourceIds.add(wrapper.getSource());
sourceIds.addAll(wrapper.getSourceChildren());
for(String menuId : sourceIds){
if(StringUtils.hasText(menuId)){
sqlBatcher.addArg(new Object[]{menuId});
}
}
sqlBatcher.execute(jdbcTemplate);
standardMenuAndRoleTable();
}
@Override
@Transactional
public void importMenuPlugins(List<String> pluginIds) throws Exception {

6
io.sc.platform.system/src/main/java/io/sc/platform/system/role/jpa/entity/RoleEntity.java

@ -31,12 +31,12 @@ public class RoleEntity extends CorporationAuditorEntity<RoleVo> {
@Size(max=36)
private String id;
//角色名称
//代码
@Column(name="CODE_",nullable=false,length=254)
@Size(max=254)
private String code;
//显示名称
//名称
@Column(name="NAME_", length=254)
@Size(min=1,max=255)
private String name;
@ -60,7 +60,7 @@ public class RoleEntity extends CorporationAuditorEntity<RoleVo> {
@ManyToMany(mappedBy="roles",fetch=FetchType.LAZY,cascade={CascadeType.MERGE,CascadeType.REFRESH})
private List<UserEntity> users =new ArrayList<UserEntity>();
//角色可用的资源集合
//角色可用的菜单集合
@ManyToMany(mappedBy="roles",fetch=FetchType.LAZY,cascade={CascadeType.MERGE,CascadeType.REFRESH})
private List<MenuEntity> menus =new ArrayList<MenuEntity>();

3
io.sc.platform.system/src/main/resources/META-INF/platform/plugins/components.json

@ -15,6 +15,9 @@
"io.sc.platform.system.auditlog.controller",
"io.sc.platform.system.auditlog.service.impl",
"io.sc.platform.system.application.controller",
"io.sc.platform.system.application.service.impl",
"io.sc.platform.system.corporation.controller",
"io.sc.platform.system.corporation.service.impl",

3
io.sc.platform.system/src/main/resources/META-INF/platform/plugins/repositories.json

@ -15,6 +15,9 @@
"io.sc.platform.system.auditlog.jpa.entity",
"io.sc.platform.system.auditlog.jpa.repository",
"io.sc.platform.system.application.jpa.entity",
"io.sc.platform.system.application.jpa.repository",
"io.sc.platform.system.corporation.jpa.entity",
"io.sc.platform.system.corporation.jpa.repository",

59
io.sc.platform.system/src/main/resources/liquibase/io.sc.platform.system_8.0.0_20220606__System Database Schema DDL.xml

@ -11,6 +11,34 @@
"
>
<changeSet id="io.sc.platform.system_8.0.0_20220606__System Database Schema DDL" author="platform">
<!-- 应用表 -->
<createTable tableName="SYS_APPLICATION" remarks="应用表">
<column name="ID_" type="NVARCHAR(36)" remarks="ID">
<constraints primaryKey="true"/>
</column>
<column name="CODE_" type="NVARCHAR(255)" remarks="应用代码"/>
<column name="NAME_" type="NVARCHAR(255)" remarks="应用名称"></column>
<column name="DESCRIPTION_" type="NVARCHAR(255)" remarks="描述"></column>
<column name="ENABLE_" type="SMALLINT" remarks="是否可用(0:不可用,1:可用)"></column>
<column name="ORDER_" type="INTEGER" remarks="顺序"></column>
<column name="JPA_VERSION_" type="INTEGER" remarks="JPA乐观锁版本"/>
<column name="DATA_COME_FROM_" type="NVARCHAR(10)" remarks="数据来源(INPUT:手工录入,IMPORT:系统自动导入)"/>
<column name="CREATOR_" type="NVARCHAR(255)" remarks="创建人"/>
<column name="CREATE_DATE_" type="DATETIME" remarks="创建日期"/>
<column name="LAST_MODIFIER_" type="NVARCHAR(255)" remarks="最后修改人"/>
<column name="LAST_MODIFYDATE_" type="DATETIME" remarks="最后修改日期"/>
<column name="CORP_CODE_" type="NVARCHAR(255)" remarks="所属法人代码"/>
</createTable>
<addUniqueConstraint tableName="SYS_APPLICATION" columnNames="CODE_,CORP_CODE_"></addUniqueConstraint>
<addNotNullConstraint columnName="CODE_" columnDataType="NVARCHAR(255)" tableName="SYS_APPLICATION" constraintName="CONST_SYS_APPLICATION_CODE"/>
<addNotNullConstraint columnName="NAME_" columnDataType="NVARCHAR(255)" tableName="SYS_APPLICATION" constraintName="CONST_SYS_APPLICATION_VALUE"/>
<addNotNullConstraint columnName="ENABLE_" columnDataType="SMALLINT" tableName="SYS_APPLICATION" constraintName="CONST_SYS_APPLICATION_ENABLE"/>
<addNotNullConstraint columnName="DATA_COME_FROM_" columnDataType="NVARCHAR(10)" tableName="SYS_APPLICATION" constraintName="CONST_SYS_APPLICATION_DCF"/>
<addNotNullConstraint columnName="CORP_CODE_" columnDataType="NVARCHAR(255)" tableName="SYS_APPLICATION" constraintName="CONST_SYS_APPLICATION_CORP"/>
<addDefaultValue columnName="ENABLE_" columnDataType="SMALLINT" tableName="SYS_APPLICATION" defaultValueNumeric="1"/>
<addDefaultValue columnName="DATA_COME_FROM_" columnDataType="NVARCHAR(10)" tableName="SYS_APPLICATION" defaultValue="INPUT"/>
<addDefaultValue columnName="CORP_CODE_" columnDataType="NVARCHAR(255)" tableName="SYS_APPLICATION" defaultValue="_PRIMARY_"/>
<!-- 系统审计日志表 -->
<createTable tableName="SYS_AUDIT_LOG" remarks="系统审计日志表">
<column name="ID_" type="NVARCHAR(36)" remarks="ID">
@ -286,6 +314,37 @@
referencedTableName="SYS_MENU"
referencedColumnNames="ID_" onDelete="CASCADE"/>
<!-- 应用和菜单关系表 -->
<createTable tableName="SYS_APPLICATION_MENU" remarks="应用和菜单关系表">
<column name="APPLICATION_ID_" type="NVARCHAR(36)" remarks="应用ID"></column>
<column name="MENU_ID_" type="NVARCHAR(36)" remarks="菜单ID"></column>
</createTable>
<addNotNullConstraint columnName="APPLICATION_ID_" columnDataType="NVARCHAR(36)" tableName="SYS_APPLICATION_MENU" constraintName="CONST_SYS_APP_MENU_APP_ID"/>
<addNotNullConstraint columnName="MENU_ID_" columnDataType="NVARCHAR(36)" tableName="SYS_APPLICATION_MENU" constraintName="CONST_SYS_APP_MENU_MENU_ID"/>
<createIndex tableName="SYS_APPLICATION_MENU" indexName="IDX_SYS_APP_MENU_APP_ID">
<column name="APPLICATION_ID_"></column>
</createIndex>
<createIndex tableName="SYS_APPLICATION_MENU" indexName="IDX_SYS_APP_MENU_MENU_ID">
<column name="MENU_ID_"></column>
</createIndex>
<!-- 当菜单被删除,应用和菜单关系也被删除 -->
<addForeignKeyConstraint
constraintName="FKC_SYS_APP_MENU_MENU_ID"
baseTableName="SYS_APPLICATION_MENU"
baseColumnNames="MENU_ID_"
referencedTableName="SYS_MENU"
referencedColumnNames="ID_" onDelete="CASCADE"/>
<!-- 当应用被删除,应用和菜单关系也被删除 -->
<addForeignKeyConstraint
constraintName="FKC_SYS_APP_MENU_APP_ID"
baseTableName="SYS_APPLICATION_MENU"
baseColumnNames="APPLICATION_ID_"
referencedTableName="SYS_APPLICATION"
referencedColumnNames="ID_" onDelete="CASCADE"/>
<!-- 角色和菜单关系表 -->
<createTable tableName="SYS_ROLE_MENU" remarks="角色和菜单关系表">
<column name="ROLE_ID_" type="NVARCHAR(36)" remarks="角色ID"></column>

4
io.sc.standard.frontend/package.json

@ -1,6 +1,6 @@
{
"name": "io.sc.standard.frontend",
"version": "8.1.38",
"version": "8.1.40",
"description": "",
"private": false,
"keywords": [],
@ -92,7 +92,7 @@
"luckyexcel": "1.0.1",
"mockjs": "1.1.0",
"pinia": "2.1.7",
"platform-core": "8.1.190",
"platform-core": "8.1.193",
"quasar": "2.15.3",
"tailwindcss": "3.4.3",
"vue": "3.4.24",

102
io.sc.standard.frontend/src/views/standard/Country.vue

@ -132,74 +132,13 @@ let currencyMap = {};
let currencyList = [];
const { t } = useI18n();
const currencyOptionsRef = ref([]);
const columnsRef = ref([
{ width: 70, name: 'code', label: t('code') },
{ width: 70, name: 'codeLatin3', label: t('code') + '3' },
{
width: 150,
name: 'nameChinese',
label: t('name'),
format: (value, row) => {
if (row) {
return getCountryName(row);
}
},
},
{
name: 'currency',
label: t('standard.entity.currency'),
columns: [
{
width: 70,
name: 'currencyCode',
label: t('code'),
sortable: false,
format: (value, row) => {
if (row.currency) {
return currencyMap[row.currency]?.code;
}
},
},
{
width: 70,
name: 'currencyCodeLatin',
label: t('code') + '2',
sortable: false,
format: (value, row) => {
if (row.currency) {
return currencyMap[row.currency]?.codeLatin;
}
},
},
{
width: 150,
name: 'currencyname',
label: t('name'),
sortable: false,
format: (value, row) => {
if (row.currency) {
return getCurrencyName(currencyMap[row.currency]);
}
},
},
],
},
{
name: 'language',
label: t('standard.entity.language'),
columns: [{ width: 100, name: 'languageCode3', label: t('code') }],
},
{ width: 100, name: 'lastModifier', label: t('lastModifier') },
{ width: 120, name: 'lastModifyDate', label: t('lastModifyDate'), format: Formater.dateOnly() },
]);
const columnsComputed = computed(() => {
const result = [
{ width: 70, name: 'code', label: t('code') },
{ width: 70, name: 'codeLatin3', label: t('code') + '3' },
{
width: 150,
width: 120,
name: 'nameChinese',
label: t('name'),
format: (value, row) => {
@ -235,7 +174,7 @@ const columnsComputed = computed(() => {
},
},
{
width: 150,
width: 120,
name: 'currencyname',
label: t('name'),
sortable: false,
@ -248,6 +187,7 @@ const columnsComputed = computed(() => {
],
},
{
width: 80,
name: 'language',
label: t('standard.entity.language'),
columns: [{ width: 100, name: 'languageCode3', label: t('code') }],
@ -257,7 +197,8 @@ const columnsComputed = computed(() => {
{ width: 120, name: 'lastModifyDate', label: t('lastModifyDate'), format: Formater.dateOnly() },
];
if (Environment.getConfigure().setting.i18n.locale === 'zh_CN') {
currentLocale = Environment.getConfigure().setting.i18n.locale;
if (currentLocale.endsWith('CN')) {
result[2].name = 'nameChinese';
} else {
result[2].name = 'nameEnglish';
@ -265,33 +206,6 @@ const columnsComputed = computed(() => {
return result;
});
eventBus.on('onLocaleChanged', (locale) => {
currentLocale = locale;
if (locale === 'zh_CN') {
columnsRef.value.slice(2, 1, {
width: 200,
name: 'nameChinese',
label: t('name'),
format: (value, row) => {
if (row) {
return getCountryName(row);
}
},
});
} else {
columnsRef.value.slice(2, 1, {
width: 200,
name: 'nameEnglish',
label: t('name'),
format: (value, row) => {
if (row) {
return getCountryName(row);
}
},
});
}
});
onMounted(() => {
axios.get(Environment.apiContextPath('/api/standard/currency?pageable=false&sortBy=code')).then((response) => {
currencyMap = {};
@ -315,7 +229,7 @@ onMounted(() => {
});
const getCurrencyLabel = (currency) => {
return currency.code + ' ' + currency.codeLatin + ' ' + (currentLocale === 'zh_CN' ? currency.nameChinese : currency.nameEnglish);
return currency.code + ' ' + currency.codeLatin + ' ' + (currentLocale.endsWith('CN') ? currency.nameChinese : currency.nameEnglish);
};
const createCurrencyOptionItem = (currency) => {
@ -330,14 +244,14 @@ const createCurrencyOptionItem = (currency) => {
const getCountryName = (country) => {
if (country) {
return currentLocale === 'zh_CN' ? country.nameChinese : country.nameEnglish;
return currentLocale.endsWith('CN') ? country.nameChinese : country.nameEnglish;
}
return null;
};
const getCurrencyName = (currency) => {
if (currency) {
return currentLocale === 'zh_CN' ? currency.nameChinese : currency.nameEnglish;
return currentLocale.endsWith('CN') ? currency.nameChinese : currency.nameEnglish;
}
return null;
};

Loading…
Cancel
Save