2026-06-06 11:45:53 +08:00
|
|
|
<!DOCTYPE html>
|
|
|
|
|
<html lang="zh-CN">
|
|
|
|
|
<head>
|
|
|
|
|
<meta charset="UTF-8">
|
|
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
|
|
<title>Apigo Base 能力展示 (Mega Demo - Sync)</title>
|
|
|
|
|
<!-- 引入 Bootstrap 5.3 CSS -->
|
|
|
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
|
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css" rel="stylesheet">
|
|
|
|
|
|
|
|
|
|
<!-- 核心同步脚本加载 (使用本地副本消灭路径风险) -->
|
|
|
|
|
<script src="./lib/state.js"></script>
|
|
|
|
|
<script src="./lib/bootstrap.js"></script>
|
|
|
|
|
<script src="./lib/base.js"></script>
|
|
|
|
|
|
|
|
|
|
<style>
|
|
|
|
|
body { height: 100vh; overflow: hidden; }
|
|
|
|
|
.main-container { height: 100%; display: flex; }
|
|
|
|
|
.sidebar { min-width: 200px; max-width: 500px; display: flex; flex-direction: column; }
|
|
|
|
|
.content { flex: 1; overflow: auto; padding: 20px; }
|
|
|
|
|
.section-card { margin-bottom: 30px; border: 1px solid #dee2e6; border-radius: 8px; overflow: hidden; }
|
|
|
|
|
.section-header { background: #f8f9fa; padding: 10px 15px; border-bottom: 1px solid #dee2e6; font-weight: bold; }
|
|
|
|
|
.section-body { padding: 15px; }
|
|
|
|
|
[data-bs-theme="dark"] .section-header { background: #343a40; border-bottom-color: #495057; }
|
|
|
|
|
[data-bs-theme="dark"] .section-card { border-color: #495057; }
|
|
|
|
|
.btn-xs { padding: 0.1rem 0.3rem; font-size: 0.75rem; }
|
|
|
|
|
</style>
|
|
|
|
|
<script>
|
|
|
|
|
// 1. 静态结构数据 (用于组件初始化,必须在 body 解析前就绪)
|
|
|
|
|
window.brand = { icon: 'cpu', label: 'Base 能力展示' };
|
|
|
|
|
window.navList = [
|
|
|
|
|
{ type: 'button', name: 'welcome', label: '项目概览', icon: 'house' },
|
|
|
|
|
{ type: 'button', name: 'forms', label: '表单与控件', icon: 'input-cursor-text' },
|
|
|
|
|
{ type: 'button', name: 'lists', label: '增强列表', icon: 'list-ul' },
|
|
|
|
|
{ type: 'button', name: 'ui', label: '交互反馈', icon: 'chat-square-dots' },
|
|
|
|
|
{ type: 'button', name: 'api', label: '网络通讯', icon: 'cloud-arrow-down' }
|
|
|
|
|
];
|
|
|
|
|
window.formSchema = [
|
|
|
|
|
{ name: 'username', label: '用户名', type: 'text', placeholder: '请输入用户名', setting: { required: true } },
|
|
|
|
|
{ name: 'password', label: '密码', type: 'password' },
|
|
|
|
|
{ name: 'dateRange', label: '日期范围', type: 'DatePicker', setting: { rangeEnd: 'endDate' } },
|
|
|
|
|
{ name: 'endDate', label: '结束日期', type: 'date', hidden: true },
|
|
|
|
|
{ name: 'theme', label: '主题色', type: 'ColorPicker' },
|
|
|
|
|
{ name: 'icon', label: '图标', type: 'IconPicker' },
|
|
|
|
|
{ name: 'tags', label: '标签', type: 'TagsInput' },
|
|
|
|
|
{ name: 'role', label: '角色', type: 'select', options: [{ label: '管理员', value: 'admin' }, { label: '普通用户', value: 'user' }] },
|
|
|
|
|
{ name: 'gender', label: '性别', type: 'checkbox', options: ['男', '女', '保密'] },
|
|
|
|
|
{ name: 'notify', label: '接收通知', type: 'switch' }
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
// 2. 初始化全局 State 初始值
|
|
|
|
|
// 使用 window.State 因为 state.js 已经同步加载完毕
|
|
|
|
|
Object.assign(window.State, {
|
|
|
|
|
currentSection: 'welcome',
|
|
|
|
|
testTitle: 'Synchronous State Ready',
|
|
|
|
|
testShow: true,
|
|
|
|
|
testColor: 'blue',
|
|
|
|
|
testItems: ['Alpha', 'Beta', 'Gamma'],
|
|
|
|
|
showOuter: true,
|
|
|
|
|
innerItems: ['Red', 'Green', 'Blue'],
|
|
|
|
|
members: [
|
|
|
|
|
{ id: 1, name: 'Alice (1)', odd: true },
|
|
|
|
|
{ id: 2, name: 'Bob (2)', odd: false },
|
|
|
|
|
{ id: 3, name: 'Charlie (3)', odd: true },
|
|
|
|
|
{ id: 4, name: 'David (4)', odd: false },
|
|
|
|
|
{ id: 5, name: 'Eve (5)', odd: true }
|
|
|
|
|
],
|
|
|
|
|
showOddOnly: false,
|
|
|
|
|
listData: [],
|
|
|
|
|
listGroups: [
|
|
|
|
|
{ id: 'group1', label: '开发团队', icon: 'code-slash' },
|
|
|
|
|
{ id: 'group2', label: '设计团队', icon: 'palette' }
|
|
|
|
|
],
|
|
|
|
|
listMode: 'tree',
|
|
|
|
|
formData: { username: 'ApigoUser', theme: '#0d6efd', tags: ['Fast'] },
|
|
|
|
|
apiResult: null
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!Hash.nav) Hash.nav = 'welcome';
|
|
|
|
|
</script>
|
|
|
|
|
</head>
|
|
|
|
|
<body $data-bs-theme="LocalStorage.darkMode ? 'dark' : 'light'">
|
|
|
|
|
|
|
|
|
|
<div class="main-container">
|
|
|
|
|
<!-- 侧边栏导航 -->
|
|
|
|
|
<div id="sidebar" class="sidebar bg-body-tertiary border-end" style="width: 260px;">
|
|
|
|
|
<Nav id="mainNav" vertical class="flex-fill"
|
|
|
|
|
$.state.brand="window.brand"
|
|
|
|
|
$.state.list="window.navList"
|
|
|
|
|
$onnav="State.currentSection = event.detail.item.name">
|
|
|
|
|
</Nav>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 拖拽调节器 -->
|
|
|
|
|
<Resizer target="sidebar" min="150" max="450"></Resizer>
|
|
|
|
|
|
|
|
|
|
<!-- 主内容区 -->
|
|
|
|
|
<div class="content bg-body">
|
|
|
|
|
<div class="container-fluid">
|
|
|
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
|
|
|
<h2 $text="window.navList.find(s => s.name === State.currentSection)?.label || '欢迎'"></h2>
|
|
|
|
|
<div class="d-flex gap-2">
|
|
|
|
|
<button class="btn btn-outline-secondary btn-sm" onclick="LocalStorage.darkMode = !LocalStorage.darkMode">
|
|
|
|
|
<i class="bi" $class="LocalStorage.darkMode ? 'bi-sun' : 'bi-moon'"></i>
|
|
|
|
|
切换主题
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 1. 表单展示 -->
|
|
|
|
|
<template $if="State.currentSection === 'forms'">
|
|
|
|
|
<div class="section-card">
|
|
|
|
|
<div class="section-header">AutoForm & 扩展控件</div>
|
|
|
|
|
<div class="section-body">
|
|
|
|
|
<div class="row">
|
|
|
|
|
<div class="col-md-8">
|
|
|
|
|
<AutoForm id="demoForm" vertical
|
|
|
|
|
$.state.schema="window.formSchema"
|
2026-06-08 21:57:18 +08:00
|
|
|
$.state.data="State.formData"
|
2026-06-06 11:45:53 +08:00
|
|
|
$onsubmit="UI.toast('提交数据: ' + JSON.stringify(event.detail))">
|
|
|
|
|
</AutoForm>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="col-md-4">
|
|
|
|
|
<h6>实时数据预览:</h6>
|
|
|
|
|
<pre class="bg-light p-2 border rounded" $text="JSON.stringify(State.formData, null, 2)"></pre>
|
|
|
|
|
<button class="btn btn-primary w-100" onclick="resetFormData()">重置数据</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<!-- 2. 列表展示 -->
|
|
|
|
|
<template $if="State.currentSection === 'lists'">
|
|
|
|
|
<div class="section-card">
|
|
|
|
|
<div class="section-header">List 增强列表 (虚拟滚动 + 树形)</div>
|
|
|
|
|
<div class="section-body">
|
|
|
|
|
<div class="mb-3 d-flex gap-2">
|
|
|
|
|
<button class="btn btn-sm btn-outline-primary" onclick="State.listMode = 'normal'">普通列表</button>
|
|
|
|
|
<button class="btn btn-sm btn-outline-primary" onclick="State.listMode = 'group'">分组列表</button>
|
|
|
|
|
<button class="btn btn-sm btn-outline-primary" onclick="State.listMode = 'tree'">树形列表</button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div style="height: 500px;" class="border rounded overflow-hidden">
|
|
|
|
|
<List id="demoList" fast collapsible auto-select class="h-100 overflow-auto"
|
|
|
|
|
$mode="State.listMode"
|
2026-06-08 21:57:18 +08:00
|
|
|
$.state.list="State.listData"
|
2026-06-06 11:45:53 +08:00
|
|
|
$.state.groups="State.listGroups"
|
|
|
|
|
$onitemclick="UI.toast('点击项目: ' + event.detail.item.label)">
|
|
|
|
|
<template slot-id="item">
|
|
|
|
|
<i class="bi me-2" $class="item.icon || 'bi-file-earmark'"></i>
|
|
|
|
|
<span $text="item.label"></span>
|
|
|
|
|
<span class="ms-auto badge bg-secondary" $text="item.id"></span>
|
|
|
|
|
</template>
|
|
|
|
|
</List>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<!-- 3. 交互展示 -->
|
|
|
|
|
<template $if="State.currentSection === 'ui'">
|
|
|
|
|
<div class="section-card">
|
|
|
|
|
<div class="section-header">UI 交互工具</div>
|
|
|
|
|
<div class="section-body">
|
|
|
|
|
<div class="row g-3">
|
|
|
|
|
<div class="col-md-4"><button class="btn btn-info w-100" onclick="UI.toast('这是一个通知消息')">Toast (Info)</button></div>
|
|
|
|
|
<div class="col-md-4"><button class="btn btn-danger w-100" onclick="UI.toast('操作失败!', { type: 'danger' })">Toast (Danger)</button></div>
|
|
|
|
|
<div class="col-md-4"><button class="btn btn-success w-100" onclick="UI.toastConfirm('你确定要完成吗?').then(r => UI.toast(r ? '已确认' : '已取消'))">Toast Confirm</button></div>
|
|
|
|
|
<div class="col-md-4"><button class="btn btn-primary w-100" onclick="UI.alert('这是一个警告框')">UI.alert</button></div>
|
|
|
|
|
<div class="col-md-4"><button class="btn btn-warning w-100" onclick="UI.confirm('你确定要删除吗?').then(r => UI.toast(r ? '已点击确认' : '已点击取消'))">UI.confirm</button></div>
|
|
|
|
|
<div class="col-md-4"><button class="btn btn-dark w-100" onclick="showComplexDialog()">UI.showDialog</button></div>
|
|
|
|
|
<div class="col-md-4"><button class="btn btn-secondary w-100" onclick="testHTTP()">HTTP.get Test</button></div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<!-- 4. 网络展示 -->
|
|
|
|
|
<template $if="State.currentSection === 'api'">
|
|
|
|
|
<div class="section-card">
|
|
|
|
|
<div class="section-header">API 声明式请求</div>
|
|
|
|
|
<div class="section-body">
|
|
|
|
|
<API id="userApi"
|
|
|
|
|
$request="{ url: 'https://jsonplaceholder.typicode.com/users/1', method: 'GET' }"
|
|
|
|
|
$onresponse="State.apiResult = event.detail.result"
|
|
|
|
|
$onerror="UI.toast('API 请求失败: ' + event.detail.error, { type: 'danger' })">
|
|
|
|
|
</API>
|
|
|
|
|
<div class="d-flex gap-2 mb-3">
|
|
|
|
|
<button class="btn btn-primary" onclick="userApi.do()">触发请求</button>
|
|
|
|
|
<button class="btn btn-outline-secondary" onclick="State.apiResult = null">清空结果</button>
|
|
|
|
|
</div>
|
|
|
|
|
<h6>请求结果 (JSONPlaceholder):</h6>
|
|
|
|
|
<pre class="bg-light p-2 border rounded" $text="State.apiResult ? JSON.stringify(State.apiResult, null, 2) : '等待请求...'"></pre>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<!-- 默认页 -->
|
|
|
|
|
<template $if="State.currentSection === 'welcome'">
|
|
|
|
|
<div class="text-center py-5">
|
|
|
|
|
<i class="bi bi-rocket-takeoff text-primary" style="font-size: 5rem;"></i>
|
|
|
|
|
<h1 class="mt-3">Apigo Base Mega Demo</h1>
|
|
|
|
|
<p class="text-muted">点击左侧菜单查看不同组件的能力展示</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="mt-5 p-4 border rounded bg-light">
|
|
|
|
|
<h4 class="mb-4 border-bottom pb-2 text-start">核心原子能力验证 (State singleton)</h4>
|
|
|
|
|
|
|
|
|
|
<div class="row g-4 text-start">
|
|
|
|
|
<!-- 1. $text & style binding -->
|
|
|
|
|
<div class="col-md-6">
|
|
|
|
|
<div class="card h-100">
|
|
|
|
|
<div class="card-header">1. $text & 样式绑定</div>
|
|
|
|
|
<div class="card-body text-center">
|
|
|
|
|
<p class="fs-4 fw-bold" $text="State.testTitle" $style="'color:' + State.testColor"></p>
|
|
|
|
|
<button class="btn btn-sm btn-outline-primary" onclick="State.testColor = State.testColor === 'blue' ? 'green' : 'blue'">切换颜色</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 2. $if (True/False) -->
|
|
|
|
|
<div class="col-md-6">
|
|
|
|
|
<div class="card h-100">
|
|
|
|
|
<div class="card-header">2. $if 显式模板判断</div>
|
|
|
|
|
<div class="card-body text-center">
|
|
|
|
|
<template $if="State.testShow">
|
|
|
|
|
<div class="alert alert-success py-2">已激活 ($if="true")</div>
|
|
|
|
|
</template>
|
|
|
|
|
<button class="btn btn-sm btn-outline-primary mt-2" onclick="State.testShow = !State.testShow">切换显示状态</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 3. $each (Simple) -->
|
|
|
|
|
<div class="col-md-6">
|
|
|
|
|
<div class="card h-100">
|
|
|
|
|
<div class="card-header">3. $each 循环渲染</div>
|
|
|
|
|
<div class="card-body">
|
|
|
|
|
<div class="list-group list-group-flush border rounded">
|
|
|
|
|
<template $each="State.testItems">
|
|
|
|
|
<div class="list-group-item py-1" $text="item"></div>
|
|
|
|
|
</template>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 4. Nested each inside if -->
|
|
|
|
|
<div class="col-md-6">
|
|
|
|
|
<div class="card h-100">
|
|
|
|
|
<div class="card-header">4. $if 嵌套 $each</div>
|
|
|
|
|
<div class="card-body text-center">
|
|
|
|
|
<template $if="State.showOuter">
|
|
|
|
|
<div class="p-2 border rounded bg-white mb-2">
|
|
|
|
|
<template $each="State.innerItems">
|
|
|
|
|
<span class="badge bg-secondary me-1" $text="item"></span>
|
|
|
|
|
</template>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
<button class="btn btn-sm btn-outline-primary" onclick="State.showOuter = !State.showOuter">切换外层容器</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 5. Nested if inside each (Filter) -->
|
|
|
|
|
<div class="col-md-12">
|
|
|
|
|
<div class="card">
|
|
|
|
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
|
|
|
5. $each 嵌套 $if (奇偶成员过滤)
|
|
|
|
|
<button class="btn btn-xs btn-primary py-0" onclick="State.showOddOnly = !State.showOddOnly" $text="State.showOddOnly ? '显示全部' : '仅看奇数项'"></button>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="card-body">
|
|
|
|
|
<div class="d-flex flex-wrap gap-2">
|
|
|
|
|
<template $each="State.members">
|
|
|
|
|
<template $if="!State.showOddOnly || item.odd">
|
|
|
|
|
<div class="p-2 border rounded bg-white shadow-sm">
|
|
|
|
|
<i class="bi bi-person-fill text-primary"></i>
|
|
|
|
|
<span $text="item.name"></span>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
</template>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<script src="./capability.js"></script>
|
|
|
|
|
</body>
|
|
|
|
|
</html>
|