1***@qq.com
1***@qq.com
  • 发布:2026-04-28 11:17
  • 更新:2026-04-28 11:28
  • 阅读:139

开发微信小程序长表单输入框聚焦会导致页面滚动到其他地方

分类:uni-app

产品分类: uniapp/小程序/微信

PC开发环境操作系统: Mac

PC开发环境操作系统版本号: 15.7.2

HBuilderX类型: 正式

HBuilderX版本号: 5.06

第三方开发者工具版本号: 2.01.2510290

基础库版本号: 3.15.1

项目创建方式: HBuilderX

示例代码:
 <template>  
  <view class="page-container" :style="isInputFocused ? { position: 'fixed', top: `-${scrollTop}px`, left: 0, right: 0, width: '100%' } : {}">  

    <!-- 重要提示弹窗 -->  
    <view class="modal-overlay" v-if="showNoticeModal">  
      <view class="modal-content">  
        <view class="modal-header">  
          <text class="modal-title">重要提示</text>  
          <image class="modal-warning-icon" src="/static/icons/waring.png" mode="aspectFit"></image>  
        </view>  
        <text class="modal-subtitle">本平台仅受理以下6个类别违纪违法问题:</text>  

        <view class="modal-list">  
          <view class="modal-list-item" v-for="(item, index) in noticeItems" :key="index">  
            <text class="modal-list-num">{{index + 1}}.</text>  
            <text class="modal-list-text">{{item}}</text>  
          </view>  
        </view>  

        <button class="modal-btn" :class="{'modal-btn-disabled': countdown > 0}" @click="closeNoticeModal">  
          {{ countdown > 0 ? `我已知晓(${countdown}s)` : '我已知晓' }}  
        </button>  
      </view>  
    </view>  

    <view class="scroll-content">  
      <!-- 举报人信息 -->  
      <view class="card" id="section1">  
        <text class="card-title">举报人信息</text>  

        <view class="form-item form-row">  
          <view class="form-label-group">  
            <text class="form-label">匿名提交</text>  
            <text class="form-desc">开启后无需填写个人信息</text>  
          </view>  
          <switch :checked="isAnonymous === 0" @change="handleAnonymousChange" color="#4A90E2" style="transform: scale(0.8);" />  
        </view>  

        <view v-if="isAnonymous !== 0">  
          <view class="form-item">  
            <text class="form-label">举报人姓名</text>  
            <input  @focus="handleFocus" @blur="handleBlur" class="input-field" placeholder="请输入您的姓名" placeholder-class="placeholder" v-model="formData.reportName" maxlength="20" :adjust-position="false" />  
        </view>  
        <view class="form-item">  
          <text class="form-label">举报人联系方式</text>  
          <input @focus="handleFocus" @blur="handleBlur" class="input-field" placeholder="请输入手机号或座机号" placeholder-class="placeholder" v-model="formData.reportPhone" maxlength="20" :adjust-position="false" />  
        </view>  
      </view>  
    </view>  

    <!-- 被举报人信息 -->  
    <view class="card" id="section2">  
      <text class="card-title">被举报人信息</text>  
      <view class="form-item">  
        <view class="label-row">  
          <text class="form-label">被举报人姓名</text>  
          <text class="required">*</text>  
        </view>  
        <input @focus="handleFocus" @blur="handleBlur" class="input-field" placeholder="请输入被举报人姓名" placeholder-class="placeholder" v-model="formData.reportedName" maxlength="50" :adjust-position="false" />  
      </view>  

        <view class="form-item">  
          <view class="label-row"><text class="form-label">被举报人所在地区</text><text class="required">*</text></view>  
          <view class="picker-view-wrap">  
            <region-picker   
              v-model="formData.reportedArea"  
              :region-data="regionList"  
              placeholder="请选择被举报人所在地区"  
            />  
          </view>  
        </view>  

        <view class="form-item">  
          <text class="form-label">被举报人职务</text>  
          <input @focus="handleFocus" @blur="handleBlur" class="input-field" placeholder="请输入职务" placeholder-class="placeholder" v-model="formData.reportedPost" maxlength="50" :adjust-position="false" />  
      </view>  

      <view class="form-item">  
        <text class="form-label">被举报人联系方式</text>  
        <input @focus="handleFocus" @blur="handleBlur" class="input-field" placeholder="请输入联系方式" placeholder-class="placeholder" v-model="formData.reportedPhone" maxlength="20" :adjust-position="false" />  
      </view>  
      </view>  

      <!-- 问题详情 -->  
      <view class="card" id="section3">  
        <text class="card-title">问题详情</text>  

        <view class="form-item">  
          <view class="label-row"><text class="form-label">问题类型</text><text class="required">*</text></view>  
          <view class="type-list">  
            <view class="type-item"   
                  :class="{'type-item-active': currentProblemType === index}"   
                  v-for="(item, index) in problemTypes"   
                  :key="index"  
                  @click="currentProblemType = index">  
              <text class="type-title" :class="{'type-title-active': currentProblemType === index}">{{item.dictLabel}}</text>  
              <text class="type-desc" :class="{'type-desc-active': currentProblemType === index}">{{item.remark}}</text>  
            </view>  
          </view>  
        </view>  

        <view class="form-item">  
          <view class="label-row"><text class="form-label">问题发生时间</text><text class="required">*</text></view>  
          <datetime-picker v-model="formData.happenTime" placeholder="请选择问题发生时间" />  
        </view>  

        <view class="form-item">  
          <view class="label-row"><text class="form-label">问题发生地点</text><text class="required">*</text></view>  
          <input @focus="handleFocus" @blur="handleBlur" class="input-field" placeholder="请输入问题发生地点" placeholder-class="placeholder" v-model="formData.happenPlace" maxlength="100" :adjust-position="false" />  
      </view>  

      <view class="form-item">  
        <view class="label-row"><text class="form-label">涉及人员/业务</text><text class="required">*</text></view>  
        <input @focus="handleFocus" @blur="handleBlur" class="input-field" placeholder="请描述涉及的具体人员或业务事项" placeholder-class="placeholder" v-model="formData.relatedBusiness" maxlength="200" :adjust-position="false" />  
      </view>  

      <view class="form-item">  
        <view class="label-row"><text class="form-label">关键事实描述</text><text class="required">*</text></view>  
        <textarea :fixed="true" @focus="handleFocus" @blur="handleBlur" class="textarea-field" placeholder="请详细描述问题经过,包括时间、地点、涉及人员/业务、关键事实" placeholder-class="placeholder" v-model="formData.factDesc" maxlength="2000" :adjust-position="false"></textarea>  
      </view>  

        <view class="form-item">  
          <text class="form-label">佐证材料</text>  

          <view class="upload-area" @click="chooseFiles" v-if="fileList.length < 9">  
            <image class="upload-icon" src="/static/icons/upload.png" mode="widthFix"></image>  
            <text class="upload-text">点击上传文件</text>  
            <text class="upload-desc">支持图片、视频、录音格式,单个文件不超过50MB,最多9个文件</text>  
          </view>  

          <view class="file-list" v-if="fileList.length > 0">  
            <!-- 已经选择的文件列表 -->  
            <view class="file-item" v-for="(file, index) in fileList" :key="index">  
              <text class="file-icon">{{ file.icon }}</text>  
              <view class="file-info">  
                <text class="file-name">{{ file.name }}</text>  
                <text class="file-size">{{ file.sizeText }}</text>  
              </view>  
              <view class="file-delete" @click="removeFile(index)">  
                <text class="delete-icon">×</text>  
              </view>  
            </view>  
          </view>  

        </view>  
      </view>  

      <!-- 底部提示 -->  
      <view class="tips-area">  
        <view class="tip-item"><text class="tip-dot">•</text><text class="tip-text">实名举报将在5个工作日内反馈处理</text></view>  
        <view class="tip-item"><text class="tip-dot">•</text><text class="tip-text">匿名举报直接办结,不及微处理结果</text></view>  
        <view class="tip-item"><text class="tip-dot">•</text><text class="tip-text">您的个人信息将严格保密,严禁泄露</text></view>  
      </view>  

      <!-- 底部安全距离 -->  
      <view class="safe-bottom-area"></view>  
    </view>  
    <!-- 底部固定按钮 -->  
    <view class="bottom-bar">  
      <button class="submit-btn" :loading="isSubmitting" :disabled="isSubmitting" @click="confirmSubmit">提交举报</button>  
    </view>  
  </view>  
</template>  

<script setup>  
import { ref, reactive, onMounted, onUnmounted } from 'vue'  
import { onPageScroll } from '@dcloudio/uni-app'  
import { config } from '@/utils/config.uts'  
import { submitReportInfo, getDictDataByType, getRegionList, type DictData } from '@/utils/api.uts'  
import {   
  validateFile,   
  validateFileCount,  
  getFileIcon,  
  formatFileSize,  
  MAX_FILE_COUNT  
} from '@/utils/file-validator.uts'  
import {   
  handleNetworkError,   
  handleBusinessError,   
  handleAuthError,  
  handleValidationError  
} from '@/utils/error-handler.uts'  

import datetimePicker from '@/components/datetime-picker/datetime-picker.uvue'  
import regionPicker from '@/components/region-picker/region-picker.uvue'  

const isAnonymous = ref(1) // 0 是开启匿名,1 是关闭匿名  
const currentProblemType = ref(0)  
const isSubmitting = ref(false)  

const isInputFocused = ref(false)  
const scrollTop = ref(0)  
let focusTimer: number | null = null  

onPageScroll((e) => {  
  if (!isInputFocused.value) {  
    scrollTop.value = e.scrollTop  
  }  
})  

const handleFocus = () => {  
  if (focusTimer !== null) {  
    clearTimeout(focusTimer)  
  }  
  isInputFocused.value = true  
}  

const handleBlur = (e: any) => {  
  isInputFocused.value = false  
  // 恢复滚动位置  
  uni.pageScrollTo({  
    scrollTop: scrollTop.value,  
    duration: 0  
  })  
}  

type FileItem = {  
  id?: string  
  name: string  
  path: string  
  size: number  
  type?: string  
  icon: string  
  sizeText: string  
}  
const fileList = ref<FileItem[]>([])  

const showNoticeModal = ref(true)  
const countdown = ref(10)  
let timer: number | null = null  

const noticeItems = ref([  
  '违规收费,搭车收费。',  
  '刁难客户,吃拿卡要。',  
  '违规收受礼品礼金或可能影响公正执行公务宴请、健身、娱乐活动',  
  '推诿搪塞、态度粗暴',  
  '为亲友或他人谋取利益',  
  '失职渎职,玩忽职守'  
])  

const closeNoticeModal = () => {  
  if (countdown.value <= 0) {  
    showNoticeModal.value = false  
  }  
}  

const regionList = ref([])  

const getRegionData = async () => {  
  try {  
    const res = await getRegionList()  
    if (res.code === 200 && res.data) {  
      regionList.value = res.data  
    }  
  } catch (e) {  
    console.error('获取地区列表失败', e)  
  }  
}  

const formData = reactive({  
  reportName: '',  
  reportPhone: '',  
  reportedName: '',  
  reportedArea: '',  
  reportedPost: '',  
  reportedPhone: '',  
  happenTime: '',  
  happenPlace: '',  
  relatedBusiness: '',  
  factDesc: '',  
  attachmentIds: []  
})  

const problemTypes = ref<DictData[]>([])  

const getProblemTypes = async () => {  
  try {  
    const res = await getDictDataByType('problem_type')  
    if (res.code === 200 && res.data) {  
      problemTypes.value = res.data  
    }  
  } catch (e) {  
    console.error('获取问题类型失败', e)  
  }  
}  

const handleAnonymousChange = (e: any) => {  
  isAnonymous.value = e.detail.value ? 0 : 1  
}  

const handleFiles = (tempFiles: Array<any>) => {  
  for (let i = 0; i < tempFiles.length; i++) {  
    const rawFile = tempFiles[i]  
    console.log(rawFile)  
    // 兼容 chooseMedia 和 chooseFile 的返回结构  
    const filePath = rawFile.path || rawFile.tempFilePath  
    console.log(filePath)  
    const type = filePath.split('.').pop()  

    const fileName = rawFile.name || `file_${Date.now()}_${i}.${type}`  
    const fileSize = rawFile.size  
    console.log(type)  
    // 验证文件  
    const validationResult = validateFile({  
      name: fileName,  
      size: fileSize,  
      type,  
      path: filePath  
    })  

    if (!validationResult.valid) {  
      uni.showToast({   
        title: validationResult.message,   
        icon: 'none',  
        duration: 3000  
      })  
      continue  
    }  

    // 显示上传中提示  
    uni.showLoading({ title: '上传中...', mask: true })  

    // 上传文件  
    uploadFile({  
      path: filePath,  
      name: fileName,  
      size: fileSize,  
      type: type  
    })  
  }  
}  

const chooseFiles = () => {  
  const currentCount = fileList.value.length  
  const remainCount = MAX_FILE_COUNT - currentCount  

  // 验证文件数量  
  const countResult = validateFileCount(currentCount, 1)  
  if (!countResult.valid) {  
    uni.showToast({ title: countResult.message, icon: 'none', duration: 3000 })  
    return  
  }  

  uni.showActionSheet({  
    itemList: ['图片或视频', '其他文件'],  
    success: (res) => {  
      if (res.tapIndex === 0) {  
        // 选择图片或视频  
        uni.chooseMedia({  
          count: remainCount,  
          mediaType: ['image', 'video'],  
          sourceType: ['album', 'camera'],  
          success: (mediaRes) => {  
            console.log(mediaRes,111)  
            handleFiles(mediaRes.tempFiles as Array<any>)  
          },  
          fail: (err) => {  
            console.error('选择媒体失败', err)  
            const errMsg = err['errMsg'] as string | null  
            if (errMsg != null && !errMsg.includes('cancel')) {  
              uni.showToast({ title: '选择取消或失败', icon: 'none' })  
            }  
          }  
        })  
      } else if (res.tapIndex === 1) {  
        // 选择其他文件  
        uni.chooseMessageFile({  
          count: remainCount,  
          type: 'file',  
          success: (fileRes) => {  
            handleFiles(fileRes.tempFiles as Array<any>)  
          },  
          fail: (err) => {  
            console.error('选择文件失败', err)  
            const errMsg = err['errMsg'] as string | null  
            if (errMsg != null && !errMsg.includes('cancel')) {  
              uni.showToast({ title: '选择取消或失败', icon: 'none' })  
            }  
          }  
        })  
      }  
    }  
  })  
}  

// 上传文件  
const uploadFile = (file: any) => {  
  uni.uploadFile({  
    url: `${config.baseUrl}/msjd-report-info/upload`,  
    filePath: file.path,  
    name: 'file',  
    header: {  
      'Authorization': `Bearer ${uni.getStorageSync('token') || ''}`  
    },  
    success: (uploadFileRes) => {  
      try {  
        const resData = JSON.parse(uploadFileRes.data as string)  

        if (resData.code === 200) {  
          // 添加到文件列表  
          formData.attachmentIds.push(resData.data.id)  
          fileList.value.push({  
            id: resData.data.id,  
            name: file.name || `image_${Date.now()}.jpg`,  
            path: resData.data.url || file.path,  
            size: file.size,  
            type: file.type,  
            icon: getFileIcon(file.name || 'image.jpg'),  
            sizeText: formatFileSize(file.size)  
          })  

          uni.showToast({ title: '上传成功', icon: 'success' })  
        } else {  
          uni.showToast({   
            title: resData.msg || '上传失败',   
            icon: 'none',  
            duration: 3000  
          })  
        }  
      } catch (e) {  
        console.error('解析响应失败', e)  
        uni.showToast({   
          title: '解析响应失败',   
          icon: 'none',  
          duration: 3000  
        })  
      }  
    },  
    fail: (err) => {  
      console.error('上传异常', err)  
      uni.showToast({   
        title: '网络异常,上传失败',   
        icon: 'none',  
        duration: 3000  
      })  
    },  
    complete: () => {  
      uni.hideLoading()  
    }  
  })  
}  

const removeFile = (index: number) => {  
  uni.showModal({  
    title: '确认删除',  
    content: '确定要删除这个文件吗?',  
    success: (res) => {  
      if (res.confirm) {  
        formData.attachmentIds.splice(index, 1)  
        fileList.value.splice(index, 1)  
        uni.showToast({ title: '已删除', icon: 'success' })  
      }  
    }  
  })  
}  

onMounted(() => {  
  // 启动倒计时  
  timer = setInterval(() => {  
    if (countdown.value > 0) {  
      countdown.value--  
    } else {  
      if (timer !== null) {  
        clearInterval(timer)  
        timer = null  
      }  
    }  
  }, 1000)  

  // 获取问题类型字典数据  
  getProblemTypes()  
  getRegionData()  
})  

onUnmounted(() => {  
  if (timer !== null) {  
    clearInterval(timer)  
    timer = null  
  }  
})  

const confirmSubmit = () => {  
  // 手机号格式验证  
  const phoneReg = /^(?:(?:\+|00)86)?1[3-9]\d{9}$|^0\d{2,3}-?\d{7,8}$/  

  if (isAnonymous.value !== 0) {  
    if (!formData.reportName) {  
      uni.showToast({ title: '请输入举报人姓名', icon: 'none' })  
      return  
    }  
    if (!formData.reportPhone) {  
      uni.showToast({ title: '请输入举报人联系方式', icon: 'none' })  
      return  
    }  
    if (!phoneReg.test(formData.reportPhone)) {  
      uni.showToast({ title: '举报人联系方式格式不正确', icon: 'none' })  
      return  
    }  
  }  

  // 先进行必填项校验  
  if (!formData.reportedName) {  
    uni.showToast({ title: '请输入被举报人姓名', icon: 'none' })  
    return  
  }  
  if (!formData.reportedArea) {  
    uni.showToast({ title: '请选择被举报人所在地区', icon: 'none' })  
    return  
  }  
  if (formData.reportedPhone && !phoneReg.test(formData.reportedPhone)) {  
    uni.showToast({ title: '被举报人联系方式格式不正确', icon: 'none' })  
    return  
  }  
  if (!formData.happenTime) {  
    uni.showToast({ title: '请选择问题发生时间', icon: 'none' })  
    return  
  }  
  if (!formData.happenPlace) {  
    uni.showToast({ title: '请输入问题发生地点', icon: 'none' })  
    return  
  }  
  if (!formData.relatedBusiness) {  
    uni.showToast({ title: '请描述涉及的具体人员或业务事项', icon: 'none' })  
    return  
  }  
  if (!formData.factDesc) {  
    uni.showToast({ title: '请填写关键事实描述', icon: 'none' })  
    return  
  }  

  uni.showModal({  
    title: '提交确认',  
    content: '请确认举报信息是否真实有效,一经提交将不可修改。',  
    confirmText: '确认提交',  
    cancelText: '再检查下',  
    confirmColor: '#4A90E2',  
    success: (res) => {  
      if (res.confirm) {  
        handleSubmit()  
      }  
    }  
  })  
}  

const handleSubmit = async () => {  
  // 简单的XSS防护及特殊字符过滤  
  const xssFilter = (str: string) => {  
    if (!str) return str  
    return str.replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, '&#x27;')  
  }  

  // 组装提交参数  
  const params = {  
    reportName: isAnonymous.value === 0 ? '' : xssFilter(formData.reportName),  
    reportPhone: isAnonymous.value === 0 ? '' : formData.reportPhone,  
    isAnonymous: isAnonymous.value,  
    reportedName: xssFilter(formData.reportedName),  
    reportedArea: formData.reportedArea,  
    reportedPost: xssFilter(formData.reportedPost),  
    reportedPhone: formData.reportedPhone,  
    problemType: problemTypes.value.length > 0 ? problemTypes.value[currentProblemType.value].dictValue : '',  
    happenTime: formData.happenTime,  
    happenPlace: xssFilter(formData.happenPlace),  
    relatedBusiness: xssFilter(formData.relatedBusiness),  
    factDesc: xssFilter(formData.factDesc),  
    attachmentIds: formData.attachmentIds.join(',')  
  }  

  isSubmitting.value = true  
  uni.showLoading({ title: '提交中...', mask: true })  
  try {  
    const res = await submitReportInfo(params)  
    uni.hideLoading()  

    if (res.code === 200) {  
      uni.showToast({ title: '提交成功', icon: 'success' })  
      // 跳转到成功页,携带返回的查询编码  
      let queryNo = ''  
      if (res.data != null) {   
         // 根据后端实际返回的数据结构来取查询编码  
         if (typeof res.data === 'string') {  
           queryNo = res.data as string  
         } else {  
           const resData = res.data as UTSJSONObject  
           const codeVal = resData['code'] ?? resData['queryNo']  
           if (codeVal != null) {  
             queryNo = `${codeVal}`  
           }  
         }  
      }  
      setTimeout(() => {  
        uni.redirectTo({  
          url: `/pages/success/success?type=report&queryNo=${queryNo || '--------'}`  
        })  
      }, 1500)  
    } else if (res.code === 401) {  
      handleAuthError()  
    } else {  
      handleBusinessError(res.code, res.msg || '提交失败')  
    }  
  } catch (err) {  
    uni.hideLoading()  
    console.error('表单提交失败', err)  
    handleNetworkError(err)  
  } finally {  
    isSubmitting.value = false  
  }  
}  
</script>  

<style>  
.page-container {  
  background-color: #F5F7FA;  
  min-height: 100vh;  
  /* 解决内部子元素无法撑开外部导致无法滚动的问题 */  
  overflow: visible;  
  padding-bottom: 200rpx; /* 给底部按钮留出空间 */  
}  

/* 弹窗样式 */  
.modal-overlay {  
  position: fixed;  
  top: 0;  
  left: 0;  
  right: 0;  
  bottom: 0;  
  background-color: rgba(0, 0, 0, 0.5);  
  z-index: 999;  
  align-items: center;  
  justify-content: center;  
}  
.modal-content {  
  width: 678rpx;  
  background-color: #FFFFFF;  
  border-radius: 24rpx;  
  padding: 40rpx;  
  /* 顶部淡粉色渐变效果,如果有兼容问题可改用纯色 */  
  background-image: linear-gradient(180deg, #FFF0F0 0%, #FFFFFF 15%);  
}  
.modal-header {  
  flex-direction: row;  
  justify-content: space-between;  
  align-items: flex-start;  
  margin-bottom: 24rpx;  
}  
.modal-title {  
  font-size: 36rpx;  
  font-weight: bold;  
  color: #333333;  
}  
.modal-warning-icon {  
  width: 98rpx;  
  height: 97rpx;  
  margin-top: -20rpx;  
  margin-right: 20rpx;  
}  
.modal-subtitle {  
  font-size: 28rpx;  
  color: #7C889C;  
  margin-bottom: 30rpx;  
}  
.modal-list {  
  margin-bottom: 40rpx;  
}  
.modal-list-item {  
  flex-direction: row;  
  margin-bottom: 20rpx;  
  align-items: flex-start;  
}  
.modal-list-num {  
  font-size: 28rpx;  
  color: #3BA7DF;  
  font-weight: bold;  
  margin-right: 12rpx;  
  width: 32rpx;  
}  
.modal-list-text {  
  flex: 1;  
  font-size: 28rpx;  
  color: #3B3D3F;  
  line-height: 1.5;  
}  
.modal-btn {  
  background-color: #3BA7DF;  
  color: #FFFFFF;  
  font-size: 32rpx;  
  border-radius: 16rpx;  
  height: 88rpx;  
  line-height: 88rpx;  
  width: 100%;  
}  
.modal-btn-disabled {  
  opacity: 0.6;  
}  
.modal-btn:active {  
  background-color: #2b8cbe;  
}  

/* 顶部步骤指示器 */  
.step-indicator {  
  flex-direction: row;  
  align-items: center;  
  justify-content: center;  
  padding: 30rpx 0;  
  background-color: #FFFFFF;  
  position: sticky;  
  top: 0;  
  z-index: 100;  
}  
.step {  
  flex-direction: row;  
  align-items: center;  
}  
.step-circle {  
  width: 40rpx;  
  height: 40rpx;  
  border-radius: 20rpx;  
  background-color: #E4E7ED;  
  align-items: center;  
  justify-content: center;  
  margin-right: 12rpx;  
}  
.active-circle {  
  background-color: #35ACDF;  
}  
.circle-text {  
  font-size: 24rpx;  
  color: #FFFFFF;  
}  
.step-label {  
  font-size: 26rpx;  
  color: #999999;  
}  
.active-label {  
  color: #35ACDF;  
  font-weight: bold;  
}  
.step-arrow {  
  width: 24rpx;  
  height: 24rpx;  
  margin: 0 20rpx;  
}  

/* 滚动区域 */  
.scroll-content {  
  padding: 30rpx;  
}  

/* 卡片样式 */  
.card {  
  background-color: #FFFFFF;  
  border-radius: 24rpx;  
  padding: 40rpx 30rpx;  
  margin-bottom: 30rpx;  
}  
.card-title {  
  font-size: 32rpx;  
  font-weight: bold;  
  color: #1E2939;  
  margin-bottom: 40rpx;  
}  

/* 表单项 */  
.form-item {  
  margin-bottom: 36rpx;  
}  
.form-row {  
  flex-direction: row;  
  justify-content: space-between;  
  align-items: center;  
}  
.label-row {  
  flex-direction: row;  
  align-items: center;  
  margin-bottom: 16rpx;  
}  
.form-label {  
  font-size: 28rpx;  
  color: #3B3D3F;  
  margin-bottom: 16rpx;  
}  
.label-row .form-label {  
  margin-bottom: 0;  
}  
.required {  
  color: #E02020;  
  font-size: 28rpx;  
  margin-left: 8rpx;  
}  
.form-desc {  
  font-size: 24rpx;  
  color: #999999;  
  margin-top: 8rpx;  
}  

/* 输入框 */  
.input-field {  
  height: 88rpx;  
  background-color: #F8F9FA;  
  border-radius: 16rpx;  
  padding: 0 24rpx;  
  font-size: 28rpx;  
  color: #333333;  
}  
.textarea-field {  
  height: 200rpx;  
  background-color: #F8F9FA;  
  border-radius: 16rpx;  
  padding: 24rpx;  
  font-size: 28rpx;  
  color: #333333;  
  width: 100%;  
  box-sizing: border-box;  
}  
.placeholder {  
  color: #1E293980;  
}  

/* 选择器模拟 */  
.picker-view-wrap {  
  flex-direction: row;  
  align-items: center;  
  justify-content: space-between;  
  height: 80rpx;  
  background-color: #F8F9FA;  
  border-radius: 12rpx;  
  padding: 0 24rpx;  
}  
.picker-text {  
  font-size: 28rpx;  
  color: #1E293980  
}  
.picker-arrow {  
  width: 24rpx;  
  height: 24rpx;  
}  

/* 问题类型列表 */  
.type-list {  
  flex-direction: column;  
}  
.type-item {  
  background-color: #F8F9FA;  
  border-radius: 16rpx;  
  padding: 24rpx;  
  margin-bottom: 20rpx;  
  border: 2rpx solid transparent;  
}  
.type-item-active {  
  background-color: #FFFFFF;  
  border: 2rpx solid #4A90E2;  
}  
.type-title {  
  font-size: 28rpx;  
  font-weight: bold;  
  color: #333333;  
  margin-bottom: 8rpx;  
}  
.type-title-active {  
  color: #333333;  
}  
.type-desc {  
  font-size: 24rpx;  
  color: #999999;  
}  
.type-desc-active {  
  color: #999999;  
}  

/* 上传区域 */  
.upload-area {  
  background-color: #F8F9FA;  
  border: 2rpx dashed #DCDFE6;  
  border-radius: 16rpx;  
  padding: 40rpx;  
  align-items: center;  
  justify-content: center;  
}  
.upload-icon {  
  width: 64rpx;  
  height: 64rpx;  
  margin-bottom: 20rpx;  
}  
.upload-text {  
  font-size: 28rpx;  
  color: #666666;  
  margin-bottom: 12rpx;  
}  
.upload-desc {  
  font-size: 24rpx;  
  color: #999999;  
  text-align: center;  
}  

.file-list {  
  margin-top: 20rpx;  
}  
.file-item {  
  flex-direction: row;  
  align-items: center;  
  background-color: #F8F9FA;  
  padding: 16rpx 24rpx;  
  border-radius: 8rpx;  
  margin-bottom: 20rpx;  
}  
.file-icon {  
  font-size: 40rpx;  
  margin-right: 16rpx;  
}  
.file-info {  
  flex: 1;  
  flex-direction: column;  
}  
.file-size {  
  font-size: 24rpx;  
  color: #999999;  
  margin-top: 4rpx;  
}  
.file-name {  
  font-size: 28rpx;  
  color: #333333;  
  lines: 1;  
  text-overflow: ellipsis;  
}  
.file-delete {  
  width: 40rpx;  
  height: 40rpx;  
  align-items: center;  
  justify-content: center;  
  margin-left: 12rpx;  
}  
.delete-icon {  
  color: #999999;  
  font-size: 36rpx;  
  line-height: 1;  
}  

/* 底部提示 */  
.tips-area {  
  padding: 0 20rpx 40rpx 20rpx;  
}  
.tip-item {  
  flex-direction: row;  
  margin-bottom: 12rpx;  
}  
.tip-dot {  
  color: #999999;  
  margin-right: 12rpx;  
  font-size: 24rpx;  
}  
.tip-text {  
  color: #999999;  
  font-size: 24rpx;  
  flex: 1;  
}  

/* 底部安全距离 */  
.safe-bottom-area {  
  height: 20rpx;  
}  

/* 底部固定按钮 */  
.bottom-bar {  
  z-index: 100;  
  position: fixed;  
  bottom: 0;  
  left: 0;  
  width: 100%;  
  background-color: #FFFFFF;  
  padding: 20rpx 30rpx 60rpx 30rpx; /* 底部留白适配全面屏 */  
  box-shadow: 0rpx -4rpx 20rpx rgba(0, 0, 0, 0.05);  
  box-sizing: border-box;  
}  
.submit-btn {  
  background-color: #4A90E2;  
  color: #FFFFFF;  
  font-size: 32rpx;  
  border-radius: 25rpx;  
  height: 88rpx;  
  line-height: 88rpx;  
  width: 700rpx;  
  height: 88rpx;  
}  
.submit-btn:active {  
  background-color: #3A7CC8;  
}  
/* 修正 uni-datetime-picker 在部分环境下的弹窗层级问题 */  
.picker-container {  
  width: 100%;  
}  
:deep(.uni-datetime-picker) {  
  width: 100%;  
}  
:deep(.uni-date-x) {  
  background-color: transparent;  
}  
</style>  

操作步骤:

输入框多聚焦几次就会出现该问题

预期结果:

输入框多聚焦几次就会出现页面滚动到其他地方

实际结果:

输入框多聚焦几次就会出现页面滚动到其他地方

bug描述:

长表单页面输入框聚焦会导致页面滚动到其他地方
使用的uni-app x
adjustPosition 设置为 false 无效

2026-04-28 11:17 负责人:无 分享
已邀请:
DCloud_UNI_JBB

DCloud_UNI_JBB

试试同样的写法原生微信小程序有没有这个问题

  • 1***@qq.com (作者)

    微信原生给输入框加上adjust-position="{{false}}" 可以解决 ,在 uniapp 中加上无效

    2026-04-28 13:09

要回复问题请先登录注册