Browse Source

update

main
wangshaoping 11 months ago
parent
commit
ec6d1ce302
  1. 45
      build.gradle
  2. 2
      erm.frontend/package.json
  3. 2
      gradle.properties
  4. 2
      io.sc.engine.mv.frontend/package.json
  5. 2
      io.sc.engine.rule.frontend/package.json
  6. 25
      io.sc.engine.rule.frontend/src/utils/PassOrNotFormater.ts
  7. 5
      io.sc.engine.rule.frontend/src/views/resources/Resources.vue
  8. 191
      io.sc.engine.rule.frontend/src/views/resources/designer/DesignerDialog.vue
  9. 1
      io.sc.engine.rule.frontend/src/views/resources/designer/Option.vue
  10. 1
      io.sc.engine.rule.frontend/src/views/resources/designer/Parameter.vue
  11. 1
      io.sc.engine.rule.frontend/src/views/resources/designer/Processor.vue
  12. 102
      io.sc.engine.rule.frontend/src/views/resources/designer/TestCaseParameter.vue
  13. 14
      io.sc.engine.rule.frontend/src/views/resources/designer/Testcase.vue
  14. 1
      io.sc.engine.rule.frontend/src/views/resources/designer/Validator.vue
  15. 2
      io.sc.engine.rule.server/src/main/java/io/sc/engine/rule/server/resource/entity/ModelResourceEntity.java
  16. 7
      io.sc.engine.rule.server/src/main/java/io/sc/engine/rule/server/testcase/controller/TestCaseWebController.java
  17. 2
      io.sc.platform.core.frontend/package.json
  18. 150
      io.sc.platform.core.frontend/src/platform/components/dialog/WDialog copy.vue
  19. 149
      io.sc.platform.core.frontend/src/platform/components/dialog/WDialog.vue
  20. 185
      io.sc.platform.core.frontend/src/platform/components/grid/WGrid.vue
  21. 5
      io.sc.platform.core.frontend/src/platform/components/index.ts
  22. 31
      io.sc.platform.core.frontend/src/platform/components/layout/WVExpandDiv.vue
  23. 11
      io.sc.platform.core.frontend/src/platform/components/layout/WVLayout.vue
  24. 193
      io.sc.platform.core.frontend/src/platform/components/workflow/BpmTask.vue
  25. 67
      io.sc.platform.core.frontend/src/platform/components/workflow/CompleteTaskDialog.vue
  26. 69
      io.sc.platform.core.frontend/src/platform/components/workflow/SelectAssigneeDialog.vue
  27. 96
      io.sc.platform.core.frontend/src/platform/components/workflow/WWorkflowAction.vue
  28. 158
      io.sc.platform.core.frontend/src/platform/components/workflow/WorkflowAction.vue
  29. 31
      io.sc.platform.core.frontend/src/platform/i18n/messages.json
  30. 30
      io.sc.platform.core.frontend/src/platform/i18n/messages_tw_CN.json
  31. 30
      io.sc.platform.core.frontend/src/platform/i18n/messages_zh_CN.json
  32. 1
      io.sc.platform.core.frontend/src/platform/index.ts
  33. 3
      io.sc.platform.core.frontend/src/platform/layout/sub-layout/Main.vue
  34. 1
      io.sc.platform.core.frontend/src/routes/routes.json
  35. 25
      io.sc.platform.core.frontend/src/utils/PassOrNotFormater.ts
  36. 333
      io.sc.platform.core.frontend/src/views/FormElements.vue
  37. 4
      io.sc.platform.core.frontend/template-project/package.json
  38. 1
      io.sc.platform.core.frontend/template-project/src/routes/routes.json
  39. 25
      io.sc.platform.core.frontend/template-project/src/utils/PassOrNotFormater.ts
  40. 333
      io.sc.platform.core.frontend/template-project/src/views/FormElements.vue
  41. 4
      io.sc.platform.core/src/main/resources/io/sc/platform/core/i18n/words.properties
  42. 4
      io.sc.platform.core/src/main/resources/io/sc/platform/core/i18n/words_tw_CN.properties
  43. 4
      io.sc.platform.core/src/main/resources/io/sc/platform/core/i18n/words_zh_CN.properties
  44. 1
      io.sc.platform.developer.doc/asciidoc/9999-appendix/appendix.adoc
  45. 10
      io.sc.platform.developer.doc/asciidoc/9999-appendix/refrence/html/html.adoc
  46. 2
      io.sc.platform.developer.frontend/package.json
  47. 1
      io.sc.platform.developer.frontend/src/i18n/messages_tw_CN.json
  48. 1
      io.sc.platform.developer.frontend/src/i18n/messages_zh_CN.json
  49. 2
      io.sc.platform.developer.frontend/src/menus/menus.json
  50. 34
      io.sc.platform.flowable/src/main/java/io/sc/platform/flowable/controller/ProcessToolsWebController.java
  51. 2
      io.sc.platform.flowable/src/main/java/io/sc/platform/flowable/jpa/entity/AgentEntity.java
  52. 2
      io.sc.platform.flowable/src/main/java/io/sc/platform/flowable/jpa/entity/ProcessEntity.java
  53. 6
      io.sc.platform.flowable/src/main/java/io/sc/platform/flowable/service/ProcessToolsService.java
  54. 10
      io.sc.platform.flowable/src/main/java/io/sc/platform/flowable/service/impl/ProcessOperationServiceImpl.java
  55. 46
      io.sc.platform.flowable/src/main/java/io/sc/platform/flowable/service/impl/ProcessToolsServiceImpl.java
  56. 17
      io.sc.platform.flowable/src/main/java/io/sc/platform/flowable/support/CompleteTaskResponse.java
  57. 25
      io.sc.platform.flowable/src/main/java/io/sc/platform/flowable/support/SelectAssigneeWrapper.java
  58. 2
      io.sc.platform.lcdp.frontend/package.json
  59. 13
      io.sc.platform.lcdp.frontend/src/i18n/messages.json
  60. 13
      io.sc.platform.lcdp.frontend/src/i18n/messages_tw_CN.json
  61. 13
      io.sc.platform.lcdp.frontend/src/i18n/messages_zh_CN.json
  62. 140
      io.sc.platform.lcdp.frontend/src/views/bpm/Bpm.vue
  63. 68
      io.sc.platform.lcdp.frontend/src/views/bpm/CompleteTaskDialog.vue
  64. 51
      io.sc.platform.lcdp.frontend/src/views/bpm/CreateProcessInstanceDialog.vue
  65. 8
      io.sc.platform.lcdp.frontend/src/views/bpm/JumpTaskDialog.vue
  66. 65
      io.sc.platform.lcdp.frontend/src/views/bpm/SelectAssigneeDialog.vue
  67. 158
      io.sc.platform.lcdp.frontend/src/views/bpm/WWorkflowAction.vue
  68. 2
      io.sc.platform.lcdp/src/main/java/io/sc/platform/lcdp/configure/jpa/entity/ConfigureEntity.java
  69. 2
      io.sc.platform.mvc.frontend/package.json
  70. 15
      io.sc.platform.orm.api/src/main/java/io/sc/platform/orm/api/vo/AuditorVo.java
  71. 12
      io.sc.platform.orm.api/src/main/java/io/sc/platform/orm/api/vo/CorporationAuditorVo.java
  72. 9
      io.sc.platform.orm.api/src/main/java/io/sc/platform/orm/api/vo/VersionVo.java
  73. 7
      io.sc.platform.orm/src/main/java/io/sc/platform/orm/entity/AuditorEntity.java
  74. 2
      io.sc.platform.orm/src/main/java/io/sc/platform/orm/entity/BaseEntity.java
  75. 1
      io.sc.platform.orm/src/main/java/io/sc/platform/orm/entity/CorporationAuditorEntity.java
  76. 9
      io.sc.platform.orm/src/main/java/io/sc/platform/orm/entity/VersionEntity.java
  77. 2
      io.sc.platform.orm/src/main/java/io/sc/platform/orm/service/impl/DaoServiceImpl.java
  78. 2
      io.sc.platform.security.frontend/package.json
  79. 8
      io.sc.platform.system.api/src/main/java/io/sc/platform/system/api/menu/MenuGroupVo.java
  80. 30
      io.sc.platform.system.api/src/main/java/io/sc/platform/system/api/user/UserVo.java
  81. 2
      io.sc.platform.system.frontend/package.json
  82. 1
      io.sc.platform.system.frontend/src/i18n/messages.json
  83. 1
      io.sc.platform.system.frontend/src/i18n/messages_tw_CN.json
  84. 1
      io.sc.platform.system.frontend/src/i18n/messages_zh_CN.json
  85. 33
      io.sc.platform.system.frontend/src/views/menu/Menu.vue
  86. 15
      io.sc.platform.system/src/main/java/io/sc/platform/system/menu/jpa/entity/MenuGroupEntity.java
  87. 1
      io.sc.platform.system/src/main/java/io/sc/platform/system/menu/mapper/MenuEntityMapper.java
  88. 3
      io.sc.platform.system/src/main/java/io/sc/platform/system/menu/service/impl/MenuServiceImpl.java
  89. 33
      io.sc.platform.system/src/main/java/io/sc/platform/system/user/service/impl/UserServiceImpl.java
  90. 1
      io.sc.platform.system/src/main/resources/liquibase/io.sc.platform.system_8.0.0_20220606__System Database Schema DDL.xml
  91. 2
      io.sc.standard.frontend/package.json

45
build.gradle

@ -269,20 +269,37 @@ subprojects {
*----------------------------------------------------------------*/
task jrebelIdea() {}
tasks.jrebelIdea.doLast {
File resourcesFile =file('src/main/resources')
if(resourcesFile!=null && resourcesFile.exists()){
File rebelFile = file('src/main/resources/rebel.xml')
rebelFile.withWriter('UTF-8') { writer ->
writer.write('<?xml version="1.0" encoding="UTF-8"?>\n');
writer.write('<application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.zeroturnaround.com" xsi:schemaLocation="http://www.zeroturnaround.com http://update.zeroturnaround.com/jrebel/rebel-2_1.xsd">\n');
writer.write('\t<classpath>\n');
if(file(project.name + '/src/main').exists()){
writer.write('\t\t<dir name="' + project.rootProject.projectDir + '/out/production/' + project.name + '"/>\n');
}
writer.write('\t</classpath>\n');
writer.write('</application>');
}
}
if(file('package.json').exists()){
File resourcesFile = file('java-src/main/resources')
if (resourcesFile != null && resourcesFile.exists()) {
File rebelFile = file('java-src/main/resources/rebel.xml')
rebelFile.withWriter('UTF-8') { writer ->
writer.write('<?xml version="1.0" encoding="UTF-8"?>\n');
writer.write('<application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.zeroturnaround.com" xsi:schemaLocation="http://www.zeroturnaround.com http://update.zeroturnaround.com/jrebel/rebel-2_1.xsd">\n');
writer.write('\t<classpath>\n');
if (file(project.name + '/java-src/main').exists()) {
writer.write('\t\t<dir name="' + project.rootProject.projectDir + '/out/production/' + project.name + '"/>\n');
}
writer.write('\t</classpath>\n');
writer.write('</application>');
}
}
}else {
File resourcesFile = file('src/main/resources')
if (resourcesFile != null && resourcesFile.exists()) {
File rebelFile = file('src/main/resources/rebel.xml')
rebelFile.withWriter('UTF-8') { writer ->
writer.write('<?xml version="1.0" encoding="UTF-8"?>\n');
writer.write('<application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.zeroturnaround.com" xsi:schemaLocation="http://www.zeroturnaround.com http://update.zeroturnaround.com/jrebel/rebel-2_1.xsd">\n');
writer.write('\t<classpath>\n');
if (file(project.name + '/src/main').exists()) {
writer.write('\t\t<dir name="' + project.rootProject.projectDir + '/out/production/' + project.name + '"/>\n');
}
writer.write('\t</classpath>\n');
writer.write('</application>');
}
}
}
}
/*-----------------------------------------------------------------

2
erm.frontend/package.json

@ -78,7 +78,7 @@
"luckyexcel": "1.0.1",
"mockjs": "1.1.0",
"pinia": "2.1.7",
"platform-core": "8.1.152",
"platform-core": "8.1.155",
"quasar": "2.14.5",
"tailwindcss": "3.4.0",
"vue": "3.4.3",

2
gradle.properties

@ -38,7 +38,7 @@ application_version=1.0.0
platform_group=io.sc
platform_version=8.1.29
platform_plugin_version=8.1.13
platform_core_frontend_version=8.1.152
platform_core_frontend_version=8.1.155
###########################################################
# dependencies version

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

@ -78,7 +78,7 @@
"luckyexcel": "1.0.1",
"mockjs": "1.1.0",
"pinia": "2.1.7",
"platform-core": "8.1.152",
"platform-core": "8.1.155",
"quasar": "2.14.5",
"tailwindcss": "3.4.0",
"vue": "3.4.3",

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

@ -78,7 +78,7 @@
"luckyexcel": "1.0.1",
"mockjs": "1.1.0",
"pinia": "2.1.7",
"platform-core": "8.1.152",
"platform-core": "8.1.155",
"quasar": "2.14.5",
"tailwindcss": "3.4.0",
"vue": "3.4.3",

25
io.sc.engine.rule.frontend/src/utils/PassOrNotFormater.ts

@ -0,0 +1,25 @@
import { Tools } from 'platform-core';
const PassOrNotFormater = (value) => {
if (Tools.isUndefinedOrNull(value)) {
return '';
}
if (value === 'PASSED') {
return {
componentType: 'QIcon',
attrs: { name: 'bi-check-circle', size: '20px', color: 'green' },
};
} else if (value === 'UN_PASSED') {
return {
componentType: 'QIcon',
attrs: { name: 'bi-x-circle', size: '20px', color: 'red' },
};
} else if (value === 'ERROR') {
return {
componentType: 'QIcon',
attrs: { name: 'bi-x-circle', size: '20px', color: 'red' },
};
}
};
export default PassOrNotFormater;

5
io.sc.engine.rule.frontend/src/views/resources/Resources.vue

@ -1,5 +1,5 @@
<template>
<div>
<div style="height: 100%">
<w-grid
ref="treeGridRef"
:title="$t('re.resources.grid.title')"
@ -225,14 +225,11 @@
:editor="{
dialog: {
width: '600px',
height: '400px',
},
form: {
colsNum: 1,
fields: [
{ name: 'type', label: $t('type'), type: 'text', hidden: true },
{ name: 'status', label: $t('status'), type: 'text', hidden: true },
{ name: 'version', label: $t('version'), type: 'number', hidden: true },
{
name: 'code',
label: $t('code'),

191
io.sc.engine.rule.frontend/src/views/resources/designer/DesignerDialog.vue

@ -10,8 +10,9 @@
"
:can-maximize="false"
:maximized="true"
body-padding="2px 2px 2px 2px"
>
<q-splitter v-model="verticalSplitterRef" unit="px" separator-style="width: 3px">
<q-splitter v-model="verticalSplitterRef" unit="px" separator-style="width: 3px;" style="height: 100%">
<template #before>
<Model
:resource="currentSelectedResourceRef"
@ -54,113 +55,109 @@
</template>
<template #after>
<div>
<q-splitter v-model="horizontalSplitterRef" unit="px" separator-style="height: 3px" horizontal>
<template #before>
<q-tabs
v-model="statusReactive.parameterAndTestcaseTab"
no-caps
inline-label
align="left"
@update:model-value="
(value) => {
if (value === 'testcase') {
horizontalSplitterRef = 300;
} else {
horizontalSplitterRef = 500;
}
<q-splitter v-model="horizontalSplitterRef" unit="px" separator-style="height: 3px" horizontal>
<template #before>
<q-tabs
v-model="statusReactive.parameterAndTestcaseTab"
no-caps
inline-label
align="left"
@update:model-value="
(value) => {
if (value === 'testcase') {
horizontalSplitterRef = 300;
} else {
horizontalSplitterRef = 500;
}
"
>
<q-tab v-if="statusReactive.isShowParameter" name="parameter" icon="bi-p-square" :label="$t('re.resources.designer.parameter.tab.title')" />
<q-tab v-if="statusReactive.isShowTestCase" name="testcase" icon="bi-receipt" :label="$t('re.resources.designer.testcase.tab.title')" />
</q-tabs>
<q-tab-panels v-model="statusReactive.parameterAndTestcaseTab" animated>
<q-tab-panel v-if="statusReactive.isShowParameter" name="parameter" class="px-0">
<Parameter
ref="parameterGridRef"
:model="currentSelectedModelRef"
@row-click="
(evt, row, index) => {
currentSelectedParameterRef = row;
if (row.type === 'IN') {
statusReactive.isShowValidator = true;
statusReactive.isShowProcessor = false;
statusReactive.isShowOption = false;
statusReactive.isShowTestCaseParameter = false;
validatorGridRef?.refresh();
} else if (row.type === 'IN_OPTION') {
statusReactive.isShowValidator = false;
statusReactive.isShowProcessor = false;
statusReactive.isShowOption = true;
statusReactive.isShowTestCaseParameter = false;
optionGridRef?.refresh();
} else if (row.type == 'OUT' || row.type == 'INTERMEDIATE' || row.type == 'RULE_RESULT' || row.type == 'SINGLE_RULE_RESULT') {
statusReactive.isShowValidator = false;
statusReactive.isShowProcessor = true;
statusReactive.isShowOption = false;
statusReactive.isShowTestCaseParameter = false;
processorGridRef?.refresh();
} else {
statusReactive.isShowValidator = false;
statusReactive.isShowProcessor = false;
statusReactive.isShowOption = false;
statusReactive.isShowTestCaseParameter = false;
}
}
"
@before-request-data="
() => {
statusReactive.isShowValidator = false;
}
"
>
<q-tab v-if="statusReactive.isShowParameter" name="parameter" icon="bi-p-square" :label="$t('re.resources.designer.parameter.tab.title')" />
<q-tab v-if="statusReactive.isShowTestCase" name="testcase" icon="bi-receipt" :label="$t('re.resources.designer.testcase.tab.title')" />
</q-tabs>
<q-tab-panels v-model="statusReactive.parameterAndTestcaseTab" animated style="height: calc(100% - 50px)">
<q-tab-panel v-if="statusReactive.isShowParameter" name="parameter" class="px-0" style="height: 100%">
<Parameter
ref="parameterGridRef"
:model="currentSelectedModelRef"
@row-click="
(evt, row, index) => {
currentSelectedParameterRef = row;
if (row.type === 'IN') {
statusReactive.isShowValidator = true;
statusReactive.isShowProcessor = false;
statusReactive.isShowOption = false;
statusReactive.isShowTestCaseParameter = false;
}
"
></Parameter>
</q-tab-panel>
<q-tab-panel v-if="statusReactive.isShowTestCase" name="testcase" class="px-0">
<TestCase
ref="testCaseGridRef"
:model="currentSelectedModelRef"
@row-click="
(evt, row, index) => {
currentSelectedTestCaseRef = row;
validatorGridRef?.refresh();
} else if (row.type === 'IN_OPTION') {
statusReactive.isShowValidator = false;
statusReactive.isShowProcessor = false;
statusReactive.isShowOption = true;
statusReactive.isShowTestCaseParameter = false;
optionGridRef?.refresh();
} else if (row.type == 'OUT' || row.type == 'INTERMEDIATE' || row.type == 'RULE_RESULT' || row.type == 'SINGLE_RULE_RESULT') {
statusReactive.isShowValidator = false;
statusReactive.isShowProcessor = true;
statusReactive.isShowOption = false;
statusReactive.isShowTestCaseParameter = true;
testCaseParameterGridRef?.refresh();
}
"
@before-request-data="
() => {
statusReactive.isShowTestCaseParameter = false;
processorGridRef?.refresh();
} else {
statusReactive.isShowValidator = false;
statusReactive.isShowProcessor = false;
statusReactive.isShowOption = false;
statusReactive.isShowTestCaseParameter = false;
}
"
></TestCase>
</q-tab-panel>
</q-tab-panels>
</template>
<template #after>
<div v-if="statusReactive.isShowValidator">
<Validator ref="validatorGridRef" :parameter="currentSelectedParameterRef"></Validator>
</div>
<div v-if="statusReactive.isShowProcessor">
<Processor ref="processorGridRef" :parameter="currentSelectedParameterRef"></Processor>
</div>
<div v-if="statusReactive.isShowOption">
<Option ref="optionGridRef" :parameter="currentSelectedParameterRef"></Option>
</div>
<div v-if="statusReactive.isShowTestCaseParameter">
<TestCaseParameter ref="testCaseParameterGridRef" :test-case="currentSelectedTestCaseRef"></TestCaseParameter>
</div>
</template>
</q-splitter>
</div>
}
"
@before-request-data="
() => {
statusReactive.isShowValidator = false;
statusReactive.isShowProcessor = false;
statusReactive.isShowOption = false;
statusReactive.isShowTestCaseParameter = false;
}
"
></Parameter>
</q-tab-panel>
<q-tab-panel v-if="statusReactive.isShowTestCase" name="testcase" class="px-0">
<TestCase
ref="testCaseGridRef"
:model="currentSelectedModelRef"
@row-click="
(evt, row, index) => {
currentSelectedTestCaseRef = row;
statusReactive.isShowValidator = false;
statusReactive.isShowProcessor = false;
statusReactive.isShowOption = false;
statusReactive.isShowTestCaseParameter = true;
testCaseParameterGridRef?.refresh();
}
"
@before-request-data="
() => {
statusReactive.isShowValidator = false;
statusReactive.isShowProcessor = false;
statusReactive.isShowOption = false;
statusReactive.isShowTestCaseParameter = false;
}
"
></TestCase>
</q-tab-panel>
</q-tab-panels>
</template>
<template #after>
<div style="height: 100%">
<Validator v-if="statusReactive.isShowValidator" ref="validatorGridRef" :parameter="currentSelectedParameterRef"></Validator>
<Processor v-if="statusReactive.isShowProcessor" ref="processorGridRef" :parameter="currentSelectedParameterRef"></Processor>
<Option v-if="statusReactive.isShowOption" ref="optionGridRef" :parameter="currentSelectedParameterRef"></Option>
<TestCaseParameter
v-if="statusReactive.isShowTestCaseParameter"
ref="testCaseParameterGridRef"
:test-case="currentSelectedTestCaseRef"
></TestCaseParameter>
</div>
</template>
</q-splitter>
</template>
</q-splitter>
</w-dialog>
@ -185,7 +182,7 @@ const props = defineProps({
});
const dialogRef = ref();
const horizontalSplitterRef = ref(500);
const horizontalSplitterRef = ref(400);
const verticalSplitterRef = ref(400);
const currentSelectedResourceRef = ref();

1
io.sc.engine.rule.frontend/src/views/resources/designer/Option.vue

@ -1,7 +1,6 @@
<template>
<w-grid
ref="gridRef"
:height="200"
:title="$t('re.resources.designer.option.grid.title')"
dense-body
class="px-1"

1
io.sc.engine.rule.frontend/src/views/resources/designer/Parameter.vue

@ -1,7 +1,6 @@
<template>
<w-grid
ref="gridRef"
:height="200"
:title="$t('re.resources.designer.parameter.grid.title')"
dense-body
class="px-1"

1
io.sc.engine.rule.frontend/src/views/resources/designer/Processor.vue

@ -1,7 +1,6 @@
<template>
<w-grid
ref="gridRef"
:height="200"
:title="$t('re.resources.designer.processor.grid.title')"
dense-body
class="px-1"

102
io.sc.engine.rule.frontend/src/views/resources/designer/TestCaseParameter.vue

@ -1,7 +1,6 @@
<template>
<w-grid
ref="gridRef"
:height="200"
:title="$t('re.resources.designer.testCase.grid.title')"
dense-body
class="px-1"
@ -36,6 +35,11 @@
name: 'execute',
label: $t('execute'),
icon: 'bi-caret-right',
click: () => {
axios.post(Environment.apiContextPath('/api/re/testCase/executeTestCase/' + testCase.id)).then(() => {
gridRef?.refresh();
});
},
},
'separator',
'view',
@ -58,7 +62,7 @@
{ width: 100, name: 'expectValue', label: $t('re.resources.designer.testCaseParameter.grid.entity.expectValue') },
{ width: 100, name: 'resultValue', label: $t('re.resources.designer.testCaseParameter.grid.entity.resultValue') },
{ width: 100, name: 'skipCheck', label: $t('re.resources.designer.testCaseParameter.grid.entity.skipCheck') },
{ width: 100, name: 'testResult', label: $t('re.resources.designer.testCaseParameter.grid.entity.testResult'), format: passOrNotFormater },
{ width: 100, name: 'testResult', label: $t('re.resources.designer.testCaseParameter.grid.entity.testResult'), format: PassOrNotFormater },
]"
:editor="{
dialog: {
@ -68,24 +72,59 @@
form: {
colsNum: 1,
fields: [
{ name: 'testCaseId', label: $t(''), type: 'text', defaultValue: testCase.id },
{ name: 'parent', label: $t(''), type: 'text' },
{ name: 'id', label: $t(''), type: 'text' },
{ name: 'category', label: $t('') },
{ name: 'code', label: $t('') },
{ name: 'name', label: $t('') },
{ name: 'skipCheck', label: $t('') },
{ name: 'source', label: $t('') },
{ name: 'type', label: $t('') },
{ name: 'parameterType', label: $t('') },
{ name: 'scoreCardVarType', label: $t('') },
{ name: 'valueType', label: $t('') },
{ name: 'defaultValue', label: $t('') },
{ name: 'inputValue', label: $t('') },
{ name: 'expectValue', label: $t('') },
{ name: 'resultValue', label: $t('') },
{ name: 'testResult', label: $t('') },
{ name: 'order', label: $t('') },
{ name: 'testCase', label: $t('testCase'), type: 'text', defaultValue: testCase.id, hidden: true },
{ name: 'id', label: $t('id'), type: 'text', hidden: true },
{ name: 'source', label: $t('source'), type: 'text', hidden: true },
{ name: 'parameterType', label: $t('parameterType'), type: 'text', hidden: true },
{ name: 'scoreCardVarType', label: $t('scoreCardVarType'), type: 'text', hidden: true },
{ name: 'indicatorType', label: $t('indicatorType'), type: 'text', hidden: true },
{
name: 'inputValue',
label: $t('re.resources.designer.testCaseParameter.grid.entity.inputValue'),
type: 'code-mirror',
rows: 4,
showIf: (arg) => {
const parameterType = arg.form.getFieldValue('parameterType');
const scoreCardVarType = arg.form.getFieldValue('scoreCardVarType');
const indicatorType = arg.form.getFieldValue('indicatorType');
if (
(!Tools.isUndefinedOrNull(parameterType) && (parameterType === 'IN' || parameterType === 'IN_OPTION' || parameterType === 'INDICATOR')) ||
(!Tools.isUndefinedOrNull(scoreCardVarType) && scoreCardVarType !== 'RESULT') ||
(!Tools.isUndefinedOrNull(indicatorType) && indicatorType === 'INTERFACE')
) {
return true;
}
return false;
},
},
{
name: 'expectValue',
label: $t('re.resources.designer.testCaseParameter.grid.entity.expectValue'),
type: 'code-mirror',
rows: 4,
showIf: (arg) => {
const parameterType = arg.form.getFieldValue('parameterType');
const scoreCardVarType = arg.form.getFieldValue('scoreCardVarType');
const indicatorType = arg.form.getFieldValue('indicatorType');
if (
(!Tools.isUndefinedOrNull(parameterType) &&
(parameterType === 'INTERMEDIATE' || parameterType === 'OUT' || parameterType === 'RULE_RESULT' || parameterType === 'SINGLE_RULE_RESULT')) ||
!Tools.isUndefinedOrNull(scoreCardVarType) ||
(!Tools.isUndefinedOrNull(indicatorType) && indicatorType === 'INDICATOR')
) {
return true;
}
return false;
},
},
{
name: 'resultValue',
label: $t('re.resources.designer.testCaseParameter.grid.entity.resultValue'),
type: 'code-mirror',
rows: 4,
lang: 'json',
},
{ name: 'skipCheck', label: $t('re.resources.designer.testCaseParameter.grid.entity.skipCheck'), type: 'checkbox' },
],
},
}"
@ -117,6 +156,7 @@
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import { axios, Environment, EnumTools, Formater, Options, Tools } from 'platform-core';
import PassOrNotFormater from '@/utils/PassOrNotFormater';
const props = defineProps({
testCase: { type: Object, default: undefined },
@ -146,26 +186,4 @@ if (response && response.data) {
ValueTypeMap[item.key] = item.value;
}
}
const passOrNotFormater = (value) => {
if (Tools.isUndefinedOrNull(value)) {
return '';
}
if (value === 'PASSED') {
return {
componentType: 'QIcon',
attrs: { name: 'bi-check-circle', size: '20px', color: 'green' },
};
} else if (value === 'UN_PASSED') {
return {
componentType: 'QIcon',
attrs: { name: 'bi-x-circle', size: '20px', color: 'red' },
};
} else if (value === 'ERROR') {
return {
componentType: 'QIcon',
attrs: { name: 'bi-x-circle', size: '20px', color: 'red' },
};
}
};
</script>

14
io.sc.engine.rule.frontend/src/views/resources/designer/Testcase.vue

@ -51,18 +51,9 @@
name: 'testResult',
label: $t('re.resources.designer.testCase.grid.entity.testResult'),
align: 'center',
format: (value, row) => {
if (row.testResult) {
if (row.testResult === 'PASSED') {
return {};
} else {
return {};
}
}
return '';
},
format: PassOrNotFormater,
},
{ width: 120, name: 'lastTestDate', label: $t('re.resources.designer.testCase.grid.entity.lastTestDate') },
{ width: 150, name: 'lastTestDate', label: $t('re.resources.designer.testCase.grid.entity.lastTestDate') },
{ width: 400, name: 'name', label: $t('name') },
{ width: '100%', name: 'description', label: $t('description') },
]"
@ -117,6 +108,7 @@
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import { axios, Environment, EnumTools, Formater, Options, Tools } from 'platform-core';
import PassOrNotFormater from '@/utils/PassOrNotFormater';
const props = defineProps({
model: { type: Object, default: undefined },

1
io.sc.engine.rule.frontend/src/views/resources/designer/Validator.vue

@ -1,7 +1,6 @@
<template>
<w-grid
ref="gridRef"
:height="200"
:title="$t('re.resources.designer.validator.grid.title')"
dense-body
class="px-1"

2
io.sc.engine.rule.server/src/main/java/io/sc/engine/rule/server/resource/entity/ModelResourceEntity.java

@ -21,7 +21,7 @@ import java.util.List;
@JsonTypeName("MODEL")
public class ModelResourceEntity extends ReleasableResourceEntity {
//模型
@OneToOne(mappedBy="resource",cascade= {CascadeType.PERSIST})
@OneToOne(mappedBy="resource")
private ModelEntity model;
@Override

7
io.sc.engine.rule.server/src/main/java/io/sc/engine/rule/server/testcase/controller/TestCaseWebController.java

@ -31,13 +31,6 @@ public class TestCaseWebController extends RestCrudController<TestCaseVo,TestCas
}
return EntityVoUtil.toVo(service.findByOwnerId(ownerId,queryParameter));
}
@RequestMapping(value="isc/fetchAll", method=RequestMethod.GET)
@ResponseBody
public Page<TestCaseWrapper> fetchAll(HttpServletRequest request,HttpServletResponse response,TestCaseEntity queryExampleEntity) throws Exception{
//return service.queryAll(queryExampleEntity, queryParameter);
return QueryResult.emptyPage();
}
@RequestMapping(value="deepClone/{testCaseId}",method=RequestMethod.POST)
@ResponseBody

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

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

150
io.sc.platform.core.frontend/src/platform/components/dialog/WDialog copy.vue

@ -0,0 +1,150 @@
<template>
<!-- 组件默认带上的官方属性说明
allow-focus-outside: 允许对话框外的元素可聚焦不设置该值会导致luckysheet在窗口中无法编辑
no-esc-dismiss: 用户不能按 ESC 键关闭对话框如果还设置了 'persistent' 属性则无需设置它
no-backdrop-dismiss: 用户不能通过单击对话框外部来关闭对话框如果还设置了 'persistent' 属性则无需设置它
no-refocus: 当对话框被隐藏时不要重新聚焦以前有聚焦过的 DOM 元素
-->
<q-dialog
ref="dialogRef"
v-model="dialog.show"
:maximized="dialog.maximized"
allow-focus-outside
no-esc-dismiss
no-backdrop-dismiss
no-refocus
v-bind="attrs"
>
<q-card
:style="{
width: dialog.maximized ? '100vw' : props.width,
'max-width': '100vw',
height: dialog.maximized ? '100vh' : props.height,
'max-height': '100vh',
}"
>
<div class="w-full h-full">
<div>
<q-card-section style="height: 59px">
<div class="flex justify-between">
<div class="text-h6">{{ title }}</div>
<div class="flex justify-end gap-4">
<template v-if="buttons && buttons.length > 0">
<template v-for="(btn, index) in buttons as any" :key="index">
<q-btn v-if="typeof btn === 'object'" :loading="false" :color="'primary'" v-bind="btn" @click="btn.click ? btn.click() : () => {}"> </q-btn>
</template>
</template>
<slot name="buttons"></slot>
<q-btn v-if="canMaximize" dense flat :icon="!dialog.maximized ? IconEnum.全屏 : IconEnum.退出全屏" @click="maximizeBtnClick">
<q-tooltip v-if="!dialog.maximized">全屏</q-tooltip>
<q-tooltip v-else-if="dialog.maximized">退出全屏</q-tooltip>
</q-btn>
<q-btn v-close-popup dense flat :icon="IconEnum.关闭">
<q-tooltip>关闭</q-tooltip>
</q-btn>
</div>
</div>
</q-card-section>
<q-separator />
</div>
<div ref="dialogContentDivRef" class="dialog_content_div" style="height: calc(100% - 60px)">
<q-card-section style="height: 100%; padding: 0px" class="scroll">
<slot></slot>
</q-card-section>
</div>
</div>
</q-card>
</q-dialog>
</template>
<script setup lang="ts">
import { ref, reactive, useAttrs } from 'vue';
import { IconEnum } from '@/platform/enums';
const attrs = useAttrs();
const props = defineProps({
title: { type: String, default: '' },
width: { type: String, default: '70%' },
height: { type: String, default: '70%' },
canMaximize: { type: Boolean, default: true },
buttons: { type: Array, default: () => [] },
});
const emit = defineEmits<{
(
e: 'maximized', //
maximized: boolean, // true:,false:退
): void;
}>();
const dialogContentDivRef = ref();
const dialog = reactive({
show: false,
maximized: attrs.maximized || false,
});
const maximizeBtnClick = () => {
dialog.maximized = !dialog.maximized;
emit('maximized', dialog.maximized);
};
const show = () => {
dialog.show = true;
};
const hide = () => {
dialog.show = false;
};
//
// const getElementViewPosition = (element) => {
// //x
// let actualLeft = element.offsetLeft;
// let xcurrent = element.offsetParent;
// while (xcurrent !== null) {
// actualLeft += xcurrent.offsetLeft + xcurrent.clientLeft;
// xcurrent = xcurrent.offsetParent;
// }
// let elementScrollLeft = document.documentElement.scrollLeft;
// if (document.compatMode == 'BackCompat') {
// elementScrollLeft = document.body.scrollLeft;
// }
// const left = actualLeft - elementScrollLeft;
// //y
// let actualTop = element.offsetTop;
// let ycurrent = element.offsetParent;
// while (ycurrent !== null) {
// actualTop += ycurrent.offsetTop + ycurrent.clientTop;
// ycurrent = ycurrent.offsetParent;
// }
// let elementScrollTop = document.documentElement.scrollTop;
// if (document.compatMode == 'BackCompat') {
// elementScrollTop = document.body.scrollTop;
// }
// var right = actualTop - elementScrollTop;
// //
// return { x: left, y: right };
// };
// const getContentHeight = () => {
// if (dialogContentDivRef?.value) {
// console.info('dialogContentDivRef.value', dialogContentDivRef.value.offsetHeight);
// return {
// height: dialogContentDivRef.value.offsetHeight,
// y: getElementViewPosition(dialogContentDivRef.value).y,
// };
// }
// return null;
// };
const getContent = () => {
if (dialogContentDivRef?.value) {
return dialogContentDivRef.value;
}
return null;
};
defineExpose({
show,
hide,
getContent,
});
</script>

149
io.sc.platform.core.frontend/src/platform/components/dialog/WDialog.vue

@ -15,59 +15,49 @@
no-refocus
v-bind="attrs"
>
<q-card
:style="{
width: dialog.maximized ? '100vw' : props.width,
'max-width': '100vw',
height: dialog.maximized ? '100vh' : props.height,
'max-height': '100vh',
}"
>
<div class="w-full h-full">
<div>
<q-card-section style="height: 59px">
<div class="flex justify-between">
<div class="text-h6">{{ title }}</div>
<div class="flex justify-end gap-4">
<template v-if="buttons && buttons.length > 0">
<template v-for="(btn, index) in buttons as any" :key="index">
<q-btn v-if="typeof btn === 'object'" :loading="false" :color="'primary'" v-bind="btn" @click="btn.click ? btn.click() : () => {}"> </q-btn>
</template>
</template>
<slot name="buttons"></slot>
<q-btn v-if="canMaximize" dense flat :icon="!dialog.maximized ? IconEnum.全屏 : IconEnum.退出全屏" @click="maximizeBtnClick">
<q-tooltip v-if="!dialog.maximized">全屏</q-tooltip>
<q-tooltip v-else-if="dialog.maximized">退出全屏</q-tooltip>
</q-btn>
<q-btn v-close-popup dense flat :icon="IconEnum.关闭">
<q-tooltip>关闭</q-tooltip>
</q-btn>
</div>
</div>
</q-card-section>
<q-separator />
<q-card :style="generateDialogStyle()">
<q-card-section style="height: 59px">
<div class="flex justify-between">
<div class="text-h6">{{ title }}</div>
<div class="flex justify-end gap-4">
<template v-if="buttons && buttons.length > 0">
<template v-for="(btn, index) in buttons as any" :key="index">
<q-btn v-if="typeof btn === 'object'" :loading="false" :color="'primary'" v-bind="btn" @click="btn.click ? btn.click() : () => {}"> </q-btn>
</template>
</template>
<slot name="buttons"></slot>
<q-btn v-if="canMaximize" dense flat :icon="!dialog.maximized ? 'fullscreen' : 'fullscreen_exit'" @click="maximizeBtnClick">
<q-tooltip v-if="!dialog.maximized">{{ $t('fullScreen') }}</q-tooltip>
<q-tooltip v-else-if="dialog.maximized">{{ $t('fullScreenExit') }}</q-tooltip>
</q-btn>
<q-btn v-close-popup dense flat icon="close">
<q-tooltip>{{ $t('close') }}</q-tooltip>
</q-btn>
</div>
</div>
<div ref="dialogContentDivRef" class="dialog_content_div" style="height: calc(100% - 60px)">
<q-card-section style="height: 100%; padding: 0px" class="scroll">
<slot></slot>
</q-card-section>
</q-card-section>
<q-separator />
<q-card-section :style="generateDialogBodyStyle()" class="overflow-y-scroll">
<div ref="dialogContentDivRef" style="height: 100%">
<slot></slot>
</div>
</div>
</q-card-section>
</q-card>
</q-dialog>
</template>
<script setup lang="ts">
import { ref, reactive, useAttrs } from 'vue';
import { IconEnum } from '@/platform/enums';
import { useQuasar } from 'quasar';
const attrs = useAttrs();
const props = defineProps({
title: { type: String, default: '' },
width: { type: String, default: '70%' },
height: { type: String, default: '70%' },
width: { type: String, default: undefined },
height: { type: String, default: undefined },
canMaximize: { type: Boolean, default: true },
buttons: { type: Array, default: () => [] },
bodyPadding: { type: String, default: '2px 10px 20px 10px' },
});
const emit = defineEmits<{
(
@ -76,6 +66,7 @@ const emit = defineEmits<{
): void;
}>();
const quasar = useQuasar();
const dialogContentDivRef = ref();
const dialog = reactive({
show: false,
@ -87,6 +78,45 @@ const maximizeBtnClick = () => {
emit('maximized', dialog.maximized);
};
const generateDialogStyle = () => {
const result = {
'min-width': '400px',
'max-width': '100vw',
'min-height': '50px',
'max-height': '100vh',
};
if (dialog.maximized) {
result.width = '100vw';
result.height = '100vh';
} else {
if (props.width) {
result.width = props.width;
}
if (props.height) {
result.height = props.height;
}
}
return result;
};
const generateDialogBodyStyle = () => {
const result = {
width: '100%',
'min-height': '50px',
'max-height': 'calc(100vh - 60px)',
padding: props.bodyPadding,
};
if (dialog.maximized) {
result.height = quasar.screen.height - 60 + 'px';
} else {
if (props.height) {
result.height = 'calc(' + props.height + ' - 60px)';
}
}
return result;
};
const show = () => {
dialog.show = true;
};
@ -94,47 +124,6 @@ const hide = () => {
dialog.show = false;
};
//
// const getElementViewPosition = (element) => {
// //x
// let actualLeft = element.offsetLeft;
// let xcurrent = element.offsetParent;
// while (xcurrent !== null) {
// actualLeft += xcurrent.offsetLeft + xcurrent.clientLeft;
// xcurrent = xcurrent.offsetParent;
// }
// let elementScrollLeft = document.documentElement.scrollLeft;
// if (document.compatMode == 'BackCompat') {
// elementScrollLeft = document.body.scrollLeft;
// }
// const left = actualLeft - elementScrollLeft;
// //y
// let actualTop = element.offsetTop;
// let ycurrent = element.offsetParent;
// while (ycurrent !== null) {
// actualTop += ycurrent.offsetTop + ycurrent.clientTop;
// ycurrent = ycurrent.offsetParent;
// }
// let elementScrollTop = document.documentElement.scrollTop;
// if (document.compatMode == 'BackCompat') {
// elementScrollTop = document.body.scrollTop;
// }
// var right = actualTop - elementScrollTop;
// //
// return { x: left, y: right };
// };
// const getContentHeight = () => {
// if (dialogContentDivRef?.value) {
// console.info('dialogContentDivRef.value', dialogContentDivRef.value.offsetHeight);
// return {
// height: dialogContentDivRef.value.offsetHeight,
// y: getElementViewPosition(dialogContentDivRef.value).y,
// };
// }
// return null;
// };
const getContent = () => {
if (dialogContentDivRef?.value) {
return dialogContentDivRef.value;

185
io.sc.platform.core.frontend/src/platform/components/grid/WGrid.vue

@ -1,5 +1,5 @@
<template>
<div>
<div style="height: 100%">
<q-table
ref="tableRef"
v-model:pagination="state.pagination"
@ -16,7 +16,7 @@
:rows-per-page-options="pageable && !tree && state.refHeightWidth.middleWidth > 600 ? state.pagination.rowsPerPageOptions : []"
:loading="state.loading"
:class="tableClassComputed"
:table-style="tableHeightComputed"
:table-style="tableHeightComputed()"
:row-key="rowKey_"
:visible-columns="visibleColumnsComputed"
@request="onRequest"
@ -56,7 +56,7 @@
<q-checkbox v-model="table.allTicked" flat :dense="denseHeaderComputed" @update:model-value="allTickedUpdateFun" />
</q-th>
<q-th v-if="rIndex === 0 && table.sortNo && !props.tree" :rowspan="columnTitleState.columnTitleRowNum" :style="moreColumnTitleTableSortNoStyle">
序号
{{ $t('rownum') }}
</q-th>
<q-th
v-for="c in r"
@ -71,7 +71,7 @@
{{ c.label }}
</q-th>
</q-tr>
<q-tr v-if="table.rows.length === 0" :style="noDataTrHeightComputed" class="noDataTr">
<q-tr v-if="table.rows.length === 0" :style="noDataTrHeightComputed()" class="noDataTr">
<q-td :colspan="noDataTrColspanComputed" align="center" valian="middle"><q-icon size="2em" :name="IconEnum.提示" />{{ state.noDataLabel }}</q-td>
</q-tr>
</template>
@ -96,7 +96,7 @@
</q-th>
</template>
</q-tr>
<q-tr v-if="table.rows.length === 0" :style="noDataTrHeightComputed" class="noDataTr">
<q-tr v-if="table.rows.length === 0" :style="noDataTrHeightComputed()" class="noDataTr">
<q-td :colspan="noDataTrColspanComputed" align="center" valian="middle"><q-icon size="2em" :name="IconEnum.提示" />{{ state.noDataLabel }}</q-td>
</q-tr>
</template>
@ -205,9 +205,9 @@
@update:model-value="pageChange"
/>
</template>
<span> {{ state.pagination.rowsNumber }} 条记录</span>
<span>{{ $t('tip.pagenation.totalRecord', { count: state.pagination.rowsNumber }) }}</span>
</template>
<template v-else> {{ state.pagination.rowsNumber }} 条记录 </template>
<template v-else> {{ $t('tip.pagenation.totalRecord', { count: state.pagination.rowsNumber }) }} </template>
</template>
<!--
<template v-if="!attrs['hide-bottom']" #no-data="{ message }">
@ -222,7 +222,7 @@
<w-dialog ref="dialogRef" v-bind="editor.dialog" :title="dialog.dialogTitle" :buttons="dialog.dialogButtons">
<w-form ref="dialogFormRef" v-bind="editor.form" class="pt-1.5 px-1.5"></w-form>
</w-dialog>
<w-drawer ref="drawerRef" title="查看" v-bind="viewer.drawer">
<w-drawer ref="drawerRef" :title="$t('action.view')" v-bind="viewer.drawer">
<div class="p-2.5">
<w-info-panel ref="infoRef" v-bind="viewer.panel" :info="viewInfo.infoArray"></w-info-panel>
</div>
@ -232,6 +232,7 @@
<script setup lang="ts">
import { ref, reactive, computed, onMounted, nextTick, toRaw, useAttrs, getCurrentInstance, provide, watchEffect, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { axios, Environment, NotifyManager, TreeBuilder, VueTools, Tools } from '@/platform';
import { useQuasar, getCssVar, exportFile } from 'quasar';
import { IconEnum } from '@/platform/enums';
@ -401,6 +402,7 @@ const emit = defineEmits<{
): void;
}>();
const { t } = useI18n();
const $q = useQuasar();
const queryFormRef = ref();
const tableRef = ref();
@ -534,6 +536,7 @@ enum ButtonEnum {
expand = 'expand', // ?
resetDefaultValues = 'resetDefaultValues', //
}
/**
* 内置按钮
*/
@ -555,12 +558,12 @@ const remove = () => {
};
axios(requestParams)
.then((resp) => {
NotifyManager.info('操作成功');
NotifyManager.info(t('tip.operationSuccess'));
onRequest({ pagination: state.pagination });
})
.catch((error) => {
console.info('error====', error);
NotifyManager.error('操作失败');
console.error(error);
NotifyManager.error(t('tip.operationFailed'));
});
};
const resetDefaultValues = () => {
@ -570,12 +573,12 @@ const resetDefaultValues = () => {
};
axios(requestParams)
.then((resp) => {
NotifyManager.info('操作成功');
NotifyManager.info(t('tip.operationSuccess'));
onRequest({ pagination: state.pagination });
})
.catch((error) => {
console.info('error====', error);
NotifyManager.error('操作失败');
console.error(error);
NotifyManager.error(t('tip.operationFailed'));
});
};
const buttonObj = {
@ -583,7 +586,7 @@ const buttonObj = {
query: {
name: ButtonEnum.query,
icon: IconEnum.查询,
label: '查询',
label: t('action.query'),
click: () => {
refresh();
},
@ -591,7 +594,7 @@ const buttonObj = {
moreQuery: {
name: ButtonEnum.moreQuery,
icon: IconEnum.更多查询,
label: '更多查询',
label: t('action.moreQueryConditions'),
enableIf: () => {
if (props.queryFormFields.length <= table.queryFormFields.length && !table.moreQueryStatus) {
return false;
@ -607,7 +610,7 @@ const buttonObj = {
reset: {
name: ButtonEnum.reset,
icon: IconEnum.重置,
label: '重置',
label: t('action.reset'),
click: () => {
queryFormRef.value.reset();
},
@ -615,7 +618,7 @@ const buttonObj = {
refresh: {
name: ButtonEnum.refresh,
icon: IconEnum.刷新,
label: '刷新',
label: t('action.refresh'),
click: () => {
refresh();
},
@ -623,9 +626,9 @@ const buttonObj = {
add: {
name: ButtonEnum.add,
icon: IconEnum.新增,
label: '新增',
label: t('action.addNew'),
click: () => {
dialog.dialogTitle = '新增';
dialog.dialogTitle = t('action.addNew');
dialogRef.value.show();
nextTick(() => {
dialogFormRef.value.setStatus(PageStatusEnum.新增);
@ -636,7 +639,7 @@ const buttonObj = {
edit: {
name: ButtonEnum.edit,
icon: IconEnum.编辑,
label: '编辑',
label: t('action.edit'),
enableIf: (args) => {
if (args.selected) {
return true;
@ -645,10 +648,10 @@ const buttonObj = {
},
click: (args) => {
if (!args.selected) {
NotifyManager.warn('请选择要编辑的记录');
NotifyManager.warn(t('action.edit.tip'));
} else {
dialogRef.value.show();
dialog.dialogTitle = '编辑';
dialog.dialogTitle = t('action.edit');
nextTick(() => {
dialogFormRef.value.setStatus(PageStatusEnum.编辑);
dialogFormRef.value.setData(args.selected);
@ -660,7 +663,7 @@ const buttonObj = {
clone: {
name: ButtonEnum.clone,
icon: 'content_copy',
label: '复制',
label: t('action.copy'),
enableIf: (args) => {
if (args.selected) {
return true;
@ -669,10 +672,10 @@ const buttonObj = {
},
click: (args) => {
if (!args.selected) {
NotifyManager.warn('请选择要复制的记录');
NotifyManager.warn(t('action.copy.tip'));
} else {
dialogRef.value.show();
dialog.dialogTitle = '复制';
dialog.dialogTitle = t('action.copy');
nextTick(() => {
dialogFormRef.value.setStatus(PageStatusEnum.新增);
dialogFormRef.value.setData(args.selected);
@ -683,7 +686,7 @@ const buttonObj = {
remove: {
name: ButtonEnum.remove,
icon: IconEnum.删除,
label: '删除',
label: t('action.remove'),
enableIf: (args) => {
if (args.ticked) {
return true;
@ -697,8 +700,8 @@ const buttonObj = {
remove();
} else {
$q.dialog({
title: '询问',
message: '您确认要删除所选记录吗?',
title: t('confirm'),
message: t('action.remove.tip'),
cancel: true,
persistent: true,
}).onOk(() => {
@ -710,7 +713,7 @@ const buttonObj = {
view: {
name: ButtonEnum.view,
icon: IconEnum.查看,
label: '查看',
label: t('action.view'),
enableIf: (args) => {
if (args.selected) {
return true;
@ -724,7 +727,7 @@ const buttonObj = {
export: {
name: ButtonEnum.export,
icon: 'file_download',
label: '导出',
label: t('action.export'),
click: () => {
const content = [tableColumns.value.map((col) => wrapCsvValue(col.label))]
.concat(
@ -743,16 +746,16 @@ const buttonObj = {
});
if (status !== true) {
NotifyManager.error('导出失败');
NotifyManager.error(t('action.export.failed'));
}
},
},
addTop: {
name: ButtonEnum.addTop,
icon: IconEnum.新增,
label: '新增顶级节点',
label: t('action.addTop'),
click: () => {
dialog.dialogTitle = '新增顶级节点';
dialog.dialogTitle = t('action.addTop');
dialogRef.value.show();
nextTick(() => {
dialogFormRef.value.setStatus('addTop');
@ -763,7 +766,7 @@ const buttonObj = {
addChild: {
name: ButtonEnum.addChild,
icon: 'playlist_add',
label: '新增子节点',
label: t('action.addChild'),
enableIf: (args) => {
if (args.selected) {
return true;
@ -771,7 +774,7 @@ const buttonObj = {
return false;
},
click: () => {
dialog.dialogTitle = '新增子节点';
dialog.dialogTitle = t('action.addChild');
dialogRef.value.show();
nextTick(() => {
dialogFormRef.value.setStatus('addChild');
@ -791,14 +794,14 @@ const buttonObj = {
resetDefaultValues: {
name: ButtonEnum.resetDefaultValues,
icon: 'bi-copy',
label: '恢复默认值',
label: t('action.resetDefaultValues'),
click: (tips: boolean = true) => {
if (!tips) {
resetDefaultValues();
} else {
$q.dialog({
title: '询问',
message: '您确认要恢复默认值吗?',
title: t('confirm'),
message: t('action.resetDefaultValues.tip'),
cancel: true,
persistent: true,
}).onOk(() => {
@ -898,9 +901,9 @@ const visibleColumnsComputed = computed(() => {
});
const state = reactive({
noDataLabel: '没有可用数据',
noDataLabel: t('tip.noData'),
loading: false,
loadingLabel: '数据加载中',
loadingLabel: t('tip.dataLoading'),
bodyCellHeight: denseBodyComputed.value ? 24 : 48,
//
refHeightWidth: {
@ -965,11 +968,11 @@ const allTickedStatus = () => {
};
const dialog = reactive({
dialogTitle: '新增',
dialogTitle: t('action.addNew'),
dialogButtons: [
{
icon: IconEnum.提交,
label: '提交',
label: t('action.submit'),
loading: false,
click: () => {
save();
@ -1140,65 +1143,43 @@ const selectionComputed = computed(() => {
}
});
const tableHeightComputed = computed(() => {
//
const screenHeight = $q.screen.height;
// XX
const footerHeight = gc.theme.footer.show ? gc.theme.footer.height : 0;
//
const mainPaddingBottom = gc.theme.main.paddingBottom || 0;
//
const mainContainerPaddingBottom = gc.theme.main.containerPaddingBottom || 0;
// 4px
const otherHeight = table.spaceHeight || 4;
const resultHeight =
screenHeight -
state.refHeightWidth.yLocation -
(state.refHeightWidth.topHeight || 0) -
(state.refHeightWidth.bottomHeight || 0) -
footerHeight -
mainPaddingBottom -
mainContainerPaddingBottom -
otherHeight;
return {
height: props.height > 0 ? props.height + 'px' : resultHeight + 'px',
const tableHeightComputed = () => {
let availableHeight = 0;
const parentHtmlElement = tableRef?.value?.$el?.parentElement;
if (parentHtmlElement) {
availableHeight = Math.floor(parentHtmlElement.clientHeight);
}
availableHeight -= state.refHeightWidth.topHeight || 0;
availableHeight -= state.refHeightWidth.bottomHeight || 0;
availableHeight -= table.spaceHeight || 0;
const style = {
height: props.height > 0 ? props.height + 'px' : availableHeight > 0 ? availableHeight + 'px' : '0px',
};
// if ((table.rows && table.rows.length > 0) || attrs['hide-bottom']) {
return style;
};
// } else {
// return {
// height: state.refHeightWidth.middleHeight + 'px',
// };
// }
});
const noDataTrHeightComputed = () => {
let availableHeight = 0;
const parentHtmlElement = tableRef?.value?.$el?.parentElement;
if (parentHtmlElement) {
availableHeight = Math.floor(parentHtmlElement.clientHeight);
}
availableHeight -= state.refHeightWidth.topHeight || 0;
availableHeight -= state.refHeightWidth.bottomHeight || 0;
availableHeight -= table.spaceHeight || 0;
const noDataTrHeightComputed = computed(() => {
//
const screenHeight = $q.screen.height;
// XX
const footerHeight = gc.theme.footer.show ? gc.theme.footer.height : 0;
//
const mainPaddingBottom = gc.theme.main.paddingBottom || 0;
//
const mainContainerPaddingBottom = gc.theme.main.containerPaddingBottom || 0;
// 4px
const otherHeight = table.spaceHeight || 4;
const resultHeight =
screenHeight -
state.refHeightWidth.noDataTrYLocation -
(state.refHeightWidth.bottomHeight || 0) -
footerHeight -
mainPaddingBottom -
mainContainerPaddingBottom -
(state.refHeightWidth.middleScrollWidth - state.refHeightWidth.middleWidth > 0 ? 15 : 0) -
otherHeight;
return {
height:
props.height > 0
? props.height - state.refHeightWidth.titleTotalHeight - (state.refHeightWidth.middleScrollWidth - state.refHeightWidth.middleWidth > 0 ? 15 : 0) + 'px'
: resultHeight + 'px',
availableHeight -= state.refHeightWidth.titleTotalHeight;
availableHeight -= state.refHeightWidth.middleScrollWidth - state.refHeightWidth.middleWidth > 0 ? 15 : 0;
const style = {
height: props.height > 0 ? props.height + 'px' : availableHeight > 0 ? availableHeight + 'px' : '0px',
};
});
return style;
};
const noDataTrColspanComputed = computed(() => {
let colspan = excludeColumnNum.value;
colspan += visibleColumnsComputed.value.length;
@ -1464,14 +1445,14 @@ const save = async () => {
.then((resp) => {
dialog.dialogButtons[0].loading = false;
emit('afterEditorDataSubmit', resp.data);
NotifyManager.info('操作成功');
NotifyManager.info(t('tip.operationSuccess'));
dialogRef.value.hide();
onRequest({ pagination: state.pagination });
})
.catch((error) => {
console.info('error====', error);
dialog.dialogButtons[0].loading = false;
NotifyManager.error('操作失败');
NotifyManager.error(t('tip.operationFailed'));
});
} else {
dialog.dialogButtons[0].loading = false;
@ -1482,7 +1463,7 @@ const save = async () => {
};
const view = () => {
if (!getSelectedRowsComputed.value || getSelectedRowsComputed.value.length <= 0) {
NotifyManager.warn('请选择要查看的记录');
NotifyManager.warn(t('action.view.tip'));
} else {
viewInfo.infoArray = <any>[];
if (props.viewer.panel.fields && props.viewer.panel.fields.length > 0) {
@ -2226,7 +2207,7 @@ const setQueryCriteriaFieldValueHandler = (criteria, fieldName, fieldValue) => {
};
const setQueryCriteriaFieldValue = (fieldName, fieldValue) => {
if (Object.keys(queryCriteria).length === 0) {
console.error('queryCriteria未设置,无法执行setQueryCriteriaFieldValue方法');
console.error('queryCriteria NOT setting, can NOT execute setQueryCriteriaFieldValue method');
return;
}
setQueryCriteriaFieldValueHandler(queryCriteria, fieldName, fieldValue);

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

@ -42,6 +42,8 @@ import WToolbar from './toolbar/WToolbar.vue';
import WTreeGrid from './tree/WTreeGrid.vue';
import WWorkflowAction from './workflow/WWorkflowAction.vue';
export default {
install: (app: App) => {
app.component('WPlatformPage', WPlatformPage);
@ -85,6 +87,8 @@ export default {
app.component('WToolbar', WToolbar);
app.component('WTreeGrid', WTreeGrid);
app.component('WWorkflowAction', WWorkflowAction);
},
};
@ -119,6 +123,7 @@ export {
WSuccessFailedTag,
WToolbar,
WTreeGrid,
WWorkflowAction,
};
export { PlatformIconEnum } from './utils';

31
io.sc.platform.core.frontend/src/platform/components/layout/WVExpandDiv.vue

@ -1,21 +1,17 @@
<template>
<div
ref="containerRef"
:style="{
'min-height': containerHeightRef + 'px',
}"
>
<div ref="containerRef" :style="generateStyle()">
<slot></slot>
</div>
</template>
<script setup lang="ts">
import { inject, ref, onMounted, watch, nextTick, onUpdated } from 'vue';
import { inject, ref, onMounted, watch, onUpdated } from 'vue';
import { useQuasar } from 'quasar';
import { Environment } from '@/platform/plugin/environment';
const props = defineProps({
expand: { type: String, default: 'parent' },
minHeight: { type: Number, default: 100 },
expand: { type: String, default: 'main' },
fixed: { type: Boolean, default: false },
});
const gc = Environment.getConfigure();
@ -23,6 +19,20 @@ const q = useQuasar();
const containerRef = ref<HTMLElement>();
const containerHeightRef = ref(props.minHeight);
const generateStyle = () => {
if (props.fixed) {
return {
overflow: 'scroll',
height: containerHeightRef.value + 'px',
'min-height': containerHeightRef.value + 'px',
};
} else {
return {
'min-height': containerHeightRef.value + 'px',
};
}
};
onMounted(() => {
const eventBus = inject('eventBus');
eventBus.on('onWindowResize', () => {
@ -47,9 +57,10 @@ const changeDivHeight = () => {
containerHeightRef.value = 0;
} else {
let height = availableHeight - y;
height -= gc.theme.main.containerPaddingBottom;
height -= gc.theme.main.containerPaddingBottom || 0;
height -= gc.theme.main.paddingBottom || 0;
if (gc.theme.footer.enable) {
height -= gc.theme.footer.height;
height -= gc.theme.footer.height || 0;
}
containerHeightRef.value = height > 0 ? height : 0;
}

11
io.sc.platform.core.frontend/src/platform/components/layout/WVLayout.vue

@ -0,0 +1,11 @@
<template>
<div style="height:100%">
</div>
</template>
<script setup lang="ts">
const props = defineProps({
});
</script>

193
io.sc.platform.core.frontend/src/platform/components/workflow/BpmTask.vue

@ -0,0 +1,193 @@
<template>
<w-list-grid
ref="listGridRef"
:title="$t('org.wsp.framework.flowable.task.grid.title')"
:data-url="$fc.apiContextPath + '/system/process/query/task/isc'"
:form-fields="listGridFields.query"
:form-field-counter="{ xxs:1, xs:1, sm:2, md:2, lg:2, xl:2, xxl:2 }"
:table-columns="listGridFields.grid"
:detail-fields="listGridFields.detail"
:can-pageable="true"
:tool-bar-actions="listGridToolBarActions"
:table-scroll="{x:'100%',y:$fc.ui.contentHeight-$fc.ui.toolBarHeight-$fc.ui.tableHeaderHeight-$fc.ui.paginationHeight-40}"
@selected-rows-change="selectedRowsChange"
>
</w-list-grid>
<CompleteTaskDialog ref="completeTaskDialogRef" @after-completed="afterCompleted"></CompleteTaskDialog>
</template>
<script setup>
import {
computed, ref, onMounted, createVNode, toRaw
} from 'vue';
import { useRouter, useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { Modal, notification } from 'ant-design-vue';
import { QuestionCircleOutlined } from '@ant-design/icons-vue';
import {
useFrameworkConfiguration, useAxios,
useEnumOptions, getOptionText, useTrueFalseOptions,
useYesNoOptions, useDataComeFromOptions, useRoleOptions,
useOrgOptions, useTableColumn
} from 'framework-core';
import CompleteTaskDialog from './CompleteTaskDialog';
/**
* 定义组件支持的自定义事件
*/
const emit = defineEmits([
'selectedRowsChange', //
]);
const { t } = useI18n();
const axios = useAxios();
const fc = useFrameworkConfiguration();
const router = useRouter();
const route = useRoute();
const listGridRef = ref(null);
const completeTaskDialogRef = ref(null);
const listGridFields = computed(() => {
return {
query: [
{ component:'input', name:'processInstanceId', label:t('org.wsp.framework.flowable.task.entity.processInstanceId') },
],
grid: [
useTableColumn({ width:150, name:'name', title:t('org.wsp.framework.flowable.task.entity.name') }),
useTableColumn({ width:80, name:'owner', title:t('org.wsp.framework.flowable.task.entity.owner') }),
useTableColumn({ width:100, name:'assignee', title:t('org.wsp.framework.flowable.task.entity.assignee') }),
useTableColumn({ width:150, name:'createTime', title:t('org.wsp.framework.flowable.task.entity.createTime') }),
useTableColumn({ width:150, name:'claimTime', title:t('org.wsp.framework.flowable.task.entity.claimTime') }),
useTableColumn({ width:150, name:'processInstanceId', title:t('org.wsp.framework.flowable.task.entity.processInstanceId') }),
]
};
});
const listGridToolBarActions = computed(() => {
return ['*query', 'reset', 'refresh', 'divider',
{
icon: 'PartitionOutlined',
title: t('org.wsp.framework.flowable.action.showWorkflowDiagram'),
enableIf: (selectedRecords) => {
const record = listGridRef?.value?.getSelectedRow();
return selectedRecords.length > 0;
},
click: () => {
console.log('1');
},
},
'divider',
{
icon: 'RightCircleOutlined',
title: t('org.wsp.framework.flowable.action.complete.task'),
enableIf: (selectedRecords) => {
const record = listGridRef?.value?.getSelectedRow();
return selectedRecords.length > 0;
},
click: () => {
const record = listGridRef?.value?.getSelectedRow();
completeTaskDialogRef?.value?.open(record.id);
},
},
{
icon: 'DownCircleOutlined',
title: t('org.wsp.framework.flowable.action.claim.task'),
enableIf: (selectedRecords) => {
const record = listGridRef?.value?.getSelectedRow();
return selectedRecords.length > 0 && !record.assignee;
},
click: () => {
const record = listGridRef?.value?.getSelectedRow();
axios.post(
fc.apiContextPath + '/system/process/operation/claim/' + record.id,
null
).then(() => {
notification.success({
message: t('operationSuccess'),
duration:2
});
listGridRef?.value.refresh();
});
},
},
{
icon: 'UpCircleOutlined',
title: t('org.wsp.framework.flowable.action.unclaim.task'),
enableIf: (selectedRecords) => {
const record = listGridRef?.value?.getSelectedRow();
return selectedRecords.length > 0 && record.assignee;
},
click: () => {
const record = listGridRef?.value?.getSelectedRow();
axios.post(
fc.apiContextPath + '/system/process/operation/unClaim/' + record.id,
null
).then(() => {
notification.success({
message: t('operationSuccess'),
duration:2
});
listGridRef?.value.refresh();
});
},
},
{
icon: 'RollbackOutlined',
title: t('org.wsp.framework.flowable.action.jump.task'),
enableIf: (selectedRecords) => {
const record = listGridRef?.value?.getSelectedRow();
return selectedRecords.length > 0;
},
click: () => {
console.log('2');
},
},
{
icon: 'CloseCircleOutlined',
title: t('org.wsp.framework.flowable.action.terminate.processInstance'),
enableIf: (selectedRecords) => {
const record = listGridRef?.value?.getSelectedRow();
return selectedRecords.length > 0;
},
click: () => {
const record = listGridRef?.value?.getSelectedRow();
axios.post(
fc.apiContextPath + '/system/process/operation/terminateProcessInstance/' + record.id,
null
).then(() => {
notification.success({
message: t('operationSuccess'),
duration:2
});
listGridRef?.value.refresh();
});
},
}
];
});
const selectedRowsChange = (selectedRecords) => {
if (selectedRecords && selectedRecords.length > 0) {
emit('selectedRowsChange', selectedRecords[0]);
} else {
emit('selectedRowsChange', null);
}
};
const refresh = () => {
listGridRef.value.refresh();
};
const afterCompleted = () => {
notification.success({
message: t('operationSuccess'),
duration:2
});
listGridRef?.value.refresh();
};
defineExpose({
refresh,
});
</script>

67
io.sc.platform.core.frontend/src/platform/components/workflow/CompleteTaskDialog.vue

@ -0,0 +1,67 @@
<template>
<a-modal
v-model:visible="visible"
:force-render="true"
:mask-closable="false"
:title="$t('org.wsp.framework.flowable.action.complete.task')"
>
<a-form
:model="formModelRef"
layout="horizontal"
:label-col="{ span: 6 }"
:wrapper-col="{ span: 18 }"
autocomplete="off"
>
<a-form-item name="variables" :label="$t('org.wsp.framework.flowable.instance.form.variables')">
<a-textarea v-model:value="formModelRef.variables" :rows="6" />
</a-form-item>
<a-form-item name="transientVariables" :label="$t('org.wsp.framework.flowable.instance.form.transientVariables')">
<a-textarea v-model:value="formModelRef.transientVariables" :rows="6" />
</a-form-item>
</a-form>
<template #footer>
<WorkflowAction
ref="workflowActionRef"
action-url="/system/process/operation/complete/"
@after-submit="afterSubmit"
>
</WorkflowAction>
</template>
</a-modal>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue';
import { useI18n } from 'vue-i18n';
import WorkflowAction from './WorkflowAction';
/**
* 定义组件支持的自定义事件
*/
const emit = defineEmits([
'afterCompleted', //
]);
const { t } = useI18n();
const visible = ref(false);
const formModelRef = reactive({});
const workflowActionRef = ref([]);
const open = (taskId) => {
const transientVariables = {};
transientVariables.task_treatment = '此处填写处理意见';
formModelRef.transientVariables = JSON.stringify(transientVariables, null, 2);
workflowActionRef.value.setTaskId(taskId);
workflowActionRef.value.setDataRef(formModelRef);
visible.value = true;
};
const afterSubmit = (taskId) => {
visible.value = false;
emit('afterCompleted');
};
defineExpose({
open
});
</script>

69
io.sc.platform.core.frontend/src/platform/components/workflow/SelectAssigneeDialog.vue

@ -0,0 +1,69 @@
<template>
<a-modal
v-model:visible="visible"
:force-render="true"
:mask-closable="false"
:title="$t('org.wsp.framework.flowable.window.selectAssignee.title')"
centered
@ok="okHandle"
>
<a-space direction="vertical">
<div>{{ t('org.wsp.framework.flowable.task.tip.selectAssignee') }}</div>
<a-form
:model="formModelRef"
layout="horizontal"
:label-col="{ span: 6 }"
:wrapper-col="{ span: 18 }"
autocomplete="off"
>
<a-form-item name="assignee" :label="$t('org.wsp.framework.flowable.task.entity.assignee')">
<a-select v-model:value="formModelRef.assignee" :options="assigneeOptionsRef" />
</a-form-item>
</a-form>
</a-space>
</a-modal>
</template>
<script setup>
import {
ref, reactive, toRaw, computed, onMounted
} from 'vue';
import { useI18n } from 'vue-i18n';
import { notification } from 'ant-design-vue';
import { useFrameworkConfiguration, useAxios } from 'framework-core';
/**
* 定义组件支持的自定义事件
*/
const emit = defineEmits([
'assigneeSelected', //
]);
const fc = useFrameworkConfiguration();
const axios = useAxios();
const { t } = useI18n();
const visible = ref(false);
const formModelRef = reactive({});
const assigneeOptionsRef = ref(null);
const okHandle = () => {
emit('assigneeSelected', formModelRef.assignee);
visible.value = false;
};
const open = (assignees) => {
visible.value = true;
formModelRef.assignee = null;
const result = [];
if (assignees) {
for (let i = 0; i < assignees.length; i++) {
result.push({ value : assignees[i].loginName, label : assignees[i].loginName + '/' + assignees[i].userName, text : assignees[i].loginName + '/' + assignees[i].userName });
}
}
assigneeOptionsRef.value = result;
};
defineExpose({
open
});
</script>

96
io.sc.platform.core.frontend/src/platform/components/workflow/WWorkflowAction.vue

@ -0,0 +1,96 @@
<template>
<div class="flex justify-end gap-4">
<q-btn v-for="action in actionsRef" :key="action.name" :label="action.title" @click="buttonClick(action)"></q-btn>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { Environment, axios } from '@/platform';
/**
* 定义组件支持的自定义属性
*/
const props = defineProps({
// 退 url,,使
gobackActionUrl: { type: String, default: '/api/flowable/process/operation/getGobacks' },
// url
actionUrl: { type: String, default: undefined },
// ID
taskId: { type: String, default: undefined },
//
transientVariables: { type: Object, default: undefined },
// 退
actionButtonWidth: { type: Number, default: 100 },
//
defaultActionButtons: { type: [Array, Object], default: undefined },
//
defaultActionButtonsAlign: { type: String, default: 'right' },
});
/**
* 定义组件支持的自定义事件
*/
const emit = defineEmits([
'afterSubmit', //
]);
const { t } = useI18n();
const taskIdRef = ref(props.taskId);
const actionsRef = ref([]);
const buildMembers = (gobacks) => {
actionsRef.value.splice(0, actionsRef.value.length);
const members = [];
// ()
if (props.defaultActionButtonsAlign === 'left') {
if (props.defaultActionButtons && props.defaultActionButtons && props.defaultActionButtons.length > 0) {
for (let i = 0; i < props.defaultActionButtons.length; i++) {
members.push(props.defaultActionButtons[i]);
}
}
}
// 退
if (gobacks) {
for (let i = 0; i < gobacks.length; i++) {
const goback = gobacks[i];
const transientVariables = {};
transientVariables[goback.variableName] = goback.variableValue;
members.push({
title: goback.title || t('goback'),
transientVariables,
});
}
}
//
members.push({ title: t('submit') });
// ()
if (props.defaultActionButtonsAlign === 'right') {
if (props.defaultActionButtons && props.defaultActionButtons && props.defaultActionButtons.length > 0) {
for (let i = 0; i < props.defaultActionButtons.length; i++) {
members.push(props.defaultActionButtons[i]);
}
}
}
//
for (let i = 0; i < members.length; i++) {
actionsRef.value.push(members[i]);
}
};
const setTaskId = (taskId) => {
taskIdRef.value = taskId;
axios.get(Environment.apiContextPath(props.gobackActionUrl + '/' + taskId)).then((data) => {
buildMembers(data.data);
});
};
defineExpose({
setTaskId,
});
</script>

158
io.sc.platform.core.frontend/src/platform/components/workflow/WorkflowAction.vue

@ -0,0 +1,158 @@
<template>
<div>
<a-button v-for="action in actionsRef" @click="buttonClick(action)">{{ action.title }}</a-button>
</div>
<SelectAssigneeDialog ref="selectAssigneeDialogRef" @assignee-selected="assigneeSelected"></SelectAssigneeDialog>
</template>
<script setup>
import {
ref, reactive, toRaw, computed, onMounted
} from 'vue';
import { useI18n } from 'vue-i18n';
import { useStore } from 'vuex';
import { notification } from 'ant-design-vue';
import { useFrameworkConfiguration, useAxios } from 'framework-core';
import SelectAssigneeDialog from './SelectAssigneeDialog';
/**
* 定义组件支持的自定义属性
*/
const props = defineProps({
// 退 url,,使
gobackActionUrl : { type: String, default: '/system/process/operation/getGobacks/' },
// url
actionUrl : { type: String, default: undefined },
// ID
taskId : { type: String, default: undefined },
//
transientVariables : { type: Object, default: undefined },
// 退
actionButtonWidth : { type: Number, default: 100 },
//
defaultActionButtons : { type: [Array, Object], default: undefined },
//
defaultActionButtonsAlign : { type: String, default: 'right' },
});
/**
* 定义组件支持的自定义事件
*/
const emit = defineEmits([
'afterSubmit', //
]);
const fc = useFrameworkConfiguration();
const store = useStore();
const axios = useAxios();
const { t } = useI18n();
const actionIdRef = ref(null);
const selectAssigneeDialogRef = ref(null);
let dataRef = null;
let currentAction = null;
const actionsRef = reactive([]);
const buildMembers = (gobacks) => {
actionsRef.splice(0, actionsRef.length);
const members = [];
// ()
if (props.defaultActionButtonsAlign === 'left') {
if (props.defaultActionButtons && props.defaultActionButtons && props.defaultActionButtons.length > 0) {
for (let i = 0; i < props.defaultActionButtons.length; i++) {
members.push(props.defaultActionButtons[i]);
}
}
}
// 退
if (gobacks) {
for (let i = 0; i < gobacks.length; i++) {
const goback = gobacks[i];
const transientVariables = {};
transientVariables[goback.variableName] = goback.variableValue;
members.push({
title : goback.title || t('goback'),
transientVariables,
});
}
}
//
members.push({ title: t('submit') });
// ()
if (props.defaultActionButtonsAlign === 'right') {
if (props.defaultActionButtons && props.defaultActionButtons && props.defaultActionButtons.length > 0) {
for (let i = 0; i < props.defaultActionButtons.length; i++) {
members.push(props.defaultActionButtons[i]);
}
}
}
//
for (let i = 0; i < members.length; i++) {
actionsRef.push(members[i]);
}
};
const setTaskId = (taskId) => {
actionIdRef.value = taskId;
axios.get(
fc.apiContextPath + props.gobackActionUrl + taskId,
).then((data) => {
buildMembers(data.data);
});
};
const buttonClick = (action) => {
currentAction = action;
//
const transientVariables = {};
// 退退
axios.post(
fc.apiContextPath + props.actionUrl + actionIdRef.value,
action
).then((data) => {
const rawData = data.data;
if (rawData.code === 0) { //
emit('afterSubmit');
} else if (rawData.code === 1) { //
selectAssigneeDialogRef.value.open(rawData.assignees);
}
});
};
const assigneeSelected = (assignee) => {
const data = currentAction;
if (dataRef.variables) {
data.variables = JSON.parse(dataRef.variables);
}
if (dataRef.transientVariables) {
data.transientVariables = JSON.parse(dataRef.transientVariables);
data.transientVariables.assignee = assignee;
}
axios.post(
fc.apiContextPath + props.actionUrl + actionIdRef.value,
data
).then((response) => {
const rawData = response.data;
if (rawData.code === 0) { //
emit('afterSubmit');
}
});
};
const setDataRef = (_dataRef) => {
if (_dataRef) {
dataRef = _dataRef;
}
};
defineExpose({
setTaskId,
setDataRef
});
</script>

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

@ -32,6 +32,7 @@
"myTasks": "My Tasks",
"org.springframework.security.authentication.InsufficientAuthenticationException": "Please login and try again!",
"reset": "Reset",
"rownum": "Row Number",
"search": "Search",
"security.login.message.error": "authentication failed! please check your user name and password.",
"security.login.password.placeholder": "Please input password",
@ -117,5 +118,33 @@
"cron.year.notSpecify": "Not Specify",
"cron.year.period.1": "From",
"cron.year.period.2": " Year, To ",
"cron.year.period.3": " Year"
"cron.year.period.3": " Year",
"action.query": "Query",
"action.moreQueryConditions": "More Query Conditions",
"action.reset": "Reset",
"action.refresh": "Refresh",
"action.addNew": "Add New",
"action.addTop": "Add Top Node",
"action.addChild": "Add Child Node",
"action.edit": "Edit",
"action.edit.tip": "Please select a record for edit",
"action.copy": "Copy",
"action.copy.tip": "Please select a record for copy",
"action.remove": "Remove",
"action.remove.tip": "Are you sure to remove the record?",
"action.resetDefaultValues": "Restore Default Values",
"action.resetDefaultValues.tip": "Are you sure to restore the default values?",
"action.view": "View",
"action.view.tip": "Please select a record for view",
"action.export": "Export",
"action.export.failed": "Export Failed",
"action.submit": "Submit",
"tip.noData": "No Data",
"tip.dataLoading": "Data Loading",
"tip.operationSuccess": "Operation Success",
"tip.operationFailed": "Operation Failed",
"tip.pagenation.totalRecord": "{count} records was found."
}

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

@ -32,6 +32,7 @@
"myTasks": "我的任務",
"org.springframework.security.authentication.InsufficientAuthenticationException": "當前用戶還未登錄, 請登錄後重试!",
"reset": "重置",
"rownum": "序號",
"search": "搜索",
"security.login.message.error": "認證失敗!請檢查您輸入的登陸名和密碼是否正確",
"security.login.password.placeholder": "請輸入密碼",
@ -117,5 +118,32 @@
"cron.year.notSpecify": "不指定",
"cron.year.period.1": "从",
"cron.year.period.2": "年, 到",
"cron.year.period.3": "年"
"cron.year.period.3": "年",
"action.query": "查詢",
"action.moreQueryConditions": "更多查詢條件",
"action.reset": "重置",
"action.refresh": "刷新",
"action.addNew": "新增",
"action.addTop": "新增頂級節點",
"action.addChild": "新增子節點",
"action.edit": "編輯",
"action.edit.tip": "請選擇一條記錄進行編輯",
"action.copy": "複製",
"action.copy.tip": "請選擇一條記錄進行複製",
"action.remove": "刪除",
"action.remove.tip": "您確定要刪除該記錄嗎?",
"action.resetDefaultValues": "恢復默認值",
"action.resetDefaultValues.tip": "您確定要恢復默認值嗎?",
"action.view": "查看",
"action.view.tip": "請選擇一條記錄進行查看",
"action.export": "導出",
"action.export.failed": "導出失敗",
"action.submit": "提交",
"tip.noData": "未查找到任何數據",
"tip.dataLoading": "數據加載中",
"tip.operationSuccess": "操作成功",
"tip.operationFailed": "操作失敗",
"tip.pagenation.totalRecord": "共 {count} 條記錄."
}

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

@ -32,6 +32,7 @@
"myTasks": "我的任务",
"org.springframework.security.authentication.InsufficientAuthenticationException": "当前用户还未登录, 请登录后重试!",
"reset": "重置",
"rownum": "序号",
"search": "搜索",
"security.login.message.error": "认证失败! 请检查您输入的登录名和密码是否正确。",
"security.login.password.placeholder": "请输入密码",
@ -117,5 +118,32 @@
"cron.year.notSpecify": "不指定",
"cron.year.period.1": "从",
"cron.year.period.2": "年, 到",
"cron.year.period.3": "年"
"cron.year.period.3": "年",
"action.query": "查询",
"action.moreQueryConditions": "更多查询条件",
"action.reset": "重置",
"action.refresh": "刷新",
"action.addNew": "新增",
"action.addTop": "新增顶级节点",
"action.addChild": "新增子节点",
"action.edit": "编辑",
"action.edit.tip": "请选择一条记录进行编辑",
"action.copy": "复制",
"action.copy.tip": "请选择一条记录进行复制",
"action.remove": "删除",
"action.remove.tip": "您确定要删除该记录吗?",
"action.resetDefaultValues": "恢复默认值",
"action.resetDefaultValues.tip": "您确定要恢复默认值吗?",
"action.view": "查看",
"action.view.tip": "请选择一条记录进行查看",
"action.export": "导出",
"action.export.failed": "导出失败",
"action.submit": "提交",
"tip.noData": "未查找到任何数据",
"tip.dataLoading": "数据加载中",
"tip.operationSuccess": "操作成功",
"tip.operationFailed": "操作失败",
"tip.pagenation.totalRecord": "共 {count} 条记录."
}

1
io.sc.platform.core.frontend/src/platform/index.ts

@ -139,6 +139,7 @@ export {
WSuccessFailedTag,
WToolbar,
WTreeGrid,
WWorkflowAction,
} from './components';
export { PlatformIconEnum } from './components';

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

@ -23,7 +23,7 @@
<router-view v-slot="{ Component }" class="full">
<keep-alive>
<suspense>
<w-v-expand-div>
<w-v-expand-div fixed expand="main">
<component :is="Component" />
</w-v-expand-div>
</suspense>
@ -151,5 +151,4 @@
import SiderCollapser from './SiderCollapser.vue';
import MoreTagViewAction from './MoreTagViewAction.vue';
import { TagViewManager } from '@/platform/plugin/manager';
import WVExpandDiv from '@/platform/components/layout/WVExpandDiv.vue';
</script>

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

@ -11,6 +11,7 @@
"permissions": ["/testcase/formElements/**/*"]
}
},
{
"name": "route.testcase.likm.dialog",
"path": "testcase/likm/dialog",

25
io.sc.platform.core.frontend/src/utils/PassOrNotFormater.ts

@ -0,0 +1,25 @@
import { Tools } from '@/platform';
const PassOrNotFormater = (value) => {
if (Tools.isUndefinedOrNull(value)) {
return '';
}
if (value === 'PASSED') {
return {
componentType: 'QIcon',
attrs: { name: 'bi-check-circle', size: '20px', color: 'green' },
};
} else if (value === 'UN_PASSED') {
return {
componentType: 'QIcon',
attrs: { name: 'bi-x-circle', size: '20px', color: 'red' },
};
} else if (value === 'ERROR') {
return {
componentType: 'QIcon',
attrs: { name: 'bi-x-circle', size: '20px', color: 'red' },
};
}
};
export default PassOrNotFormater;

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

@ -1,258 +1,85 @@
<template>
<div class="q-pa-md">
<q-table flat bordered separator="cell" title="Treats" :rows="rows2" :columns="columns" row-key="name" hide-no-data>
<template v-if="rows && rows.length > 0" #header="props">
<q-tr :props="props">
<q-th v-for="col in props.cols" :key="col.name" :props="props" class="text-italic text-purple">
{{ col.label }}
</q-th>
</q-tr>
</template>
<template v-else #header="props">
<q-tr :props="props">
<q-th v-for="col in props.cols" :key="col.name" :props="props" class="text-italic text-purple">
{{ col.label }}
</q-th>
</q-tr>
<q-tr :props="props" style="height: 300px"> </q-tr>
</template>
<template #body="props">
<q-tr :props="props">
<q-td key="name" :props="props">
{{ props.row.name }}
</q-td>
<q-td key="calories" :props="props">
<q-badge color="green">
{{ props.row.calories }}
</q-badge>
</q-td>
<q-td key="fat" :props="props">
<q-badge color="purple">
{{ props.row.fat }}
</q-badge>
</q-td>
<q-td key="carbs" :props="props">
<q-badge color="orange">
{{ props.row.carbs }}
</q-badge>
</q-td>
<q-td key="protein" :props="props">
<q-badge color="primary">
{{ props.row.protein }}
</q-badge>
</q-td>
<q-td key="sodium" :props="props">
<q-badge color="teal">
{{ props.row.sodium }}
</q-badge>
</q-td>
<q-td key="calcium" :props="props">
<q-badge color="accent">
{{ props.row.calcium }}
</q-badge>
</q-td>
<q-td key="iron" :props="props">
<q-badge color="amber">
{{ props.row.iron }}
</q-badge>
</q-td>
</q-tr>
</template>
</q-table>
</div>
<w-dialog v-model="isShow">
<w-grid
ref="userGridRef"
:title="$t('system.user.grid.title')"
:config-button="true"
selection="multiple"
:checkbox-selection="true"
:data-url="Environment.apiContextPath('/api/system/user')"
:pagination="{
sortBy: 'loginName',
descending: false,
}"
:query-form-cols-num="3"
:query-form-fields="[
{ name: 'loginName', label: $t('loginName'), type: 'text' },
{ name: 'userName', label: $t('userName'), type: 'text' },
{ name: 'enable', label: $t('isEnable'), type: 'select' },
]"
:toolbar-configure="{ noIcon: false }"
:toolbar-actions="[
'query',
'refresh',
'separator',
'add',
'clone',
'edit',
'remove',
'separator',
{
name: 'setPassword',
label: $t('system.user.grid.toolbar.setPassword'),
icon: 'bi-shield-check',
enableIf: function (arg) {
return arg.selected;
},
click: function (arg) {},
},
{
name: 'setAllPassword',
label: $t('system.user.grid.toolbar.setAllPassword'),
icon: 'bi-shield',
click: function () {},
},
'separator',
{
name: 'resetPassword',
label: $t('system.user.grid.toolbar.resetPassword'),
icon: 'bi-shield-fill-check',
enableIf: function (arg) {
return arg.selected;
},
click: function (arg) {},
},
{
name: 'resetAllPassword',
label: $t('system.user.grid.toolbar.resetAllPassword'),
icon: 'bi-shield-fill',
click: function () {},
},
'separator',
'view',
'separator',
'export',
]"
:columns="[
{ width: 150, name: 'loginName', label: $t('loginName') },
{ width: '100%', name: 'userName', label: $t('userName') },
{
width: 150,
name: 'enable',
label: $t('status'),
},
{ width: 100, name: 'lastModifier', label: $t('lastModifier') },
{ width: 110, name: 'lastModifyDate', label: $t('lastModifyDate') },
]"
></w-grid>
</w-dialog>
</template>
<script>
import { ref } from 'vue';
const columns = [
{
name: 'name',
required: true,
label: 'Dessert (100g serving)',
align: 'left',
field: (row) => row.name,
format: (val) => `${val}`,
sortable: true,
},
{ name: 'calories', align: 'center', label: 'Calories', field: 'calories', sortable: true },
{ name: 'fat', label: 'Fat (g)', field: 'fat', sortable: true },
{ name: 'carbs', label: 'Carbs (g)', field: 'carbs' },
{ name: 'protein', label: 'Protein (g)', field: 'protein' },
{ name: 'sodium', label: 'Sodium (mg)', field: 'sodium' },
{ name: 'calcium', label: 'Calcium (%)', field: 'calcium', sortable: true, sort: (a, b) => parseInt(a, 10) - parseInt(b, 10) },
{ name: 'iron', label: 'Iron (%)', field: 'iron', sortable: true, sort: (a, b) => parseInt(a, 10) - parseInt(b, 10) },
];
const rows = [];
const rows2 = [
{
name: 'Frozen Yogurt',
calories: 159,
fat: 6.0,
carbs: 24,
protein: 4.0,
sodium: 87,
calcium: '14%',
iron: '1%',
},
{
name: 'Ice cream sandwich',
calories: 237,
fat: 9.0,
carbs: 37,
protein: 4.3,
sodium: 129,
calcium: '8%',
iron: '1%',
},
{
name: 'Eclair',
calories: 262,
fat: 16.0,
carbs: 23,
protein: 6.0,
sodium: 337,
calcium: '6%',
iron: '7%',
},
{
name: 'Cupcake',
calories: 305,
fat: 3.7,
carbs: 67,
protein: 4.3,
sodium: 413,
calcium: '3%',
iron: '8%',
},
{
name: 'Gingerbread',
calories: 356,
fat: 16.0,
carbs: 49,
protein: 3.9,
sodium: 327,
calcium: '7%',
iron: '16%',
},
{
name: 'Jelly bean',
calories: 375,
fat: 0.0,
carbs: 94,
protein: 0.0,
sodium: 50,
calcium: '0%',
iron: '0%',
},
{
name: 'Lollipop',
calories: 392,
fat: 0.2,
carbs: 98,
protein: 0,
sodium: 38,
calcium: '0%',
iron: '2%',
},
{
name: 'Honeycomb',
calories: 408,
fat: 3.2,
carbs: 87,
protein: 6.5,
sodium: 562,
calcium: '0%',
iron: '45%',
},
{
name: 'Donut',
calories: 452,
fat: 25.0,
carbs: 51,
protein: 4.9,
sodium: 326,
calcium: '2%',
iron: '22%',
},
{
name: 'KitKat',
calories: 518,
fat: 26.0,
carbs: 65,
protein: 7,
sodium: 54,
calcium: '12%',
iron: '6%',
},
{
name: 'KitKat',
calories: 518,
fat: 26.0,
carbs: 65,
protein: 7,
sodium: 54,
calcium: '12%',
iron: '6%',
},
{
name: 'KitKat',
calories: 518,
fat: 26.0,
carbs: 65,
protein: 7,
sodium: 54,
calcium: '12%',
iron: '6%',
},
{
name: 'KitKat',
calories: 518,
fat: 26.0,
carbs: 65,
protein: 7,
sodium: 54,
calcium: '12%',
iron: '6%',
},
{
name: 'KitKat',
calories: 518,
fat: 26.0,
carbs: 65,
protein: 7,
sodium: 54,
calcium: '12%',
iron: '6%',
},
{
name: 'KitKat',
calories: 518,
fat: 26.0,
carbs: 65,
protein: 7,
sodium: 54,
calcium: '12%',
iron: '6%',
},
{
name: 'KitKat',
calories: 518,
fat: 26.0,
carbs: 65,
protein: 7,
sodium: 54,
calcium: '12%',
iron: '6%',
},
];
<script setup lang="ts">
import { Environment } from '@/platform';
export default {
setup() {
return {
columns,
rows,
};
},
};
const isShow = true;
</script>

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

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

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

@ -11,6 +11,7 @@
"permissions": ["/testcase/formElements/**/*"]
}
},
{
"name": "route.testcase.likm.dialog",
"path": "testcase/likm/dialog",

25
io.sc.platform.core.frontend/template-project/src/utils/PassOrNotFormater.ts

@ -0,0 +1,25 @@
import { Tools } from '@/platform';
const PassOrNotFormater = (value) => {
if (Tools.isUndefinedOrNull(value)) {
return '';
}
if (value === 'PASSED') {
return {
componentType: 'QIcon',
attrs: { name: 'bi-check-circle', size: '20px', color: 'green' },
};
} else if (value === 'UN_PASSED') {
return {
componentType: 'QIcon',
attrs: { name: 'bi-x-circle', size: '20px', color: 'red' },
};
} else if (value === 'ERROR') {
return {
componentType: 'QIcon',
attrs: { name: 'bi-x-circle', size: '20px', color: 'red' },
};
}
};
export default PassOrNotFormater;

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

@ -1,258 +1,85 @@
<template>
<div class="q-pa-md">
<q-table flat bordered separator="cell" title="Treats" :rows="rows2" :columns="columns" row-key="name" hide-no-data>
<template v-if="rows && rows.length > 0" #header="props">
<q-tr :props="props">
<q-th v-for="col in props.cols" :key="col.name" :props="props" class="text-italic text-purple">
{{ col.label }}
</q-th>
</q-tr>
</template>
<template v-else #header="props">
<q-tr :props="props">
<q-th v-for="col in props.cols" :key="col.name" :props="props" class="text-italic text-purple">
{{ col.label }}
</q-th>
</q-tr>
<q-tr :props="props" style="height: 300px"> </q-tr>
</template>
<template #body="props">
<q-tr :props="props">
<q-td key="name" :props="props">
{{ props.row.name }}
</q-td>
<q-td key="calories" :props="props">
<q-badge color="green">
{{ props.row.calories }}
</q-badge>
</q-td>
<q-td key="fat" :props="props">
<q-badge color="purple">
{{ props.row.fat }}
</q-badge>
</q-td>
<q-td key="carbs" :props="props">
<q-badge color="orange">
{{ props.row.carbs }}
</q-badge>
</q-td>
<q-td key="protein" :props="props">
<q-badge color="primary">
{{ props.row.protein }}
</q-badge>
</q-td>
<q-td key="sodium" :props="props">
<q-badge color="teal">
{{ props.row.sodium }}
</q-badge>
</q-td>
<q-td key="calcium" :props="props">
<q-badge color="accent">
{{ props.row.calcium }}
</q-badge>
</q-td>
<q-td key="iron" :props="props">
<q-badge color="amber">
{{ props.row.iron }}
</q-badge>
</q-td>
</q-tr>
</template>
</q-table>
</div>
<w-dialog v-model="isShow">
<w-grid
ref="userGridRef"
:title="$t('system.user.grid.title')"
:config-button="true"
selection="multiple"
:checkbox-selection="true"
:data-url="Environment.apiContextPath('/api/system/user')"
:pagination="{
sortBy: 'loginName',
descending: false,
}"
:query-form-cols-num="3"
:query-form-fields="[
{ name: 'loginName', label: $t('loginName'), type: 'text' },
{ name: 'userName', label: $t('userName'), type: 'text' },
{ name: 'enable', label: $t('isEnable'), type: 'select' },
]"
:toolbar-configure="{ noIcon: false }"
:toolbar-actions="[
'query',
'refresh',
'separator',
'add',
'clone',
'edit',
'remove',
'separator',
{
name: 'setPassword',
label: $t('system.user.grid.toolbar.setPassword'),
icon: 'bi-shield-check',
enableIf: function (arg) {
return arg.selected;
},
click: function (arg) {},
},
{
name: 'setAllPassword',
label: $t('system.user.grid.toolbar.setAllPassword'),
icon: 'bi-shield',
click: function () {},
},
'separator',
{
name: 'resetPassword',
label: $t('system.user.grid.toolbar.resetPassword'),
icon: 'bi-shield-fill-check',
enableIf: function (arg) {
return arg.selected;
},
click: function (arg) {},
},
{
name: 'resetAllPassword',
label: $t('system.user.grid.toolbar.resetAllPassword'),
icon: 'bi-shield-fill',
click: function () {},
},
'separator',
'view',
'separator',
'export',
]"
:columns="[
{ width: 150, name: 'loginName', label: $t('loginName') },
{ width: '100%', name: 'userName', label: $t('userName') },
{
width: 150,
name: 'enable',
label: $t('status'),
},
{ width: 100, name: 'lastModifier', label: $t('lastModifier') },
{ width: 110, name: 'lastModifyDate', label: $t('lastModifyDate') },
]"
></w-grid>
</w-dialog>
</template>
<script>
import { ref } from 'vue';
const columns = [
{
name: 'name',
required: true,
label: 'Dessert (100g serving)',
align: 'left',
field: (row) => row.name,
format: (val) => `${val}`,
sortable: true,
},
{ name: 'calories', align: 'center', label: 'Calories', field: 'calories', sortable: true },
{ name: 'fat', label: 'Fat (g)', field: 'fat', sortable: true },
{ name: 'carbs', label: 'Carbs (g)', field: 'carbs' },
{ name: 'protein', label: 'Protein (g)', field: 'protein' },
{ name: 'sodium', label: 'Sodium (mg)', field: 'sodium' },
{ name: 'calcium', label: 'Calcium (%)', field: 'calcium', sortable: true, sort: (a, b) => parseInt(a, 10) - parseInt(b, 10) },
{ name: 'iron', label: 'Iron (%)', field: 'iron', sortable: true, sort: (a, b) => parseInt(a, 10) - parseInt(b, 10) },
];
const rows = [];
const rows2 = [
{
name: 'Frozen Yogurt',
calories: 159,
fat: 6.0,
carbs: 24,
protein: 4.0,
sodium: 87,
calcium: '14%',
iron: '1%',
},
{
name: 'Ice cream sandwich',
calories: 237,
fat: 9.0,
carbs: 37,
protein: 4.3,
sodium: 129,
calcium: '8%',
iron: '1%',
},
{
name: 'Eclair',
calories: 262,
fat: 16.0,
carbs: 23,
protein: 6.0,
sodium: 337,
calcium: '6%',
iron: '7%',
},
{
name: 'Cupcake',
calories: 305,
fat: 3.7,
carbs: 67,
protein: 4.3,
sodium: 413,
calcium: '3%',
iron: '8%',
},
{
name: 'Gingerbread',
calories: 356,
fat: 16.0,
carbs: 49,
protein: 3.9,
sodium: 327,
calcium: '7%',
iron: '16%',
},
{
name: 'Jelly bean',
calories: 375,
fat: 0.0,
carbs: 94,
protein: 0.0,
sodium: 50,
calcium: '0%',
iron: '0%',
},
{
name: 'Lollipop',
calories: 392,
fat: 0.2,
carbs: 98,
protein: 0,
sodium: 38,
calcium: '0%',
iron: '2%',
},
{
name: 'Honeycomb',
calories: 408,
fat: 3.2,
carbs: 87,
protein: 6.5,
sodium: 562,
calcium: '0%',
iron: '45%',
},
{
name: 'Donut',
calories: 452,
fat: 25.0,
carbs: 51,
protein: 4.9,
sodium: 326,
calcium: '2%',
iron: '22%',
},
{
name: 'KitKat',
calories: 518,
fat: 26.0,
carbs: 65,
protein: 7,
sodium: 54,
calcium: '12%',
iron: '6%',
},
{
name: 'KitKat',
calories: 518,
fat: 26.0,
carbs: 65,
protein: 7,
sodium: 54,
calcium: '12%',
iron: '6%',
},
{
name: 'KitKat',
calories: 518,
fat: 26.0,
carbs: 65,
protein: 7,
sodium: 54,
calcium: '12%',
iron: '6%',
},
{
name: 'KitKat',
calories: 518,
fat: 26.0,
carbs: 65,
protein: 7,
sodium: 54,
calcium: '12%',
iron: '6%',
},
{
name: 'KitKat',
calories: 518,
fat: 26.0,
carbs: 65,
protein: 7,
sodium: 54,
calcium: '12%',
iron: '6%',
},
{
name: 'KitKat',
calories: 518,
fat: 26.0,
carbs: 65,
protein: 7,
sodium: 54,
calcium: '12%',
iron: '6%',
},
{
name: 'KitKat',
calories: 518,
fat: 26.0,
carbs: 65,
protein: 7,
sodium: 54,
calcium: '12%',
iron: '6%',
},
];
<script setup lang="ts">
import { Environment } from '@/platform';
export default {
setup() {
return {
columns,
rows,
};
},
};
const isShow = true;
</script>

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

@ -102,6 +102,7 @@ normal=Normal
oldValue=Old Value
operation=Operation
operationSuccess=Operation Sucess
operationFailed=Operation Failed
order=Order
org=Organization
parent=Parent
@ -205,4 +206,5 @@ maxValue=Maximum Value
isMaxValueInclude=Is Maximum Value Include
RegExp=Regular Expression
execute=Execute
executeAll=Execute All
executeAll=Execute All
moreQueryCondition=More Query Condition

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

@ -102,6 +102,7 @@ normal=\u6B63\u5E38
oldValue=\u539F\u503C
operation=\u64CD\u4F5C
operationSuccess=\u64CD\u4F5C\u6210\u529F
operationFailed=\u64CD\u4F5C\u5931\u6557
order=\u9806\u5E8F
org=\u6A5F\u69CB
parent=\u7236
@ -205,4 +206,5 @@ maxValue=\u6700\u5927\u503C
isMaxValueInclude=\u662F\u5426\u5305\u542B\u6700\u5927\u503C
RegExp=\u6B63\u5247\u8868\u9054\u5F0F
execute=\u57F7\u884C
executeAll=\u57F7\u884C\u6240\u6709
executeAll=\u57F7\u884C\u6240\u6709
moreQueryCondition=\u66F4\u591A\u67E5\u8A62\u689D\u4EF6

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

@ -102,6 +102,7 @@ normal=\u6B63\u5E38
oldValue=\u539F\u503C
operation=\u64CD\u4F5C
operationSuccess=\u64CD\u4F5C\u6210\u529F
operationFailed=\u64CD\u4F5C\u5931\u8D25
order=\u987A\u5E8F
org=\u673A\u6784
parent=\u7236
@ -205,4 +206,5 @@ maxValue=\u6700\u5927\u503C
isMaxValueInclude=\u662F\u5426\u5305\u542B\u6700\u5927\u503C
RegExp=\u6B63\u5219\u8868\u8FBE\u5F0F
execute=\u6267\u884C
executeAll=\u6267\u884C\u6240\u6709
executeAll=\u6267\u884C\u6240\u6709
moreQueryCondition=\u66F4\u591A\u67E5\u8BE2\u6761\u4EF6

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

@ -14,5 +14,6 @@ include::oauth2/oauth2.adoc[]
include::java/java.adoc[]
include::rwa/rwa.adoc[]
include::icaap/icaap.adoc[]
include::refrence/refrence.adoc[]

10
io.sc.platform.developer.doc/asciidoc/9999-appendix/refrence/html/html.adoc

@ -1 +1,11 @@
= The HTML DOM API
= clientHeight、offsetHeight、scrollHeight
image::9999-appendix/docker/tidb/001.png[,60%]
|===
| clientHeight | 元素高度 + 内边距
| offsetHeight |
| scrollHeight |
|===

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

@ -78,7 +78,7 @@
"luckyexcel": "1.0.1",
"mockjs": "1.1.0",
"pinia": "2.1.7",
"platform-core": "8.1.152",
"platform-core": "8.1.155",
"quasar": "2.14.5",
"tailwindcss": "3.4.0",
"vue": "3.4.3",

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

@ -41,5 +41,4 @@
"developer.backend.export.liquibase.schema" : "方案",
"developer.backend.export.liquibase.tables" : "表",
"developer.backend.export.liquibase.export.tip" : "您確定要導出嗎?"
}

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

@ -41,5 +41,4 @@
"developer.backend.export.liquibase.schema" : "方案",
"developer.backend.export.liquibase.tables" : "表",
"developer.backend.export.liquibase.export.tip" : "您确定要导出吗?"
}

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

@ -74,7 +74,7 @@
/*/*/
{"type":"GROUP", "order":500, "parentId":"menu.developer", "id":"menu.developer.backend", "titleI18nKey":"menu.developer.backend", "icon":"bi-server"},
/*//*/
{"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":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"},

34
io.sc.platform.flowable/src/main/java/io/sc/platform/flowable/controller/ProcessToolsWebController.java

@ -0,0 +1,34 @@
package io.sc.platform.flowable.controller;
import io.sc.platform.flowable.service.ProcessQueryService;
import io.sc.platform.flowable.service.ProcessToolsService;
import io.sc.platform.flowable.support.*;
import io.sc.platform.mvc.support.FileDownloader;
import io.sc.platform.orm.service.support.QueryParameter;
import io.sc.platform.orm.service.support.criteria.Criteria;
import io.sc.platform.orm.service.support.criteria.impl.Equals;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.util.Collections;
import java.util.List;
@RestController
@RequestMapping("/api/flowable/tools")
public class ProcessToolsWebController {
@Autowired private ProcessToolsService service;
@PostMapping("cleanRuntimeData")
public void cleanRuntimeData() throws Exception{
service.cleanRuntimeData();
}
@PostMapping("cleanHistoryData")
public void cleanHistoryData() throws Exception{
service.cleanHistoryData();
}
}

2
io.sc.platform.flowable/src/main/java/io/sc/platform/flowable/jpa/entity/AgentEntity.java

@ -79,7 +79,7 @@ public class AgentEntity extends CorporationAuditorEntity<AgentVo> {
@Override
public AgentVo toVo() {
AgentVo vo =new AgentVo();
CorporationAuditorEntity.toVo(vo,this);
super.toVo(vo);
vo.setId(this.getId());
vo.setLoginName(this.getLoginName());
vo.setUserName(this.getUserName());

2
io.sc.platform.flowable/src/main/java/io/sc/platform/flowable/jpa/entity/ProcessEntity.java

@ -25,8 +25,6 @@ import org.hibernate.annotations.GenericGenerator;
@Entity
@Table(name="SYS_PROCESS")
public class ProcessEntity extends CorporationAuditorEntity<ProcessVo> {
private static final long serialVersionUID = 2056614495312244260L;
//主键
@Id
@GeneratedValue(generator = "system-uuid")

6
io.sc.platform.flowable/src/main/java/io/sc/platform/flowable/service/ProcessToolsService.java

@ -0,0 +1,6 @@
package io.sc.platform.flowable.service;
public interface ProcessToolsService {
public void cleanRuntimeData() throws Exception;
public void cleanHistoryData() throws Exception;
}

10
io.sc.platform.flowable/src/main/java/io/sc/platform/flowable/service/impl/ProcessOperationServiceImpl.java

@ -17,10 +17,7 @@ import io.sc.platform.flowable.service.AgentService;
import io.sc.platform.flowable.service.AssigneeQueryService;
import io.sc.platform.flowable.service.ProcessEntityService;
import io.sc.platform.flowable.service.ProcessOperationService;
import io.sc.platform.flowable.support.Assignee;
import io.sc.platform.flowable.support.CompleteTaskException;
import io.sc.platform.flowable.support.FrameworkVariableNames;
import io.sc.platform.flowable.support.Goback;
import io.sc.platform.flowable.support.*;
import io.sc.platform.flowable.support.command.JumpTaskCommand;
import io.sc.platform.security.util.SecurityUtil;
import org.flowable.bpmn.model.BpmnModel;
@ -184,7 +181,10 @@ public class ProcessOperationServiceImpl implements ProcessOperationService {
List<Assignee> assignees =assigneeQueryService.query(null,newTask, newUserTaskDefinition);
if(assignees!=null && assignees.size()>0){
if(assignees.size()>1){
throw new CompleteTaskException(objectMapper.writeValueAsString(assignees));
SelectAssigneeWrapper selectAssigneeWrapper =new SelectAssigneeWrapper();
selectAssigneeWrapper.setActiveName(newUserTaskDefinition.getName());
selectAssigneeWrapper.setAssignees(assignees);
throw new CompleteTaskException(objectMapper.writeValueAsString(selectAssigneeWrapper));
}else{
taskService.setAssignee(newTask.getId(), assignees.get(0).getLoginName());
return processInstance;

46
io.sc.platform.flowable/src/main/java/io/sc/platform/flowable/service/impl/ProcessToolsServiceImpl.java

@ -0,0 +1,46 @@
package io.sc.platform.flowable.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import io.sc.platform.flowable.service.ProcessToolsService;
import javax.transaction.Transactional;
@Service
public class ProcessToolsServiceImpl implements ProcessToolsService{
@Autowired private JdbcTemplate jdbcTemplate;
@Override
@Transactional
public void cleanRuntimeData() throws Exception {
jdbcTemplate.update("delete from ACT_RU_ACTINST");
jdbcTemplate.update("delete from ACT_RU_DEADLETTER_JOB");
jdbcTemplate.update("delete from ACT_RU_ENTITYLINK");
jdbcTemplate.update("delete from ACT_RU_EVENT_SUBSCR");
jdbcTemplate.update("delete from ACT_RU_EXTERNAL_JOB");
jdbcTemplate.update("delete from ACT_RU_HISTORY_JOB");
jdbcTemplate.update("delete from ACT_RU_IDENTITYLINK");
jdbcTemplate.update("delete from ACT_RU_JOB");
jdbcTemplate.update("delete from ACT_RU_SUSPENDED_JOB");
jdbcTemplate.update("delete from ACT_RU_TASK");
jdbcTemplate.update("delete from ACT_RU_TIMER_JOB");
jdbcTemplate.update("delete from ACT_RU_VARIABLE");
jdbcTemplate.update("delete from ACT_RU_EXECUTION");
}
@Override
@Transactional
public void cleanHistoryData() throws Exception {
jdbcTemplate.update("delete from ACT_HI_ACTINST");
jdbcTemplate.update("delete from ACT_HI_ATTACHMENT");
jdbcTemplate.update("delete from ACT_HI_COMMENT");
jdbcTemplate.update("delete from ACT_HI_DETAIL");
jdbcTemplate.update("delete from ACT_HI_ENTITYLINK");
jdbcTemplate.update("delete from ACT_HI_IDENTITYLINK");
jdbcTemplate.update("delete from ACT_HI_PROCINST");
jdbcTemplate.update("delete from ACT_HI_TASKINST");
jdbcTemplate.update("delete from ACT_HI_TSK_LOG");
jdbcTemplate.update("delete from ACT_HI_VARINST");
}
}

17
io.sc.platform.flowable/src/main/java/io/sc/platform/flowable/support/CompleteTaskResponse.java

@ -12,13 +12,17 @@ import java.util.List;
*
*/
public class CompleteTaskResponse {
private int code =CompleteTaskResponseCode.SUCCESS; //响应代码
private int code =CompleteTaskResponseCode.SUCCESS; //响应代码
private String activeName; // 节点名称
private List<Assignee> assignees =new ArrayList<Assignee>(); //处理人列表
public static CompleteTaskResponse fromAssigneesJson(String assigneesJson) throws Exception{
SelectAssigneeWrapper wrapper =ObjectMapper4Json.getMapper().readValue(assigneesJson,SelectAssigneeWrapper.class);
CompleteTaskResponse result =new CompleteTaskResponse();
result.setCode(CompleteTaskResponseCode.NEED_SELECT_ASSIGNEE);
result.setAssignees((List<Assignee>) ObjectMapper4Json.getMapper().readValue(assigneesJson, new TypeReference<List<Assignee>>(){}));
result.setActiveName(wrapper.getActiveName());
result.setAssignees(wrapper.getAssignees());
return result;
}
@ -35,6 +39,15 @@ public class CompleteTaskResponse {
public void setCode(int code) {
this.code = code;
}
public String getActiveName() {
return activeName;
}
public void setActiveName(String activeName) {
this.activeName = activeName;
}
public List<Assignee> getAssignees() {
return assignees;
}

25
io.sc.platform.flowable/src/main/java/io/sc/platform/flowable/support/SelectAssigneeWrapper.java

@ -0,0 +1,25 @@
package io.sc.platform.flowable.support;
import java.util.ArrayList;
import java.util.List;
public class SelectAssigneeWrapper {
private String activeName;
private List<Assignee> assignees =new ArrayList<>();
public String getActiveName() {
return activeName;
}
public void setActiveName(String activeName) {
this.activeName = activeName;
}
public List<Assignee> getAssignees() {
return assignees;
}
public void setAssignees(List<Assignee> assignees) {
this.assignees = assignees;
}
}

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

@ -91,7 +91,7 @@
"luckyexcel": "1.0.1",
"mockjs": "1.1.0",
"pinia": "2.1.7",
"platform-core": "8.1.152",
"platform-core": "8.1.156",
"quasar": "2.14.5",
"tailwindcss": "3.4.0",
"vue": "3.4.3",

13
io.sc.platform.lcdp.frontend/src/i18n/messages.json

@ -147,6 +147,7 @@
"lcdp.bpm.tabs.processDefine": "Process Define",
"lcdp.bpm.tabs.processInstance": "Process Instance",
"lcdp.bpm.tabs.task": "Process Task",
"lcdp.bpm.tabs.tools": "Tools",
"lcdp.bpm.processDefine.grid.title": "Process Define List",
"lcdp.bpm.processDefine.grid.toolbar.design": "Design",
@ -205,10 +206,20 @@
"lcdp.bpm.variables.grid.entity.text":"Text",
"lcdp.bpm.variables.grid.entity.text2":"Text2",
"lcdp.bpm.completeTask.dialog.title": "Complete Task",
"lcdp.bpm.selectAssignee.dialog.title": "Select Assignee",
"lcdp.bpm.selectAssignee.entity.assignee": "Assignee",
"lcdp.bpm.selectAssignee.entity.assignee.tip": "Next Task: {activeName} <br/>There are some assignees can handler this task, but they can NOT claim this task. <br/>Please select one assignee:",
"lcdp.bpm.jumpTask.dialog.title": "Task Jump",
"lcdp.bpm.jumpTask.form.entity.taskId": "Task ID",
"lcdp.bpm.jumpTask.form.entity.targetActivityId": "Target Activity Node",
"lcdp.bpm.jumpTask.form.entity.targetAssignee": "Assignee"
"lcdp.bpm.jumpTask.form.entity.targetAssignee": "Assignee",
"lcdp.bpm.tools.action.cleanRuntimeData": "Clean Runtime Data",
"lcdp.bpm.tools.action.cleanRuntimeData.tip": "Are you sure to clean the runtime data?",
"lcdp.bpm.tools.action.cleanHistoryData": "Clean History Data",
"lcdp.bpm.tools.action.cleanHistoryData.tip": "Are you sure to clean the history data?"
}

13
io.sc.platform.lcdp.frontend/src/i18n/messages_tw_CN.json

@ -147,6 +147,7 @@
"lcdp.bpm.tabs.processDefine": "流程定義",
"lcdp.bpm.tabs.processInstance": "流程實例",
"lcdp.bpm.tabs.task": "工作任務",
"lcdp.bpm.tabs.tools": "工具",
"lcdp.bpm.processDefine.grid.title": "流程定義列表",
"lcdp.bpm.processDefine.grid.toolbar.design": "設計",
@ -205,8 +206,18 @@
"lcdp.bpm.variables.grid.entity.text":"文本",
"lcdp.bpm.variables.grid.entity.text2":"文本2",
"lcdp.bpm.completeTask.dialog.title": "完成任務",
"lcdp.bpm.selectAssignee.dialog.title": "選擇處理人",
"lcdp.bpm.selectAssignee.entity.assignee": "處理人",
"lcdp.bpm.selectAssignee.entity.assignee.tip": "下一個處理任務: {activeName} <br/>存在多個候選人,且流程不允許自行認領任務. <br/>請選擇一個任務處理人:",
"lcdp.bpm.jumpTask.dialog.title": "任務跳轉",
"lcdp.bpm.jumpTask.form.entity.taskId": "任務ID",
"lcdp.bpm.jumpTask.form.entity.targetActivityId": "目標節點",
"lcdp.bpm.jumpTask.form.entity.targetAssignee": "處理人"
"lcdp.bpm.jumpTask.form.entity.targetAssignee": "處理人",
"lcdp.bpm.tools.action.cleanRuntimeData": "清除運行時數據",
"lcdp.bpm.tools.action.cleanRuntimeData.tip": "您確定要清除清楚運行時數據嗎?",
"lcdp.bpm.tools.action.cleanHistoryData": "清除歷史數據",
"lcdp.bpm.tools.action.cleanHistoryData.tip": "您確定要清除歷史數據嗎?"
}

13
io.sc.platform.lcdp.frontend/src/i18n/messages_zh_CN.json

@ -148,6 +148,7 @@
"lcdp.bpm.tabs.processDefine": "流程定义",
"lcdp.bpm.tabs.processInstance": "流程实例",
"lcdp.bpm.tabs.task": "工作任务",
"lcdp.bpm.tabs.tools": "工具",
"lcdp.bpm.processDefine.grid.title": "流程定义列表",
"lcdp.bpm.processDefine.grid.toolbar.design": "设计",
@ -206,8 +207,18 @@
"lcdp.bpm.variables.grid.entity.text":"文本",
"lcdp.bpm.variables.grid.entity.text2":"文本2",
"lcdp.bpm.completeTask.dialog.title": "完成任务",
"lcdp.bpm.selectAssignee.dialog.title": "选择处理人",
"lcdp.bpm.selectAssignee.entity.assignee": "处理人",
"lcdp.bpm.selectAssignee.entity.assignee.tip": "下一个处理任务: {activeName} <br/>存在多个候选人,且流程不允许由候选人自行认领任务. <br/>请选择一个任务处理人:",
"lcdp.bpm.jumpTask.dialog.title": "任务跳转",
"lcdp.bpm.jumpTask.form.entity.taskId": "任务ID",
"lcdp.bpm.jumpTask.form.entity.targetActivityId": "目标节点",
"lcdp.bpm.jumpTask.form.entity.targetAssignee": "处理人"
"lcdp.bpm.jumpTask.form.entity.targetAssignee": "处理人",
"lcdp.bpm.tools.action.cleanRuntimeData": "清除运行时数据",
"lcdp.bpm.tools.action.cleanRuntimeData.tip": "您确定要清除运行时数据吗?",
"lcdp.bpm.tools.action.cleanHistoryData": "清除历史数据",
"lcdp.bpm.tools.action.cleanHistoryData.tip": "您确定要清除历史数据吗?"
}

140
io.sc.platform.lcdp.frontend/src/views/bpm/Bpm.vue

@ -1,16 +1,17 @@
<template>
<div>
<q-splitter :model-value="100" unit="px" class="w-full h-full">
<div style="height: 100%">
<q-splitter :model-value="150" unit="px" class="w-full" style="height: 100%">
<template #before>
<q-tabs v-model="selectedTabRef" vertical>
<q-tab name="processDefine" icon="mail" :label="$t('lcdp.bpm.tabs.processDefine')" />
<q-tab name="processInstance" icon="mail" :label="$t('lcdp.bpm.tabs.processInstance')" />
<q-tab name="task" icon="mail" :label="$t('lcdp.bpm.tabs.task')" />
<q-tabs v-model="selectedTabRef" vertical no-caps>
<q-tab name="processDefine" icon="bi-share" :label="$t('lcdp.bpm.tabs.processDefine')" />
<q-tab name="processInstance" icon="bi-sliders" :label="$t('lcdp.bpm.tabs.processInstance')" />
<q-tab name="task" icon="bi-receipt" :label="$t('lcdp.bpm.tabs.task')" />
<q-tab name="tools" icon="bi-wrench-adjustable" :label="$t('lcdp.bpm.tabs.tools')" />
</q-tabs>
</template>
<template #after>
<q-tab-panels v-model="selectedTabRef" animated swipeable vertical transition-prev="jump-up" transition-next="jump-up" keep-alive>
<q-tab-panel name="processDefine">
<q-tab-panels v-model="selectedTabRef" animated swipeable vertical transition-prev="jump-up" transition-next="jump-up" keep-alive style="height: 100%">
<q-tab-panel name="processDefine" style="height: 100%">
<w-grid
ref="processDefineGridRef"
:title="$t('lcdp.bpm.processDefine.grid.title')"
@ -254,15 +255,14 @@
name: 'queryTask',
label: $t('lcdp.bpm.processInstance.grid.toolbar.queryTask'),
icon: 'bi-list-ul',
enableIf: (selecteds) => {
return selecteds && selecteds.length > 0;
enableIf: (arg) => {
return arg.selected;
},
click: (selecteds: object[]) => {
if (selecteds && selecteds.length > 0) {
const selected = selecteds[0];
click: (arg) => {
if (arg.selected) {
selectedTabRef = 'task';
nextTick(() => {
taskGridRef.getQueryForm().setFieldValue('processInstanceId', selected.id);
taskGridRef.getQueryForm().setFieldValue('processInstanceId', arg.selected.id);
taskGridRef.refresh();
});
}
@ -272,13 +272,12 @@
name: 'variables',
label: $t('lcdp.bpm.task.grid.toolbar.variables'),
icon: 'bi-record',
enableIf: function (selecteds) {
return selecteds && selecteds.length > 0;
enableIf: (arg) => {
return arg.selected;
},
click: (selecteds) => {
if (selecteds && selecteds.length > 0) {
const selected = selecteds[0];
variableDialogRef.open(selected.id);
click: (arg) => {
if (arg.selected) {
variableDialogRef.open(arg.selected.id);
}
},
},
@ -336,34 +335,32 @@
[
{
label: $t('operation'),
icon: 'bi-cursor',
},
{
name: 'complete',
label: $t('lcdp.bpm.task.grid.toolbar.complete'),
enableIf: function (selecteds) {
return selecteds && selecteds.length > 0;
icon: 'bi-caret-right',
enableIf: function (arg) {
return arg.selected;
},
click: (selecteds) => {
if (selecteds && selecteds.length > 0) {
DialogManager.confirm($t('lcdp.bpm.task.grid.toolbar.complete.tip'), () => {
axios.post(Environment.apiContextPath('/api/flowable/process/operation/complete/') + selecteds[0].id).then(() => {
taskGridRef.refresh();
NotifyManager.success();
});
});
click: (arg) => {
if (arg.selected) {
completeTaskDialogRef.open(arg.selected.id);
}
},
},
{
name: 'claim',
label: $t('lcdp.bpm.task.grid.toolbar.claim'),
icon: 'bi-clipboard-check',
enableIf: function (selecteds) {
return selecteds && selecteds.length > 0 && !selecteds[0].assignee;
},
click: (selecteds) => {
if (selecteds && selecteds.length > 0) {
click: (arg) => {
if (arg.selected) {
DialogManager.confirm($t('lcdp.bpm.task.grid.toolbar.claim.tip'), () => {
axios.post(Environment.apiContextPath('/api/flowable/process/operation/claim/') + selecteds[0].id).then(() => {
axios.post(Environment.apiContextPath('/api/flowable/process/operation/claim/') + arg.selected.id).then(() => {
taskGridRef.refresh();
NotifyManager.success();
});
@ -374,13 +371,14 @@
{
name: 'unclaim',
label: $t('lcdp.bpm.task.grid.toolbar.unclaim'),
enableIf: function (selecteds) {
return selecteds && selecteds.length > 0 && selecteds[0].assignee;
icon: 'bi-clipboard-x',
enableIf: function (arg) {
return arg.selected && arg.selected.assignee;
},
click: (selecteds) => {
if (selecteds && selecteds.length > 0) {
click: (arg) => {
if (arg.selected) {
DialogManager.confirm($t('lcdp.bpm.task.grid.toolbar.unclaim.tip'), () => {
axios.post(Environment.apiContextPath('/api/flowable/process/operation/unClaim/') + selecteds[0].id).then(() => {
axios.post(Environment.apiContextPath('/api/flowable/process/operation/unClaim/') + arg.selected.id).then(() => {
taskGridRef.refresh();
NotifyManager.success();
});
@ -391,25 +389,27 @@
{
name: 'jump',
label: $t('lcdp.bpm.task.grid.toolbar.jump'),
enableIf: function (selecteds) {
return selecteds && selecteds.length > 0;
icon: 'bi-sign-turn-right',
enableIf: function (arg) {
return arg.selected;
},
click: (selecteds) => {
if (selecteds && selecteds.length > 0) {
jumpTaskDialogRef.open(selecteds[0].id);
click: (arg) => {
if (arg.selected) {
jumpTaskDialogRef.open(arg.selected.id);
}
},
},
{
name: 'terminate',
label: $t('lcdp.bpm.task.grid.toolbar.terminate'),
enableIf: function (selecteds) {
return selecteds && selecteds.length > 0;
icon: 'bi-stop',
enableIf: function (arg) {
return arg.selected;
},
click: (selecteds) => {
if (selecteds && selecteds.length > 0) {
click: (arg) => {
if (arg.selected) {
DialogManager.confirm($t('lcdp.bpm.task.grid.toolbar.terminate.tip'), () => {
axios.post(Environment.apiContextPath('/api/flowable/process/operation/terminateProcessInstance/') + selecteds[0].id).then(() => {
axios.post(Environment.apiContextPath('/api/flowable/process/operation/terminateProcessInstance/') + arg.selected.id).then(() => {
taskGridRef.refresh();
NotifyManager.success();
});
@ -422,13 +422,12 @@
name: 'variables',
label: $t('lcdp.bpm.task.grid.toolbar.variables'),
icon: 'bi-record',
enableIf: function (selecteds) {
return selecteds && selecteds.length > 0;
enableIf: function (arg) {
return arg.selected;
},
click: (selecteds) => {
if (selecteds && selecteds.length > 0) {
const selected = selecteds[0];
variableDialogRef.open(selected.processInstanceId);
click: (arg) => {
if (arg.selected) {
variableDialogRef.open(arg.selected.processInstanceId);
}
},
},
@ -464,6 +463,12 @@
}"
></w-grid>
</q-tab-panel>
<q-tab-panel name="tools">
<div class="flex 'justify-center' gap-4">
<q-btn :label="$t('lcdp.bpm.tools.action.cleanRuntimeData')" color="primary" no-caps @click="cleanRuntimeData"></q-btn>
<q-btn :label="$t('lcdp.bpm.tools.action.cleanHistoryData')" color="primary" no-caps @click="cleanHistoryData"></q-btn>
</div>
</q-tab-panel>
</q-tab-panels>
</template>
</q-splitter>
@ -473,30 +478,55 @@
</ProcessDesigner>
<CreateProcessInstanceDialog ref="createProcessInstanceDialogRef"></CreateProcessInstanceDialog>
<VariableDialog ref="variableDialogRef"></VariableDialog>
<CompleteTaskDialog ref="completeTaskDialogRef" @after-task-completed="afterTaskCompleted"></CompleteTaskDialog>
<JumpTaskDialog ref="jumpTaskDialogRef"></JumpTaskDialog>
</div>
</template>
<script setup lang="ts">
import { ref, nextTick } from 'vue';
import { useI18n } from 'vue-i18n';
import { Environment, DialogManager, NotifyManager, axios, EnumTools, DictionaryTools, Formater, Options } from 'platform-core';
import ProcessDesigner from './ProcessDesigner.vue';
import CreateProcessInstanceDialog from './CreateProcessInstanceDialog.vue';
import VariableDialog from './VariableDialog.vue';
import CompleteTaskDialog from './CompleteTaskDialog.vue';
import JumpTaskDialog from './JumpTaskDialog.vue';
const { t } = useI18n();
const selectedTabRef = ref('processDefine');
const processDefineGridRef = ref();
const processInstanceGridRef = ref();
const taskGridRef = ref();
const variableDialogRef = ref();
const completeTaskDialogRef = ref();
const jumpTaskDialogRef = ref();
const processDesignerDialogRef = ref();
const processDesignerIframeRef = ref();
const createProcessInstanceDialogRef = ref();
const afterTaskCompleted = () => {
NotifyManager.success();
taskGridRef.value.refresh();
};
const cleanRuntimeData = () => {
DialogManager.confirm(t('lcdp.bpm.tools.action.cleanRuntimeData.tip'), () => {
axios.post(Environment.apiContextPath('/api/flowable/tools/cleanRuntimeData')).then(() => {
NotifyManager.success();
});
});
};
const cleanHistoryData = () => {
DialogManager.confirm(t('lcdp.bpm.tools.action.cleanHistoryData.tip'), () => {
axios.post(Environment.apiContextPath('/api/flowable/tools/cleanHistoryData')).then(() => {
NotifyManager.success();
});
});
};
const ProcessStatusEnum = await EnumTools.fetch('io.sc.platform.flowable.enums.ProcessStatus');
const ProcessCategoryDictionaries = await DictionaryTools.fetch('WORK_FLOW');
</script>

68
io.sc.platform.lcdp.frontend/src/views/bpm/CompleteTaskDialog.vue

@ -0,0 +1,68 @@
<template>
<w-dialog ref="dialogRef" :title="$t('lcdp.bpm.completeTask.dialog.title')" width="800px" :can-maximize="false">
<template #buttons>
<WWorkflowAction
ref="workflowActionRef"
:task-id="taskIdRef"
:data="formModelValue"
:action-url="Environment.apiContextPath('/api/flowable/process/operation/complete')"
@after-submit="afterSubmit"
>
</WWorkflowAction>
</template>
<w-form
v-model="formModelValue"
:cols-num="1"
:fields="[
{ name: 'variables', label: $t('lcdp.bpm.processInstance.grid.entity.variables'), type: 'code-mirror', lang: 'json', rows: 5 },
{ name: 'transientVariables', label: $t('lcdp.bpm.processInstance.grid.entity.transientVariables'), type: 'code-mirror', lang: 'json', rows: 5 },
]"
>
</w-form>
</w-dialog>
</template>
<script setup lang="ts">
import { nextTick, ref, reactive } from 'vue';
import WWorkflowAction from './WWorkflowAction.vue';
import { Environment } from 'platform-core';
/**
* 定义组件支持的自定义事件
*/
const emit = defineEmits([
'afterTaskCompleted', //
]);
const dialogRef = ref();
const taskIdRef = ref<string>('');
const workflowActionRef = ref();
const formModelValue = reactive({
variables: undefined,
transientVariables: undefined,
});
const open = (taskId: string) => {
taskIdRef.value = taskId;
formModelValue.transientVariables = JSON.stringify({ task_treatment: '此处填写处理意见' }, null, 2);
formModelValue.variables = undefined;
dialogRef.value.show();
// nextTick(() => {
// workflowActionRef.value.setTaskId(taskId);
// workflowActionRef.value.setData(formModelValue);
// });
};
const close = () => {
dialogRef.value.hide();
};
const afterSubmit = () => {
close();
emit('afterTaskCompleted');
};
defineExpose({
open,
close,
});
</script>

51
io.sc.platform.lcdp.frontend/src/views/bpm/CreateProcessInstanceDialog.vue

@ -2,26 +2,14 @@
<w-dialog
ref="dialogRef"
:title="$t('lcdp.bpm.createProcessInstance.dialog.title') + ' - ' + processDefineNameRef"
width="500px"
height="450px"
width="700px"
:can-maximize="false"
:buttons="[
{
label: $t('submit'),
noCaps: true,
click: () => {
const data = formRef.getData();
axios
.post(Environment.apiContextPath('/api/flowable/process/operation/start/' + processDefineKeyRef), {
bussinessKey: data.bussinessKey,
variables: Tools.json2Object(data.variables),
transientVariables: Tools.json2Object(data.transientVariables),
autoCompleteFirstTask: data.autoCompleteFirstTask,
})
.then(() => {
NotifyManager.success();
close();
});
submit();
},
},
]"
@ -44,18 +32,53 @@
]"
class="p-2"
></w-form>
<SelectAssigneeDialog ref="selectAssigneeDialogRef" @assignee-selected="assigneeSelected"></SelectAssigneeDialog>
</w-dialog>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { axios, Environment, NotifyManager, Tools } from 'platform-core';
import SelectAssigneeDialog from './SelectAssigneeDialog.vue';
const dialogRef = ref();
const formRef = ref();
const selectAssigneeDialogRef = ref();
const processDefineKeyRef = ref('');
const processDefineNameRef = ref('');
const submit = (assignee) => {
const data = formRef.value.getData();
const bussinessKey = data.bussinessKey;
const variables = Tools.json2Object(data.variables) || {};
const transientVariables = Tools.json2Object(data.transientVariables) || {};
if (assignee) {
transientVariables.assignee = assignee;
}
const autoCompleteFirstTask = data.autoCompleteFirstTask;
axios
.post(Environment.apiContextPath('/api/flowable/process/operation/start/' + processDefineKeyRef.value), {
bussinessKey: bussinessKey,
variables: variables,
transientVariables: transientVariables,
autoCompleteFirstTask: autoCompleteFirstTask,
})
.then((response) => {
if (response.data.code === 0) {
//
NotifyManager.success();
close();
} else if (response.data.code === 1) {
//
selectAssigneeDialogRef.value.open(response.data);
}
});
};
const assigneeSelected = (assignee) => {
submit(assignee);
};
const open = (key: string, name: string) => {
processDefineKeyRef.value = key;
processDefineNameRef.value = name;

8
io.sc.platform.lcdp.frontend/src/views/bpm/JumpTaskDialog.vue

@ -3,7 +3,6 @@
ref="dialogRef"
:title="$t('lcdp.bpm.jumpTask.dialog.title')"
width="400px"
height="250px"
:can-maximize="false"
:buttons="[
{
@ -59,18 +58,17 @@ import { axios, Environment, NotifyManager, Tools } from 'platform-core';
const dialogRef = ref();
const formRef = ref();
const taskIdRef = ref();
const activityListRef = ref();
const activityListRef = ref([]);
const candidateListRef = ref<any[]>([]);
const open = (taskId: string) => {
if (taskId) {
taskIdRef.value = taskId;
axios.get(Environment.apiContextPath('/api/flowable/process/query/activity/' + taskId)).then((response) => {
const activityList: any[] = [];
activityListRef.value.splice(0, activityListRef.value.length);
for (const item of response.data) {
activityList.push({ label: item.name, value: item.id });
activityListRef.value.push({ value: item.id, label: item.name });
}
activityListRef.value = activityList;
});
}
dialogRef.value.show();

65
io.sc.platform.lcdp.frontend/src/views/bpm/SelectAssigneeDialog.vue

@ -0,0 +1,65 @@
<template>
<w-dialog
ref="dialogRef"
:title="$t('lcdp.bpm.selectAssignee.dialog.title')"
width="500px"
:can-maximize="false"
:buttons="[
{
name: 'confirm',
label: $t('confirm'),
click: () => {
emit('assigneeSelected', formRef.getFieldValue('assignee'));
close();
},
},
]"
>
<div
v-dompurify-html="$t('lcdp.bpm.selectAssignee.entity.assignee.tip', { activeName: selectAssigneeWrapperRef.activeName })"
class="text-body2 py-2"
></div>
<w-form
ref="formRef"
:cols-num="1"
:fields="[{ name: 'assignee', label: $t('lcdp.bpm.selectAssignee.entity.assignee'), type: 'select', options: assigneeOptionsRef }]"
>
</w-form>
</w-dialog>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue';
/**
* 定义组件支持的自定义事件
*/
const emit = defineEmits([
'assigneeSelected', //
]);
const dialogRef = ref();
const formRef = ref();
const assigneeOptionsRef = ref([]);
const selectAssigneeWrapperRef = ref();
const open = (selectAssigneeWrapper) => {
selectAssigneeWrapperRef.value = selectAssigneeWrapper;
assigneeOptionsRef.value.splice(0, assigneeOptionsRef.value.length);
const assignees = selectAssigneeWrapper?.assignees;
if (assignees) {
for (let i = 0; i < assignees.length; i++) {
assigneeOptionsRef.value.push({ value: assignees[i].loginName, label: assignees[i].loginName + '/' + assignees[i].userName });
}
}
dialogRef.value.show();
};
const close = () => {
dialogRef.value.hide();
};
defineExpose({
open,
close,
});
</script>

158
io.sc.platform.lcdp.frontend/src/views/bpm/WWorkflowAction.vue

@ -0,0 +1,158 @@
<template>
<div :class="`flex ${align === 'right' ? 'justify-end' : ''} gap-4`">
<q-btn v-for="action in actionsRef" :key="action.name" :label="action.title" color="primary" @click="buttonClick(action)"></q-btn>
</div>
<SelectAssigneeDialog ref="selectAssigneeDialogRef" @assignee-selected="assigneeSelected"></SelectAssigneeDialog>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { useI18n } from 'vue-i18n';
import { Environment, Tools, axios } from 'platform-core';
import SelectAssigneeDialog from './SelectAssigneeDialog.vue';
import { reactive } from 'vue';
/**
* 定义组件支持的自定义属性
*/
const props = defineProps({
// ID
taskId: { type: String, default: undefined },
//
data: {
type: Object,
default: () => {
return {};
},
},
//
align: { type: String, default: 'center' },
// url
actionUrl: { type: String, default: undefined },
// 退 url,,使
gobackActionUrl: { type: String, default: '/api/flowable/process/operation/getGobacks' },
//
defaultActionButtons: { type: [Array, Object], default: undefined },
//
defaultActionButtonsPlacement: { type: String, default: 'right' },
// goback
isGobackActionDropdown: { type: Boolean, default: false },
});
/**
* 定义组件支持的自定义事件
*/
const emit = defineEmits([
'afterSubmit', //
]);
const { t } = useI18n();
const actionsRef = ref([]);
const selectAssigneeDialogRef = ref();
const currentActionRef = ref(null);
const buildActions = (taskId: string) => {
axios.get(Environment.apiContextPath(props.gobackActionUrl + '/' + taskId)).then((data) => {
buildButtons(data.data);
});
};
const buildButtons = (gobacks) => {
const buttons = [];
// ()
if (props.defaultActionButtonsPlacement === 'left') {
if (props.defaultActionButtons && props.defaultActionButtons && props.defaultActionButtons.length > 0) {
for (let i = 0; i < props.defaultActionButtons.length; i++) {
buttons.push(props.defaultActionButtons[i]);
}
}
}
// 退
if (gobacks && gobacks.length > 0) {
for (let i = 0; i < gobacks.length; i++) {
const goback = gobacks[i];
const transientVariables = {};
transientVariables[goback.variableName] = goback.variableValue;
buttons.push({
title: goback.title || t('goback'),
transientVariables,
});
}
}
//
buttons.push({ title: t('submit') });
// ()
if (props.defaultActionButtonsPlacement === 'right') {
if (props.defaultActionButtons && props.defaultActionButtons && props.defaultActionButtons.length > 0) {
for (let i = 0; i < props.defaultActionButtons.length; i++) {
buttons.push(props.defaultActionButtons[i]);
}
}
}
//
actionsRef.value.splice(0, actionsRef.value.length);
for (let i = 0; i < buttons.length; i++) {
actionsRef.value.push(buttons[i]);
}
};
const buttonClick = (action) => {
currentActionRef.value = action;
const data = {
variables: {},
transientVariables: {},
};
if (!Tools.isUndefinedOrNull(props.data)) {
if (!Tools.isEmpty(props.data.variables)) {
data.variables = JSON.parse(props.data.variables);
}
if (!Tools.isEmpty(props.data.transientVariables)) {
data.transientVariables = JSON.parse(props.data.transientVariables);
}
}
if (!Tools.isUndefinedOrNull(action)) {
if (!Tools.isUndefinedOrNull(action.transientVariables)) {
Tools.mergeObject(data.transientVariables, action.transientVariables);
}
}
console.log(data);
// 退退
axios.post(props.actionUrl + '/' + props.taskId, data).then((response) => {
if (response.data.code === 0) {
//
emit('afterSubmit');
} else if (response.data.code === 1) {
//
selectAssigneeDialogRef.value.open(response.data);
}
});
};
const assigneeSelected = (assignee) => {
const data = currentActionRef.value;
if (props.data.variables) {
data.variables = JSON.parse(props.data.variables);
}
if (props.data.transientVariables) {
data.transientVariables = JSON.parse(props.data.transientVariables);
data.transientVariables.assignee = assignee;
}
axios.post(Environment.apiContextPath(props.actionUrl + props.taskId), data).then((response) => {
const rawData = response.data;
if (rawData.code === 0) {
//
emit('afterSubmit');
}
});
};
onMounted(() => {
if (props.taskId) {
buildActions(props.taskId);
}
});
</script>

2
io.sc.platform.lcdp/src/main/java/io/sc/platform/lcdp/configure/jpa/entity/ConfigureEntity.java

@ -54,7 +54,7 @@ public class ConfigureEntity extends CorporationAuditorEntity<ConfigureVo> {
@Override
public ConfigureVo toVo() {
ConfigureVo vo =new ConfigureVo();
CorporationAuditorEntity.toVo(vo,this);
super.toVo(vo);
vo.setId(this.getId());
vo.setName(this.getName());
vo.setActive(this.getActive());

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

@ -78,7 +78,7 @@
"luckyexcel": "1.0.1",
"mockjs": "1.1.0",
"pinia": "2.1.7",
"platform-core": "8.1.152",
"platform-core": "8.1.155",
"quasar": "2.14.5",
"tailwindcss": "3.4.0",
"vue": "3.4.3",

15
io.sc.platform.orm.api/src/main/java/io/sc/platform/orm/api/vo/AuditorVo.java

@ -1,6 +1,6 @@
package io.sc.platform.orm.api.vo;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.sc.platform.orm.api.enums.DataComeFrom;
import java.util.Date;
@ -8,38 +8,35 @@ import java.util.Date;
/**
* 审计实体 VO
*/
@JsonPropertyOrder({
"jpaVersion",
"dataComeFrom",
"creator",
"createDate",
"lastModifier",
"lastModifyDate"
})
public class AuditorVo extends VersionVo {
/**
* 数据来源
*/
@JsonProperty(index = 10002)
private DataComeFrom dataComeFrom;
/**
* 创建人(登录名)
*/
@JsonProperty(index = 10003)
protected String creator;
/**
* 创建日期
*/
@JsonProperty(index = 10004)
protected Date createDate;
/**
* 最后修改人(登录名)
*/
@JsonProperty(index = 10005)
protected String lastModifier;
/**
* 最后修改日期
*/
@JsonProperty(index = 10006)
protected Date lastModifyDate;
public DataComeFrom getDataComeFrom() {

12
io.sc.platform.orm.api/src/main/java/io/sc/platform/orm/api/vo/CorporationAuditorVo.java

@ -1,25 +1,17 @@
package io.sc.platform.orm.api.vo;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* 多法人审计实体 VO
*/
@JsonPropertyOrder({
"jpaVersion",
"dataComeFrom",
"creator",
"createDate",
"lastModifier",
"lastModifyDate",
"corporationCode"
})
public class CorporationAuditorVo extends AuditorVo {
public static final String DEFAULT_CODE ="_PRIMARY_";
/**
* 法人代码
*/
@JsonProperty(index = 10007)
protected String corporationCode;
public String getCorporationCode() {

9
io.sc.platform.orm.api/src/main/java/io/sc/platform/orm/api/vo/VersionVo.java

@ -1,16 +1,19 @@
package io.sc.platform.orm.api.vo;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* 版本化实体 VO
*/
public abstract class VersionVo extends BaseVo {
protected int jpaVersion;
@JsonProperty(index = 10001)
protected Integer jpaVersion;
public int getJpaVersion() {
public Integer getJpaVersion() {
return jpaVersion;
}
public void setJpaVersion(int jpaVersion) {
public void setJpaVersion(Integer jpaVersion) {
this.jpaVersion = jpaVersion;
}
}

7
io.sc.platform.orm/src/main/java/io/sc/platform/orm/entity/AuditorEntity.java

@ -26,26 +26,22 @@ public abstract class AuditorEntity<V extends AuditorVo> extends VersionEntity<V
// 数据来源
@Column(name="DATA_COME_FROM_",length=10)
@Enumerated(EnumType.STRING)
@JsonProperty(index = 1001)
private DataComeFrom dataComeFrom =DataComeFrom.INPUT;
//创建人(登录名)
@CreatedBy
@Column(name="CREATOR_", length=255, nullable=false)
@JsonProperty(index = 1002)
protected String creator;
//创建日期
@CreatedDate
@Column(name="CREATE_DATE_", nullable=false)
@Temporal(TemporalType.TIMESTAMP)
@JsonProperty(index = 1003)
protected Date createDate;
//最后修改人(登录名)
@LastModifiedBy
@Column(name="LAST_MODIFIER_", length=255, nullable=false)
@JsonProperty(index = 1004)
protected String lastModifier;
//最后修改日期
@ -55,7 +51,8 @@ public abstract class AuditorEntity<V extends AuditorVo> extends VersionEntity<V
@JsonProperty(index = 1005)
protected Date lastModifyDate;
public void toVo(V vo){
@Override
public void toVo(AuditorVo vo) {
if(vo!=null){
super.toVo(vo);
vo.setDataComeFrom(this.getDataComeFrom());

2
io.sc.platform.orm/src/main/java/io/sc/platform/orm/entity/BaseEntity.java

@ -14,7 +14,7 @@ public abstract class BaseEntity<V extends BaseVo> implements Serializable {
return toVo();
}
public static void toVo(BaseVo vo,BaseEntity entity){
public void toVo(V vo){
}
}

1
io.sc.platform.orm/src/main/java/io/sc/platform/orm/entity/CorporationAuditorEntity.java

@ -16,6 +16,7 @@ public abstract class CorporationAuditorEntity<V extends CorporationAuditorVo> e
@Column(name="CORP_CODE_",length=36)
protected String corporationCode ="_PRIMARY_";
@Override
public void toVo(CorporationAuditorVo vo){
if(vo!=null){
super.toVo(vo);

9
io.sc.platform.orm/src/main/java/io/sc/platform/orm/entity/VersionEntity.java

@ -7,27 +7,30 @@ import io.sc.platform.orm.api.vo.BaseVo;
import io.sc.platform.orm.api.vo.VersionVo;
import javax.persistence.Column;
import javax.persistence.MappedSuperclass;
import javax.persistence.Version;
/**
* 版本化实体类,可实现乐观锁
*/
@MappedSuperclass
public abstract class VersionEntity<V extends VersionVo> extends BaseEntity<V>{
@Version
@Column(name="JPA_VERSION_")
protected int jpaVersion;
protected Integer jpaVersion;
@Override
public void toVo(VersionVo vo){
if(vo!=null){
vo.setJpaVersion(this.getJpaVersion());
}
}
public int getJpaVersion() {
public Integer getJpaVersion() {
return jpaVersion;
}
public void setJpaVersion(int jpaVersion) {
public void setJpaVersion(Integer jpaVersion) {
this.jpaVersion = jpaVersion;
}
}

2
io.sc.platform.orm/src/main/java/io/sc/platform/orm/service/impl/DaoServiceImpl.java

@ -166,9 +166,9 @@ public abstract class DaoServiceImpl<E,ID extends Serializable,R extends DaoRepo
if(!willBeRemoved.isEmpty()){
collection.removeAll(willBeRemoved);
}
oldEntityWrapper.setPropertyValue(entry.getKey(), newValue);
}
}
oldEntityWrapper.setPropertyValue(entry.getKey(), newValue);
}
}
}

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

@ -99,6 +99,6 @@
"vue-dompurify-html": "5.0.1",
"vue-i18n": "9.8.0",
"vue-router": "4.2.5",
"platform-core": "8.1.152"
"platform-core": "8.1.155"
}
}

8
io.sc.platform.system.api/src/main/java/io/sc/platform/system/api/menu/MenuGroupVo.java

@ -4,5 +4,13 @@ import java.util.ArrayList;
import java.util.List;
public class MenuGroupVo extends MenuVo {
private boolean asRoot;
public boolean getAsRoot() {
return asRoot;
}
public void setAsRoot(boolean asRoot) {
this.asRoot = asRoot;
}
}

30
io.sc.platform.system.api/src/main/java/io/sc/platform/system/api/user/UserVo.java

@ -1,37 +1,67 @@
package io.sc.platform.system.api.user;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.sc.platform.orm.api.vo.CorporationAuditorVo;
public class UserVo extends CorporationAuditorVo {
//主键
@JsonProperty(index = 1)
private String id;
//登录名
@JsonProperty(index = 2)
private String loginName;
//用户名
@JsonProperty(index = 3)
private String userName;
//描述
@JsonProperty(index = 4)
private String description;
//是否可用
@JsonProperty(index = 5)
private Boolean enable;
//账户是否过期
@JsonProperty(index = 6)
private Boolean accountExpired;
//账户是否被锁定
@JsonProperty(index = 7)
private Boolean accountLocked;
//密码是否过期
@JsonProperty(index = 8)
private Boolean credentialsExpired;
//默认所属角色ID
@JsonProperty(index = 9)
private String defaultRoleId;
//默认所属机构ID
@JsonProperty(index = 10)
private String defaultOrgId;
//email
@JsonProperty(index = 11)
private String email;
//电话
@JsonProperty(index = 12)
private String phone;
//手机号
@JsonProperty(index = 13)
private String mobile;
//微信号
@JsonProperty(index = 14)
private String weixin;
//QQ号
@JsonProperty(index = 15)
private String qq;
public String getId() {

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

@ -78,7 +78,7 @@
"luckyexcel": "1.0.1",
"mockjs": "1.1.0",
"pinia": "2.1.7",
"platform-core": "8.1.152",
"platform-core": "8.1.155",
"quasar": "2.14.5",
"tailwindcss": "3.4.0",
"vue": "3.4.3",

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

@ -78,6 +78,7 @@
"system.menu.grid.toolbar.addTop":"Add Top Menu",
"system.menu.grid.toolbar.addChild":"Add Child Menu",
"system.menu.grid.entity.titleI18nKey":"Title i18n Key",
"system.menu.grid.entity.asRoot":"Is show as root",
"system.menu.grid.entity.javaScript":"JavaScript",
"system.menu.grid.entity.routeName":"Route Name",
"system.menu.grid.entity.routeQuery":"Route Query",

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

@ -78,6 +78,7 @@
"system.menu.grid.toolbar.addTop":"新增頂級菜單",
"system.menu.grid.toolbar.addChild":"新增子菜單",
"system.menu.grid.entity.titleI18nKey":"標題多語言消息鍵",
"system.menu.grid.entity.asRoot":"是否作為根菜單顯示",
"system.menu.grid.entity.javaScript":"JavaScript",
"system.menu.grid.entity.routeName":"路由名稱",
"system.menu.grid.entity.routeQuery":"路由查詢參數",

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

@ -78,6 +78,7 @@
"system.menu.grid.toolbar.addTop":"新增顶级菜单",
"system.menu.grid.toolbar.addChild":"新增子菜单",
"system.menu.grid.entity.titleI18nKey":"标题多语言消息键",
"system.menu.grid.entity.asRoot":"是否作为根菜单显示",
"system.menu.grid.entity.javaScript":"JavaScript",
"system.menu.grid.entity.routeName":"路由名称",
"system.menu.grid.entity.routeQuery":"路由查询参数",

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

@ -83,41 +83,50 @@
form: {
colsNum: 1,
fields: [
{ name: 'parent', label: $t('parent'), type: 'text' },
{ name: 'type', label: $t('type'), type: 'select', required: true, options: Options.enum(MenuTypeEnum, false), defaultValue: 'GROUP' },
{ name: 'titleI18nKey', label: $t('system.menu.grid.entity.titleI18nKey'), type: 'text', required: true },
{ name: 'icon', label: $t('icon'), type: 'text' },
{ name: 'enable', label: $t('enable'), type: 'checkbox', defaultValue: true },
{ name: 'order', label: $t('order'), type: 'text' },
{ name: 'order', label: $t('order'), type: 'number' },
{
name: 'asRoot',
label: $t('system.menu.grid.entity.asRoot'),
type: 'checkbox',
showIf: (arg) => {
return arg.form.getFieldValue('type') === 'GROUP' && Tools.isUndefinedOrNull(arg.form.getFieldValue('parent'));
},
},
{
name: 'javaScript',
label: $t('system.menu.grid.entity.javaScript'),
type: 'textarea',
showIf: (form) => {
return form.getFieldValue('type') === 'JAVASCRIPT';
showIf: (arg) => {
return arg.form.getFieldValue('type') === 'JAVASCRIPT';
},
},
{
name: 'routeName',
label: $t('system.menu.grid.entity.routeName'),
type: 'text',
showIf: (form) => {
return form.getFieldValue('type') === 'ROUTE';
showIf: (arg) => {
return arg.form.getFieldValue('type') === 'ROUTE';
},
},
{
name: 'routeQuery',
label: $t('system.menu.grid.entity.routeQuery'),
type: 'text',
showIf: (form) => {
return form.getFieldValue('type') === 'ROUTE';
showIf: (arg) => {
return arg.form.getFieldValue('type') === 'ROUTE';
},
},
{
name: 'url',
label: $t('url'),
type: 'text',
showIf: (form) => {
return form.getFieldValue('type') === 'URL';
showIf: (arg) => {
return arg.form.getFieldValue('type') === 'URL';
},
},
{
@ -126,8 +135,8 @@
type: 'select',
options: Options.enum(UrlOpenTypeEnum, false),
defaultValue: 'NEW_WINDOW',
showIf: (form) => {
return form.getFieldValue('type') === 'URL';
showIf: (arg) => {
return arg.form.getFieldValue('type') === 'URL';
},
},
],
@ -215,7 +224,7 @@
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { Environment, axios, EnumTools, Formater, Options } from 'platform-core';
import { Environment, axios, Tools, EnumTools, Formater, Options } from 'platform-core';
import SelectRoleGrid from '../shared/SelectRoleGrid.vue';
import SelectOrgTreeGrid from '../shared/SelectOrgTreeGrid.vue';
import ImportMenuPluginDialog from './ImportMenuPluginDialog.vue';

15
io.sc.platform.system/src/main/java/io/sc/platform/system/menu/jpa/entity/MenuGroupEntity.java

@ -1,5 +1,6 @@
package io.sc.platform.system.menu.jpa.entity;
import io.sc.platform.orm.converter.NumericBooleanConverter;
import io.sc.platform.system.api.menu.MenuGroupVo;
import io.sc.platform.system.api.menu.MenuVo;
@ -13,6 +14,11 @@ import java.util.List;
@Entity
@DiscriminatorValue("GROUP")
public class MenuGroupEntity extends MenuEntity {
// 是否作为根菜单,如果是根菜单,那此菜单将不显示,并将其子作为顶级菜单显示
@Column(name="AS_ROOT_")
@Convert(converter= NumericBooleanConverter.class)
protected boolean asRoot;
//直接孩子
@OneToMany(mappedBy="parent",cascade= {CascadeType.PERSIST})
@OrderBy("order")
@ -27,9 +33,18 @@ public class MenuGroupEntity extends MenuEntity {
public MenuVo toVo() {
MenuGroupVo vo =new MenuGroupVo();
super.toVo(vo);
vo.setAsRoot(this.getAsRoot());
return vo;
}
public Boolean getAsRoot() {
return asRoot;
}
public void setAsRoot(Boolean asRoot) {
this.asRoot = asRoot;
}
public List<MenuEntity> getChildren() {
return children;
}

1
io.sc.platform.system/src/main/java/io/sc/platform/system/menu/mapper/MenuEntityMapper.java

@ -32,6 +32,7 @@ public class MenuEntityMapper implements RowMapper<MenuEntity> {
menuEntity =entity;
}else if(MenuType.GROUP.equals(type)){
MenuGroupEntity entity =new MenuGroupEntity();
entity.setAsRoot(rs.getBoolean("AS_ROOT_"));
menuEntity =entity;
}else if(MenuType.JAVASCRIPT.equals(type)){
MenuJavascriptEntity entity =new MenuJavascriptEntity();

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

@ -85,6 +85,7 @@ public class MenuServiceImpl extends DaoServiceImpl<MenuEntity, String, MenuRepo
}else{
menus =roleMenus;
}
return menus;
}
@ -166,7 +167,7 @@ public class MenuServiceImpl extends DaoServiceImpl<MenuEntity, String, MenuRepo
@Transactional
public void addRoles(CascadeMany2Many<String,String> wrapper) throws Exception {
if(wrapper==null){return;}
if(wrapper.getTargets()!=null && wrapper.getTargets().size()>0){
if(wrapper.getTargets()!=null && !wrapper.getTargets().isEmpty()){
Set<String> sourceIds =new HashSet<>();
sourceIds.add(wrapper.getSource());
sourceIds.addAll(wrapper.getSourceParents());

33
io.sc.platform.system/src/main/java/io/sc/platform/system/user/service/impl/UserServiceImpl.java

@ -11,10 +11,13 @@ import io.sc.platform.orm.service.support.QueryResult;
import io.sc.platform.orm.util.EntityVoUtil;
import io.sc.platform.security.SecurityProperties;
import io.sc.platform.security.util.SecurityUtil;
import io.sc.platform.system.api.menu.MenuGroupVo;
import io.sc.platform.system.api.menu.MenuRouteVo;
import io.sc.platform.system.api.menu.MenuVo;
import io.sc.platform.system.department.jpa.entity.DepartmentEntity;
import io.sc.platform.system.i18n.service.I18nService;
import io.sc.platform.system.menu.jpa.entity.MenuEntity;
import io.sc.platform.system.menu.jpa.entity.MenuGroupEntity;
import io.sc.platform.system.menu.service.MenuService;
import io.sc.platform.system.org.jpa.entity.OrgEntity;
import io.sc.platform.system.org.service.OrgService;
@ -42,10 +45,7 @@ import javax.transaction.Transactional;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.*;
@Service
public class UserServiceImpl extends DaoServiceImpl<UserEntity, String, UserRepository> implements UserService {
@ -62,9 +62,32 @@ public class UserServiceImpl extends DaoServiceImpl<UserEntity, String, UserRepo
@Override
public UserSession getUserSession(HttpServletRequest request) throws Exception {
List<MenuVo> menus = EntityVoUtil.toVo(menuService.getMenusByUser(request.getUserPrincipal().getName()));
//处理菜单组的 asRoot 属性
// 处理菜单组为 asRoot
List<MenuVo> asRootMenus =new ArrayList<>();
Map<String,MenuGroupVo> asRootMenuGroupMap =new HashMap<>();
if(menus!=null && !menus.isEmpty()){
for(MenuVo menu : menus){
if(menu instanceof MenuGroupVo){
MenuGroupVo menuGroup =(MenuGroupVo)menu;
if(menuGroup.getAsRoot()){
asRootMenuGroupMap.put(menuGroup.getId(),menuGroup);
}else{
asRootMenus.add(menu);
}
}else{
if(StringUtils.hasText(menu.getParent()) && asRootMenuGroupMap.containsKey(menu.getParent())) {
menu.setParent(null);
}
asRootMenus.add(menu);
}
}
}
UserSession result =new UserSession();
result.setUser(SecurityUtil.securityUser2map());
result.setMenus(menus);
result.setMenus(asRootMenus);
result.setRoutes(getFrontEndRoutes(menus));
result.setRemoteEntries(frontEndService.getRemoteEntries(request));
return result;

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

@ -247,6 +247,7 @@
<column name="ENABLE_" type="SMALLINT" remarks="是否可用(0:不可用,1:可用)"></column>
<column name="ORDER_" type="INTEGER" remarks="顺序"></column>
<column name="AUTHORIZE_EXP_" type="NVARCHAR(1024)" remarks="授权表达式"></column>
<column name="AS_ROOT_" type="SMALLINT" remarks="是否作为根菜单"></column>
<column name="PARENT_ID_" type="NVARCHAR(36)" remarks="父资源ID"></column>
<!-- javascript 菜单项 -->

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

@ -78,7 +78,7 @@
"luckyexcel": "1.0.1",
"mockjs": "1.1.0",
"pinia": "2.1.7",
"platform-core": "8.1.152",
"platform-core": "8.1.155",
"quasar": "2.14.5",
"tailwindcss": "3.4.0",
"vue": "3.4.3",

Loading…
Cancel
Save