提交 fccbd2cb 作者: 毛细亚

合并分支 '6.3' 到 'release'

6.3

查看合并请求 !11
<!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.2b8991ee.js"></script><link href="static/css/chunk-vendors.8e901099.css" rel="stylesheet"><link href="static/css/app.3be39905.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> <!DOCTYPE html>
\ No newline at end of file <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="favicon.ico">
<!-- HTTP 1.1 -->
<meta http-equiv="pragma" content="no-cache">
<!-- HTTP 1.0 -->
<meta http-equiv="cache-control" content="no-cache">
<!-- Prevent caching at the proxy server -->
<meta http-equiv="expires" content="0">
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE9" />
<title>company_app</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>
<script defer src="static/js/chunk-vendors.js"></script><script defer src="static/js/app.js"></script></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>
...@@ -144,7 +144,7 @@ export default { ...@@ -144,7 +144,7 @@ export default {
// 监听用户信息变化,只在初始化时获取一次任务数据 // 监听用户信息变化,只在初始化时获取一次任务数据
userInfo: { userInfo: {
handler(newVal, oldVal) { handler(newVal, oldVal) {
if (newVal && newVal.id && (!oldVal || !oldVal.id)) { if (newVal && newVal.id && (!oldVal || !oldVal.id) && this.token) {
console.log('用户信息初始化完成,获取任务数据:', newVal) console.log('用户信息初始化完成,获取任务数据:', newVal)
// 只在用户信息第一次设置时获取任务数据 // 只在用户信息第一次设置时获取任务数据
this.getTaskUnReadData() this.getTaskUnReadData()
...@@ -178,7 +178,6 @@ export default { ...@@ -178,7 +178,6 @@ export default {
} }
}, },
mounted() { mounted() {
this.initVuexValue()
// 页面刷新时从 Cookie 恢复 token 到 store // 页面刷新时从 Cookie 恢复 token 到 store
const cookieToken = Cookies.get('token') const cookieToken = Cookies.get('token')
if (cookieToken && !this.token) { if (cookieToken && !this.token) {
...@@ -196,12 +195,11 @@ export default { ...@@ -196,12 +195,11 @@ export default {
// 监听窗口大小变化 // 监听窗口大小变化
window.addEventListener('resize', this.handleResize) window.addEventListener('resize', this.handleResize)
// 初始检查 // 初始检查
this.$nextTick(() => { this.$nextTick(() => {
this.checkMenuOverflow() this.checkMenuOverflow()
})
this.initVuexValue() this.initVuexValue()
})
}, },
beforeDestroy() { beforeDestroy() {
window.removeEventListener('resize', this.handleResize) window.removeEventListener('resize', this.handleResize)
......
...@@ -398,3 +398,19 @@ export function markUseful(data) { ...@@ -398,3 +398,19 @@ export function markUseful(data) {
data data
}) })
} }
// 跨主体分组列表
export function cross_corp_robot_knowledge_group_index(data) {
return request({
url: returnApi('/corp_cross_knowledge_group/index'),
method: 'post',
data
})
}
// 跨主体知识库分组列表
export function cross_corp_robot_knowledge_group_getList(data) {
return request({
url: returnApi('/corp_cross_knowledge_base/getList'),
method: 'post',
data
})
}
\ No newline at end of file
...@@ -125,6 +125,20 @@ export function getClientStatus(data) { ...@@ -125,6 +125,20 @@ export function getClientStatus(data) {
data data
}) })
} }
// 获取客服号绑定的客服人员
export function getUserList(data) {
return request({
url: '/api/corp/getCserByUser',
method: 'post',
data
})
}
// 选择客服人员登录
export function cserSelected(data) {
return request({
url: '/api/sidebar_login/cserSelected',
method: 'post',
data
})
}
...@@ -86,7 +86,7 @@ const routes = [ ...@@ -86,7 +86,7 @@ const routes = [
{ {
path: '/login', path: '/login',
name: 'login', name: 'login',
component: () => import('../views/login.vue') component: () => import('../views/newLogin.vue')
}, },
] ]
const router = new VueRouter({ const router = new VueRouter({
......
...@@ -44,6 +44,7 @@ ...@@ -44,6 +44,7 @@
</div> </div>
</div> </div>
<div class="activeList"> <div class="activeList">
<div v-if=" giftInfo.rule && giftInfo.rule.level_attribute && giftInfo.rule.level_attribute.length > 0">
<div v-for="(item,index) in giftInfo.rule.level_attribute" :key="index"> <div v-for="(item,index) in giftInfo.rule.level_attribute" :key="index">
<div class="activeListItem"> <div class="activeListItem">
<div class="activeListItemTitle rowFlex spaceBetween columnCenter"> <div class="activeListItemTitle rowFlex spaceBetween columnCenter">
...@@ -112,6 +113,7 @@ ...@@ -112,6 +113,7 @@
</div> </div>
<div class="allIcon rowFlex allCenter">合计:{{ allpoints }} 积分</div> <div class="allIcon rowFlex allCenter">合计:{{ allpoints }} 积分</div>
</div> </div>
</div>
<div class="activeItem rowFlex" style="margin-top:20px;"> <div class="activeItem rowFlex" style="margin-top:20px;">
<div class="activeLabel" style="text-align:right;">备注</div> <div class="activeLabel" style="text-align:right;">备注</div>
<div class="activeValue"> <div class="activeValue">
......
...@@ -3,15 +3,12 @@ ...@@ -3,15 +3,12 @@
<div ref="giftList" class="gift-list" @scroll="handleScroll"> <div ref="giftList" class="gift-list" @scroll="handleScroll">
<div v-for="item in giftList" :key="item._id" class="gift-item"> <div v-for="item in giftList" :key="item._id" class="gift-item">
<div class="gift-info"> <div class="gift-info">
<div class="rowFlex spaceBetween columnCenter"> <div v-if="item.gift_package_group_name ">分组: {{ item.gift_package_group_name }}</div>
<div class="gift-name hidden">{{ item.gift_package_name }}</div> <div>礼包名称: {{ item.gift_package_name }}</div>
<div class="gift-time">{{ item.send_time }}</div> <div>发送时间: {{ item.send_time }}</div>
</div> <div>礼包码: {{ item.code }}</div>
<div class="gift-code">
<span class="giftCodeText">{{ item.code }}</span>
</div>
<div class="rowFlex spaceBetween columnCenter gift-sender"> <div class="rowFlex spaceBetween columnCenter gift-sender">
<div>发送客服:{{ item.cser_name }}</div> <div>发送客服: {{ item.cser_name }}</div>
<i class="el-icon-document-copy" style="cursor: pointer;" @click="handleCopy(item.code)"></i> <i class="el-icon-document-copy" style="cursor: pointer;" @click="handleCopy(item.code)"></i>
</div> </div>
</div> </div>
...@@ -111,24 +108,16 @@ import Clipboard from 'clipboard' ...@@ -111,24 +108,16 @@ import Clipboard from 'clipboard'
border: 1px solid #E5E6EB; border: 1px solid #E5E6EB;
margin-bottom:12px; margin-bottom:12px;
.gift-info { .gift-info {
.gift-name { div{
font-weight: 400; line-height: 26px;
font-size: 14px;
color: #4E5969;
text-align: left;
font-style: normal;
width: calc(100% - 180px);
} }
.gift-time { .gift-name {
font-family: PingFangSC, PingFang SC;
font-weight: 400; font-weight: 400;
font-size: 14px; font-size: 14px;
color: #4E5969; color: #4E5969;
text-align: left; text-align: left;
font-style: normal; font-style: normal;
width: 150px;
} }
.gift-code { .gift-code {
display: flex; display: flex;
align-items: center; align-items: center;
...@@ -140,7 +129,6 @@ import Clipboard from 'clipboard' ...@@ -140,7 +129,6 @@ import Clipboard from 'clipboard'
color: #323335; color: #323335;
text-align: justify; text-align: justify;
font-style: normal; font-style: normal;
margin: 10px 0;
span { span {
color: #323335; color: #323335;
font-size: 14px; font-size: 14px;
...@@ -171,7 +159,7 @@ import Clipboard from 'clipboard' ...@@ -171,7 +159,7 @@ import Clipboard from 'clipboard'
text-align: justify; text-align: justify;
font-style: normal; font-style: normal;
i{ i{
color: #409EFF; color: #00BF8A;
font-size: 14px; font-size: 14px;
} }
} }
......
...@@ -90,8 +90,13 @@ ...@@ -90,8 +90,13 @@
} }
}, },
selectChange(value) { selectChange(value) {
const valueItem = this.searchUserOption.find(item => item.role_id == value || item.id == value) const valueItem = this.searchUserOption.find(item => {
console.log(valueItem, value) if(item.role_id){
return item.role_id == value
}else{
return item.id == value
}
})
this.$emit('result', value, valueItem) this.$emit('result', value, valueItem)
}, },
requestAccountList() { requestAccountList() {
......
<template> <template>
<div class="vipToolsContent"> <div class="vipToolsContent">
<div class="gameList"> <div class="gameList">
<!-- VIP 自助工具 --> <!-- VIP 自助工具 暂时不显示 -->
<div class="gameListItem rowFlex columnCenter spaceBetween" v-if="false"> <div class="gameListItemApp rowFlex columnCenter spaceBetween" v-if="false">
<p class="rowFlex columnCenter"> <p class="rowFlex columnCenter">
<img src="@/assets/icon/vipIcon.svg" alt="vipIcon" style="width: 16px;height: 16px;margin-right: 5px;"> <img src="@/assets/icon/vipIcon.svg" alt="vipIcon" style="width: 16px;height: 16px;margin-right: 5px;">
VIP自助工具</p> VIP自助工具</p>
...@@ -12,8 +12,8 @@ ...@@ -12,8 +12,8 @@
@click="sendVipGift" @click="sendVipGift"
>发送</el-button> >发送</el-button>
</div> </div>
<!-- --> <!-- 自助链接(举报、申诉、礼包申请) -->
<div class="gameListItem rowFlex columnCenter spaceBetween"> <div class="gameListItemApp rowFlex columnCenter spaceBetween">
<p class="rowFlex columnCenter"> <p class="rowFlex columnCenter">
<i class="el-icon-s-operation" style="font-size:16px;margin-right:5px;"></i> <i class="el-icon-s-operation" style="font-size:16px;margin-right:5px;"></i>
自助链接(举报、申诉、礼包申请)</p> 自助链接(举报、申诉、礼包申请)</p>
...@@ -50,8 +50,11 @@ ...@@ -50,8 +50,11 @@
<div <div
v-for="(items,indexs) in item.children" v-for="(items,indexs) in item.children"
:key="indexs" :key="indexs"
class="gameListItem rowFlex columnCenter spaceBetween gameListItemAc" class="gameListItemChange"
> >
<div class="gameListItemApp gameListItemAppAC" >
<p v-if="items.gift_package_group_name" class="gameName">{{ items.gift_package_group_name }}</p>
<div class="rowFlex columnCenter spaceBetween" style="margin-left: 20px;">
<p class="rowFlex columnCenter spaceBetween"> <p class="rowFlex columnCenter spaceBetween">
<el-popover <el-popover
placement="top" placement="top"
...@@ -86,6 +89,8 @@ ...@@ -86,6 +89,8 @@
>发送礼包码</el-button> >发送礼包码</el-button>
</div> </div>
</div> </div>
</div>
</div>
<div <div
v-else v-else
class="rowFlex allCenter" class="rowFlex allCenter"
...@@ -227,6 +232,7 @@ ...@@ -227,6 +232,7 @@
item.label = item.main_game_name + '/' + item.main_game_id item.label = item.main_game_name + '/' + item.main_game_id
item.value = item.main_game_id item.value = item.main_game_id
item.children = item.gift_package_list item.children = item.gift_package_list
item.group_name = item.gift_package_group_name
}) })
}, },
// 发送渠道 // 发送渠道
...@@ -346,9 +352,9 @@ ...@@ -346,9 +352,9 @@
cursor: pointer; cursor: pointer;
margin: 10px 0 10px 0; margin: 10px 0 10px 0;
} }
.gameListItem { .gameListItemApp {
width: 100%; width: 100%;
height: 40px; padding-left: 20px;
.gameName{ .gameName{
max-width: 120px; max-width: 120px;
overflow: hidden; overflow: hidden;
...@@ -367,7 +373,7 @@ ...@@ -367,7 +373,7 @@
cursor: pointer; cursor: pointer;
} }
} }
.gameListItemAc { .gameListItemAppAC {
background: #f7f8fa; background: #f7f8fa;
} }
} }
......
<template>
<div class="skillLibrary">
<div v-loading="loading" class="rowFlex skillBox">
<!-- 标签 -->
<el-collapse-transition>
<div v-if="groupList.length > 0" class="tagList columnFlex">
<div v-for="(item, index) in groupList" :key="index" class="tagItem columnCenter" :draggable="false" :class="groupActive == item.value ? 'tagItemActiveText' : ''" @dragstart="handleDragStart($event, item, index)" @dragover.prevent="handleDragOver($event, item)" @dragenter="handleDragEnter($event, item, 'group')" @dragend="handleDragEnd($event, item, 'group')">
<div class="rowFlex columnCenter spaceBetween tagItemGroup" @click="groupFilter(item, index)">
<div class="rowFlex columnCenter">
<i class="el-icon-rank icon" style="cursor: move"></i>
<p class="text hidden">{{ item.name }}</p>
</div>
<i class="el-icon-caret-right rotageIcon" :class="groupActive == item.value ? 'rotage' : ''"></i>
</div>
<!-- 二级分组 -->
<div v-if="item.child.length > 0 && groupActive == item.value" class="childGroup">
<div v-for="child in item.child" :key="child._id" class="childGroupText" :class="[groupActiveChild == child._id ? 'tagItemActive' : '']" @click.stop="groupFilterChild(child)">
<p class="text hidden">{{ child.name }}</p>
</div>
</div>
</div>
</div>
<div v-else class="noContent rowFlex allCenter">
<noContent/>
</div>
</el-collapse-transition>
<!-- 话术 -->
<div class="libraryListContent">
<div class="inputContent">
<el-input v-model="requestData.title" placeholder="请输入话术内容" class="input-with-select">
<el-button slot="append" icon="el-icon-search" @click="contentSearch"></el-button>
</el-input>
</div>
<div ref="skillLibrary" v-loading="skillLoading" @scroll="paperScroll" class="scrollList">
<div v-for="(items, indexs) in groupDataList" :key="indexs" class="answerContent">
<div class="question"><span class="title">Q:</span>{{ items.question }}</div>
<div v-for="(answer, answerIndex) in items.answer" :key="answerIndex" class="answerItem" style="width: 100%">
<!-- <span class="question">{{ answerIndex + 1 }}: {{ answer }}</span> -->
<div v-if="answer.msgtype == 'text'" class="answerText rowFlex spaceBetween">
<span class="title rowFlex">A{{ answerIndex + 1 }}:</span>
<p v-if="answer.msgtype == 'text'" class="rowFlex textAnswer flex1">
{{ answer.text.content || '' }}
</p>
<i class="el-icon-document-copy copyIcon rowFlex" @click="sendMessageEdit(answer, items._id)"></i>
</div>
<div v-else-if="answer.msgtype == 'image'" class="answerText rowFlex">
<span class="title rowFlex">A{{ answerIndex + 1 }}:</span>
<el-image style="max-width: 200px" :src="answer.image.picurl" @click="sendMessageImage(answer, items._id)" :preview-src-list="[answer.image.picurl]" > </el-image>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
// procedure_group, procedureList, procedureSort, procedureGroupSort, skillQuote,
import { cross_corp_robot_knowledge_group_index, cross_corp_robot_knowledge_group_getList} from '@/api/skill'
import { mapState, mapMutations, mapActions } from 'vuex'
import { throttle, debounce, copyToClipboard,sendChatMessage } from '@/utils/index'
import noContent from '@/components/noContent.vue'
export default {
name: 'skillLibrary',
components: {
noContent
},
props: {
activeName: {
default: '',
type: String
}
},
data() {
return {
collapseActive: '',
groupActive: '0',
groupActiveChild: '0',
activeGroup: {},
groupList: [],
groupLastList: [],
groupDataList: [],
groupLastDataList: [],
pageInfo: {
page: 1,
page_size: 20,
total: 0
},
skillLoading: false,
loading: false,
requestData: {
title: '',
first_group_id: '',
second_group_id: ''
},
sortType: '',
sortID: {
_id: '',
before_id: '',
after_id: ''
},
filterText: {},
dragging: null,
orderList: [
{ label: '知识库', type: 'library' },
{ label: '个人话术', type: 'personal' },
{ label: '企业话术', type: 'company' }
]
}
},
computed: {
...mapState('game', ['accountSelect','chatUserInfo']),
},
watch: {
accountSelect(newVal, oldVal) {
if (newVal && newVal !== '') {
this.pageInfo = {
page: 1,
page_size: 20,
total: 0
}
}
},
activeName(newVal, oldVal) {
if (newVal == 'library' && newVal != oldVal) {
this.resizeSelect()
this.requestLibraryData()
}
}
},
mounted() {
this.requestLibraryData()
},
methods: {
sendMessage: throttle(function(item, id) {
console.log(item, id)
// this.skillQuote(id, item.length)
}, 500),
handleDragStart(e, item, index) {
this.sortID._id = item._id
this.dragging = item
},
paperScroll:debounce(function(){
const el = this.$refs.skillLibrary
if (el.offsetHeight + el.scrollTop + 10 >= el.scrollHeight) {
console.log('下一页')
this.pageInfo.page++
this.searchTable()
}
},500),
skillQuote(id, num) {
const data = {
type: this.activeName,
procedure_id: id,
quote_count: num || 1
}
skillQuote(data).then((res) => {
console.log(res)
})
},
sortSkill() {
procedureSort(this.sortID).then((res) => {
if (res.status_code == 1) {
this.$message.success(res.msg)
}
})
},
sortSkillGroup() {
procedureGroupSort(this.sortID).then((res) => {
if (res.status_code == 1) {
this.$message.success(res.msg)
}
})
},
// 发送语音的时候 先编辑再发送
sendMessageEdit(item, id) {
// 复制内容到粘贴板
if (item && item.text && item.text.content) {
copyToClipboard(
item.text.content,
(message) => this.$message.success(message),
(message) => this.$message.error(message)
)
}
this.sendChatMessage(item.text.content || '', 'text')
},
sendMessageImage(item){
console.log(item, 'item')
this.sendChatMessage(item.image.picurl || '', 'image')
},
contentSearch() {
this.pageInfo = {
page: 1,
page_size: 20,
total: 0
}
this.searchTable('msg')
},
// 知识库话术
requestLibraryData() {
console.log(this.chatUserInfo, 'chatUserInfo')
this.loading = true
const data = {
page: 1,
page_size: 100,
userid: this.chatUserInfo.userid
}
cross_corp_robot_knowledge_group_index(data).then((res) => {
this.loading = false
if (res.data.data) {
res.data.data.unshift({
name: '全部分组',
value: '',
child: []
})
this.groupList = res.data.data.map((item, index) => {
item.label = item.name
item.value = item._id
return item
})
this.groupLastList = this.groupList
this.groupFilter(this.groupList[0])
} else {
this.groupList = []
this.groupLastList = []
}
})
},
groupFilter(item, index) {
if (!item._id) {
this.requestData.second_group_id = ''
this.activeGroup = {}
}
this.groupActive = item.value
item.child && item.child[0] ? ((this.activeGroup = item.child[0]), (this.groupActiveChild = item.child[0]._id)) : ''
this.searchTable()
},
groupFilterChild(child) {
this.groupActiveChild = 0
this.activeGroup = child
this.groupActiveChild = child._id
this.searchTable()
this.$forceUpdate()
},
// 搜索结果
async searchTable(msg) {
this.skillLoading = true
this.requestData.second_group_id = this.activeGroup._id || ''
this.requestData.title = this.requestData.title.trim()
const data = { ...this.requestData, ...this.pageInfo, log_scan: msg ? 1 : 0, userid: this.chatUserInfo.userid }
cross_corp_robot_knowledge_group_getList(data).then((res) => {
this.skillLoading = false
this.pageInfo.page === 1 ? this.groupDataList = res.data.data : this.groupDataList = this.groupDataList.concat(res.data.data)
this.groupLastDataList = this.groupDataList
})
},
handleChange() {},
// 重置select
resizeSelect() {
this.groupActive = '0'
this.isResize = true
this.groupDataList = []
this.requestData.content = ''
this.pageInfo.page = 1
setTimeout(() => {
this.isResize = false
}, 2000)
},
sendChatMessage(content, type){
sendChatMessage(content, type)
},
}
}
</script>
<style lang="scss" scoped>
.skillLibrary {
width: 100%;
height: 100%;
overflow-y: auto;
overflow-x: hidden;
.noContent {
font-size: 200px;
}
.contentItemTitle {
position: absolute;
left: 10px;
z-index: 10;
cursor: pointer;
max-width: 45%;
word-break: break-all;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2; /* 这里是超出几行省略 */
overflow: hidden;
}
.titleFixed {
position: absolute;
right: 0px;
top: 10px;
margin-left: 15px;
margin-right: 25px;
z-index: 10;
.num {
width: auto;
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #409EFF;
margin-right: 10px;
}
.button {
width: 50px;
height: 24px;
border-radius: 4px;
border: 1px solid rgba(0, 0, 0, 0.15);
font-size: 14px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #333333;
padding: 0;
}
}
.contentItem {
position: relative;
margin-bottom: 5px;
cursor: move;
.contentItemDetails {
width: 100%;
height: auto;
margin: 20px 0;
padding-left: 10px;
.text {
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #333333;
max-width: calc(100% - 60px);
word-wrap: break-word;
line-height: 18px;
}
.sendButton {
width: 50px;
height: 24px;
border-radius: 4px;
border: 1px solid rgba(0, 0, 0, 0.15);
font-size: 14px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #333333;
padding: 0;
}
.image {
max-width: 25%;
border-radius: 5px;
}
}
}
.item {
width: 100%;
height: auto;
font-size: 14px;
font-weight: 400;
color: #333333;
padding: 5px 0;
transition: all 0.5s;
position: relative;
padding-left: 10px;
cursor: move;
.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: 80%;
}
.icon {
display: none;
position: absolute;
right: 0;
top: 12px;
}
.tags {
width: 300px;
margin-left: 10px;
.tagsItem {
width: 300px;
}
.tag {
height: 22px;
line-height: 22px;
padding: 0 8px;
background: #ffffff;
border-radius: 4px;
border: 1px solid rgba(0, 0, 0, 0.2);
font-size: 12px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #333333;
margin-right: 10px;
margin-bottom: 10px;
}
}
}
.item:hover .icon {
display: block;
}
}
::v-deep .el-tabs--border-card .is-active {
border: none !important;
}
::v-deep .is-active {
border: none;
}
/* 已移除局部 el-collapse 样式,使用全局样式 */
.inputContent {
width: 100%;
margin-bottom: 20px;
margin-left: 20px;
::v-deep .el-input {
width: 90%;
}
}
.skillBox {
width: 100%;
height: calc(100% - 20px);
}
.tagList {
width: 200px;
height: 100%;
position: relative;
border-right: 1px solid #e0e0e0;
.tagItem {
width: 100px;
height: auto;
background: #fff;
border-radius: 4px;
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #333333;
margin-bottom: 6px;
cursor: pointer;
.text {
max-width: 90px;
margin-left: 5px;
}
.tagItemGroup {
height: 36px;
padding-right: 10px;
}
}
.iconFont {
font-size: 20px;
color: #999999;
position: absolute;
right: 0;
top: 20px;
cursor: pointer;
.upIcon {
transform: rotate(90deg);
}
}
.tagItemActive {
color: #409EFF !important;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
background: #e4fff1;
}
.tagItemActiveText {
color: #409EFF;
}
}
.libraryListContent{
width: 100%;
height: 100%;
}
.rotageIcon {
cursor: pointer;
transition: all 0.5s;
transform: rotate(0deg);
font-size: 12px;
}
.rotage {
transform: rotate(90deg);
}
.childGroup {
p {
margin-left: 15px !important;
}
.childGroupText {
height: 36px;
line-height: 36px;
color: #333333;
}
}
.scrollList {
width: 100%;
height: 100%;
padding-left: 20px;
.answerContent {
width: 100%;
background: #f7f8fa;
border-radius: 5px;
border: 1px solid #e5e6eb;
padding: 10px;
margin-bottom: 20px;
.title {
width: 30px;
font-size: 14px;
font-weight: 500;
margin-right: 10px;
}
.answerItem {
margin-bottom: 10px;
}
.question {
font-weight: 500;
font-size: 14px;
color: #333333;
line-height: 20px;
text-align: left;
margin-bottom: 10px;
}
.answerText {
width: 100%;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 12px;
color: #333333;
text-align: justify;
font-style: normal;
.answerTextTitle {
width: 30px;
font-size: 16px;
font-weight: 500;
}
.textAnswer {
word-wrap: break-word;
font-size: 13px;
}
.copyIcon {
width: 20px;
color: #409EFF;
font-size: 16px;
position: relative;
top: 2px;
cursor: pointer;
margin-left: 10px;
}
}
}
}
.scrollList::-webkit-scrollbar {display:none}
.container {
// margin-top: -20px;
}
::v-deep .el-tabs,
.el-tabs__content,
.el-tab-pane {
width: 100%;
height: 100%;
}
::v-deep .el-tabs__content {
width: 100%;
height: calc(100% - 50px);
}
::v-deep .el-tab-pane {
width: 100%;
height: 100%;
}
.scrollList {
width: 100%;
height: calc(100% - 40px);
overflow: auto;
}
.draggable {
position: relative;
transition: all 0.3s;
.icon {
position: absolute;
left: 10px;
top: 15px;
z-index: 10;
}
}
.textAnswer {
word-wrap: break-word;
display: inline;
}
.img_drop {
width: 200px;
height: 200px;
background: rgba(0, 0, 0, 0.36);
position: absolute;
left: 0;
top: 0;
}
.skillLibrary::-webkit-scrollbar {display:none}
::v-deep .el-icon-circle-close {
color: #fff;
}
</style>
\ No newline at end of file
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
</el-popover> </el-popover>
<div class="rowFlex titleFixed columnCenter"> <div class="rowFlex titleFixed columnCenter">
<div class="num">{{ items.message.attachments.length > 1 ? `+${items.message.attachments.length - 1}条` : '' }}</div> <div class="num">{{items.message && items.message.attachments && items.message.attachments.length > 1 ? `+${items.message.attachments.length - 1}条` : '' }}</div>
</div> </div>
<el-collapse-item title="" :name="items._id" class="contentItem"> <el-collapse-item title="" :name="items._id" class="contentItem">
...@@ -291,7 +291,7 @@ export default { ...@@ -291,7 +291,7 @@ export default {
} }
procedure_group(data).then((res) => { procedure_group(data).then((res) => {
this.loading = false this.loading = false
if (res.data.data && res.data.data.length > 0) { if (res.data && res.data.data && res.data.data.length > 0) {
this.groupList = res.data.data.map((item, index) => { this.groupList = res.data.data.map((item, index) => {
item.label = item.group_name item.label = item.group_name
item.value = item._id item.value = item._id
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
v-model="collapseValue" v-model="collapseValue"
@change="handleChange(item,$event)" @change="handleChange(item,$event)"
> >
<el-collapse-item :name="item.order_id"> <el-collapse-item :name="item.order_id || '--'">
<template slot="title"> <template slot="title">
<div class="orderDetailsTitle"> <div class="orderDetailsTitle">
<div class="money rowFlex spaceBetween"> <div class="money rowFlex spaceBetween">
......
// UnifiedLogin.vue
<template>
<div class="loginContent">
<div class="loginContentContainer">
<p class="loginContentTitle">选择客服:</p>
<div class="loginContentInput rowFlex alignCenter">
<el-select v-model="cser_user_id" filterable placeholder="请选择客服">
<el-option v-for="item in userList" :key="item.zq_user_id" clearable :label="item.name" :value="item.zq_user_id">
</el-option>
</el-select>
<el-button type="primary" style="margin-left: 10px;" size="small" @click="loginConfirm">点击登录</el-button>
</div>
</div>
</div>
</template>
<script>
import * as ww from '@wecom/jssdk'
import { getOrganization, getAuthUser, getSignature,getUserList,cserSelected } from '@/api/user'
import Cookies from 'js-cookie'
import { getParams } from '@/utils/index'
import { mapMutations, mapState } from 'vuex'
import { getToken, setToken } from '@/utils/auth'
import jsApiList from '@/utils/jsApiList'
export default {
name: 'login',
components: {
},
data() {
return {
wecomUserInfo: null, // 企微用户信息
dingUserInfo: null, // 钉钉用户信息
signData: null, // 企微签名数据
orgList: [],
organizationNum: 5,
urlParams: {},
currentOrg: {},
showOrgDialog: false,
hoveredOrg: null,
showRefresh: false, // 控制刷新按钮显示
qrLoading: false, // 控制二维码 loading
redirectUri: process.env.NODE_ENV === 'production' ? 'https://companywx.zwnet.cn/api/api/sidebar_login/ding' : 'https://companywx.zwwlkj03.top/api/api/sidebar_login/ding',
DDTestUrl: '',
token: getToken(),
userList: [],
cser_user_id: null,
}
},
async mounted() {
this.$nextTick(() => {
this.initLogin()
})
},
computed: {
...mapState('user', ['corp_id'])
},
methods: {
...mapMutations('user', ['set_corp_id', 'set_userid', 'set_userInfo', 'set_token', 'set_cser_info', 'set_signData', 'set_cser_id', 'set_cser_name', 'set_external_userid']),
async initLogin() {
const urlParams = getParams();
const userid = Cookies.get('userid');
if (this.token && userid) { // 已经钉钉扫码过 重新获取授权 获取签名 注册企微js-sdk
this.getUserList(userid)
await this.getSignature();
} else if(!userid) {
await this.startWeComSilentAuth();
}else if(!this.token){
this.getUserList(userid)
}
},
async getUserList(userid) {
this.urlParams = getParams();
const corp_id = Cookies.get('corp_id') || this.urlParams.corp_id
const res = await getUserList({ userid: userid,corp_id:corp_id });
this.userList = res.data
},
async userStartLogin(){
if(!this.cser_user_id){
this.$message.error('请选择客服人员')
return
}
const cser_user = this.userList.find(item => item.zq_user_id === this.cser_user_id)
const corp_id = Cookies.get('corp_id')
const userid = Cookies.get('userid')
this.cacheCser(cser_user.zq_user_id, cser_user.name)
try {
const res = await cserSelected({ cser_id: cser_user.zq_user_id, corp_id: corp_id,userid:userid });
console.log(res, '选择客服人员登录')
if(res.status_code === 1 && res.data.tokens ){
this.$message({
type: 'warning',
message: `当前【${cser_user.name}】已上线,下班后请记得点击下线哦~`,
duration: 3 * 1000
})
setTimeout(() => {
this.handleDingCallback(res.data.tokens )
}, 2000)
}else{
this.$message.error(res.msg)
setTimeout(() => {
window.location.href = window.location.origin + '/company_app/index.html?corp_id=' + corp_id + '&msg=cser_error'
}, 5000)
}
} catch (error) {
console.log(error, '选择客服人员登录失败')
this.$message.error(error.msg)
setTimeout(() => {
window.location.href = window.location.origin + '/company_app/index.html?corp_id=' + corp_id + '&msg=cser_error'
}, 5000)
}
},
loginConfirm(){
const cser_user = this.userList.find(item => item.zq_user_id === this.cser_user_id)
this.$confirm(`确认登录上线吗,上线后所有会话都会归属到客服【${cser_user.name}】`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.userStartLogin()
}).catch(() => {
this.$message.info('已取消登录')
})
},
// 设置缓存
cacheCorp_id(corp_id) {
Cookies.set('corp_id', corp_id, { expires: 7 })
this.set_corp_id(corp_id)
},
cacheuserid(userid) {
Cookies.set('userid', userid, { expires: 7 })
this.set_userid(userid)
},
cacheCser(cser_id, cser_name) {
Cookies.set('cser_id', cser_id, { expires: 7 })
Cookies.set('cser_name', cser_name, { expires: 7 })
this.set_cser_info({
cser_id: cser_id,
cser_name: cser_name
})
this.set_cser_id(cser_id)
this.set_cser_name(cser_name)
},
cacheSignData(signData) {
Cookies.set('signData', JSON.stringify(signData), { expires: 7 })
this.set_signData(signData)
},
// 进入的页面地址是 https://companywx.jianshuwenhua.com/company_app/index.html?corp_id=wweaefe716636df3d1
// 1. 企微静默授权
async startWeComSilentAuth() {
this.urlParams = getParams();
const corp_id = Cookies.get('corp_id') || this.urlParams.corp_id
if (!corp_id) {
this.$message.error('当前客服号信息异常,请切换会话后重试')
return
}
// 确定是第一次进入页面 没有 code 和 state
if (!this.urlParams.code && !this.urlParams.state) {
// 跳转企微静默授权
const redirectUri = encodeURIComponent(window.location.href);
const authUrl = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${corp_id}&redirect_uri=${redirectUri}&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect`;
window.location.href = authUrl;
return;
}
// 用code
const res = await getAuthUser({ code: this.urlParams.code, url: window.location.href, corp_id: corp_id });
if (res.status_code === 1) {
if(res.data.userid){
this.cacheuserid(res.data.userid)
this.getUserList(res.data.userid)
}else{
this.$message.error('获取用户id失败')
return
}
} else {
console.log('获取useid失败', res)
// 错误处理
}
},
async getSignature() {
console.log('获取签名', window.location.href)
const corp_id = Cookies.get('corp_id')
try {
const res = await getSignature({ corp_id: corp_id, path: window.location.href });
if (res.status_code === 1) {
this.signData = res.data
this.cacheSignData(res.data)
try {
this.registerWeComSDK();
} catch (err) {
console.log(err, '初始化sdk 失败')
}
}
} catch (err) {
console.log(err, '获取签名失败')
window.location.href = window.location.origin + '/company_app/index.html?corp_id=' + corp_id + '&msg=signerror'
}
},
getCurExternalContact() {
this.$ww.getCurExternalContact({
success: (res) => {
if (res.err_msg === "getCurExternalContact:ok") {
console.log(res, '重新进入获取企微外部联系人')
this.set_external_userid(res.userId)
// 确保 Vuex 状态更新后再跳转
this.$nextTick(() => {
this.$router.replace('/')
console.log(window.location.href, 'window.location.hrefuserInfo')
})
}
},
fail: (err) => {
console.log(err, '获取企微外部联系人失败')
// 错误处理
}
});
},
// 2. 注册企微JS-SDK
registerWeComSDK() {
console.log('删除企业签名', 1231)
this.$ww.register({
corpId: Cookies.get('corp_id'),
agentId: this.signData.agent_id,
jsApiList: jsApiList,
// getConfigSignature: () => Promise.resolve({
// nonceStr: this.signData.nonce_str,
// timestamp: this.signData.signature_time,
// signature: this.signData.corp_signature,
// }),
// 只用到应用的 api 可以只进行应用的签名
getAgentConfigSignature: () => Promise.resolve({
nonceStr: this.signData.nonce_str,
timestamp: this.signData.signature_time,
signature: this.signData.agent_signature,
}),
onAgentConfigSuccess: (res) => {
console.log('注册成功可以调用企微 js-sdk', res)
// 注册成功后不立即获取外部联系人,等钉钉扫码后再获取
this.getCurExternalContact()
},
onAgentConfigFail: (err) => {
console.log('注册失败不能使用企微js-sdk', err)
// 错误处理123
}
});
},
// 3. 获取组织列表并选取默认组织
async initOrganization() {
const res = await getOrganization();
if (res.data.status_code === 1) {
this.orgList = res.data.data.data.filter(item => item.id <= this.organizationNum)
// 默认组织逻辑:可根据业务自定义
this.initCurrentApp();
}
},
initCurrentApp() {
const currentApp = this.orgList.find(
(item) => item.app_key === "dingjigp0ksn9nbljdli"
);
this.$set(this, "currentOrg", currentApp);
},
// 7. 钉钉扫码回调页面处理
async handleDingCallback(token) {
// 在这里处理钉钉扫码成功的回调
const corp_id = Cookies.get('corp_id')
if (token && token != 'undefined') {
setToken(token)
this.set_token(token)
// 获取签名
await this.getSignature();
} else {
window.location.href = window.location.origin + '/company_app/index.html?corp_id=' + corp_id + '&msg=notoken'
}
},
},
}
</script>
<style lang="scss" scoped>
.current-org {
font-weight: bold;
margin-right: 5px;
}
.loginContent {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.loginContentTitle{
font-size: 14px;
line-height: 32px;
margin-right: 10px;
text-align: left;
}
.qr-contain {
margin: 0 auto;
/* margin-top: 20px; */
width: 260px;
height: 260px;
position: relative;
overflow: hidden;
#dingTalkLoginContainer {
padding: 15px;
position: absolute;
left: 10px;
bottom: 0;
}
.refresh {
display: none;
text-align: center;
position: absolute;
width: 40px;
background: #fff;
height: 40px;
transform: translate(-50%, -50%);
left: 50%;
top: 50%;
i {
line-height: 40px;
font-size: 26px;
color: #3491FA;
cursor: pointer;
}
}
&:hover {
.refresh {
display: block;
}
}
}
.loading {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: rgba(255, 255, 255, 0.7);
display: flex;
align-items: center;
justify-content: center;
z-index: 20;
transition: opacity 0.3s;
}
.loading-spinner {
width: 40px;
height: 40px;
border: 4px solid #e0e0e0;
border-top: 4px solid #3491FA;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>
\ No newline at end of file
...@@ -190,8 +190,8 @@ export default { ...@@ -190,8 +190,8 @@ export default {
return { return {
sanjiaoxing, sanjiaoxing,
loading: false, loading: false,
activeType: 4, activeType: 2,
activeTypeStr: '4', activeTypeStr: '2',
dateDetailsValue: [], dateDetailsValue: [],
isloadMore: true, isloadMore: true,
collapseValue: ['1'], collapseValue: ['1'],
...@@ -259,10 +259,10 @@ export default { ...@@ -259,10 +259,10 @@ export default {
mounted() { mounted() {
this.payTypeList() this.payTypeList()
this.searchDate = [ this.searchDate = [
this.$moment().subtract(1, 'day').format('YYYY-MM-DD'), this.$moment().format('YYYY-MM-DD'),
this.$moment().format('YYYY-MM-DD') this.$moment().format('YYYY-MM-DD')
] ]
this.timerData.order_time_start = this.$moment().subtract(1, 'day').format('YYYY-MM-DD') this.timerData.order_time_start = this.$moment().format('YYYY-MM-DD')
this.timerData.order_time_end = this.$moment().format('YYYY-MM-DD') this.timerData.order_time_end = this.$moment().format('YYYY-MM-DD')
}, },
methods: { methods: {
......
...@@ -28,20 +28,15 @@ ...@@ -28,20 +28,15 @@
:active-name="activeName" :active-name="activeName"
/> />
</el-tab-pane> </el-tab-pane>
<!-- <el-tab-pane <el-tab-pane
v-if="workerRouter==='game' && messageSource === 'company_work'" label="跨主体知识库"
label="AI微言" name="robotLibrary"
name="aiLibrary"
> >
<template slot="label"> <crossLibrary
AI微言 v-if="activeName === 'robotLibrary'"
<span class="bate">beta</span>
</template>
<aiLibrary
v-if="activeName === 'aiLibrary'"
:active-name="activeName" :active-name="activeName"
/> />
</el-tab-pane> --> </el-tab-pane>
</el-tabs> </el-tabs>
</div> </div>
</div> </div>
...@@ -51,6 +46,7 @@ ...@@ -51,6 +46,7 @@
import skillCompany from './components/skill/skillCompany.vue' import skillCompany from './components/skill/skillCompany.vue'
import skillPersonal from './components/skill/skillPersonal.vue' import skillPersonal from './components/skill/skillPersonal.vue'
import skillLibrary from './components/skill/skillLibrary.vue' import skillLibrary from './components/skill/skillLibrary.vue'
import crossLibrary from './components/skill/crossLibrary.vue'
import { mapActions } from 'vuex' import { mapActions } from 'vuex'
export default { export default {
name: 'quickReply', name: 'quickReply',
...@@ -58,7 +54,7 @@ export default { ...@@ -58,7 +54,7 @@ export default {
skillCompany, skillCompany,
skillPersonal, skillPersonal,
skillLibrary, skillLibrary,
// aiLibrary crossLibrary
}, },
data() { data() {
return { return {
......
...@@ -123,7 +123,7 @@ ...@@ -123,7 +123,7 @@
<div v-for="(item, index) in taskList" :key="index" class="chatListItem columnCenter spaceBetween" <div v-for="(item, index) in taskList" :key="index" class="chatListItem columnCenter spaceBetween"
:class="item.id === taskDetails.id ? 'chatListItemActive' : ''" @click="selectTaskItem(item)"> :class="item.id === taskDetails.id ? 'chatListItemActive' : ''" @click="selectTaskItem(item)">
<div class="top rowFlex spaceBetween"> <div class="top rowFlex spaceBetween">
<div v-if="item.username && item.username.split('\n').length > 0" class="value"> <div v-if="item.username && item.username.split('\n') && item.username.split('\n').length > 0" class="value">
<div v-if="item.username.split('\n').length <= 1" class="rowFlex columnCenter"> <div v-if="item.username.split('\n').length <= 1" class="rowFlex columnCenter">
<div v-for="(items, indexs) in item.username.split('\n')" :key="indexs" <div v-for="(items, indexs) in item.username.split('\n')" :key="indexs"
class="rowFlex columnCenter userInfoStyle"> class="rowFlex columnCenter userInfoStyle">
...@@ -139,7 +139,7 @@ ...@@ -139,7 +139,7 @@
<span class="textHidden" style="max-width:150px;">{{ items }}</span> <span class="textHidden" style="max-width:150px;">{{ items }}</span>
</p> </p>
</div> </div>
<el-button slot="reference" type="text">{{ item.username.split("\n").length <el-button slot="reference" type="text">{{ item.username && item.username.split("\n") && item.username.split("\n").length?item.username.split("\n").length : 0
}}个</el-button> }}个</el-button>
</el-popover> </el-popover>
</div> </div>
...@@ -635,14 +635,14 @@ export default { ...@@ -635,14 +635,14 @@ export default {
const res = await taskIndex(data) const res = await taskIndex(data)
this.loading = false this.loading = false
if (this.pageInfo.page == 1) { if (this.pageInfo.page == 1) {
this.taskList = res.data.data this.taskList = res.data.data || []
} else { } else {
this.taskList = removeDp(this.taskList, res.data.data, 'id') this.taskList = removeDp(this.taskList, res.data.data, 'id')
} }
// if (!this.taskDetails.id && this.taskList.length > 0) { // if (!this.taskDetails.id && this.taskList.length > 0) {
// this.set_taskDetails(this.taskList[0]) // this.set_taskDetails(this.taskList[0])
// } // }
if (res.data.data.length < 20) { if (res?.data?.data?.length < 20) {
this.isMoreRecord = false this.isMoreRecord = false
} else { } else {
this.isMoreRecord = true this.isMoreRecord = true
......
...@@ -35,7 +35,7 @@ ...@@ -35,7 +35,7 @@
v-model="collapseValue" v-model="collapseValue"
@change="handleChange(item,$event)" @change="handleChange(item,$event)"
> >
<el-collapse-item :name="item.order_id"> <el-collapse-item :name="item.order_id || '--'">
<template slot="title"> <template slot="title">
<div class="orderDetailsTitle"> <div class="orderDetailsTitle">
<div class="money rowFlex spaceBetween"> <div class="money rowFlex spaceBetween">
......
...@@ -22,7 +22,7 @@ import roleInfo from '@/views/roleInfo.vue' ...@@ -22,7 +22,7 @@ import roleInfo from '@/views/roleInfo.vue'
import orderList from '@/views/orderList.vue' import orderList from '@/views/orderList.vue'
import violationRecord from '@/views/ViolationRecord.vue' import violationRecord from '@/views/ViolationRecord.vue'
import { mapState, mapMutations } from 'vuex' import { mapState, mapMutations } from 'vuex'
import Cookies from 'js-cookie'
export default { export default {
name: 'userInfo', name: 'userInfo',
components: { components: {
...@@ -44,7 +44,23 @@ export default { ...@@ -44,7 +44,23 @@ export default {
created() { created() {
// 初始化 vuex 中的值 // 初始化 vuex 中的值
}, },
mounted() {
this.$nextTick(() => {
this.initVuexValue()
})
},
methods: { methods: {
...mapMutations('user', ['set_userInfo']),
initVuexValue(){
const userinfo = {
cser_id: Cookies.get('cser_id'),
cser_name: Cookies.get('cser_name'),
username: Cookies.get('cser_name'),
id: Cookies.get('cser_id'),
}
this.set_userInfo(userinfo)
}
} }
} }
</script> </script>
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论