highlight shake

博客指出QML开启高亮特效后组件边框会出现抖动问题,解决方法是将高亮的z值设置为1以上,因为原z值过小导致该问题。

问题:QML开启高亮特效后,组件边框出现抖动。

方法:高亮的z值过小,应设置为1以上的值。

<template> <view class="container"> <view class="header"> <text class="header-title">在线投诉举报平台</text> <text class="header-subtitle">诚信是企业生存和发展的根本</text> </view> <!-- 欢迎页面 --> <view v-if="showWelcome" class="welcome-section"> <view class="welcome-image"> <image src="/static/OnlinComplaint.jpg" mode=""></image> </view> <view class="welcome-content"> <text class="welcome-text">欢迎举报</text> <text class="welcome-text indent">诚信是做人做事的根本,同样诚信的企业才能生存和发展。其亚的任何人,无论职务的高低,都应谨守廉洁并自觉接受各方监督。</text> <text class="welcome-text indent">任何单位和个人都有有权举报其亚员工的违法违纪行为。同时,我们将对您提供的信息严格保密,一经查实,将根据公司制度给予相应奖励。</text> <text class="welcome-text indent">您可以选择匿名举报,无需填写个人联系方式。</text> </view> <view class="accept-box" :class="{disabled: countdown > 0}" @tap="handleAccept" > <text>我已阅读并理解上述内容</text> <view class="timer"> {{ countdown }}s </view> </view> </view> <!-- 表单页面 --> <view v-else class="form-section"> <view class="section-title"> <uni-icons type="compose" size="24" color="#3498db"></uni-icons> <text>投诉信息</text> </view> <view class="form-row"> <view class="form-col"> <view class="form-group"> <label for="title" class="required">投诉标题</label> <input type="text" id="title" placeholder="请输入投诉标题" v-model="formData.title" :class="{error: errors.title}" @blur="validateField('title')" > <text v-if="errors.title" class="error-text">{{ errors.title }}</text> </view> </view> <view class="form-col"> <view class="form-group"> <label for="category" class="required">类别</label> <picker @change="bindPickerChange" :value="categoryIndex" :range="categories.map(item => item.label)" class="picker" @blur="validateField('category')" > <view class="picker-content" :class="{placeholder: !formData.category, error: errors.category}"> {{ formData.category || '请选择投诉类别' }} </view> </picker> <text v-if="errors.category" class="error-text">{{ errors.category }}</text> </view> </view> </view> <view class="form-row"> <view class="form-col"> <view class="form-group"> <label for="complaineeName" class="required">被投诉人姓名</label> <input type="text" id="complaineeName" placeholder="请输入被投诉人姓名" v-model="formData.complaineeName" :class="{error: errors.complaineeName}" @blur="validateField('complaineeName')" > <text v-if="errors.complaineeName" class="error-text">{{ errors.complaineeName }}</text> </view> </view> <view class="form-col"> <view class="form-group"> <label for="company" class="required">被投诉人公司</label> <input type="text" id="company" placeholder="请输入公司名称" v-model="formData.company" :class="{error: errors.company}" @blur="validateField('company')" > <text v-if="errors.company" class="error-text">{{ errors.company }}</text> </view> </view> </view> <view class="form-row"> <view class="form-col"> <view class="form-group"> <label for="department" class="required">被投诉人部门</label> <input type="text" id="department" placeholder="请输入部门名称" v-model="formData.department" :class="{error: errors.department}" @blur="validateField('department')" > <text v-if="errors.department" class="error-text">{{ errors.department }}</text> </view> </view> </view> <view class="form-group"> <label for="content" class="required">投诉内容</label> <textarea id="content" placeholder="请您如实详细填写投诉内容(时间、地点、事件、经过)" v-model="formData.content" :class="{error: errors.content}" @blur="validateField('content')" ></textarea> <view class="content-counter"> <text>{{ formData.content.length }}/2000</text> </view> <text v-if="errors.content" class="error-text">{{ errors.content }}</text> </view> <view class="section-title"> <uni-icons type="paperclip" size="24" color="#3498db"></uni-icons> <text>上传附件</text> </view> <view class="upload-area" @tap="chooseAndUploadFile" :class="{disabled: uploadDisabled}"> <view class="upload-icon"> <uni-icons type="cloud-upload" size="48" color="#3498db"></uni-icons> </view> <view class="upload-text">点击上传到此处</view> <view class="file-types">支持word文档、照片、MP3、MP4等格式,每次上传文件大小不超过10M,附件过大请发投诉渠道邮箱</view> <view v-if="uploading" class="upload-progress"> <uni-progress :percent="uploadProgress" stroke-width="48" activeColor="#3498db"></uni-progress> </view> </view> <view v-if="files.length > 0" class="file-list"> <view v-for="(file, index) in files" :key="index" class="file-item"> <uni-icons type="paperclip" size="18" color="#3498db"></uni-icons> <view class="file-info"> <text class="file-name">{{ file.name }}</text> <text class="file-size">({{ formatFileSize(file.size) }})</text> </view> <view class="delete-btn" @tap="removeFile(index)" >X <uni-icons type="close" size="16" color="#ffffff" ></uni-icons> </view> </view> </view> <view class="section-title"> <uni-icons type="person" size="24" color="#3498db"></uni-icons> <text>联系方式(选填,支持匿名)</text> </view> <view class="form-row"> <view class="form-col"> <view class="form-group"> <label for="name">姓名(选填)</label> <input type="text" id="name" placeholder="请输入您的姓名(选填)" v-model="formData.name" :class="{error: errors.name}" @blur="validateField('name')" > <text v-if="errors.name" class="error-text">{{ errors.name }}</text> </view> </view> <view class="form-col"> <view class="form-group"> <label for="phone">手机号(选填)</label> <input type="number" id="phone" placeholder="请输入您的手机号(选填)" v-model="formData.phone" :class="{error: errors.phone}" @blur="validateField('phone')" > <text v-if="errors.phone" class="error-text">{{ errors.phone }}</text> </view> </view> </view> <view class="form-row"> <view class="form-col"> <view class="form-group"> <label for="email">邮箱(选填)</label> <input type="email" id="email" placeholder="请输入您的邮箱(选填)" v-model="formData.email" :class="{error: errors.email}" @blur="validateField('email')" > <text v-if="errors.email" class="error-text">{{ errors.email }}</text> </view> </view> </view> <view class="privacy-note"> 您可以选择匿名举报,无需填写个人联系方式。如填写联系方式,我们会对您的个人信息及投诉内容进行严格保密,便于后续跟进调查。 </view> <button class="submit-btn" @tap="submitForm" :disabled="uploading || processing"> <template v-if="processing">处理中...</template> <template v-else>提交投诉</template> </button> </view> </view> </template> <script setup> import { ref, onMounted } from 'vue'; import { onShow } from '@dcloudio/uni-app'; // 存储openID的变量 const openId = ref(''); // 表单数据 const formData = ref({ title: '', category: '', // 存储显示的类别名称 categoryValue: '', // 存储实际提交的类别值 complaineeName: '', company: '', department: '', content: '', name: '', // 可选 phone: '', // 可选 email: '' // 可选 }); // 错误信息 const errors = ref({}); // 文件列表 - 存储上传成功后的文件信息 const files = ref([]); // 上传状态管理 const uploading = ref(false); const uploadProgress = ref(0); const uploadDisabled = ref(false); // 处理状态(用于获取OpenID和提交过程中) const processing = ref(false); // 类别选择(包含显示文本和对应的值) const categories = ref([ { label: '财务违规', value: '1' }, { label: '道德问题', value: '2' }, { label: '职场骚扰', value: '3' }, { label: '安全隐患', value: '4' } ]); const categoryIndex = ref(-1); // 欢迎页状态 const showWelcome = ref(true); const countdown = ref(5); // 处理接受按钮点击 const handleAccept = () => { if (countdown.value > 0) return; showWelcome.value = false; }; // 选择类别 - 同时记录显示文本和实际值 const bindPickerChange = (e) => { categoryIndex.value = e.detail.value; const selected = categories.value[e.detail.value]; formData.value.category = selected.label; // 显示用 formData.value.categoryValue = selected.value; // 实际提交用 validateField('category'); // 选择后立即验证 }; // 选择并上传文件 const chooseAndUploadFile = async () => { if (uploading.value) return; try { // 选择文件 const res = await uni.chooseFile({ count: 5 - files.value.length, // 限制总文件数不超过5个 type: 'all', extension: ['png', 'jpg', 'jpeg', 'gif', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'pdf', 'mp3', 'mp4'], }); if (res.tempFiles && res.tempFiles.length > 0) { // 开始上传 uploadFiles(res.tempFiles); } } catch (err) { console.log('选择文件失败', err); uni.showToast({ title: '选择文件失败', icon: 'error', duration: 2000 }); } }; // 上传文件到服务器 const uploadFiles = async (tempFiles) => { uploading.value = true; uploadDisabled.value = true; uploadProgress.value = 0; // 显示加载弹窗(小圆圈) uni.showLoading({ title: '正在上传...', mask: true // 防止背景点击 }); try { // 计算总文件大小 const totalSize = tempFiles.reduce((sum, file) => sum + file.size, 0); let uploadedSize = 0; // 逐个上传文件 for (let i = 0; i < tempFiles.length; i++) { const file = tempFiles[i]; await new Promise((resolve, reject) => { uni.uploadFile({ url: '/api/file/upload', filePath: file.path, name: 'file', header: {}, timeout: 360000, // 1分钟超时 // 移除进度更新逻辑(不再需要) success: (res) => { try { const response = JSON.parse(res.data); if (res.statusCode === 200 && response.code === 200 && response.data) { uploadedSize += file.size; files.value.push({ name: response.data.name, size: file.size, url: response.data.url, id: Date.now() + i }); resolve(); } else { throw new Error(response.msg || `文件上传失败,错误代码: ${response.code}`); } } catch (err) { reject(err); } }, fail: (err) => { reject(new Error(`上传失败: ${err.errMsg}`)); } }); }); } // 文件上传成功后提示 uni.showToast({ title: '文件上传成功', icon: 'success' }); } catch (err) { console.error('文件上传失败', err); uni.showToast({ title: err.message || '文件上传失败', icon: 'error' }); } finally { // 关闭加载弹窗 uni.hideLoading(); uploading.value = false; uploadDisabled.value = false; uploadProgress.value = 0; } }; // 移除文件 const removeFile = (index) => { files.value.splice(index, 1); }; // 格式化文件大小 const formatFileSize = (bytes) => { if (bytes === 0) return '0 B'; const k = 1024; const sizes = ['B', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; }; // 单个字段验证 const validateField = (field) => { const value = formData.value[field]; let error = ''; switch(field) { case 'title': if (!value) error = '请输入投诉标题'; else if (value.length > 100) error = '标题长度不能超过100个字符'; break; case 'category': if (!value) error = '请选择投诉类别'; break; case 'complaineeName': if (!value) error = '请输入被投诉人姓名'; else if (value.length > 50) error = '姓名长度不能超过50个字符'; break; case 'company': if (!value) error = '请输入被投诉人公司'; else if (value.length > 100) error = '公司名称长度不能超过100个字符'; break; case 'department': if (!value) error = '请输入被投诉人部门'; else if (value.length > 100) error = '部门名称长度不能超过100个字符'; break; case 'content': if (!value) error = '请输入投诉内容'; else if (value.length < 1) error = '投诉内容不能为空'; break; case 'name': if (value && value.length > 50) error = '姓名长度不能超过50个字符'; break; case 'phone': const phoneRegex = /^1[3-9]\d{9}$/; if (value && value.trim() !== '' && !phoneRegex.test(value)) { error = '请输入有效的手机号码'; } break; case 'email': const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (value && value.trim() !== '' && !emailRegex.test(value)) { error = '请输入有效的邮箱地址'; } break; } errors.value[field] = error; return !error; }; // 表单验证 - 验证所有必填项和格式 const validateForm = () => { errors.value = {}; let isValid = true; // 验证所有字段 const allFields = ['title', 'category', 'complaineeName', 'company', 'department', 'content', 'name', 'phone', 'email']; allFields.forEach(field => { if (!validateField(field)) { isValid = false; } }); return isValid; }; // 提交表单 const submitForm = async () => { if (uploading.value || processing.value) { uni.showToast({ title: '操作处理中,请稍后', icon: 'none', duration: 2000 }); return; } // 先验证表单 const isValid = validateForm(); if (!isValid) { // 滚动到第一个错误字段 const firstErrorField = Object.keys(errors.value).find(key => errors.value[key]); if (firstErrorField) { const element = document.getElementById(firstErrorField); if (element) { element.scrollIntoView({ behavior: 'smooth', block: 'center' }); // 给错误字段添加闪烁效果 element.classList.add('error-highlight'); setTimeout(() => element.classList.remove('error-highlight'), 1000); } } const firstError = Object.values(errors.value).find(msg => msg); if (firstError) { uni.showToast({ title: firstError, icon: 'none', duration: 2000 }); } return; } processing.value = true; try { // 从本地缓存获取OpenID const openId = uni.getStorageSync('wechat_openid') || ''; // 准备提交的数据 const submitData = { title: formData.value.title, complaintType: formData.value.categoryValue, bcomplaintName: formData.value.complaineeName, bcomplaintCompany: formData.value.company, bcomplaintDept: formData.value.department, bcomplaintContent: formData.value.content, processingStatus: "1", fileUrl: files.value.map(file => file.url).join(','), complaintPerson: formData.value.name || '', phoneNumber: formData.value.phone || '', email: formData.value.email || '', wxUserId: openId // 将openID传入wxUserId字段 }; uni.showLoading({ title: '提交中...', mask: true }); // 提交表单数据到接口 await new Promise((resolve, reject) => { uni.request({ url: '/api/incorruptFront/addComplainTs', method: 'POST', data: submitData, header: { 'Content-Type': 'application/json' }, success: (res) => { if (res.statusCode === 200 && res.data.code === 200) { resolve(res.data); } else { reject(new Error(res.data.msg || '提交失败')); } }, fail: (err) => { reject(new Error(`网络错误: ${err.errMsg}`)); } }); }); uni.hideLoading(); uni.showToast({ title: '投诉信息已提交', icon: 'success', duration: 2000 }); // 2秒后返回首页 setTimeout(() => { uni.reLaunch({ url: '/pages/index/index' }); }, 2000); } catch (error) { console.error('提交表单失败', error); uni.hideLoading(); uni.showToast({ title: error.message || '提交失败,请重试', icon: 'error', duration: 2000 }); } finally { processing.value = false; } }; // 页面加载 onMounted(() => { // 启动倒计时 const timer = setInterval(() => { if (countdown.value > 0) { countdown.value--; } else { clearInterval(timer); } }, 1000); // 从缓存获取openID openId.value = uni.getStorageSync('wechat_openid') || ''; }); </script> <style lang="scss"> /* 基础样式保持不变 */ .container { display: flex; flex-direction: column; min-height: 100vh; background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%); padding: 20rpx; } .header { background: linear-gradient(135deg, #3498db 0%, #2c3e50 100%); color: white; text-align: center; padding: 40rpx 20rpx; border-radius: 0 0 30rpx 30rpx; margin-bottom: 20rpx; box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.15); } .header-title { display: block; font-size: 40rpx; font-weight: bold; margin-bottom: 15rpx; letter-spacing: 1rpx; } .header-subtitle { font-size: 28rpx; opacity: 0.9; } .welcome-section { padding: 30rpx; transition: all 0.5s ease; } .welcome-image { width: 100%; height: 300rpx; border-radius: 20rpx; overflow: hidden; box-shadow: 0 8rpx 25rpx rgba(0, 0, 0, 0.1); display: flex; justify-content: center; align-items: center; } .welcome-image image { width: 100%; height: 100%; object-fit: cover; } .welcome-content { background-color: #f8f9fa; border-radius: 20rpx; padding: 30rpx; line-height: 1.8; font-size: 28rpx; color: #333; box-shadow: inset 0 0 10rpx rgba(0, 0, 0, 0.05); margin-bottom: 30rpx; } .welcome-text { display: block; margin-bottom: 20rpx; } .accept-box { display: flex; justify-content: center; align-items: center; padding: 25rpx; background-color: #e3f2fd; border-radius: 20rpx; cursor: pointer; transition: all 0.3s ease; border: 2rpx solid #bbdefb; box-shadow: 0 4rpx 15rpx rgba(0, 0, 0, 0.1); } .accept-box.disabled { background-color: #f5f5f5; cursor: not-allowed; opacity: 0.7; } .accept-box .timer { background-color: #e74c3c; color: white; width: 70rpx; height: 70rpx; border-radius: 50%; display: flex; justify-content: center; align-items: center; margin-left: 20rpx; font-weight: bold; font-size: 30rpx; } .form-section { padding: 30rpx; } .section-title { font-size: 34rpx; color: #2c3e50; margin-bottom: 30rpx; padding-bottom: 15rpx; border-bottom: 2rpx solid #3498db; display: flex; align-items: center; text { margin-left: 15rpx; font-weight: bold; } } .form-group { margin-bottom: 35rpx; position: relative; } .form-row { &.full-width { display: flex; flex-wrap: wrap; margin: 0 -15rpx; margin-bottom: 40rpx; } } .form-col { flex: 1; padding: 0 15rpx; min-width: 300rpx; } label { display: block; margin-bottom: 15rpx; font-weight: 500; color: #2c3e50; font-size: 30rpx; } label.required::after { content: "*"; color: #e74c3c; margin-left: 8rpx; } input, select, textarea, .picker-content { width: 100%; padding: 25rpx; border: 2rpx solid #e0e0e0; border-radius: 15rpx; font-size: 28rpx; transition: all 0.3s ease; background-color: #fff; } input:focus, select:focus, textarea:focus, .picker-content:active { border-color: #3498db; outline: none; box-shadow: 0 0 0 3rpx rgba(52, 152, 219, 0.2); } textarea { min-height: 200rpx; resize: vertical; } /* 新增的内容计数器样式 */ .content-counter { text-align: right; font-size: 24rpx; color: #999; margin-top: 10rpx; } .picker-content { display: flex; align-items: center; min-height: 80rpx; /* 确保选择器高度与输入框一致 */ &.placeholder { color: #999; } } .upload-area { border: 2rpx dashed #3498db; border-radius: 20rpx; padding: 40rpx 30rpx; text-align: center; background-color: #e3f2fd; cursor: pointer; transition: all 0.3s ease; margin-bottom: 30rpx; } .upload-area.disabled { background-color: #f5f5f5; border-color: #bdc3c7; cursor: not-allowed; } .upload-area:active:not(.disabled) { background-color: #bbdefb; transform: scale(0.98); } .upload-icon { margin-bottom: 20rpx; } .upload-text { color: #2c3e50; margin-bottom: 20rpx; font-size: 30rpx; font-weight: 500; } .file-types { color: #7f8c8d; font-size: 26rpx; margin-bottom: 20rpx; } .upload-progress { width: 80%; margin: 20rpx auto 0; } .progress-text { display: block; margin-top: 10rpx; font-size: 26rpx; color: #3498db; } .file-list { margin-top: 20rpx; margin-bottom: 30rpx; } .file-item { display: flex; align-items: center; padding: 20rpx; background-color: #f8f9fa; border-radius: 15rpx; margin-bottom: 15rpx; border-left: 4rpx solid #3498db; } /* 优化文件信息显示 */ .file-info { flex: 1; margin: 0 15rpx; min-width: 0; /* 允许容器收缩 */ } .file-name { display: block; font-size: 28rpx; color: #2c3e50; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; /* 不换行,超出部分显示省略号 */ } .file-size { display: block; color: #7f8c8d; font-size: 24rpx; margin-top: 5rpx; } .delete-btn { width: 40rpx; height: 40rpx; border-radius: 50%; background-color: #e74c3c; display: flex; justify-content: center; align-items: center; margin-left: 10rpx; flex-shrink: 0; /* 确保按钮不被挤压 */ transition: all 0.2s ease; color: #ffffff; } .delete-btn:active { background-color: #c0392b; transform: scale(0.9); } .error-text { display: block; color: #e74c3c; font-size: 26rpx; margin-top: 10rpx; } input.error, textarea.error, .picker-content.error { border-color: #e74c3c; animation: shake 0.5s ease; } /* 错误字段高亮动画 */ .error-highlight { animation: highlight 1s ease; } @keyframes shake { 0%, 100% { transform: translateX(0); } 20%, 60% { transform: translateX(-5rpx); } 40%, 80% { transform: translateX(5rpx); } } @keyframes highlight { 0%, 100% { box-shadow: none; } 50% { box-shadow: 0 0 0 4rpx rgba(231, 76, 60, 0.3); } } .privacy-note { background-color: #f8f9fa; border-radius: 15rpx; padding: 25rpx; margin-top: 30rpx; font-size: 26rpx; color: #7f8c8d; text-align: center; border: 1rpx solid #e9ecef; line-height: 1.6; } .submit-btn { background: linear-gradient(to right, #3498db, #2980b9); color: white; border: none; padding: 25rpx 0; width: 100%; border-radius: 50rpx; font-size: 32rpx; font-weight: 600; margin-top: 30rpx; box-shadow: 0 5rpx 20rpx rgba(52, 152, 219, 0.4); transition: all 0.3s ease; } .submit-btn:disabled { background: #bdc3c7; box-shadow: none; opacity: 0.7; } .submit-btn:active:not(:disabled) { transform: translateY(-3rpx); box-shadow: 0 8rpx 25rpx rgba(52, 152, 219, 0.6); } /* 响应式设计 */ @media (min-width: 768px) { .container { max-width: 750px; margin: 0 auto; padding: 40rpx; } .welcome-image { height: 350rpx; } } .welcome-text.indent{ text-indent: 2em; } </style> 上传附件报错:Request URL: http://www.wyqg.top/api/file/upload Request Method: POST Status Code: 400 Bad Request Remote Address: 47.109.65.73:80 Referrer Policy: strict-origin-when-cross-origin Connection: keep-alive Content-Length: 0 Date: Mon, 01 Sep 2025 10:00:25 GMT Server: nginx/1.26.3 Accept: */* Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Connection: keep-alive Content-Length: 10485948 Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryGFsvh0KKYAXIXI27 Host: www.wyqg.top Origin: http://www.wyqg.top Referer: http://www.wyqg.top/pages/OnlinComplaint/OnlinComplaint?openid=ohhFX6TzyGvbXFuIL0ZPjEIpsJOo User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1 wechatdevtools/1.06.2504030 MicroMessenger/8.0.5 Language/zh_CN webview/17566896873445375 webdebugger port/57256 token/84cac3393fe84eaa19bae7107a7c7659 file: (binary) 但是我用postman测试接口,用post方法 Body选择 form-data,键file是文件,值上传了一个MP4文件测试没问题
09-02
<template> <view class="order-container"> <!-- 顶部标题栏 --> <view class="header"> <view class="back-icon-container" @tap="goBack"> <uni-icons type="back" size="24" color="#fff" class="back-icon" /> </view> <view class="title-container"> <text class="title">货物信息查询</text> </view> </view> <!-- 表单内容区 --> <scroll-view scroll-y class="form-content"> <!-- 基本信息卡片 --> <view class="form-card"> <view class="card-title">基本信息</view> <!-- 序号 --> <view class="form-item"> <text class="label">序号</text> <input class="input" v-model="queryParams.serialNumber" placeholder="请输入序号" placeholder-style="color: #c0c4cc;" /> </view> <!-- 物品名称 --> <view class="form-item"> <text class="label">物品名称</text> <input class="input" v-model="queryParams.itemName" placeholder="请输入物品名称" placeholder-style="color: #c0c4cc;" /> </view> <!-- 货物类型 --> <view class="form-item"> <text class="label">货物类型</text> <input type="text" class="input" placeholder="请输入货物类型" v-model="currentGoods.goodsType" placeholder-style="color: #c0c4cc;" > </view> <!-- 客户代码 --> <view class="form-item"> <text class="label">客户代码</text> <input class="input" v-model="queryParams.customerCode" placeholder="请输入客户代码" placeholder-style="color: #c0c4cc;" /> </view> </view> <!-- 规格参数卡片 --> <view class="form-card"> <view class="card-title">规格参数</view> <!-- 单号 --> <view class="form-item"> <text class="label">单号</text> <view class="input-group"> <input class="input" v-model="queryParams.orderNumber" placeholder="请输入或扫描单号" placeholder-style="color: #c0c4cc;" /> <view class="scan-btn-container"> <button class="scan-btn" @click="scanBarcode"> <uni-icons type="scan" size="18" color="#fff"></uni-icons> </button> </view> </view> </view> <!-- 数量范围 --> <view class="form-row"> <view class="form-item half"> <text class="label">数量(最小)</text> <input class="input" type="number" v-model="queryParams.minQuantity" placeholder="最小数量" placeholder-style="color: #c0c4cc;" /> </view> <view class="form-item half"> <text class="label">数量(最大)</text> <input class="input" type="number" v-model="queryParams.maxQuantity" placeholder="最大数量" placeholder-style="color: #c0c4cc;" /> </view> </view> <!-- 重量范围 --> <view class="form-row"> <view class="form-item half"> <text class="label">重量(最小/kg)</text> <input class="input" type="number" v-model="queryParams.minWeight" placeholder="最小重量" placeholder-style="color: #c0c4cc;" /> </view> <view class="form-item half"> <text class="label">重量(最大/kg)</text> <input class="input" type="number" v-model="queryParams.maxWeight" placeholder="最大重量" placeholder-style="color: #c0c4cc;" /> </view> </view> <!-- 尺寸 --> <view class="form-item"> <text class="label">尺寸(长×宽×高 cm)</text> <input class="input" v-model="queryParams.dimensions" placeholder="例如: 30×20×15" placeholder-style="color: #c0c4cc;" /> </view> </view> <!-- 时间信息卡片 --> <view class="form-card"> <view class="card-title">时间信息</view> <!-- 入库日期区间 --> <view class="form-item"> <text class="label">入库日期区间</text> <view class="form-row"> <view class="form-item half"> <picker mode="date" @change="(e) => setEntryStartDate(e.detail.value)"> <view class="picker">{{ queryParams.entryStartDate || '开始日期' }}</view> </picker> </view> <view class="form-item half"> <picker mode="date" @change="(e) => setEntryEndDate(e.detail.value)"> <view class="picker">{{ queryParams.entryEndDate || '结束日期' }}</view> </picker> </view> </view> <!-- 错误提示 --> <text v-if="errorEntryDate" class="error-tip">{{ errorEntryDate }}</text> </view> <!-- 出库日期区间 --> <view class="form-item"> <text class="label">出库日期区间</text> <view class="form-row"> <view class="form-item half"> <picker mode="date" @change="(e) => setExitStartDate(e.detail.value)"> <view class="picker">{{ queryParams.exitStartDate || '开始日期' }}</view> </picker> </view> <view class="form-item half"> <picker mode="date" @change="(e) => setExitEndDate(e.detail.value)"> <view class="picker">{{ queryParams.exitEndDate || '结束日期' }}</view> </picker> </view> </view> <!-- 错误提示 --> <text v-if="errorExitDate" class="error-tip">{{ errorExitDate }}</text> </view> <!-- 渠道 --> <view class="form-item"> <text class="label">渠道</text> <input class="input" type="number" v-model="queryParams.channel" placeholder="请输入渠道" placeholder-style="color: #c0c4cc;" /> </view> <!-- 备注 --> <view class="form-item"> <text class="label">备注</text> <textarea class="textarea" v-model="queryParams.remark" placeholder="请输入备注信息" placeholder-style="color: #c0c4cc;" auto-height /> </view> </view> <!-- 操作按钮 --> <view class="action-buttons"> <button class="reset-btn" @click="resetForm">重置</button> <button class="submit-btn" @click="submitQuery">查询信息</button> </view> </scroll-view> </view> </template> <script> export default { data() { return { queryParams: { serialNumber: '', itemName: '', goodsType: '', customerCode: '', orderNumber: '', minQuantity: '', maxQuantity: '', minWeight: '', maxWeight: '', dimensions: '', entryStartDate: '', // 入库开始日期 entryEndDate: '', // 入库结束日期 exitStartDate: '', // 出库开始日期 exitEndDate: '', // 出库结束日期 channel: '', // 新增错误提示字段 errorEntryDate: '', errorExitDate: '', remark: '' }, } }, methods: { goBack() { uni.navigateBack(); }, // 扫码功能 scanBarcode() { // #ifdef APP-PLUS || MP-WEIXIN uni.scanCode({ onlyFromCamera: true, scanType: ['barCode', 'qrCode'], success: (res) => { if (res.result) { this.queryParams.orderNumber = res.result; uni.showToast({ title: '扫码成功', icon: 'success' }); } }, fail: (err) => { console.error('扫码失败:', err); uni.showToast({ title: '扫码失败', icon: 'none' }); } }); // #endif // #ifdef H5 uni.showToast({ title: '当前环境不支持扫码', icon: 'none' }); // #endif }, resetForm() { this.queryParams = { serialNumber: '', itemName: '', goodsType: '', customerCode: '', orderNumber: '', minQuantity: '', maxQuantity: '', minWeight: '', maxWeight: '', dimensions: '', entryStartDate: '', entryEndDate: '', exitStartDate: '', exitEndDate: '', channel: '', remark: '' }; uni.showToast({ title: '查询条件已重置', icon: 'success' }); }, // 设置入库开始日期(带验证) setEntryStartDate(date) { this.queryParams.entryStartDate = date; this.validateEntryDates(); }, // 设置入库结束日期(带验证) setEntryEndDate(date) { this.queryParams.entryEndDate = date; this.validateEntryDates(); }, // 设置出库开始日期(带验证) setExitStartDate(date) { this.queryParams.exitStartDate = date; this.validateExitDates(); }, // 设置出库结束日期(带验证) setExitEndDate(date) { this.queryParams.exitEndDate = date; this.validateExitDates(); }, // 验证入库日期区间 validateEntryDates() { const { entryStartDate, entryEndDate } = this.queryParams; if (entryStartDate && entryEndDate) { if (new Date(entryEndDate) < new Date(entryStartDate)) { this.errorEntryDate = '错误:结束日期不能早于开始日期'; } else { this.errorEntryDate = ''; } } else { this.errorEntryDate = ''; } }, // 验证出库日期区间 validateExitDates() { const { exitStartDate, exitEndDate } = this.queryParams; if (exitStartDate && exitEndDate) { if (new Date(exitEndDate) < new Date(exitStartDate)) { this.errorExitDate = '错误:结束日期不能早于开始日期'; } else { this.errorExitDate = ''; } } else { this.errorExitDate = ''; } }, submitQuery() { // 构建查询参数 const params = {...this.queryParams}; // 移除空值参数 Object.keys(params).forEach(key => { if (params[key] === '') { delete params[key]; } }); // 先验证日期 this.validateEntryDates(); this.validateExitDates(); if (this.errorEntryDate || this.errorExitDate) { uni.showToast({ title: '请修正日期错误', icon: 'none' }); return; } uni.showLoading({ title: '查询中...' }); // 模拟查询请求 setTimeout(() => { uni.hideLoading(); // 这里实际应调用API接口 console.log('查询参数:', params); // 跳转到查询结果页 uni.navigateTo({ url: '/pages/tabbar/storageManagement/OrderList' }); }, 1500); } } } </script> <style> /* 全局适配样式 */ * { box-sizing: border-box; margin: 0; padding: 0; } .order-container { display: flex; flex-direction: column; height: 100vh; background-color: #f5f7fa; } /* 顶部标题栏 - 适配APP和小程序 */ .header { background: linear-gradient(135deg, #5e8fff, #6b7de9); display: flex; align-items: center; color: #fff; box-shadow: 0 2px 10px rgba(94, 143, 255, 0.3); position: relative; padding: 15px 20px; } /* 小程序顶部适配 */ /* #ifdef MP-WEIXIN */ .header { padding-top: 10px; padding-bottom: 10px; } /* #endif */ /* APP顶部适配 - 避开状态栏 */ /* #ifdef APP-PLUS */ .header { padding-top: var(--status-bar-height); padding-bottom: 15px; } /* #endif */ .back-icon-container { padding: 5px; margin-right: 10px; z-index: 10; } .title-container { position: absolute; left: 50%; transform: translateX(-50%); width: 100%; text-align: center; } .title { font-size: 18px; font-weight: bold; } /* 表单内容区 - 增加左右内边距 */ .form-content { flex: 1; padding: 15px; overflow-y: auto; /* 适配小程序滚动区域 */ box-sizing: border-box; } /* 表单卡片 - 增加左右外边距 */ .form-card { background: #fff; border-radius: 12px; box-shadow: 0 3px 10px rgba(0, 0, 0, 0.08); margin-bottom: 20px; padding: 20px; box-sizing: border-box; /* 适配不同平台 */ /* #ifdef MP-WEIXIN */ margin: 0 10px 20px; /* #endif */ /* #ifdef APP-PLUS */ margin: 0 15px 20px; /* #endif */ } .card-title { display: flex; align-items: center; margin-bottom: 20px; font-weight: bold; color: #333; font-size: 16px; padding-bottom: 10px; border-bottom: 1px solid #eee; } /* 表单项 */ .form-item { margin-bottom: 20px; } .label { display: block; margin-bottom: 8px; font-size: 14px; color: #666; } .input, .textarea, .picker { width: 100%; height: 44px; padding: 0 15px; border: 1px solid #dcdfe6; border-radius: 8px; font-size: 15px; background: #fff; line-height: 44px; box-sizing: border-box; } .textarea { height: auto; min-height: 100px; padding: 10px 15px; line-height: 1.5; } .input:focus, .textarea:focus { border-color: #5e8fff; outline: none; } /* 表单行布局 */ .form-row { display: flex; margin: 0 -8px; box-sizing: border-box; } .form-item.half { flex: 1; padding: 0 8px; box-sizing: border-box; } /* 输入组 */ .input-group { display: flex; box-sizing: border-box; } .input-group .input { flex: 1; border-top-right-radius: 0; border-bottom-right-radius: 0; } .scan-btn-container { display: flex; align-items: center; } .scan-btn { background: #5e8fff; color: white; border: none; padding: 0; width: 44px; height: 44px; border-radius: 0 8px 8px 0; display: flex; align-items: center; justify-content: center; } /* 操作按钮 */ .action-buttons { display: flex; margin-top: 20px; padding: 0 10px; box-sizing: border-box; /* 适配小程序按钮 */ /* #ifdef MP-WEIXIN */ padding-bottom: 20px; /* #endif */ } .reset-btn, .submit-btn { flex: 1; height: 48px; border-radius: 10px; font-size: 16px; font-weight: 500; display: flex; align-items: center; justify-content: center; margin: 0 10px; border: none; /* 适配小程序按钮 */ /* #ifdef MP-WEIXIN */ line-height: 48px; /* #endif */ } .reset-btn { background: #f0f2f5; color: #606266; } .submit-btn { background: linear-gradient(135deg, #5e8fff, #6b7de9); color: white; box-shadow: 0 4px 12px rgba(94, 143, 255, 0.4); } /* 平台适配 */ /* 小程序适配 - 优化输入框和按钮 */ /* #ifdef MP-WEIXIN */ .header { padding-top: 10px; padding-bottom: 10px; } .form-content { padding: 10px 15px; } .form-card { padding: 15px 20px; margin: 0 5px 15px; } .input, .textarea, .picker { height: 42px; font-size: 14px; line-height: 42px; } .scan-btn { width: 42px; height: 42px; } .action-buttons { margin-bottom: 10px; } /* #endif */ /* APP适配 - 优化顶部安全区域 */ /* #ifdef APP-PLUS */ .header { padding-top: var(--status-bar-height); padding-bottom: 15px; } .form-content { padding-top: 20px; } .form-card { margin: 0 20px 25px; } .input, .textarea, .picker { font-size: 16px; } /* #endif */ /* 平板适配 */ @media (min-width: 768px) { .form-content { padding: 20px; } .form-card { max-width: 600px; margin: 0 auto 20px; } .action-buttons { max-width: 600px; margin: 20px auto; } } /* 小屏幕设备适配 */ @media (max-width: 360px) { .form-content { padding: 10px; } .form-card { padding: 15px; } .input, .textarea, .picker { padding: 0 10px; font-size: 14px; } .scan-btn { width: 38px; height: 38px; } .form-row { flex-direction: column; margin: 0; } .form-item.half { padding: 0; margin-bottom: 15px; } /* 小屏幕上的按钮 */ .reset-btn, .submit-btn { height: 45px; font-size: 15px; } } /* 新增错误提示样式 */ .error-tip { display: block; color: #e64340; font-size: 12px; margin-top: 5px; padding-left: 10px; animation: shake 0.5s; } /* 错误提示动画 */ @keyframes shake { 0%, 100% {transform: translateX(0);} 20%, 60% {transform: translateX(-3px);} 40%, 80% {transform: translateX(3px);} } /* 当有错误时高亮日期选择器 */ .picker[data-error="true"] { border-color: #e64340 !important; color: #e64340; } </style> 选择完日期时就开始判定,调整上诉代码
11-20
【电能质量扰动】基于ML和DWT的电能质量扰动分类方法研究(Matlab实现)内容概要:本文研究了一种基于机器学习(ML)和离散小波变换(DWT)的电能质量扰动分类方法,并提供了Matlab实现方案。首先利用DWT对电能质量信号进行多尺度分解,提取信号的时频域特征,有效捕捉电压暂降、暂升、中断、谐波、闪变等常见扰动的关键信息;随后结合机器学习分类器(如SVM、BP神经网络等)对提取的特征进行训练与分类,实现对不同类型扰动的自动识别与准确区分。该方法充分发挥DWT在信号去噪与特征提取方面的优势,结合ML强大的模式识别能力,提升了分类精度与鲁棒性,具有较强的实用价值。; 适合人群:电气工程、自动化、电力系统及其自动化等相关专业的研究生、科研人员及从事电能质量监测与分析的工程技术人员;具备一定的信号处理基础和Matlab编程能力者更佳。; 使用场景及目标:①应用于智能电网中的电能质量在线监测系统,实现扰动类型的自动识别;②作为高校或科研机构在信号处理、模式识别、电力系统分析等课程的教学案例或科研实验平台;③目标是提高电能质量扰动分类的准确性与效率,为后续的电能治理与设备保护提供决策依据。; 阅读建议:建议读者结合Matlab代码深入理解DWT的实现过程与特征提取步骤,重点关注小波基选择、分解层数设定及特征向量构造对分类性能的影响,并尝试对比不同机器学习模型的分类效果,以全面掌握该方法的核心技术要点。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值