Browse Source

更新下拉框组件,单选改为多选;更新事件分析页面接口;更新事件管理页面接口;更新表单二次确认框的弹出逻辑;更新排序逻辑

fxs 9 months ago
parent
commit
4bafe45f40
37 changed files with 753 additions and 280 deletions
  1. 2 0
      components.d.ts
  2. 1 1
      package.json
  3. 0 6
      public/img/platformIcon/chrome.svg
  4. 0 3
      public/img/platformIcon/tiktok.svg
  5. 1 0
      public/img/platformIcon/tt.svg
  6. 1 0
      public/img/platformIcon/web.svg
  7. 0 6
      public/img/platformIcon/wechat.svg
  8. 1 0
      public/img/platformIcon/wx.svg
  9. BIN
      src/assets/default/defaultHead.png
  10. 0 6
      src/assets/platformIcon/chrome.svg
  11. 0 3
      src/assets/platformIcon/tiktok.svg
  12. 0 6
      src/assets/platformIcon/wechat.svg
  13. 21 6
      src/components/Table.vue
  14. 9 4
      src/components/common/Dialog.vue
  15. 192 0
      src/components/common/WithIconSelect.vue
  16. 1 1
      src/components/dataAnalysis/DropDownSelection.vue
  17. 65 2
      src/components/dataAnalysis/HeaderCard.vue
  18. 11 8
      src/components/dataAnalysis/TemporalTrend.vue
  19. 40 12
      src/components/form/Form.vue
  20. 11 1
      src/hooks/useForm.ts
  21. 13 4
      src/hooks/useRequest.ts
  22. 51 29
      src/hooks/useTable.ts
  23. 1 1
      src/router/home.ts
  24. 2 3
      src/utils/axios/axiosInstance.ts
  25. 1 1
      src/utils/common/index.ts
  26. 0 1
      src/utils/table/table.ts
  27. 21 7
      src/views/AppManage/BaseInfoView.vue
  28. 45 45
      src/views/AppManage/EventDetailsView.vue
  29. 7 8
      src/views/AppManage/EventManageView.vue
  30. 80 42
      src/views/AppManage/EventMangeTable.vue
  31. 78 49
      src/views/Home/Analysis/EventAnalysisDetail.vue
  32. 58 13
      src/views/Home/Analysis/EventAnalysisTable.vue
  33. 30 2
      src/views/Home/Analysis/EventAnalysisView.vue
  34. 3 3
      src/views/Home/Analysis/KeepView.vue
  35. 3 3
      src/views/Home/Analysis/UserTrendView.vue
  36. 3 3
      src/views/Home/Overview/OverView.vue
  37. 1 1
      src/views/Index.vue

+ 2 - 0
components.d.ts

@@ -36,6 +36,7 @@ declare module 'vue' {
     ElTabPane: typeof import('element-plus/es')['ElTabPane']
     ElTabs: typeof import('element-plus/es')['ElTabs']
     ElTag: typeof import('element-plus/es')['ElTag']
+    ElTex: typeof import('element-plus/es')['ElTex']
     ElText: typeof import('element-plus/es')['ElText']
     ElTooltip: typeof import('element-plus/es')['ElTooltip']
     FilterPopover: typeof import('./src/components/toolsBtn/FilterPopover.vue')['default']
@@ -56,6 +57,7 @@ declare module 'vue' {
     Table: typeof import('./src/components/Table.vue')['default']
     TemporalTrend: typeof import('./src/components/dataAnalysis/TemporalTrend.vue')['default']
     TimeLineChart: typeof import('./src/components/echarts/TimeLineChart.vue')['default']
+    WithIconSelect: typeof import('./src/components/common/WithIconSelect.vue')['default']
   }
   export interface ComponentCustomProperties {
     vLoading: typeof import('element-plus/es')['ElLoadingDirective']

+ 1 - 1
package.json

@@ -4,7 +4,7 @@
   "private": true,
   "type": "module",
   "scripts": {
-    "dev": "vite --host 0.0.0.0",
+    "dev": "vite",
     "build": "run-p type-check \"build-only {@}\" --",
     "preview": "vite preview",
     "build-only": "vite build",

+ 0 - 6
public/img/platformIcon/chrome.svg

@@ -1,6 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 15 15">
-	<path fill="currentColor" d="M2.503 1.907A7.47 7.47 0 0 1 7.5 0a7.5 7.5 0 0 1 6.635 4H7.5a3.5 3.5 0 0 0-3.23 2.149z" />
-	<path fill="currentColor" d="M1.745 2.69a7.503 7.503 0 0 0 3.41 11.937l2.812-3.658Q7.737 11 7.5 11a3.5 3.5 0 0 1-3.412-2.716a.5.5 0 0 1-.05-.092z" />
-	<path fill="currentColor" d="M6.215 14.89Q6.842 15 7.5 15a7.5 7.5 0 0 0 7.072-10.005L14.5 5H9.95A3.5 3.5 0 0 1 11 7.5a3.5 3.5 0 0 1-.953 2.405z" />
-	<path fill="currentColor" d="M5 7.5a2.5 2.5 0 1 1 5 0a2.5 2.5 0 0 1-5 0" />
-</svg>

+ 0 - 3
public/img/platformIcon/tiktok.svg

@@ -1,3 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
-	<path fill="currentColor" d="M12 2a10 10 0 1 0 10 10A10.01 10.01 0 0 0 12 2m5.939 7.713v.646a.37.37 0 0 1-.38.37a5.36 5.36 0 0 1-2.903-1.108v4.728a3.94 3.94 0 0 1-1.18 2.81a4 4 0 0 1-2.87 1.17a4.1 4.1 0 0 1-2.862-1.17a3.98 3.98 0 0 1-1.026-3.805c.159-.642.48-1.232.933-1.713a3.58 3.58 0 0 1 2.79-1.313h.82v1.703a.348.348 0 0 1-.39.348a1.918 1.918 0 0 0-1.23 3.631c.27.155.572.246.882.267c.24.01.48-.02.708-.092a1.93 1.93 0 0 0 1.313-1.816V5.754a.36.36 0 0 1 .359-.36h1.415a.36.36 0 0 1 .359.34a3.3 3.3 0 0 0 1.282 2.245a3.25 3.25 0 0 0 1.641.636a.37.37 0 0 1 .338.35z" />
-</svg>

+ 1 - 0
public/img/platformIcon/tt.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1725874777803" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1034" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M866.304 0H157.696C71.168 0 0 69.632 0 156.16v711.168C0 954.368 71.168 1024 157.696 1024h709.12c86.528 0 157.184-69.632 157.696-156.16V156.16C1023.488 69.632 952.832 0 866.304 0zM842.24 444.416c-63.488 0.512-126.976-19.456-179.2-56.32 0 80.384 0 160.768 1.024 241.152 0 10.752 0.512 21.504-0.512 32.256-2.56 39.424-15.36 77.312-34.816 111.616-16.896 29.184-39.936 55.808-67.584 75.776-35.328 26.112-79.36 40.96-123.392 41.984-22.528 0.512-45.056-0.512-66.56-5.632-30.72-6.656-60.416-19.456-86.016-37.376-0.512-0.512-1.536-1.024-1.536-1.536-27.648-19.456-50.688-44.544-67.584-73.728-23.04-38.4-34.816-83.968-33.28-129.024 1.024-41.472 13.312-82.432 34.816-117.76 19.456-32.256 47.104-59.904 79.36-79.36 46.592-28.672 103.424-40.96 157.696-33.28v30.72c0.512 12.8 0 25.6 0.512 37.888v65.024c-16.384-5.632-34.304-5.632-51.2-1.536-19.968 4.608-39.424 13.312-55.296 26.624-9.728 8.192-17.92 17.92-23.552 29.184-9.728 18.944-13.312 40.96-11.264 62.464 2.56 20.48 11.264 40.448 24.576 56.32 9.216 10.752 20.48 19.968 32.768 27.136 33.28 18.432 76.288 16.896 108.544-3.584 31.232-19.456 51.2-54.784 51.2-91.136V322.56c0.512-62.976 0-125.952 0.512-188.928h130.048c-0.512 11.264 1.024 22.016 3.072 33.28h0.512c3.584 18.432 10.24 35.84 18.944 52.736 13.312 25.6 32.768 48.64 57.344 63.488 1.536 1.024 2.56 2.56 4.096 3.584 18.432 11.264 38.4 19.968 59.392 24.064 12.288 2.56 25.088 3.584 37.376 4.096v129.536z" fill="#000000" p-id="1035"></path></svg>

+ 1 - 0
public/img/platformIcon/web.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1725845503147" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1030" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M34.13333334-8.53333334l948.08746667 0-77.55093333 900.02773334-401.95413334 123.42613333-382.29333333-123.42613333zM793.25866668 263.44106666l22.9376-136.53333333-610.57706667-1.09226667 39.3216 409.6 397.58506667 0 0 107.04213334-133.25653334 46.96746666-138.71786666-49.152-8.73813334-57.89013333-109.22666666 1.09226667 16.384 144.1792 233.74506666 81.92 245.76-77.55093334 31.67573334-329.86453333-432.5376 0-10.92266667-138.71786667 456.56746667 0z" p-id="1031"></path></svg>

+ 0 - 6
public/img/platformIcon/wechat.svg

@@ -1,6 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
-	<g fill="none" fill-rule="evenodd">
-		<path d="M24 0v24H0V0zM12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.019-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z" />
-		<path fill="currentColor" d="M16 10c3.154 0 6 2.186 6 5.213c0 1.696-.92 3.153-2.26 4.08c-.044.265-.008.532-.008.799a.903.903 0 0 1-.897.908c-.68 0-1.31-.224-1.843-.645a7 7 0 0 1-.992.071c-3.154 0-6-2.186-6-5.213S12.846 10 16 10M9.5 3c3.777 0 7.149 2.535 7.474 6.062A8 8 0 0 0 16 9c-3.533 0-7 2.473-7 6.209q0 .531.09 1.027a9 9 0 0 1-.926-.094c-.67.549-1.443.858-2.31.858c-.518 0-.937-.434-.937-.97l.004-.197l.017-.397a2.1 2.1 0 0 0-.055-.59C3.17 13.667 2 11.794 2 9.624C2 5.807 5.525 3 9.5 3M14 13a1 1 0 1 0 0 2a1 1 0 0 0 0-2m4 0a1 1 0 1 0 0 2a1 1 0 0 0 0-2M7 6a1 1 0 1 0 0 2a1 1 0 0 0 0-2m5 0a1 1 0 1 0 0 2a1 1 0 0 0 0-2" />
-	</g>
-</svg>

+ 1 - 0
public/img/platformIcon/wx.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1725845505819" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1183" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M236.24 323.769c0 24.005 19.32 43.325 43.325 43.325s43.326-19.32 43.326-43.325c0-24.005-19.321-43.325-43.326-43.325-24.004 0-43.325 19.32-43.325 43.325z m336.064 223.067c0 18.735 15.223 33.958 33.958 33.958s33.958-15.223 33.958-33.958-15.223-33.958-33.958-33.958-33.958 15.223-33.958 33.958zM456.38 323.769c0 24.005 19.32 43.325 43.325 43.325 24.005 0 43.325-19.32 43.325-43.325 0-24.005-19.32-43.325-43.325-43.325-24.005 0-43.325 19.32-43.325 43.325z" p-id="1184"></path><path d="M858.017 0H165.983C74.648 0 0.293 74.356 0.293 166.276v690.863c0 91.92 74.355 166.276 165.69 166.276h692.034c91.335 0 165.69-74.356 165.69-166.276V166.276C1023.707 74.94 949.352 0 858.017 0zM384.366 686.18c-38.642 0-69.672-8.197-108.899-15.808L167.154 724.82l31.03-93.09C120.316 577.28 74.063 507.022 74.063 422.128c0-147.54 139.929-264.05 310.303-264.05 152.81 0 286.298 93.09 313.23 217.798a255.341 255.341 0 0 0-29.859-1.757c-147.54 0-264.05 110.07-264.05 245.315 0 22.834 3.513 44.496 9.367 64.988-9.367 1.171-18.735 1.757-28.688 1.757z m457.843 108.313l23.42 77.283-84.895-46.838c-31.03 7.61-62.06 15.808-93.09 15.808-147.541 0-264.051-100.703-264.051-225.41 0-124.12 116.51-225.408 264.05-225.408 139.344 0 263.465 101.288 263.465 225.409-0.585 70.843-46.838 132.903-108.899 179.156z" p-id="1185"></path><path d="M741.507 546.836c0 18.735 15.223 33.958 33.958 33.958s33.958-15.223 33.958-33.958-15.223-33.958-33.958-33.958c-19.32 0-33.958 15.223-33.958 33.958z" p-id="1186"></path></svg>

BIN
src/assets/default/defaultHead.png


+ 0 - 6
src/assets/platformIcon/chrome.svg

@@ -1,6 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 15 15">
-	<path fill="currentColor" d="M2.503 1.907A7.47 7.47 0 0 1 7.5 0a7.5 7.5 0 0 1 6.635 4H7.5a3.5 3.5 0 0 0-3.23 2.149z" />
-	<path fill="currentColor" d="M1.745 2.69a7.503 7.503 0 0 0 3.41 11.937l2.812-3.658Q7.737 11 7.5 11a3.5 3.5 0 0 1-3.412-2.716a.5.5 0 0 1-.05-.092z" />
-	<path fill="currentColor" d="M6.215 14.89Q6.842 15 7.5 15a7.5 7.5 0 0 0 7.072-10.005L14.5 5H9.95A3.5 3.5 0 0 1 11 7.5a3.5 3.5 0 0 1-.953 2.405z" />
-	<path fill="currentColor" d="M5 7.5a2.5 2.5 0 1 1 5 0a2.5 2.5 0 0 1-5 0" />
-</svg>

+ 0 - 3
src/assets/platformIcon/tiktok.svg

@@ -1,3 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
-	<path fill="currentColor" d="M12 2a10 10 0 1 0 10 10A10.01 10.01 0 0 0 12 2m5.939 7.713v.646a.37.37 0 0 1-.38.37a5.36 5.36 0 0 1-2.903-1.108v4.728a3.94 3.94 0 0 1-1.18 2.81a4 4 0 0 1-2.87 1.17a4.1 4.1 0 0 1-2.862-1.17a3.98 3.98 0 0 1-1.026-3.805c.159-.642.48-1.232.933-1.713a3.58 3.58 0 0 1 2.79-1.313h.82v1.703a.348.348 0 0 1-.39.348a1.918 1.918 0 0 0-1.23 3.631c.27.155.572.246.882.267c.24.01.48-.02.708-.092a1.93 1.93 0 0 0 1.313-1.816V5.754a.36.36 0 0 1 .359-.36h1.415a.36.36 0 0 1 .359.34a3.3 3.3 0 0 0 1.282 2.245a3.25 3.25 0 0 0 1.641.636a.37.37 0 0 1 .338.35z" />
-</svg>

+ 0 - 6
src/assets/platformIcon/wechat.svg

@@ -1,6 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
-	<g fill="none" fill-rule="evenodd">
-		<path d="M24 0v24H0V0zM12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.019-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z" />
-		<path fill="currentColor" d="M16 10c3.154 0 6 2.186 6 5.213c0 1.696-.92 3.153-2.26 4.08c-.044.265-.008.532-.008.799a.903.903 0 0 1-.897.908c-.68 0-1.31-.224-1.843-.645a7 7 0 0 1-.992.071c-3.154 0-6-2.186-6-5.213S12.846 10 16 10M9.5 3c3.777 0 7.149 2.535 7.474 6.062A8 8 0 0 0 16 9c-3.533 0-7 2.473-7 6.209q0 .531.09 1.027a9 9 0 0 1-.926-.094c-.67.549-1.443.858-2.31.858c-.518 0-.937-.434-.937-.97l.004-.197l.017-.397a2.1 2.1 0 0 0-.055-.59C3.17 13.667 2 11.794 2 9.624C2 5.807 5.525 3 9.5 3M14 13a1 1 0 1 0 0 2a1 1 0 0 0 0-2m4 0a1 1 0 1 0 0 2a1 1 0 0 0 0-2M7 6a1 1 0 1 0 0 2a1 1 0 0 0 0-2m5 0a1 1 0 1 0 0 2a1 1 0 0 0 0-2" />
-	</g>
-</svg>

+ 21 - 6
src/components/Table.vue

@@ -2,7 +2,7 @@
  * @Author: fxs bjnsfxs@163.com
  * @Date: 2024-08-20 18:16:18
  * @LastEditors: fxs bjnsfxs@163.com
- * @LastEditTime: 2024-09-06 17:47:17
+ * @LastEditTime: 2024-09-09 17:56:10
  * @FilePath: \Game-Backstage-Management-System\src\components\Table.vue
  * @Description: 
  * 
@@ -11,6 +11,7 @@
 import type { PropsParams, TablePaginationSetting } from '@/types/table'
 import type { ReqConfig } from '@/types/dataAnalysis'
 import { FilterType, FieldSpecialEffectType } from '@/types/table'
+import { initLoadResouce } from '@/utils/resource'
 
 import { computed, onMounted, reactive, ref, watch } from 'vue'
 import { useTable } from '@/hooks/useTable'
@@ -67,6 +68,14 @@ const reqconfig = reactive<ReqConfig>({
   otherOptions: {}
 })
 
+// 资源的加载路径
+const resourceInfo: Record<string, string> = {
+  defaultHead: `/img/default/defaultHead.png`
+}
+
+// 使用blob的资源路径信息
+const blobUrlInfo = reactive<Record<string, string>>({})
+
 // 一些公用方法
 const { getTableData } = useTable(tableData, paginationConfig2)
 
@@ -141,6 +150,7 @@ const getData = () => {
           })
           .catch((err) => {
             console.log(err)
+
             reject(err)
           })
           .finally(() => {
@@ -148,6 +158,7 @@ const getData = () => {
           })
       } else {
         loading.value = false
+
         throw new Error('no match requestConfig')
       }
     }
@@ -187,7 +198,7 @@ const resetTableData = () => {
 // 按条件查询
 const queryTableData = () => {
   if (props.requestConfig) {
-    reqconfig.otherOptions = { ...reqconfig.otherOptions, ...queryFormData }
+    reqconfig.otherOptions = { ...props.requestConfig.otherOptions, ...queryFormData }
     getData()
   } else {
     throw new Error('no match requestConfig')
@@ -195,14 +206,14 @@ const queryTableData = () => {
 }
 
 /**
- * @description: 重置整个查询表单(无效目前)
- * @param {*} formEl
+ * @description: 重置整个查询表单,重置后,再请求一次全部表格
+ * @param {*} formEl  表单对象
  * @return {*}
  */
 const resetQueryForm = (formEl: FormInstance | undefined) => {
   if (!formEl) return
   clearReactiveData(queryFormData)
-  // formEl?.resetFields()
+  queryTableData()
 }
 
 /**
@@ -375,6 +386,10 @@ onMounted(() => {
   if (!props.openPageQuery) {
     getData()
   }
+  // 去加载所有需要的资源
+  initLoadResouce(resourceInfo).then((data) => {
+    Object.assign(blobUrlInfo, data)
+  })
 })
 </script>
 
@@ -509,7 +524,7 @@ onMounted(() => {
               >
                 <template #error>
                   <!--  -->
-                  <img style="width: 35px; height: 35px" src="../assets/default/defaultHead.png" />
+                  <img style="width: 35px; height: 35px" :src="blobUrlInfo.defaultHead" />
                 </template>
               </el-image>
 

+ 9 - 4
src/components/common/Dialog.vue

@@ -2,7 +2,7 @@
  * @Author: fxs bjnsfxs@163.com
  * @Date: 2024-09-04 11:21:05
  * @LastEditors: fxs bjnsfxs@163.com
- * @LastEditTime: 2024-09-05 16:23:15
+ * @LastEditTime: 2024-09-09 12:09:08
  * @FilePath: \Game-Backstage-Management-System\src\components\common\Dialog.vue
  * @Description: 
  * 
@@ -48,9 +48,8 @@ const dialogConfig = reactive({
 
 // 游戏配置提交
 const submiteGameChange = () => {
-  dialogFormRef.value?.submitFormData().finally(() => {
+  dialogFormRef.value?.submitFormData().then(() => {
     dialogConfig.dialogVisible = false
-    emits('formSubmit')
   })
 }
 
@@ -86,10 +85,13 @@ onMounted(() => {
 const encrypt = (fields: string, useFormField: boolean, encryptMsg: Array<string>) => {
   dialogFormRef.value?.encryptData(fields, useFormField, encryptMsg).finally(() => {
     dialogConfig.dialogVisible = false
-    emits('formSubmit')
   })
 }
 
+const subForm = () => {
+  emits('formSubmit')
+}
+
 defineExpose({
   addForm,
   editForm,
@@ -103,10 +105,13 @@ defineExpose({
       @close="closeDialog"
       v-model="dialogConfig.dialogVisible"
       :title="dialogConfig.title"
+      :close-on-click-modal="false"
       style="width: 40%"
+      append-to-body
     >
       <Form
         :disabled="true"
+        @sub-form="subForm"
         ref="dialogFormRef"
         :config="{
           fieldsInfo: dialogConfigInfo.fieldsInfo,

+ 192 - 0
src/components/common/WithIconSelect.vue

@@ -0,0 +1,192 @@
+<script setup lang="ts">
+import { onMounted, ref, reactive, computed } from 'vue'
+import { initLoadResouce } from '@/utils/resource'
+import type { DropdownInstance } from 'element-plus'
+
+interface DropdownItem {
+  value: string
+  icon: string
+  label: string
+  isSelected: boolean
+}
+
+interface DropdownInfo {
+  isRadio?: boolean
+  slectInfo: Array<DropdownItem>
+}
+
+/**
+ * @description: 最大选择数
+ * @return {*}
+ */
+const maxSelect = 1
+
+// props
+const props = withDefaults(defineProps<DropdownInfo>(), {
+  isRadio: true
+})
+
+// emits
+const emits = defineEmits(['changePf'])
+
+// 下拉框
+const dropDownRef = ref<DropdownInstance>()
+
+// 资源的加载路径
+const resourceInfo: Record<string, string> = props.slectInfo.reduce(
+  (acc, item) => {
+    acc[item.value] = item.icon
+    return acc
+  },
+  {} as Record<string, string>
+)
+
+// 使用blob的资源路径信息
+const blobUrlInfo = reactive<Record<string, string>>({})
+
+// 备份信息
+const backupInfo = reactive<Array<DropdownItem>>([])
+
+/**
+ * @description: 确认选择
+ * @return {*}
+ */
+const confirmSelect = () => {
+  Object.assign(backupInfo, JSON.parse(JSON.stringify(props.slectInfo)))
+  dropDownRef.value?.handleClose()
+  emits(
+    'changePf',
+    props.slectInfo.filter((item) => item.isSelected).map((item) => item.value)
+  )
+}
+
+/**
+ * @description: 取消选择,当取消选择后,需要恢复到原来的状态
+ * @return {*}
+ */
+const cancleSelect = () => {
+  dropDownRef.value?.handleClose()
+  Object.assign(props.slectInfo, backupInfo)
+}
+
+/**
+ * @description: 下拉框出现与消失的处理
+ * @param {*} state 展示状态,值为true或者false,代表打开或者关闭
+ * @return {*}
+ */
+const dropdownVis = (state: boolean) => {
+  if (state) {
+    Object.assign(backupInfo, JSON.parse(JSON.stringify(props.slectInfo)))
+  } else {
+    Object.assign(props.slectInfo, backupInfo)
+  }
+}
+
+/**
+ * @description: 用于限制最多的选择个数
+ * @return {*}
+ */
+const canSelect = computed(() => {
+  return props.slectInfo.filter((item) => item.isSelected).length < maxSelect
+})
+
+/**
+ * @description: 选择事件,当目前可以选择或者是这个选项已经被选中的时候,就允许改变他的状态
+ * @param {*} item  当前的选项信息
+ * @return {*}
+ */
+const selectPf = (item: any) => {
+  if (canSelect.value || item.isSelected) {
+    item.isSelected = !item.isSelected
+  }
+}
+
+onMounted(() => {
+  // 去加载所有需要的资源
+  initLoadResouce(resourceInfo).then((data) => {
+    Object.assign(blobUrlInfo, data)
+  })
+})
+</script>
+
+<template>
+  <div>
+    <el-dropdown
+      trigger="click"
+      @visible-change="dropdownVis"
+      :hide-on-click="false"
+      ref="dropDownRef"
+    >
+      <span class="displayBox">
+        <div class="displayIcon">
+          <span class="iconItem" v-for="item in props.slectInfo">
+            <el-image
+              v-if="item.isSelected"
+              style="width: 20px; height: 20px; margin-right: 5px"
+              :src="item.icon"
+              :fit="'cover'"
+            />
+          </span>
+        </div>
+
+        <el-icon class="el-icon--right">
+          <arrow-down />
+        </el-icon>
+      </span>
+      <template #dropdown>
+        <el-dropdown-menu>
+          <el-dropdown-item
+            v-for="item in props.slectInfo"
+            :key="item.value"
+            :value="item.value"
+            @click="selectPf(item)"
+            :disabled="!canSelect && !item.isSelected"
+          >
+            <el-image
+              style="width: 20px; height: 20px; margin-right: 5px"
+              :src="item.icon"
+              :fit="'cover'"
+            />
+            <!-- 禁用掉原生的点击事件,自己实现点击 -->
+            <el-checkbox
+              :disabled="!canSelect && !item.isSelected"
+              v-model="item.isSelected"
+              size="small"
+              @click.native.prevent="return"
+            />
+          </el-dropdown-item>
+        </el-dropdown-menu>
+        <span class="btnGroup">
+          <el-button class="btnItem" size="small" type="primary" @click="confirmSelect"
+            >确认</el-button
+          >
+          <el-button class="btnItem" size="small" @click="cancleSelect">取消</el-button>
+        </span>
+      </template>
+    </el-dropdown>
+  </div>
+</template>
+
+<style scoped>
+.displayBox {
+  display: flex;
+  align-items: center;
+}
+
+.displayIcon {
+  display: flex;
+  align-items: center;
+}
+
+.btnGroup {
+  display: flex;
+  justify-content: center;
+}
+.btnItem {
+  margin-right: 8px;
+}
+
+.disabledSelect {
+  cursor: not-allowed !important;
+}
+</style>

+ 1 - 1
src/components/dataAnalysis/DropDownSelection.vue

@@ -2,7 +2,7 @@
  * @Author: fxs bjnsfxs@163.com
  * @Date: 2024-08-23 14:42:47
  * @LastEditors: fxs bjnsfxs@163.com
- * @LastEditTime: 2024-09-06 11:52:17
+ * @LastEditTime: 2024-09-09 09:58:18
  * @FilePath: \Game-Backstage-Management-System\src\components\dataAnalysis\DropDownSelection.vue
  * @Description: 下拉选择框,可用于分类字段或者切换平台等
  * 

+ 65 - 2
src/components/dataAnalysis/HeaderCard.vue

@@ -11,6 +11,7 @@ import router from '@/router'
 import DropDownSelection from './DropDownSelection.vue'
 import type { DropDownInfo, HeaderCardProps } from '@/types/dataAnalysis'
 import { computed, onMounted, reactive, ref, watch } from 'vue'
+import WithIconSelect from '@/components/common/WithIconSelect.vue'
 
 const props = withDefaults(defineProps<HeaderCardProps>(), {
   openDateSelect: false,
@@ -40,6 +41,27 @@ const platFormOptionInfo: DropDownInfo = {
     }
   ]
 }
+// {
+//   wx: '/img/platformIcon/wx.svg',
+//   tt: '/img/platformIcon/tt.svg',
+//   web: '/img/platformIcon/web.svg'
+// }
+// const dropDownInfo = {
+//   selectInfo: [
+//     {
+//       icon: '/img/platformIcon/wx.svg',
+//       value: 'wx',
+//       label: '微信',
+//       isSelected: true
+//     },
+//     {
+//       icon: '/img/platformIcon/tt.svg',
+//       value: 'tt',
+//       label: '抖音',
+//       isSelected: true
+//     }
+//   ]
+// }
 
 // 快速选择日期
 const shortcuts = [
@@ -162,6 +184,46 @@ onMounted(() => {
   })
   dateChange(selectDate.value)
 })
+
+interface DropdownItem {
+  value: string
+  icon: string
+  label: string
+  isSelected: boolean
+}
+
+const selectInfo = reactive<Array<DropdownItem>>([
+  {
+    value: 'web',
+    icon: '/img/platformIcon/web.svg',
+    label: '网页',
+    isSelected: true
+  },
+  {
+    value: 'wx',
+    icon: '/img/platformIcon/wx.svg',
+    label: '微信',
+    isSelected: false
+  },
+  {
+    value: 'tt',
+    icon: '/img/platformIcon/tt.svg',
+    label: '抖音',
+    isSelected: false
+  }
+])
+
+const changePlatForm = (val: Array<any>) => {
+  emits('changePf', val)
+}
+
+watch(
+  () => selectInfo,
+  (newval) => {
+    console.log('new')
+    console.log(newval)
+  }
+)
 </script>
 
 <template>
@@ -188,12 +250,13 @@ onMounted(() => {
     <div class="selectBox" v-if="props.needPfSelect">
       <el-divider direction="vertical" />
       <div class="selectItem">
-        <DropDownSelection
+        <WithIconSelect @change-pf="changePlatForm" :slect-info="selectInfo"></WithIconSelect>
+        <!-- <DropDownSelection
           @changeSelect="changePf"
           :defaultSelect="platFormOptionInfo.defaultSelect"
           :title="platFormOptionInfo.title"
           :optionsList="platFormOptionInfo.optionsList"
-        ></DropDownSelection>
+        ></DropDownSelection> -->
       </div>
     </div>
     <div v-if="props.openDateSelect" class="datePicker">

+ 11 - 8
src/components/dataAnalysis/TemporalTrend.vue

@@ -15,6 +15,17 @@ import TimeLineChart from '../echarts/TimeLineChart.vue'
 import StatisticText from './StatisticText.vue'
 
 import axiosInstance from '@/utils/axios/axiosInstance'
+import { useRequest } from '@/hooks/useRequest'
+
+const { analysisResCode } = useRequest()
+
+interface CacheData {
+  paginationConfig: TablePaginationSetting
+  tableDataList: Array<any>
+  tableFieldsInfo: Array<any>
+  chartInfo: OptionsProps
+  statisticFieldsInfo: Array<StaticField>
+}
 
 const props = withDefaults(defineProps<TemporalTrendProps>(), {
   type: 1,
@@ -26,14 +37,6 @@ const props = withDefaults(defineProps<TemporalTrendProps>(), {
 const activeTab = ref<string>('') // 激活的Tab
 const iconSize = ref(20) // 图标的尺寸
 
-interface CacheData {
-  paginationConfig: TablePaginationSetting
-  tableDataList: Array<any>
-  tableFieldsInfo: Array<any>
-  chartInfo: OptionsProps
-  statisticFieldsInfo: Array<StaticField>
-}
-
 // 缓存数据
 const cacheData: Record<string, CacheData> = {}
 

+ 40 - 12
src/components/form/Form.vue

@@ -36,13 +36,20 @@ const backupData = reactive<Record<string, any>>({})
 const submitFormData = (otherOption?: any) => {
   return new Promise((reslove, reject) => {
     Object.assign(formData, props.config.reqConfig.otherOptions.formData)
-    submitForm(formRef.value, props.config.reqConfig.url, { ...formData, ...otherOption })
-      .then(() => {
-        reslove(true)
-      })
-      .catch((err) => {
-        reject(err)
-      })
+    formRef.value?.validate(async (vaild: boolean) => {
+      if (vaild) {
+        submitForm(formRef.value, props.config.reqConfig.url, { ...formData, ...otherOption })
+          .then(() => {
+            emits('subForm')
+            reslove(true)
+          })
+          .catch((err) => {
+            reject(err)
+          })
+      } else {
+        console.log('验证不通过')
+      }
+    })
   })
 }
 
@@ -79,6 +86,10 @@ const resumeFormData = () => {
   Object.assign(formData, backupData)
 }
 
+const clearValid = () => {
+  formRef.value?.clearValidate()
+}
+
 onMounted(() => {
   // nextTick(() => {
 
@@ -109,21 +120,24 @@ const encryptData = (fields: string, useFormField: boolean, encryptMsg: Array<st
   })
 }
 
+const emits = defineEmits(['subForm'])
+
 defineExpose({
   submitFormData,
   resetForm,
   fillForm,
   encryptData,
   backupFormData,
-  resumeFormData
+  resumeFormData,
+  clearValid
 })
 </script>
 
 <template>
   <div class="formBox">
+    <!-- :inline="props.inline" -->
     <el-form
-      :class="props.inline ? '' : 'form'"
-      :inline="props.inline"
+      :class="props.inline ? 'formInline' : 'form'"
       :label-position="props.inline ? 'top' : 'left'"
       :rules="props.config.rules"
       :model="formData"
@@ -132,7 +146,11 @@ defineExpose({
       :disabled="!props.disabled"
     >
       <template v-for="item in props.config.fieldsInfo">
-        <el-form-item :prop="item.name" :label="item.cnName" class="formItem">
+        <el-form-item
+          :prop="item.name"
+          :label="item.cnName"
+          :class="['formItem', item.type === FormFieldType.RICHTEXT ? 'richtextItem' : '']"
+        >
           <el-input
             style="width: 300px"
             v-if="item.type === FormFieldType.INPUT"
@@ -156,7 +174,7 @@ defineExpose({
           <el-input
             v-if="item.type === FormFieldType.RICHTEXT"
             v-model="formData[item.name]"
-            style="width: 624px; margin-bottom: 20px"
+            style="width: 300px; margin-bottom: 20px"
             :autosize="{ minRows: 4 }"
             type="textarea"
             :placeholder="item.otherOptions?.placeholder"
@@ -180,9 +198,19 @@ defineExpose({
   justify-content: center;
 }
 
+.formInline {
+  display: flex;
+  justify-content: space-between;
+  flex-wrap: wrap;
+}
+
 .formItem {
   /* width: 50%; */
   /* display: flex; */
   justify-content: center;
 }
+
+.richtextItem {
+  flex-basis: 100%;
+}
 </style>

+ 11 - 1
src/hooks/useForm.ts

@@ -1,3 +1,12 @@
+/*
+ * @Author: fxs bjnsfxs@163.com
+ * @Date: 2024-09-04 15:07:56
+ * @LastEditors: fxs bjnsfxs@163.com
+ * @LastEditTime: 2024-09-09 12:10:43
+ * @FilePath: \Game-Backstage-Management-System\src\hooks\useForm.ts
+ * @Description:
+ *
+ */
 import type { FormInstance } from 'element-plus'
 import { ElMessage, ElMessageBox } from 'element-plus'
 import { nextTick } from 'vue'
@@ -12,7 +21,8 @@ export function useForm() {
       ElMessageBox.confirm('确认提交吗?', '警告', {
         confirmButtonText: '确认',
         cancelButtonText: '取消',
-        type: 'warning'
+        type: 'warning',
+        appendTo: 'body'
       })
         .then(() => {
           try {

+ 13 - 4
src/hooks/useRequest.ts

@@ -2,7 +2,7 @@
  * @Author: fxs bjnsfxs@163.com
  * @Date: 2024-08-20 17:24:06
  * @LastEditors: fxs bjnsfxs@163.com
- * @LastEditTime: 2024-09-06 10:21:30
+ * @LastEditTime: 2024-09-09 16:35:50
  * @FilePath: \Game-Backstage-Management-System\src\hooks\useRequest.ts
  * @Description:
  *
@@ -16,8 +16,9 @@ import type { AxiosResponse } from 'axios'
 import type { ResponseInfo } from '@/types/res'
 
 export function useRequest() {
-  // const baseIp = 'http://server.ichunhao.cn' // 线上
-  const baseIp = 'http://192.168.1.139:8000' // 本地
+  const baseIp = 'http://server.ichunhao.cn' // 测试服
+  // const baseIp = 'http://service.ichunhao.cn' // 正式库
+  // const baseIp = 'http://192.168.1.139:8000' // 本地
 
   const AllApi = {
     // mock: `http://127.0.0.1:8003/mock`,
@@ -46,13 +47,21 @@ export function useRequest() {
     userRemainDataBydDay: `${baseIp}/user/remainDataBydDay`, //用户留存数据
 
     // 事件相关
+    // 事件
     gameActionList: `${baseIp}/user/gameActionList`, // 游戏事件列表
     gameActionDetail: `${baseIp}/user/gameActionDetail`, // 事件详情
     updateGameAction: `${baseIp}/user/updateGameAction`, // 更新游戏事件
+    setGameAction: `${baseIp}/user/setGameAction`, // 新增事件
+
+    // 事件参数
     gameActionOptionList: `${baseIp}/user/gameActionOptionList`, // 获取事件参数列表
     addGameActionOption: `${baseIp}/user/addGameActionOption`, // 新增事件参数
     updateGameActionOption: `${baseIp}/user/updateGameActionOption`, // 更新事件参数
-    deleteGameActionOption: `${baseIp}/user/deleteGameActionOption` // 删除事件参数
+    deleteGameActionOption: `${baseIp}/user/deleteGameActionOption`, // 删除事件参数
+
+    // 事件分析
+    userActionDetailDistribution: `${baseIp}/user/userActionDetailDistribution`, // 事件统计趋势图
+    userActionDetail: `${baseIp}/user/userActionDetail` // 事件统计详情
   }
 
   const analysisResCode = (data: AxiosResponse, kind?: string): Promise<ResponseInfo> => {

+ 51 - 29
src/hooks/useTable.ts

@@ -2,53 +2,75 @@
  * @Author: fxs bjnsfxs@163.com
  * @Date: 2024-08-20 17:15:49
  * @LastEditors: fxs bjnsfxs@163.com
- * @LastEditTime: 2024-09-06 15:52:27
+ * @LastEditTime: 2024-09-09 14:32:30
  * @FilePath: \Game-Backstage-Management-System\src\hooks\useTable.ts
  * @Description:
  *
  */
+import type { ResponseInfo } from '@/types/res'
 import axiosInstance from '../utils/axios/axiosInstance'
-import { useRequest } from './useRequest'
+// import { useRequest } from './useRequest'
 
 import type { TablePaginationSetting, DialogSetting } from '@/types/table'
 import { type FormInstance } from 'element-plus'
 
 export function useTable(tableData: Array<any>, paginationSetting: TablePaginationSetting) {
-  const { analysisResCode } = useRequest()
+  // const { analysisResCode } = useRequest()
 
   const getTableData = (url: string, option: any, isPagination: boolean = false) => {
-    return new Promise(async (reslove) => {
+    return new Promise(async (reslove, reject) => {
       try {
-        await axiosInstance.post(url, option).then((data) => {
-          analysisResCode(data)
-            .then((info) => {
-              let data = info.data
+        await axiosInstance.post(url, option).then((result) => {
+          let info = JSON.parse(JSON.stringify(result)) as ResponseInfo
+          let data = info.data
 
-              // 如果开启了分页,那么默认这个tabledata是一个二维数组,每个位置对应当页的一个数据数组
-              // 没开启则是一个一维数组,直接赋值
-              if (isPagination) {
-                tableData[paginationSetting.currentPage] = data
-              } else {
-                tableData.splice(0, tableData.length, ...data)
-              }
+          // 如果开启了分页,那么默认这个tabledata是一个二维数组,每个位置对应当页的一个数据数组
+          // 没开启则是一个一维数组,直接赋值
+          if (isPagination) {
+            tableData[paginationSetting.currentPage] = data
+          } else {
+            tableData.splice(0, tableData.length, ...data)
+          }
 
-              // 如果有的接口没有返回count属性,就需要自己写
-              // 这个length,如果数组长为0,则需要自己赋值,不然会报错
-              if (info.count) paginationSetting.total = info.count
-              else if (info.data) {
-                paginationSetting.total = info.data.length
-              } else {
-                paginationSetting.total = 0
-              }
-              reslove(true)
-            })
-            .catch((err) => {
-              console.log(err)
-              throw new Error('请求失败')
-            })
+          // 如果有的接口没有返回count属性,就需要自己写
+          // 这个length,如果数组长为0,则需要自己赋值,不然会报错
+          if (info.count) paginationSetting.total = info.count
+          else if (info.data) {
+            paginationSetting.total = info.data.length
+          } else {
+            paginationSetting.total = 0
+          }
+          reslove(true)
+          // analysisResCode(data)
+          //   .then((info) => {
+          //     let data = info.data
+
+          //     // 如果开启了分页,那么默认这个tabledata是一个二维数组,每个位置对应当页的一个数据数组
+          //     // 没开启则是一个一维数组,直接赋值
+          //     if (isPagination) {
+          //       tableData[paginationSetting.currentPage] = data
+          //     } else {
+          //       tableData.splice(0, tableData.length, ...data)
+          //     }
+
+          //     // 如果有的接口没有返回count属性,就需要自己写
+          //     // 这个length,如果数组长为0,则需要自己赋值,不然会报错
+          //     if (info.count) paginationSetting.total = info.count
+          //     else if (info.data) {
+          //       paginationSetting.total = info.data.length
+          //     } else {
+          //       paginationSetting.total = 0
+          //     }
+          //     reslove(true)
+          //   })
+          //   .catch((err) => {
+          //     console.log(err)
+          //     throw new Error('请求失败')
+          //   })
         })
       } catch (err) {
         console.log(err)
+        reject(err)
         throw new Error('网络请求错误')
       }
     })

+ 1 - 1
src/router/home.ts

@@ -2,7 +2,7 @@
  * @Author: fxs bjnsfxs@163.com
  * @Date: 2024-08-20 14:24:58
  * @LastEditors: fxs bjnsfxs@163.com
- * @LastEditTime: 2024-09-06 17:25:34
+ * @LastEditTime: 2024-09-09 12:12:28
  * @FilePath: \Game-Backstage-Management-System\src\router\home.ts
  * @Description:
  *

+ 2 - 3
src/utils/axios/axiosInstance.ts

@@ -2,7 +2,7 @@
  * @Author: fxs bjnsfxs@163.com
  * @Date: 2024-08-20 17:18:52
  * @LastEditors: fxs bjnsfxs@163.com
- * @LastEditTime: 2024-09-05 18:07:10
+ * @LastEditTime: 2024-09-09 10:58:26
  * @FilePath: \Game-Backstage-Management-System\src\utils\axios\axiosInstance.ts
  * @Description:
  *
@@ -15,7 +15,6 @@ import { useRequest } from '@/hooks/useRequest'
 import { MessageType } from '@/types/res'
 
 const { AllApi } = useRequest()
-// import qs from "qs";
 // 创建axios实例
 const axiosInstance = axios.create()
 // 请求拦截器
@@ -68,7 +67,7 @@ axiosInstance.interceptors.response.use(
       message: '服务器错误,请稍后再试',
       duration: 1500
     })
-
+    // router.push('/login')
     return Promise.reject(error)
   }
 )

+ 1 - 1
src/utils/common/index.ts

@@ -2,7 +2,7 @@
  * @Author: fxs bjnsfxs@163.com
  * @Date: 2024-08-26 15:46:42
  * @LastEditors: fxs bjnsfxs@163.com
- * @LastEditTime: 2024-09-04 11:56:55
+ * @LastEditTime: 2024-09-09 17:55:03
  * @FilePath: \Game-Backstage-Management-System\src\utils\common\index.ts
  * @Description:
  *

+ 0 - 1
src/utils/table/table.ts

@@ -41,6 +41,5 @@ export const getAllGameInfo = async () => {
  * @return {*}
  */
 export const shouldListenToEvent = (routeName: string | symbol | undefined, mathName: string) => {
-  console.log(routeName, mathName)
   return routeName === mathName
 }

+ 21 - 7
src/views/AppManage/BaseInfoView.vue

@@ -1,7 +1,7 @@
 <script setup lang="ts">
 import HeaderCard from '@/components/dataAnalysis/HeaderCard.vue'
 
-import { reactive, onMounted, ref } from 'vue'
+import { reactive, onMounted, ref, watch } from 'vue'
 import { initLoadResouce } from '@/utils/resource'
 import { copyText } from '@/utils/common'
 import axiosInstance from '@/utils/axios/axiosInstance'
@@ -47,6 +47,9 @@ const resourceInfo: Record<string, string> = {
   defaultHead: `/img/default/defaultGame.svg`
 }
 
+// 返回的所有数据
+const allGameInfo = reactive<Array<any>>([])
+
 // 使用blob的资源路径信息
 const blobUrlInfo = reactive<Record<string, string>>({})
 
@@ -67,6 +70,14 @@ const copyGid = () => {
   })
 }
 
+const changeGameInfo = () => {
+  let nowGame = allGameInfo.find((item: any) => {
+    return item.gid === selectInfo.gid
+  })
+  Object.assign(appInfo, nowGame)
+  console.log(nowGame)
+}
+
 const getGameInfo = () => {
   axiosInstance
     .post(AllApi.getGameTable, {
@@ -74,15 +85,18 @@ const getGameInfo = () => {
     })
     .then((data) => {
       let info = data.data
-      let nowGame = info.find((item: any) => {
-        console.log(item.gid, selectInfo.gid)
-        return item.gid === selectInfo.gid
-      })
-      console.log(nowGame)
-      Object.assign(appInfo, nowGame)
+      Object.assign(allGameInfo, info)
+      changeGameInfo()
     })
 }
 
+watch(
+  () => selectInfo.gid,
+  () => {
+    getGameInfo()
+  }
+)
+
 onMounted(() => {
   // 去加载所有需要的资源
   initLoadResouce(resourceInfo).then((data) => {

+ 45 - 45
src/views/AppManage/EventDetailsView.vue

@@ -50,30 +50,19 @@ const ruleForm = reactive({
 const rules = reactive<FormRules<typeof ruleForm>>({
   actionId: [
     { required: true, message: '事件ID是必填项', trigger: 'blur' },
-    {
-      min: 1,
-      max: 20,
-      message: '事件ID长度必须在1到20个字符之间',
-      trigger: 'blur'
-    }
+    { min: 1, max: 10, message: '事件ID长度必须在1到10之间', trigger: 'blur' }
   ],
   actionName: [
-    { required: true, message: '事件名是必填项', trigger: 'blur' },
-    {
-      min: 5,
-      max: 10,
-      message: '事件显示名长度必须在5到10个字符之间',
-      trigger: 'blur'
-    }
+    { required: true, message: '事件名称是必填项', trigger: 'blur' },
+    { min: 5, max: 20, message: '事件名称长度必须在5到20之间', trigger: 'blur' }
   ],
-  status: [{ required: true, message: '使用状态是必填项', trigger: 'change' }],
   remark: [
-    {
-      min: 1,
-      max: 255,
-      message: '备注长度必须在1到255个字符之间',
-      trigger: 'blur'
-    }
+    { required: false, message: '备注是可选项', trigger: 'blur' },
+    { max: 100, message: '备注长度不能超过100个字符', trigger: 'blur' }
+  ],
+  status: [
+    { required: true, message: '是否启用是必选项', trigger: 'change' },
+    { type: 'number', message: '启用状态必须是数字', trigger: 'change' }
   ]
 })
 
@@ -143,40 +132,35 @@ const pageConfig = reactive<TablePaginationSetting>({
 // 表格字段信息
 const tableFieldConfig = reactive<Array<TableFieldInfo>>([
   {
-    name: 'actionId',
-    cnName: '事件ID',
-    isShow: true,
-    needSort: true
-  },
-  {
-    name: 'createdAt',
-    cnName: '创建时间',
-    isShow: true,
-    needSort: true
-  },
-  {
     name: 'id',
     cnName: 'ID',
     isShow: true,
     needSort: true
   },
   {
+    name: 'actionId',
+    cnName: '事件ID',
+    isShow: false,
+    needSort: false
+  },
+
+  {
     name: 'optionId',
     cnName: '选项ID',
     isShow: true,
-    needSort: true
+    needSort: false
   },
   {
     name: 'optionName',
     cnName: '选项名称',
     isShow: true,
-    needSort: true
+    needSort: false
   },
   {
     name: 'optionType',
     cnName: '选项类型',
     isShow: true,
-    needSort: true,
+    needSort: false,
     specialEffect: {
       type: FieldSpecialEffectType.TRANSLATE,
       othnerInfo: {
@@ -192,7 +176,7 @@ const tableFieldConfig = reactive<Array<TableFieldInfo>>([
     name: 'status',
     cnName: '使用状态状态',
     isShow: true,
-    needSort: true,
+    needSort: false,
     specialEffect: {
       othnerInfo: {
         text: ['启用', '禁用']
@@ -201,10 +185,16 @@ const tableFieldConfig = reactive<Array<TableFieldInfo>>([
     }
   },
   {
+    name: 'createdAt',
+    cnName: '创建时间',
+    isShow: true,
+    needSort: false
+  },
+  {
     name: 'updatedAt',
     cnName: '更新时间',
     isShow: true,
-    needSort: true
+    needSort: false
   }
 ])
 
@@ -265,12 +255,12 @@ const dialogFormFields: Array<FormField> = [
       options: [
         {
           name: 'disabled',
-          label: '禁用',
+          label: '禁用',
           value: 0 // 对应int型状态
         },
         {
           name: 'abled',
-          label: '启用',
+          label: '启用',
           value: 1 // 对应int型状态
         }
       ]
@@ -353,16 +343,19 @@ const changeEditState = (state: boolean) => {
  */
 const cancelEdit = () => {
   changeEditState(false)
+
+  eventFormRef.value.clearValid()
   eventFormRef.value.resumeFormData()
 }
 
 /**
- * @description: 保存编辑,提交表单
+ * @description: 保存编辑,提交表单,只有表单验证通过了才能更改编辑状态
  * @return {*}
  */
 const saveEdit = () => {
-  changeEditState(false)
-  eventFormRef.value.submitFormData()
+  eventFormRef.value.submitFormData().then(() => {
+    changeEditState(false)
+  })
 }
 
 /**
@@ -451,7 +444,7 @@ onMounted(() => {})
     </div>
   </div>
   <div class="eventForm">
-    <div class="formBox">
+    <div class="formBody">
       <Form
         :disabled="eventEditState"
         ref="eventFormRef"
@@ -479,15 +472,16 @@ onMounted(() => {})
             <template #default="scope">
               <el-button
                 size="small"
-                type="primary"
+                color="#626aef"
                 @click="editOption(scope.row)"
                 class="operationBtn"
               >
                 修改
               </el-button>
+
               <el-button
                 size="small"
-                type="danger"
+                color="#EF5A6F"
                 @click="delOption(scope.row)"
                 class="operationBtn"
               >
@@ -541,7 +535,13 @@ onMounted(() => {})
   padding: 24px;
 }
 
+.formBody {
+  width: 45%;
+}
+
 .operationBtn {
+  cursor: pointer;
   margin-right: 5px;
+  color: white;
 }
 </style>

+ 7 - 8
src/views/AppManage/EventManageView.vue

@@ -2,7 +2,7 @@
  * @Author: fxs bjnsfxs@163.com
  * @Date: 2024-09-02 17:57:15
  * @LastEditors: fxs bjnsfxs@163.com
- * @LastEditTime: 2024-09-06 17:47:59
+ * @LastEditTime: 2024-09-09 17:58:41
  * @FilePath: \Game-Backstage-Management-System\src\views\AppManage\EventManageView.vue
  * @Description: 
  * 
@@ -42,13 +42,12 @@ onMounted(() => {})
       <router-view v-slot="{ Component, route }">
         <!-- 是eventtable组件就去监听enterdetail事件 -->
         <!-- 注释也不要写到keep-alive里面,会报错 -->
-        <keep-alive>
-          <component
-            :is="Component"
-            v-if="shouldListenToEvent(route.name, 'EventTable')"
-            @enterDetail="headerAddPath"
-          />
-        </keep-alive>
+
+        <component
+          :is="Component"
+          v-if="shouldListenToEvent(route.name, 'EventTable')"
+          @enterDetail="headerAddPath"
+        />
 
         <!-- 如果不是正常渲染其他组件 -->
         <component v-if="route.name !== 'EventTable'" :is="Component" />

+ 80 - 42
src/views/AppManage/EventMangeTable.vue

@@ -2,6 +2,7 @@
 import Table from '@/components/Table.vue'
 import Dialog from '@/components/common/Dialog.vue'
 import { useRequest } from '@/hooks/useRequest'
+import { useCommonStore } from '@/stores/useCommon'
 
 import type { TablePaginationSetting, TableFieldInfo, QueryInfo, SelectInfo } from '@/types/table'
 import { FieldSpecialEffectType, FilterType } from '@/types/table'
@@ -11,14 +12,18 @@ import { type DialogConfig } from '@/types/dialog'
 import { FormFieldType } from '@/types/form'
 import type { FormField } from '@/types/form'
 
-import { reactive, onMounted, ref } from 'vue'
+import { reactive, onMounted, ref, watch } from 'vue'
 
 import router from '@/router'
 
+const { selectInfo } = useCommonStore()
+
 const { AllApi } = useRequest()
 
 const eventDialog = ref()
 
+const eventTable = ref()
+
 // 主要为了给面包屑导航提供信息
 const emits = defineEmits(['enterDetail'])
 
@@ -33,6 +38,12 @@ const pagingConfig = reactive<TablePaginationSetting>({
 // 表格字段信息
 const tableFieldsInfo = reactive<Array<TableFieldInfo>>([
   {
+    name: 'id',
+    cnName: 'ID',
+    isShow: true,
+    needSort: true
+  },
+  {
     name: 'gid',
     cnName: '游戏ID',
     isShow: true,
@@ -72,7 +83,7 @@ const tableFieldsInfo = reactive<Array<TableFieldInfo>>([
     name: 'createdAt',
     cnName: '创建时间',
     isShow: true,
-    needSort: true
+    needSort: false
   },
   {
     name: 'updatedAt',
@@ -85,7 +96,9 @@ const tableFieldsInfo = reactive<Array<TableFieldInfo>>([
 // 表格请求配置
 const requestConfig = reactive<ReqConfig>({
   url: AllApi.gameActionList,
-  otherOptions: {}
+  otherOptions: {
+    gid: selectInfo.gid
+  }
 })
 
 // 事件类型查询信息
@@ -97,12 +110,12 @@ const eventStatus: Array<SelectInfo> = [
   },
   {
     name: 'use',
-    cnName: '已使用',
+    cnName: '用',
     value: '1'
   },
   {
     name: 'nouse',
-    cnName: '已弃用',
+    cnName: '用',
     value: '0'
   }
 ]
@@ -128,66 +141,75 @@ const queryInfo: Array<QueryInfo> = [
 
 // 对话框表单规则字段
 const dilogRuleForm = reactive({
-  eventDisplayName: '',
-  platform: ''
+  actionId: '',
+  actionName: '',
+  remark: '',
+  status: ''
 })
 
 // 对话表单规则
 const dialogRules = reactive<FormRules<typeof dilogRuleForm>>({
-  eventDisplayName: [
-    { required: true, message: 'Event Display Name is required', trigger: 'blur' },
-    {
-      min: 5,
-      max: 10,
-      message: 'Event Display Name must be between 5 and 10 characters',
-      trigger: 'blur'
-    }
+  actionId: [
+    { required: true, message: '事件ID是必填项', trigger: 'blur' },
+    { min: 1, max: 10, message: '事件ID长度必须在1到10之间', trigger: 'blur' }
   ],
-  platform: [
-    { required: true, message: 'Platform is required', trigger: 'change' },
-    {
-      type: 'enum',
-      enum: ['web', 'wx', 'tt'],
-      message: 'Platform must be either "web", "wx", or "tt"',
-      trigger: 'change'
-    }
+  actionName: [
+    { required: true, message: '事件名称是必填项', trigger: 'blur' },
+    { min: 5, max: 20, message: '事件名称长度必须在5到20之间', trigger: 'blur' }
+  ],
+  remark: [
+    { required: false, message: '备注是可选项', trigger: 'blur' },
+    { max: 100, message: '备注长度不能超过100个字符', trigger: 'blur' }
+  ],
+  status: [
+    { required: true, message: '是否启用是必选项', trigger: 'change' },
+    { type: 'number', message: '启用状态必须是数字', trigger: 'change' }
   ]
 })
 
 // 对话框请求参数
 const dialogReq = reactive<ReqConfig>({
-  url: AllApi.mockEvent,
-  otherOptions: {}
+  url: AllApi.setGameAction,
+  otherOptions: {
+    formData: {
+      gid: selectInfo.gid
+    }
+  }
 })
 
 // 对话框表单字段信息
 const FormFields: Array<FormField> = [
   {
-    name: 'eventDisplayName',
-    cnName: '事件名',
+    name: 'actionId',
+    cnName: '事件ID',
+    type: FormFieldType.INPUT
+  },
+  {
+    name: 'actionName',
+    cnName: '事件名称',
     type: FormFieldType.INPUT
   },
   {
-    name: 'platform',
-    cnName: '平台',
+    name: 'remark',
+    cnName: '备注',
+    type: FormFieldType.RICHTEXT
+  },
+  {
+    name: 'status',
+    cnName: '是否启用',
     type: FormFieldType.SELECT,
     otherOptions: {
-      placeholder: '请选择平台',
+      placeholder: '请选择启用状态',
       options: [
         {
-          name: 'wx',
-          label: '微信',
-          value: 'wx'
+          name: 'use',
+          label: '启用',
+          value: 1
         },
         {
-          name: 'tt',
-          label: '抖音',
-          value: 'tt'
-        },
-        {
-          name: 'web',
-          label: 'Web',
-          value: 'web'
+          name: 'nouse',
+          label: '禁用',
+          value: 0
         }
       ]
     }
@@ -231,12 +253,28 @@ const addNewEvent = () => {
   eventDialog.value.addForm()
 }
 
+/**
+ * @description: 表单提交
+ * @return {*}
+ */
+const subForm = () => {
+  eventTable.value.getData()
+}
+
+watch(
+  () => selectInfo.gid,
+  (val: string) => {
+    requestConfig.otherOptions.gid = val
+  }
+)
+
 onMounted(() => {})
 </script>
 
 <template>
   <div>
     <Table
+      ref="eventTable"
       :need-rowindex="false"
       :request-config="requestConfig"
       :open-page-query="true"
@@ -258,7 +296,7 @@ onMounted(() => {})
       </template>
     </Table>
     <div class="eventDialog">
-      <Dialog ref="eventDialog" :config="dialogInfo"></Dialog>
+      <Dialog ref="eventDialog" @form-submit="subForm" :config="dialogInfo"></Dialog>
     </div>
   </div>
 </template>

+ 78 - 49
src/views/Home/Analysis/EventAnalysisDetail.vue

@@ -2,7 +2,7 @@
  * @Author: fxs bjnsfxs@163.com
  * @Date: 2024-08-27 17:11:23
  * @LastEditors: fxs bjnsfxs@163.com
- * @LastEditTime: 2024-09-06 11:34:04
+ * @LastEditTime: 2024-09-09 15:36:46
  * @FilePath: \Game-Backstage-Management-System\src\views\Home\Analysis\EventAnalysisDetail.vue
  * @Description: 
  * 
@@ -10,9 +10,12 @@
 <script setup lang="ts">
 import Table from '@/components/Table.vue'
 import TemporalTrend from '@/components/dataAnalysis/TemporalTrend.vue'
+import { resetTimeToMidnight } from '@/utils/common'
 
-import { reactive } from 'vue'
+import { onMounted, reactive, ref, watch } from 'vue'
 import { useRequest } from '@/hooks/useRequest'
+import { useRoute } from 'vue-router'
+import { useAnalysis } from '@/hooks/useAnalysis'
 
 import type {
   ReqConfig,
@@ -21,14 +24,24 @@ import type {
   TemporalTrendProps,
   TrendTableField
 } from '@/types/dataAnalysis'
-import {
-  type TablePaginationSetting,
-  type TableFieldInfo,
-  FieldSpecialEffectType
-} from '@/types/table'
+import { type TablePaginationSetting, type TableFieldInfo } from '@/types/table'
+
+const { updateReqConfig } = useAnalysis()
+
+interface eventDetailProps {
+  startTime: string
+  endTime: string
+}
+
+const props = withDefaults(defineProps<eventDetailProps>(), {
+  startTime: resetTimeToMidnight(new Date()),
+  endTime: resetTimeToMidnight(new Date())
+})
 
 const { AllApi } = useRequest()
 
+const eventId = ref(-1)
+
 // 表格分页设置
 const pagingConfig = reactive<TablePaginationSetting>({
   limit: 20,
@@ -40,56 +53,32 @@ const pagingConfig = reactive<TablePaginationSetting>({
 // 表格字段信息
 const tableFieldsInfo = reactive<Array<TableFieldInfo>>([
   {
-    name: 'eventAlias',
-    cnName: '事件别名',
-    isShow: true,
-    needSort: false
-  },
-  {
-    name: 'eventId',
-    cnName: '事件ID',
-    isShow: true,
-    needSort: false
-  },
-  {
-    name: 'platform',
-    cnName: '平台',
-    isShow: true,
-    needSort: false
-  },
-  {
-    name: 'usageStatus',
-    cnName: '用量状态',
+    name: 'date',
+    cnName: '日期',
     isShow: true,
-    needSort: false,
-    specialEffect: {
-      type: FieldSpecialEffectType.STATE,
-      othnerInfo: {
-        text: ['启用', '禁用']
-      }
-    }
+    needSort: true
   },
   {
-    name: 'eventCount',
-    cnName: '事件数',
+    name: 'activeCount',
+    cnName: '触发次数',
     isShow: true,
     needSort: true
   },
   {
-    name: 'eventDevices',
-    cnName: '事件达成设备数',
+    name: 'activeUserCount',
+    cnName: '活跃用户数',
     isShow: true,
     needSort: true
   },
   {
-    name: 'activeDeviceRate',
-    cnName: '活跃设备发生率',
+    name: 'activeCountRate',
+    cnName: '活跃用户发生率',
     isShow: true,
     needSort: false
   },
   {
-    name: 'eventPerStartup',
-    cnName: '每启动发生数',
+    name: 'activeCountUser',
+    cnName: '每启动发生数',
     isShow: true,
     needSort: true
   }
@@ -97,26 +86,34 @@ const tableFieldsInfo = reactive<Array<TableFieldInfo>>([
 
 // 表格请求配置
 const tableRequestConfig = reactive<ReqConfig>({
-  url: AllApi.mockEvent,
-  otherOptions: {}
+  url: AllApi.userActionDetail,
+  otherOptions: {
+    id: eventId,
+    startTime: props.startTime,
+    endTime: props.endTime
+  }
 })
 
 // 图表信息
 // 返回数据中字段分类
 const resDataField: ResDataFieldInfo = {
-  xAxis: 'eventCount', // X轴字段为日期
-  values: ['eventCount'] // Y轴字段为事件数
+  xAxis: 'data', // X轴字段为日期
+  values: ['data'] // Y轴字段为事件数
 }
 
 // 图表中表格的字段信息
 const chartTableField: TrendTableField = {
-  eventCount: '事件数'
+  data: '事件数'
 }
 
 // 请求参数配置
 const chartReqConfig = reactive<ReqConfig>({
-  url: AllApi.mockDate,
-  otherOptions: {}
+  url: AllApi.userActionDetailDistribution,
+  otherOptions: {
+    id: eventId,
+    startTime: resetTimeToMidnight(new Date()),
+    endTime: resetTimeToMidnight(new Date())
+  }
 })
 
 // 图表用于切换的tab
@@ -141,6 +138,38 @@ const chartProps = reactive<TemporalTrendProps>({
   resDataFieldsInfo: resDataField,
   tabInfo
 })
+
+const initParams = () => {
+  const routes = useRoute()
+  let query_actionId = routes.query.id as string
+
+  if (query_actionId) {
+    eventId.value = parseInt(query_actionId)
+  }
+}
+
+/**
+ * @description: 监听时间变化,去重新请求数据
+ * @return {*}
+ */
+watch(
+  () => [props.startTime, props.endTime],
+  ([newStart, newEnd]) => {
+    updateReqConfig(chartReqConfig, {
+      startTime: newStart,
+      endTime: newEnd
+    })
+    updateReqConfig(tableRequestConfig, {
+      startTime: newStart,
+      endTime: newEnd
+    })
+    // eventTable.value.getData()
+  }
+)
+
+initParams()
+
+onMounted(() => {})
 </script>
 <template>
   <div class="eventDetail">

+ 58 - 13
src/views/Home/Analysis/EventAnalysisTable.vue

@@ -1,8 +1,11 @@
 <script setup lang="ts">
 import Table from '@/components/Table.vue'
 
-import { reactive } from 'vue'
+import { reactive, watch, ref } from 'vue'
 import { useRequest } from '@/hooks/useRequest'
+import { useCommonStore } from '@/stores/useCommon'
+import { resetTimeToMidnight } from '@/utils/common'
+import { useAnalysis } from '@/hooks/useAnalysis'
 
 import type { ReqConfig } from '@/types/dataAnalysis'
 import {
@@ -13,10 +16,24 @@ import {
 import router from '@/router'
 
 const { AllApi } = useRequest()
+const { selectInfo } = useCommonStore()
+const { updateReqConfig } = useAnalysis()
+
+const eventTable = ref()
 
 // 主要为了给面包屑导航提供信息
 const emits = defineEmits(['enterDetail'])
 
+interface eventTableProps {
+  startTime: string
+  endTime: string
+}
+
+const props = withDefaults(defineProps<eventTableProps>(), {
+  startTime: resetTimeToMidnight(new Date()),
+  endTime: resetTimeToMidnight(new Date())
+})
+
 // 表格分页设置
 const pagingConfig = reactive<TablePaginationSetting>({
   limit: 20,
@@ -28,32 +45,38 @@ const pagingConfig = reactive<TablePaginationSetting>({
 // 表格字段信息
 const tableFieldsInfo = reactive<Array<TableFieldInfo>>([
   {
-    name: 'eventAlias',
-    cnName: '事件别名',
+    name: 'id',
+    cnName: 'ID',
+    isShow: true,
+    needSort: true
+  },
+  {
+    name: 'gid',
+    cnName: '游戏ID',
     isShow: true,
     needSort: false
   },
   {
-    name: 'eventId',
+    name: 'actionId',
     cnName: '事件ID',
     isShow: true,
     needSort: false
   },
   {
-    name: 'platform',
-    cnName: '平台',
+    name: 'actionName',
+    cnName: '事件名称',
     isShow: true,
     needSort: false
   },
   {
-    name: 'usageStatus',
-    cnName: '用量状态',
+    name: 'status',
+    cnName: '事件状态',
     isShow: true,
     needSort: false,
     specialEffect: {
       type: FieldSpecialEffectType.STATE,
       othnerInfo: {
-        text: ['启用', '禁用']
+        text: ['已使用', '已停用']
       }
     }
   },
@@ -85,11 +108,30 @@ const tableFieldsInfo = reactive<Array<TableFieldInfo>>([
 
 // 表格请求配置
 const requestConfig = reactive<ReqConfig>({
-  url: AllApi.mockEvent,
-  otherOptions: {}
+  url: AllApi.gameActionList,
+  otherOptions: {
+    gid: selectInfo.gid,
+    startTime: props.startTime,
+    endTime: props.endTime
+  }
 })
 
 /**
+ * @description: 监听事件变化,去重新请求数据
+ * @return {*}
+ */
+watch(
+  () => [props.startTime, props.endTime],
+  ([newStart, newEnd]) => {
+    updateReqConfig(requestConfig, {
+      startTime: newStart,
+      endTime: newEnd
+    })
+    eventTable.value.getData()
+  }
+)
+
+/**
  * @description: 查看详情
  * @param {*} row 行信息
  * @return {*}
@@ -101,7 +143,9 @@ const viewDetails = (row: any) => {
   })
   router.push({
     name: 'EventAnalysisDetail',
-    query: {}
+    query: {
+      id: row.id
+    }
   })
 }
 </script>
@@ -109,9 +153,10 @@ const viewDetails = (row: any) => {
   <div class="eventTable">
     <div class="content">
       <Table
+        ref="eventTable"
         :need-rowindex="false"
         :request-config="requestConfig"
-        :open-page-query="false"
+        :open-page-query="true"
         :pagination-config="pagingConfig"
         :table-fields-info="tableFieldsInfo"
         :need-left-tools="false"

+ 30 - 2
src/views/Home/Analysis/EventAnalysisView.vue

@@ -2,18 +2,20 @@
  * @Author: fxs bjnsfxs@163.com
  * @Date: 2024-08-27 17:11:23
  * @LastEditors: fxs bjnsfxs@163.com
- * @LastEditTime: 2024-09-06 17:49:01
+ * @LastEditTime: 2024-09-09 16:38:48
  * @FilePath: \Game-Backstage-Management-System\src\views\Home\Analysis\EventAnalysisView.vue
  * @Description: 
  * 
 -->
 <script setup lang="ts">
 import HeaderCard from '@/components/dataAnalysis/HeaderCard.vue'
+import { resetTimeToMidnight } from '@/utils/common'
 import { shouldListenToEvent } from '@/utils/table/table'
 import { reactive, ref } from 'vue'
 
 import type { HeaderCardProps } from '@/types/dataAnalysis'
 
+// 顶部ref
 const headerCard = ref()
 
 // 头部组件需要的 props
@@ -23,6 +25,11 @@ const headerProps = reactive<HeaderCardProps>({
   openDateSelect: true
 })
 
+// 开始时间
+const startTime = ref()
+// 结束时间
+const endTime = ref()
+
 /**
  * @description: 进入详情页,触发headercard的添加事件,增加一个面包屑导航
  * @param {*} info 传入的信息
@@ -32,6 +39,25 @@ const headerAddPath = (info: any) => {
   const { name, pathName } = info
   headerCard.value?.addPath(name, pathName)
 }
+
+/**
+ * @description: 时间改变
+ * @param {*} newDate
+ * @return {*}
+ */
+const dateChange = (newDate: Array<Date>) => {
+  startTime.value = resetTimeToMidnight(newDate[0])
+  endTime.value = resetTimeToMidnight(newDate[1])
+}
+
+/**
+ * @description: 选择的平台改变
+ * @param {*} pf  选择的平台数组,暂时只用第一个
+ * @return {*}
+ */
+const pfChange = (pf: Array<string>) => {
+  console.log(pf)
+}
 </script>
 <template>
   <div class="eventAnalysis">
@@ -42,11 +68,13 @@ const headerAddPath = (info: any) => {
         :default-pf="headerProps.defaultPf"
         :open-date-select="headerProps.openDateSelect"
         :need-breadcrumb="true"
+        @change-date="dateChange"
+        @change-pf="pfChange"
       ></HeaderCard>
     </div>
     <div class="content">
       <!-- 监听表格的跳转事件 -->
-      <router-view v-slot="{ Component, route }">
+      <router-view v-slot="{ Component, route }" :startTime="startTime" :endTime="endTime">
         <!-- 是eventtable组件就去监听enterdetail事件 -->
         <keep-alive>
           <component

+ 3 - 3
src/views/Home/Analysis/KeepView.vue

@@ -128,11 +128,11 @@ const keepTableData = reactive<Array<any>>([])
 
 /**
  * @description: 选择的平台改变
- * @param {*} pf  平台名
+ * @param {*} pf  选择的平台数组,暂时只用第一个
  * @return {*}
  */
-const changePf = (pf: string) => {
-  keepViewSelect.pf = pf
+const changePf = (pf: Array<string>) => {
+  keepViewSelect.pf = pf[0]
 }
 
 /**

+ 3 - 3
src/views/Home/Analysis/UserTrendView.vue

@@ -217,11 +217,11 @@ watch(
 
 /**
  * @description: 选择的平台改变
- * @param {*} pf  平台名
+ * @param {*} pf  选择的平台数组,暂时只用第一个
  * @return {*}
  */
-const changePf = (pf: string) => {
-  userTrendSelectInfo.pf = pf
+const changePf = (pf: Array<string>) => {
+  userTrendSelectInfo.pf = pf[0]
 }
 
 /**

+ 3 - 3
src/views/Home/Overview/OverView.vue

@@ -183,11 +183,11 @@ watch(
 
 /**
  * @description: 选择的平台改变
- * @param {*} pf  平台名
+ * @param {*} pf  选择的平台数组,暂时只用第一个
  * @return {*}
  */
-const changePf = (pf: string) => {
-  overViewSelectInfo.pf = pf
+const changePf = (pf: Array<string>) => {
+  overViewSelectInfo.pf = pf[0]
 }
 
 onMounted(() => {})

+ 1 - 1
src/views/Index.vue

@@ -2,7 +2,7 @@
  * @Author: fxs bjnsfxs@163.com
  * @Date: 2024-08-20 14:06:49
  * @LastEditors: fxs bjnsfxs@163.com
- * @LastEditTime: 2024-09-06 17:38:45
+ * @LastEditTime: 2024-09-09 10:39:52
  * @FilePath: \Game-Backstage-Management-System\src\views\Index.vue
  * @Description: 
  *