提交 4e77a778 作者: 毛细亚

合并分支 'release' 到 'master'

Release

查看合并请求 !39
<!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>
<script src="https://lf1-cdn-tos.bytegoofy.com/obj/iconpark/icons_27278_172.5464f393968eda872f41eab2242bbbd7.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>
......
......@@ -1467,3 +1467,45 @@ export function sendEmail(data) {
})
})
}
// 获取开/合服天数
export function getServerDayApi(data) {
return new Promise((resolve, reject) => {
cross_systemRequest({
system: 'zhangyou',
api: '/api/role/getServerDay',
params: data
}).then((res) => {
resolve(res)
}).catch((error) => {
reject(error)
})
})
}
// 项目-视频分类
export function teachingVideoCategoryListApi(data) {
return new Promise((resolve, reject) => {
cross_systemRequest({
system: 'zhangyou',
api: '/api/teaching_video/categoryList',
params: data
}).then((res) => {
resolve(res)
}).catch((error) => {
reject(error)
})
})
}
// 视频列表
export function teachingVideoVideoListApi(data) {
return new Promise((resolve, reject) => {
cross_systemRequest({
system: 'zhangyou',
api: '/api/teaching_video/videoList',
params: data
}).then((res) => {
resolve(res)
}).catch((error) => {
reject(error)
})
})
}
\ No newline at end of file
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#a)"><mask id="b" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="14" height="14"><path d="M14 0H0v14h14z" fill="#fff"/></mask><g mask="url(#b)" stroke="#267ef0" stroke-width=".875" stroke-linecap="round" stroke-linejoin="round"><path d="M9.333 7.525v2.45c0 2.042-.816 2.858-2.858 2.858h-2.45c-2.042 0-2.858-.816-2.858-2.858v-2.45c0-2.042.816-2.858 2.858-2.858h2.45c2.042 0 2.858.816 2.858 2.858"/><path d="M12.833 4.025v2.45c0 2.042-.816 2.858-2.858 2.858h-.642V7.525c0-2.042-.816-2.858-2.858-2.858H4.667v-.642c0-2.042.816-2.858 2.858-2.858h2.45c2.042 0 2.858.816 2.858 2.858"/></g></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h14v14H0z"/></clipPath></defs></svg>
\ No newline at end of file
import Vue from 'vue'
{/* <iconpark-icon name="icon-fuzhi"></iconpark-icon> */}
const copy = {
// 当被绑定的元素插入到DOM中时
inserted: function(el, binding) {
// 创建复制图标元素
const copyIcon = document.createElement('iconpark-icon')
// const copyIcon = document.createElement('div')
// copyIcon.setAttribute('icon-class', 'copy')
copyIcon.name='icon-fuzhi'
copyIcon.style.cursor = 'pointer'
copyIcon.style.marginLeft = '8px'
copyIcon.style.fontSize = '16px'
copyIcon.title = '点击复制'
copyIcon.innerHTML='<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#a)"><mask id="b" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="14" height="14"><path d="M14 0H0v14h14z" fill="#fff"/></mask><g mask="url(#b)" stroke="#267ef0" stroke-width=".875" stroke-linecap="round" stroke-linejoin="round"><path d="M9.333 7.525v2.45c0 2.042-.816 2.858-2.858 2.858h-2.45c-2.042 0-2.858-.816-2.858-2.858v-2.45c0-2.042.816-2.858 2.858-2.858h2.45c2.042 0 2.858.816 2.858 2.858"/><path d="M12.833 4.025v2.45c0 2.042-.816 2.858-2.858 2.858h-.642V7.525c0-2.042-.816-2.858-2.858-2.858H4.667v-.642c0-2.042.816-2.858 2.858-2.858h2.45c2.042 0 2.858.816 2.858 2.858"/></g></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h14v14H0z"/></clipPath></defs></svg>'
// 设置元素的position为relative,确保图标的absolute定位正确
if (getComputedStyle(el).position === 'static') {
el.style.position = 'relative'
}
// 添加复制图标到元素后面
el.insertBefore(copyIcon, el.nextSibling)
// 复制功能实现
copyIcon.addEventListener('click', async function(e) {
// 阻止事件冒泡
e.stopPropagation()
try {
// 获取要复制的内容
const textToCopy = binding.value || ''
if (!textToCopy) {
Vue.prototype.$message.warning('没有可复制的内容')
return
}
// 使用现代的剪贴板API
if (navigator.clipboard && window.isSecureContext) {
await navigator.clipboard.writeText(textToCopy)
} else {
// 兼容旧版浏览器
const textArea = document.createElement('textarea')
textArea.value = textToCopy
textArea.style.position = 'fixed'
textArea.style.left = '-999999px'
textArea.style.top = '-999999px'
document.body.appendChild(textArea)
textArea.focus()
textArea.select()
// 执行复制命令
const success = document.execCommand('copy')
document.body.removeChild(textArea)
if (!success) {
throw new Error('复制失败')
}
}
// 显示复制成功的提示
Vue.prototype.$message.success('复制成功')
} catch (error) {
console.error('复制失败:', error)
Vue.prototype.$message.error('复制失败,请手动复制')
}
})
// 存储图标引用,以便在组件卸载时清理
el.__copyIcon = copyIcon
},
// 当指令与元素解绑时
unbind: function(el) {
// 清理事件监听器和元素
if (el.__copyIcon) {
el.__copyIcon.removeEventListener('click', null)
el.parentNode.removeChild(el.__copyIcon)
delete el.__copyIcon
}
}
}
export default copy
\ No newline at end of file
import copy from './copy.js'
const install = function(Vue) {
Vue.directive('copy', copy)
}
if (window.Vue) {
window.copy = copy
Vue.use(install); // eslint-disable-line
}
copy.install = install
export default copy
......@@ -15,6 +15,7 @@ import '@/styles/index.scss';
import moment from 'moment'
import '@/styles/tailwind.css'
import VConsole from 'vconsole';
import copy from './directive/copy'
import uploading from '@/utils/cos-upload'
import errorHandle from '@/utils/errorHandle'
import { getParams,deepClone } from '@/utils/index'
......@@ -24,7 +25,7 @@ import loadmore from '@/directive/loadmore/index.js' // 加载更多
import clickagain from './directive/clickagain'
import permission from '@/directive/permission/index.js' // 权限判断指令
import scroll from '@/directive/scroll' // 下拉加载更多指令
Vue.use(globalComponent).use(permission).use(clickagain).use(loadmore).use(scroll)
Vue.use(globalComponent).use(permission).use(clickagain).use(loadmore).use(scroll).use(copy)
// 导入 VConsole 清理工具
import '@/utils/vconsoleCleanup'
......
......@@ -14,6 +14,7 @@ const state = {
external_userid:''
},
userid:Cookies.get('userid'),
weixin_blongs_id:localStorage.getItem('weixin_blongs_id'),//客服号项目id
corp_id:'',
external_userid:'',
token:'',
......@@ -37,6 +38,12 @@ const state = {
}
const mutations = {
set_weixin_blongs_id(state,weixin_blongs_id){
state.weixin_blongs_id = weixin_blongs_id
// Cookies.set('weixin_blongs_id', weixin_blongs_id)
localStorage.setItem('weixin_blongs_id', weixin_blongs_id)
},
set_userInfo(state,userInfo){
state.userInfo = userInfo
},
......
......@@ -537,6 +537,10 @@ li {
height: 100%;
font-size: 300px;
}
.el-loading-spinner{
display: flex;
justify-content: center;
}
.el-loading-spinner .circular {
width: 60px !important;
......
<template>
<div class="h-full flex flex-col">
<el-input
placeholder="请输入内容"
prefix-icon="el-icon-search"
v-model.trim="searchText"
@input="debouncedGetVideoList"
>
</el-input>
<el-cascader
class="w-full mt-[8px]"
v-model="categoryValue"
:props="{ emitPath: false, expandTrigger: 'click' }"
:options="categoryList"
@change="debouncedGetVideoList"
></el-cascader>
<div
class="mt-[2px] space-y-[8px] flex-1 overflow-auto pb-[10px]"
v-loading="loading"
>
<div
v-for="item in videoList"
:key="item.id"
class="flex justify-between items-center py-[3px] px-[8px] bottom-[1px] border border-[#E5E7EB] rounded-[4px]"
>
<div class="text-[14px]">{{ item.video_name }}</div>
<div class="flex items-center">
<el-button
type="text"
size="small"
class="text-[12px] button-color-hover"
@click="previewVideo(item)"
>
<div class="flex items-center">
<iconpark-icon
name="xiaoxicaozuo-chakan"
class="mr-[4px] text-[14px]"
></iconpark-icon>
预览
</div>
</el-button>
<div
@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>
<span> 发送</span>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import {
teachingVideoVideoListApi,
teachingVideoCategoryListApi,
} from "@/api/game";
import { mapMutations, mapState } from "vuex";
import { sendChatMessage } from "@/utils/index";
import { debounce } from "@/utils";
export default {
name: "InstructionalVideo",
data() {
return {
searchText: "",
categoryValue: {},
categoryList: [],
videoList: [],
debouncedGetVideoList: () => {},
loading: false,
};
},
mounted() {
this.getCategoryList();
// 初始化防抖函数,延迟300ms执行
this.debouncedGetVideoList = debounce(() => {
this.loading = true;
this.getVideoList().finally(() => {
this.loading = false;
});
}, 300);
},
computed: {
...mapState("user", ["userInfo", "weixin_blongs_id"]),
},
methods: {
...mapMutations("common", ["set_sendSkillMessage"]),
// 视频分类
async getCategoryList() {
// return;
const { data } = await teachingVideoCategoryListApi({
weixin_blongs_id: this.weixin_blongs_id,
});
this.categoryList = this.formatCategoryList(data);
},
formatCategoryList(data) {
return data.map((item) => {
return {
value: item,
label: item.main_game_name,
children: item.category.map((item) => ({
value: item,
label: item.category_name,
})),
};
});
},
// 视频列表
async getVideoList() {
if (!this.categoryValue.id) {
return;
}
const { data } = await teachingVideoVideoListApi({
weixin_blongs_id:
this.categoryValue.weixin_blongs_id || this.weixin_blongs_id,
main_game_id: this.categoryValue.main_game_id,
video_type: this.categoryValue.id,
video_name: this.searchText,
page: 1,
page_size: 200,
});
this.videoList = data.data;
},
sendVideo(item) {
try {
const link = {
title: item.video_name,
imgUrl: item.cover_url || "",
desc: "点击观看教学视频",
link: item.video_url,
};
sendChatMessage(link, "link");
} catch (error) {
console.error("发送视频链接失败:", error);
this.$message({ message: "发送视频链接失败", type: "error" });
}
},
previewVideo(item) {
window.open(item.video_url);
},
},
};
</script>
<style scoped>
.button-color-hover.el-button--text:hover {
color: #267ef0 !important;
}
.button-color-hover.el-button--text {
color: #86909c;
}
</style>
......@@ -312,7 +312,7 @@
taskDetails,
taskTrack,
} from '@/api/game'
import { memberBindExternalUser } from '@/api/works'
import { memberBindExternalUser,clientSessionBindTaskApi } from '@/api/works'
import layer from '@/components/dialog.vue'
import SendEmailDialog from './SendEmailDialog.vue'
import applyGift from '@/views/components/giftRecord/applyGift.vue'
......
......@@ -2,87 +2,81 @@
<div class="details columnFlex">
<div class="content search-form">
<el-tabs v-model="activeName">
<el-tab-pane
label="个人话术"
name="personal"
>
<el-tab-pane label="个人话术" name="personal">
<skillPersonal
v-if="activeName === 'personal'"
:active-name="activeName"
/>
</el-tab-pane>
<el-tab-pane
label="企业话术"
name="company"
>
<skillCompany
:active-name="activeName"
/>
<el-tab-pane label="企业话术" name="company">
<skillCompany :active-name="activeName" />
</el-tab-pane>
<el-tab-pane
label="知识库"
name="library"
>
<el-tab-pane label="知识库" name="library">
<skillLibrary
v-if="activeName === 'library'"
:active-name="activeName"
/>
</el-tab-pane>
<el-tab-pane
label="跨主体知识库"
name="robotLibrary"
>
<el-tab-pane label="跨主体知识库" name="robotLibrary">
<crossLibrary
v-if="activeName === 'robotLibrary'"
:active-name="activeName"
/>
</el-tab-pane>
<el-tab-pane label="教学视频" name="instructionalVideo">
<InstructionalVideo
v-if="activeName === 'instructionalVideo'"
:active-name="activeName"
/>
</el-tab-pane>
</el-tabs>
</div>
</div>
</template>
<script>
import skillCompany from './components/skill/skillCompany.vue'
import skillPersonal from './components/skill/skillPersonal.vue'
import skillLibrary from './components/skill/skillLibrary.vue'
import crossLibrary from './components/skill/crossLibrary.vue'
import { mapActions } from 'vuex'
import skillCompany from "./components/skill/skillCompany.vue";
import skillPersonal from "./components/skill/skillPersonal.vue";
import skillLibrary from "./components/skill/skillLibrary.vue";
import crossLibrary from "./components/skill/crossLibrary.vue";
import InstructionalVideo from "./components/InstructionalVideo/index.vue";
import { mapActions } from "vuex";
export default {
name: 'quickReply',
name: "quickReply",
components: {
skillCompany,
skillPersonal,
skillLibrary,
crossLibrary
crossLibrary,
InstructionalVideo,
},
data() {
return {
activeName: 'personal'
}
},
created() {
activeName: "personal",
};
},
created() {},
mounted() {
this.initializeWecom()
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>
.details {
::v-deep .el-tabs__nav-next,::v-deep .el-tabs__nav-prev{
::v-deep .el-tabs__nav-next,
::v-deep .el-tabs__nav-prev {
line-height: 50px;
}
width: 100%;
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论