提交 4907f587 作者: 毛细亚

准备更新 7.2 版本

上级 7f2fd673
...@@ -1508,4 +1508,62 @@ export function teachingVideoVideoListApi(data) { ...@@ -1508,4 +1508,62 @@ export function teachingVideoVideoListApi(data) {
reject(error) reject(error)
}) })
}) })
} }
\ No newline at end of file // 发送分身包
export function memberRegGameCloneLink(data) {
return new Promise((resolve, reject) => {
cross_systemRequest({
system: 'zhangyou',
api: '/api/member/memberRegGameCloneLink',
params: data
}).then((res) => {
resolve(res)
}).catch((error) => {
reject(error)
})
})
}
// 带教记录列表
export function roleTeachingList(data) {
return new Promise((resolve, reject) => {
cross_systemRequest({
system: 'zhangyou',
api: '/api/role/roleTeachingList',
params: data
}).then((res) => {
resolve(res)
}).catch((error) => {
reject(error)
})
})
}
// 新增带教
export function roleTeachingAdd(data) {
return new Promise((resolve, reject) => {
cross_systemRequest({
system: 'zhangyou',
api: '/api/role/roleTeachingAdd',
params: data
}).then((res) => {
resolve(res)
}).catch((error) => {
reject(error)
})
})
}
// 获取带教次数
export function roleTeachingNum(data) {
return new Promise((resolve, reject) => {
cross_systemRequest({
system: 'zhangyou',
api: '/api/role/roleTeachingNum',
params: data
}).then((res) => {
resolve(res)
}).catch((error) => {
reject(error)
})
})
}
...@@ -97,6 +97,8 @@ ...@@ -97,6 +97,8 @@
> >
下载二维码 下载二维码
</p> </p>
<!-- h5 安卓游戏 IOS游戏 发送分身包 -->
<p v-if="[2,3,4].includes(item.game_type)" class="sendLink" @click="sendTransferCloneGame(item.game_type)">发送分身包</p>
</div> </div>
<el-button <el-button
slot="reference" slot="reference"
...@@ -426,6 +428,7 @@ import { ...@@ -426,6 +428,7 @@ import {
getClonePackageLink, getClonePackageLink,
getLandingPageConfig, getLandingPageConfig,
getMemberTransStatus, getMemberTransStatus,
memberRegGameCloneLink
} from "@/api/game"; } from "@/api/game";
import { import {
getRecentSendLog, getRecentSendLog,
...@@ -1135,6 +1138,54 @@ export default { ...@@ -1135,6 +1138,54 @@ export default {
); );
this.showSendPage = true; this.showSendPage = true;
}, 500), }, 500),
// 转端发送游戏分身包 h5 安卓游戏 IOS游戏 发送分身包
async sendTransferCloneGame(type) {
const res = await memberRegGameCloneLink({ member_id: this.accountSelect })
if (res.status_code == 1) {
// 通过 type 判断 用 switch
switch (type) {
case 2:
if (!res?.data?.data?.h5_download_url) {
this.$message.warning('H5安卓分身包链接不存在,请联系掌游配置')
return
}
break
case 3:
if (!res?.data?.data?.android_download_url) {
this.$message.warning('安卓分身包链接不存在,请联系掌游配置')
return
}
break
case 4:
if (!res?.data?.data?.ios_download_url) {
this.$message.warning('IOS分身包链接不存在,请联系掌游配置')
return
}
break
default:
this.$message.warning('不支持的游戏类型')
return
}
let srt = ''
switch (type) {
case 'android':
srt = '安卓分身包链接: ' + res.data.data.android_download_url
break
case 'ios':
srt = 'IOS分身包链接: ' + res.data.data.ios_download_url
break
}
const list = [
{
msgtype: 'text',
text: { content: srt }
}
]
this.set_sendSkillMessage(list)
} else {
this.$message.warning(res.msg)
}
},
// 转端发送游戏二维码 // 转端发送游戏二维码
sendDownLoadQrCode: throttleStart(async function (items, type, index) { sendDownLoadQrCode: throttleStart(async function (items, type, index) {
if (!this.transMemberStatus) { if (!this.transMemberStatus) {
......
...@@ -321,11 +321,32 @@ ...@@ -321,11 +321,32 @@
} }
const res = await getLandingPageMemberLink(params) const res = await getLandingPageMemberLink(params)
if (res && res.data.data) { if (res && res.data.data) {
this.loading = false const responseData = res.data.data
this.close()
this.$emit('confirm', res.data.data) // 适配新接口结构:如果有 background_imgs 数组,转换为旧结构格式
this.$message.success('发送成功') let finalData = responseData
if (responseData.background_imgs && responseData.background_imgs.length > 0) {
// 新结构:使用第一个背景图的信息
const firstBg = responseData.background_imgs[0]
finalData = {
channel_qrcode: responseData.channel_qrcode || '',
background_img: firstBg.background_img || '',
'x-coordinate': firstBg['x-coordinate'] || 0,
'y-coordinate': firstBg['y-coordinate'] || 0,
background_imgs: responseData.background_imgs // 保留原始数据
}
} else if (!responseData.background_img) {
// 如果没有 background_img 也没有 background_imgs,保持原数据结构
finalData = responseData
} }
this.loading = false
this.close()
this.$emit('confirm', res.data.data)
this.$emit('confirm', finalData)
this.$message.success('发送成功')
}
} catch (error) { } catch (error) {
this.loading = false this.loading = false
console.error('获取链接失败:', error) console.error('获取链接失败:', error)
......
...@@ -3,7 +3,8 @@ ...@@ -3,7 +3,8 @@
<el-drawer <el-drawer
title="选择游戏" title="选择游戏"
:visible="show" :visible="show"
size="360px" size="400px"
append-to-body
@close="close" @close="close"
> >
<el-form ref="wxGameForm" :model="wxGameForm" :rules="wxGameRules" label-position="top" class="game-select-container"> <el-form ref="wxGameForm" :model="wxGameForm" :rules="wxGameRules" label-position="top" class="game-select-container">
...@@ -33,6 +34,7 @@ ...@@ -33,6 +34,7 @@
placeholder="请选择渠道" placeholder="请选择渠道"
style="width: 100%" style="width: 100%"
:clearable="true" :clearable="true"
@change="handleChannelChange"
> >
<el-option <el-option
v-for="(item,index) in wxGameChannelList" v-for="(item,index) in wxGameChannelList"
...@@ -43,6 +45,49 @@ ...@@ -43,6 +45,49 @@
</el-option> </el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<!-- 选择背景图 新增背景图的选项 -->
<el-form-item v-if="backgroundImgsList.length > 0" label="">
<template slot="label">
<p class="formLabel">
<span class="required-mark">*</span>
<span>背景图</span>
</p>
</template>
<el-radio-group v-model="wxGameForm.selectedBackgroundIndex" @change="handleBackgroundChange">
<div class="background-options">
<el-radio
v-for="(item, index) in backgroundImgsList"
:key="index"
:label="index"
class="background-radio-item"
>
<div class="background-card">
<div class="background-preview-wrapper">
<img
:ref="`bgImg_${index}`"
:src="item.background_img"
class="background-preview"
alt="背景图预览"
@load="handleBackgroundImageLoad(index, item)"
>
<!-- 二维码叠加显示 -->
<img
v-if="channelQrcode && item.qrStyle"
:src="channelQrcode"
class="background-qrcode"
:style="item.qrStyle"
alt="二维码"
>
</div>
<!-- 左上角小眼睛图标 -->
<div class="eye-icon" @click.stop="showPreview(item, index)">
<i class="el-icon-view"></i>
</div>
</div>
</el-radio>
</div>
</el-radio-group>
</el-form-item>
</el-form> </el-form>
<span class="dialog-footer rowFlex"> <span class="dialog-footer rowFlex">
<el-button class="btn" @click="close" size="small">取消</el-button> <el-button class="btn" @click="close" size="small">取消</el-button>
...@@ -75,6 +120,38 @@ ...@@ -75,6 +120,38 @@
> >
</div> </div>
</div> </div>
<!-- 大图预览弹窗 -->
<el-drawer
title="预览"
:visible.sync="previewVisible"
size="500px"
:close-on-click-modal="false"
append-to-body
class="preview-dialog"
>
<div class="preview-content">
<div class="preview-wrapper" v-if="previewImageInfo">
<img
ref="previewBgImgDisplay"
:src="previewImageInfo.background_img"
class="preview-background"
alt="背景图"
@load="handlePreviewImageLoad"
>
<img
v-if="previewImageInfo.channel_qrcode"
ref="previewQrImgDisplay"
:src="previewImageInfo.channel_qrcode"
class="preview-qrcode"
:style="previewQrcodeStyle"
alt="二维码"
>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="previewVisible = false">关闭</el-button>
</span>
</el-drawer>
</el-drawer> </el-drawer>
</template> </template>
...@@ -119,11 +196,17 @@ ...@@ -119,11 +196,17 @@
wxGameForm: { wxGameForm: {
wx_game_send_id: '', wx_game_send_id: '',
wx_game_send_info: {}, wx_game_send_info: {},
wx_game_channel: '' wx_game_channel: '',
selectedBackgroundIndex: 0 // 选中的背景图索引
}, },
messageInfo: null, messageInfo: null,
handleConfirmWithDebounce: {}, handleConfirmWithDebounce: {},
wxGameChannelList: [], wxGameChannelList: [],
backgroundImgsList: [], // 背景图列表
channelQrcode: '', // 二维码链接
previewVisible: false, // 预览弹窗显示状态
previewImageInfo: null, // 预览图片信息(包含背景图和二维码)
previewQrcodeStyle: {}, // 预览二维码样式
wxGameRules: { wxGameRules: {
wx_game_send_id: [ wx_game_send_id: [
{ required: true, message: '请选择微信小游戏', trigger: 'change' } { required: true, message: '请选择微信小游戏', trigger: 'change' }
...@@ -150,6 +233,8 @@ ...@@ -150,6 +233,8 @@
wx_game_channel: '' wx_game_channel: ''
} }
this.wxGameChannelList = [] this.wxGameChannelList = []
this.backgroundImgsList = []
this.channelQrcode = ''
this.closeMessage() this.closeMessage()
this.loading = false this.loading = false
// 清理图片资源 // 清理图片资源
...@@ -320,6 +405,19 @@ ...@@ -320,6 +405,19 @@
} }
}) })
}, },
// 处理渠道选择变化
async handleChannelChange(value) {
if (!value) {
// 清空渠道时,清空背景图列表
this.backgroundImgsList = []
this.channelQrcode = ''
this.wxGameForm.selectedBackgroundIndex = 0
this.imageInfo = {}
return
}
// 选择渠道后自动请求接口
await this.handleWxGameChannel()
},
async uploadCos(File) { async uploadCos(File) {
const uploadConfig = { const uploadConfig = {
dir: '/company_wx/service/avatars/' dir: '/company_wx/service/avatars/'
...@@ -336,7 +434,22 @@ ...@@ -336,7 +434,22 @@
} }
}, },
handleConfirm() { handleConfirm() {
this.handleWxGameChannel() // 检查是否选择了渠道
if (!this.wxGameForm.wx_game_channel) {
this.$message.warning('请选择渠道')
return
}
// 如果有背景图列表,直接合成图片
if (this.backgroundImgsList.length > 0) {
this.confirmAndCompose()
} else {
// 如果没有背景图列表,说明接口返回的是旧格式,直接合成
if (this.imageInfo.background_img) {
this.confirmAndCompose()
} else {
this.$message.warning('请先选择渠道')
}
}
}, },
// 保存微信小游戏的信息 // 保存微信小游戏的信息
...@@ -387,11 +500,37 @@ ...@@ -387,11 +500,37 @@
const linkRes = await getLandingPageMemberLink(params) const linkRes = await getLandingPageMemberLink(params)
console.log('获取到的落地页数据:', linkRes) console.log('获取到的落地页数据:', linkRes)
if (linkRes?.data?.data) { if (linkRes?.data?.data) {
// 检查返回的数据结构 const responseData = linkRes.data.data
console.log('设置前的imageInfo:', this.imageInfo) // 保存二维码链接
this.imageInfo = linkRes.data.data this.channelQrcode = responseData.channel_qrcode || ''
console.log('设置后的imageInfo:', this.imageInfo)
await this.composeImage() // 处理背景图列表
if (responseData.background_imgs && responseData.background_imgs.length > 0) {
// 如果有背景图列表,使用列表中的图片
// 初始化二维码样式
this.backgroundImgsList = responseData.background_imgs.map(item => ({
...item,
qrStyle: null // 等待图片加载后计算
}))
// 默认选中第一个
this.wxGameForm.selectedBackgroundIndex = 0
// 设置默认的 imageInfo
this.updateImageInfo(0)
// 不立即合成,等待用户选择后点击确认
this.loading = false
return
} else {
// 如果没有背景图列表,使用默认背景图(兼容旧逻辑)
this.backgroundImgsList = []
this.imageInfo = {
background_img: responseData.background_img || '',
channel_qrcode: this.channelQrcode,
'x-coordinate': responseData['x-coordinate'] || 0,
'y-coordinate': responseData['y-coordinate'] || 0
}
// 不立即合成,等待用户点击确认
this.loading = false
}
} else { } else {
throw new Error('获取落地页数据失败') throw new Error('获取落地页数据失败')
} }
...@@ -405,9 +544,164 @@ ...@@ -405,9 +544,164 @@
}, },
close() { close() {
this.closeMessage() this.closeMessage()
// 清理背景图相关数据
this.backgroundImgsList = []
this.channelQrcode = ''
this.wxGameForm.selectedBackgroundIndex = 0
this.imageInfo = {}
this.previewVisible = false
this.previewImageInfo = null
this.$emit('close') this.$emit('close')
this.$emit('update:show', false) this.$emit('update:show', false)
},
// 处理背景图选择变化
handleBackgroundChange(index) {
this.updateImageInfo(index)
},
// 更新 imageInfo
updateImageInfo(index) {
if (this.backgroundImgsList.length > 0 && this.backgroundImgsList[index]) {
const selectedBg = this.backgroundImgsList[index]
this.imageInfo = {
background_img: selectedBg.background_img,
channel_qrcode: this.channelQrcode,
'x-coordinate': selectedBg['x-coordinate'] || 0,
'y-coordinate': selectedBg['y-coordinate'] || 0
}
}
},
// 确认选择背景图后合成图片
async confirmAndCompose() {
if (this.backgroundImgsList.length === 0) {
// 如果没有背景图列表,直接合成(兼容旧逻辑)
await this.composeImage()
return
}
// 确保 imageInfo 已设置
if (!this.imageInfo.background_img) {
this.updateImageInfo(this.wxGameForm.selectedBackgroundIndex)
}
await this.composeImage()
},
// 显示大图预览
showPreview(item, index) {
// 构建预览图片信息
const previewInfo = {
background_img: item.background_img,
channel_qrcode: this.channelQrcode,
'x-coordinate': item['x-coordinate'] || 0,
'y-coordinate': item['y-coordinate'] || 0
}
this.previewImageInfo = previewInfo
this.previewVisible = true
// 重置二维码样式,等待图片加载后计算
this.previewQrcodeStyle = {}
// 等待 DOM 更新后,检查图片是否已加载
this.$nextTick(() => {
if (this.$refs.previewBgImgDisplay) {
const bgImg = this.$refs.previewBgImgDisplay
// 如果图片已经加载完成(complete 属性为 true),直接计算样式
if (bgImg.complete && bgImg.naturalWidth > 0) {
this.calculatePreviewQrcodeStyle()
}
}
})
},
// 处理预览背景图加载完成
handlePreviewImageLoad() {
this.calculatePreviewQrcodeStyle()
},
// 计算预览二维码样式
calculatePreviewQrcodeStyle() {
if (!this.previewImageInfo || !this.$refs.previewBgImgDisplay) {
return
}
const bgImg = this.$refs.previewBgImgDisplay
// 参考生成图片时的逻辑:背景图宽度固定为 750px
const bgOriginalWidth = 750 // 原始宽度(生成图片时的宽度)
// 等待下一帧,确保图片已渲染
this.$nextTick(() => {
// 获取背景图的实际显示尺寸(参考生成图片时的逻辑)
// 使用 getBoundingClientRect 获取实际渲染尺寸
const imgRect = bgImg.getBoundingClientRect()
const displayWidth = imgRect.width
const displayHeight = imgRect.height
// 如果图片还没有渲染完成,延迟重试
if (displayWidth === 0 || displayHeight === 0) {
setTimeout(() => {
this.calculatePreviewQrcodeStyle()
}, 100)
return
}
// 计算缩放比例(基于实际显示宽度与原始宽度的比例)
// 参考生成图片时:width = 750, height = (naturalHeight / naturalWidth) * width
const scale = displayWidth / bgOriginalWidth
// 计算二维码位置和尺寸(参考生成图片时的逻辑)
// x-coordinate 和 y-coordinate 是相对于 750px 宽度的绝对位置
// 二维码直接使用坐标值,相对于背景图容器定位
const qrSize = 180 * scale
const qrLeft = (this.previewImageInfo['x-coordinate'] || 0) * scale
const qrTop = (this.previewImageInfo['y-coordinate'] || 0) * scale
this.previewQrcodeStyle = {
position: 'absolute',
left: `${qrLeft}px`,
top: `${qrTop}px`,
width: `${qrSize}px`,
height: `${qrSize}px`
}
})
},
// 处理背景图列表中的图片加载完成
handleBackgroundImageLoad(index, item) {
const bgImgRef = this.$refs[`bgImg_${index}`]
// Vue 2 中,ref 可能是数组或单个元素
const bgImg = Array.isArray(bgImgRef) ? bgImgRef[0] : bgImgRef
if (!bgImg) {
return
} }
// 参考生成图片时的逻辑:背景图宽度固定为 750px
const bgOriginalWidth = 750 // 原始宽度(生成图片时的宽度)
// 等待下一帧,确保图片已渲染
this.$nextTick(() => {
// 获取背景图的实际显示尺寸(参考生成图片时的逻辑)
// 使用 getBoundingClientRect 获取实际渲染尺寸
const imgRect = bgImg.getBoundingClientRect()
const displayWidth = imgRect.width
const displayHeight = imgRect.height
// 计算缩放比例(基于实际显示宽度与原始宽度的比例)
// 参考生成图片时:width = 750, height = (naturalHeight / naturalWidth) * width
const scale = displayWidth / bgOriginalWidth
// 计算二维码位置和尺寸(参考生成图片时的逻辑)
// x-coordinate 和 y-coordinate 是相对于 750px 宽度的绝对位置
// 二维码直接使用坐标值,相对于背景图容器定位
const qrSize = 180 * scale
const qrLeft = (item['x-coordinate'] || 0) * scale
const qrTop = (item['y-coordinate'] || 0) * scale
// 更新二维码样式
this.$set(this.backgroundImgsList[index], 'qrStyle', {
position: 'absolute',
left: `${qrLeft}px`,
top: `${qrTop}px`,
width: `${qrSize}px`,
height: `${qrSize}px`
})
})
}
} }
} }
</script> </script>
...@@ -473,5 +767,148 @@ ...@@ -473,5 +767,148 @@
object-fit: contain; object-fit: contain;
pointer-events: none; pointer-events: none;
} }
</style> .required-mark {
color: #f53f3f;
margin-right: 4px;
}
.background-options {
display: flex;
flex-wrap: wrap;
gap: 16px;
}
.background-radio-item {
margin-right: 0 !important;
margin-bottom: 0 !important;
flex: 1 0 0;
min-width: 0;
::v-deep .el-radio__input {
display: none;
}
::v-deep .el-radio__label {
padding-left: 0;
cursor: pointer;
width: 100%;
}
}
.background-card {
position: relative;
width: 100%;
height: 235px;
background: #fff;
border: 1.5px solid #e5e7eb;
border-radius: 8px;
padding: 10px;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
transition: all 0.3s;
}
.background-radio-item.is-checked .background-card {
border-color: #00bf8a;
}
.background-preview-wrapper {
position: relative;
width: 100%;
height: auto;
display: flex;
align-items: center;
justify-content: center;
}
.background-preview {
width: 100%;
height: 100%;
object-fit: contain;
pointer-events: none;
border-radius: 5px;
}
.background-qrcode {
pointer-events: none;
object-fit: contain;
}
.eye-icon {
position: absolute;
left: 6.5px;
top: 6.5px;
width: 16px;
height: 16px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
z-index: 10;
i {
font-size: 16px;
color: #00bf8a;
}
&:hover {
opacity: 0.8;
}
}
.status-icon {
position: absolute;
right: 6.5px;
top: 6.5px;
width: 16px;
height: 16px;
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
.status-icon-checked {
font-size: 14px;
color: #00bf8a;
}
.status-icon-unchecked {
font-size: 14px;
color: #909399;
}
}
.preview-dialog {
right: 400px;
}
.preview-content {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
min-height: 400px;
}
.preview-wrapper {
position: relative;
display: inline-block;
max-width: 100%;
}
.preview-background {
max-width: 100%;
max-height: 70vh;
object-fit: contain;
display: block;
border-radius: 10px;
}
.preview-qrcode {
object-fit: contain;
pointer-events: none;
}
</style>
\ No newline at end of file
...@@ -176,6 +176,17 @@ ...@@ -176,6 +176,17 @@
</p> </p>
</div> </div>
</div> </div>
<!-- 带教记录 -->
<div class="item rowFlex columnCenter spaceBetween">
<div class="rowFlex columnCenter">
<span class="label">带教记录:</span>
<el-button
type="text"
class="text cursor-pointer"
@click.stop="openMentorRecordDrawer(items)"
>{{ items.teach_num || '-' }} 次 </el-button>
</div>
</div>
</div> </div>
</el-collapse-item> </el-collapse-item>
</div> </div>
...@@ -196,24 +207,40 @@ ...@@ -196,24 +207,40 @@
:show.sync="showAppeal" :show.sync="showAppeal"
:appeal-info="appealInfo" :appeal-info="appealInfo"
/> />
<!-- 带教记录弹窗 -->
<el-drawer
v-model="showMentorRecord"
drawer-title="带教记录"
drawer-size="400px"
:append-to-body="true"
>
<mentorRecord
v-if="showMentorRecord"
:role-id="currentRoleId"
:role-name="currentRoleName"
@refresh="handleMentorRecordRefresh"
/>
</el-drawer>
</div> </div>
</template> </template>
<script> <script>
import { mapState, mapMutations, mapActions } from "vuex"; import { mapState, mapMutations, mapActions } from "vuex";
import { getRoleHoLo, marketingRoleGrade, getServerDayApi } from "@/api/game"; import { getRoleHoLo, marketingRoleGrade, getServerDayApi,roleTeachingNum } from "@/api/game";
import noContent from "@/components/noContent.vue"; import noContent from "@/components/noContent.vue";
import appeal from "./layer/appeal.vue"; import appeal from "./layer/appeal.vue";
import watchMember from "@/mixins/watchMember"; import watchMember from "@/mixins/watchMember";
import { createDetails } from "@/views/popup/RecentActivitiesPopup/index.js"; import { createDetails } from "@/views/popup/RecentActivitiesPopup/index.js";
import { createRoleRecentActivityNotPushNum } from "@/views/hooks/useGetCount.js"; import { createRoleRecentActivityNotPushNum } from "@/views/hooks/useGetCount.js";
import vipLevel from "@/views/userInfo/components/gameInfo/vipLevel.vue"; import vipLevel from "@/views/userInfo/components/gameInfo/vipLevel.vue";
import mentorRecord from "@/views/userInfo/components/gameInfo/mentorRecord.vue";
export default { export default {
name: "roleInfo", name: "roleInfo",
components: { components: {
noContent, noContent,
appeal, appeal,
vipLevel, vipLevel,
mentorRecord,
}, },
data() { data() {
return { return {
...@@ -232,6 +259,9 @@ export default { ...@@ -232,6 +259,9 @@ export default {
recentActivitiesPopupInstance: null, //近期要开模块弹框 recentActivitiesPopupInstance: null, //近期要开模块弹框
roleRecentActivityNotPushNumInstance: null, //侧边栏计数弹框 roleRecentActivityNotPushNumInstance: null, //侧边栏计数弹框
numRoleIdList: [], numRoleIdList: [],
showMentorRecord: false, // 带教记录弹窗显示状态
currentRoleId: null, // 当前查看带教记录的角色ID
currentRoleName: '' // 当前查看带教记录的角色名称
}; };
}, },
computed: { computed: {
...@@ -240,7 +270,11 @@ export default { ...@@ -240,7 +270,11 @@ export default {
watch: { watch: {
collapseActive(newVal, oldVal) { collapseActive(newVal, oldVal) {
if (newVal.length > 0) { if (newVal.length > 0) {
this.handleChange(newVal.filter((item) => !oldVal.includes(item))); const newOpenedItems = newVal.filter(item => !oldVal.includes(item))
this.handleChange(newOpenedItems)
// 处理带教次数获取
this.handleChangeRoleTeachingNum(newOpenedItems)
} }
}, },
}, },
...@@ -295,6 +329,66 @@ export default { ...@@ -295,6 +329,66 @@ export default {
}); });
} }
}, },
/**
* 打开带教记录弹窗
*/
openMentorRecordDrawer(roleItem) {
this.currentRoleId = roleItem.role_id
this.currentRoleName = `${roleItem.role_name}-${roleItem.server_name}`
this.showMentorRecord = true
},
/**
* 带教记录刷新后,更新角色列表中的带教次数
*/
async handleMentorRecordRefresh(roleId,teachingListLength) {
// 重新获取角色列表,更新带教次数
const index = this.roleList.findIndex(item => item.role_id === roleId)
if (index !== -1) {
this.$set(this.roleList[index], 'teach_num', teachingListLength)
}
this.roleList = this.roleList.concat([])
},
/**
* 处理角色展开时获取带教次数
* @param {Array} openedRoleIds - 新展开的角色ID数组
*/
handleChangeRoleTeachingNum(openedRoleIds) {
if (!openedRoleIds || openedRoleIds.length === 0) {
return
}
// 遍历新展开的角色,获取带教次数
openedRoleIds.forEach(roleId => {
const roleItem = this.roleList.find(item => item.role_id === roleId)
if (roleItem) {
// 如果已经存在 teach_num 字段,则不请求接口
if (roleItem.teach_num === undefined || roleItem.teach_num === null) {
this.getRoleTeachingNum(roleId)
}
}
})
},
/**
* 获取角色带教次数
* @param {String|Number} roleId - 角色ID
*/
async getRoleTeachingNum(roleId) {
try {
const res = await roleTeachingNum({
role_id: roleId
})
console.log(res,'res')
if (res.status_code === 1) {
// 更新对应角色的 teach_num 字段
const index = this.roleList.findIndex(item => item.role_id === roleId)
if (index !== -1) {
this.$set(this.roleList[index], 'teach_num', res.data.data?.role_teaching_num || 0)
this.roleList = [...this.roleList]
}
}
} catch (error) {
console.error('获取带教次数失败:', error)
}
},
async handleChange(v) { async handleChange(v) {
const index = this.roleList.findIndex( const index = this.roleList.findIndex(
...@@ -344,6 +438,66 @@ export default { ...@@ -344,6 +438,66 @@ export default {
} }
); );
}, },
/**
* 打开带教记录弹窗
*/
openMentorRecordDrawer(roleItem) {
this.currentRoleId = roleItem.role_id
this.currentRoleName = `${roleItem.role_name}-${roleItem.server_name}`
this.showMentorRecord = true
},
/**
* 带教记录刷新后,更新角色列表中的带教次数
*/
async handleMentorRecordRefresh(roleId,teachingListLength) {
// 重新获取角色列表,更新带教次数
const index = this.roleList.findIndex(item => item.role_id === roleId)
if (index !== -1) {
this.$set(this.roleList[index], 'teach_num', teachingListLength)
}
this.roleList = this.roleList.concat([])
},
/**
* 处理角色展开时获取带教次数
* @param {Array} openedRoleIds - 新展开的角色ID数组
*/
handleChangeRoleTeachingNum(openedRoleIds) {
if (!openedRoleIds || openedRoleIds.length === 0) {
return
}
// 遍历新展开的角色,获取带教次数
openedRoleIds.forEach(roleId => {
const roleItem = this.roleList.find(item => item.role_id === roleId)
if (roleItem) {
// 如果已经存在 teach_num 字段,则不请求接口
if (roleItem.teach_num === undefined || roleItem.teach_num === null) {
this.getRoleTeachingNum(roleId)
}
}
})
},
/**
* 获取角色带教次数
* @param {String|Number} roleId - 角色ID
*/
async getRoleTeachingNum(roleId) {
try {
const res = await roleTeachingNum({
role_id: roleId
})
console.log(res,'res')
if (res.status_code === 1) {
// 更新对应角色的 teach_num 字段
const index = this.roleList.findIndex(item => item.role_id === roleId)
if (index !== -1) {
this.$set(this.roleList[index], 'teach_num', res.data.data?.role_teaching_num || 0)
this.roleList = [...this.roleList]
}
}
} catch (error) {
console.error('获取带教次数失败:', error)
}
}
}, },
beforeDestroy() { beforeDestroy() {
this.recentActivitiesPopupInstance.destroy(); this.recentActivitiesPopupInstance.destroy();
......
<!--
* @Author: 金多虾 937667504@qq.com
* @Date: 2025-12-11 11:01:15
* @LastEditors: 金多虾 937667504@qq.com
* @LastEditTime: 2025-12-15 14:32:47
* @FilePath: /company_wx_frontend/src/views/works/component/gameInfo/roleInfo/mentorRecord.vue
* @Description: 带教记录组件
-->
<template>
<div class="mentor-record-page">
<!-- 标题栏 -->
<div class="mentor-record-page__header">
<p class="mentor-record-page__header-title">{{ roleName }}带教记录</p>
</div>
<!-- 提示信息和添加按钮区域 -->
<div class="mentor-record-page__toolbar">
<p class="mentor-record-page__toolbar-tip">当天添加的备注,第二天才会统计带教次数</p>
<el-button
v-if="teachingList.length < 5"
type="primary"
size="small"
@click="showAddForm = !showAddForm"
>添加记录</el-button>
</div>
<!-- 新增记录表单 -->
<div v-if="showAddForm && teachingList.length < 5" class="mentor-record-page__add-form">
<el-input
v-model="formData.content"
type="textarea"
:rows="3"
placeholder="请输入"
maxlength="500"
show-word-limit
class="mentor-record-page__add-form-input"
/>
<div class="mentor-record-page__add-form-buttons">
<el-button size="small" @click="handleCancelAdd">取消</el-button>
<el-button type="primary" size="small" :loading="submitLoading" @click="handleSubmitAdd">保存</el-button>
</div>
</div>
<!-- 带教记录列表 -->
<div class="mentor-record-page__list">
<div
v-for="(item, index) in teachingList"
:key="item.id || index"
class="mentor-record-page__list-item"
>
<p class="mentor-record-page__list-item-title">{{ item.teaching_num }}次带教</p>
<p class="mentor-record-page__list-item-content">{{ item.teaching_text }}</p>
<div class="mentor-record-page__list-item-footer">
<span class="mentor-record-page__list-item-creator">新增人:{{ item.update_user || '-' }}</span>
<span class="mentor-record-page__list-item-divider">|</span>
<span class="mentor-record-page__list-item-time">{{item.update_time }}</span>
</div>
</div>
<div v-if="teachingList.length === 0 && !loading" class="mentor-record-page__empty">
<svg-icon icon-class="noContent" />
<p>暂无带教记录</p>
</div>
</div>
</div>
</template>
<script>
import { roleTeachingList, roleTeachingAdd } from '@/api/game'
import { mapState } from 'vuex'
export default {
name: 'MentorRecord',
props: {
roleId: {
type: [String, Number],
required: true
},
roleName: {
type: String,
default: ''
}
},
data() {
return {
loading: false,
submitLoading: false,
showAddForm: false,
teachingList: [],
formData: {
content: ''
}
}
},
watch: {
roleId: {
immediate: true,
handler(newVal) {
if (newVal) {
this.getTeachingList()
}
}
}
},
computed: {
...mapState('user', ['userInfo'])
},
methods: {
/**
* 获取带教记录列表
*/
async getTeachingList() {
if (!this.roleId) {
return
}
try {
this.loading = true
const res = await roleTeachingList({
role_id: this.roleId
})
if (res.status_code === 1) {
this.teachingList = res.data.data || []
// 按teaching_num倒序排列
this.teachingList.sort((a, b) => {
return (b.teaching_num || 0) - (a.teaching_num || 0)
})
} else {
this.$message({
message: res.data.msg || '获取带教记录失败',
type: 'error'
})
}
} catch (error) {
console.error('获取带教记录失败:', error)
this.$message({
message: '获取带教记录失败,请稍后重试',
type: 'error'
})
} finally {
this.loading = false
}
},
/**
* 新增带教记录
*/
async handleSubmitAdd() {
if (!this.formData.content || !this.formData.content.trim()) {
this.$message({
message: '请输入带教记录内容',
type: 'warning'
})
return
}
try {
this.submitLoading = true
const res = await roleTeachingAdd({
role_id: this.roleId,
teaching_num: this.teachingList.length + 1,
teaching_text: this.formData.content.trim(),
create_user_id: this.userInfo.id,
create_user: this.userInfo.username
})
if (res.status_code === 1) {
this.$message({
message: res.data.msg || '添加成功',
type: 'success'
})
this.formData.content = ''
this.showAddForm = false
// 重新获取列表
await this.getTeachingList()
// 通知父组件更新带教次数
this.$emit('refresh',this.roleId,this.teachingList.length)
} else {
this.$message({
message: res.data.msg || '添加失败',
type: 'error'
})
}
} catch (error) {
console.error('新增带教记录失败:', error)
this.$message({
message: '添加失败,请稍后重试',
type: 'error'
})
} finally {
this.submitLoading = false
}
},
/**
* 取消新增
*/
handleCancelAdd() {
this.formData.content = ''
this.showAddForm = false
},
}
}
</script>
<style lang="scss" scoped>
.mentor-record-page {
width: 100%;
height: 100%;
padding: 12px;
padding-top: 0;
background: #fff;
border-right: 1px solid #ebedf0;
display: flex;
flex-direction: column;
overflow: hidden;
&__header {
display: flex;
align-items: center;
padding: 12px 0;
padding-top: 0;
border-bottom: 1px solid #ebedf0;
&-title {
font-family: PingFangSC-Medium, PingFang SC;
font-size: 14px;
font-weight: 500;
line-height: 22px;
color: #131920;
}
}
&__toolbar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 0;
margin-top: 12px;
&-tip {
font-family: PingFangSC-Regular, PingFang SC;
font-size: 12px;
font-weight: 400;
line-height: 20px;
color: #909399;
}
}
&__add-form {
display: flex;
flex-direction: column;
gap: 8px;
margin-bottom: 12px;
&-input {
::v-deep .el-textarea__inner {
border: 1px solid #d6d9e0;
border-radius: 6px;
padding: 4px 6px;
font-size: 13px;
line-height: 22px;
color: #323335;
min-height: 60px;
resize: none;
&::placeholder {
color: #c9cdd4;
}
}
}
&-buttons {
display: flex;
gap: 8px;
justify-content: flex-end;
}
}
&__list {
flex: 1;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 12px;
&-item {
background: #fff;
border: 1px solid #ebedf0;
border-radius: 6px;
padding: 8px;
display: flex;
flex-direction: column;
gap: 4px;
&-title {
font-family: PingFangSC-Regular, PingFang SC;
font-size: 12px;
font-weight: 400;
line-height: 20px;
color: #6d7176;
}
&-content {
font-family: PingFangSC-Regular, PingFang SC;
font-size: 15px;
font-weight: 500;
line-height: 22px;
color: #131920;
word-break: break-all;
}
&-footer {
display: flex;
align-items: center;
gap: 4px;
}
&-creator,
&-time {
font-family: PingFangSC-Regular, PingFang SC;
font-size: 12px;
font-weight: 400;
line-height: 20px;
color: #b0b2b5;
}
&-divider {
color: #ebedf0;
font-size: 12px;
}
}
}
&__empty {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 40px 0;
color: #909399;
font-size: 14px;
p {
margin-top: 12px;
}
}
}
</style>
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论