提交 a21e58e2 作者: 施汉文

feat: 客服样式

上级 869d9cf1
<!DOCTYPE html> <!DOCTYPE html>
<html lang=""> <html lang="">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0"> <meta name="viewport" content="width=device-width,initial-scale=1.0" />
<link rel="icon" href="<%= BASE_URL %>favicon.ico"> <link rel="icon" href="<%= BASE_URL %>favicon.ico" />
<!-- HTTP 1.1 --> <!-- HTTP 1.1 -->
<meta http-equiv="pragma" content="no-cache"> <meta http-equiv="pragma" content="no-cache" />
<!-- HTTP 1.0 --> <!-- 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 --> <!-- 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" /> <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE9" />
<!-- <title><%= htmlWebpackPlugin.options.title %></title> --> <!-- <title><%= htmlWebpackPlugin.options.title %></title> -->
<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"> --> <!-- <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> <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> </head>
<body> <body>
<noscript> <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> </noscript>
<div id="app"></div> <div id="app"></div>
</body> </body>
......
import Vue from 'vue' import Vue from 'vue'
import VueRouter from 'vue-router' import VueRouter from 'vue-router'
import userInfo from '../views/userInfo/userInfo.vue' import userInfo from '../views/userInfo/userInfo.vue'
import agentStatusManagement from '../views/agentStatusManagement/index.vue'
import quickReply from '../views/quickReply.vue' import quickReply from '../views/quickReply.vue'
import giftRecord from '../views/giftRecord.vue' import giftRecord from '../views/giftRecord.vue'
import applyRecord from '../views/applyRecord.vue' import applyRecord from '../views/applyRecord.vue'
...@@ -26,6 +27,11 @@ const routes = [ ...@@ -26,6 +27,11 @@ const routes = [
path: '/index.html', path: '/index.html',
redirect: '/userInfo' redirect: '/userInfo'
}, },
{
path: '/agentStatusManagement',
name: 'agentStatusManagement',
component: agentStatusManagement
},
{ {
path: '/userInfo', path: '/userInfo',
name: 'userInfo', name: 'userInfo',
......
...@@ -11,8 +11,10 @@ const state = { ...@@ -11,8 +11,10 @@ const state = {
"agent_signature": "05a47ef848266c48ff28f52e7749ba8b70adcc14", "agent_signature": "05a47ef848266c48ff28f52e7749ba8b70adcc14",
"corp_signature": "29e61720786b3c6dac31f1041c70878b4819842e", "corp_signature": "29e61720786b3c6dac31f1041c70878b4819842e",
"time": 1747726636, "time": 1747726636,
external_userid:'' external_userid:'',
}, },
avatar:'',//客服头像
userid:Cookies.get('userid'), userid:Cookies.get('userid'),
corp_id:'', corp_id:'',
external_userid:'', external_userid:'',
...@@ -40,6 +42,9 @@ const mutations = { ...@@ -40,6 +42,9 @@ const mutations = {
set_userInfo(state,userInfo){ set_userInfo(state,userInfo){
state.userInfo = userInfo state.userInfo = userInfo
}, },
set_avatar(state,avatar){
state.avatar = avatar
},
set_userid(state,userid){ set_userid(state,userid){
state.userid = userid state.userid = userid
}, },
......
...@@ -14,6 +14,33 @@ body { ...@@ -14,6 +14,33 @@ body {
font-size: 14px; font-size: 14px;
margin: 0 ; margin: 0 ;
padding: 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 { label {
...@@ -856,4 +883,4 @@ li { ...@@ -856,4 +883,4 @@ li {
.el-message-box__message { .el-message-box__message {
max-height: 500px !important; max-height: 500px !important;
overflow-y: auto !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> <template>
<div class="bindUserList rowFlex columnCenter"> <div class="bindUserList rowFlex columnCenter">
<div class="select"> <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 label="新增关联账号" value="add" @click="addNewUser">
</el-option> </el-option>
<el-option v-for="(item, index) in bindGameUserList" :key="index" :label="item.username" <el-option
:value="item.member_id"> v-for="(item, index) in bindGameUserList"
:key="index"
:label="item.username"
:value="item.member_id"
>
<div class="rowFlex columnCenter"> <div class="rowFlex columnCenter">
<p class="text">{{ item.status_name <p class="text">
? `${item.username}/${item.status_name}` {{
: item.username}}</p> item.status_name
<span v-if="item.account_type==2" class="account_type" style="color: red;font-weight: bold;"> ()</span> ? `${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> </div>
</el-option> </el-option>
</el-select> </el-select>
</div> </div>
<p v-if="bindGameUserList.length > 0" class="num"> <p v-if="bindGameUserList.length > 0" class="num">
总共{{ bindGameUserList.length }}个账号 总共{{ bindGameUserList.length }}个账号
</p> </p>
<addUser <addUser
:show.sync="showLayer" :show.sync="showLayer"
title="选择玩家" title="选择玩家"
width="60%" width="60%"
@close="close" @close="close"
/> />
</div> </div>
</template> </template>
<script type="text/javascript"> <script type="text/javascript">
import { detailsInfoRequest } from '@/api/works' import { detailsInfoRequest } from "@/api/works";
import {memberView} from '@/api/game' import { memberView } from "@/api/game";
import { logout } from '@/api/user' import { logout } from "@/api/user";
import { mapState, mapMutations, mapActions } from 'vuex' import { mapState, mapMutations, mapActions } from "vuex";
import addUser from './addUser.vue' import addUser from "./addUser.vue";
import { getToken,removeToken } from '@/utils/auth' import { getToken, removeToken } from "@/utils/auth";
// 更新代码 // 更新代码
export default { export default {
name: 'bindUserList', name: "bindUserList",
components: { components: {
addUser addUser,
}, },
data() { data() {
return { return {
showLayer: false, showLayer: false,
accountValue: '', accountValue: "",
bindAccount:'', bindAccount: "",
memberCheckList:[], // 自定义列 memberCheckList: [], // 自定义列
} };
}, },
computed: { computed: {
...mapState('game', [ ...mapState("game", ["bindGameUserList", "accountSelect", "gameUserInfo"]),
'bindGameUserList', ...mapState("user", [
'accountSelect', "userid",
'gameUserInfo' "corp_id",
]), "external_userid",
...mapState('user', [ "client_online_status",
'userid',
'corp_id',
'external_userid',
'client_online_status'
]), ]),
}, },
watch: { watch: {
accountSelect(newVal,oldVal) { accountSelect(newVal, oldVal) {
if(newVal){ if (newVal) {
console.log(newVal,'hahhaha') console.log(newVal, "hahhaha");
this.gameMemberView() this.gameMemberView();
this.bindAccount = newVal this.bindAccount = newVal;
} }
} },
}, },
async mounted() { async mounted() {
this.bindUserList() this.bindUserList();
this.requestDetails() this.requestDetails();
this.gameMemberView() this.gameMemberView();
}, },
methods: { methods: {
...mapMutations('game', [ ...mapMutations("game", [
'set_accountSelect', "set_accountSelect",
'set_chatUserInfo', "set_chatUserInfo",
'set_gameUserInfo', "set_gameUserInfo",
'set_viewLoading' "set_viewLoading",
"set_avatar",
]), ]),
...mapActions('game', ['gameBindUser']), ...mapMutations("user", ["set_avatar"]),
...mapActions("game", ["gameBindUser"]),
handleChange(value) { handleChange(value) {
if (value == 'add') { if (value == "add") {
this.showLayer = true this.showLayer = true;
} else { } else {
this.set_accountSelect(value) this.set_accountSelect(value);
} }
}, },
close(){ close() {
this.bindAccount = this.accountSelect this.bindAccount = this.accountSelect;
}, },
async gameMemberView(item) { async gameMemberView(item) {
if (this.accountSelect && this.accountSelect !== '') { if (this.accountSelect && this.accountSelect !== "") {
this.set_viewLoading(true) this.set_viewLoading(true);
this.set_gameUserInfo({}) this.set_gameUserInfo({});
await this.$nextTick() await this.$nextTick();
const data = { member_id: this.accountSelect, need_channel: 1, need_roleInfo: 1, need_banned: 1 } const data = {
try { member_id: this.accountSelect,
const res = await memberView(data) need_channel: 1,
this.set_viewLoading(false) need_roleInfo: 1,
if (res.status_code === 1) { need_banned: 1,
this.set_gameUserInfo(res.data) };
} else { try {
this.set_gameUserInfo({}) const res = await memberView(data);
} this.set_viewLoading(false);
} catch (error) { if (res.status_code === 1) {
this.set_viewLoading(false) this.set_gameUserInfo(res.data);
this.set_gameUserInfo({}) } else {
this.set_gameUserInfo({});
} }
} } catch (error) {
this.set_viewLoading(false);
this.set_gameUserInfo({});
}
}
}, },
addNewUser() { addNewUser() {
console.log(11) console.log(11);
}, },
async requestDetails() { async requestDetails() {
const data = { const data = {
userid: this.userid, userid: this.userid,
external_userid: this.external_userid external_userid: this.external_userid,
} };
const res = await detailsInfoRequest(data) const res = await detailsInfoRequest(data);
if (res.data && res.data.userid) { if (res.data && res.data.userid) {
console.log(res.data,'1231') this.set_avatar(res.data.user.avatar);
this.chatUserDetails = res.data console.log(res.data, "1231");
this.set_chatUserInfo(this.chatUserDetails) // 设置云端信息 this.chatUserDetails = res.data;
console.log(this.chatUserDetails,'哈哈哈') this.set_chatUserInfo(this.chatUserDetails); // 设置云端信息
if (this.chatUserDetails.self_defined_columns && this.chatUserDetails.self_defined_columns.length > 0) { console.log(this.chatUserDetails, "哈哈哈");
this.memberCheckList = if (
this.chatUserDetails.self_defined_columns.map( this.chatUserDetails.self_defined_columns &&
(item) => item.name this.chatUserDetails.self_defined_columns.length > 0
) ) {
this.memberCheckList = this.chatUserDetails.self_defined_columns.map(
(item) => item.name
);
} else { } else {
this.memberCheckList = [] this.memberCheckList = [];
} }
} else { } else {
this.chatUserDetails = {} this.chatUserDetails = {};
} }
// 获取到用户的详情 储存在缓存里面 // 获取到用户的详情 储存在缓存里面
}, },
...@@ -146,20 +170,19 @@ export default { ...@@ -146,20 +170,19 @@ export default {
async bindUserList() { async bindUserList() {
const data = { const data = {
userid: this.userid, userid: this.userid,
external_userid: this.external_userid external_userid: this.external_userid,
} };
const res = await this.gameBindUser(data) const res = await this.gameBindUser(data);
if (res.length > 0) { if (res.length > 0) {
this.set_accountSelect(res[0].member_id) this.set_accountSelect(res[0].member_id);
this.bindAccount = res[0].member_id this.bindAccount = res[0].member_id;
} else { } else {
this.set_accountSelect('') this.set_accountSelect("");
this.bindAccount = '' this.bindAccount = "";
} }
},
} },
} };
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
...@@ -173,21 +196,22 @@ export default { ...@@ -173,21 +196,22 @@ export default {
min-width: 200px; min-width: 200px;
height: 32px; height: 32px;
line-height: 32px; line-height: 32px;
background-color: #E8F7FF; background-color: #e8f7ff;
color: #3491FA; color: #3491fa;
font-size: 14px; font-size: 14px;
&:hover, &:focus { &:hover,
border-color: #3491FA; &:focus {
border-color: #3491fa;
} }
} }
::v-deep .el-input__suffix { ::v-deep .el-input__suffix {
color: #3491FA; color: #3491fa;
} }
::v-deep .el-select-dropdown__item.selected { ::v-deep .el-select-dropdown__item.selected {
color: #3491FA; color: #3491fa;
} }
} }
...@@ -197,17 +221,18 @@ export default { ...@@ -197,17 +221,18 @@ export default {
font-weight: 600; font-weight: 600;
margin-left: 10px; margin-left: 10px;
white-space: nowrap; white-space: nowrap;
color: #F53F3F; color: #f53f3f;
} }
::v-deep .el-button--danger { ::v-deep .el-button--danger {
background-color: #F56C6C; background-color: #f56c6c;
border-color: #F56C6C; border-color: #f56c6c;
&:hover, &:focus { &:hover,
&:focus {
background-color: #f78989; background-color: #f78989;
border-color: #f78989; border-color: #f78989;
} }
} }
} }
</style> </style>
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论