mirror of
				https://github.com/yangjian102621/geekai.git
				synced 2025-11-04 08:13:43 +08:00 
			
		
		
		
	feat(ui):看板图表 样式调整
This commit is contained in:
		@@ -14,6 +14,7 @@
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "@arco-design/web-vue": "^2.54.6",
 | 
			
		||||
    "@gpt-vue/packages": "workspace:^1.0.0",
 | 
			
		||||
    "echarts": "^5.5.0",
 | 
			
		||||
    "md-editor-v3": "^2.2.1",
 | 
			
		||||
    "pinia": "^2.1.7",
 | 
			
		||||
    "vue": "^3.4.15",
 | 
			
		||||
 
 | 
			
		||||
@@ -80,8 +80,8 @@ const optionsEvent = {
 | 
			
		||||
          </slot>
 | 
			
		||||
        </AFormItem>
 | 
			
		||||
      </AGridItem>
 | 
			
		||||
      <AGridItem suffix>
 | 
			
		||||
        <ASpace class="flex-end">
 | 
			
		||||
      <AGridItem>
 | 
			
		||||
        <ASpace>
 | 
			
		||||
          <slot name="search-options" :option="optionsEvent">
 | 
			
		||||
            <AButton type="primary" html-type="submit" :size="size" :loading="submitting">
 | 
			
		||||
              <icon-search />
 | 
			
		||||
@@ -92,6 +92,10 @@ const optionsEvent = {
 | 
			
		||||
              <span>重置</span>
 | 
			
		||||
            </AButton>
 | 
			
		||||
          </slot>
 | 
			
		||||
        </ASpace>
 | 
			
		||||
      </AGridItem>
 | 
			
		||||
      <AGridItem suffix>
 | 
			
		||||
        <ASpace class="flex-end">
 | 
			
		||||
          <slot name="search-extra" />
 | 
			
		||||
        </ASpace>
 | 
			
		||||
      </AGridItem>
 | 
			
		||||
@@ -100,7 +104,7 @@ const optionsEvent = {
 | 
			
		||||
</template>
 | 
			
		||||
<style scoped>
 | 
			
		||||
.search-form-conteiner {
 | 
			
		||||
  padding: 16px 0;
 | 
			
		||||
  padding: 8px 0px 0px;
 | 
			
		||||
}
 | 
			
		||||
.flex-end {
 | 
			
		||||
  display: flex;
 | 
			
		||||
 
 | 
			
		||||
@@ -33,12 +33,12 @@ const handleSearch = async (tips?: boolean) => {
 | 
			
		||||
onActivated(handleSearch);
 | 
			
		||||
</script>
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="simple-header">
 | 
			
		||||
    <a-space>
 | 
			
		||||
      <slot name="header" v-bind="{ reload: handleSearch }" />
 | 
			
		||||
    </a-space>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="simple-table">
 | 
			
		||||
    <div class="simple-header">
 | 
			
		||||
      <a-space class="flex-end">
 | 
			
		||||
        <slot name="header" v-bind="{ reload: handleSearch }" />
 | 
			
		||||
      </a-space>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div ref="tableContainerRef" class="simple-table-container">
 | 
			
		||||
      <ATable
 | 
			
		||||
        v-bind="{
 | 
			
		||||
@@ -46,7 +46,7 @@ onActivated(handleSearch);
 | 
			
		||||
          ...tableConfig,
 | 
			
		||||
          ...props,
 | 
			
		||||
          scroll: useTableScroll(_columns || [], tableContainerRef as HTMLElement),
 | 
			
		||||
          columns: _columns
 | 
			
		||||
          columns: _columns,
 | 
			
		||||
        }"
 | 
			
		||||
      >
 | 
			
		||||
        <template v-for="slot in Object.keys($slots)" #[slot]="config">
 | 
			
		||||
@@ -71,6 +71,10 @@ onActivated(handleSearch);
 | 
			
		||||
  justify-content: space-between;
 | 
			
		||||
}
 | 
			
		||||
.simple-header {
 | 
			
		||||
  padding: 16px 0;
 | 
			
		||||
  padding: 8px 0px 16px;
 | 
			
		||||
}
 | 
			
		||||
.flex-end {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  justify-content: end;
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
 
 | 
			
		||||
@@ -96,7 +96,9 @@ const handleStatusChange = ({ filed, value, record, reload }) => {
 | 
			
		||||
      </a-popconfirm>
 | 
			
		||||
    </template>
 | 
			
		||||
    <template #header="{ reload }">
 | 
			
		||||
      <a-button @click="popup({ reload })" size="small"><icon-plus />新增</a-button>
 | 
			
		||||
      <a-button @click="popup({ reload })" size="small" type="primary"
 | 
			
		||||
        ><template #icon> <icon-plus /> </template>新增
 | 
			
		||||
      </a-button>
 | 
			
		||||
    </template>
 | 
			
		||||
    <template #status="{ record, reload }">
 | 
			
		||||
      <a-switch
 | 
			
		||||
 
 | 
			
		||||
@@ -96,7 +96,9 @@ const handleStatusChange = ({ filed, value, record, reload }) => {
 | 
			
		||||
      </a-popconfirm>
 | 
			
		||||
    </template>
 | 
			
		||||
    <template #header="{ reload }">
 | 
			
		||||
      <a-button @click="popup({ reload })" size="small"><icon-plus />新增</a-button>
 | 
			
		||||
      <a-button @click="popup({ reload })" size="small" type="primary"
 | 
			
		||||
        ><template #icon> <icon-plus /> </template>新增</a-button
 | 
			
		||||
      >
 | 
			
		||||
    </template>
 | 
			
		||||
    <template #status="{ record, reload }">
 | 
			
		||||
      <a-switch
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,11 @@
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import http from "@/http/config";
 | 
			
		||||
import { ref } from "vue";
 | 
			
		||||
 | 
			
		||||
const dataSet = {
 | 
			
		||||
  users: "今日新增用户",
 | 
			
		||||
  chats: "今日新增对话",
 | 
			
		||||
  tokens: "今日消耗 Tokens",
 | 
			
		||||
  income: "今日入账",
 | 
			
		||||
};
 | 
			
		||||
import { ref, nextTick } from "vue";
 | 
			
		||||
import * as echarts from "echarts/core";
 | 
			
		||||
import { GridComponent, TitleComponent } from "echarts/components";
 | 
			
		||||
import { LineChart } from "echarts/charts";
 | 
			
		||||
import { UniversalTransition } from "echarts/features";
 | 
			
		||||
import { CanvasRenderer } from "echarts/renderers";
 | 
			
		||||
 | 
			
		||||
const icons = {
 | 
			
		||||
  users: "icon-user",
 | 
			
		||||
@@ -15,24 +13,81 @@ const icons = {
 | 
			
		||||
  tokens: "icon-computer",
 | 
			
		||||
  income: "icon-wechatpay",
 | 
			
		||||
};
 | 
			
		||||
const dataSet = {
 | 
			
		||||
  users: "今日新增用户",
 | 
			
		||||
  chats: "今日新增对话",
 | 
			
		||||
  tokens: "今日消耗 Tokens",
 | 
			
		||||
  income: "今日入账",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const data = ref<Record<string, number>>({});
 | 
			
		||||
const getData = () => {
 | 
			
		||||
  http({
 | 
			
		||||
    url: "api/admin/dashboard/stats",
 | 
			
		||||
    method: "get",
 | 
			
		||||
  }).then((res) => {
 | 
			
		||||
    data.value = res.data;
 | 
			
		||||
    handeChartData(res.data.chart);
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
getData();
 | 
			
		||||
// 图表
 | 
			
		||||
const chartTitle = {
 | 
			
		||||
  historyMessage: "对话",
 | 
			
		||||
  orders: "订单",
 | 
			
		||||
  users: "用户数",
 | 
			
		||||
};
 | 
			
		||||
echarts.use([GridComponent, LineChart, CanvasRenderer, UniversalTransition, TitleComponent]);
 | 
			
		||||
const chartDomRefs = [];
 | 
			
		||||
const chartData = ref({});
 | 
			
		||||
const data = ref<Record<string, number>>({});
 | 
			
		||||
const handeChartData = (data) => {
 | 
			
		||||
  const _chartData = {};
 | 
			
		||||
  for (let key in data) {
 | 
			
		||||
    const type = data[key];
 | 
			
		||||
    _chartData[key] = {
 | 
			
		||||
      series: [],
 | 
			
		||||
      xAxis: [],
 | 
			
		||||
    };
 | 
			
		||||
    for (let date in type) {
 | 
			
		||||
      _chartData[key].series.push(type[date]);
 | 
			
		||||
      _chartData[key].xAxis.push(date);
 | 
			
		||||
    }
 | 
			
		||||
    nextTick(() => {
 | 
			
		||||
      const myChart = echarts.init(chartDomRefs.pop());
 | 
			
		||||
      myChart.setOption(createOption(_chartData[key], key));
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
  chartData.value = _chartData;
 | 
			
		||||
};
 | 
			
		||||
const createOption = (data, key) => {
 | 
			
		||||
  const { xAxis, series } = data;
 | 
			
		||||
  return {
 | 
			
		||||
    title: {
 | 
			
		||||
      left: "center",
 | 
			
		||||
      text: chartTitle[key],
 | 
			
		||||
    },
 | 
			
		||||
    xAxis: {
 | 
			
		||||
      type: "category",
 | 
			
		||||
      data: xAxis,
 | 
			
		||||
    },
 | 
			
		||||
    yAxis: {
 | 
			
		||||
      type: "value",
 | 
			
		||||
    },
 | 
			
		||||
    series: [
 | 
			
		||||
      {
 | 
			
		||||
        data: series,
 | 
			
		||||
        type: "line",
 | 
			
		||||
      },
 | 
			
		||||
    ],
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
</script>
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="dashboard">
 | 
			
		||||
    <a-grid :cols="{ xs: 1, sm: 1, md: 2, lg: 3, xl: 4 }" :colGap="12" :rowGap="16" class="grid">
 | 
			
		||||
      <a-grid-item v-for="(value, key) in dataSet" :key="key">
 | 
			
		||||
        <div class="data-card">
 | 
			
		||||
          <span :class="key" class="icon"><icon-user /></span>
 | 
			
		||||
          <span :class="key" class="icon"><component :is="icons[key]" /> </span>
 | 
			
		||||
          <span class="count"
 | 
			
		||||
            ><a-statistic :extra="value" :value="data[key]" :precision="0"
 | 
			
		||||
          /></span>
 | 
			
		||||
@@ -40,6 +95,27 @@ getData();
 | 
			
		||||
      </a-grid-item>
 | 
			
		||||
    </a-grid>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="chart">
 | 
			
		||||
    <a-grid
 | 
			
		||||
      :cols="{ xs: 1, sm: 1, md: 1, lg: 2, xl: 2, xxl: 3 }"
 | 
			
		||||
      :colGap="12"
 | 
			
		||||
      :rowGap="16"
 | 
			
		||||
      class="grid"
 | 
			
		||||
    >
 | 
			
		||||
      <a-grid-item v-for="(value, key, index) in chartData" :key="key">
 | 
			
		||||
        <div
 | 
			
		||||
          :ref="
 | 
			
		||||
            (el) => {
 | 
			
		||||
              chartDomRefs[index] = el;
 | 
			
		||||
            }
 | 
			
		||||
          "
 | 
			
		||||
          class="chartDom"
 | 
			
		||||
        >
 | 
			
		||||
          {{ key }}
 | 
			
		||||
        </div>
 | 
			
		||||
      </a-grid-item>
 | 
			
		||||
    </a-grid>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
<style lang="less" scoped>
 | 
			
		||||
.dashboard {
 | 
			
		||||
@@ -80,7 +156,15 @@ getData();
 | 
			
		||||
      display: flex;
 | 
			
		||||
      align-items: center;
 | 
			
		||||
      justify-content: center;
 | 
			
		||||
      background: #f3f3f3;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
.chart {
 | 
			
		||||
  margin-top: 15px;
 | 
			
		||||
  .chartDom {
 | 
			
		||||
    width: 450px;
 | 
			
		||||
    height: 500px;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
 
 | 
			
		||||
@@ -57,7 +57,7 @@ const handleRemove = async (id, reload) => {
 | 
			
		||||
<template>
 | 
			
		||||
  <SimpleTable :request="getList" :columns="columns" :pagination="false">
 | 
			
		||||
    <template #header="{ reload }">
 | 
			
		||||
      <a-button @click="openFormModal(reload, {})">
 | 
			
		||||
      <a-button type="primary" @click="openFormModal(reload, {})">
 | 
			
		||||
        <template #icon> <icon-plus /> </template>
 | 
			
		||||
        新增
 | 
			
		||||
      </a-button>
 | 
			
		||||
 
 | 
			
		||||
@@ -102,7 +102,9 @@ const handleStatusChange = ({ value, record, reload }) => {
 | 
			
		||||
      </a-popconfirm>
 | 
			
		||||
    </template>
 | 
			
		||||
    <template #header="{ reload }">
 | 
			
		||||
      <a-button @click="popup({ reload })" size="small"><icon-plus />新增</a-button>
 | 
			
		||||
      <a-button @click="popup({ reload })" size="small" type="primary"
 | 
			
		||||
        ><template #icon> <icon-plus /> </template>新增
 | 
			
		||||
      </a-button>
 | 
			
		||||
    </template>
 | 
			
		||||
    <template #status="{ record, reload }">
 | 
			
		||||
      <a-switch
 | 
			
		||||
 
 | 
			
		||||
@@ -105,7 +105,9 @@ const handleStatusChange = ({ value, record, reload }) => {
 | 
			
		||||
      </a-popconfirm>
 | 
			
		||||
    </template>
 | 
			
		||||
    <template #header="{ reload }">
 | 
			
		||||
      <a-button @click="popup({ reload })" size="small"><icon-plus />新增</a-button>
 | 
			
		||||
      <a-button @click="popup({ reload })" size="small" type="primary"
 | 
			
		||||
        ><template #icon> <icon-plus /> </template>新增</a-button
 | 
			
		||||
      >
 | 
			
		||||
    </template>
 | 
			
		||||
    <template #status="{ record, reload }">
 | 
			
		||||
      <a-switch
 | 
			
		||||
 
 | 
			
		||||
@@ -80,9 +80,9 @@ const handleDelete = async ({ id }: { id: string }, reload) => {
 | 
			
		||||
      <a-link @click="password({ record, reload })">重置密码</a-link>
 | 
			
		||||
    </template>
 | 
			
		||||
    <template #search-extra="{ reload }">
 | 
			
		||||
      <a-button @click="editModal({ reload })" status="success" size="small"
 | 
			
		||||
        ><icon-plus />新增用户</a-button
 | 
			
		||||
      >
 | 
			
		||||
      <a-button @click="editModal({ reload })" size="small" type="primary">
 | 
			
		||||
        <template #icon> <icon-plus /> </template>新增
 | 
			
		||||
      </a-button>
 | 
			
		||||
    </template>
 | 
			
		||||
  </SearchTable>
 | 
			
		||||
</template>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user