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

小程序难调的组件

背景。做小程序用到了自定义表单。前后端都是分开写的,没有使用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.dtcms.com/a/321465.html

相关文章:

  • 悬赏任务系统网站兼职赚钱小程序搭建地推抖音视频任务拉新源码功能详解二开
  • LangChain学习笔记05——多模态开发与工具使用
  • react+echarts实现变化趋势缩略图
  • LabVIEW数字抽取滤波
  • 点播服务器
  • RabbitMQ 中无法路由的消息会去到哪里?
  • Spring AMQP 入门与实践:整合 RabbitMQ 构建可靠消息系统
  • Android12 Framework Sim卡pin与puk码解锁
  • 用LaTeX优化FPGA开发:结合符号计算与Vivado工具链(二)
  • Nature论文-预测和捕捉人类认知的基础模型-用大模型模拟人类认知
  • 麦芽:寻常食材的中医智慧 多炮制方式各显养生价值
  • 动态规划进阶:转移方程优化技巧全解
  • 安卓应用内WebView页面调试技巧
  • WPF 双击行为实现详解:DoubleClickBehavior 源码分析与实战指南
  • 政治社会时间线
  • Java 之 多态
  • UE5太空射击游戏入门(一):项目创建与飞船控制
  • HEVC视频扩展免费下载
  • ISL9V3040D3ST-F085C一款安森美 ON生产的汽车点火IGBT模块,绝缘栅双极型晶体管ISL9V3040D3ST汽车点火电路中的线圈驱动器
  • Redis对象编码
  • 分布式系统性能优化实战:从瓶颈定位到架构升级
  • J2000与WGS84坐标系
  • Docker--docker的学习
  • Visual Studio 2019 + Qt + MySQL 开发调试全过程问题详解
  • 装配式建筑4.0:建筑工业化的智慧飞跃
  • 训练模型时梯度出现NAN或者inf
  • WiFi 核心概念与实战用例全解
  • git环境配置_笔记
  • [Linux]学习笔记系列 -- [arm[kernel]
  • modem上报SIM卡状态为unknown问题分析