先上效果图
稍稍解释一下:
由于截屏不到点击时状态效果,姑且用红色框框代表点击时效果
实现原理:
- 首先从样式上看,分两种状态: 编辑时与完成时,这两种状态的样式需要先写好,通过变量控制类名去实现
- 下面列表中每一个应用都有2种状态:+/- 这里可以给每个元素一个默认变量值为false,改变时变为true
- 核心算法就是使用数组的slice,concat方法,去删除数组arr中下标为d的元素,
// 删除下标为d的元素 arrDel(arr, d) { // arr.slice(s,e) s起始索引 e结束索引 返回值: 被删除的元素数组 return arr.slice(0, d).concat(arr.slice(d + 1)); },
这个函数简直吊炸天,有没有,不过不是姐想的,是姐的leader创造的,简直不要太好用~
好了,言归正传,来说第4条:
4.还用了一个点睛之笔的方法,就是烂大街的: arr.indexOf(a)
- 没错他就是用来判断 arr 是否包含 a,我们通常用 ==-1 来表示不包含
- 殊不知包含状态下会返回a在arr的下标值
- 如 [1,2,3,4].indexOf(3) = 2 2就是3在数组中的索引值
- 有了这个索引值就可以与 arrDel函数配置使用,达到王炸的效果
原理已说完,上代码
html:
<!-- 常用应用 --> <div class="commonOption"> <div class="title_box"> <div class="title">常用应用</div> <div class="edit" @click.this="changeName">{{editDesc}}</div> </div> <!-- 常用应用列表 --> <ul class="common_list"> <li class="common_item" v-for="(citem,cindex) in commonList" :key="cindex"> <div class="apply_box" @click="goCommonPage(citem)" :class="{'selectStatus':isEdit}"> <i :style="{background:`url(${require(`@/assets/images/life/index/${citem.icon_name}.png`)}) no-repeat center/100%`}" class="apply_icon" ></i> <p class="apply_name">{{citem.content_name}}</p> <i class="sub_icon" v-if="isEdit"></i> </div> </li> </ul> <div class="line"></div> </div> <!-- 内容列表 --> <ul :class="['content_list_box',{'contentFixed':Fixed}]" :style="{height:(screenHeight + lastTop - 260) + 'px'}" > <li v-for="(item,index) in contentBox" :key="index" class="content_list_item" @scroll="contentScroll(index)" ref="contentRef" @click="goPage(item)" > <div class="content_title" v-if="index" ref="contentBox">{{item.title}}</div> <div class="content_box"> <div v-for="(subItem,subIndex) in item.contentList" :key="subIndex" class="content_item" @click="goLifePage(subItem,subIndex)" > <div class="item_inner" :class="{'selectStatus':isEdit}"> <i :style="{background:`url(${require(`@/assets/images/life/index/${subItem.icon_name}.png`)}) no-repeat center/100%`}" class="icon" ></i> <p class="content_name">{{subItem.content_name}}</p> <i class="jia_icon" v-if="isEdit" :class="{'jianIcon':subItem.changeIcon}"></i> </div> </div> </div> <div class="line" v-if="index<contentBox.length-1"></div> </li> </ul>
js:
data() { return { contentBox: [ { type: "videoEnt", title: "视频娱乐", contentList: [ { type: "iqiyiVideo", icon_name: "aiqiyi@2x", content_name: "爱奇艺", changeIcon: false }, { type: "tencentVideo", icon_name: "tengxunshipin@2x", content_name: "腾讯视频", changeIcon: false }, { type: "youkuVideo", icon_name: "youkushipin@2x", content_name: "优酷视频", changeIcon: false } ] }, { title: "卡密相关", contentList: [ { type: "jingdongEcard", icon_name: "jingdongeka@2x", content_name: "京东E卡卡密", changeIcon: false }, { type: "wangyiEcard", icon_name: "wangyi@2x", content_name: "网易严选", changeIcon: false } ] }, { title: "充值中心", contentList: [ { type: "FlowRecharge", icon_name: "liuliang@2x", content_name: "流量充值", changeIcon: false }, { type: "PhoneRecharge", icon_name: "huafei@2x", content_name: "话费充值", changeIcon: false } ] }, { title: "生活缴费", contentList: [ { type: "lifePayment", icon_name: "shuifei@2x", content_name: "水费查缴", changeIcon: false }, { type: "lifePayment", icon_name: "dianfei@2x", content_name: "电费查缴", changeIcon: false }, { type: "lifePayment", icon_name: "ranqi@2x", content_name: "燃气查缴", changeIcon: false } ] }, { title: "车务交罚", contentList: [ { type: "violationSearch", icon_name: "chaxun@2x", content_name: "违章查询", changeIcon: false }, { type: "violationPay", icon_name: "jiaofei@2x", content_name: "违章缴费", changeIcon: false } ] }, { type: "", title: "生活服务", contentList: [ { type: "weather", icon_name: "zhiliang@2x", content_name: "天气预报", changeIcon: false }, { type: "aqi", icon_name: "yubao@2x", content_name: "空气质量", changeIcon: false } ] } ], commonList: JSON.parse( window.localStorage.getItem("commonList") && window.localStorage.getItem("commonList") == "undefined" ? "[]" : window.localStorage.getItem("commonList") ) || [], // 常用列表 editDesc: "编辑", // 按钮字体 isEdit: false, // 是否编辑状态 }; }, created() { this.dealInit(); }, methods: { //初始化图标--将列表中已存在于常用应用中的图标+改成- dealInit() { // 改变旧的数组一般使用foreach,想要获取新的数组就用map let keys = this.commonList.map(res => res.content_name); this.contentBox.forEach(item => { if (item.contentList) { item.contentList.forEach(res => { res.changeIcon = keys.indexOf(res.content_name) !== -1 ? true : false; }); } }); }, // 常用应用取消并跳转 goCommonPage(citem) { if (this.isEdit) { this.dealShift(citem); } else { this.goPage(citem); } }, // 列表中应用添加到 常用应用 goLifePage(item) { if (this.isEdit) { if (this.commonList && this.commonList.length >= 8) { this.$Toast("最多可添加8个应用"); return false; } // 加数据 this.dealShift(item); } else { this.goPage(item); } }, // 删除/添加 常用应用 dealShift(item = null) { // indexOf的一个巧妙的用处:判断数组中是否包含该元素时,= -1 代表不包含,>-1时 代表获取的元素在数组中的下标 // 通过indexOf获取下标,即可不需要考虑下标从何而来 if (Array.isArray(this.commonList)) { // 获取常用应用中以名字组成的数组 let keys = this.commonList.map(res => res.content_name); // 判断当前点击应用是否被包含在常用应用中-是则返回索引值,否则返回-1 let index = keys.indexOf(item.content_name); //存在时,点击后就-变成+ if (index !== -1) { item.changeIcon = false; this.commonList = this.arrDel(this.commonList, index); } //不存在,点击后就+变成减- else { if (item) { item.changeIcon = true; this.commonList.push(item); } } window.localStorage.setItem( "commonList", JSON.stringify(this.commonList) ); } }, // 删除下标为d的元素 arrDel(arr, d) { // arr.slice(s,e) s起始索引 e结束索引 返回值: 被删除的元素数组 return arr.slice(0, d).concat(arr.slice(d + 1)); }, // 更换字段 changeName() { if (this.editDesc === "编辑") { this.editDesc = "完成"; this.isEdit = true; } else { this.editDesc = "编辑"; this.isEdit = false; } }, }
分享一刻:
一位大佬的书: 《The Pragmatic Programmer》《程序员修炼之道》