更新记录
0.0.2(2025-11-06) 下载此版本
移除对于uview的依赖
0.0.1(2025-11-06) 下载此版本
基于uview的三级联动加末级节点多选
平台兼容性
uni-app(4.84)
| Vue2 | Vue3 | Chrome | Safari | app-vue | app-nvue | Android | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|
| √ | - | √ | √ | √ | - | - | - | - |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|
| √ | - | - | - | - | - | - | - | - | - | - |
MedicalProjectModal 医疗项目选择器
一个基于 uni-app 的三级联动选择器组件,支持多选功能,适用于医疗美容项目的选择场景。
✨ 特点
- 🎯 纯原生实现:不依赖任何第三方 UI 组件库
- 🎨 优雅动画:流畅的弹出和收起动画效果
- 📱 完美适配:支持各种屏幕尺寸,响应式设计
- ⚡ 性能优化:使用 Vue 3 Composition API,性能更优
版本支持
- ✅ Vue 3: 完全支持(使用 Composition API +
<script setup>语法) - ❌ Vue 2: 不支持(组件使用了 Vue 3 特有的
<script setup>语法)
依赖
uni-app框架(Vue 3 版本)- 无需任何第三方 UI 组件库 ✅
引入方式
1. 页面中直接引入(推荐)
<script setup>
import MedicalProjectModal from '@/components/MedicalProjectModal/MedicalProjectModal.vue';
import { ref } from 'vue';
const showModal = ref(false);
const projectData = ref([
{
id: 1,
name: '皮肤美容',
children: [
{
id: 11,
name: '祛斑美白',
children: [
{ id: 111, name: '激光祛斑' },
{ id: 112, name: '光子嫩肤' }
]
}
]
}
]);
const handleConfirm = (selectedItems) => {
console.log('选中的项目:', selectedItems);
};
</script>
<template>
<view>
<button @click="showModal = true">选择医疗项目</button>
<MedicalProjectModal
v-model:show="showModal"
:data="projectData"
@confirm="handleConfirm"
/>
</view>
</template>
2. 全局注册(可选)
// main.js
import { createSSRApp } from 'vue';
import MedicalProjectModal from '@/components/MedicalProjectModal/MedicalProjectModal.vue';
export function createApp() {
const app = createSSRApp(App);
// 全局注册
app.component('MedicalProjectModal', MedicalProjectModal);
return { app };
}
属性 Props
| 属性名 | 类型 | 默认值 | 必填 | 说明 |
|---|---|---|---|---|
| show | Boolean | false |
否 | 控制弹窗显示/隐藏,支持 v-model |
| data | Array | [] |
是 | 三级联动数据源 |
data 数据结构
interface ProjectItem {
id: number | string; // 唯一标识
name: string; // 显示名称
children?: ProjectItem[]; // 子级数据
}
type ProjectData = ProjectItem[];
data 示例
const projectData = [
{
id: 1,
name: '皮肤美容',
children: [
{
id: 11,
name: '祛斑美白',
children: [
{ id: 111, name: '激光祛斑' },
{ id: 112, name: '光子嫩肤' },
{ id: 113, name: '水光针' }
]
},
{
id: 12,
name: '抗衰老',
children: [
{ id: 121, name: '热玛吉' },
{ id: 122, name: '超声刀' }
]
}
]
},
{
id: 2,
name: '注射美容',
children: [
{
id: 21,
name: '玻尿酸',
children: [
{ id: 211, name: '苹果肌填充' },
{ id: 212, name: '隆鼻' }
]
}
]
}
];
事件 Events
| 事件名 | 参数 | 说明 |
|---|---|---|
| update:show | (value: Boolean) |
弹窗显示状态改变时触发,支持 v-model |
| confirm | (selectedItems: Array) |
点击确定按钮时触发,返回选中的项目数组 |
confirm 事件返回值
// selectedItems 结构
[
{ id: 111, name: '激光祛斑' },
{ id: 122, name: '超声刀' }
]
完整使用示例
<script setup>
import { ref } from 'vue';
import MedicalProjectModal from '@/components/MedicalProjectModal/MedicalProjectModal.vue';
// 控制弹窗显示
const showModal = ref(false);
// 已选择的项目
const selectedProjects = ref([]);
// 三级联动数据
const projectData = ref([
{
id: 1,
name: '皮肤美容',
children: [
{
id: 11,
name: '祛斑美白',
children: [
{ id: 111, name: '激光祛斑' },
{ id: 112, name: '光子嫩肤' },
{ id: 113, name: '水光针' }
]
},
{
id: 12,
name: '抗衰老',
children: [
{ id: 121, name: '热玛吉' },
{ id: 122, name: '超声刀' },
{ id: 123, name: 'Botox' }
]
}
]
},
{
id: 2,
name: '注射美容',
children: [
{
id: 21,
name: '玻尿酸',
children: [
{ id: 211, name: '苹果肌填充' },
{ id: 212, name: '隆鼻' },
{ id: 213, name: '丰唇' }
]
},
{
id: 22,
name: '肉毒素',
children: [
{ id: 221, name: '瘦脸针' },
{ id: 222, name: '除皱针' }
]
}
]
},
{
id: 3,
name: '身体塑形',
children: [
{
id: 31,
name: '减脂塑形',
children: [
{ id: 311, name: '吸脂手术' },
{ id: 312, name: '冷冻溶脂' }
]
}
]
}
]);
// 打开弹窗
const openModal = () => {
showModal.value = true;
};
// 确认选择
const handleConfirm = (items) => {
selectedProjects.value = items;
console.log('选中的项目:', items);
// 提取项目名称
const names = items.map(item => item.name).join('、');
uni.showToast({
title: `已选择:${names}`,
icon: 'none',
duration: 2000
});
};
</script>
<template>
<view class="page">
<!-- 触发按钮 -->
<button @click="openModal" class="select-btn">
选择医疗项目
</button>
<!-- 显示已选择的项目 -->
<view v-if="selectedProjects.length > 0" class="selected-list">
<text class="label">已选项目:</text>
<view
v-for="item in selectedProjects"
:key="item.id"
class="project-tag"
>
{{ item.name }}
</view>
</view>
<!-- 医疗项目选择器 -->
<MedicalProjectModal
v-model:show="showModal"
:data="projectData"
@confirm="handleConfirm"
/>
</view>
</template>
<style lang="scss" scoped>
.page {
padding: 32rpx;
}
.select-btn {
width: 100%;
height: 88rpx;
background: #FF7575;
color: #fff;
border-radius: 44rpx;
font-size: 32rpx;
}
.selected-list {
margin-top: 32rpx;
.label {
font-size: 28rpx;
color: #666;
margin-bottom: 16rpx;
}
.project-tag {
display: inline-block;
padding: 8rpx 24rpx;
margin: 8rpx 16rpx 8rpx 0;
background: #FFF5F5;
color: #FF7575;
border-radius: 32rpx;
font-size: 26rpx;
}
}
</style>
功能特性
1. 三级联动
- 第一级:选择大分类(单选)
- 第二级:选择子分类(单选)
- 第三级:选择具体项目(多选)
2. 多选支持
- 第三级支持多选,可以跨分类选择
- 选中后会显示勾选图标(✓)
- 底部展示已选择的项目标签
3. 已选项目管理
- 底部显示所有已选择的项目
- 点击标签可以快速移除(×图标)
- 确认前必须至少选择一个项目
4. 交互优化
- 自动选中第一级第一项
- 切换第一级时不清空已选项目,支持跨分类选择
- 支持点击遮罩层关闭弹窗
- 支持取消和确定按钮
- 流畅的弹出/收起动画效果
5. 视觉设计
- 粉红色主题(#FF7575),可自定义
- 选中状态带左侧指示条
- 按压反馈效果
- 圆角卡片设计
注意事项
- 数据格式:确保传入的
data符合三级结构,每级都有id、name和children字段 - 唯一 ID:每个项目的
id必须唯一,用于多选判断 - 至少选择一项:确认时如果未选择任何项目,会提示用户
- Vue 3 专用:此组件使用 Vue 3 Composition API,不兼容 Vue 2
- 纯原生组件:不依赖任何第三方 UI 库,开箱即用
样式定制
组件内部使用了 scoped 样式,如需修改样式,可以:
方法 1:使用深度选择器(推荐)
<style lang="scss">
// 修改标题颜色
:deep(.medical-project-modal .modal-header .title) {
color: #333;
}
// 修改主题色(确定按钮、选中状态等)
:deep(.medical-project-modal .confirm-btn) {
color: #007aff;
}
:deep(.picker-item.active) {
color: #007aff;
background: #E6F2FF;
}
// 修改遮罩层透明度
:deep(.modal-mask) {
background: rgba(0, 0, 0, 0.7);
}
</style>
方法 2:直接修改组件源码
修改 MedicalProjectModal.vue 文件中的 <style> 部分:
主要样式变量位置:
- 主题色:
#FF7575(搜索替换为你的品牌色) - 圆角:
border-radius: 20rpx - 弹窗高度:
height: 600rpx(.picker-container) - 动画时长:
transition: 0.3s
主题色自定义示例
如果要将主题色从粉红色改为蓝色:
// 在组件源码中搜索并替换
#FF7575 → #007aff // 主题色
#FFF5F5 → #E6F2FF // 浅色背景
实现细节
组件结构
MedicalProjectModal
├── modal-mask(遮罩层)
│ └── modal-wrapper(弹窗包装器)
│ └── medical-project-modal
│ ├── modal-header(顶部操作栏)
│ ├── picker-container(选择器容器)
│ │ └── picker-columns(三列布局)
│ │ ├── picker-column(第一级)
│ │ ├── picker-column(第二级)
│ │ └── picker-column(第三级,多选)
│ └── selected-area(已选择区域)
核心逻辑
- 弹窗动画:使用 CSS
transform和opacity实现流畅的弹出效果 - 多选实现:使用数组存储选中项,通过
findIndex判断是否已选 - 三级联动:使用
computed计算属性响应式更新子级数据 - 图标实现:使用原生文本符号(✓ 和 ×),无需图标库
图标说明
组件使用纯文本符号作为图标:
- 勾选图标:
✓(Unicode: U+2713) - 关闭图标:
×(Unicode: U+00D7)
如需更换图标样式,可以:
- 使用其他 Unicode 符号
- 使用 emoji 表情
- 使用自定义字体图标
常见问题
Q1: 如何获取选中项目的完整路径?
A: 当前组件只返回第三级选中的项目。如需完整路径,可以在 handleConfirm 中自行处理:
const handleConfirm = (items) => {
// 方案 1:修改组件,在选中时记录完整路径
// 方案 2:根据 id 从原始数据中递归查找完整路径
const findPath = (data, targetId, path = []) => {
for (const item of data) {
const currentPath = [...path, item];
if (item.id === targetId) return currentPath;
if (item.children) {
const result = findPath(item.children, targetId, currentPath);
if (result) return result;
}
}
return null;
};
};
Q2: 如何设置默认选中?
A: 可以在组件中添加 defaultSelected prop 和初始化逻辑:
// 在组件 props 中添加
const props = defineProps({
// ...其他 props
defaultSelected: {
type: Array,
default: () => []
}
});
// 在 watch 中初始化
watch(() => props.show, (newVal) => {
if (newVal) {
if (props.defaultSelected.length > 0) {
selectedThirdItems.value = [...props.defaultSelected];
}
}
});
Q3: 如何限制最多选择数量?
A: 可以在 toggleThird 方法中添加判断:
// 添加 prop
const props = defineProps({
// ...
maxCount: {
type: Number,
default: 10 // 最多选择 10 个
}
});
// 修改 toggleThird 方法
const toggleThird = (item) => {
const index = selectedThirdItems.value.findIndex(i => i.id === item.id);
if (index > -1) {
selectedThirdItems.value.splice(index, 1);
} else {
if (selectedThirdItems.value.length >= props.maxCount) {
uni.showToast({
title: `最多选择${props.maxCount}个项目`,
icon: 'none'
});
return;
}
selectedThirdItems.value.push(item);
}
};
Q4: 为什么不用图标库?
A: 为了减少依赖和提升性能,组件使用原生 Unicode 符号作为图标。这样做的好处:
- ✅ 零依赖,开箱即用
- ✅ 体积更小,加载更快
- ✅ 兼容性更好
- ✅ 易于自定义
如果你需要更精美的图标,可以轻松替换为:
- uni-app 内置图标
- 自定义 SVG 图标
- 第三方图标库(如 uview、uni-icons 等)
Q5: 如何清空已选择的项目?
A: 组件内部提供了清空方法,你可以通过 ref 调用:
<script setup>
import { ref } from 'vue';
const modalRef = ref(null);
// 清空选择
const clearSelection = () => {
if (modalRef.value) {
modalRef.value.selectedThirdItems = [];
}
};
</script>
<template>
<MedicalProjectModal
ref="modalRef"
v-model:show="showModal"
:data="projectData"
/>
</template>
更新日志
v2.0.0 (2025-11-06)
- 🎉 移除 uview 组件库依赖,改用纯原生实现
- ✨ 优化弹窗动画效果
- 🎨 使用 Unicode 符号替代图标库
- 📝 完善文档说明
v1.0.0
- 🎉 初始版本发布
- ✨ 支持三级联动选择
- ✨ 支持第三级多选
- ✨ 已选项目管理功能
License
MIT
反馈与贡献
如有问题或建议,欢迎提出 Issue 或 Pull Request。

收藏人数:
下载插件并导入HBuilderX
赞赏(0)
下载 8
赞赏 0
下载 10794589
赞赏 1798
赞赏
京公网安备:11010802035340号