【前端】Vue项目:旅游App-(22)detail:房屋信息、房屋设施、插槽

2023-11-13

本项目博客总结:【前端】Vue项目:旅游App-博客总结

目标

第一个框是房屋信息info,第二个框是房屋设施facility。
在这里插入图片描述

过程与代码

房屋信息部分info

目标是这样:

在这里插入图片描述
目标数据在整个页面数据的位置:

在这里插入图片描述
数据的对应:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
因此,我们需要给组件传数据:

detailData.mainPart.topModule

detail.vue:

 <!-- 标题 -->
 <div class="info" v-if="detailData.mainPart">
     <detailInfo :house-info="detailData.mainPart.topModule"/>
 </div>

detail-info对数据的定义:

const props = defineProps({
    houseInfo: {
        type: Object,
        default: () => { }
    }
})

根据目标搭建结构:

<template>
    <div class="info">
        <div class="name">{{ props.houseInfo.houseName }}</div>
        
        <div class="tag">
            <template v-for="(item,index) in houseTags" :key="index">
                <!-- 注意,有的房屋详情可能没有tagText -->
                <span class="item1" v-if="item.tagText" :style="{ color:  item.tagText.color ,background:item.tagText.background.color }">{{ item.tagText.text }}</span>
                <span class="item1" v-else>
                    <img :src="item.tagPic" alt="">
                </span>
            </template>
        </div>

        <div class="comment item2">
            <div class="score">{{ commentBrief.overall }}</div>
            <div class="content">{{ commentBrief.commentBrief }}</div>
            <div class="more">{{ commentBrief.totalCount }}条评论></div>
        </div>

        <div class="location item2">
            <div class="content">{{ nearByPosition.address }}</div>
            <div class="more">地图 周边></div>
        </div>
    </div>
</template>

<script setup>
const props = defineProps({
    houseInfo: {
        type: Object,
        default: () => { }
    }
})

const houseTags = props.houseInfo.houseTags
const commentBrief=props.houseInfo.commentBrief
const nearByPosition=props.houseInfo.nearByPosition

</script>

<style lang="less" scoped>

</style>

效果:

在这里插入图片描述
加入样式:

.info {
    .name {
        color: #333;
        font-size: 20px;
        font-weight: 700;
        text-align: justify;
        margin-bottom: 6px;
        letter-spacing: 0.3px;
    }

    .tag {

        display: flex;
        flex-wrap: wrap;

        .item1 {
            height: 14px;
            margin: 0 3px 3px 0;
            padding: 2px 4px;
            font-size: 12px;

            img {
                height: 18px;
            }
        }
    }

    .item2{
        display: flex;
        align-items: center;
        justify-content: space-between;

        margin: 12px 0;
        padding: 8px 12px;
        background-color: #f5f7fa;


        .score{
            font-weight: 700;
        }
        .address{
            font-weight: 700;
        }
        .more{
            color: var(--primary-color);
        }

    }
}

效果:

在这里插入图片描述

相似结构的组件section

接下来要做的内容如下:

在这里插入图片描述
显然,它们的结构是相似的,可以把它们抽取为一个组件,中间不同的地方是插槽slot

此组件的结构和样式代码:

<template>
    <div class="section">
        <div class="header">
            <h2 class="title">{{ headerText }}</h2>
        </div>
        <div class="content">
            <slot>默认内容</slot>
        </div>
        <div class="footer" v-if="moreText.length">
            {{ moreText }}
            <van-icon name="arrow" />
        </div>
    </div>
</template>

<script setup>
defineProps({
    headerText: {
        Object: String,
        default: "默认标题"
    },
    moreText: {
        Object: String,
        default: ""
    }
})
</script>

<style lang="less" scoped>
.section {
    margin-top: 10px;
    padding: 0 16px;

    .header {
        height: 52px;
        line-height: 52px;

        h2 {
            color: #333;
            font-size: 20px;
            font-weight: 600;
        }
    }

    .footer{
        display: flex;
        justify-content: flex-end;
        align-items: center;

        padding: 0 16px;
        color: var(--primary-color);
        font-weight: 600;
    }
}
</style>

效果:

在这里插入图片描述

房屋设施facility

目标:

在这里插入图片描述
根据前面组件section代码可知,我们只需要写content的内容,至于header和footer,只需要传入数据即可。

分析一下数据:框起来的是我们要显示的数据。

在这里插入图片描述
在这里插入图片描述
具体显示情况:要显示houseFacilitys[facilitySort]中数据。

在这里插入图片描述

<!-- 内容 -->
<detailSection :header-text="'房屋设施'" :more-text="'全部房屋设施'">
    <!-- 插槽内容 -->
    <detailFacility :houseFacility="detailData.mainPart.dynamicModule.facilityModule.houseFacility" />
</detailSection>

把传给facility组件的对象打印出来:

在这里插入图片描述
接下来就是根据数据搭建html结构:

<template>
    <div class="facility">
        <template v-for="(itemIndex, index) in facilitySort" :key="index">
            <div class="item1">
                <!-- 左侧title -->
                <div class="groupName">
                    <img :src="houseFacilitys[itemIndex].icon" alt="">
                    {{ houseFacilitys[itemIndex].groupName }}
                </div>
                <div class="nameList">
                    <!-- 右侧最多四个name -->
                    <template v-for="(item, indexx) in houseFacilitys[itemIndex].facilitys.slice(0, 4)" :key="indexx">
                        <van-icon name="passed" />{{ item.name }}
                    </template>
                </div>
            </div>
        </template>
    </div>
</template>

<script setup>
const props = defineProps({
    houseFacility: {
        type: Object,
        default: () => ({})
    }
})

const facilitySort = props.houseFacility.facilitySort
const houseFacilitys = props.houseFacility.houseFacilitys


</script>

<style lang="less" scoped>

</style>

效果:

在这里插入图片描述
注意:数据中右边的每个小item最多只有4个,因此要把要循环的数组先切割为只有4个的:

<template v-for="(item, indexx) in houseFacilitys[itemIndex].facilitys.slice(0,4)" :key="indexx">

加样式:

detail-facility:

.facility {
    background-color: #f7f9fb;
    color: #333;
    padding: 5px 16px 20px;
    border-radius: 6px;

    .item1 {
        display: flex;
        align-items: center;
        margin-top: 20px;

        .left {
            display: flex;
            flex-direction: column;
            align-items: center;
            width: 70px;

            .groupName {
                font-weight: 600;
                font-size: 11px;
                margin-top: 3px;
            }


            img {
                width: 20px;
                height: 20px;
            }
        }

        .nameList {
            display: grid;
            grid-template-columns: auto auto ;
            grid-template-rows: auto auto;
            margin-left: 17px;

            .item2 {
                width: 110px;
                height: 14px;

                margin-top: 7px;
            }
        }
    }
}

detail-section:

.section {
    margin-top: 10px;
    padding: 0 16px;

    .header {
        height: 52px;
        line-height: 52px;
        border-bottom: 1px solid  #DCDCDC;

        h2 {
            color: #333;
            font-size: 20px;
            font-weight: 600;
        }
    }

    .content{

        margin-top: 20px;
    }

    .footer{
        display: flex;
        justify-content: flex-end;
        align-items: center;

        padding: 15px 16px;
        color: var(--primary-color);
        font-weight: 600;
    }
}

效果:

在这里插入图片描述

效果

在这里插入图片描述

总代码

修改或添加的文件

在这里插入图片描述

detail-section

将有相似结构的内容抽取出来,不同的部分用插槽实现。

<template>
    <div class="section">
        <div class="header">
            <h2 class="title">{{ headerText }}</h2>
        </div>
        <div class="content">
            <slot>默认内容</slot>
        </div>
        <div class="footer" v-if="moreText.length">
            查看{{ moreText }}
            <van-icon name="arrow" />
        </div>
    </div>
</template>

<script setup>
defineProps({
    headerText: {
        Object: String,
        default: "默认标题"
    },
    moreText: {
        Object: String,
        default: ""
    }
})
</script>

<style lang="less" scoped>
.section {
    margin-top: 10px;
    padding: 0 16px;

    .header {
        height: 52px;
        line-height: 52px;
        border-bottom: 1px solid  #DCDCDC;

        h2 {
            color: #333;
            font-size: 20px;
            font-weight: 600;
        }
    }

    .content{

        margin-top: 20px;
    }

    .footer{
        display: flex;
        justify-content: flex-end;
        align-items: center;

        padding: 15px 16px;
        color: var(--primary-color);
        font-weight: 600;
    }
}
</style>

detail-facility

插槽:房屋设施 这一内容组件。

<template>
    <div class="facility">
        <template v-for="(itemIndex, index) in facilitySort" :key="index">
            <div class="item1">
                <!-- 左侧title -->
                <div class="left">
                    <img :src="houseFacilitys[itemIndex].icon" alt="">
                    <div class="groupName">{{ houseFacilitys[itemIndex].groupName }}</div>
                </div>
                <div class="nameList">
                    <!-- 右侧最多四个name -->
                    <template v-for="(item, indexx) in houseFacilitys[itemIndex].facilitys.slice(0, 4)" :key="indexx">
                        <div class="item2">
                            <van-icon name="passed" color="#17d2bc" />{{ item.name }}
                        </div>
                    </template>
                </div>
            </div>
        </template>
    </div>
</template>

<script setup>
const props = defineProps({
    houseFacility: {
        type: Object,
        default: () => ({})
    }
})

const facilitySort = props.houseFacility.facilitySort
const houseFacilitys = props.houseFacility.houseFacilitys


</script>

<style lang="less" scoped>
.facility {
    background-color: #f7f9fb;
    color: #333;
    padding: 5px 16px 20px;
    border-radius: 6px;

    .item1 {
        display: flex;
        align-items: center;
        margin-top: 20px;

        .left {
            display: flex;
            flex-direction: column;
            align-items: center;
            width: 70px;

            .groupName {
                font-weight: 600;
                font-size: 11px;
                margin-top: 3px;
            }


            img {
                width: 20px;
                height: 20px;
            }
        }

        .nameList {
            display: grid;
            grid-template-columns: auto auto ;
            grid-template-rows: auto auto;
            margin-left: 17px;

            .item2 {
                width: 110px;
                height: 14px;

                margin-top: 7px;
            }
        }
    }
}
</style>

detail-info

房屋信息。

<template>
    <div class="info">
        <div class="name">{{ props.houseInfo.houseName }}</div>

        <div class="tag">
            <template v-for="(item, index) in houseTags" :key="index">
                <!-- 注意,有的房屋详情可能没有tagText -->
                <span class="item1" v-if="item.tagText"
                    :style="{ color: item.tagText.color, background: item.tagText.background.color }">{{
                        item.tagText.text
                    }}</span>
                <span class="item1" v-else>
                    <img :src="item.tagPic" alt="">
                </span>
            </template>
        </div>

        <div class="comment item2">
            <div class="score">{{ commentBrief.overall }}</div>
            <div class="content">{{ commentBrief.scoreTitle }}   "{{ commentBrief.commentBrief }}"</div>
            <div class="more">{{ commentBrief.totalCount }}条评论<van-icon name="arrow" /></div>
        </div>

        <div class="location item2">
            <div class="address">{{ nearByPosition.address }}</div>
            <div class="more">地图 周边<van-icon name="arrow" /></div>
        </div>
    </div>
</template>

<script setup>
const props = defineProps({
    houseInfo: {
        type: Object,
        default: () => { }
    }
})

const houseTags = props.houseInfo.houseTags
const commentBrief = props.houseInfo.commentBrief
const nearByPosition = props.houseInfo.nearByPosition

</script>

<style lang="less" scoped>
.info {
    .name {
        color: #333;
        font-size: 20px;
        font-weight: 700;
        text-align: justify;
        margin-bottom: 6px;
        letter-spacing: 0.3px;
    }

    .tag {

        display: flex;
        flex-wrap: wrap;

        .item1 {
            height: 14px;
            margin: 0 3px 3px 0;
            padding: 2px 4px;
            font-size: 12px;

            img {
                height: 18px;
            }
        }
    }

    .item2{
        display: flex;
        align-items: center;
        justify-content: space-between;

        margin: 12px 0;
        padding: 8px 12px;
        background-color: #f5f7fa;


        .score{
            font-weight: 700;
        }
        .address{
            font-weight: 700;
        }
        .more{
            color: var(--primary-color);
        }

    }


}
</style>

detail

整个detail页面。

<template>
    <div class="detail top-page">
        <!-- 返回上级的导航栏 -->
        <van-nav-bar title="房屋详情" left-text="旅途" left-arrow @click-left="onClickLeft" />


        <div class="main" v-if="detailData.mainPart">
            <!-- 轮播图 -->
            <detailSwipe :swipe-data="detailData.mainPart.topModule.housePicture.housePics" />
            <!-- 标题 -->
            <div class="info">
                <detailInfo :house-info="detailData.mainPart.topModule" />
            </div>
            <!-- 内容 -->
            <detailSection :header-text="'房屋设施'" :more-text="'全部房屋设施'">
                <!-- 插槽内容 -->
                <detailFacility :houseFacility="detailData.mainPart.dynamicModule.facilityModule.houseFacility" />
            </detailSection>
        </div>

    </div>
</template>

<script setup>
import useDetailStore from '@/store/modules/detail';
import detailSwipe from '../detail/cpns/detail-swipe.vue'
import detailInfo from './cpns/detail-info.vue';
import detailSection from '@/components/detail-section/detail-section.vue';
import detailFacility from './cpns/detail-facility.vue';

import { useRoute } from 'vue-router';
import { storeToRefs } from 'pinia';

// const
const detailStore = useDetailStore()
const route = useRoute()

// 返回导航栏
const onClickLeft = () => history.back();

// houseId
const houseId = route.params.id

// store
detailStore.fetchDetailData(houseId)
const { detailData } = storeToRefs(detailStore)
</script>

<style lang="less" scoped>
.detail {

    .info {
        margin: 9px;
    }
}
</style>

参考

vue 静态/动态绑定style的几种方式【Vue】
CSS text-align 属性 (w3school.com.cn)
CSS 文字间距 (w3school.com.cn)
Flex 布局语法教程 | 菜鸟教程 (runoob.com)
彻底搞懂slot插槽,图文详解 - 简书 (jianshu.com)
CSS 网格布局 | 菜鸟教程 (runoob.com)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【前端】Vue项目:旅游App-(22)detail:房屋信息、房屋设施、插槽 的相关文章

随机推荐

  • Python~ModuleNotFoundError: No module named ‘pydotplus‘

    错误代码 import pydotplus 错误原因 没有安装pydotplus库 解决方案 安装对应的库 pip install pydotplus 重新导入模块 import pydotplus
  • 获取内网IP地址

    工作中遇到一个需求 要求根据不同场地的IP地址 显示相应的大屏页面 首先要配置一下Chrome浏览器 1 在Chrome浏览器中输入 chrome flags 2 搜索 enable webrtc hide local ips with m
  • 量化交易,关于止损止盈的一点思考

    如何设置止损止盈指标才能达到 使损失基本稳定 让盈利充分攀升 止损位设置 如果当天可以卖出 止损位就是成本 1 X 如x设为5 就是股价跌5 就止损 一般就是5 略多一点损失 略多一点是因为可能需要降价保证抛出和交易成本 如果考虑交易成本
  • webSocket详解:技术原理+前后端实现

    webSocket详解 技术原理 前后端实现 一 webSocket技术原理 1 内容简介 websocket就是通过服务器向客户端推送消息 客户端也可以主动向服务器发送消息 是真正的双向平等对话 是一种长连接 只需要通过一次请求进行初始化
  • IDEA设置

    目录 目录 双击idea时默认不打开最后一次的项目 设置黑色 白色主图 编辑 通过CTRL 鼠标滚轮设置字体大小 自动导入包 显示行号与方法分隔符 打开类多后 在多行显示 文件编码UTF 8设定 打开文件按顺序展示 推荐的插件 双击idea
  • 如何用多线程执行 unittest 测试用例实现方案

    前言 使用python做过自动化测试的小伙伴 想必都知道unittest和pytest这两个单元测试框架 其中unittest是python的官方库 功能相对于pytest来要逊色不少 但是uniitest使用上手简单 也受到的很多的小伙伴
  • 企业应该选择哪种区块链

    随着探索如何把区块链应用在各种场景 许多人就想到 也许不需要全世界的人共同参与 也不需要挖矿 我们只需要用到区块链的可信任 可追溯特性 通过较少节点达到拜占庭将军容错 于是私有链就诞生了 但私有链仍是中心化的 难以维持去中心化的优势 因此又
  • webdriver版本不匹配,重新下载webdriver后不知道应该放在哪个文件夹

    1 从官网上面按照对应的版本下载了对应浏览器的webdriver版本 我的是chrome windows 113开头的版本 2 下载并解压之后发现有很多个webdriver exe的程序 正确步骤是解压后复制exe文件放在原来的文件夹即可
  • Ubuntu根目录文件作用分析

    Ubuntu Linux的文件结构与Windows的文件结构不同 Windows将硬盘分成 等盘 也就是分成这些分区 而Linux操作系统不是把硬盘分 成这样的分区 它有一个根目录 用 表示 一个目录就相当于一个文件夹 根目录就相当于Lin
  • 360周鸿祎:互联网好产品六字法则——刚需、痛点、高频

    如何找到好的产品 它必须满足三个条件 刚需 痛点 高频 6月6号 奇虎360创始人董事长兼CEO周鸿祎走上颠覆式创新研习社的讲台 以他的产品经历 带来移动互联网产品观 干货满满 全程无尿点 课程实录分为上 下两部分 研习社根据演讲整理 未经
  • vue Antd单独隐藏Modal.confirm(this.$confirm方式)对话框的默认ok或cancel按钮

    有时候我们需要单独隐藏Modal对话框的默认确定或取消按钮 设置 footer null 会把两个按钮都隐藏 Antd有提供两个参数用于单独修改确定 取消按钮 对于确定按钮 设置 ok button props style display
  • STM32学习记录——74HC595四位数码管显示

    数模管作为STM32的一个重要外设 由于其成本低 稳定 被用于许多场景中 本篇文章来介绍下四位数码管的使用方法 数码管显示 一 数码管的分类 二 74HC595芯片 串入并出 三 原理图 四 代码主要操作 五 代码分析 1 void HC5
  • PyCharm安装

    lt 一 gt PyCharm安装 下载地址 http www jetbrains com pycharm download section windows professional 表示专业版 community 是社区版 推荐安装社区版
  • CSS3 Flexbox轻松实现元素的水平居中和垂直居中

    网上有很多关于Flex的教程 对于Flex的叫法也不一 有的叫Flexbox 有的叫Flex 其实这两种叫法都没有错 只是Flexbox旧一点 而Flex是刚出来不久的东西而已 为了方便说明 赶上新技术 下面我就把这种布局叫Flex布局 元
  • Unity --- 动画脚本

    1 什么是动画 在Unity中 首先如果游戏物体想要在场景中动起来的话 就必须使其对应组件中的属性发生合适的变化 而将属性的变化过程保存下来后形成的就是动画 以后如果想播放动画的话 只需要调用这段保存下来的属性变化就可以了 二 基础知识部分
  • 内存管理之一__align字节对齐

    转 http www cnblogs com ye moooooo p 4601189 html 一 什么是字节对齐 为什么要对齐 现代计算机中内存空间都是按照byte划分的 从理论上讲似乎对任何类型的变量的访问可以从任何地址开始 但实际情
  • 日志 - 客户端及服务端写法

    一 客户端 先来看一个日志类的实现方法 这个日志类也是代表着大多数客户端日志的主流写法 log h 1 ifndef LOG H 2 define LOG H 3 4 include
  • vue-echarts自适应大小的实现

    vue echarts图表自适应用到了vue resize 完整流程为 1 下载vue resize npm i vue resize S 2 main js中引入 import ResizeObserver from vue resize
  • gtj2018如何生成工程量报表_广联达BIM土建计量平台GTJ2018

    一 概述 建造整个世界 度量每种可能 相比13产品 100 项的增加与优化 量筋合 业务扩展10 以上 效率提升20 30 土建BIM模型数据上下游无缝连接 量筋合一 一次建模 无需互导 土建计量业务范围更广 工作效率提升更高 效率快 量筋
  • 【前端】Vue项目:旅游App-(22)detail:房屋信息、房屋设施、插槽

    文章目录 目标 过程与代码 房屋信息部分info 相似结构的组件section 房屋设施facility 效果 总代码 修改或添加的文件 detail section detail facility detail info detail 参