效果图:
代码实现:
1.js文件
#####(1)商品列表模块
import Utils from \"./Utils.js\";export default class GoodsItem {//设置静态变量styleBool,用来控制多次循环,CSS只设置一次static styleBool = false;data; //将外部传入的数组的item数据赋给data属性preIcon; //miniicon内目标图片constructor() {this.elem = this.createElem();//首先判断 循环创建的div是否添加CSS样式,当不存在时,执行添加CSS函数if (!GoodsItem.styleBool) GoodsItem.setStyle();//将添加HTML标签this.renderHTML();}//创建商品容器createElem() {if (this.elem) return this.elem;var div = Utils.ce(\"div\");div.className = \"goodsItem\";return div;}//添加到传入的父元素内appendTo(parent) {if (typeof parent === \"string\") parent = document.querySelector(parent);parent.appendChild(this.elem);}//给新创建的标签元素添加CSS属性setData(_data) {this.data = _data;this.icon = this.elem.querySelector(\".icon\");let presell = this.elem.querySelector(\".presell\");this.miniIconCon = this.elem.querySelector(\".miniIconCon\");var price = this.elem.querySelector(\".price\");var infoCon = this.elem.querySelector(\".infoCon\");var evaluate = this.elem.querySelector(\".evaluate\");var shopCon = this.elem.querySelector(\".shopCon\");var tagCon = this.elem.querySelector(\".tagCon\");//如果item内presell不是空字符串if (this.data.presell.trim().length > 0) {//在presel标签里添加内容,内容为传入数组内presell.textContent = this.data.presell;} else {//如果presell是空字符串,隐藏presell标签presell.style.display = \"none\"}//为添加miniicon里面的图片设置字符串var miniIconStr = \"\";//遍历item内miniIcon内的每一项this.data.miniIcon.forEach(item => {//将miniIcon内每一项图片路径转换为图片标签miniIconStr += `<img src=${item} class=\"miniIcon\">`})//将添加标签的字符串加入到miniIcon内,生成图片标签this.miniIconCon.innerHTML = miniIconStr;//给每一项小标签添加鼠标滑出事件this.miniIconCon.addEventListener(\"mouseover\", e => this.iconMouseHandler(e))//先给icon设置默认值 默认第一张图片this.changeIcon(0);//价格标签内填入item对象price内容price.textContent = this.data.price.toFixed(2);//信息标签内填入item对象info内容infoCon.textContent = this.data.info//评价标签内填入item对象evaluate内容evaluate.textContent = this.data.evaluate;//店铺标签内填入item对象shopName内容shopCon.textContent = this.data.shopName;//根据dic对象内key的名称对应item对象内tag数组内的字符串匹配,匹配成功的给该标签设置对应的class name,从而拥有CSS样式var dic={\"自营\":\"goods_icon_0\",\"放心购\":\"goods_icon_1\",\"本地仓\":\"goods_icon_0\",\"赠\":\"goods_icon_2\",\"京东物流\":\"goods_icon_2\",\"秒杀\":\"goods_icon_2\",\"免邮\":\"goods_icon_2\",\"险\":\"goods_icon_3\",\"券\":\"goods_icon_3\",}var tagStr=\"\";//匹配item里tag内容和dic内prop内容,匹配成功的 赋给classthis.data.tag.forEach(item=>{for(let prop in dic){if(item.indexOf(prop)>=0)tagStr+=`<span class=\'${dic[prop]}\'>${item}</span>`;}});tagCon.innerHTML=tagStr;}iconMouseHandler(e) {//如果目标事件不是图片类型,直接返回if (e.target.constructor !== HTMLImageElement) return;//匹配目标事件为侦听事件的哪一个子元素 将找到的下标赋给index。var index = Array.from(e.currentTarget.children).indexOf(e.target);this.changeIcon(index);}changeIcon(index) {//判断preIcon是否存在if (this.preIcon) {//如果存在,设置preIcon 的边框this.preIcon.style.border = \"1px solid #CCCCCC\";}//如果不存在 ,设置icon内图片的路径为miniicon对应的大图片this.icon.src = this.data.icon[index];//然后给preIcon赋值为miniIcon内图片的下标this.preIcon = this.miniIconCon.children[index];//给miniicon内目标事件图片设置红色边框this.preIcon.style.border = \"2px solid #e4393c\";}renderHTML() {this.elem.innerHTML = `<div class=\"iconCon\"><img class=\"icon\"><div class=\"presell\"></div><div class=\"miniIconCon\"></div><div class=\"priceCon\"><span class=\"money\">¥</span><span class=\"price\"></span></div><a class=\"infoCon\" href=\"#\"></a><div class=\"evaluateCon\"><span class=\"evaluate\"></span>条评价</div><div class=\"shopCon\"></div><div class=\"tagCon\"></div>`}static setStyle() {GoodsItem.styleBool = true;Utils.setStyle({\".goodsItem\": {width: \"240px\",height: \"466px\",float: \"left\",margin: \"5px\"},\".goodsItem:hover\": {boxShadow: \"0px 0px 4px #999999\"},\".iconCon\": {width: \"220px\",height: \"220px\",marginBottom: \"5px\",position: \"relative\",margin: \"auto\",left: 0,right: 0,},\".presell\": {width: \"200px\",height: \"25px\",position: \"absolute\",bottom: \"0px\",backgroundColor: \"rgba(0,0,0,0.4)\",fontSize: \"12px\",color: \"#FFFFFF\",paddingLeft: \"20px\",lineHeight: \"25px\"},\".miniIcon\": {width: \"25px\",height: \"25px\",// border:\"2px solid #e4393c00\",border: \"1px solid #CCCCCC\",marginRight: \"5px\"},\".priceCon\": {width: \"220px\",height: \"22px\",color: \"#e4393c\",fontSize: \"20px\",marginTop:\"5px\",},\".money\": {fontSize: \"16px\",},\".price\": {marginLeft: \"-10px\"},\".infoCon\": {width: \"220px\",height: \"40px\",wordWrap: \"break-word\",overflow: \"hidden\",display: \"block\",fontSize: \"12px\",color: \"#333333\",lineHeight: \"20px\",marginTop: \"10px\",textDecoration: \"none\"},\".evaluateCon\": {width: \"220px\",height: \"18px\",fontSize: \"12px\",color: \"#333333\",marginTop: \"5px\"},\".evaluate\": {color: \"#646FB0\",fontWeight: \"600\",},\".shopCon\": {fontSize: \"12px\",marginTop: \"5px\",color: \"#AAAAAA\",overflow: \"hidden\",whiteSpace: \"nowrap\",textOverflow: \"ellipsis\",width: \"122px\",height: \"18px\"},\".goods_icon_0\": {float: \"left\",height: \"16px\",lineHeight: \"16px\",padding: \"0 3px\",marginRight: \"3px\",overflow: \"hidden\",textAlign: \"center\",fontStyle: \"normal\",fontSize: \"12px\",fontFamily: \'\"Helvetica Neue\",\"Hiragino Sans GB\",SimSun,serif\',background: \"#e23a3a\",color: \"#FFF\",cursor: \"default\",borderRadius: \"2px\",},\".goods_icon_1\": {border: \"1px solid #e23a3a\",borderColor: \"#4d88ff\",color: \"#4d88ff\",float: \"left\",height: \"14px\",lineHeight: \"14px\",padding: \"0 3px\",marginRight: \"3px\",overflow: \"hidden\",textAlign: \"center\",fontStyle: \"normal\",fontSize: \"12px\",fontFamily: \'\"Helvetica Neue\",\"Hiragino Sans GB\",SimSun,serif\"\',borderRadius: \"2px\",},\".goods_icon_2\": {float: \'left\',height: \'14px\',lineHeight: \'14px\',padding: \'0 3px\',border: \'1px solid #e23a3a\',marginRight: \'3px\',overflow: \'hidden\',textAlign: \'center\',fontStyle: \'normal\',fontSize: \'12px\',fontFamily: \'\"Helvetica Neue\",\"Hiragino Sans GB\",SimSun,serif\',borderRadius: \'2px\',color: \'#e23a3a\',},\".goods_icon_3\": {float: \'left\',height: \'16px\',lineHeight: \'16px\',padding: \'0 3px\',marginRight: \'3px\',overflow: \'hidden\',textAlign: \'center\',fontStyle: \'normal\',fontSize: \'12px\',fontFamily: \'\"Helvetica Neue\",\"Hiragino Sans GB\",SimSun,serif\',background: \'#e23a3a\',color: \'#FFF\',cursor: \'default\',borderRadius: \'2px\',background: \"#4b9bfc\",},\".tagCon\":{marginTop:\"10px\"}})}}
(2)选择框模块
export default class CheckBox extends EventTarget{elem;label; //接收外部以参数传入的lablechecked=false; //是否选中//创建多选框constructor(_data,_label){super();//将外部传入的参数赋给全局属性this.data=_data;this.label=_label;this.elem=this.createElem();}//创建多选框容器createElem(){if(this.elem) return this.elem;let div=document.createElement(\"div\");div.style.float=\"left\";div.style.marginRight=\"5px\";div.style.marginTop=\"4px\";div.style.position=\"relative\";let icon=document.createElement(\"span\");Object.assign(icon.style,{width:\"13px\",height:\"13px\",position:\"relative\",display:\"inline-block\",marginRight:\"3px\",border:\"1px solid #666666\",borderRadius:\"3px\",});//创建多选框选中后对号let a=document.createElement(\"div\");Object.assign(a.style,{width: \"3px\",height: \"6px\",marginLeft:\"4px\",marginTop:\"1px\",borderColor: \"#FFFFFF\",borderStyle: \"solid\",borderWidth: \"0 2px 3px 0\",transform: \"rotate(45deg)\",})icon.appendChild(a)div.appendChild(icon);//创建多选框后面的文本容器let labelSpan=document.createElement(\"span\");labelSpan.textContent=this.label;labelSpan.style.userSelect=\"none\";labelSpan.style.position=\"relative\"div.appendChild(labelSpan);//div设置点击事件div.addEventListener(\"click\",e=>this.clickHandler(e));//div设置鼠标滑入事件div.addEventListener(\"mouseover\",e=>this.mouseHandler(e));//div设置鼠标滑出事件div.addEventListener(\"mouseout\",e=>this.mouseHandler(e));return div;}//将创建的元素插入到指定父元素内appendTo(parent){if(typeof parent===\"string\") parent=document.querySelector(parent);parent.appendChild(this.elem);}//将元素插入到指定的父元素内,且在指定子元素前面insertTo(parent,elem){if(typeof parent===\"string\") parent=document.querySelector(parent);if(typeof elem===\"string\") elem=document.querySelector(elem);parent.insertBefore(this.elem,elem);}//鼠标点击后执行的函数clickHandler(e){//给复选框按钮设置开关this.checked=!this.checked;//执行setCheck()函数,且将this.checked(即复选框是否选中)作为参数代入到函数内。this.setCheck(this.checked);//创建一个事件名为:change的事件var evt=new Event(\"change\");//将this.checked和this.data赋值给事件 e ,这样就可以把this.checked和this.data抛发出去了。evt.checked=this.checked;evt.data=this.data;//将事件抛发出去this.dispatchEvent(evt);}//点击事件后,具体执行的函数setCheck(_check){//获得传入的参数,并赋值给this.checkedthis.checked=_check;//根据checked的值编辑icon的背景颜色和边框。Object.assign(this.elem.firstElementChild.style,{backgroundColor:this.checked ? \"rgb(54 161 251)\" : \"#FFFFFF\",border:this.checked ? \"1px solid rgb(54 161 251)\" : \"1px solid #666666\"});//编辑多选框内小对号是否出现this.elem.firstElementChild.firstElementChild.display=this.checked ? \"block\" : \"none\"}//设置鼠标滑入滑出事件函数mouseHandler(e){//如果 复选框为选中,即this.checked为trueif(this.checked){//当复选框为选中时,无论鼠标滑入滑出,边框始终为固定值this.elem.firstElementChild.style.borderColor=\"1px solid rgb(54 161 251)\";return;}//当鼠标滑入时,改变边框颜色if(e.type===\"mouseover\"){this.elem.firstElementChild.style.borderColor=\"#aaaaaa\";}//当鼠标滑出时,改变边框颜色else{this.elem.firstElementChild.style.borderColor=\"#666666\";}}}
(3)计数器模块
import Utils from \"./Utils.js\";export default class StepNumber extends EventTarget {leftBn;rightBn;input;ids;step = 1;data;constructor(_data) {super();this.data = _data;this.elem = this.creatElem();}creatElem() {if (this.elem) return this.elem;var div = Utils.ce(\"div\", {width: \"80px\",height: \"22px\",position: \"relative\",});this.leftBn = Utils.ce(\"a\", {width: \"15px\",height: \"20px\",border: \"1px solid #cccccc\",display: \"block\",textDecoration: \"none\",color: \"#333333\",textAlign: \"center\",lineHeight: \"20px\",float: \"left\",backgroundColor: \"#FFFFFF\",});this.href = \"javaScript:void(0)\";this.textContent = \"-\";this.input = Utils.ce(\"input\", {border: \"none\",borderTop: \"1px solid #cccccc\",borderBottom: \"1px solid #cccccc\",textAlign: \"center\",outline: \"none\",float: \"left\",width: \"42px\",height: \"18px\",});this.input.value = \"1\";this.rightBn = this.leftBn.cloneNode(false);this.textContent = \"+\";div.appendChlid(this.leftBn);div.appendChlid(this.input);div.appendChlid(this.righttBn);this.leftBn.addEventListener(\"click\", (e) => this.clickHandler(e));this.rightBn.addEventListener(\"click\", (e) => this.clickHandler(e));this.input.addEventListener(\"input\", (e) => inputHandler(e));return div;}appendTo(parent) {if (typeof parent === \"string\") parent = document.querySelector(parent);parent.appendChild(this.elem);}inputHandler(e) {if (this.ids !== undefined) return;this.ids = setTimeout(() => {clearTimeout(ids);this.ids = undefined;this.setStep(this.input.value, true);});}clickHandler(e) {var bn = e.currentTarget;if (bn.bool) return;if (bn === this.leftBn) {this.step--;} else {this.step++;}this.setStep(this.step, true);}setStep(_step, bool) {if (typeof _step === \"string\") _step = Number(_step.replace(/\\D/g, \"\"));if (_step >= 99) {_step = 99;this.rightBn.style.color = \"#CCCCCC\";this.rightBn.bool = true;}if (_step <= 1) {_step = 1;this.leftBn.style.color = \"#CCCCCC\";this.leftBn.bool = true;} else {this.rightBn.bool = false;this.rightBn.style.color = \"#000000\";this.leftBn.bool = false;this.leftBn.style.color = \"#000000\";}this.step = _step;this.input.value = this.step;if (bool) {var evt = new Event(\"change\");evt.data = this.data;evt.step = this.step;this.dispatchEvent();}}}
(4)购物车模块
import Utils from \"./Utils.js\";import CheckBox from \"./CheckBox注释.js\";import StepNumber from \"./StepNumber注释.js\";export default class Shopping extends EventTarget{//table存放购物车放在的表格table;//设置静态属性styleBoll 存放是否已经创建CSS样式static styleBool=false;//存放表头内容的数组headList=[\"全选\",\"\",\"商品\",\"\",\"单价\",\"数量\",\"小计\",\"操作\"];//创建三个静态属性用于抛发事件名static CHECK_CHANGE=\"check_change_event\";static STEP_CHANGE=\"step_change_event\";static DELETE_CHANGE=\"delete_change_event\";//创建购物车constructor(){super();//如果没有创建CSS样式 ,添加CSS样式if(!Shopping.styleBool) Shopping.setStyle();this.elem=this.createElem();}//创建购物车外部容器createElem(){if(this.elem) return this.elem;return Utils.ce(\"div\");}appendTo(parent){if(typeof parent===\"string\") parent=document.querySelector(parent);parent.appendChild(this.elem);}//根据 参数带入的列表 设置数据,根据数据生成表格setData(list){//如果表格存在,删除表格,用于更新表格if(this.table) this.table.remove();//创建表格,并且给表格元素赋予class名this.table=Utils.ce(\"table\");this.table.className=\"tableClass\";//根据list给表格创建表头this.createHead(list);//根据list给表格创建每一行this.createListTr(list);//将表格 添加进购物车div里this.elem.appendChild(this.table);}//创建表头createHead(list){//创建第一行,并且赋给clssNamevar thr=Utils.ce(\"tr\");thr.className=\"thr\";//根据表头内容长度,创建单元格for(var j=0;j<this.headList.length;j++){//根据需求,将第一列跟第二列合并,此时不创建第二列。if(j===1) continue;//创建单元格var th=Utils.ce(\"th\");//单元格内文本为数组对应的内容th.textContent=this.headList[j];//单元格内元素左对齐th.style.textAlign=\"left\";//设置单元格左填充th.style.paddingLeft=\"15px\";//如果为第一个单元格时if(j===0){//合并第一,第二列th.setAttribute(\"colspan\",\"2\");//第一个单元格设置全选按钮let ck=new CheckBox();//当list内每一项元素内所有的checked 对应的值为true时,bool才为truevar bool=list.every(item=>{return item.checked})//设置全选按钮点击事件函数ck.setCheck(bool);//将ck插入到th内,且在th子元素前面ck.insertTo(th,th.firstChild);//触发change事件时,执行checkHandler函数ck.addEventListener(\"change\",e=>this.checkHandler(e))}//第三个单元格内容居中,且左填充为0if(j===2){th.style.textAlign=\"center\"th.style.paddingLeft=\"0\";}//将单元格插入到表头内thr.appendChild(th);}//将表头插入到表格里this.table.appendChild(thr)}//创建每一行createListTr(list){//根据列表里的数据创建每一行for(var i=0;i<list.length;i++){var tr=Utils.ce(\"tr\");//给tr赋予class名tr.className=\"trs\";//如果是第一行,改变边框if(i===0) tr.style.borderTop=\"2px solid #999999\";//遍历数组内每一个对象,根据信息创建单元格for(var prop in list[i]){if(prop===\"id\") continue;var td=Utils.ce(\"td\");td.style.padding=\"15px 0 10px\";td.style.wordWrap=\"break-word\";//执行createTdContent函数,给单元格赋上属性和添加CSS样式this.createTdContent(td,list[i],prop);tr.appendChild(td);}this.table.appendChild(tr);}}//给单元格添加内容和CSS属性createTdContent(td,data,prop){//根据对象内容,判断单元格内容switch(prop){case \"checked\"://创建单选框let ck=new CheckBox(data);//传入checked此时的值,判断是否选中ck.setCheck(data[prop]);ck.appendTo(td);//设置change事件侦听,获取CheckBox类内抛发的数据ck.addEventListener(\"change\",e=>this.checkHandler(e))td.style.padding=\"0 15px 0 11px\";break;case \"icon\"://创建图片var img=new Image();//从列表中获取图片srcimg.src=data[prop];//给图片赋予CSS样式Object.assign(img.style,{width:\"80px\",height:\"80px\"})td.appendChild(img);break;case \"total\":td.style.fontWeight=\"600\";case \"price\":td.textContent=\"¥\"+data[prop].toFixed(2);breakcase \"deleted\"://创建删除标签var span=Utils.ce(\"span\");span.style.marginLeft=\"10px\";span.textContent=\"删除\";//将data赋值为span的属性span.data=data;td.appendChild(span);//设置点击事件侦听span.addEventListener(\"click\",e=>this.deleteHandler(e));break;case \"num\"://创建数据累加器var step=new StepNumber(data);//将列表内数据以参数的形式传入setStep函数step.setStep(data[prop]);step.appendTo(td);//设置change事件侦听,获取StepNumber类内抛发的数据step.addEventListener(\"change\",e=>this.stepChangeHandler(e))break;default ://其他单元格内填入列表内对应数据td.textContent=data[prop];td.style.paddingLeft=\"15px\"break}}//设置事件抛发,调用该类的该方法时,侦听获取数据stepChangeHandler(e){var evt=new Event(Shopping.STEP_CHANGE);evt.data=e.data;evt.step=e.step;this.dispatchEvent(evt);}checkHandler(e){var evt=new Event(Shopping.CHECK_CHANGE);evt.data=e.data;evt.checked=e.checked;this.dispatchEvent(evt);}deleteHandler(e){var evt=new Event(Shopping.DELETE_CHANGE);evt.data=e.currentTarget.data;this.dispatchEvent(evt);}//静态函数设置CSS样式static setStyle(){//首先把静态变量styleBool设置为true,即已经设置CSS属性。Shopping.styleBool=true;Utils.setStyle({\".tableClass\":{width:\"990px\",position:\"relative\",margin:\"auto\",left:0,right:0,borderCollapse:\"collapse\"},\".thr\":{width:\"100%\",height:\"32px\",lineHeight:\"32px\",padding:\"5px 0px\",backgroundColor:\"#F3F3F3\",color:\"#666666\",fontSize:\"12px\",margin:\"0px 0px 10px\"},\".trs\":{width:\"100%\",backgroundColor:\"#fff4e8\",color:\"#666666\",fontSize:\"12px\",borderTop:\"1px solid #CCCCCC\",},\".thr>th:nth-child(1)\":{width:\"122px\",paddingLeft:\"11px\"},\".thr>th:nth-child(2)\":{width:\"208px\",},\".thr>th:nth-child(3)\":{width:\"150px\",padding:\"0 10px 0 20px\"},\".thr>th:nth-child(4)\":{width:\"100px\",paddingRight:\"40px\"},\".thr>th:nth-child(5)\":{width:\"120px\",},\".thr>th:nth-child(6)\":{width:\"100px\",paddingRight:\"40px\"},\".thr>th:nth-child(7)\":{width:\"75px\",paddingLeft:\"11px\"}})}}
(5)合成页面模块
import GoodsItem from \"./GoodsItem注释.js\";import Shopping from \"./Shopping注释.js\";import Utils from \"./Utils.js\";export default class Main {list; //接收以参数传入的列表goodsList = [];shoppingList = []; //创建一个空数组,然后存放点击商品后加入的商品数据shopping; //shopping实例化constructor(_list) {this.list = _list;//创建div容器存放商品列表var div = Utils.ce(\"div\");for (var i = 0; i < _list.length; i++) {//根据列表长度创建商品显示内容var goods = new GoodsItem();//将商品显示内容放入div中goods.appendTo(div);//将列表内对应数据加入商品显示内容goods.setData(_list[i]);//给商品显示内容添加点击侦听事件goods.addEventListener(\"click\", e => this.clickHandler(e));}document.body.appendChild(div);//生成购物车实例化对象this.shopping=new Shopping();//将购物车放入body里this.shopping.appendTo(\"body\");//购物车侦听多选框是否被选中this.shopping.addEventListener(Shopping.CHECK_CHANGE,e=>this.checkChangeHandler(e));//侦听累加器是否被修改this.shopping.addEventListener(Shopping.STEP_CHANGE,e=>this.stepChangeHandler(e));//侦听是否删除数据this.shopping.addEventListener(Shopping.DELETE_CHANGE,e=>this.deleteChangeHandler(e));//创建总价容器this.totalDiv=Utils.ce(\"div\",{fontSize:\"30px\",textAlign:\"right\",paddingRight:\"150px\",color:\"red\"},\"body\");}//点击商品显示区后,执行的点击事件,将点击的商品加入购物车clickHandler(e) {//将点击商品的信息赋值给datavar data = e.currentTarget.data;//当数据重复时,使用reduce将数据筛除,只留一条数据。var item=this.shoppingList.reduce((value,item)=>{if(item.id===data.id) value=item;return value;},null);//如果if (item) {item.num++;item.total=item.price*item.num;} else {var obj = {id: data.id,checked: false,icon: e.icon,name: data.info,info: \"\",price: data.price,num: 1,total: data.price,deleted: false}this.shoppingList.push(obj);}this.shopping.setData(this.shoppingList)}checkChangeHandler(e){if(!e.data){this.shoppingList.forEach(item=>{item.checked=e.checked;})}else{this.shoppingList.forEach(item=>{if(item.id===e.data.id) item.checked=e.checked;})}this.shopping.setData(this.shoppingList);this.totalPrice();}stepChangeHandler(e){this.shoppingList.forEach(item=>{if(item.id===e.data.id){item.num=e.step;item.total=e.step*item.price;}});this.shopping.setData(this.shoppingList);this.totalPrice();}deleteChangeHandler(e){this.shoppingList=this.shoppingList.filter(item=>{return item.id!==e.data.id;});this.shopping.setData(this.shoppingList)this.totalPrice();}totalPrice(){this.totalDiv.textContent=this.shoppingList.reduce((value,item)=>{if(item.checked) value+=item.total;return value;},0)}}
(5)创建元素板块
export default class Utils{static ce(type,style,parent){var elem=document.createElement(type);if(style){for(var prop in style){elem.style[prop]=style[prop];}}if(typeof parent===\"string\") parent=document.querySelector(parent);if(parent) parent.appendChild(elem);return elem;}}
2.html页面
<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><title>Document</title></head><body><script type=\"module\">import Main from \"./js/Main注释.js\";var arr = [{id: 1001,icon: [\"./img/a1.jpg\"],miniIcon: [\"./img/mini_a1.jpg\"],price: 1199,info: \"荣耀Play4T 全网通6GB+128GB大内存 蓝水翡翠 4000mAh大电池 4800万AI摄影 6.39英寸魅眼屏\",info1: \"\",used: false,evaluate: \"31万+\",shopName: \"荣耀京东自营旗舰店\",presell: \"\",tag: [\"自营\", \"放心购\"]},{id: 1002,icon: [\"./img/a2.jpg\"],miniIcon: [\"./img/mini_a2.jpg\"],price: 1389,info: \"荣耀Play4T Pro 麒麟810芯片 OLED屏幕指纹 4800万高感光夜拍三摄 22.5W超级快充 全网通6GB+128GB 幻夜黑\",info1: \"\",used: false,evaluate: \"10万+\",shopName: \"荣耀京东自营旗舰店\",presell: \"预售中\",tag: [\"自营\", \"放心购\", \"秒杀\", \"赠\"]},{id: 1003,icon: [\"./img/b1_1.jpg\", \"./img/b1_2.jpg\", \"./img/b1_3.jpg\"],miniIcon: [\"./img/mini_b1_1.jpg\", \"./img/mini_b1_2.jpg\", \"./img/mini_b1_3.jpg\"],price: 1389,info: \"荣耀Play4T Pro 麒麟810芯片 OLED屏幕指纹 4800万高感光夜拍三摄 22.5W超级快充 全网通6GB+128GB 蓝水翡翠\",info1: \"\",used: false,evaluate: \"37万+\",shopName: \"荣耀京东自营旗舰店\",presell: \"\",tag: [\"自营\", \"放心购\", \"本地仓\", \"秒杀\", \"赠\"]},{id: 1004,icon: [\"./img/b2_1.jpg\", \"./img/b2_2.jpg\", \"./img/b2_3.jpg\"],miniIcon: [\"./img/mini_b2_1.jpg\", \"./img/mini_b2_2.jpg\", \"./img/mini_b2_3.jpg\"],price: 1199,info: \"荣耀Play4T 全网通6GB+128GB大内存 蓝水翡翠 4000mAh大电池 4800万AI摄影 6.39英寸魅眼屏\",info1: \"\",used: false,evaluate: \"31万+\",shopName: \"荣耀京东自营旗舰店\",presell: \"\",tag: [\"自营\", \"放心购\", \"本地仓\", \"赠\"]},{id: 1005,icon: [\"./img/c1.jpg\"],miniIcon: [\"./img/mini_c1.jpg\"],price: 1389,info: \"荣耀Play4T Pro 麒麟810芯片 OLED屏幕指纹 4800万高感光夜拍三摄 22.5W超级快充 全网通6GB+128GB 幻夜黑\",info1: \"\",evaluate: \"10万+\",used: false,shopName: \"荣耀京东自营旗舰店\",presell: \"\",tag: [\"自营\", \"放心购\", \"秒杀\", \"赠\"],},{id: 1006,icon: [\"./img/c2_1.jpg\", \"./img/c2_2.jpg\", \"./img/c2_3.jpg\", \"./img/c2_4.jpg\"],miniIcon: [\"./img/mini_c2_1.jpg\", \"./img/mini_c2_2.jpg\", \"./img/mini_c2_3.jpg\", \"./img/mini_c2_4.jpg\"],price: 1389,info: \"麒麟980芯片;超感光徕卡四摄10倍混合变焦;店铺首页领白条免息券\",info1: \"\",evaluate: \"81万+\",used: true,shopName: \"华为京东自营官方旗舰店\",presell: \"\",tag: [\"自营\", \"放心购\", \"秒杀\", \"赠\"],},{id: 1007,icon: [\"./img/d1_1.jpg\", \"./img/d1_2.jpg\", \"./img/d1_3.jpg\", \"./img/d1_4.jpg\", \"./img/d1_5.jpg\"],miniIcon: [\"./img/mini_d1_1.jpg\", \"./img/mini_d1_2.jpg\", \"./img/mini_d1_3.jpg\", \"./img/mini_d1_4.jpg\", \"./img/mini_d1_5.jpg\"],price: 2489,info: \"荣耀Play4 Pro 5G双模 麒麟990 4000万超感光暗拍 40W超级快充 8GB+128GB幻夜黑\",info1: \"\",used: false,evaluate: \"21万+\",shopName: \"荣耀京东自营官方旗舰店\",presell: \"\",tag: [\"自营\", \"放心购\", \"秒杀\", \"赠\"]},{id: 1008,icon: [\"./img/d2_1.jpg\", \"./img/d2_2.jpg\", \"./img/d2_3.jpg\", \"./img/d2_4.jpg\"],miniIcon: [\"./img/mini_d2_1.jpg\", \"./img/mini_d2_2.jpg\", \"./img/mini_d2_3.jpg\", \"./img/mini_d2_4.jpg\"],price: 2399,info: \" HUAWEI nova 5 Pro 前置3200万人像超级夜景4800万AI四摄麒麟980芯片8GB+128GB亮黑色全网通双4G\",info1: \"\",used: true,evaluate: \"57万+\",shopName: \"华为京东自营官方旗舰店\",presell: \"\",tag: [\"自营\", \"放心购\", \"秒杀\"]},{id: 1009,icon: [\"./img/e1_1.jpg\", \"./img/e1_2.jpg\", \"./img/e1_3.jpg\", \"./img/e1_4.jpg\", \"./img/e1_5.jpg\"],miniIcon: [\"./img/mini_e1_1.jpg\", \"./img/mini_e1_2.jpg\", \"./img/mini_e1_3.jpg\", \"./img/mini_e1_4.jpg\", \"./img/mini_e1_5.jpg\"],price: 2489,info: \"荣耀V30 5G 双模 麒麟990 突破性相机矩阵 游戏手机 8GB+128GB 幻夜星河 移动联通电信5G 双卡双待\",info1: \"\",used: false,evaluate: \"21万+\",shopName: \"荣耀京东自营旗舰店\",presell: \"\",tag: [\"自营\", \"放心购\", \"秒杀\", \"赠\"],},{id: 1010,icon: [\"./img/e2_1.jpg\", \"./img/e2_2.jpg\", \"./img/e2_3.jpg\", \"./img/e2_4.jpg\", \"./img/e2_5.jpg\"],miniIcon: [\"./img/mini_e2_1.jpg\", \"./img/mini_e2_2.jpg\", \"./img/mini_e2_3.jpg\", \"./img/mini_e2_4.jpg\", \"./img/mini_e2_5.jpg\"],price: 1889,info: \"荣耀Play4 5G双模 6400万锐力四摄 4300mAh大电池 VC液冷散热 8GB+128GB 幻夜黑TNNH-AN00\",info1: \"\",used: false,evaluate: \"170万+\",shopName: \"荣耀京东自营旗舰店\",presell: \"\",tag: [\"自营\", \"放心购\", \"秒杀\", \"赠\"],},{id: 1011,icon: [\"./img/f1_1.jpg\", \"./img/f1_2.jpg\", \"./img/f1_3.jpg\"],miniIcon: [\"./img/mini_f1_1.jpg\", \"./img/mini_f1_2.jpg\", \"./img/mini_f1_3.jpg\"],price: 1889,info: \"荣耀Play4 5G双模 6400万锐力四摄 4300mAh大电池 VC液冷散热\",info1: \"\",used: true,evaluate: \"1.3万+\",shopName: \"荣耀京东自营旗舰店\",presell:\"\",tag: [\"自营\", \"放心购\", \"秒杀\", \"赠\"],},{id: 1012,icon: [\"./img/f2_1.jpg\", \"./img/f2_2.jpg\", \"./img/f2_3.jpg\"],miniIcon: [\"./img/mini_f2_1.jpg\", \"./img/mini_f2_2.jpg\", \"./img/mini_f2_3.jpg\"],price: 1889,info: \"华为畅享10e 手机 翡冷翠 移动全网通(4G+64G)\",info1: \"【4 + 64移动绿秒杀低至828元!直降170元!】现货速发,5000mAh超长续航,支持反向充电~!《畅享20pro咨询减钱》戳~\",used: true,evaluate: \"1.3万+\",shopName: \"荣耀京东自营旗舰店\",presell:\"\",tag: [\"京东物流\", \"放心购\", \"秒杀\", \"免邮\", \"险\"],}]new Main(arr)</script></body></html>
3.购物车思路
(1)Utils 创建元素 设置样式
CheckBox 多选框
constructor 将多选框关联的对象存入
createElem 创建多选框
appendTo 将多选框插入在父容器尾部
insertTo 将多选框插入在父容器中某个元素前面
clickHandler 点击多选框切换 并且抛发事件 现在多选框是否选中,将带入的对象也携带抛出
setCheck 设置多选框的内容
mouseHandler 改变多选框经过时的样式
(2)GoodsItem
constructor 创建整个显示容器createElem 创建容器appendTo 插入在父容器setData 设置数据,根据数据生成商品显示内容iconMouseHandler 鼠标经过小图标时事件changeIcon 修改大图标renderHTML 渲染所有HTML标签内容setStyle 设置样式clickHandler 当点击当前商品时,抛发事件将当前商品的对象数据抛出
(3) StepNumber 数据累加器
constructor 创建数据累加器,并且将当前数据的对象存入createElem 创建数据累加器内容appendTo 将当前元素放入父容器中inputHandler 当输入内容处理值clickHandler 当点击+-按钮时处理内容setStep 输入和点击按钮,以及可以外部设置值,根据设置值改变input的显示内容,并且判断是否抛发事件,事件中带有当前数据的对象和累加的结果(当输入和点击按钮时抛发,如果外部设置不抛发)
(4)Shopping
constructor 创建购物车createElem 创建购物车容器appendTo 将购物车添加在父容器中setData 设置数据,根据数据生成表格createHead 创建表头, 表头使用到CheckBox,并且设置侦听 CheckBox事件内容,设置了根据数据判断是否需要全选createListTr 创建表格各行createTdContent 创建表格每行当中的内容,分别使用到 CheckBox, stepNumber,CheckBox设置了多选框的data数据,设置多选框根据数据内容选择是否被选中,侦听多选事件,stepNumber放入时设置当前对象存储,侦听累加器是否改变,设定累加器初始值stepChangeHandler 侦听累加器收到修改事件,并且重新抛发新的事件通知外层类执行checkHandler 侦听多选框选中事件,并且重新抛发事件通知外层类执行deleteHandler 侦听删除事件,并且重新抛发事件通知外层类执行setStyle 设置样式
(5)Main
constructor 创建购物车页面内容 根据给入的数据创建了商品列表,创建商品列表的侦听事件,创建了购物车对象,创建总价容器。购物车侦听多选框是否被选中,侦听累加器是否被修改,侦听是否删除数据
clickHandler 当点击商品列表中商品时,判断再购物车shoppingList数组中是否有当前点击的商品,如果有,就给数据做累加,如果没有就添加新数据到shoppingList,并且根据当前shoppingList灌入到购物车中,重新创建购物车表格
checkChangeHandler 如果多选框被选中,判断有没有数据,如果没有数据就是表头的多选框,有数据就是每个商品数据,根据这个内容修改shoppingList数据中对应的内容,并且重新灌入到购物车中,重新生成购物车表格
stepChangeHandler 累加器被修改时,修改对应的shoppingList中对应的数据,并且重新灌入到购物车中,重新生成购物车表格
deleteChangeHandler 从shoppingList中删除需要删除的数据商品,并且重新灌入到购物车中,重新生成购物车表格
totalPrice 修改商品数量,删除商品,选中商品,都执行这个方法,并且根据数据是否选中,将商品总价累积并且显示