更新类名

master
doublekou 10 months ago
parent beceb5b1d5
commit f5d8ab546c

@ -33,3 +33,13 @@ export function updateResource(resourcesDto: resourcesDtoDTO) {
// export function deleteUser(userId: number) { // export function deleteUser(userId: number) {
// return httpClient.delete<ApiResult<void>>(`/system/user/${userId}`) // 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 startTime?: string
// 结束时间 // 结束时间
endTime?: string endTime?: string
clueId?: string
} }
/** /**
* 线record * 线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,455 @@
<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 class="center">
<!-- <a-list item-layout="horizontal" :data-source="data">
<template #renderItem="{ item }">
<a-list-item>
<a-list-item-meta description="有新线索,请注意跟单">
<template #avatar> id123455666</template>
</a-list-item-meta>
<template #actions>
<a key="list-loadmore-edit" style="margin-right: 8px">确认</a>
<a key="list-loadmore-more">定位</a>
</template>
</a-list-item>
</template>
</a-list> -->
<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 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.status === 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 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 { defineComponent, 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 { doRequest } from '@/utils/axios/request'
import { pageAccessLogs } from '@/api/customermanagement/customerList'
import router from '@/router'
//
// const show = ref(false)
//
const toggleShowAll = (itemShow: DataItem) => {
itemShow.show = !itemShow.show
}
//
//
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: 'clueLabelList',
// width: 200,
// ellipsis: true,
// align: 'center',
// onCell: () => {
// return {
// style: {
// maxWidth: 200,
// overflow: 'hidden',
// whiteSpace: 'nowrap',
// textOverflow: 'ellipsis',
// cursor: 'pointer'
// }
// }
// }
// },
{
title: '描述',
dataIndex: 'content',
ellipsis: true,
align: 'center'
},
// {
// title: '线',
// dataIndex: 'clueTime',
// width: 150,
// align: 'center',
// ellipsis: true
// },
{
title: '创建日期',
dataIndex: 'createTime',
width: 150,
align: 'center',
ellipsis: true
}
// {
// key: 'operate',
// title: '',
// width: 100,
// align: 'center',
// fixed: 'right'
// }
// {
// title: '',
// dataIndex: 'username'
// },
// {
// title: '',
// dataIndex: 'method'
// },
]
defineOptions({ name: 'MessagePage' })
interface DataItem {
id: number | undefined
type: number
createTime: string
content: string
messageKey: string
status: 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 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
}
</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;
}
// ::v-deep .ant-list-item-meta-description {
// text-align: right;
// }
// .positioning {
// padding: 0 0 0 8px !important;
// ::v-deep .ant-list-item-action > li {
// padding: 0 0 0 8px;
// }
// }
.listItem {
// display: flex;
// justify-content: space-between;
border-bottom: 1px solid #f0f0f0;
padding: 12px 0;
}
.listItemTop {
display: flex;
justify-content: space-between;
align-items: center;
// width: 300px;
.type {
font-weight: 700;
width: 130px;
overflow: hidden;
text-overflow: ellipsis;
white-space: normal; /* 允许溢出内容进行换行 */
}
.time {
color: #c3c3c3;
font-size: 12px;
}
}
.listItemCenter {
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 {
// margin-top: 5px;
display: flex;
justify-content: flex-end;
}
// .listItemLeft {
// display: flex;
// justify-content: space-between;
// width: 210px;
// .listItemLeftId {
// // width: 50%;
// // margin-right: 10px;
// }
// .listItemLeftMessage {
// // width: 50%;
// }
// }
// .listItemRight {
// width: 150px;
// }
</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,68 @@
// import { useWebSocket, type UseWebSocketReturn } from '@vueuse/core' import { useWebSocket, type UseWebSocketReturn } from '@vueuse/core'
// import { useUserStore } from '@/stores/user-store' import { useUserStore } from '@/stores/user-store'
// import { emitter } from '@/hooks/mitt' import { emitter } from '@/hooks/mitt'
// let useWebSocketReturn: UseWebSocketReturn<any> | undefined = undefined let useWebSocketReturn: UseWebSocketReturn<any> | undefined = undefined
// const useAdminWebSocket = () => { //它是一个自定义的Vue hook用于管理WebSocket连接
// if (useWebSocketReturn && useWebSocketReturn.status.value != 'CLOSED') { const useAdminWebSocket = () => {
// return useWebSocketReturn //检查是否已经存在WebSocket连接以及连接状态。如果WebSocket连接存在且状态不是已关闭则直接返回现有的WebSocket连接。
// } if (useWebSocketReturn && useWebSocketReturn.status.value != 'CLOSED') {
return useWebSocketReturn
// const { accessToken } = useUserStore() }
// // ws地址 const { accessToken } = useUserStore()
// const baseUri = import.meta.env.VITE_API_URL console.log(accessToken, 'accessToken')
// const host = window.location.host
// const wsUri = `ws://${host}${baseUri}/ws?access_token=${accessToken}` // ws地址
const baseUri = import.meta.env.VITE_API_URL
// useWebSocketReturn = useWebSocket(wsUri, { const host = window.location.host
// autoReconnect: { console.log(host, 'host')
// retries: 3, const wsUri = `ws://${host}${baseUri}/ws?access_token=${accessToken}`
// delay: 1000, console.log(wsUri, 'wsUri')
// onFailed() {
// console.error('Failed to connect WebSocket after 3 retries') //使用useWebSocket函数创建WebSocket连接并配置自动重连和心跳机制
// } useWebSocketReturn = useWebSocket(wsUri, {
// }, autoReconnect: {
// heartbeat: { retries: 3,
// message: '{"type": "ping"}', delay: 1000,
// interval: 30000 onFailed() {
// } console.error('Failed to connect WebSocket after 3 retries')
// }) }
},
// watch( heartbeat: {
// () => useWebSocketReturn!.data.value, message: '{"type": "ping"}',
// value => { interval: 30000
// let event }
// let dataMsg })
// try { //监听WebSocket的数据变化根据接收到的消息类型进行相应的处理并通过emitter发布事件或数据。
// dataMsg = JSON.parse(value) watch(
// event = dataMsg.type () => useWebSocketReturn!.data.value,
// // 心跳响应跳过发布 value => {
// if (event === 'pong') { let event
// return let dataMsg
// }
// } catch (e) { try {
// // 纯文本消息 dataMsg = JSON.parse(value)
// event = 'plaintext' event = dataMsg.type
// dataMsg = value // 心跳响应跳过发布
// } if (event === 'pong') {
// emitter.emit(event, dataMsg) return
// } }
// ) } catch (e) {
// 纯文本消息
// return useWebSocketReturn event = 'plaintext'
// } dataMsg = value
}
// export default useAdminWebSocket emitter.emit(event, dataMsg)
console.log(dataMsg, 'dataMsg')
// Show alert with the received message
alert(JSON.stringify(dataMsg))
}
)
return useWebSocketReturn
}
//将创建的WebSocket连接对象返回给调用者
export default useAdminWebSocket

@ -2,8 +2,32 @@
<span v-if="loading" :class="[$style.action, $style.account]"> <span v-if="loading" :class="[$style.action, $style.account]">
<a-spin size="small" style="margin-left: 8px; margin-right: 8px" /> <a-spin size="small" style="margin-left: 8px; margin-right: 8px" />
</span> </span>
<div v-else style="display: flex">
<div
v-if="userInfoCurrent === 'ROLE_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>
</a-menu>
</template>
</a-dropdown>
</div>
<header-dropdown v-else overlay-class-name="avatar-dropdown"> <header-dropdown overlay-class-name="avatar-dropdown">
<span :class="[$style.action, $style.account]"> <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="currentUser.avatar" alt="avatar" /> -->
<a-avatar <a-avatar
@ -42,6 +66,7 @@
</a-menu> </a-menu>
</template> </template>
</header-dropdown> </header-dropdown>
</div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@ -49,11 +74,48 @@ import HeaderDropdown from '@/layouts/components/HeaderDropdown'
import type { MenuInfo } from 'ant-design-vue/es/menu/src/interface' import type { MenuInfo } from 'ant-design-vue/es/menu/src/interface'
import { Modal } from 'ant-design-vue' import { Modal } from 'ant-design-vue'
import { logout } from '@/api/auth' import { logout } from '@/api/auth'
import { useUserStore } from '@/stores/user-store' // import { useUserStore } from '@/stores/user-store'
import { loginPath } from '@/config' import { loginPath } from '@/config'
// import { fileAbsoluteUrl } from '@/utils/file-utils' // import { fileAbsoluteUrl } from '@/utils/file-utils'
import { useMultiTabStore } from '@/stores/multitab-store' import { useMultiTabStore } from '@/stores/multitab-store'
import Avatar from '@/assets/images/avatar.png' 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 { useUserStore } from '@/stores/user-store'
const { userInfo } = useUserStore()
const userInfoCurrent = ref<string | undefined>('')
onMounted(() => {
console.log(userInfo, 'userInfo?.permissions')
// userInfoCurrent = userInfo?.roleCodes
userInfoCurrent.value = userInfo?.roleCodes?.toLocaleString()
})
const visible = ref(false)
const container = ref(null)
// 线
interface DataItem {
id: number | undefined
type: number
createTime: string
content: string
messageKey: string
status: number | undefined
title: string
show: boolean
}
const newclueList = ref<DataItem[]>([
// {
// id: undefined,
// type: '',
// createTime: '',
// content: ''
// }
])
const multiTabStore = useMultiTabStore() const multiTabStore = useMultiTabStore()
@ -113,6 +175,52 @@ const onMenuClick = (event: MenuInfo) => {
loginOut() loginOut()
} }
} }
//
const getMessageList = () => {
doRequest(
messageAccessLogs({
size: 10,
page: 1
}),
{
onSuccess: (res: any) => {
newclueList.value = res.data.records
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>
<script lang="ts"> <script lang="ts">

@ -13,6 +13,7 @@ import './styles/index.less'
import App from './App.vue' import App from './App.vue'
import { enableI18n } from '@/config' import { enableI18n } from '@/config'
import { provincesAndCitiesAndAutonomousRegions } from '@/utils/geopolitical-customers' import { provincesAndCitiesAndAutonomousRegions } from '@/utils/geopolitical-customers'
import '@/assets/iconfont.css'
// 声明一个全局变量,将数据赋值给它 // 声明一个全局变量,将数据赋值给它
declare global { 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" /> <iframe :id="id" ref="iframeRef" :src="url" class="iframe" @load="hideLoading" />
</a-spin> --> </a-spin> -->
<!-- 工具栏 -->
<customer-list-search :loading="tableRef?.loading" @search="searchTable" />
<div class="homeContent"> <div class="homeContent">
<div class="bullshit">努力开发中敬请期待...</div> <div class="bullshit">努力开发中敬请期待...</div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import CustomerListSearch from '@/views/basic/iframe/iframeSearch.vue'
// //
defineOptions({ name: 'IframeView' }) defineOptions({ name: 'IframeView' })

@ -52,6 +52,12 @@
</a-tooltip> </a-tooltip>
<span v-else>--</span> <span v-else>--</span>
</template> </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> </template>
</pro-table> </pro-table>
<!-- 详情弹窗 --> <!-- 详情弹窗 -->
@ -61,7 +67,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { ref, onMounted } from 'vue'
// //
// import { UserOutlined, DownOutlined } from '@ant-design/icons-vue' // import { UserOutlined, DownOutlined } from '@ant-design/icons-vue'
// import type { MenuProps } from 'ant-design-vue' // import type { MenuProps } from 'ant-design-vue'
@ -80,9 +86,19 @@ 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 { useRoute } from 'vue-router'
// //
// import { doRequest } from '@/utils/axios/request' // import { doRequest } from '@/utils/axios/request'
const route = useRoute()
onMounted(() => {
const customerId = route.query.clueId as string // 使
if (customerId) {
searchTable({ clueId: customerId })
}
})
defineOptions({ name: 'CustomerList' }) defineOptions({ name: 'CustomerList' })
// //
@ -141,9 +157,32 @@ const handleEdit = (record: CustomerListRecord, tabIndex: string) => {
const columns: ProColumns[] = [ const columns: ProColumns[] = [
{ {
title: '序号', title: '序号',
dataIndex: 'index',
width: '80px', width: '80px',
customRender: function ({ index }) { customRender: function ({ index, record }: { index: number; record: any }) {
return `${index + 1}` if (record.clueStageName === '阶段二') {
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 { SliderCaptcha as LoginCaptcha } from '@/components/Captcha'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { getCodeData } from '@/api/auth' import { getCodeData } from '@/api/auth'
import useAdminWebSocket from '@/hooks/websocket'
const { t } = useI18n() const { t } = useI18n()
@ -171,6 +172,24 @@ function handleSubmit(captchaId?: string) {
store(res) store(res)
const nextPath = (router.currentRoute.value.query.redirect as string) || '/' const nextPath = (router.currentRoute.value.query.redirect as string) || '/'
router.push(nextPath) 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 => { .catch(err => {
// console.log(accountLoginFormRef.value, 'accountLoginFormRef') // console.log(accountLoginFormRef.value, 'accountLoginFormRef')

@ -102,6 +102,8 @@ declare module '@vue/runtime-core' {
LovModal: typeof import('./../src/components/Lov/LovModal.vue')['default'] LovModal: typeof import('./../src/components/Lov/LovModal.vue')['default']
LovSearch: typeof import('./../src/components/Lov/LovSearch.vue')['default'] LovSearch: typeof import('./../src/components/Lov/LovSearch.vue')['default']
MailOutlined: typeof import('@ant-design/icons-vue')['MailOutlined'] 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'] MinusCircleOutlined: typeof import('@ant-design/icons-vue')['MinusCircleOutlined']
MobileOutlined: typeof import('@ant-design/icons-vue')['MobileOutlined'] MobileOutlined: typeof import('@ant-design/icons-vue')['MobileOutlined']
OperationGroup: typeof import('./../src/components/Operation/OperationGroup.vue')['default'] OperationGroup: typeof import('./../src/components/Operation/OperationGroup.vue')['default']

@ -28,12 +28,12 @@ import { antdvStyleDeps } from './src/utils/resolvers'
//ts //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' export const serverAddress = 'http://39.100.77.21:8000'
//线上ip //线上ip
// export const serverAddress = 'http://175.27.212.74:80' // 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' // export const serverAddress = 'http://8.130.96.163:8100'
// https://vitejs.dev/config/ // https://vitejs.dev/config/
// export const serverAddress = process.env.VUE_APP_BASE_API // export const serverAddress = process.env.VUE_APP_BASE_API

Loading…
Cancel
Save