0808 morning
|
Before Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 206 KiB After Width: | Height: | Size: 142 KiB |
|
Before Width: | Height: | Size: 208 KiB After Width: | Height: | Size: 150 KiB |
|
Before Width: | Height: | Size: 219 KiB After Width: | Height: | Size: 158 KiB |
|
Before Width: | Height: | Size: 175 KiB After Width: | Height: | Size: 127 KiB |
@ -471,7 +471,7 @@ export interface QuizQuestion {
|
||||
answers: string[]; // 正确答案(单选也使用数组,方便统一处理)
|
||||
}
|
||||
|
||||
// ARC Retail 弹性福利平台答题竞赛题库
|
||||
// ARC Retail 弹性福利平台答题竞赛题库 (更新版)
|
||||
export const arcRetailQuiz: QuizQuestion[] = [
|
||||
// --- 一、单选题 ---
|
||||
{
|
||||
@ -491,19 +491,19 @@ export const arcRetailQuiz: QuizQuestion[] = [
|
||||
type: 'single',
|
||||
question: '弹性福利平台上线后,健康福利的变化是?',
|
||||
options: [
|
||||
'只能选体检',
|
||||
'可在体检和牙齿清洁中自主选择',
|
||||
'同时免费领体检 + 洁牙',
|
||||
'只有体检项目',
|
||||
'可在体检和洁牙中自主选择',
|
||||
'享有中医理疗',
|
||||
'取消健康福利',
|
||||
],
|
||||
answers: ['可在体检和牙齿清洁中自主选择'],
|
||||
answers: ['可在体检和洁牙中自主选择'],
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
type: 'single',
|
||||
question: '员工想知道自己有多少福利积分,最便捷的方式是?',
|
||||
options: [
|
||||
'每月找 HR 查',
|
||||
'店经理告知',
|
||||
'登录弹性福利平台实时查看',
|
||||
'等年底邮件通知',
|
||||
'问同事打听',
|
||||
@ -513,15 +513,15 @@ export const arcRetailQuiz: QuizQuestion[] = [
|
||||
{
|
||||
id: 4,
|
||||
type: 'single',
|
||||
question: '以下哪项是 2025 年 新增 的福利兑换项目?',
|
||||
options: ['节日礼品', '员工洁牙', '法定社保', '商业保险'],
|
||||
answers: ['员工洁牙'],
|
||||
question: '以下哪项是弹性福利平台上线后,新增的福利项目?',
|
||||
options: ['节日礼品', '员工洁牙卡', '法定社保', '商业保险'],
|
||||
answers: ['员工洁牙卡'],
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
type: 'single',
|
||||
question: '福利积分 主要通过什么方式获取?',
|
||||
options: ['日常加班', '参与景仰计划', '绩效满分', '达成销售业绩'],
|
||||
question: '福利积分通过什么方式获取?',
|
||||
options: ['公益活动', '参与景仰计划', '绩效满分', '达成销售业绩'],
|
||||
answers: ['参与景仰计划'],
|
||||
},
|
||||
{
|
||||
@ -536,12 +536,12 @@ export const arcRetailQuiz: QuizQuestion[] = [
|
||||
type: 'single',
|
||||
question: '如果觉得近期压力大,心情低落,哪一项解决路径是公司平台提供的?',
|
||||
options: [
|
||||
'可以求助公司专业的EAP服务',
|
||||
'可以求助公司专业的EAP服务 (员工360关爱计划)',
|
||||
'和朋友吐苦水',
|
||||
'大吃大喝',
|
||||
'睡一觉',
|
||||
],
|
||||
answers: ['可以求助公司专业的EAP服务'],
|
||||
answers: ['可以求助公司专业的EAP服务 (员工360关爱计划)'],
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
|
||||
@ -69,56 +69,12 @@ const giftProgress = ref(0); // max = giftList.length - 1
|
||||
const timeSpent = ref(0)
|
||||
const showEndAni = ref(false); // 显示小太阳后段效果
|
||||
const showShareMask = ref(false);
|
||||
const leaderBoard = ref([
|
||||
{
|
||||
"name": "测试用户",
|
||||
"time": 1.7116000000000005,
|
||||
"region": "奥莱",
|
||||
"store": "北京斯普瑞斯"
|
||||
},
|
||||
{
|
||||
"name": "阿迪斯",
|
||||
"time": 1.9645,
|
||||
"region": "奥莱",
|
||||
"store": "北京斯普瑞斯"
|
||||
},
|
||||
{
|
||||
"name": "Mike",
|
||||
"time": 45.5,
|
||||
"region": "大北区",
|
||||
"store": "大连恒隆广场"
|
||||
},
|
||||
{
|
||||
"name": "小白",
|
||||
"time": 83.2,
|
||||
"region": "大西区",
|
||||
"store": "太原万象城"
|
||||
},
|
||||
{
|
||||
"name": "小明",
|
||||
"time": 98.2,
|
||||
"region": "大西区",
|
||||
"store": "太原万象城"
|
||||
},
|
||||
{
|
||||
"name": "Elena",
|
||||
"time": 122.2,
|
||||
"region": "大南区",
|
||||
"store": "深圳湾万象城"
|
||||
},
|
||||
{
|
||||
"name": "Elena2",
|
||||
"time": 122.4,
|
||||
"region": "大南区",
|
||||
"store": "深圳湾万象城"
|
||||
},
|
||||
{
|
||||
"name": "Elena3",
|
||||
"time": 122.7,
|
||||
"region": "大南区",
|
||||
"store": "深圳湾万象城"
|
||||
}
|
||||
])
|
||||
const leaderBoard = ref(Array(8).fill({
|
||||
name: 'Loading...',
|
||||
time: 0,
|
||||
region: 'Loading...',
|
||||
store: 'Loading...'
|
||||
})); // 初始加载时的占位数据
|
||||
const showTitleIcon = ref(true)
|
||||
|
||||
async function shoot() {
|
||||
@ -238,7 +194,7 @@ function gameLoop(currentTime = 0) {
|
||||
await wait(500);
|
||||
|
||||
sunPos.value[1] = 124;
|
||||
await wait(0);
|
||||
await wait(100);
|
||||
transitionOn.value = true;
|
||||
while (sunPos.value[1] > 66) {
|
||||
sunPos.value[1] -= SPEED * (deltaTime / 16.67) * 6;
|
||||
@ -417,6 +373,10 @@ async function gameEnd() {
|
||||
easing: 'ease-in-out',
|
||||
fill: 'forwards',
|
||||
}).finished
|
||||
.then(() => {
|
||||
sunEndEle.value?.jumpTo('进入');
|
||||
showEndAni.value = true;
|
||||
});
|
||||
|
||||
gameState.value = GAME_STATE.gameEnd;
|
||||
|
||||
@ -427,12 +387,6 @@ async function gameEnd() {
|
||||
showTitleIcon.value = false;
|
||||
});
|
||||
|
||||
await wait(1800)
|
||||
|
||||
showEndAni.value = true;
|
||||
|
||||
sunEndEle.value?.jumpTo('进入');
|
||||
|
||||
}
|
||||
|
||||
async function startQuiz() {
|
||||
@ -527,8 +481,15 @@ async function handleFinishQuiz(time: number, choices: ('none' | 'selected' | 'c
|
||||
}).then(_ => {
|
||||
fetch('/api/score', {
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
}).then(res => res.json()).then(data => leaderBoard.value = data);
|
||||
})
|
||||
}).then(res => res.json()).then(data => leaderBoard.value = data)
|
||||
.catch(err => {
|
||||
console.error('Error fetching leaderboard:', err);
|
||||
alert('获取排行榜失败,请稍后再试。' + err.message);
|
||||
});
|
||||
}).catch(err => {
|
||||
console.error('Error submitting score:', err);
|
||||
alert('提交分数失败,请稍后再试。' + err.message);
|
||||
});
|
||||
|
||||
await wait(1500);
|
||||
sunEndEle.value?.jumpTo('向上进入')
|
||||
@ -601,22 +562,23 @@ window.addEventListener('keydown', (e) => {
|
||||
<div class="sun-ani-end-wrapper abs" style="pointer-events: none; inset: 0; width: 100%;z-index: 2;"
|
||||
:style="{ display: showEndAni ? 'block' : 'none' }">
|
||||
|
||||
<AniEle :url="assets.ani.后段效果" :auto-play="false" ref="sun-ani-end" class="sun-ani-end" :height="2462" :width="1179" :rules="[
|
||||
// 0 - 97
|
||||
{ name: '进入', frame: 98, loop: 1, pauseAfter: false, duration: 33 },
|
||||
// 98 - 129
|
||||
{ name: '落下前循环', frame: 32, loop: 0, pauseAfter: true, duration: 33 },
|
||||
// 130 - 161
|
||||
{ name: '落下出屏幕', frame: 32, loop: 1, pauseAfter: true, duration: 33 },
|
||||
// 162 - 227
|
||||
{ name: '向上进入', frame: 66, loop: 1, pauseAfter: false, duration: 33 },
|
||||
// 228 - 293
|
||||
{ name: '左右循环', frame: 66, loop: 0, pauseAfter: true, duration: 33 },
|
||||
// 294 - 326
|
||||
{ name: '落下进蹦床', frame: 33, loop: 1, pauseAfter: false, duration: 33 },
|
||||
// 327 - 358
|
||||
{ name: '蹦床弹跳循环', frame: 32, loop: 0, pauseAfter: false, duration: 33 },
|
||||
]" style="width: 100%; height: auto;" />
|
||||
<AniEle :url="assets.ani.后段效果" :auto-play="false" ref="sun-ani-end" class="sun-ani-end" :height="2462"
|
||||
:width="1179" :rules="[
|
||||
// 0 - 97
|
||||
{ name: '进入', frame: 98, loop: 1, pauseAfter: false, duration: 33 },
|
||||
// 98 - 129
|
||||
{ name: '落下前循环', frame: 32, loop: 0, pauseAfter: true, duration: 33 },
|
||||
// 130 - 161
|
||||
{ name: '落下出屏幕', frame: 32, loop: 1, pauseAfter: true, duration: 33 },
|
||||
// 162 - 227
|
||||
{ name: '向上进入', frame: 66, loop: 1, pauseAfter: false, duration: 33 },
|
||||
// 228 - 293
|
||||
{ name: '左右循环', frame: 66, loop: 0, pauseAfter: true, duration: 33 },
|
||||
// 294 - 326
|
||||
{ name: '落下进蹦床', frame: 33, loop: 1, pauseAfter: false, duration: 33 },
|
||||
// 327 - 358
|
||||
{ name: '蹦床弹跳循环', frame: 32, loop: 0, pauseAfter: false, duration: 33 },
|
||||
]" style="width: 100%; height: auto;" />
|
||||
</div>
|
||||
|
||||
<Transition name="fade">
|
||||
|
||||
@ -22,23 +22,25 @@ QRCode.toDataURL(`https://arcteryx-game-demo.xn--876a.net`, {
|
||||
console.error('生成二维码失败:', err);
|
||||
});
|
||||
|
||||
const posterP1Ele = useTemplateRef('gui-ani-end-post-p1');
|
||||
const posterP2Ele = useTemplateRef('gui-ani-end-post-p2');
|
||||
|
||||
const emit = defineEmits(['resetGame', 'showShareMask']);
|
||||
|
||||
const hasP2AnimationPlayed = ref(false);
|
||||
const currentPosterIndex = ref(0);
|
||||
|
||||
onMounted(() => {
|
||||
// 在最后一页显示后设置 Intersection Observer 监听 p2 元素
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting && !hasP2AnimationPlayed.value) {
|
||||
// p2 进入视口且动画未播放过
|
||||
if (entry.isIntersecting) {
|
||||
currentPosterIndex.value = 1; // p2 进入视口
|
||||
|
||||
if (hasP2AnimationPlayed.value) return
|
||||
hasP2AnimationPlayed.value = true;
|
||||
posterP2Ele.value?.jumpTo('p2');
|
||||
console.log('Poster P2 animation triggered');
|
||||
|
||||
// 停止观察,因为只需要触发一次
|
||||
observer.unobserve(entry.target);
|
||||
}
|
||||
});
|
||||
}, {
|
||||
@ -46,7 +48,7 @@ onMounted(() => {
|
||||
rootMargin: '0px'
|
||||
});
|
||||
|
||||
// 观察 p2 元素
|
||||
const p1Element = posterP1Ele.value?.$el;
|
||||
const p2Element = posterP2Ele.value?.$el;
|
||||
if (p2Element) {
|
||||
observer.observe(p2Element);
|
||||
@ -55,21 +57,45 @@ onMounted(() => {
|
||||
console.warn('P2 element not found');
|
||||
}
|
||||
|
||||
const observerP1 = new IntersectionObserver((entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
currentPosterIndex.value = 0; // p1 进入视口
|
||||
}
|
||||
});
|
||||
}, {
|
||||
threshold: 0.5, // 当 50% 的元素进入视口时触发
|
||||
rootMargin: '0px'
|
||||
});
|
||||
|
||||
if (p1Element) {
|
||||
observerP1.observe(p1Element);
|
||||
console.log('Started observing P1 element');
|
||||
} else {
|
||||
console.warn('P1 element not found');
|
||||
}
|
||||
|
||||
// 8秒后如果用户没有看过p2,自动滚动到p2
|
||||
setTimeout(() => {
|
||||
if (!hasP2AnimationPlayed.value) {
|
||||
console.log('Auto scrolling to P2 after 8 seconds');
|
||||
const posterContainer = document.querySelector('.poster-container');
|
||||
if (posterContainer) {
|
||||
// 滚动到p2位置(第二个元素,占总宽度的50%)
|
||||
posterContainer.scrollTo({
|
||||
left: posterContainer.scrollWidth * 0.5,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
}
|
||||
scrollToPoster(1); // 滚动到 p2
|
||||
}
|
||||
}, 8000); // 8秒 = 8000毫秒
|
||||
})
|
||||
|
||||
function scrollToPoster(index: number) {
|
||||
const posterContainer = document.querySelector('.poster-container');
|
||||
if (posterContainer) {
|
||||
// 滚动到p2位置(第二个元素,占总宽度的50%)
|
||||
posterContainer.scrollTo({
|
||||
left: posterContainer.scrollWidth * 0.5 *index,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -82,7 +108,7 @@ onMounted(() => {
|
||||
style="position: absolute;top: 29.3%;left: 37.2%;width: 57.3%; height: 38%; overflow-x: auto; overflow-y: hidden; pointer-events: all; scroll-snap-type: x mandatory;">
|
||||
<div class="poster-wrapper" style="display: flex; width: 200%; height: 100%; flex-direction: row;"
|
||||
dir="ltr">
|
||||
<AniEle :url="assets.ani.海报p1" ref="gui-ani-end-post-p1 "
|
||||
<AniEle :url="assets.ani.海报p1" ref="gui-ani-end-post-p1"
|
||||
style="width: 50%; flex-shrink: 0; scroll-snap-align: start;" class="gui-ani-end-post" :height="940"
|
||||
:width="671" :rules="[
|
||||
{ name: 'p1', frame: 32, loop: 1, pauseAfter: true, duration: 33 },
|
||||
@ -95,16 +121,23 @@ onMounted(() => {
|
||||
]" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="dot" style="position: absolute;top: 29.3%;left: 37.2%;width: 57.3%; height: 38%; ">
|
||||
<div class="dot-item" @click="scrollToPoster(0)" :class="{ active: currentPosterIndex === 0 }"></div>
|
||||
<div class="dot-item" @click="scrollToPoster(1)" :class="{ active: currentPosterIndex === 1 }"></div>
|
||||
</div>
|
||||
<AniEle :url="assets.ani.线" ref="gui-ani-end" class="gui-ani-end" :height="2462" :width="1179" :rules="[
|
||||
{ name: 'all', frame: 54, loop: 1, pauseAfter: false, duration: 16 },
|
||||
]" style="width: 100%; height: auto;position: absolute;inset: 0;pointer-events: none;" />
|
||||
<img src="../../assets/game/床.webp" alt="" class="abs last-bed" style="width: 66vw;bottom: -19%;left: 17%;">
|
||||
|
||||
<img src="../../assets/game/分享海报.webp" alt="" class="abs" @click="AudioEffects.play('按钮音效'); emit('showShareMask')"
|
||||
<img src="../../assets/game/分享海报.webp" alt="" class="abs"
|
||||
@click="AudioEffects.play('按钮音效'); emit('showShareMask')"
|
||||
style="width: 34vw;bottom: -19%;left: 15%;animation: last-btn-in 0.3s ease-out forwards; cursor: pointer; pointer-events: all;">
|
||||
<img src="../../assets/game/再玩一次.webp" alt="" class="abs" @click="AudioEffects.play('按钮音效'); emit('resetGame')"
|
||||
style="width: 34vw;bottom: -19%;left: 51%;animation: last-btn-in 0.3s ease-out forwards; cursor: pointer; animation-delay: 200ms; pointer-events: all;">
|
||||
|
||||
|
||||
|
||||
<div class="time-spent abs" style="left: 10%; top:44%;width: 24%;height: 23%; display: flex;
|
||||
align-items: center; flex-direction: column;">
|
||||
<div class="line-1"
|
||||
@ -118,7 +151,7 @@ onMounted(() => {
|
||||
|
||||
<div class="time"
|
||||
style="font-size: 10vw;animation: line-in 0.5s ease-out 0.8s forwards;transform: translateX(-230%);">
|
||||
{{ timeSpent}}</div>
|
||||
{{ timeSpent }}</div>
|
||||
|
||||
<img :src="qrcodeUrl" alt=""
|
||||
style="width: 100%;bottom:7%;position: absolute;animation: line-in 0.5s ease-out 1s forwards;transform: translateX(-230%);">
|
||||
@ -137,6 +170,26 @@ onMounted(() => {
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.dot {
|
||||
transform: none;
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
padding-bottom: 1vw;
|
||||
gap: 1vw;
|
||||
}
|
||||
|
||||
.dot .dot-item {
|
||||
background-color: #b2d8ff;
|
||||
height: 1.5vw;
|
||||
width: 1.5vw;
|
||||
pointer-events: all;
|
||||
cursor: pointer;
|
||||
|
||||
&.active {
|
||||
background-color: #78b0e9;
|
||||
}
|
||||
}
|
||||
|
||||
.poster-container {
|
||||
scrollbar-width: none;
|
||||
|
||||
@ -152,5 +205,4 @@ onMounted(() => {
|
||||
.gui-ani-end-post {
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
</style>
|
||||
@ -524,8 +524,8 @@ img {
|
||||
}
|
||||
|
||||
.store-list-container {
|
||||
margin-bottom: 12vw;
|
||||
height: 35vh;
|
||||
margin-bottom: 10vw;
|
||||
height: 60vw;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
mask-image: linear-gradient(to bottom, transparent 0%, black 15%, black 85%, transparent 100%);
|
||||
|
||||