diff --git a/.cursor/rules/md.mdc b/.cursor/rules/md.mdc
new file mode 100644
index 0000000..4eab25e
--- /dev/null
+++ b/.cursor/rules/md.mdc
@@ -0,0 +1,6 @@
+---
+description:
+globs:
+alwaysApply: false
+---
+请你写代码之前先查看一下README.md文件,按照这个文件中说的写
\ No newline at end of file
diff --git a/README.md b/README.md
index 7008566..b4f6709 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,385 @@
-#酒店桌布小程序
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+# 珠宝商城项目文档
+
+## 项目概述
+本项目是一个基于uni-app开发的珠宝商城小程序,采用Vue框架开发,集成了完整的商城功能模块。
+
+## 目录结构
+```
+├── api # API接口目录
+│ ├── api.js # API统一出口
+│ ├── http.js # HTTP请求封装
+│ └── model # 业务模块API
+├── components # 公共组件
+├── mixins # 混入文件
+├── pages # 页面文件
+├── static # 静态资源
+├── store # Vuex状态管理
+├── utils # 工具函数
+└── uni_modules # uni-app插件模块
+```
+
+## 分包结构说明
+
+### pages_order分包
+分包是小程序优化加载性能的重要手段,pages_order作为独立分包,包含以下模块:
+
+```
+├── auth # 认证相关页面
+├── components # 分包内公共组件
+├── mine # 我的模块
+├── order # 订单模块
+├── product # 商品模块
+└── static # 分包静态资源
+```
+
+**分包特点:**
+- 静态资源就近原则:分包相关的图片等静态资源存放在分包目录下,避免主包体积过大
+- 模块化组织:按功能模块划分目录,便于维护和管理
+- 组件复用:分包内的通用组件集中管理,提高代码复用性
+
+## 配置文件说明
+
+### config.js
+项目核心配置文件,包含以下配置:
+
+**1. 环境配置**
+```javascript
+// 当前环境
+const type = 'prod'
+
+// 环境配置
+const config = {
+ dev: {
+ baseUrl: 'http://h5.xzaiyp.top/jewelry-admin',
+ },
+ prod: {
+ baseUrl: 'https://jewelry-admin.hhlm1688.com/jewelry-admin',
+ }
+}
+```
+
+**2. 默认配置**
+```javascript
+const defaultConfig = {
+ // 腾讯地图Key
+ mapKey: 'XMBBZ-BCPCV-SXPPQ-5Y7MY-PHZXK-YFFVU',
+ // 阿里云OSS配置
+ aliOss: {
+ url: 'https://image.hhlm1688.com/',
+ config: {
+ region: 'oss-cn-guangzhou',
+ accessKeyId: '***',
+ accessKeySecret: '***',
+ bucket: 'hanhaiimage',
+ endpoint: 'oss-cn-shenzhen.aliyuncs.com',
+ }
+ }
+}
+```
+
+**3. UI框架配置**
+```javascript
+uni.$uv.setConfig({
+ config: {
+ unit: 'rpx' // 设置默认单位
+ },
+})
+```
+
+## 核心模块详解
+
+### 1. Mixins 混入
+
+#### 1.1 list.js - 列表数据加载混入
+提供列表数据的加载、分页、下拉刷新、上拉加载更多等功能。
+
+**主要功能:**
+- 统一的分页参数处理
+- 下拉刷新和上拉加载更多
+- 数据加载状态管理
+
+**使用示例:**
+```javascript
+// 在页面中使用list混入
+import listMixin from '@/mixins/list.js'
+
+export default {
+ mixins: [listMixin],
+ data() {
+ return {
+ // 指定API接口
+ mixinsListApi: 'product.list'
+ }
+ }
+}
+```
+
+#### 1.2 configList.js - 全局配置混入
+已全局引入的配置管理混入,无需手动引入即可使用。
+
+**主要功能:**
+- 统一的分享配置
+- 全局配置管理
+- 用户信息关联
+
+**配置参数:**
+```javascript
+// 分享配置示例
+this.Gshare.title = '分享标题'
+this.Gshare.path = '分享路径'
+```
+
+### 2. API 模块
+
+#### 2.1 http.js - 请求封装
+统一的HTTP请求处理,包含:
+- 请求拦截器
+- 响应拦截器
+- 统一的错误处理
+- Token管理
+
+#### 2.2 api.js - 接口管理
+统一管理API接口,支持模块化组织。API模块采用分层结构,便于维护和扩展。
+
+**目录结构:**
+```
+api/
+├── api.js # API统一出口
+├── http.js # HTTP请求封装
+└── model/ # 业务模块API
+ ├── product.js # 商品相关接口
+ ├── order.js # 订单相关接口
+ └── user.js # 用户相关接口
+```
+
+**接口定义示例:**
+```javascript
+// api/model/product.js
+export default {
+ // GET请求示例
+ list: {
+ url: '/api/product/list',
+ method: 'GET',
+ loading: true // 显示加载提示
+ },
+
+ // POST请求示例
+ create: {
+ url: '/api/product/create',
+ method: 'POST',
+ loading: true // 显示加载提示
+ auth : true,//效验登录
+ debounce : 1000,//接口防抖,1s
+ limit : 500,//接口限流,0.5s
+ },
+}
+```
+
+**调用接口示例:**
+```javascript
+// 第一种写法:callback方式处理响应
+this.$api('product.list', {
+ pageNo: 1,
+ pageSize: 10,
+ categoryId: '123'
+}, res => {
+ // 处理列表数据
+})
+
+// 第二种写法:Promise方式处理响应
+this.$api('product.create', {
+ name: '商品名称',
+ price: 99.99,
+ description: '商品描述'
+}).then(res => {
+ if (res.code === 200) {
+ // 创建成功
+ uni.showToast({ title: '创建成功' })
+ }
+})
+```
+
+
+### 3. 公共代码
+
+#### 3.1 工具函数 (utils)
+- authorize.js: 授权处理
+- pay.js: 微信网页支付相关
+- utils.js: 通用工具函数
+- timeUtils.js: 时间处理
+- position.js: 定位与位置计算
+- oss-upload: 阿里云OSS上传模块
+
+
+**使用示例:**
+```javascript
+
+// 授权处理
+async preservationImg(img) {
+ await this.$authorize('scope.writePhotosAlbum')
+ //在执行$authorize之后,await下面的代码都是确保授权完成的情况下执行
+},
+
+// 时间格式化
+const formattedTime = this.$timeUtils.formatTime(new Date())
+
+// 微信网页支付调用
+import { wxPay } from '@/utils/pay'
+wxPay(orderData)
+```
+
+#### 3.2 公共组件
+- navbar.vue: 自定义导航栏
+- tabbar.vue: 底部导航栏
+- productItem.vue: 商品列表项
+
+**使用示例:**
+```html
+
+
+
+
+
+
+```
+
+
+#### 3.3 OSS上传模块
+
+**配置说明:**
+项目使用阿里云OSS进行文件存储,相关配置位于config.js中:
+
+
+**使用示例:**
+
+1. 单文件上传
+```javascript
+export default {
+ methods: {
+ onUpload(file) {
+ this.$Oss.ossUpload(file.path).then(url => {
+ this.filePath = url
+ })
+ }
+ }
+}
+```
+
+2. 在uv-upload组件中使用
+```html
+
+
+
+
+
+```
+
+**注意事项:**
+1. 上传前请确保OSS配置正确
+2. 建议对上传文件大小进行限制
+3. 支持的文件类型:图片、视频、文档等
+4. 上传失败时会抛出异常,请做好错误处理
+
+## 最佳实践
+
+### 1. 列表页面开发
+```javascript
+// pages/product/list.vue
+import listMixin from '@/mixins/list.js'
+
+export default {
+ mixins: [listMixin],
+ data() {
+ return {
+ mixinsListApi: 'product.list',
+ }
+ },
+ methods: {
+ // 分类切换
+ onCategoryChange(categoryId) {
+ this.queryParams.categoryId = categoryId
+ this.getData()
+ }
+ }
+}
+```
+
+### 2. 详情页面开发
+```javascript
+// pages/product/detail.vue
+import configMixin from '@/mixins/configList.js'
+
+export default {
+ mixins: [configMixin],
+ data() {
+ return {
+ productId: '',
+ detail: {}
+ }
+ },
+ onLoad(options) {
+ this.productId = options.id
+ this.getDetail()
+ },
+ methods: {
+ getDetail() {
+ this.$api('product.detail', {
+ id: this.productId
+ }, res => {
+ this.detail = res.result
+ // 设置分享信息
+ this.Gshare.title = this.detail.name
+ this.Gshare.path = `/pages/product/detail?id=${this.productId}`
+ })
+ }
+ }
+}
+```
+
+## 注意事项
+1. 使用mixins时注意命名冲突
+2. API调用建议统一使用this.$api方式
+3. 页面开发建议继承相应的混入来复用通用功能
+
+## 常见问题
+1. 列表加载失败
+ - 检查mixinsListApi是否正确配置
+ - 确认网络请求是否正常
+ - 查看请求参数格式是否正确
+
diff --git a/api/api.js b/api/api.js
index 871475a..1293351 100644
--- a/api/api.js
+++ b/api/api.js
@@ -1,9 +1,12 @@
import http from './http.js'
+import utils from '../utils/utils.js'
let limit = {}
let debounce = {}
+const models = ['login', 'index']
+
const config = {
// 示例
// wxLogin : {url : '/api/wxLogin', method : 'POST',
@@ -11,44 +14,16 @@ const config = {
// limit : 1000
// },
- getConfig : {url : '/api/getConfig', method : 'GET', limit : 500},
-
-
- // 微信登录接口
- wxLogin: {
- url: '/login/login',
- method: 'POST',
- limit : 500,
- showLoading : true,
- },
- // 修改个人信息接口
- updateInfo: {
- url: '/info/updateInfo',
- method: 'POST',
- auth: true,
- limit : 500,
- showLoading : true,
- },
- //隐私政策
- getPrivacyPolicy: {
- url: '/login/getPrivacyPolicy',
- method: 'GET',
- },
- //用户协议
- getUserAgreement: {
- url: '/login/getUserAgreement',
- method: 'GET',
- },
+ getConfig : {url : '/config_common/getConfig', method : 'GET', limit : 500},
}
-
export function api(key, data, callback, loadingTitle) {
let req = config[key]
if (!req) {
console.error('无效key' + key);
- return
+ return Promise.reject()
}
if (typeof callback == 'string') {
@@ -65,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()
}
@@ -73,11 +48,9 @@ export function api(key, data, callback, loadingTitle) {
//必须登录
if (req.auth) {
if (!uni.getStorageSync('token')) {
- uni.navigateTo({
- url: '/pages_order/auth/wxLogin'
- })
- console.error('需要登录')
- return
+ utils.toLogin()
+ console.error('需要登录', req.url)
+ return Promise.reject()
}
}
@@ -101,13 +74,32 @@ 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]
+ // config[`${key}_${k}`] = model[k]
+ }
+}
+
+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 8f7078e..6c3da63 100644
--- a/api/http.js
+++ b/api/http.js
@@ -1,6 +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) {
@@ -10,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' : method == 'POST' ? 'application/x-www-form-urlencoded' : 'application/json'
+ 'Content-Type' : 'application/x-www-form-urlencoded'
},
success: (res) => {
-
+ // console.log(res,'res')
if(showLoading){
uni.hideLoading();
}
@@ -27,14 +35,13 @@ 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('登录过期');
- uni.navigateTo({
- url: '/pages_order/auth/wxLogin'
- })
+ utils.toLogin()
}
- if(res.statusCode == 200 && res.data.code != 200){
+ if(res.statusCode == 200 && res.data.code != 200
+ && res.data.code != 902){
uni.showToast({
mask: true,
duration: 1000,
@@ -43,10 +50,12 @@ function http(uri, data, callback, method = 'GET', showLoading, title) {
});
}
- callback(res.data)
+ callback && callback(res.data)
+ resolve(res.data)
},
fail: () => {
+ reject('api fail')
uni.showLoading({})
setTimeout(()=>{
uni.hideLoading()
@@ -58,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/api/model/index.js b/api/model/index.js
new file mode 100644
index 0000000..9ec5e2e
--- /dev/null
+++ b/api/model/index.js
@@ -0,0 +1,28 @@
+
+
+// 登录相关接口
+
+const api = {
+ // 获取banner
+ getBanner: {
+ url: '/index/getBanner',
+ method: 'GET',
+ },
+ // 获取菜单
+ getIcon: {
+ url: '/index/getIcon',
+ method: 'GET',
+ },
+ // 获取商品详情
+ getProductDetail: {
+ url: '/index/getProductDetail',
+ method: 'GET',
+ },
+ // 获取商品列表
+ getProductList: {
+ url: '/index/getProductList',
+ method: 'GET',
+ },
+}
+
+export default api
\ No newline at end of file
diff --git a/api/model/login.js b/api/model/login.js
new file mode 100644
index 0000000..3ff9999
--- /dev/null
+++ b/api/model/login.js
@@ -0,0 +1,34 @@
+
+
+// 登录相关接口
+
+const api = {
+ // 微信登录接口
+ wxLogin: {
+ url: '/all_login/appletLogin',
+ method: 'GET',
+ limit : 500,
+ },
+ // 获取绑定手机号码
+ bindPhone: {
+ url: '/login_common/bindPhone',
+ method: 'GET',
+ auth: true,
+ },
+ // 修改个人信息接口
+ updateInfo: {
+ url: '/info_common/updateInfo',
+ method: 'POST',
+ auth: true,
+ limit : 500,
+ showLoading : true,
+ },
+ // 获取个人信息
+ getInfo: {
+ url: '/info_common/getInfo',
+ method: 'GET',
+ auth: true,
+ },
+}
+
+export default api
\ No newline at end of file
diff --git a/components/base/navbar.vue b/components/base/navbar.vue
index 8ada4ad..34d6fee 100644
--- a/components/base/navbar.vue
+++ b/components/base/navbar.vue
@@ -2,27 +2,33 @@
+ :style="{backgroundColor : bgColor,color}">
+
+
+
+ :color="color" size="46rpx">
{{ title }}
+ :color="color" size="58rpx">
-
-
@@ -65,7 +71,9 @@
bgColor : {
default : '#fff'
},
- color : '#333',
+ color : {
+ default : '#333'
+ }
},
created() {
},
@@ -73,9 +81,18 @@
},
data() {
return {
+ length : getCurrentPages().length
};
},
methods : {
+ toHome(){
+ if(this.length != 1){
+ return
+ }
+ uni.reLaunch({
+ url: '/pages/index/index'
+ })
+ }
}
}
@@ -99,7 +116,7 @@
justify-content: center;
font-size: 32rpx;
align-items: center;
- z-index: 99999;
+ z-index: 999;
.left{
position: absolute;
left: 40rpx;
diff --git a/components/base/tabbar.vue b/components/base/tabbar.vue
index d5e7657..6135e05 100644
--- a/components/base/tabbar.vue
+++ b/components/base/tabbar.vue
@@ -1,19 +1,14 @@
-
-
+
-
-
-
+
+
+ item.iconPath" class="tabbar-icon-image" mode="aspectFill">
-
+
{{ item.title }}
@@ -22,56 +17,47 @@
\ No newline at end of file
diff --git a/config.js b/config.js
index 4175d2b..cf71a65 100644
--- a/config.js
+++ b/config.js
@@ -13,10 +13,10 @@ const type = 'dev'
// 环境配置
const config = {
dev : {
- baseUrl : 'http://www.gcosc.fun:82',
+ baseUrl : 'http://h5.xzaiyp.top/building-admin',
},
prod : {
- baseUrl : 'http://xxx.xxx.xxx/xxx',
+ baseUrl : 'http://h5.xzaiyp.top/building-admin',
}
}
diff --git a/main.js b/main.js
index 84751ca..f971a7f 100644
--- a/main.js
+++ b/main.js
@@ -14,6 +14,10 @@ import store from '@/store/store'
import './config'
import './utils/index.js'
+import mixinConfigList from '@/mixins/configList.js'
+
+Vue.mixin(mixinConfigList)
+
//组件注册
import configPopup from '@/components/config/configPopup.vue'
import navbar from '@/components/base/navbar.vue'
diff --git a/manifest.json b/manifest.json
index ea42a21..fe96ebc 100644
--- a/manifest.json
+++ b/manifest.json
@@ -52,7 +52,7 @@
"quickapp" : {},
/* 小程序特有相关 */
"mp-weixin" : {
- "appid" : "wxe7ae8cbe1673834c",
+ "appid" : "wx328ba180b4a88d49",
"setting" : {
"urlCheck" : false
},
diff --git a/mixins/configList.js b/mixins/configList.js
new file mode 100644
index 0000000..6b5fcc1
--- /dev/null
+++ b/mixins/configList.js
@@ -0,0 +1,61 @@
+
+
+import { mapState } from 'vuex'
+export default {
+ data() {
+ return {
+ // 默认的全局分享内容
+ Gshare: {
+ // title: '三只青蛙',
+ path: '/pages_order/auth/wxLogin', // 全局分享的路径,比如 首页
+ // imageUrl: '/static/image/login/logo.png', // 全局分享的图片(可本地可网络)
+ }
+ }
+ },
+ computed: {
+ ...mapState(['configList', 'userInfo', 'riceInfo']),
+ currentPagePath() {
+ const pages = getCurrentPages();
+ const currentPage = pages[pages.length - 1];
+ let path = `/${currentPage.route}`;
+
+ // 获取当前页面的参数
+ const options = currentPage.options;
+ if (options && Object.keys(options).length > 0) {
+ const params = this.$utils.objectToUrlParams(options);
+ path += `?${params}`;
+ }
+
+ return path;
+ },
+ },
+ // 定义全局分享
+ // 1.发送给朋友
+ onShareAppMessage(res) {
+ let o = {
+ title : this.configList.logo_name,
+ ...this.Gshare,
+ }
+ if(this.userInfo.id){
+ if(this.Gshare.path.includes('?')){
+ o.path += '&shareId=' + this.userInfo.id
+ }else{
+ o.path += '?shareId=' + this.userInfo.id
+ }
+ }
+ return o
+ },
+ //2.分享到朋友圈
+ onShareTimeline(res) {
+ let o = {
+ ...this.Gshare,
+ title : this.configList.logo_name,
+ }
+ if(this.userInfo.id){
+ o.path = this.Gshare.path + '?shareId=' + this.userInfo.id
+ }
+ return o
+ },
+ methods: {
+ }
+}
\ No newline at end of file
diff --git a/mixins/order.js b/mixins/order.js
new file mode 100644
index 0000000..b1650dd
--- /dev/null
+++ b/mixins/order.js
@@ -0,0 +1,70 @@
+
+
+export default {
+ data() {
+ return {
+ }
+ },
+ computed: {
+ },
+ methods: {
+ // 立即支付
+ toPayOrder(item){
+ let api = ''
+
+ // if([0, 1].includes(item.shopState)){
+ // api = 'createOrderTwo'
+ // }else{
+ api = 'createSumOrderAgain'
+ // }
+
+ this.$api(api, {
+ orderId : item.id,
+ addressId : item.addressId
+ }, res => {
+ if(res.code == 200){
+ uni.requestPaymentWxPay(res)
+ .then(res => {
+ uni.showToast({
+ title: '支付成功',
+ icon: 'none'
+ })
+ this.getData()
+ }).catch(n => {
+ this.getData()
+ })
+ }
+ })
+ },
+ // 确认收货
+ confirmOrder(item){
+ uni.showModal({
+ title: '您收到货了吗?',
+ success : e => {
+ if(e.confirm){
+ this.$api('confirmOrder', {
+ orderId : item.id,
+ }, res => {
+ this.getData()
+ })
+ }
+ }
+ })
+ },
+ // 取消订单
+ cancelOrder(item){
+ uni.showModal({
+ title: '确认取消订单吗?',
+ success : e => {
+ if(e.confirm){
+ this.$api('cancelOrder', {
+ orderId : item.id,
+ }, res => {
+ this.getData()
+ })
+ }
+ }
+ })
+ },
+ }
+}
\ No newline at end of file
diff --git a/pages/index/category.vue b/pages/index/category.vue
index 5871e0f..98abb07 100644
--- a/pages/index/category.vue
+++ b/pages/index/category.vue
@@ -29,7 +29,7 @@
-
+
diff --git a/pages/index/center.vue b/pages/index/center.vue
index 1b332a3..8626d4a 100644
--- a/pages/index/center.vue
+++ b/pages/index/center.vue
@@ -117,7 +117,7 @@
-
+
diff --git a/pages/index/index.vue b/pages/index/index.vue
index e126cdc..4e137cd 100644
--- a/pages/index/index.vue
+++ b/pages/index/index.vue
@@ -26,7 +26,7 @@
-
+
@@ -94,16 +94,19 @@
text="立即购买">
+
-
+
diff --git a/pages_order/order/voiceOrder.vue b/pages_order/order/voiceOrder.vue
index 8fcfd45..89f0ec7 100644
--- a/pages_order/order/voiceOrder.vue
+++ b/pages_order/order/voiceOrder.vue
@@ -10,22 +10,35 @@
录制语音下单
-
+
- 长按可说话
+ {{isRecording ? '松开结束录音' : '长按可说话'}}
-
+
+ 正在录音中...
+ {{recordingTime}}s
+
+
+
+
+
+
-
-
+
+
- 录音文件01.mp3
- 12MB
+ {{audioName}}
+ {{audioSize}}
-
- 100%
+
+ {{uploadProgress}}%
@@ -34,7 +47,7 @@
-
+
快捷下单
@@ -45,8 +58,452 @@
export default {
data() {
return {
-
+ recorderManager: null, // 录音管理器
+ innerAudioContext: null, // 音频播放器
+ isRecording: false, // 是否正在录音
+ isPlaying: false, // 是否正在播放
+ audioPath: '', // 录音文件路径
+ audioName: '录音文件01.mp3', // 录音文件名称
+ audioSize: '0KB', // 录音文件大小
+ uploadProgress: 100, // 上传进度
+ recordStartTime: 0, // 录音开始时间
+ recordTimeout: null, // 录音超时计时器
+ recordMaxDuration: 60000, // 最大录音时长(毫秒)
+ recordingTime: 0, // 当前录音时长(秒)
+ recordTimer: null, // 录音计时器
+ isUploading: false, // 是否正在上传
+ recognitionResult: null, // 语音识别结果
+ waveTimer: null, // 波形动画计时器
+ waveItems: [], // 波形数据
+ audioUrl: '', // 上传后的音频URL
};
+ },
+ onLoad() {
+ // 初始化录音管理器
+ this.recorderManager = uni.getRecorderManager();
+
+ // 初始化波形数据
+ this.initWaveItems();
+
+ // 监听录音开始事件
+ this.recorderManager.onStart(() => {
+ console.log('录音开始');
+ this.isRecording = true;
+ this.recordStartTime = Date.now();
+ this.recordingTime = 0;
+
+ // 开始计时
+ this.recordTimer = setInterval(() => {
+ this.recordingTime = Math.floor((Date.now() - this.recordStartTime) / 1000);
+ }, 1000);
+
+ // 开始波形动画
+ this.startWaveAnimation();
+
+ // 设置最大录音时长
+ this.recordTimeout = setTimeout(() => {
+ if (this.isRecording) {
+ this.stopRecord();
+ }
+ }, this.recordMaxDuration);
+ });
+
+ // 监听录音结束事件
+ this.recorderManager.onStop((res) => {
+ console.log('录音结束', res);
+ this.isRecording = false;
+ if (this.recordTimeout) {
+ clearTimeout(this.recordTimeout);
+ this.recordTimeout = null;
+ }
+
+ if (this.recordTimer) {
+ clearInterval(this.recordTimer);
+ this.recordTimer = null;
+ }
+
+ // 停止波形动画
+ this.stopWaveAnimation();
+
+ if (res.tempFilePath) {
+ this.audioPath = res.tempFilePath;
+ // 计算录音文件大小并格式化
+ this.formatFileSize(res.fileSize || 0);
+ // 生成录音文件名
+ this.audioName = '录音文件' + this.formatDate(new Date()) + '.mp3';
+
+ // 如果时长过短,提示用户
+ if (this.recordingTime < 1) {
+ uni.showToast({
+ title: '录音时间太短,请重新录制',
+ icon: 'none'
+ });
+ this.audioPath = '';
+ }
+ }
+ });
+
+ // 监听录音错误事件
+ this.recorderManager.onError((err) => {
+ console.error('录音错误', err);
+ uni.showToast({
+ title: '录音失败: ' + err.errMsg,
+ icon: 'none'
+ });
+ this.isRecording = false;
+ if (this.recordTimeout) {
+ clearTimeout(this.recordTimeout);
+ this.recordTimeout = null;
+ }
+
+ if (this.recordTimer) {
+ clearInterval(this.recordTimer);
+ this.recordTimer = null;
+ }
+
+ // 停止波形动画
+ this.stopWaveAnimation();
+ });
+
+ // 初始化音频播放器
+ this.innerAudioContext = uni.createInnerAudioContext();
+
+ // 监听播放结束事件
+ this.innerAudioContext.onEnded(() => {
+ console.log('播放结束');
+ this.isPlaying = false;
+ });
+
+ // 监听播放错误事件
+ this.innerAudioContext.onError((err) => {
+ console.error('播放错误', err);
+ uni.showToast({
+ title: '播放失败',
+ icon: 'none'
+ });
+ this.isPlaying = false;
+ });
+ },
+ onUnload() {
+ // 销毁录音管理器和音频播放器
+ if (this.innerAudioContext) {
+ this.innerAudioContext.destroy();
+ }
+
+ if (this.recordTimeout) {
+ clearTimeout(this.recordTimeout);
+ this.recordTimeout = null;
+ }
+
+ if (this.recordTimer) {
+ clearInterval(this.recordTimer);
+ this.recordTimer = null;
+ }
+
+ // 停止波形动画
+ this.stopWaveAnimation();
+ },
+ methods: {
+ // 初始化波形数据
+ initWaveItems() {
+ const itemCount = 16; // 波形柱状图数量
+ this.waveItems = Array(itemCount).fill(10); // 初始高度10rpx
+ },
+
+ // 开始波形动画
+ startWaveAnimation() {
+ // 停止可能已存在的动画
+ this.stopWaveAnimation();
+
+ // 随机生成波形高度
+ this.waveTimer = setInterval(() => {
+ const newWaveItems = [];
+ for (let i = 0; i < this.waveItems.length; i++) {
+ // 生成10-60之间的随机高度
+ newWaveItems.push(Math.floor(Math.random() * 50) + 10);
+ }
+ this.waveItems = newWaveItems;
+ }, 100);
+ },
+
+ // 停止波形动画
+ stopWaveAnimation() {
+ if (this.waveTimer) {
+ clearInterval(this.waveTimer);
+ this.waveTimer = null;
+ }
+ this.initWaveItems(); // 重置波形
+ },
+
+ // 开始录音
+ startRecord() {
+ if (this.isRecording) return;
+
+ const options = {
+ duration: this.recordMaxDuration, // 最大录音时长
+ sampleRate: 44100, // 采样率
+ numberOfChannels: 1, // 录音通道数
+ encodeBitRate: 192000, // 编码码率
+ format: 'mp3', // 音频格式
+ frameSize: 50 // 指定帧大小
+ };
+
+ // 开始录音
+ this.recorderManager.start(options);
+
+ // 震动反馈
+ uni.vibrateShort({
+ success: function () {
+ console.log('震动成功');
+ }
+ });
+ },
+
+ // 停止录音
+ stopRecord() {
+ if (!this.isRecording) return;
+ this.recorderManager.stop();
+
+ // 震动反馈
+ uni.vibrateShort({
+ success: function () {
+ console.log('震动成功');
+ }
+ });
+ },
+
+ // 取消录音
+ cancelRecord() {
+ if (!this.isRecording) return;
+ this.recorderManager.stop();
+ this.audioPath = ''; // 清空录音路径
+
+ uni.showToast({
+ title: '录音已取消',
+ icon: 'none'
+ });
+ },
+
+ // 播放录音
+ playVoice() {
+ if (!this.audioPath) {
+ uni.showToast({
+ title: '没有可播放的录音',
+ icon: 'none'
+ });
+ return;
+ }
+
+ if (this.isPlaying) {
+ // 如果正在播放,则停止播放
+ this.innerAudioContext.stop();
+ this.isPlaying = false;
+ return;
+ }
+
+ // 设置音频源
+ this.innerAudioContext.src = this.audioPath;
+ this.innerAudioContext.play();
+ this.isPlaying = true;
+
+ // 播放完成后自动停止
+ setTimeout(() => {
+ if (this.isPlaying) {
+ this.isPlaying = false;
+ }
+ }, this.recordingTime * 1000 + 500); // 增加500ms缓冲时间
+ },
+
+ // 删除录音
+ deleteVoice() {
+ if (!this.audioPath) return;
+
+ uni.showModal({
+ title: '提示',
+ content: '确定要删除这段录音吗?',
+ success: (res) => {
+ if (res.confirm) {
+ // 如果正在播放,先停止播放
+ if (this.isPlaying) {
+ this.innerAudioContext.stop();
+ this.isPlaying = false;
+ }
+
+ this.audioPath = '';
+ this.audioName = '录音文件01.mp3';
+ this.audioSize = '0KB';
+ this.recordingTime = 0;
+ this.audioUrl = '';
+
+ uni.showToast({
+ title: '录音已删除',
+ icon: 'none'
+ });
+ }
+ }
+ });
+ },
+
+ // 提交语音订单
+ submitVoiceOrder() {
+ if (!this.audioPath) {
+ uni.showToast({
+ title: '请先录制语音',
+ icon: 'none'
+ });
+ return;
+ }
+
+ if (this.isUploading) {
+ uni.showToast({
+ title: '正在上传中,请稍候',
+ icon: 'none'
+ });
+ return;
+ }
+
+ // 显示加载提示
+ uni.showLoading({
+ title: '语音识别中...'
+ });
+
+ this.isUploading = true;
+ this.uploadProgress = 0;
+
+ // 上传音频文件
+ this.uploadAudioFile();
+ },
+
+ // 上传音频文件
+ uploadAudioFile() {
+ // 模拟上传进度
+ const simulateProgress = () => {
+ this.uploadProgress = 0;
+ const interval = setInterval(() => {
+ this.uploadProgress += 5;
+ if (this.uploadProgress >= 90) {
+ clearInterval(interval);
+ }
+ }, 100);
+ return interval;
+ };
+
+ const progressInterval = simulateProgress();
+
+ // 使用OSS上传服务上传音频文件
+ this.$Oss.ossUpload(this.audioPath).then(url => {
+ // 上传成功
+ clearInterval(progressInterval);
+ this.uploadProgress = 100;
+ this.audioUrl = url;
+ console.log('音频上传成功', url);
+
+ // 调用语音识别
+ this.recognizeVoice(url);
+ }).catch(err => {
+ // 上传失败
+ clearInterval(progressInterval);
+ console.error('音频上传失败', err);
+ this.handleUploadFailed('音频上传失败,请重试');
+ });
+ },
+
+ // 语音识别
+ recognizeVoice(fileUrl) {
+ // 使用统一的API调用方式
+ this.$api('order.recognizeVoice', {
+ fileUrl: fileUrl,
+ userId: uni.getStorageSync('userId') || ''
+ }, res => {
+ // 回调方式处理结果
+ if (res.code === 0) {
+ this.recognitionResult = res.data.result;
+ console.log('语音识别成功', this.recognitionResult);
+
+ // 处理订单
+ this.processOrder();
+ } else {
+ this.handleUploadFailed(res.msg || '语音识别失败');
+ }
+ }, err => {
+ // 错误处理
+ console.error('语音识别请求失败', err);
+ this.handleUploadFailed('网络请求失败,请检查网络连接');
+ });
+ },
+
+ // 处理订单
+ processOrder() {
+ // 使用统一的API调用方式
+ this.$api('order.createFromVoice', {
+ userId: uni.getStorageSync('userId') || '',
+ audioUrl: this.audioUrl,
+ recognitionResult: this.recognitionResult
+ }).then(res => {
+ // Promise方式处理结果
+ uni.hideLoading();
+ this.isUploading = false;
+
+ if (res.code === 0) {
+ // 下单成功
+ const orderId = res.data.orderId;
+
+ // 显示成功提示并跳转
+ uni.showToast({
+ title: '语音下单成功',
+ icon: 'success',
+ duration: 1500,
+ success: () => {
+ setTimeout(() => {
+ this.$utils.redirectTo('/pages_order/order/firmOrder?orderId=' + orderId);
+ }, 1500);
+ }
+ });
+ } else {
+ uni.showModal({
+ title: '提示',
+ content: res.msg || '创建订单失败',
+ showCancel: false
+ });
+ }
+ }).catch(err => {
+ // 错误处理
+ uni.hideLoading();
+ this.isUploading = false;
+ console.error('创建订单请求失败', err);
+ this.handleUploadFailed('网络请求失败,请检查网络连接');
+ });
+ },
+
+ // 处理上传失败情况
+ handleUploadFailed(message) {
+ uni.hideLoading();
+ this.isUploading = false;
+ this.uploadProgress = 0;
+
+ uni.showModal({
+ title: '上传失败',
+ content: message,
+ showCancel: false
+ });
+ },
+
+ // 格式化文件大小
+ formatFileSize(size) {
+ if (size < 1024) {
+ this.audioSize = size + 'B';
+ } else if (size < 1024 * 1024) {
+ this.audioSize = (size / 1024).toFixed(2) + 'KB';
+ } else {
+ this.audioSize = (size / (1024 * 1024)).toFixed(2) + 'MB';
+ }
+ },
+
+ // 格式化日期为字符串
+ formatDate(date) {
+ const pad = (n) => n < 10 ? '0' + n : n;
+ return pad(date.getMonth() + 1) + pad(date.getDate());
+ }
}
}
@@ -83,7 +540,43 @@
align-items: center;
justify-content: center;
border-radius: 100rpx;
+
+ &.recording {
+ background-color: rgba(220, 40, 40, 0.1);
+ border: 1rpx solid #DC2828;
+ }
}
+ .recording-status {
+ width: 85%;
+ margin: auto;
+ margin-top: 20rpx;
+ text-align: center;
+ color: #DC2828;
+ font-size: 28rpx;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+
+ .recording-time {
+ margin-left: 10rpx;
+ font-weight: bold;
+ }
+ }
+ .voice-wave {
+ width: 85%;
+ height: 120rpx;
+ margin: 20rpx auto;
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-end;
+
+ .wave-item {
+ width: 10rpx;
+ background-color: #DC2828;
+ border-radius: 10rpx;
+ transition: height 0.1s ease-in-out;
+ }
+ }
.recording-file{
height: 250rpx;
display: flex;
diff --git a/store/store.js b/store/store.js
index 7c3e47c..0151305 100644
--- a/store/store.js
+++ b/store/store.js
@@ -1,5 +1,6 @@
import Vue from 'vue'
import Vuex from 'vuex'
+import utils from '../utils/utils.js'
Vue.use(Vuex); //vue的插件机制
@@ -8,75 +9,184 @@ import api from '@/api/api.js'
//Vuex.Store 构造器选项
const store = new Vuex.Store({
state: {
- configList: [], //配置列表
- shop : false,
- userInfo : {}, //用户信息
- },
- getters: {
- // 角色 true为水洗店 false为酒店
- userShop(state){
- return state.shop
- }
+ configList: {}, //配置列表
+ userInfo: {}, //用户信息
+ riceInfo: {}, //用户相关信息
+ category: [], //分类信息
+ payOrderProduct: [], //支付订单中的商品
+ promotionUrl : '',//分享二维码
},
+ getters: {},
mutations: {
// 初始化配置
- initConfig(state){
- // api('getConfig', res => {
- // if(res.code == 200){
- // state.configList = res.result
- // }
- // })
-
- let config = ['getPrivacyPolicy', 'getUserAgreement']
- config.forEach(k => {
- api(k, res => {
- if (res.code == 200) {
- state.configList[k] = res.result
- }
- })
+ initConfig(state) {
+ api('getConfig', res => {
+ const configList = {
+ ...state.configList,
+ }
+ if (res.code == 200) {
+ res.result.forEach(n => {
+ configList[n.keyName] = n.keyContent;
+ configList[n.keyName + '_keyValue'] = n.keyValue;
+ });
+ }
+ state.configList = configList
+ uni.$emit('initConfig', state.configList)
})
+
+ // let config = ['getPrivacyPolicy', 'getUserAgreement']
+ // config.forEach(k => {
+ // api(k, res => {
+ // if (res.code == 200) {
+ // state.configList[k] = res.result
+ // }
+ // })
+ // })
},
- login(state){
+ login(state, config = {}) {
uni.showLoading({
title: '登录中...'
})
uni.login({
- success(res) {
- if(res.errMsg != "login:ok"){
+ success : res => {
+ if (res.errMsg != "login:ok") {
return
}
-
- api('wxLogin', {
- code : res.code
- }, res => {
-
+
+ let data = {
+ code: res.code,
+ }
+
+ if (uni.getStorageSync('shareId')) {
+ data.shareId = uni.getStorageSync('shareId')
+ }
+
+ api('wxLogin', data, res => {
+
uni.hideLoading()
-
- if(res.code != 200){
+
+ if (res.code != 200) {
return
}
-
+
state.userInfo = res.result.userInfo
uni.setStorageSync('token', res.result.token)
- if(!state.userInfo.nickName || !state.userInfo.headImage){
+ if(config.path){
+ let path = config.path
+
+ delete config.path
+ delete config.shareId
+
+ let para = utils.objectToUrlParams(config)
+ uni.reLaunch({
+ url: `${path}?${para}`,
+ })
+ return
+ }
+
+ if (!state.userInfo.nickName ||
+ !state.userInfo.headImage ||
+ !state.userInfo.phone
+ ) {
uni.navigateTo({
url: '/pages_order/auth/wxUserInfo'
})
- }else{
- uni.navigateBack(-1)
+ } else {
+ utils.navigateBack(-1)
}
})
}
})
},
- getUserInfo(state){
- api('infoGetInfo', res => {
- if(res.code == 200){
+ getUserInfo(state) {
+ api('getInfo', res => {
+ if (res.code == 200) {
state.userInfo = res.result
+
+ if (!state.userInfo.nickName ||
+ !state.userInfo.headImage ||
+ !state.userInfo.phone
+ ) {
+ uni.showModal({
+ title: '申请获取您的信息!',
+ cancelText: '稍后补全',
+ confirmText: '现在补全',
+ success(e) {
+ if (e.confirm) {
+ uni.navigateTo({
+ url: '/pages_order/auth/wxUserInfo'
+ })
+ }
+ }
+ })
+ }
+ }
+ })
+ },
+ getRiceInfo(state) {
+ api('getRiceInfo', {
+ token: uni.getStorageSync('token') || ''
+ }, res => {
+ if (res.code == 200) {
+ state.riceInfo = res.result
+ }
+ })
+ },
+ // 退出登录
+ logout(state, reLaunch = false) {
+ // uni.showModal({
+ // title: '确认退出登录吗',
+ // success(r) {
+ // if (r.confirm) {
+ // state.userInfo = {}
+ // uni.removeStorageSync('token')
+ // uni.reLaunch({
+ // url: '/pages/index/index'
+ // })
+ // }
+ // }
+ // })
+
+ state.userInfo = {}
+ uni.removeStorageSync('token')
+
+ if(reLaunch){
+ uni.reLaunch({
+ url: '/pages/index/index'
+ })
+ }
+
+ },
+ getQrCode(state) {
+ let that = this;
+ if(!uni.getStorageSync('token')){
+ return
+ }
+ uni.getImageInfo({
+ src: `${Vue.prototype.$config.baseUrl}/info_common/getInviteCode?token=${uni.getStorageSync('token')}`,
+ success : res => {
+ that.commit('setPromotionUrl', res.path)
+ },
+ fail : err => {
}
})
},
+ // 查询分类接口
+ getCategoryList(state) {
+ api('getCategoryPidList', res => {
+ if (res.code == 200) {
+ state.category = res.result
+ }
+ })
+ },
+ // 设置支付订单中的商品
+ setPayOrderProduct(state, data) {
+ state.payOrderProduct = data
+ },
+ setPromotionUrl(state, data){
+ state.promotionUrl = data
+ },
},
actions: {},
})
diff --git a/utils/oss-upload/oss/index.js b/utils/oss-upload/oss/index.js
index 024eb80..91b7093 100644
--- a/utils/oss-upload/oss/index.js
+++ b/utils/oss-upload/oss/index.js
@@ -2,10 +2,6 @@
* 阿里云OSS工具类
*/
import OSSConfig from "@/utils/oss-upload/oss/OSSConfig.js"
-//支持web端
-import {
- uploadFileToOSS
-} from '@/utils/oss-upload/oss/web.js'
import ossConfig from '@/config.js'
/**
@@ -93,9 +89,6 @@ export function ossUploadImage({
count: 1,
sizeType,
success(res) {
- // #ifdef H5
- return uploadFileToOSS(res.tempFiles[0]).then(success).catch(fail)
- // #endif
ossUpload(res.tempFilePaths[0], key, folder).then(success).catch(fail)
},
fail
@@ -123,9 +116,6 @@ export function ossUploadVideo({
maxDuration,
camera,
success(res) {
- // #ifdef H5
- return uploadFileToOSS(res.tempFile).then(success).catch(fail)
- // #endif
ossUpload(res.tempFilePath, key, folder).then(success).catch(fail)
},
fail
diff --git a/utils/oss-upload/oss/web.js b/utils/oss-upload/oss/web.js
deleted file mode 100644
index dfaab6e..0000000
--- a/utils/oss-upload/oss/web.js
+++ /dev/null
@@ -1,63 +0,0 @@
-// 此方法适用于web
-import OSS from "ali-oss"
-import config from '@/config.js'
-
-/**
- * 生成一个随机的Key
- */
-function storeKey() {
- let s = [];
- let hexDigits = "0123456789abcdef";
- for (let i = 0; i < 36; i++) {
- s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
- }
- s[14] = "4";
- s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);
- s[8] = s[13] = s[18] = s[23] = "-";
- return s.join("");
-}
-
-/**
- * 根据当天日期在OSS端生成文件夹
- */
-function storeFolder() {
- const date = new Date();
- const formatNumber = n => {
- n = n.toString()
- return n[1] ? n : '0' + n
- }
- return [date.getFullYear(), date.getMonth() + 1, date.getDate()].map(formatNumber).join('-')
-}
-
-
-export function uploadFileToOSS(file) {
-
- uni.showLoading({
- title: '上传中...'
- });
-
- return new Promise((resolve,reject) => {
- // 创建OSS实例
- const client = new OSS(config.aliOss.config);
-
- // 设置文件名和文件目录
- const suffix = '.' + file.name.split('.').pop();
- let key = storeFolder()
- if(key[key.length - 1] != '/') key += '/'
- const fileName = key + storeKey() + suffix; // 注意:文件名需要是唯一的
-
- // 使用put接口上传文件
- client.multipartUpload(fileName, file, {
- headers: {
- 'Content-Disposition': 'inline',
- 'Content-Type': file.type
- }
- }).then(res => {
- uni.hideLoading();
- resolve(config.aliOss.url + res.name);
- }).catch(err => {
- uni.hideLoading();
- reject(err)
- })
- })
-}
\ No newline at end of file
diff --git a/utils/utils.js b/utils/utils.js
index dabe1e0..b418508 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,6 +154,21 @@ export function copyText(content) {
})
}
+/**
+ * 将字符串中的文本格式化为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
@@ -160,19 +185,70 @@ 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){
+ if(getCurrentPages().length == 1){
+ uni.reLaunch({
+ url: '/pages/index/index'
+ })
+ return
+ }
uni.navigateBack(num)
}
+/**
+ * 重定向到指定页面
+ * @param {...any} args - 导航参数
+ */
export function redirectTo(...args){
uni.redirectTo(params(...args))
}
+/**
+ * 登录跳转函数,防止短时间内多次调用
+ * @returns {Function} 节流处理后的登录跳转函数
+ */
+export const toLogin = function(){
+ let time = 0
+ return () => {
+ if(new Date().getTime() - time < 1000){
+ return
+ }
+ time = new Date().getTime()
+ uni.navigateTo({
+ url: '/pages_order/auth/wxLogin'
+ })
+ }
+}()
+
+// 将对象转换为URL参数
+function objectToUrlParams(obj) {
+ if (!obj || typeof obj !== 'object') {
+ return '';
+ }
+
+ return Object.keys(obj)
+ .filter(key => obj[key] !== undefined && obj[key] !== null)
+ .map(key => {
+ const value = typeof obj[key] === 'object'
+ ? JSON.stringify(obj[key])
+ : obj[key];
+ return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
+ })
+ .join('&');
+}
+
export default {
toArray,
generateUUID,
@@ -185,5 +261,8 @@ export default {
navigateTo,
navigateBack,
redirectTo,
- copyText
+ copyText,
+ stringFormatHtml,
+ toLogin,
+ objectToUrlParams,
}
\ No newline at end of file