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>