当前位置: 首页 > news >正文

【组件封装】uniapp vue3 封装一个完整的Tabs(标签页)组件教程,功能由简到杂实现讲解。

文章目录

  • 前言
  • 一、简单版Tabs
    • 代码实现:
  • 二、下划线带动画的Tabs
    • API回顾:
    • 代码实现:
  • 三、内容区域滑动切换+切换动画
    • 代码实现:
    • (2)禁用手势滑动切换
    • (3)内容区域换为插槽
  • 四、标签栏可滚动
    • 代码实现


前言

手把手教你封装一个移动端 Tabs组件(标签页),功能由简到杂以uniapp vue3为代码示例。


请添加图片描述

一、简单版Tabs

实现一个最简单版的Tabs,下划线无动画无手势切换等,如下图所示:

请添加图片描述

实现说明:标签通过flex布局排列,下划线通过伪类绝对定位在选中项底部,选中项通过索引记录动态添加激活class

代码实现:

tabs.vue

<template><view class="tabs"><view :class="['tab',{active:index==selectIndex}]" v-for="(item,index) in  list" :key="index"@click="handleSelect(index)">{{item}}</view></view>
</template><script setup>import {computed,ref} from 'vue'const props = defineProps({//标签配置list: {type: Array,default: () => []},//激活颜色activeColor: {type: String,defalut: 'deepskyblue'}})//激活颜色const activeColor = computed(() => {return props.activeColor || 'deepskyblue'})//当前选中索引const selectIndex = ref(0)//切换标签const handleSelect = (index) => {if (index !== selectIndex.value) {selectIndex.value = index}}
</script><style lang="scss" scoped>.tabs {width: 100%;display: flex;align-items: center;background-color: #fff;.tab {flex: 1;padding: 25rpx 10rpx;width: 0;box-sizing: border-box;text-align: center;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;color: #333;font-size: 30rpx;position: relative;&.active {font-weight: bold;color: v-bind(activeColor);//下划线&::after {display: block;content: '';position: absolute;bottom: 0;left: 50%;transform: translateX(-50%);width: 30px;height: 6rpx;background-color: v-bind(activeColor);border-radius: 6rpx;}}}}
</style>

ps:注意scss中使用了v-bind引用vue变量activeColor动态设置标签和下划线激活颜色


二、下划线带动画的Tabs

接下来功能升级,要求切换标签的时候下划线有滑动动画,如下图所示

请添加图片描述

实现说明:因为下划线要有滑动动画,就不能相对于选中项绝对定位,而应该基于一个固定的父级元素定位,这个父元素就是组件最外层容器。选中后通过计算下划线到父容器距离(也就是下划线到页面最左边距离)确定绝对定位的left值,同时设置过渡动画。

API回顾:

在uniapp中由于底层使用引擎不同小程序或者app中无法像h5一样进行任何dom操作,只能通过官方提供的api来获取节点信息。

uni.createSelectorQuery()可用于获取节点信息,并结合如下使用方式:

import { getCurrentInstance } from 'vue';
const instance = getCurrentInstance();const query = uni.createSelectorQuery().in(instance.proxy);
query.select("#id").boundingClientRect((data) => {console.log("得到布局位置信息" + JSON.stringify(data));console.log("节点离页面顶部的距离为" + data.top);console.log("节点离页面左边的距离为" + data.left);}).exec();

来获取节点宽高和距离窗口左边或者顶部距离。

下划线位置计算:
在这里插入图片描述
下划线绝对定位left值=a段长度,a=b+标签宽/2,而b为标签与页面左边距离,b和标签宽都可以通过节点信息api获取
ps:因为下划线设置了css属性值 transform: translateX(-50%),向左平移自身一半,所以left值为a

代码实现:

tabs.vue

<template><view class="tabs"><!-- 标签栏 --><view :class="['tab',{active:index==selectIndex}]" v-for="(item,index) in  list" :key="index"@click="handleSelect(index)">{{item}}</view><!-- 下划线 --><view :class="['underline',{transition:left!==null}]"></view></view>
</template><script setup>import {computed,ref,onMounted,nextTick,getCurrentInstance} from 'vue'const props = defineProps({//标签配置list: {type: Array,default: () => []},//激活颜色activeColor: {type: String,defalut: 'deepskyblue'}})//激活颜色const activeColor = computed(() => {return props.activeColor || 'deepskyblue'})//当前选中索引const selectIndex = ref(0)//切换标签const handleSelect = (index) => {if (index !== selectIndex.value) {selectIndex.value = indexsetPosition()}}//下划线离父元素左边距const left = ref(null)//组件实例const instance=getCurrentInstance()//设置下划线位置const setPosition = () => {nextTick(() => {let query = uni.createSelectorQuery().in(instance.proxy)query.select(".active").boundingClientRect(data => {//定位距离=选中标签项与左距离+标签宽一半left.value =`${data.left+data.width/2}px`}).exec()})}onMounted(() => {//设置下划线初始位置setPosition()})
</script><style lang="scss" scoped>.tabs {width: 100%;display: flex;align-items: center;background-color: #fff;position: relative;.tab {flex: 1;padding: 25rpx 10rpx;width: 0;box-sizing: border-box;text-align: center;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;color: #333;font-size: 30rpx;position: relative;&.active {font-weight: bold;color: v-bind(activeColor);}}}//下划线.underline {position: absolute;width: 30px;height: 6rpx;background-color: v-bind(activeColor);border-radius: 6rpx;bottom: 0;left: v-bind(left);transform: translateX(-50%);display: none;&.transition {display: block;transition: all 0.3s;}}
</style>

ps:下划线绝对定位left值在scss通过 v-bind动态访问vue变量


三、内容区域滑动切换+切换动画

在上述示例基础上继续扩展功能,目标是切换标签页支持内容区域带动画同时内容区域滑动可以切换标签,如下图所示:

请添加图片描述

实现说明:结合swiper轮播图组件封装,内容区域使用swiper作为父容器,因为swiper支持手势滑动和滑动动画。

代码实现:

tabs.vue

<template><view class="comp-container"><view class="tabs"><!-- 标签栏 --><view :class="['tab',{active:index==selectIndex}]" v-for="(item,index) in  list" :key="index"@click="handleSelect(index)">{{item}}</view><!-- 下划线 --><view :class="['underline',{transition:left!==null}]"></view></view><!-- 内容区域 --><view class="content"><swiper class="swiper" :current="selectIndex" @change="onSwiperChange"><swiper-item class="swiper-item" v-for="item in list" :key="item"><scroll-view scroll-y style="height:100%"><!-- 页面内容 --><view class="main">{{item}}</view></scroll-view></swiper-item></swiper></view></view>
</template><script setup>import {computed,ref,onMounted,nextTick,getCurrentInstance} from 'vue'const props = defineProps({//标签配置list: {type: Array,default: () => []},//激活颜色activeColor: {type: String,defalut: 'deepskyblue'}})//激活颜色const activeColor = computed(() => {return props.activeColor || 'deepskyblue'})//当前选中索引const selectIndex = ref(0)//切换标签const handleSelect = (index) => {if (index !== selectIndex.value) {selectIndex.value = indexsetPosition()}}//下划线离父元素左边距const left = ref(null)//组件实例const instance = getCurrentInstance()//设置下划线位置const setPosition = () => {nextTick(() => {let query = uni.createSelectorQuery().in(instance.proxy)query.select(".active").boundingClientRect(data => {//定位距离=选中标签项与左距离+标签宽一半left.value = `${data.left+data.width/2}px`}).exec()})}onMounted(() => {//设置下划线初始位置setPosition()})//手势切换回调const onSwiperChange = e => {if (e.detail.current !== selectIndex.value) {handleSelect(e.detail.current)}}
</script><style lang="scss" scoped>.comp-container {height: 100%;display: flex;flex-direction: column;background-color: #f2f2f2;overflow: hidden;}.tabs {width: 100%;display: flex;align-items: center;background-color: #fff;position: relative;flex-shrink: 0;.tab {flex: 1;padding: 25rpx 10rpx;width: 0;box-sizing: border-box;text-align: center;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;color: #333;font-size: 30rpx;position: relative;&.active {font-weight: bold;color: v-bind(activeColor);}}}//下划线.underline {position: absolute;width: 30px;height: 6rpx;background-color: v-bind(activeColor);border-radius: 6rpx;bottom: 0;left: v-bind(left);transform: translateX(-50%);display: none;&.transition {display: block;transition: all 0.3s;}}.content {flex: 1;height: 0;overflow: hidden;.swiper {height: 100%;.swiper-item {height: 100%;}}.main {background: #f2f2f2;text-align: center;padding: 30rpx;box-sizing: border-box;}}
</style>

页面引用:
index.vue

<template><view class="container"><Tabs :list="list" /></view>
</template><script setup>import Tabs from '@/components/tabs.vue'import {ref} from 'vue'const list = ref(['手机', '电脑', '电视机', '洗衣机'])
</script><style lang="scss" scoped>.container {height: 100vh;background-color: #f2f2f2;}
</style>

说明:为了使内容区域可滚动,内嵌了scroll-view,而scroll-view需要指定高度,整个组件高度默认继承父元素100%,所以在页面使用tabs组件时父元素必须设置高度。

(2)禁用手势滑动切换

swiper组件有个属性disable-touch用来禁用轮播图触摸操作,该属性只支持App 2.5.5+、H5 2.5.5+、支付宝小程序、抖音小程序与飞书小程序。微信小程序可以通过@touchmove.prevent阻止触摸事件冒泡来阻止页面滑动

代码实现:

<template><view class="comp-container"><view class="tabs"><!-- 标签栏 --><view :class="['tab',{active:index==selectIndex}]" v-for="(item,index) in  list" :key="index"@click="handleSelect(index)">{{item}}</view><!-- 下划线 --><view :class="['underline',{transition:left!==null}]"></view></view><!-- 内容区域 --><view class="content"><swiper class="swiper" :current="selectIndex" disable-touch @change="onSwiperChange"><swiper-item class="swiper-item" v-for="item in list" :key="item" @touchmove.prevent><scroll-view scroll-y style="height:100%"><!-- 页面内容 --><view class="main">{{item}}</view></scroll-view></swiper-item></swiper></view></view>
</template>

运行效果:
请添加图片描述

(3)内容区域换为插槽

内容区域通过动态插槽提供给页面自定义渲染

核心代码如下:

	<!-- 内容区域 --><view class="content"><swiper class="swiper" :current="selectIndex" @change="onSwiperChange"><swiper-item class="swiper-item" v-for="(item,index) in list" :key="index"><scroll-view scroll-y style="height:100%"><!-- 页面内容 --><!-- #ifdef H5 ||APP --><slot :name="`content${index}`"></slot><!--#endif --><!-- #ifdef MP-WEIXIN --><slot name="content{{index}}"></slot><!--#endif --></scroll-view></swiper-item></swiper></view>

需要注意的是微信小程序端不支持vue插槽动态命名语法,需要写成双花括号形式。

完整代码:
tabs.vue

<template><view class="comp-container"><view class="tabs"><!-- 标签栏 --><view :class="['tab',{active:index==selectIndex}]" v-for="(item,index) in  list" :key="index"@click="handleSelect(index)">{{item}}</view><!-- 下划线 --><view :class="['underline',{transition:left!==null}]"></view></view><!-- 内容区域 --><view class="content"><swiper class="swiper" :current="selectIndex" @change="onSwiperChange"><swiper-item class="swiper-item" v-for="(item,index) in list" :key="index"><scroll-view scroll-y style="height:100%"><!-- 页面内容 --><!-- #ifdef H5 ||APP --><slot :name="`content${index}`"></slot><!--#endif --><!-- #ifdef MP-WEIXIN --><slot name="content{{index}}"></slot><!--#endif --></scroll-view></swiper-item></swiper></view></view>
</template><script setup>import {computed,ref,onMounted,nextTick,getCurrentInstance} from 'vue'const props = defineProps({//标签配置list: {type: Array,default: () => []},//激活颜色activeColor: {type: String,defalut: 'deepskyblue'}})//激活颜色const activeColor = computed(() => {return props.activeColor || 'deepskyblue'})//当前选中索引const selectIndex = ref(0)//切换标签const handleSelect = (index) => {if (index !== selectIndex.value) {selectIndex.value = indexsetPosition()}}//下划线离父元素左边距const left = ref(null)//组件实例const instance = getCurrentInstance()//设置下划线位置const setPosition = () => {nextTick(() => {let query = uni.createSelectorQuery().in(instance.proxy)query.select(".active").boundingClientRect(data => {//定位距离=选中标签项与左距离+标签宽一半left.value = `${data.left+data.width/2}px`}).exec()})}onMounted(() => {//设置下划线初始位置setPosition()})//手势切换回调const onSwiperChange = e => {if (e.detail.current !== selectIndex.value) {handleSelect(e.detail.current)}}
</script><style lang="scss" scoped>.comp-container {height: 100%;display: flex;flex-direction: column;background-color: #f2f2f2;overflow: hidden;}.tabs {width: 100%;display: flex;align-items: center;background-color: #fff;position: relative;flex-shrink: 0;.tab {flex: 1;padding: 25rpx 10rpx;width: 0;box-sizing: border-box;text-align: center;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;color: #333;font-size: 30rpx;position: relative;&.active {font-weight: bold;color: v-bind(activeColor);}}}//下划线.underline {position: absolute;width: 30px;height: 6rpx;background-color: v-bind(activeColor);border-radius: 6rpx;bottom: 0;left: v-bind(left);transform: translateX(-50%);display: none;&.transition {display: block;transition: all 0.3s;}}.content {flex: 1;height: 0;overflow: hidden;.swiper {height: 100%;.swiper-item {height: 100%;}}.main {background: #f2f2f2;text-align: center;padding: 30rpx;box-sizing: border-box;}}
</style>

页面调用:
index.vue

<template><view class="container"><Tabs :list="list" ><template #content0>手机</template><template #content1>电脑</template><template #content2>电视机</template><template #content3>洗衣机</template></Tabs></view>
</template><script setup>import Tabs from '@/components/tabs.vue'import {ref} from 'vue'const list = ref(['手机', '电脑', '电视机', '洗衣机'])
</script><style lang="scss" scoped>.container {height: 100vh;background-color: #f2f2f2;}
</style>

四、标签栏可滚动

最终版——功能继续升级,上述案例都是基于标签比较少的场景下使用,如果标签很多超出屏幕就不再适用,此时标签栏需要支持滚动。
我们目标不仅支持滚动,更友好操作体验还希望实现点击某个标签自动移动到屏幕中间,如下图所示:

请添加图片描述

实现说明:

1、横向滚动:
布局上标签栏外层使用scroll-view包裹,内层依然使用flex布局,每个标签设置基础宽度,当标签过多总体宽度超出屏幕就出现横向滚动。

2、选中标签移动到屏幕中间:
需要分别计算滚动条位置和下划线位置

(1)滚动条位置
scroll-view 有个scroll-left属性控制滚动条位置,我们只需计算该值即可。
在这里插入图片描述

如上图所示,假设点击了热水器标签,热水器标签要移动到屏幕中间,需要平移a段距离,a=c+b/2,其中b为标签自身宽度,c为标签距离页面左边距离-页面宽/2,最终滚动条scrollLeft值=原scrollLeft值+a,所以每次标签切换都要记录计算scrollLeft值,scrollLeft初始值为
0。上述几个值都可以通过节点信息api获取。
需要注意的是如果点击第一或第二个标签或最后一个标签情况是无法使得标签移动到正中间,因为滚动条长度有限,所以在计算scrollLeft值时候需要限制最大值最小值。最小值为0,最大值为滚动条长度-页面宽度,而滚动条长度可以通过如下api获取:

query.select('#scrollview').fields({size: true,scrollOffset: true},(data) => {console.log(data.scrollWidth,'滚动条长度')})

(2)下划线位置
下划线还是和之前的案例一样基于父容器绝对定位,因为滚动条的出现使得父容器不在位于页面最左边,而是滚动条最左边,所以下划线位置还需加上滚动条滚出左边页面区域长度也即scrollLeft值

代码实现

tabs.vue

<template><view class="comp-container"><view class="tabs"><!-- 标签栏 --><scroll-view id="scrollview" :scroll-left="scrollLeft" scroll-x style="width:100%" scroll-with-animation><view class="title-wrap" ><view :class="[selectedIndex===index ?'active':'','title-item']" v-for="(item,index) in list":key="item" @click="onSelect(index)">{{item}}</view><!-- 下划线 --><view :class="['underline',{transition:left!==null}]"></view></view></scroll-view></view><!-- 内容区域 --><view class="content"><swiper class="swiper" :current="selectedIndex" @change="onSwiperChange" ><swiper-item class="swiper-item" v-for="(item,index) in list" :key="index"><scroll-view scroll-y style="height:100%"><!-- 页面内容 --><!-- #ifdef H5 ||APP --><slot :name="`content${index}`"></slot><!--#endif --><!-- #ifdef MP-WEIXIN --><slot name="content{{index}}"></slot><!--#endif --></scroll-view></swiper-item></swiper></view></view>
</template><script setup>import {ref,onMounted,getCurrentInstance,nextTick,computed} from 'vue'const props = defineProps({//标签配置list: {type: Array,default: () => []},//激活颜色activeColor: {type: String,defalut: 'deepskyblue'}})//当前选中索引const selectedIndex = ref(0)//下划线离父元素左边距const left = ref(null)const instance = getCurrentInstance()//窗口宽度const winWidth = uni.getSystemInfoSync().windowWidth//设置滚动条和下划线位置const setPosition = () => {nextTick(() => {let query = uni.createSelectorQuery().in(instance.proxy)query.select(".active").boundingClientRect(async data => {//获取滚动条节点信息let scrollViewInfo= await getScrollViewInfo();//重新获取滚动条scrollLeft值,防止用户手动触发滚动情况下值scrollLeft未及时更新scrollLeft.value=scrollViewInfo.scrollLeftlet offsetLeft = data.leftlet offsetWidth = data.width//设置下划线位置left.value = ((offsetLeft + offsetWidth / 2) + scrollLeft.value) + 'px'//计算滚动条位置let _scrollLeft=scrollLeft.value+ data.left + offsetWidth / 2 - winWidth / 2//限制滚动范围_scrollLeft = Math.max(0, _scrollLeft)//设置滚动条位置scrollLeft.value = Math.min(_scrollLeft, scrollWidth.value - winWidth)}).exec()})}//选中标签监听事件const onSelect = index => {if (index !== selectedIndex.value) {selectedIndex.value = index;setPosition()}}//手势切换回调const onSwiperChange = e => {if (e.detail.current !== selectedIndex.value) {onSelect(e.detail.current)}}//滚动条位置const scrollLeft = ref(0)//滚动条长度const scrollWidth = ref(0)//获取滚动条长度和位置信息const getScrollViewInfo = () => {return new Promise((resolve, reject) => {let query = uni.createSelectorQuery().in(instance.proxy)query.select('#scrollview').fields({size: true,scrollOffset: true,},(data) => {resolve({scrollWidth:data.scrollWidth,scrollLeft:data.scrollLeft})}).exec()})}onMounted(() => {nextTick(async () => {//初始化化记录滚动条长度let res= await getScrollViewInfo();scrollWidth.value=res.scrollWidthsetPosition()})})
</script><style lang="scss" scoped>:deep(::-webkit-scrollbar) {display: none;}.comp-container {height: 100%;display: flex;flex-direction: column;background-color: #f2f2f2;overflow: hidden;}.tabs {width: 100%;position: relative;flex-shrink: 0;background-color: #fff;.title-wrap {width: 100%;display: flex;align-items: center;box-sizing: border-box;justify-content: flex-start;position: relative;.title-item {padding: 25rpx 10rpx;flex: 1 0 22%;width: 0;box-sizing: border-box;text-align: center;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;&.active {color: deepskyblue;font-weight: bold;}}}.underline {position: absolute;width: 30px;height: 6rpx;background-color: deepskyblue;border-radius: 6rpx;bottom: 0;left: v-bind(left);transform: translateX(-50%);display: none;&.transition {display: block;transition: all 0.3s;}}}.content {flex: 1;height: 0;overflow: hidden;.swiper {height: 100%;.swiper-item {height: 100%;}}.main {background: #f2f2f2;padding: 30rpx;text-align: center;}}
</style>

页面调用
index.vue

<template><view class="container"><Tabs :list="list"><template #content0>手机</template><template #content1>电脑</template><template #content2>电视机</template><template #content3>洗衣机</template><template #content4>洗碗机</template><template #content5>热水器</template><template #content6>电冰箱</template><template #content7>烤箱</template></Tabs></view>
</template><script setup>import Tabs from '@/components/tabs.vue'import {ref} from 'vue'const list = ref(['手机', '电脑', '电视机', '洗衣机', '洗碗机', '热水器', '电冰箱', '烤箱'])
</script><style lang="scss" scoped>.container {height: 100vh;background-color: #f2f2f2;}
</style>

相关文章:

【组件封装】uniapp vue3 封装一个完整的Tabs(标签页)组件教程,功能由简到杂实现讲解。

文章目录 前言一、简单版Tabs代码实现&#xff1a; 二、下划线带动画的TabsAPI回顾&#xff1a;代码实现&#xff1a; 三、内容区域滑动切换切换动画代码实现&#xff1a;&#xff08;2&#xff09;禁用手势滑动切换&#xff08;3&#xff09;内容区域换为插槽 四、标签栏可滚动…...

TDesign:Picker 选择器

Picker 选择器 API文档地址 单列选择器用法 /// view onTap:(){TDPicker.showMultiPicker(context,data: [controller.coinList],title: ,rightTextStyle: TextStyle(color: AppColors.ColorMain),onConfirm: (selected) {controller.onTapCoin(selected);Navigator.of(contex…...

【AI赋能心理学论文创作策略】第十二章 AI辅助临床启示撰写指南

AI赋能心理学论文创作策略-系列文章目录 第十二章 AI辅助临床启示撰写指南 文章目录 AI赋能心理学论文创作策略-系列文章目录第十二章 AI辅助临床启示撰写指南 前言基础分析框架第一阶段&#xff1a;核心要素分析第二阶段&#xff1a;应用场景展开 关键环节提示第三阶段&#x…...

Pynsist 打包应用 和 PyWebIO 构建Web 应用

Pynsist&#xff1a;一键打包Python 应用代码为Windows 安装程序。 项目地址&#xff1a; https://github.com/takluyver/pynsist PyWebIO&#xff1a;为Python 开发者提供了一种快速、简洁的方式来创建Web 应用&#xff0c;无需学习前端技术 项目地址&#xff1a;https://g…...

git 使用配置

新拿到机器想配置git 获取代码权限&#xff0c;需要的配置方法 1. git 配置用户名和邮箱 git config --global user.name xxxgit config --global user.email xxemail.com 2. 生成ssh key ssh-keygen -t rsa -C "xxemail.com" 3. 获取ssh key cat ~/.ssh/id_rsa.…...

记一次Mysql的SELECT command denied to user...报错(非权限问题)

java.sql.SQLSyntaxErrorException: SELECT command denied to user ‘user_name’‘1.1.1.1’ for table ‘table_name’。错误信息的字面意思是&#xff1a;表“table_name”拒绝用户“user_name”“1.1.1.1”的SELECT命令 。 比较多的情况是&#xff1a;用户没有查看user表…...

element-plus的el-tree的双向绑定

el-tree改造了下 可选可取消 有默认值 不包含父级id 默认展开 点击节点也可触发选择 节点内容自定义 <template>{{ childKeys }}<!--default-checked-keys:默认展开值&#xff08;正常来说需要包含父级id的 但是我们后端不要后端id &#xff09;show-checkbox&#x…...

代码随想录-算法训练营day41(动态规划04:01背包,01背包滚动数组,分割等和子集)

第九章 动态规划part04● 01背包问题&#xff0c;你该了解这些&#xff01; ● 01背包问题&#xff0c;你该了解这些&#xff01; 滚动数组 ● 416. 分割等和子集 正式开始背包问题&#xff0c;背包问题还是挺难的&#xff0c;虽然大家可能看了很多背包问题模板代码&#xf…...

c#中context.SaveChanges()方法

跟踪实体的状态&#xff1a; Entity Framework 使用 Change Tracker 来跟踪上下文中所有实体的状态。实体的状态可以是&#xff1a; Added&#xff1a;新添加的实体&#xff08;即将插入到数据库中&#xff09;。Modified&#xff1a;已修改的实体&#xff08;即将更新数据库中…...

李飞飞首个“空间智能”模型发布:一张图,生成一个3D世界 | LeetTalk Daily

“LeetTalk Daily”&#xff0c;每日科技前沿&#xff0c;由LeetTools AI精心筛选&#xff0c;为您带来最新鲜、最具洞察力的科技新闻。 在人工智能技术迅速发展的背景下&#xff0c;李飞飞创立的世界实验室于近期发布了首个“空间智能”模型&#xff0c;这一创新成果引发了3D生…...

Node.js简单接口实现教程

Node.js简单接口实现教程 1. 准备工作 确保您的计算机已安装&#xff1a; Node.js (建议版本16.x以上)npm (Node包管理器) 2. 项目初始化 # 创建项目目录 mkdir nodejs-api-tutorial cd nodejs-api-tutorial# 初始化npm项目 npm init -y# 安装必要依赖 npm install expres…...

AIGC 012-Video LDM-更进一步,SD作者将LDM扩展到视频生成任务!

AIGC 012-Video LDM-Stable Video diffusion前身&#xff0c;将LDM扩展到视频生成任务&#xff01; 文章目录 0 论文工作1论文方法实验结果 0 论文工作 Video LDM作者也是Stable diffusion的作者&#xff0c;作者在SD的架构上进行扩展&#xff0c;实现了视频的生成。后续在Vid…...

windows文件下换行, linux上不换行 解决CR换行符替换为LF notepad++

html文件是用回车换行的&#xff0c;在windows电脑上&#xff0c;显示正常。 文件上传到linux服务器后&#xff0c;文件不换行了。只有一行。而且相关js插件也没法正常运行。 用notepad查看&#xff0c;显示尾部换行符&#xff0c;是CR&#xff0c;这就是原因。CR是不被识别的。…...

npm, yarn, pnpm之间的区别

前言 在现代化的开发中&#xff0c;一个人可能同时开发多个项目&#xff0c;安装的项目越来越多&#xff0c;所随之安装的依赖包也越来越臃肿&#xff0c;而且有时候所安装的速度也很慢&#xff0c;甚至会安装失败。 因此我们就需要去了解一下&#xff0c;我们的包管理器&#…...

静态链接和动态链接的特点

静态链接 链接方式‌&#xff1a;在编译时&#xff0c;所有依赖的库代码被直接打包到生成的可执行文件中。这意味着在程序运行时&#xff0c;不需要再加载任何外部库文件‌。 优点‌&#xff1a; 独立性强‌&#xff1a;生成的可执行文件可以在没有依赖库的系统上直接运行&am…...

Mac曲线救国实现Bandizip右键一级菜单

一、前言 个人认为&#xff1a;Bandizip是Mac上最好用的压缩软件&#xff0c;没有之一。 在Mac系统上&#xff0c;学习版的Bandizip由于签名检验问题无法在访达右键的一级菜单显示 解压相关菜单。 有能力的&#xff0c;希望还是支持正版&#xff0c;找找优惠渠道应该100左右。…...

进度与预算

一个项目&#xff0c;如果进度上可以按时完成&#xff0c;一般来说预算不会超标&#xff0c;或者超标幅度有限。 一个项目&#xff0c;如果进度上严重超期&#xff0c;预算基本上会超标&#xff0c;而且超标很大。 现在很多项目&#xff0c;人力成本占比都比较大&#xff0c…...

【教程】创建NVIDIA Docker共享使用主机的GPU

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 这套是我跑完整理的。直接上干货&#xff0c;复制粘贴即可&#xff01; # 先安装toolkit sudo apt-get update sudo apt-get install -y ca-certifica…...

CEEMDAN-CPO-VMD二次分解(CEEMDAN+冠豪猪优化算法CPO优化VMD)

CEEMDAN-CPO-VMD二次分解&#xff08;CEEMDAN冠豪猪优化算法CPO优化VMD&#xff09; 目录 CEEMDAN-CPO-VMD二次分解&#xff08;CEEMDAN冠豪猪优化算法CPO优化VMD&#xff09;效果一览基本介绍程序设计参考资料 效果一览 基本介绍 首先运用CEEMDAN对数据进行一次分解&#xff…...

图论理论基础和存储方式的实现

图论1 图论 (Graph theory) 是数学的一个分支&#xff0c;图是图论的主要研究对象。图 (Graph) 是由若干给定的顶点及连接两顶点的边所构成的图形&#xff0c;这种图形通常用来描述某些事物之间的某种特定关系。顶点用于代表事物&#xff0c;连接两顶点的边则用于表示两个事物…...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…...

XCTF-web-easyupload

试了试php&#xff0c;php7&#xff0c;pht&#xff0c;phtml等&#xff0c;都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接&#xff0c;得到flag...

FFmpeg 低延迟同屏方案

引言 在实时互动需求激增的当下&#xff0c;无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作&#xff0c;还是游戏直播的画面实时传输&#xff0c;低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架&#xff0c;凭借其灵活的编解码、数据…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)

文章目录 1.什么是Redis&#xff1f;2.为什么要使用redis作为mysql的缓存&#xff1f;3.什么是缓存雪崩、缓存穿透、缓存击穿&#xff1f;3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

Docker 运行 Kafka 带 SASL 认证教程

Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明&#xff1a;server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...

iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈

在日常iOS开发过程中&#xff0c;性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期&#xff0c;开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发&#xff0c;但背后往往隐藏着系统资源调度不当…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

(一)单例模式

一、前言 单例模式属于六大创建型模式,即在软件设计过程中,主要关注创建对象的结果,并不关心创建对象的过程及细节。创建型设计模式将类对象的实例化过程进行抽象化接口设计,从而隐藏了类对象的实例是如何被创建的,封装了软件系统使用的具体对象类型。 六大创建型模式包括…...

手机平板能效生态设计指令EU 2023/1670标准解读

手机平板能效生态设计指令EU 2023/1670标准解读 以下是针对欧盟《手机和平板电脑生态设计法规》(EU) 2023/1670 的核心解读&#xff0c;综合法规核心要求、最新修正及企业合规要点&#xff1a; 一、法规背景与目标 生效与强制时间 发布于2023年8月31日&#xff08;OJ公报&…...

深度剖析 DeepSeek 开源模型部署与应用:策略、权衡与未来走向

在人工智能技术呈指数级发展的当下&#xff0c;大模型已然成为推动各行业变革的核心驱动力。DeepSeek 开源模型以其卓越的性能和灵活的开源特性&#xff0c;吸引了众多企业与开发者的目光。如何高效且合理地部署与运用 DeepSeek 模型&#xff0c;成为释放其巨大潜力的关键所在&…...