Browse Source

优化静态资源加载方式,修复表格BUG

fxs 1 year ago
parent
commit
233031a3c9

+ 2 - 0
.gitignore

@@ -28,3 +28,5 @@ coverage
 *.sw?
 
 *.tsbuildinfo
+
+stats.html

File diff suppressed because it is too large
+ 813 - 11
package-lock.json


+ 5 - 0
package.json

@@ -44,11 +44,16 @@
     "eslint-plugin-vue": "^9.23.0",
     "npm-run-all2": "^6.2.0",
     "prettier": "^3.2.5",
+    "rollup-plugin-visualizer": "^5.12.0",
+    "sharp": "^0.33.5",
+    "terser": "^5.31.6",
     "typescript": "~5.4.0",
     "unplugin-auto-import": "^0.18.2",
     "unplugin-icons": "^0.19.2",
     "unplugin-vue-components": "^0.27.4",
     "vite": "^5.3.1",
+    "vite-plugin-compression": "^0.5.1",
+    "vite-plugin-image-optimizer": "^1.1.8",
     "vue-tsc": "^2.0.21"
   }
 }

BIN
public/img/default/defaultHead.png


File diff suppressed because it is too large
+ 0 - 0
public/img/logo.svg


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

@@ -0,0 +1,6 @@
+<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>

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

@@ -0,0 +1,3 @@
+<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>

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

@@ -0,0 +1,6 @@
+<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>

+ 86 - 41
src/components/Table.vue

@@ -2,13 +2,14 @@
  * @Author: fxs bjnsfxs@163.com
  * @Date: 2024-08-20 18:16:18
  * @LastEditors: fxs bjnsfxs@163.com
- * @LastEditTime: 2024-08-30 17:50:03
+ * @LastEditTime: 2024-08-31 18:04:39
  * @FilePath: \Game-Backstage-Management-System\src\components\Table.vue
  * @Description: 
  * 
 -->
 <script setup lang="ts">
-import type { PropsParams } from '@/types/table'
+import type { PropsParams, TablePaginationSetting } from '@/types/table'
+import type { ReqConfig } from '@/types/dataAnalysis'
 import { FilterType, FieldSpecialEffectType } from '@/types/table'
 
 import { computed, onMounted, reactive, ref, watch } from 'vue'
@@ -47,13 +48,28 @@ const tableData: Array<any> = reactive([])
 // 查询表单的数据
 const queryFormData = reactive<any>({})
 
+// 分页数据
+const paginationConfig2 = reactive<TablePaginationSetting>({
+  currentPage: 0,
+  limit: 0,
+  total: 0,
+  pagesizeList: [],
+  hasLodingData: 0
+})
+
+// 请求配置
+const reqconfig = reactive<ReqConfig>({
+  url: '',
+  otherOptions: {}
+})
+
 // 一些公用方法
-const { getTableData } = useTable(tableData, props.paginationConfig)
+const { getTableData } = useTable(tableData, paginationConfig2)
 
 // 没有开启分页查询的时候使用的数据
 const tableDataNoPaging = computed(() => {
-  let curPage = props.paginationConfig.currentPage
-  let limit = props.paginationConfig.limit
+  let curPage = paginationConfig2.currentPage
+  let limit = paginationConfig2.limit
   let begin = curPage * limit - limit
   //这里不减一是因为,slice方法裁切是左闭右开数组
   let end = curPage * limit
@@ -82,17 +98,17 @@ const dateFieldsList = computed(() => {
 
 // 计算行号
 const computedRowIndex = (index: number) => {
-  return (props.paginationConfig.currentPage - 1) * props.paginationConfig.limit + index + 1
+  return (paginationConfig2.currentPage - 1) * paginationConfig2.limit + index + 1
 }
 
 // 改变页码
 const handleCurrentChange = (val: number) => {
-  props.paginationConfig.currentPage = val
+  paginationConfig2.currentPage = val
 }
 
 // 改变每页大小
 const handleSizeChange = (val: number) => {
-  props.paginationConfig.limit = val
+  paginationConfig2.limit = val
 }
 
 /**
@@ -111,13 +127,13 @@ const getData = () => {
         if (props.openPageQuery) {
           // 如果开启了分页查询,那么要计算出需要展示的页码位置所对应的偏移量
           // 同时要将查询的条数改为对应的用户选择的展示条数
-          props.requestConfig.otherOptions.offset =
-            (props.paginationConfig.currentPage - 1) * props.paginationConfig.limit
-          props.requestConfig.otherOptions.limit = props.paginationConfig.limit
+          reqconfig.otherOptions.offset =
+            (paginationConfig2.currentPage - 1) * paginationConfig2.limit
+          reqconfig.otherOptions.limit = paginationConfig2.limit
         }
 
         // 查询时要根据是否开启分页查询传入对应参数
-        getTableData(props.requestConfig.url, props.requestConfig.otherOptions, props.openPageQuery)
+        getTableData(reqconfig.url, reqconfig.otherOptions, props.openPageQuery)
           .then(() => {
             resolve(true)
           })
@@ -169,7 +185,7 @@ const resetTableData = () => {
 // 按条件查询
 const queryTableData = () => {
   if (props.requestConfig) {
-    props.requestConfig.otherOptions = { ...props.requestConfig.otherOptions, ...queryFormData }
+    reqconfig.otherOptions = { ...reqconfig.otherOptions, ...queryFormData }
     getData()
   } else {
     throw new Error('no match requestConfig')
@@ -233,17 +249,29 @@ const tableCellStyle = (info: any) => {
 
 // 根据分页大小的切换来更新数据
 // 这里将他赋值,用于根据传入的配置来选择是否开启该监听
+/**
+ * @description: 监听litmit,currentpage,gid的变化,改变后去重新请求数据
+ * 如果是limit的变化,则需要把当前页置为1
+ *
+ *  对于Gid需要去监听props的,而不是本地的,因为是外部的改变
+ * @return {*}
+ */
 const changePageLimit = watch(
-  () => [props.paginationConfig.limit, props.paginationConfig.currentPage],
-  ([newLimit], [oldLimit]) => {
+  () => [
+    paginationConfig2.limit,
+    props.requestConfig?.otherOptions.gid,
+    paginationConfig2.currentPage
+  ],
+  ([newLimit, newGid], [oldLimit, oldGid]) => {
     if (newLimit != oldLimit) {
       // 需要给分页按钮加上:current-page.sync="current_page" 配置,不然不生效
       // 当改变每页大小时把之前的缓存全部清除,重新开始
-      props.paginationConfig.currentPage = 1
-      resetTableData()
-      getData()
-    } else if (!tableData[props.paginationConfig.currentPage]) {
-      // 当对应的数组下标位置没有这页的数据的时候再去请求
+      paginationConfig2.currentPage = 1
+      // resetTableData()
+    }
+    if (newGid != oldGid) reqconfig.otherOptions.gid = newGid
+    console.log('gidchange', newGid, oldGid)
+    if (newLimit != oldLimit || !tableData[paginationConfig2.currentPage] || newGid != oldGid) {
       getData()
     }
   },
@@ -261,16 +289,13 @@ const changeDataList = watch(
   }
 )
 
-// 监听gid的变化
-const changeGid = watch(
-  () => props.requestConfig?.otherOptions.gid,
-  () => {
-    getData()
-  }
-)
-
-// 没有传入请求配置就关掉这个监听
-if (!props.requestConfig) changeGid()
+/**
+ * @description: 创建row-key优化表格性能
+ * @return {*}
+ */
+const createRowKey = () => {
+  return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
+}
 
 // 没传入datalist则取消该监听
 if (!props.dataList) {
@@ -278,7 +303,17 @@ if (!props.dataList) {
 }
 
 // 没有开启分页查询就关闭掉这个监听
-if (!props.openPageQuery) changePageLimit()
+if (!props.openPageQuery) {
+  changePageLimit()
+}
+
+const initpageConfig = () => {
+  Object.assign(paginationConfig2, props.paginationConfig)
+}
+
+const initReqConfig = () => {
+  Object.assign(reqconfig, props.requestConfig)
+}
 
 // 定义暴露出去的方法
 defineExpose({
@@ -287,10 +322,14 @@ defineExpose({
 })
 
 onMounted(() => {
+  initpageConfig()
+  initReqConfig()
   if (props.loadingState !== undefined) {
     loading.value = props.loadingState
   }
-  getData()
+  if (!props.openPageQuery) {
+    getData()
+  }
 })
 </script>
 
@@ -375,11 +414,12 @@ onMounted(() => {
     <div class="tableBox">
       <!-- 没有分页的时候需要重新计算一下data -->
       <el-table
-        :data="openPageQuery ? tableData[paginationConfig.currentPage] : tableDataNoPaging"
+        :data="openPageQuery ? tableData[paginationConfig2.currentPage] : tableDataNoPaging"
         style="width: 100%"
         class="tableBody"
         :cell-style="tableCellStyle"
         v-loading="loading"
+        :row-key="createRowKey()"
       >
         <!-- 这里还有问题,需要计算一下 -->
         <el-table-column
@@ -406,8 +446,8 @@ onMounted(() => {
               >
                 {{
                   scope.row[item.name]
-                    ? item.specialEffect.othnerInfo[0]
-                    : item.specialEffect.othnerInfo[1]
+                    ? item.specialEffect.othnerInfo.text[0]
+                    : item.specialEffect.othnerInfo.text[1]
                 }}
               </el-tag>
 
@@ -430,18 +470,23 @@ onMounted(() => {
               <!-- 文字类 -->
               <el-text
                 v-else-if="item.specialEffect?.type === FieldSpecialEffectType.TEXT"
-                :type="scope.row[item.name] ? 'danger' : 'success'"
+                :type="
+                  scope.row[item.name]
+                    ? item.specialEffect.othnerInfo.color[0]
+                    : item.specialEffect.othnerInfo.color[1]
+                "
               >
                 {{ scope.row[item.name] }}
               </el-text>
 
               <!-- 翻译类 -->
               <el-text v-else-if="item.specialEffect?.type === FieldSpecialEffectType.TRANSLATE">
-                {{ item.specialEffect.othnerInfo[scope.row[item.name]] }}
+                {{ item.specialEffect.othnerInfo.translateText[scope.row[item.name]] }}
               </el-text>
 
               <el-text v-else>
                 <!-- 其他列按默认方式显示 -->
+
                 {{
                   props.needAverage &&
                   scope.row[item.name] !== undefined &&
@@ -461,12 +506,12 @@ onMounted(() => {
         <el-pagination
           class="userTablePagination"
           background
-          :page-size="paginationConfig.limit"
-          :page-sizes="paginationConfig.pagesizeList"
+          :page-size="paginationConfig2.limit"
+          :page-sizes="paginationConfig2.pagesizeList"
           table-layout="fixed"
           layout="prev, pager, next ,jumper ,sizes,total,"
-          :total="paginationConfig.total"
-          :current-page.sync="paginationConfig.currentPage"
+          :total="paginationConfig2.total"
+          :current-page.sync="paginationConfig2.currentPage"
           @current-change="handleCurrentChange"
           @size-change="handleSizeChange"
         />

+ 3 - 3
src/components/dataAnalysis/HeaderCard.vue

@@ -9,14 +9,14 @@
 <script setup lang="ts">
 import DropDownSelection from './DropDownSelection.vue'
 import type { DropDownInfo, HeaderCardProps } from '@/types/dataAnalysis'
-import { onMounted, reactive, ref } from 'vue'
+import { onMounted, ref } from 'vue'
 
 const props = defineProps<HeaderCardProps>()
 
 const emits = defineEmits(['changePf', 'changeDate'])
 
 // 平台下拉框信息
-const platFormOptionInfo = reactive<DropDownInfo>({
+const platFormOptionInfo: DropDownInfo = {
   defaultSelect: props.defaultPf,
   title: '请选择平台',
   optionsList: [
@@ -33,7 +33,7 @@ const platFormOptionInfo = reactive<DropDownInfo>({
       label: '抖音'
     }
   ]
-})
+}
 
 // 快速选择日期
 const shortcuts = [

+ 19 - 2
src/components/dataAnalysis/TemporalTrend.vue

@@ -20,6 +20,24 @@ const props = defineProps<TemporalTrendProps>()
 const activeTab = ref<string>('') // 激活的Tab
 const iconSize = ref(20) // 图标的尺寸
 
+// 缓存数据
+// const cacheData: Record<
+//   string,
+//   {
+//     paginationConfig: object
+//     tableDataList: Array<any>
+//     tableFieldsInfo: Array<any>
+//     chartInfo: object
+//   }
+// > = {
+//   t: {
+//     paginationConfig: {},
+//     tableDataList: [],
+//     tableFieldsInfo: [],
+//     chartInfo: {}
+//   }
+// }
+
 // 表格数据
 const tableDataList = reactive<Array<any>>([])
 
@@ -91,10 +109,8 @@ const createTableData = (data: any) => {
   // 把所有的表格需要的数据假如valInfo,其中index字段需要单独拿出来用x轴的信息填充
   props.resDataFieldsInfo['values'].map((key: string) => {
     valInfo[key] = Object.values(data[key])
-    // console.log(data[key])
   })
   valInfo['index'] = xInfo
-  // console.log(valInfo)
   // 生成表格数据
   let newList = reactive<Array<any>>([])
 
@@ -179,6 +195,7 @@ const getData = async (type: number) => {
  */
 const tabChange = (tabName: string) => {
   if (props.tabInfo) {
+    // if(cacheData[tabName])
     let type = props.tabInfo.find((item) => item.name === tabName)?.type
     if (type) getData(type)
     else throw new Error('No match type')

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

@@ -33,9 +33,13 @@ const colorList = [
 // 格式化tooltip
 const formatterTooltip = (params: Array<any>) => {
   let circle = `<span style="display:inline-block;margin-right:5px;border-radius:50%;width:10px;height:10px;left:5px;background-color:`
-  let result = `${params[0].axisValueLabel}`
+  let result = `<span style="font-weight:bold;">${params[0].axisValueLabel}</span>`
+
   params.map((item, index) => {
-    let data = `${circle}${colorList[index]}"></span> ${item['seriesName']}`
+    let data = `${circle}${colorList[index]}"></span>
+    <span >
+    <span  style="display: inline-block;  box-sizing: border-box;
+  padding-right: 50px;">${item['seriesName']}</span><span>${item['value']}<span>`
     result += `<br/>${data}`
   })
   return result
@@ -188,4 +192,11 @@ onMounted(() => {
   <div class="chart" ref="chart" style="width: 100%; height: 365px"></div>
 </template>
 
-<style scoped></style>
+<style scoped>
+.t {
+  color: red;
+  display: inline-block;
+  box-sizing: border-box;
+  padding-right: 20px;
+}
+</style>

+ 3 - 3
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-08-30 17:26:07
+ * @LastEditTime: 2024-08-31 12:59:30
  * @FilePath: \Game-Backstage-Management-System\src\hooks\useRequest.ts
  * @Description:
  *
@@ -20,8 +20,8 @@ export function useRequest() {
   // 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`,
+    // mock: `http://127.0.0.1:8003/mock`,
+    // mockKeep: `http://127.0.0.1:8003/mockKeep`,
 
     getGameTable: `${baseIp}/user/getGidConfig`, // 获取游戏列表
     getUserTable: `${baseIp}/user/userList`, // 获取用户列表

+ 1 - 1
src/hooks/useTable.ts

@@ -2,7 +2,7 @@
  * @Author: fxs bjnsfxs@163.com
  * @Date: 2024-08-20 17:15:49
  * @LastEditors: fxs bjnsfxs@163.com
- * @LastEditTime: 2024-08-28 10:55:54
+ * @LastEditTime: 2024-08-31 17:35:14
  * @FilePath: \Game-Backstage-Management-System\src\hooks\useTable.ts
  * @Description:
  *

+ 8 - 15
src/router/home.ts

@@ -2,32 +2,25 @@
  * @Author: fxs bjnsfxs@163.com
  * @Date: 2024-08-20 14:24:58
  * @LastEditors: fxs bjnsfxs@163.com
- * @LastEditTime: 2024-08-30 11:58:59
+ * @LastEditTime: 2024-08-31 09:49:26
  * @FilePath: \Game-Backstage-Management-System\src\router\home.ts
  * @Description:
  *
  */
 
-import GameManageView from '../views/Home/InfoManage/GameManageView.vue'
-import PlayerManageView from '../views/Home/InfoManage/PlayerManageView.vue'
-import HomeView from '@/views/Home/HomeView.vue'
-import OverView from '@/views/Home/Overview/OverView.vue'
-import KeepView from '@/views/Home/Analysis/KeepView.vue'
-import UserTrendView from '@/views/Home/Analysis/UserTrendView.vue'
-
 export default [
   {
     path: '/home',
-    component: HomeView,
+    component: () => import('@/views/Home/HomeView.vue'),
     children: [
       {
         path: '',
-        redirect: 'gameOverview'
+        redirect: 'overView'
       },
       {
         path: 'overView',
         name: 'OverView',
-        component: OverView,
+        component: () => import('@/views/Home/Overview/OverView.vue'),
         meta: {
           needKeepAlive: true
         }
@@ -35,7 +28,7 @@ export default [
       {
         path: 'playerManageView',
         name: 'PlayerManageView',
-        component: PlayerManageView,
+        component: () => import('@/views/Home/InfoManage/PlayerManageView.vue'),
         meta: {
           needKeepAlive: true
         }
@@ -43,7 +36,7 @@ export default [
       {
         path: 'gameManageView',
         name: 'GameManageView',
-        component: GameManageView,
+        component: () => import('@/views/Home/InfoManage/GameManageView.vue'),
         meta: {
           needKeepAlive: true
         }
@@ -51,7 +44,7 @@ export default [
       {
         path: 'keepView',
         name: 'KeepView',
-        component: KeepView,
+        component: () => import('@/views/Home/Analysis/KeepView.vue'),
         meta: {
           needKeepAlive: true
         }
@@ -59,7 +52,7 @@ export default [
       {
         path: 'userTrendView',
         name: 'UserTrendView',
-        component: UserTrendView,
+        component: () => import('@/views/Home/Analysis/UserTrendView.vue'),
         meta: {
           needKeepAlive: true
         }

+ 9 - 0
src/router/index.ts

@@ -1,3 +1,12 @@
+/*
+ * @Author: fxs bjnsfxs@163.com
+ * @Date: 2024-08-20 14:06:49
+ * @LastEditors: fxs bjnsfxs@163.com
+ * @LastEditTime: 2024-08-31 15:55:29
+ * @FilePath: \Game-Backstage-Management-System\src\router\index.ts
+ * @Description:
+ *
+ */
 import { createRouter, createWebHashHistory } from 'vue-router'
 
 import { authToken } from '@/utils/axios/auth'

+ 10 - 2
src/router/login.ts

@@ -1,8 +1,16 @@
-import LoginView from '../views/Login/LoginView.vue'
+/*
+ * @Author: fxs bjnsfxs@163.com
+ * @Date: 2024-08-20 14:32:43
+ * @LastEditors: fxs bjnsfxs@163.com
+ * @LastEditTime: 2024-08-31 09:59:13
+ * @FilePath: \Game-Backstage-Management-System\src\router\login.ts
+ * @Description:
+ *
+ */
 export default [
   {
     path: '/login',
     name: 'Login',
-    component: LoginView
+    component: () => import('@/views/Login/LoginView.vue')
   }
 ]

+ 1 - 3
src/types/table.ts

@@ -1,7 +1,6 @@
 import type { ReqConfig } from './dataAnalysis'
 // 颜色类型
 export enum ColorType {
-  DEFAULT = 'default',
   PRIMARY = 'primary',
   SUCCESS = 'success',
   INFO = 'info',
@@ -59,8 +58,7 @@ export interface TableFieldInfo {
   isShow: boolean
   specialEffect?: {
     type: FieldSpecialEffectType
-    othnerInfo?: any
-    color?: Array<ColorType>
+    othnerInfo: any
   }
 }
 

+ 17 - 20
src/utils/axios/auth.ts

@@ -1,30 +1,27 @@
-// function getToken() {}
+/*
+ * @Author: fxs bjnsfxs@163.com
+ * @Date: 2024-08-21 11:12:21
+ * @LastEditors: fxs bjnsfxs@163.com
+ * @LastEditTime: 2024-08-31 10:59:37
+ * @FilePath: \Game-Backstage-Management-System\src\utils\axios\auth.ts
+ * @Description:
+ *
+ */
 
-import axiosInstance from './axiosInstance'
-import { useRequest } from '@/hooks/useRequest'
-
-const { AllApi } = useRequest()
+import { ElMessage } from 'element-plus'
+import { MessageType } from '@/types/res'
 
 export const authToken = () => {
   return new Promise((reslove, reject) => {
     let refreshToken = localStorage.getItem('refreshToken')
     if (refreshToken) {
-      axiosInstance
-        .post(AllApi.gerRefreshToken)
-        .then((data) => {
-          let result = JSON.parse(JSON.stringify(data))
-          if (result.code === 0) {
-            localStorage.setItem('token', result.data.token)
-            reslove(true)
-          } else {
-            reject(false)
-          }
-        })
-        .catch((err) => {
-          reject(false)
-          console.log(err)
-        })
+      reslove(true)
     } else {
+      ElMessage({
+        type: MessageType.Warning,
+        message: '请先登录',
+        duration: 1500
+      })
       reject(false)
     }
   })

+ 19 - 1
src/utils/axios/axiosInstance.ts

@@ -1,8 +1,18 @@
+/*
+ * @Author: fxs bjnsfxs@163.com
+ * @Date: 2024-08-20 17:18:52
+ * @LastEditors: fxs bjnsfxs@163.com
+ * @LastEditTime: 2024-08-31 12:55:24
+ * @FilePath: \Game-Backstage-Management-System\src\utils\axios\axiosInstance.ts
+ * @Description:
+ *
+ */
 // 引入axios
 import axios from 'axios'
 import router from '@/router'
 import { ElMessage } from 'element-plus'
 import { useRequest } from '@/hooks/useRequest'
+import { MessageType } from '@/types/res'
 
 const { AllApi } = useRequest()
 // import qs from "qs";
@@ -17,6 +27,7 @@ axiosInstance.interceptors.request.use(
       if (refreshToken) {
         config.headers.Authorization = refreshToken
       }
+      console.log(config.headers.Authorization)
     } else if (config.url !== AllApi.userLogin) {
       let token = localStorage.getItem('token')
 
@@ -40,8 +51,9 @@ axiosInstance.interceptors.response.use(
     if (response.data.code === -1) {
       localStorage.removeItem('token')
       localStorage.removeItem('refreshToken')
+
       ElMessage({
-        type: 'warning',
+        type: MessageType.Warning,
         message: '登录已过期,请重新登陆',
         duration: 1500
       })
@@ -51,6 +63,12 @@ axiosInstance.interceptors.response.use(
   },
   function (error) {
     // 对响应错误做点什么
+    ElMessage({
+      type: MessageType.Error,
+      message: '服务器错误,请稍后再试',
+      duration: 1500
+    })
+    router.push('/login')
     return Promise.reject(error)
   }
 )

+ 67 - 0
src/utils/resource/index.ts

@@ -0,0 +1,67 @@
+// 缓存对象
+const resourceCache: Record<string, string> = {}
+
+/**
+ * 加载资源并且缓存这个资源
+ * 如果资源已经被加载过,则直接返回缓存中的资源
+ *
+ * @param url - 资源的加载路径
+ * @returns 返回blob对象的url
+ */
+export const loadResource = (url: string): Promise<string> => {
+  // 检查是否已经存储了这个资源,已经缓存了就直接返回
+  if (resourceCache[url]) {
+    return Promise.resolve(resourceCache[url])
+  }
+
+  // 如果没有缓存则去请求这个资源,并将他转为bolb对象,并且缓存blob对象的ulr
+  return fetch(url)
+    .then((response) => {
+      if (!response.ok) {
+        throw new Error(`资源加载失败: ${url}`)
+      }
+
+      return response.blob()
+    })
+    .then((blob) => {
+      const objectURL = URL.createObjectURL(blob)
+      resourceCache[url] = objectURL // Cache the resource
+      return objectURL
+    })
+    .catch((error) => {
+      console.error(`资源加载出错 ${url}:`, error)
+      throw error
+    })
+}
+
+/**
+ * @description: 初始化加载所有的资源
+ * @param {Record} resourceObj  请求的url对象,键值都为string
+ * @return {*} 返回一个包含了key和blobURL的对象,用来表示所有资源的请求情况
+ */
+export const initLoadResouce = (
+  resourceObj: Record<string, string>
+): Promise<Record<string, string>> => {
+  // 获取所有的 URL 列表
+  let urlList = Object.entries(resourceObj) // [key, url] 格式
+
+  // 创建请求列表
+  let reqList: Promise<[string, string | null]>[] = urlList.map(([key, url]) =>
+    loadResource(url)
+      .then((objectURL) => [key, objectURL] as [string, string])
+      .catch(() => [key, null] as [string, string | null])
+  )
+
+  // 使用 Promise.all 处理所有请求
+  return Promise.all(reqList).then((results) => {
+    // 创建返回对象
+    let resultObj: Record<string, string> = {}
+    results.forEach(([key, value]) => {
+      if (value !== null) {
+        resultObj[key] = value // 如果成功,存入结果对象中
+      }
+    })
+
+    return resultObj // 返回所有成功的资源
+  })
+}

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

@@ -2,13 +2,13 @@
  * @Author: fxs bjnsfxs@163.com
  * @Date: 2024-08-27 17:11:23
  * @LastEditors: fxs bjnsfxs@163.com
- * @LastEditTime: 2024-08-30 17:45:40
+ * @LastEditTime: 2024-08-31 10:46:51
  * @FilePath: \Game-Backstage-Management-System\src\views\Home\Analysis\KeepView.vue
  * @Description: 
  * 
 -->
 <script setup lang="ts">
-import { watch, reactive, ref } from 'vue'
+import { watch, reactive, ref, toRaw } from 'vue'
 import Table from '@/components/Table.vue'
 import HeaderCard from '@/components/dataAnalysis/HeaderCard.vue'
 import axiosInstance from '@/utils/axios/axiosInstance'
@@ -38,13 +38,13 @@ const keepDataTableInfo = reactive<{
   requestConfig: ReqConfig
   tableFieldsInfo: Array<TableFieldInfo>
 }>({
-  paginationConfig: {
+  paginationConfig: toRaw({
     limit: 15, // 每页展示个数
     currentPage: 1, // 当前页码
     total: 0, // 数据总数
     pagesizeList: [15, 30], // 页数大小列表
     hasLodingData: 0 // 已经加载的数据
-  },
+  }),
   requestConfig: {
     url: AllApi.userRemainDataBydDay,
     otherOptions: {
@@ -54,7 +54,7 @@ const keepDataTableInfo = reactive<{
       endTime: resetTimeToMidnight(new Date())
     }
   },
-  tableFieldsInfo: [
+  tableFieldsInfo: toRaw([
     {
       name: 'date',
       cnName: '日期',
@@ -110,7 +110,7 @@ const keepDataTableInfo = reactive<{
       cnName: '+30日',
       isShow: true
     }
-  ]
+  ])
 })
 
 const keepTableData = reactive<Array<any>>([])
@@ -136,10 +136,10 @@ const changeDate = (date: Array<Date>) => {
   })
 }
 
-const headerCardInfo = reactive<HeaderCardProps>({
+const headerCardInfo: HeaderCardProps = {
   title: '留存分析',
   openDateSelect: true
-})
+}
 
 /**
  * @description: 获取detail表格的数据
@@ -154,7 +154,7 @@ const getTableData = () => {
       analysisResCode(info)
         .then(() => {
           let data = info.data
-          let newList = reactive<Array<any>>([])
+          let newList: Array<any> = []
           for (const [key, val] of Object.entries(data)) {
             // 为了编译通过,不做任何事
             if (key) {

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

@@ -1,7 +1,7 @@
 <script setup lang="ts">
 import HeaderCard from '@/components/dataAnalysis/HeaderCard.vue'
 import Table from '@/components/Table.vue'
-import { onMounted, reactive, watch, ref } from 'vue'
+import { onMounted, reactive, watch, ref, toRaw } from 'vue'
 import type { StaticField, ReqConfig, TemporalTrendInfo } from '@/types/dataAnalysis'
 import type { TablePaginationSetting, TableFieldInfo } from '@/types/table'
 
@@ -27,7 +27,7 @@ const userTrendSelectInfo = reactive({
 })
 
 // 总览数据的字段对应的信息
-const userTrendStaticFieldInfo = reactive<Array<StaticField>>([
+const userTrendStaticFieldInfo: Array<StaticField> = [
   {
     name: 'activeCount',
     cnName: '总活跃用户',
@@ -53,7 +53,7 @@ const userTrendStaticFieldInfo = reactive<Array<StaticField>>([
     cnName: '新注册用户',
     value: ''
   }
-])
+]
 
 // 总览请求参数的配置
 const userTrendDataReqConfig = reactive<ReqConfig>({
@@ -77,7 +77,7 @@ const dataTrendInfo = reactive<TemporalTrendInfo>({
       endTime: resetTimeToMidnight(new Date())
     }
   },
-  tabList: [
+  tabList: toRaw([
     {
       name: 'newUser',
       tabTitle: '新增用户',
@@ -108,8 +108,8 @@ const dataTrendInfo = reactive<TemporalTrendInfo>({
       tabTitle: '单日使用时长',
       type: 6
     }
-  ],
-  chartsStaticField: [
+  ]),
+  chartsStaticField: toRaw([
     {
       name: 'avg',
       cnName: '均值',
@@ -120,14 +120,14 @@ const dataTrendInfo = reactive<TemporalTrendInfo>({
       cnName: '总数',
       value: ''
     }
-  ],
-  resDataField: {
+  ]),
+  resDataField: toRaw({
     xAxis: 'imeDistribution', // x轴的刻度信息所在的字段
     values: ['imeDistribution'] // 值所在的字段
-  },
-  trendTableFields: {
+  }),
+  trendTableFields: toRaw({
     imeDistribution: '新增'
-  }
+  })
 })
 
 // 数据详情表格信息
@@ -136,13 +136,13 @@ const detailDataTableInfo = reactive<{
   requestConfig: ReqConfig
   tableFieldsInfo: Array<TableFieldInfo>
 }>({
-  paginationConfig: {
+  paginationConfig: toRaw({
     limit: 15, // 每页展示个数
     currentPage: 1, // 当前页码
     total: 0, // 数据总数
     pagesizeList: [15, 30], // 页数大小列表
     hasLodingData: 0 // 已经加载的数据
-  },
+  }),
   requestConfig: {
     url: AllApi.userDataTradesDetail,
     otherOptions: {
@@ -152,7 +152,7 @@ const detailDataTableInfo = reactive<{
       endTime: resetTimeToMidnight(new Date())
     }
   },
-  tableFieldsInfo: [
+  tableFieldsInfo: toRaw([
     {
       name: 'date',
       cnName: '日期',
@@ -188,7 +188,7 @@ const detailDataTableInfo = reactive<{
       cnName: '平均使用时长',
       isShow: true
     }
-  ]
+  ])
 })
 
 // detail表格的数据

+ 20 - 11
src/views/Home/HomeView.vue

@@ -1,13 +1,13 @@
 <script setup lang="ts">
 import { RouterView } from 'vue-router'
-import { reactive, 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'
 import type { DropDownInfo } from '@/types/dataAnalysis'
 import DropDownSelection from '@/components/dataAnalysis/DropDownSelection.vue'
 import { useCommonStore } from '@/stores/useCommon'
+import { initLoadResouce } from '@/utils/resource'
 
 const { selectInfo } = useCommonStore()
 const isCollapse = ref(false)
@@ -68,13 +68,6 @@ const logOut = () => {
   router.push('/login')
 }
 
-const iconUrl = {
-  logo: getAssetsImageUrl('logo.svg'),
-  defaultHead: getAssetsImageUrl('default/defaultHead.png')
-}
-
-// const gameSelectList = reactive<Array<DropDownItem>>([])
-
 // 游戏下拉选择框需要的数据
 const gameSelectInfo = reactive<DropDownInfo>({
   defaultSelect: '1001',
@@ -104,6 +97,22 @@ getAllGameInfo().then((data) => {
   }
   gameinfoLoad.value = true
 })
+
+// 资源的加载路径
+const resourceInfo: Record<string, string> = {
+  logo: `/img/logo.svg`,
+  defaultHead: `/img/default/defaultHead.png`
+}
+
+// 使用blob的资源路径信息
+const blobUrlInfo = reactive<Record<string, string>>({})
+
+onMounted(() => {
+  // 去加载所有需要的资源
+  initLoadResouce(resourceInfo).then((data) => {
+    Object.assign(blobUrlInfo, data)
+  })
+})
 </script>
 
 <template>
@@ -111,7 +120,7 @@ getAllGameInfo().then((data) => {
     <div class="sideBarBox">
       <el-menu :router="true" :default-active="$route.name" class="sideBar" :collapse="isCollapse">
         <el-menu-item index="/" class="logoBox">
-          <el-image :fit="'fill'" class="logoImg" :src="iconUrl.logo"></el-image>
+          <el-image :fit="'fill'" class="logoImg" :src="blobUrlInfo.logo"></el-image>
           <template #title><span class="logoText">淳皓科技</span></template>
         </el-menu-item>
         <el-sub-menu v-for="item in menuList" :index="item.title">
@@ -142,7 +151,7 @@ getAllGameInfo().then((data) => {
       <div class="headPortraitBox">
         <el-popover popper-class="headPopper" placement="bottom-end" trigger="click">
           <template #reference>
-            <el-image class="headPortrait" :src="iconUrl.defaultHead"></el-image>
+            <el-image class="headPortrait" :src="blobUrlInfo.defaultHead"></el-image>
           </template>
           <div class="userTools">
             <span class="userToolsItem" @click="logOut">

+ 4 - 4
src/views/Home/InfoManage/GameManageView.vue

@@ -36,14 +36,14 @@ const requestConfig = reactive({
 })
 
 // 配置分页数据
-const paginationConfig = reactive<TablePaginationSetting>({
+const paginationConfig: TablePaginationSetting = {
   limit: 15, // 每页展示个数
   currentPage: 1, // 当前页码
   total: 0, // 数据总数
   pagesizeList: [15, 20], // 页数大小列表
 
   hasLodingData: 0 // 已经加载的数据
-})
+}
 
 // 字段信息
 const filedsInfo = reactive<Array<TableFieldInfo>>([
@@ -80,12 +80,12 @@ const filedsInfo = reactive<Array<TableFieldInfo>>([
 ])
 
 // 游戏配置对话框设置
-const dialogConfig = reactive({
+const dialogConfig = {
   dialogVisible: false,
   title: '游戏配置',
   formLabelWidth: '150px',
   type: 0 // 0 是新增 1是修改
-})
+}
 
 // 表单校验规则
 const gameFormRule = reactive({

+ 25 - 13
src/views/Home/InfoManage/PlayerManageView.vue

@@ -44,14 +44,14 @@ const playerTableRef = ref()
 const playerDialogFormRef = ref<FormInstance>()
 
 // 配置分页数据
-const paginationConfig = reactive<TablePaginationSetting>({
+const paginationConfig: TablePaginationSetting = {
   limit: 15, // 每页展示个数
   currentPage: 1, // 当前页码
   total: 0, // 数据总数
   pagesizeList: [15, 30], // 页数大小列表
 
   hasLodingData: 0 // 已经加载的数据
-})
+}
 
 // 配置请求参数
 const requestConfig = reactive({
@@ -65,7 +65,7 @@ const requestConfig = reactive({
 })
 
 // 所有平台
-const allPf = reactive<Array<SelectInfo>>([
+const allPf: Array<SelectInfo> = [
   {
     name: 'wx',
     cnName: '微信',
@@ -81,13 +81,13 @@ const allPf = reactive<Array<SelectInfo>>([
     cnName: 'Web',
     value: 'web'
   }
-])
+]
 
 // 所有游戏信息
 const allGameInfo = reactive<Array<SelectInfo>>([])
 
 // 查询字段设置
-const queryInfo = reactive<Array<QueryInfo>>([
+const queryInfo: Array<QueryInfo> = [
   {
     name: 'pf',
     label: '平台',
@@ -96,7 +96,7 @@ const queryInfo = reactive<Array<QueryInfo>>([
     otherOption: allPf,
     default: tableStore.playerQueryInfo.pf
   }
-])
+]
 
 // 字段信息
 const filedsInfo = reactive<Array<TableFieldInfo>>([
@@ -110,7 +110,8 @@ const filedsInfo = reactive<Array<TableFieldInfo>>([
     cnName: '头像',
     isShow: true,
     specialEffect: {
-      type: FieldSpecialEffectType.IMG
+      type: FieldSpecialEffectType.IMG,
+      othnerInfo: {}
     }
   },
   {
@@ -119,8 +120,10 @@ const filedsInfo = reactive<Array<TableFieldInfo>>([
     isShow: true,
     specialEffect: {
       type: FieldSpecialEffectType.TAG,
-      othnerInfo: ['是', '否'],
-      color: [ColorType.DANGER, ColorType.SUCCESS]
+      othnerInfo: {
+        text: ['是', '否'],
+        color: [ColorType.DANGER, ColorType.SUCCESS]
+      }
     }
   },
   {
@@ -136,7 +139,14 @@ const filedsInfo = reactive<Array<TableFieldInfo>>([
   {
     name: 'option',
     cnName: '权限',
-    isShow: true
+    isShow: true,
+    specialEffect: {
+      type: FieldSpecialEffectType.TEXT,
+      othnerInfo: {
+        text: ['是', '否'],
+        color: [ColorType.WARNING, ColorType.INFO]
+      }
+    }
   },
   {
     name: 'pf',
@@ -145,9 +155,11 @@ const filedsInfo = reactive<Array<TableFieldInfo>>([
     specialEffect: {
       type: FieldSpecialEffectType.TRANSLATE,
       othnerInfo: {
-        wx: '微信',
-        tt: '抖音',
-        web: 'Web'
+        translateText: {
+          wx: '微信',
+          tt: '抖音',
+          web: 'Web'
+        }
       }
     }
   },

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

@@ -1,6 +1,6 @@
 <script setup lang="ts">
 import HeaderCard from '@/components/dataAnalysis/HeaderCard.vue'
-import { onMounted, reactive, watch, ref } from 'vue'
+import { onMounted, reactive, watch, ref, toRaw } from 'vue'
 import type { StaticField, ReqConfig, TemporalTrendInfo } from '@/types/dataAnalysis'
 
 import StatisticText from '@/components/dataAnalysis/StatisticText.vue'
@@ -23,7 +23,7 @@ const overViewSelectInfo = reactive({
 })
 
 // 总览数据的字段对应的信息
-const overViewStaticFieldInfo = reactive<Array<StaticField>>([
+const overViewStaticFieldInfo: Array<StaticField> = [
   {
     name: 'activeUserCount7',
     cnName: '7天活跃用户',
@@ -44,7 +44,7 @@ const overViewStaticFieldInfo = reactive<Array<StaticField>>([
     cnName: '用户总数',
     value: ''
   }
-])
+]
 
 // 总览请求参数的配置
 const overViewDataReqConfig = reactive<ReqConfig>({
@@ -64,7 +64,7 @@ const periodInfo = reactive<TemporalTrendInfo>({
       gid: selectInfo.gid
     }
   },
-  tabList: [
+  tabList: toRaw([
     {
       name: 'newUser',
       tabTitle: '新增用户',
@@ -80,8 +80,8 @@ const periodInfo = reactive<TemporalTrendInfo>({
       tabTitle: '启动次数',
       type: 3
     }
-  ],
-  chartsStaticField: [
+  ]),
+  chartsStaticField: toRaw([
     {
       name: 'yesterdayCount',
       cnName: '昨日',
@@ -97,16 +97,16 @@ const periodInfo = reactive<TemporalTrendInfo>({
       cnName: '昨日此时',
       value: ''
     }
-  ],
-  resDataField: {
+  ]),
+  resDataField: toRaw({
     xAxis: 'today', // x轴的刻度信息所在的字段
     values: ['yesterday', 'today'] // 值所在的字段
-  },
-  trendTableFields: {
+  }),
+  trendTableFields: toRaw({
     index: '时间段',
     today: '今日',
     yesterday: '昨日'
-  }
+  })
 })
 
 // 30日趋势信息
@@ -118,7 +118,7 @@ const monthInfo = reactive<TemporalTrendInfo>({
       gid: selectInfo.gid
     }
   },
-  tabList: [
+  tabList: toRaw([
     {
       name: 'newUser',
       tabTitle: '新增用户',
@@ -144,8 +144,8 @@ const monthInfo = reactive<TemporalTrendInfo>({
       tabTitle: '留存率',
       type: 5
     }
-  ],
-  chartsStaticField: [
+  ]),
+  chartsStaticField: toRaw([
     {
       name: 'avg',
       cnName: '平均值',
@@ -156,15 +156,15 @@ const monthInfo = reactive<TemporalTrendInfo>({
       cnName: '总数',
       value: ''
     }
-  ],
-  resDataField: {
+  ]),
+  resDataField: toRaw({
     xAxis: 'timeDistribution', // x轴的刻度信息所在的字段
     values: ['timeDistribution'] // 值所在的字段
-  },
-  trendTableFields: {
+  }),
+  trendTableFields: toRaw({
     index: '日期',
     timeDistribution: '新增'
-  }
+  })
 })
 
 /**

+ 18 - 5
src/views/Login/LoginView.vue

@@ -1,13 +1,12 @@
 <script setup lang="ts">
-import { reactive } from 'vue'
+import { onMounted, reactive } from 'vue'
 import type { RuleInfo } from '@/types/input'
 import { useRequest } from '@/hooks/useRequest'
 import router from '@/router'
 import axiosInstance from '@/utils/axios/axiosInstance'
 import MyButton from '@/components/form/MyButton.vue'
 import MyInput from '@/components/form/MyInput.vue'
-
-import { getAssetsImageUrl } from '@/utils/common'
+import { initLoadResouce } from '@/utils/resource'
 
 const { AllApi, analysisResCode } = useRequest()
 
@@ -89,16 +88,30 @@ const formFieldsRules = reactive<{
   }
 })
 
-const logoUrl = getAssetsImageUrl('logo.svg') // 转换一下logo的url
+// 资源的加载路径
+const resourceInfo: Record<string, string> = {
+  logo: `/img/logo.svg`
+}
+
+// 使用blob的资源路径数组
+const blobUrlInfo = reactive<Record<string, string>>({})
+
+onMounted(() => {
+  // 去加载所有需要的资源
+  initLoadResouce(resourceInfo).then((data) => {
+    Object.assign(blobUrlInfo, data)
+  })
+})
 </script>
 
 <template>
+  <!-- <img :src="urlList.head" style="width: 50px; height: 50px" alt="" /> -->
   <div class="container">
     <div class="loginBox">
       <div class="banner"></div>
       <div class="logoBox">
         <div class="logoImg">
-          <el-image :fit="'fill'" class="logoImg" :src="logoUrl"></el-image>
+          <el-image :fit="'fill'" class="logoImg" :src="blobUrlInfo.logo"></el-image>
         </div>
 
         <span class="logoText">淳皓科技</span>

+ 100 - 2
vite.config.ts

@@ -2,7 +2,7 @@
  * @Author: fxs bjnsfxs@163.com
  * @Date: 2024-08-20 14:06:49
  * @LastEditors: fxs bjnsfxs@163.com
- * @LastEditTime: 2024-08-30 12:09:21
+ * @LastEditTime: 2024-08-31 12:20:29
  * @FilePath: \Game-Backstage-Management-System\vite.config.ts
  * @Description:
  *
@@ -18,10 +18,108 @@ import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
 import Icons from 'unplugin-icons/vite'
 import IconsResolver from 'unplugin-icons/resolver'
 
-// https://vitejs.dev/config/
+import { visualizer } from 'rollup-plugin-visualizer'
+
+// 打包
+
+import viteCompression from 'vite-plugin-compression'
+import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'
+const DEFAULT_OPTIONS = {
+  test: /\.(jpe?g|png|gif|tiff|webp|svg|avif)$/i,
+  exclude: undefined,
+  include: undefined,
+  includePublic: true,
+  logStats: true,
+  ansiColors: true,
+  svg: {
+    multipass: true,
+    plugins: [
+      {
+        name: 'preset-default',
+        params: {
+          overrides: {
+            cleanupNumericValues: false,
+            removeViewBox: false // https://github.com/svg/svgo/issues/1128
+          },
+          cleanupIDs: {
+            minify: false,
+            remove: false
+          },
+          convertPathData: false
+        }
+      },
+      'sortAttrs',
+      {
+        name: 'addAttributesToSVGElement',
+        params: {
+          attributes: [{ xmlns: 'http://www.w3.org/2000/svg' }]
+        }
+      }
+    ]
+  },
+  png: {
+    // https://sharp.pixelplumbing.com/api-output#png
+    quality: 100
+  },
+  jpeg: {
+    // https://sharp.pixelplumbing.com/api-output#jpeg
+    quality: 100
+  },
+  jpg: {
+    // https://sharp.pixelplumbing.com/api-output#jpeg
+    quality: 100
+  },
+  tiff: {
+    // https://sharp.pixelplumbing.com/api-output#tiff
+    quality: 100
+  },
+  // gif does not support lossless compression
+  // https://sharp.pixelplumbing.com/api-output#gif
+  gif: {},
+  webp: {
+    // https://sharp.pixelplumbing.com/api-output#webp
+    lossless: true
+  },
+  avif: {
+    // https://sharp.pixelplumbing.com/api-output#avif
+    lossless: true
+  },
+  cache: false,
+  cacheLocation: undefined
+}
+
 export default defineConfig({
+  build: {
+    rollupOptions: {
+      output: {
+        manualChunks: {
+          echarts: ['echarts']
+        },
+        chunkFileNames: 'js/[name]-[hash].js', // 引入文件名的名称
+        entryFileNames: 'js/[name]-[hash].js', // 包的入口文件名称
+        assetFileNames: '[ext]/[name]-[hash].[ext]' // 资源文件像 字体,图片等
+      }
+    },
+    terserOptions: {
+      compress: {
+        drop_console: true,
+        drop_debugger: true
+      }
+    }
+  },
+
   plugins: [
     vue(),
+    ViteImageOptimizer(DEFAULT_OPTIONS),
+    viteCompression({
+      verbose: true, // 默认即可
+      disable: false, // 开启压缩(不禁用),默认即可
+      deleteOriginFile: false, // 删除源文件
+      threshold: 5120, // 压缩前最小文件大小
+      algorithm: 'gzip', // 压缩算法
+      ext: '.gz' // 文件类型
+    }),
+    visualizer({ open: true }),
     AutoImport({
       resolvers: [ElementPlusResolver()]
     }),

Some files were not shown because too many files changed in this diff