大更新
This commit is contained in:
@@ -547,3 +547,100 @@
|
||||
justify-content: flex-start;
|
||||
}
|
||||
}
|
||||
.case-summary {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
margin-top: 18px;
|
||||
}
|
||||
.case-summary article {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
padding: 15px;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 12px;
|
||||
}
|
||||
.case-summary article > span,
|
||||
.doc-case-tabs button span {
|
||||
color: #2563eb;
|
||||
font-size: 11px;
|
||||
font-weight: 800;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.case-summary small {
|
||||
color: #64748b;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.lab-grid {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(280px, 0.8fr) minmax(0, 1.2fr);
|
||||
gap: 14px;
|
||||
margin: 18px 0;
|
||||
}
|
||||
.lab-controls {
|
||||
padding: 17px;
|
||||
border: 1px solid #dbe4f0;
|
||||
border-radius: 12px;
|
||||
background: #fbfdff;
|
||||
}
|
||||
.lab-controls h3 {
|
||||
margin: 0 0 8px;
|
||||
}
|
||||
.lab-controls p {
|
||||
margin: 0 0 14px;
|
||||
color: #64748b;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.lab-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
.lab-controls button,
|
||||
.explain-box button {
|
||||
padding: 8px 11px;
|
||||
border: 1px solid #bfdbfe;
|
||||
border-radius: 8px;
|
||||
color: #1d4ed8;
|
||||
background: #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
.lab-controls button:disabled,
|
||||
.explain-box button:disabled {
|
||||
cursor: default;
|
||||
opacity: 0.55;
|
||||
}
|
||||
.boundary-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 14px;
|
||||
}
|
||||
.boundary-card {
|
||||
min-width: 0;
|
||||
padding: 15px;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 12px;
|
||||
background: #fff;
|
||||
}
|
||||
.boundary-card h3 {
|
||||
margin: 0 0 10px;
|
||||
}
|
||||
.boundary-card > pre {
|
||||
margin: 0 0 12px;
|
||||
padding: 10px;
|
||||
border-radius: 8px;
|
||||
background: #0f172a;
|
||||
color: #e2e8f0;
|
||||
overflow: auto;
|
||||
}
|
||||
.doc-case-tabs button {
|
||||
display: inline-grid;
|
||||
gap: 2px;
|
||||
}
|
||||
@media (max-width: 980px) {
|
||||
.case-summary,
|
||||
.lab-grid,
|
||||
.boundary-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
+138
-57
File diff suppressed because one or more lines are too long
Vendored
+35
@@ -0,0 +1,35 @@
|
||||
export type Amis_Schema = Record<string, unknown>;
|
||||
export interface Composition_Node {
|
||||
kind: string;
|
||||
slot?: string;
|
||||
children?: Composition_Node[];
|
||||
}
|
||||
export interface Composition_View {
|
||||
protocol: string;
|
||||
protocol_version: number;
|
||||
kind: string;
|
||||
name: string;
|
||||
title?: string;
|
||||
body: Composition_Node;
|
||||
}
|
||||
export interface Slot_Contract {
|
||||
component_kind: string;
|
||||
descriptor_api?: string;
|
||||
view_api?: string;
|
||||
data_api?: string;
|
||||
amis_api?: string;
|
||||
schema: Amis_Schema;
|
||||
}
|
||||
export interface Frontend_Manifest {
|
||||
protocol: string;
|
||||
protocol_version: number;
|
||||
view: Composition_View;
|
||||
slots: Record<string, Slot_Contract>;
|
||||
}
|
||||
export interface Ordered_Slot {
|
||||
name: string;
|
||||
slot: Slot_Contract;
|
||||
}
|
||||
export function collect_slot_names(node: Composition_Node, result?: string[]): string[];
|
||||
export function ordered_slots(manifest: Frontend_Manifest): Ordered_Slot[];
|
||||
export function resolve_renderer_kind(component_kind: string, registered: ReadonlySet<string>): string;
|
||||
@@ -0,0 +1,18 @@
|
||||
export function collect_slot_names(node, result = []) {
|
||||
if(node.kind === 'slot' && node.slot) {
|
||||
result.push(node.slot);
|
||||
}
|
||||
for(const child of node.children ?? []) {
|
||||
collect_slot_names(child, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
export function ordered_slots(manifest) {
|
||||
return collect_slot_names(manifest.view.body).map(name => ({name, slot: manifest.slots[name]})).filter(entry => Boolean(entry.slot));
|
||||
}
|
||||
export function resolve_renderer_kind(component_kind, registered) {
|
||||
if(!registered.has(component_kind)) {
|
||||
throw new Error(`未注册的 component_kind: ${component_kind}`);
|
||||
}
|
||||
return component_kind;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import {describe, it} from 'node:test';
|
||||
import {collect_slot_names, ordered_slots, resolve_renderer_kind} from './gallery_protocol.js';
|
||||
describe('gallery protocol', () => {
|
||||
it('recursively preserves backend slot order', () => {
|
||||
assert.deepEqual(collect_slot_names({kind: 'frontend', children: [{kind: 'slot', slot: 'alpha'}, {kind: 'group', children: [{kind: 'slot', slot: 'beta'}]}]}), ['alpha', 'beta']);
|
||||
});
|
||||
it('uses the manifest registry instead of business field knowledge', () => {
|
||||
const manifest = {protocol: 'adminive.composition-manifest', protocol_version: 1, view: {protocol: 'adminive.view', protocol_version: 1, kind: 'composition', name: 'demo', body: {kind: 'frontend', children: [{kind: 'slot', slot: 'one'}, {kind: 'slot', slot: 'two'}]}}, slots: {one: {component_kind: 'amis_schema', schema: {type: 'tpl'}}, two: {component_kind: 'amis_schema', schema: {type: 'form'}}}};
|
||||
assert.deepEqual(ordered_slots(manifest).map(item => item.name), ['one', 'two']);
|
||||
});
|
||||
it('rejects an unregistered component kind', () => {
|
||||
assert.throws(() => resolve_renderer_kind('vue_component', new Set(['amis_schema'])), /未注册的 component_kind/);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user