当前位置: 首页 > article >正文

TDesign组件-1

工作台

工作台

<!-- 首页 -->
<template>
  <div class="container home-wrapper">
    <!-- 顶部 card  -->
    <top-panel :top-panel-data="topPanelData"/>
    <!-- 经营分析 -->
    <Operate
      @fetchData="fetchData"
      @exportStatistics="exportStatistics"
    ></Operate>
    <!-- end -->
    <!-- 分析 -->
    <div class="analysis">
      <div class="leftCard">
        <div class="title">订单分析</div>
        <div class="cardBox">
          <div class="card">
            <div class="body">
              <div class="title">
                有效订单
                <div class="description">
                  <span class="beizhu"></span>
                  <span class="hover"
                  >在统计时间内,订单状态为已完成的订单数。(订单被取消、退款、关闭状态均不属于有效订单)</span
                  >
                </div>
              </div>
              <div class="num">
                {
  { data?.effectiveOrderNum }}<span></span>
              </div>
            </div>
            <div class="line"></div>
          </div>
          <div class="card">
            <div class="body">
              <div class="title">取消订单数</div>
              <div class="num">
                {
  { data?.cancelOrderNum || 0 }}<span></span>
              </div>
            </div>
            <div class="line"></div>
          </div>
          <div class="card">
            <div class="body">
              <div class="title">关闭订单数</div>
              <div class="num">{
  { data?.closeOrderNum }}<span></span></div>
            </div>
            <div class="line"></div>
          </div>
        </div>
      </div>
      <div class="rightCard">
        <div class="title">营业分析</div>
        <div class="cardBox">
          <div class="card">
            <div class="body">
              <div class="title">
                实付单均价
                <div class="description">
                  <span class="beizhu"></span>
                  <span class="hover"
                  >在统计时间内,平均每单实际支付额(不包含失效订单)</span
                  >
                </div>
              </div>
              <div class="num">
                {
  { data?.realPayAveragePrice }}<span></span>
              </div>
            </div>
            <div class="line"></div>
          </div>
          <div class="card">
            <div class="body">
              <div class="title">
                有效订单总额
                <div class="description">
                  <span class="beizhu"></span>
                  <span class="hover"
                  >在统计时间内,有效订单的总订单交易额</span
                  >
                </div>
              </div>
              <div class="num">
                {
  { data?.effectiveOrderTotalAmount }}<span></span>
              </div>
            </div>
            <div class="line"></div>
          </div>
        </div>
      </div>
    </div>
    <!-- 中部图表  -->
    <middle-chart
      class="row-container"
      :middle-chart-data="data?.ordersTrend"
    />
    <!-- 用户分析 -->
    <div class="operate">
      用户分析<span>{
  { nowMonthTime[0] }} - {
  { nowMonthTime[1] }}</span>
    </div>
    <!-- 用户分析 - 漏斗图和双折线图 -->
    <output-overview
      class="row-container"
      :output-overview-data="outputOverviewData"
    />
  </div>
</template>

<script setup lang="ts">
import {
     onMounted, ref, watch} from 'vue'
import {
     useRoute} from 'vue-router'
import dayjs from 'dayjs'
import {
     getDashBoardData, exportStatisticsData} from '@/api/detail'
import TopPanel from './components/TopPanel.vue'
import MiddleChart from './components/MiddleChart.vue'
import OutputOverview from './components/OutputOverview.vue'
import Operate from './components/operate.vue'

import {
     MessagePlugin} from 'tdesign-vue-next'

const route = useRoute()
// 当月时间1号到最后一天
const nowMonthTime = ref([
  dayjs().startOf('month').format('YYYY-MM-DD'),
  dayjs().subtract(1, 'day').format('YYYY-MM-DD')
])
const data = ref() // 日期
const dateRange = ref([
  dayjs().subtract(1, 'month').format('YYYY-MM-DD') + ' 00:00:00',

  dayjs().subtract(0, 'day').format('YYYY-MM-DD') + ' 23:59:59'
])

const topPanelData = ref({
     
  evaluationCount: 0,
  yesterdayEvaluationCount: 0,
  replyCount: 0,
  goodLevelRate: '',
  yesterdayReplyCount: 0,
  auditPendingCount: 0
}) // 顶部card数据
const outputOverviewData = ref([]) // 综合评分详情概览数据

// 获取tab栏切换数据

// 生命周期
onMounted(() => {
     
  fetchData(dateRange.value)
})
// 刷新
watch(
  () => route.query,
  () => {
     
    fetchData(dateRange.value)
  }
)
// 获取数据
const fetchData = async (val) => {
     
  if (val[0].length <= 10) {
     
    val[0] = val[0] + ' 00:00:00'
    val[1] = val[1] + ' 23:59:59'
  }
  await getDashBoardData({
     
    maxTime: val[1],
    minTime: val[0]
  })
    .then((res) => {
     
      if (res.code === 200) {
     
        data.value = res.data
      } else {
     
        MessagePlugin.error(res.msg)
      }
    })
    .catch((err) => {
     
      console.log(err)
    })
}
// 导出统计数据
const exportStatistics = async (val) => {
     
  if (val[0].length <= 10) {
     
    val[0] = val[0] + ' 00:00:00'
    val[1] = val[1] + ' 23:59:59'
  }
  await exportStatisticsData({
     
    maxTime: val[1],
    minTime: val[0]
  }).then((res) => {
     
    let text = decodeURIComponent(res.headers['content-disposition'])
    const parts = text.split("utf-8''")
    const blob = new Blob([res.data], {
     
      type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
    })
    const objectUrl = URL.createObjectURL(blob)
    const link = document.createElement('a')
    link.href = objectUrl
    link.download = parts[1] // 设置文件名
    link.click()
    URL.revokeObjectURL(objectUrl)
  })
}

</script>

<style lang="less" scoped src="./index.less"></style>

总结

1. 模板部分 (<template>)
页面结构
  • 顶部卡片 (top-panel):展示顶部面板数据。
  • 经营分析 (Operate):包含操作按钮,如获取数据和导出统计数据。
  • 订单分析
    • 左侧卡片 (leftCard):展示有效订单数、取消订单数和关闭订单数。
    • 右侧卡片 (rightCard):展示实付单均价和有效订单总额。
  • 中部图表 (middle-chart):展示订单趋势图。
  • 用户分析:展示用户分析的时间范围。
  • 漏斗图和双折线图 (output-overview):展示综合评分详情概览数据。
数据绑定
  • 使用了 v-bindv-on 指令来传递数据和事件处理函数。
  • 动态绑定数据到模板中的元素,例如 { { data?.effectiveOrderNum }}
2. 脚本部分 (<script setup lang="ts">)
导入依赖
import {
     onMounted, ref, watch } from 'vue'
import {
     useRoute } from 'vue-router'
import dayjs from 'dayjs'
import {
     getDashBoardData, exportStatisticsData } from '@/api/detail'
import TopPanel from './components/TopPanel.vue'
import MiddleChart from './components/MiddleChart.vue'
import OutputOverview from './components/OutputOverview.vue'
import Operate from './components/operate.vue'
import {
     MessagePlugin } from 'tdesign-vue-next'
定义响应式数据
  • nowMonthTime: 当月时间范围(从月初到最后一天)。
  • data: 存储从 API 获取的数据。
  • dateRange: 默认日期范围(上个月初到今天)。
  • topPanelData: 顶部卡片的数据。
  • outputOverviewData: 综合评分详情概览数据。
生命周期钩子
  • onMounted: 页面加载时调用 fetchData 方法获取初始数据。
  • watch: 监听路由查询参数的变化,当变化时重新获取数据。
方法
  • fetchData:
    • 根据传入的日期范围格式化时间字符串。
    • 调用 getDashBoardData API 获取数据并更新 data 值。
    • 处理成功和失败的响应。
  • exportStatistics:
    • 格式化日期字符串。
    • 调用 exportStatisticsData API 导出统计数据。
    • 处理下载文件的逻辑,包括解码文件名和创建下载链接。
3. 样式部分 (<style lang="less" scoped src="./index.less"></style>)
  • 使用 Less 作为样式预处理器,并且样式是作用域限定的(scoped),确保样式只应用于当前组件。
  • 样式文件路径为 ./index.less

订单数趋势图表

订单数趋势图表

  • <t-row> <t-col><t-row> 是行容器,<t-col> 是列容器。
  • :gutter="16" 设置了列之间的间距为 16px,:xs="12":xl="12" 表示在超小屏幕(xs)和特大屏幕(xl)上都占用12个栅格宽度(总共有24个栅格)
  • <t-card>:卡片组件,用于包裹内容并提供标题和边框。title="订单数趋势" 设置了卡片的标题,class="dashboard-chart-card" 添加了自定义样式类。
  • id="monitorContainer"ref="monitorContainer":给 div 分配了一个 ID
  • :style:使用内联样式动态设置了宽度和高度。宽度被设置为 99.6%,而高度则是根据 resizeTime 计算得出的,
  • dashboard-chart-container:这是一个自定义的CSS类,用于设置图表容器的样式。

使用:

import MiddleChart from './components/MiddleChart.vue'

<middle-chart
   class="row-container"
   :middle-chart-data="data?.ordersTrend"
 />

component组件:

<!-- 订单数趋势图表 -->
<template>
  <t-row :gutter="16" class="row-container">
    <t-col :xs="12" :xl="12">
      <t-card title="订单数趋势" class="dashboard-chart-card">
        <div
          id="monitorContainer"
          ref="monitorContainer"
          class="dashboard-chart-container"
          :style="{ width: '99.6%', height: `${resizeTime * 326}px` }"
        />
      </t-card>
    </t-col>
  </t-row>
</template>

<script setup lang="ts">
import {
      onMounted, ref, onUnmounted, nextTick, computed, watch } from 'vue'

import * as echarts from 'echarts/core'
import {
     
  TooltipComponent,
  LegendComponent,
  GridComponent
} from 'echarts/components'
import {
      LineChart } from 'echarts/charts'
import {
      CanvasRenderer } from 'echarts/renderers'
import {
      useSettingStore } from '@/store'
import {
      dateTag } from '@/utils/charts'
import {
      getLineChartDataSet } from '../index'

echarts.use([
  TooltipComponent,
  LegendComponent,
  GridComponent,
  LineChart,
  CanvasRenderer
])
// 接收父组件传递的数据
const props = defineProps({
     
  middleChartData: {
     
    type: Array,
    default: () => {
     
      return []
    }
  }
})
// 深拷贝
const dataTime = JSON.parse(JSON.stringify(dateTag))
const store = useSettingStore()
// 触发父组件方法
const emit = defineEmits(['changeLineData'])
// 大小变化比例
const resizeTime = ref(1)
// 颜色
const chartColors = computed(() => store.chartColors)

// monitorChart
let monitorContainer: HTMLElement // 监控图表容器
let monitorChart: echarts.ECharts // 监控图表实例


// 获取图表实例,初始化图表
const renderMonitorChart = () => {
     
  if (!monitorContainer) {
     
    monitorContainer = document.getElementById('monitorContainer')
  }
  monitorChart = echarts.init(monitorContainer)
  monitorChart.setOption(
    getLineChartDataSet({
      dateTime: dataTime[0].time, ...chartColors.value })
  )
}

// chartSize update
const updateContainer = () => {
     
  if (
    document.documentElement.clientWidth >= 1400 &&
    document.documentElement.clientWidth < 1920
  ) {
     
    resizeTime.value = Number(
      (document.documentElement.clientWidth / 2280).toFixed(2)
    )
  } else if (document.documentElement.clientWidth < 1080) {
     
    resizeTime.value = Number(
      (document.documentElement.clientWidth / 1080).toFixed(2)
    )
  } else {
     
    resizeTime.value = 1
  }
  monitorChart.resize({
     
    width: monitorContainer.clientWidth,
    height: resizeTime.value * 326
  })
}
// 传入的数据变化是重新渲染图表
watch(
  () => props.middleChartData,
  () => {
     
    const count = (props.middleChartData as {
      count: number }[]).map((item) => item.count)
    const countDate = (props.middleChartData as {
      dateTime: string }[]).map((item) => item.dateTime)
    onCurrencyChange(count , countDate)
  }
)
onMounted(() => {
     
  const count = (props.middleChartData as {
      count: number }[]).map((item) => item.count)
  const countDate = (props.middleChartData as {
      dateTime: string }[]).map((item) => item.dateTime)
  // 初始化图表
  renderMonitorChart()
  onCurrencyChange(count , countDate)
  nextTick(() => {
     
    // 初始化图表大小
    updateContainer()
  })
  // 初始化时间
  // dateRange.value = dateTag[0].time
  // 监听窗口大小变化
  window.addEventListener('resize', updateContainer, false)
})
watch(
  () => store.isSidebarCompact,
  () => {
     
    if (store.isSidebarCompact) {
     
      monitorChart.resize({
     
        width: monitorContainer.clientWidth + 160,
        height: resizeTime.value * 326
      })
    } else {
     
      monitorChart.resize({
     
        width: monitorContainer.clientWidth - 160,
        height: resizeTime.value * 326
      })
    }
  }
)
onUnmounted(() => {
     
  // 销毁图表
  window.removeEventListener('resize', updateContainer)
})

// 内容变化
const onCurrencyChange = (count: string[] | number[], countDate: string[]) => {
     
  monitorChart.setOption(
    getLineChartDataSet({
      count, countDate, ...chartColors.value })
  )
}

</script>

<style lang="less" scoped>
.dashboard-chart-card {
     
  padding: var(--td-comp-paddingTB-l) var(--td-comp-paddingLR-xl);
  padding-top: 0;
  padding-right: 15px;

  :deep(.t-card__header) {
     
    padding-bottom: 24px;
    padding-left: 0;
  }

  :deep(.t-card__title) {
     
    font-size: 20px;
    font-weight: 500;
    margin-right: 0;
  }
}
.rightHead {
     
  display: flex;
  align-items: center;
  .active {
     
    background-color: var(--color-main) !important;
    color: var(--color-white) !important;
  }
  .card {
     
    margin-right: 20px;
    font-size: 14px;
    padding: 5px 10px;
    background-color: var(--color-bk7);
    border-radius: 5px;
    white-space: nowrap;
    color: var(--color-bk3);
    cursor: pointer;
    &:hover {
     
      background-color: var(--color-main);
      color: var(--color-white);
    }
  }
}
:deep(.t-card__body) {
     
  padding: 0;
}
:deep(.t-card__subtitle) {
     
  font-size: 16px;
}
</style>

经营分析(日期范围+导出按钮)

经营分析

  • <div class="operate">经营分析</div>:一个简单的 div 元素,用来显示“经营分析”文本。这个元素可能被样式化以用作标题或标签。
  • <div class="statistics"><div class="export">:这些是用于布局的容器元素,可能包含了与统计数据展示和导出相关的UI组件。
  • <t-date-range-picker>:这是一个日期范围选择器组件,可能是来自 TDesign 或其他 UI 框架。此组件允许用户选择一个日期范围,并且包含以下属性:
    • ref="datePicker":为组件创建引用,可以在 JavaScript 中通过 this.$refs.datePicker 访问。
    • v-model="dateRange":双向绑定到名为 dateRange 的数据属性,表示所选的日期范围。
    • class="card-date-picker-container":添加了自定义样式类。
    • theme="primary":设置了组件的主题颜色,通常是框架提供的预设主题之一。
    • mode="date":设置选择器模式为日期(相对于时间或其他模式)。
    • allow-input:允许用户直接在输入框中键入日期。
    • :disable-date="{ after: endTime }":禁用了某些日期的选择,这里禁用了所有晚于 endTime 的日期。
    • clearable:允许用户清除已选中的日期。
    • @change="onCurrencyChange":当选择的日期变化时触发 onCurrencyChange 方法。
  • <div class="bt" @click="exportStatistics">导出明细</div>:这是一个点击按钮,用户点击它可以触发 exportStatistics 方法,从而导出统计详情。@click 是 Vue.js 的事件绑定语法,用于监听用户的点击事件。

综上所述,这段代码描述了一个具有日期范围选择功能的用户界面,以及一个可以导出统计信息的按钮。为了使该组件完全功能化,还需要相应的 JavaScript 部分来定义 dateRangeendTime 数据属性,以及 onCurrencyChangeexportStatistics 方法。

使用:

import Operate from './components/operate.vue'

<Operate
      @fetchData="fetchData"
      @exportStatistics="exportStatistics"
></Operate>

组件component:

<!-- 经营分析(时间范围+导出) -->
<template>
  <div class="operate">经营分析</div>
  <div class="statistics">
<!--    <div class="tips">
      <span></span
      >此数据统计的订单为统计时间内,已结束订单15个自然日之后的数据(已结束订单包含已取消、已关闭、已完成订单)
    </div>-->
    <div class="export">
      <t-date-range-picker
        ref="datePicker"
        v-model="dateRange"
        class="card-date-picker-container"
        theme="primary"
        mode="date"
        allow-input
        :disable-date="{
          after: endTime
        }"
        clearable
        @change="onCurrencyChange"
      />
      <div class="bt" @click="exportStatistics">导出明细</div>
    </div>
  </div>
</template>

<script setup lang="ts">
import {
      ref } from 'vue'
import {
      MessagePlugin } from 'tdesign-vue-next'
import dayjs from 'dayjs'

// 表格内时间
const dateRange = ref([
  dayjs().subtract(1, 'month').format('YYYY-MM-DD') + ' 00:00:00',
  dayjs().subtract(0, 'day').format('YYYY-MM-DD') + ' 23:59:59'
])
// 时间选择器最大时间
const endTime = ref(dayjs().subtract(0, 'day').format('YYYY-MM-DD'))

const emit = defineEmits(['fetchData', 'exportStatistics'])

// 切换时间
const onCurrencyChange = (val) => {
     
  if (dayjs(val[1]).diff(dayjs(val[0]), 'day') > 365) {
     
    // dateRange.value = []
    MessagePlugin.error('时间间隔不能大于365天')
  } else {
     
    emit('fetchData', dateRange.value)
  }
}
// 触发导出数据
const exportStatistics = () => {
     
  emit('exportStatistics', dateRange.value)
}
</script>
<style lang="less" scoped src=

http://www.kler.cn/a/471849.html

相关文章:

  • SQL概述
  • python对redis的增删查改
  • 分享:osgb倾斜数据转cesium-3dtiles 小工具.
  • MP4 与Fragmented MP4 (fMP4)的区别
  • 【python】matplotlib(radar chart)
  • 基于 GEE Sentinel-1 数据集提取水体
  • Bash语言的编程范式
  • api接口对体育直播的重要性
  • spark——DAG专题
  • 万界星空科技电机行业MES系统解决方案
  • C++头文件map
  • 电子图纸怎么保障安全?
  • Linux的proc目录与什么有关?【以及它里面的文件各自记录着什么信息】
  • 单片机控制
  • shell基础使用及vim的常用快捷键
  • 来说数据库
  • 【AI-21】深度学习框架中的神经网络
  • “挑战杯”大学生创业计划大赛(小挑)赛事介绍
  • Excel 技巧03 - 如何对齐小数位数? (★)如何去掉小数点?如何不四舍五入去掉小数点?
  • leetcode LCR 127. 跳跃训练
  • 一键完成!!网页打包成桌面应用
  • 【微服务】2、网关
  • 明源地产ERP VisitorWeb_XMLHTTP.aspx Sql注入漏洞复现(附脚本)
  • 英伟达 RTX 5090 显卡赋能医疗大模型:变革、挑战与展望
  • MybatisPlus分页配置开发环境有效,测试环境生产环境不生效,日志中的sql没有分页参数,直接查询
  • IDEA中Maven依赖包导入失败报红的潜在原因