技术标签: react.js react react native
React-native实现联系人列表分组组件(支持拼音搜索)
参考资料:
React Native使用SectionList打造城市选择列表,包含分组的跳转:https://blog.csdn.net/sinat_17775997/article/details/71424324?utm_medium=distribute.pc_relevant.none-task-blog-OPENSEARCH-5&depth_1-utm_source=distribute.pc_relevant.none-task-blog-OPENSEARCH-5
一. 功能特性和效果图
功能特性如下:
支持首字母和首个汉字分组检索。
支持右侧分组字母的跳转控制。
支持拼音搜索。
支持批量选择功能、重置功能。
效果图:
二. 技术要点
它的用途是将中文转化为拼音。
https://github.com/hotoo/pinyin
三. 实现思路
将联系人列表中的数据项转化为拼音存储,以便于拼音搜索。
遍历联系人列表,获取字母分组,得到一个set集合,并且转化为数组,该数组用于渲染右侧字母导航组件。
构建SectionList组件渲染数据所需要的列表数据结构,其数组元素结构为:{title:‘分组名称’,data:[]},其中,title表示分组名称,data表示该分组的数据列表。
遍历遍历联系人列表,根据首字母分组汇总,得到SectionList组件渲染数据所需要的列表数据结构。
四. 关键代码
componentWillMount = () => {
// 将数据列表转化为拼音存储,以便于拼音搜索
testData.forEach((item, index, arr) => {
// 将Item的名称转为拼音数组
let pinyinArr = pinyin(item.name, {style: pinyin.STYLE_NORMAL});
item.pinyinArr = pinyinArr;
let pinyinArrStr = ‘’;
// 将拼音数组转化为一个字符串,以支持拼音搜索
for (let i = 0; i < pinyinArr.length; i++) {
for (let j = 0; j < pinyinArr[i].length; j++) {
pinyinArrStr = pinyinArrStr + pinyinArr[i][j];
}
}
item.pinyinArrStr = pinyinArrStr;
});
this.transferToSectionsData(testData);
};
2. 搜索方法
search = () => {
// alert(‘搜索’);
const {dataList, searchValue} = this.state;
if (searchValue && searchValue.trim()) {
let searchValueTemp = searchValue.toLocaleLowerCase();
const resultList = [];
dataList.forEach((item, index, arr) => {
if (item.name) {
if (item.name.toLocaleLowerCase().indexOf(searchValueTemp) >= 0
|| this.pinyinSingleLetterIndexSearch(searchValueTemp, item.pinyinArr) >= 0
|| item.pinyinArrStr.toLocaleLowerCase().indexOf(searchValueTemp) >= 0) {
resultList.push(item);
}
}
});
console.log(‘search.resultList:’, resultList);
this.transferToSectionsData(resultList);
} else {
this.transferToSectionsData(dataList);
}
};
/**
* 在拼音数组中搜索单个拼音,如果匹配,则返回等于大于0的值,否则返回-1
* @param keyword
* @param pinyinArr
* @returns {number}
*/
pinyinSingleLetterIndexSearch = (keyword, pinyinArr) => {
let result = -1;
if (keyword && pinyinArr) {
for (let i = 0; i < pinyinArr.length; i++) {
for (let j = 0; j < pinyinArr[i].length; j++) {
let singleLetterIndex = pinyinArr[i][j].toLocaleLowerCase().indexOf(keyword);
if (singleLetterIndex >= 0) {
return singleLetterIndex;
}
}
}
}
return result;
};
五. 完整代码
注意:图片资源需自行补充。
/**
*
import pinyin from ‘pinyin’;
let testData = [
{id: ‘盖伦’, name: ‘盖伦’},
{id: ‘崔丝塔娜’, name: ‘崔丝塔娜’},
{id: ‘大发明家’, name: ‘大发明家’},
{id: ‘武器大师’, name: ‘武器大师’},
{id: ‘刀妹’, name: ‘刀妹’},
{id: ‘卡特琳娜’, name: ‘卡特琳娜’},
{id: ‘盲僧’, name: ‘盲僧’},
{id: ‘蕾欧娜’, name: ‘蕾欧娜’},
{id: ‘拉克丝’, name: ‘拉克丝’},
{id: ‘剑圣’, name: ‘剑圣’},
{id: ‘赏金’, name: ‘赏金’},
{id: ‘发条’, name: ‘发条’},
{id: ‘瑞雯’, name: ‘瑞雯’},
{id: ‘提莫’, name: ‘提莫’},
{id: ‘卡牌’, name: ‘卡牌’},
{id: ‘剑豪’, name: ‘剑豪’},
{id: ‘琴女’, name: ‘琴女’},
{id: ‘木木’, name: ‘木木’},
{id: ‘雪人’, name: ‘雪人’},
{id: ‘安妮’, name: ‘安妮’},
{id: ‘薇恩’, name: ‘薇恩’},
{id: ‘小法师’, name: ‘小法师’},
{id: ‘艾尼维亚’, name: ‘艾尼维亚’},
{id: ‘奥瑞利安索尔’, name: ‘奥瑞利安索尔’},
{id: ‘布兰德’, name: ‘布兰德’},
{id: ‘凯特琳’, name: ‘凯特琳’},
{id: ‘虚空’, name: ‘虚空’},
{id: ‘机器人’, name: ‘机器人’},
{id: ‘挖掘机’, name: ‘挖掘机’},
{id: ‘EZ’, name: ‘EZ’},
{id: ‘暴走萝莉’, name: ‘暴走萝莉’},
{id: ‘艾克’, name: ‘艾克’},
{id: ‘波比’, name: ‘波比’},
{id: ‘赵信’, name: ‘赵信’},
{id: ‘牛头’, name: ‘牛头’},
{id: ‘九尾’, name: ‘九尾’},
{id: ‘菲兹’, name: ‘菲兹’},
{id: ‘寒冰’, name: ‘寒冰’},
{id: ‘猴子’, name: ‘猴子’},
{id: ‘深渊’, name: ‘深渊’},
{id: ‘凯南’, name: ‘凯南’},
{id: ‘诺克萨斯’, name: ‘诺克萨斯’},
{id: ‘祖安’, name: ‘祖安’},
{id: ‘德莱文’, name: ‘德莱文’},
{id: ‘德玛西亚王子’, name: ‘德玛西亚王子’},
{id: ‘豹女’, name: ‘豹女’},
{id: ‘皮城执法官’, name: ‘皮城执法官’},
{id: ‘泽拉斯’, name: ‘泽拉斯’},
{id: ‘岩雀’, name: ‘岩雀’},
];
const selectedFieldName = ‘id’;
const isAndroid = Platform.OS === ‘android’;
export default class IndexListComponentExample extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
searchValue: null,
dataList: testData,
sections: [], //section数组
// letterArr: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'], //首字母数组
letterArr: [], //首字母数组
activeLetterIndex: 0,
selectedItemSet: new Set(),
// 是否开启批量选择模式
batchSelected: true,
refreshCount: 0,
};
}
componentWillMount = () => {
// 将数据列表转化为拼音存储,以便于拼音搜索
testData.forEach((item, index, arr) => {
// 将Item的名称转为拼音数组
let pinyinArr = pinyin(item.name, {style: pinyin.STYLE_NORMAL});
item.pinyinArr = pinyinArr;
let pinyinArrStr = '';
// 将拼音数组转化为一个字符串,以支持拼音搜索
for (let i = 0; i < pinyinArr.length; i++) {
for (let j = 0; j < pinyinArr[i].length; j++) {
pinyinArrStr = pinyinArrStr + pinyinArr[i][j];
}
}
item.pinyinArrStr = pinyinArrStr;
});
this.transferToSectionsData(testData);
};
/**
* 转化数据列表
*/
transferToSectionsData = (dataList) => {
//获取联系人列表
let sections = [], letterArr = [];
// 右侧字母栏数据处理
dataList.forEach((item, index, arr) => {
let itemTemp = pinyin(item.name.substring(0, 1), {
style: pinyin.STYLE_FIRST_LETTER,
})[0][0].toUpperCase();
letterArr.push(itemTemp);
});
letterArr = [...new Set(letterArr)].sort();
this.setState({letterArr: letterArr});
// 分组数据处理
letterArr.forEach((item, index, arr) => {
sections.push({
title: item,
data: [],
});
});
dataList.forEach((item1, index1, arr1) => {
let listItem = item1;
sections.forEach((item2, index2, arr2) => {
let firstName = listItem.name.substring(0, 1);
let firstLetter = pinyin(firstName, {style: pinyin.STYLE_FIRST_LETTER})[0][0].toUpperCase();
let pinyinStrArr = pinyin(listItem.name, {style: pinyin.STYLE_NORMAL});
console.log('pinyinStr', pinyinStrArr);
if (item2.title === firstLetter) {
item2.data.push({
firstName: firstName,
name: listItem.name,
id: listItem.id,
});
}
});
});
this.setState({sections: sections});
};
openBatchSelectedMode = (callback) => {
this.setState({
batchSelected: true,
selectedItemSet: new Set(),
}, () => {
callback && callback();
});
};
closeBatchSelectedMode = () => {
this.setState({
batchSelected: false,
selectedItemSet: new Set(),
});
};
addOrDeleteSelectedItem = (item) => {
const {batchSelected, selectedItemSet, refreshCount} = this.state;
if (!batchSelected) {
return;
}
if (item && item[selectedFieldName]) {
if (selectedItemSet.has(item[selectedFieldName])) {
selectedItemSet.delete(item[selectedFieldName]);
} else {
selectedItemSet.add(item[selectedFieldName]);
}
console.log('addOrDeleteSelectedItem.selectedItemSet', selectedItemSet);
this.setState({
selectedItemSet: selectedItemSet,
refreshCount: refreshCount + 1,
}, () => {
});
}
};
/**
* 重置选中的成员
*/
clearSelectedItem = () => {
const {batchSelected, selectedItemSet, refreshCount} = this.state;
selectedItemSet.clear();
this.setState({
selectedItemSet: selectedItemSet,
refreshCount: refreshCount + 1,
}, () => {
});
};
// 字母关联分组跳转
_onSectionselect = (key) => {
this.setState({
activeLetterIndex: key,
}, () => {
});
this.refs._sectionList.scrollToLocation({
itemIndex: 0,
sectionIndex: key,
viewOffset: 20,
});
};
// 分组列表的头部
_renderSectionHeader(sectionItem) {
const {section} = sectionItem;
return (
<View style={
{
height: 20,
backgroundColor: '#e7f0f9',
paddingHorizontal: 10,
flexDirection: 'row',
alignItems: 'center',
}}>
<Text style={
{fontSize: 16}}>{section.title.toUpperCase()}</Text>
</View>
);
}
renderItemSelectedIcon = (item) => {
if (!item) {
return;
}
const {batchSelected, selectedItemSet} = this.state;
if (batchSelected) {
let isActive = selectedItemSet.has(item[selectedFieldName]);
return (
<Image
style={
{
width: 18,
height: 18,
marginRight: 10,
}}
source={isActive
? require('@assets/icons/common/icon_item_selected.png')
: require('@assets/icons/common/icon_item_unselected.png')}
/>
);
} else {
return null;
}
};
_renderItem(item, index) {
const {batchSelected} = this.state;
return (
<TouchableOpacity
style={
{
paddingLeft: 20,
paddingRight: 30,
height: 50,
flexDirection: 'row',
justifyContent: 'flex-start',
alignItems: 'center',
borderBottomWidth: 1,
borderBottomColor: '#efefef',
}}
activeOpacity={.75}
onLongPress={() => {
if (!batchSelected) {
this.openBatchSelectedMode(() => {
this.addOrDeleteSelectedItem(item);
});
}
}}
onPress={() => {
this.addOrDeleteSelectedItem(item);
}}
>
{
this.renderItemSelectedIcon(item)
}
<View style={
{
flexDirection: 'row',
alignItems: 'center',
// justifyContent: 'space-between',
flexGrow: 1,
}}>
<View style={
{
// padding: 10,
height: 30,
width: 30,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#2988FF',
}}>
<Text style={
{
color: '#fff',
fontSize: 18,
}}>
{item.firstName}
</Text>
</View>
<View style={
{
marginLeft: 10,
}}>
<Text style={
{}}>{item.name}</Text>
</View>
</View>
</TouchableOpacity>
);
}
renderBatchSelectedHeader = () => {
const {batchSelected, selectedItemSet} = this.state;
if (!batchSelected) {
return (
<View style={
{
paddingLeft: 10,
paddingRight: 10,
height: 50,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
borderBottomWidth: 1,
borderBottomColor: '#efefef',
}}>
<TouchableOpacity
style={
{
padding: 10,
}}
>
</TouchableOpacity>
<View style={
{}}>
</View>
<TouchableOpacity
style={
{
padding: 10,
}}
onPress={() => {
this.openBatchSelectedMode();
}}
>
<Text>批量选择</Text>
</TouchableOpacity>
</View>
);
}
return (
<View style={
{
paddingLeft: 10,
paddingRight: 10,
height: 50,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
}}>
<TouchableOpacity
style={
{
padding: 10,
}}
onPress={() => {
this.closeBatchSelectedMode();
}}
>
<Text>取消</Text>
</TouchableOpacity>
<View style={
{}}>
<Text>已选择{selectedItemSet.size}条记录</Text>
</View>
<TouchableOpacity
style={
{
padding: 10,
}}
onPress={() => {
this.closeBatchSelectedMode();
}}
>
<Text>确定</Text>
</TouchableOpacity>
</View>
);
};
render = () => {
const {letterArr, sections, activeLetterIndex, batchSelected} = this.state;
//偏移量 = (设备高度 - 字母索引高度 - 底部导航栏 - 顶部标题栏 - 24)/ 2
let top_offset = (Dimensions.get('window').height - letterArr.length * 16 - 52 - 44 - 24) / 2;
if (isAndroid) {
top_offset = top_offset + StatusBar.currentHeight + 45;
}
return (
<SafeAreaView style={
{
flex: 1,
}}>
{
this.renderSearchBar()
}
{
this.renderBatchSelectedHeader()
}
<SectionList
ref="_sectionList"
renderItem={({item, index}) => this._renderItem(item, index)}
renderSectionHeader={this._renderSectionHeader.bind(this)}
sections={sections}
keyExtractor={(item, index) => item + index}
ItemSeparatorComponent={() => <View/>}
/>
{/*右侧字母栏*/}
<View style={
{position: 'absolute', width: 26, right: 0, top: top_offset}}>
<FlatList
data={letterArr}
keyExtractor={(item, index) => index.toString()}
renderItem={({item, index}) => {
let isActive = index === activeLetterIndex;
// let textStyle = isActive ? styles.activeIndicatorText : styles.inactiveIndicatorText;
// let containerStyle = isActive ? styles.activeIndicatorContainer : styles.inactiveIndicatorContainer;
let textStyle = styles.inactiveIndicatorText;
let containerStyle = styles.inactiveIndicatorContainer;
return (
<TouchableOpacity
style={[
{
marginVertical: 2,
height: 16,
width: 16,
borderRadius: 8,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
},
containerStyle,
]}
onPress={() => {
this._onSectionselect(index);
}}
>
<Text style={[{
fontSize: 12,
}, textStyle]}>
{item.toUpperCase()}
</Text>
</TouchableOpacity>
);
}}
/>
</View>
</SafeAreaView>
);
};
setSearchValue = (searchValue, callback) => {
this.setState({
searchValue: searchValue,
}, () => {
callback && callback();
});
};
search = () => {
// alert('搜索');
const {dataList, searchValue} = this.state;
if (searchValue && searchValue.trim()) {
let searchValueTemp = searchValue.toLocaleLowerCase();
const resultList = [];
dataList.forEach((item, index, arr) => {
if (item.name) {
if (item.name.toLocaleLowerCase().indexOf(searchValueTemp) >= 0
|| this.pinyinSingleLetterIndexSearch(searchValueTemp, item.pinyinArr) >= 0
|| item.pinyinArrStr.toLocaleLowerCase().indexOf(searchValueTemp) >= 0) {
resultList.push(item);
}
}
});
console.log('search.resultList:', resultList);
this.transferToSectionsData(resultList);
} else {
this.transferToSectionsData(dataList);
}
};
/**
* 在拼音数组中搜索单个拼音,如果匹配,则返回等于大于0的值,否则返回-1
* @param keyword
* @param pinyinArr
* @returns {number}
*/
pinyinSingleLetterIndexSearch = (keyword, pinyinArr) => {
let result = -1;
if (keyword && pinyinArr) {
for (let i = 0; i < pinyinArr.length; i++) {
for (let j = 0; j < pinyinArr[i].length; j++) {
let singleLetterIndex = pinyinArr[i][j].toLocaleLowerCase().indexOf(keyword);
if (singleLetterIndex >= 0) {
return singleLetterIndex;
}
}
}
}
return result;
};
renderSearchBar = () => {
const {searchValue} = this.state;
return (
<View style={
{
flexDirection: 'row',
alignItems: 'center',
paddingTop: 10,
paddingBottom: 10,
backgroundColor: '#fff',
borderBottomWidth: 1,
borderBottomColor: '#efefef',
}}>
<TouchableOpacity
style={
{
paddingLeft: 15,
paddingRight: 15,
}}
onPress={() => {
}}
>
<Image source={require('@assets/icons/common/icon_back.png')}/>
</TouchableOpacity>
<View
style={
{
flex: 1,
}}
>
<View style={
{
height: 30,
backgroundColor: '#F0F0F0',
borderRadius: 30,
paddingLeft: 10,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
}}>
<Image source={require('@assets/icons/common/icon_search.png')}
style={
{width: 15, height: 15}}/>
<TextInput
placeholder="输入查询内容..."
maxLength={100}
style={
{
flex: 1,
marginLeft: 5,
marginRight: 5,
paddingTop: 5,
paddingBottom: 5,
paddingRight: 5,
// backgroundColor: 'pink',
}}
autoFocus={false}
value={searchValue}
onChangeText={(text) => {
this.setSearchValue(text, () => {
this.search();
});
}}
onSubmitEditing={() => {
}}
/>
{
searchValue
? <TouchableOpacity
style={
{
marginRight: 10,
}}
onPress={() => {
this.setSearchValue(null, () => {
this.search();
});
}}
>
<Image source={require('@assets/icons/common/icon_search_cancel.png')}
style={
{
width: 17,
height: 17,
}}
/>
</TouchableOpacity>
: null
}
</View>
</View>
<TouchableOpacity
style={
{
paddingLeft: 10,
paddingRight: 10,
justifyContent: 'center',
alignItems: 'center',
}}
onPress={() => {
this.search();
}}
>
<Text style={
{
color: '#2988FF',
fontSize: 16,
}}>
搜索
</Text>
</TouchableOpacity>
</View>
);
};
}
const styles = StyleSheet.create({
taskNodeTitleText: {
color: ‘#333333’,
fontWeight: ‘bold’,
fontSize: 16,
},
inactiveIndicatorContainer: {},
activeIndicatorContainer: {
backgroundColor: ‘#2988FF’,
},
inactiveIndicatorText: {
color: ‘#666666’,
},
activeIndicatorText: {
color: ‘#fff’,
},
});
————————————————
版权声明:本文为CSDN博主「举世武双」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_33721382/article/details/106007853
文章浏览阅读6.9k次。WCE 下载地址:链接:https://share.weiyun.com/5MqXW47 密码:bdpqku工具界面_wce.exe -s aaa:win-9r7tfgsiqkf:00000000000000000000000000000000:a658974b892e
文章浏览阅读4.5k次。Weather Globe(Mackiev)Google Earth(Google)Virtual Earth(Microsoft)World Wind(NASA)Skyline Globe(Skylinesoft)ArcGISExplorer(ESRI)国内LTEarth(灵图)、GeoGlobe(吉奥)、EV-Globe(国遥新天地) 软件名称: 3D Weather Globe(http:/_网络地球仪
文章浏览阅读1.9w次,点赞113次,收藏57次。我要买这些东西,然后震惊整个办公室_程序员展示刀,产品经理展示枪
文章浏览阅读1.6w次,点赞7次,收藏63次。霍尔信号、编码器信号与电机转向从电机出轴方向看去,电机轴逆时针转动,霍尔信号的序列为编码器信号的序列为将霍尔信号按照H3 H2 H1的顺序组成三位二进制数,则霍尔信号翻译成状态为以120°放置霍尔为例如不给电机加电,使用示波器测量三个霍尔信号和电机三相反电动势,按照上面所说的方向用手转动电机得到下图① H1的上升沿对应电机q轴与H1位置电角度夹角为0°,..._霍尔信号
文章浏览阅读7.1k次,点赞5次,收藏36次。个人微信淘宝客返利机器人搭建一篇教程全搞定天猫淘宝有优惠券和返利,仅天猫淘宝每年返利几十亿,你知道么?技巧分享:在天猫淘宝京东拼多多上挑选好产品后,按住标题文字后“复制链接”,把复制的淘口令或链接发给机器人,复制机器人返回优惠券口令或链接,再打开天猫或淘宝就能领取优惠券啦下面教你如何搭建一个类似阿可查券返利机器人搭建查券返利机器人前提条件1、注册微信公众号(订阅号、服务号皆可)2、开通阿里妈妈、京东联盟、拼多多联盟一、注册微信公众号https://mp.weixin.qq.com/cgi-b_怎么自己制作返利机器人
文章浏览阅读2.1k次,点赞2次,收藏5次。技术分享时应秉持的基本原则:应有团队和个人、奉献者(统筹人)的概念,同时匹配团队激励、个人激励和最佳奉献者激励;团队应该打开工作内容边界,成员应该来自各内容方向;评分标准不应该过于模糊,否则没有意义,应由客观的基础分值以及分团队的主观综合结论得出。应有心愿单激励机制,促进大家共同聚焦到感兴趣的事情上;选题应有规范和框架,具体到某个小类,这样收获才有目标性,发布分享主题时大家才能快速判断是否是自己感兴趣的;流程和分享的模版应该有固定范式,避免随意的格式导致随意的内容,评分也应该部分参考于此;参会原则,应有_技术分享
文章浏览阅读1k次。在模板中,我们使用了标签,将由o2-view组件负责渲染,给o2-view传入了两个参数:app="内容管理数据"和name="所有信息",我们将在o2-view组件中使用这两个参数,用于展现“内容管理数据”这个数据应用下的“所有信息”视图。在o2-view组件中,我们主要做的事是,在vue组件挂载后,将o2的视图组件,再挂载到o2-view组件的根Dom对象。当然,这里我们要在我们的O2服务器上创建好数据应用和视图,对应本例中,就是“内容管理数据”应用下的“所有信息”视图。..._vue2 oa
文章浏览阅读222次。table是lua中非常重要的一种类型,有必要对其多了解一些。
文章浏览阅读549次,点赞30次,收藏9次。我们前面学习都有一个概念,被private封装的资源只能类内部访问,外部是不行的,但这个规定被反射赤裸裸的打破了。反射就像一面镜子,它可以清楚看到类的完整结构信息,可以在运行时动态获取类的信息,创建对象以及调用对象的属性和方法。
文章浏览阅读1.1k次,点赞35次,收藏12次。Logical Volume Manager,逻辑卷管理能够在保持现有数据不变的情况下动态调整磁盘容量,从而提高磁盘管理的灵活性/boot分区用于存放引导文件,不能基于LVM创建PV(物理卷):基于硬盘或分区设备创建而来,生成N多个PE,PE默认大小4M物理卷是LVM机制的基本存储设备,通常对应为一个普通分区或整个硬盘。创建物理卷时,会在分区或硬盘的头部创建一个保留区块,用于记录 LVM 的属性,并把存储空间分割成默认大小为 4MB 的基本单元(PE),从而构成物理卷。
文章浏览阅读379次,点赞7次,收藏10次。4、Dielecteic voltage-withstand test 介电耐压试验。1、Maximum output voltage test 输出电压试验。6、Resistance to crushing test 抗压碎试验。8、Push-back relief test 阻力缓解试验。7、Strain relief test 应变消除试验。2、Power input test 功率输入试验。3、Temperature test 高低温试验。5、Abnormal test 故障试验。
文章浏览阅读535次。镜像烧写说明_正点原子 imx6ull nand 烧录