arkTS开发鸿蒙OS个人商城案例【2024最新 新年限定开发案例QAQ】

时间:2024-02-17 22:28:40

龙年前述

源码获取>文章下方二维码,回复关键字“鸿蒙OS商场源码”

前言

arkTS是华为自己研发的一套前端语言,是在js和ts技术的基础上又进行了升级而成!

本篇文章会带领大家通过arkTS+node.js+mongoDB来完成一个鸿蒙OS版本的商城案例!

技术栈

1.arkTS

2.node.js

3.arkTS UI

4.express

5.mongoDB

技术栈讲解

arkTS

ArkTS是HarmonyOS应用开发语言。它在保持TypeScript(简称TS)基本语法风格的基础上,对TS的动态类型特性施加更严格的约束,引入静态类型。同时,提供了声明式UI、状态管理等相应的能力,让开发者可以以更简洁、更自然的方式开发高性能应用。

面向万物互联时代,华为提出一次开发多端部署、可分可合*流转、统一生态原生智能三大应用与服务开发理念,针对多设备、多入口、服务可分可合等特性,提供多种能力协助开发者降低开发门槛,同时HarmonyOS与OpenHarmony统一生态。HarmonyOS基于JS/TS语言体系,构建了全新的声明式开发语言ArkTS。除了兼容JS/TS语言生态,ArkTS扩展了声明式UI语法和轻量化并发机制。

ArkTS是HarmonyOS主力应用开发语言。为便于熟悉Web前端的开发者快速上手,HarmonyOS在UI开发框架中,还提供了“兼容JS的类Web开发范式”。它通过模板、样式、逻辑三段式来构建相应的应用UI界面,并结合相应的运行时实现了优化的运行体验。

基本语法

装饰器: 用于装饰类、结构、方法以及变量,并赋予其特殊的含义。如上述示例中@Entry、@Component和@State都是装饰器,@Component表示自定义组件,@Entry表示该自定义组件为入口组件,@State表示组件中的状态变量,状态变量变化会触发UI刷新。

UI描述:以声明式的方式来描述UI的结构,例如build方法中的代码块。

自定义组件:可复用的UI单元,可组合其他组件,如上述被@Component装饰的struct Hello。

系统组件:ArkUI框架中默认内置的基础和容器组件,可直接被开发者调用,比如示例中的Column、Text、Divider、Button。

属性方法:组件可以通过链式调用配置多项属性,如fontSize、width、height、backgroundColor等。

事件方法:组件可以通过链式调用设置多个事件的响应逻辑,如跟随在Button后面的onClick。 [4]

声明式UI

创建组件

配置属性

配置事件

配置子组件 [5]

状态管理

状态变量:被状态装饰器装饰的变量,改变会引起UI的渲染更新。

常规变量:没有状态的变量,通常应用于辅助计算。它的改变永远不会引起UI的刷新。

数据源/同步源:状态变量的原始来源,可以同步给不同的状态数据。通常意义为父组件传给子组件的数据。

命名参数机制:父组件通过指定参数传递给子组件的状态变量,为父子传递同步参数的主要手段。示例:CompA: ({ aProp: this.aProp })。

从父组件初始化:父组件使用命名参数机制,将指定参数传递给子组件。本地初始化的默认值在有父组件传值的情况下,会被覆盖。

初始化子节点:组件中状态变量可以传递给子组件,初始化子组件对应的状态变量。示例同上。

本地初始化:变量声明的时候赋值,作为初始化的默认值。示例:@State count: number = 0。 [6]

渲染控制

ArkUI通过自定义组件的build函数和@builder装饰器中的声明式UI描述语句构建相应的UI。在声明式描述语句中开发者除了使用系统组件外,还可以使用渲染控制语句来辅助UI的构建,这些渲染控制语句包括控制组件是否显示的条件渲染语句,基于数组数据快速生成组件的循环渲染语句以及针对大数据量场景的数据懒加载语句

node.js

Node.js发布于2009年5月,由Ryan Dahl开发,是一个基于Chrome V8引擎的JavaScript运行环境,使用了一个事件驱动、非阻塞式I/O模型, [1]让JavaScript 运行在服务端开发平台,它让JavaScript成为与PHPPythonPerlRuby等服务端语言平起平坐的脚本语言

Node.js对一些特殊用例进行优化,提供替代的API,使得V8在非浏览器环境下运行得更好,V8引擎执行Javascript的速度非常快,性能非常好,基于Chrome JavaScript运行时建立的平台, 用于方便地搭建响应速度快、易于扩展的网络应用

2009年2月,Ryan Dahl在博客上宣布准备基于V8创建一个轻量级的Web服务器并提供一套库。

2009年5月,Ryan Dahl在GitHub上发布了最初版本的部分Node包,随后几个月里,有人开始使用Node开发应用。

2009年11月和2010年4月,两届JSConf大会都安排了Node.js的讲座。

2010年年底,Node获得云计算服务商Joyent资助,创始人Ryan Dahl加入Joyent全职负责Node的发展。

2011年7月,Node在微软的支持下发布Windows版本。

2016年,leftpad事件,Yarn诞生

2021年,发布最新版本Node.js 17 [3]

V8引擎本身使用了一些最新的编译技术。这使得用Javascript这类脚本语言编写出来的代码运行速度获得了极大提升,又节省了开发成本。对性能的苛求是Node的一个关键因素。 Javascript是一个事件驱动语言,Node利用了这个优点,编写出可扩展性高的服务器。Node采用了一个称为“事件循环(event loop)”的架构,使得编写可扩展性高的服务器变得既容易又安全。提高服务器性能的技巧有多种多样。Node选择了一种既能提高性能,又能减低开发复杂度的架构。这是一个非常重要的特性。并发编程通常很复杂且布满地雷。Node绕过了这些,但仍提供很好的性能。

Node采用一系列“非阻塞”库来支持事件循环的方式。本质上就是为文件系统、数据库之类的资源提供接口。向文件系统发送一个请求时,无需等待硬盘(寻址并检索文件),硬盘准备好的时候非阻塞接口会通知Node。该模型以可扩展的方式简化了对慢资源的访问, 直观,易懂。尤其是对于熟悉onmouseoveronclickDOM事件的用户,更有一种似曾相识的感觉。

虽然让Javascript运行于服务器端不是Node的独特之处,但却是其一强大功能。不得不承认,浏览器环境限制了我们选择编程语言的*。任何服务器与日益复杂的浏览器客户端应用程序间共享代码的愿望只能通过Javascript来实现。虽然还存在其他一些支持Javascript在服务器端 运行的平台,但因为上述特性,Node发展迅猛,成为事实上的平台。

在Node启动的很短时间内,社区就已经贡献了大量的扩展库(模块)。其中很多是连接数据库或是其他软件的驱动,但还有很多是凭他们的实力制作出来的非常有用的软件。

最后,不得不提到的是Node社区。虽然Node项目还非常年轻,但很少看到对一个项目如此*的社区。不管是新手,还是专家,大家都围绕着项目,使用并贡献自己的能力,致力于打造一个探索、支持、分享、听取建议的乐土。

具备书写JavaScriptIDE,普通的记事本也可以进行开发。在几年的时间里,Node.JS逐渐发展成一个成熟的开发平台,吸引了许多开发者。有许多大型高流量网站都采用Node.JS进行开发,此外,开发人员还可以使用它来开发一些快速移动Web框架。

除了Web应用外,NodeJS也被应用在许多方面,本文盘点了NodeJS在其它方面所开发的十大令人神奇的项目,这些项目涉及到应用程序监控、媒体流、远程控制、桌面和移动应用等等。

效果图

鸿蒙端项目架构

shouye.ets

import  axios  from '@ohos/axios'
import router from '@ohos.router'
@Component
export struct app_shouye{
  @State zhanghao: object = router.getParams()
  // 设置搜索框的提示内容
  private changeValue:string = ''
  @State selectedItemId : number = 0
  @State one:string = ''
  // 设置商品的渲染列表Object
  @State items: Array<Object> = [];
   aboutToAppear() {
     axios({
      method: "get",
      url: 'http://localhost:3000/shangpins/find_all',
    }).then(res => {
      console.info('result:' + JSON.stringify(res.data.data));
      this.items = res.data.data
    }).catch(error => {
      console.error(error);
    })
     console.log(JSON.stringify(this.items))
  }


  build(){
    Column(){
      // 搜索栏
      Search({ value: this.changeValue, placeholder: '请输入搜索的商品',})
        .searchButton('搜索')
        .width(300)
        .height(40)
        .backgroundColor('#F5F5F5')
        .placeholderColor(Color.Grey)
        .placeholderFont({ size: 14, weight: 400 })
        .textFont({ size: 14, weight: 400 })
        .onSubmit((value: string) => {
          router.pushUrl({
            url: 'pages/sousuo',
            params: {
              sousuoValue: this.changeValue,
            }
          })
        })
        .onChange((value: string) => {
          this.changeValue = value
          console.log(this.changeValue)
        })
        .margin(20)

        // 显示商品列表
        List({ space: 20, initialIndex: 0 }){
          ForEach(this.items, (item) => {
            ListItem() {
              Row(){
                Image(item.img)
                  .alt($r('app.media.icon'))// 使用alt,在网络图片加载成功前使用占位图
                  .width(150)
                  .height(180)
                Column({space:10}){
                  Text(item.name)
                    .fontWeight(800)
                    .margin(20)
                  Text(item.detail)
                  Text('¥:'+item.price)
                    .fontColor('red')
                    .fontWeight(FontWeight.Bold)
                    .fontSize(20)
                }
                .width(180)
              }.alignItems(VerticalAlign.Top)
            }
            .onClick(() => {
              this.selectedItemId = item._id;
              // 获取点击物品的id
              this.one = item._id
              console.log(JSON.stringify(this.one))
              router.pushUrl({
                url: 'pages/detail',
                params: {
                  id: this.one,
                  zhanghao:this.zhanghao?.['zhanghao']
                }
              })
            })
          })
        }
        .height('90%')
        .listDirection(Axis.Vertical) // 排列方向
        .divider({ strokeWidth: 2, color: 0xFFFFFF, startMargin: 20, endMargin: 20 }) // 每行之间的分界线
        .edgeEffect(EdgeEffect.Spring) // 滑动到边缘无效果
    }
    .width('98%')
    .height('100%')
  }

}

fenlei.ets

import  axios  from '@ohos/axios'
import router from '@ohos.router'
@Component
export struct app_shouye{
  @State zhanghao: object = router.getParams()
  // 设置搜索框的提示内容
  private changeValue:string = ''
  @State selectedItemId : number = 0
  @State one:string = ''
  // 设置商品的渲染列表Object
  @State items: Array<Object> = [];
   aboutToAppear() {
     axios({
      method: "get",
      url: 'http://localhost:3000/shangpins/find_all',
    }).then(res => {
      console.info('result:' + JSON.stringify(res.data.data));
      this.items = res.data.data
    }).catch(error => {
      console.error(error);
    })
     console.log(JSON.stringify(this.items))
  }


  build(){
    Column(){
      // 搜索栏
      Search({ value: this.changeValue, placeholder: '请输入搜索的商品',})
        .searchButton('搜索')
        .width(300)
        .height(40)
        .backgroundColor('#F5F5F5')
        .placeholderColor(Color.Grey)
        .placeholderFont({ size: 14, weight: 400 })
        .textFont({ size: 14, weight: 400 })
        .onSubmit((value: string) => {
          router.pushUrl({
            url: 'pages/sousuo',
            params: {
              sousuoValue: this.changeValue,
            }
          })
        })
        .onChange((value: string) => {
          this.changeValue = value
          console.log(this.changeValue)
        })
        .margin(20)

        // 显示商品列表
        List({ space: 20, initialIndex: 0 }){
          ForEach(this.items, (item) => {
            ListItem() {
              Row(){
                Image(item.img)
                  .alt($r('app.media.icon'))// 使用alt,在网络图片加载成功前使用占位图
                  .width(150)
                  .height(180)
                Column({space:10}){
                  Text(item.name)
                    .fontWeight(800)
                    .margin(20)
                  Text(item.detail)
                  Text('¥:'+item.price)
                    .fontColor('red')
                    .fontWeight(FontWeight.Bold)
                    .fontSize(20)
                }
                .width(180)
              }.alignItems(VerticalAlign.Top)
            }
            .onClick(() => {
              this.selectedItemId = item._id;
              // 获取点击物品的id
              this.one = item._id
              console.log(JSON.stringify(this.one))
              router.pushUrl({
                url: 'pages/detail',
                params: {
                  id: this.one,
                  zhanghao:this.zhanghao?.['zhanghao']
                }
              })
            })
          })
        }
        .height('90%')
        .listDirection(Axis.Vertical) // 排列方向
        .divider({ strokeWidth: 2, color: 0xFFFFFF, startMargin: 20, endMargin: 20 }) // 每行之间的分界线
        .edgeEffect(EdgeEffect.Spring) // 滑动到边缘无效果
    }
    .width('98%')
    .height('100%')
  }

}

gouwuche.ets

import router from '@ohos.router'
import  axios  from '@ohos/axios'
import { Header } from '../components/Toubu'
@Component
export struct gouwuche{
  @State zhanghao: object = router.getParams()
  // 设置商品的渲染列表Object
  @State items: Array<Object> = [];

  aboutToAppear() {
    axios({
      method: "post",
      url: 'http://localhost:3000/gouwuche/find',
      data:{
        username:this.zhanghao?.['zhanghao']
      }
    }).then(res => {
      console.info('result1111:' + JSON.stringify(res.data.data));
      this.items = res.data.data
    }).catch(error => {
      console.error(error);
    })
    console.log(JSON.stringify(this.items))
  }

  build(){
    //   标题部分
    Column(){
      // 自定义组件之间传参
      // Header({message:true})
      Image($r('app.media.shuaxin'))
        .width(30)
        .margin(20)
        .onClick(() =>{
          axios({
            method: "post",
            url: 'http://localhost:3000/gouwuche/find',
            data:{
              username:this.zhanghao?.['zhanghao']
            }
          }).then(res => {
            console.info('result1111:' + JSON.stringify(res.data.data));
            this.items = res.data.data
          }).catch(error => {
            console.error(error);
          })
        })
      // Text(this.zhanghao?.['zhanghao'])
      List({ space: 20, initialIndex: 0 }){
        ForEach(this.items, (item) => {
          ListItem() {
            Row(){
              Image(item.img)
                .alt($r('app.media.icon'))// 使用alt,在网络图片加载成功前使用占位图
                .width(150)
                .height(180)
              Column({space:10}){
                Text(item.name)
                  .fontWeight(800)
                  .margin(20)
                Text(item.detail)
                Text('¥:'+item.price)
                  .fontColor('red')
                  .fontWeight(FontWeight.Bold)
                  .fontSize(20)
              }
              .width(180)
            }.alignItems(VerticalAlign.Top)
          }
        })
      }
      .height('85%')
      .listDirection(Axis.Vertical) // 排列方向
      .divider({ strokeWidth: 2, color: 0xFFFFFF, startMargin: 20, endMargin: 20 }) // 每行之间的分界线
      .edgeEffect(EdgeEffect.Spring) // 滑动到边缘无效果

      Row({space:30}){
        Button('清除购物车', { type: ButtonType.Normal, stateEffect: true })
          .borderRadius(8)
          .backgroundColor(0x317aff)
          .width(150)
          .onClick(() => {
            console.log('ButtonType.Normal')
          })

        Button('立即付款', { type: ButtonType.Normal, stateEffect: true })
          .borderRadius(8)
          .backgroundColor(0x317aff)
          .width(150)
          .onClick(() => {
            console.log('ButtonType.Normal')
          })
      }
    }
    .width('100%')
    .height('100%')
  }
}

Toubu.ets

import router from '@ohos.router'
@Component
export struct Header{
  @State message: Boolean = false
  build(){
  //   标题部分
    Row({space:5}){
      Image($r('app.media.fanhui'))
        .width(20)
        .onClick(() =>{
          router.back()
        })
      Blank()
      Image($r('app.media.shuaxin'))
        .width(30)
        .onClick(() =>{
          router.back()
        })
    }
    .width('98%')
    .height(30)
  }
}

wode.ets

import router from '@ohos.router'
@Component
export struct app_wode{
  @State zhanghao: object = router.getParams()

  build(){
    //   标题部分
    Column(){
      Row(){
        Row({space:10}){
          Image($r('app.media.icon'))
            .width(50)
            .height(50)
          Text('昵称:'+ this.zhanghao?.['zhanghao'])
            .margin({left:20})
            .fontColor('#fff')
        }
        .margin({top:30,left:50})
      }
      .width('100%')
      .height(180)
      .backgroundImage('./components/img/app_wo_bj.jpg')
      .backgroundImageSize(ImageSize.Cover)


        Row({space:50}){
          Image($r('app.media.icon'))
            .width(50)
            .height(50)
          Text('购物记录')
            .fontSize(18)
        }
        .width('100%')
        .backgroundColor("#fff")
        .margin({top:20,bottom:20})
        .padding({left:20,right:20,top:10,bottom:10})
        .onClick(()=>{
          router.pushUrl({
            url: 'pages/GouwuJilu',
          })
          console.log('123')
        })

        Row({space:50}){
          Image($r('app.media.icon'))
            .width(50)
            .height(50)
          Text('修改信息')
            .fontSize(18)
        }
        .width('100%')
        .backgroundColor("#fff")
        .margin({bottom:20})
        .padding({left:20,right:20,top:10,bottom:10})
        .onClick(()=>{
          router.pushUrl({
            url: 'pages/XiugaiXinxi',
          })
          console.log('123')
        })

        Row({space:50}){
          Image($r('app.media.icon'))
            .width(50)
            .height(50)
          Text('退出登录')
            .fontSize(18)
        }
        .width('100%')
        .backgroundColor("#fff")
        .margin({bottom:20})
        .padding({left:20,right:20,top:10,bottom:10})
        .onClick(()=>{
          router.replace({
            url: 'pages/Index',
          })
          console.log('123')
        })
    }
    .alignItems(HorizontalAlign.Start)
    .width('100%')
    .height('100%')
    .backgroundColor('#f3f3f3')
  }
}

detail.ets

import { Header } from '../components/Toubu'
import router from '@ohos.router'
import  axios  from '@ohos/axios'
@Entry
@Component
struct Detail {
  @State shangpin_detail: object = router.getParams()
  @State items:Array<Object> = []

  build() {
    Row() {
      Column() {
        Header()
        Text(this.shangpin_detail?.['zhanghao'])
        Text(this.items[0]?.['name'])

        Flex({ wrap: FlexWrap.NoWrap }) { // 子组件单行布局
          Text('').width('20%')
          List({ space: 20, initialIndex: 0 }) {
            ForEach(this.items, (item) => {
              ListItem() {
                Column() {
                  Image(item.img)
                    .alt($r('app.media.icon')) // 使用alt,在网络图片加载成功前使用占位图
                    .width(300)
                    .height(300)
                  Text(item.name)
                    .fontWeight(800)
                    .margin(20)
                  Text('¥:' + item.price)
                    .fontColor('red')
                    .fontWeight(FontWeight.Bold)
                    .fontSize(20)
                  Text(item.detail)
                }
              }
            })
          }
          .height('85%')
          .listDirection(Axis.Vertical) // 排列方向
          .divider({ strokeWidth: 2, color: 0xFFFFFF, startMargin: 20, endMargin: 20 }) // 每行之间的分界线
          .edgeEffect(EdgeEffect.Spring) // 滑动到边缘无效果
          Text('').width('20%')
        }
      //   加入购物车,立即购买
        Row({space:30}){
          Button('加入购物车', { type: ButtonType.Normal, stateEffect: true })
            .borderRadius(8)
            .backgroundColor(0x317aff)
            .width(150)
            .onClick(() => {
              axios({
                method: "post",
                url: 'http://localhost:3000/gouwuche/publish/',
                data: {
                  username: this.shangpin_detail?.['zhanghao'],
                  name:this.items[0]?.['name'],
                  detail:this.items[0]?.['detail'],
                  img:this.items[0]?.['img'],
                  select_classify:this.items[0]?.['select_classify'],
                  price:this.items[0]?.['price']
                }
              }).then(res => {
                console.log(JSON.stringify(res.data.data));
                this.items = res.data.data
              }).catch(error => {
                console.error(error);
              });
              console.log('ButtonType.Normal')
            })

          Button('立即购买', { type: ButtonType.Normal, stateEffect: true })
            .borderRadius(8)
            .backgroundColor(0x317aff)
            .width(150)
            .onClick(() => {
              console.log('ButtonType.Normal')
            })
        }
      }
      .width('100%')
    }.height('100%')
  }

  onPageShow(){
    axios({
      method: "post",
      url: 'http://localhost:3000/shangpins/find_detail/',
      data: {
        _id: this.shangpin_detail?.['id']
      }
    }).then(res => {
      console.log(JSON.stringify(res.data.data));
      this.items = res.data.data
    }).catch(error => {
      console.error(error);
    });
  }
}

GouwuJilu.ets

import { Header } from '../components/Toubu'
@Entry
@Component
struct GouwuJilu {
  @State message: string = '购物记录'

  build() {
    Column() {
      Header()
        Text(this.message)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
    }
    .width('100%')
    .height('100%')
  }
}

Index.ets

import  axios  from '@ohos/axios'
import router from '@ohos.router'
@Entry
@Component
struct Index {
  // 上传数据
  @State zhanghao: string = ''
  @State mima: string = ''
  @State zhanghao_find:string =''
  @State mima_find:string =''
  build() {
      Column() {
        Text('龙年千帆启鸿蒙')
          .margin({top:70})
          .fontWeight(FontWeight.Bold)
          .fontSize(30)
        Image($r('app.media.icon'))
          .width(150)
          .margin({top:50,bottom:20})
        // 账号登录
        TextInput({placeholder:'账号'})
          .margin(20)
          .height(50)
          .onChange(value =>{
            console.log(value)
            this.zhanghao_find = value
          })
          .backgroundColor('#36D2')
        TextInput({placeholder:'密码'})
          .margin({left:20,right:20,bottom:25})
          .height(50)
          .onChange(value =>{
            console.log(value)
            this.mima_find = value
          })
          .backgroundColor('#36D2')
        Button('登录')
          .width(200)
          .onClick(()=>{
            axios({
              method: "get",
              url: 'http://localhost:3000/users/find/'+this.zhanghao_find+ '/' + this.mima_find,
            }).then(res => {
              // console.info('result:' + JSON.stringify(res.data));
              console.info('result:' + JSON.stringify(res.data));
                // 获取data数组中的第一个元素
                const firstData = res.data.data[0];
                // 获取zhanghao字段的值
                const zhanghaoValue = firstData.zhanghao;
                console.log('zhanghaoValue:', zhanghaoValue);
              // 获取data数组中的第一个元素
              // 获取zhanghao字段的值
                router.pushUrl({
                  url: 'pages/NewApp_one',
                  params: {
                    zhanghao: zhanghaoValue,
                  }
                })
            }).catch(error => {
              console.error(error);
            })
          })
        Row(){
          Text('注册')
            .margin({right:5})
            .onClick( () =>{
              {
                router.pushUrl({
                  url: 'pages/zhuce',
                })
              }
            })
          Text('|')
          Text('忘记密码')
            .margin({left:5})
            .onClick( () =>{
              {
                router.pushUrl({
                  url: 'pages/WangjiMima',
                })
              }
            })
        }.margin(20)
      }
      .width('100%')
    .height('100%')
  }
}

NewApp_one.ets

import { app_shouye } from '../components/shouye/shouye'
import { app_wode } from '../components/wode'
import { app_fenlei } from '../components/fenlei'
import { gouwuche } from '../components/gouwuche'
import router from '@ohos.router'

@Entry
@Component
struct NewApp_one {
  // 获取上一个页面传过来的该登录的用户名
  @State zhanghao: object = router.getParams()

  build() {
      Column() {
        Tabs({ barPosition: BarPosition.End }) {
          TabContent() {
              app_shouye({ zhanghao: this.zhanghao?.['zhanghao'] })
          }
          .tabBar((new BottomTabBarStyle($r('sys.media.ohos_app_icon'),'首页')))
          TabContent() {
            app_fenlei()
          }
          .tabBar((new BottomTabBarStyle($r('sys.media.ohos_app_icon'),'分类')))

          TabContent() {
            gouwuche({ zhanghao: this.zhanghao?.['zhanghao'] })
          }
          .tabBar((new BottomTabBarStyle($r('sys.media.ohos_app_icon'),'购物车')))

          TabContent() {
            app_wode({ zhanghao: this.zhanghao?.['zhanghao'] })
          }
          .tabBar((new BottomTabBarStyle($r('sys.media.ohos_app_icon'),'我的')))
        }
      }
      .width('100%')
    .height('100%')
  }
  onPageShow(){
    console.log('这是父组件显示页面生命周期函数')
  }
}

sousuo.ets

import router from '@ohos.router'
import  axios  from '@ohos/axios'
import { Header } from '../components/Toubu'
@Entry
@Component
struct Sousuo {
  @State sousuoValue: object = router.getParams()
  @State items:Array<Object> = []
  @State selectedItemId : number = 0
  @State one:string = ''
  private changeValue:string = this.sousuoValue?.['sousuoValue']
  build() {
      Column() {
        Header()
        Search({ value: this.changeValue, placeholder: '请输入搜索的商品',})
          .searchButton('搜索')
          .width(300)
          .height(40)
          .backgroundColor('#F5F5F5')
          .placeholderColor(Color.Grey)
          .placeholderFont({ size: 14, weight: 400 })
          .textFont({ size: 14, weight: 400 })
          // 点击搜索
          .onSubmit((value: string) => {
            axios({
              method: "post",
              url: 'http://localhost:3000/shangpins/products/',
              data: {
                changeValue: this.changeValue
              }
            }).then(res => {
              console.log(JSON.stringify(res.data.data));
              this.items = res.data.data
            }).catch(error => {
              console.error(error);
            });
          })
          // 输入搜索
          .onChange((value: string) => {
            this.changeValue = value
            console.log(this.changeValue)
            axios({
              method: "post",
              url: 'http://localhost:3000/shangpins/products/',
              data: {
                changeValue: value
              }
            }).then(res => {
              console.log(JSON.stringify(res.data.data));
              this.items = res.data.data
            }).catch(error => {
              console.error(error);
            });
          })
          .margin(20)

        List({ space: 20, initialIndex: 0 }){
          ForEach(this.items, (item) => {
            ListItem() {
              Row(){
                Image(item.img)
                  .alt($r('app.media.icon'))// 使用alt,在网络图片加载成功前使用占位图
                  .width(150)
                  .height(180)
                Column({space:10}){
                  Text(item.name)
                    .fontWeight(800)
                    .margin(20)
                  Text(item.detail)
                  Text('¥:'+item.price)
                    .fontColor('red')
                    .fontWeight(FontWeight.Bold)
                    .fontSize(20)
                }
                .width(180)
              }.alignItems(VerticalAlign.Top)
            }
            .onClick(() => {
              this.selectedItemId = item._id;
              // 获取点击物品的id
              this.one = item._id
              console.log(JSON.stringify(this.one))
              router.pushUrl({
                url: 'pages/detail',
                params: {
                  id: this.one,
                }
              })
            })
          })
        }
        .height('90%')
        .listDirection(Axis.Vertical) // 排列方向
        .divider({ strokeWidth: 2, color: 0xFFFFFF, startMargin: 20, endMargin: 20 }) // 每行之间的分界线
        .edgeEffect(EdgeEffect.Spring) // 滑动到边缘无效果
      }
      .width('100%')
    .height('100%')
  }

  //上一个页面跳转过来之后查询的数据
  onPageShow() {
    console.log('wwww' + this.sousuoValue?.['sousuoValue']);
    axios({
      method: "post",
      url: 'http://localhost:3000/shangpins/products/',
      data: {
        changeValue: this.sousuoValue?.['sousuoValue'] // 修正获取参数的方式
      }
    }).then(res => {
      console.log(JSON.stringify(res.data.data));
      this.items = res.data.data
    }).catch(error => {
      console.error(error);
    });
  }

}

WangjiMima.ets

import { Header } from '../components/Toubu'
import  axios  from '@ohos/axios'
import router from '@ohos.router'
@Entry
@Component
struct Index {
  // 上传数据
  @State zhanghao: string = ''
  @State mima: string = ''

  build() {
    Column() {
      Header()
        .margin(20)
      TextInput({placeholder:'原账号'})
        .margin(20)
        .height(50)
        .onChange(value =>{
          console.log(value)
          this.zhanghao = value
        })
        .backgroundColor('#36D2')
      TextInput({placeholder:'新密码'})
        .margin({ left:20,right:20,bottom:20 })
        .height(50)
        .onChange(value =>{
          console.log(value)
          this.mima = value
        })
        .backgroundColor('#36D2')
      Button('修改密码')
        .width(200)
        .onClick(()=>{
          axios({
            method: "post",
            url: 'http://localhost:3000/users/upd',
            data:{
              zhanghao:this.zhanghao,
              newmima:this.mima
            },
          }).then(res => {
            console.info('result:' + JSON.stringify(res.data));
            {
              router.pushUrl({
                url: 'pages/NewApp_one',
              })
            }
          }).catch(error => {
            console.error(error);
          })
        })
    }
    .width('100%')
    .height('100%')
  }
}

XiugaiXinxi.ets

import { Header } from '../components/Toubu'
@Entry
@Component
struct XiugaiXinxi {
  @State message: string = '修改信息'

  build() {
      Column() {
        Header()

        Text(this.message)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
      }
      .width('100%')
    .height('100%')
  }
}

zhuce.ets

import { Header } from '../components/Toubu'
import  axios  from '@ohos/axios'
import router from '@ohos.router'
@Entry
@Component
struct Index {
  // 上传数据
  @State zhanghao: string = ''
  @State mima: string = ''
  @State zhanghao_find:string =''
  @State mima_find:string =''

  build() {
    Column() {
      Header()
        .margin(20)
      TextInput({placeholder:'注册账号'})
        .margin(20)
        .height(50)
        .onChange(value =>{
          console.log(value)
          this.zhanghao = value
        })
        .backgroundColor('#36D2')
      TextInput({placeholder:'注册密码'})
        .margin({ left:20,right:20,bottom:20 })
        .height(50)
        .onChange(value =>{
          console.log(value)
          this.mima = value
        })
        .backgroundColor('#36D2')
      Button('注册并登录')
        .width(200)
        .onClick(()=>{
          axios({
            method: "post",
            url: 'http://localhost:3000/users/publish',
            data:{
              zhanghao:this.zhanghao,
              mima:this.mima
            },
          }).then(res => {
            console.info('result:' + JSON.stringify(res.data));
              router.pushUrl({
                url: 'pages/NewApp_one',
              })
          }).catch(error => {
            console.error(error);
          })
        })
    }
    .width('100%')
    .height('100%')
  }
}

node.js后端架构

fenlei_api.js

// user_api.js
const express = require('express');
const router = express.Router();
const { fenlei } = require('../db');

router.use(express.urlencoded({ extended: true }));
router.use(express.json());

// 全部查询
router.get("/find_all", async (req, res) => { // Corrected function signature
    try {
      const results = await fenlei.find();

      if (results.length > 0) {
        // 如果找到匹配的记录,则返回所有匹配的记录
        res.json({ data: results, message: "登录成功!" });
      } else {
        res.status(404).json({ message: "未找到匹配的记录" });
      }
    } catch (error) {
      res.status(500).json({ message: "服务器内部错误" });
    }
  });

// 指定查询
router.get("/find/:name", async (req, res) => {
    try {
        const name = req.params.name;
        // 使用 find 查询所有匹配指定 name 的数据记录
        const results = await fenlei.find({ name });

        if (results.length > 0) {
            // 如果找到匹配的记录,则返回所有匹配的记录
            res.json({ data: results, message: "登录成功!" });
        } else {
            res.status(404).json({ message: "未找到匹配的记录" });
        }
    } catch (error) {
        res.status(500).json({ message: "服务器内部错误" });
    }
});

module.exports = router;

gouwuche_api.js

// user_api.js
const express = require('express');
const router = express.Router();
const { gouwuche } = require('../db');

router.use(express.urlencoded({ extended: true }));
router.use(express.json());

// 全部查询
router.post("/find", async (req, res) => {
    try {
      const { username } = req.body;
      // 使用 find 查询所有匹配指定 select_classify 的数据记录
      const results = await gouwuche.find({ username });
  
      if (results.length > 0) {
        // 如果找到匹配的记录,则返回所有匹配的记录
        res.json({ data: results, message: "登录成功!" });
      } else {
        res.status(404).json({ message: "未找到匹配的记录" });
      }
    } catch (error) {
      res.status(500).json({ message: "服务器内部错误" });
    }
  });
//   添加到购物车
router.post("/publish", async (req, res) => {
    try {
        const { username,name,detail,img,select_classifyprice,price } = req.body;
        await gouwuche.create({
            username,
            name,
            detail,
            img,
            select_classifyprice,
            price
        });
        res.send("success");
    } catch (error) {
        res.send(error, "error");
    }
});

module.exports = router;

shangpin_api.js

// user_api.js
const express = require('express');
const router = express.Router();
const { shangpin } = require('../db');

router.use(express.urlencoded({ extended: true }));
router.use(express.json());

// 全部查询
router.get("/find_all", async (req, res) => { // Corrected function signature
  try {
    const results = await shangpin.find();

    if (results.length > 0) {
      // 如果找到匹配的记录,则返回所有匹配的记录
      res.json({ data: results, message: "登录成功!" });
    } else {
      res.status(404).json({ message: "未找到匹配的记录" });
    }
  } catch (error) {
    res.status(500).json({ message: "服务器内部错误" });
  }
});

// 指定查询
router.post("/find", async (req, res) => {
  try {
    const { select_classify } = req.body;
    // 使用 find 查询所有匹配指定 select_classify 的数据记录
    const results = await shangpin.find({ select_classify });

    if (results.length > 0) {
      // 如果找到匹配的记录,则返回所有匹配的记录
      res.json({ data: results, message: "登录成功!" });
    } else {
      res.status(404).json({ message: "未找到匹配的记录" });
    }
  } catch (error) {
    res.status(500).json({ message: "服务器内部错误" });
  }
});

// 指定商品详情查询
router.post("/find_detail", async (req, res) => {
  try {
    const { _id } = req.body;
    // 使用 find 查询所有匹配指定 _id 的数据记录
    const results = await shangpin.find({ _id });

    if (results.length > 0) {
      // 如果找到匹配的记录,则返回所有匹配的记录
      res.json({ data: results, message: "登录成功!" });
    } else {
      res.status(404).json({ message: "未找到匹配的记录" });
    }
  } catch (error) {
    res.status(500).json({ message: "服务器内部错误" });
  }
});


//模糊查询
router.post('/products', async (req, res) => {
  try {
      const changeValue = req.body.changeValue; // 修正获取请求体中的参数方式

      // 使用正则表达式进行模糊查询
      const results = await shangpin.find({ name: { $regex: changeValue, $options: 'i' } });
      if (results.length > 0) {
          // 如果找到匹配的记录,则返回所有匹配的记录
          res.json({ data: results, message: "查询成功!" });
      } else {
          res.status(404).json({ message: "未找到匹配的记录" });
      }
  } catch (error) {
      console.error(error);
      res.status(500).json({ message: "服务器内部错误" });
  }
});



module.exports = router;

user_api.js

// user_api.js
const express = require('express');
const router = express.Router();
const { users } = require('../db');

router.use(express.urlencoded({ extended: true }));
router.use(express.json());

// 注册账号
router.post("/publish", async (req, res) => {
    try {
        const { zhanghao, mima } = req.body;
        await users.create({
            zhanghao, mima
        });
        res.send("success");
    } catch (error) {
        res.send(error, "error");
    }
});

// 注销账号
router.post("/del", async (req, res) => {
    console.log(req.body.zhanghao);
    try {
        const { zhanghao } = req.body;

        // 使用 deleteOne 删除指定 name 的数据
        const result = await users.deleteOne({ zhanghao });

        if (result.deletedCount === 1) {
            res.send("success");
        } else {
            res.send("未找到匹配的记录");
        }
    } catch (error) {
        res.send(error, "error");
    }
});

// 修改账号密码
router.post("/upd", async (req, res) => {
    try {
        const { zhanghao, newmima } = req.body;
        // 使用 updateOne 更新指定 name 的数据记录的 nianling 字段
        const result = await users.updateOne({ zhanghao }, { $set: { mima: newmima } });
        res.json({ message: "密码更新成功!", result });
    } catch (error) {
        res.status(500).json({ error: error.message });
    }
});

// 账号登录
router.get("/find/:zhanghao/:mima", async (req, res) => {
    try {
        const zhanghao = req.params.zhanghao;
        const mima = req.params.mima;

        // 使用 find 查询所有匹配指定 name 的数据记录
        const results = await users.find({ zhanghao, mima });

        if (results.length > 0) {
            // 如果找到匹配的记录,则返回所有匹配的记录
            res.json({ data: results, message: "登录成功!" });
        } else {
            res.status(404).json({ message: "未找到匹配的记录" });
        }
    } catch (error) {
        res.status(500).json({ message: "服务器内部错误" });
    }
});

module.exports = router;

db.js

const mongoose = require('mongoose')

//连接mongodb数据库
mongoose.connect("mongodb://localhost:27017/node_one")
    .then(() => {
        console.log("数据库连接成功!")
    })
    .catch((err) => {
        console.log("数据库连接失败!", err)
    })

// 创建表用户表
const Users = new mongoose.Schema({
    zhanghao: {
        type: String,
    },
    mima: {
        type: String
    },
})
// 创建商品表
const Shangpin = new mongoose.Schema({
    name: {
        type: String,
    },
    detail: {
        type: String
    },
    img:{
        type: String
    },
    select_classify:{
        type: String
    },
    price:{
        type: String 
    }
})
//创建分类表
const Fenlei = new mongoose.Schema({
    name: {
        type: String,
    },
})
// 创建购物车表
const Gouwuche = new mongoose.Schema({
    username:{
        type: String,
    },
    name: {
        type: String,
    },
    detail: {
        type: String
    },
    img:{
        type: String
    },
    select_classify:{
        type: String
    },
    price:{
        type: String 
    }
})

const users = mongoose.model("Users", Users);
const shangpin = mongoose.model("Shangpin", Shangpin);
const fenlei = mongoose.model("Fenlei", Fenlei);
const gouwuche = mongoose.model("Gouwuche", Gouwuche);
module.exports = {
    users,
    shangpin,
    fenlei,
    gouwuche
}

index.js

// index.js
const express = require('express');
const app = express();
const userApi = require('./user_ctrl/user_api');
const shangpinApi = require('./shangpin_ctrl/shangpin_api');
const fenleiApi = require('./fenlei_ctrl/fenlei_api');
const gouwucheApi = require('./gouwuche_ctrl/gouwuche_api');

app.use('/users', userApi);

app.use('/shangpins', shangpinApi);

app.use('/gouwuche', gouwucheApi);

app.use('/fenleis', fenleiApi);

app.listen(3000, () => {
    console.log('server running');
});

后端安装指令

1. 下载node.js框架

npm install express --save

2. 下载nodemon解决node代码更新的痛点

npm install nodemon -g

3. node.js连接mongodb数据库

npm install mongoose --save