uni-countdown.vue 6.6 KB

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