stellar-ui-plus 1.23.16 → 1.24.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/ste-coupon-list/README.md +167 -0
- package/components/ste-coupon-list/config.json +5 -0
- package/components/ste-coupon-list/props.ts +50 -0
- package/components/ste-coupon-list/ste-coupon-list.easycom.json +65 -0
- package/components/ste-coupon-list/ste-coupon-list.vue +381 -0
- package/package.json +1 -1
- package/utils/utils.ts +42 -1
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# CouponList 券列表
|
|
2
|
+
|
|
3
|
+
此组件用于券列表展示
|
|
4
|
+
|
|
5
|
+
---$
|
|
6
|
+
|
|
7
|
+
### 基础用法
|
|
8
|
+
|
|
9
|
+
- 属性`data`用于基础数据展示
|
|
10
|
+
|
|
11
|
+
```html
|
|
12
|
+
<script lang="ts" setup>
|
|
13
|
+
import { ref } from 'vue';
|
|
14
|
+
|
|
15
|
+
const data = ref({
|
|
16
|
+
title: '满11-5乐事组合【外卖】',
|
|
17
|
+
desc: '截至日期 2025.11.30',
|
|
18
|
+
image: 'https://image.whzb.com/chain/StellarUI/image/img5.jfif',
|
|
19
|
+
price: 500,
|
|
20
|
+
constraint: '满11可用',
|
|
21
|
+
footers: ['1、本券不可用于部分特惠商品、特殊商品及其他券看加使'],
|
|
22
|
+
});
|
|
23
|
+
</script>
|
|
24
|
+
<template>
|
|
25
|
+
<ste-coupon-list :data="data" />
|
|
26
|
+
</template>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### 倒计时
|
|
30
|
+
|
|
31
|
+
- 属性`endTime`用于设置倒计时结束时间
|
|
32
|
+
|
|
33
|
+
```html
|
|
34
|
+
<script lang="ts" setup>
|
|
35
|
+
import { ref } from 'vue';
|
|
36
|
+
|
|
37
|
+
const data = ref({
|
|
38
|
+
title: '满11-5乐事组合【外卖】',
|
|
39
|
+
desc: '截至日期 2025.11.30',
|
|
40
|
+
image: 'https://image.whzb.com/chain/StellarUI/image/img5.jfif',
|
|
41
|
+
price: 500,
|
|
42
|
+
constraint: '满11可用',
|
|
43
|
+
footers: ['1、本券不可用于部分特惠商品、特殊商品及其他券看加使'],
|
|
44
|
+
});
|
|
45
|
+
const endTime = computed(() => Date.now() + 60 * 60 * 1000 * 2);
|
|
46
|
+
</script>
|
|
47
|
+
<template>
|
|
48
|
+
<ste-coupon-list :data="data" :endTime="endTime" />
|
|
49
|
+
</template>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### 按钮文字
|
|
53
|
+
|
|
54
|
+
- 属性`buttonText`用于设置按钮文字内容
|
|
55
|
+
|
|
56
|
+
```html
|
|
57
|
+
<script lang="ts" setup>
|
|
58
|
+
import { ref } from 'vue';
|
|
59
|
+
|
|
60
|
+
const data = ref({
|
|
61
|
+
title: '满11-5乐事组合【外卖】',
|
|
62
|
+
desc: '截至日期 2025.11.30',
|
|
63
|
+
image: 'https://image.whzb.com/chain/StellarUI/image/img5.jfif',
|
|
64
|
+
price: 500,
|
|
65
|
+
constraint: '满11可用',
|
|
66
|
+
footers: ['1、本券不可用于部分特惠商品、特殊商品及其他券看加使'],
|
|
67
|
+
});
|
|
68
|
+
</script>
|
|
69
|
+
<template>
|
|
70
|
+
<ste-coupon-list :data="data" buttonText="去使用" />
|
|
71
|
+
</template>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### 禁用按钮
|
|
75
|
+
|
|
76
|
+
- 属性`buttonDisabled`禁用按钮
|
|
77
|
+
|
|
78
|
+
```html
|
|
79
|
+
<script lang="ts" setup>
|
|
80
|
+
import { ref } from 'vue';
|
|
81
|
+
|
|
82
|
+
const data = ref({
|
|
83
|
+
title: '满11-5乐事组合【外卖】',
|
|
84
|
+
desc: '截至日期 2025.11.30',
|
|
85
|
+
image: 'https://image.whzb.com/chain/StellarUI/image/img5.jfif',
|
|
86
|
+
price: 500,
|
|
87
|
+
constraint: '满11可用',
|
|
88
|
+
footers: ['1、本券不可用于部分特惠商品、特殊商品及其他券看加使'],
|
|
89
|
+
});
|
|
90
|
+
</script>
|
|
91
|
+
<template>
|
|
92
|
+
<ste-coupon-list :data="data" buttonDisabled />
|
|
93
|
+
</template>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### 进度条
|
|
97
|
+
|
|
98
|
+
- 属性`progress`用于设置进度条值,0-100
|
|
99
|
+
- 属性`progressText`用于设置进度条文字内容
|
|
100
|
+
|
|
101
|
+
```html
|
|
102
|
+
<script lang="ts" setup>
|
|
103
|
+
import { ref } from 'vue';
|
|
104
|
+
|
|
105
|
+
const data = ref({
|
|
106
|
+
title: '满11-5乐事组合【外卖】',
|
|
107
|
+
desc: '截至日期 2025.11.30',
|
|
108
|
+
image: 'https://image.whzb.com/chain/StellarUI/image/img5.jfif',
|
|
109
|
+
price: 500,
|
|
110
|
+
constraint: '满11可用',
|
|
111
|
+
footers: ['1、本券不可用于部分特惠商品、特殊商品及其他券看加使'],
|
|
112
|
+
});
|
|
113
|
+
</script>
|
|
114
|
+
<template>
|
|
115
|
+
<ste-coupon-list :data="data" :progress="50" progressText="仅剩40件" />
|
|
116
|
+
</template>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Position插槽
|
|
120
|
+
|
|
121
|
+
- 插槽`position`可以在任意位置插入内容
|
|
122
|
+
|
|
123
|
+
```html
|
|
124
|
+
<script lang="ts" setup>
|
|
125
|
+
import { ref } from 'vue';
|
|
126
|
+
|
|
127
|
+
const data = ref({
|
|
128
|
+
title: '满11-5乐事组合【外卖】',
|
|
129
|
+
desc: '截至日期 2025.11.30',
|
|
130
|
+
image: 'https://image.whzb.com/chain/StellarUI/image/img5.jfif',
|
|
131
|
+
price: 500,
|
|
132
|
+
constraint: '满11可用',
|
|
133
|
+
footers: ['1、本券不可用于部分特惠商品、特殊商品及其他券看加使'],
|
|
134
|
+
});
|
|
135
|
+
</script>
|
|
136
|
+
<template>
|
|
137
|
+
<ste-coupon-list :data="data">
|
|
138
|
+
<template #position>
|
|
139
|
+
<view class="position-slot">
|
|
140
|
+
<ste-image src="https://image.whzb.com/chain/StellarUI/售罄.png" width="130" height="92" />
|
|
141
|
+
</view>
|
|
142
|
+
</template>
|
|
143
|
+
</ste-coupon-list>
|
|
144
|
+
</template>
|
|
145
|
+
<style scoped>
|
|
146
|
+
.position-slot {
|
|
147
|
+
position: absolute;
|
|
148
|
+
top: 6rpx;
|
|
149
|
+
right: 6rpx;
|
|
150
|
+
}
|
|
151
|
+
</style>
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
---$
|
|
155
|
+
|
|
156
|
+
### API
|
|
157
|
+
|
|
158
|
+
<!-- props -->
|
|
159
|
+
|
|
160
|
+
#### Slot
|
|
161
|
+
|
|
162
|
+
| 插槽名称 | 说明 | 支持版本 |
|
|
163
|
+
| ---------- | ------------------ | -------- |
|
|
164
|
+
| `position` | 在任意位置插入内容 | - |
|
|
165
|
+
|
|
166
|
+
---$
|
|
167
|
+
{{xuyajun}}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { PropType } from "vue";
|
|
2
|
+
|
|
3
|
+
export interface PropData {
|
|
4
|
+
title: string;
|
|
5
|
+
desc?: string;
|
|
6
|
+
image: string;
|
|
7
|
+
price: string | number;
|
|
8
|
+
constraint?: string;
|
|
9
|
+
footers?: string[]
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default {
|
|
13
|
+
/** 基础数据 */
|
|
14
|
+
data: {
|
|
15
|
+
type: Object as PropType<PropData>,
|
|
16
|
+
default: () => ({}),
|
|
17
|
+
},
|
|
18
|
+
/** 结束时间 倒计时时间*/
|
|
19
|
+
endTime: {
|
|
20
|
+
type: [Date, String, Number],
|
|
21
|
+
default: () => null,
|
|
22
|
+
},
|
|
23
|
+
/** 剩余数量 */
|
|
24
|
+
residue: {
|
|
25
|
+
type: Number,
|
|
26
|
+
default: () => -1,
|
|
27
|
+
},
|
|
28
|
+
/** 进度条百分比 */
|
|
29
|
+
progress: {
|
|
30
|
+
type: Number,
|
|
31
|
+
default: () => -1,
|
|
32
|
+
},
|
|
33
|
+
progressText: {
|
|
34
|
+
type: String,
|
|
35
|
+
default: () => ''
|
|
36
|
+
},
|
|
37
|
+
/** 按钮文字 */
|
|
38
|
+
buttonText: {
|
|
39
|
+
type: String,
|
|
40
|
+
default: () => '购买',
|
|
41
|
+
},
|
|
42
|
+
buttonDisabled: {
|
|
43
|
+
type: Boolean,
|
|
44
|
+
default: () => false
|
|
45
|
+
},
|
|
46
|
+
backgroundColor: {
|
|
47
|
+
type: String,
|
|
48
|
+
default: () => '#fff',
|
|
49
|
+
},
|
|
50
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ste-coupon-list",
|
|
3
|
+
"description": "业务组件",
|
|
4
|
+
"example": "<ste-coupon-list></ste-coupon-list>",
|
|
5
|
+
"tutorial": "https://stellar-ui.intecloud.com.cn/?projectName=stellar-ui-plus&menu=%E7%BB%84%E4%BB%B6&active=ste-coupon-list",
|
|
6
|
+
"attributes": [
|
|
7
|
+
{
|
|
8
|
+
"name": "data",
|
|
9
|
+
"description": "基础数据",
|
|
10
|
+
"type": "{title: string;desc?: string;image: string;price: string | number;constraint?: string;footers?: string[]}",
|
|
11
|
+
"default": "{}"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"name": "endTime",
|
|
15
|
+
"description": "结束时间",
|
|
16
|
+
"type": "string | number | Date",
|
|
17
|
+
"default": ""
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"name": "residue",
|
|
21
|
+
"description": "剩余数量",
|
|
22
|
+
"type": "number",
|
|
23
|
+
"default": "-1"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"name": "progress",
|
|
27
|
+
"description": "进度条百分比",
|
|
28
|
+
"type": "string",
|
|
29
|
+
"default": ""
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"name": "buttonText",
|
|
33
|
+
"description": "按钮文字",
|
|
34
|
+
"type": "string",
|
|
35
|
+
"default": "购买"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"name": "buttonDisabled",
|
|
39
|
+
"description": "禁用按钮",
|
|
40
|
+
"type": "boolean",
|
|
41
|
+
"default": "false"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"name": "backgroundColor",
|
|
45
|
+
"description": "背景颜色",
|
|
46
|
+
"type": "string",
|
|
47
|
+
"default": "#fff"
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"name": "[event]buttonClick",
|
|
51
|
+
"description": "点击按钮时触发",
|
|
52
|
+
"type": "() => void"
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"name": "[event]footerClick",
|
|
56
|
+
"description": "点击页脚列表时触发",
|
|
57
|
+
"type": "(index:number) => void"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"name": "[event]countDown",
|
|
61
|
+
"description": "倒计时触发",
|
|
62
|
+
"type": "(data:any,time:number) => void"
|
|
63
|
+
}
|
|
64
|
+
]
|
|
65
|
+
}
|
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, onBeforeUnmount, onMounted, ref, watch, getCurrentInstance } from 'vue';
|
|
3
|
+
import propsData from './props';
|
|
4
|
+
import utils from '../../utils/utils';
|
|
5
|
+
|
|
6
|
+
const instance = getCurrentInstance();
|
|
7
|
+
const props = defineProps(propsData);
|
|
8
|
+
const emits = defineEmits(['buttonClick', 'footerClick', 'countDown']);
|
|
9
|
+
const rootStyle = computed(() => {
|
|
10
|
+
return {
|
|
11
|
+
'--ste-coupon-lis-background': props.backgroundColor,
|
|
12
|
+
};
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const countDown = ref({ days: 0, hours: 0, minutes: 0, seconds: 0 });
|
|
16
|
+
let interval: any;
|
|
17
|
+
onMounted(() => {
|
|
18
|
+
if (props.endTime) {
|
|
19
|
+
interval = utils.countDown(props.endTime, (data, time) => {
|
|
20
|
+
countDown.value = data;
|
|
21
|
+
emits('countDown', time);
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
// 当组件挂载时绘制进度条
|
|
25
|
+
drawProgress();
|
|
26
|
+
});
|
|
27
|
+
onBeforeUnmount(() => {
|
|
28
|
+
clearInterval(interval);
|
|
29
|
+
});
|
|
30
|
+
const viewTime = (v: number) => {
|
|
31
|
+
return v >= 10 ? v : `0${v}`;
|
|
32
|
+
};
|
|
33
|
+
const canvasId = utils.guid();
|
|
34
|
+
|
|
35
|
+
// #ifdef H5
|
|
36
|
+
// 绘制半圆环形进度条
|
|
37
|
+
const drawProgressH5 = () => {
|
|
38
|
+
const query = uni.createSelectorQuery();
|
|
39
|
+
|
|
40
|
+
query
|
|
41
|
+
.select('#' + canvasId)
|
|
42
|
+
.fields({ node: true, size: true }, res => {})
|
|
43
|
+
.exec(res => {
|
|
44
|
+
if (!res || !res[0]) return;
|
|
45
|
+
const canvas = res[0].node;
|
|
46
|
+
|
|
47
|
+
if (!canvas) return;
|
|
48
|
+
const ctx = canvas.getContext('2d');
|
|
49
|
+
if (!ctx) return;
|
|
50
|
+
// 清空画布
|
|
51
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
52
|
+
// 设置半圆进度条参数
|
|
53
|
+
const centerX = res[0].width / 2;
|
|
54
|
+
const centerY = res[0].height;
|
|
55
|
+
const radius = res[0].width / 2 - utils.formatPx(6, 'num'); // 留出边距
|
|
56
|
+
|
|
57
|
+
// 绘制背景圆弧(灰色)
|
|
58
|
+
ctx.beginPath();
|
|
59
|
+
ctx.arc(centerX, centerY, radius, Math.PI, 0, false);
|
|
60
|
+
ctx.lineWidth = utils.formatPx(8, 'num');
|
|
61
|
+
ctx.strokeStyle = '#F3F3F3';
|
|
62
|
+
ctx.lineCap = 'round';
|
|
63
|
+
ctx.stroke();
|
|
64
|
+
|
|
65
|
+
// 绘制进度圆弧
|
|
66
|
+
if (props.progress > 0) {
|
|
67
|
+
const progressAngle = Math.PI * (props.progress / 100);
|
|
68
|
+
ctx.beginPath();
|
|
69
|
+
ctx.arc(centerX, centerY, radius, Math.PI, Math.PI + progressAngle, false);
|
|
70
|
+
ctx.strokeStyle = '#FF283A';
|
|
71
|
+
ctx.lineCap = 'round';
|
|
72
|
+
ctx.stroke();
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
// #endif
|
|
77
|
+
|
|
78
|
+
// #ifdef MP-ALIPAY || APP
|
|
79
|
+
const drawProgressApp = () => {
|
|
80
|
+
// 使用uni.createCanvasContext创建绘图上下文
|
|
81
|
+
const ctx: UniApp.CanvasContext = uni.createCanvasContext(canvasId, instance);
|
|
82
|
+
|
|
83
|
+
// 设置canvas尺寸 - 根据样式定义的96rpx*48rpx
|
|
84
|
+
const canvasWidth = utils.formatPx(96, 'num');
|
|
85
|
+
const canvasHeight = utils.formatPx(48, 'num');
|
|
86
|
+
|
|
87
|
+
// 清空画布
|
|
88
|
+
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
|
|
89
|
+
|
|
90
|
+
// 设置半圆进度条参数
|
|
91
|
+
const centerX = canvasWidth / 2;
|
|
92
|
+
const centerY = canvasHeight;
|
|
93
|
+
const radius = canvasWidth / 2 - utils.formatPx(6, 'num'); // 留出边距
|
|
94
|
+
|
|
95
|
+
// 绘制背景圆弧(灰色)
|
|
96
|
+
ctx.beginPath();
|
|
97
|
+
ctx.arc(centerX, centerY, radius, Math.PI, 0, false);
|
|
98
|
+
ctx.setLineWidth(utils.formatPx(8, 'num'));
|
|
99
|
+
ctx.setStrokeStyle('#F3F3F3');
|
|
100
|
+
ctx.setLineCap('round');
|
|
101
|
+
ctx.stroke();
|
|
102
|
+
|
|
103
|
+
// 绘制进度圆弧
|
|
104
|
+
if (props.progress > 0) {
|
|
105
|
+
const progressAngle = Math.PI * (props.progress / 100);
|
|
106
|
+
ctx.beginPath();
|
|
107
|
+
ctx.arc(centerX, centerY, radius, Math.PI, Math.PI + progressAngle, false);
|
|
108
|
+
ctx.setStrokeStyle('#FF283A');
|
|
109
|
+
ctx.setLineCap('round');
|
|
110
|
+
ctx.stroke();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// 绘制到canvas
|
|
114
|
+
ctx.draw(false);
|
|
115
|
+
};
|
|
116
|
+
// #endif
|
|
117
|
+
|
|
118
|
+
// #ifdef MP-WEIXIN
|
|
119
|
+
const base64Progress = ref('');
|
|
120
|
+
const drawProgressWx = () => {
|
|
121
|
+
// 创建离屏canvas
|
|
122
|
+
const canvas = wx.createOffscreenCanvas({
|
|
123
|
+
type: '2d',
|
|
124
|
+
width: 96,
|
|
125
|
+
height: 48,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const ctx = canvas.getContext('2d');
|
|
129
|
+
if (!ctx) return;
|
|
130
|
+
|
|
131
|
+
// 清空画布
|
|
132
|
+
ctx.clearRect(0, 0, 96, 48);
|
|
133
|
+
|
|
134
|
+
// 设置半圆进度条参数
|
|
135
|
+
const centerX = 48;
|
|
136
|
+
const centerY = 48;
|
|
137
|
+
const radius = 42; // 留出边距
|
|
138
|
+
|
|
139
|
+
// 绘制背景圆弧(灰色)
|
|
140
|
+
ctx.beginPath();
|
|
141
|
+
ctx.arc(centerX, centerY, radius, Math.PI, 0, false);
|
|
142
|
+
ctx.lineWidth = 8;
|
|
143
|
+
ctx.strokeStyle = '#F3F3F3';
|
|
144
|
+
ctx.lineCap = 'round';
|
|
145
|
+
ctx.stroke();
|
|
146
|
+
|
|
147
|
+
// 绘制进度圆弧
|
|
148
|
+
if (props.progress > 0) {
|
|
149
|
+
const progressAngle = Math.PI * (props.progress / 100);
|
|
150
|
+
ctx.beginPath();
|
|
151
|
+
ctx.arc(centerX, centerY, radius, Math.PI, Math.PI + progressAngle, false);
|
|
152
|
+
ctx.strokeStyle = '#FF283A';
|
|
153
|
+
ctx.lineCap = 'round';
|
|
154
|
+
ctx.stroke();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// 转换为base64
|
|
158
|
+
try {
|
|
159
|
+
const dataURL = canvas.toDataURL('image/png');
|
|
160
|
+
// 在实际应用中,你可能需要将这个dataURL设置到某个变量中供模板使用
|
|
161
|
+
// 例如: progressImage.value = dataURL;
|
|
162
|
+
base64Progress.value = dataURL;
|
|
163
|
+
} catch (error) {
|
|
164
|
+
console.error('Failed to convert canvas to base64:', error);
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
// #endif
|
|
169
|
+
const drawProgress = () => {
|
|
170
|
+
if (props.progress < 0) return;
|
|
171
|
+
setTimeout(() => {
|
|
172
|
+
// #ifdef H5
|
|
173
|
+
drawProgressH5();
|
|
174
|
+
// #endif
|
|
175
|
+
// #ifdef MP-ALIPAY || APP
|
|
176
|
+
drawProgressApp();
|
|
177
|
+
// #endif
|
|
178
|
+
// #ifdef MP-WEIXIN
|
|
179
|
+
drawProgressWx();
|
|
180
|
+
// #endif
|
|
181
|
+
});
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
// 监听progress变化重新绘制
|
|
185
|
+
watch(() => props.progress, drawProgress);
|
|
186
|
+
</script>
|
|
187
|
+
<template>
|
|
188
|
+
<view class="ste-coupon-list--root" :style="rootStyle">
|
|
189
|
+
<view class="coupon-info-block">
|
|
190
|
+
<ste-image width="144" height="144" :src="data.image" radius="12" />
|
|
191
|
+
<view class="coupon-info">
|
|
192
|
+
<view class="info-title">{{ data.title }}</view>
|
|
193
|
+
<view class="info-desc">
|
|
194
|
+
<view v-if="endTime" class="coun-down">
|
|
195
|
+
<view class="coun-down-view">{{ viewTime(countDown.hours) }}</view>
|
|
196
|
+
<view class="coun-down-dot">:</view>
|
|
197
|
+
<view class="coun-down-view">{{ viewTime(countDown.minutes) }}</view>
|
|
198
|
+
<view class="coun-down-dot">:</view>
|
|
199
|
+
<view class="coun-down-view">{{ viewTime(countDown.seconds) }}</view>
|
|
200
|
+
<view class="coun-down-msg">后截止</view>
|
|
201
|
+
</view>
|
|
202
|
+
<view v-else>
|
|
203
|
+
{{ data.desc }}
|
|
204
|
+
</view>
|
|
205
|
+
</view>
|
|
206
|
+
<view class="info-price">
|
|
207
|
+
<view class="price-left">
|
|
208
|
+
<view class="price-text">
|
|
209
|
+
<ste-price :value="data.price" font-size="40" />
|
|
210
|
+
</view>
|
|
211
|
+
<text class="info-constraint">{{ data.constraint }}</text>
|
|
212
|
+
</view>
|
|
213
|
+
<view class="price-right">
|
|
214
|
+
<ste-button :disabled="buttonDisabled" :mode="100" @click="emits('buttonClick')">{{ buttonText }}</ste-button>
|
|
215
|
+
</view>
|
|
216
|
+
</view>
|
|
217
|
+
</view>
|
|
218
|
+
<view class="circle-progress-canvas" v-if="progress >= 0">
|
|
219
|
+
<!-- #ifndef MP-WEIXIN -->
|
|
220
|
+
<canvas :canvas-id="canvasId" :id="canvasId" class="canvas-element" />
|
|
221
|
+
<!-- #endif -->
|
|
222
|
+
<!-- #ifdef MP-WEIXIN -->
|
|
223
|
+
<image :src="base64Progress" class="canvas-element" />
|
|
224
|
+
<!-- #endif -->
|
|
225
|
+
|
|
226
|
+
<view class="progress-text">{{ progressText }}</view>
|
|
227
|
+
</view>
|
|
228
|
+
<slot name="position"></slot>
|
|
229
|
+
</view>
|
|
230
|
+
<view class="coupon-footer-block" v-if="data.footers?.length">
|
|
231
|
+
<view class="coupon-footer-item" v-for="(value, i) in data.footers" :key="i">
|
|
232
|
+
<view class="footer-item-text">
|
|
233
|
+
{{ value }}
|
|
234
|
+
</view>
|
|
235
|
+
<view class="footer-item-icon" @click="emits('footerClick', i)">
|
|
236
|
+
<ste-icon code="" size="12" color="#A4A4A4" />
|
|
237
|
+
</view>
|
|
238
|
+
</view>
|
|
239
|
+
</view>
|
|
240
|
+
</view>
|
|
241
|
+
</template>
|
|
242
|
+
|
|
243
|
+
<style lang="scss" scoped>
|
|
244
|
+
.ste-coupon-list--root {
|
|
245
|
+
width: 100%;
|
|
246
|
+
|
|
247
|
+
.coupon-info-block {
|
|
248
|
+
width: 100%;
|
|
249
|
+
height: 180rpx;
|
|
250
|
+
padding: 18rpx 18rpx 14rpx 18rpx;
|
|
251
|
+
display: flex;
|
|
252
|
+
align-items: flex-start;
|
|
253
|
+
justify-content: space-between;
|
|
254
|
+
background-color: var(--ste-coupon-lis-background);
|
|
255
|
+
border-radius: 16rpx;
|
|
256
|
+
position: relative;
|
|
257
|
+
|
|
258
|
+
.coupon-info {
|
|
259
|
+
width: calc(100% - 162rpx);
|
|
260
|
+
height: 100%;
|
|
261
|
+
.info-title {
|
|
262
|
+
font-weight: bold;
|
|
263
|
+
font-size: 28rpx;
|
|
264
|
+
line-height: 40rpx;
|
|
265
|
+
color: #000000;
|
|
266
|
+
}
|
|
267
|
+
.info-desc {
|
|
268
|
+
height: 36rpx;
|
|
269
|
+
font-size: 22rpx;
|
|
270
|
+
color: #727272;
|
|
271
|
+
line-height: 32rpx;
|
|
272
|
+
margin-top: 8rpx;
|
|
273
|
+
.coun-down {
|
|
274
|
+
display: flex;
|
|
275
|
+
align-items: center;
|
|
276
|
+
font-weight: 400;
|
|
277
|
+
font-size: 20rpx;
|
|
278
|
+
color: #ff283a;
|
|
279
|
+
line-height: 28rpx;
|
|
280
|
+
text-align: center;
|
|
281
|
+
.coun-down-view {
|
|
282
|
+
height: 100%;
|
|
283
|
+
width: 28rpx;
|
|
284
|
+
height: 28rpx;
|
|
285
|
+
background: #fef4f1;
|
|
286
|
+
border-radius: 4rpx;
|
|
287
|
+
}
|
|
288
|
+
.coun-down-dot {
|
|
289
|
+
margin: 0 8rpx;
|
|
290
|
+
}
|
|
291
|
+
.coun-down-msg {
|
|
292
|
+
font-weight: 400;
|
|
293
|
+
font-size: 20rpx;
|
|
294
|
+
color: #a4a4a4;
|
|
295
|
+
line-height: 28rpx;
|
|
296
|
+
margin-left: 12rpx;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
.info-price {
|
|
301
|
+
margin-top: 12rpx;
|
|
302
|
+
width: 100%;
|
|
303
|
+
height: 56rpx;
|
|
304
|
+
display: flex;
|
|
305
|
+
align-items: flex-start;
|
|
306
|
+
justify-content: space-between;
|
|
307
|
+
.price-left {
|
|
308
|
+
height: 100%;
|
|
309
|
+
display: flex;
|
|
310
|
+
align-items: flex-end;
|
|
311
|
+
.price-text {
|
|
312
|
+
height: 100%;
|
|
313
|
+
display: flex;
|
|
314
|
+
align-items: center;
|
|
315
|
+
}
|
|
316
|
+
.info-constraint {
|
|
317
|
+
font-size: 20rpx;
|
|
318
|
+
color: #363636;
|
|
319
|
+
line-height: 42rpx;
|
|
320
|
+
margin-left: 16rpx;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
.circle-progress-canvas {
|
|
326
|
+
width: 96rpx;
|
|
327
|
+
height: 48rpx;
|
|
328
|
+
position: absolute;
|
|
329
|
+
top: 24rpx;
|
|
330
|
+
right: 26rpx;
|
|
331
|
+
.canvas-element {
|
|
332
|
+
width: 100%;
|
|
333
|
+
height: 100%;
|
|
334
|
+
}
|
|
335
|
+
.progress-text {
|
|
336
|
+
width: 100%;
|
|
337
|
+
text-align: center;
|
|
338
|
+
position: absolute;
|
|
339
|
+
bottom: -6rpx;
|
|
340
|
+
font-size: 16rpx;
|
|
341
|
+
color: #a4a4a4;
|
|
342
|
+
line-height: 22rpx;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
.coupon-footer-block {
|
|
348
|
+
width: 100%;
|
|
349
|
+
margin-top: 2px;
|
|
350
|
+
background-color: var(--ste-coupon-lis-background);
|
|
351
|
+
border-radius: 16rpx;
|
|
352
|
+
padding: 14rpx 18rpx;
|
|
353
|
+
.coupon-footer-item {
|
|
354
|
+
width: 100%;
|
|
355
|
+
display: flex;
|
|
356
|
+
justify-content: space-between;
|
|
357
|
+
align-items: center;
|
|
358
|
+
.footer-item-text {
|
|
359
|
+
// 不换行
|
|
360
|
+
width: calc(100% - 54rpx);
|
|
361
|
+
white-space: nowrap;
|
|
362
|
+
text-overflow: ellipsis;
|
|
363
|
+
overflow: hidden;
|
|
364
|
+
font-weight: 400;
|
|
365
|
+
font-size: 24rpx;
|
|
366
|
+
color: #a4a4a4;
|
|
367
|
+
line-height: 34rpx;
|
|
368
|
+
}
|
|
369
|
+
.footer-item-icon {
|
|
370
|
+
width: 26rpx;
|
|
371
|
+
height: 26rpx;
|
|
372
|
+
display: flex;
|
|
373
|
+
align-items: center;
|
|
374
|
+
justify-content: center;
|
|
375
|
+
background: #f3f3f3;
|
|
376
|
+
border-radius: 50%;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
</style>
|
package/package.json
CHANGED
package/utils/utils.ts
CHANGED
|
@@ -256,7 +256,7 @@ const utils = {
|
|
|
256
256
|
const func = all ? 'selectAll' : 'select';
|
|
257
257
|
uni.createSelectorQuery()
|
|
258
258
|
.in(component)
|
|
259
|
-
|
|
259
|
+
[func](selectors)
|
|
260
260
|
.boundingClientRect(data => {
|
|
261
261
|
resolve(data as ReturnBasedOnBool<T>);
|
|
262
262
|
})
|
|
@@ -576,6 +576,47 @@ const utils = {
|
|
|
576
576
|
const rpx = (px * 750) / windowWidth;
|
|
577
577
|
return rpx;
|
|
578
578
|
},
|
|
579
|
+
/**
|
|
580
|
+
* 倒计时
|
|
581
|
+
* @param {Date|string|number} endTime 倒计时的结束时间
|
|
582
|
+
* @param {Function} callback 回调函数,如果返回false则停止倒计时
|
|
583
|
+
* @returns {NodeJS.Timeout} 定时器ID
|
|
584
|
+
*/
|
|
585
|
+
countDown(endTime: Date | string | number, callback?: (data: { days: number; hours: number; minutes: number; seconds: number }, time: number) => void | boolean) {
|
|
586
|
+
let interval: any;
|
|
587
|
+
const end = new Date(endTime).getTime();
|
|
588
|
+
const now = new Date().getTime();
|
|
589
|
+
const time = Math.floor((end - now) / 1000);
|
|
590
|
+
if (time <= 0) {
|
|
591
|
+
clearInterval(interval);
|
|
592
|
+
if (callback) callback({ days: 0, hours: 0, minutes: 0, seconds: 0, }, 0);
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
const bool = callback ? callback({
|
|
596
|
+
days: Math.floor(time / 86400),
|
|
597
|
+
hours: Math.floor((time / 3600) % 24),
|
|
598
|
+
minutes: Math.floor((time / 60) % 60),
|
|
599
|
+
seconds: Math.floor(time % 60),
|
|
600
|
+
}, time) : true;
|
|
601
|
+
if (bool === false) return clearInterval(interval);
|
|
602
|
+
interval = setInterval(() => {
|
|
603
|
+
const now = new Date().getTime();
|
|
604
|
+
const time = Math.floor((end - now) / 1000);
|
|
605
|
+
if (time <= 0) {
|
|
606
|
+
clearInterval(interval);
|
|
607
|
+
if (callback) callback({ days: 0, hours: 0, minutes: 0, seconds: 0, }, 0);
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
const bool = callback ? callback({
|
|
611
|
+
days: Math.floor(time / 86400),
|
|
612
|
+
hours: Math.floor((time / 3600) % 24),
|
|
613
|
+
minutes: Math.floor((time / 60) % 60),
|
|
614
|
+
seconds: Math.floor(time % 60),
|
|
615
|
+
}, time) : true;
|
|
616
|
+
if (bool === false) clearInterval(interval);
|
|
617
|
+
}, 1000);
|
|
618
|
+
return interval;
|
|
619
|
+
}
|
|
579
620
|
};
|
|
580
621
|
|
|
581
622
|
export default utils;
|