import formBuilderMap from "./formBuilderMap"; import { CreateElement, VNode } from "vue"; import moment from "moment"; import { getToken } from "@/utils/auth"; import {deepCopy, formatFileSize, formatTime} from "@/utils/index"; import { uploadSize } from "@/settings"; import axios from 'axios'; import { flowList } from "@/api/flow"; /** * @param {String} device 'desktop' | 'mobile' * @param {Object} info field参数 * @param {CreateElement} h * @param {Object} row 子表单的row * @param {Boolean} pWrite 主表单中子表单字段是否可写 * @param {Boolean} pReadable * @return {VNode} 主表单包含el-form-item 子表单表单组件 **/ export default function formBuilder(device, info, h, row, pWrite = false,pReadable = false,pname) { let target = row ? row : this.form let formItem; //下拉选项 let options = []; if (info?.selection_model) { options = info.selection_model_items || []; } else if (info?.stub) { options = info?.stub?.split(/\r\n/) || []; } if (device === "desktop") { // 可写并且不为查看和子表单下 if ( info._writeable || (info.type === "relation" && info._readable) || pWrite ) { switch (info.type) { case "text": formItem = h(formBuilderMap(device).get(info.type), { props: { value: target[info.name], clearable: true, placeholder: info.help_text, }, attrs: { placeholder: info.help_text, }, on: { input: (e) => { this.$set(target, info.name, e) }, }, }); break; case "textarea": formItem = h(formBuilderMap(device).get(info.type), { props: { type: "textarea", autosize: { minRows: 2, }, value: target[info.name], clearable: true, placeholder: info.help_text, }, attrs: { placeholder: info.help_text, }, on: { input: (e) => { this.$set(target, info.name, e) }, }, }); break; case "date": formItem = h(formBuilderMap(device).get(info.type), { props: { type: "date", "value-format": "yyyy-MM-dd", format: "yyyy年MM月dd日", value: target[info.name], clearable: true, placeholder: info.help_text, "picker-options": { shortcuts: [ { text: "一年前", onClick(picker) { picker.$emit( "pick", moment().subtract(1, "years").toDate() ); }, }, { text: "一月前", onClick(picker) { picker.$emit( "pick", moment().subtract(1, "months").toDate() ); }, }, { text: "一周前", onClick(picker) { picker.$emit( "pick", moment().subtract(1, "weeks").toDate() ); }, }, { text: "今天", onClick(picker) { picker.$emit("pick", new Date()); }, }, { text: "一周后", onClick(picker) { picker.$emit("pick", moment().add(1, "weeks").toDate()); }, }, { text: "一月后", onClick(picker) { picker.$emit("pick", moment().add(1, "months").toDate()); }, }, { text: "一年后", onClick(picker) { picker.$emit("pick", moment().add(1, "years").toDate()); }, }, ], }, }, attrs: { placeholder: info.help_text, }, style: { width: "100%", }, on: { input: (e) => { this.$set(target, info.name, e) }, }, }); break; case "datetime": formItem = h(formBuilderMap(device).get(info.type), { props: { type: "datetime", "value-format": "yyyy-MM-dd HH:mm:ss", format: this.datetimeFormat, value: target[info.name], clearable: true, placeholder: info.help_text, "picker-options": { shortcuts: this.shortcuts }, }, style: { width: "100%", }, attrs: { placeholder: info.help_text, }, on: { input: (e) => { this.$set(target, info.name, e) }, }, }); break; case "choice": case "select": const getSelectValue = () => { if (!!info.multiple) { return target[info.name] ? target[info.name].toString()?.split(/,|\|/).map(i => isNaN(Number(i)) ? i : Number(i)) : [] } else { return (isNaN(Number(target[info.name])) || !target[info.name]) ? target[info.name] : Number(target[info.name]) } } console.log(info.name, getSelectValue(), target[info.name]) formItem = h( formBuilderMap(device).get(info.type), { props: { value: getSelectValue(), clearable: true, placeholder: info.help_text, multiple: !!info.multiple, 'multiple-limit': info.multiple, 'collapse-tags': true, filterable: true, 'value-key': 'id', 'allow-create': !!info.is_select2_tag, 'default-first-option': true }, style: { width: "100%", }, attrs: { placeholder: info.help_text, }, on: { input: (e) => { this.$set(target, info.name, e.toString()) }, }, }, options.map((option) => h("el-option", { key: typeof option === "object" ? option.id : option, props: { label: typeof option === "object" ? option.name : option, value: typeof option === "object" ? option.id : option, }, }) ) ); break; case "radio": formItem = h( formBuilderMap(device).get(info.type), { props: { value: target[info.name], }, attrs: { placeholder: info.help_text, }, on: { input: (e) => { this.$set(target, info.name, e) }, }, }, options.map((option) => h( "el-radio", { props: { label: typeof option === "object" ? option.id : option, }, }, typeof option === "object" ? option.name : option ) ) ); break; case "file": formItem = row ? h( 'vxe-upload', { props: { value: row[info.name], 'progress-text': "{percent}%", 'more-config': { maxCount: 1, layout: 'horizontal' }, 'show-button-text': false, 'limit-size': uploadSize / 1024 / 1024, // vxeupload 单位Mb 'limit-count': info.multiple ? 20 : 1, multiple: !!info.multiple, 'name-field': 'original_name', readonly: pReadable, 'upload-method': ({ file }) => { const formData = new FormData() formData.append('file', file) window.$_uploading = true return axios.post(process.env.VUE_APP_UPLOAD_API, formData, { headers: { Authorization: `Bearer ${getToken()}`, } }).then((response) => { window.$_uploading = false if (response.status === 200 && !response.data.code) { if (!(row[info.name] instanceof Array)) { row[info.name] = [] } row[info.name].push({ response: response.data.data, name: response.data.data.original_name, url: response.data.data.url, original_name: response.data.data.original_name }) if (row[info.name].length > (info.multiple ? 20 : 1)) { row[info.name].shift() } } else { this.$message.error("上传失败") } }).catch(err => { window.$_uploading = false }) }, 'remove-method': _ => { row[info.name] = [] } } } ) : h( formBuilderMap(device).get(info.type), { props: { action: process.env.VUE_APP_UPLOAD_API, headers: { Authorization: `Bearer ${getToken()}`, }, accept: "application/msword,image/jpeg,application/pdf,image/png,application/vnd.ms-powerpoint,text/plain,application/x-zip-compressed,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document", multiple: !!info.multiple, limit: info.multiple ? 20 : 1, fileList: this.form[info.name] instanceof Array ? this.form[info.name]?.map(i => { if (i.hasOwnProperty('original_name')) { i.name = i.original_name } return i }) : [], beforeUpload: (file) => { if (file.size > uploadSize) { this.$message({ type: "warning", message: `上传图片大小超过${formatFileSize(uploadSize)}!`, }); return false; } window.$_uploading = true }, onSuccess: (response, file, fileList) => { window.$_uploading = false fileList.forEach((file) => { if (file.response?.data && !file.response?.code) { file.response = file.response.data; } }); this.form[info.name] = fileList }, onRemove: (file, fileList) => { this.form[info.name] = fileList }, onError: (err, file, fileList) => { window.$_uploading = false this.form[info.name] = fileList this.$message({ type: "warning", message: err, }); }, }, scopedSlots: { file: (scope) => { let { file } = scope; return [ h("div", {}, [ h("i", { class: { "el-icon-circle-check": file.status === "success", "el-icon-loading": file.status === "uploading", }, style: { color: file.status === "success" ? "green" : "", }, }), h( "a", { class: { "uploaded-a": file.status === "success", }, style: { padding: "0 4px", }, on: { click: _ => { this.$bus.$emit('online-file', file.url) } } }, file.original_name || file.name ), ]), h("i", { class: "el-icon-close", on: { ["click"]: () => { if (file.status === "uploading") return this.form[info.name].splice(this.form[info.name].indexOf(file), 1) }, }, }), ]; }, }, }, [ h( "el-button", { slot: "trigger", props: { size: "small", type: "primary", }, }, "选取文件" ), h( "div", { class: "el-upload__tip", slot: "tip", }, `文件不超过${formatFileSize(uploadSize)}` ), ] ); break; case "label": formItem = h( formBuilderMap(device).get(info.type), { props: { type: "primary", }, }, info.label ); break; case "static": formItem = h( formBuilderMap(device).get(info.type), { props: { type: "primary", }, attrs: { href: target[info.name], target: "_blank", }, }, info.label ); break; case "hr": formItem = h(formBuilderMap(device).get(info.type), {}, info.label); break; case "relation-flow": if (!this.flows[info.name]) { flowList('all', { page: 1, page_size: 9999, is_simple: 1, custom_model_id: info.stub, is_auth: 1, }).then(res => { this.$set(this.flows, info.name, res.data.data) }) } formItem = h( formBuilderMap(device).get(info.type), { props: { value: (target[info.name] ? target[info.name].toString()?.split(',').map(i => Number(i)) : []), clearable: true, placeholder: info.help_text, multiple: true, filterable: true, 'reserve-keyword': true, loading: this.flowSelectLoading }, attrs: { placeholder: info.help_text, }, style: { width: '100%' }, on: { input: (e) => { this.$set(target, info.name, e.toString()) } }, }, this.flows[info.name]?.map((option) => h("el-option", { props: { label: option.title, value: option.id, }, }) ) ); break; case "relation": formItem = h( "vxe-table", { ref: `subForm-${info.name}`, style: { "margin-top": "10px", }, props: { "min-height": "200px", border: true, stripe: true, data: (this.form[info.name] && typeof this.form[info.name] !== 'string') ? this.form[info.name] : [], "keep-source": true, "column-config": { resizable: true }, "show-overflow": true, "edit-rules": info._writeable ? this.subRules[`${info.name}_rules`] : {}, "edit-config": info._writeable ? { trigger: "click", mode: "row", showStatus: false, isHover: true, autoClear: false, } : {}, }, on: { "edit-closed": ({ row, column }) => { const $table = this.$refs[`subForm-${info.name}`]; if ($table) { this.$set( this.form, info.name, this.$refs[`subForm-${info.name}`].tableData ); } }, }, }, [ info._writeable ? h( "vxe-column", { props: { width: 56, align: "center", }, scopedSlots: { default: ({ row }) => { return h("el-button", { slot: "default", style: { padding: "9px", }, props: { type: "danger", size: "small", icon: "el-icon-minus", }, on: { click: async (_) => { const $table = this.$refs[`subForm-${info.name}`]; if ($table) { await $table.remove(row); this.form[info.name] = $table.getTableData()?.tableData } }, }, }); }, }, }, [ h("el-button", { slot: "header", style: { padding: "9px", }, props: { type: "primary", size: "small", icon: "el-icon-plus", }, on: { click: async (_) => { const $table = this.$refs[`subForm-${info.name}`]; if ($table) { const record = {}; this.form[info.name].unshift(record) // 临时数据不好验证长度 // const { row: newRow } = await $table.insert( // record // ); await this.$nextTick(); await $table.setEditRow(record); } }, }, }), ] ) : "", ...this.subForm .get(info.sub_custom_model_id) ?.customModel?.fields?.map((subField, subIndex) => h("vxe-column", { props: { field: subField.name, title: subField.label, align: "center", 'min-width': '180', "edit-render": {}, }, scopedSlots: { edit: ({ row: myrow }) => { return formBuilder.bind(this)( device, subField, h, myrow, info._writeable, false, ); }, [['file', 'choices', 'choice', 'select', 'radio'].indexOf(subField.type) !== -1 ? 'default' : false]: ({ row: myrow }) => { return formBuilder.bind(this)( device, subField, h, myrow, false, true, ); } }, }) ), ] ); break; } } else if (info._readable || pReadable) { switch (info.type) { case "date": formItem = h( "span", { style: { color: "#333", }, }, target[info.name] ? moment(target[info.name]).format("YYYY年MM月DD日") : '' ); break; case "datetime": formItem = h( "span", { style: { color: "#333", }, }, target[info.name] ? moment(target[info.name]).format("YYYY年MM月DD日 HH时mm分") : '' ); break; case "choice": case "select": const getDetailSelectValue = () => { let res = '' if (!!info.multiple) { res = target[info.name] ? target[info.name].toString()?.split(/,|\|/).map(i => isNaN(Number(i)) ? i : Number(i)) : [] } else { res = isNaN(Number(target[info.name])) ? target[info.name] : Number(target[info.name]) } if (res instanceof Array) { if (typeof options[0] === 'object') { return options?.filter((i) => res.indexOf(i.id) !== -1 )?.map(i => i.name)?.toString() } else { return res } } else { if (typeof options[0] === 'object') { return options?.filter((i) => res === i.id )?.map(i => i.name)?.toString() } else { return res } } } formItem = h( "span", { style: { color: "#333", }, }, getDetailSelectValue() ); break; case "relation-flow": if (!this.flows[info.name]) { flowList('all', { page: 1, page_size: 9999, is_simple: 1, custom_model_id: info.stub, is_auth: 1 }).then(res => { this.$set(this.flows, info.name, res.data.data) }) } formItem = h( "span", { style: { color: "#333", }, }, target[info.name]?.toString()?.split(',')?.map(j => { let flow = this.flows[info.name]?.find((i) => i.id == j ) return flow ? h('el-link', { props: { type: 'primary' }, style: { 'line-height': '1.5' }, on: { click: _ => { let target = this.$router.resolve({ path: '/flow/detail', query: { module_id: flow.custom_model_id, flow_id: flow.id, isSinglePage: 1 } }); this.modalRender = h => h('iframe',{ attrs: { src: target.href, }, style: { border: 'none', width: '100%', height: '100%' } }) this.isShowModal = true } } },flow.title) : '' }) ); break; case "file": formItem = row ? h( 'vxe-upload', { props: { value: row[info.name], 'name-field': 'original_name', 'progress-text': "{percent}%", 'more-config': { maxCount: 1, layout: 'horizontal' }, 'show-button-text': false, 'limit-size': uploadSize / 1024 / 1024, //vxe upload单位为Mb 'limit-count': info.multiple ? 20 : 1, readonly: true } } ) : h( formBuilderMap(device).get(info.type), { props: { action: process.env.VUE_APP_UPLOAD_API, headers: { Authorization: `Bearer ${getToken()}`, }, disabled: true, accept: "application/msword,image/jpeg,application/pdf,image/png,application/vnd.ms-powerpoint,text/plain,application/x-zip-compressed,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document", multiple: true, fileList: (this.form[info.name] instanceof Array) ? this.form[info.name]?.map(i => { if (i.hasOwnProperty('original_name')) { i.name = i.original_name } return i }) : [], }, scopedSlots: { file: (scope) => { let { file } = scope; return [ h("div", {}, [ h("i", { class: "el-icon-circle-check", style: { color: "green", }, }), h( "a", { class: { "uploaded-a": file.status === "success", }, style: { padding: "0 4px", }, on: { click: _ => { console.log(file) this.$bus.$emit('online-file', file.url) } } }, file.name ), ]), ]; }, }, }, [ h( "span", { slot: "trigger", }, `数量:${(this.form[info.name] instanceof Array) ? this.form[info.name]?.length : 0}` ), ] ); break; default: formItem = h( "span", { style: { color: "#333", }, }, target[info.name] ); } } if (formItem) { let jointlySignContent = [] let isJointly = false this.jointlySignLog.forEach(log => { const data = JSON.parse(log.data) Object.entries(data)?.forEach(([key, value]) => { if (value.hasOwnProperty('custom_field_id') && (value['custom_field_id'] === info.id)) { isJointly = log.is_jointly_sign jointlySignContent.push(h('div',[ h('span', value.value), h('br'), info.is_sign ? ( (log.user.sign_file && log.user.sign_file.url) ? h('el-image',{ style: { 'max-height': '80px', 'max-width': '120px' }, props: { src: log.user?.sign_file?.url, fit: 'contain', alt: log.user?.name }, attrs: { src: log.user?.sign_file?.url, alt: log.user?.name } }) : h('span', log.user?.name) ) : '', info.is_sign ? h('br') : '', h('span', log.updated_at ? this.$moment(log.updated_at).format('YYYY年MM月DD') : '') ])) } }) }) return row ? formItem : h( "el-form-item", { props: { prop: info.name, label: info.label_show ? info.label : '', 'label-width': !info.label_show ? '0': '', }, style: { "grid-column-start": info.gs_x + 1, "grid-column-end": info.gs_x + 1 + info.gs_width, "grid-row-start": info.gs_y + 1 + (info.name === 'flow_title' ? 0 : 1), "grid-row-end": info.gs_y + 1 + (info.name === 'flow_title' ? 0 : 1) + info.gs_height, }, }, isJointly ? [(/\/detail/.test(this.$route.path) && this.$route.query.flow_id && jointlySignContent.length > 0) ? '' : formItem,jointlySignContent] : [formItem,(() => { if (info.is_sign) { let log = this.logs.find(log => log.node?.fields?.findIndex(field => field?.field?.name === info.name && field.type === 'write') !== -1 && (target[info.name])) if (log && log.user) { return h('div',[ (log.user.sign_file && log.user.sign_file.url) ? h('el-image',{ style: { 'max-height': '80px', 'max-width': '120px' }, props: { src: log.user?.sign_file?.url, fit: 'contain', alt: log.user?.name }, attrs: { src: log.user?.sign_file?.url, alt: log.user?.name } }) : h('span', log.user?.name), h('div', this.$moment(log.updated_at).format('YYYY年MM月DD日 HH时mm分')) ]) } } })()] ); } } if (device === "mobile") { if (info._writeable || pWrite) { let myRules = [] if (row && this.subRules && this.subRules[`${pname}_rules`] && this.subRules[`${pname}_rules`][info.name]) { myRules = this.subRules[`${pname}_rules`][info.name] } else { myRules = this.rules[info.name] } switch (info.type) { case "text": formItem = h(formBuilderMap(device).get(info.type), { props: { name: info.name, label: info.label, value: row ? row[info.name] : this.form[info.name], clearable: true, placeholder: info.help_text, rules: myRules?.map(rule => { if (rule.hasOwnProperty('required')) { return rule } else if (rule.hasOwnProperty('pattern')) { return { pattern: rule.pattern, message: rule.message } } else { return rule } }), required: !!myRules?.find(j => j.required) }, attrs: { placeholder: info.help_text, for: info.name }, on: { input: (e) => { row ? this.$set(row, info.name, e) : this.$set(this.form, info.name, e); }, }, }); break; case "textarea": formItem = h(formBuilderMap(device).get(info.type), { props: { name: info.name, label: info.label, type: "textarea", value: row ? row[info.name] : this.form[info.name], clearable: true, placeholder: info.help_text, rules: this.rules[info.name], required: !!myRules?.find(j => j.required) }, attrs: { placeholder: info.help_text, for: info.name }, on: { input: (e) => { row ? this.$set(row, info.name, e) : this.$set(this.form, info.name, e); }, }, }); break; case "date": formItem = h("van-field", { props: { readonly: true, clickable: true, name: info.name, label: info.label, value: row ? row[info.name] : this.form[info.name], clearable: true, placeholder: info.help_text, rules: this.rules[info.name], required: !!myRules?.find(j => j.required) }, attrs: { placeholder: info.help_text, for: info.name }, on: { click: (_) => { this.vanCalendarOption.forFormName = info.name; this.vanCalendarOption.originalObj = row; this.vanCalendarOption.isShow = true; }, }, }); break; case "datetime": formItem = h("van-field", { props: { readonly: true, clickable: true, name: info.name, label: info.label, value: row ? row[info.name] : this.form[info.name], clearable: true, placeholder: info.help_text, rules: this.rules[info.name], required: !!myRules?.find(j => j.required) }, attrs: { for: info.name }, on: { click: (_) => { this.vanTimePickerOption.forFormName = info.name; this.vanTimePickerOption.originalObj = row; this.vanTimePickerOption.isShow = true }, }, }); break; case "select": let findSelect = options?.find((i) => typeof i === "object" ? i.id === (row ? row[info.name] : this.form[info.name]) : i === (row ? row[info.name] : this.form[info.name]) ); formItem = h("van-field", { props: { readonly: true, clickable: true, name: info.name, label: info.label, value: typeof findSelect === "object" ? findSelect.name : findSelect, clearable: true, placeholder: info.help_text, rules: this.rules[info.name], required: !!myRules?.find(j => j.required) }, attrs: { for: info.name }, on: { click: (_) => { this.vanPopupOption.forFormName = info.name; this.vanPopupOption.originalObj = row; this.$set(this.vanPopupOption, "columns", options); this.vanPopupOption.isShow = true; }, }, }); break; case "choice": let findChoice = info.multiple ? options?.filter((i) => typeof i === "object" ? (row ? row[info.name].indexOf(i.id) !== -1 : this.form[info.name].indexOf(i.id) !== -1) : i === (row ? row[info.name] : this.form[info.name]) ) : options?.find((i) => typeof i === "object" ? i.id === (row ? row[info.name] : this.form[info.name]) : i === (row ? row[info.name] : this.form[info.name]) ); formItem = h("van-field", { props: { readonly: true, clickable: true, name: info.name, label: info.label, value: (findChoice instanceof Array) ? (findChoice.map(i => typeof i === "object" ? i.name : i).toString()) : (typeof findChoice === "object" ? findChoice.name : findChoice), clearable: true, placeholder: info.help_text, rules: this.rules[info.name], required: !!myRules?.find(j => j.required) }, attrs: { for: info.name }, on: { click: (_) => { this.multipleSelectOption.forFormName = info.name; this.multipleSelectOption.originalObj = row; this.multipleSelectOption.outputType = 'array'; this.$set(this.multipleSelectOption, "columns", options); this.$set(this.multipleSelectOption, "options", { label: 'name', value: 'id' }); this.$set(this.multipleSelectOption, "multipleLimit", info.multiple); this.multipleSelectOption.isShow = true; }, }, }); break; case "file": formItem = h('van-cell',{ props: { title: info.label, rules: this.rules[info.name], required: !!myRules?.find(j => j.required) }, attrs: { for: info.name }, },[ h( formBuilderMap(device).get(info.type), { props: { action: process.env.VUE_APP_UPLOAD_API, headers: { Authorization: `Bearer ${getToken()}`, }, accept: "application/msword,image/jpeg,application/pdf,image/png,application/vnd.ms-powerpoint,text/plain,application/x-zip-compressed,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document", multiple: !!info.multiple, fileList: row ? row[info.name]?.map(i => { if (i.hasOwnProperty('original_name')) { i.name = i.original_name } return i }) : this.form[info.name]?.map(i => { if (i.hasOwnProperty('original_name')) { i.name = i.original_name } return i }), beforeUpload: (file) => { if (file.size > uploadSize) { this.$message({ type: "warning", message: `上传图片大小超过${formatFileSize(uploadSize)}!`, }); return false; } window.$_uploading = true }, onSuccess: (response, file, fileList) => { window.$_uploading = false fileList.forEach((file) => { if (file.response?.data && !file.response?.code) { file.response = file.response.data; } }); row ? row[info.name] = fileList : this.form[info.name] = fileList; }, onRemove: (file, fileList) => { row ? row[info.name] = fileList : this.form[info.name] = fileList; }, onError: (err, file, fileList) => { window.$_uploading = false row ? row[info.name] = fileList : this.form[info.name] = fileList; this.$message({ type: "warning", message: err, }); }, }, scopedSlots: { file: (scope) => { let { file } = scope; return [ h("div", {}, [ h("i", { class: { "el-icon-circle-check": file.status === "success", "el-icon-loading": file.status === "uploading", }, style: { color: file.status === "success" ? "green" : "", }, }), h( "a", { class: { "uploaded-a": file.status === "success", }, style: { padding: "0 4px", }, on: { click: _ => { console.log(file) this.$bus.$emit('online-file', file.url) } } }, file.name ), ]), h("i", { class: "el-icon-close", on: { ["click"]: () => { this.$set( this.file, info.field, this.file[info.field].filter( (item) => item !== file ) ); }, }, }), ]; }, }, }, [ h( "el-button", { slot: "trigger", props: { size: "mini", type: "primary", }, }, "选取文件" ), h( "div", { class: "el-upload__tip", slot: "tip", }, `文件不超过${formatFileSize(uploadSize)}` ), ] ) ]) break; case 'relation-flow': if (!this.flows[info.name]) { flowList('all', { page: 1, page_size: 9999, is_simple: 1, custom_model_id: info.stub, is_auth: 1 }).then(res => { this.$set(this.flows, info.name, res.data.data) }) } formItem = h("van-field", { props: { readonly: true, clickable: true, name: info.name, label: info.label, value: row ? row[info.name]?.toString()?.split(',').map(j => { return this.flows[info.name]?.find((i) => i.id == j )?.title; })?.toString() : this.form[info.name]?.toString()?.split(',').map(j => { return this.flows[info.name]?.find((i) => i.id == j )?.title; })?.toString(), clearable: true, placeholder: info.help_text, rules: this.rules[info.name], required: !!myRules?.find(j => j.required) }, attrs: { for: info.name }, on: { click: (_) => { this.multipleSelectOption.forFormName = info.name; this.multipleSelectOption.originalObj = row; this.multipleSelectOption.outputType = 'string'; this.$set(this.multipleSelectOption, "columns", this.flows[info.name]); this.$set(this.multipleSelectOption, "options", { label: 'title', value: 'id' }); this.$set(this.multipleSelectOption, "multipleLimit", info.multiple); this.multipleSelectOption.isShow = true }, }, }); break; case "relation": let copySubForm = deepCopy(this.form[info.name][0]) formItem = h("div", [ h("van-cell", { props: { "arrow-direction": "down", title: info.label, }, attrs: { for: info.name } },[ h('van-button',{ props: { color: 'var(--theme-color)', icon: 'plus', size: 'small' }, on: { 'click':_ => { this.form[info.name].push(copySubForm) } } },'新增') ]), h( "div", this.form[info.name] instanceof Array ? this.form[info.name]?.map((sForm, sIndex) => h( "van-cell-group", { props: { inset: false } }, [ h("van-cell", { props: { title: info.label + "-" + (sIndex + 1) } },[ h('van-button',{ props: { size: 'mini', type: 'danger', icon: 'minus' }, on: { click: _ => { this.form[info.name].splice(sIndex, 1) } } },'删除') ]), h( "van-form", { ref: "subVanForm-"+sIndex, props: { "scroll-to-error": true, }, style: { margin: '0 10px' } }, Array.from(this.subForm) .map((i) => i[1]?.customModel?.fields) ?.flat() .map((subField) => formBuilder.bind(this)( device, subField, h, sForm, info._writeable, null, info.name ) ) ), ] ) ) : '' ), ]); break; } } else if(info._readable || pReadable) { switch (info.type) { case "date": formItem = h( "van-cell", { attrs: { for: info.name }, props: { title: info.label, value: this.form[info.name] ? moment(this.form[info.name]).format("YYYY年MM月DD日") : '' } } ); break; case "datetime": formItem = h( "van-cell", { attrs: { for: info.name }, props: { title: info.label, value: this.form[info.name] ? moment(this.form[info.name]).format("YYYY年MM月DD日 HH时mm分ss秒") : '' } }, ); break; case "select": let findSelect = options.find((i) => typeof i === "object" ? i.id === this.form[info.name] : i === this.form[info.name] ); formItem = h( "van-cell", { attrs: { for: info.name }, props: { title: info.label, value: typeof findSelect === "object" ? findSelect.name : findSelect } }, ); break; case 'choice': let findChoice = options.find((i) => typeof i === "object" ? i.id === this.form[info.name] : i === this.form[info.name] ); formItem = h( "van-cell", { attrs: { for: info.name }, props: { title: info.label, value: typeof findChoice === "object" ? findChoice.name : findChoice } }, ); break; case 'file': let files = (row ? row[info.name] : this.form[info.name]) formItem = h( "van-cell", { attrs: { for: info.name }, props: { title: info.label, } }, ((files && files !== 'null' && files !== 'undefined') ? files : [])?.map(file => h("div", {}, [ h( "a", { class: { "uploaded-a": file.status === "success", }, style: { padding: "0 4px", }, on: { click:_ => { console.log(file) this.$bus.$emit('online-file', file.url) } } }, file.original_name || file.name ), ])) ); break case 'relation': formItem = h("div", [ h("van-cell", { props: { "arrow-direction": "down", title: info.label, }, }), h( "div", this.form[info.name] instanceof Array ? this.form[info.name]?.map((sForm, sIndex) => h( "van-cell-group", { props: { inset: false } }, [ h("van-cell", { props: { title: info.label + "-" + (sIndex + 1) } }), h( "van-form", { ref: "subVanForm", props: { "scroll-to-error": true, }, style: { margin: '0 10px' } }, Array.from(this.subForm) .map((i) => i[1]?.customModel?.fields) ?.flat() .map((subField) => formBuilder.bind(this)( device, subField, h, sForm, info._writeable, info._readable ) ) ), ] ) ) : '' ), ]); break; case 'relation-flow': if (!this.flows[info.name]) { flowList('all', { page: 1, page_size: 9999, is_simple: 1, custom_model_id: info.stub, is_auth: 1 }).then(res => { this.$set(this.flows, info.name, res.data.data) }) } formItem = h( "van-cell", { attrs: { for: info.name }, props: { title: info.label } }, this.form[info.name]?.toString()?.split(',')?.map(j => { let flow = this.flows[info.name]?.find((i) => i.id == j ) return flow ? h('el-link', { props: { type: 'primary' }, style: { 'line-height': '1.5' }, on: { click: _ => { let target = this.$router.resolve({ path: '/flow/detail', query: { module_id: flow.custom_model_id, flow_id: flow.id, isSinglePage: 1 } }); this.modalRender = h => h('iframe',{ attrs: { src: target.href, }, style: { border: 'none', width: '100%', height: '100%' } }) this.isShowModal = true } } },flow.title) : '' }) ); break; default: formItem = h('van-cell',{ attrs: { for: info.name }, props: { title: info.label, value: row ? row[info.name] : this.form[info.name] } }) } } let jointlySignContent = [] let isJointly = false this.jointlySignLog.forEach(log => { const data = JSON.parse(log.data) Object.entries(data)?.forEach(([key, value]) => { if (value.hasOwnProperty('custom_field_id') && (value['custom_field_id'] === info.id)) { isJointly = log.is_jointly_sign jointlySignContent.push(h('div',[ h('span', value.value), h('br'), info.is_sign ? ( (log.user.sign_file && log.user.sign_file.url) ? h('el-image',{ style: { 'max-height': '80px', 'max-width': '120px' }, props: { src: log.user?.sign_file?.url, fit: 'contain', alt: log.user?.name }, attrs: { src: log.user?.sign_file?.url, alt: log.user?.name } }) : h('span', log.user?.name) ) : '', info.is_sign ? h('br') : '', h('span', log.updated_at ? this.$moment(log.updated_at).format('YYYY年MM月DD') : '') ])) } }) }) return isJointly ? [(/\/detail/.test(this.$route.path) && this.$route.query.flow_id && jointlySignContent.length > 0) ? '' : formItem,jointlySignContent] : [formItem,(() => { if (info.is_sign) { let log = this.logs.find(log => log.node?.fields?.findIndex(field => field?.field?.name === info.name && field.type === 'write') !== -1 && (target[info.name])) if (log && log.user) { return h('van-cell',[ (log.user.sign_file && log.user.sign_file.url) ? h('el-image',{ style: { 'max-height': '80px', 'max-width': '120px' }, props: { src: log.user?.sign_file?.url, fit: 'contain', alt: log.user?.name }, attrs: { src: log.user?.sign_file?.url, alt: log.user?.name } }) : h('span', log.user?.name), h('div', this.$moment(log.updated_at).format('YYYY年MM月DD日 HH时mm分')) ]) } } })()] } }