From 398758800e2fb7facb385ee1501e4998ffdec2d8 Mon Sep 17 00:00:00 2001 From: yangxiangyuan Date: Thu, 30 Jul 2026 09:31:01 +0800 Subject: [PATCH] =?UTF-8?q?chore(=E9=82=AE=E4=BB=B6=E5=8F=91=E9=80=81?= =?UTF-8?q?=E6=A8=A1=E5=9D=97):=20=E6=9B=B4=E6=96=B0SQLite=E5=85=B1?= =?UTF-8?q?=E4=BA=AB=E5=86=85=E5=AD=98=E4=B8=B4=E6=97=B6=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/app_calendar_reminder/package.json | 3 +- apps/app_order_box/deploy-web.js | 64 ----- .../components/RunnerModal.tsx | 235 ++++++++++++------ apps/app_order_box/package.json | 3 +- apps/app_zen_box/package.json | 3 +- public/tools/web_order_box/order_box.css | 54 ++++ public/tools/web_order_box/order_box.js | 66 ++--- 7 files changed, 256 insertions(+), 172 deletions(-) delete mode 100644 apps/app_order_box/deploy-web.js diff --git a/apps/app_calendar_reminder/package.json b/apps/app_calendar_reminder/package.json index 14cd033..1cb2a28 100644 --- a/apps/app_calendar_reminder/package.json +++ b/apps/app_calendar_reminder/package.json @@ -6,7 +6,8 @@ "start": "expo start", "android": "expo start --android", "ios": "expo start --ios", - "web": "expo start --web" + "web": "expo start --web", + "start_web_comment": "仅用于本机快速调试 UI。正式 Web 版本不是这里构建,见孪生兄弟目录 public/tools/web_mobile_calendar_reminder。" }, "dependencies": { "@expo/vector-icons": "^15.0.3", diff --git a/apps/app_order_box/deploy-web.js b/apps/app_order_box/deploy-web.js deleted file mode 100644 index faad758..0000000 --- a/apps/app_order_box/deploy-web.js +++ /dev/null @@ -1,64 +0,0 @@ -// 自动化部署脚本:构建后自动修正路径并复制到 public -const fs = require('fs'); -const path = require('path'); - -const SOURCE_DIR = path.join(__dirname, 'dist'); -const TARGET_DIR = path.join(__dirname, '..', '..', 'public', 'tools', 'web_order_box'); - -// 确保目标目录存在 -if (!fs.existsSync(TARGET_DIR)) { - fs.mkdirSync(TARGET_DIR, { recursive: true }); -} - -// 复制所有文件 -function copyDir(src, dest) { - fs.readdirSync(src).forEach(file => { - const srcPath = path.join(src, file); - const destPath = path.join(dest, file); - if (fs.statSync(srcPath).isDirectory()) { - if (!fs.existsSync(destPath)) fs.mkdirSync(destPath, { recursive: true }); - copyDir(srcPath, destPath); - } else { - fs.copyFileSync(srcPath, destPath); - } - }); -} - -copyDir(SOURCE_DIR, TARGET_DIR); - -// 复制字体文件到 public/assets/fonts -const FONTS_SOURCE = path.join(__dirname, 'node_modules', '@expo', 'vector-icons', 'build', 'vendor', 'react-native-vector-icons', 'Fonts'); -const FONTS_TARGET = path.join(TARGET_DIR, 'assets', 'fonts'); - -if (fs.existsSync(FONTS_SOURCE)) { - if (!fs.existsSync(FONTS_TARGET)) fs.mkdirSync(FONTS_TARGET, { recursive: true }); - fs.readdirSync(FONTS_SOURCE).forEach(file => { - if (file.endsWith('.ttf')) { - fs.copyFileSync(path.join(FONTS_SOURCE, file), path.join(FONTS_TARGET, file)); - } - }); - console.log('✅ 字体文件已复制到 assets/fonts/'); -} - -// 修正 index.html:添加 base 标签并修改路径为相对路径 -const indexPath = path.join(TARGET_DIR, 'index.html'); -let indexContent = fs.readFileSync(indexPath, 'utf-8'); - -// 在 head 中添加 base 标签 -const baseTag = ''; -if (!indexContent.includes('', ` ${baseTag}\n`); -} - -// 将绝对路径改为相对路径(使 base 标签生效) -indexContent = indexContent.replace(/src="\/_expo\//g, 'src="_expo/'); -indexContent = indexContent.replace(/href="\/favicon\.ico"/g, 'href="favicon.ico"'); -indexContent = indexContent.replace(/href="\/assets\//g, 'href="assets/'); -// 修正字体路径 -indexContent = indexContent.replace(/\/assets\/node_modules\/@expo\/vector-icons\/build\/vendor\/react-native-vector-icons\/Fonts\//g, 'assets/fonts/'); - -fs.writeFileSync(indexPath, indexContent, 'utf-8'); - -console.log('✅ 部署完成!'); -console.log(`📁 目标目录:${TARGET_DIR}`); -console.log('🌐 访问地址:https://traesite.umersoft.com:8975/tools/web_order_box'); diff --git a/apps/app_order_box/kits/motion_reminder/components/RunnerModal.tsx b/apps/app_order_box/kits/motion_reminder/components/RunnerModal.tsx index 1456257..03e24c2 100644 --- a/apps/app_order_box/kits/motion_reminder/components/RunnerModal.tsx +++ b/apps/app_order_box/kits/motion_reminder/components/RunnerModal.tsx @@ -1,8 +1,9 @@ -import React, { useCallback, useEffect } from 'react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { Alert, Dimensions, Modal, + ScaledSize, StyleSheet, Text, TouchableOpacity, @@ -32,6 +33,24 @@ export const RunnerModal: React.FC = ({ runner, onClose }) => // 保持屏幕常亮 useKeepAwake(); + const [windowSize, setWindowSize] = useState(() => Dimensions.get('window')); + useEffect(() => { + const listener = ({ window }: { window: ScaledSize }) => setWindowSize(window); + const subscription = Dimensions.addEventListener('change', listener); + return () => { + if (subscription && typeof subscription.remove === 'function') { + subscription.remove(); + } else { + try { + Dimensions.removeEventListener('change', listener); + } catch (e) { /* noop */ } + } + }; + }, []); + const isLandscape = useMemo(() => windowSize.width > windowSize.height, [windowSize]); + const isWide = windowSize.width >= 900; + const splitMode = isLandscape || isWide; + // 进入后台时保存状态 const handleBackground = useCallback(async () => { if (!runnerState || runnerState.status !== 'running') return; @@ -129,9 +148,97 @@ export const RunnerModal: React.FC = ({ runner, onClose }) => const isAction = currentStep?.type === 'action'; const stepBorderColor = isAction ? theme.colors.primary : theme.colors.success; + const leftColumn = ( + + 当前动作 + + {currentStep?.name || '—'} + + + {formatDuration(runnerState.remainingSec || 0)} + + + + 下一个动作 + + {nextStep?.name || '本轮结束'} + + + + ); + + const rightColumn = ( + + + + 已执行 + + 第 {runnerState.currentLoop} 轮,步骤 {runnerState.currentStepIndex + 1}/{totalSteps} + + + + 预计剩余 + + 约 {formatDuration(remainingStepsSec)} + + + + 全局时长 + + {globalDurationSec !== null + ? `约 ${formatDuration(globalDurationSec)}` + : '无限循环'} + + + + + + handleAction(runnerState.status === 'paused' ? 'resume' : 'pause')} + > + {runnerState.status === 'paused' ? '继续' : '暂停'} + + handleAction('skip')}> + 跳过 + + + 结束 + + + + {globalDurationSec !== null && globalDurationSec > 0 && ( + + + 执行进度 + {Math.round(globalProgress * 100)}% + + + + + + 已执行 {formatDuration(executedSec)} + / + 总时长 {formatDuration(globalDurationSec)} + · + + 剩余 {formatDuration(remainingStepsSec)} + + + + )} + + ); + return ( - + {isAction ? '动作' : '休息'} @@ -143,83 +250,22 @@ export const RunnerModal: React.FC = ({ runner, onClose }) => - {/* Progress bar */} - - 当前动作 - {currentStep?.name || '—'} - {formatDuration(runnerState.remainingSec || 0)} - - - 下一个动作 - {nextStep?.name || '本轮结束'} + {splitMode ? ( + + {leftColumn} + + {rightColumn} - - - - {/* 执行信息卡片 */} - - - 已执行 - - 第 {runnerState.currentLoop} 轮,步骤 {runnerState.currentStepIndex + 1}/{totalSteps} - - - - 预计剩余 - - 约 {formatDuration(remainingStepsSec)} - - - - 全局时长 - - {globalDurationSec !== null - ? `约 ${formatDuration(globalDurationSec)}` - : '无限循环'} - - - - - {/* 控制按钮 */} - - handleAction(runnerState.status === 'paused' ? 'resume' : 'pause')} - > - {runnerState.status === 'paused' ? '继续' : '暂停'} - - handleAction('skip')}> - 跳过 - - - 结束 - - - - {/* 全局进度条 */} - {globalDurationSec !== null && globalDurationSec > 0 && ( - - - 执行进度 - {Math.round(globalProgress * 100)}% - - - - - - 已执行 {formatDuration(executedSec)} - / - 总时长 {formatDuration(globalDurationSec)} - · - 剩余 {formatDuration(remainingStepsSec)} - - - )} - + ) : ( + <> + {leftColumn} + {rightColumn} + + )} ); @@ -233,6 +279,10 @@ const styles = StyleSheet.create({ paddingHorizontal: theme.spacing.lg, paddingBottom: theme.spacing.xl, }, + runnerContainerLandscape: { + paddingTop: 24, + paddingHorizontal: 28, + }, runnerHeader: { alignItems: 'center', gap: theme.spacing.sm, @@ -265,6 +315,30 @@ const styles = StyleSheet.create({ backgroundColor: theme.colors.primary, borderRadius: 5, }, + splitLayout: { + flex: 1, + flexDirection: 'row', + gap: 24, + alignItems: 'stretch', + minHeight: 0, + }, + splitColLeft: { + flex: 1, + minWidth: 0, + minHeight: 0, + }, + splitColRight: { + flex: 1, + minWidth: 0, + minHeight: 0, + justifyContent: 'space-between', + overflow: 'scroll', + }, + splitDivider: { + width: StyleSheet.hairlineWidth, + backgroundColor: theme.colors.border, + marginVertical: 4, + }, runnerMain: { flex: 1, justifyContent: 'center', @@ -281,11 +355,18 @@ const styles = StyleSheet.create({ color: theme.colors.text, textAlign: 'center', }, + runnerCurrentTextLandscape: { + fontSize: 46, + }, runnerTimer: { fontSize: 90, fontWeight: '700', color: theme.colors.primary, }, + runnerTimerLandscape: { + fontSize: 128, + marginVertical: 8, + }, runnerNextCard: { width: '100%', backgroundColor: theme.colors.surface, @@ -297,12 +378,18 @@ const styles = StyleSheet.create({ marginTop: theme.spacing.xl, gap: theme.spacing.sm, }, + runnerNextCardLandscape: { + marginTop: 16, + }, runnerNextText: { fontSize: 26, fontWeight: '700', color: theme.colors.text, textAlign: 'center', }, + runnerNextTextLandscape: { + fontSize: 34, + }, runnerFooter: { gap: theme.spacing.md, }, diff --git a/apps/app_order_box/package.json b/apps/app_order_box/package.json index b7f3ccd..3fb106d 100644 --- a/apps/app_order_box/package.json +++ b/apps/app_order_box/package.json @@ -6,7 +6,8 @@ "start": "expo start", "android": "expo start --android", "ios": "expo start --ios", - "web": "expo start --web" + "web": "expo start --web", + "start_web_comment": "仅用于本机快速调试 UI。正式 Web 版本不是这里构建,见 web_order_box 孪生兄弟目录。" }, "dependencies": { "@expo/vector-icons": "^15.0.3", diff --git a/apps/app_zen_box/package.json b/apps/app_zen_box/package.json index 5fdbb9b..9888b32 100644 --- a/apps/app_zen_box/package.json +++ b/apps/app_zen_box/package.json @@ -6,7 +6,8 @@ "start": "expo start", "android": "expo run:android", "ios": "expo run:ios", - "web": "expo start --web" + "web": "expo start --web", + "start_web_comment": "仅用于本机快速调试 UI。正式 Web 版本不是这里构建,见孪生兄弟目录 public/tools/web_zen_box。" }, "dependencies": { "@expo/vector-icons": "^15.0.3", diff --git a/public/tools/web_order_box/order_box.css b/public/tools/web_order_box/order_box.css index 0255ab2..8731d90 100644 --- a/public/tools/web_order_box/order_box.css +++ b/public/tools/web_order_box/order_box.css @@ -385,6 +385,60 @@ input[type='number'] { -moz-appearance: textfield; appearance: textfield; } .runner-main { flex: 1; display: flex; flex-direction: column; } .runner-label { font-size: 16px; color: var(--text-secondary); margin-bottom: 4px; } +/* 横屏/大屏两栏容器 — JS 用 .runner-split-layout / .runner-col-left / .runner-col-right */ +.runner-split-layout { + display: flex; + flex-direction: column; + gap: 16px; + flex: 1; + min-height: 0; +} +.runner-col-left, .runner-col-right { + display: flex; + flex-direction: column; + min-width: 0; + min-height: 0; +} +.runner-col-right { + justify-content: flex-end; +} +.runner-col-right .runner-footer { + margin-top: 0; +} + +@media (orientation: landscape) and (min-height: 420px), (min-width: 900px) { + .runner-container { padding: 18px 28px; padding-top: calc(12px + env(safe-area-inset-top)); } + .runner-header { margin-bottom: 10px; } + .runner-progress { margin-bottom: 12px; } + .runner-split-layout { + flex-direction: row; + gap: 24px; + align-items: stretch; + } + .runner-col-left, + .runner-col-right { + flex: 1 1 0; + } + .runner-col-left { + justify-content: center; + align-items: stretch; + padding: 8px 12px; + } + .runner-col-right { + justify-content: space-between; + padding: 8px 12px; + overflow-y: auto; + border-left: 1px solid var(--border); + } + .runner-col-left .runner-next-card { margin-bottom: 0; } + .runner-timer { + font-size: clamp(72px, 14vw, 128px); + margin: 8px 0 16px; + } + .runner-current { font-size: clamp(28px, 4.2vw, 46px); } + .runner-next { font-size: clamp(22px, 3vw, 34px); } +} + /* 当前动作名 — JS 用 .runner-current */ .runner-current { font-size: 34px; font-weight: 700; margin-bottom: 8px; line-height: 1.2; word-break: break-word; } .runner-current.current-action { color: var(--primary); } diff --git a/public/tools/web_order_box/order_box.js b/public/tools/web_order_box/order_box.js index c1b9d7d..9c7085a 100644 --- a/public/tools/web_order_box/order_box.js +++ b/public/tools/web_order_box/order_box.js @@ -1261,39 +1261,43 @@ '
第 ' + rs.currentLoop + ' 轮 ' + (totalLoops ? '/ 共 ' + totalLoops + ' 轮' : '/ 无限循环') + ' · 步骤 ' + (rs.currentStepIndex + 1) + ' / ' + totalSteps + '
' + '' + '
' + - '
' + - '
当前动作
' + - '
' + escHtml(step.name || '—') + '
' + - '
--:--
' + - '
' + - '
下一个动作
' + - '
' + (nextStep ? escHtml(nextStep.name) : '本轮结束') + '
' + + '
' + + '
' + + '
当前动作
' + + '
' + escHtml(step.name || '—') + '
' + + '
--:--
' + + '
' + + '
下一个动作
' + + '
' + (nextStep ? escHtml(nextStep.name) : '本轮结束') + '
' + + '
' + '
' + - '
' + - '