uni-app学习笔记五(第5章项目实战:更多卡片视图的实现,收藏按钮实现)

本文详细介绍了在前端开发中实现不同样式的卡片布局,包括基础卡片、多图模式和大图模式,通过CSS实现样式切换。同时讨论了组件间的通信,如使用props传递模式属性,以及监听选项卡变化的watch方法。此外,还提到了云函数的使用技巧,如过滤数据库内容和处理收藏功能。文章最后讨论了错误排查和加载更多数据的组件使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本篇要学习的是下面两种卡片的实现

照着做了一下,实在没什么好写的。
简单说一下思路,不同卡片图片是因为用了三种展示方式,为每一种展示方式写一组css,三种方式通过父组件传过来。子组件通过增加props来接收。

父组件 index.vue  其他内容省略了。

<template>
	<view class="home">
		<navbar></navbar>
		<tab :list="tabList" @tab="tab_update"></tab>
		
		<list-scroll>
			<!-- <list-card v-for="item in 5"></list-card> -->
			<list-card mode="base"></list-card>
			<list-card mode="image"></list-card>
			<list-card mode="column"></list-card>
		</list-scroll>
	</view>
</template>

子组件 list-card.vue

<template>
	<view>
		<!-- 基础卡片 --><!-- aspectFill 保持纵横比缩放图片 -->
		<view v-if="mode === 'base'" class="listcard">
			<view class="listcard-image">
				
				<image src="/static/logo.png" mode="aspectFill"></image>
			</view>
			<view class="listcard-content">
				
				<view class="listcard-content_title">
					<text>
						uni-app开发框架开发框架开发框架开发框架开发框架开发框架开发框架开发框架开发框架开发框架
					</text>
				</view>
				
				<view class="listcard-content_des">
					<view class="listcard-content_des-label">
						<view class="listcard-content_des-label-item">
							前端
						</view>
					</view>
					<view class="listcard-content_des-label-browse">
						120浏览
					</view>
				</view>
			</view>
		</view>
		<!-- 多图模式 -->
		<view v-if="mode ==='column'" class="listcard mode-column">
			<view class="listcard-content">
				<view class="listcard-content_title">
					<text>
						uni-app开发框架开发框架开发框架开发框架开发框架开发框架开发框架开发框架开发框架开发框架
					</text>
					<view class="listcard-image">
						<view v-for="item in 3" :key="item" class="listcard-image_item">
							<image src="/static/logo.png" mode="aspectFill"></image>
						</view>
						
					</view>
				</view>
				
				<view class="listcard-content_des">
					<view class="listcard-content_des-label">
						<view class="listcard-content_des-label-item">
							前端
						</view>
					</view>
					<view class="listcard-content_des-label-browse">
						120浏览
					</view>
				</view>
			</view>
		</view>
		<!-- 大图模式 -->
		<view v-if="mode === 'image'" class="listcard mode-image">
			<view class="listcard-image">
				<image src="/static/logo.png" mode="aspectFill"></image>
			</view>
			<view class="listcard-content">
				<view class="listcard-content_title">
					<text>
						uni-app开发框架开发框架开发框架开发框架开发框架开发框架开发框架开发框架开发框架开发框架
					</text>

				</view>
				<view class="listcard-content_des">
					<view class="listcard-content_des-label">
						<view class="listcard-content_des-label-item">
							前端
						</view>
					</view>
					<view class="listcard-content_des-label-browse">
						120浏览
					</view>
				</view>
			</view>
		</view>
	</view>
</template>

<script>
	export default {
		props:{
			mode:{
				type:String,
				default:'base'
			}
		},
		data() {
			return {
				
			};
		}
	}
</script>

<style lang="scss">
	.listcard{
		display: flex;
		padding: 10px;
		margin:10px;
		border-radius: 5px;
		box-shadow: 0 0 5px rgba($color: #000000, $alpha: 1.0);
		box-sizing: border-box;
		.listcard-image{
			// 不让图片被挤压
			flex-shrink: 0;
			width:60px;
			height: 60px;
			border-radius: 5px;
			overflow: hidden;
			image{
				width:100%;
				height: 100%;
			}
		}
		.listcard-content{
			display: flex;
			flex-direction: column;
			justify-content: space-between;
			padding-left: 10px;
			width:100%;
			.listcard-content_title{
				padding-right: 30px;
				font-size: 14px;
				color: #333;
				font-weight: 400;
				line-height: 1.2;
				// 下面的text是做溢出隐藏
				text{
					overflow: hidden;
					text-overflow: ellipsis;
					display: -webkit-box;
					-webkit-line-clamp:2;
					-webkit-box-orient:vertical;
				}
			}
			.listcard-content_des{
				display: flex;
				justify-content: space-between;
				font-size: 12px;
				.listcard-content_des-label{
					display: flex;
					.listcard-content_des-label-item{
						padding: 0 5px;
						margin-right: 5px;
						border-radius: 15px;
						color: $mk-base-color;
						border: 1px $mk-base-color solid;
					}
				}
				.listcard-content_des-label-browse{
					color: #999999;
					line-height: 1.5;
				}
			}
		}
		&.mode-column{
			.listcard-content{
				width: 100%;
				padding-left: 0;
			}
			.listcard-image{
				display: flex;
				margin-top: 10px;
				width: 100%;
				height: 70px;
				.listcard-image_item{
					margin-left: 10px;
					width: 100%;
					border-radius: 5px;
					overflow: hidden;
					&:first-child{
						margin-left: 0;
					}
					image {
						width: 100%;
						height: 100%;
					}
				}
			}
			.listcard-content_des{
				margin-top: 10px;
			}
		}
		&.mode-image{
			flex-direction: column;
			.listcard-image{
				width: 100%;
				height: 100px;
			}
			.listcard-content{
				padding-left: 0;
				margin-top: 10px;
				.listcard-content_des{
					display: flex;
					align-items: center;
					margin-top: 10px;
				}
			}
		}
	}
</style>

效果图。

实现内容切换

由于这阶段大多数是css的调整,细节繁琐,所以不做笔记。只记录个别需要注意的地方。其他样式相关内容,直接贴代码领会。
前面记录过,这里在重复一下,如果组件名字和文件夹名字不一样,需要手动导入的。如下:

下面是零散的记录。
当滑动卡片,让选项卡也跟着变化,监听选项卡的当前indexid.用watch。下面是使用例子。注意demo和watch里面是一样的。

<template>
  <div>
    <el-input v-model="demo"></el-input>
    {{value}}
  </div>
</template>
<script>
  export default {
    name: 'index',
    data() {
      return {
        demo: '',
        value: ''
      };
    },
    watch: {
      demo(val) {
        this.value = this.demo;
      }
    }
  };
</script>

如果不想要云函数返回的数据库内容中的某项,可以通过下面方法过滤掉。

下面两种写法是一样的。

如何通过云函数查询云数据库中某个字段下面匹配某个字符串的一些数据?
具体意思,hbuilder会有注释。

懒加载:  原问题是第一种赋值的话,并不会马上刷新。所以用第二种形式。这里懒加载的意思是说如果滑动到上次滑动过的页面,那么就不刷新,否则刷新。

这个例子讲的很好,https://blog.csdn.net/qq_41428035/article/details/102687303

遇到报错信息

vue.runtime.esm.js:619 [Vue warn]: Unknown custom element: <FormError> - did you register the component correctly? For recursive components, make sure to provide the “name” option.

在这里插入图片描述

可能原因:出现这种问题的原因我目前只遇到一种,那就是vue的组件使用错误,具体原因包括以下几种:

  • import语句导入组件时from后面的路径写错
  • 注册组件时括号内的组件名称写错,与import声明的不一致
  • 注册组件关键字components写错导致无法使用
  • 使用组件时名称写错,与注册组件的名字不一致
  • 使用组件时没有使用 / 反斜杠结尾

上拉显示加载中,以及拉到底显示没有更多数据是下面组件。

对于加载中的传递是: list-scroll emit发送 -> list-item  emit发送 -> list.vue  永远是子向父组件发送的方向。

图中是什么语法

扩展语法。对数组和对象而言,就是将运算符后面的变量里东西每一项拆下来。

收藏按钮实现

1.按钮的位置是与卡片中的title同级的。
2.收藏按钮需要用绝对定位固定到卡片的右上角。

问题:点击收藏图标的时候,卡片也被点击了。
解决办法:阻止冒泡,具体如下

云函数临时测试可以通过下面的方式,比如你想测试按照userid 查询

如何添加新收藏的文章编号到用户表的数据库中?
用addToSet  避免重复。

删除操作

很巧妙的“点击收藏,再点击取消”的效果

问题:当前我点击收藏了,当我刷新页面后,收藏状态消失了。
解决办法:添加字段is_like。保存。然后读取文章内容的时候,把这个字段的值读取出来,赋给收藏红心。

问题:希望在点击收藏后,显示个提示收藏成功的提示信息
解决办法:

 

 

 

 

 

 

 

 

 

<think>我们已知用户要在uni-app实现多级下拉菜单,并且提供了三个引用作为背景。根据引用[1]和[2]可知,uni-app是一个跨平台框架,使用Vue.js语法,并提供了丰富的组件和API。引用[3]给出了一个使用内置组件navigator的示例,但用户需要的多级下拉菜单属于UI组件范畴。在uni-app实现多级下拉菜单,通常有几种方式:1.使用uni-app官方提供的扩展组件(如uni-ui)中的下拉菜单组件。2.自己手写多级下拉菜单组件,利用uni-app视图组件(如view)和事件处理。由于引用中并未直接提供多级下拉菜单的示例,我将参考uni-app的常见做法,给出一个自实现多级下拉菜单的简单示例。考虑到uni-app的跨平台特性,我们使用其内置的视图组件和样式实现。下面是一个三级下拉菜单的示例,使用Vue.js的响应式数据绑定和事件处理。步骤:1.准备数据:一个树形结构的数据,包含多级菜单。2.使用视图组件循环渲染每一级菜单。3.通过点击事件切换子菜单的显示与隐藏。示例代码如下:```html<template><view><!--一级菜单--><viewv-for="(item,index)inmenuData":key="index"><viewclass="menu-item"@click="toggleSubMenu(index)">{{item.title}}<!--根据当前菜单的展开状态显示箭头图标--><uni-icons:type="item.expanded?'arrowup':'arrowdown'"size="14"></uni-icons></view><!--二级菜单,当item.expanded为true时显示--><viewv-if="item.expanded"class="sub-menu"><viewv-for="(subItem,subIndex)initem.children":key="subIndex"class="sub-menu-item"@click.stop="toggleSubSubMenu(item,subIndex)">{{subItem.title}}<uni-icons:type="subItem.expanded?'arrowup':'arrowdown'"size="12"></uni-icons></view><!--三级菜单--><viewv-if="subItem.expanded"class="sub-sub-menu"><viewv-for="(subSubItem,subSubIndex)insubItem.children":key="subSubIndex"class="sub-sub-menu-item">{{subSubItem.title}}</view></view></view></view></view></template><script>exportdefault{data(){return{menuData:[{title:'菜单1',expanded:false,//控制二级菜单的展开children:[{title:'子菜单1-1',expanded:false,//控制三级菜单的展开children:[{title:'选项1-1-1'},{title:'选项1-1-2'}]},{title:'子菜单1-2',children:[]//无三级菜单}]},{title:'菜单2',expanded:false,children:[{title:'子菜单2-1'}]}]}},methods:{//切换一级菜单的展开状态toggleSubMenu(index){//切换当前一级菜单的展开状态,同时关闭其他一级菜单(如果需要,这里我们只切换当前)this.menuData[index].expanded=!this.menuData[index].expanded;//如果希望点击一级菜单时关闭其他一级菜单,可以这样://this.menuData.forEach((item,i)=>{//if(i===index){//item.expanded=!item.expanded;//}else{//item.expanded=false;//}//});},//切换二级菜单的展开状态(三级菜单的显示)toggleSubSubMenu(item,subIndex){//确保二级菜单有children才切换if(item.children[subIndex].children&&item.children[subIndex].children.length>0){item.children[subIndex].expanded=!item.children[subIndex].expanded;}}}}</script><style>.menu-item{padding:10px15px;background-color:#f5f5f5;border-bottom:1pxsolid#e5e5e5;display:flex;justify-content:space-between;align-items:center;}.sub-menu{background-color:#fff;padding-left:20px;/*缩进*/}.sub-menu-item{padding:10px15px;border-bottom:1pxsolid#eee;display:flex;justify-content:space-between;align-items:center;}.sub-sub-menu{background-color:#f9f9f9;padding-left:30px;/*更多缩进*/}.sub-sub-menu-item{padding:10px15px;border-bottom:1pxsolid#f0f0f0;}</style>```说明:1.我们使用了uni-app的内置组件`<view>`和`<uni-icons>`(需要先导入uni-icons组件,如果不想用图标,也可以用文本代替)。2.通过`expanded`属性控制每一级菜单的展开与收起。3.使用`@click`事件来触发切换,注意在二级菜单上使用`@click.stop`阻止事件冒泡到一级菜单。4.样式部分可以根据需要调整。注意:-如果使用`uni-icons`,需要在项目中安装或导入。也可以使用其他图标库或纯文本。-此示例是一个基础实现,实际项目中可能需要考虑更复杂的交互和样式兼容各平台。另外,uni-app的官方扩展库uni-ui中提供了`uni-dropdown`组件,但它是单级下拉菜单。多级下拉菜单通常需要自己实现或者使用其他第三方组件库。建议:1.如果需要更复杂的多级下拉菜单(如下拉树形菜单),可以考虑使用第三方插件,例如在插件市场搜索“多级下拉菜单”。2.注意跨平台兼容性,特别是各小程序平台和H5样式和事件可能有差异。相关问题:1.uni-app中如何使用uni-ui组件库?2.在uni-app中如何实现跨平台样式兼容?3.uni-app中如何引入和使用自定义图标?以上代码示例和说明应能帮助用户实现一个基本的多级下拉菜单。根据具体需求,用户可能需要调整数据结构和样式。</think> 下面是在 uni-app实现多级下拉菜单的示例代码和详细教程,结合了 uni-app 的组件特性和 Vue.js 语法: ### 解决方案(基于 uni-app 内置组件和 Vue.js) ```html <template> <view class="container"> <!-- 一级菜单 --> <view v-for="(item, index) in menuData" :key="index" class="menu-item"> <view class="menu-title" @click="toggleMenu(index)"> {{ item.title }} <uni-icons :type="item.expanded ? 'arrowup' : 'arrowdown'" size="14"></uni-icons> </view> <!-- 二级菜单 --> <view v-show="item.expanded" class="submenu"> <view v-for="(subItem, subIndex) in item.children" :key="subIndex" class="submenu-item" @click="toggleSubmenu(item, subIndex)"> {{ subItem.title }} <uni-icons v-if="subItem.children" :type="subItem.expanded ? 'arrowup' : 'arrowdown'" size="12"></uni-icons> </view> <!-- 三级菜单 --> <view v-show="subItem.expanded" class="subsubmenu"> <view v-for="(subSubItem, subSubIndex) in subItem.children" :key="subSubIndex" class="subsubmenu-item" @click="selectOption(item, subItem, subSubItem)"> {{ subSubItem.title }} </view> </view> </view> </view> </view> </template> <script> export default { data() { return { menuData: [ { title: '电子产品', expanded: false, children: [ { title: '手机', expanded: false, children: [ { title: 'iPhone' }, { title: 'Android' }, { title: '折叠屏' } ] }, { title: '电脑', children: [ { title: '笔记本' }, { title: '台式机' } ] } ] }, { title: '服装', expanded: false, children: [ { title: '男装', children: [ { title: '衬衫' }, { title: '外套' } ] }, { title: '女装', children: [ { title: '连衣裙' }, { title: '上衣' } ] } ] } ] } }, methods: { // 切换一级菜单展开状态 toggleMenu(index) { this.menuData[index].expanded = !this.menuData[index].expanded // 可选:关闭其他展开的菜单 this.menuData.forEach((item, i) => { if (i !== index) item.expanded = false }) }, // 切换二级菜单展开状态 toggleSubmenu(parentItem, subIndex) { const subItem = parentItem.children[subIndex] // 如果有子菜单才切换展开状态 if (subItem.children) { subItem.expanded = !subItem.expanded // 关闭同级其他菜单 parentItem.children.forEach((item, i) => { if (i !== subIndex && item.children) { item.expanded = false } }) } else { // 没有子菜单时直接选择该项 this.selectOption(parentItem, subItem) } }, // 选择最终选项 selectOption(parentItem, subItem, subSubItem) { const selectedItem = subSubItem || subItem uni.showToast({ title: `已选择: ${parentItem.title} > ${subItem.title}${subSubItem ? ' > ' + subSubItem.title : ''}`, icon: 'success' }) // 可在此处执行页面跳转或其他业务逻辑 // uni.navigateTo({ url: '...' }) } } } </script> <style> .container { padding: 20rpx; } .menu-item { margin-bottom: 10rpx; border-radius: 8rpx; overflow: hidden; } .menu-title { padding: 20rpx; background-color: #f5f5f5; display: flex; justify-content: space-between; align-items: center; } .submenu { background-color: #f9f9f9; padding-left: 30rpx; } .submenu-item { padding: 15rpx 20rpx; border-bottom: 1rpx solid #eee; display: flex; justify-content: space-between; align-items: center; } .subsubmenu { background-color: #fff; padding-left: 40rpx; } .subsubmenu-item { padding: 15rpx 20rpx; border-bottom: 1rpx dashed #eee; } </style> ``` ### 功能说明 1. **三级菜单结构**: - 一级菜单(垂直列表) - 二级菜单(从一级菜单右侧滑出) - 三级菜单(从二级菜单下方展开) 2. **核心交互**: - 点击菜单标题展开/收起子菜单 - 支持无限级嵌套(示例展示三级) - 点击最后一级菜单触发选择事件 3. **技术要点**: - 使用 `uni-icons` 组件显示箭头图标(需安装 `@dcloudio/uni-ui`) - 通过 Vue 的 `v-show` 控制菜单显隐 - 使用 `expanded` 状态属性管理展开状态 - 点击事件冒泡处理(`@click.stop` 阻止事件冒泡) ### 优化建议 1. **性能优化**: ```javascript // 在大型菜单中使用计算属性 computed: { activeMenu() { return this.menuData.filter(item => item.expanded) } } ``` 2. **动画效果**(添加过渡动画): ```css .submenu { transition: all 0.3s ease; max-height: 0; overflow: hidden; } .submenu.expanded { max-height: 1000px; } ``` 3. **复杂场景**: - 使用 `<scroll-view>` 包裹长列表 - 添加搜索过滤功能 - 实现多选模式(添加复选框) ### 学习资源推荐 1. [uni-app 官方组件文档](https://uniapp.dcloud.net.cn/component/view.html) - 学习基础组件使用 [^1] 2. [uni-ui 扩展组件库](https://ext.dcloud.net.cn/plugin?id=55) - 包含增强型组件 [^2] 3. [Vue.js 事件处理指南](https://cn.vuejs.org/v2/guide/events.html) - 深入理解事件机制 [^1] [^1]: Uni-app跨平台开发框架基础概念。 [^2]: uni-app快速开发模板:助力高效项目构建的技术探索。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值