diff --git a/README.md b/README.md index 6e50594..0009d1e 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,51 @@ └── uni_modules # uni-app插件模块 ``` +## 分包结构说明 + +### pages_order分包 +分包是小程序优化加载性能的重要手段,pages_order作为独立分包,包含以下模块: + +``` +├── auth # 认证相关页面 +│ ├── loginAndRegisterAndForgetPassword.vue # 登录注册 +│ ├── wxLogin.vue # 微信登录 +│ └── wxUserInfo.vue # 微信用户信息 +├── components # 分包内公共组件 +│ ├── address/ # 地址选择组件 +│ ├── areaSelector/ # 区域选择器 +│ └── product/ # 商品相关组件 +├── home # 首页相关 +│ ├── addEnterprise.vue # 添加企业 +│ ├── contact.vue # 联系我们 +│ ├── introduce.vue # 介绍页面 +│ ├── journalism.vue # 新闻资讯 +│ └── notice.vue # 公告 +├── mine # 我的模块 +│ ├── address.vue # 收货地址 +│ ├── balance.vue # 余额 +│ ├── commission.vue # 佣金 +│ ├── coupon.vue # 优惠券 +│ ├── memberCenter.vue # 会员中心 +│ └── more... # 更多功能页面 +├── order # 订单模块 +│ ├── createOrder.vue # 创建订单 +│ ├── orderDetail.vue # 订单详情 +│ └── giftList.vue # 礼品列表 +├── product # 商品模块 +│ └── productDetail.vue # 商品详情 +└── static # 分包静态资源 + ├── address/ # 地址相关图片 + ├── auth/ # 认证相关图片 + ├── coupon/ # 优惠券图片 + └── more... # 其他静态资源 +``` + +**分包特点:** +- 静态资源就近原则:分包相关的图片等静态资源存放在分包目录下,避免主包体积过大 +- 模块化组织:按功能模块划分目录,便于维护和管理 +- 组件复用:分包内的通用组件集中管理,提高代码复用性 + ## 核心模块详解 ### 1. Mixins 混入 @@ -99,14 +144,17 @@ export default { create: { url: '/api/product/create', method: 'POST', - loading: true + loading: true // 显示加载提示 + auth : true,//效验登录 + debounce : 1000,//接口防抖,1s + limit : 500,//接口限流,0.5s }, } ``` **调用接口示例:** ```javascript -// 1. 基础列表查询 +// 第一种写法:callback方式处理响应 this.$api('product.list', { pageNo: 1, pageSize: 10, @@ -115,13 +163,13 @@ this.$api('product.list', { // 处理列表数据 }) -// 2. 创建商品 +// 第二种写法:Promise方式处理响应 this.$api('product.create', { name: '商品名称', price: 99.99, description: '商品描述' -}, res => { - if (res.code === 0) { +}).then(res => { + if (res.code === 200) { // 创建成功 uni.showToast({ title: '创建成功' }) } @@ -140,6 +188,13 @@ this.$api('product.create', { **使用示例:** ```javascript + +// 授权处理 +async preservationImg(img) { + await this.$authorize('scope.writePhotosAlbum') + //在执行$authorize之后,await下面的代码都是确保授权完成的情况下执行 +}, + // 时间格式化 const formattedTime = this.$timeUtils.formatTime(new Date()) @@ -179,11 +234,6 @@ export default { data() { return { mixinsListApi: 'product.list', - queryParams: { - pageNo: 1, - pageSize: 10, - categoryId: '' - } } }, methods: { @@ -239,51 +289,6 @@ export default { - 确认网络请求是否正常 - 查看请求参数格式是否正确 -## 分包结构说明 - -### pages_order分包 -分包是小程序优化加载性能的重要手段,pages_order作为独立分包,包含以下模块: - -``` -├── auth # 认证相关页面 -│ ├── loginAndRegisterAndForgetPassword.vue # 登录注册 -│ ├── wxLogin.vue # 微信登录 -│ └── wxUserInfo.vue # 微信用户信息 -├── components # 分包内公共组件 -│ ├── address/ # 地址选择组件 -│ ├── areaSelector/ # 区域选择器 -│ └── product/ # 商品相关组件 -├── home # 首页相关 -│ ├── addEnterprise.vue # 添加企业 -│ ├── contact.vue # 联系我们 -│ ├── introduce.vue # 介绍页面 -│ ├── journalism.vue # 新闻资讯 -│ └── notice.vue # 公告 -├── mine # 我的模块 -│ ├── address.vue # 收货地址 -│ ├── balance.vue # 余额 -│ ├── commission.vue # 佣金 -│ ├── coupon.vue # 优惠券 -│ ├── memberCenter.vue # 会员中心 -│ └── more... # 更多功能页面 -├── order # 订单模块 -│ ├── createOrder.vue # 创建订单 -│ ├── orderDetail.vue # 订单详情 -│ └── giftList.vue # 礼品列表 -├── product # 商品模块 -│ └── productDetail.vue # 商品详情 -└── static # 分包静态资源 - ├── address/ # 地址相关图片 - ├── auth/ # 认证相关图片 - ├── coupon/ # 优惠券图片 - └── more... # 其他静态资源 -``` - -**分包特点:** -- 静态资源就近原则:分包相关的图片等静态资源存放在分包目录下,避免主包体积过大 -- 模块化组织:按功能模块划分目录,便于维护和管理 -- 组件复用:分包内的通用组件集中管理,提高代码复用性 - ## 配置文件说明 ### config.js diff --git a/api/api.js b/api/api.js index 349142f..d566315 100644 --- a/api/api.js +++ b/api/api.js @@ -22,8 +22,8 @@ export function api(key, data, callback, loadingTitle) { let req = config[key] if (!req) { - console.error('无效key--------' + key); - return + console.error('无效key' + key); + return Promise.reject() } if (typeof callback == 'string') { @@ -40,7 +40,7 @@ export function api(key, data, callback, loadingTitle) { let storageKey = req.url let storage = limit[storageKey] if (storage && new Date().getTime() - storage < req.limit) { - return + return Promise.reject() } limit[storageKey] = new Date().getTime() } @@ -49,8 +49,8 @@ export function api(key, data, callback, loadingTitle) { if (req.auth) { if (!uni.getStorageSync('token')) { utils.toLogin() - console.error('需要登录') - return + console.error('需要登录', req.url) + return Promise.reject() } } @@ -74,19 +74,22 @@ export function api(key, data, callback, loadingTitle) { loadingTitle || req.showLoading, loadingTitle || req.loadingTitle) }, req.debounce) - return + return Promise.reject() } - http.http(req.url, data, callback, req.method, + return http.http(req.url, data, callback, req.method, loadingTitle || req.showLoading, loadingTitle || req.loadingTitle) } - function addApiModel(model, key){ for(let k in model){ if(config[`${k}`]){ console.error(`重名api------model=${key},key=${k}`); + uni.showModal({ + title: `重名api`, + content: `model=${key},key=${k}` + }) continue } config[`${k}`] = model[k] @@ -98,4 +101,5 @@ models.forEach(key => { addApiModel(require(`./model/${key}.js`).default, key) }) + export default api \ No newline at end of file diff --git a/api/http.js b/api/http.js index 8f2e8b8..6c3da63 100644 --- a/api/http.js +++ b/api/http.js @@ -1,7 +1,7 @@ import Vue from 'vue' import utils from '../utils/utils.js' - +import store from '../store/store.js' function http(uri, data, callback, method = 'GET', showLoading, title) { @@ -11,16 +11,23 @@ function http(uri, data, callback, method = 'GET', showLoading, title) { }); } + let reject, resolve; + + let promise = new Promise((res, rej) => { + reject = rej + resolve = res + }) + uni.request({ url: Vue.prototype.$config.baseUrl + uri, - data: enhanceData(data), + data, method: method, header: { 'X-Access-Token': uni.getStorageSync('token'), 'Content-Type' : 'application/x-www-form-urlencoded' }, success: (res) => { - + // console.log(res,'res') if(showLoading){ uni.hideLoading(); } @@ -28,7 +35,7 @@ function http(uri, data, callback, method = 'GET', showLoading, title) { if(res.statusCode == 401 || res.data.message == '操作失败,token非法无效!' || res.data.message == '操作失败,用户不存在!'){ - uni.removeStorageSync('token') + store.commit('logout') console.error('登录过期'); utils.toLogin() } @@ -42,10 +49,13 @@ function http(uri, data, callback, method = 'GET', showLoading, title) { icon:'none' }); } - callback(res.data) + + callback && callback(res.data) + resolve(res.data) }, fail: () => { + reject('api fail') uni.showLoading({}) setTimeout(()=>{ uni.hideLoading() @@ -57,79 +67,11 @@ function http(uri, data, callback, method = 'GET', showLoading, title) { } } }); + + return promise } -function deleted(uri, data, callback) { - http(uri, data, callback, 'DELETE') -} - -function post(uri, data, callback) { - http(uri, data, callback, 'POST') -} - -function get(uri, data, callback) { - http(uri, data, callback, 'GET') -} - -function enhanceData(data) { - const userid = uni.getStorageSync("userid") - if (!data) { - data = {} - } - if (userid) { - data.userid = userid - } - return data -} - - - - - -function sync(method, uri, data) { - return new Promise((resolve, reject) => { - uni.request({ - url: uri, - data: data, - method: method, - header: { - 'auth': '1AS9F1HPC4FBC9EN00J7KX2L5RJ99XHZ' - }, - success: (res) => { - resolve(res.data) - }, - fail: (err) => { - reject(err); - } - }) - }) -} - - -let cache = null - -function async (method, uri, data) { - const promise = sync(method, uri, data).then(res => { - cache = res - }).catch(err => { - - }) -} - - -function syncHttp(uri, data, method = 'GET') { - async (method, uri, data) -} - - - - - export default { http: http, - delete: deleted, - post: post, - get: get, - syncHttp: syncHttp } \ No newline at end of file diff --git a/manifest.json b/manifest.json index 3c43639..dc7e3c5 100644 --- a/manifest.json +++ b/manifest.json @@ -1,6 +1,6 @@ { "name" : "unapp模板", - "appid" : "__UNI__FB049D5", + "appid" : "__UNI__197A38F", "description" : "", "versionName" : "1.0.0", "versionCode" : "100", diff --git a/mixins/list.js b/mixins/list.js index a6aa6e9..23bb70e 100644 --- a/mixins/list.js +++ b/mixins/list.js @@ -1,22 +1,21 @@ - - - +/** + * 处理查询参数 + * @param {Object} self - 组件实例 + * @param {Object} queryParams - 额外的查询参数 + * @returns {Object} 合并后的查询参数 + */ function query(self, queryParams){ - // return (self.beforeGetData && self.beforeGetData()) || - // queryParams || self.queryParams - // 深度合并对象 return self.$utils.deepMergeObject( - - self.$utils.deepMergeObject(self.queryParams, - - (self.beforeGetData && self.beforeGetData()) || {}), - - queryParams) + self.$utils.deepMergeObject(self.queryParams, + (self.beforeGetData && self.beforeGetData()) || {}), + queryParams) } - - +/** + * 列表数据加载混入 + * 提供列表数据的加载、分页、下拉刷新、上拉加载更多等功能 + */ export default { data() { return { @@ -28,16 +27,24 @@ export default { list : [], } }, + // 下拉刷新 onPullDownRefresh() { this.getData() }, + // 上拉加载更多 onReachBottom() { this.loadMoreData() }, + // 页面显示时加载数据 onShow() { this.getData() }, methods: { + /** + * 获取列表数据 + * @param {Object} queryParams - 查询参数 + * @returns {Promise} 返回Promise对象 + */ getData(queryParams){ return new Promise((success, error) => { if(!this.mixinsListApi){ @@ -47,20 +54,21 @@ export default { query(this, queryParams), res => { uni.stopPullDownRefresh() if(res.code == 200){ - success(res.result) - + // 更新列表数据 this[this.mixinsListKey || 'list'] = res.result.records || res.result - + // 更新总数 this.total = res.result.total || res.result.length - + // 调用数据加载完成的回调 this.getDataThen && this.getDataThen(res.result.records, res.result.total, res.result) } }) }) }, + /** + * 加载更多数据 + */ loadMoreData(){ - console.log('loadMoreData----', this.queryParams.pageSize < this.total); if(this.queryParams.pageSize < this.total){ this.queryParams.pageSize += 10 this.getData() diff --git a/pages.json b/pages.json index 009084e..05f4c9f 100644 --- a/pages.json +++ b/pages.json @@ -134,6 +134,12 @@ }, { "path": "mine/updateUser" + }, + { + "path": "order/instantGift" + }, + { + "path": "order/giftList" } ] }], diff --git a/pages/index/center.vue b/pages/index/center.vue index 5f2b011..4d5d93b 100644 --- a/pages/index/center.vue +++ b/pages/index/center.vue @@ -137,6 +137,10 @@ 我的地址 + + + 礼包列表 + 退出登录 diff --git a/pages/index/order.vue b/pages/index/order.vue index 3bcff02..161dcdc 100644 --- a/pages/index/order.vue +++ b/pages/index/order.vue @@ -182,6 +182,8 @@ .status { font-weight: 600; color: #FFAC2F; + flex-shrink: 0; + margin-left: 20rpx; } } diff --git a/pages_order/order/giftList.vue b/pages_order/order/giftList.vue new file mode 100644 index 0000000..92ae702 --- /dev/null +++ b/pages_order/order/giftList.vue @@ -0,0 +1,207 @@ + + + + + \ No newline at end of file diff --git a/pages_order/order/instantGift.vue b/pages_order/order/instantGift.vue new file mode 100644 index 0000000..b4c1d48 --- /dev/null +++ b/pages_order/order/instantGift.vue @@ -0,0 +1,238 @@ + + + + + \ No newline at end of file diff --git a/pages_order/order/receiveGift.vue b/pages_order/order/receiveGift.vue new file mode 100644 index 0000000..5d4e4b5 --- /dev/null +++ b/pages_order/order/receiveGift.vue @@ -0,0 +1,232 @@ + + + + + \ No newline at end of file diff --git a/pages_order/product/productDetail.vue b/pages_order/product/productDetail.vue index abdbd49..cc76241 100644 --- a/pages_order/product/productDetail.vue +++ b/pages_order/product/productDetail.vue @@ -96,7 +96,11 @@ - + @@ -142,6 +146,9 @@ this.$utils.navigateTo('/pages_order/order/createOrder') }, + toSend(){ + this.$utils.navigateTo('/pages_order/order/instantGift') + }, // 获取商品 getRiceProductDetail() { this.$api('getRiceProductDetail', { diff --git a/store/store.js b/store/store.js index d0cb825..d07d3ce 100644 --- a/store/store.js +++ b/store/store.js @@ -124,7 +124,6 @@ const store = new Vuex.Store({ success(r) { if (r.confirm) { state.userInfo = {} - state.role = false uni.removeStorageSync('token') uni.reLaunch({ url: '/pages/index/index' diff --git a/utils/pay.js b/utils/pay.js index 9f97091..a41698d 100644 --- a/utils/pay.js +++ b/utils/pay.js @@ -3,26 +3,35 @@ import jWeixin from './lib/jweixin-module.js' // #endif +/** + * 调用微信支付 + * @param {Object} res - 支付参数对象,包含appId、timeStamp、nonceStr等必要信息 + * @param {Function} successCallback - 支付成功的回调函数 + * @param {Function} failCallback - 支付失败的回调函数 + * @param {Function} optionCallback - 配置失败的回调函数 + */ export function wxPay(res, successCallback, failCallback, optionCallback) { + // 配置微信JSSDK jWeixin.config({ debug: false, - appId: res.result.appId, //必填 - jsApiList: ['chooseWXPay'] + appId: res.result.appId, //必填,公众号的唯一标识 + jsApiList: ['chooseWXPay'] //必填,需要使用的JS接口列表 }); + // JSSDK配置成功后的回调 jWeixin.ready(function() { + // 调用微信支付接口 jWeixin.chooseWXPay({ appId: res.result.appId, - timestamp: res.result - .timeStamp, // 支付签名时间戳,注意微信 jssdk 中的所有使用 timestamp 字段均为小写。但最新版的支付后台生成签名使用的 timeStamp 字段名需大写其中的 S 字符 - nonceStr: res.result.nonceStr, // 支付签名随机串,不长于 32 位 - package: res.result.packageValue, // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=\*\*\*) - signType: res.result.signType, // 微信支付V3的传入 RSA ,微信支付V2的传入格式与V2统一下单的签名格式保持一致 + timestamp: res.result.timeStamp, // 支付签名时间戳 + nonceStr: res.result.nonceStr, // 支付签名随机串 + package: res.result.packageValue, // 统一支付接口返回的prepay_id参数值 + signType: res.result.signType, // 签名类型,默认为MD5 paySign: res.result.paySign, // 支付签名 - success: function() { // 支付成功取消处理 + success: function() { successCallback && successCallback(); }, - fail: function(error) { // 支付失败或取消处理 + fail: function(error) { failCallback && failCallback(); }, cancel : function(){ @@ -31,9 +40,8 @@ export function wxPay(res, successCallback, failCallback, optionCallback) { }); }); + // JSSDK配置失败处理 jWeixin.error(function(res) { - // 配置失败处理 optionCallback && optionCallback() }); - -} \ No newline at end of file +} diff --git a/utils/utils.js b/utils/utils.js index 2b07dde..2dd6729 100644 --- a/utils/utils.js +++ b/utils/utils.js @@ -1,3 +1,8 @@ +/** + * 将数据转换为数组格式 + * @param {any} data - 需要转换的数据 + * @returns {Array} 转换后的数组 + */ function toArray(data) { if (!data) return [] if (data instanceof Array){ @@ -7,6 +12,10 @@ function toArray(data) { } } +/** + * 生成UUID + * @returns {string} 生成的UUID字符串 + */ function generateUUID() { return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) { var r = Math.random() * 16 | 0, @@ -15,6 +24,10 @@ function generateUUID() { }); } +/** + * 生成随机颜色 + * @returns {string} 生成的十六进制颜色值 + */ function generateRandomColor() { const letters = '0123456789ABCDEF'; let color = '#'; @@ -24,6 +37,10 @@ function generateRandomColor() { return color; } +/** + * 生成浅色系的随机颜色 + * @returns {string} 生成的RGB格式颜色值 + */ function generateLightRandomColor() { const min = 150; const range = 105; @@ -34,6 +51,12 @@ function generateLightRandomColor() { return color; } +/** + * 表单数据验证 + * @param {Object} data - 需要验证的表单数据 + * @param {Object} msg - 验证失败时的提示信息 + * @returns {boolean} 验证结果,true表示验证失败,false表示验证通过 + */ function verificationAll(data, msg){ if (!msg){ @@ -57,39 +80,14 @@ function verificationAll(data, msg){ return true } } - - - // let Msgs = { - // default : msg || '表单数据未填写' - // } - - // if(typeof msg == 'object'){ - // Msgs = { - // default : '表单数据未填写', - // ...msg, - // } - // } - - // if (!data){ - // uni.showToast({ - // title: Msgs.default, - // icon: "none" - // }) - // return true - // } - // for (let key in data) { - // if (!data[key] || data[key] === "") { - // uni.showToast({ - // title: (Msgs[key] || Msgs.default), - // icon: "none" - // }) - // return true - // } - // } return false } -//验证手机号是否合法 +/** + * 验证手机号是否合法 + * @param {string} phone - 需要验证的手机号 + * @returns {boolean} 验证结果,true表示合法,false表示不合法 + */ function verificationPhone(phone){ if(!/^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/.test(phone)){ return false @@ -98,6 +96,11 @@ function verificationPhone(phone){ } //获取url中参数的方法 +/** + * 获取URL中指定参数的值 + * @param {string} name - 参数名称 + * @returns {string} 参数值,如果不存在则返回空字符串 + */ export function getHrefParams(name) { var url = window.location.href; try { @@ -114,8 +117,12 @@ export function getHrefParams(name) { } } - -//深度对比合并两个对象,相同属性b会覆盖a +/** + * 深度合并两个对象,相同属性b会覆盖a + * @param {Object} a - 目标对象 + * @param {Object} b - 源对象 + * @returns {Object} 合并后的新对象 + */ export function deepMergeObject(a, b){ let data = JSON.parse(JSON.stringify(a)) function mergeObject(obj1, obj2){ @@ -131,7 +138,10 @@ export function deepMergeObject(a, b){ return mergeObject(data, b) } -//复制内容 +/** + * 复制文本到剪贴板 + * @param {string} content - 要复制的内容 + */ export function copyText(content) { uni.setClipboardData({ data: content, @@ -144,12 +154,21 @@ export function copyText(content) { }) } -// 将字符串中的文本格式化html +/** + * 将字符串中的文本格式化为HTML + * @param {string} str - 需要格式化的字符串 + * @returns {string} 格式化后的HTML字符串 + */ export function stringFormatHtml(str){ return str && str.replace(/\n/gi, '
') .replace(/ /gi, ' ') } +/** + * 处理页面导航参数 + * @param {string|Object} url - 页面路径或导航参数对象 + * @returns {Object} 处理后的导航参数对象 + */ function params(url){ if(typeof url == 'object'){ return url @@ -166,18 +185,34 @@ function params(url){ return data } - +/** + * 页面导航方法 + * @param {...any} args - 导航参数 + */ export function navigateTo(...args){ uni.navigateTo(params(...args)) } +/** + * 返回上一页 + * @param {number} num - 返回的页面数,默认为-1 + */ export function navigateBack(num = -1){ uni.navigateBack(num) } +/** + * 重定向到指定页面 + * @param {...any} args - 导航参数 + */ export function redirectTo(...args){ uni.redirectTo(params(...args)) } + +/** + * 登录跳转函数,防止短时间内多次调用 + * @returns {Function} 节流处理后的登录跳转函数 + */ export const toLogin = function(){ let time = 0 return () => {