792 lines
53 KiB
HTML
792 lines
53 KiB
HTML
<!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> |