Browse Source

refactor: 带来更美观精致的首页 (#848)

xiaoming 1 year ago
parent
commit
4fa6a47342

+ 0 - 1
build/optimize.ts

@@ -25,7 +25,6 @@ const include = [
   "sortablejs",
   "swiper/vue",
   "mint-filter",
-  "md-editor-v3",
   "@vueuse/core",
   "vue3-danmaku",
   "v-contextmenu",

File diff suppressed because it is too large
+ 0 - 38
mock/list.ts


+ 2 - 3
package.json

@@ -12,7 +12,7 @@
     "preview": "vite preview",
     "preview:build": "pnpm build && vite preview",
     "typecheck": "tsc --noEmit && vue-tsc --noEmit --skipLibCheck",
-    "svgo": "svgo -f src/assets/svg -o src/assets/svg",
+    "svgo": "svgo -f src/assets/svg",
     "cloc": "NODE_OPTIONS=--max-old-space-size=4096 cloc . --exclude-dir=node_modules --exclude-lang=YAML",
     "clean:cache": "rimraf .eslintcache && rimraf pnpm-lock.yaml && rimraf node_modules && pnpm store prune && pnpm install",
     "lint:eslint": "eslint --cache --max-warnings 0  \"{src,mock,build}/**/*.{vue,js,ts,tsx}\" --fix",
@@ -54,7 +54,7 @@
     "@logicflow/extension": "^1.2.19",
     "@pureadmin/descriptions": "^1.2.0",
     "@pureadmin/table": "^3.0.0",
-    "@pureadmin/utils": "^2.1.1",
+    "@pureadmin/utils": "^2.1.2",
     "@vueuse/core": "^10.7.1",
     "@vueuse/motion": "^2.0.0",
     "@wangeditor/editor": "^5.1.23",
@@ -72,7 +72,6 @@
     "js-cookie": "^3.0.5",
     "jsbarcode": "^3.11.6",
     "localforage": "^1.10.0",
-    "md-editor-v3": "2.7.2",
     "mint-filter": "^4.0.3",
     "mitt": "^3.0.1",
     "nprogress": "^0.2.0",

+ 4 - 12
pnpm-lock.yaml

@@ -24,8 +24,8 @@ dependencies:
     specifier: ^3.0.0
     version: 3.0.0(element-plus@2.4.4)(typescript@5.3.3)
   '@pureadmin/utils':
-    specifier: ^2.1.1
-    version: 2.1.1(echarts@5.4.3)(vue@3.4.3)
+    specifier: ^2.1.2
+    version: 2.1.2(echarts@5.4.3)(vue@3.4.3)
   '@vueuse/core':
     specifier: ^10.7.1
     version: 10.7.1(vue@3.4.3)
@@ -77,9 +77,6 @@ dependencies:
   localforage:
     specifier: ^1.10.0
     version: 1.10.0
-  md-editor-v3:
-    specifier: 2.7.2
-    version: 2.7.2
   mint-filter:
     specifier: ^4.0.3
     version: 4.0.3
@@ -1760,8 +1757,8 @@ packages:
       string-hash: 1.1.3
     dev: true
 
-  /@pureadmin/utils@2.1.1(echarts@5.4.3)(vue@3.4.3):
-    resolution: {integrity: sha512-4M0sA1OfLC2q3duTO/f56a7c7DdlCdZ1mddcnqeanCCmlB9Y16MsKLwozZ5mgrsSXEhG421b0g5Zuufxe+YShQ==}
+  /@pureadmin/utils@2.1.2(echarts@5.4.3)(vue@3.4.3):
+    resolution: {integrity: sha512-TjMLN6MBNa5WkJYMidk1LhTfmTlHcSEjFm1jtN3e5z9Q3jzfnu5jF8MC+b0WF0LzeRqnq6kICrxXpSn5/HqNIA==}
     peerDependencies:
       echarts: '*'
       vue: '*'
@@ -6035,11 +6032,6 @@ packages:
     resolution: {integrity: sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==}
     dev: true
 
-  /md-editor-v3@2.7.2:
-    resolution: {integrity: sha512-CyLG7yZhMyKplXO/MYIccpL0AOcnys74cMpbBG77rmXWlANAmzLrznUU++g6MohTv3DCRNTz+5Uh/w9h9P2sSA==}
-    engines: {node: '>=12.0.0'}
-    dev: false
-
   /mdn-data@2.0.14:
     resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==}
     dev: true

+ 0 - 5
src/api/list.ts

@@ -12,8 +12,3 @@ type Result = {
 export const getCardList = (data?: object) => {
   return http.request<Result>("post", "/get-card-list", { data });
 };
-
-/** 版本日志 */
-export const getReleases = () => {
-  return http.request<Result>("get", "/releases");
-};

+ 6 - 12
src/plugins/echarts/index.ts

@@ -1,10 +1,11 @@
 import type { App } from "vue";
 import * as echarts from "echarts/core";
-import { CanvasRenderer } from "echarts/renderers";
+import { CanvasRenderer, SVGRenderer } from "echarts/renderers";
 import { PieChart, BarChart, LineChart } from "echarts/charts";
 import {
   GridComponent,
   TitleComponent,
+  PolarComponent,
   LegendComponent,
   GraphicComponent,
   ToolboxComponent,
@@ -14,15 +15,16 @@ import {
 } from "echarts/components";
 
 const { use } = echarts;
-// const { use, registerTheme } = echarts;
 
 use([
   PieChart,
   BarChart,
   LineChart,
   CanvasRenderer,
+  SVGRenderer,
   GridComponent,
   TitleComponent,
+  PolarComponent,
   LegendComponent,
   GraphicComponent,
   ToolboxComponent,
@@ -32,16 +34,8 @@ use([
 ]);
 
 /**
- * @description 自定义主题
- * @see {@link https://echarts.apache.org/zh/download-theme.html}
- */
-// import theme from "./theme.json";
-// registerTheme("ovilia-green", theme);
-
-/**
- * @description 按需引入echarts
- * @see {@link https://echarts.apache.org/handbook/zh/basics/import#%E6%8C%89%E9%9C%80%E5%BC%95%E5%85%A5-echarts-%E5%9B%BE%E8%A1%A8%E5%92%8C%E7%BB%84%E4%BB%B6}
- * @see 温馨提示:必须将 `$echarts` 添加到全局 `globalProperties` ,为了配合 https://pure-admin-utils.netlify.app/hooks/useEcharts/useEcharts.html 使用
+ * @description 按需引入echarts,具体看 https://echarts.apache.org/handbook/zh/basics/import/#%E5%9C%A8-typescript-%E4%B8%AD%E6%8C%89%E9%9C%80%E5%BC%95%E5%85%A5
+ * @see 温馨提示:必须将 `$echarts` 添加到全局 `globalProperties` ,具体看 https://pure-admin-utils.netlify.app/hooks/useECharts/useECharts#%E4%BD%BF%E7%94%A8%E5%89%8D%E6%8F%90
  */
 export function useEcharts(app: App) {
   app.config.globalProperties.$echarts = echarts;

+ 0 - 394
src/plugins/echarts/theme.json

@@ -1,394 +0,0 @@
-{
-  "color": ["#4ea397", "#22c3aa", "#7bd9a5"],
-  "backgroundColor": "rgba(0,0,0,0)",
-  "textStyle": {},
-  "title": {
-    "textStyle": {
-      "color": "#666666"
-    },
-    "subtextStyle": {
-      "color": "#999999"
-    }
-  },
-  "line": {
-    "itemStyle": {
-      "borderWidth": "2"
-    },
-    "lineStyle": {
-      "width": "3"
-    },
-    "symbolSize": "10",
-    "symbol": "emptyCircle",
-    "smooth": true
-  },
-  "radar": {
-    "itemStyle": {
-      "borderWidth": "2"
-    },
-    "lineStyle": {
-      "width": "3"
-    },
-    "symbolSize": "10",
-    "symbol": "emptyCircle",
-    "smooth": true
-  },
-  "bar": {
-    "itemStyle": {
-      "barBorderWidth": "0",
-      "barBorderColor": "#444444"
-    },
-    "emphasis": {
-      "itemStyle": {
-        "barBorderWidth": "0",
-        "barBorderColor": "#444444"
-      }
-    }
-  },
-  "pie": {
-    "itemStyle": {
-      "borderWidth": "0",
-      "borderColor": "#444444"
-    },
-    "emphasis": {
-      "itemStyle": {
-        "borderWidth": "0",
-        "borderColor": "#444444"
-      }
-    }
-  },
-  "scatter": {
-    "itemStyle": {
-      "borderWidth": "0",
-      "borderColor": "#444444"
-    },
-    "emphasis": {
-      "itemStyle": {
-        "borderWidth": "0",
-        "borderColor": "#444444"
-      }
-    }
-  },
-  "boxplot": {
-    "itemStyle": {
-      "borderWidth": "0",
-      "borderColor": "#444444"
-    },
-    "emphasis": {
-      "itemStyle": {
-        "borderWidth": "0",
-        "borderColor": "#444444"
-      }
-    }
-  },
-  "parallel": {
-    "itemStyle": {
-      "borderWidth": "0",
-      "borderColor": "#444444"
-    },
-    "emphasis": {
-      "itemStyle": {
-        "borderWidth": "0",
-        "borderColor": "#444444"
-      }
-    }
-  },
-  "sankey": {
-    "itemStyle": {
-      "borderWidth": "0",
-      "borderColor": "#444444"
-    },
-    "emphasis": {
-      "itemStyle": {
-        "borderWidth": "0",
-        "borderColor": "#444444"
-      }
-    }
-  },
-  "funnel": {
-    "itemStyle": {
-      "borderWidth": "0",
-      "borderColor": "#444444"
-    },
-    "emphasis": {
-      "itemStyle": {
-        "borderWidth": "0",
-        "borderColor": "#444444"
-      }
-    }
-  },
-  "gauge": {
-    "itemStyle": {
-      "borderWidth": "0",
-      "borderColor": "#444444"
-    },
-    "emphasis": {
-      "itemStyle": {
-        "borderWidth": "0",
-        "borderColor": "#444444"
-      }
-    }
-  },
-  "candlestick": {
-    "itemStyle": {
-      "color": "#d0648a",
-      "color0": "#ffffff",
-      "borderColor": "#d0648a",
-      "borderColor0": "#22c3aa",
-      "borderWidth": 1
-    }
-  },
-  "graph": {
-    "itemStyle": {
-      "borderWidth": "0",
-      "borderColor": "#444444"
-    },
-    "lineStyle": {
-      "width": 1,
-      "color": "#aaa"
-    },
-    "symbolSize": "10",
-    "symbol": "emptyCircle",
-    "smooth": true,
-    "color": ["#4ea397", "#22c3aa", "#7bd9a5"],
-    "label": {
-      "color": "#ffffff"
-    }
-  },
-  "map": {
-    "itemStyle": {
-      "areaColor": "#eeeeee",
-      "borderColor": "#999999",
-      "borderWidth": "0.5"
-    },
-    "emphasis": {
-      "itemStyle": {
-        "areaColor": "rgba(34,195,170,0.25)",
-        "borderColor": "#22c3aa",
-        "borderWidth": "0.5"
-      },
-      "label": {
-        "color": "rgb(52,158,142)"
-      }
-    },
-    "label": {
-      "color": "#28544e"
-    }
-  },
-  "geo": {
-    "itemStyle": {
-      "areaColor": "#eeeeee",
-      "borderColor": "#999999",
-      "borderWidth": "0.5"
-    },
-    "emphasis": {
-      "itemStyle": {
-        "areaColor": "rgba(34,195,170,0.25)",
-        "borderColor": "#22c3aa",
-        "borderWidth": "0.5"
-      },
-      "label": {
-        "color": "rgb(52,158,142)"
-      }
-    },
-    "label": {
-      "color": "#28544e"
-    }
-  },
-  "categoryAxis": {
-    "axisLine": {
-      "show": true,
-      "lineStyle": {
-        "color": "#cccccc"
-      }
-    },
-    "axisTick": {
-      "show": false,
-      "lineStyle": {
-        "color": "#333333"
-      }
-    },
-    "axisLabel": {
-      "show": true,
-      "color": "#999999"
-    },
-    "splitLine": {
-      "show": true,
-      "lineStyle": {
-        "color": ["#eeeeee"]
-      }
-    },
-    "splitArea": {
-      "show": false,
-      "areaStyle": {
-        "color": ["rgba(250,250,250,0.3)", "rgba(200,200,200,0.3)"]
-      }
-    }
-  },
-  "valueAxis": {
-    "axisLine": {
-      "show": true,
-      "lineStyle": {
-        "color": "#cccccc"
-      }
-    },
-    "axisTick": {
-      "show": false,
-      "lineStyle": {
-        "color": "#333333"
-      }
-    },
-    "axisLabel": {
-      "show": true,
-      "color": "#999999"
-    },
-    "splitLine": {
-      "show": true,
-      "lineStyle": {
-        "color": ["#eeeeee"]
-      }
-    },
-    "splitArea": {
-      "show": false,
-      "areaStyle": {
-        "color": ["rgba(250,250,250,0.3)", "rgba(200,200,200,0.3)"]
-      }
-    }
-  },
-  "logAxis": {
-    "axisLine": {
-      "show": true,
-      "lineStyle": {
-        "color": "#cccccc"
-      }
-    },
-    "axisTick": {
-      "show": false,
-      "lineStyle": {
-        "color": "#333333"
-      }
-    },
-    "axisLabel": {
-      "show": true,
-      "color": "#999999"
-    },
-    "splitLine": {
-      "show": true,
-      "lineStyle": {
-        "color": ["#eeeeee"]
-      }
-    },
-    "splitArea": {
-      "show": false,
-      "areaStyle": {
-        "color": ["rgba(250,250,250,0.3)", "rgba(200,200,200,0.3)"]
-      }
-    }
-  },
-  "timeAxis": {
-    "axisLine": {
-      "show": true,
-      "lineStyle": {
-        "color": "#cccccc"
-      }
-    },
-    "axisTick": {
-      "show": false,
-      "lineStyle": {
-        "color": "#333333"
-      }
-    },
-    "axisLabel": {
-      "show": true,
-      "color": "#999999"
-    },
-    "splitLine": {
-      "show": true,
-      "lineStyle": {
-        "color": ["#eeeeee"]
-      }
-    },
-    "splitArea": {
-      "show": false,
-      "areaStyle": {
-        "color": ["rgba(250,250,250,0.3)", "rgba(200,200,200,0.3)"]
-      }
-    }
-  },
-  "toolbox": {
-    "iconStyle": {
-      "borderColor": "#aaaaaa"
-    },
-    "emphasis": {
-      "iconStyle": {
-        "borderColor": "#666"
-      }
-    }
-  },
-  "legend": {
-    "textStyle": {
-      "color": "#999999"
-    }
-  },
-  "tooltip": {
-    "axisPointer": {
-      "lineStyle": {
-        "color": "#ccc",
-        "width": 1
-      },
-      "crossStyle": {
-        "color": "#ccc",
-        "width": 1
-      }
-    }
-  },
-  "timeline": {
-    "lineStyle": {
-      "color": "#349e8e",
-      "width": 1
-    },
-    "itemStyle": {
-      "color": "#349e8e",
-      "borderWidth": "1"
-    },
-    "emphasis": {
-      "itemStyle": {
-        "color": "#57e8d2"
-      }
-    },
-    "controlStyle": {
-      "color": "#349e8e",
-      "borderColor": "#349e8e",
-      "borderWidth": "0"
-    },
-    "checkpointStyle": {
-      "color": "#22c3aa",
-      "borderColor": "rgba(34,195,170,0.25)"
-    },
-    "label": {
-      "color": "#349e8e"
-    }
-  },
-  "visualMap": {
-    "color": ["#d0648a", "#22c3aa", "rgba(123,217,165,0.2)"]
-  },
-  "dataZoom": {
-    "backgroundColor": "#fff",
-    "dataBackgroundColor": "#dedede",
-    "fillerColor": "rgba(34,195,170,0.25)",
-    "handleColor": "#dddddd",
-    "handleSize": "100%",
-    "textStyle": {
-      "color": "#999"
-    }
-  },
-  "markPoint": {
-    "label": {
-      "color": "#ffffff",
-      "emphasis": {
-        "textStyle": {
-          "color": "#ffffff"
-        }
-      }
-    }
-  }
-}

+ 3 - 1
src/style/dark.scss

@@ -100,8 +100,10 @@ html.dark {
   }
 
   .el-backtop {
-    --el-backtop-bg-color: var(--el-color-primary-light-9);
+    --el-backtop-bg-color: rgb(72 72 78);
     --el-backtop-hover-bg-color: var(--el-color-primary);
+
+    transition: background-color 0.25s cubic-bezier(0.7, 0.3, 0.1, 1);
   }
 
   .el-dropdown-menu__item:not(.is-disabled):hover {

+ 6 - 17
src/views/pure-table/high/echarts/columns.tsx

@@ -1,13 +1,8 @@
-import {
-  clone,
-  useDark,
-  useECharts,
-  type EchartOptions
-} from "@pureadmin/utils";
+import { ref, computed } from "vue";
 import { tableDataDrag } from "../data";
 import { message } from "@/utils/message";
 import { templateRef } from "@vueuse/core";
-import { ref, type Ref, computed } from "vue";
+import { clone, useDark, useECharts } from "@pureadmin/utils";
 
 export function useColumns() {
   const dataList = ref(clone(tableDataDrag, true).splice(0, 4));
@@ -33,19 +28,13 @@ export function useColumns() {
 
   const { isDark } = useDark();
 
-  const theme: EchartOptions["theme"] = computed(() => {
-    return isDark.value ? "dark" : "light";
-  });
+  const theme = computed(() => (isDark.value ? "dark" : "light"));
 
   dataList.value.forEach((_, i) => {
-    const { setOptions } = useECharts(
-      templateRef(`PieChartRef${i}`) as Ref<HTMLDivElement>,
-      {
-        theme
-      }
-    );
+    const { setOptions } = useECharts(templateRef(`PieChartRef${i}`), {
+      theme
+    });
 
-    // https://pure-admin-utils.netlify.app/hooks/useEcharts/useEcharts.html
     setOptions(
       {
         tooltip: {

+ 0 - 137
src/views/welcome/components/Bar.vue

@@ -1,137 +0,0 @@
-<script setup lang="ts">
-import { ref, computed, watch, type Ref } from "vue";
-import { useAppStoreHook } from "@/store/modules/app";
-import {
-  delay,
-  useDark,
-  useECharts,
-  type EchartOptions
-} from "@pureadmin/utils";
-import * as echarts from "echarts/core";
-
-const { isDark } = useDark();
-
-const theme: EchartOptions["theme"] = computed(() => {
-  return isDark.value ? "dark" : "light";
-});
-
-const barChartRef = ref<HTMLDivElement | null>(null);
-const { setOptions, resize } = useECharts(barChartRef as Ref<HTMLDivElement>, {
-  theme
-});
-
-setOptions(
-  {
-    tooltip: {
-      trigger: "axis",
-      axisPointer: {
-        type: "shadow"
-      }
-    },
-    grid: {
-      bottom: "20px",
-      right: "10px"
-    },
-    legend: {
-      //@ts-expect-error
-      right: true,
-      data: ["watchers", "fork", "star"]
-    },
-    xAxis: [
-      {
-        type: "category",
-        axisTick: {
-          alignWithLabel: true
-        },
-        axisLabel: {
-          interval: 0
-          // width: "70",
-          // overflow: "truncate"
-        },
-        data: ["2021", "2022", "2023"],
-        triggerEvent: true
-      }
-    ],
-    yAxis: [
-      {
-        type: "value",
-        triggerEvent: true
-      }
-    ],
-    series: [
-      {
-        name: "watchers",
-        type: "bar",
-        barWidth: "15%",
-        itemStyle: {
-          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
-            {
-              offset: 0,
-              color: "#e6a23c"
-            },
-            {
-              offset: 1,
-              color: "#eebe77"
-            }
-          ])
-        },
-        data: [200, 320, 800]
-      },
-      {
-        name: "fork",
-        type: "bar",
-        barWidth: "15%",
-        itemStyle: {
-          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
-            {
-              offset: 0,
-              color: "#f56c6c"
-            },
-            {
-              offset: 1,
-              color: "#f89898"
-            }
-          ])
-        },
-        data: [1600, 2460, 4500]
-      },
-      {
-        name: "star",
-        type: "bar",
-        barWidth: "15%",
-        itemStyle: {
-          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
-            {
-              offset: 0,
-              color: "#409EFF"
-            },
-            {
-              offset: 1,
-              color: "#53a7ff"
-            }
-          ])
-        },
-        data: [1450, 3620, 7500]
-      }
-    ],
-    addTooltip: true
-  },
-  {
-    name: "click",
-    callback: params => {
-      console.log("click", params);
-    }
-  }
-);
-
-watch(
-  () => useAppStoreHook().getSidebarStatus,
-  () => {
-    delay(600).then(() => resize());
-  }
-);
-</script>
-
-<template>
-  <div ref="barChartRef" style="width: 100%; height: 35vh" />
-</template>

+ 0 - 30
src/views/welcome/components/Github.vue

@@ -1,30 +0,0 @@
-<script setup lang="ts">
-import { useColumns } from "./columns";
-const { columnsA, columnsB, columnsC } = useColumns();
-
-const list = [
-  {
-    columns: columnsA,
-    column: 3
-  },
-  {
-    columns: columnsB,
-    column: 2
-  },
-  {
-    columns: columnsC,
-    column: 1
-  }
-];
-</script>
-
-<template>
-  <PureDescriptions
-    v-for="(item, index) in list"
-    :key="index"
-    :columns="item.columns"
-    :column="item.column"
-    direction="vertical"
-    border
-  />
-</template>

+ 0 - 187
src/views/welcome/components/Line.vue

@@ -1,187 +0,0 @@
-<script setup lang="ts">
-import { useIntervalFn } from "@vueuse/core";
-import { ref, computed, watch, type Ref } from "vue";
-import { useAppStoreHook } from "@/store/modules/app";
-import {
-  delay,
-  useDark,
-  useECharts,
-  type EchartOptions
-} from "@pureadmin/utils";
-
-const { isDark } = useDark();
-
-const theme: EchartOptions["theme"] = computed(() => {
-  return isDark.value ? "dark" : "default";
-});
-
-const lineChartRef = ref<HTMLDivElement | null>(null);
-const { setOptions, getInstance, resize } = useECharts(
-  lineChartRef as Ref<HTMLDivElement>,
-  { theme }
-);
-
-const xData = (() => {
-  const data: any[] = [];
-  for (let i = 1; i < 31; i++) {
-    data.push(`${i}日`);
-  }
-  return data;
-})();
-
-setOptions(
-  {
-    tooltip: {
-      trigger: "axis",
-      axisPointer: {
-        type: "shadow"
-      }
-    },
-    grid: {
-      bottom: "20px",
-      right: "10px"
-    },
-    legend: {
-      //@ts-expect-error
-      right: true,
-      data: ["fork", "star"]
-    },
-    calculable: true,
-    xAxis: [
-      {
-        triggerEvent: true,
-        type: "category",
-        splitLine: {
-          show: false
-        },
-        axisTick: {
-          show: false
-        },
-        data: xData
-      }
-    ],
-    yAxis: [
-      {
-        triggerEvent: true,
-        type: "value",
-        splitLine: {
-          show: false
-        },
-        axisLine: {
-          show: true
-        }
-      }
-    ],
-    dataZoom: [
-      {
-        type: "slider",
-        show: false,
-        realtime: true,
-        startValue: 0,
-        endValue: 24
-      }
-    ],
-    series: [
-      {
-        name: "fork",
-        type: "line",
-        symbolSize: 10,
-        symbol: "circle",
-        color: "#f56c6c",
-        markPoint: {
-          label: {
-            color: "#fff"
-          },
-          data: [
-            {
-              type: "max",
-              name: "最大值"
-            },
-            {
-              type: "min",
-              name: "最小值"
-            }
-          ]
-        },
-        data: [
-          509, 917, 2455, 2610, 2719, 3033, 3044, 3085, 2708, 2809, 2117, 2000,
-          1455, 1210, 719, 733, 944, 2285, 2208, 3372, 3936, 3693, 2962, 2810,
-          3519, 2455, 2610, 2719, 2484, 2078
-        ]
-      },
-      {
-        name: "star",
-        type: "line",
-        symbolSize: 10,
-        symbol: "circle",
-        color: "#53a7ff",
-        markPoint: {
-          label: {
-            color: "#fff"
-          },
-          data: [
-            {
-              type: "max",
-              name: "最大值"
-            },
-            {
-              type: "min",
-              name: "最小值"
-            }
-          ]
-        },
-        data: [
-          2136, 3693, 2962, 3810, 3519, 3484, 3915, 3823, 3455, 4310, 4019,
-          3433, 3544, 3885, 4208, 3372, 3484, 3915, 3748, 3675, 4009, 4433,
-          3544, 3285, 4208, 3372, 3484, 3915, 3823, 4265, 4298
-        ]
-      }
-    ],
-    addTooltip: true
-  },
-  {
-    name: "click",
-    callback: params => {
-      console.log("click", params);
-    }
-  },
-  {
-    name: "contextmenu",
-    callback: params => {
-      console.log("contextmenu", params);
-    }
-  },
-  // 点击空白处
-  {
-    type: "zrender",
-    name: "click",
-    callback: params => {
-      console.log("点击空白处", params);
-    }
-  }
-);
-
-let a = 1;
-useIntervalFn(() => {
-  if (a == xData.length - 24) {
-    a = 0;
-  }
-  getInstance()!.dispatchAction({
-    type: "dataZoom",
-    startValue: a,
-    endValue: a + 24
-  });
-  a++;
-}, 2000);
-
-watch(
-  () => useAppStoreHook().getSidebarStatus,
-  () => {
-    delay(600).then(() => resize());
-  }
-);
-</script>
-
-<template>
-  <div ref="lineChartRef" style="width: 100%; height: 35vh" />
-</template>

+ 0 - 81
src/views/welcome/components/Pie.vue

@@ -1,81 +0,0 @@
-<script setup lang="ts">
-import { ref, computed, watch, type Ref } from "vue";
-import { useAppStoreHook } from "@/store/modules/app";
-import {
-  delay,
-  useDark,
-  useECharts,
-  type EchartOptions
-} from "@pureadmin/utils";
-
-const { isDark } = useDark();
-
-const theme: EchartOptions["theme"] = computed(() => {
-  return isDark.value ? "dark" : "light";
-});
-
-const pieChartRef = ref<HTMLDivElement | null>(null);
-const { setOptions, resize } = useECharts(pieChartRef as Ref<HTMLDivElement>, {
-  theme
-});
-
-setOptions(
-  {
-    tooltip: {
-      trigger: "item"
-    },
-    legend: {
-      icon: "circle",
-      //@ts-expect-error
-      right: true
-    },
-    series: [
-      {
-        name: "Github信息",
-        type: "pie",
-        top: "20%",
-        radius: "80%",
-        center: ["40%", "50%"],
-        color: ["#e6a23c", "#f56c6c", "#53a7ff"],
-        data: [
-          { value: 400, name: "watchers" },
-          { value: 1600, name: "forks" },
-          { value: 7200, name: "star" }
-        ]
-        // emphasis: {
-        //   itemStyle: {
-        //     shadowBlur: 10,
-        //     shadowOffsetX: 0,
-        //     shadowColor: "rgba(0, 0, 0, 0.5)"
-        //   }
-        // }
-      }
-    ]
-  },
-  {
-    name: "click",
-    callback: params => {
-      console.log("click", params);
-    }
-  },
-  // 点击空白处
-  {
-    type: "zrender",
-    name: "click",
-    callback: params => {
-      console.log("点击空白处", params);
-    }
-  }
-);
-
-watch(
-  () => useAppStoreHook().getSidebarStatus,
-  () => {
-    delay(600).then(() => resize());
-  }
-);
-</script>
-
-<template>
-  <div ref="pieChartRef" style="width: 100%; height: 35vh" />
-</template>

+ 112 - 0
src/views/welcome/components/chart/bar.vue

@@ -0,0 +1,112 @@
+<script setup lang="ts">
+import { useDark, useECharts } from "@pureadmin/utils";
+import { type PropType, ref, computed, watch, nextTick } from "vue";
+
+const props = defineProps({
+  requireData: {
+    type: Array as PropType<Array<number>>,
+    default: () => []
+  },
+  questionData: {
+    type: Array as PropType<Array<number>>,
+    default: () => []
+  }
+});
+
+const { isDark } = useDark();
+
+const theme = computed(() => (isDark.value ? "dark" : "light"));
+
+const chartRef = ref();
+const { setOptions, resize } = useECharts(chartRef, {
+  theme
+});
+
+watch(
+  () => props,
+  async () => {
+    await nextTick(); // 确保DOM更新完成后再执行
+    setOptions({
+      resize: false,
+      color: ["#41b6ff", "#e85f33"],
+      tooltip: {
+        trigger: "axis",
+        axisPointer: {
+          type: "none"
+        }
+      },
+      grid: {
+        top: "20px",
+        left: "50px",
+        right: 0
+      },
+      legend: {
+        data: ["需求人数", "提问数量"],
+        textStyle: {
+          color: "#606266",
+          fontSize: "0.875rem"
+        },
+        bottom: 0
+      },
+      xAxis: [
+        {
+          type: "category",
+          data: ["周一", "周二", "周三", "周四", "周五", "周六", "周日"],
+          axisLabel: {
+            fontSize: "0.875rem"
+          },
+          axisPointer: {
+            type: "shadow"
+          }
+        }
+      ],
+      yAxis: [
+        {
+          type: "value",
+          axisLabel: {
+            fontSize: "0.875rem"
+          },
+          splitLine: {
+            show: false // 去网格线
+          }
+          // name: "单位: 个"
+        }
+      ],
+      series: [
+        {
+          name: "需求人数",
+          type: "bar",
+          barWidth: 10,
+          itemStyle: {
+            color: "#41b6ff",
+            borderRadius: [10, 10, 0, 0]
+          },
+          data: props.requireData
+        },
+        {
+          name: "提问数量",
+          type: "bar",
+          barWidth: 10,
+          itemStyle: {
+            color: "#e86033ce",
+            borderRadius: [10, 10, 0, 0]
+          },
+          data: props.questionData
+        }
+      ]
+    });
+  },
+  {
+    deep: true,
+    immediate: true
+  }
+);
+
+defineExpose({
+  resize
+});
+</script>
+
+<template>
+  <div ref="chartRef" style="width: 100%; height: 365px" />
+</template>

+ 3 - 0
src/views/welcome/components/chart/index.ts

@@ -0,0 +1,3 @@
+export { default as barChart } from "./bar.vue";
+export { default as lineChart } from "./line.vue";
+export { default as roundChart } from "./round.vue";

+ 61 - 0
src/views/welcome/components/chart/line.vue

@@ -0,0 +1,61 @@
+<script setup lang="ts">
+import { type PropType, ref, computed } from "vue";
+import { useDark, useECharts } from "@pureadmin/utils";
+
+const props = defineProps({
+  data: {
+    type: Array as PropType<Array<number>>,
+    default: () => []
+  },
+  color: {
+    type: String,
+    default: "#41b6ff"
+  }
+});
+
+const { isDark } = useDark();
+
+const theme = computed(() => (isDark.value ? "dark" : "light"));
+
+const chartRef = ref();
+const { setOptions } = useECharts(chartRef, {
+  theme,
+  renderer: "svg"
+});
+
+setOptions({
+  xAxis: {
+    type: "category",
+    show: false,
+    data: props.data
+  },
+  grid: {
+    top: "15px",
+    bottom: 0,
+    left: 0,
+    right: 0
+  },
+  yAxis: {
+    show: false,
+    type: "value"
+  },
+  series: [
+    {
+      data: props.data,
+      type: "line",
+      symbol: "none",
+      smooth: true,
+      color: props.color,
+      lineStyle: {
+        shadowOffsetY: 3,
+        shadowBlur: 7,
+        shadowColor: props.color
+      }
+    }
+  ]
+});
+</script>
+
+<template>
+  <div ref="chartRef" style="width: 100%; height: 60px" />
+</template>

+ 72 - 0
src/views/welcome/components/chart/round.vue

@@ -0,0 +1,72 @@
+<script setup lang="ts">
+import { ref, computed } from "vue";
+import { useDark, useECharts } from "@pureadmin/utils";
+
+const { isDark } = useDark();
+
+const theme = computed(() => (isDark.value ? "dark" : "light"));
+
+const chartRef = ref();
+const { setOptions } = useECharts(chartRef, {
+  theme,
+  renderer: "svg"
+});
+
+setOptions({
+  title: {
+    text: "100%",
+    left: "47%",
+    top: "30%",
+    textAlign: "center",
+    textStyle: {
+      fontSize: "16",
+      fontWeight: 600
+    }
+  },
+  polar: {
+    radius: ["100%", "90%"],
+    center: ["50%", "50%"]
+  },
+  angleAxis: {
+    max: 100,
+    show: false
+  },
+  radiusAxis: {
+    type: "category",
+    show: true,
+    axisLabel: {
+      show: false
+    },
+    axisLine: {
+      show: false
+    },
+    axisTick: {
+      show: false
+    }
+  },
+  series: [
+    {
+      type: "bar",
+      roundCap: true,
+      barWidth: 2,
+      showBackground: true,
+      backgroundStyle: {
+        color: "#dfe7ef"
+      },
+      data: [100],
+      coordinateSystem: "polar",
+      color: "#7846e5",
+      itemStyle: {
+        shadowBlur: 2,
+        shadowColor: "#7846e5",
+        shadowOffsetX: 0,
+        shadowOffsetY: 0
+      }
+    }
+  ]
+});
+</script>
+
+<template>
+  <div ref="chartRef" style="width: 100%; height: 60px" />
+</template>

+ 0 - 113
src/views/welcome/components/columns.tsx

@@ -1,113 +0,0 @@
-import TypeIt from "@/components/ReTypeit";
-import OfficeBuilding from "@iconify-icons/ep/office-building";
-import Tickets from "@iconify-icons/ep/tickets";
-import Location from "@iconify-icons/ep/location";
-import Iphone from "@iconify-icons/ep/iphone";
-import Notebook from "@iconify-icons/ep/notebook";
-import User from "@iconify-icons/ri/user-3-fill";
-
-export function useColumns() {
-  const lists = [
-    { type: "", label: "善良" },
-    { type: "success", label: "好学" },
-    { type: "info", label: "幽默" },
-    { type: "danger", label: "旅游" },
-    { type: "warning", label: "追剧" }
-  ];
-
-  const columnsA = [
-    {
-      labelRenderer: () => (
-        <div class="flex items-center">
-          <el-icon>
-            <iconify-icon-offline icon={User} />
-          </el-icon>
-          用户名
-        </div>
-      ),
-      value: "乐于分享的程序员小铭"
-    },
-    {
-      labelRenderer: () => (
-        <div class="flex items-center">
-          <el-icon>
-            <iconify-icon-offline icon={Iphone} />
-          </el-icon>
-          手机号
-        </div>
-      ),
-      value: "123456789"
-    },
-    {
-      labelRenderer: () => (
-        <div class="flex items-center">
-          <el-icon>
-            <iconify-icon-offline icon={Location} />
-          </el-icon>
-          居住地
-        </div>
-      ),
-      value: "中国"
-    }
-  ];
-
-  const columnsB = [
-    {
-      labelRenderer: () => (
-        <div class="flex items-center">
-          <el-icon>
-            <iconify-icon-offline icon={Tickets} />
-          </el-icon>
-          标签
-        </div>
-      ),
-      cellRenderer: () => {
-        return lists.map(v => {
-          return (
-            <el-tag class="mr-[10px]" type={v.type} size="small" effect="dark">
-              {v.label}
-            </el-tag>
-          );
-        });
-      }
-    },
-    {
-      labelRenderer: () => (
-        <div class="flex items-center">
-          <el-icon>
-            <iconify-icon-offline icon={OfficeBuilding} />
-          </el-icon>
-          联系地址
-        </div>
-      ),
-      value: "中华人民共和国"
-    }
-  ];
-
-  const columnsC = [
-    {
-      labelRenderer: () => (
-        <div class="flex items-center">
-          <el-icon>
-            <iconify-icon-offline icon={Notebook} />
-          </el-icon>
-          个性签名
-        </div>
-      ),
-      cellRenderer: () => (
-        <TypeIt
-          className={"github"}
-          values={["办法总比困难多"]}
-          cursor={false}
-          speed={100}
-        />
-      )
-    }
-  ];
-
-  return {
-    columnsA,
-    columnsB,
-    columnsC
-  };
-}

+ 104 - 0
src/views/welcome/components/table/columns.tsx

@@ -0,0 +1,104 @@
+import { tableData } from "../../data";
+import { delay } from "@pureadmin/utils";
+import { ref, onMounted, reactive } from "vue";
+import type { PaginationProps } from "@pureadmin/table";
+import ThumbUp from "@iconify-icons/ri/thumb-up-line";
+import Hearts from "@iconify-icons/ri/hearts-line";
+import Empty from "./empty.svg?component";
+
+export function useColumns() {
+  const dataList = ref([]);
+  const loading = ref(true);
+  const columns: TableColumnList = [
+    {
+      sortable: true,
+      label: "序号",
+      prop: "id"
+    },
+    {
+      sortable: true,
+      label: "需求人数",
+      prop: "requiredNumber",
+      filterMultiple: false,
+      // filterClassName: "pure-table-filter", // TODO:https://github.com/element-plus/element-plus/pull/15389
+      filters: [
+        { text: "≥16000", value: "more" },
+        { text: "<16000", value: "less" }
+      ],
+      filterMethod: (value, { requiredNumber }) => {
+        return value === "more"
+          ? requiredNumber >= 16000
+          : requiredNumber < 16000;
+      }
+    },
+    {
+      sortable: true,
+      label: "提问数量",
+      prop: "questionNumber"
+    },
+    {
+      sortable: true,
+      label: "解决数量",
+      prop: "resolveNumber"
+    },
+    {
+      sortable: true,
+      label: "用户满意度",
+      minWidth: 100,
+      prop: "satisfaction",
+      cellRenderer: ({ row }) => (
+        <div class="flex justify-center w-full">
+          <span class="flex items-center w-[60px]">
+            <span class="ml-auto mr-2">{row.satisfaction}%</span>
+            <iconifyIconOffline
+              icon={row.satisfaction > 98 ? Hearts : ThumbUp}
+              color="#e85f33"
+            />
+          </span>
+        </div>
+      )
+    },
+    {
+      sortable: true,
+      label: "统计日期",
+      prop: "date"
+    },
+    {
+      label: "操作",
+      fixed: "right",
+      slot: "operation"
+    }
+  ];
+
+  /** 分页配置 */
+  const pagination = reactive<PaginationProps>({
+    pageSize: 10,
+    currentPage: 1,
+    layout: "prev, pager, next",
+    total: 0,
+    align: "center"
+  });
+
+  function onCurrentChange(page: number) {
+    console.log("onCurrentChange", page);
+    loading.value = true;
+    delay(300).then(() => {
+      loading.value = false;
+    });
+  }
+
+  onMounted(() => {
+    dataList.value = tableData;
+    pagination.total = dataList.value.length;
+    loading.value = false;
+  });
+
+  return {
+    Empty,
+    loading,
+    columns,
+    dataList,
+    pagination,
+    onCurrentChange
+  };
+}

+ 1 - 0
src/views/welcome/components/table/empty.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" class="empty-icon" viewBox="0 0 1024 1024"><path d="M855.6 427.2H168.5c-12.7 0-24.4 6.9-30.6 18L4.4 684.7C1.5 689.9 0 695.8 0 701.8v287.1c0 19.4 15.7 35.1 35.1 35.1H989c19.4 0 35.1-15.7 35.1-35.1V701.8c0-6-1.5-11.8-4.4-17.1L886.2 445.2c-6.2-11.1-17.9-18-30.6-18M673.4 695.6c-16.5 0-30.8 11.5-34.3 27.7-12.7 58.5-64.8 102.3-127.2 102.3s-114.5-43.8-127.2-102.3c-3.5-16.1-17.8-27.7-34.3-27.7H119c-26.4 0-43.3-28-31.1-51.4l81.7-155.8c6.1-11.6 18-18.8 31.1-18.8h622.4c13 0 25 7.2 31.1 18.8l81.7 155.8c12.2 23.4-4.7 51.4-31.1 51.4zm146.5-486.1c-1-1.8-2.1-3.7-3.2-5.5-9.8-16.6-31.1-22.2-47.8-12.6L648.5 261c-17 9.8-22.7 31.6-12.6 48.4.9 1.4 1.7 2.9 2.5 4.4 9.5 17 31.2 22.8 48 13L807 257.3c16.7-9.7 22.4-31 12.9-47.8m-444.5 51.6L255 191.6c-16.7-9.6-38-4-47.8 12.6-1.1 1.8-2.1 3.6-3.2 5.5-9.5 16.8-3.8 38.1 12.9 47.8L337.3 327c16.9 9.7 38.6 4 48-13.1.8-1.5 1.7-2.9 2.5-4.4 10.2-16.8 4.5-38.6-12.4-48.4M512 239.3h2.5c19.5.3 35.5-15.5 35.5-35.1v-139c0-19.3-15.6-34.9-34.8-35.1h-6.4C489.6 30.3 474 46 474 65.2v139c0 19.5 15.9 35.4 35.5 35.1z"/></svg>

+ 71 - 0
src/views/welcome/components/table/index.vue

@@ -0,0 +1,71 @@
+<script setup lang="ts">
+import { useColumns } from "./columns";
+import { useRenderIcon } from "@/components/ReIcon/src/hooks";
+
+const { loading, columns, dataList, pagination, Empty, onCurrentChange } =
+  useColumns();
+</script>
+
+<template>
+  <pure-table
+    row-key="id"
+    alignWhole="center"
+    showOverflowTooltip
+    :loading="loading"
+    :loading-config="{ background: 'transparent' }"
+    :data="
+      dataList.slice(
+        (pagination.currentPage - 1) * pagination.pageSize,
+        pagination.currentPage * pagination.pageSize
+      )
+    "
+    :columns="columns"
+    :pagination="pagination"
+    @page-current-change="onCurrentChange"
+  >
+    <template #empty>
+      <el-empty description="暂无数据" :image-size="60">
+        <template #image>
+          <Empty />
+        </template>
+      </el-empty>
+    </template>
+    <template #operation="{ row }">
+      <el-button
+        plain
+        circle
+        size="small"
+        :title="`查看序号为${row.id}的详情`"
+        :icon="useRenderIcon('search')"
+      />
+    </template>
+  </pure-table>
+</template>
+
+<!-- <style lang="scss">
+.pure-table-filter {
+  .el-table-filter__list {
+    min-width: 80px;
+    padding: 0;
+
+    li {
+      line-height: 28px;
+    }
+  }
+}
+</style> -->
+
+<style lang="scss" scoped>
+:deep(.el-table) {
+  --el-table-border: none;
+  --el-table-border-color: transparent;
+
+  .el-empty__description {
+    margin: 0;
+  }
+
+  .el-scrollbar__bar {
+    display: none;
+  }
+}
+</style>

+ 134 - 0
src/views/welcome/data.ts

@@ -0,0 +1,134 @@
+import { dayjs, cloneDeep, getRandomIntBetween } from "./utils";
+import GroupLine from "@iconify-icons/ri/group-line";
+import Question from "@iconify-icons/ri/question-answer-line";
+import CheckLine from "@iconify-icons/ri/chat-check-line";
+import Smile from "@iconify-icons/ri/star-smile-line";
+
+const days = ["周日", "周一", "周二", "周三", "周四", "周五", "周六"];
+
+/** 需求人数、提问数量、解决数量、用户满意度 */
+const chartData = [
+  {
+    icon: GroupLine,
+    bgColor: "#effaff",
+    color: "#41b6ff",
+    duration: 2200,
+    name: "需求人数",
+    value: 36000,
+    percent: "+88%",
+    data: [2101, 5288, 4239, 4962, 6752, 5208, 7450] // 平滑折线图数据
+  },
+  {
+    icon: Question,
+    bgColor: "#fff5f4",
+    color: "#e85f33",
+    duration: 1600,
+    name: "提问数量",
+    value: 16580,
+    percent: "+70%",
+    data: [2216, 1148, 1255, 788, 4821, 1973, 4379]
+  },
+  {
+    icon: CheckLine,
+    bgColor: "#eff8f4",
+    color: "#26ce83",
+    duration: 1500,
+    name: "解决数量",
+    value: 16499,
+    percent: "+99%",
+    data: [861, 1002, 3195, 1715, 3666, 2415, 3645]
+  },
+  {
+    icon: Smile,
+    bgColor: "#f6f4fe",
+    color: "#7846e5",
+    duration: 100,
+    name: "用户满意度",
+    value: 100,
+    percent: "+100%",
+    data: [100]
+  }
+];
+
+/** 分析概览 */
+const barChartData = [
+  {
+    requireData: [2101, 5288, 4239, 4962, 6752, 5208, 7450],
+    questionData: [2216, 1148, 1255, 1788, 4821, 1973, 4379]
+  },
+  {
+    requireData: [2101, 3280, 4400, 4962, 5752, 6889, 7600],
+    questionData: [2116, 3148, 3255, 3788, 4821, 4970, 5390]
+  }
+];
+
+/** 解决概率 */
+const progressData = [
+  {
+    week: "周一",
+    percentage: 85,
+    duration: 110,
+    color: "#41b6ff"
+  },
+  {
+    week: "周二",
+    percentage: 86,
+    duration: 105,
+    color: "#41b6ff"
+  },
+  {
+    week: "周三",
+    percentage: 88,
+    duration: 100,
+    color: "#41b6ff"
+  },
+  {
+    week: "周四",
+    percentage: 89,
+    duration: 95,
+    color: "#41b6ff"
+  },
+  {
+    week: "周五",
+    percentage: 94,
+    duration: 90,
+    color: "#26ce83"
+  },
+  {
+    week: "周六",
+    percentage: 96,
+    duration: 85,
+    color: "#26ce83"
+  },
+  {
+    week: "周日",
+    percentage: 100,
+    duration: 80,
+    color: "#26ce83"
+  }
+].reverse();
+
+/** 数据统计 */
+const tableData = Array.from({ length: 30 }).map((_, index) => {
+  return {
+    id: index + 1,
+    requiredNumber: getRandomIntBetween(13500, 19999),
+    questionNumber: getRandomIntBetween(12600, 16999),
+    resolveNumber: getRandomIntBetween(13500, 17999),
+    satisfaction: getRandomIntBetween(95, 100),
+    date: dayjs().subtract(index, "day").format("YYYY-MM-DD")
+  };
+});
+
+/** 最新动态 */
+const latestNewsData = cloneDeep(tableData)
+  .slice(0, 14)
+  .map((item, index) => {
+    return Object.assign(item, {
+      date: `${dayjs().subtract(index, "day").format("YYYY-MM-DD")} ${
+        days[dayjs().subtract(index, "day").day()]
+      }`
+    });
+  });
+
+export { chartData, barChartData, progressData, tableData, latestNewsData };

+ 182 - 194
src/views/welcome/index.vue

@@ -1,60 +1,50 @@
 <script setup lang="ts">
-import dayjs from "dayjs";
-import MdEditor from "md-editor-v3";
-import Bar from "./components/Bar.vue";
-import Pie from "./components/Pie.vue";
-import Line from "./components/Line.vue";
-import { getReleases } from "@/api/list";
-import TypeIt from "@/components/ReTypeit";
-import { useWindowSize } from "@vueuse/core";
-import { ref, computed, markRaw } from "vue";
-import Github from "./components/Github.vue";
-import { randomColor } from "@pureadmin/utils";
+import { ref, markRaw } from "vue";
+import ReCol from "@/components/ReCol";
+import PureTable from "./components/table/index.vue";
+import { ReNormalCountTo } from "@/components/ReCountTo";
 import { useRenderFlicker } from "@/components/ReFlicker";
+import { barChart, lineChart, roundChart } from "./components/chart";
+import Segmented, { type OptionsType } from "@/components/ReSegmented";
+import { useResizeObserver, useDark, debounce, randomGradient } from "./utils";
+import { chartData, barChartData, progressData, latestNewsData } from "./data";
 
 defineOptions({
   name: "Welcome"
 });
 
-const list = ref();
-const loading = ref<boolean>(true);
-const { version } = __APP_INFO__.pkg;
-const titleClass = computed(() => {
-  return ["text-base", "font-medium"];
-});
-
-const { height } = useWindowSize();
+const barCardRef = ref();
+const barChartRef = ref();
+const { isDark } = useDark();
 
-setTimeout(() => {
-  loading.value = !loading.value;
-}, 800);
+let curWeek = ref(1); // 0上周、1本周
+const optionsBasis: Array<OptionsType> = [
+  {
+    label: "上周"
+  },
+  {
+    label: "本周"
+  }
+];
 
-getReleases().then(({ data }) => {
-  list.value = data.list.map(v => {
-    return {
-      content: v.body,
-      timestamp: dayjs(v.published_at).format("YYYY/MM/DD hh:mm:ss A"),
-      icon: markRaw(
-        useRenderFlicker({
-          background: randomColor({ type: "hex" }) as string
-        })
-      )
-    };
-  });
-});
+useResizeObserver(
+  barCardRef,
+  debounce(() => barChartRef.value.resize(), 60)
+);
 </script>
 
 <template>
   <div>
-    <el-row :gutter="24">
-      <el-col
+    <el-row :gutter="24" justify="space-around">
+      <re-col
+        v-for="(item, index) in chartData"
+        :key="index"
         v-motion
-        :xs="24"
-        :sm="24"
-        :md="12"
-        :lg="12"
-        :xl="12"
         class="mb-[18px]"
+        :value="6"
+        :md="12"
+        :sm="12"
+        :xs="24"
         :initial="{
           opacity: 0,
           y: 100
@@ -63,56 +53,54 @@ getReleases().then(({ data }) => {
           opacity: 1,
           y: 0,
           transition: {
-            delay: 200
+            delay: 80 * (index + 1)
           }
         }"
       >
-        <el-card
-          shadow="never"
-          :style="{ height: `calc(${height}px - 35vh - 250px)` }"
-        >
-          <template #header>
-            <a
-              :class="titleClass"
-              href="https://github.com/pure-admin/vue-pure-admin/releases"
-              target="_black"
+        <el-card shadow="never">
+          <div class="flex justify-between">
+            <span class="text-md font-medium">
+              {{ item.name }}
+            </span>
+            <div
+              class="w-8 h-8 flex justify-center items-center rounded-md"
+              :style="{
+                backgroundColor: isDark ? 'transparent' : item.bgColor
+              }"
             >
-              <TypeIt
-                :className="'type-it2'"
-                :values="[`PureAdmin 版本日志(当前版本 v${version})`]"
-                :cursor="false"
-                :speed="60"
+              <IconifyIconOffline
+                :icon="item.icon"
+                :color="item.color"
+                width="18"
               />
-            </a>
-          </template>
-          <el-skeleton animated :rows="7" :loading="loading">
-            <template #default>
-              <el-scrollbar :height="`calc(${height}px - 35vh - 340px)`">
-                <el-timeline v-show="list?.length > 0">
-                  <el-timeline-item
-                    v-for="(item, index) in list"
-                    :key="index"
-                    :icon="item.icon"
-                    :timestamp="item.timestamp"
-                  >
-                    <md-editor v-model="item.content" preview-only />
-                  </el-timeline-item>
-                </el-timeline>
-                <el-empty v-show="list?.length === 0" />
-              </el-scrollbar>
-            </template>
-          </el-skeleton>
+            </div>
+          </div>
+          <div class="flex justify-between items-start mt-3">
+            <div class="w-1/2">
+              <ReNormalCountTo
+                :duration="item.duration"
+                :fontSize="'1.6em'"
+                :startVal="100"
+                :endVal="item.value"
+              />
+              <p class="font-medium text-green-500">{{ item.percent }}</p>
+            </div>
+            <lineChart
+              v-if="item.data.length > 1"
+              class="!w-1/2"
+              :color="item.color"
+              :data="item.data"
+            />
+            <roundChart v-else class="!w-1/2" />
+          </div>
         </el-card>
-      </el-col>
+      </re-col>
 
-      <el-col
+      <re-col
         v-motion
-        :xs="24"
-        :sm="24"
-        :md="12"
-        :lg="12"
-        :xl="12"
         class="mb-[18px]"
+        :value="18"
+        :xs="24"
         :initial="{
           opacity: 0,
           y: 100
@@ -121,46 +109,30 @@ getReleases().then(({ data }) => {
           opacity: 1,
           y: 0,
           transition: {
-            delay: 200
+            delay: 400
           }
         }"
       >
-        <el-card
-          shadow="never"
-          :style="{ height: `calc(${height}px - 35vh - 250px)` }"
-        >
-          <template #header>
-            <a
-              :class="titleClass"
-              href="https://github.com/xiaoxian521"
-              target="_black"
-            >
-              <TypeIt
-                :className="'type-it1'"
-                :values="['GitHub信息']"
-                :cursor="false"
-                :speed="120"
-              />
-            </a>
-          </template>
-          <el-skeleton animated :rows="7" :loading="loading">
-            <template #default>
-              <el-scrollbar :height="`calc(${height}px - 35vh - 340px)`">
-                <Github />
-              </el-scrollbar>
-            </template>
-          </el-skeleton>
+        <el-card ref="barCardRef" shadow="never">
+          <div class="flex justify-between">
+            <span class="text-md font-medium">分析概览</span>
+            <Segmented v-model="curWeek" :options="optionsBasis" />
+          </div>
+          <div class="flex justify-between items-start mt-3">
+            <barChart
+              ref="barChartRef"
+              :requireData="barChartData[curWeek].requireData"
+              :questionData="barChartData[curWeek].questionData"
+            />
+          </div>
         </el-card>
-      </el-col>
+      </re-col>
 
-      <el-col
+      <re-col
         v-motion
-        :xs="24"
-        :sm="24"
-        :md="12"
-        :lg="8"
-        :xl="8"
         class="mb-[18px]"
+        :value="6"
+        :xs="24"
         :initial="{
           opacity: 0,
           y: 100
@@ -169,41 +141,45 @@ getReleases().then(({ data }) => {
           opacity: 1,
           y: 0,
           transition: {
-            delay: 400
+            delay: 480
           }
         }"
       >
         <el-card shadow="never">
-          <template #header>
-            <a
-              :class="titleClass"
-              href="https://github.com/pure-admin/vue-pure-admin"
-              target="_black"
-            >
-              <TypeIt
-                :className="'type-it4'"
-                :values="['GitHub折线图信息']"
-                :cursor="false"
-                :speed="120"
-              />
-            </a>
-          </template>
-          <el-skeleton animated :rows="7" :loading="loading">
-            <template #default>
-              <Line />
-            </template>
-          </el-skeleton>
+          <div class="flex justify-between">
+            <span class="text-md font-medium">解决概率</span>
+          </div>
+          <div
+            v-for="(item, index) in progressData"
+            :key="index"
+            :class="[
+              'flex',
+              'justify-between',
+              'items-start',
+              index === 0 ? 'mt-8' : 'mt-[2.15rem]'
+            ]"
+          >
+            <el-progress
+              :text-inside="true"
+              :percentage="item.percentage"
+              :stroke-width="21"
+              :color="item.color"
+              striped
+              striped-flow
+              :duration="item.duration"
+            />
+            <span class="text-nowrap ml-2 text-text_color_regular text-sm">
+              {{ item.week }}
+            </span>
+          </div>
         </el-card>
-      </el-col>
+      </re-col>
 
-      <el-col
+      <re-col
         v-motion
-        :xs="24"
-        :sm="24"
-        :md="12"
-        :lg="8"
-        :xl="8"
         class="mb-[18px]"
+        :value="18"
+        :xs="24"
         :initial="{
           opacity: 0,
           y: 100
@@ -212,41 +188,23 @@ getReleases().then(({ data }) => {
           opacity: 1,
           y: 0,
           transition: {
-            delay: 400
+            delay: 560
           }
         }"
       >
-        <el-card shadow="never">
-          <template #header>
-            <a
-              :class="titleClass"
-              href="https://github.com/pure-admin/vue-pure-admin"
-              target="_black"
-            >
-              <TypeIt
-                :className="'type-it3'"
-                :values="['GitHub饼图信息']"
-                :cursor="false"
-                :speed="120"
-              />
-            </a>
-          </template>
-          <el-skeleton animated :rows="7" :loading="loading">
-            <template #default>
-              <Pie />
-            </template>
-          </el-skeleton>
+        <el-card shadow="never" class="h-[580px]">
+          <div class="flex justify-between">
+            <span class="text-md font-medium">数据统计</span>
+          </div>
+          <PureTable class="mt-3" />
         </el-card>
-      </el-col>
+      </re-col>
 
-      <el-col
+      <re-col
         v-motion
-        :xs="24"
-        :sm="24"
-        :md="24"
-        :lg="8"
-        :xl="8"
         class="mb-[18px]"
+        :value="6"
+        :xs="24"
         :initial="{
           opacity: 0,
           y: 100
@@ -255,39 +213,69 @@ getReleases().then(({ data }) => {
           opacity: 1,
           y: 0,
           transition: {
-            delay: 400
+            delay: 640
           }
         }"
       >
         <el-card shadow="never">
-          <template #header>
-            <a
-              :class="titleClass"
-              href="https://github.com/pure-admin/vue-pure-admin"
-              target="_black"
-            >
-              <TypeIt
-                :className="'type-it5'"
-                :values="['GitHub柱状图信息']"
-                :cursor="false"
-                :speed="120"
-              />
-            </a>
-          </template>
-          <el-skeleton animated :rows="7" :loading="loading">
-            <template #default>
-              <Bar />
-            </template>
-          </el-skeleton>
+          <div class="flex justify-between">
+            <span class="text-md font-medium">最新动态</span>
+          </div>
+          <el-scrollbar max-height="504" class="mt-3">
+            <el-timeline>
+              <el-timeline-item
+                v-for="(item, index) in latestNewsData"
+                :key="index"
+                center
+                placement="top"
+                :icon="
+                  markRaw(
+                    useRenderFlicker({
+                      background: randomGradient({
+                        randomizeHue: true
+                      })
+                    })
+                  )
+                "
+                :timestamp="item.date"
+              >
+                <p class="text-text_color_regular text-sm">
+                  {{
+                    `新增 ${item.requiredNumber} 条问题,${item.resolveNumber} 条已解决`
+                  }}
+                </p>
+              </el-timeline-item>
+            </el-timeline>
+          </el-scrollbar>
         </el-card>
-      </el-col>
+      </re-col>
     </el-row>
   </div>
 </template>
 
 <style lang="scss" scoped>
-:deep(.el-timeline-item) {
-  margin: 6px 0 0 6px;
+:deep(.el-card) {
+  --el-card-border-color: none;
+
+  /* 解决概率进度条宽度 */
+  .el-progress--line {
+    width: 85%;
+  }
+
+  /* 解决概率进度条字体大小 */
+  .el-progress-bar__innerText {
+    font-size: 15px;
+  }
+
+  /* 隐藏 el-scrollbar 滚动条 */
+  .el-scrollbar__bar {
+    display: none;
+  }
+
+  /* el-timeline 每一项上下、左右边距 */
+  .el-timeline-item {
+    margin: 0 6px;
+  }
 }
 
 .main-content {

+ 13 - 0
src/views/welcome/utils.ts

@@ -0,0 +1,13 @@
+export { default as dayjs } from "dayjs";
+export { useResizeObserver } from "@vueuse/core";
+export {
+  useDark,
+  debounce,
+  cloneDeep,
+  randomColor,
+  randomGradient
+} from "@pureadmin/utils";
+
+export function getRandomIntBetween(min: number, max: number) {
+  return Math.floor(Math.random() * (max - min + 1)) + min;
+}

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