Trae Assistant commited on
Commit
76ee95f
·
1 Parent(s): ea89f80

Initial commit

Browse files
Dockerfile ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.9-slim
2
+
3
+ WORKDIR /app
4
+
5
+ COPY requirements.txt .
6
+ RUN pip install --no-cache-dir -r requirements.txt
7
+
8
+ COPY . .
9
+
10
+ # Create data directory and set permissions
11
+ RUN mkdir -p data && chmod 777 data
12
+
13
+ CMD ["gunicorn", "-b", "0.0.0.0:7860", "app:app"]
README.md CHANGED
@@ -1,10 +1,61 @@
1
  ---
2
- title: Startup Valuation Engine
3
- emoji: 💻
4
  colorFrom: green
5
- colorTo: green
6
  sdk: docker
7
  pinned: false
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: 创业估值与融资模拟引擎
3
+ emoji: 💰
4
  colorFrom: green
5
+ colorTo: indigo
6
  sdk: docker
7
  pinned: false
8
+ short_description: 创业项目早期估值、融资稀释模拟及投资意向书生成工具。
9
  ---
10
 
11
+ # 创业估值与融资模拟引擎 (Startup Valuation & Deal Structuring Engine)
12
+
13
+ 一个专为早期创业者设计的综合工具,帮助你科学估值、模拟融资稀释,并生成标准的投资意向书摘要。
14
+
15
+ ## 核心功能
16
+
17
+ 1. **多维度估值模型**
18
+ * **Berkus Method**: 针对早期无收入项目的经典估值法,通过评估5个关键风险维度(创意、原型、团队、战略关系、销售)来计算估值。
19
+ * **Scorecard Method (记分卡法)**: 通过对比行业平均估值,并根据团队、市场、产品等7个维度进行加权调整,得出更符合市场行情的估值。
20
+
21
+ 2. **融资与股权稀释模拟 (Cap Table Simulator)**
22
+ * 直观的股权结构表计算。
23
+ * 支持 Pre-money 估值、融资金额、期权池 (Option Pool) 设置。
24
+ * 自动计算融资后的 Post-money 估值、每股价格以及创始人、投资人、期权池的持股比例。
25
+ * 可视化饼图展示。
26
+
27
+ 3. **投资意向书生成器 (Term Sheet Lite)**
28
+ * 快速生成一份简易的投资条款清单 (Term Sheet)。
29
+ * 支持自定义清算优先权 (Liquidation Preference)、董事会席位分配等关键条款。
30
+ * 一键复制内容。
31
+
32
+ 4. **专业报告导出**
33
+ * 生成包含项目概况、估值分析、拟议融资结构的专业报告。
34
+ * 支持打印为 PDF 格式,方便发送给投资人或合伙人。
35
+
36
+ ## 技术栈
37
+
38
+ * **Backend**: Python Flask
39
+ * **Frontend**: Vue.js 3, Tailwind CSS, Chart.js
40
+ * **Deployment**: Docker (Compatible with Hugging Face Spaces)
41
+
42
+ ## 本地运行
43
+
44
+ ```bash
45
+ # 构建镜像
46
+ docker build -t valuation-engine .
47
+
48
+ # 运行容器
49
+ docker run -p 7860:7860 valuation-engine
50
+ ```
51
+
52
+ 或者直接运行 Python:
53
+
54
+ ```bash
55
+ pip install -r requirements.txt
56
+ python app.py
57
+ ```
58
+
59
+ ## 许可证
60
+
61
+ MIT License
app.py ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, render_template, request, jsonify, send_file
2
+ from werkzeug.exceptions import RequestEntityTooLarge
3
+ import os
4
+ import json
5
+ import datetime
6
+
7
+ app = Flask(__name__)
8
+ app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB max upload
9
+
10
+ # Data storage path
11
+ DATA_DIR = os.path.join(os.path.dirname(__file__), 'data')
12
+ os.makedirs(DATA_DIR, exist_ok=True)
13
+
14
+ @app.errorhandler(RequestEntityTooLarge)
15
+ def handle_file_too_large(e):
16
+ return jsonify({"success": False, "message": "File too large. Maximum size is 16MB."}), 413
17
+
18
+ @app.errorhandler(404)
19
+ def page_not_found(e):
20
+ return render_template('index.html'), 404
21
+
22
+ @app.route('/')
23
+ def index():
24
+ return render_template('index.html')
25
+
26
+ @app.route('/health')
27
+ def health_check():
28
+ return "OK", 200
29
+
30
+ @app.route('/api/save', methods=['POST'])
31
+ def save_project():
32
+ try:
33
+ data = request.json
34
+ if not data:
35
+ return jsonify({"success": False, "message": "No data provided"}), 400
36
+
37
+ project_name = data.get('projectName', 'untitled')
38
+ # Sanitize filename
39
+ safe_name = "".join([c for c in project_name if c.isalpha() or c.isdigit() or c in (' ', '-', '_')]).strip()
40
+ if not safe_name:
41
+ safe_name = 'untitled'
42
+
43
+ filename = f"{safe_name}.json"
44
+ filepath = os.path.join(DATA_DIR, filename)
45
+
46
+ with open(filepath, 'w', encoding='utf-8') as f:
47
+ json.dump(data, f, ensure_ascii=False, indent=2)
48
+
49
+ return jsonify({"success": True, "message": "项目已保存", "filename": filename})
50
+ except Exception as e:
51
+ return jsonify({"success": False, "message": str(e)}), 500
52
+
53
+ @app.route('/api/load', methods=['GET'])
54
+ def load_projects():
55
+ try:
56
+ if not os.path.exists(DATA_DIR):
57
+ os.makedirs(DATA_DIR, exist_ok=True)
58
+
59
+ files = [f for f in os.listdir(DATA_DIR) if f.endswith('.json')]
60
+ projects = []
61
+ for f in files:
62
+ filepath = os.path.join(DATA_DIR, f)
63
+ try:
64
+ with open(filepath, 'r', encoding='utf-8') as file:
65
+ data = json.load(file)
66
+ projects.append({
67
+ "filename": f,
68
+ "projectName": data.get('projectName', f.replace('.json', '')),
69
+ "updatedAt": datetime.datetime.fromtimestamp(os.path.getmtime(filepath)).strftime('%Y-%m-%d %H:%M:%S')
70
+ })
71
+ except:
72
+ continue
73
+ # Sort by update time desc
74
+ projects.sort(key=lambda x: x['updatedAt'], reverse=True)
75
+ return jsonify({"success": True, "projects": projects})
76
+ except Exception as e:
77
+ return jsonify({"success": False, "message": str(e)}), 500
78
+
79
+ @app.route('/api/load/<filename>', methods=['GET'])
80
+ def load_project_detail(filename):
81
+ try:
82
+ # Security check
83
+ if '..' in filename or not filename.endswith('.json'):
84
+ return jsonify({"success": False, "message": "Invalid filename"}), 400
85
+
86
+ filepath = os.path.join(DATA_DIR, filename)
87
+ if not os.path.exists(filepath):
88
+ return jsonify({"success": False, "message": "File not found"}), 404
89
+
90
+ with open(filepath, 'r', encoding='utf-8') as f:
91
+ data = json.load(f)
92
+ return jsonify({"success": True, "data": data})
93
+ except Exception as e:
94
+ return jsonify({"success": False, "message": str(e)}), 500
95
+
96
+ if __name__ == '__main__':
97
+ # Use 7860 for Hugging Face Spaces default
98
+ port = int(os.environ.get('PORT', 7860))
99
+ app.run(debug=True, host='0.0.0.0', port=port)
requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ flask==3.0.0
2
+ gunicorn==21.2.0
static/js/chart.js ADDED
The diff for this file is too large to render. See raw diff
 
static/js/html2canvas.min.js ADDED
The diff for this file is too large to render. See raw diff
 
static/js/tailwindcss.js ADDED
The diff for this file is too large to render. See raw diff
 
static/js/vue.global.js ADDED
The diff for this file is too large to render. See raw diff
 
templates/index.html ADDED
@@ -0,0 +1,753 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>创业估值与融资模拟引擎 | Startup Valuation Engine</title>
7
+ <script src="/static/js/tailwindcss.js"></script>
8
+ <script src="/static/js/vue.global.js"></script>
9
+ <script src="/static/js/chart.js"></script>
10
+ <script src="/static/js/html2canvas.min.js"></script>
11
+ <!-- Icons -->
12
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
13
+ <style>
14
+ @media print {
15
+ .no-print { display: none !important; }
16
+ .print-only { display: block !important; }
17
+ body { background: white; }
18
+ .card { box-shadow: none; border: 1px solid #ddd; }
19
+ }
20
+ .slider-thumb::-webkit-slider-thumb {
21
+ -webkit-appearance: none;
22
+ appearance: none;
23
+ width: 20px;
24
+ height: 20px;
25
+ background: #4F46E5;
26
+ cursor: pointer;
27
+ border-radius: 50%;
28
+ }
29
+ /* Custom scrollbar */
30
+ ::-webkit-scrollbar { width: 8px; }
31
+ ::-webkit-scrollbar-track { background: #f1f1f1; }
32
+ ::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 4px; }
33
+ ::-webkit-scrollbar-thumb:hover { background: #94a3b8; }
34
+
35
+ [v-cloak] { display: none; }
36
+ </style>
37
+ </head>
38
+ <body class="bg-slate-50 text-slate-800 font-sans">
39
+ <div id="app" v-cloak>
40
+ <!-- Header -->
41
+ <header class="bg-indigo-600 text-white shadow-lg no-print">
42
+ <div class="container mx-auto px-4 py-4 flex justify-between items-center">
43
+ <div class="flex items-center space-x-3">
44
+ <i class="fa-solid fa-chart-line text-2xl"></i>
45
+ <h1 class="text-xl font-bold">创业估值与融资模拟引擎</h1>
46
+ </div>
47
+ <div class="flex items-center space-x-4">
48
+ <button @click="loadDemoData" class="px-4 py-2 bg-indigo-500 hover:bg-indigo-400 rounded-md text-sm transition flex items-center">
49
+ <i class="fa-solid fa-magic mr-2"></i> 演示数据
50
+ </button>
51
+ <button @click="saveProject" class="px-4 py-2 bg-indigo-500 hover:bg-indigo-400 rounded-md text-sm transition flex items-center">
52
+ <i class="fa-solid fa-save mr-2"></i> 保存
53
+ </button>
54
+ <button @click="showLoadModal = true" class="px-4 py-2 bg-indigo-500 hover:bg-indigo-400 rounded-md text-sm transition flex items-center">
55
+ <i class="fa-solid fa-folder-open mr-2"></i> 打开
56
+ </button>
57
+ <button @click="printReport" class="px-4 py-2 bg-white text-indigo-600 hover:bg-gray-100 rounded-md text-sm font-semibold transition flex items-center">
58
+ <i class="fa-solid fa-print mr-2"></i> 打印报告
59
+ </button>
60
+ <button @click="exportImage" class="px-4 py-2 bg-green-500 hover:bg-green-400 text-white rounded-md text-sm font-semibold transition flex items-center">
61
+ <i class="fa-solid fa-image mr-2"></i> 导出图片
62
+ </button>
63
+ </div>
64
+ </div>
65
+ </header>
66
+
67
+ <!-- Main Content -->
68
+ <main class="container mx-auto px-4 py-8">
69
+
70
+ <!-- Project Info Card -->
71
+ <div class="bg-white rounded-xl shadow-sm p-6 mb-6 border border-slate-200">
72
+ <div class="grid grid-cols-1 md:grid-cols-3 gap-6">
73
+ <div>
74
+ <label class="block text-sm font-medium text-slate-500 mb-1">项目名称</label>
75
+ <input type="text" v-model="project.projectName" class="w-full px-4 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none transition" placeholder="例如:未来科技">
76
+ </div>
77
+ <div>
78
+ <label class="block text-sm font-medium text-slate-500 mb-1">所属行业</label>
79
+ <select v-model="project.industry" class="w-full px-4 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-indigo-500 outline-none bg-white">
80
+ <option value="SaaS">SaaS / 软件</option>
81
+ <option value="E-commerce">电商 / 消费品</option>
82
+ <option value="AI">人工智能 / 大数据</option>
83
+ <option value="Hardware">智能硬件 / IoT</option>
84
+ <option value="Biotech">生物科技 / 医疗</option>
85
+ <option value="Other">其他</option>
86
+ </select>
87
+ </div>
88
+ <div>
89
+ <label class="block text-sm font-medium text-slate-500 mb-1">当前阶段</label>
90
+ <select v-model="project.stage" class="w-full px-4 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-indigo-500 outline-none bg-white">
91
+ <option value="Idea">创意阶段 (Idea)</option>
92
+ <option value="MVP">原型阶段 (MVP)</option>
93
+ <option value="Early Revenue">早期收入 (Early Revenue)</option>
94
+ <option value="Growth">成长期 (Growth)</option>
95
+ </select>
96
+ </div>
97
+ </div>
98
+ </div>
99
+
100
+ <!-- Tabs Navigation -->
101
+ <div class="flex space-x-1 bg-slate-200 p-1 rounded-lg mb-6 no-print overflow-x-auto">
102
+ <button v-for="tab in tabs" :key="tab.id"
103
+ @click="currentTab = tab.id"
104
+ :class="['flex-1 py-2 px-4 rounded-md text-sm font-medium transition whitespace-nowrap', currentTab === tab.id ? 'bg-white text-indigo-600 shadow-sm' : 'text-slate-600 hover:text-indigo-600']">
105
+ <i :class="tab.icon + ' mr-2'"></i>${ tab.name }
106
+ </button>
107
+ </div>
108
+
109
+ <!-- Tab 1: Berkus Method -->
110
+ <div v-show="currentTab === 'berkus'" class="space-y-6">
111
+ <div class="bg-white rounded-xl shadow-sm p-6 border border-slate-200">
112
+ <div class="flex justify-between items-start mb-4">
113
+ <div>
114
+ <h2 class="text-xl font-bold text-slate-800">Berkus 估值法</h2>
115
+ <p class="text-sm text-slate-500 mt-1">适用于尚未产生收入的早期初创企业。通过评估5个关键风险维度的降低程度来计算估值。</p>
116
+ </div>
117
+ <div class="text-right">
118
+ <div class="text-sm text-slate-500">当前估值 (Pre-money)</div>
119
+ <div class="text-3xl font-bold text-indigo-600">$${ formatNumber(berkusValuation) }</div>
120
+ </div>
121
+ </div>
122
+
123
+ <div class="space-y-6 mt-8">
124
+ <div v-for="(factor, index) in project.berkusFactors" :key="index" class="bg-slate-50 p-4 rounded-lg">
125
+ <div class="flex justify-between mb-2">
126
+ <label class="font-semibold text-slate-700">${ factor.name }</label>
127
+ <span class="font-mono text-indigo-600">$${ formatNumber(factor.value) }</span>
128
+ </div>
129
+ <input type="range" v-model.number="factor.value" min="0" max="500000" step="50000" class="w-full h-2 bg-slate-300 rounded-lg appearance-none cursor-pointer">
130
+ <p class="text-xs text-slate-500 mt-2">${ factor.desc }</p>
131
+ </div>
132
+ </div>
133
+ </div>
134
+ </div>
135
+
136
+ <!-- Tab 2: Scorecard Method -->
137
+ <div v-show="currentTab === 'scorecard'" class="space-y-6">
138
+ <div class="bg-white rounded-xl shadow-sm p-6 border border-slate-200">
139
+ <div class="flex justify-between items-start mb-4">
140
+ <div>
141
+ <h2 class="text-xl font-bold text-slate-800">记分卡估值法 (Scorecard Method)</h2>
142
+ <p class="text-sm text-slate-500 mt-1">通过与同行业类似初创企业的平均估值进行对比,根据团队、市场等加权因子调整估值。</p>
143
+ </div>
144
+ <div class="text-right">
145
+ <div class="text-sm text-slate-500">当前估值 (Pre-money)</div>
146
+ <div class="text-3xl font-bold text-purple-600">$${ formatNumber(scorecardValuation) }</div>
147
+ </div>
148
+ </div>
149
+
150
+ <div class="mb-8">
151
+ <label class="block text-sm font-medium text-slate-700 mb-2">行业平均 Pre-money 估值</label>
152
+ <div class="flex items-center">
153
+ <span class="text-slate-500 mr-2">$</span>
154
+ <input type="number" v-model.number="project.industryAverageValuation" class="w-48 px-4 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-purple-500 outline-none">
155
+ </div>
156
+ </div>
157
+
158
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
159
+ <div v-for="(factor, index) in project.scorecardFactors" :key="index" class="bg-slate-50 p-4 rounded-lg border border-slate-100">
160
+ <div class="flex justify-between items-center mb-2">
161
+ <span class="font-semibold text-slate-700 text-sm">${ factor.name } (权重: ${ factor.weight }%)</span>
162
+ <span class="text-xs font-bold px-2 py-1 rounded"
163
+ :class="factor.percentage >= 100 ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700'">
164
+ ${ factor.percentage }%
165
+ </span>
166
+ </div>
167
+ <input type="range" v-model.number="factor.percentage" min="50" max="150" step="5" class="w-full h-2 bg-slate-300 rounded-lg appearance-none cursor-pointer">
168
+ <div class="flex justify-between text-xs text-slate-400 mt-1">
169
+ <span>弱 (50%)</span>
170
+ <span>平均 (100%)</span>
171
+ <span>强 (150%)</span>
172
+ </div>
173
+ </div>
174
+ </div>
175
+ </div>
176
+ </div>
177
+
178
+ <!-- Tab 3: Funding & Dilution -->
179
+ <div v-show="currentTab === 'dilution'" class="space-y-6">
180
+ <div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
181
+ <!-- Inputs -->
182
+ <div class="bg-white rounded-xl shadow-sm p-6 border border-slate-200 lg:col-span-1">
183
+ <h3 class="text-lg font-bold mb-4">融资参数设定</h3>
184
+
185
+ <div class="space-y-4">
186
+ <div>
187
+ <label class="block text-sm font-medium text-slate-700 mb-1">选定 Pre-money 估值</label>
188
+ <div class="flex space-x-2 mb-2">
189
+ <button @click="project.selectedPreMoney = berkusValuation" class="text-xs bg-indigo-100 text-indigo-700 px-2 py-1 rounded hover:bg-indigo-200">使用 Berkus</button>
190
+ <button @click="project.selectedPreMoney = scorecardValuation" class="text-xs bg-purple-100 text-purple-700 px-2 py-1 rounded hover:bg-purple-200">使用 Scorecard</button>
191
+ </div>
192
+ <div class="relative">
193
+ <span class="absolute left-3 top-2 text-slate-500">$</span>
194
+ <input type="number" v-model.number="project.selectedPreMoney" class="w-full pl-8 pr-4 py-2 border border-slate-300 rounded-lg">
195
+ </div>
196
+ </div>
197
+
198
+ <div>
199
+ <label class="block text-sm font-medium text-slate-700 mb-1">融资金额 (Investment)</label>
200
+ <div class="relative">
201
+ <span class="absolute left-3 top-2 text-slate-500">$</span>
202
+ <input type="number" v-model.number="project.investmentAmount" class="w-full pl-8 pr-4 py-2 border border-slate-300 rounded-lg">
203
+ </div>
204
+ </div>
205
+
206
+ <div>
207
+ <label class="block text-sm font-medium text-slate-700 mb-1">期权池 (Option Pool %)</label>
208
+ <div class="relative">
209
+ <input type="number" v-model.number="project.optionPool" min="0" max="30" class="w-full px-4 py-2 border border-slate-300 rounded-lg">
210
+ <span class="absolute right-3 top-2 text-slate-500">%</span>
211
+ </div>
212
+ <p class="text-xs text-slate-500 mt-1">通常为 Post-money 的 10-20%</p>
213
+ </div>
214
+
215
+ <div>
216
+ <label class="block text-sm font-medium text-slate-700 mb-1">当前总股数</label>
217
+ <input type="number" v-model.number="project.totalShares" class="w-full px-4 py-2 border border-slate-300 rounded-lg">
218
+ </div>
219
+ </div>
220
+ </div>
221
+
222
+ <!-- Results -->
223
+ <div class="bg-white rounded-xl shadow-sm p-6 border border-slate-200 lg:col-span-2">
224
+ <h3 class="text-lg font-bold mb-4">融资后股权结构 (Post-money Cap Table)</h3>
225
+
226
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-8">
227
+ <div>
228
+ <div class="mb-6">
229
+ <div class="text-sm text-slate-500">Post-money 估值</div>
230
+ <div class="text-2xl font-bold text-green-600">$${ formatNumber(postMoneyValuation) }</div>
231
+ </div>
232
+ <div class="mb-6">
233
+ <div class="text-sm text-slate-500">每股价格</div>
234
+ <div class="text-xl font-bold text-slate-800">$${ formatNumber(pricePerShare, 2) }</div>
235
+ </div>
236
+
237
+ <table class="w-full text-sm">
238
+ <thead>
239
+ <tr class="border-b">
240
+ <th class="text-left py-2">股东</th>
241
+ <th class="text-right py-2">股份 %</th>
242
+ <th class="text-right py-2">价值</th>
243
+ </tr>
244
+ </thead>
245
+ <tbody>
246
+ <tr class="text-slate-700">
247
+ <td class="py-2">创始人团队</td>
248
+ <td class="text-right font-mono">${ formatNumber(foundersPercent, 1) }%</td>
249
+ <td class="text-right font-mono">$${ formatNumber(foundersValue) }</td>
250
+ </tr>
251
+ <tr class="text-indigo-600 font-semibold">
252
+ <td class="py-2">新投资人</td>
253
+ <td class="text-right font-mono">${ formatNumber(investorsPercent, 1) }%</td>
254
+ <td class="text-right font-mono">$${ formatNumber(project.investmentAmount) }</td>
255
+ </tr>
256
+ <tr class="text-slate-500">
257
+ <td class="py-2">期权池 (ESOP)</td>
258
+ <td class="text-right font-mono">${ formatNumber(project.optionPool, 1) }%</td>
259
+ <td class="text-right font-mono">$${ formatNumber(optionPoolValue) }</td>
260
+ </tr>
261
+ </tbody>
262
+ </table>
263
+ </div>
264
+
265
+ <div class="flex items-center justify-center h-64">
266
+ <canvas id="dilutionChart"></canvas>
267
+ </div>
268
+ </div>
269
+ </div>
270
+ </div>
271
+ </div>
272
+
273
+ <!-- Tab 4: Term Sheet Generator -->
274
+ <div v-show="currentTab === 'termsheet'" class="space-y-6">
275
+ <div class="bg-white rounded-xl shadow-sm p-6 border border-slate-200">
276
+ <h2 class="text-xl font-bold mb-6">投资意向书生成器 (Lite)</h2>
277
+
278
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
279
+ <div>
280
+ <label class="block text-sm font-medium text-slate-700 mb-1">投资方名称</label>
281
+ <input type="text" v-model="project.investorName" class="w-full px-4 py-2 border border-slate-300 rounded-lg">
282
+ </div>
283
+ <div>
284
+ <label class="block text-sm font-medium text-slate-700 mb-1">交割日期</label>
285
+ <input type="date" v-model="project.closingDate" class="w-full px-4 py-2 border border-slate-300 rounded-lg">
286
+ </div>
287
+ <div>
288
+ <label class="block text-sm font-medium text-slate-700 mb-1">清算优先权 (Liquidation Preference)</label>
289
+ <select v-model="project.liquidationPref" class="w-full px-4 py-2 border border-slate-300 rounded-lg bg-white">
290
+ <option value="1x Non-Participating">1x 不参与分配 (标准)</option>
291
+ <option value="1x Participating">1x 参与分配 (对投资人有利)</option>
292
+ <option value="2x Non-Participating">2x 不参与分配 (苛刻)</option>
293
+ </select>
294
+ </div>
295
+ <div>
296
+ <label class="block text-sm font-medium text-slate-700 mb-1">董事会席位 (Board Seats)</label>
297
+ <div class="flex items-center space-x-2">
298
+ <input type="number" v-model="project.boardSeatsFounder" placeholder="创始人" class="w-1/3 px-4 py-2 border border-slate-300 rounded-lg">
299
+ <span>+</span>
300
+ <input type="number" v-model="project.boardSeatsInvestor" placeholder="投资人" class="w-1/3 px-4 py-2 border border-slate-300 rounded-lg">
301
+ <span>+</span>
302
+ <input type="number" v-model="project.boardSeatsIndependent" placeholder="独立" class="w-1/3 px-4 py-2 border border-slate-300 rounded-lg">
303
+ </div>
304
+ </div>
305
+ </div>
306
+
307
+ <div class="bg-slate-50 p-6 rounded-lg border border-slate-200 font-mono text-sm whitespace-pre-wrap leading-relaxed">${ termSheetText }</div>
308
+
309
+ <div class="mt-4 text-right">
310
+ <button @click="copyTermSheet" class="text-indigo-600 hover:text-indigo-800 font-medium">
311
+ <i class="fa-solid fa-copy mr-1"></i> 复制内容
312
+ </button>
313
+ </div>
314
+ </div>
315
+ </div>
316
+
317
+ <!-- Tab 5: Report Preview (For Print) -->
318
+ <div v-show="currentTab === 'report'" class="bg-white rounded-xl shadow-sm p-8 border border-slate-200 print-section">
319
+ <div class="text-center border-b pb-6 mb-6">
320
+ <h1 class="text-3xl font-bold text-slate-800">${ project.projectName }</h1>
321
+ <p class="text-slate-500 mt-2">估值与融资分析报告</p>
322
+ <p class="text-sm text-slate-400 mt-1">${ new Date().toLocaleDateString() }</p>
323
+ </div>
324
+
325
+ <div class="grid grid-cols-2 gap-8 mb-8">
326
+ <div>
327
+ <h3 class="font-bold text-slate-700 border-b mb-2">项目概况</h3>
328
+ <p><span class="text-slate-500 w-24 inline-block">行业:</span> ${ project.industry }</p>
329
+ <p><span class="text-slate-500 w-24 inline-block">阶段:</span> ${ project.stage }</p>
330
+ </div>
331
+ <div>
332
+ <h3 class="font-bold text-slate-700 border-b mb-2">估值综述</h3>
333
+ <p><span class="text-slate-500 w-32 inline-block">Berkus 估值:</span> $${ formatNumber(berkusValuation) }</p>
334
+ <p><span class="text-slate-500 w-32 inline-block">Scorecard 估值:</span> $${ formatNumber(scorecardValuation) }</p>
335
+ </div>
336
+ </div>
337
+
338
+ <div class="mb-8">
339
+ <h3 class="font-bold text-slate-700 border-b mb-4">拟议融资结构</h3>
340
+ <div class="grid grid-cols-3 gap-4 text-center">
341
+ <div class="bg-slate-50 p-4 rounded">
342
+ <div class="text-xs text-slate-500">Pre-money</div>
343
+ <div class="font-bold text-lg">$${ formatNumber(project.selectedPreMoney) }</div>
344
+ </div>
345
+ <div class="bg-slate-50 p-4 rounded">
346
+ <div class="text-xs text-slate-500">融资金额</div>
347
+ <div class="font-bold text-lg text-indigo-600">$${ formatNumber(project.investmentAmount) }</div>
348
+ </div>
349
+ <div class="bg-slate-50 p-4 rounded">
350
+ <div class="text-xs text-slate-500">Post-money</div>
351
+ <div class="font-bold text-lg text-green-600">$${ formatNumber(postMoneyValuation) }</div>
352
+ </div>
353
+ </div>
354
+ </div>
355
+
356
+ <div class="text-center text-xs text-slate-400 mt-12">
357
+ Generated by Startup Valuation Engine. This document is for simulation purposes only.
358
+ </div>
359
+ </div>
360
+
361
+ </main>
362
+
363
+ <!-- Load Project Modal -->
364
+ <div v-if="showLoadModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 no-print">
365
+ <div class="bg-white rounded-lg p-6 w-full max-w-md">
366
+ <h3 class="text-lg font-bold mb-4">加载项目</h3>
367
+ <div v-if="savedProjects.length === 0" class="text-center text-slate-500 py-4">
368
+ 暂无已保存的项目
369
+ </div>
370
+ <ul class="space-y-2 max-h-60 overflow-y-auto">
371
+ <li v-for="p in savedProjects" :key="p.filename"
372
+ class="flex justify-between items-center p-3 hover:bg-slate-50 rounded cursor-pointer border border-transparent hover:border-slate-200"
373
+ @click="loadProject(p.filename)">
374
+ <div>
375
+ <div class="font-medium">${ p.projectName }</div>
376
+ <div class="text-xs text-slate-400">${ p.updatedAt }</div>
377
+ </div>
378
+ <i class="fa-solid fa-chevron-right text-slate-300"></i>
379
+ </li>
380
+ </ul>
381
+ <div class="mt-4 text-right">
382
+ <button @click="showLoadModal = false" class="text-slate-500 hover:text-slate-700">关闭</button>
383
+ </div>
384
+ </div>
385
+ </div>
386
+
387
+ <!-- Toast Notification -->
388
+ <div v-if="toast.show" class="fixed bottom-4 right-4 bg-slate-800 text-white px-4 py-2 rounded shadow-lg transition-opacity duration-300 z-50">
389
+ ${ toast.message }
390
+ </div>
391
+ </div>
392
+
393
+ <script>
394
+ const { createApp, ref, computed, onMounted, watch } = Vue;
395
+
396
+ createApp({
397
+ delimiters: ['${', '}'],
398
+ setup() {
399
+ // State
400
+ const currentTab = ref('berkus');
401
+ const showLoadModal = ref(false);
402
+ const savedProjects = ref([]);
403
+ const toast = ref({ show: false, message: '' });
404
+ let chartInstance = null;
405
+
406
+ const tabs = [
407
+ { id: 'berkus', name: 'Berkus 估值', icon: 'fa-solid fa-gavel' },
408
+ { id: 'scorecard', name: '记分卡估值', icon: 'fa-solid fa-clipboard-check' },
409
+ { id: 'dilution', name: '融资稀释', icon: 'fa-solid fa-chart-pie' },
410
+ { id: 'termsheet', name: '投资意向书', icon: 'fa-solid fa-file-contract' },
411
+ { id: 'report', name: '报告预览', icon: 'fa-solid fa-file-pdf' }
412
+ ];
413
+
414
+ const project = ref({
415
+ projectName: '',
416
+ industry: 'SaaS',
417
+ stage: 'MVP',
418
+ // Berkus Data
419
+ berkusFactors: [
420
+ { name: '创意价值 (Sound Idea)', value: 0, desc: '产品创意是否存在基本价值和风险规避?(最高 $500k)' },
421
+ { name: '原型验证 (Prototype)', value: 0, desc: '是否有可工作的原型来降低技术风险?(最高 $500k)' },
422
+ { name: '管理团队 (Quality Team)', value: 0, desc: '是否有高质量的管理团队来降低执行风险?(最高 $500k)' },
423
+ { name: '战略关系 (Relationships)', value: 0, desc: '是否有战略合作伙伴/早期客户来降低市场风险?(最高 $500k)' },
424
+ { name: '产品发布/销售 (Rollout)', value: 0, desc: '产品是否已发布或产生销售来降低生产风险?(最高 $500k)' }
425
+ ],
426
+ // Scorecard Data
427
+ industryAverageValuation: 2000000,
428
+ scorecardFactors: [
429
+ { name: '团队实力', weight: 30, percentage: 100 },
430
+ { name: '市场机会大小', weight: 25, percentage: 100 },
431
+ { name: '产品/技术壁垒', weight: 15, percentage: 100 },
432
+ { name: '竞争环境', weight: 10, percentage: 100 },
433
+ { name: '市场推广/销售渠道', weight: 10, percentage: 100 },
434
+ { name: '额外资金需求', weight: 5, percentage: 100 },
435
+ { name: '其他因素', weight: 5, percentage: 100 }
436
+ ],
437
+ // Dilution Data
438
+ selectedPreMoney: 0,
439
+ investmentAmount: 500000,
440
+ optionPool: 15, // percent
441
+ totalShares: 1000000,
442
+ // Term Sheet Data
443
+ investorName: '',
444
+ closingDate: new Date().toISOString().split('T')[0],
445
+ liquidationPref: '1x Non-Participating',
446
+ boardSeatsFounder: 2,
447
+ boardSeatsInvestor: 1,
448
+ boardSeatsIndependent: 0
449
+ });
450
+
451
+ // Computed: Valuations
452
+ const berkusValuation = computed(() => {
453
+ return project.value.berkusFactors.reduce((sum, f) => sum + f.value, 0);
454
+ });
455
+
456
+ const scorecardValuation = computed(() => {
457
+ let multiplier = 0;
458
+ project.value.scorecardFactors.forEach(f => {
459
+ multiplier += (f.weight / 100) * (f.percentage / 100);
460
+ });
461
+ return Math.round(project.value.industryAverageValuation * multiplier);
462
+ });
463
+
464
+ // Auto-update selectedPreMoney if it's 0 or matches one of the calc methods
465
+ watch([berkusValuation, scorecardValuation], ([newB, newS], [oldB, oldS]) => {
466
+ if (project.value.selectedPreMoney === 0) {
467
+ project.value.selectedPreMoney = newB > 0 ? newB : newS;
468
+ }
469
+ });
470
+
471
+ // Computed: Dilution
472
+ const postMoneyValuation = computed(() => {
473
+ return project.value.selectedPreMoney + project.value.investmentAmount;
474
+ });
475
+
476
+ const optionPoolValue = computed(() => {
477
+ // Option pool is typically calculated on post-money basis
478
+ // Value = PostMoney * (OptionPool% / 100)
479
+ return Math.round(postMoneyValuation.value * (project.value.optionPool / 100));
480
+ });
481
+
482
+ // Effective Pre-Money (Pre-Money - Option Pool increase usually comes from pre-money side effectively)
483
+ // But for simplicity here:
484
+ // Founders own: (PreMoney / PostMoney) - OptionPool%?
485
+ // Let's use standard VC math:
486
+ // PostMoney = PreMoney + Investment
487
+ // Investor% = Investment / PostMoney
488
+ // OptionPool% is fixed at PostMoney
489
+ // Founders% = 100% - Investor% - OptionPool%
490
+
491
+ const investorsPercent = computed(() => {
492
+ if (postMoneyValuation.value === 0) return 0;
493
+ return (project.value.investmentAmount / postMoneyValuation.value) * 100;
494
+ });
495
+
496
+ const foundersPercent = computed(() => {
497
+ return 100 - investorsPercent.value - project.value.optionPool;
498
+ });
499
+
500
+ const foundersValue = computed(() => {
501
+ return Math.round(postMoneyValuation.value * (foundersPercent.value / 100));
502
+ });
503
+
504
+ const pricePerShare = computed(() => {
505
+ // If we assume totalShares (founders) represents the foundersPercent
506
+ // FoundersShares / Founders% = TotalPostShares
507
+ // Price = PostMoney / TotalPostShares
508
+ if (foundersPercent.value <= 0) return 0;
509
+ const totalPostShares = project.value.totalShares / (foundersPercent.value / 100);
510
+ return postMoneyValuation.value / totalPostShares;
511
+ });
512
+
513
+ // Computed: Term Sheet Text
514
+ const termSheetText = computed(() => {
515
+ return `INVESTMENT TERM SHEET (SUMMARY)
516
+
517
+ Company: ${project.value.projectName || '[Company Name]'}
518
+ Investor: ${project.value.investorName || '[Investor Name]'}
519
+ Date: ${project.value.closingDate}
520
+
521
+ 1. Valuation & Investment
522
+ - Pre-money Valuation: $${formatNumber(project.value.selectedPreMoney)}
523
+ - Investment Amount: $${formatNumber(project.value.investmentAmount)}
524
+ - Post-money Valuation: $${formatNumber(postMoneyValuation.value)}
525
+
526
+ 2. Capitalization
527
+ - Employee Option Pool: ${project.value.optionPool}% (Post-money basis)
528
+ - Investor Ownership: ${formatNumber(investorsPercent.value, 1)}%
529
+
530
+ 3. Liquidation Preference
531
+ - ${project.value.liquidationPref}
532
+ - In the event of a sale or liquidation, investors receive their preference amount before common shareholders.
533
+
534
+ 4. Board of Directors
535
+ - The Board shall consist of ${project.value.boardSeatsFounder + project.value.boardSeatsInvestor + project.value.boardSeatsIndependent} members:
536
+ - ${project.value.boardSeatsFounder} Founder representative(s)
537
+ - ${project.value.boardSeatsInvestor} Investor representative(s)
538
+ - ${project.value.boardSeatsIndependent} Independent member(s)
539
+
540
+ 5. Dividends
541
+ - Non-cumulative dividends will be paid on the Preferred Stock when and if declared by the Board.
542
+
543
+ [This is a non-binding summary for simulation purposes only.]`;
544
+ });
545
+
546
+ // Methods
547
+ const formatNumber = (num, decimals = 0) => {
548
+ if (!num && num !== 0) return '0';
549
+ return num.toLocaleString('en-US', { minimumFractionDigits: decimals, maximumFractionDigits: decimals });
550
+ };
551
+
552
+ const showToast = (msg) => {
553
+ toast.value.message = msg;
554
+ toast.value.show = true;
555
+ setTimeout(() => toast.value.show = false, 3000);
556
+ };
557
+
558
+ const saveProject = async () => {
559
+ try {
560
+ const res = await fetch('/api/save', {
561
+ method: 'POST',
562
+ headers: { 'Content-Type': 'application/json' },
563
+ body: JSON.stringify(project.value)
564
+ });
565
+ const data = await res.json();
566
+ if (data.success) {
567
+ showToast('保存成功');
568
+ loadProjectList();
569
+ } else {
570
+ showToast('保存失败: ' + data.message);
571
+ }
572
+ } catch (e) {
573
+ showToast('保存错误');
574
+ }
575
+ };
576
+
577
+ const loadProjectList = async () => {
578
+ try {
579
+ const res = await fetch('/api/load');
580
+ const data = await res.json();
581
+ if (data.success) {
582
+ savedProjects.value = data.projects;
583
+ }
584
+ } catch (e) {
585
+ console.error(e);
586
+ }
587
+ };
588
+
589
+ const loadProject = async (filename) => {
590
+ try {
591
+ const res = await fetch('/api/load/' + filename);
592
+ const data = await res.json();
593
+ if (data.success) {
594
+ project.value = data.data;
595
+ showLoadModal.value = false;
596
+ showToast('加载成功');
597
+ }
598
+ } catch (e) {
599
+ showToast('加载失败');
600
+ }
601
+ };
602
+
603
+ const copyTermSheet = () => {
604
+ navigator.clipboard.writeText(termSheetText.value);
605
+ showToast('已复制到剪贴板');
606
+ };
607
+
608
+ const printReport = () => {
609
+ currentTab.value = 'report';
610
+ setTimeout(() => {
611
+ window.print();
612
+ }, 500);
613
+ };
614
+
615
+ const exportImage = async () => {
616
+ currentTab.value = 'report';
617
+ // Wait for render
618
+ await new Promise(r => setTimeout(r, 800));
619
+
620
+ const element = document.querySelector('.print-section');
621
+ if (!element) return;
622
+
623
+ try {
624
+ showToast('正在生成图片...', 5000);
625
+ const canvas = await html2canvas(element, {
626
+ scale: 2,
627
+ backgroundColor: '#ffffff',
628
+ useCORS: true
629
+ });
630
+
631
+ const link = document.createElement('a');
632
+ link.download = `${project.value.projectName || 'project'}-valuation-report.png`;
633
+ link.href = canvas.toDataURL('image/png');
634
+ link.click();
635
+ showToast('图片导出成功');
636
+ } catch (e) {
637
+ console.error(e);
638
+ showToast('导出失败,请重试');
639
+ }
640
+ };
641
+
642
+ const loadDemoData = () => {
643
+ project.value = {
644
+ projectName: '演示科技 (DemoTech)',
645
+ industry: 'AI',
646
+ stage: 'MVP',
647
+ berkusFactors: [
648
+ { name: '创意价值 (Sound Idea)', value: 350000, desc: '产品创意是否存在基本价值和风险规避?(最高 $500k)' },
649
+ { name: '原型验证 (Prototype)', value: 400000, desc: '是否有可工作的原型来降低技术风险?(最高 $500k)' },
650
+ { name: '管理团队 (Quality Team)', value: 250000, desc: '是否有高质量的管理团队来降低执行风险?(最高 $500k)' },
651
+ { name: '战略关系 (Relationships)', value: 100000, desc: '是否有战略合作伙伴/早期客户来降低市场风险?(最高 $500k)' },
652
+ { name: '产品发布/销售 (Rollout)', value: 0, desc: '产品是否已发布或产生销售来降低生产风险?(最高 $500k)' }
653
+ ],
654
+ industryAverageValuation: 3000000,
655
+ scorecardFactors: [
656
+ { name: '团队实力', weight: 30, percentage: 120 },
657
+ { name: '市场机会大小', weight: 25, percentage: 110 },
658
+ { name: '产品/技术壁垒', weight: 15, percentage: 115 },
659
+ { name: '竞争环境', weight: 10, percentage: 90 },
660
+ { name: '市场推广/销售渠道', weight: 10, percentage: 80 },
661
+ { name: '额外资金需求', weight: 5, percentage: 100 },
662
+ { name: '其他因素', weight: 5, percentage: 100 }
663
+ ],
664
+ selectedPreMoney: 0,
665
+ investmentAmount: 800000,
666
+ optionPool: 15,
667
+ totalShares: 1000000,
668
+ investorName: '红杉资本 (Sequoia)',
669
+ closingDate: new Date().toISOString().split('T')[0],
670
+ liquidationPref: '1x Non-Participating',
671
+ boardSeatsFounder: 2,
672
+ boardSeatsInvestor: 1,
673
+ boardSeatsIndependent: 0
674
+ };
675
+ showToast('演示数据已加载');
676
+ };
677
+ const updateChart = () => {
678
+ if (!document.getElementById('dilutionChart')) return;
679
+
680
+ const ctx = document.getElementById('dilutionChart').getContext('2d');
681
+
682
+ if (chartInstance) {
683
+ chartInstance.destroy();
684
+ }
685
+
686
+ chartInstance = new Chart(ctx, {
687
+ type: 'doughnut',
688
+ data: {
689
+ labels: ['创始人团队', '新投资人', '期权池'],
690
+ datasets: [{
691
+ data: [foundersPercent.value, investorsPercent.value, project.value.optionPool],
692
+ backgroundColor: ['#94a3b8', '#4f46e5', '#cbd5e1'],
693
+ borderWidth: 0
694
+ }]
695
+ },
696
+ options: {
697
+ responsive: true,
698
+ maintainAspectRatio: false,
699
+ plugins: {
700
+ legend: { position: 'bottom' }
701
+ }
702
+ }
703
+ });
704
+ };
705
+
706
+ // Watch for chart updates
707
+ watch([foundersPercent, investorsPercent, () => project.value.optionPool, currentTab], () => {
708
+ if (currentTab.value === 'dilution') {
709
+ // Small delay to ensure DOM is ready
710
+ setTimeout(updateChart, 100);
711
+ }
712
+ });
713
+
714
+ onMounted(() => {
715
+ loadProjectList();
716
+ // Set default Berkus values to non-zero for demo
717
+ project.value.berkusFactors[0].value = 150000;
718
+ project.value.berkusFactors[1].value = 100000;
719
+
720
+ if (currentTab.value === 'dilution') {
721
+ setTimeout(updateChart, 100);
722
+ }
723
+ });
724
+
725
+ return {
726
+ currentTab,
727
+ tabs,
728
+ project,
729
+ berkusValuation,
730
+ scorecardValuation,
731
+ postMoneyValuation,
732
+ investorsPercent,
733
+ foundersPercent,
734
+ foundersValue,
735
+ optionPoolValue,
736
+ pricePerShare,
737
+ termSheetText,
738
+ formatNumber,
739
+ saveProject,
740
+ showLoadModal,
741
+ savedProjects,
742
+ loadProject,
743
+ copyTermSheet,
744
+ printReport,
745
+ toast,
746
+ loadDemoData,
747
+ exportImage
748
+ };
749
+ }
750
+ }).mount('#app');
751
+ </script>
752
+ </body>
753
+ </html>