提交 a462136c 作者: 施汉文

合并分支 'release' 到 'master'

Release

查看合并请求 !61
......@@ -16,7 +16,7 @@
<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>
<script src="https://lf1-cdn-tos.bytegoofy.com/obj/iconpark/icons_27278_172.5464f393968eda872f41eab2242bbbd7.es5.js"></script>
<script src="https://lf1-cdn-tos.bytegoofy.com/obj/iconpark/svg_27278_173.e64d61edfe0d4824e2eeb0b7f478e568.js"></script>
</head>
<body>
<noscript>
......
......@@ -57,7 +57,10 @@
<!-- 绑定的 w 账号 -->
<bindUserList />
</div>
<div class="mobile-content">
<div
class="mobile-content"
v-if="accountSelect || $route.path === '/login'"
>
<router-view></router-view>
</div>
</div>
......@@ -126,6 +129,11 @@ export default {
hasRedDot: false, // 红点状态
},
{
label: "用户待办",
path: "/userToDo",
hasRedDot: false, // 红点状态
},
{
label: "微言助手",
path: "/aiChat",
},
......@@ -142,7 +150,7 @@ export default {
},
computed: {
...mapState("user", ["external_userid", "token", "userInfo"]),
...mapState("game", ["taskData"]),
...mapState("game", ["taskData", "accountSelect"]),
// 计算任务列表是否需要显示红点
hasTaskRedDot() {
return this.taskData.user_task > 0 || this.taskData.account_task > 0;
......@@ -181,7 +189,7 @@ export default {
newVal,
window.location.href,
this.token,
Cookies.get("token")
Cookies.get("token"),
);
// 强制更新组件
this.$forceUpdate();
......@@ -204,7 +212,7 @@ export default {
// 页面刷新时从 Cookie 恢复 token 到 store
// Cookies.set(
// "token",
// "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOjQwOTAsImRhdGEiOnsiY3Nlcl9pZCI6NDA5MCwiY3Nlcl9uYW1lIjoi5q-b57uG5LqaIn0sImlhdCI6MTc2NTE3MjI0NywiZXhwIjoxNzY3NzY0MjQ3LCJuYmYiOjE3NjUxNzIyNDcsInN1YiI6InRva2Vu6K6k6K-BIiwianRpIjoiN2Q5NjYxNDVjNjgyZWU0Y2UyY2Y2MTc2ZjA0NTNlNGMifQ.QpEtYzoXK11RHwn8la-OMoS-BnlTyEEAa0lmlpYu2IQ"
// "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOjQwOTAsImRhdGEiOnsiY3Nlcl9pZCI6NDA5MCwiY3Nlcl9uYW1lIjoi5q-b57uG5LqaIn0sImlhdCI6MTc2ODU0Mjk0MiwiZXhwIjoxNzcxMTM0OTQyLCJuYmYiOjE3Njg1NDI5NDIsInN1YiI6InRva2Vu6K6k6K-BIiwianRpIjoiMjU0Zjg3OGQ3NzMyNWUyMzMyNDAwZTEwZWJkMjFkY2YifQ.3hHc6iQP-Xkz9Q5rMIOFENDdh5P-NSaRs4Y4ffbJcMg"
// );
// Cookies.set("corp_id", "wweaefe716636df3d1");
// Cookies.set("userid", "maoxiya");
......
......@@ -68,3 +68,19 @@ export function answerComment(data) {
data
})
}
// 会话生命线列表
export function sessionSummaryList(data) {
return request({
url: '/sidebar/client_session/sessionSummaryList',
method: 'post',
data
})
}
// 重新生成会话总结
export function regenerateSummary(data) {
return request({
url: '/sidebar/client_session/regenerateSummary',
method: 'post',
data
})
}
......@@ -316,3 +316,19 @@ export function corp_follow_up_task_index(data) {
data
})
}
// 【智能待办】工作台-我的
export function corpIntelligentTaskMineList(data) {
return request({
url: returnApi('/corp_intelligent_task/mineList'),
method: 'post',
data
})
}
// 【智能待办】工作台-完成、未完成
export function corpIntelligentTaskExternalList(data) {
return request({
url: returnApi('/corp_intelligent_task/externalList'),
method: 'post',
data
})
}
\ No newline at end of file
<template>
<div class="loading rowFlex allCenter">
<svg-icon icon-class="loading" class="loadingIcon" />
<!-- <svg-icon icon-class="loading" class="loadingIcon" /> -->
<i class="el-icon-loading loadingIcon text-primary"></i>
<p class="text">加载中</p>
</div>
</template>
</template>
<script>
export default {
name: 'loading',
<script>
export default {
name: "loading",
data() {
return {
}
return {};
},
mounted() {},
methods: {}
}
</script>
<style lang="scss" scoped>
.loading{
methods: {},
};
</script>
<style lang="scss" scoped>
.loading {
position: absolute;
left: 50%;
transform: translateX(-50%);
top: 0;
.loadingIcon{
.loadingIcon {
font-size: 24px;
animation: rotage linear 1s infinite;
}
.text{
color: #409EFF;
.text {
color: #409eff;
font-size: 14px;
margin-left: 5px;
}
}
@keyframes rotage {
0%{
}
@keyframes rotage {
0% {
transform: rotate(0deg);
}
100%{
100% {
transform: rotate(360deg);
}
}
</style>
\ No newline at end of file
}
</style>
<template>
<div v-if="isExternal" :style="styleExternalIcon" class="svg-external-icon svg-icon" v-on="$listeners" />
<svg v-else :class="svgClass" aria-hidden="true" :style="{ display: iconClass || svgName ? '' : 'none' }"
v-on="$listeners">
<div
v-if="isExternal"
:style="styleExternalIcon"
class="svg-external-icon svg-icon"
v-on="$listeners"
/>
<svg
v-else
:class="svgClass"
class="iconpark-icon"
aria-hidden="true"
:style="{ display: iconClass || svgName ? '' : 'none' }"
v-on="$listeners"
>
<use :xlink:href="iconName" />
</svg>
</template>
<script lang="jsx">
// doc: https://panjiachen.github.io/vue-element-admin-site/feature/component/svg-icon.html#usage
import { isExternal } from '@/utils/validate'
import { isExternal } from "@/utils/validate";
export default {
name: 'SvgIcon',
name: "SvgIcon",
props: {
iconClass: {
type: String,
default: ''
default: "",
},
svgName: {
type: String,
default: ''
default: "",
},
className: {
type: String,
default: ''
}
default: "",
},
},
computed: {
isExternal() {
return isExternal(this.iconClass)
return isExternal(this.iconClass);
},
iconName() {
if (this.svgName) {
return `#${this.svgName}`
return `#${this.svgName}`;
}
return `#icon-${this.iconClass}`
return `#icon-${this.iconClass}`;
},
svgClass() {
if (this.className) {
return 'svg-icon ' + this.className
return "svg-icon " + this.className;
} else {
return 'svg-icon'
return "svg-icon";
}
},
styleExternalIcon() {
return {
mask: `url(${this.iconClass}) no-repeat 50% 50%`,
'-webkit-mask': `url(${this.iconClass}) no-repeat 50% 50%`
}
}
}
}
"-webkit-mask": `url(${this.iconClass}) no-repeat 50% 50%`,
};
},
},
};
</script>
<style scoped>
......
import Vue from 'vue'
{/* <iconpark-icon name="icon-fuzhi"></iconpark-icon> */}
// {/* <iconpark-icon name="icon-fuzhi"></iconpark-icon> */}
const copy = {
// 当被绑定的元素插入到DOM中时
inserted: function(el, binding) {
// 创建复制图标元素
const copyIcon = document.createElement('iconpark-icon')
copyIcon.name='icon-fuzhi'
const copyIcon = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
copyIcon.innerHTML = '<use href="#icon-fuzhi"></use>'
copyIcon.setAttribute('class', 'iconpark-icon')
copyIcon.style.cursor = 'pointer'
copyIcon.style.marginLeft = '8px'
copyIcon.style.fontSize = '16px'
copyIcon.style.height = '16px'
copyIcon.style.width = '16px'
copyIcon.title = '点击复制'
// 设置元素的position为relative,确保图标的absolute定位正确
......@@ -17,7 +19,7 @@ const copy = {
}
// 添加复制图标到元素后面
el.insertBefore(copyIcon, el.nextSibling)
el.appendChild(copyIcon)
// 复制功能实现
const clickHandler = async function(e) {
......
......@@ -15,6 +15,7 @@ import taskList from '@/views/taskList.vue'
import aiChat from '@/views/components/aiChat/aiChat.vue'
import Cookies from 'js-cookie'
import store from '@/store'
import UserAgency from '@/views/userAgency/index.vue'
Vue.use(VueRouter)
import { getParams } from '@/utils/index'
const routes = [
......@@ -85,6 +86,11 @@ const routes = [
component: taskList
},
{
path: '/userToDo',
name: 'userToDo',
component: UserAgency
},
{
path: '/aiChat',
name: 'aiChat',
component: aiChat
......
......@@ -25,24 +25,40 @@
</div>
</template>
<script>
import AreaTransferApply from './components/ApplyRecords/AreaTransferApply.vue'
import errorHandle from './components/ApplyRecords/errorHandle.vue'
import TerminalTransfer from './components/ApplyRecords/TerminaTranfer.vue'
import report from './components/roleInfo/report.vue'
import AreaTransferApply from "./components/ApplyRecords/AreaTransferApply.vue";
import errorHandle from "./components/ApplyRecords/errorHandle.vue";
import TerminalTransfer from "./components/ApplyRecords/TerminaTranfer.vue";
import report from "./components/roleInfo/report.vue";
export default {
name: 'applyRecord',
name: "applyRecord",
components: {
AreaTransferApply,
errorHandle,
TerminalTransfer,
report
report,
},
data() {
return {
activeTab: 'report'
}
activeTab: "report",
};
},
created() {
const related_task_info = JSON.parse(
sessionStorage.getItem("related_task_info")
);
if (related_task_info) {
const type = {
6: "report",
12: "errorHandle",
14: "serve",
13: "terminal",
};
this.activeTab = type[related_task_info.related_task_type] || "report";
} else {
this.activeTab = "report";
}
}
},
};
</script>
<style lang="scss" scoped>
......
......@@ -33,10 +33,10 @@
@click="previewVideo(item)"
>
<div class="flex items-center">
<iconpark-icon
name="xiaoxicaozuo-chakan"
class="mr-[4px] text-[14px]"
></iconpark-icon>
<svg-icon
svgName="xiaoxicaozuo-chakan"
class="mr-[4px] h-[14px] w-[14px] text-[14px]"
></svg-icon>
预览
</div>
</el-button>
......@@ -44,10 +44,10 @@
@click="sendVideo(item)"
class="h-[24px] ml-[8px] cursor-pointer hover:bg-[#E7F1FD] text-[12px] w-[58px] p-0 flex justify-center items-center rounded-full bg-[#F7F8FA] text-[#267EF0]"
>
<iconpark-icon
name="icon-fasonghuashu"
class="text-[14px] mr-[4px]"
></iconpark-icon>
<svg-icon
svgName="icon-fasonghuashu"
class="text-[14px] w-[14px] h-[14px] mr-[4px]"
></svg-icon>
<span> 发送</span>
</div>
</div>
......
<template>
<div
class="p-[16px] h-full overflow-y-auto"
ref="conversationLifeline"
@scroll="handleScroll"
>
<div
class="flex justify-between"
v-for="(v, index) in sessionSummaryList"
:key="v._id"
>
<div class="flex flex-col py-[4px] min-w-[24px] shrink-0 items-center">
<div class="bg-primary h-[12px] w-[12px] rounded-full"></div>
<div class="pt-[10px] flex-1">
<div class="w-[1px] h-full bg-[#D6D9E0]"></div>
</div>
</div>
<div class="text-[#323335] text-[14px] flex-1">
<div class="leading-[20px] text-[12px] text-[#C9CDD4]">
{{ v.allot_time }}{{ v.close_time }}
</div>
<div
class="py-[8px] px-[12px] rounded-[8px] border-[1px] border-solid border-[#E5E6EB]"
>
<div class="flex items-center justify-between">
<span class="font-medium">会话总结</span>
<span class="text-[#86909C] text-[14px]"
>客服:{{ v.zq_user_name }}</span
>
</div>
<div class="px-[12px] py-[8px] rounded-[4px] bg-[#F7F8FA] mt-[4px]">
{{ v.session_summary }}
</div>
<div class="pt-[8px]">
<span class="font-medium">会话分析</span>
</div>
<div class="px-[12px] py-[8px] rounded-[4px] bg-[#F7F8FA] mt-[4px]">
{{ v.session_analysis }}
</div>
<div class="flex flex-wrap gap-[8px] mt-[8px]">
<div
v-for="(tag, tagIndex) in v.session_summary_label_str?.split(',')"
:key="tagIndex"
class="px-[6px] leading-[22px] rounded-[4px] bg-[#F5F5F5] text-[#131920] text-[13px]"
>
{{ tag }}
</div>
</div>
</div>
<div class="flex justify-end">
<el-button
type="text"
icon="el-icon-redo"
:loading="v.loading"
@click="getRegenerateSummary(v)"
>重新生成</el-button
>
</div>
</div>
</div>
<div
v-if="sessionSummaryList.length === 0 && !loading"
class="text-center py-8 text-[#C9CDD4]"
>
暂无会话总结
</div>
<div v-if="loading" class="text-center py-4 text-[#C9CDD4]">加载中...</div>
</div>
</template>
<script>
import { sessionSummaryList, regenerateSummary } from "@/api/aiChat";
import { mapState } from "vuex";
export default {
name: "ConversationLifeline",
data() {
return {
sessionSummaryList: [],
loading: false,
page: 1,
pageSize: 10,
total: 0,
isLastPage: false,
};
},
computed: {
...mapState("game", ["chatUserInfo"]),
...mapState("user", ["corp_id"]),
},
mounted() {
this.getSessionSummaryList();
},
methods: {
/**
* 获取会话总结列表
* @param {boolean} isLoadMore - 是否为加载更多
*/
async getSessionSummaryList(isLoadMore = false) {
// 如果已经是最后一页,则不再加载
if (isLoadMore && this.isLastPage) {
return;
}
try {
this.loading = true;
// 如果是加载更多,则页码+1
const currentPage = isLoadMore ? this.page + 1 : 1;
// 获取企业ID和外部用户ID
const corpId = this.corp_id;
const externalUserId = this.chatUserInfo.external_userid;
const params = {
corp_id: corpId,
external_userid: externalUserId,
page: currentPage,
page_size: this.pageSize,
};
const res = await sessionSummaryList(params);
if (res.status_code === 1) {
const newData = res.data.data.map((item) => ({
...item,
loading: false,
}));
const totalCount = res.data.page_info.total;
// 合并数据
if (isLoadMore) {
this.sessionSummaryList = [...this.sessionSummaryList, ...newData];
this.page = currentPage;
} else {
this.sessionSummaryList = newData;
this.page = 1;
}
this.total = totalCount;
// 判断是否为最后一页
this.isLastPage = this.sessionSummaryList.length >= this.total;
console.log("会话总结列表数据:", res.data);
return this.sessionSummaryList;
} else {
this.$message({
message: res.data.msg,
type: "error",
});
return [];
}
} catch (error) {
console.error("获取会话总结列表失败:", error);
this.$message({
message: "获取会话总结列表失败",
type: "error",
});
return [];
} finally {
this.loading = false;
}
},
/**
* 滚动到底部触发分页
*/
handleScroll() {
const container = this.$refs.conversationLifeline;
if (!container || this.loading || this.isLastPage) return;
// 滚动到底部的判断条件
const scrollTop = container.scrollTop;
const scrollHeight = container.scrollHeight;
const clientHeight = container.clientHeight;
// 当距离底部20px时触发加载更多
if (scrollHeight - scrollTop - clientHeight < 20) {
this.getSessionSummaryList(true);
}
},
// 重新生成会话总结
async getRegenerateSummary(data) {
data.loading = true;
try {
await regenerateSummary({ session_id: data.session_id });
if (res.status_code === 1) {
this.$message({
message: "重新生成成功",
type: "success",
});
// 重新加载数据
this.getSessionSummaryList();
} else {
this.$message({
message: res.data.msg,
type: "error",
});
}
} catch (error) {
console.error("重新生成会话总结失败:", error);
} finally {
data.loading = false;
}
},
},
};
</script>
......@@ -11,50 +11,53 @@
<el-tab-pane label="跟进任务记录" name="aiFollowTask">
<followTask v-if="activeName === 'aiFollowTask'" />
</el-tab-pane>
<el-tab-pane label="会话生命线" name="ConversationLifeline">
<ConversationLifeline v-if="activeName === 'ConversationLifeline'" />
</el-tab-pane>
</el-tabs>
</div>
</div>
</template>
<script>
import aiArgenChat from './aiArgenChat.vue'
import summaryList from './summaryList.vue'
import followTask from './followTask.vue'
import { mapActions } from 'vuex'
import aiArgenChat from "./aiArgenChat.vue";
import summaryList from "./summaryList.vue";
import followTask from "./followTask.vue";
import ConversationLifeline from "./ConversationLifeline.vue";
import { mapActions } from "vuex";
export default {
name: 'quickSendGame',
name: "quickSendGame",
components: {
aiArgenChat,
summaryList,
followTask,
ConversationLifeline,
},
data() {
return {
activeName: 'aiChat'
}
},
created() {
activeName: "aiChat",
};
},
created() {},
mounted() {
// this.initializeWecom()
},
methods: {
...mapActions('user', ['initWecom']),
...mapActions("user", ["initWecom"]),
async initializeWecom() {
try {
console.log('🚀 开始初始化企业微信 SDK')
const result = await this.initWecom()
console.log('✅ 企业微信 SDK 初始化成功', result)
console.log("🚀 开始初始化企业微信 SDK");
const result = await this.initWecom();
console.log("✅ 企业微信 SDK 初始化成功", result);
} catch (error) {
console.error('❌ 企业微信 SDK 初始化失败:', error)
console.error("❌ 企业微信 SDK 初始化失败:", error);
}
},
}
}
},
};
</script>
<style lang="scss" scoped>
.quickSendGame {
::v-deep .el-tabs__nav-next,
::v-deep .el-tabs__nav-prev {
line-height: 50px;
......
......@@ -11,7 +11,11 @@ module.exports = {
purge: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
darkMode: false,
theme: {
extend: {}
extend: {
colors: {
primary: '#409EFF',
}
}
},
variants: {
extend: {}
......@@ -19,6 +23,15 @@ module.exports = {
plugins: [
function({ addUtilities }) {
const newUtilities = {
'.truncate': {
display: '-webkit-box',
'-webkit-box-orient': 'vertical',
'-webkit-line-clamp': '1',
'overflow': 'hidden',
'text-overflow': 'ellipsis',
'word-wrap': 'break-word',
'word-break': 'break-word',
},
'.truncate-2': {
display: '-webkit-box',
'-webkit-box-orient': 'vertical',
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论