Skip to content

Commit

Permalink
演示模式增加倒计时功能 #61
Browse files Browse the repository at this point in the history
  • Loading branch information
umodoc committed Sep 17, 2024
1 parent 1608871 commit 48ec4f1
Show file tree
Hide file tree
Showing 7 changed files with 314 additions and 3 deletions.
1 change: 1 addition & 0 deletions src/assets/icons/time.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion src/components/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -843,7 +843,9 @@ defineExpose({
}
&.preview-mode {
&.laser-pointer {
cursor: url('@/assets/images/laser-pointer.svg'), auto;
.umo-main {
cursor: url('@/assets/images/laser-pointer.svg'), auto;
}
}
.umo-toolbar {
display: none;
Expand Down
237 changes: 237 additions & 0 deletions src/components/statusbar/countdown.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
<template>
<t-popup
:visible="visible"
:attach="container"
:z-index="2000"
trigger="click"
placement="left-start"
:popper-options="popperOptions"
>
<slot />
<template #content>
<div class="umo-preview-countdown">
<t-form label-align="left" label-width="75px" @submit="startCountdown">
<t-form-item
:label="t('preview.countdown.select')"
name="quickSelect"
>
<t-select
v-model="selectValue"
:options="options"
:placeholder="t('preview.countdown.selectTip')"
:popup-props="{ attach: container, zIndex: 2000 }"
@change="countdownSelect"
/>
</t-form-item>
<t-form-item :label="t('preview.countdown.custom')" name="custom">
<div class="umo-preview-countdown-input" size="small">
<t-input-number
v-model="hours"
theme="normal"
align="center"
:placeholder="t('preview.countdown.hours')"
:decimal-places="0"
:min="0"
:max="60"
:step="1"
:disabled="selectValue !== null"
/>
<t-input-number
v-model="minutes"
theme="normal"
align="center"
:placeholder="t('preview.countdown.minutes')"
:decimal-places="0"
:min="0"
:max="60"
:step="1"
:disabled="selectValue !== null"
/>
<t-input-number
v-model="seconds"
theme="normal"
align="center"
:placeholder="t('preview.countdown.seconds')"
:decimal-places="0"
:min="0"
:max="60"
:step="1"
:disabled="selectValue !== null"
/>
</div>
</t-form-item>
<t-form-item
:label="t('preview.countdown.whenEnd')"
name="endOptions"
>
<t-radio-group v-model="whenEnd" default-value="showEndMessage">
<t-radio value="showEndMessage">
{{ t('preview.countdown.showEndMessage') }}</t-radio
>
<t-radio value="exitPreview">
{{ t('preview.countdown.exitPreview') }}
</t-radio>
</t-radio-group>
<div></div>
</t-form-item>
<t-form-item>
<t-space size="small">
<t-button theme="primary" type="submit">
<icon name="time" /> {{ t('preview.countdown.start') }}
</t-button>
<t-button theme="default" variant="base" @click="emits('close')">
{{ t('preview.countdown.cancel') }}
</t-button>
</t-space>
</t-form-item>
</t-form>
</div>
</template>
</t-popup>
</template>

<script setup lang="ts">
const props = defineProps({
visible: {
type: Boolean,
require: true,
},
})
const emits = defineEmits('exit-preivew', 'close')
const { container } = useStore()
const popperOptions = {
modifiers: [
{
name: 'offset',
options: {
offset: [-10, 10],
},
},
],
}
const selectValue = $ref(null)
let hours = $ref(null)
let minutes = $ref(null)
let seconds = $ref(null)
const whenEnd = $ref('showEndMessage')
const options = [
{ label: t('preview.countdown.1hour'), value: 60 },
{ label: t('preview.countdown.45minutes'), value: 45 },
{ label: t('preview.countdown.30minutes'), value: 30 },
{ label: t('preview.countdown.15minutes'), value: 15 },
{ label: t('preview.countdown.10minutes'), value: 10 },
{ label: t('preview.countdown.5minutes'), value: 5 },
{ label: t('preview.countdown.custom'), value: null },
]
const countdownSelect = (value: number) => {
minutes = value
}
const countdownInfo = ref(t('preview.countdown.startCountdown'))
let messageBox: ReturnType<typeof useMessage> = null
let countdownInterval: ReturnType<typeof setInterval> | null = null
const resetCountdown = () => {
hours = null
minutes = null
seconds = null
}
const startCountdown = async () => {
messageBox?.close()
if (countdownInterval !== null) {
clearInterval(countdownInterval)
}
const totalSeconds =
(hours || 0) * 3600 + (minutes || 0) * 60 + (seconds || 0)
if (totalSeconds <= 0) {
messageBox = await useMessage('error', t('preview.countdown.error'))
return
}
let remainingTime = totalSeconds
messageBox = await useMessage('info', {
content: countdownInfo,
duration: 0,
closeBtn: true,
zIndex: 9999,
onClose() {
resetCountdown()
},
})
countdownInterval = setInterval(() => {
if (remainingTime <= 0) {
messageBox?.close()
resetCountdown()
if (countdownInterval !== null) {
clearInterval(countdownInterval)
}
if (whenEnd === 'showEndMessage') {
useMessage('warning', {
content: t('preview.countdown.endCountdown'),
duration: 0,
closeBtn: true,
zIndex: 9999,
})
}
if (whenEnd === 'exitPreview') {
emits('exit-preivew')
}
return
}
remainingTime--
// FIXME: 英文单复数
countdownInfo.value = `${t('preview.countdown.remaining')}: ${Math.floor(remainingTime / 3600)} ${t('preview.countdown.hours')} ${Math.floor((remainingTime % 3600) / 60)} ${t('preview.countdown.minutes')} ${remainingTime % 60} ${t('preview.countdown.seconds')}`
}, 1000)
emits('close')
}
onBeforeUnmount(() => {
if (countdownInterval !== null) {
clearInterval(countdownInterval)
}
messageBox?.close()
})
</script>

<style lang="less" scoped>
.umo-preview-countdown {
padding: 29px 30px 26px;
width: 320px;
cursor: default;
:deep(.umo-form) {
&__item {
&:not(:last-child) {
margin-bottom: 15px;
}
.umo-radio-group {
margin-top: 5px;
}
.umo-button__text {
display: flex;
align-items: center;
.umo-icon {
font-size: 16px;
margin-right: 5px;
}
}
}
}
&-input {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 5px;
:deep(.umo-input-number) {
width: 78px !important;
}
}
}
</style>
24 changes: 22 additions & 2 deletions src/components/statusbar/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,17 @@
</div>
</div>
<div v-else class="umo-preview-bar">
<statusbar-countdown
:visible="countdownSetting"
@visible-change="(visible: boolean) => (countdownSetting = visible)"
@exit-preivew="exitPreview"
@close="countdownSetting = false"
>
<div class="item" :class="{ active: countdownSetting }">
<icon name="time" />
{{ t('preview.countdown.title') }}
</div>
</statusbar-countdown>
<div
class="item"
:class="{ active: page.preview?.laserPointer }"
Expand Down Expand Up @@ -354,9 +365,19 @@ const togglePreview = () => {
zoomableContainer.scrollTop = 0
}
}
const exitPreview = () => {
if (page.value.preview.enabled) {
page.value.preview ??= {}
page.value.preview.enabled = false
}
}
onMounted(() => {
useHotkeys('f5', togglePreview)
})
// 演示模式倒计时
const countdownSetting = $ref(false)
watch(
() => page.value.preview?.enabled,
(value: boolean) => {
Expand All @@ -373,8 +394,7 @@ watch(
() => fullscreen?.isFullscreen?.value,
(value: boolean) => {
if (!value) {
page.value.preview ??= {}
page.value.preview.enabled = false
exitPreview()
}
},
{ deep: true },
Expand Down
24 changes: 24 additions & 0 deletions src/locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,30 @@
"preview": {
"title": "Presentation",
"disable": "Exit Presentation",
"countdown": {
"title": "Countdown",
"select": "Select",
"selectTip": "Select the countdown duration",
"custom": "Custom",
"hours": "Hours",
"minutes": "Minutes",
"seconds": "Seconds",
"whenEnd": "When End",
"showEndMessage": "Show end message",
"exitPreview": "Exit presentation mode",
"start": "Start",
"cancel": "Cancel",
"1hour": "1 Hour",
"45minutes": "45 Minutes",
"30minutes": "30 Minutes",
"15minutes": "15 Minutes",
"10minutes": "10 Minutes",
"5minutes": "5 Minutes",
"startCountdown": "Start Countdown",
"error": "Please set a valid countdown time",
"endCountdown": "Countdown has ended",
"remaining": "Remaining time"
},
"laserPointer": "Laser Pointer",
"exit": "Exit"
},
Expand Down
24 changes: 24 additions & 0 deletions src/locales/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,30 @@
"preview": {
"title": "演示模式",
"disable": "关闭演示模式",
"countdown": {
"title": "倒计时",
"select": "快速选择",
"selectTip": "请选择倒计时长",
"custom": "自定义",
"hours": "",
"minutes": "",
"seconds": "",
"whenEnd": "结束时",
"showEndMessage": "弹出演示结束提示信息",
"exitPreview": "直接退出演示模式",
"start": "开始倒计时",
"cancel": "取消",
"1hour": "1小时",
"45minutes": "45分钟",
"30minutes": "半小时",
"15minutes": "15分钟",
"10minutes": "10分钟",
"5minutes": "5分钟",
"startCountdown": "开始倒计时",
"error": "请设置有效的倒计时时间",
"endCountdown": "倒计时已结束",
"remaining": "倒计时剩余"
},
"laserPointer": "激光笔",
"exit": "退出"
},
Expand Down
3 changes: 3 additions & 0 deletions types/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ declare module 'vue' {
Modal: typeof import('./../src/components/modal.vue')['default']
PageOptions: typeof import('./../src/components/page-options.vue')['default']
Statusbar: typeof import('./../src/components/statusbar/index.vue')['default']
StatusbarCountdown: typeof import('./../src/components/statusbar/countdown.vue')['default']
StatusbarShortcuts: typeof import('./../src/components/statusbar/shortcuts.vue')['default']
TBackTop: typeof import('tdesign-vue-next/esm')['BackTop']
TButton: typeof import('tdesign-vue-next/esm')['Button']
Expand Down Expand Up @@ -179,10 +180,12 @@ declare module 'vue' {
TOption: typeof import('tdesign-vue-next/esm')['Option']
TOptionGroup: typeof import('tdesign-vue-next/esm')['OptionGroup']
TPopup: typeof import('tdesign-vue-next/esm')['Popup']
TRadio: typeof import('tdesign-vue-next/esm')['Radio']
TRadioButton: typeof import('tdesign-vue-next/esm')['RadioButton']
TRadioGroup: typeof import('tdesign-vue-next/esm')['RadioGroup']
TSelect: typeof import('tdesign-vue-next/esm')['Select']
TSlider: typeof import('tdesign-vue-next/esm')['Slider']
TSpace: typeof import('tdesign-vue-next/esm')['Space']
TTextarea: typeof import('tdesign-vue-next/esm')['Textarea']
TTooltip: typeof import('tdesign-vue-next/esm')['Tooltip']
TWatermark: typeof import('tdesign-vue-next/esm')['Watermark']
Expand Down

0 comments on commit 48ec4f1

Please sign in to comment.