删除全部页面
This commit is contained in:
parent
12be97d132
commit
f647208b6e
|
@ -5,9 +5,6 @@
|
||||||
<el-row :gutter="16" justify="space-between">
|
<el-row :gutter="16" justify="space-between">
|
||||||
<el-col :xl="12" :lg="12" :md="12" :sm="24" :xs="24">
|
<el-col :xl="12" :lg="12" :md="12" :sm="24" :xs="24">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<el-avatar :src="avatar" :size="70" class="mr-16px">
|
|
||||||
<img src="@/assets/imgs/avatar.gif" alt="" />
|
|
||||||
</el-avatar>
|
|
||||||
<div>
|
<div>
|
||||||
<div class="text-20px">
|
<div class="text-20px">
|
||||||
{{ t('workplace.welcome') }} {{ username }} {{ t('workplace.happyDay') }}
|
{{ t('workplace.welcome') }} {{ username }} {{ t('workplace.happyDay') }}
|
||||||
|
@ -150,9 +147,6 @@
|
||||||
<el-skeleton :loading="loading" animated>
|
<el-skeleton :loading="loading" animated>
|
||||||
<div v-for="(item, index) in notice" :key="`dynamics-${index}`">
|
<div v-for="(item, index) in notice" :key="`dynamics-${index}`">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<el-avatar :src="avatar" :size="35" class="mr-16px">
|
|
||||||
<img src="@/assets/imgs/avatar.gif" alt="" />
|
|
||||||
</el-avatar>
|
|
||||||
<div>
|
<div>
|
||||||
<div class="text-14px">
|
<div class="text-14px">
|
||||||
<Highlight :keys="item.keys.map((v) => t(v))">
|
<Highlight :keys="item.keys.map((v) => t(v))">
|
||||||
|
@ -187,7 +181,7 @@ const { t } = useI18n()
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
const { setWatermark } = useWatermark()
|
const { setWatermark } = useWatermark()
|
||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
const avatar = userStore.getUser.avatar
|
// const avatar = userStore.getUser.avatar
|
||||||
const username = userStore.getUser.nickname
|
const username = userStore.getUser.nickname
|
||||||
const pieOptionsData = reactive<EChartsOption>(pieOptions) as EChartsOption
|
const pieOptionsData = reactive<EChartsOption>(pieOptions) as EChartsOption
|
||||||
// 获取统计数
|
// 获取统计数
|
||||||
|
|
|
@ -1,65 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="flex">
|
|
||||||
<el-card class="user w-1/3" shadow="hover">
|
|
||||||
<template #header>
|
|
||||||
<div class="card-header">
|
|
||||||
<span>{{ t('profile.user.title') }}</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<ProfileUser />
|
|
||||||
</el-card>
|
|
||||||
<el-card class="user ml-3 w-2/3" shadow="hover">
|
|
||||||
<template #header>
|
|
||||||
<div class="card-header">
|
|
||||||
<span>{{ t('profile.info.title') }}</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<div>
|
|
||||||
<el-tabs v-model="activeName" class="profile-tabs" style="height: 400px" tab-position="top">
|
|
||||||
<el-tab-pane :label="t('profile.info.basicInfo')" name="basicInfo">
|
|
||||||
<BasicInfo />
|
|
||||||
</el-tab-pane>
|
|
||||||
<el-tab-pane :label="t('profile.info.resetPwd')" name="resetPwd">
|
|
||||||
<ResetPwd />
|
|
||||||
</el-tab-pane>
|
|
||||||
<el-tab-pane :label="t('profile.info.userSocial')" name="userSocial">
|
|
||||||
<UserSocial v-model:activeName="activeName" />
|
|
||||||
</el-tab-pane>
|
|
||||||
</el-tabs>
|
|
||||||
</div>
|
|
||||||
</el-card>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { BasicInfo, ProfileUser, ResetPwd, UserSocial } from './components'
|
|
||||||
|
|
||||||
const { t } = useI18n()
|
|
||||||
defineOptions({ name: 'Profile' })
|
|
||||||
const activeName = ref('basicInfo')
|
|
||||||
</script>
|
|
||||||
<style scoped>
|
|
||||||
.user {
|
|
||||||
max-height: 960px;
|
|
||||||
padding: 15px 20px 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.el-card .el-card__header, .el-card .el-card__body) {
|
|
||||||
padding: 15px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.profile-tabs > .el-tabs__content {
|
|
||||||
padding: 32px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #6b778c;
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-tabs--left .el-tabs__content {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,96 +0,0 @@
|
||||||
<template>
|
|
||||||
<Form ref="formRef" :labelWidth="200" :rules="rules" :schema="schema">
|
|
||||||
<template #sex="form">
|
|
||||||
<el-radio-group v-model="form['sex']">
|
|
||||||
<el-radio :label="1">{{ t('profile.user.man') }}</el-radio>
|
|
||||||
<el-radio :label="2">{{ t('profile.user.woman') }}</el-radio>
|
|
||||||
</el-radio-group>
|
|
||||||
</template>
|
|
||||||
</Form>
|
|
||||||
<div style="text-align: center">
|
|
||||||
<XButton :title="t('common.save')" type="primary" @click="submit()" />
|
|
||||||
<XButton :title="t('common.reset')" type="danger" @click="init()" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import type { FormRules } from 'element-plus'
|
|
||||||
import { FormSchema } from '@/types/form'
|
|
||||||
import type { FormExpose } from '@/components/Form'
|
|
||||||
import {
|
|
||||||
getUserProfile,
|
|
||||||
updateUserProfile,
|
|
||||||
UserProfileUpdateReqVO
|
|
||||||
} from '@/api/system/user/profile'
|
|
||||||
import { useUserStore } from '@/store/modules/user'
|
|
||||||
|
|
||||||
defineOptions({ name: 'BasicInfo' })
|
|
||||||
|
|
||||||
const { t } = useI18n()
|
|
||||||
const message = useMessage() // 消息弹窗
|
|
||||||
const userStore = useUserStore()
|
|
||||||
// 表单校验
|
|
||||||
const rules = reactive<FormRules>({
|
|
||||||
nickname: [{ required: true, message: t('profile.rules.nickname'), trigger: 'blur' }],
|
|
||||||
email: [
|
|
||||||
{ required: true, message: t('profile.rules.mail'), trigger: 'blur' },
|
|
||||||
{
|
|
||||||
type: 'email',
|
|
||||||
message: t('profile.rules.truemail'),
|
|
||||||
trigger: ['blur', 'change']
|
|
||||||
}
|
|
||||||
],
|
|
||||||
mobile: [
|
|
||||||
{ required: true, message: t('profile.rules.phone'), trigger: 'blur' },
|
|
||||||
{
|
|
||||||
pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
|
|
||||||
message: t('profile.rules.truephone'),
|
|
||||||
trigger: 'blur'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
const schema = reactive<FormSchema[]>([
|
|
||||||
{
|
|
||||||
field: 'nickname',
|
|
||||||
label: t('profile.user.nickname'),
|
|
||||||
component: 'Input'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'mobile',
|
|
||||||
label: t('profile.user.mobile'),
|
|
||||||
component: 'Input'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'email',
|
|
||||||
label: t('profile.user.email'),
|
|
||||||
component: 'Input'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'sex',
|
|
||||||
label: t('profile.user.sex'),
|
|
||||||
component: 'InputNumber',
|
|
||||||
value: 0
|
|
||||||
}
|
|
||||||
])
|
|
||||||
const formRef = ref<FormExpose>() // 表单 Ref
|
|
||||||
const submit = () => {
|
|
||||||
const elForm = unref(formRef)?.getElFormRef()
|
|
||||||
if (!elForm) return
|
|
||||||
elForm.validate(async (valid) => {
|
|
||||||
if (valid) {
|
|
||||||
const data = unref(formRef)?.formModel as UserProfileUpdateReqVO
|
|
||||||
await updateUserProfile(data)
|
|
||||||
message.success(t('common.updateSuccess'))
|
|
||||||
const profile = await init()
|
|
||||||
userStore.setUserNicknameAction(profile.nickname)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
const init = async () => {
|
|
||||||
const res = await getUserProfile()
|
|
||||||
unref(formRef)?.setValues(res)
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
onMounted(async () => {
|
|
||||||
await init()
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,99 +0,0 @@
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<div class="text-center">
|
|
||||||
<UserAvatar :img="userInfo?.avatar" />
|
|
||||||
</div>
|
|
||||||
<ul class="list-group list-group-striped">
|
|
||||||
<li class="list-group-item">
|
|
||||||
<Icon class="mr-5px" icon="ep:user" />
|
|
||||||
{{ t('profile.user.username') }}
|
|
||||||
<div class="pull-right">{{ userInfo?.username }}</div>
|
|
||||||
</li>
|
|
||||||
<li class="list-group-item">
|
|
||||||
<Icon class="mr-5px" icon="ep:phone" />
|
|
||||||
{{ t('profile.user.mobile') }}
|
|
||||||
<div class="pull-right">{{ userInfo?.mobile }}</div>
|
|
||||||
</li>
|
|
||||||
<li class="list-group-item">
|
|
||||||
<Icon class="mr-5px" icon="fontisto:email" />
|
|
||||||
{{ t('profile.user.email') }}
|
|
||||||
<div class="pull-right">{{ userInfo?.email }}</div>
|
|
||||||
</li>
|
|
||||||
<li class="list-group-item">
|
|
||||||
<Icon class="mr-5px" icon="carbon:tree-view-alt" />
|
|
||||||
{{ t('profile.user.dept') }}
|
|
||||||
<div v-if="userInfo?.dept" class="pull-right">{{ userInfo?.dept.name }}</div>
|
|
||||||
</li>
|
|
||||||
<li class="list-group-item">
|
|
||||||
<Icon class="mr-5px" icon="ep:suitcase" />
|
|
||||||
{{ t('profile.user.posts') }}
|
|
||||||
<div v-if="userInfo?.posts" class="pull-right">
|
|
||||||
{{ userInfo?.posts.map((post) => post.name).join(',') }}
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
<li class="list-group-item">
|
|
||||||
<Icon class="mr-5px" icon="icon-park-outline:peoples" />
|
|
||||||
{{ t('profile.user.roles') }}
|
|
||||||
<div v-if="userInfo?.roles" class="pull-right">
|
|
||||||
{{ userInfo?.roles.map((role) => role.name).join(',') }}
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
<li class="list-group-item">
|
|
||||||
<Icon class="mr-5px" icon="ep:calendar" />
|
|
||||||
{{ t('profile.user.createTime') }}
|
|
||||||
<div class="pull-right">{{ formatDate(userInfo.createTime) }}</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { formatDate } from '@/utils/formatTime'
|
|
||||||
import UserAvatar from './UserAvatar.vue'
|
|
||||||
|
|
||||||
import { getUserProfile, ProfileVO } from '@/api/system/user/profile'
|
|
||||||
|
|
||||||
defineOptions({ name: 'ProfileUser' })
|
|
||||||
|
|
||||||
const { t } = useI18n()
|
|
||||||
const userInfo = ref({} as ProfileVO)
|
|
||||||
const getUserInfo = async () => {
|
|
||||||
const users = await getUserProfile()
|
|
||||||
userInfo.value = users
|
|
||||||
}
|
|
||||||
onMounted(async () => {
|
|
||||||
await getUserInfo()
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.text-center {
|
|
||||||
position: relative;
|
|
||||||
height: 120px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-group-striped > .list-group-item {
|
|
||||||
padding-right: 0;
|
|
||||||
padding-left: 0;
|
|
||||||
border-right: 0;
|
|
||||||
border-left: 0;
|
|
||||||
border-radius: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-group {
|
|
||||||
padding-left: 0;
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-group-item {
|
|
||||||
padding: 11px 0;
|
|
||||||
margin-bottom: -1px;
|
|
||||||
font-size: 13px;
|
|
||||||
border-top: 1px solid #e7eaec;
|
|
||||||
border-bottom: 1px solid #e7eaec;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pull-right {
|
|
||||||
float: right !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,73 +0,0 @@
|
||||||
<template>
|
|
||||||
<el-form ref="formRef" :model="password" :rules="rules" :label-width="200">
|
|
||||||
<el-form-item :label="t('profile.password.oldPassword')" prop="oldPassword">
|
|
||||||
<InputPassword v-model="password.oldPassword" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item :label="t('profile.password.newPassword')" prop="newPassword">
|
|
||||||
<InputPassword v-model="password.newPassword" strength />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item :label="t('profile.password.confirmPassword')" prop="confirmPassword">
|
|
||||||
<InputPassword v-model="password.confirmPassword" strength />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item>
|
|
||||||
<XButton :title="t('common.save')" type="primary" @click="submit(formRef)" />
|
|
||||||
<XButton :title="t('common.reset')" type="danger" @click="reset(formRef)" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</template>
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import type { FormInstance, FormRules } from 'element-plus'
|
|
||||||
|
|
||||||
import { InputPassword } from '@/components/InputPassword'
|
|
||||||
import { updateUserPassword } from '@/api/system/user/profile'
|
|
||||||
|
|
||||||
defineOptions({ name: 'ResetPwd' })
|
|
||||||
|
|
||||||
const { t } = useI18n()
|
|
||||||
const message = useMessage()
|
|
||||||
const formRef = ref<FormInstance>()
|
|
||||||
const password = reactive({
|
|
||||||
oldPassword: '',
|
|
||||||
newPassword: '',
|
|
||||||
confirmPassword: ''
|
|
||||||
})
|
|
||||||
|
|
||||||
// 表单校验
|
|
||||||
const equalToPassword = (_rule, value, callback) => {
|
|
||||||
if (password.newPassword !== value) {
|
|
||||||
callback(new Error(t('profile.password.diffPwd')))
|
|
||||||
} else {
|
|
||||||
callback()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const rules = reactive<FormRules>({
|
|
||||||
oldPassword: [
|
|
||||||
{ required: true, message: t('profile.password.oldPwdMsg'), trigger: 'blur' },
|
|
||||||
{ min: 6, max: 20, message: t('profile.password.pwdRules'), trigger: 'blur' }
|
|
||||||
],
|
|
||||||
newPassword: [
|
|
||||||
{ required: true, message: t('profile.password.newPwdMsg'), trigger: 'blur' },
|
|
||||||
{ min: 6, max: 20, message: t('profile.password.pwdRules'), trigger: 'blur' }
|
|
||||||
],
|
|
||||||
confirmPassword: [
|
|
||||||
{ required: true, message: t('profile.password.cfPwdMsg'), trigger: 'blur' },
|
|
||||||
{ required: true, validator: equalToPassword, trigger: 'blur' }
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
const submit = (formEl: FormInstance | undefined) => {
|
|
||||||
if (!formEl) return
|
|
||||||
formEl.validate(async (valid) => {
|
|
||||||
if (valid) {
|
|
||||||
await updateUserPassword(password.oldPassword, password.newPassword)
|
|
||||||
message.success(t('common.updateSuccess'))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const reset = (formEl: FormInstance | undefined) => {
|
|
||||||
if (!formEl) return
|
|
||||||
formEl.resetFields()
|
|
||||||
}
|
|
||||||
</script>
|
|
|
@ -1,45 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="change-avatar">
|
|
||||||
<CropperAvatar
|
|
||||||
ref="cropperRef"
|
|
||||||
:btnProps="{ preIcon: 'ant-design:cloud-upload-outlined' }"
|
|
||||||
:showBtn="false"
|
|
||||||
:value="img"
|
|
||||||
width="120px"
|
|
||||||
@change="handelUpload"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { propTypes } from '@/utils/propTypes'
|
|
||||||
import { uploadAvatar } from '@/api/system/user/profile'
|
|
||||||
import { CropperAvatar } from '@/components/Cropper'
|
|
||||||
import { useUserStore } from '@/store/modules/user'
|
|
||||||
|
|
||||||
|
|
||||||
defineOptions({ name: 'UserAvatar' })
|
|
||||||
|
|
||||||
defineProps({
|
|
||||||
img: propTypes.string.def('')
|
|
||||||
})
|
|
||||||
|
|
||||||
const userStore = useUserStore()
|
|
||||||
|
|
||||||
|
|
||||||
const cropperRef = ref()
|
|
||||||
const handelUpload = async ({ data }) => {
|
|
||||||
const res = await uploadAvatar({ avatarFile: data })
|
|
||||||
cropperRef.value.close()
|
|
||||||
userStore.setUserAvatarAction(res.data)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.change-avatar {
|
|
||||||
img {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,107 +0,0 @@
|
||||||
<template>
|
|
||||||
<el-table :data="socialUsers" :show-header="false">
|
|
||||||
<el-table-column fixed="left" title="序号" type="seq" width="60" />
|
|
||||||
<el-table-column align="left" label="社交平台" width="120">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<img :src="row.img" alt="" class="h-5 align-middle" />
|
|
||||||
<p class="mr-5">{{ row.title }}</p>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="操作">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<template v-if="row.openid">
|
|
||||||
已绑定
|
|
||||||
<XTextButton class="mr-5" title="(解绑)" type="primary" @click="unbind(row)" />
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
未绑定
|
|
||||||
<XTextButton class="mr-5" title="(绑定)" type="primary" @click="bind(row)" />
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
</template>
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { SystemUserSocialTypeEnum } from '@/utils/constants'
|
|
||||||
import { getUserProfile, ProfileVO } from '@/api/system/user/profile'
|
|
||||||
import { socialAuthRedirect, socialBind, socialUnbind } from '@/api/system/user/socialUser'
|
|
||||||
|
|
||||||
defineOptions({ name: 'UserSocial' })
|
|
||||||
defineProps<{
|
|
||||||
activeName: string
|
|
||||||
}>()
|
|
||||||
const message = useMessage()
|
|
||||||
const socialUsers = ref<any[]>([])
|
|
||||||
const userInfo = ref<ProfileVO>()
|
|
||||||
|
|
||||||
const initSocial = async () => {
|
|
||||||
socialUsers.value = [] // 重置避免无限增长
|
|
||||||
const res = await getUserProfile()
|
|
||||||
userInfo.value = res
|
|
||||||
for (const i in SystemUserSocialTypeEnum) {
|
|
||||||
const socialUser = { ...SystemUserSocialTypeEnum[i] }
|
|
||||||
socialUsers.value.push(socialUser)
|
|
||||||
if (userInfo.value?.socialUsers) {
|
|
||||||
for (const j in userInfo.value.socialUsers) {
|
|
||||||
if (socialUser.type === userInfo.value.socialUsers[j].type) {
|
|
||||||
socialUser.openid = userInfo.value.socialUsers[j].openid
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const route = useRoute()
|
|
||||||
const emit = defineEmits<{
|
|
||||||
(e: 'update:activeName', v: string): void
|
|
||||||
}>()
|
|
||||||
const bindSocial = () => {
|
|
||||||
// 社交绑定
|
|
||||||
const type = getUrlValue('type')
|
|
||||||
const code = route.query.code
|
|
||||||
const state = route.query.state
|
|
||||||
if (!code) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
socialBind(type, code, state).then(() => {
|
|
||||||
message.success('绑定成功')
|
|
||||||
emit('update:activeName', 'userSocial')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 双层 encode 需要在回调后进行 decode
|
|
||||||
function getUrlValue(key: string): string {
|
|
||||||
const url = new URL(decodeURIComponent(location.href))
|
|
||||||
return url.searchParams.get(key) ?? ''
|
|
||||||
}
|
|
||||||
|
|
||||||
const bind = (row) => {
|
|
||||||
// 双层 encode 解决钉钉回调 type 参数丢失的问题
|
|
||||||
const redirectUri = location.origin + '/user/profile?' + encodeURIComponent(`type=${row.type}`)
|
|
||||||
// 进行跳转
|
|
||||||
socialAuthRedirect(row.type, encodeURIComponent(redirectUri)).then((res) => {
|
|
||||||
window.location.href = res
|
|
||||||
})
|
|
||||||
}
|
|
||||||
const unbind = async (row) => {
|
|
||||||
const res = await socialUnbind(row.type, row.openid)
|
|
||||||
if (res) {
|
|
||||||
row.openid = undefined
|
|
||||||
}
|
|
||||||
message.success('解绑成功')
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(async () => {
|
|
||||||
await initSocial()
|
|
||||||
})
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => route,
|
|
||||||
() => {
|
|
||||||
bindSocial()
|
|
||||||
},
|
|
||||||
{
|
|
||||||
immediate: true
|
|
||||||
}
|
|
||||||
)
|
|
||||||
</script>
|
|
|
@ -1,7 +0,0 @@
|
||||||
import BasicInfo from './BasicInfo.vue'
|
|
||||||
import ProfileUser from './ProfileUser.vue'
|
|
||||||
import ResetPwd from './ResetPwd.vue'
|
|
||||||
import UserAvatarVue from './UserAvatar.vue'
|
|
||||||
import UserSocial from './UserSocial.vue'
|
|
||||||
|
|
||||||
export { BasicInfo, ProfileUser, ResetPwd, UserAvatarVue, UserSocial }
|
|
|
@ -1,28 +0,0 @@
|
||||||
<template>
|
|
||||||
<div></div>
|
|
||||||
</template>
|
|
||||||
<script lang="ts" setup>
|
|
||||||
defineOptions({ name: 'Redirect' })
|
|
||||||
|
|
||||||
const { currentRoute, replace } = useRouter()
|
|
||||||
const { params, query } = unref(currentRoute)
|
|
||||||
const { path, _redirect_type = 'path' } = params
|
|
||||||
|
|
||||||
Reflect.deleteProperty(params, '_redirect_type')
|
|
||||||
Reflect.deleteProperty(params, 'path')
|
|
||||||
|
|
||||||
const _path = Array.isArray(path) ? path.join('/') : path
|
|
||||||
|
|
||||||
if (_redirect_type === 'name') {
|
|
||||||
replace({
|
|
||||||
name: _path,
|
|
||||||
query,
|
|
||||||
params
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
replace({
|
|
||||||
path: _path.startsWith('/') ? _path : '/' + _path,
|
|
||||||
query
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</script>
|
|
|
@ -1,166 +0,0 @@
|
||||||
<template>
|
|
||||||
<el-container>
|
|
||||||
<!-- 左侧:会话列表 -->
|
|
||||||
<el-aside width="260px" class="conversation-container">
|
|
||||||
<!-- 左顶部:新建对话 -->
|
|
||||||
<el-button class="w-1/1" type="primary">
|
|
||||||
<Icon icon="ep:plus" class="mr-5px" />
|
|
||||||
新建对话
|
|
||||||
</el-button>
|
|
||||||
<!-- 左顶部:搜索对话 -->
|
|
||||||
<el-input
|
|
||||||
v-model="searchName"
|
|
||||||
class="mt-10px"
|
|
||||||
placeholder="搜索历史记录"
|
|
||||||
@keyup="searchConversation"
|
|
||||||
>
|
|
||||||
<template #prefix>
|
|
||||||
<Icon icon="ep:search" />
|
|
||||||
</template>
|
|
||||||
</el-input>
|
|
||||||
<!-- 左中间:对话列表 -->
|
|
||||||
<div class="conversation-list" :style="{ height: leftHeight + 'px' }">
|
|
||||||
<el-row v-for="conversation in conversationList" :key="conversation.id">
|
|
||||||
<div
|
|
||||||
:class="conversation.id === conversationId ? 'conversation active' : 'conversation'"
|
|
||||||
@click="changeConversation(conversation)"
|
|
||||||
>
|
|
||||||
<el-image :src="conversation.avatar" class="avatar" />
|
|
||||||
<span class="title">{{ conversation.title }}</span>
|
|
||||||
<span class="button">
|
|
||||||
<!-- TODO 芋艿:缺置顶按钮 -->
|
|
||||||
<el-icon title="编辑" @click="updateConversationTitle(conversation)">
|
|
||||||
<Icon icon="ep:edit" />
|
|
||||||
</el-icon>
|
|
||||||
<el-icon title="删除会话" @click="deleteConversationTitle(conversation)">
|
|
||||||
<Icon icon="ep:delete" />
|
|
||||||
</el-icon>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</el-row>
|
|
||||||
</div>
|
|
||||||
<!-- 左底部:工具栏 TODO 芋艿:50% 不太对 -->
|
|
||||||
<div class="tool-box">
|
|
||||||
<sapn class="w-1/2"> <Icon icon="ep:user" /> 角色仓库 </sapn>
|
|
||||||
<sapn class="w-1/2"> <Icon icon="ep:delete" />清空未置顶对话</sapn>
|
|
||||||
</div>
|
|
||||||
</el-aside>
|
|
||||||
<!-- 右侧:会话详情 -->
|
|
||||||
<el-container class="detail-container">
|
|
||||||
<!-- 右顶部 TODO 芋艿:右对齐 -->
|
|
||||||
<el-header class="header">
|
|
||||||
<el-button>3.5-turbo-0125 <Icon icon="ep:setting" /></el-button>
|
|
||||||
<el-button>
|
|
||||||
<Icon icon="ep:user" />
|
|
||||||
</el-button>
|
|
||||||
<el-button>
|
|
||||||
<Icon icon="ep:download" />
|
|
||||||
</el-button>
|
|
||||||
<el-button>
|
|
||||||
<Icon icon="ep:arrow-up" />
|
|
||||||
</el-button>
|
|
||||||
</el-header>
|
|
||||||
<el-main>对话列表</el-main>
|
|
||||||
<el-footer>发送消息框</el-footer>
|
|
||||||
</el-container>
|
|
||||||
</el-container>
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
const conversationList = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
title: '测试标题',
|
|
||||||
avatar:
|
|
||||||
'http://test.yudao.iocoder.cn/96c787a2ce88bf6d0ce3cd8b6cf5314e80e7703cd41bf4af8cd2e2909dbd6b6d.png'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
title: '测试对话',
|
|
||||||
avatar:
|
|
||||||
'http://test.yudao.iocoder.cn/96c787a2ce88bf6d0ce3cd8b6cf5314e80e7703cd41bf4af8cd2e2909dbd6b6d.png'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
const conversationId = ref(1)
|
|
||||||
const searchName = ref('')
|
|
||||||
const leftHeight = window.innerHeight - 240 // TODO 芋艿:这里还不太对
|
|
||||||
|
|
||||||
const changeConversation = (conversation) => {
|
|
||||||
console.log(conversation)
|
|
||||||
conversationId.value = conversation.id
|
|
||||||
// TODO 芋艿:待实现
|
|
||||||
}
|
|
||||||
|
|
||||||
const updateConversationTitle = (conversation) => {
|
|
||||||
console.log(conversation)
|
|
||||||
// TODO 芋艿:待实现
|
|
||||||
}
|
|
||||||
|
|
||||||
const deleteConversationTitle = (conversation) => {
|
|
||||||
console.log(conversation)
|
|
||||||
// TODO 芋艿:待实现
|
|
||||||
}
|
|
||||||
|
|
||||||
const searchConversation = () => {
|
|
||||||
// TODO 芋艿:待实现
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.conversation-container {
|
|
||||||
.conversation-list {
|
|
||||||
.conversation {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-start;
|
|
||||||
width: 100%;
|
|
||||||
padding: 5px 5px 0 0;
|
|
||||||
cursor: pointer;
|
|
||||||
&.active {
|
|
||||||
// TODO 芋艿:这里不太对
|
|
||||||
background-color: #343540;
|
|
||||||
.button {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.title {
|
|
||||||
padding: 5px 10px;
|
|
||||||
max-width: 220px;
|
|
||||||
font-size: 14px;
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
.avatar {
|
|
||||||
width: 28px;
|
|
||||||
height: 28px;
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
.button {
|
|
||||||
position: absolute;
|
|
||||||
right: 2px;
|
|
||||||
top: 16px;
|
|
||||||
.el-icon {
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tool-box {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-start;
|
|
||||||
padding: 0 20px 10px 20px;
|
|
||||||
border-top: 1px solid black;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.detail-container {
|
|
||||||
.header {
|
|
||||||
width: 100%;
|
|
||||||
height: 30px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,124 +0,0 @@
|
||||||
<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>
|
|
|
@ -1,200 +0,0 @@
|
||||||
<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="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: any = 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>
|
|
|
@ -1,149 +0,0 @@
|
||||||
<template>
|
|
||||||
<!-- <doc-alert title="工作流手册" url="https://doc.iocoder.cn/bpm/" /> -->
|
|
||||||
|
|
||||||
<ContentWrap>
|
|
||||||
<el-table v-loading="loading" :data="list">
|
|
||||||
<el-table-column label="定义编号" align="center" prop="id" width="400" />
|
|
||||||
<el-table-column label="流程名称" align="center" prop="name" width="200">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button type="primary" link @click="handleBpmnDetail(scope.row)">
|
|
||||||
<span>{{ scope.row.name }}</span>
|
|
||||||
</el-button>
|
|
||||||
</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
|
|
||||||
v-if="scope.row.formType === 10"
|
|
||||||
type="primary"
|
|
||||||
link
|
|
||||||
@click="handleFormDetail(scope.row)"
|
|
||||||
>
|
|
||||||
<span>{{ scope.row.formName }}</span>
|
|
||||||
</el-button>
|
|
||||||
<el-button v-else type="primary" link @click="handleFormDetail(scope.row)">
|
|
||||||
<span>{{ scope.row.formCustomCreatePath }}</span>
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="流程版本" align="center" prop="processDefinition.version" width="80">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-tag v-if="scope.row">v{{ scope.row.version }}</el-tag>
|
|
||||||
<el-tag type="warning" v-else>未部署</el-tag>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="状态" align="center" prop="version" width="80">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-tag type="success" v-if="scope.row.suspensionState === 1">激活</el-tag>
|
|
||||||
<el-tag type="warning" v-if="scope.row.suspensionState === 2">挂起</el-tag>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
label="部署时间"
|
|
||||||
align="center"
|
|
||||||
prop="deploymentTime"
|
|
||||||
width="180"
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
label="定义描述"
|
|
||||||
align="center"
|
|
||||||
prop="description"
|
|
||||||
width="300"
|
|
||||||
show-overflow-tooltip
|
|
||||||
/>
|
|
||||||
</el-table>
|
|
||||||
<!-- 分页 -->
|
|
||||||
<Pagination
|
|
||||||
:total="total"
|
|
||||||
v-model:page="queryParams.pageNo"
|
|
||||||
v-model:limit="queryParams.pageSize"
|
|
||||||
@pagination="getList"
|
|
||||||
/>
|
|
||||||
</ContentWrap>
|
|
||||||
|
|
||||||
<!-- 弹窗:表单详情 -->
|
|
||||||
<Dialog title="表单详情" v-model="formDetailVisible" width="800">
|
|
||||||
<form-create :rule="formDetailPreview.rule" :option="formDetailPreview.option" />
|
|
||||||
</Dialog>
|
|
||||||
|
|
||||||
<!-- 弹窗:流程模型图的预览 -->
|
|
||||||
<Dialog title="流程图" v-model="bpmnDetailVisible" width="800">
|
|
||||||
<MyProcessViewer
|
|
||||||
key="designer"
|
|
||||||
v-model="bpmnXml"
|
|
||||||
:value="bpmnXml as any"
|
|
||||||
v-bind="bpmnControlForm"
|
|
||||||
:prefix="bpmnControlForm.prefix"
|
|
||||||
/>
|
|
||||||
</Dialog>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { dateFormatter } from '@/utils/formatTime'
|
|
||||||
import { MyProcessViewer } from '@/components/bpmnProcessDesigner/package'
|
|
||||||
import * as DefinitionApi from '@/api/bpm/definition'
|
|
||||||
import { setConfAndFields2 } from '@/utils/formCreate'
|
|
||||||
|
|
||||||
defineOptions({ name: 'BpmProcessDefinition' })
|
|
||||||
|
|
||||||
const { push } = useRouter() // 路由
|
|
||||||
const { query } = useRoute() // 查询参数
|
|
||||||
|
|
||||||
const loading = ref(true) // 列表的加载中
|
|
||||||
const total = ref(0) // 列表的总页数
|
|
||||||
const list = ref([]) // 列表的数据
|
|
||||||
const queryParams = reactive({
|
|
||||||
pageNo: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
key: query.key
|
|
||||||
})
|
|
||||||
|
|
||||||
/** 查询列表 */
|
|
||||||
const getList = async () => {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
const data = await DefinitionApi.getProcessDefinitionPage(queryParams)
|
|
||||||
list.value = data.list
|
|
||||||
total.value = data.total
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 流程表单的详情按钮操作 */
|
|
||||||
const formDetailVisible = ref(false)
|
|
||||||
const formDetailPreview = ref({
|
|
||||||
rule: [],
|
|
||||||
option: {}
|
|
||||||
})
|
|
||||||
const handleFormDetail = async (row) => {
|
|
||||||
if (row.formType == 10) {
|
|
||||||
// 设置表单
|
|
||||||
setConfAndFields2(formDetailPreview, row.formConf, row.formFields)
|
|
||||||
// 弹窗打开
|
|
||||||
formDetailVisible.value = true
|
|
||||||
} else {
|
|
||||||
await push({
|
|
||||||
path: row.formCustomCreatePath
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 流程图的详情按钮操作 */
|
|
||||||
const bpmnDetailVisible = ref(false)
|
|
||||||
const bpmnXml = ref(null)
|
|
||||||
const bpmnControlForm = ref({
|
|
||||||
prefix: 'flowable'
|
|
||||||
})
|
|
||||||
const handleBpmnDetail = async (row) => {
|
|
||||||
bpmnXml.value = (await DefinitionApi.getProcessDefinition(row.id))?.bpmnXml
|
|
||||||
bpmnDetailVisible.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 初始化 **/
|
|
||||||
onMounted(() => {
|
|
||||||
getList()
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,121 +0,0 @@
|
||||||
<template>
|
|
||||||
<ContentWrap>
|
|
||||||
<!-- 表单设计器 -->
|
|
||||||
<FcDesigner ref="designer" height="780px">
|
|
||||||
<template #handle>
|
|
||||||
<el-button round size="small" type="primary" @click="handleSave">
|
|
||||||
<Icon class="mr-5px" icon="ep:plus" />
|
|
||||||
保存
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</FcDesigner>
|
|
||||||
</ContentWrap>
|
|
||||||
|
|
||||||
<!-- 表单保存的弹窗 -->
|
|
||||||
<Dialog v-model="dialogVisible" title="保存表单" width="600">
|
|
||||||
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="80px">
|
|
||||||
<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="remark">
|
|
||||||
<el-input v-model="formData.remark" 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 { CommonStatusEnum } from '@/utils/constants'
|
|
||||||
import * as FormApi from '@/api/bpm/form'
|
|
||||||
import FcDesigner from '@form-create/designer'
|
|
||||||
import { encodeConf, encodeFields, setConfAndFields } from '@/utils/formCreate'
|
|
||||||
import { useTagsViewStore } from '@/store/modules/tagsView'
|
|
||||||
import { useFormCreateDesigner } from '@/components/FormCreate'
|
|
||||||
|
|
||||||
defineOptions({ name: 'BpmFormEditor' })
|
|
||||||
|
|
||||||
const { t } = useI18n() // 国际化
|
|
||||||
const message = useMessage() // 消息
|
|
||||||
const { push, currentRoute } = useRouter() // 路由
|
|
||||||
const { query } = useRoute() // 路由信息
|
|
||||||
const { delView } = useTagsViewStore() // 视图操作
|
|
||||||
|
|
||||||
const designer = ref() // 表单设计器
|
|
||||||
useFormCreateDesigner(designer) // 表单设计器增强
|
|
||||||
const dialogVisible = ref(false) // 弹窗是否展示
|
|
||||||
const formLoading = ref(false) // 表单的加载中:提交的按钮禁用
|
|
||||||
const formData = ref({
|
|
||||||
name: '',
|
|
||||||
status: CommonStatusEnum.ENABLE,
|
|
||||||
remark: ''
|
|
||||||
})
|
|
||||||
const formRules = reactive({
|
|
||||||
name: [{ required: true, message: '表单名不能为空', trigger: 'blur' }],
|
|
||||||
status: [{ required: true, message: '开启状态不能为空', trigger: 'blur' }]
|
|
||||||
})
|
|
||||||
const formRef = ref() // 表单 Ref
|
|
||||||
|
|
||||||
/** 处理保存按钮 */
|
|
||||||
const handleSave = () => {
|
|
||||||
dialogVisible.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 提交表单 */
|
|
||||||
const submitForm = async () => {
|
|
||||||
// 校验表单
|
|
||||||
if (!formRef) return
|
|
||||||
const valid = await formRef.value.validate()
|
|
||||||
if (!valid) return
|
|
||||||
// 提交请求
|
|
||||||
formLoading.value = true
|
|
||||||
try {
|
|
||||||
const data = formData.value as FormApi.FormVO
|
|
||||||
data.conf = encodeConf(designer) // 表单配置
|
|
||||||
data.fields = encodeFields(designer) // 表单字段
|
|
||||||
if (!data.id) {
|
|
||||||
await FormApi.createForm(data)
|
|
||||||
message.success(t('common.createSuccess'))
|
|
||||||
} else {
|
|
||||||
await FormApi.updateForm(data)
|
|
||||||
message.success(t('common.updateSuccess'))
|
|
||||||
}
|
|
||||||
dialogVisible.value = false
|
|
||||||
close()
|
|
||||||
} finally {
|
|
||||||
formLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/** 关闭按钮 */
|
|
||||||
const close = () => {
|
|
||||||
delView(unref(currentRoute))
|
|
||||||
push('/bpm/manager/form')
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 初始化 **/
|
|
||||||
onMounted(async () => {
|
|
||||||
// 场景一:新增表单
|
|
||||||
const id = query.id as unknown as number
|
|
||||||
if (!id) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 场景二:修改表单
|
|
||||||
const data = await FormApi.getForm(id)
|
|
||||||
formData.value = data
|
|
||||||
setConfAndFields(designer, data.conf, data.fields)
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,195 +0,0 @@
|
||||||
<template>
|
|
||||||
<!-- <doc-alert title="审批接入(流程表单)" url="https://doc.iocoder.cn/bpm/use-bpm-form/" /> -->
|
|
||||||
|
|
||||||
<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>
|
|
||||||
<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="['bpm:form:create']" plain type="primary" @click="openForm()">
|
|
||||||
<Icon class="mr-5px" icon="ep:plus" />
|
|
||||||
新增
|
|
||||||
</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</ContentWrap>
|
|
||||||
|
|
||||||
<!-- 列表 -->
|
|
||||||
<ContentWrap>
|
|
||||||
<el-table v-loading="loading" :data="list">
|
|
||||||
<el-table-column align="center" label="编号" prop="id" />
|
|
||||||
<el-table-column align="center" label="表单名" prop="name" />
|
|
||||||
<el-table-column align="center" label="状态" prop="status">
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="备注" prop="remark" />
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="创建时间"
|
|
||||||
prop="createTime"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="操作">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
v-hasPermi="['bpm:form:update']"
|
|
||||||
link
|
|
||||||
type="primary"
|
|
||||||
@click="openForm(scope.row.id)"
|
|
||||||
>
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button v-hasPermi="['bpm:form:query']" link @click="openDetail(scope.row.id)">
|
|
||||||
详情
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
v-hasPermi="['bpm:form:delete']"
|
|
||||||
link
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(scope.row.id)"
|
|
||||||
>
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
<!-- 分页 -->
|
|
||||||
<Pagination
|
|
||||||
v-model:limit="queryParams.pageSize"
|
|
||||||
v-model:page="queryParams.pageNo"
|
|
||||||
:total="total"
|
|
||||||
@pagination="getList"
|
|
||||||
/>
|
|
||||||
</ContentWrap>
|
|
||||||
|
|
||||||
<!-- 表单详情的弹窗 -->
|
|
||||||
<Dialog v-model="detailVisible" title="表单详情" width="800">
|
|
||||||
<form-create :option="detailData.option" :rule="detailData.rule" />
|
|
||||||
</Dialog>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { DICT_TYPE } from '@/utils/dict'
|
|
||||||
import { dateFormatter } from '@/utils/formatTime'
|
|
||||||
import * as FormApi from '@/api/bpm/form'
|
|
||||||
import { setConfAndFields2 } from '@/utils/formCreate'
|
|
||||||
|
|
||||||
defineOptions({ name: 'BpmForm' })
|
|
||||||
|
|
||||||
const message = useMessage() // 消息弹窗
|
|
||||||
const { t } = useI18n() // 国际化
|
|
||||||
const { currentRoute, push } = useRouter() // 路由
|
|
||||||
|
|
||||||
const loading = ref(true) // 列表的加载中
|
|
||||||
const total = ref(0) // 列表的总页数
|
|
||||||
const list = ref([]) // 列表的数据
|
|
||||||
const queryParams = reactive({
|
|
||||||
pageNo: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
name: null
|
|
||||||
})
|
|
||||||
const queryFormRef = ref() // 搜索的表单
|
|
||||||
|
|
||||||
/** 查询列表 */
|
|
||||||
const getList = async () => {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
const data = await FormApi.getFormPage(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 openForm = (id?: number) => {
|
|
||||||
const toRouter: { name: string; query?: { id: number } } = {
|
|
||||||
name: 'BpmFormEditor'
|
|
||||||
}
|
|
||||||
// 表单新建的时候id传的是event需要排除
|
|
||||||
if (typeof id === 'number') {
|
|
||||||
toRouter.query = {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
push(toRouter)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 删除按钮操作 */
|
|
||||||
const handleDelete = async (id: number) => {
|
|
||||||
try {
|
|
||||||
// 删除的二次确认
|
|
||||||
await message.delConfirm()
|
|
||||||
// 发起删除
|
|
||||||
await FormApi.deleteForm(id)
|
|
||||||
message.success(t('common.delSuccess'))
|
|
||||||
// 刷新列表
|
|
||||||
await getList()
|
|
||||||
} catch {}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 详情操作 */
|
|
||||||
const detailVisible = ref(false)
|
|
||||||
const detailData = ref({
|
|
||||||
rule: [],
|
|
||||||
option: {}
|
|
||||||
})
|
|
||||||
const openDetail = async (rowId: number) => {
|
|
||||||
// 设置表单
|
|
||||||
const data = await FormApi.getForm(rowId)
|
|
||||||
setConfAndFields2(detailData, data.conf, data.fields)
|
|
||||||
// 弹窗打开
|
|
||||||
detailVisible.value = true
|
|
||||||
}
|
|
||||||
/**表单保存返回后重新加载列表 */
|
|
||||||
watch(
|
|
||||||
() => currentRoute.value,
|
|
||||||
() => {
|
|
||||||
getList()
|
|
||||||
},
|
|
||||||
{
|
|
||||||
immediate: true
|
|
||||||
}
|
|
||||||
)
|
|
||||||
/** 初始化 **/
|
|
||||||
onMounted(() => {
|
|
||||||
getList()
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,132 +0,0 @@
|
||||||
<template>
|
|
||||||
<Dialog v-model="dialogVisible" :title="dialogTitle">
|
|
||||||
<el-form
|
|
||||||
ref="formRef"
|
|
||||||
v-loading="formLoading"
|
|
||||||
:model="formData"
|
|
||||||
:rules="formRules"
|
|
||||||
label-width="100px"
|
|
||||||
>
|
|
||||||
<el-form-item label="组名" prop="name">
|
|
||||||
<el-input v-model="formData.name" placeholder="请输入组名" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="描述">
|
|
||||||
<el-input v-model="formData.description" placeholder="请输入描述" type="textarea" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="成员" prop="userIds">
|
|
||||||
<el-select v-model="formData.userIds" 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-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>
|
|
||||||
<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 { CommonStatusEnum } from '@/utils/constants'
|
|
||||||
import * as UserGroupApi from '@/api/bpm/userGroup'
|
|
||||||
import * as UserApi from '@/api/system/user'
|
|
||||||
|
|
||||||
defineOptions({ name: 'UserGroupForm' })
|
|
||||||
|
|
||||||
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,
|
|
||||||
description: undefined,
|
|
||||||
userIds: undefined,
|
|
||||||
status: CommonStatusEnum.ENABLE
|
|
||||||
})
|
|
||||||
const formRules = reactive({
|
|
||||||
name: [{ required: true, message: '组名不能为空', trigger: 'blur' }],
|
|
||||||
description: [{ required: true, message: '描述不能为空', trigger: 'blur' }],
|
|
||||||
userIds: [{ required: true, message: '成员不能为空', trigger: 'blur' }],
|
|
||||||
status: [{ required: true, message: '状态不能为空', trigger: 'blur' }]
|
|
||||||
})
|
|
||||||
const formRef = ref() // 表单 Ref
|
|
||||||
const userList = ref<any[]>([]) // 用户列表
|
|
||||||
|
|
||||||
/** 打开弹窗 */
|
|
||||||
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 UserGroupApi.getUserGroup(id)
|
|
||||||
} finally {
|
|
||||||
formLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 加载用户列表
|
|
||||||
userList.value = await UserApi.getSimpleUserList()
|
|
||||||
}
|
|
||||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
|
||||||
|
|
||||||
/** 提交表单 */
|
|
||||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
|
||||||
const submitForm = async () => {
|
|
||||||
// 校验表单
|
|
||||||
if (!formRef) return
|
|
||||||
const valid = await formRef.value.validate()
|
|
||||||
if (!valid) return
|
|
||||||
// 提交请求
|
|
||||||
formLoading.value = true
|
|
||||||
try {
|
|
||||||
const data = formData.value as unknown as UserGroupApi.UserGroupVO
|
|
||||||
if (formType.value === 'create') {
|
|
||||||
await UserGroupApi.createUserGroup(data)
|
|
||||||
message.success(t('common.createSuccess'))
|
|
||||||
} else {
|
|
||||||
await UserGroupApi.updateUserGroup(data)
|
|
||||||
message.success(t('common.updateSuccess'))
|
|
||||||
}
|
|
||||||
dialogVisible.value = false
|
|
||||||
// 发送操作成功的事件
|
|
||||||
emit('success')
|
|
||||||
} finally {
|
|
||||||
formLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 重置表单 */
|
|
||||||
const resetForm = () => {
|
|
||||||
formData.value = {
|
|
||||||
id: undefined,
|
|
||||||
name: undefined,
|
|
||||||
description: undefined,
|
|
||||||
userIds: undefined,
|
|
||||||
status: CommonStatusEnum.ENABLE
|
|
||||||
}
|
|
||||||
formRef.value?.resetFields()
|
|
||||||
}
|
|
||||||
</script>
|
|
|
@ -1,191 +0,0 @@
|
||||||
<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="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:user-group:create']"
|
|
||||||
>
|
|
||||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
|
||||||
</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</ContentWrap>
|
|
||||||
|
|
||||||
<!-- 列表 -->
|
|
||||||
<ContentWrap>
|
|
||||||
<el-table v-loading="loading" :data="list">
|
|
||||||
<el-table-column label="编号" align="center" prop="id" />
|
|
||||||
<el-table-column label="组名" align="center" prop="name" />
|
|
||||||
<el-table-column label="描述" align="center" prop="description" />
|
|
||||||
<el-table-column label="成员" align="center">
|
|
||||||
<template #default="scope">
|
|
||||||
<span v-for="userId in scope.row.userIds" :key="userId" class="pr-5px">
|
|
||||||
{{ userList.find((user) => user.id === userId)?.nickname }}
|
|
||||||
</span>
|
|
||||||
</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="createTime"
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
/>
|
|
||||||
<el-table-column label="操作" align="center">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
link
|
|
||||||
type="primary"
|
|
||||||
@click="openForm('update', scope.row.id)"
|
|
||||||
v-hasPermi="['bpm:user-group:update']"
|
|
||||||
>
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
link
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(scope.row.id)"
|
|
||||||
v-hasPermi="['bpm:user-group: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>
|
|
||||||
|
|
||||||
<!-- 表单弹窗:添加/修改 -->
|
|
||||||
<UserGroupForm ref="formRef" @success="getList" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
|
||||||
import { dateFormatter } from '@/utils/formatTime'
|
|
||||||
import * as UserGroupApi from '@/api/bpm/userGroup'
|
|
||||||
import * as UserApi from '@/api/system/user'
|
|
||||||
import UserGroupForm from './UserGroupForm.vue'
|
|
||||||
import { UserVO } from '@/api/system/user'
|
|
||||||
|
|
||||||
defineOptions({ name: 'BpmUserGroup' })
|
|
||||||
|
|
||||||
const message = useMessage() // 消息弹窗
|
|
||||||
const { t } = useI18n() // 国际化
|
|
||||||
|
|
||||||
const loading = ref(true) // 列表的加载中
|
|
||||||
const total = ref(0) // 列表的总页数
|
|
||||||
const list = ref([]) // 列表的数据
|
|
||||||
const queryParams: any = reactive({
|
|
||||||
pageNo: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
name: null,
|
|
||||||
status: null,
|
|
||||||
createTime: []
|
|
||||||
})
|
|
||||||
const queryFormRef = ref() // 搜索的表单
|
|
||||||
const userList = ref<UserVO[]>([]) // 用户列表
|
|
||||||
|
|
||||||
/** 查询列表 */
|
|
||||||
const getList = async () => {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
const data = await UserGroupApi.getUserGroupPage(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 UserGroupApi.deleteUserGroup(id)
|
|
||||||
message.success(t('common.delSuccess'))
|
|
||||||
// 刷新列表
|
|
||||||
await getList()
|
|
||||||
} catch {}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 初始化 **/
|
|
||||||
onMounted(async () => {
|
|
||||||
await getList()
|
|
||||||
// 加载用户列表
|
|
||||||
userList.value = await UserApi.getSimpleUserList()
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,239 +0,0 @@
|
||||||
<template>
|
|
||||||
<Dialog v-model="dialogVisible" :title="dialogTitle" width="600">
|
|
||||||
<el-form
|
|
||||||
ref="formRef"
|
|
||||||
v-loading="formLoading"
|
|
||||||
:model="formData"
|
|
||||||
:rules="formRules"
|
|
||||||
label-width="110px"
|
|
||||||
>
|
|
||||||
<el-form-item label="流程标识" prop="key">
|
|
||||||
<el-input
|
|
||||||
v-model="formData.key"
|
|
||||||
:disabled="!!formData.id"
|
|
||||||
placeholder="请输入流标标识"
|
|
||||||
style="width: 330px"
|
|
||||||
/>
|
|
||||||
<el-tooltip
|
|
||||||
v-if="!formData.id"
|
|
||||||
class="item"
|
|
||||||
content="新建后,流程标识不可修改!"
|
|
||||||
effect="light"
|
|
||||||
placement="top"
|
|
||||||
>
|
|
||||||
<i class="el-icon-question" style="padding-left: 5px"></i>
|
|
||||||
</el-tooltip>
|
|
||||||
<el-tooltip v-else class="item" content="流程标识不可修改!" effect="light" placement="top">
|
|
||||||
<i class="el-icon-question" style="padding-left: 5px"></i>
|
|
||||||
</el-tooltip>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="流程名称" prop="name">
|
|
||||||
<el-input
|
|
||||||
v-model="formData.name"
|
|
||||||
:disabled="!!formData.id"
|
|
||||||
clearable
|
|
||||||
placeholder="请输入流程名称"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item v-if="formData.id" label="流程分类" prop="category">
|
|
||||||
<el-select
|
|
||||||
v-model="formData.category"
|
|
||||||
clearable
|
|
||||||
placeholder="请选择流程分类"
|
|
||||||
style="width: 100%"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
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>
|
|
||||||
<div v-if="formData.id">
|
|
||||||
<el-form-item label="表单类型" prop="formType">
|
|
||||||
<el-radio-group v-model="formData.formType">
|
|
||||||
<el-radio
|
|
||||||
v-for="dict in getIntDictOptions(DICT_TYPE.BPM_MODEL_FORM_TYPE)"
|
|
||||||
:key="dict.value"
|
|
||||||
:label="dict.value"
|
|
||||||
>
|
|
||||||
{{ dict.label }}
|
|
||||||
</el-radio>
|
|
||||||
</el-radio-group>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item v-if="formData.formType === 10" label="流程表单" prop="formId">
|
|
||||||
<el-select v-model="formData.formId" clearable style="width: 100%">
|
|
||||||
<el-option
|
|
||||||
v-for="form in formList"
|
|
||||||
:key="form.id"
|
|
||||||
:label="form.name"
|
|
||||||
:value="form.id"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item
|
|
||||||
v-if="formData.formType === 20"
|
|
||||||
label="表单提交路由"
|
|
||||||
prop="formCustomCreatePath"
|
|
||||||
>
|
|
||||||
<el-input
|
|
||||||
v-model="formData.formCustomCreatePath"
|
|
||||||
placeholder="请输入表单提交路由"
|
|
||||||
style="width: 330px"
|
|
||||||
/>
|
|
||||||
<el-tooltip
|
|
||||||
class="item"
|
|
||||||
content="自定义表单的提交路径,使用 Vue 的路由地址,例如说:bpm/oa/leave/create"
|
|
||||||
effect="light"
|
|
||||||
placement="top"
|
|
||||||
>
|
|
||||||
<i class="el-icon-question" style="padding-left: 5px"></i>
|
|
||||||
</el-tooltip>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item
|
|
||||||
v-if="formData.formType === 20"
|
|
||||||
label="表单查看地址"
|
|
||||||
prop="formCustomViewPath"
|
|
||||||
>
|
|
||||||
<el-input
|
|
||||||
v-model="formData.formCustomViewPath"
|
|
||||||
placeholder="请输入表单查看的组件地址"
|
|
||||||
style="width: 330px"
|
|
||||||
/>
|
|
||||||
<el-tooltip
|
|
||||||
class="item"
|
|
||||||
content="自定义表单的查看组件地址,使用 Vue 的组件地址,例如说:bpm/oa/leave/detail"
|
|
||||||
effect="light"
|
|
||||||
placement="top"
|
|
||||||
>
|
|
||||||
<i class="el-icon-question" style="padding-left: 5px"></i>
|
|
||||||
</el-tooltip>
|
|
||||||
</el-form-item>
|
|
||||||
</div>
|
|
||||||
</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 { 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' })
|
|
||||||
|
|
||||||
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({
|
|
||||||
formType: 10,
|
|
||||||
name: '',
|
|
||||||
category: undefined,
|
|
||||||
icon: undefined,
|
|
||||||
description: '',
|
|
||||||
formId: '',
|
|
||||||
formCustomCreatePath: '',
|
|
||||||
formCustomViewPath: ''
|
|
||||||
})
|
|
||||||
const formRules = reactive({
|
|
||||||
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) => {
|
|
||||||
dialogVisible.value = true
|
|
||||||
dialogTitle.value = t('action.' + type)
|
|
||||||
formType.value = type
|
|
||||||
resetForm()
|
|
||||||
// 修改时,设置数据
|
|
||||||
if (id) {
|
|
||||||
formLoading.value = true
|
|
||||||
try {
|
|
||||||
formData.value = await ModelApi.getModel(id)
|
|
||||||
} finally {
|
|
||||||
formLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 获得流程表单的下拉框的数据
|
|
||||||
formList.value = await FormApi.getFormSimpleList()
|
|
||||||
// 查询流程分类列表
|
|
||||||
categoryList.value = await CategoryApi.getCategorySimpleList()
|
|
||||||
}
|
|
||||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
|
||||||
|
|
||||||
/** 提交表单 */
|
|
||||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
|
||||||
const submitForm = async () => {
|
|
||||||
// 校验表单
|
|
||||||
if (!formRef) return
|
|
||||||
const valid = await formRef.value.validate()
|
|
||||||
if (!valid) return
|
|
||||||
// 提交请求
|
|
||||||
formLoading.value = true
|
|
||||||
try {
|
|
||||||
const data = formData.value as unknown as ModelApi.ModelVO
|
|
||||||
if (formType.value === 'create') {
|
|
||||||
await ModelApi.createModel(data)
|
|
||||||
// 提示,引导用户做后续的操作
|
|
||||||
await ElMessageBox.alert(
|
|
||||||
'<strong>新建模型成功!</strong>后续需要执行如下 3 个步骤:' +
|
|
||||||
'<div>1. 点击【修改流程】按钮,配置流程的分类、表单信息</div>' +
|
|
||||||
'<div>2. 点击【设计流程】按钮,绘制流程图</div>' +
|
|
||||||
'<div>3. 点击【发布流程】按钮,完成流程的最终发布</div>' +
|
|
||||||
'另外,每次流程修改后,都需要点击【发布流程】按钮,才能正式生效!!!',
|
|
||||||
'重要提示',
|
|
||||||
{
|
|
||||||
dangerouslyUseHTMLString: true,
|
|
||||||
type: 'success'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
await ModelApi.updateModel(data)
|
|
||||||
message.success(t('common.updateSuccess'))
|
|
||||||
}
|
|
||||||
dialogVisible.value = false
|
|
||||||
// 发送操作成功的事件
|
|
||||||
emit('success')
|
|
||||||
} finally {
|
|
||||||
formLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 重置表单 */
|
|
||||||
const resetForm = () => {
|
|
||||||
formData.value = {
|
|
||||||
formType: 10,
|
|
||||||
name: '',
|
|
||||||
category: undefined,
|
|
||||||
icon: '',
|
|
||||||
description: '',
|
|
||||||
formId: '',
|
|
||||||
formCustomCreatePath: '',
|
|
||||||
formCustomViewPath: ''
|
|
||||||
}
|
|
||||||
formRef.value?.resetFields()
|
|
||||||
}
|
|
||||||
</script>
|
|
|
@ -1,141 +0,0 @@
|
||||||
<template>
|
|
||||||
<Dialog v-model="dialogVisible" title="导入流程" width="400">
|
|
||||||
<div>
|
|
||||||
<el-upload
|
|
||||||
ref="uploadRef"
|
|
||||||
v-model:file-list="fileList"
|
|
||||||
:action="importUrl"
|
|
||||||
:auto-upload="false"
|
|
||||||
:data="formData"
|
|
||||||
:disabled="formLoading"
|
|
||||||
:headers="uploadHeaders"
|
|
||||||
:limit="1"
|
|
||||||
:on-error="submitFormError"
|
|
||||||
:on-exceed="handleExceed"
|
|
||||||
:on-success="submitFormSuccess"
|
|
||||||
accept=".bpmn, .xml"
|
|
||||||
drag
|
|
||||||
name="bpmnFile"
|
|
||||||
>
|
|
||||||
<Icon class="el-icon--upload" icon="ep:upload-filled" />
|
|
||||||
<div class="el-upload__text"> 将文件拖到此处,或 <em>点击上传</em></div>
|
|
||||||
<template #tip>
|
|
||||||
<div class="el-upload__tip" style="color: red">
|
|
||||||
提示:仅允许导入“bpm”或“xml”格式文件!
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="120px">
|
|
||||||
<el-form-item label="流程标识" prop="key">
|
|
||||||
<el-input
|
|
||||||
v-model="formData.key"
|
|
||||||
placeholder="请输入流标标识"
|
|
||||||
style="width: 250px"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="流程名称" prop="name">
|
|
||||||
<el-input v-model="formData.name" clearable placeholder="请输入流程名称" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="流程描述" prop="description">
|
|
||||||
<el-input v-model="formData.description" clearable type="textarea" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</el-upload>
|
|
||||||
</div>
|
|
||||||
<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 { getAccessToken, getTenantId } from '@/utils/auth'
|
|
||||||
|
|
||||||
defineOptions({ name: 'ModelImportForm' })
|
|
||||||
|
|
||||||
const message = useMessage() // 消息弹窗
|
|
||||||
|
|
||||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
|
||||||
const formLoading = ref(false) // 表单的加载中
|
|
||||||
const formData = ref({
|
|
||||||
key: '',
|
|
||||||
name: '',
|
|
||||||
description: ''
|
|
||||||
})
|
|
||||||
const formRules = reactive({
|
|
||||||
key: [{ required: true, message: '流程标识不能为空', trigger: 'blur' }],
|
|
||||||
name: [{ required: true, message: '流程名称不能为空', trigger: 'blur' }]
|
|
||||||
})
|
|
||||||
const formRef = ref() // 表单 Ref
|
|
||||||
const uploadRef = ref() // 上传 Ref
|
|
||||||
const importUrl = import.meta.env.VITE_BASE_URL + import.meta.env.VITE_API_URL + '/bpm/model/import'
|
|
||||||
const uploadHeaders = ref() // 上传 Header 头
|
|
||||||
const fileList = ref([]) // 文件列表
|
|
||||||
|
|
||||||
/** 打开弹窗 */
|
|
||||||
const open = async () => {
|
|
||||||
dialogVisible.value = true
|
|
||||||
resetForm()
|
|
||||||
}
|
|
||||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
|
||||||
|
|
||||||
/** 提交表单 */
|
|
||||||
const submitForm = async () => {
|
|
||||||
// 校验表单
|
|
||||||
if (!formRef) return
|
|
||||||
const valid = await formRef.value.validate()
|
|
||||||
if (!valid) return
|
|
||||||
if (fileList.value.length == 0) {
|
|
||||||
message.error('请上传文件')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 提交请求
|
|
||||||
uploadHeaders.value = {
|
|
||||||
Authorization: 'Bearer ' + getAccessToken(),
|
|
||||||
'tenant-id': getTenantId()
|
|
||||||
}
|
|
||||||
formLoading.value = true
|
|
||||||
uploadRef.value!.submit()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 文件上传成功 */
|
|
||||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
|
||||||
const submitFormSuccess = async (response: any) => {
|
|
||||||
if (response.code !== 0) {
|
|
||||||
message.error(response.msg)
|
|
||||||
formLoading.value = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 提示成功
|
|
||||||
message.success('导入流程成功!请点击【设计流程】按钮,进行编辑保存后,才可以进行【发布流程】')
|
|
||||||
dialogVisible.value = false
|
|
||||||
// 发送操作成功的事件
|
|
||||||
emit('success')
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 上传错误提示 */
|
|
||||||
const submitFormError = (): void => {
|
|
||||||
message.error('导入流程失败,请您重新上传!')
|
|
||||||
formLoading.value = false
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 重置表单 */
|
|
||||||
const resetForm = () => {
|
|
||||||
// 重置上传状态和文件
|
|
||||||
formLoading.value = false
|
|
||||||
uploadRef.value?.clearFiles()
|
|
||||||
// 重置表单
|
|
||||||
formData.value = {
|
|
||||||
key: '',
|
|
||||||
name: '',
|
|
||||||
description: ''
|
|
||||||
}
|
|
||||||
formRef.value?.resetFields()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 文件数超出提示 */
|
|
||||||
const handleExceed = (): void => {
|
|
||||||
message.error('最多只能上传一个文件!')
|
|
||||||
}
|
|
||||||
</script>
|
|
|
@ -1,115 +0,0 @@
|
||||||
<template>
|
|
||||||
<ContentWrap>
|
|
||||||
<!-- 流程设计器,负责绘制流程等 -->
|
|
||||||
<MyProcessDesigner
|
|
||||||
key="designer"
|
|
||||||
v-if="xmlString !== undefined"
|
|
||||||
v-model="xmlString"
|
|
||||||
:value="xmlString"
|
|
||||||
v-bind="controlForm"
|
|
||||||
keyboard
|
|
||||||
ref="processDesigner"
|
|
||||||
@init-finished="initModeler"
|
|
||||||
:additionalModel="controlForm.additionalModel"
|
|
||||||
@save="save"
|
|
||||||
/>
|
|
||||||
<!-- 流程属性器,负责编辑每个流程节点的属性 -->
|
|
||||||
<MyProcessPenal
|
|
||||||
key="penal"
|
|
||||||
:bpmnModeler="modeler as any"
|
|
||||||
:prefix="controlForm.prefix"
|
|
||||||
class="process-panel"
|
|
||||||
:model="model"
|
|
||||||
/>
|
|
||||||
</ContentWrap>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { MyProcessDesigner, MyProcessPenal } from '@/components/bpmnProcessDesigner/package'
|
|
||||||
// 自定义元素选中时的弹出菜单(修改 默认任务 为 用户任务)
|
|
||||||
import CustomContentPadProvider from '@/components/bpmnProcessDesigner/package/designer/plugins/content-pad'
|
|
||||||
// 自定义左侧菜单(修改 默认任务 为 用户任务)
|
|
||||||
import CustomPaletteProvider from '@/components/bpmnProcessDesigner/package/designer/plugins/palette'
|
|
||||||
import * as ModelApi from '@/api/bpm/model'
|
|
||||||
|
|
||||||
defineOptions({ name: 'BpmModelEditor' })
|
|
||||||
|
|
||||||
const router = useRouter() // 路由
|
|
||||||
const { query } = useRoute() // 路由的查询
|
|
||||||
const message = useMessage() // 国际化
|
|
||||||
|
|
||||||
const xmlString = ref(undefined) // BPMN XML
|
|
||||||
const modeler = ref(null) // BPMN Modeler
|
|
||||||
const controlForm = ref({
|
|
||||||
simulation: true,
|
|
||||||
labelEditing: false,
|
|
||||||
labelVisible: false,
|
|
||||||
prefix: 'flowable',
|
|
||||||
headerButtonSize: 'mini',
|
|
||||||
additionalModel: [CustomContentPadProvider, CustomPaletteProvider]
|
|
||||||
})
|
|
||||||
const model = ref<ModelApi.ModelVO>() // 流程模型的信息
|
|
||||||
|
|
||||||
/** 初始化 modeler */
|
|
||||||
const initModeler = (item) => {
|
|
||||||
setTimeout(() => {
|
|
||||||
modeler.value = item
|
|
||||||
}, 10)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 添加/修改模型 */
|
|
||||||
const save = async (bpmnXml) => {
|
|
||||||
const data = {
|
|
||||||
...model.value,
|
|
||||||
bpmnXml: bpmnXml // bpmnXml 只是初始化流程图,后续修改无法通过它获得
|
|
||||||
} as unknown as ModelApi.ModelVO
|
|
||||||
// 提交
|
|
||||||
if (data.id) {
|
|
||||||
await ModelApi.updateModel(data)
|
|
||||||
message.success('修改成功')
|
|
||||||
} else {
|
|
||||||
await ModelApi.createModel(data)
|
|
||||||
message.success('新增成功')
|
|
||||||
}
|
|
||||||
// 跳转回去
|
|
||||||
close()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 关闭按钮 */
|
|
||||||
const close = () => {
|
|
||||||
router.push({ path: '/bpm/manager/model' })
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 初始化 */
|
|
||||||
onMounted(async () => {
|
|
||||||
const modelId = query.modelId as unknown as number
|
|
||||||
if (!modelId) {
|
|
||||||
message.error('缺少模型 modelId 编号')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 查询模型
|
|
||||||
const data = await ModelApi.getModel(modelId)
|
|
||||||
if (!data.bpmnXml) {
|
|
||||||
// 首次创建的 Model 模型,它是没有 bpmnXml,此时需要给它一个默认的
|
|
||||||
data.bpmnXml = ` <?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.activiti.org/processdef">
|
|
||||||
<process id="${data.key}" name="${data.name}" isExecutable="true" />
|
|
||||||
<bpmndi:BPMNDiagram id="BPMNDiagram">
|
|
||||||
<bpmndi:BPMNPlane id="${data.key}_di" bpmnElement="${data.key}" />
|
|
||||||
</bpmndi:BPMNDiagram>
|
|
||||||
</definitions>`
|
|
||||||
}
|
|
||||||
model.value = {
|
|
||||||
...data,
|
|
||||||
bpmnXml: undefined // 清空 bpmnXml 属性
|
|
||||||
}
|
|
||||||
xmlString.value = data.bpmnXml
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
<style lang="scss">
|
|
||||||
.process-panel__container {
|
|
||||||
position: absolute;
|
|
||||||
top: 90px;
|
|
||||||
right: 60px;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,415 +0,0 @@
|
||||||
<template>
|
|
||||||
<!-- <doc-alert title="流程设计器(BPMN)" url="https://doc.iocoder.cn/bpm/model-designer-dingding/" />
|
|
||||||
<doc-alert
|
|
||||||
title="流程设计器(钉钉、飞书)"
|
|
||||||
url="https://doc.iocoder.cn/bpm/model-designer-bpmn/"
|
|
||||||
/>
|
|
||||||
<doc-alert title="选择审批人、发起人自选" url="https://doc.iocoder.cn/bpm/assignee/" />
|
|
||||||
<doc-alert title="会签、或签、依次审批" url="https://doc.iocoder.cn/bpm/multi-instance/" /> -->
|
|
||||||
|
|
||||||
<ContentWrap>
|
|
||||||
<!-- 搜索工作栏 -->
|
|
||||||
<el-form
|
|
||||||
class="-mb-15px"
|
|
||||||
:model="queryParams"
|
|
||||||
ref="queryFormRef"
|
|
||||||
:inline="true"
|
|
||||||
label-width="68px"
|
|
||||||
>
|
|
||||||
<el-form-item label="流程标识" prop="key">
|
|
||||||
<el-input
|
|
||||||
v-model="queryParams.key"
|
|
||||||
placeholder="请输入流程标识"
|
|
||||||
clearable
|
|
||||||
@keyup.enter="handleQuery"
|
|
||||||
class="!w-240px"
|
|
||||||
/>
|
|
||||||
</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="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>
|
|
||||||
<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:model:create']"
|
|
||||||
>
|
|
||||||
<Icon icon="ep:plus" class="mr-5px" /> 新建流程
|
|
||||||
</el-button>
|
|
||||||
<el-button type="success" plain @click="openImportForm" v-hasPermi="['bpm:model:import']">
|
|
||||||
<Icon icon="ep:upload" class="mr-5px" /> 导入流程
|
|
||||||
</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</ContentWrap>
|
|
||||||
|
|
||||||
<!-- 列表 -->
|
|
||||||
<ContentWrap>
|
|
||||||
<el-table v-loading="loading" :data="list">
|
|
||||||
<el-table-column label="流程标识" align="center" prop="key" width="200" />
|
|
||||||
<el-table-column label="流程名称" align="center" prop="name" width="200">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button type="primary" link @click="handleBpmnDetail(scope.row)">
|
|
||||||
<span>{{ scope.row.name }}</span>
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="流程图标" align="center" prop="icon" width="100">
|
|
||||||
<template #default="scope">
|
|
||||||
<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
|
|
||||||
v-if="scope.row.formType === 10"
|
|
||||||
type="primary"
|
|
||||||
link
|
|
||||||
@click="handleFormDetail(scope.row)"
|
|
||||||
>
|
|
||||||
<span>{{ scope.row.formName }}</span>
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
v-else-if="scope.row.formType === 20"
|
|
||||||
type="primary"
|
|
||||||
link
|
|
||||||
@click="handleFormDetail(scope.row)"
|
|
||||||
>
|
|
||||||
<span>{{ scope.row.formCustomCreatePath }}</span>
|
|
||||||
</el-button>
|
|
||||||
<label v-else>暂无表单</label>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
label="创建时间"
|
|
||||||
align="center"
|
|
||||||
prop="createTime"
|
|
||||||
width="180"
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
/>
|
|
||||||
<el-table-column label="最新部署的流程定义" align="center">
|
|
||||||
<el-table-column
|
|
||||||
label="流程版本"
|
|
||||||
align="center"
|
|
||||||
prop="processDefinition.version"
|
|
||||||
width="100"
|
|
||||||
>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-tag v-if="scope.row.processDefinition">
|
|
||||||
v{{ scope.row.processDefinition.version }}
|
|
||||||
</el-tag>
|
|
||||||
<el-tag v-else type="warning">未部署</el-tag>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
label="激活状态"
|
|
||||||
align="center"
|
|
||||||
prop="processDefinition.version"
|
|
||||||
width="85"
|
|
||||||
>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-switch
|
|
||||||
v-if="scope.row.processDefinition"
|
|
||||||
v-model="scope.row.processDefinition.suspensionState"
|
|
||||||
:active-value="1"
|
|
||||||
:inactive-value="2"
|
|
||||||
@change="handleChangeState(scope.row)"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="部署时间" align="center" prop="deploymentTime" width="180">
|
|
||||||
<template #default="scope">
|
|
||||||
<span v-if="scope.row.processDefinition">
|
|
||||||
{{ formatDate(scope.row.processDefinition.deploymentTime) }}
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="操作" align="center" width="240" fixed="right">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
link
|
|
||||||
type="primary"
|
|
||||||
@click="openForm('update', scope.row.id)"
|
|
||||||
v-hasPermi="['bpm:model:update']"
|
|
||||||
>
|
|
||||||
修改流程
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
link
|
|
||||||
type="primary"
|
|
||||||
@click="handleDesign(scope.row)"
|
|
||||||
v-hasPermi="['bpm:model:update']"
|
|
||||||
>
|
|
||||||
设计流程
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
link
|
|
||||||
type="primary"
|
|
||||||
@click="handleSimpleDesign(scope.row.id)"
|
|
||||||
v-hasPermi="['bpm:model:update']"
|
|
||||||
>
|
|
||||||
仿钉钉设计流程
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
link
|
|
||||||
type="primary"
|
|
||||||
@click="handleDeploy(scope.row)"
|
|
||||||
v-hasPermi="['bpm:model:deploy']"
|
|
||||||
>
|
|
||||||
发布流程
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
link
|
|
||||||
type="primary"
|
|
||||||
v-hasPermi="['bpm:process-definition:query']"
|
|
||||||
@click="handleDefinitionList(scope.row)"
|
|
||||||
>
|
|
||||||
流程定义
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
link
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(scope.row.id)"
|
|
||||||
v-hasPermi="['bpm:model: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>
|
|
||||||
|
|
||||||
<!-- 表单弹窗:添加/修改流程 -->
|
|
||||||
<ModelForm ref="formRef" @success="getList" />
|
|
||||||
|
|
||||||
<!-- 表单弹窗:导入流程 -->
|
|
||||||
<ModelImportForm ref="importFormRef" @success="getList" />
|
|
||||||
|
|
||||||
<!-- 弹窗:表单详情 -->
|
|
||||||
<Dialog title="表单详情" v-model="formDetailVisible" width="800">
|
|
||||||
<form-create :rule="formDetailPreview.rule" :option="formDetailPreview.option" />
|
|
||||||
</Dialog>
|
|
||||||
|
|
||||||
<!-- 弹窗:流程模型图的预览 -->
|
|
||||||
<Dialog title="流程图" v-model="bpmnDetailVisible" width="800">
|
|
||||||
<MyProcessViewer
|
|
||||||
key="designer"
|
|
||||||
v-model="bpmnXML"
|
|
||||||
:value="bpmnXML as any"
|
|
||||||
v-bind="bpmnControlForm"
|
|
||||||
:prefix="bpmnControlForm.prefix"
|
|
||||||
/>
|
|
||||||
</Dialog>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { dateFormatter, formatDate } from '@/utils/formatTime'
|
|
||||||
import { MyProcessViewer } from '@/components/bpmnProcessDesigner/package'
|
|
||||||
import * as ModelApi from '@/api/bpm/model'
|
|
||||||
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' })
|
|
||||||
|
|
||||||
const message = useMessage() // 消息弹窗
|
|
||||||
const { t } = useI18n() // 国际化
|
|
||||||
const { push } = useRouter() // 路由
|
|
||||||
|
|
||||||
const loading = ref(true) // 列表的加载中
|
|
||||||
const total = ref(0) // 列表的总页数
|
|
||||||
const list = ref([]) // 列表的数据
|
|
||||||
const queryParams = reactive({
|
|
||||||
pageNo: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
key: undefined,
|
|
||||||
name: undefined,
|
|
||||||
category: undefined
|
|
||||||
})
|
|
||||||
const queryFormRef = ref() // 搜索的表单
|
|
||||||
const categoryList: any = ref([]) // 流程分类列表
|
|
||||||
|
|
||||||
/** 查询列表 */
|
|
||||||
const getList = async () => {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
const data = await ModelApi.getModelPage(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 importFormRef = ref()
|
|
||||||
const openImportForm = () => {
|
|
||||||
importFormRef.value.open()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 删除按钮操作 */
|
|
||||||
const handleDelete = async (id: number) => {
|
|
||||||
try {
|
|
||||||
// 删除的二次确认
|
|
||||||
await message.delConfirm()
|
|
||||||
// 发起删除
|
|
||||||
await ModelApi.deleteModel(id)
|
|
||||||
message.success(t('common.delSuccess'))
|
|
||||||
// 刷新列表
|
|
||||||
await getList()
|
|
||||||
} catch {}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 更新状态操作 */
|
|
||||||
const handleChangeState = async (row) => {
|
|
||||||
const state = row.processDefinition.suspensionState
|
|
||||||
try {
|
|
||||||
// 修改状态的二次确认
|
|
||||||
const id = row.id
|
|
||||||
const statusState = state === 1 ? '激活' : '挂起'
|
|
||||||
const content = '是否确认' + statusState + '流程名字为"' + row.name + '"的数据项?'
|
|
||||||
await message.confirm(content)
|
|
||||||
// 发起修改状态
|
|
||||||
await ModelApi.updateModelState(id, state)
|
|
||||||
// 刷新列表
|
|
||||||
await getList()
|
|
||||||
} catch {
|
|
||||||
// 取消后,进行恢复按钮
|
|
||||||
row.processDefinition.suspensionState = state === 1 ? 2 : 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 设计流程 */
|
|
||||||
const handleDesign = (row) => {
|
|
||||||
push({
|
|
||||||
name: 'BpmModelEditor',
|
|
||||||
query: {
|
|
||||||
modelId: row.id
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleSimpleDesign = (row) => {
|
|
||||||
push({
|
|
||||||
name: 'SimpleWorkflowDesignEditor',
|
|
||||||
query: {
|
|
||||||
modelId: row.id
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 发布流程 */
|
|
||||||
const handleDeploy = async (row) => {
|
|
||||||
try {
|
|
||||||
// 删除的二次确认
|
|
||||||
await message.confirm('是否部署该流程!!')
|
|
||||||
// 发起部署
|
|
||||||
await ModelApi.deployModel(row.id)
|
|
||||||
message.success(t('部署成功'))
|
|
||||||
// 刷新列表
|
|
||||||
await getList()
|
|
||||||
} catch {}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 跳转到指定流程定义列表 */
|
|
||||||
const handleDefinitionList = (row) => {
|
|
||||||
push({
|
|
||||||
name: 'BpmProcessDefinition',
|
|
||||||
query: {
|
|
||||||
key: row.key
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 流程表单的详情按钮操作 */
|
|
||||||
const formDetailVisible = ref(false)
|
|
||||||
const formDetailPreview = ref({
|
|
||||||
rule: [],
|
|
||||||
option: {}
|
|
||||||
})
|
|
||||||
const handleFormDetail = async (row) => {
|
|
||||||
if (row.formType == 10) {
|
|
||||||
// 设置表单
|
|
||||||
const data = await FormApi.getForm(row.formId)
|
|
||||||
setConfAndFields2(formDetailPreview, data.conf, data.fields)
|
|
||||||
// 弹窗打开
|
|
||||||
formDetailVisible.value = true
|
|
||||||
} else {
|
|
||||||
await push({
|
|
||||||
path: row.formCustomCreatePath
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 流程图的详情按钮操作 */
|
|
||||||
const bpmnDetailVisible = ref(false)
|
|
||||||
const bpmnXML = ref(null)
|
|
||||||
const bpmnControlForm = ref({
|
|
||||||
prefix: 'flowable'
|
|
||||||
})
|
|
||||||
const handleBpmnDetail = async (row) => {
|
|
||||||
const data = await ModelApi.getModel(row.id)
|
|
||||||
bpmnXML.value = data.bpmnXml || ''
|
|
||||||
bpmnDetailVisible.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 初始化 **/
|
|
||||||
onMounted(async () => {
|
|
||||||
await getList()
|
|
||||||
// 查询流程分类列表
|
|
||||||
categoryList.value = await CategoryApi.getCategorySimpleList()
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,164 +0,0 @@
|
||||||
<template>
|
|
||||||
<el-form
|
|
||||||
ref="formRef"
|
|
||||||
v-loading="formLoading"
|
|
||||||
:model="formData"
|
|
||||||
:rules="formRules"
|
|
||||||
label-width="80px"
|
|
||||||
>
|
|
||||||
<el-form-item label="请假类型" prop="type">
|
|
||||||
<el-select v-model="formData.type" clearable placeholder="请选择请假类型">
|
|
||||||
<el-option
|
|
||||||
v-for="dict in getIntDictOptions(DICT_TYPE.BPM_OA_LEAVE_TYPE)"
|
|
||||||
:key="dict.value"
|
|
||||||
:label="dict.label"
|
|
||||||
:value="dict.value"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="开始时间" prop="startTime">
|
|
||||||
<el-date-picker
|
|
||||||
v-model="formData.startTime"
|
|
||||||
clearable
|
|
||||||
placeholder="请选择开始时间"
|
|
||||||
type="datetime"
|
|
||||||
value-format="x"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="结束时间" prop="endTime">
|
|
||||||
<el-date-picker
|
|
||||||
v-model="formData.endTime"
|
|
||||||
clearable
|
|
||||||
placeholder="请选择结束时间"
|
|
||||||
type="datetime"
|
|
||||||
value-format="x"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<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>
|
|
||||||
</el-form>
|
|
||||||
</template>
|
|
||||||
<script lang="ts" setup>
|
|
||||||
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,
|
|
||||||
reason: undefined,
|
|
||||||
startTime: undefined,
|
|
||||||
endTime: undefined
|
|
||||||
})
|
|
||||||
const formRules = reactive({
|
|
||||||
type: [{ required: true, message: '请假类型不能为空', trigger: 'blur' }],
|
|
||||||
reason: [{ required: true, message: '请假原因不能为空', trigger: 'change' }],
|
|
||||||
startTime: [{ required: true, message: '请假开始时间不能为空', trigger: 'change' }],
|
|
||||||
endTime: [{ required: true, message: '请假结束时间不能为空', trigger: 'change' }]
|
|
||||||
})
|
|
||||||
const formRef = ref() // 表单 Ref
|
|
||||||
|
|
||||||
// 指定审批人
|
|
||||||
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
|
|
||||||
// 设置指定审批人
|
|
||||||
if (startUserSelectTasks.value?.length > 0) {
|
|
||||||
data.startUserSelectAssignees = startUserSelectAssignees.value
|
|
||||||
}
|
|
||||||
await LeaveApi.createLeave(data)
|
|
||||||
message.success('发起成功')
|
|
||||||
// 关闭当前 Tab
|
|
||||||
delView(unref(currentRoute))
|
|
||||||
await push({ name: 'BpmOALeave' })
|
|
||||||
} finally {
|
|
||||||
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>
|
|
|
@ -1,51 +0,0 @@
|
||||||
<template>
|
|
||||||
<ContentWrap>
|
|
||||||
<el-descriptions :column="1" border>
|
|
||||||
<el-descriptions-item label="请假类型">
|
|
||||||
<dict-tag :type="DICT_TYPE.BPM_OA_LEAVE_TYPE" :value="detailData.type" />
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="开始时间">
|
|
||||||
{{ formatDate(detailData.startTime, 'YYYY-MM-DD') }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="结束时间">
|
|
||||||
{{ formatDate(detailData.endTime, 'YYYY-MM-DD') }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="原因">
|
|
||||||
{{ detailData.reason }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
</el-descriptions>
|
|
||||||
</ContentWrap>
|
|
||||||
</template>
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { DICT_TYPE } from '@/utils/dict'
|
|
||||||
import { formatDate } from '@/utils/formatTime'
|
|
||||||
import { propTypes } from '@/utils/propTypes'
|
|
||||||
import * as LeaveApi from '@/api/bpm/leave'
|
|
||||||
|
|
||||||
defineOptions({ name: 'BpmOALeaveDetail' })
|
|
||||||
|
|
||||||
const { query } = useRoute() // 查询参数
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
id: propTypes.number.def(undefined)
|
|
||||||
})
|
|
||||||
const detailLoading = ref(false) // 表单的加载中
|
|
||||||
const detailData = ref<any>({}) // 详情数据
|
|
||||||
const queryId = query.id as unknown as number // 从 URL 传递过来的 id 编号
|
|
||||||
|
|
||||||
/** 获得数据 */
|
|
||||||
const getInfo = async () => {
|
|
||||||
detailLoading.value = true
|
|
||||||
try {
|
|
||||||
detailData.value = await LeaveApi.getLeave(props.id || queryId)
|
|
||||||
} finally {
|
|
||||||
detailLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
defineExpose({ open: getInfo }) // 提供 open 方法,用于打开弹窗
|
|
||||||
|
|
||||||
/** 初始化 **/
|
|
||||||
onMounted(() => {
|
|
||||||
getInfo()
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,258 +0,0 @@
|
||||||
<template>
|
|
||||||
<!-- <doc-alert title="审批接入(业务表单)" url="https://doc.iocoder.cn/bpm/use-business-form/" /> -->
|
|
||||||
|
|
||||||
<ContentWrap>
|
|
||||||
<!-- 搜索工作栏 -->
|
|
||||||
<el-form
|
|
||||||
ref="queryFormRef"
|
|
||||||
:inline="true"
|
|
||||||
:model="queryParams"
|
|
||||||
class="-mb-15px"
|
|
||||||
label-width="68px"
|
|
||||||
>
|
|
||||||
<el-form-item label="请假类型" prop="type">
|
|
||||||
<el-select
|
|
||||||
v-model="queryParams.type"
|
|
||||||
class="!w-240px"
|
|
||||||
clearable
|
|
||||||
placeholder="请选择请假类型"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="dict in getIntDictOptions(DICT_TYPE.BPM_OA_LEAVE_TYPE)"
|
|
||||||
: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"
|
|
||||||
: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 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_STATUS)"
|
|
||||||
:key="dict.value"
|
|
||||||
:label="dict.label"
|
|
||||||
:value="dict.value"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="原因" prop="reason">
|
|
||||||
<el-input
|
|
||||||
v-model="queryParams.reason"
|
|
||||||
class="!w-240px"
|
|
||||||
clearable
|
|
||||||
placeholder="请输入原因"
|
|
||||||
@keyup.enter="handleQuery"
|
|
||||||
/>
|
|
||||||
</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-button plain type="primary" @click="handleCreate()">
|
|
||||||
<Icon class="mr-5px" icon="ep:plus" />
|
|
||||||
发起请假
|
|
||||||
</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</ContentWrap>
|
|
||||||
|
|
||||||
<!-- 列表 -->
|
|
||||||
<ContentWrap>
|
|
||||||
<el-table v-loading="loading" :data="list">
|
|
||||||
<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_STATUS" :value="scope.row.status" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="开始时间"
|
|
||||||
prop="startTime"
|
|
||||||
width="180"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="结束时间"
|
|
||||||
prop="endTime"
|
|
||||||
width="180"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="请假类型" prop="type">
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.BPM_OA_LEAVE_TYPE" :value="scope.row.type" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="原因" prop="reason" />
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="申请时间"
|
|
||||||
prop="createTime"
|
|
||||||
width="180"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="操作" width="200">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
v-hasPermi="['bpm:oa-leave:query']"
|
|
||||||
link
|
|
||||||
type="primary"
|
|
||||||
@click="handleDetail(scope.row)"
|
|
||||||
>
|
|
||||||
详情
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
v-hasPermi="['bpm:oa-leave:query']"
|
|
||||||
link
|
|
||||||
type="primary"
|
|
||||||
@click="handleProcessDetail(scope.row)"
|
|
||||||
>
|
|
||||||
进度
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
v-if="scope.row.result === 1"
|
|
||||||
v-hasPermi="['bpm:oa-leave:create']"
|
|
||||||
link
|
|
||||||
type="danger"
|
|
||||||
@click="cancelLeave(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, getIntDictOptions } from '@/utils/dict'
|
|
||||||
import { dateFormatter } from '@/utils/formatTime'
|
|
||||||
import * as LeaveApi from '@/api/bpm/leave'
|
|
||||||
import * as ProcessInstanceApi from '@/api/bpm/processInstance'
|
|
||||||
|
|
||||||
defineOptions({ name: 'BpmOALeave' })
|
|
||||||
|
|
||||||
const message = useMessage() // 消息弹窗
|
|
||||||
const router = useRouter() // 路由
|
|
||||||
const { t } = useI18n() // 国际化
|
|
||||||
|
|
||||||
const loading = ref(true) // 列表的加载中
|
|
||||||
const total = ref(0) // 列表的总页数
|
|
||||||
const list = ref([]) // 列表的数据
|
|
||||||
const queryParams: any = reactive({
|
|
||||||
pageNo: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
type: undefined,
|
|
||||||
status: undefined,
|
|
||||||
reason: undefined,
|
|
||||||
createTime: []
|
|
||||||
})
|
|
||||||
const queryFormRef = ref() // 搜索的表单
|
|
||||||
|
|
||||||
/** 查询列表 */
|
|
||||||
const getList = async () => {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
const data = await LeaveApi.getLeavePage(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 handleCreate = () => {
|
|
||||||
router.push({ name: 'OALeaveCreate' })
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 详情操作 */
|
|
||||||
const handleDetail = (row: LeaveApi.LeaveVO) => {
|
|
||||||
router.push({
|
|
||||||
name: 'OALeaveDetail',
|
|
||||||
query: {
|
|
||||||
id: row.id
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 取消请假操作 */
|
|
||||||
const cancelLeave = async (row) => {
|
|
||||||
// 二次确认
|
|
||||||
// @ts-expect-error
|
|
||||||
const { value } = await ElMessageBox.prompt('请输入取消原因', '取消流程', {
|
|
||||||
confirmButtonText: t('common.ok'),
|
|
||||||
cancelButtonText: t('common.cancel'),
|
|
||||||
inputPattern: /^[\s\S]*.*\S[\s\S]*$/, // 判断非空,且非空格
|
|
||||||
inputErrorMessage: '取消原因不能为空'
|
|
||||||
})
|
|
||||||
// 发起取消
|
|
||||||
await ProcessInstanceApi.cancelProcessInstanceByStartUser(row.id, value)
|
|
||||||
message.success('取消成功')
|
|
||||||
// 刷新列表
|
|
||||||
await getList()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 审批进度 */
|
|
||||||
const handleProcessDetail = (row) => {
|
|
||||||
router.push({
|
|
||||||
name: 'BpmProcessInstanceDetail',
|
|
||||||
query: {
|
|
||||||
id: row.processInstanceId
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// fix: 列表不刷新的问题。
|
|
||||||
watch(
|
|
||||||
() => router.currentRoute.value,
|
|
||||||
() => {
|
|
||||||
getList()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
/** 初始化 **/
|
|
||||||
onMounted(() => {
|
|
||||||
getList()
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,114 +0,0 @@
|
||||||
<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>
|
|
|
@ -1,182 +0,0 @@
|
||||||
<template>
|
|
||||||
<!-- <doc-alert title="流程表达式" url="https://doc.iocoder.cn/bpm/expression/" /> -->
|
|
||||||
|
|
||||||
<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: any = 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,258 +0,0 @@
|
||||||
<template>
|
|
||||||
<!-- <doc-alert title="流程发起、取消、重新发起" url="https://doc.iocoder.cn/bpm/process-instance/" /> -->
|
|
||||||
|
|
||||||
<!-- 第一步,通过流程定义的列表,选择对应的流程 -->
|
|
||||||
<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">申请信息【{{ selectProcessDefinition.name }}】</span>
|
|
||||||
<el-button style="float: right" type="primary" @click="selectProcessDefinition = undefined">
|
|
||||||
<Icon icon="ep:delete" /> 选择其它流程
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
<el-col :span="16" :offset="6" style="margin-top: 20px">
|
|
||||||
<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>
|
|
||||||
<!-- 流程图预览 -->
|
|
||||||
<ProcessInstanceBpmnViewer :bpmn-xml="bpmnXML as any" />
|
|
||||||
</ContentWrap>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
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 route = useRoute() // 路由
|
|
||||||
const { push, currentRoute } = useRouter() // 路由
|
|
||||||
const message = useMessage() // 消息
|
|
||||||
const { delView } = useTagsViewStore() // 视图操作
|
|
||||||
|
|
||||||
const processInstanceId: any = route.query.processInstanceId
|
|
||||||
const loading = ref(true) // 加载中
|
|
||||||
const categoryList:any = ref([]) // 分类的列表
|
|
||||||
const categoryActive = ref('') // 选中的分类
|
|
||||||
const processDefinitionList = ref([]) // 流程定义的列表
|
|
||||||
|
|
||||||
/** 查询列表 */
|
|
||||||
const getList = async () => {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
// 流程分类
|
|
||||||
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: any) => item.key == processInstance.processDefinition?.key
|
|
||||||
)
|
|
||||||
if (!processDefinition) {
|
|
||||||
message.error('重新发起流程失败,原因:流程定义不存在')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
await handleSelect(processDefinition, processInstance.formVariables)
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 选中分类对应的流程定义列表 */
|
|
||||||
const categoryProcessDefinitionList: any = computed(() => {
|
|
||||||
return processDefinitionList.value.filter((item: any) => item.category == categoryActive.value)
|
|
||||||
})
|
|
||||||
|
|
||||||
// ========== 表单相关 ==========
|
|
||||||
const fApi = ref<ApiAttrs>()
|
|
||||||
const detailForm: any = ref({
|
|
||||||
rule: [],
|
|
||||||
option: {},
|
|
||||||
value: {}
|
|
||||||
}) // 流程表单详情
|
|
||||||
const selectProcessDefinition = ref() // 选择的流程定义
|
|
||||||
|
|
||||||
// 指定审批人
|
|
||||||
const bpmnXML = ref(null) // BPMN 数据
|
|
||||||
const startUserSelectTasks: any = ref([]) // 发起人需要选择审批人的用户任务列表
|
|
||||||
const startUserSelectAssignees = ref({}) // 发起人选择审批人的数据
|
|
||||||
const startUserSelectAssigneesFormRef = ref() // 发起人选择审批人的表单 Ref
|
|
||||||
const startUserSelectAssigneesFormRules = ref({}) // 发起人选择审批人的表单 Rules
|
|
||||||
const userList = ref<any[]>([]) // 用户列表
|
|
||||||
|
|
||||||
/** 处理选择流程的按钮操作 **/
|
|
||||||
const handleSelect = async (row?, formVariables?) => {
|
|
||||||
// 设置选择的流程
|
|
||||||
selectProcessDefinition.value = row
|
|
||||||
|
|
||||||
// 重置指定审批人
|
|
||||||
startUserSelectTasks.value = []
|
|
||||||
startUserSelectAssignees.value = {}
|
|
||||||
startUserSelectAssigneesFormRules.value = {}
|
|
||||||
|
|
||||||
// 情况一:流程表单
|
|
||||||
if (row.formType == 10) {
|
|
||||||
// 设置表单
|
|
||||||
setConfAndFields2(detailForm, row.formConf, row.formFields, formVariables)
|
|
||||||
// 加载流程图
|
|
||||||
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 push({
|
|
||||||
path: row.formCustomCreatePath
|
|
||||||
})
|
|
||||||
// 这里暂时无需加载流程图,因为跳出到另外个 Tab;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 提交按钮 */
|
|
||||||
const submitForm = async (formData) => {
|
|
||||||
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: selectProcessDefinition.value.id,
|
|
||||||
variables: formData,
|
|
||||||
startUserSelectAssignees: startUserSelectAssignees.value
|
|
||||||
})
|
|
||||||
// 提示
|
|
||||||
message.success('发起流程成功')
|
|
||||||
// 跳转回去
|
|
||||||
delView(unref(currentRoute))
|
|
||||||
await push({
|
|
||||||
name: 'BpmProcessInstanceMy'
|
|
||||||
})
|
|
||||||
} finally {
|
|
||||||
fApi.value.btn.loading(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 初始化 */
|
|
||||||
onMounted(() => {
|
|
||||||
getList()
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,54 +0,0 @@
|
||||||
<template>
|
|
||||||
<el-card v-loading="loading" class="box-card">
|
|
||||||
<template #header>
|
|
||||||
<span class="el-icon-picture-outline">流程图</span>
|
|
||||||
</template>
|
|
||||||
<MyProcessViewer
|
|
||||||
key="designer"
|
|
||||||
:activityData="activityList"
|
|
||||||
:prefix="bpmnControlForm.prefix"
|
|
||||||
:processInstanceData="processInstance"
|
|
||||||
:taskData="tasks"
|
|
||||||
:value="bpmnXml"
|
|
||||||
v-bind="bpmnControlForm"
|
|
||||||
/>
|
|
||||||
</el-card>
|
|
||||||
</template>
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { propTypes } from '@/utils/propTypes'
|
|
||||||
import { MyProcessViewer } from '@/components/bpmnProcessDesigner/package'
|
|
||||||
import * as ActivityApi from '@/api/bpm/activity'
|
|
||||||
|
|
||||||
defineOptions({ name: 'BpmProcessInstanceBpmnViewer' })
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
loading: propTypes.bool, // 是否加载中
|
|
||||||
id: propTypes.string, // 流程实例的编号
|
|
||||||
processInstance: propTypes.any, // 流程实例的信息
|
|
||||||
tasks: propTypes.array, // 流程任务的数组
|
|
||||||
bpmnXml: propTypes.string // BPMN XML
|
|
||||||
})
|
|
||||||
|
|
||||||
const bpmnControlForm = ref({
|
|
||||||
prefix: 'flowable'
|
|
||||||
})
|
|
||||||
const activityList = ref([]) // 任务列表
|
|
||||||
|
|
||||||
/** 只有 loading 完成时,才去加载流程列表 */
|
|
||||||
watch(
|
|
||||||
() => props.loading,
|
|
||||||
async (value) => {
|
|
||||||
if (value && props.id) {
|
|
||||||
activityList.value = await ActivityApi.getActivityList({
|
|
||||||
processInstanceId: props.id
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
</script>
|
|
||||||
<style>
|
|
||||||
.box-card {
|
|
||||||
width: 100%;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,175 +0,0 @@
|
||||||
<template>
|
|
||||||
<el-card v-loading="loading" class="box-card">
|
|
||||||
<template #header>
|
|
||||||
<span class="el-icon-picture-outline">审批记录</span>
|
|
||||||
</template>
|
|
||||||
<el-col :offset="3" :span="17">
|
|
||||||
<div class="block">
|
|
||||||
<el-timeline>
|
|
||||||
<el-timeline-item
|
|
||||||
v-if="processInstance.endTime"
|
|
||||||
:type="getProcessInstanceTimelineItemType(processInstance)"
|
|
||||||
>
|
|
||||||
<p style="font-weight: 700">
|
|
||||||
结束流程:在 {{ 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
|
|
||||||
class="ml-10px"
|
|
||||||
v-if="!isEmpty(item.children)"
|
|
||||||
@click="openChildrenTask(item)"
|
|
||||||
size="small"
|
|
||||||
>
|
|
||||||
<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' }">
|
|
||||||
<label v-if="item.assigneeUser" style="margin-right: 30px; font-weight: normal">
|
|
||||||
审批人:{{ item.assigneeUser.nickname }}
|
|
||||||
<el-tag size="small" type="info">{{ item.assigneeUser.deptName }}</el-tag>
|
|
||||||
</label>
|
|
||||||
<label v-if="item.createTime" style="font-weight: normal">创建时间:</label>
|
|
||||||
<label style="font-weight: normal; color: #8a909c">
|
|
||||||
{{ formatDate(item?.createTime) }}
|
|
||||||
</label>
|
|
||||||
<label v-if="item.endTime" style="margin-left: 30px; font-weight: normal">
|
|
||||||
审批时间:
|
|
||||||
</label>
|
|
||||||
<label v-if="item.endTime" style="font-weight: normal; color: #8a909c">
|
|
||||||
{{ formatDate(item?.endTime) }}
|
|
||||||
</label>
|
|
||||||
<label v-if="item.durationInMillis" style="margin-left: 30px; font-weight: normal">
|
|
||||||
耗时:
|
|
||||||
</label>
|
|
||||||
<label v-if="item.durationInMillis" style="font-weight: normal; color: #8a909c">
|
|
||||||
{{ formatPast2(item?.durationInMillis) }}
|
|
||||||
</label>
|
|
||||||
<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>
|
|
||||||
</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 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) // 流程任务的数组
|
|
||||||
})
|
|
||||||
|
|
||||||
/** 获得流程实例对应的颜色 */
|
|
||||||
const getProcessInstanceTimelineItemType = (item: any) => {
|
|
||||||
if (item.status === 2) {
|
|
||||||
return 'success'
|
|
||||||
}
|
|
||||||
if (item.status === 3) {
|
|
||||||
return 'danger'
|
|
||||||
}
|
|
||||||
if (item.status === 4) {
|
|
||||||
return 'warning'
|
|
||||||
}
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 获得任务对应的颜色 */
|
|
||||||
const getTaskTimelineItemType = (item: any) => {
|
|
||||||
if ([0, 1, 6, 7].includes(item.status)) {
|
|
||||||
return 'primary'
|
|
||||||
}
|
|
||||||
if (item.status === 2) {
|
|
||||||
return 'success'
|
|
||||||
}
|
|
||||||
if (item.status === 3) {
|
|
||||||
return 'danger'
|
|
||||||
}
|
|
||||||
if (item.status === 4) {
|
|
||||||
return 'info'
|
|
||||||
}
|
|
||||||
if (item.status === 5) {
|
|
||||||
return 'warning'
|
|
||||||
}
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 子任务 */
|
|
||||||
const taskSignListRef = ref()
|
|
||||||
const openChildrenTask = (item: any) => {
|
|
||||||
taskSignListRef.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,89 +0,0 @@
|
||||||
<template>
|
|
||||||
<Dialog v-model="dialogVisible" title="委派任务" width="500">
|
|
||||||
<el-form
|
|
||||||
ref="formRef"
|
|
||||||
v-loading="formLoading"
|
|
||||||
:model="formData"
|
|
||||||
:rules="formRules"
|
|
||||||
label-width="110px"
|
|
||||||
>
|
|
||||||
<el-form-item label="接收人" prop="delegateUserId">
|
|
||||||
<el-select v-model="formData.delegateUserId" clearable style="width: 100%">
|
|
||||||
<el-option
|
|
||||||
v-for="item in userList"
|
|
||||||
:key="item.id"
|
|
||||||
:label="item.nickname"
|
|
||||||
:value="item.id"
|
|
||||||
/>
|
|
||||||
</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>
|
|
||||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
|
||||||
</template>
|
|
||||||
</Dialog>
|
|
||||||
</template>
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import * as TaskApi from '@/api/bpm/task'
|
|
||||||
import * as UserApi from '@/api/system/user'
|
|
||||||
|
|
||||||
defineOptions({ name: 'BpmTaskDelegateForm' })
|
|
||||||
|
|
||||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
|
||||||
const formLoading = ref(false) // 表单的加载中
|
|
||||||
const formData = ref({
|
|
||||||
id: '',
|
|
||||||
delegateUserId: undefined,
|
|
||||||
reason: ''
|
|
||||||
})
|
|
||||||
const formRules = ref({
|
|
||||||
delegateUserId: [{ required: true, message: '接收人不能为空', trigger: 'change' }],
|
|
||||||
reason: [{ required: true, message: '委派理由不能为空', trigger: 'blur' }]
|
|
||||||
})
|
|
||||||
|
|
||||||
const formRef = ref() // 表单 Ref
|
|
||||||
const userList = ref<any[]>([]) // 用户列表
|
|
||||||
|
|
||||||
/** 打开弹窗 */
|
|
||||||
const open = async (id: string) => {
|
|
||||||
dialogVisible.value = true
|
|
||||||
resetForm()
|
|
||||||
formData.value.id = id
|
|
||||||
// 获得用户列表
|
|
||||||
userList.value = await UserApi.getSimpleUserList()
|
|
||||||
}
|
|
||||||
defineExpose({ open }) // 提供 openModal 方法,用于打开弹窗
|
|
||||||
|
|
||||||
/** 提交表单 */
|
|
||||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
|
||||||
const submitForm = async () => {
|
|
||||||
// 校验表单
|
|
||||||
if (!formRef) return
|
|
||||||
const valid = await formRef.value.validate()
|
|
||||||
if (!valid) return
|
|
||||||
// 提交请求
|
|
||||||
formLoading.value = true
|
|
||||||
try {
|
|
||||||
await TaskApi.delegateTask(formData.value)
|
|
||||||
dialogVisible.value = false
|
|
||||||
// 发送操作成功的事件
|
|
||||||
emit('success')
|
|
||||||
} finally {
|
|
||||||
formLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 重置表单 */
|
|
||||||
const resetForm = () => {
|
|
||||||
formData.value = {
|
|
||||||
id: '',
|
|
||||||
delegateUserId: undefined,
|
|
||||||
reason: ''
|
|
||||||
}
|
|
||||||
formRef.value?.resetFields()
|
|
||||||
}
|
|
||||||
</script>
|
|
|
@ -1,90 +0,0 @@
|
||||||
<template>
|
|
||||||
<Dialog v-model="dialogVisible" title="回退任务" width="500">
|
|
||||||
<el-form
|
|
||||||
ref="formRef"
|
|
||||||
v-loading="formLoading"
|
|
||||||
:model="formData"
|
|
||||||
:rules="formRules"
|
|
||||||
label-width="110px"
|
|
||||||
>
|
|
||||||
<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.taskDefinitionKey"
|
|
||||||
:label="item.name"
|
|
||||||
:value="item.taskDefinitionKey"
|
|
||||||
/>
|
|
||||||
</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>
|
|
||||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
|
||||||
</template>
|
|
||||||
</Dialog>
|
|
||||||
</template>
|
|
||||||
<script lang="ts" name="TaskRollbackDialogForm" setup>
|
|
||||||
import * as TaskApi from '@/api/bpm/task'
|
|
||||||
|
|
||||||
const message = useMessage() // 消息弹窗
|
|
||||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
|
||||||
const formLoading = ref(false) // 表单的加载中
|
|
||||||
const formData = ref({
|
|
||||||
id: '',
|
|
||||||
targetTaskDefinitionKey: undefined,
|
|
||||||
reason: ''
|
|
||||||
})
|
|
||||||
const formRules = ref({
|
|
||||||
targetTaskDefinitionKey: [{ required: true, message: '必须选择回退节点', trigger: 'change' }],
|
|
||||||
reason: [{ required: true, message: '回退理由不能为空', trigger: 'blur' }]
|
|
||||||
})
|
|
||||||
|
|
||||||
const formRef = ref() // 表单 Ref
|
|
||||||
const returnList = ref([] as any)
|
|
||||||
/** 打开弹窗 */
|
|
||||||
const open = async (id: string) => {
|
|
||||||
returnList.value = await TaskApi.getTaskListByReturn(id)
|
|
||||||
if (returnList.value.length === 0) {
|
|
||||||
message.warning('当前没有可回退的节点')
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
dialogVisible.value = true
|
|
||||||
resetForm()
|
|
||||||
formData.value.id = id
|
|
||||||
}
|
|
||||||
defineExpose({ open }) // 提供 openModal 方法,用于打开弹窗
|
|
||||||
|
|
||||||
/** 提交表单 */
|
|
||||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
|
||||||
const submitForm = async () => {
|
|
||||||
// 校验表单
|
|
||||||
if (!formRef) return
|
|
||||||
const valid = await formRef.value.validate()
|
|
||||||
if (!valid) return
|
|
||||||
// 提交请求
|
|
||||||
formLoading.value = true
|
|
||||||
try {
|
|
||||||
await TaskApi.returnTask(formData.value)
|
|
||||||
message.success('回退成功')
|
|
||||||
dialogVisible.value = false
|
|
||||||
// 发送操作成功的事件
|
|
||||||
emit('success')
|
|
||||||
} finally {
|
|
||||||
formLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 重置表单 */
|
|
||||||
const resetForm = () => {
|
|
||||||
formData.value = {
|
|
||||||
id: '',
|
|
||||||
targetTaskDefinitionKey: undefined,
|
|
||||||
reason: ''
|
|
||||||
}
|
|
||||||
formRef.value?.resetFields()
|
|
||||||
}
|
|
||||||
</script>
|
|
|
@ -1,99 +0,0 @@
|
||||||
<template>
|
|
||||||
<Dialog v-model="dialogVisible" title="加签" width="500">
|
|
||||||
<el-form
|
|
||||||
ref="formRef"
|
|
||||||
v-loading="formLoading"
|
|
||||||
:model="formData"
|
|
||||||
:rules="formRules"
|
|
||||||
label-width="110px"
|
|
||||||
>
|
|
||||||
<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"
|
|
||||||
:label="item.nickname"
|
|
||||||
:value="item.id"
|
|
||||||
/>
|
|
||||||
</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('before')">
|
|
||||||
向前加签
|
|
||||||
</el-button>
|
|
||||||
<el-button :disabled="formLoading" type="primary" @click="submitForm('after')">
|
|
||||||
向后加签
|
|
||||||
</el-button>
|
|
||||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
|
||||||
</template>
|
|
||||||
</Dialog>
|
|
||||||
</template>
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import * as TaskApi from '@/api/bpm/task'
|
|
||||||
import * as UserApi from '@/api/system/user'
|
|
||||||
|
|
||||||
defineOptions({ name: 'TaskSignCreateForm' })
|
|
||||||
|
|
||||||
const message = useMessage() // 消息弹窗
|
|
||||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
|
||||||
const formLoading = ref(false) // 表单的加载中
|
|
||||||
const formData = ref({
|
|
||||||
id: '',
|
|
||||||
userIds: [],
|
|
||||||
type: '',
|
|
||||||
reason: ''
|
|
||||||
})
|
|
||||||
const formRules = ref({
|
|
||||||
userIds: [{ required: true, message: '加签处理人不能为空', trigger: 'change' }],
|
|
||||||
reason: [{ required: true, message: '加签理由不能为空', trigger: 'change' }]
|
|
||||||
})
|
|
||||||
|
|
||||||
const formRef = ref() // 表单 Ref
|
|
||||||
const userList = ref<any[]>([]) // 用户列表
|
|
||||||
|
|
||||||
/** 打开弹窗 */
|
|
||||||
const open = async (id: string) => {
|
|
||||||
dialogVisible.value = true
|
|
||||||
resetForm()
|
|
||||||
formData.value.id = id
|
|
||||||
// 获得用户列表
|
|
||||||
userList.value = await UserApi.getSimpleUserList()
|
|
||||||
}
|
|
||||||
defineExpose({ open }) // 提供 openModal 方法,用于打开弹窗
|
|
||||||
|
|
||||||
/** 提交表单 */
|
|
||||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
|
||||||
const submitForm = async (type: string) => {
|
|
||||||
// 校验表单
|
|
||||||
if (!formRef) return
|
|
||||||
const valid = await formRef.value.validate()
|
|
||||||
if (!valid) return
|
|
||||||
// 提交请求
|
|
||||||
formLoading.value = true
|
|
||||||
formData.value.type = type
|
|
||||||
try {
|
|
||||||
await TaskApi.signCreateTask(formData.value)
|
|
||||||
message.success('加签成功')
|
|
||||||
dialogVisible.value = false
|
|
||||||
// 发送操作成功的事件
|
|
||||||
emit('success')
|
|
||||||
} finally {
|
|
||||||
formLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 重置表单 */
|
|
||||||
const resetForm = () => {
|
|
||||||
formData.value = {
|
|
||||||
id: '',
|
|
||||||
userIds: [],
|
|
||||||
type: '',
|
|
||||||
reason: ''
|
|
||||||
}
|
|
||||||
formRef.value?.resetFields()
|
|
||||||
}
|
|
||||||
</script>
|
|
|
@ -1,89 +0,0 @@
|
||||||
<template>
|
|
||||||
<Dialog v-model="dialogVisible" title="减签" width="500">
|
|
||||||
<el-form
|
|
||||||
ref="formRef"
|
|
||||||
v-loading="formLoading"
|
|
||||||
:model="formData"
|
|
||||||
:rules="formRules"
|
|
||||||
label-width="110px"
|
|
||||||
>
|
|
||||||
<el-form-item label="减签任务" prop="id">
|
|
||||||
<el-radio-group v-model="formData.id">
|
|
||||||
<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>
|
|
||||||
<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>
|
|
||||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
|
||||||
</template>
|
|
||||||
</Dialog>
|
|
||||||
</template>
|
|
||||||
<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) // 表单的加载中
|
|
||||||
const formData = ref({
|
|
||||||
id: '',
|
|
||||||
reason: ''
|
|
||||||
})
|
|
||||||
const formRules = ref({
|
|
||||||
id: [{ required: true, message: '必须选择减签任务', trigger: 'change' }],
|
|
||||||
reason: [{ required: true, message: '减签理由不能为空', trigger: 'blur' }]
|
|
||||||
})
|
|
||||||
|
|
||||||
const formRef = ref() // 表单 Ref
|
|
||||||
const childrenTaskList = ref([])
|
|
||||||
/** 打开弹窗 */
|
|
||||||
const open = async (id: string) => {
|
|
||||||
childrenTaskList.value = await TaskApi.getChildrenTaskList(id)
|
|
||||||
if (isEmpty(childrenTaskList.value)) {
|
|
||||||
message.warning('当前没有可减签的任务')
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
dialogVisible.value = true
|
|
||||||
resetForm()
|
|
||||||
}
|
|
||||||
defineExpose({ open }) // 提供 openModal 方法,用于打开弹窗
|
|
||||||
|
|
||||||
/** 提交表单 */
|
|
||||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
|
||||||
const submitForm = async () => {
|
|
||||||
// 校验表单
|
|
||||||
if (!formRef) return
|
|
||||||
const valid = await formRef.value.validate()
|
|
||||||
if (!valid) return
|
|
||||||
// 提交请求
|
|
||||||
formLoading.value = true
|
|
||||||
try {
|
|
||||||
await TaskApi.signDeleteTask(formData.value)
|
|
||||||
message.success('减签成功')
|
|
||||||
dialogVisible.value = false
|
|
||||||
// 发送操作成功的事件
|
|
||||||
emit('success')
|
|
||||||
} finally {
|
|
||||||
formLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 重置表单 */
|
|
||||||
const resetForm = () => {
|
|
||||||
formData.value = {
|
|
||||||
id: '',
|
|
||||||
reason: ''
|
|
||||||
}
|
|
||||||
formRef.value?.resetFields()
|
|
||||||
}
|
|
||||||
</script>
|
|
|
@ -1,106 +0,0 @@
|
||||||
<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,89 +0,0 @@
|
||||||
<template>
|
|
||||||
<Dialog v-model="dialogVisible" title="转派任务" width="500">
|
|
||||||
<el-form
|
|
||||||
ref="formRef"
|
|
||||||
v-loading="formLoading"
|
|
||||||
:model="formData"
|
|
||||||
:rules="formRules"
|
|
||||||
label-width="110px"
|
|
||||||
>
|
|
||||||
<el-form-item label="新审批人" prop="assigneeUserId">
|
|
||||||
<el-select v-model="formData.assigneeUserId" clearable style="width: 100%">
|
|
||||||
<el-option
|
|
||||||
v-for="item in userList"
|
|
||||||
:key="item.id"
|
|
||||||
:label="item.nickname"
|
|
||||||
:value="item.id"
|
|
||||||
/>
|
|
||||||
</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>
|
|
||||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
|
||||||
</template>
|
|
||||||
</Dialog>
|
|
||||||
</template>
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import * as TaskApi from '@/api/bpm/task'
|
|
||||||
import * as UserApi from '@/api/system/user'
|
|
||||||
|
|
||||||
defineOptions({ name: 'TaskTransferForm' })
|
|
||||||
|
|
||||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
|
||||||
const formLoading = ref(false) // 表单的加载中
|
|
||||||
const formData = ref({
|
|
||||||
id: '',
|
|
||||||
assigneeUserId: undefined,
|
|
||||||
reason: ''
|
|
||||||
})
|
|
||||||
const formRules = ref({
|
|
||||||
assigneeUserId: [{ required: true, message: '新审批人不能为空', trigger: 'change' }],
|
|
||||||
reason: [{ required: true, message: '转派理由不能为空', trigger: 'blur' }]
|
|
||||||
})
|
|
||||||
|
|
||||||
const formRef = ref() // 表单 Ref
|
|
||||||
const userList = ref<any[]>([]) // 用户列表
|
|
||||||
|
|
||||||
/** 打开弹窗 */
|
|
||||||
const open = async (id: string) => {
|
|
||||||
dialogVisible.value = true
|
|
||||||
resetForm()
|
|
||||||
formData.value.id = id
|
|
||||||
// 获得用户列表
|
|
||||||
userList.value = await UserApi.getSimpleUserList()
|
|
||||||
}
|
|
||||||
defineExpose({ open }) // 提供 openModal 方法,用于打开弹窗
|
|
||||||
|
|
||||||
/** 提交表单 */
|
|
||||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
|
||||||
const submitForm = async () => {
|
|
||||||
// 校验表单
|
|
||||||
if (!formRef) return
|
|
||||||
const valid = await formRef.value.validate()
|
|
||||||
if (!valid) return
|
|
||||||
// 提交请求
|
|
||||||
formLoading.value = true
|
|
||||||
try {
|
|
||||||
await TaskApi.transferTask(formData.value)
|
|
||||||
dialogVisible.value = false
|
|
||||||
// 发送操作成功的事件
|
|
||||||
emit('success')
|
|
||||||
} finally {
|
|
||||||
formLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 重置表单 */
|
|
||||||
const resetForm = () => {
|
|
||||||
formData.value = {
|
|
||||||
id: '',
|
|
||||||
assigneeUserId: undefined,
|
|
||||||
reason: ''
|
|
||||||
}
|
|
||||||
formRef.value?.resetFields()
|
|
||||||
}
|
|
||||||
</script>
|
|
|
@ -1,381 +0,0 @@
|
||||||
<template>
|
|
||||||
<ContentWrap>
|
|
||||||
<!-- 审批信息 -->
|
|
||||||
<el-card
|
|
||||||
v-for="(item, index) in runningTasks"
|
|
||||||
:key="index"
|
|
||||||
v-loading="processInstanceLoading"
|
|
||||||
class="box-card"
|
|
||||||
>
|
|
||||||
<template #header>
|
|
||||||
<span class="el-icon-picture-outline">审批任务【{{ item.name }}】</span>
|
|
||||||
</template>
|
|
||||||
<el-col :offset="6" :span="16">
|
|
||||||
<el-form
|
|
||||||
:ref="'form' + index"
|
|
||||||
:model="auditForms[index]"
|
|
||||||
:rules="auditRule"
|
|
||||||
label-width="100px"
|
|
||||||
>
|
|
||||||
<el-form-item v-if="processInstance && processInstance.name" label="流程名">
|
|
||||||
{{ 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>
|
|
||||||
</el-form-item>
|
|
||||||
<el-card v-if="runningTasks[index].formId > 0" class="mb-15px !-mt-10px">
|
|
||||||
<template #header>
|
|
||||||
<span class="el-icon-picture-outline">
|
|
||||||
填写表单【{{ runningTasks[index]?.formName }}】
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
<form-create
|
|
||||||
v-model="approveForms[index].value"
|
|
||||||
v-model:api="approveFormFApis[index]"
|
|
||||||
:option="approveForms[index].option"
|
|
||||||
:rule="approveForms[index].rule"
|
|
||||||
/>
|
|
||||||
</el-card>
|
|
||||||
<el-form-item label="审批建议" prop="reason">
|
|
||||||
<el-input
|
|
||||||
v-model="auditForms[index].reason"
|
|
||||||
placeholder="请输入审批建议"
|
|
||||||
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)">
|
|
||||||
<Icon icon="ep:select" />
|
|
||||||
通过
|
|
||||||
</el-button>
|
|
||||||
<el-button type="danger" @click="handleAudit(item, false)">
|
|
||||||
<Icon icon="ep:close" />
|
|
||||||
不通过
|
|
||||||
</el-button>
|
|
||||||
<el-button type="primary" @click="openTaskUpdateAssigneeForm(item.id)">
|
|
||||||
<Icon icon="ep:edit" />
|
|
||||||
转办
|
|
||||||
</el-button>
|
|
||||||
<el-button type="primary" @click="handleDelegate(item)">
|
|
||||||
<Icon icon="ep:position" />
|
|
||||||
委派
|
|
||||||
</el-button>
|
|
||||||
<el-button type="primary" @click="handleSign(item)">
|
|
||||||
<Icon icon="ep:plus" />
|
|
||||||
加签
|
|
||||||
</el-button>
|
|
||||||
<el-button type="warning" @click="handleBack(item)">
|
|
||||||
<Icon icon="ep:back" />
|
|
||||||
回退
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
</el-card>
|
|
||||||
|
|
||||||
<!-- 申请信息 -->
|
|
||||||
<el-card v-loading="processInstanceLoading" class="box-card">
|
|
||||||
<template #header>
|
|
||||||
<span class="el-icon-document">申请信息【{{ processInstance.name }}】</span>
|
|
||||||
</template>
|
|
||||||
<!-- 情况一:流程表单 -->
|
|
||||||
<el-col v-if="processInstance?.processDefinition?.formType === 10" :offset="6" :span="16">
|
|
||||||
<form-create
|
|
||||||
v-model="detailForm.value"
|
|
||||||
v-model:api="fApi"
|
|
||||||
:option="detailForm.option"
|
|
||||||
:rule="detailForm.rule"
|
|
||||||
/>
|
|
||||||
</el-col>
|
|
||||||
<!-- 情况二:业务表单 -->
|
|
||||||
<div v-if="processInstance?.processDefinition?.formType === 20">
|
|
||||||
<BusinessFormComponent :id="processInstance.businessKey" />
|
|
||||||
</div>
|
|
||||||
</el-card>
|
|
||||||
|
|
||||||
<!-- 审批记录 -->
|
|
||||||
<ProcessInstanceTaskList
|
|
||||||
:loading="tasksLoad"
|
|
||||||
:process-instance="processInstance"
|
|
||||||
:tasks="tasks"
|
|
||||||
@refresh="getTaskList"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- 高亮流程图 -->
|
|
||||||
<ProcessInstanceBpmnViewer
|
|
||||||
:id="`${id}`"
|
|
||||||
:bpmn-xml="bpmnXml"
|
|
||||||
:loading="processInstanceLoading"
|
|
||||||
:process-instance="processInstance"
|
|
||||||
:tasks="tasks"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- 弹窗:转派审批人 -->
|
|
||||||
<TaskTransferForm ref="taskTransferFormRef" @success="getDetail" />
|
|
||||||
<!-- 弹窗:回退节点 -->
|
|
||||||
<TaskReturnForm ref="taskReturnFormRef" @success="getDetail" />
|
|
||||||
<!-- 弹窗:委派,将任务委派给别人处理,处理完成后,会重新回到原审批人手中-->
|
|
||||||
<TaskDelegateForm ref="taskDelegateForm" @success="getDetail" />
|
|
||||||
<!-- 弹窗:加签,当前任务审批人为A,向前加签选了一个C,则需要C先审批,然后再是A审批,向后加签B,A审批完,需要B再审批完,才算完成这个任务节点 -->
|
|
||||||
<TaskSignCreateForm ref="taskSignCreateFormRef" @success="getDetail" />
|
|
||||||
</ContentWrap>
|
|
||||||
</template>
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { useUserStore } from '@/store/modules/user'
|
|
||||||
import { setConfAndFields2 } from '@/utils/formCreate'
|
|
||||||
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 ProcessInstanceBpmnViewer from './ProcessInstanceBpmnViewer.vue'
|
|
||||||
import ProcessInstanceTaskList from './ProcessInstanceTaskList.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' })
|
|
||||||
|
|
||||||
const { query } = useRoute() // 查询参数
|
|
||||||
const message = useMessage() // 消息弹窗
|
|
||||||
const { proxy } = getCurrentInstance() as any
|
|
||||||
|
|
||||||
const userId = useUserStore().getUser.id // 当前登录的编号
|
|
||||||
const id = query.id as unknown as string // 流程实例的编号
|
|
||||||
const processInstanceLoading = ref(false) // 流程实例的加载中
|
|
||||||
const processInstance = ref<any>({}) // 流程实例
|
|
||||||
const bpmnXml = ref('') // BPMN XML
|
|
||||||
const tasksLoad = ref(true) // 任务的加载中
|
|
||||||
const tasks = ref<any[]>([]) // 任务列表
|
|
||||||
// ========== 审批信息 ==========
|
|
||||||
const runningTasks = ref<any[]>([]) // 运行中的任务
|
|
||||||
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) => {
|
|
||||||
// 1.1 获得对应表单
|
|
||||||
const index = runningTasks.value.indexOf(task)
|
|
||||||
const auditFormRef = proxy.$refs['form' + index][0]
|
|
||||||
// 1.2 校验表单
|
|
||||||
const elForm = unref(auditFormRef)
|
|
||||||
if (!elForm) return
|
|
||||||
const valid = await elForm.validate()
|
|
||||||
if (!valid) return
|
|
||||||
|
|
||||||
// 2.1 提交审批
|
|
||||||
const data = {
|
|
||||||
id: task.id,
|
|
||||||
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 {
|
|
||||||
await TaskApi.rejectTask(data)
|
|
||||||
message.success('审批不通过成功')
|
|
||||||
}
|
|
||||||
// 2.2 加载最新数据
|
|
||||||
getDetail()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 转派审批人 */
|
|
||||||
const taskTransferFormRef = ref()
|
|
||||||
const openTaskUpdateAssigneeForm = (id: string) => {
|
|
||||||
taskTransferFormRef.value.open(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 处理审批退回的操作 */
|
|
||||||
const taskDelegateForm = ref()
|
|
||||||
const handleDelegate = async (task) => {
|
|
||||||
taskDelegateForm.value.open(task.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 处理审批退回的操作 */
|
|
||||||
const taskReturnFormRef = ref()
|
|
||||||
const handleBack = async (task: any) => {
|
|
||||||
taskReturnFormRef.value.open(task.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 处理审批加签的操作 */
|
|
||||||
const taskSignCreateFormRef = ref()
|
|
||||||
const handleSign = async (task: any) => {
|
|
||||||
taskSignCreateFormRef.value.open(task.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 获得详情 */
|
|
||||||
const getDetail = () => {
|
|
||||||
// 1. 获得流程实例相关
|
|
||||||
getProcessInstance()
|
|
||||||
// 2. 获得流程任务列表(审批记录)
|
|
||||||
getTaskList()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 加载流程实例 */
|
|
||||||
const BusinessFormComponent = ref(null) // 异步组件
|
|
||||||
const getProcessInstance = async () => {
|
|
||||||
try {
|
|
||||||
processInstanceLoading.value = true
|
|
||||||
const data = await ProcessInstanceApi.getProcessInstance(id)
|
|
||||||
if (!data) {
|
|
||||||
message.error('查询不到流程信息!')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
processInstance.value = data
|
|
||||||
|
|
||||||
// 设置表单信息
|
|
||||||
const processDefinition = data.processDefinition
|
|
||||||
if (processDefinition.formType === 10) {
|
|
||||||
setConfAndFields2(
|
|
||||||
detailForm,
|
|
||||||
processDefinition.formConf,
|
|
||||||
processDefinition.formFields,
|
|
||||||
data.formVariables
|
|
||||||
)
|
|
||||||
nextTick().then(() => {
|
|
||||||
fApi.value?.btn.show(false)
|
|
||||||
fApi.value?.resetBtn.show(false)
|
|
||||||
fApi.value?.disabled(true)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
// 注意:data.processDefinition.formCustomViewPath 是组件的全路径,例如说:/crm/contract/detail/index.vue
|
|
||||||
BusinessFormComponent.value = registerComponent(data.processDefinition.formCustomViewPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 加载流程图
|
|
||||||
bpmnXml.value = (
|
|
||||||
await DefinitionApi.getProcessDefinition(processDefinition.id as number)
|
|
||||||
)?.bpmnXml
|
|
||||||
} finally {
|
|
||||||
processInstanceLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 加载任务列表 */
|
|
||||||
const getTaskList = async () => {
|
|
||||||
runningTasks.value = []
|
|
||||||
auditForms.value = []
|
|
||||||
approveForms.value = []
|
|
||||||
approveFormFApis.value = []
|
|
||||||
try {
|
|
||||||
// 获得未取消的任务
|
|
||||||
tasksLoad.value = true
|
|
||||||
const data = await TaskApi.getTaskListByProcessInstanceId(id)
|
|
||||||
tasks.value = []
|
|
||||||
// 1.1 移除已取消的审批
|
|
||||||
data.forEach((task) => {
|
|
||||||
if (task.status !== 4) {
|
|
||||||
tasks.value.push(task)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
// 1.2 排序,将未完成的排在前面,已完成的排在后面;
|
|
||||||
tasks.value.sort((a, b) => {
|
|
||||||
// 有已完成的情况,按照完成时间倒序
|
|
||||||
if (a.endTime && b.endTime) {
|
|
||||||
return b.endTime - a.endTime
|
|
||||||
} else if (a.endTime) {
|
|
||||||
return 1
|
|
||||||
} else if (b.endTime) {
|
|
||||||
return -1
|
|
||||||
// 都是未完成,按照创建时间倒序
|
|
||||||
} else {
|
|
||||||
return b.createTime - a.createTime
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// 获得需要自己审批的任务
|
|
||||||
loadRunningTask(tasks.value)
|
|
||||||
} finally {
|
|
||||||
tasksLoad.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置 runningTasks 中的任务
|
|
||||||
*/
|
|
||||||
const loadRunningTask = (tasks) => {
|
|
||||||
tasks.forEach((task) => {
|
|
||||||
if (!isEmpty(task.children)) {
|
|
||||||
loadRunningTask(task.children)
|
|
||||||
}
|
|
||||||
// 2.1 只有待处理才需要
|
|
||||||
if (task.status !== 1 && task.status !== 6) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 2.2 自己不是处理人
|
|
||||||
if (!task.assigneeUser || task.assigneeUser.id !== userId) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 2.3 添加到处理任务
|
|
||||||
runningTasks.value.push({ ...task })
|
|
||||||
auditForms.value.push({
|
|
||||||
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({}) // 占位,避免为空
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 初始化 */
|
|
||||||
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
|
|
||||||
onMounted(async () => {
|
|
||||||
getDetail()
|
|
||||||
// 获得用户列表
|
|
||||||
userOptions.value = await UserApi.getSimpleUserList()
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,260 +0,0 @@
|
||||||
<template>
|
|
||||||
<!-- <doc-alert title="流程发起、取消、重新发起" url="https://doc.iocoder.cn/bpm/process-instance/" /> -->
|
|
||||||
|
|
||||||
<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="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-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
|
|
||||||
v-hasPermi="['bpm:process-instance:query']"
|
|
||||||
@click="handleCreate()"
|
|
||||||
>
|
|
||||||
<Icon icon="ep:plus" class="mr-5px" /> 发起流程
|
|
||||||
</el-button>
|
|
||||||
</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="流程状态" 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="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
|
|
||||||
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>
|
|
||||||
<el-button link type="primary" v-else @click="handleCreate(scope.row.id)">
|
|
||||||
重新发起
|
|
||||||
</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'
|
|
||||||
|
|
||||||
defineOptions({ name: 'BpmProcessInstanceMy' })
|
|
||||||
|
|
||||||
const router = useRouter() // 路由
|
|
||||||
const message = useMessage() // 消息弹窗
|
|
||||||
const { t } = useI18n() // 国际化
|
|
||||||
|
|
||||||
const loading = ref(true) // 列表的加载中
|
|
||||||
const total = ref(0) // 列表的总页数
|
|
||||||
const list = ref([]) // 列表的数据
|
|
||||||
const queryParams: any = reactive({
|
|
||||||
pageNo: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
name: '',
|
|
||||||
processDefinitionId: undefined,
|
|
||||||
category: undefined,
|
|
||||||
status: undefined,
|
|
||||||
createTime: []
|
|
||||||
})
|
|
||||||
const queryFormRef = ref() // 搜索的表单
|
|
||||||
const categoryList: any = ref([]) // 流程分类列表
|
|
||||||
|
|
||||||
/** 查询列表 */
|
|
||||||
const getList = async () => {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
const data = await ProcessInstanceApi.getProcessInstanceMyPage(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 handleCreate = (id?) => {
|
|
||||||
router.push({
|
|
||||||
name: 'BpmProcessInstanceCreate',
|
|
||||||
query: { processInstanceId: id }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 查看详情 */
|
|
||||||
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.cancelProcessInstanceByStartUser(row.id, value)
|
|
||||||
message.success('取消成功')
|
|
||||||
// 刷新列表
|
|
||||||
await getList()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 激活时 **/
|
|
||||||
onActivated(() => {
|
|
||||||
getList()
|
|
||||||
})
|
|
||||||
|
|
||||||
/** 初始化 **/
|
|
||||||
onMounted(async () => {
|
|
||||||
await getList()
|
|
||||||
categoryList.value = await CategoryApi.getCategorySimpleList()
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,255 +0,0 @@
|
||||||
<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>
|
|
|
@ -1,162 +0,0 @@
|
||||||
<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>
|
|
|
@ -1,185 +0,0 @@
|
||||||
<template>
|
|
||||||
<!-- <doc-alert title="执行监听器、任务监听器" url="https://doc.iocoder.cn/bpm/listener/" /> -->
|
|
||||||
|
|
||||||
<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 as any"
|
|
||||||
: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>
|
|
|
@ -1,28 +0,0 @@
|
||||||
<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>
|
|
|
@ -1,137 +0,0 @@
|
||||||
<!-- 工作流 - 抄送我的流程 -->
|
|
||||||
<template>
|
|
||||||
<!-- <doc-alert
|
|
||||||
title="审批转办、委派、抄送"
|
|
||||||
url="https://doc.iocoder.cn/bpm/task-delegation-and-cc/"
|
|
||||||
/> -->
|
|
||||||
|
|
||||||
<ContentWrap>
|
|
||||||
<!-- 搜索工作栏 -->
|
|
||||||
<el-form ref="queryFormRef" :inline="true" class="-mb-15px" label-width="68px">
|
|
||||||
<el-form-item label="流程名称" prop="name">
|
|
||||||
<el-input
|
|
||||||
v-model="queryParams.processInstanceName"
|
|
||||||
class="!w-240px"
|
|
||||||
clearable
|
|
||||||
placeholder="请输入流程名称"
|
|
||||||
/>
|
|
||||||
</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="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="抄送时间"
|
|
||||||
prop="createTime"
|
|
||||||
width="180"
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
/>
|
|
||||||
<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 { dateFormatter } from '@/utils/formatTime'
|
|
||||||
import * as ProcessInstanceApi from '@/api/bpm/processInstance'
|
|
||||||
|
|
||||||
defineOptions({ name: 'BpmProcessInstanceCopy' })
|
|
||||||
|
|
||||||
const { push } = useRouter() // 路由
|
|
||||||
|
|
||||||
const loading = ref(false) // 列表的加载中
|
|
||||||
const total = ref(0) // 列表的总页数
|
|
||||||
const list = ref([]) // 列表的数据
|
|
||||||
const queryParams: any = reactive({
|
|
||||||
pageNo: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
processInstanceId: '',
|
|
||||||
processInstanceName: '',
|
|
||||||
createTime: []
|
|
||||||
})
|
|
||||||
const queryFormRef = ref() // 搜索的表单
|
|
||||||
|
|
||||||
/** 查询任务列表 */
|
|
||||||
const getList = async () => {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
const data = await ProcessInstanceApi.getProcessInstanceCopyPage(queryParams)
|
|
||||||
list.value = data.list
|
|
||||||
total.value = data.total
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 处理审批按钮 */
|
|
||||||
const handleAudit = (row: any) => {
|
|
||||||
push({
|
|
||||||
name: 'BpmProcessInstanceDetail',
|
|
||||||
query: {
|
|
||||||
id: row.processInstanceId
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 搜索按钮操作 */
|
|
||||||
const handleQuery = () => {
|
|
||||||
queryParams.pageNo = 1
|
|
||||||
getList()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 重置按钮操作 */
|
|
||||||
const resetQuery = () => {
|
|
||||||
queryFormRef.value.resetFields()
|
|
||||||
handleQuery()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 初始化 **/
|
|
||||||
onMounted(() => {
|
|
||||||
getList()
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,170 +0,0 @@
|
||||||
<template>
|
|
||||||
<!-- <doc-alert title="审批通过、不通过、驳回" url="https://doc.iocoder.cn/bpm/task-todo-done/" />
|
|
||||||
<doc-alert title="审批加签、减签" url="https://doc.iocoder.cn/bpm/sign/" />
|
|
||||||
<doc-alert
|
|
||||||
title="审批转办、委派、抄送"
|
|
||||||
url="https://doc.iocoder.cn/bpm/task-delegation-and-cc/"
|
|
||||||
/>
|
|
||||||
<doc-alert title="审批加签、减签" url="https://doc.iocoder.cn/bpm/sign/" /> -->
|
|
||||||
|
|
||||||
<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="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: 'BpmTodoTask' })
|
|
||||||
|
|
||||||
const { push } = useRouter() // 路由
|
|
||||||
|
|
||||||
const loading = ref(true) // 列表的加载中
|
|
||||||
const total = ref(0) // 列表的总页数
|
|
||||||
const list = ref([]) // 列表的数据
|
|
||||||
const queryParams: any = reactive({
|
|
||||||
pageNo: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
name: '',
|
|
||||||
createTime: []
|
|
||||||
})
|
|
||||||
const queryFormRef = ref() // 搜索的表单
|
|
||||||
|
|
||||||
/** 查询任务列表 */
|
|
||||||
const getList = async () => {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
const data = await TaskApi.getTaskDonePage(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>
|
|
|
@ -1,166 +0,0 @@
|
||||||
<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: any = 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>
|
|
|
@ -1,152 +0,0 @@
|
||||||
<template>
|
|
||||||
<!-- <doc-alert title="审批通过、不通过、驳回" url="https://doc.iocoder.cn/bpm/task-todo-done/" />
|
|
||||||
<doc-alert title="审批加签、减签" url="https://doc.iocoder.cn/bpm/sign/" />
|
|
||||||
<doc-alert
|
|
||||||
title="审批转办、委派、抄送"
|
|
||||||
url="https://doc.iocoder.cn/bpm/task-delegation-and-cc/"
|
|
||||||
/>
|
|
||||||
<doc-alert title="审批加签、减签" url="https://doc.iocoder.cn/bpm/sign/" /> -->
|
|
||||||
|
|
||||||
<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 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 { dateFormatter } from '@/utils/formatTime'
|
|
||||||
import * as TaskApi from '@/api/bpm/task'
|
|
||||||
|
|
||||||
defineOptions({ name: 'BpmTodoTask' })
|
|
||||||
|
|
||||||
const { push } = useRouter() // 路由
|
|
||||||
|
|
||||||
const loading = ref(true) // 列表的加载中
|
|
||||||
const total = ref(0) // 列表的总页数
|
|
||||||
const list = ref([]) // 列表的数据
|
|
||||||
const queryParams: any = reactive({
|
|
||||||
pageNo: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
name: '',
|
|
||||||
createTime: []
|
|
||||||
})
|
|
||||||
const queryFormRef = ref() // 搜索的表单
|
|
||||||
|
|
||||||
/** 查询任务列表 */
|
|
||||||
const getList = async () => {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
const data = await TaskApi.getTaskTodoPage(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>
|
|
|
@ -1,153 +0,0 @@
|
||||||
<template>
|
|
||||||
<ContentWrap>
|
|
||||||
<div class="pb-5 text-xl">分配给我的线索</div>
|
|
||||||
<!-- 搜索工作栏 -->
|
|
||||||
<el-form
|
|
||||||
ref="queryFormRef"
|
|
||||||
:inline="true"
|
|
||||||
:model="queryParams"
|
|
||||||
class="-mb-15px"
|
|
||||||
label-width="68px"
|
|
||||||
>
|
|
||||||
<el-form-item label="状态" prop="followUpStatus">
|
|
||||||
<el-select
|
|
||||||
v-model="queryParams.followUpStatus"
|
|
||||||
class="!w-240px"
|
|
||||||
placeholder="状态"
|
|
||||||
@change="handleQuery"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="(option, index) in FOLLOWUP_STATUS"
|
|
||||||
:label="option.label"
|
|
||||||
:value="option.value"
|
|
||||||
:key="index"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</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="name" fixed="left" width="160">
|
|
||||||
<template #default="scope">
|
|
||||||
<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="source" width="100">
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_SOURCE" :value="scope.row.source" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="手机" align="center" prop="mobile" width="120" />
|
|
||||||
<el-table-column label="电话" align="center" prop="telephone" width="130" />
|
|
||||||
<el-table-column label="邮箱" align="center" prop="email" width="180" />
|
|
||||||
<el-table-column label="地址" align="center" prop="detailAddress" width="180" />
|
|
||||||
<el-table-column align="center" label="客户行业" prop="industryId" width="100">
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_INDUSTRY" :value="scope.row.industryId" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="客户级别" prop="level" width="135">
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_LEVEL" :value="scope.row.level" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="下次联系时间"
|
|
||||||
prop="contactNextTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="备注" prop="remark" width="200" />
|
|
||||||
<el-table-column
|
|
||||||
label="最后跟进时间"
|
|
||||||
align="center"
|
|
||||||
prop="contactLastTime"
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="最后跟进记录" prop="contactLastContent" width="200" />
|
|
||||||
<el-table-column align="center" label="负责人" prop="ownerUserName" width="100px" />
|
|
||||||
<el-table-column align="center" label="所属部门" prop="ownerUserDeptName" width="100" />
|
|
||||||
<el-table-column
|
|
||||||
label="更新时间"
|
|
||||||
align="center"
|
|
||||||
prop="updateTime"
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
label="创建时间"
|
|
||||||
align="center"
|
|
||||||
prop="createTime"
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="创建人" prop="creatorName" width="100px" />
|
|
||||||
</el-table>
|
|
||||||
<!-- 分页 -->
|
|
||||||
<Pagination
|
|
||||||
:total="total"
|
|
||||||
v-model:page="queryParams.pageNo"
|
|
||||||
v-model:limit="queryParams.pageSize"
|
|
||||||
@pagination="getList"
|
|
||||||
/>
|
|
||||||
</ContentWrap>
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
import * as ClueApi from '@/api/crm/clue'
|
|
||||||
import { DICT_TYPE } from '@/utils/dict'
|
|
||||||
import { dateFormatter } from '@/utils/formatTime'
|
|
||||||
import { FOLLOWUP_STATUS } from './common'
|
|
||||||
|
|
||||||
defineOptions({ name: 'CrmClueFollowList' })
|
|
||||||
|
|
||||||
const loading = ref(true) // 列表的加载中
|
|
||||||
const total = ref(0) // 列表的总页数
|
|
||||||
const list = ref([]) // 列表的数据
|
|
||||||
const queryParams = reactive({
|
|
||||||
pageNo: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
followUpStatus: false,
|
|
||||||
transformStatus: false
|
|
||||||
})
|
|
||||||
const queryFormRef = ref() // 搜索的表单
|
|
||||||
|
|
||||||
/** 查询列表 */
|
|
||||||
const getList = async () => {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
const data = await ClueApi.getCluePage(queryParams)
|
|
||||||
list.value = data.list
|
|
||||||
total.value = data.total
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 搜索按钮操作 */
|
|
||||||
const handleQuery = () => {
|
|
||||||
queryParams.pageNo = 1
|
|
||||||
getList()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 打开线索详情 */
|
|
||||||
const { push } = useRouter()
|
|
||||||
const openDetail = (id: number) => {
|
|
||||||
push({ name: 'CrmClueDetail', params: { id } })
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 激活时 */
|
|
||||||
onActivated(async () => {
|
|
||||||
await getList()
|
|
||||||
})
|
|
||||||
|
|
||||||
/** 初始化 **/
|
|
||||||
onMounted(() => {
|
|
||||||
getList()
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,247 +0,0 @@
|
||||||
<!-- 待审核合同 -->
|
|
||||||
<template>
|
|
||||||
<ContentWrap>
|
|
||||||
<div class="pb-5 text-xl">待审核合同</div>
|
|
||||||
<!-- 搜索工作栏 -->
|
|
||||||
<el-form
|
|
||||||
ref="queryFormRef"
|
|
||||||
:inline="true"
|
|
||||||
:model="queryParams"
|
|
||||||
class="-mb-15px"
|
|
||||||
label-width="68px"
|
|
||||||
>
|
|
||||||
<el-form-item label="合同状态" prop="auditStatus">
|
|
||||||
<el-select
|
|
||||||
v-model="queryParams.auditStatus"
|
|
||||||
class="!w-240px"
|
|
||||||
placeholder="状态"
|
|
||||||
@change="handleQuery"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="(option, index) in AUDIT_STATUS"
|
|
||||||
:label="option.label"
|
|
||||||
:value="option.value"
|
|
||||||
:key="index"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</ContentWrap>
|
|
||||||
|
|
||||||
<ContentWrap>
|
|
||||||
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true">
|
|
||||||
<el-table-column align="center" fixed="left" label="合同编号" prop="no" width="180" />
|
|
||||||
<el-table-column align="center" fixed="left" label="合同名称" prop="name" width="160">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-link :underline="false" type="primary" @click="openDetail(scope.row.id)">
|
|
||||||
{{ scope.row.name }}
|
|
||||||
</el-link>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="客户名称" prop="customerName" width="120">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-link
|
|
||||||
:underline="false"
|
|
||||||
type="primary"
|
|
||||||
@click="openCustomerDetail(scope.row.customerId)"
|
|
||||||
>
|
|
||||||
{{ scope.row.customerName }}
|
|
||||||
</el-link>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="商机名称" prop="businessName" width="130">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-link
|
|
||||||
:underline="false"
|
|
||||||
type="primary"
|
|
||||||
@click="openBusinessDetail(scope.row.businessId)"
|
|
||||||
>
|
|
||||||
{{ scope.row.businessName }}
|
|
||||||
</el-link>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
align="center"
|
|
||||||
label="合同金额(元)"
|
|
||||||
prop="totalPrice"
|
|
||||||
width="140"
|
|
||||||
:formatter="erpPriceTableColumnFormatter"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
align="center"
|
|
||||||
label="下单时间"
|
|
||||||
prop="orderDate"
|
|
||||||
width="120"
|
|
||||||
:formatter="dateFormatter2"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
align="center"
|
|
||||||
label="合同开始时间"
|
|
||||||
prop="startTime"
|
|
||||||
width="120"
|
|
||||||
:formatter="dateFormatter2"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
align="center"
|
|
||||||
label="合同结束时间"
|
|
||||||
prop="endTime"
|
|
||||||
width="120"
|
|
||||||
:formatter="dateFormatter2"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="客户签约人" prop="contactName" width="130">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-link
|
|
||||||
:underline="false"
|
|
||||||
type="primary"
|
|
||||||
@click="openContactDetail(scope.row.signContactId)"
|
|
||||||
>
|
|
||||||
{{ scope.row.signContactName }}
|
|
||||||
</el-link>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="公司签约人" prop="signUserName" width="130" />
|
|
||||||
<el-table-column align="center" label="备注" prop="remark" width="200" />
|
|
||||||
<el-table-column
|
|
||||||
align="center"
|
|
||||||
label="已回款金额(元)"
|
|
||||||
prop="totalReceivablePrice"
|
|
||||||
width="140"
|
|
||||||
:formatter="erpPriceTableColumnFormatter"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
align="center"
|
|
||||||
label="未回款金额(元)"
|
|
||||||
prop="totalReceivablePrice"
|
|
||||||
width="140"
|
|
||||||
:formatter="erpPriceTableColumnFormatter"
|
|
||||||
>
|
|
||||||
<template #default="scope">
|
|
||||||
{{ erpPriceInputFormatter(scope.row.totalPrice - scope.row.totalReceivablePrice) }}
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="最后跟进时间"
|
|
||||||
prop="contactLastTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="负责人" prop="ownerUserName" width="120" />
|
|
||||||
<el-table-column align="center" label="所属部门" prop="ownerUserDeptName" width="100px" />
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="更新时间"
|
|
||||||
prop="updateTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="创建时间"
|
|
||||||
prop="createTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="创建人" prop="creatorName" width="120" />
|
|
||||||
<el-table-column align="center" fixed="right" label="合同状态" prop="auditStatus" width="120">
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.CRM_AUDIT_STATUS" :value="scope.row.auditStatus" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column fixed="right" label="操作" width="90">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
link
|
|
||||||
v-hasPermi="['crm:contract:update']"
|
|
||||||
type="primary"
|
|
||||||
@click="handleProcessDetail(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 setup lang="ts" name="CheckContract">
|
|
||||||
import { dateFormatter, dateFormatter2 } from '@/utils/formatTime'
|
|
||||||
import * as ContractApi from '@/api/crm/contract'
|
|
||||||
import { DICT_TYPE } from '@/utils/dict'
|
|
||||||
import { AUDIT_STATUS } from './common'
|
|
||||||
import { erpPriceInputFormatter, erpPriceTableColumnFormatter } from '@/utils'
|
|
||||||
|
|
||||||
const loading = ref(true) // 列表的加载中
|
|
||||||
const total = ref(0) // 列表的总页数
|
|
||||||
const list = ref([]) // 列表的数据
|
|
||||||
const queryParams = reactive({
|
|
||||||
pageNo: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
sceneType: 1, // 我负责的
|
|
||||||
auditStatus: 10
|
|
||||||
})
|
|
||||||
const queryFormRef = ref() // 搜索的表单
|
|
||||||
|
|
||||||
/** 查询列表 */
|
|
||||||
const getList = async () => {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
const data = await ContractApi.getContractPage(queryParams)
|
|
||||||
list.value = data.list
|
|
||||||
total.value = data.total
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 搜索按钮操作 */
|
|
||||||
const handleQuery = () => {
|
|
||||||
queryParams.pageNo = 1
|
|
||||||
getList()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 查看审批 */
|
|
||||||
const handleProcessDetail = (row: ContractApi.ContractVO) => {
|
|
||||||
push({ name: 'BpmProcessInstanceDetail', query: { id: row.processInstanceId } })
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 打开合同详情 */
|
|
||||||
const { push } = useRouter()
|
|
||||||
const openDetail = (id: number) => {
|
|
||||||
push({ name: 'CrmContractDetail', params: { id } })
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 打开客户详情 */
|
|
||||||
const openCustomerDetail = (id: number) => {
|
|
||||||
push({ name: 'CrmCustomerDetail', params: { id } })
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 打开联系人详情 */
|
|
||||||
const openContactDetail = (id: number) => {
|
|
||||||
push({ name: 'CrmContactDetail', params: { id } })
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 打开商机详情 */
|
|
||||||
const openBusinessDetail = (id: number) => {
|
|
||||||
push({ name: 'CrmBusinessDetail', params: { id } })
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 激活时 */
|
|
||||||
onActivated(async () => {
|
|
||||||
await getList()
|
|
||||||
})
|
|
||||||
|
|
||||||
/** 初始化 **/
|
|
||||||
onMounted(() => {
|
|
||||||
getList()
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped></style>
|
|
|
@ -1,246 +0,0 @@
|
||||||
<!-- 即将到期的合同 -->
|
|
||||||
<template>
|
|
||||||
<ContentWrap>
|
|
||||||
<div class="pb-5 text-xl"> 即将到期的合同 </div>
|
|
||||||
<!-- 搜索工作栏 -->
|
|
||||||
<el-form
|
|
||||||
class="-mb-15px"
|
|
||||||
:model="queryParams"
|
|
||||||
ref="queryFormRef"
|
|
||||||
:inline="true"
|
|
||||||
label-width="68px"
|
|
||||||
>
|
|
||||||
<el-form-item label="到期状态" prop="expiryType">
|
|
||||||
<el-select
|
|
||||||
v-model="queryParams.expiryType"
|
|
||||||
class="!w-240px"
|
|
||||||
placeholder="状态"
|
|
||||||
@change="handleQuery"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="(option, index) in CONTRACT_EXPIRY_TYPE"
|
|
||||||
:label="option.label"
|
|
||||||
:value="option.value"
|
|
||||||
:key="index"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</ContentWrap>
|
|
||||||
|
|
||||||
<ContentWrap>
|
|
||||||
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true">
|
|
||||||
<el-table-column align="center" fixed="left" label="合同编号" prop="no" width="180" />
|
|
||||||
<el-table-column align="center" fixed="left" label="合同名称" prop="name" width="160">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-link :underline="false" type="primary" @click="openDetail(scope.row.id)">
|
|
||||||
{{ scope.row.name }}
|
|
||||||
</el-link>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="客户名称" prop="customerName" width="120">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-link
|
|
||||||
:underline="false"
|
|
||||||
type="primary"
|
|
||||||
@click="openCustomerDetail(scope.row.customerId)"
|
|
||||||
>
|
|
||||||
{{ scope.row.customerName }}
|
|
||||||
</el-link>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="商机名称" prop="businessName" width="130">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-link
|
|
||||||
:underline="false"
|
|
||||||
type="primary"
|
|
||||||
@click="openBusinessDetail(scope.row.businessId)"
|
|
||||||
>
|
|
||||||
{{ scope.row.businessName }}
|
|
||||||
</el-link>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
align="center"
|
|
||||||
label="合同金额(元)"
|
|
||||||
prop="totalPrice"
|
|
||||||
width="140"
|
|
||||||
:formatter="erpPriceTableColumnFormatter"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
align="center"
|
|
||||||
label="下单时间"
|
|
||||||
prop="orderDate"
|
|
||||||
width="120"
|
|
||||||
:formatter="dateFormatter2"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
align="center"
|
|
||||||
label="合同开始时间"
|
|
||||||
prop="startTime"
|
|
||||||
width="120"
|
|
||||||
:formatter="dateFormatter2"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
align="center"
|
|
||||||
label="合同结束时间"
|
|
||||||
prop="endTime"
|
|
||||||
width="120"
|
|
||||||
:formatter="dateFormatter2"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="客户签约人" prop="contactName" width="130">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-link
|
|
||||||
:underline="false"
|
|
||||||
type="primary"
|
|
||||||
@click="openContactDetail(scope.row.signContactId)"
|
|
||||||
>
|
|
||||||
{{ scope.row.signContactName }}
|
|
||||||
</el-link>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="公司签约人" prop="signUserName" width="130" />
|
|
||||||
<el-table-column align="center" label="备注" prop="remark" width="200" />
|
|
||||||
<el-table-column
|
|
||||||
align="center"
|
|
||||||
label="已回款金额(元)"
|
|
||||||
prop="totalReceivablePrice"
|
|
||||||
width="140"
|
|
||||||
:formatter="erpPriceTableColumnFormatter"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
align="center"
|
|
||||||
label="未回款金额(元)"
|
|
||||||
prop="totalReceivablePrice"
|
|
||||||
width="140"
|
|
||||||
:formatter="erpPriceTableColumnFormatter"
|
|
||||||
>
|
|
||||||
<template #default="scope">
|
|
||||||
{{ erpPriceInputFormatter(scope.row.totalPrice - scope.row.totalReceivablePrice) }}
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="最后跟进时间"
|
|
||||||
prop="contactLastTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="负责人" prop="ownerUserName" width="120" />
|
|
||||||
<el-table-column align="center" label="所属部门" prop="ownerUserDeptName" width="100px" />
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="更新时间"
|
|
||||||
prop="updateTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="创建时间"
|
|
||||||
prop="createTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="创建人" prop="creatorName" width="120" />
|
|
||||||
<el-table-column align="center" fixed="right" label="合同状态" prop="auditStatus" width="120">
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.CRM_AUDIT_STATUS" :value="scope.row.auditStatus" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column fixed="right" label="操作" width="90">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
link
|
|
||||||
v-hasPermi="['crm:contract:update']"
|
|
||||||
type="primary"
|
|
||||||
@click="handleProcessDetail(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 setup lang="ts" name="EndContract">
|
|
||||||
import { dateFormatter, dateFormatter2 } from '@/utils/formatTime'
|
|
||||||
import * as ContractApi from '@/api/crm/contract'
|
|
||||||
import { fenToYuanFormat } from '@/utils/formatter'
|
|
||||||
import { DICT_TYPE } from '@/utils/dict'
|
|
||||||
import { CONTRACT_EXPIRY_TYPE } from './common'
|
|
||||||
import { erpPriceInputFormatter, erpPriceTableColumnFormatter } from '@/utils'
|
|
||||||
|
|
||||||
const loading = ref(true) // 列表的加载中
|
|
||||||
const total = ref(0) // 列表的总页数
|
|
||||||
const list = ref([]) // 列表的数据
|
|
||||||
const queryParams = reactive({
|
|
||||||
pageNo: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
sceneType: '1', // 自己负责的
|
|
||||||
expiryType: 1
|
|
||||||
})
|
|
||||||
const queryFormRef = ref() // 搜索的表单
|
|
||||||
|
|
||||||
/** 查询列表 */
|
|
||||||
const getList = async () => {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
const data = await ContractApi.getContractPage(queryParams)
|
|
||||||
list.value = data.list
|
|
||||||
total.value = data.total
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 搜索按钮操作 */
|
|
||||||
const handleQuery = () => {
|
|
||||||
queryParams.pageNo = 1
|
|
||||||
getList()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 查看审批 */
|
|
||||||
const handleProcessDetail = (row: ContractApi.ContractVO) => {
|
|
||||||
push({ name: 'BpmProcessInstanceDetail', query: { id: row.processInstanceId } })
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 打开合同详情 */
|
|
||||||
const { push } = useRouter()
|
|
||||||
const openDetail = (id: number) => {
|
|
||||||
push({ name: 'CrmContractDetail', params: { id } })
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 打开客户详情 */
|
|
||||||
const openCustomerDetail = (id: number) => {
|
|
||||||
push({ name: 'CrmCustomerDetail', params: { id } })
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 打开联系人详情 */
|
|
||||||
const openContactDetail = (id: number) => {
|
|
||||||
push({ name: 'CrmContactDetail', params: { id } })
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 打开商机详情 */
|
|
||||||
const openBusinessDetail = (id: number) => {
|
|
||||||
push({ name: 'CrmBusinessDetail', params: { id } })
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 激活时 */
|
|
||||||
onActivated(async () => {
|
|
||||||
await getList()
|
|
||||||
})
|
|
||||||
|
|
||||||
/** 初始化 **/
|
|
||||||
onMounted(() => {
|
|
||||||
getList()
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,170 +0,0 @@
|
||||||
<!-- 分配给我的客户 -->
|
|
||||||
<!-- WHERE followUpStatus = ? -->
|
|
||||||
<template>
|
|
||||||
<ContentWrap>
|
|
||||||
<div class="pb-5 text-xl">分配给我的客户</div>
|
|
||||||
<!-- 搜索工作栏 -->
|
|
||||||
<el-form
|
|
||||||
ref="queryFormRef"
|
|
||||||
:inline="true"
|
|
||||||
:model="queryParams"
|
|
||||||
class="-mb-15px"
|
|
||||||
label-width="68px"
|
|
||||||
>
|
|
||||||
<el-form-item label="状态" prop="followUpStatus">
|
|
||||||
<el-select
|
|
||||||
v-model="queryParams.followUpStatus"
|
|
||||||
class="!w-240px"
|
|
||||||
placeholder="状态"
|
|
||||||
@change="handleQuery"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="(option, index) in FOLLOWUP_STATUS"
|
|
||||||
:label="option.label"
|
|
||||||
:value="option.value"
|
|
||||||
:key="index"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</ContentWrap>
|
|
||||||
<!-- 列表 -->
|
|
||||||
<ContentWrap>
|
|
||||||
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true">
|
|
||||||
<el-table-column align="center" label="客户名称" fixed="left" prop="name" width="160">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-link :underline="false" type="primary" @click="openDetail(scope.row.id)">
|
|
||||||
{{ scope.row.name }}
|
|
||||||
</el-link>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="客户来源" prop="source" width="100">
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_SOURCE" :value="scope.row.source" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="手机" align="center" prop="mobile" width="120" />
|
|
||||||
<el-table-column label="电话" align="center" prop="telephone" width="130" />
|
|
||||||
<el-table-column label="邮箱" align="center" prop="email" width="180" />
|
|
||||||
<el-table-column align="center" label="客户级别" prop="level" width="135">
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_LEVEL" :value="scope.row.level" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="客户行业" prop="industryId" width="100">
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_INDUSTRY" :value="scope.row.industryId" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="下次联系时间"
|
|
||||||
prop="contactNextTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="备注" prop="remark" width="200" />
|
|
||||||
<el-table-column align="center" label="锁定状态" prop="lockStatus">
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.lockStatus" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="成交状态" prop="dealStatus">
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.dealStatus" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="最后跟进时间"
|
|
||||||
prop="contactLastTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="最后跟进记录" prop="contactLastContent" width="200" />
|
|
||||||
<el-table-column label="地址" align="center" prop="detailAddress" width="180" />
|
|
||||||
<el-table-column align="center" label="距离进入公海天数" prop="poolDay" width="140">
|
|
||||||
<template #default="scope"> {{ scope.row.poolDay }} 天</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="负责人" prop="ownerUserName" width="100px" />
|
|
||||||
<el-table-column align="center" label="所属部门" prop="ownerUserDeptName" width="100px" />
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="更新时间"
|
|
||||||
prop="updateTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="创建时间"
|
|
||||||
prop="createTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="创建人" prop="creatorName" width="100px" />
|
|
||||||
</el-table>
|
|
||||||
<!-- 分页 -->
|
|
||||||
<Pagination
|
|
||||||
v-model:limit="queryParams.pageSize"
|
|
||||||
v-model:page="queryParams.pageNo"
|
|
||||||
:total="total"
|
|
||||||
@pagination="getList"
|
|
||||||
/>
|
|
||||||
</ContentWrap>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import * as CustomerApi from '@/api/crm/customer'
|
|
||||||
import { DICT_TYPE } from '@/utils/dict'
|
|
||||||
import { dateFormatter } from '@/utils/formatTime'
|
|
||||||
import { FOLLOWUP_STATUS } from './common'
|
|
||||||
|
|
||||||
defineOptions({ name: 'CrmCustomerFollowList' })
|
|
||||||
|
|
||||||
const { push } = useRouter()
|
|
||||||
|
|
||||||
const loading = ref(true) // 列表的加载中
|
|
||||||
const total = ref(0) // 列表的总页数
|
|
||||||
const list = ref([]) // 列表的数据
|
|
||||||
const queryParams = ref({
|
|
||||||
pageNo: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
sceneType: 1,
|
|
||||||
followUpStatus: false
|
|
||||||
})
|
|
||||||
const queryFormRef = ref() // 搜索的表单
|
|
||||||
|
|
||||||
/** 查询列表 */
|
|
||||||
const getList = async () => {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
const data = await CustomerApi.getCustomerPage(queryParams.value)
|
|
||||||
list.value = data.list
|
|
||||||
total.value = data.total
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 搜索按钮操作 */
|
|
||||||
const handleQuery = () => {
|
|
||||||
queryParams.value.pageNo = 1
|
|
||||||
getList()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 打开客户详情 */
|
|
||||||
const openDetail = (id: number) => {
|
|
||||||
push({ name: 'CrmCustomerDetail', params: { id } })
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 激活时 */
|
|
||||||
onActivated(async () => {
|
|
||||||
await getList()
|
|
||||||
})
|
|
||||||
|
|
||||||
/** 初始化 **/
|
|
||||||
onMounted(() => {
|
|
||||||
getList()
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,169 +0,0 @@
|
||||||
<!-- 待进入公海的客户 -->
|
|
||||||
<template>
|
|
||||||
<ContentWrap>
|
|
||||||
<div class="pb-5 text-xl"> 待进入公海的客户 </div>
|
|
||||||
<!-- 搜索工作栏 -->
|
|
||||||
<el-form
|
|
||||||
ref="queryFormRef"
|
|
||||||
:inline="true"
|
|
||||||
:model="queryParams"
|
|
||||||
class="-mb-15px"
|
|
||||||
label-width="68px"
|
|
||||||
>
|
|
||||||
<el-form-item label="归属" prop="sceneType">
|
|
||||||
<el-select
|
|
||||||
v-model="queryParams.sceneType"
|
|
||||||
class="!w-240px"
|
|
||||||
placeholder="归属"
|
|
||||||
@change="handleQuery"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="(option, index) in SCENE_TYPES"
|
|
||||||
:label="option.label"
|
|
||||||
:value="option.value"
|
|
||||||
:key="index"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</ContentWrap>
|
|
||||||
<ContentWrap>
|
|
||||||
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true">
|
|
||||||
<el-table-column align="center" label="客户名称" fixed="left" prop="name" width="160">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-link :underline="false" type="primary" @click="openDetail(scope.row.id)">
|
|
||||||
{{ scope.row.name }}
|
|
||||||
</el-link>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="客户来源" prop="source" width="100">
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_SOURCE" :value="scope.row.source" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="手机" align="center" prop="mobile" width="120" />
|
|
||||||
<el-table-column label="电话" align="center" prop="telephone" width="130" />
|
|
||||||
<el-table-column label="邮箱" align="center" prop="email" width="180" />
|
|
||||||
<el-table-column align="center" label="客户级别" prop="level" width="135">
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_LEVEL" :value="scope.row.level" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="客户行业" prop="industryId" width="100">
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_INDUSTRY" :value="scope.row.industryId" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="下次联系时间"
|
|
||||||
prop="contactNextTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="备注" prop="remark" width="200" />
|
|
||||||
<el-table-column align="center" label="锁定状态" prop="lockStatus">
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.lockStatus" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="成交状态" prop="dealStatus">
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.dealStatus" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="最后跟进时间"
|
|
||||||
prop="contactLastTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="最后跟进记录" prop="contactLastContent" width="200" />
|
|
||||||
<el-table-column label="地址" align="center" prop="detailAddress" width="180" />
|
|
||||||
<el-table-column align="center" label="距离进入公海天数" prop="poolDay" width="140">
|
|
||||||
<template #default="scope"> {{ scope.row.poolDay }} 天</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="负责人" prop="ownerUserName" width="100px" />
|
|
||||||
<el-table-column align="center" label="所属部门" prop="ownerUserDeptName" width="100px" />
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="更新时间"
|
|
||||||
prop="updateTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="创建时间"
|
|
||||||
prop="createTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="创建人" prop="creatorName" width="100px" />
|
|
||||||
</el-table>
|
|
||||||
<!-- 分页 -->
|
|
||||||
<Pagination
|
|
||||||
v-model:limit="queryParams.pageSize"
|
|
||||||
v-model:page="queryParams.pageNo"
|
|
||||||
:total="total"
|
|
||||||
@pagination="getList"
|
|
||||||
/>
|
|
||||||
</ContentWrap>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import * as CustomerApi from '@/api/crm/customer'
|
|
||||||
import { DICT_TYPE } from '@/utils/dict'
|
|
||||||
import { dateFormatter } from '@/utils/formatTime'
|
|
||||||
import { SCENE_TYPES } from './common'
|
|
||||||
|
|
||||||
defineOptions({ name: 'CrmCustomerPutPoolRemindList' })
|
|
||||||
|
|
||||||
const loading = ref(true) // 列表的加载中
|
|
||||||
const total = ref(0) // 列表的总页数
|
|
||||||
const list = ref([]) // 列表的数据
|
|
||||||
const queryParams = ref({
|
|
||||||
pageNo: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
sceneType: 1, // 我负责的
|
|
||||||
pool: true // 固定 公海参数为 true
|
|
||||||
})
|
|
||||||
const queryFormRef = ref() // 搜索的表单
|
|
||||||
|
|
||||||
/** 查询列表 */
|
|
||||||
const getList = async () => {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
const data = await CustomerApi.getPutPoolRemindCustomerPage(queryParams.value)
|
|
||||||
list.value = data.list
|
|
||||||
total.value = data.total
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 搜索按钮操作 */
|
|
||||||
const handleQuery = () => {
|
|
||||||
queryParams.value.pageNo = 1
|
|
||||||
getList()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 打开客户详情 */
|
|
||||||
const { push } = useRouter()
|
|
||||||
const openDetail = (id: number) => {
|
|
||||||
push({ name: 'CrmCustomerDetail', params: { id } })
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 激活时 */
|
|
||||||
onActivated(async () => {
|
|
||||||
await getList()
|
|
||||||
})
|
|
||||||
|
|
||||||
/** 初始化 **/
|
|
||||||
onMounted(() => {
|
|
||||||
getList()
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss"></style>
|
|
|
@ -1,180 +0,0 @@
|
||||||
<template>
|
|
||||||
<ContentWrap>
|
|
||||||
<div class="pb-5 text-xl"> 今日需联系客户 </div>
|
|
||||||
<!-- 搜索工作栏 -->
|
|
||||||
<el-form
|
|
||||||
ref="queryFormRef"
|
|
||||||
:inline="true"
|
|
||||||
:model="queryParams"
|
|
||||||
class="-mb-15px"
|
|
||||||
label-width="68px"
|
|
||||||
>
|
|
||||||
<el-form-item label="状态" prop="contactStatus">
|
|
||||||
<el-select
|
|
||||||
v-model="queryParams.contactStatus"
|
|
||||||
class="!w-240px"
|
|
||||||
placeholder="状态"
|
|
||||||
@change="handleQuery"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="(option, index) in CONTACT_STATUS"
|
|
||||||
:label="option.label"
|
|
||||||
:value="option.value"
|
|
||||||
:key="index"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="归属" prop="sceneType">
|
|
||||||
<el-select
|
|
||||||
v-model="queryParams.sceneType"
|
|
||||||
class="!w-240px"
|
|
||||||
placeholder="归属"
|
|
||||||
@change="handleQuery"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="(option, index) in SCENE_TYPES"
|
|
||||||
:label="option.label"
|
|
||||||
:value="option.value"
|
|
||||||
:key="index"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</ContentWrap>
|
|
||||||
<ContentWrap>
|
|
||||||
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true">
|
|
||||||
<el-table-column align="center" label="客户名称" fixed="left" prop="name" width="160">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-link :underline="false" type="primary" @click="openDetail(scope.row.id)">
|
|
||||||
{{ scope.row.name }}
|
|
||||||
</el-link>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="客户来源" prop="source" width="100">
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_SOURCE" :value="scope.row.source" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="手机" align="center" prop="mobile" width="120" />
|
|
||||||
<el-table-column label="电话" align="center" prop="telephone" width="130" />
|
|
||||||
<el-table-column label="邮箱" align="center" prop="email" width="180" />
|
|
||||||
<el-table-column align="center" label="客户级别" prop="level" width="135">
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_LEVEL" :value="scope.row.level" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="客户行业" prop="industryId" width="100">
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_INDUSTRY" :value="scope.row.industryId" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="下次联系时间"
|
|
||||||
prop="contactNextTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="备注" prop="remark" width="200" />
|
|
||||||
<el-table-column align="center" label="锁定状态" prop="lockStatus">
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.lockStatus" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="成交状态" prop="dealStatus">
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.dealStatus" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="最后跟进时间"
|
|
||||||
prop="contactLastTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="最后跟进记录" prop="contactLastContent" width="200" />
|
|
||||||
<el-table-column label="地址" align="center" prop="detailAddress" width="180" />
|
|
||||||
<el-table-column align="center" label="距离进入公海天数" prop="poolDay" width="140">
|
|
||||||
<template #default="scope"> {{ scope.row.poolDay }} 天</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="负责人" prop="ownerUserName" width="100px" />
|
|
||||||
<el-table-column align="center" label="所属部门" prop="ownerUserDeptName" width="100px" />
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="更新时间"
|
|
||||||
prop="updateTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="创建时间"
|
|
||||||
prop="createTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="创建人" prop="creatorName" width="100px" />
|
|
||||||
</el-table>
|
|
||||||
<!-- 分页 -->
|
|
||||||
<Pagination
|
|
||||||
v-model:limit="queryParams.pageSize"
|
|
||||||
v-model:page="queryParams.pageNo"
|
|
||||||
:total="total"
|
|
||||||
@pagination="getList"
|
|
||||||
/>
|
|
||||||
</ContentWrap>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import * as CustomerApi from '@/api/crm/customer'
|
|
||||||
import { DICT_TYPE } from '@/utils/dict'
|
|
||||||
import { dateFormatter } from '@/utils/formatTime'
|
|
||||||
import { CONTACT_STATUS, SCENE_TYPES } from './common'
|
|
||||||
|
|
||||||
defineOptions({ name: 'CrmCustomerTodayContactList' })
|
|
||||||
|
|
||||||
const { push } = useRouter()
|
|
||||||
|
|
||||||
const loading = ref(true) // 列表的加载中
|
|
||||||
const total = ref(0) // 列表的总页数
|
|
||||||
const list = ref([]) // 列表的数据
|
|
||||||
const queryParams = ref({
|
|
||||||
pageNo: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
contactStatus: 1,
|
|
||||||
sceneType: 1,
|
|
||||||
pool: null // 是否公海数据
|
|
||||||
})
|
|
||||||
const queryFormRef = ref() // 搜索的表单
|
|
||||||
|
|
||||||
/** 查询列表 */
|
|
||||||
const getList = async () => {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
const data = await CustomerApi.getCustomerPage(queryParams.value)
|
|
||||||
list.value = data.list
|
|
||||||
total.value = data.total
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 搜索按钮操作 */
|
|
||||||
const handleQuery = () => {
|
|
||||||
queryParams.value.pageNo = 1
|
|
||||||
getList()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 打开客户详情 */
|
|
||||||
const openDetail = (id: number) => {
|
|
||||||
push({ name: 'CrmCustomerDetail', params: { id } })
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 初始化 **/
|
|
||||||
onMounted(() => {
|
|
||||||
getList()
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss"></style>
|
|
|
@ -1,201 +0,0 @@
|
||||||
<!-- 待审核回款 -->
|
|
||||||
<template>
|
|
||||||
<ContentWrap>
|
|
||||||
<div class="pb-5 text-xl"> 待审核回款 </div>
|
|
||||||
<!-- 搜索工作栏 -->
|
|
||||||
<el-form
|
|
||||||
class="-mb-15px"
|
|
||||||
:model="queryParams"
|
|
||||||
ref="queryFormRef"
|
|
||||||
:inline="true"
|
|
||||||
label-width="68px"
|
|
||||||
>
|
|
||||||
<el-form-item label="合同状态" prop="auditStatus">
|
|
||||||
<el-select
|
|
||||||
v-model="queryParams.auditStatus"
|
|
||||||
class="!w-240px"
|
|
||||||
placeholder="状态"
|
|
||||||
@change="handleQuery"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="(option, index) in AUDIT_STATUS"
|
|
||||||
:label="option.label"
|
|
||||||
:value="option.value"
|
|
||||||
:key="index"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</ContentWrap>
|
|
||||||
<!-- 列表 -->
|
|
||||||
<ContentWrap>
|
|
||||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
|
||||||
<el-table-column align="center" fixed="left" label="回款编号" prop="no" width="180">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-link :underline="false" type="primary" @click="openDetail(scope.row.id)">
|
|
||||||
{{ scope.row.no }}
|
|
||||||
</el-link>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="客户名称" prop="customerName" width="120">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-link
|
|
||||||
:underline="false"
|
|
||||||
type="primary"
|
|
||||||
@click="openCustomerDetail(scope.row.customerId)"
|
|
||||||
>
|
|
||||||
{{ scope.row.customerName }}
|
|
||||||
</el-link>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="合同编号" prop="contractNo" width="180">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-link
|
|
||||||
:underline="false"
|
|
||||||
type="primary"
|
|
||||||
@click="openContractDetail(scope.row.contractId)"
|
|
||||||
>
|
|
||||||
{{ scope.row.contract.no }}
|
|
||||||
</el-link>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter2"
|
|
||||||
align="center"
|
|
||||||
label="回款日期"
|
|
||||||
prop="returnTime"
|
|
||||||
width="150px"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
align="center"
|
|
||||||
label="回款金额(元)"
|
|
||||||
prop="price"
|
|
||||||
width="140"
|
|
||||||
:formatter="erpPriceTableColumnFormatter"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="回款方式" prop="returnType" width="130px">
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.CRM_RECEIVABLE_RETURN_TYPE" :value="scope.row.returnType" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="备注" prop="remark" width="200" />
|
|
||||||
<el-table-column
|
|
||||||
align="center"
|
|
||||||
label="合同金额(元)"
|
|
||||||
prop="contract.totalPrice"
|
|
||||||
width="140"
|
|
||||||
:formatter="erpPriceTableColumnFormatter"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="负责人" prop="ownerUserName" width="120" />
|
|
||||||
<el-table-column align="center" label="所属部门" prop="ownerUserDeptName" width="100px" />
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="更新时间"
|
|
||||||
prop="updateTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="创建时间"
|
|
||||||
prop="createTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="创建人" prop="creatorName" width="120" />
|
|
||||||
<el-table-column align="center" fixed="right" label="回款状态" prop="auditStatus" width="120">
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.CRM_AUDIT_STATUS" :value="scope.row.auditStatus" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" fixed="right" label="操作" width="180px">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
v-hasPermi="['crm:receivable:update']"
|
|
||||||
link
|
|
||||||
type="primary"
|
|
||||||
@click="handleProcessDetail(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 setup lang="ts">
|
|
||||||
import { DICT_TYPE } from '@/utils/dict'
|
|
||||||
import { dateFormatter, dateFormatter2 } from '@/utils/formatTime'
|
|
||||||
import * as ReceivableApi from '@/api/crm/receivable'
|
|
||||||
import { AUDIT_STATUS } from './common'
|
|
||||||
import { erpPriceTableColumnFormatter } from '@/utils'
|
|
||||||
|
|
||||||
defineOptions({ name: 'CrmReceivableAuditList' })
|
|
||||||
|
|
||||||
const loading = ref(true) // 列表的加载中
|
|
||||||
const total = ref(0) // 列表的总页数
|
|
||||||
const list = ref([]) // 列表的数据
|
|
||||||
const queryParams = reactive({
|
|
||||||
pageNo: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
auditStatus: 10
|
|
||||||
})
|
|
||||||
const queryFormRef = ref() // 搜索的表单
|
|
||||||
|
|
||||||
/** 查询列表 */
|
|
||||||
const getList = async () => {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
const data = await ReceivableApi.getReceivablePage(queryParams)
|
|
||||||
list.value = data.list
|
|
||||||
total.value = data.total
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 搜索按钮操作 */
|
|
||||||
const handleQuery = () => {
|
|
||||||
queryParams.pageNo = 1
|
|
||||||
getList()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 查看审批 */
|
|
||||||
const handleProcessDetail = (row: ReceivableApi.ReceivableVO) => {
|
|
||||||
push({ name: 'BpmProcessInstanceDetail', query: { id: row.processInstanceId } })
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 打开回款详情 */
|
|
||||||
const { push } = useRouter()
|
|
||||||
const openDetail = (id: number) => {
|
|
||||||
push({ name: 'CrmReceivableDetail', params: { id } })
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 打开客户详情 */
|
|
||||||
const openCustomerDetail = (id: number) => {
|
|
||||||
push({ name: 'CrmCustomerDetail', params: { id } })
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 打开合同详情 */
|
|
||||||
const openContractDetail = (id: number) => {
|
|
||||||
push({ name: 'CrmContractDetail', params: { id } })
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 激活时 */
|
|
||||||
onActivated(async () => {
|
|
||||||
await getList()
|
|
||||||
})
|
|
||||||
|
|
||||||
/** 初始化 **/
|
|
||||||
onMounted(() => {
|
|
||||||
getList()
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,220 +0,0 @@
|
||||||
<!-- 待回款提醒 -->
|
|
||||||
<template>
|
|
||||||
<ContentWrap>
|
|
||||||
<div class="pb-5 text-xl">待回款提醒</div>
|
|
||||||
<!-- 搜索工作栏 -->
|
|
||||||
<el-form
|
|
||||||
ref="queryFormRef"
|
|
||||||
:inline="true"
|
|
||||||
:model="queryParams"
|
|
||||||
class="-mb-15px"
|
|
||||||
label-width="68px"
|
|
||||||
>
|
|
||||||
<el-form-item label="合同状态" prop="remindType">
|
|
||||||
<el-select
|
|
||||||
v-model="queryParams.remindType"
|
|
||||||
class="!w-240px"
|
|
||||||
placeholder="状态"
|
|
||||||
@change="handleQuery"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="(option, index) in RECEIVABLE_REMIND_TYPE"
|
|
||||||
:label="option.label"
|
|
||||||
:value="option.value"
|
|
||||||
:key="index"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</ContentWrap>
|
|
||||||
|
|
||||||
<ContentWrap>
|
|
||||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
|
||||||
<el-table-column align="center" fixed="left" label="客户名称" prop="customerName" width="150">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-link
|
|
||||||
:underline="false"
|
|
||||||
type="primary"
|
|
||||||
@click="openCustomerDetail(scope.row.customerId)"
|
|
||||||
>
|
|
||||||
{{ scope.row.customerName }}
|
|
||||||
</el-link>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="合同编号" prop="contractNo" width="200px" />
|
|
||||||
<el-table-column align="center" label="期数" prop="period">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-link :underline="false" type="primary" @click="openDetail(scope.row.id)">
|
|
||||||
{{ scope.row.period }}
|
|
||||||
</el-link>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
align="center"
|
|
||||||
label="计划回款金额(元)"
|
|
||||||
prop="price"
|
|
||||||
width="160"
|
|
||||||
:formatter="erpPriceTableColumnFormatter"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter2"
|
|
||||||
align="center"
|
|
||||||
label="计划回款日期"
|
|
||||||
prop="returnTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="提前几天提醒" prop="remindDays" width="150" />
|
|
||||||
<el-table-column
|
|
||||||
align="center"
|
|
||||||
label="提醒日期"
|
|
||||||
prop="remindTime"
|
|
||||||
width="180px"
|
|
||||||
:formatter="dateFormatter2"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="回款方式" prop="returnType" width="130px">
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.CRM_RECEIVABLE_RETURN_TYPE" :value="scope.row.returnType" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="备注" prop="remark" />
|
|
||||||
<el-table-column label="负责人" prop="ownerUserName" width="120" />
|
|
||||||
<el-table-column
|
|
||||||
align="center"
|
|
||||||
label="实际回款金额(元)"
|
|
||||||
prop="receivable.price"
|
|
||||||
width="160"
|
|
||||||
>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-text v-if="scope.row.receivable">
|
|
||||||
{{ erpPriceInputFormatter(scope.row.receivable.price) }}
|
|
||||||
</el-text>
|
|
||||||
<el-text v-else>{{ erpPriceInputFormatter(0) }}</el-text>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
align="center"
|
|
||||||
label="实际回款日期"
|
|
||||||
prop="receivable.returnTime"
|
|
||||||
width="180px"
|
|
||||||
:formatter="dateFormatter2"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
align="center"
|
|
||||||
label="实际回款金额(元)"
|
|
||||||
prop="receivable.price"
|
|
||||||
width="160"
|
|
||||||
>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-text v-if="scope.row.receivable">
|
|
||||||
{{ erpPriceInputFormatter(scope.row.price - scope.row.receivable.price) }}
|
|
||||||
</el-text>
|
|
||||||
<el-text v-else>{{ erpPriceInputFormatter(scope.row.price) }}</el-text>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="更新时间"
|
|
||||||
prop="updateTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="创建时间"
|
|
||||||
prop="createTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="创建人" prop="creatorName" width="100px" />
|
|
||||||
<el-table-column align="center" fixed="right" label="操作" width="180px">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
v-hasPermi="['crm:receivable:create']"
|
|
||||||
link
|
|
||||||
type="success"
|
|
||||||
@click="openReceivableForm(scope.row)"
|
|
||||||
:disabled="scope.row.receivableId"
|
|
||||||
>
|
|
||||||
创建回款
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
<!-- 分页 -->
|
|
||||||
<Pagination
|
|
||||||
:total="total"
|
|
||||||
v-model:page="queryParams.pageNo"
|
|
||||||
v-model:limit="queryParams.pageSize"
|
|
||||||
@pagination="getList"
|
|
||||||
/>
|
|
||||||
</ContentWrap>
|
|
||||||
|
|
||||||
<!-- 表单弹窗:添加/修改 -->
|
|
||||||
<ReceivableForm ref="receivableFormRef" @success="getList" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { DICT_TYPE } from '@/utils/dict'
|
|
||||||
import { dateFormatter, dateFormatter2 } from '@/utils/formatTime'
|
|
||||||
import * as ReceivablePlanApi from '@/api/crm/receivable/plan'
|
|
||||||
import { RECEIVABLE_REMIND_TYPE } from './common'
|
|
||||||
import { erpPriceInputFormatter, erpPriceTableColumnFormatter } from '@/utils'
|
|
||||||
import ReceivableForm from '@/views/crm/receivable/ReceivableForm.vue'
|
|
||||||
|
|
||||||
defineOptions({ name: 'ReceivablePlanRemindList' })
|
|
||||||
|
|
||||||
const loading = ref(true) // 列表的加载中
|
|
||||||
const total = ref(0) // 列表的总页数
|
|
||||||
const list = ref([]) // 列表的数据
|
|
||||||
const queryParams = reactive({
|
|
||||||
pageNo: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
remindType: 1
|
|
||||||
})
|
|
||||||
const queryFormRef = ref() // 搜索的表单
|
|
||||||
|
|
||||||
/** 查询列表 */
|
|
||||||
const getList = async () => {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
const data = await ReceivablePlanApi.getReceivablePlanPage(queryParams)
|
|
||||||
list.value = data.list
|
|
||||||
total.value = data.total
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 搜索按钮操作 */
|
|
||||||
const handleQuery = () => {
|
|
||||||
queryParams.pageNo = 1
|
|
||||||
getList()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 创建回款操作 */
|
|
||||||
const receivableFormRef = ref()
|
|
||||||
const openReceivableForm = (row: ReceivablePlanApi.ReceivablePlanVO) => {
|
|
||||||
receivableFormRef.value.open('create', undefined, row)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 打开详情 */
|
|
||||||
const { push } = useRouter()
|
|
||||||
const openDetail = (id: number) => {
|
|
||||||
push({ name: 'CrmReceivablePlanDetail', params: { id } })
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 打开客户详情 */
|
|
||||||
const openCustomerDetail = (id: number) => {
|
|
||||||
push({ name: 'CrmCustomerDetail', params: { id } })
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 激活时 */
|
|
||||||
onActivated(async () => {
|
|
||||||
await getList()
|
|
||||||
})
|
|
||||||
|
|
||||||
/** 初始化 **/
|
|
||||||
onMounted(async () => {
|
|
||||||
await getList()
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,39 +0,0 @@
|
||||||
/** 跟进状态 */
|
|
||||||
export const FOLLOWUP_STATUS = [
|
|
||||||
{ label: '待跟进', value: false },
|
|
||||||
{ label: '已跟进', value: true }
|
|
||||||
]
|
|
||||||
|
|
||||||
/** 归属范围 */
|
|
||||||
export const SCENE_TYPES = [
|
|
||||||
{ label: '我负责的', value: 1 },
|
|
||||||
{ label: '我参与的', value: 2 },
|
|
||||||
{ label: '下属负责的', value: 3 }
|
|
||||||
]
|
|
||||||
|
|
||||||
/** 联系状态 */
|
|
||||||
export const CONTACT_STATUS = [
|
|
||||||
{ label: '今日需联系', value: 1 },
|
|
||||||
{ label: '已逾期', value: 2 },
|
|
||||||
{ label: '已联系', value: 3 }
|
|
||||||
]
|
|
||||||
|
|
||||||
/** 审批状态 */
|
|
||||||
export const AUDIT_STATUS = [
|
|
||||||
{ label: '待审批', value: 10 },
|
|
||||||
{ label: '审核通过', value: 20 },
|
|
||||||
{ label: '审核不通过', value: 30 }
|
|
||||||
]
|
|
||||||
|
|
||||||
/** 回款提醒类型 */
|
|
||||||
export const RECEIVABLE_REMIND_TYPE = [
|
|
||||||
{ label: '待回款', value: 1 },
|
|
||||||
{ label: '已逾期', value: 2 },
|
|
||||||
{ label: '已回款', value: 3 }
|
|
||||||
]
|
|
||||||
|
|
||||||
/** 合同过期状态 */
|
|
||||||
export const CONTRACT_EXPIRY_TYPE = [
|
|
||||||
{ label: '即将过期', value: 1 },
|
|
||||||
{ label: '已过期', value: 2 }
|
|
||||||
]
|
|
|
@ -1,177 +0,0 @@
|
||||||
<template>
|
|
||||||
<!-- <doc-alert title="【通用】跟进记录、待办事项" url="https://doc.iocoder.cn/crm/follow-up/" /> -->
|
|
||||||
|
|
||||||
<el-row :gutter="20">
|
|
||||||
<el-col :span="4" class="min-w-[200px]">
|
|
||||||
<div class="side-item-list">
|
|
||||||
<div
|
|
||||||
v-for="(item, index) in leftSides"
|
|
||||||
:key="index"
|
|
||||||
:class="leftMenu == item.menu ? 'side-item-select' : 'side-item-default'"
|
|
||||||
class="side-item"
|
|
||||||
@click="sideClick(item)"
|
|
||||||
>
|
|
||||||
{{ item.name }}
|
|
||||||
<el-badge v-if="item.count > 0" :max="99" :value="item.count" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="20" :xs="24">
|
|
||||||
<CustomerTodayContactList v-if="leftMenu === 'customerTodayContact'" />
|
|
||||||
<ClueFollowList v-if="leftMenu === 'clueFollow'" />
|
|
||||||
<ContractAuditList v-if="leftMenu === 'contractAudit'" />
|
|
||||||
<ReceivableAuditList v-if="leftMenu === 'receivableAudit'" />
|
|
||||||
<ContractRemindList v-if="leftMenu === 'contractRemind'" />
|
|
||||||
<CustomerFollowList v-if="leftMenu === 'customerFollow'" />
|
|
||||||
<CustomerPutPoolRemindList v-if="leftMenu === 'customerPutPoolRemind'" />
|
|
||||||
<ReceivablePlanRemindList v-if="leftMenu === 'receivablePlanRemind'" />
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import CustomerFollowList from './components/CustomerFollowList.vue'
|
|
||||||
import CustomerTodayContactList from './components/CustomerTodayContactList.vue'
|
|
||||||
import CustomerPutPoolRemindList from './components/CustomerPutPoolRemindList.vue'
|
|
||||||
import ClueFollowList from './components/ClueFollowList.vue'
|
|
||||||
import ContractAuditList from './components/ContractAuditList.vue'
|
|
||||||
import ContractRemindList from './components/ContractRemindList.vue'
|
|
||||||
import ReceivablePlanRemindList from './components/ReceivablePlanRemindList.vue'
|
|
||||||
import ReceivableAuditList from './components/ReceivableAuditList.vue'
|
|
||||||
import * as CustomerApi from '@/api/crm/customer'
|
|
||||||
import * as ClueApi from '@/api/crm/clue'
|
|
||||||
import * as ContractApi from '@/api/crm/contract'
|
|
||||||
import * as ReceivableApi from '@/api/crm/receivable'
|
|
||||||
import * as ReceivablePlanApi from '@/api/crm/receivable/plan'
|
|
||||||
|
|
||||||
defineOptions({ name: 'CrmBacklog' })
|
|
||||||
|
|
||||||
const leftMenu = ref('customerTodayContact')
|
|
||||||
|
|
||||||
const clueFollowCount = ref(0)
|
|
||||||
const customerFollowCount = ref(0)
|
|
||||||
const customerPutPoolRemindCount = ref(0)
|
|
||||||
const customerTodayContactCount = ref(0)
|
|
||||||
const contractAuditCount = ref(0)
|
|
||||||
const contractRemindCount = ref(0)
|
|
||||||
const receivableAuditCount = ref(0)
|
|
||||||
const receivablePlanRemindCount = ref(0)
|
|
||||||
|
|
||||||
const leftSides = ref([
|
|
||||||
{
|
|
||||||
name: '今日需联系客户',
|
|
||||||
menu: 'customerTodayContact',
|
|
||||||
count: customerTodayContactCount
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '分配给我的线索',
|
|
||||||
menu: 'clueFollow',
|
|
||||||
count: clueFollowCount
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '分配给我的客户',
|
|
||||||
menu: 'customerFollow',
|
|
||||||
count: customerFollowCount
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '待进入公海的客户',
|
|
||||||
menu: 'customerPutPoolRemind',
|
|
||||||
count: customerPutPoolRemindCount
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '待审核合同',
|
|
||||||
menu: 'contractAudit',
|
|
||||||
count: contractAuditCount
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '待审核回款',
|
|
||||||
menu: 'receivableAudit',
|
|
||||||
count: receivableAuditCount
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '待回款提醒',
|
|
||||||
menu: 'receivablePlanRemind',
|
|
||||||
count: receivablePlanRemindCount
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '即将到期的合同',
|
|
||||||
menu: 'contractRemind',
|
|
||||||
count: contractRemindCount
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
/** 侧边点击 */
|
|
||||||
const sideClick = (item: any) => {
|
|
||||||
leftMenu.value = item.menu
|
|
||||||
}
|
|
||||||
|
|
||||||
const getCount = () => {
|
|
||||||
CustomerApi.getTodayContactCustomerCount().then(
|
|
||||||
(count) => (customerTodayContactCount.value = count)
|
|
||||||
)
|
|
||||||
CustomerApi.getPutPoolRemindCustomerCount().then(
|
|
||||||
(count) => (customerPutPoolRemindCount.value = count)
|
|
||||||
)
|
|
||||||
CustomerApi.getFollowCustomerCount().then((count) => (customerFollowCount.value = count))
|
|
||||||
ClueApi.getFollowClueCount().then((count) => (clueFollowCount.value = count))
|
|
||||||
ContractApi.getAuditContractCount().then((count) => (contractAuditCount.value = count))
|
|
||||||
ContractApi.getRemindContractCount().then((count) => (contractRemindCount.value = count))
|
|
||||||
ReceivableApi.getAuditReceivableCount().then((count) => (receivableAuditCount.value = count))
|
|
||||||
ReceivablePlanApi.getReceivablePlanRemindCount().then(
|
|
||||||
(count) => (receivablePlanRemindCount.value = count)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 激活时 */
|
|
||||||
onActivated(async () => {
|
|
||||||
getCount()
|
|
||||||
})
|
|
||||||
|
|
||||||
/** 初始化 */
|
|
||||||
onMounted(async () => {
|
|
||||||
getCount()
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.side-item-list {
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
z-index: 1;
|
|
||||||
font-size: 14px;
|
|
||||||
background-color: var(--el-bg-color);
|
|
||||||
border: 1px solid var(--el-border-color);
|
|
||||||
border-radius: 5px;
|
|
||||||
|
|
||||||
.side-item {
|
|
||||||
position: relative;
|
|
||||||
height: 50px;
|
|
||||||
padding: 0 20px;
|
|
||||||
line-height: 50px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.side-item-default {
|
|
||||||
color: var(--el-text-color-primary);
|
|
||||||
border-right: 2px solid transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.side-item-select {
|
|
||||||
color: var(--el-color-primary);
|
|
||||||
background-color: var(--el-color-primary-light-9);
|
|
||||||
border-right: 2px solid var(--el-color-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-badge :deep(.el-badge__content) {
|
|
||||||
top: 0;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-badge {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: 15px;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,287 +0,0 @@
|
||||||
<template>
|
|
||||||
<Dialog :title="dialogTitle" v-model="dialogVisible" width="1280">
|
|
||||||
<el-form
|
|
||||||
ref="formRef"
|
|
||||||
:model="formData"
|
|
||||||
:rules="formRules"
|
|
||||||
label-width="120px"
|
|
||||||
v-loading="formLoading"
|
|
||||||
>
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="8">
|
|
||||||
<el-form-item label="商机名称" prop="name">
|
|
||||||
<el-input v-model="formData.name" placeholder="请输入商机名称" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="8">
|
|
||||||
<el-form-item label="负责人" prop="ownerUserId">
|
|
||||||
<el-select
|
|
||||||
v-model="formData.ownerUserId"
|
|
||||||
:disabled="formType !== 'create'"
|
|
||||||
class="w-1/1"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="item in userOptions"
|
|
||||||
:key="item.id"
|
|
||||||
:label="item.nickname"
|
|
||||||
:value="item.id"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="8">
|
|
||||||
<el-form-item label="客户名称" prop="customerId">
|
|
||||||
<el-select
|
|
||||||
:disabled="formData.customerDefault"
|
|
||||||
v-model="formData.customerId"
|
|
||||||
placeholder="请选择客户"
|
|
||||||
class="w-1/1"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="item in customerList"
|
|
||||||
:key="item.id"
|
|
||||||
:label="item.name"
|
|
||||||
:value="item.id"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="8">
|
|
||||||
<el-form-item label="商机状态组" prop="statusTypeId">
|
|
||||||
<el-select
|
|
||||||
v-model="formData.statusTypeId"
|
|
||||||
placeholder="请选择商机状态组"
|
|
||||||
clearable
|
|
||||||
class="w-1/1"
|
|
||||||
:disabled="formType !== 'create'"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="item in statusTypeList"
|
|
||||||
:key="item.id"
|
|
||||||
:label="item.name"
|
|
||||||
:value="item.id"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="8">
|
|
||||||
<el-form-item label="预计成交日期" prop="dealTime">
|
|
||||||
<el-date-picker
|
|
||||||
v-model="formData.dealTime"
|
|
||||||
type="date"
|
|
||||||
value-format="x"
|
|
||||||
placeholder="选择预计成交日期"
|
|
||||||
class="!w-1/1"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="8">
|
|
||||||
<el-form-item label="备注" prop="remark">
|
|
||||||
<el-input type="textarea" v-model="formData.remark" placeholder="请输入备注" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<!-- 子表的表单 -->
|
|
||||||
<ContentWrap>
|
|
||||||
<el-tabs v-model="subTabsName" class="-mt-15px -mb-10px">
|
|
||||||
<el-tab-pane label="产品清单" name="product">
|
|
||||||
<BusinessProductForm
|
|
||||||
ref="productFormRef"
|
|
||||||
:products="formData.products"
|
|
||||||
:disabled="disabled"
|
|
||||||
/>
|
|
||||||
</el-tab-pane>
|
|
||||||
</el-tabs>
|
|
||||||
</ContentWrap>
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="8">
|
|
||||||
<el-form-item label="产品总金额" prop="totalProductPrice">
|
|
||||||
<el-input
|
|
||||||
disabled
|
|
||||||
v-model="formData.totalProductPrice"
|
|
||||||
:formatter="erpPriceTableColumnFormatter"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="8">
|
|
||||||
<el-form-item label="整单折扣(%)" prop="discountPercent">
|
|
||||||
<el-input-number
|
|
||||||
v-model="formData.discountPercent"
|
|
||||||
placeholder="请输入整单折扣"
|
|
||||||
controls-position="right"
|
|
||||||
:min="0"
|
|
||||||
:precision="2"
|
|
||||||
class="!w-1/1"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="8">
|
|
||||||
<el-form-item label="折扣后金额" prop="price">
|
|
||||||
<el-input
|
|
||||||
disabled
|
|
||||||
v-model="formData.totalPrice"
|
|
||||||
placeholder="请输入商机金额"
|
|
||||||
:formatter="erpPriceTableColumnFormatter"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</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 * as BusinessApi from '@/api/crm/business'
|
|
||||||
import * as BusinessStatusApi from '@/api/crm/business/status'
|
|
||||||
import * as CustomerApi from '@/api/crm/customer'
|
|
||||||
import * as UserApi from '@/api/system/user'
|
|
||||||
import { useUserStore } from '@/store/modules/user'
|
|
||||||
import BusinessProductForm from './components/BusinessProductForm.vue'
|
|
||||||
import { erpPriceMultiply, erpPriceTableColumnFormatter } from '@/utils'
|
|
||||||
|
|
||||||
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,
|
|
||||||
customerId: undefined,
|
|
||||||
ownerUserId: undefined,
|
|
||||||
statusTypeId: undefined,
|
|
||||||
dealTime: undefined,
|
|
||||||
discountPercent: 0,
|
|
||||||
totalProductPrice: undefined,
|
|
||||||
totalPrice: undefined,
|
|
||||||
remark: undefined,
|
|
||||||
products: [],
|
|
||||||
contactId: undefined,
|
|
||||||
customerDefault: false
|
|
||||||
})
|
|
||||||
const formRules = reactive({
|
|
||||||
name: [{ required: true, message: '商机名称不能为空', trigger: 'blur' }],
|
|
||||||
customerId: [{ required: true, message: '客户不能为空', trigger: 'blur' }],
|
|
||||||
ownerUserId: [{ required: true, message: '负责人不能为空', trigger: 'blur' }],
|
|
||||||
statusTypeId: [{ required: true, message: '商机状态组不能为空', trigger: 'blur' }]
|
|
||||||
})
|
|
||||||
const formRef = ref() // 表单 Ref
|
|
||||||
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
|
|
||||||
const statusTypeList = ref([]) // 商机状态类型列表
|
|
||||||
const customerList = ref([]) // 客户列表的数据
|
|
||||||
|
|
||||||
/** 子表的表单 */
|
|
||||||
const subTabsName = ref('product')
|
|
||||||
const productFormRef = ref()
|
|
||||||
|
|
||||||
/** 计算 discountPrice、totalPrice 价格 */
|
|
||||||
watch(
|
|
||||||
() => formData.value,
|
|
||||||
(val) => {
|
|
||||||
if (!val) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const totalProductPrice = val.products.reduce((prev, curr) => prev + curr.totalPrice, 0)
|
|
||||||
const discountPrice =
|
|
||||||
val.discountPercent != null
|
|
||||||
? erpPriceMultiply(totalProductPrice, val.discountPercent / 100.0)
|
|
||||||
: 0
|
|
||||||
const totalPrice = totalProductPrice - discountPrice
|
|
||||||
// 赋值
|
|
||||||
formData.value.totalProductPrice = totalProductPrice
|
|
||||||
formData.value.totalPrice = totalPrice
|
|
||||||
},
|
|
||||||
{ deep: true }
|
|
||||||
)
|
|
||||||
|
|
||||||
/** 打开弹窗 */
|
|
||||||
const open = async (type: string, id?: number, customerId?: number, contactId?: number) => {
|
|
||||||
dialogVisible.value = true
|
|
||||||
dialogTitle.value = t('action.' + type)
|
|
||||||
formType.value = type
|
|
||||||
resetForm()
|
|
||||||
// 修改时,设置数据
|
|
||||||
if (id) {
|
|
||||||
formLoading.value = true
|
|
||||||
try {
|
|
||||||
formData.value = await BusinessApi.getBusiness(id)
|
|
||||||
} finally {
|
|
||||||
formLoading.value = false
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (customerId) {
|
|
||||||
formData.value.customerId = customerId
|
|
||||||
formData.value.customerDefault = true // 默认客户的选择,不允许变
|
|
||||||
}
|
|
||||||
// 自动关联 contactId 联系人编号
|
|
||||||
if (contactId) {
|
|
||||||
formData.value.contactId = contactId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 获得客户列表
|
|
||||||
customerList.value = await CustomerApi.getCustomerSimpleList()
|
|
||||||
// 加载商机状态类型列表
|
|
||||||
statusTypeList.value = await BusinessStatusApi.getBusinessStatusTypeSimpleList()
|
|
||||||
// 获得用户列表
|
|
||||||
userOptions.value = await UserApi.getSimpleUserList()
|
|
||||||
// 默认新建时选中自己
|
|
||||||
if (formType.value === 'create') {
|
|
||||||
formData.value.ownerUserId = useUserStore().getUser.id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
|
||||||
|
|
||||||
/** 提交表单 */
|
|
||||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
|
||||||
const submitForm = async () => {
|
|
||||||
// 校验表单
|
|
||||||
if (!formRef) return
|
|
||||||
const valid = await formRef.value.validate()
|
|
||||||
if (!valid) return
|
|
||||||
await productFormRef.value.validate()
|
|
||||||
// 提交请求
|
|
||||||
formLoading.value = true
|
|
||||||
try {
|
|
||||||
const data = formData.value as unknown as BusinessApi.BusinessVO
|
|
||||||
if (formType.value === 'create') {
|
|
||||||
await BusinessApi.createBusiness(data)
|
|
||||||
message.success(t('common.createSuccess'))
|
|
||||||
} else {
|
|
||||||
await BusinessApi.updateBusiness(data)
|
|
||||||
message.success(t('common.updateSuccess'))
|
|
||||||
}
|
|
||||||
dialogVisible.value = false
|
|
||||||
// 发送操作成功的事件
|
|
||||||
emit('success')
|
|
||||||
} finally {
|
|
||||||
formLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 重置表单 */
|
|
||||||
const resetForm = () => {
|
|
||||||
formData.value = {
|
|
||||||
id: undefined,
|
|
||||||
name: undefined,
|
|
||||||
customerId: undefined,
|
|
||||||
ownerUserId: undefined,
|
|
||||||
statusTypeId: undefined,
|
|
||||||
dealTime: undefined,
|
|
||||||
discountPercent: 0,
|
|
||||||
totalProductPrice: undefined,
|
|
||||||
totalPrice: undefined,
|
|
||||||
remark: undefined,
|
|
||||||
products: [],
|
|
||||||
contactId: undefined,
|
|
||||||
customerDefault: false
|
|
||||||
}
|
|
||||||
formRef.value?.resetFields()
|
|
||||||
}
|
|
||||||
</script>
|
|
|
@ -1,108 +0,0 @@
|
||||||
<template>
|
|
||||||
<Dialog title="变更商机状态" v-model="dialogVisible" width="400">
|
|
||||||
<el-form
|
|
||||||
ref="formRef"
|
|
||||||
:model="formData"
|
|
||||||
:rules="formRules"
|
|
||||||
label-width="80px"
|
|
||||||
v-loading="formLoading"
|
|
||||||
>
|
|
||||||
<el-form-item label="商机阶段" prop="status">
|
|
||||||
<el-select v-model="formData.status" placeholder="请选择商机阶段" class="w-1/1">
|
|
||||||
<el-option
|
|
||||||
v-for="item in statusList"
|
|
||||||
:key="item.id"
|
|
||||||
:label="item.name + '(赢单率:' + item.percent + '%)'"
|
|
||||||
:value="item.id"
|
|
||||||
/>
|
|
||||||
<el-option
|
|
||||||
v-for="item in BusinessStatusApi.DEFAULT_STATUSES"
|
|
||||||
:key="item.endStatus"
|
|
||||||
:label="item.name + '(赢单率:' + item.percent + '%)'"
|
|
||||||
:value="-item.endStatus"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</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 * as BusinessApi from '@/api/crm/business'
|
|
||||||
import * as BusinessStatusApi from '@/api/crm/business/status'
|
|
||||||
|
|
||||||
const { t } = useI18n() // 国际化
|
|
||||||
const message = useMessage() // 消息弹窗
|
|
||||||
|
|
||||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
|
||||||
const formLoading = ref(false) // 表单的加载中
|
|
||||||
const formData = ref({
|
|
||||||
id: undefined,
|
|
||||||
statusId: undefined,
|
|
||||||
endStatus: undefined,
|
|
||||||
status: undefined
|
|
||||||
})
|
|
||||||
const formRules = reactive({
|
|
||||||
status: [{ required: true, message: '商机阶段不能为空', trigger: 'blur' }]
|
|
||||||
})
|
|
||||||
const formRef = ref() // 表单 Ref
|
|
||||||
const statusList = ref([]) // 商机状态列表
|
|
||||||
|
|
||||||
/** 打开弹窗 */
|
|
||||||
const open = async (business: BusinessApi.BusinessVO) => {
|
|
||||||
dialogVisible.value = true
|
|
||||||
resetForm()
|
|
||||||
formData.value = {
|
|
||||||
id: business.id,
|
|
||||||
statusId: business.statusId,
|
|
||||||
endStatus: business.endStatus,
|
|
||||||
status: business.endStatus != null ? -business.endStatus : business.statusId
|
|
||||||
}
|
|
||||||
// 加载状态列表
|
|
||||||
formLoading.value = true
|
|
||||||
try {
|
|
||||||
statusList.value = await BusinessStatusApi.getBusinessStatusSimpleList(business.statusTypeId)
|
|
||||||
} finally {
|
|
||||||
formLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
|
||||||
|
|
||||||
/** 提交表单 */
|
|
||||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
|
||||||
const submitForm = async () => {
|
|
||||||
// 校验表单
|
|
||||||
if (!formRef) return
|
|
||||||
const valid = await formRef.value.validate()
|
|
||||||
if (!valid) return
|
|
||||||
// 提交请求
|
|
||||||
formLoading.value = true
|
|
||||||
try {
|
|
||||||
await BusinessApi.updateBusinessStatus({
|
|
||||||
id: formData.value.id,
|
|
||||||
statusId: formData.value.status > 0 ? formData.value.status : undefined,
|
|
||||||
endStatus: formData.value.status < 0 ? -formData.value.status : undefined
|
|
||||||
})
|
|
||||||
message.success('更新商机状态成功')
|
|
||||||
dialogVisible.value = false
|
|
||||||
// 发送操作成功的事件
|
|
||||||
emit('success')
|
|
||||||
} finally {
|
|
||||||
formLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 重置表单 */
|
|
||||||
const resetForm = () => {
|
|
||||||
formData.value = {
|
|
||||||
id: undefined,
|
|
||||||
statusId: undefined,
|
|
||||||
endStatus: undefined,
|
|
||||||
status: undefined
|
|
||||||
}
|
|
||||||
formRef.value?.resetFields()
|
|
||||||
}
|
|
||||||
</script>
|
|
|
@ -1,186 +0,0 @@
|
||||||
<template>
|
|
||||||
<!-- 操作栏 -->
|
|
||||||
<el-row justify="end">
|
|
||||||
<el-button @click="openForm">
|
|
||||||
<Icon class="mr-5px" icon="ep:opportunity" />
|
|
||||||
创建商机
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
@click="openBusinessModal"
|
|
||||||
v-hasPermi="['crm:contact:create-business']"
|
|
||||||
v-if="queryParams.contactId"
|
|
||||||
>
|
|
||||||
<Icon class="mr-5px" icon="ep:circle-plus" />关联
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
@click="deleteContactBusinessList"
|
|
||||||
v-hasPermi="['crm:contact:delete-business']"
|
|
||||||
v-if="queryParams.contactId"
|
|
||||||
>
|
|
||||||
<Icon class="mr-5px" icon="ep:remove" />解除关联
|
|
||||||
</el-button>
|
|
||||||
</el-row>
|
|
||||||
|
|
||||||
<!-- 列表 -->
|
|
||||||
<ContentWrap class="mt-10px">
|
|
||||||
<el-table
|
|
||||||
ref="businessRef"
|
|
||||||
v-loading="loading"
|
|
||||||
:data="list"
|
|
||||||
:stripe="true"
|
|
||||||
:show-overflow-tooltip="true"
|
|
||||||
>
|
|
||||||
<el-table-column type="selection" width="55" v-if="queryParams.contactId" />
|
|
||||||
<el-table-column label="商机名称" fixed="left" align="center" prop="name">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-link type="primary" :underline="false" @click="openDetail(scope.row.id)">
|
|
||||||
{{ scope.row.name }}
|
|
||||||
</el-link>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
label="商机金额"
|
|
||||||
align="center"
|
|
||||||
prop="price"
|
|
||||||
:formatter="erpPriceTableColumnFormatter"
|
|
||||||
/>
|
|
||||||
<el-table-column label="客户名称" align="center" prop="customerName" />
|
|
||||||
<el-table-column label="商机组" align="center" prop="statusTypeName" />
|
|
||||||
<el-table-column label="商机阶段" align="center" prop="statusName" />
|
|
||||||
</el-table>
|
|
||||||
<!-- 分页 -->
|
|
||||||
<Pagination
|
|
||||||
:total="total"
|
|
||||||
v-model:page="queryParams.pageNo"
|
|
||||||
v-model:limit="queryParams.pageSize"
|
|
||||||
@pagination="getList"
|
|
||||||
/>
|
|
||||||
</ContentWrap>
|
|
||||||
|
|
||||||
<!-- 表单弹窗:添加 -->
|
|
||||||
<BusinessForm ref="formRef" @success="getList" />
|
|
||||||
<!-- 关联商机选择弹框 -->
|
|
||||||
<BusinessListModal
|
|
||||||
ref="businessModalRef"
|
|
||||||
:customer-id="props.customerId"
|
|
||||||
@success="createContactBusinessList"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
import * as BusinessApi from '@/api/crm/business'
|
|
||||||
import * as ContactApi from '@/api/crm/contact'
|
|
||||||
import BusinessForm from './../BusinessForm.vue'
|
|
||||||
import { BizTypeEnum } from '@/api/crm/permission'
|
|
||||||
import BusinessListModal from './BusinessListModal.vue'
|
|
||||||
import { erpPriceTableColumnFormatter } from '@/utils'
|
|
||||||
|
|
||||||
const message = useMessage() // 消息
|
|
||||||
|
|
||||||
defineOptions({ name: 'CrmBusinessList' })
|
|
||||||
const props = defineProps<{
|
|
||||||
bizType: number // 业务类型
|
|
||||||
bizId: number // 业务编号
|
|
||||||
customerId?: number // 关联联系人与商机时,需要传入 customerId 进行筛选
|
|
||||||
contactId?: number // 特殊:联系人编号;在【联系人】详情中,可以传递联系人编号,默认新建的商机关联到该联系人
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const loading = ref(true) // 列表的加载中
|
|
||||||
const total = ref(0) // 列表的总页数
|
|
||||||
const list = ref([]) // 列表的数据
|
|
||||||
const queryParams = reactive({
|
|
||||||
pageNo: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
customerId: undefined as unknown, // 允许 undefined + number
|
|
||||||
contactId: undefined as unknown // 允许 undefined + number
|
|
||||||
})
|
|
||||||
|
|
||||||
/** 查询列表 */
|
|
||||||
const getList = async () => {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
// 置空参数
|
|
||||||
queryParams.customerId = undefined
|
|
||||||
queryParams.contactId = undefined
|
|
||||||
// 执行查询
|
|
||||||
let data = { list: [], total: 0 }
|
|
||||||
switch (props.bizType) {
|
|
||||||
case BizTypeEnum.CRM_CUSTOMER:
|
|
||||||
queryParams.customerId = props.bizId
|
|
||||||
data = await BusinessApi.getBusinessPageByCustomer(queryParams)
|
|
||||||
break
|
|
||||||
case BizTypeEnum.CRM_CONTACT:
|
|
||||||
queryParams.contactId = props.bizId
|
|
||||||
data = await BusinessApi.getBusinessPageByContact(queryParams)
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
list.value = data.list
|
|
||||||
total.value = data.total
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 搜索按钮操作 */
|
|
||||||
const handleQuery = () => {
|
|
||||||
queryParams.pageNo = 1
|
|
||||||
getList()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 添加操作 */
|
|
||||||
const formRef = ref()
|
|
||||||
const openForm = () => {
|
|
||||||
formRef.value.open('create', null, props.customerId, props.contactId)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 打开联系人详情 */
|
|
||||||
const { push } = useRouter()
|
|
||||||
const openDetail = (id: number) => {
|
|
||||||
push({ name: 'CrmBusinessDetail', params: { id } })
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 打开联系人与商机的关联弹窗 */
|
|
||||||
const businessModalRef = ref()
|
|
||||||
const openBusinessModal = () => {
|
|
||||||
businessModalRef.value.open()
|
|
||||||
}
|
|
||||||
const createContactBusinessList = async (businessIds: number[]) => {
|
|
||||||
const data = {
|
|
||||||
contactId: props.bizId,
|
|
||||||
businessIds: businessIds
|
|
||||||
} as ContactApi.ContactBusinessReqVO
|
|
||||||
businessRef.value.getSelectionRows().forEach((row: BusinessApi.BusinessVO) => {
|
|
||||||
data.businessIds.push(row.id)
|
|
||||||
})
|
|
||||||
await ContactApi.createContactBusinessList(data)
|
|
||||||
// 刷新列表
|
|
||||||
message.success('关联商机成功')
|
|
||||||
handleQuery()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 解除联系人与商机的关联 */
|
|
||||||
const businessRef = ref()
|
|
||||||
const deleteContactBusinessList = async () => {
|
|
||||||
const data = {
|
|
||||||
contactId: props.bizId,
|
|
||||||
businessIds: businessRef.value.getSelectionRows().map((row: BusinessApi.BusinessVO) => row.id)
|
|
||||||
} as ContactApi.ContactBusinessReqVO
|
|
||||||
if (data.businessIds.length === 0) {
|
|
||||||
return message.error('未选择商机')
|
|
||||||
}
|
|
||||||
await ContactApi.deleteContactBusinessList(data)
|
|
||||||
// 刷新列表
|
|
||||||
message.success('取关商机成功')
|
|
||||||
handleQuery()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 监听打开的 bizId + bizType,从而加载最新的列表 */
|
|
||||||
watch(
|
|
||||||
() => [props.bizId, props.bizType],
|
|
||||||
() => {
|
|
||||||
handleQuery()
|
|
||||||
},
|
|
||||||
{ immediate: true, deep: true }
|
|
||||||
)
|
|
||||||
</script>
|
|
|
@ -1,156 +0,0 @@
|
||||||
<template>
|
|
||||||
<Dialog title="关联商机" v-model="dialogVisible">
|
|
||||||
<!-- 搜索工作栏 -->
|
|
||||||
<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>
|
|
||||||
<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>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</ContentWrap>
|
|
||||||
|
|
||||||
<!-- 列表 -->
|
|
||||||
<ContentWrap class="mt-10px">
|
|
||||||
<el-table
|
|
||||||
v-loading="loading"
|
|
||||||
ref="businessRef"
|
|
||||||
:data="list"
|
|
||||||
:stripe="true"
|
|
||||||
:show-overflow-tooltip="true"
|
|
||||||
>
|
|
||||||
<el-table-column type="selection" width="55" />
|
|
||||||
<el-table-column label="商机名称" fixed="left" align="center" prop="name">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-link type="primary" :underline="false" @click="openDetail(scope.row.id)">
|
|
||||||
{{ scope.row.name }}
|
|
||||||
</el-link>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
label="商机金额"
|
|
||||||
align="center"
|
|
||||||
prop="totalPrice"
|
|
||||||
:formatter="erpPriceTableColumnFormatter"
|
|
||||||
/>
|
|
||||||
<el-table-column label="客户名称" align="center" prop="customerName" />
|
|
||||||
<el-table-column label="商机组" align="center" prop="statusTypeName" />
|
|
||||||
<el-table-column label="商机阶段" align="center" prop="statusName" />
|
|
||||||
</el-table>
|
|
||||||
<!-- 分页 -->
|
|
||||||
<Pagination
|
|
||||||
:total="total"
|
|
||||||
v-model:page="queryParams.pageNo"
|
|
||||||
v-model:limit="queryParams.pageSize"
|
|
||||||
@pagination="getList"
|
|
||||||
/>
|
|
||||||
</ContentWrap>
|
|
||||||
<template #footer>
|
|
||||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
|
||||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- 表单弹窗:添加 -->
|
|
||||||
<BusinessForm ref="formRef" @success="getList" />
|
|
||||||
</Dialog>
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
import * as BusinessApi from '@/api/crm/business'
|
|
||||||
import BusinessForm from '../BusinessForm.vue'
|
|
||||||
import { erpPriceTableColumnFormatter } from '@/utils'
|
|
||||||
|
|
||||||
const message = useMessage() // 消息弹窗
|
|
||||||
const props = defineProps<{
|
|
||||||
customerId: number
|
|
||||||
}>()
|
|
||||||
defineOptions({ name: 'BusinessListModal' })
|
|
||||||
|
|
||||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
|
||||||
const loading = ref(true) // 列表的加载中
|
|
||||||
const total = ref(0) // 列表的总页数
|
|
||||||
const list = ref([]) // 列表的数据
|
|
||||||
const queryFormRef = ref() // 搜索的表单
|
|
||||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
|
||||||
const queryParams = reactive({
|
|
||||||
pageNo: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
name: undefined,
|
|
||||||
customerId: props.customerId
|
|
||||||
})
|
|
||||||
|
|
||||||
/** 打开弹窗 */
|
|
||||||
const open = async () => {
|
|
||||||
dialogVisible.value = true
|
|
||||||
queryParams.customerId = props.customerId // 解决 props.customerId 没更新到 queryParams 上的问题
|
|
||||||
await getList()
|
|
||||||
}
|
|
||||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
|
||||||
|
|
||||||
/** 查询列表 */
|
|
||||||
const getList = async () => {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
const data = await BusinessApi.getBusinessPageByCustomer(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 = () => {
|
|
||||||
formRef.value.open('create')
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 关联商机提交 */
|
|
||||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
|
||||||
const businessRef = ref()
|
|
||||||
const submitForm = async () => {
|
|
||||||
const businessIds = businessRef.value
|
|
||||||
.getSelectionRows()
|
|
||||||
.map((row: BusinessApi.BusinessVO) => row.id)
|
|
||||||
if (businessIds.length === 0) {
|
|
||||||
return message.error('未选择商机')
|
|
||||||
}
|
|
||||||
dialogVisible.value = false
|
|
||||||
emit('success', businessIds, businessRef.value.getSelectionRows())
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 打开商机详情 */
|
|
||||||
const { push } = useRouter()
|
|
||||||
const openDetail = (id: number) => {
|
|
||||||
push({ name: 'CrmBusinessDetail', params: { id } })
|
|
||||||
}
|
|
||||||
</script>
|
|
|
@ -1,183 +0,0 @@
|
||||||
<template>
|
|
||||||
<el-form
|
|
||||||
ref="formRef"
|
|
||||||
:model="formData"
|
|
||||||
:rules="formRules"
|
|
||||||
v-loading="formLoading"
|
|
||||||
label-width="0px"
|
|
||||||
:inline-message="true"
|
|
||||||
:disabled="disabled"
|
|
||||||
>
|
|
||||||
<el-table :data="formData" class="-mt-10px">
|
|
||||||
<el-table-column label="序号" type="index" align="center" width="60" />
|
|
||||||
<el-table-column label="产品名称" min-width="180">
|
|
||||||
<template #default="{ row, $index }">
|
|
||||||
<el-form-item :prop="`${$index}.productId`" :rules="formRules.productId" class="mb-0px!">
|
|
||||||
<el-select
|
|
||||||
v-model="row.productId"
|
|
||||||
clearable
|
|
||||||
filterable
|
|
||||||
@change="onChangeProduct($event, row)"
|
|
||||||
placeholder="请选择产品"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="item in productList"
|
|
||||||
:key="item.id"
|
|
||||||
:label="item.name"
|
|
||||||
:value="item.id"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="条码" min-width="150">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<el-form-item class="mb-0px!">
|
|
||||||
<el-input disabled v-model="row.productNo" />
|
|
||||||
</el-form-item>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="单位" min-width="80">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<dict-tag :type="DICT_TYPE.CRM_PRODUCT_UNIT" :value="row.productUnit" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="价格(元)" min-width="120">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<el-form-item class="mb-0px!">
|
|
||||||
<el-input disabled v-model="row.productPrice" :formatter="erpPriceInputFormatter" />
|
|
||||||
</el-form-item>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="售价(元)" fixed="right" min-width="140">
|
|
||||||
<template #default="{ row, $index }">
|
|
||||||
<el-form-item :prop="`${$index}.businessPrice`" class="mb-0px!">
|
|
||||||
<el-input-number
|
|
||||||
v-model="row.businessPrice"
|
|
||||||
controls-position="right"
|
|
||||||
:min="0.001"
|
|
||||||
:precision="2"
|
|
||||||
class="!w-100%"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="数量" prop="count" fixed="right" min-width="120">
|
|
||||||
<template #default="{ row, $index }">
|
|
||||||
<el-form-item :prop="`${$index}.count`" :rules="formRules.count" class="mb-0px!">
|
|
||||||
<el-input-number
|
|
||||||
v-model="row.count"
|
|
||||||
controls-position="right"
|
|
||||||
:min="0.001"
|
|
||||||
:precision="3"
|
|
||||||
class="!w-100%"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="合计" prop="totalPrice" fixed="right" min-width="140">
|
|
||||||
<template #default="{ row, $index }">
|
|
||||||
<el-form-item :prop="`${$index}.totalPrice`" class="mb-0px!">
|
|
||||||
<el-input disabled v-model="row.totalPrice" :formatter="erpPriceInputFormatter" />
|
|
||||||
</el-form-item>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" fixed="right" label="操作" width="60">
|
|
||||||
<template #default="{ $index }">
|
|
||||||
<el-button @click="handleDelete($index)" link>—</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
</el-form>
|
|
||||||
<el-row justify="center" class="mt-3" v-if="!disabled">
|
|
||||||
<el-button @click="handleAdd" round>+ 添加产品</el-button>
|
|
||||||
</el-row>
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
import * as ProductApi from '@/api/crm/product'
|
|
||||||
import { erpPriceInputFormatter, erpPriceMultiply } from '@/utils'
|
|
||||||
import { DICT_TYPE } from '@/utils/dict'
|
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
products: undefined
|
|
||||||
disabled: false
|
|
||||||
}>()
|
|
||||||
const formLoading = ref(false) // 表单的加载中
|
|
||||||
const formData = ref([])
|
|
||||||
const formRules = reactive({
|
|
||||||
productId: [{ required: true, message: '产品不能为空', trigger: 'blur' }],
|
|
||||||
businessPrice: [{ required: true, message: '合同价格不能为空', trigger: 'blur' }],
|
|
||||||
count: [{ required: true, message: '产品数量不能为空', trigger: 'blur' }]
|
|
||||||
})
|
|
||||||
const formRef = ref([]) // 表单 Ref
|
|
||||||
const productList = ref<ProductApi.ProductVO[]>([]) // 产品列表
|
|
||||||
|
|
||||||
/** 初始化设置产品项 */
|
|
||||||
watch(
|
|
||||||
() => props.products,
|
|
||||||
async (val) => {
|
|
||||||
formData.value = val
|
|
||||||
},
|
|
||||||
{ immediate: true }
|
|
||||||
)
|
|
||||||
|
|
||||||
/** 监听合同产品变化,计算合同产品总价 */
|
|
||||||
watch(
|
|
||||||
() => formData.value,
|
|
||||||
(val) => {
|
|
||||||
if (!val || val.length === 0) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 循环处理
|
|
||||||
val.forEach((item) => {
|
|
||||||
if (item.businessPrice != null && item.count != null) {
|
|
||||||
item.totalPrice = erpPriceMultiply(item.businessPrice, item.count)
|
|
||||||
} else {
|
|
||||||
item.totalPrice = undefined
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
{ deep: true }
|
|
||||||
)
|
|
||||||
|
|
||||||
/** 新增按钮操作 */
|
|
||||||
const handleAdd = () => {
|
|
||||||
const row = {
|
|
||||||
id: undefined,
|
|
||||||
productId: undefined,
|
|
||||||
productUnit: undefined, // 产品单位
|
|
||||||
productNo: undefined, // 产品条码
|
|
||||||
productPrice: undefined, // 产品价格
|
|
||||||
businessPrice: undefined,
|
|
||||||
count: 1
|
|
||||||
}
|
|
||||||
formData.value.push(row)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 删除按钮操作 */
|
|
||||||
const handleDelete = (index: number) => {
|
|
||||||
formData.value.splice(index, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 处理产品变更 */
|
|
||||||
const onChangeProduct = (productId, row) => {
|
|
||||||
const product = productList.value.find((item) => item.id === productId)
|
|
||||||
if (product) {
|
|
||||||
row.productUnit = product.unit
|
|
||||||
row.productNo = product.no
|
|
||||||
row.productPrice = product.price
|
|
||||||
row.businessPrice = product.price
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 表单校验 */
|
|
||||||
const validate = () => {
|
|
||||||
return formRef.value.validate()
|
|
||||||
}
|
|
||||||
defineExpose({ validate })
|
|
||||||
|
|
||||||
/** 初始化 */
|
|
||||||
onMounted(async () => {
|
|
||||||
productList.value = await ProductApi.getProductSimpleList()
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,37 +0,0 @@
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<div class="flex items-start justify-between">
|
|
||||||
<div>
|
|
||||||
<el-col>
|
|
||||||
<el-row>
|
|
||||||
<span class="text-xl font-bold">{{ business.name }}</span>
|
|
||||||
</el-row>
|
|
||||||
</el-col>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<!-- 右上:按钮 -->
|
|
||||||
<slot></slot>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<ContentWrap class="mt-10px">
|
|
||||||
<el-descriptions :column="5" direction="vertical">
|
|
||||||
<el-descriptions-item label="客户名称">{{ business.customerName }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="商机金额(元)">
|
|
||||||
{{ erpPriceInputFormatter(business.totalPrice) }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="商机组">{{ business.statusTypeName }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="负责人">{{ business.ownerUserName }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="创建时间">
|
|
||||||
{{ formatDate(business.createTime) }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
</el-descriptions>
|
|
||||||
</ContentWrap>
|
|
||||||
</template>
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import * as BusinessApi from '@/api/crm/business'
|
|
||||||
import { formatDate } from '@/utils/formatTime'
|
|
||||||
import { erpPriceInputFormatter } from '@/utils'
|
|
||||||
|
|
||||||
const { business } = defineProps<{ business: BusinessApi.BusinessVO }>()
|
|
||||||
</script>
|
|
|
@ -1,61 +0,0 @@
|
||||||
<template>
|
|
||||||
<ContentWrap>
|
|
||||||
<el-collapse v-model="activeNames">
|
|
||||||
<el-collapse-item name="basicInfo">
|
|
||||||
<template #title>
|
|
||||||
<span class="text-base font-bold">基本信息</span>
|
|
||||||
</template>
|
|
||||||
<el-descriptions :column="4">
|
|
||||||
<el-descriptions-item label="商机姓名">{{ business.name }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="客户名称">{{ business.customerName }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="商机金额(元)">
|
|
||||||
{{ erpPriceInputFormatter(business.totalPrice) }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="预计成交日期">
|
|
||||||
{{ formatDate(business.dealTime) }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="下次联系时间">
|
|
||||||
{{ formatDate(business.contactNextTime) }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="商机状态组">
|
|
||||||
{{ business.statusTypeName }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="商机阶段">{{ business.statusName }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="备注">{{ business.remark }}</el-descriptions-item>
|
|
||||||
</el-descriptions>
|
|
||||||
</el-collapse-item>
|
|
||||||
<el-collapse-item name="systemInfo">
|
|
||||||
<template #title>
|
|
||||||
<span class="text-base font-bold">系统信息</span>
|
|
||||||
</template>
|
|
||||||
<el-descriptions :column="4">
|
|
||||||
<el-descriptions-item label="负责人">{{ business.ownerUserName }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="最后跟进时间">
|
|
||||||
{{ formatDate(business.contactLastTime) }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label=""> </el-descriptions-item>
|
|
||||||
<el-descriptions-item label=""> </el-descriptions-item>
|
|
||||||
<el-descriptions-item label="创建人">{{ business.creatorName }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="创建时间">
|
|
||||||
{{ formatDate(business.createTime) }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="更新时间">
|
|
||||||
{{ formatDate(business.updateTime) }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
</el-descriptions>
|
|
||||||
</el-collapse-item>
|
|
||||||
</el-collapse>
|
|
||||||
</ContentWrap>
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
import * as BusinessApi from '@/api/crm/business'
|
|
||||||
import { formatDate } from '@/utils/formatTime'
|
|
||||||
import { erpPriceInputFormatter } from '@/utils'
|
|
||||||
|
|
||||||
const { business } = defineProps<{
|
|
||||||
business: BusinessApi.BusinessVO
|
|
||||||
}>()
|
|
||||||
|
|
||||||
// 展示的折叠面板
|
|
||||||
const activeNames = ref(['basicInfo', 'systemInfo'])
|
|
||||||
</script>
|
|
|
@ -1,66 +0,0 @@
|
||||||
<template>
|
|
||||||
<ContentWrap>
|
|
||||||
<el-table :data="business.products" :stripe="true" :show-overflow-tooltip="true">
|
|
||||||
<el-table-column
|
|
||||||
align="center"
|
|
||||||
label="产品名称"
|
|
||||||
fixed="left"
|
|
||||||
prop="productName"
|
|
||||||
min-width="160"
|
|
||||||
>
|
|
||||||
<template #default="scope">
|
|
||||||
{{ scope.row.productName }}
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="产品条码" align="center" prop="productNo" min-width="120" />
|
|
||||||
<el-table-column align="center" label="产品单位" prop="productUnit" min-width="160">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<dict-tag :type="DICT_TYPE.CRM_PRODUCT_UNIT" :value="row.productUnit" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
label="产品价格(元)"
|
|
||||||
align="center"
|
|
||||||
prop="productPrice"
|
|
||||||
min-width="140"
|
|
||||||
:formatter="erpPriceTableColumnFormatter"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
label="商机价格(元)"
|
|
||||||
align="center"
|
|
||||||
prop="businessPrice"
|
|
||||||
min-width="140"
|
|
||||||
:formatter="erpPriceTableColumnFormatter"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
align="center"
|
|
||||||
label="数量"
|
|
||||||
prop="count"
|
|
||||||
min-width="100px"
|
|
||||||
:formatter="erpPriceTableColumnFormatter"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
label="合计金额(元)"
|
|
||||||
align="center"
|
|
||||||
prop="totalPrice"
|
|
||||||
min-width="140"
|
|
||||||
:formatter="erpPriceTableColumnFormatter"
|
|
||||||
/>
|
|
||||||
</el-table>
|
|
||||||
<el-row class="mt-10px" justify="end">
|
|
||||||
<el-col :span="3"> 整单折扣:{{ erpPriceInputFormatter(business.discountPercent) }}% </el-col>
|
|
||||||
<el-col :span="4">
|
|
||||||
产品总金额:{{ erpPriceInputFormatter(business.totalProductPrice) }} 元
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</ContentWrap>
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
import * as BusinessApi from '@/api/crm/business'
|
|
||||||
import { erpPriceInputFormatter, erpPriceTableColumnFormatter } from '@/utils'
|
|
||||||
import { DICT_TYPE } from '@/utils/dict'
|
|
||||||
|
|
||||||
const { business } = defineProps<{
|
|
||||||
business: BusinessApi.BusinessVO
|
|
||||||
}>()
|
|
||||||
</script>
|
|
|
@ -1,146 +0,0 @@
|
||||||
<template>
|
|
||||||
<BusinessDetailsHeader v-loading="loading" :business="business">
|
|
||||||
<el-button v-if="permissionListRef?.validateWrite" @click="openForm('update', business.id)">
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
v-if="permissionListRef?.validateWrite"
|
|
||||||
:disabled="business.endStatus"
|
|
||||||
type="success"
|
|
||||||
@click="openStatusForm()"
|
|
||||||
>
|
|
||||||
变更商机状态
|
|
||||||
</el-button>
|
|
||||||
<el-button v-if="permissionListRef?.validateOwnerUser" type="primary" @click="transfer">
|
|
||||||
转移
|
|
||||||
</el-button>
|
|
||||||
</BusinessDetailsHeader>
|
|
||||||
<el-col>
|
|
||||||
<el-tabs>
|
|
||||||
<el-tab-pane label="跟进记录">
|
|
||||||
<FollowUpList :biz-id="businessId" :biz-type="BizTypeEnum.CRM_BUSINESS" />
|
|
||||||
</el-tab-pane>
|
|
||||||
<el-tab-pane label="详细资料">
|
|
||||||
<BusinessDetailsInfo :business="business" />
|
|
||||||
</el-tab-pane>
|
|
||||||
<el-tab-pane label="联系人" lazy>
|
|
||||||
<ContactList
|
|
||||||
:biz-id="business.id!"
|
|
||||||
:biz-type="BizTypeEnum.CRM_BUSINESS"
|
|
||||||
:business-id="business.id"
|
|
||||||
:customer-id="business.customerId"
|
|
||||||
/>
|
|
||||||
</el-tab-pane>
|
|
||||||
<el-tab-pane label="产品">
|
|
||||||
<BusinessProductList :business="business" />
|
|
||||||
</el-tab-pane>
|
|
||||||
<el-tab-pane label="合同" lazy>
|
|
||||||
<ContractList :biz-id="business.id!" :biz-type="BizTypeEnum.CRM_BUSINESS" />
|
|
||||||
</el-tab-pane>
|
|
||||||
<el-tab-pane label="操作日志">
|
|
||||||
<OperateLogV2 :log-list="logList" />
|
|
||||||
</el-tab-pane>
|
|
||||||
<el-tab-pane label="团队成员">
|
|
||||||
<PermissionList
|
|
||||||
ref="permissionListRef"
|
|
||||||
:biz-id="business.id!"
|
|
||||||
:biz-type="BizTypeEnum.CRM_BUSINESS"
|
|
||||||
:show-action="true"
|
|
||||||
@quit-team="close"
|
|
||||||
/>
|
|
||||||
</el-tab-pane>
|
|
||||||
</el-tabs>
|
|
||||||
</el-col>
|
|
||||||
|
|
||||||
<!-- 表单弹窗:添加/修改 -->
|
|
||||||
<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 BusinessApi from '@/api/crm/business'
|
|
||||||
import BusinessDetailsHeader from './BusinessDetailsHeader.vue'
|
|
||||||
import BusinessDetailsInfo from './BusinessDetailsInfo.vue'
|
|
||||||
import PermissionList from '@/views/crm/permission/components/PermissionList.vue' // 团队成员列表(权限)
|
|
||||||
import { BizTypeEnum } from '@/api/crm/permission'
|
|
||||||
import { OperateLogVO } from '@/api/system/operatelog'
|
|
||||||
import { getOperateLogPage } from '@/api/crm/operateLog'
|
|
||||||
import BusinessForm from '@/views/crm/business/BusinessForm.vue'
|
|
||||||
import CrmTransferForm from '@/views/crm/permission/components/TransferForm.vue'
|
|
||||||
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' })
|
|
||||||
|
|
||||||
const message = useMessage()
|
|
||||||
|
|
||||||
const businessId = ref(0) // 线索编号
|
|
||||||
const loading = ref(true) // 加载中
|
|
||||||
const business = ref<BusinessApi.BusinessVO>({} as BusinessApi.BusinessVO) // 商机详情
|
|
||||||
const permissionListRef = ref<InstanceType<typeof PermissionList>>() // 团队成员列表 Ref
|
|
||||||
|
|
||||||
/** 获取详情 */
|
|
||||||
const getBusiness = async () => {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
business.value = await BusinessApi.getBusiness(businessId.value)
|
|
||||||
await getOperateLog(businessId.value)
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 编辑 */
|
|
||||||
const formRef = ref()
|
|
||||||
const openForm = (type: string, id?: number) => {
|
|
||||||
formRef.value.open(type, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 变更商机状态 */
|
|
||||||
const statusFormRef = ref()
|
|
||||||
const openStatusForm = () => {
|
|
||||||
statusFormRef.value.open(business.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 联系人转移 */
|
|
||||||
const transferFormRef = ref<InstanceType<typeof CrmTransferForm>>() // 联系人转移表单 ref
|
|
||||||
const transfer = () => {
|
|
||||||
transferFormRef.value?.open(business.value.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 获取操作日志 */
|
|
||||||
const logList = ref<OperateLogVO[]>([]) // 操作日志列表
|
|
||||||
const getOperateLog = async (contactId: number) => {
|
|
||||||
if (!contactId) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const data = await getOperateLogPage({
|
|
||||||
bizType: BizTypeEnum.CRM_BUSINESS,
|
|
||||||
bizId: contactId
|
|
||||||
})
|
|
||||||
logList.value = data.list
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 关闭窗口 */
|
|
||||||
const { delView } = useTagsViewStore() // 视图操作
|
|
||||||
const { currentRoute } = useRouter() // 路由
|
|
||||||
const close = () => {
|
|
||||||
delView(unref(currentRoute))
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 初始化 */
|
|
||||||
const { params } = useRoute()
|
|
||||||
onMounted(async () => {
|
|
||||||
if (!params.id) {
|
|
||||||
message.warning('参数错误,商机不能为空!')
|
|
||||||
close()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
businessId.value = params.id as unknown as number
|
|
||||||
await getBusiness()
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,275 +0,0 @@
|
||||||
<template>
|
|
||||||
<!-- <doc-alert title="【商机】商机管理、商机状态" url="https://doc.iocoder.cn/crm/business/" />
|
|
||||||
<doc-alert title="【通用】数据权限" url="https://doc.iocoder.cn/crm/permission/" /> -->
|
|
||||||
|
|
||||||
<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>
|
|
||||||
<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('create')">
|
|
||||||
<Icon class="mr-5px" icon="ep:plus" />
|
|
||||||
新增
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
v-hasPermi="['crm:business:export']"
|
|
||||||
:loading="exportLoading"
|
|
||||||
plain
|
|
||||||
type="success"
|
|
||||||
@click="handleExport"
|
|
||||||
>
|
|
||||||
<Icon class="mr-5px" icon="ep:download" />
|
|
||||||
导出
|
|
||||||
</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</ContentWrap>
|
|
||||||
|
|
||||||
<!-- 列表 -->
|
|
||||||
<ContentWrap>
|
|
||||||
<el-tabs v-model="activeName" @tab-click="handleTabClick">
|
|
||||||
<el-tab-pane label="我负责的" name="1" />
|
|
||||||
<el-tab-pane label="我参与的" name="2" />
|
|
||||||
<el-tab-pane label="下属负责的" name="3" />
|
|
||||||
</el-tabs>
|
|
||||||
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true">
|
|
||||||
<el-table-column align="center" fixed="left" label="商机名称" prop="name" width="160">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-link :underline="false" type="primary" @click="openDetail(scope.row.id)">
|
|
||||||
{{ scope.row.name }}
|
|
||||||
</el-link>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" fixed="left" label="客户名称" prop="customerName" width="120">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-link
|
|
||||||
:underline="false"
|
|
||||||
type="primary"
|
|
||||||
@click="openCustomerDetail(scope.row.customerId)"
|
|
||||||
>
|
|
||||||
{{ scope.row.customerName }}
|
|
||||||
</el-link>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
:formatter="erpPriceTableColumnFormatter"
|
|
||||||
align="center"
|
|
||||||
label="商机金额(元)"
|
|
||||||
prop="totalPrice"
|
|
||||||
width="140"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="预计成交日期"
|
|
||||||
prop="dealTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="备注" prop="remark" width="200" />
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="下次联系时间"
|
|
||||||
prop="contactNextTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="负责人" prop="ownerUserName" width="100px" />
|
|
||||||
<el-table-column align="center" label="所属部门" prop="ownerUserDeptName" width="100px" />
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="最后跟进时间"
|
|
||||||
prop="contactLastTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="更新时间"
|
|
||||||
prop="updateTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="创建时间"
|
|
||||||
prop="createTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="创建人" prop="creatorName" width="100px" />
|
|
||||||
<el-table-column
|
|
||||||
align="center"
|
|
||||||
fixed="right"
|
|
||||||
label="商机状态组"
|
|
||||||
prop="statusTypeName"
|
|
||||||
width="140"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
align="center"
|
|
||||||
fixed="right"
|
|
||||||
label="商机阶段"
|
|
||||||
prop="statusName"
|
|
||||||
width="120"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" fixed="right" label="操作" width="130px">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
v-hasPermi="['crm:business:update']"
|
|
||||||
link
|
|
||||||
type="primary"
|
|
||||||
@click="openForm('update', scope.row.id)"
|
|
||||||
>
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
v-hasPermi="['crm:business:delete']"
|
|
||||||
link
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(scope.row.id)"
|
|
||||||
>
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
<!-- 分页 -->
|
|
||||||
<Pagination
|
|
||||||
v-model:limit="queryParams.pageSize"
|
|
||||||
v-model:page="queryParams.pageNo"
|
|
||||||
:total="total"
|
|
||||||
@pagination="getList"
|
|
||||||
/>
|
|
||||||
</ContentWrap>
|
|
||||||
|
|
||||||
<!-- 表单弹窗:添加/修改 -->
|
|
||||||
<BusinessForm ref="formRef" @success="getList" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { dateFormatter } from '@/utils/formatTime'
|
|
||||||
import download from '@/utils/download'
|
|
||||||
import * as BusinessApi from '@/api/crm/business'
|
|
||||||
import BusinessForm from './BusinessForm.vue'
|
|
||||||
import { erpPriceTableColumnFormatter } from '@/utils'
|
|
||||||
import { TabsPaneContext } from 'element-plus'
|
|
||||||
|
|
||||||
defineOptions({ name: 'CrmBusiness' })
|
|
||||||
|
|
||||||
const message = useMessage() // 消息弹窗
|
|
||||||
const { t } = useI18n() // 国际化
|
|
||||||
|
|
||||||
const loading = ref(true) // 列表的加载中
|
|
||||||
const total = ref(0) // 列表的总页数
|
|
||||||
const list = ref([]) // 列表的数据
|
|
||||||
const queryParams = reactive({
|
|
||||||
pageNo: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
sceneType: '1', // 默认和 activeName 相等
|
|
||||||
name: null
|
|
||||||
})
|
|
||||||
const queryFormRef = ref() // 搜索的表单
|
|
||||||
const exportLoading = ref(false) // 导出的加载中
|
|
||||||
const activeName = ref('1') // 列表 tab
|
|
||||||
|
|
||||||
/** 查询列表 */
|
|
||||||
const getList = async () => {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
const data = await BusinessApi.getBusinessPage(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()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** tab 切换 */
|
|
||||||
const handleTabClick = (tab: TabsPaneContext) => {
|
|
||||||
queryParams.sceneType = tab.paneName!.toString()
|
|
||||||
handleQuery()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 打开客户详情 */
|
|
||||||
const { push } = useRouter()
|
|
||||||
const openDetail = (id: number) => {
|
|
||||||
push({ name: 'CrmBusinessDetail', params: { id } })
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 打开客户详情 */
|
|
||||||
const openCustomerDetail = (id: number) => {
|
|
||||||
push({ name: 'CrmCustomerDetail', params: { id } })
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 添加/修改操作 */
|
|
||||||
const formRef = ref()
|
|
||||||
const openForm = (type: string, id?: number) => {
|
|
||||||
formRef.value.open(type, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 删除按钮操作 */
|
|
||||||
const handleDelete = async (id: number) => {
|
|
||||||
try {
|
|
||||||
// 删除的二次确认
|
|
||||||
await message.delConfirm()
|
|
||||||
// 发起删除
|
|
||||||
await BusinessApi.deleteBusiness(id)
|
|
||||||
message.success(t('common.delSuccess'))
|
|
||||||
// 刷新列表
|
|
||||||
await getList()
|
|
||||||
} catch {}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 导出按钮操作 */
|
|
||||||
const handleExport = async () => {
|
|
||||||
try {
|
|
||||||
// 导出的二次确认
|
|
||||||
await message.exportConfirm()
|
|
||||||
// 发起导出
|
|
||||||
exportLoading.value = true
|
|
||||||
const data = await BusinessApi.exportBusiness(queryParams)
|
|
||||||
download.excel(data, '商机.xls')
|
|
||||||
} catch {
|
|
||||||
} finally {
|
|
||||||
exportLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 初始化 **/
|
|
||||||
onMounted(() => {
|
|
||||||
getList()
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,194 +0,0 @@
|
||||||
<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="deptIds">
|
|
||||||
<template #label>
|
|
||||||
<Tooltip message="不选择部门时,默认全公司生效" title="应用部门" />
|
|
||||||
</template>
|
|
||||||
<el-tree
|
|
||||||
ref="treeRef"
|
|
||||||
:data="deptList"
|
|
||||||
:props="defaultProps"
|
|
||||||
:check-strictly="!checkStrictly"
|
|
||||||
node-key="id"
|
|
||||||
placeholder="请选择归属部门"
|
|
||||||
show-checkbox
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="阶段设置" prop="statuses">
|
|
||||||
<el-table
|
|
||||||
border
|
|
||||||
style="width: 100%"
|
|
||||||
:data="formData.statuses.concat(BusinessStatusApi.DEFAULT_STATUSES)"
|
|
||||||
>
|
|
||||||
<el-table-column align="center" label="阶段" width="70">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-text v-if="!scope.row.defaultStatus">阶段 {{ scope.$index + 1 }}</el-text>
|
|
||||||
<el-text v-else>结束</el-text>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="阶段名称" width="160" prop="name">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<el-input v-if="!row.endStatus" v-model="row.name" placeholder="请输入状态名称" />
|
|
||||||
<el-text v-else>{{ row.name }}</el-text>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column width="140" align="center" label="赢单率(%)" prop="percent">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<el-input-number
|
|
||||||
v-if="!row.endStatus"
|
|
||||||
v-model="row.percent"
|
|
||||||
placeholder="请输入赢单率"
|
|
||||||
controls-position="right"
|
|
||||||
:min="0"
|
|
||||||
:max="100"
|
|
||||||
:precision="2"
|
|
||||||
class="!w-1/1"
|
|
||||||
/>
|
|
||||||
<el-text v-else>{{ row.percent }}</el-text>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="操作" width="110" align="center">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
v-if="!scope.row.endStatus"
|
|
||||||
link
|
|
||||||
type="primary"
|
|
||||||
@click="addStatus(scope.$index)"
|
|
||||||
>
|
|
||||||
添加
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
v-if="!scope.row.endStatus"
|
|
||||||
link
|
|
||||||
type="danger"
|
|
||||||
@click="deleteStatusArea(scope.$index)"
|
|
||||||
:disabled="formData.statuses.length <= 1"
|
|
||||||
>
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
</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 * as BusinessStatusApi from '@/api/crm/business/status'
|
|
||||||
import { defaultProps, handleTree } from '@/utils/tree'
|
|
||||||
import * as DeptApi from '@/api/system/dept'
|
|
||||||
|
|
||||||
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: '',
|
|
||||||
deptIds: [],
|
|
||||||
statuses: []
|
|
||||||
})
|
|
||||||
const formRules = reactive({
|
|
||||||
name: [{ required: true, message: '状态组名不能为空', trigger: 'blur' }]
|
|
||||||
})
|
|
||||||
const formRef = ref() // 表单 Ref
|
|
||||||
const deptList = ref<Tree[]>([]) // 树形结构
|
|
||||||
const treeRef = ref() // 菜单树组件 Ref
|
|
||||||
const checkStrictly = ref(true) // 是否严格模式,即父子不关联
|
|
||||||
|
|
||||||
/** 打开弹窗 */
|
|
||||||
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 BusinessStatusApi.getBusinessStatus(id)
|
|
||||||
treeRef.value.setCheckedKeys(formData.value.deptIds)
|
|
||||||
if (formData.value.statuses.length == 0) {
|
|
||||||
addStatus()
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
formLoading.value = false
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
addStatus()
|
|
||||||
}
|
|
||||||
// 加载部门树
|
|
||||||
deptList.value = handleTree(await DeptApi.getSimpleDeptList())
|
|
||||||
}
|
|
||||||
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 BusinessStatusApi.BusinessStatusTypeVO
|
|
||||||
data.deptIds = treeRef.value.getCheckedKeys(false)
|
|
||||||
if (formType.value === 'create') {
|
|
||||||
await BusinessStatusApi.createBusinessStatus(data)
|
|
||||||
message.success(t('common.createSuccess'))
|
|
||||||
} else {
|
|
||||||
await BusinessStatusApi.updateBusinessStatus(data)
|
|
||||||
message.success(t('common.updateSuccess'))
|
|
||||||
}
|
|
||||||
dialogVisible.value = false
|
|
||||||
// 发送操作成功的事件
|
|
||||||
emit('success')
|
|
||||||
} finally {
|
|
||||||
formLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 重置表单 */
|
|
||||||
const resetForm = () => {
|
|
||||||
checkStrictly.value = true
|
|
||||||
formData.value = {
|
|
||||||
id: undefined,
|
|
||||||
name: '',
|
|
||||||
deptIds: [],
|
|
||||||
statuses: []
|
|
||||||
}
|
|
||||||
treeRef.value?.setCheckedNodes([])
|
|
||||||
formRef.value?.resetFields()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 添加状态 */
|
|
||||||
const addStatus = () => {
|
|
||||||
const data = formData.value
|
|
||||||
data.statuses.push({
|
|
||||||
name: '',
|
|
||||||
percent: undefined
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 删除状态 */
|
|
||||||
const deleteStatusArea = (index: number) => {
|
|
||||||
const data = formData.value
|
|
||||||
data.statuses.splice(index, 1)
|
|
||||||
}
|
|
||||||
</script>
|
|
|
@ -1,150 +0,0 @@
|
||||||
<template>
|
|
||||||
<!-- <doc-alert title="【商机】商机管理、商机状态" url="https://doc.iocoder.cn/crm/business/" />
|
|
||||||
<doc-alert title="【通用】数据权限" url="https://doc.iocoder.cn/crm/permission/" /> -->
|
|
||||||
|
|
||||||
<ContentWrap>
|
|
||||||
<!-- 搜索工作栏 -->
|
|
||||||
<el-form
|
|
||||||
class="-mb-15px"
|
|
||||||
:model="queryParams"
|
|
||||||
ref="queryFormRef"
|
|
||||||
:inline="true"
|
|
||||||
label-width="68px"
|
|
||||||
>
|
|
||||||
<el-form-item>
|
|
||||||
<el-button
|
|
||||||
type="primary"
|
|
||||||
plain
|
|
||||||
@click="openForm('create')"
|
|
||||||
v-hasPermi="['crm:business-status: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="name" />
|
|
||||||
<el-table-column label="应用部门" align="center" prop="deptNames">
|
|
||||||
<template #default="scope">
|
|
||||||
<span v-if="scope.row?.deptNames?.length > 0">
|
|
||||||
{{ scope.row.deptNames.join(' ') }}
|
|
||||||
</span>
|
|
||||||
<span v-else>全公司</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="创建人" align="center" prop="creator" />
|
|
||||||
<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="['crm:business-status:update']"
|
|
||||||
>
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
link
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(scope.row.id)"
|
|
||||||
v-hasPermi="['crm:business-status: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>
|
|
||||||
|
|
||||||
<!-- 表单弹窗:添加/修改 -->
|
|
||||||
<BusinessStatusForm ref="formRef" @success="getList" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { dateFormatter } from '@/utils/formatTime'
|
|
||||||
// import download from '@/utils/download'
|
|
||||||
import * as BusinessStatusApi from '@/api/crm/business/status'
|
|
||||||
import BusinessStatusForm from './BusinessStatusForm.vue'
|
|
||||||
// import { deleteBusinessStatus } from '@/api/crm/business/status'
|
|
||||||
|
|
||||||
defineOptions({ name: 'CrmBusinessStatus' })
|
|
||||||
|
|
||||||
const message = useMessage() // 消息弹窗
|
|
||||||
const { t } = useI18n() // 国际化
|
|
||||||
|
|
||||||
const loading = ref(true) // 列表的加载中
|
|
||||||
const list = ref([]) // 列表的数据
|
|
||||||
const total = ref(0) // 列表的总页数
|
|
||||||
const queryParams = reactive({
|
|
||||||
pageNo: 1,
|
|
||||||
pageSize: 10
|
|
||||||
})
|
|
||||||
const queryFormRef = ref() // 搜索的表单
|
|
||||||
// const exportLoading = ref(false) // 导出的加载中
|
|
||||||
|
|
||||||
/** 查询列表 */
|
|
||||||
const getList = async () => {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
const data = await BusinessStatusApi.getBusinessStatusPage(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 BusinessStatusApi.deleteBusinessStatus(id)
|
|
||||||
message.success(t('common.delSuccess'))
|
|
||||||
// 刷新列表
|
|
||||||
await getList()
|
|
||||||
} catch {}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 初始化 **/
|
|
||||||
onMounted(() => {
|
|
||||||
getList()
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,259 +0,0 @@
|
||||||
<template>
|
|
||||||
<Dialog v-model="dialogVisible" :title="dialogTitle">
|
|
||||||
<el-form
|
|
||||||
ref="formRef"
|
|
||||||
v-loading="formLoading"
|
|
||||||
:model="formData"
|
|
||||||
:rules="formRules"
|
|
||||||
label-width="100px"
|
|
||||||
>
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="线索名称" prop="name">
|
|
||||||
<el-input v-model="formData.name" placeholder="请输入线索名称" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="客户来源" prop="source">
|
|
||||||
<el-select v-model="formData.source" placeholder="请选择客户来源" class="w-1/1">
|
|
||||||
<el-option
|
|
||||||
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_SOURCE)"
|
|
||||||
:key="dict.value"
|
|
||||||
:label="dict.label"
|
|
||||||
:value="dict.value"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="手机" prop="mobile">
|
|
||||||
<el-input v-model="formData.mobile" placeholder="请输入手机" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="负责人" prop="ownerUserId">
|
|
||||||
<el-select
|
|
||||||
v-model="formData.ownerUserId"
|
|
||||||
:disabled="formType !== 'create'"
|
|
||||||
class="w-1/1"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="item in userOptions"
|
|
||||||
:key="item.id"
|
|
||||||
:label="item.nickname"
|
|
||||||
:value="item.id"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="电话" prop="telephone">
|
|
||||||
<el-input v-model="formData.telephone" placeholder="请输入电话" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="邮箱" prop="email">
|
|
||||||
<el-input v-model="formData.email" placeholder="请输入邮箱" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="微信" prop="wechat">
|
|
||||||
<el-input v-model="formData.wechat" placeholder="请输入微信" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="QQ" prop="qq">
|
|
||||||
<el-input v-model="formData.qq" placeholder="请输入 QQ" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="客户行业" prop="industryId">
|
|
||||||
<el-select v-model="formData.industryId" placeholder="请选择客户行业" class="w-1/1">
|
|
||||||
<el-option
|
|
||||||
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_INDUSTRY)"
|
|
||||||
:key="dict.value"
|
|
||||||
:label="dict.label"
|
|
||||||
:value="dict.value"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="客户级别" prop="level">
|
|
||||||
<el-select v-model="formData.level" placeholder="请选择客户级别" class="w-1/1">
|
|
||||||
<el-option
|
|
||||||
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_LEVEL)"
|
|
||||||
:key="dict.value"
|
|
||||||
:label="dict.label"
|
|
||||||
:value="dict.value"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="地址" prop="areaId">
|
|
||||||
<el-cascader
|
|
||||||
v-model="formData.areaId"
|
|
||||||
:options="areaList"
|
|
||||||
:props="defaultProps"
|
|
||||||
class="w-1/1"
|
|
||||||
clearable
|
|
||||||
filterable
|
|
||||||
placeholder="请选择城市"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="详细地址" prop="detailAddress">
|
|
||||||
<el-input v-model="formData.detailAddress" placeholder="请输入详细地址" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="下次联系时间" prop="contactNextTime">
|
|
||||||
<el-date-picker
|
|
||||||
v-model="formData.contactNextTime"
|
|
||||||
placeholder="选择下次联系时间"
|
|
||||||
type="datetime"
|
|
||||||
value-format="x"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="备注" prop="remark">
|
|
||||||
<el-input type="textarea" v-model="formData.remark" placeholder="请输入备注" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</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 * as ClueApi from '@/api/crm/clue'
|
|
||||||
import * as AreaApi from '@/api/system/area'
|
|
||||||
import { defaultProps } from '@/utils/tree'
|
|
||||||
import * as UserApi from '@/api/system/user'
|
|
||||||
import { useUserStore } from '@/store/modules/user'
|
|
||||||
|
|
||||||
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 areaList = ref([]) // 地区列表
|
|
||||||
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
|
|
||||||
const formData = ref({
|
|
||||||
id: undefined,
|
|
||||||
name: undefined,
|
|
||||||
contactNextTime: undefined,
|
|
||||||
ownerUserId: 0,
|
|
||||||
mobile: undefined,
|
|
||||||
telephone: undefined,
|
|
||||||
qq: undefined,
|
|
||||||
wechat: undefined,
|
|
||||||
email: undefined,
|
|
||||||
areaId: undefined,
|
|
||||||
detailAddress: undefined,
|
|
||||||
industryId: undefined,
|
|
||||||
level: undefined,
|
|
||||||
source: undefined,
|
|
||||||
remark: undefined
|
|
||||||
})
|
|
||||||
const formRules = reactive({
|
|
||||||
name: [{ required: true, message: '线索名称不能为空', trigger: 'blur' }],
|
|
||||||
ownerUserId: [{ 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 ClueApi.getClue(id)
|
|
||||||
} finally {
|
|
||||||
formLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 获得地区列表
|
|
||||||
areaList.value = await AreaApi.getAreaTree()
|
|
||||||
// 获得用户列表
|
|
||||||
userOptions.value = await UserApi.getSimpleUserList()
|
|
||||||
// 默认新建时选中自己
|
|
||||||
if (formType.value === 'create') {
|
|
||||||
formData.value.ownerUserId = useUserStore().getUser.id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
|
||||||
|
|
||||||
/** 提交表单 */
|
|
||||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
|
||||||
const submitForm = async () => {
|
|
||||||
// 校验表单
|
|
||||||
if (!formRef) return
|
|
||||||
const valid = await formRef.value.validate()
|
|
||||||
if (!valid) return
|
|
||||||
// 提交请求
|
|
||||||
formLoading.value = true
|
|
||||||
try {
|
|
||||||
const data = formData.value as unknown as ClueApi.ClueVO
|
|
||||||
if (formType.value === 'create') {
|
|
||||||
await ClueApi.createClue(data)
|
|
||||||
message.success(t('common.createSuccess'))
|
|
||||||
} else {
|
|
||||||
await ClueApi.updateClue(data)
|
|
||||||
message.success(t('common.updateSuccess'))
|
|
||||||
}
|
|
||||||
dialogVisible.value = false
|
|
||||||
// 发送操作成功的事件
|
|
||||||
emit('success')
|
|
||||||
} finally {
|
|
||||||
formLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 重置表单 */
|
|
||||||
const resetForm = () => {
|
|
||||||
formData.value = {
|
|
||||||
id: undefined,
|
|
||||||
name: undefined,
|
|
||||||
contactNextTime: undefined,
|
|
||||||
ownerUserId: 0,
|
|
||||||
mobile: undefined,
|
|
||||||
telephone: undefined,
|
|
||||||
qq: undefined,
|
|
||||||
wechat: undefined,
|
|
||||||
email: undefined,
|
|
||||||
areaId: undefined,
|
|
||||||
detailAddress: undefined,
|
|
||||||
industryId: undefined,
|
|
||||||
level: undefined,
|
|
||||||
source: undefined,
|
|
||||||
remark: undefined
|
|
||||||
}
|
|
||||||
formRef.value?.resetFields()
|
|
||||||
}
|
|
||||||
</script>
|
|
|
@ -1,43 +0,0 @@
|
||||||
<template>
|
|
||||||
<div v-loading="loading">
|
|
||||||
<div class="flex items-start justify-between">
|
|
||||||
<div>
|
|
||||||
<!-- 左上:线索基本信息 -->
|
|
||||||
<el-col>
|
|
||||||
<el-row>
|
|
||||||
<span class="text-xl font-bold">{{ clue.name }}</span>
|
|
||||||
</el-row>
|
|
||||||
</el-col>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<!-- 右上:按钮 -->
|
|
||||||
<slot></slot>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<ContentWrap class="mt-10px">
|
|
||||||
<el-descriptions :column="5" direction="vertical">
|
|
||||||
<el-descriptions-item label="线索来源">
|
|
||||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_SOURCE" :value="clue.source" />
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="手机"> {{ clue.mobile }} </el-descriptions-item>
|
|
||||||
<el-descriptions-item label="负责人">
|
|
||||||
{{ clue.ownerUserName }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="创建时间">
|
|
||||||
{{ formatDate(clue.createTime) }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
</el-descriptions>
|
|
||||||
</ContentWrap>
|
|
||||||
</template>
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { DICT_TYPE } from '@/utils/dict'
|
|
||||||
import * as ClueApi from '@/api/crm/clue'
|
|
||||||
import { formatDate } from '@/utils/formatTime'
|
|
||||||
|
|
||||||
defineOptions({ name: 'CrmClueDetailsHeader' })
|
|
||||||
defineProps<{
|
|
||||||
clue: ClueApi.ClueVO // 线索信息
|
|
||||||
loading: boolean // 加载中
|
|
||||||
}>()
|
|
||||||
</script>
|
|
|
@ -1,72 +0,0 @@
|
||||||
<template>
|
|
||||||
<ContentWrap>
|
|
||||||
<el-collapse v-model="activeNames" class="">
|
|
||||||
<el-collapse-item name="basicInfo">
|
|
||||||
<template #title>
|
|
||||||
<span class="text-base font-bold">基本信息</span>
|
|
||||||
</template>
|
|
||||||
<el-descriptions :column="4">
|
|
||||||
<el-descriptions-item label="线索名称">
|
|
||||||
{{ clue.name }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="客户来源">
|
|
||||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_SOURCE" :value="clue.source" />
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="手机">{{ clue.mobile }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="电话">{{ clue.telephone }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="邮箱">{{ clue.email }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="地址">
|
|
||||||
{{ clue.areaName }} {{ clue.detailAddress }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="QQ">{{ clue.qq }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="微信">{{ clue.wechat }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="客户行业">
|
|
||||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_INDUSTRY" :value="clue.industryId" />
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="客户级别">
|
|
||||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_LEVEL" :value="clue.level" />
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="下次联系时间">
|
|
||||||
{{ formatDate(clue.contactNextTime) }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="备注">{{ clue.remark }}</el-descriptions-item>
|
|
||||||
</el-descriptions>
|
|
||||||
</el-collapse-item>
|
|
||||||
<el-collapse-item name="systemInfo">
|
|
||||||
<template #title>
|
|
||||||
<span class="text-base font-bold">系统信息</span>
|
|
||||||
</template>
|
|
||||||
<el-descriptions :column="4">
|
|
||||||
<el-descriptions-item label="负责人">{{ clue.ownerUserName }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="最后跟进记录">
|
|
||||||
{{ clue.contactLastContent }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="最后跟进时间">
|
|
||||||
{{ formatDate(clue.contactLastTime) }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label=""> </el-descriptions-item>
|
|
||||||
<el-descriptions-item label="创建人">{{ clue.creatorName }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="创建时间">
|
|
||||||
{{ formatDate(clue.createTime) }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="更新时间">
|
|
||||||
{{ formatDate(clue.updateTime) }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
</el-descriptions>
|
|
||||||
</el-collapse-item>
|
|
||||||
</el-collapse>
|
|
||||||
</ContentWrap>
|
|
||||||
</template>
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import * as ClueApi from '@/api/crm/clue'
|
|
||||||
import { DICT_TYPE } from '@/utils/dict'
|
|
||||||
import { formatDate } from '@/utils/formatTime'
|
|
||||||
|
|
||||||
defineOptions({ name: 'CrmClueDetailsInfo' })
|
|
||||||
const { clue } = defineProps<{
|
|
||||||
clue: ClueApi.ClueVO // 线索明细
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const activeNames = ref(['basicInfo', 'systemInfo']) // 展示的折叠面板
|
|
||||||
</script>
|
|
||||||
<style lang="scss" scoped></style>
|
|
|
@ -1,130 +0,0 @@
|
||||||
<template>
|
|
||||||
<ClueDetailsHeader :clue="clue" :loading="loading">
|
|
||||||
<el-button
|
|
||||||
v-if="permissionListRef?.validateWrite"
|
|
||||||
v-hasPermi="['crm:clue:update']"
|
|
||||||
type="primary"
|
|
||||||
@click="openForm"
|
|
||||||
>
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button v-if="permissionListRef?.validateOwnerUser" type="primary" @click="transfer">
|
|
||||||
转移
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
v-if="permissionListRef?.validateOwnerUser && !clue.transformStatus"
|
|
||||||
type="success"
|
|
||||||
@click="handleTransform"
|
|
||||||
>
|
|
||||||
转化为客户
|
|
||||||
</el-button>
|
|
||||||
<el-button v-else disabled type="success">已转化客户</el-button>
|
|
||||||
</ClueDetailsHeader>
|
|
||||||
<el-col>
|
|
||||||
<el-tabs>
|
|
||||||
<el-tab-pane label="跟进记录">
|
|
||||||
<FollowUpList :biz-id="clueId" :biz-type="BizTypeEnum.CRM_CLUE" />
|
|
||||||
</el-tab-pane>
|
|
||||||
<el-tab-pane label="基本信息">
|
|
||||||
<ClueDetailsInfo :clue="clue" />
|
|
||||||
</el-tab-pane>
|
|
||||||
<el-tab-pane label="团队成员">
|
|
||||||
<PermissionList
|
|
||||||
ref="permissionListRef"
|
|
||||||
:biz-id="clue.id!"
|
|
||||||
:biz-type="BizTypeEnum.CRM_CLUE"
|
|
||||||
:show-action="true"
|
|
||||||
@quit-team="close"
|
|
||||||
/>
|
|
||||||
</el-tab-pane>
|
|
||||||
<el-tab-pane label="操作日志">
|
|
||||||
<OperateLogV2 :log-list="logList" />
|
|
||||||
</el-tab-pane>
|
|
||||||
</el-tabs>
|
|
||||||
</el-col>
|
|
||||||
|
|
||||||
<!-- 表单弹窗:添加/修改 -->
|
|
||||||
<ClueForm ref="formRef" @success="getClue" />
|
|
||||||
<CrmTransferForm ref="transferFormRef" :biz-type="BizTypeEnum.CRM_CLUE" @success="close" />
|
|
||||||
</template>
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { useTagsViewStore } from '@/store/modules/tagsView'
|
|
||||||
import * as ClueApi from '@/api/crm/clue'
|
|
||||||
import ClueForm from '@/views/crm/clue/ClueForm.vue'
|
|
||||||
import ClueDetailsHeader from './ClueDetailsHeader.vue' // 线索明细 - 头部
|
|
||||||
import ClueDetailsInfo from './ClueDetailsInfo.vue' // 线索明细 - 详细信息
|
|
||||||
import PermissionList from '@/views/crm/permission/components/PermissionList.vue' // 团队成员列表(权限)
|
|
||||||
import CrmTransferForm from '@/views/crm/permission/components/TransferForm.vue'
|
|
||||||
import FollowUpList from '@/views/crm/followup/index.vue'
|
|
||||||
import { BizTypeEnum } from '@/api/crm/permission'
|
|
||||||
import type { OperateLogVO } from '@/api/system/operatelog'
|
|
||||||
import { getOperateLogPage } from '@/api/crm/operateLog'
|
|
||||||
|
|
||||||
defineOptions({ name: 'CrmClueDetail' })
|
|
||||||
|
|
||||||
const clueId = ref(0) // 线索编号
|
|
||||||
const loading = ref(true) // 加载中
|
|
||||||
const message = useMessage() // 消息弹窗
|
|
||||||
const { delView } = useTagsViewStore() // 视图操作
|
|
||||||
const { currentRoute } = useRouter() // 路由
|
|
||||||
|
|
||||||
const permissionListRef = ref<InstanceType<typeof PermissionList>>() // 团队成员列表 Ref
|
|
||||||
|
|
||||||
/** 获取详情 */
|
|
||||||
const clue = ref<ClueApi.ClueVO>({} as ClueApi.ClueVO) // 线索详情
|
|
||||||
const getClue = async () => {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
clue.value = await ClueApi.getClue(clueId.value)
|
|
||||||
await getOperateLog()
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 编辑线索 */
|
|
||||||
const formRef = ref<InstanceType<typeof ClueForm>>() // 线索表单 Ref
|
|
||||||
const openForm = () => {
|
|
||||||
formRef.value?.open('update', clueId.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 线索转移 */
|
|
||||||
const transferFormRef = ref<InstanceType<typeof CrmTransferForm>>() // 线索转移表单 ref
|
|
||||||
const transfer = () => {
|
|
||||||
transferFormRef.value?.open(clueId.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 转化为客户 */
|
|
||||||
const handleTransform = async () => {
|
|
||||||
await message.confirm(`确定将【${clue.value.name}】转化为客户吗?`)
|
|
||||||
await ClueApi.transformClue(clueId.value)
|
|
||||||
message.success(`转化客户【${clue.value.name}】成功`)
|
|
||||||
await getClue()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 获取操作日志 */
|
|
||||||
const logList = ref<OperateLogVO[]>([]) // 操作日志列表
|
|
||||||
const getOperateLog = async () => {
|
|
||||||
const data = await getOperateLogPage({
|
|
||||||
bizType: BizTypeEnum.CRM_CLUE,
|
|
||||||
bizId: clueId.value
|
|
||||||
})
|
|
||||||
logList.value = data.list
|
|
||||||
}
|
|
||||||
|
|
||||||
const close = () => {
|
|
||||||
delView(unref(currentRoute))
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 初始化 */
|
|
||||||
const { params } = useRoute()
|
|
||||||
onMounted(() => {
|
|
||||||
if (!params.id) {
|
|
||||||
message.warning('参数错误,线索不能为空!')
|
|
||||||
close()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
clueId.value = params.id as unknown as number
|
|
||||||
getClue()
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,270 +0,0 @@
|
||||||
<template>
|
|
||||||
<!-- <doc-alert title="【线索】线索管理" url="https://doc.iocoder.cn/crm/clue/" />
|
|
||||||
<doc-alert title="【通用】数据权限" url="https://doc.iocoder.cn/crm/permission/" /> -->
|
|
||||||
|
|
||||||
<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="transformStatus">
|
|
||||||
<el-select v-model="queryParams.transformStatus" class="!w-240px">
|
|
||||||
<el-option :value="false" label="未转化" />
|
|
||||||
<el-option :value="true" label="已转化" />
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="手机号" prop="mobile">
|
|
||||||
<el-input
|
|
||||||
v-model="queryParams.mobile"
|
|
||||||
placeholder="请输入手机号"
|
|
||||||
clearable
|
|
||||||
@keyup.enter="handleQuery"
|
|
||||||
class="!w-240px"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="电话" prop="telephone">
|
|
||||||
<el-input
|
|
||||||
v-model="queryParams.telephone"
|
|
||||||
placeholder="请输入电话"
|
|
||||||
clearable
|
|
||||||
@keyup.enter="handleQuery"
|
|
||||||
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" @click="openForm('create')" v-hasPermi="['crm:clue:create']">
|
|
||||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
type="success"
|
|
||||||
plain
|
|
||||||
@click="handleExport"
|
|
||||||
:loading="exportLoading"
|
|
||||||
v-hasPermi="['crm:clue:export']"
|
|
||||||
>
|
|
||||||
<Icon icon="ep:download" class="mr-5px" /> 导出
|
|
||||||
</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</ContentWrap>
|
|
||||||
|
|
||||||
<!-- 列表 -->
|
|
||||||
<ContentWrap>
|
|
||||||
<el-tabs v-model="activeName" @tab-click="handleTabClick">
|
|
||||||
<el-tab-pane label="我负责的" name="1" />
|
|
||||||
<el-tab-pane label="我参与的" name="2" />
|
|
||||||
<el-tab-pane label="下属负责的" name="3" />
|
|
||||||
</el-tabs>
|
|
||||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
|
||||||
<el-table-column label="线索名称" align="center" prop="name" fixed="left" width="160">
|
|
||||||
<template #default="scope">
|
|
||||||
<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="source" width="100">
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_SOURCE" :value="scope.row.source" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="手机" align="center" prop="mobile" width="120" />
|
|
||||||
<el-table-column label="电话" align="center" prop="telephone" width="130" />
|
|
||||||
<el-table-column label="邮箱" align="center" prop="email" width="180" />
|
|
||||||
<el-table-column label="地址" align="center" prop="detailAddress" width="180" />
|
|
||||||
<el-table-column align="center" label="客户行业" prop="industryId" width="100">
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_INDUSTRY" :value="scope.row.industryId" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="客户级别" prop="level" width="135">
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_LEVEL" :value="scope.row.level" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="下次联系时间"
|
|
||||||
prop="contactNextTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="备注" prop="remark" width="200" />
|
|
||||||
<el-table-column
|
|
||||||
label="最后跟进时间"
|
|
||||||
align="center"
|
|
||||||
prop="contactLastTime"
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="最后跟进记录" prop="contactLastContent" width="200" />
|
|
||||||
<el-table-column align="center" label="负责人" prop="ownerUserName" width="100px" />
|
|
||||||
<el-table-column align="center" label="所属部门" prop="ownerUserDeptName" width="100" />
|
|
||||||
<el-table-column
|
|
||||||
label="更新时间"
|
|
||||||
align="center"
|
|
||||||
prop="updateTime"
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
label="创建时间"
|
|
||||||
align="center"
|
|
||||||
prop="createTime"
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="创建人" prop="creatorName" width="100px" />
|
|
||||||
<el-table-column label="操作" align="center" min-width="110" fixed="right">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
link
|
|
||||||
type="primary"
|
|
||||||
@click="openForm('update', scope.row.id)"
|
|
||||||
v-hasPermi="['crm:clue:update']"
|
|
||||||
>
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
link
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(scope.row.id)"
|
|
||||||
v-hasPermi="['crm:clue: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>
|
|
||||||
|
|
||||||
<!-- 表单弹窗:添加/修改 -->
|
|
||||||
<ClueForm ref="formRef" @success="getList" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { DICT_TYPE } from '@/utils/dict'
|
|
||||||
import { dateFormatter } from '@/utils/formatTime'
|
|
||||||
import download from '@/utils/download'
|
|
||||||
import * as ClueApi from '@/api/crm/clue'
|
|
||||||
import ClueForm from './ClueForm.vue'
|
|
||||||
import { TabsPaneContext } from 'element-plus'
|
|
||||||
|
|
||||||
defineOptions({ name: 'CrmClue' })
|
|
||||||
|
|
||||||
const message = useMessage() // 消息弹窗
|
|
||||||
const { t } = useI18n() // 国际化
|
|
||||||
|
|
||||||
const loading = ref(true) // 列表的加载中
|
|
||||||
const total = ref(0) // 列表的总页数
|
|
||||||
const list = ref([]) // 列表的数据
|
|
||||||
const queryParams = reactive({
|
|
||||||
pageNo: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
sceneType: '1', // 默认和 activeName 相等
|
|
||||||
name: null,
|
|
||||||
telephone: null,
|
|
||||||
mobile: null,
|
|
||||||
transformStatus: false
|
|
||||||
})
|
|
||||||
const queryFormRef = ref() // 搜索的表单
|
|
||||||
const exportLoading = ref(false) // 导出的加载中
|
|
||||||
const activeName = ref('1') // 列表 tab
|
|
||||||
|
|
||||||
/** 查询列表 */
|
|
||||||
const getList = async () => {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
const data = await ClueApi.getCluePage(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()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** tab 切换 */
|
|
||||||
const handleTabClick = (tab: TabsPaneContext) => {
|
|
||||||
queryParams.sceneType = tab.paneName!.toString()
|
|
||||||
handleQuery()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 打开线索详情 */
|
|
||||||
const { push } = useRouter()
|
|
||||||
const openDetail = (id: number) => {
|
|
||||||
push({ name: 'CrmClueDetail', params: { id } })
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 添加/修改操作 */
|
|
||||||
const formRef = ref()
|
|
||||||
const openForm = (type: string, id?: number) => {
|
|
||||||
formRef.value.open(type, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 删除按钮操作 */
|
|
||||||
const handleDelete = async (id: number) => {
|
|
||||||
try {
|
|
||||||
// 删除的二次确认
|
|
||||||
await message.delConfirm()
|
|
||||||
// 发起删除
|
|
||||||
await ClueApi.deleteClue(id)
|
|
||||||
message.success(t('common.delSuccess'))
|
|
||||||
// 刷新列表
|
|
||||||
await getList()
|
|
||||||
} catch {}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 导出按钮操作 */
|
|
||||||
const handleExport = async () => {
|
|
||||||
try {
|
|
||||||
// 导出的二次确认
|
|
||||||
await message.exportConfirm()
|
|
||||||
// 发起导出
|
|
||||||
exportLoading.value = true
|
|
||||||
const data = await ClueApi.exportClue(queryParams)
|
|
||||||
download.excel(data, '线索.xls')
|
|
||||||
} catch {
|
|
||||||
} finally {
|
|
||||||
exportLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 初始化 **/
|
|
||||||
onMounted(() => {
|
|
||||||
getList()
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,310 +0,0 @@
|
||||||
<template>
|
|
||||||
<Dialog v-model="dialogVisible" :title="dialogTitle">
|
|
||||||
<el-form
|
|
||||||
ref="formRef"
|
|
||||||
v-loading="formLoading"
|
|
||||||
:model="formData"
|
|
||||||
:rules="formRules"
|
|
||||||
label-width="100px"
|
|
||||||
>
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="联系人姓名" prop="name">
|
|
||||||
<el-input v-model="formData.name" placeholder="请输入姓名" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="负责人" prop="ownerUserId">
|
|
||||||
<el-select
|
|
||||||
v-model="formData.ownerUserId"
|
|
||||||
:disabled="formType !== 'create'"
|
|
||||||
class="w-1/1"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="item in userOptions"
|
|
||||||
:key="item.id"
|
|
||||||
:label="item.nickname"
|
|
||||||
:value="item.id"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="客户名称" prop="customerId">
|
|
||||||
<el-select
|
|
||||||
:disabled="formData.customerDefault"
|
|
||||||
v-model="formData.customerId"
|
|
||||||
placeholder="请选择客户"
|
|
||||||
class="w-1/1"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="item in customerList"
|
|
||||||
:key="item.id"
|
|
||||||
:label="item.name"
|
|
||||||
:value="item.id"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="手机" prop="mobile">
|
|
||||||
<el-input v-model="formData.mobile" placeholder="请输入手机" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="电话" prop="telephone">
|
|
||||||
<el-input v-model="formData.telephone" placeholder="请输入电话" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="邮箱" prop="email">
|
|
||||||
<el-input v-model="formData.email" placeholder="请输入邮箱" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="微信" prop="wechat">
|
|
||||||
<el-input v-model="formData.wechat" placeholder="请输入微信" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="QQ" prop="qq">
|
|
||||||
<el-input v-model="formData.qq" placeholder="请输入 QQ" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="职位" prop="post">
|
|
||||||
<el-input v-model="formData.post" placeholder="请输入职位" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="关键决策人" prop="master" style="width: 400px">
|
|
||||||
<el-radio-group v-model="formData.master">
|
|
||||||
<el-radio
|
|
||||||
v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
|
|
||||||
:key="dict.value"
|
|
||||||
:label="dict.value"
|
|
||||||
>
|
|
||||||
{{ dict.label }}
|
|
||||||
</el-radio>
|
|
||||||
</el-radio-group>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="性别" prop="sex">
|
|
||||||
<el-select v-model="formData.sex" placeholder="请选择" class="w-1/1">
|
|
||||||
<el-option
|
|
||||||
v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)"
|
|
||||||
:key="dict.value"
|
|
||||||
:label="dict.label"
|
|
||||||
:value="dict.value"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="直属上级" prop="parentId">
|
|
||||||
<el-select v-model="formData.parentId" placeholder="请选择直属上级" class="w-1/1">
|
|
||||||
<el-option
|
|
||||||
v-for="item in contactList"
|
|
||||||
:key="item.id"
|
|
||||||
:disabled="item.id == formData.id"
|
|
||||||
:label="item.name"
|
|
||||||
:value="item.id"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="地址" prop="areaId">
|
|
||||||
<el-cascader
|
|
||||||
v-model="formData.areaId"
|
|
||||||
:options="areaList"
|
|
||||||
:props="defaultProps"
|
|
||||||
class="w-1/1"
|
|
||||||
clearable
|
|
||||||
filterable
|
|
||||||
placeholder="请选择城市"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="详细地址" prop="detailAddress">
|
|
||||||
<el-input v-model="formData.detailAddress" placeholder="请输入详细地址" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="下次联系时间" prop="contactNextTime">
|
|
||||||
<el-date-picker
|
|
||||||
v-model="formData.contactNextTime"
|
|
||||||
placeholder="选择下次联系时间"
|
|
||||||
type="datetime"
|
|
||||||
value-format="x"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="备注" prop="remark">
|
|
||||||
<el-input type="textarea" v-model="formData.remark" placeholder="请输入备注" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</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 * as ContactApi from '@/api/crm/contact'
|
|
||||||
import { DICT_TYPE, getBoolDictOptions, getIntDictOptions } from '@/utils/dict'
|
|
||||||
import * as UserApi from '@/api/system/user'
|
|
||||||
import * as CustomerApi from '@/api/crm/customer'
|
|
||||||
import * as AreaApi from '@/api/system/area'
|
|
||||||
import { defaultProps } from '@/utils/tree'
|
|
||||||
import { useUserStore } from '@/store/modules/user'
|
|
||||||
|
|
||||||
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 areaList = ref([]) // 地区列表
|
|
||||||
const formData = ref({
|
|
||||||
id: undefined,
|
|
||||||
name: undefined,
|
|
||||||
customerId: undefined,
|
|
||||||
contactNextTime: undefined,
|
|
||||||
ownerUserId: 0,
|
|
||||||
mobile: undefined,
|
|
||||||
telephone: undefined,
|
|
||||||
qq: undefined,
|
|
||||||
wechat: undefined,
|
|
||||||
email: undefined,
|
|
||||||
areaId: undefined,
|
|
||||||
detailAddress: undefined,
|
|
||||||
sex: undefined,
|
|
||||||
master: false,
|
|
||||||
post: undefined,
|
|
||||||
parentId: undefined,
|
|
||||||
remark: undefined,
|
|
||||||
businessId: undefined,
|
|
||||||
customerDefault: false
|
|
||||||
})
|
|
||||||
const formRules = reactive({
|
|
||||||
name: [{ required: true, message: '姓名不能为空', trigger: 'blur' }],
|
|
||||||
customerId: [{ required: true, message: '客户不能为空', trigger: 'blur' }],
|
|
||||||
ownerUserId: [{ required: true, message: '负责人不能为空', trigger: 'blur' }]
|
|
||||||
})
|
|
||||||
const formRef = ref() // 表单 Ref
|
|
||||||
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
|
|
||||||
const customerList = ref<CustomerApi.CustomerVO[]>([]) // 客户列表
|
|
||||||
const contactList = ref<ContactApi.ContactVO[]>([]) // 联系人列表
|
|
||||||
|
|
||||||
/** 打开弹窗 */
|
|
||||||
const open = async (type: string, id?: number, customerId?: number, businessId?: number) => {
|
|
||||||
dialogVisible.value = true
|
|
||||||
dialogTitle.value = t('action.' + type)
|
|
||||||
formType.value = type
|
|
||||||
resetForm()
|
|
||||||
// 修改时,设置数据
|
|
||||||
if (id) {
|
|
||||||
formLoading.value = true
|
|
||||||
try {
|
|
||||||
formData.value = await ContactApi.getContact(id)
|
|
||||||
} finally {
|
|
||||||
formLoading.value = false
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (customerId) {
|
|
||||||
formData.value.customerId = customerId
|
|
||||||
formData.value.customerDefault = true // 默认客户的选择,不允许变
|
|
||||||
}
|
|
||||||
// 自动关联 businessId 商机编号
|
|
||||||
if (businessId) {
|
|
||||||
formData.value.businessId = businessId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 获得联系人列表
|
|
||||||
contactList.value = await ContactApi.getSimpleContactList()
|
|
||||||
// 获得客户列表
|
|
||||||
customerList.value = await CustomerApi.getCustomerSimpleList()
|
|
||||||
// 获得地区列表
|
|
||||||
areaList.value = await AreaApi.getAreaTree()
|
|
||||||
// 获得用户列表
|
|
||||||
userOptions.value = await UserApi.getSimpleUserList()
|
|
||||||
// 默认新建时选中自己
|
|
||||||
if (formType.value === 'create') {
|
|
||||||
formData.value.ownerUserId = useUserStore().getUser.id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
|
||||||
|
|
||||||
/** 提交表单 */
|
|
||||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
|
||||||
const submitForm = async () => {
|
|
||||||
// 校验表单
|
|
||||||
if (!formRef) return
|
|
||||||
const valid = await formRef.value.validate()
|
|
||||||
if (!valid) return
|
|
||||||
// 提交请求
|
|
||||||
formLoading.value = true
|
|
||||||
try {
|
|
||||||
const data = formData.value as unknown as ContactApi.ContactVO
|
|
||||||
if (formType.value === 'create') {
|
|
||||||
await ContactApi.createContact(data)
|
|
||||||
message.success(t('common.createSuccess'))
|
|
||||||
} else {
|
|
||||||
await ContactApi.updateContact(data)
|
|
||||||
message.success(t('common.updateSuccess'))
|
|
||||||
}
|
|
||||||
dialogVisible.value = false
|
|
||||||
// 发送操作成功的事件
|
|
||||||
emit('success')
|
|
||||||
} finally {
|
|
||||||
formLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 重置表单 */
|
|
||||||
const resetForm = () => {
|
|
||||||
formData.value = {
|
|
||||||
id: undefined,
|
|
||||||
name: undefined,
|
|
||||||
customerId: undefined,
|
|
||||||
contactNextTime: undefined,
|
|
||||||
ownerUserId: 0,
|
|
||||||
mobile: undefined,
|
|
||||||
telephone: undefined,
|
|
||||||
qq: undefined,
|
|
||||||
wechat: undefined,
|
|
||||||
email: undefined,
|
|
||||||
areaId: undefined,
|
|
||||||
detailAddress: undefined,
|
|
||||||
sex: undefined,
|
|
||||||
master: false,
|
|
||||||
post: undefined,
|
|
||||||
parentId: undefined,
|
|
||||||
remark: undefined,
|
|
||||||
businessId: undefined,
|
|
||||||
customerDefault: false
|
|
||||||
}
|
|
||||||
formRef.value?.resetFields()
|
|
||||||
}
|
|
||||||
</script>
|
|
|
@ -1,185 +0,0 @@
|
||||||
<template>
|
|
||||||
<!-- 操作栏 -->
|
|
||||||
<el-row justify="end">
|
|
||||||
<el-button @click="openForm">
|
|
||||||
<Icon class="mr-5px" icon="system-uicons:contacts" />
|
|
||||||
创建联系人
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
v-if="queryParams.businessId"
|
|
||||||
v-hasPermi="['crm:contact:create-business']"
|
|
||||||
@click="openBusinessModal"
|
|
||||||
>
|
|
||||||
<Icon class="mr-5px" icon="ep:circle-plus" />
|
|
||||||
关联
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
v-if="queryParams.businessId"
|
|
||||||
v-hasPermi="['crm:contact:delete-business']"
|
|
||||||
@click="deleteContactBusinessList"
|
|
||||||
>
|
|
||||||
<Icon class="mr-5px" icon="ep:remove" />
|
|
||||||
解除关联
|
|
||||||
</el-button>
|
|
||||||
</el-row>
|
|
||||||
|
|
||||||
<!-- 列表 -->
|
|
||||||
<ContentWrap class="mt-10px">
|
|
||||||
<el-table
|
|
||||||
ref="contactRef"
|
|
||||||
v-loading="loading"
|
|
||||||
:data="list"
|
|
||||||
:show-overflow-tooltip="true"
|
|
||||||
:stripe="true"
|
|
||||||
>
|
|
||||||
<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 :underline="false" type="primary" @click="openDetail(scope.row.id)">
|
|
||||||
{{ scope.row.name }}
|
|
||||||
</el-link>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<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>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
<!-- 分页 -->
|
|
||||||
<Pagination
|
|
||||||
v-model:limit="queryParams.pageSize"
|
|
||||||
v-model:page="queryParams.pageNo"
|
|
||||||
:total="total"
|
|
||||||
@pagination="getList"
|
|
||||||
/>
|
|
||||||
</ContentWrap>
|
|
||||||
|
|
||||||
<!-- 表单弹窗:添加 -->
|
|
||||||
<ContactForm ref="formRef" @success="getList" />
|
|
||||||
<!-- 关联商机选择弹框 -->
|
|
||||||
<ContactListModal
|
|
||||||
v-if="customerId"
|
|
||||||
ref="contactModalRef"
|
|
||||||
:customer-id="customerId"
|
|
||||||
@success="createContactBusinessList"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import * as ContactApi from '@/api/crm/contact'
|
|
||||||
import ContactForm from './../ContactForm.vue'
|
|
||||||
import { DICT_TYPE } from '@/utils/dict'
|
|
||||||
import { BizTypeEnum } from '@/api/crm/permission'
|
|
||||||
import ContactListModal from './ContactListModal.vue'
|
|
||||||
|
|
||||||
defineOptions({ name: 'CrmContactList' })
|
|
||||||
const props = defineProps<{
|
|
||||||
bizType: number // 业务类型
|
|
||||||
bizId: number // 业务编号
|
|
||||||
customerId?: number // 特殊:客户编号;在【商机】详情中,可以传递客户编号,默认新建的联系人关联到该客户
|
|
||||||
businessId?: number // 特殊:商机编号;在【商机】详情中,可以传递商机编号,默认新建的联系人关联到该商机
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const loading = ref(true) // 列表的加载中
|
|
||||||
const total = ref(0) // 列表的总页数
|
|
||||||
const list = ref([]) // 列表的数据
|
|
||||||
const queryParams = reactive({
|
|
||||||
pageNo: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
customerId: undefined as unknown, // 允许 undefined + number
|
|
||||||
businessId: undefined as unknown // 允许 undefined + number
|
|
||||||
})
|
|
||||||
const message = useMessage()
|
|
||||||
|
|
||||||
/** 查询列表 */
|
|
||||||
const getList = async () => {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
// 置空参数
|
|
||||||
queryParams.customerId = undefined
|
|
||||||
// 执行查询
|
|
||||||
let data = { list: [], total: 0 }
|
|
||||||
switch (props.bizType) {
|
|
||||||
case BizTypeEnum.CRM_CUSTOMER:
|
|
||||||
queryParams.customerId = props.bizId
|
|
||||||
data = await ContactApi.getContactPageByCustomer(queryParams)
|
|
||||||
break
|
|
||||||
case BizTypeEnum.CRM_BUSINESS:
|
|
||||||
queryParams.businessId = props.bizId
|
|
||||||
data = await ContactApi.getContactPageByBusiness(queryParams)
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
list.value = data.list
|
|
||||||
total.value = data.total
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 搜索按钮操作 */
|
|
||||||
const handleQuery = () => {
|
|
||||||
queryParams.pageNo = 1
|
|
||||||
getList()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 添加操作 */
|
|
||||||
const formRef = ref()
|
|
||||||
const openForm = () => {
|
|
||||||
formRef.value.open('create', undefined, props.customerId, props.businessId)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 打开联系人详情 */
|
|
||||||
const { push } = useRouter()
|
|
||||||
const openDetail = (id: number) => {
|
|
||||||
push({ name: 'CrmContactDetail', params: { id } })
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 打开联系人与商机的关联弹窗 */
|
|
||||||
const contactModalRef = ref()
|
|
||||||
const openBusinessModal = () => {
|
|
||||||
contactModalRef.value.open()
|
|
||||||
}
|
|
||||||
const createContactBusinessList = async (contactIds: number[]) => {
|
|
||||||
const data = {
|
|
||||||
businessId: props.bizId,
|
|
||||||
contactIds: contactIds
|
|
||||||
} as ContactApi.ContactBusiness2ReqVO
|
|
||||||
contactRef.value.getSelectionRows().forEach((row: ContactApi.ContactVO) => {
|
|
||||||
data.contactIds.push(row.id)
|
|
||||||
})
|
|
||||||
await ContactApi.createContactBusinessList2(data)
|
|
||||||
// 刷新列表
|
|
||||||
message.success('关联联系人成功')
|
|
||||||
handleQuery()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 解除联系人与商机的关联 */
|
|
||||||
const contactRef = ref()
|
|
||||||
const deleteContactBusinessList = async () => {
|
|
||||||
const data = {
|
|
||||||
businessId: props.bizId,
|
|
||||||
contactIds: contactRef.value.getSelectionRows().map((row: ContactApi.ContactVO) => row.id)
|
|
||||||
} as ContactApi.ContactBusiness2ReqVO
|
|
||||||
if (data.contactIds.length === 0) {
|
|
||||||
return message.error('未选择联系人')
|
|
||||||
}
|
|
||||||
await ContactApi.deleteContactBusinessList2(data)
|
|
||||||
// 刷新列表
|
|
||||||
message.success('取关联系人成功')
|
|
||||||
handleQuery()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 监听打开的 bizId + bizType,从而加载最新的列表 */
|
|
||||||
watch(
|
|
||||||
() => [props.bizId, props.bizType],
|
|
||||||
() => {
|
|
||||||
handleQuery()
|
|
||||||
},
|
|
||||||
{ immediate: true, deep: true }
|
|
||||||
)
|
|
||||||
</script>
|
|
|
@ -1,160 +0,0 @@
|
||||||
<template>
|
|
||||||
<Dialog v-model="dialogVisible" title="关联联系人">
|
|
||||||
<!-- 搜索工作栏 -->
|
|
||||||
<ContentWrap>
|
|
||||||
<el-form
|
|
||||||
ref="queryFormRef"
|
|
||||||
:inline="true"
|
|
||||||
:model="queryParams"
|
|
||||||
class="-mb-15px"
|
|
||||||
label-width="90px"
|
|
||||||
>
|
|
||||||
<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>
|
|
||||||
<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>
|
|
||||||
</ContentWrap>
|
|
||||||
|
|
||||||
<!-- 列表 -->
|
|
||||||
<ContentWrap class="mt-10px">
|
|
||||||
<el-table
|
|
||||||
ref="contactRef"
|
|
||||||
v-loading="loading"
|
|
||||||
:data="list"
|
|
||||||
:show-overflow-tooltip="true"
|
|
||||||
:stripe="true"
|
|
||||||
>
|
|
||||||
<el-table-column type="selection" width="55" />
|
|
||||||
<el-table-column align="center" fixed="left" label="姓名" prop="name">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-link :underline="false" type="primary" @click="openDetail(scope.row.id)">
|
|
||||||
{{ scope.row.name }}
|
|
||||||
</el-link>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<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>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
<!-- 分页 -->
|
|
||||||
<Pagination
|
|
||||||
v-model:limit="queryParams.pageSize"
|
|
||||||
v-model:page="queryParams.pageNo"
|
|
||||||
:total="total"
|
|
||||||
@pagination="getList"
|
|
||||||
/>
|
|
||||||
</ContentWrap>
|
|
||||||
<template #footer>
|
|
||||||
<el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
|
|
||||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- 表单弹窗:添加 -->
|
|
||||||
<ContactForm ref="formRef" @success="getList" />
|
|
||||||
</Dialog>
|
|
||||||
</template>
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import * as ContactApi from '@/api/crm/contact'
|
|
||||||
import ContactForm from '../ContactForm.vue'
|
|
||||||
import { DICT_TYPE } from '@/utils/dict'
|
|
||||||
|
|
||||||
const message = useMessage() // 消息弹窗
|
|
||||||
const props = defineProps<{
|
|
||||||
customerId: number
|
|
||||||
}>()
|
|
||||||
defineOptions({ name: 'ContactListModal' })
|
|
||||||
|
|
||||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
|
||||||
const loading = ref(true) // 列表的加载中
|
|
||||||
const total = ref(0) // 列表的总页数
|
|
||||||
const list = ref([]) // 列表的数据
|
|
||||||
const queryFormRef = ref() // 搜索的表单
|
|
||||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
|
||||||
const queryParams = reactive({
|
|
||||||
pageNo: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
name: undefined,
|
|
||||||
customerId: props.customerId
|
|
||||||
})
|
|
||||||
|
|
||||||
/** 打开弹窗 */
|
|
||||||
const open = async () => {
|
|
||||||
dialogVisible.value = true
|
|
||||||
queryParams.customerId = props.customerId // 解决 props.customerId 没更新到 queryParams 上的问题
|
|
||||||
await getList()
|
|
||||||
}
|
|
||||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
|
||||||
|
|
||||||
/** 查询列表 */
|
|
||||||
const getList = async () => {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
const data = await ContactApi.getContactPageByCustomer(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 = () => {
|
|
||||||
formRef.value.open('create')
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 关联联系人提交 */
|
|
||||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
|
||||||
const contactRef = ref()
|
|
||||||
const submitForm = async () => {
|
|
||||||
const contactIds = contactRef.value.getSelectionRows().map((row: ContactApi.ContactVO) => row.id)
|
|
||||||
if (contactIds.length === 0) {
|
|
||||||
return message.error('未选择联系人')
|
|
||||||
}
|
|
||||||
dialogVisible.value = false
|
|
||||||
emit('success', contactIds, contactRef.value.getSelectionRows())
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 打开联系人详情 */
|
|
||||||
const { push } = useRouter()
|
|
||||||
const openDetail = (id: number) => {
|
|
||||||
push({ name: 'CrmContactDetail', params: { id } })
|
|
||||||
}
|
|
||||||
</script>
|
|
|
@ -1,33 +0,0 @@
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<div class="flex items-start justify-between">
|
|
||||||
<div>
|
|
||||||
<el-col>
|
|
||||||
<el-row>
|
|
||||||
<span class="text-xl font-bold">{{ contact.name }}</span>
|
|
||||||
</el-row>
|
|
||||||
</el-col>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<!-- 右上:按钮 -->
|
|
||||||
<slot></slot>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<ContentWrap class="mt-10px">
|
|
||||||
<el-descriptions :column="5" direction="vertical">
|
|
||||||
<el-descriptions-item label="客户名称">{{ contact.customerName }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="职务">{{ contact.post }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="手机">{{ contact.mobile }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="创建时间">
|
|
||||||
{{ formatDate(contact.createTime) }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
</el-descriptions>
|
|
||||||
</ContentWrap>
|
|
||||||
</template>
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import * as ContactApi from '@/api/crm/contact'
|
|
||||||
import { formatDate } from '@/utils/formatTime'
|
|
||||||
|
|
||||||
const { contact } = defineProps<{ contact: ContactApi.ContactVO }>()
|
|
||||||
</script>
|
|
|
@ -1,69 +0,0 @@
|
||||||
<template>
|
|
||||||
<ContentWrap>
|
|
||||||
<el-collapse v-model="activeNames">
|
|
||||||
<el-collapse-item name="basicInfo">
|
|
||||||
<template #title>
|
|
||||||
<span class="text-base font-bold">基本信息</span>
|
|
||||||
</template>
|
|
||||||
<el-descriptions :column="4">
|
|
||||||
<el-descriptions-item label="姓名">{{ contact.name }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="客户名称">{{ contact.customerName }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="手机">{{ contact.mobile }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="电话">{{ contact.telephone }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="邮箱">{{ contact.email }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="QQ">{{ contact.qq }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="微信">{{ contact.wechat }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="地址">
|
|
||||||
{{ contact.areaName }} {{ contact.detailAddress }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="职务">{{ contact.post }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="直属上级">{{ contact.parentName }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="关键决策人">
|
|
||||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="contact.master" />
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="性别">
|
|
||||||
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="contact.sex" />
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="下次联系时间">
|
|
||||||
{{ formatDate(contact.contactNextTime) }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="备注">{{ contact.remark }}</el-descriptions-item>
|
|
||||||
</el-descriptions>
|
|
||||||
</el-collapse-item>
|
|
||||||
<el-collapse-item name="systemInfo">
|
|
||||||
<template #title>
|
|
||||||
<span class="text-base font-bold">系统信息</span>
|
|
||||||
</template>
|
|
||||||
<el-descriptions :column="4">
|
|
||||||
<el-descriptions-item label="负责人">{{ contact.ownerUserName }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="最后跟进记录">
|
|
||||||
{{ contact.contactLastContent }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="最后跟进时间">
|
|
||||||
{{ formatDate(contact.contactLastTime) }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label=""> </el-descriptions-item>
|
|
||||||
<el-descriptions-item label="创建人">{{ contact.creatorName }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="创建时间">
|
|
||||||
{{ formatDate(contact.createTime) }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="更新时间">
|
|
||||||
{{ formatDate(contact.updateTime) }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
</el-descriptions>
|
|
||||||
</el-collapse-item>
|
|
||||||
</el-collapse>
|
|
||||||
</ContentWrap>
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
import * as ContactApi from '@/api/crm/contact'
|
|
||||||
import { DICT_TYPE } from '@/utils/dict'
|
|
||||||
import { formatDate } from '@/utils/formatTime'
|
|
||||||
|
|
||||||
const { contact } = defineProps<{
|
|
||||||
contact: ContactApi.ContactVO
|
|
||||||
}>()
|
|
||||||
|
|
||||||
// 展示的折叠面板
|
|
||||||
const activeNames = ref(['basicInfo', 'systemInfo'])
|
|
||||||
</script>
|
|
|
@ -1,121 +0,0 @@
|
||||||
<template>
|
|
||||||
<ContactDetailsHeader v-loading="loading" :contact="contact">
|
|
||||||
<el-button v-if="permissionListRef?.validateWrite" @click="openForm('update', contact.id)">
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button v-if="permissionListRef?.validateOwnerUser" type="primary" @click="transfer">
|
|
||||||
转移
|
|
||||||
</el-button>
|
|
||||||
</ContactDetailsHeader>
|
|
||||||
<el-col>
|
|
||||||
<el-tabs>
|
|
||||||
<el-tab-pane label="跟进记录">
|
|
||||||
<FollowUpList :biz-id="contactId" :biz-type="BizTypeEnum.CRM_CONTACT" />
|
|
||||||
</el-tab-pane>
|
|
||||||
<el-tab-pane label="详细资料">
|
|
||||||
<ContactDetailsInfo :contact="contact" />
|
|
||||||
</el-tab-pane>
|
|
||||||
<el-tab-pane label="操作日志">
|
|
||||||
<OperateLogV2 :log-list="logList" />
|
|
||||||
</el-tab-pane>
|
|
||||||
<el-tab-pane label="团队成员">
|
|
||||||
<PermissionList
|
|
||||||
ref="permissionListRef"
|
|
||||||
:biz-id="contact.id!"
|
|
||||||
:biz-type="BizTypeEnum.CRM_CONTACT"
|
|
||||||
:show-action="true"
|
|
||||||
@quit-team="close"
|
|
||||||
/>
|
|
||||||
</el-tab-pane>
|
|
||||||
<el-tab-pane label="商机" lazy>
|
|
||||||
<BusinessList
|
|
||||||
:biz-id="contact.id!"
|
|
||||||
:biz-type="BizTypeEnum.CRM_CONTACT"
|
|
||||||
:contact-id="contact.id"
|
|
||||||
:customer-id="contact.customerId"
|
|
||||||
/>
|
|
||||||
</el-tab-pane>
|
|
||||||
</el-tabs>
|
|
||||||
</el-col>
|
|
||||||
<!-- 表单弹窗:添加/修改 -->
|
|
||||||
<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'
|
|
||||||
import * as ContactApi from '@/api/crm/contact'
|
|
||||||
import ContactDetailsHeader from '@/views/crm/contact/detail/ContactDetailsHeader.vue'
|
|
||||||
import ContactDetailsInfo from '@/views/crm/contact/detail/ContactDetailsInfo.vue'
|
|
||||||
import BusinessList from '@/views/crm/business/components/BusinessList.vue' // 商机列表
|
|
||||||
import PermissionList from '@/views/crm/permission/components/PermissionList.vue' // 团队成员列表(权限)
|
|
||||||
import { BizTypeEnum } from '@/api/crm/permission'
|
|
||||||
import { OperateLogVO } from '@/api/system/operatelog'
|
|
||||||
import { getOperateLogPage } from '@/api/crm/operateLog'
|
|
||||||
import ContactForm from '@/views/crm/contact/ContactForm.vue'
|
|
||||||
import CrmTransferForm from '@/views/crm/permission/components/TransferForm.vue'
|
|
||||||
import FollowUpList from '@/views/crm/followup/index.vue'
|
|
||||||
|
|
||||||
defineOptions({ name: 'CrmContactDetail' })
|
|
||||||
|
|
||||||
const message = useMessage()
|
|
||||||
|
|
||||||
const contactId = ref(0) // 线索编号
|
|
||||||
const loading = ref(true) // 加载中
|
|
||||||
const contact = ref<ContactApi.ContactVO>({} as ContactApi.ContactVO) // 联系人详情
|
|
||||||
const permissionListRef = ref<InstanceType<typeof PermissionList>>() // 团队成员列表 Ref
|
|
||||||
|
|
||||||
/** 获取详情 */
|
|
||||||
const getContact = async () => {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
contact.value = await ContactApi.getContact(contactId.value)
|
|
||||||
await getOperateLog(contactId.value)
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 编辑 */
|
|
||||||
const formRef = ref()
|
|
||||||
const openForm = (type: string, id?: number) => {
|
|
||||||
formRef.value.open(type, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 联系人转移 */
|
|
||||||
const transferFormRef = ref<InstanceType<typeof CrmTransferForm>>() // 联系人转移表单 ref
|
|
||||||
const transfer = () => {
|
|
||||||
transferFormRef.value?.open(contact.value.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 获取操作日志 */
|
|
||||||
const logList = ref<OperateLogVO[]>([]) // 操作日志列表
|
|
||||||
const getOperateLog = async (contactId: number) => {
|
|
||||||
if (!contactId) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const data = await getOperateLogPage({
|
|
||||||
bizType: BizTypeEnum.CRM_CONTACT,
|
|
||||||
bizId: contactId
|
|
||||||
})
|
|
||||||
logList.value = data.list
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 关闭窗口 */
|
|
||||||
const { delView } = useTagsViewStore() // 视图操作
|
|
||||||
const { currentRoute } = useRouter() // 路由
|
|
||||||
const close = () => {
|
|
||||||
delView(unref(currentRoute))
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 初始化 */
|
|
||||||
const { params } = useRoute()
|
|
||||||
onMounted(async () => {
|
|
||||||
if (!params.id) {
|
|
||||||
message.warning('参数错误,联系人不能为空!')
|
|
||||||
close()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
contactId.value = params.id as unknown as number
|
|
||||||
await getContact()
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,332 +0,0 @@
|
||||||
<template>
|
|
||||||
<!-- <doc-alert title="【客户】客户管理、公海客户" url="https://doc.iocoder.cn/crm/customer/" />
|
|
||||||
<doc-alert title="【通用】数据权限" url="https://doc.iocoder.cn/crm/permission/" /> -->
|
|
||||||
|
|
||||||
<ContentWrap>
|
|
||||||
<!-- 搜索工作栏 -->
|
|
||||||
<el-form
|
|
||||||
ref="queryFormRef"
|
|
||||||
:inline="true"
|
|
||||||
:model="queryParams"
|
|
||||||
class="-mb-15px"
|
|
||||||
label-width="68px"
|
|
||||||
>
|
|
||||||
<el-form-item label="客户" prop="customerId">
|
|
||||||
<el-select
|
|
||||||
v-model="queryParams.customerId"
|
|
||||||
class="!w-240px"
|
|
||||||
clearable
|
|
||||||
lable-key="name"
|
|
||||||
placeholder="请选择客户"
|
|
||||||
value-key="id"
|
|
||||||
@keyup.enter="handleQuery"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="item in customerList"
|
|
||||||
:key="item.id"
|
|
||||||
:label="item.name"
|
|
||||||
:value="item.id!"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<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="mobile">
|
|
||||||
<el-input
|
|
||||||
v-model="queryParams.mobile"
|
|
||||||
class="!w-240px"
|
|
||||||
clearable
|
|
||||||
placeholder="请输入手机号"
|
|
||||||
@keyup.enter="handleQuery"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="电话" prop="telephone">
|
|
||||||
<el-input
|
|
||||||
v-model="queryParams.telephone"
|
|
||||||
class="!w-240px"
|
|
||||||
clearable
|
|
||||||
placeholder="请输入电话"
|
|
||||||
@keyup.enter="handleQuery"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="微信" prop="wechat">
|
|
||||||
<el-input
|
|
||||||
v-model="queryParams.wechat"
|
|
||||||
class="!w-240px"
|
|
||||||
clearable
|
|
||||||
placeholder="请输入微信"
|
|
||||||
@keyup.enter="handleQuery"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="电子邮箱" prop="email">
|
|
||||||
<el-input
|
|
||||||
v-model="queryParams.email"
|
|
||||||
class="!w-240px"
|
|
||||||
clearable
|
|
||||||
placeholder="请输入电子邮箱"
|
|
||||||
@keyup.enter="handleQuery"
|
|
||||||
/>
|
|
||||||
</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-button v-hasPermi="['crm:contact:create']" type="primary" @click="openForm('create')">
|
|
||||||
<Icon class="mr-5px" icon="ep:plus" />
|
|
||||||
新增
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
v-hasPermi="['crm:contact:export']"
|
|
||||||
:loading="exportLoading"
|
|
||||||
plain
|
|
||||||
type="success"
|
|
||||||
@click="handleExport"
|
|
||||||
>
|
|
||||||
<Icon class="mr-5px" icon="ep:download" />
|
|
||||||
导出
|
|
||||||
</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</ContentWrap>
|
|
||||||
|
|
||||||
<!-- 列表 -->
|
|
||||||
<ContentWrap>
|
|
||||||
<el-tabs v-model="activeName" @tab-click="handleTabClick">
|
|
||||||
<el-tab-pane label="我负责的" name="1" />
|
|
||||||
<el-tab-pane label="我参与的" name="2" />
|
|
||||||
<el-tab-pane label="下属负责的" name="3" />
|
|
||||||
</el-tabs>
|
|
||||||
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true">
|
|
||||||
<el-table-column align="center" fixed="left" label="联系人姓名" prop="name" width="160">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-link :underline="false" type="primary" @click="openDetail(scope.row.id)">
|
|
||||||
{{ scope.row.name }}
|
|
||||||
</el-link>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" fixed="left" label="客户名称" prop="customerName" width="120">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-link
|
|
||||||
:underline="false"
|
|
||||||
type="primary"
|
|
||||||
@click="openCustomerDetail(scope.row.customerId)"
|
|
||||||
>
|
|
||||||
{{ scope.row.customerName }}
|
|
||||||
</el-link>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="手机" prop="mobile" width="120" />
|
|
||||||
<el-table-column align="center" label="电话" prop="telephone" width="130" />
|
|
||||||
<el-table-column align="center" label="邮箱" prop="email" width="180" />
|
|
||||||
<el-table-column align="center" label="职位" prop="post" width="120" />
|
|
||||||
<el-table-column align="center" label="地址" prop="detailAddress" width="120" />
|
|
||||||
<el-table-column align="center" label="关键决策人" prop="master" width="100">
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.master" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="直属上级" prop="parentName" width="160">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-link :underline="false" type="primary" @click="openDetail(scope.row.parentId)">
|
|
||||||
{{ scope.row.parentName }}
|
|
||||||
</el-link>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="地址" align="center" prop="detailAddress" width="180" />
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="下次联系时间"
|
|
||||||
prop="contactNextTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<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 align="center" label="备注" prop="remark" />
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="最后跟进时间"
|
|
||||||
prop="contactLastTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="负责人" prop="ownerUserName" width="120" />
|
|
||||||
<el-table-column align="center" label="所属部门" prop="ownerUserDeptName" width="100" />
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="更新时间"
|
|
||||||
prop="updateTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="创建时间"
|
|
||||||
prop="createTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="创建人" prop="creatorName" width="120" />
|
|
||||||
<el-table-column align="center" fixed="right" label="操作" width="200">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
v-hasPermi="['crm:contact:update']"
|
|
||||||
link
|
|
||||||
type="primary"
|
|
||||||
@click="openForm('update', scope.row.id)"
|
|
||||||
>
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
v-hasPermi="['crm:contact:delete']"
|
|
||||||
link
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(scope.row.id)"
|
|
||||||
>
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
<!-- 分页 -->
|
|
||||||
<Pagination
|
|
||||||
v-model:limit="queryParams.pageSize"
|
|
||||||
v-model:page="queryParams.pageNo"
|
|
||||||
:total="total"
|
|
||||||
@pagination="getList"
|
|
||||||
/>
|
|
||||||
</ContentWrap>
|
|
||||||
|
|
||||||
<!-- 表单弹窗:添加/修改 -->
|
|
||||||
<ContactForm ref="formRef" @success="getList" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { dateFormatter } from '@/utils/formatTime'
|
|
||||||
import download from '@/utils/download'
|
|
||||||
import * as ContactApi from '@/api/crm/contact'
|
|
||||||
import ContactForm from './ContactForm.vue'
|
|
||||||
import { DICT_TYPE } from '@/utils/dict'
|
|
||||||
import * as CustomerApi from '@/api/crm/customer'
|
|
||||||
import { TabsPaneContext } from 'element-plus'
|
|
||||||
|
|
||||||
defineOptions({ name: 'CrmContact' })
|
|
||||||
|
|
||||||
const message = useMessage() // 消息弹窗
|
|
||||||
const { t } = useI18n() // 国际化
|
|
||||||
|
|
||||||
const loading = ref(true) // 列表的加载中
|
|
||||||
const total = ref(0) // 列表的总页数
|
|
||||||
const list = ref([]) // 列表的数据
|
|
||||||
const queryParams = reactive({
|
|
||||||
pageNo: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
sceneType: '1', // 默认和 activeName 相等
|
|
||||||
mobile: undefined,
|
|
||||||
telephone: undefined,
|
|
||||||
email: undefined,
|
|
||||||
customerId: undefined,
|
|
||||||
name: undefined,
|
|
||||||
wechat: undefined
|
|
||||||
})
|
|
||||||
const queryFormRef = ref() // 搜索的表单
|
|
||||||
const exportLoading = ref(false) // 导出的加载中
|
|
||||||
const activeName = ref('1') // 列表 tab
|
|
||||||
const customerList = ref<CustomerApi.CustomerVO[]>([]) // 客户列表
|
|
||||||
|
|
||||||
/** 查询列表 */
|
|
||||||
const getList = async () => {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
const data = await ContactApi.getContactPage(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()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** tab 切换 */
|
|
||||||
const handleTabClick = (tab: TabsPaneContext) => {
|
|
||||||
queryParams.sceneType = tab.paneName!.toString()
|
|
||||||
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 ContactApi.deleteContact(id)
|
|
||||||
message.success(t('common.delSuccess'))
|
|
||||||
// 刷新列表
|
|
||||||
await getList()
|
|
||||||
} catch {}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 导出按钮操作 */
|
|
||||||
const handleExport = async () => {
|
|
||||||
try {
|
|
||||||
// 导出的二次确认
|
|
||||||
await message.exportConfirm()
|
|
||||||
// 发起导出
|
|
||||||
exportLoading.value = true
|
|
||||||
const data = await ContactApi.exportContact(queryParams)
|
|
||||||
download.excel(data, '联系人.xls')
|
|
||||||
} catch {
|
|
||||||
} finally {
|
|
||||||
exportLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 打开联系人详情 */
|
|
||||||
const { push } = useRouter()
|
|
||||||
const openDetail = (id: number) => {
|
|
||||||
push({ name: 'CrmContactDetail', params: { id } })
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 打开客户详情 */
|
|
||||||
const openCustomerDetail = (id: number) => {
|
|
||||||
push({ name: 'CrmCustomerDetail', params: { id } })
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 初始化 **/
|
|
||||||
onMounted(async () => {
|
|
||||||
await getList()
|
|
||||||
customerList.value = await CustomerApi.getCustomerSimpleList()
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,369 +0,0 @@
|
||||||
<template>
|
|
||||||
<Dialog v-model="dialogVisible" :title="dialogTitle" width="1280">
|
|
||||||
<el-form
|
|
||||||
ref="formRef"
|
|
||||||
v-loading="formLoading"
|
|
||||||
:model="formData"
|
|
||||||
:rules="formRules"
|
|
||||||
label-width="120px"
|
|
||||||
>
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="8">
|
|
||||||
<el-form-item label="合同编号" prop="no">
|
|
||||||
<el-input disabled v-model="formData.no" placeholder="保存时自动生成" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="8">
|
|
||||||
<el-form-item label="合同名称" prop="name">
|
|
||||||
<el-input v-model="formData.name" placeholder="请输入合同名称" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="8">
|
|
||||||
<el-form-item label="负责人" prop="ownerUserId">
|
|
||||||
<el-select
|
|
||||||
v-model="formData.ownerUserId"
|
|
||||||
:disabled="formType !== 'create'"
|
|
||||||
class="w-1/1"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="item in userOptions"
|
|
||||||
:key="item.id"
|
|
||||||
:label="item.nickname"
|
|
||||||
:value="item.id"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="8">
|
|
||||||
<el-form-item label="客户名称" prop="customerId">
|
|
||||||
<el-select
|
|
||||||
v-model="formData.customerId"
|
|
||||||
placeholder="请选择客户"
|
|
||||||
class="w-1/1"
|
|
||||||
@change="handleCustomerChange"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="item in customerList"
|
|
||||||
:key="item.id"
|
|
||||||
:label="item.name"
|
|
||||||
:value="item.id"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="8">
|
|
||||||
<el-form-item label="商机名称" prop="businessId">
|
|
||||||
<el-select
|
|
||||||
@change="handleBusinessChange"
|
|
||||||
:disabled="!formData.customerId"
|
|
||||||
v-model="formData.businessId"
|
|
||||||
class="w-1/1"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="item in getBusinessOptions"
|
|
||||||
:key="item.id"
|
|
||||||
:label="item.name"
|
|
||||||
:value="item.id!"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="8">
|
|
||||||
<el-form-item label="下单日期" prop="orderDate">
|
|
||||||
<el-date-picker
|
|
||||||
v-model="formData.orderDate"
|
|
||||||
placeholder="选择下单日期"
|
|
||||||
type="date"
|
|
||||||
value-format="x"
|
|
||||||
class="!w-1/1"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="8">
|
|
||||||
<el-form-item label="开始时间" prop="startTime">
|
|
||||||
<el-date-picker
|
|
||||||
v-model="formData.startTime"
|
|
||||||
placeholder="选择开始时间"
|
|
||||||
type="date"
|
|
||||||
value-format="x"
|
|
||||||
class="!w-1/1"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="8">
|
|
||||||
<el-form-item label="结束时间" prop="endTime">
|
|
||||||
<el-date-picker
|
|
||||||
v-model="formData.endTime"
|
|
||||||
placeholder="选择结束时间"
|
|
||||||
type="date"
|
|
||||||
value-format="x"
|
|
||||||
class="!w-1/1"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="8">
|
|
||||||
<el-form-item label="公司签约人" prop="signUserId">
|
|
||||||
<el-select v-model="formData.signUserId" class="w-1/1">
|
|
||||||
<el-option
|
|
||||||
v-for="item in userOptions"
|
|
||||||
:key="item.id"
|
|
||||||
:label="item.nickname"
|
|
||||||
:value="item.id!"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="8">
|
|
||||||
<el-form-item label="客户签约人" prop="signContactId">
|
|
||||||
<el-select
|
|
||||||
v-model="formData.signContactId"
|
|
||||||
:disabled="!formData.customerId"
|
|
||||||
class="w-1/1"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="item in getContactOptions"
|
|
||||||
:key="item.id"
|
|
||||||
:label="item.name"
|
|
||||||
:value="item.id"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="8">
|
|
||||||
<el-form-item label="备注" prop="remark">
|
|
||||||
<el-input v-model="formData.remark" placeholder="请输入备注" type="textarea" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<!-- 子表的表单 -->
|
|
||||||
<ContentWrap>
|
|
||||||
<el-tabs v-model="subTabsName" class="-mt-15px -mb-10px">
|
|
||||||
<el-tab-pane label="产品清单" name="product">
|
|
||||||
<ContractProductForm
|
|
||||||
ref="productFormRef"
|
|
||||||
:products="formData.products"
|
|
||||||
:disabled="disabled"
|
|
||||||
/>
|
|
||||||
</el-tab-pane>
|
|
||||||
</el-tabs>
|
|
||||||
</ContentWrap>
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="8">
|
|
||||||
<el-form-item label="产品总金额" prop="totalProductPrice">
|
|
||||||
<el-input
|
|
||||||
disabled
|
|
||||||
v-model="formData.totalProductPrice"
|
|
||||||
:formatter="erpPriceTableColumnFormatter"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="8">
|
|
||||||
<el-form-item label="整单折扣(%)" prop="discountPercent">
|
|
||||||
<el-input-number
|
|
||||||
v-model="formData.discountPercent"
|
|
||||||
placeholder="请输入整单折扣"
|
|
||||||
controls-position="right"
|
|
||||||
:min="0"
|
|
||||||
:precision="2"
|
|
||||||
class="!w-1/1"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="8">
|
|
||||||
<el-form-item label="折扣后金额" prop="totalPrice">
|
|
||||||
<el-input
|
|
||||||
disabled
|
|
||||||
v-model="formData.totalPrice"
|
|
||||||
placeholder="请输入商机金额"
|
|
||||||
:formatter="erpPriceTableColumnFormattere"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</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 * as CustomerApi from '@/api/crm/customer'
|
|
||||||
import * as ContractApi from '@/api/crm/contract'
|
|
||||||
import * as UserApi from '@/api/system/user'
|
|
||||||
import * as ContactApi from '@/api/crm/contact'
|
|
||||||
import * as BusinessApi from '@/api/crm/business'
|
|
||||||
import { erpPriceMultiply, erpPriceTableColumnFormatter } from '@/utils'
|
|
||||||
import { useUserStore } from '@/store/modules/user'
|
|
||||||
import ContractProductForm from '@/views/crm/contract/components/ContractProductForm.vue'
|
|
||||||
|
|
||||||
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,
|
|
||||||
no: undefined,
|
|
||||||
name: undefined,
|
|
||||||
customerId: undefined,
|
|
||||||
businessId: undefined,
|
|
||||||
orderDate: undefined,
|
|
||||||
startTime: undefined,
|
|
||||||
endTime: undefined,
|
|
||||||
signUserId: undefined,
|
|
||||||
signContactId: undefined,
|
|
||||||
ownerUserId: undefined,
|
|
||||||
discountPercent: 0,
|
|
||||||
totalProductPrice: undefined,
|
|
||||||
remark: undefined,
|
|
||||||
products: []
|
|
||||||
})
|
|
||||||
const formRules = reactive({
|
|
||||||
name: [{ required: true, message: '合同名称不能为空', trigger: 'blur' }],
|
|
||||||
customerId: [{ required: true, message: '客户不能为空', trigger: 'blur' }],
|
|
||||||
orderDate: [{ required: true, message: '下单日期不能为空', trigger: 'blur' }],
|
|
||||||
ownerUserId: [{ required: true, message: '负责人不能为空', trigger: 'blur' }]
|
|
||||||
})
|
|
||||||
const formRef = ref() // 表单 Ref
|
|
||||||
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
|
|
||||||
const customerList = ref([]) // 客户列表的数据
|
|
||||||
const businessList = ref<BusinessApi.BusinessVO[]>([])
|
|
||||||
const contactList = ref<ContactApi.ContactVO[]>([])
|
|
||||||
|
|
||||||
/** 子表的表单 */
|
|
||||||
const subTabsName = ref('product')
|
|
||||||
const productFormRef = ref()
|
|
||||||
|
|
||||||
/** 计算 discountPrice、totalPrice 价格 */
|
|
||||||
watch(
|
|
||||||
() => formData.value,
|
|
||||||
(val) => {
|
|
||||||
if (!val) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const totalProductPrice = val.products.reduce((prev, curr) => prev + curr.totalPrice, 0)
|
|
||||||
const discountPrice =
|
|
||||||
val.discountPercent != null
|
|
||||||
? erpPriceMultiply(totalProductPrice, val.discountPercent / 100.0)
|
|
||||||
: 0
|
|
||||||
const totalPrice = totalProductPrice - discountPrice
|
|
||||||
// 赋值
|
|
||||||
formData.value.totalProductPrice = totalProductPrice
|
|
||||||
formData.value.totalPrice = totalPrice
|
|
||||||
},
|
|
||||||
{ deep: true }
|
|
||||||
)
|
|
||||||
|
|
||||||
/** 打开弹窗 */
|
|
||||||
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 ContractApi.getContract(id)
|
|
||||||
} finally {
|
|
||||||
formLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 获得客户列表
|
|
||||||
customerList.value = await CustomerApi.getCustomerSimpleList()
|
|
||||||
// 获得用户列表
|
|
||||||
userOptions.value = await UserApi.getSimpleUserList()
|
|
||||||
// 默认新建时选中自己
|
|
||||||
if (formType.value === 'create') {
|
|
||||||
formData.value.ownerUserId = useUserStore().getUser.id
|
|
||||||
}
|
|
||||||
// 获取联系人
|
|
||||||
contactList.value = await ContactApi.getSimpleContactList()
|
|
||||||
// 获得商机列表
|
|
||||||
businessList.value = await BusinessApi.getSimpleBusinessList()
|
|
||||||
}
|
|
||||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
|
||||||
|
|
||||||
/** 提交表单 */
|
|
||||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
|
||||||
const submitForm = async () => {
|
|
||||||
// 校验表单
|
|
||||||
if (!formRef) return
|
|
||||||
const valid = await formRef.value.validate()
|
|
||||||
if (!valid) return
|
|
||||||
// 提交请求
|
|
||||||
formLoading.value = true
|
|
||||||
productFormRef.value.validate()
|
|
||||||
try {
|
|
||||||
const data = unref(formData.value) as unknown as ContractApi.ContractVO
|
|
||||||
if (formType.value === 'create') {
|
|
||||||
await ContractApi.createContract(data)
|
|
||||||
message.success(t('common.createSuccess'))
|
|
||||||
} else {
|
|
||||||
await ContractApi.updateContract(data)
|
|
||||||
message.success(t('common.updateSuccess'))
|
|
||||||
}
|
|
||||||
dialogVisible.value = false
|
|
||||||
// 发送操作成功的事件
|
|
||||||
emit('success')
|
|
||||||
} finally {
|
|
||||||
formLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 重置表单 */
|
|
||||||
const resetForm = () => {
|
|
||||||
formData.value = {
|
|
||||||
id: undefined,
|
|
||||||
no: undefined,
|
|
||||||
name: undefined,
|
|
||||||
customerId: undefined,
|
|
||||||
businessId: undefined,
|
|
||||||
orderDate: undefined,
|
|
||||||
startTime: undefined,
|
|
||||||
endTime: undefined,
|
|
||||||
signUserId: undefined,
|
|
||||||
signContactId: undefined,
|
|
||||||
ownerUserId: undefined,
|
|
||||||
discountPercent: 0,
|
|
||||||
totalProductPrice: undefined,
|
|
||||||
remark: undefined,
|
|
||||||
products: []
|
|
||||||
}
|
|
||||||
formRef.value?.resetFields()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 处理切换客户 */
|
|
||||||
const handleCustomerChange = () => {
|
|
||||||
formData.value.businessId = undefined
|
|
||||||
formData.value.signContactId = undefined
|
|
||||||
formData.value.products = []
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 处理商机变化 */
|
|
||||||
const handleBusinessChange = async (businessId: number) => {
|
|
||||||
const business = await BusinessApi.getBusiness(businessId)
|
|
||||||
business.products.forEach((item) => {
|
|
||||||
item.contractPrice = item.businessPrice
|
|
||||||
})
|
|
||||||
formData.value.products = business.products
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 动态获取客户联系人 */
|
|
||||||
const getContactOptions = computed(() =>
|
|
||||||
contactList.value.filter((item) => item.customerId == formData.value.customerId)
|
|
||||||
)
|
|
||||||
/** 动态获取商机 */
|
|
||||||
const getBusinessOptions = computed(() =>
|
|
||||||
businessList.value.filter((item) => item.customerId == formData.value.customerId)
|
|
||||||
)
|
|
||||||
</script>
|
|
|
@ -1,136 +0,0 @@
|
||||||
<template>
|
|
||||||
<!-- 操作栏 -->
|
|
||||||
<el-row justify="end">
|
|
||||||
<el-button @click="openForm">
|
|
||||||
<Icon class="mr-5px" icon="clarity:contract-line" />
|
|
||||||
创建合同
|
|
||||||
</el-button>
|
|
||||||
</el-row>
|
|
||||||
|
|
||||||
<!-- 列表 -->
|
|
||||||
<ContentWrap class="mt-10px">
|
|
||||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
|
||||||
<el-table-column label="合同名称" fixed="left" align="center" prop="name">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-link type="primary" :underline="false" @click="openDetail(scope.row.id)">
|
|
||||||
{{ scope.row.name }}
|
|
||||||
</el-link>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="合同编号" align="center" prop="no" />
|
|
||||||
<el-table-column label="客户名称" align="center" prop="customerName" />
|
|
||||||
<el-table-column
|
|
||||||
label="合同金额(元)"
|
|
||||||
align="center"
|
|
||||||
prop="totalPrice"
|
|
||||||
:formatter="erpPriceTableColumnFormatter"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
label="开始时间"
|
|
||||||
align="center"
|
|
||||||
prop="startTime"
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
label="结束时间"
|
|
||||||
align="center"
|
|
||||||
prop="endTime"
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="状态" prop="auditStatus">
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.CRM_AUDIT_STATUS" :value="scope.row.auditStatus" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
<!-- 分页 -->
|
|
||||||
<Pagination
|
|
||||||
:total="total"
|
|
||||||
v-model:page="queryParams.pageNo"
|
|
||||||
v-model:limit="queryParams.pageSize"
|
|
||||||
@pagination="getList"
|
|
||||||
/>
|
|
||||||
</ContentWrap>
|
|
||||||
|
|
||||||
<!-- 表单弹窗:添加 -->
|
|
||||||
<ContractForm ref="formRef" @success="getList" />
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
import * as ContractApi from '@/api/crm/contract'
|
|
||||||
import ContractForm from './../ContractForm.vue'
|
|
||||||
import { BizTypeEnum } from '@/api/crm/permission'
|
|
||||||
import { dateFormatter } from '@/utils/formatTime'
|
|
||||||
import { DICT_TYPE } from '@/utils/dict'
|
|
||||||
import { erpPriceTableColumnFormatter } from '@/utils'
|
|
||||||
|
|
||||||
defineOptions({ name: 'CrmContractList' })
|
|
||||||
const props = defineProps<{
|
|
||||||
bizType: number // 业务类型
|
|
||||||
bizId: number // 业务编号
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const loading = ref(true) // 列表的加载中
|
|
||||||
const total = ref(0) // 列表的总页数
|
|
||||||
const list = ref([]) // 列表的数据
|
|
||||||
const queryParams = reactive({
|
|
||||||
pageNo: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
customerId: undefined as unknown // 允许 undefined + number
|
|
||||||
})
|
|
||||||
|
|
||||||
/** 查询列表 */
|
|
||||||
const getList = async () => {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
// 置空参数
|
|
||||||
queryParams.customerId = undefined
|
|
||||||
// 执行查询
|
|
||||||
let data = { list: [], total: 0 }
|
|
||||||
switch (props.bizType) {
|
|
||||||
case BizTypeEnum.CRM_CUSTOMER:
|
|
||||||
queryParams.customerId = props.bizId
|
|
||||||
data = await ContractApi.getContractPageByCustomer(queryParams)
|
|
||||||
break
|
|
||||||
case BizTypeEnum.CRM_BUSINESS:
|
|
||||||
queryParams.businessId = props.bizId
|
|
||||||
data = await ContractApi.getContractPageByBusiness(queryParams)
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
list.value = data.list
|
|
||||||
total.value = data.total
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 搜索按钮操作 */
|
|
||||||
const handleQuery = () => {
|
|
||||||
queryParams.pageNo = 1
|
|
||||||
getList()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 添加 */
|
|
||||||
const formRef = ref()
|
|
||||||
const openForm = () => {
|
|
||||||
formRef.value.open('create')
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 打开合同详情 */
|
|
||||||
const { push } = useRouter()
|
|
||||||
const openDetail = (id: number) => {
|
|
||||||
push({ name: 'CrmContractDetail', params: { id } })
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 监听打开的 bizId + bizType,从而加载最新的列表 */
|
|
||||||
watch(
|
|
||||||
() => [props.bizId, props.bizType],
|
|
||||||
() => {
|
|
||||||
handleQuery()
|
|
||||||
},
|
|
||||||
{ immediate: true, deep: true }
|
|
||||||
)
|
|
||||||
</script>
|
|
|
@ -1,183 +0,0 @@
|
||||||
<template>
|
|
||||||
<el-form
|
|
||||||
ref="formRef"
|
|
||||||
:model="formData"
|
|
||||||
:rules="formRules"
|
|
||||||
v-loading="formLoading"
|
|
||||||
label-width="0px"
|
|
||||||
:inline-message="true"
|
|
||||||
:disabled="disabled"
|
|
||||||
>
|
|
||||||
<el-table :data="formData" class="-mt-10px">
|
|
||||||
<el-table-column label="序号" type="index" align="center" width="60" />
|
|
||||||
<el-table-column label="产品名称" min-width="180">
|
|
||||||
<template #default="{ row, $index }">
|
|
||||||
<el-form-item :prop="`${$index}.productId`" :rules="formRules.productId" class="mb-0px!">
|
|
||||||
<el-select
|
|
||||||
v-model="row.productId"
|
|
||||||
clearable
|
|
||||||
filterable
|
|
||||||
@change="onChangeProduct($event, row)"
|
|
||||||
placeholder="请选择产品"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="item in productList"
|
|
||||||
:key="item.id"
|
|
||||||
:label="item.name"
|
|
||||||
:value="item.id"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="条码" min-width="150">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<el-form-item class="mb-0px!">
|
|
||||||
<el-input disabled v-model="row.productNo" />
|
|
||||||
</el-form-item>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="单位" min-width="80">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<dict-tag :type="DICT_TYPE.CRM_PRODUCT_UNIT" :value="row.productUnit" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="价格(元)" min-width="120">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<el-form-item class="mb-0px!">
|
|
||||||
<el-input disabled v-model="row.productPrice" :formatter="erpPriceInputFormatter" />
|
|
||||||
</el-form-item>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="售价(元)" fixed="right" min-width="140">
|
|
||||||
<template #default="{ row, $index }">
|
|
||||||
<el-form-item :prop="`${$index}.contractPrice`" class="mb-0px!">
|
|
||||||
<el-input-number
|
|
||||||
v-model="row.contractPrice"
|
|
||||||
controls-position="right"
|
|
||||||
:min="0.001"
|
|
||||||
:precision="2"
|
|
||||||
class="!w-100%"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="数量" prop="count" fixed="right" min-width="120">
|
|
||||||
<template #default="{ row, $index }">
|
|
||||||
<el-form-item :prop="`${$index}.count`" :rules="formRules.count" class="mb-0px!">
|
|
||||||
<el-input-number
|
|
||||||
v-model="row.count"
|
|
||||||
controls-position="right"
|
|
||||||
:min="0.001"
|
|
||||||
:precision="3"
|
|
||||||
class="!w-100%"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="合计" prop="totalPrice" fixed="right" min-width="140">
|
|
||||||
<template #default="{ row, $index }">
|
|
||||||
<el-form-item :prop="`${$index}.totalPrice`" class="mb-0px!">
|
|
||||||
<el-input disabled v-model="row.totalPrice" :formatter="erpPriceInputFormatter" />
|
|
||||||
</el-form-item>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" fixed="right" label="操作" width="60">
|
|
||||||
<template #default="{ $index }">
|
|
||||||
<el-button @click="handleDelete($index)" link>—</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
</el-form>
|
|
||||||
<el-row justify="center" class="mt-3" v-if="!disabled">
|
|
||||||
<el-button @click="handleAdd" round>+ 添加产品</el-button>
|
|
||||||
</el-row>
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
import * as ProductApi from '@/api/crm/product'
|
|
||||||
import { erpPriceInputFormatter, erpPriceMultiply } from '@/utils'
|
|
||||||
import { DICT_TYPE } from '@/utils/dict'
|
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
products: undefined
|
|
||||||
disabled: false
|
|
||||||
}>()
|
|
||||||
const formLoading = ref(false) // 表单的加载中
|
|
||||||
const formData = ref([])
|
|
||||||
const formRules = reactive({
|
|
||||||
productId: [{ required: true, message: '产品不能为空', trigger: 'blur' }],
|
|
||||||
contractPrice: [{ required: true, message: '合同价格不能为空', trigger: 'blur' }],
|
|
||||||
count: [{ required: true, message: '产品数量不能为空', trigger: 'blur' }]
|
|
||||||
})
|
|
||||||
const formRef = ref([]) // 表单 Ref
|
|
||||||
const productList = ref<ProductApi.ProductVO[]>([]) // 产品列表
|
|
||||||
|
|
||||||
/** 初始化设置产品项 */
|
|
||||||
watch(
|
|
||||||
() => props.products,
|
|
||||||
async (val) => {
|
|
||||||
formData.value = val
|
|
||||||
},
|
|
||||||
{ immediate: true }
|
|
||||||
)
|
|
||||||
|
|
||||||
/** 监听合同产品变化,计算合同产品总价 */
|
|
||||||
watch(
|
|
||||||
() => formData.value,
|
|
||||||
(val) => {
|
|
||||||
if (!val || val.length === 0) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 循环处理
|
|
||||||
val.forEach((item) => {
|
|
||||||
if (item.contractPrice != null && item.count != null) {
|
|
||||||
item.totalPrice = erpPriceMultiply(item.contractPrice, item.count)
|
|
||||||
} else {
|
|
||||||
item.totalPrice = undefined
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
{ deep: true }
|
|
||||||
)
|
|
||||||
|
|
||||||
/** 新增按钮操作 */
|
|
||||||
const handleAdd = () => {
|
|
||||||
const row = {
|
|
||||||
id: undefined,
|
|
||||||
productId: undefined,
|
|
||||||
productUnit: undefined, // 产品单位
|
|
||||||
productNo: undefined, // 产品条码
|
|
||||||
productPrice: undefined, // 产品价格
|
|
||||||
contractPrice: undefined,
|
|
||||||
count: 1
|
|
||||||
}
|
|
||||||
formData.value.push(row)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 删除按钮操作 */
|
|
||||||
const handleDelete = (index: number) => {
|
|
||||||
formData.value.splice(index, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 处理产品变更 */
|
|
||||||
const onChangeProduct = (productId, row) => {
|
|
||||||
const product = productList.value.find((item) => item.id === productId)
|
|
||||||
if (product) {
|
|
||||||
row.productUnit = product.unit
|
|
||||||
row.productNo = product.no
|
|
||||||
row.productPrice = product.price
|
|
||||||
row.contractPrice = product.price
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 表单校验 */
|
|
||||||
const validate = () => {
|
|
||||||
return formRef.value.validate()
|
|
||||||
}
|
|
||||||
defineExpose({ validate })
|
|
||||||
|
|
||||||
/** 初始化 */
|
|
||||||
onMounted(async () => {
|
|
||||||
productList.value = await ProductApi.getProductSimpleList()
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,103 +0,0 @@
|
||||||
<template>
|
|
||||||
<!-- <doc-alert title="【合同】合同管理、合同提醒" url="https://doc.iocoder.cn/crm/contract/" />
|
|
||||||
<doc-alert title="【通用】数据权限" url="https://doc.iocoder.cn/crm/permission/" /> -->
|
|
||||||
|
|
||||||
<ContentWrap>
|
|
||||||
<el-form
|
|
||||||
ref="formRef"
|
|
||||||
:model="formData"
|
|
||||||
:rules="formRules"
|
|
||||||
label-width="160px"
|
|
||||||
v-loading="formLoading"
|
|
||||||
>
|
|
||||||
<el-card shadow="never">
|
|
||||||
<!-- 操作 -->
|
|
||||||
<template #header>
|
|
||||||
<div class="flex items-center justify-between">
|
|
||||||
<CardTitle title="合同配置设置" />
|
|
||||||
<el-button type="primary" @click="onSubmit" v-hasPermi="['crm:contract-config:update']">
|
|
||||||
保存
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<!-- 表单 -->
|
|
||||||
<el-form-item label="提前提醒设置" prop="notifyEnabled">
|
|
||||||
<el-radio-group
|
|
||||||
v-model="formData.notifyEnabled"
|
|
||||||
@change="changeNotifyEnable"
|
|
||||||
class="ml-4"
|
|
||||||
>
|
|
||||||
<el-radio :label="false" size="large">不提醒</el-radio>
|
|
||||||
<el-radio :label="true" size="large">提醒</el-radio>
|
|
||||||
</el-radio-group>
|
|
||||||
</el-form-item>
|
|
||||||
<div v-if="formData.notifyEnabled">
|
|
||||||
<el-form-item>
|
|
||||||
提前 <el-input-number class="mx-2" v-model="formData.notifyDays" /> 天提醒
|
|
||||||
</el-form-item>
|
|
||||||
</div>
|
|
||||||
</el-card>
|
|
||||||
</el-form>
|
|
||||||
</ContentWrap>
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
import * as ContractConfigApi from '@/api/crm/contract/config'
|
|
||||||
import { CardTitle } from '@/components/Card'
|
|
||||||
|
|
||||||
defineOptions({ name: 'CrmContractConfig' })
|
|
||||||
|
|
||||||
const message = useMessage() // 消息弹窗
|
|
||||||
const { t } = useI18n() // 国际化
|
|
||||||
|
|
||||||
const formLoading = ref(false)
|
|
||||||
const formData = ref({
|
|
||||||
notifyEnabled: false,
|
|
||||||
notifyDays: undefined
|
|
||||||
})
|
|
||||||
const formRules = reactive({})
|
|
||||||
const formRef = ref() // 表单 Ref
|
|
||||||
|
|
||||||
/** 获取配置 */
|
|
||||||
const getConfig = async () => {
|
|
||||||
try {
|
|
||||||
formLoading.value = true
|
|
||||||
const data = await ContractConfigApi.getContractConfig()
|
|
||||||
if (data === null) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
formData.value = data
|
|
||||||
} finally {
|
|
||||||
formLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 提交配置 */
|
|
||||||
const onSubmit = async () => {
|
|
||||||
// 校验表单
|
|
||||||
if (!formRef) return
|
|
||||||
const valid = await formRef.value.validate()
|
|
||||||
if (!valid) return
|
|
||||||
// 提交请求
|
|
||||||
formLoading.value = true
|
|
||||||
try {
|
|
||||||
const data = formData.value as ContractConfigApi.ContractConfigVO
|
|
||||||
await ContractConfigApi.saveContractConfig(data)
|
|
||||||
message.success(t('common.updateSuccess'))
|
|
||||||
await getConfig()
|
|
||||||
formLoading.value = false
|
|
||||||
} finally {
|
|
||||||
formLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 更改提前提醒设置 */
|
|
||||||
const changeNotifyEnable = () => {
|
|
||||||
if (!formData.value.notifyEnabled) {
|
|
||||||
formData.value.notifyDays = undefined
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
getConfig()
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,45 +0,0 @@
|
||||||
<!-- 合同详情头部组件-->
|
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<div class="flex items-start justify-between">
|
|
||||||
<div>
|
|
||||||
<el-col>
|
|
||||||
<el-row>
|
|
||||||
<span class="text-xl font-bold">{{ contract.name }}</span>
|
|
||||||
</el-row>
|
|
||||||
</el-col>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<!-- 右上:按钮 -->
|
|
||||||
<slot></slot>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<ContentWrap class="mt-10px">
|
|
||||||
<el-descriptions :column="5" direction="vertical">
|
|
||||||
<el-descriptions-item label="客户名称">
|
|
||||||
{{ contract.customerName }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="合同金额(元)">
|
|
||||||
{{ erpPriceInputFormatter(contract.totalPrice) }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="下单时间">
|
|
||||||
{{ formatDate(contract.orderDate) }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="回款金额(元)">
|
|
||||||
{{ erpPriceInputFormatter(contract.totalReceivablePrice) }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="负责人">
|
|
||||||
{{ contract.ownerUserName }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
</el-descriptions>
|
|
||||||
</ContentWrap>
|
|
||||||
</template>
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import * as ContractApi from '@/api/crm/contract'
|
|
||||||
import { formatDate } from '@/utils/formatTime'
|
|
||||||
import { erpPriceInputFormatter } from '@/utils'
|
|
||||||
|
|
||||||
defineOptions({ name: 'ContractDetailsHeader' })
|
|
||||||
defineProps<{ contract: ContractApi.ContractVO }>()
|
|
||||||
</script>
|
|
|
@ -1,76 +0,0 @@
|
||||||
<!-- 合同详情组件 -->
|
|
||||||
<template>
|
|
||||||
<ContentWrap>
|
|
||||||
<el-collapse v-model="activeNames">
|
|
||||||
<el-collapse-item name="contractInfo">
|
|
||||||
<template #title>
|
|
||||||
<span class="text-base font-bold">基本信息</span>
|
|
||||||
</template>
|
|
||||||
<el-descriptions :column="4">
|
|
||||||
<el-descriptions-item label="合同编号">{{ contract.no }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="合同名称">{{ contract.name }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="客户名称">{{ contract.customerName }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="商机名称">{{ contract.businessName }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="合同金额(元)">
|
|
||||||
{{ erpPriceInputFormatter(contract.totalPrice) }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="下单时间">
|
|
||||||
{{ formatDate(contract.orderDate) }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="合同开始时间">
|
|
||||||
{{ formatDate(contract.startTime) }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="合同结束时间">
|
|
||||||
{{ formatDate(contract.endTime) }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="客户签约人">
|
|
||||||
{{ contract.signContactName }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="公司签约人">
|
|
||||||
{{ contract.signUserName }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="备注">
|
|
||||||
{{ contract.remark }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="合同状态">
|
|
||||||
<dict-tag :type="DICT_TYPE.CRM_AUDIT_STATUS" :value="contract.auditStatus" />
|
|
||||||
</el-descriptions-item>
|
|
||||||
</el-descriptions>
|
|
||||||
</el-collapse-item>
|
|
||||||
<el-collapse-item name="systemInfo">
|
|
||||||
<template #title>
|
|
||||||
<span class="text-base font-bold">系统信息</span>
|
|
||||||
</template>
|
|
||||||
<el-descriptions :column="4">
|
|
||||||
<el-descriptions-item label="负责人">{{ contract.ownerUserName }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="最后跟进时间">
|
|
||||||
{{ formatDate(contract.contactLastTime) }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label=""> </el-descriptions-item>
|
|
||||||
<el-descriptions-item label=""> </el-descriptions-item>
|
|
||||||
<el-descriptions-item label="创建人">{{ contract.creatorName }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="创建时间">
|
|
||||||
{{ formatDate(contract.createTime) }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="更新时间">
|
|
||||||
{{ formatDate(contract.updateTime) }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
</el-descriptions>
|
|
||||||
</el-collapse-item>
|
|
||||||
</el-collapse>
|
|
||||||
</ContentWrap>
|
|
||||||
</template>
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import * as ContractApi from '@/api/crm/contract'
|
|
||||||
import { formatDate } from '@/utils/formatTime'
|
|
||||||
import { DICT_TYPE } from '@/utils/dict'
|
|
||||||
import { erpPriceInputFormatter } from '@/utils'
|
|
||||||
|
|
||||||
defineOptions({ name: 'ContractDetailsInfo' })
|
|
||||||
defineProps<{
|
|
||||||
contract: ContractApi.ContractVO
|
|
||||||
}>()
|
|
||||||
|
|
||||||
// 展示的折叠面板
|
|
||||||
const activeNames = ref(['contractInfo', 'systemInfo'])
|
|
||||||
</script>
|
|
|
@ -1,66 +0,0 @@
|
||||||
<template>
|
|
||||||
<ContentWrap>
|
|
||||||
<el-table :data="contract.products" :stripe="true" :show-overflow-tooltip="true">
|
|
||||||
<el-table-column
|
|
||||||
align="center"
|
|
||||||
label="产品名称"
|
|
||||||
fixed="left"
|
|
||||||
prop="productName"
|
|
||||||
min-width="160"
|
|
||||||
>
|
|
||||||
<template #default="scope">
|
|
||||||
{{ scope.row.productName }}
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="产品条码" align="center" prop="productNo" min-width="120" />
|
|
||||||
<el-table-column align="center" label="产品单位" prop="productUnit" min-width="160">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<dict-tag :type="DICT_TYPE.CRM_PRODUCT_UNIT" :value="row.productUnit" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
label="产品价格(元)"
|
|
||||||
align="center"
|
|
||||||
prop="productPrice"
|
|
||||||
min-width="140"
|
|
||||||
:formatter="erpPriceTableColumnFormatter"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
label="合同价格(元)"
|
|
||||||
align="center"
|
|
||||||
prop="contractPrice"
|
|
||||||
min-width="140"
|
|
||||||
:formatter="erpPriceTableColumnFormatter"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
align="center"
|
|
||||||
label="数量"
|
|
||||||
prop="count"
|
|
||||||
min-width="100px"
|
|
||||||
:formatter="erpPriceTableColumnFormatter"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
label="合计金额(元)"
|
|
||||||
align="center"
|
|
||||||
prop="totalPrice"
|
|
||||||
min-width="140"
|
|
||||||
:formatter="erpPriceTableColumnFormatter"
|
|
||||||
/>
|
|
||||||
</el-table>
|
|
||||||
<el-row class="mt-10px" justify="end">
|
|
||||||
<el-col :span="3"> 整单折扣:{{ erpPriceInputFormatter(contract.discountPercent) }}% </el-col>
|
|
||||||
<el-col :span="4">
|
|
||||||
产品总金额:{{ erpPriceInputFormatter(contract.totalProductPrice) }} 元
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</ContentWrap>
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
import * as ContractApi from '@/api/crm/contract'
|
|
||||||
import { erpPriceInputFormatter, erpPriceTableColumnFormatter } from '@/utils'
|
|
||||||
import { DICT_TYPE } from '@/utils/dict'
|
|
||||||
|
|
||||||
const { contract } = defineProps<{
|
|
||||||
contract: ContractApi.ContractVO
|
|
||||||
}>()
|
|
||||||
</script>
|
|
|
@ -1,139 +0,0 @@
|
||||||
<!-- 合同详情页面组件-->
|
|
||||||
<template>
|
|
||||||
<ContractDetailsHeader v-loading="loading" :contract="contract">
|
|
||||||
<el-button v-if="permissionListRef?.validateWrite" @click="openForm('update', contract.id)">
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button v-if="permissionListRef?.validateOwnerUser" type="primary" @click="transferContract">
|
|
||||||
转移
|
|
||||||
</el-button>
|
|
||||||
</ContractDetailsHeader>
|
|
||||||
<el-col>
|
|
||||||
<el-tabs>
|
|
||||||
<el-tab-pane label="跟进记录">
|
|
||||||
<FollowUpList :biz-id="contract.id" :biz-type="BizTypeEnum.CRM_CONTRACT" />
|
|
||||||
</el-tab-pane>
|
|
||||||
<el-tab-pane label="基本信息">
|
|
||||||
<ContractDetailsInfo :contract="contract" />
|
|
||||||
</el-tab-pane>
|
|
||||||
<el-tab-pane label="产品">
|
|
||||||
<ContractProductList :contract="contract" />
|
|
||||||
</el-tab-pane>
|
|
||||||
<el-tab-pane label="回款">
|
|
||||||
<ReceivablePlanList
|
|
||||||
:contract-id="contract.id!"
|
|
||||||
:customer-id="contract.customerId"
|
|
||||||
@create-receivable="createReceivable"
|
|
||||||
/>
|
|
||||||
<ReceivableList
|
|
||||||
ref="receivableListRef"
|
|
||||||
:contract-id="contract.id!"
|
|
||||||
:customer-id="contract.customerId"
|
|
||||||
/>
|
|
||||||
</el-tab-pane>
|
|
||||||
<el-tab-pane label="团队成员">
|
|
||||||
<PermissionList
|
|
||||||
ref="permissionListRef"
|
|
||||||
:biz-id="contract.id!"
|
|
||||||
:biz-type="BizTypeEnum.CRM_CONTRACT"
|
|
||||||
:show-action="false"
|
|
||||||
@quit-team="close"
|
|
||||||
/>
|
|
||||||
</el-tab-pane>
|
|
||||||
<el-tab-pane label="操作日志">
|
|
||||||
<OperateLogV2 :log-list="logList" />
|
|
||||||
</el-tab-pane>
|
|
||||||
</el-tabs>
|
|
||||||
</el-col>
|
|
||||||
|
|
||||||
<!-- 表单弹窗:添加/修改 -->
|
|
||||||
<ContractForm ref="formRef" @success="getContractData" />
|
|
||||||
<CrmTransferForm ref="transferFormRef" :biz-type="BizTypeEnum.CRM_CONTRACT" @success="close" />
|
|
||||||
</template>
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { useTagsViewStore } from '@/store/modules/tagsView'
|
|
||||||
import { OperateLogVO } from '@/api/system/operatelog'
|
|
||||||
import * as ContractApi from '@/api/crm/contract'
|
|
||||||
import ContractDetailsInfo from './ContractDetailsInfo.vue'
|
|
||||||
import ContractDetailsHeader from './ContractDetailsHeader.vue'
|
|
||||||
import ContractProductList from './ContractProductList.vue'
|
|
||||||
import { BizTypeEnum } from '@/api/crm/permission'
|
|
||||||
import { getOperateLogPage } from '@/api/crm/operateLog'
|
|
||||||
import ContractForm from '@/views/crm/contract/ContractForm.vue'
|
|
||||||
import CrmTransferForm from '@/views/crm/permission/components/TransferForm.vue'
|
|
||||||
import PermissionList from '@/views/crm/permission/components/PermissionList.vue'
|
|
||||||
import FollowUpList from '@/views/crm/followup/index.vue'
|
|
||||||
import ReceivableList from '@/views/crm/receivable/components/ReceivableList.vue'
|
|
||||||
import ReceivablePlanList from '@/views/crm/receivable/plan/components/ReceivablePlanList.vue'
|
|
||||||
|
|
||||||
defineOptions({ name: 'CrmContractDetail' })
|
|
||||||
const props = defineProps<{ id?: number }>()
|
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
const message = useMessage()
|
|
||||||
const contractId = ref(0) // 编号
|
|
||||||
const loading = ref(true) // 加载中
|
|
||||||
const contract = ref<ContractApi.ContractVO>({} as ContractApi.ContractVO) // 详情
|
|
||||||
const permissionListRef = ref<InstanceType<typeof PermissionList>>() // 团队成员列表 Ref
|
|
||||||
|
|
||||||
/** 编辑 */
|
|
||||||
const formRef = ref()
|
|
||||||
const openForm = (type: string, id?: number) => {
|
|
||||||
formRef.value.open(type, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 获取详情 */
|
|
||||||
const getContractData = async () => {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
contract.value = await ContractApi.getContract(contractId.value)
|
|
||||||
await getOperateLog(contractId.value)
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 获取操作日志 */
|
|
||||||
const logList = ref<OperateLogVO[]>([]) // 操作日志列表
|
|
||||||
const getOperateLog = async (contractId: number) => {
|
|
||||||
if (!contractId) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const data = await getOperateLogPage({
|
|
||||||
bizType: BizTypeEnum.CRM_CONTRACT,
|
|
||||||
bizId: contractId
|
|
||||||
})
|
|
||||||
logList.value = data.list
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 从回款计划创建回款 */
|
|
||||||
const receivableListRef = ref<InstanceType<typeof ReceivableList>>() // 回款列表 Ref
|
|
||||||
const createReceivable = (planData: any) => {
|
|
||||||
receivableListRef.value?.createReceivable(planData)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 转移 */
|
|
||||||
const transferFormRef = ref<InstanceType<typeof CrmTransferForm>>() // 合同转移表单 ref
|
|
||||||
const transferContract = () => {
|
|
||||||
transferFormRef.value?.open(contract.value.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 关闭 */
|
|
||||||
const { delView } = useTagsViewStore() // 视图操作
|
|
||||||
const { currentRoute } = useRouter() // 路由
|
|
||||||
const close = () => {
|
|
||||||
delView(unref(currentRoute))
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 初始化 */
|
|
||||||
onMounted(async () => {
|
|
||||||
const id = props.id || route.params.id
|
|
||||||
if (!id) {
|
|
||||||
message.warning('参数错误,合同不能为空!')
|
|
||||||
close()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
contractId.value = id as unknown as number
|
|
||||||
await getContractData()
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,398 +0,0 @@
|
||||||
<template>
|
|
||||||
<!-- <doc-alert title="【合同】合同管理、合同提醒" url="https://doc.iocoder.cn/crm/contract/" />
|
|
||||||
<doc-alert title="【通用】数据权限" url="https://doc.iocoder.cn/crm/permission/" /> -->
|
|
||||||
|
|
||||||
<ContentWrap>
|
|
||||||
<!-- 搜索工作栏 -->
|
|
||||||
<el-form
|
|
||||||
ref="queryFormRef"
|
|
||||||
:inline="true"
|
|
||||||
:model="queryParams"
|
|
||||||
class="-mb-15px"
|
|
||||||
label-width="68px"
|
|
||||||
>
|
|
||||||
<el-form-item label="合同编号" prop="no">
|
|
||||||
<el-input
|
|
||||||
v-model="queryParams.no"
|
|
||||||
class="!w-240px"
|
|
||||||
clearable
|
|
||||||
placeholder="请输入合同编号"
|
|
||||||
@keyup.enter="handleQuery"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="合同名称" prop="name">
|
|
||||||
<el-input
|
|
||||||
v-model="queryParams.name"
|
|
||||||
class="!w-240px"
|
|
||||||
clearable
|
|
||||||
placeholder="请输入合同名称"
|
|
||||||
@keyup.enter="handleQuery"
|
|
||||||
/>
|
|
||||||
<el-form-item label="客户" prop="customerId">
|
|
||||||
<el-select
|
|
||||||
v-model="queryParams.customerId"
|
|
||||||
class="!w-240px"
|
|
||||||
clearable
|
|
||||||
lable-key="name"
|
|
||||||
placeholder="请选择客户"
|
|
||||||
value-key="id"
|
|
||||||
@keyup.enter="handleQuery"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="item in customerList"
|
|
||||||
:key="item.id"
|
|
||||||
:label="item.name"
|
|
||||||
:value="item.id!"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</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-button v-hasPermi="['crm:contract:create']" type="primary" @click="openForm('create')">
|
|
||||||
<Icon class="mr-5px" icon="ep:plus" />
|
|
||||||
新增
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
v-hasPermi="['crm:contract:export']"
|
|
||||||
:loading="exportLoading"
|
|
||||||
plain
|
|
||||||
type="success"
|
|
||||||
@click="handleExport"
|
|
||||||
>
|
|
||||||
<Icon class="mr-5px" icon="ep:download" />
|
|
||||||
导出
|
|
||||||
</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</ContentWrap>
|
|
||||||
|
|
||||||
<!-- 列表 -->
|
|
||||||
<ContentWrap>
|
|
||||||
<el-tabs v-model="activeName" @tab-click="handleTabClick">
|
|
||||||
<el-tab-pane label="我负责的" name="1" />
|
|
||||||
<el-tab-pane label="我参与的" name="2" />
|
|
||||||
<el-tab-pane label="下属负责的" name="3" />
|
|
||||||
</el-tabs>
|
|
||||||
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true">
|
|
||||||
<el-table-column align="center" fixed="left" label="合同编号" prop="no" width="180" />
|
|
||||||
<el-table-column align="center" fixed="left" label="合同名称" prop="name" width="160">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-link :underline="false" type="primary" @click="openDetail(scope.row.id)">
|
|
||||||
{{ scope.row.name }}
|
|
||||||
</el-link>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="客户名称" prop="customerName" width="120">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-link
|
|
||||||
:underline="false"
|
|
||||||
type="primary"
|
|
||||||
@click="openCustomerDetail(scope.row.customerId)"
|
|
||||||
>
|
|
||||||
{{ scope.row.customerName }}
|
|
||||||
</el-link>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="商机名称" prop="businessName" width="130">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-link
|
|
||||||
:underline="false"
|
|
||||||
type="primary"
|
|
||||||
@click="openBusinessDetail(scope.row.businessId)"
|
|
||||||
>
|
|
||||||
{{ scope.row.businessName }}
|
|
||||||
</el-link>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
align="center"
|
|
||||||
label="合同金额(元)"
|
|
||||||
prop="totalPrice"
|
|
||||||
width="140"
|
|
||||||
:formatter="erpPriceTableColumnFormatter"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
align="center"
|
|
||||||
label="下单时间"
|
|
||||||
prop="orderDate"
|
|
||||||
width="120"
|
|
||||||
:formatter="dateFormatter2"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
align="center"
|
|
||||||
label="合同开始时间"
|
|
||||||
prop="startTime"
|
|
||||||
width="120"
|
|
||||||
:formatter="dateFormatter2"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
align="center"
|
|
||||||
label="合同结束时间"
|
|
||||||
prop="endTime"
|
|
||||||
width="120"
|
|
||||||
:formatter="dateFormatter2"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="客户签约人" prop="contactName" width="130">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-link
|
|
||||||
:underline="false"
|
|
||||||
type="primary"
|
|
||||||
@click="openContactDetail(scope.row.signContactId)"
|
|
||||||
>
|
|
||||||
{{ scope.row.signContactName }}
|
|
||||||
</el-link>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="公司签约人" prop="signUserName" width="130" />
|
|
||||||
<el-table-column align="center" label="备注" prop="remark" width="200" />
|
|
||||||
<el-table-column
|
|
||||||
align="center"
|
|
||||||
label="已回款金额(元)"
|
|
||||||
prop="totalReceivablePrice"
|
|
||||||
width="140"
|
|
||||||
:formatter="erpPriceTableColumnFormatter"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
align="center"
|
|
||||||
label="未回款金额(元)"
|
|
||||||
prop="totalReceivablePrice"
|
|
||||||
width="140"
|
|
||||||
:formatter="erpPriceTableColumnFormatter"
|
|
||||||
>
|
|
||||||
<template #default="scope">
|
|
||||||
{{ erpPriceInputFormatter(scope.row.totalPrice - scope.row.totalReceivablePrice) }}
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="最后跟进时间"
|
|
||||||
prop="contactLastTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="负责人" prop="ownerUserName" width="120" />
|
|
||||||
<el-table-column align="center" label="所属部门" prop="ownerUserDeptName" width="100px" />
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="更新时间"
|
|
||||||
prop="updateTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="创建时间"
|
|
||||||
prop="createTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="创建人" prop="creatorName" width="120" />
|
|
||||||
<el-table-column align="center" fixed="right" label="合同状态" prop="auditStatus" width="120">
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.CRM_AUDIT_STATUS" :value="scope.row.auditStatus" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column fixed="right" label="操作" width="250">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
v-if="scope.row.auditStatus === 0"
|
|
||||||
v-hasPermi="['crm:contract:update']"
|
|
||||||
link
|
|
||||||
type="primary"
|
|
||||||
@click="openForm('update', scope.row.id)"
|
|
||||||
>
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
v-if="scope.row.auditStatus === 0"
|
|
||||||
v-hasPermi="['crm:contract:update']"
|
|
||||||
link
|
|
||||||
type="primary"
|
|
||||||
@click="handleSubmit(scope.row)"
|
|
||||||
>
|
|
||||||
提交审核
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
v-else
|
|
||||||
link
|
|
||||||
v-hasPermi="['crm:contract:update']"
|
|
||||||
type="primary"
|
|
||||||
@click="handleProcessDetail(scope.row)"
|
|
||||||
>
|
|
||||||
查看审批
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
v-hasPermi="['crm:contract:query']"
|
|
||||||
link
|
|
||||||
type="primary"
|
|
||||||
@click="openDetail(scope.row.id)"
|
|
||||||
>
|
|
||||||
详情
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
v-hasPermi="['crm:contract:delete']"
|
|
||||||
link
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(scope.row.id)"
|
|
||||||
>
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
<!-- 分页 -->
|
|
||||||
<Pagination
|
|
||||||
v-model:limit="queryParams.pageSize"
|
|
||||||
v-model:page="queryParams.pageNo"
|
|
||||||
:total="total"
|
|
||||||
@pagination="getList"
|
|
||||||
/>
|
|
||||||
</ContentWrap>
|
|
||||||
|
|
||||||
<!-- 表单弹窗:添加/修改 -->
|
|
||||||
<ContractForm ref="formRef" @success="getList" />
|
|
||||||
</template>
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { dateFormatter, dateFormatter2 } from '@/utils/formatTime'
|
|
||||||
import download from '@/utils/download'
|
|
||||||
import * as ContractApi from '@/api/crm/contract'
|
|
||||||
import ContractForm from './ContractForm.vue'
|
|
||||||
import { DICT_TYPE } from '@/utils/dict'
|
|
||||||
import { erpPriceInputFormatter, erpPriceTableColumnFormatter } from '@/utils'
|
|
||||||
import * as CustomerApi from '@/api/crm/customer'
|
|
||||||
import { TabsPaneContext } from 'element-plus'
|
|
||||||
|
|
||||||
defineOptions({ name: 'CrmContract' })
|
|
||||||
|
|
||||||
const message = useMessage() // 消息弹窗
|
|
||||||
const { t } = useI18n() // 国际化
|
|
||||||
|
|
||||||
const loading = ref(true) // 列表的加载中
|
|
||||||
const total = ref(0) // 列表的总页数
|
|
||||||
const list = ref([]) // 列表的数据
|
|
||||||
const queryParams: any = reactive({
|
|
||||||
pageNo: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
sceneType: '1', // 默认和 activeName 相等
|
|
||||||
name: null,
|
|
||||||
customerId: null,
|
|
||||||
orderDate: [],
|
|
||||||
no: null
|
|
||||||
})
|
|
||||||
const queryFormRef = ref() // 搜索的表单
|
|
||||||
const exportLoading = ref(false) // 导出的加载中
|
|
||||||
const activeName = ref('1') // 列表 tab
|
|
||||||
const customerList = ref<CustomerApi.CustomerVO[]>([]) // 客户列表
|
|
||||||
|
|
||||||
/** tab 切换 */
|
|
||||||
const handleTabClick = (tab: TabsPaneContext) => {
|
|
||||||
queryParams.sceneType = tab.paneName!.toString()
|
|
||||||
handleQuery()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 查询列表 */
|
|
||||||
const getList = async () => {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
const data = await ContractApi.getContractPage(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 ContractApi.deleteContract(id)
|
|
||||||
message.success(t('common.delSuccess'))
|
|
||||||
// 刷新列表
|
|
||||||
await getList()
|
|
||||||
} catch {}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 导出按钮操作 */
|
|
||||||
const handleExport = async () => {
|
|
||||||
try {
|
|
||||||
// 导出的二次确认
|
|
||||||
await message.exportConfirm()
|
|
||||||
// 发起导出
|
|
||||||
exportLoading.value = true
|
|
||||||
const data = await ContractApi.exportContract(queryParams)
|
|
||||||
download.excel(data, '合同.xls')
|
|
||||||
} catch {
|
|
||||||
} finally {
|
|
||||||
exportLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 提交审核 **/
|
|
||||||
const handleSubmit = async (row: ContractApi.ContractVO) => {
|
|
||||||
await message.confirm(`您确定提交【${row.name}】审核吗?`)
|
|
||||||
await ContractApi.submitContract(row.id)
|
|
||||||
message.success('提交审核成功!')
|
|
||||||
await getList()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 查看审批 */
|
|
||||||
const handleProcessDetail = (row: ContractApi.ContractVO) => {
|
|
||||||
push({ name: 'BpmProcessInstanceDetail', query: { id: row.processInstanceId } })
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 打开合同详情 */
|
|
||||||
const { push } = useRouter()
|
|
||||||
const openDetail = (id: number) => {
|
|
||||||
push({ name: 'CrmContractDetail', params: { id } })
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 打开客户详情 */
|
|
||||||
const openCustomerDetail = (id: number) => {
|
|
||||||
push({ name: 'CrmCustomerDetail', params: { id } })
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 打开联系人详情 */
|
|
||||||
const openContactDetail = (id: number) => {
|
|
||||||
push({ name: 'CrmContactDetail', params: { id } })
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 打开商机详情 */
|
|
||||||
const openBusinessDetail = (id: number) => {
|
|
||||||
push({ name: 'CrmBusinessDetail', params: { id } })
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 初始化 **/
|
|
||||||
onMounted(async () => {
|
|
||||||
await getList()
|
|
||||||
customerList.value = await CustomerApi.getCustomerSimpleList()
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,259 +0,0 @@
|
||||||
<template>
|
|
||||||
<Dialog v-model="dialogVisible" :title="dialogTitle">
|
|
||||||
<el-form
|
|
||||||
ref="formRef"
|
|
||||||
v-loading="formLoading"
|
|
||||||
:model="formData"
|
|
||||||
:rules="formRules"
|
|
||||||
label-width="100px"
|
|
||||||
>
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="客户名称" prop="name">
|
|
||||||
<el-input v-model="formData.name" placeholder="请输入客户名称" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="客户来源" prop="source">
|
|
||||||
<el-select v-model="formData.source" placeholder="请选择客户来源" class="w-1/1">
|
|
||||||
<el-option
|
|
||||||
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_SOURCE)"
|
|
||||||
:key="dict.value"
|
|
||||||
:label="dict.label"
|
|
||||||
:value="dict.value"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="手机" prop="mobile">
|
|
||||||
<el-input v-model="formData.mobile" placeholder="请输入手机" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="负责人" prop="ownerUserId">
|
|
||||||
<el-select
|
|
||||||
v-model="formData.ownerUserId"
|
|
||||||
:disabled="formType !== 'create'"
|
|
||||||
class="w-1/1"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="item in userOptions"
|
|
||||||
:key="item.id"
|
|
||||||
:label="item.nickname"
|
|
||||||
:value="item.id"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="电话" prop="telephone">
|
|
||||||
<el-input v-model="formData.telephone" placeholder="请输入电话" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="邮箱" prop="email">
|
|
||||||
<el-input v-model="formData.email" placeholder="请输入邮箱" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="微信" prop="wechat">
|
|
||||||
<el-input v-model="formData.wechat" placeholder="请输入微信" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="QQ" prop="qq">
|
|
||||||
<el-input v-model="formData.qq" placeholder="请输入 QQ" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="客户行业" prop="industryId">
|
|
||||||
<el-select v-model="formData.industryId" placeholder="请选择客户行业" class="w-1/1">
|
|
||||||
<el-option
|
|
||||||
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_INDUSTRY)"
|
|
||||||
:key="dict.value"
|
|
||||||
:label="dict.label"
|
|
||||||
:value="dict.value"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="客户级别" prop="level">
|
|
||||||
<el-select v-model="formData.level" placeholder="请选择客户级别" class="w-1/1">
|
|
||||||
<el-option
|
|
||||||
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_LEVEL)"
|
|
||||||
:key="dict.value"
|
|
||||||
:label="dict.label"
|
|
||||||
:value="dict.value"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="地址" prop="areaId">
|
|
||||||
<el-cascader
|
|
||||||
v-model="formData.areaId"
|
|
||||||
:options="areaList"
|
|
||||||
:props="defaultProps"
|
|
||||||
class="w-1/1"
|
|
||||||
clearable
|
|
||||||
filterable
|
|
||||||
placeholder="请选择城市"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="详细地址" prop="detailAddress">
|
|
||||||
<el-input v-model="formData.detailAddress" placeholder="请输入详细地址" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="下次联系时间" prop="contactNextTime">
|
|
||||||
<el-date-picker
|
|
||||||
v-model="formData.contactNextTime"
|
|
||||||
placeholder="选择下次联系时间"
|
|
||||||
type="datetime"
|
|
||||||
value-format="x"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="备注" prop="remark">
|
|
||||||
<el-input type="textarea" v-model="formData.remark" placeholder="请输入备注" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</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 * as CustomerApi from '@/api/crm/customer'
|
|
||||||
import * as AreaApi from '@/api/system/area'
|
|
||||||
import { defaultProps } from '@/utils/tree'
|
|
||||||
import * as UserApi from '@/api/system/user'
|
|
||||||
import { useUserStore } from '@/store/modules/user'
|
|
||||||
|
|
||||||
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 areaList = ref([]) // 地区列表
|
|
||||||
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
|
|
||||||
const formData = ref({
|
|
||||||
id: undefined,
|
|
||||||
name: undefined,
|
|
||||||
contactNextTime: undefined,
|
|
||||||
ownerUserId: 0,
|
|
||||||
mobile: undefined,
|
|
||||||
telephone: undefined,
|
|
||||||
qq: undefined,
|
|
||||||
wechat: undefined,
|
|
||||||
email: undefined,
|
|
||||||
areaId: undefined,
|
|
||||||
detailAddress: undefined,
|
|
||||||
industryId: undefined,
|
|
||||||
level: undefined,
|
|
||||||
source: undefined,
|
|
||||||
remark: undefined
|
|
||||||
})
|
|
||||||
const formRules = reactive({
|
|
||||||
name: [{ required: true, message: '客户名称不能为空', trigger: 'blur' }],
|
|
||||||
ownerUserId: [{ 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 CustomerApi.getCustomer(id)
|
|
||||||
} finally {
|
|
||||||
formLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 获得地区列表
|
|
||||||
areaList.value = await AreaApi.getAreaTree()
|
|
||||||
// 获得用户列表
|
|
||||||
userOptions.value = await UserApi.getSimpleUserList()
|
|
||||||
// 默认新建时选中自己
|
|
||||||
if (formType.value === 'create') {
|
|
||||||
formData.value.ownerUserId = useUserStore().getUser.id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
|
||||||
|
|
||||||
/** 提交表单 */
|
|
||||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
|
||||||
const submitForm = async () => {
|
|
||||||
// 校验表单
|
|
||||||
if (!formRef) return
|
|
||||||
const valid = await formRef.value.validate()
|
|
||||||
if (!valid) return
|
|
||||||
// 提交请求
|
|
||||||
formLoading.value = true
|
|
||||||
try {
|
|
||||||
const data = formData.value as unknown as CustomerApi.CustomerVO
|
|
||||||
if (formType.value === 'create') {
|
|
||||||
await CustomerApi.createCustomer(data)
|
|
||||||
message.success(t('common.createSuccess'))
|
|
||||||
} else {
|
|
||||||
await CustomerApi.updateCustomer(data)
|
|
||||||
message.success(t('common.updateSuccess'))
|
|
||||||
}
|
|
||||||
dialogVisible.value = false
|
|
||||||
// 发送操作成功的事件
|
|
||||||
emit('success')
|
|
||||||
} finally {
|
|
||||||
formLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 重置表单 */
|
|
||||||
const resetForm = () => {
|
|
||||||
formData.value = {
|
|
||||||
id: undefined,
|
|
||||||
name: undefined,
|
|
||||||
contactNextTime: undefined,
|
|
||||||
ownerUserId: 0,
|
|
||||||
mobile: undefined,
|
|
||||||
telephone: undefined,
|
|
||||||
qq: undefined,
|
|
||||||
wechat: undefined,
|
|
||||||
email: undefined,
|
|
||||||
areaId: undefined,
|
|
||||||
detailAddress: undefined,
|
|
||||||
industryId: undefined,
|
|
||||||
level: undefined,
|
|
||||||
source: undefined,
|
|
||||||
remark: undefined
|
|
||||||
}
|
|
||||||
formRef.value?.resetFields()
|
|
||||||
}
|
|
||||||
</script>
|
|
|
@ -1,158 +0,0 @@
|
||||||
<!-- 客户导入窗口 -->
|
|
||||||
<template>
|
|
||||||
<Dialog v-model="dialogVisible" title="客户导入" width="400">
|
|
||||||
<div class="flex items-center my-10px">
|
|
||||||
<span class="mr-10px">负责人</span>
|
|
||||||
<el-select v-model="ownerUserId" class="!w-240px" clearable>
|
|
||||||
<el-option
|
|
||||||
v-for="item in userOptions"
|
|
||||||
:key="item.id"
|
|
||||||
:label="item.nickname"
|
|
||||||
:value="item.id"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</div>
|
|
||||||
<el-upload
|
|
||||||
ref="uploadRef"
|
|
||||||
v-model:file-list="fileList"
|
|
||||||
:auto-upload="false"
|
|
||||||
:disabled="formLoading"
|
|
||||||
:limit="1"
|
|
||||||
:on-exceed="handleExceed"
|
|
||||||
accept=".xlsx, .xls"
|
|
||||||
action="none"
|
|
||||||
drag
|
|
||||||
>
|
|
||||||
<Icon icon="ep:upload" />
|
|
||||||
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
|
||||||
<template #tip>
|
|
||||||
<div class="el-upload__tip text-center">
|
|
||||||
<div class="el-upload__tip">
|
|
||||||
<el-checkbox v-model="updateSupport" />
|
|
||||||
是否更新已经存在的客户数据(“客户名称”重复)
|
|
||||||
</div>
|
|
||||||
<span>仅允许导入 xls、xlsx 格式文件。</span>
|
|
||||||
<el-link
|
|
||||||
:underline="false"
|
|
||||||
style="font-size: 12px; vertical-align: baseline"
|
|
||||||
type="primary"
|
|
||||||
@click="importTemplate"
|
|
||||||
>
|
|
||||||
下载模板
|
|
||||||
</el-link>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</el-upload>
|
|
||||||
<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 * as CustomerApi from '@/api/crm/customer'
|
|
||||||
import download from '@/utils/download'
|
|
||||||
import type { UploadUserFile } from 'element-plus'
|
|
||||||
import * as UserApi from '@/api/system/user'
|
|
||||||
import { useUserStore } from '@/store/modules/user'
|
|
||||||
|
|
||||||
defineOptions({ name: 'SystemUserImportForm' })
|
|
||||||
|
|
||||||
const message = useMessage() // 消息弹窗
|
|
||||||
|
|
||||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
|
||||||
const formLoading = ref(false) // 表单的加载中
|
|
||||||
const uploadRef = ref()
|
|
||||||
const fileList = ref<UploadUserFile[]>([]) // 文件列表
|
|
||||||
const updateSupport = ref(false) // 是否更新已经存在的客户数据
|
|
||||||
const ownerUserId = ref<undefined | number>() // 负责人编号
|
|
||||||
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
|
|
||||||
|
|
||||||
/** 打开弹窗 */
|
|
||||||
const open = async () => {
|
|
||||||
dialogVisible.value = true
|
|
||||||
await resetForm()
|
|
||||||
// 获得用户列表
|
|
||||||
userOptions.value = await UserApi.getSimpleUserList()
|
|
||||||
ownerUserId.value = useUserStore().getUser.id
|
|
||||||
}
|
|
||||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
|
||||||
|
|
||||||
/** 提交表单 */
|
|
||||||
const submitForm = async () => {
|
|
||||||
if (fileList.value.length == 0) {
|
|
||||||
message.error('请上传文件')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
formLoading.value = true
|
|
||||||
try {
|
|
||||||
const formData = new FormData()
|
|
||||||
formData.append('updateSupport', String(updateSupport.value))
|
|
||||||
formData.append('file', fileList.value[0].raw as Blob)
|
|
||||||
formData.append('ownerUserId', String(ownerUserId.value))
|
|
||||||
const res = await CustomerApi.handleImport(formData)
|
|
||||||
submitFormSuccess(res)
|
|
||||||
} catch {
|
|
||||||
submitFormError()
|
|
||||||
} finally {
|
|
||||||
formLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 文件上传成功 */
|
|
||||||
const emits = defineEmits(['success'])
|
|
||||||
const submitFormSuccess = (response: any) => {
|
|
||||||
if (response.code !== 0) {
|
|
||||||
message.error(response.msg)
|
|
||||||
formLoading.value = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 拼接提示语
|
|
||||||
const data = response.data
|
|
||||||
let text = '上传成功数量:' + data.createCustomerNames.length + ';'
|
|
||||||
for (let customerName of data.createCustomerNames) {
|
|
||||||
text += '< ' + customerName + ' >'
|
|
||||||
}
|
|
||||||
text += '更新成功数量:' + data.updateCustomerNames.length + ';'
|
|
||||||
for (const customerName of data.updateCustomerNames) {
|
|
||||||
text += '< ' + customerName + ' >'
|
|
||||||
}
|
|
||||||
text += '更新失败数量:' + Object.keys(data.failureCustomerNames).length + ';'
|
|
||||||
for (const customerName in data.failureCustomerNames) {
|
|
||||||
text += '< ' + customerName + ': ' + data.failureCustomerNames[customerName] + ' >'
|
|
||||||
}
|
|
||||||
message.alert(text)
|
|
||||||
formLoading.value = false
|
|
||||||
dialogVisible.value = false
|
|
||||||
// 发送操作成功的事件
|
|
||||||
emits('success')
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 上传错误提示 */
|
|
||||||
const submitFormError = (): void => {
|
|
||||||
message.error('上传失败,请您重新上传!')
|
|
||||||
formLoading.value = false
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 重置表单 */
|
|
||||||
const resetForm = async () => {
|
|
||||||
// 重置上传状态和文件
|
|
||||||
fileList.value = []
|
|
||||||
updateSupport.value = false
|
|
||||||
ownerUserId.value = undefined
|
|
||||||
await nextTick()
|
|
||||||
uploadRef.value?.clearFiles()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 文件数超出提示 */
|
|
||||||
const handleExceed = (): void => {
|
|
||||||
message.error('最多只能上传一个文件!')
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 下载模板操作 */
|
|
||||||
const importTemplate = async () => {
|
|
||||||
const res = await CustomerApi.importCustomerTemplate()
|
|
||||||
download.excel(res, '客户导入模版.xls')
|
|
||||||
}
|
|
||||||
</script>
|
|
|
@ -1,43 +0,0 @@
|
||||||
<template>
|
|
||||||
<div v-loading="loading">
|
|
||||||
<div class="flex items-start justify-between">
|
|
||||||
<div>
|
|
||||||
<!-- 左上:客户基本信息 -->
|
|
||||||
<el-col>
|
|
||||||
<el-row>
|
|
||||||
<span class="text-xl font-bold">{{ customer.name }}</span>
|
|
||||||
</el-row>
|
|
||||||
</el-col>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<!-- 右上:按钮 -->
|
|
||||||
<slot></slot>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<ContentWrap class="mt-10px">
|
|
||||||
<el-descriptions :column="5" direction="vertical">
|
|
||||||
<el-descriptions-item label="客户级别">
|
|
||||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_LEVEL" :value="customer.level" />
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="成交状态">
|
|
||||||
{{ customer.dealStatus ? '已成交' : '未成交' }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="负责人">{{ customer.ownerUserName }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="创建时间">
|
|
||||||
{{ formatDate(customer.createTime) }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
</el-descriptions>
|
|
||||||
</ContentWrap>
|
|
||||||
</template>
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { DICT_TYPE } from '@/utils/dict'
|
|
||||||
import * as CustomerApi from '@/api/crm/customer'
|
|
||||||
import { formatDate } from '@/utils/formatTime'
|
|
||||||
|
|
||||||
defineOptions({ name: 'CrmCustomerDetailsHeader' })
|
|
||||||
defineProps<{
|
|
||||||
customer: CustomerApi.CustomerVO // 客户信息
|
|
||||||
loading: boolean // 加载中
|
|
||||||
}>()
|
|
||||||
</script>
|
|
|
@ -1,72 +0,0 @@
|
||||||
<template>
|
|
||||||
<ContentWrap>
|
|
||||||
<el-collapse v-model="activeNames" class="">
|
|
||||||
<el-collapse-item name="basicInfo">
|
|
||||||
<template #title>
|
|
||||||
<span class="text-base font-bold">基本信息</span>
|
|
||||||
</template>
|
|
||||||
<el-descriptions :column="4">
|
|
||||||
<el-descriptions-item label="客户名称">
|
|
||||||
{{ customer.name }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="客户来源">
|
|
||||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_SOURCE" :value="customer.source" />
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="手机">{{ customer.mobile }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="电话">{{ customer.telephone }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="邮箱">{{ customer.email }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="地址">
|
|
||||||
{{ customer.areaName }} {{ customer.detailAddress }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="QQ">{{ customer.qq }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="微信">{{ customer.wechat }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="客户行业">
|
|
||||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_INDUSTRY" :value="customer.industryId" />
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="客户级别">
|
|
||||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_LEVEL" :value="customer.level" />
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="下次联系时间">
|
|
||||||
{{ formatDate(customer.contactNextTime) }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="备注">{{ customer.remark }}</el-descriptions-item>
|
|
||||||
</el-descriptions>
|
|
||||||
</el-collapse-item>
|
|
||||||
<el-collapse-item name="systemInfo">
|
|
||||||
<template #title>
|
|
||||||
<span class="text-base font-bold">系统信息</span>
|
|
||||||
</template>
|
|
||||||
<el-descriptions :column="4">
|
|
||||||
<el-descriptions-item label="负责人">{{ customer.ownerUserName }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="最后跟进记录">
|
|
||||||
{{ customer.contactLastContent }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="最后跟进时间">
|
|
||||||
{{ formatDate(customer.contactLastTime) }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label=""> </el-descriptions-item>
|
|
||||||
<el-descriptions-item label="创建人">{{ customer.creatorName }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="创建时间">
|
|
||||||
{{ formatDate(customer.createTime) }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="更新时间">
|
|
||||||
{{ formatDate(customer.updateTime) }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
</el-descriptions>
|
|
||||||
</el-collapse-item>
|
|
||||||
</el-collapse>
|
|
||||||
</ContentWrap>
|
|
||||||
</template>
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import * as CustomerApi from '@/api/crm/customer'
|
|
||||||
import { DICT_TYPE } from '@/utils/dict'
|
|
||||||
import { formatDate } from '@/utils/formatTime'
|
|
||||||
|
|
||||||
defineOptions({ name: 'CrmCustomerDetailsInfo' })
|
|
||||||
const { customer } = defineProps<{
|
|
||||||
customer: CustomerApi.CustomerVO // 客户明细
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const activeNames = ref(['basicInfo', 'systemInfo']) // 展示的折叠面板
|
|
||||||
</script>
|
|
||||||
<style lang="scss" scoped></style>
|
|
|
@ -1,222 +0,0 @@
|
||||||
<template>
|
|
||||||
<CustomerDetailsHeader :customer="customer" :loading="loading">
|
|
||||||
<el-button
|
|
||||||
v-if="permissionListRef?.validateWrite"
|
|
||||||
v-hasPermi="['crm:customer:update']"
|
|
||||||
type="primary"
|
|
||||||
@click="openForm"
|
|
||||||
>
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button v-if="permissionListRef?.validateOwnerUser" type="primary" @click="transfer">
|
|
||||||
转移
|
|
||||||
</el-button>
|
|
||||||
<el-button v-if="permissionListRef?.validateWrite" @click="handleUpdateDealStatus">
|
|
||||||
更改成交状态
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
v-if="customer.lockStatus && permissionListRef?.validateOwnerUser"
|
|
||||||
@click="handleUnlock"
|
|
||||||
>
|
|
||||||
解锁
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
v-if="!customer.lockStatus && permissionListRef?.validateOwnerUser"
|
|
||||||
@click="handleLock"
|
|
||||||
>
|
|
||||||
锁定
|
|
||||||
</el-button>
|
|
||||||
<el-button v-if="!customer.ownerUserId" type="primary" @click="handleReceive"> 领取</el-button>
|
|
||||||
<el-button v-if="!customer.ownerUserId" type="primary" @click="handleDistributeForm">
|
|
||||||
分配
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
v-if="customer.ownerUserId && permissionListRef?.validateOwnerUser"
|
|
||||||
@click="handlePutPool"
|
|
||||||
>
|
|
||||||
放入公海
|
|
||||||
</el-button>
|
|
||||||
</CustomerDetailsHeader>
|
|
||||||
<el-col>
|
|
||||||
<el-tabs>
|
|
||||||
<el-tab-pane label="跟进记录">
|
|
||||||
<FollowUpList :biz-id="customerId" :biz-type="BizTypeEnum.CRM_CUSTOMER" />
|
|
||||||
</el-tab-pane>
|
|
||||||
<el-tab-pane label="基本信息">
|
|
||||||
<CustomerDetailsInfo :customer="customer" />
|
|
||||||
</el-tab-pane>
|
|
||||||
<el-tab-pane label="联系人" lazy>
|
|
||||||
<ContactList :biz-id="customer.id!" :biz-type="BizTypeEnum.CRM_CUSTOMER" />
|
|
||||||
</el-tab-pane>
|
|
||||||
<el-tab-pane label="团队成员">
|
|
||||||
<PermissionList
|
|
||||||
ref="permissionListRef"
|
|
||||||
:biz-id="customer.id!"
|
|
||||||
:biz-type="BizTypeEnum.CRM_CUSTOMER"
|
|
||||||
:show-action="!permissionListRef?.isPool || false"
|
|
||||||
@quit-team="close"
|
|
||||||
/>
|
|
||||||
</el-tab-pane>
|
|
||||||
<el-tab-pane label="商机" lazy>
|
|
||||||
<BusinessList :biz-id="customer.id!" :biz-type="BizTypeEnum.CRM_CUSTOMER" />
|
|
||||||
</el-tab-pane>
|
|
||||||
<el-tab-pane label="合同" lazy>
|
|
||||||
<ContractList :biz-id="customer.id!" :biz-type="BizTypeEnum.CRM_CUSTOMER" />
|
|
||||||
</el-tab-pane>
|
|
||||||
<el-tab-pane label="回款" lazy>
|
|
||||||
<ReceivablePlanList :customer-id="customer.id!" @create-receivable="createReceivable" />
|
|
||||||
<ReceivableList ref="receivableListRef" :customer-id="customer.id!" />
|
|
||||||
</el-tab-pane>
|
|
||||||
<el-tab-pane label="操作日志">
|
|
||||||
<OperateLogV2 :log-list="logList" />
|
|
||||||
</el-tab-pane>
|
|
||||||
</el-tabs>
|
|
||||||
</el-col>
|
|
||||||
|
|
||||||
<!-- 表单弹窗:添加/修改 -->
|
|
||||||
<CustomerForm ref="formRef" @success="getCustomer" />
|
|
||||||
<CustomerDistributeForm ref="distributeForm" @success="getCustomer" />
|
|
||||||
<CrmTransferForm ref="transferFormRef" :biz-type="BizTypeEnum.CRM_CUSTOMER" @success="close" />
|
|
||||||
</template>
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { useTagsViewStore } from '@/store/modules/tagsView'
|
|
||||||
import * as CustomerApi from '@/api/crm/customer'
|
|
||||||
import CustomerForm from '@/views/crm/customer/CustomerForm.vue'
|
|
||||||
import CustomerDetailsInfo from './CustomerDetailsInfo.vue' // 客户明细 - 详细信息
|
|
||||||
import CustomerDetailsHeader from './CustomerDetailsHeader.vue' // 客户明细 - 头部
|
|
||||||
import ContactList from '@/views/crm/contact/components/ContactList.vue' // 联系人列表
|
|
||||||
import ContractList from '@/views/crm/contract/components/ContractList.vue' // 合同列表
|
|
||||||
import BusinessList from '@/views/crm/business/components/BusinessList.vue' // 商机列表
|
|
||||||
import ReceivableList from '@/views/crm/receivable/components/ReceivableList.vue' // 回款列表
|
|
||||||
import ReceivablePlanList from '@/views/crm/receivable/plan/components/ReceivablePlanList.vue' // 回款计划列表
|
|
||||||
import PermissionList from '@/views/crm/permission/components/PermissionList.vue' // 团队成员列表(权限)
|
|
||||||
import CrmTransferForm from '@/views/crm/permission/components/TransferForm.vue'
|
|
||||||
import FollowUpList from '@/views/crm/followup/index.vue'
|
|
||||||
import { BizTypeEnum } from '@/api/crm/permission'
|
|
||||||
import type { OperateLogVO } from '@/api/system/operatelog'
|
|
||||||
import { getOperateLogPage } from '@/api/crm/operateLog'
|
|
||||||
import CustomerDistributeForm from '@/views/crm/customer/pool/CustomerDistributeForm.vue'
|
|
||||||
|
|
||||||
defineOptions({ name: 'CrmCustomerDetail' })
|
|
||||||
|
|
||||||
const customerId = ref(0) // 客户编号
|
|
||||||
const loading = ref(true) // 加载中
|
|
||||||
const message = useMessage() // 消息弹窗
|
|
||||||
const { delView } = useTagsViewStore() // 视图操作
|
|
||||||
const { push, currentRoute } = useRouter() // 路由
|
|
||||||
|
|
||||||
const permissionListRef = ref<InstanceType<typeof PermissionList>>() // 团队成员列表 Ref
|
|
||||||
|
|
||||||
/** 获取详情 */
|
|
||||||
const customer = ref<CustomerApi.CustomerVO>({} as CustomerApi.CustomerVO) // 客户详情
|
|
||||||
const getCustomer = async () => {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
customer.value = await CustomerApi.getCustomer(customerId.value)
|
|
||||||
await getOperateLog()
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 编辑客户 */
|
|
||||||
const formRef = ref<InstanceType<typeof CustomerForm>>() // 客户表单 Ref
|
|
||||||
const openForm = () => {
|
|
||||||
formRef.value?.open('update', customerId.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 更新成交状态操作 */
|
|
||||||
const handleUpdateDealStatus = async () => {
|
|
||||||
const dealStatus = !customer.value.dealStatus
|
|
||||||
try {
|
|
||||||
// 更新状态的二次确认
|
|
||||||
await message.confirm(`确定更新成交状态为【${dealStatus ? '已成交' : '未成交'}】吗?`)
|
|
||||||
// 发起更新
|
|
||||||
await CustomerApi.updateCustomerDealStatus(customerId.value, dealStatus)
|
|
||||||
message.success(`更新成交状态成功`)
|
|
||||||
// 刷新数据
|
|
||||||
await getCustomer()
|
|
||||||
} catch {}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 客户转移 */
|
|
||||||
const transferFormRef = ref<InstanceType<typeof CrmTransferForm>>() // 客户转移表单 ref
|
|
||||||
const transfer = () => {
|
|
||||||
transferFormRef.value?.open(customerId.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 锁定客户 */
|
|
||||||
const handleLock = async () => {
|
|
||||||
await message.confirm(`确定锁定客户【${customer.value.name}】 吗?`)
|
|
||||||
await CustomerApi.lockCustomer(unref(customerId.value), true)
|
|
||||||
message.success(`锁定客户【${customer.value.name}】成功`)
|
|
||||||
await getCustomer()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 解锁客户 */
|
|
||||||
const handleUnlock = async () => {
|
|
||||||
await message.confirm(`确定解锁客户【${customer.value.name}】 吗?`)
|
|
||||||
await CustomerApi.lockCustomer(unref(customerId.value), false)
|
|
||||||
message.success(`解锁客户【${customer.value.name}】成功`)
|
|
||||||
await getCustomer()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 领取客户 */
|
|
||||||
const handleReceive = async () => {
|
|
||||||
await message.confirm(`确定领取客户【${customer.value.name}】 吗?`)
|
|
||||||
await CustomerApi.receiveCustomer([unref(customerId.value)])
|
|
||||||
message.success(`领取客户【${customer.value.name}】成功`)
|
|
||||||
await getCustomer()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 分配客户 */
|
|
||||||
const distributeForm = ref<InstanceType<typeof CustomerDistributeForm>>() // 分配客户表单 Ref
|
|
||||||
const handleDistributeForm = async () => {
|
|
||||||
distributeForm.value?.open(customerId.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 客户放入公海 */
|
|
||||||
const handlePutPool = async () => {
|
|
||||||
await message.confirm(`确定将客户【${customer.value.name}】放入公海吗?`)
|
|
||||||
await CustomerApi.putCustomerPool(unref(customerId.value))
|
|
||||||
message.success(`客户【${customer.value.name}】放入公海成功`)
|
|
||||||
// 加载
|
|
||||||
close()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 获取操作日志 */
|
|
||||||
const logList = ref<OperateLogVO[]>([]) // 操作日志列表
|
|
||||||
const getOperateLog = async () => {
|
|
||||||
if (!customerId.value) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const data = await getOperateLogPage({
|
|
||||||
bizType: BizTypeEnum.CRM_CUSTOMER,
|
|
||||||
bizId: customerId.value
|
|
||||||
})
|
|
||||||
logList.value = data.list
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 从回款计划创建回款 */
|
|
||||||
const receivableListRef = ref<InstanceType<typeof ReceivableList>>() // 回款列表 Ref
|
|
||||||
const createReceivable = (planData: any) => {
|
|
||||||
receivableListRef.value?.createReceivable(planData)
|
|
||||||
}
|
|
||||||
|
|
||||||
const close = () => {
|
|
||||||
delView(unref(currentRoute))
|
|
||||||
push({ name: 'CrmCustomer' })
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 初始化 */
|
|
||||||
const { params } = useRoute()
|
|
||||||
onMounted(() => {
|
|
||||||
if (!params.id) {
|
|
||||||
message.warning('参数错误,客户不能为空!')
|
|
||||||
close()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
customerId.value = params.id as unknown as number
|
|
||||||
getCustomer()
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,343 +0,0 @@
|
||||||
<template>
|
|
||||||
<!-- <doc-alert title="【客户】客户管理、公海客户" url="https://doc.iocoder.cn/crm/customer/" />
|
|
||||||
<doc-alert title="【通用】数据权限" url="https://doc.iocoder.cn/crm/permission/" /> -->
|
|
||||||
|
|
||||||
<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="mobile">
|
|
||||||
<el-input
|
|
||||||
v-model="queryParams.mobile"
|
|
||||||
class="!w-240px"
|
|
||||||
clearable
|
|
||||||
placeholder="请输入手机"
|
|
||||||
@keyup.enter="handleQuery"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="所属行业" prop="industryId">
|
|
||||||
<el-select
|
|
||||||
v-model="queryParams.industryId"
|
|
||||||
class="!w-240px"
|
|
||||||
clearable
|
|
||||||
placeholder="请选择所属行业"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_INDUSTRY)"
|
|
||||||
:key="dict.value"
|
|
||||||
:label="dict.label"
|
|
||||||
:value="dict.value"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="客户级别" prop="level">
|
|
||||||
<el-select
|
|
||||||
v-model="queryParams.level"
|
|
||||||
class="!w-240px"
|
|
||||||
clearable
|
|
||||||
placeholder="请选择客户级别"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_LEVEL)"
|
|
||||||
:key="dict.value"
|
|
||||||
:label="dict.label"
|
|
||||||
:value="dict.value"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="客户来源" prop="source">
|
|
||||||
<el-select
|
|
||||||
v-model="queryParams.source"
|
|
||||||
class="!w-240px"
|
|
||||||
clearable
|
|
||||||
placeholder="请选择客户来源"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_SOURCE)"
|
|
||||||
:key="dict.value"
|
|
||||||
:label="dict.label"
|
|
||||||
:value="dict.value"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</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-button v-hasPermi="['crm:customer:create']" type="primary" @click="openForm('create')">
|
|
||||||
<Icon class="mr-5px" icon="ep:plus" />
|
|
||||||
新增
|
|
||||||
</el-button>
|
|
||||||
<el-button v-hasPermi="['crm:customer:import']" plain type="warning" @click="handleImport">
|
|
||||||
<Icon icon="ep:upload" />
|
|
||||||
导入
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
v-hasPermi="['crm:customer:export']"
|
|
||||||
:loading="exportLoading"
|
|
||||||
plain
|
|
||||||
type="success"
|
|
||||||
@click="handleExport"
|
|
||||||
>
|
|
||||||
<Icon class="mr-5px" icon="ep:download" />
|
|
||||||
导出
|
|
||||||
</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</ContentWrap>
|
|
||||||
|
|
||||||
<!-- 列表 -->
|
|
||||||
<ContentWrap>
|
|
||||||
<el-tabs v-model="activeName" @tab-click="handleTabClick">
|
|
||||||
<el-tab-pane label="我负责的" name="1" />
|
|
||||||
<el-tab-pane label="我参与的" name="2" />
|
|
||||||
<el-tab-pane label="下属负责的" name="3" />
|
|
||||||
</el-tabs>
|
|
||||||
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true">
|
|
||||||
<el-table-column align="center" fixed="left" label="客户名称" prop="name" width="160">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-link :underline="false" type="primary" @click="openDetail(scope.row.id)">
|
|
||||||
{{ scope.row.name }}
|
|
||||||
</el-link>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="客户来源" prop="source" width="100">
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_SOURCE" :value="scope.row.source" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="手机" prop="mobile" width="120" />
|
|
||||||
<el-table-column align="center" label="电话" prop="telephone" width="130" />
|
|
||||||
<el-table-column align="center" label="邮箱" prop="email" width="180" />
|
|
||||||
<el-table-column align="center" label="客户级别" prop="level" width="135">
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_LEVEL" :value="scope.row.level" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="客户行业" prop="industryId" width="100">
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_INDUSTRY" :value="scope.row.industryId" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="下次联系时间"
|
|
||||||
prop="contactNextTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="备注" prop="remark" width="200" />
|
|
||||||
<el-table-column align="center" label="锁定状态" prop="lockStatus">
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.lockStatus" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="成交状态" prop="dealStatus">
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.dealStatus" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="最后跟进时间"
|
|
||||||
prop="contactLastTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="最后跟进记录" prop="contactLastContent" width="200" />
|
|
||||||
<el-table-column align="center" label="地址" prop="detailAddress" width="180" />
|
|
||||||
<el-table-column align="center" label="距离进入公海天数" prop="poolDay" width="140">
|
|
||||||
<template #default="scope"> {{ scope.row.poolDay }} 天</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="负责人" prop="ownerUserName" width="100px" />
|
|
||||||
<el-table-column align="center" label="所属部门" prop="ownerUserDeptName" width="100px" />
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="更新时间"
|
|
||||||
prop="updateTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="创建时间"
|
|
||||||
prop="createTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="创建人" prop="creatorName" width="100px" />
|
|
||||||
<el-table-column align="center" fixed="right" label="操作" min-width="150">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
v-hasPermi="['crm:customer:update']"
|
|
||||||
link
|
|
||||||
type="primary"
|
|
||||||
@click="openForm('update', scope.row.id)"
|
|
||||||
>
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
v-hasPermi="['crm:customer:delete']"
|
|
||||||
link
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(scope.row.id)"
|
|
||||||
>
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
<!-- 分页 -->
|
|
||||||
<Pagination
|
|
||||||
v-model:limit="queryParams.pageSize"
|
|
||||||
v-model:page="queryParams.pageNo"
|
|
||||||
:total="total"
|
|
||||||
@pagination="getList"
|
|
||||||
/>
|
|
||||||
</ContentWrap>
|
|
||||||
|
|
||||||
<!-- 表单弹窗:添加/修改 -->
|
|
||||||
<CustomerForm ref="formRef" @success="getList" />
|
|
||||||
<CustomerImportForm ref="importFormRef" @success="getList" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
|
||||||
import { dateFormatter } from '@/utils/formatTime'
|
|
||||||
import download from '@/utils/download'
|
|
||||||
import * as CustomerApi from '@/api/crm/customer'
|
|
||||||
import CustomerForm from './CustomerForm.vue'
|
|
||||||
import CustomerImportForm from './CustomerImportForm.vue'
|
|
||||||
import { TabsPaneContext } from 'element-plus'
|
|
||||||
|
|
||||||
defineOptions({ name: 'CrmCustomer' })
|
|
||||||
|
|
||||||
const message = useMessage() // 消息弹窗
|
|
||||||
const { t } = useI18n() // 国际化
|
|
||||||
|
|
||||||
const loading = ref(true) // 列表的加载中
|
|
||||||
const total = ref(0) // 列表的总页数
|
|
||||||
const list = ref([]) // 列表的数据
|
|
||||||
const queryParams = reactive({
|
|
||||||
pageNo: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
sceneType: '1', // 默认和 activeName 相等
|
|
||||||
name: '',
|
|
||||||
mobile: '',
|
|
||||||
industryId: undefined,
|
|
||||||
level: undefined,
|
|
||||||
source: undefined,
|
|
||||||
pool: undefined
|
|
||||||
})
|
|
||||||
const queryFormRef = ref() // 搜索的表单
|
|
||||||
const exportLoading = ref(false) // 导出的加载中
|
|
||||||
const activeName = ref('1') // 列表 tab
|
|
||||||
|
|
||||||
/** tab 切换 */
|
|
||||||
const handleTabClick = (tab: TabsPaneContext) => {
|
|
||||||
queryParams.sceneType = tab.paneName as string
|
|
||||||
handleQuery()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 查询列表 */
|
|
||||||
const getList = async () => {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
const data = await CustomerApi.getCustomerPage(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 { currentRoute, push } = useRouter()
|
|
||||||
const openDetail = (id: number) => {
|
|
||||||
push({ name: 'CrmCustomerDetail', params: { id } })
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 添加/修改操作 */
|
|
||||||
const formRef = ref()
|
|
||||||
const openForm = (type: string, id?: number) => {
|
|
||||||
formRef.value.open(type, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 删除按钮操作 */
|
|
||||||
const handleDelete = async (id: number) => {
|
|
||||||
try {
|
|
||||||
// 删除的二次确认
|
|
||||||
await message.delConfirm()
|
|
||||||
// 发起删除
|
|
||||||
await CustomerApi.deleteCustomer(id)
|
|
||||||
message.success(t('common.delSuccess'))
|
|
||||||
// 刷新列表
|
|
||||||
await getList()
|
|
||||||
} catch {}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 导入按钮操作 */
|
|
||||||
const importFormRef = ref<InstanceType<typeof CustomerImportForm>>()
|
|
||||||
const handleImport = () => {
|
|
||||||
importFormRef.value?.open()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 导出按钮操作 */
|
|
||||||
const handleExport = async () => {
|
|
||||||
try {
|
|
||||||
// 导出的二次确认
|
|
||||||
await message.exportConfirm()
|
|
||||||
// 发起导出
|
|
||||||
exportLoading.value = true
|
|
||||||
const data = await CustomerApi.exportCustomer(queryParams)
|
|
||||||
download.excel(data, '客户.xls')
|
|
||||||
} catch {
|
|
||||||
} finally {
|
|
||||||
exportLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 监听路由变化更新列表 */
|
|
||||||
watch(
|
|
||||||
() => currentRoute.value,
|
|
||||||
() => {
|
|
||||||
getList()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
/** 初始化 **/
|
|
||||||
onMounted(() => {
|
|
||||||
getList()
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,150 +0,0 @@
|
||||||
<template>
|
|
||||||
<Dialog :title="dialogTitle" v-model="dialogVisible">
|
|
||||||
<el-form
|
|
||||||
ref="formRef"
|
|
||||||
:model="formData"
|
|
||||||
:rules="formRules"
|
|
||||||
label-width="200px"
|
|
||||||
v-loading="formLoading"
|
|
||||||
>
|
|
||||||
<el-form-item label="规则适用人群" prop="userIds">
|
|
||||||
<el-select multiple filterable v-model="formData.userIds">
|
|
||||||
<el-option
|
|
||||||
v-for="item in userOptions"
|
|
||||||
:key="item.id"
|
|
||||||
:label="item.nickname"
|
|
||||||
:value="item.id"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="规则适用部门" prop="deptIds">
|
|
||||||
<el-tree-select
|
|
||||||
v-model="formData.deptIds"
|
|
||||||
:data="deptTree"
|
|
||||||
:props="defaultProps"
|
|
||||||
multiple
|
|
||||||
filterable
|
|
||||||
check-strictly
|
|
||||||
node-key="id"
|
|
||||||
placeholder="请选择规则适用部门"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item
|
|
||||||
:label="
|
|
||||||
formData.type === LimitConfType.CUSTOMER_QUANTITY_LIMIT
|
|
||||||
? '拥有客户数上限'
|
|
||||||
: '锁定客户数上限'
|
|
||||||
"
|
|
||||||
prop="maxCount"
|
|
||||||
>
|
|
||||||
<el-input-number v-model="formData.maxCount" placeholder="请输入数量上限" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item
|
|
||||||
label="成交客户是否占用拥有客户数"
|
|
||||||
v-if="formData.type === LimitConfType.CUSTOMER_QUANTITY_LIMIT"
|
|
||||||
prop="dealCountEnabled"
|
|
||||||
>
|
|
||||||
<el-switch v-model="formData.dealCountEnabled" />
|
|
||||||
</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 * as CustomerLimitConfigApi from '@/api/crm/customer/limitConfig'
|
|
||||||
import * as DeptApi from '@/api/system/dept'
|
|
||||||
import { defaultProps, handleTree } from '@/utils/tree'
|
|
||||||
import * as UserApi from '@/api/system/user'
|
|
||||||
import { cloneDeep } from 'lodash-es'
|
|
||||||
import { LimitConfType } from '@/api/crm/customer/limitConfig'
|
|
||||||
import { aw } from '../../../../../dist-prod/assets/index-9eac537b'
|
|
||||||
|
|
||||||
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,
|
|
||||||
type: LimitConfType.CUSTOMER_LOCK_LIMIT, // 给个默认值,避免 IDE 报错
|
|
||||||
userIds: undefined,
|
|
||||||
deptIds: undefined,
|
|
||||||
maxCount: undefined,
|
|
||||||
dealCountEnabled: false
|
|
||||||
})
|
|
||||||
const formRules = reactive({
|
|
||||||
type: [{ required: true, message: '规则类型不能为空', trigger: 'change' }],
|
|
||||||
maxCount: [{ required: true, message: '数量上限不能为空', trigger: 'blur' }]
|
|
||||||
})
|
|
||||||
const formRef = ref() // 表单 Ref
|
|
||||||
const deptTree = ref() // 部门树形结构
|
|
||||||
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
|
|
||||||
|
|
||||||
/** 打开弹窗 */
|
|
||||||
const open = async (type: string, limitConfType: LimitConfType, id?: number) => {
|
|
||||||
dialogVisible.value = true
|
|
||||||
dialogTitle.value = t('action.' + type)
|
|
||||||
formType.value = type
|
|
||||||
resetForm()
|
|
||||||
// 修改时,设置数据
|
|
||||||
if (id) {
|
|
||||||
formLoading.value = true
|
|
||||||
try {
|
|
||||||
formData.value = await CustomerLimitConfigApi.getCustomerLimitConfig(id)
|
|
||||||
} finally {
|
|
||||||
formLoading.value = false
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
formData.value.type = limitConfType
|
|
||||||
}
|
|
||||||
// 获得部门树
|
|
||||||
deptTree.value = handleTree(await DeptApi.getSimpleDeptList())
|
|
||||||
// 获得用户
|
|
||||||
userOptions.value = await UserApi.getSimpleUserList()
|
|
||||||
}
|
|
||||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
|
||||||
|
|
||||||
/** 提交表单 */
|
|
||||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
|
||||||
const submitForm = async () => {
|
|
||||||
// 校验表单
|
|
||||||
if (!formRef) return
|
|
||||||
const valid = await formRef.value.validate()
|
|
||||||
if (!valid) return
|
|
||||||
// 提交请求
|
|
||||||
formLoading.value = true
|
|
||||||
try {
|
|
||||||
const data = formData.value as unknown as CustomerLimitConfigApi.CustomerLimitConfigVO
|
|
||||||
if (formType.value === 'create') {
|
|
||||||
await CustomerLimitConfigApi.createCustomerLimitConfig(data)
|
|
||||||
message.success(t('common.createSuccess'))
|
|
||||||
} else {
|
|
||||||
await CustomerLimitConfigApi.updateCustomerLimitConfig(data)
|
|
||||||
message.success(t('common.updateSuccess'))
|
|
||||||
}
|
|
||||||
dialogVisible.value = false
|
|
||||||
// 发送操作成功的事件
|
|
||||||
emit('success')
|
|
||||||
} finally {
|
|
||||||
formLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 重置表单 */
|
|
||||||
const resetForm = () => {
|
|
||||||
formData.value = {
|
|
||||||
id: undefined,
|
|
||||||
type: LimitConfType.CUSTOMER_LOCK_LIMIT,
|
|
||||||
userIds: undefined,
|
|
||||||
deptIds: undefined,
|
|
||||||
maxCount: undefined,
|
|
||||||
dealCountEnabled: false
|
|
||||||
}
|
|
||||||
formRef.value?.resetFields()
|
|
||||||
}
|
|
||||||
</script>
|
|
|
@ -1,150 +0,0 @@
|
||||||
<template>
|
|
||||||
<el-button plain @click="handleQuery"> <Icon icon="ep:refresh" class="mr-5px" /> 刷新 </el-button>
|
|
||||||
<el-button
|
|
||||||
type="primary"
|
|
||||||
plain
|
|
||||||
@click="openForm('create')"
|
|
||||||
v-hasPermi="['crm:customer-limit-config:create']"
|
|
||||||
>
|
|
||||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
|
||||||
</el-button>
|
|
||||||
<el-table
|
|
||||||
v-loading="loading"
|
|
||||||
:data="list"
|
|
||||||
:stripe="true"
|
|
||||||
:show-overflow-tooltip="true"
|
|
||||||
class="mt-4"
|
|
||||||
>
|
|
||||||
<el-table-column label="编号" align="center" prop="id" />
|
|
||||||
<el-table-column
|
|
||||||
label="规则适用人群"
|
|
||||||
align="center"
|
|
||||||
:formatter="(row) => row.users?.map((user: any) => user.nickname).join(',')"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
label="规则适用部门"
|
|
||||||
align="center"
|
|
||||||
:formatter="(row) => row.depts?.map((dept: any) => dept.name).join(',')"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
:label="
|
|
||||||
confType === LimitConfType.CUSTOMER_QUANTITY_LIMIT ? '拥有客户数上限' : '锁定客户数上限'
|
|
||||||
"
|
|
||||||
align="center"
|
|
||||||
prop="maxCount"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
v-if="confType === LimitConfType.CUSTOMER_QUANTITY_LIMIT"
|
|
||||||
label="成交客户是否占用拥有客户数"
|
|
||||||
align="center"
|
|
||||||
prop="dealCountEnabled"
|
|
||||||
min-width="100"
|
|
||||||
>
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.dealCountEnabled" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
label="创建时间"
|
|
||||||
align="center"
|
|
||||||
prop="createTime"
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column label="操作" align="center" min-width="110" fixed="right">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
link
|
|
||||||
type="primary"
|
|
||||||
@click="openForm('update', scope.row.id)"
|
|
||||||
v-hasPermi="['crm:customer-limit-config:update']"
|
|
||||||
>
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
link
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(scope.row.id)"
|
|
||||||
v-hasPermi="['crm:customer-limit-config:delete']"
|
|
||||||
>
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
<!-- 分页 -->
|
|
||||||
<Pagination
|
|
||||||
:total="total"
|
|
||||||
v-model:page="queryParams.pageNo"
|
|
||||||
v-model:limit="queryParams.pageSize"
|
|
||||||
@pagination="getList"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- 表单弹窗:添加/修改 -->
|
|
||||||
<CustomerLimitConfigForm ref="formRef" @success="getList" />
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { dateFormatter } from '@/utils/formatTime'
|
|
||||||
import * as CustomerLimitConfigApi from '@/api/crm/customer/limitConfig'
|
|
||||||
import CustomerLimitConfigForm from './CustomerLimitConfigForm.vue'
|
|
||||||
import { DICT_TYPE } from '@/utils/dict'
|
|
||||||
import { LimitConfType } from '@/api/crm/customer/limitConfig'
|
|
||||||
|
|
||||||
defineOptions({ name: 'CustomerLimitConfigList' })
|
|
||||||
|
|
||||||
const message = useMessage() // 消息弹窗
|
|
||||||
const { t } = useI18n() // 国际化
|
|
||||||
|
|
||||||
const { confType } = defineProps<{ confType: LimitConfType }>()
|
|
||||||
|
|
||||||
const loading = ref(true) // 列表的加载中
|
|
||||||
const total = ref(0) // 列表的总页数
|
|
||||||
const list = ref([]) // 列表的数据
|
|
||||||
const queryParams = reactive({
|
|
||||||
pageNo: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
type: confType
|
|
||||||
})
|
|
||||||
|
|
||||||
/** 查询列表 */
|
|
||||||
const getList = async () => {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
const data = await CustomerLimitConfigApi.getCustomerLimitConfigPage(queryParams)
|
|
||||||
list.value = data.list
|
|
||||||
total.value = data.total
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 添加/修改操作 */
|
|
||||||
const formRef = ref()
|
|
||||||
const openForm = (type: string, id?: number) => {
|
|
||||||
formRef.value.open(type, confType, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 删除按钮操作 */
|
|
||||||
const handleDelete = async (id: number) => {
|
|
||||||
try {
|
|
||||||
// 删除的二次确认
|
|
||||||
await message.delConfirm()
|
|
||||||
// 发起删除
|
|
||||||
await CustomerLimitConfigApi.deleteCustomerLimitConfig(id)
|
|
||||||
message.success(t('common.delSuccess'))
|
|
||||||
// 刷新列表
|
|
||||||
await getList()
|
|
||||||
} catch {}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 搜索按钮操作 */
|
|
||||||
const handleQuery = () => {
|
|
||||||
queryParams.pageNo = 1
|
|
||||||
getList()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 初始化 **/
|
|
||||||
onMounted(() => {
|
|
||||||
getList()
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,22 +0,0 @@
|
||||||
<template>
|
|
||||||
<!-- <doc-alert title="【客户】客户管理、公海客户" url="https://doc.iocoder.cn/crm/customer/" />
|
|
||||||
<doc-alert title="【通用】数据权限" url="https://doc.iocoder.cn/crm/permission/" /> -->
|
|
||||||
|
|
||||||
<!-- 列表 -->
|
|
||||||
<ContentWrap>
|
|
||||||
<el-tabs>
|
|
||||||
<el-tab-pane label="拥有客户数限制">
|
|
||||||
<CustomerLimitConfigList :confType="LimitConfType.CUSTOMER_QUANTITY_LIMIT" />
|
|
||||||
</el-tab-pane>
|
|
||||||
<el-tab-pane label="锁定客户数限制">
|
|
||||||
<CustomerLimitConfigList :confType="LimitConfType.CUSTOMER_LOCK_LIMIT" />
|
|
||||||
</el-tab-pane>
|
|
||||||
</el-tabs>
|
|
||||||
</ContentWrap>
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
import CustomerLimitConfigList from './CustomerLimitConfigList.vue'
|
|
||||||
import { LimitConfType } from '@/api/crm/customer/limitConfig'
|
|
||||||
|
|
||||||
defineOptions({ name: 'CrmCustomerLimitConfig' })
|
|
||||||
</script>
|
|
|
@ -1,85 +0,0 @@
|
||||||
<template>
|
|
||||||
<Dialog v-model="dialogVisible" title="分配客户">
|
|
||||||
<el-form
|
|
||||||
ref="formRef"
|
|
||||||
v-loading="formLoading"
|
|
||||||
:model="formData"
|
|
||||||
:rules="formRules"
|
|
||||||
label-width="100px"
|
|
||||||
>
|
|
||||||
<el-form-item label="负责人" prop="ownerUserId">
|
|
||||||
<el-select v-model="formData.ownerUserId" class="w-1/1">
|
|
||||||
<el-option
|
|
||||||
v-for="item in userOptions"
|
|
||||||
:key="item.id"
|
|
||||||
:label="item.nickname"
|
|
||||||
:value="item.id"
|
|
||||||
/>
|
|
||||||
</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 * as CustomerApi from '@/api/crm/customer'
|
|
||||||
import * as UserApi from '@/api/system/user'
|
|
||||||
import { distributeCustomer } from '@/api/crm/customer'
|
|
||||||
|
|
||||||
const { t } = useI18n() // 国际化
|
|
||||||
const message = useMessage() // 消息弹窗
|
|
||||||
|
|
||||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
|
||||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
|
||||||
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
|
|
||||||
const formData = ref({
|
|
||||||
id: undefined,
|
|
||||||
ownerUserId: undefined
|
|
||||||
})
|
|
||||||
const formRules = reactive({
|
|
||||||
ownerUserId: [{ required: true, message: '负责人不能为空', trigger: 'blur' }]
|
|
||||||
})
|
|
||||||
const formRef = ref() // 表单 Ref
|
|
||||||
|
|
||||||
/** 打开弹窗 */
|
|
||||||
const open = async (id: number) => {
|
|
||||||
dialogVisible.value = true
|
|
||||||
resetForm()
|
|
||||||
formData.value.id = id
|
|
||||||
// 获得用户列表
|
|
||||||
userOptions.value = await UserApi.getSimpleUserList()
|
|
||||||
}
|
|
||||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
|
||||||
|
|
||||||
/** 提交表单 */
|
|
||||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
|
||||||
const submitForm = async () => {
|
|
||||||
// 校验表单
|
|
||||||
if (!formRef) return
|
|
||||||
const valid = await formRef.value.validate()
|
|
||||||
if (!valid) return
|
|
||||||
// 提交请求
|
|
||||||
formLoading.value = true
|
|
||||||
try {
|
|
||||||
await CustomerApi.distributeCustomer([formData.value.id], formData.value.ownerUserId)
|
|
||||||
message.success('分配客户成功')
|
|
||||||
dialogVisible.value = false
|
|
||||||
// 发送操作成功的事件
|
|
||||||
emit('success')
|
|
||||||
} finally {
|
|
||||||
formLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 重置表单 */
|
|
||||||
const resetForm = () => {
|
|
||||||
formData.value = {
|
|
||||||
id: undefined,
|
|
||||||
ownerUserId: undefined
|
|
||||||
}
|
|
||||||
formRef.value?.resetFields()
|
|
||||||
}
|
|
||||||
</script>
|
|
|
@ -1,270 +0,0 @@
|
||||||
<template>
|
|
||||||
<!-- <doc-alert title="【客户】客户管理、公海客户" url="https://doc.iocoder.cn/crm/customer/" />
|
|
||||||
<doc-alert title="【通用】数据权限" url="https://doc.iocoder.cn/crm/permission/" /> -->
|
|
||||||
|
|
||||||
<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="mobile">
|
|
||||||
<el-input
|
|
||||||
v-model="queryParams.mobile"
|
|
||||||
class="!w-240px"
|
|
||||||
clearable
|
|
||||||
placeholder="请输入手机"
|
|
||||||
@keyup.enter="handleQuery"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="所属行业" prop="industryId">
|
|
||||||
<el-select
|
|
||||||
v-model="queryParams.industryId"
|
|
||||||
class="!w-240px"
|
|
||||||
clearable
|
|
||||||
placeholder="请选择所属行业"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_INDUSTRY)"
|
|
||||||
:key="dict.value"
|
|
||||||
:label="dict.label"
|
|
||||||
:value="dict.value"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="客户级别" prop="level">
|
|
||||||
<el-select
|
|
||||||
v-model="queryParams.level"
|
|
||||||
class="!w-240px"
|
|
||||||
clearable
|
|
||||||
placeholder="请选择客户级别"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_LEVEL)"
|
|
||||||
:key="dict.value"
|
|
||||||
:label="dict.label"
|
|
||||||
:value="dict.value"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="客户来源" prop="source">
|
|
||||||
<el-select
|
|
||||||
v-model="queryParams.source"
|
|
||||||
class="!w-240px"
|
|
||||||
clearable
|
|
||||||
placeholder="请选择客户来源"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_SOURCE)"
|
|
||||||
:key="dict.value"
|
|
||||||
:label="dict.label"
|
|
||||||
:value="dict.value"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</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-button
|
|
||||||
v-hasPermi="['crm:customer:export']"
|
|
||||||
:loading="exportLoading"
|
|
||||||
plain
|
|
||||||
type="success"
|
|
||||||
@click="handleExport"
|
|
||||||
>
|
|
||||||
<Icon class="mr-5px" icon="ep:download" />
|
|
||||||
导出
|
|
||||||
</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</ContentWrap>
|
|
||||||
|
|
||||||
<!-- 列表 -->
|
|
||||||
<ContentWrap>
|
|
||||||
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true">
|
|
||||||
<el-table-column align="center" label="客户名称" fixed="left" prop="name" width="160">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-link :underline="false" type="primary" @click="openDetail(scope.row.id)">
|
|
||||||
{{ scope.row.name }}
|
|
||||||
</el-link>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="客户来源" prop="source" width="100">
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_SOURCE" :value="scope.row.source" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="手机" align="center" prop="mobile" width="120" />
|
|
||||||
<el-table-column label="电话" align="center" prop="telephone" width="130" />
|
|
||||||
<el-table-column label="邮箱" align="center" prop="email" width="180" />
|
|
||||||
<el-table-column align="center" label="客户级别" prop="level" width="135">
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_LEVEL" :value="scope.row.level" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="客户行业" prop="industryId" width="100">
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_INDUSTRY" :value="scope.row.industryId" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="下次联系时间"
|
|
||||||
prop="contactNextTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="备注" prop="remark" width="200" />
|
|
||||||
<el-table-column align="center" label="成交状态" prop="dealStatus">
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.dealStatus" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="最后跟进时间"
|
|
||||||
prop="contactLastTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="最后跟进记录" prop="contactLastContent" width="200" />
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="更新时间"
|
|
||||||
prop="updateTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
:formatter="dateFormatter"
|
|
||||||
align="center"
|
|
||||||
label="创建时间"
|
|
||||||
prop="createTime"
|
|
||||||
width="180px"
|
|
||||||
/>
|
|
||||||
<el-table-column align="center" label="创建人" prop="creatorName" width="100px" />
|
|
||||||
</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, getIntDictOptions } from '@/utils/dict'
|
|
||||||
import { dateFormatter } from '@/utils/formatTime'
|
|
||||||
import download from '@/utils/download'
|
|
||||||
import * as CustomerApi from '@/api/crm/customer'
|
|
||||||
|
|
||||||
defineOptions({ name: 'CrmCustomerPool' })
|
|
||||||
|
|
||||||
const message = useMessage() // 消息弹窗
|
|
||||||
|
|
||||||
const loading = ref(true) // 列表的加载中
|
|
||||||
const total = ref(0) // 列表的总页数
|
|
||||||
const list = ref([]) // 列表的数据
|
|
||||||
const queryParams = ref({
|
|
||||||
pageNo: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
name: '',
|
|
||||||
mobile: '',
|
|
||||||
industryId: undefined,
|
|
||||||
level: undefined,
|
|
||||||
source: undefined,
|
|
||||||
sceneType: undefined,
|
|
||||||
pool: true
|
|
||||||
})
|
|
||||||
const queryFormRef = ref() // 搜索的表单
|
|
||||||
const exportLoading = ref(false) // 导出的加载中
|
|
||||||
|
|
||||||
/** 查询列表 */
|
|
||||||
const getList = async () => {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
const data = await CustomerApi.getCustomerPage(queryParams.value)
|
|
||||||
list.value = data.list
|
|
||||||
total.value = data.total
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 搜索按钮操作 */
|
|
||||||
const handleQuery = () => {
|
|
||||||
queryParams.value.pageNo = 1
|
|
||||||
getList()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 重置按钮操作 */
|
|
||||||
const resetQuery = () => {
|
|
||||||
queryFormRef.value.resetFields()
|
|
||||||
queryParams.value = {
|
|
||||||
pageNo: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
name: '',
|
|
||||||
mobile: '',
|
|
||||||
industryId: undefined,
|
|
||||||
level: undefined,
|
|
||||||
source: undefined,
|
|
||||||
sceneType: undefined,
|
|
||||||
pool: true
|
|
||||||
}
|
|
||||||
handleQuery()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 打开客户详情 */
|
|
||||||
const { currentRoute, push } = useRouter()
|
|
||||||
const openDetail = (id: number) => {
|
|
||||||
push({ name: 'CrmCustomerDetail', params: { id } })
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 导出按钮操作 */
|
|
||||||
const handleExport = async () => {
|
|
||||||
try {
|
|
||||||
// 导出的二次确认
|
|
||||||
await message.exportConfirm()
|
|
||||||
// 发起导出
|
|
||||||
exportLoading.value = true
|
|
||||||
const data = await CustomerApi.exportCustomer(queryParams.value)
|
|
||||||
download.excel(data, '客户公海.xls')
|
|
||||||
} catch {
|
|
||||||
} finally {
|
|
||||||
exportLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 监听路由变化更新列表 */
|
|
||||||
watch(
|
|
||||||
() => currentRoute.value,
|
|
||||||
() => {
|
|
||||||
getList()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
/** 初始化 **/
|
|
||||||
onMounted(() => {
|
|
||||||
getList()
|
|
||||||
})
|
|
||||||
</script>
|
|
|
@ -1,136 +0,0 @@
|
||||||
<template>
|
|
||||||
<!-- <doc-alert title="【客户】客户管理、公海客户" url="https://doc.iocoder.cn/crm/customer/" />
|
|
||||||
<doc-alert title="【通用】数据权限" url="https://doc.iocoder.cn/crm/permission/" /> -->
|
|
||||||
|
|
||||||
<ContentWrap>
|
|
||||||
<el-form
|
|
||||||
ref="formRef"
|
|
||||||
:model="formData"
|
|
||||||
:rules="formRules"
|
|
||||||
label-width="160px"
|
|
||||||
v-loading="formLoading"
|
|
||||||
>
|
|
||||||
<el-card shadow="never">
|
|
||||||
<!-- 操作 -->
|
|
||||||
<template #header>
|
|
||||||
<div class="flex items-center justify-between">
|
|
||||||
<CardTitle title="客户公海规则设置" />
|
|
||||||
<el-button
|
|
||||||
type="primary"
|
|
||||||
@click="onSubmit"
|
|
||||||
v-hasPermi="['crm:customer-pool-config:update']"
|
|
||||||
>
|
|
||||||
保存
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<!-- 表单 -->
|
|
||||||
<el-form-item label="客户公海规则设置" prop="enabled">
|
|
||||||
<el-radio-group v-model="formData.enabled" @change="changeEnable" class="ml-4">
|
|
||||||
<el-radio :label="false" size="large">不启用</el-radio>
|
|
||||||
<el-radio :label="true" size="large">启用</el-radio>
|
|
||||||
</el-radio-group>
|
|
||||||
</el-form-item>
|
|
||||||
<div v-if="formData.enabled">
|
|
||||||
<el-form-item>
|
|
||||||
<el-input-number class="mr-2" v-model="formData.contactExpireDays" />
|
|
||||||
天不跟进或
|
|
||||||
<el-input-number class="mx-2" v-model="formData.dealExpireDays" />
|
|
||||||
天未成交
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="提前提醒设置" prop="notifyEnabled">
|
|
||||||
<el-radio-group
|
|
||||||
v-model="formData.notifyEnabled"
|
|
||||||
@change="changeNotifyEnable"
|
|
||||||
class="ml-4"
|
|
||||||
>
|
|
||||||
<el-radio :label="false" size="large">不提醒</el-radio>
|
|
||||||
<el-radio :label="true" size="large">提醒</el-radio>
|
|
||||||
</el-radio-group>
|
|
||||||
</el-form-item>
|
|
||||||
<div v-if="formData.notifyEnabled">
|
|
||||||
<el-form-item>
|
|
||||||
提前 <el-input-number class="mx-2" v-model="formData.notifyDays" /> 天提醒
|
|
||||||
</el-form-item>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-card>
|
|
||||||
</el-form>
|
|
||||||
</ContentWrap>
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
import * as CustomerPoolConfigApi from '@/api/crm/customer/poolConfig'
|
|
||||||
import { CardTitle } from '@/components/Card'
|
|
||||||
|
|
||||||
defineOptions({ name: 'CrmCustomerPoolConfig' })
|
|
||||||
|
|
||||||
const message = useMessage() // 消息弹窗
|
|
||||||
const { t } = useI18n() // 国际化
|
|
||||||
|
|
||||||
const formLoading = ref(false)
|
|
||||||
const formData = ref({
|
|
||||||
enabled: false,
|
|
||||||
contactExpireDays: undefined,
|
|
||||||
dealExpireDays: undefined,
|
|
||||||
notifyEnabled: false,
|
|
||||||
notifyDays: undefined
|
|
||||||
})
|
|
||||||
const formRules = reactive({
|
|
||||||
enabled: [{ required: true, message: '是否启用客户公海不能为空', trigger: 'blur' }]
|
|
||||||
})
|
|
||||||
const formRef = ref() // 表单 Ref
|
|
||||||
|
|
||||||
/** 获取配置 */
|
|
||||||
const getConfig = async () => {
|
|
||||||
try {
|
|
||||||
formLoading.value = true
|
|
||||||
const data = await CustomerPoolConfigApi.getCustomerPoolConfig()
|
|
||||||
if (data === null) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
formData.value = data
|
|
||||||
} finally {
|
|
||||||
formLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 提交配置 */
|
|
||||||
const onSubmit = async () => {
|
|
||||||
// 校验表单
|
|
||||||
if (!formRef) return
|
|
||||||
const valid = await formRef.value.validate()
|
|
||||||
if (!valid) return
|
|
||||||
// 提交请求
|
|
||||||
formLoading.value = true
|
|
||||||
try {
|
|
||||||
const data = formData.value as CustomerPoolConfigApi.CustomerPoolConfigVO
|
|
||||||
await CustomerPoolConfigApi.saveCustomerPoolConfig(data)
|
|
||||||
message.success(t('common.updateSuccess'))
|
|
||||||
await getConfig()
|
|
||||||
formLoading.value = false
|
|
||||||
} finally {
|
|
||||||
formLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 更改客户公海规则设置 */
|
|
||||||
const changeEnable = () => {
|
|
||||||
if (!formData.value.enabled) {
|
|
||||||
formData.value.contactExpireDays = undefined
|
|
||||||
formData.value.dealExpireDays = undefined
|
|
||||||
formData.value.notifyEnabled = false
|
|
||||||
formData.value.notifyDays = undefined
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 更改提前提醒设置 */
|
|
||||||
const changeNotifyEnable = () => {
|
|
||||||
if (!formData.value.notifyEnabled) {
|
|
||||||
formData.value.notifyDays = undefined
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
getConfig()
|
|
||||||
})
|
|
||||||
</script>
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user