Browse Source

update

main
wangshaoping 9 months ago
parent
commit
f50ca14395
  1. 1
      app.platform.scheduler.manager/build.gradle
  2. 38
      app.platform/build.gradle
  3. 198
      app.platform/src/main/java/app/platform/Application.java
  4. 2263
      app.platform/wsp.txt
  5. 406
      build-version.gradle
  6. 27
      build.gradle
  7. 4
      erm.frontend/package.json
  8. 7
      erm.frontend/webpack.config.common.cjs
  9. 1
      erm/src/main/resources/liquibase/kpi/erm.kpi_1.0.0_20221020__ERM KPI Database Schema DDL.xml
  10. 9
      gradle.properties
  11. 4
      io.sc.engine.mv.frontend/package.json
  12. 7
      io.sc.engine.mv.frontend/webpack.config.common.cjs
  13. 4
      io.sc.engine.rule.frontend/package.json
  14. 7
      io.sc.engine.rule.frontend/webpack.config.common.cjs
  15. 4
      io.sc.engine.st.frontend/package.json
  16. 7
      io.sc.engine.st.frontend/webpack.config.common.cjs
  17. 7
      io.sc.platform.app/build.gradle
  18. 2
      io.sc.platform.core.frontend/package.json
  19. 16
      io.sc.platform.core.frontend/src/components/index.ts
  20. 8
      io.sc.platform.core.frontend/src/i18n/messages.json
  21. 8
      io.sc.platform.core.frontend/src/i18n/messages_tw_CN.json
  22. 9
      io.sc.platform.core.frontend/src/i18n/messages_zh_CN.json
  23. 42
      io.sc.platform.core.frontend/src/menus/menus.json
  24. 3
      io.sc.platform.core.frontend/src/platform/layout/LeftMenuLayout.vue
  25. 2
      io.sc.platform.core.frontend/src/platform/layout/MobileLayout.vue
  26. 14
      io.sc.platform.core.frontend/src/platform/layout/WBasicLayout.vue
  27. 18
      io.sc.platform.core.frontend/src/platform/layout/sub-layout/Main.vue
  28. 4
      io.sc.platform.core.frontend/src/platform/layout/sub-layout/Sider.vue
  29. 4
      io.sc.platform.core.frontend/src/platform/layout/sub-layout/SiderCollapser.vue
  30. 1
      io.sc.platform.core.frontend/src/platform/plugin/environment/default-configure.json
  31. 19
      io.sc.platform.core.frontend/src/platform/plugin/environment/index.ts
  32. 1
      io.sc.platform.core.frontend/src/platform/plugin/manager/ApplicationInitializer.ts
  33. 17
      io.sc.platform.core.frontend/src/platform/plugin/manager/RouterManager.ts
  34. 80
      io.sc.platform.core.frontend/src/platform/plugin/manager/TagViewManager.ts
  35. 1
      io.sc.platform.core.frontend/src/platform/plugin/router.ts
  36. 1
      io.sc.platform.core.frontend/src/platform/types/ConfigureType.ts
  37. 2
      io.sc.platform.core.frontend/src/platform/types/FrontEndRouteType.ts
  38. 1
      io.sc.platform.core.frontend/src/platform/types/TagViewType.ts
  39. 74
      io.sc.platform.core.frontend/src/routes/routes.json
  40. 53
      io.sc.platform.core.frontend/src/views/testcase/excel/Excel.vue
  41. 409
      io.sc.platform.core.frontend/src/views/testcase/form/form.vue
  42. 133
      io.sc.platform.core.frontend/src/views/testcase/math/MathEditor.vue
  43. 4
      io.sc.platform.core.frontend/src/views/testcase/route/NoMenuRoute.vue
  44. 12
      io.sc.platform.core.frontend/src/views/testcase/route/OpenNoMenuRoute.vue
  45. 442
      io.sc.platform.core.frontend/src/views/testcase/tag/tag.vue
  46. 53
      io.sc.platform.core.frontend/src/views/testcase/word/Word.vue
  47. 4
      io.sc.platform.core.frontend/template-project/package.json
  48. 14
      io.sc.platform.core.frontend/template-project/src/components/index.ts
  49. 7
      io.sc.platform.core.frontend/template-project/src/i18n/messages.json
  50. 7
      io.sc.platform.core.frontend/template-project/src/i18n/messages_tw_CN.json
  51. 8
      io.sc.platform.core.frontend/template-project/src/i18n/messages_zh_CN.json
  52. 33
      io.sc.platform.core.frontend/template-project/src/menus/menus.json
  53. 61
      io.sc.platform.core.frontend/template-project/src/routes/routes.json
  54. 53
      io.sc.platform.core.frontend/template-project/src/views/testcase/excel/Excel.vue
  55. 5
      io.sc.platform.core.frontend/template-project/src/views/testcase/form/form.vue
  56. 133
      io.sc.platform.core.frontend/template-project/src/views/testcase/math/MathEditor.vue
  57. 4
      io.sc.platform.core.frontend/template-project/src/views/testcase/route/NoMenuRoute.vue
  58. 12
      io.sc.platform.core.frontend/template-project/src/views/testcase/route/OpenNoMenuRoute.vue
  59. 442
      io.sc.platform.core.frontend/template-project/src/views/testcase/tag/tag.vue
  60. 53
      io.sc.platform.core.frontend/template-project/src/views/testcase/word/Word.vue
  61. 7
      io.sc.platform.core.frontend/template-project/webpack.config.common.cjs
  62. 7
      io.sc.platform.core.frontend/webpack.config.common.cjs
  63. 4
      io.sc.platform.core/src/main/java/io/sc/platform/core/DirectoryManager.java
  64. 10
      io.sc.platform.core/src/main/java/io/sc/platform/core/Environment.java
  65. 67
      io.sc.platform.core/src/main/java/io/sc/platform/core/autoconfigure/RestTemplateAutoConfiguration.java
  66. 23
      io.sc.platform.core/src/main/java/io/sc/platform/core/bean/FirstApplicationInitializer.java
  67. 37
      io.sc.platform.core/src/main/java/io/sc/platform/core/util/DateUtil.java
  68. 16
      io.sc.platform.core/src/main/java/io/sc/platform/core/util/InputStreamUtil.java
  69. 23
      io.sc.platform.core/src/main/java/io/sc/platform/core/util/PathUtil.java
  70. 35
      io.sc.platform.core/src/main/java/io/sc/platform/core/util/PinyinUtil.java
  71. 1
      io.sc.platform.core/src/main/resources/META-INF/spring.factories
  72. 3
      io.sc.platform.core/src/main/resources/io/sc/platform/core/i18n/words.properties
  73. 3
      io.sc.platform.core/src/main/resources/io/sc/platform/core/i18n/words_tw_CN.properties
  74. 3
      io.sc.platform.core/src/main/resources/io/sc/platform/core/i18n/words_zh_CN.properties
  75. 108
      io.sc.platform.developer.doc/asciidoc/9999-appendix/oauth2/oauth2.adoc
  76. BIN
      io.sc.platform.developer.doc/asciidoc/resources/images/9999-appendix/oauth2/001.png
  77. BIN
      io.sc.platform.developer.doc/asciidoc/resources/images/9999-appendix/oauth2/002.png
  78. BIN
      io.sc.platform.developer.doc/asciidoc/resources/images/9999-appendix/oauth2/003.png
  79. BIN
      io.sc.platform.developer.doc/asciidoc/resources/images/9999-appendix/oauth2/004.png
  80. BIN
      io.sc.platform.developer.doc/asciidoc/resources/images/9999-appendix/oauth2/005.png
  81. BIN
      io.sc.platform.developer.doc/asciidoc/resources/images/9999-appendix/oauth2/006.png
  82. BIN
      io.sc.platform.developer.doc/asciidoc/resources/images/9999-appendix/oauth2/007.png
  83. BIN
      io.sc.platform.developer.doc/asciidoc/resources/images/9999-appendix/oauth2/008.png
  84. 4
      io.sc.platform.developer.frontend/package.json
  85. 2
      io.sc.platform.developer.frontend/src/components/index.ts
  86. 23
      io.sc.platform.developer.frontend/src/i18n/messages.json
  87. 23
      io.sc.platform.developer.frontend/src/i18n/messages_tw_CN.json
  88. 23
      io.sc.platform.developer.frontend/src/i18n/messages_zh_CN.json
  89. 2
      io.sc.platform.developer.frontend/src/menus/menus.json
  90. 13
      io.sc.platform.developer.frontend/src/routes/routes.json
  91. 1
      io.sc.platform.developer.frontend/src/views/backend/ExportLiquibase.vue
  92. 102
      io.sc.platform.developer.frontend/src/views/backend/sql/Sql.vue
  93. 93
      io.sc.platform.developer.frontend/src/views/backend/sql/import-excel/ImportExcel.vue
  94. 99
      io.sc.platform.developer.frontend/src/views/backend/sql/import-excel/StepDataView.vue
  95. 28
      io.sc.platform.developer.frontend/src/views/backend/sql/import-excel/StepImport.vue
  96. 156
      io.sc.platform.developer.frontend/src/views/backend/sql/import-excel/StepMapping.vue
  97. 51
      io.sc.platform.developer.frontend/src/views/backend/sql/import-excel/StepUploadFile.vue
  98. 2
      io.sc.platform.developer.frontend/src/views/springboot/Bean.vue
  99. 2
      io.sc.platform.developer.frontend/src/views/springboot/Mapping.vue
  100. 7
      io.sc.platform.developer.frontend/webpack.config.common.cjs

1
app.platform.scheduler.manager/build.gradle

@ -12,6 +12,7 @@ dependencies {
dependencies {
implementation (
project(":io.sc.platform.scheduler.manager"),
project(":io.sc.platform.developer"),
)
}

38
app.platform/build.gradle

@ -13,28 +13,30 @@ dependencies {
implementation (
project(":io.sc.platform.app"),
project(":io.sc.platform.developer"),
//project(":io.sc.platform.security.oauth2.server.authorization"),
// project(":io.sc.platform.security.oauth2.server.resource"),
project(":io.sc.platform.security.loginform"),
project(":io.sc.platform.scheduler.manager"),
project(":io.sc.platform.scheduler.executor"),
// project(":io.sc.platform.scheduler.manager"),
// project(":io.sc.platform.scheduler.executor"),
/*
project(":io.sc.engine.mv"),
project(":io.sc.engine.mv.frontend"),
project(":io.sc.engine.mv.sample"),
// project(":io.sc.engine.mv"),
// project(":io.sc.engine.mv.frontend"),
// project(":io.sc.engine.mv.sample"),
//
// project(":io.sc.engine.rule.client"),
// project(":io.sc.engine.rule.client.spring"),
// project(":io.sc.engine.rule.core"),
// project(":io.sc.engine.rule.server"),
// project(":io.sc.engine.rule.sample"),
//
// project(":io.sc.engine.st"),
// project(":io.sc.engine.st.frontend"),
//
// project(":erm"),
// project(":erm.frontend"),
project(":io.sc.engine.rule.client"),
project(":io.sc.engine.rule.client.spring"),
project(":io.sc.engine.rule.core"),
project(":io.sc.engine.rule.server"),
project(":io.sc.engine.rule.sample"),
project(":io.sc.engine.st"),
project(":io.sc.engine.st.frontend"),
project(":erm"),
project(":erm.frontend"),
*/
project(":io.sc.standard"),
)
}

198
app.platform/src/main/java/app/platform/Application.java

@ -2,9 +2,18 @@ package app.platform;
import io.sc.platform.core.ApplicationLauncher;
import io.sc.platform.core.PlatformSpringBootServletInitializer;
import io.sc.platform.core.util.DateUtil;
import io.sc.platform.core.util.FileUtil;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.WebApplicationInitializer;
import java.io.File;
import java.io.FileInputStream;
import java.text.ParseException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 应用程序入口
*/
@ -13,4 +22,193 @@ public class Application extends PlatformSpringBootServletInitializer implements
public static void main(String[] args) throws Exception {
ApplicationLauncher.run(Application.class,args);
}
public static void main2(String[] args) throws Exception {
// ApplicationLauncher.run(Application.class,args);
// if(1==1){
// return;
// }
File dir =new File("/Users/wangshaoping/wspsc/work/yusys/工程项目/宁波银行/rwa/数据资料/安硕/ddl001/table/MODIFYUSER/TABLE");
String[] files =dir.list();
Arrays.sort(files);
StringBuilder result =new StringBuilder();
for(String fileName : files){
if(!fileName.endsWith(".sql")){
continue;
}
StringBuilder sb =new StringBuilder();
String file ="/Users/wangshaoping/wspsc/work/yusys/工程项目/宁波银行/rwa/数据资料/安硕/ddl001/table/MODIFYUSER/TABLE/" + fileName;
String outputFile ="/Users/wangshaoping/wspsc/work/yusys/工程项目/宁波银行/rwa/数据资料/安硕/ddl001/table/MODIFYUSER/output/" + fileName;
List<String> list =FileUtil.readStringAsList(new FileInputStream(file),"UTF-8");
for(String s : list){
String line =s;
line =replace_CONSTRAINT(line);
line =replace_COMPRESS(line);
line =replaceGt4000(line);
line =replace_PARTITION(line);
line =replace_PRIMARY(line);
line =replace_DROP(line);
if(line.trim().equals("")){
if(sb.length()>2) {
sb.setLength(sb.length() - 2);
sb.append(" ");
}
}
sb.append(line).append("\n");
}
sb.setLength(sb.length()-2);
sb.append(";");
//FileUtil.writeString(outputFile,sb.toString(),"UTF-8");
result.append(sb.toString()).append("\n\n");
}
FileUtil.writeString("/Users/wangshaoping/wspsc/work/yusys/工程项目/宁波银行/rwa/数据资料/安硕/ddl001/table/MODIFYUSER/output/result.sql",result.toString(),"UTF-8");
}
public static void main3(String[] args) throws Exception {
File dir =new File("/Users/wangshaoping/wspsc/work/yusys/工程项目/宁波银行/rwa/数据资料/安硕/1716611768596_export_file/MODIFYUSER/TABLE");
String[] files =dir.list();
Arrays.sort(files);
StringBuilder result =new StringBuilder();
for(String fileName : files){
if(!fileName.endsWith(".sql") || "CS_SCRIPT_CONFIG.0.0.sql".equalsIgnoreCase(fileName)
|| "CS_SCRIPT_CONFIG.0.0.sql".equalsIgnoreCase(fileName)){
continue;
}
StringBuilder sb =new StringBuilder();
sb.append("TRUNCATE TABLE ").append(fileName.substring(0,fileName.length()-8)).append(";").append("\n");
String file ="/Users/wangshaoping/wspsc/work/yusys/工程项目/宁波银行/rwa/数据资料/安硕/1716611768596_export_file/MODIFYUSER/TABLE/" + fileName;
String outputFile ="/Users/wangshaoping/wspsc/work/yusys/工程项目/宁波银行/rwa/数据资料/安硕/1716611768596_export_file/MODIFYUSER/output/" + fileName;
List<String> list =FileUtil.readStringAsList(new FileInputStream(file),"UTF-8");
for(String s : list){
String line =s;
line =replace_DATE(line);
line =replace_DATE2(line);
line =replace_COMMIT(line);
sb.append(line).append("\n");
}
result.append(sb.toString()).append("\n\n");
}
FileUtil.writeString("/Users/wangshaoping/wspsc/work/yusys/工程项目/宁波银行/rwa/数据资料/安硕/1716611768596_export_file/MODIFYUSER/output/result.sql",result.toString(),"UTF-8");
}
private static String replace_CONSTRAINT(String str){
Pattern PATTERN =Pattern.compile("(CONSTRAINT\\s(\"(.+?)\") NOT\\sNULL\\sENABLE)");
Matcher matcher =PATTERN.matcher(str);
Map<String,String> founds =new HashMap<String,String>();
while (matcher.find()) {
founds.put(matcher.group(1),matcher.group(2));
}
String result =str;
if(founds!=null && founds.size()>0) {
for(String placeholder : founds.keySet()) {
result =result.replace(placeholder," NOT NULL");
}
}
return result;
}
private static String replace_COMPRESS(String str){
Pattern PATTERN =Pattern.compile("(COMPRESS\\s(.+))");
Matcher matcher =PATTERN.matcher(str);
Map<String,String> founds =new HashMap<String,String>();
while (matcher.find()) {
founds.put(matcher.group(1),"");
}
String result =str;
if(founds!=null && founds.size()>0) {
for(String placeholder : founds.keySet()) {
result =result.replace(placeholder,founds.get(placeholder));
}
}
return result;
}
private static String replace_PRIMARY(String str){
if(str.indexOf("PRIMARY")>-1){
return "";
}
return str;
}
private static String replaceGt4000(String str){
//System.out.println(str);
Pattern PATTERN =Pattern.compile("(VARCHAR2\\((\\d+)\\))");
Matcher matcher =PATTERN.matcher(str);
Map<String,String> founds =new HashMap<String,String>();
while (matcher.find()) {
String number =matcher.group(2);
if(Integer.parseInt(number)>4000){
number ="VARCHAR2(4000)";
}else{
number ="VARCHAR2(" + number + ")";
}
founds.put(matcher.group(1),number);
}
String result =str;
if(founds!=null && founds.size()>0) {
for(String placeholder : founds.keySet()) {
result =result.replace(placeholder,founds.get(placeholder));
}
}
return result;
}
private static String replace_PARTITION(String str){
if(str.indexOf("partition")>-1){
return "";
}
return str;
}
private static String replace_DROP(String str){
if(str.indexOf("DROP")>-1){
//return "";
}
return str;
}
private static String replace_DATE(String str) throws ParseException {
Pattern PATTERN =Pattern.compile("('(\\d{4}\\-\\d{2}\\-\\d{2} \\d{2}:\\d{2}:\\d{2})')");
Matcher matcher =PATTERN.matcher(str);
Map<String,String> founds =new HashMap<String,String>();
while (matcher.find()) {
Date d = DateUtil.parseDate(matcher.group(2));
founds.put(matcher.group(1),"TO_TIMESTAMP('" + matcher.group(2) + ":0','YYYY-MM-DD HH24:MI:SS:FF')");
}
String result =str;
if(founds!=null && founds.size()>0) {
for(String placeholder : founds.keySet()) {
result =result.replace(placeholder,"null");
}
}
return result;
}
private static String replace_DATE2(String str) throws ParseException {
Pattern PATTERN =Pattern.compile("('(\\d{4}\\-\\d{2}\\-\\d{2} \\d{2}:\\d{2}:\\d{2}\\.\\d{3})')");
Matcher matcher =PATTERN.matcher(str);
Map<String,String> founds =new HashMap<String,String>();
while (matcher.find()) {
Date d = DateUtil.parseDate(matcher.group(2));
founds.put(matcher.group(1),"TO_TIMESTAMP('" + matcher.group(2) + ":0','YYYY-MM-DD HH24:MI:SS:FF')");
}
String result =str;
if(founds!=null && founds.size()>0) {
for(String placeholder : founds.keySet()) {
result =result.replace(placeholder,"null");
}
}
return result;
}
private static String replace_COMMIT(String str){
if(str.indexOf("commit")>-1){
return "";
}
return str;
}
}

2263
app.platform/wsp.txt

File diff suppressed because it is too large

406
build-version.gradle

@ -1,201 +1,211 @@
/***********************************************************************
*
**********************************************************************/
ext['PlatformDependencyVersions'] =[
"commons-io:commons-io" : "${commons_io_version}",
"org.ow2.asm:asm" : "${asm_version}",
"org.checkerframework:checker-qual" : "${checker_version}",
"com.google.guava:guava" : "${guava_version}",
"com.nimbusds:nimbus-jose-jwt" : "9.22",
];
/***********************************************************************
* springboot
**********************************************************************/
subprojects {
// ext['activemq.version'] =
// ext['antlr2.version'] =
// ext['appengine-sdk.version'] =
// ext['artemis.version'] =
// ext['aspectj.version'] =
// ext['assertj.version'] =
// ext['atomikos.version'] =
// ext['awaitility.version'] =
// ext['bitronix.version'] =
// ext['build-helper-maven-plugin.version'] =
// ext['byte-buddy.version'] =
// ext['caffeine.version'] =
// ext['cassandra-driver.version'] =
// ext['classmate.version'] =
// ext['commons-codec.version'] =
// ext['commons-dbcp2.version'] =
// ext['commons-lang3.version'] =
// ext['commons-pool.version'] =
// ext['commons-pool2.version'] =
// ext['couchbase-client.version'] =
// ext['db2-jdbc.version'] =
// ext['dependency-management-plugin.version'] =
// ext['derby.version'] =
// ext['dropwizard-metrics.version'] =
// ext['ehcache.version'] =
// ext['ehcache3.version'] =
// ext['elasticsearch.version'] =
// ext['embedded-mongo.version'] =
// ext['flyway.version'] =
// ext['freemarker.version'] =
// ext['git-commit-id-plugin.version'] =
// ext['glassfish-el.version'] =
// ext['glassfish-jaxb.version'] =
// ext['groovy.version'] =
// ext['gson.version'] =
// ext['h2.version'] =
// ext['hamcrest.version'] =
// ext['hazelcast.version'] =
// ext['hazelcast-hibernate5.version'] =
// ext['hibernate.version'] =
// ext['hibernate-validator.version'] =
// ext['hikaricp.version'] =
// ext['hsqldb.version'] =
// ext['htmlunit.version'] =
// ext['httpasyncclient.version'] =
// ext['httpclient.version'] =
// ext['httpcore.version'] =
// ext['infinispan.version'] =
// ext['influxdb-java.version'] =
// ext['jackson-bom.version'] =
// ext['jakarta-activation.version'] =
// ext['jakarta-annotation.version'] =
// ext['jakarta-jms.version'] =
// ext['jakarta-json.version'] =
// ext['jakarta-json-bind.version'] =
// ext['jakarta-mail.version'] =
// ext['jakarta-persistence.version'] =
// ext['jakarta-servlet.version'] =
// ext['jakarta-servlet-jsp-jstl.version'] =
// ext['jakarta-transaction.version'] =
// ext['jakarta-validation.version'] =
// ext['jakarta-websocket.version'] =
// ext['jakarta-ws-rs.version'] =
// ext['jakarta-xml-bind.version'] =
// ext['jakarta-xml-soap.version'] =
// ext['jakarta-xml-ws.version'] =
// ext['janino.version'] =
// ext['javax-activation.version'] =
// ext['javax-annotation.version'] =
// ext['javax-cache.version'] =
// ext['javax-jaxb.version'] =
// ext['javax-jaxws.version'] =
// ext['javax-jms.version'] =
// ext['javax-json.version'] =
// ext['javax-jsonb.version'] =
// ext['javax-mail.version'] =
// ext['javax-money.version'] =
// ext['javax-persistence.version'] =
// ext['javax-transaction.version'] =
// ext['javax-validation.version'] =
// ext['javax-websocket.version'] =
// ext['jaxen.version'] =
// ext['jaybird.version'] =
// ext['jboss-logging.version'] =
// ext['jboss-transaction-spi.version'] =
// ext['jdom2.version'] =
// ext['jedis.version'] =
// ext['jersey.version'] =
// ext['jetty.version'] =
// ext['jetty-el.version'] =
// ext['jetty-jsp.version'] =
// ext['jetty-reactive-httpclient.version'] =
// ext['jmustache.version'] =
// ext['johnzon.version'] =
// ext['jolokia.version'] =
// ext['jooq.version'] =
// ext['json-path.version'] =
// ext['json-smart.version'] =
// ext['jsonassert.version'] =
// ext['jstl.version'] =
// ext['jtds.version'] =
// ext['junit.version'] =
// ext['junit-jupiter.version'] =
// ext['kafka.version'] =
// ext['kotlin.version'] =
// ext['kotlin-coroutines.version'] =
// ext['lettuce.version'] =
// ext['liquibase.version'] =
// ext['log4j2.version'] =
// ext['logback.version'] =
// ext['lombok.version'] =
// ext['mariadb.version'] =
// ext['maven-antrun-plugin.version'] =
// ext['maven-assembly-plugin.version'] =
// ext['maven-clean-plugin.version'] =
// ext['maven-compiler-plugin.version'] =
// ext['maven-dependency-plugin.version'] =
// ext['maven-deploy-plugin.version'] =
// ext['maven-enforcer-plugin.version'] =
// ext['maven-failsafe-plugin.version'] =
// ext['maven-help-plugin.version'] =
// ext['maven-install-plugin.version'] =
// ext['maven-invoker-plugin.version'] =
// ext['maven-jar-plugin.version'] =
// ext['maven-javadoc-plugin.version'] =
// ext['maven-resources-plugin.version'] =
// ext['maven-shade-plugin.version'] =
// ext['maven-source-plugin.version'] =
// ext['maven-surefire-plugin.version'] =
// ext['maven-war-plugin.version'] =
// ext['micrometer.version'] =
// ext['mimepull.version'] =
// ext['mockito.version'] =
// ext['mongodb.version'] =
// ext['mssql-jdbc.version'] =
// ext['mysql.version'] =
// ext['nekohtml.version'] =
// ext['neo4j-java-driver.version'] =
// ext['netty.version'] =
// ext['netty-tcnative.version'] =
// ext['nimbus-jose-jwt.version'] =
// ext['oauth2-oidc-sdk.version'] =
// ext['ojdbc.version'] =
// ext['okhttp3.version'] =
// ext['oracle-database.version'] ='12.2.0.1'
// ext['pooled-jms.version'] =
// ext['postgresql.version'] =
// ext['prometheus-pushgateway.version'] =
// ext['quartz.version'] =
// ext['querydsl.version'] =
// ext['r2dbc-bom.version'] =
// ext['rabbit-amqp-client.version'] =
// ext['reactive-streams.version'] =
// ext['reactor-bom.version'] =
// ext['rest-assured.version'] =
// ext['rsocket.version'] =
// ext['rxjava.version'] =
// ext['rxjava-adapter.version'] =
// ext['rxjava2.version'] =
// ext['saaj-impl.version'] =
// ext['selenium.version'] =
// ext['selenium-htmlunit.version'] =
// ext['sendgrid.version'] =
// ext['servlet-api.version'] =
// ext['slf4j.version'] =
// ext['snakeyaml.version'] =
// ext['solr.version'] =
// ext['spring-amqp.version'] =
// ext['spring-batch.version'] =
// ext['spring-data-bom.version'] =
// ext['spring-framework.version'] =
// ext['spring-hateoas.version'] =
// ext['spring-integration.version'] =
// ext['spring-kafka.version'] =
// ext['spring-ldap.version'] =
// ext['spring-restdocs.version'] =
// ext['spring-retry.version'] =
// ext['spring-security.version'] =
// ext['spring-session-bom.version'] =
// ext['spring-ws.version'] =
// ext['sqlite-jdbc.version'] =
// ext['sun-mail.version'] =
// ext['thymeleaf.version'] =
// ext['thymeleaf-extras-data-attribute.version'] =
// ext['thymeleaf-extras-java8time.version'] =
// ext['thymeleaf-extras-springsecurity.version'] =
// ext['thymeleaf-layout-dialect.version'] =
// ext['tomcat.version'] =
// ext['unboundid-ldapsdk.version'] =
// ext['undertow.version'] =
// ext['versions-maven-plugin.version'] =
// ext['webjars-hal-browser.version'] =
// ext['webjars-locator-core.version'] =
// ext['wsdl4j.version'] =
// ext['xml-maven-plugin.version'] =
// ext['xmlunit2.version'] =
}
// ext['groovy.version'] ='3.0.17'
// ext['activemq.version'] =
// ext['antlr2.version'] =
// ext['appengine-sdk.version'] =
// ext['artemis.version'] =
// ext['aspectj.version'] =
// ext['assertj.version'] =
// ext['atomikos.version'] =
// ext['awaitility.version'] =
// ext['bitronix.version'] =
// ext['build-helper-maven-plugin.version'] =
// ext['byte-buddy.version'] =
// ext['caffeine.version'] =
// ext['cassandra-driver.version'] =
// ext['classmate.version'] =
// ext['commons-codec.version'] =
// ext['commons-dbcp2.version'] =
// ext['commons-lang3.version'] =
// ext['commons-pool.version'] =
// ext['commons-pool2.version'] =
// ext['couchbase-client.version'] =
// ext['db2-jdbc.version'] =
// ext['dependency-management-plugin.version'] =
// ext['derby.version'] =
// ext['dropwizard-metrics.version'] =
// ext['ehcache.version'] =
// ext['ehcache3.version'] =
// ext['elasticsearch.version'] =
// ext['embedded-mongo.version'] =
// ext['flyway.version'] =
// ext['freemarker.version'] =
// ext['git-commit-id-plugin.version'] =
// ext['glassfish-el.version'] =
// ext['glassfish-jaxb.version'] =
// ext['groovy.version'] =
// ext['gson.version'] =
// ext['h2.version'] =
// ext['hamcrest.version'] =
// ext['hazelcast.version'] =
// ext['hazelcast-hibernate5.version'] =
// ext['hibernate.version'] =
// ext['hibernate-validator.version'] =
// ext['hikaricp.version'] =
// ext['hsqldb.version'] =
// ext['htmlunit.version'] =
// ext['httpasyncclient.version'] =
// ext['httpclient.version'] =
// ext['httpcore.version'] =
// ext['infinispan.version'] =
// ext['influxdb-java.version'] =
// ext['jackson-bom.version'] =
// ext['jakarta-activation.version'] =
// ext['jakarta-annotation.version'] =
// ext['jakarta-jms.version'] =
// ext['jakarta-json.version'] =
// ext['jakarta-json-bind.version'] =
// ext['jakarta-mail.version'] =
// ext['jakarta-persistence.version'] =
// ext['jakarta-servlet.version'] =
// ext['jakarta-servlet-jsp-jstl.version'] =
// ext['jakarta-transaction.version'] =
// ext['jakarta-validation.version'] =
// ext['jakarta-websocket.version'] =
// ext['jakarta-ws-rs.version'] =
// ext['jakarta-xml-bind.version'] =
// ext['jakarta-xml-soap.version'] =
// ext['jakarta-xml-ws.version'] =
// ext['janino.version'] =
// ext['javax-activation.version'] =
// ext['javax-annotation.version'] =
// ext['javax-cache.version'] =
// ext['javax-jaxb.version'] =
// ext['javax-jaxws.version'] =
// ext['javax-jms.version'] =
// ext['javax-json.version'] =
// ext['javax-jsonb.version'] =
// ext['javax-mail.version'] =
// ext['javax-money.version'] =
// ext['javax-persistence.version'] =
// ext['javax-transaction.version'] =
// ext['javax-validation.version'] =
// ext['javax-websocket.version'] =
// ext['jaxen.version'] =
// ext['jaybird.version'] =
// ext['jboss-logging.version'] =
// ext['jboss-transaction-spi.version'] =
// ext['jdom2.version'] =
// ext['jedis.version'] =
// ext['jersey.version'] =
// ext['jetty.version'] =
// ext['jetty-el.version'] =
// ext['jetty-jsp.version'] =
// ext['jetty-reactive-httpclient.version'] =
// ext['jmustache.version'] =
// ext['johnzon.version'] =
// ext['jolokia.version'] =
// ext['jooq.version'] =
// ext['json-path.version'] =
// ext['json-smart.version'] =
// ext['jsonassert.version'] =
// ext['jstl.version'] =
// ext['jtds.version'] =
// ext['junit.version'] =
// ext['junit-jupiter.version'] =
// ext['kafka.version'] =
// ext['kotlin.version'] =
// ext['kotlin-coroutines.version'] =
// ext['lettuce.version'] =
// ext['liquibase.version'] =
// ext['log4j2.version'] =
// ext['logback.version'] =
// ext['lombok.version'] =
// ext['mariadb.version'] =
// ext['maven-antrun-plugin.version'] =
// ext['maven-assembly-plugin.version'] =
// ext['maven-clean-plugin.version'] =
// ext['maven-compiler-plugin.version'] =
// ext['maven-dependency-plugin.version'] =
// ext['maven-deploy-plugin.version'] =
// ext['maven-enforcer-plugin.version'] =
// ext['maven-failsafe-plugin.version'] =
// ext['maven-help-plugin.version'] =
// ext['maven-install-plugin.version'] =
// ext['maven-invoker-plugin.version'] =
// ext['maven-jar-plugin.version'] =
// ext['maven-javadoc-plugin.version'] =
// ext['maven-resources-plugin.version'] =
// ext['maven-shade-plugin.version'] =
// ext['maven-source-plugin.version'] =
// ext['maven-surefire-plugin.version'] =
// ext['maven-war-plugin.version'] =
// ext['micrometer.version'] =
// ext['mimepull.version'] =
// ext['mockito.version'] =
// ext['mongodb.version'] =
// ext['mssql-jdbc.version'] =
// ext['mysql.version'] =
// ext['nekohtml.version'] =
// ext['neo4j-java-driver.version'] =
// ext['netty.version'] =
// ext['netty-tcnative.version'] =
// ext['nimbus-jose-jwt.version'] =
// ext['oauth2-oidc-sdk.version'] =
// ext['ojdbc.version'] =
// ext['okhttp3.version'] =
// ext['oracle-database.version'] ='12.2.0.1'
// ext['pooled-jms.version'] =
// ext['postgresql.version'] =
// ext['prometheus-pushgateway.version'] =
// ext['quartz.version'] =
// ext['querydsl.version'] =
// ext['r2dbc-bom.version'] =
// ext['rabbit-amqp-client.version'] =
// ext['reactive-streams.version'] =
// ext['reactor-bom.version'] =
// ext['rest-assured.version'] =
// ext['rsocket.version'] =
// ext['rxjava.version'] =
// ext['rxjava-adapter.version'] =
// ext['rxjava2.version'] =
// ext['saaj-impl.version'] =
// ext['selenium.version'] =
// ext['selenium-htmlunit.version'] =
// ext['sendgrid.version'] =
// ext['servlet-api.version'] =
// ext['slf4j.version'] =
// ext['snakeyaml.version'] =
// ext['solr.version'] =
// ext['spring-amqp.version'] =
// ext['spring-batch.version'] =
// ext['spring-data-bom.version'] =
// ext['spring-framework.version'] =
// ext['spring-hateoas.version'] =
// ext['spring-integration.version'] =
// ext['spring-kafka.version'] =
// ext['spring-ldap.version'] =
// ext['spring-restdocs.version'] =
// ext['spring-retry.version'] =
// ext['spring-security.version'] =
// ext['spring-session-bom.version'] =
// ext['spring-ws.version'] =
// ext['sqlite-jdbc.version'] =
// ext['sun-mail.version'] =
// ext['thymeleaf.version'] =
// ext['thymeleaf-extras-data-attribute.version'] =
// ext['thymeleaf-extras-java8time.version'] =
// ext['thymeleaf-extras-springsecurity.version'] =
// ext['thymeleaf-layout-dialect.version'] =
// ext['tomcat.version'] =
// ext['unboundid-ldapsdk.version'] =
// ext['undertow.version'] =
// ext['versions-maven-plugin.version'] =
// ext['webjars-hal-browser.version'] =
// ext['webjars-locator-core.version'] =
// ext['wsdl4j.version'] =
// ext['xml-maven-plugin.version'] =
// ext['xmlunit2.version'] =

27
build.gradle

@ -1,3 +1,5 @@
import org.gradle.api.artifacts.DependencyResolveDetails
apply from: "build-version.gradle"
def isFrontendProject(currentDir){
@ -48,10 +50,18 @@ subprojects {
//resolutionStrategy.cacheChangingModulesFor 0, 'seconds' //: 1.1.2
//
exclude group: "org.apache.logging.log4j", module: "log4j-api"
exclude group: "org.apache.logging.log4j", module: "log4j-to-slf4j"
exclude group: "org.slf4j", module: "slf4j-jdk14"
// exclude group: "org.apache.logging.log4j", module: "log4j-api"
// exclude group: "org.apache.logging.log4j", module: "log4j-to-slf4j"
// exclude group: "org.slf4j", module: "slf4j-jdk14"
exclude group: "org.slf4j", module: "slf4j-nop"
resolutionStrategy.eachDependency { DependencyResolveDetails detail ->
def requested =detail.requested;
def groupAndName =requested.group + ":" + requested.name;
if(PlatformDependencyVersions[groupAndName]!=null){
detail.useVersion(PlatformDependencyVersions[groupAndName]);
}
}
}
dependencyManagement {
@ -672,7 +682,16 @@ subprojects {
processResources {
if(isFrontendProject(file('.'))) {
exclude("**/${project.name}/*.*")
exclude("**/${project.name}/*.*");
if(project.name!='io.sc.platform.mvc.frontend' && project.name!='io.sc.platform.security.frontend'){
// exclude("**/${project.name}/javascript/codemirror.*");
// exclude("**/${project.name}/javascript/echarts.*");
// exclude("**/${project.name}/javascript/platform-core.*");
// exclude("**/${project.name}/javascript/quasar.*");
// exclude("**/${project.name}/javascript/vue.*");
// exclude("**/${project.name}/fonts/*.*");
// exclude("**/${project.name}/webjars/**/*.*");
}
}
}
}

4
erm.frontend/package.json

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

7
erm.frontend/webpack.config.common.cjs

@ -154,6 +154,11 @@ module.exports = {
'@': path.resolve(__dirname, 'src'),
},
// 设置支持的模块扩展名,即这些扩展名的文件可以作为模块被使用
extensions: ['.ts', '.js', '.cjs', '.vue']
extensions: ['.ts', '.js', '.cjs', '.vue'],
fallback: {
"fs": false,
"os": false,
"path": false,
}
},
};

1
erm/src/main/resources/liquibase/kpi/erm.kpi_1.0.0_20221020__ERM KPI Database Schema DDL.xml

@ -69,7 +69,6 @@
<column name="CORP_CODE_" type="NVARCHAR(255)" remarks="所属法人代码"/>
</createTable>
<addUniqueConstraint tableName="RP_LIB" columnNames="CODE_,CORP_CODE_"/>
<addNotNullConstraint columnName="CODE_" columnDataType="NVARCHAR(255)" tableName="RP_LIB" constraintName="CONST_RP_LIB_CODE"/>
<addNotNullConstraint columnName="NAME_" columnDataType="NVARCHAR(255)" tableName="RP_LIB" constraintName="CONST_RP_LIB_NAME"/>
<addNotNullConstraint columnName="ENABLE_" columnDataType="SMALLINT" tableName="RP_LIB" constraintName="CONST_RP_LIB_ENABLE"/>
<addNotNullConstraint columnName="DATA_COME_FROM_" columnDataType="NVARCHAR(10)" tableName="RP_LIB" constraintName="CONST_RP_LIB_DCF"/>

9
gradle.properties

@ -36,16 +36,18 @@ application_version=1.0.0
# platform
###########################################################
platform_group=io.sc
platform_version=8.1.40
platform_version=8.1.43
platform_plugin_version=8.1.13
platform_core_frontend_version=8.1.216
platform_core_frontend_version=8.1.219
###########################################################
# dependencies version
###########################################################
asciidoctor_version=3.3.2
asm_version=9.7
checker_version=3.43.0
commons_fileupload_version=1.4
commons_io_version=2.11.0
commons_io_version=2.16.1
cxf_version=3.2.7
dm_hibernate_version=8.1.2.192
flowable_version=6.8.0
@ -62,7 +64,6 @@ jxls_jexcel_version=1.0.7
jxls_poi_version=1.0.15
jxls_version=2.4.6
mybatis_version=3.5.10
ooxml_schemas_version=1.4
opencsv_version=5.7.1
oshi_version=6.4.2
p6spy_version=3.9.1

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

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

7
io.sc.engine.mv.frontend/webpack.config.common.cjs

@ -154,6 +154,11 @@ module.exports = {
'@': path.resolve(__dirname, 'src'),
},
// 设置支持的模块扩展名,即这些扩展名的文件可以作为模块被使用
extensions: ['.ts', '.js', '.cjs', '.vue']
extensions: ['.ts', '.js', '.cjs', '.vue'],
fallback: {
"fs": false,
"os": false,
"path": false,
}
},
};

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

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

7
io.sc.engine.rule.frontend/webpack.config.common.cjs

@ -154,6 +154,11 @@ module.exports = {
'@': path.resolve(__dirname, 'src'),
},
// 设置支持的模块扩展名,即这些扩展名的文件可以作为模块被使用
extensions: ['.ts', '.js', '.cjs', '.vue']
extensions: ['.ts', '.js', '.cjs', '.vue'],
fallback: {
"fs": false,
"os": false,
"path": false,
}
},
};

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

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

7
io.sc.engine.st.frontend/webpack.config.common.cjs

@ -154,6 +154,11 @@ module.exports = {
'@': path.resolve(__dirname, 'src'),
},
// 设置支持的模块扩展名,即这些扩展名的文件可以作为模块被使用
extensions: ['.ts', '.js', '.cjs', '.vue']
extensions: ['.ts', '.js', '.cjs', '.vue'],
fallback: {
"fs": false,
"os": false,
"path": false,
}
},
};

7
io.sc.platform.app/build.gradle

@ -2,17 +2,14 @@ dependencies {
api(
project(":io.sc.platform.communication"),
project(":io.sc.platform.csv"),
project(":io.sc.platform.flowable"),
//project(":io.sc.platform.flowable"),
project(":io.sc.platform.groovy"),
project(":io.sc.platform.jdbc.liquibase"),
project(":io.sc.platform.jdbc.schemacrawler"),
// project(":io.sc.platform.scheduler.core"),
// project(":io.sc.platform.scheduler.executor"),
// project(":io.sc.platform.scheduler.manager"),
// project(":io.sc.platform.scheduler.manager.frontend"),
project(":io.sc.platform.lcdp"),
project(":io.sc.platform.lcdp.frontend"),
project(":io.sc.platform.orm.mybatis"),
project(":io.sc.platform.poi"),
project(":io.sc.platform.system"),
project(":io.sc.platform.ws.cxf"),
project(":org.webjars.luckysheet-2.1.13"),

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

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

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

@ -2,8 +2,12 @@
* ,
*/
import component_testcase_tag from '@/views/testcase/tag/tag.vue';
import component_testcase_formElements from '@/views/FormElements.vue';
import component_testcase_openNoMenuRoute from '@/views/testcase/route/OpenNoMenuRoute.vue';
import component_testcase_noMenuRoute from '@/views/testcase/route/NoMenuRoute.vue';
import component_testcase_mathEditor from '@/views/testcase/math/MathEditor.vue';
import component_testcase_form from '@/views/testcase/form/form.vue';
import component_testcase_excel from '@/views/testcase/excel/Excel.vue';
import component_testcase_word from '@/views/testcase/word/Word.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';
@ -15,8 +19,12 @@ import component_testcase_gridLayout from '@/views/likm/GridLayout.vue';
import component_testcase_likmTreeGrid from '@/views/likm/TreeGrid.vue';
const localComponents = {
'component.testcase.tag': component_testcase_tag,
'component.testcase.formElements': component_testcase_formElements,
'component.testcase.openNoMenuRoute': component_testcase_openNoMenuRoute,
'component.testcase.noMenuRoute': component_testcase_noMenuRoute,
'component.testcase.mathEditor': component_testcase_mathEditor,
'component.testcase.form': component_testcase_form,
'component.testcase.excel': component_testcase_excel,
'component.testcase.word': component_testcase_word,
'component.testcase.likmDialog': component_testcase_likmDialog,
'component.testcase.likmDrawer': component_testcase_likmDrawer,
'component.testcase.likmForm': component_testcase_likmForm,

8
io.sc.platform.core.frontend/src/i18n/messages.json

@ -1,4 +1,10 @@
{
"menu.testcase": "Test Case",
"menu.testcase.formElements": "Form Elements"
"menu.testcase.openNoMenuRoute": "Open No Menu Route",
"menu.testcase.mathEditor": "Math Formual Editor",
"menu.testcase.form":"Form Element",
"menu.testcase.excel": "Excel",
"menu.testcase.word": "Word",
"route.testcase.noMenuRoute":"No Menu Route"
}

8
io.sc.platform.core.frontend/src/i18n/messages_tw_CN.json

@ -1,4 +1,10 @@
{
"menu.testcase": "測試用例",
"menu.testcase.formElements": "表單控件"
"menu.testcase.openNoMenuRoute": "打開無關聯菜單的路由",
"menu.testcase.mathEditor": "數學公式編輯器",
"menu.testcase.form":"表單元素",
"menu.testcase.excel": "Excel",
"menu.testcase.word": "Word",
"route.testcase.noMenuRoute":"無關聯菜單路由"
}

9
io.sc.platform.core.frontend/src/i18n/messages_zh_CN.json

@ -1,5 +1,10 @@
{
"menu.testcase": "测试用例",
"menu.testcase.tag": "Tag",
"menu.testcase.formElements": "表单控件{name}",
"menu.testcase.openNoMenuRoute": "打开无关联菜单的路由示例",
"menu.testcase.mathEditor": "数学公式编辑器",
"menu.testcase.form":"表单元素",
"menu.testcase.excel": "Excel",
"menu.testcase.word": "Word",
"route.testcase.noMenuRoute":"无关联菜单路由"
}

42
io.sc.platform.core.frontend/src/menus/menus.json

@ -22,20 +22,48 @@
"type": "ROUTE",
"order": 100,
"parentId": "menu.testcase",
"id": "menu.testcase.tag",
"titleI18nKey": "menu.testcase.tag",
"id": "menu.testcase.openNoMenuRoute",
"titleI18nKey": "menu.testcase.openNoMenuRoute",
"icon": "bi-palette",
"routeName": "route.testcase.tag"
"routeName": "route.testcase.openNoMenuRoute"
},
{
"type": "ROUTE",
"order": 100,
"order": 200,
"parentId": "menu.testcase",
"id": "menu.testcase.formElements",
"titleI18nKey": "menu.testcase.formElements",
"id": "menu.testcase.mathEditor",
"titleI18nKey": "menu.testcase.mathEditor",
"icon": "bi-palette",
"routeName": "route.testcase.formElements"
"routeName": "route.testcase.mathEditor"
},
{
"type": "ROUTE",
"order": 300,
"parentId": "menu.testcase",
"id": "menu.testcase.form",
"titleI18nKey": "menu.testcase.form",
"icon": "bi-palette",
"routeName": "route.testcase.form"
},
{
"type": "ROUTE",
"order": 400,
"parentId": "menu.testcase",
"id": "menu.testcase.excel",
"titleI18nKey": "menu.testcase.excel",
"icon": "bi-palette",
"routeName": "route.testcase.excel"
},
{
"type": "ROUTE",
"order": 500,
"parentId": "menu.testcase",
"id": "menu.testcase.word",
"titleI18nKey": "menu.testcase.word",
"icon": "bi-palette",
"routeName": "route.testcase.word"
},
{ "type": "GROUP", "order": 30000, "id": "menu.testcase.likm", "titleI18nKey": "测试用例-likm", "icon": "home" },
{
"type": "ROUTE",

3
io.sc.platform.core.frontend/src/platform/layout/LeftMenuLayout.vue

@ -3,13 +3,12 @@
<q-header>
<Topper></Topper>
</q-header>
<q-drawer
v-model="isDrawerShow"
behavior="desktop"
:side="$gc.theme.sider.position"
:width="$gc.theme.sider.width"
:mini="!$gr.isSiderExpaned"
:mini="!$gc.theme.sider.isSiderExpaned"
:mini-width="$gc.theme.sider.miniWidth"
:style="{
color: $gc.theme.sider.color,

2
io.sc.platform.core.frontend/src/platform/layout/MobileLayout.vue

@ -4,7 +4,7 @@
<Topper></Topper>
</q-header>
<q-drawer v-model="$gr.isSiderExpaned" side="left" :width="gc.theme.sider.width" :breakpoint="799" class="sider-menu" bordered>
<q-drawer v-model="$gc.theme.sider.isSiderExpaned" side="left" :width="gc.theme.sider.width" :breakpoint="799" class="sider-menu" bordered>
<Sider></Sider>
</q-drawer>

14
io.sc.platform.core.frontend/src/platform/layout/WBasicLayout.vue

@ -4,26 +4,18 @@
<script setup lang="ts">
import type { ILayoutProvider } from '@/platform';
import { computed, onBeforeMount } from 'vue';
import { useQuasar } from 'quasar';
import { computed } from 'vue';
import { PConst, Environment } from '@/platform';
import MobileLayout from './MobileLayout.vue';
import LeftMenuLayout from './LeftMenuLayout.vue';
import TopMenuLayout from './TopMenuLayout.vue';
const q = useQuasar();
class FrameworkLayoutProvider implements ILayoutProvider {
getName() {
return PConst.MAIN_LAYOUT_PROVIDER_NAME;
}
getLayout() {
// if (q.screen.lt.md) {
// return MobileLayout;
// }
if (Environment.getConfigure().theme.navigateMenuPosition === 'sider') {
return LeftMenuLayout;
} else {
@ -40,7 +32,5 @@ const getLayoutComponent = computed(() => {
return null;
});
onBeforeMount(() => {
Environment.registLayoutProvider(new FrameworkLayoutProvider());
});
Environment.registLayoutProvider(new FrameworkLayoutProvider());
</script>

18
io.sc.platform.core.frontend/src/platform/layout/sub-layout/Main.vue

@ -20,15 +20,15 @@
'background-color': $gc.theme.main.bgColor,
}"
>
<router-view v-slot="{ Component }" class="full">
<keep-alive>
<suspense>
<w-v-expand-div fixed expand="main">
<w-v-expand-div fixed expand="main">
<router-view v-slot="{ Component }" class="full">
<keep-alive>
<suspense>
<component :is="Component" />
</w-v-expand-div>
</suspense>
</keep-alive>
</router-view>
</suspense>
</keep-alive>
</router-view>
</w-v-expand-div>
</div>
<q-page-sticky
@ -72,7 +72,7 @@
:name="tagView.menuId"
:icon="tagView.menuIcon"
:label="$t(tagView.menuTitleI18nKey)"
:to="{ name: tagView.routeName, query: tagView.routeQuery }"
:to="{ name: tagView.routeName, query: tagView.routeQuery, params: tagView.params }"
no-caps
>
<q-btn

4
io.sc.platform.core.frontend/src/platform/layout/sub-layout/Sider.vue

@ -3,10 +3,10 @@
:style="{
padding: '0px',
height: $q.screen.height - $gc.theme.topper.height - 2 + 'px',
'max-width': $gr.isSiderExpaned ? $gc.theme.sider.width + 'px' : $gc.theme.sider.miniWidth + 'px',
'max-width': $gc.theme.sider.isSiderExpaned ? $gc.theme.sider.width + 'px' : $gc.theme.sider.miniWidth + 'px',
}"
>
<ExpansionMenuItem v-if="$gr.isSiderExpaned" :menus="menus"></ExpansionMenuItem>
<ExpansionMenuItem v-if="$gc.theme.sider.isSiderExpaned" :menus="menus"></ExpansionMenuItem>
<IconMenuItem v-else :menus="menus"></IconMenuItem>
</q-scroll-area>
</template>

4
io.sc.platform.core.frontend/src/platform/layout/sub-layout/SiderCollapser.vue

@ -6,11 +6,11 @@
:size="$gc.theme.tagViewBar.collapserIconSize + 'px'"
padding="0px 5px"
:icon="getCollapserIcon"
:title="$gr.isSiderExpaned ? t('collapseSiderMenu') : t('expandSiderMenu')"
:title="$gc.theme.sider.isSiderExpaned ? t('collapseSiderMenu') : t('expandSiderMenu')"
:style="{
color: $gc.theme.tagViewBar.collapserIconColor,
}"
@click="$gr.isSiderExpaned = !$gr.isSiderExpaned"
@click="$gc.theme.sider.isSiderExpaned = !$gc.theme.sider.isSiderExpaned"
>
</q-btn>
</template>

1
io.sc.platform.core.frontend/src/platform/plugin/environment/default-configure.json

@ -56,6 +56,7 @@
},
"sider" : {
"position" : "left",
"isSiderExpaned": false,
"dense" : true,
"border" : true,
"width" : 300,

19
io.sc.platform.core.frontend/src/platform/plugin/environment/index.ts

@ -32,16 +32,6 @@ window[PConst.APP][PConst.LAYOUT_PROVIDERS] = window[PConst.APP][PConst.LAYOUT_P
console.debug('Window.APP was initialized', window[PConst.APP]);
/**
* ----------------------------------------------------------------------------
*
* ----------------------------------------------------------------------------
*/
const gReactive: GlobalReactiveType = <GlobalReactiveType>window[PConst.APP][PConst.REACTIVE];
gReactive.isSiderExpaned = gReactive.isSiderExpaned || true; // 是否侧边栏处于展开状态
gReactive.selectedTagView = gReactive.selectedTagView || ''; // 当前选择的 TagView ID
const gr = reactive(gReactive);
/**
* ----------------------------------------------------------------------------
*
@ -52,6 +42,15 @@ configure.webContextPath = Tools.removeUrlSuffixSlash(configure.webContextPath)
Tools.mergeObject(defaultConfigure, configure);
const gc = <ConfigureType>reactive(defaultConfigure);
/**
* ----------------------------------------------------------------------------
*
* ----------------------------------------------------------------------------
*/
const gReactive: GlobalReactiveType = <GlobalReactiveType>window[PConst.APP][PConst.REACTIVE];
gReactive.selectedTagView = gReactive.selectedTagView || ''; // 当前选择的 TagView ID
const gr = reactive(gReactive);
/**
*
*/

1
io.sc.platform.core.frontend/src/platform/plugin/manager/ApplicationInitializer.ts

@ -15,7 +15,6 @@ import {
import { Tools, JavascriptLoader } from '@/platform/utils';
const gc = Environment.getConfigure();
const dgc = Environment.getDefaultConfigure();
/**
*

17
io.sc.platform.core.frontend/src/platform/plugin/manager/RouterManager.ts

@ -11,6 +11,7 @@ import { Tools } from '@/platform';
class RouterManager {
static #platformRoutes: any[] = PLATFORM_ROUTES;
static #localRoutes: FrontEndRouteType[];
static #mergedArray: object[];
/**
*
@ -28,6 +29,19 @@ class RouterManager {
RouterManager.#localRoutes = localRoutes;
}
/**
*
* @param name
*/
public static getRouteByName(name: string) {
for (const route of RouterManager.#mergedArray) {
if (route.name === name) {
return route;
}
}
return null;
}
/**
*
* @param routes
@ -74,6 +88,7 @@ class RouterManager {
RouterManager.createRoutes(mergedArray, RouterManager.#localRoutes, mergedMap, 'frontend-local');
RouterManager.createRoutes(mergedArray, routes, mergedMap, 'backend');
RouterManager.#mergedArray = mergedArray;
// 将合并后的路由添加到路由器中
for (const route of mergedArray) {
const parentRouteName = route['parent'];
@ -140,6 +155,7 @@ class RouterManager {
parent: cachedRoute['parent'],
name: cachedRoute['name'],
path: cachedRoute['path'],
icon: cachedRoute['icon'],
component: cachedRoute['component'],
redirect: cachedRoute['redirect'],
children: cachedRoute['children'],
@ -149,6 +165,7 @@ class RouterManager {
parent: cachedRoute['parent'],
name: cachedRoute['name'],
path: cachedRoute['path'],
icon: cachedRoute['icon'],
component: ComponentManager.getLocalComponent(cachedRoute['component']),
redirect: cachedRoute['redirect'],
children: cachedRoute['children'],

80
io.sc.platform.core.frontend/src/platform/plugin/manager/TagViewManager.ts

@ -4,7 +4,7 @@ import { SessionStorage } from 'quasar';
import { Environment } from '@/platform/plugin/environment';
import { router } from '@/platform/plugin/router';
import { PConst } from '@/platform/PConst';
import { MenuManager } from '@/platform/plugin/manager';
import { MenuManager, RouterManager } from '@/platform/plugin/manager';
import { Tools } from '@/platform/utils';
const gc = Environment.getConfigure();
@ -47,38 +47,55 @@ class TagViewManager {
return;
}
let menuId: string;
let menuTitleI18nKey: string;
let menuIcon: string;
// 是否需要添加 TagView
let needToAdd = true;
if (menu) {
TagViewManager.#tagViewsAndBreadcrumbs.menuGroupExpandStatus = TagViewManager.createMenuGroupExpandStatus(storage, menu.id);
// 是否需要添加 TagView
let needToAdd = true;
menuId = menu.id;
menuTitleI18nKey = menu.titleI18nKey;
menuIcon = menu.icon;
for (const tagView of TagViewManager.#tagViewsAndBreadcrumbs.tagViews) {
if (tagView.menuId === menu.id) {
// 如果存在相同的则无需添加
if (tagView.menuId === menuId) {
needToAdd = false;
break;
}
}
if (needToAdd) {
TagViewManager.addTagView({
menuId: menu.id,
menuTitleI18nKey: menu.titleI18nKey,
menuIcon: menu.icon,
routeName: to.name,
routePath: to.path,
routeQuery: to.query,
});
}
// 设置选中的 TagView
if (needToAdd) {
nextTick(() => {
gr.selectedTagView = menu.id;
});
} else {
const route = RouterManager.getRouteByName(to.name);
if (route) {
menuId = route.name;
menuTitleI18nKey = route.name;
menuIcon = route.icon;
for (const tagView of TagViewManager.#tagViewsAndBreadcrumbs.tagViews) {
if (tagView.menuId === menuId) {
needToAdd = false;
break;
}
}
} else {
gr.selectedTagView = menu.id;
return;
}
}
if (needToAdd) {
TagViewManager.addTagView({
menuId: menuId,
menuTitleI18nKey: menuTitleI18nKey,
menuIcon: menuIcon,
routeName: to.name,
routePath: to.path,
routeQuery: to.query,
params: to.params,
});
}
// 设置选中的 TagView
nextTick(() => {
gr.selectedTagView = menuId;
});
}
}
@ -89,9 +106,9 @@ class TagViewManager {
// 只保留存在对应菜单的 TagView
const newTagViews: TagViewType[] = [];
for (const tagView of storage.tagViews) {
if (MenuManager.getMenuById(tagView.menuId)) {
newTagViews.push(tagView);
}
//if (MenuManager.getMenuById(tagView.menuId)) {
newTagViews.push(tagView);
//}
}
return newTagViews;
}
@ -194,6 +211,15 @@ class TagViewManager {
router.push({ name: routeName });
}
}
/**
* TagView
* @param id
*/
public static addAndSelectTagView(tagView: TagViewType) {
TagViewManager.addTagView(tagView);
gr.selectedTagView = tagView.menuId;
}
}
export { TagViewManager };

1
io.sc.platform.core.frontend/src/platform/plugin/router.ts

@ -56,7 +56,6 @@ const router = createRouter({
router.beforeEach((to: any, from: any, next: any) => {
// 显示进度条
LoadingBar.start();
TagViewManager.handleTagViewAndBreadcrumbsAndKeepAlive(to);
next();
});

1
io.sc.platform.core.frontend/src/platform/types/ConfigureType.ts

@ -111,6 +111,7 @@ export type ConfigureType = {
// ---------------------------------------------
sider: {
position: 'left' | 'right'; // 位置(可选值:left,right)
isSiderExpaned: boolean; // 是否默认展开
dense: boolean; // 是否采用紧凑模式
border: boolean; // 是否显示边框

2
io.sc.platform.core.frontend/src/platform/types/FrontEndRouteType.ts

@ -7,7 +7,9 @@ export type FrontEndRouteMetaType = {
};
export type FrontEndRouteType = {
force: boolean;
name: string;
icon: string;
path: string;
parent: string;
module: string;

1
io.sc.platform.core.frontend/src/platform/types/TagViewType.ts

@ -5,4 +5,5 @@ export type TagViewType = {
routeName: string;
routePath: string;
routeQuery: Map<string, string>;
params: Map<string, string>;
};

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

@ -1,26 +1,80 @@
[
{
"name": "route.testcase.tag",
"path": "testcase/tag",
"name": "route.testcase.openNoMenuRoute",
"path": "testcase/openNoMenuRoute",
"parent": "/",
"priority": 0,
"component": "component.testcase.tag",
"componentPath": "@/views/testcase/tag/tag.vue",
"component": "component.testcase.openNoMenuRoute",
"componentPath": "@/views/testcase/route/OpenNoMenuRoute.vue",
"redirect": null,
"meta": {
"permissions": ["/testcase/tag/**/*"]
"permissions": ["/testcase/route/**/*"]
}
},
{
"name": "route.testcase.formElements",
"path": "testcase/formElements",
"force": true,
"name": "route.testcase.noMenuRoute",
"icon":'bi-1-circle',
"path": "testcase/noMenuRoute/:id",
"parent": "/",
"priority": 0,
"component": "component.testcase.formElements",
"componentPath": "@/views/FormElements.vue",
"component": "component.testcase.noMenuRoute",
"componentPath": "@/views/testcase/route/NoMenuRoute.vue",
"redirect": null,
"meta": {
"permissions": ["/testcase/route/**/*"]
}
},
{
"name": "route.testcase.mathEditor",
"path": "testcase/mathEditor",
"parent": "/",
"priority": 0,
"component": "component.testcase.mathEditor",
"componentPath": "@/views/testcase/math/MathEditor.vue",
"redirect": null,
"meta": {
"permissions": ["/testcase/math/**/*"]
}
},
{
"name": "route.testcase.form",
"path": "testcase/form",
"parent": "/",
"priority": 0,
"component": "component.testcase.form",
"componentPath": "@/views/testcase/form/form.vue",
"redirect": null,
"meta": {
"permissions": ["/testcase/form/**/*"]
}
},
{
"name": "route.testcase.excel",
"path": "testcase/excel",
"parent": "/",
"priority": 0,
"component": "component.testcase.excel",
"componentPath": "@/views/testcase/excel/Excel.vue",
"redirect": null,
"meta": {
"permissions": ["/testcase/excel/**/*"]
}
},
{
"name": "route.testcase.word",
"path": "testcase/word",
"parent": "/",
"priority": 0,
"component": "component.testcase.word",
"componentPath": "@/views/testcase/word/Word.vue",
"redirect": null,
"meta": {
"permissions": ["/testcase/formElements/**/*"]
"permissions": ["/testcase/word/**/*"]
}
},

53
io.sc.platform.core.frontend/src/views/testcase/excel/Excel.vue

@ -0,0 +1,53 @@
<template>
<div ref="divRef" class="border border-gray-200" style="height: 100%"></div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import '@univerjs/design/lib/index.css';
import '@univerjs/ui/lib/index.css';
import '@univerjs/docs-ui/lib/index.css';
import '@univerjs/sheets-ui/lib/index.css';
import '@univerjs/sheets-formula/lib/index.css';
import { Univer, UniverInstanceType, LocaleType } from '@univerjs/core';
import { defaultTheme } from '@univerjs/design';
import { UniverFormulaEnginePlugin } from '@univerjs/engine-formula';
import { UniverRenderEnginePlugin } from '@univerjs/engine-render';
import { UniverUIPlugin } from '@univerjs/ui';
import { UniverDocsPlugin } from '@univerjs/docs';
import { UniverDocsUIPlugin } from '@univerjs/docs-ui';
import { UniverSheetsPlugin } from '@univerjs/sheets';
import { UniverSheetsFormulaPlugin } from '@univerjs/sheets-formula';
import { UniverSheetsUIPlugin } from '@univerjs/sheets-ui';
const divRef = ref();
onMounted(() => {
const univer = new Univer({
locale: LocaleType.en_US,
theme: defaultTheme,
});
univer.registerPlugin(UniverRenderEnginePlugin);
univer.registerPlugin(UniverFormulaEnginePlugin);
univer.registerPlugin(UniverUIPlugin, {
container: divRef.value,
});
univer.registerPlugin(UniverDocsPlugin, {
hasScroll: false,
});
univer.registerPlugin(UniverDocsUIPlugin);
univer.registerPlugin(UniverSheetsPlugin);
univer.registerPlugin(UniverSheetsUIPlugin);
univer.registerPlugin(UniverSheetsFormulaPlugin);
univer.createUnit(UniverInstanceType.UNIVER_SHEET, {});
});
</script>

409
io.sc.platform.core.frontend/src/views/testcase/form/form.vue

@ -1,413 +1,10 @@
<template>
<div style="height: 100%">
<w-grid
:title="$t('lcdp.scheduler.task.grid.title')"
:config-button="true"
selection="multiple"
:checkbox-selection="true"
:query-form-cols-num="12"
:query-form-fields="[
{ colSpan: 4, name: 'name', label: $t('name'), type: 'text' },
{
colSpan: 4,
name: 'executor',
label: $t('lcdp.scheduler.task.grid.entity.executor'),
type: 'select',
clearable: true,
queryOperator: 'equals',
options: executorOptionsRef,
},
{
colSpan: 2,
name: 'status',
label: $t('status'),
type: 'select',
clearable: true,
queryOperator: 'equals',
options: Options.enum(Enums.TaskStatus),
},
]"
:data-url="Environment.apiContextPath('/api/scheduler/manager/task')"
:pageable="false"
:toolbar-configure="{ noIcon: false }"
:toolbar-actions="[
'query',
'refresh',
'separator',
'add',
'edit',
'remove',
'separator',
{
name: 'execute',
label: $t('lcdp.scheduler.task.grid.toolbar.execute'),
icon: 'bi-caret-right-fill',
enableIf: (selecteds) => {
return selecteds && selecteds.length > 0;
},
click: () => {},
},
{
name: 'schedule',
label: $t('lcdp.scheduler.task.grid.toolbar.schedule'),
icon: 'bi-cloud-arrow-up',
enableIf: (selecteds) => {
return selecteds && selecteds.length > 0;
},
click: () => {},
},
{
name: 'log',
label: $t('lcdp.scheduler.task.grid.toolbar.log'),
icon: 'bi-receipt',
enableIf: (selecteds) => {
return selecteds && selecteds.length > 0;
},
click: () => {},
},
'separator',
'view',
'separator',
'export',
]"
:columns="[
{
width: 80,
name: 'status',
label: $t('status'),
align: 'center',
format: (value) => {
return {
componentType: 'QChip',
attrs: { color: value == 'RUNNING' ? 'green' : 'gray', label: Formater.enum(Enums.TaskStatus)(value), dense: true },
};
},
},
{
width: 90,
name: 'type',
label: $t('lcdp.scheduler.task.grid.entity.type'),
format: Formater.enum(Enums.TaskType),
},
{ width: 150, name: 'name', label: $t('name') },
{
width: 100,
name: 'scheduleType',
label: $t('lcdp.scheduler.task.grid.entity.scheduleType'),
format: (value, row) => {
console.log(row);
return Formater.enum(Enums.ScheduleType)(value);
},
},
{
width: 140,
name: 'triggerLastTime',
label: $t('lcdp.scheduler.task.grid.entity.triggerLastTime'),
},
{ width: 140, name: 'triggerNextTime', label: $t('lcdp.scheduler.task.grid.entity.triggerNextTime') },
{
width: 400,
name: 'executorName',
label: $t('lcdp.scheduler.task.grid.entity.executorName'),
format: (value, row) => {
return {
componentType: 'q-btn',
attrs: {
flat: true,
rounded: true,
noCaps: true,
label: row.executorApplicationName + ':' + row.executorName,
color: 'blue',
onClick: () => {
executorRegistryDialogRef.open(row);
},
},
};
},
},
{ width: 80, name: 'author', label: $t('lcdp.scheduler.task.grid.entity.author') },
{ width: 200, name: 'alarmEmail', label: $t('lcdp.scheduler.task.grid.entity.alarmEmail') },
{
width: 100,
name: 'routeStrategy',
label: $t('lcdp.scheduler.task.grid.entity.routeStrategy'),
format: (value) => {
return Formater.enum(Enums.RouteStrategy)(value);
},
},
{
width: 100,
name: 'expirationPolicy',
label: $t('lcdp.scheduler.task.grid.entity.expirationPolicy'),
format: (value) => {
return Formater.enum(Enums.ExpirationPolicy)(value);
},
},
{
width: 100,
name: 'blockStrategy',
label: $t('lcdp.scheduler.task.grid.entity.blockStrategy'),
format: (value) => {
return Formater.enum(Enums.BlockStrategy)(value);
},
},
{ width: 80, name: 'timeout', label: $t('lcdp.scheduler.task.grid.entity.timeout') },
{ width: 90, name: 'failRetryCount', label: $t('lcdp.scheduler.task.grid.entity.failRetryCount') },
]"
:editor="{
dialog: {
width: '800px',
},
form: {
colsNum: 2,
fields: [
{
name: 'type',
label: $t('lcdp.scheduler.task.grid.entity.type'),
required: true,
type: 'select',
options: Options.enum(Enums.TaskType),
},
{
name: 'executor',
label: $t('lcdp.scheduler.task.grid.entity.executor'),
required: true,
type: 'select',
clearable: true,
options: executorOptionsRef,
},
{ colsFirst: true, name: 'name', label: $t('name'), type: 'text', required: true },
{ name: 'description', label: $t('description'), type: 'text' },
{ name: 'author', label: $t('lcdp.scheduler.task.grid.entity.author'), type: 'text' },
{ name: 'alarmEmail', label: $t('lcdp.scheduler.task.grid.entity.alarmEmail'), type: 'text' },
{
colsFirst: true,
name: 'scheduleType',
label: $t('lcdp.scheduler.task.grid.entity.scheduleType'),
required: true,
type: 'select',
options: Options.enum(Enums.ScheduleType),
defaultValue: 'FIX_RATE',
},
{
name: 'scheduleCron',
label: $t('lcdp.scheduler.task.grid.entity.scheduleCron'),
type: 'cron',
showIf: (arg) => {
return arg.form.getFieldValue('scheduleType') === 'CRON';
},
},
{
name: 'scheduleFixRate',
label: $t('lcdp.scheduler.task.grid.entity.scheduleFixRate'),
type: 'number',
showIf: (arg) => {
return arg.form.getFieldValue('scheduleType') === 'FIX_RATE';
},
},
{
name: 'scheduleFixDelay',
label: $t('lcdp.scheduler.task.grid.entity.scheduleFixDelay'),
type: 'number',
showIf: (arg) => {
return arg.form.getFieldValue('scheduleType') === 'FIX_DELAY';
},
},
{
colsFirst: true,
colSpan: 2,
name: 'bean',
label: $t('lcdp.scheduler.task.grid.entity.type.bean'),
type: 'text',
showIf: (arg) => {
return arg.form.getFieldValue('type') === 'BEAN';
},
},
{
colsFirst: true,
colSpan: 2,
name: 'groovy',
label: $t('lcdp.scheduler.task.grid.entity.type.groovy'),
type: 'code-mirror',
rows: 8,
lang: 'groovy',
showIf: (arg) => {
return arg.form.getFieldValue('type') === 'GROOVY';
},
},
{
colsFirst: true,
colSpan: 2,
name: 'shell',
label: $t('lcdp.scheduler.task.grid.entity.type.shell'),
type: 'code-mirror',
rows: 8,
lang: 'shell',
showIf: (arg) => {
return arg.form.getFieldValue('type') === 'SHELL';
},
},
{
colsFirst: true,
colSpan: 2,
name: 'python',
label: $t('lcdp.scheduler.task.grid.entity.type.python'),
type: 'code-mirror',
rows: 8,
lang: 'python',
showIf: (arg) => {
return arg.form.getFieldValue('type') === 'PYTHON';
},
},
{
colsFirst: true,
colSpan: 2,
name: 'php',
label: $t('lcdp.scheduler.task.grid.entity.type.php'),
type: 'code-mirror',
rows: 8,
lang: 'php',
showIf: (arg) => {
return arg.form.getFieldValue('type') === 'PHP';
},
},
{
colsFirst: true,
colSpan: 2,
name: 'nodejs',
label: $t('lcdp.scheduler.task.grid.entity.type.nodejs'),
type: 'code-mirror',
rows: 8,
lang: 'nodejs',
showIf: (arg) => {
return arg.form.getFieldValue('type') === 'NODEJS';
},
},
{
colsFirst: true,
colSpan: 2,
name: 'powershell',
label: $t('lcdp.scheduler.task.grid.entity.type.powershell'),
type: 'code-mirror',
rows: 8,
lang: 'powershell',
showIf: (arg) => {
return arg.form.getFieldValue('type') === 'POWERSHELL';
},
},
{ colSpan: 2, name: 'parameter', label: $t('lcdp.scheduler.task.grid.entity.parameter'), type: 'textarea', rows: 5 },
{
name: 'routeStrategy',
label: $t('lcdp.scheduler.task.grid.entity.routeStrategy'),
required: true,
type: 'select',
options: Options.enum(Enums.RouteStrategy),
defaultValue: 'FIRST',
},
{
name: 'expirationPolicy',
label: $t('lcdp.scheduler.task.grid.entity.expirationPolicy'),
required: true,
type: 'select',
options: Options.enum(Enums.ExpirationPolicy),
},
{
name: 'blockStrategy',
label: $t('lcdp.scheduler.task.grid.entity.blockStrategy'),
required: true,
type: 'select',
options: Options.enum(Enums.BlockStrategy),
},
{ colsFirst: true, name: 'timeout', label: $t('lcdp.scheduler.task.grid.entity.timeout.label'), type: 'number' },
{ name: 'failRetryCount', label: $t('lcdp.scheduler.task.grid.entity.failRetryCount.label'), type: 'number' },
],
},
}"
:viewer="{
panel: {
columnNum: 1,
fields: [
{ name: 'id', label: $t('id') },
{ name: 'parent', label: $t('parent') },
{ name: 'type', label: $t('lcdp.scheduler.task.grid.entity.type') },
{ name: 'executor', label: $t('lcdp.scheduler.task.grid.entity.executorId') },
{ name: 'executorName', label: $t('lcdp.scheduler.task.grid.entity.executorName') },
{ name: 'name', label: $t('name') },
{ name: 'description', label: $t('description') },
{ name: 'author', label: $t('lcdp.scheduler.task.grid.entity.author') },
{ name: 'alarmEmail', label: $t('lcdp.scheduler.task.grid.entity.alarmEmail') },
{ name: 'scheduleType', label: $t('lcdp.scheduler.task.grid.entity.scheduleType') },
{ name: 'scheduleConf', label: $t('lcdp.scheduler.task.grid.entity.scheduleConf') },
{ name: 'bean', label: $t('lcdp.scheduler.task.grid.entity.type.bean') },
{ name: 'groovy', label: $t('lcdp.scheduler.task.grid.entity.type.groovy') },
{ name: 'shell', label: $t('lcdp.scheduler.task.grid.entity.type.shell') },
{ name: 'python', label: $t('lcdp.scheduler.task.grid.entity.type.python') },
{ name: 'php', label: $t('lcdp.scheduler.task.grid.entity.type.php') },
{ name: 'nodejs', label: $t('lcdp.scheduler.task.grid.entity.type.nodejs') },
{ name: 'powershell', label: $t('lcdp.scheduler.task.grid.entity.type.powershell') },
{ name: 'parameter', label: $t('lcdp.scheduler.task.grid.entity.parameter') },
{ name: 'routeStrategy', label: $t('lcdp.scheduler.task.grid.entity.routeStrategy') },
{ name: 'expirationPolicy', label: $t('lcdp.scheduler.task.grid.entity.expirationPolicy') },
{ name: 'blockStrategy', label: $t('lcdp.scheduler.task.grid.entity.blockStrategy') },
{ name: 'timeout', label: $t('lcdp.scheduler.task.grid.entity.timeout') },
{ name: 'failRetryCount', label: $t('lcdp.scheduler.task.grid.entity.failRetryCount') },
{ name: 'triggerStatus', label: $t('lcdp.scheduler.task.grid.entity.triggerStatus') },
{ name: 'triggerLastTime', label: $t('lcdp.scheduler.task.grid.entity.triggerLastTime') },
{ name: 'triggerNextTime', label: $t('lcdp.scheduler.task.grid.entity.triggerNextTime') },
{ 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>
<ExecutorRegistryDialog ref="executorRegistryDialogRef"></ExecutorRegistryDialog>
<w-color-input-palette v-model="colorRef"></w-color-input-palette>
</div>
</template>
<script setup lang="ts">
import { ref, inject } from 'vue';
import { axios, Environment, EnumTools, Formater, Options } from 'platform-core';
import ExecutorRegistryDialog from './ExecutorRegistryDialog.vue';
const eventBus = inject('eventBus');
const Enums = await EnumTools.fetch([
'io.sc.platform.scheduler.core.enums.RouteStrategy',
'io.sc.platform.scheduler.core.enums.ExpirationPolicy',
'io.sc.platform.scheduler.core.enums.BlockStrategy',
'io.sc.platform.scheduler.core.enums.TaskType',
'io.sc.platform.scheduler.core.enums.ScheduleType',
'io.sc.platform.scheduler.core.enums.TaskStatus',
]);
const executorRegistryDialogRef = ref();
const executorOptionsRef = ref([]);
const reloadExecutor = () => {
axios.get(Environment.apiContextPath('/api/scheduler/manager/executor?pageable=false')).then((response) => {
executorOptionsRef.value.splice(0, executorOptionsRef.value.length);
const list = response?.data?.content;
if (list) {
for (const item of list) {
executorOptionsRef.value.push({ label: item.applicationName + ' : ' + item.name, value: item.id });
}
}
});
};
//
eventBus.on('io.sc.platform.scheduler.manager.ExecutorChanged', () => {
reloadExecutor();
});
import { ref } from 'vue';
reloadExecutor();
const colorRef = ref('#EEEEEE');
</script>

133
io.sc.platform.core.frontend/src/views/testcase/math/MathEditor.vue

@ -0,0 +1,133 @@
<template>
<div>
<w-expression v-model="formula3"></w-expression>
</div>
</template>
<script setup lang="ts">
const formula = '';
const formula2 = 'hello world';
const formula3 = `
<mrow>
<mi>0.12</mi>
<mo>&#xD7;</mo>
<mrow>
<msqrt>
<mi>x</mi>
</msqrt>
<mo>(</mo>
<mrow>
<mfrac>
<mrow>
<mspace></mspace>
<mi>1</mi>
<mo>-</mo>
<mrow>
<mfrac>
<mrow><mi>1</mi></mrow>
<mrow>
<msup>
<mi>e</mi>
<mi>
<mrow>
<mo>(</mo>
<mi>50</mi>
<mo>&#xD7;</mo>
<mi>PD</mi>
<mo>)</mo>
</mrow>
</mi>
</msup>
</mrow>
</mfrac>
</mrow>
</mrow>
<mrow>
<mi>1</mi>
<mo>-</mo>
<mrow>
<mfrac>
<mrow><mi>1</mi></mrow>
<mrow>
<msup>
<mi>e</mi>
<mi>50</mi>
</msup>
</mrow>
</mfrac>
</mrow>
</mrow>
</mfrac>
</mrow>
<mo>)</mo>
</mrow>
<mo>+</mo>
<mi>0.24</mi>
<mo>&#xD7;</mo>
<mrow>
<mo>(</mo>
<mi>1</mi>
<mo>-</mo>
<mrow>
<mfrac>
<mrow>
<mi>1</mi>
<mo>-</mo>
<mrow>
<mfrac>
<mrow><mi>1</mi></mrow>
<mrow>
<msup>
<mi>e</mi>
<mi>
<mrow>
<mo>(</mo>
<mi>50</mi>
<mo>&#xD7;</mo>
<mi>PD</mi>
<mo>)</mo>
</mrow>
</mi>
</msup>
</mrow>
</mfrac>
</mrow>
</mrow>
<mrow>
<mi>1</mi>
<mo>-</mo>
<mrow>
<mfrac>
<mrow><mi>1</mi></mrow>
<mrow>
<msup>
<mi>e</mi>
<mi>50</mi>
</msup>
</mrow>
</mfrac>
</mrow>
</mrow>
</mfrac>
</mrow>
<mo>)</mo>
</mrow>
<mo>-</mo>
<mi>0.04</mi>
<mo>&#xD7;</mo>
<mrow>
<mo>(</mo>
<mi>1</mi>
<mo>-</mo>
<mfrac>
<mrow>
<mi>S</mi>
<mo>-</mo>
<mi>3</mi>
</mrow>
<mi>27</mi>
</mfrac>
<mo>)</mo>
</mrow>
</mrow>
`;
</script>

4
io.sc.platform.core.frontend/src/views/testcase/route/NoMenuRoute.vue

@ -0,0 +1,4 @@
<template>
<div class="py-10">路由参数: {{ $route.params }}</div>
</template>
<script setup lang="ts"></script>

12
io.sc.platform.core.frontend/src/views/testcase/route/OpenNoMenuRoute.vue

@ -0,0 +1,12 @@
<template>
<div class="row justify-center py-10">
<q-btn
label="在标签中打开非菜单路由"
:to="{
name: 'route.testcase.noMenuRoute',
params: { id: 'xxx' },
}"
></q-btn>
</div>
</template>
<script setup lang="ts"></script>

442
io.sc.platform.core.frontend/src/views/testcase/tag/tag.vue

@ -1,44 +1,406 @@
<template>
<w-grid
ref="gridRef"
:height="300"
:title="$t('lcdp.scheduler.executor.selectOnlineExecutorDialog.grid.title')"
selection="multiple"
:full-screen-button="false"
:toolbar-configure="{ noIcon: false }"
:toolbar-actions="['refresh']"
:auto-fetch-data="true"
:pageable="true"
:fetch-data-url="Environment.apiContextPath('/api/scheduler/manager/executorRegistry')"
:columns="[
{
width: 150,
name: 'online',
label: $t('status'),
format: (value) => {
return {
componentType: 'w-enums-tag',
attrs: {
value: value,
dense: true,
items: [
{ value: true, color: 'green', label: $t('online') },
{ value: false, color: 'grey', label: $t('offline') },
],
},
};
},
},
{ width: 150, name: 'applicationName', label: $t('lcdp.scheduler.executor.selectOnlineExecutorDialog.grid.entity.applicationName') },
{ width: 100, name: 'name', label: $t('lcdp.scheduler.executor.selectOnlineExecutorDialog.grid.entity.name') },
{ width: 150, name: 'url', label: $t('url') },
{ width: 100, name: 'registDate', label: t('lcdp.scheduler.executor.selectOnlineExecutorDialog.grid.entity.registDate') },
]"
></w-grid>
<div style="height: 100%">
<w-grid
:title="$t('lcdp.scheduler.task.grid.title')"
:config-button="true"
selection="multiple"
:checkbox-selection="true"
:query-form-cols-num="12"
:query-form-fields="[
{ colSpan: 4, name: 'name', label: $t('name'), type: 'text' },
{
colSpan: 4,
name: 'executor',
label: $t('lcdp.scheduler.task.grid.entity.executor'),
type: 'select',
clearable: true,
queryOperator: 'equals',
options: executorOptionsRef,
},
{
colSpan: 2,
name: 'status',
label: $t('status'),
type: 'select',
clearable: true,
queryOperator: 'equals',
options: Options.enum(Enums.TaskStatus),
},
]"
:data-url="Environment.apiContextPath('/api/scheduler/manager/task')"
:pageable="false"
:toolbar-configure="{ noIcon: false }"
:toolbar-actions="[
'query',
'refresh',
'separator',
'add',
'edit',
'remove',
'separator',
{
name: 'execute',
label: $t('lcdp.scheduler.task.grid.toolbar.execute'),
icon: 'bi-caret-right-fill',
enableIf: (selecteds) => {
return selecteds && selecteds.length > 0;
},
click: () => {},
},
{
name: 'schedule',
label: $t('lcdp.scheduler.task.grid.toolbar.schedule'),
icon: 'bi-cloud-arrow-up',
enableIf: (selecteds) => {
return selecteds && selecteds.length > 0;
},
click: () => {},
},
{
name: 'log',
label: $t('lcdp.scheduler.task.grid.toolbar.log'),
icon: 'bi-receipt',
enableIf: (selecteds) => {
return selecteds && selecteds.length > 0;
},
click: () => {},
},
'separator',
'view',
'separator',
'export',
]"
:columns="[
{
width: 80,
name: 'status',
label: $t('status'),
align: 'center',
format: (value) => {
return {
componentType: 'QChip',
attrs: { color: value == 'RUNNING' ? 'green' : 'gray', label: Formater.enum(Enums.TaskStatus)(value), dense: true },
};
},
},
{
width: 90,
name: 'type',
label: $t('lcdp.scheduler.task.grid.entity.type'),
format: Formater.enum(Enums.TaskType),
},
{ width: 150, name: 'name', label: $t('name') },
{
width: 100,
name: 'scheduleType',
label: $t('lcdp.scheduler.task.grid.entity.scheduleType'),
format: (value, row) => {
console.log(row);
return Formater.enum(Enums.ScheduleType)(value);
},
},
{
width: 140,
name: 'triggerLastTime',
label: $t('lcdp.scheduler.task.grid.entity.triggerLastTime'),
},
{ width: 140, name: 'triggerNextTime', label: $t('lcdp.scheduler.task.grid.entity.triggerNextTime') },
{
width: 400,
name: 'executorName',
label: $t('lcdp.scheduler.task.grid.entity.executorName'),
format: (value, row) => {
return {
componentType: 'q-btn',
attrs: {
flat: true,
rounded: true,
noCaps: true,
label: row.executorApplicationName + ':' + row.executorName,
color: 'blue',
onClick: () => {
executorRegistryDialogRef.open(row);
},
},
};
},
},
{ width: 80, name: 'author', label: $t('lcdp.scheduler.task.grid.entity.author') },
{ width: 200, name: 'alarmEmail', label: $t('lcdp.scheduler.task.grid.entity.alarmEmail') },
{
width: 100,
name: 'routeStrategy',
label: $t('lcdp.scheduler.task.grid.entity.routeStrategy'),
format: (value) => {
return Formater.enum(Enums.RouteStrategy)(value);
},
},
{
width: 100,
name: 'expirationPolicy',
label: $t('lcdp.scheduler.task.grid.entity.expirationPolicy'),
format: (value) => {
return Formater.enum(Enums.ExpirationPolicy)(value);
},
},
{
width: 100,
name: 'blockStrategy',
label: $t('lcdp.scheduler.task.grid.entity.blockStrategy'),
format: (value) => {
return Formater.enum(Enums.BlockStrategy)(value);
},
},
{ width: 80, name: 'timeout', label: $t('lcdp.scheduler.task.grid.entity.timeout') },
{ width: 90, name: 'failRetryCount', label: $t('lcdp.scheduler.task.grid.entity.failRetryCount') },
]"
:editor="{
dialog: {
width: '800px',
},
form: {
colsNum: 2,
fields: [
{
name: 'type',
label: $t('lcdp.scheduler.task.grid.entity.type'),
required: true,
type: 'select',
options: Options.enum(Enums.TaskType),
},
{
name: 'executor',
label: $t('lcdp.scheduler.task.grid.entity.executor'),
required: true,
type: 'select',
clearable: true,
options: executorOptionsRef,
},
{ colsFirst: true, name: 'name', label: $t('name'), type: 'text', required: true },
{ name: 'description', label: $t('description'), type: 'text' },
{ name: 'author', label: $t('lcdp.scheduler.task.grid.entity.author'), type: 'text' },
{ name: 'alarmEmail', label: $t('lcdp.scheduler.task.grid.entity.alarmEmail'), type: 'text' },
{
colsFirst: true,
name: 'scheduleType',
label: $t('lcdp.scheduler.task.grid.entity.scheduleType'),
required: true,
type: 'select',
options: Options.enum(Enums.ScheduleType),
defaultValue: 'FIX_RATE',
},
{
name: 'scheduleCron',
label: $t('lcdp.scheduler.task.grid.entity.scheduleCron'),
type: 'text',
showIf: (arg) => {
return arg.form.getFieldValue('scheduleType') === 'CRON';
},
},
{
name: 'scheduleFixRate',
label: $t('lcdp.scheduler.task.grid.entity.scheduleFixRate'),
type: 'number',
showIf: (arg) => {
return arg.form.getFieldValue('scheduleType') === 'FIX_RATE';
},
},
{
name: 'scheduleFixDelay',
label: $t('lcdp.scheduler.task.grid.entity.scheduleFixDelay'),
type: 'number',
showIf: (arg) => {
return arg.form.getFieldValue('scheduleType') === 'FIX_DELAY';
},
},
{
colsFirst: true,
colSpan: 2,
name: 'bean',
label: $t('lcdp.scheduler.task.grid.entity.type.bean'),
type: 'text',
showIf: (arg) => {
return arg.form.getFieldValue('type') === 'BEAN';
},
},
{
colsFirst: true,
colSpan: 2,
name: 'groovy',
label: $t('lcdp.scheduler.task.grid.entity.type.groovy'),
type: 'code-mirror',
rows: 8,
lang: 'groovy',
showIf: (arg) => {
return arg.form.getFieldValue('type') === 'GROOVY';
},
},
{
colsFirst: true,
colSpan: 2,
name: 'shell',
label: $t('lcdp.scheduler.task.grid.entity.type.shell'),
type: 'code-mirror',
rows: 8,
lang: 'shell',
showIf: (arg) => {
return arg.form.getFieldValue('type') === 'SHELL';
},
},
{
colsFirst: true,
colSpan: 2,
name: 'python',
label: $t('lcdp.scheduler.task.grid.entity.type.python'),
type: 'code-mirror',
rows: 8,
lang: 'python',
showIf: (arg) => {
return arg.form.getFieldValue('type') === 'PYTHON';
},
},
{
colsFirst: true,
colSpan: 2,
name: 'php',
label: $t('lcdp.scheduler.task.grid.entity.type.php'),
type: 'code-mirror',
rows: 8,
lang: 'php',
showIf: (arg) => {
return arg.form.getFieldValue('type') === 'PHP';
},
},
{
colsFirst: true,
colSpan: 2,
name: 'nodejs',
label: $t('lcdp.scheduler.task.grid.entity.type.nodejs'),
type: 'code-mirror',
rows: 8,
lang: 'nodejs',
showIf: (arg) => {
return arg.form.getFieldValue('type') === 'NODEJS';
},
},
{
colsFirst: true,
colSpan: 2,
name: 'powershell',
label: $t('lcdp.scheduler.task.grid.entity.type.powershell'),
type: 'code-mirror',
rows: 8,
lang: 'powershell',
showIf: (arg) => {
return arg.form.getFieldValue('type') === 'POWERSHELL';
},
},
{ colSpan: 2, name: 'parameter', label: $t('lcdp.scheduler.task.grid.entity.parameter'), type: 'textarea', rows: 5 },
{
name: 'routeStrategy',
label: $t('lcdp.scheduler.task.grid.entity.routeStrategy'),
required: true,
type: 'select',
options: Options.enum(Enums.RouteStrategy),
defaultValue: 'FIRST',
},
{
name: 'expirationPolicy',
label: $t('lcdp.scheduler.task.grid.entity.expirationPolicy'),
required: true,
type: 'select',
options: Options.enum(Enums.ExpirationPolicy),
},
{
name: 'blockStrategy',
label: $t('lcdp.scheduler.task.grid.entity.blockStrategy'),
required: true,
type: 'select',
options: Options.enum(Enums.BlockStrategy),
},
{ colsFirst: true, name: 'timeout', label: $t('lcdp.scheduler.task.grid.entity.timeout.label'), type: 'number' },
{ name: 'failRetryCount', label: $t('lcdp.scheduler.task.grid.entity.failRetryCount.label'), type: 'number' },
],
},
}"
:viewer="{
panel: {
columnNum: 1,
fields: [
{ name: 'id', label: $t('id') },
{ name: 'parent', label: $t('parent') },
{ name: 'type', label: $t('lcdp.scheduler.task.grid.entity.type') },
{ name: 'executor', label: $t('lcdp.scheduler.task.grid.entity.executorId') },
{ name: 'executorName', label: $t('lcdp.scheduler.task.grid.entity.executorName') },
{ name: 'name', label: $t('name') },
{ name: 'description', label: $t('description') },
{ name: 'author', label: $t('lcdp.scheduler.task.grid.entity.author') },
{ name: 'alarmEmail', label: $t('lcdp.scheduler.task.grid.entity.alarmEmail') },
{ name: 'scheduleType', label: $t('lcdp.scheduler.task.grid.entity.scheduleType') },
{ name: 'scheduleConf', label: $t('lcdp.scheduler.task.grid.entity.scheduleConf') },
{ name: 'bean', label: $t('lcdp.scheduler.task.grid.entity.type.bean') },
{ name: 'groovy', label: $t('lcdp.scheduler.task.grid.entity.type.groovy') },
{ name: 'shell', label: $t('lcdp.scheduler.task.grid.entity.type.shell') },
{ name: 'python', label: $t('lcdp.scheduler.task.grid.entity.type.python') },
{ name: 'php', label: $t('lcdp.scheduler.task.grid.entity.type.php') },
{ name: 'nodejs', label: $t('lcdp.scheduler.task.grid.entity.type.nodejs') },
{ name: 'powershell', label: $t('lcdp.scheduler.task.grid.entity.type.powershell') },
{ name: 'parameter', label: $t('lcdp.scheduler.task.grid.entity.parameter') },
{ name: 'routeStrategy', label: $t('lcdp.scheduler.task.grid.entity.routeStrategy') },
{ name: 'expirationPolicy', label: $t('lcdp.scheduler.task.grid.entity.expirationPolicy') },
{ name: 'blockStrategy', label: $t('lcdp.scheduler.task.grid.entity.blockStrategy') },
{ name: 'timeout', label: $t('lcdp.scheduler.task.grid.entity.timeout') },
{ name: 'failRetryCount', label: $t('lcdp.scheduler.task.grid.entity.failRetryCount') },
{ name: 'triggerStatus', label: $t('lcdp.scheduler.task.grid.entity.triggerStatus') },
{ name: 'triggerLastTime', label: $t('lcdp.scheduler.task.grid.entity.triggerLastTime') },
{ name: 'triggerNextTime', label: $t('lcdp.scheduler.task.grid.entity.triggerNextTime') },
{ 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>
</div>
</template>
<script setup lang="ts">
import { Environment, $t } from '@/platform';
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
console.log(t('menu.testcase.formElements', { name: 'xxx' }, '000'));
import { ref, inject } from 'vue';
import { axios, Environment, EnumTools, Formater, Options } from '@/platform';
const eventBus = inject('eventBus');
const Enums = await EnumTools.fetch([
'io.sc.platform.scheduler.core.enums.RouteStrategy',
'io.sc.platform.scheduler.core.enums.ExpirationPolicy',
'io.sc.platform.scheduler.core.enums.BlockStrategy',
'io.sc.platform.scheduler.core.enums.TaskType',
'io.sc.platform.scheduler.core.enums.ScheduleType',
'io.sc.platform.scheduler.core.enums.TaskStatus',
]);
const executorRegistryDialogRef = ref();
const executorOptionsRef = ref([]);
const reloadExecutor = () => {
axios.get(Environment.apiContextPath('/api/scheduler/manager/executor?pageable=false')).then((response) => {
executorOptionsRef.value.splice(0, executorOptionsRef.value.length);
const list = response?.data?.content;
if (list) {
for (const item of list) {
executorOptionsRef.value.push({ label: item.applicationName + ' : ' + item.name, value: item.id });
}
}
});
};
reloadExecutor();
</script>

53
io.sc.platform.core.frontend/src/views/testcase/word/Word.vue

@ -0,0 +1,53 @@
<template>
<div ref="divRef" class="border border-gray-200" style="height: 100%"></div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import '@univerjs/design/lib/index.css';
import '@univerjs/ui/lib/index.css';
import '@univerjs/docs-ui/lib/index.css';
import '@univerjs/sheets-ui/lib/index.css';
import '@univerjs/sheets-formula/lib/index.css';
import { Univer, UniverInstanceType, LocaleType } from '@univerjs/core';
import { defaultTheme } from '@univerjs/design';
import { UniverFormulaEnginePlugin } from '@univerjs/engine-formula';
import { UniverRenderEnginePlugin } from '@univerjs/engine-render';
import { UniverUIPlugin } from '@univerjs/ui';
import { UniverDocsPlugin } from '@univerjs/docs';
import { UniverDocsUIPlugin } from '@univerjs/docs-ui';
import { UniverSheetsPlugin } from '@univerjs/sheets';
import { UniverSheetsFormulaPlugin } from '@univerjs/sheets-formula';
import { UniverSheetsUIPlugin } from '@univerjs/sheets-ui';
const divRef = ref();
onMounted(() => {
const univer = new Univer({
locale: LocaleType.en_US,
theme: defaultTheme,
});
univer.registerPlugin(UniverRenderEnginePlugin);
univer.registerPlugin(UniverFormulaEnginePlugin);
univer.registerPlugin(UniverUIPlugin, {
container: divRef.value,
});
univer.registerPlugin(UniverDocsPlugin, {
hasScroll: false,
});
univer.registerPlugin(UniverDocsUIPlugin);
univer.registerPlugin(UniverSheetsPlugin);
univer.registerPlugin(UniverSheetsUIPlugin);
univer.registerPlugin(UniverSheetsFormulaPlugin);
univer.createUnit(UniverInstanceType.UNIVER_DOC, {});
});
</script>

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

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

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

@ -2,8 +2,11 @@
* ,
*/
import component_testcase_tag from '@/views/testcase/tag/tag.vue';
import component_testcase_formElements from '@/views/FormElements.vue';
import component_testcase_openNoMenuRoute from '@/views/testcase/route/OpenNoMenuRoute.vue';
import component_testcase_noMenuRoute from '@/views/testcase/route/NoMenuRoute.vue';
import component_testcase_mathEditor from '@/views/testcase/math/MathEditor.vue';
import component_testcase_excel from '@/views/testcase/excel/Excel.vue';
import component_testcase_word from '@/views/testcase/word/Word.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';
@ -15,8 +18,11 @@ import component_testcase_gridLayout from '@/views/likm/GridLayout.vue';
import component_testcase_likmTreeGrid from '@/views/likm/TreeGrid.vue';
const localComponents = {
'component.testcase.tag': component_testcase_tag,
'component.testcase.formElements': component_testcase_formElements,
'component.testcase.openNoMenuRoute': component_testcase_openNoMenuRoute,
'component.testcase.noMenuRoute': component_testcase_noMenuRoute,
'component.testcase.mathEditor': component_testcase_mathEditor,
'component.testcase.excel': component_testcase_excel,
'component.testcase.word': component_testcase_word,
'component.testcase.likmDialog': component_testcase_likmDialog,
'component.testcase.likmDrawer': component_testcase_likmDrawer,
'component.testcase.likmForm': component_testcase_likmForm,

7
io.sc.platform.core.frontend/template-project/src/i18n/messages.json

@ -1,4 +1,9 @@
{
"menu.testcase": "Test Case",
"menu.testcase.formElements": "Form Elements"
"menu.testcase.openNoMenuRoute": "Open No Menu Route",
"menu.testcase.mathEditor": "Math Formual Editor",
"menu.testcase.excel": "Excel",
"menu.testcase.word": "Word",
"route.testcase.noMenuRoute":"No Menu Route"
}

7
io.sc.platform.core.frontend/template-project/src/i18n/messages_tw_CN.json

@ -1,4 +1,9 @@
{
"menu.testcase": "測試用例",
"menu.testcase.formElements": "表單控件"
"menu.testcase.openNoMenuRoute": "打開無關聯菜單的路由",
"menu.testcase.mathEditor": "數學公式編輯器",
"menu.testcase.excel": "Excel",
"menu.testcase.word": "Word",
"route.testcase.noMenuRoute":"無關聯菜單路由"
}

8
io.sc.platform.core.frontend/template-project/src/i18n/messages_zh_CN.json

@ -1,5 +1,9 @@
{
"menu.testcase": "测试用例",
"menu.testcase.tag": "Tag",
"menu.testcase.formElements": "表单控件{name}",
"menu.testcase.openNoMenuRoute": "打开无关联菜单的路由示例",
"menu.testcase.mathEditor": "数学公式编辑器",
"menu.testcase.excel": "Excel",
"menu.testcase.word": "Word",
"route.testcase.noMenuRoute":"无关联菜单路由"
}

33
io.sc.platform.core.frontend/template-project/src/menus/menus.json

@ -22,20 +22,39 @@
"type": "ROUTE",
"order": 100,
"parentId": "menu.testcase",
"id": "menu.testcase.tag",
"titleI18nKey": "menu.testcase.tag",
"id": "menu.testcase.openNoMenuRoute",
"titleI18nKey": "menu.testcase.openNoMenuRoute",
"icon": "bi-palette",
"routeName": "route.testcase.tag"
"routeName": "route.testcase.openNoMenuRoute"
},
{
"type": "ROUTE",
"order": 100,
"order": 200,
"parentId": "menu.testcase",
"id": "menu.testcase.mathEditor",
"titleI18nKey": "menu.testcase.mathEditor",
"icon": "bi-palette",
"routeName": "route.testcase.mathEditor"
},
{
"type": "ROUTE",
"order": 300,
"parentId": "menu.testcase",
"id": "menu.testcase.formElements",
"titleI18nKey": "menu.testcase.formElements",
"id": "menu.testcase.excel",
"titleI18nKey": "menu.testcase.excel",
"icon": "bi-palette",
"routeName": "route.testcase.formElements"
"routeName": "route.testcase.excel"
},
{
"type": "ROUTE",
"order": 400,
"parentId": "menu.testcase",
"id": "menu.testcase.word",
"titleI18nKey": "menu.testcase.word",
"icon": "bi-palette",
"routeName": "route.testcase.word"
},
{ "type": "GROUP", "order": 30000, "id": "menu.testcase.likm", "titleI18nKey": "测试用例-likm", "icon": "home" },
{
"type": "ROUTE",

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

@ -1,26 +1,67 @@
[
{
"name": "route.testcase.tag",
"path": "testcase/tag",
"name": "route.testcase.openNoMenuRoute",
"path": "testcase/openNoMenuRoute",
"parent": "/",
"priority": 0,
"component": "component.testcase.tag",
"componentPath": "@/views/testcase/tag/tag.vue",
"component": "component.testcase.openNoMenuRoute",
"componentPath": "@/views/testcase/route/OpenNoMenuRoute.vue",
"redirect": null,
"meta": {
"permissions": ["/testcase/tag/**/*"]
"permissions": ["/testcase/route/**/*"]
}
},
{
"name": "route.testcase.formElements",
"path": "testcase/formElements",
"force": true,
"name": "route.testcase.noMenuRoute",
"icon":'bi-1-circle',
"path": "testcase/noMenuRoute/:id",
"parent": "/",
"priority": 0,
"component": "component.testcase.formElements",
"componentPath": "@/views/FormElements.vue",
"component": "component.testcase.noMenuRoute",
"componentPath": "@/views/testcase/route/NoMenuRoute.vue",
"redirect": null,
"meta": {
"permissions": ["/testcase/route/**/*"]
}
},
{
"name": "route.testcase.mathEditor",
"path": "testcase/mathEditor",
"parent": "/",
"priority": 0,
"component": "component.testcase.mathEditor",
"componentPath": "@/views/testcase/math/MathEditor.vue",
"redirect": null,
"meta": {
"permissions": ["/testcase/math/**/*"]
}
},
{
"name": "route.testcase.excel",
"path": "testcase/excel",
"parent": "/",
"priority": 0,
"component": "component.testcase.excel",
"componentPath": "@/views/testcase/excel/Excel.vue",
"redirect": null,
"meta": {
"permissions": ["/testcase/excel/**/*"]
}
},
{
"name": "route.testcase.word",
"path": "testcase/word",
"parent": "/",
"priority": 0,
"component": "component.testcase.word",
"componentPath": "@/views/testcase/word/Word.vue",
"redirect": null,
"meta": {
"permissions": ["/testcase/formElements/**/*"]
"permissions": ["/testcase/word/**/*"]
}
},

53
io.sc.platform.core.frontend/template-project/src/views/testcase/excel/Excel.vue

@ -0,0 +1,53 @@
<template>
<div ref="divRef" class="border border-gray-200" style="height: 100%"></div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import '@univerjs/design/lib/index.css';
import '@univerjs/ui/lib/index.css';
import '@univerjs/docs-ui/lib/index.css';
import '@univerjs/sheets-ui/lib/index.css';
import '@univerjs/sheets-formula/lib/index.css';
import { Univer, UniverInstanceType, LocaleType } from '@univerjs/core';
import { defaultTheme } from '@univerjs/design';
import { UniverFormulaEnginePlugin } from '@univerjs/engine-formula';
import { UniverRenderEnginePlugin } from '@univerjs/engine-render';
import { UniverUIPlugin } from '@univerjs/ui';
import { UniverDocsPlugin } from '@univerjs/docs';
import { UniverDocsUIPlugin } from '@univerjs/docs-ui';
import { UniverSheetsPlugin } from '@univerjs/sheets';
import { UniverSheetsFormulaPlugin } from '@univerjs/sheets-formula';
import { UniverSheetsUIPlugin } from '@univerjs/sheets-ui';
const divRef = ref();
onMounted(() => {
const univer = new Univer({
locale: LocaleType.en_US,
theme: defaultTheme,
});
univer.registerPlugin(UniverRenderEnginePlugin);
univer.registerPlugin(UniverFormulaEnginePlugin);
univer.registerPlugin(UniverUIPlugin, {
container: divRef.value,
});
univer.registerPlugin(UniverDocsPlugin, {
hasScroll: false,
});
univer.registerPlugin(UniverDocsUIPlugin);
univer.registerPlugin(UniverSheetsPlugin);
univer.registerPlugin(UniverSheetsUIPlugin);
univer.registerPlugin(UniverSheetsFormulaPlugin);
univer.createUnit(UniverInstanceType.UNIVER_SHEET, {});
});
</script>

5
io.sc.platform.core.frontend/template-project/src/views/testcase/form/form.vue

@ -404,10 +404,5 @@ const reloadExecutor = () => {
});
};
//
eventBus.on('io.sc.platform.scheduler.manager.ExecutorChanged', () => {
reloadExecutor();
});
reloadExecutor();
</script>

133
io.sc.platform.core.frontend/template-project/src/views/testcase/math/MathEditor.vue

@ -0,0 +1,133 @@
<template>
<div>
<w-expression v-model="formula3"></w-expression>
</div>
</template>
<script setup lang="ts">
const formula = '';
const formula2 = 'hello world';
const formula3 = `
<mrow>
<mi>0.12</mi>
<mo>&#xD7;</mo>
<mrow>
<msqrt>
<mi>x</mi>
</msqrt>
<mo>(</mo>
<mrow>
<mfrac>
<mrow>
<mspace></mspace>
<mi>1</mi>
<mo>-</mo>
<mrow>
<mfrac>
<mrow><mi>1</mi></mrow>
<mrow>
<msup>
<mi>e</mi>
<mi>
<mrow>
<mo>(</mo>
<mi>50</mi>
<mo>&#xD7;</mo>
<mi>PD</mi>
<mo>)</mo>
</mrow>
</mi>
</msup>
</mrow>
</mfrac>
</mrow>
</mrow>
<mrow>
<mi>1</mi>
<mo>-</mo>
<mrow>
<mfrac>
<mrow><mi>1</mi></mrow>
<mrow>
<msup>
<mi>e</mi>
<mi>50</mi>
</msup>
</mrow>
</mfrac>
</mrow>
</mrow>
</mfrac>
</mrow>
<mo>)</mo>
</mrow>
<mo>+</mo>
<mi>0.24</mi>
<mo>&#xD7;</mo>
<mrow>
<mo>(</mo>
<mi>1</mi>
<mo>-</mo>
<mrow>
<mfrac>
<mrow>
<mi>1</mi>
<mo>-</mo>
<mrow>
<mfrac>
<mrow><mi>1</mi></mrow>
<mrow>
<msup>
<mi>e</mi>
<mi>
<mrow>
<mo>(</mo>
<mi>50</mi>
<mo>&#xD7;</mo>
<mi>PD</mi>
<mo>)</mo>
</mrow>
</mi>
</msup>
</mrow>
</mfrac>
</mrow>
</mrow>
<mrow>
<mi>1</mi>
<mo>-</mo>
<mrow>
<mfrac>
<mrow><mi>1</mi></mrow>
<mrow>
<msup>
<mi>e</mi>
<mi>50</mi>
</msup>
</mrow>
</mfrac>
</mrow>
</mrow>
</mfrac>
</mrow>
<mo>)</mo>
</mrow>
<mo>-</mo>
<mi>0.04</mi>
<mo>&#xD7;</mo>
<mrow>
<mo>(</mo>
<mi>1</mi>
<mo>-</mo>
<mfrac>
<mrow>
<mi>S</mi>
<mo>-</mo>
<mi>3</mi>
</mrow>
<mi>27</mi>
</mfrac>
<mo>)</mo>
</mrow>
</mrow>
`;
</script>

4
io.sc.platform.core.frontend/template-project/src/views/testcase/route/NoMenuRoute.vue

@ -0,0 +1,4 @@
<template>
<div class="py-10">路由参数: {{ $route.params }}</div>
</template>
<script setup lang="ts"></script>

12
io.sc.platform.core.frontend/template-project/src/views/testcase/route/OpenNoMenuRoute.vue

@ -0,0 +1,12 @@
<template>
<div class="row justify-center py-10">
<q-btn
label="在标签中打开非菜单路由"
:to="{
name: 'route.testcase.noMenuRoute',
params: { id: 'xxx' },
}"
></q-btn>
</div>
</template>
<script setup lang="ts"></script>

442
io.sc.platform.core.frontend/template-project/src/views/testcase/tag/tag.vue

@ -1,44 +1,406 @@
<template>
<w-grid
ref="gridRef"
:height="300"
:title="$t('lcdp.scheduler.executor.selectOnlineExecutorDialog.grid.title')"
selection="multiple"
:full-screen-button="false"
:toolbar-configure="{ noIcon: false }"
:toolbar-actions="['refresh']"
:auto-fetch-data="true"
:pageable="true"
:fetch-data-url="Environment.apiContextPath('/api/scheduler/manager/executorRegistry')"
:columns="[
{
width: 150,
name: 'online',
label: $t('status'),
format: (value) => {
return {
componentType: 'w-enums-tag',
attrs: {
value: value,
dense: true,
items: [
{ value: true, color: 'green', label: $t('online') },
{ value: false, color: 'grey', label: $t('offline') },
],
},
};
},
},
{ width: 150, name: 'applicationName', label: $t('lcdp.scheduler.executor.selectOnlineExecutorDialog.grid.entity.applicationName') },
{ width: 100, name: 'name', label: $t('lcdp.scheduler.executor.selectOnlineExecutorDialog.grid.entity.name') },
{ width: 150, name: 'url', label: $t('url') },
{ width: 100, name: 'registDate', label: t('lcdp.scheduler.executor.selectOnlineExecutorDialog.grid.entity.registDate') },
]"
></w-grid>
<div style="height: 100%">
<w-grid
:title="$t('lcdp.scheduler.task.grid.title')"
:config-button="true"
selection="multiple"
:checkbox-selection="true"
:query-form-cols-num="12"
:query-form-fields="[
{ colSpan: 4, name: 'name', label: $t('name'), type: 'text' },
{
colSpan: 4,
name: 'executor',
label: $t('lcdp.scheduler.task.grid.entity.executor'),
type: 'select',
clearable: true,
queryOperator: 'equals',
options: executorOptionsRef,
},
{
colSpan: 2,
name: 'status',
label: $t('status'),
type: 'select',
clearable: true,
queryOperator: 'equals',
options: Options.enum(Enums.TaskStatus),
},
]"
:data-url="Environment.apiContextPath('/api/scheduler/manager/task')"
:pageable="false"
:toolbar-configure="{ noIcon: false }"
:toolbar-actions="[
'query',
'refresh',
'separator',
'add',
'edit',
'remove',
'separator',
{
name: 'execute',
label: $t('lcdp.scheduler.task.grid.toolbar.execute'),
icon: 'bi-caret-right-fill',
enableIf: (selecteds) => {
return selecteds && selecteds.length > 0;
},
click: () => {},
},
{
name: 'schedule',
label: $t('lcdp.scheduler.task.grid.toolbar.schedule'),
icon: 'bi-cloud-arrow-up',
enableIf: (selecteds) => {
return selecteds && selecteds.length > 0;
},
click: () => {},
},
{
name: 'log',
label: $t('lcdp.scheduler.task.grid.toolbar.log'),
icon: 'bi-receipt',
enableIf: (selecteds) => {
return selecteds && selecteds.length > 0;
},
click: () => {},
},
'separator',
'view',
'separator',
'export',
]"
:columns="[
{
width: 80,
name: 'status',
label: $t('status'),
align: 'center',
format: (value) => {
return {
componentType: 'QChip',
attrs: { color: value == 'RUNNING' ? 'green' : 'gray', label: Formater.enum(Enums.TaskStatus)(value), dense: true },
};
},
},
{
width: 90,
name: 'type',
label: $t('lcdp.scheduler.task.grid.entity.type'),
format: Formater.enum(Enums.TaskType),
},
{ width: 150, name: 'name', label: $t('name') },
{
width: 100,
name: 'scheduleType',
label: $t('lcdp.scheduler.task.grid.entity.scheduleType'),
format: (value, row) => {
console.log(row);
return Formater.enum(Enums.ScheduleType)(value);
},
},
{
width: 140,
name: 'triggerLastTime',
label: $t('lcdp.scheduler.task.grid.entity.triggerLastTime'),
},
{ width: 140, name: 'triggerNextTime', label: $t('lcdp.scheduler.task.grid.entity.triggerNextTime') },
{
width: 400,
name: 'executorName',
label: $t('lcdp.scheduler.task.grid.entity.executorName'),
format: (value, row) => {
return {
componentType: 'q-btn',
attrs: {
flat: true,
rounded: true,
noCaps: true,
label: row.executorApplicationName + ':' + row.executorName,
color: 'blue',
onClick: () => {
executorRegistryDialogRef.open(row);
},
},
};
},
},
{ width: 80, name: 'author', label: $t('lcdp.scheduler.task.grid.entity.author') },
{ width: 200, name: 'alarmEmail', label: $t('lcdp.scheduler.task.grid.entity.alarmEmail') },
{
width: 100,
name: 'routeStrategy',
label: $t('lcdp.scheduler.task.grid.entity.routeStrategy'),
format: (value) => {
return Formater.enum(Enums.RouteStrategy)(value);
},
},
{
width: 100,
name: 'expirationPolicy',
label: $t('lcdp.scheduler.task.grid.entity.expirationPolicy'),
format: (value) => {
return Formater.enum(Enums.ExpirationPolicy)(value);
},
},
{
width: 100,
name: 'blockStrategy',
label: $t('lcdp.scheduler.task.grid.entity.blockStrategy'),
format: (value) => {
return Formater.enum(Enums.BlockStrategy)(value);
},
},
{ width: 80, name: 'timeout', label: $t('lcdp.scheduler.task.grid.entity.timeout') },
{ width: 90, name: 'failRetryCount', label: $t('lcdp.scheduler.task.grid.entity.failRetryCount') },
]"
:editor="{
dialog: {
width: '800px',
},
form: {
colsNum: 2,
fields: [
{
name: 'type',
label: $t('lcdp.scheduler.task.grid.entity.type'),
required: true,
type: 'select',
options: Options.enum(Enums.TaskType),
},
{
name: 'executor',
label: $t('lcdp.scheduler.task.grid.entity.executor'),
required: true,
type: 'select',
clearable: true,
options: executorOptionsRef,
},
{ colsFirst: true, name: 'name', label: $t('name'), type: 'text', required: true },
{ name: 'description', label: $t('description'), type: 'text' },
{ name: 'author', label: $t('lcdp.scheduler.task.grid.entity.author'), type: 'text' },
{ name: 'alarmEmail', label: $t('lcdp.scheduler.task.grid.entity.alarmEmail'), type: 'text' },
{
colsFirst: true,
name: 'scheduleType',
label: $t('lcdp.scheduler.task.grid.entity.scheduleType'),
required: true,
type: 'select',
options: Options.enum(Enums.ScheduleType),
defaultValue: 'FIX_RATE',
},
{
name: 'scheduleCron',
label: $t('lcdp.scheduler.task.grid.entity.scheduleCron'),
type: 'text',
showIf: (arg) => {
return arg.form.getFieldValue('scheduleType') === 'CRON';
},
},
{
name: 'scheduleFixRate',
label: $t('lcdp.scheduler.task.grid.entity.scheduleFixRate'),
type: 'number',
showIf: (arg) => {
return arg.form.getFieldValue('scheduleType') === 'FIX_RATE';
},
},
{
name: 'scheduleFixDelay',
label: $t('lcdp.scheduler.task.grid.entity.scheduleFixDelay'),
type: 'number',
showIf: (arg) => {
return arg.form.getFieldValue('scheduleType') === 'FIX_DELAY';
},
},
{
colsFirst: true,
colSpan: 2,
name: 'bean',
label: $t('lcdp.scheduler.task.grid.entity.type.bean'),
type: 'text',
showIf: (arg) => {
return arg.form.getFieldValue('type') === 'BEAN';
},
},
{
colsFirst: true,
colSpan: 2,
name: 'groovy',
label: $t('lcdp.scheduler.task.grid.entity.type.groovy'),
type: 'code-mirror',
rows: 8,
lang: 'groovy',
showIf: (arg) => {
return arg.form.getFieldValue('type') === 'GROOVY';
},
},
{
colsFirst: true,
colSpan: 2,
name: 'shell',
label: $t('lcdp.scheduler.task.grid.entity.type.shell'),
type: 'code-mirror',
rows: 8,
lang: 'shell',
showIf: (arg) => {
return arg.form.getFieldValue('type') === 'SHELL';
},
},
{
colsFirst: true,
colSpan: 2,
name: 'python',
label: $t('lcdp.scheduler.task.grid.entity.type.python'),
type: 'code-mirror',
rows: 8,
lang: 'python',
showIf: (arg) => {
return arg.form.getFieldValue('type') === 'PYTHON';
},
},
{
colsFirst: true,
colSpan: 2,
name: 'php',
label: $t('lcdp.scheduler.task.grid.entity.type.php'),
type: 'code-mirror',
rows: 8,
lang: 'php',
showIf: (arg) => {
return arg.form.getFieldValue('type') === 'PHP';
},
},
{
colsFirst: true,
colSpan: 2,
name: 'nodejs',
label: $t('lcdp.scheduler.task.grid.entity.type.nodejs'),
type: 'code-mirror',
rows: 8,
lang: 'nodejs',
showIf: (arg) => {
return arg.form.getFieldValue('type') === 'NODEJS';
},
},
{
colsFirst: true,
colSpan: 2,
name: 'powershell',
label: $t('lcdp.scheduler.task.grid.entity.type.powershell'),
type: 'code-mirror',
rows: 8,
lang: 'powershell',
showIf: (arg) => {
return arg.form.getFieldValue('type') === 'POWERSHELL';
},
},
{ colSpan: 2, name: 'parameter', label: $t('lcdp.scheduler.task.grid.entity.parameter'), type: 'textarea', rows: 5 },
{
name: 'routeStrategy',
label: $t('lcdp.scheduler.task.grid.entity.routeStrategy'),
required: true,
type: 'select',
options: Options.enum(Enums.RouteStrategy),
defaultValue: 'FIRST',
},
{
name: 'expirationPolicy',
label: $t('lcdp.scheduler.task.grid.entity.expirationPolicy'),
required: true,
type: 'select',
options: Options.enum(Enums.ExpirationPolicy),
},
{
name: 'blockStrategy',
label: $t('lcdp.scheduler.task.grid.entity.blockStrategy'),
required: true,
type: 'select',
options: Options.enum(Enums.BlockStrategy),
},
{ colsFirst: true, name: 'timeout', label: $t('lcdp.scheduler.task.grid.entity.timeout.label'), type: 'number' },
{ name: 'failRetryCount', label: $t('lcdp.scheduler.task.grid.entity.failRetryCount.label'), type: 'number' },
],
},
}"
:viewer="{
panel: {
columnNum: 1,
fields: [
{ name: 'id', label: $t('id') },
{ name: 'parent', label: $t('parent') },
{ name: 'type', label: $t('lcdp.scheduler.task.grid.entity.type') },
{ name: 'executor', label: $t('lcdp.scheduler.task.grid.entity.executorId') },
{ name: 'executorName', label: $t('lcdp.scheduler.task.grid.entity.executorName') },
{ name: 'name', label: $t('name') },
{ name: 'description', label: $t('description') },
{ name: 'author', label: $t('lcdp.scheduler.task.grid.entity.author') },
{ name: 'alarmEmail', label: $t('lcdp.scheduler.task.grid.entity.alarmEmail') },
{ name: 'scheduleType', label: $t('lcdp.scheduler.task.grid.entity.scheduleType') },
{ name: 'scheduleConf', label: $t('lcdp.scheduler.task.grid.entity.scheduleConf') },
{ name: 'bean', label: $t('lcdp.scheduler.task.grid.entity.type.bean') },
{ name: 'groovy', label: $t('lcdp.scheduler.task.grid.entity.type.groovy') },
{ name: 'shell', label: $t('lcdp.scheduler.task.grid.entity.type.shell') },
{ name: 'python', label: $t('lcdp.scheduler.task.grid.entity.type.python') },
{ name: 'php', label: $t('lcdp.scheduler.task.grid.entity.type.php') },
{ name: 'nodejs', label: $t('lcdp.scheduler.task.grid.entity.type.nodejs') },
{ name: 'powershell', label: $t('lcdp.scheduler.task.grid.entity.type.powershell') },
{ name: 'parameter', label: $t('lcdp.scheduler.task.grid.entity.parameter') },
{ name: 'routeStrategy', label: $t('lcdp.scheduler.task.grid.entity.routeStrategy') },
{ name: 'expirationPolicy', label: $t('lcdp.scheduler.task.grid.entity.expirationPolicy') },
{ name: 'blockStrategy', label: $t('lcdp.scheduler.task.grid.entity.blockStrategy') },
{ name: 'timeout', label: $t('lcdp.scheduler.task.grid.entity.timeout') },
{ name: 'failRetryCount', label: $t('lcdp.scheduler.task.grid.entity.failRetryCount') },
{ name: 'triggerStatus', label: $t('lcdp.scheduler.task.grid.entity.triggerStatus') },
{ name: 'triggerLastTime', label: $t('lcdp.scheduler.task.grid.entity.triggerLastTime') },
{ name: 'triggerNextTime', label: $t('lcdp.scheduler.task.grid.entity.triggerNextTime') },
{ 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>
</div>
</template>
<script setup lang="ts">
import { Environment, $t } from '@/platform';
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
console.log(t('menu.testcase.formElements', { name: 'xxx' }, '000'));
import { ref, inject } from 'vue';
import { axios, Environment, EnumTools, Formater, Options } from '@/platform';
const eventBus = inject('eventBus');
const Enums = await EnumTools.fetch([
'io.sc.platform.scheduler.core.enums.RouteStrategy',
'io.sc.platform.scheduler.core.enums.ExpirationPolicy',
'io.sc.platform.scheduler.core.enums.BlockStrategy',
'io.sc.platform.scheduler.core.enums.TaskType',
'io.sc.platform.scheduler.core.enums.ScheduleType',
'io.sc.platform.scheduler.core.enums.TaskStatus',
]);
const executorRegistryDialogRef = ref();
const executorOptionsRef = ref([]);
const reloadExecutor = () => {
axios.get(Environment.apiContextPath('/api/scheduler/manager/executor?pageable=false')).then((response) => {
executorOptionsRef.value.splice(0, executorOptionsRef.value.length);
const list = response?.data?.content;
if (list) {
for (const item of list) {
executorOptionsRef.value.push({ label: item.applicationName + ' : ' + item.name, value: item.id });
}
}
});
};
reloadExecutor();
</script>

53
io.sc.platform.core.frontend/template-project/src/views/testcase/word/Word.vue

@ -0,0 +1,53 @@
<template>
<div ref="divRef" class="border border-gray-200" style="height: 100%"></div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import '@univerjs/design/lib/index.css';
import '@univerjs/ui/lib/index.css';
import '@univerjs/docs-ui/lib/index.css';
import '@univerjs/sheets-ui/lib/index.css';
import '@univerjs/sheets-formula/lib/index.css';
import { Univer, UniverInstanceType, LocaleType } from '@univerjs/core';
import { defaultTheme } from '@univerjs/design';
import { UniverFormulaEnginePlugin } from '@univerjs/engine-formula';
import { UniverRenderEnginePlugin } from '@univerjs/engine-render';
import { UniverUIPlugin } from '@univerjs/ui';
import { UniverDocsPlugin } from '@univerjs/docs';
import { UniverDocsUIPlugin } from '@univerjs/docs-ui';
import { UniverSheetsPlugin } from '@univerjs/sheets';
import { UniverSheetsFormulaPlugin } from '@univerjs/sheets-formula';
import { UniverSheetsUIPlugin } from '@univerjs/sheets-ui';
const divRef = ref();
onMounted(() => {
const univer = new Univer({
locale: LocaleType.en_US,
theme: defaultTheme,
});
univer.registerPlugin(UniverRenderEnginePlugin);
univer.registerPlugin(UniverFormulaEnginePlugin);
univer.registerPlugin(UniverUIPlugin, {
container: divRef.value,
});
univer.registerPlugin(UniverDocsPlugin, {
hasScroll: false,
});
univer.registerPlugin(UniverDocsUIPlugin);
univer.registerPlugin(UniverSheetsPlugin);
univer.registerPlugin(UniverSheetsUIPlugin);
univer.registerPlugin(UniverSheetsFormulaPlugin);
univer.createUnit(UniverInstanceType.UNIVER_DOC, {});
});
</script>

7
io.sc.platform.core.frontend/template-project/webpack.config.common.cjs

@ -154,6 +154,11 @@ module.exports = {
'@': path.resolve(__dirname, 'src'),
},
// 设置支持的模块扩展名,即这些扩展名的文件可以作为模块被使用
extensions: ['.ts', '.js', '.cjs', '.vue']
extensions: ['.ts', '.js', '.cjs', '.vue'],
fallback: {
"fs": false,
"os": false,
"path": false,
}
},
};

7
io.sc.platform.core.frontend/webpack.config.common.cjs

@ -154,6 +154,11 @@ module.exports = {
'@': path.resolve(__dirname, 'src'),
},
// 设置支持的模块扩展名,即这些扩展名的文件可以作为模块被使用
extensions: ['.ts', '.js', '.cjs', '.vue']
extensions: ['.ts', '.js', '.cjs', '.vue'],
fallback: {
"fs": false,
"os": false,
"path": false,
}
},
};

4
io.sc.platform.core/src/main/java/io/sc/platform/core/DirectoryManager.java

@ -2,6 +2,7 @@ package io.sc.platform.core;
import io.sc.platform.core.plugins.PluginManager;
import io.sc.platform.core.plugins.item.Directory;
import io.sc.platform.core.util.PathUtil;
import org.springframework.core.env.ConfigurableEnvironment;
import java.util.HashMap;
@ -37,6 +38,9 @@ public class DirectoryManager {
}
}
public String getTempDir() {
return PathUtil.standardized(System.getProperty("java.io.tmpdir"));
}
public String getHomeDir(){
return homeDir;

10
io.sc.platform.core/src/main/java/io/sc/platform/core/Environment.java

@ -3,6 +3,7 @@ package io.sc.platform.core;
import io.sc.platform.core.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import java.io.IOException;
import java.nio.charset.Charset;
@ -19,6 +20,7 @@ public class Environment {
public static final String KEY_IS_RUNNING_IN_DEVELOPMENT ="application.is-running-in-development";
public static final String KEY_IS_RUNNING_IN_WEB_CONTAINER ="application.is-running-in-web-container";
private ApplicationContext applicationContext;
private String applicationName;
private boolean isRunningInDevelopment;
private boolean isRunningInWebContainer;
@ -49,6 +51,14 @@ public class Environment {
this.setRunningInDevelopment(properties.getProperty("development",Boolean.class,false));
}
public ApplicationContext getApplicationContext() {
return applicationContext;
}
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public String getApplicationName() {
return applicationName;
}

67
io.sc.platform.core/src/main/java/io/sc/platform/core/autoconfigure/RestTemplateAutoConfiguration.java

@ -1,10 +1,13 @@
package io.sc.platform.core.autoconfigure;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.*;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter;
@ -12,9 +15,15 @@ import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConve
import org.springframework.http.converter.xml.SourceHttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
@Configuration(proxyBeanMethods = false)
public class RestTemplateAutoConfiguration {
@ -46,6 +55,9 @@ public class RestTemplateAutoConfiguration {
converters.remove(removed);
}
}
// List<ClientHttpRequestInterceptor> interceptors =new ArrayList<>();
// interceptors.add(new RestTemplateInterceptor());
// restTemplate.setInterceptors(interceptors);
return restTemplate;
}
@ -57,4 +69,55 @@ public class RestTemplateAutoConfiguration {
factory.setConnectTimeout(5000);
return factory;
}
static class RestTemplateInterceptor implements ClientHttpRequestInterceptor {
private static final Logger log = LoggerFactory.getLogger(RestTemplateInterceptor.class);
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
displayRequest(request,body);
ClientHttpResponse response =execution.execute(request,body);
displayResponse(response);
return response;
}
private void displayRequest(HttpRequest request,byte[] body){
log.debug("====request info====");
log.debug("URI : {}", request.getURI());
log.debug("Method : {}", request.getMethod());
log.debug("Req Headers : {}", getHeadersString(request.getHeaders()));
log.debug("Request body: {}", body == null ? "" : new String(body, StandardCharsets.UTF_8));
}
private void displayResponse(ClientHttpResponse response) throws IOException {
StringBuilder inputStringBuilder = new StringBuilder();
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(response.getBody(), StandardCharsets.UTF_8))) {
String line = bufferedReader.readLine();
while (line != null) {
inputStringBuilder.append(line);
inputStringBuilder.append('\n');
line = bufferedReader.readLine();
}
}
log.debug("====response info====");
log.debug("Status code : {}", response.getStatusCode());
log.debug("Status text : {}", response.getStatusText());
log.debug("Resp Headers : {}", getHeadersString(response.getHeaders()));
log.debug("Response body: {}", inputStringBuilder.toString());
}
private String getHeadersString(HttpHeaders httpHeaders){
if (Objects.isNull(httpHeaders)) {
return "[]";
}
return httpHeaders.entrySet().stream()
.map(entry -> {
List<String> values = entry.getValue();
return "\t" + entry.getKey() + ":" + (values.size() == 1 ?
"\"" + values.get(0) + "\"" :
values.stream().map(s -> "\"" + s + "\"").collect(Collectors.joining(", ")));
})
.collect(Collectors.joining(", \n", "\n[\n", "\n]\n"));
}
}
}

23
io.sc.platform.core/src/main/java/io/sc/platform/core/bean/FirstApplicationInitializer.java

@ -0,0 +1,23 @@
package io.sc.platform.core.bean;
import io.sc.platform.core.Environment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Component
@Order(Integer.MIN_VALUE + 100)
public class FirstApplicationInitializer implements ApplicationRunner {
@Autowired
private ApplicationContext applicationContext;
@Override
public void run(ApplicationArguments args) throws Exception {
Environment.getInstance().setApplicationContext(applicationContext);
}
}

37
io.sc.platform.core/src/main/java/io/sc/platform/core/util/DateUtil.java

@ -4,6 +4,10 @@ import java.text.DateFormat;
import java.text.DateFormatSymbols;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
@ -65,15 +69,6 @@ public class DateUtil {
+ yyyy_MM_dd_HH_mm + "\n"
+ yyyy_MM_dd_HH + "\n"
+ yyyy_MM_dd + "\n";
/**
* 获取当前日期的 java.sql.Date
* @return 当前日期的 java.sql.Date
*/
public static java.sql.Date sqlDate(){
Date date =new Date();
return new java.sql.Date(date.getTime());
}
/**
* 获取日期格式化对象
@ -144,6 +139,13 @@ public class DateUtil {
// nothing to do
}
}
try{
long time =Long.parseLong(str);
return new Date(time);
} catch (NumberFormatException e){
// nothing to do
}
throw new ParseException(SUPPORTED_DATE_FORMAT_EXCEPTION_MESSAGE,0);
}
@ -245,6 +247,15 @@ public class DateUtil {
c.set(Calendar.SECOND, 0);
return c.getTime();
}
/**
* 获取当前日期的 java.sql.Date
* @return 当前日期的 java.sql.Date
*/
public static java.sql.Date sqlDate(){
Date date =new Date();
return new java.sql.Date(date.getTime());
}
/**
* java.lang.Date 对象转换成 java.sql.Date 对象
@ -254,4 +265,12 @@ public class DateUtil {
public static java.sql.Date toSqlDate(Date date){
return new java.sql.Date(date.getTime());
}
public static LocalDateTime toLocalDateTime(Date date){
return LocalDateTime.ofInstant(Instant.ofEpochMilli(date.getTime()),ZoneId.systemDefault());
}
public static Date fromLocalDateTime(LocalDateTime localDateTime){
return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
}
}

16
io.sc.platform.core/src/main/java/io/sc/platform/core/util/InputStreamUtil.java

@ -0,0 +1,16 @@
package io.sc.platform.core.util;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import java.io.InputStream;
public class InputStreamUtil {
public static InputStream getInputStream(String url) throws Exception {
Resource resource =new DefaultResourceLoader().getResource(url);
if(resource==null || !resource.exists()){
throw new RuntimeException(url + " NOT exists");
}
return resource.getInputStream();
}
}

23
io.sc.platform.core/src/main/java/io/sc/platform/core/util/PathUtil.java

@ -0,0 +1,23 @@
package io.sc.platform.core.util;
import org.springframework.util.StringUtils;
public class PathUtil {
public static String standardized(String path){
if (path!=null) {
String _url = path.replace('\\','/');
while (_url.endsWith("/")) {
_url = _url.substring(0, _url.length() - 1);
}
return _url;
}
return "";
}
public static void main(String[] args) {
System.out.println(standardized("a/b/c/"));
System.out.println(standardized("a/b/c"));
System.out.println(standardized("/a/b/c/"));
System.out.println(standardized("/a/b/c"));
}
}

35
io.sc.platform.core/src/main/java/io/sc/platform/core/util/PinyinUtil.java

@ -1,6 +1,7 @@
package io.sc.platform.core.util;
import net.sourceforge.pinyin4j.PinyinHelper;
import org.springframework.util.StringUtils;
/**
* 汉语拼音辅助类
@ -8,6 +9,40 @@ import net.sourceforge.pinyin4j.PinyinHelper;
*
*/
public class PinyinUtil {
public static String pinyin(String str){
if(StringUtils.hasText(str)){
StringBuilder sb =new StringBuilder();
for(int i=0;i<str.length();i++){
String[] pinyins =PinyinHelper.toHanyuPinyinStringArray(str.charAt(i));
for(String pinyin : pinyins){
sb.append(pinyin);
}
}
return sb.toString();
}
return "";
}
public static String pinyinFirstLetter(String str){
if(StringUtils.hasText(str)){
StringBuilder sb =new StringBuilder();
for(int i=0;i<str.length();i++){
String[] pinyins =PinyinHelper.toHanyuPinyinStringArray(str.charAt(i));
if(pinyins==null){
sb.append(str.charAt(i));
}else {
sb.append(pinyins[0].substring(0, 1).toUpperCase());
}
}
return sb.toString();
}
return "";
}
public static void main(String[] args) {
System.out.println(pinyinFirstLetter("兴银合盈债券A"));
}
/**
* 采用汉语拼音比较两个字符串
* @param o1 字符串1

1
io.sc.platform.core/src/main/resources/META-INF/spring.factories

@ -10,7 +10,6 @@ io.sc.platform.core.autoconfigure.RestarterInterceptorAutoConfiguration,\
io.sc.platform.core.autoconfigure.RestTemplateAutoConfiguration,\
io.sc.platform.core.autoconfigure.StringEncryptorAutoConfiguration
# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
io.sc.platform.core.springboot.BeforeEnvironmentProcessor

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

@ -225,4 +225,5 @@ copy=Copy
paste=Paste
cut =Cut
online=Online
offline=Offline
offline=Offline
finish=Finish

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

@ -225,4 +225,5 @@ copy=\u8907\u88FD
paste=\u7C98\u8CBC
cut =\u526A\u5207
online=\u5728\u7DDA
offline=\u96E2\u7DDA
offline=\u96E2\u7DDA
finish=\u5B8C\u6210

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

@ -225,4 +225,5 @@ copy=\u590D\u5236
paste=\u7C98\u8D34
cut =\u526A\u5207
online=\u5728\u7EBF
offline=\u79BB\u7EBF
offline=\u79BB\u7EBF
finish=\u5B8C\u6210

108
io.sc.platform.developer.doc/asciidoc/9999-appendix/oauth2/oauth2.adoc

@ -6,11 +6,109 @@ OAuth2 协议允许用户将认证与授权交给一个独立的第三方进行
OAuth2可以提供一个统一的认证服务。
== 应用场景
=== 快递员问题
我住在一个大型的居民小区, 小区有门禁系统, 进入的时候需要输入密码。
我经常网购和外卖,每天都有快递员来送货, 我必须找到一个办法,让快递员通过门禁系统,进入小区。
如果我把自己的密码,告诉快递员,他就拥有了与我同样的权限,这样好像不太合适。
万一我想取消他进入小区的权力,也很麻烦,我自己的密码也得跟着改了,还得通知其他的快递员。
有没有一种办法,让快递员能够自由进入小区,又不必知道小区居民的密码,而且他的唯一权限就是送货,其他需要密码的场合,他都没有权限?
于是,我设计了一套授权机制:
. 第一步,门禁系统的密码输入器下面,增加一个按钮,叫做"获取授权"。快递员需要首先按这个按钮,去申请授权。
. 第二步,他按下按钮以后,屋主(也就是我)的手机就会跳出对话框:有人正在要求授权。系统还会显示该快递员的姓名、工号和所属的快递公司。
. 我确认请求属实,就点击按钮,告诉门禁系统,我同意给予他进入小区的授权。
. 第三步,门禁系统得到我的确认以后,向快递员显示一个进入小区的令牌(access token)。令牌就是类似密码的一串数字,只在短期内(比如七天)有效。
. 第四步,快递员向门禁系统输入令牌,进入小区。
有人可能会问,为什么不是远程为快递员开门,而要为他单独生成一个令牌?这是因为快递员可能每天都会来送货,
第二天他还可以复用这个令牌。另外,有的小区有多重门禁,快递员可以使用同一个令牌通过它们。
我们把上面的例子搬到互联网,就是 OAuth 的设计了。
首先,居民小区就是储存用户数据的网络服务。比如,微信储存了我的好友信息,获取这些信息,就必须经过微信的"门禁系统"。
其次,快递员(或者说快递公司)就是第三方应用,想要穿过门禁系统,进入小区。
最后,我就是用户本人,同意授权第三方应用进入小区,获取我的数据。
简单说,OAuth 就是一种授权机制。数据的所有者告诉系统,同意授权第三方应用进入系统,获取这些数据。
系统从而产生一个短期的进入令牌(token),用来代替密码,供第三方应用使用。
=== "云冲印"网站
有一个"云冲印"的网站,可以将用户储存在Google的照片,冲印出来。用户为了使用该服务,必须让"云冲印"读取自己储存在Google上的照片。
问题是只有得到用户的授权,Google才会同意"云冲印"读取这些照片。那么,"云冲印"怎样获得用户的授权呢?
传统方法是,用户将自己的Google用户名和密码,告诉"云冲印",后者就可以读取用户的照片了。这样的做法有以下几个严重的缺点。
. "云冲印"为了后续的服务,会保存用户的密码,这样很不安全。
. Google不得不部署密码登录,而我们知道,单纯的密码登录并不安全。
. "云冲印"拥有了获取用户储存在Google所有资料的权力,用户没法限制"云冲印"获得授权的范围和有效期。
. 用户只有修改密码,才能收回赋予"云冲印"的权力。但是这样做,会使得其他所有获得用户授权的第三方应用程序全部失效。
. 只要有一个第三方应用程序被破解,就会导致用户密码泄漏,以及所有被密码保护的数据泄漏。
OAuth就是为了解决上面这些问题而诞生的。
OAuth在"客户端"与"服务提供商"之间,设置了一个授权层(authorization layer)。"客户端"不能直接登录"服务提供商",只能登录授权层,
以此将用户与客户端区分开来。"客户端"登录授权层所用的令牌(token),与用户的密码不同。用户可以在登录的时候,指定授权层令牌的权限范围和有效期。
"客户端"登录授权层以后,"服务提供商"根据令牌的权限范围和有效期,向"客户端"开放用户储存的资料。
== 模块构成
* Resource owner (资源拥有者): 拥有该资源的服务或用户,如我们自己或者资源网站
* Authorization server (认证服务器) : 即用来认证与颁发令牌(如token)的服务
* Resource server (资源服务器) : 拥有资源的服务,如我们要访问的网站
* Client (客户端) : 即访问的客户端,如我们自己用的访问网站
* Client (客户端/第三方应用程序) : 即访问的客户端,如我们自己用的访问网站
== 运行流程
image::9999-appendix/oauth2/001.png[]
* (A) 用户打开客户端以后,客户端要求用户给予授权。
* (B) 用户同意给予客户端授权。
* (C) 客户端使用上一步获得的授权,向认证服务器申请令牌。
* (D) 认证服务器对客户端进行认证以后,确认无误,同意发放令牌。
* (E) 客户端使用令牌,向资源服务器申请获取资源。
* (F) 资源服务器确认令牌无误,同意向客户端开放资源。
不难看出来,上面六个步骤之中,B 是关键,即用户怎样才能给于客户端授权。有了这个授权以后,客户端就可以获取令牌,进而凭令牌获取资源。
== Oauth 2.1 的进化过程
* 2010年 OAuth 授权规范 1.0 (rfc 5849) 版本发布。
* 2012年 更简单易用的 OAuth 2.0 规范发布(rfc 6749)版本发布。
* 到现在, 网络和移动领域发生了巨大的变化, 当时发布的授权协议标准已经远远不能满足现在的场景和需求, 为了应对这种不断变化的局面, OAuth 社区多年来一直在修补和扩展 OAuth 规范, OAuth 的格局也不断扩大, 越来越多的围绕 OAuth 2.0 core 的扩展授权规范出现, 也让 OAuth 2.0 整体看起来就像一个迷宫一样。
image::9999-appendix/oauth2/002.png[,50%]
在 OAuth 2.0 核心规范 (RFC 6749)中, 定义了四种授权类型:授权码、隐式、密码和客户端凭据, 如下:
image::9999-appendix/oauth2/003.png[,30%]
在 OAuth 2.0 中,最安全也是使用最普遍的就是授权码模式, 而对于本地应用,移动应用来说, 通常会使用隐式和密码授权, 这两种本身就是不安全的, 因为这些属于公开的客户端, 本身没有能力保护客户端机密, 但是当时并没有其它好的方案。
为了解决 OAuth 2.0 对公开客户端的授权安全问题, PKCE (RFC 6379)协议应运而生, 全称是 Proof Key for Code Exchange,PKCE 的原理是, 对于公共的客户端, 如果不能使用客户端秘钥(client_secret), 那客户端就提供一个自创建的证明 (code_verifier) 给授权服务器,其中使用了加密算法, 授权服务器通过它来验证客户端。
image::9999-appendix/oauth2/004.png[,30%]
后来,"OAuth 2.0 for Native Apps"(RFC 8252)规范发布,推荐原生应用也使用授权码 + PKCE。
image::9999-appendix/oauth2/005.png[,30%]
随着技术不断地发展, 出现了设备授权的场景, 这里设备指智能电视,打印机等, 和传统的PC或者手机不同, 这种设备是缺少浏览器或者键盘的,那 OAuth 2.0 常规的授权模式肯定是不能满足的, 于是就出现了设备授权(Device Grant) 。
image::9999-appendix/oauth2/006.png[,30%]
在 OAuth 2.0 安全最佳实践(Security BCP)中, 弃用了隐式和密码授权,并且推荐所有的客户端都应该使用 Authorization Code + PKCE 的组合。
image::9999-appendix/oauth2/007.png[,30%]
最终, 调整后的 OAuth 授权模式会更加精简, 转换成下面三种, 这也是 OAuth 2.1 的思想, 参考安全最佳实践(BCP),取其精华, 去其糟粕。
image::9999-appendix/oauth2/008.png[,50%]
归根结底, OAuth 2.1 并不是要推翻 OAuth 2.0,而是根据其安全最佳实践(BCP), 移除不安全的授权流程, 并且对扩展协议进行整合, 让原本复杂如迷宫的 OAuth 2.0 规范成为更易用,更安全的授权规范。
== 授权方式
* authorization_code (授权码模式) : 最正规的模式,客户端先将用户导向认证服务器,登录后获取授权码,然后进行授权,最后根据授权码获取访问令牌
@ -69,7 +167,9 @@ private_key_jwt 和 client_secret_jwt 唯一的区别就是生成 JWT 的方式
| /connect/register | POST | oidc 客户端注册
|===
=== /oauth2/authorize
|===
| 参数名 | 参数类型 | 参数值 | 说明
| client_id | 请求 | |
| response_type | 请求 | code | 授权码
@ -85,9 +185,10 @@ private_key_jwt 和 client_secret_jwt 唯一的区别就是生成 JWT 的方式
| error | 响应 | |
| error_description | 响应 | |
| error_uri | 响应 | |
|===
=== /oauth2/token
|===
| 参数名 | 参数类型 | 参数值 | 说明
| client_id | 请求 | |
| client_secret | 请求 | |
@ -109,11 +210,14 @@ private_key_jwt 和 client_secret_jwt 唯一的区别就是生成 JWT 的方式
| error | 响应 | |
| error_description | 响应 | |
| error_uri | 响应 | |
|===
=== /oauth2/revoke
|===
| 参数名 | 参数类型 | 参数值 | 说明
| token | 请求 | |
| token_type_hint | 请求 | |
|===
=== 获取授权码
http://localhost:8080/oauth2/authorize?client_secret=secret&client_id=platform-oidc&response_type=code&redirect_uri=http://localhost:8080/oauth2/authorized-oidc

BIN
io.sc.platform.developer.doc/asciidoc/resources/images/9999-appendix/oauth2/001.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
io.sc.platform.developer.doc/asciidoc/resources/images/9999-appendix/oauth2/002.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 404 KiB

BIN
io.sc.platform.developer.doc/asciidoc/resources/images/9999-appendix/oauth2/003.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
io.sc.platform.developer.doc/asciidoc/resources/images/9999-appendix/oauth2/004.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

BIN
io.sc.platform.developer.doc/asciidoc/resources/images/9999-appendix/oauth2/005.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

BIN
io.sc.platform.developer.doc/asciidoc/resources/images/9999-appendix/oauth2/006.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

BIN
io.sc.platform.developer.doc/asciidoc/resources/images/9999-appendix/oauth2/007.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 KiB

BIN
io.sc.platform.developer.doc/asciidoc/resources/images/9999-appendix/oauth2/008.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

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

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

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

@ -27,6 +27,7 @@ import component_developer_plugin_Swagger from '@/views/plugin/Swagger.vue';
import component_developer_plugin_SystemProperties from '@/views/plugin/SystemProperties.vue';
import component_developer_backend_importLiquibase from '@/views/backend/ImportLiquibase.vue';
import component_developer_backend_exportLiquibase from '@/views/backend/ExportLiquibase.vue';
import component_developer_backend_sql from '@/views/backend/sql/Sql.vue';
import component_developer_frontend_Icons from '@/views/frontend/Icons.vue';
const localComponents = {
@ -55,6 +56,7 @@ const localComponents = {
'component.developer.plugin.SystemProperties': component_developer_plugin_SystemProperties,
'component.developer.backend.importLiquibase': component_developer_backend_importLiquibase,
'component.developer.backend.exportLiquibase': component_developer_backend_exportLiquibase,
'component.developer.backend.sql': component_developer_backend_sql,
'component.developer.frontend.Icons': component_developer_frontend_Icons,
};

23
io.sc.platform.developer.frontend/src/i18n/messages.json

@ -30,6 +30,7 @@
"menu.developer.backend" : "Back End Tools",
"menu.developer.backend.import.liquibase" : "Data Import",
"menu.developer.backend.export.liquibase" : "Data Export",
"menu.developer.backend.sql" : "SQL",
"menu.developer.frontend" : "Front End Tools",
"menu.developer.frontend.icons" : "Icons",
@ -40,5 +41,25 @@
"developer.backend.export.liquibase.datasource" : "Datasource",
"developer.backend.export.liquibase.schema" : "Schema",
"developer.backend.export.liquibase.tables" : "Tables",
"developer.backend.export.liquibase.export.tip" : "Are you sure to export?"
"developer.backend.export.liquibase.export.tip" : "Are you sure to export?",
"developer.backend.sql.datasource" : "Datasource",
"developer.backend.sql.schema" : "Schema",
"developer.backend.sql.action.execute" : "Execute",
"developer.backend.sql.action.executeAll" : "Execute All",
"developer.backend.sql.action.import" : "Import",
"developer.backend.sql.import.dialog.title" : "Data Import",
"developer.backend.sql.import.dialog.step.upload" : "Upload File",
"developer.backend.sql.import.dialog.step.upload.fileTip" : "Please select a .xlsx or .csv file",
"developer.backend.sql.import.dialog.step.dataView" : "View Data",
"developer.backend.sql.import.dialog.step.mapping" : "Table and field mapping",
"developer.backend.sql.import.dialog.step.mapping.table": "Table Name:",
"developer.backend.sql.import.dialog.step.mapping.fieldNameRowIndex" : "Field Name Row Index",
"developer.backend.sql.import.dialog.step.mapping.grid.entity.no" : "Excel No.",
"developer.backend.sql.import.dialog.step.mapping.grid.entity.fieldName" : "Field Name",
"developer.backend.sql.import.dialog.step.mapping.grid.entity.javaType" : "Field Type",
"developer.backend.sql.import.dialog.step.import" : "Import",
"developer.backend.sql.import.dialog.step.import.progress" : "Current Running Task:",
"developer.backend.sql.import.dialog.step.result" : "Result",
}

23
io.sc.platform.developer.frontend/src/i18n/messages_tw_CN.json

@ -30,6 +30,7 @@
"menu.developer.backend" : "後端工具",
"menu.developer.backend.import.liquibase" : "數據導入",
"menu.developer.backend.export.liquibase" : "數據導出",
"menu.developer.backend.sql" : "SQL",
"menu.developer.frontend" : "前端工具",
"menu.developer.frontend.icons" : "圖標庫",
@ -40,5 +41,25 @@
"developer.backend.export.liquibase.datasource" : "數據源",
"developer.backend.export.liquibase.schema" : "方案",
"developer.backend.export.liquibase.tables" : "表",
"developer.backend.export.liquibase.export.tip" : "您確定要導出嗎?"
"developer.backend.export.liquibase.export.tip" : "您確定要導出嗎?",
"developer.backend.sql.datasource" : "數據源",
"developer.backend.sql.schema" : "方案",
"developer.backend.sql.action.execute" : "執行",
"developer.backend.sql.action.executeAll" : "執行所有",
"developer.backend.sql.action.import" : "數據導入",
"developer.backend.sql.import.dialog.title" : "數據導入",
"developer.backend.sql.import.dialog.step.upload" : "上傳文件",
"developer.backend.sql.import.dialog.step.upload.fileTip" : "請選擇一個 .xlsx 或 .csv 文件",
"developer.backend.sql.import.dialog.step.dataView" : "查看數據",
"developer.backend.sql.import.dialog.step.mapping" : "表和字段映射",
"developer.backend.sql.import.dialog.step.mapping.table": "數據庫表名:",
"developer.backend.sql.import.dialog.step.mapping.fieldNameRowIndex" : "數據庫表字段名行號",
"developer.backend.sql.import.dialog.step.mapping.grid.entity.no" : "Excel 序號",
"developer.backend.sql.import.dialog.step.mapping.grid.entity.fieldName" : "字段名",
"developer.backend.sql.import.dialog.step.mapping.grid.entity.javaType" : "數據類型",
"developer.backend.sql.import.dialog.step.import" : "導入",
"developer.backend.sql.import.dialog.step.import.progress" : "當前正在執行的任務進度:",
"developer.backend.sql.import.dialog.step.result" : "導入結果",
}

23
io.sc.platform.developer.frontend/src/i18n/messages_zh_CN.json

@ -30,6 +30,7 @@
"menu.developer.backend" : "后端工具",
"menu.developer.backend.import.liquibase" : "数据导入",
"menu.developer.backend.export.liquibase" : "数据导出",
"menu.developer.backend.sql" : "SQL",
"menu.developer.frontend" : "前端工具",
"menu.developer.frontend.icons" : "图标库",
@ -40,5 +41,25 @@
"developer.backend.export.liquibase.datasource" : "数据源",
"developer.backend.export.liquibase.schema" : "方案",
"developer.backend.export.liquibase.tables" : "表",
"developer.backend.export.liquibase.export.tip" : "您确定要导出吗?"
"developer.backend.export.liquibase.export.tip" : "您确定要导出吗?",
"developer.backend.sql.datasource" : "数据源",
"developer.backend.sql.schema" : "方案",
"developer.backend.sql.action.execute" : "执行",
"developer.backend.sql.action.executeAll" : "执行所有",
"developer.backend.sql.action.import" : "数据导入",
"developer.backend.sql.import.dialog.title" : "数据导入",
"developer.backend.sql.import.dialog.step.upload" : "上传文件",
"developer.backend.sql.import.dialog.step.upload.fileTip" : "请选择一个 .xlsx 或 .csv 文件",
"developer.backend.sql.import.dialog.step.dataView" : "查看数据",
"developer.backend.sql.import.dialog.step.mapping" : "表和字段映射",
"developer.backend.sql.import.dialog.step.mapping.table": "数据库表名:",
"developer.backend.sql.import.dialog.step.mapping.fieldNameRowIndex" : "数据库表字段名行号",
"developer.backend.sql.import.dialog.step.mapping.grid.entity.no" : "Excel 序号",
"developer.backend.sql.import.dialog.step.mapping.grid.entity.fieldName" : "字段名",
"developer.backend.sql.import.dialog.step.mapping.grid.entity.javaType" : "数据类型",
"developer.backend.sql.import.dialog.step.import" : "导入",
"developer.backend.sql.import.dialog.step.import.progress" : "当前正在执行的任务进度:",
"developer.backend.sql.import.dialog.step.result" : "导入结果",
}

2
io.sc.platform.developer.frontend/src/menus/menus.json

@ -77,6 +77,8 @@
{"type":"ROUTE", "order":100, "parentId":"menu.developer.backend", "id":"menu.developer.backend.import.liquibase", "titleI18nKey":"menu.developer.backend.import.liquibase", "icon":"bi-database-up", "routeName":"route.developer.backend.import.liquibase"},
/*//*/
{"type":"ROUTE", "order":200, "parentId":"menu.developer.backend", "id":"menu.developer.backend.export.liquibase", "titleI18nKey":"menu.developer.backend.export.liquibase", "icon":"bi-database-down", "routeName":"route.developer.backend.export.liquibase"},
/*//SQL*/
{"type":"ROUTE", "order":200, "parentId":"menu.developer.backend", "id":"menu.developer.backend.sql", "titleI18nKey":"menu.developer.backend.sql", "icon":"bi-filetype-sql", "routeName":"route.developer.backend.sql"},
/*/*/
{"type":"GROUP", "order":600, "parentId":"menu.developer", "id":"menu.developer.frontend", "titleI18nKey":"menu.developer.frontend", "icon":"bi-layout-text-window"},

13
io.sc.platform.developer.frontend/src/routes/routes.json

@ -368,6 +368,19 @@
"meta": {
}
},
{
"name": "route.developer.backend.sql",
"path": "developer/backend/sql",
"parent": "/",
"priority": 0,
"module": "io.sc.platform.developer.frontend",
"component": "component.developer.backend.sql",
"componentPath": "@/views/backend/sql/Sql.vue",
"redirect": null,
"meta": {
}
},
{
"name": "route.developer.frontend.icons",
"path": "developer/frontend/icons",

1
io.sc.platform.developer.frontend/src/views/backend/ExportLiquibase.vue

@ -194,7 +194,6 @@ const exportData = (e) => {
};
onMounted(() => {
console.log('onMounted');
loadDatasource();
datasourceChanged('');
});

102
io.sc.platform.developer.frontend/src/views/backend/sql/Sql.vue

@ -0,0 +1,102 @@
<template>
<div>
<q-toolbar class="q-gutter-md py-2">
<q-select
v-model="valueReactive.datasource"
:label="$t('developer.backend.sql.datasource')"
outlined
dense
emit-value
map-options
:options="datasourceOptionsRef"
style="width: 200px"
@update:model-value="
(value) => {
datasourceChanged(value);
}
"
></q-select>
<q-select
v-model="valueReactive.schema"
:label="$t('developer.backend.sql.schema')"
outlined
dense
emit-value
map-options
:options="schemaOptionsRef"
style="width: 200px"
@update:model-value="
(value) => {
schemaChanged(valueReactive.datasource, value);
}
"
></q-select>
<q-space />
<q-btn :label="$t('developer.backend.sql.action.execute')" no-caps outline icon="bi-lightning" />
<q-btn :label="$t('developer.backend.sql.action.executeAll')" no-caps outline icon="bi-play" />
<q-btn
:label="$t('developer.backend.sql.action.import')"
no-caps
outline
icon="bi-filetype-xlsx"
@click="
() => {
importExcelDialogRef.open();
}
"
/>
</q-toolbar>
<div class="px-3">
<w-code-mirror label="SQL" :rows="10" lang="sql"></w-code-mirror>
</div>
<ImportExcel ref="importExcelDialogRef"></ImportExcel>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted, onUpdated } from 'vue';
import { useI18n } from 'vue-i18n';
import { axios, Environment, DialogManager, Downloader } from 'platform-core';
import ImportExcel from './import-excel/ImportExcel.vue';
const datasourceOptionsRef = ref([]);
const schemaOptionsRef = ref([]);
const importExcelDialogRef = ref();
const valueReactive = reactive({
datasource: undefined,
schema: undefined,
sql: undefined,
});
const loadDatasource = () => {
axios.get(Environment.apiContextPath('/api/system/datasource?pageable=false&sortBy=name')).then((response) => {
const data = response?.data.content;
const datasourceOptions = [];
if (data && data.length > 0) {
for (let item of data) {
datasourceOptions.push({ label: item.name, value: item.name });
}
}
datasourceOptionsRef.value = datasourceOptions;
});
};
const datasourceChanged = (datasource: string) => {
datasource = datasource || '';
axios.get(Environment.apiContextPath('/api/jdbc/metadata/getSchemas?datasource=' + datasource)).then((response) => {
const data = response?.data;
const schemaOptions = [];
if (data && data.length > 0) {
for (let item of data) {
schemaOptions.push({ label: item.name, value: item.name });
}
}
schemaOptionsRef.value = schemaOptions;
});
};
onMounted(() => {
loadDatasource();
datasourceChanged('');
});
</script>

93
io.sc.platform.developer.frontend/src/views/backend/sql/import-excel/ImportExcel.vue

@ -0,0 +1,93 @@
<template>
<w-dialog ref="dialogRef" width="1024px" :can-maximize="false" :title="$t('developer.backend.sql.import.dialog.title')">
<q-stepper ref="stepper" v-model="step" animated flat>
<q-step :done="step > 1" :name="1" :title="$t('developer.backend.sql.import.dialog.step.upload')" icon="bi-caret-right">
<StepUploadFile ref="stepStepUploadFileRef"></StepUploadFile>
</q-step>
<q-step :done="step > 2" :name="2" :title="$t('developer.backend.sql.import.dialog.step.dataView')" icon="bi-link">
<StepDataView ref="stepDataViewRef"></StepDataView>
</q-step>
<q-step :done="step > 3" :name="3" :title="$t('developer.backend.sql.import.dialog.step.mapping')" icon="bi-database-up">
<StepMapping ref="stepMappingRef"></StepMapping>
</q-step>
<q-step :done="step > 4" :name="4" :title="$t('developer.backend.sql.import.dialog.step.import')" icon="bi-database-up">
<StepImport ref="stepImportRef"></StepImport>
</q-step>
<q-step :done="step > 5" :name="5" :title="$t('developer.backend.sql.import.dialog.step.result')" icon="bi-info-lg"> </q-step>
<template #navigation>
<q-stepper-navigation class="row justify-end q-gutter-sm">
<q-btn v-if="step > 1" :label="$t('previousStep')" color="primary" no-caps @click="previous" />
<q-btn :label="step === 5 ? $t('finish') : $t('nextStep')" color="primary" no-caps @click="next" />
</q-stepper-navigation>
</template>
</q-stepper>
</w-dialog>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import StepUploadFile from './StepUploadFile.vue';
import StepDataView from './StepDataView.vue';
import StepMapping from './StepMapping.vue';
import StepImport from './StepImport.vue';
import { nextTick } from 'vue';
let datasource = undefined;
let schema = undefined;
const dialogRef = ref();
const stepper = ref();
const step = ref(1);
const stepStepUploadFileRef = ref();
const stepDataViewRef = ref();
const stepMappingRef = ref();
const stepImportRef = ref();
const next = () => {
if (step.value === 1) {
stepStepUploadFileRef.value.done(datasource, schema, (data) => {
stepper.value.next();
nextTick(() => {
stepDataViewRef.value.setData(data);
});
});
} else if (step.value === 2) {
stepDataViewRef.value.done((data) => {
stepper.value.next();
nextTick(() => {
stepMappingRef.value.setData(data);
});
});
} else if (step.value === 3) {
stepMappingRef.value.done((data) => {
stepper.value.next();
nextTick(() => {
stepImportRef.value.setData(data);
});
});
}
};
const previous = () => {
stepper.value.previous();
};
const open = (_datasource, _schema) => {
datasource = _datasource;
schema = _schema;
datasource = dialogRef.value.show();
};
const close = () => {
dialogRef.value.hide();
};
defineExpose({
open,
close,
});
</script>

99
io.sc.platform.developer.frontend/src/views/backend/sql/import-excel/StepDataView.vue

@ -0,0 +1,99 @@
<template>
<q-splitter :model-value="200" unit="px" style="height: 460px">
<template #before>
<q-list class="pr-2">
<q-item
v-for="sheet in sheetsRef"
:key="sheet.name"
v-ripple
clickable
:active="activeSheetRef === sheet.name"
active-class="bg-primary text-white"
@click="
() => {
activeSheetRef = sheet.name;
sheetChanged();
}
"
>
<q-item-section :title="sheet.name" class="truncate ...">{{ sheet.name }}</q-item-section>
</q-item>
</q-list>
</template>
<template #after>
<div class="pl-2" style="height: 100%">
<q-table
v-model:pagination="paginationRef"
:columns="colsRef"
:rows="rowsRef"
flat
bordered
separator="cell"
virtual-scroll
:hide-bottom="true"
class="sticky-header-column-table"
table-header-class="bg-grey-3"
style="height: 450px"
>
</q-table>
</div>
</template>
</q-splitter>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const dataRef = ref();
const sheetsRef = ref([]);
const activeSheetRef = ref('');
const paginationRef = ref({
rowsPerPage: 0,
});
const colsRef = ref([]);
const rowsRef = ref([]);
const setData = (data) => {
dataRef.value = data;
sheetsRef.value = data.sheets;
activeSheetRef.value = sheetsRef.value[0].name;
sheetChanged();
};
const sheetChanged = () => {
for (const sheet of sheetsRef.value) {
if (sheet.name === activeSheetRef.value) {
const table = sheet.table;
const body = table.body;
const table_rows = body.rows;
const table_cols = table_rows[0].cols;
//
const cols = [];
for (const table_col of table_cols) {
cols.push({ name: table_col.name, field: table_col.name, label: table_col.name });
}
colsRef.value = cols;
//
const rows = [];
for (const table_row of table_rows) {
const record = {};
for (const col of table_row.cols) {
record[col.name] = col.value;
}
rows.push(record);
}
rowsRef.value = rows;
}
}
};
const done = (_function) => {
_function(dataRef.value);
};
defineExpose({
setData,
done,
});
</script>

28
io.sc.platform.developer.frontend/src/views/backend/sql/import-excel/StepImport.vue

@ -0,0 +1,28 @@
<template>
<div style="padding: 10px 50px">
<div class="p-1">{{ $t('developer.backend.sql.import.dialog.step.import.progress') }} {{ messageRef }}</div>
<div>
<q-linear-progress size="20px" :value="percentageRef" color="primary" rounded> </q-linear-progress>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { axios, Environment } from 'platform-core';
const dataRef = ref();
const percentageRef = ref(0.1);
const messageRef = ref('正在导入......');
const setData = (data) => {
dataRef.value = data;
console.log(data);
};
const done = (_function) => {};
defineExpose({
setData,
done,
});
</script>

156
io.sc.platform.developer.frontend/src/views/backend/sql/import-excel/StepMapping.vue

@ -0,0 +1,156 @@
<template>
<q-splitter :model-value="200" unit="px" style="height: 460px">
<template #before>
<q-list class="pr-2">
<q-item
v-for="sheet in sheetsRef"
:key="sheet.name"
v-ripple
clickable
:active="activeSheetRef === sheet.name"
active-class="bg-primary text-white"
@click="
() => {
activeSheetRef = sheet.name;
sheetChanged();
}
"
>
<q-item-section :title="sheet.name" class="truncate ...">{{ sheet.name }}</q-item-section>
</q-item>
</q-list>
</template>
<template #after>
<div class="row items-end pl-2 q-gutter-md">
<q-input
v-model="mappingModel[activeSheetRef].tableName"
:label="$t('developer.backend.sql.import.dialog.step.mapping.table')"
dense
outlined
style="width: 300px"
></q-input>
<q-input
v-model="mappingModel[activeSheetRef].fieldNameRowIndex"
:label="$t('developer.backend.sql.import.dialog.step.mapping.fieldNameRowIndex')"
dense
outlined
type="number"
style="width: 200px"
@update:model-value="sheetChanged"
></q-input>
</div>
<div class="pl-2 pt-1" style="height: 100%">
<q-table
v-model:pagination="paginationRef"
:columns="colsRef"
:rows="mappingModel[activeSheetRef].fields"
flat
bordered
separator="cell"
virtual-scroll
:hide-bottom="true"
class="sticky-header-column-table"
table-header-class="bg-grey-3"
style="height: 400px"
>
<template #body-cell="props">
<q-td v-if="props.col.name === 'fieldName'" :props="props" class="p-0">
<q-input v-model="props.row[props.col.name]" dense outlined />
</q-td>
<q-td v-else-if="props.col.name === 'javaType'" :props="props" class="p-0">
<q-select
v-model="props.row[props.col.name]"
dense
outlined
emit-value
map-options
:options="[
{ value: 'java.lang.String', label: '字符串' },
{ value: 'java.lang.Integer', label: '整数' },
{ value: 'java.lang.Double', label: '小数' },
{ value: 'java.lang.Date', label: '日期' },
]"
/>
</q-td>
<q-td v-else class="p-0">{{ props.row[props.col.name] }}</q-td>
</template>
</q-table>
</div>
</template>
</q-splitter>
</template>
<script setup lang="ts">
import { ref, reactive, toRaw } from 'vue';
import { t } from 'platform-core';
const dataRef = ref();
const sheetsRef = ref([]);
const activeSheetRef = ref('sheet');
const mappingModel = reactive({
sheet: {
tableName: '',
fieldNameRowIndex: 1,
fields: [],
},
});
const paginationRef = ref({
rowsPerPage: 0,
});
const colsRef = ref([
{ name: 'no', field: 'no', align: 'left', label: t('developer.backend.sql.import.dialog.step.mapping.grid.entity.no') },
{ name: 'fieldName', field: 'fieldName', align: 'left', label: t('developer.backend.sql.import.dialog.step.mapping.grid.entity.fieldName') },
{ name: 'javaType', field: 'javaType', align: 'left', label: t('developer.backend.sql.import.dialog.step.mapping.grid.entity.javaType') },
]);
const rowsRef = ref([]);
const setData = (data) => {
console.log(data);
dataRef.value = data;
sheetsRef.value = data.sheets;
activeSheetRef.value = sheetsRef.value[0].name;
delete mappingModel['sheet'];
for (const sheet of sheetsRef.value) {
mappingModel[sheet.name] = {
tableName: '',
fieldNameRowIndex: 1,
fields: [],
};
}
sheetChanged();
};
const sheetChanged = () => {
for (const sheet of sheetsRef.value) {
if (sheet.name === activeSheetRef.value) {
const table = sheet.table;
const body = table.body;
const table_rows = body.rows;
const table_cols = table_rows[mappingModel[sheet.name].fieldNameRowIndex - 1].cols;
const records = [];
for (const col of table_cols) {
const record = {};
record.no = col.name;
record.fieldName = col.value;
record.javaType = 'String';
records.push(record);
}
mappingModel[sheet.name].fields = records;
rowsRef.value = records;
}
}
};
const done = (_function) => {
_function({
filePath: dataRef.value.filePath,
datasource: dataRef.value.datasource,
schema: dataRef.value.schema,
mapping: toRaw(mappingModel),
});
};
defineExpose({
setData,
done,
});
</script>

51
io.sc.platform.developer.frontend/src/views/backend/sql/import-excel/StepUploadFile.vue

@ -0,0 +1,51 @@
<template>
<q-file
ref="fileRef"
v-model="fileNameRef"
:label="$t('developer.backend.sql.import.dialog.step.upload.fileTip')"
dense
outlined
clearable
counter
accept=".xlsx,.csv"
style="width: 800px"
>
<template #prepend>
<q-icon name="cloud_upload" />
</template>
</q-file>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { axios, Environment } from 'platform-core';
const fileRef = ref();
const fileNameRef = ref();
const done = (datasource, schema, _function) => {
axios
.post(
Environment.apiContextPath('/api/developer/sql/parseFile'),
{
datasource: datasource,
schema: schema,
file: fileRef.value.nativeEl.files[0],
},
{
headers: {
'Content-Type': 'multipart/form-data',
},
},
)
.then((response) => {
const data = response.data;
data.datasource = datasource;
data.schema = schema;
_function(response.data);
});
};
defineExpose({
done,
});
</script>

2
io.sc.platform.developer.frontend/src/views/springboot/Bean.vue

@ -3,7 +3,7 @@
:title="$t('menu.developer.springboot.bean')"
:checkbox-selection="false"
:fetch-data-url="Environment.apiContextPath('/api/developer/springboot/beans')"
:pageable="true"
:pageable="false"
:toolbar-actions="['refresh', 'separator', 'view', 'separator', 'export']"
:columns="[
{ width: 500, name: 'name', label: $t('name') },

2
io.sc.platform.developer.frontend/src/views/springboot/Mapping.vue

@ -11,7 +11,7 @@
:title="$t('menu.developer.springboot.mapping')"
:checkbox-selection="false"
:fetch-data-url="Environment.apiContextPath('/api/developer/springboot/mappings/dispatcherServletMappingDescriptions')"
:pageable="true"
:pageable="false"
:toolbar-actions="['refresh', 'separator', 'view', 'export']"
:columns="[
{

7
io.sc.platform.developer.frontend/webpack.config.common.cjs

@ -154,6 +154,11 @@ module.exports = {
'@': path.resolve(__dirname, 'src'),
},
// 设置支持的模块扩展名,即这些扩展名的文件可以作为模块被使用
extensions: ['.ts', '.js', '.cjs', '.vue']
extensions: ['.ts', '.js', '.cjs', '.vue'],
fallback: {
"fs": false,
"os": false,
"path": false,
}
},
};

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

Loading…
Cancel
Save