Files
yidaima/RuoYi-Vue/docs/mockups/page-designer-canvas-menu.html

1359 lines
38 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" />
<title>EasyCode 画布菜单式页面设计器原型</title>
<style>
:root {
--bg: #f4f7fb;
--panel: #ffffff;
--line: #dfe6f0;
--soft: #f8fafc;
--text: #172033;
--muted: #667085;
--faint: #98a2b3;
--primary: #2563eb;
--primary-soft: #eaf2ff;
--green: #0f9f6e;
--amber: #b7791f;
--danger: #dc2626;
font-family: "Inter", "PingFang SC", "Microsoft YaHei", Arial, sans-serif;
}
* {
box-sizing: border-box;
}
body {
margin: 0;
min-height: 100vh;
background: var(--bg);
color: var(--text);
font-size: 14px;
}
button,
input,
select {
font: inherit;
}
button {
cursor: pointer;
}
.app {
min-height: 100vh;
display: grid;
grid-template-rows: 64px minmax(0, 1fr);
}
.topbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 20px;
padding: 0 28px;
border-bottom: 1px solid var(--line);
background: var(--panel);
}
.brand {
display: flex;
align-items: center;
gap: 12px;
min-width: 220px;
}
.brand-mark {
width: 36px;
height: 36px;
display: grid;
place-items: center;
border-radius: 8px;
background: var(--primary);
color: #fff;
font-weight: 800;
}
.brand strong {
font-size: 20px;
}
.nav {
display: flex;
gap: 26px;
color: #475467;
white-space: nowrap;
}
.nav .active {
color: var(--primary);
font-weight: 700;
}
.userbar {
display: flex;
align-items: center;
gap: 10px;
color: #475467;
white-space: nowrap;
}
.designer-page {
min-height: 0;
display: grid;
grid-template-rows: auto minmax(0, 1fr);
gap: 14px;
padding: 20px 24px 24px;
}
.designer-head {
display: grid;
gap: 12px;
padding: 16px 18px;
border: 1px solid var(--line);
border-radius: 8px;
background: var(--panel);
}
.head-row,
.head-subrow {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
}
.title {
min-width: 0;
}
.crumb {
display: block;
margin-bottom: 6px;
color: var(--primary);
font-weight: 700;
}
h1,
h2,
h3,
p {
margin: 0;
}
h1 {
font-size: 24px;
line-height: 1.25;
}
.title p {
margin-top: 6px;
color: var(--muted);
line-height: 1.6;
}
.actions,
.switch-row,
.canvas-actions {
display: flex;
align-items: center;
gap: 10px;
flex-wrap: wrap;
}
.btn {
min-height: 36px;
border: 1px solid var(--line);
border-radius: 6px;
background: #fff;
color: #344054;
padding: 0 14px;
font-weight: 600;
}
.btn.primary {
border-color: var(--primary);
background: var(--primary);
color: #fff;
}
.btn.soft {
border-color: #bfdbfe;
background: var(--primary-soft);
color: var(--primary);
}
.btn.icon {
width: 32px;
min-height: 32px;
padding: 0;
font-size: 18px;
}
.badge {
display: inline-flex;
align-items: center;
min-height: 28px;
border-radius: 999px;
background: #ecfdf3;
color: #067647;
padding: 0 10px;
font-weight: 700;
white-space: nowrap;
}
.muted {
color: var(--muted);
}
.segmented {
display: inline-grid;
grid-template-columns: repeat(2, minmax(66px, 1fr));
padding: 3px;
border: 1px solid var(--line);
border-radius: 7px;
background: var(--soft);
}
.segmented button {
min-height: 30px;
border: 0;
border-radius: 5px;
background: transparent;
color: #475467;
font-weight: 700;
}
.segmented button.active {
background: #fff;
color: var(--primary);
box-shadow: 0 1px 3px rgba(16, 24, 40, .12);
}
.designer-grid {
min-height: 0;
display: grid;
grid-template-columns: minmax(230px, 270px) minmax(520px, 1fr) minmax(300px, 360px);
gap: 14px;
}
.panel,
.left-card {
min-height: 0;
border: 1px solid var(--line);
border-radius: 8px;
background: var(--panel);
overflow: hidden;
}
.left-panel {
min-height: 0;
display: grid;
grid-template-rows: minmax(0, 1fr) auto;
gap: 14px;
}
.panel-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
min-height: 56px;
padding: 12px 16px;
border-bottom: 1px solid var(--line);
background: #fff;
}
.panel-head strong {
font-size: 16px;
}
.panel-head span {
font-size: 13px;
}
.panel-body {
padding: 14px;
}
.structure-tree {
display: grid;
gap: 12px;
}
.tree-group {
display: grid;
gap: 8px;
}
.tree-group > button,
.tree-child,
.material-row {
width: 100%;
min-height: 38px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
border: 1px solid var(--line);
border-radius: 7px;
background: #fff;
color: #344054;
padding: 8px 10px;
text-align: left;
}
.tree-group > button.active,
.tree-child.active {
border-color: #93c5fd;
background: var(--primary-soft);
color: var(--primary);
font-weight: 700;
}
.tree-children {
display: grid;
gap: 7px;
padding-left: 14px;
border-left: 1px dashed #cbd5e1;
}
.count {
min-width: 24px;
height: 22px;
display: inline-grid;
place-items: center;
border-radius: 999px;
background: #eef2f7;
color: #667085;
font-size: 12px;
font-weight: 700;
}
.material-grid {
display: grid;
gap: 8px;
}
.material-row span {
color: var(--muted);
font-size: 12px;
}
.canvas-panel {
display: grid;
grid-template-rows: auto minmax(0, 1fr);
}
.canvas-body {
min-height: 0;
overflow: auto;
padding: 18px;
background:
linear-gradient(90deg, rgba(37, 99, 235, .04) 1px, transparent 1px),
linear-gradient(rgba(37, 99, 235, .04) 1px, transparent 1px),
#f8fbff;
background-size: 24px 24px;
}
.page-frame {
max-width: 980px;
min-height: 760px;
margin: 0 auto;
border: 1px solid #cbd5e1;
border-radius: 8px;
background: #fff;
box-shadow: 0 12px 36px rgba(15, 23, 42, .08);
overflow: visible;
}
.site-menu-block {
position: relative;
z-index: 10;
display: flex;
align-items: center;
justify-content: space-between;
gap: 20px;
min-height: 68px;
padding: 0 28px;
border: 2px solid transparent;
border-bottom: 1px solid #e5e7eb;
background: #fff;
}
.site-menu-block.selected {
border-color: #2f80ed;
box-shadow: 0 0 0 3px rgba(47, 128, 237, .12);
}
.site-brand {
display: flex;
align-items: center;
gap: 10px;
min-width: 150px;
font-weight: 800;
white-space: nowrap;
}
.site-logo {
width: 34px;
height: 34px;
display: grid;
place-items: center;
border-radius: 8px;
background: #14532d;
color: #fff;
font-size: 13px;
}
.site-nav {
display: flex;
align-items: center;
justify-content: center;
gap: 4px;
min-width: 0;
flex: 1;
}
.site-nav-item {
position: relative;
}
.site-nav-item > button {
min-height: 42px;
border: 0;
border-radius: 6px;
background: transparent;
color: #344054;
padding: 0 14px;
font-weight: 700;
white-space: nowrap;
}
.site-nav-item > button.active,
.site-nav-item > button:hover {
background: #eef6ff;
color: var(--primary);
}
.dropdown {
position: absolute;
top: 48px;
left: 0;
width: 190px;
display: none;
padding: 8px;
border: 1px solid var(--line);
border-radius: 8px;
background: #fff;
box-shadow: 0 16px 36px rgba(15, 23, 42, .14);
}
.site-nav-item:hover .dropdown,
.site-nav-item.open .dropdown {
display: grid;
gap: 4px;
}
.dropdown button {
min-height: 38px;
display: flex;
align-items: center;
justify-content: space-between;
border: 0;
border-radius: 6px;
background: #fff;
color: #344054;
padding: 0 10px;
text-align: left;
}
.dropdown button.active,
.dropdown button:hover {
background: var(--primary-soft);
color: var(--primary);
font-weight: 700;
}
.site-actions {
display: flex;
align-items: center;
gap: 8px;
white-space: nowrap;
}
.site-actions button {
min-height: 34px;
border: 1px solid #bbf7d0;
border-radius: 6px;
background: #f0fdf4;
color: #166534;
padding: 0 12px;
font-weight: 700;
}
.edit-handle {
position: absolute;
top: 6px;
right: 8px;
display: inline-flex;
align-items: center;
gap: 6px;
min-height: 24px;
border: 1px solid #bfdbfe;
border-radius: 999px;
background: #eff6ff;
color: var(--primary);
padding: 0 9px;
font-size: 12px;
font-weight: 700;
}
.hero-strip {
display: grid;
grid-template-columns: minmax(260px, 1fr) 260px;
gap: 22px;
padding: 28px;
background: linear-gradient(135deg, #f8fafc 0%, #ecfdf3 100%);
border-bottom: 1px solid #e5e7eb;
}
.hero-copy {
display: grid;
gap: 12px;
align-content: center;
}
.hero-copy h2 {
font-size: 26px;
line-height: 1.3;
}
.hero-copy p {
color: #475467;
line-height: 1.7;
}
.notice-card {
display: grid;
gap: 10px;
align-content: center;
border: 1px solid #fde68a;
border-radius: 8px;
background: #fffbeb;
padding: 16px;
}
.notice-card strong {
color: #92400e;
}
.canvas-band {
margin: 20px 28px;
border: 1px solid #e5e7eb;
border-radius: 8px;
background: #fff;
padding: 16px;
}
.canvas-band.active {
border-color: #2f80ed;
box-shadow: 0 0 0 3px rgba(47, 128, 237, .10);
}
.band-title {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-bottom: 14px;
}
.band-title span {
color: var(--muted);
font-size: 13px;
}
.query-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr)) 92px;
gap: 12px;
}
.input-preview {
min-height: 38px;
display: flex;
align-items: center;
border: 1px solid #d0d5dd;
border-radius: 6px;
background: #fff;
color: #667085;
padding: 0 12px;
}
.book-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 12px;
}
.book-card {
display: grid;
gap: 10px;
border: 1px solid #e5e7eb;
border-radius: 8px;
background: #fff;
padding: 12px;
}
.cover {
height: 110px;
display: grid;
place-items: center;
border-radius: 7px;
background: #eaf2ff;
color: var(--primary);
font-weight: 700;
}
.book-meta {
display: grid;
gap: 5px;
color: #667085;
font-size: 13px;
}
.action-row,
.field-row {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.chip {
min-height: 30px;
display: inline-flex;
align-items: center;
justify-content: center;
border: 1px solid #d0d5dd;
border-radius: 6px;
background: #fff;
color: #344054;
padding: 0 10px;
font-weight: 600;
}
.chip.action {
border-color: #bfdbfe;
background: #eff6ff;
color: var(--primary);
}
.right-panel {
display: grid;
grid-template-rows: auto minmax(0, 1fr);
}
.inspector {
min-height: 0;
overflow: auto;
display: grid;
gap: 14px;
align-content: start;
}
.inspector-title {
display: grid;
gap: 6px;
border-bottom: 1px solid var(--line);
padding-bottom: 14px;
}
.inspector-title strong {
font-size: 18px;
}
.inspector-title span {
color: var(--muted);
line-height: 1.5;
}
.inspector-box {
display: grid;
gap: 12px;
border: 1px solid var(--line);
border-radius: 8px;
background: var(--soft);
padding: 14px;
}
.form-grid {
display: grid;
gap: 12px;
}
.field {
display: grid;
gap: 7px;
}
.field label {
color: #344054;
font-weight: 700;
}
.field input,
.field select {
width: 100%;
min-height: 38px;
border: 1px solid #d0d5dd;
border-radius: 6px;
background: #fff;
color: #344054;
padding: 0 10px;
}
.inline-options {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 8px;
}
.option {
min-height: 38px;
display: flex;
align-items: center;
gap: 8px;
border: 1px solid var(--line);
border-radius: 6px;
background: #fff;
padding: 0 10px;
font-weight: 700;
}
.option.active {
border-color: #93c5fd;
background: var(--primary-soft);
color: var(--primary);
}
.menu-items-box {
display: grid;
gap: 8px;
}
.menu-config-row {
min-height: 42px;
display: grid;
grid-template-columns: minmax(88px, 1fr) minmax(92px, 1fr) 28px;
gap: 8px;
align-items: center;
border: 1px solid var(--line);
border-radius: 7px;
background: #fff;
padding: 8px;
}
.menu-config-row span {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.menu-config-row em {
color: var(--muted);
font-style: normal;
font-size: 12px;
}
.menu-config-row button {
width: 28px;
min-height: 28px;
border: 0;
border-radius: 6px;
background: #f8fafc;
color: #667085;
}
.json-line {
min-height: 48px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
border-top: 1px solid var(--line);
padding-top: 12px;
font-weight: 700;
}
.drawer-mask {
position: fixed;
inset: 0;
z-index: 50;
display: none;
justify-content: flex-end;
background: rgba(15, 23, 42, .28);
}
.drawer-mask.open {
display: flex;
}
.drawer {
width: min(520px, 100vw);
height: 100%;
display: grid;
grid-template-rows: auto auto minmax(0, 1fr);
background: #fff;
box-shadow: -18px 0 42px rgba(15, 23, 42, .18);
}
.drawer-head {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 18px;
padding: 20px;
border-bottom: 1px solid var(--line);
}
.drawer-head h2 {
margin-bottom: 6px;
font-size: 20px;
}
.drawer-tabs {
display: flex;
gap: 8px;
padding: 12px 20px;
border-bottom: 1px solid var(--line);
}
.drawer-tabs button {
min-height: 34px;
border: 1px solid var(--line);
border-radius: 6px;
background: #fff;
padding: 0 12px;
font-weight: 700;
}
.drawer-tabs button.active {
border-color: #93c5fd;
background: var(--primary-soft);
color: var(--primary);
}
.drawer-body {
overflow: auto;
display: grid;
gap: 10px;
align-content: start;
padding: 20px;
}
.drawer-card {
display: grid;
gap: 6px;
border: 1px solid var(--line);
border-radius: 8px;
background: #fff;
padding: 14px;
}
.drawer-card span {
color: var(--muted);
line-height: 1.5;
}
@media (max-width: 1180px) {
.designer-grid {
grid-template-columns: 230px minmax(440px, 1fr);
}
.right-panel {
grid-column: 1 / -1;
min-height: 360px;
}
}
@media (max-width: 900px) {
.topbar,
.head-row,
.head-subrow {
align-items: flex-start;
flex-direction: column;
}
.designer-grid {
grid-template-columns: 1fr;
}
.hero-strip,
.query-grid,
.book-grid {
grid-template-columns: 1fr;
}
.site-menu-block {
align-items: stretch;
flex-direction: column;
padding: 14px;
}
.site-nav {
justify-content: flex-start;
overflow-x: auto;
}
}
</style>
</head>
<body>
<div class="app">
<header class="topbar">
<div class="brand">
<div class="brand-mark">EC</div>
<strong>EasyCode</strong>
</div>
<nav class="nav">
<span>主页</span>
<span class="active">工作台</span>
<span>源码库</span>
<span>我的项目</span>
</nav>
<div class="userbar">
<span>wangpeng</span>
<button class="btn">退出</button>
</div>
</header>
<main class="designer-page">
<section class="designer-head">
<div class="head-row">
<div class="title">
<span class="crumb">图书借阅系统 / 前台页面设计</span>
<h1 id="pageTitle">图书目录</h1>
<p id="pageMeta">菜单放在画布顶部,按真实页面导航展示;点击二级菜单切换当前编辑页面。</p>
</div>
<div class="actions">
<span class="badge">有未保存修改</span>
<button class="btn" id="addContentTop">添加内容</button>
<button class="btn">预览页面</button>
<button class="btn primary">保存设计</button>
<button class="btn">返回工作台</button>
</div>
</div>
<div class="head-subrow">
<div class="switch-row">
<div class="segmented">
<button class="active">前台</button>
<button>后台</button>
</div>
<span class="muted" id="currentPagePath">当前页面:图书服务 / 图书目录</span>
</div>
<div class="canvas-actions">
<button class="btn soft">桌面</button>
<button class="btn">手机</button>
<button class="btn">撤销</button>
<button class="btn">重做</button>
</div>
</div>
</section>
<section class="designer-grid">
<aside class="left-panel">
<section class="left-card">
<div class="panel-head">
<strong>当前页面结构</strong>
<span class="muted">点击配置</span>
</div>
<div class="panel-body">
<div class="structure-tree">
<div class="tree-group">
<button class="active structure-node" type="button" data-title="菜单配置:顶部导航菜单">
<span>顶部导航菜单</span>
<span class="count">6</span>
</button>
<div class="tree-children">
<button class="tree-child structure-node active" type="button" data-title="菜单项配置:图书服务">图书服务</button>
<button class="tree-child structure-node" type="button" data-title="菜单项配置:我的中心">我的中心</button>
<button class="tree-child structure-node" type="button" data-title="菜单项配置:业务页面">业务页面</button>
</div>
</div>
<div class="tree-group">
<button class="structure-node" type="button" data-title="业务块配置:首页轮播">
<span>顶部业务块</span>
<span class="count">2</span>
</button>
<div class="tree-children">
<button class="tree-child structure-node" type="button" data-title="业务块配置:首页轮播">首页轮播</button>
<button class="tree-child structure-node" type="button" data-title="业务块配置:公告信息">公告信息</button>
</div>
</div>
<div class="tree-group">
<button class="structure-node" type="button" data-title="查询区配置">
<span>查询区</span>
<span class="count">3</span>
</button>
<div class="tree-children">
<button class="tree-child structure-node" type="button" data-title="字段配置:书名">书名</button>
<button class="tree-child structure-node" type="button" data-title="字段配置:作者">作者</button>
<button class="tree-child structure-node" type="button" data-title="字段配置:图书分类">图书分类</button>
</div>
</div>
<div class="tree-group">
<button class="structure-node" type="button" data-title="列表配置:卡片列表">
<span>卡片列表</span>
<span class="count">7</span>
</button>
<div class="tree-children">
<button class="tree-child structure-node" type="button" data-title="字段配置封面图片URL">封面图片URL</button>
<button class="tree-child structure-node" type="button" data-title="字段配置:书名">书名</button>
<button class="tree-child structure-node" type="button" data-title="按钮配置:借书">借书按钮</button>
<button class="tree-child structure-node" type="button" data-title="按钮配置:预约">预约按钮</button>
</div>
</div>
</div>
</div>
</section>
<section class="left-card">
<div class="panel-head">
<strong>添加内容</strong>
<button class="btn icon" id="addContentLeft" title="打开物料">+</button>
</div>
<div class="panel-body">
<div class="material-grid">
<button class="material-row" type="button">
<strong>字段</strong>
<span>拖入查询区或列表</span>
</button>
<button class="material-row" type="button">
<strong>按钮</strong>
<span>绑定动作</span>
</button>
<button class="material-row" type="button">
<strong>业务块</strong>
<span>公告、轮播、订单</span>
</button>
</div>
</div>
</section>
</aside>
<section class="panel canvas-panel">
<div class="panel-head">
<div>
<strong>画布</strong>
<span class="muted" id="selectedNodeText">当前选中:顶部导航菜单</span>
</div>
<button class="btn" id="addContentCanvas">添加内容</button>
</div>
<div class="canvas-body">
<div class="page-frame">
<header class="site-menu-block selected" id="siteMenuBlock">
<button class="edit-handle" id="menuConfigBtn" type="button">配置菜单</button>
<div class="site-brand">
<span class="site-logo">LIB</span>
<span>图书借阅</span>
</div>
<nav class="site-nav" aria-label="前台菜单">
<div class="site-nav-item open">
<button class="active top-menu" type="button">图书服务</button>
<div class="dropdown">
<button class="menu-link active" type="button" data-group="图书服务" data-name="图书目录" data-route="/books">图书目录 <span>/books</span></button>
<button class="menu-link" type="button" data-group="图书服务" data-name="图书详情" data-route="/books/:id">图书详情 <span>/books/:id</span></button>
</div>
</div>
<div class="site-nav-item">
<button class="top-menu" type="button">我的中心</button>
<div class="dropdown">
<button class="menu-link" type="button" data-group="我的中心" data-name="我的借阅" data-route="/borrowings">我的借阅 <span>/borrowings</span></button>
<button class="menu-link" type="button" data-group="我的中心" data-name="我的预约" data-route="/reservations">我的预约 <span>/reservations</span></button>
</div>
</div>
<div class="site-nav-item">
<button class="top-menu" type="button">业务页面</button>
<div class="dropdown">
<button class="menu-link" type="button" data-group="业务页面" data-name="购物车" data-route="/cart">购物车 <span>/cart</span></button>
<button class="menu-link" type="button" data-group="业务页面" data-name="订单中心" data-route="/orders">订单中心 <span>/orders</span></button>
</div>
</div>
</nav>
<div class="site-actions">
<button type="button">登录</button>
<button type="button">我的书架</button>
</div>
</header>
<section class="hero-strip">
<div class="hero-copy">
<h2>馆藏图书一站式借阅</h2>
<p>菜单、公告、轮播都作为真实页面内容展示。设计时看到的结构和用户最终访问的页面保持一致。</p>
<div class="field-row">
<span class="chip action">本周新书</span>
<span class="chip">热门分类</span>
<span class="chip">借阅规则</span>
</div>
</div>
<div class="notice-card">
<strong>公告信息</strong>
<p>端午假期借阅服务时间调整,请提前完成预约。</p>
</div>
</section>
<section class="canvas-band">
<div class="band-title">
<strong>查询区</strong>
<span>3 个条件</span>
</div>
<div class="query-grid">
<div class="input-preview">书名</div>
<div class="input-preview">作者</div>
<div class="input-preview">图书分类</div>
<button class="btn primary">搜索</button>
</div>
</section>
<section class="canvas-band">
<div class="band-title">
<strong>卡片列表</strong>
<span>封面 + 标题 + 操作按钮</span>
</div>
<div class="book-grid">
<article class="book-card">
<div class="cover">封面图片URL</div>
<strong>书名</strong>
<div class="book-meta">
<span>作者</span>
<span>出版社</span>
<span>可借副本数</span>
</div>
<div class="action-row">
<span class="chip action">借书</span>
<span class="chip action">预约</span>
</div>
</article>
<article class="book-card">
<div class="cover">封面图片URL</div>
<strong>书名</strong>
<div class="book-meta">
<span>作者</span>
<span>出版社</span>
<span>可借副本数</span>
</div>
<div class="action-row">
<span class="chip action">借书</span>
<span class="chip action">预约</span>
</div>
</article>
<article class="book-card">
<div class="cover">封面图片URL</div>
<strong>书名</strong>
<div class="book-meta">
<span>作者</span>
<span>出版社</span>
<span>可借副本数</span>
</div>
<div class="action-row">
<span class="chip action">借书</span>
<span class="chip action">预约</span>
</div>
</article>
</div>
</section>
</div>
</div>
</section>
<aside class="panel right-panel">
<div class="panel-head">
<strong>属性配置</strong>
<button class="btn">高级</button>
</div>
<div class="panel-body inspector">
<div class="inspector-title">
<strong id="inspectorTitle">菜单配置:顶部导航菜单</strong>
<span id="inspectorDesc">菜单在画布里真实呈现。设计态点击菜单项切换页面,点击“配置菜单”编辑菜单结构和样式。</span>
</div>
<div class="inspector-box" id="menuInspector">
<strong>菜单样式</strong>
<div class="form-grid">
<div class="field">
<label>菜单位置</label>
<select>
<option>顶部横向菜单</option>
<option>左侧竖向菜单</option>
<option>移动端折叠菜单</option>
</select>
</div>
<div class="field">
<label>菜单来源</label>
<select>
<option>页面列表自动生成</option>
<option>自定义菜单</option>
<option>权限菜单</option>
</select>
</div>
<div class="field">
<label>显示方式</label>
<div class="inline-options">
<div class="option active">一级展开</div>
<div class="option">悬浮下拉</div>
</div>
</div>
</div>
</div>
<div class="inspector-box">
<strong>一级 / 二级菜单</strong>
<div class="menu-items-box">
<div class="menu-config-row">
<span>图书服务</span>
<em>2 个页面</em>
<button type="button"></button>
</div>
<div class="menu-config-row">
<span>我的中心</span>
<em>2 个页面</em>
<button type="button"></button>
</div>
<div class="menu-config-row">
<span>业务页面</span>
<em>购物车 / 订单</em>
<button type="button"></button>
</div>
</div>
<button class="btn soft" type="button">新增一级菜单</button>
</div>
<div class="json-line">
<span>JSON 高级设置</span>
<button class="btn">展开</button>
</div>
</div>
</aside>
</section>
</main>
</div>
<div id="drawerMask" class="drawer-mask">
<aside class="drawer">
<div class="drawer-head">
<div>
<h2>添加内容</h2>
<span class="muted">字段、按钮、业务块和导航菜单都从这里添加到画布。</span>
</div>
<button id="closeDrawerBtn" class="btn">关闭</button>
</div>
<div class="drawer-tabs">
<button>结构</button>
<button>字段</button>
<button>按钮</button>
<button class="active">业务块</button>
</div>
<div class="drawer-body">
<div class="drawer-card">
<strong>导航菜单</strong>
<span>放到画布顶部,支持一级/二级菜单、权限控制和页面跳转。</span>
</div>
<div class="drawer-card">
<strong>公告信息</strong>
<span>完整业务块,只配置公告表、标题字段、内容字段、发布时间字段。</span>
</div>
<div class="drawer-card">
<strong>轮播图</strong>
<span>完整业务块,只配置图片表、图片字段、标题字段、跳转字段。</span>
</div>
<div class="drawer-card">
<strong>订单流程</strong>
<span>完整业务块,包含下单、订单列表、订单详情和状态流转。</span>
</div>
</div>
</aside>
</div>
<script>
const drawerMask = document.getElementById('drawerMask')
const openDrawer = () => drawerMask.classList.add('open')
const closeDrawer = () => drawerMask.classList.remove('open')
document.getElementById('addContentTop').addEventListener('click', openDrawer)
document.getElementById('addContentLeft').addEventListener('click', openDrawer)
document.getElementById('addContentCanvas').addEventListener('click', openDrawer)
document.getElementById('closeDrawerBtn').addEventListener('click', closeDrawer)
drawerMask.addEventListener('click', (event) => {
if (event.target === drawerMask) closeDrawer()
})
const pageTitle = document.getElementById('pageTitle')
const pageMeta = document.getElementById('pageMeta')
const currentPagePath = document.getElementById('currentPagePath')
const selectedNodeText = document.getElementById('selectedNodeText')
const inspectorTitle = document.getElementById('inspectorTitle')
const inspectorDesc = document.getElementById('inspectorDesc')
const siteMenuBlock = document.getElementById('siteMenuBlock')
const selectMenuInspector = () => {
siteMenuBlock.classList.add('selected')
selectedNodeText.textContent = '当前选中:顶部导航菜单'
inspectorTitle.textContent = '菜单配置:顶部导航菜单'
inspectorDesc.textContent = '菜单在画布里真实呈现。设计态点击菜单项切换页面,点击“配置菜单”编辑菜单结构和样式。'
}
document.getElementById('menuConfigBtn').addEventListener('click', (event) => {
event.stopPropagation()
selectMenuInspector()
})
siteMenuBlock.addEventListener('click', selectMenuInspector)
document.querySelectorAll('.top-menu').forEach((button) => {
button.addEventListener('click', (event) => {
event.stopPropagation()
document.querySelectorAll('.site-nav-item').forEach((item) => item.classList.remove('open'))
button.closest('.site-nav-item').classList.add('open')
})
})
document.querySelectorAll('.menu-link').forEach((button) => {
button.addEventListener('click', (event) => {
event.stopPropagation()
const parent = button.closest('.site-nav-item')
document.querySelectorAll('.menu-link').forEach((item) => item.classList.remove('active'))
document.querySelectorAll('.top-menu').forEach((item) => item.classList.remove('active'))
document.querySelectorAll('.site-nav-item').forEach((item) => item.classList.remove('open'))
button.classList.add('active')
parent.classList.add('open')
parent.querySelector('.top-menu').classList.add('active')
pageTitle.textContent = button.dataset.name
pageMeta.textContent = `${button.dataset.route} · 菜单内二级页面。画布保持真实导航样式,当前编辑页同步切换。`
currentPagePath.textContent = `当前页面:${button.dataset.group} / ${button.dataset.name}`
selectedNodeText.textContent = `当前选中:页面 ${button.dataset.name}`
inspectorTitle.textContent = `页面配置:${button.dataset.name}`
inspectorDesc.textContent = '页面配置只显示页面名称、路由、页面类型、布局类型和绑定数据表。'
})
})
document.querySelectorAll('.structure-node').forEach((button) => {
button.addEventListener('click', () => {
document.querySelectorAll('.structure-node').forEach((item) => item.classList.remove('active'))
button.classList.add('active')
siteMenuBlock.classList.toggle('selected', button.dataset.title.includes('菜单'))
selectedNodeText.textContent = `当前选中:${button.textContent.trim()}`
inspectorTitle.textContent = button.dataset.title
inspectorDesc.textContent = button.dataset.title.includes('菜单')
? '菜单配置用于维护一级/二级菜单、显示规则和前台导航样式。'
: '右侧只展示当前选中元素需要配置的字段,避免页面配置、字段配置、按钮配置混在一起。'
})
})
</script>
</body>
</html>