修改录入员和业务员

master
doublekou 10 months ago
parent d6d30d17c2
commit c9fdc712aa

@ -3,4 +3,5 @@ VITE_API_TIME_OUT=30000
VITE_PASSWORD_SECRET_KEY===ad-distribute= VITE_PASSWORD_SECRET_KEY===ad-distribute=
VITE_IMAGE_DOMAIN=https://hccake-img.oss-cn-shanghai.aliyuncs.com VITE_IMAGE_DOMAIN=https://hccake-img.oss-cn-shanghai.aliyuncs.com
# 用来回显图片 # 用来回显图片
VITE_SERVERURL=http://39.100.77.21:8001 # http://39.100.77.21:8001
VITE_SERVERURL=https://byffp.top

@ -3,4 +3,5 @@ VITE_API_TIME_OUT=10000
VITE_PASSWORD_SECRET_KEY===ad-distribute= VITE_PASSWORD_SECRET_KEY===ad-distribute=
VITE_IMAGE_DOMAIN=https://hccake-img.oss-cn-shanghai.aliyuncs.com VITE_IMAGE_DOMAIN=https://hccake-img.oss-cn-shanghai.aliyuncs.com
# 用来回显图片 # 用来回显图片
VITE_SERVERURL=http://39.100.77.21:8001 # http://39.100.77.21:8001
VITE_SERVERURL=https://byffp.top

Binary file not shown.

@ -1,5 +1,19 @@
import httpClient from '@/utils/axios' import httpClient from '@/utils/axios'
import type { I18nData, I18nDataDTO, I18nDataQO, I18nImportData, ReadFileId } from './types' import type {
I18nData,
I18nDataDTO,
I18nDataQO,
I18nImportData,
ReadFileId,
authorizedPlatformLink,
singleImportType,
otherImportType,
ConfigurationListPageParam,
linkDetail,
addChannelData,
applicationData,
chexkedChannelIdListType
} from './types'
import type { FileObject } from '@/components/CropperModal/types' import type { FileObject } from '@/components/CropperModal/types'
import type { ImportMode } from '@/api/types' import type { ImportMode } from '@/api/types'
import type { ApiResult } from '@/api/types' import type { ApiResult } from '@/api/types'
@ -58,9 +72,7 @@ export function checkHeaderInformation(data: I18nImportData) {
// return httpClient.post<ApiResult<void>>('/clueFile/readFile', data) // return httpClient.post<ApiResult<void>>('/clueFile/readFile', data)
// } // }
export function importI18nDataExcel(data: ReadFileId) { export function importI18nDataExcel(data: ReadFileId) {
return httpClient.get<ApiResult>('/clueFile/readFile', { return httpClient.post<ApiResult>('/clueFile/readFile', data)
params: data
})
} }
// export function downloadI18nDataExcelTemplate() { // export function downloadI18nDataExcelTemplate() {
@ -85,3 +97,55 @@ export function deleteFile(id: string) {
// } // }
) )
} }
//获取授权平台跳转url
export function getAuthorizedPlatformLink(data: authorizedPlatformLink) {
return httpClient.get<ApiResult>('/api/oceanEngine/url', {
params: data
})
}
//单个导入
export function singleImport(data: singleImportType) {
return httpClient.post('/custom/manual/create', data)
}
/**
*
* @param pageParams
*/
export function pageAccessLogs(pageParams: ConfigurationListPageParam) {
return httpClient.get<ApiResult>('/pushLink/page', {
params: pageParams
})
}
//其他导入新增
export function otherImport(data: otherImportType) {
return httpClient.post('/pushLink/add', data)
}
//其他导入编辑
export function updateOtherImport(data: otherImportType) {
return httpClient.post('/pushLink/update', data)
}
//链接详情
export function linkDetail(data: linkDetail) {
return httpClient.get<ApiResult>('/pushLink/detail', {
params: data
})
}
//获取渠道标识列表
export function channelIdList(data: chexkedChannelIdListType) {
return httpClient.get<ApiResult>('/api/tag/list', {
params: data
})
}
//新增渠道标识
export function addChannelId(data: addChannelData) {
return httpClient.post('/api/tag/add', data)
}
//应用
export function application(data: applicationData[]) {
return httpClient.post('/api/tag/update', data)
}
//获取分发人下拉框列表数据
export function getDistributorsUserIdList(roleId: number | undefined) {
return httpClient.get<ApiResult>(`/system/user/findSalesman/${roleId}`)
}

@ -1,5 +1,6 @@
import type { ImportMode } from '@/api/types' import type { ImportMode } from '@/api/types'
import type { UploadFile } from 'ant-design-vue/lib/upload/interface' import type { UploadFile } from 'ant-design-vue/lib/upload/interface'
import type { PageParam } from '@/api/types'
export interface I18nDataQO { export interface I18nDataQO {
// 国际化标识 // 国际化标识
@ -50,6 +51,107 @@ export interface I18nImportData {
export interface ReadFileId { export interface ReadFileId {
// 文件内容 // 文件内容
recordId?: string recordId?: string
// 导入模式 //分发人id数组
// importMode: ImportMode distributorsUserIdList: number[]
//渠道名称
channelName: string
}
// 授权平台跳转链接
export interface authorizedPlatformLink {
authorizeName?: string
}
//单个导入
export interface singleImportType {
customNid: string
remark: string
}
/**
*
*/
export type ConfigurationListPageParam = ConfigurationListQO & PageParam
/**
*
*/
export type ConfigurationListQO = {
//标签
clueLabelName?: string
//
httpStatus?: number
//号码
nid?: string
// 开始时间
startTime?: string
// 结束时间
endTime?: string
clueId?: string
}
//其他导入
export interface otherImportType {
id?: number | undefined
channelType: number | undefined
name: string | undefined
channelIdentifyingList?: string[]
authType: number | undefined
}
//链接详情
export interface linkDetail {
id: number | undefined
}
//新增渠道标识需传的数据
export interface addChannelData {
routeName: string
}
//应用
export interface applicationData {
id?: number | undefined
routeName?: string | undefined
routeStatus?: number | undefined
}
/**
* record
*/
export type ConfigurationListRecord = {
channelType: number | undefined
channelUrl: string
createBy: number | undefined
createTime: string
id: number | undefined
userId: number | undefined
name: string
status: number | undefined
channelIdentifying?: string[]
}
////获取选中项渠道标识列表
export type chexkedChannelIdListType = {
routeStatus?: number | undefined
}
/**
*
*/
export interface clueDetailsSearch {
// 响应状态码
httpStatus?: number
//订单编号
batchNo?: string
//报名状态
enrollStatus?: string
// 访问时间区间的开始值
startTime?: string
// 访问时间区间的结束值
endTime?: string
}
/**
*
*/
export interface configurationListSearch {
// 响应状态码
httpStatus?: number
//渠道名称
name?: string
// 接入状态
channelType?: string
// 访问时间区间的开始值
startTime?: string
// 访问时间区间的结束值
endTime?: string
} }

@ -1,7 +1,12 @@
import httpClient from '@/utils/axios' import httpClient from '@/utils/axios'
import type { ApiResult } from '@/api/types' import type { ApiResult } from '@/api/types'
// SysMenuDTO // SysMenuDTO
import type { SysUserPageParam, resourcesDtoDTO } from '@/api/customermanagement/customerList/types' import type {
SysUserPageParam,
resourcesDtoDTO,
queryReservation,
isEffectiveType
} from '@/api/customermanagement/customerList/types'
/** /**
* 线 * 线
@ -43,3 +48,13 @@ export function messageAccessLogs(pageParams: SysUserPageParam) {
params: pageParams params: pageParams
}) })
} }
//我的线索导出功能
export function exportClueData(params: queryReservation) {
return httpClient.get('/clue/export', { params, responseType: 'blob' })
}
//修改是否有效
export function isEffective(query: isEffectiveType) {
return httpClient.get('/clue/changeIsEffective', {
params: query
})
}

@ -110,3 +110,14 @@ export interface resourcesDtoDTO {
// // 地区语言标签 // // 地区语言标签
// languageTag: string // languageTag: string
// } // }
//我的线索导出
export interface queryReservation {
nid?: string | undefined
startTime?: string
endTime?: string
}
//修改是否有效
export interface isEffectiveType {
id?: string
effective: number | undefined
}

@ -20,7 +20,7 @@ const useAdminWebSocket = () => {
const host = window.location.host const host = window.location.host
console.log(host, 'host') console.log(host, 'host')
const wsUri = `ws://${host}${baseUri}/ws?access_token=${accessToken}` const wsUri = `ws://${host}${baseUri}/ws?access_token=${accessToken}`
console.log(wsUri, 'wsUri') console.log(baseUri, 'baseUri')
//使用useWebSocket函数创建WebSocket连接并配置自动重连和心跳机制 //使用useWebSocket函数创建WebSocket连接并配置自动重连和心跳机制
useWebSocketReturn = useWebSocket(wsUri, { useWebSocketReturn = useWebSocket(wsUri, {

@ -0,0 +1,296 @@
<template>
<a-modal
:title="title"
:visible="visible"
:mask-closable="false"
:centered="true"
:body-style="{ padding: '24px 40px 24px 40px' }"
:confirm-loading="submitLoading"
:width="600"
@cancel="handleClose"
>
<a-form ref="formRef" :model="formModel" :label-col="{ span: 5 }" :wrapper-col="{ span: 15 }">
<a-form-item
label="渠道名称"
name="channelName"
:rules="[{ required: true, message: '请输入渠道名称', trigger: ['blur', 'change'] }]"
>
<a-input v-model:value="formModel.channelName" placeholder="请输入" />
</a-form-item>
<a-form-item
ref="distributorsUserIdListRef"
label="分发人选择"
name="distributorsUserIdList"
:rules="[
{ type: 'array', required: true, message: '请选择分发人', trigger: ['blur', 'change'] }
]"
>
<a-select
v-model:value="formModel.distributorsUserIdList"
mode="multiple"
style="width: 100%"
placeholder="请选择分发人"
:options="selectedPersonOptions"
></a-select>
</a-form-item>
</a-form>
<div class="importModeOne" style="margin-left: 108px">
<div style="display: flex"></div>
<div style="display: flex">
<a-upload
v-model:file-list="fileLists"
accept=".xls,.xlsx"
:max-count="1"
:show-upload-list="{ showDownloadIcon: true, showRemoveIcon: true }"
:before-upload="beforeUpload"
@change="handleChange"
>
<a-button :disabled="fileLists.length > 0"> <UploadOutlined />上传文件 </a-button>
<template #downloadIcon>download</template>
<template #removeIcon> </template>
</a-upload>
<a-button type="primary" style="margin-left: 20px" @click.stop="downloadTemplate"
>通用模版下载</a-button
>
<a-button
type="primary"
style="margin: 0 20px"
:disabled="fileLists.length === 0"
@click.stop="reset"
>重置</a-button
>
<a-upload
v-model:file-list="customDefinitionFileList"
accept=".xls,.xlsx"
:max-count="1"
:show-upload-list="{ showDownloadIcon: true, showRemoveIcon: true }"
:before-upload="beforeUpload"
@change="handleChange"
>
<template #downloadIcon>download</template>
<template #removeIcon>
<delete-outlined></delete-outlined>
</template>
</a-upload>
</div>
</div>
<template #footer>
<a-button @click="handleClose()"></a-button>
<a-button type="primary" @click="onSubmit()"></a-button>
</template>
<!-- 对话框 -->
<a-modal v-model:visible="importVisible" title="提示" @ok="handleOk">
<template #footer>
<a-button key="Return" @click="handleCancel('删除')"></a-button>
<a-button key="Submit" type="primary" :loading="loading" @click="handleOk"></a-button>
</template>
<p>
上传成功,表头信息是否为<span
v-for="(item, index) in resData"
:key="index"
style="font-weight: 700"
>{{ item }}</span
>请确认?
</p>
</a-modal>
</a-modal>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue'
import { useModal } from '@/hooks/modal'
import { useFormAction, FormAction } from '@/hooks/form'
import type { CustomerListRecord } from '@/api/customermanagement/customerList/types'
import { doRequest } from '@/utils/axios/request'
import { message } from 'ant-design-vue'
import { deleteFile, getDistributorsUserIdList } from '@/api/clueaccess/clueImport'
import type { UploadFile } from 'ant-design-vue/lib/upload/interface'
import type { UploadChangeParam } from 'ant-design-vue'
import {
checkHeaderInformation,
importI18nDataExcel,
downloadI18nDataExcelTemplate
} from '@/api/clueaccess/clueImport'
import { remoteFileDownload } from '@/utils/file-utils'
import type { SelectProps } from 'ant-design-vue'
//
const selectedPersonOptions = ref<SelectProps['options']>([])
//
const fileLists = ref<UploadFile[]>([])
const customDefinitionFileList = ref<UploadFile[]>([])
// const formModelFile = ref<UploadFile>()
export interface I18nImportData {
//
file?: UploadFile
distributorsUserIdList: number[]
channelName: string
}
const formModel = reactive<I18nImportData>({
file: undefined,
distributorsUserIdList: [],
channelName: ''
})
const formRef = ref()
function beforeUpload(file: UploadFile) {
formModel.file = file
return false
}
const resData = reactive<string[]>([])
const currentId = ref('')
const handleChange = ({ file, fileList }: UploadChangeParam) => {
console.log(file.status, 'file')
if (file.status === 'removed') {
if (currentId.value) {
handleCancel('删除')
} else {
fileList.length = 0
message.success('移除成功', 1)
}
} else {
doRequest(checkHeaderInformation(toRaw(formModel)), {
onSuccess: (res: any) => {
resData.length = 0
if (res.code === 200) {
importVisible.value = true
resData.push(...res.data.excelHead)
currentId.value = res.data.clueRecordId
}
},
onFail: error => {
currentId.value = ''
fileList[0].status = 'error'
fileList[0].response = error.message
customDefinitionFileList.value.length = 0
fileLists.value.length = 0
}
})
}
}
//
const loading = ref<boolean>(false)
const importVisible = ref<boolean>(false)
//
const handleOk = () => {
importVisible.value = false
}
//
const handleCancel = (type: string) => {
doRequest(deleteFile(currentId.value), {
onSuccess: (res: any) => {
if (res.code === 200) {
if (type === '删除') {
message.success('删除成功', 1)
}
fileLists.value.length = 0
importVisible.value = false
}
},
onError: () => {
message.error('删除失败', 1)
}
})
}
//
const reset = () => {
handleCancel('删除')
}
// excel
function downloadTemplate() {
downloadI18nDataExcelTemplate().then(response => {
remoteFileDownload(response)
// downloadFile(response, '', 'xlsx')
})
}
const emits = defineEmits<{
(e: 'submit-success', resetPageIndex?: boolean): void
}>()
const { title, visible, openModal, closeModal } = useModal()
const { formAction } = useFormAction()
//
const submitLoading = ref(false)
const resetForm = () => {
formRef.value.resetFields()
}
const onSubmit = () => {
formRef.value
.validate()
.then(() => {
if (fileLists.value.length === 0) {
message.error('请选择上传文件')
return
}
doRequest(
importI18nDataExcel({
recordId: currentId.value,
distributorsUserIdList: formModel.distributorsUserIdList,
channelName: formModel.channelName
}),
{
onSuccess: (res: any) => {
message.success('上传成功', 1)
importVisible.value = false
customDefinitionFileList.value.length = 0
fileLists.value.length = 0
//
handleClose()
submitLoading.value = false
emits('submit-success')
}
}
)
})
.catch((error: any) => {
return false
})
}
//
const handleClose = () => {
if (
(formModel.file && !formModel.channelName) ||
(formModel.file && formModel.distributorsUserIdList.length === 0)
) {
handleCancel('关闭')
}
resetForm()
closeModal()
submitLoading.value = false
}
interface openObj {
newFormAction: FormAction
type?: number | undefined
record?: CustomerListRecord
}
defineExpose({
open(opendata: openObj) {
selectedPersonOptions.value = []
doRequest(getDistributorsUserIdList(20), {
onSuccess: (res: any) => {
const a = res.data.map((item: any) => {
return { label: item.userName, value: item.userId }
})
a.forEach((item: any) => {
selectedPersonOptions.value?.push({
label: item.label,
value: item.value
})
})
}
})
openModal()
title.value = '批量导入'
formAction.value = opendata.newFormAction
}
})
</script>
<style lang="less" scoped></style>

@ -0,0 +1,220 @@
<template>
<!-- 头部 -->
<a-card :bordered="false" :body-style="{ paddingBottom: 0 }">
<div class="resourceList-title">资源列表</div>
<!-- <a-row>
<a-col
:span="8"
:offset="8"
style="margin-top: 24px; margin-bottom: 24px; width: 50%; border: 1px solid #dcdfe6"
>
<div style="display: flex; justify-content: center">
<a-statistic title="Active Users" :value="112893" style="margin-right: 50px" />
<a-statistic title="Account Balance (CNY)" :precision="2" :value="112893" />
</div>
</a-col>
</a-row> -->
</a-card>
<!-- 工具栏 -->
<clue-details-search :loading="tableRef?.loading" @search="searchTable" />
<!-- 批量操作更多操作 -->
<a-card :bordered="false" :body-style="{ paddingBottom: 0 }">
<div class="operationButtonArea">
<a-button type="primary" @click="singleImport"></a-button>
<a-button type="primary" @click="batchImport"></a-button>
</div>
</a-card>
<!-- 底部表格 -->
<pro-table
ref="tableRef"
row-key="customId"
:request="tableRequest"
:columns="columns"
:scroll="{ x: 1100 }"
:tool-bar-render="false"
class="protable"
:pagination="{ showQuickJumper: true }"
>
<!-- <template #bodyCell="{ column, record }">
<template v-if="column.key === 'operate'">
<operation-group>
<a @click="handleView(record)"></a>
<a v-if="record.enrollStatus === 0" @click="handleEdit(record)"></a>
</operation-group>
</template>
</template> -->
</pro-table>
<!-- 单个导入新建弹窗 -->
<clue-details-single-modal ref="clueDetailsSingleModalRef" @submit-success="reloadTable" />
<!-- 批量导入新建弹窗 -->
<clue-details-batch-modal ref="clueDetailsBatchModalRef" @submit-success="reloadTable" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
//
import ProTable from '#/table'
import type { ProTableInstanceExpose, TableRequest, ProColumns } from '#/table'
import { mergePageParam } from '@/utils/page-utils'
import { FormAction } from '@/hooks/form'
import ClueDetailsSingleModal from '@/views/clueDetails/clueDetailsSingleModal.vue'
import ClueDetailsBatchModal from '@/views/clueDetails/clueDetailsBatchModal.vue'
import ClueDetailsSearch from '@/views/clueDetails/clueDetailsSearch.vue'
import { pageAccessLogs } from '@/api/geopoliticalCustomers'
import type {
GeopoliticalCustomersRecord,
GeopoliticalCustomersPageParam
} from '@/api/geopoliticalCustomers/types'
defineOptions({ name: 'ResourceList' })
//
import { useUserStore } from '@/stores/user-store'
const { userInfo } = useUserStore()
const salesmanType = ref<number | undefined>()
onMounted(() => {
salesmanType.value = userInfo?.salesmanType
})
//
//
const tableRef = ref<ProTableInstanceExpose>()
//ref
const clueDetailsSingleModalRef = ref<InstanceType<typeof ClueDetailsSingleModal>>()
//ref
const clueDetailsBatchModalRef = ref<InstanceType<typeof ClueDetailsBatchModal>>()
//
let searchParams: GeopoliticalCustomersPageParam = {}
//
const tableRequest: TableRequest = (params, sorter, filter) => {
const pageParam = mergePageParam(params, sorter, filter, salesmanType.value)
return pageAccessLogs({ ...pageParam, ...searchParams })
}
//
const reloadTable = (resetPageIndex?: boolean) => {
tableRef.value?.actionRef?.reload(resetPageIndex)
}
//
const searchTable = (params: GeopoliticalCustomersPageParam) => {
searchParams = params
reloadTable(true) // tableRequest
}
//
const singleImport = () => {
clueDetailsSingleModalRef.value?.open({
newFormAction: FormAction.CREATE
})
}
//
const batchImport = () => {
clueDetailsBatchModalRef.value?.open({
newFormAction: FormAction.CREATE
})
}
//
// const handleView = (record: GeopoliticalCustomersRecord) => {
// geopoliticalCustomersModalRef.value?.open({
// newFormAction: FormAction.UPDATE,
// type: 'view',
// record: record
// })
// }
//
// const handleEdit = (record: GeopoliticalCustomersRecord) => {
// geopoliticalCustomersModalRef.value?.open({
// newFormAction: FormAction.UPDATE,
// type: 'edit',
// record: record
// })
// }
const columns: ProColumns[] = [
{
title: '订单编号',
width: 160,
dataIndex: 'batchNo'
},
{
title: '客户类型',
dataIndex: 'channelType',
customRender: function ({ record }: any) {
if (record.channelType === 1) {
return h('div', '手动创建')
} else if (record.channelType === 2) {
return h('div', '文件上传')
} else if (record.channelType === 3) {
return h('div', '飞鱼回传')
} else if (record.channelType === 4) {
return h('div', '话单回传')
} else if (record.channelType === 5) {
return h('div', 'api回传')
} else {
return h('div', '-')
}
}
},
{
title: '联系方式',
dataIndex: 'customNid'
},
{
title: '报名状态',
dataIndex: 'enrollStatus',
customRender: function ({ record }: any) {
if (record.enrollStatus === 0) {
return h('div', '待提交')
} else if (record.enrollStatus === 1) {
return h('div', '已报名')
} else if (record.enrollStatus === 2) {
return h('div', '审核通过')
}
}
},
{
title: '备注',
dataIndex: 'remark'
},
{
title: '录入日期',
dataIndex: 'createTime',
width: 150,
align: 'center',
customRender: function ({ record }: any) {
if (record.createTime) {
return record.createTime
} else if (record.createTime === null) {
return '--'
}
}
}
// {
// key: 'operate',
// title: '',
// align: 'center',
// width: 100,
// fixed: 'right'
// }
]
</script>
<style lang="less" scoped>
.resourceList-title {
font-size: 17px;
font-weight: 600;
color: #000;
}
//
.operationButtonArea {
padding-bottom: 20px;
::v-deep .ant-btn-primary {
margin-right: 10px;
}
}
.editable-row-operations a {
margin-right: 8px;
}
</style>

@ -0,0 +1,92 @@
<template>
<a-card :bordered="false" style="margin-bottom: 16px" :body-style="{ paddingBottom: 0 }">
<a-form :model="formModel" :label-col="labelCol">
<a-row :gutter="16">
<a-col :xl="8" :md="12" :sm="24">
<a-form-item label="订单编号">
<a-input v-model:value="formModel.batchNo" placeholder="请输入" />
</a-form-item>
</a-col>
<a-col :xl="8" :md="12" :sm="24">
<a-form-item label="报名状态">
<dict-select
v-model:value="formModel.enrollStatus"
dict-code="registration_status"
allow-clear
placeholder="请选择"
/>
</a-form-item>
</a-col>
<a-col :xl="8" :md="12" :sm="24">
<a-form-item label="填写时间">
<a-range-picker
v-model:value="searchTimeValue"
show-time
format="YYYY-MM-DD HH:mm:ss"
style="width: 100%"
:ranges="{
Today: [dayjs().startOf('date'), dayjs()]
}"
/>
</a-form-item>
</a-col>
<a-col :xl="24" :md="24" :sm="24">
<search-actions :loading="props.loading" @search="search" @reset="reset" />
</a-col>
</a-row>
</a-form>
</a-card>
</template>
<script setup lang="ts">
import { Form } from 'ant-design-vue'
import type { clueDetailsSearch } from '@/api/clueaccess/clueImport/types'
import dayjs from 'dayjs'
import type { Dayjs } from 'dayjs'
const useForm = Form.useForm
// label
const labelCol = { md: { span: 6 } }
const props = withDefaults(
defineProps<{
loading?: boolean
}>(),
{ loading: false }
)
const emits = defineEmits<{
(e: 'search', params: Record<string, any>): void
}>()
const searchTimeValue = ref<[Dayjs, Dayjs]>()
const formModel = reactive<clueDetailsSearch>({
batchNo: '',
enrollStatus: undefined,
//,
httpStatus: undefined
})
const { resetFields } = useForm(formModel)
const search = () => {
const param = toRaw(formModel)
if (searchTimeValue.value && searchTimeValue.value.length == 2) {
param.startTime = searchTimeValue.value[0].format('YYYY-MM-DD HH:mm:ss')
param.endTime = searchTimeValue.value[1].format('YYYY-MM-DD HH:mm:ss')
} else {
param.startTime = ''
param.endTime = ''
}
emits('search', param)
}
const reset = () => {
//
resetFields()
//
searchTimeValue.value = undefined
search()
}
</script>

@ -0,0 +1,132 @@
<template>
<a-modal
:title="title"
:visible="visible"
:mask-closable="false"
:centered="true"
:body-style="{ padding: '24px 40px 8px 40px' }"
:confirm-loading="submitLoading"
:width="500"
@cancel="handleClose"
>
<a-form
ref="formRef"
:model="formModel"
:label-col="labelCol"
:wrapper-col="wrapperCol"
class="interfaceDocking"
>
<a-form-item
ref="customNid"
label="手机号:"
:label-col="{ span: 4 }"
:wrapper-col="{ span: 14 }"
name="customNid"
:rules="[
{ required: true, message: '请输入手机号' },
{
pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
message: '请输入正确的手机号',
trigger: ['blur', 'change']
}
]"
>
<a-input v-model:value="formModel.customNid" title="点击一键复制" />
</a-form-item>
<a-form-item
ref="remark"
label="备注:"
:label-col="{ span: 4 }"
:wrapper-col="{ span: 14 }"
name="remark"
:rules="[{ required: true, message: '请输入备注', trigger: ['blur', 'change'] }]"
>
<a-textarea v-model:value="formModel.remark" />
</a-form-item>
</a-form>
<template #footer>
<a-button @click="handleClose()"></a-button>
<a-button type="primary" style="margin-left: 10px" @click="onSubmit()"></a-button>
</template>
</a-modal>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue'
import { useModal } from '@/hooks/modal'
import { useFormAction, FormAction } from '@/hooks/form'
import type { ColProps } from 'ant-design-vue'
import type { CustomerListRecord } from '@/api/customermanagement/customerList/types'
import { doRequest } from '@/utils/axios/request'
import { singleImport } from '@/api/clueaccess/clueImport'
import { message } from 'ant-design-vue'
import type { ValidateErrorEntity } from 'ant-design-vue/es/form/interface'
const labelCol: ColProps = { sm: { span: 24 }, md: { span: 4 } }
const wrapperCol: ColProps = { sm: { span: 24 }, md: { span: 20 } }
const emits = defineEmits<{
(e: 'submit-success', resetPageIndex?: boolean): void
}>()
const { title, visible, openModal, closeModal } = useModal()
const { formAction } = useFormAction()
//
const submitLoading = ref(false)
const formRef = ref()
interface FormState {
customNid: string
remark: string
}
//
const formModel = reactive<FormState>({
customNid: '',
remark: ''
})
//
const onSubmit = () => {
formRef.value
.validate()
.then(() => {
doRequest(singleImport(formModel), {
onSuccess: (res: any) => {
if (res.code === 200) {
message.success('导入成功')
handleClose()
emits('submit-success')
}
}
})
})
.catch((error: ValidateErrorEntity<FormState>) => {
console.log('error', error)
})
}
const resetForm = () => {
formRef.value.resetFields()
}
//
const handleClose = () => {
resetForm()
closeModal()
submitLoading.value = false
}
interface openObj {
newFormAction: FormAction
type?: number | undefined
record?: CustomerListRecord
}
defineExpose({
open(opendata: openObj) {
openModal()
title.value = '单个导入'
formAction.value = opendata.newFormAction
}
})
</script>
<style lang="less" scoped></style>

@ -0,0 +1,219 @@
<!--渠道标识-->
<template>
<div class="channelIdentification-page">
<div class="channelIdentification-wrap">
<div class="title">渠道标识设置</div>
<a-form
ref="formRef"
:model="channelIdFormState"
:rule="channelIdRules"
:label-col="labelCol"
:wrapper-col="wrapperCol"
class="interfaceDocking"
>
<a-form-item
label="渠道标识:"
:label-col="{ span: 2 }"
:wrapper-col="{ span: 21 }"
name="tags"
>
<a-checkbox-group
v-model:value="channelIdFormState.fieldList"
@change="handleCheckboxChange"
>
<a-checkbox v-for="item in monitoringList" :key="item.id" :value="item.routeName">{{
item.routeName
}}</a-checkbox>
</a-checkbox-group>
<a-form-item-rest>
<a-input
v-show="inputVisible"
ref="inputRef"
v-model:value="inputValue"
type="text"
size="small"
:style="{ width: '78px' }"
@blur="handleInputConfirm"
@keyup.enter="handleInputConfirm"
></a-input>
<a-tag
v-show="!inputVisible"
style="background: #fff; border-style: dashed"
@click="showInput"
>
<plus-outlined />
新建标识
</a-tag>
</a-form-item-rest>
</a-form-item>
<a-form-item style="text-align: center">
<a-button type="primary" style="margin-left: 10px" @click="onSubmit()"></a-button>
</a-form-item>
</a-form>
</div>
</div>
</template>
<script setup lang="ts">
//
defineOptions({ name: 'ChannelIdentification' })
import { ref, reactive, nextTick } from 'vue'
import { PlusOutlined } from '@ant-design/icons-vue'
//
import type { UnwrapRef } from 'vue'
import { labelCol, wrapperCol } from '@/hooks/form'
//
import { doRequest } from '@/utils/axios/request'
import { message } from 'ant-design-vue'
import { channelIdList, addChannelId, application } from '@/api/clueaccess/clueImport'
import type { applicationData } from '@/api/clueaccess/clueImport/types'
interface MonitoringList {
id?: number | undefined
routeName?: string | undefined
routeStatus?: number | undefined
}
//checkbox
const monitoringList = ref<MonitoringList[]>([])
//
interface ChannelIdFormState {
fieldList: string[]
}
const channelIdFormState: UnwrapRef<ChannelIdFormState> = reactive({
fieldList: []
})
const channelIdRules = {
fieldList: [{ required: true, message: '请输入渠道标识', trigger: 'blur' }]
}
//
const inputVisible = ref(false)
//model
const inputValue = ref('')
// loading
const subloading = ref(false)
//
const submitArr = ref<MonitoringList[]>([])
const checkedArr = ref<MonitoringList[]>([])
const uncheckedArr = ref<MonitoringList[]>([])
onMounted(() => {
getChannelList()
})
//
const getChannelList = () => {
doRequest(channelIdList({}), {
onSuccess: (res: any) => {
if (res.data.length > 0) {
monitoringList.value = res.data
}
if (monitoringList.value) {
monitoringList.value.forEach((item: any) => {
if (item.routeStatus === 1) {
channelIdFormState.fieldList.push(item.routeName)
}
})
}
}
})
}
///
const handleInputConfirm = () => {
const inputValues = inputValue.value
if (inputValues.trim()) {
if (monitoringList.value) {
const index = monitoringList.value.findIndex(
(item: any) => item.routeName === inputValue.value
)
if (index === -1) {
// arridobjid
doRequest(
addChannelId({
routeName: inputValues
}),
{
onSuccess: (res: any) => {
if (res.code === 200) {
getChannelList()
} else {
message.error(res.msg)
}
}
}
)
} else {
message.info('该渠道标签已存在')
}
}
}
inputVisible.value = false
inputValue.value = ''
}
const showInput = () => {
inputVisible.value = true
nextTick(() => {
inputRef.value.focus()
})
}
// change
const handleCheckboxChange = (val: any) => {
checkedArr.value = []
uncheckedArr.value = []
submitArr.value = []
checkedArr.value = monitoringList.value
.filter(item => {
return val.includes(item.routeName)
})
.map(item => {
return Object.assign({}, { id: item.id, routeStatus: 1 })
})
uncheckedArr.value = monitoringList.value
.filter(item => {
return !val.includes(item.routeName)
})
.map(item => {
return Object.assign({}, { id: item.id, routeStatus: 0 })
})
submitArr.value.push(...checkedArr.value, ...uncheckedArr.value)
}
// ()
const onSubmit = () => {
formRef.value
.validate()
.then(() => {
subloading.value = true
doRequest(application(submitArr.value), {
onSuccess: (res: any) => {
if (res.code === 200) {
message.success('操作成功')
getChannelList()
subloading.value = false
}
}
})
})
.catch((error: any) => {
return false
})
}
const formRef = ref()
const inputRef = ref()
</script>
<style lang="less" scoped>
.channelIdentification-page {
width: 100%;
height: calc(100vh - 84px);
padding: 10px;
.channelIdentification-wrap {
padding: 20px;
background-color: #fff;
}
.title {
font-size: 17px;
font-weight: 600;
color: #000;
margin-bottom: 20px;
}
}
</style>

@ -0,0 +1,429 @@
<!---->
<template>
<div class="clueImport-page">
<div class="clueImport-wrap">
<div class="title">线索导出</div>
<a-form
ref="otherFormRef"
:model="otherFormState"
:label-col="labelCol"
:wrapper-col="wrapperCol"
>
<!-- <a-row>
<a-col :xs="16" :sm="16" :md="16" :lg="16" :xl="16"> -->
<a-form-item
ref="channelName"
label="渠道名:"
:label-col="{ span: 3 }"
:wrapper-col="{ span: 14 }"
name="channelName"
:rules="[{ required: true, message: '请输入渠道名', trigger: 'blur' }]"
>
<a-input v-model:value="otherFormState.channelName" style="width: 70%" />
</a-form-item>
<a-form-item
ref="secret"
label="secret"
:label-col="{ span: 3 }"
:wrapper-col="{ span: 14 }"
name="secret"
>
<a-input v-model:value="otherFormState.secret" style="width: 70%; margin-right: 5%" />
</a-form-item>
<a-form-item
ref="appid"
label="appid"
:label-col="{ span: 3 }"
:wrapper-col="{ span: 14 }"
name="appid"
>
<div style="display: flex; align-items: center">
<a-input v-model:value="otherFormState.appid" style="width: 70%" />
<a-button
type="primary"
style="margin-top: -60px; margin-left: 5%"
@click="generatePushAddress"
>一键生成</a-button
>
</div>
</a-form-item>
<span type="primary" style="margin: 0 20px" @click.stop="downloadTemplate"
>接口文档下载</span
>
<!-- </a-col> -->
<!-- <a-col :xs="13" :sm="13" :md="13" :lg="13" :xl="13">
<a-button type="primary">一键生成</a-button></a-col
> -->
<!-- <a-col :xs="13" :sm="13" :md="13" :lg="13" :xl="13">
<a-form-item :wrapper-col="{ span: 24 }" style="text-align: end">
<a-button @click="adPlatformResetForm()"></a-button>
<a-button type="primary" style="margin-left: 10px" @click="adPlatformSubmit()"
>一键生成</a-button
>
</a-form-item>
</a-col>
<a-col :xs="13" :sm="13" :md="13" :lg="13" :xl="13">
<span type="primary" style="margin: 0 20px" @click.stop="downloadTemplate"
>接口文档下载</span
>
</a-col> -->
<!-- </a-row> -->
</a-form>
<!-- 对话框 -->
<a-modal v-model:visible="visible" title="提示" @ok="handleOk">
<template #footer>
<a-button key="Return" @click="handleCancel"></a-button>
<a-button key="Submit" type="primary" :loading="loading" @click="handleOk"></a-button>
</template>
<p>
上传成功,表头信息是否为<span
v-for="(item, index) in resData"
:key="index"
style="font-weight: 700"
>{{ item }}</span
>请确认?
</p>
</a-modal>
</div>
</div>
</template>
<script setup lang="ts">
//
defineOptions({ name: 'ClueImport' })
import { reactive, ref, toRaw } from 'vue'
//:
import { labelCol, wrapperCol } from '@/hooks/form'
import type { ValidateErrorEntity } from 'ant-design-vue/es/form/interface'
import type { UnwrapRef } from 'vue'
//:
import { UploadOutlined } from '@ant-design/icons-vue'
import type { UploadFile } from 'ant-design-vue/lib/upload/interface'
import {
downloadI18nDataExcelTemplate,
importI18nDataExcel,
deleteFile,
checkHeaderInformation
} from '@/api/clueaccess/clueImport'
import { remoteFileDownload } from '@/utils/file-utils'
import { doRequest } from '@/utils/axios/request'
import type { UploadChangeParam } from 'ant-design-vue'
import type { I18nImportData } from '@/api/clueaccess/clueImport/types'
//广
import { getAuthorizedPlatformLink, singleImport } from '@/api/clueaccess/clueImport'
//
// import useClipboard from 'vue-clipboard3'
import { message } from 'ant-design-vue'
//tabs
const activeKey = ref('1')
const handleTabChange = (key: string | number) => {
console.log('Tab changed:', key)
resetForm()
otherResetForm()
//
}
//
// const { t } = useI18n()
const fileLists = ref<UploadFile[]>([])
const customDefinitionFileList = ref<UploadFile[]>([])
// const formModelFile = ref<UploadFile>()
const formModel = reactive<I18nImportData>({
file: undefined
// importMode: ImportMode.SKIP_EXISTING
})
function beforeUpload(file: UploadFile) {
// fileList.value = [file]
// customDefinitionFileList.value = [file]
formModel.file = file
return false
}
const resData = reactive<string[]>([])
const currentId = ref('')
const handleChange = ({ file, fileList }: UploadChangeParam) => {
console.log(file.status, 'file')
if (file.status === 'removed') {
if (currentId.value) {
handleCancel()
} else {
fileList.length = 0
message.success('移除成功', 1)
}
} else {
doRequest(checkHeaderInformation(toRaw(formModel)), {
onSuccess: (res: any) => {
console.log(res, 'res')
resData.length = 0
if (res.code === 200) {
visible.value = true
resData.push(...res.data.excelHead)
currentId.value = res.data.clueRecordId
}
},
onFail: error => {
currentId.value = ''
fileList[0].status = 'error'
fileList[0].response = error.message
customDefinitionFileList.value.length = 0
fileLists.value.length = 0
// currentId.value = ''
// fileList[0].status = 'error'
// fileList[0].response = error.message
// customDefinitionFileList.value.length = 0
// fileLists.value.length = 0
},
onError: () => {
// fileList[0].status = 'error'
// fileLists.value.length = 0
// fileList[0].status = 'error'
// fileList.length = 0
// submitLoading.value = false
}
})
}
}
// const parseTime = (time: any, cFormat: any) => {
// const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
// let date
// if (typeof time === 'undefined' || time === null || time === 'null') {
// return ''
// } else if (typeof time === 'object') {
// date = time
// } else {
// if (typeof time === 'string' && /^[0-9]+$/.test(time)) {
// time = parseInt(time)
// }
// if (typeof time === 'number' && time.toString().length === 10) {
// time = time * 1000
// }
// date = new Date(time)
// }
// const formatObj = {
// y: date.getFullYear(),
// m: date.getMonth() + 1,
// d: date.getDate(),
// h: date.getHours(),
// i: date.getMinutes(),
// s: date.getSeconds(),
// a: date.getDay()
// }
// const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result: any, key: any) => {
// let value = formatObj[key]
// // Note: getDay() returns 0 on Sunday
// if (key === 'a') {
// return ['', '', '', '', '', '', ''][value]
// }
// if (result.length > 0 && value < 10) {
// value = '0' + value
// }
// return value || 0
// })
// return time_str
// }
// const downloadFile = (obj: any, name: any, suffix: any) => {
// const url = window.URL.createObjectURL(
// new Blob([obj], { type: 'application/vnd.ms-excel;chartset=utf-8' })
// )
// console.log('🚀 ~ file: index.js:380 ~ downloadFile ~ url:', url)
// const link = document.createElement('a')
// link.style.display = 'none'
// link.href = url
// const fileName = parseTime(new Date(), '{y}-{m}-{d} {h}:{i}:{s}') + '-' + name + '.' + suffix
// // const fileName = parseTime(new Date()) + '-' + newHeaders + '.' + suffix
// link.setAttribute('download', fileName)
// document.body.appendChild(link)
// link.click()
// document.body.removeChild(link)
// }
//
const loading = ref<boolean>(false)
const visible = ref<boolean>(false)
//
const handleOk = () => {
doRequest(importI18nDataExcel(toRaw({ recordId: currentId.value })), {
onSuccess: (res: any) => {
// resData.length = 0
// if (res.code === 200) {
// visible.value = true
// resData.push(...res.data.excelHead)
// currentId.value = res.data.clueRecordId
message.success('上传成功', 1)
visible.value = false
customDefinitionFileList.value.length = 0
fileLists.value.length = 0
// }
},
onFail: error => {
// currentId.value = ''
// fileList[0].status = 'error'
// fileList[0].response = error.message
// customDefinitionFileList.value.length = 0
// fileLists.value.length = 0
},
onError: () => {
// fileList[0].status = 'error'
// fileLists.value.length = 0
// fileList[0].status = 'error'
// fileList.length = 0
// submitLoading.value = false
}
})
}
//
const handleCancel = () => {
doRequest(deleteFile(currentId.value), {
onSuccess: (res: any) => {
if (res.code === 200) {
message.success('删除成功', 1)
fileLists.value.length = 0
visible.value = false
}
},
onError: () => {
message.error('删除失败', 1)
}
})
}
// excel
function downloadTemplate() {
message.info('暂无文档,正在开发中')
// downloadI18nDataExcelTemplate().then(response => {
// remoteFileDownload(response)
// // downloadFile(response, '', 'xlsx')
// })
}
//
interface FormState {
customNid: string
remark: string
}
const formRef = ref()
const formState: UnwrapRef<FormState> = reactive({
customNid: '',
remark: ''
})
// const rules = {
// name: [{ required: true, message: '', trigger: 'blur' }]
// }
const onSubmit = () => {
formRef.value
.validate()
.then(() => {
doRequest(singleImport(formState), {
onSuccess: (res: any) => {
if (res.code === 200) {
message.success('导入成功')
}
}
})
})
.catch((error: ValidateErrorEntity<FormState>) => {
console.log('error', error)
})
}
const resetForm = () => {
formRef.value.resetFields()
}
//
interface OtherFormState {
channelName: string
channelIdentification: string
authorizationPlatform: string
pushAddress: string
secret: string
appid: string
}
const otherFormRef = ref()
const otherFormState: UnwrapRef<OtherFormState> = reactive({
channelName: '',
channelIdentification: '1',
authorizationPlatform: '',
pushAddress: '',
secret: '',
appid: ''
})
// const rules = {
// name: [{ required: true, message: '', trigger: 'blur' }]
// }
const otherSubmit = (type: string) => {
otherFormRef.value
.validate()
.then(() => {
// console.log('values', adPlatformFormState, toRaw(adPlatformFormState))
if (type === '巨量授权') {
doRequest(getAuthorizedPlatformLink({ authorizeName: otherFormState.channelName }), {
onSuccess: (res: any) => {
if (res.code === 200) {
window.location.href = res.data
}
}
})
} else {
message.info('正在开发中')
}
})
.catch((error: ValidateErrorEntity<OtherFormState>) => {
console.log('error', error)
})
}
const otherResetForm = () => {
otherFormRef.value.resetFields()
}
//url
const getAuthorizedPlatformUrl = (type: string) => {
otherSubmit(type)
}
//
const generatePushAddress = () => {
const fields = ['channelName'] //
otherFormRef.value
?.validate(fields)
.then(() => {
//
console.log('校验通过')
})
.catch(() => {
//
console.log('校验失败')
})
}
</script>
<style lang="less" scoped>
.clueImport-page {
width: 100%;
height: calc(100vh - 84px);
padding: 10px;
.clueImport-wrap {
padding: 20px;
background-color: #fff;
}
.title {
font-size: 17px;
font-weight: 600;
color: #000;
padding-bottom: 20px;
}
.importMode {
font-size: 13px;
font-weight: 600;
color: #000;
margin: 20px 0;
}
.importModeOne,
.importModeTwo {
margin: 0 0 50px 13px;
}
.a-upload-button {
border-radius: 20px;
}
.a-upload__tip {
margin-top: 10px;
}
}
</style>

@ -2,10 +2,65 @@
<template> <template>
<div class="clueImport-page"> <div class="clueImport-page">
<div class="clueImport-wrap"> <div class="clueImport-wrap">
<div class="title">线索导入</div> <!-- <div class="title">线索导入</div> -->
<!-- 导入方式一 --> <a-tabs v-model:activeKey="activeKey" @change="handleTabChange">
<a-tab-pane key="1" tab="单个导入">
<!-- 导入方式一:表单导入 -->
<div class="importModeTwo">
<a-form
ref="formRef"
:model="formState"
:label-col="labelCol"
:wrapper-col="wrapperCol"
class="interfaceDocking"
>
<a-row>
<a-col :xs="13" :sm="13" :md="13" :lg="13" :xl="13">
<a-form-item
ref="customNid"
label="手机号:"
:label-col="{ span: 3 }"
:wrapper-col="{ span: 12 }"
name="customNid"
:rules="[
{ required: true, message: '请输入手机号' },
{
pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
message: '请输入正确的手机号',
trigger: ['blur', 'change']
}
]"
>
<a-input v-model:value="formState.customNid" title="点击一键复制" />
</a-form-item>
<a-form-item
ref="remark"
label="备注:"
:label-col="{ span: 3 }"
:wrapper-col="{ span: 12 }"
name="remark"
:rules="[
{ required: true, message: '请输入备注', trigger: ['blur', 'change'] }
]"
>
<a-textarea v-model:value="formState.remark" />
</a-form-item>
</a-col>
<a-col :xs="13" :sm="13" :md="13" :lg="13" :xl="13">
<a-form-item :wrapper-col="{ span: 15 }" style="text-align: end">
<a-button @click="resetForm()"></a-button>
<a-button type="primary" style="margin-left: 10px" @click="onSubmit()"
>确定</a-button
>
</a-form-item>
</a-col>
</a-row>
</a-form>
</div>
</a-tab-pane>
<a-tab-pane key="2" tab="批量导入" force-render>
<!-- 导入方式二:文件上传 -->
<div class="importModeOne"> <div class="importModeOne">
<div class="importMode">导入方式一文件上传</div>
<div style="display: flex"></div> <div style="display: flex"></div>
<div style="display: flex"> <div style="display: flex">
<a-upload <a-upload
@ -44,45 +99,128 @@
</a-upload> </a-upload>
</div> </div>
</div> </div>
<!-- 暂时注释,之后要用,不能删除!!!!!!!!!!!!!!!!!!!!!!! --> </a-tab-pane>
<!-- 导入方式二 --> <a-tab-pane key="3" tab="广告平台接入"> </a-tab-pane>
<!-- <div class="importModeTwo"> <a-tab-pane key="4" tab="外呼接入"> </a-tab-pane>
<div class="importMode">导入方式二接口对接</div> <a-tab-pane key="5" tab="第三方接入"> </a-tab-pane>
</a-tabs>
<a-form <a-form
ref="formRef" v-if="activeKey === '3' || activeKey === '4' || activeKey === '5'"
:model="formState" ref="otherFormRef"
:model="otherFormState"
:label-col="labelCol" :label-col="labelCol"
:wrapper-col="wrapperCol" :wrapper-col="wrapperCol"
class="interfaceDocking"
> >
<a-row> <a-row>
<a-col :xs="13" :sm="13" :md="13" :lg="13" :xl="13"> <a-col :xs="16" :sm="16" :md="16" :lg="16" :xl="16">
<a-form-item <a-form-item
ref="name" ref="channelName"
label="秘钥" label="渠道名"
:label-col="{ span: 5 }" :label-col="{ span: 3 }"
:wrapper-col="{ span: 19 }" :wrapper-col="{ span: 14 }"
name="name" name="channelName"
:rules="[{ required: true, message: '请输入秘钥', trigger: 'blur' }]" :rules="[{ required: true, message: '请输入渠道名', trigger: 'blur' }]"
> >
<a-input v-model:value="otherFormState.channelName" style="width: 70%" />
</a-form-item>
<a-form-item
v-if="activeKey === '3' || activeKey === '4'"
ref="channelIdentification"
label="渠道标识:"
:label-col="{ span: 3 }"
name="channelIdentification"
>
<a-radio-group v-model:value="otherFormState.channelIdentification">
<a-radio value="1">Sponsor</a-radio>
<a-radio value="2">Venue</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item
v-if="activeKey === '3'"
ref="authorizationPlatform"
label="平台授权:"
:label-col="{ span: 3 }"
:wrapper-col="{ span: 14 }"
name="authorizationPlatform"
>
<a-button style="margin-right: 10px" @click="getAuthorizedPlatformUrl('巨量授权')"
>巨量授权</a-button
>
<a-button style="margin-right: 10px" @click="getAuthorizedPlatformUrl('百度授权')"
>百度授权</a-button
>
<a-button @click="getAuthorizedPlatformUrl('广点通授权')">广</a-button>
</a-form-item>
<a-form-item
v-if="activeKey === '4'"
ref="pushAddress"
label="推送地址:"
:label-col="{ span: 3 }"
:wrapper-col="{ span: 14 }"
name="pushAddress"
>
<div style="display: flex; align-items: center">
<a-input <a-input
v-model:value="formState.name" v-model:value="otherFormState.pushAddress"
title="点击一键复制" style="margin-right: 5%; width: 70%"
@click="($event:any) => copy(formState.name)"
/> />
<a-button type="primary" @click="generatePushAddress"></a-button>
</div>
</a-form-item> </a-form-item>
<a-form-item
v-if="activeKey === '5'"
ref="secret"
label="secret"
:label-col="{ span: 3 }"
:wrapper-col="{ span: 14 }"
name="secret"
>
<a-input v-model:value="otherFormState.secret" style="width: 70%; margin-right: 5%" />
</a-form-item>
<a-form-item
v-if="activeKey === '5'"
ref="appid"
label="appid"
:label-col="{ span: 3 }"
:wrapper-col="{ span: 14 }"
name="appid"
>
<div style="display: flex; align-items: center">
<a-input v-model:value="otherFormState.appid" style="width: 70%" />
<a-button
type="primary"
style="margin-top: -60px; margin-left: 5%"
@click="generatePushAddress"
>一键生成</a-button
>
</div>
</a-form-item>
<span
v-if="activeKey === '5'"
type="primary"
style="margin: 0 20px"
@click.stop="downloadTemplate"
>接口文档下载</span
>
</a-col> </a-col>
<a-col :xs="13" :sm="13" :md="13" :lg="13" :xl="13"> <!-- <a-col :xs="13" :sm="13" :md="13" :lg="13" :xl="13">
<a-button type="primary">一键生成</a-button></a-col
> -->
<!-- <a-col :xs="13" :sm="13" :md="13" :lg="13" :xl="13">
<a-form-item :wrapper-col="{ span: 24 }" style="text-align: end"> <a-form-item :wrapper-col="{ span: 24 }" style="text-align: end">
<a-button type="primary" @click="onSubmit()">API</a-button> <a-button @click="adPlatformResetForm()"></a-button>
<a-button type="primary" style="margin-left: 10px" @click="resetForm()" <a-button type="primary" style="margin-left: 10px" @click="adPlatformSubmit()"
>生成秘钥</a-button >一键生成</a-button
> >
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :xs="13" :sm="13" :md="13" :lg="13" :xl="13">
<span type="primary" style="margin: 0 20px" @click.stop="downloadTemplate"
>接口文档下载</span
>
</a-col> -->
</a-row> </a-row>
</a-form> </a-form>
</div> -->
<!-- 对话框 --> <!-- 对话框 -->
<a-modal v-model:visible="visible" title="提示" @ok="handleOk"> <a-modal v-model:visible="visible" title="提示" @ok="handleOk">
<template #footer> <template #footer>
@ -106,7 +244,11 @@
// //
defineOptions({ name: 'ClueImport' }) defineOptions({ name: 'ClueImport' })
import { reactive, ref, toRaw } from 'vue' import { reactive, ref, toRaw } from 'vue'
// //:
import { labelCol, wrapperCol } from '@/hooks/form'
import type { ValidateErrorEntity } from 'ant-design-vue/es/form/interface'
import type { UnwrapRef } from 'vue'
//:
import { UploadOutlined } from '@ant-design/icons-vue' import { UploadOutlined } from '@ant-design/icons-vue'
import type { UploadFile } from 'ant-design-vue/lib/upload/interface' import type { UploadFile } from 'ant-design-vue/lib/upload/interface'
import { import {
@ -117,22 +259,22 @@ import {
} from '@/api/clueaccess/clueImport' } from '@/api/clueaccess/clueImport'
import { remoteFileDownload } from '@/utils/file-utils' import { remoteFileDownload } from '@/utils/file-utils'
import { doRequest } from '@/utils/axios/request' import { doRequest } from '@/utils/axios/request'
import type { UploadChangeParam, UploadProps } from 'ant-design-vue' import type { UploadChangeParam } from 'ant-design-vue'
import type { I18nImportData } from '@/api/clueaccess/clueImport/types' import type { I18nImportData } from '@/api/clueaccess/clueImport/types'
// import type { I18nImportData } from '@/api/i18n/types' //广
// import { Moment } from 'moment' import { getAuthorizedPlatformLink, singleImport } from '@/api/clueaccess/clueImport'
// import { useI18n } from 'vue-i18n'
//
import { labelCol, wrapperCol } from '@/hooks/form'
import type { ValidateErrorEntity } from 'ant-design-vue/es/form/interface'
import type { UnwrapRef } from 'vue'
// //
import useClipboard from 'vue-clipboard3' // import useClipboard from 'vue-clipboard3'
import { message } from 'ant-design-vue' import { message } from 'ant-design-vue'
//
import type { ProTableInstanceExpose } from '#/table'
import axios from 'axios'
//tabs
const activeKey = ref('1')
const handleTabChange = (key: string | number) => {
console.log('Tab changed:', key)
resetForm()
otherResetForm()
//
}
// //
// const { t } = useI18n() // const { t } = useI18n()
const fileLists = ref<UploadFile[]>([]) const fileLists = ref<UploadFile[]>([])
@ -247,13 +389,6 @@ const handleChange = ({ file, fileList }: UploadChangeParam) => {
// document.body.removeChild(link) // document.body.removeChild(link)
// } // }
// excel
function downloadTemplate() {
downloadI18nDataExcelTemplate().then(response => {
remoteFileDownload(response)
// downloadFile(response, '', 'xlsx')
})
}
// //
const loading = ref<boolean>(false) const loading = ref<boolean>(false)
const visible = ref<boolean>(false) const visible = ref<boolean>(false)
@ -303,14 +438,24 @@ const handleCancel = () => {
} }
}) })
} }
// excel
function downloadTemplate() {
message.info('暂无文档,正在开发中')
// downloadI18nDataExcelTemplate().then(response => {
// remoteFileDownload(response)
// // downloadFile(response, '', 'xlsx')
// })
}
// //
interface FormState { interface FormState {
name: string customNid: string
remark: string
} }
const formRef = ref() const formRef = ref()
const formState: UnwrapRef<FormState> = reactive({ const formState: UnwrapRef<FormState> = reactive({
name: '' customNid: '',
remark: ''
}) })
// const rules = { // const rules = {
// name: [{ required: true, message: '', trigger: 'blur' }] // name: [{ required: true, message: '', trigger: 'blur' }]
@ -319,7 +464,13 @@ const onSubmit = () => {
formRef.value formRef.value
.validate() .validate()
.then(() => { .then(() => {
console.log('values', formState, toRaw(formState)) doRequest(singleImport(formState), {
onSuccess: (res: any) => {
if (res.code === 200) {
message.success('导入成功')
}
}
})
}) })
.catch((error: ValidateErrorEntity<FormState>) => { .catch((error: ValidateErrorEntity<FormState>) => {
console.log('error', error) console.log('error', error)
@ -328,17 +479,70 @@ const onSubmit = () => {
const resetForm = () => { const resetForm = () => {
formRef.value.resetFields() formRef.value.resetFields()
} }
//
const { toClipboard } = useClipboard() //
const copy = async (text: string) => {
const re = new RegExp('<[^<>]+>', 'g') interface OtherFormState {
const cotext = text.replace(re, '') channelName: string
try { channelIdentification: string
await toClipboard(cotext) // authorizationPlatform: string
message.success('复制成功', 1) pushAddress: string
} catch (e) { secret: string
console.error(e) appid: string
}
const otherFormRef = ref()
const otherFormState: UnwrapRef<OtherFormState> = reactive({
channelName: '',
channelIdentification: '1',
authorizationPlatform: '',
pushAddress: '',
secret: '',
appid: ''
})
// const rules = {
// name: [{ required: true, message: '', trigger: 'blur' }]
// }
const otherSubmit = (type: string) => {
otherFormRef.value
.validate()
.then(() => {
// console.log('values', adPlatformFormState, toRaw(adPlatformFormState))
if (type === '巨量授权') {
doRequest(getAuthorizedPlatformLink({ authorizeName: otherFormState.channelName }), {
onSuccess: (res: any) => {
if (res.code === 200) {
window.location.href = res.data
}
}
})
} else {
message.info('正在开发中')
} }
})
.catch((error: ValidateErrorEntity<OtherFormState>) => {
console.log('error', error)
})
}
const otherResetForm = () => {
otherFormRef.value.resetFields()
}
//url
const getAuthorizedPlatformUrl = (type: string) => {
otherSubmit(type)
}
//
const generatePushAddress = () => {
const fields = ['channelName'] //
otherFormRef.value
?.validate(fields)
.then(() => {
//
console.log('校验通过')
})
.catch(() => {
//
console.log('校验失败')
})
} }
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
@ -371,38 +575,5 @@ const copy = async (text: string) => {
.a-upload__tip { .a-upload__tip {
margin-top: 10px; margin-top: 10px;
} }
::v-deep .ant-col ant-form-item-label {
// width: 63px;
}
//
.interfaceDocking {
// width: 50%;
// min-width: 300px;
}
}
.ant-form-item {
// min-width: 220px;
}
.ant-form-item-control-input-content {
// min-width: 180px;
} }
::v-deep .ant-form-item-control-input {
// text-align: end !important;
// min-width: 180px;
}
.ant-row {
// justify-content: end !important;
}
::v-deep .ant-form-horizontal {
// min-width: 200px !important;
// flex: 1 1 1;
}
// ::v-deep .ant-col-sm-24 {
// flex: unset;
// }
// ::v-deep .ant-col {
// text-align: end;
// }
</style> </style>

@ -0,0 +1,263 @@
<template>
<a-modal
:title="title"
:visible="visible"
:mask-closable="false"
:centered="true"
:body-style="{ padding: '24px 40px 8px 40px' }"
:confirm-loading="submitLoading"
:width="650"
@ok="handleSubmit"
@cancel="handleClose"
>
<a-form
ref="otherFormRef"
:model="otherFormState"
:label-col="labelCol"
:wrapper-col="wrapperCol"
>
<a-form-item
v-if="formAction === FormAction.UPDATE"
ref="status"
label="使用状态:"
:label-col="{ span: 4 }"
:wrapper-col="{ span: 14 }"
name="status"
>
<a-switch v-model:checked="checked" @change="handleSwitchChange" />
</a-form-item>
<a-form-item
v-if="formAction === FormAction.CREATE"
ref="name"
label="渠道名:"
:label-col="{ span: 4 }"
:wrapper-col="{ span: 14 }"
name="name"
v-bind="validateInfos.name"
>
<a-input v-model:value="otherFormState.name" style="width: 70%" />
</a-form-item>
<a-form-item
v-if="type === 0 || type === 1"
ref="channelIdentifyingList"
label="渠道标识:"
:label-col="{ span: 4 }"
name="channelIdentifyingList"
v-bind="validateInfos.channelIdentifyingList"
>
<a-checkbox-group v-model:value="otherFormState.channelIdentifyingList" style="width: 100%">
<a-checkbox v-for="item in channelId" :key="item.id" :value="item.routeName">{{
item.routeName
}}</a-checkbox>
</a-checkbox-group>
</a-form-item>
<a-form-item
v-if="type === 0 && formAction === FormAction.CREATE"
ref="authType"
label="平台授权:"
:label-col="{ span: 4 }"
:wrapper-col="{ span: 14 }"
name="authType"
>
<a-radio-group v-model:value="otherFormState.authType">
<a-radio :value="0"> 巨量授权 </a-radio>
<a-radio :value="1"> 百度授权 </a-radio>
<a-radio :value="2"> 广点通授权 </a-radio>
</a-radio-group>
</a-form-item>
<span v-if="type === 2" type="primary" style="margin: 0 20px" @click.stop="downloadTemplate"
>接口文档下载</span
>
</a-form>
<template #footer>
<a-button @click="handleClose()"></a-button>
<a-button type="primary" @click="handleSubmit()"></a-button>
</template>
</a-modal>
</template>
<script setup lang="ts">
import { useModal } from '@/hooks/modal'
import { FormAction, useAdminForm, useFormAction } from '@/hooks/form'
import type { FormRequestMapping } from '@/hooks/form'
import { reactive, ref } from 'vue'
import type { ColProps } from 'ant-design-vue'
import { doRequest } from '@/utils/axios/request'
import { message } from 'ant-design-vue'
import type { UnwrapRef } from 'vue'
import { otherImport, updateOtherImport, channelIdList } from '@/api/clueaccess/clueImport'
import type { ConfigurationListRecord } from '@/api/clueaccess/clueImport/types'
const labelCol: ColProps = { sm: { span: 24 }, md: { span: 4 } }
const wrapperCol: ColProps = { sm: { span: 24 }, md: { span: 20 } }
//使
const checked = ref<boolean>(false)
const handleSwitchChange = (checked: any) => {
console.log('Switch changed:', checked)
//
if (checked === true) {
otherFormState.status = 1
} else {
otherFormState.status = 0
}
}
interface MonitoringModeTwoList {
routeName: string
id: number
}
//checkbox
const channelId = ref<MonitoringModeTwoList[]>([])
//广
const type = ref<number | undefined>(undefined)
const emits = defineEmits<{
(e: 'submit-success', resetPageIndex?: boolean): void
}>()
const { title, visible, openModal, closeModal } = useModal()
const { formAction } = useFormAction()
const otherFormRef = ref()
//
interface OtherFormState {
id?: number | undefined
userId?: number | undefined
createBy?: number | undefined
status: any
channelType: number | undefined
name: string | undefined
channelIdentifyingList?: string[]
authType: number | undefined
}
const otherFormState: UnwrapRef<OtherFormState> = reactive({
status: true,
channelType: undefined,
name: '',
channelIdentifyingList: [],
authType: 0
})
//
const otherFormRule = reactive<any>({
name: [{ required: true, message: '请输入渠道名', trigger: ['blur', 'change'] }],
channelIdentifyingList: [
{ type: 'array', required: true, message: '请选择渠道标识', trigger: ['blur', 'change'] }
]
})
//
const formRequestMapping: FormRequestMapping<OtherFormState> = {
[FormAction.CREATE]: otherImport,
[FormAction.UPDATE]: updateOtherImport
}
const { submitLoading, validateAndSubmit, validateInfos, resetFields } = useAdminForm(
formAction,
formRequestMapping,
otherFormState,
otherFormRule
)
/* 表单提交处理 */
const handleSubmit = () => {
if (checked.value === true) {
otherFormState.status = 1
} else {
otherFormState.status = 0
}
if (type.value === 0) {
otherFormState.channelType = 0
if (otherFormState.authType === 1 || otherFormState.authType === 2) {
message.info('正在开发中')
return false
}
} else if (type.value === 1) {
otherFormState.channelType = 1
} else if (type.value === 2) {
otherFormState.channelType = 2
otherFormRule.channelIdentifyingList = undefined
}
validateAndSubmit(
{ ...otherFormState },
{
onSuccess: (res: any) => {
if (res.code === 200) {
if (
type.value === 0 &&
otherFormState.authType === 0 &&
formAction.value === FormAction.CREATE
) {
window.location.href = res.data
}
message.success('导入成功')
closeModal()
submitLoading.value = false
emits('submit-success')
}
}
}
)
}
/* 弹窗关闭方法 */
const handleClose = () => {
closeModal()
submitLoading.value = false
}
function downloadTemplate() {
message.info('暂无文档,正在开发中')
// downloadI18nDataExcelTemplate().then(response => {
// remoteFileDownload(response)
// // downloadFile(response, '', 'xlsx')
// })
}
interface openObj {
newFormAction: FormAction
type?: number | undefined
record?: ConfigurationListRecord
}
defineExpose({
open(opendata: openObj) {
doRequest(channelIdList({ routeStatus: 1 }), {
onSuccess: (res: any) => {
if (res.code === 200) {
channelId.value = res.data
}
}
})
openModal()
resetFields()
if (opendata.newFormAction === FormAction.CREATE) {
type.value = opendata.type
} else {
otherFormState.id = opendata.record?.id
otherFormState.userId = opendata.record?.userId
otherFormState.createBy = opendata.record?.createBy
type.value = opendata.record?.channelType
otherFormState.name = opendata.record?.name
if (opendata.record?.status === 0) {
checked.value = false
} else {
checked.value = true
}
if (opendata.record) {
otherFormState.channelIdentifyingList?.push(...opendata.record.channelIdentifying)
}
}
if (type.value === 0) {
title.value = '广告平台接入'
} else if (type.value === 1) {
title.value = '外呼接入'
} else if (type.value === 2) {
title.value = '第三方接入'
}
formAction.value = opendata.newFormAction
}
})
</script>

@ -0,0 +1,207 @@
<template>
<!-- 头部 -->
<a-card :bordered="false" :body-style="{ paddingBottom: 0 }">
<div class="geopoliticalCustomers-title">配置列表</div>
<!-- <a-row>
<a-col
:span="8"
:offset="8"
style="margin-top: 24px; margin-bottom: 24px; width: 50%; border: 1px solid #dcdfe6"
>
<div style="display: flex; justify-content: center">
<a-statistic title="Active Users" :value="112893" style="margin-right: 50px" />
<a-statistic title="Account Balance (CNY)" :precision="2" :value="112893" />
</div>
</a-col>
</a-row> -->
</a-card>
<!-- 工具栏 -->
<configuration-list-search :loading="tableRef?.loading" @search="searchTable" />
<!-- 批量操作更多操作 -->
<a-card :bordered="false" :body-style="{ paddingBottom: 0 }">
<div class="operationButtonArea">
<a-button type="primary" @click="adPlatformAccess">广</a-button>
<a-button type="primary" @click="outboundCallAccess"></a-button>
<a-button type="primary" @click="thirdPartyAccess"></a-button>
</div>
</a-card>
<!-- 底部表格 -->
<pro-table
ref="tableRef"
row-key="customId"
:request="tableRequest"
:columns="columns"
:scroll="{ x: 1100 }"
:tool-bar-render="false"
class="protable"
:pagination="{ showQuickJumper: true }"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'operate'">
<operation-group>
<a @click="handleView(record)"></a>
<a @click="handleEdit(record)"></a>
</operation-group>
</template>
</template>
</pro-table>
<!-- 详情弹窗 -->
<configuration-list-view-modal
ref="configurationListViewModalRef"
@submit-success="reloadTable"
/>
<!-- 其他接入新建/编辑弹窗 -->
<configuration-list-modal ref="configurationListModalRef" @submit-success="reloadTable" />
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
//
import ProTable from '#/table'
import type { ProTableInstanceExpose, TableRequest, ProColumns } from '#/table'
import { mergePageParam } from '@/utils/page-utils'
import { FormAction } from '@/hooks/form'
import GeopoliticalCustomersModal from '@/views/geopoliticalCustomers/geopoliticalCustomersModal.vue'
import ConfigurationListModal from '@/views/configurationList/configurationListModal.vue'
import ConfigurationListViewModal from '@/views/configurationList/configurationListViewModal.vue'
import ConfigurationListSearch from '@/views/configurationList/configurationListSearch.vue'
import { pageAccessLogs } from '@/api/clueaccess/clueImport'
import type { GeopoliticalCustomersPageParam } from '@/api/geopoliticalCustomers/types'
import type { ConfigurationListRecord } from '@/api/clueaccess/clueImport/types'
import { message } from 'ant-design-vue'
defineOptions({ name: 'ConfigurationList' })
//
//
const tableRef = ref<ProTableInstanceExpose>()
//ref
const configurationListModalRef = ref<InstanceType<typeof ConfigurationListModal>>()
//ref
const configurationListViewModalRef = ref<InstanceType<typeof ConfigurationListViewModal>>()
//
let searchParams: GeopoliticalCustomersPageParam = {}
//
const tableRequest: TableRequest = (params, sorter, filter) => {
const pageParam = mergePageParam(params, sorter, filter)
return pageAccessLogs({ ...pageParam, ...searchParams })
}
//
const reloadTable = (resetPageIndex?: boolean) => {
tableRef.value?.actionRef?.reload(resetPageIndex)
}
//
const searchTable = (params: GeopoliticalCustomersPageParam) => {
searchParams = params
reloadTable(true) // tableRequest
}
//广
const adPlatformAccess = () => {
configurationListModalRef.value?.open({
newFormAction: FormAction.CREATE,
type: 0
})
}
//
const outboundCallAccess = () => {
configurationListModalRef.value?.open({
newFormAction: FormAction.CREATE,
type: 1
})
}
//
const thirdPartyAccess = () => {
message.info('正在开发中')
// configurationListModalRef.value?.open({
// newFormAction: FormAction.CREATE,
// type: 2
// })
}
//
const handleView = (record: ConfigurationListRecord) => {
configurationListViewModalRef.value?.open({
newFormAction: FormAction.UPDATE,
type: 'view',
record: record
})
}
//
const handleEdit = (record: ConfigurationListRecord) => {
configurationListModalRef.value?.open({
newFormAction: FormAction.UPDATE,
record: record
})
}
const columns: ProColumns[] = [
{
title: '编号',
width: 80,
dataIndex: 'id'
},
{
title: '渠道名称',
dataIndex: 'name',
width: 100
},
{
title: '接入类型',
dataIndex: 'channelType',
width: 120,
customRender: function ({ record }: any) {
if (record.channelType === 0) {
return h('div', '广告平台接入')
} else if (record.channelType === 1) {
return h('div', '外呼接入')
} else if (record.channelType === 2) {
return h('div', '第三方接入')
} else {
return h('div', '-')
}
}
},
{
title: '录入日期',
dataIndex: 'createTime',
width: 150,
align: 'center',
customRender: function ({ record }: any) {
if (record.createTime) {
return record.createTime
} else if (record.createTime === null) {
return '--'
}
}
},
{
key: 'operate',
title: '操作',
align: 'center',
width: 100,
fixed: 'right'
}
]
</script>
<style lang="less" scoped>
.geopoliticalCustomers-title {
font-size: 17px;
font-weight: 600;
color: #000;
}
//
.operationButtonArea {
padding-bottom: 20px;
::v-deep .ant-btn-primary {
margin-right: 10px;
}
}
.editable-row-operations a {
margin-right: 8px;
}
</style>

@ -0,0 +1,101 @@
<template>
<a-card :bordered="false" style="margin-bottom: 16px" :body-style="{ paddingBottom: 0 }">
<a-form :model="formModel" :label-col="labelCol">
<a-row :gutter="16">
<a-col :xl="8" :md="12" :sm="24">
<a-form-item label="渠道名称">
<a-input v-model:value="formModel.name" placeholder="请输入" />
</a-form-item>
</a-col>
<dict-select
v-model:value="formModel.httpStatus"
style="display: none"
dict-code="user_status"
allow-clear
placeholder="请选择"
/>
<a-col :xl="8" :md="12" :sm="24">
<a-form-item label="接入类型">
<dict-select
v-model:value="formModel.channelType"
dict-code="channel_type"
allow-clear
placeholder="请选择"
/>
</a-form-item>
</a-col>
<a-col :xl="8" :md="12" :sm="24">
<a-form-item label="创建时间">
<a-range-picker
v-model:value="searchTimeValue"
show-time
format="YYYY-MM-DD HH:mm:ss"
style="width: 100%"
:ranges="{
Today: [dayjs().startOf('date'), dayjs()]
}"
/>
</a-form-item>
</a-col>
<a-col :xl="24" :md="24" :sm="24">
<search-actions :loading="props.loading" @search="search" @reset="reset" />
</a-col>
</a-row>
</a-form>
</a-card>
</template>
<script setup lang="ts">
import { Form } from 'ant-design-vue'
import dayjs from 'dayjs'
import type { Dayjs } from 'dayjs'
import type { configurationListSearch } from '@/api/clueaccess/clueImport/types'
const useForm = Form.useForm
// label
const labelCol = { md: { span: 8 } }
const props = withDefaults(
defineProps<{
loading?: boolean
}>(),
{ loading: false }
)
const emits = defineEmits<{
(e: 'search', params: Record<string, any>): void
}>()
const searchTimeValue = ref<[Dayjs, Dayjs]>()
const formModel = reactive<configurationListSearch>({
//
name: '',
//
channelType: '',
//,
httpStatus: undefined
})
const { resetFields } = useForm(formModel)
const search = () => {
const param = toRaw(formModel)
if (searchTimeValue.value && searchTimeValue.value.length == 2) {
param.startTime = searchTimeValue.value[0].format('YYYY-MM-DD HH:mm:ss')
param.endTime = searchTimeValue.value[1].format('YYYY-MM-DD HH:mm:ss')
} else {
param.startTime = ''
param.endTime = ''
}
emits('search', param)
}
const reset = () => {
//
resetFields()
//
searchTimeValue.value = undefined
search()
}
</script>

@ -0,0 +1,194 @@
<template>
<a-modal
:title="title"
:visible="visible"
:mask-closable="false"
:centered="true"
:body-style="{ padding: '24px 40px 8px 40px' }"
:confirm-loading="submitLoading"
:width="650"
@cancel="handleClose"
>
<a-form :model="formModel" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-form-item
v-if="type === 0"
ref="link"
label="飞鱼跳转链接:"
:label-col="{ span: 4 }"
:wrapper-col="{ span: 20 }"
name="link"
>
<div style="display: flex; align-items: center">
<span
style="
margin-right: 15px;
text-overflow: -o-ellipsis-lastline;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
"
>{{ formModel.link }}</span
>
<a-button
key="submit"
type="primary"
size="small"
style="margin-right: 10px"
@click="copy(formModel.link)"
>复制</a-button
>
<a-button key="submit" size="small" type="primary" @click="goTo"></a-button>
</div>
</a-form-item>
<a-form-item
v-if="type === 1"
ref="pushAddress"
label="大坝推送地址:"
:label-col="{ span: 4 }"
:wrapper-col="{ span: 20 }"
name="pushAddress"
>
<div style="display: flex; align-items: center">
<span
style="
margin-right: 15px;
text-overflow: -o-ellipsis-lastline;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
"
>{{ formModel.pushAddress }}</span
>
<a-button
key="submit"
type="primary"
size="small"
style="margin-right: 10px"
@click="copy(formModel.pushAddress)"
>复制</a-button
>
</div>
</a-form-item>
<a-form-item
v-if="type === 2"
ref="secret"
label="secret"
:label-col="{ span: 4 }"
:wrapper-col="{ span: 15 }"
name="secret"
>
<a-input v-model:value="formModel.secret" style="width: 70%" />
</a-form-item>
<a-form-item
v-if="type === 2"
ref="appid"
label="appid"
:label-col="{ span: 4 }"
:wrapper-col="{ span: 15 }"
name="appid"
>
<a-input v-model:value="formModel.appid" style="width: 70%" />
</a-form-item>
</a-form>
<template #footer>
<a-button key="submit" @click="handleSubmit"></a-button>
</template>
</a-modal>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue'
import { useModal } from '@/hooks/modal'
import { useFormAction, FormAction } from '@/hooks/form'
import type { ColProps } from 'ant-design-vue'
import { linkDetail } from '@/api/clueaccess/clueImport'
import type { ConfigurationListRecord } from '@/api/clueaccess/clueImport/types'
import { doRequest } from '@/utils/axios/request'
//
import useClipboard from 'vue-clipboard3'
import { message } from 'ant-design-vue'
const labelCol: ColProps = { sm: { span: 24 }, md: { span: 4 } }
const wrapperCol: ColProps = { sm: { span: 24 }, md: { span: 20 } }
const type = ref<number | undefined>(undefined)
const { title, visible, openModal, closeModal } = useModal()
const { formAction } = useFormAction()
//
const submitLoading = ref(false)
interface formModelType {
link?: any
pushAddress?: any
secret?: string
appid?: string
}
//
const formModel = reactive<formModelType>({
link: '',
pushAddress: '',
secret: '',
appid: ''
})
//
const handleSubmit = () => {
closeModal()
}
//
const handleClose = () => {
closeModal()
submitLoading.value = false
}
//
const { toClipboard } = useClipboard()
const copy = async (text: string) => {
const re = new RegExp('<[^<>]+>', 'g')
const cotext = text.replace(re, '')
try {
await toClipboard(cotext) //
message.success('复制成功', 1)
} catch (e) {
console.error(e)
}
}
//
const goTo = () => {
window.location.href = formModel.link
}
interface openObj {
newFormAction: FormAction
type: string
record?: ConfigurationListRecord
}
//id
defineExpose({
open(opendata: openObj) {
doRequest(linkDetail({ id: opendata.record?.id }), {
onSuccess: (res: any) => {
if (res.code === 200) {
if (type.value === 0) {
formModel.link = res.data.channelUrl
} else if (type.value === 1) {
formModel.pushAddress = res.data.channelUrl
}
}
}
})
openModal()
title.value = '详情'
type.value = opendata.record?.channelType
formAction.value = opendata.newFormAction
}
})
</script>
<style lang="less" scoped></style>

@ -6,6 +6,11 @@
<!-- 工具栏 --> <!-- 工具栏 -->
<customer-list-search :loading="tableRef?.loading" @search="searchTable" /> <customer-list-search :loading="tableRef?.loading" @search="searchTable" />
<!-- 批量操作更多操作 --> <!-- 批量操作更多操作 -->
<a-card :bordered="false" :body-style="{ paddingBottom: 0 }">
<div class="download">
<a-button v-if="salesmanType === 0" @click.stop="downloadTemplate"></a-button>
</div>
</a-card>
<!-- 底部表格 --> <!-- 底部表格 -->
<pro-table <pro-table
ref="tableRef" ref="tableRef"
@ -21,7 +26,7 @@
<template v-if="column.key === 'operate'"> <template v-if="column.key === 'operate'">
<operation-group> <operation-group>
<a @click="handleView(record, '1')">详情</a> <a @click="handleView(record, '1')">详情</a>
<a @click="handleEdit(record, '2')"></a> <a v-if="salesmanType === 4" @click="handleEdit(record, '2')"></a>
<!-- 删除 --> <!-- 删除 -->
<!-- <delete-text-button @confirm="handleDelete(record)" /> --> <!-- <delete-text-button @confirm="handleDelete(record)" /> -->
<!-- 打电话 --> <!-- 打电话 -->
@ -29,6 +34,12 @@
<!-- <a href="tel:16603827325">操作</a> --> <!-- <a href="tel:16603827325">操作</a> -->
</operation-group> </operation-group>
</template> </template>
<template v-if="column.key === 'isEffective' && salesmanType === 4">
<a-switch
:checked="record.isEffective === 1 || record.isEffective === true ? true : false"
@change="switchChange(record)"
/>
</template>
<template v-else-if="column.key === 'clueLabelList'"> <template v-else-if="column.key === 'clueLabelList'">
<a-tooltip v-if="record.clueLabelList.length > 0"> <a-tooltip v-if="record.clueLabelList.length > 0">
<template #title <template #title
@ -86,25 +97,43 @@ import { pageAccessLogs } from '@/api/customermanagement/customerList'
import CustomerListSearch from '@/views/customermanagement/customerList/customerListSearch.vue' import CustomerListSearch from '@/views/customermanagement/customerList/customerListSearch.vue'
import CustomerListViewModal from '@/views/customermanagement/customerList/customerListModal.vue' import CustomerListViewModal from '@/views/customermanagement/customerList/customerListModal.vue'
import CustomerListEditModal from '@/views/customermanagement/customerList/customerListEditModal.vue' import CustomerListEditModal from '@/views/customermanagement/customerList/customerListEditModal.vue'
import { exportClueData, isEffective } from '@/api/customermanagement/customerList'
import { remoteFileDownload } from '@/utils/file-utils'
import { doRequest } from '@/utils/axios/request'
// import { useRoute } from 'vue-router' // import { useRoute } from 'vue-router'
// //
// import { doRequest } from '@/utils/axios/request' // import { doRequest } from '@/utils/axios/request'
// const route = useRoute() // const route = useRoute()
defineOptions({ name: 'CustomerList' })
//
import { useUserStore } from '@/stores/user-store'
const { userInfo } = useUserStore()
const salesmanType = ref<number | undefined>()
onMounted(() => { onMounted(() => {
salesmanType.value = userInfo?.salesmanType
const savedUserData = localStorage.getItem('clueId') const savedUserData = localStorage.getItem('clueId')
if (savedUserData) { if (savedUserData) {
searchTable({ clueId: savedUserData }) searchTable({ clueId: savedUserData })
localStorage.setItem('clueId', '') localStorage.setItem('clueId', '')
} }
if (salesmanType.value === 4) {
columns.value.splice(4, 0, {
title: '有效开关',
dataIndex: 'isEffective',
ellipsis: true,
align: 'center'
})
}
// const customerId = route.query.clueId as string // 使 // const customerId = route.query.clueId as string // 使
// if (customerId) { // if (customerId) {
// searchTable({ clueId: customerId }) // searchTable({ clueId: customerId })
// } // }
}) })
// onMounted(() => {
defineOptions({ name: 'CustomerList' }) // })
// //
// const handleMenuClick: MenuProps['onClick'] = e => { // const handleMenuClick: MenuProps['onClick'] = e => {
@ -122,7 +151,7 @@ let searchParams: CustomerListQO = {}
// //
const tableRequest: TableRequest = (params, sorter, filter) => { const tableRequest: TableRequest = (params, sorter, filter) => {
const pageParam = mergePageParam(params, sorter, filter) const pageParam = mergePageParam(params, sorter, filter, salesmanType.value)
return pageAccessLogs({ ...pageParam, ...searchParams }) return pageAccessLogs({ ...pageParam, ...searchParams })
} }
@ -159,7 +188,7 @@ const handleEdit = (record: CustomerListRecord, tabIndex: string) => {
// }) // })
// } // }
const columns: ProColumns[] = [ const columns = ref<any[]>([
{ {
title: '序号', title: '序号',
dataIndex: 'index', dataIndex: 'index',
@ -245,7 +274,14 @@ const columns: ProColumns[] = [
dataIndex: 'clueTime', dataIndex: 'clueTime',
width: 150, width: 150,
align: 'center', align: 'center',
ellipsis: true ellipsis: true,
customRender: function ({ record }: any) {
if (record.clueTime) {
return record.clueTime
} else if (record.clueTime === '') {
return '--'
}
}
}, },
{ {
title: '创建日期', title: '创建日期',
@ -261,20 +297,35 @@ const columns: ProColumns[] = [
align: 'center', align: 'center',
fixed: 'right' fixed: 'right'
} }
// { ])
// title: '',
// dataIndex: 'username'
// },
// {
// title: '',
// dataIndex: 'method'
// },
]
// //
// const call=(record,number)=>{ // const call=(record,number)=>{
// window.location.href = 'tel://' + number // window.location.href = 'tel://' + number
// } // }
//
const switchChange = (record: any) => {
if (record.isEffective === 0 || record.isEffective === null) {
record.chargingStatus = 0
doRequest(
isEffective({
id: record.clueId,
effective: 1
}),
{
onSuccess: (res: any) => {
reloadTable()
}
}
)
}
}
//
function downloadTemplate() {
exportClueData(searchParams).then(res => {
remoteFileDownload(res)
})
}
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.customerList-title { .customerList-title {
@ -284,6 +335,10 @@ const columns: ProColumns[] = [
} }
// //
//
.download {
padding-bottom: 20px;
}
// .operationButtonArea { // .operationButtonArea {
// padding-bottom: 20px; // padding-bottom: 20px;
// } // }

@ -29,6 +29,7 @@ declare module '@vue/runtime-core' {
AEmpty: typeof import('ant-design-vue/es')['Empty'] AEmpty: typeof import('ant-design-vue/es')['Empty']
AForm: typeof import('ant-design-vue/es')['Form'] AForm: typeof import('ant-design-vue/es')['Form']
AFormItem: typeof import('ant-design-vue/es')['FormItem'] AFormItem: typeof import('ant-design-vue/es')['FormItem']
AFormItemRest: typeof import('ant-design-vue/es')['FormItemRest']
AInput: typeof import('ant-design-vue/es')['Input'] AInput: typeof import('ant-design-vue/es')['Input']
AInputGroup: typeof import('ant-design-vue/es')['InputGroup'] AInputGroup: typeof import('ant-design-vue/es')['InputGroup']
AInputNumber: typeof import('ant-design-vue/es')['InputNumber'] AInputNumber: typeof import('ant-design-vue/es')['InputNumber']

Loading…
Cancel
Save