chore(邮件发送模块): 更新SQLite共享内存临时文件

This commit is contained in:
yangxiangyuan
2026-07-30 09:31:01 +08:00
parent 154b79c8f1
commit 398758800e
7 changed files with 256 additions and 172 deletions
+2 -1
View File
@@ -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",
-64
View File
@@ -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 = '<base href="/tools/web_order_box/" />';
if (!indexContent.includes('<base')) {
indexContent = indexContent.replace('</head>', ` ${baseTag}\n</head>`);
}
// 将绝对路径改为相对路径(使 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');
@@ -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<RunnerModalProps> = ({ runner, onClose }) =>
// 保持屏幕常亮
useKeepAwake();
const [windowSize, setWindowSize] = useState<ScaledSize>(() => 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<RunnerModalProps> = ({ runner, onClose }) =>
const isAction = currentStep?.type === 'action';
const stepBorderColor = isAction ? theme.colors.primary : theme.colors.success;
const leftColumn = (
<View style={styles.runnerMain}>
<Text style={styles.runnerLabel}>当前动作</Text>
<Text
style={[
styles.runnerCurrentText,
splitMode && styles.runnerCurrentTextLandscape,
isAction && { color: stepBorderColor },
]}
>
{currentStep?.name || '—'}
</Text>
<Text style={[styles.runnerTimer, splitMode && styles.runnerTimerLandscape]}>
{formatDuration(runnerState.remainingSec || 0)}
</Text>
<View style={[styles.runnerNextCard, splitMode && styles.runnerNextCardLandscape, { borderColor: stepBorderColor }]}>
<Text style={styles.runnerLabel}>下一个动作</Text>
<Text style={[styles.runnerNextText, splitMode && styles.runnerNextTextLandscape]}>
{nextStep?.name || '本轮结束'}
</Text>
</View>
</View>
);
const rightColumn = (
<View style={styles.runnerFooter}>
<View style={styles.runnerInfoCard}>
<View style={styles.runnerInfoRow}>
<Text style={styles.runnerInfoLabel}>已执行</Text>
<Text style={styles.runnerInfoValue}>
第 {runnerState.currentLoop} 轮,步骤 {runnerState.currentStepIndex + 1}/{totalSteps}
</Text>
</View>
<View style={styles.runnerInfoRow}>
<Text style={styles.runnerInfoLabel}>预计剩余</Text>
<Text style={[styles.runnerInfoValue, styles.runnerInfoHighlight]}>
约 {formatDuration(remainingStepsSec)}
</Text>
</View>
<View style={styles.runnerInfoRow}>
<Text style={styles.runnerInfoLabel}>全局时长</Text>
<Text style={[styles.runnerInfoValue, styles.runnerInfoHighlight]}>
{globalDurationSec !== null
? `约 ${formatDuration(globalDurationSec)}`
: '无限循环'}
</Text>
</View>
</View>
<View style={styles.runnerButtonRow}>
<TouchableOpacity
style={styles.runnerButton}
onPress={() => handleAction(runnerState.status === 'paused' ? 'resume' : 'pause')}
>
<Text style={styles.runnerButtonText}>{runnerState.status === 'paused' ? '继续' : '暂停'}</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.runnerButton} onPress={() => handleAction('skip')}>
<Text style={styles.runnerButtonText}>跳过</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.runnerButton, styles.runnerDangerButton]} onPress={handleStop}>
<Text style={styles.runnerButtonText}>结束</Text>
</TouchableOpacity>
</View>
{globalDurationSec !== null && globalDurationSec > 0 && (
<View style={styles.globalProgressWrap}>
<View style={styles.globalProgressHeader}>
<Text style={styles.globalProgressLabel}>执行进度</Text>
<Text style={styles.globalProgressPercent}>{Math.round(globalProgress * 100)}%</Text>
</View>
<View style={styles.globalProgressBarContainer}>
<View style={[styles.globalProgressBar, { width: `${globalProgress * 100}%` }]} />
</View>
<View style={styles.globalProgressStats}>
<Text style={styles.globalProgressStat}>已执行 {formatDuration(executedSec)}</Text>
<Text style={styles.globalProgressStatDivider}>/</Text>
<Text style={styles.globalProgressStat}>总时长 {formatDuration(globalDurationSec)}</Text>
<Text style={styles.globalProgressStatDivider}>·</Text>
<Text style={[styles.globalProgressStat, styles.globalProgressRemaining]}>
剩余 {formatDuration(remainingStepsSec)}
</Text>
</View>
</View>
)}
</View>
);
return (
<Modal visible={!!runnerState} animationType="slide" onRequestClose={handleStop}>
<View style={styles.runnerContainer}>
<View style={[styles.runnerContainer, splitMode && styles.runnerContainerLandscape]}>
<View style={styles.runnerHeader}>
<Text style={[styles.runnerBadge, { borderColor: stepBorderColor }]}>
{isAction ? '动作' : '休息'}
@@ -143,83 +250,22 @@ export const RunnerModal: React.FC<RunnerModalProps> = ({ runner, onClose }) =>
</Text>
</View>
{/* Progress bar */}
<View style={styles.progressBarContainer}>
<View style={[styles.progressBar, { width: `${stepProgress * 100}%` }]} />
</View>
<View style={styles.runnerMain}>
<Text style={styles.runnerLabel}>当前动作</Text>
<Text style={[styles.runnerCurrentText, isAction && { color: stepBorderColor }]}>{currentStep?.name || '—'}</Text>
<Text style={styles.runnerTimer}>{formatDuration(runnerState.remainingSec || 0)}</Text>
<View style={[styles.runnerNextCard, { borderColor: stepBorderColor }]}>
<Text style={styles.runnerLabel}>下一个动作</Text>
<Text style={styles.runnerNextText}>{nextStep?.name || '本轮结束'}</Text>
{splitMode ? (
<View style={styles.splitLayout}>
<View style={styles.splitColLeft}>{leftColumn}</View>
<View style={[styles.splitDivider]} />
<View style={styles.splitColRight}>{rightColumn}</View>
</View>
</View>
<View style={styles.runnerFooter}>
{/* 执行信息卡片 */}
<View style={styles.runnerInfoCard}>
<View style={styles.runnerInfoRow}>
<Text style={styles.runnerInfoLabel}>已执行</Text>
<Text style={styles.runnerInfoValue}>
第 {runnerState.currentLoop} 轮,步骤 {runnerState.currentStepIndex + 1}/{totalSteps}
</Text>
</View>
<View style={styles.runnerInfoRow}>
<Text style={styles.runnerInfoLabel}>预计剩余</Text>
<Text style={[styles.runnerInfoValue, styles.runnerInfoHighlight]}>
约 {formatDuration(remainingStepsSec)}
</Text>
</View>
<View style={styles.runnerInfoRow}>
<Text style={styles.runnerInfoLabel}>全局时长</Text>
<Text style={[styles.runnerInfoValue, styles.runnerInfoHighlight]}>
{globalDurationSec !== null
? `约 ${formatDuration(globalDurationSec)}`
: '无限循环'}
</Text>
</View>
</View>
{/* 控制按钮 */}
<View style={styles.runnerButtonRow}>
<TouchableOpacity
style={styles.runnerButton}
onPress={() => handleAction(runnerState.status === 'paused' ? 'resume' : 'pause')}
>
<Text style={styles.runnerButtonText}>{runnerState.status === 'paused' ? '继续' : '暂停'}</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.runnerButton} onPress={() => handleAction('skip')}>
<Text style={styles.runnerButtonText}>跳过</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.runnerButton, styles.runnerDangerButton]} onPress={handleStop}>
<Text style={styles.runnerButtonText}>结束</Text>
</TouchableOpacity>
</View>
{/* 全局进度条 */}
{globalDurationSec !== null && globalDurationSec > 0 && (
<View style={styles.globalProgressWrap}>
<View style={styles.globalProgressHeader}>
<Text style={styles.globalProgressLabel}>执行进度</Text>
<Text style={styles.globalProgressPercent}>{Math.round(globalProgress * 100)}%</Text>
</View>
<View style={styles.globalProgressBarContainer}>
<View style={[styles.globalProgressBar, { width: `${globalProgress * 100}%` }]} />
</View>
<View style={styles.globalProgressStats}>
<Text style={styles.globalProgressStat}>已执行 {formatDuration(executedSec)}</Text>
<Text style={styles.globalProgressStatDivider}>/</Text>
<Text style={styles.globalProgressStat}>总时长 {formatDuration(globalDurationSec)}</Text>
<Text style={styles.globalProgressStatDivider}>·</Text>
<Text style={[styles.globalProgressStat, styles.globalProgressRemaining]}>剩余 {formatDuration(remainingStepsSec)}</Text>
</View>
</View>
)}
</View>
) : (
<>
{leftColumn}
{rightColumn}
</>
)}
</View>
</Modal>
);
@@ -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,
},
+2 -1
View File
@@ -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",
+2 -1
View File
@@ -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",