qsh 1 month ago
parent 742e85ae65
commit fa2ee0cd6a
  1. 3
      .env.base
  2. 50
      src/api/clue/clueRemark.js
  3. 8
      src/permission.js
  4. 39
      src/router/modules/remaining.ts
  5. 161
      src/views/Clue/Order/Comp/OrderRemark.vue
  6. 5
      src/views/Clue/Order/index.vue
  7. 104
      src/views/Clue/Pool/Comp/DialogRemark.vue
  8. 16
      src/views/Clue/Pool/index.vue
  9. 100
      src/views/Clue/ReceiveInput/index.vue
  10. 228
      src/views/Clue/Set/Comp/RemarkGroup.vue
  11. 5
      src/views/Clue/Set/index.vue

@ -5,7 +5,8 @@ VITE_DEV=true
# 请求路径
# VITE_BASE_URL='http://47.98.161.246:48080'
VITE_BASE_URL='http://114.215.207.150:48080'
# VITE_BASE_URL='http://114.215.207.150:48080'
VITE_BASE_URL='http://localhost:48080'
# 高德地图key密钥
# 1寻驾

@ -0,0 +1,50 @@
import request from '@/config/axios'
// 获得微信群列表
export const getWxGroupPage = (params) => {
return request.get({ url: '/admin-api/crm/wxgroup/page', params })
}
// 创建微信群
export const createWxGroup = (data) => {
return request.post({ url: '/admin-api/crm/wxgroup/create', data, isSubmitForm: true })
}
// 更新微信群
export const updateWxGroup = (data) => {
return request.put({ url: '/admin-api/crm/wxgroup/update', data })
}
// 删除微信群
export const deleteWxGroup = (id) => {
return request.delete({ url: `/admin-api/crm/wxgroup?groupId=${id}` })
}
export const getWxGroupSimpleList = async () => {
return await request.get({ url: '/admin-api/crm/wxgroup/simple-list' })
}
// 报备
export const reportClue = (data) => {
return request.post({ url: '/admin-api/crm/clue-remark/remark/save', data })
}
// 接待提交报名信息
export const registerClue = (data) => {
return request.post({ url: '/admin-api/crm/clue-remark/sign/save', data })
}
// 分页查询报备信息
export const getClueRemarkPage = (params) => {
return request.get({ url: '/admin-api/crm/clue-remark/page', params })
}
// 区域查询报备信息
export const getClueRemarkByRemarkId = (params) => {
return request.get({ url: `/admin-api/crm/clue-remark/sign/get`, params })
}
// 销售查询报备信息
export const getClueRemarkByClueId = (params) => {
return request.get({ url: `/admin-api/crm/clue-remark/remark/get`, params })
}

@ -9,6 +9,7 @@ import { useUserStoreWithOut } from '@/store/modules/user'
import { usePermissionStoreWithOut } from '@/store/modules/permission'
import { useAppStoreWithOut } from '@/store/modules/app'
import { getTenantId, getAppId, setTenantId, setAppId } from '@/utils/auth'
import { checkPermi } from '@/utils/permission'
const { start, done } = useNProgress()
@ -40,6 +41,13 @@ router.beforeEach(async (to, from, next) => {
await waitTime(1500)
}
next({ path: '/' })
} else if (to.name == 'ReceiveInput') {
if (!checkPermi(['clue:remark:receive-input'])) {
next({ path: '/403', replace: true, query: { noGoBack: true } })
return
} else {
next()
}
} else {
// 获取所有字典
const dictStore = useDictStoreWithOut()

@ -58,21 +58,21 @@ const remainingRouter: AppRouteRecordRaw[] = [
meta: {
title: '首页',
hidden: true
},
children: [
{
path: '/index',
component: () => import('@/views/Basic/Menu/index.vue'),
name: 'Home',
meta: {
title: '首页',
icon: 'ep:home-filled',
noTagsView: true,
affix: true
// hidden: true
}
// ,children: [
// {
// path: '/index',
// component: () => import('@/views/Basic/Menu/index.vue'),
// name: 'Home',
// meta: {
// title: '首页',
// icon: 'ep:home-filled',
// noTagsView: true,
// affix: true
// // hidden: true
// }
// }
// ]
}
]
},
{
path: '/swagger',
@ -164,6 +164,17 @@ const remainingRouter: AppRouteRecordRaw[] = [
// }
// ]
// },
{
path: '/receive-input/:id',
name: 'ReceiveInput',
component: () => import('@/views/Clue/ReceiveInput/index.vue'),
meta: {
title: '接待信息录入',
hidden: true,
noTagsView: true,
permission: 'clue:receiveInput:index'
}
},
{
path: '/login',
component: () => import('@/views/Login/Login.vue'),

@ -0,0 +1,161 @@
<template>
<div>
<el-form :model="searchForm" label-width="0" inline>
<el-form-item>
<el-input v-model="searchForm.name" placeholder="学员姓名" clearable style="width: 120px" />
</el-form-item>
<el-form-item>
<el-input
v-model="searchForm.phone"
placeholder="联系方式"
clearable
style="width: 120px"
/>
</el-form-item>
<el-form-item>
<el-select
v-model="searchForm.signUser"
placeholder="跟进人"
clearable
filterable
style="width: 120px"
>
<el-option
v-for="item in userOptions"
:key="item.id"
:label="item.nickname"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-date-picker
v-model="searchForm.dealDate"
type="daterange"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
range-separator="-"
start-placeholder="报备日期"
end-placeholder="报备日期"
style="width: 240px"
/>
</el-form-item>
<el-form-item>
<el-date-picker
v-model="searchForm.createDate"
type="daterange"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
range-separator="-"
start-placeholder="报名日期"
end-placeholder="报名日期"
style="width: 240px"
/>
</el-form-item>
<el-form-item>
<el-select
v-model="searchForm.signUser"
placeholder="报名状态"
clearable
filterable
style="width: 120px"
>
<el-option label="未报名" :value="0" />
<el-option label="已报名" :value="1" />
</el-select>
</el-form-item>
<el-form-item>
<el-select
v-model="searchForm.signUser"
placeholder="登记状态"
clearable
filterable
style="width: 120px"
>
<el-option label="未登记" :value="0" />
<el-option label="已登记" :value="1" />
</el-select>
</el-form-item>
<el-form-item>
<el-button @click="handleSearch">查询</el-button>
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
<el-table v-loading="loading" :data="tableList" border show-summary>
<el-table-column prop="name" label="学员姓名" width="90px" />
<el-table-column prop="phone" label="联系方式" width="120px" />
<el-table-column prop="signUserName" label="跟进人" width="90" />
<el-table-column prop="dealDate" label="报备日期" width="120px" :formatter="dateFormatter" />
<el-table-column prop="signSchool" label="报备人" width="90px" />
<el-table-column prop="signPlace" label="报备内容" min-width="150px" />
<el-table-column prop="dealDate" label="报名日期" width="120px" :formatter="dateFormatter" />
<el-table-column prop="signSchool" label="接待人" width="90px" />
<el-table-column prop="signPlace" label="报名信息" min-width="150px" />
<el-table-column prop="isCommissioned" label="报名状态" width="90px" fixed="right" />
<el-table-column prop="settlementState" label="登记状态" width="90px" fixed="right" />
</el-table>
<Pagination
v-model:limit="searchForm.pageSize"
v-model:page="searchForm.pageNo"
:total="total"
@pagination="getList"
/>
</div>
</template>
<script setup>
import { getSimpleUserList as getUserOption } from '@/api/system/user'
// import { removeNullField } from '@/utils/index'
import { dateFormatter } from '@/utils/formatTime'
onMounted(() => {
getOptions()
handleSearch()
})
const userOptions = ref([])
function getOptions() {
getUserOption().then((data) => {
userOptions.value = data
})
}
const searchForm = ref({
name: undefined,
phone: undefined,
pageSize: 20,
pageNo: 1
})
function handleReset() {
searchForm.value = {
name: undefined,
phone: undefined,
pageSize: 20,
pageNo: 1
}
}
function handleSearch() {
searchForm.value.pageNo = 1
getList()
}
const loading = ref(false)
const tableList = ref([])
const total = ref(0)
async function getList() {
loading.value = true
try {
// const data = await SettleApi.getSchoolSettlePage(removeNullField(searchForm.value))
// tableList.value = data.list
// total.value = data.total
} finally {
loading.value = false
}
}
</script>
<style lang="scss" scoped></style>

@ -26,6 +26,9 @@
<el-tab-pane label="对外结算" name="settle" v-if="checkPermi(['clue:order:settle-list'])">
<Settle v-if="tabName == 'settle'" />
</el-tab-pane>
<el-tab-pane label="邀约报备" name="remark" v-if="checkPermi(['clue:order:remark'])">
<OrderRemark v-if="tabName == 'remark'" />
</el-tab-pane>
</el-tabs>
</template>
@ -37,6 +40,8 @@ import Reback from './Comp/Reback.vue'
import AfterSales from './Comp/AfterSales.vue'
import Delivery from './Comp/Delivery.vue'
import Settle from './Comp/Settle.vue'
import OrderRemark from './Comp/OrderRemark.vue'
import { checkPermi } from '@/utils/permission'
const appStore = useAppStore()

@ -0,0 +1,104 @@
<template>
<Dialog title="邀约报备" v-model="show" width="600px">
<el-form :model="form" ref="formRef" :rules="rules" label-width="0">
<el-form-item prop="remarkContent">
<el-input
type="textarea"
placeholder="请输入报备信息"
v-model="form.remarkContent"
:autosize="{ minRows: 10 }"
/>
</el-form-item>
<el-form-item prop="groupId">
<el-select v-model="form.groupId" placeholder="选择群聊发送报备信息并@区域" filterable>
<el-option
v-for="item in groupOptions"
:key="item.groupId"
:label="item.groupName"
:value="item.groupId"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<span>
<el-button @click="show = false"> </el-button>
<el-button :disabled="formLoading" type="primary" @click="handleSave"> </el-button>
</span>
</template>
</Dialog>
</template>
<script setup>
import { getWxGroupSimpleList, getClueRemarkByClueId, reportClue } from '@/api/clue/clueRemark'
import { useUserStore } from '@/store/modules/user'
const userStore = useUserStore()
const form = ref({
remarkContent: '',
groupId: ''
})
const rules = {
remarkContent: [{ required: true, message: '请输入报备信息', trigger: 'blur' }],
groupId: [{ required: true, message: '请选择群聊', trigger: 'change' }]
}
const formLoading = ref(false)
const show = ref(false)
async function open(row) {
show.value = true
//
getOptions()
//
const data = await getClueRemarkByClueId({ clueId: row.clueId })
if (data && data.remarkId) {
form.value = { ...data }
return
}
form.value.clueId = row.clueId
let remarkInfo = `学员姓名:${row.name}\n联系方式:${row.phone}\n意向班型:`
if (row.diyParams?.licenseType) {
remarkInfo += `${row.diyParams.licenseType}\n`
} else {
remarkInfo += `\n`
}
const name = userStore.getUser.nickname.substring(0, 1)
remarkInfo += `意向成交价:\n邀约时间:\n邀约人:${name}教练\n备注:`
form.value.remarkContent = remarkInfo
}
defineExpose({ open })
const groupOptions = ref([])
function getOptions() {
getWxGroupSimpleList().then((response) => {
groupOptions.value = response
})
}
const formRef = ref(null)
function handleSave() {
formRef.value.validate(async (valid) => {
if (valid) {
formLoading.value = true
try {
const data = await reportClue(form.value)
message.success(data)
} catch (error) {
console.error('提交报备信息失败', error)
} finally {
formLoading.value = false
}
} else {
console.log('表单验证失败')
return false
}
})
}
</script>
<style lang="scss" scoped></style>

@ -133,6 +133,15 @@
>
修改
</el-button>
<el-button
type="primary"
link
@click="handleRemark(scope.row)"
:disabled="appStore.getAppInfo?.instanceType == 1 && scope.row.state == '成交'"
v-hasPermi="['clue:pool:remark']"
>
报备
</el-button>
<el-button
type="primary"
link
@ -195,6 +204,7 @@
:userOptions="allStatusUserOptions"
@success="getTableList"
/>
<DialogRemark ref="remarkRef" />
</div>
</template>
@ -207,6 +217,7 @@ import DialogSuccess from './Comp/DialogSuccess.vue'
import DialogFollow from './Comp/DialogFollow.vue'
import ClueMap from './Comp/ClueMap.vue'
import DialogBatchChangeFollow from './Comp/DialogBatchChangeFollow.vue'
import DialogRemark from './Comp/DialogRemark.vue'
import {
getSimpleUserList as getUserOption,
getAllUserList,
@ -363,6 +374,11 @@ async function makeCall(phone) {
console.log('打电话:' + phone)
}
const remarkRef = ref()
function handleRemark(row) {
remarkRef.value.open(row)
}
//
function handleSuccess(row) {
successRef.value.open(row.clueId)

@ -0,0 +1,100 @@
<template>
<div>
<el-card shadow="always" :body-style="{ padding: '10px' }">
<template #header>
<div>
<span>报备</span>
</div>
</template>
<el-input
class="remark"
type="textarea"
v-model="remarkContent"
:autosize="{ minRows: 5 }"
readonly
/>
</el-card>
<el-card shadow="always" :body-style="{ padding: '10px' }" class="mt-10px">
<template #header>
<div>
<span>接待录入</span>
</div>
</template>
<el-form :model="form" ref="formRef" :rules="rules" label-width="0">
<el-form-item prop="signInfo">
<el-input
class="remark"
type="textarea"
placeholder="请输入报名信息"
v-model="form.signInfo"
:autosize="{ minRows: 5 }"
/>
</el-form-item>
<el-form-item prop="group">
<el-select v-model="form.group" placeholder="选择群聊并发送报名信息" filterable>
<el-option
v-for="item in groupOptions"
:key="item.groupId"
:label="item.groupName"
:value="item.groupId"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button class="w-full" type="primary" size="large" @click="handleSubmit">
提交
</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</template>
<script setup>
import { getWxGroupSimpleList } from '@/api/clue/clueRemark'
const remarkContent = ref(
`学员姓名:测试1\n联系方式:1234321\n意向班型:\n意向成交价:\n邀约时间:\n邀约人:仇教练\n备注:`
)
const groupOptions = ref([])
const form = ref({
signInfo: '',
group: ''
})
const rules = {
signInfo: [{ required: true, message: '请输入接待信息', trigger: 'blur' }],
group: [{ required: true, message: '请选择群聊', trigger: 'change' }]
}
const formRef = ref(null)
function handleSubmit() {
formRef.value.validate((valid) => {
if (valid) {
console.log('提交成功', form)
//
} else {
console.log('表单验证失败')
return false
}
})
}
onMounted(() => {
getWxGroupSimpleList().then((response) => {
groupOptions.value = response.data
})
})
</script>
<style lang="scss" scoped>
::v-deep(.remark .el-textarea__inner) {
font-size: 15px;
color: #333;
}
::v-deep(.el-card__header) {
padding: 10px;
}
</style>

@ -0,0 +1,228 @@
<template>
<div>
<el-form ref="queryForm" :model="searchForm" label-width="0" inline>
<el-form-item>
<el-input
v-model="searchForm.groupName"
placeholder="请输入群名称"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery">搜索</el-button>
<el-button @click="resetQuery">重置</el-button>
<el-button type="primary" @click="openForm('create', null)">新增</el-button>
</el-form-item>
</el-form>
<el-table v-loading="loading" :data="tableList">
<el-table-column prop="groupName" label="群名称" />
<el-table-column label="区域负责人" prop="areaUserName" />
<el-table-column prop="remark" label="备注" />
<el-table-column label="操作">
<template #default="{ row }">
<el-button type="primary" style="padding: 0" text @click="openForm('update', row)">
修改
</el-button>
<el-button type="danger" text @click="handleDelete(row.groupId)"> 删除 </el-button>
</template>
</el-table-column>
</el-table>
<Pagination
v-model:limit="searchForm.pageSize"
v-model:page="searchForm.pageNo"
:total="total"
@pagination="getList"
/>
<Dialog :title="dialogTitle" v-model="dialogVisible" width="600px">
<el-form
ref="formRef"
v-loading="formLoading"
:model="formData"
:rules="formRules"
label-width="100px"
>
<el-form-item label="微信群" prop="groupId">
<el-select
filterable
v-model="formData.groupId"
placeholder="请选择群聊"
:disabled="dialogType === 'update'"
style="width: 100%"
>
<el-option
v-for="item in groupOptions"
:key="item.wxGroupId"
:label="item.wxGroupName"
:value="item.wxGroupId"
/>
</el-select>
</el-form-item>
<el-form-item label="区域负责人" prop="areaUser">
<el-select v-model="formData.areaUser" placeholder="请选择" filterable>
<el-option
v-for="item in userOptions"
:key="item.id"
:label="item.nickname"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input
type="textarea"
v-model="formData.remark"
placeholder="请输入备注"
:autosize="{ minRows: 4, maxRows: 8 }"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button :disabled="formLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</div>
</template>
<script setup>
import * as wxgroupApi from '@/api/clue/clueRemark'
import { getSimpleUserList as getUserOption } from '@/api/system/user'
import { getWxGroupList } from '@/api/okr/meeting'
const { t } = useI18n() //
const message = useMessage() //
const searchForm = ref({
groupName: '',
pageSize: 20,
pageNo: 1
})
const total = ref(0)
const tableList = ref([])
const loading = ref(false)
function handleQuery() {
searchForm.value.pageNo = 1
getList()
}
function resetQuery() {
searchForm.value = {
groupName: '',
pageSize: 20,
pageNo: 1
}
getList()
}
async function getList() {
loading.value = true
try {
const data = await wxgroupApi.getWxGroupPage(searchForm.value)
tableList.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
const dialogVisible = ref(false) //
const dialogTitle = ref('') //
const dialogType = ref('create') // create update
const formLoading = ref(false) // 12
const formData = ref({
groupId: '',
areaUser: '',
remark: ''
})
const formRules = reactive({
groupId: [{ required: true, message: '微信群不能为空', trigger: 'change' }],
areaUser: [{ required: true, message: '区域负责人不能为空', trigger: 'change' }]
})
const formRef = ref(null) // ref
const groupOptions = ref([]) //
const userOptions = ref([]) //
async function openForm(type, row) {
dialogType.value = type
dialogTitle.value = type === 'create' ? '新增报备群' : '修改报备群'
dialogVisible.value = true
resetForm()
//
if (row) {
formLoading.value = true
try {
formData.value = { ...row }
} finally {
formLoading.value = false
}
}
}
function resetForm() {
formData.value = {
groupId: '',
areaUser: '',
remark: ''
}
if (formRef.value) {
formRef.value.clearValidate()
}
}
async function handleDelete(id) {
try {
//
await message.delConfirm()
//
await wxgroupApi.deleteWxGroup(id)
message.success(t('common.delSuccess'))
//
await getList()
} catch {}
}
function submitForm() {
if (!formRef.value) return
formRef.value.validate(async (valid) => {
if (valid) {
formLoading.value = true
formData.value.groupName = groupOptions.value.find(
(item) => item.wxGroupId == formData.value.groupId
)?.wxGroupName
try {
if (dialogType.value === 'create') {
await wxgroupApi.createWxGroup(formData.value)
message.success(t('common.addSuccess'))
} else {
await wxgroupApi.updateWxGroup(formData.value)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
getList()
} finally {
formLoading.value = false
}
}
})
}
function getOptions() {
getWxGroupList().then((resp) => {
groupOptions.value = resp || []
})
//
getUserOption().then((data) => {
userOptions.value = data
})
}
onMounted(() => {
handleQuery()
getOptions()
})
</script>
<style lang="scss" scoped></style>

@ -33,6 +33,10 @@
<el-tab-pane label="其他费用项" :name="37" v-if="checkPermi(['clue:setting:other-pay'])">
<OtherPayType v-if="tabIndex == 37" />
</el-tab-pane>
<!-- 报备群设置 -->
<el-tab-pane label="报备群设置" :name="38" v-if="checkPermi(['clue:setting:remarkGroup'])">
<RemarkGroup v-if="tabIndex == 38" />
</el-tab-pane>
<el-tab-pane label="常规设置" :name="40" v-if="checkPermi(['clue:setting:general-setting'])">
<GeneralSet v-if="tabIndex == 40" :appType="appStore.getAppInfo?.instanceType" />
</el-tab-pane>
@ -54,6 +58,7 @@ import IntentionStatus from './Comp/IntentionStatus.vue'
import ExtraFeeType from './Comp/ExtraFeeType.vue'
import OtherPayType from './Comp/OtherPayType.vue'
import GeneralSet from './Comp/GeneralSet.vue'
import RemarkGroup from './Comp/RemarkGroup.vue'
import { checkPermi } from '@/utils/permission'
import { useAppStore } from '@/store/modules/app'

Loading…
Cancel
Save