初始化本地仓库

This commit is contained in:
2026-08-31 11:33:59 +08:00
commit 663f74b633
2390 changed files with 402918 additions and 0 deletions
@@ -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>
@@ -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>
@@ -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>
@@ -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>
@@ -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>
@@ -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>
@@ -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>
@@ -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>
@@ -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>
@@ -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>
@@ -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>
@@ -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>
@@ -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>
@@ -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>