Files
xianyan/docs/widget_management_preview.html
Developer 6f5400ec4b feat: 5.4.0版本大更新,新增多端桌面小组件与多项功能优化
- 重构「灵感」模块为「发现」模块,统一页面命名与文案
- 新增flutter_tts语音朗读依赖与鸿蒙Nearby配对方式
- 添加Android/iOS/鸿蒙全平台桌面小组件支持(7种类型)
- 完善文件传输模块,新增画布邀请消息与删除会话功能
- 优化协作画布光标广播节流逻辑,修复已知bug
- 更新应用英文名与隐私政策入口,新增翻译API抽象层
- 移除用户中心多余的加号按钮,完善空状态组件类型
2026-05-19 05:39:50 +08:00

1512 lines
57 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>闲言APP — 桌面小部件管理方案</title>
<style>
:root {
--primary: #6C63FF;
--primary-light: #8B83FF;
--primary-dark: #4A42E0;
--secondary: #FF6B6B;
--accent: #4ECDC4;
--success: #10B981;
--warning: #F59E0B;
--error: #EF4444;
--info: #3B82F6;
--bg-primary: #FAFAFA;
--bg-secondary: #F5F5F5;
--bg-card: #FFFFFF;
--bg-elevated: #FFFFFF;
--text-primary: #1A1A2E;
--text-secondary: #6B7280;
--text-hint: #9CA3AF;
--radius-sm: 4px;
--radius-md: 8px;
--radius-lg: 12px;
--radius-xl: 16px;
--radius-2xl: 20px;
--radius-full: 9999px;
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1);
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1);
--shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1);
--space-1: 4px;
--space-2: 8px;
--space-3: 16px;
--space-4: 24px;
--space-5: 32px;
--space-6: 48px;
--font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'SF Pro Text', 'Helvetica Neue', Arial, sans-serif;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: var(--font-family);
background: var(--bg-secondary);
color: var(--text-primary);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
.container {
max-width: 960px;
margin: 0 auto;
padding: var(--space-5);
}
/* Header */
.header {
text-align: center;
padding: var(--space-6) 0 var(--space-5);
}
.header h1 {
font-size: 2rem;
font-weight: 700;
background: linear-gradient(135deg, var(--primary), var(--accent));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin-bottom: var(--space-2);
}
.header p {
color: var(--text-secondary);
font-size: 1rem;
}
/* Section */
.section {
background: var(--bg-card);
border-radius: var(--radius-xl);
padding: var(--space-5);
margin-bottom: var(--space-4);
box-shadow: var(--shadow-sm);
}
.section-title {
font-size: 1.25rem;
font-weight: 700;
margin-bottom: var(--space-4);
display: flex;
align-items: center;
gap: var(--space-2);
}
.section-title .icon {
width: 28px;
height: 28px;
border-radius: var(--radius-md);
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
flex-shrink: 0;
}
/* Badge */
.badge {
display: inline-flex;
align-items: center;
padding: 2px 10px;
border-radius: var(--radius-full);
font-size: 0.75rem;
font-weight: 600;
gap: 4px;
}
.badge-primary { background: rgba(108,99,255,0.12); color: var(--primary); }
.badge-success { background: rgba(16,185,129,0.12); color: var(--success); }
.badge-warning { background: rgba(245,158,11,0.12); color: var(--warning); }
.badge-error { background: rgba(239,68,68,0.12); color: var(--error); }
.badge-info { background: rgba(59,130,246,0.12); color: var(--info); }
.badge-accent { background: rgba(78,205,196,0.12); color: var(--accent); }
/* Widget Card Grid */
.widget-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: var(--space-4);
}
.widget-card {
background: var(--bg-primary);
border-radius: var(--radius-lg);
padding: var(--space-4);
border: 1px solid rgba(0,0,0,0.06);
transition: all 0.2s ease;
position: relative;
overflow: hidden;
}
.widget-card:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-lg);
border-color: var(--primary-light);
}
.widget-card .card-header {
display: flex;
align-items: center;
gap: var(--space-3);
margin-bottom: var(--space-3);
}
.widget-card .card-icon {
width: 48px;
height: 48px;
border-radius: var(--radius-lg);
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
flex-shrink: 0;
}
.widget-card .card-title {
font-size: 1rem;
font-weight: 600;
}
.widget-card .card-subtitle {
font-size: 0.8rem;
color: var(--text-secondary);
}
.widget-card .card-desc {
font-size: 0.85rem;
color: var(--text-secondary);
margin-bottom: var(--space-3);
line-height: 1.5;
}
.widget-card .card-badges {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-bottom: var(--space-3);
}
.widget-card .card-preview {
background: linear-gradient(135deg, #1A1A2E, #2D2D44);
border-radius: var(--radius-md);
padding: var(--space-3);
color: #fff;
min-height: 120px;
display: flex;
flex-direction: column;
justify-content: space-between;
position: relative;
overflow: hidden;
}
.widget-card .card-preview::before {
content: '';
position: absolute;
top: -50%;
right: -30%;
width: 120px;
height: 120px;
border-radius: 50%;
background: rgba(108,99,255,0.15);
filter: blur(30px);
}
.widget-card .card-preview .preview-label {
font-size: 0.7rem;
color: rgba(255,255,255,0.5);
text-transform: uppercase;
letter-spacing: 1px;
}
.widget-card .card-preview .preview-content {
font-size: 1rem;
font-weight: 500;
position: relative;
z-index: 1;
}
.widget-card .card-preview .preview-author {
font-size: 0.8rem;
color: rgba(255,255,255,0.6);
text-align: right;
position: relative;
z-index: 1;
}
.widget-card .card-preview .preview-count {
font-size: 2rem;
font-weight: 700;
color: var(--accent);
position: relative;
z-index: 1;
}
.widget-card .card-preview .preview-unit {
font-size: 0.8rem;
color: rgba(255,255,255,0.5);
position: relative;
z-index: 1;
}
.widget-card .card-status {
position: absolute;
top: var(--space-3);
right: var(--space-3);
}
/* Platform Table */
.platform-table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
border-radius: var(--radius-lg);
overflow: hidden;
border: 1px solid rgba(0,0,0,0.06);
}
.platform-table th {
background: var(--bg-primary);
padding: var(--space-3) var(--space-4);
text-align: left;
font-weight: 600;
font-size: 0.85rem;
color: var(--text-secondary);
border-bottom: 1px solid rgba(0,0,0,0.06);
}
.platform-table td {
padding: var(--space-3) var(--space-4);
font-size: 0.85rem;
border-bottom: 1px solid rgba(0,0,0,0.04);
vertical-align: top;
}
.platform-table tr:last-child td { border-bottom: none; }
.platform-table tr:hover td { background: rgba(108,99,255,0.03); }
/* Phone Mockup */
.phone-mockup {
width: 375px;
margin: 0 auto;
background: var(--bg-card);
border-radius: 40px;
border: 8px solid #1A1A2E;
overflow: hidden;
box-shadow: var(--shadow-xl);
position: relative;
}
.phone-mockup .notch {
width: 150px;
height: 28px;
background: #1A1A2E;
border-radius: 0 0 20px 20px;
margin: 0 auto;
position: relative;
z-index: 10;
}
.phone-mockup .screen {
min-height: 700px;
background: var(--bg-secondary);
overflow-y: auto;
}
.phone-mockup .nav-bar {
display: flex;
align-items: center;
padding: var(--space-2) var(--space-3);
background: rgba(255,255,255,0.85);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
position: sticky;
top: 0;
z-index: 5;
border-bottom: 0.5px solid rgba(0,0,0,0.08);
}
.phone-mockup .nav-bar .back-btn {
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
color: var(--primary);
cursor: pointer;
}
.phone-mockup .nav-bar .nav-title {
flex: 1;
text-align: center;
font-weight: 600;
font-size: 1rem;
}
.phone-mockup .nav-bar .nav-right {
width: 32px;
}
/* Widget Management Page UI */
.mgmt-section {
padding: var(--space-3);
}
.mgmt-section-title {
font-size: 0.8rem;
font-weight: 600;
color: var(--text-secondary);
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: var(--space-2);
padding-left: var(--space-1);
}
.mgmt-widget-item {
display: flex;
align-items: center;
gap: var(--space-3);
padding: var(--space-3);
background: var(--bg-card);
border-radius: var(--radius-lg);
margin-bottom: var(--space-2);
box-shadow: var(--shadow-sm);
transition: all 0.2s;
}
.mgmt-widget-item:hover {
box-shadow: var(--shadow-md);
}
.mgmt-widget-icon {
width: 44px;
height: 44px;
border-radius: var(--radius-md);
display: flex;
align-items: center;
justify-content: center;
font-size: 22px;
flex-shrink: 0;
}
.mgmt-widget-info {
flex: 1;
min-width: 0;
}
.mgmt-widget-name {
font-size: 0.9rem;
font-weight: 600;
}
.mgmt-widget-desc {
font-size: 0.75rem;
color: var(--text-secondary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.mgmt-widget-action {
display: flex;
align-items: center;
gap: var(--space-2);
flex-shrink: 0;
}
.mgmt-btn {
padding: 6px 14px;
border-radius: var(--radius-full);
font-size: 0.75rem;
font-weight: 600;
border: none;
cursor: pointer;
transition: all 0.2s;
}
.mgmt-btn-primary {
background: var(--primary);
color: #fff;
}
.mgmt-btn-primary:hover {
background: var(--primary-dark);
}
.mgmt-btn-outline {
background: transparent;
color: var(--primary);
border: 1.5px solid var(--primary);
}
.mgmt-btn-outline:hover {
background: rgba(108,99,255,0.08);
}
.mgmt-btn-success {
background: var(--success);
color: #fff;
}
/* Toggle Switch */
.toggle {
width: 44px;
height: 26px;
border-radius: 13px;
background: #D1D5DB;
position: relative;
cursor: pointer;
transition: background 0.3s;
flex-shrink: 0;
}
.toggle.active {
background: var(--success);
}
.toggle::after {
content: '';
width: 22px;
height: 22px;
border-radius: 50%;
background: #fff;
position: absolute;
top: 2px;
left: 2px;
transition: transform 0.3s;
box-shadow: 0 1px 3px rgba(0,0,0,0.15);
}
.toggle.active::after {
transform: translateX(18px);
}
/* Info Banner */
.info-banner {
display: flex;
align-items: flex-start;
gap: var(--space-3);
padding: var(--space-3);
border-radius: var(--radius-lg);
margin-bottom: var(--space-3);
}
.info-banner-tip {
background: rgba(59,130,246,0.08);
border: 1px solid rgba(59,130,246,0.15);
}
.info-banner-warn {
background: rgba(245,158,11,0.08);
border: 1px solid rgba(245,158,11,0.15);
}
.info-banner-icon {
font-size: 18px;
flex-shrink: 0;
margin-top: 1px;
}
.info-banner-text {
font-size: 0.8rem;
color: var(--text-secondary);
line-height: 1.5;
}
/* Architecture Diagram */
.arch-diagram {
background: var(--bg-primary);
border-radius: var(--radius-lg);
padding: var(--space-4);
border: 1px solid rgba(0,0,0,0.06);
overflow-x: auto;
}
.arch-layer {
display: flex;
align-items: center;
gap: var(--space-3);
margin-bottom: var(--space-2);
position: relative;
}
.arch-layer:last-child { margin-bottom: 0; }
.arch-layer-label {
width: 100px;
font-size: 0.75rem;
font-weight: 600;
color: var(--text-secondary);
text-align: right;
flex-shrink: 0;
}
.arch-layer-content {
display: flex;
gap: var(--space-2);
flex: 1;
flex-wrap: wrap;
}
.arch-box {
padding: 6px 12px;
border-radius: var(--radius-md);
font-size: 0.75rem;
font-weight: 500;
border: 1.5px solid;
white-space: nowrap;
}
.arch-box-flutter {
background: rgba(108,99,255,0.08);
border-color: rgba(108,99,255,0.3);
color: var(--primary);
}
.arch-box-android {
background: rgba(16,185,129,0.08);
border-color: rgba(16,185,129,0.3);
color: var(--success);
}
.arch-box-ios {
background: rgba(59,130,246,0.08);
border-color: rgba(59,130,246,0.3);
color: var(--info);
}
.arch-box-ohos {
background: rgba(245,158,11,0.08);
border-color: rgba(245,158,11,0.3);
color: var(--warning);
}
.arch-connector {
width: 2px;
height: 12px;
background: rgba(0,0,0,0.1);
margin-left: 106px;
}
/* File Tree */
.file-tree {
font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
font-size: 0.8rem;
line-height: 1.8;
color: var(--text-primary);
background: var(--bg-primary);
border-radius: var(--radius-lg);
padding: var(--space-4);
border: 1px solid rgba(0,0,0,0.06);
overflow-x: auto;
}
.file-tree .dir { color: var(--primary); font-weight: 600; }
.file-tree .file { color: var(--text-secondary); }
.file-tree .new { color: var(--success); font-weight: 600; }
.file-tree .comment { color: var(--text-hint); font-style: italic; }
/* Tab system */
.tabs {
display: flex;
gap: 2px;
background: var(--bg-primary);
border-radius: var(--radius-lg);
padding: 3px;
margin-bottom: var(--space-4);
}
.tab {
flex: 1;
padding: 8px 16px;
border-radius: var(--radius-md);
font-size: 0.85rem;
font-weight: 500;
text-align: center;
cursor: pointer;
transition: all 0.2s;
color: var(--text-secondary);
border: none;
background: transparent;
}
.tab.active {
background: var(--bg-card);
color: var(--text-primary);
box-shadow: var(--shadow-sm);
font-weight: 600;
}
.tab:hover:not(.active) {
color: var(--text-primary);
}
.tab-content { display: none; }
.tab-content.active { display: block; }
/* Progress Steps */
.steps {
display: flex;
gap: var(--space-2);
margin-bottom: var(--space-5);
flex-wrap: wrap;
}
.step {
display: flex;
align-items: center;
gap: var(--space-2);
padding: 6px 14px;
border-radius: var(--radius-full);
font-size: 0.8rem;
font-weight: 500;
background: var(--bg-primary);
border: 1px solid rgba(0,0,0,0.06);
}
.step-num {
width: 22px;
height: 22px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.7rem;
font-weight: 700;
background: var(--primary);
color: #fff;
}
/* Comparison cards */
.compare-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: var(--space-3);
}
.compare-card {
background: var(--bg-primary);
border-radius: var(--radius-lg);
padding: var(--space-4);
border: 1px solid rgba(0,0,0,0.06);
text-align: center;
}
.compare-card .platform-logo {
font-size: 2rem;
margin-bottom: var(--space-2);
}
.compare-card .platform-name {
font-weight: 700;
font-size: 1rem;
margin-bottom: var(--space-1);
}
.compare-card .platform-version {
font-size: 0.75rem;
color: var(--text-hint);
margin-bottom: var(--space-3);
}
.compare-card .feature-list {
text-align: left;
font-size: 0.8rem;
}
.compare-card .feature-item {
display: flex;
align-items: center;
gap: 6px;
padding: 4px 0;
color: var(--text-secondary);
}
.feature-item .check { color: var(--success); }
.feature-item .cross { color: var(--error); }
.feature-item .partial { color: var(--warning); }
/* Responsive */
@media (max-width: 768px) {
.container { padding: var(--space-3); }
.widget-grid { grid-template-columns: 1fr; }
.compare-grid { grid-template-columns: 1fr; }
.phone-mockup { width: 100%; border-radius: 24px; border-width: 4px; }
.phone-mockup .notch { width: 120px; height: 20px; }
}
/* Animations */
@keyframes fadeInUp {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
.animate-in {
animation: fadeInUp 0.5s ease forwards;
}
.delay-1 { animation-delay: 0.1s; opacity: 0; }
.delay-2 { animation-delay: 0.2s; opacity: 0; }
.delay-3 { animation-delay: 0.3s; opacity: 0; }
.delay-4 { animation-delay: 0.4s; opacity: 0; }
.delay-5 { animation-delay: 0.5s; opacity: 0; }
</style>
</head>
<body>
<div class="container">
<!-- Header -->
<div class="header animate-in">
<h1>📱 闲言APP — 桌面小部件管理方案</h1>
<p>将「会员中心」替换为「桌面小部件」入口 · 创建小部件管理页面 · 三端原生代码适配</p>
</div>
<!-- Change Summary -->
<div class="section animate-in delay-1">
<div class="section-title">
<span class="icon" style="background:rgba(108,99,255,0.1);">🔄</span>
变更概览
</div>
<div class="steps">
<div class="step"><span class="step-num">1</span> profile_page 会员中心 → 桌面小部件</div>
<div class="step"><span class="step-num">2</span> 新建 WidgetManagementPage</div>
<div class="step"><span class="step-num">3</span> 扩展 HomeWidgetService</div>
<div class="step"><span class="step-num">4</span> Android 原生 Widget 实现</div>
<div class="step"><span class="step-num">5</span> iOS Widget Extension 实现</div>
<div class="step"><span class="step-num">6</span> 鸿蒙 Form Extension 实现</div>
</div>
<div class="info-banner info-banner-tip">
<span class="info-banner-icon">💡</span>
<span class="info-banner-text">
项目已有 <code>home_widget</code> 本地 forkv0.9.1-ohos.1),支持 Android/iOS/鸿蒙三端。
已有 <code>HomeWidgetService</code> 封装稍后读计数和每日一句数据推送。
本次扩展将新增更多小部件类型,并创建可视化管理页面。
</span>
</div>
</div>
<!-- Widget Analysis -->
<div class="section animate-in delay-2">
<div class="section-title">
<span class="icon" style="background:rgba(78,205,196,0.1);">🧩</span>
可创建的小部件分析
</div>
<div class="widget-grid">
<!-- Widget 1: Daily Sentence -->
<div class="widget-card">
<div class="card-status"><span class="badge badge-success">✅ 已有基础</span></div>
<div class="card-header">
<div class="card-icon" style="background:linear-gradient(135deg,#6C63FF,#8B83FF);">📜</div>
<div>
<div class="card-title">每日一句</div>
<div class="card-subtitle">DailySentenceWidget</div>
</div>
</div>
<div class="card-desc">每日推送一条精选句子,含作者和出处。支持小/中两种尺寸。</div>
<div class="card-badges">
<span class="badge badge-primary">iOS</span>
<span class="badge badge-success">Android</span>
<span class="badge badge-warning">鸿蒙</span>
<span class="badge badge-accent">2×1 / 4×1</span>
</div>
<div class="card-preview">
<div class="preview-label">闲言 · 每日一句</div>
<div class="preview-content">"人生如逆旅,我亦是行人。"</div>
<div class="preview-author">—— 苏轼</div>
</div>
</div>
<!-- Widget 2: Readlater -->
<div class="widget-card">
<div class="card-status"><span class="badge badge-success">✅ 已有基础</span></div>
<div class="card-header">
<div class="card-icon" style="background:linear-gradient(135deg,#4ECDC4,#44B8A8);">📖</div>
<div>
<div class="card-title">稍后读</div>
<div class="card-subtitle">ReadlaterWidget</div>
</div>
</div>
<div class="card-desc">显示稍后读未读数量和最新一条预览。点击直接打开稍后读列表。</div>
<div class="card-badges">
<span class="badge badge-primary">iOS</span>
<span class="badge badge-success">Android</span>
<span class="badge badge-warning">鸿蒙</span>
<span class="badge badge-accent">2×1 / 4×2</span>
</div>
<div class="card-preview">
<div class="preview-label">闲言 · 稍后读</div>
<div style="display:flex;align-items:baseline;gap:8px;">
<div class="preview-count">12</div>
<div class="preview-unit">条未读</div>
</div>
<div class="preview-content" style="font-size:0.85rem;opacity:0.8;">山有木兮木有枝,心悦君兮...</div>
</div>
</div>
<!-- Widget 3: Daily Card -->
<div class="widget-card">
<div class="card-status"><span class="badge badge-info">🆕 新增</span></div>
<div class="card-header">
<div class="card-icon" style="background:linear-gradient(135deg,#FF6B6B,#FF8E8E);">🃏</div>
<div>
<div class="card-title">日签卡片</div>
<div class="card-subtitle">DailyCardWidget</div>
</div>
</div>
<div class="card-desc">展示精美日签卡片,含日期、句子和背景图。每日自动更新。</div>
<div class="card-badges">
<span class="badge badge-primary">iOS</span>
<span class="badge badge-success">Android</span>
<span class="badge badge-warning">鸿蒙</span>
<span class="badge badge-accent">4×2 / 4×3</span>
</div>
<div class="card-preview" style="min-height:140px;">
<div style="display:flex;justify-content:space-between;align-items:center;">
<div class="preview-label">闲言 · 日签</div>
<div style="font-size:0.75rem;opacity:0.5;">5月19日</div>
</div>
<div class="preview-content" style="font-size:0.9rem;line-height:1.6;">落霞与孤鹜齐飞,<br>秋水共长天一色。</div>
<div class="preview-author">—— 王勃《滕王阁序》</div>
</div>
</div>
<!-- Widget 4: Daily Fortune -->
<div class="widget-card">
<div class="card-status"><span class="badge badge-info">🆕 新增</span></div>
<div class="card-header">
<div class="card-icon" style="background:linear-gradient(135deg,#F59E0B,#FBBF24);">🔮</div>
<div>
<div class="card-title">每日运势</div>
<div class="card-subtitle">DailyFortuneWidget</div>
</div>
</div>
<div class="card-desc">展示今日运势指数和幸运关键词。趣味性强,适合桌面展示。</div>
<div class="card-badges">
<span class="badge badge-primary">iOS</span>
<span class="badge badge-success">Android</span>
<span class="badge badge-warning">鸿蒙</span>
<span class="badge badge-accent">2×1 / 2×2</span>
</div>
<div class="card-preview">
<div class="preview-label">闲言 · 今日运势</div>
<div style="display:flex;align-items:center;gap:12px;">
<div style="font-size:2.5rem;">🌟</div>
<div>
<div style="font-size:1.1rem;font-weight:700;">大吉</div>
<div style="font-size:0.75rem;opacity:0.6;">幸运色:青绿 · 宜:读书</div>
</div>
</div>
</div>
</div>
<!-- Widget 5: Countdown -->
<div class="widget-card">
<div class="card-status"><span class="badge badge-info">🆕 新增</span></div>
<div class="card-header">
<div class="card-icon" style="background:linear-gradient(135deg,#3B82F6,#60A5FA);"></div>
<div>
<div class="card-title">倒计时</div>
<div class="card-subtitle">CountdownWidget</div>
</div>
</div>
<div class="card-desc">自定义倒计时事件,显示剩余天数。适合考试、节日等场景。</div>
<div class="card-badges">
<span class="badge badge-primary">iOS</span>
<span class="badge badge-success">Android</span>
<span class="badge badge-warning">鸿蒙</span>
<span class="badge badge-accent">2×1 / 4×1</span>
</div>
<div class="card-preview">
<div class="preview-label">闲言 · 倒计时</div>
<div style="display:flex;align-items:baseline;gap:8px;">
<div class="preview-count" style="color:#60A5FA;">28</div>
<div class="preview-unit">天后</div>
</div>
<div style="font-size:0.85rem;opacity:0.7;">距离 高考</div>
</div>
</div>
<!-- Widget 6: Pomodoro -->
<div class="widget-card">
<div class="card-status"><span class="badge badge-info">🆕 新增</span></div>
<div class="card-header">
<div class="card-icon" style="background:linear-gradient(135deg,#EF4444,#F87171);">🍅</div>
<div>
<div class="card-title">番茄钟</div>
<div class="card-subtitle">PomodoroWidget</div>
</div>
</div>
<div class="card-desc">桌面快捷启动番茄钟,显示当前专注进度。交互式小组件。</div>
<div class="card-badges">
<span class="badge badge-primary">iOS ⚠️</span>
<span class="badge badge-success">Android</span>
<span class="badge badge-warning">鸿蒙</span>
<span class="badge badge-accent">2×2 / 4×2</span>
</div>
<div class="card-preview">
<div class="preview-label">闲言 · 番茄钟</div>
<div style="text-align:center;padding:8px 0;">
<div style="font-size:2rem;font-weight:700;">25:00</div>
<div style="font-size:0.75rem;opacity:0.5;margin-top:4px;">点击开始专注</div>
</div>
</div>
</div>
<!-- Widget 7: Solar Term -->
<div class="widget-card">
<div class="card-status"><span class="badge badge-info">🆕 新增</span></div>
<div class="card-header">
<div class="card-icon" style="background:linear-gradient(135deg,#10B981,#34D399);">🌿</div>
<div>
<div class="card-title">节气诗词</div>
<div class="card-subtitle">SolarTermWidget</div>
</div>
</div>
<div class="card-desc">展示当前节气信息和对应诗词。文化气息浓厚,适合桌面展示。</div>
<div class="card-badges">
<span class="badge badge-primary">iOS</span>
<span class="badge badge-success">Android</span>
<span class="badge badge-warning">鸿蒙</span>
<span class="badge badge-accent">2×2 / 4×2</span>
</div>
<div class="card-preview">
<div class="preview-label">闲言 · 节气</div>
<div style="display:flex;align-items:center;gap:12px;">
<div style="font-size:2rem;">🌾</div>
<div>
<div style="font-size:1rem;font-weight:600;">小满</div>
<div style="font-size:0.7rem;opacity:0.5;">5月21日 · 夏熟作物初熟</div>
</div>
</div>
<div style="font-size:0.8rem;opacity:0.7;margin-top:6px;">夜莺啼绿柳,皓月醒长空</div>
</div>
</div>
<!-- Widget 8: Check-in -->
<div class="widget-card">
<div class="card-status"><span class="badge badge-info">🆕 新增</span></div>
<div class="card-header">
<div class="card-icon" style="background:linear-gradient(135deg,#8B5CF6,#A78BFA);"></div>
<div>
<div class="card-title">每日签到</div>
<div class="card-subtitle">CheckinWidget</div>
</div>
</div>
<div class="card-desc">展示连续签到天数和今日签到状态。桌面快捷签到按钮。</div>
<div class="card-badges">
<span class="badge badge-primary">iOS</span>
<span class="badge badge-success">Android</span>
<span class="badge badge-warning">鸿蒙</span>
<span class="badge badge-accent">2×1 / 4×1</span>
</div>
<div class="card-preview">
<div class="preview-label">闲言 · 签到</div>
<div style="display:flex;align-items:center;justify-content:space-between;">
<div>
<div style="font-size:0.8rem;opacity:0.6;">连续签到</div>
<div style="font-size:1.8rem;font-weight:700;color:#A78BFA;">7天</div>
</div>
<div style="padding:6px 16px;border-radius:20px;background:rgba(167,139,250,0.3);font-size:0.8rem;font-weight:600;">今日签到</div>
</div>
</div>
</div>
</div>
</div>
<!-- Management Page UI Preview -->
<div class="section animate-in delay-3">
<div class="section-title">
<span class="icon" style="background:rgba(108,99,255,0.1);">📱</span>
管理页面 UI 方案
</div>
<div style="display:flex;gap:var(--space-5);flex-wrap:wrap;justify-content:center;">
<!-- Phone Mockup -->
<div class="phone-mockup">
<div class="notch"></div>
<div class="screen">
<div class="nav-bar">
<div class="back-btn"></div>
<div class="nav-title">桌面小部件</div>
<div class="nav-right"></div>
</div>
<div class="mgmt-section">
<div class="info-banner info-banner-tip" style="margin-bottom:var(--space-3);">
<span class="info-banner-icon">💡</span>
<span class="info-banner-text">长按桌面空白处可添加小部件,或在下方点击「添加到桌面」</span>
</div>
<div class="mgmt-section-title">已添加的小部件</div>
<div class="mgmt-widget-item">
<div class="mgmt-widget-icon" style="background:linear-gradient(135deg,#6C63FF,#8B83FF);">📜</div>
<div class="mgmt-widget-info">
<div class="mgmt-widget-name">每日一句</div>
<div class="mgmt-widget-desc">每日推送精选句子 · 已添加到桌面</div>
</div>
<div class="mgmt-widget-action">
<div class="toggle active" onclick="this.classList.toggle('active')"></div>
</div>
</div>
<div class="mgmt-widget-item">
<div class="mgmt-widget-icon" style="background:linear-gradient(135deg,#4ECDC4,#44B8A8);">📖</div>
<div class="mgmt-widget-info">
<div class="mgmt-widget-name">稍后读</div>
<div class="mgmt-widget-desc">12条未读 · 已添加到桌面</div>
</div>
<div class="mgmt-widget-action">
<div class="toggle active" onclick="this.classList.toggle('active')"></div>
</div>
</div>
<div class="mgmt-widget-item">
<div class="mgmt-widget-icon" style="background:linear-gradient(135deg,#FF6B6B,#FF8E8E);">🃏</div>
<div class="mgmt-widget-info">
<div class="mgmt-widget-name">日签卡片</div>
<div class="mgmt-widget-desc">精美日签 · 已添加但未启用</div>
</div>
<div class="mgmt-widget-action">
<div class="toggle" onclick="this.classList.toggle('active')"></div>
</div>
</div>
</div>
<div class="mgmt-section" style="padding-top:0;">
<div class="mgmt-section-title">更多小部件</div>
<div class="mgmt-widget-item">
<div class="mgmt-widget-icon" style="background:linear-gradient(135deg,#F59E0B,#FBBF24);">🔮</div>
<div class="mgmt-widget-info">
<div class="mgmt-widget-name">每日运势</div>
<div class="mgmt-widget-desc">今日运势指数和幸运关键词</div>
</div>
<div class="mgmt-widget-action">
<button class="mgmt-btn mgmt-btn-primary">添加</button>
</div>
</div>
<div class="mgmt-widget-item">
<div class="mgmt-widget-icon" style="background:linear-gradient(135deg,#3B82F6,#60A5FA);"></div>
<div class="mgmt-widget-info">
<div class="mgmt-widget-name">倒计时</div>
<div class="mgmt-widget-desc">自定义倒计时事件</div>
</div>
<div class="mgmt-widget-action">
<button class="mgmt-btn mgmt-btn-primary">添加</button>
</div>
</div>
<div class="mgmt-widget-item">
<div class="mgmt-widget-icon" style="background:linear-gradient(135deg,#EF4444,#F87171);">🍅</div>
<div class="mgmt-widget-info">
<div class="mgmt-widget-name">番茄钟</div>
<div class="mgmt-widget-desc">桌面快捷专注计时</div>
</div>
<div class="mgmt-widget-action">
<button class="mgmt-btn mgmt-btn-primary">添加</button>
</div>
</div>
<div class="mgmt-widget-item">
<div class="mgmt-widget-icon" style="background:linear-gradient(135deg,#10B981,#34D399);">🌿</div>
<div class="mgmt-widget-info">
<div class="mgmt-widget-name">节气诗词</div>
<div class="mgmt-widget-desc">当前节气与对应诗词</div>
</div>
<div class="mgmt-widget-action">
<button class="mgmt-btn mgmt-btn-primary">添加</button>
</div>
</div>
<div class="mgmt-widget-item">
<div class="mgmt-widget-icon" style="background:linear-gradient(135deg,#8B5CF6,#A78BFA);"></div>
<div class="mgmt-widget-info">
<div class="mgmt-widget-name">每日签到</div>
<div class="mgmt-widget-desc">连续签到天数和快捷签到</div>
</div>
<div class="mgmt-widget-action">
<button class="mgmt-btn mgmt-btn-primary">添加</button>
</div>
</div>
</div>
<div class="mgmt-section" style="padding-top:0;">
<div class="mgmt-section-title">平台说明</div>
<div class="info-banner info-banner-warn">
<span class="info-banner-icon">⚠️</span>
<span class="info-banner-text">
iOS 小部件需在 WidgetKit 中配置Android 需在系统桌面长按添加;
鸿蒙需通过服务卡片添加。各平台操作方式略有差异。
</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Cross Platform -->
<div class="section animate-in delay-4">
<div class="section-title">
<span class="icon" style="background:rgba(16,185,129,0.1);">🌍</span>
跨平台兼容性分析
</div>
<div class="compare-grid" style="margin-bottom:var(--space-4);">
<div class="compare-card">
<div class="platform-logo">🤖</div>
<div class="platform-name">Android</div>
<div class="platform-version">API 26+ (Android 8.0)</div>
<div class="feature-list">
<div class="feature-item"><span class="check"></span> AppWidgetProvider</div>
<div class="feature-item"><span class="check"></span> RemoteViews 布局</div>
<div class="feature-item"><span class="check"></span> Glance (Jetpack)</div>
<div class="feature-item"><span class="check"></span> requestPinWidget</div>
<div class="feature-item"><span class="check"></span> 交互式按钮</div>
<div class="feature-item"><span class="check"></span> 自由缩放</div>
<div class="feature-item"><span class="check"></span> 后台更新</div>
<div class="feature-item"><span class="check"></span> renderFlutterWidget</div>
</div>
</div>
<div class="compare-card">
<div class="platform-logo">🍎</div>
<div class="platform-name">iOS</div>
<div class="platform-version">iOS 16+ (WidgetKit)</div>
<div class="feature-list">
<div class="feature-item"><span class="check"></span> WidgetKit Extension</div>
<div class="feature-item"><span class="check"></span> SwiftUI 视图</div>
<div class="feature-item"><span class="check"></span> Small/Medium/Large</div>
<div class="feature-item"><span class="check"></span> Live Activity</div>
<div class="feature-item"><span class="partial">⚠️</span> 交互式按钮 (iOS 17+)</div>
<div class="feature-item"><span class="partial">⚠️</span> 番茄钟实时刷新受限</div>
<div class="feature-item"><span class="check"></span> App Group 数据共享</div>
<div class="feature-item"><span class="check"></span> renderFlutterWidget</div>
</div>
</div>
<div class="compare-card">
<div class="platform-logo">🔴</div>
<div class="platform-name">HarmonyOS</div>
<div class="platform-version">API 12+ (Form Extension)</div>
<div class="feature-list">
<div class="feature-item"><span class="check"></span> Form Extension</div>
<div class="feature-item"><span class="check"></span> ArkTS/ArkUI 布局</div>
<div class="feature-item"><span class="check"></span> 1×2 / 2×2 / 2×4 / 4×4</div>
<div class="feature-item"><span class="partial">⚠️</span> 交互能力有限</div>
<div class="feature-item"><span class="cross"></span> 无 requestPinWidget</div>
<div class="feature-item"><span class="cross"></span> 无实时刷新</div>
<div class="feature-item"><span class="check"></span> Preferences 数据共享</div>
<div class="feature-item"><span class="cross"></span> 无 renderFlutterWidget</div>
</div>
</div>
</div>
<table class="platform-table">
<thead>
<tr>
<th>小部件</th>
<th>Android</th>
<th>iOS</th>
<th>鸿蒙</th>
<th>优先级</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>📜 每日一句</strong></td>
<td><span class="badge badge-success">✅ 完整</span></td>
<td><span class="badge badge-success">✅ 完整</span></td>
<td><span class="badge badge-warning">⚠️ 基础</span></td>
<td><span class="badge badge-error">P0</span></td>
</tr>
<tr>
<td><strong>📖 稍后读</strong></td>
<td><span class="badge badge-success">✅ 完整</span></td>
<td><span class="badge badge-success">✅ 完整</span></td>
<td><span class="badge badge-warning">⚠️ 基础</span></td>
<td><span class="badge badge-error">P0</span></td>
</tr>
<tr>
<td><strong>🃏 日签卡片</strong></td>
<td><span class="badge badge-info">🆕 新增</span></td>
<td><span class="badge badge-info">🆕 新增</span></td>
<td><span class="badge badge-info">🆕 新增</span></td>
<td><span class="badge badge-error">P1</span></td>
</tr>
<tr>
<td><strong>🔮 每日运势</strong></td>
<td><span class="badge badge-info">🆕 新增</span></td>
<td><span class="badge badge-info">🆕 新增</span></td>
<td><span class="badge badge-info">🆕 新增</span></td>
<td><span class="badge badge-warning">P2</span></td>
</tr>
<tr>
<td><strong>⏳ 倒计时</strong></td>
<td><span class="badge badge-info">🆕 新增</span></td>
<td><span class="badge badge-info">🆕 新增</span></td>
<td><span class="badge badge-info">🆕 新增</span></td>
<td><span class="badge badge-warning">P2</span></td>
</tr>
<tr>
<td><strong>🍅 番茄钟</strong></td>
<td><span class="badge badge-info">🆕 新增</span></td>
<td><span class="badge badge-warning">⚠️ 受限</span></td>
<td><span class="badge badge-warning">⚠️ 受限</span></td>
<td><span class="badge badge-accent">P3</span></td>
</tr>
<tr>
<td><strong>🌿 节气诗词</strong></td>
<td><span class="badge badge-info">🆕 新增</span></td>
<td><span class="badge badge-info">🆕 新增</span></td>
<td><span class="badge badge-info">🆕 新增</span></td>
<td><span class="badge badge-accent">P3</span></td>
</tr>
<tr>
<td><strong>✅ 每日签到</strong></td>
<td><span class="badge badge-info">🆕 新增</span></td>
<td><span class="badge badge-info">🆕 新增</span></td>
<td><span class="badge badge-info">🆕 新增</span></td>
<td><span class="badge badge-accent">P3</span></td>
</tr>
</tbody>
</table>
</div>
<!-- Architecture -->
<div class="section animate-in delay-5">
<div class="section-title">
<span class="icon" style="background:rgba(59,130,246,0.1);">🏗️</span>
架构设计与文件结构
</div>
<div class="tabs">
<button class="tab active" onclick="switchTab(event, 'arch-diagram-tab')">架构图</button>
<button class="tab" onclick="switchTab(event, 'flutter-files-tab')">Flutter 文件</button>
<button class="tab" onclick="switchTab(event, 'android-files-tab')">Android 原生</button>
<button class="tab" onclick="switchTab(event, 'ios-files-tab')">iOS 原生</button>
<button class="tab" onclick="switchTab(event, 'ohos-files-tab')">鸿蒙原生</button>
</div>
<!-- Architecture Diagram -->
<div id="arch-diagram-tab" class="tab-content active">
<div class="arch-diagram">
<div class="arch-layer">
<div class="arch-layer-label">Flutter UI</div>
<div class="arch-layer-content">
<div class="arch-box arch-box-flutter">WidgetManagementPage</div>
<div class="arch-box arch-box-flutter">WidgetCard</div>
<div class="arch-box arch-box-flutter">WidgetProvider</div>
</div>
</div>
<div class="arch-connector"></div>
<div class="arch-layer">
<div class="arch-layer-label">Flutter Service</div>
<div class="arch-layer-content">
<div class="arch-box arch-box-flutter">HomeWidgetService</div>
<div class="arch-box arch-box-flutter">WidgetDataSync</div>
<div class="arch-box arch-box-flutter">WidgetClickHandler</div>
</div>
</div>
<div class="arch-connector"></div>
<div class="arch-layer">
<div class="arch-layer-label">home_widget</div>
<div class="arch-layer-content">
<div class="arch-box arch-box-flutter">saveWidgetData</div>
<div class="arch-box arch-box-flutter">updateWidget</div>
<div class="arch-box arch-box-flutter">getInstalledWidgets</div>
<div class="arch-box arch-box-flutter">registerInteractivityCallback</div>
</div>
</div>
<div class="arch-connector"></div>
<div class="arch-layer">
<div class="arch-layer-label">Native</div>
<div class="arch-layer-content">
<div class="arch-box arch-box-android">AppWidgetProvider</div>
<div class="arch-box arch-box-android">RemoteViews</div>
<div class="arch-box arch-box-ios">WidgetKit</div>
<div class="arch-box arch-box-ios">SwiftUI</div>
<div class="arch-box arch-box-ohos">FormExtension</div>
<div class="arch-box arch-box-ohos">ArkUI</div>
</div>
</div>
</div>
</div>
<!-- Flutter Files -->
<div id="flutter-files-tab" class="tab-content">
<div class="file-tree">
<span class="dir">lib/features/widget/</span>
├── <span class="dir">models/</span>
│ ├── <span class="new">widget_type.dart</span> <span class="comment">// 小部件类型枚举 + 元数据</span>
│ └── <span class="new">widget_config.dart</span> <span class="comment">// 小部件配置模型</span>
├── <span class="dir">presentation/</span>
│ ├── <span class="new">widget_management_page.dart</span> <span class="comment">// 管理页面主入口</span>
│ └── <span class="dir">widgets/</span>
│ ├── <span class="new">widget_card.dart</span> <span class="comment">// 单个小部件卡片组件</span>
│ ├── <span class="new">widget_preview.dart</span> <span class="comment">// 小部件预览组件</span>
│ └── <span class="new">platform_guide.dart</span> <span class="comment">// 平台操作指引组件</span>
├── <span class="dir">providers/</span>
│ └── <span class="new">widget_provider.dart</span> <span class="comment">// 小部件状态管理</span>
└── <span class="dir">services/</span>
└── <span class="new">widget_data_service.dart</span> <span class="comment">// 小部件数据推送服务</span>
<span class="dir">lib/core/services/data/</span>
└── <span class="file">home_widget_service.dart</span> <span class="comment">// ← 扩展:新增多种小部件数据推送</span>
<span class="dir">lib/core/registry/</span>
└── <span class="file">page_registry.dart</span> <span class="comment">// ← 修改:注册新路由</span>
<span class="dir">lib/features/profile/</span>
└── <span class="file">profile_page.dart</span> <span class="comment">// ← 修改:会员中心 → 桌面小部件</span>
</div>
</div>
<!-- Android Files -->
<div id="android-files-tab" class="tab-content">
<div class="file-tree">
<span class="dir">android/app/src/main/</span>
├── <span class="dir">kotlin/com/xianyan/</span>
│ ├── <span class="file">MainActivity.kt</span> <span class="comment">// ← 已有</span>
│ └── <span class="dir">widget/</span>
│ ├── <span class="new">DailySentenceProvider.kt</span> <span class="comment">// 每日一句 WidgetProvider</span>
│ ├── <span class="new">ReadlaterProvider.kt</span> <span class="comment">// 稍后读 WidgetProvider</span>
│ ├── <span class="new">DailyCardProvider.kt</span> <span class="comment">// 日签卡片 WidgetProvider</span>
│ ├── <span class="new">FortuneProvider.kt</span> <span class="comment">// 每日运势 WidgetProvider</span>
│ ├── <span class="new">CountdownProvider.kt</span> <span class="comment">// 倒计时 WidgetProvider</span>
│ ├── <span class="new">PomodoroProvider.kt</span> <span class="comment">// 番茄钟 WidgetProvider</span>
│ ├── <span class="new">SolarTermProvider.kt</span> <span class="comment">// 节气诗词 WidgetProvider</span>
│ └── <span class="new">CheckinProvider.kt</span> <span class="comment">// 每日签到 WidgetProvider</span>
├── <span class="dir">res/</span>
│ ├── <span class="dir">layout/</span>
│ │ ├── <span class="new">widget_daily_sentence.xml</span> <span class="comment">// 每日一句布局</span>
│ │ ├── <span class="new">widget_readlater.xml</span> <span class="comment">// 稍后读布局</span>
│ │ ├── <span class="new">widget_daily_card.xml</span> <span class="comment">// 日签卡片布局</span>
│ │ ├── <span class="new">widget_fortune.xml</span> <span class="comment">// 运势布局</span>
│ │ ├── <span class="new">widget_countdown.xml</span> <span class="comment">// 倒计时布局</span>
│ │ ├── <span class="new">widget_pomodoro.xml</span> <span class="comment">// 番茄钟布局</span>
│ │ ├── <span class="new">widget_solar_term.xml</span> <span class="comment">// 节气布局</span>
│ │ └── <span class="new">widget_checkin.xml</span> <span class="comment">// 签到布局</span>
│ ├── <span class="dir">drawable/</span>
│ │ └── <span class="new">widget_background_*.xml</span> <span class="comment">// 各样式背景</span>
│ └── <span class="dir">xml/</span>
│ ├── <span class="new">widget_daily_sentence_info.xml</span>
│ ├── <span class="new">widget_readlater_info.xml</span>
│ ├── <span class="new">widget_daily_card_info.xml</span>
│ ├── <span class="new">widget_fortune_info.xml</span>
│ ├── <span class="new">widget_countdown_info.xml</span>
│ ├── <span class="new">widget_pomodoro_info.xml</span>
│ ├── <span class="new">widget_solar_term_info.xml</span>
│ └── <span class="new">widget_checkin_info.xml</span>
└── <span class="file">AndroidManifest.xml</span> <span class="comment">// ← 注册 receiver</span>
</div>
</div>
<!-- iOS Files -->
<div id="ios-files-tab" class="tab-content">
<div class="file-tree">
<span class="dir">ios/</span>
├── <span class="dir">Runner/</span> <span class="comment">// ← 已有</span>
└── <span class="dir">XianyanWidgets/</span> <span class="comment">// Widget Extension (新增)</span>
├── <span class="new">XianyanWidgets.swift</span> <span class="comment">// Widget 入口 + TimelineProvider</span>
├── <span class="new">DailySentenceWidget.swift</span> <span class="comment">// 每日一句视图</span>
├── <span class="new">ReadlaterWidget.swift</span> <span class="comment">// 稍后读视图</span>
├── <span class="new">DailyCardWidget.swift</span> <span class="comment">// 日签卡片视图</span>
├── <span class="new">FortuneWidget.swift</span> <span class="comment">// 每日运势视图</span>
├── <span class="new">CountdownWidget.swift</span> <span class="comment">// 倒计时视图</span>
├── <span class="new">PomodoroWidget.swift</span> <span class="comment">// 番茄钟视图 (iOS 17+)</span>
├── <span class="new">SolarTermWidget.swift</span> <span class="comment">// 节气诗词视图</span>
├── <span class="new">CheckinWidget.swift</span> <span class="comment">// 每日签到视图</span>
├── <span class="dir">Assets.xcassets/</span>
│ └── <span class="new">WidgetBackground.imageset/</span>
└── <span class="new">Info.plist</span>
<span class="comment">// 需在 Xcode 中添加 Widget Extension Target</span>
<span class="comment">// App Group: group.com.xianyan.share</span>
<span class="comment">// 支持 Small / Medium / Large 三种尺寸</span>
</div>
</div>
<!-- Ohos Files -->
<div id="ohos-files-tab" class="tab-content">
<div class="file-tree">
<span class="dir">ohos/</span>
├── <span class="dir">entry/src/main/ets/</span>
│ ├── <span class="dir">entryability/</span>
│ │ └── <span class="file">EntryAbility.ets</span> <span class="comment">// ← 已有</span>
│ └── <span class="dir">widget/</span> <span class="comment">// Form Extension (新增)</span>
│ ├── <span class="new">DailySentenceForm.ets</span> <span class="comment">// 每日一句卡片</span>
│ ├── <span class="new">ReadlaterForm.ets</span> <span class="comment">// 稍后读卡片</span>
│ ├── <span class="new">DailyCardForm.ets</span> <span class="comment">// 日签卡片</span>
│ ├── <span class="new">FortuneForm.ets</span> <span class="comment">// 每日运势卡片</span>
│ ├── <span class="new">CountdownForm.ets</span> <span class="comment">// 倒计时卡片</span>
│ ├── <span class="new">SolarTermForm.ets</span> <span class="comment">// 节气诗词卡片</span>
│ └── <span class="new">CheckinForm.ets</span> <span class="comment">// 每日签到卡片</span>
├── <span class="dir">entry/src/main/resources/</span>
│ └── <span class="dir">base/profile/</span>
│ ├── <span class="new">widget_daily_sentence.json</span> <span class="comment">// 卡片配置</span>
│ ├── <span class="new">widget_readlater.json</span>
│ ├── <span class="new">widget_daily_card.json</span>
│ ├── <span class="new">widget_fortune.json</span>
│ ├── <span class="new">widget_countdown.json</span>
│ ├── <span class="new">widget_solar_term.json</span>
│ └── <span class="new">widget_checkin.json</span>
└── <span class="file">module.json5</span> <span class="comment">// ← 注册 ExtensionAbility</span>
<span class="comment">// 鸿蒙使用 FormExtensionAbility</span>
<span class="comment">// 数据通过 Preferences 共享</span>
<span class="comment">// 布局使用 ArkUI 声明式语法</span>
<span class="comment">// 番茄钟因实时刷新限制,鸿蒙端暂不实现</span>
</div>
</div>
</div>
<!-- Implementation Plan -->
<div class="section">
<div class="section-title">
<span class="icon" style="background:rgba(245,158,11,0.1);">📋</span>
实施计划
</div>
<table class="platform-table">
<thead>
<tr>
<th>阶段</th>
<th>内容</th>
<th>涉及文件</th>
</tr>
</thead>
<tbody>
<tr>
<td><span class="badge badge-error">P0</span></td>
<td>
<strong>1. 修改入口按钮</strong><br>
profile_page 会员中心 → 桌面小部件<br>
路由 /member → /widget-management
</td>
<td>profile_page.dart, page_registry.dart, app_router.dart</td>
</tr>
<tr>
<td><span class="badge badge-error">P0</span></td>
<td>
<strong>2. 创建管理页面</strong><br>
WidgetManagementPage + Provider<br>
展示已安装/可添加小部件列表<br>
平台操作指引
</td>
<td>widget_management_page.dart, widget_provider.dart, widget_type.dart</td>
</tr>
<tr>
<td><span class="badge badge-error">P0</span></td>
<td>
<strong>3. 扩展 HomeWidgetService</strong><br>
新增日签/运势/倒计时/节气/签到数据推送<br>
统一数据 Key 管理
</td>
<td>home_widget_service.dart, widget_data_service.dart</td>
</tr>
<tr>
<td><span class="badge badge-error">P0</span></td>
<td>
<strong>4. Android 原生实现</strong><br>
8个 AppWidgetProvider + 布局 XML<br>
AndroidManifest 注册
</td>
<td>android/app/src/main/kotlin/com/xianyan/widget/</td>
</tr>
<tr>
<td><span class="badge badge-error">P0</span></td>
<td>
<strong>5. iOS Widget Extension</strong><br>
Xcode 添加 Widget Extension Target<br>
WidgetKit + SwiftUI 视图<br>
App Group 数据共享
</td>
<td>ios/XianyanWidgets/</td>
</tr>
<tr>
<td><span class="badge badge-warning">P1</span></td>
<td>
<strong>6. 鸿蒙 Form Extension</strong><br>
FormExtensionAbility + ArkUI<br>
module.json5 注册<br>
Preferences 数据共享
</td>
<td>ohos/entry/src/main/ets/widget/</td>
</tr>
<tr>
<td><span class="badge badge-accent">P2</span></td>
<td>
<strong>7. 交互式小部件</strong><br>
番茄钟实时刷新 (Android Glance)<br>
签到按钮交互<br>
iOS 17+ Button Intent
</td>
<td>PomodoroProvider, CheckinProvider</td>
</tr>
</tbody>
</table>
<div class="info-banner info-banner-warn" style="margin-top:var(--space-3);">
<span class="info-banner-icon">⚠️</span>
<span class="info-banner-text">
<strong>注意事项:</strong><br>
1. iOS Widget Extension 需在 Xcode 中手动添加 Target无法通过 Flutter CLI 自动完成<br>
2. 鸿蒙 Form Extension 需在 DevEco Studio 中配置module.json5 需手动注册<br>
3. Android 的 requestPinWidget 仅部分启动器支持<br>
4. 番茄钟小部件在 iOS/鸿蒙上受系统限制,无法实时刷新倒计时<br>
5. 各平台小部件更新频率受系统限制Android 最少30分钟iOS 由系统决定)
</span>
</div>
</div>
</div>
<script>
function switchTab(event, tabId) {
const tabs = event.target.closest('.tabs');
const section = tabs.closest('.section');
tabs.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
event.target.classList.add('active');
section.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
document.getElementById(tabId).classList.add('active');
}
</script>
</body>
</html>