Compare commits
34 Commits
Author | SHA1 | Date | |
---|---|---|---|
ed47c26c63 | |||
819fd0862a | |||
35366d583c | |||
6f8bd932f1 | |||
58a27586a3 | |||
7ae5762c78 | |||
b64e9830ea | |||
58ba6d3fe2 | |||
f3e0692bff | |||
12c926b103 | |||
569cbad572 | |||
1538f5620f | |||
7224c710d4 | |||
912e23ad44 | |||
5d5dadf0d5 | |||
2fa158d3ff | |||
8d77b9855c | |||
f98b460c14 | |||
ce06bcd7b6 | |||
9690df6930 | |||
377888d6e2 | |||
40c8ca0565 | |||
2ad995d565 | |||
20c946cdee | |||
8ebe23bf49 | |||
ecef636640 | |||
c1a5340a73 | |||
b93d0f1515 | |||
7984b7bc65 | |||
04decd7050 | |||
7298bd3347 | |||
b2a10eb187 | |||
3c62621bca | |||
d7e760c920 |
@ -136,7 +136,7 @@
|
||||
<div class="app-loading">
|
||||
<div class="app-loading-wrap">
|
||||
<div class="app-loading-title">
|
||||
<img src="/logo.gif" class="app-loading-logo" alt="Logo" />
|
||||
<!-- <img src="/logo.gif" class="app-loading-logo" alt="Logo" />-->
|
||||
<div class="app-loading-title">%VITE_APP_TITLE%</div>
|
||||
</div>
|
||||
<div class="app-loading-item">
|
||||
|
@ -12,12 +12,27 @@ export const FollowUpRecordApi = {
|
||||
return await request.post({ url: `/intelligentForm/saveDynamicData`, data })
|
||||
},
|
||||
|
||||
//查询对应表单的采集数据列表
|
||||
collectDataList: async(query) => {
|
||||
return await request.get({
|
||||
url: '/intelligentForm/collectDataList',
|
||||
method: 'get',
|
||||
params: {id:query}
|
||||
})
|
||||
//修改智能表单
|
||||
updateDynamicData: async (data) => {
|
||||
return await request.post({ url: `/intelligentForm/updateDynamicData`, data })
|
||||
},
|
||||
|
||||
//删除智能表单
|
||||
deleteDynamicDataById: async (id) => {
|
||||
return await request.get({ url: `/intelligentForm/deleteDynamicDataById?id=` + id, })
|
||||
},
|
||||
|
||||
// 查询文章管理列表
|
||||
collectDataList: async (id) => {
|
||||
return await request.get({ url: `/intelligentForm/collectDataList?id=` + id, })
|
||||
}
|
||||
|
||||
// //查询对应表单的采集数据列表
|
||||
// collectDataList: async(query) => {
|
||||
// return await request.get({
|
||||
// url: '/intelligentForm/collectDataList',
|
||||
// method: 'get',
|
||||
// params: {id:query}
|
||||
// })
|
||||
// }
|
||||
}
|
||||
|
50
yudao-admin-vue3/src/api/mall/promotion/advertising/index.ts
Normal file
@ -0,0 +1,50 @@
|
||||
import request from '@/config/axios'
|
||||
|
||||
// 开屏广告 VO
|
||||
export interface AdvertisingVO {
|
||||
id: number // id
|
||||
status: number // 广告状态
|
||||
time: number // 广告时间(秒)
|
||||
property: string // 广告属性
|
||||
picData: []
|
||||
stat: boolean,
|
||||
}
|
||||
|
||||
// 开屏广告 API
|
||||
export const AdvertisingApi = {
|
||||
// 查询开屏广告分页
|
||||
getAdvertisingPage: async (params: any) => {
|
||||
return await request.get({ url: `/promotion/advertising/page`, params })
|
||||
},
|
||||
|
||||
// 查询开屏广告详情
|
||||
getAdvertising: async () => {
|
||||
return await request.get({ url: `/promotion/advertising/getAdvertising`})
|
||||
},
|
||||
|
||||
// 新增开屏广告
|
||||
createAdvertising: async (data: AdvertisingVO) => {
|
||||
return await request.post({ url: `/promotion/advertising/create`, data })
|
||||
},
|
||||
|
||||
// 修改开屏广告
|
||||
updateAdvertising: async (data: AdvertisingVO) => {
|
||||
return await request.put({ url: `/promotion/advertising/update`, data })
|
||||
},
|
||||
|
||||
// 删除开屏广告
|
||||
deleteAdvertising: async (id: number) => {
|
||||
return await request.delete({ url: `/promotion/advertising/delete?id=` + id })
|
||||
},
|
||||
|
||||
// 导出开屏广告 Excel
|
||||
exportAdvertising: async (params) => {
|
||||
return await request.download({ url: `/promotion/advertising/export-excel`, params })
|
||||
},
|
||||
|
||||
// 新增修稿开屏广告
|
||||
saveAdvertising: async (data: AdvertisingVO) => {
|
||||
return await request.post({ url: `/promotion/advertising/saveAdvertising`, data })
|
||||
},
|
||||
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
import request from '@/config/axios'
|
||||
|
||||
// 客服配置 VO
|
||||
export interface ConfigurationVO {
|
||||
id: number // id
|
||||
type: number // 客服类型
|
||||
feedback: string // 客服反馈
|
||||
phone: string // 客服电话
|
||||
link: string // 客服链接
|
||||
enterpriseID: string // 企业ID
|
||||
}
|
||||
|
||||
// 客服配置 API
|
||||
export const ConfigurationApi = {
|
||||
// 查询客服配置分页
|
||||
getConfigurationPage: async (params: any) => {
|
||||
return await request.get({ url: `/promotion/ke-fu-configuration/page`, params })
|
||||
},
|
||||
|
||||
// 查询客服配置详情
|
||||
getConfiguration: async (id: number) => {
|
||||
return await request.get({ url: `/promotion/ke-fu-configuration/get?id=` + id })
|
||||
},
|
||||
|
||||
// 新增客服配置
|
||||
createConfiguration: async (data: ConfigurationVO) => {
|
||||
return await request.post({ url: `/promotion/ke-fu-configuration/create`, data })
|
||||
},
|
||||
|
||||
// 修改客服配置
|
||||
updateConfiguration: async (data: ConfigurationVO) => {
|
||||
return await request.put({ url: `/promotion/ke-fu-configuration/update`, data })
|
||||
},
|
||||
|
||||
// 删除客服配置
|
||||
deleteConfiguration: async (id: number) => {
|
||||
return await request.delete({ url: `/promotion/ke-fu-configuration/delete?id=` + id })
|
||||
},
|
||||
|
||||
// 导出客服配置 Excel
|
||||
exportConfiguration: async (params) => {
|
||||
return await request.download({ url: `/promotion/ke-fu-configuration/export-excel`, params })
|
||||
}
|
||||
}
|
@ -56,3 +56,32 @@ export const getDiyTemplateProperty = async (id: number) => {
|
||||
export const updateDiyTemplateProperty = async (data: DiyTemplateVO) => {
|
||||
return await request.put({ url: `/promotion/diy-template/update-property`, data })
|
||||
}
|
||||
|
||||
|
||||
// 设置商品分类接口
|
||||
export const setDiyProjuctClass = async (id) => {
|
||||
return await request.get({
|
||||
url: `/system/dict-data/diy-template-goods?id=` + id
|
||||
})
|
||||
}
|
||||
|
||||
// 获取商品分类接口
|
||||
export const getDiyProjuctClass = async () => {
|
||||
return await request.get({
|
||||
url: `/system/dict-data/getGoods`
|
||||
})
|
||||
}
|
||||
|
||||
// 设置主题风格
|
||||
export const setDiyZtClass = async (id) => {
|
||||
return await request.get({
|
||||
url: `/system/dict-data/diy-template-theme?id=` + id
|
||||
})
|
||||
}
|
||||
|
||||
// 获取主题风格
|
||||
export const getDiyZtClass = async () => {
|
||||
return await request.get({
|
||||
url: `/system/dict-data/getTheme`
|
||||
})
|
||||
}
|
@ -25,8 +25,8 @@ export const saveDynamicData = async (data: ArticleVO) => {
|
||||
}
|
||||
|
||||
// 查询文章管理列表
|
||||
export const collectDataList = async (query: any) => {
|
||||
return await request.get({ url: `/intelligentForm/collectDataList`, query })
|
||||
export const collectDataList = async (id: any) => {
|
||||
return await request.get({ url: `/intelligentForm/collectDataList?id=` + id, })
|
||||
}
|
||||
|
||||
// // 修改文章管理
|
||||
|
@ -23,8 +23,8 @@ export const KeFuConversationApi = {
|
||||
return await request.get({ url: '/promotion/kefu-conversation/list' })
|
||||
},
|
||||
// 获得客服会话列表
|
||||
getConversationListByKefuId: async (kefuId: number) => {
|
||||
return await request.get({ url: '/promotion/kefu-conversation/list?kefuId=' + kefuId })
|
||||
getConversationListByKefuId: async (kefuId: number , name: string) => {
|
||||
return await request.get({ url: '/promotion/kefu-conversation/list?kefuId=' + kefuId + `&name=` + name })
|
||||
},
|
||||
// 客服会话置顶
|
||||
updateConversationPinned: async (data: any) => {
|
||||
|
@ -32,5 +32,10 @@ export const KeFuMessageApi = {
|
||||
// 获得消息分页数据
|
||||
getKeFuMessagePage: async (params: any) => {
|
||||
return await request.get({ url: '/promotion/kefu-message/page', params })
|
||||
},
|
||||
|
||||
// 获得消息分页数据
|
||||
getBySenderIdStat: async (senderId: number) => {
|
||||
return await request.get({ url: '/promotion/kefu-message/getBySenderIdStat?senderId=' + senderId })
|
||||
}
|
||||
}
|
||||
|
@ -44,5 +44,12 @@ export const SupportStaffApi = {
|
||||
// 导出客服人员 Excel
|
||||
exportSupportStaff: async (params) => {
|
||||
return await request.download({ url: `/promotion/support-staff/export-excel`, params })
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
// 修改客服人员
|
||||
updateLineStatus: async (id: string,lineStatus: number) => {
|
||||
return await request.get({ url: `/promotion/support-staff/updateLineStatus?id=` + id + `&lineStatus=` + lineStatus })
|
||||
},
|
||||
|
||||
}
|
||||
|
BIN
yudao-admin-vue3/src/assets/imgs/liebiao.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
yudao-admin-vue3/src/assets/imgs/quxiao.png
Normal file
After Width: | Height: | Size: 4.0 KiB |
BIN
yudao-admin-vue3/src/assets/imgs/tubiao.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
@ -24,7 +24,7 @@
|
||||
:content="appLink.path" placement="bottom" :show-after="300">
|
||||
|
||||
<el-button class="m-b-8px m-r-8px m-l-0px!"
|
||||
:type="isSameLink(appLink.path, activeAppLink.path) ? 'primary' : 'default'"
|
||||
:type="appLink.path == activeAppLink.path ? 'primary' : 'default'"
|
||||
@click="handleAppLinkSelected(appLink)">
|
||||
{{ appLink.name }}
|
||||
</el-button>
|
||||
@ -109,9 +109,14 @@
|
||||
|
||||
// 处理 APP 链接选中
|
||||
const handleAppLinkSelected = (appLink : AppLink) => {
|
||||
if (!isSameLink(appLink.path, activeAppLink.value.path)) {
|
||||
activeAppLink.value = appLink
|
||||
console.log(activeAppLink.value,activeAppLink.value.path,"activeAppLink.value")
|
||||
if(!appLink.path.includes('/pages/index/page')){
|
||||
if (!isSameLink(appLink.path, activeAppLink.value.path)) {
|
||||
activeAppLink.value = appLink
|
||||
// console.log(activeAppLink.value,activeAppLink.value.path,"activeAppLink.value")
|
||||
}
|
||||
}else{
|
||||
activeAppLink.value.path = appLink.path
|
||||
console.log(activeAppLink.value.path,"activeAppLink.value.path")
|
||||
}
|
||||
switch (appLink.type) {
|
||||
case APP_LINK_TYPE_ENUM.PRODUCT_CATEGORY_LIST:
|
||||
|
@ -65,11 +65,11 @@ export const APP_LINK_GROUP_LIST = [
|
||||
name: '商品搜索',
|
||||
path: '/pages/index/search'
|
||||
},
|
||||
{
|
||||
name: '自定义页面',
|
||||
path: '/pages/index/page',
|
||||
type: APP_LINK_TYPE_ENUM.DIY_PAGE_DETAIL
|
||||
},
|
||||
// {
|
||||
// name: '自定义页面',
|
||||
// path: '/pages/index/page',
|
||||
// type: APP_LINK_TYPE_ENUM.DIY_PAGE_DETAIL
|
||||
// },
|
||||
{
|
||||
name: '客服',
|
||||
path: '/pages/chat/index'
|
||||
@ -78,13 +78,25 @@ export const APP_LINK_GROUP_LIST = [
|
||||
name: '系统设置',
|
||||
path: '/pages/public/setting'
|
||||
},
|
||||
{
|
||||
name: '常见问题',
|
||||
path: '/pages/public/faq'
|
||||
},
|
||||
// {
|
||||
// name: '常见问题',
|
||||
// path: '/pages/public/faq'
|
||||
// },
|
||||
{
|
||||
name: '积分商城',
|
||||
path: '/pages/public/faq'
|
||||
path: '/pages/index/page?id=3'
|
||||
},
|
||||
{
|
||||
name:'我的积分',
|
||||
path:'/pages/user/wallet/score'
|
||||
},
|
||||
{
|
||||
name:'兑换记录',
|
||||
path:'/pages/activity/point/exchange_list'
|
||||
},
|
||||
{
|
||||
name:'积分商品列表',
|
||||
path:'/pages/activity/point/exchange_listall?id=3'
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -111,11 +123,11 @@ export const APP_LINK_GROUP_LIST = [
|
||||
path: '/pages/goods/seckill',
|
||||
type: APP_LINK_TYPE_ENUM.PRODUCT_DETAIL_SECKILL
|
||||
},
|
||||
{
|
||||
name: '促销列表',
|
||||
path: '/pages/goods/sales',
|
||||
type: APP_LINK_TYPE_ENUM.PRODUCT_DETAIL_SECKILL
|
||||
},
|
||||
// {
|
||||
// name: '促销列表',
|
||||
// path: '/pages/goods/sales',
|
||||
// type: APP_LINK_TYPE_ENUM.PRODUCT_DETAIL_SECKILL
|
||||
// },
|
||||
{
|
||||
name: '门店管理',
|
||||
path: '/pages/user/goods_details_store/index',
|
||||
@ -195,11 +207,12 @@ export const APP_LINK_GROUP_LIST = [
|
||||
{
|
||||
name: '充值记录',
|
||||
path: '/pages/pay/recharge-log'
|
||||
},
|
||||
{
|
||||
name: '核销记录',
|
||||
path: '/pages/pay/recharge-log'
|
||||
}
|
||||
// ,
|
||||
// {
|
||||
// name: '核销记录',
|
||||
// path: '/pages/pay/recharge-log'
|
||||
// }
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -248,7 +261,19 @@ export const APP_LINK_GROUP_LIST = [
|
||||
{
|
||||
name: '会员中心',
|
||||
path: '/pages/user/user_vip/index'
|
||||
}
|
||||
},
|
||||
{
|
||||
name:'付费会员',
|
||||
path:'/pages/user/user_vip/list'
|
||||
},
|
||||
{
|
||||
name:'预约中心',
|
||||
path:'/pages/subscribe/subscribe'
|
||||
},
|
||||
{
|
||||
name:'预约记录',
|
||||
path:'pages/reservation_record/reservation_record'
|
||||
}
|
||||
]
|
||||
}
|
||||
// ,
|
||||
|
@ -112,7 +112,7 @@ defineProps<{ property: MenuGridProperty }>()
|
||||
border-radius: 5px;
|
||||
margin: 0 auto;
|
||||
padding: 5px 0;
|
||||
margin-top: 10px;
|
||||
// margin-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
padding-top: 10px;
|
||||
&>.t {
|
||||
|
@ -1,33 +1,34 @@
|
||||
<template>
|
||||
<ComponentContainerProperty v-model="formData.style">
|
||||
<!-- 表单 -->
|
||||
<el-form label-width="80px" :model="formData" class="m-t-8px">
|
||||
<el-form-item label="每行数量" prop="column">
|
||||
<el-radio-group v-model="formData.column">
|
||||
<el-radio :label="3">3个</el-radio>
|
||||
<el-radio :label="4">4个</el-radio>
|
||||
<el-radio :label="5">5个</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<ComponentContainerProperty v-model="formData.style">
|
||||
<!-- 表单 -->
|
||||
<el-form label-width="80px" :model="formData" class="m-t-8px">
|
||||
<!-- <el-form-item label="每行数量" prop="column">
|
||||
<el-radio-group v-model="formData.column">
|
||||
<el-radio :label="3">3个</el-radio>
|
||||
<el-radio :label="4">4个</el-radio>
|
||||
<el-radio :label="5">5个</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item> -->
|
||||
|
||||
<el-card header="菜单设置" class="property-group" shadow="never">
|
||||
<Draggable v-model="formData.list" :empty-item="EMPTY_MENU_GRID_ITEM_PROPERTY">
|
||||
<template #default="{ element }">
|
||||
<el-form-item label="图标" prop="iconUrl">
|
||||
<UploadImg v-model="element.iconUrl" height="80px" width="80px">
|
||||
<template #tip> 建议尺寸:44 * 44 </template>
|
||||
</UploadImg>
|
||||
</el-form-item>
|
||||
<el-form-item label="标题" prop="title">
|
||||
<InputWithColor v-model="element.title" v-model:color="element.titleColor" />
|
||||
</el-form-item>
|
||||
<el-form-item label="副标题" prop="subtitle">
|
||||
<el-card header="菜单设置" class="property-group" shadow="never">
|
||||
<Draggable v-model="formData.list" handle="false">
|
||||
<template #default="{ element }">
|
||||
<!-- <el-form-item label="图标" prop="iconUrl">
|
||||
<UploadImg v-model="element.iconUrl" height="80px" width="80px">
|
||||
<template #tip> 建议尺寸:44 * 44 </template>
|
||||
</UploadImg>
|
||||
</el-form-item> -->
|
||||
<el-form-item label="标题" prop="title">
|
||||
<!-- <InputWithColor v-model="element.title" v-model:color="element.titleColor" /> -->
|
||||
<div>{{element.title}}</div>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="副标题" prop="subtitle">
|
||||
<InputWithColor v-model="element.subtitle" v-model:color="element.subtitleColor" />
|
||||
</el-form-item>
|
||||
<el-form-item label="链接" prop="url">
|
||||
<AppLinkInput v-model="element.url" />
|
||||
</el-form-item>
|
||||
<el-form-item label="显示角标" prop="badge.show">
|
||||
</el-form-item> -->
|
||||
<el-form-item label="链接" prop="url">
|
||||
<AppLinkInput v-model="element.url" />
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="显示角标" prop="badge.show">
|
||||
<el-switch v-model="element.badge.show" />
|
||||
</el-form-item>
|
||||
<template v-if="element.badge.show">
|
||||
@ -40,27 +41,67 @@
|
||||
<el-form-item label="背景颜色" prop="badge.bgColor">
|
||||
<ColorInput v-model="element.badge.bgColor" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
</template>
|
||||
</Draggable>
|
||||
</el-card>
|
||||
</el-form>
|
||||
</ComponentContainerProperty>
|
||||
</template> -->
|
||||
</template>
|
||||
</Draggable>
|
||||
</el-card>
|
||||
</el-form>
|
||||
</ComponentContainerProperty>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
||||
import {
|
||||
EMPTY_MENU_GRID_ITEM_PROPERTY,
|
||||
MenuGridProperty
|
||||
} from '@/components/DiyEditor/components/mobile/MenuGrid/config'
|
||||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
||||
import {
|
||||
EMPTY_MENU_GRID_ITEM_PROPERTY,
|
||||
MenuGridProperty
|
||||
} from '@/components/DiyEditor/components/mobile/MenuGrid/config'
|
||||
|
||||
/** 宫格导航属性面板 */
|
||||
defineOptions({ name: 'MenuGridProperty' })
|
||||
/** 宫格导航属性面板 */
|
||||
defineOptions({ name: 'MenuGridProperty' })
|
||||
|
||||
const props = defineProps<{ modelValue: MenuGridProperty }>()
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const { formData } = usePropertyForm(props.modelValue, emit)
|
||||
const props = defineProps<{ modelValue : MenuGridProperty }>()
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const { formData } = usePropertyForm(props.modelValue, emit)
|
||||
if (formData.value.list.length == 1 || formData.value.list.length == 2 || formData.value.list.length == 3) {
|
||||
formData.value = {
|
||||
column: 3,
|
||||
list: [
|
||||
{
|
||||
title: "今日推荐",
|
||||
url: '/pages/index/page?id=19'
|
||||
},
|
||||
{
|
||||
title: "热门榜单",
|
||||
url: '/pages/index/page?id=21'
|
||||
},
|
||||
{
|
||||
title: "首发新品",
|
||||
url: '/pages/index/page?id=22'
|
||||
},
|
||||
{
|
||||
title: "促销单品",
|
||||
url: '/pages/index/page?id=23'
|
||||
}
|
||||
],
|
||||
style: {
|
||||
bgColor: "#fff",
|
||||
bgType: "color",
|
||||
borderBottomLeftRadius: 8,
|
||||
borderBottomRightRadius: 8,
|
||||
borderRadius: 8,
|
||||
borderTopLeftRadius: 8,
|
||||
borderTopRightRadius: 8,
|
||||
marginBottom: 8,
|
||||
marginLeft: 8,
|
||||
marginRight: 8,
|
||||
padding: 0,
|
||||
paddingBottom: 0,
|
||||
paddingLeft: 0,
|
||||
paddingRight: 0,
|
||||
paddingTop: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
<style scoped lang="scss"></style>
|
@ -120,7 +120,8 @@
|
||||
<IFrame class="w-375px border-4px border-rounded-8px border-solid p-2px h-667px!" :src="previewUrl" />
|
||||
<div class="flex flex-col">
|
||||
<el-text>手机扫码预览</el-text>
|
||||
<Qrcode :text="previewUrl" logo="/logo.gif" />
|
||||
<Qrcode :text="previewUrl" />
|
||||
<!-- <Qrcode :text="previewUrl" logo="/logo.gif" />-->
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
@ -512,4 +513,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
@ -47,7 +47,7 @@ export const useRenderLayout = () => {
|
||||
|
||||
<div
|
||||
|
||||
style="transition: all var(--transition-time-02);width:85%;margin:0 auto;"
|
||||
style="transition: all var(--transition-time-02);width:83%;margin:0 auto;"
|
||||
>
|
||||
<ElScrollbar
|
||||
v-loading={pageLoading.value}
|
||||
|
@ -124,6 +124,9 @@ export enum DICT_TYPE {
|
||||
TECHNICIAN_STATUS = 'technician_status',
|
||||
SEX = 'sex',
|
||||
|
||||
//客服配置类型
|
||||
KEFU_CONFIGURATION_TYPE = 'kefu_configuration_type',
|
||||
|
||||
INFRA_FILE_TYPE = 'infra_file_type',
|
||||
|
||||
PAY_WALLET_RECHARGE_PAY_STATUS = 'pay_wallet_recharge_pay_status',
|
||||
|
@ -184,9 +184,9 @@ const loginData = reactive({
|
||||
captchaEnable: import.meta.env.VITE_APP_CAPTCHA_ENABLE,
|
||||
tenantEnable: import.meta.env.VITE_APP_TENANT_ENABLE,
|
||||
loginForm: {
|
||||
tenantName: '雲頂玖號',
|
||||
username: 'ydadmin',
|
||||
password: '123456',
|
||||
tenantName: '众悦e家',
|
||||
username: 'admin',
|
||||
password: 'admin123',
|
||||
captchaVerification: '',
|
||||
rememberMe: true // 默认记录我。如果不需要,可手动修改
|
||||
}
|
||||
@ -351,4 +351,4 @@ onMounted(() => {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
@ -1,376 +1,470 @@
|
||||
<template>
|
||||
<doc-alert title="上传下载" url="https://doc.iocoder.cn/file/" />
|
||||
<doc-alert title="上传下载" url="https://doc.iocoder.cn/file/"/>
|
||||
|
||||
<div class="flex-container">
|
||||
<!-- 菜单区域 -->
|
||||
<div class="menu-area">
|
||||
<div class="flex-container">
|
||||
<!-- 菜单区域 -->
|
||||
<div class="menu-area">
|
||||
|
||||
<el-button type="primary" plain @click="createType" style="width: 90;font-size: 12px;">
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增分类
|
||||
</el-button>
|
||||
<el-button type="primary" plain @click="createType" style="width: 90;font-size: 12px;">
|
||||
<Icon icon="ep:plus" class="mr-5px"/>
|
||||
新增分类
|
||||
</el-button>
|
||||
|
||||
<el-menu :default-active="targetMenuId" style="width:182px">
|
||||
<el-menu-item :index="targetMenuId" :key="targetMenuId" @click="clickMenu(targetMenuId)">
|
||||
全部类型
|
||||
</el-menu-item>
|
||||
<el-menu-item v-for="item in typeMenu" :index="item.value" :key="item.value"
|
||||
@click="clickMenu(item.value)">
|
||||
{{ item.label }}
|
||||
<el-icon style="margin-left: 60px;width: 10px;" @mouseover="showActions = item.value"
|
||||
@mouseleave="showActions = null">
|
||||
<MoreFilled />
|
||||
<div v-if="showActions === item.value" class="action-buttons">
|
||||
<el-button size="small" @click.stop="editItem(item.id,item.label)">编辑</el-button>
|
||||
<br />
|
||||
<el-button size="small" @click.stop="deleteItem(item.id)">删除</el-button>
|
||||
</div>
|
||||
</el-icon>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
<el-menu :default-active="targetMenuId" style="width:200px">
|
||||
<el-menu-item :index="targetMenuId" :key="targetMenuId" @click="clickMenu(targetMenuId)">
|
||||
全部类型
|
||||
</el-menu-item>
|
||||
<el-menu-item v-for="item in typeMenu" :index="item.value" :key="item.value"
|
||||
@click="clickMenu(item.value)">
|
||||
{{ item.label }}
|
||||
<el-icon style="margin-left: 60px;width: 10px;" @mouseover="showActions = item.value"
|
||||
@mouseleave="showActions = null">
|
||||
<MoreFilled/>
|
||||
<div v-if="showActions === item.value" class="action-buttons">
|
||||
<el-button size="small" @click.stop="editItem(item.id,item.label)">编辑</el-button>
|
||||
<br/>
|
||||
<el-button size="small" @click.stop="deleteItem(item.id)">删除</el-button>
|
||||
</div>
|
||||
</el-icon>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 内容区域 -->
|
||||
<div class="content-wrap">
|
||||
<ContentWrap>
|
||||
<el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
|
||||
<!-- <el-form-item label="文件路径" prop="path">
|
||||
<el-input
|
||||
v-model="queryParams.path"
|
||||
placeholder="请输入文件路径"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item> -->
|
||||
<el-form-item label="文件类型" prop="type" width="80">
|
||||
<el-input v-model="queryParams.type" placeholder="请输入文件类型" clearable
|
||||
@keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<!-- 内容区域 -->
|
||||
<div class="content-wrap">
|
||||
|
||||
<el-form-item label="创建时间" prop="createTime">
|
||||
<el-date-picker v-model="queryParams.createTime" value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange" start-placeholder="开始日期" end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery">
|
||||
<Icon icon="ep:search" class="mr-5px" /> 搜索
|
||||
</el-button>
|
||||
<el-button @click="resetQuery">
|
||||
<Icon icon="ep:refresh" class="mr-5px" /> 重置
|
||||
</el-button>
|
||||
<el-button type="primary" plain @click="openForm">
|
||||
<Icon icon="ep:upload" class="mr-5px" /> 上传文件
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
<div style="margin-top: 25px;margin-left: 20px;">
|
||||
<el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true"
|
||||
label-width="68px">
|
||||
<el-form-item label="文件类型" prop="type" width="80">
|
||||
<el-input v-model="queryParams.type" placeholder="请输入文件类型" clearable
|
||||
@keyup.enter="handleQuery"/>
|
||||
</el-form-item>
|
||||
|
||||
<!-- <el-form-item label="创建时间" prop="createTime">
|
||||
<el-date-picker v-model="queryParams.createTime" value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange" start-placeholder="开始日期" end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" />
|
||||
</el-form-item> -->
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery">
|
||||
<Icon icon="ep:search" class="mr-5px"/>
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button @click="resetQuery">
|
||||
<Icon icon="ep:refresh" class="mr-5px"/>
|
||||
重置
|
||||
</el-button>
|
||||
<el-button type="primary" plain @click="openForm">
|
||||
<Icon icon="ep:upload" class="mr-5px"/>
|
||||
上传文件
|
||||
</el-button>
|
||||
<img @click="liebiao" style="cursor: pointer;margin-left: 240px;" class="mr-10px h-27px w-27px"
|
||||
src="@/assets/imgs/liebiao.png"/>
|
||||
<img @click="tubiao" style="cursor: pointer;" class="mr-10px h-27px w-27px"
|
||||
src="@/assets/imgs/tubiao.png"/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="文件内容" align="center" prop="url" width="110px">
|
||||
<template #default="{ row }">
|
||||
<el-image v-if="row.type.includes('image')" class="h-80px w-80px" lazy :src="row.url"
|
||||
:preview-src-list="[row.url]" preview-teleported fit="cover" />
|
||||
<el-link v-else-if="row.type.includes('pdf')" type="primary" :href="row.url"
|
||||
:underline="false" target="_blank">预览</el-link>
|
||||
<el-link v-else type="primary" download :href="row.url" :underline="false"
|
||||
target="_blank">下载</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="文件名" align="center" prop="name" :show-overflow-tooltip="true" />
|
||||
<!-- <el-table-column label="文件路径" align="center" prop="path" :show-overflow-tooltip="true" /> -->
|
||||
<el-table-column label="URL" align="center" prop="url" :show-overflow-tooltip="true" />
|
||||
<!-- <el-table-column label="文件大小" align="center" prop="size" width="120"
|
||||
:formatter="fileSizeFormatter" /> -->
|
||||
<!-- <el-table-column label="文件类型" align="center" prop="type" width="180px" />
|
||||
<el-table-column label="图片分类" align="center" prop="picType">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.INFRA_FILE_TYPE" :value="scope.row.picType" />
|
||||
</template>
|
||||
</el-table-column> -->
|
||||
<div style="margin-top: 10px;" >
|
||||
<el-table v-loading="loading" :data="list" v-show="panduan == '1'">
|
||||
<el-table-column label="文件内容" align="center" prop="url" width="110px">
|
||||
<template #default="{ row }">
|
||||
<el-image v-if="row.type.includes('image')" class="h-40px w-40px" lazy :src="row.url"
|
||||
:preview-src-list="[row.url]" preview-teleported fit="cover"/>
|
||||
<el-link v-else-if="row.type.includes('pdf')" type="primary" :href="row.url"
|
||||
:underline="false" target="_blank">预览
|
||||
</el-link>
|
||||
<el-link v-else type="primary" download :href="row.url" :underline="false"
|
||||
target="_blank">下载
|
||||
</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="文件名" align="center" prop="name" :show-overflow-tooltip="true"/>
|
||||
<!-- <el-table-column label="文件路径" align="center" prop="path" :show-overflow-tooltip="true" /> -->
|
||||
<el-table-column label="URL" align="center" prop="url" :show-overflow-tooltip="true"/>
|
||||
|
||||
<el-table-column label="上传时间" align="center" prop="createTime" width="180"
|
||||
:formatter="dateFormatter" />
|
||||
<el-table-column label="操作" align="center">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary"
|
||||
@click="updateForm('update', scope.row.id , scope.row.picType)">
|
||||
更改类型
|
||||
</el-button>
|
||||
<el-button link type="danger" @click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['infra:file:delete']">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</ContentWrap>
|
||||
<!-- 分页 -->
|
||||
<Pagination :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList" />
|
||||
<el-table-column label="上传时间" align="center" prop="createTime" width="180"
|
||||
:formatter="dateFormatter"/>
|
||||
<el-table-column label="操作" align="center">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary"
|
||||
@click="updateForm('update', scope.row.id , scope.row.picType)">
|
||||
更改类型
|
||||
</el-button>
|
||||
<el-button link type="danger" @click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['infra:file:delete']">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<FileForm ref="formRef" @success="getList" />
|
||||
<div v-show="panduan == '2'">
|
||||
<!-- 图片展示区域 -->
|
||||
<div class="image-container">
|
||||
<div
|
||||
v-for="item in tubiaoData"
|
||||
:key="item.id"
|
||||
class="image-item"
|
||||
@mouseover="hover(item.id, true)"
|
||||
@mouseleave="hover(item.id, false)"
|
||||
>
|
||||
<img :src="item.url" alt="图表"/>
|
||||
<div class="image-name">{{ item.name }}</div>
|
||||
<div v-show="hoveredIndex === item.id" class="button-container">
|
||||
<button class="button">按钮1</button>
|
||||
<button class="button">按钮2</button>
|
||||
<button class="button">按钮3</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<UpdateForm ref="forRef" @success="getList" />
|
||||
</div>
|
||||
|
||||
<Dialog v-model="dialogVisibles" :title="dialogTitles">
|
||||
<el-form ref="formRef" v-loading="formLoading" :model="formData" :rules="formRules" label-width="80px">
|
||||
|
||||
<el-form-item label="数据标签" prop="label">
|
||||
<el-input v-model="formData.label" placeholder="请输入数据标签" />
|
||||
</el-form-item>
|
||||
<el-form-item label="显示排序" prop="sort">
|
||||
<el-input-number v-model="formData.sort" :min="0" controls-position="right" />
|
||||
</el-form-item>
|
||||
<!-- 分页 -->
|
||||
<Pagination :total="total" v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"/>
|
||||
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="dialogVisibles = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
|
||||
|
||||
<Dialog v-model="dialogVisible" :title="dialogTitle">
|
||||
<el-form ref="formRef" v-loading="formLoading" :model="formData" label-width="80px">
|
||||
<el-form-item label="数据标签" prop="label">
|
||||
<el-input v-model="updateLabel" placeholder="请输入数据标签" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button :disabled="formLoading" type="primary" @click="submitForms">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<FileForm ref="formRef" @success="getList"/>
|
||||
|
||||
<UpdateForm ref="forRef" @success="getList"/>
|
||||
|
||||
<Dialog v-model="dialogVisibles" :title="dialogTitles">
|
||||
<el-form ref="formRef" v-loading="formLoading" :model="formData" :rules="formRules"
|
||||
label-width="80px">
|
||||
|
||||
<el-form-item label="数据标签" prop="label">
|
||||
<el-input v-model="formData.label" placeholder="请输入数据标签"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="显示排序" prop="sort">
|
||||
<el-input-number v-model="formData.sort" :min="0" controls-position="right"/>
|
||||
</el-form-item>
|
||||
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="dialogVisibles = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
|
||||
<Dialog v-model="dialogVisible" :title="dialogTitle">
|
||||
<el-form ref="formRef" v-loading="formLoading" :model="formData" label-width="80px">
|
||||
<el-form-item label="数据标签" prop="label">
|
||||
<el-input v-model="updateLabel" placeholder="请输入数据标签"/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button :disabled="formLoading" type="primary" @click="submitForms">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
|
||||
import { fileSizeFormatter } from '@/utils'
|
||||
import { Search, MoreFilled } from '@element-plus/icons-vue';
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
// import * as ElementPlusIconsVue from '@element-plus/icons-vue'
|
||||
import * as FileApi from '@/api/infra/file'
|
||||
import FileForm from './FileForm.vue'
|
||||
import UpdateForm from './updateForm.vue'
|
||||
import * as DictDataApi from '@/api/system/dict/dict.data'
|
||||
const typeMenu = ref<DictDataApi.DictDataVO[]>([]) //
|
||||
const targetMenuId = ref('0')
|
||||
defineOptions({ name: 'InfraFile' })
|
||||
const showActions = ref(null);
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const dialogVisibles = ref(false) // 弹窗的是否展示
|
||||
const dialogTitles = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
sort: undefined,
|
||||
label: '',
|
||||
value: '',
|
||||
dictType: '',
|
||||
// status: CommonStatusEnum.ENABLE,
|
||||
colorType: '',
|
||||
cssClass: '',
|
||||
remark: ''
|
||||
})
|
||||
const formRules = reactive({
|
||||
label: [{ required: true, message: '数据标签不能为空', trigger: 'blur' }],
|
||||
sort: [{ required: true, message: '数据顺序不能为空', trigger: 'blur' }],
|
||||
})
|
||||
const updateLabel = ref('')
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
import {getIntDictOptions, DICT_TYPE} from '@/utils/dict'
|
||||
import {fileSizeFormatter} from '@/utils'
|
||||
import {Search, MoreFilled} from '@element-plus/icons-vue';
|
||||
import {dateFormatter} from '@/utils/formatTime'
|
||||
// import * as ElementPlusIconsVue from '@element-plus/icons-vue'
|
||||
import * as FileApi from '@/api/infra/file'
|
||||
import FileForm from './FileForm.vue'
|
||||
import UpdateForm from './updateForm.vue'
|
||||
import * as DictDataApi from '@/api/system/dict/dict.data'
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const total = ref(0) // 列表的总页数
|
||||
const list = ref([]) // 列表的
|
||||
const menuId = ref()
|
||||
const typeMenu = ref<DictDataApi.DictDataVO[]>([]) //
|
||||
const targetMenuId = ref('0')
|
||||
defineOptions({name: 'InfraFile'})
|
||||
const showActions = ref(null);
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const dialogVisibles = ref(false) // 弹窗的是否展示
|
||||
const dialogTitles = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const panduan = ref('1')
|
||||
const tubiaoData = ref<FileDataVO[]>([]);
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
sort: undefined,
|
||||
label: '',
|
||||
value: '',
|
||||
dictType: '',
|
||||
// status: CommonStatusEnum.ENABLE,
|
||||
colorType: '',
|
||||
cssClass: '',
|
||||
remark: ''
|
||||
})
|
||||
const formRules = reactive({
|
||||
label: [{required: true, message: '数据标签不能为空', trigger: 'blur'}],
|
||||
sort: [{required: true, message: '数据顺序不能为空', trigger: 'blur'}],
|
||||
})
|
||||
const updateLabel = ref('')
|
||||
const message = useMessage() // 消息弹窗
|
||||
const {t} = useI18n() // 国际化
|
||||
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: undefined,
|
||||
type: undefined,
|
||||
picType: '',
|
||||
path: undefined,
|
||||
createTime: [],
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const total = ref(0) // 列表的总页数
|
||||
const list = ref([]) // 列表的
|
||||
const menuId = ref()
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await FileApi.getFilePage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: undefined,
|
||||
type: undefined,
|
||||
picType: '',
|
||||
path: undefined,
|
||||
createTime: [],
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
|
||||
|
||||
//新增分类
|
||||
const createType = () => {
|
||||
dialogVisibles.value = true
|
||||
dialogTitles.value = '新增分类'
|
||||
// const hoveredIndex = ref(null);
|
||||
|
||||
// const hover = (index: string, status: boolean) => {
|
||||
// if (status) {
|
||||
// hoveredIndex.value = index; // 鼠标悬停时显示按钮
|
||||
// } else {
|
||||
// hoveredIndex.value = ''; // 鼠标离开时隐藏按钮
|
||||
// }
|
||||
// }
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await FileApi.getFilePage(queryParams)
|
||||
list.value = data.list
|
||||
tubiaoData.value = data.list
|
||||
console.log('111111', tubiaoData)
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
interface FileDataVO {
|
||||
id: string
|
||||
configId: string
|
||||
path: string
|
||||
name: string
|
||||
url: string
|
||||
type: string
|
||||
picType: string
|
||||
size: string
|
||||
createTime: Date
|
||||
}
|
||||
|
||||
/** 添加分类菜单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
if (!formRef) return
|
||||
const valid = await formRef.value.validate()
|
||||
if (!valid) return
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value as DictDataApi.DictDataVO
|
||||
await DictDataApi.createPicType(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
dialogVisibles.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
getTypeList()
|
||||
const tubiao = () => {
|
||||
panduan.value = '2'
|
||||
}
|
||||
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
const liebiao = () => {
|
||||
panduan.value = '1'
|
||||
}
|
||||
|
||||
//修改分类菜单
|
||||
const submitForms = async () => {
|
||||
// 校验表单
|
||||
if (!formRef) return
|
||||
const valid = await formRef.value.validate()
|
||||
if (!valid) return
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
//新增分类
|
||||
const createType = () => {
|
||||
dialogVisibles.value = true
|
||||
dialogTitles.value = '新增分类'
|
||||
}
|
||||
|
||||
await DictDataApi.updateMenu(menuId.value, updateLabel.value)
|
||||
message.success(t('common.createSuccess'))
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
getTypeList()
|
||||
/** 添加分类菜单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
if (!formRef) return
|
||||
const valid = await formRef.value.validate()
|
||||
if (!valid) return
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value as DictDataApi.DictDataVO
|
||||
await DictDataApi.createPicType(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
dialogVisibles.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
getTypeList()
|
||||
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
//分类菜单修改按钮
|
||||
const editItem = (id : number | undefined, lable : string) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = '菜单编辑'
|
||||
menuId.value = id
|
||||
updateLabel.value = lable
|
||||
// 处理编辑事件
|
||||
console.log('编辑:',);
|
||||
};
|
||||
//分类菜单删除按钮
|
||||
const deleteItem = async (id : number | undefined) => {
|
||||
// 处理删除事件
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
await DictDataApi.deleteMenu(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
getTypeList()
|
||||
//修改分类菜单
|
||||
const submitForms = async () => {
|
||||
// 校验表单
|
||||
if (!formRef) return
|
||||
const valid = await formRef.value.validate()
|
||||
if (!valid) return
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
|
||||
};
|
||||
//图片素材管理分类菜单
|
||||
const getTypeList = async () => {
|
||||
const data = await DictDataApi.getTypeList()
|
||||
typeMenu.value = data
|
||||
console.log('1111111111', typeMenu)
|
||||
}
|
||||
await DictDataApi.updateMenu(menuId.value, updateLabel.value)
|
||||
message.success(t('common.createSuccess'))
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
getTypeList()
|
||||
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
//分类菜单修改按钮
|
||||
const editItem = (id: number | undefined, lable: string) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = '菜单编辑'
|
||||
menuId.value = id
|
||||
updateLabel.value = lable
|
||||
// 处理编辑事件
|
||||
console.log('编辑:',);
|
||||
};
|
||||
//分类菜单删除按钮
|
||||
const deleteItem = async (id: number | undefined) => {
|
||||
// 处理删除事件
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
await DictDataApi.deleteMenu(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
getTypeList()
|
||||
|
||||
};
|
||||
//图片素材管理分类菜单
|
||||
const getTypeList = async () => {
|
||||
const data = await DictDataApi.getTypeList()
|
||||
typeMenu.value = data
|
||||
}
|
||||
|
||||
|
||||
/** */
|
||||
const clickMenu = (id : string) => {
|
||||
queryParams.picType = id
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
/** */
|
||||
const clickMenu = (id: string) => {
|
||||
queryParams.picType = id
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
|
||||
const formRef = ref()
|
||||
const openForm = () => {
|
||||
formRef.value.open()
|
||||
}
|
||||
const formRef = ref()
|
||||
const openForm = () => {
|
||||
formRef.value.open()
|
||||
}
|
||||
|
||||
const forRef = ref()
|
||||
/** 修改操作 */
|
||||
const updateForm = (type : string, id : number, picType : number) => {
|
||||
forRef.value.open(type, id, picType)
|
||||
}
|
||||
const forRef = ref()
|
||||
/** 修改操作 */
|
||||
const updateForm = (type: string, id: number, picType: number) => {
|
||||
forRef.value.open(type, id, picType)
|
||||
}
|
||||
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await FileApi.deleteFile(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id : number) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await FileApi.deleteFile(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch { }
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
queryParams.picType = targetMenuId.value
|
||||
getTypeList()
|
||||
getList()
|
||||
})
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
queryParams.picType = targetMenuId.value
|
||||
getTypeList()
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
<style scoped>
|
||||
.flex-container {
|
||||
height: 100vh;
|
||||
/* 使容器填满视口高度 */
|
||||
display: flex;
|
||||
}
|
||||
.flex-container {
|
||||
height: 100vh;
|
||||
/* 使容器填满视口高度 */
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.menu-area {
|
||||
padding: 25px;
|
||||
background-color: white;
|
||||
width: 180px;
|
||||
height: 1000px;
|
||||
/* 固定高度 */
|
||||
overflow-y: hidden;
|
||||
/* 禁止滚动 */
|
||||
border-right: 1px solid #e0e0e0;
|
||||
/* 可选:添加分隔线 */
|
||||
}
|
||||
.menu-area {
|
||||
padding: 25px;
|
||||
background-color: white;
|
||||
width: 180px;
|
||||
height: 700px;
|
||||
/* 固定高度 */
|
||||
overflow-y: hidden;
|
||||
/* 禁止滚动 */
|
||||
border-right: 1px solid #e0e0e0;
|
||||
/* 可选:添加分隔线 */
|
||||
}
|
||||
|
||||
.content-wrap {
|
||||
flex-grow: 1;
|
||||
/* 使内容区域占据剩余空间 */
|
||||
overflow-y: auto;
|
||||
/* 允许内容区域滚动 */
|
||||
}
|
||||
</style>
|
||||
.content-wrap {
|
||||
background-color: white;
|
||||
flex-grow: 1;
|
||||
/* 使内容区域占据剩余空间 */
|
||||
/* overflow-y: auto; */
|
||||
/* 允许内容区域滚动 */
|
||||
}
|
||||
|
||||
|
||||
/* 图片容器样式 */
|
||||
.image-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px; /* 设置图片之间的间距 */
|
||||
justify-content: flex-start; /* 如果图片少于5张,会平均分布 */
|
||||
max-width: 1000px; /* 设置外部容器的最大宽度 */
|
||||
margin: 0 auto; /* 居中显示 */
|
||||
}
|
||||
|
||||
/* 每张图片占用的宽度,使每行显示5张 */
|
||||
.image-item {
|
||||
width: calc(20% - 8px); /* 宽度设为容器的20%,减去间距 */
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
/* 设置图片的最大宽度 */
|
||||
.image-item img {
|
||||
width: 80%;
|
||||
height: 80%;
|
||||
/* border-radius: 8px; /* 图片边缘圆角(可选) */
|
||||
border: 3px solid #f3f3f3;
|
||||
}
|
||||
|
||||
.image-name {
|
||||
margin-top: 5 px; /* 图片和名称之间的间距 */
|
||||
font-size: 14px; /* 文字大小 */
|
||||
color: #333; /* 文字颜色 */
|
||||
word-wrap: break-word; /* 如果名称很长,自动换行 */
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
|
@ -0,0 +1,118 @@
|
||||
<template>
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="100px"
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<el-form-item label="广告状态" prop="status">
|
||||
<el-radio-group v-model="formData.status">
|
||||
<el-radio
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.PROMOTION_DIY_ADVERTISING)"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="广告时间(秒)" prop="time">
|
||||
<el-date-picker
|
||||
v-model="formData.time"
|
||||
type="date"
|
||||
value-format="x"
|
||||
placeholder="选择广告时间(秒)"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="广告属性" prop="property">
|
||||
<el-input v-model="formData.property" placeholder="请输入广告属性" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
|
||||
import { AdvertisingApi, AdvertisingVO } from '@/api/mall/promotion/advertising'
|
||||
|
||||
/** 开屏广告 表单 */
|
||||
defineOptions({ name: 'AdvertisingForm' })
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
status: undefined,
|
||||
time: undefined,
|
||||
property: undefined
|
||||
})
|
||||
const formRules = reactive({
|
||||
status: [{ required: true, message: '广告状态不能为空', trigger: 'blur' }],
|
||||
time: [{ required: true, message: '广告时间(秒)不能为空', trigger: 'blur' }],
|
||||
property: [{ required: true, message: '广告属性不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type: string, id?: number) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = t('action.' + type)
|
||||
formType.value = type
|
||||
resetForm()
|
||||
// 修改时,设置数据
|
||||
if (id) {
|
||||
formLoading.value = true
|
||||
try {
|
||||
formData.value = await AdvertisingApi.getAdvertising(id)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
await formRef.value.validate()
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value as unknown as AdvertisingVO
|
||||
if (formType.value === 'create') {
|
||||
await AdvertisingApi.createAdvertising(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await AdvertisingApi.updateAdvertising(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
status: undefined,
|
||||
time: undefined,
|
||||
property: undefined
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
</script>
|
228
yudao-admin-vue3/src/views/mall/promotion/advertising/index.vue
Normal file
@ -0,0 +1,228 @@
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<span>数据配置</span>
|
||||
<el-button type="primary" @click="baocun" style="margin-left:85%">保存</el-button>
|
||||
</ContentWrap>
|
||||
|
||||
<ContentWrap>
|
||||
<div class="container">
|
||||
<div class="left">
|
||||
|
||||
<img :src="image" style="width: 100%;height: 100%;border-radius: 10px;" v-show="check == 1" />
|
||||
|
||||
<span v-show="check == 2" style="margin-left:80px">暂无图片,请添加~</span>
|
||||
</div>
|
||||
<div class="right">
|
||||
<div>
|
||||
<p style="font-weight: 700;">引导页设置</p>
|
||||
<span>开屏广告:</span><span> <el-switch v-model="formData.stat" size="large" /> </span>
|
||||
<br />
|
||||
<span>广告时间:</span><span> <el-input v-model="formData.time" style="width: 240px"
|
||||
placeholder="请输入秒" /></span><span> 单位(秒)</span>
|
||||
<br />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
<div v-for="(item, index) in timeRanges" :key="index" class="rectangle-container">
|
||||
<div class="imageLeft">
|
||||
<UploadImg width="100px" height="100px" v-model="item.pic" />
|
||||
</div>
|
||||
<div class="inputRight">
|
||||
<div style="margin-top: 20%;">
|
||||
<span>图片名称:</span>
|
||||
</div>
|
||||
<br />
|
||||
<div>
|
||||
<span>链接地址:</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<el-input style="width: 200px;margin-top: 3%;" type="text" v-model="item.name"
|
||||
placeholder="请输入名称" />
|
||||
</div>
|
||||
<br />
|
||||
<div>
|
||||
<AppLinkInput v-model="item.url" />
|
||||
</div>
|
||||
</div>
|
||||
<img @click="removePic(index)"
|
||||
style="width: 25px;height: 25px;position: absolute;top: 1px;right: 1px;cursor: pointer;"
|
||||
src="@/assets/imgs/quxiao.png" />
|
||||
<br />
|
||||
</div>
|
||||
|
||||
<br />
|
||||
<el-button type="primary" @click="addNewPic">添加图片</el-button>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { AdvertisingApi, AdvertisingVO } from '@/api/mall/promotion/advertising'
|
||||
|
||||
|
||||
/** 开屏广告 列表 */
|
||||
defineOptions({ name: 'Advertising' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
status: undefined,
|
||||
stat: false,
|
||||
time: undefined,
|
||||
property: undefined,
|
||||
picData: [{ pic: '', name: '', url: '' }]
|
||||
})
|
||||
|
||||
const image = ref('')
|
||||
const check = ref(2)
|
||||
|
||||
const timeRanges = ref([{ pic: '', name: '', url: '' }]);
|
||||
//添加
|
||||
const addNewPic = () => {
|
||||
if (timeRanges.value.length <= 5) {
|
||||
timeRanges.value.push({ pic: '', name: '', url: '' });
|
||||
}
|
||||
};
|
||||
//移出
|
||||
const removePic = (index : number) => {
|
||||
if (timeRanges.value.length >= 1) {
|
||||
timeRanges.value.splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//提交保存
|
||||
const baocun = async () => {
|
||||
|
||||
formData.value.picData = timeRanges.value
|
||||
const data = formData.value as unknown as AdvertisingVO
|
||||
await AdvertisingApi.saveAdvertising(data)
|
||||
getAdvertising()
|
||||
message.success('保存成功')
|
||||
|
||||
}
|
||||
|
||||
|
||||
/** 查询开屏广告数据配置 */
|
||||
const getAdvertising = async () => {
|
||||
|
||||
formData.value = await AdvertisingApi.getAdvertising()
|
||||
|
||||
if (formData.value.id == null) {
|
||||
console.log('11111:', formData.value)
|
||||
} else {
|
||||
console.log('22222:', formData.value)
|
||||
check.value = 1
|
||||
timeRanges.value = formData.value.picData
|
||||
if (formData.value.picData && formData.value.picData.length > 0) {
|
||||
image.value = formData.value.picData[0].pic
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// if(formData.value.picData.length < 1){
|
||||
// timeRanges.value = [{ pic: '', name: '', url: '' }]
|
||||
// }
|
||||
// if(formData.value.picData.length > 0){
|
||||
//
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getAdvertising()
|
||||
})
|
||||
</script>
|
||||
<style>
|
||||
.container {
|
||||
display: flex;
|
||||
/* justify-content: space-between; /* 左右分布 */
|
||||
width: 1000px;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
.left {
|
||||
margin-left: 20px;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
width: 300px;
|
||||
height: 550px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.right {
|
||||
margin-left: 50px;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 上传图片
|
||||
*/
|
||||
.rectangle-container {
|
||||
padding: 10px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8px;
|
||||
position: relative;
|
||||
margin-top: 13px;
|
||||
display: grid;
|
||||
/* 使用 Grid 布局 */
|
||||
grid-template-columns: 25% 20% 55%;
|
||||
/* 设置四列,每列占 25% 宽度 */
|
||||
/* 元素间的间隔 */
|
||||
width: 450px;
|
||||
/* 或者设置固定宽度 */
|
||||
height: 100px;
|
||||
|
||||
}
|
||||
|
||||
/* .imageLeft {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
} */
|
||||
|
||||
/* .inputRight {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
} */
|
||||
|
||||
/* .right-input {
|
||||
background-color: aqua;
|
||||
} */
|
||||
|
||||
|
||||
|
||||
.image-preview {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
/* object-fit: cover;
|
||||
border-radius: 8px; */
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* .inputRight input {
|
||||
margin-bottom: 10px;
|
||||
padding: 8px;
|
||||
width: 200px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
} */
|
||||
</style>
|
@ -67,9 +67,9 @@
|
||||
@click="openForm('create')"
|
||||
v-hasPermi="['promotion:auto-response:create']"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增自动回复
|
||||
</el-button>
|
||||
<el-button
|
||||
<!-- <el-button
|
||||
type="success"
|
||||
plain
|
||||
@click="handleExport"
|
||||
@ -77,7 +77,7 @@
|
||||
v-hasPermi="['promotion:auto-response:export']"
|
||||
>
|
||||
<Icon icon="ep:download" class="mr-5px" /> 导出
|
||||
</el-button>
|
||||
</el-button> -->
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
|
@ -0,0 +1,141 @@
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<span>客服配置</span>
|
||||
<el-divider />
|
||||
<div style="height:380px;width:650px;padding: 20px;">
|
||||
<span>客服类型:</span>
|
||||
<span>
|
||||
<el-radio-group v-model="radios" @change="handleRadioChange">
|
||||
<el-radio v-for="dict in getIntDictOptions(DICT_TYPE.KEFU_CONFIGURATION_TYPE)"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
size="large"
|
||||
>
|
||||
{{dict.label}}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</span>
|
||||
<p style="margin-left: 80px;color:#bcbaba;font-size:13px">系统客服:点击联系客服使用系统的自带客服;拨打电话:点击联系客服拨打客服电话;跳转链接:跳转外部链接联系客服</p>
|
||||
|
||||
<div v-show="radios == 1">
|
||||
<span style="margin-top: 60px; vertical-align: middle;">客服反馈:</span>
|
||||
<span style="vertical-align: middle;">
|
||||
<el-input
|
||||
v-model="formData.feedback"
|
||||
placeholder="请输入客服反馈"
|
||||
style="width:550px"
|
||||
:rows="5"
|
||||
type="textarea"
|
||||
|
||||
/>
|
||||
</span>
|
||||
<p style="margin-left: 80px;color:#bcbaba;font-size:13px">暂无客服在线是,联系客服跳转的客服反馈页面的显示文字</p>
|
||||
</div>
|
||||
|
||||
<div v-show="radios == 2">
|
||||
<span style="margin-top: 60px; vertical-align: middle;">客服电话:</span>
|
||||
<span style="vertical-align: middle;">
|
||||
<el-input
|
||||
v-model="formData.phone"
|
||||
placeholder="请输入客服电话"
|
||||
style="width:550px"
|
||||
/>
|
||||
</span>
|
||||
<p style="margin-left: 80px;color:#bcbaba;font-size:13px">客服类型选择拨打电话时,用户点击联系客服的联系电话</p>
|
||||
</div>
|
||||
|
||||
<div v-show="radios == 3">
|
||||
<span style="margin-top: 60px; vertical-align: middle;">客服链接:</span>
|
||||
<span style="vertical-align: middle;">
|
||||
<el-input
|
||||
v-model="formData.link"
|
||||
placeholder="请输入客服链接"
|
||||
style="width:550px"
|
||||
/>
|
||||
</span>
|
||||
<p style="margin-left: 80px;color:#bcbaba;font-size:13px">客服类型选择跳转链接时,跳转的链接地址</p>
|
||||
|
||||
<span style="margin-top: 60px; vertical-align: middle;"> 企业ID:</span>
|
||||
<span style="vertical-align: middle;">
|
||||
<el-input
|
||||
v-model="formData.enterpriseID"
|
||||
placeholder="请输入企业ID"
|
||||
style="width:550px"
|
||||
/>
|
||||
</span>
|
||||
<p style="margin-left: 80px;color:#bcbaba;font-size:13px">如果客服链接填写企业微信客服,小程序需要跳转企业微信客服的话需要配置此项,并且在小程序客服中绑定企业ID</p>
|
||||
|
||||
</div>
|
||||
|
||||
<el-button style="margin-left: 80px;" type="primary" @click="submit">提交</el-button>
|
||||
|
||||
</div>
|
||||
</ContentWrap>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
|
||||
|
||||
import { ConfigurationApi, ConfigurationVO } from '@/api/mall/promotion/configuration'
|
||||
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
|
||||
const radios = ref(1)
|
||||
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
type: undefined,
|
||||
feedback: undefined,
|
||||
phone: undefined,
|
||||
link: undefined,
|
||||
enterpriseID: undefined
|
||||
})
|
||||
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
|
||||
formData.value = await ConfigurationApi.getConfiguration(radios.value)
|
||||
// radios.value = formData.value.id
|
||||
console.log('11111',radios.value)
|
||||
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const handleRadioChange = async (value) =>{
|
||||
formData.value = await ConfigurationApi.getConfiguration(value)
|
||||
|
||||
|
||||
console.log("选中的值是: ", radios.value); // 输出选中的值
|
||||
|
||||
}
|
||||
|
||||
|
||||
const submit = async () => {
|
||||
const data = formData.value as unknown as ConfigurationVO
|
||||
await ConfigurationApi.updateConfiguration(data)
|
||||
message.success('提交成功')
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
@ -0,0 +1,124 @@
|
||||
<template>
|
||||
<div class="main">
|
||||
<div class="mainTop">
|
||||
<div>商品分类</div>
|
||||
<div class="right">
|
||||
<div class="save" @click="save">保存</div>
|
||||
<div class="cz" @click="cz">重置</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mainBottom">
|
||||
<div class='item' :class="currItem == 1 ? 'on': ''" @click="clickItem(1)">
|
||||
<img class="img" src="https://zysc.fjptzykj.com:3000/shangcheng/921278a710b8035ac2e4fd40a08f64e3652c5130c08c633b7e136714d57c1001.jpg" />
|
||||
<div class="text">样式1</div>
|
||||
</div>
|
||||
<div class='item' :class="currItem == 2 ? 'on': ''" @click="clickItem(2)">
|
||||
<img class="img" src="https://zysc.fjptzykj.com:3000/shangcheng/909ef630cff7cdc957962ae782ee9a882c86c35732140b1c9e013341fa559126.jpg" />
|
||||
<div class="text">样式2</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
// TODO @疯狂:要不要建个 decorate 目录,然后挪进去,改成 index.vue,这样可以更明确看到是个独立界面哈,更好找
|
||||
import * as DiyTemplateApi from '@/api/mall/promotion/diy/template'
|
||||
import { useTagsdivStore } from '@/store/modules/tagsdiv'
|
||||
import { DiyComponentLibrary, PAGE_LIBS } from '@/components/DiyEditor/util' // 商城的 DIY 组件,在 DiyEditor 目录下
|
||||
import { toNumber } from 'lodash-es'
|
||||
const message = useMessage() // 消息弹窗
|
||||
const currItem = ref();
|
||||
|
||||
function clickItem (val){
|
||||
currItem.value = val;
|
||||
}
|
||||
|
||||
function cz (val){
|
||||
currItem.value = 1;
|
||||
}
|
||||
|
||||
function save (){
|
||||
setProjuctClass(currItem.value);
|
||||
// console.log("请求接口还没有写啊!!!!快让后端提供啊")
|
||||
}
|
||||
|
||||
// 设置商品分类
|
||||
const setProjuctClass = async (id) => {
|
||||
const res = await DiyTemplateApi.setDiyProjuctClass(id);
|
||||
console.log(res, "sssss");
|
||||
if(res){
|
||||
message.success('保存成功')
|
||||
}
|
||||
}
|
||||
|
||||
// 获取商品分类
|
||||
const getProjuctClass = async () => {
|
||||
const res = await DiyTemplateApi.getDiyProjuctClass();
|
||||
currItem.value = res
|
||||
}
|
||||
getProjuctClass()
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.main{
|
||||
display:flex;
|
||||
flex-wrap:wrap;
|
||||
.mainTop{
|
||||
width:100%;
|
||||
margin:10px 10px;
|
||||
background:white;
|
||||
padding:10px 20px;
|
||||
font-weight:700;
|
||||
display:flex;
|
||||
justify-content:space-between;
|
||||
align-items: center;
|
||||
.right{
|
||||
display:flex;
|
||||
div{
|
||||
padding:5px 15px;
|
||||
font-weight:400;
|
||||
font-size: 14px;
|
||||
}
|
||||
.save{
|
||||
background:#0256FF;
|
||||
margin-right:10px;
|
||||
color:white;
|
||||
cursor: pointer;
|
||||
}
|
||||
.cz{
|
||||
border:1px solid #cccccc;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
.mainBottom{
|
||||
width:100%;
|
||||
margin:10px 10px;
|
||||
background:white;
|
||||
padding:30px 30px;
|
||||
display:flex;
|
||||
.item{
|
||||
margin-right:20px;
|
||||
text-align: center;
|
||||
border-radius: 12px;
|
||||
cursor: pointer;
|
||||
&.on{
|
||||
.text{
|
||||
color:#0256ff;
|
||||
}
|
||||
.img{
|
||||
border:2px solid #0256ff;
|
||||
}
|
||||
}
|
||||
.img{
|
||||
width:260px;
|
||||
border-radius: 12px;
|
||||
border:2px solid white;
|
||||
}
|
||||
.text{
|
||||
margin-top:10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,215 @@
|
||||
<template>
|
||||
<div class="main">
|
||||
<div class="mainTop">
|
||||
<div>主题风格</div>
|
||||
<div class="right">
|
||||
<div class="save" @click="save">保存</div>
|
||||
<!-- <div class="cz" @click="cz">重置</div> -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ztfg">
|
||||
<div class='top'>
|
||||
<div class="item" :class="currItem== '' ? 'on': ''" @click="clickItem('')">
|
||||
<div class="le" style="background: rgb(233, 52, 34);"></div>
|
||||
<div class="ri">红色</div>
|
||||
</div>
|
||||
<div class="item" :class="currItem== 'blue' ? 'on': ''" @click="clickItem('blue')">
|
||||
<div class="le"></div>
|
||||
<div class="ri">天空蓝</div>
|
||||
</div>
|
||||
<div class="item" :class="currItem== 'lv' ? 'on': ''" @click="clickItem('lv')">
|
||||
<div class="le" style="background:rgb(66, 202, 77);"></div>
|
||||
<div class="ri">生鲜绿</div>
|
||||
</div>
|
||||
<div class="item" :class="currItem== 'pink' ? 'on': ''" @click="clickItem('pink')">
|
||||
<div class="le" style="background:#ff448f;"></div>
|
||||
<div class="ri">魅力粉</div>
|
||||
</div>
|
||||
<div class="item" :class="currItem== 'orange' ? 'on': ''" @click="clickItem('orange')">
|
||||
<div class="le" style="background:#fe5c2d;"></div>
|
||||
<div class="ri">活力橙</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mainBottom">
|
||||
<div class='item' v-show="currItem== ''">
|
||||
<img class="img"
|
||||
src="https://zysc.fjptzykj.com:3000/shangcheng/f973a0a4648d476a25b14a29250356d065507a1b9134b6303d7acd0b2d2d9cc7.jpg"/>
|
||||
</div>
|
||||
|
||||
<div class='item' v-show="currItem== 'blue'">
|
||||
<img class="img"
|
||||
src="https://zysc.fjptzykj.com:3000/shangcheng/f2c0e04f8491b618d691eb95c3df2f89ba565134cacc9b4e39ff96caa4d1cec0.jpg" />
|
||||
</div>
|
||||
<div class='item' v-show="currItem== 'lv'">
|
||||
<img class="img"
|
||||
src="https://zysc.fjptzykj.com:3000/shangcheng/1ba06619f3f87677b51dd735b03e0378da352abbe47b188dd43c58262ed47cca.jpg" />
|
||||
</div>
|
||||
<div class='item' v-show="currItem== 'pink'">
|
||||
<img class="img"
|
||||
src="https://zysc.fjptzykj.com:3000/shangcheng/13fd006f8883b492db10df8f1d03347b88c8836e68a603db874fd68bc01c7a71.jpg" />
|
||||
</div>
|
||||
<div class='item' v-show="currItem== 'orange'">
|
||||
<img class="img"
|
||||
src="https://zysc.fjptzykj.com:3000/shangcheng/da61c583680494e23a31f39acbb0246cf1d439080ceb53d85dc564da2c1ecfe9.jpg" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
// TODO @疯狂:要不要建个 decorate 目录,然后挪进去,改成 index.vue,这样可以更明确看到是个独立界面哈,更好找
|
||||
import * as DiyTemplateApi from '@/api/mall/promotion/diy/template'
|
||||
import * as DiyPageApi from '@/api/mall/promotion/diy/page'
|
||||
import { useTagsdivStore } from '@/store/modules/tagsdiv'
|
||||
import { DiyComponentLibrary, PAGE_LIBS } from '@/components/DiyEditor/util' // 商城的 DIY 组件,在 DiyEditor 目录下
|
||||
import { toNumber } from 'lodash-es'
|
||||
const message = useMessage() // 消息弹窗
|
||||
const currItem = ref('blue');
|
||||
|
||||
function clickItem(val) {
|
||||
currItem.value = val;
|
||||
}
|
||||
|
||||
function cz(val) {
|
||||
currItem.value = 'blue';
|
||||
}
|
||||
|
||||
function save() {
|
||||
const val = '';
|
||||
// currItem.value = val;
|
||||
if(currItem.value == 'lv'){
|
||||
setZtClass('lv')
|
||||
}else if(currItem.value == 'blue'){
|
||||
setZtClass('blue')
|
||||
}else if(currItem.value == 'pink'){
|
||||
setZtClass('pink')
|
||||
}else if(currItem.value == 'orange'){
|
||||
setZtClass('orange')
|
||||
}else{
|
||||
setZtClass('')
|
||||
}
|
||||
// console.log("请求接口还没有写啊!!!!快让后端提供啊")
|
||||
}
|
||||
|
||||
// 设置主题风格
|
||||
const setZtClass = async (id) => {
|
||||
const res = await DiyTemplateApi.setDiyZtClass(id);
|
||||
console.log(res, "sssss");
|
||||
if(res || res == ''){
|
||||
message.success('保存成功')
|
||||
}
|
||||
}
|
||||
|
||||
// 获取商品分类
|
||||
const getZtClass = async () => {
|
||||
const res = await DiyTemplateApi.getDiyZtClass();
|
||||
currItem.value = res
|
||||
}
|
||||
getZtClass()
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.main {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.mainTop {
|
||||
width: 100%;
|
||||
margin: 10px 10px;
|
||||
background: white;
|
||||
padding: 10px 20px;
|
||||
font-weight: 700;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.right {
|
||||
display: flex;
|
||||
|
||||
div {
|
||||
padding: 5px 15px;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.save {
|
||||
background: #0256FF;
|
||||
margin-right: 10px;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.cz {
|
||||
border: 1px solid #cccccc;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ztfg {
|
||||
width: 100%;
|
||||
margin: 10px 10px;
|
||||
background: white;
|
||||
padding: 30px 30px;
|
||||
.top{
|
||||
display: flex;
|
||||
margin-bottom: 10px;
|
||||
.item{
|
||||
padding:10px 15px;
|
||||
border:1px solid #cccccc;
|
||||
border-radius:6px;
|
||||
margin-right:10px;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
cursor: pointer;
|
||||
&.on{
|
||||
border:1px solid #0256ff;
|
||||
}
|
||||
.le{
|
||||
margin-right:10px;
|
||||
border-radius:6px;
|
||||
width:25px;
|
||||
height:25px;
|
||||
background:rgb(28, 165, 233);
|
||||
}
|
||||
.ti{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
.mainBottom {
|
||||
|
||||
display: flex;
|
||||
|
||||
.item {
|
||||
margin-right: 20px;
|
||||
text-align: center;
|
||||
border-radius: 12px;
|
||||
|
||||
// &.on {
|
||||
// .text {
|
||||
// color: #0256ff;
|
||||
// }
|
||||
|
||||
// .img {
|
||||
// border: 2px solid #0256ff;
|
||||
// }
|
||||
// }
|
||||
|
||||
.img {
|
||||
width: 800px;
|
||||
border-radius: 12px;
|
||||
border: 2px solid white;
|
||||
}
|
||||
|
||||
.text {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
@ -2,7 +2,7 @@
|
||||
<DiyEditor v-if="formData && !formLoading" v-model="currentFormData!.property"
|
||||
:title="templateItems[selectedTemplateItem].name" :libs="libs" :show-page-config="selectedTemplateItem !== 0"
|
||||
:show-tab-bar="selectedTemplateItem === 0" :show-navigation-bar="selectedTemplateItem !== 0"
|
||||
:preview-url="previewUrl" @save="submitForm" @reset="handleEditorReset">
|
||||
:preview-url="previewUrl" @save="submitForm" @reset="handleEditorReset" :showTabBar = "false">
|
||||
<template #toolBarLeft>
|
||||
<!-- <el-radio-group
|
||||
v-model="selectedTemplateItem"
|
||||
|
@ -4,17 +4,17 @@
|
||||
:show-tab-bar="selectedTemplateItem === 0" :show-navigation-bar="selectedTemplateItem !== 0"
|
||||
:preview-url="previewUrl" @save="submitForm" @reset="handleEditorReset">
|
||||
<template #toolBarLeft>
|
||||
<!-- <el-radio-group
|
||||
v-model="selectedTemplateItem"
|
||||
class="h-full!"
|
||||
@change="handleTemplateItemChange"
|
||||
>
|
||||
<el-tooltip v-for="(item, index) in templateItems" :key="index" :content="item.name">
|
||||
<el-radio-button :label="index">
|
||||
<Icon :icon="item.icon" :size="24" />
|
||||
</el-radio-button>
|
||||
</el-tooltip>
|
||||
</el-radio-group> -->
|
||||
<!-- <el-radio-group-->
|
||||
<!-- v-model="selectedTemplateItem"-->
|
||||
<!-- class="h-full!"-->
|
||||
<!-- @change="handleTemplateItemChange"-->
|
||||
<!-- >-->
|
||||
<!-- <el-tooltip v-for="(item, index) in templateItems" :key="index" :content="item.name">-->
|
||||
<!-- <el-radio-button :label="index">-->
|
||||
<!-- <Icon :icon="item.icon" :size="24" />-->
|
||||
<!-- </el-radio-button>-->
|
||||
<!-- </el-tooltip>-->
|
||||
<!-- </el-radio-group>-->
|
||||
</template>
|
||||
</DiyEditor>
|
||||
</template>
|
||||
@ -172,4 +172,4 @@
|
||||
// console.log(newPath,'newPathnewPath');
|
||||
// // handleTemplateItemChange()
|
||||
// }, { immediate: true });
|
||||
</script>
|
||||
</script>
|
||||
|
@ -11,14 +11,17 @@
|
||||
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
|
||||
<el-card class="box-card" v-for="o in cardList" :key="o">
|
||||
<div class="clearfix">
|
||||
<span style="font-size: 23px;
|
||||
color: #000000;
|
||||
background-color:#ffffff;
|
||||
border-color: #fdfdff;">{{o.title}}</span>
|
||||
<el-button style="float: right; padding: 3px 0" type="text" @click="handelExtend(o.id)">推广</el-button>
|
||||
<el-button style="float: right;" type="text" @click="deleteForm(o.id)"> 删除</el-button>
|
||||
<el-button style="float: right;" type="text" @click="handelExtend(o.id)">推广</el-button>
|
||||
<el-button style="float: right;" type="text" @click="preview(o.id)">预览</el-button>
|
||||
<el-button style="float: right;" type="text" @click="redacts(o)">编辑</el-button>
|
||||
</div>
|
||||
<div style="display: flex;justify-content: space-between">
|
||||
<div style="width: 30%">
|
||||
@ -31,8 +34,8 @@
|
||||
<el-link style="font-size: 18px;
|
||||
color: #999595;
|
||||
background-color:#ffffff;
|
||||
border-color: #fdfdff;" @click="extendList(o.id)">采集数量</el-link>
|
||||
<p style="font-size: 25px" @click="extendList(o.id)">{{o.collectionQuantity}}</p>
|
||||
border-color: #fdfdff;" @click="extendList(o.id,o.title)">采集数量</el-link>
|
||||
<p style="font-size: 25px" @click="extendList(o.id,o.title)">{{o.collectionQuantity}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex;justify-content: space-between">
|
||||
@ -48,71 +51,111 @@
|
||||
border-color: #fdfdff;">创建时间:{{o.createTime}}</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
|
||||
<!-- 添加表单对话框 -->
|
||||
<el-dialog :title="title" v-model="open" width="500">
|
||||
|
||||
|
||||
<div style="text-align: center">
|
||||
<!-- <el-form-item label="表单标题">-->
|
||||
<div><span>标题</span> <el-input style="border-bottom: 1px solid skyblue;margin-bottom: 10px"
|
||||
<div>
|
||||
<span>标题</span>
|
||||
<el-input
|
||||
style="border-left: none; border-right: none; border-top: none; border-bottom: 1px solid skyblue; margin-bottom: 10px"
|
||||
v-model="addParams.title" placeholder="请输入表单标题" />
|
||||
</div>
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item label="字段名称">-->
|
||||
<div v-for="(item,index) in inputarr" :key="index" style="margin-bottom: 10px;">
|
||||
<span>字段</span>
|
||||
<el-input style="border: 1px solid #DCDFE6;border-radius: 3px; "
|
||||
v-model="inputarr[index]" placeholder="请输入字段" />
|
||||
<span>字段</span>
|
||||
<el-input style="border: 1px solid #DCDFE6;border-radius: 3px; " v-model="inputarr[index]"
|
||||
placeholder="请输入字段" />
|
||||
</div>
|
||||
<div>
|
||||
<el-button @click="adinput"
|
||||
style="text-align: center;width: 90%;background-color: rgb(232,244,255);color:#1890ff">添加字段</el-button>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- </el-form-item>-->
|
||||
</div>
|
||||
|
||||
|
||||
<div class="el-input-foot">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
|
||||
<!-- 推广链接对话框-->
|
||||
<el-dialog :title="extendTitle" v-model="extendOpen" width="500px" append-to-body>
|
||||
<el-input v-model="extendLink" placeholder="请输入内容" :disabled="true" />
|
||||
<el-button @click="copyAddress">复制</el-button>
|
||||
</el-dialog>
|
||||
|
||||
|
||||
<!-- 采集数据列表 -->
|
||||
<el-dialog :title="collectTitle" v-model="collectOpen" append-to-body>
|
||||
<el-table border v-loading="loading" :data="collectLink" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<div>
|
||||
<el-dialog :title="collectTitle" v-model="collectOpen" append-to-body>
|
||||
|
||||
</div>
|
||||
<!-- <el-table-column label="编号" align="center" prop="id" />-->
|
||||
<!-- <el-table-column label="id" align="center" prop="dynamicDataId" />-->
|
||||
<!-- <el-table-column v-for="item in collectLink" :label="item.map" align="center" :prop="item" />-->
|
||||
<template v-for="(item, index) in Object.keys(collectLink[0])" :key="index">
|
||||
<el-table-column :label="item">
|
||||
<template #default="scope">
|
||||
<span>{{scope.row[item]}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table border v-loading="loading" :data="collectLink" @selection-change="handleSelectionChange">
|
||||
<!-- <el-table-column type="selection" width="55" align="center" /> -->
|
||||
|
||||
<template v-for="(item, index) in getColumnNames" :key="index">
|
||||
<el-table-column
|
||||
:label="capitalize(item)"
|
||||
:prop="item"
|
||||
/>
|
||||
</template>
|
||||
</el-table>
|
||||
</el-dialog>
|
||||
|
||||
|
||||
|
||||
<!--编辑弹出框-->
|
||||
<el-dialog :title="redactTitle" v-model="redactOpen" width="500">
|
||||
|
||||
<div style="text-align: center">
|
||||
<!-- 标题 -->
|
||||
<div>
|
||||
<span>标题</span>
|
||||
<el-input
|
||||
style="border-left: none; border-right: none; border-top: none; border-bottom: 1px solid skyblue; margin-bottom: 10px"
|
||||
v-model="redactName" placeholder="请输入表单标题" />
|
||||
</div>
|
||||
<!-- 字段 -->
|
||||
<div v-for="(item,index) in inputarr" :key="index" style="margin-bottom: 10px;">
|
||||
<span>字段</span>
|
||||
<el-input style="border: 1px solid #DCDFE6;border-radius: 3px; " v-model="inputarr[index]"
|
||||
placeholder="请输入字段" />
|
||||
</div>
|
||||
<!-- 添加字段按钮 -->
|
||||
<div>
|
||||
<el-button @click="adinput"
|
||||
style="text-align: center;width: 90%;background-color: rgb(232,244,255);color:#1890ff">添加字段</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="el-input-foot">
|
||||
<el-button type="primary" @click="submitForms">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {FollowUpRecordApi} from "@/api/crm/intelligent.ts";
|
||||
import { FollowUpRecordApi} from "@/api/crm/intelligent.ts";
|
||||
// import {Message} from 'element-ui';
|
||||
export default {
|
||||
name: "Business",
|
||||
dicts: ['stage_type', 'sales_stage'],
|
||||
data() {
|
||||
return {
|
||||
// 编辑弹出层标题
|
||||
redactTitle: "编辑",
|
||||
// 是否显示编辑弹出层
|
||||
redactOpen: false,
|
||||
//编辑id
|
||||
redactId: '',
|
||||
//编辑标题
|
||||
redactName: '',
|
||||
id: '',
|
||||
inputarr: [''],
|
||||
// 遮罩层
|
||||
@ -142,12 +185,13 @@
|
||||
//推广链接数据
|
||||
extendLink: '',
|
||||
//采集数据列表数据
|
||||
collectLink: [{}],
|
||||
collectLink: [],
|
||||
//下拉框客户列表
|
||||
customerList: [],
|
||||
//卡片列表
|
||||
cardList: [],
|
||||
addParams: {
|
||||
id: '',
|
||||
title: '',
|
||||
pageView: '',
|
||||
collectionQuantity: '',
|
||||
@ -158,23 +202,38 @@
|
||||
created() {
|
||||
this.getIntelligentList();
|
||||
},
|
||||
computed: {
|
||||
// 获取数据对象的所有键名(字段名),用于动态渲染列
|
||||
getColumnNames() {
|
||||
// 确保 collectLink 不是 null 或 undefined,并且不是空数组
|
||||
if (!this.collectLink || this.collectLink.length === 0) {
|
||||
return [];
|
||||
}
|
||||
return Object.keys(this.collectLink[0]);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
capitalize(str) {
|
||||
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||
},
|
||||
//添加表单
|
||||
adinput() {
|
||||
this.inputarr.push('')
|
||||
console.log(this.inputarr)
|
||||
},
|
||||
//卡片数据
|
||||
getIntelligentList() {
|
||||
this.loading = true;
|
||||
FollowUpRecordApi.selectDynamicData().then(response => {
|
||||
this.cardList = response;
|
||||
console.log(response,'response.data')
|
||||
console.log(response, 'response.data')
|
||||
// this.total = response.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
// 取消按钮
|
||||
cancel() {
|
||||
this.redactOpen = false;
|
||||
this.open = false;
|
||||
},
|
||||
//提交按钮
|
||||
@ -191,6 +250,25 @@
|
||||
this.getIntelligentList();
|
||||
});
|
||||
},
|
||||
//修改提交按钮
|
||||
submitForms() {
|
||||
|
||||
this.addParams.id = this.redactId
|
||||
this.addParams.inputarr = this.inputarr
|
||||
this.addParams.title = this.redactName
|
||||
|
||||
FollowUpRecordApi.updateDynamicData(this.addParams).then(response => {
|
||||
// this.cardList = response.data;
|
||||
// this.total = response.total;
|
||||
this.loading = false;
|
||||
this.redactOpen = false;
|
||||
this.getIntelligentList();
|
||||
// this.$message({
|
||||
// message: '修改成功',
|
||||
// type: 'success'
|
||||
// });
|
||||
});
|
||||
},
|
||||
/** 新增按钮操作 */
|
||||
handleAdd() {
|
||||
this.inputarr = []
|
||||
@ -206,6 +284,53 @@
|
||||
this.extendLink = 'http://zysc.fjptzykj.com/form/extendLink/form?id=' + id
|
||||
this.extendOpen = true;
|
||||
this.extendTitle = "推广链接";
|
||||
},
|
||||
//删除按钮操作
|
||||
deleteForm(id) {
|
||||
// this.$confirm('此操作将永久删除该表单, 是否继续?', '提示', {
|
||||
// confirmButtonText: '确定',
|
||||
// cancelButtonText: '取消',
|
||||
// type: 'warning'
|
||||
// }).then(() => {
|
||||
// this.$message({
|
||||
// type: 'success',
|
||||
// message: '删除成功!'
|
||||
// });
|
||||
// }).catch(() => {
|
||||
// this.$message({
|
||||
// type: 'info',
|
||||
// message: '已取消删除'
|
||||
// });
|
||||
// });
|
||||
FollowUpRecordApi.deleteDynamicDataById(id).then(response => {
|
||||
// this.cardList = response.data;
|
||||
// this.total = response.total;
|
||||
this.getIntelligentList();
|
||||
});
|
||||
},
|
||||
// 预览
|
||||
preview(id) {
|
||||
this.extendLink = 'http://zysc.fjptzykj.com/form/extendLink/form?id=' + id
|
||||
// 打开新页面
|
||||
const newWindow = window.open(this.extendLink, '_blank');
|
||||
// 设置手机端视口(Viewport)meta标签,用来模拟手机尺寸
|
||||
newWindow.onload = () => {
|
||||
const metaTag = newWindow.document.createElement('meta');
|
||||
metaTag.name = 'viewport';
|
||||
metaTag.content = 'width=device-width, initial-scale=1.0, user-scalable=no'; // 设置视口宽度为设备宽度,并禁用缩放
|
||||
newWindow.document.head.appendChild(metaTag);
|
||||
};
|
||||
},
|
||||
// 编辑点击事件
|
||||
redacts(cardData) {
|
||||
this.inputarr = [''];
|
||||
// 1. 获取 cardData.data 中的所有键名
|
||||
this.inputarr = Object.keys(cardData.data);
|
||||
this.redactOpen = true;
|
||||
this.redactId = cardData.id
|
||||
this.redactName = cardData.title;
|
||||
|
||||
|
||||
},
|
||||
//复制按钮操作
|
||||
copyAddress() {
|
||||
@ -218,14 +343,17 @@
|
||||
alert('已复制到剪贴板');
|
||||
},
|
||||
//采集数据展示
|
||||
extendList(id) {
|
||||
extendList(id,title) {
|
||||
FollowUpRecordApi.collectDataList(id).then(response => {
|
||||
this.collectLink = response
|
||||
console.log('111111: ' + JSON.stringify(response));
|
||||
});
|
||||
|
||||
this.collectLink = null;
|
||||
this.id = id
|
||||
this.collectOpen = true;
|
||||
this.collectTitle = "采集数据";
|
||||
collectDataList(this.id).then(response => {
|
||||
this.collectLink = response.data
|
||||
});
|
||||
this.collectTitle = title+"采集数据";
|
||||
|
||||
},
|
||||
// 多选框选中数据
|
||||
handleSelectionChange(selection) {
|
||||
@ -276,6 +404,6 @@
|
||||
|
||||
::v-deep.el-input--medium .el-input__inner {
|
||||
border: none;
|
||||
/* border-bottom:1px solid blue; */
|
||||
border-bottom: 1px solid blue;
|
||||
}
|
||||
</style>
|
||||
|
@ -36,9 +36,11 @@
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 右键,进行操作(类似微信) -->
|
||||
<ul v-show="showRightMenu" :style="rightMenuStyle" class="right-menu-ul">
|
||||
<li
|
||||
@ -87,8 +89,8 @@ const activeConversationId = ref(-1) // 选中的会话
|
||||
const collapse = computed(() => appStore.getCollapse) // 折叠菜单
|
||||
|
||||
/** 加载会话列表 */
|
||||
const getConversationList = async () => {
|
||||
const list = await KeFuConversationApi.getConversationListByKefuId(getStaffToken())
|
||||
const getConversationList = async (name: string) => {
|
||||
const list = await KeFuConversationApi.getConversationListByKefuId(getStaffToken(),name)
|
||||
list.sort((a: KeFuConversationRespVO, _) => (a.adminPinned ? -1 : 1))
|
||||
conversationList.value = list
|
||||
}
|
||||
@ -182,16 +184,18 @@ watch(showRightMenu, (val) => {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.kefu {
|
||||
background-color: white;
|
||||
|
||||
&-conversation {
|
||||
height: 55px;
|
||||
padding: 8px;
|
||||
height: 50px;
|
||||
padding: 5px;
|
||||
//background-color: #fff;
|
||||
transition: border-left 0.05s ease-in-out; /* 设置过渡效果 */
|
||||
|
||||
.username {
|
||||
font-size: 20%;
|
||||
font-size: 15%;
|
||||
min-width: 0;
|
||||
max-width: 60%;
|
||||
max-width: 50%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
@ -201,7 +205,7 @@ watch(showRightMenu, (val) => {
|
||||
|
||||
.last-message {
|
||||
font-size: 15px;
|
||||
width: 200px;
|
||||
width: 170px;
|
||||
overflow: hidden; // 隐藏超出的文本
|
||||
white-space: nowrap; // 禁止换行
|
||||
text-overflow: ellipsis; // 添加省略号
|
||||
|
@ -1,14 +1,14 @@
|
||||
<template>
|
||||
<el-container v-if="showKeFuMessageList" class="kefu">
|
||||
<el-container v-if="showKeFuMessageList" style="background-color: white;height:98%" class="kefu">
|
||||
<!-- <el-header>
|
||||
<div class="kefu-title">{{ conversation.userNickname }}</div>
|
||||
</el-header> -->
|
||||
<el-main class="kefu-content overflow-visible">
|
||||
<el-scrollbar ref="scrollbarRef" always height="calc(100vh - 420px)" @scroll="handleScroll">
|
||||
<!-- <ContentWrap></ContentWrap> -->
|
||||
<el-main class="kefu-content overflow-visible">
|
||||
<el-scrollbar ref="scrollbarRef" always height="calc(100vh - 350px)" @scroll="handleScroll" style="overflow: hidden;">
|
||||
<div v-if="refreshContent" ref="innerRef" class="w-[100%] pb-3px">
|
||||
|
||||
|
||||
|
||||
<!-- 消息列表 -->
|
||||
<div v-for="(item, index) in getMessageList0" :key="item.id" class="w-[100%]">
|
||||
<div class="flex justify-center items-center mb-20px">
|
||||
@ -32,7 +32,7 @@
|
||||
? `ss-row-right`
|
||||
: ''
|
||||
]" class="flex mb-20px w-[100%]">
|
||||
<el-avatar v-if="item.senderType === UserTypeEnum.MEMBER" :src="conversation.userAvatar"
|
||||
<el-avatar v-if="item.senderType === UserTypeEnum.MEMBER" :src="conversation.userAvatar"
|
||||
alt="avatar" class="w-60px h-60px" />
|
||||
<div :class="{ 'kefu-message': KeFuMessageContentTypeEnum.TEXT === item.contentType }"
|
||||
class="p-10px">
|
||||
@ -64,14 +64,11 @@
|
||||
:message="item" class="max-w-100%" />
|
||||
</MessageItem>
|
||||
</div>
|
||||
<el-avatar v-if="item.senderType === UserTypeEnum.ADMIN" :src="item.senderAvatar"
|
||||
<el-avatar style="margin-right: 12px;" v-if="item.senderType === UserTypeEnum.ADMIN" :src="item.senderAvatar"
|
||||
alt="avatar" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
<div v-show="showNewMessageTip" class="newMessageTip flex items-center cursor-pointer"
|
||||
@ -80,9 +77,12 @@
|
||||
<Icon class="ml-5px" icon="ep:bottom" />
|
||||
</div>
|
||||
</el-main>
|
||||
<el-footer height="230px">
|
||||
|
||||
<el-divider style="margin: 4px 0;" />
|
||||
|
||||
<el-footer height="186px">
|
||||
<div class="h-[100%]">
|
||||
<div class="chat-tools flex items-center">
|
||||
<div class="flex items-center">
|
||||
<EmojiSelectPopover @select-emoji="handleEmojiSelect" />
|
||||
<PictureSelectUpload class="ml-15px mt-3px cursor-pointer" @send-picture="handleSendPicture" />
|
||||
<!-- <VerbalTrick class="ml-11px mt-5px cursor-pointer" /> -->
|
||||
@ -104,17 +104,16 @@
|
||||
</el-dropdown-item>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
|
||||
|
||||
</div>
|
||||
<el-input v-model="message" :rows="6" style="border-style: none" type="textarea" />
|
||||
<br/>
|
||||
<textarea style="border: none; outline: none;" v-model="message" placeholder="请输入文字内容" rows="5" cols="75"></textarea>
|
||||
<div class="h-45px flex justify-end">
|
||||
<el-button class="mt-10px" type="primary" @click="handleSendMessage">发送</el-button>
|
||||
<el-button class="mt-5px" type="primary" @click="handleSendMessage">发送</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-footer>
|
||||
</el-container>
|
||||
<el-empty v-else description="请选择左侧的一个会话后开始" />
|
||||
<el-empty style="background-color: white;" v-else description="请选择左侧的一个会话后开始" />
|
||||
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible" :modal="false" width="700px">
|
||||
|
||||
@ -461,6 +460,9 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
|
||||
|
||||
.hover-shadow:hover {
|
||||
background-color: lightgray;
|
||||
|
||||
@ -495,6 +497,7 @@
|
||||
.kefu-message {
|
||||
margin-left: 20px;
|
||||
position: relative;
|
||||
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
@ -537,7 +540,8 @@
|
||||
|
||||
// 消息气泡
|
||||
.kefu-message {
|
||||
color: #a9a9a9;
|
||||
font-size: 14px;
|
||||
color: #101010;
|
||||
border-radius: 5px;
|
||||
box-shadow: 3px 3px 5px rgba(220, 220, 220, 0.1);
|
||||
padding: 5px 10px;
|
||||
|
Before Width: | Height: | Size: 4.8 KiB |
After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 994 B |
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 1.4 KiB |
@ -2,11 +2,18 @@
|
||||
<template>
|
||||
<div v-show="!isEmpty(conversation)" class="kefu">
|
||||
<!-- <div class="header-title h-60px flex justify-center items-center">他的足迹</div> -->
|
||||
<el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick">
|
||||
<el-tab-pane label="最近浏览" name="a" />
|
||||
<el-tab-pane label="订单列表" name="b" />
|
||||
<el-tabs tab-position="top" v-model="activeName" class="demo-tabs" @tab-click="handleClick">
|
||||
<el-tab-pane label="足迹" name="a" />
|
||||
<el-tab-pane label="购买" name="b" />
|
||||
</el-tabs>
|
||||
<div>
|
||||
<div >
|
||||
<el-input @input="findgoodsNameInput" v-model="goodsName" placeholder="搜索商品名称" v-if="activeName === 'a'"
|
||||
style="width: 100%;"/>
|
||||
|
||||
<el-input @input="findNoInput" v-model="no" placeholder="搜索订单编号" v-if="activeName === 'b'"
|
||||
style="width: 100%;"/>
|
||||
</div>
|
||||
<div style="margin-top: 5px;">
|
||||
<el-scrollbar ref="scrollbarRef" always height="calc(115vh - 400px)" @scroll="handleScroll">
|
||||
<!-- 最近浏览 -->
|
||||
<ProductBrowsingHistory v-if="activeName === 'a'" ref="productBrowsingHistoryRef" />
|
||||
@ -30,7 +37,8 @@ import { ElScrollbar as ElScrollbarType } from 'element-plus/es/components/scrol
|
||||
defineOptions({ name: 'MemberBrowsingHistory' })
|
||||
|
||||
const activeName = ref('a')
|
||||
|
||||
const goodsName = ref('')
|
||||
const no = ref('')
|
||||
/** tab 切换 */
|
||||
const productBrowsingHistoryRef = ref<InstanceType<typeof ProductBrowsingHistory>>()
|
||||
const orderBrowsingHistoryRef = ref<InstanceType<typeof OrderBrowsingHistory>>()
|
||||
@ -40,21 +48,32 @@ const handleClick = async (tab: TabsPaneContext) => {
|
||||
await getHistoryList()
|
||||
}
|
||||
|
||||
//查询
|
||||
const findgoodsNameInput = async () => {
|
||||
getHistoryList()
|
||||
}
|
||||
|
||||
/** 获得历史数据 */
|
||||
// TODO @puhui:不要用 a、b 哈。就订单列表、浏览列表这种噶
|
||||
const getHistoryList = async () => {
|
||||
|
||||
switch (activeName.value) {
|
||||
case 'a':
|
||||
await productBrowsingHistoryRef.value?.getHistoryList(conversation.value)
|
||||
await productBrowsingHistoryRef.value?.getHistoryList(conversation.value,goodsName.value)
|
||||
break
|
||||
case 'b':
|
||||
await orderBrowsingHistoryRef.value?.getHistoryList(conversation.value)
|
||||
await orderBrowsingHistoryRef.value?.getHistoryList(conversation.value,no.value)
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
//搜索框
|
||||
const findNoInput = async () => {
|
||||
getHistoryList()
|
||||
}
|
||||
|
||||
/** 加载下一页数据 */
|
||||
const loadMore = async () => {
|
||||
switch (activeName.value) {
|
||||
|
@ -0,0 +1,92 @@
|
||||
<!-- 目录是不是叫 member 好点。然后这个组件是 MemberInfo,里面有浏览足迹 -->
|
||||
<template>
|
||||
<div v-show="!isEmpty(conversation)" class="kefu">
|
||||
<!-- <div class="header-title h-60px flex justify-center items-center">他的足迹</div> -->
|
||||
<el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick">
|
||||
<el-tab-pane label="全部" name="a" />
|
||||
<el-tab-pane label="未支付" name="b" />
|
||||
<el-tab-pane label="未发货" name="c" />
|
||||
<el-tab-pane label="退款中" name="d" />
|
||||
</el-tabs>
|
||||
<div>
|
||||
<el-input @input="findNoInput" v-model="no" placeholder="搜索订单编号"
|
||||
style="width: 100%;"/>
|
||||
</div>
|
||||
<div>
|
||||
<el-scrollbar ref="scrollbarRef" always height="calc(115vh - 400px)" @scroll="handleScroll">
|
||||
<OrderBrowsingHistorys ref="orderBrowsingHistorysRef" />
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</div>
|
||||
<el-empty v-show="isEmpty(conversation)" description="请选择左侧的一个会话后开始" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { TabsPaneContext } from 'element-plus'
|
||||
import OrderBrowsingHistorys from './OrderBrowsingHistorys.vue'
|
||||
import { KeFuConversationRespVO } from '@/api/mall/promotion/kefu/conversation'
|
||||
import { isEmpty } from '@/utils/is'
|
||||
import { debounce } from 'lodash-es'
|
||||
import { ElScrollbar as ElScrollbarType } from 'element-plus/es/components/scrollbar/index'
|
||||
|
||||
defineOptions({ name: 'MemberBrowsingHistorys' })
|
||||
|
||||
const activeName = ref('a')
|
||||
|
||||
/** tab 切换 */
|
||||
const orderBrowsingHistorysRef = ref<InstanceType<typeof OrderBrowsingHistorys>>()
|
||||
const handleClick = async (tab: TabsPaneContext) => {
|
||||
activeName.value = tab.paneName as string
|
||||
await nextTick()
|
||||
await getHistoryList()
|
||||
}
|
||||
const no = ref('')
|
||||
|
||||
/** 获得历史数据 */
|
||||
// TODO @puhui:不要用 a、b 哈。就订单列表、浏览列表这种噶 : 我就要用abcd😈
|
||||
const getHistoryList = async () => {
|
||||
console.log('11111执行11111',activeName.value)
|
||||
|
||||
await orderBrowsingHistorysRef.value?.getHistoryList(conversation.value,activeName.value,no.value)
|
||||
|
||||
}
|
||||
|
||||
//搜索框
|
||||
const findNoInput = async () => {
|
||||
getHistoryList()
|
||||
|
||||
}
|
||||
|
||||
/** 加载下一页数据 */
|
||||
const loadMore = async () => {
|
||||
await orderBrowsingHistorysRef.value?.loadMore()
|
||||
}
|
||||
|
||||
/** 浏览历史初始化 */
|
||||
const conversation = ref<KeFuConversationRespVO>({} as KeFuConversationRespVO) // 用户会话
|
||||
const initHistory = async (val: KeFuConversationRespVO) => {
|
||||
activeName.value = 'a'
|
||||
conversation.value = val
|
||||
await nextTick()
|
||||
await getHistoryList()
|
||||
}
|
||||
defineExpose({ initHistory })
|
||||
|
||||
/** 处理消息列表滚动事件(debounce 限流) */
|
||||
const scrollbarRef = ref<InstanceType<typeof ElScrollbarType>>()
|
||||
const handleScroll = debounce(() => {
|
||||
const wrap = scrollbarRef.value?.wrapRef
|
||||
// 触底重置
|
||||
if (Math.abs(wrap!.scrollHeight - wrap!.clientHeight - wrap!.scrollTop) < 1) {
|
||||
loadMore()
|
||||
}
|
||||
}, 200)
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.header-title {
|
||||
border-bottom: #e4e0e0 solid 1px;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
@ -15,7 +15,8 @@ const total = ref(0) // 总数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
userId: 0
|
||||
userId: 0,
|
||||
no: '',
|
||||
})
|
||||
const skipGetMessageList = computed(() => {
|
||||
// 已加载到最后一页的话则不触发新的消息获取
|
||||
@ -23,8 +24,9 @@ const skipGetMessageList = computed(() => {
|
||||
}) // 跳过消息获取
|
||||
|
||||
/** 获得浏览记录 */
|
||||
const getHistoryList = async (val: KeFuConversationRespVO) => {
|
||||
const getHistoryList = async (val: KeFuConversationRespVO,no: string) => {
|
||||
queryParams.userId = val.userId
|
||||
queryParams.no = no
|
||||
const res = await getOrderPage(queryParams)
|
||||
total.value = res.total
|
||||
list.value = res.list
|
||||
@ -38,7 +40,8 @@ const loadMore = async () => {
|
||||
queryParams.pageNo += 1
|
||||
const res = await getOrderPage(queryParams)
|
||||
total.value = res.total
|
||||
concat(list.value, res.list)
|
||||
// concat(list.value, res.list)
|
||||
list.value = list.value.concat(res.list);
|
||||
}
|
||||
defineExpose({ getHistoryList, loadMore })
|
||||
</script>
|
||||
|
@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<OrderItems v-for="item in list" :key="item.id" :order="item" class="mb-10px" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import OrderItems from '@/views/mall/promotion/kefu/components/message/OrderItems.vue'
|
||||
import { KeFuConversationRespVO } from '@/api/mall/promotion/kefu/conversation'
|
||||
import { getOrderPage } from '@/api/mall/trade/order'
|
||||
import { concat } from 'lodash-es'
|
||||
|
||||
defineOptions({ name: 'OrderBrowsingHistorys' })
|
||||
|
||||
const list = ref<any>([]) // 列表
|
||||
const total = ref(0) // 总数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
userId: 0,
|
||||
activeName: 'a',
|
||||
no: '',
|
||||
})
|
||||
const skipGetMessageList = computed(() => {
|
||||
// 已加载到最后一页的话则不触发新的消息获取
|
||||
return total.value > 0 && Math.ceil(total.value / queryParams.pageSize) === queryParams.pageNo
|
||||
}) // 跳过消息获取
|
||||
|
||||
/** 获得浏览记录 */
|
||||
const getHistoryList = async (val: KeFuConversationRespVO,activeName: string,no: string) => {
|
||||
queryParams.userId = val.userId
|
||||
queryParams.activeName = activeName
|
||||
queryParams.no = no
|
||||
queryParams.pageNo = 1
|
||||
const res = await getOrderPage(queryParams)
|
||||
total.value = res.total
|
||||
list.value = res.list
|
||||
}
|
||||
|
||||
/** 加载下一页数据 */
|
||||
const loadMore = async () => {
|
||||
if (skipGetMessageList.value) {
|
||||
return
|
||||
}
|
||||
queryParams.pageNo += 1
|
||||
const res = await getOrderPage(queryParams)
|
||||
total.value = res.total
|
||||
// concat(list.value, res.list)
|
||||
list.value = list.value.concat(res.list);
|
||||
}
|
||||
defineExpose({ getHistoryList, loadMore })
|
||||
</script>
|
@ -27,7 +27,8 @@ const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
userId: 0,
|
||||
userDeleted: false
|
||||
userDeleted: false,
|
||||
goodsName: ''
|
||||
})
|
||||
const skipGetMessageList = computed(() => {
|
||||
// 已加载到最后一页的话则不触发新的消息获取
|
||||
@ -35,8 +36,9 @@ const skipGetMessageList = computed(() => {
|
||||
}) // 跳过消息获取
|
||||
|
||||
/** 获得浏览记录 */
|
||||
const getHistoryList = async (val: KeFuConversationRespVO) => {
|
||||
const getHistoryList = async (val: KeFuConversationRespVO,goodsName: string) => {
|
||||
queryParams.userId = val.userId
|
||||
queryParams.goodsName = goodsName
|
||||
const res = await getBrowseHistoryPage(queryParams)
|
||||
total.value = res.total
|
||||
list.value = res.list
|
||||
@ -50,7 +52,8 @@ const loadMore = async () => {
|
||||
queryParams.pageNo += 1
|
||||
const res = await getBrowseHistoryPage(queryParams)
|
||||
total.value = res.total
|
||||
concat(list.value, res.list)
|
||||
// concat(list.value, res.list)
|
||||
list.value = list.value.concat(res.list);
|
||||
}
|
||||
defineExpose({ getHistoryList, loadMore })
|
||||
</script>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import KeFuConversationList from './KeFuConversationList.vue'
|
||||
import KeFuMessageList from './KeFuMessageList.vue'
|
||||
import MemberBrowsingHistory from './history/MemberBrowsingHistory.vue'
|
||||
import UserInfo from './UserInfo.vue'
|
||||
import MemberBrowsingHistorys from './history/MemberBrowsingHistorys.vue'
|
||||
|
||||
export { KeFuConversationList, KeFuMessageList, MemberBrowsingHistory ,UserInfo}
|
||||
export { KeFuConversationList, KeFuMessageList, MemberBrowsingHistory ,MemberBrowsingHistorys}
|
||||
|
@ -0,0 +1,182 @@
|
||||
<template>
|
||||
<div v-if="isObject(getMessageContent)">
|
||||
<div :key="getMessageContent.id" class="order-list-card-box mt-14px">
|
||||
<div class="order-card-header flex items-center justify-between p-x-5px">
|
||||
<div class="order-no">
|
||||
订单号:
|
||||
<span style="cursor: pointer" @click="openDetail(getMessageContent.id)">
|
||||
{{ getMessageContent.no }}
|
||||
</span>
|
||||
</div>
|
||||
<div :class="formatOrderColor(getMessageContent)" class="order-state font-16">
|
||||
{{ formatOrderStatus(getMessageContent) }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-for="item in getMessageContent.items" :key="item.id" class="border-bottom">
|
||||
<ProductItem
|
||||
:spu-id="item.spuId"
|
||||
:num="item.count"
|
||||
:picUrl="item.picUrl"
|
||||
:price="item.price"
|
||||
:skuText="item.properties.map((property: any) => property.valueName).join(' ')"
|
||||
:title="item.spuName"
|
||||
/>
|
||||
</div>
|
||||
<div class="pay-box flex justify-end pr-5px">
|
||||
<div class="flex items-center">
|
||||
<div class="discounts-title pay-color"
|
||||
>共 {{ getMessageContent?.productCount }} 件商品,总金额:
|
||||
</div>
|
||||
<div class="discounts-money pay-color">
|
||||
¥{{ fenToYuan(getMessageContent?.payPrice) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { fenToYuan, jsonParse } from '@/utils'
|
||||
import { KeFuMessageRespVO } from '@/api/mall/promotion/kefu/message'
|
||||
import { isObject } from '@/utils/is'
|
||||
import ProductItem from '@/views/mall/promotion/kefu/components/message/ProductItem.vue'
|
||||
|
||||
const { push } = useRouter()
|
||||
|
||||
defineOptions({ name: 'OrderItems' })
|
||||
const props = defineProps<{
|
||||
message?: KeFuMessageRespVO
|
||||
order?: any
|
||||
}>()
|
||||
|
||||
const getMessageContent = computed(() =>
|
||||
typeof props.message !== 'undefined' ? jsonParse(props!.message!.content) : props.order
|
||||
)
|
||||
|
||||
/** 查看订单详情 */
|
||||
const openDetail = (id: number) => {
|
||||
console.log(getMessageContent)
|
||||
push({ name: 'TradeOrderDetail', params: { id } })
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化订单状态的颜色
|
||||
*
|
||||
* @param order 订单
|
||||
* @return {string} 颜色的 class 名称
|
||||
*/
|
||||
function formatOrderColor(order: any) {
|
||||
if (order.status === 0) {
|
||||
return 'info-color'
|
||||
}
|
||||
if (order.status === 10 || order.status === 20 || (order.status === 30 && !order.commentStatus)) {
|
||||
return 'warning-color'
|
||||
}
|
||||
if (order.status === 30 && order.commentStatus) {
|
||||
return 'success-color'
|
||||
}
|
||||
return 'danger-color'
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化订单状态
|
||||
*
|
||||
* @param order 订单
|
||||
*/
|
||||
function formatOrderStatus(order: any) {
|
||||
if (order.status === 0) {
|
||||
return '待付款'
|
||||
}
|
||||
if (order.status === 10 && order.deliveryType === 1) {
|
||||
return '待发货'
|
||||
}
|
||||
if (order.status === 10 && order.deliveryType === 2) {
|
||||
return '待核销'
|
||||
}
|
||||
if (order.status === 20) {
|
||||
return '待收货'
|
||||
}
|
||||
if (order.status === 30 && !order.commentStatus) {
|
||||
return '待评价'
|
||||
}
|
||||
if (order.status === 30 && order.commentStatus) {
|
||||
return '已完成'
|
||||
}
|
||||
if (order.status === 100) {
|
||||
return '退款中'
|
||||
}
|
||||
return '已关闭'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.order-list-card-box {
|
||||
border-radius: 10px;
|
||||
padding: 10px;
|
||||
border: 1px var(--el-border-color) solid;
|
||||
background-color: var(--app-content-bg-color);
|
||||
|
||||
.order-card-header {
|
||||
height: 28px;
|
||||
|
||||
.order-no {
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
|
||||
span {
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
color: var(--left-menu-bg-active-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pay-box {
|
||||
padding-top: 10px;
|
||||
|
||||
.discounts-title {
|
||||
font-size: 16px;
|
||||
line-height: normal;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.discounts-money {
|
||||
font-size: 16px;
|
||||
line-height: normal;
|
||||
color: #999;
|
||||
font-family: OPPOSANS;
|
||||
}
|
||||
|
||||
.pay-color {
|
||||
font-size: 13px;
|
||||
color: var(--left-menu-text-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.warning-color {
|
||||
color: #faad14;
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.danger-color {
|
||||
color: #ff3000;
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.success-color {
|
||||
color: #52c41a;
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.info-color {
|
||||
color: #999999;
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
@ -3,7 +3,7 @@
|
||||
<el-popover :width="500" placement="top" trigger="click">
|
||||
<template #reference>
|
||||
<!-- <Icon :size="30" class="ml-10px cursor-pointer" icon="twemoji:grinning-face" /> -->
|
||||
<img :src="biaoqing" style="margin-left: 10px;" class="w-23px h-23px" />
|
||||
<img :src="biaoqing" class="w-23px h-25px" />
|
||||
</template>
|
||||
<ElScrollbar height="300px">
|
||||
<ul class="ml-2 flex flex-wrap px-2">
|
||||
@ -27,7 +27,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
defineOptions({ name: 'EmojiSelectPopover' })
|
||||
import biaoqing from '@/views/mall/promotion/kefu/components/asserts/biaoqian.png'
|
||||
import biaoqing from '@/views/mall/promotion/kefu/components/asserts/biaoqing.png'
|
||||
import { Emoji, useEmoji } from './emoji'
|
||||
|
||||
|
||||
|
@ -1,51 +1,58 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- 新区域,放在头部 -->
|
||||
<el-row style="display: flex; justify-content: center;">
|
||||
|
||||
<el-row style="display: flex; justify-content: center;">
|
||||
|
||||
<el-col :span="24">
|
||||
<div style="width:100%;height:70px;background-color:#3c80ff;">
|
||||
<div style="width:100%;height:68px;background-color:#3c80ff;">
|
||||
<el-row>
|
||||
<el-col :span="6">
|
||||
<el-input
|
||||
style="width: 80%;margin-top: 20px;margin-left:10px;"
|
||||
:suffix-icon="Search"
|
||||
/>
|
||||
<el-input @input="findNameInput" v-model="findName"
|
||||
style="width: 80%;margin-top: 20px;margin-left:23px;"
|
||||
:suffix-icon="Search"/>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="6">
|
||||
<span style="display: flex; margin-top: 15px;">
|
||||
<el-avatar
|
||||
:src="pic"
|
||||
/>
|
||||
<span style="margin-left:5px;margin-top: 9px;">{{name}}</span>
|
||||
<!-- <el-switch
|
||||
style="margin-top: 4px;--el-switch-on-color: #13ce66; --el-switch-off-color: #b6bac1;"
|
||||
v-model="value6"
|
||||
class="ml-2"
|
||||
width="60"
|
||||
inline-prompt
|
||||
active-text="在线"
|
||||
inactive-text="下线"
|
||||
/> -->
|
||||
</span>
|
||||
<el-col :span="1">
|
||||
<el-avatar style=" margin-top: 15px;" :src="pic"/>
|
||||
<!-- <span style="display: flex; margin-top: 15px;">
|
||||
|
||||
<span style="margin-left:5px;margin-top: 9px;">{{name}}</span>
|
||||
<el-switch
|
||||
style="margin-top: 4px;--el-switch-on-color: #13ce66; --el-switch-off-color: #b6bac1;"
|
||||
v-model="lineStatus" class="ml-2" width="60" inline-prompt active-text="在线"
|
||||
inactive-text="下线" @change="handleSwitchChange"/>
|
||||
</span> -->
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-button @click="out" size="small" round style="margin-top:23px;margin-left:75%">退出</el-button>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="6">
|
||||
<el-menu
|
||||
background-color="#3c80ff"
|
||||
text-color="white"
|
||||
active-text-color="white"
|
||||
style="width:100%;display: flex;"
|
||||
>
|
||||
<el-menu-item @click="userInfo" style="width:33%;height:70px" index="1">客户信息</el-menu-item>
|
||||
<el-menu-item @click="zuoji" style="width:33%;height:70px" index="2">他的足迹</el-menu-item>
|
||||
<!-- <el-menu-item style="width:34%;height:70px" index="3">商品信息</el-menu-item> -->
|
||||
</el-menu>
|
||||
<el-col :span="7">
|
||||
|
||||
<span style="display: flex; margin-top: 15px;">
|
||||
<!-- <el-avatar :src="pic"/> -->
|
||||
<span style="margin-left:5px;margin-top: 9px;">{{name}}</span>
|
||||
<el-switch
|
||||
style="margin-top: 4px;--el-switch-on-color: #13ce66; --el-switch-off-color: #b6bac1;"
|
||||
v-model="lineStatus" class="ml-2" width="60" inline-prompt active-text="在线"
|
||||
inactive-text="下线" @change="handleSwitchChange"/>
|
||||
</span>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="4">
|
||||
<el-button @click="out" size="small" round
|
||||
style="margin-top:23px;margin-left:72%">退出
|
||||
</el-button>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="6">
|
||||
<el-menu background-color="#3c80ff" text-color="white" active-text-color="white"
|
||||
style="width:100%;display: flex;">
|
||||
<el-menu-item @click="userInfo" style="width:33%;height:70px" index="1">客户信息
|
||||
</el-menu-item>
|
||||
<el-menu-item @click="jiaoyi" style="width:33%;height:70px" index="3">交易订单
|
||||
</el-menu-item>
|
||||
<el-menu-item @click="zuoji" style="width:33%;height:70px" index="2">商品信息
|
||||
</el-menu-item>
|
||||
<!-- <el-menu-item style="width:34%;height:70px" index="3">商品信息</el-menu-item> -->
|
||||
</el-menu>
|
||||
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
@ -64,62 +71,78 @@
|
||||
|
||||
<!-- 会话详情(选中会话的消息列表) -->
|
||||
<el-col :span="12">
|
||||
<ContentWrap>
|
||||
<KeFuMessageList ref="keFuChatBoxRef" @change="getConversationList"/>
|
||||
</ContentWrap>
|
||||
|
||||
<KeFuMessageList ref="keFuChatBoxRef" @change="getConversationList"/>
|
||||
|
||||
</el-col>
|
||||
|
||||
<!-- 会员足迹(选中会话的会员足迹) -->
|
||||
<el-col :span="6">
|
||||
<ContentWrap v-show="chick == '2'">
|
||||
|
||||
<ContentWrap v-show="chick == '1' && clickUser == 2">
|
||||
<div style="height: 522px ;">
|
||||
<div>
|
||||
<span style="display: flex;">
|
||||
<el-avatar style="border: 1px solid #f8f9ee;" :src="user.avatar"/>
|
||||
<span style="margin-left:5px;margin-top: 9px;">{{user.nickname}}</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<el-divider style="border-color: #f5f5f5;margin-top: 15px;margin-bottom:15px;"/>
|
||||
<div>
|
||||
<span style="color: #5d5d59;font-size: 13px ;">手机号</span><span
|
||||
style="margin-left: 47px;font-size: 14px ;">{{user.mobile}}</span>
|
||||
</div>
|
||||
<div style="margin-top: 5px;">
|
||||
<span style="color: #5d5d59;font-size: 13px ;">分组</span><span
|
||||
style="margin-left: 60px;font-size: 14px ;">{{user.groupName}}</span>
|
||||
</div>
|
||||
<div style="margin-top: 5px;">
|
||||
<span style="color: #5d5d59;font-size: 13px ;">用户标签</span><span
|
||||
style="margin-left: 33px;font-size: 14px ;">客户</span>
|
||||
</div>
|
||||
<el-divider style="border-color: #f5f5f5;margin-top: 15px;margin-bottom:15px;"/>
|
||||
|
||||
<div>
|
||||
<span style="color: #5d5d59;font-size: 13px ;">用户等级</span><span
|
||||
style="margin-left: 35px;font-size: 14px ;">{{user.levelName}}</span>
|
||||
</div>
|
||||
<div style="margin-top: 5px;">
|
||||
<span style="color: #5d5d59;font-size: 13px ;">推荐人</span><span
|
||||
style="margin-left: 47px;font-size: 14px ;">客户</span>
|
||||
</div>
|
||||
<!-- <div style="margin-top: 5px;">
|
||||
<span style="color: #5d5d59;font-size: 13px ;">用户类型</span><span
|
||||
style="margin-left: 33px;font-size: 14px ;">小客户</span>
|
||||
</div> -->
|
||||
<div style="margin-top: 5px;">
|
||||
<span style="color: #5d5d59;font-size: 13px ;">积分</span><span
|
||||
style="margin-left: 60px;font-size: 14px ;">{{user.point}}</span>
|
||||
</div>
|
||||
<div style="margin-top: 5px;">
|
||||
<span style="color: #5d5d59;font-size: 13px ;">推广员</span><span
|
||||
style="margin-left: 47px;font-size: 14px ;">客户</span>
|
||||
</div>
|
||||
<div style="margin-top: 5px;">
|
||||
<span style="color: #5d5d59;font-size: 13px ;">生日</span><span
|
||||
style="margin-left: 60px;font-size: 14px ;">{{user.birthday}}</span>
|
||||
</div>
|
||||
<el-divider style="border-color: #f5f5f5;margin-top: 15px;margin-bottom:15px;"/>
|
||||
</div>
|
||||
</ContentWrap>
|
||||
|
||||
<ContentWrap v-show="chick == '1' && clickUser == 1">
|
||||
<el-empty description="请选择左侧的一个会话后开始" />
|
||||
</ContentWrap>
|
||||
|
||||
<ContentWrap v-show="chick == '2'">
|
||||
<MemberBrowsingHistory ref="memberBrowsingHistoryRef"/>
|
||||
</ContentWrap>
|
||||
|
||||
<ContentWrap v-show = "chick == '1'">
|
||||
<div style="height: 522px ;" >
|
||||
<div>
|
||||
<span style="display: flex;">
|
||||
<el-avatar
|
||||
style="border: 1px solid #f8f9ee;"
|
||||
:src="user.avatar"
|
||||
/>
|
||||
<span style="margin-left:5px;margin-top: 9px;">{{user.nickname}}</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<el-divider style="border-color: #f5f5f5;margin-top: 15px;margin-bottom:15px;" />
|
||||
<div>
|
||||
<span style="color: #5d5d59;font-size: 13px ;">手机号</span><span style="margin-left: 47px;font-size: 14px ;">{{user.mobile}}</span>
|
||||
</div>
|
||||
<div style="margin-top: 5px;">
|
||||
<span style="color: #5d5d59;font-size: 13px ;">分组</span><span style="margin-left: 60px;font-size: 14px ;">{{user.groupName}}</span>
|
||||
</div>
|
||||
<div style="margin-top: 5px;">
|
||||
<span style="color: #5d5d59;font-size: 13px ;">用户标签</span><span style="margin-left: 33px;font-size: 14px ;">小客户</span>
|
||||
</div>
|
||||
<el-divider style="border-color: #f5f5f5;margin-top: 15px;margin-bottom:15px;" />
|
||||
|
||||
<div>
|
||||
<span style="color: #5d5d59;font-size: 13px ;">用户等级</span><span style="margin-left: 35px;font-size: 14px ;">{{user.levelName}}</span>
|
||||
</div>
|
||||
<div style="margin-top: 5px;">
|
||||
<span style="color: #5d5d59;font-size: 13px ;">推荐人</span><span style="margin-left: 47px;font-size: 14px ;">客户</span>
|
||||
</div>
|
||||
<div style="margin-top: 5px;">
|
||||
<span style="color: #5d5d59;font-size: 13px ;">用户类型</span><span style="margin-left: 33px;font-size: 14px ;">小客户</span>
|
||||
</div>
|
||||
<div style="margin-top: 5px;">
|
||||
<span style="color: #5d5d59;font-size: 13px ;">积分</span><span style="margin-left: 60px;font-size: 14px ;">{{user.point}}</span>
|
||||
</div>
|
||||
<div style="margin-top: 5px;">
|
||||
<span style="color: #5d5d59;font-size: 13px ;">推广员</span><span style="margin-left: 47px;font-size: 14px ;">客户</span>
|
||||
</div>
|
||||
<div style="margin-top: 5px;">
|
||||
<span style="color: #5d5d59;font-size: 13px ;">生日</span><span style="margin-left: 60px;font-size: 14px ;">{{user.birthday}}</span>
|
||||
</div>
|
||||
<el-divider style="border-color: #f5f5f5;margin-top: 15px;margin-bottom:15px;" />
|
||||
</div>
|
||||
<ContentWrap v-show="chick == '3'">
|
||||
<MemberBrowsingHistorys ref="memberBrowsingHistorysRef"/>
|
||||
</ContentWrap>
|
||||
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
@ -127,122 +150,127 @@
|
||||
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { KeFuConversationList, KeFuMessageList,MemberBrowsingHistory,UserInfo } from './components'
|
||||
import {
|
||||
KeFuConversationList,
|
||||
KeFuMessageList,
|
||||
MemberBrowsingHistorys,
|
||||
MemberBrowsingHistory
|
||||
|
||||
} from './components'
|
||||
import {WebSocketMessageTypeConstants} from './components/tools/constants'
|
||||
import { KeFuMessageApi, KeFuMessageRespVO } from '@/api/mall/promotion/kefu/message'
|
||||
import {KeFuConversationRespVO} from '@/api/mall/promotion/kefu/conversation'
|
||||
import {getRefreshToken, getAccessToken} from '@/utils/auth'
|
||||
import {useWebSocket} from '@vueuse/core'
|
||||
import {Search} from '@element-plus/icons-vue'
|
||||
import * as UserApi from '@/api/member/user'
|
||||
|
||||
|
||||
import {SupportStaffApi, SupportStaffVO} from '@/api/mall/promotion/supportstaff'
|
||||
import {string} from 'vue-types'
|
||||
import type { TabsPaneContext } from 'element-plus'
|
||||
const findName = ref('')
|
||||
|
||||
defineOptions({name: 'KeFu'})
|
||||
|
||||
const lineStatus = ref(true)
|
||||
|
||||
const clickUser = ref(1)
|
||||
|
||||
const value6 = ref(true)
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const name = params.get('name');
|
||||
const pic = params.get('pic');
|
||||
const kefuId = params.get('id')
|
||||
const conversations = ref<KeFuConversationRespVO[]>([])
|
||||
// const userInfoRef = ref<InstanceType<typeof UserInfo>>()
|
||||
const user = ref<UserApi.UserVO>({} as UserApi.UserVO)
|
||||
const userId = ref(0)
|
||||
|
||||
const stat = ref(false)
|
||||
|
||||
// ======================= WebSocket start =======================
|
||||
const server = ref(
|
||||
(import.meta.env.VITE_BASE_URL + '/infra/ws').replace('http', 'ws') +
|
||||
'?token=' +
|
||||
getRefreshToken() // 使用 getRefreshToken() 方法,而不使用 getAccessToken() 方法的原因:WebSocket 无法方便的刷新访问令牌
|
||||
) // WebSocket 服务地址
|
||||
|
||||
const server = ref(
|
||||
(import.meta.env.VITE_BASE_URL + '/infra/ws').replace('http', 'ws') +
|
||||
'?token=' + getRefreshToken()
|
||||
// getAccessToken()
|
||||
// 使用 getRefreshToken() 方法,而不使用 getAccessToken() 方法的原因:WebSocket 无法方便的刷新访问令牌
|
||||
) // WebSocket 服务地址
|
||||
|
||||
let a = 0;
|
||||
|
||||
const {status, data, send, open, close} = useWebSocket(server.value, {
|
||||
onConnected: function (ws) {
|
||||
console.log('websocket 连接成功!', ws);
|
||||
},
|
||||
onDisconnected: function (ws, event) {
|
||||
console.log('WebSocket 连接断开', event);
|
||||
},
|
||||
onError: function (ws, event) {
|
||||
console.error('WebSocket 连接错误:', event);
|
||||
if (event instanceof ErrorEvent) {
|
||||
console.error('详细错误信息:', event.message);
|
||||
} else {
|
||||
console.error('非标准错误:', event);
|
||||
}
|
||||
},
|
||||
onMessage: function (ws, event) {
|
||||
console.log('收到的 WebSocket 消息:', event.data);
|
||||
a = a + 1 ;
|
||||
if(a == 2){
|
||||
getConversationList()
|
||||
keFuChatBoxRef.value?.refreshMessageList()
|
||||
a = 0;
|
||||
}
|
||||
|
||||
// 如果收到的数据存在
|
||||
// if (event.data) {
|
||||
// let parsedData;
|
||||
|
||||
// // 尝试解析消息
|
||||
// try {
|
||||
// parsedData = JSON.parse(event.data);
|
||||
// } catch (error) {
|
||||
// console.error('JSON 解析错误:', error, '原始数据:', event.data);
|
||||
// return; // 如果解析失败,跳过后续处理
|
||||
// }
|
||||
|
||||
// // 根据消息的 type 类型来处理不同的消息
|
||||
// const { type } = parsedData;
|
||||
// if (!type) {
|
||||
// console.warn('收到未定义类型的消息:', parsedData);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// // 示例:处理特定类型的消息
|
||||
// switch (type) {
|
||||
// case 'pong':
|
||||
// // 处理心跳消息
|
||||
// console.log('收到心跳 ping 消息');
|
||||
// // 如果需要回复 pong,可以在这里发送消息
|
||||
// send(JSON.stringify({ type: 'pong' }));
|
||||
// break;
|
||||
|
||||
// case 'chat':
|
||||
// // 处理聊天消息
|
||||
// console.log('收到聊天消息:', parsedData.content);
|
||||
// // 处理聊天消息的逻辑
|
||||
// break;
|
||||
|
||||
// case 'notification':
|
||||
// // 处理通知消息
|
||||
// console.log('收到通知消息:', parsedData.message);
|
||||
// // 执行通知操作
|
||||
// break;
|
||||
|
||||
// default:
|
||||
// console.warn('收到未知类型的消息:', type);
|
||||
// break;
|
||||
// }
|
||||
// } else {
|
||||
// console.warn('接收到空数据:', event);
|
||||
// }
|
||||
},
|
||||
autoReconnect: false, // 开启自动重连
|
||||
heartbeat: true
|
||||
});
|
||||
|
||||
|
||||
|
||||
const handleSwitchChange = async (value) => {
|
||||
console.log('11111:', value)
|
||||
let a = 0;
|
||||
|
||||
if (value == true) {
|
||||
a = 1;
|
||||
await SupportStaffApi.updateLineStatus(kefuId, a)
|
||||
message.success('已上线')
|
||||
} else {
|
||||
a = 2;
|
||||
await SupportStaffApi.updateLineStatus(kefuId, a)
|
||||
message.success('已下线')
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const shangxian = async () => {
|
||||
await SupportStaffApi.updateLineStatus(kefuId, 1)
|
||||
}
|
||||
|
||||
const xiaxian = async () => {
|
||||
await SupportStaffApi.updateLineStatus(kefuId, 2)
|
||||
}
|
||||
|
||||
let a = 0;
|
||||
|
||||
const {status, data, send, open, close} = useWebSocket(server.value, {
|
||||
onConnected: function (ws) {
|
||||
shangxian(); //连接成功上线
|
||||
console.log('websocket 连接成功!', ws);
|
||||
},
|
||||
onDisconnected: function (ws, event) {
|
||||
xiaxian(); //断开下线
|
||||
console.log('WebSocket 连接断开', event);
|
||||
},
|
||||
// onError: function (ws, event) {
|
||||
// console.error('WebSocket 连接错误:', event);
|
||||
// if (event instanceof ErrorEvent) {
|
||||
// console.error('详细错误信息:', event.message);
|
||||
// } else {
|
||||
// console.error('非标准错误:', event);
|
||||
// }
|
||||
// },
|
||||
onMessage: function (ws, event) {
|
||||
// console.log('收到的 WebSocket 消息:', event.data);
|
||||
|
||||
a = a + 1;
|
||||
if (a == 2) {
|
||||
getConversationList()
|
||||
if(userId.value != 0){
|
||||
getBySenderIdStat()
|
||||
if(stat.value){
|
||||
keFuChatBoxRef.value?.refreshMessageList()
|
||||
}
|
||||
}
|
||||
|
||||
a = 0;
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
autoReconnect: true, // 开启自动重连
|
||||
heartbeat: true
|
||||
});
|
||||
|
||||
// const { data, close, open } = useWebSocket(server.value, {
|
||||
// autoReconnect: true,
|
||||
// heartbeat: true
|
||||
// })
|
||||
|
||||
|
||||
/** 监听 WebSocket 数据 */
|
||||
watchEffect(() => {
|
||||
console.log('连接服务器得到消息:', data.value)
|
||||
if (!data.value) {
|
||||
return
|
||||
}
|
||||
@ -251,16 +279,19 @@
|
||||
if (data.value === 'pong') {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// 2.1 解析 type 消息类型
|
||||
const jsonMessage = JSON.parse(data.value)
|
||||
const type = jsonMessage.type
|
||||
|
||||
|
||||
if (!type) {
|
||||
message.error('未知的消息类型:' + data.value)
|
||||
return
|
||||
}
|
||||
// 2.2 消息类型:KEFU_MESSAGE_TYPE
|
||||
if (type === WebSocketMessageTypeConstants.KEFU_MESSAGE_TYPE) {
|
||||
console.log('来自用户发送的消息:', JSON.parse(jsonMessage.content))
|
||||
// 刷新会话列表
|
||||
// TODO @puhui999:不应该刷新列表,而是根据消息,本地 update 列表的数据;
|
||||
getConversationList()
|
||||
@ -279,40 +310,64 @@
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
// ======================= WebSocket end =======================
|
||||
/** 加载会话列表 */
|
||||
const keFuConversationRef = ref<InstanceType<typeof KeFuConversationList>>()
|
||||
const getConversationList = () => {
|
||||
keFuConversationRef.value?.getConversationList()
|
||||
keFuConversationRef.value?.getConversationList(findName.value)
|
||||
}
|
||||
|
||||
/** 加载指定会话的消息列表 */
|
||||
const keFuChatBoxRef = ref<InstanceType<typeof KeFuMessageList>>()
|
||||
const memberBrowsingHistoryRef = ref<InstanceType<typeof MemberBrowsingHistory>>()
|
||||
const handleChange = (conversation: KeFuConversationRespVO) => {
|
||||
conversations.value = conversation
|
||||
chick.value = '2'
|
||||
userId.value = conversation.userId
|
||||
const memberBrowsingHistorysRef = ref<InstanceType<typeof MemberBrowsingHistorys>>()
|
||||
const handleChange = async (conversation: KeFuConversationRespVO) => {
|
||||
conversations.value = conversation
|
||||
// chick.value = '1'
|
||||
clickUser.value = 2
|
||||
userId.value = conversation.userId
|
||||
userInfo()
|
||||
keFuChatBoxRef.value?.getNewMessageList(conversation)
|
||||
memberBrowsingHistoryRef.value?.initHistory(conversation)
|
||||
memberBrowsingHistorysRef.value?.initHistory(conversation)
|
||||
}
|
||||
|
||||
const out = async () => {
|
||||
// await SupportStaffApi.updateLineStatus(kefuId, 2)
|
||||
window.close();
|
||||
// window.location.href = '/kefu/support-staff';
|
||||
}
|
||||
|
||||
const out = () =>{
|
||||
window.close();
|
||||
// window.location.href = '/kefu/support-staff';
|
||||
const getBySenderIdStat = async () => {
|
||||
stat.value = await KeFuMessageApi.getBySenderIdStat(userId.value)
|
||||
}
|
||||
|
||||
|
||||
const chick = ref('1')
|
||||
const userInfo = async () => {
|
||||
chick.value = '1'
|
||||
if(clickUser.value == 2){
|
||||
user.value = await UserApi.getUserInfo(userId.value)
|
||||
}
|
||||
|
||||
}
|
||||
const zuoji = () => {
|
||||
chick.value = '2'
|
||||
// keFuChatBoxRef.value?.getNewMessageList(conversations.value)
|
||||
memberBrowsingHistoryRef.value?.initHistory(conversations.value)
|
||||
}
|
||||
const jiaoyi = () => {
|
||||
chick.value = '3'
|
||||
// keFuChatBoxRef.value?.getNewMessageList(conversations.value)
|
||||
// memberBrowsingHistoryRef.value?.initHistory(conversations.value)
|
||||
}
|
||||
|
||||
|
||||
const chick = ref('2')
|
||||
const userInfo = async () =>{
|
||||
chick.value = '1'
|
||||
user.value = await UserApi.getUserInfo(userId.value)
|
||||
}
|
||||
const zuoji = () =>{
|
||||
chick.value = '2'
|
||||
// keFuChatBoxRef.value?.getNewMessageList(conversations.value)
|
||||
memberBrowsingHistoryRef.value?.initHistory(conversations.value)
|
||||
|
||||
const findNameInput = () => {
|
||||
keFuConversationRef.value?.getConversationList(findName.value)
|
||||
}
|
||||
|
||||
/** 初始化 */
|
||||
@ -320,21 +375,22 @@
|
||||
getConversationList()
|
||||
// 打开 websocket 连接
|
||||
open()
|
||||
console.log('WebSocket 已初始化');
|
||||
console.log('WebSocket 已初始化');
|
||||
})
|
||||
|
||||
/** 销毁 */
|
||||
onBeforeUnmount(() => {
|
||||
// 关闭 websocket 连接
|
||||
close()
|
||||
console.log('WebSocket 已关闭');
|
||||
})
|
||||
// /** 销毁 */
|
||||
// onBeforeUnmount(() => {
|
||||
// // 关闭 websocket 连接
|
||||
// close()
|
||||
// console.log('WebSocket 已关闭');
|
||||
// })
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.kefu {
|
||||
height: calc(100vh - 165px);
|
||||
overflow: auto; /* 确保内容可滚动 */
|
||||
overflow: auto;
|
||||
/* 确保内容可滚动 */
|
||||
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="登录账号" prop="account">
|
||||
<!-- <el-form-item label="登录账号" prop="account">
|
||||
<el-input
|
||||
v-model="queryParams.account"
|
||||
placeholder="请输入登录账号"
|
||||
@ -34,7 +34,7 @@
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form-item> -->
|
||||
<el-form-item label="客服状态" prop="status">
|
||||
<el-select
|
||||
v-model="queryParams.status"
|
||||
@ -70,7 +70,7 @@
|
||||
@click="openForm('create')"
|
||||
v-hasPermi="['promotion:support-staff:create']"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增客服
|
||||
</el-button>
|
||||
<el-button
|
||||
type="success"
|
||||
@ -247,7 +247,7 @@ const handleDelete = async (id: number) => {
|
||||
/** 客服进入工作台 */
|
||||
const handleEnterConsole = async (id: number,name: string,pic: string) => {
|
||||
setStaffToken(id);
|
||||
const url = `${window.location.origin}/kefu/kefu?name=${encodeURIComponent(name)}&pic=${encodeURIComponent(pic)}`;
|
||||
const url = `${window.location.origin}/kefu/kefu?id=${encodeURIComponent(id)}&name=${encodeURIComponent(name)}&pic=${encodeURIComponent(pic)}`;
|
||||
window.open(url, '_blank');
|
||||
}
|
||||
/** 导出按钮操作 */
|
||||
|
@ -7,7 +7,7 @@
|
||||
label-width="100px"
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<el-form-item label="分类" prop="type">
|
||||
<el-form-item label="话术分类" prop="type">
|
||||
<el-select v-model="formData.type" placeholder="请选择分类">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.KEFU_VERBAL_TRICK_TYPE)"
|
||||
@ -17,10 +17,10 @@
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="标题" prop="title">
|
||||
<el-form-item label="话术标题" prop="title">
|
||||
<el-input v-model="formData.title" placeholder="请输入标题" />
|
||||
</el-form-item>
|
||||
<el-form-item label="详情" prop="details">
|
||||
<el-form-item label="话术内容" prop="details">
|
||||
<el-input v-model="formData.details" type="textarea" placeholder="请输入详情" :rows="6" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
@ -6,7 +6,7 @@
|
||||
<el-button type="primary" plain @click="createType" style="width: 90;font-size: 12px;">
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增分类
|
||||
</el-button>
|
||||
<el-menu :default-active="targetMenuId" style="width:183px">
|
||||
<el-menu :default-active="targetMenuId" style="width:209px">
|
||||
<el-menu-item v-for="item in huashuType" :index="item.value" :key="item.value"
|
||||
@click="clickMenu(item.value)">{{item.label}}</el-menu-item>
|
||||
</el-menu>
|
||||
@ -14,9 +14,7 @@
|
||||
|
||||
<!-- 内容区域 -->
|
||||
<div class="content-wrap">
|
||||
|
||||
|
||||
<ContentWrap>
|
||||
<div style="margin-top: 25px;margin-left:20px">
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
|
||||
<!-- <el-form-item label="分类" prop="type">
|
||||
@ -25,7 +23,7 @@
|
||||
:key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item> -->
|
||||
<el-form-item label="标题" prop="title">
|
||||
<!-- <el-form-item label="标题" prop="title">
|
||||
<el-input v-model="queryParams.title" placeholder="请输入标题" clearable @keyup.enter="handleQuery"
|
||||
class="!w-240px" />
|
||||
</el-form-item>
|
||||
@ -33,28 +31,30 @@
|
||||
<el-date-picker v-model="queryParams.createTime" value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange" start-placeholder="开始日期" end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" class="!w-240px" />
|
||||
</el-form-item>
|
||||
</el-form-item> -->
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery">
|
||||
<!-- <el-button @click="handleQuery">
|
||||
<Icon icon="ep:search" class="mr-5px" /> 搜索
|
||||
</el-button>
|
||||
<el-button @click="resetQuery">
|
||||
<Icon icon="ep:refresh" class="mr-5px" /> 重置
|
||||
</el-button>
|
||||
</el-button> -->
|
||||
<el-button type="primary" plain @click="openForm('create')"
|
||||
v-hasPermi="['promotion:verbal-trick:create']">
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 添加话术
|
||||
</el-button>
|
||||
<el-button type="success" plain @click="handleExport" :loading="exportLoading"
|
||||
<!-- <el-button type="success" plain @click="handleExport" :loading="exportLoading"
|
||||
v-hasPermi="['promotion:verbal-trick:export']">
|
||||
<Icon icon="ep:download" class="mr-5px" /> 导出
|
||||
</el-button>
|
||||
</el-button> -->
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||
<el-table-column label="ID" align="center" prop="id" />
|
||||
<el-table-column label="分类" align="center" prop="type">
|
||||
@ -80,7 +80,7 @@
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 分页 -->
|
||||
<Pagination :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList" />
|
||||
@ -281,7 +281,7 @@
|
||||
padding: 25px;
|
||||
background-color: white;
|
||||
width: 180px;
|
||||
height: 1000px;
|
||||
height: 700px;
|
||||
/* 固定高度 */
|
||||
overflow-y: hidden;
|
||||
/* 禁止滚动 */
|
||||
@ -293,6 +293,7 @@
|
||||
flex-grow: 1;
|
||||
/* 使内容区域占据剩余空间 */
|
||||
overflow-y: auto;
|
||||
background-color: #ffffff;
|
||||
/* 允许内容区域滚动 */
|
||||
}
|
||||
</style>
|
@ -76,7 +76,8 @@ public class SecurityFrameworkUtils {
|
||||
if (authentication == null) {
|
||||
return null;
|
||||
}
|
||||
return authentication.getPrincipal() instanceof LoginUser ? (LoginUser) authentication.getPrincipal() : null;
|
||||
LoginUser loginUser = authentication.getPrincipal() instanceof LoginUser ? (LoginUser) authentication.getPrincipal() : null;
|
||||
return loginUser;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -38,9 +38,11 @@ public abstract class AbstractWebSocketMessageSender implements WebSocketMessage
|
||||
|
||||
@Override
|
||||
public void send(String sessionId, String messageType, String messageContent) {
|
||||
send(sessionId, null, null, messageType, messageContent);
|
||||
send(sessionId, (Integer) null,null, messageType, messageContent);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
*
|
||||
|
@ -37,6 +37,8 @@ public interface WebSocketMessageSender {
|
||||
*/
|
||||
void send(String sessionId, String messageType, String messageContent);
|
||||
|
||||
|
||||
|
||||
default void sendObject(Integer userType, Long userId, String messageType, Object messageContent) {
|
||||
send(userType, userId, messageType, JsonUtils.toJsonString(messageContent));
|
||||
}
|
||||
@ -49,4 +51,6 @@ public interface WebSocketMessageSender {
|
||||
send(sessionId, messageType, JsonUtils.toJsonString(messageContent));
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -1,10 +1,16 @@
|
||||
package cn.iocoder.yudao.framework.websocket.core.sender.redis;
|
||||
|
||||
import cn.iocoder.yudao.framework.mq.redis.core.RedisMQTemplate;
|
||||
import cn.iocoder.yudao.framework.security.core.LoginUser;
|
||||
import cn.iocoder.yudao.framework.websocket.core.sender.AbstractWebSocketMessageSender;
|
||||
import cn.iocoder.yudao.framework.websocket.core.sender.WebSocketMessageSender;
|
||||
import cn.iocoder.yudao.framework.websocket.core.session.WebSocketSessionManager;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* 基于 Redis 的 {@link WebSocketMessageSender} 实现类
|
||||
@ -16,6 +22,10 @@ public class RedisWebSocketMessageSender extends AbstractWebSocketMessageSender
|
||||
|
||||
private final RedisMQTemplate redisMQTemplate;
|
||||
|
||||
@Resource
|
||||
private WebSocketSessionManager webSocketSessionManager;
|
||||
|
||||
|
||||
public RedisWebSocketMessageSender(WebSocketSessionManager sessionManager,
|
||||
RedisMQTemplate redisMQTemplate) {
|
||||
super(sessionManager);
|
||||
@ -27,6 +37,7 @@ public class RedisWebSocketMessageSender extends AbstractWebSocketMessageSender
|
||||
sendRedisMessage(null, userId, userType, messageType, messageContent);
|
||||
}
|
||||
|
||||
//用户发
|
||||
@Override
|
||||
public void send(Integer userType, String messageType, String messageContent) {
|
||||
sendRedisMessage(null, null, userType, messageType, messageContent);
|
||||
@ -37,6 +48,8 @@ public class RedisWebSocketMessageSender extends AbstractWebSocketMessageSender
|
||||
sendRedisMessage(sessionId, null, null, messageType, messageContent);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 通过 Redis 广播消息
|
||||
*
|
||||
@ -48,6 +61,7 @@ public class RedisWebSocketMessageSender extends AbstractWebSocketMessageSender
|
||||
*/
|
||||
private void sendRedisMessage(String sessionId, Long userId, Integer userType,
|
||||
String messageType, String messageContent) {
|
||||
|
||||
RedisWebSocketMessage mqMessage = new RedisWebSocketMessage()
|
||||
.setSessionId(sessionId).setUserId(userId).setUserType(userType)
|
||||
.setMessageType(messageType).setMessageContent(messageContent);
|
||||
|
@ -3,6 +3,7 @@ package cn.iocoder.yudao.framework.websocket.core.session;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* {@link WebSocketSession} 管理器的接口
|
||||
@ -18,6 +19,7 @@ public interface WebSocketSessionManager {
|
||||
*/
|
||||
void addSession(WebSocketSession session);
|
||||
|
||||
ConcurrentMap<String, WebSocketSession> idSessions();
|
||||
/**
|
||||
* 移除 Session
|
||||
*
|
||||
@ -25,6 +27,8 @@ public interface WebSocketSessionManager {
|
||||
*/
|
||||
void removeSession(WebSocketSession session);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获得指定编号的 Session
|
||||
*
|
||||
|
@ -4,12 +4,12 @@ import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.security.core.LoginUser;
|
||||
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
|
||||
import cn.iocoder.yudao.framework.websocket.core.util.WebSocketFrameworkUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import java.net.URI;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
@ -19,6 +19,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
public class WebSocketSessionManagerImpl implements WebSocketSessionManager {
|
||||
|
||||
/**
|
||||
@ -28,6 +29,8 @@ public class WebSocketSessionManagerImpl implements WebSocketSessionManager {
|
||||
*/
|
||||
private final ConcurrentMap<String, WebSocketSession> idSessions = new ConcurrentHashMap<>();
|
||||
|
||||
private HttpSession httpSession;
|
||||
|
||||
/**
|
||||
* user 与 WebSocketSession 映射
|
||||
*
|
||||
@ -39,6 +42,7 @@ public class WebSocketSessionManagerImpl implements WebSocketSessionManager {
|
||||
|
||||
@Override
|
||||
public void addSession(WebSocketSession session) {
|
||||
|
||||
// 添加到 idSessions 中
|
||||
idSessions.put(session.getId(), session);
|
||||
// 添加到 userSessions 中
|
||||
@ -60,7 +64,18 @@ public class WebSocketSessionManagerImpl implements WebSocketSessionManager {
|
||||
sessions = userSessionsMap.get(user.getId());
|
||||
}
|
||||
}
|
||||
System.out.println("---------sessions---------: " + session);
|
||||
sessions.add(session);
|
||||
String uri = session.getUri().toString();
|
||||
System.out.println(uri);
|
||||
|
||||
Map<String, Object> attributes = session.getAttributes();
|
||||
System.out.println("---------sessions---------: " + attributes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConcurrentMap<String, WebSocketSession> idSessions() {
|
||||
return idSessions;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -39,11 +39,13 @@ public interface WebSocketSenderApi {
|
||||
*/
|
||||
void send(String sessionId, String messageType, String messageContent);
|
||||
|
||||
|
||||
|
||||
default void sendObject(Integer userType, Long userId, String messageType, Object messageContent) {
|
||||
send(userType, userId, messageType, JsonUtils.toJsonString(messageContent));
|
||||
}
|
||||
|
||||
default void sendObject(Integer userType, String messageType, Object messageContent) { //用户发送消息
|
||||
default void sendObject(Integer userType, String messageType, Object messageContent) {
|
||||
send(userType, messageType, JsonUtils.toJsonString(messageContent));
|
||||
}
|
||||
|
||||
@ -51,4 +53,5 @@ public interface WebSocketSenderApi {
|
||||
send(sessionId, messageType, JsonUtils.toJsonString(messageContent));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -31,4 +31,6 @@ public class WebSocketSenderApiImpl implements WebSocketSenderApi {
|
||||
webSocketMessageSender.send(sessionId, messageType, messageContent);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -61,6 +61,22 @@ public class ProductBrowseHistoryController {
|
||||
productBrowseHistoryRespVO.setIntroduction(productSpuDO.getIntroduction());
|
||||
resultList.add(productBrowseHistoryRespVO);
|
||||
}
|
||||
|
||||
|
||||
if (pageReqVO.getGoodsName() != null){
|
||||
List<ProductBrowseHistoryRespVO> results = new ArrayList<>();
|
||||
for (ProductBrowseHistoryRespVO productBrowseHistoryRespVO : resultList) {
|
||||
if (productBrowseHistoryRespVO.getSpuName().toLowerCase().contains(pageReqVO.getGoodsName().toLowerCase())) {
|
||||
results.add(productBrowseHistoryRespVO);
|
||||
}
|
||||
}
|
||||
PageResult<ProductBrowseHistoryRespVO> result = new PageResult<>();
|
||||
result.setTotal((long)results.size());
|
||||
result.setList(results);
|
||||
return success(result);
|
||||
|
||||
}
|
||||
|
||||
PageResult<ProductBrowseHistoryRespVO> result = new PageResult<>();
|
||||
result.setTotal(pageResult.getTotal());
|
||||
result.setList(resultList);
|
||||
|
@ -30,4 +30,6 @@ public class ProductBrowseHistoryPageReqVO extends SortablePageParam {
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
private String goodsName;
|
||||
|
||||
}
|
@ -19,6 +19,10 @@ public interface ErrorCodeConstants {
|
||||
// ========== 兑换记录 TODO 补充编号 ==========
|
||||
ErrorCode POINT_ORDER_NOT_EXISTS = new ErrorCode(11111, "兑换记录不存在");
|
||||
|
||||
ErrorCode ADVERTISING_NOT_EXISTS = new ErrorCode(333333, "开屏广告不存在");
|
||||
|
||||
|
||||
ErrorCode KE_FU_CONFIGURATION_NOT_EXISTS = new ErrorCode(22222222, "客服配置不存在");
|
||||
// ========== Banner 相关 1-013-002-000 ============
|
||||
ErrorCode BANNER_NOT_EXISTS = new ErrorCode(1_013_002_000, "Banner 不存在");
|
||||
|
||||
|
@ -0,0 +1,104 @@
|
||||
package cn.iocoder.yudao.module.promotion.controller.admin.advertising;
|
||||
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import javax.annotation.Resource;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
|
||||
import javax.validation.constraints.*;
|
||||
import javax.validation.*;
|
||||
import javax.servlet.http.*;
|
||||
import java.util.*;
|
||||
import java.io.IOException;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
|
||||
|
||||
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
|
||||
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*;
|
||||
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.advertising.vo.*;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.advertising.AdvertisingDO;
|
||||
import cn.iocoder.yudao.module.promotion.service.advertising.AdvertisingService;
|
||||
|
||||
@Tag(name = "管理后台 - 开屏广告")
|
||||
@RestController
|
||||
@RequestMapping("/promotion/advertising")
|
||||
@Validated
|
||||
public class AdvertisingController {
|
||||
|
||||
@Resource
|
||||
private AdvertisingService advertisingService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建开屏广告")
|
||||
@PreAuthorize("@ss.hasPermission('promotion:advertising:create')")
|
||||
public CommonResult<Long> createAdvertising(@Valid @RequestBody AdvertisingSaveReqVO createReqVO) {
|
||||
return success(advertisingService.createAdvertising(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新开屏广告")
|
||||
@PreAuthorize("@ss.hasPermission('promotion:advertising:update')")
|
||||
public CommonResult<Boolean> updateAdvertising(@Valid @RequestBody AdvertisingSaveReqVO updateReqVO) {
|
||||
advertisingService.updateAdvertising(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除开屏广告")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('promotion:advertising:delete')")
|
||||
public CommonResult<Boolean> deleteAdvertising(@RequestParam("id") Long id) {
|
||||
advertisingService.deleteAdvertising(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得开屏广告分页")
|
||||
@PreAuthorize("@ss.hasPermission('promotion:advertising:query')")
|
||||
public CommonResult<PageResult<AdvertisingRespVO>> getAdvertisingPage(@Valid AdvertisingPageReqVO pageReqVO) {
|
||||
PageResult<AdvertisingDO> pageResult = advertisingService.getAdvertisingPage(pageReqVO);
|
||||
return success(BeanUtils.toBean(pageResult, AdvertisingRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/export-excel")
|
||||
@Operation(summary = "导出开屏广告 Excel")
|
||||
@PreAuthorize("@ss.hasPermission('promotion:advertising:export')")
|
||||
@ApiAccessLog(operateType = EXPORT)
|
||||
public void exportAdvertisingExcel(@Valid AdvertisingPageReqVO pageReqVO,
|
||||
HttpServletResponse response) throws IOException {
|
||||
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
|
||||
List<AdvertisingDO> list = advertisingService.getAdvertisingPage(pageReqVO).getList();
|
||||
// 导出 Excel
|
||||
ExcelUtils.write(response, "开屏广告.xls", "数据", AdvertisingRespVO.class,
|
||||
BeanUtils.toBean(list, AdvertisingRespVO.class));
|
||||
}
|
||||
|
||||
|
||||
//添加修改开屏广告
|
||||
@PostMapping("/saveAdvertising")
|
||||
public CommonResult<Long> saveAdvertising(@Valid @RequestBody AdvertisingSaveReqVO createReqVO) {
|
||||
return success(advertisingService.saveAdvertising(createReqVO));
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("/getAdvertising")
|
||||
public CommonResult<AdvertisingRespVO> getAdvertising() {
|
||||
AdvertisingDO advertising = advertisingService.getAdvertising();
|
||||
return success(BeanUtils.toBean(advertising, AdvertisingRespVO.class));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package cn.iocoder.yudao.module.promotion.controller.admin.advertising.vo;
|
||||
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Schema(description = "管理后台 - 开屏广告分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class AdvertisingPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "广告状态", example = "2")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "广告时间(秒)")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private Integer[] time;
|
||||
|
||||
@Schema(description = "广告属性")
|
||||
private String property;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package cn.iocoder.yudao.module.promotion.controller.admin.advertising.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import java.time.LocalDateTime;
|
||||
import com.alibaba.excel.annotation.*;
|
||||
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
|
||||
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
|
||||
|
||||
@Schema(description = "管理后台 - 开屏广告 Response VO")
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
public class AdvertisingRespVO {
|
||||
|
||||
@Schema(description = "id", requiredMode = Schema.RequiredMode.REQUIRED, example = "684")
|
||||
@ExcelProperty("id")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "广告状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||
@ExcelProperty(value = "广告状态", converter = DictConvert.class)
|
||||
@DictFormat("promotion_diy_advertising") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "广告时间(秒)", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@ExcelProperty("广告时间(秒)")
|
||||
private Integer time;
|
||||
|
||||
@Schema(description = "广告属性", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@ExcelProperty("广告属性")
|
||||
private String property;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@ExcelProperty("创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
private List<Object> picData;
|
||||
|
||||
private Boolean stat;
|
||||
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package cn.iocoder.yudao.module.promotion.controller.admin.advertising.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import javax.validation.constraints.*;
|
||||
|
||||
@Schema(description = "管理后台 - 开屏广告新增/修改 Request VO")
|
||||
@Data
|
||||
public class AdvertisingSaveReqVO {
|
||||
|
||||
@Schema(description = "id", requiredMode = Schema.RequiredMode.REQUIRED, example = "684")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "广告状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "广告时间(秒)", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "广告时间(秒)不能为空")
|
||||
private Integer time;
|
||||
|
||||
@Schema(description = "广告属性", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private String property;
|
||||
|
||||
@Schema(description = "图片添加不能为空", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private List<Object> picData;
|
||||
|
||||
private Boolean stat;
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
package cn.iocoder.yudao.module.promotion.controller.admin.configuration;
|
||||
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import javax.annotation.Resource;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
|
||||
import javax.validation.constraints.*;
|
||||
import javax.validation.*;
|
||||
import javax.servlet.http.*;
|
||||
import java.util.*;
|
||||
import java.io.IOException;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
|
||||
|
||||
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
|
||||
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*;
|
||||
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.configuration.vo.*;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.configuration.KeFuConfigurationDO;
|
||||
import cn.iocoder.yudao.module.promotion.service.configuration.KeFuConfigurationService;
|
||||
|
||||
@Tag(name = "管理后台 - 客服配置")
|
||||
@RestController
|
||||
@RequestMapping("/promotion/ke-fu-configuration")
|
||||
@Validated
|
||||
public class KeFuConfigurationController {
|
||||
|
||||
@Resource
|
||||
private KeFuConfigurationService keFuConfigurationService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建客服配置")
|
||||
@PreAuthorize("@ss.hasPermission('promotion:ke-fu-configuration:create')")
|
||||
public CommonResult<Long> createKeFuConfiguration(@Valid @RequestBody KeFuConfigurationSaveReqVO createReqVO) {
|
||||
return success(keFuConfigurationService.createKeFuConfiguration(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新客服配置")
|
||||
@PreAuthorize("@ss.hasPermission('promotion:ke-fu-configuration:update')")
|
||||
public CommonResult<Boolean> updateKeFuConfiguration(@Valid @RequestBody KeFuConfigurationSaveReqVO updateReqVO) {
|
||||
keFuConfigurationService.updateKeFuConfiguration(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除客服配置")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('promotion:ke-fu-configuration:delete')")
|
||||
public CommonResult<Boolean> deleteKeFuConfiguration(@RequestParam("id") Long id) {
|
||||
keFuConfigurationService.deleteKeFuConfiguration(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得客服配置")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('promotion:ke-fu-configuration:query')")
|
||||
public CommonResult<KeFuConfigurationRespVO> getKeFuConfiguration(@RequestParam("id") Long id) {
|
||||
KeFuConfigurationDO keFuConfiguration = keFuConfigurationService.getKeFuConfiguration(id);
|
||||
return success(BeanUtils.toBean(keFuConfiguration, KeFuConfigurationRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得客服配置分页")
|
||||
@PreAuthorize("@ss.hasPermission('promotion:ke-fu-configuration:query')")
|
||||
public CommonResult<PageResult<KeFuConfigurationRespVO>> getKeFuConfigurationPage(@Valid KeFuConfigurationPageReqVO pageReqVO) {
|
||||
PageResult<KeFuConfigurationDO> pageResult = keFuConfigurationService.getKeFuConfigurationPage(pageReqVO);
|
||||
return success(BeanUtils.toBean(pageResult, KeFuConfigurationRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/export-excel")
|
||||
@Operation(summary = "导出客服配置 Excel")
|
||||
@PreAuthorize("@ss.hasPermission('promotion:ke-fu-configuration:export')")
|
||||
@ApiAccessLog(operateType = EXPORT)
|
||||
public void exportKeFuConfigurationExcel(@Valid KeFuConfigurationPageReqVO pageReqVO,
|
||||
HttpServletResponse response) throws IOException {
|
||||
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
|
||||
List<KeFuConfigurationDO> list = keFuConfigurationService.getKeFuConfigurationPage(pageReqVO).getList();
|
||||
// 导出 Excel
|
||||
ExcelUtils.write(response, "客服配置.xls", "数据", KeFuConfigurationRespVO.class,
|
||||
BeanUtils.toBean(list, KeFuConfigurationRespVO.class));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package cn.iocoder.yudao.module.promotion.controller.admin.configuration.vo;
|
||||
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Schema(description = "管理后台 - 客服配置分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class KeFuConfigurationPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "客服类型", example = "1")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "客服反馈")
|
||||
private String feedback;
|
||||
|
||||
@Schema(description = "客服电话")
|
||||
private String phone;
|
||||
|
||||
@Schema(description = "客服链接")
|
||||
private String link;
|
||||
|
||||
@Schema(description = "企业ID", example = "20474")
|
||||
private String enterpriseID;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package cn.iocoder.yudao.module.promotion.controller.admin.configuration.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import java.time.LocalDateTime;
|
||||
import com.alibaba.excel.annotation.*;
|
||||
|
||||
@Schema(description = "管理后台 - 客服配置 Response VO")
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
public class KeFuConfigurationRespVO {
|
||||
|
||||
@Schema(description = "id", requiredMode = Schema.RequiredMode.REQUIRED, example = "5189")
|
||||
@ExcelProperty("id")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "客服类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@ExcelProperty("客服类型")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "客服反馈")
|
||||
@ExcelProperty("客服反馈")
|
||||
private String feedback;
|
||||
|
||||
@Schema(description = "客服电话")
|
||||
@ExcelProperty("客服电话")
|
||||
private String phone;
|
||||
|
||||
@Schema(description = "客服链接")
|
||||
@ExcelProperty("客服链接")
|
||||
private String link;
|
||||
|
||||
@Schema(description = "企业ID", example = "20474")
|
||||
@ExcelProperty("企业ID")
|
||||
private String enterpriseID;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@ExcelProperty("创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package cn.iocoder.yudao.module.promotion.controller.admin.configuration.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import javax.validation.constraints.*;
|
||||
|
||||
@Schema(description = "管理后台 - 客服配置新增/修改 Request VO")
|
||||
@Data
|
||||
public class KeFuConfigurationSaveReqVO {
|
||||
|
||||
@Schema(description = "id", requiredMode = Schema.RequiredMode.REQUIRED, example = "5189")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "客服类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotEmpty(message = "客服类型不能为空")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "客服反馈")
|
||||
private String feedback;
|
||||
|
||||
@Schema(description = "客服电话")
|
||||
private String phone;
|
||||
|
||||
@Schema(description = "客服链接")
|
||||
private String link;
|
||||
|
||||
@Schema(description = "企业ID", example = "20474")
|
||||
private String enterpriseID;
|
||||
|
||||
}
|
@ -38,6 +38,14 @@ public class DynamicDataController {
|
||||
return success("保存成功");
|
||||
}
|
||||
|
||||
|
||||
//修改数据
|
||||
@PostMapping("/updateDynamicData")
|
||||
public CommonResult<String> updateDynamicData(@RequestBody Map<String, Object> requestData) {
|
||||
dynamicDataService.updateDynamicData(requestData);
|
||||
return success("保存成功");
|
||||
}
|
||||
|
||||
//查询全部数据
|
||||
@GetMapping("/selectDynamicData")
|
||||
public CommonResult<List<DynamicData>> selectDynamicData(@RequestHeader("Tenant-Id") String userAgent) {
|
||||
@ -52,6 +60,13 @@ public class DynamicDataController {
|
||||
return success(dynamicDataService.doSelectDynamicDataById(id));
|
||||
}
|
||||
|
||||
//根据id查询数据
|
||||
@GetMapping("/deleteDynamicDataById")
|
||||
@PermitAll
|
||||
public CommonResult<Boolean> deleteDynamicDataById(String id){
|
||||
return success(dynamicDataService.deleteDynamicDataById(id));
|
||||
}
|
||||
|
||||
//采集数据
|
||||
@PostMapping("/collectData")
|
||||
@PermitAll
|
||||
|
@ -20,6 +20,7 @@ import javax.validation.Valid;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
@ -56,7 +57,7 @@ public class KeFuConversationController {
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获得客服会话列表")
|
||||
@PreAuthorize("@ss.hasPermission('promotion:kefu-conversation:query')")
|
||||
public CommonResult<List<KeFuConversationRespVO>> getConversationList(@RequestParam(required = false, value = "kefuId") Long kefuId) {
|
||||
public CommonResult<List<KeFuConversationRespVO>> getConversationList(@RequestParam(required = false, value = "kefuId") Long kefuId,String name) {
|
||||
// 如果会话列表中的kefuId为null,则转接给当前kefuId(处理小程序新会话)
|
||||
if (kefuId != null) {
|
||||
List<KeFuConversationDO> updateList = new ArrayList<>();
|
||||
@ -80,8 +81,20 @@ public class KeFuConversationController {
|
||||
Map<Long, MemberUserRespDTO> userMap = memberUserApi.getUserMap(convertSet(respList, KeFuConversationRespVO::getUserId));
|
||||
respList.forEach(item -> findAndThen(userMap, item.getUserId(),
|
||||
memberUser -> item.setUserAvatar(memberUser.getAvatar()).setUserNickname(memberUser.getNickname())));
|
||||
return success(respList);
|
||||
|
||||
|
||||
List<KeFuConversationRespVO> result = new ArrayList<>();
|
||||
for (KeFuConversationRespVO keFuConversationRespVO : respList) {
|
||||
if (keFuConversationRespVO.getUserNickname().toLowerCase().contains(name.toLowerCase())) {
|
||||
result.add(keFuConversationRespVO);
|
||||
}
|
||||
}
|
||||
|
||||
return success(result);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Operation(summary = "转接会话给指定客服")
|
||||
@GetMapping("/transfer/{id}/{kefuId}")
|
||||
public CommonResult<String> transferConversation(@PathVariable("id") Long id, @PathVariable("kefuId") Long kefuId) {
|
||||
|
@ -47,6 +47,11 @@ public class KeFuMessageController {
|
||||
return success(messageService.sendKefuMessage(sendReqVO));
|
||||
}
|
||||
|
||||
@GetMapping("/sendMess")
|
||||
public String sendMess(String mess){
|
||||
return messageService.sendMess(mess);
|
||||
}
|
||||
|
||||
@PutMapping("/update-read-status")
|
||||
@Operation(summary = "更新客服消息已读状态")
|
||||
@Parameter(name = "conversationId", description = "会话编号", required = true)
|
||||
@ -72,4 +77,11 @@ public class KeFuMessageController {
|
||||
return success(result);
|
||||
}
|
||||
|
||||
@GetMapping("/getBySenderIdStat")
|
||||
@Operation(summary = "发送客服消息")
|
||||
public CommonResult<Boolean> getLatestMessageBySenderId(Integer senderId) {
|
||||
return success(messageService.getLatestMessageBySenderId(senderId));
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -42,4 +42,6 @@ public class KeFuMessageRespVO {
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
private String kefuName;
|
||||
|
||||
}
|
@ -91,4 +91,9 @@ public class SupportStaffController {
|
||||
BeanUtils.toBean(list, SupportStaffRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/updateLineStatus")
|
||||
public CommonResult<Boolean> updateLineStatus (String id , Integer lineStatus){
|
||||
supportStaffService.updateLineStatus(id, lineStatus);
|
||||
return success(true);
|
||||
}
|
||||
}
|
@ -38,6 +38,9 @@ public class SupportStaffPageReqVO extends PageParam {
|
||||
@Schema(description = "订单通知")
|
||||
private Integer orderInform;
|
||||
|
||||
@Schema(description = "线上状态")
|
||||
private Integer lineStatus;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
|
@ -55,4 +55,6 @@ public class SupportStaffRespVO {
|
||||
@ExcelProperty("创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
private Integer lineStatus;
|
||||
|
||||
}
|
@ -39,4 +39,6 @@ public class SupportStaffSaveReqVO {
|
||||
@Schema(description = "订单通知")
|
||||
private Integer orderInform;
|
||||
|
||||
private Integer lineStatus;
|
||||
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package cn.iocoder.yudao.module.promotion.controller.app.Advertising;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.advertising.vo.AdvertisingRespVO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.advertising.AdvertisingDO;
|
||||
import cn.iocoder.yudao.module.promotion.service.advertising.AdvertisingService;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/promotion/advertising")
|
||||
public class AppAdvertisingController {
|
||||
|
||||
@Resource
|
||||
private AdvertisingService advertisingService;
|
||||
|
||||
|
||||
@GetMapping("/getAdvertising")
|
||||
public CommonResult<AdvertisingRespVO> getAdvertising() {
|
||||
AdvertisingDO advertising = advertisingService.getAppAdvertising();
|
||||
return success(BeanUtils.toBean(advertising, AdvertisingRespVO.class));
|
||||
}
|
||||
|
||||
}
|
@ -10,7 +10,11 @@ import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message.KeFuMessageRespVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.app.kefu.vo.message.AppKeFuMessagePageReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.app.kefu.vo.message.AppKeFuMessageSendReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuConversationDO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuMessageDO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.supportstaff.SupportStaffDO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.mysql.kefu.KeFuConversationMapper;
|
||||
import cn.iocoder.yudao.module.promotion.dal.mysql.supportstaff.SupportStaffMapper;
|
||||
import cn.iocoder.yudao.module.promotion.service.kefu.KeFuMessageService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
@ -33,9 +37,15 @@ public class AppKeFuMessageController {
|
||||
@Resource
|
||||
private KeFuMessageService kefuMessageService;
|
||||
|
||||
@Resource
|
||||
KeFuConversationMapper keFuConversationMapper;
|
||||
|
||||
@Resource
|
||||
private MemberUserApi memberUserApi;
|
||||
|
||||
@Resource
|
||||
private SupportStaffMapper supportStaffMapper;
|
||||
|
||||
|
||||
@PostMapping("/send")
|
||||
@Operation(summary = "发送客服消息")
|
||||
@ -70,11 +80,16 @@ public class AppKeFuMessageController {
|
||||
String systemUserAvatar = kefuMessageService.findSystemUserAvatar(keFuMessageDO.getSenderId());
|
||||
keFuMessageDO.setSenderAvatar(systemUserAvatar);
|
||||
}
|
||||
|
||||
KeFuConversationDO user_di = keFuConversationMapper.selectOne("user_id", getLoginUserId());
|
||||
SupportStaffDO supportStaffDO = supportStaffMapper.selectOne("id", user_di.getKefuId());
|
||||
keFuMessageDO.setKefuName(supportStaffDO.getName());
|
||||
}
|
||||
|
||||
|
||||
return success(BeanUtils.toBean(pageResult, KeFuMessageRespVO.class));
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("/test")
|
||||
@Operation(summary = "获得客服消息分页")
|
||||
@PreAuthenticated
|
||||
|
@ -0,0 +1,59 @@
|
||||
package cn.iocoder.yudao.module.promotion.dal.dataobject.advertising;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalDateTime;
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
|
||||
/**
|
||||
* 开屏广告 DO
|
||||
*
|
||||
* @author 管理员
|
||||
*/
|
||||
@TableName("promotion_diy_template_advertising")
|
||||
@KeySequence("promotion_diy_template_advertising_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class AdvertisingDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* id
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
/**
|
||||
* 广告状态
|
||||
*
|
||||
* 枚举 {@link TODO promotion_diy_advertising 对应的类}
|
||||
*/
|
||||
private Integer status;
|
||||
/**
|
||||
* 广告时间(秒)
|
||||
*/
|
||||
private Integer time;
|
||||
/**
|
||||
* 广告属性
|
||||
*/
|
||||
private String property;
|
||||
|
||||
|
||||
/**
|
||||
* 图片数据
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private List<Object> picData;
|
||||
|
||||
/**
|
||||
* 开屏状态
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private Boolean stat;
|
||||
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
package cn.iocoder.yudao.module.promotion.dal.dataobject.configuration;
|
||||
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalDateTime;
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
|
||||
/**
|
||||
* 客服配置 DO
|
||||
*
|
||||
* @author 管理员
|
||||
*/
|
||||
@TableName("promotion_kefu_configuration")
|
||||
@KeySequence("promotion_kefu_configuration_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class KeFuConfigurationDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* id
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
/**
|
||||
* 客服类型
|
||||
*/
|
||||
private Integer type;
|
||||
/**
|
||||
* 客服反馈
|
||||
*/
|
||||
private String feedback;
|
||||
/**
|
||||
* 客服电话
|
||||
*/
|
||||
private String phone;
|
||||
/**
|
||||
* 客服链接
|
||||
*/
|
||||
private String link;
|
||||
/**
|
||||
* 企业ID
|
||||
*/
|
||||
@TableField("enterpriseID")
|
||||
private String enterpriseID;
|
||||
|
||||
}
|
@ -3,12 +3,11 @@ package cn.iocoder.yudao.module.promotion.dal.dataobject.kefu;
|
||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.module.promotion.enums.kefu.KeFuMessageContentTypeEnum;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 客服消息 DO
|
||||
*
|
||||
@ -82,4 +81,11 @@ public class KeFuMessageDO extends BaseDO {
|
||||
|
||||
@TableField(exist = false)
|
||||
private String senderAvatar;
|
||||
|
||||
|
||||
@TableField(exist = false)
|
||||
private String kefuName;
|
||||
|
||||
@TableField(exist = false)
|
||||
private Integer stat;
|
||||
}
|
||||
|
@ -63,4 +63,9 @@ public class SupportStaffDO extends BaseDO {
|
||||
*/
|
||||
private Integer orderInform;
|
||||
|
||||
/**
|
||||
* 线上状态
|
||||
*/
|
||||
private Integer lineStatus;
|
||||
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package cn.iocoder.yudao.module.promotion.dal.mysql.advertising;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.advertising.AdvertisingDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.advertising.vo.*;
|
||||
|
||||
/**
|
||||
* 开屏广告 Mapper
|
||||
*
|
||||
* @author 管理员
|
||||
*/
|
||||
@Mapper
|
||||
public interface AdvertisingMapper extends BaseMapperX<AdvertisingDO> {
|
||||
|
||||
default PageResult<AdvertisingDO> selectPage(AdvertisingPageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<AdvertisingDO>()
|
||||
.eqIfPresent(AdvertisingDO::getStatus, reqVO.getStatus())
|
||||
.betweenIfPresent(AdvertisingDO::getTime, reqVO.getTime())
|
||||
.eqIfPresent(AdvertisingDO::getProperty, reqVO.getProperty())
|
||||
.betweenIfPresent(AdvertisingDO::getCreateTime, reqVO.getCreateTime())
|
||||
.orderByDesc(AdvertisingDO::getId));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package cn.iocoder.yudao.module.promotion.dal.mysql.configuration;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.configuration.KeFuConfigurationDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.configuration.vo.*;
|
||||
|
||||
/**
|
||||
* 客服配置 Mapper
|
||||
*
|
||||
* @author 管理员
|
||||
*/
|
||||
@Mapper
|
||||
public interface KeFuConfigurationMapper extends BaseMapperX<KeFuConfigurationDO> {
|
||||
|
||||
default PageResult<KeFuConfigurationDO> selectPage(KeFuConfigurationPageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<KeFuConfigurationDO>()
|
||||
.eqIfPresent(KeFuConfigurationDO::getType, reqVO.getType())
|
||||
.eqIfPresent(KeFuConfigurationDO::getFeedback, reqVO.getFeedback())
|
||||
.eqIfPresent(KeFuConfigurationDO::getPhone, reqVO.getPhone())
|
||||
.eqIfPresent(KeFuConfigurationDO::getLink, reqVO.getLink())
|
||||
.eqIfPresent(KeFuConfigurationDO::getEnterpriseID, reqVO.getEnterpriseID())
|
||||
.betweenIfPresent(KeFuConfigurationDO::getCreateTime, reqVO.getCreateTime())
|
||||
.orderByDesc(KeFuConfigurationDO::getId));
|
||||
}
|
||||
|
||||
}
|
@ -51,4 +51,7 @@ public interface KeFuMessageMapper extends BaseMapperX<KeFuMessageDO> {
|
||||
@Select(" SELECT avatar FROM system_users where id = #{id} ")
|
||||
String findSystemUserAvatar(Long id);
|
||||
|
||||
@Select("SELECT read_status as stat FROM promotion_kefu_message WHERE sender_id = #{senderId} ORDER BY create_time DESC LIMIT 1")
|
||||
String getLatestMessageBySenderId(Integer senderId);
|
||||
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
package cn.iocoder.yudao.module.promotion.service.advertising;
|
||||
|
||||
import java.util.*;
|
||||
import javax.validation.*;
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.advertising.vo.*;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.advertising.AdvertisingDO;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
|
||||
/**
|
||||
* 开屏广告 Service 接口
|
||||
*
|
||||
* @author 管理员
|
||||
*/
|
||||
public interface AdvertisingService {
|
||||
|
||||
/**
|
||||
* 创建开屏广告
|
||||
*
|
||||
* @param createReqVO 创建信息
|
||||
* @return 编号
|
||||
*/
|
||||
Long createAdvertising(@Valid AdvertisingSaveReqVO createReqVO);
|
||||
|
||||
/**
|
||||
* 更新开屏广告
|
||||
*
|
||||
* @param updateReqVO 更新信息
|
||||
*/
|
||||
void updateAdvertising(@Valid AdvertisingSaveReqVO updateReqVO);
|
||||
|
||||
/**
|
||||
* 删除开屏广告
|
||||
*
|
||||
* @param id 编号
|
||||
*/
|
||||
void deleteAdvertising(Long id);
|
||||
|
||||
/**
|
||||
* 获得开屏广告
|
||||
*
|
||||
* @return 开屏广告
|
||||
*/
|
||||
AdvertisingDO getAdvertising();
|
||||
|
||||
AdvertisingDO getAppAdvertising();
|
||||
|
||||
/**
|
||||
* 获得开屏广告分页
|
||||
*
|
||||
* @param pageReqVO 分页查询
|
||||
* @return 开屏广告分页
|
||||
*/
|
||||
PageResult<AdvertisingDO> getAdvertisingPage(AdvertisingPageReqVO pageReqVO);
|
||||
|
||||
|
||||
Long saveAdvertising(@Valid AdvertisingSaveReqVO createReqVO);
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
package cn.iocoder.yudao.module.promotion.service.advertising;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import org.springframework.stereotype.Service;
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.*;
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.advertising.vo.*;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.advertising.AdvertisingDO;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
|
||||
import cn.iocoder.yudao.module.promotion.dal.mysql.advertising.AdvertisingMapper;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
|
||||
|
||||
/**
|
||||
* 开屏广告 Service 实现类
|
||||
*
|
||||
* @author 管理员
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class AdvertisingServiceImpl implements AdvertisingService {
|
||||
|
||||
@Resource
|
||||
private AdvertisingMapper advertisingMapper;
|
||||
|
||||
@Override
|
||||
public Long createAdvertising(AdvertisingSaveReqVO createReqVO) {
|
||||
// 插入
|
||||
AdvertisingDO advertising = BeanUtils.toBean(createReqVO, AdvertisingDO.class);
|
||||
advertisingMapper.insert(advertising);
|
||||
// 返回
|
||||
return advertising.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAdvertising(AdvertisingSaveReqVO updateReqVO) {
|
||||
// 校验存在
|
||||
validateAdvertisingExists(updateReqVO.getId());
|
||||
// 更新
|
||||
AdvertisingDO updateObj = BeanUtils.toBean(updateReqVO, AdvertisingDO.class);
|
||||
advertisingMapper.updateById(updateObj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteAdvertising(Long id) {
|
||||
// 校验存在
|
||||
validateAdvertisingExists(id);
|
||||
// 删除
|
||||
advertisingMapper.deleteById(id);
|
||||
}
|
||||
|
||||
private void validateAdvertisingExists(Long id) {
|
||||
if (advertisingMapper.selectById(id) == null) {
|
||||
throw exception(ADVERTISING_NOT_EXISTS);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdvertisingDO getAdvertising() {
|
||||
List<AdvertisingDO> advertisingDOS = advertisingMapper.selectList();
|
||||
if (advertisingDOS.isEmpty()){
|
||||
return new AdvertisingDO();
|
||||
}
|
||||
AdvertisingDO advertisingDO = advertisingDOS.get(0);
|
||||
advertisingDO.setPicData(JSON.parseArray(advertisingDO.getProperty()));
|
||||
if (advertisingDO.getStatus() == 1){
|
||||
advertisingDO.setStat(true);
|
||||
}
|
||||
if (advertisingDO.getStatus() == 2){
|
||||
advertisingDO.setStat(false);
|
||||
}
|
||||
|
||||
return advertisingDO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdvertisingDO getAppAdvertising() {
|
||||
List<AdvertisingDO> advertisingDOS = advertisingMapper.selectList();
|
||||
return advertisingDOS.get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<AdvertisingDO> getAdvertisingPage(AdvertisingPageReqVO pageReqVO) {
|
||||
return advertisingMapper.selectPage(pageReqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long saveAdvertising(@Valid AdvertisingSaveReqVO createReqVO) {
|
||||
if (createReqVO.getStat()){
|
||||
createReqVO.setStatus(1);
|
||||
}else {
|
||||
createReqVO.setStatus(2);
|
||||
}
|
||||
List<AdvertisingDO> advertisingDOS = advertisingMapper.selectList();
|
||||
if (advertisingDOS.isEmpty()){
|
||||
createReqVO.setProperty(JSON.toJSONString(createReqVO.getPicData()));
|
||||
AdvertisingDO advertising = BeanUtils.toBean(createReqVO, AdvertisingDO.class);
|
||||
return (long) advertisingMapper.insert(advertising);
|
||||
}
|
||||
AdvertisingDO advertisingDO = advertisingDOS.get(0);
|
||||
advertisingDO.setProperty(JSON.toJSONString(createReqVO.getPicData()));
|
||||
return (long)advertisingMapper.updateById(advertisingDO);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
package cn.iocoder.yudao.module.promotion.service.configuration;
|
||||
|
||||
import java.util.*;
|
||||
import javax.validation.*;
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.configuration.vo.*;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.configuration.KeFuConfigurationDO;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
|
||||
/**
|
||||
* 客服配置 Service 接口
|
||||
*
|
||||
* @author 管理员
|
||||
*/
|
||||
public interface KeFuConfigurationService {
|
||||
|
||||
/**
|
||||
* 创建客服配置
|
||||
*
|
||||
* @param createReqVO 创建信息
|
||||
* @return 编号
|
||||
*/
|
||||
Long createKeFuConfiguration(@Valid KeFuConfigurationSaveReqVO createReqVO);
|
||||
|
||||
/**
|
||||
* 更新客服配置
|
||||
*
|
||||
* @param updateReqVO 更新信息
|
||||
*/
|
||||
void updateKeFuConfiguration(@Valid KeFuConfigurationSaveReqVO updateReqVO);
|
||||
|
||||
/**
|
||||
* 删除客服配置
|
||||
*
|
||||
* @param id 编号
|
||||
*/
|
||||
void deleteKeFuConfiguration(Long id);
|
||||
|
||||
/**
|
||||
* 获得客服配置
|
||||
*
|
||||
* @param id 编号
|
||||
* @return 客服配置
|
||||
*/
|
||||
KeFuConfigurationDO getKeFuConfiguration(Long id);
|
||||
|
||||
/**
|
||||
* 获得客服配置分页
|
||||
*
|
||||
* @param pageReqVO 分页查询
|
||||
* @return 客服配置分页
|
||||
*/
|
||||
PageResult<KeFuConfigurationDO> getKeFuConfigurationPage(KeFuConfigurationPageReqVO pageReqVO);
|
||||
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
package cn.iocoder.yudao.module.promotion.service.configuration;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import javax.annotation.Resource;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.*;
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.configuration.vo.*;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.configuration.KeFuConfigurationDO;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
|
||||
import cn.iocoder.yudao.module.promotion.dal.mysql.configuration.KeFuConfigurationMapper;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
|
||||
|
||||
/**
|
||||
* 客服配置 Service 实现类
|
||||
*
|
||||
* @author 管理员
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class KeFuConfigurationServiceImpl implements KeFuConfigurationService {
|
||||
|
||||
@Resource
|
||||
private KeFuConfigurationMapper keFuConfigurationMapper;
|
||||
|
||||
@Override
|
||||
public Long createKeFuConfiguration(KeFuConfigurationSaveReqVO createReqVO) {
|
||||
// 插入
|
||||
KeFuConfigurationDO keFuConfiguration = BeanUtils.toBean(createReqVO, KeFuConfigurationDO.class);
|
||||
keFuConfigurationMapper.insert(keFuConfiguration);
|
||||
// 返回
|
||||
return keFuConfiguration.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateKeFuConfiguration(KeFuConfigurationSaveReqVO updateReqVO) {
|
||||
|
||||
// 校验存在
|
||||
validateKeFuConfigurationExists(updateReqVO.getId());
|
||||
// 更新
|
||||
KeFuConfigurationDO updateObj = BeanUtils.toBean(updateReqVO, KeFuConfigurationDO.class);
|
||||
keFuConfigurationMapper.updateById(updateObj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteKeFuConfiguration(Long id) {
|
||||
// 校验存在
|
||||
validateKeFuConfigurationExists(id);
|
||||
// 删除
|
||||
keFuConfigurationMapper.deleteById(id);
|
||||
}
|
||||
|
||||
private void validateKeFuConfigurationExists(Long id) {
|
||||
if (keFuConfigurationMapper.selectById(id) == null) {
|
||||
throw exception(KE_FU_CONFIGURATION_NOT_EXISTS);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeFuConfigurationDO getKeFuConfiguration(Long id) {
|
||||
return keFuConfigurationMapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<KeFuConfigurationDO> getKeFuConfigurationPage(KeFuConfigurationPageReqVO pageReqVO) {
|
||||
return keFuConfigurationMapper.selectPage(pageReqVO);
|
||||
}
|
||||
|
||||
}
|
@ -13,6 +13,8 @@ import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId;
|
||||
|
||||
|
||||
@ -31,6 +33,7 @@ public class DynamicDataService {
|
||||
String pageView = (String)requestData.get("pageView");
|
||||
String collectionQuantity = (String)requestData.get("collectionQuantity");
|
||||
dynamicData.setTitle(title);
|
||||
requestData.remove("id");
|
||||
requestData.remove("title");
|
||||
dynamicData.setPageView(pageView);
|
||||
requestData.remove("pageView");
|
||||
@ -51,6 +54,31 @@ public class DynamicDataService {
|
||||
return dynamicDataRepository.save(dynamicData);//插入dynamicData数据
|
||||
}
|
||||
|
||||
|
||||
//修改数据
|
||||
public DynamicData updateDynamicData(Map<String, Object> requestData){
|
||||
|
||||
//构造数据结构、
|
||||
Optional<DynamicData> id = dynamicDataRepository.findById((String) requestData.get("id"));
|
||||
DynamicData dynamicData = id.get();
|
||||
|
||||
String title = (String)requestData.get("title");
|
||||
dynamicData.setTitle(title);
|
||||
requestData.remove("title");
|
||||
requestData.remove("id");
|
||||
requestData.remove("pageView");
|
||||
requestData.remove("collectionQuantity");
|
||||
|
||||
ArrayList<String> inputarr = (ArrayList)requestData.get("inputarr");
|
||||
for (String s : inputarr) {
|
||||
requestData.put(s,null);
|
||||
}
|
||||
requestData.remove("inputarr");
|
||||
dynamicData.setData(requestData);
|
||||
|
||||
return dynamicDataRepository.save(dynamicData);//插入dynamicData数据
|
||||
}
|
||||
|
||||
//查询全部数据
|
||||
public List<DynamicData> doSelectDynamicData(String httpServletRequest){
|
||||
//查询对应租户下的卡片列表数据
|
||||
@ -71,4 +99,12 @@ public class DynamicDataService {
|
||||
return dynamicDataRepository.save(dynamicData);
|
||||
}
|
||||
|
||||
//根据id查询数据
|
||||
public boolean deleteDynamicDataById(String id){
|
||||
dynamicDataRepository.deleteById(id);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.promotion.controller.app.kefu.vo.message.AppKeFuM
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuMessageDO;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 客服消息 Service 接口
|
||||
@ -24,6 +25,8 @@ public interface KeFuMessageService {
|
||||
*/
|
||||
Long sendKefuMessage(@Valid KeFuMessageSendReqVO sendReqVO);
|
||||
|
||||
String sendMess(String mess);
|
||||
|
||||
/**
|
||||
* 【会员】发送客服消息
|
||||
*
|
||||
@ -63,4 +66,11 @@ public interface KeFuMessageService {
|
||||
|
||||
String findSystemUserAvatar(Long id);
|
||||
|
||||
/**
|
||||
* 根据 senderId 查询创建时间最近的一条数据
|
||||
* @param senderId 发送人编号
|
||||
* @return Optional<KeFuMessageDO> 返回最近的一条消息
|
||||
*/
|
||||
Boolean getLatestMessageBySenderId(Integer senderId);
|
||||
|
||||
}
|
@ -7,6 +7,7 @@ import cn.hutool.extra.spring.SpringUtil;
|
||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
|
||||
import cn.iocoder.yudao.module.infra.api.websocket.WebSocketSenderApi;
|
||||
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
|
||||
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
|
||||
@ -14,20 +15,30 @@ import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message.KeFuMe
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message.KeFuMessageSendReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.app.kefu.vo.message.AppKeFuMessagePageReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.app.kefu.vo.message.AppKeFuMessageSendReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.configuration.KeFuConfigurationDO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuConversationDO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuMessageDO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.supportstaff.SupportStaffDO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.mysql.configuration.KeFuConfigurationMapper;
|
||||
import cn.iocoder.yudao.module.promotion.dal.mysql.kefu.KeFuConversationMapper;
|
||||
import cn.iocoder.yudao.module.promotion.dal.mysql.kefu.KeFuMessageMapper;
|
||||
import cn.iocoder.yudao.module.promotion.dal.mysql.supportstaff.SupportStaffMapper;
|
||||
import cn.iocoder.yudao.module.promotion.enums.kefu.KeFuMessageContentTypeEnum;
|
||||
import cn.iocoder.yudao.module.system.api.notify.NotifyMessageSendApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyMessageDO;
|
||||
import cn.iocoder.yudao.module.system.dal.mysql.notify.NotifyMessageMapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
|
||||
@ -55,9 +66,15 @@ public class KeFuMessageServiceImpl implements KeFuMessageService {
|
||||
@Resource
|
||||
private WebSocketSenderApi webSocketSenderApi;
|
||||
|
||||
@Resource
|
||||
private SupportStaffMapper supportStaffMapper;
|
||||
|
||||
@Resource
|
||||
private NotifyMessageMapper notifyMessageMapper;
|
||||
|
||||
@Resource
|
||||
private KeFuConfigurationMapper keFuConfigurationMapper;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long sendKefuMessage(KeFuMessageSendReqVO sendReqVO) { //客服发消息
|
||||
@ -80,6 +97,12 @@ public class KeFuMessageServiceImpl implements KeFuMessageService {
|
||||
return kefuMessage.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String sendMess(String mess) {
|
||||
webSocketSenderApi.sendObject(UserTypeEnum.ADMIN.getValue(), 1L, KEFU_MESSAGE_TYPE, mess);
|
||||
return "发送成功";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long sendKefuMessage(AppKeFuMessageSendReqVO sendReqVO) { //用户发消息
|
||||
// 1.1 设置会话编号
|
||||
@ -93,10 +116,33 @@ public class KeFuMessageServiceImpl implements KeFuMessageService {
|
||||
// 2. 更新会话消息冗余
|
||||
conversationService.updateConversationLastMessage(kefuMessage);
|
||||
|
||||
getSelf().sendAsyncMessageToMembers(conversation.getKefuId(), KEFU_MESSAGE_TYPE, kefuMessage);
|
||||
//客服实时接收消息
|
||||
// getSelf().sendAsyncMessageToMembers(conversation.getKefuId(), KEFU_MESSAGE_TYPE, kefuMessage);
|
||||
|
||||
//判断客服是否在线,自动回复
|
||||
SupportStaffDO supportStaffDO = supportStaffMapper.selectOne("id", conversation.getKefuId());
|
||||
if (supportStaffDO.getLineStatus() == 2){ //离线
|
||||
KeFuConfigurationDO type = keFuConfigurationMapper.selectOne("type", 1);
|
||||
if (type != null){ //是否配置了客服不在线回复
|
||||
if (type.getFeedback() != null){
|
||||
KeFuMessageDO keFuMessageDO = new KeFuMessageDO();
|
||||
keFuMessageDO.setSenderId(conversation.getKefuId());
|
||||
keFuMessageDO.setSenderType(UserTypeEnum.ADMIN.getValue());
|
||||
keFuMessageDO.setContent(type.getFeedback());
|
||||
keFuMessageDO.setContentType(KeFuMessageContentTypeEnum.TEXT.getType());
|
||||
keFuMessageDO.setConversationId(conversation.getId());
|
||||
keFuMessageDO.setReceiverId(conversation.getUserId()).setReceiverType(UserTypeEnum.MEMBER.getValue()); // 设置接收人
|
||||
keFuMessageMapper.insert(keFuMessageDO);
|
||||
// 2.2 更新会话消息冗余
|
||||
conversationService.updateConversationLastMessage(keFuMessageDO);
|
||||
// 3.1 发送消息给用户
|
||||
getSelf().sendAsyncMessageToMember(conversation.getUserId(), KEFU_MESSAGE_TYPE, keFuMessageDO);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 通知所有管理员更新对话
|
||||
getSelf().sendAsyncMessageToAdmins(KEFU_MESSAGE_TYPE, kefuMessage);
|
||||
getSelf().sendAsyncMessageToAdmin(KEFU_MESSAGE_TYPE, kefuMessage);
|
||||
return kefuMessage.getId();
|
||||
}
|
||||
//添加站内信
|
||||
@ -165,7 +211,7 @@ public class KeFuMessageServiceImpl implements KeFuMessageService {
|
||||
|
||||
@Async
|
||||
public void sendAsyncMessageToMembers(Long userId, String messageType, Object content) {
|
||||
webSocketSenderApi.sendObject(UserTypeEnum.MEMBER.getValue(), userId, messageType, content);
|
||||
webSocketSenderApi.sendObject(UserTypeEnum.ADMIN.getValue(), userId, messageType, content);
|
||||
}
|
||||
|
||||
@Async
|
||||
@ -173,10 +219,6 @@ public class KeFuMessageServiceImpl implements KeFuMessageService {
|
||||
webSocketSenderApi.sendObject(UserTypeEnum.ADMIN.getValue(), messageType, content);
|
||||
}
|
||||
|
||||
@Async
|
||||
public void sendAsyncMessageToAdmins(String messageType, Object content) {
|
||||
webSocketSenderApi.sendObject(UserTypeEnum.MEMBER.getValue(), messageType, content);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<KeFuMessageDO> getKeFuMessagePage(KeFuMessagePageReqVO pageReqVO) {
|
||||
@ -200,6 +242,15 @@ public class KeFuMessageServiceImpl implements KeFuMessageService {
|
||||
return keFuMessageMapper.findSystemUserAvatar(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getLatestMessageBySenderId(Integer senderId) {
|
||||
String stat = keFuMessageMapper.getLatestMessageBySenderId(senderId);
|
||||
if (stat.equals("0")){ //未读:0, 已读:1
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private KeFuMessageServiceImpl getSelf() {
|
||||
return SpringUtil.getBean(getClass());
|
||||
}
|
||||
|
@ -50,4 +50,9 @@ public interface SupportStaffService {
|
||||
*/
|
||||
PageResult<SupportStaffDO> getSupportStaffPage(SupportStaffPageReqVO pageReqVO);
|
||||
|
||||
/**
|
||||
* 修改客服是否在线状态
|
||||
*/
|
||||
void updateLineStatus(String id , Integer lineStatus );
|
||||
|
||||
}
|
@ -27,6 +27,7 @@ public class SupportStaffServiceImpl implements SupportStaffService {
|
||||
public Integer createSupportStaff(SupportStaffSaveReqVO createReqVO) {
|
||||
// 插入
|
||||
SupportStaffDO supportStaff = BeanUtils.toBean(createReqVO, SupportStaffDO.class);
|
||||
supportStaff.setLineStatus(2);
|
||||
supportStaffMapper.insert(supportStaff);
|
||||
// 返回
|
||||
return supportStaff.getId();
|
||||
@ -65,4 +66,11 @@ public class SupportStaffServiceImpl implements SupportStaffService {
|
||||
return supportStaffMapper.selectPage(pageReqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateLineStatus(String id,Integer lineStatus) {
|
||||
SupportStaffDO supportStaffDO = supportStaffMapper.selectOne("id", id);
|
||||
supportStaffDO.setLineStatus(lineStatus);
|
||||
supportStaffMapper.updateById(supportStaffDO);
|
||||
}
|
||||
|
||||
}
|
@ -52,6 +52,8 @@ public class TradeOrderController {
|
||||
@Operation(summary = "获得交易订单分页")
|
||||
@PreAuthorize("@ss.hasPermission('trade:order:query')")
|
||||
public CommonResult<PageResult<TradeOrderPageItemRespVO>> getOrderPage(TradeOrderPageReqVO reqVO) {
|
||||
|
||||
|
||||
// 查询订单
|
||||
PageResult<TradeOrderDO> pageResult = tradeOrderQueryService.getOrderPage(reqVO);
|
||||
if (CollUtil.isEmpty(pageResult.getList())) {
|
||||
|
@ -61,4 +61,7 @@ public class TradeOrderPageReqVO extends PageParam {
|
||||
@InEnum(value = TerminalEnum.class, message = "订单来源 {value}")
|
||||
private Integer terminal;
|
||||
|
||||
//客服工作台用户交易订单类型
|
||||
private String activeName;
|
||||
|
||||
}
|
||||
|
@ -6,10 +6,14 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSalePageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface AfterSaleMapper extends BaseMapperX<AfterSaleDO> {
|
||||
@ -48,4 +52,9 @@ public interface AfterSaleMapper extends BaseMapperX<AfterSaleDO> {
|
||||
.in(AfterSaleDO::getStatus, statuses));
|
||||
}
|
||||
|
||||
@Select(" SELECT a.* FROM trade_order a " +
|
||||
" INNER JOIN trade_after_sale b on a.id = b.order_id " +
|
||||
" where a.user_id = #{userId} and b.status != 50 LIMIT #{startRow},#{limit} ")
|
||||
List<TradeOrderDO> traderOrderList(@Param("userId") Long userId,@Param("startRow") Integer startRow, @Param("limit") Integer limit);
|
||||
|
||||
}
|
||||
|
@ -12,9 +12,11 @@ import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderPageReq
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderSummaryRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppPointOrderVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderPageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.mysql.aftersale.AfterSaleMapper;
|
||||
import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderItemMapper;
|
||||
import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper;
|
||||
import cn.iocoder.yudao.module.trade.dal.redis.RedisKeyConstants;
|
||||
@ -57,6 +59,9 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService {
|
||||
@Resource
|
||||
private MemberUserApi memberUserApi;
|
||||
|
||||
@Resource
|
||||
private AfterSaleMapper afterSaleMapper;
|
||||
|
||||
// =================== Order ===================
|
||||
|
||||
@Override
|
||||
@ -89,6 +94,31 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService {
|
||||
|
||||
@Override
|
||||
public PageResult<TradeOrderDO> getOrderPage(TradeOrderPageReqVO reqVO) {
|
||||
if(reqVO.getActiveName() != null){
|
||||
if (reqVO.getActiveName().equals("b")){ //未付款
|
||||
reqVO.setStatus(TradeOrderStatusEnum.UNPAID.getStatus());
|
||||
}
|
||||
if (reqVO.getActiveName().equals("c")){ //未发货
|
||||
reqVO.setStatus(TradeOrderStatusEnum.UNDELIVERED.getStatus());
|
||||
}
|
||||
if (reqVO.getActiveName().equals("d")){ //退款中
|
||||
Integer startRow = (reqVO.getPageNo() - 1) * reqVO.getPageSize();
|
||||
List<TradeOrderDO> tradeOrderDOS = afterSaleMapper.traderOrderList(reqVO.getUserId(), startRow, reqVO.getPageSize());
|
||||
for (int i = 0; i < tradeOrderDOS.size(); i++) {
|
||||
tradeOrderDOS.get(i).setStatus(100);
|
||||
}
|
||||
// 根据用户查询条件构建用户编号列表
|
||||
Set<Long> userIds = buildQueryConditionUserIds(reqVO);
|
||||
if (userIds == null) { // 没查询到用户,说明肯定也没他的订单
|
||||
return PageResult.empty();
|
||||
}
|
||||
// 分页查询
|
||||
PageResult<TradeOrderDO> tradeOrderDOPageResult = new PageResult<>();
|
||||
tradeOrderDOPageResult.setList(tradeOrderDOS);
|
||||
tradeOrderDOPageResult.setTotal((long)tradeOrderDOS.size());
|
||||
return tradeOrderDOPageResult;
|
||||
}
|
||||
}
|
||||
// 根据用户查询条件构建用户编号列表
|
||||
Set<Long> userIds = buildQueryConditionUserIds(reqVO);
|
||||
if (userIds == null) { // 没查询到用户,说明肯定也没他的订单
|
||||
|
@ -225,9 +225,12 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService {
|
||||
//获取用户头像和昵称
|
||||
PayWalletDO payWalletDO = payWalletMapper.selectById(payWalletRechargeDO.getWalletId());
|
||||
MemberUserRespDTO user = memberUserApi.getUser(payWalletDO.getUserId());
|
||||
|
||||
payWalletRechargeDO.setName(user.getNickname());
|
||||
payWalletRechargeDO.setAvatar(user.getAvatar());
|
||||
if(user.getLevelId() != null){
|
||||
payWalletRechargeDO.setName(user.getNickname());
|
||||
}
|
||||
if (user.getAvatar() != null) {
|
||||
payWalletRechargeDO.setAvatar(user.getAvatar());
|
||||
}
|
||||
|
||||
}
|
||||
return payWalletRechargeDOPageResult;
|
||||
|