You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

278 lines
6.6 KiB

3 weeks ago
  1. <template>
  2. <view class="uni-countdown">
  3. <text v-if="showDay" :style="[timeStyle]" class="uni-countdown__number">{{ d }}</text>
  4. <text v-if="showDay" :style="[splitorStyle]" class="uni-countdown__splitor">{{dayText}}</text>
  5. <text v-if="showHour" :style="[timeStyle]" class="uni-countdown__number">{{ h }}</text>
  6. <text v-if="showHour" :style="[splitorStyle]" class="uni-countdown__splitor">{{ showColon ? ':' : hourText }}</text>
  7. <text v-if="showMinute" :style="[timeStyle]" class="uni-countdown__number">{{ i }}</text>
  8. <text v-if="showMinute" :style="[splitorStyle]" class="uni-countdown__splitor">{{ showColon ? ':' : minuteText }}</text>
  9. <text :style="[timeStyle]" class="uni-countdown__number">{{ s }}</text>
  10. <text v-if="!showColon" :style="[splitorStyle]" class="uni-countdown__splitor">{{secondText}}</text>
  11. </view>
  12. </template>
  13. <script>
  14. import {
  15. initVueI18n
  16. } from '@dcloudio/uni-i18n'
  17. import messages from './i18n/index.js'
  18. const {
  19. t
  20. } = initVueI18n(messages)
  21. /**
  22. * Countdown 倒计时
  23. * @description 倒计时组件
  24. * @tutorial https://ext.dcloud.net.cn/plugin?id=25
  25. * @property {String} backgroundColor 背景色
  26. * @property {String} color 文字颜色
  27. * @property {Number} day 天数
  28. * @property {Number} hour 小时
  29. * @property {Number} minute 分钟
  30. * @property {Number} second
  31. * @property {Number} timestamp 时间戳
  32. * @property {Boolean} showDay = [true|false] 是否显示天数
  33. * @property {Boolean} showHour = [true|false] 是否显示小时
  34. * @property {Boolean} showMinute = [true|false] 是否显示分钟
  35. * @property {Boolean} show-colon = [true|false] 是否以冒号为分隔符
  36. * @property {String} splitorColor 分割符号颜色
  37. * @event {Function} timeup 倒计时时间到触发事件
  38. * @example <uni-countdown :day="1" :hour="1" :minute="12" :second="40"></uni-countdown>
  39. */
  40. export default {
  41. name: 'UniCountdown',
  42. emits: ['timeup'],
  43. props: {
  44. showDay: {
  45. type: Boolean,
  46. default: true
  47. },
  48. showHour: {
  49. type: Boolean,
  50. default: true
  51. },
  52. showMinute: {
  53. type: Boolean,
  54. default: true
  55. },
  56. showColon: {
  57. type: Boolean,
  58. default: true
  59. },
  60. start: {
  61. type: Boolean,
  62. default: true
  63. },
  64. backgroundColor: {
  65. type: String,
  66. default: ''
  67. },
  68. color: {
  69. type: String,
  70. default: '#333'
  71. },
  72. fontSize: {
  73. type: Number,
  74. default: 14
  75. },
  76. splitorColor: {
  77. type: String,
  78. default: '#333'
  79. },
  80. day: {
  81. type: Number,
  82. default: 0
  83. },
  84. hour: {
  85. type: Number,
  86. default: 0
  87. },
  88. minute: {
  89. type: Number,
  90. default: 0
  91. },
  92. second: {
  93. type: Number,
  94. default: 0
  95. },
  96. timestamp: {
  97. type: Number,
  98. default: 0
  99. },
  100. filterShow : {
  101. type:Object,
  102. default () {
  103. return {}
  104. }
  105. }
  106. },
  107. data() {
  108. return {
  109. timer: null,
  110. syncFlag: false,
  111. d: '00',
  112. h: '00',
  113. i: '00',
  114. s: '00',
  115. leftTime: 0,
  116. seconds: 0
  117. }
  118. },
  119. computed: {
  120. dayText() {
  121. return t("uni-countdown.day")
  122. },
  123. hourText(val) {
  124. return t("uni-countdown.h")
  125. },
  126. minuteText(val) {
  127. return t("uni-countdown.m")
  128. },
  129. secondText(val) {
  130. return t("uni-countdown.s")
  131. },
  132. timeStyle() {
  133. const {
  134. color,
  135. backgroundColor,
  136. fontSize
  137. } = this
  138. return {
  139. color,
  140. backgroundColor,
  141. fontSize: `${fontSize}px`,
  142. width: `${fontSize * 22 / 14}px`, // 按字体大小为 14px 时的比例缩放
  143. lineHeight: `${fontSize * 20 / 14}px`,
  144. borderRadius: `${fontSize * 3 / 14}px`,
  145. }
  146. },
  147. splitorStyle() {
  148. const { splitorColor, fontSize, backgroundColor } = this
  149. return {
  150. color: splitorColor,
  151. fontSize: `${fontSize * 12 / 14}px`,
  152. margin: backgroundColor ? `${fontSize * 4 / 14}px` : ''
  153. }
  154. }
  155. },
  156. watch: {
  157. day(val) {
  158. this.changeFlag()
  159. },
  160. hour(val) {
  161. this.changeFlag()
  162. },
  163. minute(val) {
  164. this.changeFlag()
  165. },
  166. second(val) {
  167. this.changeFlag()
  168. },
  169. start: {
  170. immediate: true,
  171. handler(newVal, oldVal) {
  172. if (newVal) {
  173. this.startData();
  174. } else {
  175. if (!oldVal) return
  176. clearInterval(this.timer)
  177. }
  178. }
  179. }
  180. },
  181. created: function(e) {
  182. this.seconds = this.toSeconds(this.timestamp, this.day, this.hour, this.minute, this.second)
  183. this.countDown()
  184. },
  185. // #ifndef VUE3
  186. destroyed() {
  187. clearInterval(this.timer)
  188. },
  189. // #endif
  190. // #ifdef VUE3
  191. unmounted() {
  192. clearInterval(this.timer)
  193. },
  194. // #endif
  195. methods: {
  196. toSeconds(timestamp, day, hours, minutes, seconds) {
  197. if (timestamp) {
  198. return timestamp - parseInt(new Date().getTime() / 1000, 10)
  199. }
  200. return day * 60 * 60 * 24 + hours * 60 * 60 + minutes * 60 + seconds
  201. },
  202. timeUp() {
  203. clearInterval(this.timer)
  204. this.$emit('timeup')
  205. },
  206. countDown() {
  207. let seconds = this.seconds
  208. let [day, hour, minute, second] = [0, 0, 0, 0]
  209. if (seconds > 0) {
  210. day = Math.floor(seconds / (60 * 60 * 24))
  211. hour = Math.floor(seconds / (60 * 60)) - (day * 24)
  212. minute = Math.floor(seconds / 60) - (day * 24 * 60) - (hour * 60)
  213. second = Math.floor(seconds) - (day * 24 * 60 * 60) - (hour * 60 * 60) - (minute * 60)
  214. } else {
  215. this.timeUp()
  216. }
  217. this.d = String(day).padStart(this.validFilterShow(this.filterShow.d), '0')
  218. this.h = String(hour).padStart(this.validFilterShow(this.filterShow.h), '0')
  219. this.i = String(minute).padStart(this.validFilterShow(this.filterShow.m), '0')
  220. this.s = String(second).padStart(this.validFilterShow(this.filterShow.s), '0')
  221. },
  222. validFilterShow(filter){
  223. return (filter && filter > 0) ? filter : 2;
  224. },
  225. startData() {
  226. this.seconds = this.toSeconds(this.timestamp, this.day, this.hour, this.minute, this.second)
  227. if (this.seconds <= 0) {
  228. this.seconds = this.toSeconds(0, 0, 0, 0, 0)
  229. this.countDown()
  230. return
  231. }
  232. clearInterval(this.timer)
  233. this.countDown()
  234. this.timer = setInterval(() => {
  235. this.seconds--
  236. if (this.seconds < 0) {
  237. this.timeUp()
  238. return
  239. }
  240. this.countDown()
  241. }, 1000)
  242. },
  243. update(){
  244. this.startData();
  245. },
  246. changeFlag() {
  247. if (!this.syncFlag) {
  248. this.seconds = this.toSeconds(this.timestamp, this.day, this.hour, this.minute, this.second)
  249. this.startData();
  250. this.syncFlag = true;
  251. }
  252. }
  253. }
  254. }
  255. </script>
  256. <style lang="scss" scoped>
  257. $font-size: 14px;
  258. .uni-countdown {
  259. display: flex;
  260. flex-direction: row;
  261. justify-content: flex-start;
  262. align-items: center;
  263. &__splitor {
  264. margin: 0 2px;
  265. font-size: $font-size;
  266. color: #333;
  267. }
  268. &__number {
  269. border-radius: 3px;
  270. text-align: center;
  271. font-size: $font-size;
  272. }
  273. }
  274. </style>