哪个购物网站最便宜,html5开发软件,网上服务平台官网入口,wordpress手机 typechoSpringboot写电商系统#xff08;2#xff09; 1.新增收货地址1.创建t_addresss数据库表2.创建Address实体类3.数据库操作的持久层1.接口写抽象方法2.xml写方法映射sql3.测试 4.前后数据交互的业务层1.sql操作的异常抛出2.交互方法的接口定义3.接口的方法实现4.测试 5.与前端… Springboot写电商系统2 1.新增收货地址1.创建t_addresss数据库表2.创建Address实体类3.数据库操作的持久层1.接口写抽象方法2.xml写方法映射sql3.测试 4.前后数据交互的业务层1.sql操作的异常抛出2.交互方法的接口定义3.接口的方法实现4.测试 5.与前端交互的控制层1.添加请求成功的异常响应2.处理请求 6.前端ajax请求7.后端获取省市区三级菜单1.创建地址表2.创建实体类3.sql持久层4.前后端数据操作的业务层5.响应前端的控制层6.前端请求 2.删除收货地址1.持久层2.业务层3.控制层4.前端请求和渲染 3.显示购物车列表1.VO实体类2.持久层3.业务层4.控制层5.前端请求与渲染 4.补充知识点 1.新增收货地址
1.创建t_addresss数据库表
name是关键字,所以需要用
CREATE TABLE t_address (aid INT AUTO_INCREMENT COMMENT 收货地址id,uid INT COMMENT 归属的用户id,name VARCHAR(20) COMMENT 收货人姓名,province_name VARCHAR(15) COMMENT 省-名称,province_code CHAR(6) COMMENT 省-行政代号,city_name VARCHAR(15) COMMENT 市-名称,city_code CHAR(6) COMMENT 市-行政代号,area_name VARCHAR(15) COMMENT 区-名称,area_code CHAR(6) COMMENT 区-行政代号,zip CHAR(6) COMMENT 邮政编码,address VARCHAR(50) COMMENT 详细地址,phone VARCHAR(20) COMMENT 手机,tel VARCHAR(20) COMMENT 固话,tag VARCHAR(6) COMMENT 标签,is_default INT COMMENT 是否默认0-不默认1-默认,created_user VARCHAR(20) COMMENT 创建人,created_time DATETIME COMMENT 创建时间,modified_user VARCHAR(20) COMMENT 修改人,modified_time DATETIME COMMENT 修改时间,PRIMARY KEY (aid)
) ENGINEINNODB DEFAULT CHARSETutf8;2.创建Address实体类
记得要继承BaseEntity类里面有四个字段。 把除了BaseEntity里面的四个字段外的所有字段在这个实体类里添加然后创建他们的getsetequals和hashCodetoString。 实体类的字段名要符合驼峰命名所以会有和数据库字段名不一样的记得要在持久层做字段名的映射。
/**收货地址额实体类*/
public class Address extends BaseEntity {private Integer aid;private Integer uid;private String name;private String provinceName;private String provinceCode;private String cityName;private String cityCode;private String areaName;private String areaCode;private String zip;private String address;private String phone;private String tel;private String tag;private Integer isDefault;/*** get,set* equals和hashCode* toString*/
}3.数据库操作的持久层
新增收获地址之前要判断一下这个uid用户的收货地址是否已经有20个了如果是就不能再新增如果是0那么用户添加的第一个就要默认地址赋值1如果是1-20那默认地址就要赋值0。所以这里面设计到2个SQL语句
insert into t_address (aid以外的所有字段) values (字段值)
select count(*) from t_address where uid?1.接口写抽象方法
在接口AddressMapper里面写上面两个SQL语句的抽象方法第一个传递参数address第二个传递uid。
public interface AddressMapper {Integer insert (Address address);Integer countByUid(Integer uid);
}2.xml写方法映射sql
在AddressMapper.xml里面首先写xml对mybatis和上面接口的映射。
?xml version1.0 encodingUTF-8 ?
!DOCTYPE mapperPUBLIC -//mybatis.org//DTD Mapper 3.0//ENhttp://mybatis.org/dtd/mybatis-3-mapper.dtd
mapper namespacecom.example.store.mapper.AddressMapper
/mapper然后在resultMap标签里面写表字段和实体字段的映射这里写名字不一样的字段和自增的主键字段aid,type写实体类的全路径。 resultMap idAddressEntityMap typecom.example.store.entity.Addressid columnaid propertyaid/result columnprovince_name propertyprovinceName/result columnprovince_code propertyprovinceCode/result columncity_name propertycityName/result columncity_code propertycityCode/result columnarea_name propertyareaName/result columnarea_code propertyareaCode/result columnis_default propertyisDefault/result columncreated_user propertycreatedUser/result columncreated_time propertycreatedTime/result columnmodified_user propertymodifiedUser/result columnmodified_time propertymodifiedTime//resultMap最后写上面2个方法的sql映射id对应接口里定义的方法名有自增主键的要加上useGeneratedKeys和keyProperty定义返回类型的加上resultType。 insert idinsert useGeneratedKeystrue keyPropertyaidINSERT INTO t_address (uid, name, province_name, province_code, city_name, city_code, area_name, area_code, zip,address, phone, tel,tag, is_default, created_user, created_time, modified_user, modified_time) VALUES (#{uid}, #{name}, #{provinceName}, #{provinceCode}, #{cityName}, #{cityCode}, #{areaName},#{areaCode}, #{zip}, #{address}, #{phone}, #{tel}, #{tag}, #{isDefault}, #{createdUser},#{createdTime}, #{modifiedUser}, #{modifiedTime})/insert!--resultTypejava.lang.Integer不写会报错,因为Integer不是基本数据类型--select idcountByUid resultTypejava.lang.Integerselect count(*) from t_address where uid#{uid}/select3.测试
首先给AddressMapperTests添加两个注解一个是声明是测试类最终不会打包的SpringBootTest一个是可以运行的测试类RunWith(SpringRunner.class)然后通过Autowired自动装配Mapper就可以使用mapper里面的方法了。
SpringBootTest
RunWith(SpringRunner.class)
public class AddressMapperTests {Autowiredprivate AddressMapper addressMapper;Testpublic void insert(){Address addressnew Address();address.setUid(1);address.setName(zoe);address.setAddress(Los Angeles);addressMapper.insert(address);}Testpublic void countByUid(){Integer countaddressMapper.countByUid(1);System.err.println(count);}
}4.前后数据交互的业务层
拿到前端数据进行数据插入时会出现错误uid的用户不存在这个异常UsernameNotFoundException已存在。 当用户插入数据超过20条时不可以再添加所以需要添加异常AddressCountLimitException。 插入操作中出现的异常InsertException已存在。
1.sql操作的异常抛出
需要添加一个AddressCountLimitException异常并继承ServiceException 然后重构5个构造方法。
public class AddressCountLimitException extends ServiceException {/**重写ServiceException的所有构造方法*/
}2.交互方法的接口定义
在IAddressService 接口里面定义新增地址的方法addNewAddress这里面从前端拿到的表单信息是address这里面有name是收货人然后从session中拿到uid和username这个将是修改人和创建人。
public interface IAddressService {void addNewAddress(Integer uid, String username, Address address);
}3.接口的方法实现
首先要给这个接口实现类添加注解Service之后spring才能找到这个bean。 在AddressServiceImpl里面实现接口方法。业务层里面将会用到持久层里面定义的sql方法所以将用到方法的mapper接口导入并用Autowired自动装载。 这里用到了一个超参用户收获地址的最大条数在application.properties里面设置user.address.max-count20然后在类里面通过注解将值赋值到变量maxCount中。
Service//交给spring管理,所以需要在类上加Service
public class AddressServiceImpl implements IAddressService {Autowiredprivate AddressMapper addressMapper;Autowiredprivate UserMapper userMapper;Value(${user.address.max-count})private Integer maxCount;Overridepublic void addNewAddress(Integer uid, String username, Address address) {User resultuserMapper.findByUid(uid);if(resultnull||result.getIsDelete()1){throw new UsernameNotFoundException(用户数据不存在);}Integer countaddressMapper.countByUid(uid);if(countmaxCount){throw new AddressCountLimitException(用户收货地址超出上限);}Integer isDefaultcount0?1:0;address.setIsDefault(isDefault);address.setUid(uid);address.setCreatedUser(username);address.setModifiedUser(username);address.setCreatedTime(new Date());address.setModifiedTime(new Date());Integer rowsaddressMapper.insert(address);if (rows!1){throw new InsertException(插入用户收获地址时发生未知异常);}}
}4.测试
还是先添加测试的两个注解SpringBootTestRunWith(SpringRunner.class)然后自动装载Autowired业务层的接口在测试方法里使用接口方法测试。
SpringBootTest
RunWith(SpringRunner.class)
public class AddressServiceTests {Autowiredprivate IAddressService addressService;Testpublic void addNewAddress(){Address addressnew Address();address.setName(老王);addressService.addNewAddress(2,zoe,address);}
}5.与前端交互的控制层
1.添加请求成功的异常响应
首先在业务层的时候有一个异常抛出所以在控制层里面要把这个异常添加到响应的异常里面在BaseController里添加
else if (e instanceof AddressCountLimitException) {result.setState(4003);result.setMessage(用户的收货地址超出上限的异常);
}2.处理请求
首先AddressController要继承BaseController。 然后添加注解RequestMapping(addresses)RestController用到了业务层的接口所以用Autowired自动装配添加功能的响应地址RequestMapping(add_new_address)然后拿到前端数据进行处理。
RequestMapping(addresses)
RestController
public class AddressController extends BaseController{Autowiredprivate IAddressService addressService;RequestMapping(add_new_address)public JsonResultVoidaddNewAddress(Address address, HttpSession session){Integer uidgetUidFromSession(session);String usernamegetUsernameFromSession(session);addressService.addNewAddress(uid,username,address);return new JsonResult(OK);}
}浏览器中输入http://localhost:8080/addresses/add_new_address?nametomphone987进行测试。
6.前端ajax请求 script$(#btn-add-new-address).click(function () {$.ajax({url: /addresses/add_new_address,type: POST,data: $(#form-add-new-address).serialize(),dataType: JSON,success: function (json) {if (json.state 200) {alert(新增收货地址成功)} else {alert(新增收货地址失败)}},error: function (xhr) {alert(新增收货地址时产生未知的异常!xhr.message);}});});/script然后就可以提交表单进行测试。
7.后端获取省市区三级菜单
现在的三级菜单功能是写在了前端的js里面但实际业务中是要把这些数据写在数据库里面的所以现在需要实现这个功能首先把前端这个js代码删除掉
script typetext/javascript src../js/distpicker.data.js/script
script typetext/javascript src../js/distpicker.js/script然后开始实现这个功能这里前端用户选择省市区之后有一个联动效果这个就是通过父code查到子name的操作。 还有一个就是用户选择省市区之后传递给后端的其实是一个code要补全数据库内容的话还需要有一个自己code查自己name的操作。所以这里的sql将是两个操作。
1.创建地址表
parent代表父区域的代码号code代表自身的代码号省的父代码号是86,代表中国
CREATE TABLE t_dict_district (id INT(11) NOT NULL AUTO_INCREMENT,parent VARCHAR(6) DEFAULT NULL,code VARCHAR(6) DEFAULT NULL,name VARCHAR(16) DEFAULT NULL,PRIMARY KEY (id)
) ENGINEINNODB DEFAULT CHARSETutf8;然后将这些规定好的数据插入到表中
LOCK TABLES t_dict_district WRITE;
INSERT INTO t_dict_district VALUES (1,110100,110101,东城区),(2,110100,110102,西城区)等等等等;
UNLOCK TABLES;2.创建实体类
这里就不用再继承了因为这个表里面没有那四个是字段这个表只是用来查询父子地址的。 因为没有继承BaseEntity所以需要实现接口Serializable序列化。 添加表字段之后添加get,set equals和hashCode toString方法。
public class District implements Serializable {private Integer id;private String parent;private String code;private String name;
}3.sql持久层
为了之后这个功能的复用将这个新建持久层DistrictMapper接口然后写mapper接口和sql映射
public interface DistrictMapper {ListDistrict findByParent(String parent);String findNameByCode(String code);
}
?xml version1.0 encodingUTF-8 ?
!DOCTYPE mapperPUBLIC -//mybatis.org//DTD Mapper 3.0//ENhttp://mybatis.org/dtd/mybatis-3-mapper.dtd
mapper namespacecom.example.store.mapper.DistrictMapperselect idfindByParent resultTypecom.example.store.entity.Districtselect * from t_dict_district where parent#{parent} order by code ASC/selectselect idfindNameByCode resultTypejava.lang.Stringselect name from t_dict_district where code#{code}/select
/mapper测试持久层
SpringBootTest
RunWith(SpringRunner.class)
public class DistrictMapperTests {Autowiredprivate DistrictMapper districtMapper;Testpublic void findByParent(){ListDistrict listdistrictMapper.findByParent(86);for (District d:list){System.out.println(d);}}Testpublic void findNameByCode(){String name districtMapper.findNameByCode(610000);System.out.println(name);}
}4.前后端数据操作的业务层
首先查询的时候不会有什么异常所以直接定义抽象接口方法然后实现方法。从前端拿到parent父的code然后查询并返回数据这里返回数据的时候将每一条子数据的id和parent都置为null利于数据的传输所以每个子数据传到前端的就是这个子的code和name。
public interface IDistrictService {List District findByParent(String parent);String findNameByCode(String code);
}
Service
public class DistrictServiceImpl implements IDistrictService {Autowiredprivate DistrictMapper districtMapper;Overridepublic ListDistrict findByParent(String parent) {ListDistrict listdistrictMapper.findByParent(parent);for (District d :list){d.setId(null);d.setParent(null);}return list;}Overridepublic String findNameByCode(String code) {return districtMapper.findNameByCode(code);}
}测试业务层
SpringBootTest
RunWith(SpringRunner.class)
public class DistrictServiceTests {Autowiredprivate IDistrictService districtService;Testpublic void findByParent(){ListDistrict list districtService.findByParent(86);for (District d:list){System.out.println(d);}}Testpublic void findNameByCode(){String namedistrictService.findNameByCode(610000);System.out.println(name);}
}这里要分开讨论首先是联动选择当前端选择一个父code到后端之后后端需要拿到子code和那么这个和前端交互所以需要写一个controller当前端有动作请求时后端就要判断是不是要用这个控制器来返回给前端子name数据。 但是通过code找到自己的name这个数据不需要传递给前端只需要在后端把这个name数据添加到district对象上然后保存到数据库。所以第二个code找name的方法不用到控制层在Address添加地址的时候把前端传来的code找到name然后添加到数据库即可。所以在AddressServiceImpl新增收货地址的时候用这个服务实现功能
Autowired
private IDistrictService districtService;String provinceNamedistrictService.findNameByCode(address.getProvinceCode());String cityName districtService.findNameByCode(address.getCityCode());String areaName districtService.findNameByCode(address.getAreaCode());address.setProvinceName(provinceName);address.setCityName(cityName);address.setAreaName(areaName);5.响应前端的控制层
首先是没有新的error所以不用处理异常直接继承BaseConrtroller即可然后开始处理响应
RequestMapping(districts)
RestController
public class DistrictController extends BaseController {Autowiredprivate IDistrictService districtService;RequestMapping({/,})public JsonResultListDistrict findByParent(String parent){ListDistrict listdistrictService.findByParent(parent);return new JsonResult(OK,list);}
}输入http://localhost:8080/districts?parent86网址进行测试。
6.前端请求
进入页面显示省的列表加载然后省的组件有改变的话就向后端发送请求市同理获取第三级数据。
var defaultOptionoption value0-----请选择-----/option;// option标签并不会把内容发送到后端,而是将value值发送给后端,所以用value表示当前这个区域的code值$(document).ready(function () {showProvinceList();//调用这个方法给省列表数据$(#province-list).append(defaultOption);//将省,市,县的下拉列表内容设为-----请选择-----$(#city-list).append(defaultOption);$(#area-list).append(defaultOption);});function showProvinceList() {$.ajax({url: /districts,type: POST,data: parent86,//发送请求用于获取所有省对象,一级菜单dataType: JSON,success: function (json) {if (json.state 200) {var list json.data;//获取所有省对象的List集合for (var i 0; i list.length; i) {var opt option valuelist[i].codelist[i].name/option;$(#province-list).append(opt);}} else {alert(省/直辖区的信息加载失败)}}});}$(#province-list).change(function () {//省组件有改变先获取到省区域父代码号var parent $(#province-list).val();$(#city-list).empty();$(#area-list).empty();$(#city-list).append(defaultOption);$(#area-list).append(defaultOption);if (parent 0) {//如果继续程序,后面的ajax接收的json数据中的data是空集合[],进不了for循环,没有任何意义,所以直接在这里终止程序return;}$.ajax({url: /districts,type: POST,data: parentparent,dataType: JSON,success: function (json) {if (json.state 200) {var list json.data;for (var i 0; i list.length; i) {var opt option valuelist[i].codelist[i].name/option;$(#city-list).append(opt);}} else {alert(市的信息加载失败)}}});});$(#city-list).change(function () {var parent $(#city-list).val();$(#area-list).empty();$(#area-list).append(defaultOption);if (parent 0) {return;}$.ajax({url: /districts,type: POST,data: parentparent,dataType: JSON,success: function (json) {if (json.state 200) {var list json.data;for (var i 0; i list.length; i) {var opt option valuelist[i].codelist[i].name/option;$(#area-list).append(opt);}} else {alert(县的信息加载失败)}}});});2.删除收货地址
设为默认地址和删除收货地址有很多相似之处我就只写一下删除吧。首先是sql语句的设计。
1.持久层
删除的话就是通过aid删除数据但如果删除的数据是默认地址通过getIsDefault拿到真否那么就要得到modeified_time最近的那一条数据作为默认拿到之后设为默认设默认这个功能我已经实现了可以自己写一下 那么些接口和映射文件limit 0,1表示查询到的第一条数据(limit (n-1),pageSize),这样查询后就只会获得第一条数据。 select 整个数据时一定要有resultMapAddressEntityMap对实体和表进行映射。
Integer deleteByAid(Integer aid);
Address findLastModified(Integer uid);
delete iddeleteByAiddelete from t_address where aid#{aid}
/delete
select idfindLastModified resultMapAddressEntityMapselect * from t_address where uid#{uid} order by modified_time DESC limit 0,1
/select2.业务层
这里sql执行会出现的异常包括没有该条地址数据(已开发)地址数据归属错误(已开发)和删除的时候可能会产生未知的异常DeleteException 。
public class DeleteException extends ServiceException{/**重写ServiceException的所有构造方法*/
}然后实现delete这个功能
void delete(Integer aid,Integer uid,String username);
Overridepublic void delete(Integer aid, Integer uid, String username) {Address resultaddressMapper.findByAid(aid);if (result null) {throw new AddressNotFoundException(收货地址数据不存在);}if (!result.getUid().equals(uid)) {throw new AccessDeniedException(非法数据访问);}Integer rows addressMapper.deleteByAid(aid);if (rows ! 1) {throw new DeleteException(删除数据时产生未知的异常);}if (result.getIsDefault() 0) {return;}Integer count addressMapper.countByUid(uid);if (count 0) {return;}Address address addressMapper.findLastModified(uid);rows addressMapper.updateDefaultByAid(address.getAid(), username, new Date());if (rows ! 1) {throw new UpdateException(更新数据时产生未知的异常);}}3.控制层
添加delete的异常到BaseController
else if (e instanceof DeleteException) {result.setState(5002);result.setMessage(删除数据时产生未知的异常);
}然后是后端响应/addresses/{aid}/set_default(以前的数据是通过表单直接提交的,还有一种提交方式就是RestFul风格,这种提交方式可以提交更多的数据,这里用这个提交方式)。 RestFul编写时不管参数名和占位符是否一致都必须加PathVariable(“aid”)
RequestMapping({aid}/delete)
public JsonResultVoid delete(PathVariable(aid) Integer aid,HttpSession session) {addressService.delete(aid,getUidFromSession(session),getUsernameFromSession(session));return new JsonResult(OK);
}4.前端请求和渲染
给显示列表的删除按钮添加onclick属性并指向deleteByAid(aid)方法。
tda onclickdelete_(#{aid}) classbtn btn-xs add-del btn-infospan classfa fa-trash-o/span 删除/a/td在显示列表的for循环中为占位符aid赋值。
tr tr.replace(#{aid},list[i].aid);然后写点击了删除按钮和触发的delete_函数这里不能写delete因为这是一个关键字。
function delete_(aid) {$.ajax({url: /addresses/aid/delete,type: POST,dataType: JSON,success: function (json) {if (json.state 200) {//重新加载收货地址列表页面showAddressList();} else {alert(删除收货地址失败)}},error: function (xhr) {alert(删除收货地址时产生未知的异常!xhr.message);}});}3.显示购物车列表
这里涉及到一个多表查询用到了cart和product两个表的信息所以表不用建但是这两个表组成的数据是什么类型的实体呢即不是product也不是cart所以在store包下创建一个vo包,在该包下面创建CartVO类,不需要继承BaseController类,那相应的就需要单独实现Serializable接口。 VO全称Value Object,值对象。当进行select查询时,查询的结果属于多张表中的内容,此时发现结果集不能直接使用某个POJO实体类来接收,因为POJO实体类不能包含多表查询出来的信息,解决方式是:重新去构建一个新的对象,这个对象用于存储所查询出来的结果集对应的映射,所以把这个对象称之为值对象。
1.VO实体类
首先分析前端需要的数据都有哪些哪些是cart表里的数据哪些是product表里的数据最后在生成他们的getsetequal hash和tostring
public class CartVO implements Serializable {private Integer cid;//购物车页面的勾选功能要有cidprivate Integer uid;//要拿到某个用户的购物车信息要有uidprivate Integer pid;//购物车操作后要更新product表里面的库存等信息所以要有pidprivate Long price;//这个价格是用户加入购物车时的商品价格private Integer num;//这个num是购物车里这个用户这个商品的数量和product里的num不一样库存量private String title;//这个是product表里的信息要展示在购物车列表的前端private Long realPrice;//这个是product表里的价格private String image;//这个是product表里的信息要展示在购物车列表的前端
}2.持久层
首先在CartMapper接口中定义方法这个方法是通过uid查询到上面vo实体数据然后两表关联的时候不希望有null数据所以以cart表作为主表左连接查询此用户的vo信息。
ListCartVO findVoByUid(Integer uid);
select idfindVoByUid resultTypecom.example.store.vo.CartVOselect cid,uid,pid,t_cart.price,t_cart.num,title,t_product.price as realPrice,imagefrom t_cart left join t_product on t_cart.pidt_product.idwhere uid#{uid}order by t_cart.created_time desc/select3.业务层
ListCartVO getVOByUid(Integer uid);
Overridepublic ListCartVO getVOByUid(Integer uid) {return cartMapper.findVoByUid(uid);}4.控制层
RequestMapping({, /})
public JsonResultListCartVO getVOByUid(HttpSession session) {ListCartVO data cartService.getVOByUid(getUidFromSession(session));return new JsonResultListCartVO(OK, data);
}5.前端请求与渲染
script typetext/javascript$(document).ready(function() {showCartList();});//展示购物车列表数据function showCartList() {$(#cart-list).empty();$.ajax({url: /carts,type: GET,dataType: JSON,success: function(json) {var list json.data;for (var i 0; i list.length; i) {var tr tr\n td\n input namecids value#{cid} typecheckbox classckitem /\n /td\n tdimg src..#{image}collect.png classimg-responsive //td\n td#{title}#{msg}/td\n td¥span idgoodsPrice#{cid}#{singlePrice}/span/td\n td\n input typebutton value- classnum-btn οnclickreduceNum(1) /\n input idgoodsCount#{cid} typetext size2 readonlyreadonly classnum-text value#{num}\n input classnum-btn typebutton value οnclickaddNum(#{cid}) /\n /td\n tdspan idgoodsCast#{cid}#{totalPrice}/span/td\n td\n input typebutton οnclickdelCartItem(this) classcart-del btn btn-default btn-xs value删除 /\n /td\n /tr;tr tr.replaceAll(/#{cid}/g, list[i].cid);tr tr.replaceAll(/#{image}/g, list[i].image);tr tr.replaceAll(/#{title}/g, list[i].title);tr tr.replaceAll(/#{singlePrice}/g, list[i].realPrice);tr tr.replaceAll(/#{num}/g, list[i].num);tr tr.replaceAll(/#{totalPrice}/g, list[i].realPrice * list[i].num);if (list[i].realPrice list[i].price) {tr tr.replace(/#{msg}/g, 比加入时降价 (list[i].price - list[i].realPrice) 元);} else {tr tr.replace(/#{msg}/g, );}$(#cart-list).append(tr);}},error: function (xhr) {alert(加载购物车列表数据时产生未知的异常xhr.status);}});}
/script4.补充知识点
1.当从url中拿数据传递给后端时在ajax请求里的data可以用下面的方式
data:location.search.substring(1),// URL 中的 ? 及之后的部分1就是从下标为1的开始相当于把去掉了