小程序难调的组件

背景。做小程序用到了自定义表单。前后端都是分开写的,没有使用web-view。所以要做到功能对称

  • 时间选择器。需要区分datetime, year, day等类型使用uview组件较方便
<template><view class="u-date-picker" v-if="visible"><view class="label-container"><text v-if="required" class="required-mark">*</text><text class="label">{{ label }}</text></view><view class="picker-container" :class="{ 'picker-disabled': disabled }" @click="showPicker"><text class="picker-text" :class="{ 'text-disabled': disabled }">{{ displayText }}</text><text class="picker-arrow" :class="{ 'arrow-disabled': disabled }">></text></view><!-- 日期选择器弹窗 --><u-pickerv-model="showPickerFlag"mode="time":params="pickerParams":default-time="defaultTime":start-year="startYear":end-year="endYear":show-time-tag="showTimeTag"@confirm="onConfirm"@cancel="onCancel"></u-picker></view>
</template><script>
export default {name: 'UDatePicker',props: {/*** 标签文本*/label: {type: String,default: '选择日期'},/*** 占位符文本*/placeholder: {type: String,default: '请选择日期'},/*** 当前选中的值*/value: {type: String,default: ''},/*** 日期类型:date-仅日期,datetime-日期时间*/type: {type: String,default: 'date'},/*** 开始年份*/startYear: {type: [String, Number],default: 1950},/*** 结束年份*/endYear: {type: [String, Number],default: 2050},/*** 是否显示时间标签(年月日时分秒)*/showTimeTag: {type: Boolean,default: true},/*** 日期格式*/format: {type: String,default: 'YYYY-MM-DD'},/*** 是否禁用*/disabled: {type: Boolean,default: false},/*** 是否必填*/required: {type: Boolean,default: false},/*** 是否可见*/visible: {type: Boolean,default: true}},data() {return {showPickerFlag: false,pickerParams: this.getPickerParams(),defaultTime: '',selectedDate: ''};},computed: {/*** 显示的文本*/displayText() {if (this.value) {const t = (this.type || 'date').toLowerCase();if (t === 'year') {// 年份类型:直接显示年份return this.value;} else if (t === 'month') {// 月份类型:显示年月return this.value;} else if (t === 'date') {// 日期类型:显示完整日期return this.value;} else if (t === 'week') {// 周类型:显示日期并计算周数try {const date = new Date(this.value);if (!isNaN(date.getTime())) {const weekNumber = this.getWeekNumber(date);return `${this.value} (第${weekNumber}周)`;}} catch (e) {console.error('日期解析错误:', e);}return this.value;} else if (t === 'datetime') {// 日期时间类型:显示完整日期时间return this.value;} else if (t.indexOf('time') !== -1) {// 时间类型:显示时间return this.value;}return this.value;}return this.placeholder;},/*** 根据类型确定选择器模式*/pickerMode() {const t = (this.type || 'date').toLowerCase();if (t === 'year') return 'date';if (t === 'month') return 'date';if (t === 'week') return 'date';if (t === 'date') return 'date';if (t.indexOf('time') !== -1) return 'time';if (t === 'datetime') return 'date'; // datetime 降级为 datereturn 'date';}},watch: {/*** 监听类型变化,更新选择器参数*/type: {handler(newType) {this.pickerParams = this.getPickerParams();},immediate: true},/*** 监听值变化,更新默认时间*/value: {handler(newValue) {if (newValue) {this.defaultTime = newValue;}},immediate: true}},methods: {/*** 根据类型获取选择器参数*/getPickerParams() {const t = (this.type || 'date').toLowerCase();if (t === 'year') {// 年份选择:只显示年份return {year: true,month: false,day: false,hour: false,minute: false,second: false};} else if (t === 'month') {// 月份选择:显示年月return {year: true,month: true,day: false,hour: false,minute: false,second: false};} else if (t === 'date') {// 日期选择:显示年月日return {year: true,month: true,day: true,hour: false,minute: false,second: false};} else if (t === 'week') {// 周选择:显示年月日(周选择需要完整日期来计算周数)return {year: true,month: true,day: true,hour: false,minute: false,second: false};} else if (t === 'datetime') {// 日期时间选择:显示年月日时分秒return {year: true,month: true,day: true,hour: true,minute: true,second: true};} else if (t.indexOf('time') !== -1) {// 时间选择:只显示时分秒return {year: false,month: false,day: false,hour: true,minute: true,second: true};} else {// 默认日期选择return {year: true,month: true,day: true,hour: false,minute: false,second: false};}},/*** 显示选择器*/showPicker() {if (this.disabled) {return;}this.showPickerFlag = true;},/*** 确认选择* @param {Object} e 选择结果*/onConfirm(e) {const { year, month, day, hour, minute, second } = e;console.log('onConfirm', e)// 格式化日期let formattedDate = '';const t = (this.type || 'date').toLowerCase();if (t === 'year') {// 年份选择:只返回年份formattedDate = `${year}`;} else if (t === 'month') {// 月份选择:返回年月formattedDate = `${year}-${this.formatNumber(month)}`;} else if (t === 'date') {// 日期选择:返回完整日期formattedDate = `${year}-${this.formatNumber(month)}-${this.formatNumber(day)}`;} else if (t === 'week') {// 周选择:返回完整日期(用于计算周数)const date = new Date(year, month - 1, day);const weekNumber = this.getWeekNumber(date);formattedDate = `${year}-${this.formatNumber(month)}-${this.formatNumber(day)}`;// 可以添加周数信息到返回值中console.log(`${weekNumber}`);} else if (t === 'datetime') {// 日期时间选择:返回完整日期时间formattedDate = `${year}-${this.formatNumber(month)}-${this.formatNumber(day)} ${this.formatNumber(hour)}:${this.formatNumber(minute)}:${this.formatNumber(second)}`;} else if (t.indexOf('time') !== -1) {// 时间选择:返回时间formattedDate = `${this.formatNumber(hour)}:${this.formatNumber(minute)}:${this.formatNumber(second)}`;} else {// 默认日期格式formattedDate = `${year}-${this.formatNumber(month)}-${this.formatNumber(day)}`;}this.selectedDate = formattedDate;this.$emit('input', formattedDate);this.$emit('change', formattedDate);this.showPickerFlag = false;},/*** 取消选择*/onCancel() {this.showPickerFlag = false;},/*** 格式化数字,补零* @param {Number} num 数字* @returns {String} 格式化后的字符串*/formatNumber(num) {return num < 10 ? `${num}` : `${num}`;},/*** 计算周数* @param {Date} date 日期对象* @returns {Number} 周数*/getWeekNumber(date) {const firstDayOfYear = new Date(date.getFullYear(), 0, 1);const pastDaysOfYear = (date - firstDayOfYear) / 86400000;return Math.ceil((pastDaysOfYear + firstDayOfYear.getDay() + 1) / 7);}}
};
</script><style lang="scss" scoped>
.u-date-picker {margin-bottom: 20rpx;.label-container {display: flex;align-items: center;margin-bottom: 10rpx;.required-mark {color: #ff4757;font-size: 28rpx;margin-right: 8rpx;font-weight: bold;}.label {font-size: 28rpx;color: #333;font-weight: 500;}}.picker-container {display: flex;align-items: center;justify-content: space-between;padding: 20rpx;background-color: #fff;border-radius: 8rpx;border: 1rpx solid #e0e0e0;transition: all 0.3s ease;min-height: 88rpx;box-sizing: border-box;&.picker-disabled {background-color: #f5f5f5;border-color: #d9d9d9;cursor: not-allowed;}.picker-text {flex: 1;font-size: 28rpx;color: #333;line-height: 1.5;&.text-disabled {color: #999;}}.picker-arrow {font-size: 24rpx;color: #999;margin-left: 10rpx;font-weight: bold;&.arrow-disabled {color: #ccc;}}}
}
</style>
  • checkbox选择器,带着其他输入框
<template><view class="dynamic-checkbox"><view class="checkbox-label"><text v-if="required" class="required-mark">*</text><text class="label-text">{{ label }}</text></view><u-checkbox-group :disabled="disabled":active-color="activeColor":size="checkboxSize"><u-checkboxv-for="opt in localOptions"v-model="opt.checked":name="opt.value":disabled="disabled":active-color="activeColor":size="checkboxSize"@change="(value) => handleCheckboxChange(opt, value)"><text class="checkbox-text">{{ opt.label }}</text></u-checkbox><!-- 其他选项的输入框 --></u-checkbox-group><view v-if="otherChoose" class="other-input-container"><u-inputv-model="otherValue":placeholder="otherPlaceholder || '请输入其他内容'"class="other-input":border="true"@input="handleOtherChange"/></view></view>
</template><script>
export default {name: 'DynamicCheckBox',props: {/** 标签文本 */label: {type: String,default: ''},/** 当前选中的值 */value: {type: Array,default: () => []},/** 选项配置数组 */options: {type: Array,default: () => []},/** 是否禁用 */disabled: {type: Boolean,default: false},/** 是否必填 */required: {type: Boolean,default: false},/** 排列方式. horizontal: 水平排列, vertical: 垂直排列 */alignDirection: {type: String,default: 'vertical'},/** 其他选项:输入框提示 */otherPlaceholder: {type: String,default: ''}},data() {return {checkboxValues: [],activeColor: '#2979ff',checkboxSize: 34,localOptions: [], // 本地选项数据,包含checked状态otherValue: '',// 其他输入框值      };},computed: {otherChoose() {// 判断checkboxValues中是否包含-99,如果包含,则返回true\let flag = false;if (this.checkboxValues.length > 0) {flag = this.checkboxValues.some(opt => opt.value === -99 || opt.value === '-99');}console.log('otherChoose:', flag);return flag;}},watch: {value: {handler(newVal) {// 判断newVal与this.checkboxValues是否相等, 若相等, 则不进行更新if (JSON.stringify(newVal) === JSON.stringify(this.checkboxValues)) {return;}// 确保value是数组类型const valueArray = Array.isArray(newVal) ? newVal : [];this.checkboxValues = [...valueArray];// 更新本地选项的checked状态this.updateLocalOptions();},immediate: true},options: {handler(newVal) {console.log('watch.options:', this.checkboxValues );this.localOptions = (newVal || []).map(option => ({...option,checked: false}));this.updateLocalOptions();},immediate: true}},methods: {/*** 更新本地选项的checked状态*/updateLocalOptions() {console.log('updateLocalOptions.checkboxValues:', this.checkboxValues);// 判断this.localOptions中是否有checkboxValues相等的值,  若有, 则将checked设置为truethis.localOptions.forEach(option => {if (this.checkboxValues.some(opt => opt.value === option.value)) {option.checked = true;}});// 遍历this.checkboxValues, 若value == '-99', 则将otherValue赋值给this.otherValuethis.checkboxValues.forEach(opt => {if (opt.value == '-99') {this.otherValue = opt.otherValue;}});},/*** 处理单个checkbox变化事件* @param {Object} option 选项对象* @param {Number} index 选项索引* @param {Boolean} value 是否选中*/handleCheckboxChange(option, value) {// 当option.checked为true时,将option.name添加到checkboxValues中if (value.value) {this.checkboxValues.push({...option});} else {// this.checkboxValues.splice(this.checkboxValues.indexOf({...option}), 1);// 检查checkboxValues数组中是否有value.name的值,有则删除// this.checkboxValues.splice(this.checkboxValues.indexOf({...option}), 1);// 对checkboxvalues重新赋值, 若value.name的值在checkboxvalues中存在则删除this.checkboxValues = this.checkboxValues.filter(opt => opt.value !== option.value);}console.log('单个checkbox变化:', option, value, this.checkboxValues);this.emitChange();},/*** 触发change事件*/emitChange() {this.$emit('change', this.checkboxValues);},/*** 处理其他选项输入框的输入事件* @param {String} value 输入的值*/handleOtherChange(value) {this.otherValue = value;// 轮询checkboxValues数组,找到value=='-99'的对象添加otherValuethis.checkboxValues.forEach(opt => {if (opt.value === '-99') {opt.otherValue = this.otherValue;}});console.log('handleOtherChange:', value, this.checkboxValues);this.emitChange();},/*** 处理其他选项输入框获得焦点事件* @param {Number} index 选项索引*/handleOtherFocus(index) {console.log(`其他选项输入框获得焦点,索引: ${index}`);},/*** 处理其他选项输入框失去焦点事件* @param {Number} index 选项索引*/handleOtherBlur(index) {console.log(`其他选项输入框失去焦点,索引: ${index}`);// 失焦时也触发change事件,确保数据同步// this.emitChange();},}
};
</script><style scoped>
.dynamic-checkbox {background-color: #fff;border-radius: 12rpx;padding: 30rpx;margin-bottom: 20rpx;box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
}.checkbox-label {display: flex;align-items: center;margin-bottom: 20rpx;
}.required-mark {color: #ff4757;font-size: 28rpx;margin-right: 8rpx;font-weight: bold;
}.label-text {font-size: 28rpx;color: #333;font-weight: 500;
}.checkbox-container {display: flex;flex-direction: column;gap: 40rpx;
}.checkbox-container.checkbox-horizontal {flex-direction: row;flex-wrap: wrap;gap: 20rpx;
}.checkbox-container.checkbox-vertical {flex-direction: column;gap: 40rpx;
}.checkbox-item {display: flex;flex-direction: column;align-items: flex-start;padding: 16rpx;border-radius: 8rpx;transition: all 0.2s ease;border: 2rpx solid transparent;min-width: 200rpx;
}.checkbox-item:not(.checkbox-disabled):hover {background-color: #f8f9fa;border-color: #e9ecef;
}.checkbox-item.checkbox-disabled {opacity: 0.6;cursor: not-allowed;
}.checkbox-item.checkbox-checked {background-color: #f0f8ff;border-color: #2979ff;
}.checkbox-option {display: flex;align-items: center;cursor: pointer;width: 100%;
}.checkbox-text {font-size: 28rpx;color: #333;margin-left: 12rpx;user-select: none;flex: 1;line-height: 1.4;
}/* 其他选项输入框样式 */
.other-input-container {width: 100%;margin-top: 16rpx;
}.other-input {width: 100%;
}/* 选中状态下的其他选项样式 */
.checkbox-item.checkbox-checked .other-input {border-color: #2979ff;background-color: #f8f9ff;
}/* 水平排列时的样式优化 */
.checkbox-container.checkbox-horizontal .checkbox-item {flex: 0 0 auto;min-width: 180rpx;max-width: 300rpx;
}.checkbox-container.checkbox-horizontal .other-input-container {margin-top: 12rpx;
}
</style>

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

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

相关文章

从零构建TransformerP2-新闻分类Demo

欢迎来到啾啾的博客&#x1f431;。 记录学习点滴。分享工作思考和实用技巧&#xff0c;偶尔也分享一些杂谈&#x1f4ac;。 有很多很多不足的地方&#xff0c;欢迎评论交流&#xff0c;感谢您的阅读和评论&#x1f604;。 目录引言1 一个完整的Transformer模型2 需要准备的“工…

qt qml实现电话簿 通讯录

qml实现电话簿&#xff0c;基于github上开源代码修改而来&#xff0c;增加了搜索和展开&#xff0c;效果如下 代码如下 #include <QGuiApplication> #include <QQmlApplicationEngine>int main(int argc, char *argv[]) {QCoreApplication::setAttribute(Qt::AA_…

顺序表——C语言

顺序表实现代码解析与学习笔记一、顺序表基础概念顺序表是线性表的一种顺序存储结构&#xff0c;它使用一段连续的内存空间&#xff08;数组&#xff09;存储数据元素&#xff0c;通过下标直接访问元素&#xff0c;具有随机访问的特性。其核心特点是&#xff1a;元素在内存中连…

【Oracle篇】Oracle Data Pump远程备份技术:直接从远端数据库备份至本地环境

&#x1f4ab;《博主主页》&#xff1a;    &#x1f50e; CSDN主页__奈斯DB    &#x1f50e; IF Club社区主页__奈斯、 &#x1f525;《擅长领域》&#xff1a;擅长阿里云AnalyticDB for MySQL(分布式数据仓库)、Oracle、MySQL、Linux、prometheus监控&#xff1b;并对…

Linux系统--文件系统

大家好&#xff0c;我们今天继续来学习Linux系统部分。上一次我们学习了内存级的文件&#xff0c;下面我们来学习磁盘级的文件。那么话不多说&#xff0c;我们开始今天的学习&#xff1a; 目录 Ext系列⽂件系统 1. 理解硬件 1-1 磁盘、服务器、机柜、机房 1-2 磁盘物理结构…

KUKA库卡焊接机器人氩气节气设备

在焊接生产过程中&#xff0c;氩气作为一种重要的保护气体被广泛应用于KUKA库卡焊接机器人的焊接操作中。氩气的消耗往往是企业生产成本的一个重要组成部分&#xff0c;因此实现库卡焊接机器人节气具有重要的经济和环保意义。WGFACS节气装置的出现为解决这一问题提供了有效的方…

远程连接----ubuntu ,rocky 等Linux系统,WindTerm_2.7.0

新一代开源免费的终端工具-WindTerm github 27.5k⭐ https://github.com/kingToolbox/WindTerm/releases/download/2.7.0/WindTerm_2.7.0_Windows_Portable_x86_64.zip 主机填写你自己要连接的主机ip 端口默认 22 改成你ssh文件配置的端口 输入远程的 用户名 与密码 成功连接…

笔试——Day32

文章目录第一题题目思路代码第二题题目&#xff1a;思路代码第三题题目&#xff1a;思路代码第一题 题目 素数回文 思路 模拟 构建新的数字&#xff0c;判断该数是否为素数 代码 第二题 题目&#xff1a; 活动安排 思路 区间问题的贪⼼&#xff1a;排序&#xff0c;然…

超高车辆如何影响城市立交隧道安全?预警系统如何应对?

超高车辆对立交隧道安全的潜在威胁在城市立交和隧道中&#xff0c;限高设施的设计通常考虑到大部分正常通行的货车和运输车辆。然而&#xff0c;一些超高的货车、集装箱车或特殊车辆如果未经有效监测而进入限高区域&#xff0c;就可能对道路设施造成极大的安全隐患。尤其在立交…

解决 MinIO 上传文件时报 S3 API Requests must be made to API port错误

在使用 MinIO 进行文件上传时&#xff0c;我遇到了一个比较坑的问题。错误日志如下&#xff1a; io.minio.errors.InvalidResponseException: Non-XML response from server. Response code: 400, Content-Type: text/xml; charsetutf-8, body: <?xml version"1.0&quo…

linux_https,udp,tcp协议(更新中)

目录 https 加密类型 对称加密 非对称加密 加密方案 只用对程加密 只用非对程加密 双方都是用非对程加密 非对称对称加密 非对称对称加密证书 流程图 校验流程图 udp udp协议格式 特点 UDP缓冲区 tcp tcp协议格式 32位序号及确认序号 4位首部 6位标志位 1…

web端-登录页面验证码的实现(springboot+vue前后端分离)超详细

目录 一、项目技术栈 二、实现效果图 ​三、实现路线 四、验证码的实现步骤 五、完整代码 1.前端 2.后端 一、项目技术栈 登录页面暂时涉及到的技术栈如下: 前端 Vue2 Element UI Axios&#xff0c;后端 Spring Boot 2 MyBatis MySQL JWT Maven 二、实现效果图…

疯狂星期四文案网第33天运营日记

网站运营第33天&#xff0c;点击观站&#xff1a; 疯狂星期四 crazy-thursday.com 全网最全的疯狂星期四文案网站 运营报告 今日访问量 今日搜索引擎收录情况 必应收录239个页面&#xff0c;还在持续增加中&#xff0c;已经获得必应的认可&#xff0c;逐渐收录所有页面 百度…

客户端利用MinIO对服务器数据进行同步

MinIO 是一款高性能、开源的对象存储服务&#xff0c;专为海量数据存储设计&#xff0c;兼容 Amazon S3 API&#xff08;即与 AWS S3 协议兼容&#xff09;&#xff0c;可用于构建私有云存储、企业级数据湖、备份归档系统等场景。它以轻量、灵活、高效为核心特点&#xff0c;广…

WPF 双击行为实现详解:DoubleClickBehavior 源码分析与实战指南

WPF 双击行为实现详解:DoubleClickBehavior 源码分析与实战指南 文章目录 WPF 双击行为实现详解:DoubleClickBehavior 源码分析与实战指南 引言 一、行为(Behavior)基础概念 1.1 什么是行为? 1.2 行为的优势 二、DoubleClickBehavior 源码分析 2.1 类定义与依赖属性 2.2 双…

零知开源——基于STM32F103RBT6的TDS水质监测仪数据校准和ST7789显示实战教程

✔零知开源是一个真正属于国人自己的开源软硬件平台&#xff0c;在开发效率上超越了Arduino平台并且更加容易上手&#xff0c;大大降低了开发难度。零知开源在软件方面提供了完整的学习教程和丰富示例代码&#xff0c;让不懂程序的工程师也能非常轻而易举的搭建电路来创作产品&…

luogu P3387 【模板】缩点

原题链接 原题再现 题目描述 给定一个 n 个点 m 条边有向图&#xff0c;每个点有一个权值&#xff0c;求一条路径&#xff0c;使路径经过的点权值之和最大。你只需要求出这个权值和。 允许多次经过一条边或者一个点&#xff0c;但是&#xff0c;重复经过的点&#xff0c;权…

P1119 灾后重建【题解】

P1119 灾后重建 题目背景 B 地区在地震过后&#xff0c;所有村庄都造成了一定的损毁&#xff0c;而这场地震却没对公路造成什么影响。但是在村庄重建好之前&#xff0c;所有与未重建完成的村庄的公路均无法通车。换句话说&#xff0c;只有连接着两个重建完成的村庄的公路才能通…

Horse3D引擎研发笔记(二):基于QtOpenGL使用仿Three.js的BufferAttribute结构重构三角形绘制

在Horse3D引擎的研发过程中&#xff0c;我们致力于构建一个高效、灵活且易于扩展的3D图形引擎。在本篇博客中&#xff0c;我们将详细记录如何基于QtOpenGL框架&#xff0c;使用仿Three.js的BufferAttribute结构&#xff0c;重构三角形绘制流程。通过这一过程&#xff0c;我们希…

MCU程序段的分类

程序的下载&#xff08;烧录到存储器中&#xff09;通常是按照程序文件分段&#xff08;Code段、RO_data段、RW_data段、ZI_data段&#xff09;的方式存储的&#xff0c;但运行时内存的布局会按照程序进程分段&#xff08;TEXT段、DATA段、BSS段、堆栈段&#xff09;进行组织。…