通知节点

This commit is contained in:
caixiaofeng 2024-05-27 22:24:29 +08:00
parent 85df8b8a96
commit 757e56cf7c
9 changed files with 234 additions and 3 deletions

View File

@ -13,6 +13,7 @@ declare module 'vue' {
ElButtonGroup: typeof import('element-plus/es')['ElButtonGroup']
ElCard: typeof import('element-plus/es')['ElCard']
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
ElCol: typeof import('element-plus/es')['ElCol']
ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
@ -25,7 +26,6 @@ declare module 'vue' {
ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElIcon: typeof import('element-plus/es')['ElIcon']
ElInput: typeof import('element-plus/es')['ElInput']
ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
ElLink: typeof import('element-plus/es')['ElLink']
ElOption: typeof import('element-plus/es')['ElOption']
ElPopconfirm: typeof import('element-plus/es')['ElPopconfirm']

View File

@ -6,6 +6,7 @@ import type {
ApprovalNode,
BranchNode,
CcNode,
NotifyNode,
ConditionNode,
ExclusiveNode,
NodeType
@ -167,6 +168,33 @@ const addTimer = (node: FlowNode) => {
child.pid = id
}
}
const addNotify = (node: FlowNode) => {
const child = node.child
const id = nextId()
node.child = {
id: id,
pid: node.id,
name: '消息通知',
type: 'notify',
child: child,
assigneeType: 'user',
formUser: '',
formRole: '',
users: [],
roles: [],
leader: 1,
orgLeader: 1,
choice: false,
self: false,
types: ['site'],
subject: '',
content: ''
} as NotifyNode
if (child) {
child.pid = id
}
}
const addApproval = (node: FlowNode) => {
const child = node.child
const id = nextId()
@ -210,6 +238,7 @@ const addNode = (type: NodeType, node: FlowNode) => {
condition: addCondition,
cc: addCc,
timer: addTimer,
notify: addNotify,
approval: addApproval
}
const fun = addMap[type]

View File

@ -26,6 +26,10 @@ const addTimerNode = () => {
$emits('addNode', 'timer')
popoverRef.value?.hide()
}
const addNotifyNode = () => {
$emits('addNode', 'notify')
popoverRef.value?.hide()
}
</script>
<template>
@ -54,6 +58,10 @@ const addTimerNode = () => {
<svg-icon name="el:Timer" />
<el-text>计时等待</el-text>
</div>
<div class="node-select" @click="addNotifyNode">
<svg-icon name="el:BellFilled" />
<el-text>消息通知</el-text>
</div>
</el-space>
<template #reference>
<el-button
@ -105,6 +113,10 @@ const addTimerNode = () => {
&.Timer {
background-color: #e872b7;
}
&.BellFilled {
background-color: #95d475;
}
}
.el-text {

View File

@ -0,0 +1,104 @@
<script setup lang="ts">
import Node from './Node.vue'
import type { NotifyNode } from './type'
import type { Ref } from 'vue'
import type { ErrorInfo } from './type'
import { getById } from '@/api/modules/role'
import type { Field } from '@/components/Render/type'
import { getByUsername } from '@/api/modules/user'
const props = defineProps<{
node: NotifyNode
}>()
const { fields, nodesError } = inject<{
fields: Ref<Field[]>
nodesError: Ref<Recordable<ErrorInfo[]>>
}>('flowDesign', { fields: ref([]), nodesError: ref({}) })
const content = ref<string>('')
watchEffect(() => {
const errors: ErrorInfo[] = []
const {
id,
assigneeType,
name,
users,
roles,
leader,
choice,
formUser,
formRole,
orgLeader,
subject,
types
} = props.node
if (assigneeType === 'user') {
if (users.length > 0) {
const all = users.map((user) => getByUsername(user))
Promise.all(all).then((users) => {
content.value = users.map((user) => user.data.name).join('、')
})
} else {
errors.push({ id: id, name: name, message: '未指定人员' })
content.value = '未指定人员'
}
} else if (assigneeType === 'choice') {
content.value = `发起人自选(${choice ? '多选' : '单选'}`
} else if (assigneeType === 'self') {
content.value = '发起人自己'
} else if (assigneeType === 'leader') {
content.value = leader === 1 ? '直属上级' : `${leader}级上级`
} else if (assigneeType === 'orgLeader') {
content.value = orgLeader === 1 ? '直属主管' : `${orgLeader}级主管`
} else if (assigneeType === 'formUser') {
if (!formUser) {
errors.push({ id: id, name: name, message: '未指定表单内人员' })
}
const title = fields.value.find((e) => e.id === formUser)?.label || formUser || '?'
content.value = `表单内(${title})人员`
} else if (assigneeType === 'formRole') {
if (!formRole) {
errors.push({ id: id, name: name, message: '未指定表单内角色' })
}
const title = fields.value.find((e) => e.id === formRole)?.label || formRole || '?'
content.value = `表单内(${title})角色`
} else if (assigneeType === 'role') {
if (roles.length > 0) {
const all = roles.map((id) => getById(id))
Promise.all(all).then((roles) => {
content.value = roles.map((res) => res.data.name).join('、')
})
} else {
errors.push({ id: id, name: name, message: '未指定角色' })
content.value = '未指定角色'
}
} else {
errors.push({ id: id, name: name, message: '未知错误' })
content.value = name
}
if (types.length === 0) {
errors.push({ id: id, name: name, message: '未指定通知类型' })
}
if (!subject) {
errors.push({ id: id, name: name, message: '消息主题为空' })
}
if (!props.node.content) {
errors.push({ id: id, name: name, message: '消息内容为空' })
}
//
if (errors.length > 0) {
nodesError.value[id] = errors
} else {
delete nodesError.value[id]
}
})
</script>
<template>
<Node v-bind="$attrs" icon="el:BellFilled" color="#95d475" :node="node">
<el-text>{{ content }}</el-text>
</Node>
</template>
<style scoped lang="scss"></style>

View File

@ -6,6 +6,7 @@ import End from './EndNode.vue'
import Approval from './ApprovalNode.vue'
import Cc from './CcNode.vue'
import Timer from './TimerNode.vue'
import Notify from './NotifyNode.vue'
import Exclusive from './ExclusiveNode.vue'
import Condition from './ConditionNode.vue'
@ -17,6 +18,7 @@ const nodes: Recordable<Component> = {
approval: Approval,
cc: Cc,
timer: Timer,
notify: Notify,
exclusive: Exclusive,
condition: Condition,
end: End

View File

@ -1,6 +1,14 @@
import type { FilterRules } from '@/components/AdvancedFilter/type'
export type NodeType = 'start' | 'approval' | 'cc' | 'exclusive' | 'timer' | 'condition' | 'end'
export type NodeType =
| 'start'
| 'approval'
| 'cc'
| 'exclusive'
| 'timer'
| 'notify'
| 'condition'
| 'end'
export interface FlowNode {
id: string
@ -50,6 +58,12 @@ export interface CcNode extends AssigneeNode {
formProperties: FormProperty[]
}
export interface NotifyNode extends AssigneeNode {
types: ('site' | 'email' | 'sms' | 'wechat' | 'dingtalk' | 'feishu')[]
subject: string
content: string
}
export interface ApprovalNode extends AssigneeNode {
// 多人审批方式
multi: 'sequential' | 'joint' | 'single'

View File

@ -5,7 +5,7 @@ import type { Field } from '@/components/Render/type'
defineProps<{
activeData: AssigneeNode
fields: Field[]
type: '审批' | '抄送' | '办理'
type: '审批' | '抄送' | '办理' | '通知'
}>()
</script>

View File

@ -0,0 +1,68 @@
<script setup lang="ts">
import type { NotifyNode } from '../nodes/type'
import type { Ref } from 'vue'
import type { Field } from '@/components/Render/type'
import AssigneePanel from './AssigneePanel.vue'
const { fields } = inject<{ fields: Ref<Field[]> }>('flowDesign', { fields: ref([]) })
defineProps<{
activeData: NotifyNode
}>()
</script>
<template>
<el-form label-position="top" label-width="90px">
<AssigneePanel :active-data="activeData" :fields="fields" type="通知" />
<el-form-item prop="types" label="通知类型">
<el-checkbox-group v-model="activeData.types">
<el-checkbox label="站内" value="site" />
<el-checkbox label="邮件" value="email" />
<el-checkbox label="短信" value="sms" />
<el-checkbox label="企业微信" value="wechat" />
<el-checkbox label="钉钉" value="dingtalk" />
<el-checkbox label="飞书" value="feishu" />
</el-checkbox-group>
</el-form-item>
<el-form-item prop="subject" label="消息主题">
<template #label>
<div class="flex-items-center gap3px">
<el-tooltip content="可以使用 ${字段名} 字段名填充内容" placement="top">
<el-icon>
<QuestionFilled />
</el-icon>
</el-tooltip>
<span>消息主题</span>
</div>
</template>
<el-input
v-model="activeData.subject"
:maxlength="255"
clearable
placeholder="请输入消息主题"
/>
</el-form-item>
<el-form-item prop="content" label="消息内容">
<template #label>
<div class="flex-items-center gap3px">
<el-tooltip content="可以使用 ${字段名} 字段名填充内容" placement="top">
<el-icon>
<QuestionFilled />
</el-icon>
</el-tooltip>
<span>消息内容</span>
</div>
</template>
<el-input
v-model="activeData.content"
:autosize="{ minRows: 6, maxRows: 8 }"
type="textarea"
:maxlength="1000"
show-word-limit
placeholder="请输入消息内容"
>
</el-input>
</el-form-item>
</el-form>
</template>
<style scoped lang="scss"></style>

View File

@ -5,6 +5,7 @@ import Start from './StartPanel.vue'
import Approval from './ApprovalPanel.vue'
import Cc from './CcPanel.vue'
import Timer from './TimerPanel.vue'
import Notify from './NotifyPanel.vue'
import Condition from './ConditionPanel.vue'
import type { FlowNode } from '../nodes/type'
@ -17,6 +18,7 @@ const panels: Recordable<Component> = {
approval: Approval,
cc: Cc,
timer: Timer,
notify: Notify,
condition: Condition
}
const showInput = ref(false)