fix(mp): DevTools 卡死 + 主包 2MB→766KB + 代码质量 4 项全通过

根因:主包 2MB 全量组件注入导致 DevTools 渲染引擎内存渐增,
叠加离线时固定 3s 抑制期后的请求洪泛。

修复:
- app.config.ts 添加 lazyCodeLoading: requiredComponents
  主包 2.0MB→766KB,taro.js 526→131KB,vendors.js 230→28KB
- request.ts 离线抑制改为指数退避(3s→6s→12s→30s cap)
  后端不可达时自动延长抑制,防止请求风暴
- SegmentTabs Tab 接口改为 readonly,修复 TS 编译错误
- AbortController polyfill 补齐小程序运行时缺失
- 健康首页/设备同步/健康档案/报告/设置页 UI 重构
- 文章页公开端点适配游客访问
- 健康首页 Swiper 间隔优化 4s→5s,动画 500→300ms
This commit is contained in:
iven
2026-05-24 11:32:40 +08:00
parent 675f8a4b10
commit 1e59007bd5
58 changed files with 4950 additions and 494 deletions

View File

@@ -23,6 +23,7 @@ export interface ArticleListItem {
review_note?: string;
view_count: number;
sort_order: number;
is_public: boolean;
published_at?: string;
created_at: string;
updated_at: string;
@@ -43,6 +44,7 @@ export interface CreateArticleReq {
category_id?: string;
tag_ids?: string[];
sort_order?: number;
is_public?: boolean;
}
export interface UpdateArticleReq {
@@ -55,6 +57,7 @@ export interface UpdateArticleReq {
category_id?: string;
tag_ids?: string[];
sort_order?: number;
is_public?: boolean;
version: number;
}

View File

@@ -45,6 +45,7 @@ export default function ArticleEditor() {
const [categoryId, setCategoryId] = useState<string | undefined>(undefined);
const [selectedTagIds, setSelectedTagIds] = useState<string[]>([]);
const [sortOrder, setSortOrder] = useState(0);
const [isPublic, setIsPublic] = useState(true);
const [version, setVersion] = useState(0);
// 选项数据
@@ -101,6 +102,7 @@ export default function ArticleEditor() {
setCategoryId(article.category_id);
setSelectedTagIds(article.tags?.map((t) => t.id) || []);
setSortOrder(article.sort_order);
setIsPublic(article.is_public ?? true);
setVersion(article.version);
} catch {
message.error('加载文章失败');
@@ -230,6 +232,7 @@ export default function ArticleEditor() {
category_id: categoryId,
tag_ids: selectedTagIds,
sort_order: sortOrder,
is_public: isPublic,
version,
});
message.success('文章已保存');
@@ -245,6 +248,7 @@ export default function ArticleEditor() {
category_id: categoryId,
tag_ids: selectedTagIds,
sort_order: sortOrder,
is_public: isPublic,
});
message.success('文章已创建');
navigate('/health/articles');
@@ -256,7 +260,7 @@ export default function ArticleEditor() {
}
}, [
id, isEdit, title, summary, content, coverImage, slug, categoryId,
selectedTagIds, sortOrder, version, navigate,
selectedTagIds, sortOrder, isPublic, version, navigate,
]);
const handleSubmit = useCallback(async () => {
@@ -277,6 +281,7 @@ export default function ArticleEditor() {
category_id: categoryId,
tag_ids: selectedTagIds,
sort_order: sortOrder,
is_public: isPublic,
version,
});
const updated = await articleApi.get(id);
@@ -292,6 +297,7 @@ export default function ArticleEditor() {
category_id: categoryId,
tag_ids: selectedTagIds,
sort_order: sortOrder,
is_public: isPublic,
});
currentVersion = created.version;
setVersion(created.version);
@@ -312,7 +318,7 @@ export default function ArticleEditor() {
}
}, [
id, isEdit, title, summary, content, coverImage, slug, categoryId,
selectedTagIds, sortOrder, version, navigate,
selectedTagIds, sortOrder, isPublic, version, navigate,
]);
if (loading) {
@@ -463,6 +469,8 @@ export default function ArticleEditor() {
onSlugChange={setSlug}
sortOrder={sortOrder}
onSortOrderChange={setSortOrder}
isPublic={isPublic}
onIsPublicChange={setIsPublic}
categories={categories}
tags={tags}
/>

View File

@@ -1,5 +1,5 @@
import { useState } from 'react';
import { Drawer, Input, Select, Space, Upload, Button, message } from 'antd';
import { Drawer, Input, Select, Space, Upload, Button, Switch, message } from 'antd';
import { UploadOutlined, PictureOutlined } from '@ant-design/icons';
import type { ArticleTagItem } from '../../../api/health/articles';
import { uploadFile } from '../../../api/upload';
@@ -21,6 +21,8 @@ interface ArticleSettingsDrawerProps {
onSlugChange: (v: string) => void;
sortOrder: number;
onSortOrderChange: (v: number) => void;
isPublic: boolean;
onIsPublicChange: (v: boolean) => void;
categories: { id: string; name: string }[];
tags: ArticleTagItem[];
}
@@ -53,6 +55,8 @@ export default function ArticleSettingsDrawer({
onSlugChange,
sortOrder,
onSortOrderChange,
isPublic,
onIsPublicChange,
categories,
tags,
}: ArticleSettingsDrawerProps) {
@@ -190,6 +194,17 @@ export default function ArticleSettingsDrawer({
placeholder="0"
/>
</div>
{/* 公开可见 */}
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
<div>
<label style={{ ...labelStyle(isDark), marginBottom: 2 }}></label>
<div style={{ fontSize: 12, color: isDark ? '#64748b' : '#94a3b8' }}>
</div>
</div>
<Switch checked={isPublic} onChange={onIsPublicChange} />
</div>
</div>
</Drawer>