从零开始使用vue2+element搭建后台管理系统(框架布局+面包屑+内容页加载实现)
先在components下分别创建侧边栏、顶部、布局等组件,用于全局配置:
CommonAside.vue
<template>
<el-menu
class="el-menu-vertical"
background-color="#282e38"
text-color="#f4f4f5"
active-text-color="#f57d2d"
:default-active="activeMenu"
:router="true"
:collapse="isCollapse"
@open="handleOpen"
@close="handleClose"
>
<!-- :background-color="global.menuBg" -->
<!-- :text-color="global.menuText" -->
<!-- :active-text-color="global.menuActiveText" -->
<el-menu-item
@click="clickItem(item)"
v-for="item in noChildren"
:key="item.name"
:index="item.path"
>
<i :class="`el-icon-${item.icon}`"></i>
<span slot="title">{{ item.label }}</span>
</el-menu-item>
<el-submenu
v-for="item in hasChildren"
:key="item.label"
:index="item.path"
>
<template slot="title">
<i :class="`el-icon-${item.icon}`"></i>
<span slot="title">{{ item.label }}</span>
</template>
<el-menu-item-group v-for="subItem in item.children" :key="subItem.name">
<el-menu-item @click="clickItem(subItem)" :index="subItem.path">{{
subItem.label
}}</el-menu-item>
</el-menu-item-group>
</el-submenu>
</el-menu>
</template>
<style scoped lang="scss">
@import "@/assets/css/global.scss";
.el-menu-vertical:not(.el-menu--collapse) {
width: 210px;
min-height: 400px;
}
.el-menu {
height: 100vh;
border-right: none;
}
.el-menu-item:focus,
.el-menu-item:hover {
outline: 0;
background-color: $menuHover !important;
}
</style>
<script>
import global from "@/assets/css/global.scss";
export default {
data() {
return {};
},
methods: {
handleOpen(key, keyPath) {
console.log(key, keyPath);
},
handleClose(key, keyPath) {
console.log(key, keyPath);
},
clickItem(item) {
// 防止自己跳自己的报错
if (
this.$route.path !== item.path &&
!(this.$route.path === "/home" && item.path === "/")
) {
this.$router.push(item.path);
}
// 面包屑
this.$store.commit("basic/SelectMenu", item);
},
},
computed: {
// 获取菜单
MenuData() {
console.log(this.$store.state.basic.menu, "xxxx");
return this.$store.state.basic.menu;
},
noChildren() {
// 如果没有children则返回true,会被过滤器留下
return this.MenuData.filter((item) => !item.children);
},
hasChildren() {
return this.MenuData.filter((item) => item.children);
},
// 要放到计算属性,自动计算
isCollapse() {
return this.$store.state.basic.isCollapse;
},
// 当前页面
activeMenu() {
console.log(this.$route.path, "-----------this.$route.path");
return this.$route.path;
},
// 通用样式配置
global() {
return global;
},
},
};
</script>
顺手贴一个公用样式文件:

$blue: #324157;
$light-blue: #3a71a8;
$red: #c03639;
$pink: #e65d6e;
$green: #30b08f;
$tiffany: #4ab7bd;
$yellow: #fec171;
$panGreen: #30b08f;
// $menuText: #f4f4f5;
$menuText: #cecece;
$menuActiveText: #f57d2d;
$subMenuActiveText: #f4f4f5;
$menuBg: #282e38;
$menuHover: #3e434c;
$subMenuBg: #c2c2c2;
$subMenuHover: #b6b6b6;
:export {
menuText: $menuText;
menuActiveText: $menuActiveText;
subMenuActiveText: $subMenuActiveText;
menuBg: $menuBg;
menuHover: $menuHover;
subMenuBg: $subMenuBg;
subMenuHover: $subMenuHover;
}
导出后,在main.js中全局引入:

侧边栏因为涉及到左侧菜单,因此需要对菜单进行管理,首先获取用户信息的接口需要返回菜单(假接口):
export function getInfo(mobile) {
console.log(mobile, "mobile");
return {
code: 10000,
msg: "成功",
data: {
name: "WUN",
avatar:
"https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png",
menuList: [
{
path: "/account/account",
name: "user",
label: "烛月账号管理",
icon: "s-custom",
url: "account/AccountView",
},
{
path: "/order/order",
name: "order",
label: "订单管理",
icon: "s-marketing",
url: "order/OrderView",
},
{
path: "/auth/auth",
name: "auth",
label: "权限管理",
icon: "s-tools",
url: "auth/AuthView",
},
],
},
};
}
然后登录成功后,获取用户信息时进行存储:
// 获取用户信息
async getUserInfo(mobile) {
try {
this.loading = true;
const res = await getInfo(mobile);
if (res.code !== 10000)
this.$message.error(res.msg || "获取用户信息失败!");
if (res.code === 10000 && res.data) {
this.$store.commit("user/SET_NAME", res.data.name);
this.$store.commit("user/SET_AVATAR", res.data.avatar);
this.$store.commit("basic/setMenu", res.data.menuList);
setTimeout(() => {
this.$router.push("/home");
});
}
} finally {
this.loading = false;
}
},
这里因为是假接口,所以直接在方法中进行了处理,实际上是在store的actions中进行请求及数据管理的。接下来是store文件夹下basic.js文件的内容:
import Vue from "vue";
import Vuex from "vuex";
import Cookie from "js-cookies";
import { getMenu } from "@/utils/auth";
Vue.use(Vuex);
const getDefaultState = () => {
return {
// 导航栏是否折叠
isCollapse: false,
// 页签数据
tabList: [
{
path: "/",
name: "home",
label: "首页",
icon: "s-home",
url: "Home/Home",
},
],
// 菜单
menu: getMenu(),
};
};
const state = getDefaultState();
const mutations = {
// 修改导航栏展开和收起的方法
CollapseMenu(state) {
state.isCollapse = !state.isCollapse;
},
// 更新页签数据
SelectMenu(state, item) {
// 如果点击的不在页签数据中,则添加
const index = state.tabList.findIndex((val) => val.name === item.name);
if (index === -1) {
state.tabList.push(item);
}
},
// 删除tag:删除tabList中对应的item
closeTag(state, item) {
const index = state.tabList.findIndex((val) => val.name === item.name);
state.tabList.splice(index, 1);
},
// 设置不同用户的菜单
setMenu(state, val) {
state.menu = val;
return Cookie.setItem("menu", JSON.stringify(val));
},
// 动态添加路由
addMenu(state, router) {
// 判断Cookie
if (!Cookie.getItem("menu")) return;
const menu = JSON.parse(Cookie.getItem("menu"));
state.menu = menu;
const menuArray = [];
// 组装路由
menu.forEach((item) => {
// 判断是否有子路由
if (item.children) {
item.children = item.children.map((child) => {
child.component = () => import(`../../views/${child.url}`);
return child;
});
menuArray.push(...item.children);
} else {
item.component = () => import(`../../views/${item.url}`);
menuArray.push(item);
}
});
menuArray.forEach((item) => {
router.addRoute("Main", item);
});
},
};
const actions = {};
export default {
namespaced: true,
state,
mutations,
actions,
};
其中包括获取cookie中缓存的菜单数组,方法在utils/auth下:
export function getMenu() {
const menuStr = Cookie.getItem("menu");
if (menuStr) {
return JSON.parse(menuStr);
} else {
return [];
}
}
因为cookie缓存是转字符串的,因此做一层格式处理。简单的侧边栏就完成咯。
CommonHeader.vue
<template>
<div class="header-container">
<div class="l-content">
<i v-if="isCollapse" class="el-icon-s-unfold" @click="handleMenu"></i>
<i v-else class="el-icon-s-fold" @click="handleMenu"></i>
<img class="header-icon" src="../assets/logo.png" alt="" />
<h3>
{{ isCollapse ? "后台" : "后台管理系统" }}
</h3>
<!-- 面包屑 -->
<el-breadcrumb class="app-breadcrumb" separator="/">
<transition-group name="breadcrumb">
<el-breadcrumb-item
v-for="(item, index) in levelList"
:key="item.path"
>
<span
v-if="
item.redirect === 'noRedirect' || index == levelList.length - 1
"
class="no-redirect"
>{{ item.meta.title }}</span
>
<a v-else @click.prevent="handleLink(item)">{{
item.meta.title
}}</a>
</el-breadcrumb-item>
</transition-group>
</el-breadcrumb>
</div>
<div class="r-content">
<el-dropdown @command="handleClick">
<span class="el-dropdown-link">
<img class="user" src="../assets/logo.png" alt="" />
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>个人信息</el-dropdown-item>
<el-dropdown-item command="logout">退出</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</div>
</template>
<script>
export default {
data() {
return {
levelList: null,
};
},
watch: {
$route() {
this.getBreadcrumb();
},
},
created() {
this.getBreadcrumb();
},
methods: {
// 切换菜单伸缩
handleMenu() {
this.$store.commit("basic/CollapseMenu");
},
// 退出登录
handleClick(command) {
if (command === "logout") {
this.$store.dispatch("user/logout").then(() => {
this.$router.push({
name: "/login",
});
});
}
},
// 获取面包屑
getBreadcrumb() {
// this.$route.matched匹配到一个路由数组
let matched = this.$route.matched.filter(
(item) => item.meta && item.meta.title
);
const first = matched[0];
if (!this.isHome(first)) {
matched = [
{
path: "/home",
meta: { title: "首页", redirect: "/home", path: "/home" },
},
].concat(matched);
}
this.levelList = matched.filter(
(item) => item.meta && item.meta.title && item.meta.breadcrumb !== false
);
},
// 判断是否为主页
isHome(route) {
const name = route && route.name;
if (!name) {
return false;
}
// 对路由进行大小写处理
return name.trim().toLocaleLowerCase() === "home".toLocaleLowerCase();
},
// 转译路由
pathCompile(path) {
// 将字符串转化为正则表达式的方法
var pathToRegexp = require("path-to-regexp");
// 参阅:https://github.com/PanJiaChen/vue-element-admin/issues/561
const { params } = this.$route;
const toPath = pathToRegexp.compile(path);
return toPath(params);
},
// 跳转
handleLink(item) {
const { redirect, path } = item;
if (redirect) {
this.$router.push(redirect);
return;
}
this.$router.push(this.pathCompile(path));
},
},
computed: {
isCollapse() {
return this.$store.state.basic.isCollapse;
},
},
};
</script>
<style lang="scss" scoped>
.header-container {
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
height: 60px;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 20px;
.el-dropdown-link {
cursor: pointer;
color: #409eff;
.user {
width: 40px;
height: 40px;
border-radius: 50%;
}
}
.header-icon {
margin: 0 16px;
height: 48px;
}
h3 {
text-align: center;
line-height: 48px;
color: #ffffff;
font-size: 18px;
font-weight: 600;
color: #454545;
}
}
.l-content {
display: flex;
align-items: center;
cursor: pointer;
.el-breadcrumb {
margin-left: 15px;
.el-breadcrumb__item {
.el-breadcrumb__inner {
&.is-link {
color: #666666;
}
}
&:last-child {
.el-breadcrumb__inner {
color: #ffffff;
}
}
}
}
}
</style>
注意头部因为有面包屑,因此需要导入path-to-regexp插件用来处理 url 中地址与参数:yarn add path-to-regexp
LayoutView.js
<template>
<el-container>
<el-aside width="auto">
</el-aside>
<el-container>
<el-header>
<common-header />
</el-header>
<!-- <common-tags /> -->
<el-main>
<div class="page-container-wn">
<router-view></router-view>
</div>
</el-main>
</el-container>
</el-container>
</template>
<script>
import CommonAside from "../components/CommonAside.vue";
import CommonHeader from "../components/CommonHeader.vue";
export default {
data() {
return {};
},
components: {
CommonAside,
CommonHeader,
// CommonTags,
},
};
</script>
<style lang="scss" scoped>
.el-header {
padding: 0;
}
</style>
就可以对应上一篇博客的router文件夹下的index文件中的路由配置中的父级component

同时,我们需要在页面中间的内容部分做一个全局加载的功能,避免element框架loading功能的全屏加载不够友好。在utils文件夹下新建loading.js文件:
import Vue from "vue";
// loading框设置局部刷新,且所有请求完成后关闭loading框
let loading;
let needLoadingRequestCount = 0; // 声明一个对象用于存储请求个数
function startLoading() {
loading = Vue.prototype.$loading({
lock: true,
text: "努力加载中(っ•̀ω•́)っ⁾⁾",
background: "rgba(255,255,255,.4)",
target: document.querySelector(".page-container-wn"), // 设置加载动画区域
});
}
function endLoading() {
loading.close();
}
export function showPageLoading(target) {
if (needLoadingRequestCount === 0) {
startLoading(target);
}
needLoadingRequestCount++;
}
export function hidePageLoading() {
if (needLoadingRequestCount <= 0) return;
needLoadingRequestCount--;
if (needLoadingRequestCount === 0) {
endLoading();
}
}
export default {
showPageLoading,
hidePageLoading,
};
然后写一个简单的动态渲染的表单组件,在components文件夹下新增CustomForm.vue,这样就可以用后端返回的表单配置json直接进行渲染啦:
<template>
<div class="filterPanel">
<!--是否行内表单-->
<el-form
class="form"
:inline="inline"
:model="form"
:rules="rules"
ref="form"
>
<!--标签显示名称-->
<div class="labelGroup">
<slot></slot>
<el-form-item
v-for="item in formLabel"
:key="item.model"
:label="item.label"
:prop="item.model"
>
<!--根据type来显示是什么标签-->
<!-- 一般输入框 -->
<el-input
v-model="form[item.model]"
:placeholder="item.placeholder || '请输入' + item.label"
v-if="item.type === 'input'"
>
</el-input>
<!-- 数字输入框 -->
<el-input
v-model="form[item.model]"
:min="0"
type="number"
:placeholder="item.placeholder || '请输入' + item.label"
v-if="item.type === 'number'"
>
</el-input>
<el-autocomplete
class="inline-input"
v-model="form[item.model]"
v-if="item.type === 'searchInput'"
:fetch-suggestions="
(queryString, cb) => {
searchOptionName(queryString, cb, item.opts);
}
"
:placeholder="item.placeholder || '请输入' + item.label"
:trigger-on-focus="false"
></el-autocomplete>
<el-select
v-model="form[item.model]"
:placeholder="item.placeholder || '请选择' + item.label"
v-if="item.type === 'select'"
>
<!--如果是select或者checkbox 、Radio,还需要选项信息-->
<el-option
v-for="item in item.opts"
:key="item.value"
v-show="item.label"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
<!-- 开关 -->
<el-switch
v-model="form[item.model]"
v-if="item.type === 'switch'"
></el-switch>
<!-- 单个日期选择器 -->
<el-date-picker
v-model="form[item.model]"
type="date"
placeholder="选择日期"
v-if="item.type === 'date'"
value-format="yyyy-MM-dd"
>
</el-date-picker>
<!-- 日期范围选择器 -->
<el-date-picker
v-model="form[item.model]"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
type="datetimerange"
placeholder="选择日期"
v-if="item.type === 'dateRange'"
value-format="yyyy-MM-dd"
>
</el-date-picker>
<!-- 日期时间选择器 -->
<el-date-picker
v-model="form[item.model]"
type="datetimerange"
range-separator="至"
v-if="item.type === 'dateTimeRange'"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="yyyy-MM-dd HH:mm:ss"
>
</el-date-picker>
</el-form-item>
</div>
<div class="btnGroup">
<el-form-item>
<el-button type="primary" @click="search">搜索</el-button>
<el-button @click="reset">重置</el-button>
</el-form-item>
</div>
</el-form>
</div>
</template>
<script>
// import Bus from "./formEventBus";
export default {
name: "CustomForm",
//inline 属性可以让表单域变为行内的表单域
//form 表单数据 formLabel 是标签数据
props: {
inline: {
type: Boolean,
default: true,
},
formLabel: Array,
rules: Object,
},
watch: {
formLabel: {
handler(newVal) {
if (newVal) {
newVal.forEach((item) => {
this.$set(this.form, item.model, item.default || "");
});
}
},
immediate: true,
},
},
data() {
return {
form: {},
};
},
mounted() {
let obj = {};
this.formLabel.forEach(async (item, index) => {
if (item.optsConfig) {
//获取动态下拉选项
let val = await this.getOpts(item);
this.$set(this.formLabel[index], "opts", val);
obj[item.model] = val;
this.$emit("getSelect", obj);
}
});
},
methods: {
searchOptionName(queryString, cb, data) {
var restaurants = data;
var results = queryString
? restaurants.filter(this.createFilter(queryString))
: restaurants;
cb(results);
},
createFilter(queryString) {
return (restaurant) => {
return (
restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) !=
-1
);
};
},
reset() {
this.form.pageNum = 1;
this.$refs["form"].resetFields();
this.$emit("confirm", this.form);
// Bus.$emit('getParam', this.form);//给Table传查询参数
},
search() {
this.form.pageNum = 1;
this.$emit("confirm", this.form);
// Bus.$emit('getParam', this.form);//给Table传查询参数
},
async getOpts(oData) {
let { api, param, labelKey, valueKey } = oData.optsConfig;
let opts = [];
const res = await api(param);
if (res.code === 1) {
opts = res.data.map((item) => {
if (oData.model === "goodsSku" && item["goodsSku"] != "") {
//SKU特殊处理
const itemObj = JSON.parse(item.goodsSku);
return {
label: itemObj[labelKey].join("-"),
value: itemObj[valueKey],
...item,
};
} else {
return {
label: item[labelKey],
value: item[valueKey],
...item,
};
}
});
}
return opts;
},
},
};
</script>
<style lang="scss">
.filterPanel {
margin-bottom: 20px;
padding: 20px 20px 0 20px;
.form {
display: flex;
justify-content: space-between;
.btnGroup {
min-width: 150px;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
}
}
.el-input__inner {
background-color: #fff;
height: 33px;
line-height: 33px;
}
.filterPanel {
width: 100%;
border-radius: 4px;
background: #f7f8fa;
padding-top: 20px;
padding-right: 20px;
}
.btnContainer {
margin-bottom: 20px;
}
.el-button {
height: 32px !important;
padding: 0 16px;
}
.el-date-editor .el-range__icon,
.el-range-separator {
line-height: 26px;
}
}
</style>
接下来整一个页面看看效果。首先在api文件夹下新增此页面的api文件,我称之为order.js:
/**
* 订单管理接口列表
*/
export function orderInit() {
return {
code: 10000,
msg: "请求成功",
data: {
searchList: [
{
label: "",
model: "time",
type: "dateTimeRange",
},
{
label: "",
placeholder: "请选择游戏",
model: "game",
type: "select",
opts: [
{
label: "游戏1",
value: "game1",
},
{
label: "游戏2",
value: "game2",
},
],
},
{
label: "",
placeholder: "全部订单",
model: "order",
type: "select",
opts: [
{
label: "订单1",
value: "order1",
},
{
label: "订单2",
value: "order2",
},
],
},
{
label: "",
placeholder: "订单类型",
model: "orderType",
type: "select",
opts: [
{
label: "平台订单号",
value: "orderId",
},
{
label: "游戏订单号",
value: "gameOrderId",
},
],
},
{
label: "",
placeholder: "输入文本",
model: "text",
type: "input",
},
],
searchRules: {},
tableHeader: [
{ id: "orderId", name: "平台订单号" },
{ id: "gameName", name: "游戏名称" },
{ id: "userName", name: "用户名" },
{ id: "avaName", name: "区服/角色名" },
{ id: "sum", name: "金额" },
{ id: "createTime", name: "创建时间" },
{ id: "payStatus", name: "支付状态" },
{ id: "noticeStatus", name: "通知状态" },
],
},
};
}
export function orderList(params) {
console.log(params, "params");
return {
code: 10000,
msg: "请求成功",
data: {
tableData: [
{
id: "1212121",
orderId: "xxxx",
gameName: "华夏回事路",
userName: "138xxxx2323",
avaName: "xxxxxxxxx",
sum: "648",
createTime: "2023-05-26 00:00:00",
payStatus: "成功",
noticeStatus: "成功",
},
{
id: "12121221",
orderId: "xxxx",
gameName: "华夏回事路2",
userName: "138xxxx2323",
avaName: "xxxxxxxxx",
sum: "648",
createTime: "2023-05-26 00:00:00",
payStatus: "成功",
noticeStatus: "成功",
},
{
id: "12121321",
orderId: "xxxx",
gameName: "华夏回3事路",
userName: "138xxxx2323",
avaName: "xxxxxxxxx",
sum: "648",
createTime: "2023-05-26 00:00:00",
payStatus: "成功",
noticeStatus: "成功",
},
],
page: {
pageSize: 10,
current: 1,
total: 200,
},
},
};
}
当然接口是假的,接下来写页面,在views文件夹下新增order文件夹,新建index.vue文件:
<template>
<div>
<el-card class="box-card search-card">
<CustomForm
:formLabel="searchList"
:rules="searchRules"
@confirm="handleSearch"
/>
</el-card>
<el-card class="box-card">
<el-table
:data="tableData"
border
v-loading="tableLoading"
style="width: 100%; margin-bottom: 16px"
>
<el-table-column
v-for="item in tableHeader || []"
:key="item.id"
:prop="item.id"
:label="item.name"
/>
<el-table-column fixed="right" label="操作" width="120">
<template slot-scope="scope">
<el-button @click="handleDetail(scope.row)" type="text"
>详情</el-button
>
<el-button type="text" disabled>补单</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="tablePage.current || 1"
:page-size="tablePage.current || 10"
layout="total, sizes, prev, pager, next"
:total="tablePage.total || total"
>
<!-- :page-sizes="[10, 20, 50, 100]" -->
</el-pagination>
</el-card>
<DataDetail
:rowInfo="rowInfo"
:visible="drawerVisible"
@updateVisible="updateVisible"
/>
</div>
</template>
<script>
import CustomForm from "@/components/CustomForm.vue";
import DataDetail from "./DataDetail";
import { orderInit, orderList } from "@/api/order";
import { showPageLoading, hidePageLoading } from "@/utils/loading";
export default {
name: "OrderView",
data() {
return {
formInline: {
user: "",
region: "",
},
tableHeader: [],
tableData: [],
searchList: [],
searchRules: {},
tablePage: {
pageSize: 10,
current: 1,
total: 400,
},
searchData: {},
rowInfo: {},
drawerVisible: false,
tableLoading: false,
};
},
mounted() {
this.init();
this.handleSearch();
},
methods: {
// 初始化
async init() {
try {
showPageLoading(); // 开启
const res = await orderInit();
if (res.code !== 10000) this.$message.error(res.msg);
if (res.code === 10000 && res.data) {
this.searchList = res.data.searchList;
this.searchRules = res.data.searchRules;
this.tableHeader = res.data.tableHeader;
}
} finally {
setTimeout(() => {
hidePageLoading();
}, 2000);
}
},
// 查询列表数据
async handleSearch(data) {
try {
const tableParams = { ...this.tablePage, ...(data || {}) };
this.searchData = { ...tableParams };
this.tableLoading = true;
const res = await orderList(tableParams);
if (res.code !== 10000) this.$message.error(res.msg);
if (res.code === 10000 && res.data) {
this.tableData = res.data.tableData;
this.tablePage = { ...res.data.page };
}
} finally {
this.tableLoading = false;
}
},
// 查看详情
handleDetail(data) {
this.rowInfo = data;
this.drawerVisible = true;
},
// 切换每页展示条数
handleSizeChange(val) {
this.tablePage = { ...this.tablePage, pageSize: val };
this.handleSearch({
...this.searchData,
...this.tablePage,
pageSize: val,
});
},
// 切换页码
handleCurrentChange(val) {
this.tablePage = { ...this.tablePage, current: val };
this.handleSearch({
...this.searchData,
...this.tablePage,
current: val,
});
},
updateVisible() {
this.drawerVisible = !this.drawerVisible;
},
},
components: {
CustomForm,
DataDetail,
},
};
</script>
<style lang="scss" scoped>
.search-card {
margin-bottom: 16px;
}
</style>
可以看到引入了@/utils/loading中导出的两个方法,并在初始化接口中进行了使用(写了个2秒的setTimeOut看效果)
然后新建详情抽屉组件,依旧在order文件夹下,新建DataDetail.vue:
<template>
<el-drawer :visible.sync="showDrawer" size="45%" title="详情">
<!-- @tab-click="handleCheckTab" -->
<el-tabs v-model="activeName" style="margin: 24px">
<el-tab-pane label="订单详情" name="first">
<el-descriptions :column="2" :labelStyle="{ fontWeight: 'bold' }">
<el-descriptions-item
v-for="item in data.first || []"
:key="item.key"
:label="item.label"
>{{ item.value }}</el-descriptions-item
>
</el-descriptions>
</el-tab-pane>
<el-tab-pane label="支付回调" name="second">
<el-table
:data="data.second?.tableData || []"
border
style="width: 100%; margin-bottom: 16px"
>
<el-table-column
v-for="item in data.second?.tableHeader || []"
:key="item.id"
:prop="item.id"
:label="item.name"
>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="通知游戏" name="third">
<el-table
:data="data.third?.tableData || []"
border
style="width: 100%; margin-bottom: 16px"
>
<el-table-column
v-for="item in data.third?.tableHeader || []"
:key="item.id"
:prop="item.id"
:label="item.name"
>
</el-table-column>
</el-table>
</el-tab-pane>
</el-tabs>
</el-drawer>
</template>
<script>
export default {
name: "DataDetail",
props: {
visible: {
type: Boolean,
default: false,
},
rowInfo: Object,
},
data() {
return {
activeName: "first",
data: {},
};
},
computed: {
showDrawer: {
get() {
return this.visible;
},
set(val) {
this.$emit("updateVisible", val);
},
},
},
mounted() {
this.data = {
first: [
{ key: "orderId", label: "平台订单号", value: "xxxxx" },
{ key: "orderId2", label: "支付消息", value: "xxxxx" },
{ key: "orderId3", label: "支付方式", value: "支付宝" },
{ key: "orderId4", label: "游戏名称", value: "xxxxx" },
{ key: "orderId5", label: "支付渠道单号", value: "xxxxxxxxxxxxxxx" },
{ key: "orderId6", label: "订单金额", value: "648.00" },
{ key: "orderId7", label: "支付状态", value: "成功" },
{ key: "orderId8", label: "通知游戏状态", value: "成功" },
{ key: "orderId9", label: "游戏区服", value: "xxx1服" },
{ key: "orderId10", label: "游戏角色名", value: "ABB" },
{ key: "orderId11", label: "游戏订单号", value: "xxxxxxxxxx" },
{ key: "orderId12", label: "商品名称", value: "648礼包" },
{ key: "orderId13", label: "回调地址", value: "https://xxxx.ccc.com" },
],
second: {
tableHeader: [
{ id: "xxTime", name: "回调时间" },
{ id: "xxStatus", name: "回调状态" },
],
tableData: [
{ id: "12121212", xxTime: "2022-05-21 23-33", xxStatus: "成功" },
{ id: "12134321212", xxTime: "2022-05-21 13-33", xxStatus: "失败" },
{ id: "1212221212", xxTime: "2022-05-21 23-23", xxStatus: "成功" },
],
},
third: {
tableHeader: [
{ id: "xxTime", name: "通知时间" },
{ id: "xxStatus", name: "通知状态" },
],
tableData: [
{ id: "12121212", xxTime: "2022-05-21 23-33", xxStatus: "成功" },
{ id: "12121212", xxTime: "2022-05-21 13-33", xxStatus: "失败" },
{ id: "12121212", xxTime: "2022-05-21 23-23", xxStatus: "成功" },
],
},
};
},
methods: {
// handleCheckTab(val) {
// console.log(val);
// },
},
};
</script>
看看效果:

查看详情:


浙公网安备 33010602011771号