【招生平台用户端】 新增# 线索管理:详情处理
parent
8cf9f6e0ea
commit
04760b070f
|
@ -28,3 +28,36 @@ export const conversionMap = {
|
||||||
[converStatusEnum.CONVERTED]: '已成交',
|
[converStatusEnum.CONVERTED]: '已成交',
|
||||||
[converStatusEnum.FAILED]: '已战败'
|
[converStatusEnum.FAILED]: '已战败'
|
||||||
}
|
}
|
||||||
|
export const conversionOptions = [
|
||||||
|
{ label: '待领取', value: converStatusEnum.UN_RECEIVED },
|
||||||
|
{ label: '转化中', value: converStatusEnum.CONVERTED_PROCESS },
|
||||||
|
{ label: '已添加', value: converStatusEnum.ADD_RELATION },
|
||||||
|
{ label: '异常待处理', value: converStatusEnum.EXCEPTION },
|
||||||
|
{ label: '已成交', value: converStatusEnum.CONVERTED },
|
||||||
|
{ label: '已战败', value: converStatusEnum.FAILED }
|
||||||
|
]
|
||||||
|
|
||||||
|
export enum CluseSourceEnum {
|
||||||
|
OFFLINE_LIST = 0
|
||||||
|
}
|
||||||
|
export const ClueSourceMap = {
|
||||||
|
[CluseSourceEnum.OFFLINE_LIST]: '线下列表'
|
||||||
|
}
|
||||||
|
export enum stateEnum {
|
||||||
|
ADD_RELATION = 0, //账号已添加
|
||||||
|
NO_EXIST = 1, //账号不存在
|
||||||
|
UN_PASS = 2 //账号未通过
|
||||||
|
}
|
||||||
|
export const stateMap: Record<stateEnum, string> = {
|
||||||
|
[stateEnum.ADD_RELATION]: '账号已添加',
|
||||||
|
[stateEnum.NO_EXIST]: '账号不存在',
|
||||||
|
[stateEnum.UN_PASS]: '账号未通过'
|
||||||
|
}
|
||||||
|
export enum ConverSuccessEnum {
|
||||||
|
YES = 1,
|
||||||
|
NO = 0
|
||||||
|
}
|
||||||
|
export const conversitionMap: Record<ConverSuccessEnum, string> = {
|
||||||
|
[ConverSuccessEnum.YES]: '是',
|
||||||
|
[ConverSuccessEnum.NO]: '否'
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
--color-white: #ffffff;
|
--color-white: #ffffff;
|
||||||
--color-red: #d9001b;
|
--color-red: #d9001b;
|
||||||
--color-green: #00b42a;
|
--color-green: #00b42a;
|
||||||
|
--color-green2: #baf114;
|
||||||
--table-header-bg-color: #f8f8f8;
|
--table-header-bg-color: #f8f8f8;
|
||||||
--el-font-size-extra-large: 18px;
|
--el-font-size-extra-large: 18px;
|
||||||
--el-menu-base-level-padding: 16px;
|
--el-menu-base-level-padding: 16px;
|
||||||
|
@ -50,4 +51,5 @@
|
||||||
--el-box-shadow-light: 0px 0px 12px rgba(0, 0, 0, 0.12);
|
--el-box-shadow-light: 0px 0px 12px rgba(0, 0, 0, 0.12);
|
||||||
--el-box-shadow-lighter: 0px 0px 6px rgba(0, 0, 0, 0.12);
|
--el-box-shadow-lighter: 0px 0px 6px rgba(0, 0, 0, 0.12);
|
||||||
--el-box-shadow-dark: 0px 16px 48px 16px rgba(0, 0, 0, 0.08), 0px 12px 32px rgba(0, 0, 0, 0.12), 0px 8px 16px -8px rgba(0, 0, 0, 0.16);
|
--el-box-shadow-dark: 0px 16px 48px 16px rgba(0, 0, 0, 0.08), 0px 12px 32px rgba(0, 0, 0, 0.12), 0px 8px 16px -8px rgba(0, 0, 0, 0.16);
|
||||||
|
--color-purple: '#800080';
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,80 @@
|
||||||
<template>
|
<template>
|
||||||
<el-space direction="vertical" alignment="normal" :size="24" v-loading="loading">
|
<el-space style="width: 100%" direction="vertical" alignment="normal" :size="24" v-loading="loading">
|
||||||
<el-descriptions v-for="item in baseInfo" :key="item.title" :title="item.title" direction="vertical" :column="4" border>
|
<el-descriptions v-for="item in baseInfo" :key="item.title" :title="item.title" direction="vertical" :column="4" border>
|
||||||
<el-descriptions-item v-for="(itemy, indey) in item.data" :key="indey" :label="itemy.label">{{ itemy.value }}</el-descriptions-item>
|
<el-descriptions-item v-for="(itemy, indey) in item.data" :key="indey" :label="itemy.label">
|
||||||
|
<span v-if="itemy.custom">{{ formatValue(itemy.field, itemy.value) }}</span>
|
||||||
|
<span v-else>{{ itemy.value }}</span>
|
||||||
|
</el-descriptions-item>
|
||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
</el-space>
|
</el-space>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { clueDetail } from '@/api/clue'
|
||||||
|
import { ClueSourceMap, conversitionMap, stateMap } from '@/enums'
|
||||||
|
|
||||||
|
const optionsMap: Record<string, any> = {
|
||||||
|
listSource: ClueSourceMap,
|
||||||
|
state: stateMap,
|
||||||
|
isConversion: conversitionMap
|
||||||
|
}
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: {
|
modelValue: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'baseInfo'
|
default: 'baseInfo'
|
||||||
|
},
|
||||||
|
id: {
|
||||||
|
type: Number
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
const baseInfo = ref<any[]>([])
|
||||||
|
const formatValue = computed(() => (field: string, value: number) => optionsMap[field][value])
|
||||||
|
|
||||||
|
const fetchClueDetail = async () => {
|
||||||
|
const { id } = props
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const result = await clueDetail({ id })
|
||||||
|
assemData(result)
|
||||||
|
} catch (error) {}
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
// 组装数据格式
|
||||||
|
const assemData = (result: any) => {
|
||||||
|
type ClueBaseInfoKeys = keyof typeof clueBaseInfo
|
||||||
|
|
||||||
|
const clueBaseInfo = {
|
||||||
|
线索基本信息: { listSource: '线索来源', studentName: '学生名字', phone: '联系电话', basicInformation: '基本情况' },
|
||||||
|
电销信息: { telemarketingTeacherName: '负责人', telemarketingOrganization: '所属组织', state: '线索状态', followUpTime: '跟进时间' },
|
||||||
|
招生信息: {
|
||||||
|
recruitTeacherName: '负责人',
|
||||||
|
recruitOrganization: '所属组织',
|
||||||
|
state: '线索状态',
|
||||||
|
getTime: '领取时间',
|
||||||
|
isConversion: '是否转化成功',
|
||||||
|
remark: '备注',
|
||||||
|
updateTime: '成交时间'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Object.keys(clueBaseInfo).forEach((key: ClueBaseInfoKeys) => {
|
||||||
|
const data = clueBaseInfo[key]
|
||||||
|
const list = Object.keys(data).map(item => {
|
||||||
|
const value = result[item]
|
||||||
|
return {
|
||||||
|
label: data[item],
|
||||||
|
value,
|
||||||
|
custom: ['listSource', 'state', 'isConversion'].includes(item),
|
||||||
|
field: item
|
||||||
|
}
|
||||||
|
})
|
||||||
|
baseInfo.value.push({
|
||||||
|
title: key,
|
||||||
|
data: list
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
watch(
|
watch(
|
||||||
() => props.modelValue,
|
() => props.modelValue,
|
||||||
() => {
|
() => {
|
||||||
|
@ -21,49 +82,5 @@ watch(
|
||||||
},
|
},
|
||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
)
|
)
|
||||||
const fetchClueDetail = () => {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
} catch (error) {}
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
const baseInfo = ref([
|
|
||||||
{
|
|
||||||
title: '线索基本信息',
|
|
||||||
data: [
|
|
||||||
{ label: '线索提供者', value: '张三' },
|
|
||||||
{ label: '线索来源', value: '线下名单' },
|
|
||||||
{ label: '创建人', value: 'admin' },
|
|
||||||
{ label: '创建时间', value: '2025-02-12 11:30:00' },
|
|
||||||
{ label: '客户姓名', value: '韩梅梅' },
|
|
||||||
{ label: '联系电话', value: '18866668888' },
|
|
||||||
{
|
|
||||||
label: '基本情况',
|
|
||||||
value: '学生爸爸接电话,学生高三毕业,300多分,家长不清楚学生收到录取通知,家长说学生不读书了,我让家长先问问学生对未来的规划先和学生沟通一下,家长同意我们加他微信发专业资料给他看看,可以在微信上问问学生具体情况。推荐3+2,给家长发一下学校简介和专业资料。'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '电销信息',
|
|
||||||
data: [
|
|
||||||
{ label: '负责人', value: '张三' },
|
|
||||||
{ label: '所属组织', value: '广州团队-A组' },
|
|
||||||
{ label: '线索状态', value: '已添加' },
|
|
||||||
{ label: '跟进时间', value: '2025-02-12 11:30:00' }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '招生信息',
|
|
||||||
data: [
|
|
||||||
{ label: '负责人', value: '张三' },
|
|
||||||
{ label: '所属组织', value: '广州团队-A组' },
|
|
||||||
{ label: '领取时间', value: '2025-02-12 11:19:00' },
|
|
||||||
{ label: '状态', value: '账号已添加' },
|
|
||||||
{ label: '是否转化成功', value: '' },
|
|
||||||
{ label: '备注', value: '' },
|
|
||||||
{ label: '成交时间', value: '' }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
])
|
|
||||||
</script>
|
</script>
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<search-form v-model="queryParams" @reset-page="resetPage" @reset-params="resetParams" />
|
<search-form v-model="queryParams" @reset-page="resetPage" @reset-params="resetParams" />
|
||||||
<clue-list :loading="pager.loading" :tableData="pager.lists">
|
<clue-list :loading="pager.loading" :tableData="pager.lists">
|
||||||
<template #operation="{ row }">
|
<template #operation="{ row }">
|
||||||
<el-button type="primary" link @click="handleDetail(row)">详情</el-button>
|
<el-button type="primary" link @click="handleDetail(row.id)">详情</el-button>
|
||||||
</template>
|
</template>
|
||||||
</clue-list>
|
</clue-list>
|
||||||
</div>
|
</div>
|
||||||
|
@ -29,9 +29,10 @@ const { pager, getLists, resetPage, resetParams } = usePaging({
|
||||||
getLists()
|
getLists()
|
||||||
|
|
||||||
const clueDetailRef = ref()
|
const clueDetailRef = ref()
|
||||||
const handleDetail = () => {
|
const handleDetail = (id: number) => {
|
||||||
clueDetailRef.value.openDrawer({
|
clueDetailRef.value.openDrawer({
|
||||||
title: '线索详情'
|
title: '线索详情',
|
||||||
|
data: { id }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<ProDrawer ref="proDrawerRef">
|
<ProDrawer ref="proDrawerRef">
|
||||||
<el-tabs v-model="activeTab">
|
<el-tabs v-model="activeTab">
|
||||||
<el-tab-pane v-for="tab in tabs" :key="tab.name" :label="tab.label" :name="tab.name">
|
<el-tab-pane v-for="tab in tabs" :key="tab.name" :label="tab.label" :name="tab.name">
|
||||||
<component v-if="tab.name == activeTab" :is="tab.component" v-model="activeTab" />
|
<component v-if="tab.name == activeTab" :is="tab.component" v-model="activeTab" :id="clueId" />
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
</ProDrawer>
|
</ProDrawer>
|
||||||
|
@ -17,9 +17,11 @@ const proDrawerRef = ref<InstanceType<typeof ProDrawer>>()
|
||||||
const activeTab = ref('baseInfo')
|
const activeTab = ref('baseInfo')
|
||||||
const tabs = shallowRef([
|
const tabs = shallowRef([
|
||||||
{ label: '基础信息', name: 'baseInfo', component: baseIfno },
|
{ label: '基础信息', name: 'baseInfo', component: baseIfno },
|
||||||
{ label: '线索来源', name: 'clueRecord', component: clueRecord }
|
{ label: '线索记录', name: 'clueRecord', component: clueRecord }
|
||||||
])
|
])
|
||||||
|
const clueId = ref()
|
||||||
const openDrawer = (params: IParams) => {
|
const openDrawer = (params: IParams) => {
|
||||||
|
clueId.value = params.data.id
|
||||||
proDrawerRef.value?.openDrawer(params)
|
proDrawerRef.value?.openDrawer(params)
|
||||||
}
|
}
|
||||||
defineExpose({
|
defineExpose({
|
||||||
|
|
|
@ -5,7 +5,10 @@
|
||||||
<span>{{ parseClueSource(row.listSource) }}</span>
|
<span>{{ parseClueSource(row.listSource) }}</span>
|
||||||
</template>
|
</template>
|
||||||
<template #situation="{ row }">
|
<template #situation="{ row }">
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<span class="w-[6px] h-[6px] rounded-[3px]" :class="conversionStyle[row.situation]"></span>
|
||||||
<span>{{ parseConversion(row.situation) }}</span>
|
<span>{{ parseConversion(row.situation) }}</span>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #operation="{ row }">
|
<template #operation="{ row }">
|
||||||
<slot name="operation" :row="row" />
|
<slot name="operation" :row="row" />
|
||||||
|
@ -17,6 +20,14 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { converStatusEnum, conversionMap } from '@/enums'
|
import { converStatusEnum, conversionMap } from '@/enums'
|
||||||
|
|
||||||
|
const conversionStyle: Record<number, string> = {
|
||||||
|
[converStatusEnum.UN_RECEIVED]: 'bg-green2',
|
||||||
|
[converStatusEnum.CONVERTED_PROCESS]: 'bg-primary',
|
||||||
|
[converStatusEnum.ADD_RELATION]: 'bg-purple',
|
||||||
|
[converStatusEnum.EXCEPTION]: 'bg-error',
|
||||||
|
[converStatusEnum.CONVERTED]: 'bg-success',
|
||||||
|
[converStatusEnum.FAILED]: 'bg-error'
|
||||||
|
}
|
||||||
defineProps({
|
defineProps({
|
||||||
loading: {
|
loading: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
<el-card shadow="never" class="!border-none">
|
<el-card shadow="never" class="!border-none">
|
||||||
<el-form ref="formRef" class="mb-[-16px]" :model="modelValue" :inline="true">
|
<el-form ref="formRef" class="mb-[-16px]" :model="modelValue" :inline="true">
|
||||||
<el-form-item label="学生名字">
|
<el-form-item label="学生名字">
|
||||||
<el-input class="w-[280px]" placeholder="请输入" v-model="modelValue.name" clearable @keyup.enter="$emit('resetPage')" />
|
<el-input class="w-[280px]" placeholder="请输入" v-model="modelValue.likeWork" clearable @keyup.enter="$emit('resetPage')" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="转化情况">
|
<el-form-item label="转化情况">
|
||||||
<el-select class="w-[280px]" v-model="modelValue.conversionStatus">
|
<el-select class="w-[280px]" v-model="modelValue.situation">
|
||||||
<el-option v-for="option in conversionOptions" :key="option.value" :label="option.label" :value="option.value" />
|
<el-option v-for="option in conversionOptions" :key="option.value" :label="option.label" :value="option.value" />
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
@ -18,24 +18,17 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { conversionOptions } from '@/enums'
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
modelValue: {
|
modelValue: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({
|
default: () => ({
|
||||||
name: '',
|
likeWork: '',
|
||||||
conversionStatus: ''
|
situation: ''
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
defineEmits(['resetPage', 'resetParams'])
|
defineEmits(['resetPage', 'resetParams'])
|
||||||
const conversionOptions = ref([
|
|
||||||
{ label: '有意向', value: 1 },
|
|
||||||
{ label: '待领取', value: 2 },
|
|
||||||
{ label: '转化中', value: 3 },
|
|
||||||
{ label: '已添加', value: 4 },
|
|
||||||
{ label: '异常待处理', value: 5 },
|
|
||||||
{ label: '已成交', value: 6 },
|
|
||||||
{ label: '已战败', value: 7 }
|
|
||||||
])
|
|
||||||
</script>
|
</script>
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|
|
@ -36,7 +36,9 @@ module.exports = {
|
||||||
'fill-lighter': 'var(--el-fill-color-lighter)',
|
'fill-lighter': 'var(--el-fill-color-lighter)',
|
||||||
mask: 'var(--el-mask-color)',
|
mask: 'var(--el-mask-color)',
|
||||||
green: 'var(--color-green)',
|
green: 'var(--color-green)',
|
||||||
black2: 'var(--el-text-color-black2)'
|
green2: 'var(--color-green2)',
|
||||||
|
black2: 'var(--el-text-color-black2)',
|
||||||
|
purple: 'var(--color-purple)'
|
||||||
},
|
},
|
||||||
fontFamily: {
|
fontFamily: {
|
||||||
sans: ['PingFang SC', 'Arial', 'Hiragino Sans GB', 'Microsoft YaHei', 'sans-serif']
|
sans: ['PingFang SC', 'Arial', 'Hiragino Sans GB', 'Microsoft YaHei', 'sans-serif']
|
||||||
|
|
Loading…
Reference in New Issue