91 changed files with 2192 additions and 1040 deletions
@ -0,0 +1,25 @@ |
|||||
|
import { Tools } from 'platform-core'; |
||||
|
|
||||
|
const PassOrNotFormater = (value) => { |
||||
|
if (Tools.isUndefinedOrNull(value)) { |
||||
|
return ''; |
||||
|
} |
||||
|
if (value === 'PASSED') { |
||||
|
return { |
||||
|
componentType: 'QIcon', |
||||
|
attrs: { name: 'bi-check-circle', size: '20px', color: 'green' }, |
||||
|
}; |
||||
|
} else if (value === 'UN_PASSED') { |
||||
|
return { |
||||
|
componentType: 'QIcon', |
||||
|
attrs: { name: 'bi-x-circle', size: '20px', color: 'red' }, |
||||
|
}; |
||||
|
} else if (value === 'ERROR') { |
||||
|
return { |
||||
|
componentType: 'QIcon', |
||||
|
attrs: { name: 'bi-x-circle', size: '20px', color: 'red' }, |
||||
|
}; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
export default PassOrNotFormater; |
@ -0,0 +1,150 @@ |
|||||
|
<template> |
||||
|
<!-- 组件默认带上的官方属性说明 |
||||
|
allow-focus-outside: 允许对话框外的元素可聚焦(不设置该值会导致luckysheet在窗口中无法编辑) |
||||
|
no-esc-dismiss: 用户不能按 ESC 键关闭对话框;如果还设置了 'persistent' 属性,则无需设置它 |
||||
|
no-backdrop-dismiss: 用户不能通过单击对话框外部来关闭对话框;如果还设置了 'persistent' 属性,则无需设置它 |
||||
|
no-refocus: 当对话框被隐藏时,不要重新聚焦以前有聚焦过的 DOM 元素 |
||||
|
--> |
||||
|
<q-dialog |
||||
|
ref="dialogRef" |
||||
|
v-model="dialog.show" |
||||
|
:maximized="dialog.maximized" |
||||
|
allow-focus-outside |
||||
|
no-esc-dismiss |
||||
|
no-backdrop-dismiss |
||||
|
no-refocus |
||||
|
v-bind="attrs" |
||||
|
> |
||||
|
<q-card |
||||
|
:style="{ |
||||
|
width: dialog.maximized ? '100vw' : props.width, |
||||
|
'max-width': '100vw', |
||||
|
height: dialog.maximized ? '100vh' : props.height, |
||||
|
'max-height': '100vh', |
||||
|
}" |
||||
|
> |
||||
|
<div class="w-full h-full"> |
||||
|
<div> |
||||
|
<q-card-section style="height: 59px"> |
||||
|
<div class="flex justify-between"> |
||||
|
<div class="text-h6">{{ title }}</div> |
||||
|
<div class="flex justify-end gap-4"> |
||||
|
<template v-if="buttons && buttons.length > 0"> |
||||
|
<template v-for="(btn, index) in buttons as any" :key="index"> |
||||
|
<q-btn v-if="typeof btn === 'object'" :loading="false" :color="'primary'" v-bind="btn" @click="btn.click ? btn.click() : () => {}"> </q-btn> |
||||
|
</template> |
||||
|
</template> |
||||
|
<slot name="buttons"></slot> |
||||
|
<q-btn v-if="canMaximize" dense flat :icon="!dialog.maximized ? IconEnum.全屏 : IconEnum.退出全屏" @click="maximizeBtnClick"> |
||||
|
<q-tooltip v-if="!dialog.maximized">全屏</q-tooltip> |
||||
|
<q-tooltip v-else-if="dialog.maximized">退出全屏</q-tooltip> |
||||
|
</q-btn> |
||||
|
<q-btn v-close-popup dense flat :icon="IconEnum.关闭"> |
||||
|
<q-tooltip>关闭</q-tooltip> |
||||
|
</q-btn> |
||||
|
</div> |
||||
|
</div> |
||||
|
</q-card-section> |
||||
|
<q-separator /> |
||||
|
</div> |
||||
|
<div ref="dialogContentDivRef" class="dialog_content_div" style="height: calc(100% - 60px)"> |
||||
|
<q-card-section style="height: 100%; padding: 0px" class="scroll"> |
||||
|
<slot></slot> |
||||
|
</q-card-section> |
||||
|
</div> |
||||
|
</div> |
||||
|
</q-card> |
||||
|
</q-dialog> |
||||
|
</template> |
||||
|
|
||||
|
<script setup lang="ts"> |
||||
|
import { ref, reactive, useAttrs } from 'vue'; |
||||
|
import { IconEnum } from '@/platform/enums'; |
||||
|
|
||||
|
const attrs = useAttrs(); |
||||
|
const props = defineProps({ |
||||
|
title: { type: String, default: '' }, |
||||
|
width: { type: String, default: '70%' }, |
||||
|
height: { type: String, default: '70%' }, |
||||
|
canMaximize: { type: Boolean, default: true }, |
||||
|
buttons: { type: Array, default: () => [] }, |
||||
|
}); |
||||
|
const emit = defineEmits<{ |
||||
|
( |
||||
|
e: 'maximized', // 全屏按钮点击事件 |
||||
|
maximized: boolean, // 第一个参数,true:全屏状态,false:退出全屏 |
||||
|
): void; |
||||
|
}>(); |
||||
|
|
||||
|
const dialogContentDivRef = ref(); |
||||
|
const dialog = reactive({ |
||||
|
show: false, |
||||
|
maximized: attrs.maximized || false, |
||||
|
}); |
||||
|
|
||||
|
const maximizeBtnClick = () => { |
||||
|
dialog.maximized = !dialog.maximized; |
||||
|
emit('maximized', dialog.maximized); |
||||
|
}; |
||||
|
|
||||
|
const show = () => { |
||||
|
dialog.show = true; |
||||
|
}; |
||||
|
const hide = () => { |
||||
|
dialog.show = false; |
||||
|
}; |
||||
|
|
||||
|
// 获取元素的绝对位置坐标(像对于浏览器视区左上角) |
||||
|
// const getElementViewPosition = (element) => { |
||||
|
// //计算x坐标 |
||||
|
// let actualLeft = element.offsetLeft; |
||||
|
// let xcurrent = element.offsetParent; |
||||
|
// while (xcurrent !== null) { |
||||
|
// actualLeft += xcurrent.offsetLeft + xcurrent.clientLeft; |
||||
|
// xcurrent = xcurrent.offsetParent; |
||||
|
// } |
||||
|
// let elementScrollLeft = document.documentElement.scrollLeft; |
||||
|
// if (document.compatMode == 'BackCompat') { |
||||
|
// elementScrollLeft = document.body.scrollLeft; |
||||
|
// } |
||||
|
// const left = actualLeft - elementScrollLeft; |
||||
|
|
||||
|
// //计算y坐标 |
||||
|
// let actualTop = element.offsetTop; |
||||
|
// let ycurrent = element.offsetParent; |
||||
|
// while (ycurrent !== null) { |
||||
|
// actualTop += ycurrent.offsetTop + ycurrent.clientTop; |
||||
|
// ycurrent = ycurrent.offsetParent; |
||||
|
// } |
||||
|
// let elementScrollTop = document.documentElement.scrollTop; |
||||
|
// if (document.compatMode == 'BackCompat') { |
||||
|
// elementScrollTop = document.body.scrollTop; |
||||
|
// } |
||||
|
// var right = actualTop - elementScrollTop; |
||||
|
// //返回结果 |
||||
|
// return { x: left, y: right }; |
||||
|
// }; |
||||
|
|
||||
|
// const getContentHeight = () => { |
||||
|
// if (dialogContentDivRef?.value) { |
||||
|
// console.info('dialogContentDivRef.value', dialogContentDivRef.value.offsetHeight); |
||||
|
// return { |
||||
|
// height: dialogContentDivRef.value.offsetHeight, |
||||
|
// y: getElementViewPosition(dialogContentDivRef.value).y, |
||||
|
// }; |
||||
|
// } |
||||
|
// return null; |
||||
|
// }; |
||||
|
const getContent = () => { |
||||
|
if (dialogContentDivRef?.value) { |
||||
|
return dialogContentDivRef.value; |
||||
|
} |
||||
|
return null; |
||||
|
}; |
||||
|
|
||||
|
defineExpose({ |
||||
|
show, |
||||
|
hide, |
||||
|
getContent, |
||||
|
}); |
||||
|
</script> |
@ -0,0 +1,11 @@ |
|||||
|
<template> |
||||
|
<div style="height:100%"> |
||||
|
|
||||
|
</div> |
||||
|
</template> |
||||
|
<script setup lang="ts"> |
||||
|
const props = defineProps({ |
||||
|
|
||||
|
}); |
||||
|
|
||||
|
</script> |
@ -0,0 +1,193 @@ |
|||||
|
<template> |
||||
|
<w-list-grid |
||||
|
ref="listGridRef" |
||||
|
:title="$t('org.wsp.framework.flowable.task.grid.title')" |
||||
|
:data-url="$fc.apiContextPath + '/system/process/query/task/isc'" |
||||
|
:form-fields="listGridFields.query" |
||||
|
:form-field-counter="{ xxs:1, xs:1, sm:2, md:2, lg:2, xl:2, xxl:2 }" |
||||
|
:table-columns="listGridFields.grid" |
||||
|
:detail-fields="listGridFields.detail" |
||||
|
:can-pageable="true" |
||||
|
:tool-bar-actions="listGridToolBarActions" |
||||
|
:table-scroll="{x:'100%',y:$fc.ui.contentHeight-$fc.ui.toolBarHeight-$fc.ui.tableHeaderHeight-$fc.ui.paginationHeight-40}" |
||||
|
@selected-rows-change="selectedRowsChange" |
||||
|
> |
||||
|
</w-list-grid> |
||||
|
<CompleteTaskDialog ref="completeTaskDialogRef" @after-completed="afterCompleted"></CompleteTaskDialog> |
||||
|
</template> |
||||
|
<script setup> |
||||
|
import { |
||||
|
computed, ref, onMounted, createVNode, toRaw |
||||
|
} from 'vue'; |
||||
|
import { useRouter, useRoute } from 'vue-router'; |
||||
|
import { useI18n } from 'vue-i18n'; |
||||
|
import { Modal, notification } from 'ant-design-vue'; |
||||
|
import { QuestionCircleOutlined } from '@ant-design/icons-vue'; |
||||
|
import { |
||||
|
useFrameworkConfiguration, useAxios, |
||||
|
useEnumOptions, getOptionText, useTrueFalseOptions, |
||||
|
useYesNoOptions, useDataComeFromOptions, useRoleOptions, |
||||
|
useOrgOptions, useTableColumn |
||||
|
} from 'framework-core'; |
||||
|
|
||||
|
import CompleteTaskDialog from './CompleteTaskDialog'; |
||||
|
|
||||
|
/** |
||||
|
* 定义组件支持的自定义事件 |
||||
|
*/ |
||||
|
const emit = defineEmits([ |
||||
|
'selectedRowsChange', // 选择的行发生改变 |
||||
|
]); |
||||
|
|
||||
|
const { t } = useI18n(); |
||||
|
const axios = useAxios(); |
||||
|
const fc = useFrameworkConfiguration(); |
||||
|
const router = useRouter(); |
||||
|
const route = useRoute(); |
||||
|
|
||||
|
const listGridRef = ref(null); |
||||
|
const completeTaskDialogRef = ref(null); |
||||
|
|
||||
|
const listGridFields = computed(() => { |
||||
|
return { |
||||
|
query: [ |
||||
|
{ component:'input', name:'processInstanceId', label:t('org.wsp.framework.flowable.task.entity.processInstanceId') }, |
||||
|
], |
||||
|
grid: [ |
||||
|
useTableColumn({ width:150, name:'name', title:t('org.wsp.framework.flowable.task.entity.name') }), |
||||
|
useTableColumn({ width:80, name:'owner', title:t('org.wsp.framework.flowable.task.entity.owner') }), |
||||
|
useTableColumn({ width:100, name:'assignee', title:t('org.wsp.framework.flowable.task.entity.assignee') }), |
||||
|
useTableColumn({ width:150, name:'createTime', title:t('org.wsp.framework.flowable.task.entity.createTime') }), |
||||
|
useTableColumn({ width:150, name:'claimTime', title:t('org.wsp.framework.flowable.task.entity.claimTime') }), |
||||
|
useTableColumn({ width:150, name:'processInstanceId', title:t('org.wsp.framework.flowable.task.entity.processInstanceId') }), |
||||
|
] |
||||
|
}; |
||||
|
}); |
||||
|
|
||||
|
const listGridToolBarActions = computed(() => { |
||||
|
return ['*query', 'reset', 'refresh', 'divider', |
||||
|
{ |
||||
|
icon: 'PartitionOutlined', |
||||
|
title: t('org.wsp.framework.flowable.action.showWorkflowDiagram'), |
||||
|
enableIf: (selectedRecords) => { |
||||
|
const record = listGridRef?.value?.getSelectedRow(); |
||||
|
return selectedRecords.length > 0; |
||||
|
}, |
||||
|
click: () => { |
||||
|
console.log('1'); |
||||
|
}, |
||||
|
}, |
||||
|
'divider', |
||||
|
{ |
||||
|
icon: 'RightCircleOutlined', |
||||
|
title: t('org.wsp.framework.flowable.action.complete.task'), |
||||
|
enableIf: (selectedRecords) => { |
||||
|
const record = listGridRef?.value?.getSelectedRow(); |
||||
|
return selectedRecords.length > 0; |
||||
|
}, |
||||
|
click: () => { |
||||
|
const record = listGridRef?.value?.getSelectedRow(); |
||||
|
completeTaskDialogRef?.value?.open(record.id); |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
icon: 'DownCircleOutlined', |
||||
|
title: t('org.wsp.framework.flowable.action.claim.task'), |
||||
|
enableIf: (selectedRecords) => { |
||||
|
const record = listGridRef?.value?.getSelectedRow(); |
||||
|
return selectedRecords.length > 0 && !record.assignee; |
||||
|
}, |
||||
|
click: () => { |
||||
|
const record = listGridRef?.value?.getSelectedRow(); |
||||
|
axios.post( |
||||
|
fc.apiContextPath + '/system/process/operation/claim/' + record.id, |
||||
|
null |
||||
|
).then(() => { |
||||
|
notification.success({ |
||||
|
message: t('operationSuccess'), |
||||
|
duration:2 |
||||
|
}); |
||||
|
listGridRef?.value.refresh(); |
||||
|
}); |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
icon: 'UpCircleOutlined', |
||||
|
title: t('org.wsp.framework.flowable.action.unclaim.task'), |
||||
|
enableIf: (selectedRecords) => { |
||||
|
const record = listGridRef?.value?.getSelectedRow(); |
||||
|
return selectedRecords.length > 0 && record.assignee; |
||||
|
}, |
||||
|
click: () => { |
||||
|
const record = listGridRef?.value?.getSelectedRow(); |
||||
|
axios.post( |
||||
|
fc.apiContextPath + '/system/process/operation/unClaim/' + record.id, |
||||
|
null |
||||
|
).then(() => { |
||||
|
notification.success({ |
||||
|
message: t('operationSuccess'), |
||||
|
duration:2 |
||||
|
}); |
||||
|
listGridRef?.value.refresh(); |
||||
|
}); |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
icon: 'RollbackOutlined', |
||||
|
title: t('org.wsp.framework.flowable.action.jump.task'), |
||||
|
enableIf: (selectedRecords) => { |
||||
|
const record = listGridRef?.value?.getSelectedRow(); |
||||
|
return selectedRecords.length > 0; |
||||
|
}, |
||||
|
click: () => { |
||||
|
console.log('2'); |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
icon: 'CloseCircleOutlined', |
||||
|
title: t('org.wsp.framework.flowable.action.terminate.processInstance'), |
||||
|
enableIf: (selectedRecords) => { |
||||
|
const record = listGridRef?.value?.getSelectedRow(); |
||||
|
return selectedRecords.length > 0; |
||||
|
}, |
||||
|
click: () => { |
||||
|
const record = listGridRef?.value?.getSelectedRow(); |
||||
|
axios.post( |
||||
|
fc.apiContextPath + '/system/process/operation/terminateProcessInstance/' + record.id, |
||||
|
null |
||||
|
).then(() => { |
||||
|
notification.success({ |
||||
|
message: t('operationSuccess'), |
||||
|
duration:2 |
||||
|
}); |
||||
|
listGridRef?.value.refresh(); |
||||
|
}); |
||||
|
}, |
||||
|
} |
||||
|
]; |
||||
|
}); |
||||
|
|
||||
|
const selectedRowsChange = (selectedRecords) => { |
||||
|
if (selectedRecords && selectedRecords.length > 0) { |
||||
|
emit('selectedRowsChange', selectedRecords[0]); |
||||
|
} else { |
||||
|
emit('selectedRowsChange', null); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
const refresh = () => { |
||||
|
listGridRef.value.refresh(); |
||||
|
}; |
||||
|
|
||||
|
const afterCompleted = () => { |
||||
|
notification.success({ |
||||
|
message: t('operationSuccess'), |
||||
|
duration:2 |
||||
|
}); |
||||
|
listGridRef?.value.refresh(); |
||||
|
}; |
||||
|
|
||||
|
defineExpose({ |
||||
|
refresh, |
||||
|
}); |
||||
|
</script> |
@ -0,0 +1,67 @@ |
|||||
|
<template> |
||||
|
<a-modal |
||||
|
v-model:visible="visible" |
||||
|
:force-render="true" |
||||
|
:mask-closable="false" |
||||
|
:title="$t('org.wsp.framework.flowable.action.complete.task')" |
||||
|
> |
||||
|
<a-form |
||||
|
:model="formModelRef" |
||||
|
layout="horizontal" |
||||
|
:label-col="{ span: 6 }" |
||||
|
:wrapper-col="{ span: 18 }" |
||||
|
autocomplete="off" |
||||
|
> |
||||
|
<a-form-item name="variables" :label="$t('org.wsp.framework.flowable.instance.form.variables')"> |
||||
|
<a-textarea v-model:value="formModelRef.variables" :rows="6" /> |
||||
|
</a-form-item> |
||||
|
<a-form-item name="transientVariables" :label="$t('org.wsp.framework.flowable.instance.form.transientVariables')"> |
||||
|
<a-textarea v-model:value="formModelRef.transientVariables" :rows="6" /> |
||||
|
</a-form-item> |
||||
|
</a-form> |
||||
|
<template #footer> |
||||
|
<WorkflowAction |
||||
|
ref="workflowActionRef" |
||||
|
action-url="/system/process/operation/complete/" |
||||
|
@after-submit="afterSubmit" |
||||
|
> |
||||
|
</WorkflowAction> |
||||
|
</template> |
||||
|
</a-modal> |
||||
|
</template> |
||||
|
<script setup> |
||||
|
import { ref, reactive, onMounted } from 'vue'; |
||||
|
import { useI18n } from 'vue-i18n'; |
||||
|
import WorkflowAction from './WorkflowAction'; |
||||
|
|
||||
|
/** |
||||
|
* 定义组件支持的自定义事件 |
||||
|
*/ |
||||
|
const emit = defineEmits([ |
||||
|
'afterCompleted', // 提交成功后 |
||||
|
]); |
||||
|
|
||||
|
const { t } = useI18n(); |
||||
|
|
||||
|
const visible = ref(false); |
||||
|
const formModelRef = reactive({}); |
||||
|
const workflowActionRef = ref([]); |
||||
|
|
||||
|
const open = (taskId) => { |
||||
|
const transientVariables = {}; |
||||
|
transientVariables.task_treatment = '此处填写处理意见'; |
||||
|
formModelRef.transientVariables = JSON.stringify(transientVariables, null, 2); |
||||
|
workflowActionRef.value.setTaskId(taskId); |
||||
|
workflowActionRef.value.setDataRef(formModelRef); |
||||
|
visible.value = true; |
||||
|
}; |
||||
|
|
||||
|
const afterSubmit = (taskId) => { |
||||
|
visible.value = false; |
||||
|
emit('afterCompleted'); |
||||
|
}; |
||||
|
|
||||
|
defineExpose({ |
||||
|
open |
||||
|
}); |
||||
|
</script> |
@ -0,0 +1,69 @@ |
|||||
|
<template> |
||||
|
<a-modal |
||||
|
v-model:visible="visible" |
||||
|
:force-render="true" |
||||
|
:mask-closable="false" |
||||
|
:title="$t('org.wsp.framework.flowable.window.selectAssignee.title')" |
||||
|
centered |
||||
|
@ok="okHandle" |
||||
|
> |
||||
|
<a-space direction="vertical"> |
||||
|
<div>{{ t('org.wsp.framework.flowable.task.tip.selectAssignee') }}</div> |
||||
|
<a-form |
||||
|
:model="formModelRef" |
||||
|
layout="horizontal" |
||||
|
:label-col="{ span: 6 }" |
||||
|
:wrapper-col="{ span: 18 }" |
||||
|
autocomplete="off" |
||||
|
> |
||||
|
<a-form-item name="assignee" :label="$t('org.wsp.framework.flowable.task.entity.assignee')"> |
||||
|
<a-select v-model:value="formModelRef.assignee" :options="assigneeOptionsRef" /> |
||||
|
</a-form-item> |
||||
|
</a-form> |
||||
|
</a-space> |
||||
|
</a-modal> |
||||
|
</template> |
||||
|
<script setup> |
||||
|
import { |
||||
|
ref, reactive, toRaw, computed, onMounted |
||||
|
} from 'vue'; |
||||
|
import { useI18n } from 'vue-i18n'; |
||||
|
import { notification } from 'ant-design-vue'; |
||||
|
import { useFrameworkConfiguration, useAxios } from 'framework-core'; |
||||
|
|
||||
|
/** |
||||
|
* 定义组件支持的自定义事件 |
||||
|
*/ |
||||
|
const emit = defineEmits([ |
||||
|
'assigneeSelected', // 选择了候选人 |
||||
|
]); |
||||
|
|
||||
|
const fc = useFrameworkConfiguration(); |
||||
|
const axios = useAxios(); |
||||
|
const { t } = useI18n(); |
||||
|
|
||||
|
const visible = ref(false); |
||||
|
const formModelRef = reactive({}); |
||||
|
const assigneeOptionsRef = ref(null); |
||||
|
|
||||
|
const okHandle = () => { |
||||
|
emit('assigneeSelected', formModelRef.assignee); |
||||
|
visible.value = false; |
||||
|
}; |
||||
|
|
||||
|
const open = (assignees) => { |
||||
|
visible.value = true; |
||||
|
formModelRef.assignee = null; |
||||
|
const result = []; |
||||
|
if (assignees) { |
||||
|
for (let i = 0; i < assignees.length; i++) { |
||||
|
result.push({ value : assignees[i].loginName, label : assignees[i].loginName + '/' + assignees[i].userName, text : assignees[i].loginName + '/' + assignees[i].userName }); |
||||
|
} |
||||
|
} |
||||
|
assigneeOptionsRef.value = result; |
||||
|
}; |
||||
|
|
||||
|
defineExpose({ |
||||
|
open |
||||
|
}); |
||||
|
</script> |
@ -0,0 +1,96 @@ |
|||||
|
<template> |
||||
|
<div class="flex justify-end gap-4"> |
||||
|
<q-btn v-for="action in actionsRef" :key="action.name" :label="action.title" @click="buttonClick(action)"></q-btn> |
||||
|
</div> |
||||
|
</template> |
||||
|
<script setup lang="ts"> |
||||
|
import { ref } from 'vue'; |
||||
|
import { useI18n } from 'vue-i18n'; |
||||
|
import { Environment, axios } from '@/platform'; |
||||
|
|
||||
|
/** |
||||
|
* 定义组件支持的自定义属性 |
||||
|
*/ |
||||
|
const props = defineProps({ |
||||
|
// 获取回退按钮列表的 url,由系统自动提供,使用者无需指定 |
||||
|
gobackActionUrl: { type: String, default: '/api/flowable/process/operation/getGobacks' }, |
||||
|
// 操作对应的服务器端控制器 url |
||||
|
actionUrl: { type: String, default: undefined }, |
||||
|
// 任务ID |
||||
|
taskId: { type: String, default: undefined }, |
||||
|
// 临时变量 |
||||
|
transientVariables: { type: Object, default: undefined }, |
||||
|
// 回退按钮默认宽度 |
||||
|
actionButtonWidth: { type: Number, default: 100 }, |
||||
|
// 默认按钮 |
||||
|
defaultActionButtons: { type: [Array, Object], default: undefined }, |
||||
|
// 默认按钮对齐方式 |
||||
|
defaultActionButtonsAlign: { type: String, default: 'right' }, |
||||
|
}); |
||||
|
|
||||
|
/** |
||||
|
* 定义组件支持的自定义事件 |
||||
|
*/ |
||||
|
const emit = defineEmits([ |
||||
|
'afterSubmit', // 提交成功后 |
||||
|
]); |
||||
|
|
||||
|
const { t } = useI18n(); |
||||
|
const taskIdRef = ref(props.taskId); |
||||
|
const actionsRef = ref([]); |
||||
|
|
||||
|
const buildMembers = (gobacks) => { |
||||
|
actionsRef.value.splice(0, actionsRef.value.length); |
||||
|
|
||||
|
const members = []; |
||||
|
// 添加默认按钮(左边) |
||||
|
if (props.defaultActionButtonsAlign === 'left') { |
||||
|
if (props.defaultActionButtons && props.defaultActionButtons && props.defaultActionButtons.length > 0) { |
||||
|
for (let i = 0; i < props.defaultActionButtons.length; i++) { |
||||
|
members.push(props.defaultActionButtons[i]); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 回退按钮 |
||||
|
if (gobacks) { |
||||
|
for (let i = 0; i < gobacks.length; i++) { |
||||
|
const goback = gobacks[i]; |
||||
|
const transientVariables = {}; |
||||
|
transientVariables[goback.variableName] = goback.variableValue; |
||||
|
members.push({ |
||||
|
title: goback.title || t('goback'), |
||||
|
transientVariables, |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 提交按钮 |
||||
|
members.push({ title: t('submit') }); |
||||
|
|
||||
|
// 添加默认按钮(右边) |
||||
|
if (props.defaultActionButtonsAlign === 'right') { |
||||
|
if (props.defaultActionButtons && props.defaultActionButtons && props.defaultActionButtons.length > 0) { |
||||
|
for (let i = 0; i < props.defaultActionButtons.length; i++) { |
||||
|
members.push(props.defaultActionButtons[i]); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 更新到响应式变量中 |
||||
|
for (let i = 0; i < members.length; i++) { |
||||
|
actionsRef.value.push(members[i]); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
const setTaskId = (taskId) => { |
||||
|
taskIdRef.value = taskId; |
||||
|
axios.get(Environment.apiContextPath(props.gobackActionUrl + '/' + taskId)).then((data) => { |
||||
|
buildMembers(data.data); |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
defineExpose({ |
||||
|
setTaskId, |
||||
|
}); |
||||
|
</script> |
@ -0,0 +1,158 @@ |
|||||
|
<template> |
||||
|
<div> |
||||
|
<a-button v-for="action in actionsRef" @click="buttonClick(action)">{{ action.title }}</a-button> |
||||
|
</div> |
||||
|
<SelectAssigneeDialog ref="selectAssigneeDialogRef" @assignee-selected="assigneeSelected"></SelectAssigneeDialog> |
||||
|
</template> |
||||
|
<script setup> |
||||
|
import { |
||||
|
ref, reactive, toRaw, computed, onMounted |
||||
|
} from 'vue'; |
||||
|
import { useI18n } from 'vue-i18n'; |
||||
|
import { useStore } from 'vuex'; |
||||
|
import { notification } from 'ant-design-vue'; |
||||
|
import { useFrameworkConfiguration, useAxios } from 'framework-core'; |
||||
|
import SelectAssigneeDialog from './SelectAssigneeDialog'; |
||||
|
|
||||
|
/** |
||||
|
* 定义组件支持的自定义属性 |
||||
|
*/ |
||||
|
const props = defineProps({ |
||||
|
// 获取回退按钮列表的 url,由系统自动提供,使用者无需指定 |
||||
|
gobackActionUrl : { type: String, default: '/system/process/operation/getGobacks/' }, |
||||
|
// 操作对应的服务器端控制器 url |
||||
|
actionUrl : { type: String, default: undefined }, |
||||
|
// 任务ID |
||||
|
taskId : { type: String, default: undefined }, |
||||
|
// 临时变量 |
||||
|
transientVariables : { type: Object, default: undefined }, |
||||
|
// 回退按钮默认宽度 |
||||
|
actionButtonWidth : { type: Number, default: 100 }, |
||||
|
// 默认按钮 |
||||
|
defaultActionButtons : { type: [Array, Object], default: undefined }, |
||||
|
// 默认按钮对齐方式 |
||||
|
defaultActionButtonsAlign : { type: String, default: 'right' }, |
||||
|
}); |
||||
|
|
||||
|
/** |
||||
|
* 定义组件支持的自定义事件 |
||||
|
*/ |
||||
|
const emit = defineEmits([ |
||||
|
'afterSubmit', // 提交成功后 |
||||
|
]); |
||||
|
|
||||
|
const fc = useFrameworkConfiguration(); |
||||
|
const store = useStore(); |
||||
|
const axios = useAxios(); |
||||
|
const { t } = useI18n(); |
||||
|
|
||||
|
const actionIdRef = ref(null); |
||||
|
const selectAssigneeDialogRef = ref(null); |
||||
|
|
||||
|
let dataRef = null; |
||||
|
let currentAction = null; |
||||
|
const actionsRef = reactive([]); |
||||
|
|
||||
|
const buildMembers = (gobacks) => { |
||||
|
actionsRef.splice(0, actionsRef.length); |
||||
|
|
||||
|
const members = []; |
||||
|
// 添加默认按钮(左边) |
||||
|
if (props.defaultActionButtonsAlign === 'left') { |
||||
|
if (props.defaultActionButtons && props.defaultActionButtons && props.defaultActionButtons.length > 0) { |
||||
|
for (let i = 0; i < props.defaultActionButtons.length; i++) { |
||||
|
members.push(props.defaultActionButtons[i]); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 回退按钮 |
||||
|
if (gobacks) { |
||||
|
for (let i = 0; i < gobacks.length; i++) { |
||||
|
const goback = gobacks[i]; |
||||
|
const transientVariables = {}; |
||||
|
transientVariables[goback.variableName] = goback.variableValue; |
||||
|
members.push({ |
||||
|
title : goback.title || t('goback'), |
||||
|
transientVariables, |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 提交按钮 |
||||
|
members.push({ title: t('submit') }); |
||||
|
|
||||
|
// 添加默认按钮(右边) |
||||
|
if (props.defaultActionButtonsAlign === 'right') { |
||||
|
if (props.defaultActionButtons && props.defaultActionButtons && props.defaultActionButtons.length > 0) { |
||||
|
for (let i = 0; i < props.defaultActionButtons.length; i++) { |
||||
|
members.push(props.defaultActionButtons[i]); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 更新到响应式变量中 |
||||
|
for (let i = 0; i < members.length; i++) { |
||||
|
actionsRef.push(members[i]); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
const setTaskId = (taskId) => { |
||||
|
actionIdRef.value = taskId; |
||||
|
axios.get( |
||||
|
fc.apiContextPath + props.gobackActionUrl + taskId, |
||||
|
).then((data) => { |
||||
|
buildMembers(data.data); |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
const buttonClick = (action) => { |
||||
|
currentAction = action; |
||||
|
// 首先清除临时变量,为第二次提交初始化数据 |
||||
|
const transientVariables = {}; |
||||
|
|
||||
|
// 如果是点击了“回退”按钮,将回退控制变量付给临时变量 |
||||
|
axios.post( |
||||
|
fc.apiContextPath + props.actionUrl + actionIdRef.value, |
||||
|
action |
||||
|
).then((data) => { |
||||
|
const rawData = data.data; |
||||
|
if (rawData.code === 0) { // 操作成功 |
||||
|
emit('afterSubmit'); |
||||
|
} else if (rawData.code === 1) { // 需要选择处理人 |
||||
|
selectAssigneeDialogRef.value.open(rawData.assignees); |
||||
|
} |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
const assigneeSelected = (assignee) => { |
||||
|
const data = currentAction; |
||||
|
if (dataRef.variables) { |
||||
|
data.variables = JSON.parse(dataRef.variables); |
||||
|
} |
||||
|
if (dataRef.transientVariables) { |
||||
|
data.transientVariables = JSON.parse(dataRef.transientVariables); |
||||
|
data.transientVariables.assignee = assignee; |
||||
|
} |
||||
|
axios.post( |
||||
|
fc.apiContextPath + props.actionUrl + actionIdRef.value, |
||||
|
data |
||||
|
).then((response) => { |
||||
|
const rawData = response.data; |
||||
|
if (rawData.code === 0) { // 操作成功 |
||||
|
emit('afterSubmit'); |
||||
|
} |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
const setDataRef = (_dataRef) => { |
||||
|
if (_dataRef) { |
||||
|
dataRef = _dataRef; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
defineExpose({ |
||||
|
setTaskId, |
||||
|
setDataRef |
||||
|
}); |
||||
|
</script> |
@ -0,0 +1,25 @@ |
|||||
|
import { Tools } from '@/platform'; |
||||
|
|
||||
|
const PassOrNotFormater = (value) => { |
||||
|
if (Tools.isUndefinedOrNull(value)) { |
||||
|
return ''; |
||||
|
} |
||||
|
if (value === 'PASSED') { |
||||
|
return { |
||||
|
componentType: 'QIcon', |
||||
|
attrs: { name: 'bi-check-circle', size: '20px', color: 'green' }, |
||||
|
}; |
||||
|
} else if (value === 'UN_PASSED') { |
||||
|
return { |
||||
|
componentType: 'QIcon', |
||||
|
attrs: { name: 'bi-x-circle', size: '20px', color: 'red' }, |
||||
|
}; |
||||
|
} else if (value === 'ERROR') { |
||||
|
return { |
||||
|
componentType: 'QIcon', |
||||
|
attrs: { name: 'bi-x-circle', size: '20px', color: 'red' }, |
||||
|
}; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
export default PassOrNotFormater; |
@ -1,258 +1,85 @@ |
|||||
<template> |
<template> |
||||
<div class="q-pa-md"> |
<w-dialog v-model="isShow"> |
||||
<q-table flat bordered separator="cell" title="Treats" :rows="rows2" :columns="columns" row-key="name" hide-no-data> |
<w-grid |
||||
<template v-if="rows && rows.length > 0" #header="props"> |
ref="userGridRef" |
||||
<q-tr :props="props"> |
:title="$t('system.user.grid.title')" |
||||
<q-th v-for="col in props.cols" :key="col.name" :props="props" class="text-italic text-purple"> |
:config-button="true" |
||||
{{ col.label }} |
selection="multiple" |
||||
</q-th> |
:checkbox-selection="true" |
||||
</q-tr> |
:data-url="Environment.apiContextPath('/api/system/user')" |
||||
</template> |
:pagination="{ |
||||
<template v-else #header="props"> |
sortBy: 'loginName', |
||||
<q-tr :props="props"> |
descending: false, |
||||
<q-th v-for="col in props.cols" :key="col.name" :props="props" class="text-italic text-purple"> |
}" |
||||
{{ col.label }} |
:query-form-cols-num="3" |
||||
</q-th> |
:query-form-fields="[ |
||||
</q-tr> |
{ name: 'loginName', label: $t('loginName'), type: 'text' }, |
||||
<q-tr :props="props" style="height: 300px"> </q-tr> |
{ name: 'userName', label: $t('userName'), type: 'text' }, |
||||
|
{ name: 'enable', label: $t('isEnable'), type: 'select' }, |
||||
|
]" |
||||
|
:toolbar-configure="{ noIcon: false }" |
||||
|
:toolbar-actions="[ |
||||
|
'query', |
||||
|
'refresh', |
||||
|
'separator', |
||||
|
'add', |
||||
|
'clone', |
||||
|
'edit', |
||||
|
'remove', |
||||
|
'separator', |
||||
|
{ |
||||
|
name: 'setPassword', |
||||
|
label: $t('system.user.grid.toolbar.setPassword'), |
||||
|
icon: 'bi-shield-check', |
||||
|
enableIf: function (arg) { |
||||
|
return arg.selected; |
||||
|
}, |
||||
|
click: function (arg) {}, |
||||
|
}, |
||||
|
{ |
||||
|
name: 'setAllPassword', |
||||
|
label: $t('system.user.grid.toolbar.setAllPassword'), |
||||
|
icon: 'bi-shield', |
||||
|
click: function () {}, |
||||
|
}, |
||||
|
'separator', |
||||
|
{ |
||||
|
name: 'resetPassword', |
||||
|
label: $t('system.user.grid.toolbar.resetPassword'), |
||||
|
icon: 'bi-shield-fill-check', |
||||
|
enableIf: function (arg) { |
||||
|
return arg.selected; |
||||
|
}, |
||||
|
click: function (arg) {}, |
||||
|
}, |
||||
|
{ |
||||
|
name: 'resetAllPassword', |
||||
|
label: $t('system.user.grid.toolbar.resetAllPassword'), |
||||
|
icon: 'bi-shield-fill', |
||||
|
click: function () {}, |
||||
|
}, |
||||
|
'separator', |
||||
|
'view', |
||||
|
'separator', |
||||
|
'export', |
||||
|
]" |
||||
|
:columns="[ |
||||
|
{ width: 150, name: 'loginName', label: $t('loginName') }, |
||||
|
{ width: '100%', name: 'userName', label: $t('userName') }, |
||||
|
{ |
||||
|
width: 150, |
||||
|
name: 'enable', |
||||
|
label: $t('status'), |
||||
|
}, |
||||
|
{ width: 100, name: 'lastModifier', label: $t('lastModifier') }, |
||||
|
{ width: 110, name: 'lastModifyDate', label: $t('lastModifyDate') }, |
||||
|
]" |
||||
|
></w-grid> |
||||
|
</w-dialog> |
||||
</template> |
</template> |
||||
|
|
||||
<template #body="props"> |
<script setup lang="ts"> |
||||
<q-tr :props="props"> |
import { Environment } from '@/platform'; |
||||
<q-td key="name" :props="props"> |
|
||||
{{ props.row.name }} |
|
||||
</q-td> |
|
||||
<q-td key="calories" :props="props"> |
|
||||
<q-badge color="green"> |
|
||||
{{ props.row.calories }} |
|
||||
</q-badge> |
|
||||
</q-td> |
|
||||
<q-td key="fat" :props="props"> |
|
||||
<q-badge color="purple"> |
|
||||
{{ props.row.fat }} |
|
||||
</q-badge> |
|
||||
</q-td> |
|
||||
<q-td key="carbs" :props="props"> |
|
||||
<q-badge color="orange"> |
|
||||
{{ props.row.carbs }} |
|
||||
</q-badge> |
|
||||
</q-td> |
|
||||
<q-td key="protein" :props="props"> |
|
||||
<q-badge color="primary"> |
|
||||
{{ props.row.protein }} |
|
||||
</q-badge> |
|
||||
</q-td> |
|
||||
<q-td key="sodium" :props="props"> |
|
||||
<q-badge color="teal"> |
|
||||
{{ props.row.sodium }} |
|
||||
</q-badge> |
|
||||
</q-td> |
|
||||
<q-td key="calcium" :props="props"> |
|
||||
<q-badge color="accent"> |
|
||||
{{ props.row.calcium }} |
|
||||
</q-badge> |
|
||||
</q-td> |
|
||||
<q-td key="iron" :props="props"> |
|
||||
<q-badge color="amber"> |
|
||||
{{ props.row.iron }} |
|
||||
</q-badge> |
|
||||
</q-td> |
|
||||
</q-tr> |
|
||||
</template> |
|
||||
</q-table> |
|
||||
</div> |
|
||||
</template> |
|
||||
|
|
||||
<script> |
const isShow = true; |
||||
import { ref } from 'vue'; |
|
||||
const columns = [ |
|
||||
{ |
|
||||
name: 'name', |
|
||||
required: true, |
|
||||
label: 'Dessert (100g serving)', |
|
||||
align: 'left', |
|
||||
field: (row) => row.name, |
|
||||
format: (val) => `${val}`, |
|
||||
sortable: true, |
|
||||
}, |
|
||||
{ name: 'calories', align: 'center', label: 'Calories', field: 'calories', sortable: true }, |
|
||||
{ name: 'fat', label: 'Fat (g)', field: 'fat', sortable: true }, |
|
||||
{ name: 'carbs', label: 'Carbs (g)', field: 'carbs' }, |
|
||||
{ name: 'protein', label: 'Protein (g)', field: 'protein' }, |
|
||||
{ name: 'sodium', label: 'Sodium (mg)', field: 'sodium' }, |
|
||||
{ name: 'calcium', label: 'Calcium (%)', field: 'calcium', sortable: true, sort: (a, b) => parseInt(a, 10) - parseInt(b, 10) }, |
|
||||
{ name: 'iron', label: 'Iron (%)', field: 'iron', sortable: true, sort: (a, b) => parseInt(a, 10) - parseInt(b, 10) }, |
|
||||
]; |
|
||||
const rows = []; |
|
||||
const rows2 = [ |
|
||||
{ |
|
||||
name: 'Frozen Yogurt', |
|
||||
calories: 159, |
|
||||
fat: 6.0, |
|
||||
carbs: 24, |
|
||||
protein: 4.0, |
|
||||
sodium: 87, |
|
||||
calcium: '14%', |
|
||||
iron: '1%', |
|
||||
}, |
|
||||
{ |
|
||||
name: 'Ice cream sandwich', |
|
||||
calories: 237, |
|
||||
fat: 9.0, |
|
||||
carbs: 37, |
|
||||
protein: 4.3, |
|
||||
sodium: 129, |
|
||||
calcium: '8%', |
|
||||
iron: '1%', |
|
||||
}, |
|
||||
{ |
|
||||
name: 'Eclair', |
|
||||
calories: 262, |
|
||||
fat: 16.0, |
|
||||
carbs: 23, |
|
||||
protein: 6.0, |
|
||||
sodium: 337, |
|
||||
calcium: '6%', |
|
||||
iron: '7%', |
|
||||
}, |
|
||||
{ |
|
||||
name: 'Cupcake', |
|
||||
calories: 305, |
|
||||
fat: 3.7, |
|
||||
carbs: 67, |
|
||||
protein: 4.3, |
|
||||
sodium: 413, |
|
||||
calcium: '3%', |
|
||||
iron: '8%', |
|
||||
}, |
|
||||
{ |
|
||||
name: 'Gingerbread', |
|
||||
calories: 356, |
|
||||
fat: 16.0, |
|
||||
carbs: 49, |
|
||||
protein: 3.9, |
|
||||
sodium: 327, |
|
||||
calcium: '7%', |
|
||||
iron: '16%', |
|
||||
}, |
|
||||
{ |
|
||||
name: 'Jelly bean', |
|
||||
calories: 375, |
|
||||
fat: 0.0, |
|
||||
carbs: 94, |
|
||||
protein: 0.0, |
|
||||
sodium: 50, |
|
||||
calcium: '0%', |
|
||||
iron: '0%', |
|
||||
}, |
|
||||
{ |
|
||||
name: 'Lollipop', |
|
||||
calories: 392, |
|
||||
fat: 0.2, |
|
||||
carbs: 98, |
|
||||
protein: 0, |
|
||||
sodium: 38, |
|
||||
calcium: '0%', |
|
||||
iron: '2%', |
|
||||
}, |
|
||||
{ |
|
||||
name: 'Honeycomb', |
|
||||
calories: 408, |
|
||||
fat: 3.2, |
|
||||
carbs: 87, |
|
||||
protein: 6.5, |
|
||||
sodium: 562, |
|
||||
calcium: '0%', |
|
||||
iron: '45%', |
|
||||
}, |
|
||||
{ |
|
||||
name: 'Donut', |
|
||||
calories: 452, |
|
||||
fat: 25.0, |
|
||||
carbs: 51, |
|
||||
protein: 4.9, |
|
||||
sodium: 326, |
|
||||
calcium: '2%', |
|
||||
iron: '22%', |
|
||||
}, |
|
||||
{ |
|
||||
name: 'KitKat', |
|
||||
calories: 518, |
|
||||
fat: 26.0, |
|
||||
carbs: 65, |
|
||||
protein: 7, |
|
||||
sodium: 54, |
|
||||
calcium: '12%', |
|
||||
iron: '6%', |
|
||||
}, |
|
||||
{ |
|
||||
name: 'KitKat', |
|
||||
calories: 518, |
|
||||
fat: 26.0, |
|
||||
carbs: 65, |
|
||||
protein: 7, |
|
||||
sodium: 54, |
|
||||
calcium: '12%', |
|
||||
iron: '6%', |
|
||||
}, |
|
||||
{ |
|
||||
name: 'KitKat', |
|
||||
calories: 518, |
|
||||
fat: 26.0, |
|
||||
carbs: 65, |
|
||||
protein: 7, |
|
||||
sodium: 54, |
|
||||
calcium: '12%', |
|
||||
iron: '6%', |
|
||||
}, |
|
||||
{ |
|
||||
name: 'KitKat', |
|
||||
calories: 518, |
|
||||
fat: 26.0, |
|
||||
carbs: 65, |
|
||||
protein: 7, |
|
||||
sodium: 54, |
|
||||
calcium: '12%', |
|
||||
iron: '6%', |
|
||||
}, |
|
||||
{ |
|
||||
name: 'KitKat', |
|
||||
calories: 518, |
|
||||
fat: 26.0, |
|
||||
carbs: 65, |
|
||||
protein: 7, |
|
||||
sodium: 54, |
|
||||
calcium: '12%', |
|
||||
iron: '6%', |
|
||||
}, |
|
||||
{ |
|
||||
name: 'KitKat', |
|
||||
calories: 518, |
|
||||
fat: 26.0, |
|
||||
carbs: 65, |
|
||||
protein: 7, |
|
||||
sodium: 54, |
|
||||
calcium: '12%', |
|
||||
iron: '6%', |
|
||||
}, |
|
||||
{ |
|
||||
name: 'KitKat', |
|
||||
calories: 518, |
|
||||
fat: 26.0, |
|
||||
carbs: 65, |
|
||||
protein: 7, |
|
||||
sodium: 54, |
|
||||
calcium: '12%', |
|
||||
iron: '6%', |
|
||||
}, |
|
||||
]; |
|
||||
|
|
||||
export default { |
|
||||
setup() { |
|
||||
return { |
|
||||
columns, |
|
||||
rows, |
|
||||
}; |
|
||||
}, |
|
||||
}; |
|
||||
</script> |
</script> |
||||
|
@ -0,0 +1,25 @@ |
|||||
|
import { Tools } from '@/platform'; |
||||
|
|
||||
|
const PassOrNotFormater = (value) => { |
||||
|
if (Tools.isUndefinedOrNull(value)) { |
||||
|
return ''; |
||||
|
} |
||||
|
if (value === 'PASSED') { |
||||
|
return { |
||||
|
componentType: 'QIcon', |
||||
|
attrs: { name: 'bi-check-circle', size: '20px', color: 'green' }, |
||||
|
}; |
||||
|
} else if (value === 'UN_PASSED') { |
||||
|
return { |
||||
|
componentType: 'QIcon', |
||||
|
attrs: { name: 'bi-x-circle', size: '20px', color: 'red' }, |
||||
|
}; |
||||
|
} else if (value === 'ERROR') { |
||||
|
return { |
||||
|
componentType: 'QIcon', |
||||
|
attrs: { name: 'bi-x-circle', size: '20px', color: 'red' }, |
||||
|
}; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
export default PassOrNotFormater; |
@ -1,258 +1,85 @@ |
|||||
<template> |
<template> |
||||
<div class="q-pa-md"> |
<w-dialog v-model="isShow"> |
||||
<q-table flat bordered separator="cell" title="Treats" :rows="rows2" :columns="columns" row-key="name" hide-no-data> |
<w-grid |
||||
<template v-if="rows && rows.length > 0" #header="props"> |
ref="userGridRef" |
||||
<q-tr :props="props"> |
:title="$t('system.user.grid.title')" |
||||
<q-th v-for="col in props.cols" :key="col.name" :props="props" class="text-italic text-purple"> |
:config-button="true" |
||||
{{ col.label }} |
selection="multiple" |
||||
</q-th> |
:checkbox-selection="true" |
||||
</q-tr> |
:data-url="Environment.apiContextPath('/api/system/user')" |
||||
</template> |
:pagination="{ |
||||
<template v-else #header="props"> |
sortBy: 'loginName', |
||||
<q-tr :props="props"> |
descending: false, |
||||
<q-th v-for="col in props.cols" :key="col.name" :props="props" class="text-italic text-purple"> |
}" |
||||
{{ col.label }} |
:query-form-cols-num="3" |
||||
</q-th> |
:query-form-fields="[ |
||||
</q-tr> |
{ name: 'loginName', label: $t('loginName'), type: 'text' }, |
||||
<q-tr :props="props" style="height: 300px"> </q-tr> |
{ name: 'userName', label: $t('userName'), type: 'text' }, |
||||
|
{ name: 'enable', label: $t('isEnable'), type: 'select' }, |
||||
|
]" |
||||
|
:toolbar-configure="{ noIcon: false }" |
||||
|
:toolbar-actions="[ |
||||
|
'query', |
||||
|
'refresh', |
||||
|
'separator', |
||||
|
'add', |
||||
|
'clone', |
||||
|
'edit', |
||||
|
'remove', |
||||
|
'separator', |
||||
|
{ |
||||
|
name: 'setPassword', |
||||
|
label: $t('system.user.grid.toolbar.setPassword'), |
||||
|
icon: 'bi-shield-check', |
||||
|
enableIf: function (arg) { |
||||
|
return arg.selected; |
||||
|
}, |
||||
|
click: function (arg) {}, |
||||
|
}, |
||||
|
{ |
||||
|
name: 'setAllPassword', |
||||
|
label: $t('system.user.grid.toolbar.setAllPassword'), |
||||
|
icon: 'bi-shield', |
||||
|
click: function () {}, |
||||
|
}, |
||||
|
'separator', |
||||
|
{ |
||||
|
name: 'resetPassword', |
||||
|
label: $t('system.user.grid.toolbar.resetPassword'), |
||||
|
icon: 'bi-shield-fill-check', |
||||
|
enableIf: function (arg) { |
||||
|
return arg.selected; |
||||
|
}, |
||||
|
click: function (arg) {}, |
||||
|
}, |
||||
|
{ |
||||
|
name: 'resetAllPassword', |
||||
|
label: $t('system.user.grid.toolbar.resetAllPassword'), |
||||
|
icon: 'bi-shield-fill', |
||||
|
click: function () {}, |
||||
|
}, |
||||
|
'separator', |
||||
|
'view', |
||||
|
'separator', |
||||
|
'export', |
||||
|
]" |
||||
|
:columns="[ |
||||
|
{ width: 150, name: 'loginName', label: $t('loginName') }, |
||||
|
{ width: '100%', name: 'userName', label: $t('userName') }, |
||||
|
{ |
||||
|
width: 150, |
||||
|
name: 'enable', |
||||
|
label: $t('status'), |
||||
|
}, |
||||
|
{ width: 100, name: 'lastModifier', label: $t('lastModifier') }, |
||||
|
{ width: 110, name: 'lastModifyDate', label: $t('lastModifyDate') }, |
||||
|
]" |
||||
|
></w-grid> |
||||
|
</w-dialog> |
||||
</template> |
</template> |
||||
|
|
||||
<template #body="props"> |
<script setup lang="ts"> |
||||
<q-tr :props="props"> |
import { Environment } from '@/platform'; |
||||
<q-td key="name" :props="props"> |
|
||||
{{ props.row.name }} |
|
||||
</q-td> |
|
||||
<q-td key="calories" :props="props"> |
|
||||
<q-badge color="green"> |
|
||||
{{ props.row.calories }} |
|
||||
</q-badge> |
|
||||
</q-td> |
|
||||
<q-td key="fat" :props="props"> |
|
||||
<q-badge color="purple"> |
|
||||
{{ props.row.fat }} |
|
||||
</q-badge> |
|
||||
</q-td> |
|
||||
<q-td key="carbs" :props="props"> |
|
||||
<q-badge color="orange"> |
|
||||
{{ props.row.carbs }} |
|
||||
</q-badge> |
|
||||
</q-td> |
|
||||
<q-td key="protein" :props="props"> |
|
||||
<q-badge color="primary"> |
|
||||
{{ props.row.protein }} |
|
||||
</q-badge> |
|
||||
</q-td> |
|
||||
<q-td key="sodium" :props="props"> |
|
||||
<q-badge color="teal"> |
|
||||
{{ props.row.sodium }} |
|
||||
</q-badge> |
|
||||
</q-td> |
|
||||
<q-td key="calcium" :props="props"> |
|
||||
<q-badge color="accent"> |
|
||||
{{ props.row.calcium }} |
|
||||
</q-badge> |
|
||||
</q-td> |
|
||||
<q-td key="iron" :props="props"> |
|
||||
<q-badge color="amber"> |
|
||||
{{ props.row.iron }} |
|
||||
</q-badge> |
|
||||
</q-td> |
|
||||
</q-tr> |
|
||||
</template> |
|
||||
</q-table> |
|
||||
</div> |
|
||||
</template> |
|
||||
|
|
||||
<script> |
const isShow = true; |
||||
import { ref } from 'vue'; |
|
||||
const columns = [ |
|
||||
{ |
|
||||
name: 'name', |
|
||||
required: true, |
|
||||
label: 'Dessert (100g serving)', |
|
||||
align: 'left', |
|
||||
field: (row) => row.name, |
|
||||
format: (val) => `${val}`, |
|
||||
sortable: true, |
|
||||
}, |
|
||||
{ name: 'calories', align: 'center', label: 'Calories', field: 'calories', sortable: true }, |
|
||||
{ name: 'fat', label: 'Fat (g)', field: 'fat', sortable: true }, |
|
||||
{ name: 'carbs', label: 'Carbs (g)', field: 'carbs' }, |
|
||||
{ name: 'protein', label: 'Protein (g)', field: 'protein' }, |
|
||||
{ name: 'sodium', label: 'Sodium (mg)', field: 'sodium' }, |
|
||||
{ name: 'calcium', label: 'Calcium (%)', field: 'calcium', sortable: true, sort: (a, b) => parseInt(a, 10) - parseInt(b, 10) }, |
|
||||
{ name: 'iron', label: 'Iron (%)', field: 'iron', sortable: true, sort: (a, b) => parseInt(a, 10) - parseInt(b, 10) }, |
|
||||
]; |
|
||||
const rows = []; |
|
||||
const rows2 = [ |
|
||||
{ |
|
||||
name: 'Frozen Yogurt', |
|
||||
calories: 159, |
|
||||
fat: 6.0, |
|
||||
carbs: 24, |
|
||||
protein: 4.0, |
|
||||
sodium: 87, |
|
||||
calcium: '14%', |
|
||||
iron: '1%', |
|
||||
}, |
|
||||
{ |
|
||||
name: 'Ice cream sandwich', |
|
||||
calories: 237, |
|
||||
fat: 9.0, |
|
||||
carbs: 37, |
|
||||
protein: 4.3, |
|
||||
sodium: 129, |
|
||||
calcium: '8%', |
|
||||
iron: '1%', |
|
||||
}, |
|
||||
{ |
|
||||
name: 'Eclair', |
|
||||
calories: 262, |
|
||||
fat: 16.0, |
|
||||
carbs: 23, |
|
||||
protein: 6.0, |
|
||||
sodium: 337, |
|
||||
calcium: '6%', |
|
||||
iron: '7%', |
|
||||
}, |
|
||||
{ |
|
||||
name: 'Cupcake', |
|
||||
calories: 305, |
|
||||
fat: 3.7, |
|
||||
carbs: 67, |
|
||||
protein: 4.3, |
|
||||
sodium: 413, |
|
||||
calcium: '3%', |
|
||||
iron: '8%', |
|
||||
}, |
|
||||
{ |
|
||||
name: 'Gingerbread', |
|
||||
calories: 356, |
|
||||
fat: 16.0, |
|
||||
carbs: 49, |
|
||||
protein: 3.9, |
|
||||
sodium: 327, |
|
||||
calcium: '7%', |
|
||||
iron: '16%', |
|
||||
}, |
|
||||
{ |
|
||||
name: 'Jelly bean', |
|
||||
calories: 375, |
|
||||
fat: 0.0, |
|
||||
carbs: 94, |
|
||||
protein: 0.0, |
|
||||
sodium: 50, |
|
||||
calcium: '0%', |
|
||||
iron: '0%', |
|
||||
}, |
|
||||
{ |
|
||||
name: 'Lollipop', |
|
||||
calories: 392, |
|
||||
fat: 0.2, |
|
||||
carbs: 98, |
|
||||
protein: 0, |
|
||||
sodium: 38, |
|
||||
calcium: '0%', |
|
||||
iron: '2%', |
|
||||
}, |
|
||||
{ |
|
||||
name: 'Honeycomb', |
|
||||
calories: 408, |
|
||||
fat: 3.2, |
|
||||
carbs: 87, |
|
||||
protein: 6.5, |
|
||||
sodium: 562, |
|
||||
calcium: '0%', |
|
||||
iron: '45%', |
|
||||
}, |
|
||||
{ |
|
||||
name: 'Donut', |
|
||||
calories: 452, |
|
||||
fat: 25.0, |
|
||||
carbs: 51, |
|
||||
protein: 4.9, |
|
||||
sodium: 326, |
|
||||
calcium: '2%', |
|
||||
iron: '22%', |
|
||||
}, |
|
||||
{ |
|
||||
name: 'KitKat', |
|
||||
calories: 518, |
|
||||
fat: 26.0, |
|
||||
carbs: 65, |
|
||||
protein: 7, |
|
||||
sodium: 54, |
|
||||
calcium: '12%', |
|
||||
iron: '6%', |
|
||||
}, |
|
||||
{ |
|
||||
name: 'KitKat', |
|
||||
calories: 518, |
|
||||
fat: 26.0, |
|
||||
carbs: 65, |
|
||||
protein: 7, |
|
||||
sodium: 54, |
|
||||
calcium: '12%', |
|
||||
iron: '6%', |
|
||||
}, |
|
||||
{ |
|
||||
name: 'KitKat', |
|
||||
calories: 518, |
|
||||
fat: 26.0, |
|
||||
carbs: 65, |
|
||||
protein: 7, |
|
||||
sodium: 54, |
|
||||
calcium: '12%', |
|
||||
iron: '6%', |
|
||||
}, |
|
||||
{ |
|
||||
name: 'KitKat', |
|
||||
calories: 518, |
|
||||
fat: 26.0, |
|
||||
carbs: 65, |
|
||||
protein: 7, |
|
||||
sodium: 54, |
|
||||
calcium: '12%', |
|
||||
iron: '6%', |
|
||||
}, |
|
||||
{ |
|
||||
name: 'KitKat', |
|
||||
calories: 518, |
|
||||
fat: 26.0, |
|
||||
carbs: 65, |
|
||||
protein: 7, |
|
||||
sodium: 54, |
|
||||
calcium: '12%', |
|
||||
iron: '6%', |
|
||||
}, |
|
||||
{ |
|
||||
name: 'KitKat', |
|
||||
calories: 518, |
|
||||
fat: 26.0, |
|
||||
carbs: 65, |
|
||||
protein: 7, |
|
||||
sodium: 54, |
|
||||
calcium: '12%', |
|
||||
iron: '6%', |
|
||||
}, |
|
||||
{ |
|
||||
name: 'KitKat', |
|
||||
calories: 518, |
|
||||
fat: 26.0, |
|
||||
carbs: 65, |
|
||||
protein: 7, |
|
||||
sodium: 54, |
|
||||
calcium: '12%', |
|
||||
iron: '6%', |
|
||||
}, |
|
||||
]; |
|
||||
|
|
||||
export default { |
|
||||
setup() { |
|
||||
return { |
|
||||
columns, |
|
||||
rows, |
|
||||
}; |
|
||||
}, |
|
||||
}; |
|
||||
</script> |
</script> |
||||
|
@ -1 +1,11 @@ |
|||||
= The HTML DOM API |
= The HTML DOM API |
||||
|
= clientHeight、offsetHeight、scrollHeight |
||||
|
image::9999-appendix/docker/tidb/001.png[,60%] |
||||
|
|
||||
|
|=== |
||||
|
| clientHeight | 元素高度 + 内边距 |
||||
|
| offsetHeight | |
||||
|
| scrollHeight | |
||||
|
|=== |
||||
|
|
||||
|
|
||||
|
@ -0,0 +1,34 @@ |
|||||
|
package io.sc.platform.flowable.controller; |
||||
|
|
||||
|
import io.sc.platform.flowable.service.ProcessQueryService; |
||||
|
import io.sc.platform.flowable.service.ProcessToolsService; |
||||
|
import io.sc.platform.flowable.support.*; |
||||
|
import io.sc.platform.mvc.support.FileDownloader; |
||||
|
import io.sc.platform.orm.service.support.QueryParameter; |
||||
|
import io.sc.platform.orm.service.support.criteria.Criteria; |
||||
|
import io.sc.platform.orm.service.support.criteria.impl.Equals; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.data.domain.Page; |
||||
|
import org.springframework.web.bind.annotation.*; |
||||
|
|
||||
|
import javax.servlet.http.HttpServletRequest; |
||||
|
import javax.servlet.http.HttpServletResponse; |
||||
|
import java.io.InputStream; |
||||
|
import java.util.Collections; |
||||
|
import java.util.List; |
||||
|
|
||||
|
@RestController |
||||
|
@RequestMapping("/api/flowable/tools") |
||||
|
public class ProcessToolsWebController { |
||||
|
@Autowired private ProcessToolsService service; |
||||
|
|
||||
|
@PostMapping("cleanRuntimeData") |
||||
|
public void cleanRuntimeData() throws Exception{ |
||||
|
service.cleanRuntimeData(); |
||||
|
} |
||||
|
|
||||
|
@PostMapping("cleanHistoryData") |
||||
|
public void cleanHistoryData() throws Exception{ |
||||
|
service.cleanHistoryData(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,6 @@ |
|||||
|
package io.sc.platform.flowable.service; |
||||
|
|
||||
|
public interface ProcessToolsService { |
||||
|
public void cleanRuntimeData() throws Exception; |
||||
|
public void cleanHistoryData() throws Exception; |
||||
|
} |
@ -0,0 +1,46 @@ |
|||||
|
package io.sc.platform.flowable.service.impl; |
||||
|
|
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.jdbc.core.JdbcTemplate; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
import io.sc.platform.flowable.service.ProcessToolsService; |
||||
|
|
||||
|
import javax.transaction.Transactional; |
||||
|
|
||||
|
@Service |
||||
|
public class ProcessToolsServiceImpl implements ProcessToolsService{ |
||||
|
@Autowired private JdbcTemplate jdbcTemplate; |
||||
|
|
||||
|
@Override |
||||
|
@Transactional |
||||
|
public void cleanRuntimeData() throws Exception { |
||||
|
jdbcTemplate.update("delete from ACT_RU_ACTINST"); |
||||
|
jdbcTemplate.update("delete from ACT_RU_DEADLETTER_JOB"); |
||||
|
jdbcTemplate.update("delete from ACT_RU_ENTITYLINK"); |
||||
|
jdbcTemplate.update("delete from ACT_RU_EVENT_SUBSCR"); |
||||
|
jdbcTemplate.update("delete from ACT_RU_EXTERNAL_JOB"); |
||||
|
jdbcTemplate.update("delete from ACT_RU_HISTORY_JOB"); |
||||
|
jdbcTemplate.update("delete from ACT_RU_IDENTITYLINK"); |
||||
|
jdbcTemplate.update("delete from ACT_RU_JOB"); |
||||
|
jdbcTemplate.update("delete from ACT_RU_SUSPENDED_JOB"); |
||||
|
jdbcTemplate.update("delete from ACT_RU_TASK"); |
||||
|
jdbcTemplate.update("delete from ACT_RU_TIMER_JOB"); |
||||
|
jdbcTemplate.update("delete from ACT_RU_VARIABLE"); |
||||
|
jdbcTemplate.update("delete from ACT_RU_EXECUTION"); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
@Transactional |
||||
|
public void cleanHistoryData() throws Exception { |
||||
|
jdbcTemplate.update("delete from ACT_HI_ACTINST"); |
||||
|
jdbcTemplate.update("delete from ACT_HI_ATTACHMENT"); |
||||
|
jdbcTemplate.update("delete from ACT_HI_COMMENT"); |
||||
|
jdbcTemplate.update("delete from ACT_HI_DETAIL"); |
||||
|
jdbcTemplate.update("delete from ACT_HI_ENTITYLINK"); |
||||
|
jdbcTemplate.update("delete from ACT_HI_IDENTITYLINK"); |
||||
|
jdbcTemplate.update("delete from ACT_HI_PROCINST"); |
||||
|
jdbcTemplate.update("delete from ACT_HI_TASKINST"); |
||||
|
jdbcTemplate.update("delete from ACT_HI_TSK_LOG"); |
||||
|
jdbcTemplate.update("delete from ACT_HI_VARINST"); |
||||
|
} |
||||
|
} |
@ -0,0 +1,25 @@ |
|||||
|
package io.sc.platform.flowable.support; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.List; |
||||
|
|
||||
|
public class SelectAssigneeWrapper { |
||||
|
private String activeName; |
||||
|
private List<Assignee> assignees =new ArrayList<>(); |
||||
|
|
||||
|
public String getActiveName() { |
||||
|
return activeName; |
||||
|
} |
||||
|
|
||||
|
public void setActiveName(String activeName) { |
||||
|
this.activeName = activeName; |
||||
|
} |
||||
|
|
||||
|
public List<Assignee> getAssignees() { |
||||
|
return assignees; |
||||
|
} |
||||
|
|
||||
|
public void setAssignees(List<Assignee> assignees) { |
||||
|
this.assignees = assignees; |
||||
|
} |
||||
|
} |
@ -0,0 +1,68 @@ |
|||||
|
<template> |
||||
|
<w-dialog ref="dialogRef" :title="$t('lcdp.bpm.completeTask.dialog.title')" width="800px" :can-maximize="false"> |
||||
|
<template #buttons> |
||||
|
<WWorkflowAction |
||||
|
ref="workflowActionRef" |
||||
|
:task-id="taskIdRef" |
||||
|
:data="formModelValue" |
||||
|
:action-url="Environment.apiContextPath('/api/flowable/process/operation/complete')" |
||||
|
@after-submit="afterSubmit" |
||||
|
> |
||||
|
</WWorkflowAction> |
||||
|
</template> |
||||
|
<w-form |
||||
|
v-model="formModelValue" |
||||
|
:cols-num="1" |
||||
|
:fields="[ |
||||
|
{ name: 'variables', label: $t('lcdp.bpm.processInstance.grid.entity.variables'), type: 'code-mirror', lang: 'json', rows: 5 }, |
||||
|
{ name: 'transientVariables', label: $t('lcdp.bpm.processInstance.grid.entity.transientVariables'), type: 'code-mirror', lang: 'json', rows: 5 }, |
||||
|
]" |
||||
|
> |
||||
|
</w-form> |
||||
|
</w-dialog> |
||||
|
</template> |
||||
|
<script setup lang="ts"> |
||||
|
import { nextTick, ref, reactive } from 'vue'; |
||||
|
import WWorkflowAction from './WWorkflowAction.vue'; |
||||
|
import { Environment } from 'platform-core'; |
||||
|
|
||||
|
/** |
||||
|
* 定义组件支持的自定义事件 |
||||
|
*/ |
||||
|
const emit = defineEmits([ |
||||
|
'afterTaskCompleted', // 提交成功后 |
||||
|
]); |
||||
|
|
||||
|
const dialogRef = ref(); |
||||
|
const taskIdRef = ref<string>(''); |
||||
|
const workflowActionRef = ref(); |
||||
|
const formModelValue = reactive({ |
||||
|
variables: undefined, |
||||
|
transientVariables: undefined, |
||||
|
}); |
||||
|
|
||||
|
const open = (taskId: string) => { |
||||
|
taskIdRef.value = taskId; |
||||
|
formModelValue.transientVariables = JSON.stringify({ task_treatment: '此处填写处理意见' }, null, 2); |
||||
|
formModelValue.variables = undefined; |
||||
|
dialogRef.value.show(); |
||||
|
// nextTick(() => { |
||||
|
// workflowActionRef.value.setTaskId(taskId); |
||||
|
// workflowActionRef.value.setData(formModelValue); |
||||
|
// }); |
||||
|
}; |
||||
|
|
||||
|
const close = () => { |
||||
|
dialogRef.value.hide(); |
||||
|
}; |
||||
|
|
||||
|
const afterSubmit = () => { |
||||
|
close(); |
||||
|
emit('afterTaskCompleted'); |
||||
|
}; |
||||
|
|
||||
|
defineExpose({ |
||||
|
open, |
||||
|
close, |
||||
|
}); |
||||
|
</script> |
@ -0,0 +1,65 @@ |
|||||
|
<template> |
||||
|
<w-dialog |
||||
|
ref="dialogRef" |
||||
|
:title="$t('lcdp.bpm.selectAssignee.dialog.title')" |
||||
|
width="500px" |
||||
|
:can-maximize="false" |
||||
|
:buttons="[ |
||||
|
{ |
||||
|
name: 'confirm', |
||||
|
label: $t('confirm'), |
||||
|
click: () => { |
||||
|
emit('assigneeSelected', formRef.getFieldValue('assignee')); |
||||
|
close(); |
||||
|
}, |
||||
|
}, |
||||
|
]" |
||||
|
> |
||||
|
<div |
||||
|
v-dompurify-html="$t('lcdp.bpm.selectAssignee.entity.assignee.tip', { activeName: selectAssigneeWrapperRef.activeName })" |
||||
|
class="text-body2 py-2" |
||||
|
></div> |
||||
|
<w-form |
||||
|
ref="formRef" |
||||
|
:cols-num="1" |
||||
|
:fields="[{ name: 'assignee', label: $t('lcdp.bpm.selectAssignee.entity.assignee'), type: 'select', options: assigneeOptionsRef }]" |
||||
|
> |
||||
|
</w-form> |
||||
|
</w-dialog> |
||||
|
</template> |
||||
|
<script setup lang="ts"> |
||||
|
import { reactive, ref } from 'vue'; |
||||
|
|
||||
|
/** |
||||
|
* 定义组件支持的自定义事件 |
||||
|
*/ |
||||
|
const emit = defineEmits([ |
||||
|
'assigneeSelected', // 选择了候选人 |
||||
|
]); |
||||
|
|
||||
|
const dialogRef = ref(); |
||||
|
const formRef = ref(); |
||||
|
const assigneeOptionsRef = ref([]); |
||||
|
const selectAssigneeWrapperRef = ref(); |
||||
|
|
||||
|
const open = (selectAssigneeWrapper) => { |
||||
|
selectAssigneeWrapperRef.value = selectAssigneeWrapper; |
||||
|
assigneeOptionsRef.value.splice(0, assigneeOptionsRef.value.length); |
||||
|
const assignees = selectAssigneeWrapper?.assignees; |
||||
|
if (assignees) { |
||||
|
for (let i = 0; i < assignees.length; i++) { |
||||
|
assigneeOptionsRef.value.push({ value: assignees[i].loginName, label: assignees[i].loginName + '/' + assignees[i].userName }); |
||||
|
} |
||||
|
} |
||||
|
dialogRef.value.show(); |
||||
|
}; |
||||
|
|
||||
|
const close = () => { |
||||
|
dialogRef.value.hide(); |
||||
|
}; |
||||
|
|
||||
|
defineExpose({ |
||||
|
open, |
||||
|
close, |
||||
|
}); |
||||
|
</script> |
@ -0,0 +1,158 @@ |
|||||
|
<template> |
||||
|
<div :class="`flex ${align === 'right' ? 'justify-end' : ''} gap-4`"> |
||||
|
<q-btn v-for="action in actionsRef" :key="action.name" :label="action.title" color="primary" @click="buttonClick(action)"></q-btn> |
||||
|
</div> |
||||
|
<SelectAssigneeDialog ref="selectAssigneeDialogRef" @assignee-selected="assigneeSelected"></SelectAssigneeDialog> |
||||
|
</template> |
||||
|
<script setup lang="ts"> |
||||
|
import { ref, onMounted } from 'vue'; |
||||
|
import { useI18n } from 'vue-i18n'; |
||||
|
import { Environment, Tools, axios } from 'platform-core'; |
||||
|
import SelectAssigneeDialog from './SelectAssigneeDialog.vue'; |
||||
|
import { reactive } from 'vue'; |
||||
|
|
||||
|
/** |
||||
|
* 定义组件支持的自定义属性 |
||||
|
*/ |
||||
|
const props = defineProps({ |
||||
|
//任务 ID |
||||
|
taskId: { type: String, default: undefined }, |
||||
|
//数据对象 |
||||
|
data: { |
||||
|
type: Object, |
||||
|
default: () => { |
||||
|
return {}; |
||||
|
}, |
||||
|
}, |
||||
|
//对齐方式 |
||||
|
align: { type: String, default: 'center' }, |
||||
|
// 完成操作对应的服务器端控制器 url |
||||
|
actionUrl: { type: String, default: undefined }, |
||||
|
// 获取回退按钮列表的 url,由系统自动提供,使用者无需指定 |
||||
|
gobackActionUrl: { type: String, default: '/api/flowable/process/operation/getGobacks' }, |
||||
|
// 默认按钮 |
||||
|
defaultActionButtons: { type: [Array, Object], default: undefined }, |
||||
|
// 默认按钮放置位置 |
||||
|
defaultActionButtonsPlacement: { type: String, default: 'right' }, |
||||
|
// 是否 goback 按钮组采用下拉列表模式 |
||||
|
isGobackActionDropdown: { type: Boolean, default: false }, |
||||
|
}); |
||||
|
|
||||
|
/** |
||||
|
* 定义组件支持的自定义事件 |
||||
|
*/ |
||||
|
const emit = defineEmits([ |
||||
|
'afterSubmit', // 提交成功后 |
||||
|
]); |
||||
|
|
||||
|
const { t } = useI18n(); |
||||
|
const actionsRef = ref([]); |
||||
|
const selectAssigneeDialogRef = ref(); |
||||
|
const currentActionRef = ref(null); |
||||
|
|
||||
|
const buildActions = (taskId: string) => { |
||||
|
axios.get(Environment.apiContextPath(props.gobackActionUrl + '/' + taskId)).then((data) => { |
||||
|
buildButtons(data.data); |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
const buildButtons = (gobacks) => { |
||||
|
const buttons = []; |
||||
|
// 添加默认按钮(左边) |
||||
|
if (props.defaultActionButtonsPlacement === 'left') { |
||||
|
if (props.defaultActionButtons && props.defaultActionButtons && props.defaultActionButtons.length > 0) { |
||||
|
for (let i = 0; i < props.defaultActionButtons.length; i++) { |
||||
|
buttons.push(props.defaultActionButtons[i]); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 回退按钮 |
||||
|
if (gobacks && gobacks.length > 0) { |
||||
|
for (let i = 0; i < gobacks.length; i++) { |
||||
|
const goback = gobacks[i]; |
||||
|
const transientVariables = {}; |
||||
|
transientVariables[goback.variableName] = goback.variableValue; |
||||
|
buttons.push({ |
||||
|
title: goback.title || t('goback'), |
||||
|
transientVariables, |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 提交按钮 |
||||
|
buttons.push({ title: t('submit') }); |
||||
|
|
||||
|
// 添加默认按钮(右边) |
||||
|
if (props.defaultActionButtonsPlacement === 'right') { |
||||
|
if (props.defaultActionButtons && props.defaultActionButtons && props.defaultActionButtons.length > 0) { |
||||
|
for (let i = 0; i < props.defaultActionButtons.length; i++) { |
||||
|
buttons.push(props.defaultActionButtons[i]); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 更新到响应式变量中 |
||||
|
actionsRef.value.splice(0, actionsRef.value.length); |
||||
|
for (let i = 0; i < buttons.length; i++) { |
||||
|
actionsRef.value.push(buttons[i]); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
const buttonClick = (action) => { |
||||
|
currentActionRef.value = action; |
||||
|
const data = { |
||||
|
variables: {}, |
||||
|
transientVariables: {}, |
||||
|
}; |
||||
|
if (!Tools.isUndefinedOrNull(props.data)) { |
||||
|
if (!Tools.isEmpty(props.data.variables)) { |
||||
|
data.variables = JSON.parse(props.data.variables); |
||||
|
} |
||||
|
if (!Tools.isEmpty(props.data.transientVariables)) { |
||||
|
data.transientVariables = JSON.parse(props.data.transientVariables); |
||||
|
} |
||||
|
} |
||||
|
if (!Tools.isUndefinedOrNull(action)) { |
||||
|
if (!Tools.isUndefinedOrNull(action.transientVariables)) { |
||||
|
Tools.mergeObject(data.transientVariables, action.transientVariables); |
||||
|
} |
||||
|
} |
||||
|
console.log(data); |
||||
|
|
||||
|
// 如果是点击了“回退”按钮,将回退控制变量付给临时变量 |
||||
|
axios.post(props.actionUrl + '/' + props.taskId, data).then((response) => { |
||||
|
if (response.data.code === 0) { |
||||
|
// 操作成功 |
||||
|
emit('afterSubmit'); |
||||
|
} else if (response.data.code === 1) { |
||||
|
// 需要选择处理人 |
||||
|
selectAssigneeDialogRef.value.open(response.data); |
||||
|
} |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
const assigneeSelected = (assignee) => { |
||||
|
const data = currentActionRef.value; |
||||
|
if (props.data.variables) { |
||||
|
data.variables = JSON.parse(props.data.variables); |
||||
|
} |
||||
|
if (props.data.transientVariables) { |
||||
|
data.transientVariables = JSON.parse(props.data.transientVariables); |
||||
|
data.transientVariables.assignee = assignee; |
||||
|
} |
||||
|
axios.post(Environment.apiContextPath(props.actionUrl + props.taskId), data).then((response) => { |
||||
|
const rawData = response.data; |
||||
|
if (rawData.code === 0) { |
||||
|
// 操作成功 |
||||
|
emit('afterSubmit'); |
||||
|
} |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
onMounted(() => { |
||||
|
if (props.taskId) { |
||||
|
buildActions(props.taskId); |
||||
|
} |
||||
|
}); |
||||
|
</script> |
@ -1,16 +1,19 @@ |
|||||
package io.sc.platform.orm.api.vo; |
package io.sc.platform.orm.api.vo; |
||||
|
|
||||
|
import com.fasterxml.jackson.annotation.JsonProperty; |
||||
|
|
||||
/** |
/** |
||||
* 版本化实体 VO 类 |
* 版本化实体 VO 类 |
||||
*/ |
*/ |
||||
public abstract class VersionVo extends BaseVo { |
public abstract class VersionVo extends BaseVo { |
||||
protected int jpaVersion; |
@JsonProperty(index = 10001) |
||||
|
protected Integer jpaVersion; |
||||
|
|
||||
public int getJpaVersion() { |
public Integer getJpaVersion() { |
||||
return jpaVersion; |
return jpaVersion; |
||||
} |
} |
||||
|
|
||||
public void setJpaVersion(int jpaVersion) { |
public void setJpaVersion(Integer jpaVersion) { |
||||
this.jpaVersion = jpaVersion; |
this.jpaVersion = jpaVersion; |
||||
} |
} |
||||
} |
} |
||||
|
Loading…
Reference in new issue