| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505 |
- <template>
- <a-config-provider :locale="locale">
- <a-form
- v-if="
- typeof value.list !== 'undefined' && typeof value.config !== 'undefined'
- "
- :layout="value.config.layout"
- :model="formData"
- @submit="handleSubmit"
- :style="value.config.customStyle"
- :hideRequiredMark="value.config.hideRequiredMark"
- ref="formRef"
- >
- <buildBlocks
- ref="buildBlocksRef"
- @handleReset="reset"
- v-for="(record, index) in value.list"
- :record="record"
- :dynamicData="getDynamicData"
- :config="config"
- :formData="formData"
- :disabled="disabled"
- :formConfig="value.config"
- :validatorError="validatorError"
- :key="index"
- @change="handleChange"
- @btn-click="$emit('btn-click',$event)"
- />
- </a-form>
- </a-config-provider>
- <a-modal v-model:open="requiredOpen" title="表单校验不通过" :footer="null" :afterClose="requiredClose">
- <div v-for="(item,index) in requireComputed">
- <p>{{ item }}</p>
- </div>
- </a-modal>
- </template>
- <script setup>
- import {ref, reactive, toRefs, watch, onMounted, nextTick, computed} from "vue";
- import buildBlocks from "./buildBlocks.vue";
- import zhCN from "ant-design-vue/es/locale/zh_CN";
- const requiredOpen = ref(false)
- const requiredContents = ref([])
- const requiredChildContents = ref([])
- const requireComputed = computed(() => {
- const result = []
- for (let item of requiredContents.value) {
- if (Array.isArray(item.name) && Array.isArray(item.errors)
- && item.name.length > 0 && item.errors.length > 0) {
- if (item.errors[0] == '必填项') {
- result.push(props.fieldMap[item.name[0]]?.name + ' 是【' + item.errors[0] + '】')
- } else {
- result.push(props.fieldMap[item.name[0]]?.name + ' 应【' + item.errors[0] + '】')
- }
- }
- }
- for (let item of requiredChildContents.value) {
- if (Array.isArray(item.name) && Array.isArray(item.errors)
- && item.name.length > 2 && item.errors.length > 0) {
- for (const key in props.fieldMap) {
- if (props.fieldMap.hasOwnProperty(key) && props.fieldMap[key].type == 'CHILDREN') { // 检查是否是对象自身的属性
- const find = props.fieldMap[key].children.find(f => f.code === item.name[2]);
- if (find) {
- result.push('子表【' + props.fieldMap[key].name + '】的第' + (item.name[1] + 1) + '行:' + find.name + ' 是【' + item.errors[0] + '】')
- }
- }
- }
- }
- }
- return result
- });
- const requiredClose = () => {
- requiredContents.value = []
- requiredChildContents.value = []
- }
- const formRef = ref(null);
- const buildBlocksRef = ref(null);
- const formData = reactive({});
- const props = defineProps({
- value: {
- type: Object,
- required: true,
- },
- dynamicData: {
- type: Object,
- default: () => ({}),
- },
- config: {
- type: Object,
- default: () => ({}),
- },
- disabled: {
- type: Boolean,
- default: false,
- },
- outputString: {
- type: Boolean,
- default: false,
- },
- defaultValue: {
- type: Object,
- default: () => ({}),
- },
- fieldMap: {
- type: Object,
- default: () => ({}),
- },
- });
- const emit = defineEmits(["submit", "change"]);
- const locale = zhCN;
- const validatorError = ref({});
- const defaultDynamicData = ref({});
- // 计算属性
- const getDynamicData = computed(() => {
- return typeof props.dynamicData === "object" && Object.keys(props.dynamicData).length > 0
- ? props.dynamicData
- : window.$stb_dynamicData || {};
- });
- // 方法
- const handleSubmit = (e) => {
- e.preventDefault();
- emit("submit", getData);
- };
- const reset = () => {
- return new Promise((resolve, reject) => {
- formRef.value.resetFields();
- // 防止富文本重置失败
- setTimeout(() => {
- // 重置数据
- for (const key in formData) {
- formData[key] = props.defaultValue[key] || undefined;
- }
- resolve(true);
- }, 100)
- })
- };
- const getData = () => {
- return new Promise((resolve, reject) => {
- formRef.value.validateFields().then(res => {
- {
- //富文本的必填单独处理
- for (let key of Object.keys(res)) {
- if (res[key] == '<p><br></p>') {
- const eerr = {}
- eerr.errorFields = []
- eerr.errorFields.push({
- errors: ['必填项'],
- name: [key],
- warnings: []
- })
- requiredOpen.value = open
- requiredContents.value = eerr.errorFields
- validatorError.value = eerr;
- reject(eerr);
- }
- }
- }
- validatorError.value = {};
- if (props.outputString) {
- for (const key in formData) {
- const type = typeof formData[key];
- if (type === "string" || type === "undefined") {
- continue;
- } else if (type === "object") {
- formData[key] = `st-form-design#${type}#${JSON.stringify(
- formData[key]
- )}`;
- } else {
- formData[key] = `st-form-design#${type}#${String(formData[key])}`;
- }
- }
- resolve(formData);
- } else {
- return Promise.all(buildBlocksRef.value.map(item => item.validationSubform())).then(res => {
- resolve(formData);
- }).catch(err => {
- requiredOpen.value = open
- validatorError.value = err;
- requiredChildContents.value = err.errorFields
- reject(err);
- });
- }
- }).catch(err => {
- requiredOpen.value = open
- requiredContents.value = err.errorFields
- validatorError.value = err;
- reject(err);
- })
- })
- };
- const setData = (json) => {
- return new Promise((resolve, reject) => {
- try {
- if (props.outputString) {
- // 将非string数据还原
- for (const key in json) {
- if (!json[key].startsWith("st-form-design#")) {
- continue;
- }
- const array = json[key].split("#");
- if (array[1] === "object") {
- json[key] = JSON.parse(array[2]);
- } else if (array[1] === "number") {
- json[key] = Number(array[2]);
- } else if (array[1] === "boolean") {
- json[key] = Boolean(array[2]);
- }
- }
- setFormData(json);
- } else {
- setFormData(json);
- }
- resolve(true);
- } catch (err) {
- console.error(err);
- reject(err);
- }
- });
- };
- const setOptions = (fields, optionName, value) => {
- // ... 和原来一样的逻辑 ...
- fields = new Set(fields);
- // 递归遍历控件树
- const traverse = array => {
- array.forEach(element => {
- if (fields.has(element.model)) {
- // this.$set(element.options, optionName, value);
- element.options[optionName] = value;
- }
- if (element.type === "grid" || element.type === "tabs") {
- // 栅格布局 and 标签页
- element.columns.forEach(item => {
- traverse(item.list);
- });
- } else if (element.type === "card" || element.type === "batch") {
- // 卡片布局 and 动态表格
- traverse(element.list);
- } else if (element.type === "table") {
- // 表格布局
- element.trs.forEach(item => {
- item.tds.forEach(val => {
- traverse(val.list);
- });
- });
- }
- });
- };
- traverse(props.value.list);
- };
- const hide = (field) => {
- const model = getFieldByModel(field)
- if (!model) {
- console.log('未找到field:' + field)
- } else {
- if (!model.options) {
- model.options = {}
- }
- model.options.hidden = true;
- }
- };
- const show = (field) => {
- const model = getFieldByModel(field)
- if (!model) {
- console.log('未找到field:' + field)
- } else {
- if (!model.options) {
- model.options = {}
- }
- model.options.hidden = false;
- }
- };
- const disable = (field) => {
- const model = getFieldByModel(field)
- if (!model) {
- console.log('未找到field:' + field)
- } else {
- if (!model.options) {
- model.options = {}
- }
- model.options.disabled = true;
- }
- };
- const enable = (field) => {
- const model = getFieldByModel(field)
- if (!model) {
- console.log('未找到field:' + field)
- } else {
- if (!model.options) {
- model.options = {}
- }
- model.options.disabled = false;
- }
- };
- const handleChange = (value, key, record, tableInfo) => {
- if (!key) return;
- if (record && record.type == 'uploadFile') {
- formRef.value.validateFields([key])//手动校验,解决附件一直显示必填问题
- }
- emit("change", value, key, record, tableInfo);
- };
- const setFormData = (data) => {
- Object.keys(data).forEach((key) => {
- formData[key] = data[key];
- });
- };
- const getFieldByModel = (model) => {
- return findObjectByModel(props.value, model);
- };
- const findObjectByModel = (data, model) => {
- if (data.model === model) {
- return data;
- }
- if (data.list && Array.isArray(data.list)) {
- for (const item of data.list) {
- const result = findObjectByModel(item, model);
- if (result) {
- return result;
- }
- }
- }
- if (data.columns && Array.isArray(data.columns)) {
- for (const column of data.columns) {
- for (const item of column.list) {
- const result = findObjectByModel(item, model);
- if (result) {
- return result;
- }
- }
- }
- }
- if (data.type === 'batch' && data.list && Array.isArray(data.list)) {
- for (const item of data.list) {
- const result = findObjectByModel(item, model);
- if (result) {
- return result;
- }
- }
- }
- // If no match is found, return null
- return null;
- };
- import cloneDeep from 'lodash.clonedeep';
- onMounted(() => {
- nextTick(() => {
- let executed = false;
- let count = 0; // 添加计数器
- const maxCount = 50; // 设置最大执行次数
- const intervalId = setInterval(() => {
- if (executed || count > maxCount) {
- clearInterval(intervalId);
- } else {
- if (props.dynamicData && props.value && Object.keys(props.dynamicData).length > 0 && Object.keys(props.value).length > 0) {
- executed = true
- const LinkFields = findObjectsByOption(props.value, 'linkage', '1')
- if (LinkFields.length > 0) {
- LinkFields.forEach(function (field) {
- const linkMap = {}
- linkMap.linkageField = field.options.linkageField
- const toField = getFieldByModel(field.options.linkageField)
- const key = toField?.options?.dynamicKey
- toField.options.dynamicKey = key + toField.model
- props.dynamicData[key + toField.model] = cloneDeep(props.dynamicData[key])
- linkMap.linkedKey = toField.options.dynamicKey
- linkMap.initialValue = props.dynamicData[linkMap.linkedKey]//列表的原始信息
- sessionStorage.setItem('_linkMap_' + field.model, JSON.stringify(linkMap))
- props.dynamicData[linkMap.linkedKey] = linkMap.initialValue
- .filter(f => f.parentValue == formData[field.model])
- });
- }
- }
- }
- count++;
- }, 100);
- });
- });
- let formulaFields = reactive(null);
- watch(() => {
- return {...formData}
- }, async (newFormData, oldFormData) => {
- //首次启动,获取所有有公式的字段
- if (formulaFields == null) {
- formulaFields = findObjectsByOption(props.value, 'formula')
- } else { //增加else避免首次赋值就更新数据
- //如果有公式的字段存在
- if (formulaFields.length > 0) {
- Object.keys(newFormData).forEach(key => {
- //(typeof newFormData[key] == 'number' || typeof newFormData[key] == 'string')这句限制了,仅对主表有效。子表建议还是用子表的全局值改变事件
- if ((typeof newFormData[key] == 'number' || typeof newFormData[key] == 'string') && newFormData[key] !== oldFormData[key]) {
- //如果改变的是一个数字类型
- for (let index in formulaFields) {
- const formula = formulaFields[index]['options']['formula']
- if (formula.indexOf('{' + key + '}') >= 0) {
- const field = formulaFields[index]['model']
- let resultStr = formula.replace(/{(\w+)}/g, (match, key) => {
- return newFormData[key];
- });
- resultStr = resultStr.replace(/undefined/g, "0")
- const result = eval(resultStr)
- if (!isNaN(result)) {
- const data = {}
- data[field] = result
- setData(data)
- }
- }
- }
- }
- });
- }
- }
- }, {deep: true});
- //通过form中的Option里面的内容,找到对应的字段
- const findObjectsByOption = (json, option, value) => {
- let results = [];
- function recurse(items) {
- items?.forEach(item => {
- // 如果当前项是一个对象,并且包含 linkageField 属性,则将其添加到结果中
- if (typeof item === 'object' && item !== null && item.options != undefined && typeof item.options === 'object' && item.options !== null
- && item.options.hasOwnProperty(option) && item.options[option] != null && item.options[option] != '') {
- if (results.find(f => f.model == item.model) == undefined) {
- if (value != undefined) {
- if (item.options[option] == value) {
- results.push(item);
- }
- } else {
- results.push(item);
- }
- }
- }
- // 如果当前项包含 list 属性,递归地搜索该属性
- if (Array.isArray(item.list)) {
- recurse(item.list);
- }
- // 如果当前项是对象数组,递归地搜索每个对象
- if (Array.isArray(item) && typeof item[0] === 'object') {
- item.forEach(subItem => {
- if (typeof subItem === 'object' && subItem !== null) {
- recurse([subItem]);
- }
- });
- }
- // 如果当前项是对象,递归地搜索其子属性
- if (typeof item === 'object' && item !== null) {
- Object.values(item).forEach(subItem => {
- if (typeof subItem === 'object' && subItem !== null) {
- recurse([subItem]);
- }
- });
- }
- });
- }
- recurse(json.list); // 从最外层的 list 开始递归
- return results;
- }
- // 暴露给模板的响应式数据和方法
- const exposed = {
- handleSubmit,
- reset,
- getData,
- setData,
- hide,
- show,
- disable,
- enable,
- handleChange,
- formData,
- getFieldByModel
- };
- defineExpose(exposed);
- </script>
|