# uniapp图书商城(h5微信小程序) **Repository Path**: hellojinjin/uniapp_shop ## Basic Information - **Project Name**: uniapp图书商城(h5微信小程序) - **Description**: uni-app图书商城项目, 主要用了uView作为ui框架。目前实现了: - 首页展示 - 商品分类 - 购物车 - 订单功能 - 个人中心 - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 10 - **Forks**: 5 - **Created**: 2021-12-08 - **Last Updated**: 2025-09-07 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 关于图书商城 uni-app图书商城项目, 主要用了uView作为ui框架。目前实现了: - 注册登陆 - 首页展示 - 商品分类 - 购物车 - 订单功能 - 个人中心 其中,购物车,和订单部分功能用了原生uni组件和mix-mall 电商项目模版速成 ## uView版本 目前刚更新uView2.0,不过很多内置样式和组件都不完,建议使用1.x版本 直接在官网下载已经配置uView空项目即可。 ## Http请求 ```js // http.api.js let url = { index: '/api/index' } // 此处第二个参数vm,就是我们在页面使用的this,你可以通过vm获取vuex等操作,更多内容详见uView对拦截器的介绍部分: // https://uviewui.com/js/http.html#%E4%BD%95%E8%B0%93%E8%AF%B7%E6%B1%82%E6%8B%A6%E6%88%AA%EF%BC%9F const install = (Vue, vm) => { // 此处没有使用传入的params参数 let getIndex = (params = {}) => vm.$u.get(url.index, params) // 将各个定义的接口名称,统一放进对象挂载到vm.$u.api(因为vm就是this,也即this.$u.api)下 vm.$u.api = { getIndex } } export default { install } ``` ```js // http.interceptor.js /** * uView已经为用户考虑好了所有的情况,并详细指导您如何在外部JS中引用vuex变量以及Vue的this实例等。 建议在根目录下新建/common/http.interceptor.js文件,也即创建common目录(如果没有的话),再创建http.interceptor.js文件,将拦截器相关代码写在里面。 */ const install = (Vue, vm) => { // 此为自定义配置参数,具体参数见上方说明 Vue.prototype.$u.http.setConfig({ baseUrl: 'https://api.shop.eduwork.cn', // 请求的本域名 // 设置为json,返回后会对数据进行一次JSON.parse() dataType: 'json', showLoading: true, // 是否显示请求中的loading loadingText: '请求中...', // 请求loading中的文字提示 loadingTime: 800, // 在此时间内,请求还没回来的话,就显示加载中动画,单位ms originalData: true, // 是否在拦截器中返回服务端的原始数据 loadingMask: true, // 展示loading的时候,是否给一个透明的蒙层,防止触摸穿透 // 配置请求头信息 header: { 'content-type': 'application/json;charset=UTF-8' }, }); // 请求拦截部分,如配置,每次请求前都会执行,见上方说明 Vue.prototype.$u.http.interceptor.request = (config) => { // config.header.token = token; config.header.Token = 'Bearer '+ 'xxxxxx'; // 可以对某个url进行特别处理,此url参数为this.$u.get(url)中的url值 // if(config.url == '/user/login') config.header.noToken = true; // 最后需要将config进行return return config; } // 响应拦截,如配置,每次请求结束都会执行本方法 Vue.prototype.$u.http.interceptor.response = ({ statusCode, data }) => { if(statusCode< 400) { // 200 201 204 301 302 省事 // statusCode为服务端返回值,可能有code,result等字段 // 这里对res.result进行返回,将会在this.$u.post(url).then(res => {})的then回调中的res的到 // 如果配置了originalData为true,请留意这里的返回值 return data } else if(statusCode == 201) { // 假设201为token失效,这里跳转登录 vm.$u.toast('验证失败,请重新登录'); setTimeout(() => { // 此为uView的方法,详见路由相关文档 vm.$u.route('/pages/user/login') }, 1500) return false; } else { // 如果返回false,则会调用Promise的reject回调, // 并将进入this.$u.post(url).then().catch(res=>{})的catch回调中,res为服务端的返回值 return false; } } } export default { install } ``` ## API集中管理 ## vuex配置 ```js // index.js import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) let lifeData = {}; try{ // 尝试获取本地是否存在lifeData变量,第一次启动APP时是不存在的 lifeData = uni.getStorageSync('lifeData'); }catch(e){ } // 需要永久存储,且下次APP启动需要取出的,在state中的变量名 let saveStateKeys = ['vuex_user', 'vuex_token']; // 保存变量到本地存储中 const saveLifeData = function(key, value){ // 判断变量名是否在需要存储的数组中 if(saveStateKeys.indexOf(key) != -1) { // 获取本地存储的lifeData对象,将变量添加到对象中 let tmp = uni.getStorageSync('lifeData'); // 第一次打开APP,不存在lifeData变量,故放一个{}空对象 tmp = tmp ? tmp : {}; tmp[key] = value; // 执行这一步后,所有需要存储的变量,都挂载在本地的lifeData对象中 uni.setStorageSync('lifeData', tmp); } } const store = new Vuex.Store({ // 下面这些值仅为示例,使用过程中请删除 state: { // 如果上面从本地获取的lifeData对象下有对应的属性,就赋值给state中对应的变量 // 加上vuex_前缀,是防止变量名冲突,也让人一目了然 vuex_user: lifeData.vuex_user ? lifeData.vuex_user : {name: '明月'}, vuex_token: lifeData.vuex_token ? lifeData.vuex_token : '', // 如果vuex_version无需保存到本地永久存储,无需lifeData.vuex_version方式 vuex_version: '1.0.1', }, mutations: { $uStore(state, payload) { // 判断是否多层级调用,state中为对象存在的情况,诸如user.info.score = 1 let nameArr = payload.name.split('.'); let saveKey = ''; let len = nameArr.length; if(nameArr.length >= 2) { let obj = state[nameArr[0]]; for(let i = 1; i < len - 1; i ++) { obj = obj[nameArr[i]]; } obj[nameArr[len - 1]] = payload.value; saveKey = nameArr[0]; } else { // 单层级变量,在state就是一个普通变量的情况 state[payload.name] = payload.value; saveKey = payload.name; } // 保存变量到本地,见顶部函数定义 saveLifeData(saveKey, state[saveKey]) } } }) export default store ``` ```js // $u.mixin.js import { mapState } from 'vuex' import store from "@/store" // 尝试将用户在根目录中的store/index.js的vuex的state变量,全部加载到全局变量中 let $uStoreKey = []; try{ $uStoreKey = store.state ? Object.keys(store.state) : []; }catch(e){ } module.exports = { created() { // 将vuex方法挂在到$u中 // 使用方法为:如果要修改vuex的state中的user.name变量为"史诗" => this.$u.vuex('user.name', '史诗') // 如果要修改vuex的state的version变量为1.0.1 => this.$u.vuex('version', '1.0.1') this.$u.vuex = (name, value) => { this.$store.commit('$uStore', { name,value }) } }, computed: { // 将vuex的state中的所有变量,解构到全局混入的mixin中 ...mapState($uStoreKey) } } ``` ## main.js ```js import Vue from 'vue' import App from './App' Vue.config.productionTip = false App.mpType = 'app' // 引入全局uView import uView from 'uview-ui' Vue.use(uView); import store from '@/store'; let vuexStore = require("@/store/$u.mixin.js"); Vue.mixin(vuexStore); const app = new Vue({ ...App, store }) // http拦截器,将此部分放在new Vue()和app.$mount()之间,才能App.vue中正常使用 import httpInterceptor from '@/common/http.interceptor.js' Vue.use(httpInterceptor, app) // http接口API集中管理引入部分 import httpApi from '@/common/http.api.js' Vue.use(httpApi, app) app.$mount() ``` ## 下面就是具体撸业务 省略... ## 项目展示 image image image image image image image