|
@@ -1,58 +1,75 @@
|
|
|
<template>
|
|
<template>
|
|
|
<MoveablePanel
|
|
<MoveablePanel
|
|
|
class="countdown-timer"
|
|
class="countdown-timer"
|
|
|
- :width="180"
|
|
|
|
|
- :height="110"
|
|
|
|
|
|
|
+ :width="250"
|
|
|
|
|
+ :height="170"
|
|
|
:left="left"
|
|
:left="left"
|
|
|
:top="top"
|
|
:top="top"
|
|
|
>
|
|
>
|
|
|
- <div class="header">
|
|
|
|
|
- <span class="text-btn" @click="toggle()">{{ inTiming ? '暂停' : '开始'}}</span>
|
|
|
|
|
- <span class="text-btn" @click="reset()">重置</span>
|
|
|
|
|
- <span class="text-btn" @click="toggleCountdown()" :class="{ 'active': isCountdown }">倒计时</span>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="content">
|
|
|
|
|
- <div class="timer">
|
|
|
|
|
- <input
|
|
|
|
|
- type="text"
|
|
|
|
|
- :value="fillDigit(minute, 2)"
|
|
|
|
|
- :maxlength="3" :disabled="inputEditable"
|
|
|
|
|
- @mousedown.stop
|
|
|
|
|
- @blur="$event => changeTime($event, 'minute')"
|
|
|
|
|
- @keydown.stop
|
|
|
|
|
- @keydown.enter.stop="$event => changeTime($event, 'minute')"
|
|
|
|
|
- >
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="colon">:</div>
|
|
|
|
|
- <div class="timer">
|
|
|
|
|
- <input
|
|
|
|
|
- type="text"
|
|
|
|
|
- :value="fillDigit(second, 2)"
|
|
|
|
|
- :maxlength="3" :disabled="inputEditable"
|
|
|
|
|
- @mousedown.stop
|
|
|
|
|
- @blur="$event => changeTime($event, 'second')"
|
|
|
|
|
- @keydown.stop
|
|
|
|
|
- @keydown.enter.stop="$event => changeTime($event, 'second')"
|
|
|
|
|
- >
|
|
|
|
|
|
|
+ <div class="panel-body">
|
|
|
|
|
+ <div class="row time-row">
|
|
|
|
|
+ <div class="time-box">
|
|
|
|
|
+ <input
|
|
|
|
|
+ ref="hourInputRef"
|
|
|
|
|
+ type="number"
|
|
|
|
|
+ class="digit-input"
|
|
|
|
|
+ v-model.number="hourInput"
|
|
|
|
|
+ @blur="handleBlur"
|
|
|
|
|
+ @click="focusedInput = 'hour'"
|
|
|
|
|
+ @focus="handleFocus('hour')"
|
|
|
|
|
+ @keyup.enter="handleEnter"
|
|
|
|
|
+ min="0"
|
|
|
|
|
+ />
|
|
|
|
|
+ <span class="colon">:</span>
|
|
|
|
|
+ <input
|
|
|
|
|
+ ref="minuteInputRef"
|
|
|
|
|
+ type="number"
|
|
|
|
|
+ class="digit-input"
|
|
|
|
|
+ v-model.number="minuteInput"
|
|
|
|
|
+ @blur="handleBlur"
|
|
|
|
|
+ @click="focusedInput = 'minute'"
|
|
|
|
|
+ @focus="handleFocus('minute')"
|
|
|
|
|
+ @keyup.enter="handleEnter"
|
|
|
|
|
+ min="0"
|
|
|
|
|
+ max="59"
|
|
|
|
|
+ />
|
|
|
|
|
+ <span class="colon">:</span>
|
|
|
|
|
+ <input
|
|
|
|
|
+ ref="secondInputRef"
|
|
|
|
|
+ type="number"
|
|
|
|
|
+ class="digit-input"
|
|
|
|
|
+ v-model.number="secondInput"
|
|
|
|
|
+ @blur="handleBlur"
|
|
|
|
|
+ @click="focusedInput = 'second'"
|
|
|
|
|
+ @focus="handleFocus('second')"
|
|
|
|
|
+ @keyup.enter="handleEnter"
|
|
|
|
|
+ min="0"
|
|
|
|
|
+ max="59"
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="side-controls">
|
|
|
|
|
+ <button class="square-btn" @click="increment()">+</button>
|
|
|
|
|
+ <button class="square-btn" @click="decrement()" :disabled="time <= 0">−</button>
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
+ <div class="divider"></div>
|
|
|
|
|
+ <button class="primary-btn" @click="toggle()">{{ inTiming ? '暂停' : '开始计时' }}</button>
|
|
|
</div>
|
|
</div>
|
|
|
-
|
|
|
|
|
- <div class="close-btn" @click="emit('close')"><IconClose class="icon" /></div>
|
|
|
|
|
|
|
+ <!-- <div class="close-btn" @click="emit('close')"><IconClose class="icon" /></div> -->
|
|
|
</MoveablePanel>
|
|
</MoveablePanel>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script lang="ts" setup>
|
|
<script lang="ts" setup>
|
|
|
-import { computed, onUnmounted, ref } from 'vue'
|
|
|
|
|
-import { fillDigit } from '@/utils/common'
|
|
|
|
|
|
|
+import { onUnmounted, ref, watch } from 'vue'
|
|
|
|
|
|
|
|
-import MoveablePanel from '@/components/MoveablePanel.vue'
|
|
|
|
|
|
|
+import MoveablePanel from '@/components/MoveablePanel2.vue'
|
|
|
|
|
|
|
|
withDefaults(defineProps<{
|
|
withDefaults(defineProps<{
|
|
|
left?: number
|
|
left?: number
|
|
|
top?: number
|
|
top?: number
|
|
|
}>(), {
|
|
}>(), {
|
|
|
- left: 5,
|
|
|
|
|
- top: 5,
|
|
|
|
|
|
|
+ left: -200,
|
|
|
|
|
+ top: -100,
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
const emit = defineEmits<{
|
|
const emit = defineEmits<{
|
|
@@ -62,18 +79,95 @@ const emit = defineEmits<{
|
|
|
(event: 'timer-reset'): void
|
|
(event: 'timer-reset'): void
|
|
|
(event: 'timer-stop'): void
|
|
(event: 'timer-stop'): void
|
|
|
(event: 'timer-finish'): void
|
|
(event: 'timer-finish'): void
|
|
|
|
|
+ (event: 'timer-update', payload: { durationSec: number }): void
|
|
|
}>()
|
|
}>()
|
|
|
|
|
|
|
|
const timer = ref<number | null>(null)
|
|
const timer = ref<number | null>(null)
|
|
|
const inTiming = ref(false)
|
|
const inTiming = ref(false)
|
|
|
-const isCountdown = ref(false)
|
|
|
|
|
-const time = ref(0)
|
|
|
|
|
-const minute = computed(() => Math.floor(time.value / 60))
|
|
|
|
|
-const second = computed(() => time.value % 60)
|
|
|
|
|
|
|
+// 仅倒计时模式,默认 03:00:00
|
|
|
|
|
+const time = ref(3 * 60 * 60)
|
|
|
|
|
|
|
|
-const inputEditable = computed(() => {
|
|
|
|
|
- return !isCountdown.value || inTiming.value
|
|
|
|
|
-})
|
|
|
|
|
|
|
+// 可编辑的时分秒输入值
|
|
|
|
|
+const hourInput = ref(3)
|
|
|
|
|
+const minuteInput = ref(0)
|
|
|
|
|
+const secondInput = ref(0)
|
|
|
|
|
+
|
|
|
|
|
+// 输入框引用
|
|
|
|
|
+const hourInputRef = ref<HTMLInputElement | null>(null)
|
|
|
|
|
+const minuteInputRef = ref<HTMLInputElement | null>(null)
|
|
|
|
|
+const secondInputRef = ref<HTMLInputElement | null>(null)
|
|
|
|
|
+
|
|
|
|
|
+// 当前聚焦的输入框类型:'hour' | 'minute' | 'second' | null
|
|
|
|
|
+const focusedInput = ref<'hour' | 'minute' | 'second' | null>(null)
|
|
|
|
|
+// 当前正在编辑的输入框(有焦点)
|
|
|
|
|
+const editingInput = ref<'hour' | 'minute' | 'second' | null>(null)
|
|
|
|
|
+
|
|
|
|
|
+// 从 time 同步到输入框(如果输入框没有被编辑)
|
|
|
|
|
+watch(time, (newTime) => {
|
|
|
|
|
+ if (editingInput.value !== 'hour') {
|
|
|
|
|
+ hourInput.value = Math.floor(newTime / 3600)
|
|
|
|
|
+ }
|
|
|
|
|
+ if (editingInput.value !== 'minute') {
|
|
|
|
|
+ minuteInput.value = Math.floor((newTime % 3600) / 60)
|
|
|
|
|
+ }
|
|
|
|
|
+ if (editingInput.value !== 'second') {
|
|
|
|
|
+ secondInput.value = newTime % 60
|
|
|
|
|
+ }
|
|
|
|
|
+}, { immediate: true })
|
|
|
|
|
+
|
|
|
|
|
+// 处理输入框获得焦点
|
|
|
|
|
+const handleFocus = (type: 'hour' | 'minute' | 'second') => {
|
|
|
|
|
+ focusedInput.value = type
|
|
|
|
|
+ editingInput.value = type
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 处理输入框失焦
|
|
|
|
|
+const handleBlur = () => {
|
|
|
|
|
+ editingInput.value = null
|
|
|
|
|
+ updateTimeFromInputs()
|
|
|
|
|
+ // 不清除选中状态,保持用户选择的输入框,直到用户点击其他输入框
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 处理回车键
|
|
|
|
|
+const handleEnter = () => {
|
|
|
|
|
+ if (editingInput.value) {
|
|
|
|
|
+ editingInput.value = null
|
|
|
|
|
+ updateTimeFromInputs()
|
|
|
|
|
+ // 失焦当前输入框
|
|
|
|
|
+ if (hourInputRef.value && document.activeElement === hourInputRef.value) {
|
|
|
|
|
+ hourInputRef.value.blur()
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (minuteInputRef.value && document.activeElement === minuteInputRef.value) {
|
|
|
|
|
+ minuteInputRef.value.blur()
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (secondInputRef.value && document.activeElement === secondInputRef.value) {
|
|
|
|
|
+ secondInputRef.value.blur()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 从输入框更新 time 值
|
|
|
|
|
+const updateTimeFromInputs = () => {
|
|
|
|
|
+ // 验证并限制输入范围
|
|
|
|
|
+ const h = Math.max(0, hourInput.value || 0)
|
|
|
|
|
+ const m = Math.max(0, Math.min(59, minuteInput.value || 0))
|
|
|
|
|
+ const s = Math.max(0, Math.min(59, secondInput.value || 0))
|
|
|
|
|
+
|
|
|
|
|
+ // 更新输入值(确保显示正确)
|
|
|
|
|
+ hourInput.value = h
|
|
|
|
|
+ minuteInput.value = m
|
|
|
|
|
+ secondInput.value = s
|
|
|
|
|
+
|
|
|
|
|
+ // 计算总秒数(不限制最大时间)
|
|
|
|
|
+ const newTime = h * 3600 + m * 60 + s
|
|
|
|
|
+ const oldTime = time.value
|
|
|
|
|
+ time.value = newTime
|
|
|
|
|
+
|
|
|
|
|
+ // 如果正在计时且时间发生变化,重新开始计时
|
|
|
|
|
+ if (inTiming.value && newTime !== oldTime) {
|
|
|
|
|
+ restart()
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
const clearTimer = () => {
|
|
const clearTimer = () => {
|
|
|
if (timer.value !== null) {
|
|
if (timer.value !== null) {
|
|
@@ -89,40 +183,43 @@ const pause = () => {
|
|
|
emit('timer-pause', { pausedAt: new Date().toISOString() })
|
|
emit('timer-pause', { pausedAt: new Date().toISOString() })
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const reset = () => {
|
|
|
|
|
- clearTimer()
|
|
|
|
|
- inTiming.value = false
|
|
|
|
|
-
|
|
|
|
|
- if (isCountdown.value) time.value = 600
|
|
|
|
|
- else time.value = 0
|
|
|
|
|
- emit('timer-reset')
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
const start = () => {
|
|
const start = () => {
|
|
|
clearTimer()
|
|
clearTimer()
|
|
|
|
|
|
|
|
- if (isCountdown.value) {
|
|
|
|
|
- timer.value = window.setInterval(() => {
|
|
|
|
|
- time.value = time.value - 1
|
|
|
|
|
-
|
|
|
|
|
- if (time.value <= 0) {
|
|
|
|
|
- time.value = 0
|
|
|
|
|
- clearTimer()
|
|
|
|
|
- inTiming.value = false
|
|
|
|
|
- emit('timer-finish')
|
|
|
|
|
- }
|
|
|
|
|
- }, 1000)
|
|
|
|
|
- }
|
|
|
|
|
- else {
|
|
|
|
|
- timer.value = window.setInterval(() => {
|
|
|
|
|
- time.value = time.value + 1
|
|
|
|
|
|
|
+ // 倒计时
|
|
|
|
|
+ timer.value = window.setInterval(() => {
|
|
|
|
|
+ time.value = time.value - 1
|
|
|
|
|
|
|
|
- if (time.value > 36000) pause()
|
|
|
|
|
- }, 1000)
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (time.value <= 0) {
|
|
|
|
|
+ time.value = 0
|
|
|
|
|
+ clearTimer()
|
|
|
|
|
+ inTiming.value = false
|
|
|
|
|
+ emit('timer-finish')
|
|
|
|
|
+ }
|
|
|
|
|
+ }, 1000)
|
|
|
|
|
|
|
|
inTiming.value = true
|
|
inTiming.value = true
|
|
|
- emit('timer-start', { isCountdown: isCountdown.value, startAt: new Date().toISOString(), durationSec: isCountdown.value ? time.value : undefined })
|
|
|
|
|
|
|
+ emit('timer-start', { isCountdown: true, startAt: new Date().toISOString(), durationSec: time.value })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 重新开始计时(用于时间更新时重置)
|
|
|
|
|
+const restart = () => {
|
|
|
|
|
+ if (!inTiming.value) return
|
|
|
|
|
+ clearTimer()
|
|
|
|
|
+
|
|
|
|
|
+ // 倒计时
|
|
|
|
|
+ timer.value = window.setInterval(() => {
|
|
|
|
|
+ time.value = time.value - 1
|
|
|
|
|
+
|
|
|
|
|
+ if (time.value <= 0) {
|
|
|
|
|
+ time.value = 0
|
|
|
|
|
+ clearTimer()
|
|
|
|
|
+ inTiming.value = false
|
|
|
|
|
+ emit('timer-finish')
|
|
|
|
|
+ }
|
|
|
|
|
+ }, 1000)
|
|
|
|
|
+
|
|
|
|
|
+ emit('timer-start', { isCountdown: true, startAt: new Date().toISOString(), durationSec: time.value })
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const toggle = () => {
|
|
const toggle = () => {
|
|
@@ -130,87 +227,197 @@ const toggle = () => {
|
|
|
else start()
|
|
else start()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const toggleCountdown = () => {
|
|
|
|
|
- isCountdown.value = !isCountdown.value
|
|
|
|
|
- reset()
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
onUnmounted(() => {
|
|
onUnmounted(() => {
|
|
|
emit('timer-stop')
|
|
emit('timer-stop')
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
-const changeTime = (e: FocusEvent | KeyboardEvent, type: 'minute' | 'second') => {
|
|
|
|
|
- const inputRef = e.target as HTMLInputElement
|
|
|
|
|
- let value = inputRef.value
|
|
|
|
|
- const isNumber = /^(\d)+$/.test(value)
|
|
|
|
|
- if (isNumber) {
|
|
|
|
|
- if (type === 'second' && +value >= 60) value = '59'
|
|
|
|
|
- time.value = type === 'minute' ? (+value * 60 + second.value) : (+value + minute.value * 60)
|
|
|
|
|
|
|
+// 根据当前聚焦的输入框增加时间(默认操作秒)
|
|
|
|
|
+const increment = () => {
|
|
|
|
|
+ const currentFocus = focusedInput.value || 'second' // 默认操作秒
|
|
|
|
|
+
|
|
|
|
|
+ if (currentFocus === 'hour') {
|
|
|
|
|
+ hourInput.value = Math.max(0, (hourInput.value || 0) + 1)
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (currentFocus === 'minute') {
|
|
|
|
|
+ const newMinute = (minuteInput.value || 0) + 1
|
|
|
|
|
+ if (newMinute > 59) {
|
|
|
|
|
+ minuteInput.value = 0
|
|
|
|
|
+ hourInput.value = Math.max(0, (hourInput.value || 0) + 1)
|
|
|
|
|
+ }
|
|
|
|
|
+ else {
|
|
|
|
|
+ minuteInput.value = newMinute
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ else {
|
|
|
|
|
+ // 默认操作秒
|
|
|
|
|
+ const newSecond = (secondInput.value || 0) + 1
|
|
|
|
|
+ if (newSecond > 59) {
|
|
|
|
|
+ secondInput.value = 0
|
|
|
|
|
+ const newMinute = (minuteInput.value || 0) + 1
|
|
|
|
|
+ if (newMinute > 59) {
|
|
|
|
|
+ minuteInput.value = 0
|
|
|
|
|
+ hourInput.value = Math.max(0, (hourInput.value || 0) + 1)
|
|
|
|
|
+ }
|
|
|
|
|
+ else {
|
|
|
|
|
+ minuteInput.value = newMinute
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ else {
|
|
|
|
|
+ secondInput.value = newSecond
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
- else inputRef.value = type === 'minute' ? fillDigit(minute.value, 2) : fillDigit(second.value, 2)
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // 更新总时间
|
|
|
|
|
+ updateTimeFromInputs()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 根据当前聚焦的输入框减少时间(默认操作秒)
|
|
|
|
|
+const decrement = () => {
|
|
|
|
|
+ const currentFocus = focusedInput.value || 'second' // 默认操作秒
|
|
|
|
|
+
|
|
|
|
|
+ if (currentFocus === 'hour') {
|
|
|
|
|
+ hourInput.value = Math.max(0, (hourInput.value || 0) - 1)
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (currentFocus === 'minute') {
|
|
|
|
|
+ const newMinute = (minuteInput.value || 0) - 1
|
|
|
|
|
+ if (newMinute < 0) {
|
|
|
|
|
+ if (hourInput.value > 0) {
|
|
|
|
|
+ minuteInput.value = 59
|
|
|
|
|
+ hourInput.value = Math.max(0, (hourInput.value || 0) - 1)
|
|
|
|
|
+ }
|
|
|
|
|
+ else {
|
|
|
|
|
+ minuteInput.value = 0
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ else {
|
|
|
|
|
+ minuteInput.value = newMinute
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ else {
|
|
|
|
|
+ // 默认操作秒
|
|
|
|
|
+ const newSecond = (secondInput.value || 0) - 1
|
|
|
|
|
+ if (newSecond < 0) {
|
|
|
|
|
+ if (minuteInput.value > 0 || hourInput.value > 0) {
|
|
|
|
|
+ secondInput.value = 59
|
|
|
|
|
+ const newMinute = (minuteInput.value || 0) - 1
|
|
|
|
|
+ if (newMinute < 0) {
|
|
|
|
|
+ if (hourInput.value > 0) {
|
|
|
|
|
+ minuteInput.value = 59
|
|
|
|
|
+ hourInput.value = Math.max(0, (hourInput.value || 0) - 1)
|
|
|
|
|
+ }
|
|
|
|
|
+ else {
|
|
|
|
|
+ minuteInput.value = 0
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ else {
|
|
|
|
|
+ minuteInput.value = newMinute
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ else {
|
|
|
|
|
+ secondInput.value = 0
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ else {
|
|
|
|
|
+ secondInput.value = newSecond
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 更新总时间
|
|
|
|
|
+ updateTimeFromInputs()
|
|
|
}
|
|
}
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
<style lang="scss" scoped>
|
|
|
.countdown-timer {
|
|
.countdown-timer {
|
|
|
user-select: none;
|
|
user-select: none;
|
|
|
|
|
+ background: rgba(0, 0, 0, 0.6);
|
|
|
|
|
+ border-radius: 12px;
|
|
|
|
|
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.25);
|
|
|
|
|
+ /* padding: 14px; */
|
|
|
}
|
|
}
|
|
|
-.header {
|
|
|
|
|
- height: 16px;
|
|
|
|
|
- font-size: 13px;
|
|
|
|
|
- margin-bottom: 16px;
|
|
|
|
|
|
|
+.panel-body {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
- align-items: center;
|
|
|
|
|
-
|
|
|
|
|
- .text-btn {
|
|
|
|
|
- margin-right: 8px;
|
|
|
|
|
- cursor: pointer;
|
|
|
|
|
-
|
|
|
|
|
- &:hover, &.active {
|
|
|
|
|
- color: $themeColor;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ gap: 12px;
|
|
|
}
|
|
}
|
|
|
-.content {
|
|
|
|
|
|
|
+.time-row {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
justify-content: space-between;
|
|
justify-content: space-between;
|
|
|
- padding: 0 5px;
|
|
|
|
|
-}
|
|
|
|
|
-.timer {
|
|
|
|
|
- width: 54px;
|
|
|
|
|
- height: 54px;
|
|
|
|
|
- border-radius: 50%;
|
|
|
|
|
- background-color: rgba($color: $themeColor, $alpha: .05);
|
|
|
|
|
- overflow: hidden;
|
|
|
|
|
-
|
|
|
|
|
- input {
|
|
|
|
|
- width: 100%;
|
|
|
|
|
- height: 100%;
|
|
|
|
|
- border: 0;
|
|
|
|
|
- outline: 0;
|
|
|
|
|
- background-color: transparent;
|
|
|
|
|
- text-align: center;
|
|
|
|
|
- font-size: 22px;
|
|
|
|
|
- }
|
|
|
|
|
}
|
|
}
|
|
|
.colon {
|
|
.colon {
|
|
|
- height: 54px;
|
|
|
|
|
- line-height: 54px;
|
|
|
|
|
- font-size: 22px;
|
|
|
|
|
|
|
+ margin: 0 6px;
|
|
|
|
|
+ color: #fff;
|
|
|
}
|
|
}
|
|
|
-.icon-btn {
|
|
|
|
|
- width: 20px;
|
|
|
|
|
- height: 20px;
|
|
|
|
|
|
|
+.time-box {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
- justify-content: center;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
|
|
+ align-items: baseline;
|
|
|
|
|
+}
|
|
|
|
|
+.digit-input {
|
|
|
|
|
+ width: 50px;
|
|
|
|
|
+ color: #fff;
|
|
|
|
|
+ font-size: 40px;
|
|
|
|
|
+ font-weight: 700;
|
|
|
|
|
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
|
|
|
|
+ background: transparent;
|
|
|
|
|
+ border: none;
|
|
|
|
|
+ outline: none;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+ padding: 0;
|
|
|
|
|
+ -moz-appearance: textfield;
|
|
|
|
|
+ appearance: textfield;
|
|
|
|
|
+}
|
|
|
|
|
+.digit-input::-webkit-outer-spin-button,
|
|
|
|
|
+.digit-input::-webkit-inner-spin-button {
|
|
|
|
|
+ -webkit-appearance: none;
|
|
|
|
|
+ appearance: none;
|
|
|
|
|
+ margin: 0;
|
|
|
|
|
+}
|
|
|
|
|
+.digit-input:disabled {
|
|
|
|
|
+ opacity: 1;
|
|
|
|
|
+ cursor: not-allowed;
|
|
|
|
|
+}
|
|
|
|
|
+.digit-input:focus {
|
|
|
|
|
+ background: rgba(255, 255, 255, 0.1);
|
|
|
|
|
+ border-radius: 4px;
|
|
|
|
|
+}
|
|
|
|
|
+.side-controls {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ gap: 8px;
|
|
|
|
|
+}
|
|
|
|
|
+.square-btn {
|
|
|
|
|
+ width: 36px;
|
|
|
|
|
+ height: 32px;
|
|
|
|
|
+ border: 0;
|
|
|
|
|
+ border-radius: 6px;
|
|
|
|
|
+ background: rgba(255, 255, 255, 0.18);
|
|
|
|
|
+ color: #fff;
|
|
|
|
|
+ font-size: 16px;
|
|
|
cursor: pointer;
|
|
cursor: pointer;
|
|
|
}
|
|
}
|
|
|
-.pause, .play {
|
|
|
|
|
- font-size: 17px;
|
|
|
|
|
|
|
+.square-btn:disabled {
|
|
|
|
|
+ opacity: .5;
|
|
|
|
|
+ cursor: not-allowed;
|
|
|
|
|
+}
|
|
|
|
|
+.divider {
|
|
|
|
|
+ height: 1px;
|
|
|
|
|
+ background: rgba(255, 255, 255, 0.35);
|
|
|
|
|
+ margin: 2px 0;
|
|
|
|
|
+}
|
|
|
|
|
+.primary-btn {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ height: 44px;
|
|
|
|
|
+ border: 0;
|
|
|
|
|
+ border-radius: 8px;
|
|
|
|
|
+ background: #ffcc00;
|
|
|
|
|
+ color: #333;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ font-size: 16px;
|
|
|
|
|
+ cursor: pointer;
|
|
|
}
|
|
}
|
|
|
-.reset {
|
|
|
|
|
- font-size: 12px;
|
|
|
|
|
|
|
+.primary-btn:hover {
|
|
|
|
|
+ filter: brightness(0.98);
|
|
|
}
|
|
}
|
|
|
.close-btn {
|
|
.close-btn {
|
|
|
position: absolute;
|
|
position: absolute;
|
|
@@ -219,5 +426,6 @@ const changeTime = (e: FocusEvent | KeyboardEvent, type: 'minute' | 'second') =>
|
|
|
padding: 10px;
|
|
padding: 10px;
|
|
|
line-height: 1;
|
|
line-height: 1;
|
|
|
cursor: pointer;
|
|
cursor: pointer;
|
|
|
|
|
+ color: #fff;
|
|
|
}
|
|
}
|
|
|
</style>
|
|
</style>
|