Ver Fonte

feat:会员-根据各个销售公司查看看课的会员数量和最近看课天数

caoliqin há 1 mês atrás
pai
commit
bb1b3be42a
2 ficheiros alterados com 227 adições e 2 exclusões
  1. 8 0
      src/api/his/user.js
  2. 219 2
      src/views/his/user/indexProject.vue

+ 8 - 0
src/api/his/user.js

@@ -151,3 +151,11 @@ export function batchUnbindUser(data) {
     data: data
   })
 }
+
+export function statisticsList(query) {
+  return request({
+    url: '/his/user/statisticsList',
+    method: 'get',
+    params: query
+  })
+}

+ 219 - 2
src/views/his/user/indexProject.vue

@@ -179,6 +179,14 @@
           v-hasPermi="['his:user:unbind']"
         >解绑会员</el-button>
       </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="info"
+          icon="el-icon-data-analysis"
+          size="mini"
+          @click="openMemberStatistics"
+        >会员统计</el-button>
+      </el-col>
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
 
@@ -369,6 +377,83 @@
       <userDetailsByNew  ref="userDetailsByNew" />
     </el-drawer>
 
+    <!-- 会员统计 -->
+    <el-dialog
+      title="会员统计"
+      :visible.sync="memberStatisticsOpen"
+      width="1260px"
+      append-to-body
+      @close="resetMemberStatisticsDialog"
+    >
+      <el-form :inline="true" size="small">
+        <el-form-item label="销售公司">
+          <el-select
+            v-model="memberStatisticsQuery.companyId"
+            placeholder="请选择销售公司"
+            clearable
+            filterable
+            style="width: 240px"
+          >
+            <el-option
+              v-for="item in companyQueryOptions"
+              :key="item.companyId"
+              :label="item.companyName"
+              :value="item.companyId"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" icon="el-icon-search" size="mini" @click="handleMemberStatisticsSearch">搜索</el-button>
+        </el-form-item>
+      </el-form>
+      <div v-loading="memberStatisticsLoading" style="min-height: 200px">
+        <el-table
+          v-if="memberStatisticsRows.length > 0"
+          :data="memberStatisticsRows"
+          border
+          max-height="480"
+        >
+          <el-table-column
+            v-for="col in statisticsDisplayColumns"
+            :key="col.prop"
+            :label="col.label"
+            :prop="col.prop"
+            min-width="120"
+            show-overflow-tooltip
+          >
+            <template slot-scope="scope">
+              <template v-if="col.prop === 'status'">
+                <el-tag v-if="String(scope.row.status) === '1'" type="success">正常</el-tag>
+                <el-tag v-else-if="String(scope.row.status) === '0'" type="danger">禁止</el-tag>
+                <span v-else>{{ scope.row.status }}</span>
+              </template>
+              <template v-else-if="col.prop === 'avatar'">
+                <el-popover
+                  v-if="scope.row.avatar"
+                  placement="right"
+                  title=""
+                  trigger="hover"
+                >
+                  <img slot="reference" :src="scope.row.avatar" width="40" height="40" style="object-fit: cover; border-radius: 4px;" />
+                  <img :src="scope.row.avatar" style="max-width: 220px; max-height: 220px;" />
+                </el-popover>
+                <span v-else>-</span>
+              </template>
+              <span v-else>{{ scope.row[col.prop] }}</span>
+            </template>
+          </el-table-column>
+        </el-table>
+        <el-empty v-else-if="!memberStatisticsLoading" description="暂无数据" />
+      </div>
+      <pagination
+        v-show="memberStatisticsTotal > 0"
+        :total="memberStatisticsTotal"
+        :page.sync="memberStatisticsQuery.pageNum"
+        :limit.sync="memberStatisticsQuery.pageSize"
+        @pagination="getMemberStatisticsList"
+      />
+    </el-dialog>
+
     <!-- 更换会员归属对话框 -->
     <el-dialog title="更换会员归属" :visible.sync="changeCompanyUserOpen" width="500px" append-to-body>
       <el-form ref="changeCompanyUserForm" :model="changeCompanyUserForm" :rules="changeCompanyUserRules" label-width="100px">
@@ -402,7 +487,7 @@
 </template>
 
 <script>
-import {listUserByProject, getUser, addUser, updateUser, exportUser, delUserCompanyUser,exportListProject, batchUnbindUser} from "@/api/his/user";
+import {listUserByProject, getUser, addUser, updateUser, exportUser, delUserCompanyUser,exportListProject, batchUnbindUser, statisticsList} from "@/api/his/user";
 import { getCompanyUserList, changeCompanyUser, getCompanyList } from '@/api/company/companyUser';
 import userDetailsByNew from '@/views/his/user/userDetails.vue'
 export default {
@@ -511,9 +596,46 @@ export default {
       companyUserOptions: [],
       companyOptions: [],
       projectOptions: [],
-      selectedUser: []
+      selectedUser: [],
+      // 会员统计弹窗
+      memberStatisticsOpen: false,
+      memberStatisticsLoading: false,
+      memberStatisticsRows: [],
+      memberStatisticsTotal: 0,
+      memberStatisticsQuery: {
+        pageNum: 1,
+        pageSize: 10,
+        companyId: null,
+      }
     };
   },
+  computed: {
+    statisticsDisplayColumns() {
+      const row = this.memberStatisticsRows[0]
+      if (!row) {
+        return []
+      }
+      const labelMap = {
+        userId: '会员ID',
+        nickName: '会员昵称',
+        avatar: '头像',
+        // phone: '手机号码',
+        status: '状态',
+        // companyId: '公司ID',
+        companyName: '所属公司',
+        watchDaysLast3: '最近3天看课天数',
+        watchDaysLast5: '最近5天看课天数',
+        watchDaysLast7: '最近7天看课天数',
+      }
+      // 只展示配置了中文的字段,并按 labelMap 的顺序展示
+      return Object.keys(labelMap)
+        .filter(prop => Object.prototype.hasOwnProperty.call(row, prop))
+        .map(prop => ({
+          prop,
+          label: labelMap[prop]
+        }))
+    }
+  },
   created() {
     this.getDicts("project_user_status").then((response) => {
       this.statusOptions = response.data;
@@ -874,6 +996,101 @@ export default {
     getProjectLabel(projectId) {
       return this.projectOptions.find(item => parseInt(item.dictValue) === projectId)?.dictLabel;
     },
+
+    // 如果主页面有选择公司参数,则直接使用,否则就使用下拉框的第一个参数
+    resolveDefaultStatisticsCompanyId() {
+      const options = this.companyQueryOptions || []
+      if (!options.length) {
+        return null
+      }
+      let companyId = this.queryParams.companyId
+      if (companyId != null && companyId !== '') {
+        const exists = options.some(o => String(o.companyId) === String(companyId))
+        if (exists) {
+          return companyId
+        }
+      }
+      return options[0].companyId
+    },
+    openMemberStatistics() {
+      const ensureOptions = () => {
+        return new Promise((res) => {
+          const options = this.companyQueryOptions || []
+          if (options.length) {
+            res(true)
+            return
+          }
+          getCompanyList().then(response => {
+            if (response.code === 200) {
+              this.companyQueryOptions = response.data || []
+              res(!!this.companyQueryOptions.length)
+            } else {
+              this.$message.error(response.msg || '获取公司列表失败')
+              res(false)
+            }
+          }).catch(() => {
+            this.$message.error('获取公司列表失败')
+            res(false)
+          })
+        })
+      }
+      ensureOptions().then(ok => {
+        if (!ok) {
+          this.$message.warning('没有公司数据')
+          return
+        }
+        const companyId = this.resolveDefaultStatisticsCompanyId()
+        if (companyId == null) {
+          this.$message.warning('没有所属公司')
+          return
+        }
+        this.memberStatisticsQuery = {
+          pageNum: 1,
+          pageSize: 10,
+          companyId,
+        }
+        this.memberStatisticsOpen = true
+        this.$nextTick(() => this.getMemberStatisticsList())
+      })
+    },
+
+    // 查询列表
+    getMemberStatisticsList() {
+      if (this.memberStatisticsQuery.companyId == null || this.memberStatisticsQuery.companyId === '') {
+        this.$message.warning('请选择销售公司')
+        return
+      }
+      this.memberStatisticsLoading = true
+      statisticsList(this.memberStatisticsQuery).then(response => {
+        this.memberStatisticsRows = response.rows || []
+        this.memberStatisticsTotal = response.total || 0
+      }).catch(() => {
+        this.memberStatisticsRows = []
+        this.memberStatisticsTotal = 0
+      }).finally(() => {
+        this.memberStatisticsLoading = false
+      })
+    },
+
+    // handleMemberStatisticsCompanyChange() {
+    //   this.memberStatisticsQuery.pageNum = 1
+    //   this.getMemberStatisticsList()
+    // },
+    handleMemberStatisticsSearch() {
+      this.memberStatisticsQuery.pageNum = 1
+      this.getMemberStatisticsList()
+    },
+
+    // 关闭弹窗
+    resetMemberStatisticsDialog() {
+      this.memberStatisticsRows = []
+      this.memberStatisticsTotal = 0
+      this.memberStatisticsQuery = {
+        pageNum: 1,
+        pageSize: 10,
+        companyId: null,
+      }
+    }
   }
 };