飘泊客 10 months ago
commit 2b768b368c

@ -33,3 +33,13 @@ export function updateResource(resourcesDto: resourcesDtoDTO) {
// export function deleteUser(userId: number) {
// return httpClient.delete<ApiResult<void>>(`/system/user/${userId}`)
// }
/**
*
* @param pageParams
*/
export function messageAccessLogs(pageParams: SysUserPageParam) {
return httpClient.get<ApiResult>('/notify/user-announcement/page', {
params: pageParams
})
}

@ -18,6 +18,7 @@ export type CustomerListQO = {
startTime?: string
// 结束时间
endTime?: string
clueId?: string
}
/**
* 线record

@ -0,0 +1,38 @@
@font-face {
font-family: 'iconfont'; /* Project id 4340011 */
src: url('//at.alicdn.com/t/c/font_4340011_rzfq6m1kbn.woff2?t=1700639506016') format('woff2'),
url('//at.alicdn.com/t/c/font_4340011_rzfq6m1kbn.woff?t=1700639506016') format('woff'),
url('//at.alicdn.com/t/c/font_4340011_rzfq6m1kbn.ttf?t=1700639506016') format('truetype');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-xin2:before {
content: '\e647';
}
.icon-xin1:before {
content: '\e600';
}
.icon-xin:before {
content: '\e6c9';
}
.icon-xiaoxi1:before {
content: '\e611';
}
.icon-xiaoxizhongxin:before {
content: '\e7d9';
}
.icon-xiaoxi:before {
content: '\e6c5';
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

@ -0,0 +1,376 @@
<template>
<div class="ms-container">
<div class="top-column">
<div>消息列表</div>
<div class="seeMore" @click="seeMore"></div>
</div>
<div class="item-conent frame-style">
<div v-if="newclueList.length > 0" class="center">
<div v-for="(item, index) in newclueList" :key="index" class="listItem">
<div class="listItemTop">
<div class="type">
{{ item.type === 1 ? '系统公告' : '导出通知' }}{{ item.title }}
</div>
<div class="time">{{ item.createTime }}</div>
</div>
<div class="listItemCenter">
<span ref="contentRef" class="content" :class="{ expanded: item.show }">{{
item.content
}}</span>
<div class="iconContainer" @click="toggleShowAll(item)">
<div v-if="item.show" class="openOrClose">
<span><down-outlined /></span>
</div>
<div v-else class="openOrClose">
<up-outlined />
</div>
</div>
</div>
<div class="listItemBottom">
<a key="list-loadmore-edit" style="margin-right: 8px">{{
item.state === 1 ? '已读' : '确认'
}}</a>
<a
key="list-loadmore-more"
style="border-left: 1px solid rgba(0, 0, 0, 0.06); padding-left: 8px"
@click="positioning(item.messageKey)"
>定位</a
>
</div>
</div>
</div>
<div v-else class="empty">
<a-empty description="暂无数据" :image="zanwu" />
</div>
</div>
<!-- 新消息列表弹出框 -->
<div>
<a-modal
v-model:visible="listVisible"
title="新线索列表"
:mask-closable="false"
:width="900"
@ok="handleOk"
>
<!-- 工具栏 -->
<message-search :loading="tableRef?.loading" @search="searchTable" />
<!-- 底部表格 -->
<pro-table
ref="tableRef"
row-key="userId"
:request="tableRequest"
:columns="columns"
:scroll="{ x: 800 }"
:tool-bar-render="false"
class="protable"
:pagination="{ showQuickJumper: true }"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'clueLabelList'">
<a-tooltip v-if="record.clueLabelList.length > 0">
<template #title
><a-tag
v-for="(item, index) in record.clueLabelList"
:key="index"
class="tagGroupContentItem"
>{{ item }}</a-tag
></template
>
<a-tag v-for="(item, index) in record.clueLabelList" :key="index">{{ item }}</a-tag>
</a-tooltip>
<span v-else>--</span>
</template>
<template v-else-if="column.key === 'remark'">
<a-tooltip v-if="record.remark">
<template #title
><span>{{ record.remark }}</span></template
>
<span>{{ record.remark }}</span>
</a-tooltip>
<span v-else>--</span>
</template>
</template>
</pro-table>
</a-modal>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, watch, onMounted } from 'vue'
import zanwu from '@/assets/images/zanwu.png'
//
import ProTable from '#/table'
import type { ProTableInstanceExpose, TableRequest, ProColumns } from '#/table'
import { mergePageParam } from '@/utils/page-utils'
import { messageAccessLogs } from '@/api/customermanagement/customerList'
import type { CustomerListQO } from '@/api/customermanagement/customerList/types'
import MessageSearch from '@/components/Message/messageSearch.vue'
import type { DistributeTaskManagementPageParam } from '@/api/controlcenterstation/distributionTaskManagement/types'
import router from '@/router'
defineOptions({ name: 'MessagePage' })
interface DataItem {
id: number | undefined
type: number
createTime: string
content: string
messageKey: string
state: number | undefined
title: string
show: boolean
}
const props = defineProps({
newclueList: {
type: Array as () => DataItem[],
required: true,
default: () => []
}
// props...
})
// // props.listVisible visible
// watch(
// () => props.listVisible,
// newValue => {
// listVisible.value = newValue
// console.log(newValue, 'newValue')
// }
// )
const emits = defineEmits<{
(e: 'submit-success'): void
(e: 'click-success', isSuccessful: boolean): void
}>()
//
//
const tableRef = ref<ProTableInstanceExpose>()
//
let searchParams: CustomerListQO = {}
//
const tableRequest: TableRequest = (params, sorter, filter) => {
const pageParam = mergePageParam(params, sorter, filter)
return messageAccessLogs({ ...pageParam, ...searchParams })
}
//
const reloadTable = (resetPageIndex?: boolean) => {
tableRef.value?.actionRef?.reload(resetPageIndex)
}
//
const searchTable = (params: DistributeTaskManagementPageParam) => {
searchParams = params
reloadTable(true) // tableRequest
}
const columns: ProColumns[] = [
{
title: '序号',
width: '80px',
customRender: function ({ index }) {
return `${index + 1}`
}
},
{
title: '标题',
dataIndex: 'title',
width: '120px',
customRender: function ({ record }: any) {
if (record.nid) {
return record.nid
} else if (record.nid === '') {
return '--'
}
}
},
{
title: '类型',
dataIndex: 'type',
width: '100px',
ellipsis: true,
align: 'center',
customRender: function ({ record }: any) {
if (record.type === 1) {
return '系统公告'
} else if (record.type === 2) {
return '导出通知'
}
}
},
{
title: '状态',
dataIndex: 'state',
width: '100px',
ellipsis: true,
align: 'center',
customRender: function ({ record }: any) {
if (record.state === 0) {
return '未读'
} else if (record.state === 1) {
return '已读'
}
}
},
{
title: '描述',
dataIndex: 'content',
ellipsis: true,
align: 'center'
},
{
title: '创建日期',
dataIndex: 'createTime',
width: 150,
align: 'center',
ellipsis: true
}
]
const listVisible = ref<boolean>(false)
const seeMore = () => {
emits('submit-success')
setTimeout(() => {
listVisible.value = true
}, 500) // 0.5 listVisible.value
}
//
const handleOk = (e: MouseEvent) => {
console.log(e)
listVisible.value = false
}
//
const positioning = (messageKey: string) => {
emits('click-success', false)
router.push({ path: '/task/customerlist', query: { clueId: messageKey } }) // /task/customerlist
}
//
const toggleShowAll = (itemShow: DataItem) => {
itemShow.show = !itemShow.show
}
const contentRef = ref<HTMLElement | null>(null)
</script>
<style lang="less" scoped>
.ms-container {
width: 320px;
}
.top-column {
display: flex;
justify-content: space-between;
margin-bottom: 6px;
padding: 10px;
& div:nth-child(1) {
font-size: 14px;
font-weight: 600;
}
& div:nth-child(2) {
font-size: 14px;
color: #1890ff;
cursor: pointer;
}
}
.item-conent {
overflow: auto;
overflow-y: overlay; /* 让滚动条覆盖内容 */
overflow-y: auto;
padding: 0 10px 10px; /* 为滚动条留出空间 */
.item-msg {
border: 1px solid #303133;
border-radius: 4px;
padding: 5px;
margin-bottom: 8px;
}
.iconimg {
width: 13px;
height: auto;
margin-right: 5px;
}
.topImg {
width: 17px;
height: auto;
}
.conent-top {
height: 450.96px;
padding: 0 10px;
}
.conent-bot {
width: 100%;
text-align: center;
height: 25px;
line-height: 25px;
font-size: 14px;
color: #1890ff;
border-top: 1px solid #e0e0e0;
}
.empty {
margin-top: 30px;
}
}
.frame-style {
height: 480px;
}
.listItem {
border-bottom: 1px solid #f0f0f0;
padding: 12px 0;
}
.listItemTop {
display: flex;
justify-content: space-between;
align-items: center;
.type {
font-weight: 700;
width: 130px;
overflow: hidden;
text-overflow: ellipsis;
white-space: normal; /* 允许溢出内容进行换行 */
}
.time {
color: #c3c3c3;
font-size: 12px;
}
}
.listItemCenter {
display: flex;
align-items: end;
color: #767575;
width: 100%;
height: auto;
word-wrap: break-word;
word-break: break-all;
overflow: hidden;
white-space: normal !important;
margin: 5px 0;
font-size: 13px;
}
.iconContainer {
display: inline-block; /* 让容器内的内容水平排列 */
vertical-align: middle; /* 垂直居中对齐 */
}
.content {
white-space: normal; /* 不换行 */
overflow: hidden; /* 超出部分隐藏 */
text-overflow: clip; /* 超出部分裁剪隐藏 */
}
.content.expanded {
white-space: normal; /* 不换行 */
overflow: hidden; /* 超出部分隐藏 */
text-overflow: clip; /* 超出部分裁剪隐藏 */
display: -webkit-box; /* 将元素作为弹性伸缩盒子模型显示 */
-webkit-box-orient: vertical; /* 设置伸缩盒子的子元素垂直排列 */
-webkit-line-clamp: 2; /* 设置显示的行数 */
}
.openOrClose {
display: inline;
margin-left: 4px;
float: right;
}
.listItemBottom {
display: flex;
justify-content: flex-end;
}
</style>

@ -0,0 +1,72 @@
<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="12" :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="3" :md="3" :sm="12">
<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 { DistributeTaskManagementSearch } from '@/api/controlcenterstation/distributionTaskManagement/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<DistributeTaskManagementSearch>({})
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>

@ -1,57 +1,72 @@
// import { useWebSocket, type UseWebSocketReturn } from '@vueuse/core'
// import { useUserStore } from '@/stores/user-store'
// import { emitter } from '@/hooks/mitt'
// let useWebSocketReturn: UseWebSocketReturn<any> | undefined = undefined
// const useAdminWebSocket = () => {
// if (useWebSocketReturn && useWebSocketReturn.status.value != 'CLOSED') {
// return useWebSocketReturn
// }
// const { accessToken } = useUserStore()
// // ws地址
// const baseUri = import.meta.env.VITE_API_URL
// const host = window.location.host
// const wsUri = `ws://${host}${baseUri}/ws?access_token=${accessToken}`
// useWebSocketReturn = useWebSocket(wsUri, {
// autoReconnect: {
// retries: 3,
// delay: 1000,
// onFailed() {
// console.error('Failed to connect WebSocket after 3 retries')
// }
// },
// heartbeat: {
// message: '{"type": "ping"}',
// interval: 30000
// }
// })
// watch(
// () => useWebSocketReturn!.data.value,
// value => {
// let event
// let dataMsg
// try {
// dataMsg = JSON.parse(value)
// event = dataMsg.type
// // 心跳响应跳过发布
// if (event === 'pong') {
// return
// }
// } catch (e) {
// // 纯文本消息
// event = 'plaintext'
// dataMsg = value
// }
// emitter.emit(event, dataMsg)
// }
// )
// return useWebSocketReturn
// }
// export default useAdminWebSocket
import { useWebSocket, type UseWebSocketReturn } from '@vueuse/core'
import { useUserStore } from '@/stores/user-store'
import { emitter } from '@/hooks/mitt'
import { message } from 'ant-design-vue'
let useWebSocketReturn: UseWebSocketReturn<any> | undefined = undefined
//它是一个自定义的Vue hook用于管理WebSocket连接
const useAdminWebSocket = () => {
//检查是否已经存在WebSocket连接以及连接状态。如果WebSocket连接存在且状态不是已关闭则直接返回现有的WebSocket连接。
if (useWebSocketReturn && useWebSocketReturn.status.value != 'CLOSED') {
return useWebSocketReturn
}
const { accessToken } = useUserStore()
console.log(accessToken, 'accessToken')
// ws地址
const baseUri = import.meta.env.VITE_API_URL
const host = window.location.host
console.log(host, 'host')
const wsUri = `ws://${host}${baseUri}/ws?access_token=${accessToken}`
console.log(wsUri, 'wsUri')
//使用useWebSocket函数创建WebSocket连接并配置自动重连和心跳机制
useWebSocketReturn = useWebSocket(wsUri, {
autoReconnect: {
retries: 3,
delay: 1000,
onFailed() {
console.error('Failed to connect WebSocket after 3 retries')
}
},
heartbeat: {
message: '{"type": "ping"}',
interval: 30000
}
})
//监听WebSocket的数据变化根据接收到的消息类型进行相应的处理并通过emitter发布事件或数据。
watch(
() => useWebSocketReturn!.data.value,
value => {
let event
let dataMsg
try {
dataMsg = JSON.parse(value)
event = dataMsg.type
// 心跳响应跳过发布
if (event === 'pong') {
return
}
} catch (e) {
// 纯文本消息
event = 'plaintext'
dataMsg = value
}
// emitter.emit(event, dataMsg)
// console.log(dataMsg, 'dataMsg')
// Show alert with the received message
// alert(JSON.stringify(dataMsg.title) + JSON.stringify(dataMsg.content))
message.info(
JSON.stringify(dataMsg.title).slice(1, -1) +''+ JSON.stringify(dataMsg.content).slice(1, -1)
)
}
)
return useWebSocketReturn
}
//将创建的WebSocket连接对象返回给调用者
export default useAdminWebSocket

@ -2,46 +2,71 @@
<span v-if="loading" :class="[$style.action, $style.account]">
<a-spin size="small" style="margin-left: 8px; margin-right: 8px" />
</span>
<header-dropdown v-else overlay-class-name="avatar-dropdown">
<span :class="[$style.action, $style.account]">
<!-- <a-avatar size="small" :class="$style.avatar" :src="currentUser.avatar" alt="avatar" /> -->
<a-avatar
size="small"
:class="$style.avatar"
:src="
userStore.userInfo?.avatar
? envs.VITE_SERVERURL + '/avatar/' + userStore.userInfo?.avatar
: Avatar
"
alt="avatar"
/>
<span :class="[$style.name, 'anticon']">{{ currentUser.nickname }}</span>
</span>
<template #overlay>
<a-menu :class="$style.menu" @click="onMenuClick">
<template v-if="props.menu">
<router-link to="/system/usercenter">
<a-menu-item key="center">
<UserOutlined />
个人中心
<div v-else style="display: flex">
<div
v-if="userInfoCurrent === 'ROLE_SALESMAN' || userInfoCurrent === 'ROLE_STORE_SALESMAN'"
ref="container"
@mousedown="handleDocumentClick"
>
<a-dropdown :visible="visible" @click="dropdownClick">
<a class="ant-dropdown-link" @click.prevent>
<i class="iconfont icon-xiaoxi1" style="font-size: 20px" />
</a>
<template #overlay>
<a-menu>
<a-menu-item>
<Message
:newclue-list="newclueList"
@mousedown.stop
@submit-success="reloadTable"
@click-success="reloadTable"
/>
</a-menu-item>
</router-link>
<!-- <a-menu-item key="settings">
</a-menu>
</template>
</a-dropdown>
</div>
<header-dropdown overlay-class-name="avatar-dropdown">
<span :class="[$style.action, $style.account]">
<!-- <a-avatar size="small" :class="$style.avatar" :src="currentUser.avatar" alt="avatar" /> -->
<a-avatar
size="small"
:class="$style.avatar"
:src="
userStore.userInfo?.avatar
? envs.VITE_SERVERURL + '/avatar/' + userStore.userInfo?.avatar
: Avatar
"
alt="avatar"
/>
<span :class="[$style.name, 'anticon']">{{ currentUser.nickname }}</span>
</span>
<template #overlay>
<a-menu :class="$style.menu" @click="onMenuClick">
<template v-if="props.menu">
<router-link to="/system/usercenter">
<a-menu-item key="center">
<UserOutlined />
个人中心
</a-menu-item>
</router-link>
<!-- <a-menu-item key="settings">
<SettingOutlined />
个人设置
</a-menu-item>
<a-menu-divider style="margin: 4px 0" /> -->
</template>
</template>
<a-menu-item key="logout">
<LogoutOutlined />
退出登录
</a-menu-item>
</a-menu>
</template>
</header-dropdown>
<a-menu-item key="logout">
<LogoutOutlined />
退出登录
</a-menu-item>
</a-menu>
</template>
</header-dropdown>
</div>
</template>
<script setup lang="ts">
@ -49,11 +74,53 @@ import HeaderDropdown from '@/layouts/components/HeaderDropdown'
import type { MenuInfo } from 'ant-design-vue/es/menu/src/interface'
import { Modal } from 'ant-design-vue'
import { logout } from '@/api/auth'
import { useUserStore } from '@/stores/user-store'
// import { useUserStore } from '@/stores/user-store'
import { loginPath } from '@/config'
// import { fileAbsoluteUrl } from '@/utils/file-utils'
import { useMultiTabStore } from '@/stores/multitab-store'
import Avatar from '@/assets/images/avatar.png'
import Message from '@/components/Message/index.vue'
import { ref, watch, onMounted, onUnmounted } from 'vue'
import { doRequest } from '@/utils/axios/request'
import { messageAccessLogs } from '@/api/customermanagement/customerList'
import useAdminWebSocket from '@/hooks/websocket'
//
import { useUserStore } from '@/stores/user-store'
const { userInfo } = useUserStore()
const userInfoCurrent = ref<string | undefined>('')
onMounted(() => {
userInfoCurrent.value = userInfo?.roleCodes?.toLocaleString()
if (
userInfoCurrent.value === 'ROLE_SALESMAN' ||
userInfoCurrent.value === 'ROLE_STORE_SALESMAN'
) {
useAdminWebSocket()
}
})
const visible = ref(false)
const container = ref(null)
// 线
interface DataItem {
id: number | undefined
type: number
createTime: string
content: string
messageKey: string
state: number | undefined
title: string
show: boolean
}
const newclueList = ref<DataItem[]>([
// {
// id: undefined,
// type: '',
// createTime: '',
// content: ''
// }
])
const multiTabStore = useMultiTabStore()
@ -113,6 +180,53 @@ const onMenuClick = (event: MenuInfo) => {
loginOut()
}
}
//
const getMessageList = () => {
doRequest(
messageAccessLogs({
size: 10,
page: 1
}),
{
onSuccess: (res: any) => {
newclueList.value = res.data.records
console.log(newclueList.value, 'newclueList.value')
newclueList.value.forEach(item => {
item.show = true
})
}
}
)
}
const dropdownClick = async () => {
if (!visible.value) {
await getMessageList()
}
visible.value = !visible.value
}
const handleDocumentClick = (event: MouseEvent) => {
const messageElement = document.querySelector('.message')
if (
visible.value &&
container.value &&
!(container.value as HTMLElement).contains(event.target as Node)
) {
if (!messageElement || !messageElement.contains(event.target as Node)) {
visible.value = false
}
}
}
document.addEventListener('mousedown', handleDocumentClick)
onUnmounted(() => {
document.removeEventListener('mousedown', handleDocumentClick)
})
//
const reloadTable = () => {
visible.value = false
}
</script>
<script lang="ts">

@ -13,6 +13,7 @@ import './styles/index.less'
import App from './App.vue'
import { enableI18n } from '@/config'
import { provincesAndCitiesAndAutonomousRegions } from '@/utils/geopolitical-customers'
import '@/assets/iconfont.css'
// 声明一个全局变量,将数据赋值给它
declare global {

@ -0,0 +1,69 @@
<template>
<div>
<dict-select
v-model:value="formModel.httpStatus"
style="display: none"
dict-code="user_status"
allow-clear
placeholder="请选择"
/>
</div>
</template>
<script setup lang="ts">
import { Form } from 'ant-design-vue'
import dayjs from 'dayjs'
import type { Dayjs } from 'dayjs'
import type { CustomerListSearch } from '@/api/customermanagement/customerList/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<CustomerListSearch>({
clueLabelName: '',
//线
userId: undefined,
//
httpStatus: undefined,
//
ip: '',
//
nid: ''
})
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>

@ -8,12 +8,15 @@
<iframe :id="id" ref="iframeRef" :src="url" class="iframe" @load="hideLoading" />
</a-spin> -->
<!-- 工具栏 -->
<customer-list-search :loading="tableRef?.loading" @search="searchTable" />
<div class="homeContent">
<div class="bullshit">努力开发中敬请期待...</div>
</div>
</template>
<script setup lang="ts">
import CustomerListSearch from '@/views/basic/iframe/iframeSearch.vue'
//
defineOptions({ name: 'IframeView' })

@ -52,6 +52,12 @@
</a-tooltip>
<span v-else>--</span>
</template>
<!-- <template v-else-if="column.key === 'index'">
<span>
<i class="iconfont icon-xin" style="font-size: 18px; margin-right: 8px; color: red"></i>
{{ index + 1 }}
</span>
</template> -->
</template>
</pro-table>
<!-- 详情弹窗 -->
@ -61,7 +67,7 @@
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { ref, onMounted } from 'vue'
//
// import { UserOutlined, DownOutlined } from '@ant-design/icons-vue'
// import type { MenuProps } from 'ant-design-vue'
@ -80,9 +86,21 @@ import { pageAccessLogs } from '@/api/customermanagement/customerList'
import CustomerListSearch from '@/views/customermanagement/customerList/customerListSearch.vue'
import CustomerListViewModal from '@/views/customermanagement/customerList/customerListModal.vue'
import CustomerListEditModal from '@/views/customermanagement/customerList/customerListEditModal.vue'
import { useRoute } from 'vue-router'
// import useAdminWebSocket from '@/hooks/websocket'
//
// import { doRequest } from '@/utils/axios/request'
const route = useRoute()
onMounted(() => {
const customerId = route.query.clueId as string // 使
if (customerId) {
searchTable({ clueId: customerId })
}
// useAdminWebSocket()
})
defineOptions({ name: 'CustomerList' })
//
@ -141,9 +159,32 @@ const handleEdit = (record: CustomerListRecord, tabIndex: string) => {
const columns: ProColumns[] = [
{
title: '序号',
dataIndex: 'index',
width: '80px',
customRender: function ({ index }) {
return `${index + 1}`
customRender: function ({ index, record }: { index: number; record: any }) {
if (record.isNewClue === true) {
return {
children: [
h('span', [
h('i', {
style: 'color: red; margin-right: 4px;',
class: 'iconfont icon-xin'
}),
`${index + 1}`
])
]
}
} else {
return {
children: h(
'span',
{
style: 'margin-left: 20px;'
},
`${index + 1}`
)
}
}
}
},
{

@ -80,6 +80,7 @@ import { projectTitle, projectDesc, enableLoginCaptcha } from '@/config'
import { SliderCaptcha as LoginCaptcha } from '@/components/Captcha'
import { useI18n } from 'vue-i18n'
import { getCodeData } from '@/api/auth'
// import useAdminWebSocket from '@/hooks/websocket'
const { t } = useI18n()
@ -171,6 +172,26 @@ function handleSubmit(captchaId?: string) {
store(res)
const nextPath = (router.currentRoute.value.query.redirect as string) || '/'
router.push(nextPath)
// WebSocket
// const webSocket =
// useAdminWebSocket()
// // WebSocket
// if (webSocket && webSocket.status.value === 'OPEN') {
// //
// const messageData = {
// type: 'chatMessage',
// content: 'Hello, WebSocket!'
// }
// //
// webSocket.send(JSON.stringify(messageData))
// //
// console.log('Sent message:', JSON.stringify(messageData))
// } else {
// console.error('WebSocket connection is not open')
// }
})
.catch(err => {
// console.log(accountLoginFormRef.value, 'accountLoginFormRef')

@ -102,6 +102,8 @@ declare module '@vue/runtime-core' {
LovModal: typeof import('./../src/components/Lov/LovModal.vue')['default']
LovSearch: typeof import('./../src/components/Lov/LovSearch.vue')['default']
MailOutlined: typeof import('@ant-design/icons-vue')['MailOutlined']
Message: typeof import('./../src/components/Message/index.vue')['default']
MessageSearch: typeof import('./../src/components/Message/messageSearch.vue')['default']
MinusCircleOutlined: typeof import('@ant-design/icons-vue')['MinusCircleOutlined']
MobileOutlined: typeof import('@ant-design/icons-vue')['MobileOutlined']
OperationGroup: typeof import('./../src/components/Operation/OperationGroup.vue')['default']

@ -26,14 +26,14 @@ import { antdvStyleDeps } from './src/utils/resolvers'
//jt
// export const serverAddress = 'http://172.18.1.8:8000'
//ts
// export const serverAddress = 'http://172.18.0.228:8000'
export const serverAddress = 'http://172.18.0.228:8000'
//测试
// export const serverAddress = 'http://39.100.77.21:8000'
//线上ip
// export const serverAddress = 'http://175.27.212.74:80'
//线上域名,现在用这个
export const serverAddress = 'https://byffp.top'
// export const serverAddress = 'https://byffp.top'
// export const serverAddress = 'http://8.130.96.163:8100'
// https://vitejs.dev/config/
// export const serverAddress = process.env.VUE_APP_BASE_API
@ -45,7 +45,7 @@ export default defineConfig({
'^/api': {
target: serverAddress,
changeOrigin: true,
// ws: true,
ws: true,
rewrite: path => {
return path.replace(/^\/api/, '')
}

Loading…
Cancel
Save