基于uniapp vue3.0 uView 做一个点单页面(包括加入购物车动画和左右联动)

news2024/12/26 11:44:59

1、实现效果:

下拉有自定义组件(商品卡片、进步器、侧边栏等)源码

2、左右联动功能

使用scroll-view来做右边的菜单页,title的id动态绑定充当锚点

<scroll-view :scroll-into-view="toView" scroll-with-animation="true" class="main" @scroll="scroll" scroll-y>
				<view class="scroll_main">
					<view class="" v-for="(item,index) in list" :id="'type' + index">
						<view :id="'title' + index">
							<u-divider>{{item.meal_name}}</u-divider>
						</view>
						<card v-for="(item2,indax) in item.goods" :data="item2" @change="cardChange"></card>
					</view>
				</view>
			</scroll-view>

侧边栏组件点击事件,返回分类信息,根据分类的id,定位到scroll-view对应的title

<view class="nav">
				<left-nav :data="list" :current="current" @change="navChange"></left-nav>
			</view>
function navChange(e) {
		current.value = e
		getRightScrollDistance()
	}

 scroll-view属性@scroll用于监听scroll的滚动距离,注意用防抖(我用的是uView里自带的防抖方法),防止nav跳动

获取每个titile距离盒子顶部的距离,用于判断滚动距离是否超出某个分类

onReady(() => {
		list.value.forEach((item, index) => {
			uni.createSelectorQuery().select('#title' + index).boundingClientRect(data => {
				console.log(data);
				titleH.value.push(data)
			}).exec()
		})
	})

获取“this”:

const {
		appContext: {
			app: {
				config: {
					globalProperties
				}
			}
		}
	} = getCurrentInstance()
/* 菜单滚动监听 */
	function scroll(e) {
		//防抖
		globalProperties.$u.debounce(() => {
			console.log(e.detail.scrollTop);
			titleH.value.forEach((item, index) => {
				if ((e.detail.scrollTop + item.height) > item.top) {
					current.value = index
				}
			})
			scrollH.value = e.detail.scrollTop
		}, 100)

	}

3、加入购物车动画

购物车是固定的,我们得给它固定的id以便找到它

<view class="bottom">
			<view id="left_icon" class="left_icon" ref="cartBtn" @click="showPop = !showPop">
				<u-icon name="bag" size="80" color="#fff"></u-icon>
			</view>
			<view class="bottom_info">
				<view>共计:<text style="font-weight: bold;color: #FB3B26;">{{35}}</text>元</view>
				<view>已点:早餐、中餐、晚餐</view>
			</view>
			<view class="submit">
				确认预订
			</view>
		</view>
	</view>

定义移动小球的样式,写活它的初始位置

<!-- 小球 -->
	<view class="ball" v-if="showAnimation" :animation="animation"
		:style="{ top: ballTop + 'px', left: ballLeft + 'px' }">
	</view>
.ball {
		position: absolute;
		z-index: 1;
		width: 40rpx;
		height: 40rpx;
		background-color: red;
		border-radius: 50%;
	}

写活“+”号的id,以便我们获取实例

<view v-if="id" class="plus" :id="id" @click="addClick">
			<u-icon :name="plusIcon" size="32" color="#ffffff" :customStyle="iconStyle"></u-icon>
		</view>

 用uni.createAnimation()来制作动画,按钮的位置减去购物车的位置等于偏移的位置

/* 动画效果控制 */
	function addToCart(item) {
		const btn = '#id_' + item.id;
		const car = '.left_icon';
		console.log('#id_' + item.id);

		uni.createSelectorQuery().select(btn).boundingClientRect().exec((rect) => {
			const btnRect = rect[0];
			const left = btnRect.left;
			const top = btnRect.top;
			ballTop.value = top;
			ballLeft.value = left;
			uni.createSelectorQuery().select(car).boundingClientRect().exec((rect) => {
				console.log(rect);
				const carRect = rect[0];
				const x = carRect.left;
				const y = carRect.top;
				carTop.value = carRect.top;
				carLeft.value = carRect.left;
				animationData.value = uni.createAnimation()
				animationData.value.translate(x - left + 20, y - top).step({
					duration: 300,
				})
				animationTimeout.value
				clearTimeout(animationTimeout.value)
				animation.value = animationData.value.export()
				showAnimation.value = true;
				animationTimeout.value = setTimeout(() => {
					showAnimation.value = false;
				}, 300);
			});
		});
	}

4、代码

页面booking.vue

<template>
	<view class="booking">
		<view class="content">
			<view class="nav">
				<left-nav :data="list" :current="current" @change="navChange"></left-nav>
			</view>
			<scroll-view :scroll-into-view="toView" scroll-with-animation="true" class="main" @scroll="scroll" scroll-y>
				<view class="scroll_main">
					<view class="" v-for="(item,index) in list" :id="'type' + index">
						<view :id="'title' + index">
							<u-divider>{{item.meal_name}}</u-divider>
						</view>
						<card v-for="(item2,indax) in item.goods" :data="item2" @change="cardChange"></card>
					</view>
				</view>
			</scroll-view>
		</view>

		<view class="bottom">
			<view id="left_icon" class="left_icon" ref="cartBtn" @click="showPop = !showPop">
				<u-icon name="bag" size="80" color="#fff"></u-icon>
			</view>
			<view class="bottom_info">
				<view>共计:<text style="font-weight: bold;color: #FB3B26;">{{35}}</text>元</view>
				<view>已点:早餐、中餐、晚餐</view>
			</view>
			<view class="submit">
				确认预订
			</view>
		</view>
	</view>

	<!-- 弹出层 -->
	<u-popup v-model="showPop" mode="bottom" border-radius="20" closeable z-index="1">
		<scroll-view class="pop_main" scroll-y>
			<view class="pop_title">
				已选菜品
			</view>
			<view class="scroll_main">
				<view class="" v-for="(item,index) in list" :id="'type' + index">
					<view :id="'title' + index">
						<u-divider>{{item.meal_name}}</u-divider>
					</view>
					<card v-for="(item2,indax) in item.goods" :data="item2" @change="cardChange" :isAdd="false"></card>
				</view>
			</view>
		</scroll-view>
	</u-popup>

	<!-- 小球 -->
	<view class="ball" v-if="showAnimation" :animation="animation"
		:style="{ top: ballTop + 'px', left: ballLeft + 'px' }">
	</view>
</template>

<script setup>
	import leftNav from "@/components/booking/nav.vue"
	import card from "@/components/booking/card.vue"
	import {
		mockData
	} from "../binding/mock.js"
	import {
		getCurrentInstance,
		ref
	} from "vue";
	import {
		onLoad,
		onReady
	} from '@dcloudio/uni-app';
	onLoad(e => {
		mock.value = mockData
		list.value = mock.value.data.datas
		console.log(list.value);
	})
	onReady(() => {
		list.value.forEach((item, index) => {
			uni.createSelectorQuery().select('#title' + index).boundingClientRect(data => {
				console.log(data);
				titleH.value.push(data)
			}).exec()
		})
	})
	const showPop = ref(false)
	const animationData = ref()
	const animation = ref()
	const animationTimeout = ref()

	const titleH = ref([])

	const scrollH = ref(0)

	const toView = ref("")

	const current = ref(0)

	const mock = ref()

	const list = ref([{}])
	let ballTop = ref(0);
	let ballLeft = ref(0);
	let carTop = ref(0);
	let carLeft = ref(0);
	const showAnimation = ref(false);

	const {
		appContext: {
			app: {
				config: {
					globalProperties
				}
			}
		}
	} = getCurrentInstance()

	/* 菜单滚动监听 */
	function scroll(e) {
		//防抖
		globalProperties.$u.debounce(() => {
			console.log(e.detail.scrollTop);
			titleH.value.forEach((item, index) => {
				if ((e.detail.scrollTop + item.height) > item.top) {
					current.value = index
				}
			})
			scrollH.value = e.detail.scrollTop
		}, 100)

	}

	function cardChange(e) {
		addToCart(e)
	}

	function navChange(e) {
		current.value = e
		getRightScrollDistance()
	}

	function getRightScrollDistance() {
		toView.value = "title" + current.value;
	}

	/* 动画效果控制 */
	function addToCart(item) {
		const btn = '#id_' + item.id;
		const car = '.left_icon';
		console.log('#id_' + item.id);

		uni.createSelectorQuery().select(btn).boundingClientRect().exec((rect) => {
			const btnRect = rect[0];
			const left = btnRect.left;
			const top = btnRect.top;
			ballTop.value = top;
			ballLeft.value = left;
			uni.createSelectorQuery().select(car).boundingClientRect().exec((rect) => {
				console.log(rect);
				const carRect = rect[0];
				const x = carRect.left;
				const y = carRect.top;
				carTop.value = carRect.top;
				carLeft.value = carRect.left;
				animationData.value = uni.createAnimation()
				animationData.value.translate(x - left + 20, y - top).step({
					duration: 300,
				})
				animationTimeout.value
				clearTimeout(animationTimeout.value)
				animation.value = animationData.value.export()
				showAnimation.value = true;
				animationTimeout.value = setTimeout(() => {
					showAnimation.value = false;
				}, 300);
			});
		});
	}
</script>

<style lang="scss" scoped>
	page {
		background-color: #fff;
	}

	.content {
		min-height: 100vh;
		display: flex;

		.nav {
			flex: 1;
			min-width: 164rpx;
			background-color: #F6F6F6;
		}

		.main {
			flex: 3.5;
			height: 100vh;
			background-color: #fff;

			.scroll_main {
				padding-bottom: 150rpx;
			}
		}
	}


	.bottom {
		position: absolute;
		z-index: 2;
		bottom: 0;
		width: 750rpx;
		height: 132rpx;
		background: #FFFFFF;
		box-shadow: 0rpx -2rpx 16rpx 2rpx rgba(164, 164, 164, 0.11);
		border-radius: 0rpx 0rpx 0rpx 0rpx;
		display: flex;
		justify-content: space-between;
		align-items: center;

		.bottom_info {
			flex: 1;
			margin: 0 20rpx;
			font-size: 26rpx;
			line-height: 40rpx;

			&>view:nth-child(2) {
				font-size: 24rpx;
				color: #aaa;
			}

		}

		.submit {
			color: #FFFFFF;
			padding: 10rpx 20rpx;
			background-color: #FB3B26;
			font-size: 26rpx;
			border-radius: 30rpx;
			margin-right: 50rpx;
		}

		#left_icon {
			margin-top: -30rpx;
			margin-left: 40rpx;
			width: 120rpx;
			height: 120rpx;
			background: #FB3B26;
			border-radius: 40rpx;
			line-height: 150rpx;
			text-align: center;
		}
	}

	.ball {
		position: absolute;
		z-index: 1;
		width: 40rpx;
		height: 40rpx;
		background-color: red;
		border-radius: 50%;
	}


	.pop_main {
		position: relative;
		max-height: 60vh;
		padding-top: 100rpx;
		padding-bottom: 150rpx;

		&>.pop_title {
			text-align: center;
			width: 100vw;
			height: 100rpx;
			font-size: 32rpx;
			font-weight: bold;
			position: fixed;
			top: 0;
			z-index: 1;
			background-color: #fff;
			line-height: 100rpx;
			text-align: center;
		}
	}
</style>

侧边栏组件nav.vue

<template>
	<view class="nav_main">
		<view v-for="(item,index) in data" :class="{'tool-box':true,'item':true,'item_act':current==index}"
			@click="change(index)">
			{{item.meal_name}}
		</view>
	</view>
</template>

<script setup>
	const emit = defineEmits(['change'])
	const props = defineProps({
		data: {
			type: Array,
			default: () => ([])
		},
		current: {
			type: Number,
			default: () => (0)
		},
	});

	function change(index) {
		emit('change', index) // 当前值 + 进步值
	}
</script>

<style scoped lang="scss">
	.nav_main {
		position: fixed;
	}

	.item {
		width: 164rpx;
		text-align: center;
		padding: 30rpx 0;
		font-size: 26rpx;
		color: #000000;
		font-weight: 400;
		position: relative;


	}

	.item_act {
		background-color: #fff;
		font-size: 26rpx;
		font-weight: 700;

		&::before {
			content: "";
			display: inline-block;
			width: 12rpx;
			height: 34rpx;
			background: #FC4E3E;
			border-radius: 0rpx 30rpx 30rpx 0rpx;
			position: absolute;
			left: 0;
		}
	}
</style>

商品卡片组件card.vue

<template>
	<view class="card_body">
		<view class="image">

		</view>
		<view class="foods_info">
			<view>{{data.name}}</view>
			<view></view>
			<view>
				<view class="">
					¥{{data.price}}
				</view>
				<counter v-if="isAdd" :id="'id_' + data.id" :number="data.number ?? 0" @change-click="change"></counter>
			</view>
		</view>
	</view>
</template>

<script setup>
	import counter from "@/components/booking/counter.vue"
	const emit = defineEmits(['change'])

	const props = defineProps({
		data: {
			type: Object,
			default: () => ({})
		},
		isAdd: {
			type: Boolean,
			default: () => true
		}
	});

	function change(e) {
		let obj = props.data
		obj.number = e
		console.log(obj);
		emit('change', obj)
	}
</script>

<style scoped lang="scss">
	.card_body {
		display: flex;
		margin: 30rpx 20rpx;

		.image {
			width: 180rpx;
			height: 180rpx;
			background-color: #a1a1a1;
			border-radius: 10rpx;
			margin-right: 20rpx;
		}

		.foods_info {
			display: flex;
			flex-direction: column;
			justify-content: space-between;
			flex: 1;

			&>view:nth-child(1) {
				font-weight: 700;
				font-size: 28rpx;
				color: #000000;
			}

			&>view:nth-child(3) {
				display: flex;
				align-items: center;
				font-weight: 400;
				font-size: 32rpx;
				color: #000000;
				justify-content: space-between;
			}
		}
	}
</style>

进步器组件counter.vue

<template>

	<view class="counter">
		<u-icon v-if="number>0" :name="reduceIcon" size="60" color="#8E8E8E" @click="reduceClick"></u-icon>
		<input v-if="number>0" type="number" :value="number" @blur="inputBlurEvent" @input="inputChangeEvent"
			:disabled="disabled">
		<view v-if="id" class="plus" :id="id" @click="addClick">
			<u-icon :name="plusIcon" size="32" color="#ffffff" :customStyle="iconStyle"></u-icon>
		</view>
	</view>
</template>

<script setup>
	import {
		ref,
		reactive,
		computed,
		nextTick
	} from "vue";
	const props = defineProps({
		id: {
			type: String,
			default: ""
		},
		disabled: {
			type: Number,
			default: false
		},
		number: {
			type: Number,
			default: 0
		},
		maxNumber: {
			type: Number,
			default: 99999
		},
		minNumber: {
			type: Number,
			default: 0
		},
		progressValue: {
			type: Number,
			default: 1
		},
		reduceIcon: {
			type: String,
			default: "minus-circle"
		},
		plusIcon: {
			type: String,
			default: "plus"
		}
	})
	const temp = computed(() => {
		return props.number
	})
	const iconStyle = reactive({
		fontWeight: 'blod'
	})
	const emit = defineEmits(['change-click'])
	// 加
	function addClick(ev) {
		emit('change-click', props.number + props.progressValue) // 当前值 + 进步值
	}
	// 减
	function reduceClick() {
		if (props.number <= props.minNumber) {
			console.log("不能继续减少啦 ~");
			return;
		}
		if ((props.number - props.progressValue) < props.minNumber) {
			console.log("不能继续减少");
			return;
		}
		// 3、执行 减操作
		emit('change-click', props.number - props.progressValue)
	}

	function inputBlurEvent(e) {
		let number = parseInt(e.detail.value)
		if (isNaN(number) || number === 0) {
			emit('change-click', 0)
			return;
		}


		// 条件:输入数不为进步值的倍数,则往前取成倍数值
		let multipie = Math.ceil(number / props.progressValue) // 获取倍数
		number = multipie * props.progressValue // 向上获取最近的倍数
		if (number > props.maxNumber) {
			number = props.maxNumber
			emit('change-click', number)
		} else if (number <= props.minNumber) {
			emit('change-click', props.minNumber)
		} else {
			emit('change-click', number)
		}
	}

	function inputChangeEvent(e) {
		// 限制输入在最大与最小值之间
		// 注意:因为都是赋值最大或最小值,所以会出现值复用无法重新渲染页面的情况(第一次能重新渲染,之后的都不渲染):已解决
		let number = parseInt(e.detail.value)
		if (isNaN(number) || number === 0) {
			// 为空为0
			return
		}
		if (number > props.maxNumber) {
			emit('change-click', props.maxNumber)
		} else if (number <= props.minNumber) {
			emit('change-click', props.minNumber)
		} else {
			emit('change-click', number)
		}
	}
</script>

<style lang="scss" scoped>
	.counter {
		display: flex;
		align-items: center;

		&>input {
			width: 2em;
			font-size: 28rpx;
			font-family: Source Han Sans CN-Bold, Source Han Sans CN;
			font-weight: bold;
			color: #000000;
			flex: 1;
			text-align: center;
		}

		.plus {
			margin: 8rpx;
			width: 48rpx;
			height: 48rpx;
			border-radius: 50%;
			background: #FF3232;

			display: flex;
			justify-content: center;
			align-items: center;

			&>image {
				width: 32rpx;
				height: 30rpx;
				margin-right: 5rpx;
			}
		}
	}
</style>

模拟数据mock.js

const mockData = {
	"code": 200,
	"msg": "",
	"data": {
		"datas": [{
				"meal_id": 5,
				"meal_name": "早餐",
				"meal_type": 1,
				"goods": [{
						"id": 4,
						"name": "牛奶",
						"price": "3.00",
						"img": ""
					},
					{
						"id": 5,
						"name": "馒头",
						"price": "2.00",
						"img": "http://192.168.1.23:9508/campus_pay_resource/goods/f82315767e959b536f64b0a199f99eb5.png"
					},
					{
						"id": 6,
						"name": "手抓饼",
						"price": "6.00",
						"img": "http://192.168.1.23:9508/campus_pay_resource/goods/9370838db9f50a2e950070995975e3b7.png"
					}
				]
			},
			{
				"meal_id": 5,
				"meal_name": "午餐",
				"meal_type": 1,
				"goods": [{
						"id": 7,
						"name": "牛奶",
						"price": "3.00",
						"img": ""
					},
					{
						"id": 8,
						"name": "牛奶",
						"price": "3.00",
						"img": ""
					},
					{
						"id": 9,
						"name": "牛奶",
						"price": "3.00",
						"img": ""
					},
					{
						"id": 10,
						"name": "牛奶",
						"price": "3.00",
						"img": ""
					},
					{
						"id": 11,
						"name": "牛奶",
						"price": "3.00",
						"img": ""
					},
					{
						"id": 12,
						"name": "牛奶",
						"price": "3.00",
						"img": ""
					},
					{
						"id": 13,
						"name": "牛奶",
						"price": "3.00",
						"img": ""
					},
					{
						"id": 14,
						"name": "牛奶",
						"price": "3.00",
						"img": ""
					},
					{
						"id": 15,
						"name": "牛奶",
						"price": "3.00",
						"img": ""
					},
					{
						"id": 16,
						"name": "牛奶",
						"price": "3.00",
						"img": ""
					},
					{
						"id": 17,
						"name": "牛奶",
						"price": "3.00",
						"img": ""
					},
					{
						"id": 18,
						"name": "牛奶",
						"price": "3.00",
						"img": ""
					},
					{
						"id": 19,
						"name": "馒头",
						"price": "2.00",
						"img": "http://192.168.1.23:9508/campus_pay_resource/goods/f82315767e959b536f64b0a199f99eb5.png"
					},
					{
						"id": 20,
						"name": "手抓饼",
						"price": "6.00",
						"img": "http://192.168.1.23:9508/campus_pay_resource/goods/9370838db9f50a2e950070995975e3b7.png"
					}
				]
			},
			{
				"meal_id": 5,
				"meal_name": "晚餐",
				"meal_type": 1,
				"goods": [{
						"id": 21,
						"name": "牛奶",
						"price": "3.00",
						"img": ""
					},
					{
						"id": 22,
						"name": "馒头",
						"price": "2.00",
						"img": "http://192.168.1.23:9508/campus_pay_resource/goods/f82315767e959b536f64b0a199f99eb5.png"
					},
					{
						"id": 23,
						"name": "手抓饼",
						"price": "6.00",
						"img": "http://192.168.1.23:9508/campus_pay_resource/goods/9370838db9f50a2e950070995975e3b7.png"
					}
				]
			},
			{
				"meal_id": 5,
				"meal_name": "宵夜",
				"meal_type": 1,
				"goods": [{
						"id": 24,
						"name": "牛奶",
						"price": "3.00",
						"img": ""
					},
					{
						"id": 25,
						"name": "馒头",
						"price": "2.00",
						"img": "http://192.168.1.23:9508/campus_pay_resource/goods/f82315767e959b536f64b0a199f99eb5.png"
					},
					{
						"id": 26,
						"name": "手抓饼",
						"price": "6.00",
						"img": "http://192.168.1.23:9508/campus_pay_resource/goods/9370838db9f50a2e950070995975e3b7.png"
					}
				]
			}
		],
		"school_name": "测试学校"
	}
}

export {
	mockData
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue+element之解决upload组件上传文件失败后仍显示在列表上、自动上传、过滤、findIndex、splice、filter

MENU 前言错误案例(没有用)正确方法结束语 前言 el-upload上传失败后&#xff0c;文件仍显示在列表上。 这个pdf文件上传失败&#xff0c;仍显示在列表&#xff0c;给人错觉是上传成功&#xff0c;所以要把它去掉。 在element中&#xff0c;file-list和v-model:file-list是用于…

Codigger数据篇(中):数据可控性的灵活配置

在数据服务领域中&#xff0c;数据可控性无疑是至关重要的一环。数据可控性不仅关乎数据的安全性和隐私性&#xff0c;更直接影响到数据价值的实现。Codigger&#xff0c;在其数据可控性方面的灵活配置&#xff0c;为用户提供了更加便捷、高效的数据管理体验。 一、自主选择数…

拌合楼管理系统(十八)如何从一个winForm中的事件修改另外一个Form的控件的值

前言&#xff1a; 上篇讲述了如何手工调用海康的车牌识别摄像头进行拍照和识别车牌&#xff0c;我车牌识别的程序在应用的一个窗体&#xff0c;需要去更新另外一个窗体里面的lable中的内容为识别的车牌信息&#xff0c;同时还要写入到另外窗体的datagridview中。 一、实现效果 …

以23年电赛吉林赛区为例,如何避免冤假错案?再谈无人机赛题中不让使用机载计算机的问题!!!

有同学反馈吉林赛区在无人机赛题中使用树莓派后不被允许测评&#xff0c;由于缺少同省样本支撑&#xff0c;并且吉林省G题无人机获得国奖仅有一组&#xff0c;所以仅根据特定情况做回复&#xff0c;不代表任何组委会立场&#xff0c;因为按照该用户表述&#xff0c;自己没有任何…

【HTTP协议】了解http需要学习哪些内容

HTTP&#xff08;Hypertext Transfer Protocol&#xff09;是超文本传输协议&#xff0c;互联网上应用最广泛的一种协议&#xff0c;它负责在客户端和服务器之间传输数据。本文将从HTTP协议的基本原理、请求-响应模型、常见特性以及应用场景等方面进行总结。 1. HTTP基本原理 …

【云原生】Docker 实践(一):在 Docker 中部署第一个应用

Docker 实践&#xff08;一&#xff09;&#xff1a;在 Docker 中部署第一个应用 1.使用 YUM 方式安装 Docker2.验证 Docker 环境3.在 Docker 中部署第一个应用3.1 小插曲&#xff1a;docker pull 报 missing signature key 错误3.2 重新安装 Nginx 1.使用 YUM 方式安装 Docker…

极目楚天 共襄星汉 | 同元软控受邀参加2024年中国航天大会

4月23日至26日&#xff0c;2024 年中国航天大会&#xff08;CSC2024&#xff09;在湖北省武汉市成功举办。大会由中国宇航学会和中国航天基金会联合主办&#xff0c;以“极目楚天 共襄星汉”为主题&#xff0c;汇聚国内外航天领域知名专家、学者、管理者&#xff0c;深入探讨航…

数字IC后端先进工艺设计实现之TSMC 12nm 6Track工艺数字IC后端实现重点难点盘点

大家知道咱们社区近期TSMC 12nm ARM Cortexa-A72(1P9M 6Track Metal Stack)已经开班。这里小编要强调一点:不要认为跑了先进工艺的项目就会很有竞争力&#xff01;如果你仅仅是跑个先进工艺的flow&#xff0c;不懂先进工艺在数字IC后端实现上的不同点&#xff0c;为何有这样的不…

2024年十款开源测试开发工具推荐(自动化、性能、混沌测试、造数据、流量复制)

今天为大家奉献一篇测试开发工具集锦干货。在本篇文章中&#xff0c;将给大家推荐10款日常工作中经常用到的测试开发工具神器&#xff0c;涵盖了自动化测试、性能压测、流量复制、混沌测试、造数据等。 1、AutoMeter-API 自动化测试平台 AutoMeter 是一款针对分布式服务&…

MT3420B 航天民芯代理 1.5MHZ 2A 同步降压转换器 6V输入

深圳市润泽芯电子有限公司为航天民芯一级代理技术支持 欢迎试样~Tel&#xff1a;18028786817 简介 MT3420B是一个1.5MHz恒定频率&#xff0c;电流模式降压转换器。它是理想的便携式设备&#xff0c;需要非常大电流的单电池锂离子电池&#xff0c;同时在峰值负载条件下仍达到超…

HR是“打手”?

在刚过去的上海公开课上&#xff0c;有一位学员在课间问了我个问题&#xff0c;他说“感觉HR更像是个打手&#xff0c;不知李老师怎么看&#xff1f;”基于我在现场与他交流的内容&#xff0c;我总结成这篇文章吧。 这位学员的问题&#xff0c;表他了他对HR这个岗位的负面感受…

机器学习每周挑战——客户流失数据预测

# 字段 说明 # RowNumber 每行数据的唯一标识符。 # CustomerId 客户的唯一标识符。 # Surname 客户的姓氏&#xff08;出于隐私考虑&#xff0c;请对这些数据进行匿名处理&#xff09;。 # CreditScore 客户在数据收集时的信用评分。 # Geography 客户所在的国…

文件分块+断点续传 实现大文件上传全栈解决方案(前端+nodejs)

1. 文件分块 将大文件切分成较小的片段&#xff08;通常称为分片或块&#xff09;&#xff0c;然后逐个上传这些分片。这种方法可以提高上传的稳定性&#xff0c;因为如果某个分片上传失败&#xff0c;只需要重新上传该分片而不需要重新上传整个文件。同时&#xff0c;分片上传…

React的路由

1. 什么是前端路由 一个路径 path 对应一个组件 component 当我们在浏览器中访问一个 path 的时候&#xff0c;path 对应的组件会在页面中进行渲染 2. 创建路由开发环境 # 使用CRA创建项目 npm create-react-app react-router-pro# 安装最新的ReactRouter包 npm i react-ro…

AI图书推荐:将 ChatGPT和Excel融合倍增工作效率

《将 ChatGPT和Excel融合倍增工作效率》&#xff08; Hands-on ChatGPT in Excel. Enhance Your Excel Workbooks&#xff09;由Mitja Martini撰写&#xff0c;旨在教授读者如何将ChatGPT与Excel结合使用&#xff0c;以提升工作效率和创造AI增强的Excel工具。它还提供了Excel中…

「中标喜报」合众致达中标深圳安居乐寓智能水电表供货及安装项目

2024年4月25日&#xff0c;深圳合众致达科技有限公司(以下简称“我司”)成功中标安居乐寓2023盐田区保障性租赁住房改造提升项目的水电表供货与安装工程(二次)项目&#xff0c;此次中标标志着我司在城中村公寓出租房能源计费领域的专业实力及市场竞争力得到了进一步的认可。 我…

uniapp视频播放器(h5+app)

关于uniapp视频播放器遇到的一些问题&#xff0c;mark下。 中途遇到了很多问题&#xff0c;如果有相同的伙伴遇到了类似的&#xff0c;欢迎交流 官方的video播放器在app上不友好&#xff0c;有以下功能不支持。 loadedmetadata、controlstoggle不支持导致只能手写控制层。 不…

Pycharm远程连接实验室服务器Conda环境配置

如何配置Pycharm和远程服务器 这类博客较多&#xff0c;参考内容 https://blog.csdn.net/fengbao24/article/details/125515542 Python解释器选择&#xff08;conda3&#xff09; 1. Settings -> Add Interpreter -> On SSH 注意&#xff0c;这里的SSH需要在你把远程…

【TCP:可靠数据传输,快速重传,流量控制,TCP流量控制】

文章目录 可靠数据传输TCP&#xff1a;可靠数据传输TCP发送方事件快速重传流量控制TCP流量控制 可靠数据传输 TCP&#xff1a;可靠数据传输 TCP在IP不可靠服务的基础上建立了rdt 管道化的报文段 GBN or SR 累计确认&#xff08;像GBN&#xff09;单个重传定时器&#xff08;像…

深入浅出TCP 与 UDP

&#x1f525; 引言 在互联网的广阔天地里&#xff0c;TCP&#xff08;Transmission Control Protocol&#xff09;和UDP&#xff08;User Datagram Protocol&#xff09;作为传输层的两大支柱&#xff0c;各自承担着不同的使命。下面这篇文章将带你从基础到进阶&#xff0c;全…