feat: 首页静态页面

fix-0609-xwk
向文可 2 years ago
parent c9fb1fa5b8
commit 35154fdc58

6798
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -21,6 +21,7 @@
"axios": "^0.26.1",
"china-area-data": "^5.0.1",
"dayjs": "^1.11.0",
"echarts": "^5.3.2",
"element-plus": "^2.1.7",
"lodash": "^4.17.21",
"qs": "^6.10.3",

@ -0,0 +1,249 @@
import * as api from '@/api/system/notify.js';
import { ElMessage, ElMessageBox } from '@/plugins/element-plus';
const state = () => ({
opts: {
init: false,
summary: [
{
label: '今日访客数',
value: 1526,
icon: 'bar-chart',
},
{
label: '今日订单数',
value: 200,
icon: 'file-list',
},
{
label: '今日销售额',
value: 5000,
icon: 'money-cny-box',
},
{
label: '近7天销售总额',
value: 50000,
icon: 'line-chart',
},
],
order: [
{
label: '待付款订单',
value: '10',
route: {
name: 'OrderManagement',
params: {
status: 1,
},
},
},
{
label: '待处理退款申请',
value: '10',
route: {
name: 'ServiceManagement',
params: {
status: 1,
},
},
},
{
label: '待发货订单',
value: '10',
route: {
name: 'OrderManagement',
params: {
status: 1,
},
},
},
{
label: '待确认退货订单',
value: '10',
route: {
name: 'ServiceManagement',
params: {
status: 1,
},
},
},
{
label: '已发货订单',
value: '10',
route: {
name: 'OrderManagement',
params: {
status: 1,
},
},
},
],
teleport: [
{
label: '添加商品',
value: 'product-hunt',
route: {
name: 'CreateProduct',
},
},
{
label: '订单列表',
value: 'money-cny-circle',
route: {
name: 'OrderManagement',
},
},
{
label: '秒杀管理',
value: 'auction',
route: {
name: 'LimitActivity',
},
},
{
label: '消息通知',
value: 'notification-3',
route: {
name: 'NotifyManagement',
},
},
{
label: '广告管理',
value: 'advertisement',
route: {
name: 'AdvertiseManagement',
},
},
],
product: [
{
label: '已上架',
value: 100,
},
{
label: '未上架',
value: 400,
},
{
label: '库存紧张',
value: 50,
},
{
label: '全部商品',
value: 500,
},
],
customer: [
{
label: '今日新增',
value: 100,
},
{
label: '昨日新添',
value: 200,
},
{
label: '本月新增',
value: 1000,
},
{
label: '用户总数',
value: 5000,
},
],
},
chart: [
[
{ label: '1月', value: 152 },
{ label: '2月', value: 215 },
{ label: '3月', value: 198 },
{ label: '4月', value: 348 },
{ label: '5月', value: 111 },
],
[
{ label: '1月', value: 152 },
{ label: '2月', value: 215 },
{ label: '3月', value: 198 },
{ label: '4月', value: 348 },
{ label: '5月', value: 111 },
],
[
{ label: '1月', value: 152 },
{ label: '2月', value: 215 },
{ label: '3月', value: 198 },
{ label: '4月', value: 348 },
{ label: '5月', value: 111 },
],
],
});
const getters = {};
const mutations = {
setCode: (state, data) => (state.code = data),
setCondition: (state, data) => (state.condition = data),
setList: (state, data) => (state.list = data),
setTotal: (state, data) => (state.total = data),
setOpts: (state, data) => (state.opts = data),
};
const actions = {
search: async ({ state, commit, rootGetters }) => {
let data = { ...state.condition };
data.startReleaseTime = data.dateRange?.[0];
data.endReleaseTime = data.dateRange?.[1];
delete data.dateRange;
let res = await api.search({ ...rootGetters['local/page'](state.code), ...state.condition });
if (res) {
commit('setList', res.records);
commit('setTotal', res.total);
} else {
ElMessage.error('查询失败');
commit('setList', []);
}
return res;
},
load: async ({ commit }) => {
commit('setOpts', {
init: true,
});
},
detail: async (context, id) => {
let res = await api.detail(id);
if (!res) {
ElMessage.error('加载详情失败');
}
return res;
},
save: async ({ dispatch }, data) => {
let save = data.id ? api.update : api.create;
let res = await save(data);
if (res) {
ElMessage.success('保存成功');
dispatch('search');
} else {
ElMessage.error('保存失败');
}
return res;
},
remove: async ({ dispatch }, idList) => {
if (!idList.length) {
ElMessage.warning('请选择要删除的数据');
} else {
try {
await ElMessageBox.confirm('数据删除后无法恢复,确定要删除吗?', '危险操作');
let res = await api.remove(idList.join(','));
if (res) {
ElMessage.success('删除成功');
dispatch('search');
} else {
ElMessage.error('删除失败');
}
} catch (e) {
console.info('取消删除', e);
}
}
},
};
export default {
state,
getters,
mutations,
actions,
};

@ -1,7 +1,392 @@
<template>
<div class="container">欢迎使用马士兵管理平台</div>
<div class="container">
<div class="flex row row-1">
<el-card v-for="(item, index) in opts.summary" :key="index">
<div class="flex">
<div class="text">
<p class="label">{{ item.label }}</p>
<p class="value">{{ item.value }}</p>
</div>
<el-icon :name="item.icon" :size="54" />
</div>
</el-card>
</div>
<el-card class="row row-2" header="待处理事务">
<ul>
<li v-for="(item, index) in opts.order" :key="index" @click="handleRoute(item.route)">
<span class="label">
{{ item.label }}
</span>
<span class="value">
(
<span class="red">
{{ item.value }}
</span>
)
</span>
</li>
</ul>
</el-card>
<el-card class="row row-3" header="运营快捷入口">
<ul>
<li v-for="(item, index) in opts.teleport" :key="index" @click="handleRoute(item.route)">
<p class="value">
<el-icon :name="item.value" :size="54" />
</p>
<p class="label">
{{ item.label }}
</p>
</li>
</ul>
</el-card>
<div class="flex row row-4">
<el-card header="商品总览">
<ul>
<li v-for="(item, index) in opts.product" :key="index" @click="handleRoute(item.route)">
<p class="value red">
{{ item.value }}
</p>
<p class="label">
{{ item.label }}
</p>
</li>
</ul>
</el-card>
<el-card header="用户总览">
<ul>
<li v-for="(item, index) in opts.customer" :key="index" @click="handleRoute(item.route)">
<p class="value red">
{{ item.value }}
</p>
<p class="label">
{{ item.label }}
</p>
</li>
</ul>
</el-card>
</div>
<el-card class="row row-5" header="访客统计">
<div class="flex">
<div class="left">
<div class="text">
<p class="label">本周访客数量</p>
<p class="value">1000</p>
<p class="rate">
<span class="down">10%</span>
<span class="desc">同比上周</span>
</p>
</div>
<div class="text">
<p class="label">本月访客数量</p>
<p class="value">10000</p>
<p class="rate">
<span class="up">10%</span>
<span class="desc">同比上月</span>
</p>
</div>
</div>
<div class="right">
<div class="header flex">
<p class="title">近一周访客统计</p>
<div class="condition">
<el-date-picker type="daterange" />
</div>
</div>
<div class="chart"></div>
</div>
</div>
</el-card>
<el-card class="row row-6" header="订单统计">
<div class="flex">
<div class="left">
<div class="text">
<p class="label">本周订单数量</p>
<p class="value">1000</p>
<p class="rate">
<span class="down">10%</span>
<span class="desc">同比上周</span>
</p>
</div>
<div class="text">
<p class="label">本月订单总数</p>
<p class="value">10000</p>
<p class="rate">
<span class="up">10%</span>
<span class="desc">同比上月</span>
</p>
</div>
</div>
<div class="right">
<div class="header flex">
<p class="title">近一周订单统计</p>
<div class="condition">
<el-date-picker type="daterange" />
</div>
</div>
<div class="chart"></div>
</div>
</div>
</el-card>
<el-card class="row row-7" header="销售统计">
<div class="flex">
<div class="left">
<div class="text">
<p class="label">本周销售总额</p>
<p class="value">1000</p>
<p class="rate">
<span class="down">10%</span>
<span class="desc">同比上周</span>
</p>
</div>
<div class="text">
<p class="label">本月销售总额</p>
<p class="value">10000</p>
<p class="rate">
<span class="up">10%</span>
<span class="desc">同比上月</span>
</p>
</div>
</div>
<div class="right">
<div class="header flex">
<p class="title">近一周销售统计</p>
<div class="condition">
<el-date-picker type="daterange" />
</div>
</div>
<div class="chart"></div>
</div>
</div>
</el-card>
</div>
</template>
<script setup></script>
<script setup>
import * as echarts from 'echarts';
const router = useRouter();
const store = useStore();
const opts = computed(() => store.state.home.opts);
const chartData = computed(() => store.state.home.chart);
const handleRoute = (route) => router.push(route);
const handleChart = (selector, color, name, data) => {
const chart = echarts.init(document.querySelector(selector));
const option = {
color: color,
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
},
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true,
},
xAxis: [
{
type: 'category',
boundaryGap: false,
data: data.map((item) => item.label),
},
],
yAxis: [
{
type: 'value',
},
],
series: [
{
name,
type: 'line',
stack: 'Total',
smooth: true,
lineStyle: {
width: 0,
},
showSymbol: false,
areaStyle: {
opacity: 0.8,
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: 'rgb(128, 255, 165)',
},
{
offset: 1,
color: 'rgb(1, 191, 236)',
},
]),
},
emphasis: {
focus: 'series',
},
data: data.map((item) => item.value),
},
],
};
chart.setOption(option);
};
onMounted(() => {
handleChart('.row-5 .chart', '#80FFA5', '访客数量', unref(chartData)[0]);
handleChart('.row-6 .chart', '#00DDFF', '订单数量', unref(chartData)[1]);
handleChart('.row-7 .chart', '#37A2FF', '销售额', unref(chartData)[2]);
});
</script>
<style lang="less" scoped></style>
<style lang="less" scoped>
@big-font: 30px;
.container {
height: auto !important;
.flex {
display: flex;
}
.red {
color: var(--el-color-danger);
}
.el-card {
flex: 1;
:deep(.el-card__header) {
padding: @layout-space;
font-weight: bolder;
}
:deep(.el-card__body) {
padding: @layout-space;
}
}
.row {
& + .row {
margin-top: @layout-space;
}
.el-card {
& + .el-card {
margin-left: @layout-space;
}
}
&-1 {
.flex {
justify-content: space-around;
align-items: center;
}
.x-icon {
color: var(--el-color-success);
}
}
&-2 {
ul {
display: flex;
flex-wrap: wrap;
li {
width: 45%;
padding: @layout-space 3%;
margin: 0 2%;
border-bottom: 1px solid #eee;
cursor: pointer;
display: flex;
justify-content: space-between;
&:hover {
background: #eee;
color: var(--el-color-primary);
}
}
}
}
&-3 {
ul {
display: flex;
li {
flex: 1;
padding: @layout-space 3%;
margin: 0 2%;
cursor: pointer;
display: flex;
flex-direction: column;
align-items: center;
&:hover {
background: #eee;
color: var(--el-color-primary);
}
.red {
color: var(--el-color-danger);
}
}
}
}
&-4 {
ul {
display: flex;
justify-content: space-evenly;
li {
flex: 1;
padding: @layout-space 3%;
margin: 0 2%;
display: flex;
flex-direction: column;
align-items: center;
}
}
.value {
font-size: @big-font;
}
}
&-5,
&-6,
&-7 {
.left {
border-right: 1px solid #eee;
margin-right: @layout-space-super;
padding-right: @layout-space-super;
.text {
& + .text {
margin-top: @layout-space-super;
}
.label {
font-size: 14px;
color: @color-white3;
}
.desc {
font-size: 12px;
color: @color-white2;
margin-left: @layout-space;
}
.value {
font-size: @big-font;
}
.down {
color: var(--el-color-success);
&::before {
content: '▼';
font-size: 12px;
position: relative;
top: -4px;
}
}
.up {
color: var(--el-color-danger);
&::before {
content: '▲';
font-size: 12px;
position: relative;
top: -4px;
}
}
}
}
.right {
flex: 1;
.header {
margin-bottom: @layout-space;
justify-content: space-between;
align-items: center;
}
.chart {
width: 100%;
height: 300px;
background: #eee;
}
}
}
}
}
</style>

Loading…
Cancel
Save