|
@@ -1,6 +1,6 @@
|
|
|
<template>
|
|
<template>
|
|
|
<div class="app-container">
|
|
<div class="app-container">
|
|
|
- <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
|
|
|
|
|
|
|
+ <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="88px">
|
|
|
<el-form-item label="直播ID" prop="liveId">
|
|
<el-form-item label="直播ID" prop="liveId">
|
|
|
<el-input
|
|
<el-input
|
|
|
v-model="queryParams.liveId"
|
|
v-model="queryParams.liveId"
|
|
@@ -10,40 +10,788 @@
|
|
|
@keyup.enter.native="handleQuery"
|
|
@keyup.enter.native="handleQuery"
|
|
|
/>
|
|
/>
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
|
|
+ <el-form-item label="直播名称" prop="liveName">
|
|
|
|
|
+ <el-input
|
|
|
|
|
+ v-model="queryParams.liveName"
|
|
|
|
|
+ placeholder="请输入直播名称"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ @keyup.enter.native="handleQuery"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="用户ID" prop="userId">
|
|
|
|
|
+ <el-input
|
|
|
|
|
+ v-model="queryParams.userId"
|
|
|
|
|
+ placeholder="请输入用户ID"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ @keyup.enter.native="handleQuery"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="用户昵称" prop="userName">
|
|
|
|
|
+ <el-input
|
|
|
|
|
+ v-model="queryParams.userName"
|
|
|
|
|
+ placeholder="请输入用户昵称"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ @keyup.enter.native="handleQuery"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="公司" prop="companyId">
|
|
|
|
|
+ <el-select
|
|
|
|
|
+ v-model="queryParams.companyId"
|
|
|
|
|
+ placeholder="请选择公司"
|
|
|
|
|
+ filterable
|
|
|
|
|
+ clearable
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ style="width: 200px"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-option
|
|
|
|
|
+ v-for="item in companyList"
|
|
|
|
|
+ :key="item.companyId"
|
|
|
|
|
+ :label="item.companyName"
|
|
|
|
|
+ :value="item.companyId"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="销售ID" prop="companyUserId">
|
|
|
|
|
+ <el-input
|
|
|
|
|
+ v-model="queryParams.companyUserId"
|
|
|
|
|
+ placeholder="请输入销售用户ID"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ @keyup.enter.native="handleQuery"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="观看时间" prop="dateRange">
|
|
|
|
|
+ <el-date-picker
|
|
|
|
|
+ v-model="dateRange"
|
|
|
|
|
+ type="datetimerange"
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ value-format="yyyy-MM-dd HH:mm:ss"
|
|
|
|
|
+ range-separator="-"
|
|
|
|
|
+ start-placeholder="开始时间"
|
|
|
|
|
+ end-placeholder="结束时间"
|
|
|
|
|
+ :default-time="['00:00:00', '23:59:59']"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-form-item>
|
|
|
<el-form-item>
|
|
<el-form-item>
|
|
|
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
|
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
|
|
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
|
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
</el-form>
|
|
</el-form>
|
|
|
|
|
+
|
|
|
|
|
+ <el-row :gutter="10" class="mb8">
|
|
|
|
|
+ <el-col :span="1.5">
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ type="primary"
|
|
|
|
|
+ plain
|
|
|
|
|
+ size="mini"
|
|
|
|
|
+ v-hasPermi="['app:user:bindTag']"
|
|
|
|
|
+ @click="openBindTagPage(false, 0)"
|
|
|
|
|
+ >批量添加标签
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ <el-col :span="1.5">
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ type="info"
|
|
|
|
|
+ plain
|
|
|
|
|
+ size="mini"
|
|
|
|
|
+ v-hasPermi="['app:user:unbindTag']"
|
|
|
|
|
+ @click="openUnbindTagPage(false, 1)"
|
|
|
|
|
+ >批量移除标签
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ <el-col :span="1.5">
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ type="primary"
|
|
|
|
|
+ plain
|
|
|
|
|
+ size="mini"
|
|
|
|
|
+ v-hasPermi="['app:user:batchBindTag']"
|
|
|
|
|
+ @click="openBindTagPage(true, 0)"
|
|
|
|
|
+ >批量添加标签(筛选条件)
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ <el-col :span="1.5">
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ type="info"
|
|
|
|
|
+ plain
|
|
|
|
|
+ size="mini"
|
|
|
|
|
+ v-hasPermi="['app:user:batchUnbindTag']"
|
|
|
|
|
+ @click="openUnbindTagPage(true, 1)"
|
|
|
|
|
+ >批量移除标签(筛选条件)
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ <el-col :span="1.5">
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ type="success"
|
|
|
|
|
+ plain
|
|
|
|
|
+ size="mini"
|
|
|
|
|
+ @click="openRemarkPage(false)"
|
|
|
|
|
+ >批量修改备注
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ <el-col :span="1.5">
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ type="success"
|
|
|
|
|
+ plain
|
|
|
|
|
+ size="mini"
|
|
|
|
|
+ @click="openRemarkPage(true)"
|
|
|
|
|
+ >批量修改备注(筛选条件)
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
|
|
|
|
+ </el-row>
|
|
|
|
|
+
|
|
|
|
|
+ <el-table v-loading="loading" :data="list" border @selection-change="handleSelectionChange">
|
|
|
|
|
+ <el-table-column type="selection" width="55" align="center" />
|
|
|
|
|
+ <el-table-column label="用户ID" align="center" prop="userId" width="90" />
|
|
|
|
|
+ <el-table-column label="用户昵称" align="center" prop="userName" min-width="120" show-overflow-tooltip />
|
|
|
|
|
+ <el-table-column label="直播ID" align="center" prop="liveId" width="90" />
|
|
|
|
|
+ <el-table-column label="直播名称" align="center" prop="liveName" min-width="140" show-overflow-tooltip />
|
|
|
|
|
+ <el-table-column label="直播观看时长" align="center" min-width="120">
|
|
|
|
|
+ <template slot-scope="scope">
|
|
|
|
|
+ {{ formatDuration(scope.row.liveWatchDuration) }}
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column label="回放观看时长" align="center" min-width="120">
|
|
|
|
|
+ <template slot-scope="scope">
|
|
|
|
|
+ {{ formatDuration(scope.row.playbackWatchDuration) }}
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column label="是否完课" align="center" width="90">
|
|
|
|
|
+ <template slot-scope="scope">
|
|
|
|
|
+ <el-tag :type="scope.row.isCompleted === 1 ? 'success' : 'info'" size="mini">
|
|
|
|
|
+ {{ scope.row.isCompleted === 1 ? '是' : '否' }}
|
|
|
|
|
+ </el-tag>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column label="订单数" align="center" prop="orderCount" width="80" />
|
|
|
|
|
+ <el-table-column label="订单金额" align="center" prop="orderAmount" width="100" />
|
|
|
|
|
+ <el-table-column label="公司" align="center" prop="companyName" min-width="120" show-overflow-tooltip />
|
|
|
|
|
+ <el-table-column label="销售" align="center" prop="salesName" min-width="100" show-overflow-tooltip />
|
|
|
|
|
+ <el-table-column prop="rewardType" label="领取类型" min-width="220" align="center">
|
|
|
|
|
+ <template slot-scope="scope">
|
|
|
|
|
+ <div v-if="hasClaimedReward(scope.row.rewardType)">
|
|
|
|
|
+ <dict-tag :options="liveRewardTypeList" :value="scope.row.rewardType"/>
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ v-if="hasRedOrPointsReward(scope.row.rewardType)"
|
|
|
|
|
+ type="text"
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ style="margin-left: 8px;"
|
|
|
|
|
+ @click="handleOpenRedPacketLog(scope.row)"
|
|
|
|
|
+ >红包/积分领取详细</el-button>
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ v-if="hasCouponReward(scope.row.rewardType)"
|
|
|
|
|
+ type="text"
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ style="margin-left: 8px;"
|
|
|
|
|
+ @click="handleOpenCouponLog(scope.row)"
|
|
|
|
|
+ >核销卷领取详情</el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <span v-else>
|
|
|
|
|
+ <dict-tag
|
|
|
|
|
+ v-if="scope.row.rewardType === 0 || scope.row.rewardType === '0' || scope.row.rewardType === 99 || scope.row.rewardType === '99'"
|
|
|
|
|
+ :options="liveRewardTypeList"
|
|
|
|
|
+ :value="scope.row.rewardType"
|
|
|
|
|
+ />
|
|
|
|
|
+ <span v-else>未领取</span>
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ </el-table>
|
|
|
|
|
+
|
|
|
|
|
+ <pagination
|
|
|
|
|
+ v-show="total > 0"
|
|
|
|
|
+ :total="total"
|
|
|
|
|
+ :page.sync="queryParams.pageNum"
|
|
|
|
|
+ :limit.sync="queryParams.pageSize"
|
|
|
|
|
+ @pagination="getList"
|
|
|
|
|
+ />
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 红包领取详情侧边栏 -->
|
|
|
|
|
+ <el-drawer
|
|
|
|
|
+ title="红包领取详情"
|
|
|
|
|
+ :visible.sync="redPacketLogDrawerVisible"
|
|
|
|
|
+ direction="rtl"
|
|
|
|
|
+ size="60%"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-table
|
|
|
|
|
+ :data="redPacketLogList"
|
|
|
|
|
+ v-loading="redPacketLogLoading"
|
|
|
|
|
+ border
|
|
|
|
|
+ style="width: 100%"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-table-column type="index" label="序号" width="60" align="center"></el-table-column>
|
|
|
|
|
+ <el-table-column prop="liveId" label="直播间id" min-width="120"></el-table-column>
|
|
|
|
|
+ <el-table-column prop="userId" label="用户id" min-width="120"></el-table-column>
|
|
|
|
|
+ <el-table-column prop="amount" label="金额" min-width="120" align="center">
|
|
|
|
|
+ <template slot-scope="scope">
|
|
|
|
|
+ {{ scope.row.amount != null ? scope.row.amount : '-' }}
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column prop="status" label="状态" min-width="100" align="center">
|
|
|
|
|
+ <template slot-scope="scope">
|
|
|
|
|
+ <el-tag v-if="scope.row.status == 1" type="success">已领取</el-tag>
|
|
|
|
|
+ <el-tag v-else-if="scope.row.status == 0" type="info">未领取</el-tag>
|
|
|
|
|
+ <el-tag v-else type="info">{{ scope.row.status }}</el-tag>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column prop="createTime" label="领取时间" min-width="160" align="center"></el-table-column>
|
|
|
|
|
+ <el-table-column prop="remark" label="备注" min-width="160" align="center"></el-table-column>
|
|
|
|
|
+ </el-table>
|
|
|
|
|
+ <pagination
|
|
|
|
|
+ v-show="redPacketLogTotal > 0"
|
|
|
|
|
+ :total="redPacketLogTotal"
|
|
|
|
|
+ :page.sync="redPacketLogQueryParams.pageNum"
|
|
|
|
|
+ :limit.sync="redPacketLogQueryParams.pageSize"
|
|
|
|
|
+ :page-sizes="[10, 20, 50]"
|
|
|
|
|
+ @pagination="loadRedPacketLogList"
|
|
|
|
|
+ style="margin-top: 20px;"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-drawer>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 核销卷领取详情侧边栏 -->
|
|
|
|
|
+ <el-drawer
|
|
|
|
|
+ title="核销卷领取详情"
|
|
|
|
|
+ :visible.sync="couponLogDrawerVisible"
|
|
|
|
|
+ direction="rtl"
|
|
|
|
|
+ size="60%"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-table
|
|
|
|
|
+ :data="couponLogList"
|
|
|
|
|
+ v-loading="couponLogLoading"
|
|
|
|
|
+ border
|
|
|
|
|
+ style="width: 100%"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-table-column type="index" label="序号" width="60" align="center"></el-table-column>
|
|
|
|
|
+ <el-table-column prop="nickName" label="会员昵称" min-width="120"></el-table-column>
|
|
|
|
|
+ <el-table-column prop="couponTitle" label="优惠券名称" min-width="120"></el-table-column>
|
|
|
|
|
+ <el-table-column prop="limitTime" label="优惠券有效期" min-width="160" align="center"></el-table-column>
|
|
|
|
|
+ <el-table-column prop="startTime" label="开始时间" min-width="160" align="center"></el-table-column>
|
|
|
|
|
+ <el-table-column prop="updateTime" label="更新时间" min-width="160" align="center"></el-table-column>
|
|
|
|
|
+ <el-table-column prop="status" label="状态" min-width="100" align="center">
|
|
|
|
|
+ <template slot-scope="scope">
|
|
|
|
|
+ <el-tag v-if="scope.row.status == 1" type="success">已使用</el-tag>
|
|
|
|
|
+ <el-tag v-else-if="scope.row.status == 0" type="info">未使用</el-tag>
|
|
|
|
|
+ <el-tag v-else type="info">{{ scope.row.status }}</el-tag>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ </el-table>
|
|
|
|
|
+ <pagination
|
|
|
|
|
+ v-show="couponLogTotal > 0"
|
|
|
|
|
+ :total="couponLogTotal"
|
|
|
|
|
+ :page.sync="couponLogQueryParams.pageNum"
|
|
|
|
|
+ :limit.sync="couponLogQueryParams.pageSize"
|
|
|
|
|
+ :page-sizes="[10, 20, 50]"
|
|
|
|
|
+ @pagination="loadCouponLogList"
|
|
|
|
|
+ style="margin-top: 20px;"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-drawer>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 批量添加/移除标签 -->
|
|
|
|
|
+ <el-dialog :title="tagConfig.title" :visible.sync="tagConfig.visible" width="800px" append-to-body>
|
|
|
|
|
+ <div>搜索标签:
|
|
|
|
|
+ <el-input
|
|
|
|
|
+ v-model="tagConfig.queryParams.groupName"
|
|
|
|
|
+ placeholder="请输入标签名称"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ style="width: 200px;margin-right: 10px"
|
|
|
|
|
+ />
|
|
|
|
|
+ <el-button type="primary" icon="el-icon-search" size="mini" @click="handleSearchTags">搜索</el-button>
|
|
|
|
|
+ <el-button type="primary" icon="el-icon-refresh" size="mini" @click="cancelSearchTags">重置</el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="dialog-body">
|
|
|
|
|
+ <div v-for="groupItem in tagConfig.tagGroupList" :key="groupItem.id">
|
|
|
|
|
+ <div style="font-size: 20px;margin-top: 20px;margin-bottom: 20px;">
|
|
|
|
|
+ <span class="name-background">{{ groupItem.name }}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="tag-container">
|
|
|
|
|
+ <a
|
|
|
|
|
+ v-for="tagItem in groupItem.tags"
|
|
|
|
|
+ :key="tagItem.id"
|
|
|
|
|
+ class="tag-box"
|
|
|
|
|
+ @click="tagSelection(tagItem)"
|
|
|
|
|
+ :class="{ 'tag-selected': tagItem.isSelected }"
|
|
|
|
|
+ >
|
|
|
|
|
+ {{ tagItem.name }}
|
|
|
|
|
+ </a>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <pagination
|
|
|
|
|
+ v-show="tagConfig.total > 0"
|
|
|
|
|
+ :total="tagConfig.total"
|
|
|
|
|
+ :page.sync="tagConfig.queryParams.pageNum"
|
|
|
|
|
+ :limit.sync="tagConfig.queryParams.pageSize"
|
|
|
|
|
+ @pagination="getTagGroupList"
|
|
|
|
|
+ />
|
|
|
|
|
+ <div slot="footer" class="dialog-footer">
|
|
|
|
|
+ <el-button type="primary" @click="bindOrUnbindTagSubmit">确 定</el-button>
|
|
|
|
|
+ <el-button @click="bindTagCancel">取 消</el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-dialog>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 批量修改备注 -->
|
|
|
|
|
+ <el-dialog :title="remarkConfig.title" :visible.sync="remarkConfig.visible" width="600px" append-to-body>
|
|
|
|
|
+ <el-form label-width="90px">
|
|
|
|
|
+ <el-form-item label="备注类型">
|
|
|
|
|
+ <el-radio-group v-model="remarkConfig.remarkType">
|
|
|
|
|
+ <el-radio :label="1">用户名称添加在新备注前</el-radio>
|
|
|
|
|
+ <el-radio :label="2">用户名称添加在新备注后</el-radio>
|
|
|
|
|
+ <el-radio :label="3">不添加用户名称</el-radio>
|
|
|
|
|
+ </el-radio-group>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="备注内容">
|
|
|
|
|
+ <el-input
|
|
|
|
|
+ v-model="remarkConfig.remark"
|
|
|
|
|
+ type="textarea"
|
|
|
|
|
+ :rows="3"
|
|
|
|
|
+ placeholder="请输入备注信息"
|
|
|
|
|
+ maxlength="30"
|
|
|
|
|
+ show-word-limit
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-form>
|
|
|
|
|
+ <div slot="footer" class="dialog-footer">
|
|
|
|
|
+ <el-button type="primary" :loading="remarkConfig.loading" @click="submitRemark">确 定</el-button>
|
|
|
|
|
+ <el-button @click="cancelRemark">取 消</el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-dialog>
|
|
|
</div>
|
|
</div>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
|
<script>
|
|
|
|
|
+import { liveWatchUserList } from '@/api/live/liveWatchUser'
|
|
|
|
|
+import { getCompanyList } from '@/api/company/company'
|
|
|
|
|
+import { listLiveRedPacketLog } from '@/api/live/liveRedPacketLog'
|
|
|
|
|
+import { listCourseCouponUserListVO } from '@/api/his/courseCouponUser'
|
|
|
|
|
+import { listTagGroupForUserBindTag } from '@/api/app/tag/tagGroup'
|
|
|
|
|
+import { bindTagV2, unbindTagV2 } from '@/api/app/tag/bindTag'
|
|
|
|
|
+import { batchUpdateFriendRemark } from '@/api/course/courseWatchLog'
|
|
|
|
|
|
|
|
export default {
|
|
export default {
|
|
|
name: 'LiveWatchUser',
|
|
name: 'LiveWatchUser',
|
|
|
data() {
|
|
data() {
|
|
|
return {
|
|
return {
|
|
|
|
|
+ loading: false,
|
|
|
showSearch: true,
|
|
showSearch: true,
|
|
|
- queryForm: null,
|
|
|
|
|
|
|
+ total: 0,
|
|
|
|
|
+ list: [],
|
|
|
|
|
+ ids: [],
|
|
|
|
|
+ companyList: [],
|
|
|
|
|
+ liveRewardTypeList: [],
|
|
|
|
|
+ dateRange: [],
|
|
|
queryParams: {
|
|
queryParams: {
|
|
|
pageNum: 1,
|
|
pageNum: 1,
|
|
|
- pageSize: 10
|
|
|
|
|
|
|
+ pageSize: 10,
|
|
|
|
|
+ liveId: null,
|
|
|
|
|
+ liveName: null,
|
|
|
|
|
+ userId: null,
|
|
|
|
|
+ userName: null,
|
|
|
|
|
+ companyId: null,
|
|
|
|
|
+ companyUserId: null,
|
|
|
|
|
+ bTime: null,
|
|
|
|
|
+ eTime: null
|
|
|
|
|
+ },
|
|
|
|
|
+ tagConfig: {
|
|
|
|
|
+ visible: false,
|
|
|
|
|
+ enableFilter: false,
|
|
|
|
|
+ title: null,
|
|
|
|
|
+ opType: 0,
|
|
|
|
|
+ queryParams: {
|
|
|
|
|
+ groupName: null,
|
|
|
|
|
+ pageNum: 1,
|
|
|
|
|
+ pageSize: 10
|
|
|
|
|
+ },
|
|
|
|
|
+ total: 0,
|
|
|
|
|
+ tagGroupList: [],
|
|
|
|
|
+ selected: []
|
|
|
|
|
+ },
|
|
|
|
|
+ redPacketLogDrawerVisible: false,
|
|
|
|
|
+ redPacketLogList: [],
|
|
|
|
|
+ redPacketLogLoading: false,
|
|
|
|
|
+ redPacketLogTotal: 0,
|
|
|
|
|
+ redPacketLogQueryParams: {
|
|
|
|
|
+ pageNum: 1,
|
|
|
|
|
+ pageSize: 10,
|
|
|
|
|
+ liveId: null,
|
|
|
|
|
+ userId: null
|
|
|
|
|
+ },
|
|
|
|
|
+ couponLogDrawerVisible: false,
|
|
|
|
|
+ couponLogList: [],
|
|
|
|
|
+ couponLogLoading: false,
|
|
|
|
|
+ couponLogTotal: 0,
|
|
|
|
|
+ couponLogQueryParams: {
|
|
|
|
|
+ pageNum: 1,
|
|
|
|
|
+ pageSize: 10,
|
|
|
|
|
+ userId: null,
|
|
|
|
|
+ liveId: null,
|
|
|
|
|
+ sendType: 2
|
|
|
|
|
+ },
|
|
|
|
|
+ remarkConfig: {
|
|
|
|
|
+ visible: false,
|
|
|
|
|
+ enableFilter: false,
|
|
|
|
|
+ title: '批量修改备注',
|
|
|
|
|
+ remarkType: 1,
|
|
|
|
|
+ remark: '',
|
|
|
|
|
+ loading: false
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
created() {
|
|
created() {
|
|
|
-
|
|
|
|
|
|
|
+ this.getDicts('live_watch_reward_type').then(res => {
|
|
|
|
|
+ this.liveRewardTypeList = res.data || []
|
|
|
|
|
+ })
|
|
|
|
|
+ this.loadCompanyList()
|
|
|
|
|
+ this.getList()
|
|
|
},
|
|
},
|
|
|
methods: {
|
|
methods: {
|
|
|
|
|
+ // 多选框选中数据(按会员 userId 去重)
|
|
|
|
|
+ handleSelectionChange(selection) {
|
|
|
|
|
+ this.ids = [...new Set(selection.map(item => item.userId).filter(id => id != null))]
|
|
|
|
|
+ },
|
|
|
|
|
+ openBindTagPage(enableFilter = false, opType = 0) {
|
|
|
|
|
+ this.tagConfig.enableFilter = enableFilter
|
|
|
|
|
+ this.tagConfig.opType = opType
|
|
|
|
|
+ if (!enableFilter && (!this.ids || this.ids.length === 0)) {
|
|
|
|
|
+ this.$message.closeAll()
|
|
|
|
|
+ return this.$message('请选择需要添加标签的用户')
|
|
|
|
|
+ }
|
|
|
|
|
+ this.getTagGroupList()
|
|
|
|
|
+ this.tagConfig.title = '批量添加标签' + (enableFilter ? '(筛选条件)' : '')
|
|
|
|
|
+ this.tagConfig.visible = true
|
|
|
|
|
+ },
|
|
|
|
|
+ openUnbindTagPage(enableFilter = false, opType = 1) {
|
|
|
|
|
+ this.tagConfig.enableFilter = enableFilter
|
|
|
|
|
+ this.tagConfig.opType = opType
|
|
|
|
|
+ if (!enableFilter && (!this.ids || this.ids.length === 0)) {
|
|
|
|
|
+ this.$message.closeAll()
|
|
|
|
|
+ return this.$message('请选择需要移除标签的用户')
|
|
|
|
|
+ }
|
|
|
|
|
+ this.getTagGroupList()
|
|
|
|
|
+ this.tagConfig.title = '批量移除标签' + (enableFilter ? '(筛选条件)' : '')
|
|
|
|
|
+ this.tagConfig.visible = true
|
|
|
|
|
+ },
|
|
|
|
|
+ handleSearchTags() {
|
|
|
|
|
+ this.tagConfig.queryParams.pageNum = 1
|
|
|
|
|
+ this.getTagGroupList()
|
|
|
|
|
+ },
|
|
|
|
|
+ cancelSearchTags() {
|
|
|
|
|
+ this.tagConfig.queryParams = {
|
|
|
|
|
+ groupName: null,
|
|
|
|
|
+ pageNum: 1,
|
|
|
|
|
+ pageSize: 10
|
|
|
|
|
+ }
|
|
|
|
|
+ this.getTagGroupList()
|
|
|
|
|
+ },
|
|
|
|
|
+ tagSelection(row) {
|
|
|
|
|
+ row.isSelected = !row.isSelected
|
|
|
|
|
+ if (row.isSelected) {
|
|
|
|
|
+ this.tagConfig.selected.push({ tagId: row.id, tagName: row.name })
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.tagConfig.selected = this.tagConfig.selected.filter(item => item.tagId !== row.id)
|
|
|
|
|
+ }
|
|
|
|
|
+ this.$forceUpdate()
|
|
|
|
|
+ },
|
|
|
|
|
+ resetBindTag() {
|
|
|
|
|
+ this.tagConfig = {
|
|
|
|
|
+ visible: false,
|
|
|
|
|
+ enableFilter: false,
|
|
|
|
|
+ title: null,
|
|
|
|
|
+ opType: 0,
|
|
|
|
|
+ queryParams: {
|
|
|
|
|
+ groupName: null,
|
|
|
|
|
+ pageNum: 1,
|
|
|
|
|
+ pageSize: 10
|
|
|
|
|
+ },
|
|
|
|
|
+ total: 0,
|
|
|
|
|
+ tagGroupList: [],
|
|
|
|
|
+ selected: []
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ /** 筛选条件绑标签:映射到 FsUserParam 可用字段(直播专属条件后端不会按 live 过滤) */
|
|
|
|
|
+ buildFsUserParam() {
|
|
|
|
|
+ const q = this.buildQuery()
|
|
|
|
|
+ return {
|
|
|
|
|
+ userId: q.userId,
|
|
|
|
|
+ nickName: q.userName,
|
|
|
|
|
+ companyId: q.companyId,
|
|
|
|
|
+ companyUserId: q.companyUserId
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ bindOrUnbindTagSubmit() {
|
|
|
|
|
+ const opType = this.tagConfig.opType
|
|
|
|
|
+ if (!this.tagConfig.selected || this.tagConfig.selected.length === 0) {
|
|
|
|
|
+ return this.$message('请选择标签')
|
|
|
|
|
+ }
|
|
|
|
|
+ const tmpParam = {
|
|
|
|
|
+ enableFilter: this.tagConfig.enableFilter,
|
|
|
|
|
+ tagId: this.tagConfig.selected.map(item => item.tagId),
|
|
|
|
|
+ userId: this.ids,
|
|
|
|
|
+ fsUserParam: this.buildFsUserParam()
|
|
|
|
|
+ }
|
|
|
|
|
+ if (opType === 0) {
|
|
|
|
|
+ bindTagV2(tmpParam)
|
|
|
|
|
+ .then(() => {
|
|
|
|
|
+ this.tagConfig.visible = false
|
|
|
|
|
+ this.resetBindTag()
|
|
|
|
|
+ this.msgSuccess('正在添加中!')
|
|
|
|
|
+ setTimeout(() => this.getList(), 500)
|
|
|
|
|
+ })
|
|
|
|
|
+ .catch(() => {
|
|
|
|
|
+ this.msgError('添加标签失败!')
|
|
|
|
|
+ })
|
|
|
|
|
+ } else {
|
|
|
|
|
+ unbindTagV2(tmpParam)
|
|
|
|
|
+ .then(() => {
|
|
|
|
|
+ this.tagConfig.visible = false
|
|
|
|
|
+ this.resetBindTag()
|
|
|
|
|
+ this.msgSuccess('正在移除中!')
|
|
|
|
|
+ setTimeout(() => this.getList(), 500)
|
|
|
|
|
+ })
|
|
|
|
|
+ .catch(() => {
|
|
|
|
|
+ this.msgError('移除标签失败!')
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ bindTagCancel() {
|
|
|
|
|
+ this.resetBindTag()
|
|
|
|
|
+ },
|
|
|
|
|
+ openRemarkPage(enableFilter = false) {
|
|
|
|
|
+ if (!enableFilter && (!this.ids || this.ids.length === 0)) {
|
|
|
|
|
+ this.$message.closeAll()
|
|
|
|
|
+ return this.$message('请选择需要修改备注的用户')
|
|
|
|
|
+ }
|
|
|
|
|
+ if (enableFilter && (!this.total || this.total <= 0)) {
|
|
|
|
|
+ this.$message.closeAll()
|
|
|
|
|
+ return this.$message('当前筛选条件下没有数据')
|
|
|
|
|
+ }
|
|
|
|
|
+ this.remarkConfig.enableFilter = enableFilter
|
|
|
|
|
+ this.remarkConfig.remarkType = 1
|
|
|
|
|
+ this.remarkConfig.remark = ''
|
|
|
|
|
+ this.remarkConfig.loading = false
|
|
|
|
|
+ this.remarkConfig.title = '批量修改备注' + (enableFilter ? '(筛选条件)' : '')
|
|
|
|
|
+ this.remarkConfig.visible = true
|
|
|
|
|
+ },
|
|
|
|
|
+ cancelRemark() {
|
|
|
|
|
+ this.remarkConfig.visible = false
|
|
|
|
|
+ this.remarkConfig.enableFilter = false
|
|
|
|
|
+ this.remarkConfig.remarkType = 1
|
|
|
|
|
+ this.remarkConfig.remark = ''
|
|
|
|
|
+ this.remarkConfig.loading = false
|
|
|
|
|
+ },
|
|
|
|
|
+ async submitRemark() {
|
|
|
|
|
+ const remark = (this.remarkConfig.remark || '').trim()
|
|
|
|
|
+ if (!remark) {
|
|
|
|
|
+ return this.$message.error('请输入备注内容')
|
|
|
|
|
+ }
|
|
|
|
|
+ if (remark.length > 30) {
|
|
|
|
|
+ return this.$message.error('备注内容不能超过30个字符')
|
|
|
|
|
+ }
|
|
|
|
|
+ this.remarkConfig.loading = true
|
|
|
|
|
+ try {
|
|
|
|
|
+ let userIds = []
|
|
|
|
|
+ if (this.remarkConfig.enableFilter) {
|
|
|
|
|
+ const queryParams = this.buildQuery()
|
|
|
|
|
+ queryParams.pageNum = 1
|
|
|
|
|
+ queryParams.pageSize = this.total > 0 ? this.total : 10000
|
|
|
|
|
+ const response = await liveWatchUserList(queryParams)
|
|
|
|
|
+ const rows = response.data || []
|
|
|
|
|
+ userIds = [...new Set(rows.map(item => item.userId).filter(id => id != null))]
|
|
|
|
|
+ } else {
|
|
|
|
|
+ userIds = this.ids
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!userIds || userIds.length === 0) {
|
|
|
|
|
+ this.remarkConfig.loading = false
|
|
|
|
|
+ return this.$message.error('没有可修改备注的用户')
|
|
|
|
|
+ }
|
|
|
|
|
+ await batchUpdateFriendRemark({
|
|
|
|
|
+ remarkType: this.remarkConfig.remarkType,
|
|
|
|
|
+ remark,
|
|
|
|
|
+ userId: userIds
|
|
|
|
|
+ })
|
|
|
|
|
+ this.msgSuccess('正在修改备注中!')
|
|
|
|
|
+ this.cancelRemark()
|
|
|
|
|
+ setTimeout(() => {
|
|
|
|
|
+ this.getList()
|
|
|
|
|
+ }, 500)
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ this.msgError('修改备注失败!')
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ this.remarkConfig.loading = false
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ getTagGroupList() {
|
|
|
|
|
+ listTagGroupForUserBindTag(this.tagConfig.queryParams).then(response => {
|
|
|
|
|
+ this.tagConfig.tagGroupList = response.rows || []
|
|
|
|
|
+ this.tagConfig.total = response.total || 0
|
|
|
|
|
+ // 回显已选
|
|
|
|
|
+ const selectedIds = new Set(this.tagConfig.selected.map(i => i.tagId))
|
|
|
|
|
+ this.tagConfig.tagGroupList.forEach(group => {
|
|
|
|
|
+ (group.tags || []).forEach(tag => {
|
|
|
|
|
+ this.$set(tag, 'isSelected', selectedIds.has(tag.id))
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+ },
|
|
|
|
|
+ /** live_watch_reward_type:0无 / 99未设置 视为未领取;97/98 特殊状态只展示文案 */
|
|
|
|
|
+ hasClaimedReward(type) {
|
|
|
|
|
+ if (type == null || type === '') return false
|
|
|
|
|
+ const t = Number(type)
|
|
|
|
|
+ if (Number.isNaN(t) || t === 0 || t === 99) return false
|
|
|
|
|
+ return true
|
|
|
|
|
+ },
|
|
|
|
|
+ /** 按位:1红包 2积分(排除 97+ 特殊码) */
|
|
|
|
|
+ hasRedOrPointsReward(type) {
|
|
|
|
|
+ const t = Number(type)
|
|
|
|
|
+ if (Number.isNaN(t) || t >= 97) return false
|
|
|
|
|
+ return (t & 1) !== 0 || (t & 2) !== 0
|
|
|
|
|
+ },
|
|
|
|
|
+ /** 按位:4核销券 */
|
|
|
|
|
+ hasCouponReward(type) {
|
|
|
|
|
+ const t = Number(type)
|
|
|
|
|
+ if (Number.isNaN(t) || t >= 97) return false
|
|
|
|
|
+ return (t & 4) !== 0
|
|
|
|
|
+ },
|
|
|
|
|
+ handleOpenRedPacketLog(row) {
|
|
|
|
|
+ this.redPacketLogDrawerVisible = true
|
|
|
|
|
+ this.redPacketLogQueryParams.liveId = row.liveId
|
|
|
|
|
+ this.redPacketLogQueryParams.userId = row.userId
|
|
|
|
|
+ this.redPacketLogQueryParams.pageNum = 1
|
|
|
|
|
+ this.loadRedPacketLogList()
|
|
|
|
|
+ },
|
|
|
|
|
+ handleOpenCouponLog(row) {
|
|
|
|
|
+ this.couponLogDrawerVisible = true
|
|
|
|
|
+ this.couponLogQueryParams.userId = row.userId
|
|
|
|
|
+ this.couponLogQueryParams.liveId = row.liveId
|
|
|
|
|
+ this.couponLogQueryParams.pageNum = 1
|
|
|
|
|
+ this.loadCouponLogList()
|
|
|
|
|
+ },
|
|
|
|
|
+ loadCouponLogList() {
|
|
|
|
|
+ this.couponLogLoading = true
|
|
|
|
|
+ listCourseCouponUserListVO({
|
|
|
|
|
+ pageNum: this.couponLogQueryParams.pageNum,
|
|
|
|
|
+ pageSize: this.couponLogQueryParams.pageSize,
|
|
|
|
|
+ userId: this.couponLogQueryParams.userId,
|
|
|
|
|
+ sendType: this.couponLogQueryParams.sendType,
|
|
|
|
|
+ liveId: this.couponLogQueryParams.liveId
|
|
|
|
|
+ }).then(response => {
|
|
|
|
|
+ this.couponLogList = response.rows || []
|
|
|
|
|
+ this.couponLogTotal = response.total || 0
|
|
|
|
|
+ this.couponLogLoading = false
|
|
|
|
|
+ }).catch(() => {
|
|
|
|
|
+ this.couponLogLoading = false
|
|
|
|
|
+ })
|
|
|
|
|
+ },
|
|
|
|
|
+ loadRedPacketLogList() {
|
|
|
|
|
+ this.redPacketLogLoading = true
|
|
|
|
|
+ listLiveRedPacketLog({
|
|
|
|
|
+ pageNum: this.redPacketLogQueryParams.pageNum,
|
|
|
|
|
+ pageSize: this.redPacketLogQueryParams.pageSize,
|
|
|
|
|
+ liveId: this.redPacketLogQueryParams.liveId,
|
|
|
|
|
+ userId: this.redPacketLogQueryParams.userId
|
|
|
|
|
+ }).then(response => {
|
|
|
|
|
+ if (response.code === 200) {
|
|
|
|
|
+ this.redPacketLogList = response.rows || []
|
|
|
|
|
+ this.redPacketLogTotal = response.total || 0
|
|
|
|
|
+ }
|
|
|
|
|
+ this.redPacketLogLoading = false
|
|
|
|
|
+ }).catch(() => {
|
|
|
|
|
+ this.redPacketLogLoading = false
|
|
|
|
|
+ })
|
|
|
|
|
+ },
|
|
|
|
|
+ loadCompanyList() {
|
|
|
|
|
+ getCompanyList().then(res => {
|
|
|
|
|
+ this.companyList = res.data || []
|
|
|
|
|
+ })
|
|
|
|
|
+ },
|
|
|
|
|
+ buildQuery() {
|
|
|
|
|
+ const params = { ...this.queryParams }
|
|
|
|
|
+ if (this.dateRange && this.dateRange.length === 2) {
|
|
|
|
|
+ params.bTime = this.dateRange[0]
|
|
|
|
|
+ params.eTime = this.dateRange[1]
|
|
|
|
|
+ } else {
|
|
|
|
|
+ params.bTime = null
|
|
|
|
|
+ params.eTime = null
|
|
|
|
|
+ }
|
|
|
|
|
+ ;['liveId', 'userId', 'companyId', 'companyUserId'].forEach(key => {
|
|
|
|
|
+ if (params[key] === '' || params[key] === undefined) {
|
|
|
|
|
+ params[key] = null
|
|
|
|
|
+ } else if (params[key] != null) {
|
|
|
|
|
+ params[key] = Number(params[key])
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ if (params.liveName === '') params.liveName = null
|
|
|
|
|
+ if (params.userName === '') params.userName = null
|
|
|
|
|
+ return params
|
|
|
|
|
+ },
|
|
|
|
|
+ getList() {
|
|
|
|
|
+ this.loading = true
|
|
|
|
|
+ liveWatchUserList(this.buildQuery()).then(res => {
|
|
|
|
|
+ this.list = res.data || []
|
|
|
|
|
+ this.total = res.total || 0
|
|
|
|
|
+ }).finally(() => {
|
|
|
|
|
+ this.loading = false
|
|
|
|
|
+ })
|
|
|
|
|
+ },
|
|
|
handleQuery() {
|
|
handleQuery() {
|
|
|
|
|
+ this.queryParams.pageNum = 1
|
|
|
|
|
+ this.getList()
|
|
|
},
|
|
},
|
|
|
resetQuery() {
|
|
resetQuery() {
|
|
|
|
|
+ this.dateRange = []
|
|
|
|
|
+ this.resetForm('queryForm')
|
|
|
|
|
+ this.queryParams.pageNum = 1
|
|
|
|
|
+ this.queryParams.pageSize = 10
|
|
|
|
|
+ this.handleQuery()
|
|
|
|
|
+ },
|
|
|
|
|
+ formatDuration(seconds) {
|
|
|
|
|
+ const s = Number(seconds) || 0
|
|
|
|
|
+ if (s < 60) return s + '秒'
|
|
|
|
|
+ const m = Math.floor(s / 60)
|
|
|
|
|
+ const remain = s % 60
|
|
|
|
|
+ if (m < 60) return remain ? `${m}分${remain}秒` : `${m}分钟`
|
|
|
|
|
+ const h = Math.floor(m / 60)
|
|
|
|
|
+ const rm = m % 60
|
|
|
|
|
+ return rm ? `${h}小时${rm}分` : `${h}小时`
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
|
-<style scoped>
|
|
|
|
|
|
|
+<style lang="scss" scoped>
|
|
|
|
|
+.dialog-body {
|
|
|
|
|
+ height: 400px;
|
|
|
|
|
+ overflow: auto;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.tag-container {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-wrap: wrap;
|
|
|
|
|
+ gap: 8px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.name-background {
|
|
|
|
|
+ display: inline-block;
|
|
|
|
|
+ background-color: #abece6;
|
|
|
|
|
+ padding: 4px 8px;
|
|
|
|
|
+ border-radius: 4px;
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
|
|
+.tag-box {
|
|
|
|
|
+ padding: 8px 12px;
|
|
|
|
|
+ border: 1px solid #989797;
|
|
|
|
|
+ border-radius: 4px;
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+ display: inline-block;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.tag-selected {
|
|
|
|
|
+ background-color: #00bc98;
|
|
|
|
|
+ color: #fff;
|
|
|
|
|
+ border-color: #00bc98;
|
|
|
|
|
+}
|
|
|
</style>
|
|
</style>
|