Vue卖座电影中后台开发纪实(二)

@跑起样板工程

  • 克隆代码
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>

@页头实现

完成效果

image.png

页头组件封装思路

  • 父组件通过名曰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>

@导航菜单实现

完成效果

image.png

实现思路

  • 使用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
喜欢就支持一下吧
点赞5 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容