目录
前言
好久不发博文了,最近都忙于面试,忙于找工作,这段时间终于找到工作了。我对鸿蒙开发的激情依然没有减退,前几天做了一个鸿蒙的APP,现在给大家分享一下!
具体功能如下图:

1.我的小屋
**1.1 锁门:**对大门开关锁的控制
1.2设备状态:展示了某台设备的详细信息
1.3 控制面板:房门开关、车库门开关、窗帘开关、灯开关
1.4**添加设备:**根据设备信息添加设备
我的小屋

锁门

设备状态

2.查找设备
2.1 搜索:对已经添加的设备进行搜索
2.2 搜索历史:可以查看到搜索过的信息
查找设备

3.个人主页
3.1 **个人资料:**展示个人的资料信息,支持修改
3.2账号与安全:包含修改密码和退出登录



注册页面代码
TypeScript
// 导入必要的模块
import router from '@ohos.router';
import Preferences from '@ohos.data.preferences';
import UserBean from '../common/bean/UserBean';
// 注册页面
@Entry
@Component
struct RegisterPage {
@State username: string = '';
@State password: string = '';
@State confirmPwd: string = '';
@State errorMsg: string = '';
private prefKey: string = 'userData'
private context = getContext(this)
// 保存用户数据(保持原逻辑)
async saveUser() {
try {
let prefs = await Preferences.getPreferences(this.context, this.prefKey)
const newUser: UserBean = {
// @ts-ignore
username: this.username,
password: this.password
}
await prefs.put(this.username, JSON.stringify(newUser))
await prefs.flush()
router.back()
} catch (e) {
console.error('注册失败:', e)
}
}
// 注册验证(保持原逻辑)
handleRegister() {
if (!this.username || !this.password) {
this.errorMsg = '用户名和密码不能为空'
return
}
if (this.password !== this.confirmPwd) {
this.errorMsg = '两次密码不一致'
return
}
this.saveUser()
}
build() {
Column() {
// 用户名输入框
TextInput({ placeholder: '用户名' })
.width('80%')
.height(50)
.margin({ bottom: 20 })
.backgroundColor('#FFFFFF') // 新增白色背景
.onChange(v => this.username = v)
// 密码输入框
// @ts-ignore
TextInput({ placeholder: '密码', type: InputType.Password })
.width('80%')
.height(50)
.margin({ bottom: 20 })
.backgroundColor('#FFFFFF') // 新增白色背景
.onChange(v => this.password = v)
// 确认密码输入框
// @ts-ignore
TextInput({ placeholder: '确认密码', type: InputType.Password })
.width('80%')
.height(50)
.margin({ bottom: 20 })
.backgroundColor('#FFFFFF') // 新增白色背景
.onChange(v => this.confirmPwd = v)
// 错误提示(保持原样)
if (this.errorMsg) {
Text(this.errorMsg)
.fontColor('red')
.margin({ bottom: 10 })
}
// 注册按钮(保持原样)
Button('注册', { type: ButtonType.Capsule })
.width('80%')
.height(50)
.backgroundColor('#007AFF')
.onClick(() => this.handleRegister())
// 返回按钮(保持原样)
Button('返回登录', { type: ButtonType.Capsule })
.width('80%')
.height(40)
.margin({ top: 20 })
.backgroundColor('#34C759')
.onClick(() => router.back())
}
.width('100%')
.height('100%')
.padding(20)
.justifyContent(FlexAlign.Center)
.backgroundColor('#1A1A1A') // 新增暗黑背景色
}
}
登录页面代码
TypeScript
// 导入必要的模块
import router from '@ohos.router';
import Preferences from '@ohos.data.preferences';
import DeviceBean from '../common/bean/DeviceBean';
// 用户数据模型
class User {
username: string = '';
password: string = '';
}
// 登录页面
@Entry
@Component
struct LoginPage {
@State username: string = '';
@State password: string = '';
@State errorMsg: string = '';
@State deviceList:Array<DeviceBean> =[new DeviceBean(0,'设备1',0,0,0,0,0,0,0,0,0,0,),new DeviceBean(1,'设备2',0,0,0,0,0,0,0,0,0,0,),new DeviceBean(3,'设备3',0,0,0,0,0,0,0,0,0,0,)];
private prefKey: string = 'userData'
// 获取本地存储上下文
private context = getContext(this)
// 获取用户数据
async getUser(): Promise<User | null> {
try {
// @ts-ignore
return userData ? JSON.parse(userData) : null
} catch (e) {
console.error('读取失败:', e)
return null
}
}
// 登录处理
async handleLogin(username:string,password:string,) {
if (!this.username || !this.password) {
this.errorMsg = '用户名和密码不能为空'
return
}
const user = await this.getUser()
if ( username == 'admin' && password=='123456') {
AppStorage.SetOrCreate("deviceList",this.deviceList);
AppStorage.SetOrCreate("signature","金克斯的含义就是金克斯")
AppStorage.SetOrCreate("nickName","金克斯")
AppStorage.SetOrCreate("nickname","金克斯")
AppStorage.SetOrCreate("login_username",this.username)
// 登录成功跳转主页
router.pushUrl({ url: 'pages/MainPages', params: { username: this.username } })
} else {
this.errorMsg = '用户名或密码错误'
}
}
build() {
Column() {
// 新增欢迎语
Text('欢迎登录智能家庭app')
.fontSize(24)
.margin({ bottom: 40 })
.fontColor('#FFFFFF') // 白色文字
TextInput({ placeholder: '用户名' })
.width('80%')
.height(50)
.margin({ bottom: 20 })
.onChange(v => this.username = v).fontColor(Color.White).placeholderColor(Color.White)
// @ts-ignore
TextInput({ placeholder: '密码', type: InputType.Password}).fontColor(Color.White).placeholderColor(Color.White)
.width('80%')
.height(50)
.margin({ bottom: 20 })
.onChange(v => this.password = v)
if (this.errorMsg) {
Text(this.errorMsg)
.fontColor('red')
.margin({ bottom: 10 })
}
Button('登录', { type: ButtonType.Capsule })
.width('80%')
.height(50)
.backgroundColor('#007AFF')
.onClick(() => this.handleLogin(this.username,this.password))
Button('注册', { type: ButtonType.Capsule })
.width('80%')
.height(40)
.margin({ top: 20 })
.backgroundColor('#34C759')
.onClick(() => router.pushUrl({ url: 'pages/RegisterPage' }))
}
.width('100%')
.height('100%')
.padding(20)
.justifyContent(FlexAlign.Center)
.backgroundColor('#1A1A1A') // 新增暗黑背景色
}
}
主页代码
TypeScript
import prompt from '@ohos.prompt';
import { TabID, TabItemList } from '../model/TabItem'
import { DeviceSearchComponent } from '../view/DeviceSearchComponent';
import HouseStateComponent from '../view/HouseStateComponent';
import { MineComponent } from '../view/MineComponent';
import { NotLogin } from '../view/NotLogin';
@Entry
@Component
struct MainPages {
@State pageIndex:number = 0;//页面索引
private tabController:TabsController = new TabsController();//tab切换控制器
@Builder MyTabBuilder(idx:number){
Column(){
Image(idx === this.pageIndex ? TabItemList[idx].icon_selected:
TabItemList[idx].icon)
.width(32)
.height(32)
// .margin({top:5})
Text(TabItemList[idx].title)
.fontSize(14)
.fontWeight(FontWeight.Bold)
.fontColor(this.pageIndex === idx ? '#006eee':'#888')
}
}
build() {
Tabs({barPosition:BarPosition.End}){
TabContent(){
// Text('小屋状态')
HouseStateComponent()
}
// .tabBar('小屋状态')
.tabBar(this.MyTabBuilder(TabID.HOUSE)) //调用自定义的TabBar
TabContent(){
// Text('搜索设备')
DeviceSearchComponent()//调用设备搜索组件
}
// .tabBar('搜索设备')
.tabBar(this.MyTabBuilder(TabID.SEARCH_DEVICE)) //调用自定义的TabBar
TabContent(){
// Text('我的')
MineComponent();//个人主页
// NotLogin()
}
// .tabBar('我的')
.tabBar(this.MyTabBuilder(TabID.MINE)) //调用自定义的TabBar
}
.barWidth('100%')
.barMode(BarMode.Fixed)
.width('100%')
.height('100%')
.onChange((index)=>{//绑定onChange函数切换页签
this.pageIndex = index;
})
.backgroundColor('#000')
}
}
我的小屋代码
TypeScript
import router from '@ohos.router';
import { Action } from '@ohos.multimodalInput.keyEvent';
import DeviceBean from '../common/bean/DeviceBean';
import MainViewModel from '../model/MainViewModel';
import promptAction from '@ohos.promptAction';
// 自定义弹窗组件
@CustomDialog
struct SimpleDialog {
@State deviceList:Array<DeviceBean> =[new DeviceBean(0,'设备1',0,0,0,0,0,0,0,0,0,0,),new DeviceBean(1,'设备2',0,0,0,0,0,0,0,0,0,0,),new DeviceBean(3,'设备3',0,0,0,0,0,0,0,0,0,0,)];
// @ts-ignore
@State deviceId: number=1; //设备ID
@State name: string=''; //设备名
@State temperator: number=25.3; //室内温度
@State humidity: number =20; //室内湿度
@State lumination: number =10; //光照
@State isRain: number =30; //下雨预警 0:正常 1:触发报警
@State isSmoke: number =0; //烟雾报警 0:正常 1:触发报警
@State isFire: number=0; //火灾报警 0:正常 1:触发报警
@State doorStatus: number=0; //门开关状态 0:正常 1:开启
@State garageStatus: number=0; //车库门开关 0:正常 1:开启
@State curtainStatus: number=0; //窗帘开关 0:正常 1:开启
@State lightStatus: number=0; //灯开关 0:正常 1:开启
@State tempDevice: DeviceBean = new DeviceBean(0,'',0,0,0,0,0,0,0,0,0,0,);
tmpMap:Map<string, string> = new Map();
controller:CustomDialogController;
build() {
Column() {
// 设备基本信息
TextInput({ placeholder: '设备ID(数字)' })
.onChange(v => this.tempDevice.deviceId = Number(v))
TextInput({ placeholder: '设备名称' })
.onChange(v => this.tempDevice.name = v)
// 环境参数
/* TextInput({ placeholder: '温度(0-100)' })
.onChange(v => this.tempDevice.temperator = Number(v))
TextInput({ placeholder: '湿度(0-100)' })
.onChange(v => this.tempDevice.humidity = Number(v))
TextInput({ placeholder: '光照强度' })
.onChange(v => this.tempDevice.lumination = Number(v))*/
Button('保存').onClick(()=>{
promptAction.showToast({
message:'保存成功!',
duration:2000,
})
// 添加新设备到列表
//this.deviceList = [...this.deviceList, {...this.tempDevice}];
console.log(JSON.stringify(this.tempDevice))
this.deviceList.push(this.tempDevice); // 关键步骤:创建新数组
AppStorage.SetOrCreate("deviceList",this.deviceList);
this.controller.close()
})
/* .onClick(() => {
this.deviceList = [...this.deviceList, {
// @ts-ignore
id: this.id,
name: this.name,
}]
this.controller.close()
})*/
}
.padding(20)
}
}
@Component
export default struct HouseStateComponent{
private exitDialog() { }
@State handlePopup: boolean = false
@State devices: DeviceBean[]=[]
// 确保控制器正确初始化
private dialogController: CustomDialogController = new CustomDialogController({
builder: SimpleDialog(), // 确认组件正确引用
cancel: this.exitDialog, // 关闭回调
autoCancel: true // 允许点击外部关闭
})
@Builder AddMenu(){
Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
Column(){
Text('扫一扫')
.fontSize(20)
.width('100%')
.height(30)
.align(Alignment.Center)
Divider().width('80%').color('#ccc')
Text('添加设备')
.fontSize(20)
.width('100%')
.height(30)
.align(Alignment.Center).onClick(()=>{
this.dialogController.open()
})
}
.padding(5)
.height(65)
}.width(100)
}
build(){
Column({space:8}){
Row() {
Text('小屋状态')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.fontColor(Color.White)
.margin({ top: 10 })
.textAlign(TextAlign.Start)
Image($r('app.media.jiahao_0'))
.width(32)
.height(32)
.margin({top:10,left:160})
.bindMenu(this.AddMenu())
Image($r('app.media.news'))
.fillColor(Color.Black)
.width(32)
.height(32)
.margin({top:10})
.onClick(() => {
this.handlePopup = !this.handlePopup
})
.bindPopup(this.handlePopup,{
message:'当前暂无未读消息',
})
// .onClick({
//
// })
}
.width('95%')
.justifyContent(FlexAlign.SpaceBetween)
Divider().width('95%').color(Color.White)
Flex({ wrap: FlexWrap.Wrap }){
Button()
Row() {
Shape() {
Circle({ width: 60, height: 60 }).fill('#494848')
.margin({left:15,top:'32%'})
.opacity(0.7)
Image($r('app.media.lock')).width(30)
.margin({ left: 30 ,top:'42%'})
}
.height('100%')
Column() {
Text('大门')
.fontSize(25)
.fontColor('#838383')
.fontWeight(FontWeight.Bold)
.margin({ left: 20, top: 0 })
Text('已锁')
.fontSize(25)
.fontColor('#838383')
.fontWeight(FontWeight.Bold)
.margin({ left: 20, top: 0 })
}
}
.borderRadius(20)
.backgroundColor('#1c1c1e')
// .backgroundImage($r('app.media.Button_img'))
// .backgroundImageSize(ImageSize.Cover)
// .border({ width: 1 })
// .borderColor('#b9b9b9')
.width('47%')
.height(160)
.onClick(()=>{
router.pushUrl({
url:"house/HomeGate"
})
})
Button()
Row() {
Shape() {
Circle({ width: 60, height: 60 }).fill('#494848')
.margin({left:15,top:'32%'})
.opacity(0.7)
Image($r('app.media.Device_icon')).width(30)
.margin({ left: 30 ,top:'42%'})
}
.height('100%')
Column() {
Text('设备')
.fontSize(25)
.fontColor('#838383')
.fontWeight(FontWeight.Bold)
.margin({ left: 20, top: 0 })
Text('状态')
.fontSize(25)
.fontColor('#838383')
.fontWeight(FontWeight.Bold)
.margin({ left: 20, top: 0 })
}
}
.onClick(()=>{
router.pushUrl({
url:"house/DeviceStatus"
})
})
.borderRadius(20)
.backgroundColor('#1c1c1e')
.margin({left:20})
// .border({ width: 1 })
// .borderColor('#b9b9b9')
.width('47%')
.height(160)
}
.width('95%')
.padding(10)
Flex() {
Button()
Row() {
Shape() {
Circle({ width: 60, height: 60 }).fill('#494848')
.margin({ left: 15, top: '32%' })
.opacity(0.7)
Image($r('app.media.console_1')).width(30)
.margin({ left: 30, top: '47%' })
}
.height('100%')
Column() {
Text('控制')
.fontSize(25)
.fontColor('#838383')
.fontWeight(FontWeight.Bold)
.margin({ left: 20, top: 0 })
Text('面板')
.fontSize(25)
.fontColor('#838383')
.fontWeight(FontWeight.Bold)
.margin({ left: 20, top: 0 })
}
}
.onClick(() => {
router.pushUrl({
url: "house/ControlConsole"
})
})
.borderRadius(20)
.backgroundColor('#1c1c1e')
// .border({ width: 1 })
// .borderColor('#b9b9b9')
.width('47%')
.height(160)
}
.width('95%')
.padding(10)
}
.width('100%')
.height('100%')
.backgroundImage($r('app.media.index_background1'))
.backgroundImageSize(ImageSize.Cover)
}
}
设备搜索代码
TypeScript
import DeviceBean from '../common/bean/DeviceBean'
@Component
export struct DeviceSearchComponent {
@State submitValue: string = '' //获取历史记录数据
@State allDevices:Array<DeviceBean> =[new DeviceBean(0,'设备1',0,0,0,0,0,0,0,0,0,0,),new DeviceBean(1,'设备2',0,0,0,0,0,0,0,0,0,0,)];
@State all_devices:Array<DeviceBean> =AppStorage.Get("deviceList");
@State filteredDevices:Array<DeviceBean>=[];
controller: SearchController = new SearchController()
scroll: Scroller = new Scroller()
@State historyValueArr: Array<string> = [] //历史记录存放
private swiperController: SwiperController = new SwiperController()
build() {
Column({ space: 8 }) {
Row({space:1}){
Search({placeholder:'搜索一下',controller: this.controller})
.searchButton('搜索')
.margin(15)
.width('80%')
.height(40)
.backgroundColor('#F5F5F5')
.placeholderColor(Color.Grey)
.placeholderFont({ size: 14, weight: 400 })
.textFont({ size: 14, weight: 400 })
.onSubmit((value: string) => { //绑定 输入内容添加到submit中
this.submitValue = value
for (let i = 0; i < this.historyValueArr.length; i++) {
if (this.historyValueArr[i] === this.submitValue) {
this.historyValueArr.splice(i, 1);
break;
}
}
this.historyValueArr.unshift(this.submitValue) //将输入数据添加到历史数据
// 若历史记录超过10条,则移除最后一项
if (this.historyValueArr.length > 10) {
this.historyValueArr.splice(this.historyValueArr.length - 1);
}
let devices: Array<DeviceBean> = AppStorage.Get("deviceList") as Array<DeviceBean>
JSON.stringify(devices);
// 增加过滤逻辑
this.filteredDevices = devices.filter(item =>
item.name.includes(value)
)
})
// 搜索结果列表:ml-citation{ref="7" data="citationList"}
//二维码
Scroll(this.scroll){
Image($r('app.media.saomiao')).width(35).height(35)
.objectFit(ImageFit.Contain)
}
}
.margin({top:20})
// 轮播图
Swiper(this.swiperController) {
Image($r('app.media.lunbotu1' ))
.width('100%')
.height('100%')
.objectFit(ImageFit.Auto) //让图片自适应大小 刚好沾满
// .backgroundImageSize(ImageSize.Cover)
Image($r('app.media.lbt2' ))
.width('100%')
.height('100%')
.objectFit(ImageFit.Auto)
Image($r('app.media.lbt3' ))
.width('100%')
.height('100%')
.objectFit(ImageFit.Auto)
Image($r('app.media.lbt4' ))
.width('100%')
.height('100%')
.objectFit(ImageFit.Auto)
Image($r('app.media.tips' ))
.width('100%')
.height('100%')
.objectFit(ImageFit.Auto)
}
.loop(true)
.autoPlay(true)
.interval(5000) //每隔5秒换一张
.width('90%')
.height('25%')
.borderRadius(20)
// 历史记录
Row() {
// 搜索历史标题
Text('搜索历史').fontSize('31lpx').fontColor("#828385")
// 清空记录按钮
Text('清空记录')
.fontSize('27lpx').fontColor("#828385")
// 清空记录按钮点击事件,清空历史记录数组
.onClick(() => {
this.historyValueArr.length = 0;
})
}
.margin({top:30})
// 设置Row组件的宽度、对齐方式和内外边距
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.padding({
left: '37lpx',
top: '11lpx',
bottom: '11lpx',
right: '37lpx'
})
Row(){
// 搜索结果列表:ml-citation{ref="7" data="citationList"}
List({ space: 10 }) {
if (this.filteredDevices.length===0){
ListItem() {
Text(`暂时未找到任何设备..`).fontColor(Color.White)
}
}
ForEach(this.filteredDevices, (item: DeviceBean) => {
ListItem() {
Text(`${item.deviceId} (${item.name})`).fontColor(Color.White)
}
}, item => item.deviceId)
}
/* List({ space: 10 }) {
ForEach(this.filteredDevices, (item: DeviceBean) => {
ListItem() {
Text('模拟设备1').fontColor(Color.White)
}
ListItem() {
Text('模拟设备2').fontColor(Color.White)
}
}, item => item.id)
}*/
}.margin({top:50,left:30})
// 使用Flex布局,包裹搜索历史记录
Flex({
direction: FlexDirection.Row,
wrap: FlexWrap.Wrap,
}) {
// 遍历历史记录数组,创建Text组件展示每一条历史记录
ForEach(this.historyValueArr, (item: string, value: number) => {
Text(item)
.padding({
left: '15lpx',
right: '15lpx',
top: '7lpx',
bottom: '7lpx'
})
// 设置背景颜色、圆角和间距
.backgroundColor("#EFEFEF")
.borderRadius(10)
// .margin('11lpx')
.margin({top:5,left:20})
})
}
// 设置Flex容器的宽度和内外边距
.width('100%')
.padding({
top: '11lpx',
bottom: '11lpx',
right: '26lpx'
})
}
.width('100%')
.height('100%')
// .backgroundColor('#f1f2f3')
.backgroundImage($r('app.media.index_background1'))
.backgroundImageSize(ImageSize.Cover)
}
}
// }
个人主页代码
TypeScript
import router from '@ohos.router';
import promptAction from '@ohos.promptAction';
import UserBean from '../common/bean/UserBean';
import { InfoItem, MineItemList } from '../model/MineItemList';
@Entry
@Component
export struct MineComponent{
@State userBean:UserBean = new UserBean()
@State nickname:string = this.userBean.getNickName();//昵称
@State signature:string = this.userBean.getSignature();//签名
build(){
Column() {
Image($r('app.media.user_avtar1'))
.width('100%')
.height('25%')
.opacity(0.5)
Column() {
Shape() {
Circle({ width: 80, height: 80 }).fill('#3d3f46')
Image($r('app.media.user_avtar1'))
.width(68)
.height(68)
.margin({ top: 6, left: 6 })
.borderRadius(34)
}
Text(`${this.nickname}`)
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor(Color.White)
Text(`${this.signature}`)
.fontSize(14)
.fontColor(Color.Orange)
}
.width('95%')
.margin({ top: -34 })
//功能列表
Shape() {
Rect()
.width('100%')
.height(240)
.fill('#3d3f46')
.radius(40)
.opacity(0.5)
Column() {
List() {
ForEach(MineItemList, (item: InfoItem) => {
ListItem() {
// Text(item.title)
Row() {
Row() {
Image(item.icon)
.width(30)
.height(30)
if (item.title == '个人资料') {
Shape() {
Rect().width('80%').height(30)
.opacity(0)
Text(item.title)
.fontSize(22)
.fontColor(Color.White)
.margin({ left: 10 })
}
.onClick(() => {
router.pushUrl({
url:"myhomepage/PersonalData"
})
})
}else if (item.title == '账号与安全') {
Shape() {
Rect().width('80%').height(30)
.opacity(0)
Text(item.title)
.fontSize(22)
.fontColor(Color.White)
.margin({ left: 10 })
}
.onClick(() => {
router.pushUrl({
url:"myhomepage/AccountSecurity"
})
})
}else if (item.title == '检查更新') {
Shape() {
Rect().width('80%').height(30)
.opacity(0)
Text(item.title)
.fontSize(22)
.fontColor(Color.White)
.margin({ left: 10 })
}
.onClick(()=>{
promptAction.showToast({
message:"当前暂无更新",
duration:2000,
})
})
}else if (item.title == '关于') {
Shape() {
Rect().width('80%').height(30)
.opacity(0)
Text(item.title)
.fontSize(22)
.fontColor(Color.White)
.margin({ left: 10 })
}
.onClick(()=>{
promptAction.showToast({
message:"当前版本为1.0.0",
duration:2000,
})
})
}else{
Text(item.title)
.fontSize(22)
.fontColor(Color.White)
.margin({ left: 10 })
}
}
Image($r('app.media.right'))
.width(38)
.height(38)
}
.width('100%')
.height(52)
.justifyContent(FlexAlign.SpaceBetween)
}
// .border({
// width: { bottom: 1 },
// color: Color.Orange
// })
}, item => JSON.stringify(item))
}
.margin(10)
.border({
radius: {
topLeft: 24,
topRight: 24
}
})
// .backgroundColor('#115f7691')
.width('95%')
// .height('100%')
.margin({ top: '5%', left: '5%' })
}
}
.margin({ top: 20 })
.width('90%')
}
.width('100%')
.height('100%')
.backgroundImage($r('app.media.index_background1'))
.backgroundImageSize(ImageSize.Cover)
}
}
感谢大家的阅读和点赞,你们的支持 是我前进的动力,我会继续保持热爱,贡献自己的微薄码力!
'