@跑起样板工程
- 克隆代码
git clone https://gitee.com/steveouyang/vue-manager-demo.git
- 运行服务端
cd mymovie-server
npm install
npm run start #8002端口
- 修改管理端运行端口 + 跨域代理
根目录/vite.config.js
// https://vitejs.dev/config/
export default defineConfig({
...
// 配置前端服务地址和端口
server: {
host: "0.0.0.0",
// 自定义运行端口
port: 5000,
// 是否开启 https
https: false,
// 让开发服务器帮我们做代理
proxy: {
// http://localhost:5000/api
"/api": {
target: "http://localhost:8002", //API服务地址
changeOrigin: true, //开启跨域
rewrite: (path) => path.replace(/^\/api/, ""),
},
},
},
});
- 运行管理端
cd mymovie-manager
npm install
npm run dev #5000端口
@切分页面布局
- 将页面切分为上下两部分,下部切分为左右两部分
App.vue模板
<template>
<div class="top">
...
</div>
<main>
<div class="left">
...
</div>
<div class="right">
...
</div>
</main>
</template>
<style lang="scss" scoped>
.top {
width: 50px;
}
main {
display: flex;
height: calc(100vh - 50px);
.left {
width: 200px;
padding-right: 1px;
border-right: 1px solid black;
}
.right {
flex-grow: 1;
padding: 10px;
}
}
</style>
@页头实现
完成效果
页头组件封装思路
- 父组件通过名曰title的props注入标题内容;
- 当用户点击最右侧action按钮时,通过event通知父组件,由父组件决定如何响应;
- action按钮的模板和样式,父组件可以通过插槽进行覆盖
页头组件核心代码
components/MyPageHeader
<template>
<div class="epHeader" v-if="!$route.meta.hideFrame">
<!-- 左侧返回按钮:导航回退 -->
<div class="left" @click="$router.back">
<el-icon><ArrowLeftBold /></el-icon>
</div>
<!-- 中间显示父组件注入的标题 -->
<div class="middle">{{ title }}</div>
<div class="right">
<!-- Element头像 -->
<el-avatar
:size="24"
class="mr-3"
src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"
/>
<!-- 欢迎回来 -->
<span class="content-elem"
>欢迎回来:
<span class="pointer username" v-change="{ color: 'cyan' }">admin</span>
</span>
<!-- 右侧action按钮区:点击时通知父组件 -->
<div
class="action pointer centerbox"
@click="emit(`actionBtnClick`, Date.now())"
>
<!-- action按钮默认使用X号按钮 父组件可以通过具名插槽覆盖action按钮 -->
<slot name="action">
<!-- 当用户点击action btn的时候 通知父组件 让父组件决定该干什么 -->
<el-icon size="24"> <CloseBold /></el-icon>
</slot>
</div>
</div>
</div>
</template>
<script setup>
import { defineProps, defineEmits } from "vue";
import {
ArrowLeftBold,
SwitchButton,
CloseBold,
} from "@element-plus/icons-vue";
/* 定义props */
const { title } = defineProps({
title: String,
});
/* 定义向父组件发送的事件 */
const emit = defineEmits({
actionBtnClick: null,
});
</script>
页头组件部署
App.vue
<!-- 页头 -->
<MyPageHeader
title="欢迎光临卖座电影管理后台"
@action-btn-click="onActionBtnClick"
>
<!-- 覆盖action按钮插槽默认内容 -->
<template #action>
<el-icon
size="24"
class="content-elem pointer"
v-change="{ color: 'cyan' }"
><SwitchButton
/></el-icon>
</template>
</MyPageHeader>
@导航菜单实现
完成效果
实现思路
- 使用ElementPlus的Menu组件进行实现
- 拷贝官方文档中的代码进行改良
- 需要用到的几个核心组件主菜单
Menu
,子菜单SubMenu
,菜单项MenuItem
- 子菜单可以展开,最终的菜单项中设置页面跳转链接
- 子菜单和菜单项都可以设置Icon图标
- 注意为所有的子菜单和菜单项设置唯一的index
- 通过
Menu
属性可设置菜单项常态下的文字颜色和背景颜色 + 菜单项激活时的文字颜色
active-text-color="#e9f4ff"
background-color="#545c64"
text-color="#fff"
实现代码
App.vue
<div class="left">
<el-menu
default-active="2"
class="el-menu-vertical-demo"
@open="handleOpen"
@close="handleClose"
active-text-color="#e9f4ff"
background-color="#545c64"
text-color="#fff"
>
<!-- 数据看板 -->
<el-menu-item index="1">
<el-icon><PieChart /></el-icon>
<RouterLink class="tab" to="/data">数据看板</RouterLink>
</el-menu-item>
<!-- 影片管理 -->
<el-sub-menu index="2">
<template #title>
<el-icon><Film /></el-icon>
<span>影片管理</span>
</template>
<!-- 正在热映 -->
<el-menu-item index="2-1">
<el-icon><VideoCameraFilled /></el-icon>
<RouterLink class="tab" to="/film/playing">正在热映</RouterLink>
</el-menu-item>
<!-- 即将上映 -->
<el-menu-item index="2-2">
<el-icon><Histogram /></el-icon>
<RouterLink class="tab" to="/film/coming">即将上映</RouterLink>
</el-menu-item>
</el-sub-menu>
<!-- 影院管理 -->
<el-sub-menu index="3">
<template #title>
<el-icon><PictureFilled /></el-icon>
<span>影院管理</span>
</template>
<!-- 热门城市 -->
<el-sub-menu index="3-1">
<template #title>
<el-icon><Star /></el-icon>
热门城市
</template>
<el-menu-item index="3-1-1">
<RouterLink class="tab" to="/cinema/hot">北京</RouterLink>
</el-menu-item>
<el-menu-item index="3-1-2">
<RouterLink class="tab" to="/cinema/hot">上海</RouterLink>
</el-menu-item>
<el-menu-item index="3-1-3">
<RouterLink class="tab" to="/cinema/hot">广州</RouterLink>
</el-menu-item>
</el-sub-menu>
<!-- 所有城市 -->
<el-menu-item index="3-2">
<el-icon><LocationFilled /></el-icon>
<RouterLink class="tab" to="/cinema/all">所有城市</RouterLink>
</el-menu-item>
</el-sub-menu>
<!-- 用户管理 -->
<el-menu-item index="4">
<el-icon><User /></el-icon>
<RouterLink class="tab" to="/user">用户管理</RouterLink>
</el-menu-item>
<!-- 案例管理 -->
<el-menu-item index="5">
<el-icon><Grid /></el-icon>
<RouterLink class="tab" to="/demos">案例管理</RouterLink>
</el-menu-item>
</el-menu>
</div>
import {
Grid,
PictureFilled,
Star,
LocationFilled,
Film,
VideoCameraFilled,
Histogram,
PieChart,
ArrowLeftBold,
SwitchButton,
} from "@element-plus/icons-vue";
几个特殊样式
src/assets/main.scss
.el-menu {
border: 0;
width: 100%;
/* 让出2像素 否则菜单项高亮时边缘会溢出 需要浏览器慢慢调试o(╥﹏╥)o */
.el-menu-item {
width: 198px;
min-width: 198px;
}
}
// 菜单项激活时的背景颜色
.is-active {
background-color: rgb(46, 57, 65);
}
@内容区实时切换
实现思路
- 在App.vue中部署RouterView,用于承载页面级组件
- 提前部署好路由表
- 在菜单项的RouterLink被点击时,右侧的RouterView会加载子页面
内容区模板
App.vue
<!-- 右侧内容区 -->
<div class="right">
<router-view v-slot="{ Component }">
<transition name="slide-fade" mode="out-in">
<keep-alive>
<component :is="Component" />
</keep-alive>
</transition>
</router-view>
</div>
组件切换动画定义
src/assets/transition.css
该样式是在main.scss中被全局引入的
/* 定义过渡动画 */
/* 定义fade动画的入场和出场效果 */
.slide-fade-enter-active,
.slide-fade-leave-active {
transition: all 0.5s ease;
}
/* 定义fade动画的场外状态 */
.slide-fade-enter-from,
.slide-fade-leave-to {
opacity: 0;
transform: translateY(20px);
}
/* 定义leftward动画的场外状态 */
.leftward-enter-active,
.leftward-fade-leave-active {
transition: all 0.5s ease;
}
.leftward-enter-from,
.leftward-leave-to {
opacity: 0;
transform: translateX(-100%);
}
/* 定义fade动画的场外状态 */
.fade-enter-active,
.fade-fade-leave-active {
transition: all 0.5s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
@表格显示数据
实现思路
- 要会看文档
- 要会看文档
- 要会看文档
表格部署模板
<!--
:data="tableData" // 表格数据
stripe //隔行变色
style="width: 100%" //额外样式,会透传到组件的布局中为根元素所接收
:default-sort="{ prop: 'date', order: 'ascending' }" //默认根据数据项的date字段升序排列
@selection-change="handleSelectionChange" //多选变化时的处理函数,载荷为勾选上的数据集
-->
<el-table
:data="tableData"
stripe
style="width: 100%"
:default-sort="{ prop: 'date', order: 'ascending' }"
@selection-change="handleSelectionChange"
>
<!-- 多选显示栏 -->
<el-table-column type="selection" width="40" />
<!-- fixed固定 sortable字段可排序 label=当前列标题 width当前列像素宽度 -->
<el-table-column sortable fixed prop="date" label="日期" width="120" />
<el-table-column sortable prop="name" label="姓名" width="100" />
<el-table-column sortable prop="state" label="州" width="120" />
<el-table-column sortable prop="city" label="城市" width="120" />
<el-table-column sortable prop="address" label="地址" width="300" />
<el-table-column sortable prop="zip" label="邮编" width="120" />
<!-- fixed="right"右侧固定 -->
<el-table-column fixed="right" label="操作" width="90">
<!-- 覆盖为小号圆形的编辑按钮与删除按钮 -->
<template #default>
<el-button type="primary" :icon="Edit" circle size="small" />
<el-button type="danger" :icon="Delete" circle size="small" />
</template>
</el-table-column>
</el-table>
表格数据与多选监听
/* 表格数据 */
const tableData = [
{
date: "2016-05-03",
name: "Cindy",
state: "California",
city: "Los Angeles",
address: "No. 189, Grove St, Los Angeles",
zip: "CA 90036",
tag: "Home",
},
...
];
/* 多选变更时此处能拿到选中的子数组 */
const handleSelectionChange = (val) => {
console.log("handleSelectionChange", val);
// multipleSelection.value = val;
};
@数据分页
实现思路
- 要会看文档 * 10086
分页器部署模板
<!-- 翻页侦听的两种玩法 -->
<!-- @current-change="onCurrentChange" 分页器分页侦听的事件回调版(低级玩法/废弃) -->
<!-- v-model:current-page="currentPage" 双向绑定分页器的当前页码(高级玩法/推荐) -->
<!--
:page-size="5"一页5条
:total="tableData.length"总数据量取决于数据总条数
v-model:current-page="currentPage" 双向绑定分页器的当前页码
-->
<el-pagination
background
layout="prev, pager, next"
:page-size="5"
:total="tableData.length"
v-model:current-page="currentPage"
/>
翻页侦听
/* 实时记录当前页码 */
const currentPage = ref(2);
/* 根据页码实时截取一页数据供表格显示 */
const computedData = computed(() =>
tableData.slice((currentPage.value - 1) * 5, currentPage.value * 5)
);
/* 响应分页器的翻页事件,拿到实时页码同步给currentPage */
// 当前已经用更好的方式实现翻页侦听,即: v-model:current-page="currentPage"
const onCurrentChange = (page) => {
console.log("onCurrentChange", page);
currentPage.value = page;
};
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END
暂无评论内容