uniapp动态表单

使用了uniapp自带扩展组件uv-ui组件库自行安装下载

bash 复制代码
<template>
  <view class="assetEdit_container">
    <view class="type-box">
      <uv-form
        labelPosition="left"
        labelWidth="140rpx"
        :model="formData"
        ref="formRef"
        :rules="rulesAll"
      >
        <view class="content-box">
          <!-- 白框 -->
          <view class="formbox">
            <!-- 扩展字段================================================================= -->
            <template v-for="item in ExtrasAll" :key="item.id">
              <!-- 单行文本 -->
              <uv-form-item
                :label="item.label"
                :prop="item.prop"
                borderBottom
                v-if="item.formType == '0'"
                :required="item.isRequire == '0'"
              >
                <uv-input
                  v-model="formData[item.prop]"
                  border="none"
                  :placeholder="`${item.placeholder || ''}`"
                >
                </uv-input>
              </uv-form-item>
              <!-- 多行文本 -->
              <uv-form-item
                :label="item.label"
                :prop="item.prop"
                borderBottom
                v-if="item.formType == '1'"
                :required="item.isRequire == '0'"
              >
                <uv-textarea
                  v-model="formData[item.prop]"
                  count
                  :customStyle="{
                    'min-height': '240rpx',
                  }"
                  autoHeight
                  :maxlength="item.length"
                  :placeholder="`${item.placeholder || ''}`"
                ></uv-textarea>
              </uv-form-item>
              <!-- 数字输入框 -->
              <uv-form-item
                :label="item.label"
                :prop="item.prop"
                borderBottom
                v-if="item.formType == '2'"
                :required="item.isRequire == '0'"
              >
                <uv-number-box
                  v-model="formData[item.prop]"
                  inputWidth="100"
                  :min="0"
                  @change="changeNum(item.prop)"
                ></uv-number-box>
              </uv-form-item>
              <!-- 单选框组 -->
              <uv-form-item
                :label="item.label"
                :prop="item.prop"
                borderBottom
                v-if="item.formType == '3'"
                :required="item.isRequire == '0'"
              >
                <uv-radio-group v-model="formData[item.prop]">
                  <uv-radio
                    :customStyle="{ margin: '8px' }"
                    v-for="i in item.options.split(',')"
                    :key="i"
                    :label="i"
                    :name="i"
                  >
                  </uv-radio>
                </uv-radio-group>
              </uv-form-item>
              <!-- 下拉单选 -->
              <uv-form-item
                :label="item.label"
                :prop="item.prop"
                borderBottom
                v-if="item.formType == '4'"
                @click="showSelect(item)"
                :required="item.isRequire == '0'"
              >
                <uv-input
                  v-model="formData[item.prop]"
                  disabled
                  disabledColor="#ffffff"
                  :placeholder="`${item.placeholder || ''}`"
                  border="none"
                >
                </uv-input>
                <template v-slot:right>
                  <uv-icon name="arrow-right"></uv-icon>
                </template>
              </uv-form-item>
              <!-- 日期选择 -->
              <uv-form-item
                :label="item.label"
                :prop="item.prop"
                borderBottom
                v-if="item.formType == '5'"
                @click="opendatetime(item)"
                :required="item.isRequire == '0'"
              >
                <uv-input
                  v-model="formData[item.prop]"
                  border="none"
                  disabled
                  disabledColor="#ffffff"
                  suffixIcon="calendar"
                  suffixIconStyle="color: #909399"
                >
                </uv-input>
              </uv-form-item>
              <!-- 文件 -->
              <uv-form-item
                :label="item.label"
                :prop="item.prop"
                borderBottom
                v-if="item.formType == '6'"
                :required="item.isRequire == '0'"
              >
                <uni-section title="选择任意文件" type="line">
                  <view class="example-body">
                    <uni-file-picker
                      limit="5"
                      file-mediatype="all"
                      @select="(e) => uploadFile(e, item)"
                      :value="formatFile(formData[item.prop])"
                      @delete="({ index }) => deleteFile(index, item)"
                    ></uni-file-picker>
                  </view>
                </uni-section>
              </uv-form-item>
              <!-- 图片 -->
              <uv-form-item
                :label="item.label"
                :prop="item.prop"
                borderBottom
                v-if="item.formType == '7'"
                :required="item.isRequire == '0'"
              >
                <uni-section title="只选择图片" type="line">
                  <view class="example-body">
                    <uni-file-picker
                      limit="9"
                      title=""
                      @select="(e) => uploadFilePic(e, item)"
                      :value="formatPic(formData[item.prop])"
                      @delete="({ index }) => deleteFilePic(index, item)"
                    ></uni-file-picker>
                  </view>
                </uni-section>
              </uv-form-item>
            </template>
          </view>
        </view>
      </uv-form>
    </view>
    <view class="btns">
      <view class="btn">
        <uv-button type="primary" text="提交" shape="circle"></uv-button>
      </view>
    </view>
    <!-- 动态选择框 -->
    <uv-picker
      ref="selectRef"
      :keyName="selectLabel"
      :columns="columns"
      @confirm="confirmSelect"
      @change="changeSelect"
    ></uv-picker>
    <!-- 动态日期选择 -->
    <uv-datetime-picker
      ref="datetimeRef"
      v-model="datetimevalue"
      mode="date"
      :formatter="formatter"
      @confirm="confirmDatetime"
    >
    </uv-datetime-picker>
  </view>
</template>

<script setup>
import { onLoad } from "@dcloudio/uni-app";
import { ref, onMounted, computed, getCurrentInstance } from "vue";
import { getExtras, getFieldExtras } from "../../api/common";
import { getToken } from "@/utils/auth.js";
import dayjs from "dayjs";
import config from "@/config/index.js";
const { proxy } = getCurrentInstance();
// 主要是获取不同的动态字段,并且根据动态字段创建校验信息
onMounted(async () => {
  // 获取全局动态字段
  Extras.value = (await getExtras()).data;
  // 获取动态校验内容
  getRule();
});
onLoad(async ({ id }) => {
  // 根据分类id获取动态字段 FieldExtras
  FieldExtras.value = (await getFieldExtras(formData.value.typeId)).data;
  // 获取动态校验内容
  getRule();
});

// 静态字段校验规则
let rules = ref({
  typeName: {
    type: "string",
    required: true,
    message: "提示信息",
    trigger: ["change"],
  },
});
// 动态校验数据
let FieldRules = ref({});
// 所有的校验:静态+动态
let rulesAll = computed(() => {
  return { ...rules.value, ...FieldRules.value };
});
// 通用的动态字段
let Extras = ref([]);
// 指定的动态字段
let FieldExtras = ref([]);
// 所有动态字段:通用的动态字段+指定的动态字段
let ExtrasAll = computed(() => {
  return Extras.value.concat(FieldExtras.value); //动态字段拼接
});
// 动态校验
// 根据扩展字段的属性配置获取校验规则
let getRule = () => {
  FieldRules.value = {};
  ExtrasAll.value.forEach((item) => {
    if (item.formType == "2" || item.formType == "3" || item.formType == "4") {
      FieldRules.value[item.prop] = {
        type: item.formType == "2" ? "number" : "string",
        required: item.isRequire == "0",
        message: `${item.label}${proxy.$t("AssetList.Cannotbeempty")}`,
        trigger: ["change"],
      };
    } else {
      FieldRules.value[item.prop] = {
        type: "string",
        required: item.isRequire == "0",
        message: `${item.label}${proxy.$t("AssetList.Cannotbeempty")}`,
        trigger: ["blur"],
      };
    }
  });
};
// ======================================================================
// 3、动态字段下拉框自定义配置
let columns = ref([]);
// 选择弹窗
let selectRef = ref(null);
// 定义一个和选择弹窗绑定的中间件
let selectProp = ref("");
// 点击状态选择输入框弹出选项
let showSelect = (item) => {
  selectRef.value.open(); //打开弹窗
  columns.value = [item.options.split(",")]; //下拉框数据
  selectProp.value = item.prop; //绑定键名
};
let confirmSelect = (e) => {
  console.log(e.value[0], 244);
  // 确认选择之后的赋值操作
  formData.value[selectProp.value] = e.value[0];
  formRef.value?.validateField(selectProp.value);
};
// 数字改变
let changeNum = (prop) => {
  formRef.value?.validateField(prop);
};
// 4、动态日期下拉框自定义配置
let datetimeRef = ref(null);
let datetimeProp = ref("");
let datetimevalue = ref(Number(new Date())); //当前打开选择器的时间
let opendatetime = (item) => {
  datetimeRef.value.open(); //打开弹窗
  datetimeProp.value = item.prop; //绑定键名
  console.log(item, 267);
  if (formData.value[item.prop]) {
    // 如果时间存在则回显时间,如果没值则选择器定位到当前时间
    datetimevalue.value = new Date(formData.value[item.prop]);
  }
};
let confirmDatetime = (e) => {
  formData.value[datetimeProp.value] = dayjs(e.value).format("YYYY-MM-DD");
  formRef.value?.validateField(datetimeProp.value);
};
let formatter = (type, value) => {
  if (type === "year") {
    return `${value}${proxy.$t("AssetList.year")}`;
  }
  if (type === "month") {
    return `${value}${proxy.$t("AssetList.month")}`;
  }
  if (type === "day") {
    return `${value}${proxy.$t("AssetList.day")}`;
  }
  return value;
};
// 5、动态文件上传
let formatFile = (UrlString) => {
  if (UrlString) {
    return UrlString.split(",").map((item) => {
      return {
        url:
          config.baseUrl +
          item
            .split("/")
            .slice(0, item.split("/").length - 1)
            .join("/"),
        extname: item.split(".").pop(),
        name: item.split("/").pop(),
      };
    });
  } else {
    return [];
  }
};
let uploadFile = (e, item) => {
  const file = e;
  if (file.tempFilePaths.length === 0) {
    uni.showToast({
      title: proxy.$t("AssetList.Nofileselected"),
      icon: "none",
    });
    return;
  }
  const uploadTask = uni.uploadFile({
    url: config.baseUrl + "/admin/sys-file/upload",
    filePath: file.tempFilePaths[0],
    name: "file", // 这里根据后端API的字段来定义
    header: {
      Authorization: "Bearer " + getToken(),
      "TENANT-ID": uni.getStorageSync("tenantId"),
    },
    success: (uploadRes) => {
      // 一个上传返回的字段,逗号拼接所有文件地址
      if (formData.value[item.prop]) {
        formData.value[item.prop] += "," + JSON.parse(uploadRes.data).data.url;
      } else {
        formData.value[item.prop] = JSON.parse(uploadRes.data).data.url;
      }
      formRef.value?.validateField(item.prop);
      uni.showToast({
        title: proxy.$t("AssetList.UploadSuccessfully"),
        icon: "success",
      });
    },
    fail: (err) => {
      uni.showToast({
        title: proxy.$t("AssetList.Uploadfailed"),
        icon: "none",
      });
    },
  });
  // 如果需要监听上传进度可以使用 uploadTask.onProgressUpdate
  // uploadTask.onProgressUpdate((res) => {
  //   console.log('上传进度', res.progress);
  //   console.log('已经上传的数据长度', res.totalBytesSent);
  //   console.log('预期需要上传的数据总长度', res.totalBytesExpectedToSend);
  // });
};
let deleteFile = (index, item) => {
  // 删除第index项
  if (formData.value[item.prop].split(",").length > 1) {
    formData.value[item.prop] = formData.value[item.prop].split(",");
    formData.value[item.prop].splice(index, 1);
    formData.value[item.prop] = formData.value[item.prop].join(",");
  } else {
    formData.value[item.prop] = "";
  }
};
// 动态上传图片
let formatPic = (UrlString) => {
  if (UrlString) {
    return UrlString.split(",").map((item) => {
      return {
        url:
          config.baseUrl +
          item
            .split("/")
            .slice(0, item.split("/").length - 1)
            .join("/"),
        extname: item.split(".").pop(),
        name: item.split("/").pop(),
      };
    });
  } else {
    return [];
  }
};
let uploadFilePic = (e, item) => {
  const file = e;
  if (file.tempFilePaths.length === 0) {
    uni.showToast({
      title: proxy.$t("AssetList.Nofileselected"),
      icon: "none",
    });
    return;
  }
  z;
  const uploadTask = uni.uploadFile({
    url: config.baseUrl + "/admin/sys-file/upload",
    filePath: file.tempFilePaths[0],
    name: "file", // 这里根据后端API的字段来定义
    header: {
      Authorization: "Bearer " + getToken(),
      "TENANT-ID": uni.getStorageSync("tenantId"),
    },
    success: (uploadRes) => {
      console.log(formData.value[item.prop], 521);

      // 一个上传返回的字段,逗号拼接所有文件地址
      if (formData.value[item.prop]) {
        formData.value[item.prop] += "," + JSON.parse(uploadRes.data).data.url;
      } else {
        formData.value[item.prop] = JSON.parse(uploadRes.data).data.url;
      }
      formRef.value?.validateField(item.prop);
      uni.showToast({
        title: proxy.$t("AssetList.UploadSuccessfully"),
        icon: "success",
      });
    },
    fail: (err) => {
      uni.showToast({
        title: proxy.$t("AssetList.Uploadfailed"),
        icon: "none",
      });
    },
  });
  // 如果需要监听上传进度可以使用 uploadTask.onProgressUpdate
  // uploadTask.onProgressUpdate((res) => {
  //   console.log('上传进度', res.progress);
  //   console.log('已经上传的数据长度', res.totalBytesSent);
  //   console.log('预期需要上传的数据总长度', res.totalBytesExpectedToSend);
  // });
};
let deleteFilePic = (index, item) => {
  // 删除第index项
  console.log(formData.value[item.prop], 542);
  if (formData.value[item.prop].split(",").length > 1) {
    formData.value[item.prop] = formData.value[item.prop].split(",");
    formData.value[item.prop].splice(index, 1);
    formData.value[item.prop] = formData.value[item.prop].join(",");
  } else {
    formData.value[item.prop] = "";
  }
};
</script>

<style lang="scss" scoped>
.assetEdit_container {
  height: 100vh;
  width: 100vw;
  background-color: #f2f2fa;
  display: flex;
  flex-direction: column;
  .type-box {
    flex: 1;
    width: 100vw;
    overflow: auto;
    padding: 24rpx;
    .title {
      font-weight: bold;
      font-size: 28rpx;
      color: #0a1629;
      line-height: 42rpx;
    }
    .content-box {
      background: #ffffff;
      padding: 10rpx 24rpx;
      box-shadow: 0rpx 12rpx 116rpx 0rpx rgba(196, 203, 214, 0.1);
      border-radius: 20rpx;
      margin: 38rpx 0;
    }
  }

  .btns {
    height: 146rpx;
    width: 100%;
    display: flex;
    background-color: #ffffff;
    justify-content: space-around;
    align-items: center;
    .btn {
      width: 300rpx;
      height: 70rpx;
    }
  }
}
</style>
相关推荐
先知demons10 小时前
uniapp开发微信小程序笔记10-触底加载
前端·笔记·微信小程序·小程序·uni-app
wkj00112 小时前
uniapp 实现 uni-file-picker 效果
uni-app
嫩八14 小时前
uniapp 自定义导航栏增加首页按钮,仿微信小程序操作胶囊
javascript·vue.js·微信·微信小程序·uni-app
初遇你时动了情1 天前
uniapp 小程序主包使用分包的项目
小程序·uni-app
苹果电脑的鑫鑫1 天前
uni-app获取到的数据如何保留两位小数
java·前端·uni-app
一回生二回熟1 天前
uniapp中父组件调用子组件方法
前端·vue.js·uni-app
堕落年代1 天前
Uniapp自动调整元素高度
前端·javascript·uni-app
收银系统源码那点事2 天前
零售餐饮收银台源码
flutter·uni-app·零售·收银系统源码·收银系统·连锁店收银系统
anyup_前端梦工厂2 天前
前端API自动化构建工具:讲述 FlyHttp 设计思想
前端·vue.js·uni-app