CRM 场地管理详情

pull/853/head
wersd 2025-06-16 22:23:43 +08:00
parent ae637966a4
commit 3ee254dce0
7 changed files with 114 additions and 29 deletions

View File

@ -1,4 +1,5 @@
import request from '@/config/axios'
import { TransferReqVO } from '@/api/crm/permission'
// 场地信息 VO
export interface StationSiteVO {
@ -13,6 +14,8 @@ export interface StationSiteVO {
estimatedPiles: number // 可建充电桩数
siteType: number // 场地类型
siteStatus: number // 场地状态
ownerUserId: number // 责任人编号
ownerUserName?: string // 责任人名称
ownerId: number // 关联的业主方
ownerName: string // 业主方名称
managementId: number // 关联的物管方
@ -24,7 +27,6 @@ export interface StationSiteVO {
parkingSpaces: number // 车位数
rentPrice: number // 租金价格
creator: string // 填报人
creatorName?: string // 填报人名称
params: string // 参数
}
@ -58,5 +60,12 @@ export const StationSiteApi = {
// 导出场地信息 Excel
exportStationSite: async (params) => {
return await request.download({ url: `/crm/station-site/export-excel`, params })
},
}
}
// ======================= 业务操作 =======================
// 转移场地信息
export const transferStationSite = async (data: TransferReqVO) => {
return await request.put({ url: `/crm/station-site/transfer`, data })
}

View File

@ -32,10 +32,9 @@
<Icon icon="ep:refresh" class="mr-5px" />重置
</el-button>
<el-button v-if="queryParams.bizId !== undefined" v-hasPermi="['crm:follow-up-record:create']" @click="openForm('create')">
<Icon icon="ep:plus" class="mr-5px" />添加{{ bizLabel }}记录
<Icon icon="ep:plus" class="mr-5px" />添加{{ bizLabel }}
</el-button>
</el-form-item>
{{ queryParams }}
</el-form>
@ -48,7 +47,7 @@
placement="top"
>
<span>{{ bizLabel }}</span>{{ item.creatorName }}
<span v-if="Array.isArray(item.customer) && item.customer.length > 0 && item.customer[0].name" style="margin-left: 8px;">
<span v-if="queryParams.bizId !== undefined && Array.isArray(item.customer) && item.customer.length > 0 && item.customer[0].name" style="margin-left: 8px;">
客户名称{{ item.customer[0].name }}
</span>
@ -72,11 +71,11 @@
</el-link>
<span style="margin: 0 8px;"> </span>
</template>
<span class="meta-label">{{ bizLabel }}</span><dict-tag :type="DICT_TYPE.CRM_FOLLOW_UP_TYPE" :value="item.type" class="mr-10px" />
<span v-if="queryParams.bizType === BizTypeEnum.CRM_CUSTOMER" class="meta-label">{{ bizLabel }}</span><dict-tag :type="DICT_TYPE.CRM_FOLLOW_UP_TYPE" :value="item.type" class="mr-10px" />
</div>
<!-- 操作按钮统一放右上角 -->
<div class="followup-action-btns">
<el-button v-hasPermi="['crm:follow-up-record:update']" link class="followup-action-btn edit" @click="openForm('update')">
<el-button v-hasPermi="['crm:follow-up-record:update']" link class="followup-action-btn edit" @click="openForm('update', item.id)">
<Icon icon="ep:edit" class="mr-3px" /> 编辑
</el-button>
<el-button v-hasPermi="['crm:follow-up-record:delete']" link class="followup-action-btn delete" @click="handleDelete(item.id)">
@ -126,7 +125,7 @@
/>
<!-- 表单弹窗添加/修改 -->
<FollowUpRecordForm ref="formRef" @success="getList" :biz-label="bizLabel" />
<FollowUpRecordForm ref="formRef" @success="getList" />
</template>
<script lang="ts" setup>
@ -178,8 +177,8 @@ const getList = async () => {
/** 添加/修改操作 */
const formRef = ref<InstanceType<typeof FollowUpRecordForm>>()
//
const openForm = (type: 'create' | 'update') => {
formRef.value?.open(type, props.bizType, props.bizId)
const openForm = (type: 'create' | 'update', id?: number) => {
formRef.value?.open(type, props.bizType, props.bizId, id)
}
@ -198,11 +197,11 @@ const handleDelete = async (id: number) => {
}
const bizLabel = computed(() => {
switch (formRef.value?.bizType) {
switch (queryParams.bizType) {
case BizTypeEnum.CRM_CUSTOMER:
return '跟进'
case BizTypeEnum.CRM_STATION_SITE:
return '评审'
return '记录'
default:
return '跟进'
}

View File

@ -8,11 +8,10 @@
:rules="formRules"
label-width="120px"
>
{{ formData }}
<el-row>
<el-col :span="12">
<el-form-item :label="`${bizLabel}方式`" prop="type" :required="bizLabel !== '9'">
<el-select v-model="formData.type" :placeholder="`请选择${bizLabel}`" :clearable="bizLabel === '9'">
<el-form-item v-if="formData.bizType === BizTypeEnum.CRM_CUSTOMER" :label="`${bizLabel}方式`" prop="type">
<el-select v-model="formData.type" :placeholder="`请选择${bizLabel}`">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_FOLLOW_UP_TYPE)"
:key="dict.value"
@ -52,6 +51,7 @@ import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { FollowUpRecordApi, FollowUpRecordVO } from '@/api/crm/followup'
import { BizTypeEnum } from '@/api/crm/permission'
import * as ContactApi from '@/api/crm/contact'
import { F } from 'dist-test/assets/form-create-DBMZ6B7Y'
defineOptions({ name: 'FollowUpRecordForm' })
@ -63,6 +63,7 @@ const dialogTitle = ref('') // 弹窗的标题
const formLoading = ref(false) // 12
//
interface FollowUpFormData {
id?: number; // id
bizType?: number;
bizId?: number;
type?: number;
@ -75,6 +76,7 @@ interface FollowUpFormData {
contactIds?: number[];
}
const formData = ref<FollowUpFormData>({
id: undefined, // id
bizType: undefined,
bizId: undefined,
type: undefined,
@ -94,7 +96,7 @@ const bizLabel = computed(() => {
case BizTypeEnum.CRM_CUSTOMER:
return '跟进'
case BizTypeEnum.CRM_STATION_SITE:
return '评审'
return '记录'
default:
return '跟进'
}
@ -103,13 +105,13 @@ const bizLabel = computed(() => {
const formRules = reactive({
type: [
{
required: bizLabel.value !== '评审',
message: `${bizLabel.value}类型不能为空`,
required: computed(() => formData.value.bizType === BizTypeEnum.CRM_CUSTOMER),
message: `${bizLabel.value}方式不能为空`,
trigger: 'change'
}
],
content: [{ required: true, message: `${bizLabel.value}内容不能为空`, trigger: 'blur' }],
contactIds: [{ required: bizLabel.value !== '评审', message: '联系人不能为空', trigger: 'change' }]
contactIds: [{ required: computed(() => formData.value.bizType === BizTypeEnum.CRM_CUSTOMER ), message: `${bizLabel.value}人不能为空`, trigger: 'change' }]
})
const contactOptions = ref<ContactApi.ContactVO[]>([])
@ -132,21 +134,26 @@ watch(
)
/** 打开弹窗 */
const open = async (type: string, bizType: number, bizId: number) => {
const open = async (type: string, bizType: number, bizId: number, id?: number) => {
console.log(formData.value)
dialogVisible.value = true
resetForm()
formType.value = type
formData.value.bizType = bizType
formData.value.bizId = bizId
formData.value.id = id || undefined // idundefined
if (formType.value === 'update') {
if (bizId === undefined || bizId === null) {
message.warning(t('记录ID不能为空'))
return
}
if (id === undefined || id === null) {
message.warning(t('记录ID不能为空'))
return
}
formLoading.value = true
try {
const data = await FollowUpRecordApi.getFollowUpRecord(bizId)
const data = await FollowUpRecordApi.getFollowUpRecord(id)
Object.assign(formData.value, data)
} finally {
formLoading.value = false

View File

@ -50,6 +50,7 @@ import * as ClueApi from '@/api/crm/clue'
import * as ContactApi from '@/api/crm/contact'
import * as CustomerApi from '@/api/crm/customer'
import * as ContractApi from '@/api/crm/contract'
import * as StationSiteApi from '@/api/crm/stationsite'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { BizTypeEnum, PermissionLevelEnum, TransferReqVO } from '@/api/crm/permission'
@ -122,6 +123,8 @@ const transfer = async (data: TransferReqVO) => {
return await BusinessApi.transferBusiness(data)
case BizTypeEnum.CRM_CONTRACT:
return await ContractApi.transferContract(data)
case BizTypeEnum.CRM_STATION_SITE:
return await StationSiteApi.transferStationSite(data)
default:
message.error('【转移失败】没有转移接口')
throw new Error('【转移失败】没有转移接口')
@ -139,6 +142,8 @@ const getDialogTitle = () => {
return '商机转移'
case BizTypeEnum.CRM_CONTRACT:
return '合同转移'
case BizTypeEnum.CRM_STATION_SITE:
return '场地信息转移'
default:
return '转移'
}

View File

@ -8,6 +8,10 @@
</el-row>
</el-col>
</div>
<div>
<!-- 右上按钮 -->
<slot></slot>
</div>
</div>
</div>
<ContentWrap class="mt-10px">
@ -30,20 +34,24 @@
<el-descriptions-item label="物管方:">
{{ stationSite.managementName || '-' }}
</el-descriptions-item>
<el-descriptions-item label="责任人:">
{{ stationSite.ownerUserName || '-' }}
</el-descriptions-item>
<el-descriptions-item label="推荐人姓名:">
{{ stationSite.recommenderName || '-' }}
</el-descriptions-item>
<el-descriptions-item label="推荐人联系方式:">
{{ stationSite.recommenderContact || '-' }}
</el-descriptions-item>
</el-descriptions>
</ContentWrap>
</template>
<script lang="ts" setup>
import { DICT_TYPE } from '@/utils/dict'
import { StationSiteVO } from '@/api/crm/stationsite'
import { formatDate } from '@/utils/formatTime'
defineOptions({ name: 'CrmStationSiteDetailsHeader' })
defineProps<{

View File

@ -1,8 +1,20 @@
<template>
<StationSiteDetailsHeader :station-site="stationSite" :loading="loading" />
<StationSiteDetailsHeader :station-site="stationSite" :loading="loading">
<el-button
type="primary"
@click="openForm('update', stationSite.id)"
>
编辑
</el-button>
<el-button
@click="transfer"
>
转移
</el-button>
</StationSiteDetailsHeader>
<el-col>
<el-tabs>
<el-tab-pane label="评审记录"> <!-- v-if="checkPermi(['crm:follow-up-record:query'])" -->
<el-tab-pane label="动态记录"> <!--v-hasPermi="['crm:station-site:update']" v-hasPermi="['crm:station-site:transfor']" v-if="checkPermi(['crm:follow-up-record:query'])" -->
<FollowUpList :biz-id="stationSite.id" :biz-type="BizTypeEnum.CRM_STATION_SITE" />
</el-tab-pane>
</el-tabs>
@ -15,6 +27,12 @@
</el-tab-pane>
</el-tabs> -->
</el-col>
<!-- 表单弹窗添加/修改 -->
<StationSiteForm ref="formRef" @success="getStationSite" />
<CrmTransferForm ref="transferFormRef" :biz-type="BizTypeEnum.CRM_STATION_SITE" @success="close" />
</template>
<script lang="ts" setup>
import { useRoute } from 'vue-router'
@ -27,12 +45,13 @@ import { BizTypeEnum } from '@/api/crm/permission'
import { useTagsViewStore } from '@/store/modules/tagsView'
import { checkPermi } from "@/utils/permission"; //
import FollowUpList from '@/views/crm/followup/components/FollowUpList.vue'
import StationSiteForm from '../StationSiteForm.vue'
import CrmTransferForm from '@/views/crm/permission/components/TransferForm.vue'
const route = useRoute()
const stationSiteId = ref<number>()
const loading = ref(true)
const stationSite = ref<StationSiteVO>({} as StationSiteVO)
const message = useMessage() //
const fileUrl = ref() // URL
const { delView } = useTagsViewStore() //
@ -54,6 +73,15 @@ const getOperateLog = async () => {
logList.value = data.list
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}
/** 获取详情 */
const stationSite = ref<StationSiteVO>({} as StationSiteVO)
const getStationSite = async () => {
loading.value = true
try {
@ -66,6 +94,12 @@ const getStationSite = async () => {
}
}
/** 客户转移 */
const transferFormRef = ref<InstanceType<typeof CrmTransferForm>>() // ref
const transfer = () => {
transferFormRef.value?.open(Number(stationSiteId.value))
}
const close = () => {
delView(unref(currentRoute))
push({ name: 'CrmStaionSite' })

View File

@ -43,6 +43,23 @@
/>
</el-select>
</el-form-item>
<el-form-item label="责任人">
<el-select
v-model="queryParams.ownerUserId"
filterable
remote
clearable
placeholder="请选择"
style="width: 150px"
>
<el-option
v-for="user in userOptions"
:key="user.id"
:label="user.nickname"
:value="user.id"
/>
</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>
@ -94,7 +111,7 @@
<dict-tag :type="DICT_TYPE.CRM_STATION_SITE_STATUS" :value="scope.row.siteStatus" />
</template>
</el-table-column>
<el-table-column label="填报人" align="center" prop="creatorName" width="150px" />
<el-table-column label="责任人" align="center" prop="ownerUserName" width="150px" />
<el-table-column label="操作" fixed="right" align="center" min-width="150px">
<template #default="scope">
<el-button
@ -142,13 +159,12 @@
<script setup lang="ts">
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { StationSiteApi, StationSiteVO } from '@/api/crm/stationsite'
import StationSiteForm from './StationSiteForm.vue'
import * as AreaApi from '@/api/system/area'
import { defaultProps } from '@/utils/tree'
import { useUserStore } from '@/store/modules/user'
import * as UserApi from '@/api/system/user'
/** 场地信息 列表 */
defineOptions({ name: 'CrmStationSite' })
@ -164,9 +180,11 @@ const queryParams = reactive({
pageSize: 10,
name: undefined,
areaId: undefined,
ownerUserId: undefined,
siteStatus: undefined,
createTime: [],
})
const userOptions = ref<any[]>([])
const queryFormRef = ref() //
const exportLoading = ref(false) //
const areaList = ref([])
@ -267,7 +285,12 @@ watch(
}
)
onMounted(() => {
//
UserApi.getSimpleUserList().then(res => {
userOptions.value = res
})
getList()
AreaApi.getAreaTree().then(res => {
areaList.value = res