doc-forge-remark/docs/原型v3-HTML/模板管理.html

792 lines
53 KiB
HTML
Raw Permalink 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>模板管理 · AI 文档模板生成系统</title>
<script src="https://unpkg.com/lucide@latest"></script>
<style>
:root{
--c-primary:#5b5bd6; --c-primary-hover:#4a4ac0; --c-primary-soft:#eeeefb;
--c-primary-2:#7c7cdb; --c-primary-dark:#3b3ba8;
--c-bg:#f5f6f8; --c-bg-card:#ffffff; --c-bg-soft:#f0f1f3; --c-bg-hover:#eaecef;
--c-border:#e0e2e6; --c-border-light:#eaecef;
--c-text:#1a1d24; --c-text-2:#5b626e; --c-text-3:#9aa1ad; --c-text-4:#c4c8cf;
--c-success:#1a8c4a; --c-warning:#d48a00; --c-danger:#d33; --c-info:#4a90d9;
--c-orange:#e68a00;
--font:-apple-system,"PingFang SC","Microsoft YaHei","Segoe UI",Roboto,sans-serif;
--fs-h1:22px; --fs-h2:17px; --fs-h3:15px; --fs-body:14px; --fs-sm:13px; --fs-xs:12px;
--fw-bold:600; --fw-medium:500;
--sp-1:4px; --sp-2:8px; --sp-3:12px; --sp-4:16px; --sp-5:24px; --sp-6:32px;
--radius:8px; --radius-sm:6px; --radius-lg:12px;
--shadow:0 1px 3px rgba(0,0,0,.06),0 1px 2px rgba(0,0,0,.04);
--shadow-md:0 4px 12px rgba(0,0,0,.08);
--shadow-lg:0 8px 24px rgba(0,0,0,.10);
}
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
body{font-family:var(--font);font-size:var(--fs-body);color:var(--c-text);background:var(--c-bg);line-height:1.6;-webkit-font-smoothing:antialiased;height:100vh;overflow:hidden}
a{color:var(--c-primary);text-decoration:none}
[data-lucide]{width:16px;height:16px;stroke-width:1.8;vertical-align:middle;color:inherit;flex-shrink:0}
.icon-sm{width:14px;height:14px}
.icon-lg{width:20px;height:20px}
/* === top bar === */
.app{display:flex;flex-direction:column;height:100vh}
.topbar{height:52px;background:var(--c-bg-card);border-bottom:1px solid var(--c-border);display:flex;align-items:center;padding:0 var(--sp-5);flex-shrink:0;gap:var(--sp-3)}
.topbar-logo{display:flex;align-items:center;gap:var(--sp-2);font-weight:var(--fw-bold);font-size:var(--fs-h3);color:var(--c-text);margin-right:var(--sp-4);flex-shrink:0}
.logo-icon{width:28px;height:28px;background:var(--c-primary);border-radius:var(--radius-sm);display:flex;align-items:center;justify-content:center;color:#fff;font-size:13px;font-weight:700}
.topbar-nav{display:flex;align-items:center;gap:var(--sp-1)}
.topbar-tab{padding:var(--sp-2) var(--sp-4);cursor:pointer;font-size:var(--fs-sm);color:var(--c-text-2);border-radius:var(--radius-sm);transition:all .12s;user-select:none;display:flex;align-items:center;gap:var(--sp-2);white-space:nowrap}
.topbar-tab:hover{background:var(--c-bg-soft);color:var(--c-text)}
.topbar-tab.active{background:var(--c-primary-soft);color:var(--c-primary);font-weight:var(--fw-medium)}
.topbar-right{margin-left:auto;display:flex;align-items:center;gap:var(--sp-3)}
.user-badge{display:flex;align-items:center;gap:var(--sp-2);padding:var(--sp-1) var(--sp-2) var(--sp-1) var(--sp-3);background:var(--c-bg-soft);border-radius:20px;cursor:pointer}
.user-avatar-sm{width:22px;height:22px;border-radius:50%;background:var(--c-primary-soft);color:var(--c-primary);display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:var(--fw-bold)}
/* === main layout === */
.main-area{flex:1;overflow:hidden;display:flex}
.screen{display:none;flex:1;overflow:hidden}
.screen.active{display:flex}
.scroll-area{flex:1;overflow-y:auto;padding:var(--sp-5) var(--sp-6)}
/* === breadcrumb === */
.breadcrumb{display:flex;align-items:center;gap:var(--sp-2);font-size:var(--fs-sm);color:var(--c-text-3);margin-bottom:var(--sp-4);flex-wrap:wrap}
.breadcrumb span,.breadcrumb a{color:var(--c-text-2)}
/* === card === */
.card{background:var(--c-bg-card);border-radius:var(--radius-lg);border:1px solid var(--c-border);overflow:hidden}
.card-header{display:flex;align-items:center;justify-content:space-between;padding:var(--sp-4) var(--sp-5);border-bottom:1px solid var(--c-border-light);flex-shrink:0}
.card-title{font-size:var(--fs-h3);font-weight:var(--fw-bold);display:flex;align-items:center;gap:var(--sp-2)}
.card-body{padding:var(--sp-5)}
/* === buttons === */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:8px 18px;border-radius:var(--radius);font-size:var(--fs-sm);font-weight:var(--fw-medium);cursor:pointer;border:1px solid transparent;transition:all .12s;line-height:1.4;white-space:nowrap}
.btn-primary{background:var(--c-primary);color:#fff;border-color:var(--c-primary)}
.btn-primary:hover{background:var(--c-primary-hover)}
.btn-outline{background:transparent;color:var(--c-text);border-color:var(--c-border)}
.btn-outline:hover{background:var(--c-bg-soft);border-color:var(--c-text-3)}
.btn-ghost{background:transparent;color:var(--c-text-2);border-color:transparent;padding:6px 10px}
.btn-ghost:hover{background:var(--c-bg-soft)}
.btn-success{background:var(--c-success);color:#fff}
.btn-success:hover{background:#157a3e}
.btn-sm{padding:5px 12px;font-size:var(--fs-xs)}
.btn-xs{padding:3px 8px;font-size:11px;border-radius:var(--radius-sm)}
/* === form === */
.form-group{margin-bottom:var(--sp-4)}
.form-label{display:block;font-size:var(--fs-sm);font-weight:var(--fw-medium);color:var(--c-text-2);margin-bottom:var(--sp-1)}
.form-input,.form-select{width:100%;padding:8px 12px;border:1px solid var(--c-border);border-radius:var(--radius);font-size:var(--fs-body);font-family:var(--font);color:var(--c-text);background:var(--c-bg-card);outline:none;transition:border-color .15s}
.form-input:focus,.form-select:focus{border-color:var(--c-primary);box-shadow:0 0 0 2px var(--c-primary-soft)}
.form-textarea{width:100%;min-height:80px;padding:8px 12px;border:1px solid var(--c-border);border-radius:var(--radius);font-size:var(--fs-body);font-family:var(--font);resize:vertical;outline:none}
.form-textarea:focus{border-color:var(--c-primary);box-shadow:0 0 0 2px var(--c-primary-soft)}
/* === template list === */
.template-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(320px,1fr));gap:var(--sp-4)}
.tpl-card{border:1px solid var(--c-border-light);border-radius:var(--radius-lg);background:var(--c-bg-card);padding:var(--sp-5);cursor:pointer;transition:all .12s}
.tpl-card:hover{box-shadow:var(--shadow-md);border-color:var(--c-primary-2)}
.tpl-card .tpl-name{font-size:var(--fs-body);font-weight:var(--fw-bold);margin-bottom:var(--sp-1);display:flex;align-items:center;gap:var(--sp-2)}
.tpl-card .tpl-desc{font-size:var(--fs-xs);color:var(--c-text-3);margin-bottom:var(--sp-3)}
.tpl-card .tpl-meta{display:flex;align-items:center;gap:var(--sp-3);font-size:var(--fs-xs);color:var(--c-text-3)}
.tpl-card .tpl-actions{margin-top:var(--sp-3);display:flex;gap:var(--sp-2)}
.tpl-status{font-size:10px;padding:2px 10px;border-radius:10px;display:inline-block}
.tpl-status.ready{background:#e8f5e9;color:var(--c-success)}
.tpl-status.editing{background:#fff3e0;color:var(--c-warning)}
/.tpl-status.empty{background:var(--c-bg-soft);color:var(--c-text-3)}
/* === template editor LAYOUT (left+right split) === */
.tpl-editor{display:flex;flex:1;overflow:hidden}
.tpl-left{width:380px;flex-shrink:0;border-right:1px solid var(--c-border);background:var(--c-bg-card);display:flex;flex-direction:column;overflow:hidden}
.tpl-left-header{display:flex;align-items:center;justify-content:space-between;padding:var(--sp-3) var(--sp-4);border-bottom:1px solid var(--c-border-light);flex-shrink:0}
.tpl-left-scroll{flex:1;overflow-y:auto;padding:var(--sp-4)}
.tpl-right{flex:1;display:flex;flex-direction:column;overflow:hidden;background:var(--c-bg)}
.tpl-right-toolbar{display:flex;align-items:center;padding:var(--sp-2) var(--sp-4);border-bottom:1px solid var(--c-border);background:var(--c-bg-card);flex-shrink:0;gap:var(--sp-2)}
.tpl-right-body{flex:1;overflow-y:auto;padding:var(--sp-5) var(--sp-6);display:flex;justify-content:center}
/* === mode toggle === */
.mode-group{display:flex;border:1px solid var(--c-border);border-radius:var(--radius);overflow:hidden;flex-shrink:0}
.mode-btn{padding:4px 14px;cursor:pointer;font-size:var(--fs-xs);color:var(--c-text-2);background:var(--c-bg-card);border:none;border-right:1px solid var(--c-border);transition:all .12s}
.mode-btn:last-child{border-right:none}
.mode-btn.active{background:var(--c-primary-soft);color:var(--c-primary);font-weight:var(--fw-medium)}
.mode-btn:hover:not(.active){background:var(--c-bg-soft)}
/* === paragraph markers === */
.doc-edit-page{width:794px;background:#fff;box-shadow:0 2px 12px rgba(0,0,0,.08);padding:80px 72px 120px;min-height:500px;height:auto;font-size:14px;line-height:1.8;position:relative}
.doc-edit-page .doc-title{font-size:20px;font-weight:var(--fw-bold);text-align:center;margin-bottom:var(--sp-5)}
.doc-edit-page .doc-subtitle{text-align:center;font-size:var(--fs-sm);color:var(--c-text-2);margin-bottom:var(--sp-5)}
.doc-edit-page .sec-title{font-size:16px;font-weight:var(--fw-bold);margin-top:var(--sp-5);margin-bottom:var(--sp-3);padding-bottom:var(--sp-1);border-bottom:2px solid var(--c-text)}
.doc-edit-page .para-block{position:relative;padding:var(--sp-2);margin:var(--sp-1) 0;border-radius:var(--radius-sm);border:2px solid transparent;cursor:pointer;transition:all .12s}
.doc-edit-page .para-block:hover{border-color:var(--c-border)}
.doc-edit-page .para-block.selected{border-color:var(--c-primary);background:var(--c-primary-soft)}
.doc-edit-page .para-block .para-tag{position:absolute;right:4px;top:4px;font-size:9px;padding:1px 6px;border-radius:4px;display:none}
.doc-edit-page .para-block:hover .para-tag,.doc-edit-page .para-block.selected .para-tag{display:block}
.doc-edit-page .para-block .para-tag.ai{background:var(--c-primary-soft);color:var(--c-primary)}
.doc-edit-page .para-block .para-tag.manual{background:#fff3e0;color:var(--c-orange)}
.doc-edit-page table{width:100%;border-collapse:collapse;margin:var(--sp-3) 0;font-size:var(--fs-sm)}
.doc-edit-page th,.doc-edit-page td{padding:6px 10px;border:1px solid #ddd;text-align:left}
.doc-edit-page th{background:var(--c-bg-soft);font-weight:var(--fw-medium);font-size:var(--fs-xs)}
/* === edit mode rich editor === */
.edit-toolbar{display:flex;align-items:center;gap:var(--sp-1);padding:var(--sp-2) var(--sp-3);border:1px solid var(--c-border);border-bottom:none;border-radius:var(--radius) var(--radius) 0 0;background:var(--c-bg-card);flex-wrap:wrap;flex-shrink:0}
.edit-toolbar .tb-divider{width:1px;height:18px;background:var(--c-border);margin:0 var(--sp-1)}
.edit-toolbar .tb-btn{width:28px;height:28px;display:flex;align-items:center;justify-content:center;border-radius:var(--radius-sm);cursor:pointer;color:var(--c-text-2);font-size:var(--fs-sm)}
.edit-toolbar .tb-btn:hover{background:var(--c-bg-soft);color:var(--c-text)}
.edit-toolbar .tb-btn.active{background:var(--c-primary-soft);color:var(--c-primary)}
.edit-body{border:1px solid var(--c-border);border-radius:0 0 var(--radius) var(--radius);padding:var(--sp-5) var(--sp-6);background:var(--c-bg-card);min-height:500px;outline:none;font-size:14px;line-height:1.8;color:var(--c-text);width:794px}
.edit-body:focus{border-color:var(--c-primary)}
.edit-body .doc-title{font-size:20px;font-weight:var(--fw-bold);text-align:center;margin-bottom:var(--sp-5)}
.edit-body .doc-subtitle{text-align:center;font-size:var(--fs-sm);color:var(--c-text-2);margin-bottom:var(--sp-5)}
.edit-body .sec-title{font-size:16px;font-weight:var(--fw-bold);margin-top:var(--sp-5);margin-bottom:var(--sp-3);padding-bottom:var(--sp-1);border-bottom:2px solid var(--c-text)}
.edit-body table{width:100%;border-collapse:collapse;margin:var(--sp-3) 0;font-size:var(--fs-sm)}
.edit-body th,.edit-body td{padding:6px 10px;border:1px solid #ddd;text-align:left}
.edit-body th{background:var(--c-bg-soft);font-weight:var(--fw-medium);font-size:var(--fs-xs)}
/* === left panel config === */
.config-section{margin-bottom:var(--sp-5)}
.config-section-title{font-size:var(--fs-sm);font-weight:var(--fw-bold);color:var(--c-text);margin-bottom:var(--sp-3);padding-bottom:var(--sp-2);border-bottom:1px solid var(--c-border-light)}
.config-field{margin-bottom:var(--sp-3)}
.config-field .cf-label{font-size:var(--fs-xs);color:var(--c-text-3);margin-bottom:var(--sp-1)}
.config-field .cf-value{font-size:var(--fs-sm);color:var(--c-text)}
.cfg-toggle{display:flex;align-items:center;gap:var(--sp-2);cursor:pointer;user-select:none;padding:var(--sp-1) 0}
.toggle-switch{width:32px;height:18px;border-radius:9px;background:var(--c-border);position:relative;cursor:pointer;transition:background .2s;flex-shrink:0}
.toggle-switch.on{background:var(--c-primary)}
.toggle-switch::after{content:'';width:14px;height:14px;border-radius:50%;background:#fff;position:absolute;top:2px;left:2px;transition:transform .2s;box-shadow:0 1px 2px rgba(0,0,0,.15)}
.toggle-switch.on::after{transform:translateX(14px)}
.cfg-select{padding:6px 10px;border:1px solid var(--c-border);border-radius:var(--radius-sm);font-size:var(--fs-sm);font-family:var(--font);color:var(--c-text);background:var(--c-bg-card);outline:none;width:100%}
.cfg-select:focus{border-color:var(--c-primary)}
/* === left panel paragraph list (edit mode) === */
.para-list-item{display:flex;align-items:center;gap:var(--sp-2);padding:var(--sp-2) var(--sp-3);border-radius:var(--radius-sm);cursor:pointer;font-size:var(--fs-sm);color:var(--c-text-2);transition:all .12s;margin-bottom:2px}
.para-list-item:hover{background:var(--c-bg-soft)}
.para-list-item.active{background:var(--c-primary-soft);color:var(--c-primary);font-weight:var(--fw-medium)}
.para-list-item .pli-index{width:20px;height:20px;border-radius:50%;background:var(--c-bg-soft);display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:var(--fw-bold);color:var(--c-text-3);flex-shrink:0}
.para-list-item.active .pli-index{background:var(--c-primary);color:#fff}
.para-list-item .pli-badge{font-size:9px;padding:0 6px;border-radius:6px;margin-left:auto;flex-shrink:0}
.para-list-item .pli-badge.ai{background:var(--c-primary-soft);color:var(--c-primary)}
.para-list-item .pli-badge.manual{background:#fff3e0;color:var(--c-orange)}
/* === gen progress & hints === */
.gen-progress{background:var(--c-bg-card);border:1px solid var(--c-border);border-radius:var(--radius);padding:var(--sp-4);margin-bottom:var(--sp-4);display:flex;align-items:center;gap:var(--sp-4)}
.spinner{width:20px;height:20px;border:2px solid var(--c-border);border-top-color:var(--c-primary);border-radius:50%;animation:spin .8s linear infinite;flex-shrink:0}
@keyframes spin{to{transform:rotate(360deg)}}
/* === upload zone === */
.upload-zone{border:2px dashed var(--c-border);border-radius:var(--radius-lg);padding:var(--sp-6) var(--sp-4);text-align:center;cursor:pointer;transition:all .15s;background:var(--c-bg-card)}
.upload-zone:hover{border-color:var(--c-primary);background:var(--c-primary-soft)}
.upload-icon{width:48px;height:48px;background:var(--c-bg-soft);border-radius:50%;display:flex;align-items:center;justify-content:center;margin:0 auto var(--sp-3);color:var(--c-text-3)}
.uploaded-file{display:flex;align-items:center;gap:var(--sp-3);padding:var(--sp-3) var(--sp-4);background:var(--c-bg-soft);border-radius:var(--radius);margin-top:var(--sp-3)}
.uploaded-file .file-icon.excel{background:#e8f5e9;color:#2e7d32}
/* === model card === */
.model-card{border:1px solid var(--c-border-light);border-radius:var(--radius);padding:var(--sp-4);margin-bottom:var(--sp-3);display:flex;align-items:center;gap:var(--sp-4)}
.model-card .mc-icon{width:40px;height:40px;border-radius:var(--radius);display:flex;align-items:center;justify-content:center;flex-shrink:0;font-size:16px;font-weight:var(--fw-bold);color:#fff}
.model-card .mc-info{flex:1;min-width:0}
.model-card .mc-name{font-size:var(--fs-sm);font-weight:var(--fw-medium)}
.model-card .mc-provider{font-size:var(--fs-xs);color:var(--c-text-3)}
.model-card .mc-status{font-size:var(--fs-xs)}
.model-card .mc-status.on{color:var(--c-success)}
.model-card .mc-status.off{color:var(--c-text-3)}
/* === test result modal === */
.modal-overlay{display:none;position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,.4);z-index:1000;align-items:center;justify-content:center}
.modal-overlay.open{display:flex}
.modal{background:var(--c-bg-card);border-radius:var(--radius-lg);width:640px;max-width:90vw;max-height:80vh;overflow-y:auto;box-shadow:var(--shadow-lg)}
.modal-header{display:flex;align-items:center;justify-content:space-between;padding:var(--sp-4) var(--sp-5);border-bottom:1px solid var(--c-border);font-size:var(--fs-h3);font-weight:var(--fw-bold)}
.modal-header .close-btn{cursor:pointer;color:var(--c-text-3);padding:4px;border-radius:var(--radius-sm)}
.modal-header .close-btn:hover{background:var(--c-bg-soft);color:var(--c-text)}
.modal-body{padding:var(--sp-5)}
.modal-footer{display:flex;justify-content:flex-end;gap:var(--sp-2);padding:var(--sp-4) var(--sp-5);border-top:1px solid var(--c-border-light)}
/* === typography === */
.text-xs{font-size:var(--fs-xs)}
.text-sm{font-size:var(--fs-sm)}
.text-muted{color:var(--c-text-3)}
.flex{display:flex}
.flex-1{flex:1}
.gap-2{gap:var(--sp-2)}
.gap-3{gap:var(--sp-3)}
.items-center{align-items:center}
.justify-between{justify-content:space-between}
.mb-3{margin-bottom:var(--sp-3)}
.mb-4{margin-bottom:var(--sp-4)}
.mt-3{margin-top:var(--sp-3)}
.p-3{padding:var(--sp-3)}
.p-4{padding:var(--sp-4)}
</style>
</head>
<body>
<div class="app">
<header class="topbar">
<div class="topbar-logo"><div class="logo-icon">A</div><span>AI 文档模板</span></div>
<a class="topbar-tab active" href="模板管理.html"><i data-lucide="layout-dashboard" class="icon-sm"></i> 模板管理</a>
<a class="topbar-tab" href="模型管理.html"><i data-lucide="cpu" class="icon-sm"></i> 模型管理</a>
<a class="topbar-tab" href="执行生成.html"><i data-lucide="zap" class="icon-sm"></i> 执行生成</a>
<a class="topbar-tab" href="生成记录.html"><i data-lucide="clock" class="icon-sm"></i> 生成记录</a>
<div class="topbar-right">
<span class="text-xs text-muted">v3.0</span>
<div class="user-badge"><span class="text-xs" style="font-weight:var(--fw-medium)">周文涛</span><div class="user-avatar-sm"></div></div>
</div>
</header>
<div class="main-area">
<div class="screen active" id="screen-template">
<!-- Template list -->
<div id="tplList" style="display:flex;flex-direction:column;flex:1;overflow:hidden">
<div class="scroll-area">
<div class="breadcrumb"><i data-lucide="home" class="icon-sm"></i> 模板管理 / <span>所有模板</span></div>
<div class="flex items-center justify-between mb-4">
<span class="text-sm text-muted">共 3 个模板</span>
<button class="btn btn-primary btn-sm" onclick="startNewTemplate()"><i data-lucide="plus"></i>新建模板</button>
</div>
<div class="template-grid">
<div class="tpl-card">
<div class="tpl-name"><i data-lucide="file-text" style="color:var(--c-primary)"></i> 季度经济活动分析报告 <span class="tpl-status ready" style="margin-left:auto">已编辑</span></div>
<div class="tpl-desc">含经营指标、成本分析、问题总结、工作措施等 8 个段落。3 个段落需上传参考文件。</div>
<div class="tpl-meta"><span><i data-lucide="calendar" class="icon-sm"></i> 最后编辑2026-07-02</span><span><i data-lucide="list" class="icon-sm"></i> 段落8</span></div>
<div class="tpl-actions">
<button class="btn btn-outline btn-xs" onclick="editTemplate('季度经济活动分析报告')"><i data-lucide="edit-3" class="icon-sm"></i>编辑模板</button>
<button class="btn btn-primary btn-xs" onclick="goGenerate('季度经济活动分析报告')"><i data-lucide="zap" class="icon-sm"></i>前去生成</button>
</div>
</div>
<div class="tpl-card">
<div class="tpl-name"><i data-lucide="file-text" style="color:var(--c-warning)"></i> 年度工作总结模板 <span class="tpl-status editing" style="margin-left:auto">编辑中</span></div>
<div class="tpl-desc">含工作概况、主要成绩、存在问题、明年计划等 6 个段落。</div>
<div class="tpl-meta"><span><i data-lucide="calendar" class="icon-sm"></i> 最后编辑2026-06-28</span><span><i data-lucide="list" class="icon-sm"></i> 段落6</span></div>
<div class="tpl-actions">
<button class="btn btn-outline btn-xs" onclick="editTemplate('年度工作总结模板')"><i data-lucide="edit-3" class="icon-sm"></i>继续编辑</button>
<button class="btn btn-outline btn-xs"><i data-lucide="zap" class="icon-sm"></i>前去生成</button>
</div>
</div>
<div class="tpl-card" onclick="startNewTemplate()">
<div class="tpl-name"><i data-lucide="file-plus" style="color:var(--c-text-3)"></i> 新建模板 <span class="tpl-status empty" style="margin-left:auto">未开始</span></div>
<div class="tpl-desc">点击创建新模板,上传 Word 文件,配置段落。</div>
<div class="tpl-meta"><span></span><span>段落0</span></div>
<div class="tpl-actions"><button class="btn btn-primary btn-xs" onclick="event.stopPropagation();startNewTemplate()"><i data-lucide="plus"></i>新建模板</button></div>
</div>
</div>
</div>
</div>
<!-- Template editor (3-column) -->
<div id="tplEditor" style="display:none;flex:1;overflow:hidden;flex-direction:column">
<div style="display:flex;align-items:center;padding:var(--sp-2) var(--sp-4);border-bottom:1px solid var(--c-border);background:var(--c-bg-card);flex-shrink:0;gap:var(--sp-3)">
<a href="#" onclick="backToTplList()" style="font-size:var(--fs-sm);color:var(--c-primary);display:flex;align-items:center;gap:var(--sp-1)"><i data-lucide="arrow-left" class="icon-sm"></i>返回</a>
<span style="color:var(--c-text-4)">|</span>
<span style="font-size:var(--fs-sm);font-weight:var(--fw-medium)" id="editorTplName">季度经济活动分析报告</span>
<span style="flex:1"></span>
<button class="btn btn-primary btn-sm" onclick="alert('模板配置已保存!')"><i data-lucide="save"></i>保存模板</button>
</div>
<div style="display:flex;flex:1;overflow:hidden">
<!-- LEFT: 段落列表 -->
<div style="width:220px;flex-shrink:0;border-right:1px solid var(--c-border);background:var(--c-bg-card);display:flex;flex-direction:column;overflow:hidden">
<div style="padding:var(--sp-3) var(--sp-4);border-bottom:1px solid var(--c-border-light);font-size:var(--fs-sm);font-weight:var(--fw-medium);flex-shrink:0">段落列表 <span class="text-xs text-muted" style="font-weight:400">(点击定位)</span></div>
<div style="flex:1;overflow-y:auto;padding:var(--sp-2)" id="leftParaList"></div>
</div>
<!-- CENTER: 文档预览/编辑 -->
<div style="flex:1;display:flex;flex-direction:column;overflow:hidden;background:var(--c-bg)">
<div style="display:flex;align-items:center;padding:var(--sp-2) var(--sp-4);border-bottom:1px solid var(--c-border);background:var(--c-bg-card);flex-shrink:0;gap:var(--sp-1)">
<span class="tb-btn active" title="加粗"><b>B</b></span><span class="tb-btn" title="斜体"><i>I</i></span><span class="tb-btn" title="下划线"><u>U</u></span>
<span class="tb-divider"></span>
<span class="tb-btn" title="标题"><i data-lucide="heading" class="icon-sm"></i></span>
<span class="tb-btn" title="列表"><i data-lucide="list" class="icon-sm"></i></span>
<span class="tb-btn" title="表格"><i data-lucide="table" class="icon-sm"></i></span>
<span class="tb-divider"></span>
<span class="tb-btn" title="左对齐"><i data-lucide="align-left" class="icon-sm"></i></span>
<span class="tb-btn" title="居中"><i data-lucide="align-center" class="icon-sm"></i></span>
<span class="tb-btn" title="右对齐"><i data-lucide="align-right" class="icon-sm"></i></span>
<span style="margin-left:auto;font-size:var(--fs-xs);color:var(--c-text-3)" id="centerHint">点击左侧段落或文档中的段落块查看配置</span>
</div>
<div style="flex:1;overflow-y:auto;padding:var(--sp-5);display:flex;justify-content:center;align-items:flex-start" id="centerScroll">
<div class="doc-edit-page" id="docPreviewPage">
<div class="doc-title">2025年第一季度经济活动分析报告</div>
<div class="doc-subtitle">某某集团有限公司</div>
<div class="para-block" data-para="1" id="paraBlock1" onclick="selectParaFromDoc(1)">
<span class="para-tag manual">人工编辑</span>
<div class="sec-title" style="margin-top:0">一、主要经营指标完成情况</div>
<p>本季度公司经营整体保持平稳各项主要经营指标完成情况良好。营业收入完成12.35亿元同比上升8.7%利润总额完成1.25亿元同比上升5.3%净利润完成1.02亿元同比上升4.8%。主要原因是电量增长、成本控制有效以及市场销售情况改善。</p>
<p>从各业务板块来看主营业务收入同比增长9.2%得益于新增客户拓展和存量客户深度挖掘非主营业务收入同比增长4.1%,略低于预期。</p>
</div>
<div class="para-block" data-para="2" id="paraBlock2" onclick="selectParaFromDoc(2)">
<span class="para-tag ai">AI 生成 · 表格</span>
<div class="sec-title" style="margin-top:0">主要经营指标完成情况表</div>
<table><thead><tr><th>指标名称</th><th>本期完成值</th><th>同比增减</th><th>完成率</th><th>责任部门</th></tr></thead>
<tbody>
<tr><td>营业收入(亿元)</td><td>12.35</td><td>+8.7%</td><td>102.3%</td><td>营销部</td></tr>
<tr><td>利润总额(亿元)</td><td>1.25</td><td>+5.3%</td><td>100.8%</td><td>财务部</td></tr>
<tr><td>发电量(亿度)</td><td>45.67</td><td>+6.2%</td><td>101.2%</td><td>运行部</td></tr>
</tbody>
</table>
</div>
<div class="para-block" data-para="3" id="paraBlock3" onclick="selectParaFromDoc(3)">
<span class="para-tag ai">AI 生成</span>
<div class="sec-title" style="margin-top:0">二、成本费用分析</div>
<p>本季度营业总成本为9.87亿元同比上升6.2%。其中燃料成本4.23亿元占比42.9%人工成本1.86亿元占比18.8%维修费用0.92亿元占比9.3%。燃料成本同比上升7.8%主要受煤炭市场价格波动影响。通过优化燃料采购策略和库存管理有效控制了成本涨幅。人工成本同比上升4.5%主要为年度调薪及新增岗位所致。维修费用同比上升3.2%,主要因部分设备进入大修周期。综合来看,成本增速低于收入增速,成本管控成效显著。</p>
</div>
<div class="para-block" data-para="4" id="paraBlock4" onclick="selectParaFromDoc(4)">
<span class="para-tag ai">AI 生成</span>
<div class="sec-title" style="margin-top:0">三、存在的主要问题</div>
<p>一是部分指标完成质量不高存在一定的虚增因素需加强数据核验二是成本控制压力仍然较大燃料成本同比上升明显对利润空间形成挤压三是设备老化问题突出非计划停运次数较去年同期增加2次影响发电效率四是安全风险管控仍需加强现场违章作业问题时有发生需进一步强化安全教育和监管力度。</p>
</div>
<div class="para-block" data-para="5" id="paraBlock5" onclick="selectParaFromDoc(5)">
<span class="para-tag ai">AI 生成</span>
<div class="sec-title" style="margin-top:0">四、下一步工作措施</div>
<p>一是加强成本管控,深入推进降本增效专项行动,确保完成年度利润目标;二是强化设备管理,加大设备检修维护力度,提高设备可靠性,减少非计划停运;三是严格落实安全生产责任制,深入开展安全隐患排查治理,坚决杜绝较大及以上安全事故;四是加快数字化转型,推进智慧运营平台建设,提升管理效率。</p>
</div>
<div class="para-block" data-para="6" id="paraBlock6" onclick="selectParaFromDoc(6)">
<span class="para-tag manual">人工编辑</span>
<p style="margin-top:var(--sp-2)">封面信息:某某集团有限公司 · 2025年4月15日</p>
</div>
</div>
</div>
</div>
<!-- RIGHT: 段落配置 -->
<div style="width:380px;flex-shrink:0;border-left:1px solid var(--c-border);background:var(--c-bg-card);display:flex;flex-direction:column;overflow:hidden">
<div style="padding:var(--sp-3) var(--sp-4);border-bottom:1px solid var(--c-border-light);font-size:var(--fs-sm);font-weight:var(--fw-medium);flex-shrink:0">
<span id="rightPanelTitle">段落配置</span>
<span class="text-xs text-muted" style="font-weight:400;margin-left:var(--sp-2)" id="rightParaName"></span>
</div>
<div style="flex:1;overflow-y:auto;padding:var(--sp-4)" id="rightPanelBody">
<!-- Populated by JS -->
</div>
</div>
</div>
</div>
</div>
<!-- Test result modal -->
<div class="modal-overlay" id="testModal">
<div class="modal" style="width:680px">
<div class="modal-header">
<span><i data-lucide="zap" class="icon-sm" style="color:var(--c-primary)"></i> 段落测试</span>
<span class="close-btn" onclick="closeTestModal()"><i data-lucide="x" class="icon-sm"></i></span>
</div>
<div class="modal-body" id="testModalBody">
<!-- Step 1: Upload file -->
<div id="testStep1">
<div class="config-section-title" style="border:none;padding:0;margin-bottom:var(--sp-4)">
<span style="background:var(--c-primary);color:#fff;width:22px;height:22px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;font-size:12px;font-weight:700;margin-right:var(--sp-2)">1</span>
上传附件
</div>
<div style="background:var(--c-bg-soft);border-radius:var(--radius);padding:var(--sp-4);margin-bottom:var(--sp-3)">
<div class="flex items-center gap-2 mb-3">
<i data-lucide="file-text" class="icon-sm" style="color:var(--c-primary)"></i>
<span style="font-size:var(--fs-sm);font-weight:var(--fw-medium)">一、主要经营指标完成情况</span>
</div>
<div style="border:1px dashed var(--c-border);border-radius:var(--radius);padding:var(--sp-4);text-align:center;cursor:pointer" onclick="simulateUpload()">
<i data-lucide="upload" style="width:32px;height:32px;color:var(--c-text-3);margin-bottom:var(--sp-2)"></i>
<div style="font-size:var(--fs-sm);color:var(--c-text-2)">点击上传参考文件(财务数据报表.xlsx</div>
</div>
</div>
<div id="testUploaded" style="display:none">
<div class="uploaded-file" style="margin-bottom:var(--sp-3);margin-top:0">
<div class="file-icon excel" style="width:36px;height:36px"><i data-lucide="file-spreadsheet" class="icon-lg"></i></div>
<div class="file-info"><div class="file-name">财务数据报表.xlsx</div><div class="file-size">128 KB</div></div>
<span class="text-xs" style="color:var(--c-success)"><i data-lucide="check-circle" class="icon-sm"></i> 已上传</span>
</div>
<button class="btn btn-primary btn-block" style="width:100%;justify-content:center" onclick="startTestStep2()">
<i data-lucide="play" class="icon-sm"></i> 开始测试
</button>
</div>
</div>
<!-- Step 2: Processing -->
<div id="testStep2" style="display:none">
<div style="background:var(--c-bg-card);border:1px solid var(--c-border);border-radius:var(--radius);padding:var(--sp-4)">
<div class="flex items-center gap-3 mb-4">
<div class="spinner"></div>
<div style="flex:1;font-size:var(--fs-sm);color:var(--c-text-2)" id="testStepText">正在解析文件...</div>
</div>
<div style="display:flex;flex-direction:column;gap:var(--sp-2)">
<div class="flex items-center gap-2">
<span id="tsk1" style="color:var(--c-text-4);font-size:var(--fs-xs)"><i data-lucide="circle" class="icon-sm"></i></span>
<span style="font-size:var(--fs-sm);color:var(--c-text-3)">文件解析</span>
</div>
<div class="flex items-center gap-2">
<span id="tsk2" style="color:var(--c-text-4);font-size:var(--fs-xs)"><i data-lucide="circle" class="icon-sm"></i></span>
<span style="font-size:var(--fs-sm);color:var(--c-text-3)">请求 AI 大模型Claude 3.5 Sonnet</span>
</div>
<div class="flex items-center gap-2">
<span id="tsk3" style="color:var(--c-text-4);font-size:var(--fs-xs)"><i data-lucide="circle" class="icon-sm"></i></span>
<span style="font-size:var(--fs-sm);color:var(--c-text-3)">生成结果填充</span>
</div>
</div>
</div>
</div>
<!-- Step 3: Result -->
<div id="testStep3" style="display:none">
<div class="flex items-center gap-3 mb-4">
<span style="background:var(--c-success);color:#fff;padding:2px 10px;border-radius:4px;font-size:var(--fs-xs);font-weight:var(--fw-medium)">测试完成</span>
<span class="text-xs text-muted">使用模型: Claude 3.5 Sonnet</span>
<span class="text-xs" style="color:var(--c-success)">总耗时 4.1s</span>
</div>
<div style="display:flex;gap:var(--sp-2);margin-bottom:var(--sp-3)">
<div class="flex items-center gap-1 text-xs text-muted"><i data-lucide="check-circle" class="icon-sm" style="color:var(--c-success)"></i> 文件解析完成</div>
<div class="flex items-center gap-1 text-xs text-muted"><i data-lucide="check-circle" class="icon-sm" style="color:var(--c-success)"></i> AI 响应成功</div>
</div>
<div style="background:#f0f0ff;border-radius:var(--radius);padding:var(--sp-4);border-left:3px solid var(--c-primary);font-size:var(--fs-sm);line-height:1.8">
<div class="text-xs" style="color:var(--c-primary);font-weight:var(--fw-medium);margin-bottom:var(--sp-2)"><i data-lucide="sparkles" class="icon-sm"></i> AI 生成结果</div>
<p>本季度公司经营整体保持平稳,各项主要经营指标完成情况良好。营业收入完成<strong>12.35亿元</strong>,同比上升<strong>8.7%</strong>;利润总额完成<strong>1.25亿元</strong>,同比上升<strong>5.3%</strong>;净利润完成<strong>1.02亿元</strong>,同比上升<strong>4.8%</strong>。主要原因是电量增长、成本控制有效以及市场销售情况改善。</p>
<p style="margin-top:var(--sp-2)">从各业务板块来看主营业务收入同比增长9.2%得益于新增客户拓展和存量客户深度挖掘非主营业务收入同比增长4.1%略低于预期。成本端燃料成本同比上升6.5%,主要受煤价波动影响,但通过优化采购策略,涨幅控制在可接受范围内。</p>
</div>
</div>
</div>
<div class="modal-footer" id="testModalFooter" style="display:none">
<button class="btn btn-ghost btn-sm" onclick="closeTestModal()">关闭</button>
<button class="btn btn-primary btn-sm" onclick="alert('已确认该段落配置,保存后将影响后续正式生成')">确认配置</button>
</div>
</div>
</div>
<script>
lucide.createIcons();
// === Screen switch ===
function switchScreen(el, id) {
// If called with a single string argument, treat it as screen id
if(!id && typeof el === 'string') {
document.querySelectorAll('.topbar-tab').forEach(t=>t.classList.remove('active'));
document.querySelectorAll('.screen').forEach(s=>s.classList.remove('active'));
document.getElementById(el).classList.add('active');
return;
}
document.querySelectorAll('.topbar-tab').forEach(t=>t.classList.remove('active'));
document.querySelectorAll('.screen').forEach(s=>s.classList.remove('active'));
el.classList.add('active');
document.getElementById(id).classList.add('active');
}
// === Template list nav ===
function backToTplList() {
document.getElementById('tplList').style.display = 'flex';
document.getElementById('tplEditor').style.display = 'none';
}
function editTemplate(name) {
document.getElementById('tplList').style.display = 'none';
document.getElementById('tplEditor').style.display = 'flex';
document.getElementById('editorTplName').textContent = name;
initParaList();
selectPara(1);
}
function startNewTemplate() {
document.getElementById('tplList').style.display = 'none';
document.getElementById('tplEditor').style.display = 'flex';
document.getElementById('editorTplName').textContent = '新建模板';
alert('模拟:上传 Word 模板弹窗');
initParaList();
selectPara(1);
}
// === 3-column editor functions ===
const paraData = [
{id:1, name:'标题2025年Q1报告', badge:'manual', label:'人工编辑'},
{id:2, name:'一、主要经营指标完成情况', badge:'ai', label:'AI 生成'},
{id:3, name:'主要经营指标完成情况表', badge:'ai', label:'AI 生成'},
{id:4, name:'二、成本费用分析', badge:'ai', label:'AI 生成'},
{id:5, name:'三、存在的主要问题', badge:'ai', label:'AI 生成'},
{id:6, name:'四、下一步工作措施', badge:'ai', label:'AI 生成'},
{id:7, name:'封面信息', badge:'manual', label:'人工编辑'},
{id:8, name:'附图:各季度营收趋势图', badge:'ai', label:'AI 生成'},
];
const paraConfigs = {
1: {name:'一、主要经营指标完成情况', mode:'AI 生成', model:'Claude 3.5 Sonnet', prompt:'请根据上传的财务数据,生成经营指标完成情况章节,要求包括营业收入、利润总额等核心指标的完成值、同比变化及原因分析。', needPrompt:true, needFile:true, format:'正式报告段落', note:'需上传财务部提供的季度财务数据报表Excel格式含各指标完成值'},
2: {name:'主要经营指标完成情况表', mode:'AI 生成', model:'GPT-4o', prompt:'根据上传的各部门数据,生成指标完成情况表,包含指标名、完成值、同比、完成率、责任部门。', needPrompt:true, needFile:true, format:'表格形式', note:'需上传各部门经营数据汇总表Excel格式含指标明细'},
3: {name:'二、成本费用分析', mode:'AI 生成', model:'Claude 3.5 Sonnet', prompt:'根据财务数据生成成本费用分析内容,包括各项成本构成、同比变化及原因。', needPrompt:true, needFile:false, format:'正式报告段落', note:''},
4: {name:'三、存在的主要问题', mode:'AI 生成', model:'Claude 3.5 Sonnet', prompt:'基于经营数据和报告要求,分析当前存在的主要问题和风险。', needPrompt:true, needFile:false, format:'正式报告段落', note:''},
5: {name:'四、下一步工作措施', mode:'AI 生成', model:'Claude 3.5 Sonnet', prompt:'针对问题生成具体可执行的工作措施。', needPrompt:true, needFile:false, format:'正式报告段落', note:''},
6: {name:'封面信息', mode:'人工编辑', model:'—', prompt:'', needPrompt:false, needFile:false, format:'—', note:''},
};
function initParaList() {
let html = '';
paraData.forEach(p => {
html += '<div class="para-list-item" id="leftParaItem'+p.id+'" onclick="selectPara('+p.id+')">'
+ '<span class="pli-index">'+p.id+'</span>'
+ '<span style="flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">'+p.name+'</span>'
+ '<span class="pli-badge '+p.badge+'">'+p.label+'</span>'
+ '</div>';
});
document.getElementById('leftParaList').innerHTML = html;
}
function selectPara(id) {
// Update left list
document.querySelectorAll('.para-list-item').forEach(e => e.classList.remove('active'));
const leftItem = document.getElementById('leftParaItem'+id);
if(leftItem) leftItem.classList.add('active');
// Scroll center to paragraph
const block = document.getElementById('paraBlock'+id);
if(block) {
block.scrollIntoView({behavior:'smooth', block:'center'});
document.querySelectorAll('.para-block').forEach(b => b.classList.remove('selected'));
block.classList.add('selected');
}
// Update right panel
buildRightPanel(id);
}
function selectParaFromDoc(id) {
selectPara(id);
}
function buildRightPanel(id) {
const cfg = paraConfigs[id] || paraConfigs[1];
const isAi = cfg.mode === 'AI 生成';
document.getElementById('rightPanelTitle').textContent = '段落配置';
document.getElementById('rightParaName').textContent = '第 '+id+' 段 · '+(cfg.name||'');
const html = `
<div class="config-section">
<div class="config-section-title">段落信息</div>
<div class="config-field"><div class="cf-label">段落名称</div><div class="cf-value" style="font-weight:var(--fw-medium)">${cfg.name}</div></div>
<div class="config-field"><div class="cf-label">所处位置</div><div class="cf-value text-sm text-muted">第 ${id} 段</div></div>
</div>
<div class="config-section">
<div class="config-section-title">生成配置</div>
<div class="config-field">
<div class="cf-label">编辑方式</div>
<select class="cfg-select" onchange="onEditModeChange(this,${id})">
<option value="人工编辑" ${cfg.mode==='人工编辑'?'selected':''}>人工编辑</option>
<option value="AI 生成" ${cfg.mode==='AI 生成'?'selected':''}>AI 生成</option>
</select>
</div>
<div class="config-field" id="rmodelField${id}" style="${isAi?'':'display:none'}">
<div class="cf-label">生成模型</div>
<select class="cfg-select">
<option ${cfg.model==='Claude 3.5 Sonnet'?'selected':''}>Claude 3.5 Sonnet</option>
<option ${cfg.model==='GPT-4o'?'selected':''}>GPT-4o</option>
<option ${cfg.model==='DeepSeek-V3'?'selected':''}>DeepSeek-V3</option>
</select>
</div>
<div class="config-field" id="rformatField${id}" style="${isAi?'':'display:none'}">
<div class="cf-label">输出格式</div>
<select class="cfg-select">
<option ${cfg.format==='正式报告段落'?'selected':''}>正式报告段落</option>
<option ${cfg.format==='表格形式'?'selected':''}>表格形式</option>
<option ${cfg.format==='分条列出'?'selected':''}>分条列出</option>
<option ${cfg.format==='图表形式'?'selected':''}>图表形式</option>
</select>
</div>
<div class="config-field">
<div class="cf-label">是否需要提示词</div>
<div class="cfg-toggle" onclick="toggleSw(this)">
<div class="toggle-switch ${cfg.needPrompt?'on':''}"></div>
<span class="text-xs">${cfg.needPrompt?'是':'否'}</span>
</div>
</div>
<div class="config-field" id="rpromptField${id}" style="${cfg.needPrompt?'':'display:none'}">
<div class="cf-label">预设提示词 <span class="text-xs text-muted">(编辑模板时设置)</span></div>
<textarea class="form-textarea" style="min-height:80px;font-size:var(--fs-sm)">${cfg.prompt || '请在此输入提示词...'}</textarea>
<div class="text-xs text-muted mt-3">提示词在模板编辑时预设,生成时可确认或微调</div>
</div>
<div class="config-field">
<div class="cf-label">是否需要参考文件</div>
<div class="cfg-toggle" onclick="toggleSw(this)">
<div class="toggle-switch ${cfg.needFile?'on':''}"></div>
<span class="text-xs">${cfg.needFile?'是':'否'}</span>
</div>
</div>
<div class="config-field" id="rnoteField${id}" style="${cfg.needFile?'':'display:none'}">
<div class="cf-label">备注说明 <span class="text-xs text-muted">(执行生成时提示需上传什么文件)</span></div>
<textarea class="form-textarea" style="min-height:60px;font-size:var(--fs-sm)" placeholder="例如:需上传财务部提供的季度财务数据报表">${cfg.note || ''}</textarea>
</div>
</div>
${isAi ? `
<div id="rtestBtn${id}">
<button class="btn btn-primary btn-block" style="width:100%;justify-content:center" onclick="testParagraph(${id})">
<i data-lucide="zap" class="icon-sm"></i> 立即测试
</button>
<div class="text-xs text-muted" style="text-align:center;margin-top:var(--sp-2)">针对此段落单独测试 · 需上传附件 → 文件解析 → 请求AI → 填充预览</div>
</div>
` : `
<div style="background:var(--c-bg-soft);padding:var(--sp-3);border-radius:var(--radius);text-align:center">
<span class="text-xs text-muted">该段落为「人工编辑」,无需 AI 测试</span>
</div>
`}
`;
document.getElementById('rightPanelBody').innerHTML = html;
lucide.createIcons();
}
function onEditModeChange(sel, paraId) {
const isAi = sel.value === 'AI 生成';
document.getElementById('rmodelField'+paraId).style.display = isAi ? 'block' : 'none';
document.getElementById('rformatField'+paraId).style.display = isAi ? 'block' : 'none';
document.getElementById('rpromptField'+paraId).style.display = 'block';
const testBtn = document.getElementById('rtestBtn'+paraId);
if(testBtn) testBtn.style.display = isAi ? 'block' : 'none';
}
// === Toggle switch ===
function toggleSw(el) {
const sw = el.querySelector('.toggle-switch');
const txt = el.querySelector('.text-xs');
sw.classList.toggle('on');
if(txt) txt.textContent = sw.classList.contains('on') ? '是' : '否';
// Show/hide note field (sibling config-field with noteField id)
const noteField = el.closest('.config-field')?.nextElementSibling;
if(noteField && noteField.id && noteField.id.startsWith('noteField')) {
noteField.style.display = sw.classList.contains('on') ? 'block' : 'none';
}
}
// === Test paragraph - 3 step flow ===
function testParagraph(id) {
// Reset modal to step 1
document.getElementById('testStep1').style.display = 'block';
document.getElementById('testStep2').style.display = 'none';
document.getElementById('testStep3').style.display = 'none';
document.getElementById('testModalFooter').style.display = 'none';
document.getElementById('testUploaded').style.display = 'none';
document.getElementById('testModal').classList.add('open');
document.querySelector('#testStep1 .config-section-title span').style.background = 'var(--c-primary)';
}
function simulateUpload() {
document.querySelector('#testStep1 > div:first-child > div > div').style.display = 'none'; // hide upload box
document.getElementById('testUploaded').style.display = 'block';
lucide.createIcons();
}
function startTestStep2() {
document.getElementById('testStep1').style.display = 'none';
document.getElementById('testStep2').style.display = 'block';
document.getElementById('testModalFooter').style.display = 'none';
// Reset task icons
['tsk1','tsk2','tsk3'].forEach(id => {
const el = document.getElementById(id);
el.innerHTML = '<i data-lucide="circle" class="icon-sm"></i>';
el.style.color = 'var(--c-text-4)';
});
lucide.createIcons();
// Simulate step progression
const t1 = document.getElementById('tsk1');
const t2 = document.getElementById('tsk2');
const t3 = document.getElementById('tsk3');
const stepText = document.getElementById('testStepText');
// Step 1: File parsing (after 1.2s)
setTimeout(() => {
t1.innerHTML = '<i data-lucide="loader" class="icon-sm"></i>';
t1.style.color = 'var(--c-primary)';
stepText.textContent = '正在解析文件...';
lucide.createIcons();
}, 200);
setTimeout(() => {
t1.innerHTML = '<i data-lucide="check-circle" class="icon-sm"></i>';
t1.style.color = 'var(--c-success)';
// Step 2: AI request
t2.innerHTML = '<i data-lucide="loader" class="icon-sm"></i>';
t2.style.color = 'var(--c-primary)';
stepText.textContent = '正在请求 AI 大模型Claude 3.5 Sonnet...';
lucide.createIcons();
}, 1400);
setTimeout(() => {
t2.innerHTML = '<i data-lucide="check-circle" class="icon-sm"></i>';
t2.style.color = 'var(--c-success)';
// Step 3: Fill result
t3.innerHTML = '<i data-lucide="loader" class="icon-sm"></i>';
t3.style.color = 'var(--c-primary)';
stepText.textContent = '正在填充生成结果...';
lucide.createIcons();
}, 3200);
setTimeout(() => {
t3.innerHTML = '<i data-lucide="check-circle" class="icon-sm"></i>';
t3.style.color = 'var(--c-success)';
// Switch to step 3
document.getElementById('testStep2').style.display = 'none';
document.getElementById('testStep3').style.display = 'block';
document.getElementById('testModalFooter').style.display = 'flex';
lucide.createIcons();
}, 4800);
}
function closeTestModal() {
document.getElementById('testModal').classList.remove('open');
}
// === Model management modals ===
function openModelModal(mode, name, provider, endpoint, apikey) {
const title = document.getElementById('modelModalTitle');
const mName = document.getElementById('mName');
const mProvider = document.getElementById('mProvider');
const mFormat = document.getElementById('mFormat');
const mEndpoint = document.getElementById('mEndpoint');
const mApiKey = document.getElementById('mApiKey');
if(mode === 'add') {
title.innerHTML = '<i data-lucide="cpu" class="icon-sm" style="color:var(--c-primary)"></i> 添加模型';
mName.value = '';
mProvider.value = '';
mFormat.value = 'OpenAI';
mEndpoint.value = '';
mApiKey.value = '';
} else {
title.innerHTML = '<i data-lucide="settings" class="icon-sm" style="color:var(--c-primary)"></i> 编辑模型 - ' + name;
mName.value = name || '';
mProvider.value = provider || '';
// Guess format from provider name
if(provider && provider.toLowerCase().includes('anthropic')) mFormat.value = 'Anthropic';
else if(provider && (provider.toLowerCase().includes('openai') || provider.toLowerCase().includes('deepseek') || provider.toLowerCase().includes('阿里'))) mFormat.value = 'OpenAI';
else mFormat.value = 'OpenAI';
mEndpoint.value = endpoint || '';
mApiKey.value = apikey || '';
}
document.getElementById('modelModal').classList.add('open');
lucide.createIcons();
}
function closeModelModal() {
document.getElementById('modelModal').classList.remove('open');
}
// === Generation ===
function startGeneration() {
document.getElementById('genLeftStatus').style.display = 'block';
document.getElementById('genDoneSide').style.display = 'none';
let pct = 0;
const bar = document.getElementById('genProgressBar');
const gpText = document.getElementById('genLeftText');
const steps = [
{id:'g1', name:'一、主要经营指标完成情况'},
{id:'g2', name:'主要经营指标完成情况表'},
{id:'g3', name:'二、成本费用分析'},
{id:'g4', name:'三、存在的主要问题'},
{id:'g5', name:'四、下一步工作措施'},
{id:'g6', name:'附图:各季度营收趋势图'}
];
let currentStep = 0;
const interval = setInterval(() => {
pct += Math.floor(Math.random()*8)+3;
if(pct>=100) {
pct=100;
clearInterval(interval);
document.getElementById('genLeftStatus').style.display = 'none';
document.getElementById('genDoneSide').style.display = 'flex';
// Mark all remaining as done
for(let i=currentStep;i<steps.length;i++) {
const el = document.getElementById(steps[i].id);
el.innerHTML = '<i data-lucide="check-circle" class="icon-sm"></i>';
el.style.color = 'var(--c-success)';
}
lucide.createIcons();
return;
}
bar.style.width = pct+'%';
// Update step progress
const stepIdx = Math.floor(pct / 17);
if(stepIdx > currentStep && stepIdx < steps.length) {
currentStep = stepIdx;
// Mark previous as done
if(currentStep > 0) {
const prev = document.getElementById(steps[currentStep-1].id);
if(prev) { prev.innerHTML = '<i data-lucide="check-circle" class="icon-sm"></i>'; prev.style.color = 'var(--c-success)'; }
}
// Mark current as in progress
const cur = document.getElementById(steps[currentStep].id);
if(cur) { cur.innerHTML = '<i data-lucide="loader" class="icon-sm"></i>'; cur.style.color = 'var(--c-primary)'; }
gpText.textContent = '正在生成「'+steps[currentStep].name+'」...';
}
lucide.createIcons();
},600);
}
function goGenerate(name) {
const sel = document.querySelector('#screen-generate .form-select');
if(sel) sel.value = name;
location.href='执行生成.html';
}
function onTplSelect() {}
</script>
</body>
</html>