现在我们继续来进行后台主控台的开发和交互 首先是统计面板组件的开发: 首先需要拿到数据,就从提供的接口拿: 在api文件夹下创建一个index.js
import axios from '~/axios'
export function getStatistics1(){
return axios.get("/admin/statistics1")
}
然后再编辑Index.vue
<template>
<div>
<el-row :gutter="20">
<!-- 每块6个 -->
<el-col :span="6" :offset="0" v-for="(item, index) in panels" :key="index">
<!-- 卡片组件 -->
<!-- 去掉边框 -->
<el-card shadow="hover" class="border-0">
<template #header>
<!-- 利用flex布局的左右布局justify-between -->
<div class="flex justify-between">
<!-- 字体大小改为text-sm -->
<span class="text-sm">{{ item.title }}</span>
<!-- 后端返回回来的颜色值是unitColor -->
<el-tag :type="item.unitColor" effect="plain">
<!-- 后端返回的值(年) -->
{{ item.unit }}
</el-tag>
</div>
</template>
<!-- card body -->
<!-- 加大字体,加粗,灰色 -->
<span class="text-3xl font-bold text-gray-500">
<!-- 数值 -->
{{ item.value }}
</span>
<!-- 分割线 -->
<el-divider />
<!-- 字体缩小,灰色 -->
<div class="flex justify-between text-sm text-gray-500">
<span>{{ item.subTitle }}</span>
<span>{{ item.subValue }}</span>
</div>
</el-card>
</el-col>
</el-row>
</div>
</template>
<!-- 响应式api ,ref,一个变量响应式,普通类型,script里面count.value,template里面直接{{count}}-->
<!-- 响应式api , reactive,用于对象 script里面form.count++,template里面直接{{form.count}}-->
<script setup>
import { ref } from 'vue'
import { getStatistics1 } from "~/api/index.js"
const panels = ref([])
getStatistics1()
.then(res => {
panels.value = res.panels
})
</script>
主要是用到了卡片组件以及layout布局,因为element plus提供的layout布局默认是占24格,而我们第一层数据面板有四条数据,所以我们就是每一块占6个,所以<el-col :span="6" :offset="0" v-for="(item, index) in panels" :key="index"> 里面的:span里面的值是6.看看效果: 拿到了数据。
然后我们利用骨架屏优化体验: 这里我们要用到element提供给我们的一个骨架屏组件: skeleton骨架屏 在index.vue里面加上骨架屏:
<template>
<div>
<el-row :gutter="20">
<!-- 只有当内容全部为0的时候,才显示骨架屏 -->
<template v-if="panels.length == 0">
<!-- 骨架屏 -->
<el-col :span="6" v-for="i in 4" :key="i">
<el-skeleton style="width:100%;" animated loading>
<template #template>
<!-- 卡片组件 -->
<!-- 去掉边框 -->
<el-card shadow="hover" class="border-0">
<template #header>
<!-- 利用flex布局的左右布局justify-between -->
<div class="flex justify-between">
<!-- 字体大小改为text-sm -->
<el-skeleton-item variant="text" style="width: 50%" />
<!-- 后端返回回来的颜色值是unitColor -->
<el-skeleton-item variant="text" style="width: 10%" />
</div>
</template>
<!-- card body -->
<!-- 加大字体,加粗,灰色 -->
<el-skeleton-item variant="h3" style="width: 80%" />
<!-- 分割线 -->
<el-divider />
<!-- 字体缩小,灰色 -->
<div class="flex justify-between text-sm text-gray-500">
<el-skeleton-item variant="text" style="width: 50%" />
<el-skeleton-item variant="text" style="width: 10%" />
</div>
</el-card>
<el-skeleton-item variant="text" style="width: 30%" />
</template>
</el-skeleton>
</el-col>
</template>
<!-- 内容区域 -->
<!-- 每块6个 -->
<el-col :span="6" :offset="0" v-for="(item, index) in panels" :key="index">
<!-- 卡片组件 -->
<!-- 去掉边框 -->
<el-card shadow="hover" class="border-0">
<template #header>
<!-- 利用flex布局的左右布局justify-between -->
<div class="flex justify-between">
<!-- 字体大小改为text-sm -->
<span class="text-sm">{{ item.title }}</span>
<!-- 后端返回回来的颜色值是unitColor -->
<el-tag :type="item.unitColor" effect="plain">
<!-- 后端返回的值(年) -->
{{ item.unit }}
</el-tag>
</div>
</template>
<!-- card body -->
<!-- 加大字体,加粗,灰色 -->
<span class="text-3xl font-bold text-gray-500">
<!-- 数值 -->
{{ item.value }}
</span>
<!-- 分割线 -->
<el-divider />
<!-- 字体缩小,灰色 -->
<div class="flex justify-between text-sm text-gray-500">
<span>{{ item.subTitle }}</span>
<span>{{ item.subValue }}</span>
</div>
</el-card>
</el-col>
</el-row>
</div>
</template>
<!-- 响应式api ,ref,一个变量响应式,普通类型,script里面count.value,template里面直接{{count}}-->
<!-- 响应式api , reactive,用于对象 script里面form.count++,template里面直接{{form.count}}-->
<script setup>
import { ref } from 'vue'
import { getStatistics1 } from "~/api/index.js"
const panels = ref([])
getStatistics1()
.then(res => {
panels.value = res.panels
})
</script>
于是现在当面板数据没加载出来的时候会显示骨架屏,即loading效果。
接着来进行数字滚动动画的实现: 要用到gsap的第三方库,
npm i gsap
单独封装这个,在component文件夹下新建一个CountTo.vue
<template>
{{ d.num.toFixed(0) }}
</template>
<script setup>
import gsap from 'gsap'
import { reactive, watch } from 'vue'
const props = defineProps({
value:{
type:Number,
default: 0
}
})
const d = reactive({
num:0
})
function AnimateToValue(){
gsap.to(d,{
duration:0.5,
num:props.value
})
}
AnimateToValue()
watch(()=>props.value,()=>AnimateToValue())
</script>
然后在index.vue导入 然后在原本{{ item.value }}的地方用CountTou子组件替换一下:
然后我们继续来进行分类组件的开发和跳转: 在components文件夹下面创建indexNavs.vue
<template>
<!-- 距离上面有个外间距 -->
<el-row :gutter="20" class="mt-5">
<el-col :span="3" :offset="0" v-for="(item,index) in iconNavs" :key="index">
<!-- 卡片组件 -->
<el-card shadow="hover" @click="$router.push(item.path)">
<div class="flex flex-col items-center justify-center cursor-pointer">
<el-icon size="16" :class="item.color">
<!-- 动态组件 -->
<component :is="item.icon"></component>
</el-icon>
<span class="text-sm mt-2">{{ item.title }}</span>
</div>
</el-card>
</el-col>
</el-row>
</template>
<script setup>
const iconNavs = [
{
icon:"user",
color:"text-light-blue-500",
title:"用户",
path:"/user/list"
},
{
icon:"shopping-cart",
color:"text-violet-500",
title:"商品",
path:"/goods/list"
},
{
icon:"tickets",
color:"text-fuchsia-500",
title:"订单",
path:"/order/list"
},
{
icon:"chat-dot-square",
color:"text-teal-500",
title:"评价",
path:"/comment/list"
},
{
icon:"picture",
color:"text-rose-500",
title:"图库",
path:"/image/list"
},
{
icon:"bell",
color:"text-green-500",
title:"公告",
path:"/notice/list"
},
{
icon:"set-up",
color:"text-grey-500",
title:"配置",
path:"/setting/base"
},
{
icon:"files",
color:"text-yellow-500",
title:"优惠券",
path:"/coupon/list"
}
]
</script>
这就是那第二行的一串图标。 然后跳转的话就上面代码里加了@click="$router.push(item.path)"
然后再在pages文件夹下面创建每一个页面的文件夹以及vue文件,并写上template,像这样 然后配置路由,在router文件夹下面的Index.js
import { createRouter, createWebHashHistory } from "vue-router";
import Index from "~/pages/index.vue";
import Login from "~/pages/login.vue";
import NotFound from "~/pages/404.vue";
import GoodList from "~/pages/goods/list.vue";
import CategoryList from "~/pages/category/list.vue";
import Admin from "~/layouts/admin.vue";
import UserList from "~/pages/user/list.vue";
import OrderList from "~/pages/order/list.vue";
import CommentList from "~/pages/comment/list.vue";
import ImageList from "~/pages/image/list.vue";
import NoticeList from "~/pages/notice/list.vue";
import SettingBase from "~/pages/setting/base.vue";
import CouponList from "~/pages/coupon/list.vue";
const routes = [
{
path: "/",
name: "admin",
component: Admin,
},
{
path: "/login",
component: Login,
meta: {
title: "登录页",
},
},
{
path: "/:pathMatch(.*)*",
name: "NotFound",
component: NotFound,
},
];
const asyncRoutes = [
{
path: "/",
name: "/",
component: Index,
meta: {
title: "后台首页",
},
},
{
path: "/goods/list",
name: "/goods/list",
component: GoodList,
meta: {
title: "商品管理",
},
},
{
path: "/category/list",
name: "/category/list",
component: CategoryList,
meta: {
title: "分类列表",
},
},
{
path: "/user/list",
name: "/user/list",
component: UserList,
meta: {
title: "用户列表",
},
},
{
path: "/order/list",
name: "/order/list",
component: OrderList,
meta: {
title: "订单列表",
},
},
{
path: "/comment/list",
name: "/comment/list",
component: CommentList,
meta: {
title: "评价列表",
},
},
{
path: "/image/list",
name: "/image/list",
component: ImageList,
meta: {
title: "图库列表",
},
},
{
path: "/notice/list",
name: "/notice/list",
component: NoticeList,
meta: {
title: "公告列表",
},
},
{
path: "/setting/base",
name: "/setting/base",
component: SettingBase,
meta: {
title: "配置",
},
},
{
path: "/coupon/list",
name: "/coupon/list",
component: CouponList,
meta: {
title: "优惠券列表",
},
},
];
export const router = createRouter({
history: createWebHashHistory(),
routes,
});
export function addRoutes(menus) {
let hasNewRoutes = false;
const findAndAddRouteByMenus = (arr) => {
arr.forEach((e) => {
let item = asyncRoutes.find((o) => o.path == e.frontpath);
if (item && !router.hasRoute(item.path)) {
router.addRoute("admin", item);
hasNewRoutes = true;
}
if (e.child && e.child.length > 0) {
findAndAddRouteByMenus(e.child);
}
});
};
findAndAddRouteByMenus(menus);
return hasNewRoutes;
}
这样就能实现分类组件的开发以及每个分类页面的跳转了。 然后我们继续来进行echarts图表组件的开发和交互。 先安装
npm install echarts
在components文件夹下创建IndexChart.vue,然后主要就是进行echart里面的柱状图的调用,传给其x轴和y轴的值
<template>
<el-card shadow="never">
<template #header>
<div class="flex justify-between">
<span class="text-sm">订单统计</span>
<div>
<!-- tag标签里面的可选中标签 -->
<!-- :checked是动态的是否选中 -->
<el-check-tag v-for="(item, index) in options" :key="index" :checked="current == item.value" checked
style="margin-right: 8px" @click="handleChoose(item.value)">
{{ item.text }}
</el-check-tag>
</div>
</div>
</template>
<div ref="el" id="chart" style="width: 100%; height: 300px;">
</div>
</el-card>
</template>
<script setup>
import * as echarts from 'echarts';
import { ref, onMounted, onBeforeUnmount } from 'vue'
import { getStatistics3 } from '~/api/index.js'
import { useResizeObserver } from '@vueuse/core'
const current = ref("week")
const options = [{
text: "近一个月",
value: "month"
}, {
text: "近一周",
value: "week"
}, {
text: "近24小时",
value: "hour"
},]
const handleChoose = (type) => {
current.value = type
getData()
}
var myChart = null
onMounted(() => {
var chartDom = document.getElementById('chart');
myChart = echarts.init(chartDom);
getData()
})
onBeforeUnmount(() => {
if (myChart) echarts.dispose(myChart)
})
function getData() {
let option = {
xAxis: {
type: 'category',
data: []
},
yAxis: {
type: 'value'
},
series: [
{
data: [],
type: 'bar',
showBackground: true,
backgroundStyle: {
color: 'rgba(180, 180, 180, 0.2)'
}
}
]
};
myChart.showLoading()
getStatistics3(current.value).then(res => {
option.xAxis.data = res.x
option.series[0].data = res.y
myChart.setOption(option)
}).finally(() => {
myChart.hideLoading()
})
}
const el = ref(null)
useResizeObserver(el, (entries) => {
myChart.resize()
})
</script>
在api文件夹下的index.js写echart的接口:
import axios from '~/axios'
export function getStatistics1(){
return axios.get("/admin/statistics1")
}
export function getStatistics3(type){
return axios.get("/admin/statistics3?type="+type)
}
在index.vue里调用 到此,echart部分就开发结束了,来看目前效果:
然后我们继续店铺和交易提示组件开发与交互。 首先api文件夹下面的index.js里面:
import axios from '~/axios'
export function getStatistics1(){
return axios.get("/admin/statistics1")
}
export function getStatistics2(){
return axios.get("/admin/statistics2")
}
export function getStatistics3(type){
return axios.get("/admin/statistics3?type="+type)
}
然后在components下面创建IndexCard.vue 然后
<template>
<el-card shadow="never">
<template #header>
<!-- 利用flex布局的左右布局justify-between -->
<div class="flex justify-between">
<!-- 字体大小改为text-sm -->
<span class="text-sm">{{ title }}</span>
<!-- 颜色值是红色danger -->
<el-tag :type="danger" effect="plain">
<!-- 后端返回的值 -->
{{ tip }}
</el-tag>
</div>
</template>
<!-- card body -->
<el-row :gutter="20">
<el-col :span="6" :offset="0" v-for="(item,index) in btns" :key="index">
<el-card shadow="hover" class="border-0 bg-light-400">
<div class="flex flex-col items-center justify-center">
<span class="text-xl mb-2">{{ item.value }}</span>
<span class="text-xs text-gray-500">{{ item.label }}</span>
</div>
</el-card>
</el-col>
</el-row>
</el-card>
</template>
<script setup>
defineProps({
title: String,
tip: String,
btns: Array
})
</script>
在index.vue里面:加上店铺部分和交易相关 然后运行结果:完全没有问题 到此为止,我们的主页部分就全部开发完了,撒花!明天开始会继续其他部分的开发,大家一起加油啊!
项目gitee地址
|