userList.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525
  1. <template>
  2. <div class="layout-container">
  3. <!-- 菜单栏 -->
  4. <From :form-items="dynamicFormItems" @formSubmitted="handleFormSubmitted" @formReset="handleFormReset" />
  5. <!-- 表格 -->
  6. <div class="layout-container">
  7. <Table @getTableData="changeTableData" v-model:page="page" ref="table" :data="tableData"
  8. @selection-change="handleSelectionChange">
  9. <el-table-column prop="userId" label="查看ECPM" width="130" fixed="left">
  10. <template #default="scope">
  11. <el-button type="primary" @click="lookEcpm(scope.row.userId)">查看ECPM</el-button>
  12. </template>
  13. </el-table-column>
  14. <el-table-column prop="userId" label="用户ID" width="100" fixed="left" />
  15. <el-table-column prop="nickName" label="用户昵称" fixed="left" width="90" />
  16. <el-table-column prop="userStatus" label="用户状态" width="90">
  17. <template #default="scope">
  18. {{ getDictionaryName('user_status',Number(scope.row.userStatus)) }}
  19. </template>
  20. </el-table-column>
  21. <!-- <el-table-column prop="userType" label="用户类型" width="90" />-->
  22. <el-table-column prop="appId" label="应用ID" width="150" />
  23. <el-table-column prop="appName" label="应用名称" width="150" />
  24. <el-table-column prop="appType" label="应用类型" width="90">
  25. <template #default="scope">
  26. {{ getDictionaryName('app_type',scope.row.appType) }}
  27. </template>
  28. </el-table-column>
  29. <el-table-column prop="ditchName" label="渠道来源" width="150" />
  30. <el-table-column prop="todayVideo" label="今日视频播放数" width="130" />
  31. <el-table-column prop="totalVideo" label="视频播放总数" width="110" />
  32. <el-table-column prop="totalIncome" label="用户贡献(当日/总共)" sortable width="200">
  33. <template #default="scope">
  34. {{ roundPrice(scope.row.todayIncome === 0 ? '0.00' : scope.row.todayIncome ?? '0.00')}} /
  35. {{ roundPrice(scope.row.totalIncome === 0 ? '0.00' : scope.row.totalIncome ?? '0.00') }}
  36. </template>
  37. </el-table-column>
  38. <el-table-column prop="communicationOperator" label="通信运营商" width="100" />
  39. <el-table-column prop="deviceRepeatCount" label="设备重复数量" width="110" />
  40. <el-table-column prop="ipRepeatCount" label="IP重复数量" width="100" />
  41. <el-table-column prop="lastLoginIp" label="最新登录IP" width="150" />
  42. <el-table-column prop="lastLoginTime" label="最新登录时间" width="160">
  43. <template #default="scope">
  44. {{ convertUTCToBeijing(scope.row.lastLoginTime) }}
  45. </template>
  46. </el-table-column>
  47. <el-table-column prop="loginDays" label="登录天数" width="90" />
  48. <el-table-column prop="pointsBalance" label="积分余额" width="90" />
  49. <el-table-column prop="pointsTotal" label="积分总额" width="90" />
  50. <el-table-column prop="redPacketAmount" label="红包总额" width="90" />
  51. <el-table-column prop="redPacketBalance" label="红包余额" width="90" />
  52. <el-table-column prop="registryTime" label="注册时间" width="160">
  53. <template #default="scope">
  54. {{ convertUTCToBeijing(scope.row.registryTime) }}
  55. </template>
  56. </el-table-column>
  57. <el-table-column prop="versionCode" label="版本号" />
  58. <el-table-column prop="withdrawCount" label="提现笔数" width="90" />
  59. <el-table-column prop="withdrawTotal" label="提现总额" width="90" />
  60. <el-table-column label="操作" width="220">
  61. <template #default="scope">
  62. <div class="button">
  63. <el-link class="button-item" type="primary" @click="editUserType(scope.row)">
  64. 风控解除
  65. </el-link>
  66. <el-link v-if="scope.row.userStatus < 3" class="button-item" type="danger" @click="edit(scope.row)">
  67. 封禁用户
  68. </el-link>
  69. <el-popconfirm title="确认锁定该用户?" @confirm="lockUser(scope.row)">
  70. <template #reference>
  71. <el-link v-if="scope.row.userStatus < 2" class="button-item" type="warning">锁定用户</el-link>
  72. </template>
  73. </el-popconfirm>
  74. </div>
  75. </template>
  76. </el-table-column>
  77. </Table>
  78. </div>
  79. <!-- 操作弹窗 -->
  80. <Layer :layer="layer" @confirm="submit(ruleForm)" @close="layer.show = false">
  81. <el-form :model="formEdit" :rules="rules" ref="ruleForm" label-width="120px" style="margin-right:30px;">
  82. <el-form-item label="封禁期限:" required prop="bannedLimit">
  83. <el-input v-model="formEdit.bannedLimit" type="number" placeholder="请输入封禁期限" clearable />
  84. </el-form-item>
  85. <el-form-item label="封禁原因:" required prop="bannedReason">
  86. <el-input v-model="formEdit.bannedReason" placeholder="请输入封禁原因" clearable />
  87. </el-form-item>
  88. </el-form>
  89. </Layer>
  90. <Layer :layer="layer1" @confirm="submit1(ruleForm1)" @close="layer.show = false">
  91. <el-form :model="formEdit1" :rules="rules1" ref="ruleForm1" label-width="120px" style="margin-right:30px;">
  92. <el-form-item label="用户状态:" required prop="userStatus">
  93. <el-select v-model="formEdit1.userStatus" placeholder="请选择状态" filterable>
  94. <el-option v-for="option in getOptions('user_status')" :key="option.label" :label="option.label"
  95. :value="option.value">
  96. </el-option>
  97. </el-select>
  98. </el-form-item>
  99. <el-form-item label="原因:" prop="reason">
  100. <el-input v-model="formEdit1.reason" placeholder="请输入原因" clearable />
  101. </el-form-item>
  102. </el-form>
  103. </Layer>
  104. <Layer :layer="ecpmLayer" @confirm="ecpmLayer.show = false" @close="ecpmLayer.show = false">
  105. <From :form-items="dynamicFormItems1" @formSubmitted="handleFormSubmitted1" @formReset="handleFormReset1" />
  106. <el-divider></el-divider>
  107. <Table :showPage="false" ref="ecpmTable" :data="ecpmData" :height="ecpmLayer.height">
  108. <el-table-column prop="userId" label="用户ID" width="100" />
  109. <el-table-column prop="adSourceId" label="广告源ID" width="100" />
  110. <el-table-column prop="beginTime" label="开始时间" />
  111. <el-table-column prop="finishTime" label="完成时间" />
  112. <el-table-column prop="ecpm" label="ECPM" width="100" >
  113. <template #default="scope">
  114. {{ roundPrice(scope.row.ecpm) || 0.00 }}
  115. </template>
  116. </el-table-column>
  117. <el-table-column prop="adSourceType" label="广告源类型" width="160">
  118. <template #default="scope">
  119. {{ getDictionaryName('ad_source_type',scope.row.adSourceType) }}
  120. </template>
  121. </el-table-column>
  122. <el-table-column prop="adSourceIndex" label="竞价匹配优先级" width="130" />
  123. <el-table-column prop="networkFormId" label="广告平台ID" width="100" />
  124. <el-table-column prop="networkName" label="广告平台名称" width="120" />
  125. <el-table-column prop="networkPlacementId" label="广告平台广告位ID" width="160" />
  126. <el-table-column prop="nickName" label="用户昵称" width="100" />
  127. <el-table-column prop="placementId" label="广告位ID" width="100" />
  128. <el-table-column prop="recordId" label="广告记录ID" width="100" />
  129. <el-table-column prop="revenue" label="收益" width="100">
  130. <template #default="scope">
  131. {{ roundPrice(scope.row.revenue) || 0.00 }}
  132. </template>
  133. </el-table-column>
  134. </Table>
  135. <template #bottom>
  136. <div style="position: absolute;bottom: 0px;right: 10px;">
  137. 共:{{ ecpmData.length }}
  138. </div>
  139. </template>
  140. </Layer>
  141. </div>
  142. </template>
  143. <script setup>
  144. import { onBeforeMount, ref, reactive } from "vue";
  145. import From from "@/components/from/index.vue";
  146. import Table from "@/components/table/index.vue";
  147. import Layer from '@/components/layer/index.vue'
  148. import { ElMessage } from 'element-plus'
  149. import { getUserList, riskBannedUser, riskLockUser, appUserEcpm } from '@/api/userModule.js'
  150. import { ditchList } from '@/api/outBagModule.js'
  151. import { appList } from "@/api/formworkErection.js";
  152. import { riskChangeUserStatus } from '@/api/riskModule.js'
  153. import { convertUTCToBeijing, roundPrice } from '@/utils/index.js'
  154. import { useGetDictList } from '@/hooks/useGetDictList.js'
  155. import { useStore } from 'vuex'
  156. const store = useStore()
  157. const { loadDictData, getOptions, getDictionaryName } = useGetDictList();
  158. const tableData = ref([]);
  159. // 分页参数, 供table使用
  160. const page = reactive({
  161. pageNum: 1,
  162. pageSize: 20,
  163. total: 0,
  164. });
  165. const formSearch = ref({
  166. ditchId: undefined,// 渠道来源
  167. channelType: undefined,// 渠道类型
  168. lastLoginTime: undefined,// 最新登录时间
  169. limit: 20,// 当前页数量(查询量)
  170. nickName: undefined,// 用户昵称
  171. userId: undefined,// 用户ID
  172. page: 1,// 当前页码
  173. registryTimeBegin: undefined,// 注册时间
  174. registryTimeEnd: undefined,// 注册时间
  175. userStatus: 1,// 用户类型
  176. appIds: undefined, //所属应用
  177. });
  178. const dynamicFormItems = ref([])
  179. const dynamicFormItems1 = ref([])
  180. onBeforeMount(() => {
  181. settingData()
  182. getList();
  183. getApiOptions()
  184. });
  185. // 获取缓存数据设置筛选数据
  186. const settingData = () => {
  187. loadDictData().then(() => {
  188. dynamicFormItems.value = [
  189. {
  190. label: '用户昵称',
  191. prop: 'nickName',
  192. type: 'input',
  193. needEnterEvent: true
  194. },
  195. {
  196. label: '用户ID',
  197. prop: 'userId',
  198. type: 'input',
  199. needEnterEvent: true
  200. },
  201. {
  202. label: '所属应用',
  203. prop: 'appIds',
  204. type: 'select',
  205. options: [],
  206. },
  207. {
  208. label: '渠道来源',
  209. prop: 'ditchId',
  210. type: 'select',
  211. options: getOptions('channel_origin'),
  212. },
  213. {
  214. label: '用户状态',
  215. prop: 'userStatus',
  216. type: 'select',
  217. defaultVal: 1,
  218. options: getOptions('user_status'),
  219. },
  220. { label: '注册时间开始', prop: 'registryTimeBegin', type: 'date' },
  221. { label: '注册时间结束', prop: 'registryTimeEnd', type: 'date' },
  222. ]
  223. // 查看ECPM
  224. dynamicFormItems1.value = [
  225. {
  226. label: '广告源类型',
  227. prop: 'adSourceType',
  228. type: 'select',
  229. labelWidth: 100,
  230. options: getOptions('ad_source_type'),
  231. },
  232. ]
  233. })
  234. }
  235. //渠道来源
  236. const getApiOptions = async() => {
  237. let ditchOptions = []
  238. let appsOptions = []
  239. let res = await ditchList({page:1,limit:9999})
  240. res.data.map((item)=>{
  241. ditchOptions.push({
  242. label: item.ditchName,
  243. value: item.ditchId
  244. })
  245. })
  246. dynamicFormItems.value[3].options = ditchOptions
  247. let app = await appList({page:1,limit:9999})
  248. app.data.map((item)=>{
  249. appsOptions.push({
  250. label: item.appName,
  251. value: item.appId
  252. })
  253. })
  254. dynamicFormItems.value[2].options = appsOptions
  255. }
  256. // 分页数据
  257. const getList = async () => {
  258. let res = await getUserList({ ...formSearch.value });
  259. tableData.value = res.data;
  260. page.total = res.pageMeta.total;
  261. };
  262. const changeTableData = (type) => {
  263. formSearch.value.page = type ? 1 : page.pageNum;
  264. formSearch.value.limit = page.pageSize;
  265. // 分页切换
  266. getList();
  267. };
  268. // 搜索
  269. const handleFormSubmitted = (formData) => {
  270. // console.log("接收到子组件传递的数据", formData);
  271. formSearch.value.page = 1;
  272. formSearch.value.limit = 20;
  273. formSearch.value.nickName = formData.nickName;
  274. formSearch.value.userId = formData.userId;
  275. formSearch.value.ditchId = formData.ditchId;
  276. formSearch.value.userStatus = formData.userStatus;
  277. formSearch.value.appIds = formData.appIds;
  278. formSearch.value.registryTimeBegin = formData.registryTimeBegin || null
  279. formSearch.value.registryTimeEnd = formData.registryTimeEnd || null
  280. getList();
  281. };
  282. // 表单重置
  283. const handleFormReset = () => {
  284. formSearch.value = {
  285. ditchId: undefined,// 渠道来源
  286. channelType: undefined,// 渠道类型
  287. lastLoginTime: undefined,// 最新登录时间
  288. limit: 20,// 当前页数量(查询量)
  289. nickName: undefined,// 用户昵称
  290. userId: undefined,// 用户ID
  291. page: 1,// 当前页码
  292. registryTime: undefined,// 注册时间
  293. userType: undefined,// 用户类型
  294. appIds: undefined, //所属应用
  295. };
  296. getList();
  297. };
  298. // 选择监听器
  299. const handleSelectionChange = (val) => {
  300. context.emit("selection-change", val)
  301. }
  302. // 弹窗
  303. const layer = ref({
  304. show: false,
  305. title: "封禁用户",
  306. showButton: true,
  307. width: '300px'
  308. });
  309. const formEdit = ref({
  310. bannedLimit: null, //封禁期限
  311. bannedReason: '',//封禁原因
  312. bannedTargetId: '',//封禁目标ID
  313. bannedType: '',//封禁类型 1-渠道 2-平台
  314. operator: '',//操作人
  315. operatorName: '',//操作人名称
  316. userId: '', //用户ID
  317. appId: '',//应用ID
  318. })
  319. const edit = (row) => {
  320. ruleForm.value?.resetFields()
  321. layer.value.show = true
  322. formEdit.value.bannedTargetId = row.appId
  323. formEdit.value.bannedType = row.channelType
  324. formEdit.value.operator = store.state.user.info.loginName
  325. formEdit.value.operatorName = store.state.user.info.nickName
  326. formEdit.value.userId = row.userId
  327. formEdit.value.appId = row.appId
  328. }
  329. const ruleForm = ref(null);
  330. const rules = reactive({
  331. formEditbannedLimit: [
  332. { required: true, message: "请输入封禁期限", trigger: "blur" },
  333. ],
  334. bannedReason: [
  335. {
  336. required: true,
  337. message: "请输入封禁原因",
  338. trigger: "blur",
  339. },
  340. ],
  341. });
  342. const submit = async (formEl) => {
  343. await formEl.validate(async (valid, fields) => {
  344. if (valid) {
  345. // 提交内容
  346. riskBannedUser({ ...formEdit.value }).then((res) => {
  347. ElMessage.success('封禁用户成功')
  348. layer.value.show = false
  349. getList();
  350. })
  351. } else {
  352. console.log("error submit!", fields);
  353. }
  354. })
  355. }
  356. // 弹窗2
  357. const layer1 = ref({
  358. show: false,
  359. title: "更改用户状态",
  360. showButton: true,
  361. width: '300px'
  362. });
  363. const formEdit1 = ref({
  364. bannedType: null,//封禁类型 1-渠道 2-平台
  365. operator: '',//操作人
  366. operatorName: '',//操作人名称
  367. reason: '',//原因
  368. userId: '', //用户ID
  369. appId: '', //应用ID
  370. userStatus: null, //用户状态
  371. })
  372. const editUserType = (row) => {
  373. ruleForm1.value?.resetFields()
  374. layer1.value.show = true
  375. formEdit1.value.bannedType = row.channelType
  376. formEdit1.value.operator = store.state.user.info.loginName
  377. formEdit1.value.operatorName = store.state.user.info.nickName
  378. formEdit1.value.userId = row.userId
  379. formEdit1.value.appId = row.appId
  380. }
  381. const ruleForm1 = ref(null);
  382. const rules1 = reactive({
  383. userStatus: [
  384. {
  385. required: true,
  386. message: "请选择用户状态",
  387. trigger: "change",
  388. },
  389. ],
  390. reason: [
  391. {
  392. required: true,
  393. message: "请输入更改原因",
  394. trigger: ["blur"],
  395. },
  396. ],
  397. });
  398. const submit1 = async (formEl) => {
  399. await formEl.validate(async (valid, fields) => {
  400. if (valid) {
  401. // 提交内容
  402. riskChangeUserStatus(formEdit1.value).then((res) => {
  403. ElMessage.success('更改用户状态成功')
  404. layer1.value.show = false
  405. getList();
  406. })
  407. } else {
  408. console.log("error submit!", fields);
  409. }
  410. })
  411. }
  412. // 锁定用户
  413. const lockUser = async (row) => {
  414. riskLockUser({ userId: row.userId, appId: row.appId }).then((res) => {
  415. ElMessage.success('锁定用户成功')
  416. getList();
  417. })
  418. }
  419. // 查看ECPM
  420. const ecpmLayer = ref({
  421. show: false,
  422. title: "查看ECPM",
  423. showButton: true,
  424. width: '90vw',
  425. height: '60vh'
  426. });
  427. const formSearch1 = ref({
  428. userId: undefined, //用户ID
  429. adSourceType: null,// 广告源类型
  430. });
  431. const handleFormSubmitted1 = (formData) => {
  432. // console.log("接收到子组件传递的数据", formData);
  433. formSearch1.value.adSourceType = formData.adSourceType;
  434. lookEcpm();
  435. };
  436. const handleFormReset1 = () => {
  437. delete formSearch1.value.adSourceType // 渠道来源
  438. lookEcpm();
  439. };
  440. const ecpmTable = ref(null)
  441. const ecpmData = ref([])
  442. const lookEcpm = async(userId)=> {
  443. if(userId) {
  444. formSearch1.value.userId = userId
  445. // delete formSearch1.value.adSourceType
  446. }
  447. ecpmData.value = []
  448. let res = await appUserEcpm({userId: formSearch1.value.userId,adsourceType: formSearch1.value.adSourceType })
  449. ecpmData.value = res.data
  450. ecpmLayer.value.show = true
  451. }
  452. </script>
  453. <style scoped lang="scss">
  454. .layout-container {
  455. .card {
  456. .title {
  457. margin-bottom: 10px;
  458. font-weight: 600;
  459. }
  460. display: flex;
  461. flex-direction: column;
  462. align-items: start;
  463. width: calc(100% - 60px);
  464. margin: 30px 30px 0;
  465. }
  466. .button {
  467. display: flex;
  468. //flex-direction: column;
  469. .button-item {
  470. margin: 4px;
  471. }
  472. }
  473. }
  474. </style>