简介
大家好,我是来自中原工学院鸿蒙课程的学生孟子浩。这里分享的是我自己的鸿蒙期末考查大作业-,通过一学期课程的学习,研究出来的一些成果,本文内容实现页面基本的布局以及一部分相关业务逻辑。自我感觉代码还有待改进,望多多包涵。
项目背景
本项目旨在实现一种用于电子商务的系统,旨在为用户提供在线购物和交易的平台
环境配置
软件要求
DevEco Studio版本:DevEco Studio 3.1 Release。
HarmonyOS SDK版本:API version 9
语言要求:
ArkTs开发语言
涉及的代码结构
其中ets/pages/目录下的LoadingPage.ets实现应用启动页面时的加载。然后来到Login.ets页面,此页面包含基本的登入功能、注册功能以及修改账号密码功能,且注册和修改功能分别对应Register.ets和ResetPassword.ets。若登录成功,点击确认则跳转至HomePage.ets页面。Detail.ets页面:商品详情页,点击任意商品则立即跳转至此。settlePage.ets时商品订单结算页,MinePage.ets作为应用内账号主页。以及NewPage.ets作为潮流商品页。
及文件
应用启动页:
登录页面
展示效果图:以下分是登录、注册、忘记密码时的页面,实现基本的账号登录,注册以及修改功能。
相关代码展示:
//注册
@Component
export struct Login {
@State account: string = ''
@State password: string = ''
build() {
Row() {
Column({ space: 20 }) {
Text('欢迎').fontColor(Color.Orange).fontSize(40)
TextInput({ placeholder: "请输入用户名" })
.placeholderColor(Color.White)
.width(250)
.border({
color:Color.White,
radius:30,
width:1,
style:BorderStyle.Solid
})
.height(60)
.onChange(value => {
this.account = value
})
TextInput({ placeholder: "请输入密码" })
.placeholderColor(Color.White)
.fontColor(Color.White)
.type(InputType.Password)
.border({
color:Color.White,
radius:30,
width:1,
style:BorderStyle.Solid
})
.width(250)
.height(60)
.onChange(value => {
this.password =value
})
Button("登录")
.fontColor(Color.White)
.backgroundColor(Color.Orange)
.width(250)
.height(40)
.type(ButtonType.Capsule).onClick(() => {
router.pushUrl({
url: 'pages/HomePage',
params: {
account:this.account,
password:this.password
}
})
})
Row(){
Text('忘记密码')
.fontColor(Color.White).onClick(()=>{
router.pushUrl({
url: 'pages/ResetPassword'
})
})
Text("|").fontColor(Color.White).margin(30)
Text('立即注册')
.fontColor(Color.White)
.onClick(()=>{
router.pushUrl({
url: 'pages/Register'
})
})
}
}
.width('100%')
}
.height('100%')
.linearGradient(
{
angle:110,
colors:[['#ff1661be',0,1],['#ff2ed6bd',0.8],['#ff12bf74',0.1]]
}
)
}
}
//注册
@Component
export struct register{
@State account: string = ''
@State password: string = ''
@State password2: string = ''
build(){
Column(){
Row() {
Column({ space: 20 }) {
Text('注册').fontColor(Color.Orange).fontSize(40)
TextInput({ placeholder: "请输入注册的账号" })
.placeholderColor(Color.White)
.width(250)
.border({
color:Color.White,
radius:30,
width:1,
style:BorderStyle.Solid
})
.height(60)
.onChange(value => {
this.account = value
})
TextInput({ placeholder: "请输入密码" })
.placeholderColor(Color.White)
.fontColor(Color.White)
.type(InputType.Password)
.border({
color:Color.White,
radius:30,
width:1,
style:BorderStyle.Solid
})
.width(250)
.height(60)
.onChange(value => {
this.password =value
})
TextInput({ placeholder: "请再次输入密码" })
.placeholderColor(Color.White)
.fontColor(Color.White)
.type(InputType.Password)
.border({
color:Color.White,
radius:30,
width:1,
style:BorderStyle.Solid
})
.width(250)
.height(60)
.onChange(value => {
this.password2 =value
})
Button("注册")
.fontColor(Color.White)
.backgroundColor(Color.Orange)
.width(250)
.height(40)
.type(ButtonType.Capsule).onClick(() => {
if(this.password!=this.password2){
AlertDialog.show(
{
title: '密码输入错误',
message: '输入密码不一致',
autoCancel: true,
alignment: DialogAlignment.Bottom,
offset: { dx: 0, dy: -20 },
gridCount: 3,
confirm: {
value: '确认',
action: () => {
console.info('Button-clicking callback')
}
},
cancel: () => {
console.info('Closed callbacks')
}
}
)
}else {
router.pushUrl({
url: 'pages/HomePage',
params: {
account: this.account,
password: this.password
}
})
}
})
}
.width('100%')
}
.height('100%')
.linearGradient(
{
angle:110,
colors:[['#ff1661be',0,1],['#ff2ed6bd',0.8],['#ff12bf74',0.1]]
}
)
}
}
}
//重置密码
@Component
export struct Reset{
@State account: string = ''
@State password: string = ''
build(){
Column(){
Row() {
Column({ space: 20 }) {
Text('重置密码').fontColor(Color.Orange).fontSize(40)
TextInput({ placeholder: "请输入要重置的账号" })
.placeholderColor(Color.White)
.width(250)
.border({
color:Color.White,
radius:30,
width:1,
style:BorderStyle.Solid
})
.height(60)
.onChange(value => {
this.account = value
})
TextInput({ placeholder: "请输入要重置的账号密码" })
.placeholderColor(Color.White)
.fontColor(Color.White)
.type(InputType.Password)
.border({
color:Color.White,
radius:30,
width:1,
style:BorderStyle.Solid
})
.width(250)
.height(60)
.onChange(value => {
this.password =value
})
Button("重置")
.fontColor(Color.White)
.backgroundColor(Color.Orange)
.width(250)
.height(40)
.type(ButtonType.Capsule).onClick(() => {
AlertDialog.show(
{
title: '注册成功',
message: '跳转主页',
autoCancel: true,
alignment: DialogAlignment.Bottom,
offset: { dx: 0, dy: -20 },
gridCount: 3,
confirm: {
value: '确认',
action: () => {
router.pushUrl({
url: 'pages/Login',
params: {
account:this.account,
password:this.password
}
})
}
},
cancel: () => {
console.info('Closed callbacks')
}
}
)
})
}
.width('100%')
}
.height('100%')
.linearGradient(
{
angle:110,
colors:[['#ff1661be',0,1],['#ff2ed6bd',0.8],['#ff12bf74',0.1]]
}
)
}
}
}
代码首页
包含四个分页
build() {
Tabs({barPosition:BarPosition.End}) {
TabContent() {
Goods()
}
.tabBar(this.bar('主页', $rawfile('main.png'), 0))
TabContent() {
NewGoods()
}
.tabBar(this.bar('新品', $rawfile('new.png'), 1))
TabContent() {
BuyCar()
}
.tabBar(this.bar('购物车', $rawfile('shopCar.png'), 2))
TabContent() {
MinePage()
}
.tabBar(this.bar('我的', $rawfile('mine.png'), 3))
}.onChange((index)=>{
this.currentTabIndex=index
})
}
@Component
struct Goods {//实现主页分页
@State currentTabIndex: number = 0
build(){
Tabs(){
ForEach(categories,(item,index)=>{
TabContent(){
Grid(){
ForEach(goods.filter(good=>good.category===this.currentTabIndex||index===0),(item,index)=>{
GridItem() {
GoodItem({good:item})
}
})
}
.rowsGap(6)
.columnsGap(6)
.columnsTemplate('1fr 1fr ')
}.tabBar(this.bar(item.name,index))
})
}
.width('90%')
.padding({bottom:20})
.onChange((index)=>{
this.currentTabIndex=index
})
}
@Builder
bar(name: string, index: number) {
Flex({
direction: FlexDirection.Column,
alignItems: ItemAlign.Center,
justifyContent: FlexAlign.Center
}) {
Text(name).fontSize(30).fontColor(0x3E3E3E).margin(10).fontColor(this.currentTabIndex === index ? '#ff1286c6' : '')
}
}
}
@Component
struct NewGoods {//新产品分页
@State currentTabIndex: number = 0
categories: Array<{
id: number,
name: string|Resource
}> =
[{ id: 1, name: '新品' } ]
build(){
Tabs(){
ForEach(this.categories,(item,index)=>{
TabContent(){
Grid(){
ForEach(goods.filter(good=>good.isNew===1),(item,index)=>{
GridItem() {
GoodItem({good:item})
}
})
}
.rowsGap(6)
.columnsGap(6)
.columnsTemplate('1fr')
}.tabBar(this.bar(item.name,index))
})
}
.width('90%')
.padding({bottom:20})
.onChange((index)=>{
this.currentTabIndex=index
})
}
@Builder
bar(name: string, index: number) {
Flex({
direction: FlexDirection.Column,
alignItems: ItemAlign.Center,
justifyContent: FlexAlign.Center
}) {
// .backgroundColor(this.currentTabIndex === index ? '#55ff55' : '')
Text(name).fontSize(30).fontColor(0x3E3E3E).margin(10).fontColor(this.currentTabIndex === index ? '#55ff55' : '')
}
}
}
@Component
export struct BuyCar {//购物车
goods: Array<GoodInfo> = [
{
id: 0,
name: 'xx手机',
price: 5999,
category: 1,
img: 'iphone1.jpg',
count: 21,
area: '广州',
provider: "数码科技",
isNew: 0,
detail: '高性能',
state: 0
},
{
id: 1,
name: '蓝牙耳机',
price: 646,
category: 1,
img: 'airpods.jpg',
count: 23,
area: '广州',
provider: "数码科技",
isNew: 1,
detail: '居家必备',
state: 0
},
{
id: 2,
name: '平板',
price: 2999,
category: 1,
img: 'ipad.jpg',
count: 99,
area: '广州',
provider: "数码科技",
isNew: 1,
detail: '致敬时代',
state: 0
},
{
id: 3,
name: '智能手表',
price: 299,
category: 1,
img: 'iwacth.jpg',
count: 32,
area: '广州',
provider: "数码科技",
isNew: 1,
detail: '智能生活助手',
state: 0
},
{
id: 4,
name: '笔记本',
price: 4999,
category: 1,
img: 'macBook.jpg',
count: 12,
area: '广州',
provider: "数码科技",
isNew: 1,
detail: '商务轻薄笔记本',
state: 0
},
{
id: 5,
name: '潮流衣服',
price: 99,
category: 2,
img: 'cloth1.jpg',
count: 21,
area: '广州',
provider: "数码科技",
isNew: 1,
detail: '温暖舒适',
state: 0
},
{
id: 6,
name: '女鞋',
price: 199,
category: 2,
img: 'cloth2.jpg',
count: 24,
area: '广州',
provider: "数码科技",
isNew: 1,
detail: '轻便快捷',
state: 0
},
]
goodInfoNew: GoodInfo = (router.getParams() as { goodInfo: GoodInfo }).goodInfo
id1: number = (router.getParams() as { goodInfo: GoodInfo }).goodInfo.id
@State currentTabIndex: number = 0
categories: Array<{
id: number,
name: string | Resource
}> =
[{ id: 1, name: '购物车' }]
build() {
Tabs() {
ForEach(this.goods.filter(good => good.name == this.goodInfoNew.name)
.map(good => good.state = 1), (item, index) => {
})
ForEach(this.categories, (item, index) => {
TabContent() {
Grid() {
ForEach(this.goods.filter(good => (good.state === 1)), (item, index) => {
GridItem() {
GoodItem({ good: item })
}
})
}
.rowsGap(6)
.columnsGap(6)
.columnsTemplate('1fr')
}.tabBar(this.bar(item, index))
})
}
.width('90%')
.padding({ bottom: 20 })
.onChange((index) => {
this.currentTabIndex = index
})
}
@Builder
bar(goods: GoodInfo, index: number) {
Flex({
direction: FlexDirection.Column,
alignItems: ItemAlign.Center,
justifyContent: FlexAlign.Center
}) {
Button('提交订单').backgroundColor(Color.Red)
.fontColor(Color.White).onClick(()=>{
router.pushUrl({
url:'pages/settlePage',
params:{
goodInfo:this.goods
}
})
})
}
}
}
//主页展示的仅是静态数据,写的粗糙
@Entry
@Component
export struct MinePage {
private peopleInfo:PeopleInfo={name:'李先生',address: '东泰五街410号',
phone: '149-7330-3277',img: 'boy.png',
account: '123456',
password: '123456'}
private account = (router.getParams()?.['account'] )
private password = (router.getParams()?.['password'])
build() {
Row() {
Column() {
Row(){
// if (this.account =='123456'&&this.password=='123456'){
Image($rawfile(this.peopleInfo.img)).height(50)
.width(50)
Column(){
Text(this.peopleInfo.name)
Text(this.peopleInfo.phone+this.peopleInfo.address).margin(5)
}
}.position({x: 10, y: 20})
}
}
}
}
可以实现根据商品的类别进行分类
商品详情页:
订单确认页:
购物车页:
相关代码:
@Component
export struct BuyCar {//购物车页
goods: Array<GoodInfo> = [
{
id: 0,
name: 'xx手机',
price: 5999,
category: 1,
img: 'iphone1.jpg',
count: 21,
area: '广州',
provider: "数码科技",
isNew: 0,
detail: '高性能',
state: 0
},
{
id: 1,
name: '蓝牙耳机',
price: 646,
category: 1,
img: 'airpods.jpg',
count: 23,
area: '广州',
provider: "数码科技",
isNew: 1,
detail: '居家必备',
state: 0
},
{
id: 2,
name: '平板',
price: 2999,
category: 1,
img: 'ipad.jpg',
count: 99,
area: '广州',
provider: "数码科技",
isNew: 1,
detail: '致敬时代',
state: 0
},
{
id: 3,
name: '智能手表',
price: 299,
category: 1,
img: 'iwacth.jpg',
count: 32,
area: '广州',
provider: "数码科技",
isNew: 1,
detail: '智能生活助手',
state: 0
},
{
id: 4,
name: '笔记本',
price: 4999,
category: 1,
img: 'macBook.jpg',
count: 12,
area: '广州',
provider: "数码科技",
isNew: 1,
detail: '商务轻薄笔记本',
state: 0
},
{
id: 5,
name: '潮流衣服',
price: 99,
category: 2,
img: 'cloth1.jpg',
count: 21,
area: '广州',
provider: "数码科技",
isNew: 1,
detail: '温暖舒适',
state: 0
},
{
id: 6,
name: '女鞋',
price: 199,
category: 2,
img: 'cloth2.jpg',
count: 24,
area: '广州',
provider: "数码科技",
isNew: 1,
detail: '轻便快捷',
state: 0
},
]
goodInfoNew: GoodInfo = (router.getParams() as { goodInfo: GoodInfo }).goodInfo
id1: number = (router.getParams() as { goodInfo: GoodInfo }).goodInfo.id
@State currentTabIndex: number = 0
categories: Array<{
id: number,
name: string | Resource
}> =
[{ id: 1, name: '购物车' }]
build() {
Tabs() {
ForEach(this.goods.filter(good => good.name == this.goodInfoNew.name)
.map(good => good.state = this.goodInfoNew.state), (item, index) => {
})
ForEach(this.categories, (item, index) => {
TabContent() {
Grid() {
ForEach(this.goods.filter(good => (good.state === 1)), (item, index) => {
GridItem() {
GoodItem({ good: item })
}
})
}
.rowsGap(6)
.columnsGap(6)
.columnsTemplate('1fr')
}.tabBar(this.bar(item, index))
})
}
.width('90%')
.padding({ bottom: 20 })
.onChange((index) => {
this.currentTabIndex = index
})
}
@Builder
bar(goods: GoodInfo, index: number) {
Flex({
direction: FlexDirection.Column,
alignItems: ItemAlign.Center,
justifyContent: FlexAlign.Center
}) {
Button('提交订单').backgroundColor(Color.Red)
.fontColor(Color.White).onClick(()=>{
router.pushUrl({
url:'pages/settlePage',
params:{
goodInfo:this.goods
}
})
})
}
}
}
@Entry
@Component
struct SettlementPage {//订单详情页
private goodInfo = (router.getParams() as {goodInfo: GoodInfo}).goodInfo
private people:PeopleInfo={name:'李先生',address: '东泰五街410号',
phone: '149-7330-3277',img: 'boy.png',
account: '123456',
password: '123456'}
build() {
Column({space:10}){
settleTitle()
Column() {
Image($rawfile('position.png')).width(40).height(40).position({x: 10, y: 26})
Text(this.people.name).fontSize(20)
Text(this.people.phone).fontSize(20)
Text(this.people.address).fontSize(20)
}
.backgroundColor(Color.White)
.height(100)
.width(300)
.justifyContent(FlexAlign.SpaceBetween)
Column(){
Image($rawfile(this.goodInfo.img)).width(200).height(200)
Text(this.goodInfo.name).fontSize(20)
Text(" "+this.goodInfo.price).fontSize(20)
Row(){
Text('配送').fontSize(20)
Text('标椎配送').fontSize(20)
}.justifyContent(FlexAlign.SpaceBetween)
Divider().width('80%')
Row(){
Text('发票').fontSize(20)
Text('电子普通发票').fontSize(20)
}.justifyContent(FlexAlign.SpaceBetween)
}.backgroundColor(Color.White)
.width(300)
.height(400)
Row(){
Text('¥ '+this.goodInfo.price).fontColor(Color.Red).fontSize(20)
Button('提交订单').backgroundColor(Color.Red)
.fontColor(Color.White)
.onClick(() => {
this.goodInfo.state=0
AlertDialog.show(
{
title: '提交成功',
message: '返回首页',
autoCancel: true,
alignment: DialogAlignment.Bottom,
offset: { dx: 0, dy: -20 },
gridCount: 3,
confirm: {
value: '确认',
action: () => {
router.pushUrl({
url: 'pages/HomePage',
params: {
goodInfo:this.goodInfo
}
})
}
},
cancel: () => {
console.info('Closed callbacks')
}
}
)
})
}.justifyContent(FlexAlign.SpaceEvenly)
.width(400)
.height(80)
}.width('100%')
.height('100%')
.backgroundColor('#faf7f8f7')
}
}
@Component
struct settleTitle {
build() {
Row() {
Image($rawfile('back.png'))
.width(20)
.height(20)
.margin({right: 5})
.onClick(() => {
router.back()
})
Text('确认订单')
.fontSize(40)
}
.width('100%')
.margin({top: 8})
.padding({left: 20})
.justifyContent(FlexAlign.Start)
}
}
@Entry
@Component
struct Detail {//商品详情页
private foodInfo = (router.getParams() as {foodInfo: GoodInfo}).foodInfo
// private foodInfo = {"id":0,"name":"番茄","kk":15,"category":1,"img":{"id":16777232,"type":20000,"params":[],"bundleName":"com.example.healthydiet","moduleName":"entry"},"rl":0,"yy":0,"zf":0}
build() {
Column() {
PageTitle()
FoodImage({foodInfo: this.foodInfo})
Content({foodInfo: this.foodInfo})
}
.height('100%')
.backgroundColor('#dedede')
}
}
@Component
struct PageTitle {
build() {
Row() {
Image($rawfile('back.png'))
.width(20)
.height(20)
.margin({right: 5})
.onClick(() => {
router.back()
})
Text('宝贝详情').fontSize(26).fontColor(0x3E3E3E)
}
.width('100%')
.margin({top: 8})
.padding({left: 20})
.justifyContent(FlexAlign.Start)
}
}
@Component
struct FoodImage {
private foodInfo: GoodInfo
build() {
Stack() {
Image(this.foodInfo.img)
.objectFit(ImageFit.Contain)
Text(this.foodInfo.name).fontSize(26).fontColor(0x3E3E3E).width('90%').position({x: 10, y: 230})
}
.height(300)
}
}
@Component
struct Content {
private foodInfo: GoodInfo
build() {
Column() {
this.LineContent('#ffaaaa', '价格/件', this.foodInfo.price, '元')
this.LineContent('#aaffaa', '数量', this.foodInfo.count, '件')
this.LineContent('#aaaaff', '地区', this.foodInfo.area, '')
}
.width('90%')
.margin({top: 20, bottom: 20})
.padding(20)
.borderRadius(10)
.backgroundColor(Color.White)
}
@Builder
LineContent(color: ResourceColor, name: string, count: number|string, unit: string) {
Row() {
Circle({width: 6, height: 6})
.margin({right: 12})
.fill(color)
Text(name).fontSize(22).fontColor(0x3E3E3E)
Blank()
Text(count + ' ' + unit).fontSize(22).fontColor(0x3E3E3E)
}
.width('100%')
.margin(10)
.padding({left: 10, right: 10})
}
}
结尾
本文针对鸿蒙期末大作业创作的一篇鸿蒙4.0应用报告,应用尚有瑕疵,后面尚待改善,谢谢。