【高心星出品】
4.主界面
1. 顶部搜索区域
-
左侧菜单按钮
-
中间搜索输入框
-
右侧搜索按钮
-
整体采用白色背景,圆角设计
2. 天气信息卡片
-
城市名称(大号字体)
-
当前温度(特大号字体)
-
天气图标(大尺寸)
-
天气描述
-
详细信息网格:
-
湿度
-
风速
-
PM2.5
-
空气质量
-
卡片采用白色背景,带阴影效果
3. 天气预报区域
-
标题:“未来天气预报”
-
三个天气卡片:
-
显示星期
-
天气图标
-
温度范围
-
采用白色背景,带阴影效果
4. 左侧抽屉菜单
-
顶部标题栏:
-
"搜索历史"标题
-
编辑/完成按钮
-
历史记录列表:
-
城市名称
-
搜索时间
-
删除按钮(编辑模式)
-
空状态显示:
-
空历史图标
-
"暂无搜索历史"提示
-
底部清空按钮(编辑模式)
-
支持滑动关闭
build() {
Stack() {
Column() {
// 搜索框布局
Row() {
Image($r('app.media.menu'))
.width(30)
.onClick(() => {
this.isshow = !this.isshow
})
TextInput({ placeholder: '输入城市名称' })
.width('60%')
.height(40)
.backgroundColor(Color.White)
.borderRadius(8)
.onChange((value: string) => {
this.searchText = value
})
Button('搜索')
.width('25%')
.height(40)
.backgroundColor('#0083b0')
.onClick(() => {
if (this.searchText.trim()) {
this.loadweather(this.searchText.trim(), true)
} else {
promptAction.showToast({ message: '请输入城市名称' })
}
})
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.margin({ bottom: 20 })
// 天气信息卡片布局
Column() {
// 城市名称
Text(this.cityName)
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 10 })
// 温度显示
Text(this.temperature)
.fontSize(48)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 10 })
// 天气图标
Image(getweathericon(this.weatherDesc))
.width(100)
.height(100)
.margin({ bottom: 10 })
// 天气描述
Text(this.weatherDesc)
.fontSize(18)
.margin({ bottom: 20 })
// 详细信息网格布局
Grid() {
// 湿度信息
GridItem() {
Column() {
Text('湿度')
.fontSize(14)
.fontColor('#666666')
Text(this.humidity)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.margin({ top: 5 })
}
}
// 风速信息
GridItem() {
Column() {
Text('风速')
.fontSize(14)
.fontColor('#666666')
Text(this.windSpeed)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.margin({ top: 5 })
}
}
// PM2.5信息
GridItem() {
Column() {
Text('PM2.5')
.fontSize(14)
.fontColor('#666666')
Text(this.pm25)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.margin({ top: 5 })
}
}
// 空气质量信息
GridItem() {
Column() {
Text('空气质量')
.fontSize(14)
.fontColor('#666666')
Text(this.quality)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.margin({ top: 5 })
}
}
}
.columnsTemplate('1fr 1fr')
.rowsTemplate('1fr 1fr')
.width('100%')
.height(160)
.margin({ top: 20 })
}
.width('100%')
.padding(20)
.backgroundColor('#FFFFFF')
.borderRadius(16)
.shadow({ radius: 6, color: '#1A000000', offsetY: 2 })
// 天气预报布局
Column() {
Text('未来天气预报')
.fontSize(20)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 15 })
// 未来三天天气预报
Row() {
// 按照引用传值才会引起ui刷新
this.ForecastItem({ day: this.day1week, tempe: this.day1wendu, type: this.day1type })
this.ForecastItem({ day: this.day2week, tempe: this.day2wendu, type: this.day2type })
this.ForecastItem({ day: this.day3week, tempe: this.day3wendu, type: this.day3type })
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
}
.width('100%')
.padding(20)
.backgroundColor('#FFFFFF')
.borderRadius(16)
.margin({ top: 20 })
.shadow({ radius: 6, color: '#1A000000', offsetY: 2 })
}
.width('100%')
.height('100%')
.padding(20)
.backgroundColor('#F5F5F5')
.onClick(() => {
this.isshow = false
})
// 左侧抽屉布局
if (this.isshow) {
Column() {
// 顶部标题栏布局
Row() {
if (this.searchHistory.length > 0) {
Text('搜索历史')
.fontSize(20)
.fontWeight(FontWeight.Bold)
}
Blank()
if (this.searchHistory.length > 0) {
Text(this.isEditing ? '完成' : '编辑')
.fontSize(16)
.fontColor('#0083b0')
.onClick(() => {
this.isEditing = !this.isEditing
// 编辑完成后清除要删除的id
if (!this.isEditing) {
this.delids.clear()
}
})
}
}
.width('100%')
.height(50)
.padding(16)
.margin({ top: 20 })
.backgroundColor('#FFFFFF')
// 搜索历史为空时的显示
if (this.searchHistory.length === 0) {
Column() {
Image($r('app.media.empty_history'))
.width(80)
.height(80)
.margin({ bottom: 16 })
Text('暂无搜索历史')
.fontSize(16)
.fontColor('#999999')
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
} else {
// 搜索历史列表布局
List({ space: 10 }) {
ForEach(this.searchHistory, (item: SearchHistoryItem) => {
ListItem() {
Row() {
Row({ space: 20 }) {
Text(item.keyword)
.fontSize(16)
.fontColor(Color.Black)
Text(item.timestamp)
.fontSize(14)
.fontColor('#555555')
.margin({ top: 4 })
}.onClick(async () => {
await this.loadweather(item.keyword, false)
this.isshow = false
})
.layoutWeight(1)
// 编辑模式下显示删除按钮
if (this.isEditing) {
Image($r('app.media.delete'))
.width(20)
.height(20)
.margin({ left: 16 })
.onClick(() => {
this.deleteHistoryItem(item.id as number)
})
.transition(this.effect1)
}
}
.width('100%')
.padding(15)
}
.translate({ x: this.delids.has(item.id) ? '-100%' : '0%' })
})
}
.width('100%')
.layoutWeight(1)
.margin({ top: 10 })
.divider({ strokeWidth: 2, color: 'rgba(0,0,0,0.4)' })
}
// 编辑模式下显示清空按钮
if (this.searchHistory.length > 0 && this.isEditing) {
Button('清空历史记录')
.width('90%')
.height(40)
.margin({ bottom: 16 })
.backgroundColor('#FF4D4F')
.onClick(() => {
this.clearHistory()
})
.transition(this.effect1)
}
}
.width('60%')
.height('100%')
.backgroundColor(Color.White)
.transition(this.effect)
.borderRadius({ topRight: 20, bottomRight: 20 })
.border({ width: 1, color: 'rgba(0,0,100,0.5)' })
// 抽屉出现时加载搜索历史
.onAppear(() => {
let sd = new searchdao(this.context)
sd.queryall().then((value) => {
this.searchHistory = value
}).catch((e: Error) => {
console.error('gxxt 查询搜索历史失败: ', e.message)
})
})
// 抽屉滑动关闭处理
.onTouch((event) => {
if (event.type == TouchType.Down) {
this.downxy = { x: event.touches[0].windowX, y: event.touches[0].windowY }
} else if (event.type == TouchType.Move) {
if (event.touches[0].windowX - (this.downxy.x as number) < 0) {
if (event.touches[0].windowX - (this.downxy.x as number) <= -60) {
this.isshow = false
}
}
}
})
}
}
.width('100%')
.height('100%')
.alignContent(Alignment.Start)
}
5. 用户交互阶段
-
搜索功能:
-
用户输入城市名称
-
点击搜索按钮
-
加载对应城市天气
-
保存搜索记录
Button('搜索') .width('25%') .height(40) .backgroundColor('#0083b0') .onClick(() => { if (this.searchText.trim()) { this.loadweather(this.searchText.trim(), true) } else { promptAction.showToast({ message: '请输入城市名称' }) } })
-
-
历史记录管理:
-
点击菜单按钮打开侧边栏
-
显示搜索历史列表
-
支持编辑模式切换
-
可以删除单条记录或清空所有记录
-
// 左侧抽屉布局
if (this.isshow) {
Column() {
// 顶部标题栏布局
Row() {
if (this.searchHistory.length > 0) {
Text('搜索历史')
.fontSize(20)
.fontWeight(FontWeight.Bold)
}
Blank()
if (this.searchHistory.length > 0) {
Text(this.isEditing ? '完成' : '编辑')
.fontSize(16)
.fontColor('#0083b0')
.onClick(() => {
this.isEditing = !this.isEditing
// 编辑完成后清除要删除的id
if (!this.isEditing) {
this.delids.clear()
}
})
}
}
.width('100%')
.height(50)
.padding(16)
.margin({ top: 20 })
.backgroundColor('#FFFFFF')
// 搜索历史为空时的显示
if (this.searchHistory.length === 0) {
Column() {
Image($r('app.media.empty_history'))
.width(80)
.height(80)
.margin({ bottom: 16 })
Text('暂无搜索历史')
.fontSize(16)
.fontColor('#999999')
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
} else {
// 搜索历史列表布局
List({ space: 10 }) {
ForEach(this.searchHistory, (item: SearchHistoryItem) => {
ListItem() {
Row() {
Row({ space: 20 }) {
Text(item.keyword)
.fontSize(16)
.fontColor(Color.Black)
Text(item.timestamp)
.fontSize(14)
.fontColor('#555555')
.margin({ top: 4 })
}.onClick(async () => {
await this.loadweather(item.keyword, false)
this.isshow = false
})
.layoutWeight(1)
// 编辑模式下显示删除按钮
if (this.isEditing) {
Image($r('app.media.delete'))
.width(20)
.height(20)
.margin({ left: 16 })
.onClick(() => {
this.deleteHistoryItem(item.id as number)
})
.transition(this.effect1)
}
}
.width('100%')
.padding(15)
}
.translate({ x: this.delids.has(item.id) ? '-100%' : '0%' })
})
}
.width('100%')
.layoutWeight(1)
.margin({ top: 10 })
.divider({ strokeWidth: 2, color: 'rgba(0,0,0,0.4)' })
}
// 编辑模式下显示清空按钮
if (this.searchHistory.length > 0 && this.isEditing) {
Button('清空历史记录')
.width('90%')
.height(40)
.margin({ bottom: 16 })
.backgroundColor('#FF4D4F')
.onClick(() => {
this.clearHistory()
})
.transition(this.effect1)
}
}
.width('60%')
.height('100%')
.backgroundColor(Color.White)
.transition(this.effect)
.borderRadius({ topRight: 20, bottomRight: 20 })
.border({ width: 1, color: 'rgba(0,0,100,0.5)' })
// 抽屉出现时加载搜索历史
.onAppear(() => {
let sd = new searchdao(this.context)
sd.queryall().then((value) => {
this.searchHistory = value
}).catch((e: Error) => {
console.error('gxxt 查询搜索历史失败: ', e.message)
})
})
// 抽屉滑动关闭处理
.onTouch((event) => {
if (event.type == TouchType.Down) {
this.downxy = { x: event.touches[0].windowX, y: event.touches[0].windowY }
} else if (event.type == TouchType.Move) {
if (event.touches[0].windowX - (this.downxy.x as number) < 0) {
if (event.touches[0].windowX - (this.downxy.x as number) <= -60) {
this.isshow = false
}
}
}
})
}
}
.width('100%')
.height('100%')
.alignContent(Alignment.Start)
5. 数据持久化阶段
-
搜索历史保存:
-
每次搜索后自动保存
-
记录包含关键词和时间戳
// 抽屉出现时加载搜索历史 .onAppear(() => { let sd = new searchdao(this.context) sd.queryall().then((value) => { this.searchHistory = value }).catch((e: Error) => { console.error('gxxt 查询搜索历史失败: ', e.message) }) }) ................................ /** * 查询所有搜索历史记录 * @returns 返回搜索历史记录数组 */ async queryall() { // 定义存储搜索历史的数组 let datas: SearchHistoryItem[] = [] try { // 获取数据库实例 let rdb = await getrdb(this.context) // 创建查询条件 let predicates = new relationalStore.RdbPredicates(TABLENAME1) // 执行查询 let rs = await rdb.query(predicates) // 遍历结果集 while (rs.goToNextRow()) { // 将每条记录添加到数组中 datas.push({ id: rs.getLong(rs.getColumnIndex('id')), keyword: rs.getString(rs.getColumnIndex('keyword')), timestamp: rs.getString(rs.getColumnIndex('timestamp')), }) } } catch (e) { console.error('gxxt searchdao 查询失败: ', JSON.stringify(e)) } return datas; }
-
-
数据更新:
-
实时更新天气信息
-
保存用户操作记录
-
6. 错误处理阶段
-
网络错误:
-
显示错误提示
-
使用默认数据
-
-
定位失败:
-
提示用户检查定位设置
-
使用默认城市
-
-
数据加载失败:
-
显示错误对话框
-
提供重试选项
-
7. 界面更新阶段
-
状态变化触发UI更新:
-
天气数据更新
-
历史记录变化
-
编辑模式切换
-
-
动画效果:
-
侧边栏滑动
-
加载动画
-
删除动画
-
8. 资源释放阶段
-
页面关闭时:
-
清理定时器
-
保存未保存的数据
-
释放系统资源
-
最后:
完整项目:https://download.csdn.net/download/gao_xin_xing/90848357