| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371 |
- /**
- * useFlow - 业务侧流程集成 Composable
- *
- * 设计目标:业务组件只需关心业务逻辑,无需关心流程引擎细节
- *
- * 使用示例:
- * const { startFlow, flowStatus, approvalHistory, isRunning, canWithdraw } = useFlow('leave_apply', businessKey)
- * await startFlow({ title: '张三请假申请', variables: { days: 3 } })
- */
- import { computed, readonly, ref, watch } from 'vue'
- import flowApi from '@/api/flow'
- import { useUserStore } from '@/store'
- /**
- * 业务流程集成
- * @param {string} processKey - 流程模型 Key(对应 FlowModel.modelKey)
- * @param {import('vue').Ref<string>|string} businessKeyRef - 业务唯一标识(可传 ref 或字符串)
- */
- export function useFlow(processKey, businessKeyRef) {
- const userStore = useUserStore()
- // ======= 状态 =======
- const statusData = ref(null) // FlowBusiness 业务状态数据
- const loading = ref(false)
- const submitting = ref(false)
- // ======= 计算属性 =======
- const flowStatus = computed(() => statusData.value?.status || 'none')
- /** 流程是否在运行中 */
- const isRunning = computed(() => flowStatus.value === 'running')
- /** 流程是否已结束(通过/驳回/取消) */
- const isFinished = computed(() =>
- ['approved', 'rejected', 'canceled'].includes(flowStatus.value),
- )
- /** 是否可以发起(未发起或已结束才能重新发起) */
- const canStart = computed(() =>
- flowStatus.value === 'none' || flowStatus.value === 'canceled',
- )
- /** 发起人是否可以撤回(运行中才能撤回) */
- const canWithdraw = computed(() =>
- isRunning.value && statusData.value?.applyUserId === userStore.userId,
- )
- /** 状态文本 */
- const statusText = computed(() => {
- const map = {
- none: '未发起',
- draft: '草稿',
- running: '审批中',
- approved: '已通过',
- rejected: '已驳回',
- canceled: '已取消',
- }
- return map[flowStatus.value] || '未知'
- })
- /** 状态徽标类型(对应 Naive UI NTag type) */
- const statusTagType = computed(() => {
- const map = {
- none: 'default',
- draft: 'default',
- running: 'warning',
- approved: 'success',
- rejected: 'error',
- canceled: 'default',
- }
- return map[flowStatus.value] || 'default'
- })
- // ======= 获取业务 Key =======
- function getBusinessKey() {
- if (typeof businessKeyRef === 'string')
- return businessKeyRef
- if (businessKeyRef?.value)
- return businessKeyRef.value
- return null
- }
- // ======= 核心方法 =======
- /**
- * 刷新流程状态
- */
- async function refreshStatus() {
- const businessKey = getBusinessKey()
- if (!businessKey)
- return
- loading.value = true
- try {
- const res = await flowApi.getProcessStatus(businessKey)
- if (res.code === 200 && res.data) {
- statusData.value = res.data
- }
- else {
- statusData.value = null
- }
- }
- catch (e) {
- statusData.value = null
- }
- finally {
- loading.value = false
- }
- }
- /**
- * 发起流程
- * @param {object} options
- * @param {string} options.title 流程标题(必填)
- * @param {string} [options.businessType] 业务类型(可选,用于分类过滤)
- * @param {object} [options.variables] 流程变量(可选)
- * @returns {Promise<{success: boolean, processInstanceId: string}>}
- */
- async function startFlow({ title, businessType, variables = {} } = {}) {
- const businessKey = getBusinessKey()
- if (!businessKey)
- throw new Error('businessKey 不能为空')
- if (!title)
- throw new Error('流程标题 title 不能为空')
- submitting.value = true
- try {
- const res = await flowApi.startProcess(processKey, {
- businessKey,
- businessType: businessType || processKey,
- title,
- variables,
- userId: userStore.userId,
- userName: userStore.userInfo?.nickName || userStore.userInfo?.userName,
- deptId: userStore.userInfo?.deptId,
- deptName: userStore.userInfo?.deptName,
- })
- if (res.code === 200) {
- await refreshStatus()
- return { success: true, processInstanceId: res.data }
- }
- return { success: false, message: res.message }
- }
- finally {
- submitting.value = false
- }
- }
- /**
- * 撤回流程(发起人在运行中撤回)
- * @param {string} [reason] 撤回原因
- */
- async function withdrawFlow(reason = '') {
- const businessKey = getBusinessKey()
- if (!businessKey)
- return
- submitting.value = true
- try {
- const res = await flowApi.withdrawProcess({
- processInstanceId: statusData.value?.processInstanceId,
- userId: userStore.userId,
- reason,
- })
- if (res.code === 200) {
- await refreshStatus()
- return { success: true }
- }
- return { success: false, message: res.message }
- }
- finally {
- submitting.value = false
- }
- }
- /**
- * 终止流程(管理员强制终止)
- * @param {string} reason 终止原因
- */
- async function terminateFlow(reason = '') {
- const businessKey = getBusinessKey()
- if (!businessKey)
- return
- submitting.value = true
- try {
- const res = await flowApi.terminateProcess(businessKey, {
- userId: userStore.userId,
- reason,
- })
- if (res.code === 200) {
- await refreshStatus()
- return { success: true }
- }
- return { success: false, message: res.message }
- }
- finally {
- submitting.value = false
- }
- }
- /**
- * 获取流程审批历史
- * @returns {Promise<Array>}
- */
- async function getApprovalHistory() {
- if (!statusData.value?.processInstanceId)
- return []
- try {
- const res = await flowApi.getProcessComments(statusData.value.processInstanceId)
- if (res.code === 200)
- return res.data || []
- }
- catch (e) {
- console.error('获取审批历史失败:', e)
- }
- return []
- }
- /**
- * 获取流程图信息(节点高亮)
- * @returns {Promise<object|null>}
- */
- async function getDiagramInfo() {
- if (!statusData.value?.processInstanceId)
- return null
- try {
- const res = await flowApi.getProcessDiagramInfo(statusData.value.processInstanceId)
- if (res.code === 200)
- return res.data
- }
- catch (e) {
- console.error('获取流程图失败:', e)
- }
- return null
- }
- // businessKey 变化时自动刷新状态
- if (businessKeyRef && typeof businessKeyRef !== 'string') {
- watch(businessKeyRef, (newKey) => {
- if (newKey)
- refreshStatus()
- }, { immediate: true })
- }
- else {
- // 字符串形式,立即初始化
- const bk = getBusinessKey()
- if (bk)
- refreshStatus()
- }
- return {
- // 状态
- statusData: readonly(statusData),
- flowStatus,
- statusText,
- statusTagType,
- loading: readonly(loading),
- submitting: readonly(submitting),
- // 计算属性
- isRunning,
- isFinished,
- canStart,
- canWithdraw,
- // 方法
- startFlow,
- withdrawFlow,
- terminateFlow,
- refreshStatus,
- getApprovalHistory,
- getDiagramInfo,
- }
- }
- /**
- * 用于审批页面(任务处理人视角)的 composable
- * 封装通过/驳回/转办等操作
- */
- export function useFlowTask() {
- const userStore = useUserStore()
- const processing = ref(false)
- /**
- * 审批通过
- */
- async function approve(taskId, comment, variables = {}) {
- processing.value = true
- try {
- const res = await flowApi.approveTask({
- taskId,
- userId: userStore.userId,
- comment,
- variables,
- })
- return { success: res.code === 200, message: res.message }
- }
- catch (e) {
- return { success: false, message: e.message }
- }
- finally {
- processing.value = false
- }
- }
- /**
- * 审批驳回
- */
- async function reject(taskId, comment) {
- processing.value = true
- try {
- const res = await flowApi.rejectTask({
- taskId,
- userId: userStore.userId,
- comment,
- })
- return { success: res.code === 200, message: res.message }
- }
- catch (e) {
- return { success: false, message: e.message }
- }
- finally {
- processing.value = false
- }
- }
- /**
- * 转办
- */
- async function delegate(taskId, targetUserId, comment = '') {
- processing.value = true
- try {
- const res = await flowApi.delegateTask({
- taskId,
- userId: userStore.userId,
- targetUserId,
- comment,
- })
- return { success: res.code === 200, message: res.message }
- }
- catch (e) {
- return { success: false, message: e.message }
- }
- finally {
- processing.value = false
- }
- }
- /**
- * 签收候选任务
- */
- async function claim(taskId) {
- processing.value = true
- try {
- const res = await flowApi.claimTask(taskId, userStore.userId)
- return { success: res.code === 200, message: res.message }
- }
- catch (e) {
- return { success: false, message: e.message }
- }
- finally {
- processing.value = false
- }
- }
- return {
- processing: readonly(processing),
- approve,
- reject,
- delegate,
- claim,
- }
- }
|