提交 9e546236 作者: 毛细亚

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

Shw feat 7.1

查看合并请求 !38
<!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>
......
......@@ -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) {
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';
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>
......@@ -8,22 +8,22 @@
>
<div>
<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-option
v-for="(item,index) in roleList"
:key="index+1"
: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-select v-model="ruleForm.gift_type" placeholder="请选择" style="width:90%;margin-bottom:10px;" @change="giftTypeResult">
<el-option
v-for="(item,index) in giftTypeList"
:key="index+1"
:key="item.value"
:label="item.label"
:value="item.value"
>
......@@ -31,11 +31,11 @@
</el-select>
</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-option
v-for="(item,index) in activeList"
:key="index+1"
:key="item.id"
:label="item.title_name"
:value="item.id"
>
......@@ -56,16 +56,16 @@
</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" />
<!-- 单日累充 积分开的情况 -->
<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" />
<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 || 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>
</el-drawer>
</template>
<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 giftDetails from './giftDetails.vue'
import roleRecharge from './giftDetails/roleRecharge.vue'
......@@ -79,8 +79,8 @@
oneDayCharge, // 单日累充
roleTimeRecharge // 角色时间段累充
},
// type 3:image 4:video
props: ['show', 'width', 'title'],
// type 3:image 4:video task_id // 账号任务点击礼包申请过来传递的任务id member_id // 我的任务 任务详情 点击 礼包申请的时候 需要用到任务详情的 member_id,这时候需要传递过来
props: ['show', 'width', 'title', 'task_id','member_id'],
data() {
return {
roleList: [],
......@@ -124,6 +124,15 @@
this.requestRoleList()
this.giftTypeListData()
},
beforeDestroy() {
// 清理组件销毁时的状态,防止内存泄漏
this.roleList = []
this.activeList = []
this.giftTypeList = []
this.roleActiveInfo = []
this.activeInfo = {}
this.giftDetailsInfo = {}
},
methods: {
requestActiveList() {
if (this.ruleForm.role_id !== '' && this.ruleForm.gift_type !== '') {
......@@ -156,15 +165,18 @@
})
},
requestRoleList() {
if (this.accountSelect == '') {
if (this.accountSelect == '' && !this.member_id) {
this.$message.warning('暂无关联的账号,请先去关联账号!')
return false
}
const data = {
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.data.data.length > 0) {
const list = res.data.data.sort((a, b) => { return Number(b.recharge_total) - Number(a.recharge_total) })
......@@ -197,7 +209,7 @@
this.activeList = []
const gift_type = this.giftTypeList.find(item => item.value == data)
console.log(gift_type, 'gift_type')
this.gift_type_text = gift_type.label
this.gift_type_text = gift_type.label || ''
this.requestActiveList()
this.activeInfo = {}
this.giftInfo = {}
......@@ -246,7 +258,7 @@
this.showGiftDetails = true
} else {
this.ruleForm.recharge_date = ''
if (this.ruleForm.gift_type == 2) {
if (this.ruleForm.gift_type == 2 || this.ruleForm.gift_type == 7) {
this.requestGiftDetails()
this.showGiftDetails = true
}
......@@ -274,6 +286,21 @@
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) {
const data = {
role_id: this.ruleForm.role_id,
......
......@@ -55,7 +55,7 @@
</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" @click="close">取 消</el-button>
</span>
......@@ -68,7 +68,7 @@
export default {
name: 'confirmLayer',
// type 3:image 4:video
props: ['show', 'width', 'title', 'activeInfo', 'remark'],
props: ['show', 'width', 'title', 'activeInfo', 'remark','task_id','svipObj'],
data() {
return {
loading: false
......@@ -104,8 +104,11 @@
role_id,
remark: this.remark,
recharge_date: this.activeInfo[0].recharge_date || '',
task_id: this.task_id || null,
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 => {
this.loading = false
......
......@@ -28,6 +28,13 @@
</el-option>
</el-select>
</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-input v-model="form.role_name_or_cp_id" placeholder="请输入角色名称" style="width:100%;"
@change="searchInput"></el-input>
......@@ -59,6 +66,7 @@
</div>
<el-collapse-transition>
<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
}}</span> </p>
<p v-else-if="item.status == '已提交'" class="text"><label>状态:</label> <span class="sended">{{
......@@ -165,8 +173,10 @@ export default {
role_name_or_cp_id: '',
member_id: '',
active_title: '',
gift_type: ''
gift_type: '',
right_type: '',
},
benefitOptions: [],
inputValue: '',
pageInfo: {
page: 0,
......@@ -198,6 +208,7 @@ export default {
mounted() {
this.requestGameList()
this.requestGiftType()
this.getBenefitOptions()
},
methods: {
// 重新拉去数据
......@@ -286,6 +297,27 @@ export default {
this.emailGiftList = []
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() {
this.listLoading = true
if (this.accountSelect == '') {
......
......@@ -24,9 +24,23 @@
<div class="activeValue">{{ activeInfo.recharge_start_date + '至' + activeInfo.recharge_end_date }}</div>
</div>
<!-- 选择的时候重新请求活动详情接口 -->
<div class="activeItem rowFlex">
<div class="activeLabel">充值日期</div>
<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>
<div style="display: flex; align-items: center;width: 100%;">
<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 class="activeItem rowFlex">
<div class="activeLabel">申请角色</div>
......@@ -127,13 +141,13 @@
<el-button class="btn" size="small" @click="close">取 消</el-button>
</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>
</el-drawer>
</template>
<script type="text/javascript">
import { giftBagApply } from '@/api/game'
import { giftBagApply,selectSearch } from '@/api/game'
import { mapState, mapMutations, mapActions } from 'vuex'
import confirmLayer from '../confirmLayer'
export default {
......@@ -142,7 +156,7 @@ export default {
components: {
confirmLayer
},
props: ['show', 'width', 'title', 'info', 'body', 'giftInfo', 'activeInfo', 'changeDate','requestLoading'],
props: ['show', 'width', 'title', 'info', 'body', 'giftInfo', 'activeInfo', 'changeDate','requestLoading','task_id'],
data() {
return {
remark: '',
......@@ -155,7 +169,16 @@ export default {
num: '',
allpoints: 0,
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 {
},
mounted() {
this.recharge_date_time = this.activeInfo.recharge_date
this.getBenefitOptions()
},
computed: {
// 根据选择类型显示对应的内容
showDatePicker() {
return this.select_type === 1
},
showBenefitSelect() {
return this.select_type === 2
}
},
methods: {
close() {
this.$emit('update:show', false)
......@@ -187,6 +221,50 @@ export default {
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) {
console.log(item, index, value)
this.$set(this.giftInfo.rule.level_attribute[index], 'showContent', value)
......@@ -242,6 +320,8 @@ export default {
}
this.showConfirmLayer = true
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 {
left: 50%;
transform: translateX(-50%);
bottom: 100px;
z-index: 100;
}
.consumption-point{
font-size: 14px;
......
......@@ -57,7 +57,7 @@
<el-button size="small" class="btn" @click="close">取 消</el-button>
</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>
</el-drawer>
</template>
......@@ -72,7 +72,7 @@ export default {
components: {
confirmLayer
},
props: ['show', 'width', 'title', 'info', 'body', 'giftInfo', 'activeInfo','requestLoading'],
props: ['show', 'width', 'title', 'info', 'body', 'giftInfo', 'activeInfo','requestLoading','task_id'],
data() {
return {
isSubmit: false,
......@@ -121,6 +121,7 @@ export default {
role_id,
remark: this.remark,
recharge_date: this.activeInfo[0].recharge_date || '',
task_id: this.task_id || null,
create_user: this.cser_name,
rule: rule
}
......
......@@ -64,8 +64,30 @@
class="activeValue"
>{{ activeInfo.role_info.label }}</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
v-if="activeInfo.gift_type == 2"
class="activeItem rowFlex"
......@@ -234,7 +256,7 @@
type="primary"
size="small"
:disabled="requestLoading"
@click="submit"
@click="submitBtn"
:loading="btnLoading"
>确 定</el-button>
<el-button
......@@ -247,6 +269,7 @@
<confirmLayer
v-if="showConfirmLayer"
:remark="remark"
:task_id="task_id"
:active-info="[activeInfo]"
:show.sync="showConfirmLayer"
title="请核对申请奖品信息"
......@@ -266,7 +289,7 @@ export default {
components: {
confirmLayer
},
props: ['show', 'width', 'title', 'info', 'body', 'giftInfo', 'activeInfo', 'changeDate','requestLoading'],
props: ['show', 'width', 'title', 'info', 'body', 'giftInfo', 'activeInfo', 'changeDate','requestLoading','task_id'],
data() {
return {
remark: '',
......@@ -285,6 +308,9 @@ export default {
showConfirmLayer: false
}
},
computed: {
...mapState('user', ['cser_name'])
},
watch: {
show(newVal, oldVal) {
if (newVal) {
......@@ -385,7 +411,58 @@ export default {
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
const data = {
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 @@
</div>
</div>
<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>
<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>
</div>
</el-collapse-item>
......@@ -92,17 +94,28 @@
</div>
</div>
</div>
<!-- 礼包申请弹窗 -->
<applyGift
v-if="showApplyGift"
:show.sync="showApplyGift"
title="礼包申请"
width="25%"
:task_id="task_id"
@requestData="handleGiftApplySuccess"
/>
</div>
</template>
<script>
import { taskTrack, taskRecord, logsSave, searchcondition } from '@/api/game'
import { mapState } from 'vuex'
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 {
components: {
textEditor,
noContent,
applyGift
},
data() {
return {
......@@ -131,6 +144,8 @@
showLayer: false,
taskLoading: false,
remarkLoading: false,
showApplyGift: false,
task_id: null,
pageInfo: {
page: 0,
page_size: 20,
......@@ -189,8 +204,26 @@
this.searchTrackList()
this.searchconditionError()
},
methods: {
searchcondition() {
methods: {
/**
* 显示礼包申请弹窗
*/
giftApply(item) {
this.task_id = item.id || null
this.showApplyGift = true
},
/**
* 处理礼包申请完成后的回调
* 关闭弹窗并显示成功提示
*/
handleGiftApplySuccess() {
this.showApplyGift = false
this.$message({
message: '礼包申请已提交',
type: 'success'
})
},
searchcondition() {
const data = {
type: 'dictionaries',
table_name: 'zs_operator_plan',
......
......@@ -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%;
......
<!--
* @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
* @Date: 2025-09-13 14:05:01
* @LastEditors: maoxiya 937667504@qq.com
* @LastEditTime: 2025-09-22 17:24:19
* @LastEditors: 金多虾 937667504@qq.com
* @LastEditTime: 2025-12-05 17:03:24
* @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
-->
......@@ -16,7 +16,7 @@
:disabled="loading"
>
<!-- 最大宽度 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">
<i class="el-icon-loading"></i>
<span>加载中...</span>
......@@ -25,7 +25,22 @@
<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'}" >
<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>
</div>
<div v-if="vipLevelBenefit.length === 0" class="no-data">
......@@ -37,11 +52,62 @@
<span class="vipLevelText" v-if="vip_role_info.vip_level" @click.stop="showVipLevel">{{ `SVIP等级${vip_role_info.vip_level}` }}</span>
</div>
</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>
</template>
<script>
import { marketingRoleGradeBenefit,marketingMemberRoleGrade } from '@/api/game'
import { marketingRoleGradeBenefit,marketingMemberRoleGrade,useBenefit } from '@/api/game'
import { mapState, mapMutations } from 'vuex'
import BenefitUsageRecord from './benefitusageRecord.vue'
export default {
name: 'VipLevel',
data() {
......@@ -49,9 +115,15 @@ export default {
vip_role_info: {},
vipLevelBenefit: [],
popoverVisible: false,
loading: false
loading: false,
usageRecordVisible: false,
currentBenefitItem: null,
useBenefitDialogVisible: false
}
},
components: {
BenefitUsageRecord
},
props: {
roleInfo: {
type: Object,
......@@ -73,7 +145,8 @@ export default {
}
},
computed: {
...mapState('game', ['accountSelect'])
...mapState('game', ['accountSelect']),
...mapState('user', ['userInfo']),
},
methods: {
async marketingMemberRoleGrade() {
......@@ -134,7 +207,80 @@ export default {
// 获取数据并显示popover
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>
......@@ -185,8 +331,54 @@ export default {
.value {
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>
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论