提交 a21e58e2 作者: 施汉文

feat: 客服样式

上级 869d9cf1
<!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.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<link rel="icon" href="<%= BASE_URL %>favicon.ico" />
<!-- HTTP 1.1 -->
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="pragma" content="no-cache" />
<!-- HTTP 1.0 -->
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="cache-control" content="no-cache" />
<!-- Prevent caching at the proxy server -->
<meta http-equiv="expires" content="0">
<meta http-equiv="expires" content="0" />
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE9" />
<!-- <title><%= htmlWebpackPlugin.options.title %></title> -->
<title>企微侧边栏</title>
<!-- <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0,shrink-to-fit=no,user-scalable=no"> -->
<script src="https://g.alicdn.com/dingding/dinglogin/0.0.5/ddLogin.js"></script>
<!-- iconpark CDN链接 -->
<script src="https://lf1-cdn-tos.bytegoofy.com/obj/iconpark/icons_27278_168.abab0e7be76224e5929cf4315f14d58c.es5.js"></script>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
<strong
>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work
properly without JavaScript enabled. Please enable it to
continue.</strong
>
</noscript>
<div id="app"></div>
</body>
......
import Vue from 'vue'
import VueRouter from 'vue-router'
import userInfo from '../views/userInfo/userInfo.vue'
import agentStatusManagement from '../views/agentStatusManagement/index.vue'
import quickReply from '../views/quickReply.vue'
import giftRecord from '../views/giftRecord.vue'
import applyRecord from '../views/applyRecord.vue'
......@@ -26,6 +27,11 @@ const routes = [
path: '/index.html',
redirect: '/userInfo'
},
{
path: '/agentStatusManagement',
name: 'agentStatusManagement',
component: agentStatusManagement
},
{
path: '/userInfo',
name: 'userInfo',
......
......@@ -11,8 +11,10 @@ const state = {
"agent_signature": "05a47ef848266c48ff28f52e7749ba8b70adcc14",
"corp_signature": "29e61720786b3c6dac31f1041c70878b4819842e",
"time": 1747726636,
external_userid:''
external_userid:'',
},
avatar:'',//客服头像
userid:Cookies.get('userid'),
corp_id:'',
external_userid:'',
......@@ -40,6 +42,9 @@ const mutations = {
set_userInfo(state,userInfo){
state.userInfo = userInfo
},
set_avatar(state,avatar){
state.avatar = avatar
},
set_userid(state,userid){
state.userid = userid
},
......
......@@ -14,6 +14,33 @@ body {
font-size: 14px;
margin: 0 ;
padding: 0;
// 设置全局滚动条不占位置
overflow: overlay;
}
// 全局滚动条样式
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background-color: rgba(144, 147, 153, 0.3);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background-color: rgba(144, 147, 153, 0.5);
}
// Firefox 滚动条样式
* {
scrollbar-width: thin;
scrollbar-color: rgba(144, 147, 153, 0.3) transparent;
}
label {
......@@ -856,4 +883,4 @@ li {
.el-message-box__message {
max-height: 500px !important;
overflow-y: auto !important;
}
\ No newline at end of file
}
<template>
<div class="p-3 h-full">
<div class="text-[13px] text-[#131920] font-medium">在线客服</div>
<div class="px-[9px] pt-[60px] flex flex-col items-center justify-center">
<div class="h-[80px] w-[80px]">
<img :src="avatar" alt="" class="h-full w-full rounded-[6px]" />
</div>
<div class="text-[14px] mt-[8px] text-black">
{{ userInfo.username }}
</div>
<div
v-if="clientStatus === 'rest'"
class="mt-[8px] text-[12px] flex items-center justify-center text-[#FA8F25] bg-[rgba(250,143,37,0.1)] px-[8px] rounded-full"
>
<div class="h-[6px] w-[6px] rounded-full bg-[#FA8F25] mr-[4px]"></div>
{{ clientStatusText }}
</div>
<div
v-else
class="mt-[8px] text-[12px] flex items-center justify-center text-[#26BF4C] bg-[rgba(38,191,76,0.1)] px-[8px] rounded-full"
>
<div class="h-[6px] w-[6px] rounded-full bg-[#26BF4C] mr-[4px]"></div>
{{ clientStatusText }}
</div>
<div class="mt-[32px] w-full space-y-[12px] space-x-0">
<el-button
plain
class="!w-full !h-[40px]"
@click="logout"
v-if="clientStatus !== 'offline'"
:loading="logoutLoading"
>
<div class="flex items-center justify-center">
<iconpark-icon
name="kefucaozuo-xiaxian"
class="mr-[8px] text-[14px]"
></iconpark-icon>
下线
</div>
</el-button>
<el-tooltip
effect="dark"
:content="'午休或者临时有事可点击休息'"
placement="top"
v-if="clientStatus === 'online'"
>
<el-button
plain
class="!w-full !h-[40px]"
@click="handleStartRest"
:loading="startRestLoading"
>
<div class="flex items-center justify-center">
<iconpark-icon
name="kefucaozuo-xiuxi"
class="mr-[8px] text-[14px]"
></iconpark-icon>
开始休息
</div>
</el-button>
</el-tooltip>
<el-button
type="primary"
class="!w-full !h-[40px]"
@click="handleFinishRest"
v-if="clientStatus === 'rest'"
:loading="finishRestLoading"
>
<div class="flex items-center justify-center">
<iconpark-icon
name="kefucaozuo-xiuxi"
class="mr-[8px] font-bold text-[14px]"
></iconpark-icon>
结束休息
</div>
</el-button>
<el-button
plain
class="!w-full !h-[40px]"
@click="handleSendComment"
:loading="sendCommentLoading"
>
<div class="flex items-center justify-center">
<iconpark-icon
name="kefucaozuo-pingjia"
class="mr-[8px] text-[14px]"
></iconpark-icon>
发送评价
</div>
</el-button>
</div>
</div>
</div>
</template>
<script>
import { mapState, mapMutations, mapActions } from "vuex";
import {
getClientStatus,
finishRest,
client_session_rest,
sendComment,
logout,
} from "@/api/user.js";
import { sendChatMessage } from "@/utils/index.js";
import { getToken, removeToken } from "@/utils/auth";
import Cookies from "js-cookie";
export default {
name: "AgentStatusManagement",
data() {
return {
logoutLoading: false,
startRestLoading: false,
finishRestLoading: false,
sendCommentLoading: false,
};
},
computed: {
...mapState("user", [
"userInfo",
"avatar",
"client_online_status",
"corp_id",
"external_userid",
"userid",
"token",
]),
// 客服状态文本
clientStatusText() {
const statusMap = {
online: "在线",
offline: "离线",
rest: "休息中",
};
return statusMap[this.client_online_status] || "未知";
},
// 客服状态
clientStatus() {
console.log(this.client_online_status, 123131231);
return this.client_online_status;
},
},
mounted() {
// 获取客服状态和相关信息
this.$nextTick(() => {
if (this.userid && this.token) {
this.getInitialData();
}
});
},
methods: {
...mapMutations("user", ["set_client_online_status"]),
...mapActions("user", ["initWecom"]),
// 获取初始数据
async getInitialData() {
console.log(this.userInfo, 1111111);
// 如果userInfo.id存在,说明已经获取过客服状态,直接返回
if (this.userInfo.id) return;
try {
// 获取客服休息状态
const statusRes = await getClientStatus();
if (statusRes.status_code === 1) {
if (statusRes.data.client_online_status === "offline") {
removeToken();
window.location.href =
window.location.origin +
"/company_app/index.html?corp_id=" +
this.corp_id;
}
console.log(statusRes.data, 123131231);
this.set_client_online_status(statusRes.data.client_online_status);
}
} catch (error) {
console.error("获取初始数据失败:", 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() {
this.logoutLoading = true;
try {
const data = {
userid: this.userid,
};
const res = await logout(data);
if (res.status_code === 1) {
this.$message({
type: "success",
message: "下线成功",
});
removeToken();
Cookies.remove("external_userid");
Cookies.remove("userid");
window.location.href =
window.location.origin +
"/company_app/index.html?corp_id=" +
this.corp_id;
} else {
this.$message({
type: "error",
message: "下线失败",
});
}
} finally {
this.logoutLoading = false;
}
},
// 开始休息
async handleStartRest() {
this.startRestLoading = true;
try {
const res = await client_session_rest();
if (res.status_code === 1) {
this.set_client_online_status("rest");
this.$message.success("已开始休息");
} else {
this.$message.error(res.msg || "开始休息失败");
}
} catch (error) {
console.error("开始休息失败:", error);
this.$message.error("开始休息失败");
} finally {
this.startRestLoading = false;
}
},
// 结束休息
async handleFinishRest() {
this.finishRestLoading = true;
try {
const res = await finishRest();
if (res.status_code === 1) {
this.set_client_online_status("online");
this.$message.success("已结束休息");
} else {
this.$message.error(res.msg || "结束休息失败");
}
} catch (error) {
console.error("结束休息失败:", error);
this.$message.error("结束休息失败");
} finally {
this.finishRestLoading = false;
}
},
// 发送评价
async handleSendComment() {
this.sendCommentLoading = true;
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("评价发送失败");
}
}
} catch (error) {
console.error("发送评价失败:", error);
this.$message.error("发送评价失败");
} finally {
this.sendCommentLoading = false;
}
},
},
};
</script>
<template>
<div class="bindUserList rowFlex columnCenter">
<div class="select">
<el-select v-model="bindAccount" placeholder="请选择关联账号" :clearable="false" @change="handleChange">
<el-select
v-model="bindAccount"
placeholder="请选择关联账号"
:clearable="false"
@change="handleChange"
>
<el-option label="新增关联账号" value="add" @click="addNewUser">
</el-option>
<el-option v-for="(item, index) in bindGameUserList" :key="index" :label="item.username"
:value="item.member_id">
<el-option
v-for="(item, index) in bindGameUserList"
:key="index"
:label="item.username"
:value="item.member_id"
>
<div class="rowFlex columnCenter">
<p class="text">{{ item.status_name
? `${item.username}/${item.status_name}`
: item.username}}</p>
<span v-if="item.account_type==2" class="account_type" style="color: red;font-weight: bold;"> ()</span>
<p class="text">
{{
item.status_name
? `${item.username}/${item.status_name}`
: item.username
}}
</p>
<span
v-if="item.account_type == 2"
class="account_type"
style="color: red; font-weight: bold"
>
()</span
>
</div>
</el-option>
</el-select>
</div>
<p v-if="bindGameUserList.length > 0" class="num">
总共{{ bindGameUserList.length }}个账号
</p>
<addUser
:show.sync="showLayer"
title="选择玩家"
width="60%"
@close="close"
/>
:show.sync="showLayer"
title="选择玩家"
width="60%"
@close="close"
/>
</div>
</template>
<script type="text/javascript">
import { detailsInfoRequest } from '@/api/works'
import {memberView} from '@/api/game'
import { logout } from '@/api/user'
import { mapState, mapMutations, mapActions } from 'vuex'
import addUser from './addUser.vue'
import { getToken,removeToken } from '@/utils/auth'
import { detailsInfoRequest } from "@/api/works";
import { memberView } from "@/api/game";
import { logout } from "@/api/user";
import { mapState, mapMutations, mapActions } from "vuex";
import addUser from "./addUser.vue";
import { getToken, removeToken } from "@/utils/auth";
// 更新代码
export default {
name: 'bindUserList',
name: "bindUserList",
components: {
addUser
addUser,
},
data() {
return {
showLayer: false,
accountValue: '',
bindAccount:'',
memberCheckList:[], // 自定义列
}
accountValue: "",
bindAccount: "",
memberCheckList: [], // 自定义列
};
},
computed: {
...mapState('game', [
'bindGameUserList',
'accountSelect',
'gameUserInfo'
]),
...mapState('user', [
'userid',
'corp_id',
'external_userid',
'client_online_status'
...mapState("game", ["bindGameUserList", "accountSelect", "gameUserInfo"]),
...mapState("user", [
"userid",
"corp_id",
"external_userid",
"client_online_status",
]),
},
watch: {
accountSelect(newVal,oldVal) {
if(newVal){
console.log(newVal,'hahhaha')
this.gameMemberView()
this.bindAccount = newVal
accountSelect(newVal, oldVal) {
if (newVal) {
console.log(newVal, "hahhaha");
this.gameMemberView();
this.bindAccount = newVal;
}
}
},
},
async mounted() {
this.bindUserList()
this.requestDetails()
this.gameMemberView()
this.bindUserList();
this.requestDetails();
this.gameMemberView();
},
methods: {
...mapMutations('game', [
'set_accountSelect',
'set_chatUserInfo',
'set_gameUserInfo',
'set_viewLoading'
...mapMutations("game", [
"set_accountSelect",
"set_chatUserInfo",
"set_gameUserInfo",
"set_viewLoading",
"set_avatar",
]),
...mapActions('game', ['gameBindUser']),
...mapMutations("user", ["set_avatar"]),
...mapActions("game", ["gameBindUser"]),
handleChange(value) {
if (value == 'add') {
this.showLayer = true
if (value == "add") {
this.showLayer = true;
} else {
this.set_accountSelect(value)
this.set_accountSelect(value);
}
},
close(){
this.bindAccount = this.accountSelect
close() {
this.bindAccount = this.accountSelect;
},
async gameMemberView(item) {
if (this.accountSelect && this.accountSelect !== '') {
this.set_viewLoading(true)
this.set_gameUserInfo({})
await this.$nextTick()
const data = { member_id: this.accountSelect, need_channel: 1, need_roleInfo: 1, need_banned: 1 }
try {
const res = await memberView(data)
this.set_viewLoading(false)
if (res.status_code === 1) {
this.set_gameUserInfo(res.data)
} else {
this.set_gameUserInfo({})
}
} catch (error) {
this.set_viewLoading(false)
this.set_gameUserInfo({})
if (this.accountSelect && this.accountSelect !== "") {
this.set_viewLoading(true);
this.set_gameUserInfo({});
await this.$nextTick();
const data = {
member_id: this.accountSelect,
need_channel: 1,
need_roleInfo: 1,
need_banned: 1,
};
try {
const res = await memberView(data);
this.set_viewLoading(false);
if (res.status_code === 1) {
this.set_gameUserInfo(res.data);
} else {
this.set_gameUserInfo({});
}
}
} catch (error) {
this.set_viewLoading(false);
this.set_gameUserInfo({});
}
}
},
addNewUser() {
console.log(11)
console.log(11);
},
async requestDetails() {
const data = {
userid: this.userid,
external_userid: this.external_userid
}
const res = await detailsInfoRequest(data)
external_userid: this.external_userid,
};
const res = await detailsInfoRequest(data);
if (res.data && res.data.userid) {
console.log(res.data,'1231')
this.chatUserDetails = res.data
this.set_chatUserInfo(this.chatUserDetails) // 设置云端信息
console.log(this.chatUserDetails,'哈哈哈')
if (this.chatUserDetails.self_defined_columns && this.chatUserDetails.self_defined_columns.length > 0) {
this.memberCheckList =
this.chatUserDetails.self_defined_columns.map(
(item) => item.name
)
this.set_avatar(res.data.user.avatar);
console.log(res.data, "1231");
this.chatUserDetails = res.data;
this.set_chatUserInfo(this.chatUserDetails); // 设置云端信息
console.log(this.chatUserDetails, "哈哈哈");
if (
this.chatUserDetails.self_defined_columns &&
this.chatUserDetails.self_defined_columns.length > 0
) {
this.memberCheckList = this.chatUserDetails.self_defined_columns.map(
(item) => item.name
);
} else {
this.memberCheckList = []
this.memberCheckList = [];
}
} else {
this.chatUserDetails = {}
this.chatUserDetails = {};
}
// 获取到用户的详情 储存在缓存里面
},
......@@ -146,20 +170,19 @@ export default {
async bindUserList() {
const data = {
userid: this.userid,
external_userid: this.external_userid
}
const res = await this.gameBindUser(data)
external_userid: this.external_userid,
};
const res = await this.gameBindUser(data);
if (res.length > 0) {
this.set_accountSelect(res[0].member_id)
this.bindAccount = res[0].member_id
this.set_accountSelect(res[0].member_id);
this.bindAccount = res[0].member_id;
} else {
this.set_accountSelect('')
this.bindAccount = ''
this.set_accountSelect("");
this.bindAccount = "";
}
}
}
}
},
},
};
</script>
<style lang="scss" scoped>
......@@ -173,21 +196,22 @@ export default {
min-width: 200px;
height: 32px;
line-height: 32px;
background-color: #E8F7FF;
color: #3491FA;
background-color: #e8f7ff;
color: #3491fa;
font-size: 14px;
&:hover, &:focus {
border-color: #3491FA;
&:hover,
&:focus {
border-color: #3491fa;
}
}
::v-deep .el-input__suffix {
color: #3491FA;
color: #3491fa;
}
::v-deep .el-select-dropdown__item.selected {
color: #3491FA;
color: #3491fa;
}
}
......@@ -197,17 +221,18 @@ export default {
font-weight: 600;
margin-left: 10px;
white-space: nowrap;
color: #F53F3F;
color: #f53f3f;
}
::v-deep .el-button--danger {
background-color: #F56C6C;
border-color: #F56C6C;
&:hover, &:focus {
background-color: #f56c6c;
border-color: #f56c6c;
&:hover,
&:focus {
background-color: #f78989;
border-color: #f78989;
}
}
}
</style>
\ No newline at end of file
</style>
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论