Merge remote-tracking branch 'origin/main' into dev-cl
This commit is contained in:
11
src/api/clue/clueCache.js
Normal file
11
src/api/clue/clueCache.js
Normal file
@@ -0,0 +1,11 @@
|
||||
import request from '@/config/axios'
|
||||
|
||||
// 查询用户配置
|
||||
export const getClueCache = async (params) => {
|
||||
return await request.get({ url: '/admin-api/crm/param-user-setting/get-by-user', params })
|
||||
}
|
||||
|
||||
// 保存用户配置
|
||||
export const setClueCache = async (data) => {
|
||||
return await request.post({ url: '/admin-api/crm/param-user-setting/save', data })
|
||||
}
|
||||
40
src/api/clue/clueField.js
Normal file
40
src/api/clue/clueField.js
Normal file
@@ -0,0 +1,40 @@
|
||||
import request from '@/config/axios'
|
||||
|
||||
// 创建
|
||||
export const createField = (data) => {
|
||||
return request.post({ url: '/admin-api/crm/clue-param/create', data })
|
||||
}
|
||||
|
||||
// 更新
|
||||
export const updateField = (data) => {
|
||||
return request.put({ url: '/admin-api/crm/clue-param/update', data })
|
||||
}
|
||||
|
||||
// 删除
|
||||
export const deleteField = (id) => {
|
||||
return request.delete({ url: `/admin-api/crm/clue-param/delete?id=${id}` })
|
||||
}
|
||||
|
||||
// 获得
|
||||
export const getField = (id) => {
|
||||
return request.get({ url: `/admin-api/crm/clue-param/get?id=${id}` })
|
||||
}
|
||||
|
||||
// 获得精简信息列表
|
||||
export const getSimpleFieldList = () => {
|
||||
return request.get({ url: '/admin-api/crm/clue-param/simple-list' })
|
||||
}
|
||||
|
||||
// 获取自定义字段
|
||||
export const getDiyFieldList = () => {
|
||||
return request.get({ url: '/admin-api/crm/clue-param/get-diy-param' })
|
||||
}
|
||||
|
||||
// 状态修改
|
||||
export const updateFieldStatus = (signParamId, status) => {
|
||||
const data = {
|
||||
signParamId,
|
||||
status
|
||||
}
|
||||
return request.put({ url: '/admin-api/crm/clue-param/status/update', data: data })
|
||||
}
|
||||
21
src/api/clue/clueGetSet.js
Normal file
21
src/api/clue/clueGetSet.js
Normal file
@@ -0,0 +1,21 @@
|
||||
import request from '@/config/axios'
|
||||
|
||||
// 线索获取规则
|
||||
export const getClueGainRuleList = () => {
|
||||
return request.get({ url: '/admin-api/crm/sch-clue-gain-rule/list' })
|
||||
}
|
||||
|
||||
// 删除
|
||||
export const deleteClueGainRule = (id) => {
|
||||
return request.delete({ url: `/admin-api/crm/sch-clue-gain-rule/delete?id=${id}` })
|
||||
}
|
||||
|
||||
// 线索规则
|
||||
export const getClueDistributeRuleList = () => {
|
||||
return request.get({ url: '/admin-api/crm/sch-clue-gain-rule/list' })
|
||||
}
|
||||
|
||||
// 删除
|
||||
export const deleteClueDistributeRule = (id) => {
|
||||
return request.delete({ url: `/admin-api/crm/sch-clue-gain-rule/delete?id=${id}` })
|
||||
}
|
||||
16
src/api/clue/followRecord.js
Normal file
16
src/api/clue/followRecord.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import request from '@/config/axios'
|
||||
|
||||
// 查询(精简)列表
|
||||
export const getFollowList = async (params) => {
|
||||
return await request.get({ url: '/admin-api/crm/clue-follow-record/list', params })
|
||||
}
|
||||
|
||||
// 新增
|
||||
export const createFollow = async (data) => {
|
||||
return await request.post({ url: '/admin-api/crm/clue-follow-record/create', data: data })
|
||||
}
|
||||
|
||||
// 删除
|
||||
export const deleteFollow = async (id) => {
|
||||
return await request.delete({ url: '/admin-api/crm/clue-follow-record/delete?id=' + id })
|
||||
}
|
||||
46
src/api/clue/index.js
Normal file
46
src/api/clue/index.js
Normal file
@@ -0,0 +1,46 @@
|
||||
import request from '@/config/axios'
|
||||
|
||||
// 查询(精简)列表
|
||||
export const getSimpleClueList = async () => {
|
||||
return await request.get({ url: '/admin-api/crm/sch-clue/list-all-simple' })
|
||||
}
|
||||
|
||||
// 查询列表
|
||||
export const getCluePage = async (params) => {
|
||||
return await request.get({ url: '/admin-api/crm/sch-clue/page', params })
|
||||
}
|
||||
|
||||
// 查询详情
|
||||
export const getClue = async (id) => {
|
||||
return await request.get({ url: '/admin-api/crm/sch-clue/get?id=' + id })
|
||||
}
|
||||
|
||||
// 新增
|
||||
export const createClue = async (data) => {
|
||||
return await request.post({ url: '/admin-api/crm/sch-clue/create', data: data })
|
||||
}
|
||||
|
||||
// 修改
|
||||
export const updateClue = async (params) => {
|
||||
return await request.put({ url: '/admin-api/crm/sch-clue/update', data: params })
|
||||
}
|
||||
|
||||
// 删除
|
||||
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 })
|
||||
}
|
||||
40
src/api/clue/orderField.js
Normal file
40
src/api/clue/orderField.js
Normal file
@@ -0,0 +1,40 @@
|
||||
import request from '@/config/axios'
|
||||
|
||||
// 创建
|
||||
export const createField = (data) => {
|
||||
return request.post({ url: '/admin-api/crm/sign-param/create', data })
|
||||
}
|
||||
|
||||
// 更新
|
||||
export const updateField = (data) => {
|
||||
return request.put({ url: '/admin-api/crm/sign-param/update', data })
|
||||
}
|
||||
|
||||
// 删除
|
||||
export const deleteField = (id) => {
|
||||
return request.delete({ url: `/admin-api/crm/sign-param/delete?id=${id}` })
|
||||
}
|
||||
|
||||
// 获得
|
||||
export const getField = (id) => {
|
||||
return request.get({ url: `/admin-api/crm/sign-param/get?id=${id}` })
|
||||
}
|
||||
|
||||
// 获得精简信息列表
|
||||
export const getSimpleFieldList = () => {
|
||||
return request.get({ url: '/admin-api/crm/sign-param/simple-list' })
|
||||
}
|
||||
|
||||
// 获取自定义字段
|
||||
export const getDiyFieldList = () => {
|
||||
return request.get({ url: '/admin-api/crm/sign-param/get-diy-param' })
|
||||
}
|
||||
|
||||
// 状态修改
|
||||
export const updateFieldStatus = (signParamId, status) => {
|
||||
const data = {
|
||||
signParamId,
|
||||
status
|
||||
}
|
||||
return request.put({ url: '/admin-api/crm/sign-param/status/update', data: data })
|
||||
}
|
||||
26
src/api/clue/skill.js
Normal file
26
src/api/clue/skill.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import request from '@/config/axios'
|
||||
|
||||
// 获得列表
|
||||
export const getSkillPage = (params) => {
|
||||
return request.get({ url: '/admin-api/crm/skill/page', params })
|
||||
}
|
||||
|
||||
// 创建
|
||||
export const createSkill = (data) => {
|
||||
return request.post({ url: '/admin-api/crm/skill/create', data })
|
||||
}
|
||||
|
||||
// 更新
|
||||
export const updateSkill = (data) => {
|
||||
return request.put({ url: '/admin-api/crm/skill/update', data })
|
||||
}
|
||||
|
||||
// 删除
|
||||
export const deleteSkill = (id) => {
|
||||
return request.delete({ url: `/admin-api/crm/skill/delete?id=${id}` })
|
||||
}
|
||||
|
||||
// 获得
|
||||
export const getSkill = (id) => {
|
||||
return request.get({ url: `/admin-api/crm/skill/get?id=${id}` })
|
||||
}
|
||||
31
src/api/clue/source.js
Normal file
31
src/api/clue/source.js
Normal file
@@ -0,0 +1,31 @@
|
||||
import request from '@/config/axios'
|
||||
|
||||
// 查询(精简)列表
|
||||
export const getSimpleSourceList = async () => {
|
||||
return await request.get({ url: '/admin-api/crm/source/list' })
|
||||
}
|
||||
|
||||
// 查询列表
|
||||
export const getSourcePage = async (params) => {
|
||||
return await request.get({ url: '/admin-api/crm/source/list', params })
|
||||
}
|
||||
|
||||
// 查询详情
|
||||
export const getSource = async (id) => {
|
||||
return await request.get({ url: '/admin-api/crm/source/get?id=' + id })
|
||||
}
|
||||
|
||||
// 新增
|
||||
export const createSource = async (data) => {
|
||||
return await request.post({ url: '/admin-api/crm/source/create', data: data })
|
||||
}
|
||||
|
||||
// 修改
|
||||
export const updateSource = async (params) => {
|
||||
return await request.put({ url: '/admin-api/crm/source/update', data: params })
|
||||
}
|
||||
|
||||
// 删除
|
||||
export const deleteSource = async (id) => {
|
||||
return await request.delete({ url: '/admin-api/crm/source/delete?id=' + id })
|
||||
}
|
||||
@@ -55,6 +55,8 @@ const dialogStyle = computed(() => {
|
||||
height: unref(dialogHeight)
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['close'])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -66,6 +68,7 @@ const dialogStyle = computed(() => {
|
||||
draggable
|
||||
lock-scroll
|
||||
v-bind="getBindValue"
|
||||
@close="emit('close')"
|
||||
>
|
||||
<template #header>
|
||||
<div class="flex justify-between">
|
||||
|
||||
@@ -181,11 +181,7 @@ export default defineComponent({
|
||||
const slotsMap: Recordable = {
|
||||
...setItemComponentSlots(slots, item?.componentProps?.slots, item.field)
|
||||
}
|
||||
if (
|
||||
item?.component !== 'SelectV2' &&
|
||||
item?.component !== 'Cascader' &&
|
||||
item?.componentProps?.options
|
||||
) {
|
||||
if (item?.component !== 'SelectV2' && item?.component !== 'Cascader' && item?.options) {
|
||||
slotsMap.default = () => renderOptions(item)
|
||||
}
|
||||
|
||||
@@ -236,6 +232,8 @@ export default defineComponent({
|
||||
vModel={formModel.value[item.field]}
|
||||
{...(autoSetPlaceholder && setTextPlaceholder(item))}
|
||||
{...setComponentProps(item)}
|
||||
format={item.component == 'DatePicker' ? 'YYYY-MM-DD' : null}
|
||||
value-format={item.component == 'DatePicker' ? 'YYYY-MM-DD' : null}
|
||||
style={baseSty + item.componentProps?.style}
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
{...(notRenderOptions.includes(item?.component as string) && item?.componentProps?.options
|
||||
|
||||
@@ -5,12 +5,12 @@ import { defineComponent } from 'vue'
|
||||
export const useRenderCheckbox = () => {
|
||||
const renderCheckboxOptions = (item: FormSchema) => {
|
||||
// 如果有别名,就取别名
|
||||
const labelAlias = item?.componentProps?.optionsAlias?.labelField
|
||||
const valueAlias = item?.componentProps?.optionsAlias?.valueField
|
||||
const labelAlias = item?.componentProps?.optionsAlias?.labelField || 'id'
|
||||
const valueAlias = item?.componentProps?.optionsAlias?.valueField || 'name'
|
||||
const Com = (item.component === 'Checkbox' ? ElCheckbox : ElCheckboxButton) as ReturnType<
|
||||
typeof defineComponent
|
||||
>
|
||||
return item?.componentProps?.options?.map((option) => {
|
||||
return item?.options?.map((option) => {
|
||||
const { ...other } = option
|
||||
return (
|
||||
<Com {...other} label={option[valueAlias || 'value']}>
|
||||
|
||||
@@ -5,12 +5,12 @@ import { defineComponent } from 'vue'
|
||||
export const useRenderRadio = () => {
|
||||
const renderRadioOptions = (item: FormSchema) => {
|
||||
// 如果有别名,就取别名
|
||||
const labelAlias = item?.componentProps?.optionsAlias?.labelField
|
||||
const valueAlias = item?.componentProps?.optionsAlias?.valueField
|
||||
const labelAlias = item?.componentProps?.optionsAlias?.labelField || 'id'
|
||||
const valueAlias = item?.componentProps?.optionsAlias?.valueField || 'name'
|
||||
const Com = (item.component === 'Radio' ? ElRadio : ElRadioButton) as ReturnType<
|
||||
typeof defineComponent
|
||||
>
|
||||
return item?.componentProps?.options?.map((option) => {
|
||||
return item?.options?.map((option) => {
|
||||
const { ...other } = option
|
||||
return (
|
||||
<Com {...other} label={option[valueAlias || 'value']}>
|
||||
|
||||
@@ -9,12 +9,12 @@ export const useRenderSelect = (slots: Slots) => {
|
||||
const renderSelectOptions = (item: FormSchema) => {
|
||||
// 如果有别名,就取别名
|
||||
const labelAlias = item?.componentProps?.optionsAlias?.labelField
|
||||
return item?.componentProps?.options?.map((option) => {
|
||||
if (option?.options?.length) {
|
||||
return item?.options?.map((option) => {
|
||||
if (option?.length) {
|
||||
return (
|
||||
<ElOptionGroup label={option[labelAlias || 'label']}>
|
||||
{() => {
|
||||
return option?.options?.map((v) => {
|
||||
return option?.map((v) => {
|
||||
return renderSelectOptionItem(item, v)
|
||||
})
|
||||
}}
|
||||
@@ -29,8 +29,8 @@ export const useRenderSelect = (slots: Slots) => {
|
||||
// 渲染 select option item
|
||||
const renderSelectOptionItem = (item: FormSchema, option: ComponentOptions) => {
|
||||
// 如果有别名,就取别名
|
||||
const labelAlias = item?.componentProps?.optionsAlias?.labelField
|
||||
const valueAlias = item?.componentProps?.optionsAlias?.valueField
|
||||
const labelAlias = item?.componentProps?.optionsAlias?.labelField || 'id'
|
||||
const valueAlias = item?.componentProps?.optionsAlias?.valueField || 'name'
|
||||
|
||||
const { label, value, ...other } = option
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
@end="onDragEnd"
|
||||
>
|
||||
<template #item="{ element: item }">
|
||||
<el-checkbox :key="item.field" :label="item.field">
|
||||
<el-checkbox :key="item.id" :label="item.id">
|
||||
{{ item.label }}
|
||||
</el-checkbox>
|
||||
</template>
|
||||
@@ -48,9 +48,8 @@
|
||||
|
||||
<script setup>
|
||||
import draggable from 'vuedraggable'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
import * as ClueCacheApi from '@/api/clue/clueCache'
|
||||
import { useRoute } from 'vue-router'
|
||||
import cache from '@/plugins/cache'
|
||||
|
||||
const props = defineProps({
|
||||
tableObject: { type: Object, default: () => ({ tableList: [] }) },
|
||||
@@ -59,7 +58,6 @@ const props = defineProps({
|
||||
|
||||
const emit = defineEmits(['update:tableObject', 'getList', 'getCheckedColumns'])
|
||||
const route = useRoute()
|
||||
const { id: userId } = useUserStore().user //取用户ID
|
||||
|
||||
const pageNo = ref(props.tableObject?.pageNo || 1)
|
||||
|
||||
@@ -68,7 +66,7 @@ const pageSize = ref(props.tableObject?.pageSize || 20)
|
||||
const ColumnSetting = ref()
|
||||
const TableColumnPop = ref()
|
||||
// 展示在设置里的所有表格列,由于会排序,所以使用新属性,不直接修改原值
|
||||
const allColumns = ref({})
|
||||
const allColumns = ref([...props.tableColumns])
|
||||
// 已勾选的选项
|
||||
const checkedColumns = ref([])
|
||||
|
||||
@@ -85,67 +83,50 @@ const clickSetting = () => {
|
||||
unref(TableColumnPop).TableColumnPop?.delayHide?.()
|
||||
}
|
||||
|
||||
// 获取所有的表格列,注意如果本地有缓存使用缓存数据,因为用户可能已对表格列排序,并且不依赖网络请求,可更快渲染表头
|
||||
function getAllColumns() {
|
||||
// 1. 先获取缓存表头
|
||||
const localData = getColumn('TableColumnAll')[route.name] || []
|
||||
// 2. 如果有缓存的表头,直接使用,并将新增的标题加入,如果没有缓存,那就得用获取到的表头
|
||||
if (localData && localData) {
|
||||
const newColumns = props.tableColumns.filter(
|
||||
(item) => !localData.some((it) => it.field == item.field)
|
||||
)
|
||||
allColumns.value = [...localData, ...newColumns]
|
||||
} else {
|
||||
allColumns.value = [...props.tableColumns]
|
||||
}
|
||||
}
|
||||
// 获取缓存的表头
|
||||
function getColumn(name = 'shitTable') {
|
||||
return cache.local.get(`${name}-${userId}`) || {}
|
||||
}
|
||||
// 设置表头缓存
|
||||
function setColumn(val, name = 'shitTable') {
|
||||
cache.local.set(`${name}-${userId}`, val)
|
||||
const routeMap = {
|
||||
CluePool: 1,
|
||||
ClueOrder: 2
|
||||
}
|
||||
|
||||
// 获取用户已勾选的表头
|
||||
function getUserCheckedColumns() {
|
||||
// 1. 先获取缓存
|
||||
const localData = getColumn('shitTable')[route.name]
|
||||
// 2. 如果有缓存,使用缓存表头,否则使用默认的所有表头
|
||||
if (localData && localData.length) {
|
||||
checkedColumns.value = localData
|
||||
} else {
|
||||
checkedColumns.value = allColumns.value.map((it) => it.field)
|
||||
}
|
||||
// 3. 回显到表格中
|
||||
async function getUserCheckedColumns() {
|
||||
checkedColumns.value = await ClueCacheApi.getClueCache({
|
||||
settingType: 2,
|
||||
model: routeMap[route.name]
|
||||
})
|
||||
// 回显到表格中
|
||||
emitColumns()
|
||||
}
|
||||
|
||||
// 表格列排序
|
||||
function onDragEnd() {
|
||||
const obj = getColumn('TableColumnAll')
|
||||
obj[route.name] = allColumns.value
|
||||
// 1. 设置缓存
|
||||
setColumn(obj, 'TableColumnAll')
|
||||
// 2. 表格回显
|
||||
ClueCacheApi.setClueCache({
|
||||
settingType: 2,
|
||||
model: routeMap[route.name],
|
||||
clueParamId: checkedColumns.value
|
||||
})
|
||||
|
||||
// 表格回显
|
||||
emitColumns()
|
||||
}
|
||||
|
||||
// 勾选确认
|
||||
function confirm() {
|
||||
const obj = getColumn()
|
||||
obj[route.name] = checkedColumns.value
|
||||
setColumn(obj, 'shitTable')
|
||||
ClueCacheApi.setClueCache({
|
||||
settingType: 2,
|
||||
model: routeMap[route.name],
|
||||
clueParamId: checkedColumns.value
|
||||
})
|
||||
// usedSchema.value = allColumns.value.filter((it) => checkedColumns.value.includes(it.id))
|
||||
emitColumns()
|
||||
}
|
||||
|
||||
// 将表头数据返回至父组件
|
||||
function emitColumns() {
|
||||
const arr = allColumns.value.filter((item) => checkedColumns.value.includes(item.field))
|
||||
const arr = allColumns.value.filter((item) => checkedColumns.value.includes(item.id))
|
||||
emit('getCheckedColumns', arr)
|
||||
}
|
||||
|
||||
getAllColumns()
|
||||
getUserCheckedColumns()
|
||||
|
||||
defineExpose({ getUserCheckedColumns })
|
||||
|
||||
@@ -7,12 +7,10 @@ import { findIndex } from '@/utils'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { FormSchema } from '@/types/form'
|
||||
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
import { useRoute } from 'vue-router'
|
||||
import cache from '@/plugins/cache'
|
||||
import * as ClueCacheApi from '@/api/clue/clueCache'
|
||||
|
||||
const route = useRoute()
|
||||
const { id: userId } = useUserStore().user //取用户ID
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
@@ -46,7 +44,7 @@ const props = defineProps({
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['search', 'reset'])
|
||||
// const emit = defineEmits(['search', 'reset'])
|
||||
|
||||
const visible = ref(true)
|
||||
|
||||
@@ -80,37 +78,26 @@ const newSchema = computed(() => {
|
||||
return schema
|
||||
})
|
||||
|
||||
function initSearch() {
|
||||
reset()
|
||||
// 1. 先获取缓存
|
||||
const localData = getColumn('Schema')[route.name]
|
||||
// 2. 如果有缓存,使用缓存表头,否则使用默认的所有表头
|
||||
if (localData && localData.length) {
|
||||
usedSchema.value = localData
|
||||
} else {
|
||||
const obj = getColumn('Schema')
|
||||
obj[route.name] = [props.schema[0]]
|
||||
setSchema(obj)
|
||||
usedSchema.value = [props.schema[0]]
|
||||
const routeMap = {
|
||||
CluePool: 1,
|
||||
ClueOrder: 2
|
||||
}
|
||||
checkedSchema.value = usedSchema.value.map((it) => it.field)
|
||||
async function initSearch() {
|
||||
reset()
|
||||
checkedSchema.value = await ClueCacheApi.getClueCache({
|
||||
settingType: 1,
|
||||
model: routeMap[route.name]
|
||||
})
|
||||
usedSchema.value = props.schema.filter((it) => checkedSchema.value.includes(it.id))
|
||||
}
|
||||
|
||||
function changeSearch() {
|
||||
const obj = getColumn('Schema')
|
||||
obj[route.name] = props.schema.filter((item) => checkedSchema.value.includes(item.field))
|
||||
setSchema(obj)
|
||||
initSearch()
|
||||
}
|
||||
|
||||
// 获取缓存的查询条件
|
||||
function getColumn(name = 'Schema') {
|
||||
return cache.local.get(`${name}-${userId}`) || {}
|
||||
}
|
||||
|
||||
// 设置查询条件缓存
|
||||
function setSchema(val: Array<Object>, name = 'Schema') {
|
||||
cache.local.set(`${name}-${userId}`, val)
|
||||
ClueCacheApi.setClueCache({
|
||||
settingType: 1,
|
||||
model: routeMap[route.name],
|
||||
clueParamId: checkedSchema.value
|
||||
})
|
||||
usedSchema.value = props.schema.filter((it) => checkedSchema.value.includes(it.id))
|
||||
}
|
||||
|
||||
function setSearch() {
|
||||
@@ -121,23 +108,34 @@ const { register, elFormRef, methods } = useForm({
|
||||
model: props.model || {}
|
||||
})
|
||||
|
||||
const search = async () => {
|
||||
await unref(elFormRef)?.validate(async (isValid) => {
|
||||
if (isValid) {
|
||||
const { getFormData } = methods
|
||||
const model = await getFormData()
|
||||
emit('search', model)
|
||||
}
|
||||
})
|
||||
}
|
||||
// const search = async () => {
|
||||
// await unref(elFormRef)?.validate(async (isValid) => {
|
||||
// if (isValid) {
|
||||
// const { getFormData } = methods
|
||||
// const model = await getFormData()
|
||||
// emit('search', model)
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
|
||||
const reset = async () => {
|
||||
unref(elFormRef)?.resetFields()
|
||||
// const { getFormData } = methods
|
||||
// const model = await getFormData()
|
||||
// emit('reset', model)
|
||||
}
|
||||
|
||||
async function getFormModel() {
|
||||
const { getFormData } = methods
|
||||
const model = await getFormData()
|
||||
emit('reset', model)
|
||||
return model
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
getFormModel,
|
||||
reset
|
||||
})
|
||||
|
||||
const bottonButtonStyle = computed(() => {
|
||||
return {
|
||||
textAlign: props.buttomPosition as unknown as 'left' | 'center' | 'right'
|
||||
@@ -168,10 +166,7 @@ initSearch()
|
||||
>
|
||||
<template #action>
|
||||
<div v-if="layout === 'inline'">
|
||||
<ElButton ref="SchemaSetting" @click="setSearch">
|
||||
<!-- <Icon class="mr-5px" icon="ep:setting" /> -->
|
||||
查询设置
|
||||
</ElButton>
|
||||
<ElButton ref="SchemaSetting" @click="setSearch"> 查询设置 </ElButton>
|
||||
<el-popover
|
||||
ref="SettingPop"
|
||||
:virtual-ref="SchemaSetting"
|
||||
@@ -181,7 +176,7 @@ initSearch()
|
||||
virtual-triggering
|
||||
>
|
||||
<el-checkbox-group v-model="checkedSchema" @change="changeSearch">
|
||||
<el-checkbox v-for="item in schema" :key="item.field" :label="item.field">
|
||||
<el-checkbox v-for="item in schema" :key="item.id" :label="item.id">
|
||||
{{ item.label }}
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
|
||||
@@ -179,7 +179,7 @@ service.interceptors.response.use(
|
||||
})
|
||||
}
|
||||
} else if (code === 500) {
|
||||
ElMessage.error(t('sys.api.errMsg500'))
|
||||
ElMessage.error(t(msg || 'sys.api.errMsg500'))
|
||||
return Promise.reject(new Error(msg))
|
||||
} else if (code === 901) {
|
||||
ElMessage.error({
|
||||
|
||||
@@ -119,7 +119,8 @@ const filterSearchSchema = (crudSchema: CrudSchema[], allSchemas: AllSchemas): F
|
||||
componentProps: comonentProps,
|
||||
...schemaItem.search,
|
||||
field: schemaItem.field,
|
||||
label: schemaItem.search?.label || schemaItem.label
|
||||
label: schemaItem.search?.label || schemaItem.label,
|
||||
id: schemaItem.clueParamId
|
||||
}
|
||||
if (searchSchemaItem.api) {
|
||||
searchRequestTask.push(async () => {
|
||||
@@ -165,7 +166,8 @@ const filterTableSchema = (crudSchema: CrudSchema[]): TableColumn[] => {
|
||||
}
|
||||
return {
|
||||
...schema.table,
|
||||
...schema
|
||||
...schema,
|
||||
id: schema.clueParamId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,3 +209,14 @@ export const yuanToFen = (amount: string | number): number => {
|
||||
export const fenToYuan = (amount: string | number): number => {
|
||||
return Number((Number(amount) / 100).toFixed(2))
|
||||
}
|
||||
|
||||
export const removeNullField = (obj: Object) => {
|
||||
for (const key in obj) {
|
||||
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
||||
if (obj[key] == '') {
|
||||
delete obj[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
@@ -1,46 +1,52 @@
|
||||
<template>
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible" width="800px">
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible" width="800px" @close="destroyMap">
|
||||
<el-tabs v-model="tabName">
|
||||
<el-tab-pane label="线索信息" name="info">
|
||||
<Form
|
||||
ref="formRef"
|
||||
v-loading="formLoading"
|
||||
:rules="rules"
|
||||
isCol
|
||||
:schema="allSchemas.formSchema"
|
||||
/>
|
||||
<Form ref="formRef" v-loading="formLoading" :rules="rules" isCol :schema="formSchema" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="跟进信息" name="follow">
|
||||
<el-button type="primary" @click="handleAppendFollow">新增跟进人</el-button>
|
||||
<el-table :data="followList">
|
||||
<el-table-column label="跟进人">
|
||||
<template #default="{ row }">
|
||||
<el-select v-model="row.followUser" placeholder="选择跟进人" filterable>
|
||||
<el-select
|
||||
v-model="row.userId"
|
||||
placeholder="选择跟进人"
|
||||
filterable
|
||||
:disabled="!row.editable"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in userOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
v-for="item in props.userOptions"
|
||||
:key="item.id"
|
||||
:label="item.nickname"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="nextFollowTime" label="下次跟进时间">
|
||||
<el-table-column prop="followTime" label="下次跟进时间">
|
||||
<template #default="{ row }">
|
||||
<el-date-picker v-model="row.nextFollowTime" type="date" placeholder="选择日期时间" />
|
||||
<el-date-picker
|
||||
v-model="row.followTime"
|
||||
type="date"
|
||||
placeholder="选择日期时间"
|
||||
:disabled="!row.editable"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="80">
|
||||
<template #default="{ $index }">
|
||||
<Icon icon="ep:remove-filled" class="text-red-500" @click="handleRemove($index)" />
|
||||
<!-- <el-button @click="handleRemove(row, $index)">
|
||||
|
||||
</el-button> -->
|
||||
<template #default="{ row, $index }">
|
||||
<Icon
|
||||
v-if="row.editable"
|
||||
icon="ep:remove-filled"
|
||||
class="text-red-500"
|
||||
@click="handleRemove($index)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="位置信息" name="map">
|
||||
<el-tab-pane v-if="appStore.getAppInfo?.instanceType == 1" label="位置信息" name="map">
|
||||
<div class="flex justify-between">
|
||||
<el-select
|
||||
v-model="areaValue"
|
||||
@@ -64,11 +70,15 @@
|
||||
<span style="float: right; color: #8492a6; font-size: 13px">{{ item.district }}</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
<el-checkbox v-model="showSchool" :label="true" @change="handleShowSchool"
|
||||
>展示场地</el-checkbox
|
||||
>
|
||||
<el-checkbox v-model="showSchool" :label="true" @change="handleShowSchool">
|
||||
展示场地
|
||||
</el-checkbox>
|
||||
</div>
|
||||
<div id="dialogMap" class="mt-20px" style="height: 400px; width: 100%"></div>
|
||||
<div class="flex items-center mt-10px mb-10px">
|
||||
<div class="w-100px">线索位置:</div>
|
||||
<el-input v-model="address" placeholder="请输入线索位置" clearable />
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<template #footer>
|
||||
@@ -80,12 +90,58 @@
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { allSchemas, rules } from '../cluePool.data'
|
||||
<script setup name="DialogClue">
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
import { getPlaceList } from '@/api/school/place'
|
||||
import * as ClueApi from '@/api/clue'
|
||||
import { getDiyFieldList } from '@/api/clue/clueField'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
import AMapLoader from '@amap/amap-jsapi-loader'
|
||||
import ImgPostion from '@/assets/imgs/flag/flag_red.png'
|
||||
import ImgFlag from '@/assets/imgs/flag/position_blue.png'
|
||||
import FlagRed from '@/assets/imgs/flag/flag_red.png'
|
||||
import FlagYellow from '@/assets/imgs/flag/flag_yellow.png'
|
||||
import FlagPurple from '@/assets/imgs/flag/flag_purple.png'
|
||||
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 props = defineProps({
|
||||
schema: {
|
||||
type: Array
|
||||
},
|
||||
userOptions: {
|
||||
type: Array
|
||||
}
|
||||
})
|
||||
|
||||
const formSchema = computed(() => {
|
||||
return [
|
||||
...props.schema,
|
||||
{
|
||||
component: 'Input',
|
||||
label: '诉求',
|
||||
field: 'requirement',
|
||||
componentProps: {
|
||||
type: 'textarea'
|
||||
},
|
||||
colProps: {
|
||||
span: 24
|
||||
}
|
||||
},
|
||||
{
|
||||
component: 'Editor',
|
||||
label: '备注',
|
||||
field: 'remark',
|
||||
colProps: {
|
||||
span: 24
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
@@ -93,47 +149,130 @@ const formLoading = ref(false) // 表单的加载中:1)修改时的数据加
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
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' },
|
||||
consultTime: { required: true, message: '咨询日期不可为空', trigger: 'change' }
|
||||
}
|
||||
|
||||
const tabName = ref('info')
|
||||
|
||||
const followList = ref([])
|
||||
const userOptions = ref([])
|
||||
|
||||
const areaValue = ref('新天地国际')
|
||||
const areaValue = ref('')
|
||||
const areaList = ref([])
|
||||
const address = ref('')
|
||||
const defaultLatLng = ref({
|
||||
lat: 31.86119,
|
||||
lng: 117.283042
|
||||
})
|
||||
const info = ref({})
|
||||
|
||||
const open = async (type, info) => {
|
||||
const diyFieldArr = ref([])
|
||||
|
||||
const open = async (type, id) => {
|
||||
dialogVisible.value = true
|
||||
tabName.value = 'info'
|
||||
dialogTitle.value = type == 'create' ? '新增线索' : '修改线索'
|
||||
formType.value = type
|
||||
resetForm()
|
||||
// 修改时,设置数据
|
||||
if (info) {
|
||||
if (id) {
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = { ...info }
|
||||
const data = await ClueApi.getClue(id)
|
||||
info.value = { ...data, ...data.diyParams }
|
||||
nextTick(() => {
|
||||
formRef.value.setValues(data)
|
||||
followList.value = data.followUser
|
||||
address.value = data.address || ''
|
||||
formRef.value.setValues(info.value)
|
||||
})
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
} else {
|
||||
followList.value = []
|
||||
address.value = ''
|
||||
defaultLatLng.value = {
|
||||
lat: 31.86119,
|
||||
lng: 117.283042
|
||||
}
|
||||
if (!dialogMap.value) {
|
||||
}
|
||||
if (appStore.getAppInfo?.instanceType == 1 && !dialogMap.value) {
|
||||
nextTick(() => {
|
||||
initMap()
|
||||
remoteMethod('新天地国际')
|
||||
getSchoolPlace()
|
||||
initMap(info.value)
|
||||
remoteMethod(address.value)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
function handleSave() {
|
||||
console.log('保存')
|
||||
function resetForm() {
|
||||
info.value.address = undefined
|
||||
info.value.lat = undefined
|
||||
info.value.lng = undefined
|
||||
info.value.followUsers = []
|
||||
info.value.diyParams = {}
|
||||
}
|
||||
|
||||
const placeList = ref([])
|
||||
function getSchoolPlace() {
|
||||
getPlaceList().then((data) => {
|
||||
placeList.value = data.placeList
|
||||
})
|
||||
}
|
||||
|
||||
const emit = defineEmits(['success'])
|
||||
|
||||
async function handleSave() {
|
||||
// 校验表单
|
||||
if (!formRef.value) return
|
||||
const valid = await formRef.value.getElFormRef().validate()
|
||||
if (!valid) return
|
||||
if (followList.value && followList.value.length && followList.value.some((it) => !it.userId)) {
|
||||
message.info('请将跟进人填写完整!')
|
||||
return
|
||||
}
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
let params = { ...formRef.value.formModel, address: address.value }
|
||||
params.lat = defaultLatLng.value.lat
|
||||
params.lng = defaultLatLng.value.lng
|
||||
params.followUsers = [...followList.value]
|
||||
params.diyParams = {}
|
||||
diyFieldArr.value.map((it) => {
|
||||
params.diyParams[it.field] = undefined
|
||||
})
|
||||
for (const key in params.diyParams) {
|
||||
if (Object.hasOwnProperty.call(params, key)) {
|
||||
params.diyParams[key] = params[key]
|
||||
}
|
||||
}
|
||||
if (formType.value === 'create') {
|
||||
await ClueApi.createClue(params)
|
||||
message.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await ClueApi.updateClue(params)
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function handleAppendFollow() {
|
||||
followList.value.push({
|
||||
followUser: undefined,
|
||||
nextFollowTime: formatDate(new Date())
|
||||
userId: undefined,
|
||||
followTime: formatDate(new Date()),
|
||||
editable: true
|
||||
})
|
||||
}
|
||||
function handleRemove(index) {
|
||||
@@ -145,28 +284,57 @@ const dialogMap = ref(null)
|
||||
const aMap = ref(null)
|
||||
let AutoComplete = ref(null)
|
||||
let geoCoder = ref(null)
|
||||
function initMap() {
|
||||
|
||||
function initMap(data) {
|
||||
AMapLoader.load({
|
||||
key: '2ffb0e2ea90b1df0b8be48ed66e18fc8', //设置您的key
|
||||
version: '2.0',
|
||||
plugins: ['AMap.Geocoder', 'AMap.AutoComplete']
|
||||
}).then((AMap) => {
|
||||
aMap.value = AMap
|
||||
if (data.lng || data.lat) {
|
||||
defaultLatLng.value = {
|
||||
lng: data.lng,
|
||||
lat: data.lat
|
||||
}
|
||||
}
|
||||
dialogMap.value = new AMap.Map('dialogMap', {
|
||||
zoom: 12,
|
||||
zooms: [2, 22],
|
||||
center: [117.283042, 31.86119]
|
||||
center: [defaultLatLng.value.lng, defaultLatLng.value.lat]
|
||||
})
|
||||
if (data.lng || data.lat) {
|
||||
addmark(data.lng, data.lat, AMap)
|
||||
}
|
||||
AutoComplete.value = new AMap.AutoComplete({
|
||||
city: '全国'
|
||||
})
|
||||
geoCoder.value = new AMap.Geocoder()
|
||||
dialogMap.value.on('click', (e) => {
|
||||
defaultLatLng.value = {
|
||||
lng: e.lnglat.getLng(),
|
||||
lat: e.lnglat.getLat()
|
||||
}
|
||||
addmark(e.lnglat.getLng(), e.lnglat.getLat(), AMap)
|
||||
regeoCode(e.lnglat.getLng(), e.lnglat.getLat())
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function regeoCode(lng, lat) {
|
||||
try {
|
||||
geoCoder.value.getAddress([lng, lat], (status, result) => {
|
||||
if (status === 'complete' && result.regeocode) {
|
||||
address.value = result.regeocode.formattedAddress
|
||||
} else {
|
||||
message.error('根据经纬度查询地址失败')
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
let marker = ref(null)
|
||||
function addmark(lat, lng, AMap) {
|
||||
marker.value && removeMarker()
|
||||
@@ -186,29 +354,30 @@ const showSchool = ref(false)
|
||||
const schoolMarkers = ref([])
|
||||
function handleShowSchool() {
|
||||
if (showSchool.value) {
|
||||
let marker1 = new aMap.value.Marker({
|
||||
const flagMap = {
|
||||
red: FlagRed,
|
||||
yellow: FlagYellow,
|
||||
purple: FlagPurple,
|
||||
green: FlagGreen,
|
||||
blue: FlagBlue,
|
||||
black: FlagBlack
|
||||
}
|
||||
schoolMarkers.value = []
|
||||
for (let i = 0; i < placeList.value.length; i++) {
|
||||
const place = placeList.value[i]
|
||||
const marker = new aMap.value.Marker({
|
||||
map: dialogMap.value,
|
||||
position: [117.258001, 31.895216],
|
||||
position: [place.lng, place.lat],
|
||||
label: {
|
||||
content: '慧安驾校桃花社区训练基地',
|
||||
content: place.name,
|
||||
direction: 'left'
|
||||
},
|
||||
icon: ImgFlag,
|
||||
// extData: element,
|
||||
icon: flagMap[place.flagColor || 'red'],
|
||||
extData: place,
|
||||
clickable: true
|
||||
})
|
||||
let marker2 = new aMap.value.Marker({
|
||||
map: dialogMap.value,
|
||||
position: [117.286731, 31.902396],
|
||||
label: {
|
||||
content: '(皖西)瑞星驾校总校(D)',
|
||||
direction: 'left'
|
||||
},
|
||||
icon: ImgFlag,
|
||||
// extData: element,
|
||||
clickable: true
|
||||
})
|
||||
schoolMarkers.value = [marker1, marker2]
|
||||
schoolMarkers.value.push(marker)
|
||||
}
|
||||
} else {
|
||||
dialogMap.value.remove(schoolMarkers.value)
|
||||
}
|
||||
@@ -231,8 +400,24 @@ function currentSelect(val) {
|
||||
if (area) {
|
||||
addmark(area.location?.lng, area.location?.lat, aMap.value)
|
||||
dialogMap.value.setCenter([area.location?.lng, area.location?.lat], '', 500)
|
||||
regeoCode(area.location?.lng, area.location?.lat)
|
||||
}
|
||||
}
|
||||
|
||||
function destroyMap() {
|
||||
dialogMap.value = null
|
||||
aMap.value = null
|
||||
}
|
||||
|
||||
function getDiyList() {
|
||||
getDiyFieldList().then((data) => {
|
||||
diyFieldArr.value = data
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getDiyList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -1,24 +1,56 @@
|
||||
<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"
|
||||
v-model="skillSearch.question"
|
||||
placeholder="请输入关键字查询关键话术"
|
||||
clearable
|
||||
@keyup.enter="filterList"
|
||||
@keyup.enter="getSkillList"
|
||||
/>
|
||||
<el-collapse v-model="activeQues" :accordion="false">
|
||||
<el-collapse-item
|
||||
v-for="item in showList"
|
||||
v-for="item in skillList"
|
||||
:key="item.skillId"
|
||||
:title="item.question"
|
||||
:name="item.skillId"
|
||||
@@ -26,247 +58,100 @@
|
||||
<div v-dompurify-html="item.content"></div>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
<Pagination
|
||||
v-model:limit="skillSearch.pageSize"
|
||||
v-model:page="skillSearch.pageNo"
|
||||
:total="skillCount"
|
||||
small
|
||||
layout="total, prev, pager, next, jumper"
|
||||
@pagination="getSkillList"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<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 { getSkillPage } from '@/api/clue/skill'
|
||||
import { createFollow } from '@/api/clue/followRecord'
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
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) {
|
||||
dialogTitle.value = '新增跟进记录'
|
||||
resetForm(clueId)
|
||||
}
|
||||
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
function resetForm(clueId) {
|
||||
form.value = {
|
||||
clueId,
|
||||
operateTime: undefined,
|
||||
nextFollowTime: undefined,
|
||||
content: undefined
|
||||
}
|
||||
}
|
||||
|
||||
const skillList = ref([])
|
||||
const activeQues = ref('')
|
||||
|
||||
const skillSearch = ref({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
question: undefined
|
||||
})
|
||||
|
||||
const skillCount = ref(0)
|
||||
|
||||
function getSkillList() {
|
||||
getSkillPage(skillSearch.value).then((data) => {
|
||||
skillList.value = data.list
|
||||
skillCount.value = data.total
|
||||
})
|
||||
}
|
||||
|
||||
async function handleSave() {
|
||||
// 校验表单
|
||||
if (!formRef.value) return
|
||||
const valid = await formRef.value.validate()
|
||||
if (!valid) return
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = { ...info }
|
||||
formRef.value.setValues(data)
|
||||
await createFollow(form.value)
|
||||
message.success(t('common.createSuccess'))
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
const resultList = [
|
||||
{
|
||||
searchValue: null,
|
||||
createBy: null,
|
||||
createTime: null,
|
||||
updateBy: null,
|
||||
updateTime: null,
|
||||
remark: null,
|
||||
params: {},
|
||||
orderName: null,
|
||||
orderType: null,
|
||||
skillId: 1,
|
||||
deptId: 167,
|
||||
question: '暂时先看题的',
|
||||
skillKey: '先看科目一',
|
||||
keyList: ['先看科目一'],
|
||||
content:
|
||||
'<p>科目一的题目不难的,等会可以加个微信给您发一些做题技巧,我们驾校离你也很近,后面您计划找驾校报名了可以接您来我们驾校实地看看的</p>',
|
||||
status: 2
|
||||
},
|
||||
{
|
||||
searchValue: null,
|
||||
createBy: null,
|
||||
createTime: null,
|
||||
updateBy: null,
|
||||
updateTime: null,
|
||||
remark: null,
|
||||
params: {},
|
||||
orderName: null,
|
||||
orderType: null,
|
||||
skillId: 2,
|
||||
deptId: 167,
|
||||
question: '你们怎么练车的',
|
||||
skillKey: '练车人数',
|
||||
keyList: ['练车人数'],
|
||||
content:
|
||||
'<p>我们驾校这边练车是自己提前在微信上跟教练约时间来练车的,科二练车的时候到你练车了是你一个人一辆车,教练一对一教学的,也可以一对二,不用排队等的</p>',
|
||||
status: 2
|
||||
},
|
||||
{
|
||||
searchValue: null,
|
||||
createBy: null,
|
||||
createTime: null,
|
||||
updateBy: null,
|
||||
updateTime: null,
|
||||
remark: null,
|
||||
params: {},
|
||||
orderName: null,
|
||||
orderType: null,
|
||||
skillId: 3,
|
||||
deptId: 167,
|
||||
question: '教练怎么样 凶嘛',
|
||||
skillKey: '教练凶',
|
||||
keyList: ['教练凶'],
|
||||
content:
|
||||
'<p>我们驾校教练都是驾校本部教练,也都是经过正规培训上岗的,脾气比较温和幽默的,不存在凶学员的情况,学车氛围也比较好;学员也不需要给教练送礼的</p>',
|
||||
status: 2
|
||||
},
|
||||
{
|
||||
searchValue: null,
|
||||
createBy: null,
|
||||
createTime: null,
|
||||
updateBy: null,
|
||||
updateTime: null,
|
||||
remark: null,
|
||||
params: {},
|
||||
orderName: null,
|
||||
orderType: null,
|
||||
skillId: 4,
|
||||
deptId: 167,
|
||||
question: '驾校通过率咋样',
|
||||
skillKey: '通过率',
|
||||
keyList: ['通过率'],
|
||||
content:
|
||||
'<p>我们驾校在合肥也是老牌驾校的了,在合肥干了十几年了,并且拥有自家考场,科二科三考试都在我们自家考场考试,所以通过率一直非常高</p>',
|
||||
status: 2
|
||||
},
|
||||
{
|
||||
searchValue: null,
|
||||
createBy: null,
|
||||
createTime: null,
|
||||
updateBy: null,
|
||||
updateTime: null,
|
||||
remark: null,
|
||||
params: {},
|
||||
orderName: null,
|
||||
orderType: null,
|
||||
skillId: 5,
|
||||
deptId: 167,
|
||||
question: '可以晚上练车吗?',
|
||||
skillKey: '晚上练车',
|
||||
keyList: ['晚上练车'],
|
||||
content:
|
||||
'<p>我们驾校练车时间是根据学员的时间来安排的,但是晚上练车视线不太好,因为考试的时候也是白天嘛,建议最好是白天过来练车,然后等熟练了后期可以让教练加加班晚上</p>',
|
||||
status: 2
|
||||
},
|
||||
{
|
||||
searchValue: null,
|
||||
createBy: null,
|
||||
createTime: null,
|
||||
updateBy: null,
|
||||
updateTime: null,
|
||||
remark: null,
|
||||
params: {},
|
||||
orderName: null,
|
||||
orderType: null,
|
||||
skillId: 6,
|
||||
deptId: 167,
|
||||
question: '大学生有优惠嘛',
|
||||
skillKey: '学生优惠',
|
||||
keyList: ['学生优惠'],
|
||||
content: '<p>学生我们驾校是有优惠的,而且我们驾校有专设学生班,练车不用排队,随到随练的</p>',
|
||||
status: 2
|
||||
},
|
||||
{
|
||||
searchValue: null,
|
||||
createBy: null,
|
||||
createTime: null,
|
||||
updateBy: null,
|
||||
updateTime: null,
|
||||
remark: null,
|
||||
params: {},
|
||||
orderName: null,
|
||||
orderType: null,
|
||||
skillId: 7,
|
||||
deptId: 167,
|
||||
question: '拿证时间多久',
|
||||
skillKey: '拿证时间',
|
||||
keyList: ['拿证时间'],
|
||||
content: '<p>C1手动挡正常拿证时间45天左右拿证,C2自动挡正常35天左右拿证</p>',
|
||||
status: 2
|
||||
},
|
||||
{
|
||||
searchValue: null,
|
||||
createBy: null,
|
||||
createTime: null,
|
||||
updateBy: null,
|
||||
updateTime: null,
|
||||
remark: null,
|
||||
params: {},
|
||||
orderName: null,
|
||||
orderType: null,
|
||||
skillId: 8,
|
||||
deptId: 167,
|
||||
question: '驾校有熟人',
|
||||
skillKey: '熟人',
|
||||
keyList: ['熟人'],
|
||||
content:
|
||||
'<p>你朋友认识的人是教练还是驾校里面工作人员呢,驾校场地在哪呢 学车肯定是要对比的 要个近的合适的,我们驾校离你很近,而且学费目前也在做活动比较优惠,您可以对比一下的,找驾校也是可以自己来现场看过后再决定的</p>',
|
||||
status: 2
|
||||
},
|
||||
{
|
||||
searchValue: null,
|
||||
createBy: null,
|
||||
createTime: null,
|
||||
updateBy: null,
|
||||
updateTime: null,
|
||||
remark: null,
|
||||
params: {},
|
||||
orderName: null,
|
||||
orderType: null,
|
||||
skillId: 9,
|
||||
deptId: 167,
|
||||
question: '考试地点在哪',
|
||||
skillKey: '考试地点',
|
||||
keyList: ['考试地点'],
|
||||
content:
|
||||
'<p>科一和科四是统一要去滨湖车管所考试的,科二科三合肥市总共六个考场,科二和科三考试是就近安排考场考试的,这样考试也方便些</p>',
|
||||
status: 2
|
||||
},
|
||||
{
|
||||
searchValue: null,
|
||||
createBy: null,
|
||||
createTime: null,
|
||||
updateBy: null,
|
||||
updateTime: null,
|
||||
remark: null,
|
||||
params: {},
|
||||
orderName: null,
|
||||
orderType: null,
|
||||
skillId: 10,
|
||||
deptId: 167,
|
||||
question: '没时间练车',
|
||||
skillKey: '没时间学车',
|
||||
keyList: ['没时间学车'],
|
||||
content:
|
||||
'<p>我们驾校练车时间根据学员时间来安排,并且也不用每天都来的,一周抽时间来练个三四次就可以的,每次来一个小时这样,科二来个十来次 科三来个十来次就行,耽误不了你太多时间的</p>',
|
||||
status: 2
|
||||
}
|
||||
]
|
||||
|
||||
const showList = ref(resultList)
|
||||
const keyword = ref('')
|
||||
const activeQues = ref('')
|
||||
|
||||
function filterList() {
|
||||
showList.value = resultList.filter((it) => it.question.includes(keyword.value))
|
||||
}
|
||||
|
||||
function handleSave() {
|
||||
console.log('保存成功')
|
||||
dialogVisible.value = false
|
||||
}
|
||||
onMounted(() => {
|
||||
getSkillList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
||||
@@ -15,12 +15,12 @@
|
||||
<div class="flex justify-between" style="height: 32px">
|
||||
<div class="flex" style="align-items: center">
|
||||
<b class="mr-5px text-24px">{{ info.name }}</b>
|
||||
<div class="mr-5px text-16px">1888888888</div>
|
||||
<el-tag type="success">A高意向</el-tag>
|
||||
<div class="mr-5px text-16px">{{ info.phone }}</div>
|
||||
<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>
|
||||
@@ -39,55 +39,26 @@
|
||||
<el-tab-pane label="跟进记录" name="followRecord">
|
||||
<el-button class="mb-10px" type="primary" @click="addFollow">添加跟进记录</el-button>
|
||||
<el-timeline>
|
||||
<el-timeline-item timestamp="2024-04-01" placement="top">
|
||||
<el-timeline-item
|
||||
v-for="item in followRecordList"
|
||||
:key="item.recordId"
|
||||
:timestamp="item.operateDate"
|
||||
placement="top"
|
||||
>
|
||||
<el-card shadow="always" :body-style="{ padding: '10px' }">
|
||||
<div>
|
||||
<div class="flex justify-between" style="align-items: center">
|
||||
<div class="flex align-baseline">
|
||||
<b class="text-18px">张三</b>
|
||||
<span class="text-14 ml-10px">2024-04-01 09:00:00</span>
|
||||
</div>
|
||||
<div>
|
||||
<el-button type="primary" plain @click="updateFollow()">修改</el-button>
|
||||
<el-button type="danger" plain>删除</el-button>
|
||||
<b class="text-18px" style="line-height: 36px">{{ item.operateUserName }}</b>
|
||||
</div>
|
||||
</div>
|
||||
<div>{{ followContent }}</div>
|
||||
<div>{{ item.content }}</div>
|
||||
<div class="flex mt-10px" style="align-items: center">
|
||||
<div class="flex" style="color: #666; align-items: center">
|
||||
<Icon icon="ep:clock" class="mr-5px" />
|
||||
<span>本次跟进时间:2024-04-05 10:57</span>
|
||||
<span>本次跟进时间:{{ item.followTime }}</span>
|
||||
</div>
|
||||
<div class="flex ml-50px" style="color: #666; align-items: center">
|
||||
<Icon icon="ep:clock" class="mr-5px" />
|
||||
<span>下次跟进时间:2024-04-10 10:00</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
<el-timeline-item timestamp="2024-02-01" placement="top">
|
||||
<el-card shadow="always" :body-style="{ padding: '10px' }">
|
||||
<div>
|
||||
<div class="flex justify-between" style="align-items: center">
|
||||
<div class="flex align-baseline">
|
||||
<b class="text-18px">李四</b>
|
||||
<span class="text-14 ml-10px">2024-02-01 09:00:00</span>
|
||||
</div>
|
||||
<div>
|
||||
<el-button type="primary" plain>修改</el-button>
|
||||
<el-button type="danger" plain>删除</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div>{{ followContent2 }}</div>
|
||||
<div class="flex mt-10px" style="align-items: center">
|
||||
<div class="flex" style="color: #666; align-items: center">
|
||||
<Icon icon="ep:clock" class="mr-5px" />
|
||||
<span>本次跟进时间:2024-02-05 10:57</span>
|
||||
</div>
|
||||
<div class="flex ml-50px" style="color: #666; align-items: center">
|
||||
<Icon icon="ep:clock" class="mr-5px" />
|
||||
<span>下次跟进时间:2024-02-10 10:00</span>
|
||||
<span>下次跟进时间:{{ item.nextFollowTime }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -96,74 +67,33 @@
|
||||
</el-timeline>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="详细信息" name="infoDetail">
|
||||
<Descriptions :data="info" :schema="schema" :columns="2" />
|
||||
<el-checkbox v-model="showSchool" :label="true" @change="handleShowSchool"
|
||||
>展示场地</el-checkbox
|
||||
>
|
||||
<Descriptions :data="info" :schema="showSchema" :columns="2" />
|
||||
<div v-if="appStore.getAppInfo?.instanceType == 1">
|
||||
<el-checkbox v-model="showSchool" :label="true" @change="handleShowSchool">
|
||||
展示场地
|
||||
</el-checkbox>
|
||||
<div id="dialogMap" class="mt-20px" style="height: 400px; width: 100%"></div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="操作记录" name="operateRecord">
|
||||
<el-timeline>
|
||||
<el-timeline-item timestamp="2024-04-01" placement="top">
|
||||
<el-timeline-item
|
||||
v-for="item in operateRecordList"
|
||||
:key="item.recordId"
|
||||
:timestamp="item.operateDate"
|
||||
placement="top"
|
||||
>
|
||||
<el-card shadow="always" :body-style="{ padding: '10px' }">
|
||||
<div style="color: #666">
|
||||
<div class="pt-5px">
|
||||
<span>操作人:机器人1号</span>
|
||||
<span>操作人:{{ item.operateUserName }}</span>
|
||||
</div>
|
||||
<div class="pt-5px pb-5px">
|
||||
<span>成交线索</span>
|
||||
<span>{{ item.content }}</span>
|
||||
</div>
|
||||
<div class="flex" style="align-items: center">
|
||||
<Icon icon="ep:clock" class="mr-5px" />
|
||||
<span>操作时间:2024-04-05 10:57</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
<el-timeline-item timestamp="2024-02-01" placement="top">
|
||||
<el-card shadow="always" :body-style="{ padding: '10px' }">
|
||||
<div style="color: #666">
|
||||
<div class="pt-5px">
|
||||
<span>操作人:机器人2号</span>
|
||||
</div>
|
||||
<div class="pt-5px pb-5px">
|
||||
<span>修改意向状态为:高意向</span>
|
||||
</div>
|
||||
<div class="flex" style="align-items: center">
|
||||
<Icon icon="ep:clock" class="mr-5px" />
|
||||
<span>操作时间:2024-04-1 10:57</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
<el-timeline-item timestamp="2024-02-01" placement="top">
|
||||
<el-card shadow="always" :body-style="{ padding: '10px' }">
|
||||
<div style="color: #666">
|
||||
<div class="pt-5px">
|
||||
<span>操作人:机器人1号</span>
|
||||
</div>
|
||||
<div class="pt-5px pb-5px">
|
||||
<span>跟进线索</span>
|
||||
</div>
|
||||
<div class="flex" style="align-items: center">
|
||||
<Icon icon="ep:clock" class="mr-5px" />
|
||||
<span>操作时间:2024-04-1 10:57</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
<el-timeline-item timestamp="2024-02-01" placement="top">
|
||||
<el-card shadow="always" :body-style="{ padding: '10px' }">
|
||||
<div style="color: #666">
|
||||
<div class="pt-5px">
|
||||
<span>操作人:机器人2号</span>
|
||||
</div>
|
||||
<div class="pt-5px pb-5px">
|
||||
<span>创建线索</span>
|
||||
</div>
|
||||
<div class="flex" style="align-items: center">
|
||||
<Icon icon="ep:clock" class="mr-5px" />
|
||||
<span>操作时间:2024-04-1 10:57</span>
|
||||
<span>操作时间:{{ item.followTime }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
@@ -172,51 +102,57 @@
|
||||
</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'
|
||||
import { getPlaceList } from '@/api/school/place'
|
||||
|
||||
import DialogFollow from './DialogFollow.vue'
|
||||
import ImgFlag from '@/assets/imgs/flag/position_blue.png'
|
||||
import AMapLoader from '@amap/amap-jsapi-loader'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
|
||||
import ImgPostion from '@/assets/imgs/flag/position_blue.png'
|
||||
import FlagRed from '@/assets/imgs/flag/flag_red.png'
|
||||
import FlagYellow from '@/assets/imgs/flag/flag_yellow.png'
|
||||
import FlagPurple from '@/assets/imgs/flag/flag_purple.png'
|
||||
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)
|
||||
const info = ref(null)
|
||||
const loading = ref(false)
|
||||
|
||||
const schema = ref([
|
||||
const props = defineProps({
|
||||
schema: {
|
||||
type: Array
|
||||
}
|
||||
})
|
||||
|
||||
const showSchema = computed(() => {
|
||||
const arr = [
|
||||
{
|
||||
field: 'name',
|
||||
label: '线索名称'
|
||||
},
|
||||
{
|
||||
field: 'contact',
|
||||
label: '联系方式'
|
||||
},
|
||||
{
|
||||
field: 'supplier',
|
||||
label: '意向状态'
|
||||
},
|
||||
{
|
||||
field: 'supplier',
|
||||
label: '创建时间'
|
||||
},
|
||||
{
|
||||
field: 'purchaseCount',
|
||||
field: 'requirement',
|
||||
label: '诉求',
|
||||
span: 2
|
||||
},
|
||||
{
|
||||
field: 'remark',
|
||||
label: '备注',
|
||||
isEditor: true,
|
||||
span: 2
|
||||
}
|
||||
])
|
||||
|
||||
const followContent = `<p style="color: red;">这是本次跟进的内容。</p><br/><p>我还能放图片,但需要你自己排版:</p><br/><img style="width: 200px;" src="https://q6.itc.cn/images01/20240407/0e6be21aebc847648109304f20370790.jpeg">`
|
||||
|
||||
const followContent2 = `<p style="color: red;">这是本次跟进的内容。</p>`
|
||||
]
|
||||
return [...props.schema, ...arr]
|
||||
})
|
||||
|
||||
const followList = ref([
|
||||
{
|
||||
@@ -233,67 +169,138 @@ const followList = ref([
|
||||
}
|
||||
])
|
||||
|
||||
const followRecordList = ref([])
|
||||
const operateRecordList = ref([])
|
||||
|
||||
// 地图相关
|
||||
const dialogMap = ref(null)
|
||||
const aMap = ref(null)
|
||||
|
||||
function open(row) {
|
||||
info.value = row
|
||||
show.value = true
|
||||
if (!dialogMap.value) {
|
||||
nextTick(() => {
|
||||
initMap()
|
||||
const clueId = ref('')
|
||||
|
||||
function getFollowList() {
|
||||
FollowApi.getFollowList({ clueId: clueId.value }).then((data) => {
|
||||
followRecordList.value = data.map((item) => ({
|
||||
operateUserName: item.operateUserName,
|
||||
content: item.content,
|
||||
operateDate: formatDate(item.operateTime),
|
||||
followTime: formatDate(item.operateTime, 'YYYY-MM-DD HH:mm'),
|
||||
nextFollowTime: formatDate(item.nextFollowTime)
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
async function open(id) {
|
||||
clueId.value = id
|
||||
try {
|
||||
getFollowList()
|
||||
ClueApi.getOpearateRecord({ clueId: id }).then((data) => {
|
||||
operateRecordList.value = data.map((item) => ({
|
||||
operateUserName: item.operateUserName,
|
||||
content: item.content,
|
||||
operateDate: formatDate(item.operateTime),
|
||||
followTime: formatDate(item.operateTime, 'YYYY-MM-DD HH:mm')
|
||||
}))
|
||||
})
|
||||
const data = await ClueApi.getClue(id)
|
||||
info.value = { ...data, ...data.diyParams }
|
||||
show.value = true
|
||||
infoIndex.value = 'followRecord'
|
||||
|
||||
if (appStore.getAppInfo?.instanceType == 1 && !dialogMap.value) {
|
||||
nextTick(() => {
|
||||
getSchoolPlace()
|
||||
initMap(info.value)
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
function initMap() {
|
||||
const placeList = ref([])
|
||||
function getSchoolPlace() {
|
||||
getPlaceList().then((data) => {
|
||||
placeList.value = data.placeList
|
||||
})
|
||||
}
|
||||
|
||||
const defaultLatLng = ref({
|
||||
lat: 31.86119,
|
||||
lng: 117.283042
|
||||
})
|
||||
|
||||
function initMap(data) {
|
||||
AMapLoader.load({
|
||||
key: '2ffb0e2ea90b1df0b8be48ed66e18fc8', //设置您的key
|
||||
version: '2.0'
|
||||
}).then((AMap) => {
|
||||
aMap.value = AMap
|
||||
if (data.lng || data.lat) {
|
||||
defaultLatLng.value = {
|
||||
lng: data.lng,
|
||||
lat: data.lat
|
||||
}
|
||||
}
|
||||
dialogMap.value = new AMap.Map('dialogMap', {
|
||||
zoom: 12,
|
||||
zooms: [2, 22],
|
||||
center: [117.283042, 31.86119]
|
||||
center: [defaultLatLng.value.lng, defaultLatLng.value.lat]
|
||||
})
|
||||
if (data.lng || data.lat) {
|
||||
addmark(data.lng, data.lat, AMap)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const showSchool = ref(false)
|
||||
const schoolMarkers = ref([])
|
||||
|
||||
function handleShowSchool() {
|
||||
if (showSchool.value) {
|
||||
let marker1 = new aMap.value.Marker({
|
||||
const flagMap = {
|
||||
red: FlagRed,
|
||||
yellow: FlagYellow,
|
||||
purple: FlagPurple,
|
||||
green: FlagGreen,
|
||||
blue: FlagBlue,
|
||||
black: FlagBlack
|
||||
}
|
||||
schoolMarkers.value = []
|
||||
for (let i = 0; i < placeList.value.length; i++) {
|
||||
const place = placeList.value[i]
|
||||
const marker = new aMap.value.Marker({
|
||||
map: dialogMap.value,
|
||||
position: [117.258001, 31.895216],
|
||||
position: [place.lng, place.lat],
|
||||
label: {
|
||||
content: '慧安驾校桃花社区训练基地',
|
||||
content: place.name,
|
||||
direction: 'left'
|
||||
},
|
||||
icon: ImgFlag,
|
||||
// extData: element,
|
||||
icon: flagMap[place.flagColor || 'red'],
|
||||
extData: place,
|
||||
clickable: true
|
||||
})
|
||||
let marker2 = new aMap.value.Marker({
|
||||
map: dialogMap.value,
|
||||
position: [117.286731, 31.902396],
|
||||
label: {
|
||||
content: '(皖西)瑞星驾校总校(D)',
|
||||
direction: 'left'
|
||||
},
|
||||
icon: ImgFlag,
|
||||
// extData: element,
|
||||
clickable: true
|
||||
})
|
||||
schoolMarkers.value = [marker1, marker2]
|
||||
schoolMarkers.value.push(marker)
|
||||
}
|
||||
} else {
|
||||
dialogMap.value.remove(schoolMarkers.value)
|
||||
}
|
||||
}
|
||||
|
||||
let marker = ref(null)
|
||||
function addmark(lat, lng, AMap) {
|
||||
marker.value && removeMarker()
|
||||
marker.value = new AMap.Marker({
|
||||
position: new AMap.LngLat(lat, lng),
|
||||
zoom: 13,
|
||||
icon: ImgPostion
|
||||
})
|
||||
dialogMap.value.add(marker.value)
|
||||
dialogMap.value.setCenter([lat, lng], '', 500)
|
||||
}
|
||||
function removeMarker() {
|
||||
dialogMap.value.remove(marker.value)
|
||||
}
|
||||
|
||||
const infoIndex = ref('followRecord')
|
||||
|
||||
defineExpose({
|
||||
@@ -302,16 +309,36 @@ defineExpose({
|
||||
|
||||
const followRef = ref()
|
||||
function addFollow() {
|
||||
followRef.value.open('create', null)
|
||||
}
|
||||
function updateFollow() {
|
||||
followRef.value.open('update', { nextFollowTime: '2024-04-01 12:12' })
|
||||
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)
|
||||
@@ -1,200 +0,0 @@
|
||||
// import { CrudSchema } from '@/hooks/web/useCrudSchemas'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
// import * as MailAccountApi from '@/api/system/mail/account'
|
||||
|
||||
// 表单校验
|
||||
export const rules = reactive({
|
||||
username: [required],
|
||||
password: [required],
|
||||
host: [required],
|
||||
port: [required],
|
||||
sslEnable: [required]
|
||||
})
|
||||
|
||||
// const userList = await MailAccountApi.getSimpleMailAccountList()
|
||||
const userList = []
|
||||
|
||||
// CrudSchema:https://doc.iocoder.cn/vue3/crud-schema/
|
||||
const crudSchemas = reactive([
|
||||
{
|
||||
label: '线索名称',
|
||||
field: 'name',
|
||||
isSearch: true,
|
||||
isTable: true
|
||||
},
|
||||
{
|
||||
label: '联系方式',
|
||||
field: 'contact',
|
||||
isSearch: true,
|
||||
isTable: true
|
||||
},
|
||||
{
|
||||
label: '线索位置',
|
||||
field: 'address',
|
||||
isSearch: true,
|
||||
isTable: true,
|
||||
isForm: false
|
||||
},
|
||||
{
|
||||
label: '线索来源',
|
||||
field: 'resource',
|
||||
isSearch: true,
|
||||
isTable: true,
|
||||
search: {
|
||||
component: 'Select',
|
||||
api: () => userList,
|
||||
componentProps: {
|
||||
optionsAlias: {
|
||||
labelField: 'name',
|
||||
valueField: 'id'
|
||||
}
|
||||
}
|
||||
},
|
||||
form: {
|
||||
component: 'Select',
|
||||
api: () => userList,
|
||||
componentProps: {
|
||||
optionsAlias: {
|
||||
labelField: 'name',
|
||||
valueField: 'id'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '意向状态',
|
||||
field: 'intention',
|
||||
isSearch: true,
|
||||
isTable: true,
|
||||
table: {
|
||||
fixed: 'left'
|
||||
},
|
||||
search: {
|
||||
component: 'Select',
|
||||
api: () => userList,
|
||||
componentProps: {
|
||||
optionsAlias: {
|
||||
labelField: 'name',
|
||||
valueField: 'id'
|
||||
}
|
||||
}
|
||||
},
|
||||
form: {
|
||||
component: 'Select',
|
||||
api: () => userList,
|
||||
componentProps: {
|
||||
optionsAlias: {
|
||||
labelField: 'name',
|
||||
valueField: 'id'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '跟进人员',
|
||||
field: 'userId',
|
||||
isSearch: true,
|
||||
isTable: true,
|
||||
isForm: false,
|
||||
search: {
|
||||
component: 'Select',
|
||||
api: () => userList,
|
||||
componentProps: {
|
||||
optionsAlias: {
|
||||
labelField: 'name',
|
||||
valueField: 'id'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '下次跟进时间',
|
||||
field: 'nextTime',
|
||||
isSearch: true,
|
||||
isTable: true,
|
||||
isForm: false,
|
||||
formatter: dateFormatter,
|
||||
detail: {
|
||||
dateFormat: 'YYYY-MM-DD'
|
||||
},
|
||||
search: {
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
type: 'daterange',
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
startPlaceholder: '下次跟进时间',
|
||||
endPlaceholder: '下次跟进时间'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '诉求',
|
||||
field: 'need',
|
||||
isTable: true,
|
||||
form: {
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
type: 'textarea'
|
||||
},
|
||||
colProps: {
|
||||
span: 24
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '最新跟进时间',
|
||||
field: 'latestFollowTime',
|
||||
isTable: true,
|
||||
isForm: false
|
||||
},
|
||||
{
|
||||
label: '创建时间',
|
||||
field: 'createTime',
|
||||
isSearch: true,
|
||||
isTable: true,
|
||||
table: {
|
||||
fixed: 'left'
|
||||
},
|
||||
formatter: dateFormatter,
|
||||
detail: {
|
||||
dateFormat: 'YYYY-MM-DD'
|
||||
},
|
||||
search: {
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
type: 'daterange',
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
startPlaceholder: '创建时间',
|
||||
endPlaceholder: '创建时间'
|
||||
}
|
||||
},
|
||||
form: {
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
placeholder: '创建时间'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '跟进记录',
|
||||
field: 'followRecord',
|
||||
isTable: true
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
field: 'remark',
|
||||
isTable: true,
|
||||
form: {
|
||||
component: 'Editor',
|
||||
colProps: {
|
||||
span: 24
|
||||
}
|
||||
}
|
||||
}
|
||||
])
|
||||
export const { allSchemas } = useCrudSchemas(crudSchemas)
|
||||
@@ -1,30 +1,33 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="relative">
|
||||
<el-tabs v-model="searchForm.mode" size="small">
|
||||
<el-tabs v-model="queryType" size="small" @tab-change="getTableList">
|
||||
<el-tab-pane label="全部" name="0" />
|
||||
<el-tab-pane name="1">
|
||||
<template #label>
|
||||
<Tooltip message="除了无效线索和已成交的线索" />
|
||||
<el-badge :value="123" :max="9999">
|
||||
<el-badge v-if="clueCount.unSignNum" :value="clueCount.unSignNum" :max="9999">
|
||||
<span class="ml-3px">未成交</span>
|
||||
</el-badge>
|
||||
<span v-else class="ml-3px">未成交</span>
|
||||
</template>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane name="2">
|
||||
<template #label>
|
||||
<Tooltip message="下次跟进时间在今日之前的未成交线索" />
|
||||
<el-badge :value="234" :max="9999">
|
||||
<el-badge v-if="clueCount.followNum" :value="clueCount.followNum" :max="9999">
|
||||
<span class="ml-3px">待跟进</span>
|
||||
</el-badge>
|
||||
<span v-else class="ml-3px">待跟进</span>
|
||||
</template>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane name="3">
|
||||
<template #label>
|
||||
<Tooltip message="只有创建时间,无下次跟进时间的未成交线索" />
|
||||
<el-badge :value="423" :max="9999">
|
||||
<el-badge v-if="clueCount.newNum" :value="clueCount.newNum" :max="9999">
|
||||
<span class="ml-3px">新线索</span>
|
||||
</el-badge>
|
||||
<span v-else class="ml-3px">新线索</span>
|
||||
</template>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="公海" name="4" />
|
||||
@@ -37,7 +40,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<!-- 搜索工作栏 -->
|
||||
<Search :schema="allSchemas.searchSchema" labelWidth="0">
|
||||
<Search v-if="!loading" ref="searchRef" :schema="allSchemas.searchSchema" labelWidth="0">
|
||||
<template #actionMore>
|
||||
<el-button @click="getTableList" v-hasPermi="['clue:pool:search']"> 搜索 </el-button>
|
||||
<el-button @click="resetQuery" v-hasPermi="['clue:pool:reset']"> 重置 </el-button>
|
||||
@@ -45,6 +48,7 @@
|
||||
</Search>
|
||||
<!-- 列表 -->
|
||||
<SSTable
|
||||
v-if="!loading"
|
||||
class="mt-20px"
|
||||
v-model:tableObject="tableObject"
|
||||
:tableColumns="allSchemas.tableColumns"
|
||||
@@ -74,6 +78,9 @@
|
||||
<span>{{ row[item.field] }}</span>
|
||||
<Icon class="ml-5px" icon="ep:phone" @click="makeCall(row.contact)" />
|
||||
</div>
|
||||
<div v-else-if="item.form?.component == 'DatePicker'">
|
||||
<span>{{ formatDate(row[item.field]) }}</span>
|
||||
</div>
|
||||
<span v-else>{{ row[item.field] }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -103,40 +110,79 @@
|
||||
>
|
||||
登记
|
||||
</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>
|
||||
|
||||
<DialogClue ref="formRef" />
|
||||
<DrawerClue ref="drawerRef" />
|
||||
<DialogClue
|
||||
v-if="!loading"
|
||||
ref="formRef"
|
||||
:userOptions="userOptions"
|
||||
:schema="allSchemas.formSchema"
|
||||
@sucess="getTableList"
|
||||
/>
|
||||
<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>
|
||||
|
||||
<script setup name="CluePool">
|
||||
import { allSchemas } from './cluePool.data'
|
||||
import { getSimpleFieldList } from '@/api/clue/clueField'
|
||||
import DialogClue from './Comp/DialogClue.vue'
|
||||
import DrawerClue from './Comp/DrawerClue.vue'
|
||||
import DialogSuccess from './Comp/DialogSuccess.vue'
|
||||
import DialogFollow from './Comp/DialogFollow.vue'
|
||||
import { getSimpleUserList as getUserOption } from '@/api/system/user'
|
||||
|
||||
import { removeNullField } from '@/utils'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
|
||||
import * as ClueApi from '@/api/clue'
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const searchRef = ref()
|
||||
const queryType = ref('2')
|
||||
|
||||
const searchForm = ref({
|
||||
mode: '2'
|
||||
})
|
||||
const formRef = ref()
|
||||
const drawerRef = ref()
|
||||
const successRef = ref()
|
||||
const followRef = ref()
|
||||
|
||||
// const { tableObject, tableMethods } = useTable({
|
||||
// getListApi: MailTemplateApi.getMailTemplatePage, // 分页接口
|
||||
// delListApi: MailTemplateApi.deleteMailTemplate // 删除接口
|
||||
// })
|
||||
const loading = ref(true)
|
||||
|
||||
const allSchemas = ref({})
|
||||
|
||||
async function getCurdSchemas() {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await getSimpleFieldList()
|
||||
allSchemas.value = useCrudSchemas(data).allSchemas
|
||||
} finally {
|
||||
loading.value = false
|
||||
nextTick(() => {
|
||||
getTableList()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const tableObject = ref({
|
||||
tableList: [{ name: '测试', contact: '17318531354' }],
|
||||
tableList: [],
|
||||
loading: false,
|
||||
total: 1,
|
||||
pageSize: 20,
|
||||
@@ -151,15 +197,40 @@ function getCheckedColumns(list) {
|
||||
}
|
||||
|
||||
function resetQuery() {
|
||||
searchForm.value = {
|
||||
pageNo: 1,
|
||||
pageSize: 10
|
||||
}
|
||||
searchRef.value.reset()
|
||||
tableObject.value.currentPage = 1
|
||||
getTableList()
|
||||
}
|
||||
|
||||
function getTableList() {
|
||||
async function getTableList() {
|
||||
// 查询
|
||||
tableObject.value.loading = true
|
||||
getSearchCount()
|
||||
try {
|
||||
const queryParams = await searchRef.value.getFormModel()
|
||||
const params = {
|
||||
...queryParams,
|
||||
pageNo: tableObject.value.currentPage,
|
||||
pageSize: tableObject.value.pageSize,
|
||||
queryType: queryType.value
|
||||
}
|
||||
const data = await ClueApi.getCluePage(removeNullField(params))
|
||||
tableObject.value.tableList = data.list.map((it) => ({ ...it, ...it.diyParams }))
|
||||
tableObject.value.total = data.total
|
||||
} finally {
|
||||
tableObject.value.loading = false
|
||||
}
|
||||
}
|
||||
|
||||
const clueCount = ref({
|
||||
unSignNum: 0,
|
||||
followNum: 0,
|
||||
newNum: 0
|
||||
})
|
||||
function getSearchCount() {
|
||||
ClueApi.getClueCount().then((data) => {
|
||||
clueCount.value = data
|
||||
})
|
||||
}
|
||||
|
||||
// 新增
|
||||
@@ -168,15 +239,15 @@ function handleInsert() {
|
||||
}
|
||||
// 编辑
|
||||
function handleEdit(row) {
|
||||
formRef.value.open('update', row)
|
||||
formRef.value.open('update', row.clueId)
|
||||
}
|
||||
// 详情
|
||||
function handleDetail(row) {
|
||||
drawerRef.value.open(row)
|
||||
drawerRef.value.open(row.clueId)
|
||||
}
|
||||
|
||||
function handleFollow(row) {
|
||||
followRef.value.open('create', row)
|
||||
followRef.value.open(row.clueId)
|
||||
}
|
||||
|
||||
async function makeCall(phone) {
|
||||
@@ -187,6 +258,35 @@ 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
|
||||
})
|
||||
getCurdSchemas()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-table :data="list" border>
|
||||
<el-table v-loading="loading" :data="list" border>
|
||||
<el-table-column type="index" width="50" />
|
||||
<el-table-column label="来源名称" width="200px">
|
||||
<template #default="{ row }">
|
||||
<el-input v-model="row.name" placeholder="请输入" :clearable="false" />
|
||||
<el-input v-model="row.source" placeholder="请输入" :clearable="false" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="渠道" width="300px">
|
||||
@@ -24,14 +24,14 @@
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<span>{{ row.resourceName }}</span>
|
||||
<span>{{ row.channel }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="获取连接" prop="link" />
|
||||
<el-table-column label="获取连接" prop="url" />
|
||||
<el-table-column label="参数详情" prop="params" />
|
||||
<el-table-column label="是否启用" width="100px">
|
||||
<template #default="{ row }">
|
||||
<el-switch v-model="row.inEnable" :active-value="true" :inactive-value="false" />
|
||||
<el-switch v-model="row.status" :active-value="0" :inactive-value="1" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="100px">
|
||||
@@ -49,56 +49,60 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const list = ref([
|
||||
{
|
||||
name: '一点通账号1',
|
||||
resourceName: '驾校一点通',
|
||||
link: 'https://sscrm.ahduima.com/clue/get?cid=1001&aid=1001&res=2',
|
||||
params: '参数详情'
|
||||
},
|
||||
{
|
||||
name: '一点通账号2',
|
||||
resourceName: '驾校一点通',
|
||||
link: 'https://sscrm.ahduima.com/clue/get?cid=1001&aid=1001&res=2',
|
||||
params: '参数详情'
|
||||
},
|
||||
{
|
||||
name: '宝典账号',
|
||||
resourceName: '驾考宝典',
|
||||
link: 'https://sscrm.ahduima.com/clue/get?cid=1001&aid=1001&res=1',
|
||||
params: '参数详情'
|
||||
},
|
||||
{
|
||||
name: '抖音',
|
||||
resourceName: '抖音/开心学车',
|
||||
link: 'https://sscrm.ahduima.com/clue/get?cid=1001&aid=1001&res=3',
|
||||
params: '参数详情'
|
||||
}
|
||||
])
|
||||
<script setup name="ClueSet">
|
||||
import { getClueGainRuleList, deleteClueGainRule } from '@/api/clue/clueGetSet'
|
||||
|
||||
const resourceOptions = ref([
|
||||
{ value: 1, label: '驾考宝典' },
|
||||
{ value: 2, label: '一点通' },
|
||||
{ value: 3, label: '抖音' }
|
||||
])
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const list = ref([])
|
||||
const loading = ref(false)
|
||||
const resourceOptions = ref([])
|
||||
|
||||
async function getList() {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await getClueGainRuleList()
|
||||
list.value = data
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function handleInsert() {
|
||||
list.value.push({ name: '', link: '', edit: true, inEnable: true })
|
||||
list.value.push({ source: '', url: '', edit: true, status: 0 })
|
||||
}
|
||||
|
||||
function onSubmit() {
|
||||
console.log('保存成功')
|
||||
}
|
||||
|
||||
function handleRemove(idx) {
|
||||
async function handleRemove(idx) {
|
||||
if (list.value[idx].ruleId) {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await deleteClueGainRule(list.value[idx].ruleId)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
} else {
|
||||
list.value.splice(idx, 1)
|
||||
}
|
||||
}
|
||||
|
||||
function handleChange(row) {
|
||||
row.link = `https://sscrm.ahduima.com/clue/get?cid=1001&aid=1001&res=${row.resource}`
|
||||
row.url = `https://sscrm.ahduima.com/clue/get?cid=1001&aid=1001&res=${row.resource}`
|
||||
row.params = '参数详情'
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
||||
@@ -2,22 +2,33 @@
|
||||
<div class="flex">
|
||||
<div class="mr-20px" style="width: 500px">
|
||||
<el-input
|
||||
v-model="searchForm.keyword"
|
||||
v-model="searchForm.nickname"
|
||||
placeholder="请输入关键字查询"
|
||||
clearable
|
||||
class="mb-10px"
|
||||
@keyup.enter="getUserList"
|
||||
/>
|
||||
|
||||
<el-table :data="userList" @cell-click="selectUser">
|
||||
<el-table-column prop="name" label="员工姓名" />
|
||||
<el-table-column prop="phone" label="电话" />
|
||||
<el-table-column prop="workNum" label="工号" />
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="userList"
|
||||
:row-class-name="setRowClass"
|
||||
@row-click="handleRowClick"
|
||||
>
|
||||
<el-table-column prop="nickname" label="员工姓名" />
|
||||
<el-table-column prop="mobile" label="电话" />
|
||||
<el-table-column prop="status" label="状态">
|
||||
<template #default="{ row }">
|
||||
{{ ['在职', '离职'][row.status] }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
v-model:limit="searchForm.pageSize"
|
||||
v-model:page="searchForm.pageNum"
|
||||
v-model:page="searchForm.pageNo"
|
||||
small
|
||||
layout="total, prev, pager, next, jumper"
|
||||
:total="total"
|
||||
@pagination="getUserList"
|
||||
/>
|
||||
@@ -65,16 +76,27 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
<script setup name="ClueSend">
|
||||
import { getUserPage } from '@/api/system/user'
|
||||
|
||||
// const message = useMessage() // 消息弹窗
|
||||
// const { t } = useI18n() // 国际化
|
||||
|
||||
const searchForm = ref({
|
||||
keyword: '',
|
||||
nickname: '',
|
||||
pageSize: 20,
|
||||
pageNum: 1
|
||||
pageNo: 1
|
||||
})
|
||||
|
||||
const total = ref(0)
|
||||
const loading = ref(false)
|
||||
const userList = ref([])
|
||||
|
||||
const userList = ref([{ name: '张三', phone: '1888888888', workNum: '202101030001' }])
|
||||
function setRowClass({ row }) {
|
||||
return row.field == currentRowId.value ? 'current-row' : ''
|
||||
}
|
||||
|
||||
const currentRowId = ref('')
|
||||
|
||||
const form = ref({
|
||||
isAuto: 1,
|
||||
@@ -84,8 +106,18 @@ const form = ref({
|
||||
})
|
||||
const rules = ref({})
|
||||
|
||||
function getUserList() {
|
||||
console.log('获取列表')
|
||||
async function getUserList() {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await getUserPage(searchForm.value)
|
||||
userList.value = data.list
|
||||
if (userList.value.length) {
|
||||
handleRowClick(userList.value[0])
|
||||
}
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function onSubmit() {
|
||||
@@ -111,9 +143,14 @@ function resourceCheckedChange(val) {
|
||||
resourceIndeterminate.value = checkedCount > 0 && checkedCount < resourceOptions.value.length
|
||||
}
|
||||
|
||||
function selectUser(row) {
|
||||
console.log(row)
|
||||
function handleRowClick(row) {
|
||||
currentRowId.value = row.ruleId
|
||||
form.value = { ...row }
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getUserList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form ref="queryForm" :model="searchForm" label-width="0" inline>
|
||||
<el-form ref="queryForm" :model="searchForm" label-width="0" inline @submit.prevent>
|
||||
<el-form-item>
|
||||
<el-input
|
||||
v-model="searchForm.name"
|
||||
v-model="searchForm.sourceName"
|
||||
placeholder="请输入名称"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
@@ -22,84 +22,89 @@
|
||||
:tree-props="{ children: 'children' }"
|
||||
>
|
||||
<el-table-column prop="sourceName" label="来源名称" />
|
||||
<el-table-column prop="orderNum" label="排序" width="100px" />
|
||||
<el-table-column prop="sort" label="排序" width="100px" />
|
||||
<el-table-column prop="remark" label="备注" />
|
||||
<el-table-column label="创建时间" prop="createTime" width="180px" />
|
||||
<el-table-column label="创建人" prop="createUser" width="150px" />
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
prop="createTime"
|
||||
width="180px"
|
||||
:formatter="dateFormatter"
|
||||
/>
|
||||
<el-table-column label="状态">
|
||||
<template #default="{ row }">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作">
|
||||
<template #default="scope">
|
||||
<el-button type="primary" text @click="openForm('update', scope.row)">修改</el-button>
|
||||
<el-button type="primary" text @click="openForm('createChildren', scope.row)"
|
||||
>新增</el-button
|
||||
>
|
||||
<el-button type="danger" text @click="handleDelete(scope.row)">删除</el-button>
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" text @click="openForm('update', row)">修改</el-button>
|
||||
<el-button type="primary" text @click="openForm('createChildren', row)"> 新增 </el-button>
|
||||
<el-button type="danger" text @click="handleDelete(row.sourceId)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<Pagination
|
||||
v-model:limit="searchForm.pageSize"
|
||||
v-model:page="searchForm.pageNum"
|
||||
:total="total"
|
||||
@pagination="handleQuery"
|
||||
/>
|
||||
|
||||
<DialogSource ref="sourceDialog" @success="handleQuery" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="ClueSource">
|
||||
import { handleTree } from '@/utils/tree'
|
||||
import * as SourceApi from '@/api/clue/source'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import DialogSource from './DialogSource.vue'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const searchForm = ref({
|
||||
pageNum: 1,
|
||||
pageSize: 20
|
||||
sourceName: undefined
|
||||
})
|
||||
|
||||
const total = ref(0)
|
||||
const sourceDialog = ref()
|
||||
const tableList = ref([])
|
||||
const loading = ref(false)
|
||||
|
||||
function handleQuery() {
|
||||
searchForm.value.pageNum = 1
|
||||
getList()
|
||||
}
|
||||
function resetQuery() {
|
||||
searchForm.value = {
|
||||
question: '',
|
||||
pageSize: 20,
|
||||
pageNum: 1
|
||||
sourceName: ''
|
||||
}
|
||||
getList()
|
||||
}
|
||||
|
||||
function getList() {
|
||||
tableList.value = [
|
||||
{
|
||||
sourceId: 1,
|
||||
sourceName: '测试',
|
||||
level: 1,
|
||||
children: [{ sourceId: 1001, sourceName: '二级来源', level: 2, parentSource: '测试' }]
|
||||
async function getList() {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await SourceApi.getSourcePage(searchForm.value)
|
||||
tableList.value = handleTree(data, 'sourceId')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
function openForm(type, info) {
|
||||
sourceDialog.value.open(type, info)
|
||||
}
|
||||
|
||||
async function handleDelete(row) {
|
||||
async function handleDelete(id) {
|
||||
try {
|
||||
console.log(row)
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
// await UserApi.deleteUser(row.id)
|
||||
await SourceApi.deleteSource(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
||||
@@ -7,14 +7,20 @@
|
||||
:rules="formRules"
|
||||
label-width="80px"
|
||||
>
|
||||
<el-form-item v-if="formData.level > 1" label="上级来源">
|
||||
<el-form-item v-if="formData.parentSource" label="上级来源">
|
||||
<el-input v-model="formData.parentSource" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="来源名称" prop="sourceName">
|
||||
<el-input v-model="formData.sourceName" placeholder="请输入来源名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="排序" prop="orderNum">
|
||||
<el-input v-model="formData.orderNum" placeholder="请输入排序" type="number" :min="0" />
|
||||
<el-form-item label="排序" prop="sort">
|
||||
<el-input v-model="formData.sort" placeholder="请输入排序" type="number" :min="0" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-radio-group v-model="formData.status">
|
||||
<el-radio :label="0"> 启用 </el-radio>
|
||||
<el-radio :label="1"> 禁用 </el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input
|
||||
@@ -32,6 +38,8 @@
|
||||
</Dialog>
|
||||
</template>
|
||||
<script name="DialogSource" setup>
|
||||
import * as SourceApi from '@/api/clue/source'
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
@@ -41,7 +49,7 @@ const formLoading = ref(false) // 表单的加载中:1)修改时的数据加
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const formData = ref({
|
||||
sourceName: '',
|
||||
orderNum: 1,
|
||||
sort: 1,
|
||||
remark: ''
|
||||
})
|
||||
const formRules = reactive({
|
||||
@@ -56,16 +64,15 @@ const open = async (type, info) => {
|
||||
formType.value = type
|
||||
resetForm()
|
||||
// 修改时,设置数据
|
||||
if (info.sourceId) {
|
||||
if (info?.sourceId) {
|
||||
formLoading.value = true
|
||||
try {
|
||||
if (type == 'update') {
|
||||
formData.value = { ...info }
|
||||
formData.value = await SourceApi.getSource(info.sourceId)
|
||||
} else {
|
||||
formData.value.level = info.level + 1
|
||||
formData.value.parentSource = info.sourceName
|
||||
formData.value.parentId = info.sourceId
|
||||
}
|
||||
// formData.value = await UserApi.getUser(id)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
@@ -83,13 +90,12 @@ const submitForm = async () => {
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
// const data = formData.value as unknown as UserApi.UserVO
|
||||
if (formType.value === 'create') {
|
||||
// await UserApi.createUser(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
} else {
|
||||
// await UserApi.updateUser(data)
|
||||
if (formType.value === 'update') {
|
||||
await SourceApi.updateSource(formData.value)
|
||||
message.success(t('common.updateSuccess'))
|
||||
} else {
|
||||
await SourceApi.createSource(formData.value)
|
||||
message.success(t('common.createSuccess'))
|
||||
}
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
@@ -103,7 +109,8 @@ const submitForm = async () => {
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
sourceName: '',
|
||||
orderNum: 1,
|
||||
status: 0,
|
||||
sort: 1,
|
||||
remark: ''
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
|
||||
@@ -2,45 +2,55 @@
|
||||
<el-row :gutter="80">
|
||||
<el-col :span="10" :offset="0">
|
||||
<el-button class="mb-10px" type="primary" @click="handleInsert">新增属性</el-button>
|
||||
<el-table :data="tableList">
|
||||
<el-table-column prop="name" label="名称" />
|
||||
<el-table-column label="启用状态">
|
||||
<el-table :data="tableList" :row-class-name="setRowClass" @row-click="handleRowClick">
|
||||
<el-table-column prop="label" label="名称" />
|
||||
<el-table-column prop="field" label="属性编码" />
|
||||
<el-table-column prop="component" label="类型" width="200px">
|
||||
<template #default="{ row }">
|
||||
{{ typeOptions.find((it) => it.value == row.component).label }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="启用状态" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-switch
|
||||
v-model="row.status"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
:disabled="!row.canUpdate"
|
||||
:active-value="0"
|
||||
:inactive-value="1"
|
||||
@change="changeStatus(row)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="80px">
|
||||
<template #default="{ row }">
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
:disabled="!row.canUpdate"
|
||||
style="padding: 0"
|
||||
@click="remove(row)"
|
||||
<el-button type="primary" text style="padding: 0" @click="remove(row.clueParamId)"
|
||||
>删除</el-button
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-col>
|
||||
<el-col :span="14" :offset="0">
|
||||
<el-form :model="form" ref="fieldForm" :rules="rules" label-width="80px" :inline="false">
|
||||
<el-form-item label="属性名称" prop="name">
|
||||
<el-input v-model="form.name" placeholder="请输入属性名称" />
|
||||
<el-col :span="14" :offset="0" v-if="tableList.length || formType == 'create'">
|
||||
<el-form :model="form" ref="fieldForm" :rules="rules" label-width="100px" :inline="false">
|
||||
<el-form-item label="属性名称" prop="label">
|
||||
<el-input v-model="form.label" placeholder="请输入属性名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="属性类型" prop="type">
|
||||
<el-form-item prop="field">
|
||||
<template #label>
|
||||
<div class="flex justify-center" style="align-items: center">
|
||||
<span>属性编码</span>
|
||||
<Tooltip message="请输入字母或数字,必须以字母开头" />
|
||||
</div>
|
||||
</template>
|
||||
<el-input v-model="form.field" placeholder="请输入属性编码" />
|
||||
</el-form-item>
|
||||
<el-form-item label="属性类型" prop="component">
|
||||
<el-select
|
||||
v-model="form.type"
|
||||
v-model="form.component"
|
||||
placeholder="请选择属性类型"
|
||||
clearable
|
||||
filterable
|
||||
style="width: 100%"
|
||||
@change="form.options = []"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in typeOptions"
|
||||
@@ -51,72 +61,174 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="['Checkbox', 'Radio', 'Select'].includes(form.type)"
|
||||
v-if="['Checkbox', 'Radio', 'Select'].includes(form.component)"
|
||||
label="选项"
|
||||
prop="option"
|
||||
key="option"
|
||||
prop="options"
|
||||
key="options"
|
||||
>
|
||||
<div>
|
||||
<el-button type="primary" @click="optionList.push([])"> 新增选项 </el-button>
|
||||
<el-button type="primary" @click="handleAddOption"> 新增选项 </el-button>
|
||||
<div
|
||||
class="flex justify-between mt-10px"
|
||||
v-for="(item, index) in optionList"
|
||||
v-for="(item, index) in form.options"
|
||||
:key="index"
|
||||
>
|
||||
<el-input v-model="item.label" placeholder="请输入选项内容" clearable />
|
||||
<el-button type="primary" text @click="optionList.splice(index, 1)">删除</el-button>
|
||||
<el-input
|
||||
v-model="item.name"
|
||||
placeholder="请输入选项内容"
|
||||
clearable
|
||||
style="width: 300px !important"
|
||||
@blur="item.id = item.name"
|
||||
/>
|
||||
<el-button type="primary" text @click="form.options.splice(index, 1)">删除</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="onSubmit">保存</el-button>
|
||||
<el-button type="primary" :disabled="formLoading" @click="onSubmit">保存</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const tableList = ref([{ name: '咨询车型', status: 0, canUpdate: false }])
|
||||
<script setup name="FieldClue">
|
||||
import * as FieldApi from '@/api/clue/clueField'
|
||||
import { getDictOptions } from '@/utils/dict'
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const tableList = ref([])
|
||||
const currentRowId = ref('')
|
||||
|
||||
function setRowClass({ row }) {
|
||||
return row.field == currentRowId.value ? 'current-row' : ''
|
||||
}
|
||||
|
||||
function handleRowClick(row) {
|
||||
formType.value = 'update'
|
||||
currentRowId.value = row.field
|
||||
form.value = { ...row }
|
||||
}
|
||||
|
||||
const form = ref({
|
||||
name: undefined,
|
||||
type: undefined,
|
||||
option: undefined
|
||||
label: undefined,
|
||||
field: '',
|
||||
component: undefined,
|
||||
options: [],
|
||||
status: 0,
|
||||
isCustom: true,
|
||||
isForm: true,
|
||||
isSearch: true,
|
||||
isTable: true
|
||||
})
|
||||
|
||||
const typeOptions = ref([
|
||||
{ label: '输入框', value: 'Input' },
|
||||
{ label: '多选', value: 'Checkbox' },
|
||||
{ label: '单选', value: 'Radio' },
|
||||
{ label: '下拉选', value: 'Select' },
|
||||
{ label: '开关', value: 'Switch' },
|
||||
{ label: '日期选择', value: 'DatePicker' },
|
||||
{ label: '时间选择', value: 'TimePicker' },
|
||||
{ label: '富文本', value: 'Editor' },
|
||||
{ label: '图片', value: 'UploadImg' },
|
||||
{ label: '文件', value: 'UploadFile' }
|
||||
])
|
||||
const loading = ref(false)
|
||||
|
||||
const rules = {}
|
||||
const typeOptions = ref([])
|
||||
|
||||
const optionList = ref([])
|
||||
const rules = {
|
||||
label: { required: true, message: '名称不可为空', trigger: 'blur' },
|
||||
field: { required: true, message: '编码不可为空', trigger: 'blur' },
|
||||
component: { required: true, message: '类型不可为空', trigger: 'change' }
|
||||
}
|
||||
|
||||
async function getList() {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await FieldApi.getDiyFieldList()
|
||||
tableList.value = data
|
||||
if (data.length) {
|
||||
handleRowClick(data[0])
|
||||
}
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const formType = ref('')
|
||||
|
||||
function handleInsert() {
|
||||
console.log('新增')
|
||||
formType.value = 'create'
|
||||
form.value = {
|
||||
label: undefined,
|
||||
field: '',
|
||||
component: undefined,
|
||||
options: [],
|
||||
status: 0,
|
||||
isCustom: true,
|
||||
isForm: true,
|
||||
isSearch: true,
|
||||
isTable: true
|
||||
}
|
||||
}
|
||||
|
||||
function changeStatus(row) {
|
||||
console.log(row.status)
|
||||
async function remove(id) {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await FieldApi.deleteField(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
}
|
||||
|
||||
function remove(row) {
|
||||
console.log(row.status)
|
||||
async function changeStatus(row) {
|
||||
try {
|
||||
// 二次确认
|
||||
await message.confirm('是否确认修改状态')
|
||||
await FieldApi.updateFieldStatus(row.clueParamId, row.status)
|
||||
message.success('修改成功')
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {
|
||||
// 取消后,进行恢复按钮
|
||||
row.status = row.status == 0 ? 1 : 0
|
||||
}
|
||||
}
|
||||
|
||||
function onSubmit() {
|
||||
console.log('保存')
|
||||
const fieldForm = ref()
|
||||
const formLoading = ref(false)
|
||||
|
||||
function handleAddOption() {
|
||||
if (form.value.options) {
|
||||
form.value.options.push({})
|
||||
} else {
|
||||
form.value.options = []
|
||||
}
|
||||
}
|
||||
|
||||
async function onSubmit() {
|
||||
// 校验表单
|
||||
if (!fieldForm.value) return
|
||||
const valid = await fieldForm.value.validate()
|
||||
if (!valid) return
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
if (formType.value === 'create') {
|
||||
await FieldApi.createField(form.value)
|
||||
message.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await FieldApi.updateField(form.value)
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
// 发送操作成功的事件
|
||||
getList()
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
typeOptions.value = getDictOptions('attribute_type')
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
||||
@@ -2,45 +2,55 @@
|
||||
<el-row :gutter="80">
|
||||
<el-col :span="10" :offset="0">
|
||||
<el-button class="mb-10px" type="primary" @click="handleInsert">新增属性</el-button>
|
||||
<el-table :data="tableList">
|
||||
<el-table-column prop="name" label="名称" />
|
||||
<el-table-column label="启用状态">
|
||||
<el-table :data="tableList" :row-class-name="setRowClass" @row-click="handleRowClick">
|
||||
<el-table-column prop="label" label="名称" />
|
||||
<el-table-column prop="field" label="属性编码" />
|
||||
<el-table-column prop="component" label="类型" width="200px">
|
||||
<template #default="{ row }">
|
||||
{{ typeOptions.find((it) => it.value == row.component).label }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="启用状态" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-switch
|
||||
v-model="row.status"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
:disabled="!row.canUpdate"
|
||||
:active-value="0"
|
||||
:inactive-value="1"
|
||||
@change="changeStatus(row)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="80px">
|
||||
<template #default="{ row }">
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
:disabled="!row.canUpdate"
|
||||
style="padding: 0"
|
||||
@click="remove(row)"
|
||||
<el-button type="primary" text style="padding: 0" @click="remove(row.clueParamId)"
|
||||
>删除</el-button
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-col>
|
||||
<el-col :span="14" :offset="0">
|
||||
<el-form :model="form" ref="fieldForm" :rules="rules" label-width="80px" :inline="false">
|
||||
<el-form-item label="属性名称" prop="name">
|
||||
<el-input v-model="form.name" placeholder="请输入属性名称" />
|
||||
<el-col :span="14" :offset="0" v-if="tableList.length || formType == 'create'">
|
||||
<el-form :model="form" ref="fieldForm" :rules="rules" label-width="100px" :inline="false">
|
||||
<el-form-item label="属性名称" prop="label">
|
||||
<el-input v-model="form.label" placeholder="请输入属性名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="属性类型" prop="type">
|
||||
<el-form-item prop="field">
|
||||
<template #label>
|
||||
<div class="flex justify-center" style="align-items: center">
|
||||
<span>属性编码</span>
|
||||
<Tooltip message="请输入字母或数字,必须以字母开头" />
|
||||
</div>
|
||||
</template>
|
||||
<el-input v-model="form.field" placeholder="请输入属性编码" />
|
||||
</el-form-item>
|
||||
<el-form-item label="属性类型" prop="component">
|
||||
<el-select
|
||||
v-model="form.type"
|
||||
v-model="form.component"
|
||||
placeholder="请选择属性类型"
|
||||
clearable
|
||||
filterable
|
||||
style="width: 100%"
|
||||
@change="form.options = []"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in typeOptions"
|
||||
@@ -51,72 +61,174 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="['Checkbox', 'Radio', 'Select'].includes(form.type)"
|
||||
v-if="['Checkbox', 'Radio', 'Select'].includes(form.component)"
|
||||
label="选项"
|
||||
prop="option"
|
||||
key="option"
|
||||
prop="options"
|
||||
key="options"
|
||||
>
|
||||
<div>
|
||||
<el-button type="primary" @click="optionList.push([])"> 新增选项 </el-button>
|
||||
<el-button type="primary" @click="handleAddOption"> 新增选项 </el-button>
|
||||
<div
|
||||
class="flex justify-between mt-10px"
|
||||
v-for="(item, index) in optionList"
|
||||
v-for="(item, index) in form.options"
|
||||
:key="index"
|
||||
>
|
||||
<el-input v-model="item.label" placeholder="请输入选项内容" clearable />
|
||||
<el-button type="primary" text @click="optionList.splice(index, 1)">删除</el-button>
|
||||
<el-input
|
||||
v-model="item.name"
|
||||
placeholder="请输入选项内容"
|
||||
clearable
|
||||
style="width: 300px !important"
|
||||
@blur="item.id = item.name"
|
||||
/>
|
||||
<el-button type="primary" text @click="form.options.splice(index, 1)">删除</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="onSubmit">保存</el-button>
|
||||
<el-button type="primary" :disabled="formLoading" @click="onSubmit">保存</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const tableList = ref([{ name: '额外支出费用', status: 1, canUpdate: true }])
|
||||
<script setup name="FieldOrder">
|
||||
import * as FieldApi from '@/api/clue/orderField.js'
|
||||
import { getDictOptions } from '@/utils/dict'
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const tableList = ref([])
|
||||
const currentRowId = ref('')
|
||||
|
||||
function setRowClass({ row }) {
|
||||
return row.field == currentRowId.value ? 'current-row' : ''
|
||||
}
|
||||
|
||||
function handleRowClick(row) {
|
||||
formType.value = 'update'
|
||||
currentRowId.value = row.field
|
||||
form.value = { ...row }
|
||||
}
|
||||
|
||||
const form = ref({
|
||||
name: undefined,
|
||||
type: undefined,
|
||||
option: undefined
|
||||
label: undefined,
|
||||
field: '',
|
||||
component: undefined,
|
||||
options: [],
|
||||
status: 0,
|
||||
isCustom: true,
|
||||
isForm: true,
|
||||
isSearch: true,
|
||||
isTable: true
|
||||
})
|
||||
|
||||
const typeOptions = ref([
|
||||
{ label: '输入框', value: 'Input' },
|
||||
{ label: '多选', value: 'Checkbox' },
|
||||
{ label: '单选', value: 'Radio' },
|
||||
{ label: '下拉选', value: 'Select' },
|
||||
{ label: '开关', value: 'Switch' },
|
||||
{ label: '日期选择', value: 'DatePicker' },
|
||||
{ label: '时间选择', value: 'TimePicker' },
|
||||
{ label: '富文本', value: 'Editor' },
|
||||
{ label: '图片', value: 'UploadImg' },
|
||||
{ label: '文件', value: 'UploadFile' }
|
||||
])
|
||||
const loading = ref(false)
|
||||
|
||||
const rules = {}
|
||||
const typeOptions = ref([])
|
||||
|
||||
const optionList = ref([])
|
||||
const rules = {
|
||||
label: { required: true, message: '名称不可为空', trigger: 'blur' },
|
||||
field: { required: true, message: '编码不可为空', trigger: 'blur' },
|
||||
component: { required: true, message: '类型不可为空', trigger: 'change' }
|
||||
}
|
||||
|
||||
async function getList() {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await FieldApi.getDiyFieldList()
|
||||
tableList.value = data
|
||||
if (data.length) {
|
||||
handleRowClick(data[0])
|
||||
}
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const formType = ref('')
|
||||
|
||||
function handleInsert() {
|
||||
console.log('新增')
|
||||
formType.value = 'create'
|
||||
form.value = {
|
||||
label: undefined,
|
||||
field: '',
|
||||
component: undefined,
|
||||
options: [],
|
||||
status: 0,
|
||||
isCustom: true,
|
||||
isForm: true,
|
||||
isSearch: true,
|
||||
isTable: true
|
||||
}
|
||||
}
|
||||
|
||||
function changeStatus(row) {
|
||||
console.log(row.status)
|
||||
async function remove(id) {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await FieldApi.deleteField(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
}
|
||||
|
||||
function remove(row) {
|
||||
console.log(row.status)
|
||||
async function changeStatus(row) {
|
||||
try {
|
||||
// 二次确认
|
||||
await message.confirm('是否确认修改状态')
|
||||
await FieldApi.updateFieldStatus(row.clueParamId, row.status)
|
||||
message.success('修改成功')
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {
|
||||
// 取消后,进行恢复按钮
|
||||
row.status = row.status == 0 ? 1 : 0
|
||||
}
|
||||
}
|
||||
|
||||
function onSubmit() {
|
||||
console.log('保存')
|
||||
const fieldForm = ref()
|
||||
const formLoading = ref(false)
|
||||
|
||||
function handleAddOption() {
|
||||
if (form.value.options) {
|
||||
form.value.options.push({})
|
||||
} else {
|
||||
form.value.options = []
|
||||
}
|
||||
}
|
||||
|
||||
async function onSubmit() {
|
||||
// 校验表单
|
||||
if (!fieldForm.value) return
|
||||
const valid = await fieldForm.value.validate()
|
||||
if (!valid) return
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
if (formType.value === 'create') {
|
||||
await FieldApi.createField(form.value)
|
||||
message.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await FieldApi.updateField(form.value)
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
// 发送操作成功的事件
|
||||
getList()
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
typeOptions.value = getDictOptions('attribute_type')
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
||||
@@ -1,27 +1,35 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-tabs v-model="tabIndex" type="border-card">
|
||||
<el-tab-pane label="线索属性" :name="0">
|
||||
<FieldClue />
|
||||
<el-tab-pane label="线索属性" :name="0" v-if="checkPermi(['clue:setting:clue-props'])">
|
||||
<FieldClue v-if="tabIndex == 0" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="成交属性" :name="10">
|
||||
<FieldOrder />
|
||||
<el-tab-pane label="成交属性" :name="10" v-if="checkPermi(['clue:setting:order-props'])">
|
||||
<FieldOrder v-if="tabIndex == 10" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="线索来源" :name="15">
|
||||
<ClueSource />
|
||||
<el-tab-pane label="线索来源" :name="15" v-if="checkPermi(['clue:setting:clue-resource'])">
|
||||
<ClueSource v-if="tabIndex == 15" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="线索获取规则" :name="20">
|
||||
<ClueGet />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="线索分配规则" :name="30">
|
||||
<ClueSend />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="常规设置" :name="40">
|
||||
<GeneralSet />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="消息通知" :name="50">
|
||||
<MsgSend />
|
||||
<!-- <el-tab-pane
|
||||
label="线索获取规则"
|
||||
:name="20"
|
||||
v-if="checkPermi(['clue:setting:clue-get-rules'])"
|
||||
>
|
||||
<ClueGet v-if="tabIndex == 20" />
|
||||
</el-tab-pane> -->
|
||||
<!-- <el-tab-pane
|
||||
label="线索分配规则"
|
||||
:name="30"
|
||||
v-if="checkPermi(['clue:setting:clue-send-rules'])"
|
||||
>
|
||||
<ClueSend v-if="tabIndex == 30" />
|
||||
</el-tab-pane> -->
|
||||
<el-tab-pane label="常规设置" :name="40" v-if="checkPermi(['clue:setting:general-setting'])">
|
||||
<GeneralSet v-if="tabIndex == 40" />
|
||||
</el-tab-pane>
|
||||
<!-- <el-tab-pane label="消息通知" :name="50" v-if="checkPermi(['mall:setting:prod'])">
|
||||
<MsgSend v-if="tabIndex == 50" />
|
||||
</el-tab-pane> -->
|
||||
</el-tabs>
|
||||
</div>
|
||||
</template>
|
||||
@@ -30,10 +38,11 @@
|
||||
import FieldClue from './Comp/FieldClue.vue'
|
||||
import FieldOrder from './Comp/FieldOrder.vue'
|
||||
import ClueSource from './Comp/ClueSource.vue'
|
||||
import ClueGet from './Comp/ClueGet.vue'
|
||||
import ClueSend from './Comp/ClueSend.vue'
|
||||
import MsgSend from './Comp/MsgSend.vue'
|
||||
// import ClueGet from './Comp/ClueGet.vue'
|
||||
// import ClueSend from './Comp/ClueSend.vue'
|
||||
// import MsgSend from './Comp/MsgSend.vue'
|
||||
import GeneralSet from './Comp/GeneralSet.vue'
|
||||
import { checkPermi } from '@/utils/permission'
|
||||
|
||||
const tabIndex = ref(0)
|
||||
</script>
|
||||
|
||||
@@ -11,9 +11,13 @@
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleQuery">搜索</el-button>
|
||||
<el-button @click="resetQuery">重置</el-button>
|
||||
<el-button type="primary" plain @click="handleAdd">新增</el-button>
|
||||
<el-button type="primary" @click="handleQuery" v-hasPermi="['clue:skill:search']">
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button @click="resetQuery" v-hasPermi="['clue:skill:reset']">重置</el-button>
|
||||
<el-button type="primary" plain @click="handleAdd" v-hasPermi="['clue:skill:add']">
|
||||
新增
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-table :data="tableList">
|
||||
@@ -27,8 +31,22 @@
|
||||
<el-table-column label="关键词" align="center" prop="skillKey" />
|
||||
<el-table-column label="操作" align="center">
|
||||
<template #default="scope">
|
||||
<el-button type="primary" text @click="handleUpdate(scope.row)">修改</el-button>
|
||||
<el-button type="primary" text @click="handleDelete(scope.row)">删除</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
@click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['clue:skill:update']"
|
||||
>
|
||||
修改
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
@click="handleDelete(scope.row)"
|
||||
v-hasPermi="['clue:skill:delete']"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
@@ -51,8 +51,8 @@
|
||||
|
||||
<el-table class="mt-20px" :data="tableList" border>
|
||||
<el-table-column type="index" width="50px" />
|
||||
<el-table-column prop="" label="产品名称" />
|
||||
<el-table-column prop="" label="规格" />
|
||||
<el-table-column prop="productName" label="产品名称" />
|
||||
<el-table-column prop="specsName" label="规格" />
|
||||
<el-table-column label="变动类型">
|
||||
<template #default="{ row }">
|
||||
{{ ['', '入库', '出库'][row.changeType] }}
|
||||
@@ -61,8 +61,8 @@
|
||||
<el-table-column prop="num" label="数量" />
|
||||
<el-table-column prop="money" label="金额" />
|
||||
<el-table-column prop="changeTime" label="变动时间" :formatter="dateFormatter" />
|
||||
<el-table-column prop="changeUser" label="变动人员" />
|
||||
<el-table-column prop="" label="所属仓库" />
|
||||
<el-table-column prop="changeUserName" label="变动人员" />
|
||||
<el-table-column prop="warehouseName" label="所属仓库" />
|
||||
<el-table-column prop="remark" label="备注" />
|
||||
</el-table>
|
||||
<Pagination
|
||||
|
||||
@@ -25,12 +25,19 @@
|
||||
<el-button
|
||||
type="primary"
|
||||
style="padding: 0"
|
||||
:disabled="row.type == 1"
|
||||
text
|
||||
@click="openForm('update', row.warehouseId)"
|
||||
>
|
||||
修改
|
||||
</el-button>
|
||||
<el-button type="danger" style="padding: 0" text @click="handleRemove(row.warehouseId)">
|
||||
<el-button
|
||||
type="danger"
|
||||
style="padding: 0"
|
||||
:disabled="row.type == 1"
|
||||
text
|
||||
@click="handleRemove(row.warehouseId)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
|
||||
@@ -15,16 +15,16 @@
|
||||
<el-tab-pane label="常规设置" :name="4" v-if="checkPermi(['mall:setting:general'])">
|
||||
<GeneralSet v-if="tabIndex == 4" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="消息通知" :name="9" v-if="checkPermi(['mall:setting:msg'])">
|
||||
<!-- <el-tab-pane label="消息通知" :name="9" v-if="checkPermi(['mall:setting:msg'])">
|
||||
<MsgSend v-if="tabIndex == 9" />
|
||||
</el-tab-pane>
|
||||
</el-tab-pane> -->
|
||||
</el-tabs>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import GeneralSet from './Comp/GeneralSet.vue'
|
||||
import FieldProduct from './Comp/FieldProduct.vue'
|
||||
import MsgSend from './Comp/MsgSend.vue'
|
||||
// import MsgSend from './Comp/MsgSend.vue'
|
||||
import CategorySet from './Comp/CategorySet.vue'
|
||||
import BrandSet from './Comp/BrandSet.vue'
|
||||
import SupplierSet from './Comp/SupplierSet.vue'
|
||||
|
||||
Reference in New Issue
Block a user