NAC_Blockchain/nac-ai-valuation/valuation-ui/public/index.html

1373 lines
69 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>NAC AI 资产估值顾问 v2.0 | NewAssetChain</title>
<meta name="description" content="NAC NewAssetChain RWA专用公链AI估值顾问 — 20大类资产 × 60+司法辖区专业估值XTZH三重价值输出">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>💎</text></svg>">
<style>
:root {
--bg-primary: #0a0e1a;
--bg-secondary: #0f1628;
--bg-card: #141c2e;
--bg-input: #1a2340;
--accent-gold: #d4a843;
--accent-gold-light: #f0c060;
--accent-blue: #3b82f6;
--accent-cyan: #00bcd4;
--accent-green: #10b981;
--accent-purple: #8b5cf6;
--text-primary: #e8eaf0;
--text-secondary: #8892a4;
--text-muted: #4a5568;
--border: #1e2d4a;
--border-active: #3b82f6;
--shadow: 0 4px 24px rgba(0,0,0,0.4);
--radius: 12px;
}
* { margin:0; padding:0; box-sizing:border-box; }
body {
font-family: 'PingFang SC', 'Microsoft YaHei', -apple-system, sans-serif;
background: var(--bg-primary);
color: var(--text-primary);
height: 100vh;
display: flex;
flex-direction: column;
overflow: hidden;
}
/* ===== HEADER ===== */
.header {
background: var(--bg-secondary);
border-bottom: 1px solid var(--border);
padding: 0 20px;
height: 56px;
display: flex;
align-items: center;
justify-content: space-between;
flex-shrink: 0;
}
.header-left { display:flex; align-items:center; gap:12px; }
.logo {
width: 34px; height: 34px;
background: linear-gradient(135deg, #1e88e5, #00bcd4);
border-radius: 8px;
display: flex; align-items: center; justify-content: center;
font-size: 13px; font-weight: 900; color: white; letter-spacing: -1px;
}
.header-title { font-size: 15px; font-weight: 700; color: var(--text-primary); }
.header-subtitle { font-size: 10px; color: var(--text-secondary); margin-top: 1px; }
.version-badge {
background: rgba(0,188,212,0.12);
color: var(--accent-cyan);
border: 1px solid rgba(0,188,212,0.25);
padding: 2px 8px; border-radius: 10px; font-size: 10px; font-weight: 600;
}
.header-right { display:flex; align-items:center; gap:10px; }
.xtzh-price-badge {
background: rgba(212,168,67,0.08);
border: 1px solid rgba(212,168,67,0.25);
border-radius: 18px;
padding: 5px 12px;
display: flex; align-items: center; gap: 7px;
cursor: pointer; transition: background 0.2s;
}
.xtzh-price-badge:hover { background: rgba(212,168,67,0.15); }
.xtzh-price-badge .label { font-size: 10px; color: var(--text-secondary); }
.xtzh-price-badge .price { font-size: 13px; font-weight: 700; color: var(--accent-gold); }
.price-dot { width:6px; height:6px; border-radius:50%; background:var(--accent-green); animation: pulse 2s infinite; }
@keyframes pulse { 0%,100%{opacity:1} 50%{opacity:0.4} }
.lang-select {
background: var(--bg-card); border: 1px solid var(--border);
color: var(--text-primary); border-radius: 7px;
padding: 4px 8px; font-size: 11px; cursor: pointer; outline: none;
}
/* ===== MAIN LAYOUT ===== */
.main { display: flex; flex: 1; overflow: hidden; }
/* ===== SIDEBAR ===== */
.sidebar {
width: 240px;
background: var(--bg-secondary);
border-right: 1px solid var(--border);
display: flex; flex-direction: column;
overflow: hidden; flex-shrink: 0;
}
.sidebar-header {
padding: 12px;
border-bottom: 1px solid var(--border);
display: flex; align-items: center; gap: 8px;
}
.new-chat-btn {
flex: 1;
background: linear-gradient(135deg, #1e88e5, #00bcd4);
color: white; border: none; border-radius: 7px;
padding: 8px 12px; font-size: 12px; font-weight: 700;
cursor: pointer; transition: opacity 0.2s;
}
.new-chat-btn:hover { opacity: 0.85; }
.clear-btn {
background: var(--bg-card); border: 1px solid var(--border);
color: var(--text-secondary); border-radius: 7px;
padding: 8px 10px; font-size: 12px; cursor: pointer; transition: all 0.2s;
}
.clear-btn:hover { border-color: var(--accent-blue); color: var(--accent-blue); }
.sidebar-section { padding: 10px 12px 8px; }
.sidebar-section-title {
font-size: 10px; color: var(--text-muted);
text-transform: uppercase; letter-spacing: 1px; margin-bottom: 7px;
}
/* 辖区下拉 */
.juris-select {
width: 100%; background: var(--bg-card); border: 1px solid var(--border);
color: var(--text-primary); border-radius: 7px;
padding: 7px 10px; font-size: 12px; cursor: pointer; outline: none;
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 10 10'%3E%3Cpath fill='%238892a4' d='M5 7L0 2h10z'/%3E%3C/svg%3E");
background-repeat: no-repeat; background-position: right 8px center; padding-right: 24px;
transition: border-color 0.2s;
}
.juris-select:focus { border-color: var(--accent-cyan); }
.juris-select option { background: #141c2e; }
/* 20大类资产 */
.asset-cat-select {
width: 100%; background: var(--bg-card); border: 1px solid var(--border);
color: var(--text-primary); border-radius: 7px;
padding: 7px 10px; font-size: 12px; cursor: pointer; outline: none;
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 10 10'%3E%3Cpath fill='%238892a4' d='M5 7L0 2h10z'/%3E%3C/svg%3E");
background-repeat: no-repeat; background-position: right 8px center; padding-right: 24px;
transition: border-color 0.2s; margin-bottom: 6px;
}
.asset-cat-select:focus { border-color: var(--accent-blue); }
.asset-cat-select option { background: #141c2e; }
/* 子类标签 */
.subcat-grid { display: flex; flex-wrap: wrap; gap: 4px; max-height: 120px; overflow-y: auto; }
.subcat-tag {
background: var(--bg-card); border: 1px solid var(--border);
color: var(--text-secondary); border-radius: 5px;
padding: 3px 7px; font-size: 10px; cursor: pointer; transition: all 0.2s;
}
.subcat-tag:hover { border-color: var(--accent-blue); color: var(--accent-blue); }
.subcat-tag.active { background: rgba(59,130,246,0.12); border-color: var(--accent-blue); color: var(--accent-blue); }
/* 历史对话 */
.history-list { flex: 1; overflow-y: auto; padding: 6px; }
.history-item {
padding: 7px 9px; border-radius: 7px; cursor: pointer;
font-size: 11px; color: var(--text-secondary);
transition: all 0.2s; white-space: nowrap;
overflow: hidden; text-overflow: ellipsis; margin-bottom: 2px;
}
.history-item:hover { background: var(--bg-card); color: var(--text-primary); }
.history-item.active { background: rgba(212,168,67,0.08); color: var(--accent-gold); }
/* ===== CHAT AREA ===== */
.chat-area { flex: 1; display: flex; flex-direction: column; overflow: hidden; }
/* 顶部数据面板 */
.data-panel {
background: linear-gradient(135deg, rgba(30,136,229,0.06), rgba(0,188,212,0.04));
border-bottom: 1px solid rgba(30,136,229,0.15);
padding: 8px 20px;
display: flex; align-items: center; gap: 20px;
flex-shrink: 0; overflow-x: auto;
}
.dp-stat { display: flex; flex-direction: column; flex-shrink: 0; }
.dp-label { font-size: 9px; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.5px; }
.dp-value { font-size: 13px; font-weight: 700; color: var(--accent-gold); }
.dp-value.cyan { color: var(--accent-cyan); }
.dp-value.green { color: var(--accent-green); }
.dp-value.blue { color: var(--accent-blue); }
.dp-sub { font-size: 9px; color: var(--text-muted); }
.dp-divider { width: 1px; height: 28px; background: var(--border); flex-shrink: 0; }
/* 消息列表 */
.messages {
flex: 1; overflow-y: auto; padding: 16px 20px;
display: flex; flex-direction: column; gap: 14px;
}
.messages::-webkit-scrollbar { width: 4px; }
.messages::-webkit-scrollbar-track { background: transparent; }
.messages::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
/* 欢迎界面 */
.welcome {
display: flex; flex-direction: column; align-items: center;
justify-content: flex-start; flex: 1; padding: 24px 20px; text-align: center;
overflow-y: auto;
}
.welcome-icon {
width: 64px; height: 64px;
background: linear-gradient(135deg, #1e88e5, #00bcd4);
border-radius: 18px;
display: flex; align-items: center; justify-content: center;
font-size: 30px; margin-bottom: 14px;
box-shadow: 0 8px 28px rgba(30,136,229,0.3);
}
.welcome h1 { font-size: 20px; font-weight: 800; margin-bottom: 6px; background: linear-gradient(135deg, #1e88e5, #00bcd4); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
.welcome-desc { font-size: 13px; color: var(--text-secondary); max-width: 520px; line-height: 1.6; margin-bottom: 6px; }
.welcome-stats { display: flex; gap: 24px; margin: 10px 0 16px; }
.ws-item { text-align: center; }
.ws-num { font-size: 20px; font-weight: 800; color: var(--accent-cyan); }
.ws-label { font-size: 10px; color: var(--text-muted); margin-top: 1px; }
/* 快捷卡片 */
.quick-cards {
display: grid; grid-template-columns: repeat(3, 1fr);
gap: 8px; max-width: 720px; width: 100%;
}
.quick-card {
background: var(--bg-card); border: 1px solid var(--border);
border-radius: var(--radius); padding: 12px 14px;
cursor: pointer; transition: all 0.2s; text-align: left;
}
.quick-card:hover {
border-color: var(--accent-cyan);
background: rgba(0,188,212,0.05);
transform: translateY(-1px); box-shadow: var(--shadow);
}
.quick-card .card-icon { font-size: 18px; margin-bottom: 5px; }
.quick-card .card-title { font-size: 12px; font-weight: 600; color: var(--text-primary); }
.quick-card .card-desc { font-size: 10px; color: var(--text-secondary); margin-top: 3px; line-height: 1.4; }
/* 消息气泡 */
.message { display: flex; gap: 10px; max-width: 100%; }
.message.user { flex-direction: row-reverse; }
.message-avatar {
width: 30px; height: 30px; border-radius: 7px;
display: flex; align-items: center; justify-content: center;
font-size: 13px; flex-shrink: 0;
}
.message.user .message-avatar { background: linear-gradient(135deg, var(--accent-blue), #1d4ed8); }
.message.ai .message-avatar { background: linear-gradient(135deg, #1e88e5, #00bcd4); color: white; font-weight: 900; font-size: 11px; }
.message-content {
max-width: 78%; background: var(--bg-card); border: 1px solid var(--border);
border-radius: var(--radius); padding: 11px 14px;
font-size: 13px; line-height: 1.7;
}
.message.user .message-content { background: rgba(59,130,246,0.1); border-color: rgba(59,130,246,0.22); }
.message-content pre { background: rgba(0,0,0,0.3); border: 1px solid var(--border); border-radius: 7px; padding: 10px; overflow-x: auto; font-size: 11px; margin: 7px 0; }
.message-content table { border-collapse: collapse; width: 100%; margin: 7px 0; font-size: 12px; }
.message-content th, .message-content td { border: 1px solid var(--border); padding: 5px 9px; text-align: left; }
.message-content th { background: rgba(30,136,229,0.1); color: var(--accent-cyan); font-size: 11px; }
.message-content tr:nth-child(even) td { background: rgba(255,255,255,0.02); }
.message-content blockquote { border-left: 3px solid var(--accent-cyan); padding-left: 10px; color: var(--text-secondary); margin: 7px 0; }
.message-content h1 { font-size: 15px; color: var(--accent-cyan); margin: 12px 0 6px; border-bottom: 1px solid var(--border); padding-bottom: 4px; }
.message-content h2 { font-size: 13px; color: var(--accent-cyan); margin: 10px 0 5px; }
.message-content h3 { font-size: 12px; color: var(--accent-gold-light); margin: 8px 0 4px; }
.message-content hr { border: none; border-top: 1px solid var(--border); margin: 10px 0; }
/* 打字动画 */
.typing-indicator { display: flex; gap: 4px; align-items: center; padding: 3px 0; }
.typing-dot { width: 5px; height: 5px; background: var(--accent-cyan); border-radius: 50%; animation: typing 1.2s infinite; }
.typing-dot:nth-child(2) { animation-delay: 0.2s; }
.typing-dot:nth-child(3) { animation-delay: 0.4s; }
@keyframes typing { 0%,60%,100%{transform:translateY(0);opacity:0.4} 30%{transform:translateY(-5px);opacity:1} }
.stream-cursor { display: inline-block; width: 2px; height: 14px; background: var(--accent-cyan); animation: blink 0.7s infinite; vertical-align: middle; margin-left: 2px; }
@keyframes blink { 0%,100%{opacity:1} 50%{opacity:0} }
/* 估值结果卡片 */
.valuation-card {
background: linear-gradient(135deg, rgba(30,136,229,0.08), rgba(0,188,212,0.04));
border: 1px solid rgba(0,188,212,0.25);
border-radius: var(--radius); padding: 14px; margin-top: 10px;
}
.vc-header { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; flex-wrap: wrap; }
.vc-title { font-size: 13px; font-weight: 700; color: var(--accent-cyan); }
.vc-badge {
background: rgba(212,168,67,0.12); color: var(--accent-gold);
border: 1px solid rgba(212,168,67,0.25);
padding: 2px 8px; border-radius: 10px; font-size: 10px;
}
/* 三重价值网格 */
.triple-value { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-bottom: 10px; }
.tv-item { background: rgba(0,0,0,0.2); border-radius: 8px; padding: 9px 10px; text-align: center; }
.tv-label { font-size: 9px; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 3px; }
.tv-value { font-size: 15px; font-weight: 800; }
.tv-value.gold { color: var(--accent-gold); }
.tv-value.cyan { color: var(--accent-cyan); }
.tv-value.green { color: var(--accent-green); }
.tv-sub { font-size: 9px; color: var(--text-muted); margin-top: 2px; }
/* 质押行 */
.pledge-row {
background: rgba(212,168,67,0.06); border: 1px solid rgba(212,168,67,0.18);
border-radius: 7px; padding: 8px 12px;
display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;
}
.pledge-label { font-size: 11px; color: var(--text-secondary); }
.pledge-value { font-size: 13px; font-weight: 700; color: var(--accent-gold); }
/* 置信度条 */
.confidence-bar { height: 3px; background: var(--border); border-radius: 2px; margin-bottom: 10px; overflow: hidden; }
.confidence-fill { height: 100%; background: linear-gradient(90deg, var(--accent-blue), var(--accent-cyan)); border-radius: 2px; transition: width 0.8s ease; }
/* 操作按钮 */
.chain-actions { display: flex; gap: 7px; flex-wrap: wrap; }
.chain-btn {
flex: 1; min-width: 80px; padding: 8px 10px; border-radius: 7px;
font-size: 11px; font-weight: 600; cursor: pointer; transition: all 0.2s; border: none;
}
.chain-btn.primary { background: linear-gradient(135deg, #1e88e5, #00bcd4); color: white; }
.chain-btn.primary:hover { opacity: 0.85; }
.chain-btn.secondary { background: transparent; border: 1px solid var(--border); color: var(--text-secondary); }
.chain-btn.secondary:hover { border-color: var(--accent-cyan); color: var(--accent-cyan); }
/* 输入区域 */
.input-area { padding: 12px 16px; border-top: 1px solid var(--border); background: var(--bg-secondary); }
.context-bar { display: flex; gap: 6px; margin-bottom: 8px; flex-wrap: wrap; align-items: center; }
.ctx-chip {
display: flex; align-items: center; gap: 4px;
background: var(--bg-card); border: 1px solid var(--border);
padding: 3px 8px; border-radius: 12px; font-size: 10px; color: var(--text-secondary);
}
.ctx-chip.active { border-color: var(--accent-cyan); color: var(--accent-cyan); background: rgba(0,188,212,0.07); }
.input-wrapper { display: flex; gap: 8px; align-items: flex-end; }
.chat-input {
flex: 1; background: var(--bg-card); border: 1px solid var(--border);
border-radius: var(--radius); padding: 10px 14px;
color: var(--text-primary); font-size: 13px; resize: none; outline: none;
min-height: 44px; max-height: 140px; transition: border-color 0.2s;
font-family: inherit; line-height: 1.5;
}
.chat-input:focus { border-color: var(--accent-cyan); }
.chat-input::placeholder { color: var(--text-muted); }
.send-btn {
width: 44px; height: 44px;
background: linear-gradient(135deg, #1e88e5, #00bcd4);
border: none; border-radius: 9px; color: white;
cursor: pointer; transition: opacity 0.2s;
display: flex; align-items: center; justify-content: center; flex-shrink: 0;
}
.send-btn:hover { opacity: 0.85; }
.send-btn:disabled { opacity: 0.4; cursor: not-allowed; }
.send-btn svg { width: 18px; height: 18px; fill: white; }
.toolbar { display: flex; gap: 6px; margin-top: 8px; flex-wrap: wrap; align-items: center; }
.tool-chip {
background: var(--bg-card); border: 1px solid var(--border);
color: var(--text-secondary); border-radius: 12px;
padding: 3px 10px; font-size: 11px; cursor: pointer; transition: all 0.2s;
}
.tool-chip:hover { border-color: var(--accent-blue); color: var(--accent-blue); }
.tool-chip.active { background: rgba(59,130,246,0.1); border-color: var(--accent-blue); color: var(--accent-blue); }
.toolbar-hint { margin-left: auto; font-size: 10px; color: var(--text-muted); }
/* 滚动条 */
::-webkit-scrollbar { width: 4px; height: 4px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
/* 响应式 */
@media (max-width: 900px) { .sidebar { display: none; } }
@media (max-width: 600px) { .quick-cards { grid-template-columns: repeat(2, 1fr); } .data-panel { display: none; } }
@media (max-width: 400px) { .quick-cards { grid-template-columns: 1fr; } }
</style>
</head>
<body>
<!-- HEADER -->
<div class="header">
<div class="header-left">
<div class="logo">NAC</div>
<div>
<div class="header-title" data-i18n="title">NAC AI 资产估值顾问</div>
<div class="header-subtitle" data-i18n="subtitle">20大类资产 · 60+辖区 · SDR锚定 · 三重价值</div>
</div>
<span class="version-badge">v2.0</span>
</div>
<div class="header-right">
<div class="xtzh-price-badge" onclick="refreshXTZHPrice()" title="点击刷新XTZH实时价格">
<div class="price-dot"></div>
<div>
<div class="label">XTZH/USD</div>
<div class="price" id="header-xtzh-price">加载中...</div>
</div>
</div>
<select class="lang-select" id="langSelect" onchange="switchLanguage(this.value)">
<option value="zh">🇨🇳 中文</option>
<option value="en">🇺🇸 English</option>
<option value="ar">🇸🇦 العربية</option>
<option value="ja">🇯🇵 日本語</option>
<option value="ko">🇰🇷 한국어</option>
<option value="fr">🇫🇷 Français</option>
<option value="de">🇩🇪 Deutsch</option>
<option value="ru">🇷🇺 Русский</option>
<option value="es">🇪🇸 Español</option>
</select>
</div>
</div>
<!-- MAIN -->
<div class="main">
<!-- SIDEBAR -->
<div class="sidebar">
<div class="sidebar-header">
<button class="new-chat-btn" onclick="newChat()" data-i18n="newChat">+ 新建估值</button>
<button class="clear-btn" onclick="clearMemory()" title="清除对话记忆">🗑</button>
</div>
<!-- 司法辖区选择 -->
<div class="sidebar-section">
<div class="sidebar-section-title" data-i18n="jurisdiction">司法辖区</div>
<select class="juris-select" id="jurisdictionSelect" onchange="setJurisdiction(this.value)">
<optgroup label="🌏 东亚 / East Asia">
<option value="HK">🇭🇰 香港 Hong Kong</option>
<option value="CN">🇨🇳 中国大陆 China Mainland</option>
<option value="TW">🇹🇼 台湾 Taiwan</option>
<option value="JP">🇯🇵 日本 Japan</option>
<option value="KR">🇰🇷 韩国 South Korea</option>
<option value="MO">🇲🇴 澳门 Macau</option>
</optgroup>
<optgroup label="🌏 东南亚 / Southeast Asia">
<option value="SG">🇸🇬 新加坡 Singapore</option>
<option value="MY">🇲🇾 马来西亚 Malaysia</option>
<option value="TH">🇹🇭 泰国 Thailand</option>
<option value="ID">🇮🇩 印尼 Indonesia</option>
<option value="PH">🇵🇭 菲律宾 Philippines</option>
<option value="VN">🇻🇳 越南 Vietnam</option>
</optgroup>
<optgroup label="🌍 中东 / Middle East">
<option value="AE">🇦🇪 阿联酋 UAE</option>
<option value="SA">🇸🇦 沙特 Saudi Arabia</option>
<option value="QA">🇶🇦 卡塔尔 Qatar</option>
<option value="KW">🇰🇼 科威特 Kuwait</option>
<option value="BH">🇧🇭 巴林 Bahrain</option>
<option value="IL">🇮🇱 以色列 Israel</option>
</optgroup>
<optgroup label="🌍 欧洲 / Europe">
<option value="GB">🇬🇧 英国 UK</option>
<option value="DE">🇩🇪 德国 Germany</option>
<option value="FR">🇫🇷 法国 France</option>
<option value="CH">🇨🇭 瑞士 Switzerland</option>
<option value="NL">🇳🇱 荷兰 Netherlands</option>
<option value="IT">🇮🇹 意大利 Italy</option>
<option value="ES">🇪🇸 西班牙 Spain</option>
<option value="EU">🇪🇺 欧盟 EU</option>
</optgroup>
<optgroup label="🌎 美洲 / Americas">
<option value="US">🇺🇸 美国 USA</option>
<option value="CA">🇨🇦 加拿大 Canada</option>
<option value="BR">🇧🇷 巴西 Brazil</option>
<option value="MX">🇲🇽 墨西哥 Mexico</option>
<option value="CL">🇨🇱 智利 Chile</option>
<option value="AR">🇦🇷 阿根廷 Argentina</option>
</optgroup>
<optgroup label="🌏 大洋洲 / Oceania">
<option value="AU">🇦🇺 澳大利亚 Australia</option>
<option value="NZ">🇳🇿 新西兰 New Zealand</option>
</optgroup>
<optgroup label="🌍 南亚 / South Asia">
<option value="IN">🇮🇳 印度 India</option>
</optgroup>
<optgroup label="🌍 非洲 / Africa">
<option value="ZA">🇿🇦 南非 South Africa</option>
<option value="NG">🇳🇬 尼日利亚 Nigeria</option>
<option value="EG">🇪🇬 埃及 Egypt</option>
<option value="KE">🇰🇪 肯尼亚 Kenya</option>
</optgroup>
<optgroup label="🌍 其他 / Others">
<option value="RU">🇷🇺 俄罗斯 Russia</option>
<option value="TR">🇹🇷 土耳其 Turkey</option>
</optgroup>
</select>
</div>
<!-- 20大类资产选择 -->
<div class="sidebar-section">
<div class="sidebar-section-title" data-i18n="assetType">资产类别GNACS</div>
<select class="asset-cat-select" id="assetCatSelect" onchange="setAssetCategory(this.value)">
<option value="real_estate">🏢 GNACS-01 不动产类</option>
<option value="financial_securities">💹 GNACS-02 金融证券类</option>
<option value="commodities">🏭 GNACS-03 大宗商品类</option>
<option value="art_collectibles">🎨 GNACS-04 艺术品与收藏品类</option>
<option value="intellectual_property">💡 GNACS-05 知识产权类</option>
<option value="digital_assets">🔗 GNACS-06 数字资产类</option>
<option value="infrastructure">🏗️ GNACS-07 基础设施类</option>
<option value="natural_resources">🌿 GNACS-08 自然资源类</option>
<option value="environmental_rights">🌱 GNACS-09 环境权益类</option>
<option value="corporate_equity">📊 GNACS-10 企业权益类</option>
<option value="debt_assets">📋 GNACS-11 债权类</option>
<option value="insurance_products">🛡️ GNACS-12 保险产品类</option>
<option value="agricultural_assets">🌾 GNACS-13 农业资产类</option>
<option value="vehicles">🚗 GNACS-14 交通工具类</option>
<option value="machinery_equipment">⚙️ GNACS-15 机械设备类</option>
<option value="data_assets">💾 GNACS-16 数据资产类</option>
<option value="brand_assets">🏷️ GNACS-17 品牌资产类</option>
<option value="sports_assets">⚽ GNACS-18 体育资产类</option>
<option value="entertainment_assets">🎬 GNACS-19 娱乐资产类</option>
<option value="other_assets">📦 GNACS-20 其他资产类</option>
</select>
<!-- 子类标签 -->
<div class="subcat-grid" id="subcatGrid"></div>
</div>
<!-- 历史对话 -->
<div class="sidebar-section">
<div class="sidebar-section-title" data-i18n="history">历史估值</div>
</div>
<div class="history-list" id="historyList"></div>
</div>
<!-- CHAT AREA -->
<div class="chat-area">
<!-- 顶部数据面板 -->
<div class="data-panel" id="dataPanel">
<div class="dp-stat">
<div class="dp-label">XTZH/USD</div>
<div class="dp-value" id="dp-xtzh-price">--</div>
<div class="dp-sub">SDR锚定</div>
</div>
<div class="dp-divider"></div>
<div class="dp-stat">
<div class="dp-label">黄金覆盖率</div>
<div class="dp-value cyan" id="dp-gold">--</div>
<div class="dp-sub">储备保障</div>
</div>
<div class="dp-divider"></div>
<div class="dp-stat">
<div class="dp-label">当前辖区</div>
<div class="dp-value blue" id="dp-jurisdiction">HK</div>
<div class="dp-sub" id="dp-juris-name">香港</div>
</div>
<div class="dp-divider"></div>
<div class="dp-stat">
<div class="dp-label">资产类别</div>
<div class="dp-value green" id="dp-asset-type">不动产</div>
<div class="dp-sub" id="dp-gnacs">GNACS-01</div>
</div>
<div class="dp-divider"></div>
<div class="dp-stat">
<div class="dp-label">辖区货币</div>
<div class="dp-value" id="dp-currency">HKD</div>
<div class="dp-sub" id="dp-usd-rate">1 HKD ≈ 0.128 USD</div>
</div>
<div class="dp-divider"></div>
<div class="dp-stat">
<div class="dp-label">监管机构</div>
<div class="dp-value cyan" id="dp-regulator">SFC/HKMA</div>
<div class="dp-sub" id="dp-tier">Tier 1</div>
</div>
</div>
<!-- 消息区域 -->
<div class="messages" id="messages">
<!-- 欢迎界面 -->
<div class="welcome" id="welcome">
<div class="welcome-icon">💎</div>
<h1 data-i18n="welcomeTitle">NAC AI 资产估值顾问</h1>
<p class="welcome-desc" data-i18n="welcomeDesc">基于 NAC 公链原生知识库,支持 20 大类资产 × 60+ 司法辖区。采用 SDR 锚定 XTZH 统一定价,输出当地货币 + USD + XTZH 三重价值,一键申请上链。</p>
<div class="welcome-stats">
<div class="ws-item"><div class="ws-num">20</div><div class="ws-label">大类资产</div></div>
<div class="ws-item"><div class="ws-num">60+</div><div class="ws-label">司法辖区</div></div>
<div class="ws-item"><div class="ws-num">50+</div><div class="ws-label">估值子类</div></div>
<div class="ws-item"><div class="ws-num">3</div><div class="ws-label">重价值输出</div></div>
</div>
<div class="quick-cards">
<div class="quick-card" onclick="quickAsk('香港九龙区120平米住宅公寓市值800万港元请估算XTZH价值和质押要求')">
<div class="card-icon">🏠</div>
<div class="card-title">住宅不动产估值</div>
<div class="card-desc">香港/新加坡/阿联酋住宅资产三重价值换算</div>
</div>
<div class="quick-card" onclick="quickAsk('我有一首原创音乐的版权每年版税收入约50万元剩余保护期70年请用收益法估值')">
<div class="card-icon">🎵</div>
<div class="card-title">音乐版权估值</div>
<div class="card-desc">版税折现法,剩余保护期,授权范围分析</div>
</div>
<div class="quick-card" onclick="quickAsk('我有一项发明专利技术领域是新能源电池每年许可费收入约200万美元请估值')">
<div class="card-icon">🔬</div>
<div class="card-title">发明专利估值</div>
<div class="card-desc">许可费节省法/超额收益法,技术替代风险</div>
</div>
<div class="quick-card" onclick="quickAsk('某知名艺人的肖像权和商业形象权年商业化收入约1000万元请估值')">
<div class="card-icon">🌟</div>
<div class="card-title">肖像权/IP估值</div>
<div class="card-desc">品牌溢价法,商业化收益折现,知名度评估</div>
</div>
<div class="quick-card" onclick="quickAsk('新加坡CBD商业写字楼1200平米年租金收入180万SGD请用收益法估值并换算XTZH')">
<div class="card-icon">🏢</div>
<div class="card-title">商业地产收益法</div>
<div class="card-desc">收益资本化法,新加坡辖区特定参数</div>
</div>
<div class="quick-card" onclick="quickAsk('我有一个注册商标覆盖全球30个国家年品牌授权收入约500万美元请估值')">
<div class="card-icon">™️</div>
<div class="card-title">商标权估值</div>
<div class="card-desc">品牌收益折现,注册地域,市场溢价分析</div>
</div>
<div class="quick-card" onclick="quickAsk('我持有某非上市公司30%股权公司年净利润约2000万元请用市场乘数法估值')">
<div class="card-icon">📊</div>
<div class="card-title">非上市股权估值</div>
<div class="card-desc">市场乘数法/DCF流动性折价控制权溢价</div>
</div>
<div class="quick-card" onclick="quickAsk('我有一批碳排放权VCS标准核证共100万吨CO2当量请估值并说明NAC上链流程')">
<div class="card-icon">🌱</div>
<div class="card-title">碳排放权估值</div>
<div class="card-desc">VCS/Gold Standard核证市场价格法</div>
</div>
<div class="quick-card" onclick="quickAsk('一幅毕加索真迹油画上次拍卖价格为1200万美元请估值并分析艺术品上链流程')">
<div class="card-icon">🎨</div>
<div class="card-title">艺术品估值</div>
<div class="card-desc">拍卖记录法,专家鉴定,艺术家声誉溢价</div>
</div>
<div class="quick-card" onclick="quickAsk('请解释XTZH的SDR锚定定价机制当前价格如何计算黄金储备覆盖率是多少')">
<div class="card-icon">💰</div>
<div class="card-title">XTZH定价机制</div>
<div class="card-desc">SDR五货币篮子+黄金储备双重保障原理</div>
</div>
<div class="quick-card" onclick="quickAsk('请对比香港、新加坡、阿联酋三个辖区的不动产估值差异,包括资本化率和外籍购房规则')">
<div class="card-icon">🌍</div>
<div class="card-title">辖区对比分析</div>
<div class="card-desc">Tier 1辖区不动产参数对比外籍规则</div>
</div>
<div class="quick-card" onclick="quickAsk('资产上链完整流程是什么从估值到TOKEN生成、权证发行、代币发行需要哪些步骤')">
<div class="card-icon">⛓️</div>
<div class="card-title">一键上链流程</div>
<div class="card-desc">估值→合规→TOKEN→权证→代币发行</div>
</div>
</div>
</div>
</div>
<!-- 输入区域 -->
<div class="input-area">
<div class="context-bar" id="contextBar">
<div class="ctx-chip active" id="ctx-jurisdiction">🌏 <span id="ctx-juris-text">香港</span></div>
<div class="ctx-chip active" id="ctx-asset">📦 <span id="ctx-asset-text">不动产</span></div>
<div class="ctx-chip" id="ctx-mode">💎 估值模式</div>
</div>
<div class="input-wrapper">
<textarea
class="chat-input"
id="chatInput"
rows="1"
placeholder="描述您的资产类型、位置、面积、价格等AI将自动估算三重价值..."
onkeydown="handleKeyDown(event)"
oninput="autoResize(this)"
></textarea>
<button class="send-btn" id="sendBtn" onclick="sendMessage()" title="发送">
<svg viewBox="0 0 24 24"><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></svg>
</button>
</div>
<div class="toolbar">
<div class="tool-chip active" id="chip-valuation" onclick="toggleMode('valuation', this)">💎 估值</div>
<div class="tool-chip" id="chip-chain" onclick="toggleMode('chain', this)">⛓️ 上链</div>
<div class="tool-chip" id="chip-compare" onclick="toggleMode('compare', this)">📊 辖区对比</div>
<div class="tool-chip" id="chip-explain" onclick="toggleMode('explain', this)">📖 方法论</div>
<div class="tool-chip" id="chip-gnacs" onclick="toggleMode('gnacs', this)">🔖 GNACS</div>
<div class="toolbar-hint">Enter发送 · Shift+Enter换行</div>
</div>
</div>
</div>
</div>
<script>
// ============================================================
// 配置
// ============================================================
const CONFIG = {
valuationApiBase: '/api/v4',
inferenceApiBase: '/api/inference',
maxMemory: 12,
lang: 'zh'
};
// ============================================================
// 20大类资产定义与 server.js 知识库对应)
// ============================================================
const ASSET_CATEGORIES = {
real_estate: { name: '不动产', gnacs: 'GNACS-01', icon: '🏢',
subcats: ['住宅(公寓/别墅)', '商业地产', '工业用地', '农业用地', '基础设施', '酒店/服务式公寓'] },
financial_securities: { name: '金融证券', gnacs: 'GNACS-02', icon: '💹',
subcats: ['股票/股权', '债券', '基金份额', '期货/期权', 'REITs', '结构化产品'] },
commodities: { name: '大宗商品', gnacs: 'GNACS-03', icon: '🏭',
subcats: ['贵金属(黄金/白银)', '能源(石油/天然气)', '农产品', '工业金属', '稀土/稀有金属'] },
art_collectibles: { name: '艺术品与收藏品', gnacs: 'GNACS-04', icon: '🎨',
subcats: ['绘画/油画', '雕塑', '古董/文物', '名酒/威士忌', '名表', '珠宝/宝石', '邮票/钱币'] },
intellectual_property: { name: '知识产权', gnacs: 'GNACS-05', icon: '💡',
subcats: ['发明专利', '实用新型专利', '外观设计专利', '版权(文学/音乐/影视)', '商标权', '肖像权/形象权', '地理标志', '商业秘密', '软件著作权', '域名权益'] },
digital_assets: { name: '数字资产', gnacs: 'GNACS-06', icon: '🔗',
subcats: ['加密货币', 'NFT艺术', '元宇宙土地', '游戏道具/资产', '数字藏品', 'DeFi份额'] },
infrastructure: { name: '基础设施', gnacs: 'GNACS-07', icon: '🏗️',
subcats: ['港口/机场', '能源管道', '通信基础设施', '水务设施', '交通基础设施', '数据中心'] },
natural_resources: { name: '自然资源', gnacs: 'GNACS-08', icon: '🌿',
subcats: ['矿产资源', '森林/林地', '水资源', '渔业权', '海洋资源'] },
environmental_rights: { name: '环境权益', gnacs: 'GNACS-09', icon: '🌱',
subcats: ['碳排放权VCS/CDM', '可再生能源配额REC', '排污权', '水权', '生物多样性信用'] },
corporate_equity: { name: '企业权益', gnacs: 'GNACS-10', icon: '📊',
subcats: ['上市公司股权', '非上市公司股权', '有限合伙份额', '特许经营权', '并购目标估值'] },
debt_assets: { name: '债权', gnacs: 'GNACS-11', icon: '📋',
subcats: ['应收账款', '商业票据', '企业债券', '不良资产', '供应链金融'] },
insurance_products: { name: '保险产品', gnacs: 'GNACS-12', icon: '🛡️',
subcats: ['人寿保险现金价值', '年金权益', '保险结构化产品', '再保险权益'] },
agricultural_assets: { name: '农业资产', gnacs: 'GNACS-13', icon: '🌾',
subcats: ['耕地/农场', '农业设备', '农业知识产权', '农产品期货', '养殖权益'] },
vehicles: { name: '交通工具', gnacs: 'GNACS-14', icon: '🚗',
subcats: ['豪华汽车/超跑', '游艇', '私人飞机', '直升机', '商用车队'] },
machinery_equipment: { name: '机械设备', gnacs: 'GNACS-15', icon: '⚙️',
subcats: ['工业设备', '医疗设备', '航空设备', '建筑机械', '精密仪器'] },
data_assets: { name: '数据资产', gnacs: 'GNACS-16', icon: '💾',
subcats: ['用户数据集', '医疗/基因数据', '金融数据', 'AI训练数据', '地理数据'] },
brand_assets: { name: '品牌资产', gnacs: 'GNACS-17', icon: '🏷️',
subcats: ['企业品牌价值', '产品品牌', '个人品牌/IP', '特许加盟权', '域名品牌'] },
sports_assets: { name: '体育资产', gnacs: 'GNACS-18', icon: '⚽',
subcats: ['球员转会权', '体育俱乐部股权', '赛事转播权', '体育场馆', '运动员代言权'] },
entertainment_assets: { name: '娱乐资产', gnacs: 'GNACS-19', icon: '🎬',
subcats: ['影视版权', '音乐版权', '游戏IP', '综艺版权', '艺人经纪合同', '流媒体权益'] },
other_assets: { name: '其他资产', gnacs: 'GNACS-20', icon: '📦',
subcats: ['特殊用途资产', '混合型资产', '跨类别资产组合'] }
};
// 辖区信息(简化版,完整版从 API 获取)
const JURISDICTION_INFO = {
HK: { name: '香港', currency: 'HKD', usdRate: 0.1282, regulator: 'SFC/HKMA', tier: 1, flag: '🇭🇰' },
CN: { name: '中国大陆', currency: 'CNY', usdRate: 0.1379, regulator: 'CSRC/PBOC', tier: 1, flag: '🇨🇳' },
TW: { name: '台湾', currency: 'TWD', usdRate: 0.0311, regulator: 'FSC', tier: 2, flag: '🇹🇼' },
JP: { name: '日本', currency: 'JPY', usdRate: 0.0066, regulator: 'FSA', tier: 1, flag: '🇯🇵' },
KR: { name: '韩国', currency: 'KRW', usdRate: 0.00073, regulator: 'FSC', tier: 1, flag: '🇰🇷' },
MO: { name: '澳门', currency: 'MOP', usdRate: 0.1245, regulator: 'AMCM', tier: 2, flag: '🇲🇴' },
SG: { name: '新加坡', currency: 'SGD', usdRate: 0.7432, regulator: 'MAS', tier: 1, flag: '🇸🇬' },
MY: { name: '马来西亚', currency: 'MYR', usdRate: 0.2252, regulator: 'SC', tier: 2, flag: '🇲🇾' },
TH: { name: '泰国', currency: 'THB', usdRate: 0.0278, regulator: 'SEC', tier: 2, flag: '🇹🇭' },
ID: { name: '印度尼西亚', currency: 'IDR', usdRate: 0.000063, regulator: 'OJK', tier: 2, flag: '🇮🇩' },
PH: { name: '菲律宾', currency: 'PHP', usdRate: 0.0174, regulator: 'SEC', tier: 2, flag: '🇵🇭' },
VN: { name: '越南', currency: 'VND', usdRate: 0.0000394, regulator: 'SSC', tier: 3, flag: '🇻🇳' },
AE: { name: '阿联酋', currency: 'AED', usdRate: 0.2723, regulator: 'DFSA/ADGM', tier: 1, flag: '🇦🇪' },
SA: { name: '沙特阿拉伯', currency: 'SAR', usdRate: 0.2666, regulator: 'CMA', tier: 1, flag: '🇸🇦' },
QA: { name: '卡塔尔', currency: 'QAR', usdRate: 0.2747, regulator: 'QFMA', tier: 2, flag: '🇶🇦' },
KW: { name: '科威特', currency: 'KWD', usdRate: 3.2573, regulator: 'CMA', tier: 2, flag: '🇰🇼' },
BH: { name: '巴林', currency: 'BHD', usdRate: 2.6525, regulator: 'CBB', tier: 2, flag: '🇧🇭' },
IL: { name: '以色列', currency: 'ILS', usdRate: 0.2703, regulator: 'ISA', tier: 1, flag: '🇮🇱' },
GB: { name: '英国', currency: 'GBP', usdRate: 1.2650, regulator: 'FCA', tier: 1, flag: '🇬🇧' },
DE: { name: '德国', currency: 'EUR', usdRate: 1.0850, regulator: 'BaFin', tier: 1, flag: '🇩🇪' },
FR: { name: '法国', currency: 'EUR', usdRate: 1.0850, regulator: 'AMF', tier: 1, flag: '🇫🇷' },
CH: { name: '瑞士', currency: 'CHF', usdRate: 1.1250, regulator: 'FINMA', tier: 1, flag: '🇨🇭' },
NL: { name: '荷兰', currency: 'EUR', usdRate: 1.0850, regulator: 'AFM', tier: 1, flag: '🇳🇱' },
IT: { name: '意大利', currency: 'EUR', usdRate: 1.0850, regulator: 'CONSOB', tier: 1, flag: '🇮🇹' },
ES: { name: '西班牙', currency: 'EUR', usdRate: 1.0850, regulator: 'CNMV', tier: 1, flag: '🇪🇸' },
EU: { name: '欧盟', currency: 'EUR', usdRate: 1.0850, regulator: 'ESMA', tier: 1, flag: '🇪🇺' },
US: { name: '美国', currency: 'USD', usdRate: 1.0, regulator: 'SEC/CFTC', tier: 1, flag: '🇺🇸' },
CA: { name: '加拿大', currency: 'CAD', usdRate: 0.7380, regulator: 'CSA', tier: 1, flag: '🇨🇦' },
BR: { name: '巴西', currency: 'BRL', usdRate: 0.1980, regulator: 'CVM', tier: 2, flag: '🇧🇷' },
MX: { name: '墨西哥', currency: 'MXN', usdRate: 0.0492, regulator: 'CNBV', tier: 2, flag: '🇲🇽' },
CL: { name: '智利', currency: 'CLP', usdRate: 0.00107, regulator: 'CMF', tier: 2, flag: '🇨🇱' },
AR: { name: '阿根廷', currency: 'ARS', usdRate: 0.00105, regulator: 'CNV', tier: 3, flag: '🇦🇷' },
AU: { name: '澳大利亚', currency: 'AUD', usdRate: 0.6380, regulator: 'ASIC', tier: 1, flag: '🇦🇺' },
NZ: { name: '新西兰', currency: 'NZD', usdRate: 0.5980, regulator: 'FMA', tier: 2, flag: '🇳🇿' },
IN: { name: '印度', currency: 'INR', usdRate: 0.01198, regulator: 'SEBI', tier: 2, flag: '🇮🇳' },
ZA: { name: '南非', currency: 'ZAR', usdRate: 0.0543, regulator: 'FSCA', tier: 2, flag: '🇿🇦' },
NG: { name: '尼日利亚', currency: 'NGN', usdRate: 0.000625, regulator: 'SEC', tier: 3, flag: '🇳🇬' },
EG: { name: '埃及', currency: 'EGP', usdRate: 0.0203, regulator: 'FRA', tier: 3, flag: '🇪🇬' },
KE: { name: '肯尼亚', currency: 'KES', usdRate: 0.00775, regulator: 'CMA', tier: 3, flag: '🇰🇪' },
RU: { name: '俄罗斯', currency: 'RUB', usdRate: 0.01095, regulator: 'CBR', tier: 2, flag: '🇷🇺' },
TR: { name: '土耳其', currency: 'TRY', usdRate: 0.0292, regulator: 'SPK', tier: 2, flag: '🇹🇷' }
};
// 多语言
const I18N = {
zh: {
title: 'NAC AI 资产估值顾问',
subtitle: '20大类资产 · 60+辖区 · SDR锚定 · 三重价值',
newChat: '+ 新建估值',
jurisdiction: '司法辖区',
assetType: '资产类别GNACS',
history: '历史估值',
welcomeTitle: 'NAC AI 资产估值顾问',
welcomeDesc: '基于 NAC 公链原生知识库,支持 20 大类资产 × 60+ 司法辖区。采用 SDR 锚定 XTZH 统一定价,输出当地货币 + USD + XTZH 三重价值,一键申请上链。',
placeholder: '描述您的资产类型、位置、面积、价格等AI将自动估算三重价值...',
sending: '正在分析...',
chainBtn: '申请上链',
detailBtn: '查看详情',
compareBtn: '辖区对比',
confidence: '置信度',
finalXTZH: 'XTZH价值',
finalUSD: 'USD估值',
localValue: '当地货币',
stakeRequired: '质押要求(80%)',
},
en: {
title: 'NAC AI Asset Valuation Advisor',
subtitle: '20 Asset Classes · 60+ Jurisdictions · SDR-Pegged · Triple Value',
newChat: '+ New Valuation',
jurisdiction: 'Jurisdiction',
assetType: 'Asset Category (GNACS)',
history: 'History',
welcomeTitle: 'NAC AI Asset Valuation Advisor',
welcomeDesc: 'Native NAC blockchain knowledge base supporting 20 asset classes × 60+ jurisdictions. SDR-pegged XTZH pricing with local currency + USD + XTZH triple value output.',
placeholder: 'Describe your asset (type, location, area, price...) for triple value estimation...',
sending: 'Analyzing...',
chainBtn: 'Apply On-Chain',
detailBtn: 'View Details',
compareBtn: 'Compare Jurisdictions',
confidence: 'Confidence',
finalXTZH: 'XTZH Value',
finalUSD: 'USD Value',
localValue: 'Local Currency',
stakeRequired: 'Stake Required (80%)',
}
};
// ============================================================
// 状态管理
// ============================================================
let state = {
messages: [],
sessionId: generateSessionId(),
conversationMemory: [],
currentJurisdiction: 'HK',
currentAssetCategory: 'real_estate',
currentSubcat: null,
currentMode: 'valuation',
xtzhPrice: null,
isStreaming: false,
history: JSON.parse(localStorage.getItem('nac_valuation_history_v2') || '[]')
};
function generateSessionId() {
return 'vs_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
}
// ============================================================
// 初始化辖区和资产类别
// ============================================================
function setJurisdiction(code) {
state.currentJurisdiction = code;
const info = JURISDICTION_INFO[code];
if (info) {
document.getElementById('dp-jurisdiction').textContent = code;
document.getElementById('dp-juris-name').textContent = info.name;
document.getElementById('dp-currency').textContent = info.currency;
document.getElementById('dp-usd-rate').textContent = `1 ${info.currency}${info.usdRate} USD`;
document.getElementById('dp-regulator').textContent = info.regulator || 'N/A';
document.getElementById('dp-tier').textContent = `Tier ${info.tier}`;
document.getElementById('ctx-juris-text').textContent = info.flag + ' ' + info.name;
}
}
function setAssetCategory(key) {
state.currentAssetCategory = key;
state.currentSubcat = null;
const cat = ASSET_CATEGORIES[key];
if (cat) {
document.getElementById('dp-asset-type').textContent = cat.name;
document.getElementById('dp-gnacs').textContent = cat.gnacs;
document.getElementById('ctx-asset-text').textContent = cat.icon + ' ' + cat.name;
renderSubcats(cat.subcats);
}
}
function renderSubcats(subcats) {
const grid = document.getElementById('subcatGrid');
if (!subcats || subcats.length === 0) { grid.innerHTML = ''; return; }
grid.innerHTML = subcats.map(s => `<div class="subcat-tag" onclick="selectSubcat(this, '${s.replace(/'/g, '')}')">${s}</div>`).join('');
}
function selectSubcat(el, subcat) {
document.querySelectorAll('.subcat-tag').forEach(t => t.classList.remove('active'));
el.classList.add('active');
state.currentSubcat = subcat;
// 自动填充输入框提示
const cat = ASSET_CATEGORIES[state.currentAssetCategory];
const juris = JURISDICTION_INFO[state.currentJurisdiction];
const input = document.getElementById('chatInput');
if (!input.value) {
input.value = `${juris ? juris.name : state.currentJurisdiction}辖区,${subcat},请提供估值方法和关键参数说明`;
autoResize(input);
}
}
// ============================================================
// XTZH实时价格
// ============================================================
async function fetchXTZHPrice() {
try {
const res = await fetch(CONFIG.valuationApiBase + '/xtzh-price');
if (!res.ok) throw new Error('HTTP ' + res.status);
const data = await res.json();
state.xtzhPrice = data;
const usd = parseFloat(data.usd || data.price || 4.3944).toFixed(4);
document.getElementById('header-xtzh-price').textContent = '$' + usd;
document.getElementById('dp-xtzh-price').textContent = '$' + usd;
const goldCov = data.goldCoverage ? (data.goldCoverage * 100).toFixed(0) + '%' : '125%';
document.getElementById('dp-gold').textContent = goldCov;
} catch(e) {
document.getElementById('header-xtzh-price').textContent = '$4.3944';
document.getElementById('dp-xtzh-price').textContent = '$4.3944';
document.getElementById('dp-gold').textContent = '125%';
state.xtzhPrice = { usd: 4.3944, source: 'fallback' };
}
}
function refreshXTZHPrice() {
document.getElementById('header-xtzh-price').textContent = '刷新中...';
fetchXTZHPrice();
}
// ============================================================
// 模式切换
// ============================================================
function toggleMode(mode, el) {
state.currentMode = mode;
document.querySelectorAll('.tool-chip').forEach(c => c.classList.remove('active'));
el.classList.add('active');
document.getElementById('ctx-mode').textContent = el.textContent;
document.getElementById('ctx-mode').classList.add('active');
const input = document.getElementById('chatInput');
const modeHints = {
valuation: '描述您的资产类型、位置、面积、价格等AI将自动估算三重价值...',
chain: '描述您希望上链的资产AI将提供完整的ACC-20上链流程指引...',
compare: '请输入您想对比的辖区或资产类型AI将提供详细对比分析...',
explain: '请输入您想了解的估值方法(如:收益资本化法、许可费节省法等)...',
gnacs: '请输入您想了解的GNACS资产分类编码或资产类型...'
};
input.placeholder = modeHints[mode] || modeHints.valuation;
}
// ============================================================
// 发送消息
// ============================================================
async function sendMessage() {
const input = document.getElementById('chatInput');
const text = input.value.trim();
if (!text || state.isStreaming) return;
// 隐藏欢迎界面
const welcome = document.getElementById('welcome');
if (welcome) welcome.style.display = 'none';
input.value = '';
input.style.height = 'auto';
state.isStreaming = true;
document.getElementById('sendBtn').disabled = true;
appendUserMessage(text);
if (state.messages.length === 1) addToHistory(text.substring(0, 30) + (text.length > 30 ? '...' : ''));
const msgId = 'msg_' + Date.now();
appendAIMessage('', msgId);
// 构建消息历史
state.conversationMemory.push({ role: 'user', content: text });
if (state.conversationMemory.length > CONFIG.maxMemory * 2) {
state.conversationMemory = state.conversationMemory.slice(-CONFIG.maxMemory * 2);
}
const jurisInfo = JURISDICTION_INFO[state.currentJurisdiction] || {};
const catInfo = ASSET_CATEGORIES[state.currentAssetCategory] || {};
const requestBody = {
question: text,
messages: state.conversationMemory.slice(-CONFIG.maxMemory * 2),
jurisdictionCode: state.currentJurisdiction,
assetCategory: state.currentAssetCategory,
assetSubcat: state.currentSubcat,
mode: state.currentMode,
lang: CONFIG.lang,
context: {
jurisdiction: state.currentJurisdiction,
jurisdictionName: jurisInfo.name,
currency: jurisInfo.currency,
assetCategory: state.currentAssetCategory,
assetName: catInfo.name,
gnacs: catInfo.gnacs,
subcat: state.currentSubcat
}
};
try {
const response = await fetch(CONFIG.inferenceApiBase + '/stream', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(requestBody)
});
if (!response.ok) throw new Error('HTTP ' + response.status);
const reader = response.body.getReader();
const decoder = new TextDecoder();
let fullContent = '';
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop() || '';
for (const line of lines) {
if (!line.startsWith('data: ')) continue;
const dataStr = line.slice(6).trim();
if (dataStr === '[DONE]') {
removeCursor(msgId);
updateAIMessage(msgId, fullContent, false);
state.conversationMemory.push({ role: 'assistant', content: fullContent });
state.isStreaming = false;
document.getElementById('sendBtn').disabled = false;
break;
}
try {
const parsed = JSON.parse(dataStr);
if (parsed.type === 'valuation_result') {
renderValuationResult(parsed, msgId);
continue;
}
if (parsed.type === 'chunk' && parsed.content) {
fullContent += parsed.content;
updateAIMessage(msgId, fullContent, true);
} else if (parsed.choices && parsed.choices[0]) {
const delta = parsed.choices[0].delta;
if (delta && delta.content) {
fullContent += delta.content;
updateAIMessage(msgId, fullContent, true);
}
}
} catch(e) { /* skip */ }
}
}
} catch(e) {
updateAIMessage(msgId, `**连接错误:** ${e.message}\n\n请检查网络连接后重试。`, false);
} finally {
state.isStreaming = false;
document.getElementById('sendBtn').disabled = false;
removeCursor(msgId);
}
}
// ============================================================
// 渲染估值结果卡片(三重价值)
// ============================================================
function renderValuationResult(data, msgId) {
const v = data.valuation || data;
const xtzhPrice = (state.xtzhPrice && state.xtzhPrice.usd) ? state.xtzhPrice.usd : 4.3944;
const jurisInfo = JURISDICTION_INFO[state.currentJurisdiction] || { currency: 'USD', usdRate: 1.0, name: '全球' };
const catInfo = ASSET_CATEGORIES[state.currentAssetCategory] || { name: '资产', gnacs: 'GNACS-20' };
const t = I18N[CONFIG.lang] || I18N.zh;
const finalXTZH = v.finalXTZH || v.valueXTZH?.amount || 0;
const finalUSD = v.finalUSD || v.valueUSD?.amount || (finalXTZH * xtzhPrice);
const finalLocal = v.valueLocal?.amount || (finalUSD / jurisInfo.usdRate);
const stakeXTZH = finalXTZH * 0.8;
const confidence = v.confidence || 0.85;
const summaryText = `**资产估值完成** ✅\n\n估值方法:${v.valuationMethod || v.methodsUsed?.join(' + ') || '多方法融合'}\n\n> 基于 NAC 原生知识库,${jurisInfo.name}辖区,${catInfo.name}${catInfo.gnacs}`;
updateAIMessage(msgId, summaryText, false);
const contentEl = document.querySelector('#' + msgId + ' .message-content');
if (contentEl) {
const card = document.createElement('div');
card.className = 'valuation-card';
card.innerHTML = `
<div class="vc-header">
<span class="vc-title">💎 ${t.finalXTZH} 估值结果</span>
<span class="vc-badge">置信度 ${(confidence*100).toFixed(0)}%</span>
<span class="vc-badge" style="background:rgba(0,188,212,0.1);color:var(--accent-cyan);border-color:rgba(0,188,212,0.25)">${catInfo.gnacs}</span>
</div>
<div class="triple-value">
<div class="tv-item">
<div class="tv-label">${t.localValue} (${jurisInfo.currency})</div>
<div class="tv-value gold">${formatNumber(finalLocal)} ${jurisInfo.currency}</div>
<div class="tv-sub">${jurisInfo.name}市场价值</div>
</div>
<div class="tv-item">
<div class="tv-label">${t.finalUSD}</div>
<div class="tv-value cyan">$${formatNumber(finalUSD)}</div>
<div class="tv-sub">按 ${jurisInfo.currency}/USD 汇率换算</div>
</div>
<div class="tv-item">
<div class="tv-label">${t.finalXTZH}</div>
<div class="tv-value green">${formatNumber(finalXTZH)} XTZH</div>
<div class="tv-sub">@$${parseFloat(xtzhPrice).toFixed(4)}/XTZH</div>
</div>
</div>
<div class="pledge-row">
<span class="pledge-label">🔒 ${t.stakeRequired}(上链质押)</span>
<span class="pledge-value">${formatNumber(stakeXTZH)} XTZH</span>
</div>
<div class="confidence-bar"><div class="confidence-fill" style="width:${confidence*100}%"></div></div>
<div class="chain-actions">
<button class="chain-btn primary" onclick="applyOnChain(${finalXTZH.toFixed(2)}, ${finalUSD.toFixed(0)}, '${state.currentJurisdiction}')">
⛓️ ${t.chainBtn}
</button>
<button class="chain-btn secondary" onclick="viewValuationDetail()">📄 ${t.detailBtn}</button>
<button class="chain-btn secondary" onclick="compareJurisdictions()">🌍 ${t.compareBtn}</button>
</div>
`;
contentEl.appendChild(card);
}
scrollToBottom();
}
function formatNumber(n) {
if (!n || isNaN(n)) return '0';
if (n >= 1000000) return (n / 1000000).toFixed(2) + 'M';
if (n >= 1000) return n.toLocaleString('zh-CN', { maximumFractionDigits: 0 });
return n.toFixed(2);
}
function applyOnChain(xtzh, usd, jurisdiction) {
const msg = `我希望将这个资产上链,估值为 ${xtzh} XTZH$${usd} USD辖区 ${jurisdiction}。请告诉我完整的ACC-20上链流程和需要准备的材料。`;
document.getElementById('chatInput').value = msg;
toggleMode('chain', document.getElementById('chip-chain'));
sendMessage();
}
function viewValuationDetail() {
quickAsk('请详细解释刚才的估值计算过程,包括使用的方法、关键参数、辖区调整系数和三重价值换算逻辑');
}
function compareJurisdictions() {
const cat = ASSET_CATEGORIES[state.currentAssetCategory] || {};
quickAsk(`请对比香港、新加坡、阿联酋三个辖区对于${cat.name || '不动产'}的估值差异,包括资本化率、外籍规则和税务考量`);
}
// ============================================================
// DOM操作
// ============================================================
function appendUserMessage(text) {
const messages = document.getElementById('messages');
const div = document.createElement('div');
div.className = 'message user';
div.innerHTML = `
<div class="message-avatar">👤</div>
<div class="message-content">${escapeHtml(text)}</div>
`;
messages.appendChild(div);
state.messages.push({ role: 'user', content: text });
scrollToBottom();
}
function appendAIMessage(content, id) {
const messages = document.getElementById('messages');
const div = document.createElement('div');
div.className = 'message ai';
div.id = id;
div.innerHTML = `
<div class="message-avatar">NAC</div>
<div class="message-content">${content ? renderMarkdown(content) : '<div class="typing-indicator"><div class="typing-dot"></div><div class="typing-dot"></div><div class="typing-dot"></div></div>'}</div>
`;
messages.appendChild(div);
scrollToBottom();
return div;
}
function updateAIMessage(id, content, streaming) {
const el = document.querySelector('#' + id + ' .message-content');
if (!el) return;
el.innerHTML = renderMarkdown(content) + (streaming ? '<span class="stream-cursor"></span>' : '');
scrollToBottom();
}
function removeCursor(id) {
const cursor = document.querySelector('#' + id + ' .stream-cursor');
if (cursor) cursor.remove();
}
function scrollToBottom() {
const messages = document.getElementById('messages');
messages.scrollTop = messages.scrollHeight;
}
// ============================================================
// Markdown渲染
// ============================================================
function escapeInline(text) {
return text.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
}
function applyInline(text) {
return text
.replace(/\*\*(.+?)\*\*/g, '<strong style="color:var(--accent-gold-light)">$1</strong>')
.replace(/\*(.+?)\*/g, '<em style="color:var(--accent-cyan)">$1</em>')
.replace(/`([^`]+)`/g, '<code style="background:rgba(0,0,0,0.3);padding:2px 5px;border-radius:4px;font-size:11px">$1</code>');
}
function renderMarkdown(text) {
if (!text) return '';
const lines = text.split('\n');
const output = [];
let inTable = false, tableRows = [], isFirstRow = true;
let inCode = false, codeLines = [];
function flushTable() {
if (tableRows.length) {
output.push('<table>' + tableRows.join('') + '</tbody></table>');
}
inTable = false; tableRows = []; isFirstRow = true;
}
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
const trimmed = line.trim();
// 代码块
if (trimmed.startsWith('```')) {
if (!inCode) { inCode = true; codeLines = []; continue; }
else { inCode = false; output.push('<pre><code>' + codeLines.map(l => escapeInline(l)).join('\n') + '</code></pre>'); codeLines = []; continue; }
}
if (inCode) { codeLines.push(line); continue; }
// 表格
const isTableRow = /^\|.+\|$/.test(trimmed);
const isSeparator = /^\|[-|: ]+\|$/.test(trimmed);
if (isTableRow && !isSeparator) {
if (!inTable) { inTable = true; isFirstRow = true; tableRows = []; }
const cells = trimmed.split('|').filter(c => c !== '').map(c => applyInline(escapeInline(c.trim())));
if (isFirstRow) {
tableRows.push('<thead><tr>' + cells.map(c => `<th>${c}</th>`).join('') + '</tr></thead><tbody>');
isFirstRow = false;
} else {
tableRows.push('<tr>' + cells.map(c => `<td>${c}</td>`).join('') + '</tr>');
}
continue;
}
if (isSeparator) continue;
if (inTable) flushTable();
// 普通行
if (trimmed === '') { output.push('<br>'); continue; }
if (trimmed === '---' || trimmed === '***') { output.push('<hr>'); continue; }
const e = escapeInline(line);
if (/^### /.test(e)) { output.push(e.replace(/^### (.+)$/, (_, c) => `<h3>${applyInline(c)}</h3>`)); continue; }
if (/^## /.test(e)) { output.push(e.replace(/^## (.+)$/, (_, c) => `<h2>${applyInline(c)}</h2>`)); continue; }
if (/^# /.test(e)) { output.push(e.replace(/^# (.+)$/, (_, c) => `<h1>${applyInline(c)}</h1>`)); continue; }
if (/^&gt; /.test(e)) { output.push(e.replace(/^&gt; (.+)$/, (_, c) => `<blockquote>${applyInline(c)}</blockquote>`)); continue; }
if (/^[-*] /.test(e)) { output.push(e.replace(/^[-*] (.+)$/, (_, c) => `<li>${applyInline(c)}</li>`)); continue; }
if (/^\d+\. /.test(e)) { output.push(e.replace(/^\d+\. (.+)$/, (_, c) => `<li>${applyInline(c)}</li>`)); continue; }
output.push('<p>' + applyInline(e) + '</p>');
}
if (inTable) flushTable();
return output.join('\n')
.replace(/(<li>[\s\S]*?<\/li>\n?)+/g, m => `<ul>${m}</ul>`)
.replace(/<\/ul>\n?<ul>/g, '');
}
function escapeHtml(text) {
return text.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
}
// ============================================================
// 快捷问题
// ============================================================
function quickAsk(text) {
document.getElementById('chatInput').value = text;
sendMessage();
}
// ============================================================
// 新建对话 / 清除记忆
// ============================================================
function newChat() {
state.messages = [];
state.conversationMemory = [];
state.sessionId = generateSessionId();
state.isStreaming = false;
document.getElementById('sendBtn').disabled = false;
const messages = document.getElementById('messages');
messages.innerHTML = `
<div class="welcome" id="welcome">
<div class="welcome-icon">💎</div>
<h1 data-i18n="welcomeTitle">NAC AI 资产估值顾问</h1>
<p class="welcome-desc">基于 NAC 公链原生知识库,支持 20 大类资产 × 60+ 司法辖区。采用 SDR 锚定 XTZH 统一定价,输出当地货币 + USD + XTZH 三重价值,一键申请上链。</p>
<div class="welcome-stats">
<div class="ws-item"><div class="ws-num">20</div><div class="ws-label">大类资产</div></div>
<div class="ws-item"><div class="ws-num">60+</div><div class="ws-label">司法辖区</div></div>
<div class="ws-item"><div class="ws-num">50+</div><div class="ws-label">估值子类</div></div>
<div class="ws-item"><div class="ws-num">3</div><div class="ws-label">重价值输出</div></div>
</div>
<div class="quick-cards">
<div class="quick-card" onclick="quickAsk('香港九龙区120平米住宅公寓市值800万港元请估算XTZH价值和质押要求')"><div class="card-icon">🏠</div><div class="card-title">住宅不动产估值</div><div class="card-desc">香港/新加坡/阿联酋住宅资产三重价值换算</div></div>
<div class="quick-card" onclick="quickAsk('我有一首原创音乐的版权每年版税收入约50万元剩余保护期70年请用收益法估值')"><div class="card-icon">🎵</div><div class="card-title">音乐版权估值</div><div class="card-desc">版税折现法,剩余保护期,授权范围分析</div></div>
<div class="quick-card" onclick="quickAsk('我有一项发明专利技术领域是新能源电池每年许可费收入约200万美元请估值')"><div class="card-icon">🔬</div><div class="card-title">发明专利估值</div><div class="card-desc">许可费节省法/超额收益法,技术替代风险</div></div>
<div class="quick-card" onclick="quickAsk('某知名艺人的肖像权和商业形象权年商业化收入约1000万元请估值')"><div class="card-icon">🌟</div><div class="card-title">肖像权/IP估值</div><div class="card-desc">品牌溢价法,商业化收益折现,知名度评估</div></div>
<div class="quick-card" onclick="quickAsk('新加坡CBD商业写字楼1200平米年租金收入180万SGD请用收益法估值并换算XTZH')"><div class="card-icon">🏢</div><div class="card-title">商业地产收益法</div><div class="card-desc">收益资本化法,新加坡辖区特定参数</div></div>
<div class="quick-card" onclick="quickAsk('我有一个注册商标覆盖全球30个国家年品牌授权收入约500万美元请估值')"><div class="card-icon">™️</div><div class="card-title">商标权估值</div><div class="card-desc">品牌收益折现,注册地域,市场溢价分析</div></div>
<div class="quick-card" onclick="quickAsk('我持有某非上市公司30%股权公司年净利润约2000万元请用市场乘数法估值')"><div class="card-icon">📊</div><div class="card-title">非上市股权估值</div><div class="card-desc">市场乘数法/DCF流动性折价控制权溢价</div></div>
<div class="quick-card" onclick="quickAsk('我有一批碳排放权VCS标准核证共100万吨CO2当量请估值并说明NAC上链流程')"><div class="card-icon">🌱</div><div class="card-title">碳排放权估值</div><div class="card-desc">VCS/Gold Standard核证市场价格法</div></div>
<div class="quick-card" onclick="quickAsk('一幅毕加索真迹油画上次拍卖价格为1200万美元请估值并分析艺术品上链流程')"><div class="card-icon">🎨</div><div class="card-title">艺术品估值</div><div class="card-desc">拍卖记录法,专家鉴定,艺术家声誉溢价</div></div>
<div class="quick-card" onclick="quickAsk('请解释XTZH的SDR锚定定价机制当前价格如何计算黄金储备覆盖率是多少')"><div class="card-icon">💰</div><div class="card-title">XTZH定价机制</div><div class="card-desc">SDR五货币篮子+黄金储备双重保障原理</div></div>
<div class="quick-card" onclick="quickAsk('请对比香港、新加坡、阿联酋三个辖区的不动产估值差异,包括资本化率和外籍购房规则')"><div class="card-icon">🌍</div><div class="card-title">辖区对比分析</div><div class="card-desc">Tier 1辖区不动产参数对比外籍规则</div></div>
<div class="quick-card" onclick="quickAsk('资产上链完整流程是什么从估值到TOKEN生成、权证发行、代币发行需要哪些步骤')"><div class="card-icon">⛓️</div><div class="card-title">一键上链流程</div><div class="card-desc">估值→合规→TOKEN→权证→代币发行</div></div>
</div>
</div>
`;
}
function clearMemory() {
state.conversationMemory = [];
newChat();
}
// ============================================================
// 历史记录
// ============================================================
function addToHistory(title) {
const item = { id: state.sessionId, title, time: Date.now() };
state.history.unshift(item);
if (state.history.length > 20) state.history = state.history.slice(0, 20);
localStorage.setItem('nac_valuation_history_v2', JSON.stringify(state.history));
renderHistory();
}
function renderHistory() {
const list = document.getElementById('historyList');
list.innerHTML = state.history.map(item => `
<div class="history-item ${item.id === state.sessionId ? 'active' : ''}">
💎 ${item.title}
<div style="font-size:9px;color:var(--text-muted);margin-top:2px">${new Date(item.time).toLocaleDateString('zh-CN')}</div>
</div>
`).join('');
}
// ============================================================
// 语言切换
// ============================================================
function switchLanguage(lang) {
CONFIG.lang = lang;
document.querySelectorAll('[data-i18n]').forEach(el => {
const key = el.getAttribute('data-i18n');
const t = I18N[lang] || I18N.zh;
if (t[key]) el.textContent = t[key];
});
const input = document.getElementById('chatInput');
const t = I18N[lang] || I18N.zh;
if (t.placeholder) input.placeholder = t.placeholder;
}
// ============================================================
// 键盘事件
// ============================================================
function handleKeyDown(e) {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
}
function autoResize(el) {
el.style.height = 'auto';
el.style.height = Math.min(el.scrollHeight, 140) + 'px';
}
// ============================================================
// 初始化
// ============================================================
document.addEventListener('DOMContentLoaded', () => {
fetchXTZHPrice();
setInterval(fetchXTZHPrice, 60000);
renderHistory();
setJurisdiction('HK');
setAssetCategory('real_estate');
document.getElementById('chatInput').focus();
});
</script>
</body>
</html>