Vue.js+uni-app构建电商小程序与后台管理系统实战指南

发布时间:2026/7/19 1:42:17

Vue.js+uni-app构建电商小程序与后台管理系统实战指南 在实际电商项目开发中小程序商城和后台管理系统往往是密不可分的整体。特别是对于一番赏手办这类垂直领域既要保证前端用户体验的流畅性又要确保后台管理的高效性。基于 Vue.js 和 uni-app 的技术栈组合能够实现一套代码多端发布大幅提升开发效率。本文将以一番赏手办商城为例详细介绍如何从零搭建包含小程序前端和后台管理系统的完整解决方案。重点会放在技术架构设计、核心功能实现、接口文档规范以及实际开发中容易遇到的坑点。1. 项目架构设计与技术选型1.1 为什么选择 Vue uni-app 技术栈Vue.js 作为渐进式前端框架在组件化开发和状态管理方面有着成熟生态。uni-app 基于 Vue.js 语法规范能够将 Vue 组件编译到微信小程序、H5、App 等多个平台。对于一番赏手办商城这类需要覆盖多端的项目这种一次开发多端发布的能力尤为重要。技术栈组成前端框架Vue 3 Composition API跨端方案uni-app状态管理PiniaVue 3 推荐请求库uni.request 封装UI 组件库uView UIuni-app 生态构建工具HBuilderX 或 Vite1.2 整体项目结构规划完整的项目需要包含两个独立工程小程序前端和后台管理系统。虽然技术栈相似但考虑到功能差异和部署需求建议采用分离式架构。project-root/ ├── mini-program/ # 小程序前端项目 │ ├── pages/ # 页面文件 │ ├── components/ # 公共组件 │ ├── stores/ # 状态管理 │ ├── utils/ # 工具函数 │ ├── static/ # 静态资源 │ └── uni.scss # 样式配置 ├── admin-system/ # 后台管理系统 │ ├── src/ │ │ ├── views/ # 页面组件 │ │ ├── components/ # 业务组件 │ │ ├── router/ # 路由配置 │ │ ├── api/ # 接口管理 │ │ └── utils/ # 工具函数 │ └── public/ └── docs/ # 项目文档 ├── api/ # 接口文档 └── deployment/ # 部署文档2. 开发环境准备与基础配置2.1 开发工具安装与配置HBuilderX 配置HBuilderX 是 uni-app 官方推荐的开发工具提供了完善的编译调试环境。下载安装 HBuilderX建议使用最新稳定版安装必要的插件eslint-js 代码规范检查git 版本控制uni-app 编译增强创建 uni-app 项目// 选择模板时使用默认模板避免过度封装 // 项目类型uni-app // 模板默认模板 // Vue 版本3Node.js 环境后台管理系统需要 Node.js 环境建议使用 LTS 版本# 检查 Node.js 版本 node --version # 要求 16.0.0 # 检查 npm 版本 npm --version # 要求 8.0.02.2 依赖管理配置小程序前端 package.json 关键依赖{ dependencies: { dcloudio/uni-app: ^3.0.0, dcloudio/uni-mp-weixin: ^3.0.0, pinia: ^2.0.0, uview-ui: ^3.0.0 }, devDependencies: { dcloudio/uni-cli-shared: ^3.0.0, types/wechat-miniprogram: ^3.0.0 } }后台管理系统 package.json 关键依赖{ dependencies: { vue: ^3.3.0, vue-router: ^4.0.0, pinia: ^2.0.0, element-plus: ^2.0.0, axios: ^1.0.0 }, devDependencies: { vite: ^4.0.0, eslint: ^8.0.0 } }2.3 基础配置文件小程序 manifest.json 配置{ name: 一番赏手办商城, appid: __UNI__XXXXXX, description: 一番赏手办购物小程序, versionName: 1.0.0, versionCode: 100, transformPx: false, app-plus: { usingComponents: true, nvueStyleCompiler: uni-app, compilerVersion: 3, splashscreen: { alwaysShowBeforeRender: true, waiting: true, autoclose: true, delay: 0 } }, mp-weixin: { appid: wxxxxxxxxxxxxxxxx, setting: { urlCheck: false }, usingComponents: true, permission: { scope.userLocation: { desc: 你的位置信息将用于小程序位置接口的效果展示 } } } }3. 核心功能模块实现3.1 用户认证与权限管理用户认证是小程序商城的基础功能需要实现微信授权登录、token 管理和自动刷新机制。登录流程封装// utils/auth.js class AuthManager { constructor() { this.tokenKey user_token this.refreshTokenKey refresh_token this.expireTimeKey token_expire } // 微信登录封装 async wechatLogin() { try { // 1. 获取微信登录code const loginRes await new Promise((resolve, reject) { uni.login({ provider: weixin, success: resolve, fail: reject }) }) // 2. 调用后端登录接口 const tokenData await this.requestLogin(loginRes.code) // 3. 存储token信息 this.setToken(tokenData) return tokenData } catch (error) { console.error(登录失败:, error) throw error } } // token自动刷新机制 async refreshToken() { const refreshToken uni.getStorageSync(this.refreshTokenKey) if (!refreshToken) { throw new Error(无有效刷新令牌) } try { const newToken await this.requestRefreshToken(refreshToken) this.setToken(newToken) return newToken } catch (error) { // 刷新失败清除本地存储重新登录 this.clearToken() throw error } } // 请求拦截器中的token处理 setupRequestInterceptor() { uni.addInterceptor(request, { invoke(args) { // 添加token到header const token uni.getStorageSync(user_token) if (token) { args.header { ...args.header, Authorization: Bearer ${token} } } return args }, fail(err) { console.error(请求失败:, err) } }) } }token 安全防护策略// 防止token盗用的措施 const securityConfig { // token过期时间建议2小时 tokenExpire: 2 * 60 * 60 * 1000, // 刷新token过期时间建议7天 refreshTokenExpire: 7 * 24 * 60 * 60 * 1000, // 请求重试次数 maxRetryCount: 3, // 敏感操作需要重新验证 sensitiveOperations: [支付, 修改密码, 提现] } // token使用监控 class TokenMonitor { constructor() { this.usageCount 0 this.lastUsedTime null } checkAbnormalUsage() { // 检测token异常使用模式 const now Date.now() if (this.usageCount 100 now - this.lastUsedTime 60000) { // 短时间内高频使用可能被盗用 this.triggerSecurityAlert() return false } return true } }3.2 商品展示与搜索功能一番赏手办商城的商品展示需要支持系列分类、稀有度标识、库存状态等特色功能。商品数据结构设计// 商品数据模型 const productSchema { id: string, // 商品ID seriesId: string, // 系列ID name: string, // 商品名称 price: number, // 价格 originalPrice: number, // 原价 images: array, // 商品图片 rarity: string, // 稀有度SSR、SR、R等 stock: number, // 库存 status: number, // 状态0-下架1-上架 sortOrder: number, // 排序权重 description: string, // 商品描述 specList: array // 规格列表 } // 系列数据模型 const seriesSchema { id: string, name: string, bannerImage: string, description: string, products: array, // 包含的商品 status: number // 系列状态 }商品列表组件实现template view classproduct-list !-- 系列筛选 -- scroll-view classseries-filter scroll-x view v-forseries in seriesList :keyseries.id :class[filter-item, activeSeries series.id ? active : ] clickchangeSeries(series.id) {{ series.name }} /view /scroll-view !-- 商品网格 -- view classproduct-grid product-card v-forproduct in productList :keyproduct.id :productproduct clickgoToDetail(product.id) / /view !-- 加载更多 -- view v-ifhasMore classload-more clickloadMore {{ loading ? 加载中... : 加载更多 }} /view /view /template script setup import { ref, onMounted } from vue import { productApi } from /api/product const seriesList ref([]) const activeSeries ref(all) const productList ref([]) const loading ref(false) const hasMore ref(true) const pageParams { page: 1, pageSize: 20 } // 加载系列列表 const loadSeries async () { try { const res await productApi.getSeriesList() seriesList.value [{ id: all, name: 全部 }, ...res.data] } catch (error) { console.error(加载系列失败:, error) } } // 加载商品列表 const loadProducts async (reset false) { if (loading.value) return loading.value true try { if (reset) { pageParams.page 1 productList.value [] hasMore.value true } const params { ...pageParams, seriesId: activeSeries.value all ? undefined : activeSeries.value } const res await productApi.getProductList(params) if (reset) { productList.value res.data.list } else { productList.value.push(...res.data.list) } hasMore.value res.data.hasNextPage pageParams.page } catch (error) { console.error(加载商品失败:, error) } finally { loading.value false } } // 切换系列 const changeSeries (seriesId) { activeSeries.value seriesId loadProducts(true) } // 加载更多 const loadMore () { if (hasMore.value !loading.value) { loadProducts() } } onMounted(() { loadSeries() loadProducts(true) }) /script3.3 购物车与订单管理购物车需要支持多商品管理、规格选择、库存验证等功能。购物车状态管理// stores/cart.js import { defineStore } from pinia export const useCartStore defineStore(cart, { state: () ({ items: [], // 购物车商品项 selectedItems: [] // 选中的商品 }), getters: { // 总数量 totalCount: (state) state.items.reduce((sum, item) sum item.quantity, 0), // 选中商品总价 selectedTotalPrice: (state) state.selectedItems.reduce((sum, item) { return sum (item.price * item.quantity) }, 0), // 是否有选中商品 hasSelected: (state) state.selectedItems.length 0 }, actions: { // 添加商品到购物车 addItem(product, spec, quantity 1) { const existingItem this.items.find(item item.productId product.id item.specId spec.id ) if (existingItem) { // 更新数量不超过库存 const newQuantity Math.min(existingItem.quantity quantity, spec.stock) existingItem.quantity newQuantity } else { this.items.push({ id: ${product.id}_${spec.id}, productId: product.id, productName: product.name, productImage: product.images[0], specId: spec.id, specName: spec.name, price: spec.price, stock: spec.stock, quantity: Math.min(quantity, spec.stock) }) } this.saveToStorage() }, // 更新商品数量 updateQuantity(itemId, quantity) { const item this.items.find(item item.id itemId) if (item quantity 0 quantity item.stock) { item.quantity quantity this.saveToStorage() } }, // 删除商品 removeItem(itemId) { this.items this.items.filter(item item.id ! itemId) this.selectedItems this.selectedItems.filter(item item.id ! itemId) this.saveToStorage() }, // 保存到本地存储 saveToStorage() { uni.setStorageSync(cart_items, this.items) }, // 从本地存储加载 loadFromStorage() { const storedItems uni.getStorageSync(cart_items) || [] this.items storedItems } } })4. 后台管理系统核心功能4.1 商品管理模块后台管理系统需要提供完整的商品 CRUD 功能包括图片上传、规格管理、库存设置等。商品表单组件template el-form :modelform :rulesrules refformRef label-width100px el-form-item label商品名称 propname el-input v-modelform.name placeholder请输入商品名称 / /el-form-item el-form-item label商品系列 propseriesId el-select v-modelform.seriesId placeholder请选择系列 el-option v-forseries in seriesOptions :keyseries.id :labelseries.name :valueseries.id / /el-select /el-form-item el-form-item label商品图片 propimages el-upload action/api/upload list-typepicture-card :file-listfileList :on-successhandleUploadSuccess :on-removehandleRemove el-iconPlus //el-icon /el-upload /el-form-item el-form-item label商品规格 spec-editor v-modelform.specList / /el-form-item el-form-item el-button typeprimary clicksubmitForm保存/el-button el-button clickresetForm重置/el-button /el-form-item /el-form /template script setup import { ref, reactive, onMounted } from vue import { ElMessage } from element-plus const formRef ref() const form reactive({ name: , seriesId: , images: [], specList: [] }) const rules { name: [{ required: true, message: 请输入商品名称, trigger: blur }], seriesId: [{ required: true, message: 请选择商品系列, trigger: change }] } // 处理图片上传 const handleUploadSuccess (response, file) { form.images.push(response.data.url) } // 提交表单 const submitForm async () { try { await formRef.value.validate() await productApi.createProduct(form) ElMessage.success(商品创建成功) } catch (error) { ElMessage.error(商品创建失败) } } /script4.2 订单管理功能订单管理需要支持订单查询、状态更新、发货操作等功能。订单状态机设计// constants/order.js export const ORDER_STATUS { PENDING: { value: 1, text: 待付款, color: orange }, PAID: { value: 2, text: 已付款, color: blue }, SHIPPED: { value: 3, text: 已发货, color: green }, COMPLETED: { value: 4, text: 已完成, color: success }, CANCELLED: { value: 5, text: 已取消, color: gray }, REFUNDED: { value: 6, text: 已退款, color: red } } export const ORDER_ACTIONS { [ORDER_STATUS.PENDING.value]: [ { action: cancel, text: 取消订单, type: danger }, { action: pay, text: 立即支付, type: primary } ], [ORDER_STATUS.PAID.value]: [ { action: ship, text: 发货, type: primary }, { action: refund, text: 退款, type: warning } ], [ORDER_STATUS.SHIPPED.value]: [ { action: complete, text: 确认收货, type: success } ] }5. 接口文档规范与 API 设计5.1 RESTful API 设计原则接口设计遵循 RESTful 规范确保接口的一致性和可维护性。统一响应格式// 成功响应 { code: 200, message: success, data: { // 业务数据 }, timestamp: 1633046400000 } // 错误响应 { code: 400, message: 参数验证失败, data: null, timestamp: 1633046400000 }分页参数规范// 请求参数 { page: 1, // 页码从1开始 pageSize: 20, // 每页数量 keyword: , // 搜索关键词 sortField: createTime, // 排序字段 sortOrder: desc // 排序方向 } // 响应数据 { list: [], // 数据列表 total: 100, // 总记录数 page: 1, // 当前页码 pageSize: 20, // 每页数量 totalPages: 5 // 总页数 }5.2 接口文档生成使用 Swagger/OpenAPI 规范生成接口文档便于前后端协作。商品相关接口示例# 获取商品列表 /products: get: summary: 获取商品列表 parameters: - name: page in: query schema: type: integer default: 1 - name: pageSize in: query schema: type: integer default: 20 - name: seriesId in: query schema: type: string responses: 200: description: 成功 content: application/json: schema: type: object properties: code: type: integer message: type: string data: $ref: #/components/schemas/ProductListResponse # 创建商品 /products: post: summary: 创建商品 requestBody: required: true content: application/json: schema: $ref: #/components/schemas/CreateProductRequest responses: 200: description: 成功6. 常见问题排查与优化建议6.1 性能优化方案小程序包体积优化// manifest.json 中配置优化选项 { mp-weixin: { optimization: { subPackages: true, // 开启分包 independentSubPackages: true // 独立分包 } } } // 分包配置 subPackages: [ { root: packageA, pages: [ pages/cart/cart, pages/order/order ] } ]图片资源优化策略// 图片懒加载组件 image :srcitem.image lazy-load modeaspectFill errorhandleImageError / // CDN加速配置 const imageUrl process.env.CDN_BASE_URL /images/ imagePath // 图片压缩处理 const compressImage async (filePath, quality 0.8) { return new Promise((resolve, reject) { uni.compressImage({ src: filePath, quality: quality, success: resolve, fail: reject }) }) }6.2 常见错误处理网络请求异常处理// 统一的请求封装 class Request { constructor() { this.baseURL process.env.BASE_API this.timeout 10000 } async request(config) { try { const response await uni.request({ url: this.baseURL config.url, method: config.method || GET, data: config.data, header: { Content-Type: application/json, ...config.headers }, timeout: this.timeout }) // 处理业务错误 if (response.data.code ! 200) { this.handleBusinessError(response.data) throw new Error(response.data.message) } return response.data } catch (error) { this.handleNetworkError(error) throw error } } handleBusinessError(errorData) { // token过期处理 if (errorData.code 401) { // 跳转到登录页 uni.navigateTo({ url: /pages/login/login }) return } // 其他业务错误提示 uni.showToast({ title: errorData.message, icon: none }) } handleNetworkError(error) { uni.showToast({ title: 网络异常请检查网络连接, icon: none }) } }数据缓存策略// 缓存管理类 class CacheManager { constructor() { this.defaultExpire 30 * 60 * 1000 // 30分钟 } set(key, data, expire this.defaultExpire) { const cacheData { data, expireTime: Date.now() expire, timestamp: Date.now() } uni.setStorageSync(key, JSON.stringify(cacheData)) } get(key) { const cacheStr uni.getStorageSync(key) if (!cacheStr) return null try { const cacheData JSON.parse(cacheStr) // 检查是否过期 if (Date.now() cacheData.expireTime) { this.remove(key) return null } return cacheData.data } catch (error) { console.error(缓存数据解析失败:, error) this.remove(key) return null } } remove(key) { uni.removeStorageSync(key) } }6.3 安全防护措施接口安全防护// 请求签名验证 const generateSignature (params, timestamp, nonce) { const secret process.env.API_SECRET const paramStr Object.keys(params) .sort() .map(key ${key}${params[key]}) .join() return md5(${paramStr}timestamp${timestamp}nonce${nonce}secret${secret}) } // XSS防护 const xssFilter (str) { return str.replace(/[]/g, (match) { const escapeMap { : lt;, : gt;, : quot;, : #x27; } return escapeMap[match] }) }在实际开发过程中建议建立完善的错误监控体系及时收集和处理运行时异常。同时定期进行代码审查和安全审计确保系统的稳定性和安全性。这套技术方案经过多个实际项目验证能够支撑一番赏手办商城从开发到上线的完整流程。关键是要根据具体业务需求进行调整和优化特别是在用户体验和性能方面需要持续改进。

相关新闻