<template>
	<view class="uv-code">
		<!-- 此組件功能由js完成，無需寫html邏輯 -->
	</view>
</template>
<script>
	import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
	import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
	import props from './props.js';
	/**
	 * Code 驗證碼倒計時
	 * @description 考慮到用戶實際發送驗證碼的場景，可能是壹個按鈕，也可能是壹段文字，提示語各有不同，所以本組件不提供界面顯示，只提供倒計時文本，由用戶將文本嵌入到具體的場景
	 * @tutorial https://www.uvui.cn/components/code.html
	 * @property {String | Number}	seconds			倒計時所需的秒數（默認 60 ）
	 * @property {String}			startText		開始前的提示語，見官網說明（默認 '獲取驗證碼' ）
	 * @property {String}			changeText		倒計時期間的提示語，必須帶有字母"x"，見官網說明（默認 'X秒重新獲取' ）
	 * @property {String}			endText			倒計結束的提示語，見官網說明（默認 '重新獲取' ）
	 * @property {Boolean}			keepRunning		是否在H5刷新或各端返回再進入時繼續倒計時（ 默認false ）
	 * @property {String}			uniqueKey		為了區分多個頁面，或者壹個頁面多個倒計時組件本地存儲的繼續倒計時變了
	 *
	 * @event {Function}	change	倒計時期間，每秒觸發壹次
	 * @event {Function}	start	開始倒計時觸發
	 * @event {Function}	end		結束倒計時觸發
	 * @example <uv-code ref="uCode" @change="codeChange" seconds="20"></uv-code>
	 */
	export default {
		name: "uv-code",
		mixins: [mpMixin, mixin, props],
		data() {
			return {
				secNum: this.seconds,
				timer: null,
				canGetCode: true, // 是否可以執行驗證碼操作
			}
		},
		mounted() {
			this.checkKeepRunning()
		},
		watch: {
			seconds: {
				immediate: true,
				handler(n) {
					this.secNum = n
				}
			}
		},
		methods: {
			checkKeepRunning() {
				// 獲取上壹次退出頁面(H5還包括刷新)時的時間戳，如果沒有上次的保存，此值可能為空
				let lastTimestamp = Number(uni.getStorageSync(this.uniqueKey + '_$uCountDownTimestamp'))
				if (!lastTimestamp) return this.changeEvent(this.startText)
				// 當前秒的時間戳
				let nowTimestamp = Math.floor((+new Date()) / 1000)
				// 判斷當前的時間戳，是否小於上壹次的本該按設定結束，卻提前結束的時間戳
				if (this.keepRunning && lastTimestamp && lastTimestamp > nowTimestamp) {
					// 剩余尚未執行完的倒計秒數
					this.secNum = lastTimestamp - nowTimestamp
					// 清除本地保存的變量
					uni.removeStorageSync(this.uniqueKey + '_$uCountDownTimestamp')
					// 開始倒計時
					this.start()
				} else {
					// 如果不存在需要繼續上壹次的倒計時，執行正常的邏輯
					this.changeEvent(this.startText)
				}
			},
			// 開始倒計時
			start() {
				// 防止快速點擊獲取驗證碼的按鈕而導致內部產生多個定時器導致混亂
				if (this.timer) {
					clearInterval(this.timer)
					this.timer = null
				}
				this.$emit('start')
				this.canGetCode = false
				// 這裏放這句，是為了壹開始時就提示，否則要等setInterval的1秒後才會有提示
				this.changeEvent(this.changeText.replace(/x|X/, this.secNum))
				this.timer = setInterval(() => {
					if (--this.secNum) {
						// 用當前倒計時的秒數替換提示字符串中的"x"字母
						this.changeEvent(this.changeText.replace(/x|X/, this.secNum))
					} else {
						clearInterval(this.timer)
						this.timer = null
						this.changeEvent(this.endText)
						this.secNum = this.seconds
						this.$emit('end')
						this.canGetCode = true
					}
				}, 1000)
				this.setTimeToStorage()
			},
			// 重置，可以讓用戶再次獲取驗證碼
			reset() {
				this.canGetCode = true
				clearInterval(this.timer)
				this.secNum = this.seconds
				this.changeEvent(this.endText)
			},
			changeEvent(text) {
				this.$emit('change', text)
			},
			// 保存時間戳，為了防止倒計時尚未結束，H5刷新或者各端的右上角返回上壹頁再進來
			setTimeToStorage() {
				if (!this.keepRunning || !this.timer) return
				// 記錄當前的時間戳，為了下次進入頁面，如果還在倒計時內的話，繼續倒計時
				// 倒計時尚未結束，結果大於0；倒計時已經開始，就會小於初始值，如果等於初始值，說明沒有開始倒計時，無需處理
				if (this.secNum > 0 && this.secNum <= this.seconds) {
					// 獲取當前時間戳(+ new Date()為特殊寫法)，除以1000變成秒，再去除小數部分
					let nowTimestamp = Math.floor((+new Date()) / 1000)
					// 將本該結束時候的時間戳保存起來 => 當前時間戳 + 剩余的秒數
					uni.setStorage({
						key: this.uniqueKey + '_$uCountDownTimestamp',
						data: nowTimestamp + Number(this.secNum)
					})
				}
			}
		},
		// #ifdef VUE2
		// 組件銷毀的時候，清除定時器，否則定時器會繼續存在，系統不會自動清除
		beforeDestroy() {
			this.setTimeToStorage()
			clearTimeout(this.timer)
			this.timer = null
		},
		// #endif
		// #ifdef VUE3
		// 組件銷毀，兼容vue3
		unmounted() {
			this.setTimeToStorage()
			clearTimeout(this.timer)
			this.timer = null
		}
		// #endif
	}
</script>