feat: 第一次生产需求文档

This commit is contained in:
R524809
2025-10-10 13:30:16 +08:00
commit 8eb49f02b1
7 changed files with 2381 additions and 0 deletions

12
.gitignore vendored Normal file
View File

@@ -0,0 +1,12 @@
# ---> AL
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# Local History for Visual Studio Code
.history/
*.app
.snapshots/*

33
PRD.md Normal file
View File

@@ -0,0 +1,33 @@
## 核心思想
名称选择:思投录
愿景:让每笔投资都经得起思考
副标题:**投资决策与复盘工具**。
### 核心功能
#### 持仓
记录自己的股票持仓,统计各个股票持仓占比,按基金收益法统计的持仓收益。可以基于用户设置的单只股票仓位上限进行预警。
#### 交易计划
- “计划你的交易,交易你的计划”,让用户可以创建交易计划,选择股票、市场、目标价格、截止时间、投资金额或股份数(两者选一个即可)等。
- 之后到目标价后可以提醒用户。
- 用户可以为计划设置步骤,默认分三步进行买入等,可以分步设置买入价格。
#### 交易记录/复盘
- 引导用户记录每一笔交易,可以是从计划中点完成计划等操作跳转到记录页中,也可以是用户主动记录。用户可以记录每一笔交易的企业名称、买卖份数、买卖单价,最重要的是要引导用户写下买卖思考。
- 定期弹出页面应到用户写下复盘和思考。
- 用户可以参看每一笔交易的附带思考和复盘记录的时间线,让用户可以在交易中学习和成长,类似 QQ 空间的说说功能。
- 用户可以分享自己的交易时间线(不确定是否需要)。
#### 我的
不确定是否应该把 我的 作为一个 Tabbar。
- 展示一些我的信息。
- 最主要的是一些工具入口,例如 投资检查清单、复利计算器、估值工具(可以分老唐估值法和两段式现金流折现估值法)、自由目标等。
- 投资检查清单:可以分为买入和卖出两份检查清单,在每次设置交易计划时最后弹出,让用户每项检查。
- 复利计算器:侧重于投资者角度,可以填入初始金额,每年可投入的金额,调整预计的年复合增长率,然后查看未来的总收益。
- 自由目标:“让自己有的选”,自由目标可以和复利计算器和持仓结合起来,让用户自己设定一个总资产达成目标。把这个目标和财务自由、赎回自己的时间关联起来,让用户有“奔头”。
## App名称
中文名:思投录
英文名VestMind
“Vest”投资记录与管理和“Mind”思考与决策清单。用户能直观感受到这是一个与“投资”和“思考”相关的工具。
Mind”强调了投资不应是冲动行为而应是经过深思熟虑的、理性的“思维活动”这与你“让每笔投资都经得起思考”的愿景高度契合。
这个词组合起来听起来专业、简洁,且带有一种“智慧投资”的质感,能吸引那些希望提升自己投资决策质量的用户。
## 主题色
主题色需要偏紫色调,注重让人安静思考的色调。

124
README.md Normal file
View File

@@ -0,0 +1,124 @@
# 思投录 (VestMind) - 投资决策与复盘工具
## 项目概述
思投录是一款专注于投资决策与复盘的工具应用,旨在"让每笔投资都经得起思考"。本UI设计适用于移动端APP和小程序采用统一的紫色主题设计风格。
## 核心功能
### 1. 持仓管理
- 记录股票持仓信息
- 统计持仓占比和收益
- 基于仓位上限的预警功能
- 实时收益展示
### 2. 交易计划
- 创建详细的交易计划
- 设置目标价格和截止时间
- 分步骤买入计划
- 进度跟踪和提醒
### 3. 交易记录/复盘
- 记录每笔交易的详细信息
- 交易思考记录
- 时间线展示
- 定期复盘提醒
### 4. 我的工具
- 投资检查清单
- 复利计算器
- 估值工具(老唐估值法、现金流折现)
- 自由目标规划
## 设计特色
### 视觉设计
- **主题色**:紫色调(#8b5cf6, #7c3aed),营造安静思考的氛围
- **字体**Inter字体现代简洁
- **布局**:移动端优先,响应式设计
- **动效**:微妙的动画效果,提升用户体验
### 交互设计
- 底部导航栏,四个主要功能模块
- 模态框设计,支持复杂表单操作
- 卡片式布局,信息层次清晰
- 时间线展示,直观的交易历史
## 技术实现
### 文件结构
```
design-vest-mind/
├── index.html # 主页面
├── styles.css # 样式文件
├── script.js # 交互逻辑
└── README.md # 项目说明
```
### 技术栈
- **HTML5**:语义化标签,良好的可访问性
- **CSS3**Flexbox布局CSS Grid动画效果
- **JavaScript**ES6+语法,模块化设计
- **响应式设计**:适配不同屏幕尺寸
### 兼容性
- 移动端浏览器
- 微信小程序
- iOS Safari
- Android Chrome
## 使用说明
1. 打开 `index.html` 文件即可查看完整UI设计
2. 支持所有交互功能,包括:
- 页面切换
- 数据展示
- 模态框操作
- 工具计算
## 设计亮点
### 1. 紫色主题
- 主色调采用紫色渐变,符合"安静思考"的设计理念
- 渐变背景和阴影效果,营造层次感
### 2. 卡片设计
- 圆角卡片布局,现代简洁
- 悬停效果和阴影,增强交互反馈
### 3. 数据可视化
- 进度条展示计划完成度
- 颜色编码区分盈亏状态
- 时间线展示交易历史
### 4. 工具集成
- 投资检查清单,帮助理性决策
- 复利计算器,规划长期投资
- 估值工具,辅助价值判断
- 自由目标,激励投资动力
## 扩展功能
### 可添加的功能
- 数据持久化LocalStorage
- 图表展示Chart.js
- 推送通知
- 数据导入导出
- 多账户管理
### 小程序适配
- 使用微信小程序组件
- 适配小程序生命周期
- 集成微信API
## 开发建议
1. **数据管理**建议使用状态管理库如Vuex、Redux
2. **图表展示**集成ECharts或Chart.js
3. **数据持久化**使用IndexedDB或云数据库
4. **性能优化**:懒加载、虚拟滚动
5. **测试**:单元测试和端到端测试
## 总结
本UI设计完全基于PRD文档要求实现了思投录应用的核心功能界面。设计风格现代简洁交互流畅自然完全适配移动端和小程序使用场景。紫色主题营造了安静思考的投资氛围符合"让每笔投资都经得起思考"的产品愿景。

229
index.html Normal file
View File

@@ -0,0 +1,229 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>思投录 - 投资决策与复盘工具</title>
<link rel="stylesheet" href="styles.css">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
</head>
<body>
<!-- 主容器 -->
<div class="app-container">
<!-- 顶部导航栏 -->
<header class="header">
<div class="header-content">
<h1 class="app-title">思投录</h1>
<p class="app-subtitle">让每笔投资都经得起思考</p>
</div>
</header>
<!-- 主要内容区域 -->
<main class="main-content">
<!-- 持仓概览卡片 -->
<section class="overview-card">
<div class="card-header">
<h2>持仓概览</h2>
<span class="total-value">¥128,450.00</span>
</div>
<div class="portfolio-summary">
<div class="summary-item">
<span class="label">今日收益</span>
<span class="value positive">+¥1,250.00</span>
</div>
<div class="summary-item">
<span class="label">总收益率</span>
<span class="value positive">+12.5%</span>
</div>
</div>
</section>
<!-- 底部导航栏 -->
<nav class="bottom-nav">
<div class="nav-item active" data-tab="portfolio">
<div class="nav-icon">📊</div>
<span>持仓</span>
</div>
<div class="nav-item" data-tab="plans">
<div class="nav-icon">📋</div>
<span>计划</span>
</div>
<div class="nav-item" data-tab="records">
<div class="nav-icon">📝</div>
<span>记录</span>
</div>
<div class="nav-item" data-tab="tools">
<div class="nav-icon">🛠️</div>
<span>我的</span>
</div>
</nav>
</main>
<!-- 持仓页面 -->
<div class="page" id="portfolio-page">
<div class="page-header">
<h2>我的持仓</h2>
<button class="add-btn">+ 添加</button>
</div>
<div class="holdings-list">
<div class="holding-item">
<div class="stock-info">
<div class="stock-name">贵州茅台</div>
<div class="stock-code">600519</div>
</div>
<div class="holding-details">
<div class="shares">100股</div>
<div class="current-price">¥1,850.00</div>
<div class="profit positive">+¥2,500.00 (+15.6%)</div>
</div>
</div>
<div class="holding-item">
<div class="stock-info">
<div class="stock-name">腾讯控股</div>
<div class="stock-code">00700</div>
</div>
<div class="holding-details">
<div class="shares">200股</div>
<div class="current-price">¥320.00</div>
<div class="profit negative">-¥800.00 (-1.2%)</div>
</div>
</div>
</div>
</div>
<!-- 交易计划页面 -->
<div class="page hidden" id="plans-page">
<div class="page-header">
<h2>交易计划</h2>
<button class="add-btn">+ 新建</button>
</div>
<div class="plans-list">
<div class="plan-item">
<div class="plan-header">
<div class="stock-info">
<div class="stock-name">招商银行</div>
<div class="stock-code">600036</div>
</div>
<div class="plan-status pending">进行中</div>
</div>
<div class="plan-details">
<div class="plan-row">
<span class="label">目标价格:</span>
<span class="value">¥45.00</span>
</div>
<div class="plan-row">
<span class="label">计划金额:</span>
<span class="value">¥10,000</span>
</div>
<div class="plan-row">
<span class="label">截止时间:</span>
<span class="value">2024-03-15</span>
</div>
</div>
<div class="plan-progress">
<div class="progress-bar">
<div class="progress-fill" style="width: 60%"></div>
</div>
<span class="progress-text">已完成 60%</span>
</div>
</div>
</div>
</div>
<!-- 交易记录页面 -->
<div class="page hidden" id="records-page">
<div class="page-header">
<h2>交易记录</h2>
<button class="add-btn">+ 记录</button>
</div>
<div class="timeline">
<div class="timeline-item">
<div class="timeline-date">2024-01-15</div>
<div class="timeline-content">
<div class="transaction-card">
<div class="transaction-header">
<div class="stock-info">
<div class="stock-name">贵州茅台</div>
<div class="stock-code">600519</div>
</div>
<div class="transaction-type buy">买入</div>
</div>
<div class="transaction-details">
<div class="detail-row">
<span>数量:</span>
<span>100股</span>
</div>
<div class="detail-row">
<span>价格:</span>
<span>¥1,600.00</span>
</div>
</div>
<div class="transaction-thoughts">
<h4>交易思考:</h4>
<p>基于茅台品牌价值和长期增长潜力,认为当前价格具有投资价值。白酒行业龙头地位稳固,现金流优秀。</p>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 我的页面 -->
<div class="page hidden" id="tools-page">
<div class="page-header">
<h2>我的工具</h2>
</div>
<div class="tools-grid">
<div class="tool-card" data-tool="checklist">
<div class="tool-icon"></div>
<div class="tool-name">投资检查清单</div>
<div class="tool-desc">买入卖出检查项</div>
</div>
<div class="tool-card" data-tool="calculator">
<div class="tool-icon">🧮</div>
<div class="tool-name">复利计算器</div>
<div class="tool-desc">计算未来收益</div>
</div>
<div class="tool-card" data-tool="valuation">
<div class="tool-icon">📈</div>
<div class="tool-name">估值工具</div>
<div class="tool-desc">企业价值评估</div>
</div>
<div class="tool-card" data-tool="freedom">
<div class="tool-icon">🎯</div>
<div class="tool-name">自由目标</div>
<div class="tool-desc">财务自由规划</div>
</div>
</div>
</div>
</div>
<!-- 模态框 -->
<div class="modal" id="modal">
<div class="modal-content">
<div class="modal-header">
<h3 id="modal-title">标题</h3>
<button class="close-btn" id="close-modal">&times;</button>
</div>
<div class="modal-body" id="modal-body">
<!-- 动态内容 -->
</div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>

851
script.js Normal file
View File

@@ -0,0 +1,851 @@
// 思投录 - 投资决策与复盘工具
class VestMindApp {
constructor() {
this.currentTab = 'portfolio';
this.modal = document.getElementById('modal');
this.modalTitle = document.getElementById('modal-title');
this.modalBody = document.getElementById('modal-body');
this.closeModalBtn = document.getElementById('close-modal');
this.init();
}
init() {
this.bindEvents();
this.showPage('portfolio');
this.loadData();
}
bindEvents() {
// 底部导航栏事件
document.querySelectorAll('.nav-item').forEach(item => {
item.addEventListener('click', (e) => {
const tab = e.currentTarget.dataset.tab;
this.switchTab(tab);
});
});
// 模态框关闭事件
this.closeModalBtn.addEventListener('click', () => {
this.hideModal();
});
this.modal.addEventListener('click', (e) => {
if (e.target === this.modal) {
this.hideModal();
}
});
// 工具卡片点击事件
document.querySelectorAll('.tool-card').forEach(card => {
card.addEventListener('click', (e) => {
const tool = e.currentTarget.dataset.tool;
this.openTool(tool);
});
});
// 添加按钮事件
document.querySelectorAll('.add-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
const page = e.currentTarget.closest('.page').id;
this.showAddModal(page);
});
});
}
switchTab(tab) {
// 更新导航栏状态
document.querySelectorAll('.nav-item').forEach(item => {
item.classList.remove('active');
});
document.querySelector(`[data-tab="${tab}"]`).classList.add('active');
// 显示对应页面
this.showPage(tab);
this.currentTab = tab;
}
showPage(pageId) {
// 隐藏所有页面
document.querySelectorAll('.page').forEach(page => {
page.classList.add('hidden');
});
// 显示目标页面
const targetPage = document.getElementById(`${pageId}-page`);
if (targetPage) {
targetPage.classList.remove('hidden');
}
}
loadData() {
// 模拟数据加载
this.loadPortfolioData();
this.loadPlansData();
this.loadRecordsData();
}
loadPortfolioData() {
// 模拟持仓数据
const holdings = [
{
name: '贵州茅台',
code: '600519',
shares: 100,
currentPrice: 1850.00,
profit: 2500.00,
profitRate: 15.6
},
{
name: '腾讯控股',
code: '00700',
shares: 200,
currentPrice: 320.00,
profit: -800.00,
profitRate: -1.2
},
{
name: '招商银行',
code: '600036',
shares: 500,
currentPrice: 42.50,
profit: 1250.00,
profitRate: 6.2
}
];
this.renderHoldings(holdings);
}
renderHoldings(holdings) {
const container = document.querySelector('.holdings-list');
if (!container) return;
container.innerHTML = holdings.map(holding => `
<div class="holding-item">
<div class="stock-info">
<div class="stock-name">${holding.name}</div>
<div class="stock-code">${holding.code}</div>
</div>
<div class="holding-details">
<div class="shares">${holding.shares}股</div>
<div class="current-price">¥${holding.currentPrice.toFixed(2)}</div>
<div class="profit ${holding.profit >= 0 ? 'positive' : 'negative'}">
${holding.profit >= 0 ? '+' : ''}¥${holding.profit.toFixed(2)} (${holding.profitRate >= 0 ? '+' : ''}${holding.profitRate}%)
</div>
</div>
</div>
`).join('');
}
loadPlansData() {
// 模拟交易计划数据
const plans = [
{
name: '招商银行',
code: '600036',
targetPrice: 45.00,
amount: 10000,
deadline: '2024-03-15',
progress: 60,
status: 'pending'
},
{
name: '中国平安',
code: '601318',
targetPrice: 55.00,
amount: 15000,
deadline: '2024-04-20',
progress: 30,
status: 'pending'
}
];
this.renderPlans(plans);
}
renderPlans(plans) {
const container = document.querySelector('.plans-list');
if (!container) return;
container.innerHTML = plans.map(plan => `
<div class="plan-item">
<div class="plan-header">
<div class="stock-info">
<div class="stock-name">${plan.name}</div>
<div class="stock-code">${plan.code}</div>
</div>
<div class="plan-status ${plan.status}">进行中</div>
</div>
<div class="plan-details">
<div class="plan-row">
<span class="label">目标价格:</span>
<span class="value">¥${plan.targetPrice.toFixed(2)}</span>
</div>
<div class="plan-row">
<span class="label">计划金额:</span>
<span class="value">¥${plan.amount.toLocaleString()}</span>
</div>
<div class="plan-row">
<span class="label">截止时间:</span>
<span class="value">${plan.deadline}</span>
</div>
</div>
<div class="plan-progress">
<div class="progress-bar">
<div class="progress-fill" style="width: ${plan.progress}%"></div>
</div>
<span class="progress-text">已完成 ${plan.progress}%</span>
</div>
</div>
`).join('');
}
loadRecordsData() {
// 模拟交易记录数据
const records = [
{
date: '2024-01-15',
type: 'buy',
name: '贵州茅台',
code: '600519',
shares: 100,
price: 1600.00,
thoughts: '基于茅台品牌价值和长期增长潜力,认为当前价格具有投资价值。白酒行业龙头地位稳固,现金流优秀。'
},
{
date: '2024-01-10',
type: 'sell',
name: '比亚迪',
code: '002594',
shares: 200,
price: 280.00,
thoughts: '新能源汽车行业竞争加剧,估值偏高,选择获利了结。'
}
];
this.renderRecords(records);
}
renderRecords(records) {
const container = document.querySelector('.timeline');
if (!container) return;
container.innerHTML = records.map(record => `
<div class="timeline-item">
<div class="timeline-date">${record.date}</div>
<div class="timeline-content">
<div class="transaction-card">
<div class="transaction-header">
<div class="stock-info">
<div class="stock-name">${record.name}</div>
<div class="stock-code">${record.code}</div>
</div>
<div class="transaction-type ${record.type}">${record.type === 'buy' ? '买入' : '卖出'}</div>
</div>
<div class="transaction-details">
<div class="detail-row">
<span>数量:</span>
<span>${record.shares}股</span>
</div>
<div class="detail-row">
<span>价格:</span>
<span>¥${record.price.toFixed(2)}</span>
</div>
</div>
<div class="transaction-thoughts">
<h4>交易思考:</h4>
<p>${record.thoughts}</p>
</div>
</div>
</div>
</div>
`).join('');
}
openTool(tool) {
switch (tool) {
case 'checklist':
this.showChecklistModal();
break;
case 'calculator':
this.showCalculatorModal();
break;
case 'valuation':
this.showValuationModal();
break;
case 'freedom':
this.showFreedomModal();
break;
}
}
showChecklistModal() {
this.modalTitle.textContent = '投资检查清单';
this.modalBody.innerHTML = `
<div class="checklist-container">
<div class="checklist-section">
<h4>买入检查清单</h4>
<div class="checklist-items">
<label class="checklist-item">
<input type="checkbox">
<span>企业基本面是否优秀?</span>
</label>
<label class="checklist-item">
<input type="checkbox">
<span>估值是否合理?</span>
</label>
<label class="checklist-item">
<input type="checkbox">
<span>行业前景如何?</span>
</label>
<label class="checklist-item">
<input type="checkbox">
<span>管理层是否可信?</span>
</label>
<label class="checklist-item">
<input type="checkbox">
<span>现金流是否健康?</span>
</label>
</div>
</div>
<div class="checklist-section">
<h4>卖出检查清单</h4>
<div class="checklist-items">
<label class="checklist-item">
<input type="checkbox">
<span>基本面是否恶化?</span>
</label>
<label class="checklist-item">
<input type="checkbox">
<span>估值是否过高?</span>
</label>
<label class="checklist-item">
<input type="checkbox">
<span>是否有更好的投资机会?</span>
</label>
<label class="checklist-item">
<input type="checkbox">
<span>是否需要资金配置?</span>
</label>
</div>
</div>
</div>
`;
this.showModal();
}
showCalculatorModal() {
this.modalTitle.textContent = '复利计算器';
this.modalBody.innerHTML = `
<div class="calculator-container">
<div class="input-group">
<label>初始金额(元)</label>
<input type="number" id="initial-amount" placeholder="100000" value="100000">
</div>
<div class="input-group">
<label>每年投入(元)</label>
<input type="number" id="annual-investment" placeholder="50000" value="50000">
</div>
<div class="input-group">
<label>年复合增长率(%</label>
<input type="number" id="growth-rate" placeholder="10" value="10" step="0.1">
</div>
<div class="input-group">
<label>投资年限</label>
<input type="number" id="years" placeholder="10" value="10">
</div>
<button class="calculate-btn" onclick="app.calculateCompound()">计算</button>
<div class="result-container" id="calculator-result" style="display: none;">
<h4>计算结果</h4>
<div class="result-item">
<span>总投入:</span>
<span id="total-investment">¥0</span>
</div>
<div class="result-item">
<span>最终金额:</span>
<span id="final-amount">¥0</span>
</div>
<div class="result-item">
<span>总收益:</span>
<span id="total-profit">¥0</span>
</div>
</div>
</div>
`;
this.showModal();
}
calculateCompound() {
const initialAmount = parseFloat(document.getElementById('initial-amount').value) || 0;
const annualInvestment = parseFloat(document.getElementById('annual-investment').value) || 0;
const growthRate = parseFloat(document.getElementById('growth-rate').value) || 0;
const years = parseInt(document.getElementById('years').value) || 0;
const rate = growthRate / 100;
let totalInvestment = initialAmount + annualInvestment * years;
let finalAmount = initialAmount * Math.pow(1 + rate, years);
// 计算每年投入的复利
for (let i = 1; i <= years; i++) {
finalAmount += annualInvestment * Math.pow(1 + rate, years - i);
}
const totalProfit = finalAmount - totalInvestment;
document.getElementById('total-investment').textContent = `¥${totalInvestment.toLocaleString()}`;
document.getElementById('final-amount').textContent = `¥${finalAmount.toLocaleString()}`;
document.getElementById('total-profit').textContent = `¥${totalProfit.toLocaleString()}`;
document.getElementById('calculator-result').style.display = 'block';
}
showValuationModal() {
this.modalTitle.textContent = '估值工具';
this.modalBody.innerHTML = `
<div class="valuation-container">
<div class="valuation-tabs">
<button class="tab-btn active" data-method="tang">老唐估值法</button>
<button class="tab-btn" data-method="dcf">现金流折现</button>
</div>
<div class="valuation-content">
<div class="method-content" id="tang-method">
<div class="input-group">
<label>净利润(亿元)</label>
<input type="number" id="net-profit" placeholder="100" value="100">
</div>
<div class="input-group">
<label>无风险收益率(%</label>
<input type="number" id="risk-free-rate" placeholder="3" value="3" step="0.1">
</div>
<div class="input-group">
<label>合理PE倍数</label>
<input type="number" id="pe-ratio" placeholder="25" value="25">
</div>
<button class="calculate-btn" onclick="app.calculateTangValuation()">计算估值</button>
<div class="result-container" id="tang-result" style="display: none;">
<h4>估值结果</h4>
<div class="result-item">
<span>合理估值:</span>
<span id="tang-value">¥0亿</span>
</div>
</div>
</div>
</div>
</div>
`;
this.showModal();
}
calculateTangValuation() {
const netProfit = parseFloat(document.getElementById('net-profit').value) || 0;
const riskFreeRate = parseFloat(document.getElementById('risk-free-rate').value) || 0;
const peRatio = parseFloat(document.getElementById('pe-ratio').value) || 0;
const reasonablePE = 1 / (riskFreeRate / 100);
const valuation = netProfit * Math.min(peRatio, reasonablePE);
document.getElementById('tang-value').textContent = `¥${valuation.toFixed(2)}亿`;
document.getElementById('tang-result').style.display = 'block';
}
showFreedomModal() {
this.modalTitle.textContent = '自由目标';
this.modalBody.innerHTML = `
<div class="freedom-container">
<div class="input-group">
<label>目标资产(万元)</label>
<input type="number" id="target-assets" placeholder="1000" value="1000">
</div>
<div class="input-group">
<label>当前资产(万元)</label>
<input type="number" id="current-assets" placeholder="100" value="100">
</div>
<div class="input-group">
<label>年复合增长率(%</label>
<input type="number" id="freedom-growth-rate" placeholder="10" value="10" step="0.1">
</div>
<div class="input-group">
<label>每年投入(万元)</label>
<input type="number" id="freedom-annual-investment" placeholder="20" value="20">
</div>
<button class="calculate-btn" onclick="app.calculateFreedom()">计算达成时间</button>
<div class="result-container" id="freedom-result" style="display: none;">
<h4>自由目标分析</h4>
<div class="result-item">
<span>预计达成时间:</span>
<span id="freedom-years">0年</span>
</div>
<div class="result-item">
<span>届时年龄:</span>
<span id="freedom-age">0岁</span>
</div>
<div class="result-item">
<span>总投入:</span>
<span id="freedom-total-investment">¥0万</span>
</div>
</div>
</div>
`;
this.showModal();
}
calculateFreedom() {
const targetAssets = parseFloat(document.getElementById('target-assets').value) || 0;
const currentAssets = parseFloat(document.getElementById('current-assets').value) || 0;
const growthRate = parseFloat(document.getElementById('freedom-growth-rate').value) || 0;
const annualInvestment = parseFloat(document.getElementById('freedom-annual-investment').value) || 0;
const rate = growthRate / 100;
let years = 0;
let assets = currentAssets;
// 模拟逐年增长
while (assets < targetAssets && years < 50) {
assets = assets * (1 + rate) + annualInvestment;
years++;
}
const totalInvestment = currentAssets + annualInvestment * years;
const currentAge = 30; // 假设当前年龄
const targetAge = currentAge + years;
document.getElementById('freedom-years').textContent = `${years}`;
document.getElementById('freedom-age').textContent = `${targetAge}`;
document.getElementById('freedom-total-investment').textContent = `¥${totalInvestment.toFixed(2)}`;
document.getElementById('freedom-result').style.display = 'block';
}
showAddModal(page) {
switch (page) {
case 'portfolio-page':
this.showAddHoldingModal();
break;
case 'plans-page':
this.showAddPlanModal();
break;
case 'records-page':
this.showAddRecordModal();
break;
}
}
showAddHoldingModal() {
this.modalTitle.textContent = '添加持仓';
this.modalBody.innerHTML = `
<div class="form-container">
<div class="input-group">
<label>股票名称</label>
<input type="text" id="stock-name" placeholder="请输入股票名称">
</div>
<div class="input-group">
<label>股票代码</label>
<input type="text" id="stock-code" placeholder="请输入股票代码">
</div>
<div class="input-group">
<label>持股数量</label>
<input type="number" id="shares" placeholder="请输入持股数量">
</div>
<div class="input-group">
<label>成本价格</label>
<input type="number" id="cost-price" placeholder="请输入成本价格" step="0.01">
</div>
<button class="submit-btn" onclick="app.addHolding()">添加持仓</button>
</div>
`;
this.showModal();
}
showAddPlanModal() {
this.modalTitle.textContent = '新建交易计划';
this.modalBody.innerHTML = `
<div class="form-container">
<div class="input-group">
<label>股票名称</label>
<input type="text" id="plan-stock-name" placeholder="请输入股票名称">
</div>
<div class="input-group">
<label>股票代码</label>
<input type="text" id="plan-stock-code" placeholder="请输入股票代码">
</div>
<div class="input-group">
<label>目标价格</label>
<input type="number" id="target-price" placeholder="请输入目标价格" step="0.01">
</div>
<div class="input-group">
<label>计划金额</label>
<input type="number" id="plan-amount" placeholder="请输入计划金额">
</div>
<div class="input-group">
<label>截止时间</label>
<input type="date" id="deadline">
</div>
<button class="submit-btn" onclick="app.addPlan()">创建计划</button>
</div>
`;
this.showModal();
}
showAddRecordModal() {
this.modalTitle.textContent = '记录交易';
this.modalBody.innerHTML = `
<div class="form-container">
<div class="input-group">
<label>交易类型</label>
<select id="transaction-type">
<option value="buy">买入</option>
<option value="sell">卖出</option>
</select>
</div>
<div class="input-group">
<label>股票名称</label>
<input type="text" id="record-stock-name" placeholder="请输入股票名称">
</div>
<div class="input-group">
<label>股票代码</label>
<input type="text" id="record-stock-code" placeholder="请输入股票代码">
</div>
<div class="input-group">
<label>交易数量</label>
<input type="number" id="record-shares" placeholder="请输入交易数量">
</div>
<div class="input-group">
<label>交易价格</label>
<input type="number" id="record-price" placeholder="请输入交易价格" step="0.01">
</div>
<div class="input-group">
<label>交易思考</label>
<textarea id="transaction-thoughts" placeholder="请记录您的交易思考..." rows="4"></textarea>
</div>
<button class="submit-btn" onclick="app.addRecord()">记录交易</button>
</div>
`;
this.showModal();
}
addHolding() {
// 模拟添加持仓
this.showToast('持仓添加成功!');
this.hideModal();
}
addPlan() {
// 模拟添加计划
this.showToast('交易计划创建成功!');
this.hideModal();
}
addRecord() {
// 模拟添加记录
this.showToast('交易记录添加成功!');
this.hideModal();
}
showModal() {
this.modal.classList.add('show');
document.body.style.overflow = 'hidden';
}
hideModal() {
this.modal.classList.remove('show');
document.body.style.overflow = 'auto';
}
showToast(message) {
// 创建提示框
const toast = document.createElement('div');
toast.className = 'toast';
toast.textContent = message;
toast.style.cssText = `
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(0, 0, 0, 0.8);
color: white;
padding: 12px 24px;
border-radius: 8px;
font-size: 14px;
z-index: 10000;
opacity: 0;
transition: opacity 0.3s ease;
`;
document.body.appendChild(toast);
// 显示动画
setTimeout(() => {
toast.style.opacity = '1';
}, 100);
// 自动隐藏
setTimeout(() => {
toast.style.opacity = '0';
setTimeout(() => {
document.body.removeChild(toast);
}, 300);
}, 2000);
}
}
// 初始化应用
const app = new VestMindApp();
// 添加一些额外的样式
const additionalStyles = `
<style>
.checklist-container {
max-height: 400px;
overflow-y: auto;
}
.checklist-section {
margin-bottom: 20px;
}
.checklist-section h4 {
font-size: 16px;
font-weight: 600;
color: #374151;
margin-bottom: 12px;
padding-bottom: 8px;
border-bottom: 1px solid #e5e7eb;
}
.checklist-item {
display: flex;
align-items: center;
padding: 8px 0;
cursor: pointer;
font-size: 14px;
color: #374151;
}
.checklist-item input[type="checkbox"] {
margin-right: 12px;
width: 16px;
height: 16px;
accent-color: #8b5cf6;
}
.calculator-container,
.valuation-container,
.freedom-container,
.form-container {
max-height: 400px;
overflow-y: auto;
}
.input-group {
margin-bottom: 16px;
}
.input-group label {
display: block;
font-size: 14px;
font-weight: 500;
color: #374151;
margin-bottom: 6px;
}
.input-group input,
.input-group select,
.input-group textarea {
width: 100%;
padding: 10px 12px;
border: 1px solid #d1d5db;
border-radius: 8px;
font-size: 14px;
transition: border-color 0.3s ease;
}
.input-group input:focus,
.input-group select:focus,
.input-group textarea:focus {
outline: none;
border-color: #8b5cf6;
box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.1);
}
.calculate-btn,
.submit-btn {
width: 100%;
background: linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%);
color: white;
border: none;
border-radius: 8px;
padding: 12px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
margin-top: 8px;
}
.calculate-btn:hover,
.submit-btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(139, 92, 246, 0.3);
}
.result-container {
margin-top: 20px;
padding: 16px;
background: #f8f9fa;
border-radius: 8px;
border-left: 3px solid #8b5cf6;
}
.result-container h4 {
font-size: 16px;
font-weight: 600;
color: #374151;
margin-bottom: 12px;
}
.result-item {
display: flex;
justify-content: space-between;
margin-bottom: 8px;
font-size: 14px;
}
.result-item span:first-child {
color: #6b7280;
}
.result-item span:last-child {
font-weight: 600;
color: #374151;
}
.valuation-tabs {
display: flex;
margin-bottom: 20px;
border-bottom: 1px solid #e5e7eb;
}
.tab-btn {
flex: 1;
padding: 10px;
background: none;
border: none;
font-size: 14px;
font-weight: 500;
color: #6b7280;
cursor: pointer;
border-bottom: 2px solid transparent;
transition: all 0.3s ease;
}
.tab-btn.active {
color: #8b5cf6;
border-bottom-color: #8b5cf6;
}
.tab-btn:hover {
color: #8b5cf6;
}
</style>
`;
document.head.insertAdjacentHTML('beforeend', additionalStyles);

702
styles.css Normal file
View File

@@ -0,0 +1,702 @@
/* 基础样式重置 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
color: #333;
overflow-x: hidden;
}
/* 主容器 */
.app-container {
max-width: 414px;
margin: 0 auto;
min-height: 100vh;
background: #f8f9fa;
position: relative;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
}
/* 顶部导航栏 */
.header {
background: linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%);
color: white;
padding: 20px;
text-align: center;
position: relative;
overflow: hidden;
}
.header::before {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, transparent 70%);
animation: float 6s ease-in-out infinite;
}
@keyframes float {
0%, 100% { transform: translate(-50%, -50%) rotate(0deg); }
50% { transform: translate(-50%, -50%) rotate(180deg); }
}
.header-content {
position: relative;
z-index: 1;
}
.app-title {
font-size: 28px;
font-weight: 700;
margin-bottom: 4px;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.app-subtitle {
font-size: 14px;
opacity: 0.9;
font-weight: 300;
}
/* 主要内容区域 */
.main-content {
padding: 20px;
padding-bottom: 100px;
}
/* 概览卡片 */
.overview-card {
background: white;
border-radius: 16px;
padding: 20px;
margin-bottom: 20px;
box-shadow: 0 4px 20px rgba(139, 92, 246, 0.1);
border: 1px solid rgba(139, 92, 246, 0.1);
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
.card-header h2 {
font-size: 18px;
font-weight: 600;
color: #374151;
}
.total-value {
font-size: 24px;
font-weight: 700;
color: #8b5cf6;
}
.portfolio-summary {
display: flex;
gap: 20px;
}
.summary-item {
flex: 1;
text-align: center;
}
.summary-item .label {
display: block;
font-size: 12px;
color: #6b7280;
margin-bottom: 4px;
}
.summary-item .value {
display: block;
font-size: 16px;
font-weight: 600;
}
.summary-item .value.positive {
color: #10b981;
}
.summary-item .value.negative {
color: #ef4444;
}
/* 底部导航栏 */
.bottom-nav {
position: fixed;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 100%;
max-width: 414px;
background: white;
border-top: 1px solid #e5e7eb;
display: flex;
padding: 8px 0;
box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.1);
z-index: 100;
}
.nav-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
padding: 8px 4px;
cursor: pointer;
transition: all 0.3s ease;
border-radius: 8px;
margin: 0 4px;
}
.nav-item:hover {
background: rgba(139, 92, 246, 0.1);
}
.nav-item.active {
background: rgba(139, 92, 246, 0.1);
color: #8b5cf6;
}
.nav-icon {
font-size: 20px;
margin-bottom: 4px;
}
.nav-item span {
font-size: 12px;
font-weight: 500;
}
/* 页面样式 */
.page {
padding: 20px;
padding-bottom: 100px;
min-height: calc(100vh - 120px);
}
.page.hidden {
display: none;
}
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.page-header h2 {
font-size: 20px;
font-weight: 600;
color: #374151;
}
.add-btn {
background: linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%);
color: white;
border: none;
border-radius: 20px;
padding: 8px 16px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
}
.add-btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(139, 92, 246, 0.3);
}
/* 持仓列表 */
.holdings-list {
display: flex;
flex-direction: column;
gap: 12px;
}
.holding-item {
background: white;
border-radius: 12px;
padding: 16px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
border: 1px solid #f3f4f6;
transition: all 0.3s ease;
}
.holding-item:hover {
transform: translateY(-2px);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
}
.stock-info {
margin-bottom: 8px;
}
.stock-name {
font-size: 16px;
font-weight: 600;
color: #374151;
margin-bottom: 2px;
}
.stock-code {
font-size: 12px;
color: #6b7280;
}
.holding-details {
display: flex;
justify-content: space-between;
align-items: center;
}
.shares {
font-size: 14px;
color: #6b7280;
}
.current-price {
font-size: 16px;
font-weight: 600;
color: #374151;
}
.profit {
font-size: 14px;
font-weight: 500;
text-align: right;
}
.profit.positive {
color: #10b981;
}
.profit.negative {
color: #ef4444;
}
/* 计划列表 */
.plans-list {
display: flex;
flex-direction: column;
gap: 16px;
}
.plan-item {
background: white;
border-radius: 12px;
padding: 16px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
border: 1px solid #f3f4f6;
}
.plan-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.plan-status {
padding: 4px 12px;
border-radius: 12px;
font-size: 12px;
font-weight: 500;
}
.plan-status.pending {
background: rgba(251, 191, 36, 0.1);
color: #f59e0b;
}
.plan-status.completed {
background: rgba(16, 185, 129, 0.1);
color: #10b981;
}
.plan-details {
margin-bottom: 12px;
}
.plan-row {
display: flex;
justify-content: space-between;
margin-bottom: 4px;
font-size: 14px;
}
.plan-row .label {
color: #6b7280;
}
.plan-row .value {
font-weight: 500;
color: #374151;
}
.plan-progress {
display: flex;
align-items: center;
gap: 8px;
}
.progress-bar {
flex: 1;
height: 6px;
background: #e5e7eb;
border-radius: 3px;
overflow: hidden;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #8b5cf6 0%, #7c3aed 100%);
border-radius: 3px;
transition: width 0.3s ease;
}
.progress-text {
font-size: 12px;
color: #6b7280;
white-space: nowrap;
}
/* 时间线 */
.timeline {
position: relative;
padding-left: 20px;
}
.timeline::before {
content: '';
position: absolute;
left: 8px;
top: 0;
bottom: 0;
width: 2px;
background: linear-gradient(180deg, #8b5cf6 0%, #7c3aed 100%);
}
.timeline-item {
position: relative;
margin-bottom: 24px;
}
.timeline-item::before {
content: '';
position: absolute;
left: -16px;
top: 8px;
width: 12px;
height: 12px;
background: #8b5cf6;
border-radius: 50%;
border: 3px solid white;
box-shadow: 0 0 0 2px #8b5cf6;
}
.timeline-date {
font-size: 12px;
color: #6b7280;
margin-bottom: 8px;
font-weight: 500;
}
.timeline-content {
background: white;
border-radius: 12px;
padding: 16px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
border: 1px solid #f3f4f6;
}
.transaction-card {
width: 100%;
}
.transaction-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.transaction-type {
padding: 4px 12px;
border-radius: 12px;
font-size: 12px;
font-weight: 500;
}
.transaction-type.buy {
background: rgba(16, 185, 129, 0.1);
color: #10b981;
}
.transaction-type.sell {
background: rgba(239, 68, 68, 0.1);
color: #ef4444;
}
.transaction-details {
margin-bottom: 12px;
}
.detail-row {
display: flex;
justify-content: space-between;
margin-bottom: 4px;
font-size: 14px;
}
.transaction-thoughts {
background: #f8f9fa;
border-radius: 8px;
padding: 12px;
border-left: 3px solid #8b5cf6;
}
.transaction-thoughts h4 {
font-size: 14px;
font-weight: 600;
color: #374151;
margin-bottom: 8px;
}
.transaction-thoughts p {
font-size: 13px;
color: #6b7280;
line-height: 1.5;
}
/* 工具网格 */
.tools-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 16px;
}
.tool-card {
background: white;
border-radius: 12px;
padding: 20px;
text-align: center;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
border: 1px solid #f3f4f6;
cursor: pointer;
transition: all 0.3s ease;
}
.tool-card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 24px rgba(139, 92, 246, 0.15);
border-color: #8b5cf6;
}
.tool-icon {
font-size: 32px;
margin-bottom: 12px;
}
.tool-name {
font-size: 16px;
font-weight: 600;
color: #374151;
margin-bottom: 4px;
}
.tool-desc {
font-size: 12px;
color: #6b7280;
}
/* 模态框 */
.modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
opacity: 0;
visibility: hidden;
transition: all 0.3s ease;
}
.modal.show {
opacity: 1;
visibility: visible;
}
.modal-content {
background: white;
border-radius: 16px;
width: 90%;
max-width: 400px;
max-height: 80vh;
overflow-y: auto;
transform: scale(0.9);
transition: transform 0.3s ease;
}
.modal.show .modal-content {
transform: scale(1);
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px;
border-bottom: 1px solid #e5e7eb;
}
.modal-header h3 {
font-size: 18px;
font-weight: 600;
color: #374151;
}
.close-btn {
background: none;
border: none;
font-size: 24px;
color: #6b7280;
cursor: pointer;
padding: 0;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
transition: all 0.3s ease;
}
.close-btn:hover {
background: #f3f4f6;
color: #374151;
}
.modal-body {
padding: 20px;
}
/* 响应式设计 */
@media (max-width: 375px) {
.app-container {
max-width: 100%;
}
.main-content {
padding: 16px;
}
.page {
padding: 16px;
}
.tools-grid {
grid-template-columns: 1fr;
}
}
/* 小程序适配 */
@media (max-width: 320px) {
.app-title {
font-size: 24px;
}
.nav-item span {
font-size: 11px;
}
.nav-icon {
font-size: 18px;
}
}
/* 动画效果 */
@keyframes slideInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.holding-item,
.plan-item,
.timeline-content,
.tool-card {
animation: slideInUp 0.3s ease forwards;
}
/* 加载状态 */
.loading {
display: flex;
align-items: center;
justify-content: center;
padding: 40px;
color: #6b7280;
}
.loading::after {
content: '';
width: 20px;
height: 20px;
border: 2px solid #e5e7eb;
border-top: 2px solid #8b5cf6;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-left: 8px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* 空状态 */
.empty-state {
text-align: center;
padding: 40px 20px;
color: #6b7280;
}
.empty-state-icon {
font-size: 48px;
margin-bottom: 16px;
opacity: 0.5;
}
.empty-state-text {
font-size: 16px;
margin-bottom: 8px;
}
.empty-state-desc {
font-size: 14px;
opacity: 0.7;
}

430
产品需求文档.md Normal file
View File

@@ -0,0 +1,430 @@
# 思投录 (VestMind) 产品需求文档
## 1. 产品概述
### 1.1 产品定位
**产品名称**:思投录 (VestMind)
**产品定位**:投资决策与复盘工具
**产品愿景**:让每笔投资都经得起思考
**目标用户**:个人投资者、价值投资者、投资新手
### 1.2 产品价值
- 帮助用户建立系统化的投资决策流程
- 通过复盘机制提升投资决策质量
- 提供专业的投资工具和检查清单
- 培养理性投资思维,避免冲动交易
### 1.3 设计原则
- **紫色主题**:营造安静思考的投资氛围
- **移动端优先**适配APP和小程序
- **简洁易用**:降低使用门槛
- **数据驱动**:基于数据做决策
## 2. 功能架构
### 2.1 核心功能模块
```
思投录
├── 持仓管理
├── 交易计划
├── 交易记录/复盘
└── 我的工具
```
### 2.2 功能优先级
- **P0**:持仓管理、交易记录
- **P1**:交易计划、基础工具
- **P2**:高级工具、分享功能
## 3. 页面详细设计
### 3.1 首页/持仓页面
#### 3.1.1 页面概述
用户进入应用后的主页面,展示整体投资概况和持仓详情。
#### 3.1.2 页面布局
```
┌─────────────────────────┐
│ 思投录 (顶部导航) │
│ 让每笔投资都经得起思考 │
├─────────────────────────┤
│ 持仓概览卡片 │
│ ┌─────────────────────┐ │
│ │ 总资产: ¥128,450.00 │ │
│ │ 今日收益: +¥1,250 │ │
│ │ 总收益率: +12.5% │ │
│ └─────────────────────┘ │
├─────────────────────────┤
│ 持仓列表 │
│ ┌─────────────────────┐ │
│ │ 贵州茅台 600519 │ │
│ │ 100股 ¥1,850.00 │ │
│ │ +¥2,500.00 (+15.6%) │ │
│ └─────────────────────┘ │
│ ┌─────────────────────┐ │
│ │ 腾讯控股 00700 │ │
│ │ 200股 ¥320.00 │ │
│ │ -¥800.00 (-1.2%) │ │
│ └─────────────────────┘ │
├─────────────────────────┤
│ 底部导航栏 │
│ [持仓] [计划] [记录] [我的] │
└─────────────────────────┘
```
#### 3.1.3 功能需求
**持仓概览卡片**
- 显示总资产金额
- 显示今日收益(正负用颜色区分)
- 显示总收益率
- 支持点击查看详细统计
**持仓列表**
- 显示股票名称、代码
- 显示持股数量、当前价格
- 显示盈亏金额和收益率
- 支持点击查看单只股票详情
- 支持添加新持仓
**添加持仓功能**
- 股票名称输入
- 股票代码输入
- 持股数量输入
- 成本价格输入
- 仓位上限设置(预警功能)
#### 3.1.4 交互需求
- 下拉刷新更新数据
- 长按持仓项显示操作菜单
- 点击"+"按钮添加新持仓
- 滑动删除持仓(需确认)
### 3.2 交易计划页面
#### 3.2.1 页面概述
帮助用户制定和执行交易计划,实现"计划你的交易,交易你的计划"。
#### 3.2.2 页面布局
```
┌─────────────────────────┐
│ 交易计划 (顶部导航) │
│ [+ 新建] │
├─────────────────────────┤
│ 计划列表 │
│ ┌─────────────────────┐ │
│ │ 招商银行 600036 │ │
│ │ 状态: 进行中 │ │
│ │ 目标价格: ¥45.00 │ │
│ │ 计划金额: ¥10,000 │ │
│ │ 截止时间: 2024-03-15│ │
│ │ ████████░░ 60% │ │
│ └─────────────────────┘ │
│ ┌─────────────────────┐ │
│ │ 中国平安 601318 │ │
│ │ 状态: 已完成 │ │
│ │ 目标价格: ¥55.00 │ │
│ │ 计划金额: ¥15,000 │ │
│ │ 截止时间: 2024-04-20│ │
│ │ ██████████ 100% │ │
│ └─────────────────────┘ │
├─────────────────────────┤
│ 底部导航栏 │
│ [持仓] [计划] [记录] [我的] │
└─────────────────────────┘
```
#### 3.2.3 功能需求
**计划列表**
- 显示计划状态(进行中/已完成/已取消)
- 显示股票信息、目标价格
- 显示计划金额、截止时间
- 显示完成进度条
- 支持点击查看计划详情
**新建计划功能**
- 股票选择(名称、代码)
- 市场选择A股/港股/美股)
- 目标价格设置
- 截止时间设置
- 投资金额或股份数选择
- 分步买入设置默认3步
- 每步买入价格设置
**计划执行**
- 到达目标价格提醒
- 支持手动标记完成
- 支持从计划跳转到交易记录
- 支持修改计划参数
#### 3.2.4 交互需求
- 点击计划项查看详情
- 长按显示操作菜单(编辑/删除/完成)
- 滑动标记为完成
- 支持计划搜索和筛选
### 3.3 交易记录/复盘页面
#### 3.3.1 页面概述
记录每笔交易的详细信息,通过时间线展示交易历史和思考过程。
#### 3.3.2 页面布局
```
┌─────────────────────────┐
│ 交易记录 (顶部导航) │
│ [+ 记录] │
├─────────────────────────┤
│ 时间线 │
│ ┌─────────────────────┐ │
│ │ 2024-01-15 │ │
│ │ ┌─────────────────┐ │ │
│ │ │ 贵州茅台 600519 │ │ │
│ │ │ 买入 100股 │ │ │
│ │ │ 价格: ¥1,600.00 │ │ │
│ │ │ ┌─────────────┐ │ │ │
│ │ │ │ 交易思考: │ │ │ │
│ │ │ │ 基于茅台品牌 │ │ │ │
│ │ │ │ 价值和长期增 │ │ │ │
│ │ │ │ 长潜力... │ │ │ │
│ │ │ └─────────────┘ │ │ │
│ │ └─────────────────┘ │ │
│ └─────────────────────┘ │
│ ┌─────────────────────┐ │
│ │ 2024-01-10 │ │
│ │ ┌─────────────────┐ │ │
│ │ │ 比亚迪 002594 │ │ │
│ │ │ 卖出 200股 │ │ │
│ │ │ 价格: ¥280.00 │ │ │
│ │ │ ┌─────────────┐ │ │ │
│ │ │ │ 交易思考: │ │ │ │
│ │ │ │ 新能源汽车行 │ │ │ │
│ │ │ │ 业竞争加剧...│ │ │ │
│ │ │ └─────────────┘ │ │ │
│ │ └─────────────────┘ │ │
│ └─────────────────────┘ │
├─────────────────────────┤
│ 底部导航栏 │
│ [持仓] [计划] [记录] [我的] │
└─────────────────────────┘
```
#### 3.3.3 功能需求
**时间线展示**
- 按时间倒序显示交易记录
- 显示交易日期
- 显示交易类型(买入/卖出)
- 显示股票信息、数量、价格
- 显示交易思考内容
**记录交易功能**
- 交易类型选择(买入/卖出)
- 股票信息输入
- 交易数量输入
- 交易价格输入
- 交易思考记录(必填)
- 支持从计划跳转记录
**复盘功能**
- 定期弹出复盘提醒
- 支持为历史交易添加复盘
- 复盘内容记录
- 复盘时间记录
**分享功能**(可选)
- 支持分享单笔交易
- 支持分享交易时间线
- 隐私设置控制
#### 3.3.4 交互需求
- 点击交易记录查看详情
- 长按显示操作菜单(编辑/删除/复盘)
- 支持交易记录搜索
- 支持按股票筛选
- 支持按时间范围筛选
### 3.4 我的工具页面
#### 3.4.1 页面概述
提供各种投资工具和计算器,帮助用户做出更好的投资决策。
#### 3.4.2 页面布局
```
┌─────────────────────────┐
│ 我的工具 (顶部导航) │
├─────────────────────────┤
│ 工具网格 │
│ ┌─────────┐ ┌─────────┐ │
│ │ ✅ │ │ 🧮 │ │
│ │投资检查清单│ │复利计算器│ │
│ │买入卖出检查│ │计算未来收益│ │
│ └─────────┘ └─────────┘ │
│ ┌─────────┐ ┌─────────┐ │
│ │ 📈 │ │ 🎯 │ │
│ │ 估值工具 │ │ 自由目标 │ │
│ │企业价值评估│ │财务自由规划│ │
│ └─────────┘ └─────────┘ │
├─────────────────────────┤
│ 用户信息 │
│ ┌─────────────────────┐ │
│ │ 头像 | 用户名 │ │
│ │ 投资天数: 365天 │ │
│ │ 总交易次数: 25次 │ │
│ └─────────────────────┘ │
├─────────────────────────┤
│ 底部导航栏 │
│ [持仓] [计划] [记录] [我的] │
└─────────────────────────┘
```
#### 3.4.3 功能需求
**投资检查清单**
- 买入检查清单
- 企业基本面是否优秀?
- 估值是否合理?
- 行业前景如何?
- 管理层是否可信?
- 现金流是否健康?
- 卖出检查清单
- 基本面是否恶化?
- 估值是否过高?
- 是否有更好的投资机会?
- 是否需要资金配置?
- 支持在创建交易计划时自动弹出
**复利计算器**
- 初始金额输入
- 每年投入金额输入
- 年复合增长率设置
- 投资年限设置
- 计算总投入、最终金额、总收益
- 生成收益曲线图
**估值工具**
- 老唐估值法
- 净利润输入
- 无风险收益率设置
- 合理PE倍数计算
- 估值结果输出
- 现金流折现法
- 自由现金流输入
- 增长率设置
- 折现率设置
- 估值结果输出
**自由目标**
- 目标资产设置
- 当前资产输入
- 年复合增长率设置
- 每年投入金额设置
- 计算达成时间
- 与持仓数据联动
#### 3.4.4 交互需求
- 点击工具卡片打开对应工具
- 工具界面支持数据输入和计算
- 支持结果保存和分享
- 支持历史记录查看
## 4. 技术需求
### 4.1 平台支持
- **移动端APP**iOS、Android
- **小程序**:微信小程序
- **响应式设计**:适配不同屏幕尺寸
### 4.2 数据存储
- 本地存储:用户数据、设置
- 云端同步:多设备数据同步
- 数据备份:定期备份重要数据
### 4.3 性能要求
- 页面加载时间 < 2秒
- 操作响应时间 < 500ms
- 支持离线使用基础功能
## 5. 用户体验需求
### 5.1 易用性
- 界面简洁直观
- 操作流程简单
- 新手引导完善
- 错误提示友好
### 5.2 可访问性
- 支持字体大小调节
- 支持颜色对比度调节
- 支持语音输入
- 支持键盘导航
### 5.3 个性化
- 主题颜色自定义
- 功能模块自定义
- 提醒设置个性化
- 数据展示个性化
## 6. 安全需求
### 6.1 数据安全
- 本地数据加密存储
- 网络传输加密
- 用户隐私保护
- 数据访问权限控制
### 6.2 功能安全
- 重要操作二次确认
- 数据删除保护
- 异常操作监控
- 安全日志记录
## 7. 运营需求
### 7.1 数据统计
- 用户行为分析
- 功能使用统计
- 性能监控
- 错误日志收集
### 7.2 用户反馈
- 意见反馈入口
- 问题报告功能
- 用户满意度调查
- 功能建议收集
## 8. 开发计划
### 8.1 版本规划
- **V1.0**基础功能持仓记录
- **V1.1**交易计划功能
- **V1.2**工具集成
- **V2.0**高级功能和优化
### 8.2 里程碑
- 需求确认1周
- UI设计2周
- 开发实现8周
- 测试优化2周
- 上线发布1周
## 9. 成功指标
### 9.1 用户指标
- 日活跃用户数
- 用户留存率
- 功能使用率
- 用户满意度
### 9.2 产品指标
- 交易记录完成率
- 计划执行率
- 工具使用频率
- 数据准确性
---
*本文档版本V1.0*
*最后更新2024年1月*
*文档状态:待评审*