Browse Source

上传

master
前端-胡立永 6 days ago
parent
commit
1409de47cc
15 changed files with 904 additions and 207 deletions
  1. +60
    -55
      README.md
  2. +12
    -8
      api/api.js
  3. +17
    -75
      api/http.js
  4. +1
    -1
      manifest.json
  5. +27
    -19
      mixins/list.js
  6. +6
    -0
      pages.json
  7. +4
    -0
      pages/index/center.vue
  8. +2
    -0
      pages/index/order.vue
  9. +207
    -0
      pages_order/order/giftList.vue
  10. +238
    -0
      pages_order/order/instantGift.vue
  11. +232
    -0
      pages_order/order/receiveGift.vue
  12. +8
    -1
      pages_order/product/productDetail.vue
  13. +0
    -1
      store/store.js
  14. +20
    -12
      utils/pay.js
  15. +70
    -35
      utils/utils.js

+ 60
- 55
README.md View File

@ -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


+ 12
- 8
api/api.js View File

@ -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

+ 17
- 75
api/http.js View File

@ -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
}

+ 1
- 1
manifest.json View File

@ -1,6 +1,6 @@
{
"name" : "unapp模板",
"appid" : "__UNI__FB049D5",
"appid" : "__UNI__197A38F",
"description" : "",
"versionName" : "1.0.0",
"versionCode" : "100",


+ 27
- 19
mixins/list.js View File

@ -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()


+ 6
- 0
pages.json View File

@ -134,6 +134,12 @@
},
{
"path": "mine/updateUser"
},
{
"path": "order/instantGift"
},
{
"path": "order/giftList"
}
]
}],


+ 4
- 0
pages/index/center.vue View File

@ -137,6 +137,10 @@
<image src="@/static/image/center/5.png" mode="widthFix" />
<view class="title">我的地址</view>
</view>
<view class="boxs" @click="$utils.navigateTo('/pages_order/order/giftList')">
<image src="@/static/image/center/6.png" mode="widthFix" />
<view class="title">礼包列表</view>
</view>
<view class="boxs" @click="$store.commit('logout')">
<image src="@/static/image/center/6.png" mode="widthFix" />
<view class="title">退出登录</view>


+ 2
- 0
pages/index/order.vue View File

@ -182,6 +182,8 @@
.status {
font-weight: 600;
color: #FFAC2F;
flex-shrink: 0;
margin-left: 20rpx;
}
}


+ 207
- 0
pages_order/order/giftList.vue View File

@ -0,0 +1,207 @@
<template>
<view class="page">
<!-- 导航栏 -->
<navbar title="礼包列表" leftClick @leftClick="$utils.navigateBack" bgColor="#E3441A" color="#fff" />
<!-- 订单筛选 -->
<view class="tabs">
<uv-tabs :list="tabs"
:activeStyle="{color : '#fff', fontWeight : 600}"
lineColor="#fff"
:inactiveStyle="{color: 'rgba(255,255,255,.8)'}"
lineHeight="8rpx"
lineWidth="50rpx"
:current="current"
@click="clickTabs"></uv-tabs>
</view>
<view class="list">
<view class="item" v-for="(item, index) in list" @click="toGiftDetail(item.id)" :key="index">
<view class="content">
<view class="top">
<view class="service">
{{ item.title }}
</view>
<view class="status">
<text>{{ item.state === 0 ? '待领取' : '已领取' }}</text>
</view>
</view>
<view class="main">
<view class="left">
<image mode="aspectFill" :src="item.image && item.image.split(',')[0]"></image>
</view>
<view class="right">
<view class="text-hidden-1">
赠送人{{item.senderName}}
</view>
<view class="text-hidden-1">
赠送时间{{item.createTime}}
</view>
<view class="text-hidden-2">
祝福语{{item.message}}
</view>
</view>
</view>
</view>
<view class="bottom">
<view class="price">
<text class="total-title">礼包价值</text>
<text class="unit"></text>
<text class="num">{{item.price}}</text>
<text class="c-unit"></text>
</view>
<view @click.stop="receiveGift(item)" class="btn" v-if="item.state === 0">
立即领取
</view>
</view>
</view>
<view style="margin-top: 20rpx; min-width: 700rpx;">
<uv-empty mode="list" v-if="list.length === 0"></uv-empty>
</view>
</view>
</view>
</template>
<script>
import mixinsList from '@/mixins/list.js'
export default {
mixins: [mixinsList],
data() {
return {
mixinsListApi: 'getGiftList',
tabs: [
{
name: '我赠送的'
},
{
name: '我收到的'
}
],
current : 0,
}
},
methods: {
clickTabs({index}){
},
//
toGiftDetail(id) {
uni.navigateTo({
url: '/pages_order/order/giftDetail?id=' + id
})
},
//
receiveGift(item) {
uni.showModal({
title: '提示',
content: '确认领取该礼包吗?',
success: async (res) => {
if (res.confirm) {
await this.$http.post('/gift/receive', {
id: item.id
})
uni.showToast({
title: '领取成功',
icon: 'success'
})
this.getData()
}
}
})
}
}
}
</script>
<style scoped lang="scss">
.list {
.item {
width: calc(100% - 40rpx);
background-color: #fff;
margin: 20rpx;
box-sizing: border-box;
border-radius: 16rpx;
padding: 30rpx;
.content {
.top {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 34rpx;
.status {
font-weight: 600;
color: #FFAC2F;
flex-shrink: 0;
margin-left: 20rpx;
}
}
.main {
display: flex;
margin: 20rpx 0rpx;
.left {
display: flex;
align-items: center;
justify-content: center;
width: 180rpx;
height: 180rpx;
image {
width: 95%;
height: 95%;
border-radius: 10rpx;
}
}
.right {
display: flex;
flex-direction: column;
justify-content: space-between;
width: calc(100% - 200rpx);
color: #777;
font-size: 26rpx;
padding: 30rpx 20rpx;
box-sizing: border-box;
margin-left: 20rpx;
border-radius: 10rpx;
background-color: #F8F8F8;
}
}
}
.bottom {
display: flex;
justify-content: space-between;
font-size: 25rpx;
.price {
.num {
font-size: 36rpx;
}
.num,
.unit,
.c-unit {
color: $uni-color;
}
}
.btn {
border: 1px solid #C7C7C7;
padding: 10rpx 20rpx;
border-radius: 40rpx;
color: #575757;
}
}
}
}
</style>

+ 238
- 0
pages_order/order/instantGift.vue View File

@ -0,0 +1,238 @@
<template>
<view class="instant-gift">
<navbar title="立即送礼" leftClick @leftClick="$utils.navigateBack" />
<!-- 主图片展示区 -->
<view class="main-image">
<image :src="selectedGift.image" mode="aspectFill"></image>
</view>
<!-- 礼品选择列表 -->
<scroll-view scroll-x class="gift-list">
<view
class="gift-item"
v-for="(item, index) in giftList"
:key="index"
:class="{ active: selectedIndex === index }"
@click="selectGift(index)"
>
<image :src="item.image" mode="aspectFill"></image>
<text class="gift-name">{{item.name}}</text>
</view>
</scroll-view>
<!-- 祝福语区域 -->
<view class="blessing-area">
<view class="blessing-title">送祝福给TA</view>
<view class="blessing-input">
<input type="text" v-model="blessing" placeholder="蛇年到,福气绕。钱包满满当当!" />
</view>
</view>
<!-- 底部区域 -->
<view class="bottom-area">
<view class="countdown">距离礼包失效{{countdownText}}</view>
<button class="send-btn" @click="sendGift">立即送礼</button>
</view>
</view>
</template>
<script>
export default {
data() {
return {
selectedIndex: 0,
blessing: '',
countdown: 24 * 60 * 60, //
countdownTimer: null,
giftList: [
{
name: '蛇行大运',
image: 'https://picb3.photophoto.cn/38/090/38090873_1.jpg'
},
{
name: '蛇来运转',
image: 'https://picb3.photophoto.cn/38/090/38090873_1.jpg'
},
{
name: '2025',
image: 'https://picb3.photophoto.cn/38/090/38090873_1.jpg'
},
{
name: '身体健康',
image: 'https://picb3.photophoto.cn/38/090/38090873_1.jpg'
}
]
}
},
computed: {
selectedGift() {
return this.giftList[this.selectedIndex]
},
countdownText() {
const hours = Math.floor(this.countdown / 3600)
const minutes = Math.floor((this.countdown % 3600) / 60)
const seconds = this.countdown % 60
return `${hours}${minutes}${seconds}`
}
},
methods: {
navigateBack() {
uni.navigateBack()
},
selectGift(index) {
this.selectedIndex = index
},
sendGift() {
//
uni.showToast({
title: '送礼成功',
icon: 'success'
})
},
startCountdown() {
this.countdownTimer = setInterval(() => {
if (this.countdown > 0) {
this.countdown--
} else {
clearInterval(this.countdownTimer)
}
}, 1000)
}
},
mounted() {
this.startCountdown()
},
beforeDestroy() {
if (this.countdownTimer) {
clearInterval(this.countdownTimer)
}
}
}
</script>
<style lang="scss" scoped>
.instant-gift {
min-height: 100vh;
background: #fff;
.nav-header {
display: flex;
align-items: center;
height: 88rpx;
padding: 0 30rpx;
.back-icon, .more-icon {
width: 60rpx;
text-align: center;
}
.title {
flex: 1;
text-align: center;
font-size: 32rpx;
font-weight: 500;
}
}
.main-image {
width: 100%;
height: 400rpx;
image {
width: 100%;
height: 100%;
}
}
.gift-list {
padding: 30rpx;
white-space: nowrap;
.gift-item {
display: inline-block;
width: 200rpx;
margin-right: 20rpx;
text-align: center;
position: relative;
z-index: 1;
border: 4rpx solid transparent;
padding: 0 10rpx 10rpx 10rpx;
&.active {
position: relative;
z-index: 2;
border: 4rpx solid $uni-color;
border-radius: 12rpx;
}
image {
width: 100%;
height: 200rpx;
border-radius: 12rpx;
}
.gift-name {
display: block;
font-size: 28rpx;
margin-top: 10rpx;
}
}
}
.blessing-area {
padding: 30rpx;
.blessing-title {
font-size: 30rpx;
color: #666;
margin-bottom: 20rpx;
}
.blessing-input {
background: #FFF5F5;
padding: 20rpx;
border-radius: 12rpx;
border: 2rpx solid #FFE0E0;
input {
width: 100%;
height: 60rpx;
color: #333;
font-size: 28rpx;
}
}
}
.bottom-area {
position: fixed;
left: 0;
bottom: 0;
width: 100%;
box-sizing: border-box;
padding: 20rpx 30rpx calc(30rpx + env(safe-area-inset-bottom)) 30rpx;
background: #fff;
box-shadow: 0 -2rpx 10rpx rgba(0,0,0,0.05);
z-index: 10;
.countdown {
text-align: center;
font-size: 28rpx;
color: #666;
margin-bottom: 20rpx;
font-weight: 500;
}
.send-btn {
width: calc(100%);
height: 88rpx;
line-height: 88rpx;
background: linear-gradient(to right, #FF4B4B, $uni-color);
color: #fff;
border-radius: 44rpx;
font-size: 32rpx;
font-weight: 500;
}
}
}
</style>

+ 232
- 0
pages_order/order/receiveGift.vue View File

@ -0,0 +1,232 @@
<template>
<view class="receive-gift">
<navbar title="礼品领取" leftClick @leftClick="$utils.navigateBack" />
<!-- 主图片展示区 -->
<view class="main-image">
<image :src="giftInfo.image" mode="aspectFill"></image>
</view>
<!-- 礼品信息区域 -->
<view class="gift-info">
<view class="gift-name">{{giftInfo.name}}</view>
<view class="gift-value">
<text>礼品价值</text>
<text class="price">{{giftInfo.price}}</text>
</view>
</view>
<!-- 祝福语区域 -->
<view class="blessing-area">
<view class="sender-info">
<text class="label">来自</text>
<text class="sender">{{giftInfo.senderName}}</text>
<text class="label">的祝福</text>
</view>
<view class="blessing-content">
{{giftInfo.message}}
</view>
</view>
<!-- 底部区域 -->
<view class="bottom-area">
<view class="countdown">距离礼包失效{{countdownText}}</view>
<button class="receive-btn" @click="receiveGift" :disabled="giftInfo.state !== 0">{{giftInfo.state === 0 ? '立即领取' : '已领取'}}</button>
</view>
</view>
</template>
<script>
export default {
data() {
return {
giftId: '',
giftInfo: {
name: '',
image: '',
price: 0,
senderName: '',
message: '',
state: 0, // 0- 1-
expireTime: ''
},
countdown: 24 * 60 * 60, //
countdownTimer: null
}
},
computed: {
countdownText() {
const hours = Math.floor(this.countdown / 3600)
const minutes = Math.floor((this.countdown % 3600) / 60)
const seconds = this.countdown % 60
return `${hours}${minutes}${seconds}`
}
},
methods: {
//
async getGiftInfo() {
try {
const res = await this.$http.get(`/gift/detail/${this.giftId}`)
this.giftInfo = res.data
//
if (this.giftInfo.expireTime) {
const expireTime = new Date(this.giftInfo.expireTime).getTime()
const now = new Date().getTime()
this.countdown = Math.max(0, Math.floor((expireTime - now) / 1000))
this.startCountdown()
}
} catch (e) {
uni.showToast({
title: '获取礼品信息失败',
icon: 'none'
})
}
},
//
async receiveGift() {
try {
await this.$http.post('/gift/receive', {
id: this.giftId
})
uni.showToast({
title: '领取成功',
icon: 'success'
})
this.giftInfo.state = 1
} catch (e) {
uni.showToast({
title: '领取失败',
icon: 'none'
})
}
},
//
startCountdown() {
this.countdownTimer = setInterval(() => {
if (this.countdown > 0) {
this.countdown--
} else {
clearInterval(this.countdownTimer)
}
}, 1000)
}
},
onLoad(options) {
if (options.id) {
this.giftId = options.id
this.getGiftInfo()
}
},
beforeDestroy() {
if (this.countdownTimer) {
clearInterval(this.countdownTimer)
}
}
}
</script>
<style lang="scss" scoped>
.receive-gift {
min-height: 100vh;
background: #fff;
.main-image {
width: 100%;
height: 400rpx;
image {
width: 100%;
height: 100%;
}
}
.gift-info {
padding: 30rpx;
.gift-name {
font-size: 36rpx;
font-weight: 600;
color: #333;
margin-bottom: 20rpx;
}
.gift-value {
font-size: 28rpx;
color: #666;
.price {
color: #E3441A;
font-weight: 600;
margin-left: 10rpx;
}
}
}
.blessing-area {
padding: 30rpx;
background: #FFF5F5;
margin: 30rpx;
border-radius: 12rpx;
.sender-info {
text-align: center;
margin-bottom: 20rpx;
.label {
color: #666;
font-size: 28rpx;
}
.sender {
color: #333;
font-size: 32rpx;
font-weight: 600;
margin: 0 10rpx;
}
}
.blessing-content {
color: #333;
font-size: 30rpx;
line-height: 1.6;
text-align: center;
}
}
.bottom-area {
position: fixed;
left: 0;
bottom: 0;
width: 100%;
box-sizing: border-box;
padding: 20rpx 30rpx calc(30rpx + env(safe-area-inset-bottom)) 30rpx;
background: #fff;
box-shadow: 0 -2rpx 10rpx rgba(0,0,0,0.05);
z-index: 10;
.countdown {
text-align: center;
font-size: 28rpx;
color: #666;
margin-bottom: 20rpx;
font-weight: 500;
}
.receive-btn {
width: 100%;
height: 88rpx;
line-height: 88rpx;
background: linear-gradient(to right, #FF4B4B, #E3441A);
color: #fff;
border-radius: 44rpx;
font-size: 32rpx;
font-weight: 500;
&[disabled] {
background: #ccc;
opacity: 0.8;
}
}
}
}
</style>

+ 8
- 1
pages_order/product/productDetail.vue View File

@ -96,7 +96,11 @@
<uv-parse :content="productDetail.details"></uv-parse>
<!-- 分享和购买按钮 -->
<submit @service="service" @submit="submit" :detail="productDetail" />
<submit
@service="service"
@toSend="toSend"
@submit="submit"
:detail="productDetail" />
<!-- 联系客服 -->
<customerServicePopup ref="customerServicePopup" />
@ -142,6 +146,9 @@
this.$utils.navigateTo('/pages_order/order/createOrder')
},
toSend(){
this.$utils.navigateTo('/pages_order/order/instantGift')
},
//
getRiceProductDetail() {
this.$api('getRiceProductDetail', {


+ 0
- 1
store/store.js View File

@ -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'


+ 20
- 12
utils/pay.js View File

@ -3,26 +3,35 @@
import jWeixin from './lib/jweixin-module.js'
// #endif
/**
* 调用微信支付
* @param {Object} res - 支付参数对象包含appIdtimeStampnonceStr等必要信息
* @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()
});
}
}

+ 70
- 35
utils/utils.js View File

@ -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, '<br>')
.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 () => {


Loading…
Cancel
Save