Browse Source

no message

master
xpz2018 8 months ago
parent
commit
df051dc5e8
11 changed files with 1008 additions and 13 deletions
  1. 10
      src/router/menu.ts
  2. 10
      src/router/routes/modules/invite.ts
  3. 2
      src/views/components/Profile.vue
  4. 2
      src/views/invite/index/index.vue
  5. 443
      src/views/invite/inviteInfo/data.ts
  6. 468
      src/views/invite/inviteInfo/index.vue
  7. 64
      src/views/invite/inviteInfo/modal.vue
  8. 2
      src/views/invite/list/index.vue
  9. 2
      src/views/invite/myList/index.vue
  10. 2
      src/views/invite/seasList/index.vue
  11. 16
      src/views/market/appointment/index.vue

10
src/router/menu.ts

@ -123,6 +123,16 @@ const inviteMenu: Menu = {
title: 'routes.invite.seasList', title: 'routes.invite.seasList',
}, },
}, },
,
{
path: 'inviteInfo',
name: 'InviteInfo',
component: '/invite/inviteInfo/index.vue',
meta: {
title: 'routes.clue.customer',
hideMenu: true,
},
},
], ],
} }

10
src/router/routes/modules/invite.ts

@ -47,6 +47,16 @@ const inviteRoute: AppRouteModule = {
title: t('routes.invite.seasList'), title: t('routes.invite.seasList'),
}, },
}, },
{
path: 'inviteInfo',
name: 'InviteInfo',
component: () => import('/src/views/invite/inviteInfo/index.vue'),
meta: {
// roles: ['/staff/staff'],
title: t('routes.clue.customer'),
hideMenu: true,
},
},
], ],
} }

2
src/views/components/Profile.vue

@ -1,7 +1,7 @@
<template> <template>
<div class="flex-row-center-start" style="padding-left: 12px;"> <div class="flex-row-center-start" style="padding-left: 12px;">
<div class="flex-col-center-center" style="min-width: 100px;"> <div class="flex-col-center-center" style="min-width: 100px;">
<Avatar :src="info.profilePhoto || 'https://dating-agency-prod.oss-cn-shenzhen.aliyuncs.com/827036501B11.png'" :size="64" />
<Avatar :src="info.profilePhoto || `https://dating-agency-prod.oss-cn-shenzhen.aliyuncs.com/${info.genderCode == 1 ? 'BB25BB9200BB' : '897B0B201483'}.png`" :size="64" />
<div style="background-color: #FAA19D;border-radius: 24px;padding: 2px 12px;margin-top: 8px;" v-if="info.cityName"> <div style="background-color: #FAA19D;border-radius: 24px;padding: 2px 12px;margin-top: 8px;" v-if="info.cityName">
<span style="font-size: 12px;color: #fff;">{{info.provinceName}}-{{info.cityName}}</span> <span style="font-size: 12px;color: #fff;">{{info.provinceName}}-{{info.cityName}}</span>
</div> </div>

2
src/views/invite/index/index.vue

@ -161,7 +161,7 @@
const { id } = record const { id } = record
router.push({ router.push({
query: { id }, query: { id },
path: '/clue/customer',
path: '/invite/inviteInfo',
}) })
} }
const datingClueIdList = ref<string[]>([]); const datingClueIdList = ref<string[]>([]);

443
src/views/invite/inviteInfo/data.ts

@ -0,0 +1,443 @@
import { useAddressData } from '/@/hooks/common'
import { ref } from 'vue'
import dayjs, { Dayjs } from 'dayjs'
import { FormSchema } from '/@/components/Form'
import { genderList, channelList, followStageList, paymentList, marriageList, storeAppointmentStatus } from '/@/enums/customerEnum'
import {
getIncomeList,
getNationList,
getEducationList,
getFamilyTiesList,
getOccupationList,
getBodilyFormList,
getPropertyPermits,
getAccountTypeList,
getIdentityTypeList,
getMaritalStatusList,
getConstellationList,
getCarPurchaseSituation,
} from '/@/api/essentialData'
import { incomeList } from '/@/enums/customerEnum'
import { getStoreTeam } from '/@/api/clue'
// 基础信息的额外数据
export const basicInfoMoreData = ref<any>({})
// 获取地区数据
const { addressList, domicilePlaceList } = useAddressData()
// 获取职业列表
export const occupationList = ref<any>([])
getOccupationList().then((res) => {
handleOccupationList(res)
occupationList.value = res || []
})
function handleOccupationList(data: any, ifFirst = true) {
data?.forEach?.((item: any) => {
const { industry, industryCode, occupation, occupationCode, occupationList } = item
item.label = ifFirst ? industry : occupation
item.value = ifFirst ? industryCode : occupationCode
if (occupationList?.length) {
item.children = occupationList
handleOccupationList(occupationList, false)
}
})
}
// 基本信息
export const basicSchema: FormSchema[] = [
{
field: 'genderCode',
label: '性别',
component: 'Select',
colProps: { span: 8 },
componentProps: ({ formModel }) => {
return {
options: genderList,
disabled: !!formModel.name && formModel.name !== -1,
onChange: (_: any, v: any) => {
basicInfoMoreData.value.genderValue = v?.label
},
}
},
},
{
field: 'channelType',
label: '渠道来源',
defaultValue: 1,
colProps: { span: 8 },
component: 'Select',
componentProps: {
options: channelList,
},
},
{ field: 'age', label: '年龄', component: 'InputNumber', colProps: { span: 8 }, },
{ field: 'height', label: '身高(cm)', component: 'InputNumber', colProps: { span: 8 }, },
{ field: 'weight', label: '体重(kg)', component: 'InputNumber', colProps: { span: 8 }, },
{
field: 'educationCode',
label: '学历',
component: 'ApiSelect',
colProps: { span: 8 },
componentProps: {
labelField: 'desc',
api: getEducationList,
getPopupContainer: () => document.body,
onChange: (_: any, v: any) => {
basicInfoMoreData.value.education = v?.label
},
},
},
{
field: 'incomeCode',
label: '月收入',
component: 'ApiSelect',
colProps: { span: 8 },
componentProps: {
labelField: 'desc',
api: getIncomeList,
getPopupContainer: () => document.body,
onChange: (_: any, v: any) => {
basicInfoMoreData.value.income = v?.label
},
},
},
{
field: 'maritalStatusCode',
label: '婚姻状况',
component: 'ApiSelect',
colProps: { span: 8 },
componentProps: {
labelField: 'desc',
api: getMaritalStatusList,
getPopupContainer: () => document.body,
onChange: (_: any, v: any) => {
basicInfoMoreData.value.maritalStatusName = v?.label
},
},
},
// { field: 'name', label: '姓名', component: 'Input', componentProps: { disabled: true }, colProps: { span: 8 }, },
{
field: 'constellationCode',
label: '星座',
component: 'ApiSelect',
colProps: { span: 8 },
componentProps: {
labelField: 'desc',
api: getConstellationList,
getPopupContainer: () => document.body,
onChange: (_: any, v: any) => {
basicInfoMoreData.value.constellation = v?.label
},
},
},
{ field: 'childrenNum', label: '孩子数量', component: 'InputNumber', colProps: { span: 8 }, },
{
field: 'address',
label: '居住地',
component: 'Cascader',
colProps: { span: 8 },
componentProps: {
options: addressList,
onChange: (_: any, v: any) => {
basicInfoMoreData.value.provinceName = v?.[0]?.label
basicInfoMoreData.value.cityName = v?.[1]?.label
basicInfoMoreData.value.districtName = v?.[2]?.label
},
},
},
// {
// field: 'domicilePlace',
// label: '户口所在地',
// component: 'Cascader',
// colProps: { span: 8 },
// componentProps: () => {
// return {
// options: domicilePlaceList.value,
// onChange: (_: any, v: any) => {
// basicInfoMoreData.value.domicilePlaceProvinceName = v?.[0]?.label
// basicInfoMoreData.value.domicilePlaceCityName = v?.[1]?.label
// },
// }
// },
// },
{
field: 'hometown',
label: '家乡',
component: 'Cascader',
colProps: { span: 8 },
componentProps: () => {
return {
options: domicilePlaceList.value,
onChange: (_: any, v: any) => {
basicInfoMoreData.value.hometownProvinceName = v?.[0]?.label
basicInfoMoreData.value.hometownCityName = v?.[1]?.label
},
}
},
},
// {
// field: 'identityType',
// label: '身份',
// component: 'ApiSelect',
// colProps: { span: 8 },
// componentProps: {
// labelField: 'desc',
// api: getIdentityTypeList,
// getPopupContainer: () => document.body,
// onChange: (_: any, v: any) => {
// basicInfoMoreData.value.identityTypeName = v?.label
// },
// },
// },
// {
// field: 'nationCode',
// label: '民族',
// component: 'ApiSelect',
// colProps: { span: 8 },
// componentProps: {
// labelField: 'cn',
// valueField: 'id',
// api: getNationList,
// getPopupContainer: () => document.body,
// onChange: (_: any, v: any) => {
// basicInfoMoreData.value.nation = v?.label
// },
// },
// },
// {
// field: 'bodilyFormCode',
// label: '体型',
// component: 'ApiSelect',
// colProps: { span: 8 },
// componentProps: {
// labelField: 'desc',
// api: getBodilyFormList,
// getPopupContainer: () => document.body,
// onChange: (_: any, v: any) => {
// basicInfoMoreData.value.bodilyForm = v?.label
// },
// },
// },
// {
// field: 'accountTypeCode',
// label: '户口',
// component: 'ApiSelect',
// colProps: { span: 8 },
// componentProps: {
// labelField: 'desc',
// api: getAccountTypeList,
// getPopupContainer: () => document.body,
// onChange: (_: any, v: any) => {
// basicInfoMoreData.value.accountTypeName = v?.label
// },
// },
// },
{
field: 'nativePlaceCode',
label: '籍贯',
component: 'Select',
colProps: { span: 8 },
componentProps: {
options: domicilePlaceList,
getPopupContainer: () => document.body,
onChange: (_: any, v: any) => {
basicInfoMoreData.value.nativePlaceName = v?.label
},
},
},
{
label: '职业',
field: 'occupationList',
component: 'Cascader',
colProps: { span: 8 },
componentProps: {
options: occupationList,
getPopupContainer: () => document.body,
onChange: (_: any, v: any) => {
const industry = v?.[0] || {}
const occupation = v?.[1] || {}
basicInfoMoreData.value.industry = industry.label
basicInfoMoreData.value.industryCode = industry.value
basicInfoMoreData.value.occupation = occupation.label
basicInfoMoreData.value.occupationCode = occupation.value
},
},
},
// {
// field: 'onlyChild',
// label: '是否独生子女',
// component: 'Select',
// colProps: { span: 8 },
// componentProps: {
// options: [
// { label: '是', value: 1 },
// { label: '否', value: 0 },
// ],
// },
// },
{
field: 'housePurchase',
label: '购房情况',
component: 'Select',
colProps: { span: 8 },
componentProps: {
options: [
{ label: '已购房', value: 1 },
{ label: '未购房', value: 0 },
],
},
},
{
field: 'carPurchase',
label: '购车情况',
component: 'Select',
colProps: { span: 8 },
componentProps: {
options: [
{ label: '已购车', value: 1 },
{ label: '未购车', value: 0 },
],
},
},
{
field: 'paymentIntention',
label: '付费意愿',
colProps: { span: 8 },
component: 'Select',
componentProps: {
options: paymentList
},
},
{
field: 'marriageIntention',
label: '结婚意愿',
colProps: { span: 8 },
component: 'Select',
componentProps: {
options: marriageList
},
},
{
field: 'remark',
label: '个人描述',
component: 'InputTextArea',
colProps: { span: 24 },
componentProps: ({ formModel }) => {
return {
autoSize: {
minRows: 4,
},
disabled: formModel?.describeAudit,
}
},
},
{ field: 'empty1', label: '', slot: 'empty1', component: 'Input' },
]
export const modalFormSchema: FormSchema[] = [
// {
// field: 'datingClueFollowType',
// label: '线索跟进方式',
// component: 'Select',
// required: true,
// componentProps: {
// options: followTypeList,
// },
// },
{
field: 'datingClueFollowStage',
label: '线索跟进阶段',
required: true,
component: 'Select',
componentProps: {
options: followStageList,
},
},
// {
// label: '下次跟进时间',
// component: 'DatePicker',
// field: 'nextFollowTime',
// componentProps: {
// ...dateComProps,
// },
// },
{
field: 'remark',
label: '备注',
required: false,
component: 'InputTextArea',
componentProps: {
autosize: {
minRows: 4,
},
},
},
]
export const appointmentFormSchema: FormSchema[] = [
{
field: 'datingStoreOrgId',
label: '预约门店',
component: 'ApiSelect',
required: true,
componentProps: {
valueField: 'orgId',
labelField: 'orgName',
api: getStoreTeam,
},
},
{
label: '预约到店时间',
component: 'DatePicker',
field: 'appointmentTime',
required: true,
componentProps: {
showTime: true,
style: { width: '100%' },
},
},
{
field: 'remark',
label: '备注',
required: false,
component: 'InputTextArea',
componentProps: {
autosize: {
minRows: 4,
},
},
},
]
import { BasicColumn } from '/@/components/Table'
export const tableColumns: BasicColumn[] = [
// { width: 70, title: '性别', dataIndex: 'genderCode', customRender: ({ text }) => { return genderList.find((find) => find.value === text)?.label} },
{ title: '门店', dataIndex: 'datingStoreOrgName' },
// { width: 100, title: '渠道来源', dataIndex: 'channelType',
// customRender: ({ text }) => {
// return channelList.find((find) => find.value === text)?.label
// },
// },
{ title: '预约到店时间', dataIndex: 'appointmentTime' },
{ title: '到店状态', dataIndex: 'datingStoreAppointmentStatus',
customRender: ({ text }) => {
return storeAppointmentStatus.find((find) => find.value === text)?.label
},
},
{ title: '销售', dataIndex: 'salesConsultantName' },
{ title: '电邀人', dataIndex: 'telemarketerName' },
// { width: 100, title: '跟进状态', dataIndex: 'followStatus',
// customRender: ({ text }) => {
// return followStatusList.find((find) => find.value === text)?.label
// },
// },
{ title: '电邀团队', dataIndex: 'teleInvitationOrgName' },
// { width: 100, title: '跟进结果', dataIndex: 'validStatus',
// customRender: ({ text }) => {
// return validStatusList.find((find) => find.value === text)?.label
// },
// },
// { width: 160, title: '录入时间', dataIndex: 'creatTime' },
]

468
src/views/invite/inviteInfo/index.vue

@ -0,0 +1,468 @@
<template>
<div class="flex-col" style="height:100%;padding: 16px;" v-if="details">
<div class="flex-row" style="background: white;width: 100%;padding: 10px;">
<CropperAvatar :value="avatar" width="90px" @change="uploadAvatarAfter"/>
<div class="flex-col" style="flex: 1;margin-left: 10px;">
<div class="flex-row-center-start">
<span style="font-size: 18px;font-weight: bold;">{{details.nickName}}</span>
<!-- <Icon icon="ant-design:edit-twotone" size="18px" style="margin-left: 12px;color:#0960bd;"/> -->
<Popconfirm placement="bottom" icon="修改昵称" @confirm="handleNickname">
<template #title>
<Input placeholder="请输入昵称" v-model:value="nickName" style="margin-top: 12px;"/>
</template>
<Button type="link"><template #icon><Icon icon="ant-design:edit-twotone" size="16px" style="margin-left: 12px;color:#0960bd;"/></template></Button>
</Popconfirm>
<span style="color: #666;margin: 0 12px;">(ID{{details.id}} | 渠道来源{{channelList.find((find) => find.value === details.channelType)?.label}})</span>
<Tag color="pink">#{{followStageList.find((find) => find.value === details.followStatus)?.label}}</Tag>
</div>
<div class="flex-row-center-start" style="margin-top: 5px;">
<div class="flex-row-center-start" style="width: 240px;">
<span style="color: #666;">电话</span>
<span style="color: #333;font-weight: bold;">{{details.phone}}</span>
</div>
<div class="flex-row-center-start" style="width: 240px;">
<span style="color: #666;">微信</span>
<span style="color: #333;font-weight: bold;">{{details.weChatId || '--'}}</span>
</div>
<!-- <div class="flex-row-center-start" style="width: 240px;">
<span style="color: #666;">客户标签</span>
<Tag color="#f50" closable @close="onLabelClose">无效</Tag>
<Popconfirm placement="bottom" icon="添加标签" @confirm="handleLabel">
<template #title>
<Input placeholder="请输入标签" v-model:value="labelName" style="margin-top: 12px;"/>
</template>
<Button style="height: 22px;line-height: 22px;width: 24px;border: 1px solid #0960bd;">
<template #icon><Icon icon="ant-design:plus-outlined" size="14px" style="color:#0960bd;display: block;"/></template>
</Button>
</Popconfirm>
</div> -->
</div>
<div class="flex-row-center-start" style="margin-top: 5px;">
<div class="flex-row-center-start" style="width: 240px;">
<span style="color: #666;">下次跟进时间</span>
<span style="color: #333;">{{details.nextFollowTime || '--'}}</span>
</div>
<!-- <div class="flex-row-center-start" style="width: 240px;">
<span style="color: #666;">创建人</span>
<span style="color: #333;">{{details.creatorName || '--'}}</span>
</div> -->
<div class="flex-row-center-start" style="width: 240px;">
<span style="color: #666;">跟进人</span>
<span style="color: #333;">{{details.followName || '--'}}</span>
</div>
<div class="flex-row-center-end" style="flex:1;padding-right: 32px;">
<Button size="small" type="primary">查看下一个客户</Button>
</div>
</div>
</div>
</div>
<div class="flex-col" style="background: white;width: 100%;margin-top: 16px;padding: 10px 40px;">
<div class="flex-row-center-space">
<div class="flex-row-center-start" style="flex: 1;">
<span style="color: #666;">签约销售</span>
<span style="color: #333;font-weight: bold;">--</span>
</div>
<div class="flex-row-center-start" style="flex: 1;">
<span style="color: #666;">服务红娘</span>
<span style="color: #333;font-weight: bold;">--</span>
</div>
<div class="flex-row-center-start" style="flex: 1;">
<span style="color: #666;">约见红娘</span>
<span style="color: #333;font-weight: bold;">--</span>
</div>
<div class="flex-row-center-start" style="flex: 1;">
<span style="color: #666;">店邀人</span>
<span style="color: #333;font-weight: bold;">--</span>
</div>
<div class="flex-row-center-start" style="flex: 1;">
<span style="color: #666;">核验人</span>
<span style="color: #333;font-weight: bold;">{{details.verifierName || '--'}}</span>
</div>
<div class="flex-row-center-start" style="flex: 1;">
<span style="color: #666;">录入人</span>
<span style="color: #333;font-weight: bold;">{{details.creatorName || '--'}}</span>
</div>
</div>
<div class="flex-row-center-space" style="margin-top: 4px;">
<div class="flex-row-center-start" style="flex: 1;">
<span style="color: #666;">签约时间</span>
<span style="color: #333;font-weight: bold;">--</span>
</div>
<div class="flex-row-center-start" style="flex: 1;">
<span style="color: #666;">服务次数</span>
<span style="color: #333;font-weight: bold;">--</span>
</div>
<div class="flex-row-center-start" style="flex: 1;">
<span style="color: #666;">约见次数</span>
<span style="color: #333;font-weight: bold;">--</span>
</div>
<div class="flex-row-center-start" style="flex: 1;">
<span style="color: #666;">店邀时间</span>
<span style="color: #333;font-weight: bold;">--</span>
</div>
<div class="flex-row-center-start" style="flex: 1;">
<span style="color: #666;">核验时间</span>
<span style="color: #333;font-weight: bold;">--</span>
</div>
<div class="flex-row-center-start" style="flex: 1;">
<span style="color: #666;">录入时间</span>
<span style="color: #333;font-weight: bold;">{{details.createTime || '--'}}</span>
</div>
</div>
</div>
<div class="flex-row" style="width: 100%;flex: 1;margin-top: 16px;">
<div style="flex: 1;background: white;padding: 0px 10px 10px 10px;">
<Tabs v-model:activeKey="activeKey" :animated="false" @change="onTabChange">
<TabPane key="1" tab="客户资料">
<div class="flex-col" style="flex:1;padding-top: 16px;">
<Tabs tab-position="left" v-model:activeKey="activeKey3" :animated="false" @change="onInfoChange">
<TabPane key="1" tab="个人资料">
<BasicForm @register="registerForm1" style="padding-right: 48px;"></BasicForm>
</TabPane>
<TabPane key="2" tab="择偶要求">
<BasicForm @register="registerForm3" style="padding-right: 48px;">
<template #age="{ model }">
<div class="flex-row-center-start">
<InputNumber placeholder="请输入" :min="0" v-model:value="model['minAge']" />
<span style="margin: 0 5px">-</span>
<InputNumber placeholder="请输入" :min="0" v-model:value="model['maxAge']" />
</div>
</template>
<template #height="{ model }">
<div class="flex-row-center-start">
<InputNumber placeholder="请输入" :min="0" v-model:value="model['minHeight']" />
<span style="margin: 0 5px">-</span>
<InputNumber placeholder="请输入" :min="0" v-model:value="model['maxHeight']" />
</div>
</template>
<template #income="{ model }">
<div class="flex-row-center-start">
<InputNumber placeholder="请输入" :min="0" v-model:value="model['minimumIncome']" />
<span style="margin: 0 5px">-</span>
<InputNumber placeholder="请输入" :min="0" v-model:value="model['maximumIncome']" />
</div>
</template>
</BasicForm>
</TabPane>
<TabPane key="3" tab="线索影像">
<div style="padding: 16px;">
<OssUpload v-model="uploadList" @remove="onUploadRemove" :multiple="true" :if-custom-render="true" />
</div>
</TabPane>
</Tabs>
</div>
</TabPane>
<TabPane key="2" tab="邀约进店">
<div style="height: 100%;width: 100%;position: relative;">
<!-- <Button shape="circle" type="primary" ghost style="position: absolute;right: 24rpx;bottom: 24rpx;">
<template #icon><Icon icon="ant-design:plus-outlined" size="16px" style="color: #0960bd;"/></template>
</Button> -->
<!-- <Table bordered :pagination="false" :columns="tableColumns" :dataSource="appointmentList">
</Table> -->
<BasicTable @register="registerTable">
<template #action="{ record }">
<TableAction
:actions="[
{
label: '取消',
color: 'error',
// onClick: handleCancel.bind(null, record['id']),
},
{
label: '编辑',
onClick: handleAppointment.bind(null, record),
},
{
label: '结算',
color: 'error',
ifShow: record['status'] === 5,
// onClick: handleSettle.bind(null, record['id']),
}
]"
/>
</template>
</BasicTable>
<!-- <Result subTitle="暂无数据"></Result> -->
</div>
</TabPane>
<TabPane key="3" tab="合同">
<Result subTitle="暂无数据"></Result>
</TabPane>
<TabPane key="4" tab="约见记录">
<Result subTitle="暂无数据"></Result>
</TabPane>
<template #tabBarExtraContent>
<Button type="link" @click="handleSubmit" v-if="activeKey == '1'">
<template #icon><Icon icon="ant-design:edit-twotone" size="16px" style="margin-right: -4px;"/></template>保存资料
</Button>
<Button type="link" @click="handleAppointment({datingClueId: details.id})" v-else-if="activeKey == '2'">
<template #icon>
<div style="display: inline;"><Icon icon="ant-design:plus-outlined" size="16px" style="margin-right: 2px;"/></div>
</template>
<span>邀约进店</span>
</Button>
</template>
</Tabs>
</div>
<div style="width: 400px;background: white;margin-left: 16px;padding: 0px 10px 10px 10px;" v-if="details.allocationStatus !== 1">
<Tabs v-model:activeKey="activeKey2" :animated="false" @change="onReportChange">
<TabPane key="1" tab="跟进小计">
<div class="flex-col-center-start" style="flex:1;padding-top: 16px;">
<BasicForm @register="registerForm"></BasicForm>
<div class="flex-row-center-end" style="width: 100%;padding-right: 40px;">
<Button type="primary" style="width: 120px;margin-top: 36px;" @click="handleReport">保存</Button>
</div>
</div>
</TabPane>
<TabPane key="2" tab="跟进记录" style="flex:1;padding: 16px 16px 0px 16px;">
<Spin :spinning="spinning">
<Timeline v-if="reportList.length">
<TimelineItem :color="followStageList.find((find) => find.value === item.datingClueFollowStage)?.color" v-for="(item, index) in reportList" :key="index">
<div class="flex-row-center-space">
<div class="flex-row-center-start">
<span style="color: #333;">{{item.operatorName || item.operator}}</span>
<span style="color: #999;margin-left: 8px;">{{item.createTime}}</span>
</div>
<span style="color: #333;">[{{followStageList.find((find) => find.value === item.datingClueFollowStage)?.label}}]</span>
</div>
<div class="flex-row-center-start" v-if="item.nextFollowTime">
<span style="color: #0960bd;">下次跟进时间{{item.nextFollowTime}}</span>
</div>
<div class="flex-row-center-start" v-if="item.remark">
<span style="color: #333;">备注{{item.remark || "--"}}</span>
</div>
</TimelineItem>
</Timeline>
<Result subTitle="暂无数据" v-else></Result>
</Spin>
</TabPane>
</Tabs>
</div>
</div>
<AppointmentModal @register="registerModal" @success="handleSuccess" />
</div>
</template>
<script setup lang="ts">
import { useRoute } from 'vue-router'
import { onMounted, ref, nextTick } from 'vue'
import { Tabs, Input, Popconfirm, Button, TabPane, Tag, Timeline, TimelineItem, Result, Spin, InputNumber, Table } from 'ant-design-vue'
import { Icon } from '/@/components/Icon'
import { useMessage } from '/@/hooks/web/useMessage'
import { CropperAvatar } from '/@/components/Cropper';
import { OssUpload } from '/@/components/OssUpload'
import BasicForm from '/@/components/Form/src/BasicForm.vue'
import { BasicTable, useTable, TableAction } from '/@/components/Table'
import { useForm } from '/@/components/Form'
import { formatToDateTime } from '/@/utils/dateUtil'
import { basicSchema, modalFormSchema, tableColumns } from './data'
import { useModal } from '/@/components/Modal'
import AppointmentModal from './modal.vue'
import { demandMarriageMoreData, demandMarriageSchema } from '/@/views/clue/components/data'
import { getClueInfo, editClueRecord, recordClueing, getClueRecord, getAppointmentList } from '/@/api/clue'
import { genderList, channelList, clueStatusList, followTypeList, followStageList } from '/@/enums/customerEnum'
const details = ref<any>()
const uploadList = ref<any>([])
onMounted(() => {
initFetch()
})
async function initFetch() {
demandMarriageMoreData.value = {}
const { query } = useRoute()
const { id } = query || {}
if (!id) return
details.value = await getClueInfo(`${id}`)
const { domicilePlaceProvinceCode, domicilePlaceCityCode } = details.value || {}
const domicilePlace = domicilePlaceCityCode && domicilePlaceProvinceCode ? [domicilePlaceProvinceCode, domicilePlaceCityCode] : []
const { provinceCode, cityCode, districtCode } = details.value || {}
const address = provinceCode && cityCode && districtCode ? [provinceCode, cityCode, districtCode] : []
avatar.value = details.value.profilePhoto
await nextTick()
await resetFields1()
await setFieldsValue1({ ...details.value, domicilePlace, address })
if(details.value.datingClueImageList && details.value.datingClueImageList.length){
uploadList.value = details.value.datingClueImageList.map((item: string) => {
return {url: item}
})
}
activeKey.value = '1'
}
async function onInfoChange(){
if(activeKey3.value == '2' && details.value.datingClueDemand){
await nextTick()
const { domicilePlaceProvinceCode, domicilePlaceCityCode } = details.value.datingClueDemand || {}
const domicilePlace = domicilePlaceCityCode && domicilePlaceProvinceCode ? [domicilePlaceProvinceCode, domicilePlaceCityCode] : []
const { provinceCode, cityCode, districtCode } = details.value.datingClueDemand || {}
const address = provinceCode && cityCode && districtCode ? [provinceCode, cityCode, districtCode] : []
await setFieldsValue3({ ...details.value.datingClueDemand, domicilePlace, address })
}
}
function onTabChange(){
if(activeKey.value == '2'){
reloadTable()
}
}
async function reloadTable(){
const dataSource= await getAppointmentList({datingClueId: details.value.id})
setTableData(dataSource)
}
const activeKey = ref<string>('1')
const activeKey2 = ref<string>('1')
const activeKey3 = ref<string>('1')
const avatar = ref<string>('')
const nickName = ref<string>('')
const labelName = ref<string>('')
async function handleNickname(){
if(!nickName.value){
createMessage.warning('请输入昵称')
return
}
await editClueRecord({id: details.value.id, nickName: nickName.value, phone: details.value.phone, weChatId: details.value.weChatId})
createMessage.success('修改昵称成功')
details.value.nickName = nickName.value
nickName.value = ''
}
async function onLabelClose(id){
}
async function handleLabel(){
if(!labelName.value){
createMessage.warning('请输入标签')
return
}
// await editClueRecord({id: details.value.id, nickName: nickName.value})
// createMessage.success('')
// details.value.nickName = nickName.value
labelName.value = ''
}
//
const [registerForm1, { validate: validate1, resetFields: resetFields1, getFieldsValue: getFieldsValue1, setFieldsValue: setFieldsValue1 }] = useForm({
labelWidth: 120,
schemas: basicSchema,
baseColProps: { span: 22 },
showActionButtonGroup: false,
})
//
async function uploadAvatarAfter(value) {
await editClueRecord({id: details.value.id, profilePhoto: value, phone: details.value.phone, weChatId: details.value.weChatId})
details.value.profilePhoto = value
}
function onUploadRemove({file, index}){
uploadList.value.splice(index, 1)
}
//
const [registerForm3, { getFieldsValue: getFieldsValue3, setFieldsValue: setFieldsValue3 }] =
useForm({
labelWidth: 120,
schemas: demandMarriageSchema,
baseColProps: { span: 22 },
showActionButtonGroup: false,
})
async function handleSubmit() {
await validate1()
const baseInfo: any = getFieldsValue1()
const address1 = baseInfo.address
baseInfo.provinceCode = address1?.[0]
baseInfo.cityCode = address1?.[1]
baseInfo.districtCode = address1?.[2]
const domicilePlace1 = baseInfo.domicilePlace
baseInfo.domicilePlaceProvinceCode = domicilePlace1?.[0]
baseInfo.phone = details.value.phone || ''
baseInfo.weChatId = details.value.weChatId || ''
const demandInfo: any = getFieldsValue3()
const { domicilePlace, address } = demandInfo || {}
baseInfo.datingClueDemand = {
...demandInfo,
...demandMarriageMoreData.value,
cityCode: address?.[1],
provinceCode: address?.[0],
districtCode: address?.[2],
domicilePlaceCityCode: domicilePlace?.[1],
domicilePlaceProvinceCode: domicilePlace?.[0]
}
const datingClueImageList = uploadList.value.map((item: any) => {
if(item.response && item.response.url){
return item.response.url
}
return item.url
})
await editClueRecord({id: details.value.id, ...baseInfo, datingClueImageList})
createMessage.success('保存成功')
}
const { createMessage } = useMessage()
const [registerForm, { resetFields, validate, getFieldsValue }] =
useForm({
labelWidth: 120,
schemas: modalFormSchema,
baseColProps: { span: 22 },
showActionButtonGroup: false,
})
async function handleReport() {
await validate()
const values: any = getFieldsValue()
const { nextFollowTime } = values
values.nextFollowTime = formatToDateTime(nextFollowTime)
values.datingClueId = details.value.id
await recordClueing(values)
createMessage.success('提交成功')
resetFields()
}
const spinning = ref<boolean>(false)
const reportList = ref<any[]>([]);
async function onReportChange(){
if(activeKey2.value == '2'){
spinning.value = true
reportList.value = await getClueRecord(details.value.id)
spinning.value = false
}
}
// const appointmentList = ref<any[]>([]);
const [registerTable, { setTableData }] = useTable({
bordered: true,
immediate: false,
canResize: false,
pagination: false,
useSearchForm: false,
showIndexColumn: false,
showTableSetting: false,
columns: tableColumns,
actionColumn: {
width: 120,
title: '操作',
fixed: 'right',
dataIndex: 'action',
slots: { customRender: 'action' },
},
})
const [registerModal, { openModal, closeModal }] = useModal()
function handleAppointment(record) {
openModal(true, {record})
}
//
function handleSuccess() {
closeModal()
reloadTable()
initFetch()
}
</script>
<style scoped lang="less">
::v-deep .ant-input-number {
min-width: 90px;
width: 100% !important;
max-width: 100%;
}
</style>

64
src/views/invite/inviteInfo/modal.vue

@ -0,0 +1,64 @@
<template>
<BasicModal v-bind="$attrs" @ok="handleOk" @register="registerModal">
<BasicForm @register="registerForm" />
</BasicModal>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { appointmentFormSchema } from './data'
import { useMessage } from '/@/hooks/web/useMessage'
import { BasicForm, useForm } from '/@/components/Form'
import { BasicModal, useModalInner } from '/@/components/Modal'
import { createAppointment, editAppointment } from '/@/api/clue'
import { formatToDateTime } from '/@/utils/dateUtil'
const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({
labelWidth: 100,
schemas: appointmentFormSchema,
baseColProps: { span: 22 },
showActionButtonGroup: false,
})
const id = ref<string>('')
const datingClueId = ref<string>('')
const ifUpdate = ref<boolean>(false)
const [registerModal, { setModalProps }] = useModalInner(async (data) => {
const { record } = data
await resetFields()
id.value = record?.id
datingClueId.value = record?.datingClueId
ifUpdate.value = !!id.value
setModalProps({
minHeight: 50,
title: `预约到店`,
confirmLoading: false,
})
if (!!ifUpdate.value) {
await setFieldsValue({ ...record })
}
})
const { createMessage } = useMessage()
const emits = defineEmits(['success'])
async function handleOk() {
try {
const values = await validate()
setModalProps({ confirmLoading: true })
const { appointmentTime } = values
values.appointmentTime = formatToDateTime(appointmentTime)
if(id.value){
values.id = id.value
await editAppointment(values)
createMessage.success(`编辑成功!`)
} else {
values.datingClueId = datingClueId.value
await createAppointment(values)
createMessage.success(`邀约成功!`)
}
emits('success')
} finally {
setModalProps({ confirmLoading: false })
}
}
</script>

2
src/views/invite/list/index.vue

@ -167,7 +167,7 @@
const { id } = record const { id } = record
router.push({ router.push({
query: { id }, query: { id },
path: '/clue/customer',
path: '/invite/inviteInfo',
}) })
} }
const datingClueIdList = ref<string[]>([]); const datingClueIdList = ref<string[]>([]);

2
src/views/invite/myList/index.vue

@ -117,7 +117,7 @@
const { id } = record const { id } = record
router.push({ router.push({
query: { id }, query: { id },
path: '/clue/customer',
path: '/invite/inviteInfo',
}) })
} }

2
src/views/invite/seasList/index.vue

@ -108,7 +108,7 @@
const { id } = record const { id } = record
router.push({ router.push({
query: { id }, query: { id },
path: '/clue/customer',
path: '/invite/inviteInfo',
}) })
} }
const { createMessage } = useMessage() const { createMessage } = useMessage()

16
src/views/market/appointment/index.vue

@ -125,14 +125,14 @@
} }
} }
const router = useRouter()
function toDetail(record: any) {
const { id } = record
router.push({
query: { id },
path: '/clue/customer',
})
}
// const router = useRouter()
// function toDetail(record: any) {
// const { id } = record
// router.push({
// query: { id },
// path: '/clue/customer',
// })
// }
// modal // modal
const [appointmentModal, { openModal: openAppointmentModal, closeModal: closeAppointmentModal }] = useModal() const [appointmentModal, { openModal: openAppointmentModal, closeModal: closeAppointmentModal }] = useModal()

Loading…
Cancel
Save