提交 4e77a778 作者: 毛细亚

合并分支 'release' 到 'master'

Release

查看合并请求 !39
<!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>
<script src="https://lf1-cdn-tos.bytegoofy.com/obj/iconpark/icons_27278_172.5464f393968eda872f41eab2242bbbd7.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>
......
...@@ -1467,3 +1467,45 @@ export function sendEmail(data) { ...@@ -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'; ...@@ -15,6 +15,7 @@ import '@/styles/index.scss';
import moment from 'moment' import moment from 'moment'
import '@/styles/tailwind.css' import '@/styles/tailwind.css'
import VConsole from 'vconsole'; import VConsole from 'vconsole';
import copy from './directive/copy'
import uploading from '@/utils/cos-upload' import uploading from '@/utils/cos-upload'
import errorHandle from '@/utils/errorHandle' import errorHandle from '@/utils/errorHandle'
import { getParams,deepClone } from '@/utils/index' import { getParams,deepClone } from '@/utils/index'
...@@ -24,7 +25,7 @@ import loadmore from '@/directive/loadmore/index.js' // 加载更多 ...@@ -24,7 +25,7 @@ import loadmore from '@/directive/loadmore/index.js' // 加载更多
import clickagain from './directive/clickagain' import clickagain from './directive/clickagain'
import permission from '@/directive/permission/index.js' // 权限判断指令 import permission from '@/directive/permission/index.js' // 权限判断指令
import scroll from '@/directive/scroll' // 下拉加载更多指令 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 清理工具 // 导入 VConsole 清理工具
import '@/utils/vconsoleCleanup' import '@/utils/vconsoleCleanup'
......
...@@ -14,6 +14,7 @@ const state = { ...@@ -14,6 +14,7 @@ const state = {
external_userid:'' external_userid:''
}, },
userid:Cookies.get('userid'), userid:Cookies.get('userid'),
weixin_blongs_id:localStorage.getItem('weixin_blongs_id'),//客服号项目id
corp_id:'', corp_id:'',
external_userid:'', external_userid:'',
token:'', token:'',
...@@ -37,6 +38,12 @@ const state = { ...@@ -37,6 +38,12 @@ const state = {
} }
const mutations = { 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){ set_userInfo(state,userInfo){
state.userInfo = userInfo state.userInfo = userInfo
}, },
......
...@@ -537,6 +537,10 @@ li { ...@@ -537,6 +537,10 @@ li {
height: 100%; height: 100%;
font-size: 300px; font-size: 300px;
} }
.el-loading-spinner{
display: flex;
justify-content: center;
}
.el-loading-spinner .circular { .el-loading-spinner .circular {
width: 60px !important; 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 @@ ...@@ -312,7 +312,7 @@
taskDetails, taskDetails,
taskTrack, taskTrack,
} from '@/api/game' } from '@/api/game'
import { memberBindExternalUser } from '@/api/works' import { memberBindExternalUser,clientSessionBindTaskApi } from '@/api/works'
import layer from '@/components/dialog.vue' import layer from '@/components/dialog.vue'
import SendEmailDialog from './SendEmailDialog.vue' import SendEmailDialog from './SendEmailDialog.vue'
import applyGift from '@/views/components/giftRecord/applyGift.vue' import applyGift from '@/views/components/giftRecord/applyGift.vue'
......
...@@ -2,87 +2,81 @@ ...@@ -2,87 +2,81 @@
<div class="details columnFlex"> <div class="details columnFlex">
<div class="content search-form"> <div class="content search-form">
<el-tabs v-model="activeName"> <el-tabs v-model="activeName">
<el-tab-pane <el-tab-pane label="个人话术" name="personal">
label="个人话术"
name="personal"
>
<skillPersonal <skillPersonal
v-if="activeName === 'personal'" v-if="activeName === 'personal'"
:active-name="activeName" :active-name="activeName"
/> />
</el-tab-pane> </el-tab-pane>
<el-tab-pane <el-tab-pane label="企业话术" name="company">
label="企业话术" <skillCompany :active-name="activeName" />
name="company"
>
<skillCompany
:active-name="activeName"
/>
</el-tab-pane> </el-tab-pane>
<el-tab-pane <el-tab-pane label="知识库" name="library">
label="知识库"
name="library"
>
<skillLibrary <skillLibrary
v-if="activeName === 'library'" v-if="activeName === 'library'"
:active-name="activeName" :active-name="activeName"
/> />
</el-tab-pane> </el-tab-pane>
<el-tab-pane <el-tab-pane label="跨主体知识库" name="robotLibrary">
label="跨主体知识库"
name="robotLibrary"
>
<crossLibrary <crossLibrary
v-if="activeName === 'robotLibrary'" v-if="activeName === 'robotLibrary'"
:active-name="activeName" :active-name="activeName"
/> />
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="教学视频" name="instructionalVideo">
<InstructionalVideo
v-if="activeName === 'instructionalVideo'"
:active-name="activeName"
/>
</el-tab-pane>
</el-tabs> </el-tabs>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
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 crossLibrary from "./components/skill/crossLibrary.vue";
import { mapActions } from 'vuex' import InstructionalVideo from "./components/InstructionalVideo/index.vue";
import { mapActions } from "vuex";
export default { export default {
name: 'quickReply', name: "quickReply",
components: { components: {
skillCompany, skillCompany,
skillPersonal, skillPersonal,
skillLibrary, skillLibrary,
crossLibrary crossLibrary,
InstructionalVideo,
}, },
data() { data() {
return { return {
activeName: 'personal' activeName: "personal",
} };
},
created() {
}, },
created() {},
mounted() { mounted() {
this.initializeWecom() this.initializeWecom();
}, },
methods: { methods: {
...mapActions('user', ['initWecom']), ...mapActions("user", ["initWecom"]),
async initializeWecom() { async initializeWecom() {
try { try {
console.log('🚀 开始初始化企业微信 SDK') console.log("🚀 开始初始化企业微信 SDK");
const result = await this.initWecom() const result = await this.initWecom();
console.log('✅ 企业微信 SDK 初始化成功', result) console.log("✅ 企业微信 SDK 初始化成功", result);
} catch (error) { } catch (error) {
console.error('❌ 企业微信 SDK 初始化失败:', error) console.error("❌ 企业微信 SDK 初始化失败:", error);
} }
}, },
} },
} };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.details { .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; line-height: 50px;
} }
width: 100%; width: 100%;
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论