Kaynağa Gözat

feat: 增加看板

gemercheung 2 yıl önce
ebeveyn
işleme
3377169fa7

+ 1 - 1
.vscode/settings.json

@@ -96,7 +96,7 @@
     "editor.codeActionsOnSave": {
       "source.fixAll.eslint": false
     },
-    "editor.defaultFormatter": "octref.vetur"
+    "editor.defaultFormatter": "esbenp.prettier-vscode"
   },
   "i18n-ally.localesPaths": ["src/locales/lang"],
   "i18n-ally.keystyle": "nested",

+ 78 - 69
src/router/routes/index.ts

@@ -1,69 +1,78 @@
-import type { AppRouteRecordRaw, AppRouteModule } from '/@/router/types';
-
-import { PAGE_NOT_FOUND_ROUTE, REDIRECT_ROUTE } from '/@/router/routes/basic';
-
-import { mainOutRoutes } from './mainOut';
-import { PageEnum } from '/@/enums/pageEnum';
-import { t } from '/@/hooks/web/useI18n';
-import { LAYOUT } from '/@/router/constant';
-const modules = import.meta.globEager('./modules/**/*.ts');
-
-const routeModuleList: AppRouteModule[] = [];
-
-Object.keys(modules).forEach((key) => {
-  const mod = modules[key].default || {};
-  const modList = Array.isArray(mod) ? [...mod] : [mod];
-  routeModuleList.push(...modList);
-});
-
-export const asyncRoutes = [PAGE_NOT_FOUND_ROUTE, ...routeModuleList];
-
-export const RootRoute: AppRouteRecordRaw = {
-  path: '/',
-  name: 'Root',
-  redirect: PageEnum.BASE_HOME,
-  meta: {
-    title: 'Root',
-  },
-};
-
-export const LoginRoute: AppRouteRecordRaw = {
-  path: '/login',
-  name: 'Login',
-  component: () => import('/@/views/sys/login/Login.vue'),
-  meta: {
-    title: t('routes.basic.login'),
-  },
-};
-
-export const WelcomeRoute: AppRouteRecordRaw = {
-  path: '/welcome',
-  name: 'Welcome',
-  component: LAYOUT,
-  redirect: '/welcome/index',
-  // component: () => import('/@/views/welcome/index.vue'),
-  meta: {
-    title: t('routes.basic.welcome'),
-  },
-  children: [
-    {
-      path: 'index',
-      name: 'welcome',
-      component: () => import('/@/views/welcome/index.vue'),
-      meta: {
-        title: t('routes.basic.welcomeLogin'),
-        hideMenu: true,
-      },
-    },
-  ],
-};
-
-// Basic routing without permission
-export const basicRoutes = [
-  LoginRoute,
-  WelcomeRoute,
-  RootRoute,
-  ...mainOutRoutes,
-  REDIRECT_ROUTE,
-  PAGE_NOT_FOUND_ROUTE,
-];
+import type { AppRouteRecordRaw, AppRouteModule } from '/@/router/types';
+
+import { PAGE_NOT_FOUND_ROUTE, REDIRECT_ROUTE } from '/@/router/routes/basic';
+
+import { mainOutRoutes } from './mainOut';
+import { PageEnum } from '/@/enums/pageEnum';
+import { t } from '/@/hooks/web/useI18n';
+import { LAYOUT } from '/@/router/constant';
+const modules = import.meta.globEager('./modules/**/*.ts');
+
+const routeModuleList: AppRouteModule[] = [];
+
+Object.keys(modules).forEach((key) => {
+  const mod = modules[key].default || {};
+  const modList = Array.isArray(mod) ? [...mod] : [mod];
+  routeModuleList.push(...modList);
+});
+
+export const asyncRoutes = [PAGE_NOT_FOUND_ROUTE, ...routeModuleList];
+
+export const RootRoute: AppRouteRecordRaw = {
+  path: '/',
+  name: 'Root',
+  redirect: PageEnum.BASE_HOME,
+  meta: {
+    title: 'Root',
+  },
+};
+
+export const LoginRoute: AppRouteRecordRaw = {
+  path: '/login',
+  name: 'Login',
+  component: () => import('/@/views/sys/login/Login.vue'),
+  meta: {
+    title: t('routes.basic.login'),
+  },
+};
+
+export const WelcomeRoute: AppRouteRecordRaw = {
+  path: '/welcome',
+  name: 'Welcome',
+  component: LAYOUT,
+  redirect: '/welcome/index',
+  // component: () => import('/@/views/welcome/index.vue'),
+  meta: {
+    title: t('routes.basic.welcome'),
+  },
+  children: [
+    {
+      path: 'index',
+      name: 'welcome',
+      component: () => import('/@/views/welcome/index.vue'),
+      meta: {
+        title: t('routes.basic.welcomeLogin'),
+        hideMenu: true,
+      },
+    },
+    {
+      path: 'analysis',
+      name: 'analysis',
+      component: () => import('/@/views/dashboard/analysis/index.vue'),
+      meta: {
+        title: t('routes.dashboard.analysis'),
+        hideMenu: true,
+      },
+    },
+  ],
+};
+
+// Basic routing without permission
+export const basicRoutes = [
+  LoginRoute,
+  WelcomeRoute,
+  RootRoute,
+  ...mainOutRoutes,
+  REDIRECT_ROUTE,
+  PAGE_NOT_FOUND_ROUTE,
+];

+ 95 - 89
src/views/dashboard/analysis/components/VisitAnalysis.vue

@@ -1,89 +1,95 @@
-<template>
-  <div ref="chartRef" :style="{ height, width }"></div>
-</template>
-<script lang="ts">
-  import { basicProps } from './props';
-</script>
-<script lang="ts" setup>
-  import { onMounted, ref, Ref } from 'vue';
-  import { useECharts } from '/@/hooks/web/useECharts';
-
-  defineProps({
-    ...basicProps,
-  });
-  const chartRef = ref<HTMLDivElement | null>(null);
-  const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
-
-  onMounted(() => {
-    setOptions({
-      tooltip: {
-        trigger: 'axis',
-        axisPointer: {
-          lineStyle: {
-            width: 1,
-            color: '#019680',
-          },
-        },
-      },
-      xAxis: {
-        type: 'category',
-        boundaryGap: false,
-        data: [...new Array(18)].map((_item, index) => `${index + 6}:00`),
-        splitLine: {
-          show: true,
-          lineStyle: {
-            width: 1,
-            type: 'solid',
-            color: 'rgba(226,226,226,0.5)',
-          },
-        },
-        axisTick: {
-          show: false,
-        },
-      },
-      yAxis: [
-        {
-          type: 'value',
-          max: 80000,
-          splitNumber: 4,
-          axisTick: {
-            show: false,
-          },
-          splitArea: {
-            show: true,
-            areaStyle: {
-              color: ['rgba(255,255,255,0.2)', 'rgba(226,226,226,0.2)'],
-            },
-          },
-        },
-      ],
-      grid: { left: '1%', right: '1%', top: '2  %', bottom: 0, containLabel: true },
-      series: [
-        {
-          smooth: true,
-          data: [
-            111, 222, 4000, 18000, 33333, 55555, 66666, 33333, 14000, 36000, 66666, 44444, 22222,
-            11111, 4000, 2000, 500, 333, 222, 111,
-          ],
-          type: 'line',
-          areaStyle: {},
-          itemStyle: {
-            color: '#5ab1ef',
-          },
-        },
-        {
-          smooth: true,
-          data: [
-            33, 66, 88, 333, 3333, 5000, 18000, 3000, 1200, 13000, 22000, 11000, 2221, 1201, 390,
-            198, 60, 30, 22, 11,
-          ],
-          type: 'line',
-          areaStyle: {},
-          itemStyle: {
-            color: '#019680',
-          },
-        },
-      ],
-    });
-  });
-</script>
+<template>
+  <div ref="chartRef" :style="{ height, width }"></div>
+</template>
+<script lang="ts">
+  import { basicProps } from './props';
+</script>
+<script lang="ts" setup>
+  import { onMounted, ref, Ref } from 'vue';
+  import { useECharts } from '/@/hooks/web/useECharts';
+
+  defineProps({
+    ...basicProps,
+  });
+  const chartRef = ref<HTMLDivElement | null>(null);
+  const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
+
+  onMounted(() => {
+    setOptions({
+      tooltip: {
+        trigger: 'axis',
+        axisPointer: {
+          lineStyle: {
+            width: 1,
+            color: '#019680',
+          },
+        },
+      },
+      legend: {
+        x: 'center',
+        y: 'bottom',
+      },
+      xAxis: {
+        type: 'category',
+        boundaryGap: false,
+        data: [...new Array(18)].map((_item, index) => `${index + 6}:00`),
+        splitLine: {
+          show: true,
+          lineStyle: {
+            width: 1,
+            type: 'solid',
+            color: 'rgba(226,226,226,0.5)',
+          },
+        },
+        axisTick: {
+          show: false,
+        },
+      },
+      yAxis: [
+        {
+          type: 'value',
+          max: 80000,
+          splitNumber: 4,
+          axisTick: {
+            show: false,
+          },
+          splitArea: {
+            show: true,
+            areaStyle: {
+              color: ['rgba(255,255,255,0.2)', 'rgba(226,226,226,0.2)'],
+            },
+          },
+        },
+      ],
+      grid: { left: '1%', right: '1%', top: '2%', bottom: '10%', containLabel: true },
+      series: [
+        {
+          smooth: true,
+          data: [
+            111, 222, 4000, 18000, 33333, 55555, 66666, 33333, 14000, 36000, 66666, 44444, 22222,
+            11111, 4000, 2000, 500, 333, 222, 111,
+          ],
+          type: 'line',
+          areaStyle: {},
+          name: '留言总人数',
+          itemStyle: {
+            color: '#5ab1ef',
+          },
+        },
+        {
+          smooth: true,
+          data: [
+            33, 66, 88, 333, 3333, 5000, 18000, 3000, 1200, 13000, 22000, 11000, 2221, 1201, 390,
+            198, 60, 30, 22, 11,
+          ],
+          type: 'line',
+          name: '留言总数',
+          areaStyle: {},
+          itemStyle: {
+            color: '#019680',
+          },
+        },
+      ],
+    });
+  });
+</script>

+ 59 - 47
src/views/dashboard/analysis/components/VisitAnalysisBar.vue

@@ -1,47 +1,59 @@
-<template>
-  <div ref="chartRef" :style="{ height, width }"></div>
-</template>
-<script lang="ts">
-  import { basicProps } from './props';
-</script>
-<script lang="ts" setup>
-  import { onMounted, ref, Ref } from 'vue';
-  import { useECharts } from '/@/hooks/web/useECharts';
-
-  defineProps({
-    ...basicProps,
-  });
-
-  const chartRef = ref<HTMLDivElement | null>(null);
-  const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
-  onMounted(() => {
-    setOptions({
-      tooltip: {
-        trigger: 'axis',
-        axisPointer: {
-          lineStyle: {
-            width: 1,
-            color: '#019680',
-          },
-        },
-      },
-      grid: { left: '1%', right: '1%', top: '2  %', bottom: 0, containLabel: true },
-      xAxis: {
-        type: 'category',
-        data: [...new Array(12)].map((_item, index) => `${index + 1}月`),
-      },
-      yAxis: {
-        type: 'value',
-        max: 8000,
-        splitNumber: 4,
-      },
-      series: [
-        {
-          data: [3000, 2000, 3333, 5000, 3200, 4200, 3200, 2100, 3000, 5100, 6000, 3200, 4800],
-          type: 'bar',
-          barMaxWidth: 80,
-        },
-      ],
-    });
-  });
-</script>
+<template>
+  <div ref="chartRef" :style="{ height, width }"></div>
+</template>
+<script lang="ts">
+  import { basicProps } from './props';
+</script>
+<script lang="ts" setup>
+  import { onMounted, ref, Ref } from 'vue';
+  import { useECharts } from '/@/hooks/web/useECharts';
+  defineProps({
+    ...basicProps,
+  });
+
+  const chartRef = ref<HTMLDivElement | null>(null);
+  const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
+  onMounted(() => {
+    setOptions({
+      tooltip: {
+        trigger: 'axis',
+        axisPointer: {
+          lineStyle: {
+            width: 1,
+            color: '#019680',
+          },
+        },
+      },
+      legend: {
+        x: 'center',
+        y: 'bottom',
+      },
+      grid: { left: '1%', right: '1%', top: '2%', bottom: '10%', containLabel: true },
+      xAxis: {
+        type: 'category',
+        data: [...new Array(30)].map((_item, index) => `${index + 1}日`),
+      },
+      yAxis: {
+        type: 'value',
+        max: 8000,
+        splitNumber: 4,
+      },
+      series: [
+        {
+          data: [3000, 2000, 3333, 5000, 3200, 4200, 3200, 2100, 3000, 5100, 6000, 3200, 4800],
+          type: 'bar',
+          itemStyle: { normal: { color: '#38a0ff' } },
+          barMaxWidth: 80,
+          name: '用户浏览量',
+        },
+        {
+          data: [100, 2000, 3333, 5000, 3200, 4200, 3200, 2100, 3000, 5100, 6000, 3200, 4800],
+          type: 'bar',
+          itemStyle: { normal: { color: '#4cca73' } },
+          barMaxWidth: 80,
+          name: '用户分享数',
+        },
+      ],
+    });
+  });
+</script>

+ 126 - 25
src/views/dashboard/analysis/index.vue

@@ -1,25 +1,126 @@
-<template>
-  <div class="p-4">
-    <GrowCard :loading="loading" class="enter-y" />
-    <SiteAnalysis class="!my-4 enter-y" :loading="loading" />
-    <div class="md:flex enter-y">
-      <VisitRadar class="md:w-1/3 w-full" :loading="loading" />
-      <VisitSource class="md:w-1/3 !md:mx-4 !md:my-0 !my-4 w-full" :loading="loading" />
-      <SalesProductPie class="md:w-1/3 w-full" :loading="loading" />
-    </div>
-  </div>
-</template>
-<script lang="ts" setup>
-  import { ref } from 'vue';
-  import GrowCard from './components/GrowCard.vue';
-  import SiteAnalysis from './components/SiteAnalysis.vue';
-  import VisitSource from './components/VisitSource.vue';
-  import VisitRadar from './components/VisitRadar.vue';
-  import SalesProductPie from './components/SalesProductPie.vue';
-
-  const loading = ref(true);
-
-  setTimeout(() => {
-    loading.value = false;
-  }, 1500);
-</script>
+<template>
+  <div class="p-4">
+    <!-- <GrowCard :loading="loading" class="enter-y" /> -->
+    <BasicTable @register="registerTable">
+      <template #headerTop>
+        <div class="md:flex enter-y">
+          <div class="md:w-1/2 enter-y">
+            <Card class="w-full">
+              <VisitAnalysisBar :loading="loading" />
+            </Card>
+          </div>
+          <div class="!md:mx-2"></div>
+          <Card class="md:w-1/2 enter-y">
+            <!-- <VisitAnalysis :loading="loading" /> -->
+            <VisitAnalysis :loading="loading" />
+          </Card>
+        </div>
+      </template>
+      <template #toolbar>
+        <!-- <a-button type="primary" @click="openAddModal">{{
+          t('routes.devices.addCamera')
+        }}</a-button> -->
+      </template>
+    </BasicTable>
+    <!-- <div class="md:flex enter-y">
+      <div class="md:w-1/2 enter-y">
+        <Card class="w-full">
+          <VisitAnalysisBar :loading="loading" />
+        </Card>
+      </div>
+      <div class="!md:mx-2"></div>
+      <Card class="md:w-1/2 enter-y">
+
+        <VisitAnalysis :loading="loading" />
+      </Card>
+    </div>
+    <div class="md:flex enter-y mt-4">
+      <Card class="md:w-1/2 md:w-full"> </Card>
+    </div> -->
+  </div>
+</template>
+<script lang="ts" setup>
+  import { ref } from 'vue';
+  import { BasicTable, useTable, BasicColumn, FormProps } from '/@/components/Table';
+  // import { useI18n } from '/@/hooks/web/useI18n';
+  // import GrowCard from './components/GrowCard.vue';
+  import { Card } from 'ant-design-vue';
+  import VisitAnalysis from './components/VisitAnalysis.vue';
+  import VisitAnalysisBar from './components/VisitAnalysisBar.vue';
+
+  const loading = ref(true);
+  // const { t } = useI18n();
+
+  const columns: BasicColumn[] = [
+    {
+      title: '微信昵称',
+      dataIndex: 'key1',
+      width: 120,
+    },
+    {
+      title: '手机号',
+      dataIndex: 'key2',
+      width: 120,
+    },
+    {
+      title: '房间名称',
+      dataIndex: 'key3',
+      width: 120,
+    },
+    {
+      title: '在线时长(min)',
+      dataIndex: 'key4',
+      width: 120,
+    },
+    {
+      title: '留言条数',
+      dataIndex: 'key5',
+      width: 120,
+    },
+    {
+      title: '留言内容',
+      dataIndex: 'key6',
+      width: 320,
+    },
+  ];
+  const searchForm: Partial<FormProps> = {
+    labelWidth: 100,
+    schemas: [
+      {
+        field: 'name',
+        label: '房间名称',
+        component: 'Input',
+        colProps: {
+          xl: 5,
+          xxl: 5,
+        },
+        componentProps: {
+          maxLength: 100,
+        },
+      },
+      {
+        field: 'time',
+        label: '时间段',
+        component: 'RangePicker',
+        colProps: {
+          xl: 5,
+          xxl: 5,
+        },
+        componentProps: {
+          maxLength: 100,
+        },
+      },
+    ],
+  };
+
+  const [registerTable] = useTable({
+    title: '房间留言状态',
+    columns: columns,
+    useSearchForm: true,
+    formConfig: searchForm,
+  });
+
+  setTimeout(() => {
+    loading.value = false;
+  }, 1500);
+</script>