提交 9e546236 作者: 毛细亚

合并分支 'shw-feat-7.1' 到 'test'

Shw feat 7.1

查看合并请求 !38
<!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>
......
...@@ -1424,3 +1424,88 @@ export function getClonePackageLink(data) { ...@@ -1424,3 +1424,88 @@ export function getClonePackageLink(data) {
}) })
}) })
} }
// 使用权益
export function useBenefit(data) {
return new Promise((resolve, reject) => {
cross_systemRequest({
system: 'zhangyou',
api: '/api/marketing_role_grade/useRight',
params: data
}).then((res) => {
resolve(res)
}).catch((error) => {
reject(error)
})
})
}
// 使用权益记录
export function useRightList(data) {
return new Promise((resolve, reject) => {
cross_systemRequest({
system: 'zhangyou',
api: '/api/marketing_role_grade/useRightList',
params: data
}).then((res) => {
resolve(res)
}).catch((error) => {
reject(error)
})
})
}
// 发送邮件
export function sendEmail(data) {
return new Promise((resolve, reject) => {
cross_systemRequest({
system: 'zhangyou',
api: '/api/operator_task/sendEmail',
params: data
}).then((res) => {
resolve(res)
}).catch((error) => {
reject(error)
})
})
}
// 获取开/合服天数
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
...@@ -284,3 +284,19 @@ export function getUserQrCode(data) { ...@@ -284,3 +284,19 @@ export function getUserQrCode(data) {
data, data,
}); });
} }
// 打开会话绑定任务
export function clientSessionBindTaskApi(data) {
return request({
url: returnApi('/client_session/bindTask'),
method: 'post',
data,
});
}
// 根据w账号获取上一次交互时间
export function getMemberInfoApi(data) {
return request({
url: returnApi('/corp_zyou_bind/getMemberInfo'),
method: 'post',
data,
});
}
<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>
...@@ -8,22 +8,22 @@ ...@@ -8,22 +8,22 @@
> >
<div> <div>
<el-form ref="ruleForm" :model="ruleForm" label-width="100px" class="content"> <el-form ref="ruleForm" :model="ruleForm" label-width="100px" class="content">
<el-form-item label="选择角色"> <el-form-item label="选择角色:">
<el-select v-model="ruleForm.role_id" placeholder="请选择角色" style="width:90%;margin-bottom:10px;" @change="selectRole"> <el-select v-model="ruleForm.role_id" placeholder="请选择角色" style="width:90%;margin-bottom:10px;" @change="selectRole">
<el-option <el-option
v-for="(item,index) in roleList" v-for="(item,index) in roleList"
:key="index+1" :key="item.value"
:label="item.label" :label="item.label"
:value="item.value" :value="item.value"
> >
</el-option> </el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="活动类型"> <el-form-item label="活动类型:">
<el-select v-model="ruleForm.gift_type" placeholder="请选择" style="width:90%;margin-bottom:10px;" @change="giftTypeResult"> <el-select v-model="ruleForm.gift_type" placeholder="请选择" style="width:90%;margin-bottom:10px;" @change="giftTypeResult">
<el-option <el-option
v-for="(item,index) in giftTypeList" v-for="(item,index) in giftTypeList"
:key="index+1" :key="item.value"
:label="item.label" :label="item.label"
:value="item.value" :value="item.value"
> >
...@@ -31,11 +31,11 @@ ...@@ -31,11 +31,11 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<!-- 活动类型为角色累充的时候 活动可以多选其他的都为单选 --> <!-- 活动类型为角色累充的时候 活动可以多选其他的都为单选 -->
<el-form-item label="选择活动" prop="rule_id"> <el-form-item label="选择活动:" prop="rule_id">
<el-select v-model="ruleForm.rule_id" :disabled="activeList.length==0" placeholder="请选择" style="width:90%;margin-bottom:10px;" :multiple="ruleForm.gift_type==1 || ruleForm.gift_type == 4 || ruleForm.gift_type== 5 " clearable @change="activeListResult"> <el-select v-model="ruleForm.rule_id" :disabled="activeList.length==0" placeholder="请选择" style="width:90%;margin-bottom:10px;" :multiple="ruleForm.gift_type==1 || ruleForm.gift_type == 4 || ruleForm.gift_type== 5 " clearable @change="activeListResult">
<el-option <el-option
v-for="(item,index) in activeList" v-for="(item,index) in activeList"
:key="index+1" :key="item.id"
:label="item.title_name" :label="item.title_name"
:value="item.id" :value="item.id"
> >
...@@ -56,16 +56,16 @@ ...@@ -56,16 +56,16 @@
</el-form> </el-form>
<!-- 申请礼包 --> <!-- 申请礼包 -->
<!-- 角色累充 --> <!-- 角色累充 -->
<roleRecharge v-if="showGiftDetails && (ruleForm.gift_type==1 || ruleForm.gift_type==4 || ruleForm.gift_type==5) && roleActiveInfo.length>0" :show.sync="showGiftDetails" :request-loading="requestLoading" :active-info="roleActiveInfo" :gift-info="giftDetailsInfo" title="礼包详情" /> <roleRecharge v-if="showGiftDetails && (ruleForm.gift_type==1 || ruleForm.gift_type==4 || ruleForm.gift_type==5) && roleActiveInfo.length>0" :show.sync="showGiftDetails" :request-loading="requestLoading" :active-info="roleActiveInfo" :task_id="task_id" :gift-info="giftDetailsInfo" title="礼包详情" />
<!-- 时间段累充 和 单日累充 积分关的情况 --> <!-- 时间段累充 和 单日累充 积分关的情况 -->
<roleTimeRecharge v-if="showGiftDetails && (ruleForm.gift_type==2 || (ruleForm.gift_type==3 && activeInfo.exchange_score_status==2 ) ) && activeInfo.id " :change-date="changeDate" :request-loading="requestLoading" :show.sync="showGiftDetails" :active-info="activeInfo" :gift-info="giftDetailsInfo" title="礼包详情" @changeDateResult="changeDateResult" @giftDetailsInfo="requestDetailsInfo" /> <roleTimeRecharge v-if="showGiftDetails && (ruleForm.gift_type==2 || ruleForm.gift_type == 7 || (ruleForm.gift_type==3 && activeInfo.exchange_score_status==2 ) ) && activeInfo.id " :change-date="changeDate" :request-loading="requestLoading" :task_id="task_id" :show.sync="showGiftDetails" :active-info="activeInfo" :gift-info="giftDetailsInfo" title="礼包详情" @changeDateResult="changeDateResult" @giftDetailsInfo="requestDetailsInfo" />
<!-- 单日累充 积分开的情况 --> <!-- 单日累充 积分开的情况 显示 svip 权益 并且可以选择 svip权益-->
<oneDayCharge v-if="showGiftDetails && ruleForm.gift_type==3 && activeInfo.exchange_score_status==1 && activeInfo.id" :show.sync="showGiftDetails" :request-loading="requestLoading" :active-info="activeInfo" :change-date="changeDate" :gift-info="giftDetailsInfo" title="礼包详情" @changeDateResult="changeDateResult" @giftDetailsInfo="requestDetailsInfo" /> <oneDayCharge v-if="showGiftDetails && (ruleForm.gift_type==3 || ruleForm.gift_type == 7) && activeInfo.exchange_score_status==1 && activeInfo.id" :show.sync="showGiftDetails" :task_id="task_id" :request-loading="requestLoading" :active-info="activeInfo" :change-date="changeDate" :gift-info="giftDetailsInfo" title="礼包详情" @changeDateResult="changeDateResult" @giftDetailsSVIPInfo="giftDetailsSVIPInfo" @giftDetailsInfo="requestDetailsInfo" />
</div> </div>
</el-drawer> </el-drawer>
</template> </template>
<script type="text/javascript"> <script type="text/javascript">
import { roleList, activeList, giftBagApply, giftTypeList, giftDetailsData } from '@/api/game' import { getRoleHoLo, activeList, giftBagApply, giftTypeList, giftDetailsData } from '@/api/game'
import { mapState, mapMutations, mapActions } from 'vuex' import { mapState, mapMutations, mapActions } from 'vuex'
// import giftDetails from './giftDetails.vue' // import giftDetails from './giftDetails.vue'
import roleRecharge from './giftDetails/roleRecharge.vue' import roleRecharge from './giftDetails/roleRecharge.vue'
...@@ -79,8 +79,8 @@ ...@@ -79,8 +79,8 @@
oneDayCharge, // 单日累充 oneDayCharge, // 单日累充
roleTimeRecharge // 角色时间段累充 roleTimeRecharge // 角色时间段累充
}, },
// type 3:image 4:video // type 3:image 4:video task_id // 账号任务点击礼包申请过来传递的任务id member_id // 我的任务 任务详情 点击 礼包申请的时候 需要用到任务详情的 member_id,这时候需要传递过来
props: ['show', 'width', 'title'], props: ['show', 'width', 'title', 'task_id','member_id'],
data() { data() {
return { return {
roleList: [], roleList: [],
...@@ -124,6 +124,15 @@ ...@@ -124,6 +124,15 @@
this.requestRoleList() this.requestRoleList()
this.giftTypeListData() this.giftTypeListData()
}, },
beforeDestroy() {
// 清理组件销毁时的状态,防止内存泄漏
this.roleList = []
this.activeList = []
this.giftTypeList = []
this.roleActiveInfo = []
this.activeInfo = {}
this.giftDetailsInfo = {}
},
methods: { methods: {
requestActiveList() { requestActiveList() {
if (this.ruleForm.role_id !== '' && this.ruleForm.gift_type !== '') { if (this.ruleForm.role_id !== '' && this.ruleForm.gift_type !== '') {
...@@ -156,15 +165,18 @@ ...@@ -156,15 +165,18 @@
}) })
}, },
requestRoleList() { requestRoleList() {
if (this.accountSelect == '') { if (this.accountSelect == '' && !this.member_id) {
this.$message.warning('暂无关联的账号,请先去关联账号!') this.$message.warning('暂无关联的账号,请先去关联账号!')
return false return false
} }
const data = { const data = {
api_search_name: '', api_search_name: '',
member_id: this.accountSelect member_id:this.member_id || this.accountSelect, // 先取任务详情的member_id,没有的话取账号的member_id
search_type: 'list',
page_size:100,
page:1
} }
roleList(data).then(res => { getRoleHoLo(data).then(res => {
if (res.status_code == 1) { if (res.status_code == 1) {
if (res.data.data.length > 0) { if (res.data.data.length > 0) {
const list = res.data.data.sort((a, b) => { return Number(b.recharge_total) - Number(a.recharge_total) }) const list = res.data.data.sort((a, b) => { return Number(b.recharge_total) - Number(a.recharge_total) })
...@@ -197,7 +209,7 @@ ...@@ -197,7 +209,7 @@
this.activeList = [] this.activeList = []
const gift_type = this.giftTypeList.find(item => item.value == data) const gift_type = this.giftTypeList.find(item => item.value == data)
console.log(gift_type, 'gift_type') console.log(gift_type, 'gift_type')
this.gift_type_text = gift_type.label this.gift_type_text = gift_type.label || ''
this.requestActiveList() this.requestActiveList()
this.activeInfo = {} this.activeInfo = {}
this.giftInfo = {} this.giftInfo = {}
...@@ -246,7 +258,7 @@ ...@@ -246,7 +258,7 @@
this.showGiftDetails = true this.showGiftDetails = true
} else { } else {
this.ruleForm.recharge_date = '' this.ruleForm.recharge_date = ''
if (this.ruleForm.gift_type == 2) { if (this.ruleForm.gift_type == 2 || this.ruleForm.gift_type == 7) {
this.requestGiftDetails() this.requestGiftDetails()
this.showGiftDetails = true this.showGiftDetails = true
} }
...@@ -274,6 +286,21 @@ ...@@ -274,6 +286,21 @@
this.$message.warning('请选择充值日期') this.$message.warning('请选择充值日期')
} }
}, },
// 积分开的情况 显示 svip 权益 并且可以选择 svip权益
giftDetailsSVIPInfo(params){
console.log(params, 'params')
const data = {
role_id: this.ruleForm.role_id,
rule_id: this.ruleForm.rule_id.toString(),
recharge_date: params.recharge_date || '',
right_type: params.right_type || '',
select_type: params.select_type || ''
}
giftDetailsData(data).then(res => {
this.giftDetailsInfo = res.data
this.showGiftDetails = true
})
},
requestGiftDetails(value) { requestGiftDetails(value) {
const data = { const data = {
role_id: this.ruleForm.role_id, role_id: this.ruleForm.role_id,
......
...@@ -55,7 +55,7 @@ ...@@ -55,7 +55,7 @@
</div> </div>
</div> </div>
<!-- 活动列表 --> <!-- 活动列表 -->
<span class="dialog-footer rowFlex"> <span class="rowFlex">
<el-button class="btn" type="primary" :loading="loading" @click="submit">确 定</el-button> <el-button class="btn" type="primary" :loading="loading" @click="submit">确 定</el-button>
<el-button class="btn" @click="close">取 消</el-button> <el-button class="btn" @click="close">取 消</el-button>
</span> </span>
...@@ -68,7 +68,7 @@ ...@@ -68,7 +68,7 @@
export default { export default {
name: 'confirmLayer', name: 'confirmLayer',
// type 3:image 4:video // type 3:image 4:video
props: ['show', 'width', 'title', 'activeInfo', 'remark'], props: ['show', 'width', 'title', 'activeInfo', 'remark','task_id','svipObj'],
data() { data() {
return { return {
loading: false loading: false
...@@ -104,8 +104,11 @@ ...@@ -104,8 +104,11 @@
role_id, role_id,
remark: this.remark, remark: this.remark,
recharge_date: this.activeInfo[0].recharge_date || '', recharge_date: this.activeInfo[0].recharge_date || '',
task_id: this.task_id || null,
create_user: this.cser_name, create_user: this.cser_name,
rule: rule rule: rule,
right_type: this.svipObj?.right_type || '',
select_type: this.svipObj?.select_type || ''
} }
giftBagApply(data).then(res => { giftBagApply(data).then(res => {
this.loading = false this.loading = false
......
...@@ -28,6 +28,13 @@ ...@@ -28,6 +28,13 @@
</el-option> </el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="svip权益">
<el-select v-model.trim="form.right_type" filterable style="width:100%;"
placeholder="请选择svip权益" @change="searchInput">
<el-option v-for="item in benefitOptions" :key="item.value" :label="item.label" :value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="角色名称"> <el-form-item label="角色名称">
<el-input v-model="form.role_name_or_cp_id" placeholder="请输入角色名称" style="width:100%;" <el-input v-model="form.role_name_or_cp_id" placeholder="请输入角色名称" style="width:100%;"
@change="searchInput"></el-input> @change="searchInput"></el-input>
...@@ -59,6 +66,7 @@ ...@@ -59,6 +66,7 @@
</div> </div>
<el-collapse-transition> <el-collapse-transition>
<div v-if="item.showDetails"> <div v-if="item.showDetails">
<p class="text hidden"><label>svip权益:</label> {{ item.right_type_name || '-'}}</p>
<p v-if="item.status == '待提交'" class="text"><label>状态:</label> <span class="noSend">{{ item.status <p v-if="item.status == '待提交'" class="text"><label>状态:</label> <span class="noSend">{{ item.status
}}</span> </p> }}</span> </p>
<p v-else-if="item.status == '已提交'" class="text"><label>状态:</label> <span class="sended">{{ <p v-else-if="item.status == '已提交'" class="text"><label>状态:</label> <span class="sended">{{
...@@ -165,8 +173,10 @@ export default { ...@@ -165,8 +173,10 @@ export default {
role_name_or_cp_id: '', role_name_or_cp_id: '',
member_id: '', member_id: '',
active_title: '', active_title: '',
gift_type: '' gift_type: '',
right_type: '',
}, },
benefitOptions: [],
inputValue: '', inputValue: '',
pageInfo: { pageInfo: {
page: 0, page: 0,
...@@ -198,6 +208,7 @@ export default { ...@@ -198,6 +208,7 @@ export default {
mounted() { mounted() {
this.requestGameList() this.requestGameList()
this.requestGiftType() this.requestGiftType()
this.getBenefitOptions()
}, },
methods: { methods: {
// 重新拉去数据 // 重新拉去数据
...@@ -286,6 +297,27 @@ export default { ...@@ -286,6 +297,27 @@ export default {
this.emailGiftList = [] this.emailGiftList = []
this.requestemailGiftList() this.requestemailGiftList()
}, },
async getBenefitOptions() {
try {
const data = {
type: 'svip_right',
}
const res = await selectSearch(data)
if (res.status_code === 1 && res.data && res.data.data && res.data.data.length > 0) {
const showList = ['转生石福利','月核心玩家礼包','超R生日福利礼包','超R行会专属礼包']
this.benefitOptions = res.data.data.filter((item, index) => {
return showList.includes(item.label)
}).map(item => ({
...item,
value: item.value || item.id // 确保每个选项都有value属性,优先使用value,不存在则使用id
}));
console.log(this.benefitOptions)
}
} catch (error) {
console.error('获取权益选项失败:', error)
}
},
requestemailGiftList() { requestemailGiftList() {
this.listLoading = true this.listLoading = true
if (this.accountSelect == '') { if (this.accountSelect == '') {
......
...@@ -24,9 +24,23 @@ ...@@ -24,9 +24,23 @@
<div class="activeValue">{{ activeInfo.recharge_start_date + '至' + activeInfo.recharge_end_date }}</div> <div class="activeValue">{{ activeInfo.recharge_start_date + '至' + activeInfo.recharge_end_date }}</div>
</div> </div>
<!-- 选择的时候重新请求活动详情接口 --> <!-- 选择的时候重新请求活动详情接口 -->
<div class="activeItem rowFlex"> <div class="activeItem rowFlex">
<div class="activeLabel">充值日期</div> <div style="display: flex; align-items: center;width: 100%;">
<el-date-picker v-model="recharge_date_time" :default-value="activeInfo.recharge_date" style="width: 210px" type="date" value-format="yyyy-MM-dd" :picker-options="activeInfo.pickerOptions " @change="dateChange"> </el-date-picker> <el-select v-model="select_type" :clearable="false" style="width: 120px" @change="onSelectTypeChange">
<el-option label="充值日期" :value="1"></el-option>
<el-option label="SVIP权益" :value="2"></el-option>
</el-select>
<template v-if="select_type === 1">
<el-date-picker v-model="recharge_date_time" :default-value="activeInfo.recharge_date" style="width: 210px" type="date" value-format="yyyy-MM-dd" :picker-options="activeInfo.pickerOptions " @change="dateChange"> </el-date-picker>
</template>
<template v-else-if="select_type === 2">
<el-select v-model="right_type" :clearable="false" style="width: 210px" placeholder="请选择权益" @change="onRightTypeChange">
<el-option v-for="item in benefitOptions" :key="item.id || item.value" :label="item.label" :value="item.value || item.id"></el-option>
</el-select>
</template>
</div>
</div> </div>
<div class="activeItem rowFlex"> <div class="activeItem rowFlex">
<div class="activeLabel">申请角色</div> <div class="activeLabel">申请角色</div>
...@@ -127,13 +141,13 @@ ...@@ -127,13 +141,13 @@
<el-button class="btn" size="small" @click="close">取 消</el-button> <el-button class="btn" size="small" @click="close">取 消</el-button>
</span> </span>
<!-- 确认弹窗 --> <!-- 确认弹窗 -->
<confirmLayer v-if="showConfirmLayer" :remark="remark" :active-info="[activeInfo]" :show.sync="showConfirmLayer" title="请核对申请奖品信息" @close="close" /> <confirmLayer v-if="showConfirmLayer" :remark="remark" :task_id="task_id" :active-info="[activeInfo]" :show.sync="showConfirmLayer" :svipObj="svipObj" title="请核对申请奖品信息" @close="close" />
</div> </div>
</el-drawer> </el-drawer>
</template> </template>
<script type="text/javascript"> <script type="text/javascript">
import { giftBagApply } from '@/api/game' import { giftBagApply,selectSearch } from '@/api/game'
import { mapState, mapMutations, mapActions } from 'vuex' import { mapState, mapMutations, mapActions } from 'vuex'
import confirmLayer from '../confirmLayer' import confirmLayer from '../confirmLayer'
export default { export default {
...@@ -142,7 +156,7 @@ export default { ...@@ -142,7 +156,7 @@ export default {
components: { components: {
confirmLayer confirmLayer
}, },
props: ['show', 'width', 'title', 'info', 'body', 'giftInfo', 'activeInfo', 'changeDate','requestLoading'], props: ['show', 'width', 'title', 'info', 'body', 'giftInfo', 'activeInfo', 'changeDate','requestLoading','task_id'],
data() { data() {
return { return {
remark: '', remark: '',
...@@ -155,7 +169,16 @@ export default { ...@@ -155,7 +169,16 @@ export default {
num: '', num: '',
allpoints: 0, allpoints: 0,
showContent: true, showContent: true,
showConfirmLayer: false benefitOptions:[],
showConfirmLayer: false,
svipObj: {
right_type: '',
select_type: ''
},
// 新增:1 充值日期 2 svip权益
select_type: 1,
// 权益类型选中的值
right_type: ''
} }
}, },
...@@ -175,7 +198,18 @@ export default { ...@@ -175,7 +198,18 @@ export default {
}, },
mounted() { mounted() {
this.recharge_date_time = this.activeInfo.recharge_date this.recharge_date_time = this.activeInfo.recharge_date
this.getBenefitOptions()
}, },
computed: {
// 根据选择类型显示对应的内容
showDatePicker() {
return this.select_type === 1
},
showBenefitSelect() {
return this.select_type === 2
}
},
methods: { methods: {
close() { close() {
this.$emit('update:show', false) this.$emit('update:show', false)
...@@ -187,6 +221,50 @@ export default { ...@@ -187,6 +221,50 @@ export default {
this.$emit('giftDetailsInfo', value) this.$emit('giftDetailsInfo', value)
} }
}, },
async getBenefitOptions() {
try {
const data = {
type: 'svip_right',
}
const res = await selectSearch(data)
if (res.status_code === 1 && res.data && res.data.data && res.data.data.length > 0) {
const showList = ['月核心玩家礼包','超R生日福利礼包']
this.benefitOptions = res.data.data.filter((item, index) => {
return showList.includes(item.label)
}).map(item => ({
...item,
value: item.value || item.id // 确保每个选项都有value属性,优先使用value,不存在则使用id
}));
console.log(this.benefitOptions)
}
} catch (error) {
console.error('获取权益选项失败:', error)
}
},
// 选择类型改变时触发
onSelectTypeChange() {
// 重置选择的值
if (this.select_type === 1) {
this.recharge_date_time = this.activeInfo.recharge_date
// 触发日期变化的接口
this.dateChange(this.recharge_date_time)
} else {
this.right_type = ''
}
},
// 权益类型改变时触发
onRightTypeChange() {
if (this.right_type) {
const data = {
right_type: this.right_type,
select_type: this.select_type
}
// 复用日期变化的接口
this.$emit('giftDetailsSVIPInfo', data)
}
},
openPrizeItem(item, index, value) { openPrizeItem(item, index, value) {
console.log(item, index, value) console.log(item, index, value)
this.$set(this.giftInfo.rule.level_attribute[index], 'showContent', value) this.$set(this.giftInfo.rule.level_attribute[index], 'showContent', value)
...@@ -242,6 +320,8 @@ export default { ...@@ -242,6 +320,8 @@ export default {
} }
this.showConfirmLayer = true this.showConfirmLayer = true
this.activeInfo.type_details = this.giftInfo this.activeInfo.type_details = this.giftInfo
this.svipObj.right_type = this.right_type
this.svipObj.select_type = this.select_type
} }
} }
} }
...@@ -360,6 +440,7 @@ export default { ...@@ -360,6 +440,7 @@ export default {
left: 50%; left: 50%;
transform: translateX(-50%); transform: translateX(-50%);
bottom: 100px; bottom: 100px;
z-index: 100;
} }
.consumption-point{ .consumption-point{
font-size: 14px; font-size: 14px;
......
...@@ -57,7 +57,7 @@ ...@@ -57,7 +57,7 @@
<el-button size="small" class="btn" @click="close">取 消</el-button> <el-button size="small" class="btn" @click="close">取 消</el-button>
</span> </span>
<!-- 确认弹窗 --> <!-- 确认弹窗 -->
<confirmLayer :is-submit="isSubmit" :remark="remark" :active-info="activeInfo" :show.sync="showConfirmLayer" title="请核对申请奖品信息" @close="close" /> <confirmLayer :is-submit="isSubmit" :remark="remark" :task_id="task_id" :active-info="activeInfo" :show.sync="showConfirmLayer" title="请核对申请奖品信息" @close="close" />
</div> </div>
</el-drawer> </el-drawer>
</template> </template>
...@@ -72,7 +72,7 @@ export default { ...@@ -72,7 +72,7 @@ export default {
components: { components: {
confirmLayer confirmLayer
}, },
props: ['show', 'width', 'title', 'info', 'body', 'giftInfo', 'activeInfo','requestLoading'], props: ['show', 'width', 'title', 'info', 'body', 'giftInfo', 'activeInfo','requestLoading','task_id'],
data() { data() {
return { return {
isSubmit: false, isSubmit: false,
...@@ -121,6 +121,7 @@ export default { ...@@ -121,6 +121,7 @@ export default {
role_id, role_id,
remark: this.remark, remark: this.remark,
recharge_date: this.activeInfo[0].recharge_date || '', recharge_date: this.activeInfo[0].recharge_date || '',
task_id: this.task_id || null,
create_user: this.cser_name, create_user: this.cser_name,
rule: rule rule: rule
} }
......
...@@ -64,8 +64,30 @@ ...@@ -64,8 +64,30 @@
class="activeValue" class="activeValue"
>{{ activeInfo.role_info.label }}</div> >{{ activeInfo.role_info.label }}</div>
</div> </div>
<div class="activeItem rowFlex">
<div class="activeLabel">SVIP权益</div>
<div
v-if="giftInfo.rule?.right_type_name"
class="activeValue"
>{{ giftInfo.rule?.right_type_name || '-' }}</div>
</div>
<div class="rowFlex">
<div class="activeItem rowFlex">
<div class="activeLabel" style="width: 70px">可领取次数</div>
<div
v-if="giftInfo.total_recharge"
class="activeValue"
>{{ giftInfo.total_recharge || '-' }}</div>
</div>
<div class="activeItem rowFlex">
<div class="activeLabel" style="width: 90px">剩余领取次数</div>
<div
v-if="giftInfo.real_score"
class="activeValue"
>{{ giftInfo.real_score || '-' }}</div>
</div>
</div>
<!-- 积分关的情况 --> <!-- 积分关的情况 -->
<div <div
v-if="activeInfo.gift_type == 2" v-if="activeInfo.gift_type == 2"
class="activeItem rowFlex" class="activeItem rowFlex"
...@@ -234,7 +256,7 @@ ...@@ -234,7 +256,7 @@
type="primary" type="primary"
size="small" size="small"
:disabled="requestLoading" :disabled="requestLoading"
@click="submit" @click="submitBtn"
:loading="btnLoading" :loading="btnLoading"
>确 定</el-button> >确 定</el-button>
<el-button <el-button
...@@ -247,6 +269,7 @@ ...@@ -247,6 +269,7 @@
<confirmLayer <confirmLayer
v-if="showConfirmLayer" v-if="showConfirmLayer"
:remark="remark" :remark="remark"
:task_id="task_id"
:active-info="[activeInfo]" :active-info="[activeInfo]"
:show.sync="showConfirmLayer" :show.sync="showConfirmLayer"
title="请核对申请奖品信息" title="请核对申请奖品信息"
...@@ -266,7 +289,7 @@ export default { ...@@ -266,7 +289,7 @@ export default {
components: { components: {
confirmLayer confirmLayer
}, },
props: ['show', 'width', 'title', 'info', 'body', 'giftInfo', 'activeInfo', 'changeDate','requestLoading'], props: ['show', 'width', 'title', 'info', 'body', 'giftInfo', 'activeInfo', 'changeDate','requestLoading','task_id'],
data() { data() {
return { return {
remark: '', remark: '',
...@@ -285,6 +308,9 @@ export default { ...@@ -285,6 +308,9 @@ export default {
showConfirmLayer: false showConfirmLayer: false
} }
}, },
computed: {
...mapState('user', ['cser_name'])
},
watch: { watch: {
show(newVal, oldVal) { show(newVal, oldVal) {
if (newVal) { if (newVal) {
...@@ -385,7 +411,58 @@ export default { ...@@ -385,7 +411,58 @@ export default {
handleChange() { handleChange() {
}, },
async submit() { submitBtn(){
// svip 权益礼包
if(this.activeInfo.gift_type==7){
this.submitSvipGfit()
} else{
this.submitGift()
}
},
submitSvipGfit(){
this.loading = true
const role_id = this.activeInfo.role_info.value
// 直接处理activeInfo对象,避免不必要的数组包装和map操作
const activeInfo = this.activeInfo
let level_attribute = []
try {
// 安全地访问嵌套属性
if (this.giftInfo?.rule?.gift_type === 1) {
level_attribute = this.giftInfo.rule.level_attribute || []
} else if (this.giftInfo?.rule?.level_attribute) {
// 过滤出可申请的等级属性
level_attribute = this.giftInfo.rule.level_attribute.filter(item => item.apply_num > 0)
}
} catch (error) {
console.error('处理礼包等级属性时出错:', error)
}
// 构建规则数据
const rule = [{ level_attribute, id: activeInfo.id }]
const data = {
role_id:role_id,
recharge_date:'',
remark: this.remark,
create_user: this.cser_name,
task_id: this.task_id || null,
right_type: this.activeInfo.right_type || '',
select_type:this.activeInfo.gift_type==7?2:'',
rule: rule
}
giftBagApply(data).then(res => {
this.loading = false
if (res.status_code === 1) {
this.$message.success(res.msg)
this.close()
this.$emit('close')
} else {
this.close()
}
}, err => {
this.loading = false
})
},
async submitGift() {
this.btnLoading = true this.btnLoading = true
const data = { const data = {
role_id: this.giftInfo.role_id, role_id: this.giftInfo.role_id,
......
<!--
* @Description: 发送邮件弹窗组件
* @Date: 2025-10-15
-->
<template>
<el-dialog
:visible.sync="dialogVisible"
title="发送邮件"
width="300px"
:close-on-click-modal="false"
:before-close="handleClose"
append-to-body
>
<el-form
ref="emailForm"
:model="formData"
:rules="formRules"
label-width="80px"
class="email-form"
>
<el-form-item label="邮件标题:" prop="email_title">
<el-input
v-model="formData.email_title"
placeholder="请输入邮件标题"
maxlength="100"
show-word-limit
clearable
/>
</el-form-item>
<el-form-item label="邮件内容:" prop="email_content">
<el-input
v-model="formData.email_content"
type="textarea"
:rows="8"
placeholder="请输入邮件内容"
maxlength="1000"
show-word-limit
clearable
/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button size="small" @click="handleClose">取消</el-button>
<el-button
type="primary"
:loading="sending"
@click="handleConfirm"
size="small"
>确定</el-button>
</div>
</el-dialog>
</template>
<script>
import { sendEmail } from '@/api/game'
export default {
name: 'SendEmailDialog',
props: {
// 控制弹窗显示
visible: {
type: Boolean,
default: false
},
// 任务ID
taskId: {
type: [String, Number],
required: true
}
},
data() {
return {
// 弹窗显示状态
dialogVisible: false,
// 发送中状态
sending: false,
// 表单数据
formData: {
email_title: '',
email_content: ''
},
// 表单验证规则
formRules: {
email_title: [
{ required: true, message: '请输入邮件标题', trigger: 'blur' },
{ min: 1, max: 100, message: '邮件标题长度在 1 到 100 个字符', trigger: 'blur' }
],
email_content: [
{ required: true, message: '请输入邮件内容', trigger: 'blur' },
{ min: 1, max: 1000, message: '邮件内容长度在 1 到 1000 个字符', trigger: 'blur' }
]
}
}
},
watch: {
visible(val) {
this.dialogVisible = val
if (!val) {
// 关闭时重置表单
this.resetForm()
}
}
},
methods: {
/**
* 关闭弹窗
*/
handleClose() {
this.dialogVisible = false
this.$emit('update:visible', false)
this.resetForm()
},
/**
* 重置表单
*/
resetForm() {
this.formData = {
email_title: '',
email_content: ''
}
// 清除表单验证
this.$nextTick(() => {
this.$refs.emailForm && this.$refs.emailForm.clearValidate()
})
},
/**
* 确认发送邮件
*/
handleConfirm() {
// 表单验证
this.$refs.emailForm.validate((valid) => {
if (valid) {
this.sendEmailRequest()
} else {
console.log('表单验证失败')
return false
}
})
},
/**
* 发送邮件请求
*/
async sendEmailRequest() {
try {
this.sending = true
const params = {
id: this.taskId,
email_title: this.formData.email_title,
email_content: this.formData.email_content
}
const res = await sendEmail(params)
if (res.status_code === 1) {
this.$message({
message: res.msg || '邮件发送成功',
type: 'success'
})
// 通知父组件发送成功
this.$emit('send-success')
// 关闭弹窗
this.handleClose()
}
} catch (error) {
console.error('发送邮件失败:', error)
} finally {
this.sending = false
}
}
}
}
</script>
<style lang="scss" scoped>
.email-form {
::v-deep .el-form-item {
margin-bottom: 22px;
}
::v-deep .el-textarea__inner {
resize: vertical;
font-family: inherit;
}
::v-deep .el-input__count {
background-color: transparent;
}
}
.dialog-footer {
text-align: right;
padding: 10px 20px 0;
border: none;
.el-button {
min-width: 80px;
}
}
</style>
...@@ -76,9 +76,11 @@ ...@@ -76,9 +76,11 @@
</div> </div>
</div> </div>
<div class="btns rowFlex allCenter" style="margin-top: 20px"> <div class="btns rowFlex allCenter" style="margin-top: 20px">
<el-button :disabled="item.status == 3" :loading="remarkLoading" <el-button size="small" :disabled="item.status == 3" :loading="remarkLoading"
@click="saveRemak(item, index)">保存</el-button> @click="saveRemak(item, index)">保存</el-button>
<el-button type="primary" :disabled="item.status == 3" :loading="taskLoading" <!-- 单日申请礼包任务才展示礼包申请按钮 v-if="item.plan_type == 19 || item.plan_type == 21" 新增 累充礼包申请时 和 单日礼包申请一样 显示 礼包申请按钮 -->
<el-button type="primary" size="small" v-if="item.plan_type == 19 || item.plan_type == 21" :loading="taskLoading" @click="giftApply(item)">礼包申请</el-button>
<el-button type="primary" size="small" :disabled="item.status == 3" :loading="taskLoading"
@click="completeTask(item, index)">保存并完成任务</el-button> @click="completeTask(item, index)">保存并完成任务</el-button>
</div> </div>
</el-collapse-item> </el-collapse-item>
...@@ -92,17 +94,28 @@ ...@@ -92,17 +94,28 @@
</div> </div>
</div> </div>
</div> </div>
<!-- 礼包申请弹窗 -->
<applyGift
v-if="showApplyGift"
:show.sync="showApplyGift"
title="礼包申请"
width="25%"
:task_id="task_id"
@requestData="handleGiftApplySuccess"
/>
</div> </div>
</template> </template>
<script> <script>
import { taskTrack, taskRecord, logsSave, searchcondition } from '@/api/game' import { taskTrack, taskRecord, logsSave, searchcondition } from '@/api/game'
import { mapState } from 'vuex' import { mapState } from 'vuex'
import textEditor from '@/components/textEditor.vue' import textEditor from '@/components/textEditor.vue'
import noContent from '@/components/noContent.vue' import noContent from '@/components/noContent.vue'
import applyGift from '@/views/components/giftRecord/applyGift.vue'
export default { export default {
components: { components: {
textEditor, textEditor,
noContent, noContent,
applyGift
}, },
data() { data() {
return { return {
...@@ -131,6 +144,8 @@ ...@@ -131,6 +144,8 @@
showLayer: false, showLayer: false,
taskLoading: false, taskLoading: false,
remarkLoading: false, remarkLoading: false,
showApplyGift: false,
task_id: null,
pageInfo: { pageInfo: {
page: 0, page: 0,
page_size: 20, page_size: 20,
...@@ -189,8 +204,26 @@ ...@@ -189,8 +204,26 @@
this.searchTrackList() this.searchTrackList()
this.searchconditionError() this.searchconditionError()
}, },
methods: { methods: {
searchcondition() { /**
* 显示礼包申请弹窗
*/
giftApply(item) {
this.task_id = item.id || null
this.showApplyGift = true
},
/**
* 处理礼包申请完成后的回调
* 关闭弹窗并显示成功提示
*/
handleGiftApplySuccess() {
this.showApplyGift = false
this.$message({
message: '礼包申请已提交',
type: 'success'
})
},
searchcondition() {
const data = { const data = {
type: 'dictionaries', type: 'dictionaries',
table_name: 'zs_operator_plan', table_name: 'zs_operator_plan',
......
...@@ -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%;
......
<!--
* @Author: maoxiya 937667504@qq.com
* @Date: 2025-10-31 15:00:00
* @LastEditors: maoxiya 937667504@qq.com
* @LastEditTime: 2025-10-31 17:18:17
* @FilePath: /company_wx_frontend/src/components/common/game/benefitUsageRecord.vue
* @Description: 权益使用记录组件
-->
<template>
<div class="benefit-usage-record">
<!-- 搜索区域 -->
<div class="search-content">
<el-form label-position="top" :model="formData" class="search-form">
<el-form-item label="权益">
<el-select
v-model="formData.type"
clearable
placeholder="请选择权益"
@change="handleTypeChange"
style="width:100%;"
>
<el-option
v-for="item in benefitOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="时间范围">
<el-date-picker
v-model="formData.dateRange"
type="datetimerange"
format="yyyy-MM-dd HH:mm:ss"
value-format="yyyy-MM-dd HH:mm:ss"
range-separator="至"
start-placeholder="开始时间"
end-placeholder="结束时间"
clearable
:default-time="['00:00:00', '23:59:59']"
style="width:100%;"
/>
</el-form-item>
<el-form-item>
<el-button
type="primary"
:loading="loading"
@click="startSearch"
>
搜索
</el-button>
<el-button @click="resetForm">重置</el-button>
</el-form-item>
</el-form>
</div>
<!-- 表格区域 -->
<div class="table-container">
<el-table
v-loading="loading"
:data="tableData"
style="width: 100%"
:header-cell-style="{ background: '#f5f7fa', color: '#606266' }"
>
<el-table-column
label="角色名称/cp角色Id"
min-width="200"
>
<template slot-scope="scope">
<p>{{ scope.row.role_name }}</p>
<p class="infoText">{{ scope.row.cp_role_id }}</p>
</template>
</el-table-column>
<el-table-column
label="权益"
prop="type_name"
min-width="150"
/>
<el-table-column
label="使用人"
prop="create_user"
min-width="120"
/>
<el-table-column
label="使用时间"
prop="create_time"
min-width="180"
/>
</el-table>
<!-- 分页 -->
<div class="pagination-container">
<el-pagination
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
:current-page="page_info.page"
:page-sizes="[10, 20, 50, 100]"
:page-size="page_info.page_size"
layout="total, sizes, prev, pager, next, jumper"
:total="page_info.total"
/>
</div>
</div>
</div>
</template>
<script>
import { useRightList,selectSearch } from '@/api/game'
export default {
name: 'BenefitUsageRecord',
props: {
benefitItem: {
type: Object,
default: () => ({})
},
roleInfo: {
type: Object,
default: () => ({})
}
},
data() {
return {
loading: false,
formData: {
type: '',
dateRange: []
},
benefitOptions: [],
tableData: [],
tableColums: [
{
label: '角色名称/cp角色Id',
prop: 'role_info',
minWidth: 200,
slotScope: true
},
{
label: '权益',
prop: 'type_name',
minWidth: 150
},
{
label: '使用人',
prop: 'create_user',
minWidth: 120,
},
{
label: '使用时间',
prop: 'create_time',
minWidth: 180
}
],
page_info: {
page: 1,
page_size: 20,
total: 0
}
}
},
mounted() {
this.getBenefitOptions()
this.getTableData()
},
methods: {
/**
* 获取权益下拉选项
*/
async getBenefitOptions() {
try {
const data = {
type: 'svip_right',
}
const res = await selectSearch(data)
if (res.status_code === 1 && res.data && res.data.data && res.data.data.length > 0) {
this.benefitOptions = res.data.data
}
} catch (error) {
console.error('获取权益选项失败:', error)
}
},
/**
* 获取表格数据
*/
async getTableData() {
try {
this.loading = true
const requestData = {
cp_role_id: this.roleInfo.cp_role_id,
type: this.formData.type,
create_time_start: this.formData.dateRange && this.formData.dateRange.length > 0 ? this.formData.dateRange[0] : '',
create_time_end: this.formData.dateRange && this.formData.dateRange.length > 0 ? this.formData.dateRange[1] : '',
page: this.page_info.page,
page_size: this.page_info.page_size
}
const res = await useRightList(requestData)
if (res.status_code === 1) {
this.tableData = res.data.data || []
this.page_info.total = Number(res.data.page_info.total) || 0
} else {
this.$message({
message: res.data.msg || '获取数据失败',
type: 'error'
})
}
} catch (error) {
console.error('获取权益使用记录失败:', error)
this.$message({
message: '获取数据失败,请重试',
type: 'error'
})
} finally {
this.loading = false
}
},
/**
* 处理页码变化
*/
handleCurrentChange(currentPage) {
this.page_info.page = currentPage
this.getTableData()
},
/**
* 处理每页条数变化
*/
handleSizeChange(pageSize) {
this.page_info.page_size = pageSize
this.page_info.page = 1
this.getTableData()
},
/**
* 处理权益类型变化
*/
handleTypeChange() {
this.page_info.page = 1
this.getTableData()
},
/**
* 开始搜索
*/
startSearch() {
this.page_info.page = 1
this.getTableData()
},
/**
* 重置表单
*/
resetForm() {
this.formData = {
type: '',
dateRange: []
}
this.startSearch()
}
}
}
</script>
<style lang="scss" scoped>
.benefit-usage-record {
::v-deep .el-table__header-wrapper{
position: static;
top: 0;
}
.search-content {
padding: 20px;
background: #fff;
border-radius: 4px;
max-height: 80%;
overflow: auto;
.search-form {
.el-form-item {
margin-bottom: 16px;
&:last-child {
margin-bottom: 0;
}
}
}
}
.table-container {
background: #fff;
border-radius: 4px;
padding: 20px;
}
.pagination-container {
margin-top: 20px;
text-align: right;
}
.infoText {
color: #c9cdd4;
font-size: 12px;
margin-top: 4px;
}
}
</style>
<!-- <!--
* @Author: maoxiya 937667504@qq.com * @Author: maoxiya 937667504@qq.com
* @Date: 2025-09-13 14:05:01 * @Date: 2025-09-13 14:05:01
* @LastEditors: maoxiya 937667504@qq.com * @LastEditors: 金多虾 937667504@qq.com
* @LastEditTime: 2025-09-22 17:24:19 * @LastEditTime: 2025-12-05 17:03:24
* @FilePath: /company_wx_frontend/src/views/works/component/gameInfo/vipLevel.vue * @FilePath: /company_wx_frontend/src/views/works/component/gameInfo/vipLevel.vue
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
--> -->
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
:disabled="loading" :disabled="loading"
> >
<!-- 最大宽度 400px --> <!-- 最大宽度 400px -->
<div class="vipLevelContent" style="max-width: 400px;max-height: 300px;overflow: auto;"> <div class="vipLevelContent" style="min-width:250px;">
<div v-if="loading" class="loading-content"> <div v-if="loading" class="loading-content">
<i class="el-icon-loading"></i> <i class="el-icon-loading"></i>
<span>加载中...</span> <span>加载中...</span>
...@@ -25,7 +25,22 @@ ...@@ -25,7 +25,22 @@
<div class="vipLevelItem rowFlex columnCenter" v-for="(item,index) in vipLevelBenefit" :key="index"> <div class="vipLevelItem rowFlex columnCenter" v-for="(item,index) in vipLevelBenefit" :key="index">
<p class="vipLevelItemRow" v-if="item.num" :style="{color: item.target ? '#333333' : '#c9cdd4'}" > <p class="vipLevelItemRow" v-if="item.num" :style="{color: item.target ? '#333333' : '#c9cdd4'}" >
<span v-if="item.name" class="label">{{item.name}} </span> <span v-if="item.name" class="label">{{item.name}} </span>
<span v-if="item.num" :style="{color: item.target ? '#00bf8a' : '#c9cdd4'}" class="value"> {{item.num }}</span> <span v-if="item.num && item.type!=8" :style="{color: item.target ? '#00bf8a' : '#c9cdd4'}" class="value">
{{item.remain_num || item.num }}/{{item.num}} {{item.unit || '次'}}
</span>
<!-- 人工权益显示使用按钮 -->
<div v-if="item.monitor_type === 2" class="benefit-actions">
<el-button
type="primary"
size="mini"
style="margin-left: 10px;"
:disabled="!item.target"
@click.stop="handleUseBenefit(item)"
class="use-button"
>
使用
</el-button>
</div>
</p> </p>
</div> </div>
<div v-if="vipLevelBenefit.length === 0" class="no-data"> <div v-if="vipLevelBenefit.length === 0" class="no-data">
...@@ -37,11 +52,62 @@ ...@@ -37,11 +52,62 @@
<span class="vipLevelText" v-if="vip_role_info.vip_level" @click.stop="showVipLevel">{{ `SVIP等级${vip_role_info.vip_level}` }}</span> <span class="vipLevelText" v-if="vip_role_info.vip_level" @click.stop="showVipLevel">{{ `SVIP等级${vip_role_info.vip_level}` }}</span>
</div> </div>
</el-popover> </el-popover>
<!-- 使用权益确认弹窗 -->
<el-dialog
v-if="useBenefitDialogVisible"
title="确认提示"
:visible.sync="useBenefitDialogVisible"
width="400px"
:before-close="handleDialogClose"
>
<div class="dialog-content">
<div class="rowFlex columnCenter">
<i class="el-icon-warning" style="color: #E6A23C; font-size: 24px; margin-right: 10px;"></i>
<span>是否确认使用{{ currentBenefitItem?.name }}权益?</span>
</div>
<p class="text-center" style="color: #999; font-size: 14px; margin-top: 10px;">点击确定后,权益剩余使用数量将减 1</p>
</div>
<div slot="footer" >
<el-button
type="text"
@click="showUsageRecordFromDialog"
class="record-button"
>
使用记录
</el-button>
<el-button @click="handleDialogClose">取消</el-button>
<el-button
type="primary"
:loading="loading"
@click="confirmUseBenefit"
>
确认
</el-button>
</div>
</el-dialog>
<!-- 使用记录弹窗 -->
<el-dialog
title="权益使用记录"
:visible.sync="usageRecordVisible"
width="80%"
:before-close="closeUsageRecord"
append-to-body
@close="closeUsageRecord"
>
<BenefitUsageRecord
:benefit-item="currentBenefitItem"
:role-info="vip_role_info"
/>
</el-dialog>
</div> </div>
</template> </template>
<script> <script>
import { marketingRoleGradeBenefit,marketingMemberRoleGrade } from '@/api/game' import { marketingRoleGradeBenefit,marketingMemberRoleGrade,useBenefit } from '@/api/game'
import { mapState, mapMutations } from 'vuex' import { mapState, mapMutations } from 'vuex'
import BenefitUsageRecord from './benefitusageRecord.vue'
export default { export default {
name: 'VipLevel', name: 'VipLevel',
data() { data() {
...@@ -49,9 +115,15 @@ export default { ...@@ -49,9 +115,15 @@ export default {
vip_role_info: {}, vip_role_info: {},
vipLevelBenefit: [], vipLevelBenefit: [],
popoverVisible: false, popoverVisible: false,
loading: false loading: false,
usageRecordVisible: false,
currentBenefitItem: null,
useBenefitDialogVisible: false
} }
}, },
components: {
BenefitUsageRecord
},
props: { props: {
roleInfo: { roleInfo: {
type: Object, type: Object,
...@@ -73,7 +145,8 @@ export default { ...@@ -73,7 +145,8 @@ export default {
} }
}, },
computed: { computed: {
...mapState('game', ['accountSelect']) ...mapState('game', ['accountSelect']),
...mapState('user', ['userInfo']),
}, },
methods: { methods: {
async marketingMemberRoleGrade() { async marketingMemberRoleGrade() {
...@@ -134,7 +207,80 @@ export default { ...@@ -134,7 +207,80 @@ export default {
// 获取数据并显示popover // 获取数据并显示popover
await this.getVipLevel(); await this.getVipLevel();
},
/**
* 处理使用权益
*/
handleUseBenefit(item) {
this.currentBenefitItem = item;
this.useBenefitDialogVisible = true;
},
/**
* 确认使用权益
*/
async confirmUseBenefit() {
try {
this.loading = true;
const requestData = {
role_id: this.vip_role_info.role_id,
grade_config_id: this.currentBenefitItem.grade_config_id,
user_name:this.userInfo.username
};
const res = await useBenefit(requestData);
if (res.status_code === 1) {
this.$message({
message: res.data.msg || '使用成功',
type: 'success'
});
// 直接更新本地权益数据
const benefitIndex = this.vipLevelBenefit.findIndex(item =>
item.grade_config_id === this.currentBenefitItem.grade_config_id
);
if (benefitIndex !== -1) {
this.vipLevelBenefit[benefitIndex].remain_num -= 1;
}
this.useBenefitDialogVisible = false;
this.currentBenefitItem = null;
} else {
this.$message({
message: res.data.msg || '使用失败',
type: 'error'
});
}
} catch (error) {
console.error('使用权益失败:', error);
} finally {
this.loading = false;
}
},
/**
* 处理dialog关闭
*/
handleDialogClose() {
this.useBenefitDialogVisible = false;
this.currentBenefitItem = null;
},
/**
* 从dialog中显示使用记录弹窗
*/
showUsageRecordFromDialog() {
this.usageRecordVisible = true;
this.useBenefitDialogVisible = false;
},
/**
* 关闭使用记录弹窗
*/
closeUsageRecord() {
this.usageRecordVisible = false;
this.currentBenefitItem = null;
} }
} }
} }
</script> </script>
...@@ -185,8 +331,54 @@ export default { ...@@ -185,8 +331,54 @@ export default {
.value { .value {
font-weight: bold; font-weight: bold;
} }
.benefit-actions {
display: flex;
align-items: center;
gap: 8px;
margin-left: auto;
.use-button {
margin-left: 10px;
}
.record-button {
font-size: 12px;
padding: 0;
}
}
}
}
}
.dialog-content {
font-size: 16px;
color: #333;
}
.dialog-footer {
display: flex;
justify-content: flex-end;
align-items: center;
.record-button {
margin-left: 10px;
color: #409EFF;
cursor: pointer;
&:hover {
color: #66b1ff;
} }
} }
} }
// 使用记录弹窗样式
.el-dialog {
::v-deep .el-dialog__body {
padding: 0;
max-height: 70vh;
overflow-y: auto;
}
}
} }
</style> </style>
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论