mirror of
https://gitee.com/hhyykk/ipms-sjy-ui.git
synced 2025-07-15 19:35:07 +08:00
Merge branch 'feature/bpm' of https://gitee.com/yudaocode/yudao-ui-admin-vue3
This commit is contained in:
@ -291,10 +291,16 @@ const doSocialLogin = async (type: number) => {
|
||||
await getTenantId()
|
||||
// 如果获取不到,则需要弹出提示,进行处理
|
||||
if (!authUtil.getTenantId()) {
|
||||
await message.prompt('请输入租户名称', t('common.reminder')).then(async ({ value }) => {
|
||||
const res = await LoginApi.getTenantIdByName(value)
|
||||
try {
|
||||
const data = await message.prompt('请输入租户名称', t('common.reminder'))
|
||||
if (data?.action !== 'confirm') throw 'cancel'
|
||||
const res = await LoginApi.getTenantIdByName(data.value)
|
||||
authUtil.setTenantId(res)
|
||||
})
|
||||
} catch (error) {
|
||||
if (error === 'cancel') return
|
||||
} finally {
|
||||
loginLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
// 计算 redirectUri
|
||||
|
124
src/views/bpm/category/CategoryForm.vue
Normal file
124
src/views/bpm/category/CategoryForm.vue
Normal file
@ -0,0 +1,124 @@
|
||||
<template>
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="100px"
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<el-form-item label="分类名" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入分类名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="分类标志" prop="code">
|
||||
<el-input v-model="formData.code" placeholder="请输入分类标志" />
|
||||
</el-form-item>
|
||||
<el-form-item label="分类状态" prop="status">
|
||||
<el-radio-group v-model="formData.status">
|
||||
<el-radio
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="分类排序" prop="sort">
|
||||
<el-input-number
|
||||
v-model="formData.sort"
|
||||
placeholder="请输入分类排序"
|
||||
class="!w-1/1"
|
||||
:precision="0"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
|
||||
import { CategoryApi, CategoryVO } from '@/api/bpm/category'
|
||||
|
||||
/** BPM 流程分类 表单 */
|
||||
defineOptions({ name: 'CategoryForm' })
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
code: undefined,
|
||||
status: undefined,
|
||||
sort: undefined
|
||||
})
|
||||
const formRules = reactive({
|
||||
name: [{ required: true, message: '分类名不能为空', trigger: 'blur' }],
|
||||
code: [{ required: true, message: '分类标志不能为空', trigger: 'blur' }],
|
||||
status: [{ required: true, message: '分类状态不能为空', trigger: 'blur' }],
|
||||
sort: [{ required: true, message: '分类排序不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type: string, id?: number) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = t('action.' + type)
|
||||
formType.value = type
|
||||
resetForm()
|
||||
// 修改时,设置数据
|
||||
if (id) {
|
||||
formLoading.value = true
|
||||
try {
|
||||
formData.value = await CategoryApi.getCategory(id)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
await formRef.value.validate()
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value as unknown as CategoryVO
|
||||
if (formType.value === 'create') {
|
||||
await CategoryApi.createCategory(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await CategoryApi.updateCategory(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
code: undefined,
|
||||
status: undefined,
|
||||
sort: undefined
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
</script>
|
198
src/views/bpm/category/index.vue
Normal file
198
src/views/bpm/category/index.vue
Normal file
@ -0,0 +1,198 @@
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="分类名" prop="name">
|
||||
<el-input
|
||||
v-model="queryParams.name"
|
||||
placeholder="请输入分类名"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="分类标志" prop="code">
|
||||
<el-input
|
||||
v-model="queryParams.code"
|
||||
placeholder="请输入分类标志"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="分类状态" prop="status">
|
||||
<el-select
|
||||
v-model="queryParams.status"
|
||||
placeholder="请选择分类状态"
|
||||
clearable
|
||||
class="!w-240px"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.createTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openForm('create')"
|
||||
v-hasPermi="['bpm:category:create']"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||
<el-table-column label="分类编号" align="center" prop="id" />
|
||||
<el-table-column label="分类名" align="center" prop="name" />
|
||||
<el-table-column label="分类标志" align="center" prop="code" />
|
||||
<el-table-column label="分类描述" align="center" prop="description" />
|
||||
<el-table-column label="分类状态" align="center" prop="status">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="分类排序" align="center" prop="sort" />
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="操作" align="center">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['bpm:category:update']"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['bpm:category:delete']"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<CategoryForm ref="formRef" @success="getList" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import download from '@/utils/download'
|
||||
import { CategoryApi, CategoryVO } from '@/api/bpm/category'
|
||||
import CategoryForm from './CategoryForm.vue'
|
||||
|
||||
/** BPM 流程分类 列表 */
|
||||
defineOptions({ name: 'BpmCategory' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const list = ref<CategoryVO[]>([]) // 列表的数据
|
||||
const total = ref(0) // 列表的总页数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: undefined,
|
||||
code: undefined,
|
||||
status: undefined,
|
||||
createTime: []
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await CategoryApi.getCategoryPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
formRef.value.open(type, id)
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await CategoryApi.deleteCategory(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
@ -11,11 +11,7 @@
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="定义分类" align="center" prop="category" width="100">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.BPM_MODEL_CATEGORY" :value="scope.row.category" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="定义分类" align="center" prop="categoryName" width="100" />
|
||||
<el-table-column label="表单信息" align="center" prop="formType" width="200">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
@ -57,18 +53,6 @@
|
||||
width="300"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column label="操作" align="center" width="150" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="handleAssignRule(scope.row)"
|
||||
v-hasPermi="['bpm:task-assign-rule:query']"
|
||||
>
|
||||
分配规则
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
@ -88,8 +72,8 @@
|
||||
<Dialog title="流程图" v-model="bpmnDetailVisible" width="800">
|
||||
<MyProcessViewer
|
||||
key="designer"
|
||||
v-model="bpmnXML"
|
||||
:value="bpmnXML as any"
|
||||
v-model="bpmnXml"
|
||||
:value="bpmnXml as any"
|
||||
v-bind="bpmnControlForm"
|
||||
:prefix="bpmnControlForm.prefix"
|
||||
/>
|
||||
@ -97,7 +81,6 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import { MyProcessViewer } from '@/components/bpmnProcessDesigner/package'
|
||||
import * as DefinitionApi from '@/api/bpm/definition'
|
||||
@ -129,16 +112,6 @@ const getList = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
/** 点击任务分配按钮 */
|
||||
const handleAssignRule = (row) => {
|
||||
push({
|
||||
name: 'BpmTaskAssignRuleList',
|
||||
query: {
|
||||
modelId: row.id
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/** 流程表单的详情按钮操作 */
|
||||
const formDetailVisible = ref(false)
|
||||
const formDetailPreview = ref({
|
||||
@ -160,12 +133,12 @@ const handleFormDetail = async (row) => {
|
||||
|
||||
/** 流程图的详情按钮操作 */
|
||||
const bpmnDetailVisible = ref(false)
|
||||
const bpmnXML = ref(null)
|
||||
const bpmnXml = ref(null)
|
||||
const bpmnControlForm = ref({
|
||||
prefix: 'flowable'
|
||||
})
|
||||
const handleBpmnDetail = async (row) => {
|
||||
bpmnXML.value = await DefinitionApi.getProcessDefinitionBpmnXML(row.id)
|
||||
bpmnXml.value = (await DefinitionApi.getProcessDefinition(row.id))?.bpmnXml
|
||||
bpmnDetailVisible.value = true
|
||||
}
|
||||
|
||||
|
@ -13,8 +13,8 @@
|
||||
<el-form-item label="描述">
|
||||
<el-input v-model="formData.description" placeholder="请输入描述" type="textarea" />
|
||||
</el-form-item>
|
||||
<el-form-item label="成员" prop="memberUserIds">
|
||||
<el-select v-model="formData.memberUserIds" multiple placeholder="请选择成员">
|
||||
<el-form-item label="成员" prop="userIds">
|
||||
<el-select v-model="formData.userIds" multiple placeholder="请选择成员">
|
||||
<el-option
|
||||
v-for="user in userList"
|
||||
:key="user.id"
|
||||
@ -60,13 +60,13 @@ const formData = ref({
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
description: undefined,
|
||||
memberUserIds: undefined,
|
||||
userIds: undefined,
|
||||
status: CommonStatusEnum.ENABLE
|
||||
})
|
||||
const formRules = reactive({
|
||||
name: [{ required: true, message: '组名不能为空', trigger: 'blur' }],
|
||||
description: [{ required: true, message: '描述不能为空', trigger: 'blur' }],
|
||||
memberUserIds: [{ required: true, message: '成员不能为空', trigger: 'blur' }],
|
||||
userIds: [{ required: true, message: '成员不能为空', trigger: 'blur' }],
|
||||
status: [{ required: true, message: '状态不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
@ -124,7 +124,7 @@ const resetForm = () => {
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
description: undefined,
|
||||
memberUserIds: undefined,
|
||||
userIds: undefined,
|
||||
status: CommonStatusEnum.ENABLE
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
|
@ -63,7 +63,7 @@
|
||||
<el-table-column label="描述" align="center" prop="description" />
|
||||
<el-table-column label="成员" align="center">
|
||||
<template #default="scope">
|
||||
<span v-for="userId in scope.row.memberUserIds" :key="userId" class="pr-5px">
|
||||
<span v-for="userId in scope.row.userIds" :key="userId" class="pr-5px">
|
||||
{{ userList.find((user) => user.id === userId)?.nickname }}
|
||||
</span>
|
||||
</template>
|
||||
|
@ -43,13 +43,16 @@
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.BPM_MODEL_CATEGORY)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
v-for="category in categoryList"
|
||||
:key="category.code"
|
||||
:label="category.name"
|
||||
:value="category.code"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="formData.id" label="流程图标" prop="icon">
|
||||
<UploadImg v-model="formData.icon" :limit="1" height="128px" width="128px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="流程描述" prop="description">
|
||||
<el-input v-model="formData.description" clearable type="textarea" />
|
||||
</el-form-item>
|
||||
@ -126,6 +129,7 @@ import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
import * as ModelApi from '@/api/bpm/model'
|
||||
import * as FormApi from '@/api/bpm/form'
|
||||
import { CategoryApi } from '@/api/bpm/category'
|
||||
|
||||
defineOptions({ name: 'ModelForm' })
|
||||
|
||||
@ -140,20 +144,23 @@ const formData = ref({
|
||||
formType: 10,
|
||||
name: '',
|
||||
category: undefined,
|
||||
icon: undefined,
|
||||
description: '',
|
||||
formId: '',
|
||||
formCustomCreatePath: '',
|
||||
formCustomViewPath: ''
|
||||
})
|
||||
const formRules = reactive({
|
||||
category: [{ required: true, message: '参数分类不能为空', trigger: 'blur' }],
|
||||
name: [{ required: true, message: '参数名称不能为空', trigger: 'blur' }],
|
||||
key: [{ required: true, message: '参数键名不能为空', trigger: 'blur' }],
|
||||
category: [{ required: true, message: '参数分类不能为空', trigger: 'blur' }],
|
||||
icon: [{ required: true, message: '参数图标不能为空', trigger: 'blur' }],
|
||||
value: [{ required: true, message: '参数键值不能为空', trigger: 'blur' }],
|
||||
visible: [{ required: true, message: '是否可见不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
const formList = ref([]) // 流程表单的下拉框的数据
|
||||
const categoryList = ref([]) // 流程分类列表
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type: string, id?: number) => {
|
||||
@ -171,7 +178,9 @@ const open = async (type: string, id?: number) => {
|
||||
}
|
||||
}
|
||||
// 获得流程表单的下拉框的数据
|
||||
formList.value = await FormApi.getSimpleFormList()
|
||||
formList.value = await FormApi.getFormSimpleList()
|
||||
// 查询流程分类列表
|
||||
categoryList.value = await CategoryApi.getCategorySimpleList()
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
@ -190,11 +199,10 @@ const submitForm = async () => {
|
||||
await ModelApi.createModel(data)
|
||||
// 提示,引导用户做后续的操作
|
||||
await ElMessageBox.alert(
|
||||
'<strong>新建模型成功!</strong>后续需要执行如下 4 个步骤:' +
|
||||
'<strong>新建模型成功!</strong>后续需要执行如下 3 个步骤:' +
|
||||
'<div>1. 点击【修改流程】按钮,配置流程的分类、表单信息</div>' +
|
||||
'<div>2. 点击【设计流程】按钮,绘制流程图</div>' +
|
||||
'<div>3. 点击【分配规则】按钮,设置每个用户任务的审批人</div>' +
|
||||
'<div>4. 点击【发布流程】按钮,完成流程的最终发布</div>' +
|
||||
'<div>3. 点击【发布流程】按钮,完成流程的最终发布</div>' +
|
||||
'另外,每次流程修改后,都需要点击【发布流程】按钮,才能正式生效!!!',
|
||||
'重要提示',
|
||||
{
|
||||
@ -220,6 +228,7 @@ const resetForm = () => {
|
||||
formType: 10,
|
||||
name: '',
|
||||
category: undefined,
|
||||
icon: '',
|
||||
description: '',
|
||||
formId: '',
|
||||
formCustomCreatePath: '',
|
||||
|
@ -109,6 +109,7 @@ const submitFormSuccess = async (response: any) => {
|
||||
}
|
||||
// 提示成功
|
||||
message.success('导入流程成功!请点击【设计流程】按钮,进行编辑保存后,才可以进行【发布流程】')
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
}
|
||||
|
@ -89,11 +89,11 @@ onMounted(async () => {
|
||||
}
|
||||
// 查询模型
|
||||
const data = await ModelApi.getModel(modelId)
|
||||
xmlString.value = data.bpmnXml
|
||||
model.value = {
|
||||
...data,
|
||||
bpmnXml: undefined // 清空 bpmnXml 属性
|
||||
}
|
||||
xmlString.value = data.bpmnXml
|
||||
})
|
||||
</script>
|
||||
<style lang="scss">
|
||||
|
@ -36,10 +36,10 @@
|
||||
class="!w-240px"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.BPM_MODEL_CATEGORY)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
v-for="category in categoryList"
|
||||
:key="category.code"
|
||||
:label="category.name"
|
||||
:value="category.code"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
@ -72,11 +72,12 @@
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="流程分类" align="center" prop="category" width="100">
|
||||
<el-table-column label="流程图标" align="center" prop="icon" width="100">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.BPM_MODEL_CATEGORY" :value="scope.row.category" />
|
||||
<el-image :src="scope.row.icon" class="w-32px h-32px" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="流程分类" align="center" prop="categoryName" width="100" />
|
||||
<el-table-column label="表单信息" align="center" prop="formType" width="200">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
@ -164,10 +165,10 @@
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="handleAssignRule(scope.row)"
|
||||
v-hasPermi="['bpm:task-assign-rule:query']"
|
||||
@click="handleSimpleDesign(scope.row.id)"
|
||||
v-hasPermi="['bpm:model:update']"
|
||||
>
|
||||
分配规则
|
||||
仿钉钉设计流程
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
@ -229,7 +230,6 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { dateFormatter, formatDate } from '@/utils/formatTime'
|
||||
import { MyProcessViewer } from '@/components/bpmnProcessDesigner/package'
|
||||
import * as ModelApi from '@/api/bpm/model'
|
||||
@ -237,6 +237,7 @@ import * as FormApi from '@/api/bpm/form'
|
||||
import ModelForm from './ModelForm.vue'
|
||||
import ModelImportForm from '@/views/bpm/model/ModelImportForm.vue'
|
||||
import { setConfAndFields2 } from '@/utils/formCreate'
|
||||
import { CategoryApi } from '@/api/bpm/category'
|
||||
|
||||
defineOptions({ name: 'BpmModel' })
|
||||
|
||||
@ -255,6 +256,7 @@ const queryParams = reactive({
|
||||
category: undefined
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const categoryList = ref([]) // 流程分类列表
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
@ -334,6 +336,15 @@ const handleDesign = (row) => {
|
||||
})
|
||||
}
|
||||
|
||||
const handleSimpleDesign = (row) => {
|
||||
push({
|
||||
name: 'SimpleWorkflowDesignEditor',
|
||||
query: {
|
||||
modelId: row.id
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/** 发布流程 */
|
||||
const handleDeploy = async (row) => {
|
||||
try {
|
||||
@ -347,16 +358,6 @@ const handleDeploy = async (row) => {
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 点击任务分配按钮 */
|
||||
const handleAssignRule = (row) => {
|
||||
push({
|
||||
name: 'BpmTaskAssignRuleList',
|
||||
query: {
|
||||
modelId: row.id
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/** 跳转到指定流程定义列表 */
|
||||
const handleDefinitionList = (row) => {
|
||||
push({
|
||||
@ -400,7 +401,9 @@ const handleBpmnDetail = async (row) => {
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
onMounted(async () => {
|
||||
await getList()
|
||||
// 查询流程分类列表
|
||||
categoryList.value = await CategoryApi.getCategorySimpleList()
|
||||
})
|
||||
</script>
|
||||
|
@ -37,6 +37,36 @@
|
||||
<el-form-item label="原因" prop="reason">
|
||||
<el-input v-model="formData.reason" placeholder="请输请假原因" type="textarea" />
|
||||
</el-form-item>
|
||||
<el-col v-if="startUserSelectTasks.length > 0">
|
||||
<el-card class="mb-10px">
|
||||
<template #header>指定审批人</template>
|
||||
<el-form
|
||||
:model="startUserSelectAssignees"
|
||||
:rules="startUserSelectAssigneesFormRules"
|
||||
ref="startUserSelectAssigneesFormRef"
|
||||
>
|
||||
<el-form-item
|
||||
v-for="userTask in startUserSelectTasks"
|
||||
:key="userTask.id"
|
||||
:label="`任务【${userTask.name}】`"
|
||||
:prop="userTask.id"
|
||||
>
|
||||
<el-select
|
||||
v-model="startUserSelectAssignees[userTask.id]"
|
||||
multiple
|
||||
placeholder="请选择审批人"
|
||||
>
|
||||
<el-option
|
||||
v-for="user in userList"
|
||||
:key="user.id"
|
||||
:label="user.nickname"
|
||||
:value="user.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-form-item>
|
||||
<el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
</el-form-item>
|
||||
@ -46,10 +76,15 @@
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import * as LeaveApi from '@/api/bpm/leave'
|
||||
import { useTagsViewStore } from '@/store/modules/tagsView'
|
||||
import * as DefinitionApi from '@/api/bpm/definition'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
|
||||
defineOptions({ name: 'BpmOALeaveCreate' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { delView } = useTagsViewStore() // 视图操作
|
||||
const { push, currentRoute } = useRouter() // 路由
|
||||
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formData = ref({
|
||||
type: undefined,
|
||||
@ -64,18 +99,34 @@ const formRules = reactive({
|
||||
endTime: [{ required: true, message: '请假结束时间不能为空', trigger: 'change' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
const { delView } = useTagsViewStore() // 视图操作
|
||||
const { push, currentRoute } = useRouter() // 路由
|
||||
|
||||
// 指定审批人
|
||||
const processDefineKey = 'oa_leave' // 流程定义 Key
|
||||
const startUserSelectTasks = ref([]) // 发起人需要选择审批人的用户任务列表
|
||||
const startUserSelectAssignees = ref({}) // 发起人选择审批人的数据
|
||||
const startUserSelectAssigneesFormRef = ref() // 发起人选择审批人的表单 Ref
|
||||
const startUserSelectAssigneesFormRules = ref({}) // 发起人选择审批人的表单 Rules
|
||||
const userList = ref<any[]>([]) // 用户列表
|
||||
|
||||
/** 提交表单 */
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
if (!formRef) return
|
||||
const valid = await formRef.value.validate()
|
||||
if (!valid) return
|
||||
// 校验指定审批人
|
||||
if (startUserSelectTasks.value?.length > 0) {
|
||||
await startUserSelectAssigneesFormRef.value.validate()
|
||||
}
|
||||
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value as unknown as LeaveApi.LeaveVO
|
||||
const data = { ...formData.value } as unknown as LeaveApi.LeaveVO
|
||||
// 设置指定审批人
|
||||
if (startUserSelectTasks.value?.length > 0) {
|
||||
data.startUserSelectAssignees = startUserSelectAssignees.value
|
||||
}
|
||||
await LeaveApi.createLeave(data)
|
||||
message.success('发起成功')
|
||||
// 关闭当前 Tab
|
||||
@ -85,4 +136,29 @@ const submitForm = async () => {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(async () => {
|
||||
const processDefinitionDetail = await DefinitionApi.getProcessDefinition(
|
||||
undefined,
|
||||
processDefineKey
|
||||
)
|
||||
if (!processDefinitionDetail) {
|
||||
message.error('OA 请假的流程模型未配置,请检查!')
|
||||
return
|
||||
}
|
||||
startUserSelectTasks.value = processDefinitionDetail.startUserSelectTasks
|
||||
// 设置指定审批人
|
||||
if (startUserSelectTasks.value?.length > 0) {
|
||||
// 设置校验规则
|
||||
for (const userTask of startUserSelectTasks.value) {
|
||||
startUserSelectAssignees.value[userTask.id] = []
|
||||
startUserSelectAssigneesFormRules.value[userTask.id] = [
|
||||
{ required: true, message: '请选择审批人', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
// 加载用户列表
|
||||
userList.value = await UserApi.getSimpleUserList()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
@ -36,10 +36,15 @@
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="结果" prop="result">
|
||||
<el-select v-model="queryParams.result" class="!w-240px" clearable placeholder="请选择结果">
|
||||
<el-form-item label="审批结果" prop="result">
|
||||
<el-select
|
||||
v-model="queryParams.result"
|
||||
class="!w-240px"
|
||||
clearable
|
||||
placeholder="请选择审批结果"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT)"
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
@ -78,7 +83,7 @@
|
||||
<el-table-column align="center" label="申请编号" prop="id" />
|
||||
<el-table-column align="center" label="状态" prop="result">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT" :value="scope.row.result" />
|
||||
<dict-tag :type="DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS" :value="scope.row.result" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
@ -166,7 +171,7 @@ const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
type: undefined,
|
||||
result: undefined,
|
||||
status: undefined,
|
||||
reason: undefined,
|
||||
createTime: []
|
||||
})
|
||||
@ -221,7 +226,7 @@ const cancelLeave = async (row) => {
|
||||
inputErrorMessage: '取消原因不能为空'
|
||||
})
|
||||
// 发起取消
|
||||
await ProcessInstanceApi.cancelProcessInstance(row.id, value)
|
||||
await ProcessInstanceApi.cancelProcessInstanceByStartUser(row.id, value)
|
||||
message.success('取消成功')
|
||||
// 刷新列表
|
||||
await getList()
|
||||
|
114
src/views/bpm/processExpression/ProcessExpressionForm.vue
Normal file
114
src/views/bpm/processExpression/ProcessExpressionForm.vue
Normal file
@ -0,0 +1,114 @@
|
||||
<template>
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="100px"
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<el-form-item label="名字" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入名字" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-radio-group v-model="formData.status">
|
||||
<el-radio
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="表达式" prop="expression">
|
||||
<el-input type="textarea" v-model="formData.expression" placeholder="请输入表达式" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
|
||||
import { ProcessExpressionApi, ProcessExpressionVO } from '@/api/bpm/processExpression'
|
||||
import { CommonStatusEnum } from '@/utils/constants'
|
||||
|
||||
/** BPM 流程 表单 */
|
||||
defineOptions({ name: 'ProcessExpressionForm' })
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
status: undefined,
|
||||
expression: undefined
|
||||
})
|
||||
const formRules = reactive({
|
||||
name: [{ required: true, message: '名字不能为空', trigger: 'blur' }],
|
||||
status: [{ required: true, message: '状态不能为空', trigger: 'blur' }],
|
||||
expression: [{ required: true, message: '表达式不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type: string, id?: number) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = t('action.' + type)
|
||||
formType.value = type
|
||||
resetForm()
|
||||
// 修改时,设置数据
|
||||
if (id) {
|
||||
formLoading.value = true
|
||||
try {
|
||||
formData.value = await ProcessExpressionApi.getProcessExpression(id)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
await formRef.value.validate()
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value as unknown as ProcessExpressionVO
|
||||
if (formType.value === 'create') {
|
||||
await ProcessExpressionApi.createProcessExpression(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await ProcessExpressionApi.updateProcessExpression(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
status: CommonStatusEnum.ENABLE,
|
||||
expression: undefined
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
</script>
|
180
src/views/bpm/processExpression/index.vue
Normal file
180
src/views/bpm/processExpression/index.vue
Normal file
@ -0,0 +1,180 @@
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="名字" prop="name">
|
||||
<el-input
|
||||
v-model="queryParams.name"
|
||||
placeholder="请输入名字"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable class="!w-240px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.createTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openForm('create')"
|
||||
v-hasPermi="['bpm:process-expression:create']"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||
<el-table-column label="编号" align="center" prop="id" />
|
||||
<el-table-column label="名字" align="center" prop="name" />
|
||||
<el-table-column label="状态" align="center" prop="status">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="表达式" align="center" prop="expression" />
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="操作" align="center">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['bpm:process-expression:update']"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['bpm:process-expression:delete']"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<ProcessExpressionForm ref="formRef" @success="getList" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import { ProcessExpressionApi, ProcessExpressionVO } from '@/api/bpm/processExpression'
|
||||
import ProcessExpressionForm from './ProcessExpressionForm.vue'
|
||||
|
||||
/** BPM 流程表达式列表 */
|
||||
defineOptions({ name: 'BpmProcessExpression' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const list = ref<ProcessExpressionVO[]>([]) // 列表的数据
|
||||
const total = ref(0) // 列表的总页数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: undefined,
|
||||
status: undefined,
|
||||
createTime: []
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await ProcessExpressionApi.getProcessExpressionPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
formRef.value.open(type, id)
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await ProcessExpressionApi.deleteProcessExpression(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
@ -1,35 +1,45 @@
|
||||
<template>
|
||||
<!-- 第一步,通过流程定义的列表,选择对应的流程 -->
|
||||
<ContentWrap v-if="!selectProcessInstance">
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="流程名称" align="center" prop="name" />
|
||||
<el-table-column label="流程分类" align="center" prop="category">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.BPM_MODEL_CATEGORY" :value="scope.row.category" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="流程版本" align="center" prop="version">
|
||||
<template #default="scope">
|
||||
<el-tag>v{{ scope.row.version }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="流程描述" align="center" prop="description" />
|
||||
<el-table-column label="操作" align="center">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" @click="handleSelect(scope.row)">
|
||||
<Icon icon="ep:plus" /> 选择
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<ContentWrap v-if="!selectProcessDefinition" v-loading="loading">
|
||||
<el-tabs tab-position="left" v-model="categoryActive">
|
||||
<el-tab-pane
|
||||
:label="category.name"
|
||||
:name="category.code"
|
||||
:key="category.code"
|
||||
v-for="category in categoryList"
|
||||
>
|
||||
<el-row :gutter="20">
|
||||
<el-col
|
||||
:lg="6"
|
||||
:sm="12"
|
||||
:xs="24"
|
||||
v-for="definition in categoryProcessDefinitionList"
|
||||
:key="definition.id"
|
||||
>
|
||||
<el-card
|
||||
shadow="hover"
|
||||
class="mb-20px cursor-pointer"
|
||||
@click="handleSelect(definition)"
|
||||
>
|
||||
<template #default>
|
||||
<div class="flex">
|
||||
<el-image :src="definition.icon" class="w-32px h-32px" />
|
||||
<el-text class="!ml-10px" size="large">{{ definition.name }}</el-text>
|
||||
</div>
|
||||
</template>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 第二步,填写表单,进行流程的提交 -->
|
||||
<ContentWrap v-else>
|
||||
<el-card class="box-card">
|
||||
<div class="clearfix">
|
||||
<span class="el-icon-document">申请信息【{{ selectProcessInstance.name }}】</span>
|
||||
<el-button style="float: right" type="primary" @click="selectProcessInstance = undefined">
|
||||
<span class="el-icon-document">申请信息【{{ selectProcessDefinition.name }}】</span>
|
||||
<el-button style="float: right" type="primary" @click="selectProcessDefinition = undefined">
|
||||
<Icon icon="ep:delete" /> 选择其它流程
|
||||
</el-button>
|
||||
</div>
|
||||
@ -37,9 +47,43 @@
|
||||
<form-create
|
||||
:rule="detailForm.rule"
|
||||
v-model:api="fApi"
|
||||
v-model="detailForm.value"
|
||||
:option="detailForm.option"
|
||||
@submit="submitForm"
|
||||
/>
|
||||
>
|
||||
<template #type-startUserSelect>
|
||||
<el-col :span="24">
|
||||
<el-card class="mb-10px">
|
||||
<template #header>指定审批人</template>
|
||||
<el-form
|
||||
:model="startUserSelectAssignees"
|
||||
:rules="startUserSelectAssigneesFormRules"
|
||||
ref="startUserSelectAssigneesFormRef"
|
||||
>
|
||||
<el-form-item
|
||||
v-for="userTask in startUserSelectTasks"
|
||||
:key="userTask.id"
|
||||
:label="`任务【${userTask.name}】`"
|
||||
:prop="userTask.id"
|
||||
>
|
||||
<el-select
|
||||
v-model="startUserSelectAssignees[userTask.id]"
|
||||
multiple
|
||||
placeholder="请选择审批人"
|
||||
>
|
||||
<el-option
|
||||
v-for="user in userList"
|
||||
:key="user.id"
|
||||
:label="user.nickname"
|
||||
:value="user.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</template>
|
||||
</form-create>
|
||||
</el-col>
|
||||
</el-card>
|
||||
<!-- 流程图预览 -->
|
||||
@ -47,59 +91,127 @@
|
||||
</ContentWrap>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import * as DefinitionApi from '@/api/bpm/definition'
|
||||
import * as ProcessInstanceApi from '@/api/bpm/processInstance'
|
||||
import { setConfAndFields2 } from '@/utils/formCreate'
|
||||
import type { ApiAttrs } from '@form-create/element-ui/types/config'
|
||||
import ProcessInstanceBpmnViewer from '../detail/ProcessInstanceBpmnViewer.vue'
|
||||
import { CategoryApi } from '@/api/bpm/category'
|
||||
import { useTagsViewStore } from '@/store/modules/tagsView'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
|
||||
defineOptions({ name: 'BpmProcessInstanceCreate' })
|
||||
|
||||
const router = useRouter() // 路由
|
||||
const route = useRoute() // 路由
|
||||
const { push, currentRoute } = useRouter() // 路由
|
||||
const message = useMessage() // 消息
|
||||
const { delView } = useTagsViewStore() // 视图操作
|
||||
|
||||
// ========== 列表相关 ==========
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const list = ref([]) // 列表的数据
|
||||
const queryParams = reactive({
|
||||
suspensionState: 1
|
||||
})
|
||||
const processInstanceId = route.query.processInstanceId
|
||||
const loading = ref(true) // 加载中
|
||||
const categoryList = ref([]) // 分类的列表
|
||||
const categoryActive = ref('') // 选中的分类
|
||||
const processDefinitionList = ref([]) // 流程定义的列表
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
list.value = await DefinitionApi.getProcessDefinitionList(queryParams)
|
||||
// 流程分类
|
||||
categoryList.value = await CategoryApi.getCategorySimpleList()
|
||||
if (categoryList.value.length > 0) {
|
||||
categoryActive.value = categoryList.value[0].code
|
||||
}
|
||||
// 流程定义
|
||||
processDefinitionList.value = await DefinitionApi.getProcessDefinitionList({
|
||||
suspensionState: 1
|
||||
})
|
||||
|
||||
// 如果 processInstanceId 非空,说明是重新发起
|
||||
if (processInstanceId?.length > 0) {
|
||||
const processInstance = await ProcessInstanceApi.getProcessInstance(processInstanceId)
|
||||
if (!processInstance) {
|
||||
message.error('重新发起流程失败,原因:流程实例不存在')
|
||||
return
|
||||
}
|
||||
const processDefinition = processDefinitionList.value.find(
|
||||
(item) => item.key == processInstance.processDefinition?.key
|
||||
)
|
||||
if (!processDefinition) {
|
||||
message.error('重新发起流程失败,原因:流程定义不存在')
|
||||
return
|
||||
}
|
||||
await handleSelect(processDefinition, processInstance.formVariables)
|
||||
}
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 选中分类对应的流程定义列表 */
|
||||
const categoryProcessDefinitionList = computed(() => {
|
||||
return processDefinitionList.value.filter((item) => item.category == categoryActive.value)
|
||||
})
|
||||
|
||||
// ========== 表单相关 ==========
|
||||
const bpmnXML = ref(null) // BPMN 数据
|
||||
const fApi = ref<ApiAttrs>()
|
||||
const detailForm = ref({
|
||||
// 流程表单详情
|
||||
rule: [],
|
||||
option: {}
|
||||
})
|
||||
const selectProcessInstance = ref() // 选择的流程实例
|
||||
option: {},
|
||||
value: {}
|
||||
}) // 流程表单详情
|
||||
const selectProcessDefinition = ref() // 选择的流程定义
|
||||
|
||||
// 指定审批人
|
||||
const bpmnXML = ref(null) // BPMN 数据
|
||||
const startUserSelectTasks = ref([]) // 发起人需要选择审批人的用户任务列表
|
||||
const startUserSelectAssignees = ref({}) // 发起人选择审批人的数据
|
||||
const startUserSelectAssigneesFormRef = ref() // 发起人选择审批人的表单 Ref
|
||||
const startUserSelectAssigneesFormRules = ref({}) // 发起人选择审批人的表单 Rules
|
||||
const userList = ref<any[]>([]) // 用户列表
|
||||
|
||||
/** 处理选择流程的按钮操作 **/
|
||||
const handleSelect = async (row) => {
|
||||
const handleSelect = async (row, formVariables) => {
|
||||
// 设置选择的流程
|
||||
selectProcessInstance.value = row
|
||||
selectProcessDefinition.value = row
|
||||
|
||||
// 重置指定审批人
|
||||
startUserSelectTasks.value = []
|
||||
startUserSelectAssignees.value = {}
|
||||
startUserSelectAssigneesFormRules.value = {}
|
||||
|
||||
// 情况一:流程表单
|
||||
if (row.formType == 10) {
|
||||
// 设置表单
|
||||
setConfAndFields2(detailForm, row.formConf, row.formFields)
|
||||
setConfAndFields2(detailForm, row.formConf, row.formFields, formVariables)
|
||||
// 加载流程图
|
||||
bpmnXML.value = await DefinitionApi.getProcessDefinitionBpmnXML(row.id)
|
||||
const processDefinitionDetail = await DefinitionApi.getProcessDefinition(row.id)
|
||||
if (processDefinitionDetail) {
|
||||
bpmnXML.value = processDefinitionDetail.bpmnXml
|
||||
startUserSelectTasks.value = processDefinitionDetail.startUserSelectTasks
|
||||
|
||||
// 设置指定审批人
|
||||
if (startUserSelectTasks.value?.length > 0) {
|
||||
detailForm.value.rule.push({
|
||||
type: 'startUserSelect',
|
||||
props: {
|
||||
title: '指定审批人'
|
||||
}
|
||||
})
|
||||
// 设置校验规则
|
||||
for (const userTask of startUserSelectTasks.value) {
|
||||
startUserSelectAssignees.value[userTask.id] = []
|
||||
startUserSelectAssigneesFormRules.value[userTask.id] = [
|
||||
{ required: true, message: '请选择审批人', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
// 加载用户列表
|
||||
userList.value = await UserApi.getSimpleUserList()
|
||||
}
|
||||
}
|
||||
// 情况二:业务表单
|
||||
} else if (row.formCustomCreatePath) {
|
||||
await router.push({
|
||||
await push({
|
||||
path: row.formCustomCreatePath
|
||||
})
|
||||
// 这里暂时无需加载流程图,因为跳出到另外个 Tab;
|
||||
@ -108,19 +220,29 @@ const handleSelect = async (row) => {
|
||||
|
||||
/** 提交按钮 */
|
||||
const submitForm = async (formData) => {
|
||||
if (!fApi.value || !selectProcessInstance.value) {
|
||||
if (!fApi.value || !selectProcessDefinition.value) {
|
||||
return
|
||||
}
|
||||
// 如果有指定审批人,需要校验
|
||||
if (startUserSelectTasks.value?.length > 0) {
|
||||
await startUserSelectAssigneesFormRef.value.validate()
|
||||
}
|
||||
|
||||
// 提交请求
|
||||
fApi.value.btn.loading(true)
|
||||
try {
|
||||
await ProcessInstanceApi.createProcessInstance({
|
||||
processDefinitionId: selectProcessInstance.value.id,
|
||||
variables: formData
|
||||
processDefinitionId: selectProcessDefinition.value.id,
|
||||
variables: formData,
|
||||
startUserSelectAssignees: startUserSelectAssignees.value
|
||||
})
|
||||
// 提示
|
||||
message.success('发起流程成功')
|
||||
router.go(-1)
|
||||
// 跳转回去
|
||||
delView(unref(currentRoute))
|
||||
await push({
|
||||
name: 'BpmProcessInstanceMy'
|
||||
})
|
||||
} finally {
|
||||
fApi.value.btn.loading(false)
|
||||
}
|
||||
|
@ -33,21 +33,18 @@ const bpmnControlForm = ref({
|
||||
prefix: 'flowable'
|
||||
})
|
||||
const activityList = ref([]) // 任务列表
|
||||
// const bpmnXML = computed(() => { // TODO 芋艿:不晓得为啊哈不能这么搞
|
||||
// if (!props.processInstance || !props.processInstance.processDefinition) {
|
||||
// return
|
||||
// }
|
||||
// return DefinitionApi.getProcessDefinitionBpmnXML(props.processInstance.processDefinition.id)
|
||||
// })
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(async () => {
|
||||
if (props.id) {
|
||||
activityList.value = await ActivityApi.getActivityList({
|
||||
processInstanceId: props.id
|
||||
})
|
||||
/** 只有 loading 完成时,才去加载流程列表 */
|
||||
watch(
|
||||
() => props.loading,
|
||||
async (value) => {
|
||||
if (value && props.id) {
|
||||
activityList.value = await ActivityApi.getActivityList({
|
||||
processInstanceId: props.id
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
</script>
|
||||
<style>
|
||||
.box-card {
|
||||
|
@ -1,96 +0,0 @@
|
||||
<template>
|
||||
<el-drawer v-model="drawerVisible" title="子任务" size="70%">
|
||||
<!-- 当前任务 -->
|
||||
<template #header>
|
||||
<h4>【{{ baseTask.name }} 】审批人:{{ baseTask.assigneeUser?.nickname }}</h4>
|
||||
<el-button
|
||||
style="margin-left: 5px"
|
||||
v-if="isSubSignButtonVisible(baseTask)"
|
||||
type="danger"
|
||||
plain
|
||||
@click="handleSubSign(baseTask)"
|
||||
>
|
||||
<Icon icon="ep:remove" /> 减签
|
||||
</el-button>
|
||||
</template>
|
||||
<!-- 子任务列表 -->
|
||||
<el-table :data="baseTask.children" style="width: 100%" row-key="id" border>
|
||||
<el-table-column prop="assigneeUser.nickname" label="审批人" />
|
||||
<el-table-column prop="assigneeUser.deptName" label="所在部门" />
|
||||
<el-table-column label="审批状态" prop="result">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT" :value="scope.row.result" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="提交时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
width="180"
|
||||
:formatter="dateFormatter"
|
||||
/>
|
||||
<el-table-column
|
||||
label="结束时间"
|
||||
align="center"
|
||||
prop="endTime"
|
||||
width="180"
|
||||
:formatter="dateFormatter"
|
||||
/>
|
||||
<el-table-column label="操作" prop="operation">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
v-if="isSubSignButtonVisible(scope.row)"
|
||||
type="danger"
|
||||
plain
|
||||
@click="handleSubSign(scope.row)"
|
||||
>
|
||||
<Icon icon="ep:remove" /> 减签
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 减签 -->
|
||||
<TaskSubSignDialogForm ref="taskSubSignDialogForm" />
|
||||
</el-drawer>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { isEmpty } from '@/utils/is'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import TaskSubSignDialogForm from './TaskSubSignDialogForm.vue'
|
||||
|
||||
defineOptions({ name: 'ProcessInstanceChildrenTaskList' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const drawerVisible = ref(false) // 抽屉的是否展示
|
||||
|
||||
const baseTask = ref<object>({})
|
||||
/** 打开弹窗 */
|
||||
const open = async (task: any) => {
|
||||
if (isEmpty(task.children)) {
|
||||
message.warning('该任务没有子任务')
|
||||
return
|
||||
}
|
||||
baseTask.value = task
|
||||
// 展开抽屉
|
||||
drawerVisible.value = true
|
||||
}
|
||||
defineExpose({ open }) // 提供 openModal 方法,用于打开弹窗
|
||||
|
||||
/** 发起减签 */
|
||||
const taskSubSignDialogForm = ref()
|
||||
const handleSubSign = (item) => {
|
||||
taskSubSignDialogForm.value.open(item.id)
|
||||
// TODO @海洋:减签后,需要刷新下界面哈
|
||||
}
|
||||
|
||||
/** 是否显示减签按钮 */
|
||||
const isSubSignButtonVisible = (task: any) => {
|
||||
if (task && task.children && !isEmpty(task.children)) {
|
||||
// 有子任务,且子任务有任意一个是 待处理 和 待前置任务完成 则显示减签按钮
|
||||
const subTask = task.children.find((item) => item.result === 1 || item.result === 9)
|
||||
return !isEmpty(subTask)
|
||||
}
|
||||
return false
|
||||
}
|
||||
</script>
|
@ -3,25 +3,44 @@
|
||||
<template #header>
|
||||
<span class="el-icon-picture-outline">审批记录</span>
|
||||
</template>
|
||||
<el-col :offset="4" :span="16">
|
||||
<el-col :offset="3" :span="17">
|
||||
<div class="block">
|
||||
<el-timeline>
|
||||
<el-timeline-item
|
||||
v-for="(item, index) in tasks"
|
||||
:key="index"
|
||||
:icon="getTimelineItemIcon(item)"
|
||||
:type="getTimelineItemType(item)"
|
||||
v-if="processInstance.endTime"
|
||||
:type="getProcessInstanceTimelineItemType(processInstance)"
|
||||
>
|
||||
<p style="font-weight: 700">
|
||||
任务:{{ item.name }}
|
||||
<dict-tag :type="DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT" :value="item.result" />
|
||||
结束流程:在 {{ formatDate(processInstance?.endTime) }} 结束
|
||||
<dict-tag
|
||||
:type="DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS"
|
||||
:value="processInstance.status"
|
||||
/>
|
||||
</p>
|
||||
</el-timeline-item>
|
||||
<el-timeline-item
|
||||
v-for="(item, index) in tasks"
|
||||
:key="index"
|
||||
:type="getTaskTimelineItemType(item)"
|
||||
>
|
||||
<p style="font-weight: 700">
|
||||
审批任务:{{ item.name }}
|
||||
<dict-tag :type="DICT_TYPE.BPM_TASK_STATUS" :value="item.status" />
|
||||
<el-button
|
||||
style="margin-left: 5px"
|
||||
class="ml-10px"
|
||||
v-if="!isEmpty(item.children)"
|
||||
@click="openChildrenTask(item)"
|
||||
size="small"
|
||||
>
|
||||
<Icon icon="ep:memo" />
|
||||
子任务
|
||||
<Icon icon="ep:memo" /> 子任务
|
||||
</el-button>
|
||||
<el-button
|
||||
class="ml-10px"
|
||||
size="small"
|
||||
v-if="item.formId > 0"
|
||||
@click="handleFormDetail(item)"
|
||||
>
|
||||
<Icon icon="ep:document" /> 查看表单
|
||||
</el-button>
|
||||
</p>
|
||||
<el-card :body-style="{ padding: '10px' }">
|
||||
@ -45,84 +64,112 @@
|
||||
<label v-if="item.durationInMillis" style="font-weight: normal; color: #8a909c">
|
||||
{{ formatPast2(item?.durationInMillis) }}
|
||||
</label>
|
||||
<p v-if="item.reason">
|
||||
<el-tag :type="getTimelineItemType(item)">{{ item.reason }}</el-tag>
|
||||
</p>
|
||||
<p v-if="item.reason"> 审批建议:{{ item.reason }} </p>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
<el-timeline-item type="success">
|
||||
<p style="font-weight: 700">
|
||||
发起流程:【{{ processInstance.startUser?.nickname }}】在
|
||||
{{ formatDate(processInstance?.startTime) }} 发起【 {{ processInstance.name }} 】流程
|
||||
</p>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
</div>
|
||||
</el-col>
|
||||
<!-- 子任务 -->
|
||||
<ProcessInstanceChildrenTaskList ref="processInstanceChildrenTaskList" />
|
||||
</el-card>
|
||||
|
||||
<!-- 弹窗:子任务 -->
|
||||
<TaskSignList ref="taskSignListRef" @success="refresh" />
|
||||
<!-- 弹窗:表单 -->
|
||||
<Dialog title="表单详情" v-model="taskFormVisible" width="600">
|
||||
<form-create
|
||||
ref="fApi"
|
||||
v-model="taskForm.value"
|
||||
:option="taskForm.option"
|
||||
:rule="taskForm.rule"
|
||||
/>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { formatDate, formatPast2 } from '@/utils/formatTime'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { isEmpty } from '@/utils/is'
|
||||
import ProcessInstanceChildrenTaskList from './ProcessInstanceChildrenTaskList.vue'
|
||||
import TaskSignList from './dialog/TaskSignList.vue'
|
||||
import type { ApiAttrs } from '@form-create/element-ui/types/config'
|
||||
import { setConfAndFields2 } from '@/utils/formCreate'
|
||||
|
||||
defineOptions({ name: 'BpmProcessInstanceTaskList' })
|
||||
|
||||
defineProps({
|
||||
loading: propTypes.bool, // 是否加载中
|
||||
processInstance: propTypes.object, // 流程实例
|
||||
tasks: propTypes.arrayOf(propTypes.object) // 流程任务的数组
|
||||
})
|
||||
|
||||
/** 获得任务对应的 icon */
|
||||
const getTimelineItemIcon = (item) => {
|
||||
if (item.result === 1) {
|
||||
return 'el-icon-time'
|
||||
/** 获得流程实例对应的颜色 */
|
||||
const getProcessInstanceTimelineItemType = (item: any) => {
|
||||
if (item.status === 2) {
|
||||
return 'success'
|
||||
}
|
||||
if (item.result === 2) {
|
||||
return 'el-icon-check'
|
||||
if (item.status === 3) {
|
||||
return 'danger'
|
||||
}
|
||||
if (item.result === 3) {
|
||||
return 'el-icon-close'
|
||||
}
|
||||
if (item.result === 4) {
|
||||
return 'el-icon-remove-outline'
|
||||
}
|
||||
if (item.result === 5) {
|
||||
return 'el-icon-back'
|
||||
if (item.status === 4) {
|
||||
return 'warning'
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
/** 获得任务对应的颜色 */
|
||||
const getTimelineItemType = (item) => {
|
||||
if (item.result === 1) {
|
||||
const getTaskTimelineItemType = (item: any) => {
|
||||
if ([0, 1, 6, 7].includes(item.status)) {
|
||||
return 'primary'
|
||||
}
|
||||
if (item.result === 2) {
|
||||
if (item.status === 2) {
|
||||
return 'success'
|
||||
}
|
||||
if (item.result === 3) {
|
||||
if (item.status === 3) {
|
||||
return 'danger'
|
||||
}
|
||||
if (item.result === 4) {
|
||||
if (item.status === 4) {
|
||||
return 'info'
|
||||
}
|
||||
if (item.result === 5) {
|
||||
return 'warning'
|
||||
}
|
||||
if (item.result === 6) {
|
||||
return 'default'
|
||||
}
|
||||
if (item.result === 7 || item.result === 8) {
|
||||
if (item.status === 5) {
|
||||
return 'warning'
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
/**
|
||||
* 子任务
|
||||
*/
|
||||
const processInstanceChildrenTaskList = ref()
|
||||
/** 子任务 */
|
||||
const taskSignListRef = ref()
|
||||
const openChildrenTask = (item: any) => {
|
||||
taskSignListRef.value.open(item)
|
||||
}
|
||||
|
||||
const openChildrenTask = (item) => {
|
||||
processInstanceChildrenTaskList.value.open(item)
|
||||
/** 查看表单 */
|
||||
const fApi = ref<ApiAttrs>() // form-create 的 API 操作类
|
||||
const taskForm = ref({
|
||||
rule: [],
|
||||
option: {},
|
||||
value: {}
|
||||
}) // 流程任务的表单详情
|
||||
const taskFormVisible = ref(false)
|
||||
const handleFormDetail = async (row) => {
|
||||
// 设置表单
|
||||
setConfAndFields2(taskForm, row.formConf, row.formFields, row.formVariables)
|
||||
// 弹窗打开
|
||||
taskFormVisible.value = true
|
||||
// 隐藏提交、重置按钮,设置禁用只读
|
||||
await nextTick()
|
||||
fApi.value.fapi.btn.show(false)
|
||||
fApi.value?.fapi?.resetBtn.show(false)
|
||||
fApi.value?.fapi?.disabled(true)
|
||||
}
|
||||
|
||||
/** 刷新数据 */
|
||||
const emit = defineEmits(['refresh']) // 定义 success 事件,用于操作成功后的回调
|
||||
const refresh = () => {
|
||||
emit('refresh')
|
||||
}
|
||||
</script>
|
||||
|
@ -1,242 +0,0 @@
|
||||
<!-- TODO @kyle:需要在讨论下;可能直接选人更合适 -->
|
||||
<template>
|
||||
<Dialog v-model="dialogVisible" title="修改任务规则" width="600">
|
||||
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="80px">
|
||||
<el-form-item label="任务名称" prop="taskName">
|
||||
<el-input v-model="formData.taskName" disabled placeholder="请输入任务名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="任务标识" prop="taskKey">
|
||||
<el-input v-model="formData.taskKey" disabled placeholder="请输入任务标识" />
|
||||
</el-form-item>
|
||||
<el-form-item label="流程名称" prop="processInstanceName">
|
||||
<el-input v-model="formData.processInstanceName" disabled placeholder="请输入流程名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="流程标识" prop="processInstanceKey">
|
||||
<el-input v-model="formData.processInstanceKey" disabled placeholder="请输入流程标识" />
|
||||
</el-form-item>
|
||||
<el-form-item label="规则类型" prop="type">
|
||||
<el-select v-model="formData.type" clearable style="width: 100%">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.BPM_TASK_ASSIGN_RULE_TYPE)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="formData.type === 10" label="指定角色" prop="roleIds">
|
||||
<el-select v-model="formData.roleIds" clearable multiple style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in roleOptions"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="formData.type === 20 || formData.type === 21"
|
||||
label="指定部门"
|
||||
prop="deptIds"
|
||||
span="24"
|
||||
>
|
||||
<el-tree-select
|
||||
ref="treeRef"
|
||||
v-model="formData.deptIds"
|
||||
:data="deptTreeOptions"
|
||||
:props="defaultProps"
|
||||
empty-text="加载中,请稍后"
|
||||
multiple
|
||||
node-key="id"
|
||||
show-checkbox
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="formData.type === 22" label="指定岗位" prop="postIds" span="24">
|
||||
<el-select v-model="formData.postIds" clearable multiple style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in postOptions"
|
||||
:key="parseInt(item.id)"
|
||||
:label="item.name"
|
||||
:value="parseInt(item.id)"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="formData.type === 30 || formData.type === 31 || formData.type === 32"
|
||||
label="指定用户"
|
||||
prop="userIds"
|
||||
span="24"
|
||||
>
|
||||
<el-select v-model="formData.userIds" clearable multiple style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in userOptions"
|
||||
:key="parseInt(item.id)"
|
||||
:label="item.nickname"
|
||||
:value="parseInt(item.id)"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="formData.type === 40" label="指定用户组" prop="userGroupIds">
|
||||
<el-select v-model="formData.userGroupIds" clearable multiple style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in userGroupOptions"
|
||||
:key="parseInt(item.id)"
|
||||
:label="item.name"
|
||||
:value="parseInt(item.id)"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="formData.type === 50" label="指定脚本" prop="scripts">
|
||||
<el-select v-model="formData.scripts" clearable multiple style="width: 100%">
|
||||
<el-option
|
||||
v-for="dict in taskAssignScriptDictDatas"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="抄送原因" prop="reason">
|
||||
<el-input v-model="formData.reason" placeholder="请输入抄送原因" type="textarea" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<!-- 操作按钮 -->
|
||||
<template #footer>
|
||||
<el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { defaultProps, handleTree } from '@/utils/tree'
|
||||
import * as ProcessInstanceApi from '@/api/bpm/processInstance'
|
||||
import * as RoleApi from '@/api/system/role'
|
||||
import * as DeptApi from '@/api/system/dept'
|
||||
import * as PostApi from '@/api/system/post'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
import * as UserGroupApi from '@/api/bpm/userGroup'
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formData = ref({
|
||||
type: Number(undefined),
|
||||
taskName: '',
|
||||
taskKey: '',
|
||||
processInstanceName: '',
|
||||
processInstanceKey: '',
|
||||
startUserId: '',
|
||||
options: [],
|
||||
roleIds: [],
|
||||
deptIds: [],
|
||||
postIds: [],
|
||||
userIds: [],
|
||||
userGroupIds: [],
|
||||
scripts: [],
|
||||
reason: ''
|
||||
})
|
||||
const formRules = reactive({
|
||||
type: [{ required: true, message: '规则类型不能为空', trigger: 'change' }],
|
||||
roleIds: [{ required: true, message: '指定角色不能为空', trigger: 'change' }],
|
||||
deptIds: [{ required: true, message: '指定部门不能为空', trigger: 'change' }],
|
||||
postIds: [{ required: true, message: '指定岗位不能为空', trigger: 'change' }],
|
||||
userIds: [{ required: true, message: '指定用户不能为空', trigger: 'change' }],
|
||||
userGroupIds: [{ required: true, message: '指定用户组不能为空', trigger: 'change' }],
|
||||
scripts: [{ required: true, message: '指定脚本不能为空', trigger: 'change' }],
|
||||
reason: [{ required: true, message: '抄送原因不能为空', trigger: 'change' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
const roleOptions = ref<RoleApi.RoleVO[]>([]) // 角色列表
|
||||
const deptOptions = ref<DeptApi.DeptVO[]>([]) // 部门列表
|
||||
const deptTreeOptions = ref() // 部门树
|
||||
const postOptions = ref<PostApi.PostVO[]>([]) // 岗位列表
|
||||
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
|
||||
const userGroupOptions = ref<UserGroupApi.UserGroupVO[]>([]) // 用户组列表
|
||||
const taskAssignScriptDictDatas = getIntDictOptions(DICT_TYPE.BPM_TASK_ASSIGN_SCRIPT)
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (row) => {
|
||||
// 1. 先重置表单
|
||||
resetForm()
|
||||
// 2. 再设置表单
|
||||
if (row != null) {
|
||||
formData.value.type = undefined as unknown as number
|
||||
formData.value.taskName = row.name
|
||||
formData.value.taskKey = row.id
|
||||
formData.value.processInstanceName = row.processInstance.name
|
||||
formData.value.processInstanceKey = row.processInstance.id
|
||||
formData.value.startUserId = row.processInstance.startUserId
|
||||
}
|
||||
// 打开弹窗
|
||||
dialogVisible.value = true
|
||||
|
||||
// 获得角色列表
|
||||
roleOptions.value = await RoleApi.getSimpleRoleList()
|
||||
// 获得部门列表
|
||||
deptOptions.value = await DeptApi.getSimpleDeptList()
|
||||
deptTreeOptions.value = handleTree(deptOptions.value, 'id')
|
||||
// 获得岗位列表
|
||||
postOptions.value = await PostApi.getSimplePostList()
|
||||
// 获得用户列表
|
||||
userOptions.value = await UserApi.getSimpleUserList()
|
||||
// 获得用户组列表
|
||||
userGroupOptions.value = await UserGroupApi.getSimpleUserGroupList()
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
if (!formRef) return
|
||||
const valid = await formRef.value.validate()
|
||||
if (!valid) return
|
||||
|
||||
// 构建表单
|
||||
const form = {
|
||||
...formData.value
|
||||
}
|
||||
// 将 roleIds 等选项赋值到 options 中
|
||||
if (form.type === 10) {
|
||||
form.options = form.roleIds
|
||||
} else if (form.type === 20 || form.type === 21) {
|
||||
form.options = form.deptIds
|
||||
} else if (form.type === 22) {
|
||||
form.options = form.postIds
|
||||
} else if (form.type === 30 || form.type === 31 || form.type === 32) {
|
||||
form.options = form.userIds
|
||||
} else if (form.type === 40) {
|
||||
form.options = form.userGroupIds
|
||||
} else if (form.type === 50) {
|
||||
form.options = form.scripts
|
||||
}
|
||||
form.roleIds = undefined
|
||||
form.deptIds = undefined
|
||||
form.postIds = undefined
|
||||
form.userIds = undefined
|
||||
form.userGroupIds = undefined
|
||||
form.scripts = undefined
|
||||
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = form as unknown as ProcessInstanceApi.ProcessInstanceCCVO
|
||||
await ProcessInstanceApi.createProcessInstanceCC(data)
|
||||
console.log(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
</script>
|
@ -37,10 +37,12 @@ const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const formLoading = ref(false) // 表单的加载中
|
||||
const formData = ref({
|
||||
id: '',
|
||||
delegateUserId: undefined
|
||||
delegateUserId: undefined,
|
||||
reason: ''
|
||||
})
|
||||
const formRules = ref({
|
||||
delegateUserId: [{ required: true, message: '接收人不能为空', trigger: 'change' }]
|
||||
delegateUserId: [{ required: true, message: '接收人不能为空', trigger: 'change' }],
|
||||
reason: [{ required: true, message: '委派理由不能为空', trigger: 'blur' }]
|
||||
})
|
||||
|
||||
const formRef = ref() // 表单 Ref
|
||||
@ -79,7 +81,8 @@ const submitForm = async () => {
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: '',
|
||||
delegateUserId: undefined
|
||||
delegateUserId: undefined,
|
||||
reason: ''
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<Dialog v-model="dialogVisible" title="回退" width="500">
|
||||
<Dialog v-model="dialogVisible" title="回退任务" width="500">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
v-loading="formLoading"
|
||||
@ -7,13 +7,13 @@
|
||||
:rules="formRules"
|
||||
label-width="110px"
|
||||
>
|
||||
<el-form-item label="退回节点" prop="targetDefinitionKey">
|
||||
<el-select v-model="formData.targetDefinitionKey" clearable style="width: 100%">
|
||||
<el-form-item label="退回节点" prop="targetTaskDefinitionKey">
|
||||
<el-select v-model="formData.targetTaskDefinitionKey" clearable style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in returnList"
|
||||
:key="item.definitionKey"
|
||||
:key="item.taskDefinitionKey"
|
||||
:label="item.name"
|
||||
:value="item.definitionKey"
|
||||
:value="item.taskDefinitionKey"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
@ -35,19 +35,19 @@ const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const formLoading = ref(false) // 表单的加载中
|
||||
const formData = ref({
|
||||
id: '',
|
||||
targetDefinitionKey: undefined,
|
||||
targetTaskDefinitionKey: undefined,
|
||||
reason: ''
|
||||
})
|
||||
const formRules = ref({
|
||||
targetDefinitionKey: [{ required: true, message: '必须选择回退节点', trigger: 'change' }],
|
||||
targetTaskDefinitionKey: [{ required: true, message: '必须选择回退节点', trigger: 'change' }],
|
||||
reason: [{ required: true, message: '回退理由不能为空', trigger: 'blur' }]
|
||||
})
|
||||
|
||||
const formRef = ref() // 表单 Ref
|
||||
const returnList = ref([])
|
||||
const returnList = ref([] as any)
|
||||
/** 打开弹窗 */
|
||||
const open = async (id: string) => {
|
||||
returnList.value = await TaskApi.getReturnList({ taskId: id })
|
||||
returnList.value = await TaskApi.getTaskListByReturn(id)
|
||||
if (returnList.value.length === 0) {
|
||||
message.warning('当前没有可回退的节点')
|
||||
return false
|
||||
@ -82,7 +82,7 @@ const submitForm = async () => {
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: '',
|
||||
targetDefinitionKey: undefined,
|
||||
targetTaskDefinitionKey: undefined,
|
||||
reason: ''
|
||||
}
|
||||
formRef.value?.resetFields()
|
@ -7,8 +7,8 @@
|
||||
:rules="formRules"
|
||||
label-width="110px"
|
||||
>
|
||||
<el-form-item label="加签处理人" prop="userIdList">
|
||||
<el-select v-model="formData.userIdList" multiple clearable style="width: 100%">
|
||||
<el-form-item label="加签处理人" prop="userIds">
|
||||
<el-select v-model="formData.userIds" multiple clearable style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in userList"
|
||||
:key="item.id"
|
||||
@ -36,18 +36,19 @@
|
||||
import * as TaskApi from '@/api/bpm/task'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
defineOptions({ name: 'BpmTaskUpdateAssigneeForm' })
|
||||
defineOptions({ name: 'TaskSignCreateForm' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const formLoading = ref(false) // 表单的加载中
|
||||
const formData = ref({
|
||||
id: '',
|
||||
userIdList: [],
|
||||
type: ''
|
||||
userIds: [],
|
||||
type: '',
|
||||
reason: ''
|
||||
})
|
||||
const formRules = ref({
|
||||
userIdList: [{ required: true, message: '加签处理人不能为空', trigger: 'change' }],
|
||||
userIds: [{ required: true, message: '加签处理人不能为空', trigger: 'change' }],
|
||||
reason: [{ required: true, message: '加签理由不能为空', trigger: 'change' }]
|
||||
})
|
||||
|
||||
@ -75,7 +76,7 @@ const submitForm = async (type: string) => {
|
||||
formLoading.value = true
|
||||
formData.value.type = type
|
||||
try {
|
||||
await TaskApi.taskAddSign(formData.value)
|
||||
await TaskApi.signCreateTask(formData.value)
|
||||
message.success('加签成功')
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
@ -89,8 +90,9 @@ const submitForm = async (type: string) => {
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: '',
|
||||
userIdList: [],
|
||||
type: ''
|
||||
userIds: [],
|
||||
type: '',
|
||||
reason: ''
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
@ -9,8 +9,10 @@
|
||||
>
|
||||
<el-form-item label="减签任务" prop="id">
|
||||
<el-radio-group v-model="formData.id">
|
||||
<el-radio-button v-for="item in subTaskList" :key="item.id" :label="item.id">
|
||||
{{ item.name }}({{ item.assigneeUser.deptName }}{{ item.assigneeUser.nickname }}--审批)
|
||||
<el-radio-button v-for="item in childrenTaskList" :key="item.id" :label="item.id">
|
||||
{{ item.name }}
|
||||
({{ item.assigneeUser?.deptName || item.ownerUser?.deptName }} -
|
||||
{{ item.assigneeUser?.nickname || item.ownerUser?.nickname }})
|
||||
</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
@ -24,10 +26,12 @@
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script lang="ts" name="TaskRollbackDialogForm" setup>
|
||||
<script lang="ts" setup>
|
||||
import * as TaskApi from '@/api/bpm/task'
|
||||
import { isEmpty } from '@/utils/is'
|
||||
|
||||
defineOptions({ name: 'TaskSignDeleteForm' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const formLoading = ref(false) // 表单的加载中
|
||||
@ -41,11 +45,11 @@ const formRules = ref({
|
||||
})
|
||||
|
||||
const formRef = ref() // 表单 Ref
|
||||
const subTaskList = ref([])
|
||||
const childrenTaskList = ref([])
|
||||
/** 打开弹窗 */
|
||||
const open = async (id: string) => {
|
||||
subTaskList.value = await TaskApi.getChildrenTaskList(id)
|
||||
if (isEmpty(subTaskList.value)) {
|
||||
childrenTaskList.value = await TaskApi.getChildrenTaskList(id)
|
||||
if (isEmpty(childrenTaskList.value)) {
|
||||
message.warning('当前没有可减签的任务')
|
||||
return false
|
||||
}
|
||||
@ -64,7 +68,7 @@ const submitForm = async () => {
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
await TaskApi.taskSubSign(formData.value)
|
||||
await TaskApi.signDeleteTask(formData.value)
|
||||
message.success('减签成功')
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
106
src/views/bpm/processInstance/detail/dialog/TaskSignList.vue
Normal file
106
src/views/bpm/processInstance/detail/dialog/TaskSignList.vue
Normal file
@ -0,0 +1,106 @@
|
||||
<template>
|
||||
<el-drawer v-model="drawerVisible" title="子任务" size="880px">
|
||||
<!-- 当前任务 -->
|
||||
<template #header>
|
||||
<h4>【{{ parentTask.name }} 】审批人:{{ parentTask?.assigneeUser?.nickname }}</h4>
|
||||
<el-button
|
||||
style="margin-left: 5px"
|
||||
v-if="isSignDeleteButtonVisible(parentTask)"
|
||||
type="danger"
|
||||
plain
|
||||
@click="handleSignDelete(parentTask)"
|
||||
>
|
||||
<Icon icon="ep:remove" /> 减签
|
||||
</el-button>
|
||||
</template>
|
||||
<!-- 子任务列表 -->
|
||||
<el-table :data="parentTask.children" style="width: 100%" row-key="id" border>
|
||||
<el-table-column prop="assigneeUser.nickname" label="审批人" min-width="100">
|
||||
<template #default="scope">
|
||||
{{ scope.row.assigneeUser?.nickname || scope.row.ownerUser?.nickname }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="assigneeUser.deptName" label="所在部门" min-width="100">
|
||||
<template #default="scope">
|
||||
{{ scope.row.assigneeUser?.deptName || scope.row.ownerUser?.deptName }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审批状态" prop="status" width="120">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.BPM_TASK_STATUS" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="提交时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
width="180"
|
||||
:formatter="dateFormatter"
|
||||
/>
|
||||
<el-table-column
|
||||
label="结束时间"
|
||||
align="center"
|
||||
prop="endTime"
|
||||
width="180"
|
||||
:formatter="dateFormatter"
|
||||
/>
|
||||
<el-table-column label="操作" prop="operation" width="90">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
v-if="isSignDeleteButtonVisible(scope.row)"
|
||||
type="danger"
|
||||
plain
|
||||
size="small"
|
||||
@click="handleSignDelete(scope.row)"
|
||||
>
|
||||
<Icon icon="ep:remove" /> 减签
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 减签 -->
|
||||
<TaskSignDeleteForm ref="taskSignDeleteFormRef" @success="handleSignDeleteSuccess" />
|
||||
</el-drawer>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { isEmpty } from '@/utils/is'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import TaskSignDeleteForm from './TaskSignDeleteForm.vue'
|
||||
|
||||
defineOptions({ name: 'TaskSignList' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const drawerVisible = ref(false) // 抽屉的是否展示
|
||||
const parentTask = ref({} as any)
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (task: any) => {
|
||||
if (isEmpty(task.children)) {
|
||||
message.warning('该任务没有子任务')
|
||||
return
|
||||
}
|
||||
parentTask.value = task
|
||||
// 展开抽屉
|
||||
drawerVisible.value = true
|
||||
}
|
||||
defineExpose({ open }) // 提供 openModal 方法,用于打开弹窗
|
||||
|
||||
/** 发起减签 */
|
||||
const taskSignDeleteFormRef = ref()
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const handleSignDelete = (item: any) => {
|
||||
taskSignDeleteFormRef.value.open(item.id)
|
||||
}
|
||||
const handleSignDeleteSuccess = () => {
|
||||
emit('success')
|
||||
// 关闭抽屉
|
||||
drawerVisible.value = false
|
||||
}
|
||||
|
||||
/** 是否显示减签按钮 */
|
||||
const isSignDeleteButtonVisible = (task: any) => {
|
||||
return task && task.children && !isEmpty(task.children)
|
||||
}
|
||||
</script>
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<Dialog v-model="dialogVisible" title="转派审批人" width="500">
|
||||
<Dialog v-model="dialogVisible" title="转派任务" width="500">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
v-loading="formLoading"
|
||||
@ -17,6 +17,9 @@
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="转派理由" prop="reason">
|
||||
<el-input v-model="formData.reason" clearable placeholder="请输入转派理由" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
@ -28,16 +31,18 @@
|
||||
import * as TaskApi from '@/api/bpm/task'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
|
||||
defineOptions({ name: 'BpmTaskUpdateAssigneeForm' })
|
||||
defineOptions({ name: 'TaskTransferForm' })
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const formLoading = ref(false) // 表单的加载中
|
||||
const formData = ref({
|
||||
id: '',
|
||||
assigneeUserId: undefined
|
||||
assigneeUserId: undefined,
|
||||
reason: ''
|
||||
})
|
||||
const formRules = ref({
|
||||
assigneeUserId: [{ required: true, message: '新审批人不能为空', trigger: 'change' }]
|
||||
assigneeUserId: [{ required: true, message: '新审批人不能为空', trigger: 'change' }],
|
||||
reason: [{ required: true, message: '转派理由不能为空', trigger: 'blur' }]
|
||||
})
|
||||
|
||||
const formRef = ref() // 表单 Ref
|
||||
@ -63,7 +68,7 @@ const submitForm = async () => {
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
await TaskApi.updateTaskAssignee(formData.value)
|
||||
await TaskApi.transferTask(formData.value)
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
@ -76,7 +81,8 @@ const submitForm = async () => {
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: '',
|
||||
assigneeUserId: undefined
|
||||
assigneeUserId: undefined,
|
||||
reason: ''
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
@ -21,9 +21,22 @@
|
||||
{{ processInstance.name }}
|
||||
</el-form-item>
|
||||
<el-form-item v-if="processInstance && processInstance.startUser" label="流程发起人">
|
||||
{{ processInstance.startUser.nickname }}
|
||||
<el-tag size="small" type="info">{{ processInstance.startUser.deptName }}</el-tag>
|
||||
{{ processInstance?.startUser.nickname }}
|
||||
<el-tag size="small" type="info">{{ processInstance?.startUser.deptName }}</el-tag>
|
||||
</el-form-item>
|
||||
<el-card class="mb-15px !-mt-10px" v-if="runningTasks[index].formId > 0">
|
||||
<template #header>
|
||||
<span class="el-icon-picture-outline">
|
||||
填写表单【{{ runningTasks[index]?.formName }}】
|
||||
</span>
|
||||
</template>
|
||||
<form-create
|
||||
v-model:api="approveFormFApis[index]"
|
||||
v-model="approveForms[index].value"
|
||||
:option="approveForms[index].option"
|
||||
:rule="approveForms[index].rule"
|
||||
/>
|
||||
</el-card>
|
||||
<el-form-item label="审批建议" prop="reason">
|
||||
<el-input
|
||||
v-model="auditForms[index].reason"
|
||||
@ -31,6 +44,16 @@
|
||||
type="textarea"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="抄送人" prop="copyUserIds">
|
||||
<el-select v-model="auditForms[index].copyUserIds" multiple placeholder="请选择抄送人">
|
||||
<el-option
|
||||
v-for="item in userOptions"
|
||||
:key="item.id"
|
||||
:label="item.nickname"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div style="margin-bottom: 20px; margin-left: 10%; font-size: 14px">
|
||||
<el-button type="success" @click="handleAudit(item, true)">
|
||||
@ -82,25 +105,30 @@
|
||||
</el-card>
|
||||
|
||||
<!-- 审批记录 -->
|
||||
<ProcessInstanceTaskList :loading="tasksLoad" :tasks="tasks" />
|
||||
<ProcessInstanceTaskList
|
||||
:loading="tasksLoad"
|
||||
:process-instance="processInstance"
|
||||
:tasks="tasks"
|
||||
@refresh="getTaskList"
|
||||
/>
|
||||
|
||||
<!-- 高亮流程图 -->
|
||||
<ProcessInstanceBpmnViewer
|
||||
:id="`${id}`"
|
||||
:bpmn-xml="bpmnXML"
|
||||
:bpmn-xml="bpmnXml"
|
||||
:loading="processInstanceLoading"
|
||||
:process-instance="processInstance"
|
||||
:tasks="tasks"
|
||||
/>
|
||||
|
||||
<!-- 弹窗:转派审批人 -->
|
||||
<TaskUpdateAssigneeForm ref="taskUpdateAssigneeFormRef" @success="getDetail" />
|
||||
<!-- 弹窗,回退节点 -->
|
||||
<TaskReturnDialog ref="taskReturnDialogRef" @success="getDetail" />
|
||||
<!-- 委派,将任务委派给别人处理,处理完成后,会重新回到原审批人手中-->
|
||||
<TaskTransferForm ref="taskTransferFormRef" @success="getDetail" />
|
||||
<!-- 弹窗:回退节点 -->
|
||||
<TaskReturnForm ref="taskReturnFormRef" @success="getDetail" />
|
||||
<!-- 弹窗:委派,将任务委派给别人处理,处理完成后,会重新回到原审批人手中-->
|
||||
<TaskDelegateForm ref="taskDelegateForm" @success="getDetail" />
|
||||
<!-- 加签,当前任务审批人为A,向前加签选了一个C,则需要C先审批,然后再是A审批,向后加签B,A审批完,需要B再审批完,才算完成这个任务节点 -->
|
||||
<TaskAddSignDialogForm ref="taskAddSignDialogForm" @success="getDetail" />
|
||||
<!-- 弹窗:加签,当前任务审批人为A,向前加签选了一个C,则需要C先审批,然后再是A审批,向后加签B,A审批完,需要B再审批完,才算完成这个任务节点 -->
|
||||
<TaskSignCreateForm ref="taskSignCreateFormRef" @success="getDetail" />
|
||||
</ContentWrap>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
@ -110,14 +138,15 @@ import type { ApiAttrs } from '@form-create/element-ui/types/config'
|
||||
import * as DefinitionApi from '@/api/bpm/definition'
|
||||
import * as ProcessInstanceApi from '@/api/bpm/processInstance'
|
||||
import * as TaskApi from '@/api/bpm/task'
|
||||
import TaskUpdateAssigneeForm from './TaskUpdateAssigneeForm.vue'
|
||||
import ProcessInstanceBpmnViewer from './ProcessInstanceBpmnViewer.vue'
|
||||
import ProcessInstanceTaskList from './ProcessInstanceTaskList.vue'
|
||||
import TaskReturnDialog from './TaskReturnDialogForm.vue'
|
||||
import TaskDelegateForm from './TaskDelegateForm.vue'
|
||||
import TaskAddSignDialogForm from './TaskAddSignDialogForm.vue'
|
||||
import TaskReturnForm from './dialog/TaskReturnForm.vue'
|
||||
import TaskDelegateForm from './dialog/TaskDelegateForm.vue'
|
||||
import TaskTransferForm from './dialog/TaskTransferForm.vue'
|
||||
import TaskSignCreateForm from './dialog/TaskSignCreateForm.vue'
|
||||
import { registerComponent } from '@/utils/routerHelper'
|
||||
import { isEmpty } from '@/utils/is'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
|
||||
defineOptions({ name: 'BpmProcessInstanceDetail' })
|
||||
|
||||
@ -126,10 +155,10 @@ const message = useMessage() // 消息弹窗
|
||||
const { proxy } = getCurrentInstance() as any
|
||||
|
||||
const userId = useUserStore().getUser.id // 当前登录的编号
|
||||
const id = query.id as unknown as number // 流程实例的编号
|
||||
const id = query.id as unknown as string // 流程实例的编号
|
||||
const processInstanceLoading = ref(false) // 流程实例的加载中
|
||||
const processInstance = ref<any>({}) // 流程实例
|
||||
const bpmnXML = ref('') // BPMN XML
|
||||
const bpmnXml = ref('') // BPMN XML
|
||||
const tasksLoad = ref(true) // 任务的加载中
|
||||
const tasks = ref<any[]>([]) // 任务列表
|
||||
// ========== 审批信息 ==========
|
||||
@ -138,14 +167,30 @@ const auditForms = ref<any[]>([]) // 审批任务的表单
|
||||
const auditRule = reactive({
|
||||
reason: [{ required: true, message: '审批建议不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const approveForms = ref<any[]>([]) // 审批通过时,额外的补充信息
|
||||
const approveFormFApis = ref<ApiAttrs[]>([]) // approveForms 的 fAPi
|
||||
|
||||
// ========== 申请信息 ==========
|
||||
const fApi = ref<ApiAttrs>() //
|
||||
const detailForm = ref({
|
||||
// 流程表单详情
|
||||
rule: [],
|
||||
option: {},
|
||||
value: {}
|
||||
})
|
||||
}) // 流程实例的表单详情
|
||||
|
||||
/** 监听 approveFormFApis,实现它对应的 form-create 初始化后,隐藏掉对应的表单提交按钮 */
|
||||
watch(
|
||||
() => approveFormFApis.value,
|
||||
(value) => {
|
||||
value?.forEach((api) => {
|
||||
api.btn.show(false)
|
||||
api.resetBtn.show(false)
|
||||
})
|
||||
},
|
||||
{
|
||||
deep: true
|
||||
}
|
||||
)
|
||||
|
||||
/** 处理审批通过和不通过的操作 */
|
||||
const handleAudit = async (task, pass) => {
|
||||
@ -161,9 +206,16 @@ const handleAudit = async (task, pass) => {
|
||||
// 2.1 提交审批
|
||||
const data = {
|
||||
id: task.id,
|
||||
reason: auditForms.value[index].reason
|
||||
reason: auditForms.value[index].reason,
|
||||
copyUserIds: auditForms.value[index].copyUserIds
|
||||
}
|
||||
if (pass) {
|
||||
// 审批通过,并且有额外的 approveForm 表单,需要校验 + 拼接到 data 表单里提交
|
||||
const formCreateApi = approveFormFApis.value[index]
|
||||
if (formCreateApi) {
|
||||
await formCreateApi.validate()
|
||||
data.variables = approveForms.value[index].value
|
||||
}
|
||||
await TaskApi.approveTask(data)
|
||||
message.success('审批通过成功')
|
||||
} else {
|
||||
@ -175,28 +227,27 @@ const handleAudit = async (task, pass) => {
|
||||
}
|
||||
|
||||
/** 转派审批人 */
|
||||
const taskUpdateAssigneeFormRef = ref()
|
||||
const taskTransferFormRef = ref()
|
||||
const openTaskUpdateAssigneeForm = (id: string) => {
|
||||
taskUpdateAssigneeFormRef.value.open(id)
|
||||
taskTransferFormRef.value.open(id)
|
||||
}
|
||||
|
||||
const taskDelegateForm = ref()
|
||||
/** 处理审批退回的操作 */
|
||||
const taskDelegateForm = ref()
|
||||
const handleDelegate = async (task) => {
|
||||
taskDelegateForm.value.open(task.id)
|
||||
}
|
||||
|
||||
//回退弹框组件
|
||||
const taskReturnDialogRef = ref()
|
||||
/** 处理审批退回的操作 */
|
||||
const handleBack = async (task) => {
|
||||
taskReturnDialogRef.value.open(task.id)
|
||||
const taskReturnFormRef = ref()
|
||||
const handleBack = async (task: any) => {
|
||||
taskReturnFormRef.value.open(task.id)
|
||||
}
|
||||
|
||||
const taskAddSignDialogForm = ref()
|
||||
/** 处理审批加签的操作 */
|
||||
const handleSign = async (task) => {
|
||||
taskAddSignDialogForm.value.open(task.id)
|
||||
const taskSignCreateFormRef = ref()
|
||||
const handleSign = async (task: any) => {
|
||||
taskSignCreateFormRef.value.open(task.id)
|
||||
}
|
||||
|
||||
/** 获得详情 */
|
||||
@ -239,7 +290,9 @@ const getProcessInstance = async () => {
|
||||
}
|
||||
|
||||
// 加载流程图
|
||||
bpmnXML.value = await DefinitionApi.getProcessDefinitionBpmnXML(processDefinition.id as number)
|
||||
bpmnXml.value = (
|
||||
await DefinitionApi.getProcessDefinition(processDefinition.id as number)
|
||||
)?.bpmnXml
|
||||
} finally {
|
||||
processInstanceLoading.value = false
|
||||
}
|
||||
@ -247,6 +300,10 @@ const getProcessInstance = async () => {
|
||||
|
||||
/** 加载任务列表 */
|
||||
const getTaskList = async () => {
|
||||
runningTasks.value = []
|
||||
auditForms.value = []
|
||||
approveForms.value = []
|
||||
approveFormFApis.value = []
|
||||
try {
|
||||
// 获得未取消的任务
|
||||
tasksLoad.value = true
|
||||
@ -254,7 +311,7 @@ const getTaskList = async () => {
|
||||
tasks.value = []
|
||||
// 1.1 移除已取消的审批
|
||||
data.forEach((task) => {
|
||||
if (task.result !== 4) {
|
||||
if (task.status !== 4) {
|
||||
tasks.value.push(task)
|
||||
}
|
||||
})
|
||||
@ -274,8 +331,6 @@ const getTaskList = async () => {
|
||||
})
|
||||
|
||||
// 获得需要自己审批的任务
|
||||
runningTasks.value = []
|
||||
auditForms.value = []
|
||||
loadRunningTask(tasks.value)
|
||||
} finally {
|
||||
tasksLoad.value = false
|
||||
@ -291,7 +346,7 @@ const loadRunningTask = (tasks) => {
|
||||
loadRunningTask(task.children)
|
||||
}
|
||||
// 2.1 只有待处理才需要
|
||||
if (task.result !== 1 && task.result !== 6) {
|
||||
if (task.status !== 1 && task.status !== 6) {
|
||||
return
|
||||
}
|
||||
// 2.2 自己不是处理人
|
||||
@ -301,13 +356,26 @@ const loadRunningTask = (tasks) => {
|
||||
// 2.3 添加到处理任务
|
||||
runningTasks.value.push({ ...task })
|
||||
auditForms.value.push({
|
||||
reason: ''
|
||||
reason: '',
|
||||
copyUserIds: []
|
||||
})
|
||||
|
||||
// 2.4 处理 approve 表单
|
||||
if (task.formId && task.formConf) {
|
||||
const approveForm = {}
|
||||
setConfAndFields2(approveForm, task.formConf, task.formFields, task.formVariable)
|
||||
approveForms.value.push(approveForm)
|
||||
} else {
|
||||
approveForms.value.push({}) // 占位,避免为空
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(() => {
|
||||
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
|
||||
onMounted(async () => {
|
||||
getDetail()
|
||||
// 获得用户列表
|
||||
userOptions.value = await UserApi.getSimpleUserList()
|
||||
})
|
||||
</script>
|
||||
|
@ -36,15 +36,20 @@
|
||||
class="!w-240px"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.BPM_MODEL_CATEGORY)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
v-for="category in categoryList"
|
||||
:key="category.code"
|
||||
:label="category.name"
|
||||
:value="category.code"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable class="!w-240px">
|
||||
<el-form-item label="流程状态" prop="status">
|
||||
<el-select
|
||||
v-model="queryParams.status"
|
||||
placeholder="请选择流程状态"
|
||||
clearable
|
||||
class="!w-240px"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS)"
|
||||
:key="dict.value"
|
||||
@ -53,17 +58,7 @@
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="结果" prop="result">
|
||||
<el-select v-model="queryParams.result" placeholder="请选择结果" clearable class="!w-240px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="提交时间" prop="createTime">
|
||||
<el-form-item label="发起时间" prop="createTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.createTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
@ -81,7 +76,7 @@
|
||||
type="primary"
|
||||
plain
|
||||
v-hasPermi="['bpm:process-instance:query']"
|
||||
@click="handleCreate"
|
||||
@click="handleCreate()"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 发起流程
|
||||
</el-button>
|
||||
@ -92,34 +87,23 @@
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="流程编号" align="center" prop="id" width="300px" />
|
||||
<el-table-column label="流程名称" align="center" prop="name" />
|
||||
<el-table-column label="流程分类" align="center" prop="category">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.BPM_MODEL_CATEGORY" :value="scope.row.category" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="当前审批任务" align="center" prop="tasks">
|
||||
<template #default="scope">
|
||||
<el-button type="primary" v-for="task in scope.row.tasks" :key="task.id" link>
|
||||
<span>{{ task.name }}</span>
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" prop="status">
|
||||
<el-table-column label="流程名称" align="center" prop="name" min-width="200px" fixed="left" />
|
||||
<el-table-column
|
||||
label="流程分类"
|
||||
align="center"
|
||||
prop="categoryName"
|
||||
min-width="100"
|
||||
fixed="left"
|
||||
/>
|
||||
<el-table-column label="流程状态" prop="status" width="120">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="结果" prop="result">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT" :value="scope.row.result" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="提交时间"
|
||||
label="发起时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
prop="startTime"
|
||||
width="180"
|
||||
:formatter="dateFormatter"
|
||||
/>
|
||||
@ -130,7 +114,20 @@
|
||||
width="180"
|
||||
:formatter="dateFormatter"
|
||||
/>
|
||||
<el-table-column label="操作" align="center">
|
||||
<el-table-column align="center" label="耗时" prop="durationInMillis" width="160">
|
||||
<template #default="scope">
|
||||
{{ scope.row.durationInMillis > 0 ? formatPast2(scope.row.durationInMillis) : '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="当前审批任务" align="center" prop="tasks" min-width="120px">
|
||||
<template #default="scope">
|
||||
<el-button type="primary" v-for="task in scope.row.tasks" :key="task.id" link>
|
||||
<span>{{ task.name }}</span>
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="流程编号" align="center" prop="id" min-width="320px" />
|
||||
<el-table-column label="操作" align="center" fixed="right" width="180">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
@ -143,12 +140,15 @@
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-if="scope.row.result === 1"
|
||||
v-if="scope.row.status === 1"
|
||||
v-hasPermi="['bpm:process-instance:query']"
|
||||
@click="handleCancel(scope.row)"
|
||||
>
|
||||
取消
|
||||
</el-button>
|
||||
<el-button link type="primary" v-else @click="handleCreate(scope.row.id)">
|
||||
重新发起
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@ -163,11 +163,12 @@
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import { dateFormatter, formatPast2 } from '@/utils/formatTime'
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
import * as ProcessInstanceApi from '@/api/bpm/processInstance'
|
||||
import { CategoryApi } from '@/api/bpm/category'
|
||||
|
||||
defineOptions({ name: 'BpmProcessInstance' })
|
||||
defineOptions({ name: 'BpmProcessInstanceMy' })
|
||||
|
||||
const router = useRouter() // 路由
|
||||
const message = useMessage() // 消息弹窗
|
||||
@ -183,16 +184,16 @@ const queryParams = reactive({
|
||||
processDefinitionId: undefined,
|
||||
category: undefined,
|
||||
status: undefined,
|
||||
result: undefined,
|
||||
createTime: []
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const categoryList = ref([]) // 流程分类列表
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await ProcessInstanceApi.getMyProcessInstancePage(queryParams)
|
||||
const data = await ProcessInstanceApi.getProcessInstanceMyPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
@ -213,9 +214,10 @@ const resetQuery = () => {
|
||||
}
|
||||
|
||||
/** 发起流程操作 **/
|
||||
const handleCreate = () => {
|
||||
const handleCreate = (id) => {
|
||||
router.push({
|
||||
name: 'BpmProcessInstanceCreate'
|
||||
name: 'BpmProcessInstanceCreate',
|
||||
query: { processInstanceId: id }
|
||||
})
|
||||
}
|
||||
|
||||
@ -239,14 +241,20 @@ const handleCancel = async (row) => {
|
||||
inputErrorMessage: '取消原因不能为空'
|
||||
})
|
||||
// 发起取消
|
||||
await ProcessInstanceApi.cancelProcessInstance(row.id, value)
|
||||
await ProcessInstanceApi.cancelProcessInstanceByStartUser(row.id, value)
|
||||
message.success('取消成功')
|
||||
// 刷新列表
|
||||
await getList()
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
/** 激活时 **/
|
||||
onActivated(() => {
|
||||
getList()
|
||||
})
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(async () => {
|
||||
await getList()
|
||||
categoryList.value = await CategoryApi.getCategorySimpleList()
|
||||
})
|
||||
</script>
|
||||
|
255
src/views/bpm/processInstance/manager/index.vue
Normal file
255
src/views/bpm/processInstance/manager/index.vue
Normal file
@ -0,0 +1,255 @@
|
||||
<template>
|
||||
<doc-alert title="工作流手册" url="https://doc.iocoder.cn/bpm/" />
|
||||
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="发起人" prop="startUserId">
|
||||
<el-select v-model="queryParams.startUserId" placeholder="请选择发起人" class="!w-240px">
|
||||
<el-option
|
||||
v-for="user in userList"
|
||||
:key="user.id"
|
||||
:label="user.nickname"
|
||||
:value="user.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="流程名称" prop="name">
|
||||
<el-input
|
||||
v-model="queryParams.name"
|
||||
placeholder="请输入流程名称"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="所属流程" prop="processDefinitionId">
|
||||
<el-input
|
||||
v-model="queryParams.processDefinitionId"
|
||||
placeholder="请输入流程定义的编号"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="流程分类" prop="category">
|
||||
<el-select
|
||||
v-model="queryParams.category"
|
||||
placeholder="请选择流程分类"
|
||||
clearable
|
||||
class="!w-240px"
|
||||
>
|
||||
<el-option
|
||||
v-for="category in categoryList"
|
||||
:key="category.code"
|
||||
:label="category.name"
|
||||
:value="category.code"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="流程状态" prop="status">
|
||||
<el-select
|
||||
v-model="queryParams.status"
|
||||
placeholder="请选择流程状态"
|
||||
clearable
|
||||
class="!w-240px"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="发起时间" prop="createTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.createTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-220px"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="流程名称" align="center" prop="name" min-width="200px" fixed="left" />
|
||||
<el-table-column
|
||||
label="流程分类"
|
||||
align="center"
|
||||
prop="categoryName"
|
||||
min-width="100"
|
||||
fixed="left"
|
||||
/>
|
||||
<el-table-column label="流程发起人" align="center" prop="startUser.nickname" width="120" />
|
||||
<el-table-column label="发起部门" align="center" prop="startUser.deptName" width="120" />
|
||||
<el-table-column label="流程状态" prop="status" width="120">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="发起时间"
|
||||
align="center"
|
||||
prop="startTime"
|
||||
width="180"
|
||||
:formatter="dateFormatter"
|
||||
/>
|
||||
<el-table-column
|
||||
label="结束时间"
|
||||
align="center"
|
||||
prop="endTime"
|
||||
width="180"
|
||||
:formatter="dateFormatter"
|
||||
/>
|
||||
<el-table-column align="center" label="耗时" prop="durationInMillis" width="169">
|
||||
<template #default="scope">
|
||||
{{ scope.row.durationInMillis > 0 ? formatPast2(scope.row.durationInMillis) : '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="当前审批任务" align="center" prop="tasks" min-width="120px">
|
||||
<template #default="scope">
|
||||
<el-button type="primary" v-for="task in scope.row.tasks" :key="task.id" link>
|
||||
<span>{{ task.name }}</span>
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="流程编号" align="center" prop="id" min-width="320px" />
|
||||
<el-table-column label="操作" align="center" fixed="right" width="180">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['bpm:process-instance:cancel']"
|
||||
@click="handleDetail(scope.row)"
|
||||
>
|
||||
详情
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-if="scope.row.status === 1"
|
||||
v-hasPermi="['bpm:process-instance:query']"
|
||||
@click="handleCancel(scope.row)"
|
||||
>
|
||||
取消
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { dateFormatter, formatPast2 } from '@/utils/formatTime'
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
import * as ProcessInstanceApi from '@/api/bpm/processInstance'
|
||||
import { CategoryApi } from '@/api/bpm/category'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
import { cancelProcessInstanceByAdmin } from '@/api/bpm/processInstance'
|
||||
|
||||
// 它和【我的流程】的差异是,该菜单可以看全部的流程实例
|
||||
defineOptions({ name: 'BpmProcessInstanceManager' })
|
||||
|
||||
const router = useRouter() // 路由
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const total = ref(0) // 列表的总页数
|
||||
const list = ref([]) // 列表的数据
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
startUserId: undefined,
|
||||
name: '',
|
||||
processDefinitionId: undefined,
|
||||
category: undefined,
|
||||
status: undefined,
|
||||
createTime: []
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const categoryList = ref([]) // 流程分类列表
|
||||
const userList = ref<any[]>([]) // 用户列表
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await ProcessInstanceApi.getProcessInstanceManagerPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 查看详情 */
|
||||
const handleDetail = (row) => {
|
||||
router.push({
|
||||
name: 'BpmProcessInstanceDetail',
|
||||
query: {
|
||||
id: row.id
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/** 取消按钮操作 */
|
||||
const handleCancel = async (row) => {
|
||||
// 二次确认
|
||||
const { value } = await ElMessageBox.prompt('请输入取消原因', '取消流程', {
|
||||
confirmButtonText: t('common.ok'),
|
||||
cancelButtonText: t('common.cancel'),
|
||||
inputPattern: /^[\s\S]*.*\S[\s\S]*$/, // 判断非空,且非空格
|
||||
inputErrorMessage: '取消原因不能为空'
|
||||
})
|
||||
// 发起取消
|
||||
await ProcessInstanceApi.cancelProcessInstanceByAdmin(row.id, value)
|
||||
message.success('取消成功')
|
||||
// 刷新列表
|
||||
await getList()
|
||||
}
|
||||
|
||||
/** 激活时 **/
|
||||
onActivated(() => {
|
||||
getList()
|
||||
})
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(async () => {
|
||||
await getList()
|
||||
categoryList.value = await CategoryApi.getCategorySimpleList()
|
||||
userList.value = await UserApi.getSimpleUserList()
|
||||
})
|
||||
</script>
|
162
src/views/bpm/processListener/ProcessListenerForm.vue
Normal file
162
src/views/bpm/processListener/ProcessListenerForm.vue
Normal file
@ -0,0 +1,162 @@
|
||||
<template>
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="110px"
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<el-form-item label="名字" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入名字" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-radio-group v-model="formData.status">
|
||||
<el-radio
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="类型" prop="type">
|
||||
<el-select
|
||||
v-model="formData.type"
|
||||
placeholder="请选择类型"
|
||||
@change="formData.event = undefined"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in getStrDictOptions(DICT_TYPE.BPM_PROCESS_LISTENER_TYPE)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="事件" prop="event">
|
||||
<el-select v-model="formData.event" placeholder="请选择事件">
|
||||
<el-option
|
||||
v-for="event in formData.type == 'execution'
|
||||
? ['start', 'end']
|
||||
: ['create', 'assignment', 'complete', 'delete', 'update', 'timeout']"
|
||||
:label="event"
|
||||
:value="event"
|
||||
:key="event"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="值类型" prop="valueType">
|
||||
<el-select v-model="formData.valueType" placeholder="请选择值类型">
|
||||
<el-option
|
||||
v-for="dict in getStrDictOptions(DICT_TYPE.BPM_PROCESS_LISTENER_VALUE_TYPE)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="类路径" prop="value" v-if="formData.type == 'class'">
|
||||
<el-input v-model="formData.value" placeholder="请输入类路径" />
|
||||
</el-form-item>
|
||||
<el-form-item label="表达式" prop="value" v-else>
|
||||
<el-input v-model="formData.value" placeholder="请输入表达式" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { getIntDictOptions, getStrDictOptions, DICT_TYPE } from '@/utils/dict'
|
||||
import { ProcessListenerApi, ProcessListenerVO } from '@/api/bpm/processListener'
|
||||
import { CommonStatusEnum } from '@/utils/constants'
|
||||
|
||||
/** BPM 流程 表单 */
|
||||
defineOptions({ name: 'ProcessListenerForm' })
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
type: undefined,
|
||||
status: undefined,
|
||||
event: undefined,
|
||||
valueType: undefined,
|
||||
value: undefined
|
||||
})
|
||||
const formRules = reactive({
|
||||
name: [{ required: true, message: '名字不能为空', trigger: 'blur' }],
|
||||
type: [{ required: true, message: '类型不能为空', trigger: 'change' }],
|
||||
status: [{ required: true, message: '状态不能为空', trigger: 'blur' }],
|
||||
event: [{ required: true, message: '监听事件不能为空', trigger: 'blur' }],
|
||||
valueType: [{ required: true, message: '值类型不能为空', trigger: 'change' }],
|
||||
value: [{ required: true, message: '值不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type: string, id?: number) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = t('action.' + type)
|
||||
formType.value = type
|
||||
resetForm()
|
||||
// 修改时,设置数据
|
||||
if (id) {
|
||||
formLoading.value = true
|
||||
try {
|
||||
formData.value = await ProcessListenerApi.getProcessListener(id)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
await formRef.value.validate()
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value as unknown as ProcessListenerVO
|
||||
if (formType.value === 'create') {
|
||||
await ProcessListenerApi.createProcessListener(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await ProcessListenerApi.updateProcessListener(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
type: undefined,
|
||||
status: CommonStatusEnum.ENABLE,
|
||||
event: undefined,
|
||||
valueType: undefined,
|
||||
value: undefined
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
</script>
|
183
src/views/bpm/processListener/index.vue
Normal file
183
src/views/bpm/processListener/index.vue
Normal file
@ -0,0 +1,183 @@
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="85px"
|
||||
>
|
||||
<el-form-item label="名字" prop="name">
|
||||
<el-input
|
||||
v-model="queryParams.name"
|
||||
placeholder="请输入名字"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="类型" prop="type">
|
||||
<el-select v-model="queryParams.type" placeholder="请选择类型" clearable class="!w-240px">
|
||||
<el-option
|
||||
v-for="dict in getStrDictOptions(DICT_TYPE.BPM_PROCESS_LISTENER_TYPE)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openForm('create')"
|
||||
v-hasPermi="['bpm:process-listener:create']"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||
<el-table-column label="编号" align="center" prop="id" />
|
||||
<el-table-column label="名字" align="center" prop="name" />
|
||||
<el-table-column label="类型" align="center" prop="type">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.BPM_PROCESS_LISTENER_TYPE" :value="scope.row.type" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" align="center" prop="status">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="事件" align="center" prop="event" />
|
||||
<el-table-column label="值类型" align="center" prop="valueType">
|
||||
<template #default="scope">
|
||||
<dict-tag
|
||||
:type="DICT_TYPE.BPM_PROCESS_LISTENER_VALUE_TYPE"
|
||||
:value="scope.row.valueType"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="值" align="center" prop="value" />
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="操作" align="center">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['bpm:process-listener:update']"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['bpm:process-listener:delete']"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<ProcessListenerForm ref="formRef" @success="getList" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getStrDictOptions, DICT_TYPE } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import { ProcessListenerApi, ProcessListenerVO } from '@/api/bpm/processListener'
|
||||
import ProcessListenerForm from './ProcessListenerForm.vue'
|
||||
|
||||
/** BPM 流程 列表 */
|
||||
defineOptions({ name: 'BpmProcessListener' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const list = ref<ProcessListenerVO[]>([]) // 列表的数据
|
||||
const total = ref(0) // 列表的总页数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: undefined,
|
||||
type: undefined,
|
||||
event: undefined
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await ProcessListenerApi.getProcessListenerPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
formRef.value.open(type, id)
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await ProcessListenerApi.deleteProcessListener(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
28
src/views/bpm/simpleWorkflow/index.vue
Normal file
28
src/views/bpm/simpleWorkflow/index.vue
Normal file
@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<div>
|
||||
<section class="dingflow-design">
|
||||
<div class="box-scale">
|
||||
<nodeWrap v-model:nodeConfig="nodeConfig" />
|
||||
<div class="end-node">
|
||||
<div class="end-node-circle"></div>
|
||||
<div class="end-node-text">流程结束</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import nodeWrap from '@/components/SimpleProcessDesigner/src/nodeWrap.vue'
|
||||
defineOptions({ name: 'SimpleWorkflowDesignEditor' })
|
||||
let nodeConfig = ref({
|
||||
nodeName: '发起人',
|
||||
type: 0,
|
||||
id: 'root',
|
||||
formPerms: {},
|
||||
nodeUserList: [],
|
||||
childNode: {}
|
||||
})
|
||||
</script>
|
||||
<style>
|
||||
@import url('@/components/SimpleProcessDesigner/theme/workflow.css');
|
||||
</style>
|
@ -11,14 +11,6 @@
|
||||
placeholder="请输入流程名称"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="所属流程" prop="processDefinitionId">
|
||||
<el-input
|
||||
v-model="queryParams.processInstanceId"
|
||||
placeholder="请输入流程定义的编号"
|
||||
clearable
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="抄送时间" prop="createTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.createTime"
|
||||
@ -46,12 +38,17 @@
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column align="center" label="所属流程" prop="processInstanceId" width="300px" />
|
||||
<el-table-column align="center" label="流程名称" prop="processInstanceName" />
|
||||
<el-table-column align="center" label="任务名称" prop="taskName" />
|
||||
<el-table-column align="center" label="流程发起人" prop="startUserNickname" />
|
||||
<el-table-column align="center" label="抄送发起人" prop="creatorNickname" />
|
||||
<el-table-column align="center" label="抄送原因" prop="reason" />
|
||||
<el-table-column align="center" label="流程名" prop="processInstanceName" min-width="180" />
|
||||
<el-table-column align="center" label="流程发起人" prop="startUserName" min-width="100" />
|
||||
<el-table-column
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
label="流程发起时间"
|
||||
prop="processInstanceStartTime"
|
||||
width="180"
|
||||
/>
|
||||
<el-table-column align="center" label="抄送任务" prop="taskName" min-width="180" />
|
||||
<el-table-column align="center" label="抄送人" prop="creatorName" min-width="100" />
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="抄送时间"
|
||||
@ -59,9 +56,9 @@
|
||||
width="180"
|
||||
:formatter="dateFormatter"
|
||||
/>
|
||||
<el-table-column align="center" label="操作">
|
||||
<el-table-column align="center" label="操作" fixed="right" width="80">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" @click="handleAudit(scope.row)">跳转待办</el-button>
|
||||
<el-button link type="primary" @click="handleAudit(scope.row)">详情</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@ -78,14 +75,14 @@
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import * as ProcessInstanceApi from '@/api/bpm/processInstance'
|
||||
|
||||
defineOptions({ name: 'BpmCCProcessInstance' })
|
||||
defineOptions({ name: 'BpmProcessInstanceCopy' })
|
||||
|
||||
const { push } = useRouter() // 路由
|
||||
|
||||
const loading = ref(false) // 列表的加载中
|
||||
const total = ref(0) // 列表的总页数
|
||||
const list = ref([]) // 列表的数据
|
||||
const queryParams = ref({
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
processInstanceId: '',
|
||||
@ -98,7 +95,7 @@ const queryFormRef = ref() // 搜索的表单
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await ProcessInstanceApi.getProcessInstanceCCPage(queryParams)
|
||||
const data = await ProcessInstanceApi.getProcessInstanceCopyPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
@ -118,7 +115,7 @@ const handleAudit = (row: any) => {
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.value.pageNo = 1
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
@ -1,51 +0,0 @@
|
||||
<template>
|
||||
<Dialog v-model="dialogVisible" :max-height="500" :scroll="true" title="详情">
|
||||
<el-descriptions :column="1" border>
|
||||
<el-descriptions-item label="任务编号" min-width="120">
|
||||
{{ detailData.id }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="任务名称">
|
||||
{{ detailData.name }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="所属流程">
|
||||
{{ detailData.processInstance.name }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="流程发起人">
|
||||
{{ detailData.processInstance.startUserNickname }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="状态">
|
||||
<dict-tag :type="DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT" :value="detailData.result" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="原因">
|
||||
{{ detailData.reason }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间">
|
||||
{{ formatDate(detailData.createTime) }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
import * as TaskApi from '@/api/bpm/task'
|
||||
|
||||
defineOptions({ name: 'BpmTaskDetail' })
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const detailLoading = ref(false) // 表单的加载中
|
||||
const detailData = ref() // 详情数据
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (data: TaskApi.TaskVO) => {
|
||||
dialogVisible.value = true
|
||||
// 设置数据
|
||||
detailLoading.value = true
|
||||
try {
|
||||
detailData.value = data
|
||||
} finally {
|
||||
detailLoading.value = false
|
||||
}
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
</script>
|
@ -46,27 +46,51 @@
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column align="center" label="任务编号" prop="id" width="300px" />
|
||||
<el-table-column align="center" label="任务名称" prop="name" />
|
||||
<el-table-column align="center" label="所属流程" prop="processInstance.name" />
|
||||
<el-table-column align="center" label="流程发起人" prop="processInstance.startUserNickname" />
|
||||
<el-table-column align="center" label="状态" prop="result">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT" :value="scope.row.result" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="原因" prop="reason" />
|
||||
<el-table-column align="center" label="流程" prop="processInstance.name" width="180" />
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="发起人"
|
||||
prop="processInstance.startUser.nickname"
|
||||
width="100"
|
||||
/>
|
||||
<el-table-column
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
label="创建时间"
|
||||
label="发起时间"
|
||||
prop="createTime"
|
||||
width="180"
|
||||
/>
|
||||
<el-table-column align="center" label="操作">
|
||||
<el-table-column align="center" label="当前任务" prop="name" width="180" />
|
||||
<el-table-column
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
label="任务开始时间"
|
||||
prop="createTime"
|
||||
width="180"
|
||||
/>
|
||||
<el-table-column
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
label="任务结束时间"
|
||||
prop="endTime"
|
||||
width="180"
|
||||
/>
|
||||
<el-table-column align="center" label="审批状态" prop="status" width="120">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" @click="openDetail(scope.row)">详情</el-button>
|
||||
<el-button link type="primary" @click="handleAudit(scope.row)">流程</el-button>
|
||||
<dict-tag :type="DICT_TYPE.BPM_TASK_STATUS" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="审批建议" prop="reason" min-width="180" />
|
||||
<el-table-column align="center" label="耗时" prop="durationInMillis" width="160">
|
||||
<template #default="scope">
|
||||
{{ formatPast2(scope.row.durationInMillis) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="流程编号" prop="id" :show-overflow-tooltip="true" />
|
||||
<el-table-column align="center" label="任务编号" prop="id" :show-overflow-tooltip="true" />
|
||||
<el-table-column align="center" label="操作" fixed="right" width="80">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" @click="handleAudit(scope.row)">历史</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@ -78,15 +102,11 @@
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 表单弹窗:详情 -->
|
||||
<TaskDetail ref="detailRef" @success="getList" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import { dateFormatter, formatPast2 } from '@/utils/formatTime'
|
||||
import * as TaskApi from '@/api/bpm/task'
|
||||
import TaskDetail from './TaskDetail.vue'
|
||||
|
||||
defineOptions({ name: 'BpmTodoTask' })
|
||||
|
||||
@ -107,7 +127,7 @@ const queryFormRef = ref() // 搜索的表单
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await TaskApi.getDoneTaskPage(queryParams)
|
||||
const data = await TaskApi.getTaskDonePage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
@ -127,14 +147,8 @@ const resetQuery = () => {
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 详情操作 */
|
||||
const detailRef = ref()
|
||||
const openDetail = (row: TaskApi.TaskVO) => {
|
||||
detailRef.value.open(row)
|
||||
}
|
||||
|
||||
/** 处理审批按钮 */
|
||||
const handleAudit = (row) => {
|
||||
const handleAudit = (row: any) => {
|
||||
push({
|
||||
name: 'BpmProcessInstanceDetail',
|
||||
query: {
|
||||
|
166
src/views/bpm/task/manager/index.vue
Normal file
166
src/views/bpm/task/manager/index.vue
Normal file
@ -0,0 +1,166 @@
|
||||
<template>
|
||||
<doc-alert title="工作流手册" url="https://doc.iocoder.cn/bpm/" />
|
||||
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
:model="queryParams"
|
||||
class="-mb-15px"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="任务名称" prop="name">
|
||||
<el-input
|
||||
v-model="queryParams.name"
|
||||
class="!w-240px"
|
||||
clearable
|
||||
placeholder="请输入任务名称"
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.createTime"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-240px"
|
||||
end-placeholder="结束日期"
|
||||
start-placeholder="开始日期"
|
||||
type="daterange"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery">
|
||||
<Icon class="mr-5px" icon="ep:search" />
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button @click="resetQuery">
|
||||
<Icon class="mr-5px" icon="ep:refresh" />
|
||||
重置
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column align="center" label="流程" prop="processInstance.name" width="180" />
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="发起人"
|
||||
prop="processInstance.startUser.nickname"
|
||||
width="100"
|
||||
/>
|
||||
<el-table-column
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
label="发起时间"
|
||||
prop="createTime"
|
||||
width="180"
|
||||
/>
|
||||
<el-table-column align="center" label="当前任务" prop="name" width="180" />
|
||||
<el-table-column
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
label="任务开始时间"
|
||||
prop="createTime"
|
||||
width="180"
|
||||
/>
|
||||
<el-table-column
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
label="任务结束时间"
|
||||
prop="endTime"
|
||||
width="180"
|
||||
/>
|
||||
<el-table-column align="center" label="审批人" prop="assigneeUser.nickname" width="100" />
|
||||
<el-table-column align="center" label="审批状态" prop="status" width="120">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.BPM_TASK_STATUS" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="审批建议" prop="reason" min-width="180" />
|
||||
<el-table-column align="center" label="耗时" prop="durationInMillis" width="160">
|
||||
<template #default="scope">
|
||||
{{ formatPast2(scope.row.durationInMillis) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="流程编号" prop="id" :show-overflow-tooltip="true" />
|
||||
<el-table-column align="center" label="任务编号" prop="id" :show-overflow-tooltip="true" />
|
||||
<el-table-column align="center" label="操作" fixed="right" width="80">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" @click="handleAudit(scope.row)">历史</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
v-model:limit="queryParams.pageSize"
|
||||
v-model:page="queryParams.pageNo"
|
||||
:total="total"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { dateFormatter, formatPast2 } from '@/utils/formatTime'
|
||||
import * as TaskApi from '@/api/bpm/task'
|
||||
|
||||
// 它和【待办任务】【已办任务】的差异是,该菜单可以看全部的流程任务
|
||||
defineOptions({ name: 'BpmManagerTask' })
|
||||
|
||||
const { push } = useRouter() // 路由
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const total = ref(0) // 列表的总页数
|
||||
const list = ref([]) // 列表的数据
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: '',
|
||||
createTime: []
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
|
||||
/** 查询任务列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await TaskApi.getTaskManagerPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 处理审批按钮 */
|
||||
const handleAudit = (row: any) => {
|
||||
push({
|
||||
name: 'BpmProcessInstanceDetail',
|
||||
query: {
|
||||
id: row.processInstance.id
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
@ -46,27 +46,33 @@
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column align="center" label="任务编号" prop="id" width="300px" />
|
||||
<el-table-column align="center" label="任务名称" prop="name" />
|
||||
<el-table-column align="center" label="所属流程" prop="processInstance.name" />
|
||||
<el-table-column align="center" label="流程发起人" prop="processInstance.startUserNickname" />
|
||||
<el-table-column align="center" label="流程" prop="processInstance.name" width="180" />
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="发起人"
|
||||
prop="processInstance.startUser.nickname"
|
||||
width="100"
|
||||
/>
|
||||
<el-table-column
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
label="创建时间"
|
||||
label="发起时间"
|
||||
prop="createTime"
|
||||
width="180"
|
||||
/>
|
||||
<el-table-column label="任务状态" prop="suspensionState">
|
||||
<el-table-column align="center" label="当前任务" prop="name" width="180" />
|
||||
<el-table-column
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
label="任务时间"
|
||||
prop="createTime"
|
||||
width="180"
|
||||
/>
|
||||
<el-table-column align="center" label="流程编号" prop="id" :show-overflow-tooltip="true" />
|
||||
<el-table-column align="center" label="任务编号" prop="id" :show-overflow-tooltip="true" />
|
||||
<el-table-column align="center" label="操作" fixed="right" width="80">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.suspensionState === 1" type="success">激活</el-tag>
|
||||
<el-tag v-if="scope.row.suspensionState === 2" type="warning">挂起</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="操作">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" @click="handleAudit(scope.row)">审批进度</el-button>
|
||||
<el-button link type="primary" @click="handleCC(scope.row)">抄送</el-button>
|
||||
<el-button link type="primary" @click="handleAudit(scope.row)">办理</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@ -77,16 +83,14 @@
|
||||
:total="total"
|
||||
@pagination="getList"
|
||||
/>
|
||||
<TaskCCDialogForm ref="taskCCDialogForm" />
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import * as TaskApi from '@/api/bpm/task'
|
||||
import TaskCCDialogForm from '../../processInstance/detail/TaskCCDialogForm.vue'
|
||||
|
||||
defineOptions({ name: 'BpmDoneTask' })
|
||||
defineOptions({ name: 'BpmTodoTask' })
|
||||
|
||||
const { push } = useRouter() // 路由
|
||||
|
||||
@ -105,7 +109,7 @@ const queryFormRef = ref() // 搜索的表单
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await TaskApi.getTodoTaskPage(queryParams)
|
||||
const data = await TaskApi.getTaskTodoPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
@ -126,7 +130,7 @@ const resetQuery = () => {
|
||||
}
|
||||
|
||||
/** 处理审批按钮 */
|
||||
const handleAudit = (row) => {
|
||||
const handleAudit = (row: any) => {
|
||||
push({
|
||||
name: 'BpmProcessInstanceDetail',
|
||||
query: {
|
||||
@ -135,12 +139,6 @@ const handleAudit = (row) => {
|
||||
})
|
||||
}
|
||||
|
||||
const taskCCDialogForm = ref()
|
||||
/** 处理抄送按钮 */
|
||||
const handleCC = (row) => {
|
||||
taskCCDialogForm.value.open(row)
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
|
@ -1,250 +0,0 @@
|
||||
<template>
|
||||
<Dialog v-model="dialogVisible" title="修改任务规则" width="600">
|
||||
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="80px">
|
||||
<el-form-item label="任务名称" prop="taskDefinitionName">
|
||||
<el-input v-model="formData.taskDefinitionName" disabled placeholder="请输入流标标识" />
|
||||
</el-form-item>
|
||||
<el-form-item label="任务标识" prop="taskDefinitionKey">
|
||||
<el-input v-model="formData.taskDefinitionKey" disabled placeholder="请输入任务标识" />
|
||||
</el-form-item>
|
||||
<el-form-item label="规则类型" prop="type">
|
||||
<el-select v-model="formData.type" clearable style="width: 100%">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.BPM_TASK_ASSIGN_RULE_TYPE)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="formData.type === 10" label="指定角色" prop="roleIds">
|
||||
<el-select v-model="formData.roleIds" clearable multiple style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in roleOptions"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="formData.type === 20 || formData.type === 21"
|
||||
label="指定部门"
|
||||
prop="deptIds"
|
||||
span="24"
|
||||
>
|
||||
<el-tree-select
|
||||
ref="treeRef"
|
||||
v-model="formData.deptIds"
|
||||
:data="deptTreeOptions"
|
||||
:props="defaultProps"
|
||||
empty-text="加载中,请稍后"
|
||||
multiple
|
||||
node-key="id"
|
||||
show-checkbox
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="formData.type === 22" label="指定岗位" prop="postIds" span="24">
|
||||
<el-select v-model="formData.postIds" clearable multiple style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in postOptions"
|
||||
:key="parseInt(item.id)"
|
||||
:label="item.name"
|
||||
:value="parseInt(item.id)"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="formData.type === 30 || formData.type === 31 || formData.type === 32"
|
||||
label="指定用户"
|
||||
prop="userIds"
|
||||
span="24"
|
||||
>
|
||||
<el-select v-model="formData.userIds" clearable multiple style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in userOptions"
|
||||
:key="parseInt(item.id)"
|
||||
:label="item.nickname"
|
||||
:value="parseInt(item.id)"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="formData.type === 40" label="指定用户组" prop="userGroupIds">
|
||||
<el-select v-model="formData.userGroupIds" clearable multiple style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in userGroupOptions"
|
||||
:key="parseInt(item.id)"
|
||||
:label="item.name"
|
||||
:value="parseInt(item.id)"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="formData.type === 50" label="指定脚本" prop="scripts">
|
||||
<el-select v-model="formData.scripts" clearable multiple style="width: 100%">
|
||||
<el-option
|
||||
v-for="dict in taskAssignScriptDictDatas"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<!-- 操作按钮 -->
|
||||
<template #footer>
|
||||
<el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { defaultProps, handleTree } from '@/utils/tree'
|
||||
import * as TaskAssignRuleApi from '@/api/bpm/taskAssignRule'
|
||||
import * as RoleApi from '@/api/system/role'
|
||||
import * as DeptApi from '@/api/system/dept'
|
||||
import * as PostApi from '@/api/system/post'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
import * as UserGroupApi from '@/api/bpm/userGroup'
|
||||
|
||||
defineOptions({ name: 'BpmTaskAssignRuleForm' })
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formData = ref({
|
||||
type: Number(undefined),
|
||||
modelId: '',
|
||||
options: [],
|
||||
roleIds: [],
|
||||
deptIds: [],
|
||||
postIds: [],
|
||||
userIds: [],
|
||||
userGroupIds: [],
|
||||
scripts: []
|
||||
})
|
||||
const formRules = reactive({
|
||||
type: [{ required: true, message: '规则类型不能为空', trigger: 'change' }],
|
||||
roleIds: [{ required: true, message: '指定角色不能为空', trigger: 'change' }],
|
||||
deptIds: [{ required: true, message: '指定部门不能为空', trigger: 'change' }],
|
||||
postIds: [{ required: true, message: '指定岗位不能为空', trigger: 'change' }],
|
||||
userIds: [{ required: true, message: '指定用户不能为空', trigger: 'change' }],
|
||||
userGroupIds: [{ required: true, message: '指定用户组不能为空', trigger: 'change' }],
|
||||
scripts: [{ required: true, message: '指定脚本不能为空', trigger: 'change' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
const roleOptions = ref<RoleApi.RoleVO[]>([]) // 角色列表
|
||||
const deptOptions = ref<DeptApi.DeptVO[]>([]) // 部门列表
|
||||
const deptTreeOptions = ref() // 部门树
|
||||
const postOptions = ref<PostApi.PostVO[]>([]) // 岗位列表
|
||||
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
|
||||
const userGroupOptions = ref<UserGroupApi.UserGroupVO[]>([]) // 用户组列表
|
||||
const taskAssignScriptDictDatas = getIntDictOptions(DICT_TYPE.BPM_TASK_ASSIGN_SCRIPT)
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (modelId: string, row: TaskAssignRuleApi.TaskAssignVO) => {
|
||||
// 1. 先重置表单
|
||||
resetForm()
|
||||
// 2. 再设置表单
|
||||
formData.value = {
|
||||
...row,
|
||||
modelId: modelId,
|
||||
options: [],
|
||||
roleIds: [],
|
||||
deptIds: [],
|
||||
postIds: [],
|
||||
userIds: [],
|
||||
userGroupIds: [],
|
||||
scripts: []
|
||||
}
|
||||
// 将 options 赋值到对应的 roleIds 等选项
|
||||
if (row.type === 10) {
|
||||
formData.value.roleIds.push(...row.options)
|
||||
} else if (row.type === 20 || row.type === 21) {
|
||||
formData.value.deptIds.push(...row.options)
|
||||
} else if (row.type === 22) {
|
||||
formData.value.postIds.push(...row.options)
|
||||
} else if (row.type === 30 || row.type === 31 || row.type === 32) {
|
||||
formData.value.userIds.push(...row.options)
|
||||
} else if (row.type === 40) {
|
||||
formData.value.userGroupIds.push(...row.options)
|
||||
} else if (row.type === 50) {
|
||||
formData.value.scripts.push(...row.options)
|
||||
}
|
||||
// 打开弹窗
|
||||
dialogVisible.value = true
|
||||
|
||||
// 获得角色列表
|
||||
roleOptions.value = await RoleApi.getSimpleRoleList()
|
||||
// 获得部门列表
|
||||
deptOptions.value = await DeptApi.getSimpleDeptList()
|
||||
deptTreeOptions.value = handleTree(deptOptions.value, 'id')
|
||||
// 获得岗位列表
|
||||
postOptions.value = await PostApi.getSimplePostList()
|
||||
// 获得用户列表
|
||||
userOptions.value = await UserApi.getSimpleUserList()
|
||||
// 获得用户组列表
|
||||
userGroupOptions.value = await UserGroupApi.getSimpleUserGroupList()
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
if (!formRef) return
|
||||
const valid = await formRef.value.validate()
|
||||
if (!valid) return
|
||||
|
||||
// 构建表单
|
||||
const form = {
|
||||
...formData.value,
|
||||
taskDefinitionName: undefined
|
||||
}
|
||||
// 将 roleIds 等选项赋值到 options 中
|
||||
if (form.type === 10) {
|
||||
form.options = form.roleIds
|
||||
} else if (form.type === 20 || form.type === 21) {
|
||||
form.options = form.deptIds
|
||||
} else if (form.type === 22) {
|
||||
form.options = form.postIds
|
||||
} else if (form.type === 30 || form.type === 31 || form.type === 32) {
|
||||
form.options = form.userIds
|
||||
} else if (form.type === 40) {
|
||||
form.options = form.userGroupIds
|
||||
} else if (form.type === 50) {
|
||||
form.options = form.scripts
|
||||
}
|
||||
form.roleIds = undefined
|
||||
form.deptIds = undefined
|
||||
form.postIds = undefined
|
||||
form.userIds = undefined
|
||||
form.userGroupIds = undefined
|
||||
form.scripts = undefined
|
||||
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = form as unknown as TaskAssignRuleApi.TaskAssignVO
|
||||
if (!data.id) {
|
||||
await TaskAssignRuleApi.createTaskAssignRule(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await TaskAssignRuleApi.updateTaskAssignRule(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
</script>
|
@ -1,136 +0,0 @@
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="任务名" align="center" prop="taskDefinitionName" />
|
||||
<el-table-column label="任务标识" align="center" prop="taskDefinitionKey" />
|
||||
<el-table-column label="规则类型" align="center" prop="type">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.BPM_TASK_ASSIGN_RULE_TYPE" :value="scope.row.type" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="规则范围" align="center" prop="options">
|
||||
<template #default="scope">
|
||||
<el-tag class="mr-5px" :key="option" v-for="option in scope.row.options">
|
||||
{{ getAssignRuleOptionName(scope.row.type, option) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="queryParams.modelId" label="操作" align="center">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm(scope.row)"
|
||||
v-hasPermi="['bpm:task-assign-rule:update']"
|
||||
>
|
||||
修改
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</ContentWrap>
|
||||
<!-- 添加/修改弹窗 -->
|
||||
<TaskAssignRuleForm ref="formRef" @success="getList" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import * as TaskAssignRuleApi from '@/api/bpm/taskAssignRule'
|
||||
import * as RoleApi from '@/api/system/role'
|
||||
import * as DeptApi from '@/api/system/dept'
|
||||
import * as PostApi from '@/api/system/post'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
import * as UserGroupApi from '@/api/bpm/userGroup'
|
||||
import TaskAssignRuleForm from './TaskAssignRuleForm.vue'
|
||||
|
||||
defineOptions({ name: 'BpmTaskAssignRule' })
|
||||
|
||||
const { query } = useRoute() // 查询参数
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const list = ref([]) // 列表的数据
|
||||
const queryParams = reactive({
|
||||
modelId: query.modelId, // 流程模型的编号。如果 modelId 非空,则用于流程模型的查看与配置
|
||||
processDefinitionId: query.processDefinitionId // 流程定义的编号。如果 processDefinitionId 非空,则用于流程定义的查看,不支持配置
|
||||
})
|
||||
const roleOptions = ref<RoleApi.RoleVO[]>([]) // 角色列表
|
||||
const deptOptions = ref<DeptApi.DeptVO[]>([]) // 部门列表
|
||||
const postOptions = ref<PostApi.PostVO[]>([]) // 岗位列表
|
||||
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
|
||||
const userGroupOptions = ref<UserGroupApi.UserGroupVO[]>([]) // 用户组列表
|
||||
const taskAssignScriptDictDatas = getIntDictOptions(DICT_TYPE.BPM_TASK_ASSIGN_SCRIPT)
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
list.value = await TaskAssignRuleApi.getTaskAssignRuleList(queryParams)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 翻译规则范围 */
|
||||
// TODO 芋艿:各种 ts 报错
|
||||
const getAssignRuleOptionName = (type, option) => {
|
||||
if (type === 10) {
|
||||
for (const roleOption of roleOptions.value) {
|
||||
if (roleOption.id === option) {
|
||||
return roleOption.name
|
||||
}
|
||||
}
|
||||
} else if (type === 20 || type === 21) {
|
||||
for (const deptOption of deptOptions.value) {
|
||||
if (deptOption.id === option) {
|
||||
return deptOption.name
|
||||
}
|
||||
}
|
||||
} else if (type === 22) {
|
||||
for (const postOption of postOptions.value) {
|
||||
if (postOption.id === option) {
|
||||
return postOption.name
|
||||
}
|
||||
}
|
||||
} else if (type === 30 || type === 31 || type === 32) {
|
||||
for (const userOption of userOptions.value) {
|
||||
if (userOption.id === option) {
|
||||
return userOption.nickname
|
||||
}
|
||||
}
|
||||
} else if (type === 40) {
|
||||
for (const userGroupOption of userGroupOptions.value) {
|
||||
if (userGroupOption.id === option) {
|
||||
return userGroupOption.name
|
||||
}
|
||||
}
|
||||
} else if (type === 50) {
|
||||
option = option + '' // 转换成 string
|
||||
for (const dictData of taskAssignScriptDictDatas) {
|
||||
if (dictData.value === option) {
|
||||
return dictData.label
|
||||
}
|
||||
}
|
||||
}
|
||||
return '未知(' + option + ')'
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (row: TaskAssignRuleApi.TaskAssignVO) => {
|
||||
formRef.value.open(queryParams.modelId, row)
|
||||
}
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(async () => {
|
||||
await getList()
|
||||
// 获得角色列表
|
||||
roleOptions.value = await RoleApi.getSimpleRoleList()
|
||||
// 获得部门列表
|
||||
deptOptions.value = await DeptApi.getSimpleDeptList()
|
||||
// 获得岗位列表
|
||||
postOptions.value = await PostApi.getSimplePostList()
|
||||
// 获得用户列表
|
||||
userOptions.value = await UserApi.getSimpleUserList()
|
||||
// 获得用户组列表
|
||||
userGroupOptions.value = await UserGroupApi.getSimpleUserGroupList()
|
||||
})
|
||||
</script>
|
@ -4,8 +4,8 @@
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
:disabled="business.endStatus"
|
||||
v-if="permissionListRef?.validateWrite"
|
||||
:disabled="business.endStatus"
|
||||
type="success"
|
||||
@click="openStatusForm()"
|
||||
>
|
||||
@ -53,13 +53,12 @@
|
||||
</el-col>
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<BusinessForm ref="formRef" @success="getBusiness(business.id)" />
|
||||
<BusinessUpdateStatusForm ref="statusFormRef" @success="getBusiness(business.id)" />
|
||||
<CrmTransferForm ref="transferFormRef" @success="close" />
|
||||
<BusinessForm ref="formRef" @success="getBusiness" />
|
||||
<BusinessUpdateStatusForm ref="statusFormRef" @success="getBusiness" />
|
||||
<CrmTransferForm ref="transferFormRef" :biz-type="BizTypeEnum.CRM_BUSINESS" @success="close" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { useTagsViewStore } from '@/store/modules/tagsView'
|
||||
import * as ContactApi from '@/api/crm/contact'
|
||||
import * as BusinessApi from '@/api/crm/business'
|
||||
import BusinessDetailsHeader from './BusinessDetailsHeader.vue'
|
||||
import BusinessDetailsInfo from './BusinessDetailsInfo.vue'
|
||||
@ -73,6 +72,7 @@ import FollowUpList from '@/views/crm/followup/index.vue'
|
||||
import ContactList from '@/views/crm/contact/components/ContactList.vue'
|
||||
import BusinessUpdateStatusForm from '@/views/crm/business/BusinessUpdateStatusForm.vue'
|
||||
import ContractList from '@/views/crm/contract/components/ContractList.vue'
|
||||
import BusinessProductList from '@/views/crm/business/detail/BusinessProductList.vue'
|
||||
|
||||
defineOptions({ name: 'CrmBusinessDetail' })
|
||||
|
||||
@ -80,15 +80,15 @@ const message = useMessage()
|
||||
|
||||
const businessId = ref(0) // 线索编号
|
||||
const loading = ref(true) // 加载中
|
||||
const business = ref<ContactApi.ContactVO>({} as ContactApi.ContactVO) // 联系人详情
|
||||
const business = ref<BusinessApi.BusinessVO>({} as BusinessApi.BusinessVO) // 商机详情
|
||||
const permissionListRef = ref<InstanceType<typeof PermissionList>>() // 团队成员列表 Ref
|
||||
|
||||
/** 获取详情 */
|
||||
const getBusiness = async (id: number) => {
|
||||
const getBusiness = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
business.value = await BusinessApi.getBusiness(id)
|
||||
await getOperateLog(id)
|
||||
business.value = await BusinessApi.getBusiness(businessId.value)
|
||||
await getOperateLog(businessId.value)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
@ -109,7 +109,7 @@ const openStatusForm = () => {
|
||||
/** 联系人转移 */
|
||||
const transferFormRef = ref<InstanceType<typeof CrmTransferForm>>() // 联系人转移表单 ref
|
||||
const transfer = () => {
|
||||
transferFormRef.value?.open('商机转移', business.value.id, BusinessApi.transferBusiness)
|
||||
transferFormRef.value?.open(business.value.id)
|
||||
}
|
||||
|
||||
/** 获取操作日志 */
|
||||
@ -141,6 +141,6 @@ onMounted(async () => {
|
||||
return
|
||||
}
|
||||
businessId.value = params.id as unknown as number
|
||||
await getBusiness(businessId.value)
|
||||
await getBusiness()
|
||||
})
|
||||
</script>
|
||||
|
@ -18,7 +18,7 @@
|
||||
>
|
||||
转化为客户
|
||||
</el-button>
|
||||
<el-button v-else type="success" disabled>已转化客户</el-button>
|
||||
<el-button v-else disabled type="success">已转化客户</el-button>
|
||||
</ClueDetailsHeader>
|
||||
<el-col>
|
||||
<el-tabs>
|
||||
@ -45,7 +45,7 @@
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<ClueForm ref="formRef" @success="getClue" />
|
||||
<CrmTransferForm ref="transferFormRef" @success="close" />
|
||||
<CrmTransferForm ref="transferFormRef" :biz-type="BizTypeEnum.CRM_CLUE" @success="close" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { useTagsViewStore } from '@/store/modules/tagsView'
|
||||
@ -91,7 +91,7 @@ const openForm = () => {
|
||||
/** 线索转移 */
|
||||
const transferFormRef = ref<InstanceType<typeof CrmTransferForm>>() // 线索转移表单 ref
|
||||
const transfer = () => {
|
||||
transferFormRef.value?.open('线索转移', clueId.value, ClueApi.transferClue)
|
||||
transferFormRef.value?.open(clueId.value)
|
||||
}
|
||||
|
||||
/** 转化为客户 */
|
||||
|
@ -6,18 +6,20 @@
|
||||
创建联系人
|
||||
</el-button>
|
||||
<el-button
|
||||
@click="openBusinessModal"
|
||||
v-hasPermi="['crm:contact:create-business']"
|
||||
v-if="queryParams.businessId"
|
||||
v-hasPermi="['crm:contact:create-business']"
|
||||
@click="openBusinessModal"
|
||||
>
|
||||
<Icon class="mr-5px" icon="ep:circle-plus" />关联
|
||||
<Icon class="mr-5px" icon="ep:circle-plus" />
|
||||
关联
|
||||
</el-button>
|
||||
<el-button
|
||||
@click="deleteContactBusinessList"
|
||||
v-hasPermi="['crm:contact:delete-business']"
|
||||
v-if="queryParams.businessId"
|
||||
v-hasPermi="['crm:contact:delete-business']"
|
||||
@click="deleteContactBusinessList"
|
||||
>
|
||||
<Icon class="mr-5px" icon="ep:remove" />解除关联
|
||||
<Icon class="mr-5px" icon="ep:remove" />
|
||||
解除关联
|
||||
</el-button>
|
||||
</el-row>
|
||||
|
||||
@ -27,21 +29,21 @@
|
||||
ref="contactRef"
|
||||
v-loading="loading"
|
||||
:data="list"
|
||||
:stripe="true"
|
||||
:show-overflow-tooltip="true"
|
||||
:stripe="true"
|
||||
>
|
||||
<el-table-column type="selection" width="55" v-if="queryParams.businessId" />
|
||||
<el-table-column label="姓名" fixed="left" align="center" prop="name">
|
||||
<el-table-column v-if="queryParams.businessId" type="selection" width="55" />
|
||||
<el-table-column align="center" fixed="left" label="姓名" prop="name">
|
||||
<template #default="scope">
|
||||
<el-link type="primary" :underline="false" @click="openDetail(scope.row.id)">
|
||||
<el-link :underline="false" type="primary" @click="openDetail(scope.row.id)">
|
||||
{{ scope.row.name }}
|
||||
</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="手机号" align="center" prop="mobile" />
|
||||
<el-table-column label="职位" align="center" prop="post" />
|
||||
<el-table-column label="直属上级" align="center" prop="parentName" />
|
||||
<el-table-column label="是否关键决策人" align="center" prop="master" min-width="100">
|
||||
<el-table-column align="center" label="手机号" prop="mobile" />
|
||||
<el-table-column align="center" label="职位" prop="post" />
|
||||
<el-table-column align="center" label="直属上级" prop="parentName" />
|
||||
<el-table-column align="center" label="是否关键决策人" min-width="100" prop="master">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.master" />
|
||||
</template>
|
||||
@ -49,9 +51,9 @@
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
v-model:page="queryParams.pageNo"
|
||||
:total="total"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
@ -60,12 +62,13 @@
|
||||
<ContactForm ref="formRef" @success="getList" />
|
||||
<!-- 关联商机选择弹框 -->
|
||||
<ContactListModal
|
||||
v-if="customerId"
|
||||
ref="contactModalRef"
|
||||
:customer-id="props.customerId"
|
||||
:customer-id="customerId"
|
||||
@success="createContactBusinessList"
|
||||
/>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
<script lang="ts" setup>
|
||||
import * as ContactApi from '@/api/crm/contact'
|
||||
import ContactForm from './../ContactForm.vue'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
@ -76,8 +79,8 @@ defineOptions({ name: 'CrmContactList' })
|
||||
const props = defineProps<{
|
||||
bizType: number // 业务类型
|
||||
bizId: number // 业务编号
|
||||
customerId: number // 特殊:客户编号;在【商机】详情中,可以传递客户编号,默认新建的联系人关联到该客户
|
||||
businessId: number // 特殊:商机编号;在【商机】详情中,可以传递商机编号,默认新建的联系人关联到该商机
|
||||
customerId?: number // 特殊:客户编号;在【商机】详情中,可以传递客户编号,默认新建的联系人关联到该客户
|
||||
businessId?: number // 特殊:商机编号;在【商机】详情中,可以传递商机编号,默认新建的联系人关联到该商机
|
||||
}>()
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
@ -147,7 +150,7 @@ const createContactBusinessList = async (contactIds: number[]) => {
|
||||
contactIds: contactIds
|
||||
} as ContactApi.ContactBusiness2ReqVO
|
||||
contactRef.value.getSelectionRows().forEach((row: ContactApi.ContactVO) => {
|
||||
data.businessIds.push(row.id)
|
||||
data.contactIds.push(row.id)
|
||||
})
|
||||
await ContactApi.createContactBusinessList2(data)
|
||||
// 刷新列表
|
||||
|
@ -1,28 +1,35 @@
|
||||
<template>
|
||||
<Dialog title="关联联系人" v-model="dialogVisible">
|
||||
<Dialog v-model="dialogVisible" title="关联联系人">
|
||||
<!-- 搜索工作栏 -->
|
||||
<ContentWrap>
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
:model="queryParams"
|
||||
class="-mb-15px"
|
||||
label-width="90px"
|
||||
>
|
||||
<el-form-item label="联系人名称" prop="name">
|
||||
<el-input
|
||||
v-model="queryParams.name"
|
||||
placeholder="请输入联系人名称"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
clearable
|
||||
placeholder="请输入联系人名称"
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button type="primary" @click="openForm()" v-hasPermi="['crm:business:create']">
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
<el-button @click="handleQuery">
|
||||
<Icon class="mr-5px" icon="ep:search" />
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button @click="resetQuery">
|
||||
<Icon class="mr-5px" icon="ep:refresh" />
|
||||
重置
|
||||
</el-button>
|
||||
<el-button v-hasPermi="['crm:business:create']" type="primary" @click="openForm()">
|
||||
<Icon class="mr-5px" icon="ep:plus" />
|
||||
新增
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
@ -31,24 +38,24 @@
|
||||
<!-- 列表 -->
|
||||
<ContentWrap class="mt-10px">
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
ref="contactRef"
|
||||
v-loading="loading"
|
||||
:data="list"
|
||||
:stripe="true"
|
||||
:show-overflow-tooltip="true"
|
||||
:stripe="true"
|
||||
>
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column label="姓名" fixed="left" align="center" prop="name">
|
||||
<el-table-column align="center" fixed="left" label="姓名" prop="name">
|
||||
<template #default="scope">
|
||||
<el-link type="primary" :underline="false" @click="openDetail(scope.row.id)">
|
||||
<el-link :underline="false" type="primary" @click="openDetail(scope.row.id)">
|
||||
{{ scope.row.name }}
|
||||
</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="手机号" align="center" prop="mobile" />
|
||||
<el-table-column label="职位" align="center" prop="post" />
|
||||
<el-table-column label="直属上级" align="center" prop="parentName" />
|
||||
<el-table-column label="是否关键决策人" align="center" prop="master" min-width="100">
|
||||
<el-table-column align="center" label="手机号" prop="mobile" />
|
||||
<el-table-column align="center" label="职位" prop="post" />
|
||||
<el-table-column align="center" label="直属上级" prop="parentName" />
|
||||
<el-table-column align="center" label="是否关键决策人" min-width="100" prop="master">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.master" />
|
||||
</template>
|
||||
@ -56,14 +63,14 @@
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
v-model:page="queryParams.pageNo"
|
||||
:total="total"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
<template #footer>
|
||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||
<el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
|
||||
@ -71,10 +78,9 @@
|
||||
<ContactForm ref="formRef" @success="getList" />
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
<script lang="ts" setup>
|
||||
import * as ContactApi from '@/api/crm/contact'
|
||||
import ContactForm from '../ContactForm.vue'
|
||||
import { erpPriceTableColumnFormatter } from '@/utils'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
@ -31,15 +31,15 @@
|
||||
<BusinessList
|
||||
:biz-id="contact.id!"
|
||||
:biz-type="BizTypeEnum.CRM_CONTACT"
|
||||
:customer-id="contact.customerId"
|
||||
:contact-id="contact.id"
|
||||
:customer-id="contact.customerId"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-col>
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<ContactForm ref="formRef" @success="getContact(contact.id)" />
|
||||
<CrmTransferForm ref="transferFormRef" @success="close" />
|
||||
<ContactForm ref="formRef" @success="getContact" />
|
||||
<CrmTransferForm ref="transferFormRef" :biz-type="BizTypeEnum.CRM_CONTACT" @success="close" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { useTagsViewStore } from '@/store/modules/tagsView'
|
||||
@ -65,11 +65,11 @@ const contact = ref<ContactApi.ContactVO>({} as ContactApi.ContactVO) // 联系
|
||||
const permissionListRef = ref<InstanceType<typeof PermissionList>>() // 团队成员列表 Ref
|
||||
|
||||
/** 获取详情 */
|
||||
const getContact = async (id: number) => {
|
||||
const getContact = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
contact.value = await ContactApi.getContact(id)
|
||||
await getOperateLog(id)
|
||||
contact.value = await ContactApi.getContact(contactId.value)
|
||||
await getOperateLog(contactId.value)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
@ -84,7 +84,7 @@ const openForm = (type: string, id?: number) => {
|
||||
/** 联系人转移 */
|
||||
const transferFormRef = ref<InstanceType<typeof CrmTransferForm>>() // 联系人转移表单 ref
|
||||
const transfer = () => {
|
||||
transferFormRef.value?.open('联系人转移', contact.value.id, ContactApi.transferContact)
|
||||
transferFormRef.value?.open(contact.value.id)
|
||||
}
|
||||
|
||||
/** 获取操作日志 */
|
||||
@ -116,6 +116,6 @@ onMounted(async () => {
|
||||
return
|
||||
}
|
||||
contactId.value = params.id as unknown as number
|
||||
await getContact(contactId.value)
|
||||
await getContact()
|
||||
})
|
||||
</script>
|
||||
|
@ -48,7 +48,7 @@
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<ContractForm ref="formRef" @success="getContractData" />
|
||||
<CrmTransferForm ref="transferFormRef" @success="close" />
|
||||
<CrmTransferForm ref="transferFormRef" :biz-type="BizTypeEnum.CRM_CONTRACT" @success="close" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { useTagsViewStore } from '@/store/modules/tagsView'
|
||||
@ -113,10 +113,9 @@ const createReceivable = (planData: any) => {
|
||||
}
|
||||
|
||||
/** 转移 */
|
||||
// TODO @puhui999:这个组件,要不传递业务类型,然后组件里判断 title 和 api 能调用哪个;整体治理掉;好呢
|
||||
const transferFormRef = ref<InstanceType<typeof CrmTransferForm>>() // 合同转移表单 ref
|
||||
const transferContract = () => {
|
||||
transferFormRef.value?.open('合同转移', contract.value.id, ContractApi.transferContract)
|
||||
transferFormRef.value?.open(contract.value.id)
|
||||
}
|
||||
|
||||
/** 关闭 */
|
||||
|
@ -76,7 +76,7 @@
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<CustomerForm ref="formRef" @success="getCustomer" />
|
||||
<CustomerDistributeForm ref="distributeForm" @success="getCustomer" />
|
||||
<CrmTransferForm ref="transferFormRef" @success="getCustomer" />
|
||||
<CrmTransferForm ref="transferFormRef" :biz-type="BizTypeEnum.CRM_CUSTOMER" @success="close" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { useTagsViewStore } from '@/store/modules/tagsView'
|
||||
@ -142,7 +142,7 @@ const handleUpdateDealStatus = async () => {
|
||||
/** 客户转移 */
|
||||
const transferFormRef = ref<InstanceType<typeof CrmTransferForm>>() // 客户转移表单 ref
|
||||
const transfer = () => {
|
||||
transferFormRef.value?.open('客户转移', customerId.value, CustomerApi.transferCustomer)
|
||||
transferFormRef.value?.open(customerId.value)
|
||||
}
|
||||
|
||||
/** 锁定客户 */
|
||||
|
@ -29,12 +29,15 @@
|
||||
</template>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<!-- TODO @puhui999:同时添加至,还没想好下次搞 -->
|
||||
<el-form-item v-if="false && formType === 'create'" label="同时添加至" prop="toBizType">
|
||||
<el-select v-model="formData.userId">
|
||||
<el-option :value="1" label="联系人" />
|
||||
<el-option :value="1" label="商机" />
|
||||
</el-select>
|
||||
<el-form-item
|
||||
v-if="formType === 'create' && formData.bizType === BizTypeEnum.CRM_CUSTOMER"
|
||||
label="同时添加至"
|
||||
>
|
||||
<el-checkbox-group v-model="formData.toBizTypes">
|
||||
<el-checkbox :label="BizTypeEnum.CRM_CONTACT">联系人</el-checkbox>
|
||||
<el-checkbox :label="BizTypeEnum.CRM_BUSINESS">商机</el-checkbox>
|
||||
<el-checkbox :label="BizTypeEnum.CRM_CONTRACT">合同</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
@ -46,7 +49,7 @@
|
||||
<script lang="ts" setup>
|
||||
import * as UserApi from '@/api/system/user'
|
||||
import * as PermissionApi from '@/api/crm/permission'
|
||||
import { PermissionLevelEnum } from '@/api/crm/permission'
|
||||
import { BizTypeEnum, PermissionLevelEnum } from '@/api/crm/permission'
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
|
||||
defineOptions({ name: 'CrmPermissionForm' })
|
||||
|
@ -19,10 +19,7 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="老负责人">
|
||||
<el-radio-group
|
||||
v-model="oldOwnerHandler"
|
||||
@change="formData.oldOwnerPermissionLevel = undefined"
|
||||
>
|
||||
<el-radio-group v-model="oldOwnerHandler" @change="formData.oldOwnerPermissionLevel">
|
||||
<el-radio :label="false" size="large">移除</el-radio>
|
||||
<el-radio :label="true" size="large">加入团队</el-radio>
|
||||
</el-radio-group>
|
||||
@ -39,8 +36,14 @@
|
||||
</template>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="bizType === BizTypeEnum.CRM_CUSTOMER" label="同时转移">
|
||||
<el-checkbox-group v-model="formData.toBizTypes">
|
||||
<el-checkbox :label="BizTypeEnum.CRM_CONTACT">联系人</el-checkbox>
|
||||
<el-checkbox :label="BizTypeEnum.CRM_BUSINESS">商机</el-checkbox>
|
||||
<el-checkbox :label="BizTypeEnum.CRM_CONTRACT">合同</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<!-- TODO @puhui999 转移客户时,需要额外有【联系人】【商机】【合同】的 checkbox 选择 -->
|
||||
<template #footer>
|
||||
<el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
@ -49,23 +52,27 @@
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import * as UserApi from '@/api/system/user'
|
||||
import type { TransferReqVO } from '@/api/crm/customer'
|
||||
import * as BusinessApi from '@/api/crm/business'
|
||||
import * as ClueApi from '@/api/crm/clue'
|
||||
import * as ContactApi from '@/api/crm/contact'
|
||||
import * as CustomerApi from '@/api/crm/customer'
|
||||
import * as ContractApi from '@/api/crm/contract'
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { PermissionLevelEnum } from '@/api/crm/permission'
|
||||
import { BizTypeEnum, PermissionLevelEnum, TransferReqVO } from '@/api/crm/permission'
|
||||
|
||||
defineOptions({ name: 'CrmTransferForm' })
|
||||
|
||||
const props = defineProps<{
|
||||
bizType: number
|
||||
}>()
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
|
||||
const oldOwnerHandler = ref(false) // 老负责人的处理方式
|
||||
const formData = ref<TransferReqVO>({
|
||||
id: undefined, // 客户编号
|
||||
newOwnerUserId: undefined, // 新负责人的用户编号
|
||||
oldOwnerPermissionLevel: undefined // 老负责人加入团队后的权限级别
|
||||
})
|
||||
const formData = ref<TransferReqVO>({} as TransferReqVO)
|
||||
const formRules = reactive({
|
||||
newOwnerUserId: [{ required: true, message: '新负责人不能为空', trigger: 'blur' }],
|
||||
oldOwnerPermissionLevel: [
|
||||
@ -73,15 +80,13 @@ const formRules = reactive({
|
||||
]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
const transferFuncRef = ref<Function>(() => {}) // 转移所需回调
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (title: string, bizId: number, transferFunc: Function) => {
|
||||
const open = async (bizId: number) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = title
|
||||
transferFuncRef.value = transferFunc
|
||||
dialogTitle.value = getDialogTitle()
|
||||
resetForm()
|
||||
formData.value.id = bizId
|
||||
formData.value.bizId = bizId
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
@ -96,7 +101,7 @@ const submitForm = async () => {
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value
|
||||
await transferFuncRef.value(unref(data))
|
||||
await transfer(unref(data))
|
||||
message.success(dialogTitle.value + '成功')
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
@ -105,15 +110,44 @@ const submitForm = async () => {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
const transfer = async (data: TransferReqVO) => {
|
||||
switch (props.bizType) {
|
||||
case BizTypeEnum.CRM_CLUE:
|
||||
return await ClueApi.transferClue(data)
|
||||
case BizTypeEnum.CRM_CUSTOMER:
|
||||
return await CustomerApi.transferCustomer(data)
|
||||
case BizTypeEnum.CRM_CONTACT:
|
||||
return await ContactApi.transferContact(data)
|
||||
case BizTypeEnum.CRM_BUSINESS:
|
||||
return await BusinessApi.transferBusiness(data)
|
||||
case BizTypeEnum.CRM_CONTRACT:
|
||||
return await ContractApi.transferContract(data)
|
||||
default:
|
||||
message.error('【转移失败】没有转移接口')
|
||||
throw new Error('【转移失败】没有转移接口')
|
||||
}
|
||||
}
|
||||
const getDialogTitle = () => {
|
||||
switch (props.bizType) {
|
||||
case BizTypeEnum.CRM_CLUE:
|
||||
return '线索转移'
|
||||
case BizTypeEnum.CRM_CUSTOMER:
|
||||
return '客户转移'
|
||||
case BizTypeEnum.CRM_CONTACT:
|
||||
return '联系人转移'
|
||||
case BizTypeEnum.CRM_BUSINESS:
|
||||
return '商机转移'
|
||||
case BizTypeEnum.CRM_CONTRACT:
|
||||
return '合同转移'
|
||||
default:
|
||||
return '转移'
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formRef.value?.resetFields()
|
||||
formData.value = {
|
||||
id: undefined, // 客户编号
|
||||
newOwnerUserId: undefined, // 新负责人的用户编号
|
||||
oldOwnerPermissionLevel: undefined // 老负责人加入团队后的权限级别
|
||||
}
|
||||
formData.value = {} as TransferReqVO
|
||||
}
|
||||
onMounted(async () => {
|
||||
// 获得用户列表
|
||||
|
@ -10,7 +10,7 @@
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="回款编号" prop="no">
|
||||
<el-input disabled v-model="formData.no" placeholder="保存时自动生成" />
|
||||
<el-input v-model="formData.no" disabled placeholder="保存时自动生成" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
@ -38,8 +38,8 @@
|
||||
:disabled="formType !== 'create'"
|
||||
class="w-1/1"
|
||||
filterable
|
||||
@change="handleCustomerChange"
|
||||
placeholder="请选择客户"
|
||||
@change="handleCustomerChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in customerList"
|
||||
@ -57,15 +57,15 @@
|
||||
:disabled="formType !== 'create' || !formData.customerId"
|
||||
class="w-1/1"
|
||||
filterable
|
||||
@change="handleContractChange"
|
||||
placeholder="请选择合同"
|
||||
@change="handleContractChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="data in contractList"
|
||||
:key="data.id"
|
||||
:disabled="data.auditStatus !== 20"
|
||||
:label="data.name"
|
||||
:value="data.id!"
|
||||
:disabled="data.auditStatus !== 20"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
@ -78,15 +78,15 @@
|
||||
v-model="formData.planId"
|
||||
:disabled="formType !== 'create' || !formData.contractId"
|
||||
class="!w-1/1"
|
||||
@change="handleReceivablePlanChange"
|
||||
placeholder="请选择回款期数"
|
||||
@change="handleReceivablePlanChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="data in receivablePlanList"
|
||||
:key="data.id"
|
||||
:disabled="data.receivableId"
|
||||
:label="'第 ' + data.period + ' 期'"
|
||||
:value="data.id!"
|
||||
:disabled="data.receivableId"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
@ -109,11 +109,11 @@
|
||||
<el-form-item label="回款金额" prop="price">
|
||||
<el-input-number
|
||||
v-model="formData.price"
|
||||
:min="0.01"
|
||||
:precision="2"
|
||||
class="!w-100%"
|
||||
controls-position="right"
|
||||
placeholder="请输入回款金额"
|
||||
:min="0.01"
|
||||
:precision="2"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@ -145,12 +145,12 @@
|
||||
<script lang="ts" setup>
|
||||
import * as ReceivablePlanApi from '@/api/crm/receivable/plan'
|
||||
import * as ReceivableApi from '@/api/crm/receivable'
|
||||
import { ReceivableVO } from '@/api/crm/receivable'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
import * as CustomerApi from '@/api/crm/customer'
|
||||
import * as ContractApi from '@/api/crm/contract'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import form from '@/components/Form/src/Form.vue'
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
@ -185,9 +185,10 @@ const open = async (
|
||||
if (id) {
|
||||
formLoading.value = true
|
||||
try {
|
||||
formData.value = await ReceivableApi.getReceivable(id)
|
||||
await handleCustomerChange(formData.value.customerId)
|
||||
formData.value.contractId = formData.value.contract.id
|
||||
const data = (await ReceivableApi.getReceivable(id)) as ReceivableVO
|
||||
formData.value = data
|
||||
await handleCustomerChange(data.customerId!)
|
||||
formData.value.contractId = data?.contract?.id
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
@ -266,7 +267,7 @@ const handleContractChange = async (contractId: number) => {
|
||||
// 获得回款计划列表
|
||||
receivablePlanList.value = []
|
||||
receivablePlanList.value = await ReceivablePlanApi.getReceivablePlanSimpleList(
|
||||
formData.value.customerId,
|
||||
formData.value.customerId!,
|
||||
contractId
|
||||
)
|
||||
// 设置金额
|
||||
|
@ -10,7 +10,7 @@
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="还款期数" prop="period">
|
||||
<el-input disabled v-model="formData.period" placeholder="保存时自动生成" />
|
||||
<el-input v-model="formData.period" disabled placeholder="保存时自动生成" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
@ -38,8 +38,8 @@
|
||||
:disabled="formType !== 'create'"
|
||||
class="w-1/1"
|
||||
filterable
|
||||
@change="handleCustomerChange"
|
||||
placeholder="请选择客户"
|
||||
@change="handleCustomerChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in customerList"
|
||||
@ -74,11 +74,11 @@
|
||||
<el-form-item label="计划回款金额" prop="price">
|
||||
<el-input-number
|
||||
v-model="formData.price"
|
||||
:min="0.01"
|
||||
:precision="2"
|
||||
class="!w-100%"
|
||||
controls-position="right"
|
||||
placeholder="请输入计划回款金额"
|
||||
:min="0.01"
|
||||
:precision="2"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@ -136,7 +136,7 @@ import * as CustomerApi from '@/api/crm/customer'
|
||||
import * as ContractApi from '@/api/crm/contract'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { aw } from '../../../../../dist-prod/assets/index-9eac537b'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
@ -167,7 +167,10 @@ const open = async (type: string, id?: number, customerId?: number, contractId?:
|
||||
if (id) {
|
||||
formLoading.value = true
|
||||
try {
|
||||
formData.value = await ReceivablePlanApi.getReceivablePlan(id)
|
||||
const data = await ReceivablePlanApi.getReceivablePlan(id)
|
||||
formData.value = cloneDeep(data)
|
||||
await handleCustomerChange(data.customerId!)
|
||||
formData.value.contractId = data?.contractId
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
|
@ -0,0 +1,130 @@
|
||||
<!-- 客户转化率分析 -->
|
||||
<template>
|
||||
<!-- Echarts图 -->
|
||||
<el-card shadow="never">
|
||||
<el-skeleton :loading="loading" animated>
|
||||
<Echart :height="500" :options="echartsOption" />
|
||||
</el-skeleton>
|
||||
</el-card>
|
||||
|
||||
<!-- 统计列表 -->
|
||||
<el-card shadow="never" class="mt-16px">
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="序号" align="center" type="index" width="80" />
|
||||
<el-table-column label="客户名称" align="center" prop="customerName" min-width="200" />
|
||||
<el-table-column label="合同名称" align="center" prop="contractName" min-width="200" />
|
||||
<el-table-column label="合同总金额" align="center" prop="totalPrice" min-width="200" />
|
||||
<el-table-column label="回款金额" align="center" prop="receivablePrice" min-width="200" />
|
||||
<el-table-column label="负责人" align="center" prop="ownerUserName" min-width="200" />
|
||||
<el-table-column label="创建人" align="center" prop="creatorUserName" min-width="200" />
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
:formatter="dateFormatter"
|
||||
min-width="200"
|
||||
/>
|
||||
<el-table-column
|
||||
label="下单日期"
|
||||
align="center"
|
||||
prop="orderDate"
|
||||
:formatter="dateFormatter2"
|
||||
min-width="200"
|
||||
/>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
StatisticsCustomerApi,
|
||||
CrmStatisticsCustomerSummaryByDateRespVO
|
||||
} from '@/api/crm/statistics/customer'
|
||||
import { EChartsOption } from 'echarts'
|
||||
import { round } from 'lodash-es'
|
||||
import { dateFormatter, dateFormatter2 } from '@/utils/formatTime'
|
||||
|
||||
defineOptions({ name: 'CustomerConversionStat' })
|
||||
const props = defineProps<{ queryParams: any }>() // 搜索参数
|
||||
|
||||
const loading = ref(false) // 加载中
|
||||
const list = ref<CrmStatisticsCustomerSummaryByDateRespVO[]>([]) // 列表的数据
|
||||
|
||||
/** 柱状图配置:纵向 */
|
||||
const echartsOption = reactive<EChartsOption>({
|
||||
grid: {
|
||||
left: 20,
|
||||
right: 20,
|
||||
bottom: 20,
|
||||
containLabel: true
|
||||
},
|
||||
legend: {},
|
||||
series: [
|
||||
{
|
||||
name: '客户转化率',
|
||||
type: 'line',
|
||||
data: []
|
||||
}
|
||||
],
|
||||
toolbox: {
|
||||
feature: {
|
||||
dataZoom: {
|
||||
xAxisIndex: false // 数据区域缩放:Y 轴不缩放
|
||||
},
|
||||
brush: {
|
||||
type: ['lineX', 'clear'] // 区域缩放按钮、还原按钮
|
||||
},
|
||||
saveAsImage: { show: true, name: '客户转化率分析' } // 保存为图片
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
name: '转化率(%)'
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
name: '日期',
|
||||
data: []
|
||||
}
|
||||
}) as EChartsOption
|
||||
|
||||
/** 获取统计数据 */
|
||||
const loadData = async () => {
|
||||
// 1. 加载统计数据
|
||||
loading.value = true
|
||||
const customerCount = await StatisticsCustomerApi.getCustomerSummaryByDate(props.queryParams)
|
||||
const contractSummary = await StatisticsCustomerApi.getContractSummary(props.queryParams)
|
||||
// 2.1 更新 Echarts 数据
|
||||
if (echartsOption.xAxis && echartsOption.xAxis['data']) {
|
||||
echartsOption.xAxis['data'] = customerCount.map(
|
||||
(s: CrmStatisticsCustomerSummaryByDateRespVO) => s.time
|
||||
)
|
||||
}
|
||||
if (echartsOption.series && echartsOption.series[0] && echartsOption.series[0]['data']) {
|
||||
echartsOption.series[0]['data'] = customerCount.map(
|
||||
(item: CrmStatisticsCustomerSummaryByDateRespVO) => {
|
||||
return {
|
||||
name: item.time,
|
||||
value: item.customerCreateCount
|
||||
? round((item.customerDealCount / item.customerCreateCount) * 100, 2)
|
||||
: 0
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
// 2.2 更新列表数据
|
||||
list.value = contractSummary
|
||||
loading.value = false
|
||||
}
|
||||
defineExpose({ loadData })
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(() => {
|
||||
loadData()
|
||||
})
|
||||
</script>
|
@ -0,0 +1,127 @@
|
||||
<!-- 成交周期分析 -->
|
||||
<template>
|
||||
<!-- Echarts图 -->
|
||||
<el-card shadow="never">
|
||||
<el-skeleton :loading="loading" animated>
|
||||
<Echart :height="500" :options="echartsOption" />
|
||||
</el-skeleton>
|
||||
</el-card>
|
||||
|
||||
<!-- 统计列表 -->
|
||||
<el-card shadow="never" class="mt-16px">
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="序号" align="center" type="index" width="80" />
|
||||
<el-table-column label="日期" align="center" prop="ownerUserName" min-width="200" />
|
||||
<el-table-column
|
||||
label="成交周期(天)"
|
||||
align="center"
|
||||
prop="customerDealCycle"
|
||||
min-width="200"
|
||||
/>
|
||||
<el-table-column label="成交客户数" align="center" prop="customerDealCount" min-width="200" />
|
||||
</el-table>
|
||||
</el-card>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
StatisticsCustomerApi,
|
||||
CrmStatisticsCustomerDealCycleByDateRespVO,
|
||||
CrmStatisticsCustomerSummaryByDateRespVO,
|
||||
} from '@/api/crm/statistics/customer'
|
||||
import { EChartsOption } from 'echarts'
|
||||
|
||||
defineOptions({ name: 'CustomerDealCycle' })
|
||||
const props = defineProps<{ queryParams: any }>() // 搜索参数
|
||||
|
||||
const loading = ref(false) // 加载中
|
||||
const list = ref<CrmStatisticsCustomerDealCycleByDateRespVO[]>([]) // 列表的数据
|
||||
|
||||
/** 柱状图配置:纵向 */
|
||||
const echartsOption = reactive<EChartsOption>({
|
||||
grid: {
|
||||
left: 20,
|
||||
right: 20,
|
||||
bottom: 20,
|
||||
containLabel: true
|
||||
},
|
||||
legend: {},
|
||||
series: [
|
||||
{
|
||||
name: '成交周期(天)',
|
||||
type: 'bar',
|
||||
data: []
|
||||
},
|
||||
{
|
||||
name: '成交客户数',
|
||||
type: 'bar',
|
||||
data: []
|
||||
}
|
||||
],
|
||||
toolbox: {
|
||||
feature: {
|
||||
dataZoom: {
|
||||
xAxisIndex: false // 数据区域缩放:Y 轴不缩放
|
||||
},
|
||||
brush: {
|
||||
type: ['lineX', 'clear'] // 区域缩放按钮、还原按钮
|
||||
},
|
||||
saveAsImage: { show: true, name: '成交周期分析' } // 保存为图片
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
name: '数量(个)'
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
name: '日期',
|
||||
data: []
|
||||
}
|
||||
}) as EChartsOption
|
||||
|
||||
/** 获取统计数据 */
|
||||
const loadData = async () => {
|
||||
// 1. 加载统计数据
|
||||
loading.value = true
|
||||
const customerDealCycleByDate = await StatisticsCustomerApi.getCustomerDealCycleByDate(
|
||||
props.queryParams
|
||||
)
|
||||
const customerSummaryByDate = await StatisticsCustomerApi.getCustomerSummaryByDate(
|
||||
props.queryParams
|
||||
)
|
||||
const customerDealCycleByUser = await StatisticsCustomerApi.getCustomerDealCycleByUser(
|
||||
props.queryParams
|
||||
)
|
||||
// 2.1 更新 Echarts 数据
|
||||
if (echartsOption.xAxis && echartsOption.xAxis['data']) {
|
||||
echartsOption.xAxis['data'] = customerDealCycleByDate.map(
|
||||
(s: CrmStatisticsCustomerDealCycleByDateRespVO) => s.time
|
||||
)
|
||||
}
|
||||
if (echartsOption.series && echartsOption.series[0] && echartsOption.series[0]['data']) {
|
||||
echartsOption.series[0]['data'] = customerDealCycleByDate.map(
|
||||
(s: CrmStatisticsCustomerDealCycleByDateRespVO) => s.customerDealCycle
|
||||
)
|
||||
}
|
||||
if (echartsOption.series && echartsOption.series[1] && echartsOption.series[1]['data']) {
|
||||
echartsOption.series[1]['data'] = customerSummaryByDate.map(
|
||||
(s: CrmStatisticsCustomerSummaryByDateRespVO) => s.customerDealCount
|
||||
)
|
||||
}
|
||||
// 2.2 更新列表数据
|
||||
list.value = customerDealCycleByUser
|
||||
loading.value = false
|
||||
}
|
||||
defineExpose({ loadData })
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(() => {
|
||||
loadData()
|
||||
})
|
||||
</script>
|
@ -0,0 +1,124 @@
|
||||
<!-- 客户跟进次数分析 -->
|
||||
<template>
|
||||
<!-- Echarts图 -->
|
||||
<el-card shadow="never">
|
||||
<el-skeleton :loading="loading" animated>
|
||||
<Echart :height="500" :options="echartsOption" />
|
||||
</el-skeleton>
|
||||
</el-card>
|
||||
|
||||
<!-- 统计列表 -->
|
||||
<el-card shadow="never" class="mt-16px">
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="序号" align="center" type="index" width="80" />
|
||||
<el-table-column label="员工姓名" align="center" prop="ownerUserName" min-width="200" />
|
||||
<el-table-column label="跟进次数" align="right" prop="followupRecordCount" min-width="200" />
|
||||
<el-table-column
|
||||
label="跟进客户数"
|
||||
align="right"
|
||||
prop="followupCustomerCount"
|
||||
min-width="200"
|
||||
/>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
StatisticsCustomerApi,
|
||||
CrmStatisticsFollowupSummaryByDateRespVO,
|
||||
CrmStatisticsFollowupSummaryByUserRespVO
|
||||
} from '@/api/crm/statistics/customer'
|
||||
import { EChartsOption } from 'echarts'
|
||||
|
||||
defineOptions({ name: 'CustomerFollowupSummary' })
|
||||
const props = defineProps<{ queryParams: any }>() // 搜索参数
|
||||
|
||||
const loading = ref(false) // 加载中
|
||||
const list = ref<CrmStatisticsFollowupSummaryByUserRespVO[]>([]) // 列表的数据
|
||||
|
||||
/** 柱状图配置:纵向 */
|
||||
const echartsOption = reactive<EChartsOption>({
|
||||
grid: {
|
||||
left: 20,
|
||||
right: 20,
|
||||
bottom: 20,
|
||||
containLabel: true
|
||||
},
|
||||
legend: {},
|
||||
series: [
|
||||
{
|
||||
name: '跟进客户数',
|
||||
type: 'bar',
|
||||
data: []
|
||||
},
|
||||
{
|
||||
name: '跟进次数',
|
||||
type: 'bar',
|
||||
data: []
|
||||
}
|
||||
],
|
||||
toolbox: {
|
||||
feature: {
|
||||
dataZoom: {
|
||||
xAxisIndex: false // 数据区域缩放:Y 轴不缩放
|
||||
},
|
||||
brush: {
|
||||
type: ['lineX', 'clear'] // 区域缩放按钮、还原按钮
|
||||
},
|
||||
saveAsImage: { show: true, name: '客户跟进次数分析' } // 保存为图片
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
name: '数量(个)'
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
name: '日期',
|
||||
data: []
|
||||
}
|
||||
}) as EChartsOption
|
||||
|
||||
/** 获取统计数据 */
|
||||
const loadData = async () => {
|
||||
// 1. 加载统计数据
|
||||
loading.value = true
|
||||
const followupSummaryByDate = await StatisticsCustomerApi.getFollowupSummaryByDate(
|
||||
props.queryParams
|
||||
)
|
||||
const followupSummaryByUser = await StatisticsCustomerApi.getFollowupSummaryByUser(
|
||||
props.queryParams
|
||||
)
|
||||
// 2.1 更新 Echarts 数据
|
||||
if (echartsOption.xAxis && echartsOption.xAxis['data']) {
|
||||
echartsOption.xAxis['data'] = followupSummaryByDate.map(
|
||||
(s: CrmStatisticsFollowupSummaryByDateRespVO) => s.time
|
||||
)
|
||||
}
|
||||
if (echartsOption.series && echartsOption.series[0] && echartsOption.series[0]['data']) {
|
||||
echartsOption.series[0]['data'] = followupSummaryByDate.map(
|
||||
(s: CrmStatisticsFollowupSummaryByDateRespVO) => s.followupCustomerCount
|
||||
)
|
||||
}
|
||||
if (echartsOption.series && echartsOption.series[1] && echartsOption.series[1]['data']) {
|
||||
echartsOption.series[1]['data'] = followupSummaryByDate.map(
|
||||
(s: CrmStatisticsFollowupSummaryByDateRespVO) => s.followupRecordCount
|
||||
)
|
||||
}
|
||||
// 2.2 更新列表数据
|
||||
list.value = followupSummaryByUser
|
||||
loading.value = false
|
||||
}
|
||||
defineExpose({ loadData })
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(() => {
|
||||
loadData()
|
||||
})
|
||||
</script>
|
@ -0,0 +1,105 @@
|
||||
<!-- 客户跟进方式分析 -->
|
||||
<template>
|
||||
<!-- Echarts图 -->
|
||||
<el-card shadow="never">
|
||||
<el-skeleton :loading="loading" animated>
|
||||
<Echart :height="500" :options="echartsOption" />
|
||||
</el-skeleton>
|
||||
</el-card>
|
||||
|
||||
<!-- 统计列表 -->
|
||||
<el-card shadow="never" class="mt-16px">
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="序号" align="center" type="index" width="80" />
|
||||
<el-table-column label="跟进方式" align="center" prop="followupType" min-width="200" />
|
||||
<el-table-column label="个数" align="center" prop="followupRecordCount" min-width="200" />
|
||||
<el-table-column label="占比(%)" align="center" prop="portion" min-width="200" />
|
||||
</el-table>
|
||||
</el-card>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
StatisticsCustomerApi,
|
||||
CrmStatisticsFollowupSummaryByTypeRespVO
|
||||
} from '@/api/crm/statistics/customer'
|
||||
import { EChartsOption } from 'echarts'
|
||||
import { round, sumBy } from 'lodash-es'
|
||||
|
||||
defineOptions({ name: 'CustomerFollowupType' })
|
||||
const props = defineProps<{ queryParams: any }>() // 搜索参数
|
||||
|
||||
const loading = ref(false) // 加载中
|
||||
const list = ref<CrmStatisticsFollowupSummaryByTypeRespVO[]>([]) // 列表的数据
|
||||
|
||||
/** 饼图配置 */
|
||||
const echartsOption = reactive<EChartsOption>({
|
||||
title: {
|
||||
text: '客户跟进方式分析',
|
||||
left: 'center'
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
left: 'left'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{b} : {c}% '
|
||||
},
|
||||
toolbox: {
|
||||
feature: {
|
||||
saveAsImage: { show: true, name: '客户跟进方式分析' } // 保存为图片
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '跟进方式',
|
||||
type: 'pie',
|
||||
radius: '50%',
|
||||
data: [],
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}) as EChartsOption
|
||||
|
||||
/** 获取统计数据 */
|
||||
const loadData = async () => {
|
||||
// 1. 加载统计数据
|
||||
loading.value = true
|
||||
const followupSummaryByType = await StatisticsCustomerApi.getFollowupSummaryByType(
|
||||
props.queryParams
|
||||
)
|
||||
// 2.1 更新 Echarts 数据
|
||||
if (echartsOption.series && echartsOption.series[0] && echartsOption.series[0]['data']) {
|
||||
echartsOption.series[0]['data'] = followupSummaryByType.map(
|
||||
(r: CrmStatisticsFollowupSummaryByTypeRespVO) => {
|
||||
return {
|
||||
name: r.followupType,
|
||||
value: r.followupRecordCount
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
// 2.2 更新列表数据
|
||||
const totalCount = sumBy(followupSummaryByType, 'followupRecordCount')
|
||||
list.value = followupSummaryByType.map((r: CrmStatisticsFollowupSummaryByTypeRespVO) => {
|
||||
return {
|
||||
followupType: r.followupType,
|
||||
followupRecordCount: r.followupRecordCount,
|
||||
portion: round((r.followupRecordCount / totalCount) * 100, 2)
|
||||
}
|
||||
})
|
||||
loading.value = false
|
||||
}
|
||||
defineExpose({ loadData })
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(() => {
|
||||
loadData()
|
||||
})
|
||||
</script>
|
151
src/views/crm/statistics/customer/components/CustomerSummary.vue
Normal file
151
src/views/crm/statistics/customer/components/CustomerSummary.vue
Normal file
@ -0,0 +1,151 @@
|
||||
<!-- 客户总量统计 -->
|
||||
<template>
|
||||
<!-- Echarts图 -->
|
||||
<el-card shadow="never">
|
||||
<el-skeleton :loading="loading" animated>
|
||||
<Echart :height="500" :options="echartsOption" />
|
||||
</el-skeleton>
|
||||
</el-card>
|
||||
|
||||
<!-- 统计列表 -->
|
||||
<el-card shadow="never" class="mt-16px">
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="序号" align="center" type="index" width="80" />
|
||||
<el-table-column label="员工姓名" prop="ownerUserName" min-width="100" />
|
||||
<el-table-column
|
||||
label="新增客户数"
|
||||
align="right"
|
||||
prop="customerCreateCount"
|
||||
min-width="200"
|
||||
/>
|
||||
<el-table-column label="成交客户数" align="right" prop="customerDealCount" min-width="200" />
|
||||
<el-table-column label="客户成交率(%)" align="right" min-width="200">
|
||||
<template #default="scope">
|
||||
{{
|
||||
scope.row.customerCreateCount !== 0
|
||||
? round((scope.row.customerDealCount / scope.row.customerCreateCount) * 100, 2)
|
||||
: 0
|
||||
}}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="合同总金额" align="right" prop="contractPrice" min-width="200" />
|
||||
<el-table-column label="回款金额" align="right" prop="receivablePrice" min-width="200" />
|
||||
<el-table-column label="未回款金额" align="right" min-width="200">
|
||||
<!-- TODO @dhb52:参考 util/index.ts 的 // ========== ERP 专属方法 ========== 部分,搞个两个方法,一个格式化百分比,一个计算百分比 -->
|
||||
<template #default="scope">
|
||||
{{ round(scope.row.contractPrice - scope.row.receivablePrice, 2) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="回款完成率(%)" align="right" min-width="200">
|
||||
<template #default="scope">
|
||||
{{
|
||||
scope.row.contractPrice !== 0
|
||||
? round((scope.row.receivablePrice / scope.row.contractPrice) * 100, 2)
|
||||
: 0
|
||||
}}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
StatisticsCustomerApi,
|
||||
CrmStatisticsCustomerSummaryByDateRespVO,
|
||||
CrmStatisticsCustomerSummaryByUserRespVO
|
||||
} from '@/api/crm/statistics/customer'
|
||||
import { EChartsOption } from 'echarts'
|
||||
import { round } from 'lodash-es'
|
||||
|
||||
defineOptions({ name: 'CustomerSummary' })
|
||||
const props = defineProps<{ queryParams: any }>() // 搜索参数
|
||||
|
||||
const loading = ref(false) // 加载中
|
||||
const list = ref<CrmStatisticsCustomerSummaryByUserRespVO[]>([]) // 列表的数据
|
||||
|
||||
/** 柱状图配置:纵向 */
|
||||
const echartsOption = reactive<EChartsOption>({
|
||||
grid: {
|
||||
left: 20,
|
||||
right: 20,
|
||||
bottom: 20,
|
||||
containLabel: true
|
||||
},
|
||||
legend: {},
|
||||
series: [
|
||||
{
|
||||
name: '新增客户数',
|
||||
type: 'bar',
|
||||
data: []
|
||||
},
|
||||
{
|
||||
name: '成交客户数',
|
||||
type: 'bar',
|
||||
data: []
|
||||
}
|
||||
],
|
||||
toolbox: {
|
||||
feature: {
|
||||
dataZoom: {
|
||||
xAxisIndex: false // 数据区域缩放:Y 轴不缩放
|
||||
},
|
||||
brush: {
|
||||
type: ['lineX', 'clear'] // 区域缩放按钮、还原按钮
|
||||
},
|
||||
saveAsImage: { show: true, name: '客户总量分析' } // 保存为图片
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
name: '数量(个)'
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
name: '日期',
|
||||
data: []
|
||||
}
|
||||
}) as EChartsOption
|
||||
|
||||
/** 获取统计数据 */
|
||||
const loadData = async () => {
|
||||
// 1. 加载统计数据
|
||||
loading.value = true
|
||||
const customerSummaryByDate = await StatisticsCustomerApi.getCustomerSummaryByDate(
|
||||
props.queryParams
|
||||
)
|
||||
const customerSummaryByUser = await StatisticsCustomerApi.getCustomerSummaryByUser(
|
||||
props.queryParams
|
||||
)
|
||||
// 2.1 更新 Echarts 数据
|
||||
if (echartsOption.xAxis && echartsOption.xAxis['data']) {
|
||||
echartsOption.xAxis['data'] = customerSummaryByDate.map(
|
||||
(s: CrmStatisticsCustomerSummaryByDateRespVO) => s.time
|
||||
)
|
||||
}
|
||||
if (echartsOption.series && echartsOption.series[0] && echartsOption.series[0]['data']) {
|
||||
echartsOption.series[0]['data'] = customerSummaryByDate.map(
|
||||
(s: CrmStatisticsCustomerSummaryByDateRespVO) => s.customerCreateCount
|
||||
)
|
||||
}
|
||||
if (echartsOption.series && echartsOption.series[1] && echartsOption.series[1]['data']) {
|
||||
echartsOption.series[1]['data'] = customerSummaryByDate.map(
|
||||
(s: CrmStatisticsCustomerSummaryByDateRespVO) => s.customerDealCount
|
||||
)
|
||||
}
|
||||
// 2.2 更新列表数据
|
||||
list.value = customerSummaryByUser
|
||||
loading.value = false
|
||||
}
|
||||
defineExpose({ loadData })
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(() => {
|
||||
loadData()
|
||||
})
|
||||
</script>
|
166
src/views/crm/statistics/customer/index.vue
Normal file
166
src/views/crm/statistics/customer/index.vue
Normal file
@ -0,0 +1,166 @@
|
||||
<!-- 数据统计 - 员工客户分析 -->
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="时间范围" prop="orderDate">
|
||||
<el-date-picker
|
||||
v-model="queryParams.times"
|
||||
:shortcuts="defaultShortcuts"
|
||||
class="!w-240px"
|
||||
end-placeholder="结束日期"
|
||||
start-placeholder="开始日期"
|
||||
type="daterange"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="归属部门" prop="deptId">
|
||||
<el-tree-select
|
||||
v-model="queryParams.deptId"
|
||||
class="!w-240px"
|
||||
:data="deptList"
|
||||
:props="defaultProps"
|
||||
check-strictly
|
||||
node-key="id"
|
||||
placeholder="请选择归属部门"
|
||||
@change="queryParams.userId = undefined"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="员工" prop="userId">
|
||||
<el-select v-model="queryParams.userId" class="!w-240px" placeholder="员工" clearable>
|
||||
<el-option
|
||||
v-for="(user, index) in userListByDeptId"
|
||||
:label="user.nickname"
|
||||
:value="user.id"
|
||||
:key="index"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"> <Icon icon="ep:search" class="mr-5px" /> 搜索 </el-button>
|
||||
<el-button @click="resetQuery"> <Icon icon="ep:refresh" class="mr-5px" /> 重置 </el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 客户统计 -->
|
||||
<el-col>
|
||||
<el-tabs v-model="activeTab">
|
||||
<!-- 客户总量分析 -->
|
||||
<el-tab-pane label="客户总量分析" name="customerSummary" lazy>
|
||||
<CustomerSummary :query-params="queryParams" ref="customerSummaryRef" />
|
||||
</el-tab-pane>
|
||||
<!-- 客户跟进次数分析 -->
|
||||
<el-tab-pane label="客户跟进次数分析" name="followupSummary" lazy>
|
||||
<CustomerFollowupSummary :query-params="queryParams" ref="followupSummaryRef" />
|
||||
</el-tab-pane>
|
||||
<!-- 客户跟进方式分析 -->
|
||||
<el-tab-pane label="客户跟进方式分析" name="followupType" lazy>
|
||||
<CustomerFollowupType :query-params="queryParams" ref="followupTypeRef" />
|
||||
</el-tab-pane>
|
||||
<!-- 客户转化率分析 -->
|
||||
<el-tab-pane label="客户转化率分析" name="conversionStat" lazy>
|
||||
<CustomerConversionStat :query-params="queryParams" ref="conversionStatRef" />
|
||||
</el-tab-pane>
|
||||
<!-- 成交周期分析 -->
|
||||
<el-tab-pane label="成交周期分析" name="dealCycle" lazy>
|
||||
<CustomerDealCycle :query-params="queryParams" ref="dealCycleRef" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-col>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import * as DeptApi from '@/api/system/dept'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
import { beginOfDay, defaultShortcuts, endOfDay, formatDate } from '@/utils/formatTime'
|
||||
import { defaultProps, handleTree } from '@/utils/tree'
|
||||
import CustomerSummary from './components/CustomerSummary.vue'
|
||||
import CustomerFollowupSummary from './components/CustomerFollowupSummary.vue'
|
||||
import CustomerFollowupType from './components/CustomerFollowupType.vue'
|
||||
import CustomerConversionStat from './components/CustomerConversionStat.vue'
|
||||
import CustomerDealCycle from './components/CustomerDealCycle.vue'
|
||||
|
||||
defineOptions({ name: 'CrmStatisticsCustomer' })
|
||||
|
||||
const queryParams = reactive({
|
||||
deptId: useUserStore().getUser.deptId,
|
||||
userId: undefined,
|
||||
times: [
|
||||
// 默认显示最近一周的数据
|
||||
formatDate(beginOfDay(new Date(new Date().getTime() - 3600 * 1000 * 24 * 7))),
|
||||
formatDate(endOfDay(new Date(new Date().getTime() - 3600 * 1000 * 24)))
|
||||
]
|
||||
})
|
||||
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const deptList = ref<Tree[]>([]) // 部门树形结构
|
||||
const userList = ref<UserApi.UserVO[]>([]) // 全量用户清单
|
||||
// 根据选择的部门筛选员工清单
|
||||
const userListByDeptId = computed(() =>
|
||||
queryParams.deptId
|
||||
? userList.value.filter((u: UserApi.UserVO) => u.deptId === queryParams.deptId)
|
||||
: []
|
||||
)
|
||||
|
||||
// 活跃标签
|
||||
const activeTab = ref('customerSummary')
|
||||
// 1.客户总量分析
|
||||
const customerSummaryRef = ref()
|
||||
// 2.客户跟进次数分析
|
||||
const followupSummaryRef = ref()
|
||||
// 3.客户跟进方式分析
|
||||
const followupTypeRef = ref()
|
||||
// 4.客户转化率分析
|
||||
const conversionStatRef = ref()
|
||||
// 5.公海客户分析
|
||||
// 缺 crm_owner_record 表
|
||||
// 6.成交周期分析
|
||||
const dealCycleRef = ref()
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
switch (activeTab.value) {
|
||||
case 'customerSummary':
|
||||
customerSummaryRef.value?.loadData?.()
|
||||
break
|
||||
case 'followupSummary':
|
||||
followupSummaryRef.value?.loadData?.()
|
||||
break
|
||||
case 'followupType':
|
||||
followupTypeRef.value?.loadData?.()
|
||||
break
|
||||
case 'conversionStat':
|
||||
conversionStatRef.value?.loadData?.()
|
||||
break
|
||||
case 'dealCycle':
|
||||
dealCycleRef.value?.loadData?.()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 当 activeTab 改变时,刷新当前活动的 tab
|
||||
watch(activeTab, () => {
|
||||
handleQuery()
|
||||
})
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
// 加载部门树
|
||||
onMounted(async () => {
|
||||
deptList.value = handleTree(await DeptApi.getSimpleDeptList())
|
||||
userList.value = handleTree(await UserApi.getSimpleUserList())
|
||||
})
|
||||
</script>
|
@ -2,39 +2,40 @@
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openForm('create')"
|
||||
v-hasPermi="['infra:demo03-student:create']"
|
||||
plain
|
||||
type="primary"
|
||||
@click="openForm('create')"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
<Icon class="mr-5px" icon="ep:plus" />
|
||||
新增
|
||||
</el-button>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||
<el-table-column label="编号" align="center" prop="id" />
|
||||
<el-table-column label="名字" align="center" prop="name" />
|
||||
<el-table-column label="分数" align="center" prop="score" />
|
||||
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true">
|
||||
<el-table-column align="center" label="编号" prop="id" />
|
||||
<el-table-column align="center" label="名字" prop="name" />
|
||||
<el-table-column align="center" label="分数" prop="score" />
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
label="创建时间"
|
||||
prop="createTime"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="操作" align="center">
|
||||
<el-table-column align="center" label="操作">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
v-hasPermi="['infra:demo03-student:update']"
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['infra:demo03-student:update']"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
v-hasPermi="['infra:demo03-student:delete']"
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['infra:demo03-student:delete']"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
@ -43,9 +44,9 @@
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
v-model:page="queryParams.pageNo"
|
||||
:total="total"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
@ -53,7 +54,7 @@
|
||||
<Demo03CourseForm ref="formRef" @success="getList" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
<script lang="ts" setup>
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import * as Demo03StudentApi from '@/api/infra/demo/demo03/erp'
|
||||
import Demo03CourseForm from './Demo03CourseForm.vue'
|
||||
@ -62,7 +63,7 @@ const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const props = defineProps<{
|
||||
studentId: undefined // 学生编号(主表的关联字段)
|
||||
studentId?: number // 学生编号(主表的关联字段)
|
||||
}>()
|
||||
const loading = ref(false) // 列表的加载中
|
||||
const list = ref([]) // 列表的数据
|
||||
@ -70,17 +71,20 @@ const total = ref(0) // 列表的总页数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
studentId: undefined
|
||||
studentId: undefined as unknown
|
||||
})
|
||||
|
||||
/** 监听主表的关联字段的变化,加载对应的子表数据 */
|
||||
watch(
|
||||
() => props.studentId,
|
||||
(val) => {
|
||||
(val: number) => {
|
||||
if (!val) {
|
||||
return
|
||||
}
|
||||
queryParams.studentId = val
|
||||
handleQuery()
|
||||
},
|
||||
{ immediate: false }
|
||||
{ immediate: true, deep: true }
|
||||
)
|
||||
|
||||
/** 查询列表 */
|
||||
|
@ -2,39 +2,40 @@
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openForm('create')"
|
||||
v-hasPermi="['infra:demo03-student:create']"
|
||||
plain
|
||||
type="primary"
|
||||
@click="openForm('create')"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
<Icon class="mr-5px" icon="ep:plus" />
|
||||
新增
|
||||
</el-button>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||
<el-table-column label="编号" align="center" prop="id" />
|
||||
<el-table-column label="名字" align="center" prop="name" />
|
||||
<el-table-column label="班主任" align="center" prop="teacher" />
|
||||
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true">
|
||||
<el-table-column align="center" label="编号" prop="id" />
|
||||
<el-table-column align="center" label="名字" prop="name" />
|
||||
<el-table-column align="center" label="班主任" prop="teacher" />
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
label="创建时间"
|
||||
prop="createTime"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="操作" align="center">
|
||||
<el-table-column align="center" label="操作">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
v-hasPermi="['infra:demo03-student:update']"
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['infra:demo03-student:update']"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
v-hasPermi="['infra:demo03-student:delete']"
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['infra:demo03-student:delete']"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
@ -43,9 +44,9 @@
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
v-model:page="queryParams.pageNo"
|
||||
:total="total"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
@ -53,7 +54,7 @@
|
||||
<Demo03GradeForm ref="formRef" @success="getList" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
<script lang="ts" setup>
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import * as Demo03StudentApi from '@/api/infra/demo/demo03/erp'
|
||||
import Demo03GradeForm from './Demo03GradeForm.vue'
|
||||
@ -62,7 +63,7 @@ const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const props = defineProps<{
|
||||
studentId: undefined // 学生编号(主表的关联字段)
|
||||
studentId?: number // 学生编号(主表的关联字段)
|
||||
}>()
|
||||
const loading = ref(false) // 列表的加载中
|
||||
const list = ref([]) // 列表的数据
|
||||
@ -70,17 +71,20 @@ const total = ref(0) // 列表的总页数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
studentId: undefined
|
||||
studentId: undefined as unknown
|
||||
})
|
||||
|
||||
/** 监听主表的关联字段的变化,加载对应的子表数据 */
|
||||
watch(
|
||||
() => props.studentId,
|
||||
(val) => {
|
||||
(val: number) => {
|
||||
if (!val) {
|
||||
return
|
||||
}
|
||||
queryParams.studentId = val
|
||||
handleQuery()
|
||||
},
|
||||
{ immediate: false }
|
||||
{ immediate: true, deep: true }
|
||||
)
|
||||
|
||||
/** 查询列表 */
|
||||
|
@ -4,23 +4,23 @@
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
:model="queryParams"
|
||||
class="-mb-15px"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="名字" prop="name">
|
||||
<el-input
|
||||
v-model="queryParams.name"
|
||||
placeholder="请输入名字"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
clearable
|
||||
placeholder="请输入名字"
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="性别" prop="sex">
|
||||
<el-select v-model="queryParams.sex" placeholder="请选择性别" clearable class="!w-240px">
|
||||
<el-select v-model="queryParams.sex" class="!w-240px" clearable placeholder="请选择性别">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)"
|
||||
:key="dict.value"
|
||||
@ -32,33 +32,41 @@
|
||||
<el-form-item label="创建时间" prop="createTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.createTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-240px"
|
||||
end-placeholder="结束日期"
|
||||
start-placeholder="开始日期"
|
||||
type="daterange"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openForm('create')"
|
||||
v-hasPermi="['infra:demo03-student:create']"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
<el-button @click="handleQuery">
|
||||
<Icon class="mr-5px" icon="ep:search" />
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button @click="resetQuery">
|
||||
<Icon class="mr-5px" icon="ep:refresh" />
|
||||
重置
|
||||
</el-button>
|
||||
<el-button
|
||||
type="success"
|
||||
v-hasPermi="['infra:demo03-student:create']"
|
||||
plain
|
||||
@click="handleExport"
|
||||
:loading="exportLoading"
|
||||
v-hasPermi="['infra:demo03-student:export']"
|
||||
type="primary"
|
||||
@click="openForm('create')"
|
||||
>
|
||||
<Icon icon="ep:download" class="mr-5px" /> 导出
|
||||
<Icon class="mr-5px" icon="ep:plus" />
|
||||
新增
|
||||
</el-button>
|
||||
<el-button
|
||||
v-hasPermi="['infra:demo03-student:export']"
|
||||
:loading="exportLoading"
|
||||
plain
|
||||
type="success"
|
||||
@click="handleExport"
|
||||
>
|
||||
<Icon class="mr-5px" icon="ep:download" />
|
||||
导出
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
@ -69,48 +77,48 @@
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="list"
|
||||
:stripe="true"
|
||||
:show-overflow-tooltip="true"
|
||||
:stripe="true"
|
||||
highlight-current-row
|
||||
@current-change="handleCurrentChange"
|
||||
>
|
||||
<el-table-column label="编号" align="center" prop="id" />
|
||||
<el-table-column label="名字" align="center" prop="name" />
|
||||
<el-table-column label="性别" align="center" prop="sex">
|
||||
<el-table-column align="center" label="编号" prop="id" />
|
||||
<el-table-column align="center" label="名字" prop="name" />
|
||||
<el-table-column align="center" label="性别" prop="sex">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="scope.row.sex" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
label="出生日期"
|
||||
align="center"
|
||||
prop="birthday"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="简介" align="center" prop="description" />
|
||||
<el-table-column align="center" label="简介" prop="description" />
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
label="创建时间"
|
||||
prop="createTime"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="操作" align="center">
|
||||
<el-table-column align="center" label="操作">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
v-hasPermi="['infra:demo03-student:update']"
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['infra:demo03-student:update']"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
v-hasPermi="['infra:demo03-student:delete']"
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['infra:demo03-student:delete']"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
@ -119,9 +127,9 @@
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
v-model:page="queryParams.pageNo"
|
||||
:total="total"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
@ -132,17 +140,17 @@
|
||||
<ContentWrap>
|
||||
<el-tabs model-value="demo03Course">
|
||||
<el-tab-pane label="学生课程" name="demo03Course">
|
||||
<Demo03CourseList :student-id="currentRow.id" />
|
||||
<Demo03CourseList :student-id="currentRow?.id" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="学生班级" name="demo03Grade">
|
||||
<Demo03GradeList :student-id="currentRow.id" />
|
||||
<Demo03GradeList :student-id="currentRow?.id" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
|
||||
<script lang="ts" setup>
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import download from '@/utils/download'
|
||||
import * as Demo03StudentApi from '@/api/infra/demo/demo03/erp'
|
||||
|
Reference in New Issue
Block a user