1. 原来的框架使用的是taro3.1,开发自定义组件时想使用taro ui组件,由于taro ui组件的form表单的提交事件不能获取表单的值,这样所有表单的值获取与赋值需要自己维护。提高了开发的麻烦程度与困难程度。Taro UI | O2Team
  2. 参考设计集团任务流的自定义表单代码。
    1. 用的vue
    2. 组件是非常简单的,一个文件就写完了。对我们这个系统完全没有参考价值
    3. 唯一一点有参考价值的是用的京东的ui组件,他的form表单是可以正常获取到表单值并且可以复制的。NutUI - 移动端 React 小程序组件库
<template>
 
<nut-form class="layout" :model-value="formData.state" ref="ruleForm">
 
<nut-form-item
 
v-for="(item, index) in formData.formItems"
 
:key="index"
 
:label="item.name"
 
:prop="item.field"
 
>
 
<input
 
v-if="item.type === fieldType.Input"
 
class="nut-input-text"
 
placeholder="请输入"
 
type="text"
 
:disabled="!item.disabled"
 
v-model="formData.state[item.field].value"
 
/>
 
<input
 
v-if="item.type === fieldType.InputNumber"
 
class="nut-input-text"
 
placeholder="请输入数字"
 
type="digit"
 
:disabled="!item.disabled"
 
v-model="formData.state[item.field].value"
 
/>
 
<input-picker
 
v-if="
 
item.type === fieldType.Picker || item.type === fieldType.PickerCustom
 
"
 
title="请选择"
 
:columns="item.options ?? []"
 
:disabled="!item.disabled"
 
v-model="formData.state[item.field].value"
 
/>
 
<input-date-picker
 
v-if="item.type === fieldType.DatePicker"
 
:disabled="!item.disabled"
 
v-model="formData.state[item.field].value"
 
/>
 
<input-area
 
v-if="item.type === fieldType.Area"
 
:disabled="!item.disabled"
 
v-model="formData.state[item.field].value"
 
v-model:label="formData.state[item.field].label"
 
/>
 
<!-- <input-uploader
 
v-if="item.type === fieldType.Upload"
 
name="file"
 
multiple
 
capture
 
list-type="list"
 
maximum="9"
 
:url="uploadUrl"
 
:maximize="1024 * 1024 * 10"
 
:headers="headers"
 
:is-deletable="item.disabled"
 
:disabled="!item.disabled"
 
:before-delete="() => item.disabled"
 
@oversize="handleUploadOversize"
 
@success="handleUploadSuccess"
 
@file-item-click="handleFileItemClick"
 
v-model:file-list="formData.state[item.field].value"
 
>
 
<nut-button v-show="item.disabled" type="success" size="small"
 
>上传文件</nut-button
 
>
 
</input-uploader> -->
 
<!-- accept=".xlsx,.xls,image/*,.doc, .docx,.ppt, .pptx,.txt,.pdf"
 
multiple
 
capture="camera"
 
max-count="9" -->
 
<van-uploader
 
v-if="item.type === fieldType.Upload"
 
accept=".xlsx,.xls,image/*,.doc, .docx,.ppt, .pptx,.txt,.pdf"
 
result-type="file"
 
:max-size="1024 * 1024 * 10"
 
:deletable="item.disabled"
 
:before-delete="() => item.disabled"
 
:disabled="!item.disabled"
 
:after-read="afterRead"
 
@oversize="handleUploadOversize"
 
v-model="formData.state[item.field].value"
 
/>
 
<!-- <van-button icon="plus" type="primary">上传文件</van-button> -->
 
<!-- <nut-button v-show="item.disabled" type="success" size="small"
 
>上传文件</nut-button
 
> -->
 
  
 
<input-address
 
v-if="item.type === fieldType.Address"
 
:disabled="!item.disabled"
 
v-model="formData.state[item.field].value"
 
/>
 
</nut-form-item>
 
<nut-cell v-show="isValid">
 
<nut-button size="large" type="primary" color="#4076F6" @click="submit"
 
>登记信息</nut-button
 
>
 
<!-- <nut-button size="small" @click="reset">重置提示状态</nut-button> -->
 
</nut-cell>
 
</nut-form>
 
</template>
 
  
 
<script lang="ts">
 
import { defineComponent, ref, computed, reactive } from "vue";
 
import InputPicker from "./input/InputPicker.vue";
 
import InputDatePicker from "./input/InputDatePicker.vue";
 
import InputArea from "./input/InputArea.vue";
 
import InputAddress from "./input/InputAddress.vue";
 
import InputUploader from "./input/InputUploader.vue";
 
import { TaskFieldType, TaskStatus } from "@/common/type";
 
import { isEmpty } from "lodash";
 
import config from "@/utils/config";
 
import dd from "gdt-jsapi";
 
import { isValidTask } from "@/utils";
 
import { Toast } from "@/Library";
 
import { useStore } from "vuex";
 
// import moment from "moment";
 
  
 
export default defineComponent({
 
name: "custom-form",
 
components: {
 
InputPicker,
 
InputDatePicker,
 
InputArea,
 
InputAddress,
 
InputUploader
 
},
 
emits: ["submit"],
 
props: {
 
/**
 
* 是否折叠
 
*/
 
fieldList: {
 
type: Array
 
},
 
taskStatus: {
 
type: String
 
}
 
},
 
setup(props, { emit }) {
 
console.log("props: ", props);
 
const ruleForm = ref<any>(null);
 
const store = useStore();
 
const token = computed(() => store.state.user.token);
 
const uploadUrl = `${config.apiPrefix}/app/task/file/upload`;
 
const headers = {
 
"X-Access-Token": token.value,
 
"Content-Type": "multipart/form-data"
 
};
 
  
 
const isValid = computed(() => {
 
return isValidTask(props.taskStatus as TaskStatus);
 
});
 
  
 
const afterRead = async file => {
 
console.log("file: ", file);
 
file.status = "uploading";
 
file.message = "上传中...";
 
  
 
const formData = new FormData();
 
formData.append("file", file.file);
 
formData.append("type", file.type);
 
const res = await store.dispatch("app/uploadFile", formData);
 
if (res.success) {
 
file.status = "done";
 
file.message = "上传成功";
 
file.response = res;
 
} else {
 
file.status = "failed";
 
file.message = res.message;
 
}
 
};
 
  
 
const formData = computed(() => {
 
const state: any = {};
 
const formItems: any[] = [];
 
props.fieldList?.forEach((item: any) => {
 
try {
 
const attr = JSON.parse(item.jsonObj);
 
let defaultValue = item?.value ?? item?.defaultValue ?? "";
 
if (
 
item.type === TaskFieldType.Picker ||
 
item.type === TaskFieldType.PickerCustom
 
) {
 
defaultValue = [defaultValue];
 
} else if (item.type === TaskFieldType.Area) {
 
defaultValue = defaultValue.split(",") ?? [];
 
} else if (item.type === TaskFieldType.Upload) {
 
defaultValue = isEmpty(defaultValue)
 
? []
 
: defaultValue.split(",").map((item: any) => {
 
return {
 
name: item.replace(/^.*[\\\/]/, ""),
 
url: `${config.apiPrefix}${item}`,
 
status: "success",
 
message: "上传成功",
 
type: "image"
 
};
 
}) ?? [];
 
}
 
state[attr.field] = {
 
label: defaultValue,
 
value: defaultValue
 
};
 
  
 
console.log("attr.field: ", attr);
 
console.log("defaultValue: ", defaultValue);
 
formItems.push({
 
...attr,
 
...item,
 
// 如果是无效的任务,则禁用所有表单项
 
disabled: isValid.value ? attr.disabled : false
 
});
 
} catch (error) {
 
console.log("error: ", error);
 
}
 
});
 
  
 
return { state: reactive(state), formItems };
 
});
 
  
 
const handleFileItemClick = async ({ fileItem }) => {
 
console.log("fileItem: ", fileItem);
 
try {
 
await dd.openLink({
 
url: fileItem.url
 
});
 
} catch (error) {
 
console.log("error: ", error);
 
}
 
};
 
  
 
const handleUploadOversize = (files: File[]) => {
 
console.log("oversize 触发", files);
 
Toast.show({ title: "文件大小不能超过10M" });
 
};
 
  
 
const handleUploadSuccess = ({ data, fileItem }) => {
 
fileItem.response = data;
 
};
 
  
 
const submit = () => {
 
ruleForm.value.validate().then(({ valid, errors }: any) => {
 
if (!valid) {
 
console.log("errors: ", errors);
 
return;
 
}
 
  
 
console.log("success", formData.value.state);
 
  
 
// 遍历字段重新组装数据
 
const taskDellList: any[] = [];
 
const state = formData.value.state;
 
for (let key in state) {
 
let { label, value } = state[key];
 
const item = formData.value.formItems.find(
 
(item: any) => item.field === key
 
);
 
if (!item) {
 
continue;
 
}
 
  
 
// 后端存储字段为string类型,需要做转换
 
if (
 
item?.type === TaskFieldType.Picker ||
 
item?.type === TaskFieldType.PickerCustom
 
) {
 
value = value?.join("");
 
label = value;
 
} else if (item?.type === TaskFieldType.Area) {
 
// 这里不需要label赋值,因为组件支持了v-model:label
 
value = value?.join(",");
 
} else if (item?.type === TaskFieldType.Upload) {
 
const hasUploading = value?.some(
 
(item: any) => item.status === "uploading"
 
);
 
console.log("hasUploading: ", hasUploading);
 
if (hasUploading) {
 
Toast.show({ title: "请等待文件上传完成" });
 
return;
 
}
 
  
 
value = value
 
?.map((item: any) => {
 
console.log("upload item: ", item);
 
let url = "";
 
try {
 
// 如果是上传的文件,则从返回值中读取,否则截断路由前缀
 
if (item?.response) {
 
// const data = JSON.parse(item?.response);
 
url = item?.response?.data?.url ?? "";
 
} else {
 
url = item?.url?.substring(config.apiPrefix.length) ?? "";
 
}
 
} catch (error) {
 
console.log("error: ", error);
 
}
 
return url;
 
})
 
.join(",");
 
label = value;
 
} else {
 
label = value;
 
}
 
  
 
taskDellList.push({
 
taskId: item.taskId,
 
taskFieldId: item.id,
 
label: label,
 
value: value
 
});
 
}
 
  
 
console.log("taskDellList: ", taskDellList);
 
emit("submit", taskDellList);
 
});
 
};
 
const reset = () => {
 
ruleForm.value.reset();
 
};
 
  
 
return {
 
uploadUrl,
 
headers,
 
ruleForm,
 
formData,
 
isValid,
 
config: config,
 
fieldType: TaskFieldType,
 
afterRead,
 
handleUploadOversize,
 
handleUploadSuccess,
 
handleFileItemClick,
 
submit,
 
reset
 
};
 
}
 
});
 
</script>
  1. 添加京东组件的依赖,需要配置,其中有一个配置designWidth是需要taro3.4.13才可以使用的。于是开始升级框架。NutUI - 移动端 React 小程序组件库 编译配置详情 | Taro 文档

  2. taro升级框架太繁琐,框架问题太多。中间出了很多问题(依赖安装源,node版本等等)。Taro 正式发布 3.4 版本 —— 全面支持 Preact 和 Vue3.2 | Taro 文档

  3. 现在升级完后有一个代码规范的报错,暂时还解决不了。看他们的issue上面也有很多人遇到这个问题。react ts提示异常 · Issue #11097 · NervJS/taro · GitHub

  4. form表单数据录入的问题

    1. 数据录入类组件在Form组件使用的问题 · Issue #1448 · jdf2e/nutui-react · GitHub
  5. uploader组件在form表单中使用的问题Form表单 Form.Item加上传组件,会触发表单提交事件 · Issue #2186 · jdf2e/nutui-react · GitHub

    • 没人理我