index.vue 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224
  1. <template>
  2. <div class="el-container-md">
  3. <!-- 数据统计指标展示区域 -->
  4. <el-card class="statistics-card" shadow="never">
  5. <el-row :gutter="20">
  6. <el-col :span="4">
  7. <div class="statistics-item">
  8. <div class="statistics-title">累计观看人数</div>
  9. <div class="statistics-value">{{ statisticsData.totalViewers || 0 }}</div>
  10. </div>
  11. </el-col>
  12. <el-col :span="4">
  13. <div class="statistics-item">
  14. <div class="statistics-title">
  15. 累计完课人数
  16. <el-popover placement="top" width="200" trigger="click">
  17. <div>
  18. <el-input-number v-model="completeWatchTime" :min="0" :precision="0" placeholder="看课时长(分钟)" style="width: 100%;"></el-input-number>
  19. <el-button type="primary" size="mini" style="width: 100%; margin-top: 10px;" @click="updateCompleteWatchTime('total')">确定</el-button>
  20. </div>
  21. <el-button slot="reference" size="mini" type="text" icon="el-icon-setting" style="margin-left: 5px;">设置</el-button>
  22. </el-popover>
  23. </div>
  24. <div class="statistics-value">{{ statisticsData.totalCompletedCourses || 0 }}</div>
  25. </div>
  26. </el-col>
  27. <el-col :span="4">
  28. <div class="statistics-item">
  29. <div class="statistics-title">直播观看人数</div>
  30. <div class="statistics-value">{{ statisticsData.liveViewers || 0 }}</div>
  31. </div>
  32. </el-col>
  33. <el-col :span="4">
  34. <div class="statistics-item">
  35. <div class="statistics-title">
  36. 直播完课人数
  37. <el-popover placement="top" width="200" trigger="click">
  38. <div>
  39. <el-input-number v-model="liveCompleteWatchTime" :min="0" :precision="0" placeholder="看课时长(分钟)" style="width: 100%;"></el-input-number>
  40. <el-button type="primary" size="mini" style="width: 100%; margin-top: 10px;" @click="updateCompleteWatchTime('live')">确定</el-button>
  41. </div>
  42. <el-button slot="reference" size="mini" type="text" icon="el-icon-setting" style="margin-left: 5px;">设置</el-button>
  43. </el-popover>
  44. </div>
  45. <div class="statistics-value">{{ statisticsData.liveCompletedCourses || 0 }}</div>
  46. </div>
  47. </el-col>
  48. <el-col :span="4">
  49. <div class="statistics-item">
  50. <div class="statistics-title">回放观看人数</div>
  51. <div class="statistics-value">{{ statisticsData.playbackViewers || 0 }}</div>
  52. </div>
  53. </el-col>
  54. <el-col :span="4">
  55. <div class="statistics-item">
  56. <div class="statistics-title">
  57. 回放完课人数
  58. <el-popover placement="top" width="200" trigger="click">
  59. <div>
  60. <el-input-number v-model="replayCompleteWatchTime" :min="0" :precision="0" placeholder="看课时长(分钟)" style="width: 100%;"></el-input-number>
  61. <el-button type="primary" size="mini" style="width: 100%; margin-top: 10px;" @click="updateCompleteWatchTime('replay')">确定</el-button>
  62. </div>
  63. <el-button slot="reference" size="mini" type="text" icon="el-icon-setting" style="margin-left: 5px;">设置</el-button>
  64. </el-popover>
  65. </div>
  66. <div class="statistics-value">{{ statisticsData.playbackCompletedCourses || 0 }}</div>
  67. </div>
  68. </el-col>
  69. </el-row>
  70. <el-row :gutter="20" style="margin-top: 20px;">
  71. <el-col :span="4">
  72. <div class="statistics-item">
  73. <div class="statistics-title">直播峰值</div>
  74. <div class="statistics-value">{{ statisticsData.livePeak || 0 }}</div>
  75. </div>
  76. </el-col>
  77. <el-col :span="4">
  78. <div class="statistics-item">
  79. <div class="statistics-title">直播平均时长</div>
  80. <div class="statistics-value">{{ formatDuration(statisticsData.liveAvgDuration) }}</div>
  81. </div>
  82. </el-col>
  83. <el-col :span="4">
  84. <div class="statistics-item">
  85. <div class="statistics-title">回放平均时长</div>
  86. <div class="statistics-value">{{ formatDuration(statisticsData.playbackAvgDuration) }}</div>
  87. </div>
  88. </el-col>
  89. <el-col :span="4">
  90. <div class="statistics-item">
  91. <div class="statistics-title">GMV</div>
  92. <div class="statistics-value">¥{{ statisticsData.gmv || 0 }}</div>
  93. </div>
  94. </el-col>
  95. <el-col :span="4">
  96. <div class="statistics-item">
  97. <div class="statistics-title">付费人数</div>
  98. <div class="statistics-value">{{ statisticsData.paidUsers || 0 }}</div>
  99. </div>
  100. </el-col>
  101. <el-col :span="4">
  102. <div class="statistics-item">
  103. <div class="statistics-title">付费单数</div>
  104. <div class="statistics-value">{{ statisticsData.paidOrders || 0 }}</div>
  105. </div>
  106. </el-col>
  107. </el-row>
  108. <el-row :gutter="20" style="margin-top: 20px;">
  109. <el-col :span="4">
  110. <div class="statistics-item">
  111. <div class="statistics-title">销量统计</div>
  112. <div class="statistics-value">{{ statisticsData.salesCount || 0 }}</div>
  113. </div>
  114. </el-col>
  115. </el-row>
  116. </el-card>
  117. <!-- 筛选条件区域 -->
  118. <el-form :model="queryParams" class="live-data-css" ref="queryForm" :inline="true" v-show="showSearch" label-width="100px">
  119. <el-form-item label="直播名称" prop="liveName">
  120. <el-input
  121. v-model="queryParams.liveName"
  122. placeholder="请输入直播名称"
  123. clearable
  124. size="small"
  125. @keyup.enter.native="handleQuery"
  126. />
  127. </el-form-item>
  128. <el-form-item label="观看类型" prop="watchType">
  129. <el-select v-model="queryParams.watchType" placeholder="请选择观看类型" clearable size="small">
  130. <el-option label="直播" value="live"></el-option>
  131. <el-option label="回放" value="replay"></el-option>
  132. </el-select>
  133. </el-form-item>
  134. <el-form-item label="完课状态" prop="completeStatus">
  135. <el-select v-model="queryParams.completeStatus" placeholder="请选择完课状态" clearable size="small">
  136. <el-option label="已完课" value="1"></el-option>
  137. <el-option label="未完课" value="0"></el-option>
  138. </el-select>
  139. </el-form-item>
  140. <el-form-item label="付费状态" prop="payStatus">
  141. <el-select v-model="queryParams.payStatus" placeholder="请选择付费状态" clearable size="small">
  142. <el-option label="已付费" value="1"></el-option>
  143. <el-option label="未付费" value="0"></el-option>
  144. </el-select>
  145. </el-form-item>
  146. <el-form-item label="观看时长" prop="watchDuration">
  147. <el-input-number
  148. v-model="queryParams.watchDuration"
  149. :min="0"
  150. :precision="0"
  151. placeholder="观看时长(分钟)"
  152. size="small"
  153. ></el-input-number>
  154. </el-form-item>
  155. <el-form-item label="开始时间范围" prop="dateRange">
  156. <el-date-picker
  157. v-model="dateRange"
  158. type="datetimerange"
  159. range-separator="至"
  160. start-placeholder="开始日期"
  161. end-placeholder="结束日期"
  162. size="small"
  163. value-format="yyyy-MM-dd HH:mm:ss"
  164. ></el-date-picker>
  165. </el-form-item>
  166. <el-form-item>
  167. <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
  168. <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
  169. </el-form-item>
  170. </el-form>
  171. <!-- 操作工具栏 -->
  172. <div class="selection-toolbar">
  173. <el-checkbox :indeterminate="isIndeterminate" v-model="allChecked" @change="toggleSelectAll">
  174. {{ multipleSelection.length > 0 ? `已选 ${multipleSelection.length} 条` : '选中本页' }}
  175. </el-checkbox>
  176. <!-- <el-button plain size="mini" @click="handleAutoTag">自动打标签</el-button>-->
  177. <!-- <el-button plain size="mini" @click="handleAutoRemark">自动备注</el-button>-->
  178. <el-button plain type="primary" size="mini" icon="el-icon-download" :loading="exportLoading" @click="handleExport">导出</el-button>
  179. </div>
  180. <!-- 数据表格 -->
  181. <el-table
  182. ref="dataTable"
  183. :data="dataList"
  184. style="width: 100%"
  185. v-loading="loading"
  186. @selection-change="handleSelectionChange"
  187. >
  188. <el-table-column type="selection" width="55"></el-table-column>
  189. <el-table-column type="index" label="序号" width="55" align="center"></el-table-column>
  190. <el-table-column prop="liveId" label="直播间编码" width="90" ></el-table-column>
  191. <el-table-column prop="liveName" label="直播间名称" min-width="160" show-overflow-tooltip></el-table-column>
  192. <el-table-column prop="liveType" label="直播类型" width="100" align="center">
  193. <template slot-scope="scope">
  194. <el-tag type="danger" v-if="scope.row.liveType == 1">直播</el-tag>
  195. <el-tag type="success" v-else-if="scope.row.liveType == 2">录播</el-tag>
  196. <el-tag v-else-if="scope.row.liveType == 3">直播回放</el-tag>
  197. <span v-else>-</span>
  198. </template>
  199. </el-table-column>
  200. <el-table-column prop="status" label="状态" width="100" align="center">
  201. <template slot-scope="scope">
  202. <el-tag type="info" v-if="scope.row.status == 1">未开始</el-tag>
  203. <el-tag type="warning" v-else-if="scope.row.status == 2">进行中</el-tag>
  204. <el-tag type="success" v-else-if="scope.row.status == 3">已结束</el-tag>
  205. <span v-else>-</span>
  206. </template>
  207. </el-table-column>
  208. <el-table-column prop="startTime" label="开始时间" width="180" align="center"></el-table-column>
  209. <el-table-column prop="finishTime" label="结束时间" width="180" align="center"></el-table-column>
  210. <el-table-column prop="totalViewers" label="累计观看" width="110" align="center"></el-table-column>
  211. <el-table-column prop="liveViewers" label="直播观看" width="110" align="center"></el-table-column>
  212. <el-table-column prop="playbackViewers" label="回放观看" width="110" align="center"></el-table-column>
  213. <el-table-column prop="liveAvgDuration" label="直播平均时长" width="140" align="center">
  214. <template slot-scope="scope">{{ formatDuration(scope.row.liveAvgDuration) }}</template>
  215. </el-table-column>
  216. <el-table-column prop="playbackAvgDuration" label="回放平均时长" width="140" align="center">
  217. <template slot-scope="scope">{{ formatDuration(scope.row.playbackAvgDuration) }}</template>
  218. </el-table-column>
  219. <el-table-column prop="totalCompletedCourses" label="累计完课" width="110" align="center"></el-table-column>
  220. <el-table-column prop="liveCompletedCourses" label="直播完课" width="110" align="center"></el-table-column>
  221. <el-table-column prop="playbackCompletedCourses" label="回放完课" width="110" align="center"></el-table-column>
  222. <el-table-column prop="gmv" label="GMV" width="120" align="center">
  223. <template slot-scope="scope">¥{{ scope.row.gmv || 0 }}</template>
  224. </el-table-column>
  225. <el-table-column prop="paidUsers" label="付费人数" width="100" align="center"></el-table-column>
  226. <el-table-column prop="paidOrders" label="付费单数" width="100" align="center"></el-table-column>
  227. <el-table-column prop="salesCount" label="销量统计" width="100" align="center"></el-table-column>
  228. <el-table-column label="操作" width="120" fixed="right">
  229. <template slot-scope="scope">
  230. <el-button type="text" size="small" @click="handleViewDetail(scope.row)">查看详情</el-button>
  231. </template>
  232. </el-table-column>
  233. </el-table>
  234. <!-- 分页组件 -->
  235. <pagination
  236. v-show="total > 0"
  237. :total="total"
  238. :page.sync="queryParams.pageNum"
  239. :limit.sync="queryParams.pageSize"
  240. @pagination="getList"
  241. style="margin-top: 20px;"
  242. />
  243. <!-- 自动打标签弹窗 -->
  244. <el-dialog
  245. title="自动打标签"
  246. :visible.sync="tagDialogVisible"
  247. width="600px"
  248. :close-on-click-modal="false"
  249. >
  250. <el-form :model="tagForm" label-width="120px">
  251. <el-form-item label="打标签规则">
  252. <el-checkbox-group v-model="tagForm.rules">
  253. <el-checkbox label="liveComplete">直播完课</el-checkbox>
  254. <el-checkbox label="replayComplete">回放完课</el-checkbox>
  255. <el-checkbox label="pay">付费行为</el-checkbox>
  256. </el-checkbox-group>
  257. </el-form-item>
  258. <el-form-item label="标签名称">
  259. <el-input v-model="tagForm.tagName" placeholder="请输入标签名称"></el-input>
  260. </el-form-item>
  261. <el-form-item label="备注格式">
  262. <el-radio-group v-model="tagForm.remarkFormat">
  263. <el-radio label="time">时间—行为(如:2024-01-01—直播完课)</el-radio>
  264. <el-radio label="simple">仅行为(如:直播完课)</el-radio>
  265. </el-radio-group>
  266. </el-form-item>
  267. </el-form>
  268. <div slot="footer" class="dialog-footer">
  269. <el-button @click="tagDialogVisible = false">取 消</el-button>
  270. <el-button type="primary" @click="confirmAutoTag">确 定</el-button>
  271. </div>
  272. </el-dialog>
  273. <!-- 自动备注弹窗 -->
  274. <el-dialog
  275. title="自动备注"
  276. :visible.sync="remarkDialogVisible"
  277. width="600px"
  278. :close-on-click-modal="false"
  279. >
  280. <el-form :model="remarkForm" label-width="120px">
  281. <el-form-item label="备注规则">
  282. <el-checkbox-group v-model="remarkForm.rules">
  283. <el-checkbox label="liveComplete">直播完课</el-checkbox>
  284. <el-checkbox label="replayComplete">回放完课</el-checkbox>
  285. <el-checkbox label="pay">付费行为</el-checkbox>
  286. </el-checkbox-group>
  287. </el-form-item>
  288. <el-form-item label="备注格式">
  289. <el-radio-group v-model="remarkForm.remarkFormat">
  290. <el-radio label="time">时间—行为(如:2024-01-01—直播完课)</el-radio>
  291. <el-radio label="simple">仅行为(如:直播完课)</el-radio>
  292. </el-radio-group>
  293. </el-form-item>
  294. <el-form-item label="备注位置">
  295. <el-radio-group v-model="remarkForm.position">
  296. <el-radio :label="1">添加在最前面</el-radio>
  297. <el-radio :label="2">添加在最后面</el-radio>
  298. <el-radio :label="3">替换所有备注</el-radio>
  299. </el-radio-group>
  300. </el-form-item>
  301. </el-form>
  302. <div slot="footer" class="dialog-footer">
  303. <el-button @click="remarkDialogVisible = false">取 消</el-button>
  304. <el-button type="primary" @click="confirmAutoRemark">确 定</el-button>
  305. </div>
  306. </el-dialog>
  307. <!-- 详情侧边栏 -->
  308. <el-drawer
  309. title="直播间详情"
  310. :visible.sync="detailDrawerVisible"
  311. direction="rtl"
  312. size="80%"
  313. :before-close="closeDetailDrawer"
  314. >
  315. <div v-if="!showUserDetail" class="detail-content">
  316. <el-card shadow="never" style="margin-bottom: 20px;">
  317. <div slot="header">
  318. <span>总体数据</span>
  319. <el-button style="float: right; padding: 3px 0" type="text" @click="handleViewUserDetail">查看用户详情</el-button>
  320. </div>
  321. <el-row :gutter="20">
  322. <el-col :span="12">
  323. <div class="detail-item">
  324. <span class="detail-label">视频时长:</span>
  325. <span class="detail-value">{{ formatDuration(detailData.videoDuration || 0) }}</span>
  326. </div>
  327. </el-col>
  328. <el-col :span="12">
  329. <div class="detail-item">
  330. <span class="detail-label">累计观看人数:</span>
  331. <span class="detail-value">{{ detailData.totalViewers || 0 }}</span>
  332. </div>
  333. </el-col>
  334. <el-col :span="12">
  335. <div class="detail-item">
  336. <span class="detail-label">累计完课人数:</span>
  337. <span class="detail-value">{{ detailData.totalCompletedCourses || 0 }}</span>
  338. </div>
  339. </el-col>
  340. <el-col :span="12">
  341. <div class="detail-item">
  342. <span class="detail-label">到课完课率:</span>
  343. <span class="detail-value">{{ formatPercent(detailData.totalCompletionRate) }}</span>
  344. </div>
  345. </el-col>
  346. </el-row>
  347. </el-card>
  348. <el-card shadow="never" style="margin-bottom: 20px;">
  349. <div slot="header">直播数据</div>
  350. <el-row :gutter="20">
  351. <el-col :span="12">
  352. <div class="detail-item">
  353. <span class="detail-label">直播观看人数:</span>
  354. <span class="detail-value">{{ detailData.liveViewers || 0 }}</span>
  355. </div>
  356. </el-col>
  357. <el-col :span="12">
  358. <div class="detail-item">
  359. <span class="detail-label">>=20分钟人数(直播):</span>
  360. <span class="detail-value">{{ detailData.liveOver20Minutes || 0 }}</span>
  361. </div>
  362. </el-col>
  363. <el-col :span="12">
  364. <div class="detail-item">
  365. <span class="detail-label">>=30分钟人数(直播):</span>
  366. <span class="detail-value">{{ detailData.liveOver30Minutes || 0 }}</span>
  367. </div>
  368. </el-col>
  369. <el-col :span="12">
  370. <div class="detail-item">
  371. <span class="detail-label">到课完课率直播(>=20分钟):</span>
  372. <span class="detail-value">{{ formatPercent(detailData.liveCompletionRate20) }}</span>
  373. </div>
  374. </el-col>
  375. <el-col :span="12">
  376. <div class="detail-item">
  377. <span class="detail-label">到课完课率直播(>=30分钟):</span>
  378. <span class="detail-value">{{ formatPercent(detailData.liveCompletionRate30) }}</span>
  379. </div>
  380. </el-col>
  381. </el-row>
  382. </el-card>
  383. <el-card shadow="never" style="margin-bottom: 20px;">
  384. <div slot="header">回放数据</div>
  385. <el-row :gutter="20">
  386. <el-col :span="12">
  387. <div class="detail-item">
  388. <span class="detail-label">回放观看人数:</span>
  389. <span class="detail-value">{{ detailData.playbackViewers || 0 }}</span>
  390. </div>
  391. </el-col>
  392. <el-col :span="12">
  393. <div class="detail-item">
  394. <span class="detail-label">>=20分钟人数(回放):</span>
  395. <span class="detail-value">{{ detailData.playbackOver20Minutes || 0 }}</span>
  396. </div>
  397. </el-col>
  398. <el-col :span="12">
  399. <div class="detail-item">
  400. <span class="detail-label">>=30分钟人数(回放):</span>
  401. <span class="detail-value">{{ detailData.playbackOver30Minutes || 0 }}</span>
  402. </div>
  403. </el-col>
  404. <el-col :span="12">
  405. <div class="detail-item">
  406. <span class="detail-label">到课完课率回放(>=20分钟):</span>
  407. <span class="detail-value">{{ formatPercent(detailData.playbackCompletionRate20) }}</span>
  408. </div>
  409. </el-col>
  410. <el-col :span="12">
  411. <div class="detail-item">
  412. <span class="detail-label">到课完课率回放(>=30分钟):</span>
  413. <span class="detail-value">{{ formatPercent(detailData.playbackCompletionRate30) }}</span>
  414. </div>
  415. </el-col>
  416. </el-row>
  417. </el-card>
  418. <el-card shadow="never" style="margin-bottom: 20px;">
  419. <div slot="header">时长统计</div>
  420. <el-row :gutter="20">
  421. <el-col :span="12">
  422. <div class="detail-item">
  423. <span class="detail-label">直播峰值:</span>
  424. <span class="detail-value">{{ detailData.livePeak || 0 }}</span>
  425. </div>
  426. </el-col>
  427. <el-col :span="12">
  428. <div class="detail-item">
  429. <span class="detail-label">直播平均时长:</span>
  430. <span class="detail-value">{{ formatDuration(detailData.liveAvgDuration || 0) }}</span>
  431. </div>
  432. </el-col>
  433. <el-col :span="12">
  434. <div class="detail-item">
  435. <span class="detail-label">回放平均时长:</span>
  436. <span class="detail-value">{{ formatDuration(detailData.playbackAvgDuration || 0) }}</span>
  437. </div>
  438. </el-col>
  439. <el-col :span="12">
  440. <div class="detail-item">
  441. <span class="detail-label">回放完播率:</span>
  442. <span class="detail-value">{{ formatPercent(detailData.playbackFinishRate) }}</span>
  443. </div>
  444. </el-col>
  445. </el-row>
  446. </el-card>
  447. <el-card shadow="never" style="margin-bottom: 20px;">
  448. <div slot="header">订单数据</div>
  449. <el-row :gutter="20">
  450. <el-col :span="12">
  451. <div class="detail-item">
  452. <span class="detail-label">GMV:</span>
  453. <span class="detail-value">{{ formatMoney(detailData.gmv) }}</span>
  454. </div>
  455. </el-col>
  456. <el-col :span="12">
  457. <div class="detail-item">
  458. <span class="detail-label">付费人数:</span>
  459. <span class="detail-value">{{ detailData.paidUsers || 0 }}</span>
  460. </div>
  461. </el-col>
  462. <el-col :span="12">
  463. <div class="detail-item">
  464. <span class="detail-label">付费单数:</span>
  465. <span class="detail-value">{{ detailData.paidOrders || 0 }}</span>
  466. </div>
  467. </el-col>
  468. <el-col :span="12">
  469. <div class="detail-item">
  470. <span class="detail-label">峰值转化率:</span>
  471. <span class="detail-value">{{ formatPercent(detailData.peakConversionRate) }}</span>
  472. </div>
  473. </el-col>
  474. <el-col :span="12">
  475. <div class="detail-item">
  476. <span class="detail-label">总到课转化率:</span>
  477. <span class="detail-value">{{ formatPercent(detailData.totalViewerConversionRate) }}</span>
  478. </div>
  479. </el-col>
  480. <el-col :span="12">
  481. <div class="detail-item">
  482. <span class="detail-label">30min完课转化率:</span>
  483. <span class="detail-value">{{ formatPercent(detailData.completion30MinConversionRate) }}</span>
  484. </div>
  485. </el-col>
  486. <el-col :span="12">
  487. <div class="detail-item">
  488. <span class="detail-label">峰值R值:</span>
  489. <span class="detail-value">{{ formatMoney(detailData.peakRValue) }}</span>
  490. </div>
  491. </el-col>
  492. <el-col :span="12">
  493. <div class="detail-item">
  494. <span class="detail-label">完课R值:</span>
  495. <span class="detail-value">{{ formatMoney(detailData.completionRValue) }}</span>
  496. </div>
  497. </el-col>
  498. </el-row>
  499. </el-card>
  500. <el-card shadow="never" v-if="detailData.productSalesList && detailData.productSalesList.length > 0">
  501. <div slot="header">单品销量统计</div>
  502. <el-table :data="detailData.productSalesList" border>
  503. <el-table-column prop="productName" label="商品名称" min-width="200"></el-table-column>
  504. <el-table-column prop="salesCount" label="销量" width="100" align="center"></el-table-column>
  505. <el-table-column prop="salesAmount" label="销售额" width="150" align="center">
  506. <template slot-scope="scope">
  507. {{ formatMoney(scope.row.salesAmount) }}
  508. </template>
  509. </el-table-column>
  510. </el-table>
  511. </el-card>
  512. </div>
  513. <!-- 用户详情列表 -->
  514. <div v-else class="user-detail-content">
  515. <div style="margin-bottom: 20px; display: flex; justify-content: space-between; align-items: center;">
  516. <el-button type="text" icon="el-icon-arrow-left" @click="showUserDetail = false">返回</el-button>
  517. <el-button type="primary" size="small" icon="el-icon-download" :loading="userDetailExportLoading" @click="handleExportUserDetail">导出用户详情</el-button>
  518. </div>
  519. <el-table
  520. :data="userDetailList"
  521. v-loading="userDetailLoading"
  522. border
  523. style="width: 100%"
  524. >
  525. <el-table-column type="index" label="序号" width="60" align="center"></el-table-column>
  526. <el-table-column prop="userName" label="用户名称" min-width="120"></el-table-column>
  527. <el-table-column prop="liveWatchDuration" label="直播观看时长" width="140" align="center">
  528. <template slot-scope="scope">
  529. {{ formatDuration(scope.row.liveWatchDuration || 0) }}
  530. </template>
  531. </el-table-column>
  532. <el-table-column prop="playbackWatchDuration" label="回放观看时长" width="140" align="center">
  533. <template slot-scope="scope">
  534. {{ formatDuration(scope.row.playbackWatchDuration || 0) }}
  535. </template>
  536. </el-table-column>
  537. <el-table-column prop="orderCount" label="订单数" width="100" align="center"></el-table-column>
  538. <el-table-column prop="orderAmount" label="订单金额" width="120" align="center">
  539. <template slot-scope="scope">
  540. {{ formatMoney(scope.row.orderAmount) }}
  541. </template>
  542. </el-table-column>
  543. <el-table-column prop="companyName" label="分公司" min-width="150"></el-table-column>
  544. <el-table-column prop="salesName" label="销售" min-width="120"></el-table-column>
  545. <el-table-column prop="isCompleted" label="是否完课" min-width="120">
  546. <template slot-scope="scope">
  547. <el-tag v-if="scope.row.isCompleted == 1" type="success">完课</el-tag>
  548. <el-tag v-if="scope.row.isCompleted == 0" type="info">未完课</el-tag>
  549. </template>
  550. </el-table-column>
  551. <el-table-column prop="rewardType" label="领取类型" min-width="160">
  552. <template slot-scope="scope">
  553. <dict-tag :options="liveRewardTypeList" :value="scope.row.rewardType"/>
  554. <el-button
  555. v-if="scope.row.rewardType != 4 && scope.row.rewardType != 99"
  556. type="text"
  557. size="small"
  558. style="margin-left: 8px;"
  559. @click="handleOpenRedPacketLog(scope.row)"
  560. >红包/积分领取详细</el-button>
  561. <el-button
  562. v-if="scope.row.rewardType == 4 || scope.row.rewardType == 5 || scope.row.rewardType == 6 || scope.row.rewardType == 7"
  563. type="text"
  564. size="small"
  565. style="margin-left: 8px;"
  566. @click="handleOpenCouponLog(scope.row)"
  567. >核销卷领取详情</el-button>
  568. </template>
  569. </el-table-column>
  570. </el-table>
  571. <!-- 用户详情分页组件 -->
  572. <pagination
  573. v-show="userDetailTotal > 0"
  574. :total="userDetailTotal"
  575. :page.sync="userDetailQueryParams.pageNum"
  576. :limit.sync="userDetailQueryParams.pageSize"
  577. :page-sizes="[10, 100, 1000]"
  578. @pagination="handleViewUserDetail"
  579. style="margin-top: 20px;"
  580. />
  581. </div>
  582. </el-drawer>
  583. <!-- 红包领取详情侧边栏 -->
  584. <el-drawer
  585. title="红包领取详情"
  586. :visible.sync="redPacketLogDrawerVisible"
  587. direction="rtl"
  588. size="60%"
  589. >
  590. <el-table
  591. :data="redPacketLogList"
  592. v-loading="redPacketLogLoading"
  593. border
  594. style="width: 100%"
  595. >
  596. <el-table-column type="index" label="序号" width="60" align="center"></el-table-column>
  597. <el-table-column prop="liveId" label="直播间id" min-width="120"></el-table-column>
  598. <el-table-column prop="userId" label="用户id" min-width="120"></el-table-column>
  599. <el-table-column prop="amount" label="金额" min-width="120" align="center">
  600. <template slot-scope="scope">
  601. {{ scope.row.amount != null ? scope.row.amount : '-' }}
  602. </template>
  603. </el-table-column>
  604. <el-table-column prop="status" label="状态" min-width="100" align="center">
  605. <template slot-scope="scope">
  606. <el-tag v-if="scope.row.status == 1" type="success">已领取</el-tag>
  607. <el-tag v-else-if="scope.row.status == 0" type="info">未领取</el-tag>
  608. <el-tag v-else type="info">{{ scope.row.status }}</el-tag>
  609. </template>
  610. </el-table-column>
  611. <el-table-column prop="createTime" label="领取时间" min-width="160" align="center"></el-table-column>
  612. <el-table-column prop="remark" label="备注" min-width="160" align="center"></el-table-column>
  613. </el-table>
  614. <pagination
  615. v-show="redPacketLogTotal > 0"
  616. :total="redPacketLogTotal"
  617. :page.sync="redPacketLogQueryParams.pageNum"
  618. :limit.sync="redPacketLogQueryParams.pageSize"
  619. :page-sizes="[10, 20, 50]"
  620. @pagination="loadRedPacketLogList"
  621. style="margin-top: 20px;"
  622. />
  623. </el-drawer>
  624. <!-- 核销卷领取详情侧边栏 -->
  625. <el-drawer
  626. title="核销卷领取详情"
  627. :visible.sync="couponLogDrawerVisible"
  628. direction="rtl"
  629. size="60%"
  630. >
  631. <el-table
  632. :data="couponLogList"
  633. v-loading="couponLogLoading"
  634. border
  635. style="width: 100%"
  636. >
  637. <el-table-column type="index" label="序号" width="60" align="center"></el-table-column>
  638. <el-table-column prop="nickname" label="会员昵称" min-width="120"></el-table-column>
  639. <el-table-column prop="phone" label="会员手机号" min-width="120"></el-table-column>
  640. <el-table-column prop="couponTitle" label="优惠券名称" min-width="120"></el-table-column>
  641. <el-table-column prop="couponPrice" label="优惠券面值" min-width="100" align="center"></el-table-column>
  642. <el-table-column prop="useMinPrice" label="最低消费" min-width="100" align="center"></el-table-column>
  643. <el-table-column prop="limitTime" label="优惠券结束时间" min-width="160" align="center"></el-table-column>
  644. <el-table-column prop="createTime" label="领取时间" min-width="160" align="center"></el-table-column>
  645. <el-table-column prop="useTime" label="使用时间" min-width="160" align="center"></el-table-column>
  646. <el-table-column prop="status" label="状态" min-width="100" align="center">
  647. <template slot-scope="scope">
  648. <el-tag v-if="scope.row.status == 1" type="success">已使用</el-tag>
  649. <el-tag v-else-if="scope.row.status == 0" type="info">未使用</el-tag>
  650. <el-tag v-else type="info">{{ scope.row.status }}</el-tag>
  651. </template>
  652. </el-table-column>
  653. </el-table>
  654. <pagination
  655. v-show="couponLogTotal > 0"
  656. :total="couponLogTotal"
  657. :page.sync="couponLogQueryParams.pageNum"
  658. :limit.sync="couponLogQueryParams.pageSize"
  659. :page-sizes="[10, 20, 50]"
  660. @pagination="loadCouponLogList"
  661. style="margin-top: 20px;"
  662. />
  663. </el-drawer>
  664. </div>
  665. </template>
  666. <script>
  667. import { listLiveData, exportLiveData, autoTagAndRemark, dashboardData, getLiveDataDetailBySql, getLiveUserDetailListBySql, exportLiveUserDetail } from "@/api/live/liveData";
  668. import { batchUpdateExternalContactNotes } from "@/api/qw/externalContact";
  669. import { addTag } from "@/api/qw/externalContact";
  670. import { listLiveRedPacketLog } from "@/api/live/liveRedPacketLog";
  671. import { listStoreCouponUser } from "@/api/live/liveCouponUser";
  672. import { selectDictLabel } from '@/utils/common'
  673. export default {
  674. name: "LiveData",
  675. data() {
  676. return {
  677. liveId: '',
  678. loading: true,
  679. exportLoading: false,
  680. showSearch: true,
  681. dataList: [],
  682. liveRewardTypeList: [],
  683. total: 0,
  684. multipleSelection: [],
  685. allChecked: false,
  686. isIndeterminate: false,
  687. dateRange: [],
  688. // 统计数据
  689. statisticsData: {
  690. gmv: 0,
  691. liveAvgDuration: 0,
  692. liveCompletedCourses: 0,
  693. livePeak: 0,
  694. liveViewers: 0,
  695. paidOrders: 0,
  696. paidUsers: 0,
  697. playbackAvgDuration: 0,
  698. playbackCompletedCourses: 0,
  699. playbackViewers: 0,
  700. salesCount: 0,
  701. totalCompletedCourses: 0,
  702. totalViewers: 1
  703. },
  704. // 完课时长设置
  705. completeWatchTime: 0,
  706. liveCompleteWatchTime: 0,
  707. replayCompleteWatchTime: 0,
  708. // 查询参数
  709. queryParams: {
  710. pageNum: 1,
  711. pageSize: 10,
  712. liveId: null,
  713. liveName: null,
  714. watchType: null,
  715. completeStatus: null,
  716. payStatus: null,
  717. watchDuration: null,
  718. startTime: null,
  719. endTime: null
  720. },
  721. // 打标签弹窗
  722. tagDialogVisible: false,
  723. tagForm: {
  724. rules: [],
  725. tagName: '',
  726. remarkFormat: 'time'
  727. },
  728. // 备注弹窗
  729. remarkDialogVisible: false,
  730. remarkForm: {
  731. rules: [],
  732. remarkFormat: 'time',
  733. position: 1
  734. },
  735. // 详情侧边栏
  736. detailDrawerVisible: false,
  737. currentLiveId: null,
  738. detailData: {},
  739. userDetailList: [],
  740. userDetailLoading: false,
  741. showUserDetail: false,
  742. userDetailExportLoading: false,
  743. userDetailTotal: 0,
  744. userDetailQueryParams: {
  745. pageNum: 1,
  746. pageSize: 10
  747. },
  748. // 红包领取详情
  749. redPacketLogDrawerVisible: false,
  750. redPacketLogList: [],
  751. redPacketLogLoading: false,
  752. redPacketLogTotal: 0,
  753. redPacketLogQueryParams: {
  754. pageNum: 1,
  755. pageSize: 10,
  756. liveId: null,
  757. userId: null
  758. },
  759. // 核销卷领取详情
  760. couponLogDrawerVisible: false,
  761. couponLogList: [],
  762. couponLogLoading: false,
  763. couponLogTotal: 0,
  764. couponLogQueryParams: {
  765. pageNum: 1,
  766. pageSize: 10,
  767. userId: null,
  768. type: null
  769. }
  770. };
  771. },
  772. created() {
  773. this.liveId = this.$route.query.liveId;
  774. this.queryParams.liveId = this.liveId;
  775. this.getDicts("sys_live_reward_type").then((response) => {
  776. this.liveRewardTypeList = response.data;
  777. });
  778. this.getList();
  779. },
  780. methods: {
  781. selectDictLabel,
  782. /** 获取统计数据 */
  783. getStatistics() {
  784. if (!this.liveId) return;
  785. dashboardData(this.liveId).then(response => {
  786. if (response.code === 200) {
  787. this.statisticsData = response.data || {};
  788. }
  789. });
  790. },
  791. /** 获取列表数据 */
  792. getList() {
  793. this.loading = true;
  794. // 处理时间范围
  795. if (this.dateRange && this.dateRange.length === 2) {
  796. this.queryParams.startTime = this.dateRange[0];
  797. this.queryParams.endTime = this.dateRange[1];
  798. } else {
  799. this.queryParams.startTime = null;
  800. this.queryParams.endTime = null;
  801. }
  802. listLiveData(this.queryParams).then(response => {
  803. if (response.code === 200) {
  804. this.statisticsData = response.data || {};
  805. this.dataList = response.list || [];
  806. this.total = response.total || 0;
  807. }
  808. this.loading = false;
  809. }).catch(() => {
  810. this.loading = false;
  811. });
  812. },
  813. /** 搜索按钮操作 */
  814. handleQuery() {
  815. this.queryParams.pageNum = 1;
  816. this.getList();
  817. },
  818. /** 重置按钮操作 */
  819. resetQuery() {
  820. this.dateRange = [];
  821. this.$refs.queryForm.resetFields();
  822. this.handleQuery();
  823. },
  824. /** 全选或取消全选 */
  825. toggleSelectAll(val) {
  826. if (val) {
  827. this.toggleSelection(this.dataList);
  828. } else {
  829. this.toggleSelection();
  830. }
  831. },
  832. toggleSelection(rows) {
  833. if (rows) {
  834. rows.forEach(row => {
  835. this.$refs.dataTable.toggleRowSelection(row);
  836. });
  837. } else {
  838. this.$refs.dataTable.clearSelection();
  839. }
  840. },
  841. /** 多选框选中数据 */
  842. handleSelectionChange(val) {
  843. this.multipleSelection = val;
  844. this.allChecked = val.length === this.dataList.length;
  845. this.isIndeterminate = val.length > 0 && val.length < this.dataList.length;
  846. },
  847. /** 导出按钮操作 */
  848. handleExport() {
  849. if (this.dateRange && this.dateRange.length === 2) {
  850. this.queryParams.startTime = this.dateRange[0];
  851. this.queryParams.endTime = this.dateRange[1];
  852. } else {
  853. this.queryParams.startTime = null;
  854. this.queryParams.endTime = null;
  855. }
  856. this.$confirm('是否确认导出所有直播数据?', "警告", {
  857. confirmButtonText: "确定",
  858. cancelButtonText: "取消",
  859. type: "warning"
  860. }).then(() => {
  861. this.exportLoading = true;
  862. return exportLiveData(this.queryParams);
  863. }).then(response => {
  864. if (response.code === 200) {
  865. this.download(response.msg);
  866. }
  867. this.exportLoading = false;
  868. }).catch(() => {
  869. this.exportLoading = false;
  870. });
  871. },
  872. /** 自动打标签 */
  873. // handleAutoTag() {
  874. // if (this.multipleSelection.length === 0) {
  875. // this.$message.warning('请选择要打标签的数据');
  876. // return;
  877. // }
  878. // this.tagDialogVisible = true;
  879. // },
  880. /** 确认自动打标签 */
  881. confirmAutoTag() {
  882. if (this.tagForm.rules.length === 0) {
  883. this.$message.warning('请选择打标签规则');
  884. return;
  885. }
  886. const userIds = this.multipleSelection.map(item => item.userId).filter(id => id);
  887. if (userIds.length === 0) {
  888. this.$message.warning('所选数据中没有有效的用户ID');
  889. return;
  890. }
  891. const data = {
  892. userIds: userIds,
  893. rules: this.tagForm.rules,
  894. tagName: this.tagForm.tagName,
  895. remarkFormat: this.tagForm.remarkFormat,
  896. liveId: this.liveId
  897. };
  898. autoTagAndRemark(data).then(response => {
  899. if (response.code === 200) {
  900. this.$message.success('打标签成功');
  901. this.tagDialogVisible = false;
  902. this.tagForm = {
  903. rules: [],
  904. tagName: '',
  905. remarkFormat: 'time'
  906. };
  907. this.getList();
  908. }
  909. });
  910. },
  911. /** 自动备注 */
  912. // handleAutoRemark() {
  913. // if (this.multipleSelection.length === 0) {
  914. // this.$message.warning('请选择要备注的数据');
  915. // return;
  916. // }
  917. // this.remarkDialogVisible = true;
  918. // },
  919. /** 确认自动备注 */
  920. confirmAutoRemark() {
  921. if (this.remarkForm.rules.length === 0) {
  922. this.$message.warning('请选择备注规则');
  923. return;
  924. }
  925. const userIds = this.multipleSelection.map(item => item.userId).filter(id => id);
  926. if (userIds.length === 0) {
  927. this.$message.warning('所选数据中没有有效的用户ID');
  928. return;
  929. }
  930. // 生成备注内容
  931. let notes = '';
  932. const now = new Date();
  933. const dateStr = now.getFullYear() + '-' +
  934. String(now.getMonth() + 1).padStart(2, '0') + '-' +
  935. String(now.getDate()).padStart(2, '0');
  936. this.remarkForm.rules.forEach((rule, index) => {
  937. let ruleText = '';
  938. if (rule === 'liveComplete') {
  939. ruleText = '直播完课';
  940. } else if (rule === 'replayComplete') {
  941. ruleText = '回放完课';
  942. } else if (rule === 'pay') {
  943. ruleText = '付费行为';
  944. }
  945. if (this.remarkForm.remarkFormat === 'time') {
  946. notes += (index > 0 ? ';' : '') + dateStr + '—' + ruleText;
  947. } else {
  948. notes += (index > 0 ? ';' : '') + ruleText;
  949. }
  950. });
  951. const data = {
  952. userIds: userIds,
  953. notes: notes,
  954. type: this.remarkForm.position,
  955. nameType: 3, // 不添加客户名称
  956. filter: false
  957. };
  958. batchUpdateExternalContactNotes(data).then(response => {
  959. if (response.code === 200) {
  960. this.$message.success('备注成功');
  961. this.remarkDialogVisible = false;
  962. this.remarkForm = {
  963. rules: [],
  964. remarkFormat: 'time',
  965. position: 1
  966. };
  967. this.getList();
  968. }
  969. });
  970. },
  971. /** 查看详情 */
  972. handleViewDetail(row) {
  973. this.currentLiveId = row.liveId;
  974. this.detailDrawerVisible = true;
  975. this.loadDetailData();
  976. },
  977. /** 格式化时长 */
  978. formatDuration(seconds) {
  979. if (!seconds) return '00:00:00';
  980. const hours = Math.floor(seconds / 3600);
  981. const minutes = Math.floor((seconds % 3600) / 60);
  982. const secs = seconds % 60;
  983. return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(secs).padStart(2, '0')}`;
  984. },
  985. /** 更新完课时长设置 */
  986. updateCompleteWatchTime(type) {
  987. // 这里可以调用API保存设置
  988. let watchTime = 0;
  989. if (type === 'total') {
  990. watchTime = this.completeWatchTime;
  991. } else if (type === 'live') {
  992. watchTime = this.liveCompleteWatchTime;
  993. } else if (type === 'replay') {
  994. watchTime = this.replayCompleteWatchTime;
  995. }
  996. // 更新查询参数并重新获取数据
  997. this.queryParams.completeWatchTime = watchTime;
  998. // this.getStatistics();
  999. this.getList();
  1000. this.$message.success('设置成功');
  1001. },
  1002. /** 加载详情数据 */
  1003. loadDetailData() {
  1004. if (!this.currentLiveId) return;
  1005. // 使用SQL方式获取详情数据
  1006. getLiveDataDetailBySql(this.currentLiveId).then(response => {
  1007. if (response.code === 200) {
  1008. this.detailData = response.data || {};
  1009. }
  1010. });
  1011. },
  1012. /** 查看用户详情 */
  1013. handleViewUserDetail() {
  1014. if (!this.currentLiveId) return;
  1015. this.showUserDetail = true;
  1016. this.userDetailLoading = true;
  1017. getLiveUserDetailListBySql(this.currentLiveId, this.userDetailQueryParams.pageNum, this.userDetailQueryParams.pageSize).then(response => {
  1018. if (response.code === 200) {
  1019. this.userDetailList = response.data || [];
  1020. this.userDetailTotal = response.total || 0;
  1021. }
  1022. this.userDetailLoading = false;
  1023. }).catch(() => {
  1024. this.userDetailLoading = false;
  1025. });
  1026. },
  1027. /** 关闭详情侧边栏 */
  1028. closeDetailDrawer() {
  1029. this.detailDrawerVisible = false;
  1030. this.showUserDetail = false;
  1031. this.detailData = {};
  1032. this.userDetailList = [];
  1033. this.currentLiveId = null;
  1034. this.userDetailTotal = 0;
  1035. this.userDetailQueryParams = {
  1036. pageNum: 1,
  1037. pageSize: 100
  1038. };
  1039. },
  1040. /** 格式化百分比 */
  1041. formatPercent(value) {
  1042. if (value == null || value === undefined) return '0%';
  1043. return (typeof value === 'number' ? value : parseFloat(value)).toFixed(2) + '%';
  1044. },
  1045. /** 格式化金额 */
  1046. formatMoney(value) {
  1047. if (value == null || value === undefined) return '¥0.00';
  1048. return '¥' + (typeof value === 'number' ? value : parseFloat(value)).toFixed(2);
  1049. },
  1050. /** 导出用户详情数据 */
  1051. handleExportUserDetail() {
  1052. if (!this.currentLiveId) {
  1053. this.$message.warning('无法获取直播间ID');
  1054. return;
  1055. }
  1056. if (!this.userDetailList || this.userDetailList.length === 0) {
  1057. this.$message.warning('暂无用户详情数据可导出');
  1058. return;
  1059. }
  1060. this.$confirm('是否确认导出当前直播间的用户详情数据?', "提示", {
  1061. confirmButtonText: "确定",
  1062. cancelButtonText: "取消",
  1063. type: "warning"
  1064. }).then(() => {
  1065. this.userDetailExportLoading = true;
  1066. return exportLiveUserDetail(this.currentLiveId);
  1067. }).then(response => {
  1068. if (response.code === 200) {
  1069. this.download(response.msg);
  1070. this.$message.success('导出成功');
  1071. }
  1072. this.userDetailExportLoading = false;
  1073. }).catch(() => {
  1074. this.userDetailExportLoading = false;
  1075. });
  1076. },
  1077. /** 打开红包领取详情 */
  1078. handleOpenRedPacketLog(row) {
  1079. this.redPacketLogDrawerVisible = true;
  1080. this.redPacketLogQueryParams.liveId = this.currentLiveId;
  1081. this.redPacketLogQueryParams.userId = row.userId;
  1082. this.redPacketLogQueryParams.pageNum = 1;
  1083. this.loadRedPacketLogList();
  1084. },
  1085. /**
  1086. * 打开核销卷详情
  1087. */
  1088. handleOpenCouponLog(row){
  1089. this.couponLogDrawerVisible = true;
  1090. this.couponLogQueryParams.userId = row.userId;
  1091. this.couponLogQueryParams.type = '4-live-' + this.currentLiveId;
  1092. this.couponLogQueryParams.pageNum = 1;
  1093. this.loadCouponLogList();
  1094. },
  1095. /** 加载核销卷领取详情列表 */
  1096. loadCouponLogList() {
  1097. this.couponLogLoading = true;
  1098. listStoreCouponUser({
  1099. pageNum: this.couponLogQueryParams.pageNum,
  1100. pageSize: this.couponLogQueryParams.pageSize,
  1101. userId: this.couponLogQueryParams.userId,
  1102. type: this.couponLogQueryParams.type
  1103. }).then(response => {
  1104. this.couponLogList = response.rows || [];
  1105. this.couponLogTotal = response.total || 0;
  1106. this.couponLogLoading = false;
  1107. }).catch(() => {
  1108. this.couponLogLoading = false;
  1109. });
  1110. },
  1111. /** 加载红包领取详情列表 */
  1112. loadRedPacketLogList() {
  1113. this.redPacketLogLoading = true;
  1114. listLiveRedPacketLog({
  1115. pageNum: this.redPacketLogQueryParams.pageNum,
  1116. pageSize: this.redPacketLogQueryParams.pageSize,
  1117. liveId: this.redPacketLogQueryParams.liveId,
  1118. userId: this.redPacketLogQueryParams.userId
  1119. }).then(response => {
  1120. if (response.code === 200) {
  1121. this.redPacketLogList = response.rows || [];
  1122. this.redPacketLogTotal = response.total || 0;
  1123. }
  1124. this.redPacketLogLoading = false;
  1125. }).catch(() => {
  1126. this.redPacketLogLoading = false;
  1127. });
  1128. }
  1129. }
  1130. };
  1131. </script>
  1132. <style scoped>
  1133. .statistics-card {
  1134. margin-bottom: 20px;
  1135. }
  1136. .statistics-item {
  1137. text-align: center;
  1138. padding: 15px;
  1139. background: #f5f7fa;
  1140. border-radius: 4px;
  1141. }
  1142. .statistics-title {
  1143. font-size: 14px;
  1144. color: #606266;
  1145. margin-bottom: 10px;
  1146. display: flex;
  1147. align-items: center;
  1148. justify-content: center;
  1149. }
  1150. .statistics-value {
  1151. font-size: 24px;
  1152. font-weight: bold;
  1153. color: #303133;
  1154. }
  1155. .selection-toolbar {
  1156. display: flex;
  1157. align-items: center;
  1158. margin-bottom: 10px;
  1159. padding-left: 10px;
  1160. }
  1161. .selection-toolbar .el-checkbox {
  1162. margin-right: 10px;
  1163. }
  1164. .live-data-css {
  1165. padding-left: 10px;
  1166. padding-top: 30px;
  1167. }
  1168. .detail-content {
  1169. padding: 20px;
  1170. }
  1171. .detail-item {
  1172. margin-bottom: 15px;
  1173. line-height: 32px;
  1174. }
  1175. .detail-label {
  1176. color: #606266;
  1177. font-size: 14px;
  1178. }
  1179. .detail-value {
  1180. color: #303133;
  1181. font-size: 14px;
  1182. font-weight: bold;
  1183. }
  1184. .user-detail-content {
  1185. padding: 20px;
  1186. }
  1187. </style>