Compare commits
8 Commits
v3.5.2
..
9b8e3f1347
| Author | SHA1 | Date | |
|---|---|---|---|
| 9b8e3f1347 | |||
| a8a37fbc4a | |||
| 1440a59fc8 | |||
| e34416a9b3 | |||
| 11659484c6 | |||
| 6c44e9c56f | |||
| 0fb7ab7abb | |||
| 905e145af3 |
@@ -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;
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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}
|
||||||
|
|||||||
Vendored
-6
@@ -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 },
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user