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

封装一个小程序选择器(可多选、单选、搜索)

组件

<template><view class="popup" v-show="show"><view class="bg" @tap="cancelMultiple"></view><view class="selectMultiple"><view class="multipleBody"><view class="title"><view class="close" @tap="cancelMultiple">取消</view><view class="name"><!-- cancelButton="none" 不展示取消按钮--><uni-search-bar@input="updateList"@confirm="onSearchConfirm"cancelButton="none"></uni-search-bar></view><view class="confirm" @tap="confirmMultiple">确认</view></view><view class="list"><view class="mask mask-top"></view><view class="mask mask-bottom"></view><scroll-view class="diet-list" scroll-y="true"><view v-for="(item, index) in list" :class="['item', item.selected ? 'checked' : '']" @tap="onChange(index, item)"><span style="font-size: 16px;">{{item.label}}</span><view class="icon" v-show="item.selected"><icon type="success_no_circle" size="16" color="#2D8DFF"/></view></view></scroll-view></view></view></view></view>
</template><script>
export default {name:"my-curry-multi-select",data() {return {// 选中值value: [],// 选中列表selected: [],// 列表数据list: [],originList: [],};},props: {// 是否显示show: {type: Boolean,default: false},// 标题title: {type: String,default: ''},//数据列表columns: {type: Array,default: []},// 默认选中defaultIndex: {type: Array,default: [],},isMultiSelect: {type: Boolean,default: true},},watch: {// 监听是否显示show(val) {if(val) {this.openMultiple();}}},methods: {sortListWithSelectedFirst(list) {return list.slice().sort((a, b) => {const aSelected = a.selected ? 1 : 0;const bSelected = b.selected ? 1 : 0;return bSelected - aSelected;});},updateList(str) {this.list.map(e => {this.originList.map(item => {if (e.selected && item.value === e.value) {item.selected = true}})})if (str === null || str === undefined || str === '') {this.list = JSON.parse(JSON.stringify(this.originList))} else {const filtered = this.originList.filter(e => e.label.indexOf(str) > -1 || e.selected);this.list = this.sortListWithSelectedFirst(filtered);}},// 新增:处理搜索确认事件onSearchConfirm(e) {const searchValue = e.value || e;// 先更新列表this.updateList(searchValue);// 如果有搜索内容且搜索结果不为空,自动选择第一个未选中的项目if (searchValue && this.list.length > 0) {// 找到第一个未选中的项目const firstUnselectedIndex = this.list.findIndex(item => !item.selected);if (firstUnselectedIndex !== -1) {// 自动选择第一个未选中的项目this.onChange(firstUnselectedIndex, this.list[firstUnselectedIndex]);}}},// 列点击事件onChange(index, item) {// 单选if (!this.isMultiSelect) {this.value = [];this.selected = [];this.value.push(item.value.toString());this.selected.push({label: item.label,value: item.value,});return this.$emit("confirm", {selected: this.selected, value: this.value});}// 是否已选中if(this.value.indexOf(item.value.toString()) >= 0) {this.list[index].selected = false;} else {this.list[index].selected = true;}// 筛选已勾选数据this.value = [];this.selected = [];this.list.forEach((col_item, col_index) => {if(col_item.selected) {this.value.push(col_item.value.toString());this.selected.push({label: col_item.label,value: col_item.value,});}});this.list = this.sortListWithSelectedFirst(this.list);this.$emit("change", {selected: this.selected, value: this.value});},// 弹出框开启触发事件openMultiple() {// 初始化列表数据,默认勾选数据this.value = this.defaultIndex;this.columns.forEach((item, index) => {this.$set(item, "selected", false);if(this.value.indexOf(item.value.toString()) >= 0) {item.selected = true;}});this.originList = Object.assign([], this.columns);this.list = this.sortListWithSelectedFirst(JSON.parse(JSON.stringify(this.originList)));},// 确认confirmMultiple() {this.$emit("confirm", {selected: this.selected, value: this.value});},// 关闭/取消cancelMultiple() {this.$emit("cancel");},}
}
</script><style scoped lang="scss">
.popup {width: 100%;height: 100vh;position: fixed;z-index: 99999;left: 0;top: 0;.bg {width: 100%;height: 100%;background-color: rgba(black, .5);}
}
.selectMultiple {transition: none !important;will-change: transform;width: 100%;position: absolute;left: 0;top: 0;height: 50vh;background-color: white;border-radius: 0 0 20rpx 20rpx;box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.1);overflow: hidden;.multipleBody {width: 100%;height: 100%;padding: 30rpx;box-sizing: border-box;display: flex;flex-direction: column;.title {flex-shrink: 0;font-size: 28rpx;display: flex;flex-direction: row;align-items: center; /* 添加这一行 */.close {width: 80rpx;text-align: left;opacity: .5;}.name {width: 530rpx;text-align: center;overflow: hidden;display: -webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:1;}.confirm {width: 80rpx;text-align: right;color: #2D8DFF;}}.list {flex: 1;overflow: hidden;width: 100%;padding-top: 30rpx;position: relative;.mask {width: 100%;height: 60rpx; // 减小渐变区域position: absolute;left: 0;z-index: 2;pointer-events: none;&.mask-top {top: 30rpx;background-image: linear-gradient(to bottom, #fff, rgba(#fff, 0));}&.mask-bottom {bottom: 0;background-image: linear-gradient(to bottom, rgba(#fff, 0), #fff);}}.diet-list {height: 100%;max-height: none;}.item {display: flex;justify-content: space-between;align-items: center;padding: 20rpx 0;border-bottom: 1px solid rgba(#000, 0.05);span {flex: 1;font-size: 30rpx;text-align: center;}.icon {width: 32rpx;height: 32rpx;}&.checked {color: #2D8DFF;}&:last-child {border-bottom: none;margin-bottom: 60rpx;}&:first-child {margin-top: 60rpx;}}}}
}
</style>

使用

<template><view class="container"><view><uni-forms ref="taskForm" :model="taskForm" labelWidth="80px"><!-- 任务类型单选 --><uni-forms-item label="任务类型" name="type"><view class="item"><view :class="['select', taskForm.type ? 'selected' : '']"@tap="openTypeSelectionBox(taskForm.type)">{{ taskForm.type ? getTaskTypeName(taskForm.type) : '请选择任务类型' }}</view><!-- 如果有内容显示关闭图标 --><uni-icons v-if="taskForm.type !== ''" type="clear" size="24" color="#c0c4cc" class="close-btn"@tap="clearType"></uni-icons><!-- 如果没有内容显示下拉图标 --><uni-icons v-else type="pulldown" size="24" color="#c0c4cc" class="close-btn"@tap="openTypeSelectionBox(taskForm.type)"></uni-icons><my-curry-multi-select title="请选择" :show="taskTypeShow" :columns="taskTypeList":defaultIndex="defaultTaskTypeIndex":isMultiSelect="false"@confirm="confirmType($event)"@cancel="taskTypeShow = false"></my-curry-multi-select></view></uni-forms-item><!-- 负责人多选 --><uni-forms-item label="负责人" name="personInChargeIds"><view class="item"><view :class="['select', (Array.isArray(taskForm.personInChargeIds) && taskForm.personInChargeIds.length > 0) ? 'selected' : '']"@tap="openPersonInChargeIdsMultiSelectionBox(taskForm.personInChargeIds)">{{ (Array.isArray(taskForm.personInChargeIds) && taskForm.personInChargeIds.length > 0)? getUserNamesByIds(taskForm.personInChargeIds): '请选择负责人' }}</view><uni-icons v-if="Array.isArray(taskForm.personInChargeIds) && taskForm.personInChargeIds.length > 0"type="clear" size="24" color="#c0c4cc" class="close-btn"@tap="clearPersonInChargeIds"></uni-icons><uni-icons v-else type="pulldown" size="24" color="#c0c4cc" class="close-btn"@tap="openPersonInChargeIdsMultiSelectionBox(taskForm.personInChargeIds)"></uni-icons><my-curry-multi-select title="请选择" :show="taskTaskPersonInChargesShow":columns="userList":defaultIndex="defaultTaskPersonInChargesIndex":isMultiSelect="true"@confirm="confirmPersonInChargeIds($event)"@cancel="taskTaskPersonInChargesShow = false"></my-curry-multi-select></view></uni-forms-item></uni-forms><button type="primary" @click="submit">提交</button></view></view>
</template><script>
import myCurryMultiSelect from "@/components/curry-multi-select/my-curry-multi-select.vue";export default {components: {myCurryMultiSelect},data() {return {// 当前正选择哪个任务类型元素defaultTaskTypeIndex: [],// 任务类型列表,单选taskTypeList: [{"label": "指派任务", "value": 1},{"label": "设计任务", "value": 2},{"label": "代办", "value": 2}],// 是否展示任务类型下拉选项taskTypeShow: false,// 当前正选择哪些任务负责人元素defaultTaskPersonInChargesIndex: [],// 是否展示任务负责人下拉选项taskTaskPersonInChargesShow: false,// 用户列表,多选userList: [{ value: 1, label: '张三' },{ value: 2, label: '李四' },{ value: 3, label: '王五' },],// 表单数据taskForm: {taskTitle: '',insertUser: '',type: 2,personInChargeIds: [1,2],personInChargeId: null,taskContent: '',requiredCompletionTime: '',actualCompletionTime: '',weight: '',weightScore: '',timeoutStatus: '0',participants: [],taskPictureUrl: [],taskFileUrl: [],useSchedule: '1',standardWorkingHours: '',},// 表单验证规则rules: {type: {rules: [{required: true, errorMessage: '任务类型不能为空'}]},personInChargeIds: {rules: [{required: true,errorMessage: '负责人不能为空',validateFunction: (rule, value) => {return Array.isArray(value) && value.length > 0;}}]},},}},onLoad() {},created() {},onReady() {this.$refs.taskForm.setRules(this.rules)},methods: {// =====单选====// 打开任务类型选择框openTypeSelectionBox(val) {console.log('执行了openTypeSelectionBox,展开选择框时val值是:', val)this.defaultTaskTypeIndex = val !== '' ? [String(val)] : [];console.log('this.defaultTaskTypeIndex',this.defaultTaskTypeIndex)this.taskTypeShow = true;},// 清空类型选择框clearType() {this.defaultTaskTypeIndex = [];this.taskForm.type = '';},// 获取任务类型名称,把值转换为名称显示出来getTaskTypeName(value) {const option = this.taskTypeList.find(item => String(item.value) === String(value));return option ? option.label : '请选择任务类型';},// 确认选择任务类型confirmType(e) {// e是一个数组this.taskForm.type = e.value[0];this.taskTypeShow = false;},// =====多选====// 打开负责人多选框openPersonInChargeIdsMultiSelectionBox(val) {console.log('执行了openPersonInChargeIdsMultiSelectionBox,展开选择框时val值是:', val)this.defaultTaskPersonInChargesIndex = Array.isArray(val) ? val.map(item => String(item)) : [];console.log('this.defaultTaskPersonInChargesIndex', this.defaultTaskPersonInChargesIndex)this.taskTaskPersonInChargesShow = true;},// 清空负责人选择框clearPersonInChargeIds() {this.defaultTaskPersonInChargesIndex = [];this.taskForm.personInChargeIds = null; // 继续保持你的设定},// 获取任务负责人名称,把值转换为名称显示出来getUserNamesByIds(values) {if (!Array.isArray(values) || values.length === 0) return '请选择负责人';const labels = values.map(value => {const option = this.userList.find(item => String(item.value) === String(value));return option ? option.label : value;});return labels.join(',');},// 确认选择任务负责人confirmPersonInChargeIds(e) {// e是一个数组this.taskForm.personInChargeIds = e.value;this.taskTaskPersonInChargesShow = false;},// 提交表单submit() {console.log('提交时表单数据是:', this.taskForm)// 就是上面这个写法有一个问题,就是提交的时候,选择框的绑定的都是字符串。就是是数值,也是转为字符串的。但是前段字符串,后端用Long也能接收。所以问题不大。this.$refs.taskForm.validate().then(res => {this.$modal.msgSuccess("修改成功")})},}
}
</script><style lang="scss">
.item {width: 100%;padding: 0;position: relative;display: flex;align-items: center;height: 35px;.select {flex-grow: 1;border: 1px solid #dadbde;padding: 4px 9px;border-radius: 4px;font-size: 12px;box-sizing: border-box;color: #6a6a6a;line-height: 25px;height: 100%;overflow: hidden;&.selected {color: black;font-size: 15px;}}.close-btn {position: absolute;right: 6px;top: 50%;transform: translateY(-50%);color: red;cursor: pointer;}
}
</style>

效果

效果:

企业微信截图_17483173394654.png

企业微信截图_17483173487807.png

个人站点链接

我的博客链接:https://blog.yimengtut.online/

相关文章:

  • windows安装启动elasticsearch
  • 数据拟合实验
  • TechCrunch 最新文章 (2025-05-28)
  • 【Halcon】 affine_trans_image 算子详解
  • 构建安全高效的邮件网关ngx_mail_ssl_module
  • 【iOS】源码阅读(五)——类类的结构分析
  • 数字孪生赋能智能制造:某汽车发动机产线优化实践
  • SQL中各个子句的执行顺序
  • 亚远景-ISO 21434标准:汽车网络安全实践的落地指南
  • DBus总线详解
  • c++ 拷贝构造函数
  • vue 中的ref属性
  • Grafana-Gauge仪表盘
  • git配置(1): 根据remote自动选择账号执行commit
  • 【掌握文件操作】(下):文件的顺序读写、文件的随机读写、文件读取结束的判定、文件缓冲区
  • C++异常处理机制
  • :inline=“true“会发生什么
  • 酒店用品源头厂家推荐
  • SQL中的锁机制
  • mybatis的mapper对应的xml写法
  • 加强网站内容建设的意见/百度一下你就知道原版
  • 学平面设计网站/seo工具不包括
  • php做网站怎么布局/设计网页
  • 袜子网站建设规划书/seo1现在怎么看不了
  • 怎样把网站做的好看/中国新闻网最新消息
  • 苏州诗华洛网站建设/市场营销实际案例