Files
yidaima/RuoYi-Vue/docs/mockups/page-designer-editor-only.html

994 lines
24 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<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 {
display: grid;
gap: 4px;
min-width: 0;
}
.crumb {
color: var(--muted);
font-size: 13px;
}
.title h1 {
margin: 0;
color: var(--text);
font-size: 22px;
line-height: 1.2;
}
.title p {
margin: 0;
color: var(--muted);
line-height: 1.45;
}
.actions,
.switch-row,
.page-switch,
.canvas-actions {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
.btn {
min-height: 36px;
padding: 7px 13px;
border: 1px solid var(--line);
border-radius: 6px;
background: #fff;
color: #344054;
}
.btn.primary {
border-color: var(--primary);
background: var(--primary);
color: #fff;
font-weight: 700;
}
.btn.soft {
background: var(--soft);
}
.badge {
display: inline-flex;
align-items: center;
min-height: 26px;
padding: 4px 9px;
border-radius: 999px;
background: #fff8e6;
color: var(--amber);
font-size: 12px;
font-weight: 700;
white-space: nowrap;
}
.segmented {
display: flex;
align-items: center;
gap: 6px;
padding: 4px;
border: 1px solid var(--line);
border-radius: 8px;
background: var(--soft);
}
.segmented button {
min-height: 30px;
padding: 5px 12px;
border: 0;
border-radius: 6px;
background: transparent;
color: var(--muted);
}
.segmented button.active {
background: #fff;
color: var(--primary);
font-weight: 700;
box-shadow: 0 1px 4px rgba(16, 24, 40, 0.08);
}
.page-select {
min-width: 210px;
min-height: 36px;
padding: 0 10px;
border: 1px solid var(--line);
border-radius: 6px;
background: #fff;
color: #344054;
}
.designer-grid {
min-height: 0;
display: grid;
grid-template-columns: 260px minmax(520px, 1fr) 336px;
gap: 14px;
}
.panel {
min-height: 0;
overflow: hidden;
display: grid;
grid-template-rows: auto minmax(0, 1fr);
border: 1px solid var(--line);
border-radius: 8px;
background: var(--panel);
}
.panel-head {
min-height: 50px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 12px 14px;
border-bottom: 1px solid var(--line);
background: #fff;
}
.panel-head strong {
font-size: 15px;
}
.panel-body {
min-height: 0;
overflow: auto;
padding: 14px;
}
.muted {
color: var(--muted);
font-size: 12px;
}
.structure-tree {
display: grid;
gap: 8px;
}
.tree-group {
display: grid;
gap: 6px;
padding: 10px;
border: 1px solid #e8eef7;
border-radius: 8px;
background: var(--soft);
}
.tree-group > button,
.tree-child {
width: 100%;
min-height: 32px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
padding: 6px 8px;
border: 1px solid transparent;
border-radius: 6px;
background: transparent;
color: #344054;
text-align: left;
}
.tree-group > button.active,
.tree-child.active {
border-color: var(--primary);
background: var(--primary-soft);
color: var(--primary);
font-weight: 700;
}
.tree-children {
display: grid;
gap: 4px;
padding-left: 14px;
}
.tree-child {
color: var(--muted);
background: #fff;
border-color: #eef2f7;
}
.count {
color: var(--faint);
font-size: 12px;
}
.canvas-panel {
background: #f9fbff;
}
.canvas-body {
min-height: 0;
overflow: auto;
padding: 22px;
display: grid;
justify-items: center;
}
.page-frame {
width: min(100%, 880px);
display: grid;
gap: 14px;
}
.canvas-band {
padding: 14px;
border: 1px solid #dce6f3;
border-radius: 8px;
background: #fff;
}
.canvas-band.active {
border-color: var(--primary);
box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.08);
}
.band-title {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
margin-bottom: 12px;
}
.band-title strong {
font-size: 15px;
}
.band-title span {
color: var(--muted);
font-size: 12px;
}
.block-row,
.field-row,
.action-row {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.chip {
display: inline-flex;
align-items: center;
gap: 6px;
min-height: 32px;
padding: 6px 10px;
border: 1px solid var(--line);
border-radius: 6px;
background: #fff;
color: #344054;
}
.chip.block {
border-color: #cfe0f5;
background: #f5f9ff;
color: #1d4ed8;
font-weight: 700;
}
.chip.action {
border-color: #bbf7d0;
background: #ecfdf5;
color: var(--green);
font-weight: 700;
}
.query-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr)) auto;
gap: 10px;
}
.input-preview {
min-height: 36px;
display: flex;
align-items: center;
padding: 0 10px;
border: 1px solid var(--line);
border-radius: 6px;
background: #fff;
color: var(--muted);
}
.card-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 12px;
}
.book-card {
display: grid;
gap: 10px;
padding: 12px;
border: 1px solid var(--line);
border-radius: 8px;
background: #fff;
}
.cover {
height: 76px;
display: grid;
place-items: center;
border-radius: 6px;
background: #eaf2ff;
color: var(--primary);
font-size: 12px;
}
.book-meta {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.book-meta span {
padding: 4px 7px;
border-radius: 5px;
background: #f2f4f7;
color: var(--muted);
font-size: 12px;
}
.inspector {
display: grid;
align-content: start;
gap: 14px;
}
.inspector-title {
display: grid;
gap: 4px;
padding-bottom: 12px;
border-bottom: 1px solid var(--line);
}
.inspector-title strong {
font-size: 16px;
}
.inspector-title span {
color: var(--muted);
font-size: 12px;
}
.inspector-box {
display: grid;
gap: 10px;
padding: 12px;
border: 1px solid var(--line);
border-radius: 8px;
background: var(--soft);
}
.form-grid {
display: grid;
gap: 12px;
}
.field {
display: grid;
gap: 6px;
}
.field label {
color: #344054;
font-size: 13px;
font-weight: 700;
}
.field input,
.field select {
width: 100%;
min-height: 36px;
padding: 0 10px;
border: 1px solid var(--line);
border-radius: 6px;
background: #fff;
color: #344054;
}
.json-line {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
padding-top: 12px;
border-top: 1px solid var(--line);
color: var(--muted);
font-size: 13px;
}
.drawer-mask {
position: fixed;
inset: 0;
display: none;
background: rgba(15, 23, 42, 0.28);
z-index: 10;
}
.drawer-mask.open {
display: block;
}
.drawer {
position: absolute;
top: 0;
right: 0;
width: min(440px, 100vw);
height: 100%;
display: grid;
grid-template-rows: auto auto minmax(0, 1fr);
background: #fff;
box-shadow: -12px 0 32px rgba(15, 23, 42, 0.18);
}
.drawer-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 16px;
border-bottom: 1px solid var(--line);
}
.drawer-head h2 {
margin: 0;
font-size: 18px;
}
.drawer-tabs {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 8px;
padding: 12px 16px;
border-bottom: 1px solid var(--line);
}
.drawer-tabs button {
min-height: 34px;
border: 1px solid var(--line);
border-radius: 6px;
background: #fff;
color: var(--muted);
}
.drawer-tabs button.active {
border-color: var(--primary);
background: var(--primary-soft);
color: var(--primary);
font-weight: 700;
}
.drawer-body {
min-height: 0;
overflow: auto;
padding: 16px;
display: grid;
align-content: start;
gap: 10px;
}
.material {
display: grid;
gap: 5px;
padding: 12px;
border: 1px solid var(--line);
border-radius: 8px;
background: #fff;
}
.material strong {
font-size: 14px;
}
.material span {
color: var(--muted);
font-size: 12px;
}
@media (max-width: 1280px) {
.designer-grid {
grid-template-columns: 240px minmax(420px, 1fr);
}
.right-panel {
grid-column: 1 / -1;
}
}
@media (max-width: 840px) {
.topbar,
.head-row,
.head-subrow {
align-items: stretch;
flex-direction: column;
}
.designer-grid,
.query-grid,
.card-grid {
grid-template-columns: 1fr;
}
.actions,
.page-switch {
justify-content: flex-start;
}
}
</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>图书目录</h1>
<p>/books · 卡片列表 · 图书信息表。直接进入编辑,不做页面管理视图。</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>
<div class="page-switch">
<span class="muted">切换页面</span>
<select class="page-select">
<option>图书目录 /books</option>
<option>图书详情 /books/:id</option>
<option>我的借阅 /borrowings</option>
<option>我的预约 /reservations</option>
<option>+ 新增页面</option>
</select>
</div>
</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="panel">
<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" type="button">
<span>顶部业务块</span>
<span class="count">2</span>
</button>
<div class="tree-children">
<button class="tree-child active" type="button">轮播图</button>
<button class="tree-child" type="button">公告信息</button>
</div>
</div>
<div class="tree-group">
<button type="button">
<span>查询区</span>
<span class="count">3</span>
</button>
<div class="tree-children">
<button class="tree-child" type="button">书名</button>
<button class="tree-child" type="button">作者</button>
<button class="tree-child" type="button">图书分类</button>
</div>
</div>
<div class="tree-group">
<button type="button">
<span>卡片列表</span>
<span class="count">7</span>
</button>
<div class="tree-children">
<button class="tree-child" type="button">封面图片URL</button>
<button class="tree-child" type="button">书名</button>
<button class="tree-child" type="button">作者</button>
<button class="tree-child" type="button">借书按钮</button>
<button class="tree-child" type="button">预约按钮</button>
</div>
</div>
<div class="tree-group">
<button type="button">
<span>底部业务块</span>
<span class="count">0</span>
</button>
</div>
</div>
</div>
</aside>
<section class="panel canvas-panel">
<div class="panel-head">
<div>
<strong>画布</strong>
<span class="muted">当前选中:轮播图</span>
</div>
<button class="btn" id="addContentCanvas">添加内容</button>
</div>
<div class="canvas-body">
<div class="page-frame">
<section class="canvas-band active">
<div class="band-title">
<strong>顶部业务块</strong>
<span>2 个业务块</span>
</div>
<div class="block-row">
<span class="chip block">轮播图</span>
<span class="chip block">公告信息</span>
</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="card-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>
<section class="canvas-band">
<div class="band-title">
<strong>底部业务块</strong>
<span>可添加公告、轮播等</span>
</div>
<div class="field-row">
<span class="chip">空区域</span>
</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>业务块配置:轮播图</strong>
<span>右侧只显示当前选中对象的配置JSON 默认收起。</span>
</div>
<div class="inspector-box">
<strong>数据映射</strong>
<div class="form-grid">
<div class="field">
<label>轮播图表</label>
<select>
<option>首页轮播表</option>
</select>
</div>
<div class="field">
<label>图片字段</label>
<select>
<option>封面图片URL</option>
</select>
</div>
<div class="field">
<label>标题字段</label>
<select>
<option>书名</option>
</select>
</div>
<div class="field">
<label>跳转字段</label>
<select>
<option>详情页路径</option>
</select>
</div>
</div>
</div>
<div class="inspector-box">
<strong>展示设置</strong>
<div class="form-grid">
<div class="field">
<label>显示数量</label>
<input value="5" />
</div>
<div class="field">
<label>自动播放</label>
<select>
<option>开启</option>
<option>关闭</option>
</select>
</div>
</div>
</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 class="active">字段</button>
<button>按钮</button>
<button>业务块</button>
</div>
<div class="drawer-body">
<div class="material">
<strong>书名</strong>
<span>字段 · 可拖入查询区或卡片列表</span>
</div>
<div class="material">
<strong>作者</strong>
<span>字段 · 可拖入查询区或卡片列表</span>
</div>
<div class="material">
<strong>封面图片URL</strong>
<span>图片字段 · 可拖入卡片封面</span>
</div>
<div class="material">
<strong>借书</strong>
<span>按钮 · 可拖入卡片操作区</span>
</div>
<div class="material">
<strong>公告信息</strong>
<span>业务块 · 可放到顶部或底部业务块区域</span>
</div>
<div class="material">
<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('addContentCanvas').addEventListener('click', openDrawer)
document.getElementById('closeDrawerBtn').addEventListener('click', closeDrawer)
drawerMask.addEventListener('click', (event) => {
if (event.target === drawerMask) {
closeDrawer()
}
})
</script>
</body>
</html>