端上:#47~#55 打包 —— 行程双轨、动态类型、表单全局规范 (#53)

#48 计划/实际双轨:nodeAction(start/done/cancel),完成即转实际行程,
    实际行程不再显示状态;新增 actualStartedAt/actualEndedAt
#49 类型/出行方式/车辆 chip 改内部读状态(@Builder 值传参不刷新)
#50 类型点击立即变色、出行方式平铺、参与人显示已选头像
#51 节点类型动态加载(原硬编码5种)、地址 onPick 拿详细地址与坐标
#52 标签改主题色 Bold、必填红无星号、编辑行程标题同步
#53 表单两排版式(标签独占一行解决左右对不齐);电影弹层 layoutWeight 修滚动
#54 地址联想框加时间窗抑制(600ms 内不搜)、候选限高 180

含附件回填诊断日志(DIAG-ATTACH/FILL),待真机确认后移除
This commit is contained in:
2026-11-05 12:41:04 +08:00
parent 88fa38ecf9
commit 86de59616c
35 changed files with 5818 additions and 911 deletions
+67
View File
@@ -46,6 +46,73 @@ export async function fbUploadOne(path: string, field: string, uri: string,
return r.ok ? '' : r.error;
}
/**
* 暂存上传:把文件传到暂存区,返回**令牌**。
*
* 🔴 2026-11-05。用户:「先上传,拿到地址,然后提交的时候,连带着跟其他参数一起提交,
* 后台接口拿到数据,先插入行程,拿到 id,再把图片插入进去。」
*
* 这是「新增时也能挂照片」的关键:新增行程时节点还不存在,
* 只能先把文件暂存起来拿 stoken,等保存表单时把 stoken 一起提交,
* 服务端建完节点再 `claim` 上去。
*
* @param uri picker 给的文件 URI
* @param token 访问令牌
* @returns stoken 为空串 = 失败(看 error)
*/
export interface FbStageUpload {
/** 暂存令牌(空 = 失败) */
stoken: string;
/** 原始文件名 */
name: string;
/** 是否图片(服务端按 MIME 嗅探) */
isImage: boolean;
/** 预览地址(相对路径) */
url: string;
/** 错误说明(成功时为空) */
error: string;
}
export async function fbUploadStage(uri: string, token: string): Promise<FbStageUpload> {
const out: FbStageUpload = {
stoken: '', name: '', isImage: false, url: '', error: ''
};
const r: FbUploadRaw = await fbUploadRaw('/api/v1/uploads/stage', 'file', uri, token);
if (!r.ok) {
out.error = r.error !== '' ? r.error : '上传失败';
return out;
}
/* 服务端信封:{ok:true, data:{stoken,...}}(`fbApi` 层已拆过一层,
但 multipart 这条路是自己解析的,要按原始信封读) */
try {
const env: Record<string, Object> = JSON.parse(r.body) as Record<string, Object>;
let dataObj: Record<string, Object> = env;
const nested: Object | undefined = env['data'];
if (nested !== undefined) {
dataObj = nested as Record<string, Object>;
}
const st: Object | undefined = dataObj['stoken'];
if (typeof st === 'string' && st !== '') {
out.stoken = st as string;
}
const nm: Object | undefined = dataObj['name'];
if (typeof nm === 'string') {
out.name = nm as string;
}
out.isImage = dataObj['is_image'] === true;
const u: Object | undefined = dataObj['url'];
if (typeof u === 'string') {
out.url = u as string;
}
if (out.stoken === '') {
out.error = '服务端没返回令牌';
}
} catch (err) {
out.error = '响应解析失败';
}
return out;
}
/**
* 上传图片并**拿回响应载荷**(工单 #23 用)。
*