Skip to main content

sui-development

Guide for developing web applications using Yao SUI framework. Use when building SUI pages, components, templates, handling data binding, event handling, backend scripts, routing, i18n, or integrating with CUI. Trigger when user mentions SUI, Yao web development, page templates, or frontend/backend integration in Yao applications.

跳到安装

来源信息

仓库
YaoApp/yao-init
最近来源活动
2026年4月5日 12:49
检测到的 SKILL.md 语言
英语
星标
1
分支
4

安装方式

默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。

检查来源文件

决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。

文件资源管理器
4 个文件

正在显示 SKILL.md

SKILL.md
来源说明 · 只读预览
name
sui-development
description
Guide for developing web applications using Yao SUI framework. Use when building SUI pages, components, templates, handling data binding, event handling, backend scripts, routing, i18n, or integrating with CUI. Trigger when user mentions SUI, Yao web development, page templates, or frontend/backend integration in Yao applications.
# SUI Development Guide SUI (Simple UI) is Yao's built-in web framework for building server-rendered pages with reactive components. ## Core Concepts 1. **File-based routing** - Directory structure defines URL routes 2. **Page = Component** - Every page can be used as a component 3. **Server-side rendering** - Templates rendered on server with data binding 4. **Progressive enhancement** - Frontend scripts add interactivity ## Directory Structure ``` /suis/<sui>/templates/<template>/ ├── __document.html # Global document wrapper ├── __data.json # Global data ($global) ├── __assets/ # Static assets ├── __locales/ # i18n files └── pages/ └── <page>/ # Route = folder name ├── <page>.html # Template ├── <page>.css # Styles (auto-scoped) ├── <page>.ts # Frontend script ├── <page>.json # Page data config ├── <page>.config # Page settings (guard, cache, SEO) └── <page>.backend.ts # Server-side logic ``` ## Build Commands ```bash yao sui build <sui> <template> # Build for production yao sui build <sui> <template> -D # Development mode yao sui watch <sui> <template> # Watch mode yao sui trans <sui> <template> # Extract i18n strings ``` --- # Template Syntax ## Data Interpolation ```html {{ name }} <!-- Variable --> {{ user.email }} <!-- Nested --> {{ title ?? 'Default' }} <!-- Default value --> {{ price * quantity }} <!-- Expression --> {{ count > 0 ? 'Yes' : 'No' }} <!-- Ternary --> ``` ## Conditionals ```html <div s:if="{{ isActive }}">Active</div> <div s:elif="{{ isPending }}">Pending</div> <div s:else>Unknown</div> <!-- Operators: ==, !=, >, <, >=, <=, &&, ||, ! --> <div s:if="{{ isAdmin && isActive }}">Admin Panel</div> ``` ## Loops ```html <li s:for="{{ items }}" s:for-item="item" s:for-index="i"> {{ i + 1 }}. {{ item.name }} </li> <!-- With conditional --> <div s:for="{{ users }}" s:for-item="user" s:if="{{ user.active }}"> {{ user.name }} </div> ``` ## Attribute Binding ```html <a href="{{ '/user/' + id }}">Link</a> <button s:attr-disabled="{{ !valid }}">Submit</button> <div class="base {{ isActive ? 'active' : '' }}">Content</div> <div ...props></div> <!-- Spread --> ``` ## Built-in Variables | Variable | Description | Example | | ---------- | ------------------------ | --------------------- | | `$param` | Route parameters | `{{ $param.id }}` | | `$query` | URL query params | `{{ $query.search }}` | | `$payload` | POST body | `{{ $payload.name }}` | | `$global` | Global data | `{{ $global.title }}` | | `$theme` | Current theme | `{{ $theme }}` | | `$locale` | Current locale | `{{ $locale }}` | | `$auth` | OAuth info (when guarded)| `{{ $auth.user_id }}` | ## Built-in Functions ```html {{ P_('models.user.Find', id) }} <!-- Call process --> {{ True(user) }} <!-- Truthy check --> {{ Empty(items) }} <!-- Empty check --> {{ len(items) }} <!-- Array length --> {{ filter(items, .active) }} <!-- Filter array --> ``` --- # Data Binding ## Page Data (`<page>.json`) ```json { "title": "Static value", "$users": "models.user.Get", "$user": { "process": "models.user.Find", "args": ["$param.id"] }, "$items": { "process": "@GetItems", "args": ["active", 10] } } ``` - Keys with `$` prefix call processes - `@MethodName` calls backend script functions - Use `$param`, `$query`, `$payload` in args ## Backend Script (`<page>.backend.ts`) There are three types of backend functions, each with different auth mechanisms: ```typescript // Type declarations for auth interface AuthorizedInfo { user_id?: string; email?: string; team_id?: string; owner_id?: string; } declare function Authorized(): AuthorizedInfo | null; // 1. BeforeRender — called before page render // Auth: request.authorized (request is the first arg) function BeforeRender(request: Request): Record<string, any> { const id = request.params.id; // NOT $param.id return { user: Process("models.user.Find", id), items: Process("models.item.Get", { limit: 10 }), }; } // 2. ApiXxx — called via $Backend().Call("GetUsers") // Auth: Authorized() global function (request is NOT passed) // Args: only what frontend sends function ApiGetUsers(): any[] { const auth = Authorized(); if (!auth?.user_id) throw new Error("unauthorized"); return Process("models.user.Get", { wheres: [{ column: "user_id", value: auth.user_id }], }); } // 3. @FuncName — called from .json data binding // Auth: request.authorized (request is appended as last arg by SUI core) function GetRecord(request: Request): any { return Process("models.record.Find", request.params.id); } ``` > **Important**: `$param` is NOT available in backend scripts. Use `request.params`. > > **CRITICAL**: `ApiXxx` functions called via `$Backend().Call` do NOT receive a `request` > parameter. Use the `Authorized()` global function for user identity. See > `references/backend-api.md` for the full explanation and Go runtime source references. --- # Components Every page is a component. Use `is` attribute or `<import>` to embed: ```html <!-- Using is attribute --> <div is="/card" title="My Card"> <p>Content</p> </div> <!-- Using import --> <import s:as="Card" s:from="/card" /> <Card title="My Card"><p>Content</p></Card> ``` ## Component Structure **`/card/card.html`**: ```html <div class="card"> <h3>{{ title }}</h3> <div class="body"><children></children></div> </div> ``` ## Named Slots ```html <!-- Component --> <div class="modal"> <div class="header"><slot name="header"></slot></div> <div class="body"><children></children></div> <div class="footer"><slot name="footer"></slot></div> </div> <!-- Usage --> <div is="/modal"> <slot name="header"><h2>Title</h2></slot> <p>Body content</p> <slot name="footer"><button>OK</button></slot> </div> ``` ## Props Access **Frontend** (`card.ts`): ```typescript import { Component } from "@yao/sui"; const self = this as Component; const title = self.props.Get("title"); const allProps = self.props.List(); ``` **Backend** (`card.backend.ts`): ```typescript function BeforeRender(request: Request, props: Record<string, any>): Record<string, any> { const userId = props.userId; return { user: Process("models.user.Find", userId) }; } ``` --- # Event Handling ## Event Binding ```html <button s:on-click="HandleClick">Click</button> <input s:on-input="HandleInput" /> <form s:on-submit="HandleSubmit">...</form> <!-- With data --> <button s:on-click="DeleteItem" s:data-id="{{ item.id }}" s:json-item="{{ item }}"> Delete </button> ``` ## Event Handler ```typescript import { $Backend, Component, EventData } from "@yao/sui"; const self = this as Component; self.DeleteItem = async (event: Event, data: EventData) => { const id = data.id; // From s:data-id const item = data.item; // From s:json-item await $Backend().Call("DeleteItem", id); (event.target as HTMLElement).closest(".item")?.remove(); }; self.HandleSubmit = async (event: Event) => { event.preventDefault(); const form = event.target as HTMLFormElement; const formData = new FormData(form); await $Backend().Call("Submit", Object.fromEntries(formData)); }; ``` ## State Management ```typescript const self = this as Component; // Set state self.state.Set("count", 0); // Watch changes self.watch = { count: (value: number) => { self.root.querySelector(".count")!.textContent = String(value); }, }; // Trigger update self.Increment = () => { const count = self.state.Get("count") || 0; self.state.Set("count", count + 1); }; ``` --- # Frontend API ## Backend Calls ```typescript import { $Backend } from "@yao/sui"; // Calls ApiGetUsers() in backend script — no request param is passed const users = await $Backend().Call("GetUsers"); // Calls ApiGetUser(123) — args are passed positionally const user = await $Backend().Call("GetUser", 123);
在 GitHub 查看
这个 SKILL.md 很大,SkillsMP 这里只预览前一段内容。 在 GitHub 查看