Explorar o código

用户列表新增批量封禁操作按钮

wangzhiqiang hai 2 meses
pai
achega
286b92644a

+ 1 - 1
.env.development

@@ -6,6 +6,6 @@ ENV = 'development'
 # VITE_BASE_URL = 'http://119.45.71.139:25001'
 
 # 本地 
-VITE_BASE_URL = 'http://192.168.1.128:25001'
+VITE_BASE_URL = 'http://192.168.1.9:25001'
 # VITE_BASE_URL = 'http://192.168.1.159:25001'
 

+ 9 - 0
src/api/userModule.js

@@ -61,4 +61,13 @@ export function getRevenueByTime(data) {
         method: 'post',
         data
     })
+}
+
+// 批量审核用户
+export function batchAudit(data) {
+    return request({
+        url: '/agent-service/appUser/batchAudit',
+        method: 'post',
+        data
+    })
 }

+ 18 - 3
src/components/table/index.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="system-table-box">
     <el-table v-bind="$attrs" ref="table" class="system-table" border :height="height" :data="data"
-      @selection-change="handleSelectionChange">
+      @selection-change="handleSelectionChange" @select-all="handleSelectAll"  @select="handleSelect">
       <el-table-column type="selection" align="center" width="50" v-if="showSelection" />
       <el-table-column label="序号" width="60" align="center" v-if="showIndex">
         <template #default="scope">
@@ -21,7 +21,7 @@
 </template>
 
 <script lang="js">
-import { defineComponent, ref, onActivated } from 'vue'
+import { defineComponent, ref, onActivated, defineExpose  } from 'vue'
 export default defineComponent({
   props: {
     data: { type: Array, default: () => [] }, // 数据源
@@ -66,6 +66,19 @@ export default defineComponent({
     const handleSelectionChange = (val) => {
       context.emit("selection-change", val)
     }
+    // 全选
+    const handleSelectAll = (val) => {
+      context.emit("select-all", val)
+    }
+    // 单选选
+    const handleSelect = (selection, row) => {
+      context.emit("select", selection, row)
+    }
+    // 导出table供外部使用
+    defineExpose({
+      table
+    })
+
     // 解决BUG:keep-alive组件使用时,表格浮层高度不对的问题
     onActivated(() => {
       table.value.doLayout()
@@ -74,7 +87,9 @@ export default defineComponent({
       table,
       handleCurrentChange,
       handleSizeChange,
-      handleSelectionChange
+      handleSelectionChange,
+      handleSelectAll,
+      handleSelect
     }
   }
 })

+ 12 - 8
src/views/main/userModule/riskLogsList.vue

@@ -9,15 +9,19 @@
         @selection-change="handleSelectionChange">
         <el-table-column prop="appId" label="应用ID" width="150" />
         <el-table-column prop="appName" label="应用名称" width="150" />
-        <el-table-column prop="nickName" label="用户昵称" width="90" />
-        <el-table-column prop="userId" label="用户ID" width="280" />
+        <el-table-column prop="nickName" label="用户昵称" width="120" />
+        <el-table-column prop="userId" label="用户ID" width="120" />
         <el-table-column prop="userStatus" label="用户状态" width="90">
           <template #default="scope">
             {{ getDictionaryName('user_status',scope.row.userStatus) }}
           </template>
         </el-table-column>
-        <el-table-column prop="bannedLimit" label="风控期限" width="90" />
-        <el-table-column prop="bannedReason" label="封禁原因" width="200" />
+        <el-table-column prop="bannedLimit" label="风控期限" width="90">
+          <template #default="scope">
+            {{ scope.row.bannedLimit }}天
+          </template>
+        </el-table-column>
+        <el-table-column prop="bannedReason" label="封禁原因" width="260" />
         <el-table-column prop="bannedTime" label="封禁时间" width="160">
           <template #default="scope">
             {{ convertUTCToBeijing(scope.row.bannedTime) }}
@@ -28,11 +32,11 @@
             {{ scope.row.bannedType === 1 ? '渠道' : '平台' }}
           </template>
         </el-table-column>
-        <el-table-column prop="channelId" label="渠道商ID" width="90" />
-        <el-table-column prop="phoneModel" label="手机型号" width="90" />
+        <el-table-column prop="channelId" label="渠道商ID" width="110" />
+        <el-table-column prop="phoneModel" label="手机型号" width="110" />
         <el-table-column prop="platformId" label="平台ID" width="150" />
-        <el-table-column prop="communicationOperator" label="IP运营商" width="100" />
-        <el-table-column prop="ipAddr" label="IP归属地" width="100" />
+        <el-table-column prop="communicationOperator" label="IP运营商" width="120" />
+        <el-table-column prop="ipAddr" label="IP归属地" width="200" />
         <el-table-column prop="lastLoginIp" label="最新登录IP" width="150" />
         <el-table-column prop="lastLoginTime" label="最新登录时间" width="160">
           <template #default="scope">

+ 180 - 34
src/views/main/userModule/userList.vue

@@ -3,17 +3,23 @@
     <!-- 菜单栏 -->
     <From :form-items="dynamicFormItems" @formSubmitted="handleFormSubmitted" @formReset="handleFormReset" />
 
+    <!-- 其他按钮 -->
+    <div class="btn">
+      <el-button type="" v-if="!formSearch.appIds && selectData.length > 0"  @click="clearSelection">取消全选</el-button>
+      <el-button type="primary" :disabled="!selectData.length > 0" @click="userCheck">批量审核</el-button>
+    </div>
+
     <!-- 表格 -->
     <div class="layout-container">
-      <Table @getTableData="changeTableData" v-model:page="page" ref="table" :data="tableData"
-        @selection-change="handleSelectionChange" :revenue="totalRevenue">
+      <Table @getTableData="changeTableData" v-model:page="page" ref="table" :data="tableData" showSelection
+        @selection-change="handleSelectionChange" @select-all="handleSelectAll" @select="handleSelect" :revenue="totalRevenue">
         <el-table-column prop="userId" label="查看ECPM" width="130" fixed="left">
           <template #default="scope">
             <el-button type="primary" @click="lookEcpm(scope.row.userId)">查看ECPM</el-button>
           </template>
         </el-table-column>
-        <el-table-column prop="userId" label="用户ID" width="100" fixed="left" />
-        <el-table-column prop="nickName" label="用户昵称" fixed="left" width="90" />
+        <el-table-column prop="userId" label="用户ID" width="120" fixed="left" />
+        <el-table-column prop="nickName" label="用户昵称" fixed="left" width="100" />
         <el-table-column prop="userStatus" label="用户状态" width="90">
           <template #default="scope">
             {{ getDictionaryName('user_status',Number(scope.row.userStatus)) }}
@@ -36,7 +42,7 @@
             {{ roundPrice(scope.row.totalIncome === 0 ? '0.00' : scope.row.totalIncome ?? '0.00') }}
           </template>
         </el-table-column>
-        <el-table-column prop="communicationOperator" label="通信运营商" width="100" />
+        <el-table-column prop="communicationOperator" label="通信运营商" width="130" />
         <el-table-column prop="deviceRepeatCount" label="设备重复数量" width="110" />
         <el-table-column prop="ipRepeatCount" label="IP重复数量" width="100" />
         <el-table-column prop="lastLoginIp" label="最新登录IP" width="150" />
@@ -81,7 +87,7 @@
     <!-- 操作弹窗 -->
     <Layer :layer="layer" @confirm="submit(ruleForm)" @close="layer.show = false">
       <el-form :model="formEdit" :rules="rules" ref="ruleForm" label-width="120px" style="margin-right:30px;">
-        <el-form-item label="封禁期限:" required prop="bannedLimit">
+        <el-form-item label="封禁时间(天):" required prop="bannedLimit">
           <el-input v-model="formEdit.bannedLimit" type="number" placeholder="请输入封禁期限" clearable />
         </el-form-item>
         <el-form-item label="封禁原因:" required prop="bannedReason">
@@ -109,10 +115,10 @@
         <From :form-items="dynamicFormItems1" @formSubmitted="handleFormSubmitted1" @formReset="handleFormReset1" />
         <el-divider></el-divider>
         <Table :showPage="false" ref="ecpmTable" :data="ecpmData" :height="ecpmLayer.height">
-          <el-table-column prop="userId" label="用户ID" width="100" />
+          <el-table-column prop="userId" label="用户ID" width="120" />
           <el-table-column prop="adSourceId" label="广告源ID" width="100" />
-          <el-table-column prop="beginTime" label="开始时间" />
-          <el-table-column prop="finishTime" label="完成时间" />
+          <el-table-column prop="beginTime" label="开始时间" width="100" />
+          <el-table-column prop="finishTime" label="完成时间" width="100" />
           <el-table-column prop="ecpm" label="ECPM" width="100" >
             <template #default="scope">
               {{ roundPrice(scope.row.ecpm) || 0.00 }}
@@ -127,9 +133,9 @@
           <el-table-column prop="networkFormId" label="广告平台ID" width="100" />
           <el-table-column prop="networkName" label="广告平台名称" width="120" />
           <el-table-column prop="networkPlacementId" label="广告平台广告位ID" width="160" />
-          <el-table-column prop="nickName" label="用户昵称" width="100" />
+          <el-table-column prop="nickName" label="用户昵称" min-width="100" />
           <el-table-column prop="placementId" label="广告位ID" width="100" />
-          <el-table-column prop="recordId" label="广告记录ID" width="100" />
+          <el-table-column prop="recordId" label="广告记录ID" width="160" />
           <el-table-column prop="revenue" label="收益" width="100">
             <template #default="scope">
               {{ roundPrice(scope.row.revenue) || 0.00 }}
@@ -142,16 +148,32 @@
           </div>
         </template>
     </Layer>
+
+    <!-- 审核备注 -->
+    <Layer :layer="layer2" @confirm="submit2(ruleForm2)" @close="layer2.show = false">
+      <el-form :model="formEdit2" :rules="rules2" ref="ruleForm2" label-width="120px" style="margin-right:30px;">
+        <el-form-item label="封禁时间(天)" required prop="bannedLimit">
+          <el-input-number v-model="formEdit2.bannedLimit" :min="1" :step="1" step-strictly style="width: 100%;" />
+        </el-form-item>
+        <el-form-item label="生效时间(小时)" required prop="effectTime">
+          <el-input-number v-model="formEdit2.effectTime" :min="0" :step="1" step-strictly style="width: 100%;" />
+          <el-alert title="单位为小时,0表示立即执行" type="error" center :closable="false" style="margin-top: 5px;height: 30px;"  />
+        </el-form-item>
+        <el-form-item label="审核备注" required prop="remark">
+          <el-input v-model="formEdit2.remark" type="textarea" rows="4" placeholder="请输入审核备注内容" clearable />
+        </el-form-item>
+      </el-form>
+    </Layer>
   </div>
 </template>
 
 <script setup>
-  import { onBeforeMount, ref, reactive } from "vue";
+  import { onBeforeMount, ref, reactive, nextTick } from "vue";
   import From from "@/components/from/index.vue";
   import Table from "@/components/table/index.vue";
   import Layer from '@/components/layer/index.vue'
   import { ElMessage } from 'element-plus'
-  import { getUserList, riskBannedUser, riskLockUser, appUserEcpm, getRevenueByTime } from '@/api/userModule.js'
+  import { getUserList, riskBannedUser, riskLockUser, appUserEcpm, getRevenueByTime, batchAudit } from '@/api/userModule.js'
   import { ditchList } from '@/api/outBagModule.js'
   import { appList } from "@/api/formworkErection.js";
   import { riskChangeUserStatus } from '@/api/riskModule.js'
@@ -162,6 +184,7 @@
   const store = useStore()
   const { loadDictData, getOptions, getDictionaryName } = useGetDictList();
   const tableData = ref([]);
+  const table = ref(null)
 
   // 分页参数, 供table使用
   const page = reactive({
@@ -187,13 +210,12 @@
   const dynamicFormItems1 = ref([])
 
   onBeforeMount(() => {
-    settingData()
-    getList();
-    getApiOptions()
+     settingData()
+    // getList();
   });
 
   // 获取缓存数据设置筛选数据
-  const settingData = () => {
+  const settingData = async() => {
     loadDictData().then(() => {
       dynamicFormItems.value = [
         {
@@ -241,32 +263,47 @@
           options: getOptions('ad_source_type'),
         }, 
       ]
+      // 设置动态选项
+      getApiOptions()
     })
   }
 
 
   //渠道来源
   const getApiOptions = async() => {
-    let ditchOptions = []
-    let appsOptions = []
-    let res = await ditchList({page:1,limit:9999})
-    res.data.map((item)=>{
-      ditchOptions.push({
+    try {
+      // 并发获取数据
+      const [{ data: ditchData }, { data: appData }] = await Promise.all([
+        ditchList({ page: 1, limit: 9999 }),
+        appList({ page: 1, limit: 9999 })
+      ])
+
+      const ditchOptions = ditchData.map(item => ({
         label: item.ditchName,
         value: item.ditchId
-      })
-    })
-    dynamicFormItems.value[3].options = ditchOptions
+      }))
 
-   
-    let app = await appList({page:1,limit:9999})
-    app.data.map((item)=>{
-      appsOptions.push({
+      const appsOptions = appData.map(item => ({
         label: item.appName,
         value: item.appId
-      })
-    })
-    dynamicFormItems.value[2].options = appsOptions
+      }))
+
+      // 赋值到表单项
+      dynamicFormItems.value[3].options = ditchOptions
+      dynamicFormItems.value[2].options = appsOptions
+
+      // 如果有应用列表,默认选第一个
+      if (appsOptions.length > 0) {
+        const firstApp = appsOptions[0].value
+        dynamicFormItems.value[2].defaultVal = firstApp
+        formSearch.value.appIds = firstApp
+      }
+
+      // 获取列表数据
+      getList()
+    } catch (err) {
+      console.error('获取选项失败:', err)
+    }
   }
 
   const totalRevenue = ref(0)
@@ -325,9 +362,54 @@
     getList();
   };
 
+  const selectData = ref([])
+  const currentAppName = ref(null) // 存储允许全选的 appName
   // 选择监听器
   const handleSelectionChange = (val) => {
-    context.emit("selection-change", val)
+    selectData.value = val
+  }
+
+  // 全选
+  const handleSelectAll = (selection) => {
+    if (selection.length === 0) {
+      // 全选取消
+      currentAppName.value = null
+      return
+    }
+
+    // 取第一个选中的 appName 作为基准
+    const firstAppName = selection[0].appName
+    // 先全部取消
+    table.value.table.clearSelection()
+
+    // 再只选中同一 appName 的行
+    tableData.value.forEach(row => {
+      if (row.appName === firstAppName) {
+        table.value.table.toggleRowSelection(row, true)
+      }
+    })
+
+    currentAppName.value = firstAppName
+  }
+
+  // 取消全选
+  const clearSelection = () => {
+    currentAppName.value = null
+    table.value.table.clearSelection()
+  }
+
+  // 单选
+  const handleSelect = (selection, row) => {
+    // 如果是单选时第一次选中,就记录当前 appName
+    if (selection.length === 1) {
+      currentAppName.value = selection[0].appName
+    }
+
+    // 如果选了不同 appName 的数据,则取消
+    if (selection.length > 0 && row.appName !== currentAppName.value) {
+      table.value.table.toggleRowSelection(row, false)
+      ElMessage.warning('只能选择同一个应用的用户哦')
+    }
   }
 
   // 弹窗
@@ -467,7 +549,7 @@
     show: false,
     title: "查看ECPM",
     showButton: true,
-    width: '90vw',
+    width: '95vw',
     height: '60vh'
   });
 
@@ -503,6 +585,65 @@
     ecpmLayer.value.show = true
   }
 
+  // #region  审核备注
+  const userCheck = async() => {
+    ruleForm2.value?.resetFields()
+    layer2.value.show = true
+    formEdit2.value = []
+  }
+
+    // 弹窗
+  const layer2 = ref({
+    show: false,
+    title: "批量审核",
+    showButton: true,
+    width: '300px'
+  });
+
+  const formEdit2 = ref({
+    appId: undefined, //应用ID
+    ditchId: undefined,//渠道ID
+    userIds: undefined, //用户ID
+    bannedLimit: undefined, //封禁时间(天)
+    effectTime: undefined,//生效时间(小时)
+    remark: undefined, //备注
+  })
+
+  const ruleForm2 = ref(null);
+
+  const rules2 = reactive({
+    remark: [
+      { required: true, message: "请输入审核备注内容", trigger: "blur" },
+    ],
+    bannedLimit: [
+      { required: true, message: "请选择封禁时间", trigger: "change" },
+    ],
+    effectTime: [
+      { required: true, message: "请选择生效时间", trigger: "change" },
+    ],
+  });
+
+  const submit2 = async (formEl) => {
+    await formEl.validate(async (valid, fields) => {
+      if (valid) {
+        formEdit2.value.appId = selectData.value[0].appId
+        formEdit2.value.ditchId = selectData.value[0].ditchId
+        formEdit2.value.userIds = selectData.value.map((item) => item.userId).join(",")
+
+        // 提交内容
+        await batchAudit({ ...formEdit2.value }).then((res) => {
+          ElMessage.success('审核提交成功')
+          layer2.value.show = false
+          getList();
+        })
+       
+      } else {
+        console.log("error submit!", fields);
+      }
+    })  
+  }
+  // #endregion
+
 </script>
 
 <style scoped lang="scss">
@@ -530,4 +671,9 @@
       }
     }
   }
+
+  .btn {
+    display: flex;
+    margin: 10px 0 -10px 30px;
+  }
 </style>