commit
1c6e6eb24e
|
|
@ -34,6 +34,7 @@
|
||||||
"@vueuse/core": "^10.9.0",
|
"@vueuse/core": "^10.9.0",
|
||||||
"@wangeditor/editor": "^5.1.23",
|
"@wangeditor/editor": "^5.1.23",
|
||||||
"@wangeditor/editor-for-vue": "^5.1.10",
|
"@wangeditor/editor-for-vue": "^5.1.10",
|
||||||
|
"@wangeditor/plugin-mention": "^1.0.0",
|
||||||
"@zxcvbn-ts/core": "^3.0.4",
|
"@zxcvbn-ts/core": "^3.0.4",
|
||||||
"animate.css": "^4.1.1",
|
"animate.css": "^4.1.1",
|
||||||
"axios": "1.9.0",
|
"axios": "1.9.0",
|
||||||
|
|
@ -65,6 +66,7 @@
|
||||||
"pinia-plugin-persistedstate": "^3.2.1",
|
"pinia-plugin-persistedstate": "^3.2.1",
|
||||||
"qrcode": "^1.5.3",
|
"qrcode": "^1.5.3",
|
||||||
"qs": "^6.12.0",
|
"qs": "^6.12.0",
|
||||||
|
"snabbdom": "^3.6.2",
|
||||||
"sortablejs": "^1.15.3",
|
"sortablejs": "^1.15.3",
|
||||||
"steady-xml": "^0.1.0",
|
"steady-xml": "^0.1.0",
|
||||||
"url": "^0.11.3",
|
"url": "^0.11.3",
|
||||||
|
|
@ -74,6 +76,7 @@
|
||||||
"vue-i18n": "9.10.2",
|
"vue-i18n": "9.10.2",
|
||||||
"vue-router": "4.4.5",
|
"vue-router": "4.4.5",
|
||||||
"vue-types": "^5.1.1",
|
"vue-types": "^5.1.1",
|
||||||
|
"vue3-print-nb": "^0.1.4",
|
||||||
"vue3-signature": "^0.2.4",
|
"vue3-signature": "^0.2.4",
|
||||||
"vuedraggable": "^4.1.0",
|
"vuedraggable": "^4.1.0",
|
||||||
"web-storage-cache": "^1.1.1",
|
"web-storage-cache": "^1.1.1",
|
||||||
|
|
|
||||||
|
|
@ -108,3 +108,8 @@ export const getFormFieldsPermission = async (params: any) => {
|
||||||
export const getProcessInstanceBpmnModelView = async (id: string) => {
|
export const getProcessInstanceBpmnModelView = async (id: string) => {
|
||||||
return await request.get({ url: '/bpm/process-instance/get-bpmn-model-view?id=' + id })
|
return await request.get({ url: '/bpm/process-instance/get-bpmn-model-view?id=' + id })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取流程实例打印数据
|
||||||
|
export const getProcessInstancePrintData = async (id: string) => {
|
||||||
|
return await request.get({ url: '/bpm/process-instance/get-print-data?processInstanceId=' + id })
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,11 @@ import Logger from '@/utils/Logger'
|
||||||
|
|
||||||
import VueDOMPurifyHTML from 'vue-dompurify-html' // 解决v-html 的安全隐患
|
import VueDOMPurifyHTML from 'vue-dompurify-html' // 解决v-html 的安全隐患
|
||||||
|
|
||||||
|
// wangeditor插件注册
|
||||||
|
import {setupWangeditorPlugin} from "@/views/bpm/model/form/PrintTemplate";
|
||||||
|
|
||||||
|
import print from 'vue3-print-nb' // 打印插件
|
||||||
|
|
||||||
// 创建实例
|
// 创建实例
|
||||||
const setupAll = async () => {
|
const setupAll = async () => {
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
|
|
@ -62,10 +67,14 @@ const setupAll = async () => {
|
||||||
setupAuth(app)
|
setupAuth(app)
|
||||||
setupMountedFocus(app)
|
setupMountedFocus(app)
|
||||||
|
|
||||||
|
setupWangeditorPlugin()
|
||||||
|
|
||||||
await router.isReady()
|
await router.isReady()
|
||||||
|
|
||||||
app.use(VueDOMPurifyHTML)
|
app.use(VueDOMPurifyHTML)
|
||||||
|
|
||||||
|
app.use(print)
|
||||||
|
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<el-form ref="formRef" :model="modelData" label-width="120px" class="mt-20px">
|
<el-form ref="formRef" :model="modelData" label-width="130px" class="mt-20px">
|
||||||
<el-form-item class="mb-20px">
|
<el-form-item class="mb-20px">
|
||||||
<template #label>
|
<template #label>
|
||||||
<el-text size="large" tag="b">提交人权限</el-text>
|
<el-text size="large" tag="b">提交人权限</el-text>
|
||||||
|
|
@ -231,7 +231,30 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item class="mb-20px">
|
||||||
|
<template #label>
|
||||||
|
<el-text size="large" tag="b">自定义打印模板</el-text>
|
||||||
|
</template>
|
||||||
|
<div class="flex flex-col w-100%">
|
||||||
|
<div class="flex">
|
||||||
|
<el-switch
|
||||||
|
v-model="modelData.printTemplateSetting.enable"
|
||||||
|
@change="handlePrintTemplateEnableChange"
|
||||||
|
/>
|
||||||
|
<el-button
|
||||||
|
v-if="modelData.printTemplateSetting.enable"
|
||||||
|
class="ml-80px"
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
@click="handleEditPrintTemplate"
|
||||||
|
>
|
||||||
|
编辑模板
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
<print-template ref="printTemplateRef" @confirm="confirmPrintTemplate"/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
|
@ -241,6 +264,7 @@ import * as FormApi from '@/api/bpm/form'
|
||||||
import { parseFormFields } from '@/components/FormCreate/src/utils'
|
import { parseFormFields } from '@/components/FormCreate/src/utils'
|
||||||
import { ProcessVariableEnum } from '@/components/SimpleProcessDesignerV2/src/consts'
|
import { ProcessVariableEnum } from '@/components/SimpleProcessDesignerV2/src/consts'
|
||||||
import HttpRequestSetting from '@/components/SimpleProcessDesignerV2/src/nodes-config/components/HttpRequestSetting.vue'
|
import HttpRequestSetting from '@/components/SimpleProcessDesignerV2/src/nodes-config/components/HttpRequestSetting.vue'
|
||||||
|
import PrintTemplate from './PrintTemplate/Index.vue'
|
||||||
|
|
||||||
const modelData = defineModel<any>()
|
const modelData = defineModel<any>()
|
||||||
|
|
||||||
|
|
@ -394,6 +418,7 @@ const formFieldOptions4Summary = computed(() => {
|
||||||
const unParsedFormFields = ref<string[]>([])
|
const unParsedFormFields = ref<string[]>([])
|
||||||
/** 暴露给子组件 HttpRequestSetting 使用 */
|
/** 暴露给子组件 HttpRequestSetting 使用 */
|
||||||
provide('formFields', unParsedFormFields)
|
provide('formFields', unParsedFormFields)
|
||||||
|
provide('formFieldsObj', formFields)
|
||||||
|
|
||||||
/** 兼容以前未配置更多设置的流程 */
|
/** 兼容以前未配置更多设置的流程 */
|
||||||
const initData = () => {
|
const initData = () => {
|
||||||
|
|
@ -436,6 +461,11 @@ const initData = () => {
|
||||||
if (modelData.value.allowWithdrawTask) {
|
if (modelData.value.allowWithdrawTask) {
|
||||||
modelData.value.allowWithdrawTask = false
|
modelData.value.allowWithdrawTask = false
|
||||||
}
|
}
|
||||||
|
if (!modelData.value.printTemplateSetting) {
|
||||||
|
modelData.value.printTemplateSetting = {
|
||||||
|
enable: false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
defineExpose({ initData })
|
defineExpose({ initData })
|
||||||
|
|
||||||
|
|
@ -460,4 +490,21 @@ watch(
|
||||||
},
|
},
|
||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO defaultTemplate 是否需要放到 infra_config
|
||||||
|
const defaultTemplate = '<p style="text-align: center;"><span data-w-e-type="mention" data-w-e-is-void="" data-w-e-is-inline="" data-value="流程名称" data-info="%7B%22id%22%3A%22processName%22%7D">@流程名称</span></p><p style="text-align: right;">打印人:<span data-w-e-type="mention" data-w-e-is-void="" data-w-e-is-inline="" data-value="打印人" data-info="%7B%22id%22%3A%22printUser%22%7D">@打印人</span></p><p style="text-align: right;">流程编号:<span data-w-e-type="mention" data-w-e-is-void="" data-w-e-is-inline="" data-value="流程编号" data-info="%7B%22id%22%3A%22processNum%22%7D">@流程编号</span> 打印时间:<span data-w-e-type="mention" data-w-e-is-void="" data-w-e-is-inline="" data-value="打印时间" data-info="%7B%22id%22%3A%22printTime%22%7D">@打印时间</span></p><table style="width: 100%;"><tbody><tr><td colSpan="1" rowSpan="1" width="auto">发起人</td><td colSpan="1" rowSpan="1" width="auto"><span data-w-e-type="mention" data-w-e-is-void data-w-e-is-inline data-value="发起人" data-info="%7B%22id%22%3A%22startUser%22%7D">@发起人</span></td><td colSpan="1" rowSpan="1" width="auto">发起时间</td><td colSpan="1" rowSpan="1" width="auto"><span data-w-e-type="mention" data-w-e-is-void data-w-e-is-inline data-value="发起时间" data-info="%7B%22id%22%3A%22startTime%22%7D">@发起时间</span></td></tr><tr><td colSpan="1" rowSpan="1" width="auto">所属部门</td><td colSpan="1" rowSpan="1" width="auto"><span data-w-e-type="mention" data-w-e-is-void data-w-e-is-inline data-value="发起人部门" data-info="%7B%22id%22%3A%22startUserDept%22%7D">@发起人部门</span></td><td colSpan="1" rowSpan="1" width="auto">流程状态</td><td colSpan="1" rowSpan="1" width="auto"><span data-w-e-type="mention" data-w-e-is-void data-w-e-is-inline data-value="流程状态" data-info="%7B%22id%22%3A%22processStatus%22%7D">@流程状态</span></td></tr></tbody></table><p><span data-w-e-type="process-record" data-w-e-is-void data-w-e-is-inline>流程记录</span></p>'
|
||||||
|
const handlePrintTemplateEnableChange = (val: boolean) => {
|
||||||
|
if (val) {
|
||||||
|
if (!modelData.value.printTemplateSetting.template) {
|
||||||
|
modelData.value.printTemplateSetting.template = defaultTemplate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const printTemplateRef = ref()
|
||||||
|
const handleEditPrintTemplate = () => {
|
||||||
|
printTemplateRef.value.open(modelData.value.printTemplateSetting.template)
|
||||||
|
}
|
||||||
|
const confirmPrintTemplate = (template) => {
|
||||||
|
modelData.value.printTemplateSetting.template = template
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,112 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {Editor, Toolbar} from '@wangeditor/editor-for-vue'
|
||||||
|
import {IDomEditor} from '@wangeditor/editor'
|
||||||
|
import MentionModal from "./MentionModal.vue";
|
||||||
|
|
||||||
|
const emit = defineEmits(['confirm'])
|
||||||
|
|
||||||
|
// mention
|
||||||
|
const isShowModal = ref(false)
|
||||||
|
const showModal = () => {
|
||||||
|
isShowModal.value = true
|
||||||
|
}
|
||||||
|
const hideModal = () => {
|
||||||
|
isShowModal.value = false
|
||||||
|
}
|
||||||
|
const insertMention = (id, name) => {
|
||||||
|
const mentionNode = {
|
||||||
|
type: 'mention',
|
||||||
|
value: name,
|
||||||
|
info: {id},
|
||||||
|
children: [{text: ''}],
|
||||||
|
}
|
||||||
|
const editor = editorRef.value
|
||||||
|
if (editor) {
|
||||||
|
editor.restoreSelection()
|
||||||
|
editor.deleteBackward('character')
|
||||||
|
editor.insertNode(mentionNode)
|
||||||
|
editor.move(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dialog
|
||||||
|
const dialogVisible = ref(false)
|
||||||
|
const open = async (template) => {
|
||||||
|
dialogVisible.value = true
|
||||||
|
valueHtml.value = template
|
||||||
|
console.log(template)
|
||||||
|
}
|
||||||
|
defineExpose({open})
|
||||||
|
const handleConfirm = () => {
|
||||||
|
emit('confirm', valueHtml.value)
|
||||||
|
dialogVisible.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Editor
|
||||||
|
const editorRef = shallowRef<IDomEditor>()
|
||||||
|
const editorId = ref('wangeEditor-1')
|
||||||
|
const toolbarConfig = {
|
||||||
|
excludeKeys: ['group-video'],
|
||||||
|
insertKeys: {
|
||||||
|
index: 31,
|
||||||
|
keys: ['ProcessRecordMenu']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const editorConfig = {
|
||||||
|
placeholder: '请输入内容...',
|
||||||
|
EXTEND_CONF: {
|
||||||
|
mentionConfig: {
|
||||||
|
showModal,
|
||||||
|
hideModal,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const valueHtml = ref()
|
||||||
|
const handleCreated = (editor: IDomEditor) => {
|
||||||
|
editorRef.value = editor
|
||||||
|
}
|
||||||
|
|
||||||
|
// onBeforeUnmount
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
const editor = editorRef.value
|
||||||
|
if (editor == null) return
|
||||||
|
editor.destroy()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<el-dialog v-model="dialogVisible" title="自定义模板" fullscreen>
|
||||||
|
<div style="margin: 0 10px;">
|
||||||
|
<el-alert
|
||||||
|
title="输入 @ 可选择插入流程表单选项和默认选项"
|
||||||
|
type="info"
|
||||||
|
show-icon
|
||||||
|
:closable="false"/>
|
||||||
|
</div>
|
||||||
|
<div style="border: 1px solid #ccc;margin: 10px;">
|
||||||
|
<Toolbar
|
||||||
|
style="border-bottom: 1px solid #ccc;"
|
||||||
|
:editor="editorRef"
|
||||||
|
:editorId="editorId"
|
||||||
|
:defaultConfig="toolbarConfig"
|
||||||
|
/>
|
||||||
|
<Editor
|
||||||
|
style="height: 500px; overflow-y: hidden;"
|
||||||
|
v-model="valueHtml"
|
||||||
|
:defaultConfig="editorConfig"
|
||||||
|
:editorId="editorId"
|
||||||
|
@on-created="handleCreated"
|
||||||
|
/>
|
||||||
|
<MentionModal
|
||||||
|
v-if="isShowModal"
|
||||||
|
@hide-mention-modal="hideModal"
|
||||||
|
@insert-mention="insertMention"/>
|
||||||
|
</div>
|
||||||
|
<div style="margin-right: 10px;float: right;">
|
||||||
|
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||||
|
<el-button type="primary" @click="handleConfirm">确 定</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style src="@wangeditor/editor/dist/css/style.css"></style>
|
||||||
|
|
@ -0,0 +1,109 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
const emit = defineEmits(['hideMentionModal', 'insertMention'])
|
||||||
|
|
||||||
|
const inputRef = ref()
|
||||||
|
const top = ref('')
|
||||||
|
const left = ref('')
|
||||||
|
const searchVal = ref('')
|
||||||
|
const list = ref([
|
||||||
|
{id: 'startUser', name: '发起人'},
|
||||||
|
{id: 'startUserDept', name: '发起人部门'},
|
||||||
|
{id: 'processName', name: '流程名称'},
|
||||||
|
{id: 'processNum', name: '流程编号'},
|
||||||
|
{id: 'startTime', name: '发起时间'},
|
||||||
|
{id: 'endTime', name: '发起时间'},
|
||||||
|
{id: 'processStatus', name: '流程状态'},
|
||||||
|
{id: 'processResult', name: '流程结果'},
|
||||||
|
{id: 'printUser', name: '打印人'},
|
||||||
|
{id: 'printTime', name: '打印时间'},
|
||||||
|
])
|
||||||
|
const searchedList = computed(() => {
|
||||||
|
const searchValStr = searchVal.value.trim().toLowerCase()
|
||||||
|
return list.value.filter(item => {
|
||||||
|
const name = item.name.toLowerCase()
|
||||||
|
return name.indexOf(searchValStr) >= 0;
|
||||||
|
})
|
||||||
|
})
|
||||||
|
const inputKeyupHandler = (event) => {
|
||||||
|
if (event.key === 'Escape') {
|
||||||
|
emit('hideMentionModal')
|
||||||
|
}
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
const firstOne = searchedList.value[0]
|
||||||
|
if (firstOne) {
|
||||||
|
const {id, name} = firstOne
|
||||||
|
insertMentionHandler(id, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const insertMentionHandler = (id, name) => {
|
||||||
|
emit('insertMention', id, name)
|
||||||
|
emit('hideMentionModal')
|
||||||
|
}
|
||||||
|
|
||||||
|
const formFields = inject('formFieldsObj')
|
||||||
|
onMounted(()=> {
|
||||||
|
if (formFields.value && formFields.value.length > 0) {
|
||||||
|
const cloneFormField = formFields.value.map((item) => {
|
||||||
|
return {
|
||||||
|
name: '[表单]'+item.title,
|
||||||
|
id: item.field
|
||||||
|
}
|
||||||
|
})
|
||||||
|
list.value.push(...cloneFormField)
|
||||||
|
}
|
||||||
|
const domSelection = document.getSelection()
|
||||||
|
const domRange = domSelection?.getRangeAt(0)
|
||||||
|
if (domRange == null) return
|
||||||
|
const rect = domRange.getBoundingClientRect()
|
||||||
|
|
||||||
|
top.value = `${rect.top + 20}px`
|
||||||
|
left.value = `${rect.left + 5}px`
|
||||||
|
|
||||||
|
inputRef.value.focus()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div id="mention-modal" :style="{ top: top, left: left }">
|
||||||
|
<input id="mention-input" v-model="searchVal" ref="inputRef" @keyup="inputKeyupHandler" />
|
||||||
|
<ul id="mention-list">
|
||||||
|
<li
|
||||||
|
v-for="item in searchedList"
|
||||||
|
:key="item.id"
|
||||||
|
@click="insertMentionHandler(item.id, item.name)"
|
||||||
|
>{{ item.name }}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
#mention-modal {
|
||||||
|
position: absolute;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mention-modal input {
|
||||||
|
width: 100px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mention-modal ul {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mention-modal ul li {
|
||||||
|
list-style: none;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 3px 0;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mention-modal ul li:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
import {Boot} from '@wangeditor/editor'
|
||||||
|
import processRecordModule from "./module";
|
||||||
|
import mentionModule from "@wangeditor/plugin-mention";
|
||||||
|
|
||||||
|
// 注册。要在创建编辑器之前注册,且只能注册一次,不可重复注册。
|
||||||
|
export const setupWangeditorPlugin = () => {
|
||||||
|
Boot.registerModule(processRecordModule)
|
||||||
|
Boot.registerModule(mentionModule)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { SlateElement } from '@wangeditor/editor'
|
||||||
|
|
||||||
|
function processRecordToHtml(elem: SlateElement, childrenHtml: string): string {
|
||||||
|
return `<span data-w-e-type="process-record" data-w-e-is-void data-w-e-is-inline>流程记录</span>`
|
||||||
|
}
|
||||||
|
|
||||||
|
const conf = {
|
||||||
|
type: 'process-record',
|
||||||
|
elemToHtml: processRecordToHtml,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default conf
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
import {IModuleConf} from '@wangeditor/editor'
|
||||||
|
import withProcessRecord from './plugin'
|
||||||
|
import renderElemConf from './render-elem'
|
||||||
|
import elemToHtmlConf from './elem-to-html'
|
||||||
|
import parseHtmlConf from './parse-elem-html'
|
||||||
|
import processRecordMenu from "./menu/ProcessRecordMenu"
|
||||||
|
|
||||||
|
const module: Partial<IModuleConf> = {
|
||||||
|
editorPlugin: withProcessRecord,
|
||||||
|
renderElems: [renderElemConf],
|
||||||
|
elemsToHtml: [elemToHtmlConf],
|
||||||
|
parseElemsHtml: [parseHtmlConf],
|
||||||
|
menus: [processRecordMenu],
|
||||||
|
}
|
||||||
|
|
||||||
|
export default module
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
import { IButtonMenu, IDomEditor } from '@wangeditor/editor'
|
||||||
|
|
||||||
|
class ProcessRecordMenu implements IButtonMenu {
|
||||||
|
readonly tag: string;
|
||||||
|
readonly title: string;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.title = '流程记录'
|
||||||
|
this.tag = 'button'
|
||||||
|
}
|
||||||
|
|
||||||
|
getValue(editor: IDomEditor): string {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
isActive(editor: IDomEditor): boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
isDisabled(editor: IDomEditor): boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
exec(editor: IDomEditor, value: string) {
|
||||||
|
if (this.isDisabled(editor)) return
|
||||||
|
const processRecordElem = {
|
||||||
|
type: 'process-record',
|
||||||
|
children: [{ text: '' }],
|
||||||
|
}
|
||||||
|
editor.insertNode(processRecordElem)
|
||||||
|
editor.move(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ProcessRecordMenuConf = {
|
||||||
|
key: 'ProcessRecordMenu',
|
||||||
|
factory() {
|
||||||
|
return new ProcessRecordMenu()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ProcessRecordMenuConf
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { DOMElement } from './utils/dom'
|
||||||
|
import { IDomEditor, SlateDescendant, SlateElement } from '@wangeditor/editor'
|
||||||
|
|
||||||
|
function parseHtml(
|
||||||
|
elem: DOMElement,
|
||||||
|
children: SlateDescendant[],
|
||||||
|
editor: IDomEditor
|
||||||
|
): SlateElement {
|
||||||
|
return {
|
||||||
|
type: 'process-record',
|
||||||
|
children: [{ text: '' }],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const parseHtmlConf = {
|
||||||
|
selector: 'span[data-w-e-type="process-record"]',
|
||||||
|
parseElemHtml: parseHtml,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default parseHtmlConf
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { DomEditor, IDomEditor } from '@wangeditor/editor'
|
||||||
|
|
||||||
|
function withProcessRecord<T extends IDomEditor>(editor: T) {
|
||||||
|
const { isInline, isVoid } = editor
|
||||||
|
const newEditor = editor
|
||||||
|
|
||||||
|
newEditor.isInline = elem => {
|
||||||
|
const type = DomEditor.getNodeType(elem)
|
||||||
|
if (type === 'process-record') {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return isInline(elem)
|
||||||
|
}
|
||||||
|
|
||||||
|
newEditor.isVoid = elem => {
|
||||||
|
const type = DomEditor.getNodeType(elem)
|
||||||
|
if (type === 'process-record') {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return isVoid(elem)
|
||||||
|
}
|
||||||
|
|
||||||
|
return newEditor
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withProcessRecord
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
import {h, VNode} from 'snabbdom'
|
||||||
|
import {DomEditor, IDomEditor, SlateElement} from '@wangeditor/editor'
|
||||||
|
|
||||||
|
function renderProcessRecord(elem: SlateElement, children: VNode[] | null, editor: IDomEditor): VNode {
|
||||||
|
const selected = DomEditor.isNodeSelected(editor, elem)
|
||||||
|
|
||||||
|
const vnode = h(
|
||||||
|
'table',
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
contentEditable: false,
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
width: '100%',
|
||||||
|
border: selected
|
||||||
|
? '2px solid var(--w-e-textarea-selected-border-color)'
|
||||||
|
: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[
|
||||||
|
h('thead', [
|
||||||
|
h('tr', [h('th', {attrs: {colSpan: 3}}, '流程记录')])
|
||||||
|
]),
|
||||||
|
h('tbody', [
|
||||||
|
h('tr', [
|
||||||
|
h('td', [h(
|
||||||
|
'span',
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
contentEditable: false,
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
marginLeft: '3px',
|
||||||
|
marginRight: '3px',
|
||||||
|
backgroundColor: 'var(--w-e-textarea-slight-bg-color)',
|
||||||
|
borderRadius: '3px',
|
||||||
|
padding: '0 3px',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
`节点`
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
h('td', [h(
|
||||||
|
'span',
|
||||||
|
{
|
||||||
|
props: {
|
||||||
|
contentEditable: false,
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
marginLeft: '3px',
|
||||||
|
marginRight: '3px',
|
||||||
|
backgroundColor: 'var(--w-e-textarea-slight-bg-color)',
|
||||||
|
borderRadius: '3px',
|
||||||
|
padding: '0 3px',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
`操作`
|
||||||
|
)
|
||||||
|
])
|
||||||
|
])
|
||||||
|
])
|
||||||
|
]
|
||||||
|
)
|
||||||
|
return vnode
|
||||||
|
}
|
||||||
|
|
||||||
|
const conf = {
|
||||||
|
type: 'process-record',
|
||||||
|
renderElem: renderProcessRecord,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default conf
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
import $, { append, on, hide, click } from 'dom7'
|
||||||
|
|
||||||
|
if (hide) $.fn.hide = hide
|
||||||
|
if (append) $.fn.append = append
|
||||||
|
if (click) $.fn.click = click
|
||||||
|
if (on) $.fn.on = on
|
||||||
|
|
||||||
|
export { Dom7Array } from 'dom7'
|
||||||
|
export default $
|
||||||
|
|
||||||
|
// COMPAT: This is required to prevent TypeScript aliases from doing some very
|
||||||
|
// weird things for Slate's types with the same name as globals. (2019/11/27)
|
||||||
|
// https://github.com/microsoft/TypeScript/issues/35002
|
||||||
|
import DOMNode = globalThis.Node
|
||||||
|
import DOMComment = globalThis.Comment
|
||||||
|
import DOMElement = globalThis.Element
|
||||||
|
import DOMText = globalThis.Text
|
||||||
|
import DOMRange = globalThis.Range
|
||||||
|
import DOMSelection = globalThis.Selection
|
||||||
|
import DOMStaticRange = globalThis.StaticRange
|
||||||
|
export { DOMNode, DOMComment, DOMElement, DOMText, DOMRange, DOMSelection, DOMStaticRange }
|
||||||
|
|
@ -174,7 +174,10 @@ const formData: any = ref({
|
||||||
enable: false,
|
enable: false,
|
||||||
summary: []
|
summary: []
|
||||||
},
|
},
|
||||||
allowWithdrawTask: false
|
allowWithdrawTask: false,
|
||||||
|
printTemplateSetting: {
|
||||||
|
enable: false
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// 流程数据
|
// 流程数据
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,107 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import * as ProcessInstanceApi from '@/api/bpm/processInstance'
|
||||||
|
import { useUserStore } from '@/store/modules/user'
|
||||||
|
import { formatDate } from '@/utils/formatTime'
|
||||||
|
|
||||||
|
const userStore = useUserStore()
|
||||||
|
|
||||||
|
const visible = ref(false)
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
|
const printData = ref()
|
||||||
|
const userName = computed(() => userStore.user.nickname ?? '')
|
||||||
|
const printTime = ref(formatDate(new Date(), 'YYYY-MM-DD HH:mm'))
|
||||||
|
|
||||||
|
const open = async (id) => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
printData.value = await ProcessInstanceApi.getProcessInstancePrintData(id)
|
||||||
|
console.log(printData.value)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
visible.value = true
|
||||||
|
}
|
||||||
|
defineExpose({ open })
|
||||||
|
|
||||||
|
const printObj = ref({
|
||||||
|
id: 'printDivTag',
|
||||||
|
popTitle: ' ',
|
||||||
|
extraCss: '/print.css',
|
||||||
|
extraHead: '',
|
||||||
|
zIndex: 20003
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<el-dialog v-loading="loading" v-model="visible" :show-close="false">
|
||||||
|
<div id="printDivTag">
|
||||||
|
<div v-if="printData.printTemplateEnable" v-html="printData.printTemplateHtml"></div>
|
||||||
|
<div v-else>
|
||||||
|
<h2 class="text-center">{{ printData.processName }}</h2>
|
||||||
|
<div class="text-right text-15px">{{ '打印人员: ' + userName }}</div>
|
||||||
|
<div class="flex justify-between">
|
||||||
|
<div class="text-15px">{{ '流程编号: ' + printData.processInstanceId }}</div>
|
||||||
|
<div class="text-15px">{{ '打印时间: ' + printTime }}</div>
|
||||||
|
</div>
|
||||||
|
<table class="mt-20px w-100%" border="1" style="border-collapse: collapse">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td class="p-5px w-25%">发起人</td>
|
||||||
|
<td class="p-5px w-25%">{{ printData.startUser }}</td>
|
||||||
|
<td class="p-5px w-25%">发起时间</td>
|
||||||
|
<td class="p-5px w-25%">{{ printData.startTime }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="p-5px w-25%">所属部门</td>
|
||||||
|
<td class="p-5px w-25%">{{ printData.startUserDept }}</td>
|
||||||
|
<td class="p-5px w-25%">流程状态</td>
|
||||||
|
<td class="p-5px w-25%">{{ printData.processStatusShow }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="p-5px w-100% text-center" colspan="4">
|
||||||
|
<h4>表单内容</h4>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr v-for="item in printData.formFields" :key="item.formId">
|
||||||
|
<td class="p-5px w-20%">
|
||||||
|
{{ item.formName }}
|
||||||
|
</td>
|
||||||
|
<td class="p-5px w-80%" colspan="3">
|
||||||
|
<div v-html="item.formValueShow"></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="p-5px w-100% text-center" colspan="4">
|
||||||
|
<h4>流程节点</h4>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr v-for="item in printData.approveNodes" :key="item.nodeId">
|
||||||
|
<td class="p-5px w-20%">
|
||||||
|
{{ item.nodeName }}
|
||||||
|
</td>
|
||||||
|
<td class="p-5px w-80%" colspan="3">
|
||||||
|
{{ item.nodeDesc }}
|
||||||
|
<div v-if="item.signUrl !== ''">
|
||||||
|
<img class="w-90px h-40px" :src="item.signUrl" alt="" />
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button @click="visible = false">取 消</el-button>
|
||||||
|
<el-button type="primary" v-print="printObj"> 打 印</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -8,7 +8,10 @@
|
||||||
:src="auditIconsMap[processInstance.status]"
|
:src="auditIconsMap[processInstance.status]"
|
||||||
alt=""
|
alt=""
|
||||||
/>
|
/>
|
||||||
<div class="text-#878c93 h-15px">编号:{{ id }}</div>
|
<div class="flex">
|
||||||
|
<div class="text-#878c93 h-15px">编号:{{ id }}</div>
|
||||||
|
<Icon icon="ep:printer" class="ml-15px cursor-pointer" @click="handlePrint"/>
|
||||||
|
</div>
|
||||||
<el-divider class="!my-8px" />
|
<el-divider class="!my-8px" />
|
||||||
<div class="flex items-center gap-5 mb-10px h-40px">
|
<div class="flex items-center gap-5 mb-10px h-40px">
|
||||||
<div class="text-26px font-bold mb-5px">{{ processInstance.name }}</div>
|
<div class="text-26px font-bold mb-5px">{{ processInstance.name }}</div>
|
||||||
|
|
@ -125,6 +128,7 @@
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
</div>
|
</div>
|
||||||
</ContentWrap>
|
</ContentWrap>
|
||||||
|
<PrintDialog ref="printRef" />
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { formatDate } from '@/utils/formatTime'
|
import { formatDate } from '@/utils/formatTime'
|
||||||
|
|
@ -146,6 +150,7 @@ import runningSvg from '@/assets/svgs/bpm/running.svg'
|
||||||
import approveSvg from '@/assets/svgs/bpm/approve.svg'
|
import approveSvg from '@/assets/svgs/bpm/approve.svg'
|
||||||
import rejectSvg from '@/assets/svgs/bpm/reject.svg'
|
import rejectSvg from '@/assets/svgs/bpm/reject.svg'
|
||||||
import cancelSvg from '@/assets/svgs/bpm/cancel.svg'
|
import cancelSvg from '@/assets/svgs/bpm/cancel.svg'
|
||||||
|
import PrintDialog from './PrintDialog.vue'
|
||||||
|
|
||||||
defineOptions({ name: 'BpmProcessInstanceDetail' })
|
defineOptions({ name: 'BpmProcessInstanceDetail' })
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
|
@ -295,6 +300,11 @@ const refresh = () => {
|
||||||
getDetail()
|
getDetail()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const printRef = ref()
|
||||||
|
const handlePrint = async () => {
|
||||||
|
printRef.value.open(props.id)
|
||||||
|
}
|
||||||
|
|
||||||
/** 当前的Tab */
|
/** 当前的Tab */
|
||||||
const activeTab = ref('form')
|
const activeTab = ref('form')
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue