【B站 heima】小兔鲜Vue3 项目学习笔记Day04

文章目录

  • 二级分类
    • 1.整体认识和路由配置
    • 2.面包屑导航功能实现
    • 3. 基础商品列表实现
    • 4. 定制路由滚动行为
  • 详情页
    • 1.整体认识和路由配置
    • 2.基础数据渲染
    • 3.热榜区域实现
    • 4. 图片预览组件封装
    • 5.放大镜-滑块跟随移动
      • 左侧滑块跟随鼠标移动
      • 放大镜-大图效果
    • 6. props适配
    • 7. SKU组件熟悉使用
    • 8. 通用组件统一注册为全局组件
    • 小结

持续更新~~

二级分类

1.整体认识和路由配置

在这里插入图片描述
在这里插入图片描述

步骤:

  • 创建路由组件,静态模板
<!--views/subCategory/index.vue-->
<script setup>


</script>

<template>
    <div class="container ">
        <!-- 面包屑 -->
        <div class="bread-container">
            <el-breadcrumb separator=">">
                <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
                <el-breadcrumb-item :to="{ path: '/' }">居家
                </el-breadcrumb-item>
                <el-breadcrumb-item>居家生活用品</el-breadcrumb-item>
            </el-breadcrumb>
        </div>
        <div class="sub-container">
            <el-tabs>
                <el-tab-pane label="最新商品" name="publishTime"></el-tab-pane>
                <el-tab-pane label="最高人气" name="orderNum"></el-tab-pane>
                <el-tab-pane label="评论最多" name="evaluateNum"></el-tab-pane>
            </el-tabs>
            <div class="body">
                <!-- 商品列表-->
            </div>
        </div>
    </div>

</template>



<style lang="scss" scoped>
.bread-container {
    padding: 25px 0;
    color: #666;
}

.sub-container {
    padding: 20px 10px;
    background-color: #fff;

    .body {
        display: flex;
        flex-wrap: wrap;
        padding: 0 10px;
    }

    .goods-item {
        display: block;
        width: 220px;
        margin-right: 20px;
        padding: 20px 30px;
        text-align: center;

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

        p {
            padding-top: 10px;
        }

        .name {
            font-size: 16px;
        }

        .desc {
            color: #999;
            height: 29px;
        }

        .price {
            color: $priceColor;
            font-size: 20px;
        }
    }

    .pagination-container {
        margin-top: 20px;
        display: flex;
        justify-content: center;
    }


}
</style>
  • 配置路由关系

在这里插入图片描述

//router/index.js
import subCategory from '@/views/subCategory/index.vue'

//在route的children配置二级路由
 {
          path: 'category/sub/:id',
          component: subCategory
 }
  • 修改模板实现跳转
<!--Category/index.vue-->
 <!-- 分类列表 -->
            <div class="sub-list">
                <h3>全部分类</h3>
                <ul>
                    <li v-for="i in categoryData.children" :key="i.id">
                        <RouterLink :to="`/category/sub/${i.id}`">
                            <img :src="i.picture" />
                            <p>{{ i.name }}</p>
                        </RouterLink>
                    </li>
                </ul>
            </div>

最终效果:

在这里插入图片描述

2.面包屑导航功能实现

步骤:

  • 封装接口,获取面包屑数据
/**
	apis/category.js
 * @description: 获取二级分类列表数据
 * @param {*} id 分类id 
 * @return {*}
 */

export const getCategoryFilterAPI = (id) => {
    return request({
        url: '/category/sub/filter',
        params: {
            id
        }
    })
}
  • 调用接口渲染模板
<!-- subCategory/index.vue -->
<script setup>
import { getCategoryFilterAPI } from '@/apis/category.js'
import { ref, onMounted } from 'vue'
import { useRoute } from 'vue-router';

const route = useRoute()
const categoryFilter = ref({})

//获取面包屑导航数据
const getCategoryFilter = async () => {
    const res = await getCategoryFilterAPI(route.params.id)
    categoryFilter.value = res.result
}
onMounted(() => {
    getCategoryFilter()
})
    </script>
<!-- 面包屑 -->
        <div class="bread-container">
            <el-breadcrumb separator=">">
                <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
                <el-breadcrumb-item :to="{ path: `/category/${categoryFilter.parentId}` }">{{ categoryFilter.parentName
                    }}
                </el-breadcrumb-item>
                <el-breadcrumb-item>{{ categoryFilter.name }}</el-breadcrumb-item>
            </el-breadcrumb>
        </div>
  • 测试跳转

可以正常跳转,可以从面包屑这里进行跳转

在这里插入图片描述

3. 基础商品列表实现

在这里插入图片描述

实现流程:

  • 实现基础列表渲染(基础参数)

    • 封装接口
    /**             apis/category.js
     * @description: 获取导航数据
     * @data { 
            categoryId: 1005000 ,
            page: 1,
            pageSize: 20,
            sortField: 'publishTime' | 'orderNum' | 'evaluateNum'
        } 
     * @return {*}
     */
    export const getSubCategoryAPI = (data) => {
        return request({
            url: '/category/goods/temporary',
            method: 'POST',
            data
        })
    }
    
    • 准备基础参数
    //获取基础列表数据   views/subCategory/index.vue
    const baseListData = ref({
        category: route.params.id,
        page: 1,
        pageSize: 20,
        sortField: 'publishTime'
    })
    const getSubCategory = async () => {
        const res = await getSubCategoryAPI(baseListData.value)  //数据要传进去
        // console.log(res)
        baseListData.value = res.result.items
    }
    
    onMounted(() => getSubCategory())
    
    • 获取数据渲染列表,使用我们之前封装的GoodItem组件
    <script>import GoodItem from '../Home/components/GoodItem.vue'; </script>
    <div class="body">
                    <!-- 商品列表-->
                    <GoodItem :goods="goods" v-for="goods in baseListData" :key="goods.id" />
                </div>
    
  • 添加额外参数实现筛选功能

    • 获取激活项数据。使用新参数发送请求重新渲染列表

      使用的是elementPlusTabs标签,这个v-model绑定的是选项中name的值
      在这里插入图片描述

      使用这个方法,当选项改变时执行回调

      在这里插入图片描述

       <!-- tab切换 subCategory.vue-->
                  <el-tabs v-model="baseListData.sortField" @tab-change="tabChange">
                      <el-tab-pane label="最新商品" name="publishTime"></el-tab-pane>
                      <el-tab-pane label="最高人气" name="orderNum"></el-tab-pane>
                      <el-tab-pane label="评论最多" name="evaluateNum"></el-tab-pane>
                  </el-tabs>
      
      //切换tab执行的回调    subCategory.vue
      const tabChange = () => {
          baseListData.value.sortField = 1  //重置页数
          getSubCategory()  //重新请求数据
      }
      
  • 无限加载功能实现

在这里插入图片描述

使用elementPlus提供的v-infinite-scroll指令监听是否满足触底条件,满足加载条件时让页数参数加一获取下一页数据,做新老数据拼接渲染

步骤:

  • 配置v-infinite-scroll

在这里插入图片描述

相关代码  subCategory/index.vue
<div class="body" v-infinite-scroll="load">
    
    
//load回调
 const load = ()=>{
    console.log('加载更多数据喽')
}
  • 页数+1,获取下一页数据
	//页数加一
    baseListData.value.page++
    //获取下一页的数据
    const res = await getSubCategoryAPI(baseListData.value)    
  • 新老数据拼接
//将新旧数据拼接  使用...,把拼接的数组重新赋值给baseListData
    baseListData.value = [...baseListData.value, ...res.value.items]
  • 加载完毕结束监听
const disabled = ref(false)
//如果items为空,则停止加载
    if (baseListData.items.length === 0) {
        disabled.value = true
    }

  <div class="body" v-infinite-scroll="load" :infinite-scroll-disabled="disabled">

subCategory/index.vue 完整代码:

<!-- subCategory/index.vue -->
<script setup>
import { getCategoryFilterAPI, getSubCategoryAPI } from '@/apis/category.js'
import { ref, onMounted } from 'vue'
import { useRoute } from 'vue-router';
import GoodItem from '../Home/components/GoodItem.vue';

const route = useRoute()

//获取面包屑导航数据
const categoryFilter = ref({})
const getCategoryFilter = async () => {
    const res = await getCategoryFilterAPI(route.params.id)
    categoryFilter.value = res.result
}
onMounted(() => {
    getCategoryFilter()
})

//获取基础列表数据
const baseListData = ref({
    category: route.params.id,
    page: 1,
    pageSize: 20,
    sortField: 'publishTime'
})
const getSubCategory = async () => {
    const res = await getSubCategoryAPI(baseListData.value)  //数据要传进去
    // console.log(res)
    baseListData.value = res.result.items
}

onMounted(() => getSubCategory())

//切换tab执行的回调
const tabChange = () => {
    baseListData.value.page = 1  //重置页数
    // console.log(baseListData.value.sortField)
    getSubCategory()  //重新请求数据
}

//无限滚动
const disabled = ref(false)
const load = async () => {
    // console.log('加载更多数据喽')
    //页数加一
    baseListData.value.page++
    //获取下一页的数据
    const res = await getSubCategoryAPI(baseListData.value)
    //将新旧数据拼接  使用...,把拼接的数组重新赋值给baseListData
    baseListData.value = [...baseListData.value, ...res.value.items]
    //如果items为空,则停止加载
    if (baseListData.items.length === 0) {
        disabled.value = true
    }
}
</script>

<template>
    <div class="container ">
        <!-- 面包屑 -->
        <div class="bread-container">
            <el-breadcrumb separator=">">
                <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
                <el-breadcrumb-item :to="{ path: `/category/${categoryFilter.parentId}` }">{{ categoryFilter.parentName
                    }}
                </el-breadcrumb-item>
                <el-breadcrumb-item>{{ categoryFilter.name }}</el-breadcrumb-item>
            </el-breadcrumb>
        </div>
        <div class="sub-container">
            <!-- tab切换 -->
            <el-tabs v-model="baseListData.sortField" @tab-change="tabChange">
                <el-tab-pane label="最新商品" name="publishTime"></el-tab-pane>
                <el-tab-pane label="最高人气" name="orderNum"></el-tab-pane>
                <el-tab-pane label="评论最多" name="evaluateNum"></el-tab-pane>
            </el-tabs>
            <div class="body" v-infinite-scroll="load" :infinite-scroll-disabled="disabled">
                <!-- 商品列表-->
                <GoodItem :goods="goods" v-for="goods in baseListData" :key="goods.id" />
            </div>
        </div>
    </div>

</template>



<style lang="scss" scoped>
.bread-container {
    padding: 25px 0;
    color: #666;
}

.sub-container {
    padding: 20px 10px;
    background-color: #fff;

    .body {
        display: flex;
        flex-wrap: wrap;
        padding: 0 10px;
    }

    .goods-item {
        display: block;
        width: 220px;
        margin-right: 20px;
        padding: 20px 30px;
        text-align: center;

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

        p {
            padding-top: 10px;
        }

        .name {
            font-size: 16px;
        }

        .desc {
            color: #999;
            height: 29px;
        }

        .price {
            color: $priceColor;
            font-size: 20px;
        }
    }

    .pagination-container {
        margin-top: 20px;
        display: flex;
        justify-content: center;
    }


}
</style>

4. 定制路由滚动行为

切换路由,自动滚动到页面的顶部

如何配置vue-router支持scrollBehavior配置项,可以指定路由切换的滚动位置。

//router/index.js

 //路由滚动行为定制
  scrollBehavior() {
    return {
      top: 0
    }
  }

详情页

1.整体认识和路由配置

路由:

  • 创建详情组件 Views/Detail/index.vue
<script setup>


</script>

<template>
  <div class="xtx-goods-page">
    <div class="container">
      <div class="bread-container">
        <el-breadcrumb separator=">">
          <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
          <el-breadcrumb-item :to="{ path: '/' }">母婴
          </el-breadcrumb-item>
          <el-breadcrumb-item :to="{ path: '/' }">跑步鞋
          </el-breadcrumb-item>
          <el-breadcrumb-item>抓绒保暖,毛毛虫子儿童运动鞋</el-breadcrumb-item>
        </el-breadcrumb>
      </div>
      <!-- 商品信息 -->
      <div class="info-container">
        <div>
          <div class="goods-info">
            <div class="media">
              <!-- 图片预览区 -->

              <!-- 统计数量 -->
              <ul class="goods-sales">
                <li>
                  <p>销量人气</p>
                  <p> 100+ </p>
                  <p><i class="iconfont icon-task-filling"></i>销量人气</p>
                </li>
                <li>
                  <p>商品评价</p>
                  <p>200+</p>
                  <p><i class="iconfont icon-comment-filling"></i>查看评价</p>
                </li>
                <li>
                  <p>收藏人气</p>
                  <p>300+</p>
                  <p><i class="iconfont icon-favorite-filling"></i>收藏商品</p>
                </li>
                <li>
                  <p>品牌信息</p>
                  <p>400+</p>
                  <p><i class="iconfont icon-dynamic-filling"></i>品牌主页</p>
                </li>
              </ul>
            </div>
            <div class="spec">
              <!-- 商品信息区 -->
              <p class="g-name"> 抓绒保暖,毛毛虫儿童鞋 </p>
              <p class="g-desc">好穿 </p>
              <p class="g-price">
                <span>200</span>
                <span> 100</span>
              </p>
              <div class="g-service">
                <dl>
                  <dt>促销</dt>
                  <dd>12月好物放送,App领券购买直降120元</dd>
                </dl>
                <dl>
                  <dt>服务</dt>
                  <dd>
                    <span>无忧退货</span>
                    <span>快速退款</span>
                    <span>免费包邮</span>
                    <a href="javascript:;">了解详情</a>
                  </dd>
                </dl>
              </div>
              <!-- sku组件 -->

              <!-- 数据组件 -->

              <!-- 按钮组件 -->
              <div>
                <el-button size="large" class="btn">
                  加入购物车
                </el-button>
              </div>

            </div>
          </div>
          <div class="goods-footer">
            <div class="goods-article">
              <!-- 商品详情 -->
              <div class="goods-tabs">
                <nav>
                  <a>商品详情</a>
                </nav>
                <div class="goods-detail">
                  <!-- 属性 -->
                  <ul class="attrs">
                    <li v-for="item in 3" :key="item.value">
                      <span class="dt">白色</span>
                      <span class="dd">纯棉</span>
                    </li>
                  </ul>
                  <!-- 图片 -->

                </div>
              </div>
            </div>
            <!-- 24热榜+专题推荐 -->
            <div class="goods-aside">

            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>


<style scoped lang='scss'>
.xtx-goods-page {
  .goods-info {
    min-height: 600px;
    background: #fff;
    display: flex;

    .media {
      width: 580px;
      height: 600px;
      padding: 30px 50px;
    }

    .spec {
      flex: 1;
      padding: 30px 30px 30px 0;
    }
  }

  .goods-footer {
    display: flex;
    margin-top: 20px;

    .goods-article {
      width: 940px;
      margin-right: 20px;
    }

    .goods-aside {
      width: 280px;
      min-height: 1000px;
    }
  }

  .goods-tabs {
    min-height: 600px;
    background: #fff;
  }

  .goods-warn {
    min-height: 600px;
    background: #fff;
    margin-top: 20px;
  }

  .number-box {
    display: flex;
    align-items: center;

    .label {
      width: 60px;
      color: #999;
      padding-left: 10px;
    }
  }

  .g-name {
    font-size: 22px;
  }

  .g-desc {
    color: #999;
    margin-top: 10px;
  }

  .g-price {
    margin-top: 10px;

    span {
      &::before {
        content: "¥";
        font-size: 14px;
      }

      &:first-child {
        color: $priceColor;
        margin-right: 10px;
        font-size: 22px;
      }

      &:last-child {
        color: #999;
        text-decoration: line-through;
        font-size: 16px;
      }
    }
  }

  .g-service {
    background: #f5f5f5;
    width: 500px;
    padding: 20px 10px 0 10px;
    margin-top: 10px;

    dl {
      padding-bottom: 20px;
      display: flex;
      align-items: center;

      dt {
        width: 50px;
        color: #999;
      }

      dd {
        color: #666;

        &:last-child {
          span {
            margin-right: 10px;

            &::before {
              content: "•";
              color: $xtxColor;
              margin-right: 2px;
            }
          }

          a {
            color: $xtxColor;
          }
        }
      }
    }
  }

  .goods-sales {
    display: flex;
    width: 400px;
    align-items: center;
    text-align: center;
    height: 140px;

    li {
      flex: 1;
      position: relative;

      ~li::after {
        position: absolute;
        top: 10px;
        left: 0;
        height: 60px;
        border-left: 1px solid #e4e4e4;
        content: "";
      }

      p {
        &:first-child {
          color: #999;
        }

        &:nth-child(2) {
          color: $priceColor;
          margin-top: 10px;
        }

        &:last-child {
          color: #666;
          margin-top: 10px;

          i {
            color: $xtxColor;
            font-size: 14px;
            margin-right: 2px;
          }

          &:hover {
            color: $xtxColor;
            cursor: pointer;
          }
        }
      }
    }
  }
}

.goods-tabs {
  min-height: 600px;
  background: #fff;

  nav {
    height: 70px;
    line-height: 70px;
    display: flex;
    border-bottom: 1px solid #f5f5f5;

    a {
      padding: 0 40px;
      font-size: 18px;
      position: relative;

      >span {
        color: $priceColor;
        font-size: 16px;
        margin-left: 10px;
      }
    }
  }
}

.goods-detail {
  padding: 40px;

  .attrs {
    display: flex;
    flex-wrap: wrap;
    margin-bottom: 30px;

    li {
      display: flex;
      margin-bottom: 10px;
      width: 50%;

      .dt {
        width: 100px;
        color: #999;
      }

      .dd {
        flex: 1;
        color: #666;
      }
    }
  }

  >img {
    width: 100%;
  }
}

.btn {
  margin-top: 20px;

}

.bread-container {
  padding: 25px 0;
}
</style>
  • 绑定路由关系(参数),绑定模板
//router/index.js
import Detail from '@/views/Detail/index.vue'

//二级路由
{
          path: 'detail/:id',
          component: Detail
        }

<!--HomeNew.vue--> 
<RouterLink :to="`/detail/${item.id}`">
                        <img :src="item.picture" alt="" />
                        <p class="name">{{ item.name }}</p>
                        <p class="price">&yen;{{ item.price }}</p>
                    </RouterLink>

效果:

在这里插入图片描述

2.基础数据渲染

完成下图渲染,使用的统一接口

在这里插入图片描述

步骤:

  • 封装接口
//apis/detail.js
//获取详情数据
export function getDetailAPI(id) {
    return httpInstance({
        url: '/goods',
        params: {
            id
        }
    })
}
  • 获取数据
<!--Detail/index.vue-->
<script setup>
import { ref, onMounted } from 'vue';
import { getDetailAPI } from '@/apis/detail';
import { useRoute } from 'vue-router';

const goods = ref({})
const route = useRoute()
const getGoods = async () => {
    const res = await getDetailAPI(route.params.id)
    goods.value = res.result
}
onMounted(() => getGoods())
</script>

在这里插入图片描述

  • 渲染模板

面包屑导航

<!-- goods一开始时空对象,一开始访问是undefined[0] Detail/index.vue-->
                    <!-- 解决办法:1.可选链 2.v-if手动控制渲染时机,保证只有数据存在才渲染 -->
                    <el-breadcrumb-item :to="{ path: `/category/${goods.categories?.[1].id}` }">{{
                        goods.categories?.[1].name }}</el-breadcrumb-item>
                    <el-breadcrumb-item :to="{ path: `/category/${goods.categories?.[0].id}` }">{{
                        goods.categories?.[0].name }}
                    </el-breadcrumb-item>
<!-- 统计数量 Detail/index.vue-->
                            <ul class="goods-sales">
                                <li>
                                    <p>销量人气</p>
                                    <p> {{ goods.salesCount }}+ </p>
                                    <p><i class="iconfont icon-task-filling"></i>销量人气</p>
                                </li>
                                <li>
                                    <p>商品评价</p>
                                    <p>{{ goods.commitCount }}+</p>
                                    <p><i class="iconfont icon-comment-filling"></i>查看评价</p>
                                </li>
                                <li>
                                    <p>收藏人气</p>
                                    <p>{{ goods.collectCount }}+</p>
                                    <p><i class="iconfont icon-favorite-filling"></i>收藏商品</p>
                                </li>
                                <li>
                                    <p>品牌信息</p>
                                    <p>{{
                        goods.brand.name }}+</p>
                                    <p><i class="iconfont icon-dynamic-filling"></i>品牌主页</p>
                                </li>
                            </ul>

在这里插入图片描述

 <!-- 商品信息区 Detail/index.vue-->
                            <p class="g-name"> {{ goods.name }} </p>
                            <p class="g-desc">{{ goods.desc }}</p>
                            <p class="g-price">
                                <span>{{ goods.oldPrice }}</span>
                                <span> {{ goods.price }}</span>
                            </p>

在这里插入图片描述

<!-- 商品详情  Detail/index.vue-->
                            <div class="goods-tabs">
                                <nav>
                                    <a>商品详情</a>
                                </nav>
                                <div class="goods-detail">
                                    <!-- 属性 -->
                                    <ul class="attrs">
                                        <li v-for="item in goods.details?.properties" :key="item.value">
                                            <span class="dt">{{ item.name }}</span>
                                            <span class="dd">{{ item.value }}</span>
                                        </li>
                                    </ul>
                                    <!-- 图片 -->
                                    <img v-for="img in goods.details?.pictures" :src="img" :key="img" alt="" />
                                </div>
                            </div>
                        </div>

在这里插入图片描述

3.热榜区域实现

在这里插入图片描述

步骤:

  • 封装组件(因为结构一致)
<!-- DetailHot.vue -->
<script setup>

</script>


<template>
    <div class="goods-hot">
        <h3>周日榜单</h3>
        <!-- 商品区块 -->
        <RouterLink to="/" class="goods-item" v-for="item in 3" :key="item.id">
            <img :src="item.picture" alt="" />
            <p class="name ellipsis">一双男鞋</p>
            <p class="desc ellipsis">一双好穿的男鞋</p>
            <p class="price">&yen;200.00</p>
        </RouterLink>
    </div>
</template>


<style scoped lang="scss">
.goods-hot {
    h3 {
        height: 70px;
        background: $helpColor;
        color: #fff;
        font-size: 18px;
        line-height: 70px;
        padding-left: 25px;
        margin-bottom: 10px;
        font-weight: normal;
    }

    .goods-item {
        display: block;
        padding: 20px 30px;
        text-align: center;
        background: #fff;

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

        p {
            padding-top: 10px;
        }

        .name {
            font-size: 16px;
        }

        .desc {
            color: #999;
            height: 29px;
        }

        .price {
            color: $priceColor;
            font-size: 20px;
        }
    }
}
</style>

在父组件中使用两次这个组件

 <!-- 24热榜+专题推荐 Detail/index.vue-->
import DetailHot from './components/DetailHot.vue'


<div class="goods-aside">
          <!-- 24小时 -->
          <DetailHot />
          <!-- 周日 -->
          <DetailHot />
</div>
  • 获取渲染基础数据
/**
 * 获取热榜商品
 * @param {Number} id - 商品id
 * @param {Number} type - 1代表24小时热销榜 2代表周热销榜
 * @param {Number} limit - 获取个数
 */
export const getHotGoodsAPI = ({ id, type, limit = 3 }) => {
    return httpInstance({
        url: '/goods/hot',
        params: {
            id,
            type,
            limit
        }
    })
}


<!--Detail/components/DetailHot.vue-->
<script setup>
import { ref } from 'vue'
import { getHotGoodsAPI } from '@/apis/detail'
import { useRoute } from 'vue-router'

const goodList = ref([])
const route = useRoute()
const getHotList = async () => {
  const res = await getHotGoodsAPI({
    id: route.params.id,
    type: 1
  })
  goodList.value = res.result
}
getHotList()


</script>
<template>
    <div class="goods-hot">
        <h3>周日榜单</h3>
        <!-- 商品区块 -->
        <RouterLink to="/" class="goods-item" v-for="item in goodList" :key="item.id">
            <img :src="item.picture" alt="" />
            <p class="name ellipsis">{{ item.name }}</p>
            <p class="desc ellipsis">{{ item.desc }}</p>
            <p class="price">&yen;{{ item.price }}</p>
        </RouterLink>
    </div>
</template>

在这里插入图片描述

  • 适配不同标题Title和内容

定义props,给两个组件绑定不同的数据

//热榜类型 1为24小时热榜 2为周热榜  DetailHot.vue
const props = defineProps({
    hotType: {
        type: Number
    }
})
const TYPEMAP = {
    1: '24小时热榜',
    2: '周热榜'
}
const title = computed(() => TYPEMAP[props.hotType])


  <h3>{{ title }}</h3>

type决定着获取哪种数据,我们将1替换成props.hotType即可。

const getHotList = async () => {
    const res = await getHotGoodsAPI({
        id: route.params.id,
        type: props.hotType
    })
    goodList.value = res.result

4. 图片预览组件封装

在这里插入图片描述

功能:

  • 小图切换大图功能

思路:维护一个数组图片列表,鼠标划入小图记录当前小图下标值,通过下标值在数组中取对应图片,显示到大图位置

步骤:

​ 准备组件静态模板(包括图片数据列表)

<!--component/ImageView/index.vue-->
<script setup>
// 图片列表
const imageList = [
  "https://yanxuan-item.nosdn.127.net/d917c92e663c5ed0bb577c7ded73e4ec.png",
  "https://yanxuan-item.nosdn.127.net/e801b9572f0b0c02a52952b01adab967.jpg",
  "https://yanxuan-item.nosdn.127.net/b52c447ad472d51adbdde1a83f550ac2.jpg",
  "https://yanxuan-item.nosdn.127.net/f93243224dc37674dfca5874fe089c60.jpg",
  "https://yanxuan-item.nosdn.127.net/f881cfe7de9a576aaeea6ee0d1d24823.jpg"
]
</script>


<template>
  <div class="goods-image">
    <!-- 左侧大图-->
    <div class="middle" ref="target">
      <img :src="imageList[0]" alt="" />
      <!-- 蒙层小滑块 -->
      <div class="layer" :style="{ left: `0px`, top: `0px` }"></div>
    </div>
    <!-- 小图列表 -->
    <ul class="small">
      <li v-for="(img, i) in imageList" :key="i">
        <img :src="img" alt="" />
      </li>
    </ul>
    <!-- 放大镜大图 -->
    <div class="large" :style="[
      {
        backgroundImage: `url(${imageList[0]})`,
        backgroundPositionX: `0px`,
        backgroundPositionY: `0px`,
      },
    ]" v-show="false"></div>
  </div>
</template>

<style scoped lang="scss">
.goods-image {
  width: 480px;
  height: 400px;
  position: relative;
  display: flex;

  .middle {
    width: 400px;
    height: 400px;
    background: #f5f5f5;
  }

  .large {
    position: absolute;
    top: 0;
    left: 412px;
    width: 400px;
    height: 400px;
    z-index: 500;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
    background-repeat: no-repeat;
    // 背景图:盒子的大小 = 2:1  将来控制背景图的移动来实现放大的效果查看 background-position
    background-size: 800px 800px;
    background-color: #f8f8f8;
  }

  .layer {
    width: 200px;
    height: 200px;
    background: rgba(0, 0, 0, 0.2);
    // 绝对定位 然后跟随咱们鼠标控制left和top属性就可以让滑块移动起来
    left: 0;
    top: 0;
    position: absolute;
  }

  .small {
    width: 80px;

    li {
      width: 68px;
      height: 68px;
      margin-left: 12px;
      margin-bottom: 15px;
      cursor: pointer;

      &:hover,
      &.active {
        border: 2px solid $xtxColor;
      }
    }
  }
}
</style>

引入,并使用组件

<!-- 图片预览区 Detail/index.vue-->
<ImageView />

为小图绑定事件mouseEnter,记录当前激活下标值activeIndex,通过下标i切换大图显示,通过下标实现激活状态显示

//ImageView/index.vue
const activeIndex = ref(null)
//鼠标移入小图回调
const mouseEnter = (i) => {
    activeIndex.value = i
}


 <!-- 小图列表 -->
        <ul class="small">
            <li v-for="(img, i) in imageList" :key="i" @mouseenter="mouseEnter(i)">
                <img :src="img" alt="" />
            </li>
        </ul>

在这里插入图片描述

鼠标离开也激活

 <li v-for="(img, i) in   imageList  " :key="i" @mouseenter="mouseEnter(i)"
                :class="{ active: i === activeIndex }">
                
                
 //激活样式scss
 &.active {
                border: 2px solid $xtxColor;
            }

5.放大镜-滑块跟随移动

核心功能:左侧滑块跟随鼠标移动、右侧大图放大效果实现、滑块和大图的显示与隐藏

左侧滑块跟随鼠标移动

使用useMouseInElement

在这里插入图片描述

<script setup>
import { ref } from 'vue';
import { useMouseInElement } from '@vueuse/core';
import { watch } from 'vue';
// 图片列表
const imageList = [
    "https://yanxuan-item.nosdn.127.net/d917c92e663c5ed0bb577c7ded73e4ec.png",
    "https://yanxuan-item.nosdn.127.net/e801b9572f0b0c02a52952b01adab967.jpg",
    "https://yanxuan-item.nosdn.127.net/b52c447ad472d51adbdde1a83f550ac2.jpg",
    "https://yanxuan-item.nosdn.127.net/f93243224dc37674dfca5874fe089c60.jpg",
    "https://yanxuan-item.nosdn.127.net/f881cfe7de9a576aaeea6ee0d1d24823.jpg"
]
const activeIndex = ref(null)
//鼠标移入小图回调
const mouseEnter = (i) => {
    activeIndex.value = i
}

//2.获取鼠标相对位置
const target = ref(null)
const { elementX, elementY, isOutside } = useMouseInElement(target)

//3.控制滑块跟随鼠标移动,监听elementX/Y变化,一旦变化,重新设置left/top
const left = ref(0)
const top = ref(0)
watch([elementX, elementY], () => {
    //有效范围控制滑块距离
    //横向
    if (elementX.value > 100 && elementX.value < 300) {
        left.value = elementX.value - 100
    }
    //纵向
    if (elementY.value > 100 && elementY.value < 300) {
        top.value = elementY.value - 100
    }

    //处理边界
    if (elementX.value > 300) { left.value = 200 }
    if (elementX.value < 100) { left.value = 0 }

    if (elementY.value > 300) { top.value = 200 }
    if (elementY.value < 100) { top.value = 0 }
})
</script>


<template>
    <div class="goods-image">
        <!-- 左侧大图-->
        <div class="middle" ref="target">
            <img :src="imageList[activeIndex]" alt="" />
            <!-- 蒙层小滑块 -->
            <div class="layer" :style="{ left: `${left}px`, top: `${top}px` }"></div>
        </div>
        <!-- 小图列表 -->
        <ul class="small">
            <li v-for="(img, i) in   imageList  " :key="i" @mouseenter="mouseEnter(i)"
                :class="{ active: i === activeIndex }">
                <img :src="img" alt="" />
            </li>
        </ul>
        <!-- 放大镜大图 -->
        <div class="large" :style="[
                {
                    backgroundImage: `url(${imageList[0]})`,
                    backgroundPositionX: `0px`,
                    backgroundPositionY: `0px`,
                },
            ]
                " v-show="false"></div>
    </div>
</template>

<style scoped lang="scss">
.goods-image {
    width: 480px;
    height: 400px;
    position: relative;
    display: flex;

    .middle {
        width: 400px;
        height: 400px;
        background: #f5f5f5;
    }

    .large {
        position: absolute;
        top: 0;
        left: 412px;
        width: 400px;
        height: 400px;
        z-index: 500;
        box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
        background-repeat: no-repeat;
        // 背景图:盒子的大小 = 2:1  将来控制背景图的移动来实现放大的效果查看 background-position
        background-size: 800px 800px;
        background-color: #f8f8f8;
    }

    .layer {
        width: 200px;
        height: 200px;
        background: rgba(0, 0, 0, 0.2);
        // 绝对定位 然后跟随咱们鼠标控制left和top属性就可以让滑块移动起来
        left: 0;
        top: 0;
        position: absolute;
    }

    .small {
        width: 80px;

        li {
            width: 68px;
            height: 68px;
            margin-left: 12px;
            margin-bottom: 15px;
            cursor: pointer;

            &:hover,
            &.active {
                border: 2px solid $xtxColor;
            }
        }
    }
}
</style>

放大镜-大图效果

大图的宽高是小图的两倍

实现思路:大图的移动方向和滑动移动方向相反,且数值两倍

<script setup>
import { ref } from 'vue';
import { useMouseInElement } from '@vueuse/core';
import { watch } from 'vue';
// 图片列表
const imageList = [
    "https://yanxuan-item.nosdn.127.net/d917c92e663c5ed0bb577c7ded73e4ec.png",
    "https://yanxuan-item.nosdn.127.net/e801b9572f0b0c02a52952b01adab967.jpg",
    "https://yanxuan-item.nosdn.127.net/b52c447ad472d51adbdde1a83f550ac2.jpg",
    "https://yanxuan-item.nosdn.127.net/f93243224dc37674dfca5874fe089c60.jpg",
    "https://yanxuan-item.nosdn.127.net/f881cfe7de9a576aaeea6ee0d1d24823.jpg"
]
const activeIndex = ref(null)
//鼠标移入小图回调
const mouseEnter = (i) => {
    activeIndex.value = i
}

//2.获取鼠标相对位置
const target = ref(null)
const { elementX, elementY, isOutside } = useMouseInElement(target)

//3.控制滑块跟随鼠标移动,监听elementX/Y变化,一旦变化,重新设置left/top
const left = ref(0)
const top = ref(0)
const positionX = ref(0)
const positionY = ref(0)
watch([elementX, elementY], () => {
    //如果鼠标未移入盒子,下列逻辑不执行
    if (isOutside.value) return

    //有效范围控制滑块距离
    //横向
    if (elementX.value > 100 && elementX.value < 300) {
        left.value = elementX.value - 100
    }
    //纵向
    if (elementY.value > 100 && elementY.value < 300) {
        top.value = elementY.value - 100
    }

    //处理边界
    if (elementX.value > 300) { left.value = 200 }
    if (elementX.value < 100) { left.value = 0 }

    if (elementY.value > 300) { top.value = 200 }
    if (elementY.value < 100) { top.value = 0 }

    //控制大图的显示
    positionX.value = -left.value * 2
    positionY.value = -top.value * 2
})
</script>


<template>
    <div class="goods-image">
        <!-- 左侧大图-->
        <div class="middle" ref="target">
            <img :src="imageList[activeIndex]" alt="" />
            <!-- 蒙层小滑块 -->
            <div class="layer" v-show="!isOutside" :style="{ left: `${left}px`, top: `${top}px` }"></div>
        </div>
        <!-- 小图列表 -->
        <ul class="small">
            <li v-for="(img, i) in   imageList  " :key="i" @mouseenter="mouseEnter(i)"
                :class="{ active: i === activeIndex }">
                <img :src="img" alt="" />
            </li>
        </ul>
        <!-- 放大镜大图 -->
        <div class="large" :style="[
                {
                    backgroundImage: `url(${imageList[0]})`,
                    backgroundPositionX: `${positionX}px`,
                    backgroundPositionY: `${positionY}px`,
                },
            ]
                " v-show="!isOutside"></div>
    </div>
</template>

<style scoped lang="scss">
.goods-image {
    width: 480px;
    height: 400px;
    position: relative;
    display: flex;

    .middle {
        width: 400px;
        height: 400px;
        background: #f5f5f5;
    }

    .large {
        position: absolute;
        top: 0;
        left: 412px;
        width: 400px;
        height: 400px;
        z-index: 500;
        box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
        background-repeat: no-repeat;
        // 背景图:盒子的大小 = 2:1  将来控制背景图的移动来实现放大的效果查看 background-position
        background-size: 800px 800px;
        background-color: #f8f8f8;
    }

    .layer {
        width: 200px;
        height: 200px;
        background: rgba(0, 0, 0, 0.2);
        // 绝对定位 然后跟随咱们鼠标控制left和top属性就可以让滑块移动起来
        left: 0;
        top: 0;
        position: absolute;
    }

    .small {
        width: 80px;

        li {
            width: 68px;
            height: 68px;
            margin-left: 12px;
            margin-bottom: 15px;
            cursor: pointer;

            &:hover,
            &.active {
                border: 2px solid $xtxColor;
            }
        }
    }
}
</style>

在这里插入图片描述

6. props适配

//ImageView.vue
defineProps({
    imageList: {
        type: Array,
        default: () => { }
    }
})
 <!-- 图片预览区   Detail/index.vue-->
<ImageView :image-list="goods.mainPictures"/>

7. SKU组件熟悉使用

在实际工作中,经常遇到别人写好的组件。熟悉一个三方组件,重点看什么?

答:props和emit。验证必要数据是否交互功能正常,点击选择规格是否正常产出数据。

三方 文件准备好了,就是资料里的XtxSku(一个index.vue,一个power-set.js文件)文件夹,粘到components文件夹下即可

<!-- XtsSku/index.vue -->
<template>
  <div class="goods-sku">
    <dl v-for="item in goods.specs" :key="item.id">
      <dt>{{ item.name }}</dt>
      <dd>
        <template v-for="val in item.values" :key="val.name">
          <img :class="{ selected: val.selected, disabled: val.disabled }" @click="clickSpecs(item, val)"
            v-if="val.picture" :src="val.picture" />
          <span :class="{ selected: val.selected, disabled: val.disabled }" @click="clickSpecs(item, val)" v-else>{{
      val.name
    }}</span>
        </template>
      </dd>
    </dl>
  </div>
</template>
<script>
import { watchEffect } from 'vue'
import getPowerSet from './power-set'
const spliter = '★'
// 根据skus数据得到路径字典对象
const getPathMap = (skus) => {
  const pathMap = {}
  if (skus && skus.length > 0) {
    skus.forEach(sku => {
      // 1. 过滤出有库存有效的sku
      if (sku.inventory) {
        // 2. 得到sku属性值数组
        const specs = sku.specs.map(spec => spec.valueName)
        // 3. 得到sku属性值数组的子集
        const powerSet = getPowerSet(specs)
        // 4. 设置给路径字典对象
        powerSet.forEach(set => {
          const key = set.join(spliter)
          // 如果没有就先初始化一个空数组
          if (!pathMap[key]) {
            pathMap[key] = []
          }
          pathMap[key].push(sku.id)
        })
      }
    })
  }
  return pathMap
}

// 初始化禁用状态
function initDisabledStatus(specs, pathMap) {
  if (specs && specs.length > 0) {
    specs.forEach(spec => {
      spec.values.forEach(val => {
        // 设置禁用状态
        val.disabled = !pathMap[val.name]
      })
    })
  }
}

// 得到当前选中规格集合
const getSelectedArr = (specs) => {
  const selectedArr = []
  specs.forEach((spec, index) => {
    const selectedVal = spec.values.find(val => val.selected)
    if (selectedVal) {
      selectedArr[index] = selectedVal.name
    } else {
      selectedArr[index] = undefined
    }
  })
  return selectedArr
}

// 更新按钮的禁用状态
const updateDisabledStatus = (specs, pathMap) => {
  // 遍历每一种规格
  specs.forEach((item, i) => {
    // 拿到当前选择的项目
    const selectedArr = getSelectedArr(specs)
    // 遍历每一个按钮
    item.values.forEach(val => {
      if (!val.selected) {
        selectedArr[i] = val.name
        // 去掉undefined之后组合成key
        const key = selectedArr.filter(value => value).join(spliter)
        val.disabled = !pathMap[key]
      }
    })
  })
}


export default {
  name: 'XtxGoodSku',
  props: {
    // specs:所有的规格信息  skus:所有的sku组合
    goods: {
      type: Object,
      default: () => ({ specs: [], skus: [] })
    }
  },
  emits: ['change'],
  setup(props, { emit }) {
    let pathMap = {}
    watchEffect(() => {
      // 得到所有字典集合
      pathMap = getPathMap(props.goods.skus)
      // 组件初始化的时候更新禁用状态
      initDisabledStatus(props.goods.specs, pathMap)
    })

    const clickSpecs = (item, val) => {
      if (val.disabled) return false
      // 选中与取消选中逻辑
      if (val.selected) {
        val.selected = false
      } else {
        item.values.forEach(bv => { bv.selected = false })
        val.selected = true
      }
      // 点击之后再次更新选中状态
      updateDisabledStatus(props.goods.specs, pathMap)
      // 把选择的sku信息传出去给父组件
      // 触发change事件将sku数据传递出去
      const selectedArr = getSelectedArr(props.goods.specs).filter(value => value)
      // 如果选中得规格数量和传入得规格总数相等则传出完整信息(都选择了)
      // 否则传出空对象
      if (selectedArr.length === props.goods.specs.length) {
        // 从路径字典中得到skuId
        const skuId = pathMap[selectedArr.join(spliter)][0]
        const sku = props.goods.skus.find(sku => sku.id === skuId)
        // 传递数据给父组件
        emit('change', {
          skuId: sku.id,
          price: sku.price,
          oldPrice: sku.oldPrice,
          inventory: sku.inventory,
          specsText: sku.specs.reduce((p, n) => `${p} ${n.name}${n.valueName}`, '').trim()
        })
      } else {
        emit('change', {})
      }
    }
    return { clickSpecs }
  }
}
</script>

<style scoped lang="scss">
@mixin sku-state-mixin {
  border: 1px solid #e4e4e4;
  margin-right: 10px;
  cursor: pointer;

  &.selected {
    border-color: $xtxColor;
  }

  &.disabled {
    opacity: 0.6;
    border-style: dashed;
    cursor: not-allowed;
  }
}

.goods-sku {
  padding-left: 10px;
  padding-top: 20px;

  dl {
    display: flex;
    padding-bottom: 20px;
    align-items: center;

    dt {
      width: 50px;
      color: #999;
    }

    dd {
      flex: 1;
      color: #666;

      >img {
        width: 50px;
        height: 50px;
        margin-bottom: 4px;
        @include sku-state-mixin;
      }

      >span {
        display: inline-block;
        height: 30px;
        line-height: 28px;
        padding: 0 20px;
        margin-bottom: 4px;
        @include sku-state-mixin;
      }
    }
  }
}
</style>
//power-set.js
export default function bwPowerSet (originalSet) {
  const subSets = []

  // We will have 2^n possible combinations (where n is a length of original set).
  // It is because for every element of original set we will decide whether to include
  // it or not (2 options for each set element).
  const numberOfCombinations = 2 ** originalSet.length

  // Each number in binary representation in a range from 0 to 2^n does exactly what we need:
  // it shows by its bits (0 or 1) whether to include related element from the set or not.
  // For example, for the set {1, 2, 3} the binary number of 0b010 would mean that we need to
  // include only "2" to the current set.
  for (let combinationIndex = 0; combinationIndex < numberOfCombinations; combinationIndex += 1) {
    const subSet = []

    for (let setElementIndex = 0; setElementIndex < originalSet.length; setElementIndex += 1) {
      // Decide whether we need to include current element into the subset or not.
      if (combinationIndex & (1 << setElementIndex)) {
        subSet.push(originalSet[setElementIndex])
      }
    }

    // Add current subset to the list of all subsets.
    subSets.push(subSet)
  }

  return subSets
}

我们在Detail/index.vue中使用这个三方组件

引入,使用,传数据,相关代码如下:

import XtxSku from '@/components/XtxSku.index.vue'

<!-- sku组件 -->
<XtxSku :goods="goods" />

在这里插入图片描述

给组件绑定一个时间,查看sku对象,选中一个规格是空对象,选中两个规格不是空对象

const skuChange = (sku) => {
    console.log(sku)
}

<!-- sku组件 -->
<XtxSku :goods="goods" @change="skuChange"/>

在这里插入图片描述

8. 通用组件统一注册为全局组件

步骤:把components目录下的所有组件进行全局注册,在main.js中注册插件

//@/components/index.js
// 把components中的所组件都进行全局化注册
// 通过插件的方式
import ImageView from './ImageView/index.vue'
import Sku from './XtxSku/index.vue'
export const componentPlugin = {
  install (app) {
    // app.component('组件名字',组件配置对象)
    app.component('XtxImageView', ImageView)
    app.component('XtxSku', Sku)
  }
}
//main.js

// 引入全局组件插件
import { componentPlugin } from '@/components'

app.use(componentPlugin)

小结

Day04 结束
老是写bug真是困扰emmm
笔记上的代码不太全,和视频有的都对不上emmm

祝大家学习顺利
love and peace在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/636408.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

vue3项目+TypeScript前端项目 ———— elemnet-plus,svg图标配置,sass,mock数据

一.集成element-plus 官网地址 安装 pnpm install element-plus 引入 // main.ts import { createApp } from vue import ElementPlus from element-plus import element-plus/dist/index.css import App from ./App.vueconst app createApp(App)app.use(ElementPlus) app.…

气膜建筑的工作原理与优势解析—轻空间

近年来&#xff0c;气膜建筑凭借其独特的结构设计和诸多优点&#xff0c;迅速成为建筑领域的热门选择。本文将详细介绍气膜建筑的工作原理、机械系统、智能控制、索网控制和空气净化等方面&#xff0c;为您提供全面了解气膜建筑的基础知识。 气膜建筑的工作原理 气膜建筑是一种…

01 FreeRTOS 初识

1、freeRTOS 1.1 什么是FreeRTOS Free就是免费的&#xff0c;RTOS全称是real time operating system&#xff0c;即实时操作系统。FreeRTOS是一个迷你的实时操作系统内核&#xff0c;作为一个轻量级的操作系统&#xff0c;功能包括&#xff1a;任务管理、时间管理、信号量、消…

【AI基础】反向传播

文章目录 1. 先写出第一步2.将其封装成函数3. pytorch版 1. 先写出第一步 # 定义输入值和期望输出 x_1 40.0 x_2 80.0 expected_output 60.0 初始化# 定义权重 w_1_11 0.5 w_1_12 0.5 w_1_13 0.5 w_1_21 0.5 w_1_22 0.5 w_1_23 0.5w_2_11 1.0 w_2_21 1.0 w_2_31 1…

RocketMQ实战教程之RocketMQ安装(含Docker安装,建议收藏!)

RocketMQ实战教程之RocketMQ安装 这里实例采用centos系统天翼云为例,分别采用传统安装以及Docker安装的方式来进行RocketMQ的安装.JDK8我这边已经安装配置好了,这里就不在赘述.直接进入正题: 传统安装包安装 系统要求 64位操作系统&#xff0c;推荐 Linux/Unix/macOS64位 JDK…

Java 对外API接口开发 java开发api接口如何编写

Java API API&#xff08;Application Programming Interface&#xff09;是指应用程序编程接口&#xff0c;的JavaAPI是指JDK提供的各种功能的Java类 String类 String类的初始化&#xff1a; &#xff08;1&#xff09;使用字符串常量直接初始化 初始化&#xff1a;String s…

第11章 集合与迭代器

目录 目录 目录 11.1 Collection集合 11.1.1 集合的概念 11.1.2 Collection接口 1、添加元素 2、删除元素 3、查询与获取元素 11.2 List 有序集合 11.2.1 新增方法 11.2.2 ArrayList 11.2.3 LinkedList 1、单向链表 2、双向链表 3、删除元素 11.3 Set 无序集合 …

深度学习-转置卷积

转置卷积 转置卷积&#xff08;Transposed Convolution&#xff09;&#xff0c;也被称为反卷积&#xff08;Deconvolution&#xff09;&#xff0c;是深度学习中的一种操作&#xff0c;特别是在卷积神经网络&#xff08;CNN&#xff09;中。它可以将一个低维度的特征图&#x…

深入 Rust 标准库,Rust标准库源代码系统分析

系列文章目录 送书第一期 《用户画像&#xff1a;平台构建与业务实践》 送书活动之抽奖工具的打造 《获取博客评论用户抽取幸运中奖者》 送书第二期 《Spring Cloud Alibaba核心技术与实战案例》 送书第三期 《深入浅出Java虚拟机》 送书第四期 《AI时代项目经理成长之道》 …

智能猫眼锁核心解决方案以及芯片简介SSD222

书接上回&#xff0c;前篇文章我们诠释了IP 网络摄像系统的定义以及组成部分的功能&#xff0c;也大概的讲了一下所针对的市场以及举例介绍了一款相关芯片&#xff0c;详情可点击下面卡片浏览高集成IP摄像SOC处理方案简介https://blog.csdn.net/Chipsupply/article/details/139…

PDF Reader Pro for Mac 直装激活版:专业PDF阅读编辑软件

在数字化时代&#xff0c;PDF文件已成为我们日常工作和学习中不可或缺的一部分。然而&#xff0c;如何高效、便捷地阅读、编辑和管理这些PDF文件&#xff0c;却一直是许多人面临的难题。现在&#xff0c;有了PDF Reader Pro for Mac&#xff0c;这些难题将迎刃而解。 PDF Reade…

谷歌快速收录怎么做?

快速收录顾名思义&#xff0c;就是让新的的网页内容能够迅速被谷歌搜索引擎抓取、索引和显示在搜索结果中&#xff0c;这对于做seo来说非常重要&#xff0c;因为它有助于新发布的内容尽快出现在谷歌的搜索结果中&#xff0c;从而增加网站的流量 想做谷歌快速收录谷歌推荐了几种…

AD162A低功耗语音解码芯片,助眠耳机方案推荐—云信通讯

睡眠一直是很多人所追寻的目标&#xff0c;无论是因为工作压力过大&#xff0c;还是日常烦扰的思绪&#xff0c;一个好的睡眠质量对每个人来说都至关重要。为了解决这个问题&#xff0c;一些科技公司开发了高品质的助眠耳机&#xff0c;通过音乐和声音来帮助入睡&#xff0c;为…

华为大咖说 | 企业应用AI大模型的“道、法、术” ——道:认知篇

本文作者&#xff1a;郑岩&#xff08;华为云AI变革首席专家&#xff09;全文约3313字&#xff0c;阅读约需8分钟&#xff0c;请仔细看看哦~ 前阵子&#xff0c;我在公司内部发了一篇Sora的科普贴&#xff0c;本来只是个简单的技术总结&#xff0c;但让我意外的是&#xff0c;…

linux系统安全加固

目录 1、账户安全基本措施 1&#xff09;系统账户清理 2&#xff09;密码安全控制 3&#xff09;命令历史限制 2、用户切换及提权 1&#xff09;使用 su命令切换用户 2&#xff09;使用sudo机制提升权限 3、系统引导和安全登录控制 1&#xff09;开机安全控制 2&…

docker如何拉取nginx最新镜像并运行

要拉取Docker Hub上的最新Nginx镜像&#xff0c;您可以使用以下命令&#xff1a; docker pull nginx 这个命令会从Docker Hub下载最新版本的Nginx镜像。如果您想要拉取特定版本的Nginx镜像&#xff0c;可以指定版本号&#xff0c;例如&#xff1a; docker pull nginx:1.18.0 拉…

实在智能签约世界机械500强特变电工 ,加速能源装备巨头智能化升级

近日&#xff0c;世界机械500强、中国企业500强特变电工集团正式与实在智能达成战略合作&#xff0c;由实在智能为其部署RPA数字员工解决方案&#xff0c;助力集团公司各级财务部门提升自动化水平&#xff0c;进一步实现集团内降本增效目标。 关于特变电工 特变电工是国家级高…

AI工具推荐:提升工作效率与生活质量

有哪些好用&#xff0c;且国内可用的AI工具&#xff1f; 副本 在AI大发展的年代&#xff0c;还有人在工作、生活中没有使用过AI吗&#xff1f; 今天为大家推荐几款国内可用、好用的AI工具&#xff0c;不论是自媒体文案写作、打工人汇报PPT、还是论文、公文写作&#xff0c;总…

Centos7离线安装RabbitMQ教程

目录 安装包准备开始安装1. 创建目录2. 上传文件3. 安装erlang语言4. 安装socat5. 安装rabbitmq6. 启动、停止rabbitmq7. 设置开机启动8. 开启web界面管理工具9. 开启防火墙(root)10. 访问页面11. 附录 安装包准备 &#xff08;1&#xff09;准备RabbitMQ的安装包&#xff08;…

使用中心点检测进行对象检测、3D 检测和姿态估计:

检测将对象识别为图像中轴对齐的框。大多数成功的物体检测器都枚举了几乎详尽的潜在物体位置列表&#xff0c;并对每个位置进行分类。这是浪费、低效的&#xff0c;并且需要额外的后处理。在本文中&#xff0c;我们采用了不同的方法。我们将一个对象建模为一个点&#xff0c;即…