Pārlūkot izejas kodu

新增创建业务审批逻辑

hubin 1 gadu atpakaļ
vecāks
revīzija
4745f0e325

+ 6 - 0
src/router/index.ts

@@ -79,6 +79,12 @@ export const sysStaticRouter: Array<AppRouteRecordRaw> = [
 				name: 'flow_create_index',
 				meta: { hidden: true, title: '创建流程', icon: '' }
 			},
+			{
+				path: 'business',
+				component: () => import('@/views/flow/create/business.vue'),
+				name: 'flow_create_business',
+				meta: { hidden: true, title: '创建业务审批', icon: '' }
+			},
 			{
 				path: 'child',
 				component: () => import('@/views/flow/create/child.vue'),

+ 320 - 0
src/views/flow/create/business.vue

@@ -0,0 +1,320 @@
+<template>
+	<div class="create-approval">
+		<div class="create-approval-header flex flex_align-center">
+			<div v-if="false" class="create-approval-header-back">
+				<el-icon :size="18">
+					<ArrowLeft />
+				</el-icon>
+			</div>
+			<div class="create-approval-header-left-zh">
+				<div class="create-approval-header-name">{{ processName }}</div>
+				<div v-if="false" class="create-approval-header-time">最近保存:6 分钟前</div>
+			</div>
+			<div class="create-approval-header-tab-list">
+				<div
+					v-for="(item, idx) in componentsArr"
+					:key="idx"
+					class="create-approval-header-tab-item"
+					:class="[item.value === activeTab ? 'active' : '']"
+					@click="activeComponent(idx)"
+				>
+					<span class="create-approval-header-tab-counter">{{ idx + 1 }}</span>
+					<span>{{ item.label }}</span>
+				</div>
+			</div>
+			<div class="create-approval-header-right">
+				<el-button type="primary" @click="submitHandler">发布</el-button>
+			</div>
+		</div>
+		<div class="create-approval-main">
+			<component :is="item.component" v-for="(item, idx) in componentsArr" v-show="item.value === activeTab" ref="compRefs" :key="idx" />
+		</div>
+	</div>
+</template>
+
+<script setup name="flow_create">
+import { computed, nextTick, ref } from 'vue'
+import { storeToRefs } from 'pinia'
+import useFlowStore from '@/store/modules/flow'
+import BasicInfoTab from './components/BasicInfo.vue'
+import FlowDesignTab from './components/FlowDesign.vue'
+import { useRoute, useRouter } from 'vue-router'
+import process from '@/api/flow/process'
+import useStore from '@/store'
+import { ElMessage } from 'element-plus'
+import ExtendSetTab from "@/views/flow/create/components/ExtendSet.vue";
+const { tagsView } = useStore()
+const router = useRouter()
+const route = useRoute()
+const flowStore = useFlowStore()
+const { categoryId, processId, processIcon, processKey, processName, remark, modelContent, processForm, processSetting } = storeToRefs(flowStore)
+
+const compRefs = ref() // 实例化子组件
+const cache_components = ref({})
+const componentsArr = [
+	{
+		component: BasicInfoTab,
+		label: '基础信息',
+		value: '基础信息'
+		// ref: 'basicInfoRef'
+	},
+	{
+		component: FlowDesignTab,
+		label: '流程设计',
+		value: '流程设计'
+		// ref: 'flowDesignRef'
+	},
+	{
+		component: ExtendSetTab,
+		label: '扩展设置',
+		value: '扩展设置'
+		// ref: 'extendSetRef'
+	}
+]
+const activeTab = ref('基础信息')
+const removeCurTab = () => {
+	cache_components.value = {}
+	const _view = tagsView.visitedViews.find(v => v.path === '/flow_create/business')
+	if (_view)
+		tagsView.delView(_view).then(res => {
+			const latestView = res.visitedViews.slice(-1)[0]
+			if (latestView && latestView.fullPath) {
+				router.push(latestView.fullPath)
+			} else {
+				router.push('/')
+			}
+			flowStore.$reset()
+		})
+}
+const submitHandler = async () => {
+	// 基础信息
+	// 表单设计
+	// 流程设计
+	// 扩展设置
+	let leavePageFlag = await validateTabs()
+	// let _id = queryObj.value.id
+	// if (_id) {
+	//
+	// } else {
+	// 	leavePageFlag = await validateTabs()
+	// }
+	if (!leavePageFlag) return
+	const params = {
+		categoryId: categoryId.value,
+		processIcon: processIcon.value,
+		processType: 'business',
+		processKey: processKey.value,
+		processName: processName.value,
+		remark: remark.value,
+		processId: processId.value,
+		processForm: processForm.value,
+		modelContent: JSON.stringify({
+			key: processKey.value,
+			name: processName.value,
+			nodeConfig: JSON.parse(modelContent.value || '{}')
+		}),
+		processSetting: processSetting.value
+	}
+	const res = await process.progressCreateApi(params)
+	ElMessage.success('操作成功')
+	// 创建完成 删除 当前tab页
+	removeCurTab()
+}
+
+// 切换选项卡之前,做相应的保存操作
+const validateTabs = async () => {
+	const _refs = compRefs.value
+	// await nextTick()
+	for (let i = 0; i < _refs.length; i++) {
+		let bool = true
+		/*// 若没开启过的 tab 需要尝试 进行更新数据
+		if (!cache_components.value[i]) {
+			cache_components.value[i]?.updateCompInfo()
+		}*/
+		const _validate =
+			_refs[i]?.validate ||
+			function () {
+				return Promise.resolve()
+			}
+		await _validate().catch(e => {
+			activeTab.value = componentsArr[i].label
+			if (activeTab.value === '表单设计') {
+				ElMessage.error('请为流程设计表单内容')
+			}
+			bool = false
+		})
+		if (!bool) return false
+	}
+	return true
+}
+
+const activeComponent = index => {
+	const cur = componentsArr[index]
+	if (activeTab.value !== cur.value) {
+		if (activeTab.value === '表单设计') {
+			compRefs.value[1]?.exportJsonEv()
+		}
+		// 当前缓存
+		if (!cache_components.value[index]) {
+			// 更新数据
+			const updateCompInfo = compRefs.value[index]?.updateCompInfo
+			// console.error('刷新数据')
+			// console.error(updateCompInfo, 'updateCompInfo')
+			if (updateCompInfo) {
+				updateCompInfo()
+			}
+		}
+		activeTab.value = cur.value
+	}
+}
+
+const queryObj = computed(() => route.query)
+
+const getCurrentProcessDetailEv = () => {
+	let _id = queryObj.value.id
+	if (_id) {
+		cache_components.value = {}
+		process.processDetailApi(_id).then(res => {
+			processId.value = res.processId
+			categoryId.value = res.categoryId
+			processIcon.value = res.processIcon
+			processKey.value = res.processKey
+			processName.value = res.processName
+			remark.value = res.remark
+			let nodeConfig = JSON.parse(res.modelContent).nodeConfig
+			modelContent.value = JSON.stringify(nodeConfig)
+			processForm.value = res.processForm
+			flowStore.setProcessForm(processForm)
+			flowStore.setProcessSetting(res.processSetting)
+			// 默认执行一次保存
+			const _refs = compRefs.value
+			for (let i = 0; i < _refs.length; i++) {
+				const updateCompInfo = compRefs.value[i]?.updateCompInfo
+				if (updateCompInfo) {
+					updateCompInfo()
+				}
+			}
+		})
+	}
+}
+
+getCurrentProcessDetailEv()
+</script>
+
+<style scoped lang="scss">
+.create-approval {
+	height: 100%;
+	min-width: 1200px;
+	min-height: 600px;
+	overflow: auto;
+
+	&-header {
+		justify-content: flex-start;
+		height: 64px;
+		//z-index: 12;
+		position: relative;
+		box-shadow: 0 1px 6px 0 rgba(0, 0, 0, 0.06);
+		padding-left: 16px;
+		padding-right: 20px;
+
+		&-back {
+			display: flex;
+			justify-content: flex-start;
+			align-items: center;
+			height: 100%;
+			margin-right: 15px;
+		}
+
+		&-left-zh {
+			width: calc(50% - 316.5px);
+			min-width: 248px;
+		}
+
+		&-name {
+			font-weight: 500;
+			font-size: 16px;
+			white-space: nowrap;
+			cursor: pointer;
+			width: -webkit-fit-content;
+			width: -moz-fit-content;
+			width: fit-content;
+			max-width: 100%;
+			height: 24px;
+			overflow: hidden;
+			text-overflow: ellipsis;
+		}
+
+		&-time {
+			width: 100%;
+			height: 20px;
+			line-height: 20px;
+			font-size: 12px;
+			color: #8f959e;
+			overflow: hidden;
+			white-space: nowrap;
+			text-overflow: ellipsis;
+		}
+
+		&-tab-list {
+			margin-left: 10px;
+			flex-shrink: 0;
+			display: flex;
+			justify-content: center;
+		}
+
+		&-tab-item {
+			font-family: PingFang SC, Microsoft YaHei;
+			font-size: 18px;
+			line-height: 28px;
+			color: #646a73;
+			margin-right: 42px;
+			padding: 18px 0;
+			border-bottom: 2px solid transparent;
+			cursor: pointer;
+
+			&.active {
+				// var(--el-color-primary);
+				border-bottom-color: var(--el-color-primary);
+				color: var(--el-color-primary);
+				font-weight: 500;
+
+				.create-approval-header-tab-counter {
+					border-color: var(--el-color-primary);
+					background-color: var(--el-color-primary);
+					color: var(--el-color-white);
+					font-weight: 400;
+				}
+			}
+		}
+
+		&-tab-counter {
+			display: inline-block;
+			margin-right: 6px;
+			width: 24px;
+			height: 24px;
+			border-radius: 50%;
+			border: 1px solid #646a73;
+			font-size: 16px;
+			line-height: 22px;
+			text-align: center;
+		}
+
+		&-right {
+			flex: 1 1;
+			flex-shrink: 0;
+			display: flex;
+			justify-content: flex-end;
+			align-items: center;
+			position: relative;
+			height: 100%;
+			width: -webkit-fit-content;
+			width: -moz-fit-content;
+			width: fit-content;
+		}
+	}
+
+	&-main {
+		height: calc(100% - 64px);
+	}
+}
+</style>

+ 2 - 1
src/views/flow/group/components/listGroup.vue

@@ -75,6 +75,7 @@
 									<div class="group_itemSeeable">
 										<el-tag round>V{{ element.processVersion }}</el-tag>
 										<el-tag v-if="element.processType === 'child'" type="warning" round>子流程</el-tag>
+										<el-tag v-if="element.processType === 'business'" type="success" round>业务流程</el-tag>
 										<el-tag v-if="element.processState === 0" type="danger" round>已停用</el-tag>
 									</div>
 									<div class="group_itemSeeable">{{ element.processKey }}</div>
@@ -273,7 +274,7 @@ const copyEv = async (id: any) => {
 
 // 修改
 const updateEv = async (element: any) => {
-	const jumpRouterUrl = element.processType === 'child' ? '/flow_create/child' : '/flow_create/index'
+	const jumpRouterUrl = '/flow_create/' + (element.processType === 'main' ? 'index' : element.processType)
 	router.push(`${jumpRouterUrl}?id=${element.processId}`)
 }
 

+ 5 - 0
src/views/flow/group/index.vue

@@ -12,7 +12,12 @@
 						<el-button type="primary" plain :icon="CircleCheck" @click="changeComponent('sort')">完 成</el-button>
 						<el-button type="info" plain @click="changeComponent">取 消</el-button>
 					</template>
+					<el-tooltip content="不可直接审批只能作为流程任务" placement="bottom" effect="light">
 					<el-button icon="Plus" @click="createProcessEv('child')">创建子流程</el-button>
+					</el-tooltip>
+					<el-tooltip content="关联业务逻辑执行的审批流程" placement="bottom" effect="light">
+						<el-button icon="Plus" @click="createProcessEv('business')">创建业务审批</el-button>
+					</el-tooltip>
 					<el-button :type="sortFlag ? 'info' : 'primary'" icon="Plus" :disabled="sortFlag" @click="createProcessEv('index')">创建审批</el-button>
 				</div>
 			</el-header>

+ 298 - 0
src/views/flow/test/business.vue

@@ -0,0 +1,298 @@
+<template>
+	<div class="pageWrap bgc">
+		<div class="content-warp flex-column-page-wrap">
+			<!-- 公用搜索组件 -->
+			<LeSearchForm ref="searchForm" v-model:searchData="searchData" :forms="forms" :loading="tableOpts.options.loading"> </LeSearchForm>
+
+			<!--  LeTable 组件使用  -->
+			<LeTable
+				ref="tableRef"
+				v-model:searchParams="tableOpts.searchParams"
+				v-bind="tableOpts"
+				v-model:curRow="tableOpts.curRow"
+				v-model:checked-options="checkedColumns"
+				:columns="activeColumns"
+			>
+				<template #toolLeft>
+					<el-button type="primary" @click="addHandler">
+						<el-icon class="btn-icon">
+							<Plus />
+						</el-icon>
+					</el-button>
+					<el-button type="danger" :disabled="curSelectionRows.length === 0" @click="batch_del">
+						<el-icon class="btn-icon">
+							<Delete />
+						</el-icon>
+					</el-button>
+				</template>
+
+				<template #statusSlot="scope">
+					<status-indicator v-if="scope.row.status === 1" pulse type="success"></status-indicator>
+					<status-indicator v-else pulse type="danger"></status-indicator>
+				</template>
+
+				<template #actionSlot="scope">
+					<el-tooltip content="编辑" placement="bottom" effect="light">
+						<el-icon class="ibt0" @click="table_edit(scope.row)">
+							<Edit />
+						</el-icon>
+					</el-tooltip>
+					<el-divider direction="vertical"></el-divider>
+					<el-popconfirm title="确定删除吗?" @confirm="table_del(scope.row)">
+						<template #reference>
+							<el-icon class="ibt0">
+								<Delete />
+							</el-icon>
+						</template>
+					</el-popconfirm>
+				</template>
+			</LeTable>
+		</div>
+
+		<LeFormConfigDialog
+			v-if="visible"
+			ref="dialogUserRef"
+			v-model="visible"
+			:title="`${isCreate ? '新增' : '编辑'}岗位`"
+			width="600px"
+			:form-data="activeData"
+			:form-options="formOptions"
+			@submit="submitHandler"
+		/>
+	</div>
+</template>
+<script lang="tsx" setup>
+import post from '@/api/system/post'
+import { computed, nextTick, ref, watch } from 'vue'
+import { ElMessage, ElMessageBox, ElTree } from 'element-plus'
+import { useTablePage } from '@/hooks/useTablePage'
+import { Plus, Delete } from '@element-plus/icons-vue'
+import StatusIndicator from '@/components/StatusIndicator'
+
+const visible = ref(false) // 弹窗显示隐藏
+const isCreate = ref(true)
+const activeData = ref({})
+const formsDialog = [
+	{
+		prop: 'name',
+		label: '岗位名称',
+		itemType: 'input',
+		rules: [{ required: true, message: '请输入岗位名称', trigger: 'blur' }]
+	},
+	{
+		prop: 'code',
+		label: '岗位编码',
+		itemType: 'input',
+		rules: [{ required: true, message: '请输入岗位编码', trigger: 'blur' }]
+	},
+	{
+		prop: 'sort',
+		label: '排序',
+		itemType: 'inputNumber'
+	},
+	{
+		prop: 'status',
+		label: '状态',
+		itemType: 'switch',
+		activeText: '正常',
+		inactiveText: '禁用'
+	},
+	{
+		prop: 'remark',
+		label: '备注',
+		itemType: 'input'
+	}
+]
+// 新增的表单 和 编辑的表单
+const formOptions = computed(() => {
+	return {
+		forms: formsDialog,
+		labelWidth: 120,
+		span: 30,
+		showResetBtn: true,
+		formConfig: {
+			showCancelBtn: true,
+			submitLoading: false
+		}
+	}
+})
+const groupFilterText = ref('')
+const treeRef = ref<InstanceType<typeof ElTree>>()
+
+// 表格搜索条件
+const forms = ref([
+	{
+		prop: 'keyword',
+		label: '采购单名称:',
+		itemType: 'input',
+		placeholder: '请输入采购单名称'
+	}
+])
+
+// table列表数据请求
+const queryList = async () => {
+	const { options, searchParams } = tableOpts
+	options.loading = true
+	try {
+		const { records: list, total } = await post.postPageApi(searchParams)
+		tableOpts.total = total
+		tableOpts.list = list
+	} catch {
+		console.log('获取列表数据失败')
+		tableOpts.total = 0
+		tableOpts.list = []
+		options.loading = false // 更改加载中的 loading值
+	} finally {
+		options.loading = false
+	}
+}
+
+// table 参数
+const columns = [
+	{
+		prop: 'name',
+		label: '岗位名称',
+		minWidth: 80,
+		slots: {
+			default: 'filterAvatarSlot'
+		}
+	},
+	{
+		prop: 'status',
+		label: '状态',
+		minWidth: 50,
+		slots: {
+			default: 'statusSlot'
+		}
+	},
+	{
+		prop: 'code',
+		label: '编码',
+		minWidth: 100
+	},
+	{
+		prop: 'remark',
+		label: '备注',
+		minWidth: 100
+	},
+	{
+		prop: 'sort',
+		label: '排序',
+		minWidth: 80
+	},
+	{
+		prop: 'updateBy',
+		label: '修改人',
+		minWidth: 100
+	},
+	{
+		prop: 'updateTime',
+		label: '修改时间',
+		minWidth: 126
+	},
+	{
+		prop: 'createBy',
+		label: '创建人',
+		minWidth: 100
+	},
+	{
+		prop: 'createTime',
+		label: '创建时间',
+		minWidth: 126
+	},
+	{
+		prop: 'action',
+		label: '操作',
+		width: 100,
+		fixed: 'right',
+		slots: {
+			default: 'actionSlot'
+		}
+	}
+]
+
+const { searchData, tableOpts, checkedColumns, activeColumns, curSelectionRows, updateParams } = useTablePage(
+	{
+		options: {
+			showIndex: false
+		},
+		// 需要展示的列
+		columns,
+		// 控制列配置
+		columnsConfig: {
+			columns
+		}
+	},
+	{
+		queryList,
+		fetchImmediate: false
+	}
+)
+
+// 删除
+const deleteItem = async ids => {
+	// try {
+	await post.postDeleteApi(ids)
+	ElMessage.error(`删除成功~`)
+	updateParams()
+	// } catch (e) {
+	// 	console.log('删除失败')
+	// 	ElMessage.error(`删除失败~`)
+	// }
+}
+
+// 单个删除
+const table_del = row => {
+	deleteItem([row.id])
+}
+
+//批量删除
+const batch_del = () => {
+	const ids = curSelectionRows.value.map(item => item.id) // 多选数据
+	ElMessageBox.confirm('是否删除选中数据?', '提示', {
+		confirmButtonText: '确认',
+		cancelButtonText: '取消',
+		type: 'error',
+		buttonSize: 'default'
+	}).then(() => {
+		deleteItem(ids)
+	})
+}
+
+const table_edit = async row => {
+	isCreate.value = false
+	activeData.value = { ...row, status: row.status ? true : false }
+	visible.value = true
+}
+
+// 弹窗事件
+const submitHandler = async params => {
+	formOptions.value.formConfig.submitLoading = true
+	try {
+		params.status = params.status ? 1 : 0
+		params.id = activeData.value.id ? activeData.value.id : null
+		await post.postAddOrEditSaveApi(params)
+		ElMessage.success(`${isCreate.value ? '新增' : '修改'}成功~`)
+		visible.value = false
+		updateParams()
+		formOptions.value.formConfig.submitLoading = false
+	} catch (e) {
+		console.log(e)
+		formOptions.value.formConfig.submitLoading = false
+	}
+}
+
+const addHandler = () => {
+	isCreate.value = true
+	activeData.value = {}
+	visible.value = true
+}
+
+nextTick(() => {
+	queryList()
+})
+
+watch(groupFilterText, val => {
+	treeRef.value!.filter(val)
+})
+</script>