docs: 整理项目文档并统一界面表述
将项目说明按主题集中到 docs,补齐根目录 README 和 AGENTS,移除弃用方案并保留有效测试记录与未实现提案。同步文档索引、引用及改动日志生成路径,README 增加 GitHub Markdown 导航和 Mermaid 图。 - fix(client): 统一状态与操作文案,录制和截图成功后再显示完成提示 - test(client): 按文件名核对媒体类型,避免依赖时间戳排序 验证:23 份项目文档、144 处本地引用、27 条外链、2 张 Mermaid 图;前端构建和 12 项测试;截图与录制的 4 条异步反馈路径;Windows Rust 编译检查和后端测试。后端 40 项返回成功,其中 3 项 ffmpeg 集成用例因本机缺少工具而未实际执行。
This commit is contained in:
@@ -47,10 +47,14 @@ export function MediaPanel({ snapshot, onError, onInfo }: Props) {
|
||||
const active = snapshot.recordings.filter((recording) => recording.active);
|
||||
const cameras = snapshot.cameras.filter((camera) => camera.online).map((camera) => camera.id);
|
||||
|
||||
const guard = (action: Promise<unknown>) => {
|
||||
const guard = (action: Promise<unknown>, onSuccess?: () => void) => {
|
||||
setBusy(true);
|
||||
void action
|
||||
.then(() => api.refresh())
|
||||
.then(() => {
|
||||
onSuccess?.();
|
||||
refresh();
|
||||
return api.refresh();
|
||||
})
|
||||
.catch((err) => onError(err instanceof Error ? err.message : String(err)))
|
||||
.finally(() => setBusy(false));
|
||||
};
|
||||
@@ -66,10 +70,11 @@ export function MediaPanel({ snapshot, onError, onInfo }: Props) {
|
||||
type="button"
|
||||
className="btn btn--primary"
|
||||
disabled={busy || cameras.length === 0}
|
||||
title={cameras.length === 0 ? "还没有在线的摄像头" : undefined}
|
||||
title={cameras.length === 0 ? "暂无在线图传板" : undefined}
|
||||
onClick={() => {
|
||||
guard(api.startRecording(cameras));
|
||||
onInfo("开始录制。停止之前请留在这台机器上——退出会先收尾再走。");
|
||||
guard(api.startRecording(cameras), () =>
|
||||
onInfo("已开始录制。退出程序时会先结束录制并保存文件。"),
|
||||
);
|
||||
}}
|
||||
>
|
||||
<span className="media__rec-glyph" aria-hidden="true" />
|
||||
@@ -82,7 +87,6 @@ export function MediaPanel({ snapshot, onError, onInfo }: Props) {
|
||||
disabled={busy || active.length === 0}
|
||||
onClick={() => {
|
||||
guard(api.stopRecording([]));
|
||||
refresh();
|
||||
}}
|
||||
>
|
||||
停止全部录制
|
||||
@@ -97,11 +101,11 @@ export function MediaPanel({ snapshot, onError, onInfo }: Props) {
|
||||
className="btn btn--ghost"
|
||||
disabled={busy}
|
||||
onClick={() => {
|
||||
guard(api.screenshot(camera));
|
||||
onInfo(
|
||||
`已截图 0x${camera.toString(16).toUpperCase().padStart(8, "0")}(存在录制目录的 shots/ 下)。`,
|
||||
guard(api.screenshot(camera), () =>
|
||||
onInfo(
|
||||
`已截图 0x${camera.toString(16).toUpperCase().padStart(8, "0")}(已保存到录制目录的 shots/)。`,
|
||||
),
|
||||
);
|
||||
refresh();
|
||||
}}
|
||||
>
|
||||
截图
|
||||
@@ -141,7 +145,7 @@ export function MediaPanel({ snapshot, onError, onInfo }: Props) {
|
||||
{recording.framesDropped > 0 && (
|
||||
<span
|
||||
className="strip__warn"
|
||||
title="写盘跟不上时丢的帧。丢帧不报出来,回放里少半秒会被当成相机的问题。"
|
||||
title="写入速度不足时丢弃的帧数,可能影响回放速度。"
|
||||
>
|
||||
丢 {recording.framesDropped} 帧
|
||||
</span>
|
||||
@@ -152,7 +156,6 @@ export function MediaPanel({ snapshot, onError, onInfo }: Props) {
|
||||
className="btn btn--ghost"
|
||||
onClick={() => {
|
||||
guard(api.stopRecording([recording.camera]));
|
||||
refresh();
|
||||
}}
|
||||
>
|
||||
停止
|
||||
@@ -232,8 +235,8 @@ export function MediaPanel({ snapshot, onError, onInfo }: Props) {
|
||||
{playbackFailed ? (
|
||||
<div className="playback__fallback">
|
||||
<p className="playback__warn">
|
||||
界面内回放失败——多半是这个目录不在资源协议的白名单里(改成默认的录制目录,
|
||||
或者直接看下面这一条)。
|
||||
无法在界面内回放,可能是录制目录不在允许访问的范围内。可改用默认录制目录,
|
||||
或用系统播放器打开。
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
|
||||
Reference in New Issue
Block a user