stellar-ui-v2 1.40.0 → 1.40.1

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.
@@ -0,0 +1,185 @@
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
+ | ----- | ----- | --- | ------- | ------ | -------- |
161
+ | `data` | 基础数据 | `{title: string;desc?: string;image: string;price: string / number;constraint?: string;footers?: string[]}` | `{}` | - | - |
162
+ | `endTime` | 结束时间 | `string / number / Date` | `` | - | - |
163
+ | `residue` | 剩余数量 | `number` | `-1` | - | - |
164
+ | `progress` | 进度条百分比 | `string` | `` | - | - |
165
+ | `buttonText` | 按钮文字 | `string` | `购买` | - | - |
166
+ | `buttonDisabled` | 禁用按钮 | `boolean` | `false` | - | - |
167
+ | `backgroundColor` | 背景颜色 | `string` | `#fff` | - | - |
168
+
169
+
170
+ #### Events
171
+ | 事件名 | 说明 | 事件参数 | 支持版本 |
172
+ | ----- | ----- | ------- | -------- |
173
+ | `buttonClick` | 点击按钮时触发 | - | - |
174
+ | `footerClick` | 点击页脚列表时触发 | - | - |
175
+ | `countDown` | 倒计时触发 | - | - |
176
+
177
+
178
+ #### Slot
179
+
180
+ | 插槽名称 | 说明 | 支持版本 |
181
+ | ---------- | ------------------ | -------- |
182
+ | `position` | 在任意位置插入内容 | - |
183
+
184
+ ---$
185
+ {{xuyajun}}
@@ -0,0 +1,5 @@
1
+ {
2
+ "group": "业务组件",
3
+ "title": "CouponList 券列表",
4
+ "icon": "https://image.whzb.com/chain/StellarUI/%E7%BB%84%E4%BB%B6%E5%9B%BE%E6%A0%87/button.png"
5
+ }
@@ -0,0 +1,390 @@
1
+ <script>
2
+ import utils from '../../utils/utils.js';
3
+
4
+ let interval = 0;
5
+
6
+ export default {
7
+ props: {
8
+ /** 基础数据 */
9
+ data: { type: Object, default: () => ({}) },
10
+ /** 结束时间 倒计时时间 */
11
+ endTime: { type: [Date, String, Number], default: () => null },
12
+ /** 剩余数量 */
13
+ residue: { type: Number, default: () => -1 },
14
+ /** 进度条百分比 */
15
+ progress: { type: Number, default: () => -1 },
16
+ progressText: { type: String, default: () => '' },
17
+ /** 按钮文字 */
18
+ buttonText: { type: String, default: () => '购买' },
19
+ buttonDisabled: { type: Boolean, default: () => false },
20
+ backgroundColor: { type: String, default: () => '#fff' },
21
+ },
22
+ computed: {
23
+ rootStyle() {
24
+ return { '--ste-coupon-lis-background': this.backgroundColor };
25
+ },
26
+ },
27
+ data() {
28
+ return {
29
+ countDown: { days: 0, hours: 0, minutes: 0, seconds: 0 },
30
+ canvasId: utils.guid(),
31
+ // #ifdef MP-WEIXIN
32
+ base64Progress: '',
33
+ // #endif
34
+ };
35
+ },
36
+ mounted() {
37
+ if (this.endTime) {
38
+ interval = utils.countDown(this.endTime, (data, time) => {
39
+ this.countDown = data;
40
+ this.$emit('countDown', time);
41
+ });
42
+ }
43
+ // 当组件挂载时绘制进度条
44
+ this.drawProgress();
45
+ },
46
+ beforeDestroy() {
47
+ clearInterval(interval);
48
+ },
49
+ methods: {
50
+ viewTime(time) {
51
+ return time < 10 ? '0' + time : time;
52
+ },
53
+ drawProgress() {
54
+ if (this.progress < 0) return;
55
+ setTimeout(() => {
56
+ // #ifdef H5
57
+ this.drawProgressH5();
58
+ // #endif
59
+ // #ifdef MP-ALIPAY || APP
60
+ this.drawProgressApp();
61
+ // #endif
62
+ // #ifdef MP-WEIXIN
63
+ this.drawProgressWx();
64
+ // #endif
65
+ });
66
+ },
67
+ // #ifdef MP-WEIXIN
68
+ drawProgressWx() {
69
+ // 创建离屏canvas
70
+ const canvas = wx.createOffscreenCanvas({ type: '2d', width: 96, height: 48 });
71
+
72
+ const ctx = canvas.getContext('2d');
73
+ if (!ctx) return;
74
+
75
+ // 清空画布
76
+ ctx.clearRect(0, 0, 96, 48);
77
+
78
+ // 设置半圆进度条参数
79
+ const centerX = 48;
80
+ const centerY = 48;
81
+ const radius = 42; // 留出边距
82
+
83
+ // 绘制背景圆弧(灰色)
84
+ ctx.beginPath();
85
+ ctx.arc(centerX, centerY, radius, Math.PI, 0, false);
86
+ ctx.lineWidth = 8;
87
+ ctx.strokeStyle = '#F3F3F3';
88
+ ctx.lineCap = 'round';
89
+ ctx.stroke();
90
+
91
+ // 绘制进度圆弧
92
+ if (this.progress > 0) {
93
+ const progressAngle = Math.PI * (this.progress / 100);
94
+ ctx.beginPath();
95
+ ctx.arc(centerX, centerY, radius, Math.PI, Math.PI + progressAngle, false);
96
+ ctx.strokeStyle = '#FF283A';
97
+ ctx.lineCap = 'round';
98
+ ctx.stroke();
99
+ }
100
+
101
+ // 转换为base64
102
+ try {
103
+ const dataURL = canvas.toDataURL('image/png');
104
+ // 在实际应用中,你可能需要将这个dataURL设置到某个变量中供模板使用
105
+ // 例如: progressImage.value = dataURL;
106
+ this.base64Progress = dataURL;
107
+ } catch (error) {
108
+ console.error('Failed to convert canvas to base64:', error);
109
+ }
110
+ },
111
+ // #endif
112
+ // #ifdef MP-ALIPAY || APP
113
+ drawProgressApp() {
114
+ // 使用uni.createCanvasContext创建绘图上下文
115
+ const ctx: UniApp.CanvasContext = uni.createCanvasContext(this.canvasId, this);
116
+
117
+ // 设置canvas尺寸 - 根据样式定义的96rpx*48rpx
118
+ const canvasWidth = utils.formatPx(96, 'num');
119
+ const canvasHeight = utils.formatPx(48, 'num');
120
+
121
+ // 清空画布
122
+ ctx.clearRect(0, 0, canvasWidth, canvasHeight);
123
+
124
+ // 设置半圆进度条参数
125
+ const centerX = canvasWidth / 2;
126
+ const centerY = canvasHeight;
127
+ const radius = canvasWidth / 2 - utils.formatPx(6, 'num'); // 留出边距
128
+
129
+ // 绘制背景圆弧(灰色)
130
+ ctx.beginPath();
131
+ ctx.arc(centerX, centerY, radius, Math.PI, 0, false);
132
+ ctx.setLineWidth(utils.formatPx(8, 'num'));
133
+ ctx.setStrokeStyle('#F3F3F3');
134
+ ctx.setLineCap('round');
135
+ ctx.stroke();
136
+
137
+ // 绘制进度圆弧
138
+ if (this.progress > 0) {
139
+ const progressAngle = Math.PI * (this.progress / 100);
140
+ ctx.beginPath();
141
+ ctx.arc(centerX, centerY, radius, Math.PI, Math.PI + progressAngle, false);
142
+ ctx.setStrokeStyle('#FF283A');
143
+ ctx.setLineCap('round');
144
+ ctx.stroke();
145
+ }
146
+
147
+ // 绘制到canvas
148
+ ctx.draw(false);
149
+ },
150
+ // #endif
151
+ // #ifdef H5
152
+ drawProgressH5() {
153
+ const query = uni.createSelectorQuery();
154
+
155
+ query
156
+ .select('#' + this.canvasId)
157
+ .fields({ node: true, size: true }, (res) => {})
158
+ .exec((res) => {
159
+ if (!res || !res[0]) return;
160
+ const canvas = res[0].node;
161
+
162
+ if (!canvas) return;
163
+ const ctx = canvas.getContext('2d');
164
+ if (!ctx) return;
165
+ // 清空画布
166
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
167
+ // 设置半圆进度条参数
168
+ const centerX = res[0].width / 2;
169
+ const centerY = res[0].height;
170
+ const radius = res[0].width / 2 - utils.formatPx(6, 'num'); // 留出边距
171
+
172
+ // 绘制背景圆弧(灰色)
173
+ ctx.beginPath();
174
+ ctx.arc(centerX, centerY, radius, Math.PI, 0, false);
175
+ ctx.lineWidth = utils.formatPx(8, 'num');
176
+ ctx.strokeStyle = '#F3F3F3';
177
+ ctx.lineCap = 'round';
178
+ ctx.stroke();
179
+
180
+ // 绘制进度圆弧
181
+ if (this.progress > 0) {
182
+ const progressAngle = Math.PI * (this.progress / 100);
183
+ ctx.beginPath();
184
+ ctx.arc(centerX, centerY, radius, Math.PI, Math.PI + progressAngle, false);
185
+ ctx.strokeStyle = '#FF283A';
186
+ ctx.lineCap = 'round';
187
+ ctx.stroke();
188
+ }
189
+ });
190
+ },
191
+ // #endif
192
+ },
193
+ };
194
+ </script>
195
+
196
+ <template>
197
+ <view v-if="data" class="ste-coupon-list--root" :style="rootStyle">
198
+ <view class="coupon-info-block">
199
+ <ste-image width="144" height="144" :src="data.image" radius="12" />
200
+ <view class="coupon-info">
201
+ <view class="info-title">{{ data.title }}</view>
202
+ <view class="info-desc">
203
+ <view v-if="endTime" class="coun-down">
204
+ <view class="coun-down-view">{{ viewTime(countDown.hours) }}</view>
205
+ <view class="coun-down-dot">:</view>
206
+ <view class="coun-down-view">{{ viewTime(countDown.minutes) }}</view>
207
+ <view class="coun-down-dot">:</view>
208
+ <view class="coun-down-view">{{ viewTime(countDown.seconds) }}</view>
209
+ <view class="coun-down-msg">后截止</view>
210
+ </view>
211
+ <view v-else>
212
+ {{ data.desc }}
213
+ </view>
214
+ </view>
215
+ <view class="info-price">
216
+ <view class="price-left">
217
+ <view class="price-text">
218
+ <ste-price :value="data.price" font-size="40" />
219
+ </view>
220
+ <text class="info-constraint">{{ data.constraint }}</text>
221
+ </view>
222
+ <view class="price-right">
223
+ <ste-button :disabled="buttonDisabled" :mode="100" @click="emits('buttonClick')">{{ buttonText }}</ste-button>
224
+ </view>
225
+ </view>
226
+ </view>
227
+ <view class="circle-progress-canvas" v-if="progress >= 0">
228
+ <!-- #ifndef MP-WEIXIN -->
229
+ <canvas :canvas-id="canvasId" :id="canvasId" class="canvas-element" />
230
+ <!-- #endif -->
231
+ <!-- #ifdef MP-WEIXIN -->
232
+ <image :src="base64Progress" class="canvas-element" />
233
+ <!-- #endif -->
234
+
235
+ <view class="progress-text">{{ progressText }}</view>
236
+ </view>
237
+ <slot name="position"></slot>
238
+ </view>
239
+ <view v-if="data.footers && data.footers.length" class="coupon-footer-block">
240
+ <view class="coupon-footer-item" v-for="(value, i) in data.footers" :key="i">
241
+ <view class="footer-item-text">
242
+ {{ value }}
243
+ </view>
244
+ <view class="footer-item-icon" @click="emits('footerClick', i)">
245
+ <ste-icon code="&#xe674;" size="12" color="#A4A4A4" />
246
+ </view>
247
+ </view>
248
+ </view>
249
+ </view>
250
+ </template>
251
+
252
+ <style lang="scss" scoped>
253
+ .ste-coupon-list--root {
254
+ width: 100%;
255
+
256
+ .coupon-info-block {
257
+ width: 100%;
258
+ height: 180rpx;
259
+ padding: 18rpx 18rpx 14rpx 18rpx;
260
+ display: flex;
261
+ align-items: flex-start;
262
+ justify-content: space-between;
263
+ background-color: var(--ste-coupon-lis-background);
264
+ border-radius: 16rpx;
265
+ position: relative;
266
+
267
+ .coupon-info {
268
+ width: calc(100% - 162rpx);
269
+ height: 100%;
270
+ .info-title {
271
+ font-weight: bold;
272
+ font-size: 28rpx;
273
+ line-height: 40rpx;
274
+ color: #000000;
275
+ }
276
+ .info-desc {
277
+ height: 36rpx;
278
+ font-size: 22rpx;
279
+ color: #727272;
280
+ line-height: 32rpx;
281
+ margin-top: 8rpx;
282
+ .coun-down {
283
+ display: flex;
284
+ align-items: center;
285
+ font-weight: 400;
286
+ font-size: 20rpx;
287
+ color: #ff283a;
288
+ line-height: 28rpx;
289
+ text-align: center;
290
+ .coun-down-view {
291
+ height: 100%;
292
+ width: 28rpx;
293
+ height: 28rpx;
294
+ background: #fef4f1;
295
+ border-radius: 4rpx;
296
+ }
297
+ .coun-down-dot {
298
+ margin: 0 8rpx;
299
+ }
300
+ .coun-down-msg {
301
+ font-weight: 400;
302
+ font-size: 20rpx;
303
+ color: #a4a4a4;
304
+ line-height: 28rpx;
305
+ margin-left: 12rpx;
306
+ }
307
+ }
308
+ }
309
+ .info-price {
310
+ margin-top: 12rpx;
311
+ width: 100%;
312
+ height: 56rpx;
313
+ display: flex;
314
+ align-items: flex-start;
315
+ justify-content: space-between;
316
+ .price-left {
317
+ height: 100%;
318
+ display: flex;
319
+ align-items: flex-end;
320
+ .price-text {
321
+ height: 100%;
322
+ display: flex;
323
+ align-items: center;
324
+ }
325
+ .info-constraint {
326
+ font-size: 20rpx;
327
+ color: #363636;
328
+ line-height: 42rpx;
329
+ margin-left: 16rpx;
330
+ }
331
+ }
332
+ }
333
+ }
334
+ .circle-progress-canvas {
335
+ width: 96rpx;
336
+ height: 48rpx;
337
+ position: absolute;
338
+ top: 24rpx;
339
+ right: 26rpx;
340
+ .canvas-element {
341
+ width: 100%;
342
+ height: 100%;
343
+ }
344
+ .progress-text {
345
+ width: 100%;
346
+ text-align: center;
347
+ position: absolute;
348
+ bottom: -6rpx;
349
+ font-size: 16rpx;
350
+ color: #a4a4a4;
351
+ line-height: 22rpx;
352
+ }
353
+ }
354
+ }
355
+
356
+ .coupon-footer-block {
357
+ width: 100%;
358
+ margin-top: 2px;
359
+ background-color: var(--ste-coupon-lis-background);
360
+ border-radius: 16rpx;
361
+ padding: 14rpx 18rpx;
362
+ .coupon-footer-item {
363
+ width: 100%;
364
+ display: flex;
365
+ justify-content: space-between;
366
+ align-items: center;
367
+ .footer-item-text {
368
+ // 不换行
369
+ width: calc(100% - 54rpx);
370
+ white-space: nowrap;
371
+ text-overflow: ellipsis;
372
+ overflow: hidden;
373
+ font-weight: 400;
374
+ font-size: 24rpx;
375
+ color: #a4a4a4;
376
+ line-height: 34rpx;
377
+ }
378
+ .footer-item-icon {
379
+ width: 26rpx;
380
+ height: 26rpx;
381
+ display: flex;
382
+ align-items: center;
383
+ justify-content: center;
384
+ background: #f3f3f3;
385
+ border-radius: 50%;
386
+ }
387
+ }
388
+ }
389
+ }
390
+ </style>
package/package.json CHANGED
@@ -1,18 +1,18 @@
1
- {
2
- "name": "stellar-ui-v2",
3
- "version": "1.40.0",
4
- "description": "StellarUI组件库",
5
- "main": "stellar-ui/index.js",
6
- "private": false,
7
- "scripts": {},
8
- "repository": {
9
- "type": "git",
10
- "url": "git+https://github.com/wuhanshuzhiyun/stellar-ui.git"
11
- },
12
- "keywords": [],
13
- "author": "",
14
- "license": "MIT",
15
- "dependencies": {
16
- "g": "^2.0.1"
17
- }
18
- }
1
+ {
2
+ "name": "stellar-ui-v2",
3
+ "version": "1.40.1",
4
+ "description": "StellarUI组件库",
5
+ "main": "stellar-ui/index.js",
6
+ "private": false,
7
+ "scripts": {},
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/wuhanshuzhiyun/stellar-ui.git"
11
+ },
12
+ "keywords": [],
13
+ "author": "",
14
+ "license": "MIT",
15
+ "dependencies": {
16
+ "g": "^2.0.1"
17
+ }
18
+ }
package/utils/utils.js CHANGED
@@ -174,7 +174,8 @@ let utils = {
174
174
  resolve(result);
175
175
  }
176
176
  // #endif
177
- uni.createSelectorQuery()
177
+ uni
178
+ .createSelectorQuery()
178
179
  .in(component)
179
180
  [selectFn](selectors)
180
181
  .boundingClientRect((data) => {
@@ -621,6 +622,57 @@ let utils = {
621
622
  // #endif
622
623
  });
623
624
  },
625
+ /**
626
+ * 倒计时
627
+ * @param {Date|string|number} endTime 倒计时的结束时间
628
+ * @param {Function} callback 回调函数,如果返回false则停止倒计时
629
+ * @returns {NodeJS.Timeout} 定时器ID
630
+ */
631
+ countDown(endTime, callback) {
632
+ let interval = 0;
633
+ const end = new Date(endTime).getTime();
634
+ const now = new Date().getTime();
635
+ const time = Math.floor((end - now) / 1000);
636
+ if (time <= 0) {
637
+ clearInterval(interval);
638
+ if (callback) callback({ days: 0, hours: 0, minutes: 0, seconds: 0 }, 0);
639
+ return;
640
+ }
641
+ const bool = callback
642
+ ? callback(
643
+ {
644
+ days: Math.floor(time / 86400),
645
+ hours: Math.floor((time / 3600) % 24),
646
+ minutes: Math.floor((time / 60) % 60),
647
+ seconds: Math.floor(time % 60),
648
+ },
649
+ time
650
+ )
651
+ : true;
652
+ if (bool === false) return clearInterval(interval);
653
+ interval = setInterval(() => {
654
+ const now = new Date().getTime();
655
+ const time = Math.floor((end - now) / 1000);
656
+ if (time <= 0) {
657
+ clearInterval(interval);
658
+ if (callback) callback({ days: 0, hours: 0, minutes: 0, seconds: 0 }, 0);
659
+ return;
660
+ }
661
+ const bool = callback
662
+ ? callback(
663
+ {
664
+ days: Math.floor(time / 86400),
665
+ hours: Math.floor((time / 3600) % 24),
666
+ minutes: Math.floor((time / 60) % 60),
667
+ seconds: Math.floor(time % 60),
668
+ },
669
+ time
670
+ )
671
+ : true;
672
+ if (bool === false) clearInterval(interval);
673
+ }, 1000);
674
+ return interval;
675
+ },
624
676
  };
625
677
 
626
678
  export default utils;