fix
This commit is contained in:
@@ -1,45 +0,0 @@
|
||||
# Xiaomi Vela JS Application Guidelines
|
||||
|
||||
## Project Overview
|
||||
|
||||
This is a Xiaomi Vela JS quickapp (快应用) targeting smartwatch/wearable devices. Use the `vela-quickapp` skill for Vela-specific tasks.
|
||||
|
||||
## Code Style
|
||||
|
||||
- All directory names use **lowercase** (`common/`, not `Common/`)
|
||||
- Resource paths in manifest.json and CSS use absolute paths (`/common/icon.png`)
|
||||
- Code imports use relative paths (`../common/utils`)
|
||||
- Style units are `px` (logical pixels, auto-scaled by framework)
|
||||
- Use Flexbox layout exclusively (`flex-direction`, `justify-content`, `align-items`)
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
src/
|
||||
├── manifest.json # App config (package, router, config fields are REQUIRED)
|
||||
├── app.ux # App entry point, import shared utilities here
|
||||
├── pages/ # Each page = one directory with one .ux file
|
||||
└── common/ # Shared resources: styles, utils, images
|
||||
```
|
||||
|
||||
- Each page is a single `.ux` file with `<template>`, `<style>`, `<script>` sections
|
||||
- `<template>` must have exactly one root node
|
||||
- Data model uses `private: {}` in script (not `data`)
|
||||
- Use `@system.*` imports for system APIs; declare in manifest `features` first
|
||||
|
||||
## Build and Test
|
||||
|
||||
```bash
|
||||
npm install # Install dependencies (includes aiot-toolkit)
|
||||
node build.js # Build debug .rpk → dist/
|
||||
node build.js --release # Build release .rpk (requires sign/ with certs)
|
||||
```
|
||||
|
||||
## Conventions
|
||||
|
||||
- **manifest.json `config` field is mandatory** — omitting it causes build failure
|
||||
- **Router paths must match directory structure** — `"entry": "pages/index"` maps to `src/pages/index/index.ux`
|
||||
- **`component` value must match .ux filename** without extension
|
||||
- **`designWidth`**: 480 for watch, adjust for other device types
|
||||
- Use `{{}}` for data binding, `for="{{list}}"` for iteration, `if="{{cond}}"` for conditionals
|
||||
- Event binding: `onclick="handler"` or `@click="handler"` (shorthand)
|
||||
@@ -1,92 +0,0 @@
|
||||
---
|
||||
description: "Use when creating or editing .ux files for Xiaomi Vela JS applications. Covers template structure, data binding, event handling, list rendering, conditional rendering, style conventions, and script lifecycle."
|
||||
applyTo: "**/*.ux"
|
||||
---
|
||||
|
||||
# UX File Guidelines
|
||||
|
||||
## File Structure
|
||||
|
||||
Every `.ux` file has exactly three sections in this order:
|
||||
|
||||
```html
|
||||
<template>
|
||||
<!-- single root node only -->
|
||||
</template>
|
||||
|
||||
<style>
|
||||
/* Flexbox-only layout */
|
||||
</style>
|
||||
|
||||
<script>
|
||||
// MVVM data model
|
||||
</script>
|
||||
```
|
||||
|
||||
## Template Rules
|
||||
|
||||
- **Single root node** — `<template>` must contain exactly one root `<div>`
|
||||
- **Never use `<block>` as root** — only as a child for logical grouping
|
||||
- **Data binding**: `{{variable}}` — no expressions with side effects
|
||||
- **Events**: `onclick="handler"` or `@click="handler"` shorthand
|
||||
- **Lists**: `for="{{list}}"` with `tid="uniqueField"` for performance
|
||||
- **Conditionals**: `if`/`elif`/`else` must be adjacent sibling nodes
|
||||
|
||||
### List Rendering
|
||||
|
||||
```html
|
||||
<!-- Default names: $item, $idx -->
|
||||
<div for="{{items}}" tid="id">
|
||||
<text>{{$idx}}: {{$item.name}}</text>
|
||||
</div>
|
||||
|
||||
<!-- Custom names -->
|
||||
<div for="{{(index, item) in items}}">
|
||||
<text>{{index}}: {{item.name}}</text>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Conditional Rendering
|
||||
|
||||
```html
|
||||
<text if="{{status === 'ok'}}">Success</text>
|
||||
<text elif="{{status === 'error'}}">Failed</text>
|
||||
<text else>Loading</text>
|
||||
```
|
||||
|
||||
- `if` removes from VDOM when false; `show` just hides (keeps in VDOM)
|
||||
|
||||
## Style Rules
|
||||
|
||||
- Use **Flexbox** exclusively — no float, no grid
|
||||
- Units are **`px`** only (logical pixels, auto-scaled)
|
||||
- Prefer `class` over inline `style` for reusable patterns
|
||||
- Resource paths in `url()` use **absolute paths**: `url(/common/bg.png)`
|
||||
|
||||
## Script Rules
|
||||
|
||||
- Use `private: {}` for page data (not `data`)
|
||||
- `private` properties are **not overridable** by parent components
|
||||
- Import system APIs: `import router from '@system.router'`
|
||||
- Declare used APIs in manifest.json `features` array first
|
||||
|
||||
### Lifecycle Hooks
|
||||
|
||||
```javascript
|
||||
export default {
|
||||
private: { /* reactive data */ },
|
||||
onInit() {}, // data initialized
|
||||
onReady() {}, // DOM rendered
|
||||
onShow() {}, // page visible
|
||||
onHide() {}, // page hidden
|
||||
onDestroy() {} // page destroyed
|
||||
}
|
||||
```
|
||||
|
||||
## Common Mistakes
|
||||
|
||||
- Multiple root nodes in `<template>` → wrap in single `<div>`
|
||||
- Using relative paths in manifest.json `icon` → use `/common/icon.png`
|
||||
- Missing `config` field in manifest.json → build fails
|
||||
- Router path doesn't match directory → page not found
|
||||
- `for` without `tid` → poor list update performance
|
||||
@@ -1,226 +0,0 @@
|
||||
---
|
||||
name: vela-quickapp
|
||||
description: 'Create, build, and debug Xiaomi Vela JS applications (快应用). Use when: creating new Vela quickapp projects; building .rpk packages; writing UX template files; configuring manifest.json; developing for Xiaomi smartwatch/wearable devices; working with aiot-toolkit CLI; debugging Vela app compilation errors. DO NOT use for: general web development; React/Vue/Angular projects; native C/C++ Vela OS development.'
|
||||
argument-hint: '[action: create|build|debug|explain] [topic]'
|
||||
---
|
||||
|
||||
# Xiaomi Vela JS Application Development
|
||||
|
||||
## Overview
|
||||
|
||||
Xiaomi Vela JS 应用是一种基于 Xiaomi Vela OS 的轻量级应用,面向智能穿戴设备(手表等)。采用前端 MVVM 开发范式,使用 JavaScript + 类 HTML 模板 + CSS 样式。
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
project-root/
|
||||
├── src/
|
||||
│ ├── manifest.json # 必需:应用配置
|
||||
│ ├── app.ux # 必需:应用入口
|
||||
│ ├── pages/
|
||||
│ │ ├── index/
|
||||
│ │ │ └── index.ux # 主页面
|
||||
│ │ └── detail/
|
||||
│ │ └── detail.ux # 其他页面
|
||||
│ ├── common/ # 公共资源(注意小写)
|
||||
│ │ ├── style.css
|
||||
│ │ ├── utils.js
|
||||
│ │ └── icon.png
|
||||
│ └── i18n/ # 可选:多语言
|
||||
│ ├── defaults.json
|
||||
│ └── zh-CN.json
|
||||
├── sign/ # 签名文件(release 模式需要)
|
||||
│ ├── private.pem
|
||||
│ └── certificate.pem
|
||||
├── package.json
|
||||
└── build.js # 构建脚本
|
||||
```
|
||||
|
||||
## manifest.json (Required Fields)
|
||||
|
||||
```json
|
||||
{
|
||||
"package": "com.example.appname",
|
||||
"name": "应用名称",
|
||||
"icon": "/common/icon.png",
|
||||
"versionName": "1.0",
|
||||
"versionCode": 1,
|
||||
"minAPILevel": 1,
|
||||
"features": [],
|
||||
"config": {
|
||||
"logLevel": "log",
|
||||
"designWidth": 480
|
||||
},
|
||||
"router": {
|
||||
"entry": "pages/index",
|
||||
"pages": {
|
||||
"pages/index": { "component": "index" }
|
||||
}
|
||||
},
|
||||
"display": {
|
||||
"backgroundColor": "#ffffff"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Critical Rules
|
||||
- `config` 字段是**必需的**,缺少会导致构建失败
|
||||
- `router.entry` 和 `router.pages` 的 key 必须与 `src/` 下的目录路径一致
|
||||
- `component` 值必须与 `.ux` 文件名一致(不含扩展名)
|
||||
- `icon` 路径使用绝对路径(以 `/` 开头),指向 `src/` 下的资源
|
||||
- `designWidth` 根据目标设备设置(手表常用 480)
|
||||
|
||||
## UX File Syntax
|
||||
|
||||
每个页面由一个 `.ux` 文件定义,包含三个部分:
|
||||
|
||||
```html
|
||||
<template>
|
||||
<!-- 只能有一个根节点 -->
|
||||
<div class="page">
|
||||
<text>{{message}}</text>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.page {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
private: {
|
||||
message: 'Hello Vela'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
### Template 语法
|
||||
|
||||
| 特性 | 语法 | 示例 |
|
||||
|------|------|------|
|
||||
| 数据绑定 | `{{variable}}` | `<text>{{title}}</text>` |
|
||||
| 事件绑定 | `onclick="fn"` 或 `@click="fn"` | `<div @click="handleTap">` |
|
||||
| 列表渲染 | `for="{{list}}"` | `<div for="{{items}}" tid="id">` |
|
||||
| 条件渲染 | `if/elif/else` | `<text if="{{show}}">` |
|
||||
| 显示隐藏 | `show="{{visible}}"` | `<div show="{{visible}}">` |
|
||||
|
||||
### 列表渲染详细
|
||||
|
||||
```html
|
||||
<div for="{{list}}" tid="uniqueId">
|
||||
<text>{{$idx}}: {{$item.name}}</text>
|
||||
</div>
|
||||
|
||||
<!-- 自定义变量名 -->
|
||||
<div for="{{(index, item) in list}}">
|
||||
<text>{{index}}: {{item.name}}</text>
|
||||
</div>
|
||||
```
|
||||
|
||||
`tid` 属性指定数组元素唯一标识,优化渲染性能。必须保证该属性值在每个元素中唯一。
|
||||
|
||||
### Style 语法
|
||||
|
||||
- 类似 CSS,使用 Flexbox 布局
|
||||
- 单位为 `px`(逻辑像素,框架自动适配不同屏幕)
|
||||
- 支持 `class` 和 `style` 属性
|
||||
- `style` 可以是 string 或 object
|
||||
|
||||
### Script 语法
|
||||
|
||||
```javascript
|
||||
import router from '@system.router'
|
||||
|
||||
export default {
|
||||
// 页面私有数据(不可被覆盖)
|
||||
private: {
|
||||
title: '页面标题',
|
||||
list: []
|
||||
},
|
||||
|
||||
// 生命周期
|
||||
onInit() { /* 页面初始化 */ },
|
||||
onReady() { /* 页面渲染完成 */ },
|
||||
onShow() { /* 页面显示 */ },
|
||||
onHide() { /* 页面隐藏 */ },
|
||||
onDestroy() { /* 页面销毁 */ },
|
||||
|
||||
// 自定义方法
|
||||
handleClick() {
|
||||
router.push({ uri: '/pages/detail' })
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Build Process
|
||||
|
||||
### 安装依赖
|
||||
|
||||
```bash
|
||||
npm install # 安装 aiot-toolkit
|
||||
```
|
||||
|
||||
### 构建命令
|
||||
|
||||
```bash
|
||||
# 开发模式(生成 .debug.rpk)
|
||||
npx aiot build
|
||||
|
||||
# 生产模式(生成 .release.rpk,需要 sign/ 下的签名文件)
|
||||
npx aiot release
|
||||
```
|
||||
|
||||
### 构建产物
|
||||
|
||||
- `dist/` — 最终 rpk 文件
|
||||
- `build/` — webpack 编译中间产物
|
||||
- `.temp_<project>/` — 临时构建目录
|
||||
|
||||
### 生成签名文件(release 模式)
|
||||
|
||||
```bash
|
||||
openssl req -newkey rsa:2048 -nodes \
|
||||
-keyout sign/private.pem \
|
||||
-x509 -days 3650 \
|
||||
-out sign/certificate.pem \
|
||||
-subj "/CN=appname/O=Example/C=CN"
|
||||
```
|
||||
|
||||
## Common Pitfalls & Solutions
|
||||
|
||||
| 错误 | 原因 | 解决 |
|
||||
|------|------|------|
|
||||
| `must have required property 'config'` | manifest.json 缺少 config 字段 | 添加 `"config": {"logLevel": "log", "designWidth": 480}` |
|
||||
| `path does not exist` | router 路径与实际目录不匹配 | 确保 router.entry/pages key 与 src/ 下路径一致 |
|
||||
| `Compilation failed` | component 名与 ux 文件名不匹配 | component 值必须是 ux 文件名(不含 .ux) |
|
||||
| 资源文件找不到 | 大小写不匹配 | Windows 不区分大小写,但 Vela 内部可能区分,统一用小写 |
|
||||
| 导入的 css 中图片路径失效 | 被导入文件的相对路径在编译后失效 | 使用绝对路径如 `/common/img.png` |
|
||||
|
||||
## File Path Rules
|
||||
|
||||
| 类型 | 路径方式 | 示例 |
|
||||
|------|----------|------|
|
||||
| 导入代码文件 | 相对路径 | `import util from '../common/utils'` |
|
||||
| 引用资源文件 | 相对路径 | `src="./logo.png"` |
|
||||
| manifest 中的资源 | 绝对路径 | `"icon": "/common/icon.png"` |
|
||||
| CSS 中引用资源 | url() + 绝对路径 | `background: url(/common/bg.png)` |
|
||||
|
||||
## API & Components
|
||||
|
||||
- **UI 组件**: `<div>`, `<text>`, `<image>`, `<input>`, `<list>`, `<list-item>`, `<switch>`, `<slider>` 等
|
||||
- **系统接口**: 通过 `features` 在 manifest.json 中声明后使用
|
||||
- `system.router` — 页面路由
|
||||
- `system.fetch` — 网络请求
|
||||
- `system.storage` — 本地存储
|
||||
- `system.device` — 设备信息
|
||||
|
||||
## References
|
||||
|
||||
- [官方文档](https://iot.mi.com/vela/quickapp/zh/guide/)
|
||||
- [UI 组件](https://iot.mi.com/vela/quickapp/zh/components/)
|
||||
- [JS 接口](https://iot.mi.com/vela/quickapp/zh/features/)
|
||||
- [AIoT-IDE 下载](https://iot.mi.com/vela/quickapp/zh/guide/start/use-ide.html)
|
||||
@@ -14,8 +14,11 @@ src/
|
||||
├── common/
|
||||
│ ├── style.css # 公共样式
|
||||
│ └── utils.js # 公共工具函数
|
||||
└── Common/
|
||||
└── icon.png # 应用图标
|
||||
└── common/
|
||||
├── icon.png # 应用图标
|
||||
├── photo1.png # 示例照片1
|
||||
├── photo2.png # 示例照片2
|
||||
└── photo3.png # 示例照片3
|
||||
```
|
||||
|
||||
## 开发说明
|
||||
|
||||
+3
-1
@@ -4,7 +4,9 @@
|
||||
"icon": "/common/icon.png",
|
||||
"versionName": "1.0",
|
||||
"versionCode": 1,
|
||||
"minAPILevel": 1,
|
||||
"minPlatformVersion": 1100,
|
||||
"simulationVersion": "default",
|
||||
"deviceTypeList": ["watch"],
|
||||
"features": [],
|
||||
"config": {
|
||||
"logLevel": "log",
|
||||
|
||||
+14
-14
@@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<div class="page">
|
||||
<text class="title">照片查看器</text>
|
||||
<div class="photo-list">
|
||||
<div class="photo-item" for="{{photos}}">
|
||||
<list class="photo-list">
|
||||
<list-item class="photo-item" for="{{photos}}">
|
||||
<image class="photo" src="{{$item.url}}"></image>
|
||||
<text class="photo-name">{{$item.name}}</text>
|
||||
</div>
|
||||
</div>
|
||||
</list-item>
|
||||
</list>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -14,13 +14,13 @@
|
||||
.page {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
padding: 20dp;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 40px;
|
||||
font-size: 40dp;
|
||||
font-weight: bold;
|
||||
margin-bottom: 30px;
|
||||
margin-bottom: 30dp;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
@@ -32,20 +32,20 @@
|
||||
.photo-item {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
padding: 15px;
|
||||
margin-bottom: 20dp;
|
||||
padding: 15dp;
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 10px;
|
||||
border-radius: 10dp;
|
||||
}
|
||||
|
||||
.photo {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
margin-right: 20px;
|
||||
width: 100dp;
|
||||
height: 100dp;
|
||||
margin-right: 20dp;
|
||||
}
|
||||
|
||||
.photo-name {
|
||||
font-size: 30px;
|
||||
font-size: 30dp;
|
||||
color: #666666;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user