index.vue 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. <!-- 微信私域运营数据看板页面 -->
  2. <template>
  3. <PageWrapper class="page-wrapper">
  4. <!-- 页面头部内容 -->
  5. <template #headerContent>
  6. <Flex justify="space-between" align="center">
  7. <h1 class="title">微信私域用户运营数据看板</h1>
  8. <Space>
  9. <!-- 快捷日期选择按钮组 -->
  10. <div>
  11. <a-button type="link" size="small" @click="toDate(7)">近7天</a-button>
  12. <a-button type="link" size="small" @click="toDate(1)">昨天</a-button>
  13. <a-button type="link" size="small" @click="toDate(0)">今天</a-button>
  14. </div>
  15. <!-- 日期范围选择器 -->
  16. <RangePicker
  17. v-model:value="date"
  18. size="small"
  19. :allowClear="false"
  20. inputReadOnly
  21. @change="fetchData"
  22. />
  23. <!-- 超级管理员可见的组织选择器 -->
  24. <template v-if="isSuper">
  25. <UserOutlined />
  26. <Select
  27. size="small"
  28. style="width: 100px"
  29. v-model:value="organizationId"
  30. @change="fetchData"
  31. >
  32. <SelectOption :value="0">所有账号</SelectOption>
  33. <SelectOption v-for="item in departmentList" :key="item.id" :value="item.id">
  34. {{ item.name }}
  35. </SelectOption>
  36. </Select>
  37. </template>
  38. </Space>
  39. </Flex>
  40. </template>
  41. <!-- 数据加载状态 -->
  42. <Spin :spinning="loading" size="large">
  43. <!-- 图表展示区域 -->
  44. <Space v-if="charts" direction="vertical" :size="32">
  45. <!-- 第一行:数据概览卡片 -->
  46. <Row :gutter="[32, 32]">
  47. <Col :span="6" v-for="item in chartsAreaLineMap" :key="item.key">
  48. <ChartsAreaLine
  49. :label="item.label"
  50. :rate="charts[item.key].rate"
  51. :title="charts[item.key].count"
  52. :labels="charts[item.key].label"
  53. :data="charts[item.key].val"
  54. />
  55. </Col>
  56. </Row>
  57. <!-- 第二行:趋势图表 -->
  58. <Row :gutter="32">
  59. <Col :span="12">
  60. <ChartsLine
  61. title="Token 消耗趋势"
  62. :labels="charts.consume_token.label"
  63. :data="charts.consume_token.val"
  64. />
  65. </Col>
  66. <Col :span="12">
  67. <ChartsLine
  68. title="用户增长趋势"
  69. :labels="charts.new_user.label"
  70. :data="charts.new_user.val"
  71. />
  72. </Col>
  73. </Row>
  74. <!-- 第三行:标签分布和账号分析 -->
  75. <Row :gutter="32">
  76. <Col :span="12">
  77. <ChartsPie title="标签分布" :data="charts.label_dist" />
  78. </Col>
  79. <Col :span="12">
  80. <TableList title="账号分析" :columns="columns" :data="tableData" />
  81. </Col>
  82. </Row>
  83. </Space>
  84. </Spin>
  85. </PageWrapper>
  86. </template>
  87. <script lang="ts" setup>
  88. // 导入所需的组件和工具
  89. import { onMounted, ref } from 'vue';
  90. import { PageWrapper } from '@/components/Page';
  91. import { Flex, Row, Col, Space, Select, SelectOption, RangePicker, Spin } from 'ant-design-vue';
  92. import { UserOutlined } from '@ant-design/icons-vue';
  93. import ChartsAreaLine from './components/ChartsAreaLine.vue';
  94. import ChartsLine from './components/ChartsLine.vue';
  95. import ChartsPie from './components/ChartsPie.vue';
  96. import TableList from './components/TableList.vue';
  97. import dayjs from 'dayjs';
  98. import { getDashboardCharts, getDashboardWxList } from '/@/api/dashboard/dashboard';
  99. import { getDepartmentList } from '@/api/sys/department';
  100. import { useUserStore } from '/@/store/modules/user';
  101. import { DepartmentInfo } from '/@/api/sys/model/departmentModel';
  102. // 用户信息和权限判断
  103. const userStore = useUserStore();
  104. const userInfo = userStore.getUserInfo;
  105. const isSuper = userInfo.roleName.some((str) => str == '超级管理员');
  106. // 页面状态管理
  107. const loading = ref(false);
  108. const organizationId = ref(0);
  109. const departmentList = ref<DepartmentInfo[]>([]);
  110. const charts = ref();
  111. const date = ref<[dayjs.Dayjs, dayjs.Dayjs]>();
  112. const tableData = ref<object[]>([]);
  113. // 图表配置数据
  114. const chartsAreaLineMap = [
  115. { label: 'AI 回复次数', key: 'ai_response' },
  116. { label: 'SOP执行次数', key: 'sop_run' },
  117. { label: '总好友数', key: 'total_friend' },
  118. { label: '总群数', key: 'total_group' },
  119. { label: '账户余额', key: 'account_balance' },
  120. { label: 'Token消耗', key: 'consume_token' },
  121. { label: '活跃用户', key: 'active_user' },
  122. { label: '新增好友', key: 'new_user' },
  123. ];
  124. // 表格列配置
  125. const columns = [
  126. { title: '账号', dataIndex: 'nickname' },
  127. { title: '好友数', dataIndex: 'total_friend' },
  128. { title: '群数量', dataIndex: 'total_group' },
  129. { title: '互动率', dataIndex: 'interaction_rate' },
  130. ];
  131. // 页面初始化
  132. onMounted(async () => {
  133. toDate(0); // 默认显示今天的数据
  134. if (isSuper) {
  135. // 如果是超级管理员,加载部门列表
  136. const res = await getDepartmentList({ page: 1, pageSize: 100 });
  137. departmentList.value = res.data.data;
  138. }
  139. });
  140. // 获取图表数据
  141. async function fetchCharts() {
  142. try {
  143. loading.value = true;
  144. let start_date = '';
  145. let end_date = '';
  146. if (date.value && date.value.length) {
  147. [start_date, end_date] = date.value.map((o) => o.format('YYYY-MM-DD'));
  148. }
  149. const res = await getDashboardCharts({
  150. start_date,
  151. end_date,
  152. organizationId: organizationId.value,
  153. });
  154. charts.value = res.data;
  155. } finally {
  156. loading.value = false;
  157. }
  158. }
  159. // 获取账号列表数据
  160. async function fetchList() {
  161. try {
  162. let end_date = dayjs().format('YYYY-MM-DD');
  163. if (date.value && date.value.length) {
  164. end_date = date.value[1].format('YYYY-MM-DD');
  165. }
  166. const res = await getDashboardWxList({
  167. page: 1,
  168. pageSize: 1000,
  169. end_date,
  170. organizationId: organizationId.value,
  171. });
  172. tableData.value = res.data.data;
  173. } catch (error) {
  174. console.error(error);
  175. }
  176. }
  177. // 刷新所有数据
  178. function fetchData() {
  179. fetchCharts();
  180. fetchList();
  181. }
  182. // 快捷设置日期范围
  183. function toDate(day) {
  184. date.value = [dayjs().subtract(day, 'day'), dayjs()];
  185. fetchData();
  186. }
  187. </script>
  188. <style lang="scss" scoped>
  189. // 页面样式
  190. .page-wrapper {
  191. background: #fff;
  192. }
  193. .title {
  194. font-size: 1.5em;
  195. }
  196. </style>