Bootstrap4 最终开发:企业级后台管理平台全生命周期交付实战

Bootstrap4 的最终开发阶段,核心是 “从技术实现到商业交付的完整落地”—— 不再局限于单一模块开发,而是围绕企业级项目的 “需求分析、架构设计、开发实现、测试验证、部署运维” 全生命周期,整合微前端架构、复杂组件开发、性能优化、工程化流程等所有高级技术,交付可直接上线的商业级产品。本文通过 “大型后台管理平台完整交付案例”,拆解最终开发阶段的核心工作与落地思路,帮你掌握从 “技术开发者” 到 “项目交付者” 的核心能力。

一、最终开发核心认知:从 “模块开发” 到 “项目交付” 的能力跃迁

最终开发阶段与高级开发阶段的本质差异,在于 “解决问题的完整性”—— 高级开发聚焦技术模块实现,最终开发则需兼顾 “业务价值、技术落地、质量保障、运维成本”,具体体现在四个核心维度的跃迁:

能力维度高级开发阶段(聚焦 “技术实现”)最终开发阶段(聚焦 “项目交付”)商业交付价值

需求理解基于技术文档实现功能模块深入业务场景,将需求转化为技术方案并落地需求满足度提升至 95%+,业务价值最大化

项目管理遵循编码规范,完成模块开发主导项目进度、风险控制、团队协作项目按时交付率提升至 90%+,成本控制在预算内

质量保障完成单元测试,修复模块 bug建立全链路测试体系(单元 / 集成 / 性能 / 安全)线上 bug 率降低至 0.1% 以下,系统稳定性提升至 99.99%

运维支持提供模块部署文档设计运维方案(监控、告警、故障自愈)运维成本降低 60%+,故障恢复时间缩短至分钟级

二、最终开发实战:大型后台管理平台全生命周期交付(Bootstrap4 + 微前端 + Vue3)

以 “电商企业后台管理平台” 为例,完整拆解从需求分析到运维优化的全流程,整合前面章节的微前端架构、多主题定制、高级表格插件等技术,实现商业级项目交付。

1. 阶段一:需求分析与架构设计(项目启动核心)

(1)业务需求分析(从业务到技术的转化)

通过 “用户故事 + 用例图” 明确核心业务场景,输出《需求规格说明书》,关键需求如下:

业务域核心用户故事技术需求拆解优先级

用户管理管理员需查看 / 编辑 / 禁用用户账号,支持批量操作实现用户列表(高级表格)、用户编辑模态框、批量禁用接口P0(核心)

商品管理运营需上架 / 编辑 / 下架商品,支持多图上传与 AI 分类实现商品表单(多图上传组件)、AI 分类接口集成、商品状态管理P0(核心)

订单管理客服需查看订单详情,处理退款 / 取消订单请求实现订单列表(支持筛选订单状态 / 时间范围)、订单详情页、退款接口调用P0(核心)

数据统计运营需查看销售数据、用户增长趋势,生成报表实现数据看板(ECharts 图表)、报表导出(Excel/PDF)、定时邮件推送P1(重要)

系统配置管理员需配置系统主题、权限角色、接口参数实现主题切换组件、角色权限配置页、参数配置表单P1(重要)

(2)技术架构设计(支撑业务需求的技术方案)

基于需求分析,设计 “微前端 + 云原生” 架构,输出《技术架构说明书》,核心架构如下:

┌─────────────────────────────────────────────────────────────────┐

│ 前端架构:微前端(qiankun)+ Bootstrap4+Vue3+Pinia              │

│ ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌───────────┐│

│ │用户管理子应用│  │商品管理子应用│  │订单管理子应用│  │数据统计子应用││

│ │- 技术栈:Vue3+Bootstrap4+Pinia│ │- 技术栈:Vue3+Bootstrap4+Pinia│ │- 技术栈:Vue3+Bootstrap4+Pinia│ │- 技术栈:Vue3+ECharts+Bootstrap4││

│ │- 独立仓库/构建/部署│ │- 独立仓库/构建/部署│ │- 独立仓库/构建/部署│ │- 独立仓库/构建/部署││

│ └──────┬──────┘  └──────┬──────┘  └──────┬──────┘  └──────┬────┘│

│        │                │                │                │    │

│ ┌──────▼────────────────▼────────────────▼────────────────▼────┐│

│ │ 微前端基座(Base Application)                              ││

│ │- 核心能力:路由分发、子应用加载、样式隔离、全局状态管理      ││

│ │- 公共组件:主题切换、权限控制、消息提示、全局搜索            ││

│ │- 技术栈:Vue3+Bootstrap4+Pinia+TypeScript                  ││

│ └──────────────────────────┬─────────────────────────────────┘│

└────────────────────────────┼─────────────────────────────────────┘

                            │

┌────────────────────────────┼─────────────────────────────────────┐

│ 后端架构:云原生微服务(Spring Cloud Alibaba)                  │

│ ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌───────────┐│

│ │用户服务    │  │商品服务    │  │订单服务    │  │AI服务    ││

│ │- 用户CRUD  │  │- 商品管理  │  │- 订单处理  │  │- 智能分类 ││

│ │- 权限控制  │  │- 库存管理  │  │- 退款处理  │  │- 数据预测 ││

│ └──────┬──────┘  └──────┬──────┘  └──────┬──────┘  └──────┬────┘│

│        │                │                │                │    │

│ ┌──────▼────────────────▼────────────────▼────────────────▼────┐│

│ │ 服务支撑层                                                  ││

│ │- API网关:路由转发、限流熔断、权限校验                        ││

│ │- 注册中心:服务注册与发现(Nacos)                            ││

│ │- 配置中心:动态配置管理(Nacos)                              ││

│ │- 监控中心:链路追踪、性能监控(SkyWalking+Prometheus)        ││

│ └─────────────────────────────────────────────────────────────┘│

└─────────────────────────────────────────────────────────────────┘

(3)技术选型确认(兼顾业务需求与技术成本)

输出《技术选型说明书》,关键选型及理由如下:

技术领域选型选型理由成本与风险控制

前端框架Vue3+TypeScript组合式 API 便于复杂逻辑拆分,TypeScript 提升代码可维护性团队已有 Vue 经验,学习成本低,生态完善

UI 框架Bootstrap4 + 自定义主题组件丰富,定制化成本低,支持响应式适配多端避免自研 UI 框架,缩短开发周期 60%+

微前端框架qiankun成熟稳定,支持子应用独立部署,样式隔离效果好社区活跃,问题解决效率高,风险可控

状态管理Pinia轻量高效,支持 TypeScript,比 Vuex 更简洁学习成本低,与 Vue3 兼容性好

图表库ECharts支持多类型图表,配置灵活,文档完善满足数据统计场景需求,开发效率高

构建工具Vite+Rollup热更新速度快,构建效率高,支持微前端子应用打包开发体验好,构建时间缩短 70%+

测试工具Jest+Cypress+PlaywrightJest 做单元测试,Cypress 做 E2E 测试,Playwright 做跨浏览器测试覆盖全测试场景,保障线上质量

CI/CD 工具GitLab CI+Jenkins+Docker+K8s实现自动化构建、测试、部署,支持容器化运维自动化率提升至 90%+,部署时间缩短至分钟级

2. 阶段二:开发实现(核心功能落地)

基于架构设计,按 “微前端子应用” 拆分开发任务,整合前面章节的高级技术模块,实现核心功能。

(1)公共组件复用(避免重复开发)

基于 Bootstrap4 封装全局公共组件,输出《公共组件文档》,关键组件如下:

主题切换组件:复用前面章节的多主题切换逻辑,支持 “默认 / 暗黑 / 企业蓝” 三种主题,实现 localStorage 持久化与子应用主题同步;

高级表格组件:复用前面章节的自定义表格插件,支持多条件筛选、排序、分页、批量操作、数据导出,适配所有业务域的列表场景;

表单组件:封装 “输入框、下拉选择、日期选择、多图上传” 等基础表单元素,支持表单验证、动态表单(新增 / 删除表单项)、数据回显,适配用户编辑、商品上架等场景;

消息提示组件:封装 “成功 / 警告 / 错误 / 信息” 四种消息类型,支持自动关闭、手动关闭、全屏加载提示,统一全局交互体验。

(2)核心业务模块实现(以商品管理子应用为例)

整合公共组件与业务逻辑,实现商品管理核心功能,关键代码片段如下:

1. 商品列表页(使用高级表格组件)

<template>

  <div class="product-management">

    <!-- 页面标题与新增按钮 -->

    <div class="page-header d-flex justify-content-between align-items-center mb-4">

      <h4>商品管理</h4>

      <button class="btn btn-primary" www.hblxs8.com  @click="openAddProductModal">

        <i class="bi bi-plus me-1"></i>新增商品

      </button>

    </div>

    <!-- 高级表格组件 -->

    <AdvancedTable

      :table-columns="tableColumns"

      :table-data="productList"

      :total="total"

      :filter-options="filterOptions"

      :page-config="pageConfig"

      primary-key="id"

      @loadData="loadProductData"

      @edit="openEditProductModal"

      @delete="handleDeleteProduct"

      @batchDelete="handleBatchDeleteProduct"

      @export="handleExportProduct"

    >

      <!-- 自定义商品状态列(插槽) -->

      <template #column-status="{ row }">

        <span

          class="badge"

          :class="row.status  m.hblxs8.com === 1 ? 'bg-success' : 'bg-danger'"

        >

          {{ row.status === 1 ? '上架' : '下架' }}

        </span>

      </template>

      <!-- 自定义商品图片列(插槽) -->

      <template #column-image="{ row }">

        <img

          :src="row.image"

          alt="商品图片"

          class="img-thumbnail"

          style="width: 60px; height: 60px; object-fit: cover;"

          @click="previewProductImage(row.images)"

        >

      </template>

      <!-- 自定义操作列(插槽) -->

      <template #action="{ row }">

        <button class="btn btn-sm  wap.hblxs8.com btn-primary me-2" @click="openEditProductModal(row)">

          编辑

        </button>

        <button

          class="btn btn-sm"

          :class="row.status === 1 ? 'btn-warning' : 'btn-success'"

          @click="toggleProductStatus(row)"

        >

          {{ row.status === 1 ? '下架' : '上架' }}

        </button>

        <button class="btn btn-sm btn-danger ms-2" @click="handleDeleteProduct(row.id)">

          删除

        </button>

      </template>

    </AdvancedTable>

    <!-- 新增/编辑商品模态框 -->

    <ProductFormModal

      ref="productFormModal"

      @save-success=" mip.hblxs8.com loadProductData"

    ></ProductFormModal>

    <!-- 商品图片预览模态框 -->

    <ImagePreviewModal

      ref="imagePreviewModal"

      :images="previewImages"

    ></ImagePreviewModal>

  </div>

</template>

<script setup>

import { ref, reactive } from 'vue';

import { useStore } from 'pinia';

import AdvancedTable from '@/components/AdvancedTable/index.vue';

import ProductFormModal from './components/ProductFormModal.vue';

import ImagePreviewModal from '@/components/ImagePreviewModal/index.vue';

import { productApi } from '@/api/productApi'; // 商品API封装

import { Message } from '@/components/Message/index.vue';

const store = useStore();

// 商品列表数据

const productList hblxs8.com  = ref([]);

// 总数据条数

const total = ref(0);

// 分页配置

const pageConfig = reactive({

  currentPage: 1,

  pageSize: 10

});

// 表格列配置

const tableColumns = reactive([

  { label: '商品ID', prop: 'id', sortable: true },

  { label: '商品图片', prop: 'image' }, // 自定义插槽渲染

  { label: '商品名称', prop: 'name', sortable: true },

  { label: '商品分类', prop: 'categoryName' },

  { label: '售价(元)', prop: 'price', sortable: true },

  { label: '库存', prop: 'stock', sortable: true },

  { label: '状态', prop: 'status' }, // 自定义插槽渲染

  { label: '创建时间', prop: 'createTime', type: 'date', sortable: true }

]);

// 筛选选项配置

const filterOptions = reactive({

  select: {

    label: '商品状态',

    options: [

      { value: 1, label: '上架' },

      { value: 0, label: '下架' }

    ]

  },

  dateRange: true // 显示日期范围筛选

});

// 商品图片预览数据

const previewImages = ref([]);

// 组件实例

const productFormModal = ref(null);

const imagePreviewModal = ref(null);

// 加载商品数据

const loadProductData = async (params) => {

  try {

    const { filterParams,  zuqiulive.seo-king.com.cn sortParams, pageParams } = params;

    // 组装请求参数

    const requestParams = {

      keyword: filterParams.keyword,

      status: filterParams.selectValue,

      startDate: filterParams.startDate,

      endDate: filterParams.endDate,

      sortField: sortParams.prop,

      sortOrder: sortParams.order,

      pageNum: pageParams.currentPage,

      pageSize: pageParams.pageSize

    };

    // 调用API获取数据

    const response = await productApi.getProductList(requestParams);

    productList.value = response.list;

    total.value = response.total;

  } catch (error) {

    Message.error('加载商品数据失败:' + (error.message || '未知错误'));

  }

};

// 打开新增商品模态框

const openAddProductModal = () => {

  productFormModal.value.openModal();

};

// 打开编辑商品模态框

const openEditProductModal = (row) => {

  productFormModal. zuqiuzhibo-tv.seo-king.com.cn value.openModal(row);

};

// 切换商品状态(上架/下架)

const toggleProductStatus = async (row) => {

  try {

    const newStatus = row.status === 1 ? 0 : 1;

    await productApi.updateProductStatus(row.id, newStatus);

    row.status = newStatus;

    Message.success(`商品已${newStatus === 1 ? '上架' : '下架'}成功`);

  } catch (error) {

    Message.error('切换商品状态失败:' + (error.message || '未知错误'));

  }

};

// 删除商品

const handleDeleteProduct = async (id) => {

  if (!confirm('确定要删除该商品吗?删除后不可恢复!')) return;

  try {

    await productApi.deleteProduct(id);

    Message.success('删除商品成功');

    // 重新加载数据

    loadProductData({

      filterParams: { keyword: '', selectValue:</doubaocanvas>



2. 阶段二:开发实现(核心功能落地)(续)

(2)核心业务模块实现(以商品管理子应用为例)(续)

2. 商品表单模态框(支持多图上传与 AI 分类)

<template>

  <div class="modal fade" id="productFormModal" tabindex="-1" aria-hidden="true">

    <div class="modal-dialog modal-lg">

      <div class="modal-content">

        <div class="modal-header">

          <h5 class="modal-title">{{ isEdit ? '编辑商品' : '新增商品' }}</h5>

          <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="关闭"></button>

        </div>

        <div class="modal-body">

          <form ref="productForm" @submit.prevent="handleSubmit">

            <!-- 基础信息 -->

            <div class="row mb-3">

              <div class="col-md-6">

                <label for="productName" class="form-label">商品名称 <span class="text-danger">*</span></label>

                <input

                  type="text"

                  class="form-control"

                  id="productName"

                  v-model="form.name"

                  required

                  placeholder="请输入商品名称"

                >

              </div>

              <div class="col-md-6">

                <label for="productCategory" class="form-label">商品分类 <span class="text-danger">*</span></label>

                <select

                  class="form-select"

                  id="productCategory"

                  v-model="form.categoryId"

                  required

                >

                  <option value="">请选择分类</option>

                  <option v-for="category in categoryList" :key="category.id" :value="category.id">

                    {{ category.name }}

                  </option>

                </select>

                <button type="button" class="btn btn-sm btn-outline-primary mt-1" @click="handleAIClassify">

                  <i class="bi bi-robot me-1"></i>AI自动分类

                </button>

              </div>

            </div>

            <!-- 价格与库存 -->

            <div class="row mb-3">

              <div class="col-md-4">

                <label for="productPrice" class="form-label">售价(元) <span class="text-danger">*</span></label>

                <input

                  type="number"

                  step="0.01"

                  min="0"

                  class="form-control"

                  id="productPrice"

                  v-model="form.price"

                  required

                  placeholder="请输入售价"

                >

              </div>

              <div class="col-md-4">

                <label for="productCost" class="form-label">成本(元) <span class="text-danger">*</span></label>

                <input

                  type="number"

                  step="0.01"

                  min="0"

                  class="form-control"

                  id="productCost"

                  v-model="form.cost"

                  required

                  placeholder="请输入成本"

                >

              </div>

              <div class="col-md-4">

                <label for="productStock" class="form-label">库存数量 <span class="text-danger">*</span></label>

                <input

                  type="number"

                  min="0"

                  class="form-control"

                  id="productStock"

                  v-model="form.stock"

                  required

                  placeholder="请输入库存数量"

                >

              </div>

            </div>

            <!-- 商品图片上传 -->

            <div class="mb-3">

              <label class="form-label">商品图片 <span class="text-danger">*</span>(最多上传5张)</label>

              <div class="input-group mb-2">

                <input

                  type="file"

                  class="form-control"

                  id="productImages"

                  multiple

                  accept="image/*"

                  @change="handleImageUpload"

                >

              </div>

              <!-- 图片预览 -->

              <div class="row">

                <div class="col-md-2 mb-2" v-for="(img, index) in form.images" :key="index">

                  <div class="position-relative">

                    <img :src="img.url" class="img-thumbnail w-100" alt="商品图片">

                    <button

                      type="button"

                      class="btn btn-sm btn-danger position-absolute top-0 end-0"

                      @click="handleImageRemove(index)"

                    >

                      ×

                    </button>

                  </div>

                </div>

              </div>

            </div>

            <!-- 商品描述 -->

            <div class="mb-3">

              <label for="productDescription" class="form-label">商品描述</label>

              <textarea

                class="form-control"

                id="productDescription"

                rows="4"

                v-model="form.description"

                placeholder="请输入商品描述"

              ></textarea>

            </div>

            <!-- 商品状态 -->

            <div class="mb-3">

              <label class="form-check-label">

                <input

                  type="checkbox"

                  class="form-check-input"

                  v-model="form.status"

                >

                上架商品(取消则为下架状态)

              </label>

            </div>

          </form>

        </div>

        <div class="modal-footer">

          <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>

          <button type="button" class="btn btn-primary" @click="handleSubmit">保存商品</button>

        </div>

      </div>

    </div>

  </div>

</template>

<script setup>

import { ref, reactive, onMounted, emit } from 'vue';

import { productApi, categoryApi, aiApi } from '@/api';

import { Message, Loading } from '@/components';

// 模态框实例

const modal = ref(null);

// 表单实例

const productForm = ref(null);

// 是否为编辑模式

const isEdit = ref(false);

// 分类列表

const categoryList = ref([]);

// 表单数据

const form = reactive({

  id: '',

  name: '',

  categoryId: '',

  price: 0,

  cost: 0,

  stock: 0,

  images: [],

  description: '',

  status: true // 默认上架

});

// 初始化分类列表

const initCategoryList = async () => {

  try {

    const response = await categoryApi.getCategoryList();

    categoryList.value = response.list;

  } catch (error) {

    Message.error('加载分类列表失败:' + (error.message || '未知错误'));

  }

};

// 打开模态框(新增/编辑)

const openModal = (row = null) => {

  // 重置表单

  form.id = '';

  form.name = '';

  form.categoryId = '';

  form.price = 0;

  form.cost = 0;

  form.stock = 0;

  form.images = [];

  form.description = '';

  form.status = true;

  if (row) {

    // 编辑模式:填充数据

    isEdit.value = true;

    form.id = row.id;

    form.name = row.name;

    form.categoryId = row.categoryId;

    form.price = row.price;

    form.cost = row.cost;

    form.stock = row.stock;

    form.images = row.images.map(img => ({ url: img, file: null })); // 图片回显(无file对象)

    form.description = row.description;

    form.status = row.status === 1;

  } else {

    // 新增模式

    isEdit.value = false;

  }

  // 显示模态框

  modal.value.show();

};

// 图片上传处理

const handleImageUpload = (e) => {

  const files = e.target.files;

  if (!files.length) return;

  // 限制最多上传5张

  if (form.images.length + files.length > 5) {

    Message.warning('最多只能上传5张图片');

    return;

  }

  // 处理每张图片(预览+存储file对象)

  Array.from(files).forEach(file => {

    const reader = new FileReader();

    reader.onload = (event) => {

      form.images.push({

        url: event.target.result, // 预览地址

        file: file // 实际文件对象(用于上传)

      });

    };

    reader.readAsDataURL(file);

  });

  // 清空输入框(避免重复上传)

  e.target.value = '';

};

// 移除图片

const handleImageRemove = (index) => {

  form.images.splice(index, 1);

};

// AI自动分类

const handleAIClassify = async () => {

  if (form.images.length === 0) {

    Message.warning('请先上传商品图片');

    return;

  }

  const loading = Loading.show('AI正在分析图片...');

  try {

    // 取第一张图片进行AI分类

    const firstImage = form.images[0];

    // 转换图片为Base64(不含前缀)

    const base64 = firstImage.url.split(',')[1];

    // 调用AI分类接口

    const response = await aiApi.classifyProduct(base64);

    const categoryId = response.categoryId;

    const categoryName = categoryList.value.find(c => c.id === categoryId)?.name;

    if (categoryId && categoryName) {

      form.categoryId = categoryId;

      Message.success(`AI推荐分类:${categoryName}`);

    } else {

      Message.warning('AI未识别到匹配分类,请手动选择');

    }

  } catch (error) {

    Message.error('AI分类失败:' + (error.message || '未知错误'));

  } finally {

    loading.hide();

  }

};

// 提交表单

const handleSubmit = async () => {

  // 表单验证

  if (!productForm.value.checkValidity()) {

    productForm.value.reportValidity();

    return;

  }

  const loading = Loading.show('正在保存商品...');

  try {

    // 组装提交数据

    const submitData = {

      name: form.name,

      categoryId: form.categoryId,

      price: form.price,

      cost: form.cost,

      stock: form.stock,

      description: form.description,

      status: form.status ? 1 : 0,

      images: []

    };

    // 处理图片上传(仅新增/编辑时上传有变化的图片)

    if (form.images.length > 0) {

      // 过滤出需要上传的图片(含file对象的图片)

      const needUploadImages = form.images.filter(img => img.file);

      if (needUploadImages.length > 0) {

        const formData = new FormData();

        needUploadImages.forEach((img, index) => {

          formData.append(`images[${index}]`, img.file);

        });

        // 调用图片上传接口

        const uploadResponse = await productApi.uploadProductImages(formData);

        submitData.images = uploadResponse.imageUrls;

        // 补充已存在的图片(编辑模式)

        if (isEdit.value) {

          const existingImages = form.images.filter(img => !img.file).map(img => img.url);

          submitData.images = [...existingImages, ...submitData.images];

        }

      } else {

        // 无新图片上传(编辑模式)

        submitData.images = form.images.map(img => img.url);

      }

    }

    // 调用保存接口(新增/编辑)

    if (isEdit.value) {

      submitData.id = form.id;

      await productApi.updateProduct(submitData);

      Message.success('编辑商品成功');

    } else {

      await productApi.addProduct(submitData);

      Message.success('新增商品成功');

    }

    // 关闭模态框并通知父组件刷新数据

    modal.value.hide();

    emit('save-success');

  } catch (error) {

    Message.error(`保存商品失败:${error.message || '未知错误'}`);

  } finally {

    loading.hide();

  }

};

// 初始化模态框

onMounted(() => {

  modal.value = new bootstrap.Modal(document.getElementById('productFormModal'));

  // 初始化分类列表

  initCategoryList();

});

// 对外暴露方法

defineExpose({

  openModal

});

</script>

3. 阶段三:测试验证(保障线上质量)

企业级项目需建立 “全链路测试体系”,覆盖 “单元测试、集成测试、E2E 测试、性能测试、安全测试”,输出《测试报告》,关键测试环节如下:

(1)单元测试(Jest)

针对公共组件与工具函数编写单元测试,保障基础功能正确性:

// 高级表格组件单元测试(tests/unit/components/AdvancedTable.test.js)

import { shallowMount } from '@vue/test-utils';

import AdvancedTable from '@/components/AdvancedTable/index.vue';

describe('AdvancedTable Component', () => {

  // 测试表格列渲染

  test('renders table columns correctly', () => {

    const tableColumns = [

      { label: '商品ID', prop: 'id', sortable: true },

      { label: '商品名称', prop: 'name', sortable: false }

    ];

    const wrapper = shallowMount(AdvancedTable, {

      props: {

        tableColumns,

        tableData: [],

        total: 0,

        primaryKey: 'id'

      }

    });

    // 检查表头数量

    const thElements = wrapper.findAll('th');

    expect(thElements.length).toBe(tableColumns.length + 2); // 列数+复选框列+操作列

    // 检查表头文本

    expect(thElements[1].text()).toContain('商品ID');

    expect(thElements[2].text()).toContain('商品名称');

  });

  // 测试排序功能触发

  test('emits sort event when clicking sortable column', async () => {

    const tableColumns = [

      { label: '商品ID', prop: 'id', sortable: true }

    ];

    const wrapper = shallowMount(AdvancedTable, {

      props: {

        tableColumns,

        tableData: [],

        total: 0,

        primaryKey: 'id'

      }

    });

    // 点击可排序表头

    const sortableTh = wrapper.find('th.sortable');

    await sortableTh.trigger('click');

    // 检查是否触发排序事件(通过loadData事件传递排序参数)

    const loadDataEvents = wrapper.emitted('loadData');

    expect(loadDataEvents).toBeTruthy();

    expect(loadDataEvents[0][0].sortParams).toEqual({ prop: 'id', order: 'asc' });

    // 再次点击,检查排序方向切换

    await sortableTh.trigger('click');

    expect(loadDataEvents[1][0].sortParams).toEqual({ prop: 'id', order: 'desc' });

  });

});

(2)E2E 测试(Cypress)

模拟用户真实操作,测试核心业务流程(如商品新增、编辑、删除):

// 商品管理E2E测试(cypress/e2e/product-management.cy.js)

describe('Product Management', () => {

  // 登录前置操作

  beforeEach(() => {

    cy.login('admin', 'admin123'); // 自定义登录命令

    cy.visit('/product'); // 访问商品管理页面

  });

  // 测试新增商品流程

  it('should add a new product successfully', () => {

    // 点击新增按钮

    cy.get('button:contains("新增商品")').click();

    // 填写表单

    cy.get('#productName').type('测试商品');

    cy.get('#productCategory').select('服装');

    cy.get('#productPrice').type('99.99');

    cy.get('#productCost').type('50.00');

    cy.get('#productStock').type('100');

    cy.get('#productDescription').type('这是一个测试商品');

    cy.get('input[type="checkbox"][name="status"]').check();

    // 上传图片

    cy.get('#productImages').attachFile('test-image.jpg'); // 自定义文件上传命令

    // 提交表单

    cy.get('button:contains("保存商品")').click();

    // 验证成功提示

    cy.get('.message-success').should('contain', '新增商品成功');

    // 验证商品列表显示

    cy.get('table').should('contain', '测试商品');

    cy.get('table').should('contain', '</doubaocanvas>

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容