feat 搭建架构骨架
This commit is contained in:
114
src/components/QrCodePanel.vue
Normal file
114
src/components/QrCodePanel.vue
Normal file
@@ -0,0 +1,114 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { useCountDown } from '@/composables/useCountDown'
|
||||
|
||||
interface Props {
|
||||
/** 二维码有效期(秒),默认 4:32 */
|
||||
expiresIn?: number
|
||||
size?: number
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
expiresIn: 272,
|
||||
size: 200,
|
||||
})
|
||||
|
||||
const emit = defineEmits<{ refresh: [] }>()
|
||||
|
||||
const { seconds, start } = useCountDown(props.expiresIn)
|
||||
const started = ref(false)
|
||||
|
||||
const expired = computed(() => started.value && seconds.value <= 0)
|
||||
|
||||
const countdownText = computed(() => {
|
||||
const m = Math.floor(seconds.value / 60)
|
||||
const s = seconds.value % 60
|
||||
return `${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`
|
||||
})
|
||||
|
||||
function refresh(): void {
|
||||
start(props.expiresIn)
|
||||
emit('refresh')
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
start(props.expiresIn)
|
||||
started.value = true
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="qr-panel">
|
||||
<div
|
||||
class="qr-panel__box"
|
||||
:class="{ 'is-expired': expired }"
|
||||
:style="{ width: `${size}px`, height: `${size}px` }"
|
||||
@click="refresh"
|
||||
>
|
||||
<!-- TODO(api): 二维码内容由支付/签署接口返回后渲染 -->
|
||||
<template v-if="!expired">
|
||||
<span class="qr-panel__icon">▦</span>
|
||||
<span class="qr-panel__tip">扫码支付/签署</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<span class="qr-panel__icon">⟳</span>
|
||||
<span class="qr-panel__tip">二维码已过期<br>点击刷新</span>
|
||||
</template>
|
||||
</div>
|
||||
<p class="qr-panel__status text-secondary">
|
||||
<template v-if="!expired">
|
||||
二维码将在 <strong class="qr-panel__count">{{ countdownText }}</strong> 后失效(过期可点击二维码刷新)
|
||||
</template>
|
||||
<template v-else>
|
||||
二维码已过期,请点击刷新
|
||||
</template>
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.qr-panel {
|
||||
.flex(column, center);
|
||||
gap: @spacing-sm;
|
||||
|
||||
&__box {
|
||||
.flex(column, center);
|
||||
gap: @spacing-xs;
|
||||
border: 1px solid @border-color;
|
||||
border-radius: @radius-base;
|
||||
background: @white;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.2s;
|
||||
|
||||
&:hover {
|
||||
border-color: @primary-color;
|
||||
}
|
||||
|
||||
&.is-expired {
|
||||
background: @bg-color;
|
||||
color: @text-color-secondary;
|
||||
}
|
||||
}
|
||||
|
||||
&__icon {
|
||||
font-size: 56px;
|
||||
color: @heading-color;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
&__tip {
|
||||
font-size: @font-size-sm;
|
||||
color: @text-color-secondary;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__status {
|
||||
font-size: @font-size-sm;
|
||||
}
|
||||
|
||||
&__count {
|
||||
color: @danger-color;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user