【完整23章】基于 Vue3 ,打造前台+中台通用提效解决方案

519 阅读4分钟

基于 Vue3 ,打造前台+中台通用提效解决方案

核心代码,注释必读

// download:3w ukoou com

实战项目具体要求:

  • 基于 Vue3 开发一款前台 + 中台的应用品牌网站,提供基础的用户登陆、注册、收藏等功能。
  • 使用 Vue3 + Vue Router + Vuex 作为主要技术栈,同时使用 axios 作为HTTP客户端,并使用 Ant Design Vue 作为UI组件库。
  • 前台部分包括首页、商品列表页、商品详情页、购物车、用户登录注册页面等核心业务页面,实现商品的分类、搜索、排序、加入购物车、收藏等功能,用户登录注册需要实现表单校验和提交。
  • 中台部分包括用户管理、数据统计、商品管理、订单管理等后台管理页面,对数据进行增删改查等操作,并对用户角色和权限进行管理。

首先,我们需要安装和配置 Vue3 开发环境以及相关工具和插件。这些都是相当基础和常规的工作,我在这里简单说明一下,具体可以参考 Vue3 官网。

安装 Node.js:

Node.js 是一种 JavaScript 运行环境,可以在开发中运行 JavaScript 代码,很多前后端开发都需要用到。你需要到 Node.js 官网上下载安装包并安装,然后打开命令行窗口,输入下面的命令来验证安装是否成功:

node -v

安装 Git:

Git 是版本控制系统,可以帮助我们对代码进行管理。你需要到 Git 官网上下载安装包并安装,然后打开命令行窗口,输入下面的命令来验证安装是否成功:

git --version

安装 Vue CLI:

Vue CLI 是 Vue.js 官方提供的脚手架工具,可以帮助我们快速搭建基于 Vue.js 的项目。你需要打开命令行窗口,输入下面的命令进行安装:

sudo npm install -g @vue/cli

搭建应用工程:

接下来我们需要使用 Vue CLI 来搭建一个 Vue3 应用工程。你可以打开命令行窗口,输入下面的命令来创建一个名为 vue3-demo 的项目:

vue create vue3-demo

这个命令会让你选择一种预设,我们选择“Manually select features”自定义安装,然后会让你选择需要的特性,选择如下的特性:

  • Choose Vue version: 3.x
  • Router: Vue Router
  • Vuex: Vuex
  • CSS Pre-processors: Sass/SCSS (with node-sass)
  • Linter: ESLint with error prevention only
  • Formatter: Prettier
  • Config for Babel, ESLint, etc.: (Pick the left one)

最后按照提示安装即可。

接下来,为了让 Ant Design Vue 插件好用,我们需要配置一下 babel.config.js 文件:

module.exports = {
  plugins: [
    [
      "import",
      {
        libraryName: "ant-design-vue",
        libraryDirectory: "es",
        style: true,
      },
      "ant-design-vue",
    ],
  ],
};

然后,我们可以开始编写具体的实现代码了。正常开发时,我们可能会使用mock数据,本文我将用[Easy-Mock](www.easy-mock.com/)模拟数据。当然,使用…

前台页面实现

首先,我们需要实现基本的前台页面,包括首页、商品列表、商品详情、购物车、用户登录注册页面等。

首页

首页是应用的入口,也是用户浏览网站的起点。我们使用 Ant Design Vue 中的 Carousel 组件来实现首页轮播图和“经典案例”列表。代码如下:

<template>
  <div>
    <a-carousel autoplay :dot-position="dotPosition" style="margin-top: 32px;">
      <a-carousel-slide v-for="(item, index) in carouselList" :key="index">
        <a-card :cover="item.imgUrl">
          <a-card-meta
            :title="item.title"
            :description="item.description"
          />
        </a-card>
      </a-carousel-slide>
    </a-carousel>
    <a-divider orientation="left" style="margin-top: 48px;">
      经典案例
    </a-divider>
    <a-row>
      <a-col :span="6" v-for="(item, index) in classicList" :key="index">
        <a-card
          hoverable
          style="margin: 16px;"
          :cover="item.imgUrl"
        >
          <a-card-meta :title="item.title" :description="item.description" />
        </a-card>
      </a-col>
    </a-row>
  </div>
</template>

<script>
import { defineComponent } from "vue";
import { Carousel, Card, Divider, Row, Col } from "ant-design-vue";

export default defineComponent({
  name: "Home",
  components: {
    "a-carousel": Carousel,
    "a-carousel-slide": Carousel.Slide,
    "a-card": Card,
    "a-card-meta": Card.Meta,
    "a-divider": Divider,
    "a-row": Row,
    "a-col": Col,
  },
  data() {
    return {
      dotPosition: "bottom",
      carouselList: [
        {
          title: "标题1",
          description: "描述1",
          imgUrl:
            "https://www.easy-mock.com/mock/5f7a95d488fe695575895a29/demo/image/01.jpg",
        },
        {
          title: "标题2",
          description: "描述2",
          imgUrl:
            "https://www.easy-mock.com/mock/5f7a95d488fe695575895a29/demo/image/02.jpg",
        },
        {
          title: "标题3",
          description: "描述3",
          imgUrl:
            "https://www.easy-mock.com/mock/5f7a95d488fe695575895a29/demo/image/03.jpg",
        },
      ],
      classicList: [
        {
          title: "经典案例1",
          description: "描述1",
          imgUrl:
            "https://www.easy-mock.com/mock/5f7a95d488fe695575895a29/demo/image/04.jpg",
        },
        {
          title: "经典案例2",
          description: "描述2",
          imgUrl:
            "https://www.easy-mock.com/mock/5f7a95d488fe695575895a29/demo/image/05.jpg",
        },
        {
          title: "经典案例3",
          description: "描述3",
          imgUrl:
            "https://www.easy-mock.com/mock/5f7a95d488fe695575895a29/demo/image/06.jpg",
        },
        {
          title: "经典案例4",
          description: "描述4",
          imgUrl:
            "https://www.easy-mock.com/mock/5f7a95d488fe695575895a29/demo/image/07.jpg",
        },
      ],
    };
  },
});
</script>

商品列表

商品列表是网站主要的展示内容,需要满足商品的分类、搜索、排序、加入购物车、收藏等功能。我们使用 Ant Design Vue 中的 Table 组件来实现商品列表展示,PageHeader 组件实现顶部的分类筛选和搜索框,Pagination 组件实现分页功能。代码如下:

<template>
  <div>
    <a-page-header @back="goBack" :title="'商品列表'" :tags="tags">
      <template #tags>
        <a-tag v-for="(item, index) in filterData" :key="index" @click="handleFilter(item)">
          {{ item.name }}
        </a-tag>
      </template>
      <template #extra>
        <a-input-search
          :placeholder="'请输入商品名称'"
          :allow-clear="true"
          :on-search="handleSearch"
        />
        <a-dropdown placement="bottomLeft">
          <a-menu slot="overlay">
            <a-menu-item-group title="按价格排序">
              <a-menu-item @click="handleSort(PriceSortType.ASC)">
                价格由低到高
              </a-menu-item>
              <a-menu-item @click="handleSort(PriceSortType.DESC)">
                价格由高到低
              </a-menu-item>
            </a-menu-item-group>
            <a-menu-item-group title="按销量排序">
              <a-menu-item @click="handleSort(SaleSortType.ASC)">
                销量由低到高
              </a-menu-item>
              <a-menu-item @click="handleSort(SaleSortType.DESC)">
                销量由高到低
              </a-menu-item>
            </a-menu-item-group>
          </a-menu>
          <a-button style="margin-right: 16px;">
            <a-icon type="ordered-list" />
          </a-button>
        </a-dropdown>
        <a-dropdown placement="bottomLeft">
          <a-avatar slot="overlay" size="small" :src="avatarUrl" />
        </a-dropdown>
      </template>
    </a-page-header>
    <a-table :columns="columns" :dataSource="dataSource" :loading="loading">
      <template #imgUrl="record">
        <a-avatar :size="64" :src="record.imgUrl" />
      </template>
      <template #action="record">
        <a-button-group>
          <a-button type="primary" :shape="circle" @click="handleAddCart(record)">
            <a-icon type="shopping-cart" />
          </a-button>
          <a-button :shape="circle" @click="handleAddFavor(record)">
            <a-icon type="heart" />
          </a-button>
        </a-button-group>
      </template>
    </a-table>
    <a-pagination
      :current="pagination.current"
      :total="pagination.total"
      :pageSize="pagination.pageSize"
      @current-change="handlePage"
      style="margin-top: 16px; text-align: right"
    />
  </div>
</template>

<script>
import { defineComponent } from "vue";
import {
  PageHeader,
  Tag,
  Input,
  Dropdown,
  Button,
  Avatar,
  Menu,
  MenuItem,
  MenuItemGroup,
  Table,
  AvatarGroup,
  Pagination,
  ButtonGroup,
  Icon,
} from "ant-design-vue";

export default defineComponent({
  name: "GoodsList",
  components: {
    "a-page-header": PageHeader,
    "a-tag": Tag,
    "a-input-search": Input.Search,
    "a-dropdown": Dropdown,
    "a-button": Button,
    "a-avatar": Avatar,
    "a-menu": Menu,
    "a-menu-item": MenuItem,
    "a-menu-item-group": MenuItemGroup,
    "a-table": Table,
    "a-pagination": Pagination,
    "a-button-group": ButtonGroup,
    "a-icon": Icon,
  },
  data() {
    return {
      avatarUrl:
        "https://www.easy-mock.com/mock/5f7a95d488fe695575895a29/demo/avatar.png",
      tags: [],
      filterData: [
        { key: 1, name: "分类1" },
        { key: 2, name: "分类2" },
        { key: 3, name: "分类3" },
      ],
      loading: false,
      columns: [
        {
          title: "商品名称",
          dataIndex: "name",
          sorter: (a, b) => a.name.localeCompare(b.name),
          defaultSortOrder: "ascend",
        },
        {
          title: "商品图片",
          dataIndex: "imgUrl",
          slots: { customRender: "imgUrl" },
        },
        {
          title: "价格",
          dataIndex: "price",
          sorter: (a, b) => a.price - b.price,
        },
        {
          title: "销量",
          dataIndex: "sale",
          sorter: (a, b) => a.sale - b.sale,
        },
        {
          title: "操作",
          dataIndex: "",
          slots: { customRender: "action" },
        },
      ],
      dataSource: [],
      pagination: {
        current: 1,
        total: 0,
        pageSize: 10,
      },
      PriceSortType: {
        ASC: "ascend",
        DESC: "descend",
      },
      SaleSortType: {
        ASC: "sale-ascend",
        DESC: "sale-descend",
      },
    };
  },
  mounted() {
    this.fetchList();
  },
  methods: {
    goBack() {
      this.$router.go(-1);
    },
    async fetchList() {
      this.loading = true;
      const res = await this.$axios.get("/list", {
        params: {
          page: this.pagination.current,
          pageSize: this.pagination.pageSize,
        },
      });
      this.dataSource = res.data.list;
      this.pagination.total = res.data.total;
      this.loading = false;
    },
    async handleFilter(item) {
      const tags = this.tags.slice();
      const idx = tags.findIndex((tag) => tag.key === item.key);
      if (idx === -1) {
        tags.push(item);
      } else {
        tags.splice(idx, 1);
      }
      this.tags = tags;
      this.fetchList();
    },
    async handleSearch(value) {
      this.fetchList();
    },
    async handleSort(sortType) {
      this.columns[2].sortOrder = sortType;
      this.columns[3].sortOrder = null;
      this.fetchList();
    },
    async handlePage(current) {
      this.pagination.current = current;
      this.fetchList();
    },
    async handleAddCart(record) {
      this.$message.success(`已添加商品 ${record.name} 到购物车`);
    },
    async handleAddFavor(record) {
      this.$message.success(`已添加商品 ${record.name} 到收藏`);
    },
  },
});
</script>

<style>
.ant-page-header-heading-sub-description {
  font-size: 14px;
}
</style>

商品详情

商品详情页面展示了商品的详细信息,包括大图、名称、价格、描述、库存等,以及购买按钮和收藏按钮。我们使用 Ant Design Vue 中的 Card 组件来实现商品详情展示和收藏功能,使用 Button 组件实现购买按钮。代码如下:

<template>
  <div>
    <a-page-header @back="goBack" :title="'商品详情'">
      <template #extra>
        <a-button @click="handleFavor" :shape="circle">
          <a-icon :type="favorStatus ? 'heart' : 'heart-outlined'" />
        </a-button>
      </template>
    </a-page-header>
    <a-card
      :title="goodsInfo.name"
      :cover="goodsInfo.imgUrl"
      style="margin-top: 16px;"
    >
      <p style="font-size: 24px; color: red">{{ goodsInfo.price }}</p>
      <p style="font-size: 16px;">库存: {{ goodsInfo.stock }}</p>
      <hr />
      <p style="font-size: 16px;">
        {{ goodsInfo.description }}
      </p>
      <a-button type="primary" style="margin-top: 16px;" @click="handleBuy">
        购买
      </a-button>
    </a-card>
  </div>
</template>

<script>
import { defineComponent } from "vue";
import { PageHeader, Card, Button, Icon } from "ant-design-vue";

export default defineComponent({
  name: "GoodsDetail",
  components: {
    "a-page-header": PageHeader,
    "a-card": Card,
    "a-button": Button,
    "a-icon": Icon,
  },
  data() {
    return {
      goodsInfo: {},
      favorStatus: false,
    };
  },
  mounted() {
    this.fetchGoodsInfo();
  },
  methods: {
    goBack() {
      this.$router.go(-1);
    },
    async fetchGoodsInfo() {
      const res = await this.$axios.get(`/detail?id=${this.$route.query.id}`);
      this.goodsInfo = res.data;
    },
    async handleFavor() {
      this.favorStatus = !this.favorStatus;
      this.$message.success(
        `商品 ${this.goodsInfo.name} ${
          this.favorStatus ? "已添加到收藏" : "已取消收藏"
        }`
      );
    },
    async handleBuy() {
      this.$message.success(`购买成功,祝您愉快!`);
    },
  },
});
</script>

购物车

购物车主要展示用户添加的商品,包括商品个数、单价、小计、总计等信息,以及删除和结算按钮。我们使用 Ant Design Vue 中的 List 组件来实现购物车展示,使用 Divider 组件实现列表