瀏覽代碼

feats: change time ticker component

张延森 5 年之前
父節點
當前提交
a87b6e6df0

+ 3
- 0
.vscode/settings.json 查看文件

1
+{
2
+  "editor.fontFamily": "'Cascadia Code', Consolas, 'Courier New', monospace"
3
+}

+ 106
- 44
src/components/TimeTicker/index.jsx 查看文件

1
 import Taro from '@tarojs/taro'
1
 import Taro from '@tarojs/taro'
2
 import dayjs from 'dayjs'
2
 import dayjs from 'dayjs'
3
-
4
-// 未开始
5
-const STATUS_READY = -1
6
-
7
-// 进行中
8
-const STATUS_PROCESSING = 0
9
-
10
-// 已结束
11
-const STATUS_OVER = 1
3
+import {
4
+  STATUS_READY,
5
+  STATUS_PROCESSING,
6
+  STATUS_OVER,
7
+  getPreText,
8
+  getTkText,
9
+  isFunction,
10
+} from './utils'
12
 
11
 
13
 export default class TimeTicker extends Taro.Component {
12
 export default class TimeTicker extends Taro.Component {
13
+  static options = {
14
+    addGlobalClass: true
15
+  }
14
 
16
 
15
   state = {
17
   state = {
16
     status: STATUS_READY,
18
     status: STATUS_READY,
23
   timer = null
25
   timer = null
24
 
26
 
25
   componentWillMount() {
27
   componentWillMount() {
28
+    this.computeProps(this.props)
29
+  }
26
 
30
 
31
+  componentWillReceiveProps(nextProps) {
32
+    this.computeProps(nextProps)
33
+  }
27
 
34
 
35
+  componentWillUnmount() {
36
+    if (this.timer) {
37
+      clearInterval(this.timer)
38
+    }
28
   }
39
   }
29
 
40
 
30
   computeProps(props) {
41
   computeProps(props) {
44
 
55
 
45
     if (!t1 || !t2) {
56
     if (!t1 || !t2) {
46
       // 只设置了一个时间
57
       // 只设置了一个时间
47
-      targetTime = t1 ? dayjs(t1).valueOf() : dayjs(t2).valueOf()
48
-
49
-      if (now > targetTime) {
50
-        status = STATUS_OVER
51
-      } else if (now === targetTime) {
52
-        status = STATUS_PROCESSING
53
-      } else {
54
-        status = STATUS_READY
55
-      }
58
+      tmEnd = tmStart = t1 ? dayjs(t1).valueOf() : dayjs(t2).valueOf()
56
     } else {
59
     } else {
57
       // 设置了时间区间
60
       // 设置了时间区间
58
-      const dtStart = dayjs(t1).valueOf()
59
-      const dtEnd = dayjs(t2).valueOf()
60
-
61
-      if (dtStart > now) {
62
-        status = STATUS_READY
63
-        targetTime = dtStart
64
-      } else if (dtStart <= now && now < dtEnd) {
65
-        status = STATUS_PROCESSING
66
-        targetTime = dtEnd
67
-      } else {
68
-        status = STATUS_OVER
69
-        targetTime = dtEnd
70
-      }
61
+      tmStart = dayjs(t1).valueOf()
62
+      tmEnd = dayjs(t2).valueOf()
71
     }
63
     }
72
 
64
 
73
-    this.setState({ status, targetTime }, this.startTimer.bind(this))
65
+    if (tmStart > now) {
66
+      status = STATUS_READY
67
+    } else if (tmStart <= now && now < tmEnd) {
68
+      status = STATUS_PROCESSING
69
+    } else {
70
+      status = STATUS_OVER
71
+    }
72
+
73
+    this.setState({ status, tmStart, tmEnd }, () => this.startTimer(props))
74
   }
74
   }
75
 
75
 
76
-  startTimer = (props) => {
76
+  computeStatus(props) {
77
     const {
77
     const {
78
-      status,
79
-      targetTime,
80
-      interval = 1000,
81
       onStart,
78
       onStart,
82
       onProcess,
79
       onProcess,
83
       onEnd,
80
       onEnd,
84
     } = props || {}
81
     } = props || {}
85
 
82
 
86
-    if (this.timer) {
87
-      clearInterval(this.timer)
83
+    let {
84
+      status,
85
+      tmStart,
86
+      tmEnd,
87
+      preText,
88
+      tkText,
89
+    } = this.state
90
+
91
+    const now = dayjs().valueOf()
92
+    const diff = status === STATUS_READY ? tmStart - now : tmEnd - now
93
+
94
+    if (diff <= 0) {
95
+      if (status === STATUS_READY) {
96
+        status = tmStart === tmEnd ? STATUS_OVER : STATUS_PROCESSING
97
+        
98
+        if (isFunction(onStart)) {
99
+          [preText, tkText] = onStart()
100
+        }
101
+      } else {
102
+        status = STATUS_OVER
103
+        if (this.timer) {
104
+          clearInterval(this.timer)
105
+        }
106
+
107
+        if (isFunction(onEnd)) {
108
+          [preText, tkText] = onEnd()
109
+        }
110
+      }
111
+    } else {
112
+      if (isFunction(onProcess)) {
113
+        [preText, tkText] = onProcess(diff, status)
114
+      }
88
     }
115
     }
89
 
116
 
90
-    this.timer = setInterval(() => {
91
-      const now = dayjs().valueOf()
92
-      const diff = targetTime - now
117
+    if (preText === undefined) {
118
+      preText = getPreText(diff, status)
119
+    }
120
+    
121
+    if (tkText === undefined) {
122
+      tkText = getTkText(diff, status)
123
+    }
93
 
124
 
125
+    this.setState({ status, preText, tkText })
126
+  }
94
 
127
 
128
+  startTimer = (props) => {
129
+    const { interval = 1000 } = props || {}
95
 
130
 
96
-    }, interval)
131
+    if (this.timer) {
132
+      clearInterval(this.timer)
133
+    }
97
 
134
 
135
+    // 立即执行一次
136
+    this.computeStatus(props);
137
+    // 再循环执行
138
+    this.timer = setInterval(() => this.computeStatus(props), interval)
98
   }
139
   }
99
 
140
 
100
-  render() {
101
-    
102
 
141
 
103
 
142
 
143
+  classNames = (...args) => {
144
+    return (args || []).reduce((cls, item) => {
145
+      return [].concat(cls).concat(item)
146
+    }, []).filter(x => x).join(' ')
147
+  }
148
+
149
+  render() {
150
+    const wrapperCls = this.classNames('tk-box', this.props.className)
151
+    const headerCls = this.classNames('tk-box-header', this.props.headerClass)
152
+    const bodyCls = this.classNames('tk-box-body', this.props.bodyClass)
153
+
154
+    return (
155
+      <View className={wrapperCls}>
156
+        {
157
+          this.state.preText &&
158
+          (<Text className={headerCls}>{this.state.preText}</Text>)
159
+        }        
160
+        {
161
+          this.state.tkText &&
162
+          (<Text className={bodyCls}>{this.state.tkText}</Text>)
163
+        }        
164
+      </View>
165
+    );
104
   }
166
   }
105
 }
167
 }

+ 23
- 0
src/components/TimeTicker/utils.js 查看文件

1
+
2
+import { formateLeftTime } from '@utils/tools'
3
+
4
+// 未开始
5
+export const STATUS_READY = -1
6
+
7
+// 进行中
8
+export const STATUS_PROCESSING = 0
9
+
10
+// 已结束
11
+export const STATUS_OVER = 1
12
+
13
+export function getPreText(tk, status) {
14
+  return status === STATUS_OVER ? '已结束' : `距离 ${status === STATUS_READY ? '开始' : '结束'} 还有: `
15
+}
16
+
17
+export function getTkText(tk, status) {
18
+  return status === STATUS_OVER ? '' : formateLeftTime(tk)
19
+}
20
+
21
+export function isFunction(fn) {
22
+  return typeof fn === 'function'
23
+}

+ 30
- 75
src/pages/activity/detail/assemble.js 查看文件

6
 import AchieveAvatar from '@components/achieveAvatar'
6
 import AchieveAvatar from '@components/achieveAvatar'
7
 import Poster from '@components/Poster'
7
 import Poster from '@components/Poster'
8
 import FormIdCollector from '@components/formIdCollector'
8
 import FormIdCollector from '@components/formIdCollector'
9
+import TimeTicker from '@components/TimeTicker'
9
 import dayjs from 'dayjs'
10
 import dayjs from 'dayjs'
10
 // import WxParse from '@components/wxParse/wxParse'
11
 // import WxParse from '@components/wxParse/wxParse'
11
 // import getUserPhone from '@utils/getUserPhone'
12
 // import getUserPhone from '@utils/getUserPhone'
56
     shares: [], // 分享设置
57
     shares: [], // 分享设置
57
     posters: [],  // 海报设置
58
     posters: [],  // 海报设置
58
     posterTpls: [], // 海报模板
59
     posterTpls: [], // 海报模板
59
-    leftTime: 0,  // 剩余时间
60
-    ltTicker: undefined,  // 剩余时间计时器
61
     actState: ActBeforeStart,  // 活动本身状态
60
     actState: ActBeforeStart,  // 活动本身状态
62
     groupState: undefined, // 发起拼团活动的状态
61
     groupState: undefined, // 发起拼团活动的状态
63
     isStarter: true, // 是否发起人
62
     isStarter: true, // 是否发起人
80
   }
79
   }
81
 
80
 
82
   componentWillUnmount() {
81
   componentWillUnmount() {
83
-    this.stopTicker()
84
     this.state.pointRecordId && updatePoint(this.state.pointRecordId)
82
     this.state.pointRecordId && updatePoint(this.state.pointRecordId)
85
   }
83
   }
86
 
84
 
87
-  componentDidShow() {
88
-    this.startTicker()
89
-  }
90
-
91
-  componentDidHide() {
92
-    this.stopTicker()
93
-  }
94
-
95
   // 初始化页面数据
85
   // 初始化页面数据
96
   initPageData = () => {
86
   initPageData = () => {
97
     if (!this.state.detail.groupActivityId) {
87
     if (!this.state.detail.groupActivityId) {
110
     }
100
     }
111
   }
101
   }
112
 
102
 
113
-  // 启动 ticker
114
-  startTicker() {
115
-    if (this.state.ltTicker && !this.state.ltTicker.processing) {
116
-      this.state.ltTicker.start()
117
-    }
118
-  }
119
-
120
-  // 清除 ticker
121
-  stopTicker() {
122
-    if (this.state.ltTicker) {
123
-      this.state.ltTicker.stop()
124
-    }
125
-  }
126
-
127
 
103
 
128
    // 调起授权电话
104
    // 调起授权电话
129
    toggleGrantPhone = () => {
105
    toggleGrantPhone = () => {
156
   toggleActionVisible = () => {
132
   toggleActionVisible = () => {
157
     const { actionSheetVisible } = this.state
133
     const { actionSheetVisible } = this.state
158
 
134
 
159
-    if (this.state.ltTicker) {
160
-      actionSheetVisible ? this.startTicker() : this.stopTicker()
161
-    }
162
-
163
     this.setState({
135
     this.setState({
164
       actionSheetVisible: !actionSheetVisible
136
       actionSheetVisible: !actionSheetVisible
165
     })
137
     })
203
     }
175
     }
204
   }
176
   }
205
 
177
 
206
-  // 计时器更新剩余时间
207
-  getLeftTimeTicker(startDate, endDate) {
208
-    let processing = false
209
-    let ticker = undefined
210
-
211
-    const stop = () => {
212
-      ticker && clearInterval(ticker)
213
-      processing = false
214
-    }
215
-
216
-    const fn = () => {
217
-      const [actState, leftTime] = this.compActState(startDate, endDate)
218
-      this.setState({ actState, leftTime })
219
-
220
-      if (actState === ActFinished) {
221
-        stop()
222
-      }
223
-    }
224
-
225
-    const start = () => {
226
-      ticker = setInterval(fn, 1000)
227
-      processing = true
228
-    }
229
-
230
-    return {
231
-      start,
232
-      stop,
233
-      processing,
234
-    }
235
-  }
236
-
178
+  // 请求详情
237
   loadDetail() {
179
   loadDetail() {
238
     let { id, recordId, ltTicker } = this.state
180
     let { id, recordId, ltTicker } = this.state
239
     const { userInfo } = this.props
181
     const { userInfo } = this.props
240
 
182
 
241
     Taro.showLoading()
183
     Taro.showLoading()
242
     getGroupDetail(id, recordId).then(res => {
184
     getGroupDetail(id, recordId).then(res => {
243
-      this.stopTicker()
244
 
185
 
245
       const recordDetail = res.taShareRecord || {}
186
       const recordDetail = res.taShareRecord || {}
246
       let [actState, leftTime] = this.compActState(res.taShareActivity.startTime, res.taShareActivity.endTime)
187
       let [actState, leftTime] = this.compActState(res.taShareActivity.startTime, res.taShareActivity.endTime)
249
         actState = ActFinished
190
         actState = ActFinished
250
       }
191
       }
251
 
192
 
252
-      if (actState != ActFinished) {
253
-        ltTicker = this.getLeftTimeTicker(res.taShareActivity.startTime, res.taShareActivity.endTime)
254
-      }
255
-
256
       Taro.hideLoading()
193
       Taro.hideLoading()
257
       this.setState({
194
       this.setState({
258
         detail: res.taShareActivity,
195
         detail: res.taShareActivity,
266
         loaded: true,
203
         loaded: true,
267
         isStarter: userInfo.person.personId === recordDetail.personId,
204
         isStarter: userInfo.person.personId === recordDetail.personId,
268
         actState,
205
         actState,
269
-        leftTime,
270
-        ltTicker
271
-      }, () => this.startTicker())
206
+      })
272
 
207
 
273
       // Taro.setNavigationBarTitle({ title: res.taShareActivity.activityName })
208
       // Taro.setNavigationBarTitle({ title: res.taShareActivity.activityName })
274
 
209
 
473
     })
408
     })
474
   }
409
   }
475
 
410
 
411
+  handleTickerProcess = (status) => (tk, st) => {
412
+    if (this.state.actState === ActFinished) {
413
+      return ['活动已结束', null]
414
+    }
415
+
416
+    switch (status) {
417
+      case 'start':
418
+        this.setState({ actState: ActInProcess })
419
+        return ['活动马上开始', null];
420
+      case 'process':
421
+        return [(st === -1 ? '距活动开始' : '活动剩余时间')];
422
+      case 'end':
423
+        this.setState({ actState: ActFinished })
424
+        return ['活动已结束', null]
425
+    }
426
+  }
476
 
427
 
477
   render() {
428
   render() {
478
     const {
429
     const {
491
       posterTpls,
442
       posterTpls,
492
       posters,
443
       posters,
493
       qrCode,
444
       qrCode,
494
-      leftTime,
495
-      ltTicker,
496
     } = this.state
445
     } = this.state
497
 
446
 
498
     const { userInfo } = this.props
447
     const { userInfo } = this.props
523
     return (
472
     return (
524
       <Block>
473
       <Block>
525
         {/* 生成海报 */}
474
         {/* 生成海报 */}
526
-        {posterVisible && !ltTicker.processing &&
475
+        {posterVisible &&
527
           (
476
           (
528
             <Poster configs={posterConfigs} onCancel={this.togglePosterVisible} onFinish={this.togglePosterVisible}></Poster>
477
             <Poster configs={posterConfigs} onCancel={this.togglePosterVisible} onFinish={this.togglePosterVisible}></Poster>
529
           )
478
           )
545
               <View className="detail-banner">
494
               <View className="detail-banner">
546
                 <Image mode="widthFix" src={transferImage(detail.mainImg)} className="detail-banner__img"></Image>
495
                 <Image mode="widthFix" src={transferImage(detail.mainImg)} className="detail-banner__img"></Image>
547
 
496
 
548
-                <View className="rest-time">
549
-                  <Text className="row-label">{actState === ActBeforeStart ? '距活动开始 :' : (actState === ActInProcess ? '活动剩余时间 :' : '')} </Text>
550
-                  <Text className="row-text">{actState != ActFinished ? formateLeftTime(leftTime) : '活动已结束'}</Text>
551
-                </View>
497
+                <TimeTicker
498
+                  className="rest-time"
499
+                  headerClass="row-label"
500
+                  bodyClass="row-text"
501
+                  timeRange={[detail.startTime, detail.endTime]}
502
+                  onStart={this.handleTickerProcess('start')}
503
+                  onEnd={this.handleTickerProcess('end')}
504
+                  onProcess={this.handleTickerProcess('process')}
505
+                />
506
+
552
                 {detail.successNum > 0 &&
507
                 {detail.successNum > 0 &&
553
                   <View className="success-num">
508
                   <View className="success-num">
554
                     <View className="triangle"></View>
509
                     <View className="triangle"></View>

+ 33
- 80
src/pages/activity/detail/assistance.js 查看文件

6
 import AchieveAvatar from '@components/achieveAvatar'
6
 import AchieveAvatar from '@components/achieveAvatar'
7
 import Poster from '@components/Poster'
7
 import Poster from '@components/Poster'
8
 import FormIdCollector from '@components/formIdCollector'
8
 import FormIdCollector from '@components/formIdCollector'
9
+import TimeTicker from '@components/TimeTicker'
9
 import dayjs from 'dayjs'
10
 import dayjs from 'dayjs'
10
 // import WxParse from '@components/wxParse/wxParse'
11
 // import WxParse from '@components/wxParse/wxParse'
11
 // import getUserPhone from '@utils/getUserPhone'
12
 // import getUserPhone from '@utils/getUserPhone'
54
     shares: [], // 分享设置
55
     shares: [], // 分享设置
55
     posters: [],  // 海报设置
56
     posters: [],  // 海报设置
56
     posterTpls: [], // 海报模板
57
     posterTpls: [], // 海报模板
57
-    leftTime: 0,  // 剩余时间
58
-    ltTicker: undefined,  // 剩余时间计时器
59
     actState: ActBeforeStart,  // 活动本身状态
58
     actState: ActBeforeStart,  // 活动本身状态
60
     helpState: HelpInProcess, // 发起助力活动的状态
59
     helpState: HelpInProcess, // 发起助力活动的状态
61
     isStarter: true, // 是否发起人
60
     isStarter: true, // 是否发起人
80
   }
79
   }
81
 
80
 
82
   componentWillUnmount() {
81
   componentWillUnmount() {
83
-    this.stopTicker()
84
     this.state.pointRecordId && updatePoint(this.state.pointRecordId)
82
     this.state.pointRecordId && updatePoint(this.state.pointRecordId)
85
   }
83
   }
86
 
84
 
87
-  componentDidShow() {
88
-    this.startTicker()
89
-  }
90
-
91
-  componentDidHide() {
92
-    this.stopTicker()
93
-  }
94
-
95
   // 初始化页面数据
85
   // 初始化页面数据
96
   initPageData = () => {
86
   initPageData = () => {
97
     if (!this.state.detail.helpActivityId) {
87
     if (!this.state.detail.helpActivityId) {
110
     }
100
     }
111
   }
101
   }
112
 
102
 
113
-  // 启动 ticker
114
-  startTicker() {
115
-    if (this.state.ltTicker && !this.state.ltTicker.processing) {
116
-      this.state.ltTicker.start()
117
-    }
118
-  }
119
-
120
-  // 清除 ticker
121
-  stopTicker() {
122
-    if (this.state.ltTicker) {
123
-      this.state.ltTicker.stop()
124
-    }
125
-  }
126
-
127
   // 调起授权电话
103
   // 调起授权电话
128
   toggleGrantPhone = () => {
104
   toggleGrantPhone = () => {
129
     const { userInfo: { person: { phone, tel } } } = this.props
105
     const { userInfo: { person: { phone, tel } } } = this.props
155
   toggleActionVisible = () => {
131
   toggleActionVisible = () => {
156
     const { actionSheetVisible } = this.state
132
     const { actionSheetVisible } = this.state
157
 
133
 
158
-    if (this.state.ltTicker) {
159
-      actionSheetVisible ? this.startTicker() : this.stopTicker()
160
-    }
161
-
162
     this.setState({
134
     this.setState({
163
       actionSheetVisible: !actionSheetVisible
135
       actionSheetVisible: !actionSheetVisible
164
     })
136
     })
202
     }
174
     }
203
   }
175
   }
204
 
176
 
205
-  // 计时器更新剩余时间
206
-  getLeftTimeTicker(startDate, endDate) {
207
-    let processing = false
208
-    let ticker = undefined
209
-
210
-    const stop = () => {
211
-      ticker && clearInterval(ticker)
212
-      processing = false
213
-    }
214
-
215
-    const fn = () => {
216
-      const [actState, leftTime] = this.compActState(startDate, endDate)
217
-      this.setState({ actState, leftTime })
218
-
219
-      if (actState === ActFinished) {
220
-        stop()
221
-      }
222
-    }
223
-
224
-    const start = () => {
225
-      ticker = setInterval(fn, 1000)
226
-      processing = true
227
-    }
228
-
229
-    return {
230
-      start,
231
-      stop,
232
-      processing,
233
-    }
234
-  }
235
-
236
   // 请求详情
177
   // 请求详情
237
   loadDetail() {
178
   loadDetail() {
238
-    let { id, initiateId, ltTicker } = this.state
179
+    let { id, initiateId } = this.state
239
     const { userInfo } = this.props
180
     const { userInfo } = this.props
240
 
181
 
241
     Taro.showLoading()
182
     Taro.showLoading()
242
     getHelpDetail(id, initiateId).then(res => {
183
     getHelpDetail(id, initiateId).then(res => {
243
-      this.stopTicker()
244
-
245
       const initiateDetail = res.helpInitiateRecord || {}
184
       const initiateDetail = res.helpInitiateRecord || {}
246
       let [actState, leftTime] = this.compActState(res.helpActivity.startDate, res.helpActivity.endDate)
185
       let [actState, leftTime] = this.compActState(res.helpActivity.startDate, res.helpActivity.endDate)
247
 
186
 
249
         actState = ActFinished
188
         actState = ActFinished
250
       }
189
       }
251
 
190
 
252
-      if (actState != ActFinished) {
253
-        ltTicker = this.getLeftTimeTicker(res.helpActivity.startDate, res.helpActivity.endDate)
254
-      }
255
-
256
       Taro.hideLoading()
191
       Taro.hideLoading()
257
       this.setState({
192
       this.setState({
258
         detail: res.helpActivity,
193
         detail: res.helpActivity,
266
         loaded: true,
201
         loaded: true,
267
         isStarter: !initiateDetail.personId || userInfo.person.personId === initiateDetail.personId,
202
         isStarter: !initiateDetail.personId || userInfo.person.personId === initiateDetail.personId,
268
         actState,
203
         actState,
269
-        leftTime,
204
+        // leftTime,
270
         isJoin: res.isJoin,
205
         isJoin: res.isJoin,
271
-        ltTicker
272
-      }, () => this.startTicker())
273
-
274
-      // Taro.setNavigationBarTitle({ title: res.helpActivity.title })
275
-
206
+      })
207
+      
276
       // WxParse.wxParse('article', 'html', res.desc, this.$scope, 0)
208
       // WxParse.wxParse('article', 'html', res.desc, this.$scope, 0)
277
 
209
 
278
       savePoint({
210
       savePoint({
465
     })
397
     })
466
   }
398
   }
467
 
399
 
400
+  handleTickerProcess = (status) => (tk, st) => {
401
+    if (this.state.actState === ActFinished) {
402
+      return ['活动已结束', null]
403
+    }
404
+
405
+    switch (status) {
406
+      case 'start':
407
+        this.setState({ actState: ActInProcess })
408
+        return ['活动马上开始', null];
409
+      case 'process':
410
+        return [(st === -1 ? '距活动开始' : '活动剩余时间')];
411
+      case 'end':
412
+        this.setState({ actState: ActFinished })
413
+        return ['活动已结束', null]
414
+    }
415
+  }
416
+
468
   render() {
417
   render() {
469
     const {
418
     const {
470
       initiateId,
419
       initiateId,
482
       posterTpls,
431
       posterTpls,
483
       posters,
432
       posters,
484
       qrCode,
433
       qrCode,
485
-      leftTime,
486
-      ltTicker,
487
     } = this.state
434
     } = this.state
488
 
435
 
489
     const { userInfo } = this.props
436
     const { userInfo } = this.props
509
     return (
456
     return (
510
       <View>
457
       <View>
511
         {/* 生成海报 */}
458
         {/* 生成海报 */}
512
-        {posterVisible && !ltTicker.processing &&
459
+        {posterVisible &&
513
           (
460
           (
514
             <Poster configs={posterConfigs} onCancel={this.togglePosterVisible} onFinish={this.togglePosterVisible}></Poster>
461
             <Poster configs={posterConfigs} onCancel={this.togglePosterVisible} onFinish={this.togglePosterVisible}></Poster>
515
           )
462
           )
530
               <View className="detail-banner">
477
               <View className="detail-banner">
531
                 <Image mode="aspectFill" src={transferImage(detail.img)} className="detail-banner__img"></Image>
478
                 <Image mode="aspectFill" src={transferImage(detail.img)} className="detail-banner__img"></Image>
532
 
479
 
533
-                <View className="rest-time">
534
-                  <Text className="row-label">{actState === ActBeforeStart ? '距活动开始 :' : (actState === ActInProcess ? '活动剩余时间 :' : '')} </Text>
535
-                  <Text className="row-text">{actState != ActFinished ? formateLeftTime(leftTime) : '活动已结束'}</Text>
536
-                </View>
480
+                <TimeTicker
481
+                  className="rest-time"
482
+                  headerClass="row-label"
483
+                  bodyClass="row-text"
484
+                  timeRange={[detail.startDate, detail.endDate]}
485
+                  onStart={this.handleTickerProcess('start')}
486
+                  onEnd={this.handleTickerProcess('end')}
487
+                  onProcess={this.handleTickerProcess('process')}
488
+                />
489
+
537
                 {detail.successNum > 0 &&
490
                 {detail.successNum > 0 &&
538
 
491
 
539
                   <View className="success-num">
492
                   <View className="success-num">