|
|
@@ -1,146 +1,361 @@
|
|
|
<template>
|
|
|
<div class="app-container home">
|
|
|
- <!-- <el-divider /> -->
|
|
|
- <div class="index-style">
|
|
|
- <div class="typewriter-container">
|
|
|
- <span v-for="(char, index) in 'welcome!'" :key="index" :style="{ animationDelay: `${index * 0.5}s` }" class="typewriter-char">{{
|
|
|
- char
|
|
|
- }}</span>
|
|
|
- </div>
|
|
|
+ <div class="dashboard-header">
|
|
|
+ <h2>客户营销数据看板</h2>
|
|
|
+ <p>为您提供实时的客户、订单和商品数据概览</p>
|
|
|
</div>
|
|
|
+
|
|
|
+ <!-- 数据统计卡片 -->
|
|
|
+ <el-row :gutter="20" class="panel-group">
|
|
|
+ <el-col :xs="24" :sm="12" :lg="6" class="card-panel-col">
|
|
|
+ <el-card shadow="hover" class="data-card customer-card">
|
|
|
+ <div class="card-header">
|
|
|
+ <div class="card-title">总客户数</div>
|
|
|
+ <el-tag type="primary" effect="light">实时</el-tag>
|
|
|
+ </div>
|
|
|
+ <div class="card-value">1,250</div>
|
|
|
+ <div class="card-desc">
|
|
|
+ <span>较上月 <span class="trend-up">+5.2% ↗</span></span>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ <el-col :xs="24" :sm="12" :lg="6" class="card-panel-col">
|
|
|
+ <el-card shadow="hover" class="data-card order-card">
|
|
|
+ <div class="card-header">
|
|
|
+ <div class="card-title">总订单数</div>
|
|
|
+ <el-tag type="success" effect="light">实时</el-tag>
|
|
|
+ </div>
|
|
|
+ <div class="card-value">3,520</div>
|
|
|
+ <div class="card-desc">
|
|
|
+ <span>较上月 <span class="trend-up">+12.4% ↗</span></span>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ <el-col :xs="24" :sm="12" :lg="6" class="card-panel-col">
|
|
|
+ <el-card shadow="hover" class="data-card product-card">
|
|
|
+ <div class="card-header">
|
|
|
+ <div class="card-title">商品总量</div>
|
|
|
+ <el-tag type="warning" effect="light">总计</el-tag>
|
|
|
+ </div>
|
|
|
+ <div class="card-value">485</div>
|
|
|
+ <div class="card-desc">
|
|
|
+ <span>本周上新 <span class="trend-up">+15 ↗</span></span>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ <el-col :xs="24" :sm="12" :lg="6" class="card-panel-col">
|
|
|
+ <el-card shadow="hover" class="data-card revenue-card">
|
|
|
+ <div class="card-header">
|
|
|
+ <div class="card-title">本月营收</div>
|
|
|
+ <el-tag type="danger" effect="light">结算</el-tag>
|
|
|
+ </div>
|
|
|
+ <div class="card-value">¥128,500</div>
|
|
|
+ <div class="card-desc">
|
|
|
+ <span>较上月 <span class="trend-down">-1.5% ↘</span></span>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <!-- 图表与列表区域 -->
|
|
|
+ <el-row :gutter="20" class="content-row">
|
|
|
+ <!-- 最新订单 -->
|
|
|
+ <el-col :xs="24" :sm="24" :lg="16" class="card-panel-col">
|
|
|
+ <el-card shadow="hover" class="table-card">
|
|
|
+ <template #header>
|
|
|
+ <div class="clearfix">
|
|
|
+ <span class="section-title">最新订单明细</span>
|
|
|
+ <el-button style="float: right; padding: 3px 0" text>查看全部</el-button>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <el-table :data="orderList" style="width: 100%" height="350">
|
|
|
+ <el-table-column prop="orderNo" label="订单编号" width="130" />
|
|
|
+ <el-table-column prop="customer" label="客户名称" width="120" />
|
|
|
+ <el-table-column prop="product" label="商品信息" show-overflow-tooltip />
|
|
|
+ <el-table-column prop="amount" label="金额(元)" width="100">
|
|
|
+ <template #default="scope">
|
|
|
+ <span style="font-weight: bold; color: #f56c6c">¥{{ scope.row.amount }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="status" label="状态" width="90">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-tag :type="getStatusType(scope.row.status)" size="small">
|
|
|
+ {{ scope.row.status }}
|
|
|
+ </el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="date" label="下单时间" width="160" />
|
|
|
+ </el-table>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+
|
|
|
+ <!-- 热销商品榜单 -->
|
|
|
+ <el-col :xs="24" :sm="24" :lg="8" class="card-panel-col">
|
|
|
+ <el-card shadow="hover" class="list-card">
|
|
|
+ <template #header>
|
|
|
+ <div class="clearfix">
|
|
|
+ <span class="section-title">热销商品排行榜</span>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <div class="product-ranking">
|
|
|
+ <div v-for="(item, index) in productRankings" :key="index" class="ranking-item">
|
|
|
+ <div class="ranking-index" :class="{ 'top-three': index < 3 }">{{ index + 1 }}</div>
|
|
|
+ <div class="ranking-info">
|
|
|
+ <div class="product-name">{{ item.name }}</div>
|
|
|
+ <div class="product-sales">销量:{{ item.sales }} 件</div>
|
|
|
+ </div>
|
|
|
+ <div class="ranking-progress">
|
|
|
+ <el-progress :percentage="item.percentage" :color="getProgressColor(index)" :show-text="false" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script setup name="Index" lang="ts">
|
|
|
-const goTarget = (url: string) => {
|
|
|
- window.open(url, '__blank');
|
|
|
+import { ref } from 'vue';
|
|
|
+
|
|
|
+// 模拟订单数据
|
|
|
+const orderList = ref([
|
|
|
+ {
|
|
|
+ orderNo: 'ORD20231001',
|
|
|
+ customer: '张三网络',
|
|
|
+ product: '年度营销SaaS套餐(旗舰版)',
|
|
|
+ amount: '12800',
|
|
|
+ status: '已支付',
|
|
|
+ date: '2023-10-24 10:23:45'
|
|
|
+ },
|
|
|
+ { orderNo: 'ORD20231002', customer: '李四科技', product: '短信按量计费包(10万条)', amount: '4500', status: '待发货', date: '2023-10-24 09:15:30' },
|
|
|
+ { orderNo: 'ORD20231003', customer: '王五实业', product: '私有化部署实施服务费用', amount: '35000', status: '已完成', date: '2023-10-23 16:40:12' },
|
|
|
+ { orderNo: 'ORD20231004', customer: '赵六电商', product: '会员积分商城插件', amount: '899', status: '待支付', date: '2023-10-23 15:20:00' },
|
|
|
+ { orderNo: 'ORD20231005', customer: '钱七餐饮', product: '微信小程序点单系统', amount: '6800', status: '已完成', date: '2023-10-22 11:10:05' },
|
|
|
+ { orderNo: 'ORD20231006', customer: '孙八教育', product: '在线直播课程系统升级', amount: '15000', status: '已支付', date: '2023-10-22 09:05:22' }
|
|
|
+]);
|
|
|
+
|
|
|
+// 模拟商品排行榜数据
|
|
|
+const productRankings = ref([
|
|
|
+ { name: '年度营销SaaS套餐', sales: 1250, percentage: 90 },
|
|
|
+ { name: '微信小程序商城源码', sales: 980, percentage: 75 },
|
|
|
+ { name: '短信按量计费包', sales: 856, percentage: 65 },
|
|
|
+ { name: '私有化部署实施', sales: 420, percentage: 40 },
|
|
|
+ { name: '会员积分商城插件', sales: 315, percentage: 30 },
|
|
|
+ { name: '分销代理裂变系统', sales: 258, percentage: 20 }
|
|
|
+]);
|
|
|
+
|
|
|
+// 获取状态对应标签类型
|
|
|
+const getStatusType = (status: string) => {
|
|
|
+ const map: Record<string, string> = {
|
|
|
+ '已支付': 'primary',
|
|
|
+ '待发货': 'warning',
|
|
|
+ '已完成': 'success',
|
|
|
+ '待支付': 'danger'
|
|
|
+ };
|
|
|
+ return map[status] || ('info' as any);
|
|
|
+};
|
|
|
+
|
|
|
+// 排行榜进度条颜色
|
|
|
+const getProgressColor = (index: number) => {
|
|
|
+ if (index === 0) return '#f56c6c';
|
|
|
+ if (index === 1) return '#e6a23c';
|
|
|
+ if (index === 2) return '#409eff';
|
|
|
+ return '#c8c9cc';
|
|
|
};
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
.home {
|
|
|
- blockquote {
|
|
|
- padding: 10px 20px;
|
|
|
- margin: 0 0 20px;
|
|
|
- font-size: 17.5px;
|
|
|
- border-left: 5px solid #eee;
|
|
|
- }
|
|
|
- hr {
|
|
|
- margin-top: 20px;
|
|
|
- margin-bottom: 20px;
|
|
|
- border: 0;
|
|
|
- border-top: 1px solid #eee;
|
|
|
- }
|
|
|
- .col-item {
|
|
|
- margin-bottom: 20px;
|
|
|
- }
|
|
|
+ padding: 20px;
|
|
|
+ background-color: #f0f2f5;
|
|
|
+ min-height: calc(100vh - 84px);
|
|
|
|
|
|
- ul {
|
|
|
- padding: 0;
|
|
|
- margin: 0;
|
|
|
+ .dashboard-header {
|
|
|
+ margin-bottom: 24px;
|
|
|
+ h2 {
|
|
|
+ margin: 0 0 10px 0;
|
|
|
+ font-size: 24px;
|
|
|
+ color: #303133;
|
|
|
+ font-weight: 600;
|
|
|
+ letter-spacing: 1px;
|
|
|
+ }
|
|
|
+ p {
|
|
|
+ margin: 0;
|
|
|
+ font-size: 14px;
|
|
|
+ color: #909399;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- font-family: 'open sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
|
|
- font-size: 13px;
|
|
|
- color: #676a6c;
|
|
|
- overflow-x: hidden;
|
|
|
+ .panel-group {
|
|
|
+ margin-bottom: 24px;
|
|
|
|
|
|
- ul {
|
|
|
- list-style-type: none;
|
|
|
- }
|
|
|
+ .card-panel-col {
|
|
|
+ margin-bottom: 20px;
|
|
|
+ }
|
|
|
|
|
|
- h4 {
|
|
|
- margin-top: 0px;
|
|
|
- }
|
|
|
+ .data-card {
|
|
|
+ border-radius: 8px;
|
|
|
+ border: none;
|
|
|
+ transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
|
|
|
|
|
|
- h2 {
|
|
|
- margin-top: 10px;
|
|
|
- font-size: 26px;
|
|
|
- font-weight: 100;
|
|
|
- }
|
|
|
+ &:hover {
|
|
|
+ transform: translateY(-5px);
|
|
|
+ box-shadow:
|
|
|
+ 0 14px 28px rgba(0, 0, 0, 0.05),
|
|
|
+ 0 10px 10px rgba(0, 0, 0, 0.02);
|
|
|
+ }
|
|
|
|
|
|
- p {
|
|
|
- margin-top: 10px;
|
|
|
+ .card-header {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ margin-bottom: 15px;
|
|
|
|
|
|
- b {
|
|
|
- font-weight: 700;
|
|
|
- }
|
|
|
- }
|
|
|
+ .card-title {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #606266;
|
|
|
+ font-weight: bold;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- .update-log {
|
|
|
- ol {
|
|
|
- display: block;
|
|
|
- list-style-type: decimal;
|
|
|
- margin-block-start: 1em;
|
|
|
- margin-block-end: 1em;
|
|
|
- margin-inline-start: 0;
|
|
|
- margin-inline-end: 0;
|
|
|
- padding-inline-start: 40px;
|
|
|
- }
|
|
|
- }
|
|
|
- .index-style {
|
|
|
- font-size: 48px;
|
|
|
- font-weight: bold;
|
|
|
- letter-spacing: 15px;
|
|
|
- display: flex;
|
|
|
- justify-content: center;
|
|
|
- align-items: center;
|
|
|
- height: 300px;
|
|
|
- background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
|
|
- border-radius: 10px;
|
|
|
- box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
|
|
|
-
|
|
|
- .typewriter-container {
|
|
|
- display: flex;
|
|
|
- }
|
|
|
+ .card-value {
|
|
|
+ font-size: 32px;
|
|
|
+ font-weight: 700;
|
|
|
+ color: #303133;
|
|
|
+ margin-bottom: 15px;
|
|
|
+ font-family: 'Arial', sans-serif;
|
|
|
+ }
|
|
|
|
|
|
- .typewriter-char {
|
|
|
- opacity: 0;
|
|
|
- transform: translateY(20px) rotate(-5deg);
|
|
|
- animation:
|
|
|
- typewriter-animation 0.8s ease forwards,
|
|
|
- pulse 2s ease-in-out infinite 1s;
|
|
|
- color: #2d8cf0;
|
|
|
- text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
|
|
|
- transition: color 0.3s ease;
|
|
|
+ .card-desc {
|
|
|
+ font-size: 13px;
|
|
|
+ color: #909399;
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
|
|
|
- &:hover {
|
|
|
- color: #f06292;
|
|
|
- transform: scale(1.1);
|
|
|
- animation-play-state: paused;
|
|
|
+ .trend-up {
|
|
|
+ color: #67c23a;
|
|
|
+ font-weight: bold;
|
|
|
+ margin-left: 5px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .trend-down {
|
|
|
+ color: #f56c6c;
|
|
|
+ font-weight: bold;
|
|
|
+ margin-left: 5px;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- @keyframes typewriter-animation {
|
|
|
- 0% {
|
|
|
- opacity: 0;
|
|
|
- transform: translateY(20px) rotate(-5deg);
|
|
|
+ .customer-card .card-value {
|
|
|
+ color: #409eff;
|
|
|
+ }
|
|
|
+ .order-card .card-value {
|
|
|
+ color: #67c23a;
|
|
|
}
|
|
|
- 50% {
|
|
|
- opacity: 0.5;
|
|
|
- transform: translateY(10px) rotate(-2deg);
|
|
|
+ .product-card .card-value {
|
|
|
+ color: #e6a23c;
|
|
|
}
|
|
|
- 100% {
|
|
|
- opacity: 1;
|
|
|
- transform: translateY(0) rotate(0deg);
|
|
|
+ .revenue-card .card-value {
|
|
|
+ color: #f56c6c;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- @keyframes pulse {
|
|
|
- 0% {
|
|
|
- text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
|
|
|
- transform: scale(1);
|
|
|
+ .content-row {
|
|
|
+ margin-bottom: 20px;
|
|
|
+
|
|
|
+ .card-panel-col {
|
|
|
+ margin-bottom: 20px;
|
|
|
}
|
|
|
- 50% {
|
|
|
- text-shadow:
|
|
|
- 0 0 15px rgba(45, 140, 240, 0.8),
|
|
|
- 0 0 30px rgba(45, 140, 240, 0.4);
|
|
|
- transform: scale(1.05);
|
|
|
+
|
|
|
+ .section-title {
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #303133;
|
|
|
+ position: relative;
|
|
|
+ padding-left: 12px;
|
|
|
+
|
|
|
+ &::before {
|
|
|
+ content: '';
|
|
|
+ position: absolute;
|
|
|
+ left: 0;
|
|
|
+ top: 2px;
|
|
|
+ bottom: 2px;
|
|
|
+ width: 4px;
|
|
|
+ background-color: #409eff;
|
|
|
+ border-radius: 2px;
|
|
|
+ }
|
|
|
}
|
|
|
- 100% {
|
|
|
- text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
|
|
|
- transform: scale(1);
|
|
|
+
|
|
|
+ .table-card {
|
|
|
+ border-radius: 8px;
|
|
|
+ border: none;
|
|
|
+ height: 100%;
|
|
|
+ }
|
|
|
+
|
|
|
+ .list-card {
|
|
|
+ border-radius: 8px;
|
|
|
+ border: none;
|
|
|
+ height: 100%;
|
|
|
+
|
|
|
+ .product-ranking {
|
|
|
+ padding: 5px 0;
|
|
|
+
|
|
|
+ .ranking-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ margin-bottom: 20px;
|
|
|
+
|
|
|
+ &:last-child {
|
|
|
+ margin-bottom: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ranking-index {
|
|
|
+ width: 25px;
|
|
|
+ height: 25px;
|
|
|
+ line-height: 25px;
|
|
|
+ text-align: center;
|
|
|
+ background-color: #f4f4f5;
|
|
|
+ color: #909399;
|
|
|
+ border-radius: 50%;
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: bold;
|
|
|
+ margin-right: 15px;
|
|
|
+ flex-shrink: 0;
|
|
|
+
|
|
|
+ &.top-three {
|
|
|
+ background-color: #314659;
|
|
|
+ color: #fff;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .ranking-info {
|
|
|
+ flex: 1;
|
|
|
+ margin-right: 15px;
|
|
|
+ overflow: hidden;
|
|
|
+
|
|
|
+ .product-name {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #303133;
|
|
|
+ margin-bottom: 5px;
|
|
|
+ white-space: nowrap;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ }
|
|
|
+
|
|
|
+ .product-sales {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #909399;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .ranking-progress {
|
|
|
+ width: 80px;
|
|
|
+ flex-shrink: 0;
|
|
|
+ margin-top: 5px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|