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. 105
      src/views/Clue/Pool/Comp/DialogFollow.vue
  4. 43
      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 }) 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 () => { export const getClueCount = async () => {
return await request.get({ url: '/admin-api/crm/sch-clue/get-clue-num' }) return await request.get({ url: '/admin-api/crm/sch-clue/get-clue-num' })
} }
// 获取操作记录
export const getOpearateRecord = async (params) => { export const getOpearateRecord = async (params) => {
return await request.get({ url: '/admin-api/crm/clue-operate-record/list', 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' }, name: { required: true, message: '线索名称不可为空', trigger: 'blur' },
phone: { required: true, message: '联系方式不可为空', trigger: 'blur' }, phone: { required: true, message: '联系方式不可为空', trigger: 'blur' },
source: { required: true, message: '线索来源不可为空', trigger: 'change' }, 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') const tabName = ref('info')
@ -193,7 +194,7 @@ const open = async (type, id) => {
} }
} else { } else {
followList.value = [] followList.value = []
address.value = [] address.value = ''
defaultLatLng.value = { defaultLatLng.value = {
lat: 31.86119, lat: 31.86119,
lng: 117.283042 lng: 117.283042

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

@ -19,8 +19,8 @@
<el-tag type="success">{{ info.intentionState }}</el-tag> <el-tag type="success">{{ info.intentionState }}</el-tag>
</div> </div>
<div> <div>
<el-button type="primary" plain>修改</el-button> <el-button type="primary" plain @click="handleUpdate">修改</el-button>
<el-button type="danger" plain>删除</el-button> <el-button type="danger" plain @click="handleRemove">删除</el-button>
</div> </div>
</div> </div>
</el-skeleton> </el-skeleton>
@ -102,11 +102,11 @@
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
<!-- 新建编辑跟进信息 --> <!-- 新建编辑跟进信息 -->
<DialogFollow ref="followRef" /> <DialogFollow ref="followRef" @success="getFollowList" />
</el-drawer> </el-drawer>
</template> </template>
<script setup> <script setup name="DrawerClue">
import { useAppStore } from '@/store/modules/app' import { useAppStore } from '@/store/modules/app'
import * as ClueApi from '@/api/clue' import * as ClueApi from '@/api/clue'
import * as FollowApi from '@/api/clue/followRecord' 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 FlagBlue from '@/assets/imgs/flag/flag_blue.png'
import FlagBlack from '@/assets/imgs/flag/flag_black.png' import FlagBlack from '@/assets/imgs/flag/flag_black.png'
const message = useMessage() //
const { t } = useI18n() //
const appStore = useAppStore() const appStore = useAppStore()
const show = ref(false) const show = ref(false)
@ -174,8 +176,7 @@ const operateRecordList = ref([])
const dialogMap = ref(null) const dialogMap = ref(null)
const aMap = ref(null) const aMap = ref(null)
async function open(id) { function getFollowList() {
try {
FollowApi.getFollowList({ clueId: id }).then((data) => { FollowApi.getFollowList({ clueId: id }).then((data) => {
followRecordList.value = data.map((item) => ({ followRecordList.value = data.map((item) => ({
operateUserName: item.operateUserName, operateUserName: item.operateUserName,
@ -185,6 +186,11 @@ async function open(id) {
nextFollowTime: formatDate(item.nextFollowTime) nextFollowTime: formatDate(item.nextFollowTime)
})) }))
}) })
}
async function open(id) {
try {
getFollowList()
ClueApi.getOpearateRecord({ clueId: id }).then((data) => { ClueApi.getOpearateRecord({ clueId: id }).then((data) => {
operateRecordList.value = data.map((item) => ({ operateRecordList.value = data.map((item) => ({
operateUserName: item.operateUserName, operateUserName: item.operateUserName,
@ -300,13 +306,36 @@ defineExpose({
const followRef = ref() const followRef = ref()
function addFollow() { function addFollow() {
followRef.value.open('create', null) followRef.value.open(info.value.clueId)
} }
function destroyMap() { function destroyMap() {
dialogMap.value = null dialogMap.value = null
aMap.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> </script>
<style lang="scss" scoped></style> <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>
<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> </template>
</el-table-column> </el-table-column>
</SSTable> </SSTable>
@ -122,9 +129,15 @@
:schema="allSchemas.formSchema" :schema="allSchemas.formSchema"
@sucess="getTableList" @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" /> <DialogSuccess ref="successRef" />
<DialogFollow ref="followRef" /> <DialogFollow ref="followRef" @success="getTableList" />
</div> </div>
</template> </template>
@ -141,6 +154,8 @@ import { formatDate } from '@/utils/formatTime'
import * as ClueApi from '@/api/clue' import * as ClueApi from '@/api/clue'
const message = useMessage() //
const searchRef = ref() const searchRef = ref()
const queryType = ref('2') const queryType = ref('2')
@ -190,6 +205,7 @@ function resetQuery() {
async function getTableList() { async function getTableList() {
// //
tableObject.value.loading = true tableObject.value.loading = true
getSearchCount()
try { try {
const queryParams = await searchRef.value.getFormModel() const queryParams = await searchRef.value.getFormModel()
const params = { const params = {
@ -231,7 +247,7 @@ function handleDetail(row) {
} }
function handleFollow(row) { function handleFollow(row) {
followRef.value.open('create', row) followRef.value.open(row.clueId)
} }
async function makeCall(phone) { async function makeCall(phone) {
@ -242,12 +258,33 @@ async function makeCall(phone) {
function handleSuccess(row) { function handleSuccess(row) {
successRef.value.open(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([]) const userOptions = ref([])
onMounted(() => { onMounted(() => {
getUserOption().then((data) => { getUserOption().then((data) => {
userOptions.value = data userOptions.value = data
}) })
getSearchCount()
getCurdSchemas() getCurdSchemas()
}) })
</script> </script>

Loading…
Cancel
Save