| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271 |
- <template>
- <div class="app-container">
- <!-- ===== 顶部 KPI 概览卡片 ===== -->
- <el-row :gutter="16" class="mb16">
- <el-col :span="6" v-for="card in overviewCards" :key="card.label">
- <el-card shadow="hover" class="overview-card">
- <div class="card-inner">
- <div class="card-icon" :style="{ background: card.bg }"><i :class="card.icon"></i></div>
- <div class="card-info">
- <span class="card-value">{{ card.value }}</span>
- <span class="card-label">{{ card.label }}</span>
- </div>
- </div>
- </el-card>
- </el-col>
- </el-row>
- <!-- ===== 搜索与操作栏 ===== -->
- <el-card shadow="never" class="mb16 filter-card">
- <el-form :model="queryParams" ref="queryForm" :inline="true" size="small">
- <el-form-item label="租户名称" prop="tenantName">
- <el-input v-model="queryParams.tenantName" placeholder="请输入租户名称" clearable @keyup.enter.native="handleQuery" />
- </el-form-item>
- <el-form-item label="归属代理" prop="proxyName">
- <el-input v-model="queryParams.proxyName" placeholder="请输入代理名称" clearable @keyup.enter.native="handleQuery" />
- </el-form-item>
- <el-form-item>
- <el-button type="primary" icon="el-icon-search" @click="handleQuery">查询</el-button>
- <el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
- </el-form-item>
- </el-form>
- </el-card>
- <!-- ===== 操作按钮 ===== -->
- <el-row :gutter="10" class="mb8">
- <el-col :span="1.5">
- <el-button type="warning" plain icon="el-icon-download" size="mini" :loading="exportLoading" @click="handleExport">导出</el-button>
- </el-col>
- <el-col :span="1.5">
- <el-button type="primary" plain icon="el-icon-refresh" size="mini" @click="handleRefreshAll">全量统计刷新</el-button>
- </el-col>
- <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
- </el-row>
- <!-- ===== 租户用量汇总表 ===== -->
- <el-table border v-loading="loading" :data="list" size="small" style="width:100%" show-summary :summary-method="getSummary">
- <el-table-column label="租户名称" align="center" prop="tenantName" min-width="140" fixed="left" />
- <el-table-column label="归属代理" align="center" prop="proxyName" min-width="120" />
- <!-- AI 模型用量 -->
- <el-table-column label="AI Token" align="center" prop="aiTokenTotal" width="100" />
- <el-table-column label="AI外呼" align="center" prop="aiCallTotal" width="80" />
- <el-table-column label="AI对话" align="center" prop="aiChatTotal" width="80" />
- <!-- 通讯 -->
- <el-table-column label="外呼次数" align="center" prop="outboundCallCount" width="85" />
- <el-table-column label="短信量" align="center" prop="smsSentCount" width="75" />
- <!-- 个微 -->
- <el-table-column label="个微用户" align="center" prop="wxUserCount" width="85" />
- <el-table-column label="个微绑定" align="center" prop="wxAccountCount" width="85" />
- <el-table-column label="个微客户" align="center" prop="wxCustomerCount" width="85" />
- <!-- 企微 -->
- <el-table-column label="企微用户" align="center" prop="qwUserCount" width="85" />
- <el-table-column label="企微绑定" align="center" prop="qwAccountCount" width="85" />
- <!-- 业务模块 -->
- <el-table-column label="SOP" align="center" prop="sopCount" width="60" />
- <el-table-column label="课程" align="center" prop="courseCount" width="60" />
- <el-table-column label="直播" align="center" prop="liveCount" width="60" />
- <el-table-column label="商品" align="center" prop="productCount" width="60" />
- <el-table-column label="订单" align="center" prop="orderCount" width="60" />
- <!-- 龙虾引擎 -->
- <el-table-column label="工作流" align="center" prop="workflowCount" width="75" />
- <el-table-column label="龙虾模板" align="center" prop="lobsterTemplateCount" width="85" />
- <el-table-column label="龙虾任务" align="center" prop="lobsterTaskCount" width="85" />
- <!-- 组织 -->
- <el-table-column label="部门" align="center" prop="deptCount" width="60" />
- <el-table-column label="员工" align="center" prop="employeeCount" width="60" />
- <!-- 汇总 -->
- <el-table-column label="活跃模块" align="center" prop="activeModuleCount" width="85">
- <template slot-scope="scope">
- <el-tag type="success" size="mini">{{ scope.row.activeModuleCount || 0 }}</el-tag>
- </template>
- </el-table-column>
- <el-table-column label="消费总额" align="center" prop="totalConsumeAmount" width="110" fixed="right">
- <template slot-scope="scope">
- <span style="color:#1890ff;font-weight:bold">¥{{ scope.row.totalConsumeAmount || '0.00' }}</span>
- </template>
- </el-table-column>
- <el-table-column label="操作" align="center" width="80" fixed="right">
- <template slot-scope="scope">
- <el-button size="mini" type="text" icon="el-icon-view" @click="handleDetail(scope.row)">详情</el-button>
- </template>
- </el-table-column>
- </el-table>
- <pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />
- <!-- ===== 租户模型用量详情弹窗 ===== -->
- <el-dialog title="租户模型用量详情" :visible.sync="detailVisible" width="720px">
- <el-descriptions :column="3" border size="small">
- <el-descriptions-item label="租户名称" :span="2">{{ detail.tenantName }}</el-descriptions-item>
- <el-descriptions-item label="归属代理">{{ detail.proxyName || '-' }}</el-descriptions-item>
- <!-- AI模型 -->
- <el-descriptions-item label="AI Token总量">
- <span style="color:#1890ff;font-weight:bold">{{ detail.aiTokenTotal || 0 }}</span>
- </el-descriptions-item>
- <el-descriptions-item label="AI外呼总量">
- <span style="color:#722ed1;font-weight:bold">{{ detail.aiCallTotal || 0 }}</span>
- </el-descriptions-item>
- <el-descriptions-item label="AI对话总量">
- <span style="color:#52c41a;font-weight:bold">{{ detail.aiChatTotal || 0 }}</span>
- </el-descriptions-item>
- <!-- 通讯 -->
- <el-descriptions-item label="外呼拨打次数">{{ detail.outboundCallCount || 0 }}</el-descriptions-item>
- <el-descriptions-item label="短信发送量">{{ detail.smsSentCount || 0 }}</el-descriptions-item>
- <el-descriptions-item label="SOP数量">{{ detail.sopCount || 0 }}</el-descriptions-item>
- <!-- 个微 -->
- <el-descriptions-item label="个微用户数">{{ detail.wxUserCount || 0 }}</el-descriptions-item>
- <el-descriptions-item label="绑定个微账号">{{ detail.wxAccountCount || 0 }}</el-descriptions-item>
- <el-descriptions-item label="个微客户数">{{ detail.wxCustomerCount || 0 }}</el-descriptions-item>
- <!-- 企微 -->
- <el-descriptions-item label="企微用户数">{{ detail.qwUserCount || 0 }}</el-descriptions-item>
- <el-descriptions-item label="绑定企微号数">{{ detail.qwAccountCount || 0 }}</el-descriptions-item>
- <el-descriptions-item label="课程数量">{{ detail.courseCount || 0 }}</el-descriptions-item>
- <!-- 业务 -->
- <el-descriptions-item label="直播次数">{{ detail.liveCount || 0 }}</el-descriptions-item>
- <el-descriptions-item label="商品数量">{{ detail.productCount || 0 }}</el-descriptions-item>
- <el-descriptions-item label="订单数量">{{ detail.orderCount || 0 }}</el-descriptions-item>
- <!-- 龙虾 -->
- <el-descriptions-item label="工作流数量">{{ detail.workflowCount || 0 }}</el-descriptions-item>
- <el-descriptions-item label="龙虾引擎模板">{{ detail.lobsterTemplateCount || 0 }}</el-descriptions-item>
- <el-descriptions-item label="龙虾引擎任务">{{ detail.lobsterTaskCount || 0 }}</el-descriptions-item>
- <!-- 组织 -->
- <el-descriptions-item label="部门数量">{{ detail.deptCount || 0 }}</el-descriptions-item>
- <el-descriptions-item label="员工数量">{{ detail.employeeCount || 0 }}</el-descriptions-item>
- <el-descriptions-item label="活跃模块数">
- <el-tag type="success" size="mini">{{ detail.activeModuleCount || 0 }}</el-tag>
- </el-descriptions-item>
- <!-- 汇总 -->
- <el-descriptions-item label="消费总额" :span="3">
- <span style="color:#1890ff;font-weight:bold;font-size:16px">¥{{ detail.totalConsumeAmount || '0.00' }}</span>
- </el-descriptions-item>
- </el-descriptions>
- </el-dialog>
- </div>
- </template>
- <script>
- import { listModuleUsageSummary, getTenantDetail, refreshStatistics, exportModuleUsage } from '@/api/admin/moduleUsage'
- export default {
- name: 'AdminModelUsage',
- data() {
- return {
- loading: false,
- exportLoading: false,
- showSearch: true,
- list: [],
- total: 0,
- detailVisible: false,
- detail: {},
- queryParams: {
- pageNum: 1,
- pageSize: 10,
- tenantName: null,
- proxyName: null
- },
- // KPI 概览卡片
- overviewCards: [
- { label: '租户总数', value: 0, icon: 'el-icon-office-building', bg: '#e6f7ff' },
- { label: '活跃模块总数', value: 0, icon: 'el-icon-data-line', bg: '#f6ffed' },
- { label: '消费总额', value: '¥0.00', icon: 'el-icon-wallet', bg: '#fff7e6' },
- { label: 'AI调用总量', value: 0, icon: 'el-icon-cpu', bg: '#f9f0ff' }
- ]
- }
- },
- created() {
- this.getList()
- },
- methods: {
- getList() {
- this.loading = true
- listModuleUsageSummary(this.queryParams).then(res => {
- this.list = res.rows || []
- this.total = res.total || 0
- this.updateOverview(this.list)
- this.loading = false
- }).catch(() => { this.loading = false })
- },
- /** 根据列表数据更新KPI卡片 */
- updateOverview(list) {
- const totalModules = list.reduce((s, r) => s + (r.activeModuleCount || 0), 0)
- const totalConsume = list.reduce((s, r) => s + (parseFloat(r.totalConsumeAmount) || 0), 0)
- const totalAiCalls = list.reduce((s, r) => s + (r.aiCallTotal || 0) + (r.aiChatTotal || 0), 0)
- this.overviewCards[0].value = this.total
- this.overviewCards[1].value = totalModules
- this.overviewCards[2].value = '¥' + totalConsume.toFixed(2)
- this.overviewCards[3].value = totalAiCalls
- },
- /** 表格合计行 */
- getSummary({ columns, data }) {
- const sums = []
- columns.forEach((col, idx) => {
- if (idx === 0) { sums[idx] = '合计'; return }
- const prop = col.property
- if (!prop) { sums[idx] = ''; return }
- const numFields = ['aiTokenTotal','aiCallTotal','aiChatTotal','outboundCallCount','smsSentCount',
- 'wxUserCount','wxAccountCount','wxCustomerCount','qwUserCount','qwAccountCount',
- 'sopCount','courseCount','liveCount','productCount','orderCount',
- 'workflowCount','lobsterTemplateCount','lobsterTaskCount','deptCount','employeeCount','activeModuleCount']
- if (numFields.includes(prop)) {
- const val = data.reduce((s, r) => s + (r[prop] || 0), 0)
- sums[idx] = val
- } else if (prop === 'totalConsumeAmount') {
- const val = data.reduce((s, r) => s + (parseFloat(r[prop]) || 0), 0)
- sums[idx] = '¥' + val.toFixed(2)
- } else {
- sums[idx] = ''
- }
- })
- return sums
- },
- handleQuery() {
- this.queryParams.pageNum = 1
- this.getList()
- },
- resetQuery() {
- this.resetForm('queryForm')
- this.handleQuery()
- },
- handleDetail(row) {
- getTenantDetail(row.tenantId).then(res => {
- this.detail = res.data || {}
- this.detailVisible = true
- })
- },
- handleRefreshAll() {
- this.$confirm('确定触发全量统计刷新吗?', '提示', { type: 'warning' }).then(() => {
- refreshStatistics().then(() => {
- this.$message.success('全量统计任务已触发')
- this.getList()
- })
- })
- },
- handleExport() {
- this.exportLoading = true
- exportModuleUsage(this.queryParams).then(response => {
- this.download(response.msg)
- this.exportLoading = false
- }).catch(() => { this.exportLoading = false })
- }
- }
- }
- </script>
- <style scoped>
- .overview-card { cursor: default; }
- .card-inner { display: flex; align-items: center; }
- .card-icon {
- width: 46px; height: 46px; border-radius: 50%;
- display: flex; align-items: center; justify-content: center;
- margin-right: 14px; font-size: 22px; color: #fff;
- }
- .card-value { font-size: 22px; font-weight: bold; color: #333; display: block; }
- .card-label { font-size: 12px; color: #999; }
- .filter-card { padding-bottom: 0; }
- .mb16 { margin-bottom: 16px; }
- .mb8 { margin-bottom: 8px; }
- </style>
|