【招生小程序】 新增# 电销:对接修改跟进、线索列表接口

master
kaeery 2025-02-27 21:42:37 +08:00
parent 30d61360fa
commit 504d381eff
12 changed files with 196 additions and 77 deletions

View File

@ -32,3 +32,6 @@ export function apiEditRemark(params: any) {
export function apiEditClue(params: any) {
return request.post({ url: '/clue/edit', data: params })
}
export function apiTeleClueList(params: any) {
return request.get({ url: '/clue/telemarketingList', data: params })
}

View File

@ -2,10 +2,10 @@
<view class="bg-[#FAFAFE]">
<view class="bg-white px-[32rpx]">
<TForm ref="tForm" :model="form" :rules="rules" errorType="toast">
<TFormItem prop="publishName">
<TFormItem prop="recruitTeacherName">
<TInputField
v-model="form.publishName"
label="发布人"
v-model="form.recruitTeacherName"
label="电销老师"
placeholder=""
inputAlign="left"
readonly
@ -13,9 +13,9 @@
:labelWidth="120"
/>
</TFormItem>
<TFormItem prop="clientName">
<TFormItem prop="studentName">
<TInputField
v-model="form.clientName"
v-model="form.studentName"
label="客户姓名"
placeholder=""
inputAlign="left"
@ -24,9 +24,9 @@
:labelWidth="120"
/>
</TFormItem>
<TFormItem prop="mobile">
<TFormItem prop="phone">
<TInputField
v-model="form.mobile"
v-model="form.phone"
label="电话"
placeholder=""
inputAlign="left"
@ -35,9 +35,9 @@
:labelWidth="120"
/>
</TFormItem>
<TFormItem prop="desc">
<TFormItem prop="basicInformation">
<TTextareaField
v-model="form.desc"
v-model="form.basicInformation"
label="基本情况"
placeholder=""
autoHeight
@ -46,7 +46,7 @@
:labelWidth="120"
/>
</TFormItem>
<TFormItem prop="status">
<TFormItem prop="state">
<TInputField
v-model="parseStatusText"
label="状态"
@ -57,9 +57,9 @@
:labelWidth="120"
/>
</TFormItem>
<TFormItem prop="isComplete">
<TFormItem prop="isConversion">
<TSwitchField
v-model="form.isComplete"
v-model="form.isConversion"
label="是否转化成功"
:labelWidth="120"
/>
@ -78,7 +78,13 @@
</TForm>
</view>
<view class="px-[60rpx]">
<u-button class="btn" color="#0E66FB" shape="circle" @click="handleSubmit">
<u-button
class="btn"
color="#0E66FB"
shape="circle"
:loading="loading"
@click="handleSubmit"
>
提交
</u-button>
</view>
@ -87,49 +93,71 @@
<script setup lang="ts">
import { onLoad } from '@dcloudio/uni-app'
import { computed } from 'vue'
import { shallowRef } from 'vue'
import { ref } from 'vue'
import { ref, computed } from 'vue'
import { useClueDetail } from '@/hooks/useCommon'
import { optionsMap } from '@/config/options'
import { apiCompleteCluse } from '@/api/clue'
import { toast } from '@/utils/util'
const { fetchClueDetail, clueDetailInfo } = useClueDetail()
const id = ref('')
const tForm = ref()
const form = ref({
publishName: '张三',
clientName: '张三',
mobile: '18138952909',
desc: '学生爸爸接电话学生高三毕业300多分家长不清楚学生收到录取通知家长说学生不读书了我让家长先问问学生对未来的规划先和学生沟通一下家长同意我们加他微信发专业资料给他看看可以在微信上问问学生具体情况。推荐3+2给家长发一下学校简介和专业资料。',
status: 1,
isComplete: 0,
id: '',
recruitTeacherName: '',
studentName: '',
phone: '',
basicInformation: '',
state: 1,
isConversion: 0,
remark: ''
})
const rules = {
isComplete: [{ required: true, message: '请选择是否转化成功' }]
isConversion: [{ required: true, message: '请选择是否转化成功' }]
}
const statusList = shallowRef([
{ label: '账号已添加', value: 1 },
{ label: '账号不存在', value: 2 },
{ label: '账号未通过', value: 3 }
])
const parseStatusText = computed(
() => statusList.value.find(item => item.value == form.value.status)?.label
() => optionsMap.stateOptions.find(item => item.value == form.value.state)?.label
)
onLoad(option => {
onLoad(async option => {
if (option?.id) {
id.value = option.id
await fetchClueDetail(id.value)
setFormData()
}
})
const loading = ref(false)
const handleSubmit = () => {
tForm.value
.validate()
.then(valid => {
.then(async valid => {
if (valid) {
console.log('校验通过', form.value)
loading.value = true
try {
const data = {
id: form.value.id,
isConversion: form.value.isConversion,
remark: form.value.remark
}
await apiCompleteCluse(data)
toast('转化完成')
uni.navigateBack()
uni.$emit('refreshPage')
} catch (error) {}
}
})
.catch(() => {})
}
const setFormData = () => {
for (const key in clueDetailInfo.value) {
if (Object.prototype.hasOwnProperty.call(form.value, key)) {
form.value[key] = clueDetailInfo.value[key]
}
}
}
</script>
<style scoped lang="scss">

View File

@ -2,57 +2,101 @@
<view class="p-[32rpx] bg-white">
<view class="flex justify-between items-center mb-[20rpx]">
<text class="text-[44rpx] font-bold">修改跟进信息</text>
<text class="text-[28rpx]">全部清</text>
<text class="text-[28rpx]" @click="handleClear"></text>
</view>
<TForm ref="tForm" :model="form" :rules="rules" errorType="toast">
<TFormItem prop="clientName">
<TFormItem prop="studentName">
<TInputField
v-model="form.clientName"
v-model="form.studentName"
label="客户姓名"
placeholder="请填写客户姓名(必填)"
inputAlign="left"
/>
</TFormItem>
<TFormItem prop="mobile">
<TFormItem prop="phone">
<TInputField
v-model="form.mobile"
v-model="form.phone"
label="电话"
placeholder="请填写电话(必填)"
inputAlign="left"
/>
</TFormItem>
<TFormItem prop="desc">
<TTextareaField label="基本情况" placeholder="请填写基本情况(必填)" autoHeight />
<TFormItem prop="basicInformation">
<TTextareaField
v-model="form.basicInformation"
label="基本情况"
placeholder="请填写基本情况(必填)"
autoHeight
/>
</TFormItem>
</TForm>
<u-button class="btn" color="#0E66FB" shape="circle" @click="handleConfirm"></u-button>
<u-button
class="btn"
color="#0E66FB"
shape="circle"
:loading="loading"
@click="handleConfirm"
>
确认
</u-button>
</view>
</template>
<script setup lang="ts">
import { onLoad } from '@dcloudio/uni-app'
import { ref } from 'vue'
import { useClueDetail } from '@/hooks/useCommon'
import { apiEditClue } from '@/api/clue'
import { toast } from '@/utils/util'
const { fetchClueDetail, clueDetailInfo } = useClueDetail()
const tForm = ref()
const form = ref({
clientName: '',
mobile: '',
desc: ''
id: '',
studentName: '',
phone: '',
basicInformation: ''
})
const rules = {
clientName: [{ required: true, message: '请填写客户姓名', trigger: 'blur' }],
mobile: [{ required: true, message: '请填写电话', trigger: 'blur' }],
desc: [{ required: true, message: '请填写基本情况', trigger: 'blur' }]
studentName: [{ required: true, message: '请填写客户姓名', trigger: 'blur' }],
phone: [{ required: true, message: '请填写电话', trigger: 'blur' }],
basicInformation: [{ required: true, message: '请填写基本情况', trigger: 'blur' }]
}
const loading = ref(false)
const handleConfirm = () => {
tForm.value
.validate()
.then(valid => {
.then(async valid => {
if (valid) {
console.log('校验通过')
loading.value = true
try {
await apiEditClue(form.value)
toast('修改成功')
uni.navigateBack()
uni.$emit('refreshPage')
} catch (error) {}
loading.value = false
}
})
.catch(() => {})
}
const handleClear = () => {
tForm.value.resetFields()
}
onLoad(async option => {
if (option?.id) {
await fetchClueDetail(option.id)
setFormData()
}
})
const setFormData = () => {
for (const key in clueDetailInfo.value) {
if (Object.prototype.hasOwnProperty.call(form.value, key)) {
form.value[key] = clueDetailInfo.value[key] as any
}
}
}
</script>
<style scoped lang="scss">

View File

@ -5,7 +5,7 @@
<TFormItem prop="recruitTeacherName">
<TInputField
v-model="form.recruitTeacherName"
label="发布人"
label="电销老师"
placeholder=""
inputAlign="left"
readonly
@ -50,7 +50,7 @@
<TMultiSelect
v-model="form.state"
label="状态"
:groupList="statusList"
:groupList="optionsMap.stateOptions"
:labelWidth="100"
/>
</TFormItem>
@ -64,20 +64,19 @@
:loading="loading"
@click="handleSubmit"
>
提交{{ form.state }}
提交
</u-button>
</view>
</view>
</template>
<script setup lang="ts">
import { apiAddCluseProgress, apiCluseEdit } from '@/api/clue'
import { stateEnum } from '@/enums'
import { useClueDetail } from '@/hooks/useCommon'
import { toast } from '@/utils/util'
import { onLoad } from '@dcloudio/uni-app'
import { shallowRef } from 'vue'
import { ref } from 'vue'
import { useClueDetail } from '@/hooks/useCommon'
import { apiAddCluseProgress } from '@/api/clue'
import { optionsMap } from '@/config/options'
import { toast } from '@/utils/util'
const { fetchClueDetail, clueDetailInfo } = useClueDetail()
@ -95,11 +94,6 @@ const form = ref({
const rules = {
state: [{ required: true, message: '请选择状态' }]
}
const statusList = shallowRef([
{ label: '账号已添加', value: stateEnum.ADD_RELATION },
{ label: '账号不存在', value: stateEnum.NO_EXIST },
{ label: '账号未通过', value: stateEnum.UN_PASS }
])
onLoad(async option => {
if (option?.id) {
id.value = option.id

View File

@ -101,6 +101,7 @@ const onInput = e => {
const onClear = () => {
innerValue.value = ''
emit('update:modelValue', '')
emit('onInput')
}
</script>

View File

@ -48,7 +48,7 @@ const props = defineProps({
}
})
const emit = defineEmits(['update:modelValue'])
const innerValue = ref('')
const innerValue = ref(props.modelValue)
const labelStyle = computed(() => {
const { labelWidth } = props
return {

View File

@ -40,7 +40,9 @@
<view
class="flex gap-[20rpx]"
v-if="
item.situation == converStatusEnum.CONVERTED_PROCESS && item.state !== null
(item.situation == converStatusEnum.ADD_RELATION ||
item.situation == converStatusEnum.EXCEPTION) &&
item.state !== null
"
>
<text class="text-muted w-[128rpx]">状态</text>
@ -54,8 +56,8 @@
"
>
<text class="text-muted w-[128rpx]">备注</text>
<view class="flex gap-[12rpx]">
<text class="flex-1 text-error">已交一部分定位金</text>
<view class="flex gap-[12rpx] flex-1">
<text class="text-error">{{ item.remark }}</text>
<view
class="flex gap-[4rpx] items-center text-primary text-[28rpx]"
@click="handleUpdateRemark"
@ -67,13 +69,14 @@
</view>
<view class="flex gap-[20rpx]" v-if="item.situation == converStatusEnum.CONVERTED">
<text class="text-muted w-[128rpx]">成交时间</text>
<text class="flex-1">2025-02-10 16:04:00</text>
<text class="flex-1">{{ item.updateTime }}</text>
</view>
</view>
</template>
<template #action>
<view
class="px-[32rpx] border-t border-solid border-[#F3F3F3] flex justify-end py-[12rpx]"
v-if="!showAction"
>
<view class="flex justify-end gap-[16rpx]">
<u-button
@ -128,6 +131,14 @@ const stateMap: Record<stateEnum, string> = {
[stateEnum.UN_PASS]: '账号未通过'
}
const parseStateText = computed(() => stateMap[props.item.state])
const showAction = computed(() => {
const { situation } = props.item
return (
situation == converStatusEnum.EXCEPTION ||
situation == converStatusEnum.CONVERTED ||
situation == converStatusEnum.FAILED
)
})
//
const handleGet = async () => {
const {

View File

@ -33,7 +33,7 @@
</view>
<view class="flex gap-[20rpx]" v-if="item.state !== null">
<text class="text-muted w-[128rpx]">状态</text>
<text class="flex-1">账号不存在</text>
<text class="flex-1 text-orage">{{ parseStateText }}</text>
</view>
</template>
<template #action>
@ -50,8 +50,7 @@
</template>
<script setup lang="ts">
import { PropType } from 'vue'
import { computed } from 'vue'
import { computed, PropType } from 'vue'
import { converStatusEnum, stateEnum } from '@/enums'
export interface IClue {
@ -69,6 +68,7 @@ export interface IClue {
recruitTeacherName: string //
recruitTeacherId: number
telemarketingTeacherId: number
updateTime: string
}
const props = defineProps({
item: {
@ -82,6 +82,13 @@ const ellipsisDesc = computed(
? item.basicInformation?.slice(0, 14) + '...'
: item.basicInformation
)
const stateMap: Record<stateEnum, string> = {
[stateEnum.ADD_RELATION]: '账号已添加',
[stateEnum.NO_EXIST]: '账号不存在',
[stateEnum.UN_PASS]: '账号未通过'
}
const parseStateText = computed(() => stateMap[props.item.state])
const handleEditFollow = () => {
const { item } = props
uni.navigateTo({

View File

@ -25,11 +25,12 @@
</template>
<script setup lang="ts">
import { useZPaging } from '@/hooks/useZPaging'
import { onLoad } from '@dcloudio/uni-app'
import { ref } from 'vue'
import clueCard from './clue-card.vue'
import { apiCluseList } from '@/api/clue'
import { debounce } from 'lodash-es'
import clueCard from './clue-card.vue'
import { useZPaging } from '@/hooks/useZPaging'
import { apiTeleClueList } from '@/api/clue'
const queryParams = ref({
likeWork: ''
@ -37,11 +38,17 @@ const queryParams = ref({
const dataList = ref([])
const { paging, queryList, refresh, changeApi, setParams } = useZPaging(
queryParams.value,
apiCluseList,
apiTeleClueList,
() => {}
)
const searchChange = debounce(() => {
refresh()
}, 300)
onLoad(() => {
uni.$on('refreshPage', () => {
refresh(queryParams.value)
})
})
</script>
<style scoped></style>

View File

@ -1,3 +1,4 @@
import { stateEnum } from '@/enums'
import { SexEnum } from '@/enums/appEnums'
import {
OrderStatusEnum,
@ -175,5 +176,11 @@ export const optionsMap = {
label: infoCheckMap[InfoCheckEnum.RETURN],
value: InfoCheckEnum.RETURN
}
],
stateOptions: [
{ label: '账号已添加', value: stateEnum.ADD_RELATION },
{ label: '账号不存在', value: stateEnum.NO_EXIST },
{ label: '账号未通过', value: stateEnum.UN_PASS }
]
}

View File

@ -5,6 +5,7 @@
placeholder="搜索客户姓名/手机号码"
backgroundColor="#F5F5F5"
showBorder
@on-input="searchChange"
/>
<u-tabs
:list="tabs"
@ -41,15 +42,16 @@
</template>
<script setup lang="ts">
import { apiOverhaulPagelist } from '@/api/overhaul'
import { useZPaging } from '@/hooks/useZPaging'
import { ref } from 'vue'
import clueCard from '@/components/widgets/recruitsale/clue-card.vue'
import { shallowRef } from 'vue'
import { computed } from 'vue'
import { apiCluseList } from '@/api/clue'
import { apiCluseList, apiEditRemark } from '@/api/clue'
import { converStatusEnum } from '@/enums'
import { onLoad } from '@dcloudio/uni-app'
import { debounce } from 'lodash-es'
import { toast } from '@/utils/util'
const tabs = shallowRef([
{ name: '待领取', value: converStatusEnum.UN_RECEIVED },
@ -77,16 +79,31 @@ const handleChangeTab = item => {
}
const popupShow = ref(false)
const contentText = ref('')
const clueId = ref('')
const handleUpdateRemark = item => {
const { id, remark } = item
popupShow.value = true
contentText.value = remark
clueId.value = id
}
const handleConfirm = async () => {
try {
const params = {
id: clueId.value,
remark: contentText.value
}
await apiEditRemark(params)
toast('修改备注成功')
refresh(queryParams.value)
} catch (error) {}
}
const handleConfirm = () => {}
onLoad(() => {
uni.$on('refreshPage', () => {
refresh(queryParams.value)
})
})
const searchChange = debounce(() => {
refresh()
}, 300)
</script>
<style scoped></style>

View File

@ -290,6 +290,6 @@ page {
}
.u-tabbar {
.u-tabbar__content {
z-index: 99999 !important;
z-index: 999 !important;
}
}