base/test/capability.html

298 lines
18 KiB
HTML
Raw Permalink Normal View History

<!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"
$.state.data="State.formData"
$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"
$.state.list="State.listData"
$.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>