|
@@ -1,258 +1,220 @@
|
|
|
+<!-- 微信私域运营数据看板页面 -->
|
|
|
<template>
|
|
|
<PageWrapper class="page-wrapper">
|
|
|
+ <!-- 页面头部内容 -->
|
|
|
<template #headerContent>
|
|
|
<Flex justify="space-between" align="center">
|
|
|
<h1 class="title">微信私域用户运营数据看板</h1>
|
|
|
<Space>
|
|
|
+ <!-- 快捷日期选择按钮组 -->
|
|
|
<div>
|
|
|
<a-button type="link" size="small" @click="toDate(7)">近7天</a-button>
|
|
|
<a-button type="link" size="small" @click="toDate(1)">昨天</a-button>
|
|
|
<a-button type="link" size="small" @click="toDate(0)">今天</a-button>
|
|
|
</div>
|
|
|
- <RangePicker v-model:value="date" size="small" :allowClear="false" inputReadOnly @change="fetchData" />
|
|
|
+ <!-- 日期范围选择器 -->
|
|
|
+ <RangePicker
|
|
|
+ v-model:value="date"
|
|
|
+ size="small"
|
|
|
+ :allowClear="false"
|
|
|
+ inputReadOnly
|
|
|
+ @change="fetchData"
|
|
|
+ />
|
|
|
+ <!-- 超级管理员可见的组织选择器 -->
|
|
|
<template v-if="isSuper">
|
|
|
<UserOutlined />
|
|
|
- <Select size="small" style="width: 100px;" v-model:value="organizationId" @change="fetchData">
|
|
|
+ <Select
|
|
|
+ size="small"
|
|
|
+ style="width: 100px"
|
|
|
+ v-model:value="organizationId"
|
|
|
+ @change="fetchData"
|
|
|
+ >
|
|
|
<SelectOption :value="0">所有账号</SelectOption>
|
|
|
- <SelectOption v-for="item in departmentList" :value="item.id">{{ item.name }}</SelectOption>
|
|
|
+ <SelectOption v-for="item in departmentList" :key="item.id" :value="item.id">
|
|
|
+ {{ item.name }}
|
|
|
+ </SelectOption>
|
|
|
+ </Select>
|
|
|
+ <!-- 新增的 select -->
|
|
|
+ <Select
|
|
|
+ size="small"
|
|
|
+ style="width: 100px"
|
|
|
+ v-model:value="filterType"
|
|
|
+ @change="fetchData"
|
|
|
+ >
|
|
|
+ <SelectOption :value="0">全部类型</SelectOption>
|
|
|
+ <SelectOption :value="1">个人号</SelectOption>
|
|
|
+ <SelectOption :value="2">企业号</SelectOption>
|
|
|
</Select>
|
|
|
</template>
|
|
|
</Space>
|
|
|
</Flex>
|
|
|
</template>
|
|
|
+ <!-- 数据加载状态 -->
|
|
|
<Spin :spinning="loading" size="large">
|
|
|
+ <!-- 图表展示区域 -->
|
|
|
<Space v-if="charts" direction="vertical" :size="32">
|
|
|
+ <!-- 第一行:数据概览卡片 -->
|
|
|
<Row :gutter="[32, 32]">
|
|
|
- <Col :span="6" v-for="item in chartsAreaLineMap">
|
|
|
- <ChartsAreaLine :label="item.label" :rate="charts[item.key].rate" :title="charts[item.key].count" :labels="charts[item.key].label" :data="charts[item.key].val" />
|
|
|
+ <Col :span="6" v-for="item in chartsAreaLineMap" :key="item.key">
|
|
|
+ <ChartsAreaLine
|
|
|
+ :label="item.label"
|
|
|
+ :rate="charts[item.key].rate"
|
|
|
+ :title="charts[item.key].count"
|
|
|
+ :labels="charts[item.key].label"
|
|
|
+ :data="charts[item.key].val"
|
|
|
+ />
|
|
|
</Col>
|
|
|
</Row>
|
|
|
+ <!-- 第二行:趋势图表 -->
|
|
|
<Row :gutter="32">
|
|
|
<Col :span="12">
|
|
|
- <ChartsLine title="Token 消耗趋势" :labels="charts.consume_token.label" :data="charts.consume_token.val" />
|
|
|
+ <ChartsLine
|
|
|
+ title="Token 消耗趋势"
|
|
|
+ :labels="charts.consume_token.label"
|
|
|
+ :data="charts.consume_token.val"
|
|
|
+ />
|
|
|
</Col>
|
|
|
<Col :span="12">
|
|
|
- <ChartsLine title="用户增长趋势" :labels="charts.new_user.label" :data="charts.new_user.val" />
|
|
|
+ <ChartsLine
|
|
|
+ title="用户增长趋势"
|
|
|
+ :labels="charts.new_user.label"
|
|
|
+ :data="charts.new_user.val"
|
|
|
+ />
|
|
|
</Col>
|
|
|
</Row>
|
|
|
+ <!-- 第三行:标签分布和账号分析 -->
|
|
|
<Row :gutter="32">
|
|
|
<Col :span="12">
|
|
|
<ChartsPie title="标签分布" :data="charts.label_dist" />
|
|
|
</Col>
|
|
|
<Col :span="12">
|
|
|
- <TableList
|
|
|
- title="账号分析"
|
|
|
- :columns="columns"
|
|
|
- :data="tableData" />
|
|
|
+ <TableList title="账号分析" :columns="columns" :data="tableData" />
|
|
|
</Col>
|
|
|
</Row>
|
|
|
</Space>
|
|
|
</Spin>
|
|
|
</PageWrapper>
|
|
|
</template>
|
|
|
+
|
|
|
<script lang="ts" setup>
|
|
|
-import { onMounted, ref } from 'vue'
|
|
|
-import { PageWrapper } from '@/components/Page';
|
|
|
-import { Flex, Row, Col, Space, Select, SelectOption, RangePicker, Spin } from 'ant-design-vue'
|
|
|
-import { UserOutlined } from '@ant-design/icons-vue'
|
|
|
-import ChartsAreaLine from './components/ChartsAreaLine.vue';
|
|
|
-import ChartsLine from './components/ChartsLine.vue';
|
|
|
-import ChartsPie from './components/ChartsPie.vue';
|
|
|
-import TableList from './components/TableList.vue';
|
|
|
-import dayjs from 'dayjs';
|
|
|
-import { getDashboardCharts, getDashboardWxList } from '/@/api/dashboard/dashboard';
|
|
|
-import { getDepartmentList } from '@/api/sys/department';
|
|
|
-import { useUserStore } from '/@/store/modules/user';
|
|
|
-import { DepartmentInfo } from '/@/api/sys/model/departmentModel';
|
|
|
+ // 导入所需的组件和工具
|
|
|
+ import { onMounted, ref } from 'vue';
|
|
|
+ import { PageWrapper } from '@/components/Page';
|
|
|
+ import { Flex, Row, Col, Space, Select, SelectOption, RangePicker, Spin } from 'ant-design-vue';
|
|
|
+ import { UserOutlined } from '@ant-design/icons-vue';
|
|
|
+ import ChartsAreaLine from './components/ChartsAreaLine.vue';
|
|
|
+ import ChartsLine from './components/ChartsLine.vue';
|
|
|
+ import ChartsPie from './components/ChartsPie.vue';
|
|
|
+ import TableList from './components/TableList.vue';
|
|
|
+ import dayjs from 'dayjs';
|
|
|
+ import { getDashboardCharts, getDashboardWxList } from '/@/api/dashboard/dashboard';
|
|
|
+ import { getDepartmentList } from '@/api/sys/department';
|
|
|
+ import { useUserStore } from '/@/store/modules/user';
|
|
|
+ import { DepartmentInfo } from '/@/api/sys/model/departmentModel';
|
|
|
|
|
|
-// 用户信息
|
|
|
-const userStore = useUserStore();
|
|
|
-const userInfo = userStore.getUserInfo
|
|
|
-// 是否是管理员
|
|
|
-const isSuper = userInfo.roleName.some(str => str == '超级管理员')
|
|
|
+ // 用户信息和权限判断
|
|
|
+ const userStore = useUserStore();
|
|
|
+ const userInfo = userStore.getUserInfo;
|
|
|
+ const isSuper = userInfo.roleName.some((str) => str == '超级管理员');
|
|
|
|
|
|
-const loading = ref(false)
|
|
|
-const organizationId = ref(0)
|
|
|
-// 机构列表
|
|
|
-const departmentList = ref<DepartmentInfo[]>([])
|
|
|
+ // 页面状态管理
|
|
|
+ const loading = ref(false);
|
|
|
+ const organizationId = ref(0);
|
|
|
+ const departmentList = ref<DepartmentInfo[]>([]);
|
|
|
+ const charts = ref();
|
|
|
+ const date = ref<[dayjs.Dayjs, dayjs.Dayjs]>();
|
|
|
+ const tableData = ref<object[]>([]);
|
|
|
+ const filterType = ref(0);
|
|
|
|
|
|
-const charts = ref()
|
|
|
-const chartsAreaLineMap = [{
|
|
|
- label: 'AI 回复次数',
|
|
|
- key: 'ai_response'
|
|
|
-}, {
|
|
|
- label: 'SOP执行次数',
|
|
|
- key: 'sop_run'
|
|
|
-}, {
|
|
|
- label: '总好友数',
|
|
|
- key: 'total_friend'
|
|
|
-}, {
|
|
|
- label: '总群数',
|
|
|
- key: 'total_group'
|
|
|
-}, {
|
|
|
- label: '账户余额',
|
|
|
- key: 'account_balance'
|
|
|
-}, {
|
|
|
- label: 'Token消耗',
|
|
|
- key: 'consume_token'
|
|
|
-}, {
|
|
|
- label: '活跃用户',
|
|
|
- key: 'active_user'
|
|
|
-}, {
|
|
|
- label: '新增好友',
|
|
|
- key: 'new_user'
|
|
|
-}]
|
|
|
-const date = ref<[dayjs.Dayjs, dayjs.Dayjs]>()
|
|
|
+ // 图表配置数据
|
|
|
+ const chartsAreaLineMap = [
|
|
|
+ { label: 'AI 回复次数', key: 'ai_response' },
|
|
|
+ { label: 'SOP执行次数', key: 'sop_run' },
|
|
|
+ { label: '总好友数', key: 'total_friend' },
|
|
|
+ { label: '总群数', key: 'total_group' },
|
|
|
+ { label: '账户余额', key: 'account_balance' },
|
|
|
+ { label: 'Token消耗', key: 'consume_token' },
|
|
|
+ { label: '活跃用户', key: 'active_user' },
|
|
|
+ { label: '新增好友', key: 'new_user' },
|
|
|
+ ];
|
|
|
|
|
|
-const columns = [{
|
|
|
- title: '账号',
|
|
|
- dataIndex: 'nickname'
|
|
|
-}, {
|
|
|
- title: '好友数',
|
|
|
- dataIndex: 'total_friend'
|
|
|
-}, {
|
|
|
- title: '群数量',
|
|
|
- dataIndex: 'total_group'
|
|
|
-}, {
|
|
|
- title: '互动率',
|
|
|
- dataIndex: 'interaction_rate'
|
|
|
-}]
|
|
|
+ // 表格列配置
|
|
|
+ const columns = [
|
|
|
+ { title: '账号', dataIndex: 'nickname' },
|
|
|
+ { title: '好友数', dataIndex: 'total_friend' },
|
|
|
+ { title: '群数量', dataIndex: 'total_group' },
|
|
|
+ { title: '互动率', dataIndex: 'interaction_rate' },
|
|
|
+ ];
|
|
|
|
|
|
-const tableData = ref<object[]>([])
|
|
|
+ // 页面初始化
|
|
|
+ onMounted(async () => {
|
|
|
+ toDate(0); // 默认显示今天的数据
|
|
|
+ if (isSuper) {
|
|
|
+ // 如果是超级管理员,加载部门列表
|
|
|
+ const res = await getDepartmentList({ page: 1, pageSize: 100 });
|
|
|
+ departmentList.value = res.data.data;
|
|
|
+ }
|
|
|
+ });
|
|
|
|
|
|
-onMounted(async () => {
|
|
|
- toDate(0)
|
|
|
- if (isSuper) {
|
|
|
- const res = await getDepartmentList({ page: 1, pageSize: 100 })
|
|
|
- departmentList.value = res.data.data
|
|
|
+ // 获取图表数据
|
|
|
+ async function fetchCharts() {
|
|
|
+ try {
|
|
|
+ loading.value = true;
|
|
|
+ let start_date = '';
|
|
|
+ let end_date = '';
|
|
|
+ if (date.value && date.value.length) {
|
|
|
+ [start_date, end_date] = date.value.map((o) => o.format('YYYY-MM-DD'));
|
|
|
+ }
|
|
|
+ const res = await getDashboardCharts({
|
|
|
+ start_date,
|
|
|
+ end_date,
|
|
|
+ organizationId: organizationId.value,
|
|
|
+ });
|
|
|
+ charts.value = res.data;
|
|
|
+ } finally {
|
|
|
+ loading.value = false;
|
|
|
+ }
|
|
|
}
|
|
|
-})
|
|
|
|
|
|
-async function fetchCharts() {
|
|
|
- try {
|
|
|
- loading.value = true
|
|
|
- let start_date = ''
|
|
|
- let end_date = ''
|
|
|
- if (date.value && date.value.length) {
|
|
|
- [ start_date, end_date ] = date.value.map(o => o.format('YYYY-MM-DD'))
|
|
|
+ // 获取账号列表数据
|
|
|
+ async function fetchList() {
|
|
|
+ try {
|
|
|
+ let end_date = dayjs().format('YYYY-MM-DD');
|
|
|
+ if (date.value && date.value.length) {
|
|
|
+ end_date = date.value[1].format('YYYY-MM-DD');
|
|
|
+ }
|
|
|
+ const res = await getDashboardWxList({
|
|
|
+ page: 1,
|
|
|
+ pageSize: 1000,
|
|
|
+ end_date,
|
|
|
+ organizationId: organizationId.value,
|
|
|
+ });
|
|
|
+ tableData.value = res.data.data;
|
|
|
+ } catch (error) {
|
|
|
+ console.error(error);
|
|
|
}
|
|
|
- const res = await getDashboardCharts({
|
|
|
- start_date,
|
|
|
- end_date,
|
|
|
- organizationId: organizationId.value
|
|
|
- })
|
|
|
- charts.value = res.data
|
|
|
- } finally {
|
|
|
- loading.value = false
|
|
|
}
|
|
|
-}
|
|
|
-async function fetchList() {
|
|
|
- try {
|
|
|
- let end_date = dayjs().format('YYYY-MM-DD')
|
|
|
- if (date.value && date.value.length) {
|
|
|
- end_date = date.value[1].format('YYYY-MM-DD')
|
|
|
- }
|
|
|
- const res = await getDashboardWxList({ page: 1, pageSize: 1000, end_date, organizationId: organizationId.value })
|
|
|
- tableData.value = res.data.data
|
|
|
- } finally {
|
|
|
- // tableData.value = [{
|
|
|
- // wechat: '微信账号1',
|
|
|
- // friend: 856,
|
|
|
- // group: 45,
|
|
|
- // interaction: '78.5%'
|
|
|
- // }, {
|
|
|
- // wechat: '微信账号1',
|
|
|
- // friend: 856,
|
|
|
- // group: 45,
|
|
|
- // interaction: '78.5%'
|
|
|
- // }, {
|
|
|
- // wechat: '微信账号1',
|
|
|
- // friend: 856,
|
|
|
- // group: 45,
|
|
|
- // interaction: '78.5%'
|
|
|
- // }, {
|
|
|
- // wechat: '微信账号1',
|
|
|
- // friend: 856,
|
|
|
- // group: 45,
|
|
|
- // interaction: '78.5%'
|
|
|
- // }, {
|
|
|
- // wechat: '微信账号1',
|
|
|
- // friend: 856,
|
|
|
- // group: 45,
|
|
|
- // interaction: '78.5%'
|
|
|
- // }, {
|
|
|
- // wechat: '微信账号1',
|
|
|
- // friend: 856,
|
|
|
- // group: 45,
|
|
|
- // interaction: '78.5%'
|
|
|
- // }, {
|
|
|
- // wechat: '微信账号1',
|
|
|
- // friend: 856,
|
|
|
- // group: 45,
|
|
|
- // interaction: '78.5%'
|
|
|
- // }, {
|
|
|
- // wechat: '微信账号1',
|
|
|
- // friend: 856,
|
|
|
- // group: 45,
|
|
|
- // interaction: '78.5%'
|
|
|
- // }, {
|
|
|
- // wechat: '微信账号1',
|
|
|
- // friend: 856,
|
|
|
- // group: 45,
|
|
|
- // interaction: '78.5%'
|
|
|
- // }, {
|
|
|
- // wechat: '微信账号1',
|
|
|
- // friend: 856,
|
|
|
- // group: 45,
|
|
|
- // interaction: '78.5%'
|
|
|
- // }, {
|
|
|
- // wechat: '微信账号1',
|
|
|
- // friend: 856,
|
|
|
- // group: 45,
|
|
|
- // interaction: '78.5%'
|
|
|
- // }, {
|
|
|
- // wechat: '微信账号1',
|
|
|
- // friend: 856,
|
|
|
- // group: 45,
|
|
|
- // interaction: '78.5%'
|
|
|
- // }, {
|
|
|
- // wechat: '微信账号1',
|
|
|
- // friend: 856,
|
|
|
- // group: 45,
|
|
|
- // interaction: '78.5%'
|
|
|
- // }, {
|
|
|
- // wechat: '微信账号1',
|
|
|
- // friend: 856,
|
|
|
- // group: 45,
|
|
|
- // interaction: '78.5%'
|
|
|
- // }, {
|
|
|
- // wechat: '微信账号1',
|
|
|
- // friend: 856,
|
|
|
- // group: 45,
|
|
|
- // interaction: '78.5%'
|
|
|
- // }, {
|
|
|
- // wechat: '微信账号1',
|
|
|
- // friend: 856,
|
|
|
- // group: 45,
|
|
|
- // interaction: '78.5%'
|
|
|
- // }]
|
|
|
+
|
|
|
+ // 刷新所有数据
|
|
|
+ function fetchData() {
|
|
|
+ fetchCharts();
|
|
|
+ fetchList();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 快捷设置日期范围
|
|
|
+ function toDate(day) {
|
|
|
+ date.value = [dayjs().subtract(day, 'day'), dayjs()];
|
|
|
+ fetchData();
|
|
|
}
|
|
|
-}
|
|
|
-function fetchData() {
|
|
|
- fetchCharts()
|
|
|
- fetchList()
|
|
|
-}
|
|
|
-function toDate(day) {
|
|
|
- date.value = [dayjs().subtract(day, 'day'), dayjs()]
|
|
|
- fetchData()
|
|
|
-}
|
|
|
</script>
|
|
|
+
|
|
|
<style lang="scss" scoped>
|
|
|
-.page-wrapper {
|
|
|
- background: #fff;
|
|
|
-}
|
|
|
+ // 面样式
|
|
|
+ .page-wrapper {
|
|
|
+ background: #fff;
|
|
|
+ }
|
|
|
|
|
|
-.title {
|
|
|
- font-size: 1.5em;
|
|
|
-}
|
|
|
+ .title {
|
|
|
+ font-size: 1.5em;
|
|
|
+ }
|
|
|
</style>
|