构建待办应用
本教程组合前面介绍的页面、数据和系统 API,构建一个包含列表页与详情页的待办应用。应用使用本地存储保留待办数据,并在启动时请求一条提示文案。
- 已完成数据、列表与事件。
- 已在 BlueOS Studio 中创建可正常编译的 JavaScript 应用工程。
- 已了解页面路由 API使用的模块名
@blueos.app.appmanager.router。部分旧示例中的@blueos.app.router不适用于本教程。
本教程中的待办数据结构为 { id, title, done }。应用不包含后端服务和用户登录。
创建页面目录
Section titled “创建页面目录”- 在
src/pages/下创建Todos目录。 - 在
src/pages/Todos/下创建index.ux。 - 在
src/pages/下创建TodoDetail目录。 - 在
src/pages/TodoDetail/下创建index.ux。
完成后的主要工程结构如下:
src/ app.ux manifest.json pages/ Todos/index.ux TodoDetail/index.ux- 打开
src/manifest.json。 - 将应用配置替换为以下内容:
{ "package": "com.example.todos", "name": "待办", "icon": "/assets/images/logo.png", "versionName": "1.0", "versionCode": 1, "appCategory": ["utilities"], "config": { "designWidth": 466 }, "features": [ { "name": "blueos.storage.storage" }, { "name": "blueos.network.fetch" } ], "router": { "entry": "Todos", "pages": { "Todos": { "component": "index" }, "TodoDetail": { "component": "index" } } }}- 将
icon修改为工程中的实际图标路径。
配置中的 features 声明了本地存储和网络请求能力。相关字段参见 K-V 数据存储 与数据请求。
添加应用入口
Section titled “添加应用入口”- 打开
src/app.ux。 - 将文件内容替换为以下代码:
<script> export default {}</script>- 打开
src/pages/Todos/index.ux。 - 将文件内容替换为以下代码:
<script> import router from '@blueos.app.appmanager.router' import storage from '@blueos.storage.storage' import fetch from '@blueos.network.fetch'
export default { data: { tip: '今天也值得认真对待', newTitle: '', todos: [], }, onInit() { this.loadTodos() this.loadTip() }, onShow() { this.loadTodos() }, loadTodos() { storage.get({ key: 'todos', success: (data) => { this.todos = Array.isArray(data) ? data : [] }, fail: () => { this.todos = [] }, }) }, saveTodos() { storage.set({ key: 'todos', value: this.todos, fail: () => {}, }) }, loadTip() { fetch.fetch({ url: 'https://httpbin.org/json', responseType: 'json', success: (response) => { const title = response.data && response.data.slideshow && response.data.slideshow.title if (title) { this.tip = title } }, fail: () => {}, }) }, onTitleChange(evt) { this.newTitle = evt.value }, addTodo() { const title = (this.newTitle || '').trim() if (!title) { return } const next = this.todos.concat([ { id: String(Date.now()), title: title, done: false }, ]) this.todos = next this.newTitle = '' this.saveTodos() }, openDetail(item) { router.push({ uri: '/TodoDetail', params: { id: item.id }, }) }, }</script>
<template> <div class="flex flex-col w-full"> <text class="text-white text-2xl">{{ tip }}</text> <div class="flex items-center w-full mt-10"> <input class="title-input text-white" type="text" value="{{newTitle}}" placeholder="新的待办" onchange="onTitleChange" /> <input class="text-white" type="button" value="添加" onclick="addTodo" /> </div> <text if="{{todos.length === 0}}" class="text-white mt-10">还没有待办</text> <list class="w-full mt-10"> <list-item type="todo" for="{{(index, item) in todos}}" tid="id" onclick="openDetail(item)"> <div class="flex items-center w-full"> <text class="text-white">{{ item.title }}</text> </div> </list-item> </list> </div></template>
<style>@tailwind utilities;
.title-input { flex: 1;}</style>onShow 在页面每次显示时重新读取本地存储。因此,从详情页返回列表页后,列表会显示最新状态。
网络请求失败时,loadTip 执行 fail 回调,页面顶部继续显示 tip 中的默认文案。
路由参数会作为字符串添加到页面实例。列表页通过 params.id 传递标识符后,详情页使用 this.id 读取该值,因此 id 必须预先在 data 中声明。
- 打开
src/pages/TodoDetail/index.ux。 - 将文件内容替换为以下代码:
<script> import router from '@blueos.app.appmanager.router' import storage from '@blueos.storage.storage'
export default { data: { id: '', title: '', done: false, todos: [], }, onInit() { this.loadAndShow() }, loadAndShow() { storage.get({ key: 'todos', success: (data) => { const todos = Array.isArray(data) ? data : [] this.todos = todos const found = todos.filter((item) => item.id === this.id)[0] if (found) { this.title = found.title this.done = found.done } }, fail: () => { this.todos = [] }, }) }, saveTodos() { storage.set({ key: 'todos', value: this.todos, fail: () => {}, }) }, toggleDone() { const done = !this.done const next = this.todos.map((item) => { if (item.id === this.id) { return { id: item.id, title: item.title, done: done } } return item }) this.todos = next this.done = done this.saveTodos() }, removeTodo() { this.todos = this.todos.filter((item) => item.id !== this.id) storage.set({ key: 'todos', value: this.todos, success: () => { router.back() }, fail: () => {}, }) }, }</script>
<template> <div class="flex flex-col items-center w-full"> <text class="text-white text-3xl">{{ title }}</text> <text class="text-white mt-10">{{ done ? '已完成' : '未完成' }}</text> <input class="text-white mt-10" type="button" value="切换完成" onclick="toggleDone" /> <input class="text-white mt-10" type="button" value="删除" onclick="removeTodo" /> </div></template>
<style>@tailwind utilities;</style>删除待办时,页面在存储写入成功后调用 router.back()。必须等待写入成功后再返回,确保列表页读取最新数据。
- 保存全部文件。
- 在 BlueOS Studio 中重新启动编译。
- 在输入框中填写待办标题。
- 选择 添加。
- 选择新建的待办,进入详情页。
- 选择 切换完成。
- 返回列表页。
- 重新启动应用。
重新启动后,列表中应保留已添加的待办。网络不可用时,页面顶部应显示默认提示文案,不影响待办的本地操作。
使用常见问题排查黑屏、数据不更新、路由失败和样式异常。