diff --git a/src/api/flow/statistic.js b/src/api/flow/statistic.js new file mode 100644 index 0000000..f66c7d5 --- /dev/null +++ b/src/api/flow/statistic.js @@ -0,0 +1,8 @@ +import request from "@/utils/request"; +export function statistic(name, params) { + return request({ + method: 'get', + url: `/api/oa/flow-statistic/${name}`, + params + }) +} diff --git a/src/components/MobileMultipleSelect/index.vue b/src/components/MobileMultipleSelect/index.vue index 9a06144..f0be3da 100644 --- a/src/components/MobileMultipleSelect/index.vue +++ b/src/components/MobileMultipleSelect/index.vue @@ -1,20 +1,20 @@ + + diff --git a/src/utils/formBuilder.js b/src/utils/formBuilder.js index 45d267d..da2df4d 100644 --- a/src/utils/formBuilder.js +++ b/src/utils/formBuilder.js @@ -1,11 +1,12 @@ -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 { deepCopy, formatFileSize, formatTime } from "@/utils/index"; import { uploadSize } from "@/settings"; -import axios from 'axios'; +import axios from "axios"; import { flowList } from "@/api/flow"; +import MobilePicker from '@/components/MobilePicker/index.vue'; +import MobileMultipleSelect from "@/components/MobileMultipleSelect/index.vue"; /** * @param {String} device 'desktop' | 'mobile' * @param {Object} info field参数 @@ -15,8 +16,16 @@ import { flowList } from "@/api/flow"; * @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 +export default function formBuilder( + device, + info, + h, + row, + pWrite = false, + pReadable = false, + pname +) { + let target = row ? row : this.form; let formItem; //下拉选项 let options = []; @@ -34,7 +43,7 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab ) { switch (info.type) { case "text": - formItem = h(formBuilderMap(device).get(info.type), { + formItem = h("el-input", { props: { value: target[info.name], clearable: true, @@ -45,13 +54,13 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab }, on: { input: (e) => { - this.$set(target, info.name, e) + this.$set(target, info.name, e); }, }, }); break; case "textarea": - formItem = h(formBuilderMap(device).get(info.type), { + formItem = h("el-input", { props: { type: "textarea", autosize: { @@ -66,13 +75,13 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab }, on: { input: (e) => { - this.$set(target, info.name, e) + this.$set(target, info.name, e); }, }, }); break; case "date": - formItem = h(formBuilderMap(device).get(info.type), { + formItem = h("el-date-picker", { props: { type: "date", "value-format": "yyyy-MM-dd", @@ -81,59 +90,7 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab 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()); - }, - }, - ], + shortcuts: this.shortcuts, }, }, attrs: { @@ -144,22 +101,22 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab }, on: { input: (e) => { - this.$set(target, info.name, e) + this.$set(target, info.name, e); }, }, }); break; case "datetime": - formItem = h(formBuilderMap(device).get(info.type), { + formItem = h("el-date-picker", { props: { type: "datetime", "value-format": "yyyy-MM-dd HH:mm:ss", - format: this.datetimeFormat, + format: "yyyy-MM-dd HH:mm", value: target[info.name], clearable: true, placeholder: info.help_text, "picker-options": { - shortcuts: this.shortcuts + shortcuts: this.shortcuts, }, }, style: { @@ -170,7 +127,7 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab }, on: { input: (e) => { - this.$set(target, info.name, e) + this.$set(target, info.name, e); }, }, }); @@ -179,26 +136,32 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab case "select": const getSelectValue = () => { if (!!info.multiple) { - return target[info.name] ? target[info.name].toString()?.split(/,|\|/).map(i => isNaN(Number(i) || !i) ? i : Number(i)) : [] + return target[info.name] + ? target[info.name] + .toString() + ?.split(/,|\|/) + .map((i) => (isNaN(Number(i) || !i) ? i : Number(i))) + : []; } else { - return (isNaN(Number(target[info.name])) || !target[info.name]) ? target[info.name] : Number(target[info.name]) + return isNaN(Number(target[info.name])) || !target[info.name] + ? target[info.name] + : Number(target[info.name]); } - } + }; formItem = h( - formBuilderMap(device).get(info.type), + "el-select", { props: { value: getSelectValue(), clearable: true, placeholder: info.help_text, multiple: !!info.multiple, - 'multiple-limit': info.multiple, - 'collapse-tags': true, + "multiple-limit": info.multiple, filterable: true, - 'value-key': 'id', - 'reserve-keyword': true, - 'allow-create': !!info.is_select2_tag, - 'default-first-option': true + "value-key": "id", + "reserve-keyword": true, + "allow-create": !!info.is_select2_tag, + "default-first-option": true, }, style: { width: "100%", @@ -208,7 +171,7 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab }, on: { input: (e) => { - this.$set(target, info.name, e.toString()) + this.$set(target, info.name, e.toString()); }, }, }, @@ -225,7 +188,7 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab break; case "radio": formItem = h( - formBuilderMap(device).get(info.type), + "el-radio-group", { props: { value: target[info.name], @@ -235,7 +198,7 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab }, on: { input: (e) => { - this.$set(target, info.name, e) + this.$set(target, info.name, e); }, }, }, @@ -253,177 +216,187 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab ); break; case "file": - formItem = row ? - h( - 'vxe-upload', - { + 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, + "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', + "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; - file.url = file.response?.url; - } - }); - 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" : "", + "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()}`, }, - }), - h( - "a", - { - class: { - "uploaded-a": file.status === "success", - }, - style: { - padding: "0 4px", - }, - on: { - click: _ => { - this.$bus.$emit('online-file', file.url) - } + }) + .then((response) => { + window.$_uploading = false; + if (response.status === 200 && !response.data.code) { + if (!(row[info.name] instanceof Array)) { + row[info.name] = []; } - }, - 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) - }, - }, - }), - ]; + 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( - "el-button", + }) + : h( + "el-upload", { - slot: "trigger", props: { - size: "small", - type: "primary", + 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; + file.url = file.response?.url; + } + }); + 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( - "div", - { - class: "el-upload__tip", - slot: "tip", - }, - `文件不超过${formatFileSize(uploadSize)}` - ), - ] - ); + [ + 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), + "div", { props: { type: "primary", @@ -434,7 +407,7 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab break; case "static": formItem = h( - formBuilderMap(device).get(info.type), + "el-link", { props: { type: "primary", @@ -448,42 +421,47 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab ); break; case "hr": - formItem = h(formBuilderMap(device).get(info.type), {}, info.label); + formItem = h("el-divider", {}, info.label); break; case "relation-flow": if (!this.flows[info.name]) { - flowList('all', { + 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) - }) + }).then((res) => { + this.$set(this.flows, info.name, res.data.data); + }); } formItem = h( - formBuilderMap(device).get(info.type), + "el-select", { props: { - value: (target[info.name] ? target[info.name].toString()?.split(',').map(i => Number(i)) : []), + 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 + "reserve-keyword": true, + loading: this.flowSelectLoading, }, attrs: { placeholder: info.help_text, }, style: { - width: '100%' + width: "100%", }, on: { input: (e) => { - this.$set(target, info.name, e.toString()) - } + this.$set(target, info.name, e.toString()); + }, }, }, this.flows[info.name]?.map((option) => @@ -508,11 +486,17 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab "min-height": "200px", border: true, stripe: true, - data: (this.form[info.name] && typeof this.form[info.name] !== 'string') ? this.form[info.name] : [], + 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-rules": info._writeable + ? this.subRules[`${info.name}_rules`] + : {}, "edit-config": info._writeable ? { trigger: "click", @@ -563,7 +547,8 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab this.$refs[`subForm-${info.name}`]; if ($table) { await $table.remove(row); - this.form[info.name] = $table.getTableData()?.tableData + this.form[info.name] = + $table.getTableData()?.tableData; } }, }, @@ -587,7 +572,7 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab const $table = this.$refs[`subForm-${info.name}`]; if ($table) { const record = {}; - this.form[info.name].unshift(record) + this.form[info.name].unshift(record); // 临时数据不好验证长度 // const { row: newRow } = await $table.insert( // record @@ -609,7 +594,7 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab field: subField.name, title: subField.label, align: "center", - 'min-width': '180', + "min-width": "180", "edit-render": {}, }, scopedSlots: { @@ -620,19 +605,23 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab h, myrow, info._writeable, - false, + false ); }, - [['file', 'choices', 'choice', 'select', 'radio'].indexOf(subField.type) !== -1 ? 'default' : false]: ({ row: myrow }) => { + [["file", "choices", "choice", "select", "radio"].indexOf( + subField.type + ) !== -1 + ? "default" + : false]: ({ row: myrow }) => { return formBuilder.bind(this)( device, subField, h, myrow, false, - true, + true ); - } + }, }, }) ), @@ -650,7 +639,9 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab color: "#333", }, }, - target[info.name] ? moment(target[info.name]).format("YYYY年MM月DD日") : '' + target[info.name] + ? moment(target[info.name]).format("YYYY年MM月DD日") + : "" ); break; case "datetime": @@ -661,36 +652,48 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab color: "#333", }, }, - target[info.name] ? moment(target[info.name]).format("YYYY年MM月DD日 HH时mm分") : '' + target[info.name] + ? moment(target[info.name]).format("YYYY年MM月DD日 HH时mm分") + : "" ); break; case "choice": case "select": const getDetailSelectValue = () => { - let res = '' + let res = ""; if (!!info.multiple) { - res = target[info.name] ? target[info.name].toString()?.split(/,|\|/).map(i => (isNaN(Number(i)) || !i) ? i : Number(i)) : [] + res = target[info.name] + ? target[info.name] + .toString() + ?.split(/,|\|/) + .map((i) => (isNaN(Number(i)) || !i ? i : Number(i))) + : []; } else { - res = (isNaN(Number(target[info.name]))|| !target[info.name]) ? target[info.name] : Number(target[info.name]) + res = + isNaN(Number(target[info.name])) || !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() + if (typeof options[0] === "object") { + return options + ?.filter((i) => res.indexOf(i.id) !== -1) + ?.map((i) => i.name) + ?.toString(); } else { - return res + return res; } } else { - if (typeof options[0] === 'object') { - return options?.filter((i) => - res === i.id - )?.map(i => i.name)?.toString() + if (typeof options[0] === "object") { + return options + ?.filter((i) => res === i.id) + ?.map((i) => i.name) + ?.toString(); } else { - return res + return res; } } - } + }; formItem = h( "span", { @@ -703,15 +706,15 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab break; case "relation-flow": if (!this.flows[info.name]) { - flowList('all', { + 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) - }) + is_auth: 1, + }).then((res) => { + this.$set(this.flows, info.name, res.data.data); + }); } formItem = h( "span", @@ -720,123 +723,135 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab 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, + 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; + }, + }, }, - style: { - border: 'none', - width: '100%', - height: '100%' - } - }) - this.isShowModal = true - } - } - },flow.title) : '' - }) + flow.title + ) + : ""; + }) ); break; case "file": - formItem = row ? - h( - 'vxe-upload', - { + 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()}`, + "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, }, - 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: _ => { - this.$bus.$emit('online-file', file.url) + }) + : h( + "el-upload", + { + 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; } - } - }, - file.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: (_) => { + 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}` - ), - ] - ); + [ + h( + "span", + { + slot: "trigger", + }, + `数量:${ + this.form[info.name] instanceof Array + ? this.form[info.name]?.length + : 0 + }` + ), + ] + ); break; default: formItem = h( @@ -852,41 +867,53 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab } if (formItem) { - let jointlySignContent = [] - let isJointly = false - this.jointlySignLog.forEach(log => { - const data = JSON.parse(log.data) + 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 + if ( + value.hasOwnProperty("custom_field_id") && + value["custom_field_id"] === info.id + ) { + isJointly = !!log.is_jointly_sign; if (log.status) { - 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') : '') - ])) + 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( @@ -894,814 +921,948 @@ export default function formBuilder(device, info, h, row, pWrite = false,pReadab { props: { prop: info.name, - label: info.label_show ? info.label : '', - 'label-width': !info.label_show ? '0': '', + label: info.label_show ? info.label : "", + "label-width": !info.label_show ? "0" : "", }, style: { // +1为了工作标题往下顺延 "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, + "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 ? [(info._writeable) ? formItem : '',jointlySignContent] : [(() => { - if (info.name === 'flow_title') { - return formItem - } - if (info._writeable) { - return formItem - } else { - let log = this.logs.find(log => log.node?.fields?.findIndex(field => field?.field_id === info.id && field.type === 'write') !== -1 && (target[info.name])) - if (log && log.status) { - return formItem - } - } - })(),(() => { - if (info.is_sign) { - let log = this.logs.find(log => log.node?.fields?.findIndex(field => field?.field_id === info.id && field.type === 'write') !== -1 && (target[info.name])) - if (log && log.status && 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 + isJointly + ? [info._writeable ? formItem : "", jointlySignContent] + : [ + (() => { + if (info.name === "flow_title") { + return formItem; + } + if (info._writeable) { + return formItem; + } else { + let log = this.logs.find( + (log) => + log.node?.fields?.findIndex( + (field) => + field?.field_id === info.id && + field.type === "write" + ) !== -1 && target[info.name] + ); + if ((log && log.status) || log?.user?.id === this.$store.state.user.adminId) { + return formItem; } - }) : h('span', log.user?.name), - h('div', this.$moment(log.updated_at).format('YYYY年MM月DD日 HH时mm分')) - ]) - } - } - })()] + } + })(), + (() => { + if (info.is_sign) { + let log = this.logs.find( + (log) => + log.node?.fields?.findIndex( + (field) => + field?.field_id === info.id && + field.type === "write" + ) !== -1 && target[info.name] + ); + if (log && log.status && 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), { + formItem = h("van-field", { props: { name: info.name, - label: info.label, - value: row ? row[info.name] : this.form[info.name], + value: target[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) + placeholder: info.help_text || info.label, }, 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); - }, + input: (e) => this.$set(target, info.name, e), }, }); break; case "textarea": - formItem = h(formBuilderMap(device).get(info.type), { + formItem = h("van-field", { props: { name: info.name, - label: info.label, + rows: 2, type: "textarea", - value: row ? row[info.name] : this.form[info.name], + value: target[info.name], clearable: true, - placeholder: info.help_text, - rules: this.rules[info.name], - required: !!myRules?.find(j => j.required) + placeholder: info.help_text || info.label, }, 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); - }, + input: (e) => this.$set(target, info.name, e), }, }); break; + case "radio": + formItem = h( + "van-radio-group", + { + props: { + value: target[info.name], + }, + on: { + input: (e) => { + this.$set(target, info.name, e); + }, + }, + }, + options.map((option) => + h( + "van-radio", + { + props: { + name: typeof option === "object" ? option.id : option, + "checked-color": "var(--theme-color)", + }, + }, + typeof option === "object" ? option.name : option + ) + ) + ); + break; case "date": - formItem = h("van-field", { + formItem = h("el-date-picker", { props: { - readonly: true, - clickable: true, - name: info.name, - label: info.label, - value: row ? row[info.name] : this.form[info.name], + type: "date", + "value-format": "yyyy-MM-dd", + format: "yyyy年MM月dd日", + value: target[info.name], clearable: true, - placeholder: info.help_text, - rules: this.rules[info.name], - required: !!myRules?.find(j => j.required) + placeholder: info.help_text || info.label, + "picker-options": { + shortcuts: this.shortcuts, + }, }, attrs: { - placeholder: info.help_text, - for: info.name + placeholder: info.help_text || info.label, + }, + style: { + width: "100%", }, on: { - click: (_) => { - this.vanCalendarOption.forFormName = info.name; - this.vanCalendarOption.originalObj = row; - this.vanCalendarOption.isShow = true; + input: (e) => { + this.$set(target, info.name, e); }, }, }); break; case "datetime": - formItem = h("van-field", { + formItem = h("el-date-picker", { props: { - readonly: true, - clickable: true, - name: info.name, - label: info.label, - value: row ? row[info.name] : this.form[info.name], + type: "datetime", + "value-format": "yyyy-MM-dd HH:mm:ss", + format: "yyyy-MM-dd HH:mm", + value: target[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 + placeholder: info.help_text || info.label, + "picker-options": { + shortcuts: this.shortcuts, }, }, - }); - 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) + style: { + width: "100%", }, attrs: { - for: info.name + placeholder: info.help_text || info.label, }, on: { - click: (_) => { - this.vanPopupOption.forFormName = info.name; - this.vanPopupOption.originalObj = row; - this.$set(this.vanPopupOption, "columns", options); - this.vanPopupOption.isShow = true; + input: (e) => { + this.$set(target, info.name, e); }, }, }); 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", { + case "select": + // const getMoblieSelectValue = () => { + // if (!!info.multiple) { + // return target[info.name] + // ? target[info.name] + // .toString() + // ?.split(/,|\|/) + // .map((i) => (isNaN(Number(i) || !i) ? i : Number(i))) + // : []; + // } else { + // return isNaN(Number(target[info.name])) || !target[info.name] + // ? target[info.name] + // : Number(target[info.name]); + // } + // }; + const getDetailMobileSelectValue = () => { + let res = ""; + if (!!info.multiple) { + res = target[info.name] + ? target[info.name] + .toString() + ?.split(/,|\|/) + .map((i) => (isNaN(Number(i)) || !i ? i : Number(i))) + : []; + } else { + res = + isNaN(Number(target[info.name])) || !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('div', [ + h('van-field', { + props: { + value: getDetailMobileSelectValue(), + clickable: true, + readonly: true, + 'label-width': 0, + border: false + }, + on: { + click: _ => { + this.$refs[`popup-${info.name}-${row ? 'c' : 'b'}`].show() + } + } + }), + !!info.multiple ? h(MobileMultipleSelect, { + ref: `popup-${info.name}-${row ? 'c' : 'b'}`, + props: { + outputType: 'string', + selectDataOpts: options, + options: { label: 'name', value: 'id' - }); - this.$set(this.multipleSelectOption, "multipleLimit", info.multiple); - this.multipleSelectOption.isShow = true; + } }, - }, - }); + on: { + confirm: e => { + this.$set(target, info.name, e) + this.$refs[`popup-${info.name}-${row ? 'c' : 'b'}`].hide() + } + } + }) : h(MobilePicker, { + ref: `popup-${info.name}-${row ? 'c' : 'b'}`, + props: { + columns: options, + }, + on: { + confirm: e => { + this.$set(target, info.name, typeof e === "object" ? e.id : e) + this.$refs[`popup-${info.name}-${row ? 'c' : 'b'}`].hide() + } + } + }) + ]) 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; - file.url = file.response?.url; - } - }); - target[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; + formItem = h( + "el-upload", + { + 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: err, + message: `上传图片大小超过${formatFileSize( + uploadSize + )}!`, }); - }, + return false; + } + window.$_uploading = true; }, - scopedSlots: { - file: (scope) => { - let { file } = scope; - return [ - h("div", {}, [ - h("i", { + onSuccess: (response, file, fileList) => { + window.$_uploading = false; + fileList.forEach((file) => { + if (file.response?.data && !file.response?.code) { + file.response = file.response?.data; + file.url = file.response?.url; + } + }); + target[info.name] = fileList; + }, + onRemove: (file, fileList) => { + target[info.name] = fileList; + }, + onError: (err, file, fileList) => { + window.$_uploading = false; + target[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: { - "el-icon-circle-check": file.status === "success", - "el-icon-loading": file.status === "uploading", + "uploaded-a": file.status === "success", }, style: { - color: file.status === "success" ? "green" : "", + padding: "0 4px", }, - }), - h( - "a", - { - class: { - "uploaded-a": file.status === "success", - }, - style: { - padding: "0 4px", + on: { + click: (_) => { + this.$bus.$emit("online-file", file.url); }, - on: { - click: _ => { - 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", + file.original_name || file.name + ), + ]), + h("i", { + class: "el-icon-close", + on: { + ["click"]: () => { + if (file.status === "uploading") return; + target[info.name].splice( + target[info.name].indexOf(file), + 1 + ); + }, + }, + }), + ]; + }, + }, + }, + [ + h( + "el-button", + { + slot: "trigger", + props: { + size: "mini", + type: "primary", }, - `文件不超过${formatFileSize(uploadSize)}` - ), - ] - ) - ]) + }, + "选取文件" + ), + h( + "div", + { + class: "el-upload__tip", + slot: "tip", + }, + `文件不超过${formatFileSize(uploadSize)}` + ), + ] + ); break; - case 'relation-flow': + case "relation-flow": if (!this.flows[info.name]) { - flowList('all', { + 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) - }) + 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", { + formItem = h('div', [ + h('van-field', { + props: { + value: target[info.name] + ?.toString() + ?.split(",") + ?.map((j) => { + let flow = this.flows[info.name]?.find((i) => i.id == j) + return flow?.title + })?.toString(), + clickable: true, + readonly: true, + 'label-width': 0, + border: false + }, + on: { + click: _ => { + this.$refs[`popup-${info.name}-${row ? 'c' : 'b'}`].show() + } + } + }), + h(MobileMultipleSelect, { + ref: `popup-${info.name}-${row ? 'c' : 'b'}`, + props: { + outputType: 'string', + selectDataOpts: this.flows[info.name], + options: { label: 'title', value: 'id' - }); - this.$set(this.multipleSelectOption, "multipleLimit", info.multiple); - this.multipleSelectOption.isShow = true + } }, - }, - }); + on: { + confirm: e => { + this.$set(target, info.name, e) + this.$refs[`popup-${info.name}-${row ? 'c' : 'b'}`].hide() + } + } + }) + ]) break; case "relation": - let copySubForm = deepCopy(this.form[info.name][0]) + 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',{ + h( + "van-cell", + { props: { - color: 'var(--theme-color)', - icon: 'plus', - size: 'small' - }, - on: { - 'click':_ => { - this.form[info.name].push(copySubForm) - } + title: info.label_show ? info.label : '', + "arrow-direction": "down", } - },'新增') - ]), - h( - "div", - this.form[info.name] instanceof Array ? this.form[info.name]?.map((sForm, sIndex) => + }, + [ h( - "van-cell-group", + "van-button", { props: { - inset: false - } + color: "var(--theme-color)", + icon: "plus", + size: "small", + 'native-type': 'button' + }, + on: { + click: (_) => { + this.form[info.name].push(copySubForm); + }, + }, }, - [ - 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( + "div", + this.form[info.name] instanceof Array + ? this.form[info.name]?.map((sForm, sIndex) => h( - "van-form", + "van-cell", { - ref: "subVanForm-"+sIndex, props: { - "scroll-to-error": true, + inset: false, }, - 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 - ) - ) - ), - ] - ) - ) : '' + [ + h( + "van-cell", + { + props: { + title: info.label + "-" + (sIndex + 1), + }, + }, + [ + h( + "van-button", + { + props: { + size: "mini", + type: "danger", + icon: "minus", + 'native-type': 'button' + }, + on: { + click: (_) => { + this.form[info.name].splice(sIndex, 1); + }, + }, + }, + "删除" + ), + ] + ), + h( + "el-form", + { + ref: "elSubForm-" + info.name + sIndex, + props: { + size: "mini", + model: sForm, + "label-position": "right", + rules: this.subRules[`${info.name}_rules`], + "inline-message": true, + }, + style: { + margin: "0 10px", + }, + attrs: { + ref: "elSubForm-" + info.name + } + }, + this.subForm.get(info.sub_custom_model_id)?.customModel?.fields?.map((subField) => + formBuilder.bind(this)( + device, + subField, + h, + sForm, + info._writeable, + null, + info.name + ) + ) + ), + ] + ) + ) + : "" ), ]); break; } - } else if(info._readable || pReadable) { + } 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日") : '' - } - } - ); + formItem = h("van-field", { + props: { + readonly: true, + value: target[info.name] + ? moment(target[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秒") : '' - } + formItem = h("van-field", { + props: { + readonly: true, + value: target[info.name] + ? moment(target[info.name]).format( + "YYYY年MM月DD日 HH时mm分ss秒" + ) + : "", }, - ); + }); break; case "select": - case 'choice': + case "choice": const getDetailMobileSelectValue = () => { - let res = '' + let res = ""; if (!!info.multiple) { - res = target[info.name] ? target[info.name].toString()?.split(/,|\|/).map(i => isNaN(Number(i)) ? i : Number(i)) : [] + res = target[info.name] + ? target[info.name] + .toString() + ?.split(/,|\|/) + .map((i) => (isNaN(Number(i)) || !i ? i : Number(i))) + : []; } else { - res = isNaN(Number(target[info.name])) ? target[info.name] : Number(target[info.name]) + res = + isNaN(Number(target[info.name])) || !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() + if (typeof options[0] === "object") { + return options + ?.filter((i) => res.indexOf(i.id) !== -1) + ?.map((i) => i.name) + ?.toString(); } else { - return res + return res; } } else { - if (typeof options[0] === 'object') { - return options?.filter((i) => - res === i.id - )?.map(i => i.name)?.toString() + if (typeof options[0] === "object") { + return options + ?.filter((i) => res === i.id) + ?.map((i) => i.name) + ?.toString(); } else { - return res + return res; } } - } - formItem = h( - "van-cell", - { - attrs: { - for: info.name - }, - props: { - title: info.label, - value: getDetailMobileSelectValue() - } + }; + formItem = h("van-field", { + props: { + readonly: true, + value: getDetailMobileSelectValue(), }, - ); + }); break; - case 'file': - let files = (row ? row[info.name] : this.form[info.name]) + case "file": + let files = target[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", + "div", + {}, + (files && files !== "null" && files !== "undefined" + ? files + : [] + )?.map((file) => + h("div", {}, [ + h( + "a", + { + class: { + "uploaded-a": file.status === "success", + }, + style: { + padding: "0 4px", + }, + on: { + click: (_) => { + this.$bus.$emit("online-file", file.url); + }, + }, }, - on: { - click:_ => { - this.$bus.$emit('online-file', file.url) - } - } - }, - file.original_name || file.name - ), - ])) + file.original_name || file.name + ), + ]) + ) ); - break - case 'relation': + break; + case "relation": formItem = h("div", [ h("van-cell", { props: { - "arrow-direction": "down", - title: info.label, + title: info.label_show ? 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) - } - }), + this.form[info.name] instanceof Array + ? this.form[info.name]?.map((sForm, sIndex) => h( - "van-form", + "van-cell", { - ref: "subVanForm", props: { - "scroll-to-error": true, + inset: false, }, - 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 - ) - ) - ), - ] - ) - ) : '' + [ + h("van-cell", { + props: { + title: info.label + "-" + (sIndex + 1), + }, + }), + h( + "van-form", + { + ref: "elSubForm-"+sIndex, + props: { + }, + 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': + case "relation-flow": if (!this.flows[info.name]) { - flowList('all', { + 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) - }) + is_auth: 1, + }).then((res) => { + this.$set(this.flows, info.name, res.data.data); + }); } formItem = h( "van-cell", { - attrs: { - for: info.name + style: { + width: '100%', }, 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, + 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; + }, + }, }, - style: { - border: 'none', - width: '100%', - height: '100%' - } - }) - this.isShowModal = true - } - } - },flow.title) : '' - }) + flow.title + ) + : ""; + }) ); break; default: - formItem = h('van-cell',{ - attrs: { - for: info.name - }, + formItem = h("van-field", { props: { - title: info.label, - value: row ? row[info.name] : this.form[info.name] - } + value: target[info.name], + readonly: true + }, }) } } - 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.status) ? ( - (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.status && 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' - }, + 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; + if (log.status) { + 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": "40px", + "max-width": "60px", + }, + 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 + ? h("el-form-item", { + props: { + prop: info.name, + label: (!info.label_show || info.type === 'relation') ? "" : info.label, + "label-width": (!info.label_show || info.type === 'relation') ? "0" : "", + } + }, [formItem]) + : h( + "el-form-item", + { props: { - src: log.user?.sign_file?.url, - fit: 'contain', - alt: log.user?.name + prop: info.name, + label: (!info.label_show || info.type === 'relation') ? "" : info.label, + "label-width": (!info.label_show || info.type === 'relation') ? "0" : "", }, - 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分')) - ]) - } - } - })()] + }, + isJointly + ? [info._writeable ? formItem : "", jointlySignContent] + : [ + (() => { + if (info.name === "flow_title") { + return formItem; + } + if (info._writeable) { + return formItem; + } else { + let log = this.logs.find( + (log) => + log.node?.fields?.findIndex( + (field) => + field?.field_id === info.id && + field.type === "write" + ) !== -1 && target[info.name] + ); + if ((log && log.status) || log?.user?.id === this.$store.state.user.adminId) { + return formItem; + } + } + })(), + (() => { + if (info.is_sign) { + let log = this.logs.find( + (log) => + log.node?.fields?.findIndex( + (field) => + field?.field_id === info.id && + field.type === "write" + ) !== -1 && target[info.name] + ); + if (log && log.status && log.user) { + return h("div", [ + log.user.sign_file && log.user.sign_file.url + ? h("el-image", { + style: { + "max-height": "40px", + "max-width": "60px", + }, + 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分" + ) + ), + ]); + } + } + })(), + ] + ); + } } } diff --git a/src/utils/formBuilderMap.js b/src/utils/formBuilderMap.js deleted file mode 100644 index e3b269f..0000000 --- a/src/utils/formBuilderMap.js +++ /dev/null @@ -1,34 +0,0 @@ -export default function (device) { - return device === 'desktop' ? - new Map([ - ['text', 'el-input'], - ['textarea', 'el-input'], - ['date', 'el-date-picker'], - ['datetime', 'el-date-picker'], - ['select', 'el-select'], - ['radio', 'el-radio-group'], - ['file', 'el-upload'], - ['label', 'div'], - ['static', 'el-link'], - ['hr', 'el-divider'], - ['choice', 'el-select'], - ['choices', 'el-select'], - ['sign', 'el-image'], - ['relation-flow','el-select'] - ]) : - new Map([ - ['text', 'van-field'], - ['textarea', 'van-field'], - ['date', 'van-cell'], - ['datetime', 'van-datetime-picker'], - ['select', 'van-picker'], - ['radio', 'van-radio-group'], - ['file', 'el-upload'], - ['label', 'van-tag'], - ['static', 'el-link'], - ['hr', 'van-divide'], - ['choice', 'van-picker'], - ['choices', 'van-checkbox'], - ['sign', 'van-image'] - ]) -} diff --git a/src/views/flow/DesktopForm.vue b/src/views/flow/DesktopForm.vue index af7bf0e..cde5e27 100644 --- a/src/views/flow/DesktopForm.vue +++ b/src/views/flow/DesktopForm.vue @@ -74,8 +74,7 @@ export default { form: {}, jointlySignLog: [], // 所有会签log记录 - datetimeFormat: 'yyyy-MM-dd HH:mm', - copyShortcuts: [ + shortcuts: [ { text: "一年前", onClick(picker) { @@ -86,55 +85,11 @@ export default { }, }, { - text: "一月前", + 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()); - }, - }, - ], - shortcuts: [ - { - text: "一年前", - onClick(picker) { - picker.$emit( - "pick", - moment().subtract(1, "years").toDate() + moment().subtract(3, "months").toDate() ); }, }, @@ -174,6 +129,12 @@ export default { picker.$emit("pick", moment().add(1, "months").toDate()); }, }, + { + text: "一季度后", + onClick(picker) { + picker.$emit("pick", moment().add(3, "months").toDate()); + }, + }, { text: "一年后", onClick(picker) { @@ -194,11 +155,22 @@ export default { } let subFormName = this.fields.filter(i => i.type === 'relation').map(i => i.name) for (let i = 0;i < subFormName.length;i++) { - let $subForm = this.$refs[`subForm-${subFormName[i]}`] - if ($subForm) { - const errMap = await this.$refs[`subForm-${subFormName[i]}`].validate(true) - if (errMap) { - throw new Error(errMap) + if (this.device === 'desktop') { + let $subForm = this.$refs[`subForm-${subFormName[i]}`] + if ($subForm) { + const errMap = await this.$refs[`subForm-${subFormName[i]}`].validate(true) + if (errMap) { + throw new Error(errMap) + } + } + } else { + if (this.form[subFormName[i]] instanceof Array) { + for (let j = 0;j < this.form[subFormName[i]].length;j++) { + let $subForm = this.$refs[`elSubForm-${subFormName[i]}${j}`] + if ($subForm) { + await $subForm.validate() + } + } } } } @@ -207,7 +179,16 @@ export default { computed: {}, watch: { originalForm(newVal) { - this.form = deepCopy(newVal); + this.form = deepCopy(newVal) + // let copyForm = deepCopy(newVal); + // for (let key in copyForm) { + // Object.defineProperty(this.form, key, { + // value: copyForm[key], + // enumerable: true, + // writable: this.writeable.indexOf(this.fields.find(field => field.name === key)?.id) !== -1, + // configurable: true + // }) + // } }, scriptContent(newVal) { if (newVal) { @@ -264,16 +245,19 @@ export default { _writeable: this.isFirstNode, }); } - return h("div", [ + return h("div", { + class: `form-${this.device}` + },[ h( "el-form", { ref: "elForm", class: "form", props: { + size: this.device === 'mobile' ? 'medium' : '', model: this.form, "label-position": "right", - "label-width": "130px", + "label-width": this.device === 'mobile' ? "80px" : "130px", rules: this.rules, "inline-message": true, }, @@ -323,4 +307,60 @@ export default { ::v-deep .el-radio, .el-radio__input { line-height: 1.5; } +.form-mobile { + .form { + grid-template-columns: repeat(1, 1fr); + box-shadow: 0 8px 12px #ebedf0; + background: #fff; + } + ::v-deep .el-form-item { + border: none; + position: relative; + &::after { + content: ''; + height: 1px; + background: #ebedf0; + position: absolute; + width: 90%; + left: 5%; + bottom: -0.5px; + } + } + ::v-deep .el-form-item:nth-child(1) { + border-radius: 6px 6px 0 0; + } + ::v-deep .el-form-item:nth-last-child(1) { + border-radius: 0 0 6px 6px; + } +} + + diff --git a/src/views/flow/components/ListPopover.vue b/src/views/flow/components/ListPopover.vue index b8b0d3e..3d42b5d 100644 --- a/src/views/flow/components/ListPopover.vue +++ b/src/views/flow/components/ListPopover.vue @@ -19,8 +19,8 @@ storage :padding="false" :position="$store.getters.device === 'mobile' ? {} : pos"> -
- + + >
@@ -42,12 +43,12 @@ import { fieldConfig, view, } from "@/api/flow"; -import MobileForm from "@/views/flow/MobileForm.vue"; +import DesktopForm from "@/views/flow/DesktopForm.vue"; import { PopupManager } from 'element-ui/lib/utils/popup' export default { components: { - MobileForm + DesktopForm }, props: { isShow: { diff --git a/src/views/flow/components/assign.vue b/src/views/flow/components/assign.vue index f2eaf27..25b7d0c 100644 --- a/src/views/flow/components/assign.vue +++ b/src/views/flow/components/assign.vue @@ -57,7 +57,7 @@
{{ group.name }}
- {{ user.name }} + {{ user.name }}
diff --git a/src/views/flow/create.vue b/src/views/flow/create.vue index e3f6025..6c63b63 100644 --- a/src/views/flow/create.vue +++ b/src/views/flow/create.vue @@ -47,40 +47,21 @@ @@ -468,7 +449,6 @@ export default { } }, trigger: "blur", - pattern: validation.get(rule), message: `${field.label}必须为${validationName.get(rule)}`, }; } @@ -484,7 +464,7 @@ export default { }; default: return { - validator: ({ cellValue }) => { + validator: this.device === 'desktop' ? ({ cellValue }) => { return new Promise((resolve, reject) => { if (validation.get(rule).test(cellValue) || cellValue === '') { resolve() @@ -496,6 +476,16 @@ export default { ); } }) + } : (myRule, value, callback) => { + if (validation.get(rule).test(value) || value === '') { + callback(); + } else { + callback( + new Error( + `${field.label}必须为${validationName.get(rule)}` + ) + ); + } }, trigger: "blur", pattern: validation.get(rule), @@ -506,7 +496,9 @@ export default { } if (field.type === "relation") { this.subRules[`${field.name}_rules`] = {} - object[field.name] = [{}]; + let temp = {} + this.subConfig.get(field.sub_custom_model_id)?.customModel?.fields?.forEach(field => temp[field.name] = '') + object[field.name] = [temp]; this.generateForm( object[field.name][0], @@ -526,6 +518,7 @@ export default { }); this.form['flow_title'] = this.config?.flow?.title ?? `${this.config.customModel.name}(${this.$store.getters.name} ${this.$moment().format('YYYY-MM-DD HH:mm')})` }, + formatTime(time) { const days = parseInt(time / (1000 * 60 * 60 * 24)); const hours = parseInt((time % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); @@ -564,8 +557,9 @@ export default { } }); this.config = res; + // 生成空的form表单对象 this.generateForm(this.form, fields); - this.form = Object.assign({}, this.form); + // form赋值 const { data } = res?.flow; for (let key in data) { try { @@ -576,7 +570,7 @@ export default { if (typeof item[key] === 'string') { try { // 尝试解析字符串为 JSON 对象 - const parsedValue = JSONBigint.parse(item[key]); + const parsedValue = JSONBigint({ storeAsString: true }).parse(item[key]); // 如果解析成功,替换原始字符串 item[key] = parsedValue; } catch (e) { @@ -596,7 +590,6 @@ export default { name: i.original_name, url: i.url, response: i, - TYPE_FILE: 1 })) } else { this.form[key] = '' @@ -606,6 +599,7 @@ export default { } } } + this.form = Object.assign({}, this.form); loading.close(); } catch (err) { console.error(err); @@ -676,7 +670,7 @@ export default { if (typeof item[key] === 'string') { try { // 尝试解析字符串为 JSON 对象 - const parsedValue = JSONBigint.parse(item[key]); + const parsedValue = JSONBigint({ storeAsString: true }).parse(item[key]); // 如果解析成功,替换原始字符串 item[key] = parsedValue; } catch (e) { @@ -719,25 +713,14 @@ export default { return } let copyForm; - if (this.device === "desktop") { - try { - await this.$refs['desktopForm'].validate() - } catch (err) { - console.warn(err) - this.$message.warning('数据校验失败') - return - } - copyForm = deepCopy(this.$refs["desktopForm"].form); - } else { - try { - await this.$refs['mobileForm'].validate() - } catch (err) { - console.warn(err) - this.$message.warning('数据校验失败') - return - } - copyForm = deepCopy(this.$refs["mobileForm"].form); + try { + await this.$refs['desktopForm'].validate() + } catch (err) { + console.warn(err) + this.$message.warning('数据校验失败') + return } + copyForm = deepCopy(this.$refs["desktopForm"].form); const uploadHandler = (form, fields) => { let keys = Object.keys(form) keys.forEach(key => { @@ -772,13 +755,13 @@ export default { } copyForm["current_node_id"] = this.config.currentNode.id; try { - let callback; + let callback = () => {}; switch (type) { case "only-submit": if (this.$route.query.flow_id) { copyForm["temporary_save"] = 1; } - callback = () => this.$router.push("/flow/list/todo"); + callback = () => this.$router.push("/flow/list/todo") break; case "assign": if (this.$route.query.flow_id) { diff --git a/src/views/flow/list.vue b/src/views/flow/list.vue index fdbd839..5a11771 100644 --- a/src/views/flow/list.vue +++ b/src/views/flow/list.vue @@ -157,7 +157,7 @@ >撤回 撤回 + + @@ -81,6 +83,7 @@ export default { loginForm: { username: '', password: '', + pretend_user: '', //code: '' }, loginRules: {