Browse Source

基础框架发布: 8.2.28

1) 修复 jpa 中的一些 bug
  2) 完成我的工作台, 首页, 通知等功能

前端核心发布: 8.2.100
 1) 工作台界面调整
main
wangshaoping 2 months ago
parent
commit
0a922a3d47
  1. 2
      cips.frontend/package.json
  2. 1
      cips.frontend/webpack.config.common.cjs
  3. 2
      erm.frontend/package.json
  4. 1
      erm.frontend/webpack.config.common.cjs
  5. 4
      gradle.properties
  6. 2
      io.sc.engine.mv.frontend/package.json
  7. 1
      io.sc.engine.mv.frontend/webpack.config.common.cjs
  8. 2
      io.sc.engine.rule.client.spring/src/main/resources/META-INF/platform/plugins/parameters.json
  9. 32
      io.sc.engine.rule.client/src/main/resources/META-INF/platform/plugins/exportable-resources.json
  10. 2
      io.sc.engine.rule.frontend/package.json
  11. 2
      io.sc.engine.rule.frontend/src/components/index.ts
  12. 1
      io.sc.engine.rule.frontend/src/i18n/messages.json
  13. 3
      io.sc.engine.rule.frontend/src/i18n/messages_tw_CN.json
  14. 3
      io.sc.engine.rule.frontend/src/i18n/messages_zh_CN.json
  15. 4
      io.sc.engine.rule.frontend/src/remote-components/remote-components.json
  16. 6
      io.sc.engine.rule.frontend/src/routes/routes.json
  17. 16
      io.sc.engine.rule.frontend/src/views/workflow/Workflow.vue
  18. 169
      io.sc.engine.rule.frontend/src/views/workflow/WorkflowApprovalComponent.vue
  19. 1
      io.sc.engine.rule.frontend/webpack.config.common.cjs
  20. 2
      io.sc.engine.rule.server/src/main/resources/META-INF/platform/plugins/parameters.json
  21. 2
      io.sc.engine.st.frontend/package.json
  22. 1
      io.sc.engine.st.frontend/webpack.config.common.cjs
  23. 2
      io.sc.platform.ai.frontend/package.json
  24. 1
      io.sc.platform.ai.frontend/webpack.config.common.cjs
  25. 2
      io.sc.platform.core.frontend/package.json
  26. 33
      io.sc.platform.core.frontend/src/platform/i18n/messages.json
  27. 33
      io.sc.platform.core.frontend/src/platform/i18n/messages_tw_CN.json
  28. 33
      io.sc.platform.core.frontend/src/platform/i18n/messages_zh_CN.json
  29. 14
      io.sc.platform.core.frontend/src/platform/layout/sub-layout/ChangeRoleDialog.vue
  30. 168
      io.sc.platform.core.frontend/src/platform/layout/sub-layout/Topper.vue
  31. 1
      io.sc.platform.core.frontend/src/platform/plugin/environment/index.ts
  32. 3
      io.sc.platform.core.frontend/src/platform/types/ConfigureType.ts
  33. 39
      io.sc.platform.core.frontend/src/platform/views/Home.vue
  34. 76
      io.sc.platform.core.frontend/src/platform/views/home/Announcement.vue
  35. 16
      io.sc.platform.core.frontend/src/platform/views/home/AnnouncementDialog.vue
  36. 93
      io.sc.platform.core.frontend/src/platform/views/home/DoneTask.vue
  37. 93
      io.sc.platform.core.frontend/src/platform/views/home/FinishedTask.vue
  38. 88
      io.sc.platform.core.frontend/src/platform/views/home/Message.vue
  39. 86
      io.sc.platform.core.frontend/src/platform/views/home/MyAnnouncement.vue
  40. 125
      io.sc.platform.core.frontend/src/platform/views/home/MyDoneTask.vue
  41. 125
      io.sc.platform.core.frontend/src/platform/views/home/MyFinishedTask.vue
  42. 107
      io.sc.platform.core.frontend/src/platform/views/home/MyMessage.vue
  43. 64
      io.sc.platform.core.frontend/src/platform/views/home/MyShortcutMenu.vue
  44. 128
      io.sc.platform.core.frontend/src/platform/views/home/MyTask.vue
  45. 40
      io.sc.platform.core.frontend/src/platform/views/home/ShortcutMenu.vue
  46. 168
      io.sc.platform.core.frontend/src/platform/views/home/ShortcutMenuSettingDialog.vue
  47. 45
      io.sc.platform.core.frontend/src/platform/views/home/SystemMessageDialog.vue
  48. 94
      io.sc.platform.core.frontend/src/platform/views/home/Task.vue
  49. 12
      io.sc.platform.core.frontend/src/platform/views/home/UserMessageDialog.vue
  50. 4
      io.sc.platform.core.frontend/template-project/package.json
  51. 29
      io.sc.platform.core.frontend/template-project/src/views/likm/Form.vue
  52. 27
      io.sc.platform.core.frontend/template-project/src/views/likm/Grid.vue
  53. 1
      io.sc.platform.core.frontend/template-project/webpack.config.common.cjs
  54. 1
      io.sc.platform.core.frontend/webpack.config.common.cjs
  55. 1
      io.sc.platform.core/src/main/resources/META-INF/platform/plugins/application-properties.json
  56. 2
      io.sc.platform.developer.doc/package.json
  57. 2
      io.sc.platform.developer.frontend/package.json
  58. 1
      io.sc.platform.developer.frontend/webpack.config.common.cjs
  59. 1
      io.sc.platform.flowable/build.gradle
  60. 9
      io.sc.platform.flowable/src/main/java/io/sc/platform/flowable/api/ProcessVo.java
  61. 15
      io.sc.platform.flowable/src/main/java/io/sc/platform/flowable/controller/ProcessQueryWebController.java
  62. 24
      io.sc.platform.flowable/src/main/java/io/sc/platform/flowable/jpa/entity/ProcessEntity.java
  63. 2
      io.sc.platform.flowable/src/main/java/io/sc/platform/flowable/service/impl/AssigneeQueryServiceImpl.java
  64. 1
      io.sc.platform.flowable/src/main/java/io/sc/platform/flowable/service/impl/ProcessOperationServiceImpl.java
  65. 374
      io.sc.platform.flowable/src/main/java/io/sc/platform/flowable/service/impl/ProcessQueryServiceImpl.java
  66. 9
      io.sc.platform.flowable/src/main/java/io/sc/platform/flowable/support/BusinessKeyAndDescription.java
  67. 12
      io.sc.platform.flowable/src/main/java/io/sc/platform/flowable/support/ProcessTaskWrapper.java
  68. 41
      io.sc.platform.flowable/src/main/java/io/sc/platform/flowable/support/ProcessTaskWrapperMapper.java
  69. 1
      io.sc.platform.flowable/src/main/resources/liquibase/io.sc.platform.flowable_8.0.0_20220606__Process_Manager_Database_Schema_DDL.xml
  70. 2
      io.sc.platform.lcdp.frontend/package.json
  71. 8
      io.sc.platform.lcdp.frontend/src/i18n/messages.json
  72. 8
      io.sc.platform.lcdp.frontend/src/i18n/messages_tw_CN.json
  73. 8
      io.sc.platform.lcdp.frontend/src/i18n/messages_zh_CN.json
  74. 16
      io.sc.platform.lcdp.frontend/src/views/Theme.vue
  75. 84
      io.sc.platform.lcdp.frontend/src/views/bpm/Bpm.vue
  76. 61
      io.sc.platform.lcdp.frontend/src/views/theme/Home.vue
  77. 1
      io.sc.platform.lcdp.frontend/webpack.config.common.cjs
  78. 49
      io.sc.platform.lcdp/src/main/java/io/sc/platform/lcdp/configure/api/Home.java
  79. 19
      io.sc.platform.lcdp/src/main/java/io/sc/platform/lcdp/configure/api/Setting.java
  80. 9
      io.sc.platform.lcdp/src/main/java/io/sc/platform/lcdp/configure/api/Theme.java
  81. 40
      io.sc.platform.lcdp/src/main/java/io/sc/platform/lcdp/configure/service/impl/ConfigureServiceImpl.java
  82. 2
      io.sc.platform.license.keygen.frontend/package.json
  83. 1
      io.sc.platform.license.keygen.frontend/webpack.config.common.cjs
  84. 2
      io.sc.platform.mvc.frontend/package.json
  85. 1
      io.sc.platform.mvc.frontend/webpack.config.common.cjs
  86. 78
      io.sc.platform.mvc/src/main/java/io/sc/platform/mvc/controller/FrontendController.java
  87. 9
      io.sc.platform.mvc/src/main/java/io/sc/platform/mvc/plugins/item/FrontEndRoute.java
  88. 16
      io.sc.platform.mvc/src/main/java/io/sc/platform/mvc/service/impl/FrontEndServiceImpl.java
  89. 8
      io.sc.platform.mvc/src/main/resources/META-INF/platform/plugins/parameters.json
  90. 26
      io.sc.platform.orm/src/main/java/io/sc/platform/orm/service/support/criteria/impl/Between.java
  91. 29
      io.sc.platform.orm/src/main/java/io/sc/platform/orm/service/support/criteria/impl/BetweenInclusive.java
  92. 8
      io.sc.platform.orm/src/main/java/io/sc/platform/orm/service/support/criteria/impl/EndWith.java
  93. 8
      io.sc.platform.orm/src/main/java/io/sc/platform/orm/service/support/criteria/impl/Not.java
  94. 8
      io.sc.platform.orm/src/main/java/io/sc/platform/orm/service/support/criteria/impl/NotContains.java
  95. 8
      io.sc.platform.orm/src/main/java/io/sc/platform/orm/service/support/criteria/impl/NotEndWith.java
  96. 8
      io.sc.platform.orm/src/main/java/io/sc/platform/orm/service/support/criteria/impl/NotEquals.java
  97. 8
      io.sc.platform.orm/src/main/java/io/sc/platform/orm/service/support/criteria/impl/NotInSet.java
  98. 8
      io.sc.platform.orm/src/main/java/io/sc/platform/orm/service/support/criteria/impl/NotStartWith.java
  99. 8
      io.sc.platform.orm/src/main/java/io/sc/platform/orm/service/support/criteria/impl/Or.java
  100. 8
      io.sc.platform.orm/src/main/java/io/sc/platform/orm/service/support/criteria/impl/StartWith.java

2
cips.frontend/package.json

@ -112,7 +112,7 @@
"node-sql-parser": "5.3.6",
"pinia": "2.3.0",
"pinia-undo": "0.2.4",
"platform-core": "8.2.92",
"platform-core": "8.2.100",
"quasar": "2.17.6",
"sort-array": "5.0.0",
"svg-path-commander": "2.1.7",

1
cips.frontend/webpack.config.common.cjs

@ -89,6 +89,7 @@ module.exports = {
use: [
{
loader: 'vue-loader',
options: { hotReload: false },
},
],
},

2
erm.frontend/package.json

@ -112,7 +112,7 @@
"node-sql-parser": "5.3.6",
"pinia": "2.3.0",
"pinia-undo": "0.2.4",
"platform-core": "8.2.92",
"platform-core": "8.2.100",
"quasar": "2.17.6",
"sort-array": "5.0.0",
"svg-path-commander": "2.1.7",

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

@ -89,6 +89,7 @@ module.exports = {
use: [
{
loader: 'vue-loader',
options: { hotReload: false },
},
],
},

4
gradle.properties

@ -37,9 +37,9 @@ application_version=1.0.0
# platform
###########################################################
platform_group=io.sc
platform_version=8.2.27
platform_version=8.2.28
platform_plugin_version=8.2.10
platform_core_frontend_version=8.2.92
platform_core_frontend_version=8.2.100
###########################################################
# dependencies version

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

@ -112,7 +112,7 @@
"node-sql-parser": "5.3.6",
"pinia": "2.3.0",
"pinia-undo": "0.2.4",
"platform-core": "8.2.92",
"platform-core": "8.2.100",
"quasar": "2.17.6",
"sort-array": "5.0.0",
"svg-path-commander": "2.1.7",

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

@ -89,6 +89,7 @@ module.exports = {
use: [
{
loader: 'vue-loader',
options: { hotReload: false },
},
],
},

2
io.sc.engine.rule.client.spring/src/main/resources/META-INF/platform/plugins/parameters.json

@ -17,7 +17,7 @@
"id" :"parameter.re.client.generatorType",
"parentId" :"parameter.re.client",
"code" :"parameter.re.client.generatorType",
"defaultValue" :"Groovy",
"defaultValue" :"Java",
"order" : 100,
"options" :{
"Groovy" : "parameter.re.client.generatorType.Groovy",

32
io.sc.engine.rule.client/src/main/resources/META-INF/platform/plugins/exportable-resources.json

@ -29,31 +29,31 @@
},
{
"type" :"file",
"name" :"io.sc.engine.rule.core-8.2.24.jar",
"description" :"io.sc.engine.rule.core-8.2.24.jar",
"sources" :["classpath:/io/sc/engine/rule/client/jars/io.sc.engine.rule.core-8.2.24.jar"],
"target" :"${dir.engine.rule.classpath}/io.sc.engine.rule.core-8.2.24.jar"
"name" :"io.sc.engine.rule.core-8.2.27.jar",
"description" :"io.sc.engine.rule.core-8.2.27.jar",
"sources" :["classpath:/io/sc/engine/rule/client/jars/io.sc.engine.rule.core-8.2.27.jar"],
"target" :"${dir.engine.rule.classpath}/io.sc.engine.rule.core-8.2.27.jar"
},
{
"type" :"file",
"name" :"io.sc.platform.util-8.2.24.jar",
"description" :"io.sc.platform.util-8.2.24.jar",
"sources" :["classpath:/io/sc/engine/rule/client/jars/io.sc.platform.util-8.2.24.jar"],
"target" :"${dir.engine.rule.classpath}/io.sc.platform.util-8.2.24.jar"
"name" :"io.sc.platform.util-8.2.27.jar",
"description" :"io.sc.platform.util-8.2.27.jar",
"sources" :["classpath:/io/sc/engine/rule/client/jars/io.sc.platform.util-8.2.27.jar"],
"target" :"${dir.engine.rule.classpath}/io.sc.platform.util-8.2.27.jar"
},
{
"type" :"file",
"name" :"io.sc.creditreport.core-8.2.24.jar",
"description" :"io.sc.creditreport.core-8.2.24.jar",
"sources" :["classpath:/io/sc/engine/rule/client/jars/io.sc.creditreport.core-8.2.24.jar"],
"target" :"${dir.engine.rule.classpath}/io.sc.creditreport.core-8.2.24.jar"
"name" :"io.sc.creditreport.core-8.2.27.jar",
"description" :"io.sc.creditreport.core-8.2.27.jar",
"sources" :["classpath:/io/sc/engine/rule/client/jars/io.sc.creditreport.core-8.2.27.jar"],
"target" :"${dir.engine.rule.classpath}/io.sc.creditreport.core-8.2.27.jar"
},
{
"type" :"file",
"name" :"io.sc.engine.rule.client.spring-8.2.24.jar",
"description" :"io.sc.engine.rule.client.spring-8.2.24.jar",
"sources" :["classpath:/io/sc/engine/rule/client/jars/io.sc.engine.rule.client.spring-8.2.24.jar"],
"target" :"${dir.engine.rule.classpath}/io.sc.engine.rule.client.spring-8.2.24.jar"
"name" :"io.sc.engine.rule.client.spring-8.2.27.jar",
"description" :"io.sc.engine.rule.client.spring-8.2.27.jar",
"sources" :["classpath:/io/sc/engine/rule/client/jars/io.sc.engine.rule.client.spring-8.2.27.jar"],
"target" :"${dir.engine.rule.classpath}/io.sc.engine.rule.client.spring-8.2.27.jar"
},
{

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

@ -112,7 +112,7 @@
"node-sql-parser": "5.3.6",
"pinia": "2.3.0",
"pinia-undo": "0.2.4",
"platform-core": "8.2.92",
"platform-core": "8.2.100",
"quasar": "2.17.6",
"sort-array": "5.0.0",
"svg-path-commander": "2.1.7",

2
io.sc.engine.rule.frontend/src/components/index.ts

@ -2,6 +2,7 @@
* ,
*/
import io_sc_engine_rule_frontend_workflow_ApprovalComponent from '@/views/workflow/WorkflowApprovalComponent.vue';
import component_engine_rule_resources from '@/views/resources/Resources.vue';
import component_engine_rule_designer from '@/views/resources/designer/Designer.vue';
import component_engine_rule_authorization from '@/views/authorization/Authorization.vue';
@ -13,6 +14,7 @@ import component_engine_rule_testcase from '@/views/testcase/Testcase.vue';
import component_engine_rule_migration from '@/views/migration/Migration.vue';
const localComponents = {
'io.sc.engine.rule.frontend.workflow.ApprovalComponent': io_sc_engine_rule_frontend_workflow_ApprovalComponent,
'component.engine.rule.resources': component_engine_rule_resources,
'component.engine.rule.designer': component_engine_rule_designer,
'component.engine.rule.authorization': component_engine_rule_authorization,

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

@ -312,6 +312,7 @@
"re.workflow.dialog.title": "Workflow Approving",
"re.workflow.dialog.tip": "Tip: workflow approving needed, the resource will be active after approved!",
"re.workflow.dialog.historyTask": "History Tasks",
"re.workflow.dialog.entity.treatment": "Treatment",
"re.workflow.task.grid.title": "Task List",

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

@ -315,7 +315,8 @@
"re.workflow.dialog.title": "審批流程",
"re.workflow.dialog.tip": "提示: 該資源發佈需要流程審批, 待審批通過後方能生效!",
"re.workflow.dialog.entity.treatment": "說明",
"re.workflow.dialog.historyTask": "審批歷史",
"re.workflow.dialog.entity.treatment": "處理意見",
"re.workflow.task.grid.title": "任務列表",
"re.workflow.task.grid.toolbar.viewResource": "查看資源",

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

@ -318,7 +318,8 @@
"re.workflow.dialog.title": "审批流程",
"re.workflow.dialog.tip": "提示: 该资源发布需要流程审批, 待审批通过后方能生效!",
"re.workflow.dialog.entity.treatment": "说明",
"re.workflow.dialog.historyTask": "审批历史",
"re.workflow.dialog.entity.treatment": "处理意见",
"re.workflow.task.grid.title": "任务列表",
"re.workflow.task.grid.toolbar.viewResource": "查看资源",

4
io.sc.engine.rule.frontend/src/remote-components/remote-components.json

@ -5,8 +5,4 @@
"componentPath": "组件 .vue 文件路径"
}
*/
{
"component": "io.sc.engine.rule.frontend.workflow.ApprovalComponent",
"componentPath": "@/views/workflow/WorkflowApprovalComponent.vue"
}
]

6
io.sc.engine.rule.frontend/src/routes/routes.json

@ -1,4 +1,10 @@
[
{
"remoteComponent": true,
"module": "io.sc.engine.rule.frontend",
"component": "io.sc.engine.rule.frontend.workflow.ApprovalComponent",
"componentPath": "@/views/workflow/WorkflowApprovalComponent.vue"
},
{
"name": "route.engine.rule.resources",
"path": "re/resources",

16
io.sc.engine.rule.frontend/src/views/workflow/Workflow.vue

@ -36,12 +36,12 @@
name: 'attachment',
label: $t('re.workflow.task.grid.toolbar.viewAttachment'),
icon: 'bi-briefcase-fill',
enableIf: (arg) => {
return arg.selected && arg.selected.attachmentCount > 0;
enableIf: (args) => {
return args.selected && args.selected.attachmentCount > 0;
},
click: (arg) => {
if (arg.selected) {
attachmentIdRef = arg.selected.resourceId;
click: (args) => {
if (args.selected) {
attachmentIdRef = args.selected.resourceId;
attachmentDialogRef.open();
}
},
@ -176,8 +176,8 @@
},
}"
@row-click="
(evt, row, index) => {
currentSelectedTaskRef = row;
(args: any) => {
currentSelectedTaskRef = args.row;
historyProcessTaskGridRef?.refresh();
}
"
@ -201,7 +201,7 @@
:checkbox-selection="false"
:tree="false"
:fetch-data-url="
Environment.apiContextPath('/api/re/resource/workflow/task/queryHistory?processInstanceId=' + currentSelectedTaskRef.processInstanceId)
Environment.apiContextPath('/api/re/resource/workflow/task/queryHistory?processInstanceId=' + currentSelectedTaskRef?.processInstanceId)
"
:pageable="false"
:toolbar-configure="{ noIcon: false }"

169
io.sc.engine.rule.frontend/src/views/workflow/WorkflowApprovalComponent.vue

@ -1,4 +1,169 @@
<template>
<div>WorkflowApprovalComponent</div>
<w-dialog
ref="dialogRef"
:title="task.processDefinitionName + ' - ' + task.businessDescription"
width="800px"
:can-maximize="false"
body-padding="8px 8px 8px 8px"
@hide="
() => {
emit('close');
}
"
>
<w-grid
ref="historyProcessTaskGridRef"
:title="$t('re.workflow.dialog.historyTask')"
:height="200"
hide-bottom
dense-body
:config-button="false"
selection="multiple"
:checkbox-selection="false"
:tree="false"
:fetch-data-url="Environment.apiContextPath('/api/re/resource/workflow/task/queryHistory?processInstanceId=' + task.processInstanceId)"
:pageable="false"
:toolbar-configure="{ noIcon: false }"
:columns="[
{ width: 150, name: 'taskName', label: $t('re.workflow.task.grid.entity.taskName') },
{ width: 100, name: 'taskAssignee', label: $t('re.workflow.task.grid.entity.taskAssignee') },
{ width: 150, name: 'taskEndTime', label: $t('re.workflow.task.grid.entity.taskEndTime') },
{
width: '100%',
name: 'taskTreatment',
label: $t('re.workflow.task.grid.entity.taskTreatment'),
format: (value) => {
if (value) {
return value.replace(/(\r\n)|(\n)/g, '<br>');
}
return '';
},
},
]"
></w-grid>
<div style="height: 10px"></div>
<template v-if="action === 'process'">
<w-form
ref="formRef"
v-model="formModelValue"
:cols-num="1"
:fields="[{ name: 'treatment', label: $t('re.workflow.dialog.entity.treatment'), type: 'w-textarea', rows: 5, requiredIf: true }]"
>
</w-form>
<div style="height: 10px"></div>
</template>
<div class="row no-wrap items-center">
<div class="q-gutter-md">
<q-btn :label="$t('re.workflow.task.grid.toolbar.viewResource')" icon="bi-boxes" outline no-caps @click="viewResource" />
<q-btn
:disable="!(currentSelectedResourceRef?.attachmentCount > 0)"
:label="$t('re.workflow.task.grid.toolbar.viewAttachment')"
icon="bi-boxes"
outline
no-caps
@click="viewAttachment"
/>
</div>
<q-space />
<template v-if="action === 'process'">
<w-workflow-action
ref="workflowActionRef"
:task-id="task.id"
:data="formModelValue"
:action-url="Environment.apiContextPath('/api/flowable/process/operation/complete')"
@before-submit="beforeSubmit"
@after-submit="afterSubmit"
>
</w-workflow-action>
</template>
</div>
<div style="height: 10px">
<DesignerDialog ref="designerDialogRef"></DesignerDialog>
<AttachmentDialog
ref="attachmentDialogRef"
:fetch-data-url="Environment.apiContextPath('/api/system/attachment/findByBussinessKey')"
:data-url="Environment.apiContextPath('/api/system/attachment')"
foreign-key="bussinessKey"
:foreign-value="attachmentIdRef"
></AttachmentDialog>
</div>
</w-dialog>
</template>
<script setup lang="ts"></script>
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue';
import { axios, Environment, Tools, CorporationAuditorEntityManager } from 'platform-core';
import DesignerDialog from '@/views/resources/designer/DesignerDialog.vue';
import AttachmentDialog from '@/views/resources/AttachmentDialog.vue';
const props = defineProps({
task: { type: Object, default: undefined },
action: { type: String, default: undefined },
});
const emit = defineEmits(['close', 'afterTaskCompleted']);
const dialogRef = ref();
const formRef = ref();
const currentSelectedResourceRef = ref();
const historyProcessTaskGridRef = ref();
const designerDialogRef = ref();
const attachmentIdRef = ref();
const attachmentDialogRef = ref();
const formModelValue = reactive({
treatment: undefined,
});
const viewResource = () => {
designerDialogRef.value.open(currentSelectedResourceRef.value);
};
const viewAttachment = () => {
attachmentIdRef.value = currentSelectedResourceRef.value.id;
attachmentDialogRef.value.open();
};
const beforeSubmit = async (action, callback) => {
formRef.value.validate().then((value) => {
if (value) {
action.transientVariables.task_treatment = formModelValue.treatment;
callback(true);
}
});
};
const afterSubmit = () => {
close();
emit('afterTaskCompleted');
};
const close = () => {
dialogRef.value.hide();
};
onMounted(() => {
dialogRef.value.show();
const businessKey = props.task.businessKey;
const splits = businessKey.split(':');
const criteria = {
operator: 'and',
criteria: [
{
fieldName: 'code',
operator: 'equals',
value: splits[0],
},
{
fieldName: 'version',
operator: 'equals',
value: splits[1],
},
],
};
const criteriaQuery = encodeURIComponent(Tools.object2Json(criteria));
axios.get(Environment.apiContextPath('/api/re/resource?criteria=' + criteriaQuery)).then((response: any) => {
currentSelectedResourceRef.value = response.data.content[0];
});
});
</script>

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

@ -89,6 +89,7 @@ module.exports = {
use: [
{
loader: 'vue-loader',
options: { hotReload: false },
},
],
},

2
io.sc.engine.rule.server/src/main/resources/META-INF/platform/plugins/parameters.json

@ -41,7 +41,7 @@
"id" :"parameter.re.server.generatorType",
"parentId" :"parameter.re.server",
"code" :"parameter.re.server.generatorType",
"defaultValue" :"Groovy",
"defaultValue" :"Java",
"order" : 200,
"options" :{
"Groovy" : "parameter.re.server.generatorType.Groovy",

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

@ -112,7 +112,7 @@
"node-sql-parser": "5.3.6",
"pinia": "2.3.0",
"pinia-undo": "0.2.4",
"platform-core": "8.2.92",
"platform-core": "8.2.100",
"quasar": "2.17.6",
"sort-array": "5.0.0",
"svg-path-commander": "2.1.7",

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

@ -89,6 +89,7 @@ module.exports = {
use: [
{
loader: 'vue-loader',
options: { hotReload: false },
},
],
},

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

@ -112,7 +112,7 @@
"node-sql-parser": "5.3.6",
"pinia": "2.3.0",
"pinia-undo": "0.2.4",
"platform-core": "8.2.92",
"platform-core": "8.2.100",
"quasar": "2.17.6",
"sort-array": "5.0.0",
"svg-path-commander": "2.1.7",

1
io.sc.platform.ai.frontend/webpack.config.common.cjs

@ -89,6 +89,7 @@ module.exports = {
use: [
{
loader: 'vue-loader',
options: { hotReload: false },
},
],
},

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

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

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

@ -326,27 +326,24 @@
"graph.setting.panel.text.title": "Text",
"graph.setting.panel.arrange.title": "Arrange",
"home.card.shortcutmenu.title": "My Commonly used Actions",
"home.card.shortcutmenu.action.config": "Configure ...",
"home.card.myShortcutmenu.title": "My Commonly used Actions",
"home.card.myShortcutmenu.action.config": "Configure ...",
"home.card.task.title": "My Tasks",
"home.card.task.action.list": "all my tasks...",
"home.card.task.action.process": "Process",
"home.card.myTask.title": "My Tasks",
"home.card.myTask.action.list": "all my tasks",
"home.card.myTask.action.notSetting.tip": "Action not set, can NOT hand it!",
"home.card.doneTask.title": "My Tasks Done",
"home.card.doneTask.action.list": "all my done tasks...",
"home.card.doneTask.action.view": "View",
"home.card.myDoneTask.title": "My Tasks Done",
"home.card.myDoneTask.action.list": "all my done tasks",
"home.card.finishTask.title": "My Tasks Finished",
"home.card.finishTask.action.list": "all my finished tasks...",
"home.card.finishTask.action.view": "View",
"home.card.myFinishTask.title": "My Tasks Finished",
"home.card.myFinishTask.action.list": "all my finished tasks",
"home.card.message.title": "My Messages",
"home.card.message.action.list": "all my messages...",
"home.card.message.action.reply": "Reply",
"home.card.message.chatDialog.title": "Chat with {sender}",
"home.card.myMessage.title": "My Messages",
"home.card.myMessage.action.list": "all my messages",
"home.card.myMessage.chatDialog.title": "Chat with {sender}",
"home.card.myMessage.systemMessageDialog.title": "System Messaage",
"home.card.announcement.title": "Announcements",
"home.card.announcement.action.list": "all announcements...",
"home.card.announcement.action.view": "View"
"home.card.myAnnouncement.title": "Announcements",
"home.card.myAnnouncement.action.list": "all announcements"
}

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

@ -326,27 +326,24 @@
"graph.setting.panel.text.title": "文本",
"graph.setting.panel.arrange.title": "排列",
"home.card.shortcutmenu.title": "我的常用功能",
"home.card.shortcutmenu.action.config": "配置 ...",
"home.card.myShortcutmenu.title": "我的常用功能",
"home.card.myShortcutmenu.action.config": "配置 ...",
"home.card.task.title": "我的代辦",
"home.card.task.action.list": "顯示所有...",
"home.card.task.action.process": "辦理",
"home.card.myTask.title": "我的代辦",
"home.card.myTask.action.list": "顯示所有",
"home.card.myTask.action.notSetting.tip": "任務處理未設置, 无法直接处理!",
"home.card.doneTask.title": "我的已辦",
"home.card.doneTask.action.list": "顯示所有...",
"home.card.doneTask.action.view": "查看",
"home.card.myDoneTask.title": "我的已辦",
"home.card.myDoneTask.action.list": "顯示所有",
"home.card.finishTask.title": "我的辦結",
"home.card.finishTask.action.list": "顯示所有...",
"home.card.finishTask.action.view": "查看",
"home.card.myFinishTask.title": "我的辦結",
"home.card.myFinishTask.action.list": "顯示所有",
"home.card.message.title": "我的消息",
"home.card.message.action.list": "顯示所有...",
"home.card.message.action.reply": "回復",
"home.card.message.chatDialog.title": "與 {sender} 的對話",
"home.card.myMessage.title": "我的消息",
"home.card.myMessage.action.list": "顯示所有",
"home.card.myMessage.chatDialog.title": "與 {sender} 的對話",
"home.card.myMessage.systemMessageDialog.title": "系統消息",
"home.card.announcement.title": "系統公告",
"home.card.announcement.action.list": "顯示所有...",
"home.card.announcement.action.view": "查看"
"home.card.myAnnouncement.title": "系統公告",
"home.card.myAnnouncement.action.list": "顯示所有"
}

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

@ -327,27 +327,24 @@
"graph.setting.panel.text.title": "文本",
"graph.setting.panel.arrange.title": "排列",
"home.card.shortcutmenu.title": "我的常用功能",
"home.card.shortcutmenu.action.config": "配置 ...",
"home.card.myShortcutmenu.title": "我的常用功能",
"home.card.myShortcutmenu.action.config": "配置 ...",
"home.card.task.title": "我的代办",
"home.card.task.action.list": "显示所有...",
"home.card.task.action.process": "办理",
"home.card.myTask.title": "我的代办",
"home.card.myTask.action.list": "显示所有",
"home.card.myTask.action.notSetting.tip": "任务处理未设置, 无法直接处理!",
"home.card.doneTask.title": "我的已办",
"home.card.doneTask.action.list": "显示所有...",
"home.card.doneTask.action.view": "查看",
"home.card.myDoneTask.title": "我的已办",
"home.card.myDoneTask.action.list": "显示所有",
"home.card.finishTask.title": "我的办结",
"home.card.finishTask.action.list": "显示所有...",
"home.card.finishTask.action.view": "查看",
"home.card.myFinishTask.title": "我的办结",
"home.card.myFinishTask.action.list": "显示所有",
"home.card.message.title": "我的消息",
"home.card.message.action.list": "显示所有...",
"home.card.message.action.reply": "回复",
"home.card.message.chatDialog.title": "与 {sender} 的对话",
"home.card.myMessage.title": "我的消息",
"home.card.myMessage.action.list": "显示所有",
"home.card.myMessage.chatDialog.title": "与 {sender} 的对话",
"home.card.myMessage.systemMessageDialog.title": "系统消息",
"home.card.announcement.title": "系统公告",
"home.card.announcement.action.list": "显示所有...",
"home.card.announcement.action.view": "查看"
"home.card.myAnnouncement.title": "系统公告",
"home.card.myAnnouncement.action.list": "显示所有"
}

14
io.sc.platform.core.frontend/src/platform/layout/sub-layout/ChangeRoleDialog.vue

@ -67,7 +67,7 @@
{
name: 'roleId',
label: $t('role'),
type: 'select',
type: 'w-select',
required: true,
options: avaiableRoleOptionsRef,
defaultValue: currentRoleRef,
@ -80,17 +80,7 @@
import { ref, toRaw } from 'vue';
import { useRouter } from 'vue-router';
import type { UserSessionType } from '@/platform';
import {
axios,
Environment,
SessionManager,
AuthenticationManager,
MenuManager,
ComponentManager,
RouterManager,
ApplicationInitializer,
TagViewManager,
} from '@/platform';
import { axios, Environment, SessionManager, AuthenticationManager, MenuManager, ComponentManager, RouterManager, ApplicationInitializer } from '@/platform';
const emit = defineEmits(['change']);

168
io.sc.platform.core.frontend/src/platform/layout/sub-layout/Topper.vue

@ -1,4 +1,5 @@
<template>
<div>
<q-toolbar
:class="`flex flex-nowrap`"
:style="{
@ -59,80 +60,56 @@
</q-tab>
</q-tabs>
<q-tab-panels v-model="notifierTabRef" animated>
<q-tab-panel name="myTasks">
<q-tab-panel name="myTasks" class="px-2">
<q-list>
<q-item v-if="unCompletedTasksRef.length <= 0"></q-item>
<template v-if="Tools.trim(gc.setting.notifierTaskNavigateFrontendRoutePath) === ''">
<q-item v-for="item in unCompletedTasksRef" :key="item.id" v-ripple v-close-popup clickable class="px-0 pb-2" @click="handleTask(item)">
<q-item v-for="item in unCompletedTasksRef" :key="item.id" v-ripple v-close-popup clickable class="px-2 pb-2" @click="handleTask(item)">
<q-item-section avatar style="min-width: 0px; padding-right: 5px">
<q-icon color="blue" name="bi-dot" size="xl" />
<q-icon color="blue" name="bi-dot" size="sm" />
</q-item-section>
<q-item-section>
<q-item-label>{{ item.businessDescription }}</q-item-label>
<q-item-label caption lines="2">{{ item.processDefinitionName }} [{{ item.name }}]</q-item-label>
</q-item-section>
<q-item-section side top>
<q-item-label caption>{{ item.createTimeAndNowDiff }}{{ $t(item.createTimeAndNowDiffUnit) }}{{ $t('before') }}</q-item-label>
<q-item-label caption>{{ item.previousAssignee }}</q-item-label>
</q-item-section>
</q-item>
</template>
<template v-if="Tools.trim(gc.setting.notifierTaskNavigateFrontendRoutePath) !== ''">
<q-item
v-for="item in unCompletedTasksRef"
:key="item.id"
v-ripple
v-close-popup
clickable
class="px-0 pb-2"
:to="Tools.trim(gc.setting.notifierTaskNavigateFrontendRoutePath) + '?taskId=' + item.id"
exact
>
<q-item-section avatar style="min-width: 0px; padding-right: 5px">
<q-icon color="blue" name="bi-dot" size="xl" />
</q-item-section>
<q-item-section>
<q-item-label>{{ item.businessDescription }}</q-item-label>
<q-item-label class="truncate">
{{ item.businessDescription }}
<q-tooltip :delay="1000">{{ item.processDefinitionName }}</q-tooltip>
</q-item-label>
<q-item-label caption lines="2">{{ item.processDefinitionName }} [{{ item.name }}]</q-item-label>
</q-item-section>
<q-item-section side top>
<q-item-label caption>{{ item.createTimeAndNowDiff }}{{ $t(item.createTimeAndNowDiffUnit) }}{{ $t('before') }}</q-item-label>
<q-item-label caption>
{{ item.createTimeAndNowDiff }}{{ $t(item.createTimeAndNowDiffUnit) }}{{ $t('before') }}
<q-tooltip :delay="1000">{{ item.createTime }}</q-tooltip>
</q-item-label>
<q-item-label caption>{{ item.previousAssignee }}</q-item-label>
</q-item-section>
</q-item>
</template>
</q-list>
</q-tab-panel>
<q-tab-panel name="myMessages">
<q-tab-panel name="myMessages" class="px-2">
<q-list>
<q-item v-if="unReadedNotificationsRef.length <= 0"></q-item>
<q-item
v-for="item in unReadedNotificationsRef"
:key="item.id"
v-ripple
v-close-popup
clickable
class="px-0 pb-2"
@click="viewNotification(item)"
>
<q-item v-for="item in unReadedNotificationsRef" :key="item.id" v-ripple v-close-popup clickable class="px-2 pb-2" @click="handleMessage(item)">
<q-item-section avatar style="min-width: 0px; padding-right: 5px">
<q-icon color="blue" name="bi-dot" size="xl" />
<q-icon color="green" name="bi-dot" size="sm" />
</q-item-section>
<q-item-section>
<q-item-label>{{ item.title }}</q-item-label>
<q-item-label caption lines="2"><div v-html="item.content"></div></q-item-label>
<q-item-label class="truncate">
<div v-html="item.title ? item.title : item.content"></div>
<q-tooltip :delay="1000"><div v-html="item.title ? item.title : item.content"></div></q-tooltip>
</q-item-label>
</q-item-section>
<q-item-section side top>
<q-item-label caption>{{ item.sendDateAndNowDiff }}{{ $t(item.sendDateAndNowDiffUnit) }}{{ $t('before') }}</q-item-label>
<q-item-label caption>
{{ item.sendDateAndNowDiff }}{{ $t(item.sendDateAndNowDiffUnit) }}{{ $t('before') }}
<q-tooltip :delay="1000">{{ item.sendDate }}</q-tooltip>
</q-item-label>
<q-item-label caption>{{ item.sender }}</q-item-label>
</q-item-section>
</q-item>
</q-list>
</q-tab-panel>
<q-tab-panel name="systemAnnouncements">
<q-tab-panel name="systemAnnouncements" class="px-2">
<q-list>
<q-item v-if="unReadedAnnouncementsRef.length <= 0"></q-item>
<q-item
@ -141,18 +118,17 @@
v-ripple
v-close-popup
clickable
class="px-0 pb-2"
@click="viewAnnouncement(item)"
class="px-2 pb-2"
@click="handleAnnouncement(item)"
>
<q-item-section avatar style="min-width: 0px; padding-right: 5px">
<q-icon color="blue" name="bi-dot" size="xl" />
<q-icon color="orange" name="bi-dot" size="sm" />
</q-item-section>
<q-item-section>
<q-item-label>{{ item.title }}</q-item-label>
<q-item-label caption lines="2"><div v-html="item.content"></div></q-item-label>
<q-item-label class="truncate">{{ item.title }}</q-item-label>
</q-item-section>
<q-item-section side top>
<q-item-section side>
<q-item-label caption>{{ item.lastModifyDateAndNowDiff }}{{ $t(item.lastModifyDateAndNowDiffUnit) }}{{ $t('before') }}</q-item-label>
</q-item-section>
</q-item>
@ -216,7 +192,7 @@
</q-item-section>
</q-item>
<q-separator inset spaced />
<template v-if="SessionManager.getSession().user?.authorities?.length > 1">
<template v-if="SessionManager.getSession().user?.authorities?.length > 1 && gc.setting.enableChangeRole">
<q-item v-close-popup clickable @click="Environment.executeAction('changeRole')">
<q-item-section>
<q-item-label><q-icon name="group" left size="20px"></q-icon>{{ t('changeRole') }}</q-item-label>
@ -238,23 +214,29 @@
<AboutDialog ref="aboutDialog"></AboutDialog>
<ChangePasswordDialog ref="changePasswordDialog"></ChangePasswordDialog>
<ChangeRoleDialog ref="changeRoleDialog"></ChangeRoleDialog>
<ViewNotificationDialog ref="viewNotificationDialog"></ViewNotificationDialog>
<ViewAnnouncementDialog ref="viewAnnouncementDialog"></ViewAnnouncementDialog>
<UserMessageDialog ref="userMessageDialogRef" @close="findUnReadedNotifications"></UserMessageDialog>
<SystemMessageDialog ref="systemMessageDialogRef" @close="findUnReadedNotifications"></SystemMessageDialog>
<AnnouncementDialog ref="announcementDialogDialogRef" @close="findUnReadedAnnouncements"></AnnouncementDialog>
<div style="width: 0px; height: 0px">
<component :is="componentRef" style="width: 1px; height: 1px"></component>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue';
import { h, ref, computed, defineAsyncComponent, nextTick } from 'vue';
import { useRouter } from 'vue-router';
import { useQuasar, useInterval } from 'quasar';
import { useI18n } from 'vue-i18n';
import { axios, Environment, SessionManager, I18nMessageManager, AuthenticationManager, Tools } from '@/platform';
import { axios, Environment, SessionManager, I18nMessageManager, AuthenticationManager, ComponentManager, NotifyManager, Tools } from '@/platform';
import LoginDialog from '@/platform/views/LoginDialog';
import AboutDialog from './AboutDialog.vue';
import ChangePasswordDialog from './ChangePasswordDialog.vue';
import ChangeRoleDialog from './ChangeRoleDialog.vue';
import ViewNotificationDialog from './ViewNotificationDialog.vue';
import ViewAnnouncementDialog from './ViewAnnouncementDialog.vue';
import UserMessageDialog from '@/platform/views/home/UserMessageDialog.vue';
import SystemMessageDialog from '@/platform/views/home/SystemMessageDialog.vue';
import AnnouncementDialog from '@/platform/views/home/AnnouncementDialog.vue';
const { registerInterval } = useInterval();
const gc = Environment.getConfigure();
@ -265,9 +247,11 @@ const loginDialog = ref();
const aboutDialog = ref();
const changePasswordDialog = ref();
const changeRoleDialog = ref();
const viewNotificationDialog = ref();
const viewAnnouncementDialog = ref();
const userMessageDialogRef = ref();
const systemMessageDialogRef = ref();
const announcementDialogDialogRef = ref();
const { t } = useI18n();
const componentRef = ref();
const unCompletedTasksRef = ref([]);
const unCompletedTasksTotalCountRef = ref();
const unReadedNotificationsRef = ref([]);
@ -347,19 +331,42 @@ const logout = () => {
};
const doLogout = () => {
if (AuthenticationManager.isOauth2()) {
AuthenticationManager.removeLocalAccessToken();
window.location.href = Environment.getWebContextPath();
} else {
axios.post(Environment.apiContextPath(Environment.getConfigure().setting.logoutActionUrl)).then(() => {
window.location.href = Environment.getWebContextPath();
});
}
};
const findUnCompletedTasks = () => {
axios.get(Environment.apiContextPath('/api/flowable/process/query/task')).then((response) => {
axios.get(Environment.apiContextPath('/api/flowable/process/query/myTask')).then((response) => {
unCompletedTasksRef.value = response.data.content || [];
unCompletedTasksTotalCountRef.value = response.data.totalElements;
});
};
const findUnReadedNotifications = () => {
axios.get(Environment.apiContextPath('/api/system/notification/findUnReadedNotifications')).then((response) => {
const criteria = {
operator: 'and',
criteria: [
{
fieldName: 'receiver',
operator: 'equals',
value: SessionManager.getUser().loginName,
},
{
fieldName: 'receiveDate',
operator: 'isNull',
},
],
};
const criteriaQuery = encodeURIComponent(Tools.object2Json(criteria));
axios
.get(Environment.apiContextPath('/api/system/notification?page=1&size=10&pageable=true&sortBy=-sendDate&criteria=' + criteriaQuery))
.then((response: any) => {
unReadedNotificationsRef.value = response.data.content || [];
unReadedNotificationsTotalCountRef.value = response.data.totalElements;
});
@ -372,14 +379,37 @@ const findUnReadedAnnouncements = () => {
});
};
const handleTask = (item) => {};
const handleTask = (item) => {
if (item.taskHandFrontendModelName && item.taskHandFrontendComponentName) {
//
componentRef.value = null;
//
nextTick(() => {
const component = defineAsyncComponent(ComponentManager.getRemoteComponent(item.taskHandFrontendModelName, item.taskHandFrontendComponentName));
componentRef.value = h(component, {
action: 'process',
task: item,
properties: Tools.json2Object(item.taskHandFrontendComponentProperties),
onClose: findUnCompletedTasks,
});
});
} else if (item.taskHandFrontendRouteName) {
router.push({ name: item.taskHandFrontendRouteName });
} else {
NotifyManager.warn(t('home.card.task.action.notSetting.tip'));
}
};
const viewNotification = (item) => {
viewNotificationDialog.value.open(item);
const handleMessage = (item: any) => {
if (item.sender === 'system') {
systemMessageDialogRef.value.open(item);
} else {
userMessageDialogRef.value.open(item.sender);
}
};
const viewAnnouncement = (item) => {
viewAnnouncementDialog.value.open(item);
const handleAnnouncement = (item) => {
announcementDialogDialogRef.value.open(item);
};
Environment.registAction('about', about);

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

@ -207,7 +207,6 @@ class Environment {
* @param args
*/
public static executeAction(name: string, args?: any): void {
console.log(Environment.getActions());
const fun = Environment.getActions()[name];
if (fun) {
fun(args);

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

@ -53,7 +53,7 @@ export type ConfigureType = {
setting: {
sessionTimeout: number; //会话过期时间,单位:秒
homePage: string; // 首页路由 path
notifierTaskNavigateFrontendRoutePath: string; //顶部通知栏任务导航前端路由路径
logoutActionUrl: string; // 登出 Action URL
i18n: {
availableLocales: string[]; // 支持的国际化多语言数组
locale: string; // 默认区域
@ -62,6 +62,7 @@ export type ConfigureType = {
missingWarn: boolean; // 在找不到国际化多语言消息时的是否警告
changeNotify: boolean; // 当切换区域成功后是否显示通知
};
enableChangeRole: boolean; // 是否可切换角色
isMultiCorporationMode: boolean; //是否多法人模式
isPrimaryCorporation: boolean; //是否是主法人(主法人可以管理其他法人)
authenticationMode: string; //认证模式

39
io.sc.platform.core.frontend/src/platform/views/Home.vue

@ -2,21 +2,21 @@
<div>
<div class="row p-2">
<div class="col-12">
<ShortcutMenu></ShortcutMenu>
<MyShortcutMenu ref="myShortcutMenuRef"></MyShortcutMenu>
</div>
</div>
<div class="row p-2">
<div class="col-7 pr-2">
<Task></Task>
<MyTask ref="myTaskRef" @after-refresh="afterMyTaskRefresh"></MyTask>
<div style="height: 10px"></div>
<DoneTask></DoneTask>
<MyDoneTask ref="myDoneTaskRef"></MyDoneTask>
<div style="height: 10px"></div>
<FinishedTask></FinishedTask>
<MyFinishedTask ref="myFinishedTaskRef"></MyFinishedTask>
</div>
<div class="col-5 pl-2">
<Message></Message>
<MyMessage ref="myMessageRef"></MyMessage>
<div style="height: 10px"></div>
<Announcement></Announcement>
<MyAnnouncement ref="myAnnouncementRef"></MyAnnouncement>
</div>
</div>
</div>
@ -24,15 +24,24 @@
<script setup lang="ts">
import { ref } from 'vue';
import dayjs from 'dayjs';
import { axios, Environment, SessionManager, I18nMessageManager, AuthenticationManager, Tools } from '@/platform';
import ShortcutMenu from './home/ShortcutMenu.vue';
import Task from './home/Task.vue';
import DoneTask from './home/DoneTask.vue';
import FinishedTask from './home/FinishedTask.vue';
import Message from './home/Message.vue';
import Announcement from './home/Announcement.vue';
import MyShortcutMenu from './home/MyShortcutMenu.vue';
import MyTask from './home/MyTask.vue';
import MyDoneTask from './home/MyDoneTask.vue';
import MyFinishedTask from './home/MyFinishedTask.vue';
import MyMessage from './home/MyMessage.vue';
import MyAnnouncement from './home/MyAnnouncement.vue';
const gc = Environment.getConfigure();
const now = ref(dayjs().format('YYYY-MM-DD'));
const myShortcutMenuRef = ref();
const myTaskRef = ref();
const myDoneTaskRef = ref();
const myFinishedTaskRef = ref();
const myMessageRef = ref();
const myAnnouncementRef = ref();
const afterMyTaskRefresh = () => {
myDoneTaskRef?.value?.refresh();
myFinishedTaskRef?.value?.refresh();
myMessageRef?.value?.refresh();
};
</script>

76
io.sc.platform.core.frontend/src/platform/views/home/Announcement.vue

@ -1,76 +0,0 @@
<template>
<q-card ref="cardRef" flat bordered :style="{ width: '100%', height: cardHeightRef + 'px' }">
<q-resize-observer @resize="changeFirstColMaxWidth" />
<q-card-section class="text-orange px-2 pt-1 pb-0">
<div class="row no-wrap items-center">
<q-icon name="bi-megaphone" size="1.3em" />
<span class="text-weight-bolder px-2">{{ $t('home.card.announcement.title') }}</span>
<q-space />
<q-btn size="12px" icon="bi-arrow-repeat" flat no-caps padding="2px 6px 2px 6px" :title="$t('refresh')" :loading="loadingRef" @click="refresh" />
<q-btn
size="12px"
icon="bi-justify"
flat
no-caps
padding="2px 6px 2px 6px"
:title="$t('home.card.announcement.action.list')"
:to="{ name: 'route.workbench.announcement' }"
/>
</div>
</q-card-section>
<q-card-section class="p-0">
<q-markup-table flat dense separator="none" :style="{ width: '100%', height: tableHeightRef + 'px', overflowY: 'auto' }">
<tbody>
<tr v-for="item in itemsRef" :key="item.id">
<td width="100%" :title="item.content" class="truncate" :style="{ maxWidth: firstColMaxWidthRef + 'px' }">
<a href="javascript:void(0);" style="cursor: pointer; color: blue" @click="handleTask(item)" v-html="item.content"></a>
</td>
<td width="80px" :title="item.lastModifyDate" class="smallFont truncate" style="max-width: 80px" align="right">
{{ item.lastModifyDateAndNowDiff + $t(item.lastModifyDateAndNowDiffUnit) + $t('before') }}
</td>
</tr>
</tbody>
</q-markup-table>
<AnnouncementDialog ref="dialogRef"></AnnouncementDialog>
</q-card-section>
</q-card>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { axios, Environment } from '@/platform';
import AnnouncementDialog from './AnnouncementDialog.vue';
const cardRef = ref();
const cardHeightRef = ref(300);
const tableHeightRef = ref(300 - 30);
const firstColMaxWidthRef = ref(100);
const itemsRef = ref([]);
const loadingRef = ref(false);
const dialogRef = ref();
const changeFirstColMaxWidth = () => {
let width = Math.ceil(cardRef.value.$el.clientWidth);
width = width - 100;
firstColMaxWidthRef.value = width > 100 ? width : 100;
};
const refresh = () => {
loadingRef.value = true;
axios
.get(Environment.apiContextPath('/api/system/announcement/findUnReadedAnnouncements'))
.then((response: any) => {
itemsRef.value = response.data.content || [];
loadingRef.value = false;
})
.catch(() => {
loadingRef.value = false;
});
};
const handleTask = (item: any) => {
dialogRef.value.open(item);
};
refresh();
</script>

16
io.sc.platform.core.frontend/src/platform/views/home/AnnouncementDialog.vue

@ -1,20 +1,29 @@
<template>
<w-dialog
ref="dialogRef"
:title="$t('system.announcementManager.grid.title')"
:title="$t('home.card.announcement.title')"
:can-maximize="false"
:maximized="false"
body-padding="2px 2px 2px 2px"
width="60%"
@hide="
() => {
emit('close');
}
"
>
<div class="p-2">
<div class="text-h6 pb-2">{{ itemRef.title }}</div>
<p v-html="itemRef.content"></p>
<div class="text-h6 p-2">{{ itemRef.title }}</div>
<div class="p-2" v-html="itemRef.content"></div>
</div>
<div style="height: 20px"></div>
</w-dialog>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { axios, Environment } from '@/platform';
const emit = defineEmits(['close']);
const dialogRef = ref();
const itemRef = ref();
@ -22,6 +31,7 @@ const itemRef = ref();
const open = (item: any) => {
itemRef.value = item;
dialogRef.value.show();
axios.post(Environment.apiContextPath('/api/system/user/updateLastReadAnnouncementDate/' + item.id));
};
const close = () => {

93
io.sc.platform.core.frontend/src/platform/views/home/DoneTask.vue

@ -1,93 +0,0 @@
<template>
<q-card ref="cardRef" flat bordered :style="{ width: '100%', height: cardHeightRef + 'px' }">
<q-resize-observer @resize="changeFirstColMaxWidth" />
<q-card-section class="text-secondary px-2 pt-1 pb-0">
<div class="row no-wrap items-center">
<q-icon name="bi-list-check" size="1.5em" />
<span class="text-weight-bolder px-2">{{ $t('home.card.doneTask.title') }}</span>
<q-space />
<q-btn size="12px" icon="bi-arrow-repeat" flat no-caps padding="2px 6px 2px 6px" :title="$t('refresh')" :loading="loadingRef" @click="refresh" />
<q-btn
size="12px"
icon="bi-justify"
flat
no-caps
padding="2px 6px 2px 6px"
:title="$t('home.card.doneTask.action.list')"
:to="{ name: 'route.workbench.doneTask' }"
/>
</div>
</q-card-section>
<q-card-section class="p-0">
<q-markup-table flat dense separator="none" :style="{ width: '100%', height: tableHeightRef + 'px', overflowY: 'auto' }">
<tbody>
<tr v-for="item in itemsRef" :key="item.id" @click="handleTask(item)">
<td width="100%" :title="item.businessDescription" class="truncate" :style="{ maxWidth: firstColMaxWidthRef + 'px' }">
<w-html-a :label="item.businessDescription" @click="handleTask(item)"></w-html-a>
</td>
<td width="80px" :title="item.processDefinitionName" class="smallFont truncate" style="max-width: 80px; font-size: 0.8em; padding: 0px 4px">
{{ item.processDefinitionName }}
</td>
<td width="60px" :title="item.name" class="smallFont truncate" style="max-width: 60px; font-size: 0.8em; padding: 0px 4px">
{{ item.name }}
</td>
<td width="60px" :title="item.previousAssignee" class="smallFont truncate" style="max-width: 60px; font-size: 0.8em; padding: 0px 4px">
{{ item.previousAssignee }}
</td>
<td width="80px" :title="item.createTime" class="smallFont truncate" style="max-width: 80px; font-size: 0.8em; padding: 0px 4px" align="right">
{{ item.createTimeAndNowDiff + $t(item.createTimeAndNowDiffUnit) + $t('before') }}
</td>
</tr>
</tbody>
</q-markup-table>
</q-card-section>
</q-card>
</template>
<script setup lang="ts">
import { h, ref, defineAsyncComponent, nextTick } from 'vue';
import { axios, Environment, ComponentManager, Tools } from '@/platform';
const cardRef = ref();
const cardHeightRef = ref(200);
const tableHeightRef = ref(200 - 30);
const firstColMaxWidthRef = ref(100);
const itemsRef = ref([]);
const loadingRef = ref(false);
const componentRef = ref();
const changeFirstColMaxWidth = () => {
let width = Math.ceil(cardRef.value.$el.clientWidth);
width = width - 280;
firstColMaxWidthRef.value = width > 100 ? width : 100;
};
const refresh = () => {
loadingRef.value = true;
axios
.get(Environment.apiContextPath('/api/flowable/process/query/task'))
.then((response: any) => {
itemsRef.value = response.data.content || [];
loadingRef.value = false;
})
.catch(() => {
loadingRef.value = false;
});
};
const handleTask = async (item: any) => {
//
componentRef.value = null;
//
nextTick(() => {
const component = defineAsyncComponent(ComponentManager.getRemoteComponent(item.taskHandFrontendModelName, item.taskHandFrontendComponentName));
const properties = Tools.mergeObject(
{ taskId: item.id, businessKey: item.businessKey, action: 'process' },
Tools.json2Object(item.taskHandFrontendComponentProperties),
);
componentRef.value = h(component, properties);
});
};
refresh();
</script>

93
io.sc.platform.core.frontend/src/platform/views/home/FinishedTask.vue

@ -1,93 +0,0 @@
<template>
<q-card ref="cardRef" flat bordered :style="{ width: '100%', height: cardHeightRef + 'px' }">
<q-resize-observer @resize="changeFirstColMaxWidth" />
<q-card-section class="text-info px-2 pt-1 pb-0">
<div class="row no-wrap items-center">
<q-icon name="bi-card-list" size="1.4em" />
<span class="text-weight-bolder px-2">{{ $t('home.card.finishTask.title') }}</span>
<q-space />
<q-btn size="12px" icon="bi-arrow-repeat" flat no-caps padding="2px 6px 2px 6px" :title="$t('refresh')" :loading="loadingRef" @click="refresh" />
<q-btn
size="12px"
icon="bi-justify"
flat
no-caps
padding="2px 6px 2px 6px"
:title="$t('home.card.finishTask.action.list')"
:to="{ name: 'route.workbench.finishedTask' }"
/>
</div>
</q-card-section>
<q-card-section class="p-0">
<q-markup-table flat dense separator="none" :style="{ width: '100%', height: tableHeightRef + 'px', overflowY: 'auto' }">
<tbody>
<tr v-for="item in itemsRef" :key="item.id" @click="handleTask(item)">
<td width="100%" :title="item.businessDescription" class="truncate" :style="{ maxWidth: firstColMaxWidthRef + 'px' }">
<w-html-a :label="item.businessDescription" @click="handleTask(item)"></w-html-a>
</td>
<td width="80px" :title="item.processDefinitionName" class="smallFont truncate" style="max-width: 80px; font-size: 0.8em; padding: 0px 4px">
{{ item.processDefinitionName }}
</td>
<td width="60px" :title="item.name" class="smallFont truncate" style="max-width: 60px; font-size: 0.8em; padding: 0px 4px">
{{ item.name }}
</td>
<td width="60px" :title="item.previousAssignee" class="smallFont truncate" style="max-width: 60px; font-size: 0.8em; padding: 0px 4px">
{{ item.previousAssignee }}
</td>
<td width="80px" :title="item.createTime" class="smallFont truncate" style="max-width: 80px; font-size: 0.8em; padding: 0px 4px" align="right">
{{ item.createTimeAndNowDiff + $t(item.createTimeAndNowDiffUnit) + $t('before') }}
</td>
</tr>
</tbody>
</q-markup-table>
</q-card-section>
</q-card>
</template>
<script setup lang="ts">
import { h, ref, defineAsyncComponent, nextTick } from 'vue';
import { axios, Environment, ComponentManager, Tools } from '@/platform';
const cardRef = ref();
const cardHeightRef = ref(200);
const tableHeightRef = ref(200 - 30);
const firstColMaxWidthRef = ref(100);
const itemsRef = ref([]);
const loadingRef = ref(false);
const componentRef = ref();
const changeFirstColMaxWidth = () => {
let width = Math.ceil(cardRef.value.$el.clientWidth);
width = width - 280;
firstColMaxWidthRef.value = width > 100 ? width : 100;
};
const refresh = () => {
loadingRef.value = true;
axios
.get(Environment.apiContextPath('/api/flowable/process/query/task'))
.then((response: any) => {
itemsRef.value = response.data.content || [];
loadingRef.value = false;
})
.catch(() => {
loadingRef.value = false;
});
};
const handleTask = async (item: any) => {
//
componentRef.value = null;
//
nextTick(() => {
const component = defineAsyncComponent(ComponentManager.getRemoteComponent(item.taskHandFrontendModelName, item.taskHandFrontendComponentName));
const properties = Tools.mergeObject(
{ taskId: item.id, businessKey: item.businessKey, action: 'process' },
Tools.json2Object(item.taskHandFrontendComponentProperties),
);
componentRef.value = h(component, properties);
});
};
refresh();
</script>

88
io.sc.platform.core.frontend/src/platform/views/home/Message.vue

@ -1,88 +0,0 @@
<template>
<q-card ref="cardRef" flat bordered :style="{ width: '100%', height: cardHeightRef + 'px' }">
<q-resize-observer @resize="changeFirstColMaxWidth" />
<q-card-section class="text-positive px-2 pt-1 pb-0">
<div class="row no-wrap items-center">
<q-icon name="bi-chat-text" size="1.5em" />
<span class="text-weight-bolder px-2">{{ $t('home.card.message.title') }}</span>
<q-space />
<q-btn size="12px" icon="bi-arrow-repeat" flat no-caps padding="2px 6px 2px 6px" :title="$t('refresh')" :loading="loadingRef" @click="refresh" />
<q-btn
size="12px"
icon="bi-justify"
flat
no-caps
padding="2px 6px 2px 6px"
:title="$t('home.card.message.action.list')"
:to="{ name: 'route.workbench.myMessage' }"
/>
</div>
</q-card-section>
<q-card-section class="p-0">
<q-markup-table flat dense separator="none" :style="{ width: '100%', height: tableHeightRef + 'px', overflowY: 'auto' }">
<tbody>
<tr v-for="item in itemsRef" :key="item.id">
<td width="100%" :title="item.content" class="truncate" :style="{ maxWidth: firstColMaxWidthRef + 'px' }">
<a href="javascript:void(0);" style="cursor: pointer; color: blue" @click="handleTask(item)" v-html="item.content"></a>
</td>
<td width="80px" :title="item.sender" class="smallFont truncate" style="max-width: 80px">
{{ item.sender }}
</td>
<td width="80px" :title="item.sendDate" class="smallFont truncate" style="max-width: 80px" align="right">
{{ item.sendDateAndNowDiff + $t(item.sendDateAndNowDiffUnit) + $t('before') }}
</td>
</tr>
</tbody>
</q-markup-table>
</q-card-section>
<ChatDialog ref="chatDialogRef"> </ChatDialog>
</q-card>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { axios, Environment } from '@/platform';
import ChatDialog from './ChatDialog.vue';
const cardRef = ref();
const cardHeightRef = ref(310);
const tableHeightRef = ref(300 - 30);
const firstColMaxWidthRef = ref(100);
const itemsRef = ref([]);
const loadingRef = ref(false);
const chatDialogRef = ref();
const changeFirstColMaxWidth = () => {
let width = Math.ceil(cardRef.value.$el.clientWidth);
width = width - 160;
firstColMaxWidthRef.value = width > 100 ? width : 100;
};
const refresh = () => {
loadingRef.value = true;
axios
.get(Environment.apiContextPath('/api/system/notification/findUnReadedNotifications'))
.then((response: any) => {
itemsRef.value = response.data.content || [];
loadingRef.value = false;
})
.catch(() => {
loadingRef.value = false;
});
};
const handleTask = (item: any) => {
chatDialogRef.value.open(item.sender);
};
refresh();
</script>
<style scoped>
.q-markup-table td {
padding: 0px 4px;
}
.smallFont {
font-size: 0.8em;
}
</style>

86
io.sc.platform.core.frontend/src/platform/views/home/MyAnnouncement.vue

@ -0,0 +1,86 @@
<template>
<q-card ref="cardRef" flat bordered :style="{ width: '100%', height: cardHeightRef + 'px' }">
<q-resize-observer @resize="changeFirstColMaxWidth" />
<q-card-section class="text-orange px-2 pt-1 pb-0">
<div class="row no-wrap items-center">
<q-icon name="bi-megaphone" size="1.3em" />
<span class="text-weight-bolder px-2">{{ $t('home.card.myAnnouncement.title') }}</span>
<q-space />
<q-btn size="12px" icon="bi-arrow-repeat" flat no-caps padding="2px 6px 2px 6px" :loading="loadingRef" @click="refresh">
<q-tooltip :delay="1000">{{ $t('refresh') }}</q-tooltip>
</q-btn>
<q-btn size="12px" icon="bi-justify" flat no-caps padding="2px 6px 2px 6px" :to="{ name: 'route.workbench.announcement' }">
<q-tooltip :delay="1000">{{ $t('home.card.myAnnouncement.action.list') }}</q-tooltip>
</q-btn>
</div>
</q-card-section>
<q-card-section style="padding: 0px 6px 0px 6px">
<q-markup-table flat dense separator="none" :style="{ width: '100%', height: tableHeightRef + 'px', overflowY: 'auto' }">
<tbody>
<tr v-for="item in itemsRef" :key="item.id">
<td width="100%" style="padding: 0px 4px">
<a href="javascript:void(0);" class="flex flex-nowrap" :style="{ color: $gc.theme.home.announcementTextColor }" @click="handle(item)">
<q-icon :color="item.userReaded ? 'grey-4' : 'positive'" name="bi-dot" size="sm" />
<div class="truncate" :style="{ width: firstColMaxWidthRef + 'px', maxWidth: firstColMaxWidthRef + 'px' }" v-html="item.title"></div>
</a>
<q-tooltip :delay="1000"><div v-html="item.title"></div></q-tooltip>
</td>
<td width="70px" style="font-size: 0.8em; padding: 0px 4px" align="right">
<div class="truncate" style="width: 62px; max-width: 62px">
{{ item.lastModifyDateAndNowDiff + $t(item.lastModifyDateAndNowDiffUnit) + $t('before') }}
</div>
<q-tooltip :delay="1000">{{ item.lastModifyDate }}</q-tooltip>
</td>
</tr>
</tbody>
</q-markup-table>
<AnnouncementDialog ref="dialogRef"></AnnouncementDialog>
</q-card-section>
</q-card>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { axios, Environment } from '@/platform';
import AnnouncementDialog from './AnnouncementDialog.vue';
const cardRef = ref();
const cardHeightRef = ref(300);
const tableHeightRef = ref(300 - 30);
const firstColMaxWidthRef = ref(100);
const itemsRef = ref([]);
const loadingRef = ref(false);
const dialogRef = ref();
const changeFirstColMaxWidth = () => {
//card width
let width = Math.ceil(cardRef.value.$el.clientWidth);
width = width - 8 /* card-section padding width */ - 12 /* td padding width */ - 70 /* other tds width*/ - 24 /* icon width */;
//min width
width = width >= 100 ? width : 100;
firstColMaxWidthRef.value = width;
};
const refresh = () => {
loadingRef.value = true;
axios
.get(Environment.apiContextPath('/api/system/announcement?page=1&size=10&pageable=true&sortBy=-lastModifyDate'))
.then((response: any) => {
itemsRef.value = response.data.content || [];
loadingRef.value = false;
})
.catch(() => {
loadingRef.value = false;
});
};
const handle = (item: any) => {
dialogRef.value.open(item);
};
refresh();
defineExpose({
refresh,
});
</script>

125
io.sc.platform.core.frontend/src/platform/views/home/MyDoneTask.vue

@ -0,0 +1,125 @@
<template>
<div>
<q-card ref="cardRef" flat bordered :style="{ width: '100%', height: cardHeightRef + 'px' }">
<q-resize-observer @resize="changeFirstColMaxWidth" />
<q-card-section class="text-secondary px-2 pt-1 pb-0">
<div class="row no-wrap items-center">
<q-icon name="bi-list-check" size="1.5em" />
<span class="text-weight-bolder px-2">{{ $t('home.card.myDoneTask.title') }}</span>
<q-space />
<q-btn size="12px" icon="bi-arrow-repeat" flat no-caps padding="2px 6px 2px 6px" :loading="loadingRef" @click="refresh">
<q-tooltip :delay="1000">{{ $t('refresh') }}</q-tooltip>
</q-btn>
<q-btn size="12px" icon="bi-justify" flat no-caps padding="2px 6px 2px 6px" :to="{ name: 'route.workbench.doneTask' }">
<q-tooltip :delay="1000">{{ $t('home.card.myDoneTask.action.list') }}</q-tooltip>
</q-btn>
</div>
</q-card-section>
<q-card-section style="padding: 0px 6px 0px 6px">
<q-markup-table flat dense separator="none" :style="{ width: '100%', height: tableHeightRef + 'px', overflowY: 'auto' }">
<tbody>
<tr v-for="item in itemsRef" :key="item.id">
<td width="100%" style="padding: 0px 4px">
<a href="javascript:void(0);" :style="{ color: $gc.theme.home.myDoneTaskTextColor }" @click="handle(item)">
<div
class="truncate"
:style="{ width: firstColMaxWidthRef + 'px', maxWidth: firstColMaxWidthRef + 'px' }"
v-html="item.businessDescription"
></div>
</a>
<q-tooltip :delay="1000">{{ item.businessDescription }}</q-tooltip>
</td>
<td width="100px" style="font-size: 0.8em; padding: 0px 4px">
<div class="truncate" style="width: 92px; max-width: 92px">
{{ item.processDefinitionName }}
</div>
<q-tooltip :delay="1000">{{ item.processDefinitionName }}</q-tooltip>
</td>
<td width="60px" style="font-size: 0.8em; padding: 0px 4px">
<div class="truncate" style="width: 52px; max-width: 52px">{{ item.name }}</div>
<q-tooltip :delay="1000">{{ item.name }}</q-tooltip>
</td>
<td width="60px" style="font-size: 0.8em; padding: 0px 4px">
<div class="truncate" style="width: 52px; max-width: 52px">{{ item.previousAssignee }}</div>
<q-tooltip :delay="1000">{{ item.previousAssignee }}</q-tooltip>
</td>
<td width="70px" style="font-size: 0.8em; padding: 0px 4px" align="right">
<div class="truncate" style="width: 62px; max-width: 62px">
{{ item.createTimeAndNowDiff + $t(item.createTimeAndNowDiffUnit) + $t('before') }}
</div>
<q-tooltip :delay="1000">{{ item.createTime }}</q-tooltip>
</td>
</tr>
</tbody>
</q-markup-table>
</q-card-section>
</q-card>
<div style="width: 0px; height: 0px">
<component :is="componentRef" style="width: 1px; height: 1px"></component>
</div>
</div>
</template>
<script setup lang="ts">
import { h, ref, defineAsyncComponent, nextTick } from 'vue';
import { useRouter } from 'vue-router';
import { $t, axios, Environment, ComponentManager, Tools, NotifyManager } from '@/platform';
const router = useRouter();
const cardRef = ref();
const cardHeightRef = ref(200);
const tableHeightRef = ref(200 - 30);
const firstColMaxWidthRef = ref(100);
const itemsRef = ref([]);
const loadingRef = ref(false);
const componentRef = ref();
const changeFirstColMaxWidth = () => {
//card width
let width = Math.ceil(cardRef.value.$el.clientWidth);
width = width - 8 /* card-section padding width */ - 12 /* td padding width */ - 290 /* other tds width*/;
//min width
width = width >= 100 ? width : 100;
firstColMaxWidthRef.value = width;
};
const refresh = () => {
loadingRef.value = true;
axios
.get(Environment.apiContextPath('/api/flowable/process/query/myDoneTask'))
.then((response: any) => {
itemsRef.value = response.data.content || [];
loadingRef.value = false;
})
.catch(() => {
loadingRef.value = false;
});
};
const handle = async (item: any) => {
if (item.taskHandFrontendModelName && item.taskHandFrontendComponentName) {
//
componentRef.value = null;
//
nextTick(() => {
const component = defineAsyncComponent(ComponentManager.getRemoteComponent(item.taskHandFrontendModelName, item.taskHandFrontendComponentName));
componentRef.value = h(component, {
action: 'view',
task: item,
properties: Tools.json2Object(item.taskHandFrontendComponentProperties),
onClose: refresh,
});
});
} else if (item.taskHandFrontendRouteName) {
router.push({ name: item.taskHandFrontendRouteName });
} else {
NotifyManager.warn($t('home.card.task.action.notSetting.tip'));
}
};
refresh();
defineExpose({
refresh,
});
</script>

125
io.sc.platform.core.frontend/src/platform/views/home/MyFinishedTask.vue

@ -0,0 +1,125 @@
<template>
<div>
<q-card ref="cardRef" flat bordered :style="{ width: '100%', height: cardHeightRef + 'px' }">
<q-resize-observer @resize="changeFirstColMaxWidth" />
<q-card-section class="text-info px-2 pt-1 pb-0">
<div class="row no-wrap items-center">
<q-icon name="bi-card-list" size="1.4em" />
<span class="text-weight-bolder px-2">{{ $t('home.card.myFinishTask.title') }}</span>
<q-space />
<q-btn size="12px" icon="bi-arrow-repeat" flat no-caps padding="2px 6px 2px 6px" :loading="loadingRef" @click="refresh">
<q-tooltip :delay="1000">{{ $t('refresh') }}</q-tooltip>
</q-btn>
<q-btn size="12px" icon="bi-justify" flat no-caps padding="2px 6px 2px 6px" :to="{ name: 'route.workbench.finishedTask' }">
<q-tooltip :delay="1000">{{ $t('home.card.myFinishTask.action.list') }}</q-tooltip>
</q-btn>
</div>
</q-card-section>
<q-card-section style="padding: 0px 6px 0px 6px">
<q-markup-table flat dense separator="none" :style="{ width: '100%', height: tableHeightRef + 'px', overflowY: 'auto' }">
<tbody>
<tr v-for="item in itemsRef" :key="item.id">
<td width="100%" style="padding: 0px 4px">
<a href="javascript:void(0);" :style="{ color: $gc.theme.home.myFinishedTaskTextColor }" @click="handle(item)">
<div
class="truncate"
:style="{ width: firstColMaxWidthRef + 'px', maxWidth: firstColMaxWidthRef + 'px' }"
v-html="item.businessDescription"
></div>
</a>
<q-tooltip :delay="1000">{{ item.businessDescription }}</q-tooltip>
</td>
<td width="100px" style="font-size: 0.8em; padding: 0px 4px">
<div class="truncate" style="width: 92px; max-width: 92px">
{{ item.processDefinitionName }}
</div>
<q-tooltip :delay="1000">{{ item.processDefinitionName }}</q-tooltip>
</td>
<td width="60px" style="font-size: 0.8em; padding: 0px 4px">
<div class="truncate" style="width: 52px; max-width: 52px">{{ item.name }}</div>
<q-tooltip :delay="1000">{{ item.name }}</q-tooltip>
</td>
<td width="60px" style="font-size: 0.8em; padding: 0px 4px">
<div class="truncate" style="width: 52px; max-width: 52px">{{ item.previousAssignee }}</div>
<q-tooltip :delay="1000">{{ item.previousAssignee }}</q-tooltip>
</td>
<td width="70px" style="font-size: 0.8em; padding: 0px 4px" align="right">
<div class="truncate" style="width: 62px; max-width: 62px">
{{ item.createTimeAndNowDiff + $t(item.createTimeAndNowDiffUnit) + $t('before') }}
</div>
<q-tooltip :delay="1000">{{ item.createTime }}</q-tooltip>
</td>
</tr>
</tbody>
</q-markup-table>
</q-card-section>
</q-card>
<div style="width: 0px; height: 0px">
<component :is="componentRef" style="width: 1px; height: 1px"></component>
</div>
</div>
</template>
<script setup lang="ts">
import { h, ref, defineAsyncComponent, nextTick } from 'vue';
import { useRouter } from 'vue-router';
import { $t, axios, Environment, ComponentManager, Tools, NotifyManager } from '@/platform';
const router = useRouter();
const cardRef = ref();
const cardHeightRef = ref(200);
const tableHeightRef = ref(200 - 30);
const firstColMaxWidthRef = ref(100);
const itemsRef = ref([]);
const loadingRef = ref(false);
const componentRef = ref();
const changeFirstColMaxWidth = () => {
//card width
let width = Math.ceil(cardRef.value.$el.clientWidth);
width = width - 8 /* card-section padding width */ - 12 /* td padding width */ - 290 /* other tds width*/;
//min width
width = width >= 100 ? width : 100;
firstColMaxWidthRef.value = width;
};
const refresh = () => {
loadingRef.value = true;
axios
.get(Environment.apiContextPath('/api/flowable/process/query/myFinishedTask'))
.then((response: any) => {
itemsRef.value = response.data.content || [];
loadingRef.value = false;
})
.catch(() => {
loadingRef.value = false;
});
};
const handle = async (item: any) => {
if (item.taskHandFrontendModelName && item.taskHandFrontendComponentName) {
//
componentRef.value = null;
//
nextTick(() => {
const component = defineAsyncComponent(ComponentManager.getRemoteComponent(item.taskHandFrontendModelName, item.taskHandFrontendComponentName));
componentRef.value = h(component, {
action: 'view',
task: item,
properties: Tools.json2Object(item.taskHandFrontendComponentProperties),
onClose: refresh,
});
});
} else if (item.taskHandFrontendRouteName) {
router.push({ name: item.taskHandFrontendRouteName });
} else {
NotifyManager.warn($t('home.card.task.action.notSetting.tip'));
}
};
refresh();
defineExpose({
refresh,
});
</script>

107
io.sc.platform.core.frontend/src/platform/views/home/MyMessage.vue

@ -0,0 +1,107 @@
<template>
<q-card ref="cardRef" flat bordered :style="{ width: '100%', height: cardHeightRef + 'px' }">
<q-resize-observer @resize="changeFirstColMaxWidth" />
<q-card-section class="text-positive px-2 pt-1 pb-0">
<div class="row no-wrap items-center">
<q-icon name="bi-chat-text" size="1.5em" />
<span class="text-weight-bolder px-2">{{ $t('home.card.myMessage.title') }}</span>
<q-space />
<q-btn size="12px" icon="bi-arrow-repeat" flat no-caps padding="2px 6px 2px 6px" :loading="loadingRef" @click="refresh">
<q-tooltip :delay="1000">{{ $t('refresh') }}</q-tooltip>
</q-btn>
<q-btn size="12px" icon="bi-justify" flat no-caps padding="2px 6px 2px 6px" :to="{ name: 'route.workbench.myMessage' }">
<q-tooltip :delay="1000">{{ $t('home.card.myMessage.action.list') }}</q-tooltip>
</q-btn>
</div>
</q-card-section>
<q-card-section style="padding: 0px 6px 0px 6px">
<q-markup-table flat dense separator="none" :style="{ width: '100%', height: tableHeightRef + 'px', overflowY: 'auto' }">
<tbody>
<tr v-for="item in itemsRef" :key="item.id">
<td width="100%" style="padding: 0px 4px">
<a href="javascript:void(0);" class="flex flex-nowrap" :style="{ color: $gc.theme.home.myMessageTextColor }" @click="handle(item)">
<q-icon :color="item.receiveDate ? 'grey-4' : 'positive'" name="bi-dot" size="sm" />
<div
class="truncate"
:style="{ width: firstColMaxWidthRef + 'px', maxWidth: firstColMaxWidthRef + 'px' }"
v-html="item.title ? item.title : item.content"
></div>
</a>
<q-tooltip :delay="1000"><div v-html="item.title ? item.title : item.content"></div></q-tooltip>
</td>
<td width="80px" style="font-size: 0.8em; padding: 0px 4px">
<div class="truncate" style="width: 72px; max-width: 72px">{{ item.sender }}</div>
<q-tooltip :delay="1000">{{ item.sender }}</q-tooltip>
</td>
<td width="70px" style="font-size: 0.8em; padding: 0px 4px" align="right">
<div class="truncate" style="width: 62px; max-width: 62px">
{{ item.sendDateAndNowDiff + $t(item.sendDateAndNowDiffUnit) + $t('before') }}
</div>
<q-tooltip :delay="1000">{{ item.sendDate }}</q-tooltip>
</td>
</tr>
</tbody>
</q-markup-table>
</q-card-section>
<UserMessageDialog ref="userMessageDialogRef"> </UserMessageDialog>
<SystemMessageDialog ref="systemMessageDialogRef"></SystemMessageDialog>
</q-card>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { axios, Environment, SessionManager, Tools } from '@/platform';
import UserMessageDialog from './UserMessageDialog.vue';
import SystemMessageDialog from './SystemMessageDialog.vue';
const cardRef = ref();
const cardHeightRef = ref(310);
const tableHeightRef = ref(300 - 30);
const firstColMaxWidthRef = ref(100);
const itemsRef = ref([]);
const loadingRef = ref(false);
const userMessageDialogRef = ref();
const systemMessageDialogRef = ref();
const changeFirstColMaxWidth = () => {
//card width
let width = Math.ceil(cardRef.value.$el.clientWidth);
width = width - 8 /* card-section padding width */ - 12 /* td padding width */ - 150 /* other tds width*/ - 24 /* icon width */;
//min width
width = width >= 100 ? width : 100;
firstColMaxWidthRef.value = width;
};
const refresh = () => {
loadingRef.value = true;
const criteria = {
fieldName: 'receiver',
operator: 'equals',
value: SessionManager.getUser().loginName,
};
const criteriaQuery = encodeURIComponent(Tools.object2Json(criteria));
axios
.get(Environment.apiContextPath('/api/system/notification?page=1&size=10&pageable=true&sortBy=-sendDate&criteria=' + criteriaQuery))
.then((response: any) => {
itemsRef.value = response.data.content || [];
loadingRef.value = false;
})
.catch(() => {
loadingRef.value = false;
});
};
const handle = (item: any) => {
if (item.sender === 'system') {
systemMessageDialogRef.value.open(item);
} else {
userMessageDialogRef.value.open(item.sender);
}
};
refresh();
defineExpose({
refresh,
});
</script>

64
io.sc.platform.core.frontend/src/platform/views/home/MyShortcutMenu.vue

@ -0,0 +1,64 @@
<template>
<div>
<q-card flat bordered>
<q-card-section class="text-secondary px-2 pt-1 pb-0">
<div class="row no-wrap items-center">
<q-icon name="bi-cursor" size="1.5em" />
<span class="text-weight-bolder px-2">{{ $t('home.card.myShortcutmenu.title') }}</span>
<q-space />
<q-btn size="12px" icon="bi-arrow-repeat" flat no-caps padding="2px 6px 2px 6px" :loading="loadingRef" @click="refresh">
<q-tooltip :delay="1000">{{ $t('refresh') }}</q-tooltip>
</q-btn>
<q-btn size="12px" flat no-caps :label="$t('home.card.myShortcutmenu.action.config')" @click="setting(item)" />
</div>
</q-card-section>
<q-card-section class="p-2">
<div class="row">
<div class="col-12">
<div class="q-gutter-x-md q-gutter-y-md">
<template v-for="item in shortcutMenusRef" :key="item.id">
<q-btn stack :to="{ name: item.menu.routeName }">
<q-icon :name="item.menu.icon" :color="item.menuIconColor" />
<div>{{ item.shortcutMenuName }}</div>
</q-btn>
</template>
</div>
</div>
</div>
</q-card-section>
</q-card>
<ShortcutMenuSettingDialog ref="shortcutMenuSettingDialogRef" @close="refresh"></ShortcutMenuSettingDialog>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { axios, Environment, SessionManager, I18nMessageManager, AuthenticationManager, Tools } from '@/platform';
import ShortcutMenuSettingDialog from './ShortcutMenuSettingDialog.vue';
const shortcutMenuSettingDialogRef = ref();
const shortcutMenusRef = ref([]);
const loadingRef = ref(false);
const refresh = () => {
loadingRef.value = true;
axios
.get(Environment.apiContextPath('/api/system/setttings/shortcutmenu/findShortcutMenus'))
.then((response) => {
shortcutMenusRef.value = response.data || [];
loadingRef.value = false;
})
.catch(() => {
loadingRef.value = false;
});
};
const setting = () => {
shortcutMenuSettingDialogRef?.value.open();
};
refresh();
defineExpose({
refresh,
});
</script>

128
io.sc.platform.core.frontend/src/platform/views/home/MyTask.vue

@ -0,0 +1,128 @@
<template>
<div>
<q-card ref="cardRef" flat bordered :style="{ width: '100%', height: cardHeightRef + 'px' }">
<q-resize-observer @resize="changeFirstColMaxWidth" />
<q-card-section class="text-warning px-2 pt-1 pb-0">
<div class="row no-wrap items-center">
<q-icon name="bi-list-ol" size="1.5em" />
<span class="text-weight-bolder px-2">{{ $t('home.card.myTask.title') }}</span>
<q-space />
<q-btn size="12px" icon="bi-arrow-repeat" flat no-caps padding="2px 6px 2px 6px" :loading="loadingRef" @click="refresh">
<q-tooltip :delay="1000">{{ $t('refresh') }}</q-tooltip>
</q-btn>
<q-btn size="12px" icon="bi-justify" flat no-caps padding="2px 6px 2px 6px" :to="{ name: 'route.workbench.myTask' }">
<q-tooltip :delay="1000">{{ $t('home.card.myTask.action.list') }}</q-tooltip>
</q-btn>
</div>
</q-card-section>
<q-card-section style="padding: 0px 6px 0px 6px">
<q-markup-table flat dense separator="none" :style="{ width: '100%', height: tableHeightRef + 'px', overflowY: 'auto' }">
<tbody>
<tr v-for="item in itemsRef" :key="item.id">
<td width="100%" style="padding: 0px 4px">
<a href="javascript:void(0);" :style="{ color: $gc.theme.home.myTaskTextColor }" @click="handle(item)">
<div
class="truncate"
:style="{ width: firstColMaxWidthRef + 'px', maxWidth: firstColMaxWidthRef + 'px' }"
v-html="item.businessDescription"
></div>
</a>
<q-tooltip :delay="1000">{{ item.businessDescription }}</q-tooltip>
</td>
<td width="100px" style="font-size: 0.8em; padding: 0px 4px">
<div class="truncate" style="width: 92px; max-width: 92px">
{{ item.processDefinitionName }}
</div>
<q-tooltip :delay="1000">{{ item.processDefinitionName }}</q-tooltip>
</td>
<td width="60px" style="font-size: 0.8em; padding: 0px 4px">
<div class="truncate" style="width: 52px; max-width: 52px">{{ item.name }}</div>
<q-tooltip :delay="1000">{{ item.name }}</q-tooltip>
</td>
<td width="60px" style="font-size: 0.8em; padding: 0px 4px">
<div class="truncate" style="width: 52px; max-width: 52px">{{ item.previousAssignee }}</div>
<q-tooltip :delay="1000">{{ item.previousAssignee }}</q-tooltip>
</td>
<td width="70px" style="font-size: 0.8em; padding: 0px 4px" align="right">
<div class="truncate" style="width: 62px; max-width: 62px">
{{ item.createTimeAndNowDiff + $t(item.createTimeAndNowDiffUnit) + $t('before') }}
</div>
<q-tooltip :delay="1000">{{ item.createTime }}</q-tooltip>
</td>
</tr>
</tbody>
</q-markup-table>
</q-card-section>
</q-card>
<div style="width: 0px; height: 0px">
<component :is="componentRef" style="width: 1px; height: 1px"></component>
</div>
</div>
</template>
<script setup lang="ts">
import { h, ref, defineAsyncComponent, nextTick } from 'vue';
import { useRouter } from 'vue-router';
import { $t, axios, Environment, ComponentManager, Tools, NotifyManager } from '@/platform';
const emit = defineEmits(['afterRefresh']);
const router = useRouter();
const cardRef = ref();
const cardHeightRef = ref(200);
const tableHeightRef = ref(200 - 30);
const firstColMaxWidthRef = ref(100);
const itemsRef = ref([]);
const loadingRef = ref(false);
const componentRef = ref();
const changeFirstColMaxWidth = () => {
//card width
let width = Math.ceil(cardRef.value.$el.clientWidth);
width = width - 8 /* card-section padding width */ - 12 /* td padding width */ - 290 /* other tds width*/;
//min width
width = width >= 100 ? width : 100;
firstColMaxWidthRef.value = width;
};
const refresh = () => {
loadingRef.value = true;
axios
.get(Environment.apiContextPath('/api/flowable/process/query/myTask'))
.then((response: any) => {
itemsRef.value = response.data.content || [];
loadingRef.value = false;
})
.catch(() => {
loadingRef.value = false;
});
emit('afterRefresh');
};
const handle = async (item: any) => {
if (item.taskHandFrontendModelName && item.taskHandFrontendComponentName) {
//
componentRef.value = null;
//
nextTick(() => {
const component = defineAsyncComponent(ComponentManager.getRemoteComponent(item.taskHandFrontendModelName, item.taskHandFrontendComponentName));
componentRef.value = h(component, {
action: 'process',
task: item,
properties: Tools.json2Object(item.taskHandFrontendComponentProperties),
onClose: refresh,
});
});
} else if (item.taskHandFrontendRouteName) {
router.push({ name: item.taskHandFrontendRouteName });
} else {
NotifyManager.warn($t('home.card.myTask.action.notSetting.tip'));
}
};
refresh();
defineExpose({
refresh,
});
</script>

40
io.sc.platform.core.frontend/src/platform/views/home/ShortcutMenu.vue

@ -1,40 +0,0 @@
<template>
<q-card flat bordered>
<q-card-section class="text-secondary px-2 pt-1 pb-0">
<div class="row no-wrap items-center">
<q-icon name="bi-cursor" size="1.5em" />
<span class="text-weight-bolder px-2">{{ $t('home.card.shortcutmenu.title') }}</span>
<q-space />
<q-btn size="12px" flat no-caps :label="$t('home.card.shortcutmenu.action.config')" :to="{ name: 'route.system.settings' }" />
</div>
</q-card-section>
<q-card-section class="p-2">
<div class="row">
<div class="col-12">
<div class="q-gutter-x-md q-gutter-y-md">
<template v-for="item in shortcutMenusRef" :key="item.id">
<q-btn stack :to="{ name: item.menu.routeName }">
<q-icon :name="item.menu.icon" :color="item.menuIconColor" />
<div>{{ item.shortcutMenuName }}</div>
</q-btn>
</template>
</div>
</div>
</div>
</q-card-section>
</q-card>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { axios, Environment, SessionManager, I18nMessageManager, AuthenticationManager, Tools } from '@/platform';
const shortcutMenusRef = ref([]);
const findShortcutMenus = () => {
axios.get(Environment.apiContextPath('/api/system/setttings/shortcutmenu/findShortcutMenus')).then((response) => {
shortcutMenusRef.value = response.data || [];
});
};
findShortcutMenus();
</script>

168
io.sc.platform.core.frontend/src/platform/views/home/ShortcutMenuSettingDialog.vue

@ -0,0 +1,168 @@
<template>
<w-dialog
ref="dialogRef"
:title="$t('settings.shortcutMenus')"
:can-maximize="false"
:maximized="false"
body-padding="4px 4px 4px 4px"
width="60%"
@hide="
() => {
emit('close');
}
"
>
<w-grid
:title="$t('settings.shortcutMenus')"
:height="400"
:config-button="true"
dnd-mode="server"
db-click-operation="edit"
selection="multiple"
:checkbox-selection="true"
:data-url="Environment.apiContextPath('/api/system/setttings/shortcutmenu')"
:sort-by="['order']"
:query-form-cols-num="2"
:toolbar-configure="{ noIcon: false }"
:toolbar-actions="['refresh', 'separator', 'add', 'edit', 'remove', 'separator', 'view', 'separator', 'export']"
:columns="[
{ width: 200, name: 'shortcutMenuName', label: $t('settings.shortcutMenus.entity.shortcutMenuName'), sortable: false },
{
width: 100,
name: 'menuIconColor',
label: $t('settings.shortcutMenus.entity.menuIconColor'),
sortable: false,
format: (value, row) => {
return '<span class=\'text-' + value + '\'>' + value + '</span>';
},
},
{ width: 70, name: 'order', label: $t('order'), sortable: false, align: 'right' },
{ width: 300, name: 'menuCode', label: $t('code') },
{
width: 300,
name: 'menuName',
label: $t('name'),
format: (value, row) => {
return $t(row.menuCode);
},
},
]"
:editor="{
dialog: {
width: '600px',
},
form: {
colsNum: 1,
fields: [
{
name: 'menuId',
label: $t('menu'),
type: 'w-grid-select',
requiredIf: true,
selectableIf: (args: any) => {
if (args.row.type === 'GROUP') {
return false;
} else {
return true;
}
},
displayValue: (args: any) => {
const data = args.data;
return $t(data.titleI18nKey);
},
onUpdateValue: (args: any) => {
if (args.data) {
args.form.setFieldValue('menuCode', args.data.name);
args.form.setFieldValue('shortcutMenuName', $t(args.data.name));
}
},
grid: {
title: $t('system.menu.grid.title'),
height: 300,
denseBody: true,
hideBottom: true,
configButton: true,
checkboxSelection: false,
tree: true,
treeIcon: (row: any) => {
if (row.type === 'SEPARATOR') {
return { name: 'bi-dash-lg' };
} else {
return { name: row.icon };
}
},
dataUrl: Environment.apiContextPath('/api/system/menu/listAllMenusByUser'),
pageable: false,
sortBy: ['order'],
toolbarConfigure: { noIcon: false },
toolbarActions: ['refresh', 'expand'],
columns: [
{
width: '100%',
name: 'titleI18nKey',
label: $t('name'),
sortable: false,
format: (value, row) => {
if (row.type === 'SEPARATOR') {
return `<hr style='width:100px'/>`;
} else if (row.type === 'ROUTE_ACTION') {
return $t(row.i18nKey);
} else {
return $t(value);
}
},
},
{ width: 80, name: 'type', label: $t('type'), sortable: false, format: Formater.menuType() },
{ width: 80, name: 'order', label: $t('order'), align: 'right', sortable: false },
{ width: 80, name: 'enable', label: $t('status'), sortable: false, format: Formater.enableTag() },
],
},
},
{ name: 'menuCode', label: $t('menuCode'), type: 'w-text', showIf: false },
{ name: 'shortcutMenuName', label: $t('settings.shortcutMenus.entity.shortcutMenuName'), type: 'w-text' },
{
name: 'menuIconColor',
label: $t('settings.shortcutMenus.entity.shortcutMenuName'),
type: 'w-color-input-palette',
defaultValue: 'primary',
},
{ name: 'order', label: $t('order'), type: 'w-integer' },
],
},
}"
:viewer="{
panel: {
columnNum: 1,
fields: [
{ name: 'id', label: $t('id') },
{ name: 'shortcutMenuName', label: $t('settings.shortcutMenus.entity.shortcutMenuName') },
{ name: 'order', label: $t('order'), type: 'w-integer' },
{ name: 'menuCode', label: $t('menuCode') },
],
},
}"
>
</w-grid>
</w-dialog>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { Environment, Formater } from '@/platform';
const emit = defineEmits(['close']);
const dialogRef = ref();
const open = (item: any) => {
dialogRef.value.show();
};
const close = () => {
dialogRef.value.hide();
};
defineExpose({
open,
close,
});
</script>

45
io.sc.platform.core.frontend/src/platform/views/home/SystemMessageDialog.vue

@ -0,0 +1,45 @@
<template>
<w-dialog
ref="dialogRef"
:title="$t('home.card.message.systemMessageDialog.title')"
:can-maximize="false"
:maximized="false"
body-padding="2px 2px 2px 2px"
width="60%"
@hide="
() => {
emit('close');
}
"
>
<div class="p-2">
<div class="text-h6 p-2">{{ itemRef.title }}</div>
<div class="p-2" v-html="itemRef.content"></div>
</div>
<div style="height: 20px"></div>
</w-dialog>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { axios, Environment, SessionManager } from '@/platform';
const emit = defineEmits(['close']);
const dialogRef = ref();
const itemRef = ref();
const open = (item: any) => {
itemRef.value = item;
dialogRef.value.show();
axios.post(Environment.apiContextPath('api/system/notification/updateReceiveDate/' + item.id));
};
const close = () => {
dialogRef.value.hide();
};
defineExpose({
open,
close,
});
</script>

94
io.sc.platform.core.frontend/src/platform/views/home/Task.vue

@ -1,94 +0,0 @@
<template>
<q-card ref="cardRef" flat bordered :style="{ width: '100%', height: cardHeightRef + 'px' }">
<q-resize-observer @resize="changeFirstColMaxWidth" />
<q-card-section class="text-warning px-2 pt-1 pb-0">
<div class="row no-wrap items-center">
<q-icon name="bi-list-ol" size="1.5em" />
<span class="text-weight-bolder px-2">{{ $t('home.card.task.title') }}</span>
<q-space />
<q-btn size="12px" icon="bi-arrow-repeat" flat no-caps padding="2px 6px 2px 6px" :title="$t('refresh')" :loading="loadingRef" @click="refresh" />
<q-btn
size="12px"
icon="bi-justify"
flat
no-caps
padding="2px 6px 2px 6px"
:title="$t('home.card.task.action.list')"
:to="{ name: 'route.workbench.myTask' }"
/>
</div>
</q-card-section>
<q-card-section class="px-0 py-0">
<q-markup-table flat dense separator="none" :style="{ width: '100%', height: tableHeightRef + 'px', overflowY: 'auto' }">
<tbody>
<tr v-for="item in itemsRef" :key="item.id">
<td width="100%" :title="item.businessDescription" class="truncate" :style="{ maxWidth: firstColMaxWidthRef + 'px', padding: '0px 4px' }">
<w-html-a :label="item.businessDescription" @click="handleTask(item)"></w-html-a>
</td>
<td width="100px" :title="item.processDefinitionName" class="smallFont truncate" style="max-width: 100px; font-size: 0.8em; padding: 0px 4px">
{{ item.processDefinitionName }}
</td>
<td width="80px" :title="item.name" class="smallFont truncate" style="max-width: 80px; font-size: 0.8em; padding: 0px 4px">
{{ item.name }}
</td>
<td width="80px" :title="item.previousAssignee" class="smallFont truncate" style="max-width: 80px; font-size: 0.8em; padding: 0px 4px">
{{ item.previousAssignee }}
</td>
<td width="80px" :title="item.createTime" class="smallFont truncate" style="max-width: 80px; font-size: 0.8em; padding: 0px 4px" align="right">
{{ item.createTimeAndNowDiff + $t(item.createTimeAndNowDiffUnit) + $t('before') }}
</td>
</tr>
</tbody>
</q-markup-table>
</q-card-section>
<component :is="componentRef"></component>
</q-card>
</template>
<script setup lang="ts">
import { h, ref, defineAsyncComponent, nextTick } from 'vue';
import { axios, Environment, ComponentManager, Tools } from '@/platform';
const cardRef = ref();
const cardHeightRef = ref(200);
const tableHeightRef = ref(200 - 30);
const firstColMaxWidthRef = ref(100);
const itemsRef = ref([]);
const loadingRef = ref(false);
const componentRef = ref();
const changeFirstColMaxWidth = () => {
let width = Math.ceil(cardRef.value.$el.clientWidth);
width = width - 340;
firstColMaxWidthRef.value = width > 100 ? width : 100;
};
const refresh = () => {
loadingRef.value = true;
axios
.get(Environment.apiContextPath('/api/flowable/process/query/task'))
.then((response: any) => {
itemsRef.value = response.data.content || [];
loadingRef.value = false;
})
.catch(() => {
loadingRef.value = false;
});
};
const handleTask = async (item: any) => {
//
componentRef.value = null;
//
nextTick(() => {
const component = defineAsyncComponent(ComponentManager.getRemoteComponent(item.taskHandFrontendModelName, item.taskHandFrontendComponentName));
const properties = Tools.mergeObject(
{ taskId: item.id, businessKey: item.businessKey, action: 'process' },
Tools.json2Object(item.taskHandFrontendComponentProperties),
);
componentRef.value = h(component, properties);
});
};
refresh();
</script>

12
io.sc.platform.core.frontend/src/platform/views/home/ChatDialog.vue → io.sc.platform.core.frontend/src/platform/views/home/UserMessageDialog.vue

@ -6,6 +6,11 @@
:maximized="false"
body-padding="2px 2px 2px 2px"
width="60%"
@hide="
() => {
emit('close');
}
"
>
<w-splitter :model-value="400" unit="px" separator-style="height: 1px;" style="width: 100%; height: 100%" horizontal>
<template #before>
@ -13,7 +18,7 @@
<template v-for="message in messagesRef" :key="message.id">
<template v-if="message.sender === senderRef"
><!-- 他人发送的消息 -->
<q-chat-message :name="senderRef" :text-html="true" :text="[message.content]" bg-color="blue" text-color="white" size="6">
<q-chat-message :name="message.sender" :text-html="true" :text="[message.content]" stamp="" bg-color="blue" text-color="white" size="6">
<template #avatar>
<q-icon name="bi-person-circle" size="32px" color="blue" right class="p-2" />
</template>
@ -21,7 +26,7 @@
</template>
<template v-else>
<!-- 我发送的消息 -->
<q-chat-message sent :text-html="true" :text="[message.content]" bg-color="green" text-color="white" size="6">
<q-chat-message sent :name="message.sender" :text-html="true" :text="[message.content]" bg-color="green" text-color="white" size="6">
<template #avatar>
<q-icon name="bi-person-circle" size="32px" color="green" left class="p-2" />
</template>
@ -53,6 +58,8 @@ import { useQuasar } from 'quasar';
import { axios, Environment, SessionManager } from '@/platform';
const imeRef = ref(false); //
const emit = defineEmits(['close']);
const $q = useQuasar();
const dialogRef = ref();
const senderRef = ref();
@ -112,7 +119,6 @@ const doSendMessage = async (message: string) => {
receiver: senderRef.value,
content: message,
};
console.log(data);
axios.post(Environment.apiContextPath('/api/system/notification'), data).then(() => {
//

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

@ -1,6 +1,6 @@
{
"name": "platform-core",
"version": "8.2.92",
"version": "8.2.100",
"description": "前端核心包,用于快速构建前端的脚手架",
"private": false,
"keywords": [],
@ -111,7 +111,7 @@
"mockjs": "1.1.0",
"node-sql-parser": "5.3.6",
"pinia": "2.3.0",
"platform-core": "8.2.92",
"platform-core": "8.2.100",
"quasar": "2.17.6",
"sort-array": "5.0.0",
"svg-path-commander": "2.1.7",

29
io.sc.platform.core.frontend/template-project/src/views/likm/Form.vue

@ -1,5 +1,33 @@
<template>
<div>
<w-form
ref="testFormRef"
:fields="[
{
label: '发起时间',
name: 'startTime',
type: 'w-date-range',
displayMode: 'dual',
},
{
label: 'ddddd',
name: 'ddddd',
type: 'w-text',
},
]"
></w-form>
<br />
<q-btn
label="获取组件值"
@click="
() => {
console.info('=========', testFormRef.getData());
}
"
></q-btn>
<br />
<br />
<br />
<!-- <br />
<br />
<br />
@ -490,6 +518,7 @@ import { ref, reactive, computed } from 'vue';
import { Environment, Formater, $t } from '@/platform';
const mode = ref('criteria');
const testFormRef = ref();
const booleanModelValue = ref(false);
const string1ModelValue = ref('');
const string2ModelValue = ref(`name = '1' and age <> 22 and ( sex = '2' or xl NOT IN ('1','2') or ( not ( birthday = '2024-09-25' and name LIKE '%222%' ) ) )`);

27
io.sc.platform.core.frontend/template-project/src/views/likm/Grid.vue

@ -13,15 +13,32 @@
:sort-by="['name']"
:query-form-cols-num="3"
:query-form-fields="[
{ name: 'code', label: $t('code'), type: 'w-text' },
{ name: 'name', label: $t('name'), type: 'w-text' },
{ name: 'enable', label: $t('isEnable'), type: 'w-select', options: Options.yesNo() },
{ name: 'lastModifyDate', label: $t('name'), type: 'w-date-range', displayMode: 'dual', colSpan: 2 },
{ name: 'code', label: $t('code'), type: 'w-text', rules: [FormValidators.lengthRange(0, 3)] },
]"
:toolbar-configure="{ noIcon: false }"
:toolbar-actions="['query', 'refresh', 'separator', 'add', 'clone', 'edit', 'remove', 'separator', 'view', 'separator', 'export']"
:toolbar-actions="[
'query',
'reset',
'separator',
'add',
{
label: 'test',
click: (args) => {
console.info('dddddd=====', args.grid.getQueryForm().getData());
},
},
'edit',
'remove',
'separator',
'view',
'separator',
'export',
]"
:columns="[
{ width: 200, name: 'code', label: $t('code') },
{ width: '100%', name: 'name', label: $t('name') },
{ name: 'lastModifyDate', label: $t('lastModifyDate'), format: Formater.none() },
{ width: 70, name: 'enable', label: $t('status'), align: 'center', format: Formater.enableTag() },
{
width: 200,
@ -128,7 +145,7 @@
</template>
<script setup lang="ts">
import { ref, onActivated } from 'vue';
import { Environment, axios, Options, Formater, SessionManager } from '@/platform';
import { Environment, axios, Options, Formater, SessionManager, FormValidators } from '@/platform';
import SelectUserGrid from './SelectUserGrid.vue';
import SelectMenuTreeGrid from './SelectMenuTreeGrid.vue';

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

@ -89,6 +89,7 @@ module.exports = {
use: [
{
loader: 'vue-loader',
options: { hotReload: false },
},
],
},

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

@ -89,6 +89,7 @@ module.exports = {
use: [
{
loader: 'vue-loader',
options: { hotReload: false },
},
],
},

1
io.sc.platform.core/src/main/resources/META-INF/platform/plugins/application-properties.json

@ -19,6 +19,7 @@
"#application.audit-log-mode = none",
"#application.audit-log-mode = log",
"#application.audit-log-mode = database",
"application.enable-change-role = false",
"application.is-multi-corporation-mode = false"
]
},

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

@ -28,7 +28,7 @@
"vuepress": "2.0.0-rc.15"
},
"dependencies": {
"platform-core": "8.2.92",
"platform-core": "8.2.100",
"quasar": "2.17.6",
"vue": "3.5.13",
"vue-i18n": "11.0.1"

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

@ -112,7 +112,7 @@
"node-sql-parser": "5.3.6",
"pinia": "2.3.0",
"pinia-undo": "0.2.4",
"platform-core": "8.2.92",
"platform-core": "8.2.100",
"quasar": "2.17.6",
"sort-array": "5.0.0",
"svg-path-commander": "2.1.7",

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

@ -89,6 +89,7 @@ module.exports = {
use: [
{
loader: 'vue-loader',
options: { hotReload: false },
},
],
},

1
io.sc.platform.flowable/build.gradle

@ -2,6 +2,7 @@ dependencies {
api(
project(":io.sc.platform.communication"),
project(":io.sc.platform.system"),
project(":io.sc.platform.lcdp"),
"org.flowable:flowable-spring-boot-starter-process:${flowable_version}",
"org.flowable:flowable-json-converter:${flowable_version}",

9
io.sc.platform.flowable/src/main/java/io/sc/platform/flowable/api/ProcessVo.java

@ -15,6 +15,7 @@ public class ProcessVo extends CorporationAuditorVo {
private ProcessStatus status;
private Boolean canClaimTask;
private String businessDescriptionSql;
private String taskHandFrontendRouteName;
private String taskHandFrontendModelName;
private String taskHandFrontendComponentName;
private String taskHandFrontendComponentProperties;
@ -107,6 +108,14 @@ public class ProcessVo extends CorporationAuditorVo {
this.businessDescriptionSql = businessDescriptionSql;
}
public String getTaskHandFrontendRouteName() {
return taskHandFrontendRouteName;
}
public void setTaskHandFrontendRouteName(String taskHandFrontendRouteName) {
this.taskHandFrontendRouteName = taskHandFrontendRouteName;
}
public String getTaskHandFrontendModelName() {
return taskHandFrontendModelName;
}

15
io.sc.platform.flowable/src/main/java/io/sc/platform/flowable/controller/ProcessQueryWebController.java

@ -54,12 +54,21 @@ public class ProcessQueryWebController {
return service.queryProcessTasks(queryParameter);
}
@GetMapping("doneTask")
@GetMapping("myTask")
public Page<ProcessTaskWrapper> myTaskQuery(QueryParameter queryParameter) throws Exception {
Equals eq =new Equals();
eq.setFieldName("assignee");
eq.setValue(SecurityUtil.getLoginName());
queryParameter.addCriteria(eq);
return service.queryProcessTasks(queryParameter);
}
@GetMapping("myDoneTask")
public Page<ProcessTaskWrapper> doneTask(QueryParameter queryParameter) throws Exception {
return service.queryDoneProcessTasks(queryParameter);
}
@GetMapping("finishedTask")
@GetMapping("myFinishedTask")
public Page<ProcessTaskWrapper> finishedTask(QueryParameter queryParameter) throws Exception {
return service.queryFinishedProcessTasks(queryParameter);
}
@ -81,6 +90,4 @@ public class ProcessQueryWebController {
) throws Exception{
return service.queryCandidate(taskId,activityId);
}
}

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

@ -77,18 +77,23 @@ public class ProcessEntity extends CorporationAuditorEntity<ProcessVo> {
private Boolean canClaimTask;
// 任务描述SQL语句,用于生成带业务信息的任务描述
@Column(name="BUSINESS_DESC_SQL_")
@Column(name="BUSINESS_DESC_SQL_", length=1024)
@Size(max=1024)
private String businessDescriptionSql;
// 任务处理前端路由名称
@Column(name="TASK_HAND_FE_ROUTE_NAME_", length=1024)
@Size(max=1024)
private String taskHandFrontendRouteName;
// 任务处理前端组件的模块名
@Column(name="TASK_HAND_FE_MODEL_NAME_")
@Size(max=255)
@Column(name="TASK_HAND_FE_MODEL_NAME_", length=1024)
@Size(max=1024)
private String taskHandFrontendModelName;
// 任务处理前端组件的组件名
@Column(name="TASK_HAND_FE_COMP_NAME_")
@Size(max=255)
@Column(name="TASK_HAND_FE_COMP_NAME_", length=1024)
@Size(max=1024)
private String taskHandFrontendComponentName;
// 任务处理前端组件的组件属性
@ -110,6 +115,7 @@ public class ProcessEntity extends CorporationAuditorEntity<ProcessVo> {
vo.setStatus(this.getStatus());
vo.setCanClaimTask(this.getCanClaimTask());
vo.setBusinessDescriptionSql(this.getBusinessDescriptionSql());
vo.setTaskHandFrontendRouteName(this.getTaskHandFrontendRouteName());
vo.setTaskHandFrontendModelName(this.getTaskHandFrontendModelName());
vo.setTaskHandFrontendComponentName(this.getTaskHandFrontendComponentName());
vo.setTaskHandFrontendComponentProperties(this.getTaskHandFrontendComponentProperties());
@ -204,6 +210,14 @@ public class ProcessEntity extends CorporationAuditorEntity<ProcessVo> {
this.businessDescriptionSql = businessDescriptionSql;
}
public String getTaskHandFrontendRouteName() {
return taskHandFrontendRouteName;
}
public void setTaskHandFrontendRouteName(String taskHandFrontendRouteName) {
this.taskHandFrontendRouteName = taskHandFrontendRouteName;
}
public String getTaskHandFrontendModelName() {
return taskHandFrontendModelName;
}

2
io.sc.platform.flowable/src/main/java/io/sc/platform/flowable/service/impl/AssigneeQueryServiceImpl.java

@ -84,7 +84,7 @@ public class AssigneeQueryServiceImpl implements AssigneeQueryService {
for(OrgEntity org : orgs){
orgAndParentOrgs.add(org);
OrgEntity parentOrg =org.getParent();
while(parentOrg!=null && !StringUtils.hasText(parentOrg.getId())){
while(parentOrg!=null){
orgAndParentOrgs.add(parentOrg);
parentOrg =parentOrg.getParent();
}

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

@ -142,6 +142,7 @@ public class ProcessOperationServiceImpl implements ProcessOperationService {
//如果需要,自动完成第一个任务
if(autoCompleteFirstTask) {
newTask.setAssignee((String)transientVariables.get(FrameworkVariableNames.FIRST_TASK_ASSIGNEE));
completeTask(newTask.getId(),variables,transientVariables);
return processInstance;
}

374
io.sc.platform.flowable/src/main/java/io/sc/platform/flowable/service/impl/ProcessQueryServiceImpl.java

@ -6,6 +6,7 @@ import io.sc.platform.flowable.service.ProcessEntityService;
import io.sc.platform.flowable.service.ProcessQueryService;
import io.sc.platform.flowable.support.*;
import io.sc.platform.jdbc.sql.dialect.Dialect;
import io.sc.platform.lcdp.form.service.JdbcTemplateService;
import io.sc.platform.orm.service.support.QueryParameter;
import io.sc.platform.orm.service.support.QueryResult;
import io.sc.platform.orm.service.support.criteria.Criteria;
@ -13,6 +14,7 @@ import io.sc.platform.orm.service.support.criteria.impl.Contains;
import io.sc.platform.orm.service.support.criteria.impl.Equals;
import io.sc.platform.security.util.SecurityUtil;
import io.sc.platform.util.CollectionUtil;
import io.sc.platform.util.DateUtil;
import io.sc.platform.util.StringUtil;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.EndEvent;
@ -22,6 +24,7 @@ import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.runtime.ProcessInstanceQuery;
@ -49,6 +52,7 @@ public class ProcessQueryServiceImpl implements ProcessQueryService {
@Autowired private ProcessEntityService processEntityService;
@Autowired private HistoryService historyService;
@Autowired private RuntimeService runtimeService;
@Autowired private JdbcTemplateService jdbcTemplateService;
@Autowired
@Qualifier("io.sc.platform.flowable.service.impl.AssigneeQueryServiceImpl")
@ -360,6 +364,7 @@ public class ProcessQueryServiceImpl implements ProcessQueryService {
for(String processInstanceId : processInstanceAndBusinessMap.keySet()){
BusinessKeyAndDescription businessKeyAndDescription =processInstanceAndBusinessMap.get(processInstanceId);
if(businessKeyAndDescription!=null){
businessKeyAndDescription.setTaskHandFrontendRouteName(processEntity.getTaskHandFrontendRouteName());
businessKeyAndDescription.setTaskHandFrontendModelName(processEntity.getTaskHandFrontendModelName());
businessKeyAndDescription.setTaskHandFrontendComponentName(processEntity.getTaskHandFrontendComponentName());
businessKeyAndDescription.setTaskHandFrontendComponentProperties(processEntity.getTaskHandFrontendComponentProperties());
@ -409,6 +414,7 @@ public class ProcessQueryServiceImpl implements ProcessQueryService {
wrapper.setBusinessDescription(businessKeyAndDescription.getBusinessDescription());
wrapper.setProcessDefinitionName(businessKeyAndDescription.getProcessDefinitionName());
wrapper.setProcessDefinitionVersion(businessKeyAndDescription.getProcessDefinitionVersion());
wrapper.setTaskHandFrontendRouteName(businessKeyAndDescription.getTaskHandFrontendRouteName());
wrapper.setTaskHandFrontendModelName(businessKeyAndDescription.getTaskHandFrontendModelName());
wrapper.setTaskHandFrontendComponentName(businessKeyAndDescription.getTaskHandFrontendComponentName());
wrapper.setTaskHandFrontendComponentProperties(businessKeyAndDescription.getTaskHandFrontendComponentProperties());
@ -419,34 +425,358 @@ public class ProcessQueryServiceImpl implements ProcessQueryService {
@Override
public Page<ProcessTaskWrapper> queryDoneProcessTasks(QueryParameter queryParameter) throws Exception {
String sql = "select * from (" +
" select * from ( " +
" select " +
" e.business_key_ as BUSINESS_ID," +
" ht.proc_inst_id_ as PROC_INST_ID," +
" rp.name_ as PROC_NAME," +
" t.name_ as CURR_TASK_NAME," +
" t.assignee_ || '(' || u.username_ || ')' as CURR_TASK_ASSIGNEE, " +
" t.description_ as CURR_TASK_DESC,ht.start_time_ as START_TIME, " +
" ht.end_time_ as END_TIME, " +
" row_number() over(partition by ht.proc_inst_id_ order by ht.end_time_ desc) as rk " +
" from ACT_HI_TASKINST ht " +
" JOIN ACT_RU_TASK T ON T.PROC_INST_ID_ = HT.PROC_INST_ID_ " +
" JOIN ACT_RU_EXECUTION e on t.proc_inst_id_ = e.ID_ " +
" left join ACT_RE_PROCDEF rp on ht.proc_def_id_ = rp.id_ " +
" left join sys_user u on t.assignee_ = u.LOGINNAME_ " +
" where ht.ASSIGNEE_ =? and t.assignee_ <> ? and ht.end_time_ is not null" +
" ) tmp where tmp.rk = 1" +
" ) t ";
String loginName =SecurityUtil.getLoginName();
List<Map<String, Object>> list =jdbcTemplate.queryForList(sql,loginName,loginName);
String taskSql ="" +
"select * from (\n" +
" select * from (\n" +
" select\n" +
" T.ID_ as id,\n" +
" T.NAME_ as name,\n" +
" T.DESCRIPTION_ as description,\n" +
" T.PRIORITY_ as priority,\n" +
" T.OWNER_ as owner,\n" +
" T.ASSIGNEE_ as assignee,\n" +
" T.PROC_INST_ID_ as processInstanceId,\n" +
" T.EXECUTION_ID_ as executionId,\n" +
" T.PROC_DEF_ID_ as processDefinitionId,\n" +
" T.SCOPE_ID_ as scopeId,\n" +
" T.SUB_SCOPE_ID_ as subScopeId,\n" +
" T.SCOPE_TYPE_ as scopeType,\n" +
" T.SCOPE_DEFINITION_ID_ as scopeDefinitionId,\n" +
" T.CREATE_TIME_ as createTime,\n" +
" T.TASK_DEF_KEY_ as taskDefinitionKey,\n" +
" T.DUE_DATE_ as dueDate,\n" +
" T.CATEGORY_ as category,\n" +
" T.PARENT_TASK_ID_ as parentTaskId,\n" +
" T.TENANT_ID_ as tenantId,\n" +
" T.FORM_KEY_ as formKey,\n" +
" T.CLAIM_TIME_ as claimTime,\n" +
" \n" +
" RP.NAME_ as processDefinitionName,\n" +
" RP.VERSION_ as processDefinitionVersion,\n" +
" \n" +
" E.BUSINESS_KEY_ \t as businessKey,\n" +
" \n" +
" HT.START_TIME_ as START_TIME,\n" +
" HT.END_TIME_ as END_TIME,\n" +
" \n" +
" row_number() over(partition by HT.PROC_INST_ID_ order by HT.END_TIME_ desc) as RK\n" +
" from ACT_HI_TASKINST HT\n" +
" join ACT_RU_TASK T on T.PROC_INST_ID_ = HT.PROC_INST_ID_\n" +
" join ACT_RU_EXECUTION E on T.PROC_INST_ID_ = E.ID_\n" +
" left join ACT_RE_PROCDEF RP on HT.PROC_DEF_ID_ = RP.ID_\n" +
" left join SYS_USER U on T.ASSIGNEE_ = U.LOGINNAME_\n" +
" where \n" +
" HT.ASSIGNEE_ ='" + loginName + "' \n" +
" and T.ASSIGNEE_ <> '" + loginName + "' \n" +
" and HT.END_TIME_ is not null\n" +
" ) TMP where TMP.RK = 1\n" +
") t";
Map<String,Class<?>> fieldTypeMap =new HashMap<>();
fieldTypeMap.put("processDefinitionId",String.class);
fieldTypeMap.put("processInstanceId",String.class);
fieldTypeMap.put("businessKey",String.class);
fieldTypeMap.put("assignee",String.class);
fieldTypeMap.put("taskDefinitionKey",String.class);
fieldTypeMap.put("description",String.class);
Map<String, Object> page =jdbcTemplateService.listBySql(queryParameter,taskSql,fieldTypeMap,new ProcessTaskWrapperMapper());
return null;
List<ProcessTaskWrapper> wrappers =new ArrayList<>();
Set<String> processIntanceIds =new HashSet<>();
Map<String,Map<String, BusinessKeyAndDescription>> processDefineAndInstanceAndBusinessMap =new HashMap<>();
if(page==null || page.isEmpty()){
return QueryResult.emptyPage();
}
List<ProcessTaskWrapper> items =(List<ProcessTaskWrapper>)page.get("content");
if(items==null || items.isEmpty()){
return QueryResult.emptyPage();
}
for(ProcessTaskWrapper item : items){
ProcessTaskWrapper wrapper =item;
wrappers.add(wrapper);
Map<String,BusinessKeyAndDescription> processInstanceAndBusinessMap =processDefineAndInstanceAndBusinessMap.get(wrapper.getProcessDefinitionId());
if(processInstanceAndBusinessMap==null){
processInstanceAndBusinessMap =new HashMap<>();
processDefineAndInstanceAndBusinessMap.put(wrapper.getProcessDefinitionId(),processInstanceAndBusinessMap);
}
processInstanceAndBusinessMap.put(wrapper.getProcessInstanceId(),new BusinessKeyAndDescription());
processIntanceIds.add(wrapper.getProcessInstanceId());
}
// 建立流程实例和业务Key的关系
List<ProcessInstance> processInstances =runtimeService.createProcessInstanceQuery().processInstanceIds(processIntanceIds).list();
if(CollectionUtil.hasElements(processInstances)) {
for (ProcessInstance processInstance : processInstances) {
Map<String,BusinessKeyAndDescription> processInstanceAndBusinessMap =processDefineAndInstanceAndBusinessMap.get(processInstance.getProcessDefinitionId());
if(processInstanceAndBusinessMap!=null){
BusinessKeyAndDescription businessKeyAndDescription =processInstanceAndBusinessMap.get(processInstance.getId());
if(businessKeyAndDescription!=null){
businessKeyAndDescription.setProcessDefinitionName(processInstance.getProcessDefinitionName());
businessKeyAndDescription.setProcessDefinitionVersion(processInstance.getProcessDefinitionVersion());
businessKeyAndDescription.setBusinessKey(processInstance.getBusinessKey());
businessKeyAndDescription.setBusinessDescription(processInstance.getProcessDefinitionName());
List<HistoricTaskInstance> historicTaskInstances =historyService.createHistoricTaskInstanceQuery().processInstanceId(processInstance.getId()).finished().orderByHistoricTaskInstanceEndTime().desc().list();
if(CollectionUtil.hasElements(historicTaskInstances)){
businessKeyAndDescription.setPreviousAssignee(historicTaskInstances.get(0).getAssignee());
}
}
}
}
}
for(String processDefineId : processDefineAndInstanceAndBusinessMap.keySet()) {
Map<String,BusinessKeyAndDescription> processInstanceAndBusinessMap =processDefineAndInstanceAndBusinessMap.get(processDefineId);
if(processInstanceAndBusinessMap==null || processInstanceAndBusinessMap.size()<=0){
continue;
}
ProcessEntity processEntity =processEntityService.getRepository().findByDeployedId(processDefineId);
for(String processInstanceId : processInstanceAndBusinessMap.keySet()){
BusinessKeyAndDescription businessKeyAndDescription =processInstanceAndBusinessMap.get(processInstanceId);
if(businessKeyAndDescription!=null){
businessKeyAndDescription.setTaskHandFrontendRouteName(processEntity.getTaskHandFrontendRouteName());
businessKeyAndDescription.setTaskHandFrontendModelName(processEntity.getTaskHandFrontendModelName());
businessKeyAndDescription.setTaskHandFrontendComponentName(processEntity.getTaskHandFrontendComponentName());
businessKeyAndDescription.setTaskHandFrontendComponentProperties(processEntity.getTaskHandFrontendComponentProperties());
}
}
if(processEntity==null){
continue;
}
String sql =processEntity.getBusinessDescriptionSql();
if(!StringUtils.hasText(sql)){
continue;
}
Set<String> businessKeys =new HashSet<>();
for(Map.Entry<String,BusinessKeyAndDescription> entry: processInstanceAndBusinessMap.entrySet()){
businessKeys.add(entry.getValue().getBusinessKey());
}
if(!CollectionUtil.hasElements(businessKeys)){
continue;
}
Map<String,Object> variables =new HashMap<>();
variables.put("bussinessKeys","'" + StringUtil.combine("','",businessKeys) + "'");
sql =StringUtil.format(sql,variables);
List<Map<String,Object>> result =jdbcTemplate.queryForList(sql);
if(!CollectionUtil.hasElements(result)){
continue;
}
for(Map<String,Object> row : result){
String bussinessKey =row.get("BUSSINESS_KEY").toString();
String bussinessDescription =row.get("BUSSINESS_DESCRIPTION").toString();
for(String processInstanceId : processInstanceAndBusinessMap.keySet()){
BusinessKeyAndDescription businessKeyAndDescription =processInstanceAndBusinessMap.get(processInstanceId);
if(businessKeyAndDescription.getBusinessKey().equals(bussinessKey)){
businessKeyAndDescription.setBusinessDescription(bussinessDescription);
break;
}
}
}
}
//合并
for(ProcessTaskWrapper wrapper : wrappers){
Map<String,BusinessKeyAndDescription> processInstanceAndBusinessMap =processDefineAndInstanceAndBusinessMap.get(wrapper.getProcessDefinitionId());
BusinessKeyAndDescription businessKeyAndDescription =processInstanceAndBusinessMap.get(wrapper.getProcessInstanceId());
wrapper.setPreviousAssignee(businessKeyAndDescription.getPreviousAssignee());
wrapper.setBusinessKey(businessKeyAndDescription.getBusinessKey());
wrapper.setBusinessDescription(businessKeyAndDescription.getBusinessDescription());
wrapper.setProcessDefinitionName(businessKeyAndDescription.getProcessDefinitionName());
wrapper.setProcessDefinitionVersion(businessKeyAndDescription.getProcessDefinitionVersion());
wrapper.setTaskHandFrontendRouteName(businessKeyAndDescription.getTaskHandFrontendRouteName());
wrapper.setTaskHandFrontendModelName(businessKeyAndDescription.getTaskHandFrontendModelName());
wrapper.setTaskHandFrontendComponentName(businessKeyAndDescription.getTaskHandFrontendComponentName());
wrapper.setTaskHandFrontendComponentProperties(businessKeyAndDescription.getTaskHandFrontendComponentProperties());
}
Pageable pageable =queryParameter.getJpaPageable();
long total =Long.parseLong(page.get("totalElements").toString());
return new PageImpl<ProcessTaskWrapper>(wrappers,pageable,total);
}
@Override
public Page<ProcessTaskWrapper> queryFinishedProcessTasks(QueryParameter queryParameter) throws Exception {
return null;
String loginName =SecurityUtil.getLoginName();
String taskSql ="" +
"select * from (\n" +
" select * from (\n" +
" select \n" +
" ht.ID_ as id,\n" +
" ht.NAME_ as name,\n" +
" ht.DESCRIPTION_ as description,\n" +
" ht.PRIORITY_ as priority,\n" +
" ht.OWNER_ as owner,\n" +
" ht.ASSIGNEE_ as assignee,\n" +
" ht.PROC_INST_ID_ as processInstanceId,\n" +
" ht.EXECUTION_ID_ as executionId,\n" +
" ht.PROC_DEF_ID_ as processDefinitionId,\n" +
" ht.SCOPE_ID_ as scopeId,\n" +
" ht.SUB_SCOPE_ID_ as subScopeId,\n" +
" ht.SCOPE_TYPE_ as scopeType,\n" +
" ht.SCOPE_DEFINITION_ID_ as scopeDefinitionId,\n" +
" -- ht.CREATE_TIME_ as createTime,\n" +
" ht.TASK_DEF_KEY_ as taskDefinitionKey,\n" +
" ht.DUE_DATE_ as dueDate,\n" +
" ht.CATEGORY_ as category,\n" +
" ht.PARENT_TASK_ID_ as parentTaskId,\n" +
" ht.TENANT_ID_ as tenantId,\n" +
" ht.FORM_KEY_ as formKey,\n" +
" ht.CLAIM_TIME_ as claimTime,\n" +
" \n" +
" ht.START_TIME_ as createTime,\n" +
" ht.END_TIME_ as END_TIME,\n" +
" \n" +
" rp.NAME_ as processDefinitionName,\n" +
" rp.VERSION_ as processDefinitionVersion,\n" +
" \n" +
" e.BUSINESS_KEY_ \t as businessKey,\n" +
" \n" +
" row_number() over(partition by ht.proc_inst_id_ order by ht.end_time_ desc) as rk\n" +
" from ACT_HI_TASKINST ht\n" +
" left join ACT_RE_PROCDEF rp on ht.proc_def_id_ = rp.id_\n" +
" join ACT_HI_PROCINST e on ht.proc_inst_id_ = e.PROC_INST_ID_\n" +
" left join sys_user u on ht.assignee_ = u.loginname_\n" +
" where \n" +
" exists (\n" +
" select 1 from ACT_HI_TASKINST aht \n" +
" where \n" +
" aht.ASSIGNEE_ = '" + loginName + "' \n" +
" and aht.end_time_ is not null\n" +
" and exists (\n" +
" select 1 from ACT_HI_PROCINST hp \n" +
" where \n" +
" hp.end_time_ is not null \n" +
" and hp.end_act_id_ is not null \n" +
" and hp.proc_inst_id_ = ht.proc_inst_id_\n" +
" ) \n" +
" and aht.proc_inst_id_ = ht.proc_inst_id_\n" +
" )\n" +
" ) tmp where tmp.rk = 1\n" +
") t";
Map<String,Class<?>> fieldTypeMap =new HashMap<>();
fieldTypeMap.put("processDefinitionId",String.class);
fieldTypeMap.put("processInstanceId",String.class);
fieldTypeMap.put("businessKey",String.class);
fieldTypeMap.put("assignee",String.class);
fieldTypeMap.put("taskDefinitionKey",String.class);
fieldTypeMap.put("description",String.class);
Map<String, Object> page =jdbcTemplateService.listBySql(queryParameter,taskSql,fieldTypeMap,new ProcessTaskWrapperMapper());
List<ProcessTaskWrapper> wrappers =new ArrayList<>();
Set<String> processIntanceIds =new HashSet<>();
Map<String,Map<String, BusinessKeyAndDescription>> processDefineAndInstanceAndBusinessMap =new HashMap<>();
if(page==null || page.isEmpty()){
return QueryResult.emptyPage();
}
List<ProcessTaskWrapper> items =(List<ProcessTaskWrapper>)page.get("content");
if(items==null || items.isEmpty()){
return QueryResult.emptyPage();
}
for(ProcessTaskWrapper item : items){
ProcessTaskWrapper wrapper =item;
wrappers.add(wrapper);
Map<String,BusinessKeyAndDescription> processInstanceAndBusinessMap =processDefineAndInstanceAndBusinessMap.get(wrapper.getProcessDefinitionId());
if(processInstanceAndBusinessMap==null){
processInstanceAndBusinessMap =new HashMap<>();
processDefineAndInstanceAndBusinessMap.put(wrapper.getProcessDefinitionId(),processInstanceAndBusinessMap);
}
processInstanceAndBusinessMap.put(wrapper.getProcessInstanceId(),new BusinessKeyAndDescription());
processIntanceIds.add(wrapper.getProcessInstanceId());
}
// 建立流程实例和业务Key的关系
List<HistoricProcessInstance> processInstances =historyService.createHistoricProcessInstanceQuery().processInstanceIds(processIntanceIds).list();
if(CollectionUtil.hasElements(processInstances)) {
for (HistoricProcessInstance processInstance : processInstances) {
Map<String,BusinessKeyAndDescription> processInstanceAndBusinessMap =processDefineAndInstanceAndBusinessMap.get(processInstance.getProcessDefinitionId());
if(processInstanceAndBusinessMap!=null){
BusinessKeyAndDescription businessKeyAndDescription =processInstanceAndBusinessMap.get(processInstance.getId());
if(businessKeyAndDescription!=null){
businessKeyAndDescription.setProcessDefinitionName(processInstance.getProcessDefinitionName());
businessKeyAndDescription.setProcessDefinitionVersion(processInstance.getProcessDefinitionVersion());
businessKeyAndDescription.setBusinessKey(processInstance.getBusinessKey());
businessKeyAndDescription.setBusinessDescription(processInstance.getProcessDefinitionName());
List<HistoricTaskInstance> historicTaskInstances =historyService.createHistoricTaskInstanceQuery().processInstanceId(processInstance.getId()).finished().orderByHistoricTaskInstanceEndTime().desc().list();
if(CollectionUtil.hasElements(historicTaskInstances)){
businessKeyAndDescription.setPreviousAssignee(historicTaskInstances.get(0).getAssignee());
}
}
}
}
}
for(String processDefineId : processDefineAndInstanceAndBusinessMap.keySet()) {
Map<String,BusinessKeyAndDescription> processInstanceAndBusinessMap =processDefineAndInstanceAndBusinessMap.get(processDefineId);
if(processInstanceAndBusinessMap==null || processInstanceAndBusinessMap.size()<=0){
continue;
}
ProcessEntity processEntity =processEntityService.getRepository().findByDeployedId(processDefineId);
for(String processInstanceId : processInstanceAndBusinessMap.keySet()){
BusinessKeyAndDescription businessKeyAndDescription =processInstanceAndBusinessMap.get(processInstanceId);
if(businessKeyAndDescription!=null){
businessKeyAndDescription.setTaskHandFrontendRouteName(processEntity.getTaskHandFrontendRouteName());
businessKeyAndDescription.setTaskHandFrontendModelName(processEntity.getTaskHandFrontendModelName());
businessKeyAndDescription.setTaskHandFrontendComponentName(processEntity.getTaskHandFrontendComponentName());
businessKeyAndDescription.setTaskHandFrontendComponentProperties(processEntity.getTaskHandFrontendComponentProperties());
}
}
if(processEntity==null){
continue;
}
String sql =processEntity.getBusinessDescriptionSql();
if(!StringUtils.hasText(sql)){
continue;
}
Set<String> businessKeys =new HashSet<>();
for(Map.Entry<String,BusinessKeyAndDescription> entry: processInstanceAndBusinessMap.entrySet()){
businessKeys.add(entry.getValue().getBusinessKey());
}
if(!CollectionUtil.hasElements(businessKeys)){
continue;
}
Map<String,Object> variables =new HashMap<>();
variables.put("bussinessKeys","'" + StringUtil.combine("','",businessKeys) + "'");
sql =StringUtil.format(sql,variables);
List<Map<String,Object>> result =jdbcTemplate.queryForList(sql);
if(!CollectionUtil.hasElements(result)){
continue;
}
for(Map<String,Object> row : result){
String bussinessKey =row.get("BUSSINESS_KEY").toString();
String bussinessDescription =row.get("BUSSINESS_DESCRIPTION").toString();
for(String processInstanceId : processInstanceAndBusinessMap.keySet()){
BusinessKeyAndDescription businessKeyAndDescription =processInstanceAndBusinessMap.get(processInstanceId);
if(businessKeyAndDescription.getBusinessKey().equals(bussinessKey)){
businessKeyAndDescription.setBusinessDescription(bussinessDescription);
break;
}
}
}
}
//合并
for(ProcessTaskWrapper wrapper : wrappers){
Map<String,BusinessKeyAndDescription> processInstanceAndBusinessMap =processDefineAndInstanceAndBusinessMap.get(wrapper.getProcessDefinitionId());
BusinessKeyAndDescription businessKeyAndDescription =processInstanceAndBusinessMap.get(wrapper.getProcessInstanceId());
wrapper.setPreviousAssignee(businessKeyAndDescription.getPreviousAssignee());
wrapper.setBusinessKey(businessKeyAndDescription.getBusinessKey());
wrapper.setBusinessDescription(businessKeyAndDescription.getBusinessDescription());
wrapper.setProcessDefinitionName(businessKeyAndDescription.getProcessDefinitionName());
wrapper.setProcessDefinitionVersion(businessKeyAndDescription.getProcessDefinitionVersion());
wrapper.setTaskHandFrontendRouteName(businessKeyAndDescription.getTaskHandFrontendRouteName());
wrapper.setTaskHandFrontendModelName(businessKeyAndDescription.getTaskHandFrontendModelName());
wrapper.setTaskHandFrontendComponentName(businessKeyAndDescription.getTaskHandFrontendComponentName());
wrapper.setTaskHandFrontendComponentProperties(businessKeyAndDescription.getTaskHandFrontendComponentProperties());
}
Pageable pageable =queryParameter.getJpaPageable();
long total =Long.parseLong(page.get("totalElements").toString());
return new PageImpl<ProcessTaskWrapper>(wrappers,pageable,total);
}
@Override

9
io.sc.platform.flowable/src/main/java/io/sc/platform/flowable/support/BusinessKeyAndDescription.java

@ -6,6 +6,7 @@ public class BusinessKeyAndDescription {
private Integer processDefinitionVersion;
private String businessKey;
private String businessDescription;
private String taskHandFrontendRouteName;
private String taskHandFrontendModelName;
private String taskHandFrontendComponentName;
private String taskHandFrontendComponentProperties;
@ -50,6 +51,14 @@ public class BusinessKeyAndDescription {
this.businessDescription = businessDescription;
}
public String getTaskHandFrontendRouteName() {
return taskHandFrontendRouteName;
}
public void setTaskHandFrontendRouteName(String taskHandFrontendRouteName) {
this.taskHandFrontendRouteName = taskHandFrontendRouteName;
}
public String getTaskHandFrontendModelName() {
return taskHandFrontendModelName;
}

12
io.sc.platform.flowable/src/main/java/io/sc/platform/flowable/support/ProcessTaskWrapper.java

@ -2,7 +2,10 @@ package io.sc.platform.flowable.support;
import io.sc.platform.util.DateUtil;
import io.sc.platform.util.support.DateDiff;
import org.springframework.jdbc.core.RowMapper;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.temporal.ChronoUnit;
import java.util.Date;
@ -36,6 +39,7 @@ public class ProcessTaskWrapper {
private Integer processDefinitionVersion;
private String businessKey;
private String businessDescription;
private String taskHandFrontendRouteName;
private String taskHandFrontendModelName;
private String taskHandFrontendComponentName;
private String taskHandFrontendComponentProperties;
@ -212,6 +216,14 @@ public class ProcessTaskWrapper {
this.businessDescription = businessDescription;
}
public String getTaskHandFrontendRouteName() {
return taskHandFrontendRouteName;
}
public void setTaskHandFrontendRouteName(String taskHandFrontendRouteName) {
this.taskHandFrontendRouteName = taskHandFrontendRouteName;
}
public String getTaskHandFrontendModelName() {
return taskHandFrontendModelName;
}

41
io.sc.platform.flowable/src/main/java/io/sc/platform/flowable/support/ProcessTaskWrapperMapper.java

@ -0,0 +1,41 @@
package io.sc.platform.flowable.support;
import io.sc.platform.util.DateUtil;
import io.sc.platform.util.support.DateDiff;
import org.springframework.jdbc.core.RowMapper;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.temporal.ChronoUnit;
import java.util.Date;
public class ProcessTaskWrapperMapper implements RowMapper<ProcessTaskWrapper> {
@Override
public ProcessTaskWrapper mapRow(ResultSet rs, int rowNum) throws SQLException {
ProcessTaskWrapper wrapper =new ProcessTaskWrapper();
wrapper.setId(rs.getString("id"));
wrapper.setName(rs.getString("name"));
wrapper.setDescription(rs.getString("description"));
wrapper.setPriority(rs.getInt("priority"));
wrapper.setOwner(rs.getString("owner"));
wrapper.setAssignee(rs.getString("assignee"));
wrapper.setProcessInstanceId(rs.getString("processInstanceId"));
wrapper.setExecutionId(rs.getString("executionId"));
wrapper.setProcessDefinitionId(rs.getString("processDefinitionId"));
wrapper.setProcessDefinitionName(rs.getString("processDefinitionName"));
wrapper.setProcessDefinitionVersion(rs.getInt("processDefinitionVersion"));
wrapper.setScopeId(rs.getString("scopeId"));
wrapper.setSubScopeId(rs.getString("subScopeId"));
wrapper.setScopeType(rs.getString("scopeType"));
wrapper.setScopeDefinitionId(rs.getString("scopeDefinitionId"));
wrapper.setCreateTime(rs.getDate("createTime"));
wrapper.setTaskDefinitionKey(rs.getString("taskDefinitionKey"));
wrapper.setDueDate(rs.getDate("dueDate"));
wrapper.setCategory(rs.getString("category"));
wrapper.setParentTaskId(rs.getString("parentTaskId"));
wrapper.setTenantId(rs.getString("tenantId"));
wrapper.setFormKey(rs.getString("formKey"));
wrapper.setClaimTime(rs.getDate("claimTime"));
return wrapper;
}
}

1
io.sc.platform.flowable/src/main/resources/liquibase/io.sc.platform.flowable_8.0.0_20220606__Process_Manager_Database_Schema_DDL.xml

@ -26,6 +26,7 @@
<column name="STATUS_" type="NVARCHAR(10)" remarks="流程定义状态"></column>
<column name="CAN_CLAIM_TASK_" type="SMALLINT" remarks="是否可以领取任务"></column>
<column name="BUSINESS_DESC_SQL_" type="NVARCHAR(1024)" remarks="任务描述SQL语句,用于生成带业务信息的任务描述"></column>
<column name="TASK_HAND_FE_ROUTE_NAME_" type="NVARCHAR(1024)" remarks="任务处理前端路由名"></column>
<column name="TASK_HAND_FE_MODEL_NAME_" type="NVARCHAR(1024)" remarks="任务处理前端组件的模块名"></column>
<column name="TASK_HAND_FE_COMP_NAME_" type="NVARCHAR(1024)" remarks="任务处理前端组件的组件名"></column>
<column name="TASK_HAND_FE_COMP_PROPS_" type="CLOB" remarks="任务处理前端组件的组件属性"></column>

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

@ -112,7 +112,7 @@
"node-sql-parser": "5.3.6",
"pinia": "2.3.0",
"pinia-undo": "0.2.4",
"platform-core": "8.2.92",
"platform-core": "8.2.100",
"quasar": "2.17.6",
"sort-array": "5.0.0",
"svg-path-commander": "2.1.7",

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

@ -16,6 +16,13 @@
"theme.favicon": "favicon",
"theme.navigateMenuPosition": "Navigate menu position",
"theme.home": "Home Page",
"theme.home.myTaskTextColor": "My Task Text Color",
"theme.home.myDoneTaskTextColor": "My Done Task Text Color",
"theme.home.myFinishedTaskTextColor": "My Finished Task Text Color",
"theme.home.myMessageTextColor": "My Message Text Color",
"theme.home.announcementTextColor": "Announcement Text Color",
"theme.brand": "Brand",
"theme.brand.dark": "Black color in dark mode",
"theme.brand.dark-page": "Page color in dark mode",
@ -166,6 +173,7 @@
"lcdp.bpm.processDefine.grid.entity.deployId": "Deploy ID",
"lcdp.bpm.processDefine.grid.entity.canClaimTask": "Can Claim Task",
"lcdp.bpm.processDefine.grid.entity.businessDescriptionSql": "Bussiness Description SQL",
"lcdp.bpm.processDefine.grid.entity.taskHandFrontendRouteName": "Task Handler's Frontend Route Name",
"lcdp.bpm.processDefine.grid.entity.taskHandFrontendModelName": "Task Handler's Frontend Module Name",
"lcdp.bpm.processDefine.grid.entity.taskHandFrontendComponentName": "Task Handler's Frontend Component Name",
"lcdp.bpm.processDefine.grid.entity.taskHandFrontendComponentProperties": "Task Handler's Frontend Component Properties(json)",

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

@ -16,6 +16,13 @@
"theme.favicon": "favicon",
"theme.navigateMenuPosition": "導航菜單位置",
"theme.home": "首頁",
"theme.home.myTaskTextColor": "我的代辦文本顏色",
"theme.home.myDoneTaskTextColor": "我的已辦文本顏色",
"theme.home.myFinishedTaskTextColor": "我的辦結文本顏色",
"theme.home.myMessageTextColor": "我的消息文本顏色",
"theme.home.announcementTextColor": "系統公告文本顏色",
"theme.brand": "品牌顏色",
"theme.brand.dark": "黑暗模式下黑色顏色",
"theme.brand.dark-page": "黑暗模式下頁面顏色",
@ -166,6 +173,7 @@
"lcdp.bpm.processDefine.grid.entity.deployId": "發佈 ID",
"lcdp.bpm.processDefine.grid.entity.canClaimTask": "允許領取任務",
"lcdp.bpm.processDefine.grid.entity.businessDescriptionSql": "業務描述 SQL",
"lcdp.bpm.processDefine.grid.entity.taskHandFrontendRouteName": "任務處理前端路由名稱",
"lcdp.bpm.processDefine.grid.entity.taskHandFrontendModelName": "任務處理前端模塊名稱",
"lcdp.bpm.processDefine.grid.entity.taskHandFrontendComponentName": "任務處理前端組件名稱",
"lcdp.bpm.processDefine.grid.entity.taskHandFrontendComponentProperties": "任務處理前端組件屬性(json)",

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

@ -16,6 +16,13 @@
"theme.favicon": "favicon",
"theme.navigateMenuPosition": "导航菜单位置",
"theme.home": "首页",
"theme.home.myTaskTextColor": "我的代办文本颜色",
"theme.home.myDoneTaskTextColor": "我的已办文本颜色",
"theme.home.myFinishedTaskTextColor": "我的办结文本颜色",
"theme.home.myMessageTextColor": "我的消息文本颜色",
"theme.home.announcementTextColor": "系统公告文本颜色",
"theme.brand": "品牌颜色",
"theme.brand.dark": "黑暗模式下黑色颜色",
"theme.brand.dark-page": "黑暗模式下页面颜色",
@ -166,6 +173,7 @@
"lcdp.bpm.processDefine.grid.entity.deployId": "发布 ID",
"lcdp.bpm.processDefine.grid.entity.canClaimTask": "允许领取任务",
"lcdp.bpm.processDefine.grid.entity.businessDescriptionSql": "业务描述 SQL",
"lcdp.bpm.processDefine.grid.entity.taskHandFrontendRouteName": "任务处理前端路由名称",
"lcdp.bpm.processDefine.grid.entity.taskHandFrontendModelName": "任务处理前端模块名称",
"lcdp.bpm.processDefine.grid.entity.taskHandFrontendComponentName": "任务处理前端组件名称",
"lcdp.bpm.processDefine.grid.entity.taskHandFrontendComponentProperties": "任务处理前端组件属性(json)",

16
io.sc.platform.lcdp.frontend/src/views/Theme.vue

@ -28,10 +28,10 @@
</q-item>
</template>
</q-select>
<q-btn :label="$t('theme.action.setDefaultTheme')" icon="bi-heart" no-caps outline dense padding="2px 10px" @click="defaultTheme" />
<q-btn :label="$t('add')" icon="add" no-caps outline dense padding="2px 10px" @click="prompt = true" />
<q-btn :label="$t('delete')" icon="delete" no-caps outline dense padding="2px 10px" @click="removeTheme" />
<q-btn :label="$t('save')" icon="save" no-caps outline dense padding="2px 10px" @click="saveTheme" />
<q-btn :label="$t('theme.action.setDefaultTheme')" icon="bi-heart" no-caps outline @click="defaultTheme" />
<q-btn :label="$t('add')" icon="add" no-caps outline @click="prompt = true" />
<q-btn :label="$t('delete')" icon="delete" no-caps outline @click="removeTheme" />
<q-btn :label="$t('save')" icon="save" no-caps outline @click="saveTheme" />
</div>
<div class="w-full flex flex-row flex-nowrap items-start pl-5">
@ -41,6 +41,9 @@
<q-tab name="brand" icon="bi-bootstrap" no-caps class="mb-2">
<div class="text-left pl-2" style="width: 150px">{{ $t('theme.brand') }}</div>
</q-tab>
<q-tab name="home" icon="bi-house" no-caps class="mb-2">
<div class="text-left pl-2" style="width: 150px">{{ $t('theme.home') }}</div>
</q-tab>
<q-tab name="loadingBar" icon="bi-usb-c" no-caps class="mb-2">
<div class="text-left pl-2" style="width: 150px">{{ $t('theme.loadingBar') }}</div>
</q-tab>
@ -76,6 +79,7 @@
<div class="grow px-6">
<q-tab-panels v-model="selectedTab" animated swipeable vertical>
<q-tab-panel name="brand"><Brand></Brand></q-tab-panel>
<q-tab-panel name="home"><Home></Home></q-tab-panel>
<q-tab-panel name="loadingBar"><LoadingBar></LoadingBar></q-tab-panel>
<q-tab-panel name="topper"><Topper></Topper></q-tab-panel>
<q-tab-panel name="sider"><Sider></Sider></q-tab-panel>
@ -109,6 +113,7 @@ import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useQuasar } from 'quasar';
import { Environment, axios, Tools, NotifyManager } from 'platform-core';
import Home from './theme/Home.vue';
import Brand from './theme/Brand.vue';
import LoadingBar from './theme/LoadingBar.vue';
import Topper from './theme/Topper.vue';
@ -191,9 +196,11 @@ const saveTheme = () => {
persistent: true,
})
.onOk(() => {
console.log('>>>>>>>');
const data = {};
Tools.mergeObject(data, configureMap.get(selectedTheme.value));
data.theme = Environment.getConfigure().theme;
console.log(data);
axios.put(Environment.apiContextPath('/api/lcdp/configure/') + selectedTheme.value, data).then((response) => {
NotifyManager.info(t('success'));
@ -208,6 +215,7 @@ const refresh = (callback?: any) => {
selectedTheme.value = '';
const items = response.data.content;
console.log(items);
if (items && items.length > 0) {
for (const item of items) {
options.push(item);

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

@ -187,14 +187,18 @@
form.setFieldValue('key', value);
form.setFieldValue('name', Formater.dictionary(ProcessCategoryDictionaries)(value));
},
readOnlyIf: true,
readOnlyIf: (args: any) => {
return args.form.getStatus() === 'edit';
},
},
{
name: 'key',
label: $t('code'),
type: 'w-text',
defaultValue: 'SAMPLE',
readOnlyIf: true,
readOnlyIf: (args: any) => {
return args.form.getStatus() === 'edit';
},
},
{
name: 'name',
@ -210,7 +214,9 @@
label: $t('xml'),
type: 'w-textarea',
rows: 5,
readOnlyIf: true,
readOnlyIf: (args: any) => {
return args.form.getStatus() === 'edit';
},
},
{ name: 'status', label: $t('status'), type: 'w-text', defaultValue: 'SKETCH', showIf: false },
{
@ -219,7 +225,13 @@
type: 'w-checkbox',
defaultValue: true,
rule: [],
disableIf: true,
disableIf: (args: any) => {
if (args.form.getStatus() === 'edit') {
return true;
} else {
return false;
}
},
},
{
colSpan: 2,
@ -259,17 +271,66 @@
lineBreak: true,
placeholder: true,
},
{
colSpan: 3,
name: 'taskHandFrontendRouteName',
label: $t('lcdp.bpm.processDefine.grid.entity.taskHandFrontendRouteName'),
type: 'w-grid-select',
tree: true,
displayValue: (args: any) => {
return args.data.name;
},
grid: {
title: $t('re.graph.vertex.resourceAbstract.entity.resourceAbstractId'),
denseBody: true,
hideBottom: true,
configButton: true,
checkboxSelection: false,
dataUrl: Environment.apiContextPath('/api/mvc/frontend/findRouteNames'),
primaryKey: 'name',
pageable: false,
sortBy: ['type', 'namec', '-lastModifyDate'],
toolbarConfigure: { noIcon: false },
toolbarActions: ['query', 'separator', 'reset'],
queryFormColsNum: 3,
queryFormFields: [{ name: 'name', label: $t('name'), type: 'w-text' }],
columns: [
{ width: '100%', name: 'name', label: $t('name') },
{ width: '100%', name: 'module', label: $t('module') },
{ width: '100%', name: 'component', label: $t('component') },
],
},
},
{
colSpan: 1,
name: 'taskHandFrontendModelName',
label: $t('lcdp.bpm.processDefine.grid.entity.taskHandFrontendModelName'),
type: 'w-text',
type: 'w-select',
clearable: true,
options: taskHandFrontendModelNameOptionsRef,
onUpdateValue: (args: any) => {
if (args.value) {
axios.get(Environment.apiContextPath('/api/mvc/frontend/findComponentNames/' + args.value)).then((response) => {
const options = [];
response.data.forEach((item) => {
options.push({ label: item, value: item });
});
console.log(options);
taskHandFrontendComponentNameOptionsRef = options;
});
} else {
args.form.setFieldValue('taskHandFrontendComponentName', undefined);
taskHandFrontendComponentNameOptionsRef = [];
}
},
},
{
colSpan: 2,
name: 'taskHandFrontendComponentName',
label: $t('lcdp.bpm.processDefine.grid.entity.taskHandFrontendComponentName'),
type: 'w-text',
type: 'w-select',
clearable: true,
options: taskHandFrontendComponentNameOptionsRef,
},
{
colSpan: 3,
@ -668,6 +729,9 @@ const processDesignerDialogRef = ref();
const processDesignerIframeRef = ref();
const createProcessInstanceDialogRef = ref();
const taskHandFrontendModelNameOptionsRef = ref([]);
const taskHandFrontendComponentNameOptionsRef = ref([]);
const afterTaskCompleted = () => {
NotifyManager.success();
taskGridRef.value.refresh();
@ -691,4 +755,12 @@ const cleanHistoryData = () => {
const ProcessStatusEnum = await EnumTools.fetch('io.sc.platform.flowable.enums.ProcessStatus');
const ProcessCategoryDictionaries = await DictionaryTools.fetch('WORK_FLOW');
axios.get(Environment.apiContextPath('/api/mvc/frontend/findModuleNames')).then((response) => {
const options = [];
for (const item of response.data) {
options.push({ label: item, value: item });
}
taskHandFrontendModelNameOptionsRef.value = options;
});
</script>

61
io.sc.platform.lcdp.frontend/src/views/theme/Home.vue

@ -0,0 +1,61 @@
<template>
<div class="row">
<div class="col-lg-4 col-md-6 col-sm-8 col-xs-10">
<w-color-input v-model="$gc.theme.home['myTaskTextColor']" :label="$t('theme.home.myTaskTextColor')" class="p-1" dense outlined restore></w-color-input>
</div>
</div>
<div class="row">
<div class="col-lg-4 col-md-6 col-sm-8 col-xs-10">
<w-color-input
v-model="$gc.theme.home['myDoneTaskTextColor']"
:label="$t('theme.home.myDoneTaskTextColor')"
class="p-1"
dense
outlined
restore
></w-color-input>
</div>
</div>
<div class="row">
<div class="col-lg-4 col-md-6 col-sm-8 col-xs-10">
<w-color-input
v-model="$gc.theme.home['myFinishedTaskTextColor']"
:label="$t('theme.home.myFinishedTaskTextColor')"
class="p-1"
dense
outlined
restore
></w-color-input>
</div>
</div>
<div class="row">
<div class="col-lg-4 col-md-6 col-sm-8 col-xs-10">
<w-color-input
v-model="$gc.theme.home['myMessageTextColor']"
:label="$t('theme.home.myMessageTextColor')"
class="p-1"
dense
outlined
restore
></w-color-input>
</div>
</div>
<div class="row">
<div class="col-lg-4 col-md-6 col-sm-8 col-xs-10">
<w-color-input
v-model="$gc.theme.home['announcementTextColor']"
:label="$t('theme.home.announcementTextColor')"
class="p-1"
dense
outlined
restore
></w-color-input>
</div>
</div>
</template>
<script setup lang="ts">
import { Environment } from 'platform-core';
console.log(Environment.getConfigure().theme);
</script>

1
io.sc.platform.lcdp.frontend/webpack.config.common.cjs

@ -89,6 +89,7 @@ module.exports = {
use: [
{
loader: 'vue-loader',
options: { hotReload: false },
},
],
},

49
io.sc.platform.lcdp/src/main/java/io/sc/platform/lcdp/configure/api/Home.java

@ -0,0 +1,49 @@
package io.sc.platform.lcdp.configure.api;
public class Home {
private String myTaskTextColor ="black"; // 我的代办文本颜色
private String myDoneTaskTextColor ="black"; // 我的已办文本颜色
private String myFinishedTaskTextColor ="black"; // 我的办结文本颜色
private String myMessageTextColor ="black"; // 我的消息文本颜色
private String announcementTextColor ="black"; // 系统公告文本颜色
public String getMyTaskTextColor() {
return myTaskTextColor;
}
public void setMyTaskTextColor(String myTaskTextColor) {
this.myTaskTextColor = myTaskTextColor;
}
public String getMyDoneTaskTextColor() {
return myDoneTaskTextColor;
}
public void setMyDoneTaskTextColor(String myDoneTaskTextColor) {
this.myDoneTaskTextColor = myDoneTaskTextColor;
}
public String getMyFinishedTaskTextColor() {
return myFinishedTaskTextColor;
}
public void setMyFinishedTaskTextColor(String myFinishedTaskTextColor) {
this.myFinishedTaskTextColor = myFinishedTaskTextColor;
}
public String getMyMessageTextColor() {
return myMessageTextColor;
}
public void setMyMessageTextColor(String myMessageTextColor) {
this.myMessageTextColor = myMessageTextColor;
}
public String getAnnouncementTextColor() {
return announcementTextColor;
}
public void setAnnouncementTextColor(String announcementTextColor) {
this.announcementTextColor = announcementTextColor;
}
}

19
io.sc.platform.lcdp/src/main/java/io/sc/platform/lcdp/configure/api/Setting.java

@ -5,8 +5,9 @@ import io.sc.platform.security.AuthenticationMode;
public class Setting {
private long sessionTimeout =30 * 60; // 会话过期时间(单位:秒)
private String homePage ="/home"; // 首页路由 path
private String notifierTaskNavigateFrontendRoutePath;//顶部通知栏任务导航前端路由路径
private String logoutActionUrl ="/logout"; // 登出 Action URL
private I18n i18n =new I18n(); // 多语言国际化
private boolean enableChangeRole =false; // 是否允许切换角色(当一个用户拥有多个角色时)
private boolean isMultiCorporationMode =false; // 是否是多法人模式
private AuthenticationMode authenticationMode =AuthenticationMode.OAUTH2; // 认证模式
@ -26,12 +27,12 @@ public class Setting {
this.homePage = homePage;
}
public String getNotifierTaskNavigateFrontendRoutePath() {
return notifierTaskNavigateFrontendRoutePath;
public String getLogoutActionUrl() {
return logoutActionUrl;
}
public void setNotifierTaskNavigateFrontendRoutePath(String notifierTaskNavigateFrontendRoutePath) {
this.notifierTaskNavigateFrontendRoutePath = notifierTaskNavigateFrontendRoutePath;
public void setLogoutActionUrl(String logoutActionUrl) {
this.logoutActionUrl = logoutActionUrl;
}
public I18n getI18n() {
@ -42,6 +43,14 @@ public class Setting {
this.i18n = i18n;
}
public boolean isEnableChangeRole() {
return enableChangeRole;
}
public void setEnableChangeRole(boolean enableChangeRole) {
this.enableChangeRole = enableChangeRole;
}
public boolean getIsMultiCorporationMode() {
return isMultiCorporationMode;
}

9
io.sc.platform.lcdp/src/main/java/io/sc/platform/lcdp/configure/api/Theme.java

@ -5,6 +5,7 @@ public class Theme {
private int minWidth =600; // 整个页面最小宽度(避免浏览器窗口太小,导致的页面不友好)
private String favicon="favicon.svg"; // favicon
private String navigateMenuPosition ="sider"; // 导航菜单位置(可选值:topper,sider)
private Home home =new Home(); // 首页
private Brand brand =new Brand(); // 品牌颜色
private LoadingBar loadingBar =new LoadingBar();// 页面加载进度条
private Topper topper =new Topper(); // 顶部
@ -49,6 +50,14 @@ public class Theme {
this.navigateMenuPosition = navigateMenuPosition;
}
public Home getHome() {
return home;
}
public void setHome(Home home) {
this.home = home;
}
public Brand getBrand() {
return brand;
}

40
io.sc.platform.lcdp/src/main/java/io/sc/platform/lcdp/configure/service/impl/ConfigureServiceImpl.java

@ -9,10 +9,14 @@ import io.sc.platform.lcdp.configure.jpa.repository.ConfigureRepository;
import io.sc.platform.lcdp.configure.service.ConfigureService;
import io.sc.platform.mvc.service.SystemParameterService;
import io.sc.platform.orm.service.impl.DaoServiceImpl;
import io.sc.platform.orm.service.support.QueryParameter;
import io.sc.platform.orm.service.support.QueryResult;
import io.sc.platform.security.AuthenticationMode;
import io.sc.platform.security.SecurityProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.session.SessionProperties;
import org.springframework.core.env.Environment;
import org.springframework.data.domain.Page;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
@ -20,6 +24,8 @@ import org.springframework.util.StringUtils;
import javax.transaction.Transactional;
import java.time.Duration;
import java.time.temporal.TemporalUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Service("io.sc.platform.lcdp.configure.service.impl.ConfigureServiceImpl")
@ -27,6 +33,27 @@ public class ConfigureServiceImpl extends DaoServiceImpl<ConfigureEntity, String
@Autowired private Environment environment;
@Autowired private JdbcTemplate jdbcTemplate;
@Autowired private SystemParameterService systemParameterService;
@Autowired private SecurityProperties securityProperties;
@Override
@Transactional
public Page<ConfigureEntity> query(QueryParameter queryParameter) throws Exception {
Page<ConfigureEntity> page =super.query(queryParameter);
if(page!=null && page.getContent()!=null && !page.getContent().isEmpty()) {
return page;
}
List<ConfigureEntity> list =new ArrayList<>();
ConfigureEntity entity =new ConfigureEntity();
entity.setName("Default");
entity.setActive(true);
entity.setSetting(new Setting());
entity.setTheme(new Theme());
entity.setCorporationCode("_PRIMARY_");
list.add(entity);
return QueryResult.page(list);
}
@Override
public Configure getActiveConfigure() {
@ -65,19 +92,22 @@ public class ConfigureServiceImpl extends DaoServiceImpl<ConfigureEntity, String
configure.getSetting().setHomePage(homePage);
}
String logoutActionUrl =securityProperties.getLogout().getLogoutUrl();
if(StringUtils.hasText(logoutActionUrl)) {
configure.getSetting().setLogoutActionUrl(logoutActionUrl);
}
String encodePassword =parameters.get("parameter.system.login.encodePassword");
if(StringUtils.hasText(encodePassword)) {
configure.getTheme().getLogin().setEncodePassword(Boolean.valueOf(encodePassword));
}
String notifierTaskNavigateFrontendRoutePath =parameters.get("parameter.system.notifierTaskNavigateFrontendRoutePath");
if(StringUtils.hasText(notifierTaskNavigateFrontendRoutePath)) {
configure.getSetting().setNotifierTaskNavigateFrontendRoutePath(notifierTaskNavigateFrontendRoutePath);
}
AuthenticationMode authenticationMode =environment.getProperty("application.authentication.mode",AuthenticationMode.class);
configure.getSetting().setAuthenticationMode(authenticationMode);
boolean enableChangeRole =environment.getProperty("application.enable-change-role",Boolean.class,false);
configure.getSetting().setEnableChangeRole(enableChangeRole);
configure.getSetting().setIsMultiCorporationMode(io.sc.platform.core.Environment.getInstance().isMultiCorportationMode());
return configure;
}

2
io.sc.platform.license.keygen.frontend/package.json

@ -112,7 +112,7 @@
"node-sql-parser": "5.3.6",
"pinia": "2.3.0",
"pinia-undo": "0.2.4",
"platform-core": "8.2.92",
"platform-core": "8.2.100",
"quasar": "2.17.6",
"sort-array": "5.0.0",
"svg-path-commander": "2.1.7",

1
io.sc.platform.license.keygen.frontend/webpack.config.common.cjs

@ -89,6 +89,7 @@ module.exports = {
use: [
{
loader: 'vue-loader',
options: { hotReload: false },
},
],
},

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

@ -112,7 +112,7 @@
"node-sql-parser": "5.3.6",
"pinia": "2.3.0",
"pinia-undo": "0.2.4",
"platform-core": "8.2.92",
"platform-core": "8.2.100",
"quasar": "2.17.6",
"sort-array": "5.0.0",
"svg-path-commander": "2.1.7",

1
io.sc.platform.mvc.frontend/webpack.config.common.cjs

@ -89,6 +89,7 @@ module.exports = {
use: [
{
loader: 'vue-loader',
options: { hotReload: false },
},
],
},

78
io.sc.platform.mvc/src/main/java/io/sc/platform/mvc/controller/FrontendController.java

@ -1,19 +1,24 @@
package io.sc.platform.mvc.controller;
import io.sc.platform.core.Environment;
import io.sc.platform.mvc.plugins.PluginManager;
import io.sc.platform.mvc.plugins.item.FrontEndModule;
import io.sc.platform.mvc.plugins.item.FrontEndRoute;
import io.sc.platform.mvc.service.FrontEndService;
import io.sc.platform.mvc.service.FrontendExportService;
import io.sc.platform.mvc.support.ExportFileInfo;
import io.sc.platform.mvc.support.FrontendExportParam;
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.Contains;
import io.sc.platform.orm.service.support.criteria.impl.InSet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;
@RestController("io.sc.platform.mvc.controller.FrontendController")
public class FrontendController {
@ -39,4 +44,67 @@ public class FrontendController {
public ExportFileInfo export(@RequestBody FrontendExportParam param) throws Exception{
return frontEndExportService.export(param);
}
@GetMapping("/api/mvc/frontend/findRouteNames")
public List<FrontEndRoute> findRouteNames(QueryParameter queryParameter) throws Exception{
List<FrontEndRoute> frontEndRoutes =PluginManager.getInstance().getFrontEndRoutes();
//移除远程组件(remoteComponent 属性为 true), 仅保留路由
List<FrontEndRoute> result =frontEndRoutes.stream().filter((item)->{
return (item.getRemoteComponent()==null || !item.getRemoteComponent());
}).collect(Collectors.toList());
// 按路由名称排序
Collections.sort(result, new Comparator<FrontEndRoute>() {
@Override
public int compare(FrontEndRoute o1, FrontEndRoute o2) {
return o1.getName().compareTo(o2.getName());
}
});
// 通过条件过滤
if(queryParameter.existsCriteria()){
Criteria criteria =queryParameter.getCriteriaByFieldName("name");
if(criteria instanceof Contains){
Contains contains =(Contains)criteria;
String name =contains.getValue();
return result.stream().filter((item)->{
return (item.getRemoteComponent()==null || !item.getRemoteComponent()) && item.getName().contains(name);
}).collect(Collectors.toList());
}else if(criteria instanceof InSet){
InSet inset =(InSet)criteria;
String[] values =inset.getValue();
String name =values[0];
return result.stream().filter((item)->{
return (item.getRemoteComponent()==null || !item.getRemoteComponent()) && item.getName().equals(name);
}).collect(Collectors.toList());
}
}
return result;
}
@GetMapping("/api/mvc/frontend/findModuleNames")
public Set<String> findModuleNames() throws Exception{
List<FrontEndRoute> items =PluginManager.getInstance().getFrontEndRoutes();
if(items==null || items.isEmpty()) { return Collections.emptySet(); }
Set<String> result =new TreeSet<>();
for(FrontEndRoute item : items){
result.add(item.getModule());
}
return result;
}
@GetMapping("/api/mvc/frontend/findComponentNames/{moduleName}")
public Set<String> findComponentNames(@PathVariable("moduleName")String moduleName) throws Exception{
List<FrontEndRoute> items =PluginManager.getInstance().getFrontEndRoutes();
if(items==null || items.isEmpty()) { return Collections.emptySet(); }
Set<String> result =new TreeSet<>();
for(FrontEndRoute item : items){
if(!StringUtils.hasText(moduleName) || item.getModule().equals(moduleName)) {
result.add(item.getComponent());
}
}
return result;
}
}

9
io.sc.platform.mvc/src/main/java/io/sc/platform/mvc/plugins/item/FrontEndRoute.java

@ -4,6 +4,7 @@ import java.util.ArrayList;
import java.util.List;
public class FrontEndRoute {
private Boolean remoteComponent;
private Boolean force;
private String name;
private String parent;
@ -20,6 +21,14 @@ public class FrontEndRoute {
//附加属性
private String configurationFileUrl; //菜单贡献项配置文件位置
public Boolean getRemoteComponent() {
return remoteComponent;
}
public void setRemoteComponent(Boolean remoteComponent) {
this.remoteComponent = remoteComponent;
}
public Boolean getForce() {
return force;
}

16
io.sc.platform.mvc/src/main/java/io/sc/platform/mvc/service/impl/FrontEndServiceImpl.java

@ -56,12 +56,18 @@ public class FrontEndServiceImpl implements FrontEndService {
Map<String,String> result =new HashMap<>();
for (Map.Entry<String,FrontEndModule> entry : localFrontEndModuleMap.entrySet()) {
if(entry.getValue().getComponents().size()>0) {
result.put(entry.getKey(), getRemoteEntry(entry.getValue(), request));
String key =entry.getKey();
FrontEndModule value =entry.getValue();
String url =getRemoteEntryUrlPrefix(value, request) + value.getName() + "/javascript/remoteEntry.js";
result.put(key, url);
}
}
for (Map.Entry<String,FrontEndModule> entry : remoteFrontEndModuleCache.asMap().entrySet()) {
if(entry.getValue().getComponents().size()>0) {
result.put(entry.getKey(),getRemoteEntry(entry.getValue(),request));
String key =entry.getKey();
FrontEndModule value =entry.getValue();
String url =getRemoteEntryUrlPrefix(value, request) + "javascript/remoteEntry.js";
result.put(key, url);
}
}
return result;
@ -90,8 +96,7 @@ public class FrontEndServiceImpl implements FrontEndService {
remoteFrontEndModuleCache.put(frontEndModule.getName(),frontEndModule);
}
private String getRemoteEntry(FrontEndModule frontEndModule,HttpServletRequest request) {
String moduleName =frontEndModule.getName();
private String getRemoteEntryUrlPrefix(FrontEndModule frontEndModule,HttpServletRequest request) {
String protocol =null;
String host =null;
int port =80;
@ -125,9 +130,6 @@ public class FrontEndServiceImpl implements FrontEndService {
}
contextPath = UrlUtil.standardized(contextPath);
sb.append(contextPath);
sb.append(moduleName);
sb.append("/javascript/remoteEntry.js");
return sb.toString();
}
}

8
io.sc.platform.mvc/src/main/resources/META-INF/platform/plugins/parameters.json

@ -8,13 +8,5 @@
"code": "parameter.system.homePage",
"defaultValue": "/home",
"order": 100
},
/*/*/
{
"id": "parameter.system.notifierTaskNavigateFrontendRoutePath",
"parentId": "parameter.system",
"code": "parameter.system.notifierTaskNavigateFrontendRoutePath",
"defaultValue": "",
"order": 150
}
]

26
io.sc.platform.orm/src/main/java/io/sc/platform/orm/service/support/criteria/impl/Between.java

@ -26,7 +26,9 @@ public class Between<E> extends Criteria<E> {
}
Class<?> clazz =path.getJavaType();
if(Comparable.class.isAssignableFrom(clazz)){
if(StringUtils.hasText(start) && StringUtils.hasText(end)) {
if(!StringUtils.hasText(start) && !StringUtils.hasText(end)){
return null;
}else if(StringUtils.hasText(start) && StringUtils.hasText(end)) {
return builder.and(
builder.greaterThan(path, (Comparable)conversionService.convert(start, clazz)),
builder.lessThan(path, (Comparable)conversionService.convert(end, clazz))
@ -43,6 +45,11 @@ public class Between<E> extends Criteria<E> {
@Override
public Condition getCondition(Map<String, Class<?>> typeMapping) {
Condition condition =new Condition();
String nStart =":" + fieldName + "Start";
String nEnd =":" + fieldName + "End";
Class<?> clazz =typeMapping.get(fieldName);
Object pStart =start;
Object pEnd =end;
@ -50,13 +57,20 @@ public class Between<E> extends Criteria<E> {
pStart =conversionService.convert(start, clazz);
pEnd =conversionService.convert(end, clazz);
}
String nStart =":" + fieldName + "Start";
String nEnd =":" + fieldName + "End";
String where =StringUtil.format("${0} > ${1} and ${0} < ${2}",fieldName,nStart, nEnd);
Condition condition =new Condition();
condition.setWhere(where);
if(pStart==null && pEnd==null){
return condition;
}else if(pStart!=null && pEnd!=null){
condition.setWhere(StringUtil.format("${0} > ${1} and ${0} < ${2}",fieldName,nStart, nEnd));
condition.getParameters().put(nStart.substring(1),pStart);
condition.getParameters().put(nEnd.substring(1),pEnd);
}else if(pStart!=null){
condition.setWhere(StringUtil.format("${0} > ${1}",fieldName,nStart));
condition.getParameters().put(nStart.substring(1),pStart);
}else {
condition.setWhere(StringUtil.format("${0} < ${2}",fieldName,nEnd));
condition.getParameters().put(nEnd.substring(1),pEnd);
}
return condition;
}

29
io.sc.platform.orm/src/main/java/io/sc/platform/orm/service/support/criteria/impl/BetweenInclusive.java

@ -22,7 +22,9 @@ public class BetweenInclusive<E> extends Between<E> {
}
Class<?> clazz =path.getJavaType();
if(Comparable.class.isAssignableFrom(clazz)){
if(StringUtils.hasText(start) && StringUtils.hasText(end)) {
if(!StringUtils.hasText(start) && !StringUtils.hasText(end)){
return null;
}else if(StringUtils.hasText(start) && StringUtils.hasText(end)) {
return builder.and(
builder.greaterThanOrEqualTo(path, (Comparable)conversionService.convert(start, clazz)),
builder.lessThanOrEqualTo(path, (Comparable)conversionService.convert(end, clazz))
@ -39,9 +41,11 @@ public class BetweenInclusive<E> extends Between<E> {
@Override
public Condition getCondition(Map<String, Class<?>> typeMapping) {
if(!StringUtils.hasText(start) && !StringUtils.hasText(end)){
throw new IllegalArgumentException("[" + operator + "] need a [start] or [end] value");
}
Condition condition =new Condition();
String nStart =":" + fieldName + "Start";
String nEnd =":" + fieldName + "End";
Class<?> clazz =typeMapping.get(fieldName);
Object pStart =start;
Object pEnd =end;
@ -49,13 +53,20 @@ public class BetweenInclusive<E> extends Between<E> {
pStart =conversionService.convert(start, clazz);
pEnd =conversionService.convert(end, clazz);
}
String nStart =":" + fieldName + "Start";
String nEnd =":" + fieldName + "End";
String where =StringUtil.format("${0} >= ${1} and ${0} <= ${2}",fieldName,nStart, nEnd);
Condition condition =new Condition();
condition.setWhere(where);
if(pStart==null && pEnd==null){
return condition;
}else if(pStart!=null && pEnd!=null){
condition.setWhere(StringUtil.format("${0} >= ${1} and ${0} <= ${2}",fieldName,nStart, nEnd));
condition.getParameters().put(nStart.substring(1),pStart);
condition.getParameters().put(nEnd.substring(1),pEnd);
}else if(pStart!=null){
condition.setWhere(StringUtil.format("${0} >= ${1}",fieldName,nStart));
condition.getParameters().put(nStart.substring(1),pStart);
}else {
condition.setWhere(StringUtil.format("${0} <= ${2}",fieldName,nEnd));
condition.getParameters().put(nEnd.substring(1),pEnd);
}
return condition;
}
}

8
io.sc.platform.orm/src/main/java/io/sc/platform/orm/service/support/criteria/impl/EndWith.java

@ -44,4 +44,12 @@ public class EndWith<E> extends Criteria<E> {
condition.setWhere(where);
return condition;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}

8
io.sc.platform.orm/src/main/java/io/sc/platform/orm/service/support/criteria/impl/Not.java

@ -1,5 +1,6 @@
package io.sc.platform.orm.service.support.criteria.impl;
import io.sc.platform.jdbc.sql.builder.SqlBuilder;
import io.sc.platform.jdbc.sql.condition.Condition;
import io.sc.platform.orm.service.support.OperatorTypeNotSupportedException;
import io.sc.platform.orm.service.support.criteria.Criteria;
@ -27,6 +28,11 @@ public class Not<E> extends Criteria<E> {
@Override
public Condition getCondition(Map<String, Class<?>> typeMapping) {
throw new OperatorTypeNotSupportedException();
List<Condition> conditions =new ArrayList<>();
for(Criteria _criteria : criteria){
_criteria.setConversionService(conversionService);
conditions.add(_criteria.getCondition(typeMapping));
}
return SqlBuilder.getSqlWhereBuilder().not(conditions);
}
}

8
io.sc.platform.orm/src/main/java/io/sc/platform/orm/service/support/criteria/impl/NotContains.java

@ -38,4 +38,12 @@ public class NotContains<E> extends Criteria<E> {
public Condition getCondition(Map<String, Class<?>> typeMapping) {
throw new OperatorTypeNotSupportedException();
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}

8
io.sc.platform.orm/src/main/java/io/sc/platform/orm/service/support/criteria/impl/NotEndWith.java

@ -38,4 +38,12 @@ public class NotEndWith<E> extends Criteria<E> {
public Condition getCondition(Map<String, Class<?>> typeMapping) {
throw new OperatorTypeNotSupportedException();
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}

8
io.sc.platform.orm/src/main/java/io/sc/platform/orm/service/support/criteria/impl/NotEquals.java

@ -38,4 +38,12 @@ public class NotEquals<E> extends Criteria<E> {
public Condition getCondition(Map<String, Class<?>> typeMapping) {
throw new OperatorTypeNotSupportedException();
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}

8
io.sc.platform.orm/src/main/java/io/sc/platform/orm/service/support/criteria/impl/NotInSet.java

@ -32,4 +32,12 @@ public class NotInSet<E> extends Criteria<E> {
public Condition getCondition(Map<String, Class<?>> typeMapping) {
throw new OperatorTypeNotSupportedException();
}
public String[] getValue() {
return value;
}
public void setValue(String[] value) {
this.value = value;
}
}

8
io.sc.platform.orm/src/main/java/io/sc/platform/orm/service/support/criteria/impl/NotStartWith.java

@ -38,4 +38,12 @@ public class NotStartWith<E> extends Criteria<E> {
public Condition getCondition(Map<String, Class<?>> typeMapping) {
throw new OperatorTypeNotSupportedException();
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}

8
io.sc.platform.orm/src/main/java/io/sc/platform/orm/service/support/criteria/impl/Or.java

@ -1,5 +1,6 @@
package io.sc.platform.orm.service.support.criteria.impl;
import io.sc.platform.jdbc.sql.builder.SqlBuilder;
import io.sc.platform.jdbc.sql.condition.Condition;
import io.sc.platform.orm.service.support.OperatorTypeNotSupportedException;
import io.sc.platform.orm.service.support.criteria.Criteria;
@ -28,6 +29,11 @@ public class Or<E> extends Criteria<E> {
@Override
public Condition getCondition(Map<String, Class<?>> typeMapping) {
throw new OperatorTypeNotSupportedException();
List<Condition> conditions =new ArrayList<>();
for(Criteria _criteria : criteria){
_criteria.setConversionService(conversionService);
conditions.add(_criteria.getCondition(typeMapping));
}
return SqlBuilder.getSqlWhereBuilder().or(conditions);
}
}

8
io.sc.platform.orm/src/main/java/io/sc/platform/orm/service/support/criteria/impl/StartWith.java

@ -45,4 +45,12 @@ public class StartWith<E> extends Criteria<E> {
condition.setWhere(where);
return condition;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}

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

Loading…
Cancel
Save