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

龙年前述

源码获取>文章下方二维码,回复关键字“鸿蒙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成为与PHP、Python、Perl、Ruby等服务端语言平起平坐的脚本语言。

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。该模型以可扩展的方式简化了对慢资源的访问, 直观,易懂。尤其是对于熟悉onmouseover、onclick等DOM事件的用户,更有一种似曾相识的感觉。

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

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

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

具备书写JavaScript的IDE,普通的记事本也可以进行开发。在几年的时间里,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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/387762.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

flask cors 跨域问题解决

座右铭&#xff1a;怎么简单怎么来&#xff0c;以实现功能为主。 欢迎大家关注公众号与我交流 环境安装 pip install -U flask-cors 示例代码 from flask import Flask from flask_cors import CORS, cross_originapp Flask(__name__) CORS(app, supports_credentialsTrue)…

__attribute__ ---Compile

Section for attribute attribute_&#xff1f;嵌入式C代码属性怎么定义 https://www.elecfans.com/d/2269222.html section 属性的主要作用是&#xff1a;在程序编译时&#xff0c;将一个函数或者变量放到指定的段&#xff0c;即指定的section 中。 一个可执行文件注意由代…

AI算法初识之分类汇总

一、背景 AI算法的分类方式多种多样&#xff0c;可以根据不同的学习机制、功能用途以及模型结构进行划分。以下是一些主要的分类方式及相应的代表性算法&#xff1a; 1. 按照学习类型 - **监督学习**&#xff1a; - 线性回归&#xff08;Linear Regression&#xff09; …

学会如何备份u盘数据,让数据安全有保障

随着科技的发展&#xff0c;U盘已成为我们日常生活和工作中不可或缺的数据存储设备。然而&#xff0c;无论U盘的质量如何&#xff0c;数据丢失的风险始终存在。可能是硬件故障、意外删除、病毒感染或其他不可预见的原因。 尽管当前提供了多种数据恢复方案&#xff0c;然而没有一…

【Midjourney】解密Midjourney付费订阅:畅享全新体验!(详细流程与各版本一览)

一、Midjourney 付费订阅流程 1、在首页点击Purchase plan 2、进入到midjourney年月选择页面 3、这里续费一个最便宜的版本 , 按年付费 8 , 按月 10 4、输入银行卡信息 , 用的WildCard虚拟信用卡 &#xff0c;打开 5、填写完银行卡信息就订阅成功 二、Midjourney 各版本介绍…

山西电力市场日前价格预测【2024-02-12】

日前价格预测 预测说明&#xff1a; 如上图所示&#xff0c;预测明日&#xff08;2024-02-12&#xff09;山西电力市场全天平均日前电价为127.42元/MWh。其中&#xff0c;最高日前电价为369.24元/MWh&#xff0c;预计出现在18:45。最低日前电价为0.00元/MWh&#xff0c;预计出…

QT 菜单栏

添加/删除菜单栏 默认情况下QMainWindow项目一创建就自带了菜单栏&#xff0c;可以在对象树窗口中&#xff0c;右键菜单栏对象&#xff0c;移除菜单栏&#xff1a; 删除后也可以创建菜单栏&#xff0c;此时在对象树中右键MainWindow对象&#xff0c;菜单里边会多了创建菜单栏的…

[OPEN SQL] 新增数据

INSERT语句用于数据的新增操作 本次操作使用的数据库表为SCUSTOM&#xff0c;其字段内容如下所示 航班用户(SCUSTOM) 该数据库表中的部分值如下所示 1.插入单条数据 语法格式 INSERT <dbtab> FROM <wa>. INSERT INTO <dbtab> VALUES <wa>. INSERT &…

Hive的相关概念——分区表、分桶表

目录 一、Hive分区表 1.1 分区表的概念 1.2 分区表的创建 1.3 分区表数据加载及查询 1.3.1 静态分区 1.3.2 动态分区 1.4 分区表的本质及使用 1.5 分区表的注意事项 1.6 多重分区表 二、Hive分桶表 2.1 分桶表的概念 2.2 分桶表的创建 2.3 分桶表的数据加载 2.4 …

数据库第一次实验

目录 1 实验内容 2 SQL代码 3 效果截图 1 实验内容 熟悉SQL实验环境配置和进行实验数据准备&#xff0c;用SQL Server、PostgreSQL或MySQL创建数据库&#xff0c; 并按照下列关系模式定义数据表&#xff0c;加入适当约束&#xff1a; 学生&#xff08;学号、姓名、性别、…

free pascal:fpwebview 组件通过JSBridge调用本机TTS

从 https://github.com/PierceNg/fpwebview 下载 fpwebview-master.zip 简单易用。 先请看 \fpwebview-master\README.md cd \lazarus\projects\fpwebview-master\demo\js_bidir 学习 js_bidir.lpr &#xff0c;编写 js_bind_speak.lpr 如下&#xff0c;通过JSBridge调用本机…

在中国做 DePIN?你需要明白风险与机遇

撰文&#xff1a;肖飒团队 来源Techub News专栏作者 随着科技的发展&#xff0c;我们正在日益进入一个资源相对过剩的时代&#xff0c;这使我们在日常生活中虽然支付了该部分资源的使用费&#xff0c;但却时常不能将其「物尽其用」&#xff0c;难免出现资源浪费。例如&#x…

秒懂百科,C++如此简单丨第十九天:动态规划

目录 动态规划的初步理解 求最短路径数 洛谷 P1002 过河卒 题目描述 输入样例 输出样例 思路 AC Code 动态规划的初步理解 什么是动态规划&#xff1f;最直白的理解就是动态的规划。 那高级一点的理解呢&#xff1f;就是每时每刻都拿着一个小本本&#xff0c;也就是…

模型 人货场

系列文章 主要是 分享 思维模型&#xff0c;涉及各个领域&#xff0c;重在提升认知。连接消费者与商品的桥梁。 1 ”人货场“模型的应用 1.1 以抖音直播电商为背景的人货场应用-小杨哥的带货奇迹 小杨哥&#xff0c;一位知名的抖音主播&#xff0c;以其幽默风趣的直播风格和独…

Vegeta压测工具学习与使用

Vegeta压测工具学习与使用 目标&#xff1a; 能够在命令行下使用Vegeta对指定API进行测试了解如何导出结果&#xff0c;以及能获得什么样的结果(P99,P99.9,QPS)探索能否导出其他结果&#xff0c;是否能够执行复杂命令或简易脚本等 时间比较紧迫&#xff0c;预计两到三个小时内完…

pytorch tensor维度变换

目录 1. view/reshape2. squeeze/unsqueeze3. expand 扩展4. repeat5 .t转置6. transpose7. permute 1. view/reshape view(*shape) → Tensor 作用&#xff1a;类似于reshape&#xff0c;将tensor转换为指定的shape&#xff0c;原始的data不改变。返回的tensor与原始的tensor…

Python爬虫之自动化测试Selenium#7

爬虫专栏&#xff1a;http://t.csdnimg.cn/WfCSx 前言 在前一章中&#xff0c;我们了解了 Ajax 的分析和抓取方式&#xff0c;这其实也是 JavaScript 动态渲染的页面的一种情形&#xff0c;通过直接分析 Ajax&#xff0c;我们仍然可以借助 requests 或 urllib 来实现数据爬取…

[缓存] - 2.分布式缓存重磅中间件 Redis

1. 高性能 尽量使用短key 不要存过大的数据 避免使用keys *&#xff1a;使用SCAN,来代替 在存到Redis之前压缩数据 设置 key 有效期 选择回收策略(maxmemory-policy) 减少不必要的连接 限制redis的内存大小&#xff08;防止swap&#xff0c;OOM&#xff09; slowLog …

奇异递归模板模式应用1-对象计数

需求&#xff1a;有时遇到某些类特征相似而又没有共同的父类&#xff0c;希望能够知道这些类的创建数量之和。 思路&#xff1a;将这些类继承自同一个计数类&#xff0c;共享计数变量s_createCount信息&#xff0c;实现如下&#xff1a; class Counter { public:Counter() {s_…

OpenGL-ES 学习(2)---- DepthTest

深度测试 OpenGL-ES 深度测试是指在片段着色器执行之后&#xff0c;利用深度缓冲区所保存的深度值决定当前片段是否被丢弃的过程 深度缓冲区通常和颜色缓冲区有着相同的宽度和高度&#xff0c;一般由窗口系统自动创建并将其深度值存储为 16、 24 或 32 位浮点数。(注意只保存…