index.vue 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. <template>
  2. <div class="p-4">
  3. <!-- <GrowCard :loading="loading" class="enter-y" /> -->
  4. <BasicTable @register="registerTable">
  5. <template #headerTop>
  6. <div class="md:flex enter-y">
  7. <div class="md:w-1/2 enter-y">
  8. <Card class="w-full">
  9. <VisitAnalysisBar
  10. :loading="loading"
  11. :viewStatics="viewStaticsData"
  12. :shareStatics="shareStaticsData"
  13. />
  14. </Card>
  15. </div>
  16. <div class="!md:mx-2"></div>
  17. <Card class="md:w-1/2 enter-y">
  18. <!-- <VisitAnalysis :loading="loading" /> -->
  19. <VisitAnalysis
  20. :loading="loading"
  21. :bulletChatAmounts="bulletChatAmountsData"
  22. :userAmount="userAmountData"
  23. />
  24. </Card>
  25. </div>
  26. </template>
  27. <template #toolbar>
  28. <a-button type="primary" @click="handleExport">导出数据</a-button>
  29. </template>
  30. <template #Time="{ record }">
  31. <Time :value="record.createTime" mode="datetime" />
  32. </template>
  33. <template #Time1="{ record }">
  34. <Time v-if="record.firstEnterRoomTime" :value="record.firstEnterRoomTime" mode="datetime" />
  35. </template>
  36. <template #Time2="{ record }">
  37. <Time v-if="record.lastExistRoomTime" :value="record.lastExistRoomTime" mode="datetime" />
  38. </template>
  39. </BasicTable>
  40. <!-- <div class="md:flex enter-y">
  41. <div class="md:w-1/2 enter-y">
  42. <Card class="w-full">
  43. <VisitAnalysisBar :loading="loading" />
  44. </Card>
  45. </div>
  46. <div class="!md:mx-2"></div>
  47. <Card class="md:w-1/2 enter-y">
  48. <VisitAnalysis :loading="loading" />
  49. </Card>
  50. </div>
  51. <div class="md:flex enter-y mt-4">
  52. <Card class="md:w-1/2 md:w-full"> </Card>
  53. </div> -->
  54. </div>
  55. </template>
  56. <script lang="ts" setup>
  57. import { onMounted, ref } from 'vue';
  58. import { BasicTable, useTable, BasicColumn, FormProps } from '/@/components/Table';
  59. // import { useI18n } from '/@/hooks/web/useI18n';
  60. // import GrowCard from './components/GrowCard.vue';
  61. import { Card } from 'ant-design-vue';
  62. import VisitAnalysis from './components/VisitAnalysis.vue';
  63. import VisitAnalysisBar from './components/VisitAnalysisBar.vue';
  64. import { Time } from '/@/components/Time';
  65. import {
  66. bulletChatApi,
  67. userStaticsApi,
  68. bulletChatStaticsApi,
  69. bulletChatExportApi,
  70. } from '/@/api/dashboard/analysis';
  71. import { listRoomsApi } from '/@/api/scene/list';
  72. import { dateUtil, formatToDate } from '/@/utils/dateUtil';
  73. import { StaticItemType } from '/@/api/dashboard/model';
  74. const today = formatToDate(dateUtil(new Date()));
  75. const priorDate = formatToDate(dateUtil(new Date().setDate(new Date().getDate() - 30)));
  76. const loading = ref(true);
  77. // UserStaticsModel
  78. const viewStaticsData = ref<StaticItemType[]>([]);
  79. const shareStaticsData = ref<StaticItemType[]>([]);
  80. const bulletChatAmountsData = ref<StaticItemType[]>([]);
  81. const userAmountData = ref<StaticItemType[]>([]);
  82. // const { t } = useI18n();
  83. const columns: BasicColumn[] = [
  84. {
  85. title: '微信昵称',
  86. dataIndex: 'nickName',
  87. width: 120,
  88. ellipsis: true,
  89. customRender: ({ record }) => {
  90. const { nickName } = record;
  91. return nickName?.length ? nickName : ' -- ';
  92. },
  93. },
  94. {
  95. title: '手机号',
  96. dataIndex: 'userName',
  97. width: 120,
  98. customRender: ({ record }) => {
  99. const { userName } = record;
  100. let reg = /(\d{3})\d{4}(\d{4})/;
  101. const macthUserName = userName && userName.replace(reg, '$1****$2');
  102. return macthUserName;
  103. },
  104. },
  105. {
  106. title: '房间名称',
  107. dataIndex: 'liveRoomName',
  108. width: 120,
  109. },
  110. {
  111. title: '在线时长(min)',
  112. dataIndex: 'duration',
  113. width: 120,
  114. },
  115. {
  116. title: '初次进入房间',
  117. dataIndex: 'firstEnterRoomTime',
  118. slots: { customRender: 'Time1' },
  119. width: 150,
  120. },
  121. {
  122. title: '最后离开房间',
  123. dataIndex: 'lastExistRoomTime',
  124. slots: { customRender: 'Time2' },
  125. width: 150,
  126. },
  127. {
  128. title: '邀请者',
  129. dataIndex: 'inviterUserName',
  130. width: 120,
  131. },
  132. {
  133. title: '留言条数',
  134. dataIndex: 'bulletChatAmount',
  135. width: 120,
  136. },
  137. {
  138. title: '留言内容',
  139. dataIndex: 'bulletChatContents',
  140. width: 320,
  141. },
  142. ];
  143. const searchForm: Partial<FormProps> = {
  144. labelWidth: 100,
  145. schemas: [
  146. {
  147. field: 'liveRoomId',
  148. label: '全部房间',
  149. component: 'ApiSelect',
  150. // required: true,
  151. colProps: {
  152. xl: 5,
  153. xxl: 5,
  154. },
  155. componentProps: {
  156. api: listRoomsApi,
  157. labelField: 'name',
  158. resultField: 'list',
  159. valueField: 'id',
  160. immediate: false,
  161. showSearch: true,
  162. optionFilterProp: 'label',
  163. params: {
  164. page: 1,
  165. limit: 1000,
  166. state: 103,
  167. },
  168. },
  169. },
  170. {
  171. field: 'time',
  172. label: '时间段',
  173. component: 'RangePicker',
  174. defaultValue: [priorDate, today],
  175. colProps: {
  176. xl: 6,
  177. xxl: 6,
  178. },
  179. componentProps: {
  180. format: 'YYYY-MM-DD',
  181. showTime: false,
  182. disabledDate(current) {
  183. // console.log('current', current, date);
  184. return current && current > dateUtil().endOf('day');
  185. },
  186. // onChange: (value) => {
  187. // console.log('onchange', value);
  188. // },
  189. },
  190. rules: [
  191. {
  192. required: true,
  193. // @ts-ignore
  194. validator: async (rule, value) => {
  195. if (!value) {
  196. return Promise.reject('请选择时间段');
  197. }
  198. const days = Math.abs(value[1].diff(value[0], 'days'));
  199. console.log('days', days);
  200. if (days > 30) {
  201. return Promise.reject('选择时间段应小于30天');
  202. }
  203. if (days < 1) {
  204. return Promise.reject('至少选择2天以上');
  205. }
  206. return Promise.resolve();
  207. },
  208. trigger: 'change',
  209. },
  210. ],
  211. },
  212. {
  213. field: 'inviterUserName',
  214. label: '邀请者',
  215. component: 'Input',
  216. componentProps: {
  217. maxLength: 100,
  218. },
  219. colProps: {
  220. xl: 5,
  221. xxl: 5,
  222. },
  223. },
  224. ],
  225. resetFunc: handleReset,
  226. };
  227. const searchInfo = ref({
  228. liveRoomId: '',
  229. // limit: 20,
  230. // page: 1,
  231. time: [priorDate, today],
  232. });
  233. const [registerTable] = useTable({
  234. title: '房间留言状态',
  235. columns: columns,
  236. useSearchForm: true,
  237. formConfig: searchForm,
  238. immediate: true,
  239. api: bulletChatApi,
  240. beforeFetch: (data) => {
  241. console.log('beforeFetch', data);
  242. data.time = data.time.map((item) => formatToDate(item));
  243. handleStatic(data);
  244. searchInfo.value = {
  245. liveRoomId: data.liveRoomId,
  246. time: data.time,
  247. };
  248. return data;
  249. },
  250. afterFetch: function (data) {
  251. console.log('afterFetch', arguments);
  252. return data;
  253. },
  254. searchInfo: searchInfo,
  255. handleSearchInfoFn(data) {
  256. searchInfo.value = Object.assign(searchInfo.value, data);
  257. console.log(searchInfo.value);
  258. return;
  259. },
  260. });
  261. onMounted(() => {
  262. handleStatic();
  263. });
  264. async function handleStatic(search?: any) {
  265. try {
  266. loading.value = true;
  267. const searchInfoParams = search || searchInfo.value;
  268. const sData = await userStaticsApi(searchInfoParams);
  269. const bData = await bulletChatStaticsApi(searchInfoParams);
  270. console.log('sData', sData);
  271. console.log('bData', sData);
  272. viewStaticsData.value = sData.viewStatics;
  273. shareStaticsData.value = sData.shareStatics;
  274. userAmountData.value = bData.userAmount;
  275. bulletChatAmountsData.value = bData.bulletChatAmounts;
  276. loading.value = false;
  277. } catch (error) {
  278. loading.value = false;
  279. }
  280. }
  281. async function handleExport() {
  282. const data = await bulletChatExportApi(searchInfo.value);
  283. const downloadBlob = new Blob([data], {
  284. type: 'application/msexcel',
  285. });
  286. const url = URL.createObjectURL(downloadBlob);
  287. const a: HTMLAnchorElement = document.createElement('a');
  288. document.body.appendChild(a);
  289. a.style.display = 'none';
  290. a.href = url;
  291. const name = new Date().getTime();
  292. a.download = `${name}.csv`;
  293. a.click();
  294. window.URL.revokeObjectURL(url);
  295. }
  296. async function handleReset() {
  297. searchInfo.value = {
  298. liveRoomId: '',
  299. // limit: 20,
  300. // page: 1,
  301. time: [priorDate, today],
  302. };
  303. }
  304. // setTimeout(() => {
  305. // loading.value = false;
  306. // }, 1500);
  307. </script>