uni-app--》基于小程序开发的电商平台项目实战(六)

🏍️作者简介:大家好,我是亦世凡华、渴望知识储备自己的一名在校大学生

🛵个人主页:亦世凡华、

🛺系列专栏:uni-app

🚲座右铭:人生亦可燃烧,亦可腐败,我愿燃烧,耗尽所有光芒。

👀引言

        ⚓经过web前端的学习,相信大家对于前端开发有了一定深入的了解,今天我开设了uni-app专栏,主要想从移动端开发方向进一步发展,而对于我来说写移动端博文的第二站就是uni-app开发,希望看到我文章的朋友能对你有所帮助。

        今天开始使用 vue3 + uni-app 搭建一个电商购物的小程序,因为文章会将项目的每一个地方代码的书写都会讲解到,所以本项目会分成好几篇文章进行讲解,我会在最后一篇文章中会将项目代码开源到我的GitHub上,大家可以自行去进行下载运行,希望本文章对有帮助的朋友们能多多关注本专栏,学习更多前端uni-app知识。然后开篇先简单介绍一下本项目用到的技术栈都有哪几个方面(阅读此次项目实践文章能够学习到的技术):

uni-app:跨平台的应用开发框架,基于vue.js可以一套代码同时构建运行在多个平台。

pnpm:高性能、轻量级npm替代品,帮助开发人员更加高效地处理应用程序的依赖关系。

vue3:vue.js最新版本的用于构建用户界面的渐进式JavaScript框架。

typescript:JavaScript的超集,提供了静态类型检查,使得代码更加健壮。

pinia:vue3构建的Vuex替代品,具有响应式能力,提供非常简单的 API,进行状态管理。

uni-ui:基于vue.js和uni-app的前端UI组件库,开发人员可以快速地构建跨平台应用程序。

        如果是第一次接触uni-app并且想学习uni-app的朋友,我是不建议直接从此次实战项目开始看起,可以先阅读一下我以前的基础文章:什么是uniapp?如何开发uniapp?按部就班的学习可以让学习变得更轻松更容易上手哦,闲话少说我们直接开始今天的uni-app实战篇。

目录

地址模块静态搭建

新建地址功能实现

修改/删除地址功能实现

SKU模块搭建

购物车模块搭建


地址模块静态搭建

接下来实现地址模块的静态搭建,地址模块需要借助两个页面,一个是地址信息的展示另一个是新建或修改地址信息,地址信息这一块我们也将其放置在分包页面当中进行展示,首先我们要新建两个分包页面,如下:

因为address-form要进行新建页面和修改页面两部分内容,所以这里新建页面不需要设置标题后面根据传参动态设置标题即可。

收获地址的静态结构布局如下,修改地址这一块传递query参数,新建地址不需要传递:

<template><view class="viewport"><!-- 地址列表 --><scroll-view class="scroll-view" scroll-y><view v-if="true" class="address"><view class="address-list"><!-- 收货地址项 --><view class="item"><view class="item-content"><view class="user">小王子<text class="contact">13111111111</text><text v-if="true" class="badge">默认</text></view><view class="locate">广东省 广州市 天河区 程序员</view><navigatorclass="edit"hover-class="none":url="`/subpackage/address-form/address-form?id=1`">修改</navigator></view></view><!-- 收货地址项 --><view class="item"><view class="item-content"><view class="user">小公主<text class="contact">13222222222</text><text v-if="false" class="badge">默认</text></view><view class="locate">北京市 北京市 顺义区 程序员</view><navigatorclass="edit"hover-class="none":url="`/pagesMember/address-form/address-form?id=2`">修改</navigator></view></view></view></view><view v-else class="blank">暂无收货地址</view></scroll-view><!-- 添加按钮 --><view class="add-btn"><navigator hover-class="none" url="/subpackage/address-form/address-form">新建地址</navigator></view></view>
</template>

新建和修改地址的页面设计如下:

<template><view class="content"><form><!-- 表单内容 --><view class="form-item"><text class="label">收货人</text><input class="input" placeholder="请填写收货人姓名" value="" /></view><view class="form-item"><text class="label">手机号码</text><input class="input" placeholder="请填写收货人手机号码" value="" /></view><view class="form-item"><text class="label">所在地区</text><picker class="picker" mode="region" value=""><view v-if="false">广东省 广州市 天河区</view><view v-else class="placeholder">请选择省/市/区(县)</view></picker></view><view class="form-item"><text class="label">详细地址</text><input class="input" placeholder="街道、楼牌号等信息" value="" /></view><view class="form-item"><label class="label">设为默认地址</label><switch class="switch" color="#27ba9b" :checked="true" /></view></form></view><!-- 提交按钮 --><button class="button">保存并使用</button>
</template>

根据query传递过来的参数动态设置修改和新建页面的显示:

// 获取query数据
const query = defineProps<{id?: string
}>()
// 动态设置标题
uni.setNavigationBarTitle({ title: query.id ? '修改地址' : '新建地址' })

最终呈现的效果如下:

新建地址功能实现

接下来开始实现新建地址的功能,首先我们编写相应的接口函数用来收集表单数据进行新建地址,根据后端返回给我们的数据编写相应的ts类型,然后在前端接口中进行类型的限制:

根据需要的参数,在新建地址页面设置响应式ref数据用来获取相应的表单数据,如下:

// 表单数据
const form = ref({receiver: '', // 收货人contact: '', // 联系方式fullLocation: '', // 省市区(前端展示)provinceCode: '', // 省份编码(后端参数)cityCode: '', // 城市编码(后端参数)countyCode: '', // 区/县编码(后端参数)address: '', // 详细地址isDefault: 0, // 默认地址,1为是,0为否
})

正常的输入框直接使用 v-model 进行数据的双向绑定即可:

关于获取所在地区的数据在上文讲解个人信息模块的时候已经讲解过了,这里也简单提一下,通过change函数监听用户选择的数据,将文字进行前端页面展示,数字进行后端参数传递:

// 收集所在地区的事件处理函数
const onRegionChange: UniHelper.RegionPickerOnChange = (ev) => {// 省市区前端展示需要form.value.fullLocation = ev.detail.value.join(' ')// 省市区后端参数需要const [provinceCode, cityCode, countyCode] = ev.detail.code!// 将获取到的参数进行一个合并Object.assign(form.value, { provinceCode, cityCode, countyCode })
}

设置默认地址的switch按钮也是采用change函数进行监听,根据boolean类型返回数字1或0:

// 收集是否默认收获地址
const onSwitchChange: UniHelper.SwitchOnChange = (ev) => {form.value.isDefault = ev.detail.value ? 1 : 0
}

接下来给按钮设置点击函数用于表单数据的提交,然后进行页面的跳转:

// 提交表单
const onSubmit = async () => {// 新建地址请求await postMemberAddressAPI(form.value)// 成功提示uni.showToast({ icon: 'success', title: '添加成功' })// 返回上一页setTimeout(() => {uni.navigateBack()}, 400)
}

进行页面跳转之后接下来就需要进行地址页面数据的渲染了,老生常谈的事先编写相应接口函数:

编写完相应的接口函数之后,接下来我们需要调用该函数然后通过onShow页面展示的时候调用:

<script setup lang="ts">
import { ref } from 'vue'
import { getMemberAddressAPI } from '@/api/address'
import type { AddressItem } from '@/types/address'
import { onShow } from '@dcloudio/uni-app'// 获取收获地址列表数据
const addressList = ref<AddressItem[]>([])
const getMemberAddressData = async () => {const res = await getMemberAddressAPI()addressList.value = res.result
}
// 初始化调用(页面显示调用)
onShow(() => {getMemberAddressData()
})
</script>

通过v-for遍历数据,然后通过查找语法进行页面的展示:

最终呈现的结果如下:

修改/删除地址功能实现

接下来实现修改地址的功能,这里我们也先编写相应的接口函数,修改地址需要传入相应的id值:

/*** 获取收获地址详情接口* @param id 地址id(路径参数)*/
export const getMemberAddressByIdAPI = (id: string) => {return http<AddressItem>({method: 'GET',url: `/member/address/${id}`,})
}

在修改地址页面中调用接口函数获取地址的详情数据,并将数据合并到表单当中:

// 获取收获地址详情数据
const getMemberAddressByIdData = async () => {if (query.id) {const res = await getMemberAddressByIdAPI(query.id)// 把数据合并到表单中Object.assign(form.value, res.result)}
}
// 初始化调用(页面加载)
onLoad(() => {getMemberAddressByIdData()
})

接下来我们需要对表单的提交按钮再进行处理,首先我们先编写相应的修改地址的API:

/*** 获取收获地址详情接口* @param id 地址id(路径参数)* @param data 表单数据(请求体参数)*/
export const puttMemberAddressByIdAPI = (id: string, data: AddressParams) => {return http<AddressItem>({method: 'PUT',url: `/member/address/${id}`,data,})
}

编写完接口函数之后,我们就可以根据当前页面是否有query参数来判断是修改还是新增:

最终呈现的结果如下:

接下来对修改地址页面进行表单的校验规则,如下我们定义相应的规则:

// 定义校验规则
const rules: UniHelper.UniFormsRules = {receiver: { rules: [{ required: true, errorMessage: '请输入收货人姓名' }] },contact: {rules: [{ required: true, errorMessage: '请输入联系方式' },{ pattern: /^1[3-9]\d{9}$/, errorMessage: '手机号格式不正确' },],},fullLocation: { rules: [{ required: true, errorMessage: '请选择所在地区' }] },address: { rules: [{ required: true, errorMessage: '请选择详细地址' }] },
}

接下来借助uniapp中的uni-form进行相应的表单校验:

拿到相应的表单实例之后,调用表单校验函数进行验证,通过trycatch进行报错提示:

// 存储表单组件实例
const formRef = ref<UniHelper.UniFormsInstance>()
// 提交表单
const onSubmit = async () => {try {await formRef.value?.validate?.()if (query.id) {// 修改地址的APIawait puttMemberAddressByIdAPI(query.id, form.value)} else {// 新建地址请求await postMemberAddressAPI(form.value)}// 成功提示uni.showToast({ icon: 'success', title: query.id ? '修改成功' : '添加成功' })// 返回上一页setTimeout(() => {uni.navigateBack()}, 400)} catch (error) {uni.showToast({ icon: 'error', title: '请填写完整信息' })}
}

如果什么数据都没有填入就进行表单提交的话,呈现的效果如下图所示:

接下来实现删除地址功能的实现,这里我们借助uniapp给我们提供的uni-swipe-action组件进行删除业务的实现,首先我们先编写相应的删除功能的接口函数:

/*** 删除收获地址* @param id 地址id(路径参数)*/
export const deleteMemberAddressByIdAPI = (id: string) => {return http<AddressItem>({method: 'DELETE',url: `/member/address/${id}`,})
}

接下来将我们要进行删除业务的功能换上相应的组件,在组件中存放要删除按钮的插槽:

通过点击函数设置删除按钮的回调:

// 删除收获地址
const onDeleteAddress = (id: string) => {// 二次确认uni.showModal({content: '确认删除?',success: async (res) => {if (res.confirm) {// 根据id删除收获地址await deleteMemberAddressByIdAPI(id)// 重新获取数据列表getMemberAddressData()}},})
}

最终呈现的结果如下:

SKU模块搭建

SKU模块展示了购买某种商品时给我们呈现的选择样式的相关界面,uniapp插件市场也为其提供了相应的插件进行使用:插件市场选择 ,这里我们选择了vue3项目,找下载量最高的插件进行下载

进行该插件进行相应的下载:

下载完插件根据作者给我们的使用提示进行插件的部署:

配置好文件之后,记下来我们在商品详情组件引入该组件:

通过 v-model 进行双向数据绑定来显示SKU页面的显示与隐藏,当打开SKU页面的时候根据传入数据的不同来切换不同的按钮状态,mode属性就是改变其按钮模式的:

// 是否显示SKU组件
const isShowSku = ref(false)
// 商品信息
const localdata = ref({} as SkuPopupLocaldata)
// 设置按钮模式
enum SkuMode {Both = 1,Cart = 2,Buy = 3,
}
const mode = ref<SkuMode>(SkuMode.Both)
// 打开Sku弹窗修改按钮模式
const openSkuPopup = (val: SkuMode) => {// 显示SKU组件isShowSku.value = true// 修改按钮模式mode.value = val
}

当我们点击选择的时候,两个按钮都同时显示:

这里我们通过之前设置的函数传递对应的数值来显示不同按钮下显示的不同按钮模式:

最终呈现的效果如下:

接下来实现,当我们点击商品选择的时候,原本的请选择商品规格的文字就会变成我们选择的文字,这里我们通过设置SKU组件的ref实例获取组件实例之后,通过selectArr属性数据替代原本的文字数据:

// SKU组件的实例
const skuPopupRef = ref<SkuPopupInstance>()
// 计算被选中的值
const selectArrText = computed(() => {return skuPopupRef.value?.selectArr?.join(' ').trim() || '请选择商品规格'
})

然后在选择按钮文字处通过插值语法动态设置其对应的文字内容:

这里我们也可以通过 actived-style 属性设置其颜色、边框和背景等相关颜色,使其更适配当前主题

最终呈现的效果如下:

接下来实现加入购物车功能的实现,在SKU组件中调用@add-cart设置其加入购物车的回调:

// 加入购物车的事件处理函数
const onAddCart = async (ev: SkuPopupEvent) => {await postMemberCartAPI({ skuId: ev._id, count: ev.buy_num })uni.showToast({ icon: 'none', title: '添加成功' })isShowSku.value = false
}

购物车模块搭建

还是老生常谈的东西,关于购物车界面有两种情况的展示,一种是用户未登录状态另一种是用户已登录状态,这里需要我们先通过判断仓库中是否有用户信息来进行v-if和v-else展示,具体如下:

接下来开始编写接口函数来获取购物车中的数据:

/*** 获取购物车列表*/
export const getMemberCartAPI = () => {return http<CartItem[]>({method: 'GET',url: '/member/cart',})
}

在购物车组件中调用该接口函数获取相应的购物车数据,将获取到的数据存储到ref数据当中:

// 获取购物车数据
const getMemberCartData = async () => {const res = await getMemberCartAPI()cartList.value = res.result
}
// 初始化调用(页面显示)
onShow(() => {if (memberStore.profile) {getMemberCartData()}
})

下面就是通过插值语法将获取到的数据进行一个展示了,如下:

最终呈现的效果如下:

删除商品也很简单,调用相应的接口函数,如下:

/*** 删除/清空购物车单品* @param data 请求头参数 ids SKUID 的集合*/
export const deleteMemberCartAPI = (data: { ids: string[] }) => {return http({method: 'DELETE',url: '/member/cart',data,})
}

然后给删除按钮设置点击事件,如此就可以进行商品的删除了,如下:

// 点击删除按钮
const onDeleteCart = (skuid: string) => {// 二次确认uni.showModal({content: '是否删除',success: async (res) => {if (res.confirm) {// 后端删除await deleteMemberCartAPI({ ids: [skuid] })// 重新获取列表getMemberCartData()}},})
}

接下来开始设置数据点击框和商品选择框的数据的交互,首先我们先编写相应的接口函数:

/*** 修改购物车单品* @param skuId SKUID* @param data selected 选中状态 count 商品数量*/
export const putMemberCartBySkuIdAPI = (skuId: string,data: { selected?: boolean; count?: number },
) => {return http({method: 'PUT',url: `/member/cart/${skuId}`,data,})
}
/*** 购物车全选/取消全选* @param data selected 是否选中*/
export const putMemberCartSelectedAPI = (data: { selected: boolean }) => {return http({method: 'PUT',url: '/member/cart/selected',data,})
}

通过调用相关接口函数以及设置计算属性来得到改变选择框的状态:

// 修改商品数量
const onChageCount = (ev: InputNumberBoxEvent) => {putMemberCartBySkuIdAPI(ev.index, { count: ev.value })
}
// 修改选中状态-单品修改
const onChangeSelected = (item: CartItem) => {// 前端数据更新-是否选中取反item.selected = !item.selected// 后端数据更新putMemberCartBySkuIdAPI(item.skuId, { selected: item.selected })
}
// 计算全选状态
const isSelectedAll = computed(() => {return cartList?.value?.length && cartList?.value.every((v) => v.selected)
})// 修改选中状态-全选修改
const onChangeSelectedAll = () => {// 全选状态取反const _isSelectedAll = !isSelectedAll.value// 前端数据更新cartList?.value?.forEach((item) => {item.selected = _isSelectedAll})// 后端数据更新putMemberCartSelectedAPI({ selected: _isSelectedAll })
}

最终呈现的效果如下:

本项目地址管理页面、购物车页面的一些基本功能的搭建就讲解到这,下一篇文章将继续讲解项目其他页面操作,关注博主学习更多前端uni-app知识,您的支持就是博主创作的最大动力! 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端食堂技术周刊第 100 期:TS 5.3 Beta、ViteConf2023、Rspress 1.0、Fresh 1.5、Chrome 118

美味值&#xff1a;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f; 口味&#xff1a;乌龙金桂 食堂技术周刊仓库地址&#xff1a;https://github.com/Geekhyt/weekly 大家好&#xff0c;我是童欧巴。欢迎来到前端食堂技术周刊&#xff0c;我们先来看下…

springBoot实现发布订阅

应用场景&#xff1a; 在开发中&#xff0c;有时需要监听某些事件&#xff0c;在事件触发时&#xff0c;执行相应的逻辑。 也可以说是基于事件驱动的观察者模式。 比如当点击某个按钮时&#xff0c;触发相应的功能&#xff0c;或者用户下单通知等等。 SpringBoot 可以通过 …

Leetcode 2906. Construct Product Matrix

Leetcode 2906. Construct Product Matrix 1. 解题思路2. 代码实现 题目链接&#xff1a;2906. Construct Product Matrix 1. 解题思路 这道题其实算是一道数论题。 本来其实python的pow内置函数已经帮我们基本处理了所有的问题了&#xff0c;但是这里稍微做了一点复杂化操…

《Node.js+Express+MongoDB+Vue.js全栈开发实战》简介

今天介绍的这本书是《Node.jsExpressMongoDBVue.js全栈开发实战》。该书由清华大学出版社于2023年1月出版 外观 从书名故名思议&#xff0c;就是基于Node.jsExpressMongoDBVue.js来实现企业级应用全栈开发。 封面风格比较简约&#xff0c;插图是一张类似于罗马时代战车形象&…

Leetcode刷题详解——找到字符串中所有字母异位词

1. 题目链接&#xff1a;438. 找到字符串中所有字母异位词 2. 题目描述&#xff1a; 给定两个字符串 s 和 p&#xff0c;找到 s 中所有 p 的 异位词 的子串&#xff0c;返回这些子串的起始索引。不考虑答案输出的顺序。 异位词 指由相同字母重排列形成的字符串&#xff08;包括…

C语言---预处理详解

1.预定义符号 在C语言中有一些内置的预定义符号 __FILE__ __LINE__ __DATE__ __TIME__ __STDC__//进行编译的源文件 //文件当前的行号 //文件被编译的日期 //文件被编译的时间 //如果编译器遵循ANSI C&#xff0c;其值为1&#xff0c;否则未定义 编译器在__STDC__报错,说明,v…

CF837G Functions On The Segments

CF837G Functions On The Segments Functions On The Segments - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 文章目录 CF837G Functions On The Segments题目大意思路code 题目大意 你有 n n n 个函数&#xff0c;第 i i i 个函数 f i f_i fi​ 为&#xff1a; f i ( x…

【TES720D】青翼科技基于复旦微的FMQL20S400全国产化ARM核心模块

板卡概述 TES720D是一款基于上海复旦微电子FMQL20S400的全国产化核心模块。该核心模块将复旦微的FMQL20S400&#xff08;兼容FMQL10S400&#xff09;的最小系统集成在了一个50*70mm的核心板上&#xff0c;可以作为一个核心模块&#xff0c;进行功能性扩展&#xff0c;特别是用…

【数组的使用】

文章目录 前言数组的格式有两种数组是引用数据类型遍历数组获取数组的长度&#xff1a;数组名.length数组之间的引用数组中的null关于引用的注意事项总结 前言 数组的格式有两种 int[] array{1,2,3,4};int[] array2new int[10];//默认将数组进行初始化&#xff0c;里面的值都为…

问:TCP/IP协议栈在内核态的好还是用户态的好

“TCP/IP协议栈到底是内核态的好还是用户态的好&#xff1f;” 问题的根源在于&#xff0c;干嘛非要这么刻意地去区分什么内核态和用户态。 引子 为了不让本文成为干巴巴的说教&#xff0c;在文章开头&#xff0c;我以一个实例分析开始。 最近一段时间&#xff0c;我几乎每…

WPF Datagrid Header数据绑定,表头复选框实现全选、全否、部分选中,根据条目动态变化

制作一个根表头为CheckBox可全选、全不选的列表&#xff0c;且可根据条目自动调整CheckBox的状态&#xff08;选中、不选、部分选中&#xff09;。 本来是想用DataGrid做一个CheckBox的列用于勾选其中的某些行&#xff0c;当时做出来之后想着添加一个全选、全否的功能。做两个…

pytorch里常用操作(持续更新)

对不起我脑子不太记事儿每次变换都得想想想所以干脆汇总一下算了&#xff0c;当然也有一些不是torch包里面的但是没有关系hhh 官方文档里有一堆不太常用的&#xff0c;这里整理的都是自己比较常用的 张量操作 torch.tensor&#xff1a;从Python列表或NumPy数组创建张量 torc…

idea使用debug无法启动,使用run可以启动

1、将调试断点清除 使用快捷键ctrl shift F8&#xff0c;将勾选的选项去除即可 2、Error running SampleApplication: Command line is too long. Shorten command line for SampleApplication or also for Spring Boot default configuration&#xff0c;报这种错误&#x…

vr火灾逃生安全科普软件开展消防突击教育安全有效

VR火灾逃生自救虚拟体验是一种利用虚拟现实技术来模拟火灾逃生自救场景的教育工具。以下是这个体验的几个优点&#xff1a;VR消防安全体验馆的出现&#xff0c;为城市的安全教育开辟了新的途径。这种创新的体验方式&#xff0c;能够让市民在模拟的火灾场景中学习并掌握消防安全…

前端面试基础面试题——10

1. 说说你对 promise 的了解 2.解构赋值及其原理 3.箭头函数需要注意的地方 4.箭头函数和普通函数有什么区别 5.ES6 都有什么 Iterator 遍历器 6.jQuery 一个对象可以同时绑定多个事件&#xff0c;这是如何实现的&#xff1f; 7.jQuery 库中的 $() 是什么&#xff1f; 8…

tcp/ip协议2实现的插图,数据结构2 (9 - 章)

&#xff08;20&#xff09; 20 九章1 IP选项处理 ip_dooptions &#xff08;21&#xff09; 21 九章2 IP选项处理 ip_rtaddr,save_rte,ip_srcroute与结构体 &#xff08;22&#xff09;九章3 IP选项处理 ip_pcbopts, ip_insertoptions , iptime 与结构 &#xff08;23&#xf…

安装 mysql

gpt: 要在 Debian 11 上安装 MySQL 数据库服务器&#xff0c;您可以使用以下步骤&#xff1a; 1. **更新软件包列表**&#xff1a;在安装任何软件之前&#xff0c;始终建议首先更新软件包列表&#xff0c;以确保获取最新的软件包信息。在终端中运行以下命令&#xff1a; bash…

课时4作业1

Description 输入一个整型数&#xff0c;判断是否是对称数&#xff0c;如果是&#xff0c;输出yes&#xff0c;否则输出no&#xff0c;不用考虑这个整型数过大&#xff0c;int类型存不下&#xff0c;不用考虑负值&#xff1b; 例如 12321是对称数&#xff0c;输出yes&#xf…

过滤器(Filter)和拦截器(Interceptor)有什么不同?

过滤器&#xff08;Filter&#xff09;和拦截器&#xff08;Interceptor&#xff09;是用于处理请求和响应的中间件组件&#xff0c;但它们在实现方式和应用场景上有一些不同。 实现方式: 过滤器是Servlet规范中定义的一种组件&#xff0c;通常以Java类的形式实现。过滤器通过在…

编译添加了ALPHA开发板的NXP官方uboot

一. 简介 之前文章学习了 如何在NXP&#xff08;恩智浦&#xff09;官方 uboot 中添加正点原子的 ALPHA 开发板。 如何在NXP&#xff08;恩智浦&#xff09;官方 uboot 中添加正点原子的 ALPHA 开发板&#xff0c;文章如下&#xff1a; 向NXP官方uboot添加Nand版开发板-CSDN博…