【招生小程序】 新增# 电销:对接修改跟进、线索列表接口
parent
30d61360fa
commit
504d381eff
|
@ -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 })
|
||||
}
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -101,6 +101,7 @@ const onInput = e => {
|
|||
const onClear = () => {
|
||||
innerValue.value = ''
|
||||
emit('update:modelValue', '')
|
||||
emit('onInput')
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 }
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -290,6 +290,6 @@ page {
|
|||
}
|
||||
.u-tabbar {
|
||||
.u-tabbar__content {
|
||||
z-index: 99999 !important;
|
||||
z-index: 999 !important;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue