index.vue 6.55 KB
<template>
  <view class="form-page">
    <!-- uview表单容器 -->
    <u-form
        ref="formRef"
        :model="formData"
        :rules="formRules"
        label-width="180rpx"
        label-position="left"
    >
      <!-- 1. 文本域区域 -->
      <u-form-item
          label="问题描述"
          prop="content"
          class="form-item"
      >
        <u-textarea
            v-model="formData.content"
            placeholder="请输入问题描述..."
            maxlength="200"
            :show-word-limit="true"
            :height="200"
            border
            class="textarea"
        ></u-textarea>
        <view class="word-count">{{ formData.content.length }}/200</view>
      </u-form-item>

      <!-- 2. 图片上传区域(带星号,限制1-3张) -->
      <u-form-item
          label="上传图片"
          prop="images"
          class="form-item"
          required
      >
        <u-upload
            v-model="formData.images"
            :action="uploadUrl"
            :max-count="3"
            :multiple="true"
            @after-read="handleAfterRead"
            @delete="handleDelete"
            @on-exceed="handleExceed"
            upload-text="选择图片"
            del-color="#ff4d4f"
            class="upload-wrap"
        ></u-upload>
        <view class="tips">(最少1张,最多3张)</view>
        <view v-if="uploadTips" class="upload-tips">{{ uploadTips }}</view>
      </u-form-item>

      <!-- 3. 单选选择区域 -->
      <u-form-item
          label="是否转为工单"
          prop="isWorkOrder"
          class="form-item"
          required
      >
        <u-radio-group
            v-model="formData.isWorkOrder"
            :list="radioList"
            active-color="#1989fa"
            class="radio-group"
        ></u-radio-group>
      </u-form-item>
    </u-form>

    <!-- 底部提交按钮 -->
    <view class="btn-wrap">
      <u-button
          type="primary"
          size="default"
          @click="handleSubmit"
          :style="{ width: '100%', height: '88rpx', fontSize: '32rpx', borderRadius: 0 }"
      >
        提交
      </u-button>
    </view>
  </view>
</template>

<script setup>
import { ref, reactive } from 'vue';

// 表单实例
const formRef = ref(null);

// 1. 表单数据(统一管理)
const formData = reactive({
  content: '', // 问题描述
  images: [],  // 上传图片列表
  isWorkOrder: '' // 是否转为工单 1:是 2:否
});

// 2. 图片上传相关
const uploadUrl = ref(''); // 替换为真实上传接口地址
const uploadTips = ref(''); // 上传提示文字

// 3. 单选列表
const radioList = ref([
  { label: '是', value: '1' },
  { label: '否', value: '2' }
]);

// 4. 表单验证规则
const formRules = ref({
  // 问题描述验证
  content: [
    { required: true, message: '请输入问题描述', trigger: 'blur' },
    { max: 200, message: '问题描述不能超过200字', trigger: 'blur' }
  ],
  // 图片验证(自定义规则)
  images: [
    {
      validator: (rule, value, callback) => {
        if (value.length < 1) {
          callback(new Error('最少需要上传1张图片'));
          uploadTips.value = '最少需要上传1张图片!';
        } else if (value.length > 3) {
          callback(new Error('最多只能上传3张图片'));
          uploadTips.value = '最多只能上传3张图片!';
        } else {
          uploadTips.value = '';
          callback();
        }
      },
      trigger: ['change', 'blur']
    }
  ],
  // 单选验证
  isWorkOrder: [
    { required: true, message: '请选择是否转为工单', trigger: 'change' }
  ]
});

// 图片上传后处理
const handleAfterRead = (event) => {
  const { file } = event;
  // 模拟上传(实际项目替换为真实接口请求)
  setTimeout(() => {
    // 上传成功后将文件加入列表
    formData.images.push({
      url: file.url, // 本地临时路径/线上地址
      name: file.name,
      status: 'success'
    });
    // 手动触发图片验证
    formRef.value.validateField('images');
  }, 500);
};

// 删除图片
const handleDelete = (index) => {
  formData.images.splice(index, 1);
  // 手动触发图片验证
  formRef.value.validateField('images');
};

// 超出最大数量提示
const handleExceed = () => {
  uni.showToast({
    title: '最多只能上传3张图片',
    icon: 'none'
  });
};

// 提交表单
const handleSubmit = () => {
  // 触发表单整体验证
  formRef.value.validate((valid) => {
    if (!valid) {
      return; // 验证失败则返回
    }

    // 验证通过,构造提交数据
    const submitData = {
      content: formData.content,
      images: formData.images.map(item => item.url), // 提取图片地址
      isWorkOrder: formData.isWorkOrder
    };

    // 模拟接口提交
    uni.showLoading({ title: '提交中...' });
    setTimeout(() => {
      uni.hideLoading();
      uni.showToast({ title: '提交成功', icon: 'success' });
      // 重置表单
      formRef.value.resetFields();
      formData.images = []; // 手动清空图片列表
      uploadTips.value = '';
    }, 1500);
  });
};
</script>

<style scoped lang="scss">
.form-page {
  background-color: #f8f8f8;
  min-height: 100vh;
  padding: 20rpx;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  padding-bottom: 100rpx; // 给底部按钮留空间
}

// 表单项通用样式
.form-item {
  background-color: #fff;
  border-radius: 12rpx;
  padding: 20rpx;
  margin-bottom: 20rpx;
  box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.03);
}

// 文本域样式
.textarea {
  margin-bottom: 10rpx;
}

.word-count {
  font-size: 24rpx;
  color: #999;
  text-align: right;
  margin-top: 5rpx;
}

// 上传区域样式
.upload-wrap {
  margin-top: 10rpx;
}

.tips {
  font-size: 24rpx;
  color: #999;
  margin: 10rpx 0 5rpx 0;
}

.upload-tips {
  font-size: 24rpx;
  color: #ff4d4f;
  margin-top: 5rpx;
}

// 单选组样式
.radio-group {
  margin-top: 10rpx;
}

// 底部按钮容器
.btn-wrap {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 999;
  padding: 0;
  background-color: #f8f8f8;
  // 兼容微信小程序底部安全区
  /* #ifdef MP-WEIXIN */
  padding-bottom: constant(safe-area-inset-bottom);
  padding-bottom: env(safe-area-inset-bottom);
  /* #endif */
}

// 覆盖u-form默认样式(可选)
:deep(.u-form-item__content) {
  padding-left: 0 !important;
}

:deep(.u-form-item__label) {
  font-size: 30rpx !important;
  color: #333 !important;
  font-weight: 500 !important;
}

:deep(.u-form-item__error-message) {
  font-size: 24rpx !important;
  color: #ff4d4f !important;
  margin-top: 5rpx !important;
}
</style>