初始化本地仓库
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
<!-- 分析页页面 -->
|
||||
<template>
|
||||
<div>
|
||||
<ElRow :gutter="20">
|
||||
<ElCol :xl="14" :lg="15" :xs="24">
|
||||
<TodaySales />
|
||||
</ElCol>
|
||||
<ElCol :xl="10" :lg="9" :xs="24">
|
||||
<VisitorInsights />
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
|
||||
<ElRow :gutter="20">
|
||||
<ElCol :xl="10" :lg="10" :xs="24">
|
||||
<TotalRevenue />
|
||||
</ElCol>
|
||||
<ElCol :xl="7" :lg="7" :xs="24">
|
||||
<CustomerSatisfaction />
|
||||
</ElCol>
|
||||
<ElCol :xl="7" :lg="7" :xs="24">
|
||||
<TargetVsReality />
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
|
||||
<ElRow :gutter="20">
|
||||
<ElCol :xl="10" :lg="10" :xs="24">
|
||||
<TopProducts />
|
||||
</ElCol>
|
||||
<ElCol :xl="7" :lg="7" :xs="24">
|
||||
<SalesMappingByCountry />
|
||||
</ElCol>
|
||||
<ElCol :xl="7" :lg="7" :xs="24">
|
||||
<VolumeServiceLevel />
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import TodaySales from './modules/today-sales.vue'
|
||||
import VisitorInsights from './modules/visitor-insights.vue'
|
||||
import TotalRevenue from './modules/total-revenue.vue'
|
||||
import CustomerSatisfaction from './modules/customer-satisfaction.vue'
|
||||
import TargetVsReality from './modules/target-vs-reality.vue'
|
||||
import TopProducts from './modules/top-products.vue'
|
||||
import SalesMappingByCountry from './modules/sales-mapping-by-country.vue'
|
||||
import VolumeServiceLevel from './modules/volume-service-level.vue'
|
||||
|
||||
defineOptions({ name: 'Analysis' })
|
||||
</script>
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<div class="art-card h-100 p-5 mb-5 max-sm:mb-4">
|
||||
<div class="art-card-header">
|
||||
<div class="title">
|
||||
<h4>客户满意度</h4>
|
||||
</div>
|
||||
</div>
|
||||
<ArtLineChart
|
||||
height="calc(100% - 30px)"
|
||||
:data="chartData"
|
||||
:xAxisData="xAxisData"
|
||||
:showLegend="true"
|
||||
:showAxisLabel="true"
|
||||
:showAxisLine="false"
|
||||
:showSplitLine="true"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { LineDataItem } from '@/types/component/chart'
|
||||
|
||||
const AREA_STYLE_CONFIG = {
|
||||
startOpacity: 0.08,
|
||||
endOpacity: 0
|
||||
} as const
|
||||
|
||||
/**
|
||||
* X 轴数据配置
|
||||
* 表示一周的天数(周一到周日)
|
||||
*/
|
||||
const xAxisData = ['1', '2', '3', '4', '5', '6', '7']
|
||||
|
||||
/**
|
||||
* 客户满意度图表数据
|
||||
* 对比上个月和本月的满意度趋势
|
||||
*/
|
||||
const chartData = computed<LineDataItem[]>(() => [
|
||||
{
|
||||
name: '上个月',
|
||||
data: [65, 72, 68, 75, 82, 78, 85],
|
||||
areaStyle: AREA_STYLE_CONFIG
|
||||
},
|
||||
{
|
||||
name: '本月',
|
||||
data: [78, 85, 82, 88, 92, 89, 95],
|
||||
areaStyle: AREA_STYLE_CONFIG
|
||||
}
|
||||
])
|
||||
</script>
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<div class="art-card h-82 p-5 mb-5 max-sm:mb-4">
|
||||
<div class="art-card-header">
|
||||
<div class="title">
|
||||
<h4>全国销售分布</h4>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ArtRingChart
|
||||
:data="[
|
||||
{ value: 30, name: '北京' },
|
||||
{ value: 25, name: '上海' },
|
||||
{ value: 45, name: '广州' }
|
||||
]"
|
||||
:color="['#4C87F3', '#93F1B4', '#8BD8FC']"
|
||||
:radius="['46%', '60%']"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
<template>
|
||||
<div class="art-card h-100 p-5 mb-5 max-sm:mb-4">
|
||||
<div class="art-card-header">
|
||||
<div class="title">
|
||||
<h4>目标与实际</h4>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ArtBarChart
|
||||
class="p-5"
|
||||
height="12rem"
|
||||
:data="revenueData"
|
||||
:xAxisData="weekDays"
|
||||
:showAxisLine="false"
|
||||
barWidth="28%"
|
||||
/>
|
||||
|
||||
<div class="px-5 mt-4">
|
||||
<div v-for="item in totalItems" :key="item.label" class="flex-c mb-5 last:mb-0">
|
||||
<div class="flex-c justify-start w-3/5 text-sm">
|
||||
<div class="w-10 h-10 mr-3 text-lg rounded-md flex-cc" :class="item.iconClass">
|
||||
<ArtSvgIcon :icon="item.icon" />
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col items-start">
|
||||
<span class="text-base text-g-800">{{ item.label }}</span>
|
||||
<span class="mt-1 text-xs text-g-500">{{ item.subLabel }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-lg font-normal" :class="item.valueClass">{{ item.value }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
interface RevenueDataItem {
|
||||
name: string
|
||||
data: number[]
|
||||
}
|
||||
|
||||
/**
|
||||
* 一周的日期标签
|
||||
*/
|
||||
const weekDays = ref(['周一', '周二', '周三', '周四', '周五', '周六', '周日'])
|
||||
|
||||
/**
|
||||
* 目标与实际销售数据
|
||||
* 展示一周内的线上销售情况
|
||||
*/
|
||||
const revenueData = ref<RevenueDataItem[]>([
|
||||
{
|
||||
name: '线上销售',
|
||||
data: [12, 13, 5, 15, 10, 15, 18]
|
||||
}
|
||||
])
|
||||
|
||||
/**
|
||||
* 统计项数据
|
||||
*/
|
||||
const totalItems = [
|
||||
{
|
||||
icon: 'ri:shopping-bag-line',
|
||||
iconClass: 'text-theme bg-theme/12',
|
||||
label: '实际销售额',
|
||||
subLabel: '全球',
|
||||
value: '8,823',
|
||||
valueClass: 'text-theme'
|
||||
},
|
||||
{
|
||||
icon: 'ri:money-dollar-circle-line',
|
||||
iconClass: 'text-theme bg-theme/12',
|
||||
label: '目标销售额',
|
||||
subLabel: '商业',
|
||||
value: '12,122',
|
||||
valueClass: 'text-theme'
|
||||
}
|
||||
]
|
||||
</script>
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
<template>
|
||||
<div class="art-card h-82 p-5 mb-5 overflow-hidden max-lg:h-auto max-sm:mb-4">
|
||||
<div class="art-card-header pr-0">
|
||||
<div class="title">
|
||||
<h4>今日销售</h4>
|
||||
<p>销售总结</p>
|
||||
</div>
|
||||
<div class="flex-cc h-7.5 min-w-17 border border-g-300 rounded-lg text-g-500 c-p">
|
||||
<ArtSvgIcon icon="ri:arrow-up-line" class="text-base mr-1.5" />
|
||||
<span class="text-xs">导出</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-2">
|
||||
<ElRow :gutter="20">
|
||||
<ElCol :span="6" :xs="24" v-for="(item, index) in salesData" :key="index">
|
||||
<div
|
||||
class="flex px-5 flex-col justify-center h-55 border border-g-300/85 rounded-xl max-lg:mb-4 max-sm:flex-row max-sm:justify-between max-sm:items-center max-sm:h-40"
|
||||
>
|
||||
<div class="size-12 rounded-lg flex-cc bg-theme/10">
|
||||
<ArtSvgIcon :icon="item.icon" class="text-xl text-theme" />
|
||||
</div>
|
||||
|
||||
<div class="max-sm:ml-4 mt-3.5 max-sm:mt-0 max-sm:text-end">
|
||||
<ArtCountTo class="text-2xl font-medium" :target="item.value" :duration="1500" />
|
||||
<p class="mt-2 text-base text-g-600 max-sm:mt-1">{{ item.label }}</p>
|
||||
<small class="text-g-500 mt-1 max-sm:mt-0.5">
|
||||
较昨天
|
||||
<span
|
||||
class="font-medium"
|
||||
:class="[item.change.indexOf('+') === -1 ? 'text-danger' : 'text-success']"
|
||||
>{{ item.change }}</span
|
||||
>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
interface SalesDataItem {
|
||||
label: string
|
||||
value: number
|
||||
change: string
|
||||
icon: string
|
||||
class: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 今日销售数据统计
|
||||
* 包含销售额、订单量、产品销量和新客户数等关键指标
|
||||
*/
|
||||
const salesData = ref<SalesDataItem[]>([
|
||||
{
|
||||
label: '总销售额',
|
||||
value: 999,
|
||||
change: '+10%',
|
||||
icon: 'ri:bar-chart-box-ai-line',
|
||||
class: 'bg-theme'
|
||||
},
|
||||
{
|
||||
label: '总订单量',
|
||||
value: 300,
|
||||
change: '+15%',
|
||||
icon: 'ri:bar-chart-grouped-line',
|
||||
class: 'bg-warning'
|
||||
},
|
||||
{
|
||||
label: '产品销售量',
|
||||
value: 56,
|
||||
change: '-5%',
|
||||
icon: 'ri:bar-chart-2-line',
|
||||
class: 'bg-error'
|
||||
},
|
||||
{
|
||||
label: '新客户数',
|
||||
value: 68,
|
||||
change: '+8%',
|
||||
icon: 'ri:user-add-line',
|
||||
class: 'bg-success'
|
||||
}
|
||||
])
|
||||
</script>
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
<template>
|
||||
<div class="art-card h-82 p-5 mb-5 overflow-hidden max-sm:mb-4">
|
||||
<div class="art-card-header">
|
||||
<div class="title">
|
||||
<h4>热门产品</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overflow-auto h-full">
|
||||
<ArtTable
|
||||
:data="products"
|
||||
style="width: 100%"
|
||||
size="large"
|
||||
:border="false"
|
||||
:stripe="false"
|
||||
:header-cell-style="{ background: 'transparent' }"
|
||||
>
|
||||
<ElTableColumn prop="name" label="产品名称" width="200" />
|
||||
<ElTableColumn prop="popularity" label="销量">
|
||||
<template #default="scope">
|
||||
<ElProgress
|
||||
:percentage="scope.row.popularity"
|
||||
:color="getColor(scope.row.popularity)"
|
||||
:stroke-width="5"
|
||||
:show-text="false"
|
||||
/>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn prop="sales" label="销量" width="80">
|
||||
<template #default="scope">
|
||||
<span
|
||||
:style="{
|
||||
color: getColor(scope.row.popularity),
|
||||
backgroundColor: `rgba(${hexToRgb(getColor(scope.row.popularity))}, 0.08)`,
|
||||
border: '1px solid',
|
||||
padding: '3px 6px',
|
||||
borderRadius: '4px',
|
||||
fontSize: '12px'
|
||||
}"
|
||||
>{{ scope.row.sales }}</span
|
||||
>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
</ArtTable>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { hexToRgb } from '@/utils/ui'
|
||||
|
||||
interface Product {
|
||||
name: string
|
||||
popularity: number
|
||||
sales: string
|
||||
}
|
||||
|
||||
const COLOR_THRESHOLDS = {
|
||||
LOW: 25,
|
||||
MEDIUM: 50,
|
||||
HIGH: 75
|
||||
} as const
|
||||
|
||||
const POPULARITY_COLORS = {
|
||||
LOW: '#00E096',
|
||||
MEDIUM: '#0095FF',
|
||||
HIGH: '#884CFF',
|
||||
VERY_HIGH: '#FE8F0E'
|
||||
} as const
|
||||
|
||||
/**
|
||||
* 热门产品列表数据
|
||||
* 包含产品名称、热度和销量信息
|
||||
*/
|
||||
const products = computed<Product[]>(() => [
|
||||
{ name: '智能手机', popularity: 10, sales: '100' },
|
||||
{ name: '笔记本电脑', popularity: 29, sales: '100' },
|
||||
{ name: '平板电脑', popularity: 65, sales: '100' },
|
||||
{ name: '智能手表', popularity: 32, sales: '100' },
|
||||
{ name: '无线耳机', popularity: 78, sales: '100' },
|
||||
{ name: '智能音箱', popularity: 41, sales: '100' }
|
||||
])
|
||||
|
||||
/**
|
||||
* 根据热度百分比获取对应的颜色
|
||||
* @param percentage 热度百分比 (0-100)
|
||||
* @returns 对应的颜色值
|
||||
*/
|
||||
const getColor = (percentage: number): string => {
|
||||
if (percentage < COLOR_THRESHOLDS.LOW) return POPULARITY_COLORS.LOW
|
||||
if (percentage < COLOR_THRESHOLDS.MEDIUM) return POPULARITY_COLORS.MEDIUM
|
||||
if (percentage < COLOR_THRESHOLDS.HIGH) return POPULARITY_COLORS.HIGH
|
||||
return POPULARITY_COLORS.VERY_HIGH
|
||||
}
|
||||
</script>
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<div class="art-card h-100 p-5 mb-5 max-sm:mb-4">
|
||||
<div class="art-card-header">
|
||||
<div class="title">
|
||||
<h4>总收入</h4>
|
||||
</div>
|
||||
</div>
|
||||
<ArtBarChart
|
||||
height="calc(100% - 30px)"
|
||||
:data="revenueData"
|
||||
:xAxisData="weekDays"
|
||||
:showLegend="true"
|
||||
:showAxisLine="false"
|
||||
barWidth="18%"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
interface RevenueDataItem {
|
||||
name: string
|
||||
data: number[]
|
||||
}
|
||||
|
||||
/**
|
||||
* 一周的日期标签
|
||||
*/
|
||||
const weekDays = ref(['周一', '周二', '周三', '周四', '周五', '周六', '周日'])
|
||||
|
||||
/**
|
||||
* 总收入数据
|
||||
* 对比线上和线下销售的一周收入情况
|
||||
*/
|
||||
const revenueData = ref<RevenueDataItem[]>([
|
||||
{
|
||||
name: '线上销售',
|
||||
data: [12, 13, 5, 15, 10, 15, 18]
|
||||
},
|
||||
{
|
||||
name: '线下销售',
|
||||
data: [10, 11, 20, 5, 11, 13, 10]
|
||||
}
|
||||
])
|
||||
</script>
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<div class="art-card h-82 p-5 mb-5 max-sm:mb-4">
|
||||
<div class="art-card-header">
|
||||
<div class="title">
|
||||
<h4>访客洞察</h4>
|
||||
</div>
|
||||
</div>
|
||||
<ArtLineChart
|
||||
height="calc(100% - 30px)"
|
||||
:data="chartData"
|
||||
:xAxisData="xAxisData"
|
||||
:showLegend="true"
|
||||
:showAxisLabel="true"
|
||||
:showAxisLine="false"
|
||||
:showSplitLine="true"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { LineDataItem } from '@/types/component/chart'
|
||||
|
||||
/**
|
||||
* X 轴数据配置
|
||||
* 表示一年的月份(1-12月)
|
||||
*/
|
||||
const xAxisData = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12']
|
||||
|
||||
/**
|
||||
* 访客洞察图表数据
|
||||
* 对比老客户和新客户的全年访问趋势
|
||||
*/
|
||||
const chartData = computed<LineDataItem[]>(() => [
|
||||
{
|
||||
name: '老客户',
|
||||
data: [280, 350, 300, 250, 230, 210, 240, 280, 320, 350, 300, 200]
|
||||
},
|
||||
{
|
||||
name: '新客户',
|
||||
data: [260, 200, 150, 130, 180, 270, 340, 380, 300, 220, 170, 130]
|
||||
}
|
||||
])
|
||||
</script>
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
<template>
|
||||
<div class="art-card h-82 p-5 mb-5 max-sm:mb-4">
|
||||
<div class="art-card-header">
|
||||
<div class="title">
|
||||
<h4>业务量与服务水平</h4>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ArtBarChart
|
||||
class="mt-6"
|
||||
height="14.3rem"
|
||||
:data="volumeServiceData"
|
||||
:xAxisData="serviceCategories"
|
||||
:showLegend="true"
|
||||
:showAxisLine="false"
|
||||
:stack="true"
|
||||
barWidth="22%"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
interface VolumeServiceDataItem {
|
||||
name: string
|
||||
data: number[]
|
||||
stack: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务类别数据
|
||||
* 不同产品的分类标签
|
||||
*/
|
||||
const serviceCategories = ref(['产品A', '产品B', '产品C', '产品D', '产品E'])
|
||||
|
||||
/**
|
||||
* 业务量与服务量数据
|
||||
* 展示各产品的业务量和服务量对比,使用堆叠柱状图展示
|
||||
*/
|
||||
const volumeServiceData = ref<VolumeServiceDataItem[]>([
|
||||
{
|
||||
name: '业务量',
|
||||
data: [20, 25, 30, 35, 40],
|
||||
stack: 'total'
|
||||
},
|
||||
{
|
||||
name: '服务量',
|
||||
data: [30, 35, 40, 45, 50],
|
||||
stack: 'total'
|
||||
}
|
||||
])
|
||||
</script>
|
||||
@@ -0,0 +1,41 @@
|
||||
<!-- 工作台页面 -->
|
||||
<template>
|
||||
<div>
|
||||
<CardList></CardList>
|
||||
|
||||
<ElRow :gutter="20">
|
||||
<ElCol :sm="24" :md="12" :lg="10">
|
||||
<ActiveUser />
|
||||
</ElCol>
|
||||
<ElCol :sm="24" :md="12" :lg="14">
|
||||
<SalesOverview />
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
|
||||
<ElRow :gutter="20">
|
||||
<ElCol :sm="24" :md="24" :lg="12">
|
||||
<NewUser />
|
||||
</ElCol>
|
||||
<ElCol :sm="24" :md="12" :lg="6">
|
||||
<Dynamic />
|
||||
</ElCol>
|
||||
<ElCol :sm="24" :md="12" :lg="6">
|
||||
<TodoList />
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
|
||||
<AboutProject />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import CardList from './modules/card-list.vue'
|
||||
import ActiveUser from './modules/active-user.vue'
|
||||
import SalesOverview from './modules/sales-overview.vue'
|
||||
import NewUser from './modules/new-user.vue'
|
||||
import Dynamic from './modules/dynamic-stats.vue'
|
||||
import TodoList from './modules/todo-list.vue'
|
||||
import AboutProject from './modules/about-project.vue'
|
||||
|
||||
defineOptions({ name: 'Console' })
|
||||
</script>
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<div class="art-card p-5 flex-b mb-5 max-sm:mb-4">
|
||||
<div>
|
||||
<h2 class="text-2xl font-medium">关于项目</h2>
|
||||
<p class="text-g-700 mt-1">{{ systemName }} 是一款兼具设计美学与高效开发的后台系统</p>
|
||||
<p class="text-g-700 mt-1">使用了 Vue3、TypeScript、Vite、Element Plus 等前沿技术</p>
|
||||
|
||||
<div class="flex flex-wrap gap-3.5 max-w-150 mt-9">
|
||||
<div
|
||||
class="w-60 flex-cb h-12.5 px-3.5 border border-g-300 c-p rounded-lg text-sm bg-g-100 duration-300 hover:-translate-y-1 max-sm:w-full"
|
||||
v-for="link in linkList"
|
||||
:key="link.label"
|
||||
@click="goPage(link.url)"
|
||||
>
|
||||
<span class="text-g-700">{{ link.label }}</span>
|
||||
<ArtSvgIcon icon="ri:arrow-right-s-line" class="text-lg text-g-600" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<img class="w-75 max-md:!hidden" src="@imgs/draw/draw1.png" alt="draw1" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import AppConfig from '@/config'
|
||||
import { WEB_LINKS } from '@/utils/constants'
|
||||
|
||||
const systemName = AppConfig.systemInfo.name
|
||||
|
||||
const linkList = [
|
||||
{ label: '项目官网', url: WEB_LINKS.DOCS },
|
||||
{ label: '文档', url: WEB_LINKS.INTRODUCE },
|
||||
{ label: 'Github', url: WEB_LINKS.GITHUB_HOME },
|
||||
{ label: '哔哩哔哩', url: WEB_LINKS.BILIBILI }
|
||||
]
|
||||
|
||||
/**
|
||||
* 在新标签页中打开指定 URL
|
||||
* @param url 要打开的网页地址
|
||||
*/
|
||||
const goPage = (url: string): void => {
|
||||
window.open(url, '_blank', 'noopener,noreferrer')
|
||||
}
|
||||
</script>
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<div class="art-card h-105 p-4 box-border mb-5 max-sm:mb-4">
|
||||
<ArtBarChart
|
||||
class="box-border p-2"
|
||||
barWidth="50%"
|
||||
height="13.7rem"
|
||||
:showAxisLine="false"
|
||||
:data="chartData"
|
||||
:xAxisData="xAxisLabels"
|
||||
/>
|
||||
<div class="ml-1">
|
||||
<h3 class="mt-5 text-lg font-medium">用户概述</h3>
|
||||
<p class="mt-1 text-sm">比上周 <span class="text-success font-medium">+23%</span></p>
|
||||
<p class="mt-1 text-sm">我们为您创建了多个选项,可将它们组合在一起并定制为像素完美的页面</p>
|
||||
</div>
|
||||
<div class="flex-b mt-2">
|
||||
<div class="flex-1" v-for="(item, index) in list" :key="index">
|
||||
<p class="text-2xl text-g-900">{{ item.num }}</p>
|
||||
<p class="text-xs text-g-500">{{ item.name }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
interface UserStatItem {
|
||||
name: string
|
||||
num: string
|
||||
}
|
||||
|
||||
// 最近9个月
|
||||
const xAxisLabels = ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月']
|
||||
|
||||
// 每月活跃用户数
|
||||
const chartData = [160, 100, 150, 80, 190, 100, 175, 120, 160]
|
||||
|
||||
/**
|
||||
* 用户统计数据列表
|
||||
* 包含总用户量、总访问量、日访问量和周同比等关键指标
|
||||
*/
|
||||
const list: UserStatItem[] = [
|
||||
{ name: '总用户量', num: '32k' },
|
||||
{ name: '总访问量', num: '128k' },
|
||||
{ name: '日访问量', num: '1.2k' },
|
||||
{ name: '周同比', num: '+5%' }
|
||||
]
|
||||
</script>
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<ElRow :gutter="20" class="flex">
|
||||
<ElCol v-for="(item, index) in dataList" :key="index" :sm="12" :md="6" :lg="6">
|
||||
<div class="art-card relative flex flex-col justify-center h-35 px-5 mb-5 max-sm:mb-4">
|
||||
<span class="text-g-700 text-sm">{{ item.des }}</span>
|
||||
<ArtCountTo class="text-[26px] font-medium mt-2" :target="item.num" :duration="1300" />
|
||||
<div class="flex-c mt-1">
|
||||
<span class="text-xs text-g-600">较上周</span>
|
||||
<span
|
||||
class="ml-1 text-xs font-semibold"
|
||||
:class="[item.change.indexOf('+') === -1 ? 'text-danger' : 'text-success']"
|
||||
>
|
||||
{{ item.change }}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="absolute top-0 bottom-0 right-5 m-auto size-12.5 rounded-xl flex-cc bg-theme/10"
|
||||
>
|
||||
<ArtSvgIcon :icon="item.icon" class="text-xl text-theme" />
|
||||
</div>
|
||||
</div>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
interface CardDataItem {
|
||||
des: string
|
||||
icon: string
|
||||
startVal: number
|
||||
duration: number
|
||||
num: number
|
||||
change: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 卡片统计数据列表
|
||||
* 展示总访问次数、在线访客数、点击量和新用户等核心数据指标
|
||||
*/
|
||||
const dataList = reactive<CardDataItem[]>([
|
||||
{
|
||||
des: '总访问次数',
|
||||
icon: 'ri:pie-chart-line',
|
||||
startVal: 0,
|
||||
duration: 1000,
|
||||
num: 9120,
|
||||
change: '+20%'
|
||||
},
|
||||
{
|
||||
des: '在线访客数',
|
||||
icon: 'ri:group-line',
|
||||
startVal: 0,
|
||||
duration: 1000,
|
||||
num: 182,
|
||||
change: '+10%'
|
||||
},
|
||||
{
|
||||
des: '点击量',
|
||||
icon: 'ri:fire-line',
|
||||
startVal: 0,
|
||||
duration: 1000,
|
||||
num: 9520,
|
||||
change: '-12%'
|
||||
},
|
||||
{
|
||||
des: '新用户',
|
||||
icon: 'ri:progress-2-line',
|
||||
startVal: 0,
|
||||
duration: 1000,
|
||||
num: 156,
|
||||
change: '+30%'
|
||||
}
|
||||
])
|
||||
</script>
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
<template>
|
||||
<div class="art-card h-128 p-5 mb-5 max-sm:mb-4">
|
||||
<div class="art-card-header">
|
||||
<div class="title">
|
||||
<h4>动态</h4>
|
||||
<p>新增<span class="text-success">+6</span></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="h-9/10 mt-2 overflow-hidden">
|
||||
<ElScrollbar>
|
||||
<div
|
||||
class="h-17.5 leading-17.5 border-b border-g-300 text-sm overflow-hidden last:border-b-0"
|
||||
v-for="(item, index) in list"
|
||||
:key="index"
|
||||
>
|
||||
<span class="text-g-800 font-medium">{{ item.username }}</span>
|
||||
<span class="mx-2 text-g-600">{{ item.type }}</span>
|
||||
<span class="text-theme">{{ item.target }}</span>
|
||||
</div>
|
||||
</ElScrollbar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
interface DynamicItem {
|
||||
username: string
|
||||
type: string
|
||||
target: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户动态列表
|
||||
* 记录用户的关注、发文、提问、兑换等各类活动
|
||||
*/
|
||||
const list = reactive<DynamicItem[]>([
|
||||
{
|
||||
username: '中小鱼',
|
||||
type: '关注了',
|
||||
target: '誶誶淰'
|
||||
},
|
||||
{
|
||||
username: '何小荷',
|
||||
type: '发表文章',
|
||||
target: 'Vue3 + Typescript + Vite 项目实战笔记'
|
||||
},
|
||||
{
|
||||
username: '中小鱼',
|
||||
type: '关注了',
|
||||
target: '誶誶淰'
|
||||
},
|
||||
{
|
||||
username: '何小荷',
|
||||
type: '发表文章',
|
||||
target: 'Vue3 + Typescript + Vite 项目实战笔记'
|
||||
},
|
||||
{
|
||||
username: '誶誶淰',
|
||||
type: '提出问题',
|
||||
target: '主题可以配置吗'
|
||||
},
|
||||
{
|
||||
username: '发呆草',
|
||||
type: '兑换了物品',
|
||||
target: '《奇特的一生》'
|
||||
},
|
||||
{
|
||||
username: '甜筒',
|
||||
type: '关闭了问题',
|
||||
target: '发呆草'
|
||||
},
|
||||
{
|
||||
username: '冷月呆呆',
|
||||
type: '兑换了物品',
|
||||
target: '《高效人士的七个习惯》'
|
||||
}
|
||||
])
|
||||
</script>
|
||||
+169
@@ -0,0 +1,169 @@
|
||||
<template>
|
||||
<div class="art-card p-5 h-128 overflow-hidden mb-5 max-sm:mb-4">
|
||||
<div class="art-card-header">
|
||||
<div class="title">
|
||||
<h4>新用户</h4>
|
||||
<p>这个月增长<span class="text-success">+20%</span></p>
|
||||
</div>
|
||||
<ElRadioGroup v-model="radio2">
|
||||
<ElRadioButton value="本月" label="本月"></ElRadioButton>
|
||||
<ElRadioButton value="上月" label="上月"></ElRadioButton>
|
||||
<ElRadioButton value="今年" label="今年"></ElRadioButton>
|
||||
</ElRadioGroup>
|
||||
</div>
|
||||
<ArtTable
|
||||
class="w-full"
|
||||
:data="tableData"
|
||||
style="width: 100%"
|
||||
size="large"
|
||||
:border="false"
|
||||
:stripe="false"
|
||||
:header-cell-style="{ background: 'transparent' }"
|
||||
>
|
||||
<template #default>
|
||||
<ElTableColumn label="头像" prop="avatar" width="150px">
|
||||
<template #default="scope">
|
||||
<div style="display: flex; align-items: center">
|
||||
<img class="size-9 rounded-lg" :src="scope.row.avatar" alt="avatar" />
|
||||
<span class="ml-2">{{ scope.row.username }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="地区" prop="province" />
|
||||
<ElTableColumn label="性别" prop="avatar">
|
||||
<template #default="scope">
|
||||
<div style="display: flex; align-items: center">
|
||||
<span style="margin-left: 10px">{{ scope.row.sex === 1 ? '男' : '女' }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="进度" width="240">
|
||||
<template #default="scope">
|
||||
<ElProgress
|
||||
:percentage="scope.row.pro"
|
||||
:color="scope.row.color"
|
||||
:stroke-width="4"
|
||||
:aria-label="`${scope.row.username}的完成进度: ${scope.row.pro}%`"
|
||||
/>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
</template>
|
||||
</ArtTable>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import avatar1 from '@/assets/images/avatar/avatar1.webp'
|
||||
import avatar2 from '@/assets/images/avatar/avatar2.webp'
|
||||
import avatar3 from '@/assets/images/avatar/avatar3.webp'
|
||||
import avatar4 from '@/assets/images/avatar/avatar4.webp'
|
||||
import avatar5 from '@/assets/images/avatar/avatar5.webp'
|
||||
import avatar6 from '@/assets/images/avatar/avatar6.webp'
|
||||
|
||||
interface UserTableItem {
|
||||
username: string
|
||||
province: string
|
||||
sex: 0 | 1
|
||||
age: number
|
||||
percentage: number
|
||||
pro: number
|
||||
color: string
|
||||
avatar: string
|
||||
}
|
||||
|
||||
const ANIMATION_DELAY = 100
|
||||
|
||||
const radio2 = ref('本月')
|
||||
|
||||
/**
|
||||
* 新用户表格数据
|
||||
* 包含用户基本信息和完成进度
|
||||
*/
|
||||
const tableData = reactive<UserTableItem[]>([
|
||||
{
|
||||
username: '中小鱼',
|
||||
province: '北京',
|
||||
sex: 0,
|
||||
age: 22,
|
||||
percentage: 60,
|
||||
pro: 0,
|
||||
color: 'var(--art-primary)',
|
||||
avatar: avatar1
|
||||
},
|
||||
{
|
||||
username: '何小荷',
|
||||
province: '深圳',
|
||||
sex: 1,
|
||||
age: 21,
|
||||
percentage: 20,
|
||||
pro: 0,
|
||||
color: 'var(--art-secondary)',
|
||||
avatar: avatar2
|
||||
},
|
||||
{
|
||||
username: '誶誶淰',
|
||||
province: '上海',
|
||||
sex: 1,
|
||||
age: 23,
|
||||
percentage: 60,
|
||||
pro: 0,
|
||||
color: 'var(--art-warning)',
|
||||
avatar: avatar3
|
||||
},
|
||||
{
|
||||
username: '发呆草',
|
||||
province: '长沙',
|
||||
sex: 0,
|
||||
age: 28,
|
||||
percentage: 50,
|
||||
pro: 0,
|
||||
color: 'var(--art-info)',
|
||||
avatar: avatar4
|
||||
},
|
||||
{
|
||||
username: '甜筒',
|
||||
province: '浙江',
|
||||
sex: 1,
|
||||
age: 26,
|
||||
percentage: 70,
|
||||
pro: 0,
|
||||
color: 'var(--art-error)',
|
||||
avatar: avatar5
|
||||
},
|
||||
{
|
||||
username: '冷月呆呆',
|
||||
province: '湖北',
|
||||
sex: 1,
|
||||
age: 25,
|
||||
percentage: 90,
|
||||
pro: 0,
|
||||
color: 'var(--art-success)',
|
||||
avatar: avatar6
|
||||
}
|
||||
])
|
||||
|
||||
/**
|
||||
* 添加进度条动画效果
|
||||
* 延迟后将进度值从 0 更新到目标百分比,触发动画
|
||||
*/
|
||||
const addAnimation = (): void => {
|
||||
setTimeout(() => {
|
||||
tableData.forEach((item) => {
|
||||
item.pro = item.percentage
|
||||
})
|
||||
}, ANIMATION_DELAY)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
addAnimation()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.art-card {
|
||||
:deep(.el-radio-button__original-radio:checked + .el-radio-button__inner) {
|
||||
color: var(--el-color-primary) !important;
|
||||
background: transparent !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<div class="art-card h-105 p-5 mb-5 max-sm:mb-4">
|
||||
<div class="art-card-header">
|
||||
<div class="title">
|
||||
<h4>访问量</h4>
|
||||
<p>今年增长<span class="text-success">+15%</span></p>
|
||||
</div>
|
||||
</div>
|
||||
<ArtLineChart
|
||||
height="calc(100% - 56px)"
|
||||
:data="data"
|
||||
:xAxisData="xAxisData"
|
||||
:showAreaColor="true"
|
||||
:showAxisLine="false"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
/**
|
||||
* 全年访问量数据
|
||||
* 记录每月的访问量统计
|
||||
*/
|
||||
const data = [50, 25, 40, 20, 70, 35, 65, 30, 35, 20, 40, 44]
|
||||
|
||||
/**
|
||||
* X 轴月份标签
|
||||
*/
|
||||
const xAxisData = [
|
||||
'1月',
|
||||
'2月',
|
||||
'3月',
|
||||
'4月',
|
||||
'5月',
|
||||
'6月',
|
||||
'7月',
|
||||
'8月',
|
||||
'9月',
|
||||
'10月',
|
||||
'11月',
|
||||
'12月'
|
||||
]
|
||||
</script>
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
<template>
|
||||
<div class="art-card h-128 p-5 mb-5 max-sm:mb-4">
|
||||
<div class="art-card-header">
|
||||
<div class="title">
|
||||
<h4>代办事项</h4>
|
||||
<p>待处理<span class="text-danger">3</span></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="h-[calc(100%-40px)] overflow-auto">
|
||||
<ElScrollbar>
|
||||
<div
|
||||
class="flex-cb h-17.5 border-b border-g-300 text-sm last:border-b-0"
|
||||
v-for="(item, index) in list"
|
||||
:key="index"
|
||||
>
|
||||
<div>
|
||||
<p class="text-sm">{{ item.username }}</p>
|
||||
<p class="text-g-500 mt-1">{{ item.date }}</p>
|
||||
</div>
|
||||
<ElCheckbox v-model="item.complate" />
|
||||
</div>
|
||||
</ElScrollbar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
interface TodoItem {
|
||||
username: string
|
||||
date: string
|
||||
complate: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* 待办事项列表
|
||||
* 记录每日工作任务及完成状态
|
||||
*/
|
||||
const list = reactive<TodoItem[]>([
|
||||
{
|
||||
username: '查看今天工作内容',
|
||||
date: '上午 09:30',
|
||||
complate: true
|
||||
},
|
||||
{
|
||||
username: '回复邮件',
|
||||
date: '上午 10:30',
|
||||
complate: true
|
||||
},
|
||||
{
|
||||
username: '工作汇报整理',
|
||||
date: '上午 11:00',
|
||||
complate: true
|
||||
},
|
||||
{
|
||||
username: '产品需求会议',
|
||||
date: '下午 02:00',
|
||||
complate: false
|
||||
},
|
||||
{
|
||||
username: '整理会议内容',
|
||||
date: '下午 03:30',
|
||||
complate: false
|
||||
},
|
||||
{
|
||||
username: '明天工作计划',
|
||||
date: '下午 06:30',
|
||||
complate: false
|
||||
}
|
||||
])
|
||||
</script>
|
||||
@@ -0,0 +1,77 @@
|
||||
<!-- 电子商务页面 -->
|
||||
<template>
|
||||
<div class="ecommerce">
|
||||
<ElRow :gutter="20">
|
||||
<ElCol :sm="24" :md="24" :lg="16">
|
||||
<Banner />
|
||||
</ElCol>
|
||||
<ElCol :sm="12" :md="12" :lg="4">
|
||||
<TotalOrderVolume />
|
||||
</ElCol>
|
||||
<ElCol :sm="12" :md="12" :lg="4">
|
||||
<TotalProducts />
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
|
||||
<ElRow :gutter="20">
|
||||
<ElCol :sm="12" :md="12" :lg="8">
|
||||
<SalesTrend />
|
||||
</ElCol>
|
||||
<ElCol :sm="12" :md="12" :lg="8">
|
||||
<SalesClassification />
|
||||
</ElCol>
|
||||
<ElCol :sm="24" :md="24" :lg="8">
|
||||
<ElRow :gutter="20">
|
||||
<ElCol :sm="24" :md="12" :lg="12">
|
||||
<ProductSales />
|
||||
</ElCol>
|
||||
<ElCol :sm="24" :md="12" :lg="12">
|
||||
<SalesGrowth />
|
||||
</ElCol>
|
||||
<ElCol :span="24" class="no-margin-bottom">
|
||||
<CartConversionRate />
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
|
||||
<ElRow :gutter="20">
|
||||
<ElCol :sm="24" :md="12" :lg="8">
|
||||
<HotCommodity />
|
||||
</ElCol>
|
||||
<ElCol :sm="24" :md="12" :lg="8">
|
||||
<AnnualSales />
|
||||
</ElCol>
|
||||
<ElCol :sm="24" :md="24" :lg="8">
|
||||
<TransactionList />
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
|
||||
<ElRow :gutter="20">
|
||||
<ElCol :md="24" :lg="8">
|
||||
<RecentTransaction />
|
||||
</ElCol>
|
||||
<ElCol :md="24" :lg="16" class="no-margin-bottom">
|
||||
<HotProductsList />
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Banner from './modules/banner.vue'
|
||||
import TotalOrderVolume from './modules/total-order-volume.vue'
|
||||
import TotalProducts from './modules/total-products.vue'
|
||||
import SalesTrend from './modules/sales-trend.vue'
|
||||
import SalesClassification from './modules/sales-classification.vue'
|
||||
import TransactionList from './modules/transaction-list.vue'
|
||||
import HotCommodity from './modules/hot-commodity.vue'
|
||||
import RecentTransaction from './modules/recent-transaction.vue'
|
||||
import AnnualSales from './modules/annual-sales.vue'
|
||||
import ProductSales from './modules/product-sales.vue'
|
||||
import SalesGrowth from './modules/sales-growth.vue'
|
||||
import CartConversionRate from './modules/cart-conversion-rate.vue'
|
||||
import HotProductsList from './modules/hot-products-list.vue'
|
||||
|
||||
defineOptions({ name: 'Ecommerce' })
|
||||
</script>
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<div class="art-card p-5 h-[28.2rem] mb-5 max-sm:mb-4">
|
||||
<div class="art-card-header">
|
||||
<div class="title">
|
||||
<h4>年度销售额</h4>
|
||||
<p>按季度统计</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ArtBarChart
|
||||
:showAxisLabel="false"
|
||||
:showAxisLine="false"
|
||||
:showSplitLine="false"
|
||||
:data="[50, 80, 50, 90, 60, 70, 50]"
|
||||
barWidth="26px"
|
||||
height="calc(100% - 155px)"
|
||||
/>
|
||||
<div class="flex justify-around mt-10">
|
||||
<div class="flex-c">
|
||||
<div class="flex-cc size-10.5 mr-2.5 text-theme bg-theme/10 rounded-lg">
|
||||
<ArtSvgIcon icon="ri:money-cny-circle-line" class="text-xl" />
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-lg">¥200,858</p>
|
||||
<span class="text-sm">线上销售</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-c">
|
||||
<div class="flex-cc size-10.5 mr-2.5 text-theme bg-theme/10 rounded-lg">
|
||||
<ArtSvgIcon icon="ri:heart-3-line" class="text-xl" />
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-lg">¥102,927</p>
|
||||
<span class="text-sm">线下销售</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<ArtBasicBanner
|
||||
class="justify-center !h-53 mb-5 max-sm:!pt-8 max-sm:!h-48 max-sm:mb-4"
|
||||
:title="`欢迎回来 ${userInfo.userName}`"
|
||||
boxStyle="!bg-theme/10"
|
||||
titleColor="var(--art-gray-900)"
|
||||
:decoration="false"
|
||||
:meteorConfig="{
|
||||
enabled: true,
|
||||
count: 10
|
||||
}"
|
||||
:buttonConfig="{
|
||||
show: false,
|
||||
text: ''
|
||||
}"
|
||||
:imageConfig="{
|
||||
src: bannerCover,
|
||||
width: '18rem',
|
||||
bottom: '-7.5rem'
|
||||
}"
|
||||
@click="handleBannerClick"
|
||||
>
|
||||
<div class="flex mt-6">
|
||||
<div class="mr-8 pr-8 border-r border-g-400 dark:border-g-300/60">
|
||||
<p class="text-3xl">
|
||||
<ArtCountTo
|
||||
class="number box-title"
|
||||
:target="2340"
|
||||
:duration="1500"
|
||||
prefix="¥"
|
||||
separator=","
|
||||
/>
|
||||
<ArtSvgIcon icon="ri:arrow-right-up-line" class="text-xl text-success relative -top-2" />
|
||||
</p>
|
||||
<p class="mt-1 text-sm text-g-700">今日销售额</p>
|
||||
</div>
|
||||
<div class="mr-8">
|
||||
<p class="text-3xl">
|
||||
<ArtCountTo class="number box-title" :target="35" :duration="1500" suffix="%" />
|
||||
<ArtSvgIcon icon="ri:arrow-right-up-line" class="text-xl text-success relative -top-2" />
|
||||
</p>
|
||||
<p class="mt-1 text-sm text-g-700">较昨日</p>
|
||||
</div>
|
||||
</div>
|
||||
</ArtBasicBanner>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import bannerCover from '@imgs/login/lf_icon2.webp'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
|
||||
const userStore = useUserStore()
|
||||
|
||||
/**
|
||||
* 获取当前用户信息
|
||||
*/
|
||||
const userInfo = computed(() => userStore.getUserInfo)
|
||||
|
||||
/**
|
||||
* 处理横幅点击事件
|
||||
*/
|
||||
const handleBannerClick = (): void => {
|
||||
// TODO: 添加横幅点击处理逻辑
|
||||
}
|
||||
</script>
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
<template>
|
||||
<ArtLineChartCard
|
||||
class="mb-5 max-sm:mb-4"
|
||||
:value="2545"
|
||||
label="购物车转化率"
|
||||
:percentage="1.2"
|
||||
:height="13.5"
|
||||
:chartData="[120, 132, 101, 134, 90, 230, 210]"
|
||||
:showAreaColor="true"
|
||||
/>
|
||||
</template>
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<div class="art-card p-5 h-[28.2rem] mb-5 max-sm:mb-4">
|
||||
<div class="art-card-header">
|
||||
<div class="title">
|
||||
<h4>热销商品</h4>
|
||||
<p>本周销售排行</p>
|
||||
</div>
|
||||
</div>
|
||||
<ArtLineChart
|
||||
:showAxisLabel="false"
|
||||
:showAxisLine="false"
|
||||
:showSplitLine="false"
|
||||
:showAreaColor="true"
|
||||
:data="[8, 40, 82, 35, 90, 52, 35]"
|
||||
height="9rem"
|
||||
/>
|
||||
<div class="mt-10 space-y-5">
|
||||
<div v-for="item in weeklyList" :key="item.title" class="flex-c">
|
||||
<div class="size-10.5 flex-cc rounded-lg" :class="item.iconBgClass">
|
||||
<ArtSvgIcon :icon="item.icon" class="text-xl" />
|
||||
</div>
|
||||
<div class="ml-2.5">
|
||||
<p class="text-sm font-medium text-g-800">{{ item.title }}</p>
|
||||
<span class="text-sm text-g-600">{{ item.subtitle }}</span>
|
||||
</div>
|
||||
<div class="ml-auto px-3 py-1.5 text-sm text-center rounded" :class="item.valueBgClass">
|
||||
<span>+{{ item.value }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
interface WeeklyItem {
|
||||
icon: string
|
||||
title: string
|
||||
subtitle: string
|
||||
value: string
|
||||
iconBgClass: string
|
||||
valueBgClass: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 本周热销商品列表
|
||||
* 展示销量排名前三的商品信息
|
||||
*/
|
||||
const weeklyList: WeeklyItem[] = [
|
||||
{
|
||||
icon: 'ri:money-cny-circle-line',
|
||||
title: '智能手表Pro',
|
||||
subtitle: '电子产品',
|
||||
value: '1,286件',
|
||||
iconBgClass: 'bg-theme/12 text-theme',
|
||||
valueBgClass: 'bg-theme/12 text-theme'
|
||||
},
|
||||
{
|
||||
icon: 'ri:money-cny-circle-line',
|
||||
title: '时尚连衣裙',
|
||||
subtitle: '女装服饰',
|
||||
value: '892件',
|
||||
iconBgClass: 'bg-success/12 text-success',
|
||||
valueBgClass: 'bg-success/12 text-success'
|
||||
},
|
||||
{
|
||||
icon: 'ri:money-cny-circle-line',
|
||||
title: '厨房小家电',
|
||||
subtitle: '家居用品',
|
||||
value: '756件',
|
||||
iconBgClass: 'bg-error/12 text-error',
|
||||
valueBgClass: 'bg-error/12 text-error'
|
||||
}
|
||||
]
|
||||
</script>
|
||||
+195
@@ -0,0 +1,195 @@
|
||||
<template>
|
||||
<div class="art-card p-5 h-[27.8rem] mb-5 overflow-hidden max-sm:mb-4">
|
||||
<div class="art-card-header">
|
||||
<div class="title">
|
||||
<h4>热销产品</h4>
|
||||
<p>本月销售情况</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ElScrollbar style="height: 21.55rem" class="w-full">
|
||||
<ArtTable
|
||||
:data="tableData"
|
||||
style="margin-top: 0 !important"
|
||||
:border="false"
|
||||
:stripe="false"
|
||||
:header-cell-style="{ background: 'transparent' }"
|
||||
>
|
||||
<template #default>
|
||||
<ElTableColumn label="产品" prop="product" width="220px">
|
||||
<template #default="scope">
|
||||
<div class="flex-c">
|
||||
<img class="size-12.5 object-cover rounded-md" :src="scope.row.image" />
|
||||
<div class="flex flex-col ml-3">
|
||||
<div class="font-medium">{{ scope.row.name }}</div>
|
||||
<div class="text-xs text-slate-500">{{ scope.row.category }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="价格" prop="price">
|
||||
<template #default="scope">
|
||||
<span class="font-semibold">¥{{ scope.row.price.toLocaleString() }}</span>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="库存" prop="stock">
|
||||
<template #default="scope">
|
||||
<div
|
||||
class="inline-block px-2 py-1 text-xs font-medium rounded"
|
||||
:class="getStockClass(scope.row.stock)"
|
||||
>
|
||||
{{ getStockStatus(scope.row.stock) }}
|
||||
</div>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="销量" prop="sales" />
|
||||
<ElTableColumn label="销售趋势" width="240">
|
||||
<template #default="scope">
|
||||
<ElProgress :percentage="scope.row.pro" :color="scope.row.color" :stroke-width="4" />
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
</template>
|
||||
</ArtTable>
|
||||
</ElScrollbar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import product1 from '@/assets/images/3d/icon1.webp'
|
||||
import product2 from '@/assets/images/3d/icon2.webp'
|
||||
import product3 from '@/assets/images/3d/icon3.webp'
|
||||
import product4 from '@/assets/images/3d/icon4.webp'
|
||||
import product5 from '@/assets/images/3d/icon5.webp'
|
||||
import product6 from '@/assets/images/3d/icon6.webp'
|
||||
|
||||
interface ProductItem {
|
||||
name: string
|
||||
category: string
|
||||
price: number
|
||||
stock: number
|
||||
sales: number
|
||||
percentage: number
|
||||
pro: number
|
||||
color: string
|
||||
image: string
|
||||
}
|
||||
|
||||
const ANIMATION_DELAY = 100
|
||||
const STOCK_THRESHOLD = {
|
||||
LOW: 20,
|
||||
MEDIUM: 50
|
||||
} as const
|
||||
|
||||
/**
|
||||
* 热销产品表格数据
|
||||
* 包含产品信息、库存、销量和销售趋势
|
||||
*/
|
||||
const tableData = reactive<ProductItem[]>([
|
||||
{
|
||||
name: '智能手表 Pro',
|
||||
category: '电子设备',
|
||||
price: 1299,
|
||||
stock: 156,
|
||||
sales: 423,
|
||||
percentage: 75,
|
||||
pro: 0,
|
||||
color: 'var(--art-primary)',
|
||||
image: product1
|
||||
},
|
||||
{
|
||||
name: '无线蓝牙耳机',
|
||||
category: '音频设备',
|
||||
price: 499,
|
||||
stock: 89,
|
||||
sales: 652,
|
||||
percentage: 85,
|
||||
pro: 0,
|
||||
color: 'var(--art-success)',
|
||||
image: product2
|
||||
},
|
||||
{
|
||||
name: '机械键盘',
|
||||
category: '电脑配件',
|
||||
price: 399,
|
||||
stock: 12,
|
||||
sales: 238,
|
||||
percentage: 45,
|
||||
pro: 0,
|
||||
color: 'var(--art-warning)',
|
||||
image: product3
|
||||
},
|
||||
{
|
||||
name: '超薄笔记本电脑',
|
||||
category: '电子设备',
|
||||
price: 5999,
|
||||
stock: 0,
|
||||
sales: 126,
|
||||
percentage: 30,
|
||||
pro: 0,
|
||||
color: 'var(--art-error)',
|
||||
image: product4
|
||||
},
|
||||
{
|
||||
name: '智能音箱',
|
||||
category: '智能家居',
|
||||
price: 799,
|
||||
stock: 45,
|
||||
sales: 321,
|
||||
percentage: 60,
|
||||
pro: 0,
|
||||
color: 'var(--art-info)',
|
||||
image: product5
|
||||
},
|
||||
{
|
||||
name: '游戏手柄',
|
||||
category: '游戏配件',
|
||||
price: 299,
|
||||
stock: 78,
|
||||
sales: 489,
|
||||
percentage: 70,
|
||||
pro: 0,
|
||||
color: 'var(--art-secondary)',
|
||||
image: product6
|
||||
}
|
||||
])
|
||||
|
||||
/**
|
||||
* 根据库存数量获取状态文本
|
||||
* @param stock 库存数量
|
||||
* @returns 库存状态文本
|
||||
*/
|
||||
const getStockStatus = (stock: number): string => {
|
||||
if (stock === 0) return '缺货'
|
||||
if (stock < STOCK_THRESHOLD.LOW) return '低库存'
|
||||
if (stock < STOCK_THRESHOLD.MEDIUM) return '适中'
|
||||
return '充足'
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据库存数量获取状态样式类名
|
||||
* @param stock 库存数量
|
||||
* @returns CSS 类名
|
||||
*/
|
||||
const getStockClass = (stock: number): string => {
|
||||
if (stock === 0) return 'text-danger bg-danger/12'
|
||||
if (stock < STOCK_THRESHOLD.LOW) return 'text-warning bg-warning/12'
|
||||
if (stock < STOCK_THRESHOLD.MEDIUM) return 'text-info bg-info/12'
|
||||
return 'text-success bg-success/12'
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加进度条动画效果
|
||||
* 延迟后将进度值从 0 更新到目标百分比,触发动画
|
||||
*/
|
||||
const addAnimation = (): void => {
|
||||
setTimeout(() => {
|
||||
tableData.forEach((item) => {
|
||||
item.pro = item.percentage
|
||||
})
|
||||
}, ANIMATION_DELAY)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
addAnimation()
|
||||
})
|
||||
</script>
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
<template>
|
||||
<div class="art-card p-5 pt-3.5 mb-5 h-46 max-sm:mb-4">
|
||||
<div class="art-card-header">
|
||||
<div class="title">
|
||||
<h4 class="text-2xl">
|
||||
<span>14.5k</span>
|
||||
<ArtSvgIcon icon="ri:arrow-right-up-line" class="text-success text-lg ml-1" />
|
||||
</h4>
|
||||
<p>销售量</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ArtBarChart
|
||||
:showAxisLabel="false"
|
||||
:showAxisLine="false"
|
||||
:showSplitLine="false"
|
||||
:data="[50, 80, 50, 90, 60, 70, 50]"
|
||||
barWidth="16px"
|
||||
height="5.6rem"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<ArtTimelineListCard
|
||||
:list="timelineData"
|
||||
title="最近交易"
|
||||
subtitle="今日订单动态"
|
||||
class="h-[27.8rem] mb-5 max-sm:mb-4"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
interface TimelineItem {
|
||||
time: string
|
||||
status: string
|
||||
content: string
|
||||
code?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 最近交易时间线数据
|
||||
* 记录今日订单处理、商品上架、支付等关键活动
|
||||
*/
|
||||
const timelineData: TimelineItem[] = [
|
||||
{
|
||||
time: '上午 09:30',
|
||||
status: 'rgb(73, 190, 255)',
|
||||
content: '收到订单 #38291 支付 ¥385.90'
|
||||
},
|
||||
{
|
||||
time: '上午 10:00',
|
||||
status: 'rgb(54, 158, 255)',
|
||||
content: '新商品上架',
|
||||
code: 'SKU-3467'
|
||||
},
|
||||
{
|
||||
time: '上午 12:00',
|
||||
status: 'rgb(103, 232, 207)',
|
||||
content: '向供应商支付了 ¥6495.00'
|
||||
},
|
||||
{
|
||||
time: '下午 14:30',
|
||||
status: 'rgb(255, 193, 7)',
|
||||
content: '促销活动开始',
|
||||
code: 'PROMO-2023'
|
||||
},
|
||||
{
|
||||
time: '下午 15:45',
|
||||
status: 'rgb(255, 105, 105)',
|
||||
content: '订单取消提醒',
|
||||
code: 'ORD-9876'
|
||||
},
|
||||
{
|
||||
time: '下午 17:00',
|
||||
status: 'rgb(103, 232, 207)',
|
||||
content: '完成日销售报表'
|
||||
}
|
||||
]
|
||||
</script>
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<div class="art-card p-5 mb-5 h-105 max-sm:mb-4">
|
||||
<div class="art-card-header">
|
||||
<div class="title">
|
||||
<h4>销售分类</h4>
|
||||
<p>按产品类别</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ArtRingChart
|
||||
:data="[
|
||||
{ value: 30, name: '电子产品' },
|
||||
{ value: 55, name: '服装鞋包' },
|
||||
{ value: 36, name: '家居用品' }
|
||||
]"
|
||||
:color="['#4C87F3', '#EDF2FF', '#8BD8FC']"
|
||||
:radius="['70%', '80%']"
|
||||
height="16.5rem"
|
||||
:showLabel="false"
|
||||
:borderRadius="0"
|
||||
centerText="¥300,458"
|
||||
/>
|
||||
<div class="flex justify-around">
|
||||
<div class="flex-c">
|
||||
<div class="flex-cc size-10.5 mr-2.5 text-theme bg-theme/10 rounded-lg">
|
||||
<ArtSvgIcon icon="ri:money-cny-circle-line" class="text-xl" />
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-lg">¥500,458</p>
|
||||
<span class="text-sm">总收入</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-c">
|
||||
<div class="flex-cc size-10.5 mr-2.5 text-theme bg-theme/10 rounded-lg">
|
||||
<ArtSvgIcon icon="ri:heart-3-line" class="text-xl" />
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-lg">¥130,580</p>
|
||||
<span class="text-sm">净利润</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
<template>
|
||||
<div class="art-card p-5 pt-3.5 mb-5 h-46 max-sm:mb-4">
|
||||
<div class="art-card-header">
|
||||
<div class="title">
|
||||
<h4 class="text-2xl">
|
||||
<span>12%</span>
|
||||
<ArtSvgIcon icon="ri:arrow-right-up-line" class="text-success text-lg ml-1" />
|
||||
</h4>
|
||||
<p>增长</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ArtLineChart
|
||||
:showAreaColor="true"
|
||||
:showAxisLabel="false"
|
||||
:showAxisLine="false"
|
||||
:showSplitLine="false"
|
||||
:data="[50, 85, 65, 95, 75, 130, 180]"
|
||||
barWidth="16px"
|
||||
height="5rem"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
<template>
|
||||
<div class="art-card p-5 mb-5 h-105 max-sm:mb-4">
|
||||
<div class="art-card-header">
|
||||
<div class="title">
|
||||
<h4>销售趋势</h4>
|
||||
<p>月度销售对比</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ArtDualBarCompareChart
|
||||
:positiveData="[50, 80, 120, 90, 60]"
|
||||
:negativeData="[30, 60, 90, 70, 40]"
|
||||
:xAxisData="['一月', '二月', '三月', '四月', '五月']"
|
||||
height="19rem"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
<template>
|
||||
<div class="art-card h-53 p-5 pt-3.5 mb-5 max-sm:mb-4">
|
||||
<div class="art-card-header">
|
||||
<div class="title">
|
||||
<h4 class="text-2xl font-medium">205,216</h4>
|
||||
<p>这个月增长</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ArtRingChart
|
||||
:data="[
|
||||
{ value: 30, name: '已完成' },
|
||||
{ value: 25, name: '处理中' },
|
||||
{ value: 45, name: '待发货' }
|
||||
]"
|
||||
:color="['#4C87F3', '#93F1B4', '#8BD8FC']"
|
||||
:radius="['56%', '76%']"
|
||||
height="7rem"
|
||||
:showLabel="false"
|
||||
:borderRadius="0"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<div class="art-card h-53 p-5 pt-3.5 mb-5 max-sm:mb-4">
|
||||
<div class="art-card-header">
|
||||
<div class="title">
|
||||
<h4 class="text-2xl font-medium">55,231</h4>
|
||||
<p>商品总数</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ArtBarChart
|
||||
:showAxisLabel="false"
|
||||
:showAxisLine="false"
|
||||
:showSplitLine="false"
|
||||
:data="[50, 80, 40, 90, 60, 70]"
|
||||
height="7rem"
|
||||
barWidth="18px"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<ArtDataListCard
|
||||
class="mb-5 max-sm:mb-4"
|
||||
:maxCount="4"
|
||||
:list="dataList"
|
||||
title="最近活动"
|
||||
subtitle="订单处理状态"
|
||||
:showMoreButton="true"
|
||||
@more="handleMore"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
interface TransactionItem {
|
||||
title: string
|
||||
status: string
|
||||
time: string
|
||||
class: string
|
||||
icon: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 最近活动数据列表
|
||||
* 展示订单处理、退款申请、投诉处理等业务活动状态
|
||||
*/
|
||||
const dataList: TransactionItem[] = [
|
||||
{
|
||||
title: '新订单 #38291',
|
||||
status: '待处理',
|
||||
time: '5分钟',
|
||||
class: 'bg-theme/12 text-theme',
|
||||
icon: 'ri:shopping-bag-4-line'
|
||||
},
|
||||
{
|
||||
title: '退款申请 #12845',
|
||||
status: '处理中',
|
||||
time: '10分钟',
|
||||
class: 'bg-secondary/12 text-secondary',
|
||||
icon: 'ri:profile-line'
|
||||
},
|
||||
{
|
||||
title: '客户投诉处理',
|
||||
status: '待处理',
|
||||
time: '15分钟',
|
||||
class: 'bg-warning/12 text-warning',
|
||||
icon: 'ri:customer-service-2-line'
|
||||
},
|
||||
{
|
||||
title: '库存不足提醒',
|
||||
status: '紧急',
|
||||
time: '20分钟',
|
||||
class: 'bg-danger/12 text-danger',
|
||||
icon: 'ri:box-1-line'
|
||||
},
|
||||
{
|
||||
title: '订单 #29384 已发货',
|
||||
status: '已完成',
|
||||
time: '20分钟',
|
||||
class: 'bg-success/12 text-success',
|
||||
icon: 'ri:shopping-bag-3-line'
|
||||
}
|
||||
]
|
||||
|
||||
/**
|
||||
* 处理查看更多按钮点击事件
|
||||
*/
|
||||
const handleMore = (): void => {
|
||||
// TODO: 添加查看更多逻辑
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user