Skip to content

Commit c493190

Browse files
committed
feat: 添加历史代码
1 parent 67665ca commit c493190

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+13189
-0
lines changed

.gitignore

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Nuxt dev/build outputs
2+
.output
3+
.nuxt
4+
.nitro
5+
.cache
6+
dist
7+
8+
# Node dependencies
9+
node_modules
10+
11+
# Logs
12+
logs
13+
*.log
14+
15+
# Misc
16+
.DS_Store
17+
.fleet
18+
.idea
19+
20+
# Local env files
21+
.env
22+
.env.*
23+
!.env.example
24+
25+
.vscode/**
26+
!.vscode/extensions.json
27+
public/iconify

.npmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
shamefully-hoist=true

.vscode/extensions.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"recommendations": [
3+
"dbaeumer.vscode-eslint",
4+
"vue.volar",
5+
"vue.vscode-typescript-vue-plugin",
6+
"tauri-apps.tauri-vscode"
7+
]
8+
}

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
# ExCaller
2+
3+
Excaller 是一款功能丰富的随机点名工具。

app.vue

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<script lang="ts" setup>
2+
import { promiseTimeout } from '@vueuse/core'
3+
import { zhCN } from 'naive-ui'
4+
5+
const loading = ref(true)
6+
const show = ref(false)
7+
8+
promiseTimeout(1600).then(() => {
9+
loading.value = false
10+
gc()
11+
})
12+
promiseTimeout(2000).then(() => show.value = true)
13+
</script>
14+
15+
<template>
16+
<NaiveConfig
17+
class="transition-opacity"
18+
:style="{ opacity: show ? 1 : 0 }"
19+
:locale="zhCN"
20+
>
21+
<ExCaller :config="useUserConfig().value" />
22+
</NaiveConfig>
23+
<Loading
24+
v-if="!show"
25+
class="loading"
26+
:style="{ opacity: loading ? 1 : 0 }"
27+
:show="loading"
28+
/>
29+
</template>
30+
31+
<style lang="postcss">
32+
#__nuxt,
33+
#__nuxt > .n-config-provider {
34+
@apply w-screen h-screen;
35+
}
36+
.loading {
37+
transition: all .4s;
38+
background: center / auto no-repeat url("/loading.png");
39+
}
40+
</style>

app/spa-loading-template.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<style>
2+
#__nuxt {
3+
background: center / auto no-repeat url("/logo.png");
4+
}
5+
</style>

components/ex-caller.vue

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<script lang="ts" setup>
2+
const props = defineProps<{
3+
/** 用户配置。 */
4+
config: UserConfig
5+
}>()
6+
7+
const config = toRef(props, 'config')
8+
provide(CONTEXT_CONFIG_KEY, config)
9+
10+
function getRollCall(options?: Partial<RollCallConfig>) {
11+
return useRollCall({ // 点名结果
12+
options: getGroupOptions(config.value.group),
13+
duration: config.value.interval,
14+
...options,
15+
})
16+
}
17+
const result = getRollCall()
18+
19+
const unstarted = ref(true) // 是否未开始过
20+
const showingResume = ref(false) // 是否正在播放动画
21+
22+
function handleStart() {
23+
result.value.start()
24+
unstarted.value = false
25+
}
26+
function handlePause() {
27+
if (!result.value?.isActive || showingResume.value)
28+
return
29+
showingResume.value = true
30+
result.value.pause()
31+
}
32+
33+
const showSettings = ref(false)
34+
function handleOpenSettings() {
35+
showSettings.value = true
36+
// 正在点名时打开设置,停止点名,不显示点名结果
37+
if (result.value.isActive)
38+
result.value.reset()
39+
}
40+
function handleSettingsClose() {
41+
// 设置更改时需要同步到实例,但需让当前显示的值不变,让用户无感
42+
result.value = getRollCall({
43+
defaultIndex: result.value.currentIndex,
44+
defaultValue: result.value.currentValue,
45+
}).value
46+
}
47+
</script>
48+
49+
<template>
50+
<ResultBoard
51+
v-bind="$attrs"
52+
v-model:showing-resume="showingResume"
53+
:value="result?.currentValue"
54+
:show-resume="!result?.isActive"
55+
:confetti="config.ui.confetti"
56+
@start="handleStart"
57+
@pause="handlePause"
58+
/>
59+
<NaiveIcon
60+
class="settings-button absolute text-gray-200 hover:text-gray-600 cursor-pointer"
61+
name="ep:setting"
62+
:size="24"
63+
@click="handleOpenSettings"
64+
/>
65+
<Settings v-model:show="showSettings" @close="handleSettingsClose" />
66+
</template>
67+
68+
<style lang="postcss" scoped>
69+
.settings-button {
70+
top: calc(env(safe-area-inset-top) + 8px);
71+
right: calc(env(safe-area-inset-right) + 8px);
72+
transition: all .3s;
73+
}
74+
</style>

components/loading.vue

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<script lang="ts" setup>
2+
defineProps<{
3+
/** 是否显示。 */
4+
show: boolean
5+
}>()
6+
</script>
7+
8+
<template>
9+
<div class="fixed top-0 w-screen h-screen" />
10+
</template>

components/result-board.vue

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
<script lang="ts" setup>
2+
import { promiseTimeout } from '@vueuse/core'
3+
4+
const props = withDefaults(defineProps<{
5+
/** 当前抽取到的值。 */
6+
value?: string
7+
/** 是否显示“继续抽取”按钮。 @default true */
8+
showResume?: boolean
9+
/** 是否正在播放“继续抽取”按钮的显示动画。 */
10+
showingResume?: boolean
11+
/** 显示“继续抽取”按钮后,是否显示彩带效果。 @default true */
12+
confetti?: boolean
13+
}>(), {
14+
showResume: true,
15+
confetti: true,
16+
})
17+
18+
const emit = defineEmits<{
19+
(ev: 'update:showingResume', showingResume: boolean): void
20+
/** 开始抽取。 */
21+
(ev: 'start'): void
22+
/** 暂停抽取。 */
23+
(ev: 'pause'): void
24+
}>()
25+
26+
const beforeAnimation = ref(false)
27+
watch(() => props.showingResume, async (v) => {
28+
if (!v)
29+
return
30+
// 停止后等待 1s
31+
beforeAnimation.value = true
32+
await promiseTimeout(1000)
33+
beforeAnimation.value = false
34+
35+
// 播放特效
36+
if (props.confetti)
37+
confetti.addConfetti()
38+
39+
// 再等待 1s 显示按钮
40+
await promiseTimeout(1000)
41+
if (!props.showingResume)
42+
return
43+
emit('update:showingResume', false)
44+
})
45+
46+
function handlePause() {
47+
if (props.showResume) // 已经暂停
48+
return
49+
emit('pause')
50+
}
51+
</script>
52+
53+
<template>
54+
<NFlex
55+
class="h-full items-center" :class="[showingResume && 'showing-resume']"
56+
vertical
57+
justify="center"
58+
@click="handlePause"
59+
>
60+
<NButton
61+
v-if="value === undefined"
62+
class="w-16 h-16"
63+
type="primary"
64+
round
65+
@click.stop="() => emit('start')"
66+
>
67+
<template #icon>
68+
<NaiveIcon class="ml-1" name="ant-design:caret-right-filled" :size="36" />
69+
</template>
70+
</NButton>
71+
<template v-else>
72+
<span class="current-value mb-2 text-5xl">
73+
{{ value }}
74+
</span>
75+
<NButton
76+
v-if="showResume && !beforeAnimation"
77+
class="resume-button"
78+
@click.stop="() => emit('start')"
79+
>
80+
继续点名
81+
<template #icon>
82+
<NaiveIcon name="ant-design:play-circle-filled" :size="20" />
83+
</template>
84+
</NButton>
85+
</template>
86+
</NFlex>
87+
</template>
88+
89+
<style lang="postcss" scoped>
90+
.showing-resume .resume-button {
91+
animation: show-resume-button .65s forwards;
92+
}
93+
@keyframes show-resume-button {
94+
from {
95+
@apply opacity-0;
96+
transform: translateY(16px);
97+
}
98+
to {
99+
@apply opacity-100;
100+
transform: translateY(0);
101+
}
102+
}
103+
</style>

components/settings.vue

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
<script lang="ts" setup>
2+
import { promiseTimeout } from '@vueuse/core'
3+
4+
defineProps<{
5+
/** 是否显示设置。 */
6+
show: boolean
7+
}>()
8+
const emit = defineEmits<{
9+
(ev: 'update:show', show: boolean): void
10+
/** 设置面板关闭。 */
11+
(ev: 'close'): void
12+
}>()
13+
const DRAWER_DEFAULT_WIDTH = 450
14+
const DRAWER_MIN_WIDTH = 300
15+
16+
const showNameList = ref(false)
17+
const showPlan = ref(false)
18+
19+
function handleClose() {
20+
showNameList.value = false
21+
emit('close')
22+
promiseTimeout(500).then(gc)
23+
}
24+
</script>
25+
26+
<template>
27+
<NDrawer
28+
:show="show"
29+
:default-width="DRAWER_DEFAULT_WIDTH"
30+
:min-width="DRAWER_MIN_WIDTH"
31+
resizable
32+
@click.stop
33+
@after-leave="handleClose"
34+
@update:show="(v) => $emit('update:show', v)"
35+
>
36+
<NDrawerContent closable>
37+
<NCard @click="showNameList = true">
38+
<NaiveIcon class="mr-1" name="ep:list" :size="18" />
39+
名单设置
40+
<NaiveIcon class="absolute right-3" name="ep:right" />
41+
</NCard>
42+
<NCard @click="showPlan = true">
43+
<NaiveIcon class="mr-1" name="ep:flag" :size="18" />
44+
计划设置
45+
<NaiveIcon class="absolute right-3" name="ep:right" />
46+
</NCard>
47+
<SettingsUi class="mt-6" />
48+
<template #header>
49+
设置
50+
</template>
51+
</NDrawerContent>
52+
</NDrawer>
53+
54+
<NDrawer
55+
v-model:show="showNameList"
56+
:default-width="DRAWER_DEFAULT_WIDTH"
57+
:min-width="DRAWER_MIN_WIDTH"
58+
resizable
59+
@click.stop
60+
>
61+
<NDrawerContent closable>
62+
<SettingsNameList @switch-group="gc" />
63+
<template #header>
64+
名单设置
65+
</template>
66+
</NDrawerContent>
67+
</NDrawer>
68+
69+
<NDrawer
70+
v-model:show="showPlan"
71+
:default-width="DRAWER_DEFAULT_WIDTH"
72+
:min-width="DRAWER_MIN_WIDTH"
73+
resizable
74+
@click.stop
75+
>
76+
<NDrawerContent closable>
77+
<SettingsPlan />
78+
<template #header>
79+
计划设置
80+
</template>
81+
</NDrawerContent>
82+
</NDrawer>
83+
</template>
84+
85+
<style lang="postcss" scoped>
86+
:deep() .n-card {
87+
@apply cursor-pointer mb-2;
88+
transition: box-shadow .5s;
89+
90+
&:hover {
91+
box-shadow: 0 0 4px 2px var(--n-border-color);
92+
transition: box-shadow .2s;
93+
}
94+
95+
> .n-card__content {
96+
@apply p-3 flex;
97+
}
98+
}
99+
</style>

0 commit comments

Comments
 (0)