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

鸿蒙OSUniApp制作自定义的下拉菜单组件(鸿蒙系统适配版)#三方框架 #Uniapp

UniApp制作自定义的下拉菜单组件(鸿蒙系统适配版)

前言

在移动应用开发中,下拉菜单是一个常见且实用的交互组件,它能在有限的屏幕空间内展示更多的选项。虽然各种UI框架都提供了下拉菜单组件,但在一些特定场景下,我们往往需要根据产品需求定制自己的下拉菜单。尤其是在鸿蒙系统逐渐普及的今天,如何让我们的组件在华为设备上有更好的表现,是值得思考的问题。

本文将分享我在实际项目中使用UniApp开发自定义下拉菜单组件的经验,包括基础实现、动画效果以及在鸿蒙系统上的特殊适配。希望能给同样面临这类需求的开发者提供一些参考。

需求分析

在开始编码前,我们先明确一下自定义下拉菜单需要满足的基本需求:

  1. 支持单选/多选模式
  2. 可自定义菜单项的样式和内容
  3. 支持搜索筛选功能
  4. 展开/收起的流畅动画
  5. 支持级联选择
  6. 良好的交互反馈
  7. 在鸿蒙系统上的适配优化

技术选型

基于上述需求,我选择的技术栈如下:

  • UniApp作为跨端开发框架
  • Vue3 + TypeScript提供响应式编程体验
  • SCSS处理样式
  • 使用CSS3实现过渡动画
  • 鸿蒙系统特有API支持

组件设计

首先,我们来设计组件的基本结构:

<template><view class="custom-dropdown" :class="{'harmony-dropdown': isHarmonyOS}"><!-- 触发器部分 --><view class="dropdown-trigger" @click="toggleDropdown"><text class="trigger-text">{{ triggerText }}</text><view class="trigger-icon" :class="{'is-active': isOpen}"><text class="iconfont icon-down"></text></view></view><!-- 下拉内容部分 --><view class="dropdown-content" :class="{'is-open': isOpen}":style="contentStyle"><!-- 搜索框 --><view class="search-box" v-if="showSearch"><input type="text" v-model="searchText" placeholder="搜索..." class="search-input"confirm-type="search"@input="handleSearch"/><text class="clear-icon" v-if="searchText" @click.stop="clearSearch">×</text></view><!-- 选项列表 --><scroll-view scroll-y class="options-list":enhanced="isHarmonyOS":bounces="false"><view v-for="(item, index) in filteredOptions" :key="index"class="option-item":class="{'is-selected': isSelected(item),'harmony-item': isHarmonyOS}"@click="selectOption(item)"><text class="option-text">{{ item[labelKey] }}</text><text v-if="isSelected(item)" class="selected-icon iconfont icon-check"></text></view><!-- 空状态 --><view class="empty-tip" v-if="filteredOptions.length === 0"><text>无匹配结果</text></view></scroll-view><!-- 操作按钮 --><view class="action-btns" v-if="mode === 'multiple'"><view class="btn btn-clear" @click="clearSelection">清空</view><view class="btn btn-confirm" @click="confirmSelection">确定</view></view></view><!-- 遮罩层 --><view class="dropdown-mask" :class="{'is-visible': isOpen}" @click="closeDropdown"></view></view>
</template><script lang="ts">
import { defineComponent, ref, computed, watch, onMounted, onBeforeUnmount } from 'vue';
import { isHarmonyOS } from '@/utils/system';export default defineComponent({name: 'CustomDropdown',props: {// 选项列表options: {type: Array,default: () => []},// 显示的键名labelKey: {type: String,default: 'label'},// 值的键名valueKey: {type: String,default: 'value'},// 选择模式:single/multiplemode: {type: String,default: 'single'},// 是否显示搜索框showSearch: {type: Boolean,default: false},// 最大高度maxHeight: {type: [String, Number],default: 300},// 触发器文本placeholder: {type: String,default: '请选择'},// 默认选中值modelValue: {type: [String, Number, Array],default: ''}},emits: ['update:modelValue', 'change', 'open', 'close'],setup(props, { emit }) {// 状态变量const isOpen = ref(false);const searchText = ref('');const selectedOptions = ref<any[]>([]);const isHarmonyOS = ref(false);// 计算下拉内容样式const contentStyle = computed(() => {const style: any = {};if (typeof props.maxHeight === 'number') {style.maxHeight = `${props.maxHeight}px`;} else {style.maxHeight = props.maxHeight;}return style;});// 计算过滤后的选项const filteredOptions = computed(() => {if (!searchText.value) return props.options;return props.options.filter((item: any) => {const label = item[props.labelKey]?.toString() || '';return label.toLowerCase().includes(searchText.value.toLowerCase());});});// 计算触发器显示文本const triggerText = computed(() => {if (selectedOptions.value.length === 0) {return props.placeholder;}if (props.mode === 'single') {return selectedOptions.value[0][props.labelKey];}if (selectedOptions.value.length === 1) {return selectedOptions.value[0][props.labelKey];}return `已选择${selectedOptions.value.length}项`;});// 初始化选中项const initSelection = () => {if (!props.modelValue) {selectedOptions.value = [];return;}if (props.mode === 'single') {const value = props.modelValue;const option = props.options.find((item: any) => item[props.valueKey] === value);selectedOptions.value = option ? [option] : [];} else {const values = Array.isArray(props.modelValue) ? props.modelValue : [props.modelValue];selectedOptions.value = props.options.filter((item: any) => values.includes(item[props.valueKey]));}};// 检查选项是否被选中const isSelected = (option: any) => {return selectedOptions.value.some((item: any) => item[props.valueKey] === option[props.valueKey]);};// 选择选项const selectOption = (option: any) => {if (props.mode === 'single') {selectedOptions.value = [option];emitChange();closeDropdown();} else {const index = selectedOptions.value.findIndex((item: any) => item[props.valueKey] === option[props.valueKey]);if (index > -1) {selectedOptions.value.splice(index, 1);} else {selectedOptions.value.push(option);}}// 鸿蒙系统震动反馈if (isHarmonyOS.value) {vibrateForHarmony();}};// 确认多选结果const confirmSelection = () => {emitChange();closeDropdown();};// 清空选择const clearSelection = () => {selectedOptions.value = [];if (props.mode === 'single') {emitChange();}};// 处理搜索const handleSearch = () => {// 可以添加防抖逻辑};// 清空搜索const clearSearch = () => {searchText.value = '';};// 切换下拉菜单状态const toggleDropdown = () => {isOpen.value = !isOpen.value;if (isOpen.value) {emit('open');} else {emit('close');}};// 关闭下拉菜单const closeDropdown = () => {if (!isOpen.value) return;isOpen.value = false;searchText.value = '';emit('close');};// 提交变更const emitChange = () => {let value;if (props.mode === 'single') {value = selectedOptions.value.length ? selectedOptions.value[0][props.valueKey] : '';} else {value = selectedOptions.value.map((item: any) => item[props.valueKey]);}emit('update:modelValue', value);emit('change', {value,options: [...selectedOptions.value]});};// 鸿蒙系统震动反馈const vibrateForHarmony = () => {// #ifdef APP-PLUStry {if (plus.os.name === 'Android' && plus.device.vendor === 'HUAWEI') {plus.device.vibrate(10);}} catch (e) {console.error('震动反馈失败', e);}// #endif};// 点击外部关闭const handleOutsideClick = (e: Event) => {const target = e.target as HTMLElement;const dropdown = document.querySelector('.custom-dropdown');if (dropdown && !dropdown.contains(target)) {closeDropdown();}};// 监听modelValue变化watch(() => props.modelValue, () => {initSelection();}, { immediate: true });// 监听options变化watch(() => props.options, () => {initSelection();});// 组件挂载onMounted(() => {isHarmonyOS.value = isHarmonyOS();initSelection();// 添加点击外部关闭事件document.addEventListener('click', handleOutsideClick);});// 组件卸载onBeforeUnmount(() => {document.removeEventListener('click', handleOutsideClick);});return {isOpen,searchText,selectedOptions,isHarmonyOS,contentStyle,filteredOptions,triggerText,isSelected,selectOption,confirmSelection,clearSelection,handleSearch,clearSearch,toggleDropdown,closeDropdown};}
});
</script><style lang="scss">
.custom-dropdown {position: relative;width: 100%;.dropdown-trigger {display: flex;align-items: center;justify-content: space-between;height: 80rpx;padding: 0 20rpx;background-color: #fff;border: 1rpx solid #ddd;border-radius: 8rpx;.trigger-text {flex: 1;font-size: 28rpx;color: #333;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;}.trigger-icon {width: 40rpx;text-align: center;transition: transform 0.3s;&.is-active {transform: rotate(180deg);}.iconfont {font-size: 24rpx;color: #666;}}}.dropdown-content {position: absolute;top: 90rpx;left: 0;width: 100%;background-color: #fff;border: 1rpx solid #eee;border-radius: 8rpx;box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);z-index: 100;overflow: hidden;max-height: 0;opacity: 0;transform: translateY(-10rpx);transition: all 0.3s ease-out;&.is-open {max-height: var(--dropdown-max-height, 600rpx);opacity: 1;transform: translateY(0);}.search-box {position: relative;padding: 16rpx;border-bottom: 1rpx solid #eee;.search-input {width: 100%;height: 64rpx;padding: 0 60rpx 0 20rpx;background-color: #f5f5f5;border: none;border-radius: 32rpx;font-size: 26rpx;}.clear-icon {position: absolute;right: 36rpx;top: 50%;transform: translateY(-50%);width: 40rpx;height: 40rpx;line-height: 40rpx;text-align: center;font-size: 32rpx;color: #999;}}.options-list {max-height: 400rpx;.option-item {display: flex;align-items: center;justify-content: space-between;padding: 20rpx;border-bottom: 1rpx solid #f5f5f5;&:active {background-color: #f9f9f9;}&.is-selected {background-color: #f0f9ff;.option-text {color: #0078ff;font-weight: bold;}.selected-icon {color: #0078ff;}}.option-text {flex: 1;font-size: 28rpx;color: #333;}.selected-icon {font-size: 32rpx;margin-left: 10rpx;}}.empty-tip {padding: 40rpx 0;text-align: center;color: #999;font-size: 26rpx;}}.action-btns {display: flex;padding: 16rpx;border-top: 1rpx solid #eee;.btn {flex: 1;height: 70rpx;line-height: 70rpx;text-align: center;font-size: 28rpx;border-radius: 35rpx;&.btn-clear {color: #666;background-color: #f5f5f5;margin-right: 10rpx;}&.btn-confirm {color: #fff;background-color: #0078ff;margin-left: 10rpx;}}}}.dropdown-mask {position: fixed;top: 0;left: 0;right: 0;bottom: 0;background-color: rgba(0, 0, 0, 0);z-index: 99;pointer-events: none;transition: background-color 0.3s;&.is-visible {background-color: rgba(0, 0, 0, 0.4);pointer-events: auto;}}
}/* 鸿蒙系统特有样式 */
.harmony-dropdown {.dropdown-trigger {border-radius: 16rpx;border: none;background-color: #f5f7fa;box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);.trigger-text {font-family: 'HarmonyOS Sans', sans-serif;}}.dropdown-content {border-radius: 20rpx;border: none;box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.12);.search-box {padding: 24rpx 20rpx 16rpx;.search-input {background-color: #f5f7fa;border-radius: 20rpx;height: 72rpx;}}.options-list {.option-item {&.harmony-item {padding: 24rpx 20rpx;&.is-selected {background: linear-gradient(to right, #f0f7ff, #f5faff);.option-text {background: linear-gradient(to right, #0078ff, #0092ff);-webkit-background-clip: text;color: transparent;}}&:active {background-color: #f7f9fc;}}}}.action-btns {padding: 20rpx;.btn {border-radius: 20rpx;height: 80rpx;line-height: 80rpx;font-family: 'HarmonyOS Sans', sans-serif;&.btn-clear {background-color: #f5f7fa;}&.btn-confirm {background: linear-gradient(to right, #0078ff, #0092ff);box-shadow: 0 4rpx 16rpx rgba(0, 120, 255, 0.3);}}}}
}
</style>

鸿蒙系统适配关键点

在为鸿蒙系统适配我们的下拉菜单组件时,需要特别注意以下几点:

1. 检测鸿蒙系统

首先,我们需要一个工具函数来检测当前设备是否运行鸿蒙系统:

// utils/system.ts/*** 检测当前设备是否为鸿蒙系统*/
export function isHarmonyOS(): boolean {// #ifdef APP-PLUSconst systemInfo = uni.getSystemInfoSync();const systemName = systemInfo.osName || '';const systemVersion = systemInfo.osVersion || '';// 鸿蒙系统识别return systemName.toLowerCase().includes('harmony') || (systemName === 'android' && systemVersion.includes('harmony'));// #endifreturn false;
}

2. UI风格适配

鸿蒙系统的设计语言强调简洁、轻盈、自然,需要适配以下UI细节:

  1. 圆角设计:鸿蒙系统偏好较大的圆角,我们在组件中使用了20rpx的圆角值
  2. 渐变色:按钮和激活态使用渐变色提升视觉效果
  3. 阴影效果:适当的阴影增强层次感,但要保持轻盈质感
  4. 字体适配:使用鸿蒙系统的HarmonyOS Sans字体
  5. 间距调整:鸿蒙UI通常有更宽松的内边距

3. 交互体验优化

鸿蒙系统注重流畅的交互体验:

  1. 震动反馈:选择选项时添加轻微震动
  2. 滚动优化:使用enhanced模式增强滚动性能
  3. 过渡动画:确保展开/收起有流畅的过渡效果
// 鸿蒙系统震动反馈
const vibrateForHarmony = () => {// #ifdef APP-PLUStry {if (plus.os.name === 'Android' && plus.device.vendor === 'HUAWEI') {plus.device.vibrate(10); // 非常轻微的震动,提供触觉反馈}} catch (e) {console.error('震动反馈失败', e);}// #endif
};

实际应用案例

案例一:筛选条件下拉菜单

在一个电商App的商品列表页中,我们使用了自定义下拉菜单组件来实现筛选功能。用户可以通过下拉菜单选择价格区间、品牌、尺寸等筛选条件。

<template><view class="filter-bar"><custom-dropdownv-model="selectedPrice":options="priceOptions"placeholder="价格"label-key="label"value-key="value"mode="single"@change="applyFilter"></custom-dropdown><custom-dropdownv-model="selectedBrands":options="brandOptions"placeholder="品牌"label-key="name"value-key="id"mode="multiple"show-search@change="applyFilter"></custom-dropdown><custom-dropdownv-model="selectedSort":options="sortOptions"placeholder="排序"@change="applyFilter"></custom-dropdown></view>
</template><script>
import CustomDropdown from '@/components/CustomDropdown.vue';export default {components: {CustomDropdown},data() {return {selectedPrice: '',selectedBrands: [],selectedSort: 'default',priceOptions: [{ label: '全部', value: '' },{ label: '0-100元', value: '0-100' },{ label: '100-300元', value: '100-300' },{ label: '300-500元', value: '300-500' },{ label: '500元以上', value: '500-' }],brandOptions: [{ name: '华为', id: 'huawei' },{ name: '小米', id: 'xiaomi' },{ name: '苹果', id: 'apple' },{ name: '三星', id: 'samsung' },{ name: 'OPPO', id: 'oppo' },{ name: 'vivo', id: 'vivo' }],sortOptions: [{ label: '默认排序', value: 'default' },{ label: '价格从低到高', value: 'price-asc' },{ label: '价格从高到低', value: 'price-desc' },{ label: '销量优先', value: 'sales-desc' },{ label: '评分优先', value: 'rating-desc' }]};},methods: {applyFilter() {// 应用筛选条件this.$emit('filter-change', {price: this.selectedPrice,brands: this.selectedBrands,sort: this.selectedSort});}}
};
</script>

案例二:级联选择器

我们还使用自定义下拉菜单组件实现了地址选择的级联选择器,用户可以依次选择省、市、区。

<template><view class="address-selector"><custom-dropdownv-model="selectedProvince":options="provinces"placeholder="选择省份"@change="onProvinceChange"></custom-dropdown><custom-dropdownv-model="selectedCity":options="cities"placeholder="选择城市":disabled="!selectedProvince"@change="onCityChange"></custom-dropdown><custom-dropdownv-model="selectedDistrict":options="districts"placeholder="选择区县":disabled="!selectedCity"@change="onDistrictChange"></custom-dropdown></view>
</template><script>
import { defineComponent, ref, watch } from 'vue';
import CustomDropdown from '@/components/CustomDropdown.vue';
import { fetchProvinces, fetchCities, fetchDistricts } from '@/api/address';export default defineComponent({components: {CustomDropdown},emits: ['change'],setup(props, { emit }) {const selectedProvince = ref('');const selectedCity = ref('');const selectedDistrict = ref('');const provinces = ref([]);const cities = ref([]);const districts = ref([]);// 加载省份数据const loadProvinces = async () => {try {provinces.value = await fetchProvinces();} catch (error) {console.error('加载省份失败', error);}};// 加载城市数据const loadCities = async (provinceId) => {if (!provinceId) {cities.value = [];return;}try {cities.value = await fetchCities(provinceId);} catch (error) {console.error('加载城市失败', error);}};// 加载区县数据const loadDistricts = async (cityId) => {if (!cityId) {districts.value = [];return;}try {districts.value = await fetchDistricts(cityId);} catch (error) {console.error('加载区县失败', error);}};// 省份变化const onProvinceChange = () => {selectedCity.value = '';selectedDistrict.value = '';loadCities(selectedProvince.value);emitChange();};// 城市变化const onCityChange = () => {selectedDistrict.value = '';loadDistricts(selectedCity.value);emitChange();};// 区县变化const onDistrictChange = () => {emitChange();};// 发送变化事件const emitChange = () => {emit('change', {province: selectedProvince.value,city: selectedCity.value,district: selectedDistrict.value});};// 初始化onMounted(() => {loadProvinces();});return {selectedProvince,selectedCity,selectedDistrict,provinces,cities,districts,onProvinceChange,onCityChange,onDistrictChange};}
});
</script>

常见问题与解决方案

在开发和使用这个组件的过程中,我遇到了一些常见问题,分享解决方案:

1. 下拉菜单被裁剪问题

问题:当下拉菜单位于页面底部时,展开的内容可能会被裁剪。

解决方案:计算剩余空间,动态调整下拉方向:

const adjustDropdownPosition = () => {const triggerEl = triggerRef.value;const contentEl = contentRef.value;if (!triggerEl || !contentEl) return;// 获取触发器位置信息const rect = triggerEl.getBoundingClientRect();// 视窗高度const viewHeight = window.innerHeight;// 触发器底部到视窗底部的距离const spaceBelow = viewHeight - rect.bottom;// 内容高度const contentHeight = contentEl.offsetHeight;// 如果下方空间不足,向上展开if (spaceBelow < contentHeight && rect.top > contentHeight) {dropdownDirection.value = 'up';} else {dropdownDirection.value = 'down';}
};

2. 多个下拉菜单同时打开问题

问题:当页面中有多个下拉菜单时,打开一个菜单,其他已打开的菜单应该自动关闭。

解决方案:使用全局事件总线管理下拉菜单的打开状态:

// 全局事件总线
const emitter = mitt();// 打开下拉菜单
const openDropdown = () => {// 通知其他下拉菜单关闭emitter.emit('dropdown-open', dropdownId.value);isOpen.value = true;emit('open');
};onMounted(() => {// 监听其他下拉菜单打开事件emitter.on('dropdown-open', (id) => {if (id !== dropdownId.value && isOpen.value) {isOpen.value = false;emit('close');}});
});onBeforeUnmount(() => {emitter.off('dropdown-open');
});

3. 在鸿蒙系统上的滚动卡顿问题

问题:在某些华为设备上,下拉菜单内容滚动不够流畅。

解决方案:开启硬件加速和使用Native View:

<scroll-view scroll-y class="options-list":enhanced="isHarmonyOS":show-scrollbar="false":fast-deceleration="isHarmonyOS":bounces="false"
>

同时,对滚动容器添加硬件加速样式:

.options-list {transform: translateZ(0);-webkit-overflow-scrolling: touch;will-change: scroll-position;
}

总结

通过本文,我们详细介绍了如何使用UniApp开发一个自定义下拉菜单组件,并特别关注了在鸿蒙系统上的适配优化。从组件的基本结构设计,到交互细节的处理,再到在实际应用中的案例展示,希望能给大家提供一些思路。

随着鸿蒙系统的普及,做好相关适配工作将越来越重要。在下拉菜单这样的基础交互组件上,通过一些细节的优化,可以大大提升用户体验,尤其是在华为设备上。

最后,欢迎大家基于这个组件进行二次开发,添加更多功能或者根据自己的业务需求进行定制。如有任何问题或改进建议,也欢迎交流讨论。

参考资源

  1. UniApp官方文档
  2. HarmonyOS设计指南
  3. Vue3官方文档
  4. CSS Animation完整指南

相关文章:

鸿蒙OSUniApp制作自定义的下拉菜单组件(鸿蒙系统适配版)#三方框架 #Uniapp

UniApp制作自定义的下拉菜单组件&#xff08;鸿蒙系统适配版&#xff09; 前言 在移动应用开发中&#xff0c;下拉菜单是一个常见且实用的交互组件&#xff0c;它能在有限的屏幕空间内展示更多的选项。虽然各种UI框架都提供了下拉菜单组件&#xff0c;但在一些特定场景下&…...

扣子(Coze)案例:工作流生成小红书心理学卡片

大家好&#xff01;我是 Robin。专注于 AI 技术探索与实践&#xff0c;持续分享 Coze 智能体、Coze 模板&#xff0c;以及 Coze 工作流搭建案例。 工作流智能体作用&#xff1a; 输入需要生成小红书心理学知识卡片的数量&#xff0c;工作流自动批量生成图文。 首先演示一下生…...

深度理解用于多智能体强化学习的单调价值函数分解QMIX算法:基于python从零实现

引言&#xff1a;合作式多智能体强化学习与功劳分配 在合作式多智能体强化学习&#xff08;MARL&#xff09;中&#xff0c;多个智能体携手合作&#xff0c;共同达成一个目标&#xff0c;通常会收到一个团队共享的奖励。在这种场景下&#xff0c;一个关键的挑战就是功劳分配&a…...

C语言经典笔试题目分析(持续更新)

1. 描述下面代码中两个static 各自的含义 static void func (void) {static unsigned int i; }static void func(void) 中的 static 作用对象&#xff1a;函数 func。 含义&#xff1a; 限制函数的作用域&#xff08;链接属性&#xff09;&#xff0c;使其仅在当前源文件&…...

射击游戏demo11

完善地图&#xff0c;加载出装饰品&#xff0c;检测人员与地面的碰撞&#xff0c;检测子弹与岩壁的碰撞&#xff0c;检测手雷与地面的碰撞。 import pygame import sys import os import random import csv # 初始化Pygame pygame.init()# 屏幕宽度 SCREEN_WIDTH 1200 # 屏幕高…...

多智能体Multi-Agent应用实战与原理分析

一:Agent 与传统工具调用的对比 在当今的开发环境中,Agent 的出现极大地简化了工作流程。其底层主要基于提示词、模型和工具。用户只需向 Agent 输入需求,Agent 便会自动分析需求,并利用工具获取最终答案。而传统方式下,若没有 Agent,我们则需要手动编码来执行工具,还要…...

专项智能练习(定义判断)_DA_01

1. 单选题 热传导是介质内无宏观运动时的传热现象&#xff0c;其在固体、液体和气体中均可发生。但严格而言&#xff0c;只有在固体中才是纯粹的热传导&#xff0c;在流体&#xff08;泛指液体和气体&#xff09;中又是另外一种情况&#xff0c;流体即使处于静止状态&#xff0…...

关于NLP自然语言处理的简单总结

参考&#xff1a; 什么是自然语言处理&#xff1f;看这篇文章就够了&#xff01; - 知乎 (zhihu.com) 所谓自然语言理解&#xff0c;就是研究如何让机器能够理解我们人类的语言并给出一些回应。 自然语言处理&#xff08;Natural Language Processing&#xff0c;NLP&#xff0…...

SLAM定位与地图构建

SLAM介绍 SLAM全称Simultaneous Localization And Mapping&#xff0c;中文名称同时定位与地图构建。旨在让移动设备在未知环境中同时完成以下两个任务&#xff08;定位需要地图&#xff0c;而建图又依赖定位信息&#xff0c;两者互为依赖&#xff09;&#xff1a; 定位&#…...

REST架构风格介绍

一.REST&#xff08;表述性状态转移&#xff09; 1.定义 REST&#xff08;Representational State Transfer&#xff09;是由 Roy Fielding 在 2000 年提出的一种软件架构风格&#xff0c;用于设计网络应用的通信模式。它基于 HTTP 协议&#xff0c;强调通过统一的接口&#…...

前端流行框架Vue3教程:16. 组件事件配合`v-model`使用

组件事件配合v-model使用 如果是用户输入&#xff0c;我们希望在获取数据的同时发送数据配合v-model 来使用&#xff0c;帮助理解组件间的通信和数据绑定。 &#x1f9e9; 第一步&#xff1a;创建子组件&#xff08;SearchComponent.vue&#xff09; 这个组件用于处理用户的搜…...

5月15日day26打卡

函数专题1 知识点回顾&#xff1a; 函数的定义变量作用域&#xff1a;局部变量和全局变量函数的参数类型&#xff1a;位置参数、默认参数、不定参数传递参数的手段&#xff1a;关键词参数传递参数的顺序&#xff1a;同时出现三种参数类型时 作业&#xff1a; 题目1&#xff1a;…...

Java中的深拷贝与浅拷贝

什么是拷贝 在Java中&#xff0c;拷贝是指创建一个对象的副本。拷贝主要分为两种类型&#xff1a;浅拷贝(Shallow Copy)和深拷贝(Deep Copy)。理解这两种拷贝的区别对于编写正确的Java程序非常重要&#xff0c;特别是在处理对象引用时。 浅拷贝(Shallow Copy) 浅拷贝是指创建…...

springboot AOP中,通过解析SpEL 表达式动态获取参数值

切面注解 import com.bn.document.constants.FmDeptCatalogueConstants;import java.lang.annotation.*;Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) public interface FmDeptCatalogueAopAnnotation {/*** 权限类型*/FmDeptCatalogueConstants value();/…...

【论信息系统项目的合同管理】

论信息系统项目的合同管理 论文要求写作要点正文前言一、合同的签订管理二、合同履行管理三、合同变更管理四、合同档案管理五、合同违约索赔管理结语 论文要求 项目合同管理通过对项目合同的全生命周期进行管理&#xff0c;来回避和减轻可识别的项目风险。 请以“论信息系统项…...

redis持久化方式

一、RDB redis database&#xff1a;快照&#xff0c;某一时刻将内存中的数据&#xff0c;写入磁盘中生成1个dump.rdb文件RDB的触发方式&#xff1a; 手动触发&#xff1a;save&#xff08;阻塞主进程&#xff0c;阻塞其它指令&#xff0c;保证数据一致性&#xff09;、bgsave…...

free void* 指令

https://stackoverflow.com/questions/2182103/is-it-ok-to-free-void free(ptr) 仅释放指针指向的内存&#xff0c;不会修改指针变量本身的值。调用后&#xff0c;ptr 仍然指向原来的地址&#xff08;称为 "悬空指针"&#xff09;&#xff0c;但该地址对应的内存已…...

ADS1220高精度ADC(TI)——应用 源码

文章目录 德州仪器ADS1220概述资料引脚&封装布线寄存器配置寄存器0&#xff08;00h&#xff09;配置寄存器1&#xff08;01h&#xff09;配置寄存器2&#xff08;02h&#xff09;配置寄存器3&#xff08;03h&#xff09; 连续转换流程驱动源码ads1220.cads1220.h 德州仪器A…...

mysql-Java手写分布式事物提交流程

准备 innodb存储引擎开启支持分布式事务 set global innodb_support_axon分布式的流程 详细流程&#xff1a; XA START ‘a’; 作用&#xff1a;开始一个新的XA事务&#xff0c;并分配一个唯一的事务ID ‘a’。 说明&#xff1a;在这个命令之后&#xff0c;所有后续的SQL操…...

红黑树:数据世界的平衡守护者

在 C 算法的神秘森林里&#xff0c;红黑树是一棵充满智慧的 “魔法树”。它既不像普通二叉搜索树那样容易失衡&#xff0c;也不像 AVL 树对平衡要求那么苛刻。作为 C 算法小白&#xff0c;今天就和大家一起深入探索红黑树的奥秘&#xff0c;看看它是如何成为数据世界的平衡守护…...

哈夫曼树完全解析:从原理到应用

目录 一、核心概念 二、构造全流程解析 三、编码生成机制 四、C语言实现关键代码 五、核心特性深度解读 六、现代应用场景 七、压缩实战演示 一、核心概念 哈夫曼树&#xff08;最优二叉树&#xff09;是带权路径长度&#xff08;WPL&#xff09;最短的树形结构&#x…...

如何在 Windows 命令提示符中创建多个文件夹和多个文件

如何在 Windows 命令提示符中创建多个文件夹和多个文件 虽然大多数用户习惯使用 Windows 图形界面来创建文件夹&#xff0c;但如果你需要一次性创建多个文件夹或文件&#xff0c;如同在类Unix系统中可以使用mkdir和touch命令一样&#xff0c;windows下也有创建目录和文件的对应…...

【Java】Spring的声明事务在多线程场景中失效问题。

大家都知道Spring的声明式事务在多线程当中会失效&#xff0c;来看一下如下案例。 按照如下方式&#xff0c;b()方法抛出异常,由于父子线程导致事务失效&#xff0c;a()会成功插入,但是b()不会。 因此成功插入一条数据&#xff0c;事务失效。 Component public class UserServ…...

多平台图标设计与管理的终极解决方案

IconWorkshop Pro 是一款由Axialis团队开发的专业图标设计与制作软件&#xff0c;专注于为设计师、开发者及企业用户提供高效且灵活的图标创作解决方案。该软件凭借其强大的功能与跨平台适配性&#xff0c;成为Windows、macOS、iOS、Android等多系统图标设计的首选工具之一。 …...

【搭建Node-RED + MQTT Broker实现AI大模型交互】

搭建Node-RED MQTT Broker实现AI大模型交互 搭建Node-RED MQTT Broker实现AI大模型交互一、系统架构二、环境准备与安装1. 安装Node.js2. 安装Mosquitto MQTT Broker3. 配置Mosquitto4. 安装Node-RED5. 配置Node-RED监听所有网络接口6. 启动Node-RED 三、Node-RED流程配置1. …...

小结: js 在浏览器执行原理

浏览器多进程与多线程 现代浏览器的标签环境隔离主要通过多进程架构和多线程机制实现&#xff0c;以确保安全、性能和稳定性。以下是浏览器实现标签环境隔离的多进程和多线程交互架构的详细解析&#xff1a; ------------------- ------------------- -----------…...

C++核心编程--2 引用

引用就是给变量起别名&#xff0c;操作引用就等于操作原始变量。 2.1 引用基本用法 int var 10; int & r_var var; 2.2 注意事项 声明时必须初始化不允许更改引用指向的原始变量 2.3 引用作为函数参数传递 简化指针修饰函数参数 2.4 引用作为函数返回值 不要返回…...

音频/AI/BLE/WIFI/玩具/商业等方向的论坛网站总结

我爱音频网 我爱音频网 - 我们只谈音频&#xff0c;丰富的TWS真无线蓝牙耳机拆解报告 (52audio.com) 中国人工智能学会 中国人工智能学会 (caai.cn) AIIA人工智能网 https://www.aiiaw.com/ 世界人工智能论坛 世界人工智能论坛 - (amtbbs.org) 36氪 36氪_让一部分人先…...

告别碎片化!MCP 带来 AI Agent 开发生态的革命性突破

引言&#xff1a; 在当今的智能客服系统开发中&#xff0c;开发者常常面临一个棘手的挑战&#xff1a;需要整合用户的 CRM 数据、知识库和实时聊天记录。然而&#xff0c;由于缺乏统一的标准&#xff0c;每个团队都不得不手动实现这些集成。这不仅延长了开发周期&#xff0c;还…...

centos7部署mysql5.7

1.下载mysql的官方yum源 wget http://dev.mysql.com/get/mysql57-community-release-el7-11.noarch.rpm2.安装yum源 yum -y install mysql57-community-release-el7-11.noarch.rpm3.安装秘钥文件 rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-20224.安装mysql5.7…...

linux dbus

Linux D-Bus(Desktop Bus)是一种进程间通信(IPC)机制,主要用于Linux桌面环境和系统服务之间的消息传递。它允许不同的应用程序或系统组件以高效、安全的方式相互通信,是现代Linux桌面(如GNOME、KDE)的核心基础设施之一。 1. D-Bus 的核心概念 消息总线(Message Bus):…...

计量——异方差的检验及其修正

目录 1.异方差的检验 1 BP检验 2white检验 2.异方差的修正 1.异方差的检验 1 BP检验 选择检验方法&#xff1a;BP BP检验的实际步骤&#xff08;非机器&#xff09;&#xff1a; 1.y对所有x进行回归&#xff0c;得到残差u。计算残差的平方u^2 2.u^2对所有x进行回归&#…...

操作系统学习笔记第3章 内存管理(灰灰题库)

1. 单选题 某页式存储管理系统中&#xff0c;主存为 128KB&#xff0c;分成 32 块&#xff0c;块号为 0、1、2、3、…、31。某作业有 5 块&#xff0c;其页号为 0、1、2、3、4&#xff0c;被分别装入主存的 3、8、4、6、9 块中。有一逻辑地址为 [3, 70]&#xff08;其中方括号中…...

vue3项目中使用CanvasEditor开箱即用(组件的形式,组件封装好了)

canvas-editor-vue 这是canvas-editor项目的vue版本,我封装成组建了,当然了这是个已经把canvas-editor封装好的的Vue项目 项目地址GitHub地址:GitHub - aini-aini/canvas-editor-vue: this is a project than can be used in vue project as Componentthis is a project than…...

人体肢体工作识别-一步几个脚印从头设计数字生命——仙盟创梦IDE

人体肢体识别是借助计算机视觉、传感器等技术&#xff0c;对人体各肢体的位置、动作、姿态等进行检测与分析的技术。其在医疗健康、智能交互、运动训练、安全监控等多个领域具有重要价值&#xff0c; 示例代码 import cv2 import mediapipe as mp import numpy as np import c…...

【重磅】配电网智能软开关和储能联合规划

目录 1 主要内容 目标函数 数据说明 节点系统图 2 部分代码 3 程序结果 4 下载链接 1 主要内容 该程序复现《具有源荷不平衡特性的配电网智能软开关和储能联合规划》部分模型&#xff0c;未考虑聚类分析和分布鲁棒部分&#xff0c;就智能软开关和储能联合规划部分进行了…...

实验6 电子邮件

实验6 电子邮件 1、实验目的 理解电子邮件系统基本结构 理解客户端和服务器端&#xff0c;以及服务器之间的通信 分析理解SMTP&#xff0c;POP3协议 2、实验环境 硬件要求&#xff1a;阿里云云主机ECS 一台。 软件要求&#xff1a;Linux/ Windows 操作系统 3、实验内容…...

NHANES指标推荐:OBS

文章题目&#xff1a;Association between oxidative balance score and all-cause and cancer-specific mortality among cancer survivors DOI&#xff1a;10.3389/fimmu.2025.1541675 中文标题&#xff1a;癌症幸存者氧化平衡评分与全因死亡率和癌症特异性死亡率之间的关联 …...

python-修改图片背景色

在Python中&#xff0c;可以使用图像处理库&#xff08;如OpenCV或Pillow&#xff09;来修改图片的背景色。通常&#xff0c;修改背景色的流程包括以下步骤&#xff1a; 1、对图片进行分割&#xff0c;识别前景和背景。 2、对背景区域进行颜色替换。 下面是两种实现方法&#x…...

数据结构与算法--顺序表--单链表

一 顺序表 需要指向存储位置的基地址分配一段连续的内存用length记录实际的元素的个数&#xff0c;也即顺序表的长度&#xff0c;因为顺序表是允许删除和插入元素的不需要定义数组 1.1 普通结构体数组实现版本&#xff0c;实现白色的点以一定的步长移除画面&#xff0c;大量fo…...

MATLAB安装全攻略:常见问题与解决方案

MATLAB安装常见问题与解决方案 一、系统兼容性验证 安装前需确认操作系统满足MATLAB版本要求&#xff1a; Windows 10版本1903及以上&#xff08;64位&#xff09;macOS Monterey 12.6及以上Ubuntu 22.04 LTS及以上 验证命令示例&#xff1a; # Linux系统验证 lsb_release…...

constexpr 关键字的意义(入门)

author: hjjdebug date: 2025年 05月 15日 星期四 16:03:33 CST description: constexpr 关键字的意义(入门) constexpr 是c11 引入的一个关键字, 代表了一种属性. 文章目录 1. constexpr 修饰的变量, 在编译期间就可以得到其数值.2. constexpr 修饰的函数, 可以在编译期间被调…...

aptitude 深度教程:从基础到生产实践

目录 一、aptitude 基础:核心概念与环境准备 1.1 aptitude 是什么? 1.2 安装与环境配置 二、aptitude 核心操作:从命令行到交互式界面 2.1 命令行基础操作 2.2 交互式界面(TUI)入门 三、高级功能:依赖管理与版本控制 3.1 依赖冲突解决实战 3.2 版本锁定与降级 3…...

嵌入式开发学习日志(数据结构--双链表)Day21

一、双链表 1.定义 双向链表是在单链表的每个结点中&#xff0c;再设置一个指向其钱去节点的指针域。 2、声明文件 3、创建表头 4、头插 5、 遍历 6、尾插、 7、指定插 8、查找 9、修改 10.、删除 11、逆序 12、销毁链表 13、main.c 三、扩展&#xff1a;工程管理工具&#…...

抢购Python代码示例与技术解析

引言&#xff1a;抢购系统的技术挑战 在当今电子商务高度发达的时代&#xff0c;抢购活动已成为各大电商平台吸引用户的重要手段。然而&#xff0c;高并发、低延迟的抢购场景对系统设计提出了严峻挑战。本文将提供一个完整的Python抢购代码示例&#xff0c;并深入分析其技术实…...

undefined reference to CPUAllocatorSingleton::instance

它发生的原因是你声明了 CPUAllocatorSingleton 类中的 instance 变量&#xff0c;但没有提供它的定义。 这个错误是链接器无法找到 CPUAllocatorSingleton::instance 的定义。它发生的原因是你声明了 CPUAllocatorSingleton 类中的 instance 变量&#xff0c;但没有提供它的定…...

【c语言】动态内存分配

文章标题 一、为什么要进行动态内存管理二、malloc和free2.1. malloc2.2. free2.3. 举例 三、calloc和realloc3.1. calloc3.2. realloc 四、常见的动态内存错误4.1. 对NULL指针的解引用操作4.2. 对动态开辟空间的越界访问4.3. 对非动态开辟内存使用free释放4.4. 使用free释放⼀…...

深入理解JavaScript中的闭包:原理、应用与常见问题

引言 闭包(Closure)是JavaScript中一个既强大又容易让人困惑的概念。理解闭包对于成为一名优秀的JavaScript开发者至关重要。本文将深入探讨闭包的工作原理、实际应用场景以及常见问题&#xff0c;帮助你彻底掌握这一重要概念。 什么是闭包&#xff1f; 闭包是指那些能够访问…...

IPLOOK | 2025 MVNOs 世界大会:从Wi-Fi通话到卫星覆盖

2025 MVNOs 世界大会于5月12日至14日在奥地利维也纳举行&#xff0c;汇聚了来自50多个国家的550余位行业领袖&#xff0c;共同探讨移动虚拟网络运营商&#xff08;MVNO&#xff09;领域的变革趋势。本届大会聚焦数字化转型、技术创新与战略合作&#xff0c;其中IPLOOK凭借其创新…...

为什么elasticsearch配置文件JVM配置31G最佳

Elasticsearch的JVM堆内存配置为32GB被视为最佳实践&#xff0c;主要基于以下综合技术原理和性能优化考量&#xff1a; 1. ‌JVM指针压缩机制优化内存效率‌ 当堆内存≤32GB时&#xff0c;JVM启用‌对象指针压缩&#xff08;Compressed Ordinary Object Pointers, COOP&#…...