mirror of
https://gitee.com/cai_xiao_feng/lowflow-design.git
synced 2025-12-06 16:18:22 +08:00
通知节点
This commit is contained in:
parent
85df8b8a96
commit
757e56cf7c
2
src/typings/components.d.ts
vendored
2
src/typings/components.d.ts
vendored
@ -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']
|
||||
|
||||
@ -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]
|
||||
|
||||
@ -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 {
|
||||
|
||||
104
src/views/flowDesign/nodes/NotifyNode.vue
Normal file
104
src/views/flowDesign/nodes/NotifyNode.vue
Normal 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>
|
||||
@ -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
|
||||
|
||||
@ -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'
|
||||
|
||||
@ -5,7 +5,7 @@ import type { Field } from '@/components/Render/type'
|
||||
defineProps<{
|
||||
activeData: AssigneeNode
|
||||
fields: Field[]
|
||||
type: '审批' | '抄送' | '办理'
|
||||
type: '审批' | '抄送' | '办理' | '通知'
|
||||
}>()
|
||||
</script>
|
||||
|
||||
|
||||
68
src/views/flowDesign/panels/NotifyPanel.vue
Normal file
68
src/views/flowDesign/panels/NotifyPanel.vue
Normal 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>
|
||||
@ -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)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user