第17章|开关变化怎么同步到模板
这一章讲开关怎么同步到模板,核心是让设置页、预览页和结果页看到的是同一个状态。
01 时间开关生效
这一节不是只给一句结论,而是把"时间开关生效"放进整个 第17章 的链路里看。读者需要看到输入、处理和结果,所以这里会把实现边界也一起讲清。
这一节的落点是:时间开关生效 不能只停在页面上看起来对,还要真的参与到保存、恢复和验证里。
代码演示
ts
private applyToggleState(): void {
this.previewShowTime = this.showTime;
this.previewShowAddress = this.showAddress;
this.refreshPreview();
}
这里的要点
- 时间格式要统一,不能在不同页面里写成不同样式。
- 刷新时间时不要让 UI 产生跳动。
- 时间值要和保存逻辑对齐。
时间开关生效 这一节的重点不是把内容写满,而是把这一点和整页链路接起来。
02 地点开关生效
这一节不是只给一句结论,而是把"地点开关生效"放进整个 第17章 的链路里看。读者需要看到输入、处理和结果,所以这里会把实现边界也一起讲清。
这一节的落点是:地点开关生效 不能只停在页面上看起来对,还要真的参与到保存、恢复和验证里。
代码演示
ts
private applyToggleState(): void {
this.previewShowTime = this.showTime;
this.previewShowAddress = this.showAddress;
this.refreshPreview();
}
这里的要点
- 手动地址要真正回填到主状态里,不能只停在输入框。
- 空输入要拦截,脏输入要清理。
- 地址改完后,预览和历史都要跟着变。
地点开关生效 这一节的重点不是把内容写满,而是把这一点和整页链路接起来。
03 模板要联动
这一节不是只给一句结论,而是把"模板要联动"放进整个 第17章 的链路里看。读者需要看到输入、处理和结果,所以这里会把实现边界也一起讲清。
这一节的落点是:模板要联动 不能只停在页面上看起来对,还要真的参与到保存、恢复和验证里。
代码演示
ts
private applyToggleState(): void {
this.previewShowTime = this.showTime;
this.previewShowAddress = this.showAddress;
this.refreshPreview();
}
这里的要点
- 模板编号要能映射到明确样式。
- 切换模板时,预览和结果都要同步。
- 模板最好不要和其他状态混在一起。
模板要联动 这一节的重点不是把内容写满,而是把这一点和整页链路接起来。
04 结果要一致
这一节不是只给一句结论,而是把"结果要一致"放进整个 第17章 的链路里看。读者需要看到输入、处理和结果,所以这里会把实现边界也一起讲清。
这一节的落点是:结果要一致 不能只停在页面上看起来对,还要真的参与到保存、恢复和验证里。
代码演示
ts
private applyToggleState(): void {
this.previewShowTime = this.showTime;
this.previewShowAddress = this.showAddress;
this.refreshPreview();
}
这里的要点
- 先把 04 结果要一致 讲清楚,别只留一句结论。
- 再把它和状态、保存、恢复连起来。
- 最后用代码或流程图把闭环落实。
结果要一致 这一节的重点不是把内容写满,而是把这一点和整页链路接起来。
05 状态要可保存
这一节不是只给一句结论,而是把"状态要可保存"放进整个 第17章 的链路里看。读者需要看到输入、处理和结果,所以这里会把实现边界也一起讲清。
这一节的落点是:状态要可保存 不能只停在页面上看起来对,还要真的参与到保存、恢复和验证里。
代码演示
ts
private applyToggleState(): void {
this.previewShowTime = this.showTime;
this.previewShowAddress = this.showAddress;
this.refreshPreview();
}
这里的要点
- 保存要覆盖用户真的会改的字段,不能只存一半。
- 恢复要容错,旧数据缺字段时也要能工作。
- 持久化写完后,重进页面要能读回原状态。
状态要可保存 这一节的重点不是把内容写满,而是把这一点和整页链路接起来。
06 状态要可恢复
这一节不是只给一句结论,而是把"状态要可恢复"放进整个 第17章 的链路里看。读者需要看到输入、处理和结果,所以这里会把实现边界也一起讲清。
这一节的落点是:状态要可恢复 不能只停在页面上看起来对,还要真的参与到保存、恢复和验证里。
代码演示
ts
private applyToggleState(): void {
this.previewShowTime = this.showTime;
this.previewShowAddress = this.showAddress;
this.refreshPreview();
}
这里的要点
- 保存要覆盖用户真的会改的字段,不能只存一半。
- 恢复要容错,旧数据缺字段时也要能工作。
- 持久化写完后,重进页面要能读回原状态。
状态要可恢复 这一节的重点不是把内容写满,而是把这一点和整页链路接起来。
07 默认值要清楚
这一节不是只给一句结论,而是把"默认值要清楚"放进整个 第17章 的链路里看。读者需要看到输入、处理和结果,所以这里会把实现边界也一起讲清。
这一节的落点是:默认值要清楚 不能只停在页面上看起来对,还要真的参与到保存、恢复和验证里。
代码演示
ts
private applyToggleState(): void {
this.previewShowTime = this.showTime;
this.previewShowAddress = this.showAddress;
this.refreshPreview();
}
这里的要点
- 空值不是报错,但必须有默认表现。
- 兜底值要统一,避免页面看起来断开。
- 默认状态要能解释给用户。
默认值要清楚 这一节的重点不是把内容写满,而是把这一点和整页链路接起来。
08 开关要独立
这一节不是只给一句结论,而是把"开关要独立"放进整个 第17章 的链路里看。读者需要看到输入、处理和结果,所以这里会把实现边界也一起讲清。
这一节的落点是:开关要独立 不能只停在页面上看起来对,还要真的参与到保存、恢复和验证里。
代码演示
ts
this.SwitchRow('时', '显示时间', '保存照片时写入拍摄时间', this.showTime, (checked: boolean) => {
this.showTime = checked;
this.applyToggleState();
});
this.SwitchRow('址', '显示地点', '保存照片时写入地址和坐标', this.showAddress, (checked: boolean) => {
this.showAddress = checked;
this.applyToggleState();
});
这里的要点
- 开关不是装饰,它会直接影响预览和结果。
- 开关变化后要立即刷新,不能让用户猜。
- 设置页和结果页都要读取同一份状态。
开关要独立 这一节的重点不是把内容写满,而是把这一点和整页链路接起来。
09 切换要轻量
这一节不是只给一句结论,而是把"切换要轻量"放进整个 第17章 的链路里看。读者需要看到输入、处理和结果,所以这里会把实现边界也一起讲清。
这一节的落点是:切换要轻量 不能只停在页面上看起来对,还要真的参与到保存、恢复和验证里。
代码演示
ts
this.SwitchRow('时', '显示时间', '保存照片时写入拍摄时间', this.showTime, (checked: boolean) => {
this.showTime = checked;
this.applyToggleState();
});
this.SwitchRow('址', '显示地点', '保存照片时写入地址和坐标', this.showAddress, (checked: boolean) => {
this.showAddress = checked;
this.applyToggleState();
});
这里的要点
- 先把 09 切换要轻量 讲清楚,别只留一句结论。
- 再把它和状态、保存、恢复连起来。
- 最后用代码或流程图把闭环落实。
切换要轻量 这一节的重点不是把内容写满,而是把这一点和整页链路接起来。
10 UI 要同步
这一节不是只给一句结论,而是把"UI 要同步"放进整个 第17章 的链路里看。读者需要看到输入、处理和结果,所以这里会把实现边界也一起讲清。
这一节的落点是:UI 要同步 不能只停在页面上看起来对,还要真的参与到保存、恢复和验证里。
代码演示
ts
this.SwitchRow('时', '显示时间', '保存照片时写入拍摄时间', this.showTime, (checked: boolean) => {
this.showTime = checked;
this.applyToggleState();
});
this.SwitchRow('址', '显示地点', '保存照片时写入地址和坐标', this.showAddress, (checked: boolean) => {
this.showAddress = checked;
this.applyToggleState();
});
这里的要点
- 先把 10 UI 要同步 讲清楚,别只留一句结论。
- 再把它和状态、保存、恢复连起来。
- 最后用代码或流程图把闭环落实。
UI 要同步 这一节的重点不是把内容写满,而是把这一点和整页链路接起来。
11 空状态要兜底
这一节不是只给一句结论,而是把"空状态要兜底"放进整个 第17章 的链路里看。读者需要看到输入、处理和结果,所以这里会把实现边界也一起讲清。
这一节的落点是:空状态要兜底 不能只停在页面上看起来对,还要真的参与到保存、恢复和验证里。
代码演示
ts
this.SwitchRow('时', '显示时间', '保存照片时写入拍摄时间', this.showTime, (checked: boolean) => {
this.showTime = checked;
this.applyToggleState();
});
this.SwitchRow('址', '显示地点', '保存照片时写入地址和坐标', this.showAddress, (checked: boolean) => {
this.showAddress = checked;
this.applyToggleState();
});
这里的要点
- 先把 11 空状态要兜底 讲清楚,别只留一句结论。
- 再把它和状态、保存、恢复连起来。
- 最后用代码或流程图把闭环落实。
空状态要兜底 这一节的重点不是把内容写满,而是把这一点和整页链路接起来。
12 设置页要一致
这一节不是只给一句结论,而是把"设置页要一致"放进整个 第17章 的链路里看。读者需要看到输入、处理和结果,所以这里会把实现边界也一起讲清。
这一节的落点是:设置页要一致 不能只停在页面上看起来对,还要真的参与到保存、恢复和验证里。
代码演示
ts
this.SwitchRow('时', '显示时间', '保存照片时写入拍摄时间', this.showTime, (checked: boolean) => {
this.showTime = checked;
this.applyToggleState();
});
this.SwitchRow('址', '显示地点', '保存照片时写入地址和坐标', this.showAddress, (checked: boolean) => {
this.showAddress = checked;
this.applyToggleState();
});
这里的要点
- 先把 12 设置页要一致 讲清楚,别只留一句结论。
- 再把它和状态、保存、恢复连起来。
- 最后用代码或流程图把闭环落实。
设置页要一致 这一节的重点不是把内容写满,而是把这一点和整页链路接起来。
13 结果页要一致
这一节不是只给一句结论,而是把"结果页要一致"放进整个 第17章 的链路里看。读者需要看到输入、处理和结果,所以这里会把实现边界也一起讲清。
这一节的落点是:结果页要一致 不能只停在页面上看起来对,还要真的参与到保存、恢复和验证里。
代码演示
ts
this.SwitchRow('时', '显示时间', '保存照片时写入拍摄时间', this.showTime, (checked: boolean) => {
this.showTime = checked;
this.applyToggleState();
});
this.SwitchRow('址', '显示地点', '保存照片时写入地址和坐标', this.showAddress, (checked: boolean) => {
this.showAddress = checked;
this.applyToggleState();
});
这里的要点
- 先把 13 结果页要一致 讲清楚,别只留一句结论。
- 再把它和状态、保存、恢复连起来。
- 最后用代码或流程图把闭环落实。
结果页要一致 这一节的重点不是把内容写满,而是把这一点和整页链路接起来。
14 切换要可重复
这一节不是只给一句结论,而是把"切换要可重复"放进整个 第17章 的链路里看。读者需要看到输入、处理和结果,所以这里会把实现边界也一起讲清。
这一节的落点是:切换要可重复 不能只停在页面上看起来对,还要真的参与到保存、恢复和验证里。
代码演示
ts
this.SwitchRow('时', '显示时间', '保存照片时写入拍摄时间', this.showTime, (checked: boolean) => {
this.showTime = checked;
this.applyToggleState();
});
this.SwitchRow('址', '显示地点', '保存照片时写入地址和坐标', this.showAddress, (checked: boolean) => {
this.showAddress = checked;
this.applyToggleState();
});
这里的要点
- 先把 14 切换要可重复 讲清楚,别只留一句结论。
- 再把它和状态、保存、恢复连起来。
- 最后用代码或流程图把闭环落实。
切换要可重复 这一节的重点不是把内容写满,而是把这一点和整页链路接起来。
15 状态要单源
这一节不是只给一句结论,而是把"状态要单源"放进整个 第17章 的链路里看。读者需要看到输入、处理和结果,所以这里会把实现边界也一起讲清。
这一节的落点是:状态要单源 不能只停在页面上看起来对,还要真的参与到保存、恢复和验证里。
代码演示
ts
private syncTemplateByToggle(): void {
const templateId = this.watermarkTemplate;
const showTime = this.showTime;
const showAddress = this.showAddress;
this.previewTemplate = buildTemplate(templateId, showTime, showAddress);
}
这里的要点
- 先把 15 状态要单源 讲清楚,别只留一句结论。
- 再把它和状态、保存、恢复连起来。
- 最后用代码或流程图把闭环落实。
状态要单源 这一节的重点不是把内容写满,而是把这一点和整页链路接起来。
16 字段要命名清楚
这一节不是只给一句结论,而是把"字段要命名清楚"放进整个 第17章 的链路里看。读者需要看到输入、处理和结果,所以这里会把实现边界也一起讲清。
这一节的落点是:字段要命名清楚 不能只停在页面上看起来对,还要真的参与到保存、恢复和验证里。
代码演示
ts
private syncTemplateByToggle(): void {
const templateId = this.watermarkTemplate;
const showTime = this.showTime;
const showAddress = this.showAddress;
this.previewTemplate = buildTemplate(templateId, showTime, showAddress);
}
这里的要点
- 先把字段统一,避免同一个概念出现多个名字。
- 再把来源、模板和历史拆开,减少状态耦合。
- 最后把模型留出扩展口,后面加字段不会把旧逻辑撑坏。
字段要命名清楚 这一节的重点不是把内容写满,而是把这一点和整页链路接起来。
17 预览要同步刷新
这一节不是只给一句结论,而是把"预览要同步刷新"放进整个 第17章 的链路里看。读者需要看到输入、处理和结果,所以这里会把实现边界也一起讲清。
这一节的落点是:预览要同步刷新 不能只停在页面上看起来对,还要真的参与到保存、恢复和验证里。
代码演示
ts
private syncTemplateByToggle(): void {
const templateId = this.watermarkTemplate;
const showTime = this.showTime;
const showAddress = this.showAddress;
this.previewTemplate = buildTemplate(templateId, showTime, showAddress);
}
这里的要点
- 预览必须随状态刷新,而不是等用户重新进入。
- 时间、日期和地点要一起更新,避免只改一半。
- 预览要和最终结果尽量一致,不能前后两套。
预览要同步刷新 这一节的重点不是把内容写满,而是把这一点和整页链路接起来。
18 结果要反馈
这一节不是只给一句结论,而是把"结果要反馈"放进整个 第17章 的链路里看。读者需要看到输入、处理和结果,所以这里会把实现边界也一起讲清。
这一节的落点是:结果要反馈 不能只停在页面上看起来对,还要真的参与到保存、恢复和验证里。
代码演示
ts
private syncTemplateByToggle(): void {
const templateId = this.watermarkTemplate;
const showTime = this.showTime;
const showAddress = this.showAddress;
this.previewTemplate = buildTemplate(templateId, showTime, showAddress);
}
这里的要点
- 先把 18 结果要反馈 讲清楚,别只留一句结论。
- 再把它和状态、保存、恢复连起来。
- 最后用代码或流程图把闭环落实。
结果要反馈 这一节的重点不是把内容写满,而是把这一点和整页链路接起来。
19 联动要可解释
这一节不是只给一句结论,而是把"联动要可解释"放进整个 第17章 的链路里看。读者需要看到输入、处理和结果,所以这里会把实现边界也一起讲清。
这一节的落点是:联动要可解释 不能只停在页面上看起来对,还要真的参与到保存、恢复和验证里。
代码演示
ts
private syncTemplateByToggle(): void {
const templateId = this.watermarkTemplate;
const showTime = this.showTime;
const showAddress = this.showAddress;
this.previewTemplate = buildTemplate(templateId, showTime, showAddress);
}
这里的要点
- 先把 19 联动要可解释 讲清楚,别只留一句结论。
- 再把它和状态、保存、恢复连起来。
- 最后用代码或流程图把闭环落实。
联动要可解释 这一节的重点不是把内容写满,而是把这一点和整页链路接起来。
20 本章结论
这一节不是只给一句结论,而是把"本章结论"放进整个 第17章 的链路里看。读者需要看到输入、处理和结果,所以这里会把实现边界也一起讲清。
这一节的落点是:本章结论 不能只停在页面上看起来对,还要真的参与到保存、恢复和验证里。
代码演示
ts
private syncTemplateByToggle(): void {
const templateId = this.watermarkTemplate;
const showTime = this.showTime;
const showAddress = this.showAddress;
this.previewTemplate = buildTemplate(templateId, showTime, showAddress);
}
这里的要点
- 先把 20 本章结论 讲清楚,别只留一句结论。
- 再把它和状态、保存、恢复连起来。
- 最后用代码或流程图把闭环落实。
本章结论 这一节的重点不是把内容写满,而是把这一点和整页链路接起来。
本章小结
这一章把一个点讲透以后,下一章才能继续往下接,不会停留在只会看结果的层面。
流程图
#mermaid-svg-LOzCaoqm75GWTnLX{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-LOzCaoqm75GWTnLX .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-LOzCaoqm75GWTnLX .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-LOzCaoqm75GWTnLX .error-icon{fill:#552222;}#mermaid-svg-LOzCaoqm75GWTnLX .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-LOzCaoqm75GWTnLX .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-LOzCaoqm75GWTnLX .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-LOzCaoqm75GWTnLX .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-LOzCaoqm75GWTnLX .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-LOzCaoqm75GWTnLX .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-LOzCaoqm75GWTnLX .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-LOzCaoqm75GWTnLX .marker{fill:#333333;stroke:#333333;}#mermaid-svg-LOzCaoqm75GWTnLX .marker.cross{stroke:#333333;}#mermaid-svg-LOzCaoqm75GWTnLX svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-LOzCaoqm75GWTnLX p{margin:0;}#mermaid-svg-LOzCaoqm75GWTnLX .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-LOzCaoqm75GWTnLX .cluster-label text{fill:#333;}#mermaid-svg-LOzCaoqm75GWTnLX .cluster-label span{color:#333;}#mermaid-svg-LOzCaoqm75GWTnLX .cluster-label span p{background-color:transparent;}#mermaid-svg-LOzCaoqm75GWTnLX .label text,#mermaid-svg-LOzCaoqm75GWTnLX span{fill:#333;color:#333;}#mermaid-svg-LOzCaoqm75GWTnLX .node rect,#mermaid-svg-LOzCaoqm75GWTnLX .node circle,#mermaid-svg-LOzCaoqm75GWTnLX .node ellipse,#mermaid-svg-LOzCaoqm75GWTnLX .node polygon,#mermaid-svg-LOzCaoqm75GWTnLX .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-LOzCaoqm75GWTnLX .rough-node .label text,#mermaid-svg-LOzCaoqm75GWTnLX .node .label text,#mermaid-svg-LOzCaoqm75GWTnLX .image-shape .label,#mermaid-svg-LOzCaoqm75GWTnLX .icon-shape .label{text-anchor:middle;}#mermaid-svg-LOzCaoqm75GWTnLX .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-LOzCaoqm75GWTnLX .rough-node .label,#mermaid-svg-LOzCaoqm75GWTnLX .node .label,#mermaid-svg-LOzCaoqm75GWTnLX .image-shape .label,#mermaid-svg-LOzCaoqm75GWTnLX .icon-shape .label{text-align:center;}#mermaid-svg-LOzCaoqm75GWTnLX .node.clickable{cursor:pointer;}#mermaid-svg-LOzCaoqm75GWTnLX .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-LOzCaoqm75GWTnLX .arrowheadPath{fill:#333333;}#mermaid-svg-LOzCaoqm75GWTnLX .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-LOzCaoqm75GWTnLX .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-LOzCaoqm75GWTnLX .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-LOzCaoqm75GWTnLX .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-LOzCaoqm75GWTnLX .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-LOzCaoqm75GWTnLX .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-LOzCaoqm75GWTnLX .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-LOzCaoqm75GWTnLX .cluster text{fill:#333;}#mermaid-svg-LOzCaoqm75GWTnLX .cluster span{color:#333;}#mermaid-svg-LOzCaoqm75GWTnLX div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-LOzCaoqm75GWTnLX .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-LOzCaoqm75GWTnLX rect.text{fill:none;stroke-width:0;}#mermaid-svg-LOzCaoqm75GWTnLX .icon-shape,#mermaid-svg-LOzCaoqm75GWTnLX .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-LOzCaoqm75GWTnLX .icon-shape p,#mermaid-svg-LOzCaoqm75GWTnLX .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-LOzCaoqm75GWTnLX .icon-shape .label rect,#mermaid-svg-LOzCaoqm75GWTnLX .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-LOzCaoqm75GWTnLX .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-LOzCaoqm75GWTnLX .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-LOzCaoqm75GWTnLX :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 切换开关
更新状态
刷新预览
同步模板
保存设置
验证方式
- 先看每个小标题下面是不是都有正文和代码。
- 再看要点是不是围绕这个小标题本身展开。
- 最后看流程图能不能把这一章的链路串起来。