Compare commits

..

1 Commits
master ... khy1

Author SHA1 Message Date
khy
dd143f237e 种草社区(动态管理) 2024-10-30 18:44:55 +08:00
173 changed files with 919 additions and 5938 deletions

View File

@ -1,66 +0,0 @@
kind: pipeline # 定义对象类型还有secret和signature两种类型
type: docker # 定义流水线类型还有kubernetes、exec、ssh等类型
name: filesystem-drone # 定义流水线名称
clone:
disable: true
steps: # 定义流水线执行步骤,这些步骤将顺序执行
- name: build-copy-vue
image: appleboy/drone-ssh # SSH工具镜像
settings:
host: 101.43.112.107 # 远程连接地址
username: root # 远程连接账号
password:
from_secret: ssh_password
port: 22 # 远程连接端口
command_timeout: 20m # 远程执行命令超时时间
script:
- ssh root@47.118.40.3 "sh -c 'cd /build_project/allLikeMall/yudao-admin-vue3 && git pull origin master && /usr/bin/pnpm install && /usr/bin/pnpm run build:prod && zip -r dist-prod.zip dist-prod'"
- scp root@47.118.40.3:/build_project/allLikeMall/yudao-admin-vue3/dist-prod.zip /build_package/
- scp /build_package/dist-prod.zip root@1.14.205.126:/build_package/
- name: build-java-and-vue
image: appleboy/drone-ssh # SSH工具镜像
settings:
host: 1.14.205.126 # 远程连接地址
username: root # 远程连接账号
password:
from_secret: ssh_password # 从Secret中读取SSH密码
port: 22 # 远程连接端口
command_timeout: 30m # 远程执行命令超时时间
script:
- echo "build-java......"
- cd /root/allLikeMall
- git fetch origin
- git reset --hard origin/master
- git clean -fd
- /root/apache-maven-3.9.4/bin/mvn package -Dmaven.test.skip=true
- cd yudao-server
- chmod +x all.sh
- ./all.sh
- echo "build-vue......"
- cd /build_package
- rm -rf dist-prod
- unzip dist-prod.zip
- rm -rf /root/nginx/html/mall
- mv dist-prod /root/nginx/html/mall

View File

@ -1,57 +0,0 @@
kind: pipeline # 定义对象类型还有secret和signature两种类型
type: docker # 定义流水线类型还有kubernetes、exec、ssh等类型
name: filesystem-drone # 定义流水线名称
clone:
disable: true
steps: # 定义流水线执行步骤,这些步骤将顺序执行
- name: build-copy-vue
image: appleboy/drone-ssh # SSH工具镜像
settings:
host: 101.43.112.107 # 远程连接地址
username: root # 远程连接账号
password:
from_secret: ssh_password
port: 22 # 远程连接端口
command_timeout: 20m # 远程执行命令超时时间
script:
- ssh root@47.118.40.3 "sh -c 'cd /build_project/allLikeMall/yudao-admin-vue3 && git pull origin master && /usr/bin/pnpm install && /usr/bin/pnpm run build:prod && zip -r dist-prod.zip dist-prod'"
- scp root@47.118.40.3:/build_project/allLikeMall/yudao-admin-vue3/dist-prod.zip /build_package/
- scp /build_package/dist-prod.zip root@1.14.205.126:/build_package/
- name: build-vue
image: appleboy/drone-ssh # SSH工具镜像
settings:
host: 1.14.205.126 # 远程连接地址
username: root # 远程连接账号
password:
from_secret: ssh_password # 从Secret中读取SSH密码
port: 22 # 远程连接端口
command_timeout: 30m # 远程执行命令超时时间
script:
- echo "build-vue......"
- cd /build_package
- rm -rf dist-prod
- unzip dist-prod.zip
- rm -rf /root/nginx/html/mall
- mv dist-prod /root/nginx/html/mall

View File

@ -1,22 +1,32 @@
kind: pipeline # 定义对象类型还有secret和signature两种类型
type: docker # 定义流水线类型还有kubernetes、exec、ssh等类型
name: filesystem-drone # 定义流水线名称
clone:
disable: true
steps: # 定义流水线执行步骤,这些步骤将顺序执行
- name: build-java
- name: build-start
image: appleboy/drone-ssh # SSH工具镜像
settings:
host: 1.14.205.126 # 远程连接地址
username: root # 远程连接账号
password:
from_secret: ssh_password # 从Secret中读取SSH密码
port: 22 # 远程连接端口
command_timeout: 30m # 远程执行命令超时时间
script:
- echo "build-java......"
- cd /root/allLikeMall
- git fetch origin
- git reset --hard origin/master
@ -25,3 +35,4 @@ steps: # 定义流水线执行步骤,这些步骤将顺序执行
- cd yudao-server
- chmod +x all.sh
- ./all.sh

View File

@ -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">

View File

@ -12,27 +12,12 @@ export const FollowUpRecordApi = {
return await request.post({ url: `/intelligentForm/saveDynamicData`, data })
},
//修改智能表单
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}
})
}
// //查询对应表单的采集数据列表
// collectDataList: async(query) => {
// return await request.get({
// url: '/intelligentForm/collectDataList',
// method: 'get',
// params: {id:query}
// })
// }
}

View File

@ -1,50 +0,0 @@
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 })
},
}

View File

@ -4,6 +4,7 @@ import request from '@/config/axios'
export interface CircleVO {
id: number // 编号
userId: number // 用户编号
nickname: string //用户昵称
content: string // 发表内容
picUrl: string // 图片地址
likeCount: number // 点赞量

View File

@ -1,44 +0,0 @@
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 })
}
}

View File

@ -56,32 +56,3 @@ 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`
})
}

View File

@ -25,8 +25,8 @@ export const saveDynamicData = async (data: ArticleVO) => {
}
// 查询文章管理列表
export const collectDataList = async (id: any) => {
return await request.get({ url: `/intelligentForm/collectDataList?id=` + id, })
export const collectDataList = async (query: any) => {
return await request.get({ url: `/intelligentForm/collectDataList`, query })
}
// // 修改文章管理

View File

@ -23,8 +23,8 @@ export const KeFuConversationApi = {
return await request.get({ url: '/promotion/kefu-conversation/list' })
},
// 获得客服会话列表
getConversationListByKefuId: async (kefuId: number , name: string) => {
return await request.get({ url: '/promotion/kefu-conversation/list?kefuId=' + kefuId + `&name=` + name })
getConversationListByKefuId: async (kefuId: number) => {
return await request.get({ url: '/promotion/kefu-conversation/list?kefuId=' + kefuId })
},
// 客服会话置顶
updateConversationPinned: async (data: any) => {

View File

@ -32,10 +32,5 @@ 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 })
}
}

View File

@ -44,12 +44,5 @@ 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 })
},
}
}

View File

@ -20,7 +20,6 @@ export interface UserVO {
point: number | undefined | null
totalPoint: number | undefined | null
experience: number | null | undefined
groupName: string
}
// 查询会员用户列表
@ -53,7 +52,7 @@ export const updateUserBalance = async (data: any) => {
return await request.put({ url: `/member/user/update-balance`, data })
}
// 客服查询用户详情
export const getUserInfo = async (id: number) => {
return await request.get({ url: `/member/user/getUserInfo?id=` + id })
// 获得用户下拉列表
export const getMemeberUserList = async () => {
return await request.get({ url: `/member/user/member-list`})
}

View File

@ -1,56 +0,0 @@
import request from '@/config/axios'
// 钱包充值 VO
export interface WalletRechargeVO {
id: number // id
walletId: number // 钱包编号
totalPrice: number // 充值实际到账
payPrice: number // 实际支付金额
bonusPrice: number // 钱包赠送金额
packageId: number // 充值套餐编号
payStatus: boolean // 是否支付
payOrderId: number // 支付订单编号
payChannelCode: string // 支付成功的支付渠道
payTime: Date // 订单支付时间
payRefundId: number // 支付退款单编号
refundTotalPrice: number // 退款金额(包含赠送金额)
refundPayPrice: number // 退款支付金额
refundBonusPrice: number // 退款钱包赠送金额
refundTime: Date // 退款时间
refundStatus: number // 退款状态
name : string
avatar: string
}
// 钱包充值 API
export const WalletRechargeApi = {
// 查询钱包充值分页
getWalletRechargePage: async (params: any) => {
return await request.get({ url: `/pay/wallet-recharge/page`, params })
},
// 查询钱包充值详情
getWalletRecharge: async (id: number) => {
return await request.get({ url: `/pay/wallet-recharge/get?id=` + id })
},
// 新增钱包充值
createWalletRecharge: async (data: WalletRechargeVO) => {
return await request.post({ url: `/pay/wallet-recharge/create`, data })
},
// 修改钱包充值
updateWalletRecharge: async (data: WalletRechargeVO) => {
return await request.put({ url: `/pay/wallet-recharge/update`, data })
},
// 删除钱包充值
deleteWalletRecharge: async (id: number) => {
return await request.delete({ url: `/pay/wallet-recharge/delete?id=` + id })
},
// 导出钱包充值 Excel
exportWalletRecharge: async (params) => {
return await request.download({ url: `/pay/wallet-recharge/export-excel`, params })
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -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="appLink.path == activeAppLink.path ? 'primary' : 'default'"
:type="isSameLink(appLink.path, activeAppLink.path) ? 'primary' : 'default'"
@click="handleAppLinkSelected(appLink)">
{{ appLink.name }}
</el-button>
@ -109,14 +109,9 @@
// APP
const handleAppLinkSelected = (appLink : AppLink) => {
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")
if (!isSameLink(appLink.path, activeAppLink.value.path)) {
activeAppLink.value = appLink
console.log(activeAppLink.value,activeAppLink.value.path,"activeAppLink.value")
}
switch (appLink.type) {
case APP_LINK_TYPE_ENUM.PRODUCT_CATEGORY_LIST:

View File

@ -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,25 +78,13 @@ export const APP_LINK_GROUP_LIST = [
name: '系统设置',
path: '/pages/public/setting'
},
// {
// name: '常见问题',
// path: '/pages/public/faq'
// },
{
name: '常见问题',
path: '/pages/public/faq'
},
{
name: '积分商城',
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'
path: '/pages/public/faq'
}
]
},
@ -123,11 +111,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',
@ -207,12 +195,11 @@ export const APP_LINK_GROUP_LIST = [
{
name: '充值记录',
path: '/pages/pay/recharge-log'
},
{
name: '核销记录',
path: '/pages/pay/recharge-log'
}
// ,
// {
// name: '核销记录',
// path: '/pages/pay/recharge-log'
// }
]
},
{
@ -261,19 +248,7 @@ 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'
}
}
]
}
// ,

View File

@ -1,7 +1,7 @@
<template>
<el-scrollbar class="z-1 min-h-30px" wrap-class="w-full" ref="containerRef">
<div
class="new-class flex-row text-12px"
class="new-class flex flex-row text-12px"
:style="{
gap: `${property.space}px`,
width: scrollbarWidth
@ -13,13 +13,11 @@
background: property.bgImg
? `url(${property.bgImg}) 100% center / 100% 100% no-repeat`
: '#fff',
width: `150px`,
padding: `10px 10px`,
width: `${couponWidth}px`,
color: property.textColor
}"
v-for="(coupon, index) in couponList"
:key="index"
style="padding-left: 0px;padding-right: 4px;"
>
<!-- 布局11-->
<!-- <div v-if="property.columns === 1" class="m-l-16px flex flex-row justify-between p-8px">
@ -43,17 +41,16 @@
<!-- 布局22-->
<!-- v-else-if="property.columns === 2"s -->
<div
class="flex flex-row justify-between"
style=""
class="m-l-16px flex flex-row justify-between"
>
<div class="flex flex-col justify-evenly gap-4px" style="flex:1;">
<div class="flex flex-col justify-evenly gap-4px">
<!-- 优惠值 -->
<CouponDiscount :coupon="coupon" style="text-align:center;" />
<div style="text-align:center;">{{ coupon.name }}</div>
<CouponDiscount :coupon="coupon" />
<div>{{ coupon.name }}</div>
</div>
<div class="flex flex-col" style="writing-mode: vertical-rl;">
<div class="flex flex-col">
<div
class="h-full w-20px rounded-20px text-center"
class="h-full w-20px rounded-20px p-x-2px p-y-8px text-center"
:style="{
color: property.button.color,
background: property.button.bgColor
@ -141,11 +138,8 @@ onMounted(() => {
<style scoped lang="scss">
.new-class{
margin-left: 10px;
overflow-x:scroll;
white-space: nowrap;
.box-content{
margin-right:10px;
display:inline-block;
}
}
</style>

View File

@ -3,7 +3,7 @@
<div
v-for="(item, index) in property.list"
:key="index"
class="relative flex flex-col items-center"
class="relative flex flex-col items-center p-b-14px p-t-20px"
:style="{ width: `${100 * (1 / property.column)}%` }"
>
<!-- 右上角角标 -->
@ -14,11 +14,11 @@
>
{{ item.badge.text }}
</span>
<el-image v-if="item.iconUrl" class="h-40px w-40px" :src="item.iconUrl" />
<el-image v-if="item.iconUrl" class="h-28px w-28px" :src="item.iconUrl" />
<span class="m-t-8px h-16px text-12px leading-16px" :style="{ color: item.titleColor }">
{{ item.title }}
</span>
<span v-if="item.subtitle" class="m-t-6px h-12px text-10px leading-12px" :style="{ color: item.subtitleColor }">
<span class="m-t-6px h-12px text-10px leading-12px" :style="{ color: item.subtitleColor }">
{{ item.subtitle }}
</span>
</div>
@ -32,9 +32,4 @@ defineOptions({ name: 'MenuGrid' })
defineProps<{ property: MenuGridProperty }>()
</script>
<style scoped lang="scss">
.items-center{
margin:5px 0;
}
</style>
<style scoped lang="scss"></style>

View File

@ -1,80 +0,0 @@
import { ComponentStyle, DiyComponent } from '@/components/DiyEditor/util'
import { cloneDeep } from 'lodash-es'
/** 宫格导航属性 */
export interface MenuGridProperty {
// 列数
column: number
// 导航菜单列表
list: MenuGridItemProperty[]
// 组件样式
style: ComponentStyle
}
/** 宫格导航项目属性 */
export interface MenuGridItemProperty {
// 图标链接
iconUrl: string
// 标题
title: string
// 标题颜色
titleColor: string
// 副标题
subtitle: string
// 副标题颜色
subtitleColor: string
// 链接
url: string
// 角标
badge: {
// 是否显示
show: boolean
// 角标文字
text: string
// 角标文字颜色
textColor: string
// 角标背景颜色
bgColor: string
}
}
export const EMPTY_MENU_GRID_ITEM_PROPERTY = {
title: '标题',
titleColor: '#333',
subtitle: '副标题',
subtitleColor: '#bbb',
badge: {
show: false,
textColor: '#fff',
bgColor: '#FF6000'
}
} as MenuGridItemProperty
import logo from '@/assets/imgs/DiyEditorImges/组件图标-04.png'
// 定义组件
export const component = {
id: 'MenuGridTow',
name: '魔方',
// icon: 'bi:grid-3x3-gap',
icon: logo,
property: {
column: 3,
list: [cloneDeep(EMPTY_MENU_GRID_ITEM_PROPERTY)],
style: {
bgType: 'color',
bgColor: '#fff',
marginBottom: 8,
marginLeft: 8,
marginRight: 8,
padding: 8,
paddingTop: 8,
paddingRight: 8,
paddingBottom: 8,
paddingLeft: 8,
borderRadius: 8,
borderTopLeftRadius: 8,
borderTopRightRadius: 8,
borderBottomRightRadius: 8,
borderBottomLeftRadius: 8
} as ComponentStyle
}
} as DiyComponent<MenuGridProperty>

View File

@ -1,201 +0,0 @@
<template>
<div class='new_czbk'>
<div class="t">
<text class="left-font">超值爆款</text>
<div class="sub">美好生活由此开始</div>
</div>
<div class="new-list">
<!-- <div class="item" @click="sheep.$router.go('/pages/goods/sales', {
activityType: 'recommendBest',
});"> -->
<div class="item" >
<div class="nei">
<div class="l">
<div class="t">今日推荐</div>
<div class="c">店主诚意推荐品质商品</div>
<div class="b">
<!-- GO! -->
<img src="https://zysc.fjptzykj.com:3000/shangcheng/40a7582be7aeb5a6047415ab3a6728439a2798a4752ffcc1c063d66a534aa630.png"
class="img"/>
</div>
</div>
<div class="r">
<img
src="https://zysc.fjptzykj.com:3000/shangcheng/c80faeeeb2d6dadcdbe7e6d1a4caa06869b94301612cdf157414a10559b38267.png"
class="img"/>
</div>
</div>
</div>
<!-- <div class="item" @click="sheep.$router.go('/pages/goods/sales', {
activityType: 'recommendHot',
});"> -->
<div class="item">
<div class="nei">
<div class="l">
<div class="t">热门榜单</div>
<div class="c">店主诚意推荐品质商品</div>
<div class="b">
<!-- GO! -->
<img
src="https://zysc.fjptzykj.com:3000/shangcheng/5c72fc99989bf0a7e59b57f519c76c9a98cb20478384e20b1b934aa8f80cc598.png"
class="img"/>
</div>
</div>
<div class="r">
<img
src="https://zysc.fjptzykj.com:3000/shangcheng/904fd4848fde8025ccc17693662efd5b7bf5ec3f2656ecc7de407c8c46f910b2.png"
class="img"/>
</div>
</div>
</div>
<!-- <div class="item" @click="sheep.$router.go('/pages/goods/sales', {
activityType: 'recommendNew',
});"> -->
<div class="item">
<div class="nei">
<div class="l">
<div class="t">首发新品</div>
<div class="c">新品上架等 你来拿</div>
<div class="b">
<!-- GO! -->
<img
src="https://zysc.fjptzykj.com:3000/shangcheng/ebb2fc0512d1bd6a6d21a2c07f6603bfd2ea6f7e23377eb4cd8118d89d666589.png"
class="img"/>
</div>
</div>
<div class="r">
<img
src="https://zysc.fjptzykj.com:3000/shangcheng/7c71cbf2c757418302ea9ef2952893c24e1d46c4cc35a821f56be0f315bcabb0.png"
class="img"/>
</div>
</div>
</div>
<!-- <div class="item" @click="sheep.$router.go('/pages/goods/sales', {
activityType: 'recommendGood',
});"> -->
<div class="item">
<div class="nei">
<div class="l">
<div class="t">促销单品</div>
<div class="c">综合评选好 产品</div>
<div class="b">
<!-- GO! -->
<img
src="https://zysc.fjptzykj.com:3000/shangcheng/aac8baa7a6feb9ee3459d1cf75ba9a7e8bacb5d93c88ac95c1b0ecd29b847f96.png"
class="img"/>
</div>
</div>
<div class="r">
<img
src="https://zysc.fjptzykj.com:3000/shangcheng/8a61c35d82702c61f97ef3db43a8d6755b2eca3d78a22dbaa55851e815e96a23.png"
class="img" />
</div>
</div>
</div>
<div style="clear:both;"></div>
</div>
</div>
</template>
<script setup lang="ts">
import { MenuGridProperty } from './config'
/** 宫格导航 */
defineOptions({ name: 'MenuGridTow' })
defineProps<{ property: MenuGridProperty }>()
</script>
<style scoped lang="scss">
.new_czbk {
width: 100%;
background: rgba(255, 229, 227);
border-radius: 5px;
margin: 0 auto;
padding: 5px 0;
// margin-top: 10px;
padding-bottom: 10px;
padding-top: 10px;
&>.t {
display: flex;
align-items: center;
padding:0 10px;
.left-font {
color: rgba(252, 60, 62);
font-size: 17px;
font-weight: 700;
}
.sub {
background: rgba(248, 79, 43);
color: white;
border-radius: 20px 0px 20px 0px;
padding: 0 13px;
margin-left: 10px;
font-size: 13px;
}
}
.new-list {
// width: 100%;
margin-right:10px;
.item {
float: left;
width: 50%;
.nei {
margin: 10px;
margin-right:0;
margin-bottom: 0;
background: white;
border-radius: 10px;
padding: 8px 10px;
display: flex;
.l {
width: 50%;
.t {
// font-size: 17px;
}
.c {
color: rgba(153, 153, 153);
font-size: 12px;
margin: 3px 0;
}
.b {
// display: flex;
// align-items: center;
// justify-content: space-around;
// background: #329cff;
// margin: 0 3px;
// border-radius: 15px;
// color: white;
// font-weight: 700;
// padding: 0px 9px;
// font-style: oblique;
.img {
width: 76%;
height: 20px;
}
}
}
.r {
width: 49%;
.img {
width: 100%;
height: 100%;
}
}
}
}
}
}
</style>

View File

@ -1,107 +0,0 @@
<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> -->
<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-switch v-model="element.badge.show" />
</el-form-item>
<template v-if="element.badge.show">
<el-form-item label="角标内容" prop="badge.text">
<InputWithColor
v-model="element.badge.text"
v-model:color="element.badge.textColor"
/>
</el-form-item>
<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>
<script setup lang="ts">
import { usePropertyForm } from '@/components/DiyEditor/util'
import {
EMPTY_MENU_GRID_ITEM_PROPERTY,
MenuGridProperty
} from '@/components/DiyEditor/components/mobile/MenuGrid/config'
/** 宫格导航属性面板 */
defineOptions({ name: 'MenuGridProperty' })
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>

View File

@ -10,7 +10,7 @@
<div class="h-24px truncate leading-24px">{{ item.text }}</div>
</el-carousel-item>
</el-carousel>
<!-- <Icon icon="ep:arrow-right" /> -->
<Icon icon="ep:arrow-right" />
</div>
</template>

View File

@ -1,23 +1,5 @@
<template>
<div :class="`box-content min-h-30px w-full flex flex-row flex-wrap`" ref="containerRef">
<view class="new-fenlei">
<view class="list on">
<view class="t">首页新品</view>
<view class="b">最新出炉</view>
</view>
<view class="list">
<view class="t">精品推荐</view>
<view class="b">猜你喜欢</view>
</view>
<view class="list">
<view class="t">热门榜单</view>
<view class="b">好评如云</view>
</view>
<view class="list">
<view class="t">促销单品</view>
<view class="b">多买多销</view>
</view>
</view>
<div
class="relative box-content flex flex-row flex-wrap overflow-hidden bg-white"
:style="{
@ -182,44 +164,4 @@ const calculateWidth = () => {
}
</script>
<style scoped lang="scss">
.new-fenlei {
width: 100%;
display: flex;
margin:10px 0;
justify-content: space-around;
.list {
width: 100%;
display:flex;
flex-wrap:wrap;
&.on{
.t {
color: #e93422;
}
.b {
background: #e93422;
color:white;
}
}
.t {
width:100%;
text-align: center;
font-size: 16px;
}
.b {
// background: rgba(255, 102, 7);
text-align: center;
color: rgba(153,153,153);
border-radius: 15px;
width: 63%;
margin: 0 auto;
font-size: 12px;
margin-top:5px;
}
}
}
</style>
<style scoped lang="scss"></style>

View File

@ -27,8 +27,8 @@
:style="{
gridGap: `${property.space}px`,
gridTemplateColumns,
width: scrollbarWidth
}"
style="padding:0 10px;padding-bottom: 8px;"
>
<!-- 商品 -->
<div
@ -53,7 +53,7 @@
<el-image fit="cover" :src="spu.picUrl" :style="{ width: imageSize, height: imageSize }" />
<div
:class="[
'flex flex-col p-8px box-border',
'flex flex-col gap-8px p-8px box-border',
{
'w-[calc(100%-64px)]': columns === 2,
'w-full': columns === 3
@ -79,7 +79,6 @@
</span>
</div>
</div>
<view class="sss">参与拼团</view>
</div>
</div>
</el-scrollbar>
@ -146,18 +145,9 @@ onMounted(() => {
</script>
<style scoped lang="scss">
.sss {
width: 100%;
// margin-top: 10px;
padding: 3px;
font-size: 13px;
background: #e93422;
text-align: center;
color: white;
border-radius: 0px 0px 5px 5px;
}
.new-class{
padding:0 0;
padding: 10px;
// width:42%;
}

View File

@ -10,12 +10,7 @@
src="https://zysc.fjptzykj.com:3000/shangcheng/cb995c399d784c08e27d8f56b0a63ace2d13af3a1ee6aba5a2da71868dc4cf00.png" />
<span class="new-text">限时秒杀</span>
</div>
<div style="color: rgba(255, 51, 35, 1); font-size:15px;" class="title-text">
<span class="time">05</span>:
<span class="time">00</span>:
<span class="time">00</span>
</div>
<span class="text-16px" style="color: rgb(187, 187, 187);">已有99人购买</span>
</div>
<div class="item-center flex flex-row justify-center gap-4px">
<span class="text-12px" style="color: rgb(187, 187, 187);">查看更多</span>
@ -28,8 +23,10 @@
<!-- 商品网格 -->
<!-- gridGap: `${property.space}px`, -->
<div class="new-main" :style="{
gridTemplateColumns,
}" style="padding:0 10px;">
gridTemplateColumns,
width: scrollbarWidth
}">
<!-- 商品 -->
<!--
:style="{
@ -47,14 +44,19 @@
</div>
<!-- 商品封面图 -->
<el-image fit="cover" :src="spu.picUrl" :style="{ width: imageSize, height: imageSize }" />
<div>
<div :class="[
'flex flex-col gap-8px p-8px box-border',
{
'w-[calc(100%-64px)]': columns === 2,
'w-full': columns === 3
}
]">
<!-- 商品名称 -->
<div v-if="property.fields.name.show" class="truncate text-12px"
:style="{ color: property.fields.name.color }" style="margin: 5px 0;">
:style="{ color: property.fields.name.color }">
{{ spu.name }}
</div>
<div style="display: flex;">
<div class="fff"></div>
<div>
<!-- 商品价格 -->
<span v-if="property.fields.price.show" class="text-12px"
:style="{ color: property.fields.price.color }">
@ -127,24 +129,6 @@
</script>
<style scoped lang="scss">
.fff {
padding: 1px 3px;
font-size: 8px;
background: #e93422;
color: white;
border-radius: 2px;
margin-right: 2px;
}
.time {
width: 20px;
height: 20px;
background: rgba(255, 237, 238, 1);
color: rgba(255, 51, 35, 1);
text-align: center;
padding: 0 3px;
}
:deep(.el-scrollbar__view) {
background-color: white;
border-radius: 12px;
@ -152,35 +136,27 @@
.new-class {
width: 33%;
border-radius: 8px 8px 8px 0px;
padding-bottom: 10px;
// padding: 10px;
padding: 10px;
// width:42%;
}
.new-main {
display: flex;
gap: 8px;
grid-template-columns: repeat(3, auto);
padding: 0px 10px 8px;
padding-bottom: 10px;
}
// :deep(.el-image) {
// width: 100% !important;
// }
:deep(.el-image) {
width: 100% !important;
}
.wh {
position: relative;
padding-right: 10px;
display: flex;
align-items: center;
.new-text1 {
width: 30px;
.new-text1{
width:30px;
}
.new-text {
.new-text{
font-weight: 700;
}
}

View File

@ -120,8 +120,7 @@
<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" />
<!-- <Qrcode :text="previewUrl" logo="/logo.gif" />-->
<Qrcode :text="previewUrl" logo="/logo.gif" />
</div>
</div>
</Dialog>

View File

@ -118,8 +118,7 @@ export const PAGE_LIBS = [
'MenuGrid',
'MenuList',
'Popover',
'FloatingActionButton',
"MenuGridTow"
'FloatingActionButton'
]
},
{

View File

@ -35,120 +35,88 @@ const mobile = computed(() => appStore.getMobile)
// 固定菜单
const fixedMenu = computed(() => appStore.getFixedMenu)
import { useRoute } from 'vue-router'
export const useRenderLayout = () => {
const route = useRoute()
let renderClassic = null;
if(route.path == "/kefu/kefu"){
renderClassic = () => {
return (
<>
const renderClassic = () => {
return (
<>
<div
class={[
'absolute top-0 left-0 h-full layout-border__right',
{ '!fixed z-3000': mobile.value }
]}
>
{logo.value ? (
<Logo
class={[
'bg-[var(--left-menu-bg-color)] relative',
{
'!pl-0': mobile.value && collapse.value,
'w-[var(--left-menu-min-width)]': appStore.getCollapse,
'w-[var(--left-menu-max-width)]': !appStore.getCollapse
}
]}
style="transition: all var(--transition-time-02);"
></Logo>
) : undefined}
<Menu class={[{ '!h-[calc(100%-var(--logo-height))]': logo.value }]}></Menu>
</div>
<div
class={[
`${prefixCls}-content`,
'absolute top-0 h-[100%]',
{
'w-[calc(100%-var(--left-menu-min-width))] left-[var(--left-menu-min-width)]':
collapse.value && !mobile.value && !mobile.value,
'w-[calc(100%-var(--left-menu-max-width))] left-[var(--left-menu-max-width)]':
!collapse.value && !mobile.value && !mobile.value,
'fixed !w-full !left-0': mobile.value
}
]}
style="transition: all var(--transition-time-02);"
>
<ElScrollbar
v-loading={pageLoading.value}
class={[
`${prefixCls}-content-scrollbar`,
{
'!h-[calc(100%-var(--top-tool-height)-var(--tags-view-height))] mt-[calc(var(--top-tool-height)+var(--tags-view-height))]':
fixedHeader.value
}
]}
>
<div
class={[
{
'fixed top-0 left-0 z-10': fixedHeader.value,
'w-[calc(100%-var(--left-menu-min-width))] !left-[var(--left-menu-min-width)]':
collapse.value && fixedHeader.value && !mobile.value,
'w-[calc(100%-var(--left-menu-max-width))] !left-[var(--left-menu-max-width)]':
!collapse.value && fixedHeader.value && !mobile.value,
'!w-full !left-0': mobile.value
}
]}
style="transition: all var(--transition-time-02);"
>
<ToolHeader
class={[
'bg-[var(--top-header-bg-color)]',
{
'layout-border__bottom': !tagsView.value
}
]}
></ToolHeader>
<div
{tagsView.value ? (
<TagsView class="layout-border__top layout-border__bottom"></TagsView>
) : undefined}
</div>
style="transition: all var(--transition-time-02);width:83%;margin:0 auto;"
>
<ElScrollbar
v-loading={pageLoading.value}
class={[
`${prefixCls}-content-scrollbar`,
{
}
]}
>
<AppView></AppView>
</ElScrollbar>
</div>
</>
)
}
}else {
renderClassic = () => {
return (
<>
<div
class={[
'absolute top-0 left-0 h-full layout-border__right',
{ '!fixed z-3000': mobile.value }
]}
>
{logo.value ? (
<Logo
class={[
'bg-[var(--left-menu-bg-color)] relative',
{
'!pl-0': mobile.value && collapse.value,
'w-[var(--left-menu-min-width)]': appStore.getCollapse,
'w-[var(--left-menu-max-width)]': !appStore.getCollapse
}
]}
style="transition: all var(--transition-time-02);"
></Logo>
) : undefined}
<Menu class={[{ '!h-[calc(100%-var(--logo-height))]': logo.value }]}></Menu>
</div>
<div
class={[
`${prefixCls}-content`,
'absolute top-0 h-[100%]',
{
'w-[calc(100%-var(--left-menu-min-width))] left-[var(--left-menu-min-width)]':
collapse.value && !mobile.value && !mobile.value,
'w-[calc(100%-var(--left-menu-max-width))] left-[var(--left-menu-max-width)]':
!collapse.value && !mobile.value && !mobile.value,
'fixed !w-full !left-0': mobile.value
}
]}
style="transition: all var(--transition-time-02);"
>
<ElScrollbar
v-loading={pageLoading.value}
class={[
`${prefixCls}-content-scrollbar`,
{
'!h-[calc(100%-var(--top-tool-height)-var(--tags-view-height))] mt-[calc(var(--top-tool-height)+var(--tags-view-height))]':
fixedHeader.value
}
]}
>
<div
class={[
{
'fixed top-0 left-0 z-10': fixedHeader.value,
'w-[calc(100%-var(--left-menu-min-width))] !left-[var(--left-menu-min-width)]':
collapse.value && fixedHeader.value && !mobile.value,
'w-[calc(100%-var(--left-menu-max-width))] !left-[var(--left-menu-max-width)]':
!collapse.value && fixedHeader.value && !mobile.value,
'!w-full !left-0': mobile.value
}
]}
style="transition: all var(--transition-time-02);"
>
<ToolHeader
class={[
'bg-[var(--top-header-bg-color)]',
{
'layout-border__bottom': !tagsView.value
}
]}
></ToolHeader>
{tagsView.value ? (
<TagsView class="layout-border__top layout-border__bottom"></TagsView>
) : undefined}
</div>
<AppView></AppView>
</ElScrollbar>
</div>
</>
)
}
}
<AppView></AppView>
</ElScrollbar>
</div>
</>
)
}
const renderTopLeft = () => {
return (

View File

@ -124,13 +124,8 @@ 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',
//预约:项目
SUBSCRIBE_PROJECT_STATUS = 'subscribe_project_status',

View File

@ -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: '众悦e家',
username: 'admin',
password: 'admin123',
tenantName: '雲頂玖號',
username: 'ydadmin',
password: '123456',
captchaVerification: '',
rememberMe: true //
}

View File

@ -1,470 +1,376 @@
<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: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>
<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>
</div>
</div>
<!-- 内容区域 -->
<div class="content-wrap">
<!-- 内容区域 -->
<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 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>
<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: 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"/>
<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> -->
<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>
<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" />
<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>
</div>
</div>
<!-- 表单弹窗添加/修改 -->
<FileForm ref="formRef" @success="getList" />
</div>
<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">
<!-- 分页 -->
<Pagination :total="total" v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"/>
<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>
</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>
<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'
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) // 12
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 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) // 12
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 loading = ref(true) //
const total = ref(0) //
const list = ref([]) //
const menuId = ref()
const loading = ref(true) //
const total = ref(0) //
const list = ref([]) //
const menuId = ref()
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
name: undefined,
type: undefined,
picType: '',
path: undefined,
createTime: [],
})
const queryFormRef = ref() //
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
name: undefined,
type: undefined,
picType: '',
path: undefined,
createTime: [],
})
const queryFormRef = 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 createType = () => {
dialogVisibles.value = true
dialogTitles.value = '新增分类'
}
/** 添加分类菜单 */
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
}
}
//
const submitForms = async () => {
//
if (!formRef) return
const valid = await formRef.value.validate()
if (!valid) return
//
formLoading.value = true
try {
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
console.log('1111111111', typeMenu)
}
// const hoveredIndex = ref(null);
/** */
const clickMenu = (id : string) => {
queryParams.picType = id
queryParams.pageNo = 1
getList()
}
// 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
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
interface FileDataVO {
id: string
configId: string
path: string
name: string
url: string
type: string
picType: string
size: string
createTime: Date
}
const tubiao = () => {
panduan.value = '2'
}
const liebiao = () => {
panduan.value = '1'
}
//
const createType = () => {
dialogVisibles.value = true
dialogTitles.value = '新增分类'
}
/** 添加分类菜单 */
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
}
}
//
const submitForms = async () => {
//
if (!formRef) return
const valid = await formRef.value.validate()
if (!valid) return
//
formLoading.value = true
try {
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 resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
/** */
const clickMenu = (id: string) => {
queryParams.picType = id
queryParams.pageNo = 1
getList()
}
const formRef = ref()
const openForm = () => {
formRef.value.open()
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
const forRef = ref()
/** 修改操作 */
const updateForm = (type : string, id : number, picType : number) => {
forRef.value.open(type, id, picType)
}
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 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;
}
.menu-area {
padding: 25px;
background-color: white;
width: 180px;
height: 700px;
/* 固定高度 */
overflow-y: hidden;
/* 禁止滚动 */
border-right: 1px solid #e0e0e0;
/* 可选:添加分隔线 */
}
.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; /* 如果名称很长,自动换行 */
}
.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;
/* 可选:添加分隔线 */
}
.content-wrap {
flex-grow: 1;
/* 使内容区域占据剩余空间 */
overflow-y: auto;
/* 允许内容区域滚动 */
}
</style>

View File

@ -1,118 +0,0 @@
<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) // 12
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>

View File

@ -1,228 +0,0 @@
<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>

View File

@ -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>

View File

@ -7,8 +7,16 @@
label-width="100px"
v-loading="formLoading"
>
<el-form-item label="用户昵称" prop="nickname">
<el-select v-model ="formData.nickname" placeholder="请选择用户昵称" class="!w-240px">
<el-option v-for ="item in userlist"
:key="item.id"
:label="item.nickname"
:value="item.nickname"/>
</el-select>
</el-form-item>
<el-form-item label="用户编号" prop="userId">
<el-input v-model="formData.userId" placeholder="请输入用户编号" />
<el-input v-model="formData.userId" placeholder="请输入用户编号" :disabled="true"/>
</el-form-item>
<el-form-item label="发表内容" prop="content">
<Editor v-model="formData.content" height="150px" />
@ -25,9 +33,6 @@
<el-form-item label="评论数量" prop="commentCount">
<el-input v-model="formData.commentCount" placeholder="请输入评论数量" />
</el-form-item>
<!-- <el-form-item label="更新版本号" prop="version">
<el-input v-model="formData.version" placeholder="请输入更新版本号" />
</el-form-item> -->
</el-form>
<template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button>
@ -37,6 +42,7 @@
</template>
<script setup lang="ts">
import { CircleApi, CircleVO } from '@/api/mall/promotion/circle'
import * as UserVoApid from '@/api/member/user'
/** 商城动态 表单 */
defineOptions({ name: 'CircleForm' })
@ -50,6 +56,7 @@ const formLoading = ref(false) // 表单的加载中1修改时的数据加
const formType = ref('') // create - update -
const formData = ref({
id: undefined,
nickname: undefined,
userId: undefined,
content: undefined,
picUrl: undefined,
@ -104,10 +111,21 @@ const submitForm = async () => {
}
}
// 使watchformData.nickname
watch(() => formData.value.nickname, (newValue) => {
const selectedUser = userlist.value.find(user => user.nickname === newValue);
if (selectedUser) {
formData.value.userId = selectedUser.id;
} else {
formData.value.userId = ''; //
}
});
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
nickname: undefined,
userId: undefined,
content: undefined,
picUrl: undefined,
@ -118,4 +136,10 @@ const resetForm = () => {
}
formRef.value?.resetFields()
}
const userlist = ref<UserVoApid.UserVO[]>([])//
//
onMounted(async () =>{
userlist.value = (await UserVoApid.getMemeberUserList()) as UserVoApid.UserVO[]
})
</script>

View File

@ -93,12 +93,16 @@
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table-column label="编号" align="center" prop="id" />
<el-table-column label="用户编号" align="center" prop="userId" />
<el-table-column label="用户昵称" align="center" prop="nickname" />
<el-table-column label="发表内容" align="center" prop="content" />
<el-table-column label="图片地址" align="center" prop="picUrl" />
<el-table-column label="发表图片" align="center" prop="picUrl" >
<template #default="scope">
<img :src="scope.row.picUrl" style="width: 40px"/>
</template>
</el-table-column>
<el-table-column label="点赞量" align="center" prop="likeCount" />
<el-table-column label="访问量" align="center" prop="lookCount" />
<el-table-column label="评论数量" align="center" prop="commentCount" />
<!-- <el-table-column label="更新版本号" align="center" prop="version" /> -->
<el-table-column
label="创建时间"
align="center"

View File

@ -1,141 +0,0 @@
<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;">&nbsp;&nbsp;&nbsp;企业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>

View File

@ -1,124 +0,0 @@
<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>

View File

@ -1,215 +0,0 @@
<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>

View File

@ -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" :showTabBar = "false">
:preview-url="previewUrl" @save="submitForm" @reset="handleEditorReset">
<template #toolBarLeft>
<!-- <el-radio-group
v-model="selectedTemplateItem"

View File

@ -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>

View File

@ -18,10 +18,7 @@
color: #000000;
background-color:#ffffff;
border-color: #fdfdff;">{{o.title}}</span>
<el-button style="float: right;" type="text" @click="deleteForm(o.id)">&nbsp;&nbsp;&nbsp;删除</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>
<el-button style="float: right; padding: 3px 0" type="text" @click="handelExtend(o.id)">推广</el-button>
</div>
<div style="display: flex;justify-content: space-between">
<div style="width: 30%">
@ -34,8 +31,8 @@
<el-link style="font-size: 18px;
color: #999595;
background-color:#ffffff;
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>
border-color: #fdfdff;" @click="extendList(o.id)">采集数量</el-link>
<p style="font-size: 25px" @click="extendList(o.id)">{{o.collectionQuantity}}</p>
</div>
</div>
<div style="display: flex;justify-content: space-between">
@ -57,18 +54,15 @@
<div style="text-align: center">
<!-- <el-form-item label="表单标题">-->
<div>
<span>标题</span>
<el-input
style="border-left: none; border-right: none; border-top: none; border-bottom: 1px solid skyblue; margin-bottom: 10px"
<div><span>标题</span> <el-input style="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="请输入字段" />
<el-input style="border: 1px solid #DCDFE6;border-radius: 3px; "
v-model="inputarr[index]" placeholder="请输入字段" />
</div>
<div>
<el-button @click="adinput"
@ -91,71 +85,34 @@
</el-dialog>
<!-- 采集数据列表 -->
<el-dialog :title="collectTitle" v-model="collectOpen" append-to-body>
<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" /> -->
<el-table-column type="selection" width="55" align="center" />
<div>
<template v-for="(item, index) in getColumnNames" :key="index">
<el-table-column
:label="capitalize(item)"
:prop="item"
/>
</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>
</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 {Message} from 'element-ui';
import {FollowUpRecordApi} from "@/api/crm/intelligent.ts";
export default {
name: "Business",
dicts: ['stage_type', 'sales_stage'],
data() {
return {
//
redactTitle: "编辑",
//
redactOpen: false,
//id
redactId: '',
//
redactName: '',
id: '',
inputarr: [''],
//
@ -185,13 +142,12 @@
//广
extendLink: '',
//
collectLink: [],
collectLink: [{}],
//
customerList: [],
//
cardList: [],
addParams: {
id: '',
title: '',
pageView: '',
collectionQuantity: '',
@ -202,38 +158,23 @@
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;
},
//
@ -250,25 +191,6 @@
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 = []
@ -284,53 +206,6 @@
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');
// Viewportmeta
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() {
@ -343,17 +218,14 @@
alert('已复制到剪贴板');
},
//
extendList(id,title) {
FollowUpRecordApi.collectDataList(id).then(response => {
this.collectLink = response
console.log('111111: ' + JSON.stringify(response));
});
extendList(id) {
this.collectLink = null;
this.id = id
this.collectOpen = true;
this.collectTitle = title+"采集数据";
this.collectTitle = "采集数据";
collectDataList(this.id).then(response => {
this.collectLink = response.data
});
},
//
handleSelectionChange(selection) {
@ -404,6 +276,6 @@
::v-deep.el-input--medium .el-input__inner {
border: none;
border-bottom: 1px solid blue;
/* border-bottom:1px solid blue; */
}
</style>

View File

@ -1,6 +1,5 @@
<template>
<div class="kefu">
<div
v-for="item in conversationList"
:key="item.id"
@ -10,7 +9,7 @@
@contextmenu.prevent="rightClick($event as PointerEvent, item)"
>
<div class="flex justify-center items-center w-100%">
<div class="flex justify-center items-center w-40px h-40px">
<div class="flex justify-center items-center w-50px h-50px">
<!-- 头像 + 未读 -->
<el-badge
:hidden="item.adminUnreadMessageCount === 0"
@ -20,10 +19,10 @@
<el-avatar :src="item.userAvatar" alt="avatar" />
</el-badge>
</div>
<div class="ml-3px w-full">
<div class="flex justify-between items-center w-full">
<div class="ml-10px w-100%">
<div class="flex justify-between items-center w-100%">
<span class="username">{{ item.userNickname }}</span>
<span class="color-[var(--left-menu-text-color)]" style="font-size: 12px">
<span class="color-[var(--left-menu-text-color)]" style="font-size: 13px">
{{ formatPast(item.lastMessageTime, 'YYYY-MM-DD') }}
</span>
</div>
@ -36,11 +35,9 @@
>
</div>
</div>
</div>
</div>
<!-- 右键进行操作类似微信 -->
<ul v-show="showRightMenu" :style="rightMenuStyle" class="right-menu-ul">
<li
@ -89,8 +86,8 @@ const activeConversationId = ref(-1) // 选中的会话
const collapse = computed(() => appStore.getCollapse) //
/** 加载会话列表 */
const getConversationList = async (name: string) => {
const list = await KeFuConversationApi.getConversationListByKefuId(getStaffToken(),name)
const getConversationList = async () => {
const list = await KeFuConversationApi.getConversationListByKefuId(getStaffToken())
list.sort((a: KeFuConversationRespVO, _) => (a.adminPinned ? -1 : 1))
conversationList.value = list
}
@ -184,18 +181,15 @@ watch(showRightMenu, (val) => {
<style lang="scss" scoped>
.kefu {
background-color: white;
&-conversation {
height: 50px;
padding: 5px;
height: 60px;
padding: 10px;
//background-color: #fff;
transition: border-left 0.05s ease-in-out; /* 设置过渡效果 */
.username {
font-size: 15%;
min-width: 0;
max-width: 50%;
max-width: 60%;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
@ -204,8 +198,8 @@ watch(showRightMenu, (val) => {
}
.last-message {
font-size: 15px;
width: 170px;
font-size: 13px;
width: 200px;
overflow: hidden; //
white-space: nowrap; //
text-overflow: ellipsis; //

View File

@ -1,19 +1,17 @@
<template>
<el-container v-if="showKeFuMessageList" style="background-color: white;height:98%" class="kefu">
<!-- <el-header>
<el-container v-if="showKeFuMessageList" class="kefu">
<el-header>
<div class="kefu-title">{{ conversation.userNickname }}</div>
</el-header> -->
<!-- <ContentWrap></ContentWrap> -->
<el-main class="kefu-content overflow-visible">
<el-scrollbar ref="scrollbarRef" always height="calc(100vh - 350px)" @scroll="handleScroll" style="overflow: hidden;">
</el-header>
<el-main class="kefu-content overflow-visible">
<el-scrollbar ref="scrollbarRef" always height="calc(100vh - 495px)" @scroll="handleScroll">
<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">
<!-- 日期 -->
<div v-if="
<div
v-if="
item.contentType !== KeFuMessageContentTypeEnum.SYSTEM && showTime(item, index)
" class="date-message">
{{ formatDate(item.createTime) }}
@ -23,18 +21,19 @@
{{ item.content }}
</div>
</div>
<div :class="[
<div
:class="[
item.senderType === UserTypeEnum.MEMBER
? `ss-row-left`
: item.senderType === UserTypeEnum.ADMIN
? `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 }"
<div
:class="{ 'kefu-message': KeFuMessageContentTypeEnum.TEXT === item.contentType }"
class="p-10px">
<!-- 文本消息 -->
<MessageItem :message="item">
@ -45,13 +44,15 @@
</MessageItem>
<!-- 图片消息 -->
<MessageItem :message="item">
<el-image v-if="KeFuMessageContentTypeEnum.IMAGE === item.contentType"
<el-image
v-if="KeFuMessageContentTypeEnum.IMAGE === item.contentType"
:initial-index="0" :preview-src-list="[item.content]" :src="item.content"
class="w-200px" fit="contain" preview-teleported />
</MessageItem>
<!-- 商品消息 -->
<MessageItem :message="item">
<ProductItem v-if="KeFuMessageContentTypeEnum.PRODUCT === item.contentType"
<ProductItem
v-if="KeFuMessageContentTypeEnum.PRODUCT === item.contentType"
:spuId="getMessageContent(item).spuId" :picUrl="getMessageContent(item).picUrl"
:price="getMessageContent(item).price"
:skuText="getMessageContent(item).introduction"
@ -60,60 +61,57 @@
</MessageItem>
<!-- 订单消息 -->
<MessageItem :message="item">
<OrderItem v-if="KeFuMessageContentTypeEnum.ORDER === item.contentType"
<OrderItem
v-if="KeFuMessageContentTypeEnum.ORDER === item.contentType"
:message="item" class="max-w-100%" />
</MessageItem>
</div>
<el-avatar style="margin-right: 12px;" v-if="item.senderType === UserTypeEnum.ADMIN" :src="item.senderAvatar"
<el-avatar
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"
<div
v-show="showNewMessageTip" class="newMessageTip flex items-center cursor-pointer"
@click="handleToNewMessage">
<span>有新消息</span>
<Icon class="ml-5px" icon="ep:bottom" />
</div>
</el-main>
<el-divider style="margin: 4px 0;" />
<el-footer height="186px">
<el-footer height="230px">
<div class="h-[100%]">
<div class="flex items-center">
<div class="chat-tools 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" /> -->
<!-- 话术库 -->
<div style="margin-left: 15px; margin-top:4px;cursor: pointer;">
<img :src="xiaoxi" class="w-22px h-22px" @click="huashu" />
<div style="margin-left: 9px; margin-top:5px;cursor: pointer;">
<img :src="Picture" class="w-32px h-32px" @click="huashu" />
</div>
<!-- 转接按钮 -->
<el-dropdown placement="top"
style="margin-left: auto;margin-right: 15px;cursor: pointer;"
ref="dropdown1" trigger="contextmenu">
<el-dropdown placement="top" style="margin-left: auto;margin-right: 15px; margin-top:5px;margin-top:5px;cursor: pointer;" ref="dropdown1" trigger="contextmenu">
<div>
<img :src="Picture2" class="w-62px h-20px" @click="getOnlineStaffList" title="转接" />
<img :src="Picture2" class="w-27px h-27px" @click="getOnlineStaffList" title="转接"/>
</div>
<template #dropdown>
<el-dropdown-item v-for="staff in onlineStaffList" :key="staff.id"
:disabled="staff.id===getStaffToken()" @click="transferConversion(staff.id)">
<el-dropdown-item v-for="staff in onlineStaffList" :key="staff.id" :disabled="staff.id===getStaffToken()" @click="transferConversion(staff.id)">
{{ staff.name }}
</el-dropdown-item>
</template>
</el-dropdown>
</div>
<br/>
<textarea style="border: none; outline: none;" v-model="message" placeholder="请输入文字内容" rows="5" cols="75"></textarea>
<el-input v-model="message" :rows="6" style="border-style: none" type="textarea" />
<div class="h-45px flex justify-end">
<el-button class="mt-5px" type="primary" @click="handleSendMessage">发送</el-button>
<el-button class="mt-10px" type="primary" @click="handleSendMessage">发送</el-button>
</div>
</div>
</el-footer>
</el-container>
<el-empty style="background-color: white;" v-else description="请选择左侧的一个会话后开始" />
<el-empty v-else description="请选择左侧的一个会话后开始" />
<Dialog :title="dialogTitle" v-model="dialogVisible" :modal="false" width="700px">
@ -122,15 +120,12 @@
<!-- 左边占 30% -->
<div style="flex: 0 0 20%; padding: 10px;">
<el-menu :default-active="targetMenuId">
<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-item v-for="item in huashuType" :index="item.value" :key="item.value" @click="clickMenu(item.value)">{{item.label}}</el-menu-item>
</el-menu>
</div>
<!-- 右边占 70% -->
<div style="flex: 1; padding: 5px; overflow-y: auto; max-height: 400px;">
<p v-for="item in verbalTrickList" :key="item.id"
style="font-size: 12px;cursor: pointer;transition: background-color 0.3s;" class="hover-shadow"
@click="huashuClick(item.details)">
<p v-for="item in verbalTrickList" :key="item.id" style="font-size: 12px;cursor: pointer;transition: background-color 0.3s;" class="hover-shadow" @click="huashuClick(item.details)">
{{item.details}}
</p>
</div>
@ -148,8 +143,7 @@
import PictureSelectUpload from './tools/PictureSelectUpload.vue'
// import VerbalTrick from './tools/VerbalTrick.vue'
import Picture from '@/views/mall/promotion/kefu/components/asserts/huashu.png'
import xiaoxi from '@/views/mall/promotion/kefu/components/asserts/xiaoxi.png'
import Picture2 from '@/views/mall/promotion/kefu/components/asserts/zj.png'
import Picture2 from '@/views/mall/promotion/kefu/components/asserts/zhuanjie.png'
import ProductItem from './message/ProductItem.vue'
import OrderItem from './message/OrderItem.vue'
import { Emoji, useEmoji } from './tools/emoji'
@ -161,7 +155,7 @@
import relativeTime from 'dayjs/plugin/relativeTime'
import { debounce } from 'lodash-es'
import { jsonParse } from '@/utils'
import { getStaffToken, setStaffToken } from '@/utils/auth'
import { getStaffToken, setStaffToken} from '@/utils/auth'
import type { DropdownInstance } from 'element-plus'
import { ref } from 'vue'
@ -176,7 +170,7 @@
let dialogVisible = ref(false) //
const dialogTitle = ref('客服话术') //
//
const verbalTrickList = ref<VerbalTrickVO[]>([])
const verbalTrickList = ref<VerbalTrickVO[]>([])
const message = ref('') //
const { replaceEmoji } = useEmoji()
const messageTool = useMessage()
@ -184,11 +178,9 @@
const conversation = ref<KeFuConversationRespVO>({} as KeFuConversationRespVO) //
const showNewMessageTip = ref(false) //
import { SupportStaffApi, SupportStaffVO } from '@/api/mall/promotion/supportstaff' //
import { KeFuConversationApi } from '@/api/mall/promotion/kefu/conversation'
import { KeFuConversationApi} from '@/api/mall/promotion/kefu/conversation'
import { number } from 'vue-types'
const onlineStaffList = ref<SupportStaffVO[]>([]) // 线
const messages = useMessage() //
const kefuName = ref('')
const queryParams = reactive({
pageNo: 1,
@ -261,7 +253,6 @@
/** 获得新会话的消息列表 */
// TODO @puhui999 list merge
const getNewMessageList = async (val : KeFuConversationRespVO) => {
// console.log('22222222',val)
// ,
queryParams.pageNo = 1
messageList.value = []
@ -306,21 +297,22 @@
//
const getHuaShuTypeList = async () => {
const data = await DictDataApi.getHuaShuTypeList()
huashuType.value = data
huashuType.value = data
}
//
const getVerbalTrickList = async (id : string) => {
const getVerbalTrickList = async (id: string) => {
const response = await VerbalTrickApi.getVerbalTrickList(id);
verbalTrickList.value = response; // verbalTrickList
verbalTrickList.value = response; // verbalTrickList
}
/*选择话术库内容*/
const huashuClick = (content : string) => {
const huashuClick = (content: string) => {
message.value = content;
dialogVisible.value = false;
}
const clickMenu = (id : string) => {
const clickMenu = (id: string) => {
console.log('1111111111',id)
getVerbalTrickList(id)
}
@ -428,26 +420,14 @@
status: 1,
})
onlineStaffList.value = data.list
} finally {
}
}
/** 转接客服人员列表 id会话id kefuId客服人员id */
const transferConversion = async (kefuId : number) => {
const transferConversion = async (kefuId: number) => {
try {
const re = await KeFuConversationApi.transferConversion(queryParams.conversationId, kefuId)
kefuName.value = re
// 2.
const msg = {
conversationId: conversation.value.id,
contentType: KeFuMessageContentTypeEnum.SYSTEM,
content: '已为您转接至' + re,
}
await sendMessage(msg)
messages.success('转接成功')
conversation.value = null
await KeFuConversationApi.transferConversion(queryParams.conversationId, kefuId)
} finally {
// todo
}
@ -455,20 +435,15 @@
/** 初始化 **/
onMounted(() => {
getHuaShuTypeList()
getVerbalTrickList(targetMenuId.value)
getVerbalTrickList(targetMenuId.value)
})
</script>
<style lang="scss" scoped>
.hover-shadow:hover {
background-color: lightgray;
/* 可根据需要调整颜色 */
background-color: lightgray; /* 可根据需要调整颜色 */
}
.kefu {
&-title {
border-bottom: #e4e0e0 solid 1px;
@ -498,7 +473,6 @@
margin-left: 20px;
position: relative;
&::before {
content: '';
width: 10px;
@ -540,8 +514,7 @@
//
.kefu-message {
font-size: 14px;
color: #101010;
color: #a9a9a9;
border-radius: 5px;
box-shadow: 3px 3px 5px rgba(220, 220, 220, 0.1);
padding: 5px 10px;

View File

@ -1,62 +0,0 @@
<template>
<div style="height: 522px ;" ref="userInfoRef" >
<div>
<span style="display: flex;">
<el-avatar
style="border: 1px solid #f8f9ee;"
src="https://zysc.fjptzykj.com:3000/shangcheng/29e07b7973621a1a9679744e18f2b4efd1fd50d857fe594879e813d67be3e2db.png"
/>
<span style="margin-left:5px;margin-top: 9px;">Marvin</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 ;">暂无</span>
</div>
<div style="margin-top: 5px;">
<span style="color: #5d5d59;font-size: 13px ;">分组</span><span style="margin-left: 60px;font-size: 14px ;">D类客户</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 ;">暂无</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>
<span style="color: #5d5d59;font-size: 13px ;">余额</span><span style="margin-left: 60px;font-size: 14px ;">暂无</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 ;">小客户</span>
</div>
<el-divider style="border-color: #f5f5f5;margin-top: 15px;margin-bottom:15px;" />
</div>
</template>
<script >
import * as UserApi from '@/api/member/user'
// const user = ref<UserApi.UserVO>({} as UserApi.UserVO)
// const getUserId = async (id: string) => {
// user.value = await UserApi.getUserInfo(id)
// }
// defineExpose({ getUserId })
</script>
<style>
</style>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 994 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -1,19 +1,12 @@
<!-- 目录是不是叫 member 好点然后这个组件是 MemberInfo里面有浏览足迹 -->
<template>
<div v-show="!isEmpty(conversation)" class="kefu">
<!-- <div class="header-title h-60px flex justify-center items-center">他的足迹</div> -->
<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" />
<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>
<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;">
<div>
<el-scrollbar ref="scrollbarRef" always height="calc(115vh - 400px)" @scroll="handleScroll">
<!-- 最近浏览 -->
<ProductBrowsingHistory v-if="activeName === 'a'" ref="productBrowsingHistoryRef" />
@ -37,8 +30,7 @@ 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>>()
@ -48,32 +40,21 @@ const handleClick = async (tab: TabsPaneContext) => {
await getHistoryList()
}
//
const findgoodsNameInput = async () => {
getHistoryList()
}
/** 获得历史数据 */
// TODO @puhui ab
const getHistoryList = async () => {
switch (activeName.value) {
case 'a':
await productBrowsingHistoryRef.value?.getHistoryList(conversation.value,goodsName.value)
await productBrowsingHistoryRef.value?.getHistoryList(conversation.value)
break
case 'b':
await orderBrowsingHistoryRef.value?.getHistoryList(conversation.value,no.value)
await orderBrowsingHistoryRef.value?.getHistoryList(conversation.value)
break
default:
break
}
}
//
const findNoInput = async () => {
getHistoryList()
}
/** 加载下一页数据 */
const loadMore = async () => {
switch (activeName.value) {

View File

@ -1,92 +0,0 @@
<!-- 目录是不是叫 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 ab : 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>

View File

@ -15,8 +15,7 @@ const total = ref(0) // 总数
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
userId: 0,
no: '',
userId: 0
})
const skipGetMessageList = computed(() => {
//
@ -24,9 +23,8 @@ const skipGetMessageList = computed(() => {
}) //
/** 获得浏览记录 */
const getHistoryList = async (val: KeFuConversationRespVO,no: string) => {
const getHistoryList = async (val: KeFuConversationRespVO) => {
queryParams.userId = val.userId
queryParams.no = no
const res = await getOrderPage(queryParams)
total.value = res.total
list.value = res.list
@ -40,8 +38,7 @@ const loadMore = async () => {
queryParams.pageNo += 1
const res = await getOrderPage(queryParams)
total.value = res.total
// concat(list.value, res.list)
list.value = list.value.concat(res.list);
concat(list.value, res.list)
}
defineExpose({ getHistoryList, loadMore })
</script>

View File

@ -1,50 +0,0 @@
<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>

View File

@ -27,8 +27,7 @@ const queryParams = reactive({
pageNo: 1,
pageSize: 10,
userId: 0,
userDeleted: false,
goodsName: ''
userDeleted: false
})
const skipGetMessageList = computed(() => {
//
@ -36,9 +35,8 @@ const skipGetMessageList = computed(() => {
}) //
/** 获得浏览记录 */
const getHistoryList = async (val: KeFuConversationRespVO,goodsName: string) => {
const getHistoryList = async (val: KeFuConversationRespVO) => {
queryParams.userId = val.userId
queryParams.goodsName = goodsName
const res = await getBrowseHistoryPage(queryParams)
total.value = res.total
list.value = res.list
@ -52,8 +50,7 @@ const loadMore = async () => {
queryParams.pageNo += 1
const res = await getBrowseHistoryPage(queryParams)
total.value = res.total
// concat(list.value, res.list)
list.value = list.value.concat(res.list);
concat(list.value, res.list)
}
defineExpose({ getHistoryList, loadMore })
</script>

View File

@ -1,6 +1,5 @@
import KeFuConversationList from './KeFuConversationList.vue'
import KeFuMessageList from './KeFuMessageList.vue'
import MemberBrowsingHistory from './history/MemberBrowsingHistory.vue'
import MemberBrowsingHistorys from './history/MemberBrowsingHistorys.vue'
export { KeFuConversationList, KeFuMessageList, MemberBrowsingHistory ,MemberBrowsingHistorys}
export { KeFuConversationList, KeFuMessageList, MemberBrowsingHistory }

View File

@ -1,182 +0,0 @@
<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>

View File

@ -2,8 +2,7 @@
<template>
<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" class="w-23px h-25px" />
<Icon :size="30" class="ml-10px cursor-pointer" icon="twemoji:grinning-face" />
</template>
<ElScrollbar height="300px">
<ul class="ml-2 flex flex-wrap px-2">
@ -27,10 +26,8 @@
<script lang="ts" setup>
defineOptions({ name: 'EmojiSelectPopover' })
import biaoqing from '@/views/mall/promotion/kefu/components/asserts/biaoqing.png'
import { Emoji, useEmoji } from './emoji'
const { getEmojiList } = useEmoji()
const emojiList = computed(() => getEmojiList())

View File

@ -1,12 +1,12 @@
<!-- 图片选择 -->
<template>
<div>
<img :src="tupian" class="w-23px h-23px" @click="selectAndUpload" />
<img :src="Picture" class="w-35px h-35px" @click="selectAndUpload" />
</div>
</template>
<script lang="ts" setup>
import tupian from '@/views/mall/promotion/kefu/components/asserts/tupian.png'
import Picture from '@/views/mall/promotion/kefu/components/asserts/picture.svg'
import * as FileApi from '@/api/infra/file'
defineOptions({ name: 'PictureSelectUpload' })

View File

@ -1,276 +1,53 @@
<template>
<div>
<!-- 新区域放在头部 -->
<el-row style="display: flex; justify-content: center;">
<el-col :span="24">
<div style="width:100%;height:68px;background-color:#3c80ff;">
<el-row>
<el-col :span="6">
<el-input @input="findNameInput" v-model="findName"
style="width: 80%;margin-top: 20px;margin-left:23px;"
:suffix-icon="Search"/>
</el-col>
<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="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>
</div>
</el-col>
</el-row>
<!-- 原有的三个区域 -->
<el-row style="display: flex; justify-content: center;">
<!-- 会话列表 -->
<el-col :span="6">
<ContentWrap>
<KeFuConversationList ref="keFuConversationRef" @change="handleChange"/>
</ContentWrap>
</el-col>
<!-- 会话详情选中会话的消息列表 -->
<el-col :span="12">
<KeFuMessageList ref="keFuChatBoxRef" @change="getConversationList"/>
</el-col>
<!-- 会员足迹选中会话的会员足迹 -->
<el-col :span="6">
<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 == '3'">
<MemberBrowsingHistorys ref="memberBrowsingHistorysRef"/>
</ContentWrap>
</el-col>
</el-row>
</div>
<el-row :gutter="10" style="display: flex; justify-content: center;">
<!-- 会话列表 -->
<el-col :span="5" >
<ContentWrap>
<KeFuConversationList ref="keFuConversationRef" @change="handleChange" />
</ContentWrap>
</el-col>
<!-- 会话详情选中会话的消息列表 -->
<el-col :span="10">
<ContentWrap>
<KeFuMessageList ref="keFuChatBoxRef" @change="getConversationList" />
</ContentWrap>
</el-col>
<!-- 会员足迹选中会话的会员足迹 -->
<el-col :span="5">
<ContentWrap>
<MemberBrowsingHistory ref="memberBrowsingHistoryRef" />
</ContentWrap>
</el-col>
</el-row>
</template>
<script lang="ts" setup>
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)
import { KeFuConversationList, KeFuMessageList, MemberBrowsingHistory } from './components'
import { WebSocketMessageTypeConstants } from './components/tools/constants'
import { KeFuConversationRespVO } from '@/api/mall/promotion/kefu/conversation'
import { getRefreshToken, getAccessToken } from '@/utils/auth'
import { useWebSocket } from '@vueuse/core'
defineOptions({ name: 'KeFu' })
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()
// getAccessToken()
// 使 getRefreshToken() 使 getAccessToken() WebSocket 便访
'?token=' +
getRefreshToken() // 使 getRefreshToken() 使 getAccessToken() WebSocket 便访
) // WebSocket
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, //
/** 发起 WebSocket 连接 */
const { data, close, open } = useWebSocket(server.value, {
autoReconnect: false,
heartbeat: true
});
// const { data, close, open } = useWebSocket(server.value, {
// autoReconnect: true,
// heartbeat: true
// })
})
/** 监听 WebSocket 数据 */
watchEffect(() => {
console.log('连接服务器得到消息:', data.value)
if (!data.value) {
return
}
@ -283,15 +60,12 @@ import type { TabsPaneContext } from 'element-plus'
// 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()
@ -309,65 +83,19 @@ import type { TabsPaneContext } from 'element-plus'
console.error(error)
}
})
// ======================= WebSocket end =======================
/** 加载会话列表 */
const keFuConversationRef = ref<InstanceType<typeof KeFuConversationList>>()
const getConversationList = () => {
keFuConversationRef.value?.getConversationList(findName.value)
keFuConversationRef.value?.getConversationList()
}
/** 加载指定会话的消息列表 */
const keFuChatBoxRef = ref<InstanceType<typeof KeFuMessageList>>()
const memberBrowsingHistoryRef = ref<InstanceType<typeof MemberBrowsingHistory>>()
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()
const handleChange = (conversation: KeFuConversationRespVO) => {
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 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 findNameInput = () => {
keFuConversationRef.value?.getConversationList(findName.value)
}
/** 初始化 */
@ -375,23 +103,19 @@ import type { TabsPaneContext } from 'element-plus'
getConversationList()
// websocket
open()
console.log('WebSocket 已初始化');
})
// /** */
// onBeforeUnmount(() => {
// // websocket
// close()
// console.log('WebSocket ');
// })
/** 销毁 */
onBeforeUnmount(() => {
// websocket
close()
})
</script>
<style lang="scss">
.kefu {
height: calc(100vh - 165px);
overflow: auto;
/* 确保内容可滚动 */
overflow: auto; /* 确保内容可滚动 */
}
/* 定义滚动条样式 */
@ -409,10 +133,8 @@ import type { TabsPaneContext } from 'element-plus'
/* 定义滑块 内阴影+圆角 */
::-webkit-scrollbar-thumb {
border-radius: 10px;
box-shadow: inset 0 0 0 rgba(240, 240, 240, 0.5);
background-color: rgba(240, 240, 240, 0.5);
}
</style>

View File

@ -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,6 +34,15 @@
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<!-- <el-form-item label="登录密码" prop="password">
<el-input
v-model="queryParams.password"
placeholder="请输入登录密码"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item> -->
<el-form-item label="客服状态" prop="status">
<el-select
@ -50,6 +59,36 @@
/>
</el-select>
</el-form-item>
<!-- <el-form-item label="手机订单管理" prop="orderManage">
<el-select
v-model="queryParams.orderManage"
placeholder="请选择手机订单管理"
clearable
class="!w-240px"
>
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.KEFU_SUPPORT_STAFF_ORDER_MANAGE)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item> -->
<!-- <el-form-item label="订单通知" prop="orderInform">
<el-select
v-model="queryParams.orderInform"
placeholder="请选择订单通知"
clearable
class="!w-240px"
>
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.KEFU_SUPPORT_STAFF_ORDER_INFORM)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item> -->
<el-form-item label="创建时间" prop="createTime">
<el-date-picker
v-model="queryParams.createTime"
@ -70,7 +109,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"
@ -104,11 +143,22 @@
</el-table-column>
<el-table-column label="手机号码" align="center" prop="phone" />
<el-table-column label="登录账号" align="center" prop="account" />
<!-- <el-table-column label="登录密码" align="center" prop="password" /> -->
<el-table-column label="客服状态" align="center" prop="status">
<template #default="scope">
<dict-tag :type="DICT_TYPE.KEFU_SUPPORT_STAFF_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<!-- <el-table-column label="手机订单管理" align="center" prop="orderManage">
<template #default="scope">
<dict-tag :type="DICT_TYPE.KEFU_SUPPORT_STAFF_ORDER_MANAGE" :value="scope.row.orderManage" />
</template>
</el-table-column>
<el-table-column label="订单通知" align="center" prop="orderInform">
<template #default="scope">
<dict-tag :type="DICT_TYPE.KEFU_SUPPORT_STAFF_ORDER_INFORM" :value="scope.row.orderInform" />
</template>
</el-table-column> -->
<el-table-column
label="创建时间"
align="center"
@ -138,7 +188,7 @@
v-if="scope.row.status == 1"
link
type="success"
@click="handleEnterConsole(scope.row.id,scope.row.name,scope.row.pic)"
@click="handleEnterConsole(scope.row.id)"
>
进入工作台
</el-button>
@ -156,9 +206,6 @@
<!-- 表单弹窗添加/修改 -->
<SupportStaffForm ref="formRef" @success="getList" />
</template>
<script setup lang="ts">
@ -168,7 +215,6 @@ import download from '@/utils/download'
import { SupportStaffApi, SupportStaffVO } from '@/api/mall/promotion/supportstaff'
import SupportStaffForm from './SupportStaffForm.vue'
import { setStaffToken} from '@/utils/auth'
import { createImageViewer } from '@/components/ImageViewer'
/** 客服人员 列表 */
defineOptions({ name: 'SupportStaff' })
@ -225,13 +271,6 @@ const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}
const imagePreview = (imgUrl: string) => {
createImageViewer({
zIndex: 9999999,
urlList: [imgUrl]
})
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {
@ -245,10 +284,9 @@ const handleDelete = async (id: number) => {
} catch {}
}
/** 客服进入工作台 */
const handleEnterConsole = async (id: number,name: string,pic: string) => {
const handleEnterConsole = async (id: number) => {
setStaffToken(id);
const url = `${window.location.origin}/kefu/kefu?id=${encodeURIComponent(id)}&name=${encodeURIComponent(name)}&pic=${encodeURIComponent(pic)}`;
window.open(url, '_blank');
window.open(`${window.location.origin}/kefu/kefu`, '_blank');
}
/** 导出按钮操作 */
const handleExport = async () => {

View File

@ -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>

View File

@ -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:209px">
<el-menu :default-active="targetMenuId" style="width:183px">
<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,7 +14,9 @@
<!-- 内容区域 -->
<div class="content-wrap">
<div style="margin-top: 25px;margin-left:20px">
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<!-- <el-form-item label="分类" prop="type">
@ -23,7 +25,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>
@ -31,30 +33,28 @@
<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>
</div>
</ContentWrap>
<!-- 列表 -->
<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: 700px;
height: 1000px;
/* 固定高度 */
overflow-y: hidden;
/* 禁止滚动 */
@ -293,7 +293,6 @@
flex-grow: 1;
/* 使内容区域占据剩余空间 */
overflow-y: auto;
background-color: #ffffff;
/* 允许内容区域滚动 */
}
</style>

View File

@ -1,181 +0,0 @@
<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="walletId">
<el-input v-model="formData.walletId" placeholder="请输入钱包编号" />
</el-form-item>
<el-form-item label="充值实际到账" prop="totalPrice">
<el-input v-model="formData.totalPrice" placeholder="请输入充值实际到账" />
</el-form-item>
<el-form-item label="实际支付金额" prop="payPrice">
<el-input v-model="formData.payPrice" placeholder="请输入实际支付金额" />
</el-form-item>
<el-form-item label="钱包赠送金额" prop="bonusPrice">
<el-input v-model="formData.bonusPrice" placeholder="请输入钱包赠送金额" />
</el-form-item>
<el-form-item label="充值套餐编号" prop="packageId">
<el-input v-model="formData.packageId" placeholder="请输入充值套餐编号" />
</el-form-item>
<el-form-item label="是否支付" prop="payStatus">
<el-select v-model="formData.payStatus" placeholder="请选择是否支付">
<el-option
v-for="dict in getBoolDictOptions(DICT_TYPE.PAY_WALLET_RECHARGE_PAY_STATUS)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="支付订单编号" prop="payOrderId">
<el-input v-model="formData.payOrderId" placeholder="请输入支付订单编号" />
</el-form-item>
<el-form-item label="支付成功的支付渠道" prop="payChannelCode">
<el-input v-model="formData.payChannelCode" placeholder="请输入支付成功的支付渠道" />
</el-form-item>
<el-form-item label="订单支付时间" prop="payTime">
<el-date-picker
v-model="formData.payTime"
type="date"
value-format="x"
placeholder="选择订单支付时间"
/>
</el-form-item>
<el-form-item label="支付退款单编号" prop="payRefundId">
<el-input v-model="formData.payRefundId" placeholder="请输入支付退款单编号" />
</el-form-item>
<el-form-item label="退款金额(包含赠送金额)" prop="refundTotalPrice">
<el-input v-model="formData.refundTotalPrice" placeholder="请输入退款金额(包含赠送金额)" />
</el-form-item>
<el-form-item label="退款支付金额" prop="refundPayPrice">
<el-input v-model="formData.refundPayPrice" placeholder="请输入退款支付金额" />
</el-form-item>
<el-form-item label="退款钱包赠送金额" prop="refundBonusPrice">
<el-input v-model="formData.refundBonusPrice" placeholder="请输入退款钱包赠送金额" />
</el-form-item>
<el-form-item label="退款时间" prop="refundTime">
<el-date-picker
v-model="formData.refundTime"
type="date"
value-format="x"
placeholder="选择退款时间"
/>
</el-form-item>
<el-form-item label="退款状态" prop="refundStatus">
<el-radio-group v-model="formData.refundStatus">
<el-radio label="1">请选择字典生成</el-radio>
</el-radio-group>
</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 { getBoolDictOptions, DICT_TYPE } from '@/utils/dict'
import { WalletRechargeApi, WalletRechargeVO } from '@/api/pay/wallet/recharge'
/** 钱包充值 表单 */
defineOptions({ name: 'WalletRechargeForm' })
const { t } = useI18n() //
const message = useMessage() //
const dialogVisible = ref(false) //
const dialogTitle = ref('') //
const formLoading = ref(false) // 12
const formType = ref('') // create - update -
const formData = ref({
id: undefined,
walletId: undefined,
totalPrice: undefined,
payPrice: undefined,
bonusPrice: undefined,
packageId: undefined,
payStatus: undefined,
payOrderId: undefined,
payChannelCode: undefined,
payTime: undefined,
payRefundId: undefined,
refundTotalPrice: undefined,
refundPayPrice: undefined,
refundBonusPrice: undefined,
refundTime: undefined,
refundStatus: undefined
})
const formRules = reactive({
})
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 WalletRechargeApi.getWalletRecharge(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 WalletRechargeVO
if (formType.value === 'create') {
await WalletRechargeApi.createWalletRecharge(data)
message.success(t('common.createSuccess'))
} else {
await WalletRechargeApi.updateWalletRecharge(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
//
emit('success')
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
walletId: undefined,
totalPrice: undefined,
payPrice: undefined,
bonusPrice: undefined,
packageId: undefined,
payStatus: undefined,
payOrderId: undefined,
payChannelCode: undefined,
payTime: undefined,
payRefundId: undefined,
refundTotalPrice: undefined,
refundPayPrice: undefined,
refundBonusPrice: undefined,
refundTime: undefined,
refundStatus: undefined
}
formRef.value?.resetFields()
}
</script>

View File

@ -1,280 +0,0 @@
<template>
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="100px"
>
<el-form-item label="是否支付" prop="payStatus">
<el-select
v-model="queryParams.payStatus"
placeholder="请选择是否支付"
clearable
class="!w-240px"
>
<el-option
v-for="dict in getBoolDictOptions(DICT_TYPE.PAY_WALLET_RECHARGE_PAY_STATUS)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="支付渠道" prop="payChannelCode">
<el-input
v-model="queryParams.payChannelCode"
placeholder="请输入支付渠道"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="订单支付时间" prop="payTime">
<el-date-picker
v-model="queryParams.payTime"
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 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')]"
class="!w-240px"
/>
</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('create')"
v-hasPermi="['pay:wallet-recharge:create']"
>
<Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button>
<el-button
type="success"
plain
@click="handleExport"
:loading="exportLoading"
v-hasPermi="['pay:wallet-recharge:export']"
>
<Icon icon="ep:download" class="mr-5px" /> 导出
</el-button> -->
</el-form-item>
</el-form>
</ContentWrap>
<!-- 列表 -->
<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="name" />
<!-- <el-table-column label="用户头像" align="center" prop="avatar" /> -->
<el-table-column label="用户头像" align="center" prop="avatar" >
<template #default="{ row }">
<div class="flex justify-center items-center">
<el-image
fit="cover"
:src="row.avatar"
class="flex-none w-50px h-50px"
@click="imagePreview(row.avatar)"
/>
</div>
</template>
</el-table-column>
<el-table-column label="支付订单编号" align="center" prop="payOrderId" />
<el-table-column label="支付金额" align="center" prop="payPrice" />
<el-table-column label="支付渠道" align="center" prop="payChannelCode" />
<!-- <el-table-column label="钱包赠送金额" align="center" prop="bonusPrice" />
<el-table-column label="充值套餐编号" align="center" prop="packageId" /> -->
<el-table-column label="是否支付" align="center" prop="payStatus">
<template #default="scope">
<dict-tag :type="DICT_TYPE.PAY_WALLET_RECHARGE_PAY_STATUS" :value="scope.row.payStatus" />
</template>
</el-table-column>
<el-table-column
label="订单支付时间"
align="center"
prop="payTime"
:formatter="dateFormatter"
width="180px"
/>
<!-- <el-table-column label="支付退款单编号" align="center" prop="payRefundId" />
<el-table-column label="退款金额(包含赠送金额)" align="center" prop="refundTotalPrice" />
<el-table-column label="退款支付金额" align="center" prop="refundPayPrice" />
<el-table-column label="退款钱包赠送金额" align="center" prop="refundBonusPrice" />
<el-table-column
label="退款时间"
align="center"
prop="refundTime"
:formatter="dateFormatter"
width="180px"
/>
<el-table-column label="退款状态" align="center" prop="refundStatus" /> -->
<el-table-column
label="创建时间"
align="center"
prop="createTime"
:formatter="dateFormatter"
width="180px"
/>
<el-table-column label="操作" align="center">
<template #default="scope">
<!-- <el-button
link
type="primary"
@click="openForm('update', scope.row.id)"
v-hasPermi="['pay:wallet-recharge:update']"
>
编辑
</el-button> -->
<el-button
link
type="danger"
@click="handleDelete(scope.row.id)"
v-hasPermi="['pay:wallet-recharge:delete']"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
<!-- 表单弹窗添加/修改 -->
<WalletRechargeForm ref="formRef" @success="getList" />
</template>
<script setup lang="ts">
import { getBoolDictOptions, DICT_TYPE } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { WalletRechargeApi, WalletRechargeVO } from '@/api/pay/wallet/recharge'
import WalletRechargeForm from './WalletRechargeForm.vue'
import { createImageViewer } from "@/components/ImageViewer"
/** 钱包充值 列表 */
defineOptions({ name: 'WalletRecharge' })
const message = useMessage() //
const { t } = useI18n() //
const loading = ref(true) //
const list = ref<WalletRechargeVO[]>([]) //
const total = ref(0) //
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
walletId: undefined,
totalPrice: undefined,
payPrice: undefined,
bonusPrice: undefined,
packageId: undefined,
payStatus: undefined,
payOrderId: undefined,
payChannelCode: undefined,
payTime: [],
payRefundId: undefined,
refundTotalPrice: undefined,
refundPayPrice: undefined,
refundBonusPrice: undefined,
refundTime: [],
refundStatus: undefined,
createTime: []
})
const queryFormRef = ref() //
const exportLoading = ref(false) //
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const data = await WalletRechargeApi.getWalletRechargePage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
const imagePreview = (imgUrl : string) => {
createImageViewer({
urlList: [imgUrl]
})
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {
//
await message.delConfirm()
//
await WalletRechargeApi.deleteWalletRecharge(id)
message.success(t('common.delSuccess'))
//
await getList()
} catch {}
}
/** 导出按钮操作 */
const handleExport = async () => {
try {
//
await message.exportConfirm()
//
exportLoading.value = true
const data = await WalletRechargeApi.exportWalletRecharge(queryParams)
download.excel(data, '钱包充值.xls')
} catch {
} finally {
exportLoading.value = false
}
}
/** 初始化 **/
onMounted(() => {
getList()
})
</script>

View File

@ -117,14 +117,13 @@ const submitForm = async () => {
if (formType.value === 'create') {
await LitemallBrandApi.createLitemallBrand(data)
message.success(t('common.createSuccess'))
} else {
await LitemallBrandApi.updateLitemallBrand(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
dialogVisible.value = false
//
emit('success')
emit('success')
} finally {
formLoading.value = false
}

View File

@ -56,7 +56,7 @@
<!-- <el-table-column label="门店管理员" align="center" prop="userId" />-->
<el-table-column label="门店图片" align="center" prop="picUrl">
<template #default="{ row }">
<div class="flex justify-center items-center">
<div class="flex">
<el-image fit="cover" :src="row.picUrl" class="flex-none w-50px h-50px"
@click="imagePreview(row.picUrl)" />
</div>
@ -79,7 +79,7 @@
</template>
</el-table-column>
<!-- <el-table-column label="权重" align="center" prop="weight" />-->
<!-- <el-table-column label="备注" align="center" prop="remark" /> -->
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="创建时间" align="center" prop="createTime" :formatter="dateFormatter" width="180px" />
<el-table-column label="操作" align="center" fixed="right" width="110">
<template #default="scope">

View File

@ -8,14 +8,10 @@
v-loading="formLoading"
>
<el-form-item label="门店" prop="brandId">
<!-- <el-input v-model="formData.brandId" placeholder="请输入门店id" />-->
<el-select v-model="formData.brandId" placeholder="请选择门店" clearable class="!w-240px">
<el-option
v-for="organizationNameOptions in option"
:key="organizationNameOptions.id"
:label="organizationNameOptions.name"
:value="organizationNameOptions.id"
:disabled="organizationNameOptions.disabled"
/>
<el-option v-for="organizationNameOptions in option" :key="organizationNameOptions.id"
:label="organizationNameOptions.name" :value="organizationNameOptions.id" />
</el-select>
</el-form-item>
<el-form-item label="项目名称" prop="name">

View File

@ -54,7 +54,7 @@
<!-- <el-table-column label="项目图片" align="center" prop="pictrue" /> -->
<el-table-column label="项目图片" align="center" prop="pictrue">
<template #default="{ row }">
<div class="flex justify-center items-center" style="display: flex; align-items: center;">
<div class="flex" style="display: flex; align-items: center;">
<el-image fit="cover" :src="row.pictrue" class="flex-none w-50px h-50px"
@click="imagePreview(row.pictrue)" />
</div>

View File

@ -2,8 +2,8 @@
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<el-form-item label="所属项目" prop="brandId">
<el-select v-model="queryParams.brandId" placeholder="请选择项目" clearable class="!w-240px">
<el-form-item label="所属门店" prop="brandId">
<el-select v-model="queryParams.brandId" placeholder="请选择门店" clearable class="!w-240px">
<el-option v-for="organizationNameOptions in option" :key="organizationNameOptions.id"
:label="organizationNameOptions.name" :value="organizationNameOptions.id" />
</el-select>
@ -47,7 +47,7 @@
<el-table-column label="名称" align="center" prop="name" />
<el-table-column label="图片" align="center" prop="photo">
<template #default="{ row }">
<div class="flex justify-center items-center">
<div class="flex">
<el-image fit="cover" :src="row.photo" class="flex-none w-50px h-50px"
@click="imagePreview(row.photo)" />
</div>

View File

@ -76,8 +76,7 @@ public class SecurityFrameworkUtils {
if (authentication == null) {
return null;
}
LoginUser loginUser = authentication.getPrincipal() instanceof LoginUser ? (LoginUser) authentication.getPrincipal() : null;
return loginUser;
return authentication.getPrincipal() instanceof LoginUser ? (LoginUser) authentication.getPrincipal() : null;
}
/**

View File

@ -29,6 +29,6 @@ public class WebSocketProperties {
* 可选值localredisrocketmqkafkarabbitmq
*/
@NotNull(message = "WebSocket 的消息发送者不能为空")
private String senderType = "redis";
private String senderType = "local";
}

View File

@ -5,7 +5,6 @@ import cn.iocoder.yudao.framework.mq.redis.core.RedisMQTemplate;
import cn.iocoder.yudao.framework.websocket.core.handler.JsonWebSocketMessageHandler;
import cn.iocoder.yudao.framework.websocket.core.listener.WebSocketMessageListener;
import cn.iocoder.yudao.framework.websocket.core.security.LoginUserHandshakeInterceptor;
import cn.iocoder.yudao.framework.websocket.core.security.WebSocketAuthorizeRequestsCustomizer;
import cn.iocoder.yudao.framework.websocket.core.sender.kafka.KafkaWebSocketMessageConsumer;
import cn.iocoder.yudao.framework.websocket.core.sender.kafka.KafkaWebSocketMessageSender;
import cn.iocoder.yudao.framework.websocket.core.sender.local.LocalWebSocketMessageSender;
@ -77,15 +76,10 @@ public class YudaoWebSocketAutoConfiguration {
return new WebSocketSessionManagerImpl();
}
@Bean
public WebSocketAuthorizeRequestsCustomizer webSocketAuthorizeRequestsCustomizer(WebSocketProperties webSocketProperties) {
return new WebSocketAuthorizeRequestsCustomizer(webSocketProperties);
}
// ==================== Sender 相关 ====================
@Configuration
@ConditionalOnProperty(prefix = "yudao.websocket", name = "sender-type", havingValue = "local")
@ConditionalOnProperty(prefix = "yudao.websocket", name = "sender-type", havingValue = "local", matchIfMissing = true)
public class LocalWebSocketMessageSenderConfiguration {
@Bean
@ -96,7 +90,7 @@ public class YudaoWebSocketAutoConfiguration {
}
@Configuration
@ConditionalOnProperty(prefix = "yudao.websocket", name = "sender-type", havingValue = "redis")
@ConditionalOnProperty(prefix = "yudao.websocket", name = "sender-type", havingValue = "redis", matchIfMissing = true)
public class RedisWebSocketMessageSenderConfiguration {
@Bean
@ -114,7 +108,7 @@ public class YudaoWebSocketAutoConfiguration {
}
@Configuration
@ConditionalOnProperty(prefix = "yudao.websocket", name = "sender-type", havingValue = "rocketmq")
@ConditionalOnProperty(prefix = "yudao.websocket", name = "sender-type", havingValue = "rocketmq", matchIfMissing = true)
public class RocketMQWebSocketMessageSenderConfiguration {
@Bean
@ -133,7 +127,7 @@ public class YudaoWebSocketAutoConfiguration {
}
@Configuration
@ConditionalOnProperty(prefix = "yudao.websocket", name = "sender-type", havingValue = "rabbitmq")
@ConditionalOnProperty(prefix = "yudao.websocket", name = "sender-type", havingValue = "rabbitmq", matchIfMissing = true)
public class RabbitMQWebSocketMessageSenderConfiguration {
@Bean
@ -162,7 +156,7 @@ public class YudaoWebSocketAutoConfiguration {
}
@Configuration
@ConditionalOnProperty(prefix = "yudao.websocket", name = "sender-type", havingValue = "kafka")
@ConditionalOnProperty(prefix = "yudao.websocket", name = "sender-type", havingValue = "kafka", matchIfMissing = true)
public class KafkaWebSocketMessageSenderConfiguration {
@Bean

View File

@ -38,11 +38,9 @@ public abstract class AbstractWebSocketMessageSender implements WebSocketMessage
@Override
public void send(String sessionId, String messageType, String messageContent) {
send(sessionId, (Integer) null,null, messageType, messageContent);
send(sessionId, null, null, messageType, messageContent);
}
/**
* 发送消息
*

View File

@ -37,8 +37,6 @@ 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));
}
@ -51,6 +49,4 @@ public interface WebSocketMessageSender {
send(sessionId, messageType, JsonUtils.toJsonString(messageContent));
}
}

View File

@ -1,16 +1,10 @@
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} 实现类
@ -22,10 +16,6 @@ public class RedisWebSocketMessageSender extends AbstractWebSocketMessageSender
private final RedisMQTemplate redisMQTemplate;
@Resource
private WebSocketSessionManager webSocketSessionManager;
public RedisWebSocketMessageSender(WebSocketSessionManager sessionManager,
RedisMQTemplate redisMQTemplate) {
super(sessionManager);
@ -37,7 +27,6 @@ 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);
@ -48,8 +37,6 @@ public class RedisWebSocketMessageSender extends AbstractWebSocketMessageSender
sendRedisMessage(sessionId, null, null, messageType, messageContent);
}
/**
* 通过 Redis 广播消息
*
@ -61,7 +48,6 @@ 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);

View File

@ -3,7 +3,6 @@ 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} 管理器的接口
@ -19,7 +18,6 @@ public interface WebSocketSessionManager {
*/
void addSession(WebSocketSession session);
ConcurrentMap<String, WebSocketSession> idSessions();
/**
* 移除 Session
*
@ -27,8 +25,6 @@ public interface WebSocketSessionManager {
*/
void removeSession(WebSocketSession session);
/**
* 获得指定编号的 Session
*

View File

@ -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 javax.servlet.http.HttpSession;
import java.net.URI;
import java.util.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
@ -19,7 +19,6 @@ import java.util.concurrent.CopyOnWriteArrayList;
*
* @author 芋道源码
*/
@Service
public class WebSocketSessionManagerImpl implements WebSocketSessionManager {
/**
@ -29,8 +28,6 @@ public class WebSocketSessionManagerImpl implements WebSocketSessionManager {
*/
private final ConcurrentMap<String, WebSocketSession> idSessions = new ConcurrentHashMap<>();
private HttpSession httpSession;
/**
* user WebSocketSession 映射
*
@ -42,7 +39,6 @@ public class WebSocketSessionManagerImpl implements WebSocketSessionManager {
@Override
public void addSession(WebSocketSession session) {
// 添加到 idSessions
idSessions.put(session.getId(), session);
// 添加到 userSessions
@ -64,18 +60,7 @@ 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

View File

@ -39,8 +39,6 @@ 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));
}
@ -53,5 +51,4 @@ public interface WebSocketSenderApi {
send(sessionId, messageType, JsonUtils.toJsonString(messageContent));
}
}

View File

@ -31,6 +31,4 @@ public class WebSocketSenderApiImpl implements WebSocketSenderApi {
webSocketMessageSender.send(sessionId, messageType, messageContent);
}
}

View File

@ -61,22 +61,6 @@ 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);

View File

@ -30,6 +30,4 @@ public class ProductBrowseHistoryPageReqVO extends SortablePageParam {
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
private String goodsName;
}

View File

@ -201,23 +201,4 @@ public class AppProductSpuController {
return pageResult;
}
// TODO 芋艿商品的浏览记录
@GetMapping("/getActivityGoods")
public CommonResult<List<AppProductSpuRespVO>> getActivityGoods(String activityType) {
List<ProductSpuDO> list = productSpuService.getActivityGoods(activityType);
if (CollUtil.isEmpty(list)) {
return success(Collections.emptyList());
}
// 拼接返回
list.forEach(spu -> spu.setSalesCount(spu.getSalesCount() + spu.getVirtualSalesCount()));
List<AppProductSpuRespVO> voList = BeanUtils.toBean(list, AppProductSpuRespVO.class);
// 处理 vip 价格
MemberLevelRespDTO memberLevel = getMemberLevel();
voList.forEach(vo -> vo.setVipPrice(calculateVipPrice(vo.getPrice(), memberLevel)));
return success(voList);
}
}

View File

@ -6,7 +6,6 @@ import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuSaveReq
import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuUpdateStatusReqVO;
import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO;
import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuRecommendPageReqVo;
import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuRespVO;
import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
import org.springframework.scheduling.annotation.Async;
@ -139,7 +138,4 @@ public interface ProductSpuService {
* @return
*/
List<ProductSpuDO> getRecommendProductIdList(AppProductSpuRecommendPageReqVo pageVo);
List<ProductSpuDO> getActivityGoods(String activityType);
}

View File

@ -15,7 +15,6 @@ import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuSaveReq
import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuUpdateStatusReqVO;
import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO;
import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuRecommendPageReqVo;
import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuRespVO;
import cn.iocoder.yudao.module.product.dal.dataobject.category.ProductCategoryDO;
import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
import cn.iocoder.yudao.module.product.dal.mysql.spu.ProductSpuMapper;
@ -23,7 +22,6 @@ import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;
import cn.iocoder.yudao.module.product.service.brand.ProductBrandService;
import cn.iocoder.yudao.module.product.service.category.ProductCategoryService;
import cn.iocoder.yudao.module.product.service.sku.ProductSkuService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.collect.Maps;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
@ -196,8 +194,6 @@ public class ProductSpuServiceImpl implements ProductSpuService {
return productSpuMapper.selectList(wrapperX);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteSpu(Long id) {
@ -312,31 +308,4 @@ public class ProductSpuServiceImpl implements ProductSpuService {
return productSpuMapper.selectCount(ProductSpuDO::getCategoryId, categoryId);
}
@Override
public List<ProductSpuDO> getActivityGoods(String activityType) {
String activity = "";
if (activityType.equals("recommendHot")){
activity = "recommend_hot";
}
if (activityType.equals("recommendBenefit")){
activity = "recommend_benefit";
}
if (activityType.equals("recommendBest")){
activity = "recommend_best";
}
if (activityType.equals("recommendNew")){
activity = "recommend_new";
}
if (activityType.equals("recommendGood")){
activity = "recommend_good";
}
QueryWrapper<ProductSpuDO> queryWrapper = new QueryWrapper<>();
queryWrapper.eq(activity,1).eq("status",1);
return productSpuMapper.selectList(queryWrapper);
}
}

View File

@ -19,10 +19,6 @@ 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 不存在");

View File

@ -79,12 +79,6 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-module-system-biz</artifactId>
<version>2.1.0-jdk8-snapshot</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@ -1,104 +0,0 @@
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));
}
}

View File

@ -1,32 +0,0 @@
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;
}

View File

@ -1,42 +0,0 @@
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;
}

View File

@ -1,32 +0,0 @@
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;
}

View File

@ -18,6 +18,9 @@ public class CirclePageReqVO extends PageParam {
@Schema(description = "用户编号", example = "20140")
private Long userId;
@Schema(description = "用户昵称", example = "用户000")
private String nickname;
@Schema(description = "发表内容")
private String content;

View File

@ -22,6 +22,9 @@ public class CircleRespVO {
@ExcelProperty("用户编号")
private Long userId;
@Schema(description = "用户昵称", example = "用户000")
private String nickname;
@Schema(description = "发表内容")
@ExcelProperty("发表内容")
private String content;

View File

@ -15,6 +15,9 @@ public class CircleSaveReqVO {
@Schema(description = "用户编号", example = "20140")
private Long userId;
@Schema(description = "用户昵称", example = "用户000")
private String nickname;
@Schema(description = "发表内容")
private String content;

View File

@ -1,95 +0,0 @@
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));
}
}

View File

@ -1,37 +0,0 @@
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;
}

View File

@ -1,43 +0,0 @@
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;
}

Some files were not shown because too many files have changed in this diff Show More