8 Commits

Author SHA1 Message Date
jacy 9b8e3f1347 Merge pull request 'v3.5.2 大屏调整' (#23) from dev into main
CI / verify (push) Has been cancelled
Reviewed-on: https://git.yqidian.com/jacy/dukang/pulls/23
2026-08-20 12:01:21 +08:00
jacy a8a37fbc4a Merge pull request 'v3.5.2 大屏调整' (#22) from dev_jacy into dev
CI / verify (pull_request) Has been cancelled
Reviewed-on: https://git.yqidian.com/jacy/dukang/pulls/22
2026-08-20 12:00:57 +08:00
jacy 1440a59fc8 Merge pull request 'Dev' (#21) from dev into main
CI / verify (push) Has been cancelled
Reviewed-on: https://git.yqidian.com/jacy/dukang/pulls/21
2026-08-20 00:54:54 +08:00
jacy e34416a9b3 Merge pull request 'Dev jacy mock 支付调整' (#20) from dev_jacy into dev
CI / verify (pull_request) Has been cancelled
Reviewed-on: https://git.yqidian.com/jacy/dukang/pulls/20
2026-08-20 00:54:17 +08:00
jacy 11659484c6 Merge pull request 'v3.5.1 版本更新' (#19) from dev into main
CI / verify (push) Has been cancelled
Reviewed-on: https://git.yqidian.com/jacy/dukang/pulls/19
2026-08-19 16:01:31 +08:00
jacy 6c44e9c56f Merge pull request 'v3.5.1 版本更新' (#18) from dev_jacy into dev
CI / verify (pull_request) Has been cancelled
Reviewed-on: https://git.yqidian.com/jacy/dukang/pulls/18
2026-08-19 16:00:56 +08:00
jacy 0fb7ab7abb Merge pull request 'v3.4.18' (#17) from dev into main
CI / verify (push) Has been cancelled
Reviewed-on: https://git.yqidian.com/jacy/dukang/pulls/17
2026-08-17 23:45:19 +08:00
jacy 905e145af3 Merge pull request 'v3.4.18' (#16) from dev_jacy into dev
CI / verify (pull_request) Has been cancelled
Reviewed-on: https://git.yqidian.com/jacy/dukang/pulls/16
2026-08-17 23:45:00 +08:00
7 changed files with 28 additions and 103 deletions
+14 -9
View File
@@ -308,6 +308,14 @@ body:has(.big-screen-page),
box-sizing: border-box; box-sizing: border-box;
} }
.big-screen-list-head {
flex: 0 0 auto;
padding: 0 28px 10px 40px;
font-size: 15px;
color: rgba(170, 200, 230, 0.55);
letter-spacing: 2px;
}
.big-screen-list-body { .big-screen-list-body {
flex: 1 1 0%; flex: 1 1 0%;
min-height: 0; min-height: 0;
@@ -452,20 +460,16 @@ body:has(.big-screen-page),
} }
.big-screen-empty { .big-screen-empty {
position: absolute; display: flex;
left: 0; align-items: center;
right: 0; justify-content: center;
/* 全屏高度黄金分割点(≈0.382),略偏上 */ height: 100%;
top: 38.2%;
transform: translateY(-50%);
z-index: 2;
text-align: center; text-align: center;
color: rgba(145, 190, 230, 0.42); color: rgba(145, 190, 230, 0.42);
padding: 0 40px; padding: 48px 40px;
font-size: 60px; font-size: 60px;
letter-spacing: 6px; letter-spacing: 6px;
font-weight: 400; font-weight: 400;
pointer-events: none;
} }
.big-screen-fx { .big-screen-fx {
@@ -718,6 +722,7 @@ body:has(.big-screen-page),
.big-screen-clock { .big-screen-clock {
font-size: 56px; font-size: 56px;
} }
.big-screen-list-head,
.big-screen-row { .big-screen-row {
grid-template-columns: minmax(140px, 20%) minmax(0, 1fr) minmax(110px, 16%) minmax(130px, 18%); grid-template-columns: minmax(140px, 20%) minmax(0, 1fr) minmax(110px, 16%) minmax(130px, 18%);
font-size: 16px; font-size: 16px;
+13 -88
View File
@@ -1,8 +1,5 @@
import { useCallback, useEffect, useMemo, useRef, useState, type CSSProperties } from 'react'; import { useCallback, useEffect, useMemo, useRef, useState, type CSSProperties } from 'react';
import { request } from '../lib/api'; import { request } from '../lib/api';
import sfxTier1 from '../assets/big-screen/celebrate-t1.mp3';
import sfxTier2 from '../assets/big-screen/celebrate-t2.mp3';
import sfxTier3 from '../assets/big-screen/celebrate-t3.mp3';
export type BigScreenOrder = { export type BigScreenOrder = {
id: string; id: string;
@@ -24,57 +21,6 @@ const MIN_SCROLL_ROWS = 10;
const FX_DURATION_MS = 10_000; const FX_DURATION_MS = 10_000;
const WEEKDAYS = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']; const WEEKDAYS = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
const CELEBRATE_SFX: Record<AmountTier, string> = {
1: sfxTier1,
2: sfxTier2,
3: sfxTier3,
};
/** 浏览器自动播放策略:需用户手势后才能出声 */
let audioUnlocked = false;
const sharedAudio = typeof Audio !== 'undefined' ? new Audio() : null;
function unlockCelebrateAudio() {
if (audioUnlocked || !sharedAudio) return;
sharedAudio.muted = true;
sharedAudio.src = CELEBRATE_SFX[1];
void sharedAudio
.play()
.then(() => {
sharedAudio.pause();
sharedAudio.currentTime = 0;
sharedAudio.muted = false;
audioUnlocked = true;
})
.catch(() => {
/* 等待下次手势 */
});
}
function playCelebrateSfx(tier: AmountTier): () => void {
if (!sharedAudio) return () => undefined;
try {
sharedAudio.pause();
sharedAudio.currentTime = 0;
sharedAudio.src = CELEBRATE_SFX[tier];
sharedAudio.volume = tier === 3 ? 0.85 : tier === 2 ? 0.75 : 0.65;
sharedAudio.muted = false;
void sharedAudio.play().catch(() => {
/* 未解锁时静默失败 */
});
} catch {
/* ignore */
}
return () => {
try {
sharedAudio.pause();
sharedAudio.currentTime = 0;
} catch {
/* ignore */
}
};
}
function pad(n: number) { function pad(n: number) {
return String(n).padStart(2, '0'); return String(n).padStart(2, '0');
} }
@@ -95,20 +41,11 @@ function formatOrderTime(iso: string): string {
} }
} }
// function formatAmount(n: number): string {
// return Number(n || 0).toLocaleString('zh-CN', {
// minimumFractionDigits: 0,
// maximumFractionDigits: 0,
// });
// }
function formatAmount(n: number): string { function formatAmount(n: number): string {
const num = Number(n || 0); return Number(n || 0).toLocaleString('zh-CN', {
// 先保留两位小数,再分割处理 minimumFractionDigits: 0,
const fixed = num.toFixed(2); maximumFractionDigits: 0,
const [intStr, decStr] = fixed.split('.'); });
const intFormatted = Number(intStr).toLocaleString('zh-CN');
return decStr === '00' ? intFormatted : `${intFormatted}.${decStr}`;
} }
function amountCssClass(payAmount: number, base: string): string { function amountCssClass(payAmount: number, base: string): string {
@@ -238,11 +175,6 @@ function CelebrateFx({ order, onDone }: { order: BigScreenOrder; onDone: () => v
return () => window.clearTimeout(timer); return () => window.clearTimeout(timer);
}, [order.id]); }, [order.id]);
useEffect(() => {
const stop = playCelebrateSfx(tier);
return stop;
}, [order.id, tier]);
useEffect(() => { useEffect(() => {
if (tier !== 3) return; if (tier !== 3) return;
const start = performance.now(); const start = performance.now();
@@ -416,17 +348,6 @@ export default function BigScreenPage() {
return () => clearInterval(id); return () => clearInterval(id);
}, [fetchData]); }, [fetchData]);
useEffect(() => {
unlockCelebrateAudio();
const unlock = () => unlockCelebrateAudio();
window.addEventListener('pointerdown', unlock, { once: true });
window.addEventListener('keydown', unlock, { once: true });
return () => {
window.removeEventListener('pointerdown', unlock);
window.removeEventListener('keydown', unlock);
};
}, []);
useEffect(() => { useEffect(() => {
const html = document.documentElement; const html = document.documentElement;
const prevHtml = html.style.overflow; const prevHtml = html.style.overflow;
@@ -491,8 +412,16 @@ export default function BigScreenPage() {
<LiveClock /> <LiveClock />
<div className="big-screen-list"> <div className="big-screen-list">
<div className="big-screen-list-head">
<span></span>
<span></span>
<span></span>
<span></span>
</div>
<div className="big-screen-list-body" ref={viewportRef}> <div className="big-screen-list-body" ref={viewportRef}>
{items.length === 0 ? null : ( {items.length === 0 ? (
<div className="big-screen-empty">{loadError || '当日暂无订单'}</div>
) : (
<div <div
className={`big-screen-track${rolling ? ' is-rolling' : ''}${paused ? ' is-paused' : ''}`} className={`big-screen-track${rolling ? ' is-rolling' : ''}${paused ? ' is-paused' : ''}`}
style={rolling ? ({ ['--marquee-ms']: `${marqueeMs}ms` } as CSSProperties) : undefined} style={rolling ? ({ ['--marquee-ms']: `${marqueeMs}ms` } as CSSProperties) : undefined}
@@ -509,10 +438,6 @@ export default function BigScreenPage() {
</div> </div>
</div> </div>
{items.length === 0 ? (
<div className="big-screen-empty">{loadError || '当日暂无订单'}</div>
) : null}
{celebrate ? ( {celebrate ? (
<CelebrateFx <CelebrateFx
key={celebrate.id} key={celebrate.id}
-6
View File
@@ -1,6 +0,0 @@
/// <reference types="vite/client" />
declare module '*.mp3' {
const src: string;
export default src;
}
@@ -32,6 +32,7 @@ export class AdminOrdersService {
startOfToday.setHours(0, 0, 0, 0); startOfToday.setHours(0, 0, 0, 0);
const orders = await this.prisma.order.findMany({ const orders = await this.prisma.order.findMany({
where: { where: {
payAmount: { gte: 100 },
payStatus: 'PAID', payStatus: 'PAID',
paidAt: { gte: startOfToday }, paidAt: { gte: startOfToday },
}, },