salary
qsh 9 months ago
parent a3657f86ab
commit 47a4498b8b
  1. 6
      src/api/clue/index.js
  2. 5
      src/views/Clue/Pool/Comp/DialogClue.vue
  3. 109
      src/views/Clue/Pool/Comp/DialogFollow.vue
  4. 57
      src/views/Clue/Pool/Comp/DrawerClue.vue
  5. 59
      src/views/Clue/Pool/Comp/follow.data.js
  6. 47
      src/views/Clue/Pool/index.vue

@ -30,11 +30,17 @@ export const deleteClue = async (id) => {
return await request.delete({ url: '/admin-api/crm/sch-clue/delete?id=' + id })
}
// 释放
export const releaseClue = async (data) => {
return await request.put({ url: '/admin-api/crm/sch-clue/public/save', data })
}
// 通用查询数量
export const getClueCount = async () => {
return await request.get({ url: '/admin-api/crm/sch-clue/get-clue-num' })
}
// 获取操作记录
export const getOpearateRecord = async (params) => {
return await request.get({ url: '/admin-api/crm/clue-operate-record/list', params })
}

@ -153,7 +153,8 @@ const rules = {
name: { required: true, message: '线索名称不可为空', trigger: 'blur' },
phone: { required: true, message: '联系方式不可为空', trigger: 'blur' },
source: { required: true, message: '线索来源不可为空', trigger: 'change' },
intentionState: { required: true, message: '意向状态不可为空', trigger: 'change' }
intentionState: { required: true, message: '意向状态不可为空', trigger: 'change' },
consultTime: { required: true, message: '咨询日期不可为空', trigger: 'change' }
}
const tabName = ref('info')
@ -193,7 +194,7 @@ const open = async (type, id) => {
}
} else {
followList.value = []
address.value = []
address.value = ''
defaultLatLng.value = {
lat: 31.86119,
lng: 117.283042

@ -1,14 +1,46 @@
<template>
<Dialog :title="dialogTitle" v-model="dialogVisible" width="1000px">
<div class="flex">
<Form
style="flex: 1"
ref="formRef"
v-loading="formLoading"
isCol
:rules="rules"
:schema="allSchemas.formSchema"
/>
<el-form style="flex: 1" :model="form" ref="formRef" :rules="rules" label-width="120px">
<el-row :gutter="20">
<el-col :span="12" :offset="0">
<el-form-item label="本次跟进时间" prop="operateTime">
<el-date-picker
v-model="form.operateTime"
type="datetime"
format="YYYY-MM-DD HH:mm"
valueFormat="YYYY-MM-DD HH:mm"
placeholder="本次跟进时间"
/>
</el-form-item>
</el-col>
<el-col :span="12" :offset="0">
<el-form-item label="下次跟进时间" prop="nextFollowTime">
<el-date-picker
v-model="form.nextFollowTime"
type="date"
format="YYYY-MM-DD"
valueFormat="YYYY-MM-DD"
placeholder="下次跟进时间"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24" :offset="0">
<el-form-item label="跟进内容" prop="content">
<el-input
v-model="form.content"
type="textarea"
:autosize="{ minRows: 4 }"
placeholder="请输入跟进内容"
clearable
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div class="ml-20px" style="width: 300px">
<el-input
v-model="keyword"
@ -31,38 +63,46 @@
<template #footer>
<span>
<el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" @click="handleSave"> </el-button>
<el-button :disabled="formLoading" type="primary" @click="handleSave"> </el-button>
</span>
</template>
</Dialog>
</template>
<script setup>
import { allSchemas, rules } from './follow.data'
<script setup name="DialogFollow">
import { createFollow } from '@/api/clue/followRecord'
const { t } = useI18n() //
const message = useMessage() //
const dialogVisible = ref(false) //
const dialogTitle = ref('') //
const formLoading = ref(false) // 12
const formType = ref('') // create - update -
const formRef = ref() // Ref
const open = async (type, info) => {
const form = ref({})
const rules = {
operateTime: { required: true, message: '本次跟进时间不可为空', trigger: 'change' },
nextFollowTime: { required: true, message: '下次跟进时间不可为空', trigger: 'change' },
content: { required: true, message: '跟进内容不可为空', trigger: 'blur' }
}
const open = async (clueId) => {
dialogVisible.value = true
dialogTitle.value = type == 'create' ? '新增跟进记录' : '修改跟进记录'
formType.value = type
//
if (info) {
formLoading.value = true
try {
const data = { ...info }
formRef.value.setValues(data)
} finally {
formLoading.value = false
}
}
dialogTitle.value = '新增跟进记录'
resetForm(clueId)
}
defineExpose({ open }) // open
function resetForm(clueId) {
form.value = {
clueId,
operateTime: undefined,
nextFollowTime: undefined,
content: undefined
}
}
const resultList = [
{
@ -263,9 +303,22 @@ function filterList() {
showList.value = resultList.filter((it) => it.question.includes(keyword.value))
}
function handleSave() {
console.log('保存成功')
dialogVisible.value = false
async function handleSave() {
//
if (!formRef.value) return
const valid = await formRef.value.validate()
if (!valid) return
//
formLoading.value = true
try {
await createFollow(form.value)
message.success(t('common.createSuccess'))
dialogVisible.value = false
//
emit('success')
} finally {
formLoading.value = false
}
}
</script>

@ -19,8 +19,8 @@
<el-tag type="success">{{ info.intentionState }}</el-tag>
</div>
<div>
<el-button type="primary" plain>修改</el-button>
<el-button type="danger" plain>删除</el-button>
<el-button type="primary" plain @click="handleUpdate">修改</el-button>
<el-button type="danger" plain @click="handleRemove">删除</el-button>
</div>
</div>
</el-skeleton>
@ -102,11 +102,11 @@
</el-tab-pane>
</el-tabs>
<!-- 新建编辑跟进信息 -->
<DialogFollow ref="followRef" />
<DialogFollow ref="followRef" @success="getFollowList" />
</el-drawer>
</template>
<script setup>
<script setup name="DrawerClue">
import { useAppStore } from '@/store/modules/app'
import * as ClueApi from '@/api/clue'
import * as FollowApi from '@/api/clue/followRecord'
@ -124,6 +124,8 @@ import FlagGreen from '@/assets/imgs/flag/flag_green.png'
import FlagBlue from '@/assets/imgs/flag/flag_blue.png'
import FlagBlack from '@/assets/imgs/flag/flag_black.png'
const message = useMessage() //
const { t } = useI18n() //
const appStore = useAppStore()
const show = ref(false)
@ -174,17 +176,21 @@ const operateRecordList = ref([])
const dialogMap = ref(null)
const aMap = ref(null)
function getFollowList() {
FollowApi.getFollowList({ clueId: id }).then((data) => {
followRecordList.value = data.map((item) => ({
operateUserName: item.operateUserName,
centent: item.centent,
operateDate: formatDate(item.operateTime),
followTime: formatDate(item.operateTime, 'YYYY-MM-DD HH:mm'),
nextFollowTime: formatDate(item.nextFollowTime)
}))
})
}
async function open(id) {
try {
FollowApi.getFollowList({ clueId: id }).then((data) => {
followRecordList.value = data.map((item) => ({
operateUserName: item.operateUserName,
centent: item.centent,
operateDate: formatDate(item.operateTime),
followTime: formatDate(item.operateTime, 'YYYY-MM-DD HH:mm'),
nextFollowTime: formatDate(item.nextFollowTime)
}))
})
getFollowList()
ClueApi.getOpearateRecord({ clueId: id }).then((data) => {
operateRecordList.value = data.map((item) => ({
operateUserName: item.operateUserName,
@ -300,13 +306,36 @@ defineExpose({
const followRef = ref()
function addFollow() {
followRef.value.open('create', null)
followRef.value.open(info.value.clueId)
}
function destroyMap() {
dialogMap.value = null
aMap.value = null
}
const emit = defineEmits(['update', 'getList'])
//
function handleUpdate() {
emit('update', info.value)
show.value = false
}
//
async function handleRemove() {
try {
//
await message.delConfirm()
//
await ClueApi.deleteClue(info.value.clueId)
message.success(t('common.delSuccess'))
//
emit('getList')
show.value = false
} catch (err) {
console.log(err)
}
}
</script>
<style lang="scss" scoped></style>

@ -1,59 +0,0 @@
import { dateFormatter } from '@/utils/formatTime'
// 表单校验
export const rules = reactive({
username: [required],
password: [required]
})
// CrudSchema:https://doc.iocoder.cn/vue3/crud-schema/
const crudSchemas = reactive([
{
label: '本次跟进时间',
field: 'createTime',
formatter: dateFormatter,
form: {
component: 'DatePicker',
componentProps: {
type: 'datetime',
format: 'YYYY-MM-DD HH:mm',
valueFormat: 'YYYY-MM-DD HH:mm',
placeholder: '本次跟进时间'
}
}
},
{
label: '下次跟进时间',
field: 'nextTime',
isForm: true,
formatter: dateFormatter,
detail: {
dateFormat: 'YYYY-MM-DD HH:mm'
},
form: {
component: 'DatePicker',
componentProps: {
type: 'datetime',
format: 'YYYY-MM-DD HH:mm',
valueFormat: 'YYYY-MM-DD HH:mm',
placeholder: '下次跟进时间'
}
}
},
{
label: '跟进内容',
field: 'remark',
isTable: true,
form: {
component: 'Input',
componentProps: {
type: 'textarea',
autosize: { minRows: 5, maxRows: 10 }
},
colProps: {
span: 24
}
}
}
])
export const { allSchemas } = useCrudSchemas(crudSchemas)

@ -110,7 +110,14 @@
>
登记
</el-button>
<el-button type="primary" link v-hasPermi="['clue:pool:release']"> 释放 </el-button>
<el-button
type="primary"
link
v-hasPermi="['clue:pool:release']"
@click="handleRelease(scope.row.clueId)"
>
释放
</el-button>
</template>
</el-table-column>
</SSTable>
@ -122,9 +129,15 @@
:schema="allSchemas.formSchema"
@sucess="getTableList"
/>
<DrawerClue v-if="!loading" ref="drawerRef" :schema="allSchemas.formSchema" />
<DrawerClue
v-if="!loading"
ref="drawerRef"
:schema="allSchemas.formSchema"
@get-list="getTableList"
@update="handleEdit"
/>
<DialogSuccess ref="successRef" />
<DialogFollow ref="followRef" />
<DialogFollow ref="followRef" @success="getTableList" />
</div>
</template>
@ -141,6 +154,8 @@ import { formatDate } from '@/utils/formatTime'
import * as ClueApi from '@/api/clue'
const message = useMessage() //
const searchRef = ref()
const queryType = ref('2')
@ -190,6 +205,7 @@ function resetQuery() {
async function getTableList() {
//
tableObject.value.loading = true
getSearchCount()
try {
const queryParams = await searchRef.value.getFormModel()
const params = {
@ -231,7 +247,7 @@ function handleDetail(row) {
}
function handleFollow(row) {
followRef.value.open('create', row)
followRef.value.open(row.clueId)
}
async function makeCall(phone) {
@ -242,12 +258,33 @@ async function makeCall(phone) {
function handleSuccess(row) {
successRef.value.open(row)
}
//
function handleRelease(id) {
message.prompt('请先输入释放原因', '是否确认释放线索?').then((res) => {
if (res.value) {
try {
ClueApi.releaseClue({
clueId: id,
publicClue: true,
discardReason: res.value
}).then(() => {
message.success('释放成功')
})
} finally {
getTableList()
}
} else {
message.info('请将释放原因填写完整!')
}
})
}
const userOptions = ref([])
onMounted(() => {
getUserOption().then((data) => {
userOptions.value = data
})
getSearchCount()
getCurdSchemas()
})
</script>

Loading…
Cancel
Save