Browse Source

上传

master
前端-胡立永 4 days ago
parent
commit
2d6c7e5543
20 changed files with 837 additions and 1245 deletions
  1. +88
    -99
      README.md
  2. +7
    -0
      api/model/index.js
  3. +1
    -1
      components/base/navbar.vue
  4. +0
    -251
      components/sn-swiper/esc-swiper-item/index.vue
  5. +0
    -21
      components/sn-swiper/esc-swiper/helper.js
  6. +0
    -198
      components/sn-swiper/esc-swiper/index.vue
  7. +0
    -303
      components/sn-swiper/esc-swiper/mixins/base.mixin.js
  8. +0
    -91
      components/sn-swiper/esc-swiper/mixins/bindingx.js
  9. +0
    -143
      components/sn-swiper/esc-swiper/mixins/index.wxs
  10. +0
    -62
      components/sn-swiper/esc-swiper/mixins/mpother.js
  11. +0
    -7
      components/sn-swiper/esc-swiper/mixins/mpwxs.js
  12. +0
    -15
      components/utils/style.js
  13. +0
    -3
      pages.json
  14. +2
    -0
      pages_order/components/address/addressList.vue
  15. +227
    -0
      pages_order/components/address/redactAddressForm.vue
  16. +0
    -3
      pages_order/mine/memberCenter.vue
  17. +4
    -3
      pages_order/order/giftList.vue
  18. +2
    -2
      pages_order/order/instantGift.vue
  19. +505
    -41
      pages_order/order/receiveGift.vue
  20. +1
    -2
      pages_order/product/productDetail.vue

+ 88
- 99
README.md View File

@ -63,6 +63,55 @@
- 模块化组织:按功能模块划分目录,便于维护和管理
- 组件复用:分包内的通用组件集中管理,提高代码复用性
## 配置文件说明
### 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 混入
@ -187,25 +236,49 @@ this.$api('product.create', {
- position.js: 定位与位置计算
- oss-upload: 阿里云OSS上传模块
#### 3.2 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
<template>
<view>
<navbar title="商品列表" />
<product-item
v-for="item in list"
:key="item.id"
:product="item"
/>
</view>
</template>
```
#### 3.3 OSS上传模块
**配置说明:**
项目使用阿里云OSS进行文件存储,相关配置位于config.js中:
```javascript
const defaultConfig = {
aliOss: {
url: 'https://image.hhlm1688.com/',
config: {
region: 'oss-cn-guangzhou',
accessKeyId: '***',
accessKeySecret: '***',
bucket: 'hanhaiimage',
endpoint: 'oss-cn-shenzhen.aliyuncs.com',
}
}
}
```
**使用示例:**
@ -309,42 +382,6 @@ export default {
3. 支持的文件类型:图片、视频、文档等
4. 上传失败时会抛出异常,请做好错误处理
**使用示例:**
```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
<template>
<view>
<navbar title="商品列表" />
<product-item
v-for="item in list"
:key="item.id"
:product="item"
/>
</view>
</template>
```
## 最佳实践
### 1. 列表页面开发
@ -412,51 +449,3 @@ export default {
- 确认网络请求是否正常
- 查看请求参数格式是否正确
## 配置文件说明
### 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' // 设置默认单位
},
})
```

+ 7
- 0
api/model/index.js View File

@ -190,6 +190,13 @@ const api = {
auth : true,
limit : 1000,
},
// 2.点击抽奖 =》抽奖
getGiveShopLottery: {
url: '/index_common/getGiveShopLottery',
method: 'GET',
auth : true,
limit : 1000,
},
}
export default api

+ 1
- 1
components/base/navbar.vue View File

@ -116,7 +116,7 @@
justify-content: center;
font-size: 32rpx;
align-items: center;
z-index: 99999;
z-index: 999;
.left{
position: absolute;
left: 40rpx;


+ 0
- 251
components/sn-swiper/esc-swiper-item/index.vue View File

@ -1,251 +0,0 @@
<template>
<view class="swiper-item" ref="swiper_item" :style="itemStyle">
<view class="item-cntent" bubble="true" @click.stop="onClick"><slot></slot></view>
</view>
</template>
<script>
import { getStyleStr } from '../../utils/style.js';
// #ifdef APP-NVUE
const animation = uni.requireNativePlugin('animation');
// #endif
/**
* esc-swiper-item
* @description esc-swiper-item (不支持使用class)
* @property {Number} index 索引(必填)
* @property {Boolean} clickAny 可以点击任意项
* @event {Function} click 点击事件
*/
export default {
name: 'esc-swiper-item',
inject: ['config'],
props: {
index: {
type: Number,
default: 0
},
clickAny: {
type: Boolean,
default: false
}
},
data() {
return {
isAnimated: false,
timingFunction: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
duration: 0,
current: 0,
position: 0,
mScale: 0,
canClick: true
};
},
created() {},
computed: {
size() {
return this.config.size;
},
width() {
return this.config.width;
},
height() {
return this.config.height;
},
itemWidth() {
return this.config.itemWidth;
},
itemHeight() {
return this.config.itemHeight;
},
space() {
return this.config.space;
},
itemStyle() {
if (this.index == this.size - 1) {
return this.rightSpaceStyle();
} else if (this.index == this.size - 2) {
return this.centerSpaceStyle();
} else {
return this.leftSpaceStyle();
}
},
scale() {
if (!this.config.is3D) {
return 1;
}
if (this.myCurrent == this.current) {
return this.mScale || this.config.scale;
} else {
return this.mScale || 1;
}
},
// swiper-itemcurrent
myCurrent() {
if (!this.config.isCircular) {
return this.index;
}
const p = this.index;
const plus = this.config.plus;
const actSize = this.size - plus * 2;
let current = 0;
if (p < plus) {
current = p + (actSize - plus);
} else if (p >= this.size - plus) {
current = p - (actSize + plus);
} else {
current = p - plus;
}
return current;
}
},
methods: {
itemSize() {
let style = {
width: this.itemWidth + 'rpx',
height: (this.itemHeight || this.height) + 'rpx'
};
if (this.config.is3D) {
// #ifndef APP-NVUE
if (this.isAnimated) {
style.transition = 'transform ' + this.duration + 'ms ' + this.timingFunction;
} else {
style.transition = '';
}
style.transform = 'scale(' + this.scale + ')';
// #endif
// #ifdef APP-NVUE
const isIOS = uni.getSystemInfoSync().platform == 'ios';
if (isIOS) {
style.transform = 'scale(' + this.scale + ')';
} else {
if (!this.isAnimated) style.transform = 'scale(' + this.scale + ')';
}
// #endif
}
return style;
},
leftSpaceStyle() {
return getStyleStr({
...this.itemSize(),
marginLeft: this.space + 'rpx'
});
},
centerSpaceStyle() {
return getStyleStr({
...this.itemSize(),
marginLeft: this.space + 'rpx',
marginRight: this.space + 'rpx'
});
},
rightSpaceStyle() {
return getStyleStr({
...this.itemSize(),
marginRight: this.space + 'rpx'
});
},
onClick(e) {
if (!this.canClick) {
return;
}
// #ifdef APP-NVUE
e.stopPropagation();
// #endif
//
if (this.clickAny) {
this.$emit('click', e);
return;
}
//
if (this.myCurrent == this.current) {
this.$emit('click', e);
}
},
restoreScale(duration) {
if (!this.config.is3D) {
return;
}
// #ifndef APP-NVUE
this.duration = duration;
this.isAnimated = true;
this.mScale = 0;
setTimeout(() => {
this.duration = 0;
this.isAnimated = false;
}, duration);
// #endif
// #ifdef APP-NVUE
this.isAnimated = true;
this.mScale = 0;
animation.transition(
this.$refs['swiper_item'].ref,
{
styles: {
transform: 'scale(' + this.scale + ')'
},
duration, //ms
timingFunction: this.timingFunction,
needLayout: false,
delay: 0 //ms
},
() => {
this.isAnimated = false;
}
);
// #endif
},
restoreToScale(scale, duration) {
if (!this.config.is3D) {
return;
}
// #ifndef APP-NVUE
this.duration = duration;
this.isAnimated = true;
this.mScale = scale;
setTimeout(() => {
this.duration = 0;
this.isAnimated = false;
this.mScale = 0;
}, duration);
// #endif
// #ifdef APP-NVUE
this.isAnimated = true;
animation.transition(
this.$refs['swiper_item'].ref,
{
styles: {
transform: 'scale(' + scale + ')'
},
duration, //ms
timingFunction: this.timingFunction,
needLayout: false,
delay: 0 //ms
},
() => {
this.isAnimated = false;
this.mScale = 0;
}
);
// #endif
}
}
};
</script>
<style scoped>
.swiper-item {
position: relative;
}
.item-cntent {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
}
</style>

+ 0
- 21
components/sn-swiper/esc-swiper/helper.js View File

@ -1,21 +0,0 @@
/**
* getSwiperList
* @description 获取Swiper数据
* @param {Array} list 原数据
* @param {Object} options 配置
* @param {Boolean} options.circular 是否循环
* @param {Number} options.plus 左右追加个数(开启循环必填至少为2)
* @return {Array}
*/
export function getSwiperList(list, options = {
circular: true,
plus: 2
}) {
if (!options.circular) {
return list
}
const plus = options.plus || 2
const leftPlusList = [...list].reverse().slice(0, plus).reverse();
const rightPlusList = [...list].slice(0, plus);
return [].concat(leftPlusList).concat(list).concat(rightPlusList);
}

+ 0
- 198
components/sn-swiper/esc-swiper/index.vue View File

@ -1,198 +0,0 @@
<template>
<!-- #ifdef APP-VUE || H5 -->
<view class="box" :style="boxStyle">
<view
ref="container"
class="container"
:change:prop="swipe.changeData"
:prop="wxsData"
@touchstart.stop="swipe.touchstart"
@touchmove.stop="swipe.touchmove"
@touchend.stop="swipe.touchend"
:style="containerStyle"
>
<slot></slot>
</view>
</view>
<!-- #endif -->
<!-- #ifdef MP-WEIXIN -->
<view class="box" :style="boxStyle">
<view
ref="container"
class="container"
:change:prop="swipe.changeData"
:prop="wxsData"
@touchstart="swipe.touchstart"
@touchmove="swipe.touchmove"
@touchend="swipe.touchend"
:style="containerStyle"
>
<slot></slot>
</view>
</view>
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
<view class="box" :style="boxStyle">
<view ref="container" class="container" @horizontalpan="touchstart" :style="containerStyle"><slot></slot></view>
</view>
<!-- #endif -->
<!-- 其他平台使用 js 长列表性能可能会有影响-->
<!-- #ifndef APP-PLUS || MP-WEIXIN || H5 -->
<view class="box" @touchstart="touchstart" @touchmove="touchmove" @touchend="touchend" :style="boxStyle">
<view ref="container" class="container" :style="containerStyle"><slot></slot></view>
</view>
<!-- #endif -->
</template>
<script src="./mixins/index.wxs" module="swipe" lang="wxs"></script>
<script>
import SwiperMixin from './mixins/base.mixin.js';
// #ifdef APP-VUE || MP-WEIXIN || H5
import MpMixin from './mixins/mpwxs.js';
// #endif
// #ifdef APP-NVUE
import BindingxMixin from './mixins/bindingx.js';
// #endif
// #ifndef APP-PLUS || MP-WEIXIN || H5
import OtherMixin from './mixins/mpother.js';
// #endif
const mixins = [
// #ifdef APP-VUE || MP-WEIXIN || H5
MpMixin,
// #endif
// #ifdef APP-NVUE
BindingxMixin,
// #endif
// #ifndef APP-PLUS || MP-WEIXIN || H5
OtherMixin,
// #endif
SwiperMixin
];
/**
* esc-swiper
* @description 自定义swiper (不支持使用class)
* @property {String} mode = [normal|3d] 模式
* @property {Number} scale 3D模式选中项的scale
* @property {Number} width
* @property {Number} height
* @property {Number} itemWidth 项宽
* @property {Number} itemHeight 项高
* @property {Number} space 间距
* @property {Number} plus 左右追加个数(开启循环必填至少为2)
* @property {Number} current 选中项索引
* @property {Boolean} autoplay 自动轮播
* @property {Boolean} circular 是否循环,如果开启至少需要3项
* @property {Boolean} bounce 阻尼效果
* @event {Function} change 索引变化
*/
export default {
name: 'esc-swiper',
mixins,
props: {
mode: {
type: String,
default: 'normal'
},
scale: Number,
width: Number,
height: Number,
itemWidth: {
type: Number,
default: 0
},
itemHeight: {
type: Number,
default: 0
},
space: {
type: Number,
default: 0
},
plus: {
type: Number,
default: 0
},
autoplay: {
type: Boolean,
default: false
},
current: {
type: Number,
default: 0
},
interval: {
type: Number,
default: 5000
},
duration: {
type: Number,
default: 500
},
circular: {
type: Boolean,
default: false
},
bounce: {
type: Boolean,
default: true
},
size: {
type: Number,
default: 0
}
},
provide() {
return {
config: {
is3D: this.is3D,
isCircular: this.circular,
scale: this.scale,
size: this._size,
width: this.width,
height: this.height,
itemWidth: this.itemWidth,
itemHeight: this.itemHeight,
space: this.space,
plus: this.plus
}
};
},
mounted() {
if (this.autoplay) {
this.autoplayInterval = setInterval(() => {
this.next();
}, this.interval);
}
},
watch: {
autoplay(newV) {
if (!newV) {
clearInterval(this.autoplayInterval);
} else {
this.autoplayInterval = setInterval(() => {
this.next();
}, this.interval);
}
}
},
methods: {}
};
</script>
<style scoped>
.box {
position: relative;
overflow: hidden;
}
.container {
position: absolute;
top: 0;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
}
</style>

+ 0
- 303
components/sn-swiper/esc-swiper/mixins/base.mixin.js View File

@ -1,303 +0,0 @@
import {
getStyleStr
} from '../../../utils/style.js';
// #ifdef APP-NVUE
const animation = uni.requireNativePlugin('animation')
// #endif
export default {
data() {
return {
timingFunction: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
isAnimated: false,
isScrolling: false,
customDuration: 0,
left: 0,
mCurrent: this.current
};
},
created() {},
mounted() {
// #ifdef MP
this.swiperViews = this.$children
// #endif
// #ifdef APP-PLUS || H5
this.swiperViews = this.$slots.default.map(it => it.child)
// #endif
this._setLeft();
this.mCurrent = this.current
this._notifyCurrentForItems(this.current, this.position)
},
watch: {
mCurrent() {
let current = this.mCurrent
if (this.circular) {
if (this.position == 1) {
current = this.actualSize - (this.plus - 1)
// console.log('最前了', current)
} else if (this.position == this._size - 2) {
current = this.plus - 2;
// console.log('最后了', current)
}
if (current < 0) {
current = this.position + 1
}
current %= this.actualSize
}
// console.log('position', this.position, current)
this.$emit('update:current', current)
this.$emit('change', current)
this._notifyCurrentForItems(current, this.position)
}
},
computed: {
is3D() {
return this.mode == '3d'
},
position() {
return this.circular ? (this.mCurrent + this.plus) : this.mCurrent
},
manualDuration() {
if (this.customDuration > 0)
return this.customDuration
return this.isAnimated ? this.duration : 0
},
boxStyle() {
return getStyleStr({
width: this.width + 'rpx',
height: this.height + 'rpx'
});
},
containerStyle() {
const style = {
height: this.height + 'rpx'
};
// #ifdef APP-NVUE
// FIXME: 理论isAnimated=false应该不设置transform,但是ios有个奇怪的问题,top不为0导致布局错位
const isIOS = uni.getSystemInfoSync().platform == 'ios'
if (isIOS) {
style.transform = 'translate(' + uni.upx2px(this.left) + 'px' + ',0px)'
} else {
if (this.isAnimated == false) {
style.transform = 'translate(' + uni.upx2px(this.left) + 'px' + ',0px)'
}
}
// #endif
// #ifndef APP-NVUE
style.left = this.left + 'rpx'
style.transition = 'left ' + this.manualDuration + 'ms ' + this.timingFunction
// #endif
return getStyleStr(style);
},
_size() {
return (this.$slots && this.$slots.default && this.$slots.default.length) || this.size;
},
// plus * 2
plusSize() {
return this.circular ? this.plus * 2 : 0;
},
// 真实长度
actualSize() {
return this._size - this.plusSize;
}
},
methods: {
prev() {
if (this.isAnimated) return;
if (this.isScrolling) return;
if (this.mCurrent == 0 && this.circular == false) return
this.mCurrent--;
this._run()
},
next() {
if (this.isAnimated) return;
if (this.isScrolling) return;
if (this.circular == true) {
this.mCurrent++;
if (this.mCurrent == this._size) {
this.mCurrent = 0;
}
} else {
if (this.mCurrent == this._size - 1) return
this.mCurrent++;
}
this._run()
},
moveTo(e) {
if (this.isAnimated) return
const {
deltaX,
left
} = e
this.isScrolling = true
if (!this.circular) {
if (
// 第一项,不能向右滑(上一项)
(deltaX > 0 && this.mCurrent == 0) ||
// 最后一项,不能向左滑(下一项)
(deltaX < 0 && this.mCurrent == this._size - 1)
) {
if (!this.bounce) return
// 添加阻尼滑动
const _left = this._left || this.wxsData.left
this.left = _left + (deltaX * (1 - Math.abs(deltaX) * 3 / (this.width * 5)))
this._set3DScale(deltaX)
return
}
}
this.left = left
this._set3DScale(deltaX)
},
moveEnd(e) {
const {
velocity,
deltaX,
deltaY
} = e
this.isScrolling = false
if (!this.circular) {
// 第一项,不能向右滑(上一项)
if (deltaX > 0 && this.mCurrent == 0) {
this._restoreStartTouch()
return
}
// 最后一项,不能向左滑(下一项)
if (deltaX < 0 && this.mCurrent == this._size - 1) {
this._restoreStartTouch()
return
}
}
const isTurnPage = Math.abs(deltaX) > this.itemWidth / 2
if (isTurnPage || velocity > 0.2) {
if (deltaX < 0) {
this.customDuration = 350
this.next()
} else if (deltaX > 0) {
this.customDuration = 350
this.prev()
}
} else {
this._restoreStartTouch()
}
},
_set3DScale(deltaX) {
if (this.is3D) {
const min = Math.min
const maxScale = Math.abs(this.scale - 1)
const mScale = deltaX * maxScale / this.width
const mRealScale = min(this.scale, this.scale - Math.abs(mScale))
this.swiperViews[this.position].mScale = mRealScale < 1 ? 1 : mRealScale
if (this.position - 1 > -1) {
this.swiperViews[this.position - 1].mScale = mScale > 0 ? min(this.scale, 1 + mScale) : min(1, 1 + mScale)
}
if (this.position + 1 < this._size) {
this.swiperViews[this.position + 1].mScale = mScale > 0 ? min(1, 1 - mScale) : min(this.scale, 1 - mScale)
}
}
},
_restoreStartTouch() {
const self = this
this.customDuration = 300
// #ifdef APP-VUE || MP-WEIXIN || H5
this.left = this.wxsData.left
// #endif
// #ifndef APP-PLUS || MP-WEIXIN || H5
this.left = this._left
// #endif
this._run(false)
if (this.is3D) {
this.swiperViews[this.position].restoreScale(this.manualDuration)
if (this.position - 1 > -1) {
this.swiperViews[this.position - 1].restoreScale(this.manualDuration)
}
if (this.position + 1 < this._size) {
this.swiperViews[this.position + 1].restoreScale(this.manualDuration)
}
}
},
_notifyCurrentForItems(current, position) {
this.swiperViews && this.swiperViews.forEach(it => {
it.current = current
it.position = position
})
},
_run(isSet = true) {
this.isAnimated = true;
if (isSet)
this._setLeft();
const self = this;
if (this.is3D) {
this.swiperViews[this.position].restoreToScale(this.scale, this.manualDuration)
if (this.position - 1 > -1) {
this.swiperViews[this.position - 1].restoreToScale(1, this.manualDuration)
}
if (this.position + 1 < this._size) {
this.swiperViews[this.position + 1].restoreToScale(1, this.manualDuration)
}
}
// #ifdef APP-NVUE
animation.transition(this.$refs.container, {
styles: {
transform: 'translate(' + uni.upx2px(this.left) + 'px' + ',0px)',
},
duration: this.manualDuration, //ms
timingFunction: this.timingFunction,
needLayout: false,
delay: 0 //ms
}, function() {
self._reset();
})
// #endif
// #ifndef APP-NVUE
setTimeout(() => {
this._reset();
}, this.manualDuration);
// #endif
},
_setLeft() {
if (this.circular == true) {
const s1 = (this.width - this.itemWidth - this.space * 2) / 2;
let left = (this.plus + this.mCurrent) * (this.space + this.itemWidth) - s1;
this.left = -left;
} else {
this.left = -(this.itemWidth + this.space) * this.mCurrent
}
// #ifdef APP-VUE || MP-WEIXIN || H5
this.wxsData = {
left: this.left,
bounce: this.bounce
}
// #endif
},
_reset() {
this.isScrolling = false
this.isAnimated = false
this.customDuration = 0
if (this.circular == true) {
if (this.position == 1) {
this.mCurrent = this.actualSize - (this.plus - 1);
this._setLeft();
this._restoreScale()
}
// -2:原数组索引-1 + plus数组索引-1
if (this.position == this._size - 2) {
this.mCurrent = this.plus - 2;
this._setLeft();
this._restoreScale()
}
}
},
_restoreScale() {
if (this.is3D) {
this.swiperViews[this.position].restoreToScale(this.scale, 0)
if (this.position - 1 > -1) {
this.swiperViews[this.position - 1].restoreToScale(1, 0)
}
if (this.position + 1 < this._size) {
this.swiperViews[this.position + 1].restoreToScale(1, 0)
}
}
}
}
}

+ 0
- 91
components/sn-swiper/esc-swiper/mixins/bindingx.js View File

@ -1,91 +0,0 @@
const BindingX = uni.requireNativePlugin('bindingx');
const animation = uni.requireNativePlugin('animation');
export default {
mounted() {
this.container = this.getEl(this.$refs['container'])
},
methods: {
_notifyTouchChangeForItems(isTouchEnd) {
this.swiperViews && this.swiperViews.forEach(it => {
it.canClick = isTouchEnd
})
},
touchstart(e) {
if (this.isAnimated) return;
if (this.stop) return
this.isScrolling = true
this._notifyTouchChangeForItems(false)
this.stop = true
this.startTime = new Date().getTime()
const props = [{
element: this.container,
property: 'transform.translateX',
expression: uni.upx2px(this.left) + '+x'
}]
if (this.is3D) {
const deltaScale = `${this.scale}/${this.width}*x`
const currentScale = `${this.scale}-abs(x)/${this.width}*1`
const realScale = `min(${currentScale},${this.scale})`
const currentCardExp = `${realScale} < 1 ? 1 : ${realScale}`;
const leftCardExp = `${deltaScale} > 0 ? min(${this.scale},(1 + ${deltaScale})) : min(1,(1 + ${deltaScale}))`;
const rightCardExp = `${deltaScale} > 0 ? min(1,(1 - ${deltaScale})) : min(${this.scale},(1 - ${deltaScale}))`;
props.push({
element: this.getSwipteItemEl(this.position),
property: 'transform.scale',
expression: currentCardExp
})
if (this.position - 1 > -1) {
props.push({
element: this.getSwipteItemEl(this.position - 1),
property: 'transform.scale',
expression: leftCardExp
})
}
if (this.position + 1 > this._size) {
props.push({
element: this.getSwipteItemEl(this.position + 1),
property: 'transform.scale',
expression: rightCardExp
})
}
}
this.eventpan = BindingX.bind({
anchor: this.container,
eventType: 'pan',
props
}, (e) => {
if (e.state === 'end' ||
e.state === 'exit') {
setTimeout(() => {
this._notifyTouchChangeForItems(true)
}, 10)
const timing = new Date().getTime() - this.startTime
const velocity = Math.abs(e.deltaX / timing)
this.stop = false
this.isScrolling = false
if (this.eventpan && this.eventpan.token) {
BindingX.unbind({
token: this.eventpan.token,
eventType: 'pan'
})
}
this.moveEnd({
velocity,
deltaX: e.deltaX,
deltaY: e.deltaY
})
}
})
},
getSwipteItemEl(idx) {
return this.swiperViews[idx].$el.ref
},
/**
* 获取ref
* @param {Object} el
*/
getEl(el) {
return el.ref
}
}
}

+ 0
- 143
components/sn-swiper/esc-swiper/mixins/index.wxs View File

@ -1,143 +0,0 @@
var MIN_DISTANCE = 10;
function changeData(newValue, oldValue, ownerInstance, instance) {
var state = instance.getState();
if (newValue.left != undefined) {
state.left = newValue.left
}
if (newValue.bounce != undefined) {
state.bounce = newValue.bounce
}
// console.log('changeData', JSON.stringify(newValue))
}
/**
* 开始触摸操作
* @param {Object} e
* @param {Object} ins
*/
function touchstart(e, ins) {
var instance = e.instance;
// 计算滑动开始位置
stopTouchStart(e, ins)
}
/**
* 开始滑动操作
* @param {Object} e
* @param {Object} ownerInstance
*/
function touchmove(e, ownerInstance) {
var instance = e.instance;
// 是否可以滑动页面
stopTouchMove(e);
if (e.preventDefault) {
// 阻止页面滚动
e.preventDefault()
}
// var state = instance.getState();
// && state.bounce
move(instance, ownerInstance)
return false
}
/**
* 结束触摸操作
* @param {Object} e
* @param {Object} ownerInstance
*/
function touchend(e, ownerInstance) {
var instance = e.instance;
var state = instance.getState()
ownerInstance.callMethod('moveEnd', {
velocity: Math.abs(state.deltaX / state.timing),
direction: state.direction,
deltaX: state.deltaX,
deltaY: state.deltaY
})
}
/**
* 设置移动距离
* @param {Object} instance
* @param {Object} ownerInstance
*/
function move(instance, ownerInstance) {
var state = instance.getState()
var value = state.deltaX || 0
var state = instance.getState()
if (state.direction == 'horizontal') {
// instance.requestAnimationFrame(function() {
// instance.setStyle({
// transform: 'translateX(' + value + 'px)',
// '-webkit-transform': 'translateX(' + value + 'px)'
// })
// })
ownerInstance.callMethod('moveTo', {
deltaX: value,
deltaY: state.deltaY || 0,
left: state.left + value
})
}
}
/**
* 滑动中,是否禁止打开
* @param {Object} event
*/
function stopTouchMove(event) {
var instance = event.instance;
var state = instance.getState();
var touch = event.touches[0];
state.timing = getDate().getTime() - state.startTime;
state.deltaX = touch.clientX - state.startX;
state.deltaY = touch.clientY - state.startY;
state.offsetX = Math.abs(state.deltaX);
state.offsetY = Math.abs(state.deltaY);
state.direction = state.direction || getDirection(state.offsetX, state.offsetY);
}
/**
* 设置滑动开始位置
* @param {Object} event
*/
function stopTouchStart(event) {
var instance = event.instance;
var state = instance.getState();
resetTouchStatus(instance);
var touch = event.touches[0];
state.startTime = getDate().getTime();
state.startX = touch.clientX;
state.startY = touch.clientY;
}
function getDirection(x, y) {
if (x > y && x > MIN_DISTANCE) {
return 'horizontal';
}
if (y > x && y > MIN_DISTANCE) {
return 'vertical';
}
return '';
}
/**
* 重置滑动状态
* @param {Object} event
*/
function resetTouchStatus(instance) {
var state = instance.getState();
state.direction = '';
state.deltaX = 0;
state.deltaY = 0;
state.offsetX = 0;
state.offsetY = 0;
}
module.exports = {
changeData: changeData,
touchstart: touchstart,
touchmove: touchmove,
touchend: touchend
}

+ 0
- 62
components/sn-swiper/esc-swiper/mixins/mpother.js View File

@ -1,62 +0,0 @@
const MIN_DISTANCE = 10;
export default {
methods: {
touchstart(e) {
this._left = this.left
this.stopTouchStart(e)
},
touchmove(e) {
// 是否可以滑动页面
this.stopTouchMove(e);
if (this.direction == 'horizontal') {
this.moveTo({
deltaX: this.deltaX,
deltaY: this.deltaY || 0,
left: this._left + this.deltaX
})
}
// FIXME: 冒泡
return false
},
touchend() {
this.moveEnd({
velocity: Math.abs(this.deltaX / this.timing),
direction: this.direction,
deltaX: this.deltaX,
deltaY: this.deltaY,
})
},
stopTouchStart(event) {
this.resetTouchStatus();
const touch = event.touches[0];
this.startTime = new Date().getTime();
this.startX = touch.clientX;
this.startY = touch.clientY;
},
stopTouchMove(event) {
const touch = event.touches[0];
this.timing = new Date().getTime() - this.startTime;
this.deltaX = touch.clientX - this.startX;
this.deltaY = touch.clientY - this.startY;
this.offsetX = Math.abs(this.deltaX);
this.offsetY = Math.abs(this.deltaY);
this.direction = this.direction || this.getDirection(this.offsetX, this.offsetY);
},
getDirection(x, y) {
if (x > y && x > MIN_DISTANCE) {
return 'horizontal';
}
if (y > x && y > MIN_DISTANCE) {
return 'vertical';
}
return '';
},
resetTouchStatus() {
this.direction = '';
this.deltaX = 0;
this.deltaY = 0;
this.offsetX = 0;
this.offsetY = 0;
}
}
}

+ 0
- 7
components/sn-swiper/esc-swiper/mixins/mpwxs.js View File

@ -1,7 +0,0 @@
export default {
data() {
return {
wxsData: {}
}
}
}

+ 0
- 15
components/utils/style.js View File

@ -1,15 +0,0 @@
const toLine = (name) => {
return name.replace(/([A-Z])/g, '-$1').toLowerCase();
}
/**
* style对象转化为style字符串
* @return {string}
*/
export const getStyleStr = (styleObject) => {
let transfrom = '';
for (let i in styleObject) {
let line = toLine(i);
transfrom += line + ':' + styleObject[i] + ';';
}
return transfrom
}

+ 0
- 3
pages.json View File

@ -1,7 +1,4 @@
{
"easycom": {
"^esc-(.*)": "@/components/sn-swiper/esc-$1/index.vue"
},
"pages": [{
"path": "pages/index/index",
"style": {


+ 2
- 0
pages_order/components/address/addressList.vue View File

@ -123,6 +123,8 @@
overflow: hidden;
margin-bottom: 20rpx;
padding: 15rpx 15rpx 0rpx 15rpx;
width: 100%;
box-sizing: border-box;
.address-item-top {
border-bottom: 1px dashed #D3D1D1;


+ 227
- 0
pages_order/components/address/redactAddressForm.vue View File

@ -0,0 +1,227 @@
<template>
<view class="redact-address">
<uv-form label-width="210rpx" :model="addressDetail" ref="form">
<uv-form-item label="联系人" prop="name">
<uv-input v-model="addressDetail.name" placeholder="请输入联系人姓名" border="none">
</uv-input>
</uv-form-item>
<uv-form-item label="手机号" prop="phone">
<uv-input v-model="addressDetail.phone" placeholder="请输入手机号" border="none">
</uv-input>
</uv-form-item>
<uv-form-item label="所在地区" prop="address">
<uv-input v-model="addressDetail.address" placeholder="请选择所在地区" border="none">
</uv-input>
<template #right>
<view style="padding-right: 40rpx;color: #FBAB32;" @click.stop="selectAddr">
<image src="../../static/address/selectIcon.png" mode="aspectFit"></image>定位
</view>
</template>
</uv-form-item>
<uv-form-item label="详细地址" prop="addressDetail">
<uv-input v-model="addressDetail.addressDetails" placeholder="请输入详细地址" border="none">
</uv-input>
</uv-form-item>
</uv-form>
</view>
</template>
<script>
import Position from '@/utils/position.js'
export default {
data() {
return {
addressDetail: {
name: '',
phone: '',
address: '',
addressDetails: '',
defaultFlag: '',
latitude: '',
longitude: ''
}
}
},
props: {
title: {
type: String,
default: '新增地址'
}
},
methods: {
open(addressDetail) {
this.addressDetail = addressDetail
this.$refs.addressPopup.open('bottom')
},
close(){
this.$refs.addressPopup.close()
},
//
onSubmit() {
let isOk = this.parameterVerification(this.addressDetail)
if (isOk && !isOk.auth) {
return uni.showToast({
icon: 'none',
title: isOk.title,
'zIndex': 10000
})
}
this.$emit('saveOrUpdate', this.addressDetail)
},
//
parameterVerification(addressForm) {
let {
name,
phone,
address,
addressDetails
} = addressForm
if (name.trim() == '') {
return {
title: '请填写联系人',
auth: false
}
} else if (phone.trim() == '') {
return {
title: '请填写手机号',
auth: false
}
} else if (address.trim() == '') {
return {
title: '请填写所在地区',
auth: false
}
} else if (addressDetails.trim() == '') {
return {
title: '请填写详细地址',
auth: false
}
} else if (phone.trim() != '') {
if (!this.$utils.verificationPhone(phone)) {
return {
title: '手机号格式不合法',
auth: false
}
}
}
return {
title: '验证通过',
auth: true
}
},
//
selectAddr() {
// Position.getLocation(res => {
Position.selectAddress(0, 0, success => {
this.setAddress(success)
})
// })
},
//
setAddress(res) {
//
this.addressDetail.latitude = res.latitude
this.addressDetail.longitude = res.longitude
if (!res.address && res.name) { //
return this.addressDetail.address = res.name
}
if (res.address || res.name) {
return this.addressDetail.address = res.address + res.name
}
this.addressDetail.address = '' //
},
}
}
</script>
<style lang="scss" scoped>
.redact-address {
box-sizing: border-box;
.redact-address-title {
height: 80rpx;
line-height: 80rpx;
font-size: 30rpx;
color: #333333;
font-weight: 600;
}
.save {
display: flex;
align-items: center;
justify-content: center;
width: 90%;
height: 80rpx;
border-radius: 40rpx;
color: white;
font-size: 28rpx;
margin: 0rpx auto;
background: $uni-color;
margin-top: 150rpx;
}
image {
width: 25rpx;
height: 25rpx;
}
//
.uv-form {
padding: 30rpx 0rpx;
}
.uv-input__content__field-wrapper__field{
padding: 30rpx !important;
height: 180rpx !important;
}
&::v-deep .uv-cell {
padding: 0rpx 0rpx;
font-size: 26rpx;
&::after {
border: none !important;
}
.uv-field__label {
display: flex;
align-items: center;
height: 80rpx;
}
.uv-field__control,
.uv-field__right-icon {
height: 80rpx;
font-size: 26rpx;
border-bottom: 2rpx solid #cbc8c8;
}
.uv-field__right-icon {
display: flex;
align-items: center;
height: 78rpx;
color: #5FCC9F;
}
.uv-cell__value {
height: 120rpx;
}
}
&::v-deep .uv-field__error-message {
color: #5AC796;
font-size: 20rpx;
margin-top: 10rpx;
}
}
</style>

+ 0
- 3
pages_order/mine/memberCenter.vue View File

@ -134,9 +134,6 @@
<script>
import mixinsList from '@/mixins/list.js'
import {
getSwiperList
} from '@/components/sn-swiper/esc-swiper/helper.js';
import {
mapState
} from 'vuex'


+ 4
- 3
pages_order/order/giftList.vue View File

@ -6,12 +6,13 @@
<!-- 订单筛选 -->
<view class="tabs">
<uv-tabs :list="tabs"
:activeStyle="{color : '#fff', fontWeight : 600}"
lineColor="#fff"
:inactiveStyle="{color: 'rgba(255,255,255,.8)'}"
:activeStyle="{color : 'rgb(235, 51, 0)', fontWeight : 600}"
lineColor="rgb(235, 51, 0)"
:inactiveStyle="{color: 'rgba(235, 51, 0, .8)'}"
lineHeight="8rpx"
lineWidth="50rpx"
:current="current"
:scrollable="false"
@click="clickTabs"></uv-tabs>
</view>


+ 2
- 2
pages_order/order/instantGift.vue View File

@ -76,7 +76,7 @@
imageUrl : this.giftList[this.selectedIndex].image,
}
if(this.userInfo.id){
o.path = this.Gshare.path + '?shareId=' + this.userInfo.id
o.path += '&shareId=' + this.userInfo.id
}
return o
},
@ -88,7 +88,7 @@
imageUrl : this.giftList[this.selectedIndex].image,
}
if(this.userInfo.id){
o.path = this.Gshare.path + '?shareId=' + this.userInfo.id
o.path += '&shareId=' + this.userInfo.id
}
return o
},


+ 505
- 41
pages_order/order/receiveGift.vue View File

@ -1,59 +1,202 @@
<template>
<view class="receive-gift">
<navbar title="礼品领取" leftClick @leftClick="$utils.navigateBack" />
<!-- 主图片展示区 -->
<view class="main-image">
<image :src="giftInfo.image" mode="aspectFill"></image>
</view>
<template v-if="isOpeningEnd">
<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.title}}</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.name}}</text>
<text class="label">的祝福</text>
</view>
<view class="blessing-content">
{{ giftInfo.giveTitle }}
</view>
</view>
<!-- 底部区域 -->
<view class="bottom-area">
<!-- <view class="countdown">距离礼包失效{{countdownText}}</view> -->
<!-- <button class="receive-btn"
@click="receiveGift"
:disabled="giftInfo.giveStatus !== 0">
{{giftInfo.giveStatus === 0 ? '立即领取' : '已领取'}}
</button> -->
<button class="receive-btn"
v-if="giftInfo.giveStatus != 1"
@click="openAddress">
立即领取
</button>
</view>
</template>
<!-- 礼物开启动画弹窗 -->
<uv-popup ref="giftPopup"
mode="center"
:round="20"
:closeable="false"
:closeOnClickOverlay="false"
:maskClick="false"
:customStyle="{backgroundColor: 'transparent', padding: 0}"
overlayOpacity="0.8">
<view class="gift-card" :class="{ 'open': isOpening }">
<view class="gift-icon" :class="{ 'bounce': !isOpening }">
<uv-icon name="gift-fill" size="120" color="#FFD700"></uv-icon>
</view>
<view class="gift-content">
<view class="gift-title" :class="{ 'fade': isOpening }">
{{ giftInfo.name }}送你一份礼物
</view>
<view class="gift-open-btn"
@click="openGift"
:class="{ 'pulse': !isOpening }">
<text></text>
<view class="btn-sparkle"></view>
</view>
<view class="gift-tip" :class="{ 'fade': isOpening }">
{{ giftInfo.giveTitle }}
</view>
</view>
</view>
</uv-popup>
<!-- 抽奖弹窗 -->
<uv-popup ref="lotteryPopup"
mode="center"
:round="20"
:closeable="false"
:maskClick="false"
:closeOnClickOverlay="false"
:customStyle="{backgroundColor: 'transparent', padding: 0}"
overlayOpacity="0.8">
<view class="lottery-card">
<view class="lottery-title">{{ giftInfo.name }}为您精选了一份抽奖礼包</view>
<view class="gift-tip">
{{ giftInfo.giveTitle }}
</view>
<view class="lottery-content">
<view class="product-image">
<image :src="giftInfo.image" mode="aspectFill"></image>
</view>
<view class="product-info">
<view class="product-name">{{giftInfo.title}}</view>
<view class="product-price">{{giftInfo.price}}</view>
</view>
</view>
<view class="lottery-btn" @click="startLottery" v-if="!isSpinning">
试试手气
</view>
<view class="lottery-result" v-if="showResult">
<text v-if="isWinner">恭喜您中奖了</text>
<text v-else>很遗憾下次再来</text>
</view>
<view class="lottery-btn"
@click="openAddress"
v-if="isWinner && giftInfo.giveStatus != 1">
立即领取
</view>
</view>
</uv-popup>
<!-- 礼品信息区域 -->
<view class="gift-info">
<view class="gift-name">{{giftInfo.title}}</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.name}}</text>
<text class="label">的祝福</text>
<!-- 地址选择 -->
<uv-popup ref="addressPopup" :round="30" style="padding-bottom: 90rpx;">
<view class="addressPopupTitle">
填写地址就能收礼了
</view>
<view class="blessing-content">
{{ giftInfo.giveTitle }}
<!-- <view class="tabs">
<uv-tabs :list="tabs"
:activeStyle="{color : 'rgb(235, 51, 0)', fontWeight : 600}"
lineColor="#fff"
:inactiveStyle="{color: 'rgba(235, 51, 0, 0.8)'}"
lineHeight="8rpx"
lineWidth="50rpx"
:current="addressCurrent"
@click="clickTabs"></uv-tabs>
</view> -->
<addressList
ref="addressList"
height="60vh"
v-if="addressCurrent == 1"
@select="receiveGift" />
<view class="redactAddressForm"
v-else>
<redactAddressForm
ref="redactAddressForm"
@saveOrUpdate="receiveGift"
/>
<view
@click="$refs.redactAddressForm.onSubmit"
class="save">确认收礼</view>
</view>
</view>
<!-- 底部区域 -->
<view class="bottom-area">
<!-- <view class="countdown">距离礼包失效{{countdownText}}</view> -->
<!-- <button class="receive-btn"
@click="receiveGift"
:disabled="giftInfo.giveStatus !== 0">
{{giftInfo.giveStatus === 0 ? '立即领取' : '已领取'}}
</button> -->
<button class="receive-btn"
@click="receiveGift">
立即领取
</button>
</view>
</uv-popup>
</view>
</template>
<script>
import addressList from '../components/address/addressList.vue'
import redactAddressForm from '../components/address/redactAddressForm.vue'
export default {
components : {
addressList,
redactAddressForm,
},
data() {
return {
giftId: '',
giftInfo: {
},
countdown: 24 * 60 * 60, //
countdownTimer: null
countdownTimer: null,
//
isOpening: false,
isOpeningEnd : false,
//
isSpinning: false,
showResult: false,
isWinner: false,
//
addressTotal: 0,
tabs: [
{
name: '新地址'
},
{
name: '现有地址'
}
],
addressCurrent : 0,
}
},
computed: {
@ -65,6 +208,44 @@
}
},
methods: {
//
openGift() {
this.isOpening = true
setTimeout(() => {
this.$refs.giftPopup.close()
this.isOpeningEnd = true
}, 1000)
},
//
startLottery() {
if (this.isSpinning) return
uni.showLoading({
title: '抽奖中...',
})
this.isSpinning = true
this.showResult = false
//
setTimeout(() => {
this.$api('getGiveShopLottery', {
orderId : this.giftId,
}).then(res => {
uni.hideLoading()
this.showResult = true
if(res.code == 200){
this.isWinner = !!res.result
}
})
// this.isSpinning = false
//
// this.isWinner = Math.random() > 0.5
}, 2000)
},
//
async getGiftInfo() {
try {
@ -72,6 +253,16 @@
id: this.giftId
})
this.giftInfo = res.result
this.$nextTick(() => {
//
if(this.giftInfo.isGive == 3){
this.$refs.lotteryPopup.open()
}else{
this.$refs.giftPopup.open()
}
})
//
// if (this.giftInfo.expireTime) {
// const expireTime = new Date(this.giftInfo.expireTime).getTime()
@ -87,12 +278,17 @@
}
},
//
async receiveGift() {
async receiveGift(address) {
try {
let res = await this.$api('getGiveShop', {
orderId : this.giftId
orderId : this.giftId,
address : address.address,
addressDetails : address.addressDetails,
name : address.name,
phone : address.phone,
})
if(res.code == 200){
this.$refs.addressPopup.close()
uni.showToast({
title: '领取成功',
icon: 'success'
@ -115,7 +311,26 @@
clearInterval(this.countdownTimer)
}
}, 1000)
}
},
//
openAddress() {
this.$refs.addressPopup.open('bottom')
},
//
getAddressList() {
//
this.$refs.addressList.getAddressList().then(res => {
this.addressTotal = res.total
})
},
clickTabs({index}){
this.addressCurrent = index
if(index == 1){
this.$nextTick(() => {
this.getAddressList()
})
}
},
},
onLoad(options) {
if (options.id) {
@ -123,6 +338,8 @@
this.getGiftInfo()
}
},
onShow() {
},
beforeDestroy() {
if (this.countdownTimer) {
clearInterval(this.countdownTimer)
@ -235,4 +452,251 @@
}
}
}
.addressPopupTitle{
padding: 40rpx;
font-size: 32rpx;
font-weight: 900;
text-align: center;
}
.redactAddressForm{
padding: 0 40rpx;
padding-bottom: 40rpx;
.save {
display: flex;
align-items: center;
justify-content: center;
width: 90%;
height: 80rpx;
border-radius: 40rpx;
color: white;
font-size: 28rpx;
margin: 0rpx auto;
background: $uni-color;
margin-top: 150rpx;
}
}
//
.gift-card {
position: relative;
background: $uni-color;
border-radius: 20rpx;
padding: 60rpx 40rpx;
width: 600rpx;
transform-origin: center;
transition: all 0.8s cubic-bezier(0.68, -0.55, 0.265, 1.55);
.gift-icon {
position: absolute;
top: -60rpx;
left: 50%;
transform: translateX(-50%);
width: 120rpx;
height: 120rpx;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55);
transform-origin: center bottom;
animation: giftBounce 2s infinite;
}
.gift-content {
margin-top: 60rpx;
transform-origin: center;
transition: all 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55);
color: #fff;
text-align: center;
.gift-open-btn {
position: relative;
width: 120rpx;
height: 120rpx;
border-radius: 50%;
background: linear-gradient(135deg, #FF4B4B, #E3441A);
margin: 40rpx auto;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
overflow: hidden;
box-shadow: 0 6rpx 20rpx rgba(227, 68, 26, 0.3);
text {
color: #fff;
font-size: 36rpx;
font-weight: bold;
z-index: 1;
}
.btn-sparkle {
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: linear-gradient(90deg,
rgba(255,255,255,0) 0%,
rgba(255,255,255,0.2) 50%,
rgba(255,255,255,0) 100%);
transform: rotate(45deg);
animation: sparkle 2s infinite;
}
&.pulse {
animation: pulse 2s infinite;
}
}
.gift-title{
color: #fff;
font-size: 32rpx;
font-weight: bold;
margin-bottom: 20rpx;
}
.gift-title, .gift-tip {
transition: all 0.5s ease;
&.fade {
opacity: 0;
transform: translateY(-20rpx);
}
}
.gift-icon {
&.bounce {
animation: giftBounce 2s infinite;
}
}
}
&.open {
transform: perspective(1000px) rotateY(720deg) scale(0.5);
opacity: 0;
.gift-icon {
transform: translateX(-50%) translateY(-200%) scale(1.5) rotate(720deg);
opacity: 0;
}
.gift-content {
transform: scale(0) rotate(-720deg);
opacity: 0;
}
}
}
@keyframes sparkle {
0% {
transform: rotate(45deg) translateX(-100%);
}
50% {
transform: rotate(45deg) translateX(100%);
}
100% {
transform: rotate(45deg) translateX(100%);
}
}
@keyframes pulse {
0% {
transform: scale(1);
}
50% {
transform: scale(1.05);
}
100% {
transform: scale(1);
}
}
// giftBounce
@keyframes giftBounce {
0%, 100% {
transform: translateX(-50%) translateY(0) rotate(0deg);
}
25% {
transform: translateX(-50%) translateY(-15rpx) rotate(-5deg);
}
75% {
transform: translateX(-50%) translateY(-15rpx) rotate(5deg);
}
}
//
.lottery-card {
width: 600rpx;
background: linear-gradient(135deg, #FF6B6B, #E3441A);
border-radius: 20rpx;
padding: 40rpx;
text-align: center;
.lottery-title {
color: #fff;
font-size: 32rpx;
font-weight: bold;
margin-bottom: 20rpx;
}
.gift-tip{
color: #fff;
font-size: 28rpx;
margin-bottom: 40rpx;
}
.lottery-content {
background: #fff;
border-radius: 16rpx;
padding: 30rpx;
margin: 0 auto 30rpx;
.product-image {
width: 300rpx;
height: 300rpx;
margin: 0 auto 20rpx;
border-radius: 8rpx;
overflow: hidden;
image {
width: 100%;
height: 100%;
}
}
.product-info {
text-align: center;
.product-name {
font-size: 28rpx;
color: #333;
margin-bottom: 10rpx;
}
.product-price {
font-size: 36rpx;
color: #E3441A;
font-weight: bold;
}
}
}
.lottery-btn {
margin-top: 40rpx;
background: #FFD700;
color: #E3441A;
padding: 20rpx 60rpx;
border-radius: 40rpx;
display: inline-block;
font-weight: bold;
box-shadow: 0 6rpx 20rpx rgba(255, 215, 0, 0.3);
}
.lottery-result {
margin-top: 30rpx;
color: #fff;
font-size: 32rpx;
font-weight: bold;
animation: fadeIn 0.5s ease;
}
}
</style>

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

@ -150,9 +150,8 @@
this.$store.commit('setPayOrderProduct', [
this.productDetail
])
// 1893686035042070530
// this.$utils.navigateTo('/pages_order/order/receiveGift?id=1893686035042070530')
// this.$utils.navigateTo('/pages_order/order/receiveGift?id=1894006757315850241')
this.$utils.navigateTo('/pages_order/order/createOrder?type=give')
},


Loading…
Cancel
Save