jinxia.mo 1 месяц назад
Родитель
Сommit
a762a3d1fc

+ 9 - 0
back-ui/src/api/dz/cards.js

@@ -207,3 +207,12 @@ export function getCardStatistics(query) {
     params: query,
   });
 }
+
+// 统计详情 - 查询学习卡列表
+export function getStatisticsDetail(query) {
+  return request({
+    url: "/dz/cards/statisticsDetail",
+    method: "get",
+    params: query,
+  });
+}

+ 46 - 7
back-ui/src/views/dz/cards/components/CardTable.vue

@@ -1,7 +1,14 @@
 <template>
-  <div class="mt-4 flex-1 min-h-1 relative">
-    <div class="absolute top-0 left-0 w-full h-full">
-      <el-table :data="data" class="w-full" height="100%" @selection-change="handleSelectionChange" v-loading="loading">
+  <div class="mt-4 flex-1 min-h-1 relative" :class="{ 'card-table-in-dialog': inDialog }">
+    <div class="absolute top-0 left-0 w-full h-full" :class="{ 'card-table-in-dialog-inner': inDialog }">
+      <el-table 
+        :data="data" 
+        class="w-full" 
+        :height="inDialog ? undefined : '100%'"
+        :max-height="inDialog ? '60vh' : undefined"
+        @selection-change="handleSelectionChange" 
+        v-loading="loading"
+      >
         <el-table-column label="" type="selection" min-width="60" fixed="left"></el-table-column>
         <el-table-column label="卡号" prop="cardNo" align="center" min-width="100" fixed="left"></el-table-column>
         <el-table-column label="密码" prop="password" align="center" min-width="100"></el-table-column>
@@ -53,8 +60,10 @@
         </el-table-column>
         <el-table-column label="考生类型(注册)" prop="assignExamType" align="center" min-width="140">
           <template #default="scope">
-            <dict-tag v-if="assignExamType" :options="exam_type" :value="scope.row.assignExamType" />
-            <span>-</span>
+            <template v-if="scope.row && scope.row.assignExamType && exam_type">
+              <dict-tag :options="exam_type" :value="scope.row.assignExamType" />
+            </template>
+            <span v-else>-</span>
           </template>
         </el-table-column>
         <el-table-column label="卡类型" prop="type" align="center" min-width="120">
@@ -107,7 +116,7 @@
             <span>{{ scope.row.payTime ? parseTime(scope.row.payTime, '{y}-{m}-{d}') : '-' }}</span>
           </template>
         </el-table-column>
-        <el-table-column label="操作" min-width="110" fixed="right" align="center">
+        <el-table-column v-if="!hideActions" label="操作" min-width="110" fixed="right" align="center">
           <template #default="scope">
             <el-button type="danger" text icon="Delete" @click="handleDelete(scope.row)">删除</el-button>
           </template>
@@ -131,6 +140,14 @@ const props = defineProps({
     type: Boolean,
     default: false,
   },
+  inDialog: {
+    type: Boolean,
+    default: false,
+  },
+  hideActions: {
+    type: Boolean,
+    default: false,
+  },
 });
 const emit = defineEmits(['selectionChange', 'delete']);
 const handleSelectionChange = (selection) => {
@@ -139,5 +156,27 @@ const handleSelectionChange = (selection) => {
 const handleDelete = (row) => {
   emit('delete', row);
 };
+
+// 安全获取 assignExamType
+const getAssignExamType = (row) => {
+  return row && row.assignExamType ? row.assignExamType : null;
+};
 </script>
-<style lang="scss" scoped></style>
+<style lang="scss" scoped>
+/* 在弹窗中使用时的样式 */
+.card-table-in-dialog {
+  position: relative !important;
+  height: auto !important;
+  min-height: 400px;
+  max-height: 60vh;
+  margin-top: 0 !important;
+}
+
+.card-table-in-dialog-inner {
+  position: relative !important;
+  height: auto !important;
+  width: 100% !important;
+  top: 0 !important;
+  left: 0 !important;
+}
+</style>

+ 224 - 5
back-ui/src/views/dz/cards/statistics.vue

@@ -58,25 +58,83 @@
                 </template>
               </el-table-column>
               <el-table-column prop="agentId" label="账号" align="center" />
-              <el-table-column prop="openCard" label="已开卡" align="center" />
-              <el-table-column prop="closeCard" label="已关卡" align="center" />
-              <el-table-column prop="payCard" label="已缴费" align="center" />
-              <el-table-column prop="settlementCard" label="已结算" align="center" />
+              <el-table-column prop="openCard" label="已开卡" align="center">
+                <template #default="scope">
+                  <el-link type="primary" @click="handleDetailClick(scope.row, 'openCard')" :underline="false">
+                    {{ scope.row.openCard || 0 }}
+                  </el-link>
+                </template>
+              </el-table-column>
+              <el-table-column prop="closeCard" label="已关卡" align="center">
+                <template #default="scope">
+                  <el-link type="primary" @click="handleDetailClick(scope.row, 'closeCard')" :underline="false">
+                    {{ scope.row.closeCard || 0 }}
+                  </el-link>
+                </template>
+              </el-table-column>
+              <el-table-column prop="payCard" label="已缴费" align="center">
+                <template #default="scope">
+                  <el-link type="primary" @click="handleDetailClick(scope.row, 'payCard')" :underline="false">
+                    {{ scope.row.payCard || 0 }}
+                  </el-link>
+                </template>
+              </el-table-column>
+              <el-table-column prop="settlementCard" label="已结算" align="center">
+                <template #default="scope">
+                  <el-link type="primary" @click="handleDetailClick(scope.row, 'settlementCard')" :underline="false">
+                    {{ scope.row.settlementCard || 0 }}
+                  </el-link>
+                </template>
+              </el-table-column>
             </el-table>
           </el-col>
         </pane>
       </splitpanes>
     </el-row>
+
+    <!-- 统计详情弹窗 -->
+    <el-dialog
+      :title="detailDialogTitle"
+      v-model="detailDialogVisible"
+      width="90%"
+      append-to-body
+      :close-on-click-modal="false"
+      class="statistics-detail-dialog"
+      :before-close="handleDialogClose"
+      @opened="handleDialogOpened"
+    >
+      <div class="dialog-table-wrapper">
+        <card-table
+          :data="detailCardList"
+          :loading="detailLoading"
+          :in-dialog="true"
+          :hide-actions="true"
+          @selectionChange="handleDetailSelectionChange"
+          @delete="handleDetailDelete"
+        />
+      </div>
+      <div class="flex justify-end mt-4">
+        <Pagination
+          :total="detailTotal"
+          v-model:page="detailQueryParams.pageNum"
+          v-model:limit="detailQueryParams.pageSize"
+          @pagination="getDetailList"
+        />
+      </div>
+    </el-dialog>
   </div>
 </template>
 
 <script setup name="CardStatistics">
-import { getCardStatistics } from "@/api/dz/cards"
+import { getCardStatistics, getStatisticsDetail, delCards } from "@/api/dz/cards"
 import IeInstitutionSelect from '@/components/IeInstitutionSelect/index.vue'
 import IeAgentSelect from '@/components/IeAgentSelect/index.vue'
 import useAppStore from '@/store/modules/app'
 import { Splitpanes, Pane } from "splitpanes"
 import "splitpanes/dist/splitpanes.css"
+import CardTable from './components/CardTable.vue'
+import Pagination from '@/components/Pagination/index.vue'
+import { nextTick } from 'vue'
 
 const { proxy } = getCurrentInstance()
 const appStore = useAppStore()
@@ -93,6 +151,27 @@ const queryParams = reactive({
   agentId: undefined
 })
 
+// 详情弹窗相关
+const detailDialogVisible = ref(false)
+const detailDialogTitle = ref('')
+const detailCardList = ref([])
+const detailLoading = ref(false)
+const detailTotal = ref(0)
+const detailQueryParams = reactive({
+  pageNum: 1,
+  pageSize: 10,
+  statisticsType: '',
+  agentId: null
+})
+
+// 统计类型标题映射
+const statisticsTypeMap = {
+  openCard: '已开卡',
+  closeCard: '已关卡',
+  payCard: '已缴费',
+  settlementCard: '已结算'
+}
+
 /** 查询统计列表 */
 function getList() {
   loading.value = true
@@ -216,6 +295,89 @@ function getRowStyle({ row }) {
   return {}
 }
 
+/** 点击统计数字,显示详情 */
+function handleDetailClick(row, statisticsType) {
+  // 确定使用的 agentId(优先使用 leafAgentId,如果没有则使用 agentId)
+  const agentId = row.leafAgentId || row.agentId
+  if (!agentId) {
+    proxy.$modal.msgWarning('代理商ID不存在')
+    return
+  }
+  
+  detailQueryParams.statisticsType = statisticsType
+  detailQueryParams.agentId = agentId
+  detailQueryParams.pageNum = 1
+  detailQueryParams.pageSize = 10
+  
+  const agentName = row.leafAgentName || row.agentName || ''
+  detailDialogTitle.value = `${agentName} - ${statisticsTypeMap[statisticsType]}详情`
+  
+  // 先打开弹窗
+  detailDialogVisible.value = true
+  // 立即加载数据
+  getDetailList()
+}
+
+/** 查询详情列表 */
+function getDetailList() {
+  if (!detailQueryParams.agentId || !detailQueryParams.statisticsType) {
+    return
+  }
+  
+  detailLoading.value = true
+  getStatisticsDetail({
+    statisticsType: detailQueryParams.statisticsType,
+    agentId: detailQueryParams.agentId,
+    pageNum: detailQueryParams.pageNum,
+    pageSize: detailQueryParams.pageSize
+  }).then(response => {
+    console.log('详情接口响应:', response)
+    detailCardList.value = response.rows || []
+    detailTotal.value = response.total || 0
+    console.log('设置后的 detailCardList:', detailCardList.value)
+    console.log('设置后的 detailTotal:', detailTotal.value)
+  }).catch(error => {
+    console.error('获取详情失败:', error)
+    proxy.$modal.msgError('获取详情失败')
+  }).finally(() => {
+    detailLoading.value = false
+  })
+}
+
+/** 详情列表选择变化 */
+function handleDetailSelectionChange(selection) {
+  // 可以在这里处理选择逻辑
+}
+
+/** 详情列表删除 */
+function handleDetailDelete(row) {
+  proxy.$modal.confirm(`是否确认删除学习卡号为"${row.cardNo}"的数据项?`).then(() => {
+    delCards(row.cardId).then(() => {
+      proxy.$modal.msgSuccess('删除成功')
+      getDetailList()
+      getList() // 刷新统计列表
+    })
+  })
+}
+
+/** 弹窗打开后处理 */
+function handleDialogOpened() {
+  // 弹窗打开后,确保表格能正确渲染
+  // 如果数据还没有加载,则加载数据
+  if (detailCardList.value.length === 0 && detailQueryParams.agentId) {
+    nextTick(() => {
+      getDetailList()
+    })
+  }
+}
+
+/** 弹窗关闭处理 */
+function handleDialogClose() {
+  detailDialogVisible.value = false
+  detailCardList.value = []
+  detailTotal.value = 0
+}
+
 onMounted(() => {
   getList()
 })
@@ -237,5 +399,62 @@ onMounted(() => {
   padding: 0;
   overflow: visible;
 }
+
+/* 弹窗中的表格容器 */
+.dialog-table-wrapper {
+  min-height: 400px;
+  max-height: 60vh;
+  position: relative;
+  overflow: visible;
+}
+
+/* 弹窗中的 CardTable 样式调整 - 覆盖绝对定位,使其在弹窗中正常显示 */
+:deep(.statistics-detail-dialog .dialog-table-wrapper) {
+  position: relative;
+  display: block;
+  width: 100%;
+}
+
+:deep(.statistics-detail-dialog .dialog-table-wrapper > div) {
+  position: relative !important;
+  height: auto !important;
+  min-height: 400px;
+  max-height: 60vh;
+  width: 100% !important;
+}
+
+:deep(.statistics-detail-dialog .dialog-table-wrapper .mt-4) {
+  position: relative !important;
+  height: auto !important;
+  min-height: 400px;
+  max-height: 60vh;
+  width: 100% !important;
+  margin-top: 0 !important;
+}
+
+:deep(.statistics-detail-dialog .dialog-table-wrapper .absolute) {
+  position: relative !important;
+  height: auto !important;
+  width: 100% !important;
+  top: 0 !important;
+  left: 0 !important;
+}
+
+:deep(.statistics-detail-dialog .dialog-table-wrapper .el-table) {
+  height: auto !important;
+  min-height: 400px;
+  max-height: 60vh;
+  width: 100% !important;
+}
+
+:deep(.statistics-detail-dialog .dialog-table-wrapper .el-table__body-wrapper) {
+  max-height: calc(60vh - 150px);
+  overflow-y: auto !important;
+}
+
+/* 确保表格内容可见 */
+:deep(.statistics-detail-dialog .el-table__body) {
+  display: table-row-group !important;
+}
 </style>
 

+ 17 - 0
ie-admin/src/main/java/com/ruoyi/web/controller/dz/DzCardsController.java

@@ -311,4 +311,21 @@ public class DzCardsController extends BaseController
         return (null == institutionId || institutionId.equals(101L)) ? CardType.Platform : CardType.Dept;
     }
 
+    /**
+     * 统计详情 - 根据统计类型查询学习卡列表
+     */
+    @PreAuthorize("@ss.hasPermi('dz:cards:list')")
+    @GetMapping("/statisticsDetail")
+    @ApiOperation("统计详情 - 查询学习卡列表")
+    public TableDataInfo statisticsDetail(@RequestParam String statisticsType,
+                                          @RequestParam Long agentId)
+    {
+        DzCards dzCards = new DzCards();
+        dzCards.setStatisticsType(statisticsType);
+        dzCards.setAgentId(agentId);
+        startPage();
+        List<DzCards> list = dzCardsService.selectDzCardsList(prepare(dzCards));
+        return getDataTable(list);
+    }
+
 }

+ 11 - 0
ie-system/src/main/java/com/ruoyi/dz/domain/DzCards.java

@@ -176,6 +176,9 @@ public class DzCards extends BaseEntity
 
     private String location;
 
+    /** 统计类型(用于详情查询) */
+    private String statisticsType;
+
 
 
 
@@ -601,6 +604,14 @@ public class DzCards extends BaseEntity
         this.location = location;
     }
 
+    public String getStatisticsType() {
+        return statisticsType;
+    }
+
+    public void setStatisticsType(String statisticsType) {
+        this.statisticsType = statisticsType;
+    }
+
     @Override
     public String toString() {
         return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)

+ 21 - 0
ie-system/src/main/java/com/ruoyi/enums/StatisticsType.java

@@ -0,0 +1,21 @@
+package com.ruoyi.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 统计类型枚举
+ * 
+ * @author ruoyi
+ */
+@Getter
+@AllArgsConstructor
+public enum StatisticsType {
+    openCard("已开卡"),
+    closeCard("已关卡"),
+    payCard("已缴费"),
+    settlementCard("已结算");
+
+    private final String title;
+}
+

+ 14 - 0
ie-system/src/main/resources/mapper/dz/DzCardsMapper.xml

@@ -85,6 +85,20 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="end != null "> and card_no &lt;= #{end}</if>
             <if test="assignTimeBegin != null "> and date(distribute_time) &gt;= date(#{assignTimeBegin})</if>
             <if test="assignTimeEnd != null "> and date(distribute_time) &lt;= date(#{assignTimeEnd})</if>
+
+            <!-- 根据统计类型添加条件 -->
+            <if test="statisticsType != null and statisticsType == 'openCard'">
+                AND (status = 10 OR status = 20 OR status = 30)
+            </if>
+            <if test="statisticsType != null and statisticsType == 'closeCard'">
+                AND distribute_status = 30
+            </if>
+            <if test="statisticsType != null and statisticsType == 'payCard'">
+                AND pay_status = 20
+            </if>
+            <if test="statisticsType != null and statisticsType == 'settlementCard'">
+                AND is_settlement = 1
+            </if>
         </where>
         order by card_id desc
     </select>