(根据居然老师直播课内容整理)
一、页面功能简介
- 在“用户中心”的任一界面,点击左边“收货地址”后,显示下面界面
- 此界面包括以下4个功能:
- 新增收货地址
- 删除当前收货地址
- 编辑(当前记录收货地址)
- 设为默认
- 以4个功能中,“新增收货地址”和“编辑” 需要弹出表单,进行编辑
二、显示收货地址信息页面
1、实现分析
- 在“用户中心”的任一界面,点击左边“收货地址”
- 前端向后端发起 user/addresses路由发起 get请求
- 后端接收请求,判断用户是否登录
- 如果已登录,返回页面
- 如果未登录,跳转到登录页面
2、后端veiw实现
# /apps/users/views.py class AddressView(LoginRequiredMixin, View):"""用户收货地址"""def get(self, request):"""提供收货地址界面"""return render(request, 'user_center_site.html')
3、路由注册
# /apps/users/urls.py # 展示用户地址path('addresses/', views.AddressView.as_view(),name="addresses"),
三、收货地址数据模型
- 用户地址模型类定在users应用的models.py中
1、收货地址模型类
class Address(BaseModel):"""用户地址"""user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='addresses', verbose_name='用户')title = models.CharField(max_length=20, verbose_name='地址名称')receiver = models.CharField(max_length=20, verbose_name='收货人')province = models.ForeignKey('areas.Area', on_delete=models.PROTECT, related_name='province_addresses', verbose_name='省')city = models.ForeignKey('areas.Area', on_delete=models.PROTECT, related_name='city_addresses', verbose_name='市')district = models.ForeignKey('areas.Area', on_delete=models.PROTECT, related_name='district_addresses', verbose_name='区')place = models.CharField(max_length=50, verbose_name='地址')mobile = models.CharField(max_length=11, verbose_name='手机')tel = models.CharField(max_length=20, null=True, blank=True, default='', verbose_name='固定电话')email = models.CharField(max_length=30, null=True, blank=True, default='', verbose_name='电子邮箱')is_deleted = models.BooleanField(default=False, verbose_name='逻辑删除')class Meta:db_table = 'tb_address'verbose_name = '用户地址'verbose_name_plural = verbose_nameordering = ['-update_time'] # 按 update_time倒序 排序
2、收货地址模型类说明
- 收货地址模型类中省、市、县的外键指向areas/models里面的Area。
- 指明外键时,可以使用应用名.模型类名来定义,也可以使用模型类
- ordering表示在进行收货地址模型查询时,默认使用的排序方式。
- ordering = [’-update_time’] : 根据更新的时间倒叙。
3、补充用户模型默认地址字段
- 默认地址字段应保存到user表中,与用户绑定,需要修改User模型
4、数据库迁移
python manage.py makemigrations
python manage.py migrate
四、新增用户收货地址
1、接口设计和定义
1.1 请求方式:
选项
|
方案
|
请求方法
|
POST
|
请求地址
|
/users/addresses/create/
|
1.2 请求参数 :
参数名
|
类型
|
是否必传
|
说明
|
eceiver
|
string
|
是
|
收货人
|
province_id
|
string
|
是
|
省份ID
|
city_id
|
string
|
是
|
城市ID
|
district_id
|
string
|
是
|
区县ID
|
place
|
string
|
是
|
收货地址
|
mobile
|
string
|
是
|
手机号
|
tel
|
string
|
否
|
固定电话
|
email
|
string
|
否
|
邮箱
|
1.3 响应结果 : json
响应结果
|
响应内容
|
code
|
状态码
|
errmsg
|
错误信息
|
id
|
地址ID
|
receiver
|
收货人
|
province
|
省份名称
|
city
|
城市名称
|
district
|
区县名称
|
place
|
收货地址
|
mobile
|
手机号
|
tel
|
固定电话
|
email
|
邮箱
|
2、后端view实现
- 判断用户是否登录
- 接收参数
- 校验参数
- 必传参数量否传递
- 手机号校验
- 如果填有固定电话,固定电话需要校验
- 如果填了邮箱,邮箱需校验
- 保存到数据库
- 根据前端需要,返回详细数据
class AddressCreateView(LoginRequiredJSONMixin, View):"""新增地址"""def post(self,request):# 接收参数json_dict=json.loads(request.body.decode())receiver = json_dict.get('receiver')province_id = json_dict.get('province_id')city_id = json_dict.get('city_id')district_id = json_dict.get('district_id')place = json_dict.get('place')mobile = json_dict.get('mobile')tel = json_dict.get('tel')email = json_dict.get('email')# 验证if not all([receiver,province_id,city_id,district_id,place,mobile]):return http.HttpResponseForbidden('缺少必传参数')if not re.match(r"^1[3-9]\d{9}$", mobile):return http.HttpResponseForbidden('参数mobile有误')if tel and not re.match(r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel):return http.HttpResponseForbidden('参数固定电话有误')if not email and not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):return http.HttpResponseForbidden('参数email有误')# 保存try:address = Address.objects.create(user=request.user,title=receiver,receiver=receiver,province_id=province_id,city_id=city_id,district_id=district_id,place=place,mobile=mobile,tel=tel,email=email,)except Exception as e:logging.error(e)return http.HttpResponseServerError({"code": RETCODE.DBERR, "errmsg": "新增收获地址失败"})# 响应address_dict = {"id": address.id,"receiver": address.receiver,"province": address.province.name,"city": address.city.name,"district": address.district.name,"place": address.place,"mobile": address.mobile,"tel": address.tel,"email": address.email}return http.JsonResponse({"code": RETCODE.OK, "errmsg": "新增收货地址成功", "address": address_dict})
3、定义路由
4、前端实现
4.1 新增地址JS
// 新增地址save_address(){if (this.error_receiver || this.error_place || this.error_mobile || this.error_email || !this.form_address.province_id || !this.form_address.city_id || !this.form_address.district_id ) {alert('信息填写有误!');} else {// 新增地址let url = '/users/addresses/create/';axios.post(url, this.form_address, {headers: {'X-CSRFToken':getCookie('csrftoken')},responseType: 'json'}).then(response => {if (response.data.code == '0') {// 局部刷新界面:展示所有地址信息,将新的地址添加到头部this.addresses.splice(0, 0, response.data.address);this.is_show_edit = false;} else if (response.data.code == '4101') {location.href = '/login/?next=/users/addresses/';} else {alert(response.data.errmsg);}}).catch(error => {console.log(error.response);})}},
4.2 新增地址html
<form><div class="form_group"><label>*收货人:</label><input v-model="form_address.receiver" @blur="check_receiver" type="text" class="receiver"><span v-show="error_receiver" class="receiver_error">请填写收件人</span></div><div class="form_group"><label>*所在地区:</label><select v-model="form_address.province_id"><option v-for="province in provinces" :value="province.id">[[ province.name ]]</option></select><select v-model="form_address.city_id"><option v-for="city in cities" :value="city.id">[[ city.name ]]</option></select><select v-model="form_address.district_id"><option v-for="district in districts" :value="district.id">[[ district.name ]]</option></select></div><div class="form_group"><label>*详细地址:</label><input v-model="form_address.place" @blur="check_place" type="text" class="place"><span v-show="error_place" class="place_error">请填写地址信息</span></div><div class="form_group"><label>*手机:</label><input v-model="form_address.mobile" @blur="check_mobile" type="text" class="mobile"><span v-show="error_mobile" class="mobile_error">手机信息有误</span></div><div class="form_group"><label>固定电话:</label><input v-model="form_address.tel" @blur="check_tel" type="text" class="tel"><span v-show="error_tel" class="tel_error">固定电话有误</span></div><div class="form_group"><label>邮箱:</label><input v-model="form_address.email" @blur="check_email" type="text" class="email"><span v-show="error_email" class="email_error">邮箱信息有误</span></div><input @click="save_address" type="button" name="" value="新 增" class="info_submit"><input @click="is_show_edit=false" type="reset" name="" value="取 消" class="info_submit info_reset">
</form>
5、后端优化
5.1 用户地址不能超过限制,提交后端后就需要判断
- 前端提交后,应首先判断用户收货地址数据量是否超过上限,
- 用户信息可以通过request.user得到
5.2 设置默认收货地址
- 新增第一个地址时,应该添加为默认收货地址
- 或者新增时,判断一下用户默认地址是否为空,如果为空,将收货地下添加为默认收货地址
五、完善显示收货地址页面
- 点击收货地址页面时,后端就应该将已有收货址信息传递给前端,以供显示
1、后端实现
1.1 展示地址请求方式:
选项
|
方案
|
请求方法
|
POST
|
请求地址
|
/users/addresses/
|
1.2 展示地址请求参数: 无
1.3 展示地址响应结果:HTML,参数
参数为{“addresses”:[address字典,],“default_address_id”:缺省地址id}
1.4 代码实现:
- 获取当前用户的收货地址列表
- 循环生成每个地址字典组成的用户地址列表
- 将用户字典列表与缺省地址组合成参数
- 返回html和参数
class AddressView(LoginRequiredMixin, View):"""用户收货地址"""def get(self, request):"""提供收货地址界面"""login_user=request.useraddresses=Address.Objects.filter(user=login_user, is_deleted=False)address_list = []for address in addresses:address_dict = {'id': address.id,'title': address.title,'receiver': address.receiver,'province': address.province.name,'city': address.city.name,'district': address.district.name,'place': address.place,'mobile': address.mobile,'tel': address.tel,'email': address.email}address_list.append(address_dict)context = {'addresses': address_list,"default_address_id": request.user.default_address_id if request.user.default_address_id else 0 }return render(request, 'user_center_site.html',context)
2、前端js
- 将后端模板数据传递到Vue.js
- /static/js/user_center_site.js
- /templates/user_center_site.html
3、user_center_site.html中渲染地址信息
<div class="right_content clearfix" v-cloak><div class="site_top_con"><a @click="show_add_site">新增收货地址</a><span>你已创建了<b>[[ addresses.length ]]</b>个收货地址,最多可创建<b>20</b>个</span></div><div class="site_con" v-for="(address, index) in addresses"><div class="site_title"><h3>[[ address.title ]]</h3><a href="javascript:;" class="edit_icon"></a><em v-if="address.id===default_address_id">默认地址</em><span class="del_site">×</span></div><ul class="site_list"><li><span>收货人:</span><b>[[ address.receiver ]]</b></li><li><span>所在地区:</span><b>[[ address.province ]] [[address.city]] [[ address.district ]]</b></li><li><span>地址:</span><b>[[ address.place ]]</b></li><li><span>手机:</span><b>[[ address.mobile ]]</b></li><li><span>固定电话:</span><b>[[ address.tel ]]</b></li><li><span>电子邮箱:</span><b>[[ address.email ]]</b></li></ul><div class="down_btn"><a v-if="address.id!=default_address_id">设为默认</a><a href="javascript:;" class="edit_icon">编辑</a></div></div>
</div>
4、完善user_center_site.js中成功新增地址后的局部刷新
六、编辑收货地址
- 点击某条收货地址“编辑”按钮时,会显示用户地址修改界面
- 删除地址后端逻辑和新增地址后端逻辑非常的相似。
1、修改地址接口设计和定义
1.1 修改地址请求方式:
选项
|
方案
|
请求方法
|
PUT
|
请求地址
|
/addresses/(?P<address_id>\d+)/
|
1.2 修改地址请求参数: 路径参数 和 JSON
参数名
|
类型
|
是否必传
|
说明
|
address_id
|
string
|
是
|
要修改的地址ID(路径参数)
|
eceiver
|
string
|
是
|
收货人
|
province_id
|
string
|
是
|
省份ID
|
city_id
|
string
|
是
|
城市ID
|
district_id
|
string
|
是
|
区县ID
|
place
|
string
|
是
|
收货地址
|
mobile
|
string
|
是
|
手机号
|
tel
|
string
|
否
|
固定电话
|
email
|
string
|
否
|
邮箱
|
1.3 修改地址响应结果:JSON
响应结果
|
响应内容
|
code
|
状态码
|
errmsg
|
错误信息
|
id
|
地址ID
|
receiver
|
收货人
|
province
|
省份名称
|
city
|
城市名称
|
district
|
区县名称
|
place
|
收货地址
|
mobile
|
手机号
|
tel
|
固定电话
|
email
|
邮箱
|
2、后端view实现
- 判断用户是否登录
- 接收参数
- 校验参数
- 必传参数量否传递
- 手机号校验
- 如果填有固定电话,固定电话需要校验
- 如果填了邮箱,邮箱需校验
- 更新用户地址
- 有两种方法:
- 方法一:Address.objects.get(id=address_id)得到对象,然后依次赋值
- 方法二:Address.objects.filter(id=address_id).update(参数)
- 构造响应数据
- 根据前端需要,返回详细数据
class UpdateDestoryAddressView(LoginRequiredJSONMixin, View):"""更新和删除地址"""def put(self, request, address_id):# 接收参数json_dict = json.loads(request.body.decode())receiver = json_dict.get('receiver')province_id = json_dict.get('province_id')city_id = json_dict.get('city_id')district_id = json_dict.get('district_id')place = json_dict.get('place')mobile = json_dict.get('mobile')tel = json_dict.get('tel')email = json_dict.get('email')# 验证if not all([receiver, province_id, city_id, district_id, place, mobile]):return http.HttpResponseForbidden('缺少必传参数')if not re.match(r"^1[3-9]\d{9}$", mobile):return http.HttpResponseForbidden('参数mobile有误')if tel and not re.match(r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel):return http.HttpResponseForbidden('参数固定电话有误')if email and not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):return http.HttpResponseForbidden('参数email有误')# 更新数据# address = Address.objects.get(id=address_id)# address.title = receiver# address.save()try:# update 返回受影响的行数Address.objects.filter(id=address_id,user=request.user).update(user=request.user,title=receiver,receiver=receiver,province_id=province_id,city_id=city_id,district_id=district_id,place=place,mobile=mobile,tel=tel,email=email,)except Exception as e:return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '修改地址失败'})address = Address.objects.get(id=address_id)address_dict = {'id': address.id,'receiver': address.title,'province': address.province.name,'city': address.city.name,'district': address.district.name,'place': address.place,'mobile': address.mobile,'tel': address.tel,'email': address.email}# 响应新的地址给前端渲染return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '修改地址成功', 'address': address_dict})
3、 路由定义
3、修改地址前端逻辑实现
3.1 添加修改地址的标记
- 新增地址和修改地址的交互不同。
- 为了区分用户是新增地址还是修改地址,我们可以选择添加一个变量,作为标记。
- 为了方便得到正在修改的地址信息,我们可以选择展示地址时对应的序号作为标记。
3.2 实现编辑按钮对应的事件
七、 删除收货地址
1、修改地址接口设计和定义
1.1 修改地址请求方式:
选项
|
方案
|
请求方法
|
DELETE
|
请求地址
|
/addresses/(?P<address_id>\d+)/
|
1.2 修改地址请求参数: 路径参数 和 JSON
参数名
|
类型
|
是否必传
|
说明
|
address_id
|
string
|
是
|
要修改的地址ID(路径参数)
|
1.3 修改地址响应结果:JSON
响应结果
|
响应内容
|
code
|
状态码
|
errmsg
|
错误信息
|
2、后端view实现
- 判断用户是否登录
- 获取登录用户对象
- 通过 Address.objects.get(id=address_id) 得到对象,将is_deleted赋值为 True
- 判断登录用户的缺省地址是否是删除对象
- 返回前端操作状态
class UpdateDestoryAddressView(LoginRequiredJSONMixin, View):"""更新收获地址"""def delete(self, request, address_id):passdef delete(self, request, address_id):login_user=request.usertry:address=Address.objects.get(id=address_id,user=login_user)address.is_deleted=Trueaddress.save()if login_user.default_address_id==address.id:login_user.default_address=Nonelogin_user.save()except Exception as e:logger.error(e)return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '删除地址失败'})return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '删除地址成功'})
3、路由定义
- 删除与修改路由一样,只是方法不同,故共用一个路由
4、删除地址前端逻辑实现
- 后端返回成功后,删除address列表中当前序号的元素
- this.addresses.splice(起始序号,要删除的项目数量)
- /static/js/user_center_site.js
八、设置默认地址
1、接口设计和定义
1.1 请求方式:
选项
|
方案
|
请求方法
|
PUT
|
请求地址
|
addresses/(?P<address_id>\d+)/default/
|
1.2 请求参数: 路径参数
参数名
|
类型
|
是否必传
|
说明
|
address_id
|
string
|
是
|
要修改的地址ID(路径参数)
|
1.3 响应结果:JSON
响应结果
|
响应内容
|
code
|
状态码
|
errmsg
|
错误信息
|
2、后端view实现
- 判断用户是否登录
- 获取登录用户对象
- 通过 Address.objects.get(id=address_id,user=登录用户) 得到对象
- 判断登录用户的缺省地址是否是查询到的对象
- 将查询到的对象赋值给登录用户缺省地址,并保存
- 返回前端操作结果
class DefaultAddressView(LoginRequiredJSONMixin,View):"""设置默认地址"""def put(self,request,address_id):login_user=request.usertry:address=Address.objects.get(id=address_id,user=login_user)# if not address:# return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '收货地址数据有误'})login_user.default_address=addresslogin_user.save()except Address.DoesNotExist as e:logger.error(e)return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '设置默认收货地址失败'})return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '设置默认收货地址成功'})
3、路由定义
4、前端逻辑实现
- 默认地址标识实现方法:
- 专门定义了一个变量default_address_id,记录默认地址id
- 每条记录都有一个默认地址标识,只有当此行地址id=default_address_id时,默认地址标只才显示
- 当默认地址返回正确后,将该记录的地址id 赋值给default_address_id
九、修改地址标题
1、接口设计和定义
1.1 请求方式:
选项
|
方案
|
请求方法
|
PUT
|
请求地址
|
addresses/(?P<address_id>\d+)/title/
|
1.2 请求参数: 路径参数 和 JSON
参数名
|
类型
|
是否必传
|
说明
|
address_id
|
string
|
是
|
要修改的地址ID(路径参数)
|
title
|
string
|
是
|
要修改的 title名称
|
1.3 响应结果:JSON
响应结果
|
响应内容
|
code
|
状态码
|
errmsg
|
错误信息
|
2、后端view实现
- 判断用户是否登录
- 获取参数:put 参数在request.body中
- address_id是路径参数,路由解析得到
- json_dict=json.loads(request.body.decode())
- 校验参数
- 获取登录用户对象
- 通过 Address.objects.get(id=address_id,user=登录用户) 得到对象
- 将title的值赋给对象的title,并保存
- 返回前端操作结果
class TitleAddressView(LoginRequiredJSONMixin,View):"""设置地址标题"""def put(self,request,address_id):login_user=request.userjson_dict=json.loads(request.body.decode())if not json_dict or not json_dict["title"]:return http.HttpResponseForbidden('缺少必传参数')try:address=Address.objects.get(user=login_user,id=address_id)address.title=json_dict["title"]address.save()except Address.DoesNotExist as e:logger.error(e)return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '收货地址标题保存失败'})return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '收货地址标题保存成功'})
3、路由定义
4、前端逻辑实现
- 定义一个变量edit_title_index,用于控制编辑框显示
- 每个地址信息上方都有编辑框和按钮,只有当edit_title_index=当前idex时,才会显示
4.1 用超链接点击事件,显示编辑框及按钮
4.2 编辑框及保存取消按钮显示
4.2 取消和保存处理
Django项目实践(商城):十一、收货地址相关推荐
- Django项目(五)收货地址的实现
一.省市区地址查询 首先分析数据模型类的设计 设计成3张表肯定是不合理的,由于省,市,区具有共性,我们将其设置成一张表 id,name,parent 省的parent设置为null,市的parent设 ...
- Django 19购物商城项目(收货地址:添加、修改)
dDjango 19购物商城项目 1.新建axf_addr,收货地址表 2.路由 3.cart页面,添加默认收货地址 4.视图(主要修改了cart.新建了收货地址相关方法) 5.收货地址列表 6.收货 ...
- 设置默认收货地址【项目 商城】
设置默认收货地址[项目 商城] 设置默认收货地址 1. 持久层 1.1 规划SQL语句 1.2 设计抽象方法 1.3 配置SQL映射 测试 2.业务层 2.1 异常规划 2.2 抽象方法 2.3 实现 ...
- DRF 商城项目 - 用户操作(收藏, 留言, 收货地址)
个人收藏 整体逻辑类似于 个人中心 ( 个人中心的相关逻辑梳理详情 点击这里 ) 也是两个序列化组价的分流 查看收藏 ( list ) 详情指向 收藏详情 的组价 创建收藏 ( create ) ...
- 39 Flutter仿京东商城项目 收货地址列表、增加 修改收货地址布局、弹出省市区选择器...
加群452892873 下载对应39课文件,运行方法,建好项目,直接替换lib目录 pubspec.yaml city_pickers: ^0.1.22 AddressAdd.dart import ...
- 美多商城之用户中心(收货地址1)
三.收货地址 用户地址的主要业务逻辑有: 展示省市区数据 用户地址的增删改查处理 设置默认地址 设置地址标题 3.1 省市区三级联动 1. 展示收货地址界面 提示: 省市区数据是在收货地址界面展示的, ...
- web电商、商城pc端、商城、购物车、订单、线上支付、web商城、pc商城、登录注册、人工客服、收货地址、现金券、优惠券、礼品卡、团购订单、评价晒单、消息通知、电子产品商城、手机商城、电脑商城
web电商.商城pc端.商城.购物车.订单.线上支付.web商城.pc商城.登录注册.人工客服.收货地址.现金券.优惠券.礼品卡.团购订单.评价晒单.消息通知.电子产品商城.手机商城.电脑商城 Axu ...
- UNIAPP实战项目笔记43 购物车页面修改收货地址和修改默认地址
UNIAPP实战项目笔记43 购物车页面修改收货地址和修改默认地址 实际案例图片 修改收货地址和修改默认地址页面布局和功能 具体内容图片自己替换哈,随便找了个图片的做示例 用到了vuex的状态机,具体 ...
- php商城手机端省市显示,jQuery仿手机京东商城收货地址城市选择
jQuery仿手机京东商城收货地址城市选择 js代码 /** * 默认调用 */ !function () { var $target = $('#J_Address'); $target.cityS ...
最新文章
- 网站设计支离破碎:究竟是谁之过
- VC中CListCtrl中的LVCOLUMN和LVITEM详细介绍
- 【PAT乙级】1009 说反话 (20 分)
- ViewModelBase ObservableObject
- Canvas绘制星球轨迹移动
- 如何在 SAP 电商云 Spartacus UI 里新建一个页面
- 6. 堪比JMeter的.Net压测工具 - Crank 实战篇 - 收集诊断跟踪信息与如何分析瓶颈
- 从头编写 asp.net core 2.0 web api 基础框架 (2)
- 论文写作思路_2018年的16个写作思路
- div+css 固定宽度且居中 文字左对齐
- 利用VS2010进行SQL Server服务器和本地的数据融合
- 数学模型的相关概念及意义等理论内容
- SPSS实战:单因素方差分析(ANOVA)
- ecshop模板支持php,ecshop模板支持php数据运算的代码实例
- 7-10 学生成绩排序 (15 分)
- 网页视频倍速播放的方法
- ffmpeg截取视频内容和批量处理视频
- 竟然如愿让我拿到诸多大厂offer(头条,PDD,Alibaba)-来自Alibaba的Java面试指南,
- moodle 1.9 课程 恢复 2.0
- 安卓(android)6.0高通平台下设备树专题视频讲解【全国独家+设备树视频教程】
热门文章
- java jdk安装失败 mac_Mac环境下JDK安装方法
- ASP.NET债务管理系统源码
- MySQL MySQL进阶路:从小工到专家的必读书籍和必备工具
- Jflash 命令行 烧录程序 注意事项
- 英飞凌ADS编译器汉化
- java web网上商城项目实战与源码
- Linux编译时如何减小so库文件的大小
- Dialog(对话框窗口)
- 非常好用的有道词典 For Alfred
- jQuery入门jQuery API-1