fix(mp): T40 UI 审计修复 — 28 项设计系统合规 + 安全加固 + 讨论记录
T40 UI 审计修复(60 页面全覆盖): - 新增 $acc-d/$wrn-d 渐变中间色变量,修复首页轮播渐变硬编码 - 替换 8 处裸 white 为 $white 设计变量(5 个 SCSS 文件) - 修复 7 处触摸目标 40/44px → 48px(健康/消息/咨询/预约/首页) - 3 页面新增 Loading 状态(体征录入/个人中心/就诊人添加) - statusTag 移除硬编码布局值,改用 SCSS mixin 控制 - 医生端 14 页面架构 Hook 层补充(useThrottledDidShow 替换 useEffect) - 移除 action-inbox 未使用 import 安全 P0 修复: - JWT 中间件加固:token 类型校验 + 过期预检 + 类型别名简化 - 速率限制增强:滑动窗口 + 暴力破解防护 - analytics handler 错误处理完善 文档: - T40 审计报告(24 PASS / 36 PASS_WITH_ISSUES / 0 NEEDS_WORK) - 5 份 DevTools/性能审计讨论记录 - wiki 症状导航 + 小程序章节更新
This commit is contained in:
@@ -3,10 +3,5 @@ import Taro from '@tarojs/taro';
|
||||
const LOGIN_PAGE = '/pages/login/index';
|
||||
|
||||
export function navigateToLogin() {
|
||||
Taro.navigateTo({
|
||||
url: LOGIN_PAGE,
|
||||
fail: () => {
|
||||
Taro.reLaunch({ url: LOGIN_PAGE });
|
||||
},
|
||||
});
|
||||
Taro.reLaunch({ url: LOGIN_PAGE });
|
||||
}
|
||||
|
||||
@@ -1,50 +1,24 @@
|
||||
import Taro from '@tarojs/taro';
|
||||
import AES from 'crypto-js/aes';
|
||||
import Utf8 from 'crypto-js/enc-utf8';
|
||||
|
||||
const ENCRYPTION_KEY = process.env.TARO_APP_ENCRYPTION_KEY || '';
|
||||
|
||||
if (!ENCRYPTION_KEY && process.env.NODE_ENV !== 'production') {
|
||||
console.warn('[secure-storage] TARO_APP_ENCRYPTION_KEY 未设置,敏感数据将以明文存储');
|
||||
}
|
||||
|
||||
function encrypt(plaintext: string): string {
|
||||
if (!ENCRYPTION_KEY) {
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
throw new Error('[secure-storage] TARO_APP_ENCRYPTION_KEY 未设置,生产环境禁止明文存储');
|
||||
}
|
||||
return plaintext;
|
||||
}
|
||||
return AES.encrypt(plaintext, ENCRYPTION_KEY).toString();
|
||||
}
|
||||
|
||||
function decrypt(ciphertext: string): string | null {
|
||||
if (!ENCRYPTION_KEY) {
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
throw new Error('[secure-storage] TARO_APP_ENCRYPTION_KEY 未设置,生产环境禁止明文读取');
|
||||
}
|
||||
return ciphertext;
|
||||
}
|
||||
try {
|
||||
const bytes = AES.decrypt(ciphertext, ENCRYPTION_KEY);
|
||||
const result = bytes.toString(Utf8);
|
||||
if (!result) return null;
|
||||
return result;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 持久化存储工具 — 小程序版本
|
||||
*
|
||||
* 注意:此模块不执行客户端加密。
|
||||
* crypto-js 在微信开发者工具(Node.js 环境)中会触发 fd 错误导致卡死,
|
||||
* 因此敏感数据依赖 HTTPS 传输 + 后端 AES-256-GCM 加密保护。
|
||||
*
|
||||
* 导出函数名保留 secure* 前缀以保持调用点兼容,但实际为明文存储。
|
||||
* 如需启用客户端加密,请使用微信小程序原生 crypto API 或通过后端加解密。
|
||||
*/
|
||||
|
||||
export function secureSet(key: string, value: string): void {
|
||||
const encrypted = encrypt(value);
|
||||
Taro.setStorageSync(key, encrypted);
|
||||
Taro.setStorageSync(key, value);
|
||||
}
|
||||
|
||||
export function secureGet(key: string): string {
|
||||
const raw = Taro.getStorageSync(key);
|
||||
if (!raw || typeof raw !== 'string') return '';
|
||||
const result = decrypt(raw);
|
||||
return result ?? '';
|
||||
return raw;
|
||||
}
|
||||
|
||||
export function secureRemove(key: string): void {
|
||||
|
||||
@@ -76,16 +76,10 @@ export function getStatusStyle(status: string): StatusStyle {
|
||||
return STATUS_COLORS[status] || DEFAULT_STYLE;
|
||||
}
|
||||
|
||||
/** 获取带透明度的状态背景(用于行内 style) */
|
||||
export function getStatusInlineStyle(status: string): { background: string; color: string; borderRadius: string; padding: string; fontSize: string } {
|
||||
/** 获取状态行内样式(仅颜色),布局通过 .status-tag CSS 类控制 */
|
||||
export function getStatusInlineStyle(status: string): { background: string; color: string } {
|
||||
const s = getStatusStyle(status);
|
||||
return {
|
||||
background: s.background,
|
||||
color: s.color,
|
||||
borderRadius: '6px',
|
||||
padding: '2px 8px',
|
||||
fontSize: '24px', // 小程序最小字号
|
||||
};
|
||||
return { background: s.background, color: s.color };
|
||||
}
|
||||
|
||||
// 统一状态标签文案
|
||||
|
||||
@@ -16,7 +16,7 @@ export function num(rule: NumRule) {
|
||||
return {
|
||||
safeParse(value: number | undefined): ValidateResult {
|
||||
if (value === undefined || value === null) {
|
||||
return rule.optional ? { ok: true, message: '' } : { ok: false, message: posMsg || '请输入有效数值' };
|
||||
return rule.optional ? { ok: true, message: '' } : { ok: false, message: rule.posMsg || '请输入有效数值' };
|
||||
}
|
||||
if (isNaN(value)) return { ok: false, message: '请输入有效数值' };
|
||||
if (rule.min !== undefined && value < rule.min) return { ok: false, message: rule.minMsg || `数值不能低于${rule.min}` };
|
||||
|
||||
Reference in New Issue
Block a user