Ver código fonte

更新接口

fxs 9 meses atrás
pai
commit
dc3001b5b9

+ 4 - 0
components.d.ts

@@ -40,6 +40,7 @@ declare module 'vue' {
     HeaderCard: typeof import('./src/components/dataAnalysis/HeaderCard.vue')['default']
     IconIcBaselineVisibility: typeof import('~icons/ic/baseline-visibility')['default']
     IconIcBaselineVisibilityOff: typeof import('~icons/ic/baseline-visibility-off')['default']
+    IconIconParkGameThree: typeof import('~icons/icon-park/game-three')['default']
     IconLogosChrome: typeof import('~icons/logos/chrome')['default']
     IconMaterialSymbolsLightLogout: typeof import('~icons/material-symbols-light/logout')['default']
     IconMdiHome: typeof import('~icons/mdi/home')['default']
@@ -58,4 +59,7 @@ declare module 'vue' {
     TemporalTrend: typeof import('./src/components/dataAnalysis/TemporalTrend.vue')['default']
     TimeLineChart: typeof import('./src/components/echarts/TimeLineChart.vue')['default']
   }
+  export interface ComponentCustomProperties {
+    vLoading: typeof import('element-plus/es')['ElLoadingDirective']
+  }
 }

+ 11 - 0
package-lock.json

@@ -24,6 +24,7 @@
       "devDependencies": {
         "@iconify-json/ep": "^1.1.16",
         "@iconify-json/ic": "^1.1.18",
+        "@iconify-json/icon-park": "^1.1.14",
         "@iconify-json/logos": "^1.1.44",
         "@iconify-json/material-symbols-light": "^1.1.28",
         "@iconify-json/mdi": "^1.1.68",
@@ -693,6 +694,16 @@
         "@iconify/types": "*"
       }
     },
+    "node_modules/@iconify-json/icon-park": {
+      "version": "1.1.14",
+      "resolved": "https://registry.npmmirror.com/@iconify-json/icon-park/-/icon-park-1.1.14.tgz",
+      "integrity": "sha512-D/Tyww0fPhEwLqeQuzKMxE4HKjWsCG8TPRZsUIgjGh1qBOI3Ad9G5y0mQi4mLBtHrdCPdfbCz2oZJHxPoLyScA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@iconify/types": "*"
+      }
+    },
     "node_modules/@iconify-json/logos": {
       "version": "1.1.44",
       "resolved": "https://registry.npmmirror.com/@iconify-json/logos/-/logos-1.1.44.tgz",

+ 1 - 0
package.json

@@ -29,6 +29,7 @@
   "devDependencies": {
     "@iconify-json/ep": "^1.1.16",
     "@iconify-json/ic": "^1.1.18",
+    "@iconify-json/icon-park": "^1.1.14",
     "@iconify-json/logos": "^1.1.44",
     "@iconify-json/material-symbols-light": "^1.1.28",
     "@iconify-json/mdi": "^1.1.68",

Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 1
src/assets/logo.svg


+ 151 - 32
src/components/Table.vue

@@ -19,11 +19,21 @@ const queryFormRef = ref<FormInstance>()
 // 传过来的配置
 const props = defineProps<PropsParams>()
 
+// 父组件触发的方法
 const emits = defineEmits(['addNewItem'])
 
+// 加载动画
+const loading = ref(false)
+
 // 表格数据
 const tableData: Array<any> = reactive([])
 
+// 查询表单的数据
+const queryFormData = reactive<any>({})
+
+// 一些公用方法
+const { getTableData } = useTable(tableData, props.paginationConfig)
+
 // 没有开启分页查询的时候使用的数据
 const tableDataNoPaging = computed(() => {
   let curPage = props.paginationConfig.currentPage
@@ -34,12 +44,6 @@ const tableDataNoPaging = computed(() => {
   return tableData.slice(begin, end)
 })
 
-// 查询表单的数据
-const queryFormData = reactive<any>({})
-
-// 一些公用方法
-const { getTableData } = useTable(tableData, props.paginationConfig)
-
 // 所有类型为input的表单控件信息
 const inputFieldsList = computed(() => {
   return props.queryInfo?.filter((item) => item.type === FilterType.INPUT)
@@ -54,12 +58,17 @@ const selectFieldsList = computed(() => {
     return item.type === FilterType.SELECT
   })
 })
-console.log(selectFieldsList)
+
 // 所有类型为date的表单控件信息
 const dateFieldsList = computed(() => {
   return props.queryInfo?.filter((item) => item.type === FilterType.DATE)
 })
 
+// 计算行号
+const computedRowIndex = (index: number) => {
+  return (props.paginationConfig.currentPage - 1) * props.paginationConfig.limit + index + 1
+}
+
 // 改变页码
 const handleCurrentChange = (val: number) => {
   props.paginationConfig.currentPage = val
@@ -70,18 +79,44 @@ const handleSizeChange = (val: number) => {
   props.paginationConfig.limit = val
 }
 
-// 获取数据
+/**
+ * @description: 获取数据,如果没有直接传入数据,则去请求数据,有则直接用
+ * @return {*}
+ */
 const getData = () => {
-  if (props.openPageQuery) {
-    // 如果开启了分页查询,那么要计算出需要展示的页码位置所对应的偏移量
-    // 同时要将查询的条数改为对应的用户选择的展示条数
-    props.requestConfig.other.offset =
-      (props.paginationConfig.currentPage - 1) * props.paginationConfig.limit
-    props.requestConfig.other.limit = props.paginationConfig.limit
-  }
-
-  // 查询时要根据是否开启分页查询传入对应参数
-  getTableData(props.requestConfig.url, props.requestConfig.other, props.openPageQuery)
+  return new Promise((resolve, reject) => {
+    loading.value = true
+    if (props.dataList) {
+      tableData.splice(0, tableData.length, ...props.dataList)
+
+      loading.value = false
+    } else {
+      if (props.requestConfig) {
+        if (props.openPageQuery) {
+          // 如果开启了分页查询,那么要计算出需要展示的页码位置所对应的偏移量
+          // 同时要将查询的条数改为对应的用户选择的展示条数
+          props.requestConfig.other.offset =
+            (props.paginationConfig.currentPage - 1) * props.paginationConfig.limit
+          props.requestConfig.other.limit = props.paginationConfig.limit
+        }
+
+        // 查询时要根据是否开启分页查询传入对应参数
+        getTableData(props.requestConfig.url, props.requestConfig.other, props.openPageQuery)
+          .then(() => {
+            resolve(true)
+          })
+          .catch((err) => {
+            console.log(err)
+            reject(err)
+          })
+          .finally(() => {
+            loading.value = false
+          })
+      } else {
+        throw new Error('no match requestConfig')
+      }
+    }
+  })
 }
 
 // 清空表格数据
@@ -89,6 +124,61 @@ const resetTableData = () => {
   tableData.splice(0, tableData.length)
 }
 
+// 按条件查询
+const queryTableData = () => {
+  if (props.requestConfig) {
+    props.requestConfig.other = { ...props.requestConfig.other, ...queryFormData }
+    getData()
+  } else {
+    throw new Error('no match requestConfig')
+  }
+}
+
+/**
+ * @description: 重置整个查询表单(无效目前)
+ * @param {*} formEl
+ * @return {*}
+ */
+const resetQueryForm = (formEl: FormInstance | undefined) => {
+  if (!formEl) return
+  formEl?.resetFields()
+}
+
+/**
+ * @description: 在获取完数据后,插入均值行
+ * @param {*} start 插入的位置
+ * @param {*} rowData 插入的数据
+ * @return {*}
+ */
+const insertRow = (start: number, rowData: any) => {
+  if (props.openPageQuery) {
+    tableData[start].splice(0, 0, rowData)
+  } else {
+    // tableData.push(rowData)
+    tableData.splice(start, 0, rowData)
+  }
+  // tableData.splice(0, 0, rowData)
+}
+
+/**
+ * @description: 单独处理拥有均值行的表格每个单元格的样式,均值字段均加粗,其他需要比较的字段根据自身百分比显示颜色
+ *              其中使用row-style无效,scope会导致无法覆盖样式
+ *               同时由于我自定义了表格内容,哪里的样式会覆盖row的样式,所以只能单独对单元格设置
+ * @param {*} info  每个单元格的信息
+ * @return {*}
+ */
+const tableCellStyle = (info: any) => {
+  if (info.row.date === '均值')
+    return {
+      'font-weight': 'bold'
+    }
+  else if (info.columnIndex > 1 && props.needAverage) {
+    return {
+      'background-color': `rgba(59, 157, 247,${info.row[info.column.property] / 100})`
+    }
+  } else return {}
+}
+
 // 根据分页大小的切换来更新数据
 // 这里将他赋值,用于根据传入的配置来选择是否开启该监听
 const changePageLimit = watch(
@@ -108,17 +198,15 @@ const changePageLimit = watch(
   { deep: true }
 )
 
-// 按条件查询
-const queryTableData = () => {
-  props.requestConfig.other = { ...props.requestConfig.other, ...queryFormData }
-  getData()
-}
-
-// 重置查询的条件
-const resetQueryForm = (formEl: FormInstance | undefined) => {
-  if (!formEl) return
-  formEl?.resetFields()
-}
+watch(
+  () => [props.dataList],
+  ([newList]) => {
+    getData()
+  },
+  {
+    deep: true
+  }
+)
 
 // 没有开启分页查询就关闭掉这个监听
 if (!props.openPageQuery) changePageLimit()
@@ -130,7 +218,17 @@ defineExpose({
 })
 
 onMounted(() => {
-  getData()
+  getData().then(() => {
+    // 插入一行均值行
+    if (props.needAverage) {
+      let rowData: any = {}
+      Object.values(props.tableFieldsInfo).map((item, index) => {
+        if (index === 0) rowData[item.name] = '均值'
+        else rowData[item.name] = Math.random() * 100
+      })
+      insertRow(0, rowData)
+    }
+  })
 })
 </script>
 
@@ -218,8 +316,17 @@ onMounted(() => {
         :data="openPageQuery ? tableData[paginationConfig.currentPage] : tableDataNoPaging"
         style="width: 100%"
         class="tableBody"
+        :cell-style="tableCellStyle"
+        v-loading="loading"
       >
-        <el-table-column align="center" label="#" type="index" :index="1" />
+        <!-- 这里还有问题,需要计算一下 -->
+        <el-table-column
+          v-if="props.needRowindex"
+          align="center"
+          label="#"
+          type="index"
+          :index="computedRowIndex"
+        />
         <template v-for="item in tableFieldsInfo">
           <el-table-column
             :prop="item.name"
@@ -303,6 +410,7 @@ onMounted(() => {
 .tableContent {
   margin: 0 auto;
   width: 98%;
+
   /* height: 100%; */
 }
 .filterBox,
@@ -368,7 +476,7 @@ onMounted(() => {
 }
 
 .filterItem {
-  width: 30%;
+  width: 20%;
   display: flex;
   align-items: center;
 }
@@ -412,4 +520,15 @@ onMounted(() => {
   display: flex;
   justify-content: center;
 }
+
+.averageItem {
+  font-size: 14px;
+  color: #515b6f;
+}
+
+.normalItem {
+  font-size: 14px;
+  color: #515b6f;
+  font-weight: 400;
+}
 </style>

+ 9 - 8
src/components/dataAnalysis/DropDownSelection.vue

@@ -1,14 +1,15 @@
 <!--
-
-* @FileDescription: 下拉选择框,可用于分类字段或者切换平台等
-
-* @Author: FFF
-
+ * @Author: fxs bjnsfxs@163.com
+ * @Date: 2024-08-23 14:42:47
+ * @LastEditors: fxs bjnsfxs@163.com
+ * @LastEditTime: 2024-08-28 12:25:54
+ * @FilePath: \Game-Backstage-Management-System\src\components\dataAnalysis\DropDownSelection.vue
+ * @Description: 下拉选择框,可用于分类字段或者切换平台等
+ * 
 -->
-
 <script setup lang="ts">
 import type { DropDownInfo } from '@/types/dataAnalysis'
-import { onMounted, reactive, ref } from 'vue'
+import { onMounted, ref } from 'vue'
 
 const props = defineProps<DropDownInfo>()
 const emits = defineEmits(['changeSelect'])
@@ -31,7 +32,7 @@ const changeSelect = (val: any) => {
       @change="changeSelect"
       v-model="selectVal"
       :placeholder="title"
-      size="small"
+      :size="props.size ? props.size : 'small'"
     >
       <el-option
         v-for="item in optionsList"

+ 4 - 36
src/components/dataAnalysis/HeaderCard.vue

@@ -9,21 +9,12 @@
 <script setup lang="ts">
 import DropDownSelection from './DropDownSelection.vue'
 import { useTableStore } from '@/stores/useTable'
-import type { DropDownInfo } from '@/types/dataAnalysis'
+import type { DropDownInfo, HeaderCardProps } from '@/types/dataAnalysis'
 import { reactive, ref } from 'vue'
-import { resetTimeToMidnight } from '@/utils/common/time'
-
-interface HeaderCardProps {
-  title: string // title信息
-  defaultPf: string // 默认选择的pf
-  defaultGame: string // 默认选择的游戏
-  openDateSelect: boolean // 是否开启时间选择
-}
 
 const props = defineProps<HeaderCardProps>()
 
-const emits = defineEmits(['changePf', 'changeGame'])
-const { allGameInfo } = useTableStore()
+const emits = defineEmits(['changePf', 'changeDate'])
 
 // 平台下拉框信息
 const platFormOptionInfo = reactive<DropDownInfo>({
@@ -79,29 +70,13 @@ const shortcuts = [
 // 选择的日期
 const selectDate = ref(shortcuts[0].value())
 
-// 把游戏的信息转换成对应格式
-const gameInfoList = allGameInfo.map((item) => {
-  return { value: item.gid, label: item.gameName }
-})
-
-// 游戏下拉框信息
-const gameOptionInfo = reactive<DropDownInfo>({
-  defaultSelect: props.defaultGame,
-  title: '请选择游戏',
-  optionsList: gameInfoList
-})
-
 const dateChange = (val: any) => {
-  console.log(selectDate.value)
+  emits('changeDate', val)
 }
 
 const changePf = (val: any) => {
   emits('changePf', val)
 }
-
-const changeGame = (val: any) => {
-  emits('changeGame', val)
-}
 </script>
 
 <template>
@@ -117,14 +92,6 @@ const changeGame = (val: any) => {
           :optionsList="platFormOptionInfo.optionsList"
         ></DropDownSelection>
       </div>
-      <div class="selectItem">
-        <DropDownSelection
-          @changeSelect="changeGame"
-          :defaultSelect="gameOptionInfo.defaultSelect"
-          :title="gameOptionInfo.title"
-          :optionsList="gameOptionInfo.optionsList"
-        ></DropDownSelection>
-      </div>
     </div>
     <div v-if="props.openDateSelect" class="datePicker">
       <el-date-picker
@@ -150,6 +117,7 @@ const changeGame = (val: any) => {
   display: flex;
   align-items: center;
   padding: 20px 24px;
+  background-color: white;
 }
 
 .title {

+ 35 - 57
src/components/dataAnalysis/StatisticText.vue

@@ -2,32 +2,22 @@
  * @Author: fxs bjnsfxs@163.com
  * @Date: 2024-08-26 13:57:37
  * @LastEditors: fxs bjnsfxs@163.com
- * @LastEditTime: 2024-08-27 16:49:56
+ * @LastEditTime: 2024-08-28 15:32:44
  * @FilePath: \Game-Backstage-Management-System\src\components\dataAnalysis\StatisticText.vue
- * @Description: 
+ * @Description: 用于展示统计数据,如总览页面上方的总览数据
  * 
 -->
-<!--
-
-* @FileDescription: 统计数据组件,用于展示核心数据
-
-* @Author: FFF
-
--->
 
 <script setup lang="ts">
-import type { StaticDataInfo, StaticDataItemInfo } from '@/types/dataAnalysis'
+import type { StaticDataInfo, StaticField } from '@/types/dataAnalysis'
 import { decimalToPercentage } from '@/utils/common'
-import { StaticDataType } from '@/types/dataAnalysis'
-import { onMounted, reactive, watch, watchEffect } from 'vue'
+import { onMounted, reactive, watchEffect } from 'vue'
 import axiosInstance from '@/utils/axios/axiosInstance'
 import { useRequest } from '@/hooks/useRequest'
 
 const { analysisResCode } = useRequest()
 const props = defineProps<StaticDataInfo>()
-const dataList = reactive<Array<StaticDataItemInfo>>([])
-
-const formatterTime = (val: any) => {}
+const dataList = reactive<Array<StaticField>>([])
 
 /**
  * @description: 用于获取数据
@@ -35,52 +25,42 @@ const formatterTime = (val: any) => {}
  * @return {*}
  */
 const getData = () => {
-  axiosInstance.post(props.requestConfig.url, props.requestConfig.otherOptions).then((data) => {
-    analysisResCode(data).then((info) => {
-      console.log(info)
-      // dataList.splice(0, dataList.length, info.data)
+  if (props.requestConfig) {
+    axiosInstance.post(props.requestConfig.url, props.requestConfig.otherOptions).then((data) => {
+      analysisResCode(data).then((info) => {
+        dataList.splice(0, dataList.length) // 清空一下
+        let data = info.data
+        props.fieldsInfo.map((item) => {
+          dataList.push({
+            name: item.name,
+            cnName: item.cnName,
+            value: data[item.name]
+          })
+        })
+      })
+    })
+  } else {
+    dataList.splice(0, dataList.length) // 清空一下
+    props.fieldsInfo.map((item) => {
+      dataList.push({
+        name: item.name,
+        cnName: item.cnName,
+        value: item.value
+      })
     })
-  })
-}
-
-// 模拟数据
-// 上方总览数据
-let staticData = reactive<Array<StaticDataItemInfo>>([
-  {
-    dataTitle: '累计设备',
-    dataType: StaticDataType.NUM,
-    value: 30,
-    compareVal: 100
-  },
-  {
-    dataTitle: '近7日活跃设备',
-    dataType: StaticDataType.NUM,
-    value: 80,
-    compareVal: 20
-  },
-  {
-    dataTitle: '近30日活跃设备',
-    dataType: StaticDataType.NUM,
-    value: 200,
-    compareVal: 300
-  },
-  {
-    dataTitle: '近7日单设备日均使用时长',
-    dataType: StaticDataType.TEXT,
-    value: '00:00:00'
   }
-])
+}
 
 /**
  * @description: 监听requesconfig的变化,一旦变化就重新请求
  * @return {*}
  */
-// watchEffect(() => getData())
+watchEffect(() => getData())
+
+onMounted(() => {})
 
-onMounted(() => {
-  // getData()
-  // 模拟一下
-  dataList.splice(0, dataList.length, ...staticData)
+defineExpose({
+  getData
 })
 </script>
 
@@ -89,12 +69,10 @@ onMounted(() => {
     <div class="dataBody">
       <div class="dataItem" v-for="item in dataList">
         <div class="header">
-          <span :class="titleClass ? titleClass : 'dataTitle'">{{ item.dataTitle }}</span>
+          <span :class="titleClass ? titleClass : 'dataTitle'">{{ item.cnName }}</span>
         </div>
         <div class="body">
-          <span :class="valueClass ? valueClass : 'value'">{{
-            item.dataType === StaticDataType.DATE ? formatterTime(item.value) : item.value
-          }}</span>
+          <span :class="valueClass ? valueClass : 'value'">{{ item.value }}</span>
           <span class="compare" v-if="item.compareVal">
             <span>
               {{ decimalToPercentage((item.value - item.compareVal) / item.compareVal) }}

+ 160 - 93
src/components/dataAnalysis/TemporalTrend.vue

@@ -7,60 +7,45 @@
 -->
 
 <script setup lang="ts">
-import { onMounted, reactive, ref, watchEffect } from 'vue'
-import {
-  StaticDataType,
-  type StaticDataItemInfo,
-  type TemporalTrendProps,
-  type StaticReqConfig
-} from '@/types/dataAnalysis'
+import { onMounted, reactive, ref, watch, watchEffect } from 'vue'
+import { type StaticField, type TemporalTrendProps, type OptionsProps } from '@/types/dataAnalysis'
 import type { TablePaginationSetting, TableFieldInfo } from '@/types/table'
 import Table from '../Table.vue'
 import TimeLineChart from '../echarts/TimeLineChart.vue'
 import StatisticText from './StatisticText.vue'
 import { useRequest } from '@/hooks/useRequest'
 import axiosInstance from '@/utils/axios/axiosInstance'
+import { useAnalysis } from '@/hooks/useAnalysis'
 
+const { updateReqConfig } = useAnalysis()
 const { AllApi, analysisResCode } = useRequest()
 const props = defineProps<TemporalTrendProps>()
-const activeTab = ref() // 激活的Tab
+const activeTab = ref<string>('') // 激活的Tab
 const iconSize = ref(20) // 图标的尺寸
 
-const yesterdayData = reactive<Array<StaticDataItemInfo>>([
+// 图表上放统计数据字段信息
+const compareFieldInfo = reactive<Array<StaticField>>([
   {
-    dataTitle: '昨日',
-    value: 16,
-    dataType: StaticDataType.NUM,
-    compareVal: 30
+    name: 'yesterdayCount',
+    cnName: '昨日总数',
+    value: ''
   },
   {
-    dataTitle: '昨日此时',
-    value: 16,
-    dataType: StaticDataType.NUM,
-    compareVal: 10
+    name: 'todayCount',
+    cnName: '今日总数',
+    value: ''
   },
   {
-    dataTitle: '今日',
-    value: 16,
-    dataType: StaticDataType.NUM
+    name: 'yesterdayThisTimeCount',
+    cnName: '昨日此时',
+    value: ''
   }
 ])
 
-// 配置总览数据的请求参数
-const compareDataReqConfig = reactive<StaticReqConfig>({
-  url: '',
-  otherOptions: ''
-})
-
-// 配置请求参数
-const tableRequestConfig = reactive({
-  url: AllApi.mock,
-  other: {
-    appSecret: '6YJSuc50uJ18zj45'
-  }
-})
+// 表格数据
+const tableDataList = reactive<Array<any>>([])
 
-// 配置分页数据
+// 配置表格分页数据
 const paginationConfig = reactive<TablePaginationSetting>({
   limit: 6, // 每页展示个数
   currentPage: 1, // 当前页码
@@ -70,25 +55,18 @@ const paginationConfig = reactive<TablePaginationSetting>({
   hasLodingData: 0 // 已经加载的数据
 })
 
-// 字段信息
-const filedsInfo = reactive<Array<TableFieldInfo>>([
-  {
-    name: 'date',
-    cnName: '日期',
-    isShow: true
-  },
-  {
-    name: '新增设备',
-    cnName: 'newEquip',
-    isShow: true
-  }
-])
+// 表格字段信息
+const filedsInfo = reactive<Array<TableFieldInfo>>([])
 
-// 选择的展示形式
+// 图表的展示形式,曲线图或者是表格
 const selectShape = ref('trend')
 
-// 图表上方总览数据
-const overViewData = reactive<Array<StaticDataItemInfo>>([])
+// 图表的信息,包括图例,数据,以及x轴的刻度label
+const chartInfo = reactive<OptionsProps>({
+  legendData: [],
+  xAxisData: [],
+  seriesData: []
+})
 
 /**
  * @description: 改变图表形式的时候
@@ -99,28 +77,118 @@ const changeSelectShape = (name: string) => {
   selectShape.value = name
 }
 
-const getData = async (url: string) => {
-  axiosInstance.post(url, props.requestConfig).then((data) => {
-    analysisResCode(data).then((info) => {
-      console.log(info)
+/**
+ * @description: 获取数据
+ * @param {*} url 请求地址
+ * @param {*} props 传入的配置参数
+ * @return {*}
+ */
+const getData = async (type: number) => {
+  axiosInstance
+    .post(props.requestConfig.url, { ...props.requestConfig.otherOptions, type })
+    .then((data) => {
+      analysisResCode(data).then((info) => {
+        let data = info.data
+
+        // 总览数据赋值
+        // 根据返回数据的key给对应的字段赋值
+        compareFieldInfo.map((item) => {
+          item.value = data[item.name]
+        })
+
+        // 表格赋值
+        let newList = reactive<Array<any>>([])
+        if (props.type === 1) {
+          filedsInfo.splice(0, filedsInfo.length)
+
+          let timesList = Object.keys(data.today)
+          let todayList = data.today
+          let yesterdayList = data.yesterday
+
+          let matchItem = props.tabInfo.find((item) => item.name === activeTab.value)
+
+          let todayName = `today${matchItem?.name as string}`
+          let yesterDayName = `yesterday${matchItem?.name as string}`
+
+          filedsInfo.push({
+            name: 'date',
+            cnName: `时间段`,
+            isShow: true
+          })
+          filedsInfo.push({
+            name: todayName,
+            cnName: `今日${(matchItem?.tabTitle as string).slice(0, 2)}`,
+            isShow: true
+          })
+          filedsInfo.push({
+            name: yesterDayName,
+            cnName: `昨日${(matchItem?.tabTitle as string).slice(0, 2)}`,
+            isShow: true
+          })
+
+          timesList.map((item, index) => {
+            newList.push({
+              date: item,
+              [todayName]: todayList[item],
+              [yesterDayName]: yesterdayList[item]
+            })
+          })
+
+          tableDataList.splice(0, tableDataList.length, ...newList)
+          paginationConfig.total = timesList.length
+          chartInfo.legendData = [
+            `今日${matchItem?.tabTitle.slice(0, 2) as string}`,
+            `昨日${matchItem?.tabTitle.slice(0, 2) as string}`
+          ]
+          chartInfo.seriesData = [todayList, yesterdayList]
+          chartInfo.xAxisData = timesList
+          console.log(chartInfo)
+        } else {
+          filedsInfo.splice(0, filedsInfo.length)
+
+          let matchItem = props.tabInfo.find((item) => item.name === activeTab.value)
+          let timeDistribution = data.timeDistribution
+          let matchName = matchItem?.name as string
+
+          filedsInfo.push({
+            name: 'date',
+            cnName: `日期`,
+            isShow: true
+          })
+
+          filedsInfo.push({
+            name: matchName,
+            cnName: matchItem?.tabTitle as string,
+            isShow: true
+          })
+          for (const [k, v] of Object.entries(timeDistribution)) {
+            newList.push({
+              date: k,
+              [matchName]: v
+            })
+          }
+          tableDataList.splice(0, tableDataList.length, ...newList)
+          paginationConfig.total = Object.entries(timeDistribution).length
+        }
+
+        console.log(newList)
+      })
     })
-  })
 }
 
 /**
- * @description: 当标签页切换的时候,重新获取数据
- * @param {*} TabPaneName 对应的tabName,去tabinfo中找到对应的url
+ * @description: 当标签页切换的时候,重新获取数据,这里可能会改为修改配置,而不是修改url
+ * @param {*} tabName 对应的tabName,去tabinfo中找到对应的url
  * @return {*}
  */
-const tabChange = (TabPaneName: string) => {
-  let url = props.tabInfo.find((item) => item.tabTitle === TabPaneName)?.url
-  if (url) getData(url)
-  else throw new Error('No match url')
+const tabChange = (tabName: string) => {
+  let type = props.tabInfo.find((item) => item.name === tabName)?.type
+  if (type) getData(type)
+  else throw new Error('No match type')
 }
 
-// watchEffect(() => getData(props.requestConfig.url))
-
 onMounted(() => {
+  getData(1)
   activeTab.value = props.defaultActive
 })
 </script>
@@ -140,41 +208,40 @@ onMounted(() => {
     <div class="chartsBox">
       <el-tabs class="tabsBox" v-model="activeTab" @tab-change="tabChange">
         <el-tab-pane
-          class="tabItem"
           :lazy="true"
           v-for="item in tabInfo"
           :label="item.tabTitle"
-          :name="item.tabTitle"
-          :key="item.tabTitle"
+          :name="item.name"
+          :key="item.name"
         >
-          <div class="chartContent" v-if="selectShape === 'trend'">
-            <div class="yesterDayDataBox">
-              <StatisticText
-                :request-config="compareDataReqConfig"
-                :value-class="'chartStaticValue'"
-                :data-list="yesterdayData"
-              ></StatisticText>
-            </div>
-            <TimeLineChart
-              :legend-data="chartInfo.legendData"
-              :series-data="chartInfo.seriesData"
-              :x-axis-data="chartInfo.xAxisData"
-              class="chart"
-              v-if="activeTab === item.tabTitle"
-            ></TimeLineChart>
-          </div>
-          <div class="tableContent" v-else>
-            <Table
-              :need-right-tools="false"
-              :pagination-config="paginationConfig"
-              :table-fields-info="filedsInfo"
-              :request-config="tableRequestConfig"
-              :need-left-tools="false"
-              :open-filter-query="false"
-              :open-page-query="false"
-            ></Table>
-          </div>
         </el-tab-pane>
+        <div class="chartContent" v-if="selectShape === 'trend'">
+          <div class="yesterDayDataBox">
+            <StatisticText
+              :fields-info="compareFieldInfo"
+              :value-class="'chartStaticValue'"
+            ></StatisticText>
+          </div>
+          <TimeLineChart
+            :legend-data="chartInfo.legendData"
+            :series-data="chartInfo.seriesData"
+            :x-axis-data="chartInfo.xAxisData"
+            class="chart"
+          ></TimeLineChart>
+        </div>
+        <div class="tableContent" v-else>
+          <Table
+            :data-list="tableDataList"
+            :need-rowindex="true"
+            :need-average="false"
+            :need-right-tools="false"
+            :pagination-config="paginationConfig"
+            :table-fields-info="filedsInfo"
+            :need-left-tools="false"
+            :open-filter-query="false"
+            :open-page-query="false"
+          ></Table>
+        </div>
       </el-tabs>
       <div class="toolsBox">
         <span class="toolItem" @click="changeSelectShape('trend')">

+ 10 - 3
src/components/echarts/TimeLineChart.vue

@@ -1,5 +1,5 @@
 <script setup lang="ts">
-import { onMounted, reactive, ref, shallowRef } from 'vue'
+import { onMounted, reactive, ref, shallowRef, watch, watchEffect } from 'vue'
 import echarts from '.'
 import { nextTick } from 'vue'
 import { debounceFunc } from '@/utils/common'
@@ -124,7 +124,6 @@ const initOptions = () => {
   options.series = finalSeriesList
   options.legend.data = props.legendData as any
   options.xAxis.data = props.xAxisData as any
-
   linechart.value.setOption(options)
 }
 
@@ -132,7 +131,15 @@ onMounted(() => {
   nextTick(() => {
     linechart.value = echarts.init(chart.value)
     initOptions()
-    window.addEventListener('resize', debounceFunc(changeSize, 500))
+    /**
+     * @description: 只监听window会导致当侧边栏缩放时,dom大小变化但无法resize
+     *              所以需要使用ovserver对整个dom进行监听
+     * @return {*}
+     */
+    // window.addEventListener('resize', debounceFunc(changeSize, 500))
+    const debounceChangeSize = debounceFunc(changeSize, 500)
+    const ro = new ResizeObserver(debounceChangeSize)
+    ro.observe(chart.value)
   })
 })
 </script>

+ 25 - 0
src/hooks/useAnalysis.ts

@@ -0,0 +1,25 @@
+/*
+ * @Author: fxs bjnsfxs@163.com
+ * @Date: 2024-08-20 17:15:49
+ * @LastEditors: fxs bjnsfxs@163.com
+ * @LastEditTime: 2024-08-28 10:55:54
+ * @FilePath: \Game-Backstage-Management-System\src\hooks\useTable.ts
+ * @Description:
+ *
+ */
+import type { StaticReqConfig } from '@/types/dataAnalysis'
+
+export function useAnalysis() {
+  /**
+   * @description: 更新统计组件的请求参数
+   * @return {*}
+   */
+  const updateReqConfig = (config: StaticReqConfig, newVal: any) => {
+    Object.keys(newVal).map((k) => {
+      config.otherOptions[k] = newVal[k]
+    })
+  }
+  return {
+    updateReqConfig
+  }
+}

+ 12 - 2
src/hooks/useRequest.ts

@@ -7,10 +7,13 @@ import type { AxiosResponse } from 'axios'
 import type { ResponseInfo } from '@/types/res'
 
 export function useRequest() {
-  const baseIp = 'http://server.ichunhao.cn'
+  // const baseIp = 'http://server.ichunhao.cn'  // 线上
+  const baseIp = 'http://192.168.1.139:8000' // 本地
 
   const AllApi = {
     mock: `http://127.0.0.1:8003/mock`,
+    mockKeep: `http://127.0.0.1:8003/mockKeep`,
+
     getGameTable: `${baseIp}/user/getGidConfig`, // 获取游戏列表
     getUserTable: `${baseIp}/user/userList`, // 获取用户列表
     addGame: `${baseIp}/user/addGidConfig`, // 添加/修改 游戏配置
@@ -21,7 +24,14 @@ export function useRequest() {
     getInterfaceInfo: `${baseIp}/user/getInterfaceInfo`, // 拿到所有接口的信息
     getInterfaceDataByDay: `${baseIp}/user/getInterfaceDataByDay`, //获取接口的请求频次 (按天)
     gerRefreshToken: `${baseIp}/user/refreshToken`, // 刷新token
-    getOverViewData: `${baseIp}/user/overview` // 总览数据
+    getOverViewData: `${baseIp}/user/overview`, // 总览数据
+
+    // 数据分析相关URL
+    timeDistributionData: `${baseIp}/user/timeDistributionData`, //用户概览 -时段分布
+    userSummary: `${baseIp}/user/summary`, //用户概览 -总览
+    userMouthDistributionData: `${baseIp}/user/mouthDistributionData`, //用户概览 -30日趋势
+    userTrendsOverview: `${baseIp}/user/userTrendsOverview`, //用户趋势 -总览
+    userDataTrades: `${baseIp}/user/dataTrades` //用户趋势 -数据趋势
   }
 
   const analysisResCode = (data: AxiosResponse, kind?: string): Promise<ResponseInfo> => {

+ 10 - 1
src/hooks/useTable.ts

@@ -1,3 +1,12 @@
+/*
+ * @Author: fxs bjnsfxs@163.com
+ * @Date: 2024-08-20 17:15:49
+ * @LastEditors: fxs bjnsfxs@163.com
+ * @LastEditTime: 2024-08-28 10:55:54
+ * @FilePath: \Game-Backstage-Management-System\src\hooks\useTable.ts
+ * @Description:
+ *
+ */
 import axiosInstance from '../utils/axios/axiosInstance'
 import { useRequest } from './useRequest'
 
@@ -14,12 +23,12 @@ export function useTable(tableData: Array<any>, paginationSetting: TablePaginati
           analysisResCode(data)
             .then((info) => {
               let data = info.data
+
               // 如果开启了分页,那么默认这个tabledata是一个二维数组,每个位置对应当页的一个数据数组
               // 没开启则是一个一维数组,直接赋值
               if (isPagination) {
                 tableData[paginationSetting.currentPage] = data
               } else {
-                console.log(data)
                 tableData.splice(0, tableData.length, ...data)
               }
 

+ 22 - 0
src/stores/useCommon.ts

@@ -0,0 +1,22 @@
+/*
+ * @Author: fxs bjnsfxs@163.com
+ * @Date: 2024-08-28 11:46:10
+ * @LastEditors: fxs bjnsfxs@163.com
+ * @LastEditTime: 2024-08-28 12:08:24
+ * @FilePath: \Game-Backstage-Management-System\src\stores\useCommon.ts
+ * @Description:通用的store,在多个页面均会使用
+ *
+ */
+import { reactive } from 'vue'
+import { defineStore } from 'pinia'
+
+interface SelectInfo {
+  gid: string
+}
+
+export const useCommonStore = defineStore('commonStore', () => {
+  const selectInfo = reactive<SelectInfo>({
+    gid: '1001'
+  })
+  return { selectInfo }
+})

+ 6 - 0
src/stores/useDataAnalysis.ts

@@ -0,0 +1,6 @@
+import { reactive } from 'vue'
+import { defineStore } from 'pinia'
+
+export const useDataAnalysisStore = defineStore('dataAnalysisStore', () => {
+  return {}
+})

+ 10 - 1
src/stores/useTable.ts

@@ -1,3 +1,13 @@
+/*
+ * @Author: fxs bjnsfxs@163.com
+ * @Date: 2024-08-22 10:05:10
+ * @LastEditors: fxs bjnsfxs@163.com
+ * @LastEditTime: 2024-08-28 11:44:55
+ * @FilePath: \Game-Backstage-Management-System\src\stores\useTable.ts
+ * @Description:
+ *
+ */
+
 import { reactive } from 'vue'
 import { defineStore } from 'pinia'
 
@@ -29,7 +39,6 @@ export const useTableStore = defineStore('tableStore', () => {
     // allGameInfo = list
     allGameInfo.length = 0
     allGameInfo.push(...list)
-    console.log(allGameInfo)
   }
 
   return { playerQueryInfo, allGameInfo, updateQueryInfo, setGameInfo }

+ 25 - 19
src/types/dataAnalysis.ts

@@ -2,11 +2,12 @@
  * @Author: fxs bjnsfxs@163.com
  * @Date: 2024-08-23 14:58:29
  * @LastEditors: fxs bjnsfxs@163.com
- * @LastEditTime: 2024-08-27 16:10:30
+ * @LastEditTime: 2024-08-28 16:16:44
  * @FilePath: \Game-Backstage-Management-System\src\types\dataAnalysis.ts
  * @Description:用于dataAnalysis相关组件的type
  *
  */
+
 // 下拉选项的信息
 export interface DropDownItem {
   value: any
@@ -15,26 +16,12 @@ export interface DropDownItem {
 
 // 下拉框所需要的props信息
 export interface DropDownInfo {
+  size?: string
   defaultSelect: any
   title: string
   optionsList: Array<DropDownItem>
 }
 
-// 数据分析页面头部数据类型
-export enum StaticDataType {
-  TEXT = 'text',
-  NUM = 'number',
-  DATE = 'date'
-}
-
-// 数据分析页面头部数据信息
-export interface StaticDataItemInfo {
-  dataTitle: string // 标题
-  dataType: StaticDataType // 什么类型的数据
-  value: any // 值
-  compareVal?: any
-}
-
 /**
  * @description: 数据展示组件的请求配置
  *
@@ -45,17 +32,29 @@ export interface StaticReqConfig {
   otherOptions: any
 }
 
+// 数据展示的字段信息
+export interface StaticField {
+  name: string
+  cnName: string
+  value: any // 值
+  compareVal?: any
+}
+
 // 数据分析页面头部数据所需要的props
+// 其中如果配置了请求参数,那么会使用url去查询,然后赋值
+// 如果没有则直接使用fieldinfo里的value
 export interface StaticDataInfo {
-  requestConfig: StaticReqConfig // 请求参数配置
+  requestConfig?: StaticReqConfig // 请求参数配置
+  fieldsInfo: Array<StaticField> //字段信息
   titleClass?: string // 标题的样式
   valueClass?: string // 值的样式
 }
 
 // 时间趋势图的tab信息
 export interface TabInfo {
+  name: string // tab的字段
   tabTitle: string // tab的标题
-  url: string // 到时候需要获取数据的url
+  type: number // 到时候需要获取数据的url
 }
 
 /**
@@ -71,9 +70,16 @@ export interface OptionsProps {
 
 // 时间趋势组件所需要的props
 export interface TemporalTrendProps {
+  type: number // 趋势组件的类型,1为小时段,2为日期统计
   tabInfo: Array<TabInfo> // 用于切换的tab的信息
   requestConfig: StaticReqConfig // 请求参数配置,需要监听,一旦变化,需要重新获取数据
   defaultActive: string // 默认选中的tab
-  chartInfo: OptionsProps // 图表的option配置
   title: string // 上方显示的title
 }
+
+// 头部切换组件所需要的props
+export interface HeaderCardProps {
+  title: string // title信息
+  defaultPf?: string // 默认选择的pf
+  openDateSelect: boolean // 是否开启时间选择
+}

+ 4 - 1
src/types/table.ts

@@ -66,14 +66,17 @@ export interface TableFieldInfo {
 
 // props的参数格式
 export interface PropsParams {
+  needRowindex: boolean // 是否需要行号
+  needAverage: boolean // 是否需要均值功能
   needLeftTools: boolean // 是否需要左侧的工具栏
   needRightTools: boolean // 是否需要右侧工具栏
   openFilterQuery: boolean // 是否开启上方查询功能
   openPageQuery: boolean // 是否开启分页查询
+  dataList?: Array<any> // 表格数据,可以直接传入,也可以给请求地址来请求
   queryInfo?: Array<QueryInfo> // 上方查询功能所需要的信息
   paginationConfig: TablePaginationSetting // 表格分页的信息
   tableFieldsInfo: Array<TableFieldInfo> // 表格字段信息
-  requestConfig: {
+  requestConfig?: {
     // 表格请求的配置
     url: string
     other: any

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

@@ -1,3 +1,12 @@
+/*
+ * @Author: fxs bjnsfxs@163.com
+ * @Date: 2024-08-26 15:46:42
+ * @LastEditors: fxs bjnsfxs@163.com
+ * @LastEditTime: 2024-08-27 17:38:24
+ * @FilePath: \Game-Backstage-Management-System\src\utils\common\index.ts
+ * @Description:
+ *
+ */
 // 防抖
 export function debounceFunc<T extends (...args: any[]) => any>(
   func: T,
@@ -27,7 +36,7 @@ export function decimalToPercentage(decimal: number, decimalPlaces: number = 0):
 // 生成时间刻度
 export function generateHourlyArray(count: number) {
   const result = []
-  for (let i = 1; i <= count; i++) {
+  for (let i = 0; i <= count; i++) {
     result.push(`${i}:00`)
   }
   return result

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

@@ -2,17 +2,138 @@
  * @Author: fxs bjnsfxs@163.com
  * @Date: 2024-08-27 17:11:23
  * @LastEditors: fxs bjnsfxs@163.com
- * @LastEditTime: 2024-08-27 17:13:44
+ * @LastEditTime: 2024-08-28 11:03:06
  * @FilePath: \Game-Backstage-Management-System\src\views\Home\Analysis\KeepView.vue
  * @Description: 
  * 
 -->
 <script setup lang="ts">
+import { ref, reactive, onMounted } from 'vue'
 import Table from '@/components/Table.vue'
 import HeaderCard from '@/components/dataAnalysis/HeaderCard.vue'
+import type { TablePaginationSetting } from '@/types/table'
+import type { HeaderCardProps } from '@/types/dataAnalysis'
+import { useRequest } from '@/hooks/useRequest'
+
+const { AllApi } = useRequest()
+// 配置请求参数
+const requestConfig = reactive({
+  url: AllApi.mockKeep,
+  other: {
+    appSecret: '6YJSuc50uJ18zj45'
+  }
+})
+
+// 配置分页数据
+const paginationConfig = reactive<TablePaginationSetting>({
+  limit: 20, // 每页展示个数
+  currentPage: 1, // 当前页码
+  total: 0, // 数据总数
+  pagesizeList: [20, 30], // 页数大小列表
+  loading: true, // 加载图标
+  hasLodingData: 0 // 已经加载的数据
+})
+
+// 字段信息
+const fieldsInfo = reactive([
+  {
+    name: 'date',
+    cnName: '日期',
+    isShow: true
+  },
+  {
+    name: 'userCount',
+    cnName: '用户数',
+    isShow: true
+  },
+  {
+    name: 'dayPlus1',
+    cnName: '+1日',
+    isShow: true
+  },
+  {
+    name: 'dayPlus2',
+    cnName: '+2日',
+    isShow: true
+  },
+  {
+    name: 'dayPlus3',
+    cnName: '+3日',
+    isShow: true
+  },
+  {
+    name: 'dayPlus4',
+    cnName: '+4日',
+    isShow: true
+  },
+  {
+    name: 'dayPlus5',
+    cnName: '+5日',
+    isShow: true
+  },
+  {
+    name: 'dayPlus6',
+    cnName: '+6日',
+    isShow: true
+  },
+  {
+    name: 'dayPlus7',
+    cnName: '+7日',
+    isShow: true
+  },
+  {
+    name: 'dayPlus14',
+    cnName: '+14日',
+    isShow: true
+  },
+  {
+    name: 'dayPlus30',
+    cnName: '+30日',
+    isShow: true
+  }
+])
+
+const headerCardInfo = reactive<HeaderCardProps>({
+  title: '留存分析',
+  openDateSelect: true
+})
+
+onMounted(() => {})
 </script>
 <template>
-  <div class="KeepViewBox"></div>
+  <div class="KeepViewBox">
+    <div class="header">
+      <HeaderCard
+        :title="headerCardInfo.title"
+        :open-date-select="headerCardInfo.openDateSelect"
+      ></HeaderCard>
+    </div>
+    <div class="tableBox">
+      <Table
+        :need-rowindex="true"
+        :need-average="true"
+        :need-left-tools="false"
+        :need-right-tools="false"
+        :open-filter-query="false"
+        :open-page-query="false"
+        :pagination-config="paginationConfig"
+        :table-fields-info="fieldsInfo"
+        :request-config="requestConfig"
+      ></Table>
+    </div>
+  </div>
 </template>
 
-<style scoped></style>
+<style scoped>
+.KeepViewBox {
+  width: 98%;
+  margin: 1% auto;
+  /* background-color: white; */
+  border: 1px solid #e5e6eb;
+}
+
+.header,
+.tableBox {
+  background-color: white;
+}
+</style>

+ 65 - 2
src/views/Home/HomeView.vue

@@ -1,11 +1,15 @@
 <script setup lang="ts">
 import { RouterView } from 'vue-router'
-import { onMounted, ref } from 'vue'
+import { onMounted, reactive, ref } from 'vue'
 import { ElMessage } from 'element-plus'
 import { getAllGameInfo } from '@/utils/table/table'
 import router from '@/router'
 import { getAssetsImageUrl } from '@/utils/common/img'
+import type { DropDownInfo, DropDownItem } from '@/types/dataAnalysis'
+import DropDownSelection from '@/components/dataAnalysis/DropDownSelection.vue'
+import { useCommonStore } from '@/stores/useCommon'
 
+const { selectInfo } = useCommonStore()
 const isCollapse = ref(false)
 const menuList = [
   {
@@ -65,8 +69,35 @@ const iconUrl = {
   defaultHead: getAssetsImageUrl('default/defaultHead.png')
 }
 
+// const gameSelectList = reactive<Array<DropDownItem>>([])
+
+// 游戏下拉选择框需要的数据
+const gameSelectInfo = reactive<DropDownInfo>({
+  defaultSelect: '1001',
+  title: '请选择游戏',
+  optionsList: []
+})
+
 const gameinfoLoad = ref(false)
-getAllGameInfo().then(() => {
+
+/**
+ * @description: 更新整个页面的游戏选择
+ * @param {*} gid 游戏id
+ * @return {*}
+ */
+const changeGame = (gid: any) => {
+  selectInfo.gid = gid
+}
+
+getAllGameInfo().then((data) => {
+  if (data) {
+    data.map((item) => {
+      gameSelectInfo.optionsList.push({
+        value: item.gid,
+        label: item.gameName
+      })
+    })
+  }
   gameinfoLoad.value = true
 })
 </script>
@@ -92,6 +123,18 @@ getAllGameInfo().then(() => {
       </el-menu>
     </div>
     <div class="navBarBox">
+      <div class="gameSelect">
+        <el-icon class="gameIcon" :size="20">
+          <icon-icon-park-game-three></icon-icon-park-game-three>
+        </el-icon>
+        <DropDownSelection
+          :default-select="gameSelectInfo.defaultSelect"
+          :title="gameSelectInfo.title"
+          :options-list="gameSelectInfo.optionsList"
+          :size="'default'"
+          @change-select="changeGame"
+        ></DropDownSelection>
+      </div>
       <div class="headPortraitBox">
         <el-popover popper-class="headPopper" placement="bottom-end" trigger="click">
           <template #reference>
@@ -119,6 +162,26 @@ getAllGameInfo().then(() => {
   height: 100vh;
 }
 
+.gameSelect {
+  position: absolute;
+  top: 50%;
+  transform: translateY(-50%);
+  /* width: 10%; */
+  height: 80%;
+  display: flex;
+  align-items: center;
+  right: 10%;
+  display: flex;
+  align-items: center;
+
+  /* background-color: lightblue; */
+}
+.gameIcon {
+  /* box-sizing: border-box; */
+  /* padding-right: 12px; */
+  margin-right: 12px;
+}
+
 /* 设置宽度后,content无法适应宽度,只能去间接的调整内部元素的宽度 */
 .sideBarBox {
   position: relative;

+ 2 - 0
src/views/Home/InfoManage/GameManageView.vue

@@ -211,6 +211,8 @@ const submiteGameChange = () => {
   <div class="gameMangeBox">
     <Table
       ref="gameTableRef"
+      :need-rowindex="true"
+      :need-average="false"
       :need-right-tools="true"
       :need-left-tools="true"
       :open-filter-query="false"

+ 3 - 1
src/views/Home/InfoManage/PlayerManageView.vue

@@ -248,6 +248,8 @@ onMounted(() => {
   <div class="gameMangeBox">
     <Table
       ref="playerTableRef"
+      :need-rowindex="true"
+      :need-average="false"
       :need-right-tools="true"
       :need-left-tools="false"
       :open-page-query="true"
@@ -307,7 +309,7 @@ onMounted(() => {
               >加密上传</el-button
             >
             <el-button class="subBtnItem" type="primary" @click="submiteOptionChange()">
-              确认
+              普通上传
             </el-button>
             <el-button class="subBtnItem" @click="dialogClose(playerDialogFormRef, dialogConfig)"
               >取消</el-button

+ 169 - 119
src/views/Home/Overview/OverView.vue

@@ -1,158 +1,206 @@
 <script setup lang="ts">
 import HeaderCard from '@/components/dataAnalysis/HeaderCard.vue'
-import { onMounted, reactive, watch } from 'vue'
-import type {
-  StaticDataItemInfo,
-  TabInfo,
-  OptionsProps,
-  StaticReqConfig
-} from '@/types/dataAnalysis'
-import { StaticDataType } from '@/types/dataAnalysis'
+import { onMounted, reactive, watch, ref } from 'vue'
+import type { StaticField, TabInfo, OptionsProps, StaticReqConfig } from '@/types/dataAnalysis'
+
 import StatisticText from '@/components/dataAnalysis/StatisticText.vue'
 import TemporalTrend from '@/components/dataAnalysis/TemporalTrend.vue'
 import { formatDate, generateHourlyArray } from '@/utils/common'
+import { useCommonStore } from '@/stores/useCommon'
+import { useRequest } from '@/hooks/useRequest'
+import { useAnalysis } from '@/hooks/useAnalysis'
+import type { TableFieldInfo } from '@/types/table'
+
+const { updateReqConfig } = useAnalysis()
+const { AllApi } = useRequest()
+const { selectInfo } = useCommonStore()
+
+// 总览数据的ref
+const overviewStaticRef = ref()
 
 // 目前选择的信息
 // 这里不太合理,应该根据返回的数据中的pf和game赋值,因为这个可能会变
-const selectInfo = reactive({
-  pf: 'web',
-  game: '1001',
-
-  periodSelect: '新增用户',
-  onMonthSelect: '留存用户'
+const overViewSelectInfo = reactive({
+  pf: 'web'
 })
 
+// 总览数据的字段对应的信息
+const overViewStaticFieldInfo = reactive<Array<StaticField>>([
+  {
+    name: 'activeUserCount7',
+    cnName: '7天活跃用户',
+    value: ''
+  },
+  {
+    name: 'activeUserCount30',
+    cnName: '30天活跃用户',
+    value: ''
+  },
+  {
+    name: 'activeUserCount7Time',
+    cnName: '7天平均使用时长',
+    value: ''
+  },
+  {
+    name: 'userCount',
+    cnName: '用户总数',
+    value: ''
+  }
+])
+
 // 总览请求参数的配置
 const overViewDataReqConfig = reactive<StaticReqConfig>({
-  url: '',
+  url: AllApi.userSummary,
   otherOptions: {
-    pf: 'web',
-    gid: '1001'
+    pf: overViewSelectInfo.pf,
+    gid: selectInfo.gid
   }
 })
 
 // 分时段组件请求参数配置
 const periodDataReqConfig = reactive<StaticReqConfig>({
-  url: '',
-  otherOptions: {}
-})
-
-// 一个月内组件请求参数配置
-const monthDataReqConfig = reactive<StaticReqConfig>({
-  url: '',
-  otherOptions: {}
+  url: AllApi.timeDistributionData,
+  otherOptions: {
+    pf: overViewSelectInfo.pf,
+    gid: selectInfo.gid
+  }
 })
 
-/**
- * @description: 监听pf和game的变化,数据变化后立即重新请求所有相关数据
- * @tip watch监听reactive的数据时,必须以getter形式,不然会警告
- * @return {*}
- */
-watch(
-  () => [selectInfo.game, selectInfo.pf],
-  () => {}
-)
-
-/**
- * @description: 选择游戏改变
- * @param {*} game 游戏名
- * @return {*}
- */
-const changeGame = (game: string) => {
-  selectInfo.game = game
-}
-
-/**
- * @description: 选择的平台改变
- * @param {*} pf  平台名
- * @return {*}
- */
-const changePf = (pf: string) => {
-  selectInfo.pf = pf
-}
-
-// 模拟时段数据需要的tab信息
+// 分时段组件据需要的tab信息
 const periodTabList = reactive<Array<TabInfo>>([
   {
+    name: 'newUser',
     tabTitle: '新增用户',
-    url: 'example'
+    type: 1
   },
   {
+    name: 'activeUser',
     tabTitle: '活跃用户',
-    url: 'example'
+    type: 2
+  },
+  {
+    name: 'launchCount',
+    tabTitle: '启动次数',
+    type: 3
   }
 ])
 
-// 模拟一个月内数据需要的tab信息
+// 一个月内组件请求参数配置
+const monthDataReqConfig = reactive<StaticReqConfig>({
+  url: AllApi.userMouthDistributionData,
+  otherOptions: {
+    pf: overViewSelectInfo.pf,
+    gid: selectInfo.gid
+  }
+})
+
+// 模拟一个月内组件需要的tab信息
 const monthTabList = reactive<Array<TabInfo>>([
   {
+    name: 'newUser',
     tabTitle: '新增用户',
-    url: 'example'
+    type: 1
   },
   {
+    name: 'activeUser',
     tabTitle: '活跃用户',
-    url: 'example'
+    type: 2
+  },
+  {
+    name: 'launchCount',
+    tabTitle: '启动次数',
+    type: 3
   },
   {
+    name: 'deviceDuration',
+    tabTitle: '单设备时长',
+    type: 4
+  },
+  {
+    name: 'retentionRate',
     tabTitle: '留存率',
-    url: 'example'
+    type: 5
   }
 ])
 
-// 模拟时间段图表数据
-const periodData = reactive<OptionsProps>({
-  xAxisData: generateHourlyArray(24),
-  legendData: ['今日新增', '昨日新增'],
-  seriesData: [
-    [1, 23, 3, 4, 5, 566, 7, 7],
-    [1, 23, 34, 42, 5, 6, 71, 7]
-  ]
-})
+/**
+ * @description: 监听pf和gid的变化,数据变化后立即重新请求所有相关数据
+ * @tip watch监听reactive的数据时,必须以getter形式,不然会警告
+ * @return {*}
+ */
+watch(
+  () => [overViewSelectInfo.pf, selectInfo.gid],
+  ([newPf, newGid]) => {
+    updateReqConfig(overViewDataReqConfig, { pf: newPf, gid: newGid })
+    updateReqConfig(periodDataReqConfig, { pf: newPf, gid: newGid })
+    updateReqConfig(monthDataReqConfig, { pf: newPf, gid: newGid })
+  }
+)
+
+/**
+ * @description: 选择的平台改变
+ * @param {*} pf  平台名
+ * @return {*}
+ */
+const changePf = (pf: string) => {
+  overViewSelectInfo.pf = pf
+}
+
+// // 模拟函数
+// function generateRandomArray(numOfArrays: number, lengthOfArray: number) {
+//   return Array.from({ length: numOfArrays }, () =>
+//     Array.from({ length: lengthOfArray }, () => Math.floor(Math.random() * 100))
+//   )
+// }
+
+// // 模拟时间段图表数据
+// const periodData = reactive<OptionsProps>({
+//   xAxisData: generateHourlyArray(23),
+//   legendData: ['今日新增', '昨日新增'],
+//   seriesData: generateRandomArray(2, 24)
+// })
 
 // 一个月的日期模拟
-const oneMonthDate = [
-  '20240801',
-  '20240802',
-  '20240803',
-  '20240804',
-  '20240805',
-  '20240806',
-  '20240807',
-  '20240808',
-  '20240809',
-  '20240810',
-  '20240811',
-  '20240812',
-  '20240813',
-  '20240814',
-  '20240815',
-  '20240816',
-  '20240817',
-  '20240818',
-  '20240819',
-  '20240820',
-  '20240821',
-  '20240822',
-  '20240823',
-  '20240824',
-  '20240825',
-  '20240826',
-  '20240827',
-  '20240828',
-  '20240829',
-  '20240830',
-  '20240831'
-]
+// const oneMonthDate = [
+//   '20240801',
+//   '20240802',
+//   '20240803',
+//   '20240804',
+//   '20240805',
+//   '20240806',
+//   '20240807',
+//   '20240808',
+//   '20240809',
+//   '20240810',
+//   '20240811',
+//   '20240812',
+//   '20240813',
+//   '20240814',
+//   '20240815',
+//   '20240816',
+//   '20240817',
+//   '20240818',
+//   '20240819',
+//   '20240820',
+//   '20240821',
+//   '20240822',
+//   '20240823',
+//   '20240824',
+//   '20240825',
+//   '20240826',
+//   '20240827',
+//   '20240828',
+//   '20240829',
+//   '20240830',
+//   '20240831'
+// ]
 
 // 模拟30天内图表数据
-const oneMonthData = reactive<OptionsProps>({
-  xAxisData: oneMonthDate.map((item) => formatDate(item)),
-  legendData: ['今日新增', '昨日新增'],
-  seriesData: [
-    [1, 23, 3, 4, 5, 566, 7, 7],
-    [1, 23, 34, 42, 5, 6, 71, 7]
-  ]
-})
+// const oneMonthData = reactive<OptionsProps>({
+//   xAxisData: oneMonthDate.map((item) => formatDate(item)),
+//   legendData: ['新增'],
+//   seriesData: generateRandomArray(1, 31)
+// })
 
 onMounted(() => {})
 </script>
@@ -160,32 +208,34 @@ onMounted(() => {})
   <div class="overViewBox">
     <div class="header">
       <HeaderCard
-        :open-date-select="true"
-        :default-game="selectInfo.game"
-        :default-pf="selectInfo.pf"
-        @change-game="changeGame"
+        :open-date-select="false"
+        :default-pf="overViewSelectInfo.pf"
         @change-pf="changePf"
         :title="'数据总览'"
       ></HeaderCard>
     </div>
     <div class="staticBox">
-      <StatisticText :request-config="overViewDataReqConfig"></StatisticText>
+      <StatisticText
+        ref="overviewStaticRef"
+        :fields-info="overViewStaticFieldInfo"
+        :request-config="overViewDataReqConfig"
+      ></StatisticText>
     </div>
     <div class="periodTrendBox">
       <TemporalTrend
+        :type="1"
         :request-config="periodDataReqConfig"
         :title="'时段分布'"
-        :chart-info="periodData"
-        :defaultActive="'新增用户'"
+        :defaultActive="periodTabList[0].name"
         :tabInfo="periodTabList"
       ></TemporalTrend>
     </div>
     <div class="oneMonthTrendBox">
       <TemporalTrend
+        :type="2"
         :request-config="monthDataReqConfig"
         :title="'30日内趋势'"
-        :chart-info="oneMonthData"
-        :defaultActive="'新增用户'"
+        :defaultActive="monthTabList[0].name"
         :tabInfo="monthTabList"
       ></TemporalTrend>
     </div>

Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff