提交 4813cf77 作者: 毛细亚

合并分支 '1.2' 到 'release'

1.2

查看合并请求 !5
<!doctype html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="favicon.ico"><meta http-equiv="pragma" content="no-cache"><meta http-equiv="cache-control" content="no-cache"><meta http-equiv="expires" content="0"><meta http-equiv="X-UA-Compatible" content="IE=EmulateIE9"/><title>company_app</title><script src="https://g.alicdn.com/dingding/dinglogin/0.0.5/ddLogin.js"></script><script defer="defer" src="static/js/chunk-vendors.b6398f5b.js"></script><script defer="defer" src="static/js/app.101d7d80.js"></script><link href="static/css/chunk-vendors.8e901099.css" rel="stylesheet"><link href="static/css/app.1b60c483.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but company_app doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>
\ No newline at end of file
<!doctype html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="favicon.ico"><meta http-equiv="pragma" content="no-cache"><meta http-equiv="cache-control" content="no-cache"><meta http-equiv="expires" content="0"><meta http-equiv="X-UA-Compatible" content="IE=EmulateIE9"/><title>company_app</title><script src="https://g.alicdn.com/dingding/dinglogin/0.0.5/ddLogin.js"></script><script defer="defer" src="static/js/chunk-vendors.72a90e47.js"></script><script defer="defer" src="static/js/app.003145b5.js"></script><link href="static/css/chunk-vendors.8e901099.css" rel="stylesheet"><link href="static/css/app.8a15faf7.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but company_app doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>
\ No newline at end of file
# 客服休息状态功能文档
本文档描述了客服休息状态功能的实现方法和使用说明,该功能支持客服在忙碌或休息时暂时停止接收新消息。
## 功能概述
客服休息状态功能允许客服在午休或临时有事时设置自己为"休息中"状态,以便合理安排工作时间。同时,还提供了发送评价功能,方便客服向客户发送评价请求。
## 相关API
### 1. 获取客服休息状态
```javascript
import { getClientStatus } from '@/api/user.js'
// 获取客服休息状态
const response = await getClientStatus()
if (response.status_code === 1) {
const status = response.data.client_online_status
// status 可能的值:
// - online: 在线
// - offline: 离线
// - rest: 休息中
}
```
### 2. 开始休息
```javascript
import { client_session_rest } from '@/api/user.js'
// 开始休息
const response = await client_session_rest()
if (response.status_code === 1) {
// 休息开始成功
// 可以更新界面显示为"休息中"状态
}
```
### 3. 结束休息
```javascript
import { finishRest } from '@/api/user.js'
// 结束休息
const response = await finishRest()
if (response.status_code === 1) {
// 休息结束成功
// 可以更新界面显示为"在线"状态
}
```
### 4. 发送评价
```javascript
import { sendComment } from '@/api/user.js'
import { sendChatMessage } from '@/utils/index.js'
// 发送评价
const response = await sendComment({
corp_id: '企业ID',
external_userid: '外部联系人ID',
userid: '客服ID'
})
if (response.status_code === 1 && response.data.news) {
// 使用企业微信JSSDK发送评价
const result = await sendChatMessage(response.data.news, 'link')
if (result.success) {
// 评价发送成功
}
}
```
## 会话内容存档相关API
### 1. 检查客户是否同意聊天内容存档
```javascript
import { checkSingleAgree } from '@/api/user.js'
// 检查客户是否同意聊天内容存档
const response = await checkSingleAgree({
external_userid: '外部联系人ID',
userid: '客服ID'
})
if (response.status_code === 1) {
const agreeStatus = response.data.agree_status
// agreeStatus 可能的值:
// - Agreen: 已同意
// - Disagree: 未同意
}
```
### 2. 检查客服号是否开启会话内容存档
```javascript
import { checkUserPermit } from '@/api/user.js'
// 检查客服号是否开启会话内容存档
const response = await checkUserPermit({
userid: '客服ID'
})
if (response.status_code === 1) {
const hasPermit = response.data.has_permit
// hasPermit: true 已授权, false 未授权
}
```
### 3. 同步智能标签
```javascript
import { remarkSessionIntelTag } from '@/api/user.js'
// 同步智能标签
await remarkSessionIntelTag({
corp_id: '企业ID',
external_userid: '外部联系人ID',
userid: '客服ID'
})
```
## 使用示例
### 在Vue组件中整合所有功能
```javascript
import { mapState, mapMutations, mapActions } from 'vuex'
import {
getClientStatus,
remarkSessionIntelTag,
finishRest,
client_session_rest,
checkSingleAgree,
checkUserPermit,
sendComment
} from '@/api/user.js'
import { sendChatMessage } from '@/utils/index.js'
export default {
data() {
return {
// 相关状态
agreeStatus: '', // 用户是否同意聊天内容存档
hasPermit: false // 客服号是否开启会话内容存档权限
}
},
computed: {
...mapState('user', ['client_online_status', 'corp_id', 'external_userid', 'userid']),
// 状态文本转换
clientStatusText() {
const statusMap = {
'online': '在线',
'offline': '离线',
'rest': '休息中'
}
return statusMap[this.client_online_status] || '未知'
}
},
created() {
// 初始化企业微信SDK
this.initializeWecom()
// 获取各种状态
this.getInitialData()
},
methods: {
...mapActions('user', ['initWecom']),
// 获取初始数据
async getInitialData() {
// 实现获取状态逻辑
},
// 开始休息
async handleStartRest() {
// 实现开始休息逻辑
},
// 结束休息
async handleFinishRest() {
// 实现结束休息逻辑
},
// 发送评价
async handleSendComment() {
// 实现发送评价逻辑
}
}
}
```
## 界面展示
客服状态显示和按钮应该包含以下元素:
1. 当前状态显示:显示客服当前是"在线"、"离线"还是"休息中"
2. 休息相关按钮:
- 在线状态下显示"开始休息"按钮
- 休息中状态显示"结束休息"按钮
3. 发送评价按钮:用于向客户发送评价请求
4. 会话内容存档状态显示:
- 显示客户是否已开启会话内容存档
- 显示客服号是否已授权会话内容存档
## 注意事项
1. 客服在"休息中"状态时不能直接下线,必须先结束休息
2. 开始休息按钮悬停时应显示提示文字:"午休或者临时有事可点击休息"
3. 使用企业微信JSSDK前需确保已初始化成功
4. 所有状态变更应同步更新到Vuex store,以便在多个组件中共享
\ No newline at end of file
{
"recommendations": ["stagewise.stagewise-vscode-extension"]
}
\ No newline at end of file
......@@ -36,8 +36,9 @@
},
"devDependencies": {
"@babel/core": "^7.12.16",
"@stagewise/toolbar": "^0.4.4",
"@stagewise/toolbar-vue": "^0.4.4",
"@stagewise-plugins/vue": "^0.4.6",
"@stagewise/toolbar": "^0.4.8",
"@stagewise/toolbar-vue": "^0.4.8",
"@vue/cli-plugin-babel": "~5.0.0",
"@vue/cli-plugin-router": "~5.0.0",
"@vue/cli-plugin-vuex": "~5.0.0",
......
......@@ -84,12 +84,15 @@ importers:
'@babel/core':
specifier: ^7.12.16
version: 7.27.1
'@stagewise-plugins/vue':
specifier: ^0.4.6
version: 0.4.6(@stagewise/toolbar@0.4.8)
'@stagewise/toolbar':
specifier: ^0.4.4
version: 0.4.4
specifier: ^0.4.8
version: 0.4.8
'@stagewise/toolbar-vue':
specifier: ^0.4.4
version: 0.4.4(vue@2.7.16)
specifier: ^0.4.8
version: 0.4.8(vue@2.7.16)
'@vue/cli-plugin-babel':
specifier: ~5.0.0
version: 5.0.8(@vue/cli-service@5.0.8(@vue/compiler-sfc@3.5.14)(lodash@4.17.21)(sass-loader@16.0.5(node-sass@4.14.1)(sass@1.89.0)(webpack@5.99.8))(vue-template-compiler@2.7.16)(vue@2.7.16)(webpack-sources@3.2.3))(core-js@3.42.0)(vue@2.7.16)
......@@ -809,13 +812,18 @@ packages:
'@soda/get-current-script@1.0.2':
resolution: {integrity: sha512-T7VNNlYVM1SgQ+VsMYhnDkcGmWhQdL0bDyGm5TlQ3GBXnJscEClUUOKduWTmm2zCnvNLC1hc3JpuXjs/nFOc5w==}
'@stagewise/toolbar-vue@0.4.4':
resolution: {integrity: sha512-0+r8SGExjz3+A64aMGgbg0clESg6yxZPwlO1aY+3bIwBjE+F/V3MetRE+5b7AuRKqEoz8GUXnll5O/zHIL+I3Q==}
'@stagewise-plugins/vue@0.4.6':
resolution: {integrity: sha512-Y/cdDLXDN2cusvpmFYxbQT1DEW1fYzoFjmsnXBth52sSLYNc83XXMTXt2kvYSGWOW+ZxM1Dj+T4eE9bY8b/QSA==}
peerDependencies:
'@stagewise/toolbar': 0.4.8
'@stagewise/toolbar-vue@0.4.8':
resolution: {integrity: sha512-Zvxz59apepocu2cOD8UqUWIF7fIABGr+DNDSmz6Kp2nf1APNmfiK3QJ52rI2PfVDG9jaSE56EfYUA0t1xFJLYw==}
peerDependencies:
vue: '>=3.0.0'
'@stagewise/toolbar@0.4.4':
resolution: {integrity: sha512-hVxGqeYFx780m9SIv+YqhHx4o/fq94pMwr8OoemOCyOKozCGkvSIjpqkONvUyY5yWR+AVcop2p79LsSTdA6Etw==}
'@stagewise/toolbar@0.4.8':
resolution: {integrity: sha512-0ByvC4hYdHHf3rK5M+xSR9mipHYr8naNn2OgDBtv4DE0SoSCr08KfQtZ6VpsBNbOW/Mh1Y4c/AoWcyCTOc2ocA==}
'@trysound/sax@0.2.0':
resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==}
......@@ -1433,6 +1441,9 @@ packages:
brace-expansion@1.1.11:
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
brace-expansion@1.1.12:
resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
braces@3.0.3:
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
engines: {node: '>=8'}
......@@ -6275,12 +6286,16 @@ snapshots:
'@soda/get-current-script@1.0.2': {}
'@stagewise/toolbar-vue@0.4.4(vue@2.7.16)':
'@stagewise-plugins/vue@0.4.6(@stagewise/toolbar@0.4.8)':
dependencies:
'@stagewise/toolbar': 0.4.8
'@stagewise/toolbar-vue@0.4.8(vue@2.7.16)':
dependencies:
'@stagewise/toolbar': 0.4.4
'@stagewise/toolbar': 0.4.8
vue: 2.7.16
'@stagewise/toolbar@0.4.4': {}
'@stagewise/toolbar@0.4.8': {}
'@trysound/sax@0.2.0': {}
......@@ -7324,6 +7339,12 @@ snapshots:
balanced-match: 1.0.2
concat-map: 0.0.1
brace-expansion@1.1.12:
dependencies:
balanced-match: 1.0.2
concat-map: 0.0.1
optional: true
braces@3.0.3:
dependencies:
fill-range: 7.1.1
......@@ -8689,7 +8710,7 @@ snapshots:
fs.realpath: 1.0.0
inflight: 1.0.6
inherits: 2.0.4
minimatch: 3.1.2
minimatch: 3.0.8
once: 1.4.0
path-is-absolute: 1.0.1
optional: true
......@@ -9625,7 +9646,7 @@ snapshots:
minimatch@3.0.8:
dependencies:
brace-expansion: 1.1.11
brace-expansion: 1.1.12
optional: true
minimatch@3.1.2:
......@@ -11776,7 +11797,7 @@ snapshots:
wide-align@1.1.5:
dependencies:
string-width: 4.2.3
string-width: 1.0.2
optional: true
wildcard@2.0.1: {}
......
......@@ -98,6 +98,10 @@ export default {
path: '/quickSendGame'
},
// {
// label: '任务列表',
// path: '/taskList'
// },
// {
// label: '通讯录',
// path: '/addressBook'
// },
......
......@@ -94,7 +94,7 @@ export function sendComment(data) {
export function client_session_rest(data) {
// 发送一个post请求,请求的url为'/sidebar/client_session/rest',请求的数据为data
return request({
url: '/sidebar/client_session/rest',
url: '/sidebar/work_wei_xin/rest',
method: 'post',
data
})
......@@ -120,7 +120,7 @@ export function remarkSessionIntelTag(data) {
// 获取客户号的休息状态
export function getClientStatus(data) {
return request({
url: '/sidebar/work_wei_xin/getClientStatus',
url: '/sidebar/work_wei_xin/info',
method: 'post',
data
})
......
......@@ -225,3 +225,21 @@ export function zyouGetMemberLink(data) {
data
})
}
// 我的任务获取红点数组
export function getTaskUnReadData(data) {
return request({
url: returnApi('/corp_zyou_bind/getTaskUnReadData'),
method: 'post',
data
})
}
// 我的任务小时红点数字
export function clearTaskUnReadData(data) {
return request({
url: returnApi('/corp_zyou_bind/clearTaskUnReadData'),
method: 'post',
data
})
}
\ No newline at end of file
......@@ -363,19 +363,13 @@
padding-bottom: 120px;
.input {
width: 250px;
::v-deep .el-input__inner {
width: vw(250);
}
::v-deep .el-button {
width: vw(76);
}
}
.contnet {
width: 100%;
height: auto;
margin-top: 20px;
.contnetLeft {
width: vw(500);
width: auto;
height: 600px;
border-radius: 8px;
border: 1px solid rgba(0, 0, 0, 0.06);
......@@ -384,7 +378,7 @@
padding-top: 40px;
}
.contnetRight {
width: vw(260);
width: 260px;
height: 600px;
border-radius: 8px;
border: 1px solid rgba(0, 0, 0, 0.06);
......
......@@ -62,7 +62,7 @@ function createScrollHandler(el, binding, vnode) {
const options = el.__infinite_scroll_options;
// 检查是否禁用
if (options.disabled) return;
if (options && options.disabled) return;
const scrollContainer = el.__infinite_scroll_container;
const isBottom = isScrollBottom(scrollContainer, el, options.distance);
......
......@@ -26,16 +26,26 @@ Vue.use(globalComponent).use(permission).use(clickagain).use(loadmore).use(scrol
import '@/utils/vconsoleCleanup'
// 开发环境下初始化 stagewise 工具栏
// if (process.env.NODE_ENV === 'development') {
// import('@stagewise/toolbar').then(({ initToolbar }) => {
// const stagewiseConfig = {
// plugins: []
// };
// initToolbar(stagewiseConfig);
// }).catch(err => {
// console.error('Failed to initialize stagewise toolbar:', err);
// });
// }
if (process.env.NODE_ENV === 'development') {
import('@stagewise/toolbar-vue').then(({ StagewiseToolbar }) => {
import('@stagewise-plugins/vue').then(({ VuePlugin }) => {
const stagewiseConfig = {
plugins: [VuePlugin]
};
// 动态创建并挂载 StagewiseToolbar 组件
const ToolbarConstructor = Vue.extend({
render(h) {
return h(StagewiseToolbar, { props: { config: stagewiseConfig } });
}
});
const toolbarInstance = new ToolbarConstructor();
toolbarInstance.$mount();
document.body.appendChild(toolbarInstance.$el);
});
}).catch(err => {
console.error('Failed to initialize stagewise toolbar:', err);
});
}
// 开发环境不收集日志
if (process.env.NODE_ENV !== 'development') {
......
......@@ -11,6 +11,7 @@ import violationRecord from '../views/ViolationRecord.vue'
import taskRecord from '../views/taskRecord.vue'
import mailList from '@/views/mailList.vue'
import quickSendGame from '@/views/quickSendGame.vue'
// import taskList from '@/views/taskList.vue'
import Cookies from 'js-cookie'
import store from '@/store'
Vue.use(VueRouter)
......@@ -77,6 +78,11 @@ const routes = [
name: 'quickSendGame',
component: quickSendGame
},
// {
// path: '/taskList',
// name: 'taskList',
// component: taskList
// },
{
path: '/login',
name: 'login',
......
......@@ -14,6 +14,7 @@ const state = {
send_game_log: null, // 转游发送渠道新增日志发送信息
chatUserInfo: {}, // 当前选中的用户的详情
viewLoading:false, // 查看用户详情的时候 加载状态
taskDetails: {}, // 任务详情
}
const mutations = {
......@@ -34,6 +35,9 @@ const mutations = {
},
set_viewLoading(state, data) {
state.viewLoading = data
},
set_taskDetails(state, data) {
state.taskDetails = data
}
}
......
......@@ -31,6 +31,7 @@ const state = {
},
weixin_blongs_id_list:[],
isWecomSDKReady: false, // 添加企业微信 SDK 就绪状态
client_online_status: '', // 客服休息状态: online上线 offline下线 rest休息中
// 六子的 用户id wm5rUgMgAAjqjOcqp8i3lEhFZDQieWug
// 我的 userid JinDuoXia cser_id 4090 corp_id wweaefe716636df3d1 cser_id 4090 token token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. eyJpc3MiOjQwOTAsImlhdCI6MTc0NzgxMjMxMiwiZXhwIjoxNzQ4NDE3MTEyLCJuYmYiOjE3NDc4MTIzMTIsInN1YiI6InRva2Vu6K6k6K-BIiwianRpIjoiMjBkOTY3MDZiYzI1MDdmY2MxOWI2MjU1YTM0YWQ3M2YifQ.yX7E7QHV7x2ubpa8iK3Avy794EiHNCaW2CtB4A4UQWo
}
......@@ -73,6 +74,9 @@ const mutations = {
set_isWecomSDKReady(state, status) {
state.isWecomSDKReady = status
},
set_client_online_status(state, status) {
state.client_online_status = status
},
}
const actions = {
......
......@@ -700,7 +700,6 @@ li {
/* ----------------- el-collapse 组件全局样式 --------------------*/
.el-collapse {
.el-collapse-item {
margin-bottom: 8px;
border-radius: 4px;
overflow: hidden;
&__header {
......@@ -849,3 +848,7 @@ li {
font-weight: 500;
}
}
.el-input__icon{
line-height: 1;
}
......@@ -79,8 +79,10 @@ service.interceptors.response.use(
if (res.status_code === -100) {
// 登录 过期 重新去登录
setTimeout(() => {
if(process.env.NODE_ENV !== 'development'){
removeToken()
window.location.href = window.location.origin +'/company_app/index.html?corp_id='+Cookies.get('corp_id')
}
}, 2000);
return res
}
......
......@@ -586,7 +586,7 @@ export default {
.chatListItem {
width: 100%;
height: 68px;
padding: 3px vw(20);
padding: 3px 10px;
position: relative;
cursor: pointer;
color: #333333;
......@@ -813,7 +813,7 @@ export default {
}
.trans-follow-1 {
width: vw(300);
width: 300px;
min-height: fit-content;
margin: 6px 0 12px 0;
padding: 12px 12px 2px 12px;
......
......@@ -516,7 +516,7 @@ export default {
.chatListItem {
width: 100%;
height: 68px;
padding: 3px vw(20);
padding: 3px 10px;
position: relative;
cursor: pointer;
color: #333333;
......@@ -743,7 +743,7 @@ export default {
}
.trans-follow-1 {
width: vw(300);
width: 300px;
min-height: fit-content;
margin: 6px 0 12px 0;
padding: 12px 12px 2px 12px;
......
......@@ -305,7 +305,7 @@ export default {
position: relative;
.detailsTitle {
width: 100%;
padding: 0 vw(20);
padding: 0 10px;
height: 60px;
font-size: 18px;
font-family: PingFangSC-Medium, PingFang SC;
......@@ -379,7 +379,7 @@ export default {
width: 40px;
height: 40px;
border-radius: 6px;
margin-right: vw(10);
margin-right: 10px;
}
.label {
color: #999999;
......
......@@ -10,6 +10,7 @@
<el-form
ref="form"
:model="form"
label-position="top"
:rules="rules"
label-width="120px"
>
......
......@@ -13,7 +13,6 @@
<p v-if="bindGameUserList.length > 0" class="num">
总共{{ bindGameUserList.length }}个账号
</p>
<el-button type="danger" style="margin-left: 10px;" size="mini" @click="logout">下线</el-button>
<addUser
:show.sync="showLayer"
title="选择玩家"
......@@ -54,7 +53,8 @@ export default {
...mapState('user', [
'userid',
'corp_id',
'external_userid'
'external_userid',
'client_online_status'
]),
},
watch: {
......@@ -109,39 +109,6 @@ export default {
}
}
},
logout(){
this.$confirm('确定下线吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.userLogout()
}).catch(() => {
this.$message({
type: 'info',
message: '已取消'
})
})
},
async userLogout(){
const data = {
userid: this.userid,
}
const res = await logout(data)
if(res.status_code === 1){
this.$message({
type: 'success',
message: '下线成功'
})
removeToken()
window.location.href = window.location.origin +'/company_app/index.html?corp_id='+this.corp_id
}else{
this.$message({
type: 'error',
message: '下线失败'
})
}
},
addNewUser() {
console.log(11)
},
......
......@@ -109,8 +109,8 @@
cursor: pointer;
}
.qrImage {
width: vw(140);
height: vw(140);
width: 140px;
height: 140px;
}
.tableImage {
width: 30px;
......
......@@ -482,7 +482,7 @@
background: #fff;
.detailsTitle {
width: 100%;
padding: 0 vw(20);
padding: 0 10px;
height: 60px;
font-size: 18px;
font-family: PingFangSC-Medium, PingFang SC;
......@@ -561,7 +561,7 @@
width: 40px;
height: 40px;
border-radius: 6px;
margin-right: vw(10);
margin-right: 10px;
}
.label {
......
......@@ -627,7 +627,7 @@
.chatListItem {
width: 100%;
height: 68px;
padding: 3px vw(20);
padding: 3px 10px;
position: relative;
cursor: pointer;
color: #333333;
......@@ -854,7 +854,7 @@
}
.trans-follow-1 {
width: vw(300);
width: 300px;
min-height: fit-content;
margin: 6px 0 12px 0;
padding: 12px 12px 2px 12px;
......
......@@ -630,7 +630,7 @@
.chatListItem {
width: 100%;
height: 68px;
padding: 3px vw(20);
padding: 3px 10px;
position: relative;
cursor: pointer;
color: #333333;
......@@ -857,7 +857,7 @@
}
.trans-follow-1 {
width: vw(300);
width: 300px;
min-height: fit-content;
margin: 6px 0 12px 0;
padding: 12px 12px 2px 12px;
......
......@@ -200,7 +200,7 @@
overflow: auto;
.detailsTitle {
width: 100%;
padding: 0 vw(20);
padding: 0 10px;
height: 60px;
font-size: 18px;
font-family: PingFangSC-Medium, PingFang SC;
......@@ -252,7 +252,7 @@
width: 40px;
height: 40px;
border-radius: 6px;
margin-right: vw(10);
margin-right: 10px;
}
.label {
color: #999999;
......@@ -270,10 +270,10 @@
top: 12px;
}
.tags {
width: vw(300);
width: 300px;
margin-left: 10px;
.tagsItem {
width: vw(300);
width: 300px;
}
.tag {
height: 22px;
......
......@@ -357,12 +357,12 @@ export default {
</script>
<style lang="scss" scoped>
.details {
width: vw(444);
width: 100%;
height: 100%;
background: #fff;
.detailsTitle {
width: 100%;
padding: 0 vw(20);
padding: 0 10px;
height: 60px;
font-size: 18px;
font-family: PingFangSC-Medium, PingFang SC;
......@@ -376,7 +376,7 @@ export default {
}
.content {
width: 100%;
padding: vw(20);
padding: 10px;
height: 100%;
background: red;
.contentItemTitle {
......@@ -471,7 +471,7 @@ export default {
width: 40px;
height: 40px;
border-radius: 6px;
margin-right: vw(10);
margin-right: 10px;
}
.label {
color: #999999;
......@@ -489,10 +489,10 @@ export default {
top: 12px;
}
.tags {
width: vw(300);
width:250px;
margin-left: 10px;
.tagsItem {
width: vw(300);
width: 250px;
}
.tag {
height: 22px;
......@@ -534,12 +534,12 @@ export default {
margin-top: 20px;
}
.tagList {
width: vw(130);
width: 150px;
height: 100%;
position: relative;
border-right: 1px solid #e0e0e0;
.tagItem {
width: vw(128);
width: 100px;
height: 36px;
background: #fff;
border-radius: 4px;
......@@ -551,7 +551,7 @@ export default {
margin-bottom: 6px;
cursor: pointer;
.text {
max-width: vw(90);
max-width: 100px;
margin-left: 10px;
}
}
......
......@@ -375,7 +375,7 @@ export default {
width: 40px;
height: 40px;
border-radius: 6px;
margin-right: vw(10);
margin-right: 10px;
}
.label {
color: #999999;
......@@ -393,10 +393,10 @@ export default {
top: 12px;
}
.tags {
width: vw(300);
width: 300px;
margin-left: 10px;
.tagsItem {
width: vw(300);
width: 300px;
}
.tag {
height: 22px;
......@@ -439,13 +439,13 @@ export default {
}
.tagList {
width: vw(130);
width: 200px;
height: 100%;
position: relative;
border-right: 1px solid #e0e0e0;
.tagItem {
width: vw(128);
width: 100px;
height: auto;
background: #fff;
border-radius: 4px;
......@@ -456,7 +456,7 @@ export default {
margin-bottom: 6px;
cursor: pointer;
.text {
max-width: vw(90);
max-width: 90px;
margin-left: 5px;
}
.tagItemGroup {
......
......@@ -354,12 +354,12 @@ export default {
</script>
<style lang="scss" scoped>
.details {
width: vw(444);
width: 100%;
height: 100%;
background: #fff;
.detailsTitle {
width: 100%;
padding: 0 vw(20);
padding: 0 10px;
height: 60px;
font-size: 18px;
font-family: PingFangSC-Medium, PingFang SC;
......@@ -373,7 +373,7 @@ export default {
}
.content {
width: 100%;
padding: vw(20);
padding: 10px;
height: 100%;
background: red;
.contentItemTitle {
......@@ -468,7 +468,7 @@ export default {
width: 40px;
height: 40px;
border-radius: 6px;
margin-right: vw(10);
margin-right: 10px;
}
.label {
color: #999999;
......@@ -486,10 +486,10 @@ export default {
top: 12px;
}
.tags {
width: vw(300);
width:260px;
margin-left: 10px;
.tagsItem {
width: vw(300);
width: 260px;
}
.tag {
height: 22px;
......@@ -531,12 +531,12 @@ export default {
margin-top: 20px;
}
.tagList {
width: vw(130);
width: 140px;
height: 100%;
position: relative;
border-right: 1px solid #e0e0e0;
.tagItem {
width: vw(128);
width: 100px;
height: 36px;
background: #fff;
border-radius: 4px;
......@@ -548,7 +548,7 @@ export default {
margin-bottom: 6px;
cursor: pointer;
.text {
max-width: vw(90);
max-width: 90px;
margin-left: 10px;
}
}
......
<template>
<div class="task-info-container columnFlex">
<!-- <div class="detailsTitle rowFlex spaceBetween columnCenter">
<p>任务记录</p>
</div> -->
<div class="account-task-container-content" v-scroll="requestOrderList" v-loading="loading">
<!-- 运营任务 和 用户任务 -->
<div
class="orderDetailsScroll"
>
<div v-if="orderList.length > 0">
<div
v-for="(item, index) in orderList"
:key="index"
class="orderDetails"
>
<div class="orderDetailsList">
<el-collapse
v-model="collapseValue"
@change="handleChange(item,$event)"
>
<el-collapse-item :name="item.order_id">
<template slot="title">
<div class="orderDetailsTitle">
<div class="money rowFlex spaceBetween">
<p class="text">{{ item.role_name }} - {{ item.server_name }} - ¥{{ item.recharge_total }}</p>
<div class="btns">
<span style="color: #0988f2">{{ taskTypeList.find((items) => items.value == item.plan_type) && taskTypeList.find((items) => items.value == item.plan_type).label?taskTypeList.find((items) => items.value == item.plan_type).label:'' }}</span>
<span
v-if="item.status_name"
class="btn"
:class="[item.status_name == '待跟进' ? 'noSend' : '', item.status_name == '已跟进' ? 'sended' : '', item.status_name == '已完成' ? 'sended' : '', item.status_name == '跟进中' ? 'noSend' : '']"
>{{ item.status_name }}</span>
</div>
</div>
</div>
</template>
<div class="item rowFlex columnCenter spaceBetween">
<div class="rowFlex columnCenter">
<span class="label">跟进客服</span>
<p class="text">{{ item.tracer_name }}</p>
</div>
</div>
<div class="item rowFlex columnCenter spaceBetween">
<div class="rowFlex">
<span class="label">待维护日期</span>
<p class="text">{{ item.assignment_time }}</p>
</div>
</div>
<div class="editLayer">
<el-form
:model="webForm"
>
<!-- 新增异常原因筛选 当 plan_type==5 5:为大R异跟进异常时 新增异常原因筛选 -->
<el-form-item
v-if="taskInfo.plan_type && taskInfo.plan_type==5"
label="异常原因"
prop="abnormal_types"
label-width="100px"
>
<el-select
v-model="webForm.abnormal_types"
style="width: 90%"
placeholder="请选择"
multiple
collapse-tags
>
<el-option
v-for="item in errorTypeList"
:key="item.value"
:label="item.label"
:value="item.value"
>
</el-option>
</el-select>
</el-form-item>
<el-form-item
label="跟进结果:"
prop="trace_result"
label-width="100px"
>
<el-select
v-model="webForm.trace_result"
style="width: 90%"
placeholder="请选择"
>
<el-option
v-for="item in traceList"
:key="item.value"
:label="item.label"
:value="item.value"
> </el-option>
</el-select>
</el-form-item>
<el-form-item
v-if="activeType == 'user_task'"
label="免打扰:"
label-width="80px"
prop="no_trouble"
>
<el-checkbox
v-model="webForm.no_trouble"
class="noDisturb rowFlex allCenter"
:true-label="1"
:false-label="0"
>免打扰(勾选后该用户不会再被分配任务)</el-checkbox>
</el-form-item>
</el-form>
</div>
<div
v-for="(remark, indexs) in item.remarks"
:key="indexs"
class="item rowFlex columnCenter spaceBetween"
>
<div
class="rowFlex spaceBetween"
style="width: 100%;"
>
<span
class="label"
style="width: 50px"
>备注:</span>
<div class="text rowFlex remark flex1">
<textEditor
:remark.sync="remark.remark"
:domid="'taskRemark' + indexs"
:contenteditable="!Boolean(remark.id)"
@resultReamrk="resultReamrk"
/>
</div>
<i
v-if="indexs == 0"
class="el-icon-circle-plus-outline remarkHandle"
type="primary"
@click="addRemark(index)"
></i>
<i
v-else-if="indexs != 0 && !remark.id"
class="el-icon-remove-outline remarkHandle"
type="primary"
@click="removeRemark(index, indexs)"
></i>
</div>
</div>
<div
class="btns rowFlex allCenter"
style="margin-top: 20px"
>
<el-button
:disabled="item.status==3"
:loading="remarkLoading"
@click="saveRemak(item, index)"
>保存</el-button>
<el-button
type="primary"
:disabled="item.status==3"
:loading="taskLoading"
@click="completeTask(item, index)"
>保存并完成任务</el-button>
</div>
</el-collapse-item>
</el-collapse>
</div>
</div>
</div>
<div
v-else-if="!loading && orderList.length == 0"
class="noContent rowFlex allCenter"
>
<noContent />
</div>
</div>
</div>
</div>
</template>
<script>
import { taskTrack, taskRecord, logsSave, searchcondition } from '@/api/game'
import { mapState } from 'vuex'
import textEditor from '@/components/textEditor.vue'
import noContent from '@/components/noContent.vue'
export default {
components: {
textEditor,
noContent
},
data() {
return {
isloadMore: true,
loading: false,
collapseValue: ['1'],
orderList: [],
gameUserInfo: {
recharge_total: 0,
today_amount: 0
},
orderTypeList: [
{ label: '账号任务', type: 1 },
{ label: '用户任务', type: 2 }
],
traceList: [],
webForm: {
trace_result: '',
remark: '',
abnormal_types: [],
no_trouble: 0
},
taskTypeList: [],
errorTypeList: [],
taskInfo: {},
showLayer: false,
taskLoading: false,
remarkLoading: false,
pageInfo: {
page: 0,
page_size: 20,
total: 0
}
}
},
computed: {
...mapState('game', ['accountSelect', 'taskDetails']),
...mapState('user', ['userInfo'])
},
props: {
taskForm: {
type: Object,
default: () => {}
},
activeType: {
type: String,
default: ''
}
},
watch: {
accountSelect(newVal, oldVal) {
if (newVal && newVal !== '') {
this.pageInfo = {
page: 0,
page_size: 20,
total: 0
}
this.isloadMore = true
this.orderList = []
this.requestOrderList()
// this.gameMemberView()
this.searchcondition()
this.searchTrackList()
this.searchconditionError()
}
},
activeType(newVal, oldVal) {
if (newVal && (newVal == 'user_task' || newVal == 'account_task')) {
this.isloadMore = true
this.pageInfo = {
page: 0,
page_size: 20,
total: 0
}
this.orderList = []
this.requestOrderList()
}
}
},
mounted() {
// this.gameMemberView()
this.requestOrderList()
this.searchcondition()
this.searchTrackList()
this.searchconditionError()
},
methods: {
searchcondition() {
const data = {
type: 'dictionaries',
table_name: 'zs_operator_plan',
field_name: 'plan_type'
}
searchcondition(data).then((res) => {
this.taskTypeList = res.data.data
})
},
searchconditionError() {
const data = {
type: 'dictionaries',
table_name: 'zs_operator_task',
field_name: 'abnormal_type'
}
searchcondition(data).then((res) => {
this.errorTypeList = res.data.data
})
},
resultReamrk(text) {
console.log(text, '最后编辑器的内容')
},
searchTrackList() {
const data = {
type: 'dictionaries',
table_name: 'zs_operator_task',
field_name: 'trace_result'
}
searchcondition(data).then((res) => {
this.traceList = res.data.data
})
},
completeTask(item, index) {
this.taskInfo = item
this.onConfirm(item, index)
},
saveRemak(item, index) {
this.remarkLoading = true
const remarks = item.remarks.filter((item) => !item.id && item.remark.trim() !== '')
const data = {
task_id: item.id,
create_name: this.userInfo.username,
create_department: this.userInfo.department_name,
remarks: remarks || []
}
logsSave(data).then((res) => {
if (res.status_code == 1) {
this.remarkLoading = false
this.$message.success(res.msg)
this.isloadMore = true
this.pageInfo = {
page: 0,
page_size: 20,
total: 0
}
this.orderList = []
this.requestOrderList()
this.$forceUpdate()
} else {
this.remarkLoading = false
}
}, (err) => {
console.log(err, 'err')
this.remarkLoading = false
})
},
submitForm(item) {
this.taskLoading = true
const remarks = item.remarks.filter((item) => !item.id && item.remark.trim() !== '')
console.log(item, remarks, 'remarks')
const data = {
id: this.taskInfo.id,
trace_result: this.webForm.trace_result,
abnormal_types: this.webForm.abnormal_types,
create_user: this.userInfo.username,
remark: remarks,
create_department: this.userInfo.department_name
}
this.activeType == 'account_task' ? data.no_trouble = this.webForm.no_trouble : ''
taskTrack(data).then((res) => {
if (res.status_code == 1) {
this.taskLoading = false
this.$message.success(res.msg)
this.showLayer = false
this.webForm = {
trace_result: '',
remark: '',
abnormal_types: [],
no_trouble: 0
}
this.isloadMore = true
this.pageInfo = {
page: 0,
page_size: 20,
total: 0
}
this.orderList = []
this.requestOrderList()
} else {
this.taskLoading = false
}
}, (err) => {
console.log(err, 'err')
this.taskLoading = false
})
},
onConfirm(item, index) {
if (this.webForm.trace_result === '') {
this.$message.warning('请选择跟进结果')
return false
}
if (this.webForm.abnormal_types.length === 0 && this.taskInfo.plan_type === 5) {
this.$message.warning('请选择异常原因')
return false
}
this.submitForm(item, index)
},
addRemark(index) {
this.orderList[index].remarks.push({ remark: '' })
this.$forceUpdate()
},
removeRemark(index, indexs) {
this.orderList[index].remarks.splice(indexs, 1)
},
handleChange(item, $event) {
this.taskInfo = item
this.webForm = {
trace_result: '',
remark: '',
abnormal_types: [],
no_trouble: 0
}
},
requestOrderList() {
if (this.accountSelect == '') {
this.$message.warning('暂无关联的账号,请先去关联账号!')
return false
}
if (!this.isloadMore) {
console.log('没有更多数据了')
return false
}
this.pageInfo.page += 1
this.loading = true
const data = {
member_id: this.accountSelect,
user_type: this.activeType == 'user_task' ? 2 : this.activeType == 'account_task' ? 1 : 0,
...this.pageInfo
}
taskRecord(data).then(
(res) => {
this.loading = false
if (res.data.data && res.data.data.length < 20) {
this.isloadMore = false
}
this.orderList = this.orderList.concat(res.data.data)
this.orderList.map((item) => {
!item.remarks || item.remarks.length === 0 ? (item.remarks = [{ remark: '' }]) : ''
})
if (res.status_code == 1) {
this.$message.success(res.msg)
}
},
(err) => {
this.loading = false
}
)
}
}
}
</script>
<style lang="scss" scoped>
.task-info-container {
width: 100%;
height: 100%;
background: #fff;
position: relative;
overflow: hidden;
.detailsTitle {
width: 100%;
padding: 0 10px;
height: 60px;
font-size: 18px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #333333;
border-bottom: 1px solid #ebeef5;
border-left: 1px solid #ebeef5;
p {
color: #333333;
}
}
.account-task-container-content {
width: 100%;
height: 100%;
overflow: auto;
overflow-x: hidden;
padding: 20px 0;
padding-top: 0px;
.contentItem {
position: relative;
.title {
position: absolute;
left: 10px;
top: 14px;
font-size: 14px;
color: #999999;
}
}
.item {
width: 100%;
height: auto;
font-size: 14px;
font-weight: 400;
color: #333333;
transition: all 0.5s;
position: relative;
cursor: pointer;
div {
width: 100%;
margin-bottom: 5px;
}
.remark {
::v-deep .el-textarea__inner {
height: 80px;
}
}
.tableImage {
width: 40px;
height: 40px;
border-radius: 6px;
margin-right: 10px;
}
.label {
color: #999999;
}
.text {
color: #333333;
margin-left: 10px;
word-break: break-all;
max-width: 75%;
}
.icon {
display: none;
position: absolute;
right: 0;
top: 12px;
}
.dianFail {
display: inline-block;
width: 8px;
height: 8px;
background: #f45454;
border-radius: 5px;
}
.dian {
display: inline-block;
width: 8px;
height: 8px;
background: #00bf8a;
border-radius: 5px;
}
.dian2 {
display: inline-block;
width: 8px;
height: 8px;
background: #ff9d02;
border-radius: 5px;
}
}
.orderMoney {
width: calc(100% + 40px);
height: 80px;
// margin-left: -20px;
padding: 10px 0;
.orderMoneyItem {
width: 50%;
text-align: center;
margin-top: 5px;
span {
font-size: 14px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #333333;
}
p {
font-size: 22px;
color: #00bf8a;
}
}
}
.filterList {
margin-bottom: 10px;
.filterListInput {
width: 60%;
margin-left: 15px;
margin-bottom: 10px;
}
.filterListDate {
width: 150px;
margin-bottom: 10px;
}
::v-deep .search-item .item-label {
margin-right: 20px;
}
}
.orderDetailsScroll {
width: 100%;
}
.orderDetails {
width: 100%;
height: auto;
margin-top: 20px;
position: relative;
.bridgeMain {
position: absolute;
top: 0px;
right: 0px;
width: 50px;
height: 50px;
.text {
font-size: 8px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #ff9d02;
transform: rotate(48deg);
z-index: 100;
position: absolute;
right: -6px;
top: 10px;
width: 50px;
text-align: center;
}
.bridge {
font-size: 50px;
position: absolute;
top: 0;
right: 0;
}
}
.orderDetailsTitle {
width: 100%;
background: #f9faff;
.money {
width: 100%;
height: auto;
padding-left: 10px;
.btns {
padding-right: 40px;
}
.btn {
background: #fff;
border-radius: 4px;
padding: 2px 5px;
margin-left: 10px;
font-size: 12px;
border: 1px solid rgba(0, 0, 0, 0.15);
color: #333333;
cursor: pointer;
}
.btnnot {
background: #ffdddd;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #f56c6c;
border: none;
}
.btnsuccess {
background: #e1fff0;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #00bf8a;
border: none;
}
.sended {
padding: 0 8px;
height: 20px;
line-height: 20px;
background: #e1fff0;
border-radius: 4px;
font-size: 12px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #00bf8a;
}
.noSend {
padding: 0 8px;
height: 20px;
line-height: 20px;
background: #fffae0;
border-radius: 4px;
font-size: 12px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #ffa81d;
}
}
.text {
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #333333;
max-width: 200px;
overflow: hidden;
white-space: nowrap; /* 防止文字换行 */
text-overflow: ellipsis; /* 超出部分显示省略号 */
}
}
.orderDetailsList {
width: 100%;
height: auto;
background: #ffffff;
border: 1px solid #ebeef5;
position: relative;
.titleFix {
position: absolute;
left: 10px;
top: 20px;
color: #999999;
}
}
}
}
.remarkHandle {
font-size: 20px;
color: #0ac358;
cursor: pointer;
margin-right: 5px;
position:absolute;
right: 0;
top: 0;
}
::v-deep .el-tabs__item {
line-height: 26px;
font-size: 16px;
font-weight: 500;
}
::v-deep .el-collapse {
border: none;
}
::v-deep .el-collapse-item__header {
width: 100%;
color: #333333;
font-size: 14px;
font-weight: 400;
/* 单行显示省略号 */
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
::v-deep .el-collapse-item__arrow {
position: absolute;
right: 5px;
}
::v-deep .el-collapse-item__content{
padding: 10px;
}
.editLayer {
margin-left: -20px;
.noDisturb{
// 换行
white-space: wrap;
}
::v-deep .el-form-item__label{
font-weight: normal;
color: #999999;
}
::v-deep .el-form-item--small.el-form-item{
margin-bottom: 10px;
}
}
}
</style>
\ No newline at end of file
......@@ -413,8 +413,7 @@ export default {
.search-type-select {
width: 80px;
flex-shrink: 0;
margin-right: -10px;
::v-deep .el-input__inner {
border: 1px solid #dcdcdc;
border-radius: 4px;
......@@ -425,7 +424,6 @@ export default {
.search-input {
flex: 1;
::v-deep .el-input__inner {
border: 1px solid #dcdcdc;
border-radius: 4px;
......
......@@ -470,7 +470,7 @@ export default {
.detailsTitle {
width: 100%;
padding: 0 vw(20);
padding: 0 10px;
height: 60px;
font-size: 18px;
font-family: PingFangSC-Medium, PingFang SC;
......@@ -559,7 +559,7 @@ export default {
width: 40px;
height: 40px;
border-radius: 6px;
margin-right: vw(10);
margin-right: 10px;
}
.label {
......
......@@ -105,7 +105,7 @@ export default {
}
.detailsTitle {
width: 100%;
padding: 0 vw(20);
padding: 0 10px;
height: 60px;
font-size: 18px;
font-family: PingFangSC-Medium, PingFang SC;
......
......@@ -66,7 +66,7 @@ export default {
}
.detailsTitle {
width: 100%;
padding: 0 vw(20);
padding: 0 10px;
height: 60px;
font-size: 18px;
font-family: PingFangSC-Medium, PingFang SC;
......
<template>
<div class="taskList">
<el-tabs v-model="taskForm.type" @tab-click="taskTypeChange">
<el-tab-pane v-for="(item, index) in typeList" :key="index" :label="item.label" :name="item.value + ''">
<template slot="label">
<!-- 有数字 -->
<div v-if="item.redNum && item.redNum > 0">
<el-badge is-dot class="badgeItem">
<span>{{ item.label }}</span>
</el-badge>
<!-- <el-badge
v-else
:value="item.redNum"
class="badgeItem"
>
<span>{{ item.label }}</span>
</el-badge> -->
</div>
<div v-else>
<span class="badgeLabel">{{ item.label }}</span>
</div>
</template>
</el-tab-pane>
</el-tabs>
<!-- 运营任务 -->
<div class="taskListContent" v-if="taskForm.type !== 'user_task' && taskForm.type !== 'account_task'" v-scroll="paperScroll" >
<el-form ref="taskForm" :model="taskForm" class="taskForm" label-position="top" label-width="85px">
<!-- 运营任务 -->
<div v-if="taskForm.type == 1" class="taskFormItem">
<el-form-item label="跟进状态">
<el-select v-model="taskForm.status" style="width: 100%" placeholder="请选择状态"
@change="filterChange">
<el-option v-for="item in traceList" :key="item.value" :label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="W账号">
<div class="rowFlex columnCenter">
<el-select v-model="userSelect" placeholder="请选择" :clearable="false" @change="filterChange">
<el-option v-for="item in userSelectList" :key="item.value" :label="item.label"
:value="item.value">
</el-option>
</el-select>
<el-input v-model="taskForm.username" :placeholder="userSelect == 1 ? '请输入w账号' : '请输入用户账号'"
@change="filterChange">
</el-input>
</div>
</el-form-item>
<el-form-item label="主游戏">
<mainGameSelect label="" :default-value="taskForm.main_game_id[0]" style="width: 100%"
width="100%" @result="mainGameResult" />
</el-form-item>
<el-form-item label="已跟进次数">
<el-select v-model="taskForm.trace_num" style="width: 100%" placeholder="请选择"
@change="filterChange">
<el-option v-for="item in trace_num_list" :key="item.value" :label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="分配时间">
<selectDate :width="'100%'" type="datetimerange" :defaultValue="taskForm.timeDate"
@result="selectChange" />
</el-form-item>
<el-form-item label="任务类型">
<el-select v-model="taskForm.plan_type" style="width: 100%" multiple collapse-tags
placeholder="请选择" @change="filterChange">
<el-option v-for="item in taskTypeList" :key="item.value" :label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="计划名称">
<el-select v-model="taskForm.plan_id" style="width: 100%" placeholder="请输入计划名称" filterable
clearable remote :remote-method="requstPlanList" :loading="selectLoading"
@change="filterChange">
<el-option v-for="item in planList" :key="item.value" :label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
</div>
<!-- 举报申请 -->
<div v-if="taskForm.type == 2 || taskForm.type == 3 || taskForm.type == 4" class="taskFormItem">
<el-form-item label="W账号">
<el-input v-model="reportForm.username" style="width: 100%" placeholder="请输入w账号" clearable
@change="filterChange">
</el-input>
</el-form-item>
<el-form-item label="角色名称">
<el-input v-model="reportForm.role_name" style="width: 100%" clearable placeholder="请输入角色名称"
@change="filterChange">
</el-input>
</el-form-item>
<el-form-item v-if="taskForm.type != 4" label="审批状态">
<el-select v-model="reportForm.approval_status" style="width: 100%" clearable
placeholder="请选择审批状态" @change="filterChange">
<el-option v-for="item in approvalList" :key="item.value" :label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="登记时间">
<selectDate style="width: 100%" format="yyyy-MM-dd HH:mm:ss" value-format="yyyy-MM-dd HH:mm:ss"
:default-time="['00:00:00', '23:59:59']" type="datetimerange"
:defaultValue="reportForm.createTimeDate" @result="createResult" />
</el-form-item>
</div>
</el-form>
<!-- <div class="filterChat rowFlex">
<div
ref="btnList"
class="btnList rowFlex columnCenter flexWarp"
>
<el-button
v-for="(item,index) in taskTypeList"
:key="index"
class="btnListScroll"
:class="item.value===plan_type?'itemActive':'btnItem'"
round
@click="handleClick(item)"
>{{ item.label || '' }}</el-button>
</div>
</div> -->
<div v-if="taskForm.type == 1" class="taskSort rowFlex columnCenter spaceBetween">
<p>任务数:{{ taskListNum || 0 }}</p>
<p class="rowFlex columnCenter sortIcon">
<span>按金额排序</span>
<span class="columnFlex columnCenter">
<i class="el-icon-caret-top" style="margin-bottom: -4px" :class="topActive ? 'activeIcon' : ''"
@click="iconSort('top')"></i>
<i class="el-icon-caret-bottom" style="margin-top: -4px"
:class="bottomActive ? 'activeIcon' : ''" @click="iconSort('bottom')"></i>
</span>
</p>
</div>
<div class="taskListScroll" v-loading="loading"
:class="taskForm.type != 1 ? 'taskListScrollActive' : ''">
<!-- 运营任务 -->
<div v-if="taskForm.type == 1" class="scrollMain">
<div v-for="(item, index) in taskList" :key="index" class="chatListItem columnCenter spaceBetween"
:class="item.id === taskDetails.id ? 'chatListItemActive' : ''" @click="selectTaskItem(item)">
<div class="top rowFlex spaceBetween">
<div v-if="item.username && item.username.split('\n').length > 0" class="value">
<div v-if="item.username.split('\n').length <= 1" class="rowFlex columnCenter">
<div v-for="(items, indexs) in item.username.split('\n')" :key="indexs"
class="rowFlex columnCenter userInfoStyle">
<p class="textHidden rowFlex columnCenter userAlias" style="max-width:150px;">
{{ items }}
</p>
</div>
</div>
<el-popover v-else placement="top" trigger="click">
<div style="max-width: 400px">
<p v-for="(items, indexs) in item.username.split('\n')" :key="indexs"
class="rowFlex columnCenter userInfoStyle" style="margin-bottom: 10px;">
<span class="textHidden" style="max-width:150px;">{{ items }}</span>
</p>
</div>
<el-button slot="reference" type="text">{{ item.username.split("\n").length
}}个</el-button>
</el-popover>
</div>
<p>
已跟进
<span style="color: #0787f2">{{ item.trace_num || 0 }}</span>
</p>
</div>
<div class="center rowFlex spaceBetween">
<p class="info">
{{ item.role_name }}
<span style="color: #0787f2">{{
taskTypeList.find((k) => k.value == item.plan_type) && taskTypeList.find((k) =>
k.value
==
item.plan_type).label ? taskTypeList.find((k) => k.value == item.plan_type).label :
""
}}</span>
</p>
<div class="right">¥{{ formatNumber(item.member_recharge) }}</div>
</div>
<div class="bottom">分配时间:{{ item.assignment_time }}</div>
</div>
</div>
<!-- 举报申请 玩家投诉 -->
<div v-if="taskForm.type == 2 || taskForm.type == 3 || taskForm.type == 4" class="scrollMain">
<div v-for="(item, index) in taskList" :key="index"
class="reportItem rowFlex spaceBetween columnCenter"
:class="item.id === taskDetails.id ? 'chatListItemActive' : ''" @click="selectTaskItem(item)">
<div class="reportItemLeft">
<p>{{ item.username }}</p>
<p>
<span class="label">角色名称:</span><span class="value">{{ item.role_name }}</span>
</p>
<p>
<span class="label">累计充值:</span><span class="value">{{ item.recharge_total_amount
}}</span>
</p>
<p>
<span class="label">近一周充值:</span><span class="value">{{ item.recharge_week_amount
}}</span>
</p>
<p v-if="taskForm.type == 2">
<span class="label">违规操作:</span><span class="value">{{ item.violation_type_text
}}</span>
</p>
<p v-else-if="taskForm.type == 3">
<span class="label">违规操作:</span><span class="value">{{ item.appeal_type_text }}</span>
</p>
<p>
<span class="label">登记时间:</span><span class="value">{{ item.create_time }}</span>
</p>
</div>
<div class="reportItemRight columnFlex columnCenter">
<el-button
v-if="item.approval_status == 1 && item.create_user_id == userInfo.id && taskForm.type != 4"
type="primary" size="mini" class="handleReport"
@click="handleReport(item)">撤销</el-button>
<img v-if="item.approval_status == 1" :src="shenpi1" class="icon" />
<img v-else-if="item.approval_status == 2" :src="shenpi2" class="icon" />
<img v-else-if="item.approval_status == 3" :src="shenpi3" class="icon" />
<img v-else-if="item.approval_status == 4" :src="shenpi4" class="icon" />
<img v-else-if="item.approval_status == 5" :src="shenpi5" class="icon" />
</div>
</div>
</div>
</div>
<noContent v-if="taskList.length == 0" />
</div>
<!-- 用户任务 和 运营任务 -->
<div class="taskListContent" v-else-if="taskForm.type == 'user_task' || taskForm.type == 'account_task'">
<userTask :taskForm="taskForm" :activeType="taskForm.type" />
</div>
</div>
</template>
<script>
import {
taskIndex,
searchcondition,
reportIndex,
appealList,
reportCancel,
appealCancel
} from '@/api/game'
import { getTaskUnReadData, clearTaskUnReadData } from '@/api/works'
import { mapState, mapMutations } from 'vuex'
import mainGameSelect from '@/components/mainGame.vue'
import { removeDp } from '@/utils/index'
import { report_request_list, playerReport } from '@/api/game'
import selectDate from '@/components/selectDate.vue'
import { formatNumber } from '@/utils/index'
import shenpi1 from '@/assets/icon/shenpi1.svg'
import shenpi2 from '@/assets/icon/shenpi2.svg'
import shenpi3 from '@/assets/icon/shenpi3.svg'
import shenpi4 from '@/assets/icon/shenpi4.svg'
import shenpi5 from '@/assets/icon/shenpi5.svg'
import noContent from '@/components/noContent.vue'
import userTask from './components/taskList/uesrTask.vue'
export default {
props: {
},
watch: {
accountSelect(newVal, oldVal) {
console.log(newVal, 'newVal')
if (newVal && newVal !== '') {
this.filterChange()
}
},
},
computed: {
...mapState('game', ['taskDetails', 'accountSelect']),
...mapState('user', ['userInfo', 'corp_id'])
},
components: {
mainGameSelect,
selectDate,
noContent,
userTask
},
data() {
return {
formatNumber,
taskForm: {
type: '1',
status: '',
trace_num: '',
main_game_id: [],
username: '',
timeDate: [],
plan_type: '',
plan_id: '',
field: 'recharge_total',
order: '',
assignment_time_start: '',
assignment_time_end: ''
},
selectLoading: false,
taskListNum: false,
userSelect: 1,
userSelectList: [
{
label: 'W账号',
value: 1
},
{
label: '用户账号',
value: 2
}
],
topActive: false,
bottomActive: false,
reportForm: {
customer_id: '',
member_id: '',
role_id: '',
username: '',
role_name: '',
approval_status: '',
create_time_start: '',
createTimeDate: [],
create_time_end: ''
},
pageInfo: {
page: 1,
page_size: 20
},
loading: false,
typeList: [
// 举报申请 玩家申诉 流程结束才会推送红点给客服 玩家举报 新消息进来的时候有红点
{
label: '运营任务',
type: 'operator_task',
value: 1
},
{
label: '用户任务',
type: 'user_task',
value: 'user_task'
},
{
label: '账号任务',
type: 'account_task',
value: 'account_task'
},
// {
// label: '举报申请',
// type: 'report_request',
// value: '2'
// },
// {
// label: '玩家申诉',
// type: 'appeal_request',
// value: '3'
// },
// {
// label: '玩家举报',
// type: 'member_report_request',
// value: '4'
// }
],
taskList: [],
isMoreRecord: false,
traceList: [
{
label: '全部',
value: ''
},
{
label: '待跟进',
value: 1
},
{
label: '跟进中',
value: 2
},
{
label: '已完成',
value: 3
}
],
trace_num_list: [
{
label: '全部',
value: ''
},
{
label: 1,
value: 1
},
{
label: 2,
value: 2
},
{
label: 3,
value: 3
},
{
label: 4,
value: 4
},
{
label: 5,
value: 5
}
],
approvalList: [],
taskTypeList: [],
planList: [],
// 审批状态图标
shenpi1,
shenpi2,
shenpi3,
shenpi4,
shenpi5
}
},
created() {
this.getTaskUnReadData()
this.reportForm.create_time_start = this.$moment().subtract(30, 'days').format('YYYY-MM-DD 00:00:00')
this.reportForm.create_time_end = this.$moment().format('YYYY-MM-DD 23:59:59')
this.reportForm.createTimeDate = [this.reportForm.create_time_start, this.reportForm.create_time_end]
this.taskForm.timeDate = [this.$moment().subtract(7, 'days').format('YYYY-MM-DD 00:00:00'), this.$moment().format('YYYY-MM-DD 23:59:59')]
this.taskForm.assignment_time_start = this.$moment().subtract(7, 'days').format('YYYY-MM-DD 00:00:00')
this.taskForm.assignment_time_end = this.$moment().format('YYYY-MM-DD 23:59:59')
this.requstPlanList('')
},
mounted() {
const taskForm = window.sessionStorage.getItem('newTaskForm')
if (taskForm) {
const form = JSON.parse(taskForm)
!form.type || form.type == '' ? (form.type = '1') : form.type = form.type + ''
// if (form.assignment_time_start == '') {
// form.timeDate = [this.$moment().subtract(7, 'days').format('YYYY-MM-DD 00:00:00'), this.$moment().format('YYYY-MM-DD 23:59:59')]
// form.assignment_time_start = this.$moment().subtract(7, 'days').format('YYYY-MM-DD 00:00:00')
// form.assignment_time_end = this.$moment().format('YYYY-MM-DD 00:00:00')
// }
this.taskForm = form
}
this.requestList()
this.searchcondition()
this.requstApprovalList()
},
methods: {
...mapMutations('game', ['set_taskDetails']),
filterChange() {
this.pageInfo.page = 1
this.taskList = []
this.isMoreRecord = true
this.requestList()
},
// 获取更新
getTaskUnReadData() {
let taskNum = {}
getTaskUnReadData().then(res => {
if (res.status_code === 1) {
taskNum = res.data
this.typeList.map(item => {
for (const key in taskNum) {
if (item.type == key) {
item.redNum = taskNum[key]
}
}
})
this.typeList[0].redNum = 0
this.$forceUpdate()
}
})
},
iconSort(type) {
if (type == 'top') {
this.bottomActive = false
this.topActive = !this.topActive
if (this.topActive) {
console.log('top 排序')
this.taskForm.order = 'ASC'
} else {
this.taskForm.order = ''
console.log('默认排序')
}
} else {
this.topActive = false
this.bottomActive = !this.bottomActive
if (this.bottomActive) {
console.log('bottom 排序')
this.taskForm.order = 'DESC'
} else {
this.taskForm.order = ''
console.log('默认排序')
}
}
this.requestList()
},
taskTypeChange() {
this.reportForm.create_time_start = this.$moment().subtract(30, 'days').format('YYYY-MM-DD 00:00:00')
this.reportForm.create_time_end = this.$moment().format('YYYY-MM-DD 23:59:59')
const tabItem = this.typeList.find((item) => item.value == this.taskForm.type)
this.loading = true
this.set_taskDetails({})
this.filterChange()
if (tabItem.redNum > 0) {
this.clearTaskUnReadData(tabItem.type)
}
},
// 清除红点
async clearTaskUnReadData(type) {
const res = await clearTaskUnReadData({ type: type })
this.getTaskUnReadData()
},
mainGameResult(data) {
this.taskForm.main_game_id = [data]
this.filterChange()
this.requstPlanList()
},
selectChange(data) {
if (data) {
this.taskForm.assignment_time_start = data[0]
this.taskForm.assignment_time_end = data[1]
} else {
this.taskForm.assignment_time_start = ''
this.taskForm.assignment_time_end = ''
}
this.filterChange()
},
createResult(data) {
if (data) {
this.reportForm.create_time_start = data[0]
this.reportForm.create_time_end = data[1]
} else {
this.reportForm.create_time_start = ''
this.reportForm.create_time_end = ''
}
this.filterChange()
},
async requstApprovalList() {
const data = {
type: 'dictionaries',
table_name: 'zs_refund_request',
field_name: 'approval_status'
}
const res = await searchcondition(data)
if (res.status_code === 1) {
this.approvalList = res.data.data
}
},
async requstPlanList(query) {
this.selectLoading = true
const data = {
type: 'operator_plan',
plan_name: query,
main_game_id: this.taskForm.main_game_id[0]
}
const res = await searchcondition(data)
this.selectLoading = false
if (res.status_code === 1) {
this.planList = res.data.data
}
},
searchcondition() {
const data = {
type: 'dictionaries',
table_name: 'zs_operator_plan',
field_name: 'plan_type'
}
searchcondition(data).then((res) => {
this.taskTypeList = res.data.data
})
},
selectResult(value) {
this.reportForm.role_name = value
},
selectTaskItem(item) {
this.set_taskDetails(item)
},
handleClick(item) {
this.taskForm.plan_type = item.value
this.filterChange()
},
paperScroll() {
if (this.isMoreRecord) {
this.requestNextPage()
} else {
console.log('没有更多了')
}
},
requestNextPage(pageInfo) {
console.log('下一页')
this.pageInfo.page++
this.requestList()
},
requestList() {
if (this.taskForm.type == 1) {
this.taskIndex()
} else if (this.taskForm.type == 2) {
this.reportIndex()
} else if (this.taskForm.type == 3) {
this.appealList()
} else if (this.taskForm.type == 4) {
this.report_request_list()
}
},
handleCurrentChange(val) {
this.pageInfo.page = val
this.requestList()
},
handleReport(item) {
this.$confirm('确定要撤销该申请么?', '确认提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
.then(() => {
if (this.taskForm.type == 2) {
this.reportCancel(item)
} else if (this.taskForm.type == 3) {
this.appealCancel(item)
}
})
.catch(() => {
this.$message({
type: 'info',
message: '已取消'
})
})
},
reportCancel(item) {
const data = {
id: item.id,
user_id: this.userInfo.id,
user_name: this.userInfo.username
}
reportCancel(data).then((res) => {
if (res.status_code === 1) {
this.$message({
type: 'success',
message: '撤销成功!'
})
this.loading = true
this.filterChange()
}
})
},
appealCancel(item) {
const data = {
id: item.id,
user_id: this.userInfo.id,
user_name: this.userInfo.username
}
appealCancel(data).then((res) => {
if (res.status_code === 1) {
this.$message({
type: 'success',
message: '撤销成功!'
})
this.loading = true
this.filterChange()
}
})
},
async taskIndex() {
if (this.pageInfo.page == 1) {
this.taskList = []
}
const {
status,
trace_num,
main_game_id,
username,
plan_type,
plan_id,
assignment_time_start,
order,
assignment_time_end
} = this.taskForm
const data = {
status,
trace_num,
main_game_id,
plan_type,
plan_id,
field: 'recharge_total',
order: this.taskForm.order,
assignment_time_start,
assignment_time_end,
...this.pageInfo
}
this.userSelect == 1
? (data.username = username)
: (data.user = username)
window.sessionStorage.setItem('newTaskForm', JSON.stringify(this.taskForm))
try {
const res = await taskIndex(data)
this.loading = false
if (this.pageInfo.page == 1) {
this.taskList = res.data.data
} else {
this.taskList = removeDp(this.taskList, res.data.data, 'id')
}
if (!this.taskDetails.id && this.taskList.length > 0) {
this.set_taskDetails(this.taskList[0])
}
if (res.data.data.length < 20) {
this.isMoreRecord = false
} else {
this.isMoreRecord = true
}
this.taskListNum = res.data?.page_info?.total || 0
} catch (error) {
this.loading = false
}
},
// 举报列表
async reportIndex() {
if (this.pageInfo.page == 1) {
this.taskList = []
}
const { username, role_name, approval_status, create_time_start, create_time_end } = this.reportForm
const { id } = this.userInfo
const data = {
username,
role_name,
approval_status,
customer_id: id,
create_time_start,
create_time_end,
...this.pageInfo
}
try {
const res = await reportIndex(data)
this.loading = false
if (this.pageInfo.page == 1) {
this.taskList = res.data.data
} else {
this.taskList = removeDp(this.taskList, res.data.data, 'id')
}
if (!this.taskDetails.id && this.taskList.length > 0) {
this.set_taskDetails(this.taskList[0])
}
if (res.data.data.length < 20) {
this.isMoreRecord = false
} else {
this.isMoreRecord = true
}
} catch (error) {
this.loading = false
}
},
// 审核列表
async appealList() {
if (this.pageInfo.page == 1) {
this.taskList = []
}
const { username, role_name, approval_status, create_time_start, create_time_end } = this.reportForm
const { id } = this.userInfo
const data = {
username,
role_name,
approval_status,
customer_id: id,
create_time_start,
create_time_end,
...this.pageInfo
}
try {
const res = await appealList(data)
this.loading = false
if (this.pageInfo.page == 1) {
this.taskList = res.data.data
} else {
this.taskList = removeDp(this.taskList, res.data.data, 'id')
}
if (!this.taskDetails.id && this.taskList.length > 0) {
this.set_taskDetails(this.taskList[0])
}
if (res.data.data.length < 20) {
this.isMoreRecord = false
} else {
this.isMoreRecord = true
}
} catch (error) {
this.loading = false
}
},
async report_request_list() {
if (this.pageInfo.page == 1) {
this.taskList = []
}
const { username, role_name, create_time_start, create_time_end } = this.reportForm
const { id } = this.userInfo
const user_name = this.userInfo.username
// approval_status 1 待审批 2 审批中 3 通过 4 驳回 5 已撤销 默认是待审批
const data = {
username,
role_name,
// 这歌地方写死是 1 待审批状态
// approval_status: 1,
// customer_id: id,
user_id: id,
user_name: user_name,
create_time_start,
create_time_end,
register_type: 2, // register_type 1 客服登记 2 玩家登记 默认是 2
...this.pageInfo
}
try {
const res = await playerReport(data)
console.log(res.data.data, 'res')
this.loading = false
if (this.pageInfo.page == 1) {
this.taskList = res.data.data
} else {
this.taskList = removeDp(this.taskList, res.data.data, 'id')
}
if (!this.taskDetails.id && this.taskList.length > 0) {
this.set_taskDetails(this.taskList[0])
}
if (res.data.data.length < 20) {
this.isMoreRecord = false
} else {
this.isMoreRecord = true
}
} catch (error) {
this.loading = false
}
}
}
}
</script>
<style lang="scss" scoped>
.taskList {
width: 100%;
height: calc(100%);
padding: 10px;
::v-deep .el-tabs {
height: auto;
}
.taskListContent {
width: 100%;
height: calc(100% - 40px);
overflow-y: auto;
}
.taskFormItem {
margin-top: 10px;
}
.taskForm {
::v-deep .el-form-item {
margin-bottom: 5px;
}
::v-deep .el-tabs__item {
padding: 0 10px;
}
::v-deep .el-tabs__nav-next,
::v-deep .el-tabs__nav-prev {
line-height: 50px;
}
}
// 会话筛选
.filterChat {
width: 100%;
margin-top: 20px;
::v-deep .el-button {
margin-left: 6px;
margin-right: 0 !important;
}
.btnListScroll {
margin-bottom: 10px;
}
.chatIcon {
font-size: 20px;
margin-left: 5px;
margin-top: 10px;
cursor: pointer;
}
.itemActive {
background: #e1fff0;
font-size: 12px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #00bf8a;
border: none;
}
.btnList {
width: 100%;
margin-bottom: 10px;
overflow: auto;
.btnItem {
border: none;
background: #eef0f4;
font-size: 12px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #6e7073;
}
}
}
// 任务排序
.taskSort {
width: 100%;
padding: 0 10px 10px 10px;
.sortIcon {
i {
font-size: 16px;
cursor: pointer;
color: #c0c4cc;
}
.activeIcon {
color: #38ce81;
}
}
}
.taskListScroll {
width: 100%;
.scrollMain {
width: 100%;
height: auto;
margin-bottom: 50px;
padding: 0 10px;
//举报申请
.reportItem {
padding: 10px;
background: #f7f8fa;
margin-bottom: 10px;
position: relative;
cursor: pointer;
color: #333333;
font-size: 14px;
font-weight: 500;
height: auto;
position: relative;
p {
line-height: 25px;
}
.reportItemLeft {
.label {
font-weight: 400;
font-size: 14px;
color: #86909c;
line-height: 20px;
margin-right: 10px;
}
.value {
font-weight: 400;
font-size: 14px;
color: #333333;
line-height: 20px;
text-align: left;
}
}
.reportItemRight {
height: 100%;
.handleReport {
position: absolute;
top: 15px;
right: 5px;
}
.icon {
font-size: 50px;
position: relative;
right: -8px;
top: 5px;
}
}
}
.chatListItem {
width: 100%;
height: auto;
padding: 8px 10px;
position: relative;
cursor: pointer;
color: #333333;
font-size: 14px;
font-weight: 500;
border-bottom: 1px solid #ececec;
.left {
color: #333333;
font-size: 14px;
font-weight: 500;
}
.info,
.top {
font-size: 14px;
font-weight: 400;
}
.bottom {
font-size: 14px;
color: #999999;
font-weight: 400;
}
.right {
font-size: 12px;
font-weight: 400;
}
}
}
.chatListItemActive {
background: #e4fff1 !important;
}
.chatListItem:hover {
background: #e4fff1 !important;
}
}
}
.badgeItem {
::v-deep .is-dot {
top: 8px;
}
::v-deep .el-badge__content {
top: 8px;
}
}
.badgeLabel {
position: relative;
top: 2px;
}
</style>
\ No newline at end of file
......@@ -453,7 +453,7 @@ export default {
</script>
<style lang="scss" scoped>
.task-info-container {
width: vw(424);
width: 100%;
height: 100%;
background: #fff;
margin-left: 2px;
......@@ -461,7 +461,7 @@ export default {
overflow: hidden;
.detailsTitle {
width: 100%;
padding: 0 vw(20);
padding: 0 10px;
height: 60px;
font-size: 18px;
font-family: PingFangSC-Medium, PingFang SC;
......@@ -529,7 +529,7 @@ export default {
width: 40px;
height: 40px;
border-radius: 6px;
margin-right: vw(10);
margin-right: 10px;
}
.label {
color: #999999;
......
......@@ -21,8 +21,29 @@
>
<p>高风险用户,禁止转端 !!!</p>
</div>
<div class="cser_info">
<div class="cser_name">
<span>当前客服:{{ cser_name }}</span>
<span>当前客服:{{ `${cser_name}(${clientStatusText})` }}</span>
</div>
<!-- 添加客服状态显示及按钮 -->
<div class="cser_status">
<div class="status-actions">
<el-button type="danger" style="margin-left: 0px;" size="mini" @click="logout">下线</el-button>
<!-- 休息中状态显示结束休息按钮 -->
<el-button v-if="clientStatus === 'rest'" type="primary" size="mini" @click="handleFinishRest">结束休息</el-button>
<!-- 在线状态显示开始休息按钮 -->
<el-tooltip v-if="clientStatus === 'online'" content="午休或者临时有事可点击休息" placement="top">
<el-button type="warning" size="mini" @click="handleStartRest" >开始休息</el-button>
</el-tooltip>
<!-- 发送评价按钮 -->
<el-button type="primary" style="margin-left: 0px;" size="mini" @click="handleSendComment">发送评价</el-button>
</div>
</div>
</div>
<!-- 会话内容存档状态 -->
<div class="archive-status" v-if="agreeStatus!=='Agree' || !hasPermit">
<p v-if="agreeStatus!=='Agree'">当前微信用户未开启会话内容存档</p>
<p v-if="!hasPermit">当前客服号未授权开启会话内容存档</p>
</div>
<div class="item rowFlex">
<!-- 公共的信息 -->
......@@ -135,17 +156,12 @@
chatUserDetails.tag_group &&
chatUserDetails.tag_group.length > 0
"
class="tags rowFlex columnCenter flexWarp"
>
<!-- 第一个标签组的所有标签 -->
<div class="tagsItem rowFlex columnCenter flexWarp">
<span
<el-tag
v-for="(items, indexs) in chatUserDetails.tag_group[0].tag"
:key="indexs"
class="tag hidden"
>{{ items.name }}</span>
</div>
>{{ items.name }}</el-tag>
<!-- 如果有多个标签组,显示+n -->
<el-popover
v-if="chatUserDetails.tag_group.length > 1"
......@@ -159,11 +175,11 @@
:key="groupIndex"
class="group-item"
>
<span
<el-tag
v-for="(tagItem, tagIndex) in group.tag"
:key="tagIndex"
class="tag-item"
>{{ tagItem.name }}</span>
style="margin-right: 10px;"
>{{ tagItem.name }}</el-tag>
</div>
</div>
<span
......@@ -199,7 +215,7 @@
</div>
</template>
<script>
import { mapState,mapMutations } from 'vuex'
import { mapState, mapMutations, mapActions } from 'vuex'
import gameDetails from './gameInfo/gameUserInfo.vue'
import shareInfo from './shareInfo.vue'
import changePhone from './changePhone.vue'
......@@ -207,6 +223,9 @@ import watchMember from '@/mixins/watchMember'
import { autoResetPassword,bindUserSelfAdd } from '@/api/game'
import { memberBindCser,editUser,zyouUnBind } from '@/api/works'
import selectTag from '@/components/selectTag.vue'
import { getClientStatus, remarkSessionIntelTag, finishRest, client_session_rest, checkSingleAgree, checkUserPermit, sendComment, logout } from '@/api/user.js'
import { sendChatMessage } from '@/utils/index.js'
import { getToken,removeToken } from '@/utils/auth'
export default {
name: 'info',
components: {
......@@ -233,7 +252,10 @@ import watchMember from '@/mixins/watchMember'
showInputValue: '',
inputIndex: -1,
changePhone:false,
showTag:false
showTag:false,
// 新增状态数据
agreeStatus: '', // 用户是否同意聊天内容存档:Agreen同意 Disagree不同意
hasPermit: false, // 客服号是否开启会话内容存档权限
}
},
computed: {
......@@ -243,13 +265,208 @@ import watchMember from '@/mixins/watchMember'
'bindGameUserList',
'viewLoading'
]),
...mapState('user', ['cser_info','cser_id','cser_name'])
...mapState('user', ['cser_info', 'cser_id', 'cser_name', 'corp_id', 'external_userid', 'userid', 'client_online_status']),
// 客服状态文本
clientStatusText() {
const statusMap = {
'online': '在线',
'offline': '离线',
'rest': '休息中'
}
return statusMap[this.client_online_status] || '未知'
},
// 客服休息状态:online上线 offline下线 rest休息中
clientStatus() {
return this.client_online_status
},
},
mixins: [watchMember],
created() {
// 初始化企业微信SDK
this.initializeWecom()
// 获取客服状态和相关信息
this.getInitialData()
},
mounted() {
},
methods: {
...mapMutations('game', ['set_accountSelect']),
...mapActions('user', ['initWecom']),
// 初始化企业微信SDK
async initializeWecom() {
try {
console.log('🚀 开始初始化企业微信 SDK')
const result = await this.initWecom()
console.log('✅ 企业微信 SDK 初始化成功', result)
} catch (error) {
console.error('❌ 企业微信 SDK 初始化失败:', error)
}
},
logout(){
if(this.client_online_status === 'rest'){
this.$message({
type: 'error',
message: '当前客服号处于休息状态,不能下线'
})
return
}
this.$confirm('确定下线吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.userLogout()
}).catch(() => {
this.$message({
type: 'info',
message: '已取消'
})
})
},
async userLogout(){
const data = {
userid: this.userid,
}
const res = await logout(data)
if(res.status_code === 1){
this.$message({
type: 'success',
message: '下线成功'
})
removeToken()
window.location.href = window.location.origin +'/company_app/index.html?corp_id='+this.corp_id
}else{
this.$message({
type: 'error',
message: '下线失败'
})
}
},
// 获取初始数据
async getInitialData() {
try {
// 1. 获取客服休息状态
const statusRes = await getClientStatus()
if (statusRes.status_code === 1) {
this.$store.commit('user/set_client_online_status', statusRes.data.client_online_status)
}
// 2. 同步智能标签
this.syncIntelligentTags()
// 3. 检查用户是否同意聊天内容存档
this.checkAgreeStatus()
// 4. 检查客服号是否开启会话内容存档
this.checkPermitStatus()
} catch (error) {
console.error('获取初始数据失败:', error)
}
},
// 同步智能标签
async syncIntelligentTags() {
try {
await remarkSessionIntelTag({
corp_id: this.corp_id,
external_userid: this.external_userid,
userid: this.userid
})
console.log('智能标签同步成功')
} catch (error) {
console.error('智能标签同步失败:', error)
}
},
// 检查用户是否同意聊天内容存档
async checkAgreeStatus() {
try {
const res = await checkSingleAgree({
external_userid: this.external_userid,
userid: this.userid
})
if (res.status_code === 1) {
this.agreeStatus = res.data.agree_status
}
} catch (error) {
console.error('检查用户同意状态失败:', error)
}
},
// 检查客服号是否开启会话内容存档
async checkPermitStatus() {
try {
const res = await checkUserPermit({
userid: this.userid
})
if (res.status_code === 1) {
this.hasPermit = res.data.has_permit
}
} catch (error) {
console.error('检查客服权限失败:', error)
}
},
// 开始休息
async handleStartRest() {
try {
const res = await client_session_rest()
if (res.status_code === 1) {
this.$store.commit('user/set_client_online_status', 'rest')
this.$message.success('已开始休息')
} else {
this.$message.error(res.msg || '开始休息失败')
}
} catch (error) {
console.error('开始休息失败:', error)
this.$message.error('开始休息失败')
}
},
// 结束休息
async handleFinishRest() {
try {
const res = await finishRest()
if (res.status_code === 1) {
this.$store.commit('user/set_client_online_status', 'online')
this.$message.success('已结束休息')
} else {
this.$message.error(res.msg || '结束休息失败')
}
} catch (error) {
console.error('结束休息失败:', error)
this.$message.error('结束休息失败')
}
},
// 发送评价
async handleSendComment() {
try {
const res = await sendComment({
corp_id: this.corp_id,
external_userid: this.external_userid,
userid: this.userid
})
if (res.status_code === 1 && res.data.news) {
// 使用企业微信JSSDK发送评价
const result = await sendChatMessage(res.data.news, 'link')
if (result.success) {
this.$message.success('评价已发送')
} else {
this.$message.error('评价发送失败')
}
} else {
this.$message.error(res.msg || '获取评价内容失败')
}
} catch (error) {
console.error('发送评价失败:', error)
this.$message.error('发送评价失败')
}
},
memberChange() {
this.requestBindUser()
},
......@@ -417,16 +634,6 @@ import watchMember from '@/mixins/watchMember'
}
this.editUserInfo(params)
},
editUserInfo(data) {
editUser(data).then((res) => {
if (res.status_code == 1) {
this.$message({
type: 'success',
message: res.msg
})
}
})
},
}
}
</script>
......@@ -456,6 +663,30 @@ import watchMember from '@/mixins/watchMember'
font-size: 14px;
margin-bottom: 10px;
}
.cser_status {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 10px;
font-size: 14px;
.status-actions {
display: flex;
gap: 10px;
}
}
.archive-status {
margin-bottom: 15px;
padding: 8px;
background-color: #f8f8f8;
border-radius: 4px;
font-size: 14px;
p {
margin: 5px 0;
color: #F56C6C;
font-weight: 600;
}
}
.detailsTitle {
height: 50px;
padding: 0 20px;
......@@ -576,18 +807,9 @@ import watchMember from '@/mixins/watchMember'
.tag-more {
display: inline-block;
background-color: #E8F7FF;
color: #3491FA;
padding: 2px 8px;
border-radius: 2px;
margin-left: 5px;
font-size: 12px;
cursor: pointer;
transition: all 0.3s;
&:hover {
background-color: #d0edff;
}
}
.tags-popover-content {
......@@ -617,7 +839,6 @@ import watchMember from '@/mixins/watchMember'
margin-bottom: 8px;
padding-bottom: 8px;
border-bottom: 1px dashed #eee;
&:last-child {
margin-bottom: 0;
padding-bottom: 0;
......
......@@ -172,9 +172,9 @@
</div>
</div>
<div v-if="gameUserInfo.service_wechat_number_info && gameUserInfo.service_wechat_number_info.length>0" class="item rowFlex columnCenter spaceBetween">
<div class="columnFlex " style="width:100%;">
<div class="rowFlex columnCenter" style="width:100%;">
<span class="label">客服微信号:</span>
<div class="rowFlex columnCenter" style="margin-top:10px;width:100%;">
<div class="rowFlex columnCenter">
<!-- 显示第一个微信号 -->
<div class="rowFlex columnCenter">
<p class="hidden wxName">{{ gameUserInfo.service_wechat_number_info[0].service_wechat_number_name }}({{ gameUserInfo.service_wechat_number_info[0].service_type_name }})</p>
......@@ -430,7 +430,7 @@ import { debounce } from '@/utils'
margin-left: 10px;
}
.showInputRemarkInput {
width: vw(220);
width: 200px;
margin-left: 10px;
}
.label {
......@@ -462,10 +462,10 @@ import { debounce } from '@/utils'
cursor: pointer;
}
.tags {
width: vw(300);
width: 250px;
margin-left: 10px;
.tagsItem {
width: vw(300);
width: 250px;
}
.tag {
height: 22px;
......
......@@ -169,10 +169,10 @@
width: 40px;
height: 40px;
border-radius: 6px;
margin-right: vw(10);
margin-right: 10px;
}
.showInputRemarkInput {
width: vw(220);
width: 200px;
margin-left: 10px;
}
.label {
......@@ -204,10 +204,10 @@
cursor: pointer;
}
.tags {
width: vw(300);
width: 250px;
margin-left: 10px;
.tagsItem {
width: vw(300);
width: 250px;
}
.tag {
height: 22px;
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论