当前位置:   article > 正文

【江鸟中原】鸿蒙-简易购物车_鸿蒙开发 购物车页面

鸿蒙开发 购物车页面

工具人小白带来项目-简易购物车

开发工具:DevEco Studio

图标:HarmonyOS图标库和阿里巴巴矢量图标库

一、登录界面

使用了Ohos前端框架的登录页面组件,它采用了类似于React的组件化开发模式。

来浅浅分析一下这段代码:

1.import语句引入了三个Ohos模块:@ohos.router、@ohos.prompt和@ohos.promptAction。这些模块提供了路由导航、提示框和动作列表等常用功能。

2.定义了一个名为LoginPage的组件,使用了@Entry和@Component装饰器,表明这是一个入口组件和普通组件。

3.在LoginPage组件内部,定义了两个状态变量@State password和@State username,用于存储密码和用户名。即@State注解定义了两个状态变量:password和username,它们被初始化为字符串类型的空字符串。这些状态变量可以在组件中被修改,并且当它们的值被修改时,页面会自动重新渲染。

4.build()方法用于构建组件树。在build方法中,使用了Column、Row、Text、Image、TextInput、Divider和Button等控件,构建了登录页面的UI布局和交互逻辑。首先创建一个Column组件,并设置其样式为居中对齐。然后在Column组件中添加了多个子组件,包括标题、用户名输入框、密码输入框和登录按钮。在用户名和密码输入框中,使用了Image组件来显示图标,使用了TextInput组件来获取用户输入。当用户输入改变时,使用onChange方法更新username和password状态变量的值。

5.在Button的onClick事件处理函数中,根据输入的用户名和密码进行判断,如果匹配成功则跳转到"pages/mainUI"页面,否则显示密码或用户名错误的提示消息。如果用户名和密码均为"admin",则使用router.replaceUrl()函数将页面导航到pages/mainUI页面;否则,使用promptAction.showToast()函数显示一个提示框,提示用户输入的用户名或密码错误。

总体上看,这段代码实现了一个简单的登录页面,包括了UI布局和基本的交互逻辑。它使用了Ohos前端框架提供的组件和状态管理功能,以及路由和提示消息的操作。

代码如下:

  1. import router from '@ohos.router'
  2. import prompt from '@ohos.prompt'
  3. import promptAction from '@ohos.promptAction'
  4. @Entry
  5. @Component
  6. struct LoginPage {
  7. @State password: string = ''
  8. @State username: string = ''
  9. build() {
  10. Column() {
  11. Text("登陆")
  12. .fontSize(50)
  13. .fontWeight(FontWeight.Bold).margin({
  14. bottom: 60
  15. })
  16. Row() {
  17. Text("用户名")
  18. .fontSize(18)
  19. .fontWeight(FontWeight.Bold)
  20. }.width("100%")
  21. Row() {
  22. Image($r("app.media.ic_user_portrait")).width(30)
  23. TextInput({
  24. placeholder: "请输入用户名"
  25. }).width(200).onChange((val: string) => {
  26. this.username = val
  27. })
  28. }.margin({
  29. bottom: 8,
  30. top: 8
  31. }).width("100%")
  32. Divider().strokeWidth(4)
  33. Row() {
  34. Text("密码")
  35. .fontSize(18)
  36. .fontWeight(FontWeight.Bold).margin({
  37. bottom: 8,
  38. top: 8
  39. })
  40. }.width("100%")
  41. Row() {
  42. Image($r("app.media.ic_public_lock")).width(30)
  43. TextInput({
  44. placeholder: "请输入密码"
  45. }).width(200).onChange((val: string) => {
  46. this.password = val
  47. }).type(InputType.Password)
  48. }.width("100%")
  49. Divider().strokeWidth(4)
  50. Button("登陆").width("90%").height(60).backgroundColor(Color.Orange).onClick(() => {
  51. if (this.username == "admin" && this.password == "admin") {
  52. router.replaceUrl({
  53. url: "pages/mainUI"
  54. })
  55. }
  56. else {
  57. promptAction.showToast({
  58. message:"密码或用户名错误,请重新输入"
  59. })
  60. }
  61. })
  62. }
  63. .width('100%')
  64. .height('100%')
  65. .justifyContent(FlexAlign.Center)
  66. .alignItems(HorizontalAlign.Center)
  67. .padding({
  68. left: 20,
  69. right: 20
  70. })
  71. }
  72. }

二、购物界面

1.实现了一个简单的页面布局和交互设计,通过自定义的 tab 组件和 tabsController 实现了选项卡的切换和样式变化。具体如下:   定义了一个名为 Index 的结构体,用于创建页面的入口组件。Index 结构体包含了三个自定义的 tab 组件:tabHome、tabContactsGroup 和 tabMy。tabHome 组件用于创建“首页”tab,它是一个 Row 组件内嵌一个 Column 组件。在 Column 组件中,首先添加了一个上方的 Blank(填充)组件,然后根据当前 index 值判断是否显示一个绿色的图标,图标的样式通过 $r 方法获取。接下来是一个 Text 组件用于显示文本内容“首页”,字体大小为 16px,字体颜色根据 index 值来决定。最后,又添加了一个下方的 Blank(填充)组件。整个 tabHome 组件的点击事件处理逻辑是将 index 设置为 0,并调用 tabsController 的 changeIndex 方法来改变选项卡的索引值。tabContactsGroup 和 tabMy 组件的结构与 tabHome 类似,只是显示的图标、文本和点击事件处理不同。tabContactsGroup 组件对应“购物车”tab,显示了一个购物车的图标;tabMy 组件对应“我的”tab,显示了一个联系人的图标。最后,在 Index 结构体中,定义了一个私有的 tabsController 变量,并初始化为一个 TabsController 对象。同时,还定义了一个 @State 注解修饰的 index 变量,初始值为 0,用于记录当前选中的 tab 的索引。

2.接下来是几个自定义的选项卡组件,每个组件都包含一个图标和标题,并且可以根据当前选中的选项卡的下标来改变样式。然后,在build方法中,使用Tabs组件创建了一个选项卡容器。通过设置barPosition属性为BarPosition.End,将导航栏放在底部。TabContent组件中定义了每个选项卡的内容。在首页的选项卡中,展示了一些商品列表,每个商品都有名称、价格和一个“选购”按钮。点击“选购”按钮会触发addToCart方法,将商品添加到购物车中。在购物车的选项卡中,展示了购物车中的商品列表,每个商品都有一个复选框、名称和价格。点击复选框会触发toggleCheck方法,切换商品的选中状态。点击“结算”按钮会筛选出已选中的商品,并计算总价。在我的的选项卡中,展示了一些个人信息和功能选项,如基本信息、收藏、关注店铺、收货地址和设置。

  1. @Entry
  2. @Component
  3. struct Index {
  4. //#63bf02 绿色
  5. //索引从0开始
  6. private tabsController: TabsController = new TabsController();
  7. //选项卡的下标,从0开始,初始为0(表示被选中
  8. @State index: number = 0
  9. //自定义“首页”的tab组件
  10. @Builder tabHome() {
  11. Row() {
  12. Column() {
  13. //上填充
  14. Blank()
  15. Image(this.index == 0 && $r('app.media.ic_public_home'))
  16. .size({ width: 25, height: 25 })
  17. Text('首页').fontSize(16)
  18. .fontColor(this.index == 0 ? "#63bf02" : "#000000")
  19. //下填充
  20. Blank()
  21. }
  22. .height('100%')
  23. .width('100%')
  24. .onClick(() => {
  25. this.index = 0;
  26. this.tabsController.changeIndex(this.index);
  27. })
  28. }
  29. }
  30. //自定义“购物车”的tab组件
  31. @Builder tabContactsGroup() {
  32. Row() {
  33. Column() {
  34. //上填充
  35. Blank()
  36. Image(this.index == 1 && $r('app.media.gouwuche'))
  37. .size({ width: 25, height: 25 })
  38. Text('购物车').fontSize(16)
  39. .fontColor(this.index == 1 ? "#63bf02" : "#000000")
  40. //下填充
  41. Blank()
  42. }
  43. .height('100%')
  44. .width('100%')
  45. .onClick(() => {
  46. this.index = 1;
  47. this.tabsController.changeIndex(this.index);
  48. })
  49. }
  50. }
  51. //自定义“我的”的tab组件
  52. @Builder tabMy() {
  53. Row() {
  54. Column() {
  55. //上填充
  56. Blank()
  57. Image(this.index == 2 && $r('app.media.ic_public_contacts'))
  58. .size({ width: 25, height: 25 })
  59. Text('我的').fontSize(16)
  60. .fontColor(this.index == 2 ? "#63bf02" : "#000000")
  61. //下填充
  62. Blank()
  63. }
  64. .height('100%')
  65. .width('100%')
  66. .onClick(() => {
  67. this.index = 2;
  68. this.tabsController.changeIndex(this.index);
  69. })
  70. }
  71. }
  72. cartItems: {
  73. name: string;
  74. price: string;
  75. }[] = [];
  76. private cart: {
  77. name: string;
  78. price: string;
  79. select: boolean
  80. }[] = [];
  81. private total: number = 0;
  82. addToCart = (itemName: string, itemPrice: string) => {
  83. const newCart = [...this.cart, { name: itemName, price: itemPrice, select: false }];
  84. const newTotal = this.total + parseInt(itemPrice);
  85. this.cart = newCart;
  86. this.total = newTotal;
  87. };
  88. toggleCheck = (index: number) => {
  89. const updatedCart = [...this.cart];
  90. updatedCart[index].select = !updatedCart[index].select;
  91. this.cart = updatedCart;
  92. };
  93. build() {
  94. Row() {
  95. Column() {
  96. Tabs({
  97. index: 0,
  98. barPosition: BarPosition.End,
  99. controller: this.tabsController
  100. }) {
  101. //首页的tab绑定
  102. TabContent() {
  103. Row() {
  104. Column() {
  105. Text("商品列表")
  106. .fontSize(50)
  107. .fontWeight(FontWeight.Bold).margin({
  108. bottom: 60
  109. })
  110. Row() {
  111. Text("幸运咖啡")
  112. .fontSize(28)
  113. .fontWeight(FontWeight.Bold).margin({
  114. bottom: 8,
  115. top: 8,
  116. right: 90, //添加右边距
  117. })
  118. Text("价格:").fontSize(28)
  119. Text("26").fontSize(28)
  120. Button("选购").width("15%").height(30).backgroundColor(Color.Orange)
  121. .onClick(() => {
  122. const itemName = "幸运咖啡";
  123. const itemPrice = "26";
  124. this.addToCart(itemName, itemPrice);
  125. })
  126. }.width("100%")
  127. Row() {
  128. Text("拿铁")
  129. .fontSize(28)
  130. .fontWeight(FontWeight.Bold).margin({
  131. bottom: 8,
  132. top: 8,
  133. right: 150, //添加右边距
  134. })
  135. Text("价格:").fontSize(28)
  136. Text("16").fontSize(28)
  137. Button("选购").width("15%").height(30).backgroundColor(Color.Orange)
  138. .onClick(() => {
  139. const itemName = "拿铁";
  140. const itemPrice = "16";
  141. this.addToCart(itemName, itemPrice);
  142. })
  143. }.width("100%")
  144. Row() {
  145. Text("柠檬水")
  146. .fontSize(28)
  147. .fontWeight(FontWeight.Bold).margin({
  148. bottom: 8,
  149. top: 8,
  150. right: 120, //添加右边距
  151. })
  152. Text("价格:").fontSize(28)
  153. Text("4").fontSize(28).fontWeight(FontWeight.Bold).margin({
  154. right: 20, //添加右边距
  155. })
  156. Button("选购").width("15%").height(30).backgroundColor(Color.Orange)
  157. .onClick(() => {
  158. const itemName = "柠檬水";
  159. const itemPrice = "4";
  160. this.addToCart(itemName, itemPrice);
  161. })
  162. }.width("100%")
  163. Row() {
  164. Text("四季春果")
  165. .fontSize(28)
  166. .fontWeight(FontWeight.Bold).margin({
  167. bottom: 8,
  168. top: 8,
  169. right: 90, //添加右边距
  170. })
  171. Text("价格:").fontSize(28)
  172. Text("15").fontSize(28)
  173. Button("选购").width("15%").height(30).backgroundColor(Color.Orange).onClick(() => {
  174. const itemName = "四季春果";
  175. const itemPrice = "15";
  176. this.addToCart(itemName, itemPrice);
  177. })
  178. }.width("100%")
  179. Row() {
  180. Text("芋泥豆乳")
  181. .fontSize(28)
  182. .fontWeight(FontWeight.Bold).margin({
  183. bottom: 8,
  184. top: 8,
  185. right: 90, //添加右边距
  186. })
  187. Text("价格:").fontSize(28)
  188. Text("18").fontSize(28)
  189. Button("选购").width("15%").height(30).backgroundColor(Color.Orange)
  190. .onClick(() => {
  191. const itemName = "芋泥豆乳";
  192. const itemPrice = "18";
  193. this.addToCart(itemName, itemPrice);
  194. })
  195. }.width("100%")
  196. Row() {
  197. Text("益和烤奶")
  198. .fontSize(28)
  199. .fontWeight(FontWeight.Bold).margin({
  200. bottom: 8,
  201. top: 8,
  202. right: 90, //添加右边距
  203. })
  204. Text("价格:").fontSize(28)
  205. Text("16").fontSize(28)
  206. Button("选购").width("15%").height(30).backgroundColor(Color.Orange)
  207. .onClick(() => {
  208. const itemName = "益和烤奶";
  209. const itemPrice = "16";
  210. this.addToCart(itemName, itemPrice);
  211. })
  212. }.width("100%")
  213. }
  214. .width('100%')
  215. }
  216. .height('100%')
  217. }
  218. .tabBar(this.tabHome())
  219. //购物车的tab绑定
  220. TabContent() {
  221. Row() {
  222. Column() {
  223. Text("购物车")
  224. .textAlign(TextAlign.Center)
  225. .fontSize(50)
  226. .fontWeight(FontWeight.Bold).margin({
  227. bottom: 600,
  228. });
  229. this.cart.map((item, index) => {
  230. Row() {
  231. Checkbox()
  232. .select(item.select)
  233. .width(40)
  234. .height(40)
  235. .onClick(() => this.toggleCheck(index))
  236. Text(item.name.toString())
  237. .fontSize(28)
  238. .fontWeight("bold")
  239. .margin({ left: 10, right: 90 })
  240. Text(item.price.toString())
  241. .fontSize(28)
  242. .fontWeight("bold")
  243. .margin({ left: 10, right: 90 })
  244. }.width("100%")
  245. .height(60)
  246. .alignItems(VerticalAlign.Center)
  247. .padding({ top: 10 })
  248. }
  249. )
  250. Row() {
  251. Button("结算")
  252. .width("40%")
  253. .height(60)
  254. .backgroundColor(Color.Orange)
  255. .onClick(() => {
  256. const selectedItems = this.cart.filter(item => item.select);
  257. const selectedTotal = selectedItems.reduce((total, item) => total + parseInt(item.price), 0);
  258. console.log(JSON.stringify(selectedItems));
  259. console.log(String(selectedTotal));
  260. })
  261. Text(`合计:${this.total}`)
  262. .fontSize(28)
  263. .fontWeight(FontWeight.Bold);
  264. }.width("100%")
  265. }
  266. .width('100%')
  267. }
  268. .height('100%')
  269. }
  270. .tabBar(this.tabContactsGroup())
  271. //我的的tab绑定
  272. TabContent() {
  273. Row() {
  274. Column() {
  275. Text("我的")
  276. .fontSize(50)
  277. .fontWeight(FontWeight.Bold).margin({
  278. bottom: 60
  279. })
  280. Row() {
  281. Image($r("app.media.ic_user_portrait")).width(30)
  282. Text("基本信息")
  283. .fontSize(18)
  284. .fontWeight(FontWeight.Bold).margin({
  285. bottom: 8,
  286. top: 8
  287. })
  288. }.width("100%")
  289. Row() {
  290. Image($r("app.media.ic_public_favor")).width(30)
  291. Text("收藏")
  292. .fontSize(18)
  293. .fontWeight(FontWeight.Bold).margin({
  294. bottom: 8,
  295. top: 8
  296. })
  297. }.width("100%")
  298. Row() {
  299. Image($r("app.media.ic_public_highlight")).width(30)
  300. Text("关注店铺")
  301. .fontSize(18)
  302. .fontWeight(FontWeight.Bold).margin({
  303. bottom: 8,
  304. top: 8
  305. })
  306. }.width("100%")
  307. Row() {
  308. Image($r("app.media.shouhuodizhi")).width(30)
  309. Text("收货地址")
  310. .fontSize(18)
  311. .fontWeight(FontWeight.Bold)
  312. }.width("100%")
  313. Row() {
  314. Image($r("app.media.ic_public_settings")).width(30)
  315. Text("设置")
  316. .fontSize(18)
  317. .fontWeight(FontWeight.Bold)
  318. }.width("100%")
  319. }
  320. .width('100%')
  321. }
  322. .height('100%')
  323. }
  324. .tabBar(this.tabMy())
  325. }
  326. .scrollable(true)
  327. .width('100%')
  328. .height('100%')
  329. .barHeight(60)
  330. .barMode(BarMode.Fixed)
  331. //如果用户点了组件,就产生回调
  332. .onChange((index: number) => {
  333. this.index = index;
  334. })
  335. }
  336. .width('100%')
  337. }
  338. .height('100%')
  339. }
  340. }

但是在上述代码中有一段代码出现错误,如下:

  1. this.cart.map((item, index) => {
  2. Row() {
  3. Checkbox()
  4. .select(item.select)
  5. .width(40)
  6. .height(40)
  7. .onClick(() => this.toggleCheck(index))
  8. Text(item.name.toString())
  9. .fontSize(28)
  10. .fontWeight("bold")
  11. .margin({ left: 10, right: 90 })
  12. Text(item.price.toString())
  13. .fontSize(28)
  14. .fontWeight("bold")
  15. .margin({ left: 10, right: 90 })
  16. }.width("100%")
  17. .height(60)
  18. .alignItems(VerticalAlign.Center)
  19. .padding({ top: 10 })
  20. }
  21. )

经分析,this.state.cart.map(...)从逻辑上讲这段代码是正确的,但是还会报错,原因是这部分代码不符合UI组件的语法,this.cart 是一个数据数组,而不是一个 UI 组件。为了符合 UI 组件语法,我需要将 this.cart 的处理逻辑放在一个单独的函数或方法中,并在需要的地方调用该函数来生成 UI 组件。第一次尝试我建立了一个空的逻辑数组cartItems,将 this.cart.map 的结果存储在 cartItems 数组中,并使用 forEach 循环来渲染每个 Row 组件(如下代码)。

  1. const cartItems = this.cart.map((item, index) => {
  2. return Row() {
  3. Checkbox()
  4. .select(item.select)
  5. .width(40)
  6. .height(40)
  7. .onClick(() => this.toggleCheck(index))
  8. Text(item.name.toString())
  9. .fontSize(28)
  10. .fontWeight("bold")
  11. .margin({ left: 10, right: 90 })
  12. Text(item.price.toString())
  13. .fontSize(28)
  14. .fontWeight("bold")
  15. .margin({ left: 10, right: 90 })
  16. }.width("100%")
  17. .height(60)
  18. .alignItems(VerticalAlign.Center)
  19. .padding({ top: 10 })
  20. });
  21. cartItems.forEach((item) => {
  22. item;
  23. });

仍会报错并显示无法查询到Row()方法,之后有尝试另一种方法(如下代码)

  1. function generateCartItem(item, index) {
  2. return Row(){
  3. Checkbox()
  4. .select(item.select)
  5. .width(40)
  6. .height(40)
  7. .onClick(() => this.toggleCheck(index))
  8. Text(item.name.toString())
  9. .fontSize(28)
  10. .fontWeight("bold")
  11. .margin({ left: 10, right: 90 })
  12. Text(item.price.toString())
  13. .fontSize(28)
  14. .fontWeight("bold")
  15. .margin({ left: 10, right: 90 })
  16. }.width("100%")
  17. .height(60)
  18. .alignItems(VerticalAlign.Center)
  19. .padding({ top: 10 })
  20. // 在你的组件渲染方法中调用 generateCartItem 函数来生成 UI 组件
  21. render() {
  22. return (
  23. {this.cart.map((item, index) => generateCartItem(item, index))}
  24. );
  25. }

但仍旧报错,所以我处于知道错误的原因,但对解决的方法无法处理错误。

由于远程虚拟机一直显示被占用,运行结果由视图展示

三、不足之处

1.界面未进行美化,比较粗糙

2.无注册功能,用户唯一

3.功能简单,操作简单,复杂程度不够

四、改进之处

1.应增加注册功能,这样可以拥有多个界面,还应该增加商品详情界面,使功能复杂化

2.在商品列表中,应建立数组,将商品信息存储在数组中,以便简化商品列表界面商品信息显示的代码

3.增加复杂度,是代码内容更丰富

五、总结

通过学习这门课程以及此次代码编程,对鸿蒙有了初步了解,也学习到了新的知识,收获很大。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/221040
推荐阅读
相关标签
  

闽ICP备14008679号