feat: add template edit shell layout

This commit is contained in:
zwt13703
2026-07-01 20:34:24 +08:00
parent 217af9bc99
commit b4e128c227
9 changed files with 579 additions and 6 deletions
@@ -0,0 +1,89 @@
<template>
<aside class="config-panel">
<div class="panel-tabs">
<button class="tab active" type="button">区域配置</button>
<button class="tab" type="button">批量规则</button>
</div>
<div class="panel-body">
<section class="section">
<h2>基础信息</h2>
<label>
区域名称
<a-input placeholder="请选择预览区块" />
</label>
<label>
区域类型
<a-select value="ai_generate">
<a-select-option value="ai_generate">AI 生成</a-select-option>
<a-select-option value="manual">人工填写</a-select-option>
<a-select-option value="fixed">固定内容</a-select-option>
<a-select-option value="table">表格区域</a-select-option>
</a-select>
</label>
</section>
<section class="section">
<h2>提示词</h2>
<a-textarea :rows="6" placeholder="选择区域后配置提示词" />
</section>
<a-button block type="primary">保存配置</a-button>
</div>
</aside>
</template>
<style scoped>
.config-panel {
display: flex;
min-width: 0;
min-height: 0;
flex-direction: column;
border-right: 1px solid var(--c-border);
background: var(--c-surface);
}
.panel-tabs {
display: flex;
flex-shrink: 0;
border-bottom: 1px solid var(--c-border);
}
.tab {
padding: 12px 16px 10px;
border: 0;
border-bottom: 2px solid transparent;
color: var(--c-text-secondary);
background: transparent;
cursor: pointer;
}
.tab.active {
border-bottom-color: var(--c-primary);
color: var(--c-primary);
font-weight: 500;
}
.panel-body {
min-height: 0;
flex: 1;
padding: 16px;
overflow: auto;
}
.section {
margin-bottom: 18px;
}
.section h2 {
margin: 0 0 12px;
padding-bottom: 8px;
border-bottom: 1px solid var(--c-border-light);
font-size: 14px;
}
label {
display: grid;
gap: 6px;
margin-bottom: 12px;
color: var(--c-text-secondary);
font-size: 12px;
}
</style>
@@ -0,0 +1,116 @@
<script setup lang="ts">
import {
AppstoreOutlined,
DatabaseOutlined,
FileTextOutlined,
SettingOutlined,
} from "@ant-design/icons-vue";
</script>
<template>
<aside class="left-menu">
<div class="brand">
<div class="brand-mark">AI</div>
<span>AI 文档模板系统</span>
</div>
<nav class="menu-list">
<div class="menu-group-title">模板管理</div>
<button class="menu-item active" type="button">
<FileTextOutlined />
模板标注
</button>
<button class="menu-item" type="button">
<AppstoreOutlined />
模板中心
</button>
<div class="menu-group-title">资料与配置</div>
<button class="menu-item" type="button">
<DatabaseOutlined />
数据源
</button>
<button class="menu-item" type="button">
<SettingOutlined />
系统配置
</button>
</nav>
</aside>
</template>
<style scoped>
.left-menu {
min-width: 0;
border-right: 1px solid var(--c-border);
background: var(--c-surface);
}
.brand {
display: flex;
align-items: center;
gap: 8px;
height: 52px;
padding: 0 16px;
border-bottom: 1px solid var(--c-border);
font-weight: 600;
}
.brand-mark {
display: grid;
width: 26px;
height: 26px;
place-items: center;
border-radius: 5px;
color: #fff;
background: var(--c-primary);
font-size: 13px;
font-weight: 700;
}
.menu-list {
padding: 8px 0;
}
.menu-group-title {
padding: 8px 16px 4px;
color: var(--c-text-muted);
font-size: 11px;
font-weight: 500;
}
.menu-item {
position: relative;
display: flex;
align-items: center;
width: 100%;
gap: 8px;
padding: 8px 16px;
border: 0;
color: var(--c-text-secondary);
background: transparent;
cursor: pointer;
text-align: left;
}
.menu-item:hover {
color: var(--c-text);
background: var(--c-surface-soft);
}
.menu-item.active {
color: var(--c-primary);
background: var(--c-primary-soft);
font-weight: 500;
}
.menu-item.active::before {
position: absolute;
top: 4px;
bottom: 4px;
left: 0;
width: 3px;
border-radius: 0 2px 2px 0;
background: var(--c-primary);
content: "";
}
</style>
@@ -0,0 +1,108 @@
<script setup lang="ts">
const nodes = [
{ id: "block_001", name: "企业经营分析报告", status: "done" },
{ id: "block_003", name: "一、经营概况", status: "pending" },
{ id: "block_005", name: "经营指标表", status: "empty" },
];
</script>
<template>
<aside class="structure-tree">
<header>
<span>文档结构</span>
<span class="count">{{ nodes.length }}</span>
</header>
<ul>
<li v-for="node in nodes" :key="node.id">
<button type="button">
<span class="dot" :class="node.status"></span>
<span class="node-name">{{ node.name }}</span>
<span class="node-id">{{ node.id }}</span>
</button>
</li>
</ul>
</aside>
</template>
<style scoped>
.structure-tree {
display: flex;
min-width: 0;
min-height: 0;
flex-direction: column;
border-left: 1px solid var(--c-border);
background: var(--c-surface);
}
header {
display: flex;
align-items: center;
justify-content: space-between;
height: 43px;
padding: 0 14px;
border-bottom: 1px solid var(--c-border);
font-weight: 500;
}
.count {
padding: 0 7px;
border-radius: 9px;
color: var(--c-text-muted);
background: var(--c-surface-soft);
font-size: 11px;
line-height: 18px;
}
ul {
flex: 1;
min-height: 0;
margin: 0;
padding: 8px 0;
overflow: auto;
list-style: none;
}
button {
display: grid;
width: 100%;
grid-template-columns: 8px minmax(0, 1fr);
gap: 8px;
align-items: center;
padding: 7px 12px;
border: 0;
background: transparent;
cursor: pointer;
text-align: left;
}
button:hover {
background: var(--c-surface-soft);
}
.dot {
width: 7px;
height: 7px;
border-radius: 50%;
background: var(--c-text-muted);
}
.dot.done {
background: var(--c-success);
}
.dot.pending {
background: var(--c-warn);
}
.node-name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.node-id {
grid-column: 2;
color: var(--c-text-muted);
font-size: 11px;
}
</style>
@@ -0,0 +1,104 @@
<script setup lang="ts">
import {
DownloadOutlined,
EyeOutlined,
FullscreenOutlined,
PlayCircleOutlined,
SaveOutlined,
} from "@ant-design/icons-vue";
</script>
<template>
<header class="top-toolbar">
<div class="toolbar-left">
<div class="breadcrumb">模板管理 / 模板标注</div>
<div class="template-meta">
<strong>企业经营分析报告模板</strong>
<a-tag color="blue">v1.0.0</a-tag>
<span class="save-status">已保存</span>
</div>
</div>
<div class="toolbar-actions">
<a-button size="small">
<template #icon><EyeOutlined /></template>
预览
</a-button>
<a-button size="small" type="primary">
<template #icon><SaveOutlined /></template>
保存
</a-button>
<a-button size="small">
<template #icon><PlayCircleOutlined /></template>
生成测试
</a-button>
<a-button size="small">
<template #icon><DownloadOutlined /></template>
导出模板
</a-button>
<a-button size="small" shape="circle" aria-label="全屏">
<template #icon><FullscreenOutlined /></template>
</a-button>
</div>
</header>
</template>
<style scoped>
.top-toolbar {
display: flex;
align-items: center;
justify-content: space-between;
min-width: 0;
padding: 0 16px;
border-bottom: 1px solid var(--c-border);
background: var(--c-surface);
}
.toolbar-left {
min-width: 0;
}
.breadcrumb {
color: var(--c-text-muted);
font-size: 12px;
line-height: 18px;
}
.template-meta {
display: flex;
align-items: center;
gap: 8px;
min-width: 0;
line-height: 20px;
}
.template-meta strong {
overflow: hidden;
max-width: 320px;
text-overflow: ellipsis;
white-space: nowrap;
}
.save-status {
display: inline-flex;
align-items: center;
gap: 5px;
color: var(--c-text-secondary);
font-size: 12px;
}
.save-status::before {
width: 7px;
height: 7px;
border-radius: 50%;
background: var(--c-success);
content: "";
}
.toolbar-actions {
display: flex;
align-items: center;
gap: 8px;
flex-shrink: 0;
}
</style>
@@ -0,0 +1,93 @@
<template>
<section class="preview-wrap">
<div class="doc-page">
<h1 data-block-id="block_001">企业经营分析报告</h1>
<p data-block-id="block_002">
本报告用于展示 Word 模板解析后的 A4 预览效果后续会由后端返回的 preview_html 驱动渲染
</p>
<h2 data-block-id="block_003">经营概况</h2>
<p data-block-id="block_004">
点击预览区块后左侧配置面板将展示对应区域的提示词数据源和输出格式配置
</p>
<table data-block-id="block_005">
<thead>
<tr>
<th>指标</th>
<th>本期</th>
<th>同比</th>
</tr>
</thead>
<tbody>
<tr>
<td>营业收入</td>
<td>-</td>
<td>-</td>
</tr>
<tr>
<td>利润率</td>
<td>-</td>
<td>-</td>
</tr>
</tbody>
</table>
</div>
</section>
</template>
<style scoped>
.preview-wrap {
min-width: 0;
min-height: 0;
padding: 24px;
overflow: auto;
background: var(--c-surface-soft);
}
.doc-page {
width: 794px;
min-height: 1123px;
margin: 0 auto;
padding: 72px 64px;
background: var(--c-surface);
box-shadow: 0 2px 12px rgb(0 0 0 / 8%);
color: var(--c-text);
font-size: 14px;
line-height: 1.8;
}
h1 {
margin: 0 0 28px;
text-align: center;
font-size: 20px;
}
h2 {
margin: 24px 0 12px;
padding-bottom: 6px;
border-bottom: 2px solid var(--c-text);
font-size: 16px;
}
p {
margin: 8px 0;
text-align: justify;
text-indent: 2em;
}
table {
width: 100%;
margin: 14px 0;
border-collapse: collapse;
}
th,
td {
padding: 7px 10px;
border: 1px solid var(--c-border);
text-align: left;
}
th {
background: var(--c-surface-soft);
}
</style>
+6
View File
@@ -1,6 +1,7 @@
import { createRouter, createWebHistory } from "vue-router";
import TemplateCenter from "@/views/TemplateCenter.vue";
import TemplateEdit from "@/views/TemplateEdit.vue";
const router = createRouter({
history: createWebHistory(),
@@ -10,6 +11,11 @@ const router = createRouter({
name: "template-center",
component: TemplateCenter,
},
{
path: "/templates/:id/edit",
name: "template-edit",
component: TemplateEdit,
},
],
});
+44
View File
@@ -0,0 +1,44 @@
<script setup lang="ts">
import ConfigPanel from "@/components/TemplateEdit/ConfigPanel.vue";
import LeftMenu from "@/components/TemplateEdit/LeftMenu.vue";
import StructureTree from "@/components/TemplateEdit/StructureTree.vue";
import TopToolbar from "@/components/TemplateEdit/TopToolbar.vue";
import WordPreview from "@/components/TemplateEdit/WordPreview.vue";
</script>
<template>
<main class="template-edit">
<LeftMenu />
<section class="edit-main">
<TopToolbar />
<div class="edit-workspace">
<ConfigPanel />
<WordPreview />
<StructureTree />
</div>
</section>
</main>
</template>
<style scoped>
.template-edit {
display: grid;
grid-template-columns: 220px minmax(0, 1fr);
height: 100vh;
overflow: hidden;
background: var(--c-bg);
}
.edit-main {
display: grid;
min-width: 0;
grid-template-rows: 48px minmax(0, 1fr);
}
.edit-workspace {
display: grid;
min-height: 0;
grid-template-columns: 380px minmax(520px, 1fr) 240px;
overflow: hidden;
}
</style>