<template>
|
<el-form
|
style="display: inline-block; width: 100%"
|
:inline="true"
|
ref="volform"
|
@submit.prevent
|
:model="formFields"
|
:label-width="labelWidth"
|
:rules="rules"
|
>
|
<template v-for="(row, findex) in formRules" :key="findex">
|
<div class="vol-form-item">
|
<el-form-item
|
:label="item.title ? item.title + ':' : ''"
|
v-show="!item.hidden"
|
v-for="(item, index) in row"
|
:prop="item.field"
|
:key="item.field + index"
|
:style="{ width: getColWidth(item) + '%' }"
|
>
|
<!-- render -->
|
<form-expand
|
v-if="item.render && typeof item.render == 'function'"
|
:render="item.render"
|
:par="12"
|
></form-expand>
|
<!-- 2021.10.17增加表单实时方法计算 -->
|
<span
|
v-else-if="
|
item.readonly && typeof formFields[item.field] == 'function'
|
"
|
>{{ formFields[item.field]() }}</span
|
>
|
<!-- 只读图片或文件 -->
|
<div v-else-if="isReadonlyImgFile(item, formFields)">
|
<div v-if="item.type == 'img'" class="form-imgs">
|
<div
|
class="img-item"
|
v-for="(img, imgIndex) in formFields[item.field]"
|
:key="imgIndex"
|
>
|
<img
|
:src="getSrc(img.path)"
|
:onerror="errorImg"
|
@click="previewImg(img.path)"
|
/>
|
</div>
|
</div>
|
<div
|
v-else
|
class="form-file-list"
|
v-for="(file, fileIndex) in formFields[item.field]"
|
:key="fileIndex"
|
>
|
<a @click="dowloadFile(formFields[item.field][fileIndex])">{{
|
file.name
|
}}</a>
|
</div>
|
</div>
|
|
<div v-else :class="{ 'form-item-extra': item.extra }">
|
<!-- 只读属性 -->
|
<label
|
:style="item.inputStyle"
|
v-if="item.type == 'label'"
|
class="readonly-input"
|
>{{ getText(formFields, item) }}</label
|
>
|
<!-- 20223.05.13集成el-tree-select -->
|
<!-- :filter-method="(value)=>{filterMethod(value,item.data)}" -->
|
<!-- :filterable="true" -->
|
<el-tree-select
|
style="width: 100%"
|
v-else-if="item.type == 'treeSelect'"
|
v-model="formFields[item.field]"
|
:data="item.data"
|
:multiple="item.multiple"
|
:render-after-expand="false"
|
:show-checkbox="false"
|
:check-strictly="true"
|
check-on-click-node
|
node-key="key"
|
:props="{ label: 'label' }"
|
>
|
<template #default="{data,node }">
|
<!-- <el-checkbox v-model="node.checked"></el-checkbox> -->
|
<!-- {{getNode(node, data)}} -->
|
<!-- {{node.checked}} -->
|
<!-- 这里还有点问题,后面处理 -->
|
{{ data.label}}</template
|
>
|
</el-tree-select>
|
<template
|
v-else-if="['select', 'selectList'].indexOf(item.type) != -1"
|
>
|
<el-select-v2
|
:disabled="item.readonly || item.disabled"
|
v-show="!item.hidden"
|
style="width: 100%"
|
:size="size"
|
v-if="item.data.length > select2Count"
|
v-model="formFields[item.field]"
|
filterable
|
:multiple="item.type == 'select' ? false : true"
|
:placeholder="item.placeholder ? item.placeholder : item.title"
|
:allow-create="item.autocomplete"
|
:options="item.data"
|
@change="
|
(val) => {
|
item.onChange(val, item.data);
|
}
|
"
|
clearable
|
>
|
<template #default="{ item }">
|
{{ item.label }}
|
</template>
|
</el-select-v2>
|
<el-select
|
:disabled="item.readonly || item.disabled"
|
v-show="!item.hidden"
|
style="width: 100%"
|
:size="size"
|
v-else-if="item.remote || item.url"
|
v-model="formFields[item.field]"
|
filterable
|
:multiple="item.type == 'select' ? false : true"
|
:placeholder="item.placeholder ? item.placeholder : item.title"
|
clearable
|
:remote-method="
|
(val) => {
|
remoteSearch(item, formFields, val);
|
}
|
"
|
>
|
<el-option
|
v-for="item in item.data"
|
:key="item.key"
|
:label="item.value"
|
:value="item.key"
|
>
|
</el-option>
|
</el-select>
|
<el-select
|
:disabled="item.readonly || item.disabled"
|
v-show="!item.hidden"
|
style="width: 100%"
|
:size="size"
|
v-else
|
v-model="formFields[item.field]"
|
filterable
|
:multiple="item.type == 'select' ? false : true"
|
:placeholder="item.placeholder ? item.placeholder : item.title"
|
:allow-create="item.autocomplete"
|
@change="
|
(val) => {
|
item.onChange(val, item.data);
|
}
|
"
|
clearable
|
>
|
<el-option
|
v-show="!item.hidden"
|
:disabled="item.disabled"
|
v-for="item in item.data"
|
:key="item.key"
|
:label="item.value"
|
:value="item.key"
|
>
|
</el-option>
|
</el-select>
|
</template>
|
|
<el-switch
|
v-show="!item.hidden"
|
v-else-if="item.type == 'switch'"
|
v-model="formFields[item.field]"
|
:disabled="item.readonly || item.disabled"
|
active-color="#0f84ff"
|
@change="item.onChange"
|
inactive-color="rgb(194 194 194)"
|
:active-value="
|
typeof formFields[item.field] == 'boolean'
|
? true
|
: typeof formFields[item.field] == 'string'
|
? '1'
|
: 1
|
"
|
:inactive-value="
|
typeof formFields[item.field] == 'boolean'
|
? false
|
: typeof formFields[item.field] == 'string'
|
? '0'
|
: 0
|
"
|
>
|
</el-switch>
|
|
<el-radio-group
|
:disabled="item.readonly || item.disabled"
|
v-show="!item.hidden"
|
v-model="formFields[item.field]"
|
v-else-if="item.type == 'radio'"
|
@change="item.onChange"
|
>
|
<el-radio
|
v-for="kv in item.data"
|
:disabled="item.readonly || item.disabled"
|
:key="kv.key"
|
:label="kv.key"
|
>{{ kv.value }}</el-radio
|
>
|
</el-radio-group>
|
|
<el-checkbox-group
|
:disabled="item.readonly || item.disabled"
|
v-show="!item.hidden"
|
v-model="formFields[item.field]"
|
v-else-if="item.type == 'checkbox'"
|
@change="item.onChange"
|
>
|
<el-checkbox
|
v-for="kv in item.data"
|
:key="kv.key"
|
:disabled="item.readonly || item.disabled"
|
:label="kv.key"
|
>{{ kv.value }}</el-checkbox
|
>
|
</el-checkbox-group>
|
<div
|
class="v-date-range"
|
style="display: flex"
|
v-else-if="
|
['date', 'datetime'].indexOf(item.type) != -1 && item.range
|
"
|
>
|
<el-date-picker
|
:size="size"
|
:disabled="item.readonly || item.disabled"
|
style="flex: 1; width: auto"
|
v-model="formFields[item.field][0]"
|
:type="item.type == 'date' ? 'date' : 'datetime'"
|
:disabledDate="(val) => getDateOptions(val, item)"
|
placeholder="开始时间"
|
@change="
|
(val) => {
|
dateRangeChange(val, item);
|
}
|
"
|
:value-format="getDateFormat(item)"
|
>
|
</el-date-picker>
|
<span style="margin: 0px 5px; font-size: 13px; color: #6f6b6b"
|
>至</span
|
>
|
<el-date-picker
|
:size="size"
|
:disabled="item.readonly || item.disabled"
|
style="flex: 1; width: auto"
|
v-model="formFields[item.field][1]"
|
placeholder="结束时间"
|
:type="item.type == 'date' ? 'date' : 'datetime'"
|
:disabledDate="(val) => getDateOptions(val, item)"
|
@change="
|
(val) => {
|
dateRangeChange(val, item);
|
}
|
"
|
:value-format="getDateFormat(item)"
|
>
|
</el-date-picker>
|
</div>
|
<!-- v-show不添加根节点就会报错没有根点节 -->
|
<div
|
v-show="!item.hidden"
|
style="width: 100%"
|
v-else-if="['date', 'datetime', 'month'].indexOf(item.type) != -1"
|
>
|
<el-date-picker
|
:size="size"
|
clearable
|
:disabled="item.readonly || item.disabled"
|
style="width: 100%"
|
v-model="formFields[item.field]"
|
@change="item.onChange"
|
:type="item.type"
|
:placeholder="
|
item.placeholder ? item.placeholder : '请选择' + item.title
|
"
|
:disabledDate="(val) => getDateOptions(val, item)"
|
:value-format="getDateFormat(item)"
|
>
|
</el-date-picker>
|
</div>
|
|
<el-time-picker
|
:size="size"
|
v-else-if="item.type == 'time'"
|
v-model="formFields[item.field]"
|
:disabled="item.readonly || item.disabled"
|
placeholder="请选择时间"
|
:value-format="item.format || 'HH:mm:ss'"
|
:format="item.format"
|
style="width: 100%"
|
>
|
</el-time-picker>
|
|
<el-scrollbar
|
style="border: 1px solid #c7d8db; border-radius: 5px"
|
:height="item.height || 150"
|
v-else-if="
|
item.type == 'editor' && (item.readonly || item.disabled)
|
"
|
>
|
<div ref="editor" v-html="formFields[item.field]"></div>
|
</el-scrollbar>
|
|
<vol-wang-editor
|
ref="editor"
|
v-else-if="item.type == 'editor'"
|
:url="item.url || editor.uploadImgUrl"
|
:upload="item.upload || editor.upload"
|
v-model="formFields[item.field]"
|
:height="item.height || 350"
|
></vol-wang-editor>
|
|
<vol-upload
|
v-show="!item.hidden"
|
v-else-if="isFile(item, formFields)"
|
:desc="item.desc"
|
:multiple="item.multiple"
|
:max-file="item.maxFile"
|
:max-size="item.maxSize"
|
:autoUpload="item.autoUpload"
|
:fileInfo="formFields[item.field]"
|
:url="item.url"
|
:img="item.type == 'img' || item.columnType == 'img'"
|
:excel="item.type == 'excel'"
|
:fileTypes="item.fileTypes ? item.fileTypes : []"
|
:upload-before="item.uploadBefore"
|
:upload-after="item.uploadAfter"
|
:append="item.multiple"
|
:on-change="
|
(files) => {
|
return fileOnChange(files, item);
|
}
|
"
|
:file-click="item.fileClick"
|
:remove-before="item.removeBefore"
|
:downLoad="item.downLoad ? true : false"
|
></vol-upload>
|
<el-cascader
|
:size="size"
|
clearable
|
style="width: 100%; margin-top: -3px"
|
v-model="formFields[item.field]"
|
:disabled="item.readonly || item.disabled"
|
v-else-if="item.type == 'cascader'"
|
:options="item.data"
|
:props="{
|
checkStrictly: item.changeOnSelect || item.checkStrictly
|
}"
|
@change="item.onChange"
|
>
|
</el-cascader>
|
<el-rate
|
v-else-if="item.type == 'rate'"
|
@change="
|
(val) => {
|
item.onChange && item.onChange(val);
|
}
|
"
|
:max="item.max"
|
v-model="formFields[item.field]"
|
/>
|
<div
|
style="display: flex"
|
v-else-if="item.type == 'range' || item.range"
|
>
|
<el-input
|
:size="size"
|
:disabled="item.readonly || item.disabled"
|
style="flex: 1"
|
v-model="formFields[item.field][0]"
|
clearable
|
/>
|
<span style="margin: 0 5px">-</span>
|
<el-input
|
:size="size"
|
:disabled="item.readonly || item.disabled"
|
style="flex: 1"
|
v-model="formFields[item.field][1]"
|
clearable
|
/>
|
</div>
|
<el-input
|
:size="size"
|
clearable
|
:ref="item.field"
|
:input-style="item.inputStyle"
|
:disabled="item.readonly || item.disabled"
|
v-else-if="item.type == 'textarea'"
|
v-model="formFields[item.field]"
|
type="textarea"
|
:autosize="{
|
minRows: item.minRows || 2,
|
maxRows: item.maxRows || 10
|
}"
|
:placeholder="item.placeholder ? item.placeholder : item.title"
|
/>
|
<el-input-number
|
:size="size"
|
style="width: 100%"
|
:ref="item.field"
|
:input-style="item.inputStyle"
|
v-else-if="item.type == 'number'"
|
v-model="formFields[item.field]"
|
:min="item.min"
|
:disabled="item.readonly || item.disabled"
|
:max="item.max"
|
controls-position="right"
|
/>
|
<el-input
|
:size="size"
|
clearable
|
:input-style="item.inputStyle"
|
v-else-if="item.type == 'password'"
|
type="password"
|
v-model="formFields[item.field]"
|
:disabled="item.readonly || item.disabled"
|
v-show="!item.hidden"
|
:placeholder="item.placeholder ? item.placeholder : item.title"
|
/>
|
<!-- 2021.11.18修复el-input没有默认enter事件时回车异常 -->
|
<el-input
|
:size="size"
|
clearable
|
:ref="item.field"
|
:input-style="item.inputStyle"
|
v-else-if="item.onKeyPress"
|
:placeholder="item.placeholder ? item.placeholder : item.title"
|
:disabled="item.readonly || item.disabled"
|
v-show="!item.hidden"
|
v-model="formFields[item.field]"
|
@keypress="
|
($event) => {
|
onKeyPress($event, item);
|
}
|
"
|
@change="item.onKeyPress"
|
@keyup.enter="item.onKeyPress"
|
></el-input>
|
<el-input
|
:size="size"
|
clearable
|
v-else
|
:ref="item.field"
|
:input-style="item.inputStyle"
|
:placeholder="item.placeholder ? item.placeholder : item.title"
|
:disabled="item.readonly || item.disabled"
|
v-show="!item.hidden"
|
v-model="formFields[item.field]"
|
></el-input>
|
|
<div class="form-extra" v-if="item.extra">
|
<form-expand
|
v-if="item.extra.render"
|
:render="item.extra.render"
|
></form-expand>
|
<a
|
v-else-if="item.extra.click"
|
:style="item.extra.style"
|
@click="item.extra.click(item, formFields[item.field])"
|
>
|
<i v-if="item.extra.icon" :class="item.extra.icon" />
|
{{ item.extra.text }}
|
</a>
|
<a v-else :style="item.extra.style">
|
<i v-if="item.extra.icon" :class="item.extra.icon" />
|
{{ item.extra.text }}
|
</a>
|
</div>
|
</div>
|
</el-form-item>
|
</div>
|
</template>
|
<slot></slot>
|
<div style="width: 100%">
|
<slot name="footer"></slot>
|
</div>
|
</el-form>
|
</template>
|
<script>
|
const rule = {
|
change: [
|
'checkbox',
|
'select',
|
'date',
|
'datetime',
|
'drop',
|
'radio',
|
'cascader'
|
], // 2020.05.31增加级联类型
|
phone: /^[1][3,4,5,6,7,8,9][0-9]{9}$/,
|
decimal: /(^[\-0-9][0-9]*(.[0-9]+)?)$/,
|
number: /(^[\-0-9][0-9]*([0-9]+)?)$/
|
};
|
const inputTypeArr = ['text', 'string', 'mail', 'textarea', 'password'];
|
const types = {
|
int: 'number',
|
byte: 'number',
|
decimal: 'number', // "float",
|
string: 'string',
|
bool: 'boolean',
|
date: 'datetime',
|
date: 'date',
|
mail: 'email'
|
};
|
//表单验证注意:每次验证都必须执行callback,否则验证不执行回调方法
|
const colPow = Math.pow(10, 3);
|
import FormExpand from './VolForm/VolFormRender';
|
import {
|
defineAsyncComponent,
|
defineComponent,
|
ref,
|
reactive,
|
toRefs,
|
getCurrentInstance,
|
onMounted,
|
watch
|
} from 'vue';
|
export default defineComponent({
|
components: {
|
FormExpand,
|
'vol-upload': defineAsyncComponent(() =>
|
import('@/components/basic/VolUpload.vue')
|
),
|
'vol-wang-editor': defineAsyncComponent(() =>
|
import('@/components/editor/VolWangEditor.vue')
|
)
|
},
|
props: {
|
loadKey: {
|
// 是否加载formRules字段配置的数据源
|
type: Boolean,
|
default: true
|
},
|
width: {
|
// 表单宽度
|
type: Number,
|
default: 0
|
},
|
labelWidth: {
|
// 表单左边label文字标签的宽度
|
type: Number,
|
default: 100
|
},
|
formRules: {
|
// 表单配置规则,如字段类型,是否必填
|
type: Array,
|
default: []
|
},
|
formFields: {
|
type: Object,
|
default: () => {
|
return {};
|
}
|
},
|
editor: {
|
// 2021.01.16编辑器信息 {uploadImgUrl:"",upload:null//上传方法}
|
type: Object,
|
default: () => {
|
return {};
|
}
|
},
|
size: {
|
type: String, //large / default / small
|
default: 'large'
|
},
|
select2Count: {
|
//超出数量显示select2组件
|
type: Number,
|
default: 500
|
}
|
},
|
computed: {
|
rules() {
|
let ruleResult = {};
|
this.formRules.forEach((option, xIndex) => {
|
option.forEach((item) => {
|
ruleResult[item.field] = [this.getRule(item, this.formFields)];
|
});
|
});
|
if (this.$refs.volform) {
|
setTimeout(() => {
|
this.$refs.volform.clearValidate();
|
}, 100);
|
}
|
return ruleResult;
|
}
|
},
|
setup(props, context) {
|
const { appContext, proxy } = getCurrentInstance();
|
const remoteCall = ref(true);
|
const span = ref(1);
|
const rangeFields = toRefs([]);
|
const volform = ref(null);
|
const numberFields = toRefs([]);
|
onMounted(() => {});
|
const initFormRules = (init) => {
|
if (props.loadKey) {
|
initSource();
|
}
|
props.formRules.forEach((row, xIndex) => {
|
if (row.length > span.value) span.value = row.length;
|
let _count = 0,
|
_size = 0;
|
row.forEach((x) => {
|
if (x.colSize > 0) {
|
_size = _size + x.colSize;
|
_count++;
|
}
|
});
|
if (_count > 0 && row.length - _count > 0) {
|
let _cellSize = (12 - _size) / (row.length - _count);
|
row.forEach((x) => {
|
if (!x.colSize) {
|
x.colSize = _cellSize;
|
}
|
});
|
}
|
row.forEach((item, yIndex) => {
|
if (item.type == 'number') {
|
numberFields.push(item.field);
|
}
|
// 目前只支持select单选远程搜索,remote远程从后台字典数据源进行搜索,url从指定的url搜索
|
if (item.remote || item.url) {
|
// item.remoteData = [];
|
item.loading = false;
|
item.point = { x: xIndex, y: yIndex };
|
}
|
// 初始化上传文件信息
|
initUpload(item, init);
|
// 初始化数据源空对象
|
if (item.dataKey) {
|
// 下拉框都强制设置为字符串类型
|
item.columnType = 'string';
|
if (!item.data) {
|
item.data = [];
|
}
|
}
|
|
if (item.range || item.type == 'range') {
|
if (
|
!(props.formFields[item.field] instanceof Array) ||
|
props.formFields[item.field].length != 2
|
) {
|
props.formFields[item.field] = ['', ''];
|
}
|
rangeFields.push(item.field);
|
}
|
});
|
});
|
};
|
|
const initSource = () => {
|
let keys = [],
|
binds = [];
|
// 初始化字典数据源
|
props.formRules.forEach((item) => {
|
item.forEach((x) => {
|
if (x.dataKey && (!x.data || x.data.length == 0) && !x.remote) {
|
x.data = [];
|
binds.push({ key: x.dataKey, data: x.data, type: x.type });
|
if (keys.indexOf(x.dataKey) == -1) {
|
keys.push(x.dataKey);
|
}
|
}
|
});
|
});
|
|
if (keys.length == 0) return;
|
appContext.config.globalProperties.http
|
.post('/api/Sys_Dictionary/GetVueDictionary', keys)
|
.then((dic) => {
|
bindOptions(dic, binds);
|
proxy.$emit('dicInited', dic);
|
});
|
};
|
const bindOptions = (dic, binds) => {
|
dic.forEach((d) => {
|
if (d.data.length > props.select2Count) {
|
if (
|
!binds.some((x) => {
|
return x.key == d.dicNo && x.type == 'cascader';
|
})
|
) {
|
d.data.forEach((item) => {
|
item.label = item.value;
|
item.value = item.key;
|
});
|
}
|
}
|
binds.forEach((x) => {
|
if (x.key != d.dicNo) return true;
|
// 如果有数据的则不查询
|
if (x.data.length > 0) return true;
|
//2022.03.13增加级联数据源自动转换
|
if (x.type == 'cascader' || x.type == 'treeSelect') {
|
let _data = JSON.parse(JSON.stringify(d.data));
|
let cascaderArr = appContext.config.globalProperties.base.convertTree(
|
_data,
|
(node, data, isRoot) => {
|
if (!node.inited) {
|
node.inited = true;
|
node.label = node.value;
|
node.value = node.key;
|
}
|
}
|
);
|
props.formRules.forEach((option) => {
|
option.forEach((item) => {
|
if (item.dataKey == x.key) {
|
item.orginData = x.data;
|
item.data = cascaderArr;
|
}
|
});
|
});
|
} else if (d.data.length > 0 && !d.data[0].hasOwnProperty('key')) {
|
let source = d.data,
|
newSource = new Array(source.length);
|
for (let index = 0; index < source.length; index++) {
|
newSource[index] = {
|
key: source['key'] + '',
|
value: source['value']
|
};
|
}
|
x.data.push(...newSource);
|
} else {
|
x.data.push(...d.data);
|
}
|
});
|
});
|
};
|
|
const initUpload = (item, init) => {
|
if (!init) return;
|
if (
|
['img', 'excel', 'file'].indexOf(item.type != -1) ||
|
item.columnType == 'img'
|
) {
|
// 只是没设置是否自动上传的,默认都是选择文件后自动上传
|
if (!item.hasOwnProperty('autoUpload')) {
|
item.autoUpload = true;
|
}
|
if (!item.hasOwnProperty('fileList')) {
|
item.fileList = true;
|
}
|
if (!item.hasOwnProperty('downLoad')) {
|
item.downLoad = true;
|
}
|
if (!item.removeBefore) {
|
item.removeBefore = (index, file, files) => {
|
return true;
|
};
|
}
|
if (!item.fileClick) {
|
item.fileClick = (index, file, files) => {
|
return true;
|
};
|
}
|
if (!item.onChange) {
|
item.onChange = (files) => {
|
return true;
|
};
|
}
|
if (!item.uploadAfter) {
|
item.uploadAfter = (result, files) => {
|
return true;
|
};
|
}
|
if (!item.uploadBefore) {
|
item.uploadBefore = (files) => {
|
return true;
|
};
|
}
|
}
|
};
|
const validate = (callback) => {
|
let result = true;
|
volform.value.validate((valid) => {
|
if (!valid) {
|
appContext.config.globalProperties.$message.error('数据验证未通过!');
|
result = false;
|
} else if (typeof callback === 'function') {
|
try {
|
callback(valid);
|
} catch (error) {
|
let msg = `表单验证回调方法异常:${error.message}`;
|
appContext.config.globalProperties.$message.error(msg);
|
console.log(msg);
|
}
|
}
|
});
|
return result;
|
};
|
|
initFormRules(true);
|
return {
|
remoteCall,
|
span,
|
rangeFields,
|
numberFields,
|
validate,
|
volform
|
// initFormRules,
|
// initSource
|
};
|
},
|
created() {
|
this.formRules.forEach((rules) => {
|
rules.forEach((option) => {
|
if (option.type == 'treeSelect' && option.multiple === undefined) {
|
option.multiple = true;
|
}
|
});
|
});
|
},
|
|
data() {
|
return {
|
// remoteCall: true,
|
errorImg: 'this.src="' + require('@/assets/imgs/error-img.png') + '"'
|
// span: 1,
|
// rangeFields: [],
|
};
|
},
|
methods: {
|
getColWidth(item) {
|
//2021.08.30 增加动态计算表单宽度
|
let _span = 0;
|
this.formRules.forEach((row, xIndex) => {
|
//2022.05.06 追加表单中隐藏的元素不参与动态计算表单宽度
|
let rowLength = row.filter((item) => {
|
return !item.hidden;
|
}).length;
|
if (rowLength > _span) _span = rowLength;
|
});
|
let rete =
|
Math.round(((item.colSize || 12 / _span) / 0.12) * colPow, 10.0) /
|
colPow;
|
if (item.colSize) return rete.toFixed(3);
|
return rete.toFixed(3);
|
// return (100 - rete).toFixed(3);
|
},
|
previewImg(url) {
|
this.base.previewImg(url, this.http.ipAddress);
|
},
|
getSrc(path) {
|
if (!path) return;
|
if (!this.base.isUrl(path) && path.indexOf('.') != -1) {
|
return this.http.ipAddress + path;
|
}
|
return path;
|
},
|
// 是否为图片文件等格式并对字段的转换成数组:[{name:'1.jpg',path:'127.0.0.1/ff/1.jpg'}]
|
isFile(item, formFields) {
|
if (
|
item.type == 'img' ||
|
item.columnType == 'img' ||
|
item.type == 'excel' ||
|
item.type == 'file'
|
) {
|
this.convertFileToArray(item, formFields);
|
return true;
|
}
|
return false;
|
},
|
isReadonlyImgFile(item, formFields) {
|
if ((item.disabled || item.readonly) && this.isFile(item, formFields)) {
|
return true;
|
}
|
return false;
|
},
|
convertFileToArray(item, formFields) {
|
if (!item.maxFile) {
|
item.maxFile = 1; // 默认只能上传一个文件,可以在onInit中设置
|
}
|
|
let fileInfo = formFields[item.field];
|
if (fileInfo instanceof Array) {
|
return;
|
}
|
if (fileInfo === null || fileInfo === undefined) {
|
formFields[item.field] = [];
|
return;
|
}
|
// 将以逗号隔开的文件分割成数组127.0.0.1/aa/1.jpg,将127.0.0.1/aa/2.jpg
|
if (typeof fileInfo === 'string') {
|
if (fileInfo.trim() === '') {
|
formFields[item.field] = [];
|
return;
|
}
|
// 如果文件路径是字符串,则使用,拆分
|
fileInfo = fileInfo.replace(/\\/g, '/');
|
let files = fileInfo.split(',');
|
formFields[item.field] = [];
|
for (let index = 0; index < files.length; index++) {
|
let file = files[index];
|
let splitFile = file.split('/');
|
formFields[item.field].push({
|
name: splitFile.length > 0 ? splitFile[splitFile.length - 1] : file,
|
path: file // this.base.isUrl(file) ? file : this.http.ipAddress + file,
|
});
|
}
|
}
|
},
|
dowloadFile(file) {
|
this.base.dowloadFile(
|
file.path,
|
file.name,
|
{
|
Authorization: this.$store.getters.getToken()
|
},
|
this.http.ipAddress
|
);
|
},
|
validatorPhone(ruleOption, value, callback) {
|
if (!ruleOption.required && !value && value != '0') {
|
return callback();
|
}
|
if (!rule.phone.test((value || '').trim())) {
|
return callback(new Error('请输入正确的手机号'));
|
}
|
callback();
|
},
|
validatorPwd(ruleOption, value, callback) {
|
if (!ruleOption.required && !value && value != '0') {
|
return callback();
|
}
|
if ((value + '').trim().length < 6) {
|
return callback(new Error('密码长度不能小于6位'));
|
}
|
callback();
|
},
|
convertArrayValue(data, val) {
|
// 2020.12.13增加表单多选只转换字典
|
// 编辑多选table显示
|
//2023.04.20修复只读为label时原数据被字典替换了的问题
|
let valArr = Array.isArray(val)
|
? val.map((x) => {
|
return x;
|
})
|
: val.split(',');
|
for (let index = 0; index < valArr.length; index++) {
|
var _item = data.find((x) => {
|
return x.key && x.key != '0' && x.key + '' == valArr[index] + '';
|
});
|
if (_item) {
|
valArr[index] = _item.value;
|
}
|
}
|
return valArr.join(',');
|
},
|
getText(formFields, item) {
|
// 2019.10.24修复表单select组件为只读的属性时没有绑定数据源
|
let text = formFields[item.field];
|
if (typeof text === 'function') return text(formFields);
|
if (text === 'null' || text === '' || text === null || text === undefined)
|
return '--';
|
//2021.03.02增加只读时日期处理
|
if (item.type == 'date') {
|
return text.replace('T', ' ').split(' ')[0];
|
}
|
//2021.03.31修复表单switch只读时没有转换值的问题
|
if (item.type == 'switch') {
|
return text ? '是' : '否';
|
}
|
if (!item.data) return text;
|
if (item.type == 'selectList' || item.type == 'checkbox') {
|
return this.convertArrayValue(item.data, text);
|
}
|
var _item = item.data.find((x) => {
|
return x.key == text;
|
});
|
return _item ? _item.value : text;
|
},
|
onClear(item, formFields) {
|
// 远程select标签清空选项
|
item.data.splice(0);
|
// console.log(2);
|
},
|
onChange(item, value) {
|
if (item.onChange && typeof item.onChange === 'function') {
|
item.onChange(value, item);
|
}
|
},
|
onRemoteChange(item, value) {
|
// 第二次打开时,默认值成了undefined,待查viewgrid中重置代码
|
if (value == undefined && item.data.length > 0) {
|
this.formFields[item.field] = item.data[0].key;
|
// console.log('undefined');
|
}
|
this.remoteCall = false;
|
if (item.onChange && typeof item.onChange === 'function') {
|
item.onChange(value, item);
|
}
|
},
|
getData(item) {
|
return item.data;
|
},
|
|
// 远程搜索(打开弹出框时应该禁止搜索)
|
remoteSearch(item, formFields, val) {
|
if (!item.remote && !item.url) {
|
return;
|
}
|
if (
|
val == '' ||
|
(item.data.length == 1 &&
|
(val == item.data[0].key || val == item.data[0].value))
|
) {
|
return;
|
}
|
// 弹出框或初始化表单时给data设置数组默认值2
|
// 2020.09.26修复远程搜索自定义url不起作用的问题
|
let url;
|
if (typeof item.url === 'function') {
|
url = item.url(val, item.dataKey, item);
|
} else {
|
url =
|
(item.url || '/api/Sys_Dictionary/GetSearchDictionary') +
|
'?dicNo=' +
|
item.dataKey +
|
'&value=' +
|
val;
|
}
|
this.http.post(url).then((dicData) => {
|
//this.$set(item, "loading", false);
|
item.loading = false;
|
item.data = dicData;
|
this.formRules[item.point.x].splice(item.point.y, 1, item);
|
});
|
},
|
getObject(date) {
|
if (typeof date === 'object') {
|
return date;
|
}
|
return new Date(date);
|
},
|
reset(sourceObj) {
|
// 重置表单时,禁用远程查询
|
this.$refs['volform'].resetFields();
|
if (this.rangeFields.length) {
|
this.rangeFields.forEach((key) => {
|
this.formFields[key].splice(0);
|
this.formFields[key] = [null, null];
|
});
|
}
|
if (!sourceObj) return;
|
for (const key in this.formFields) {
|
if (sourceObj.hasOwnProperty(key)) {
|
this.formFields[key] = sourceObj[key];
|
if (this.numberFields.indexOf(key) != -1) {
|
this.formFields[key] = sourceObj[key] * 1 || 0;
|
}
|
}
|
}
|
// this.remoteCall = true;
|
},
|
|
fileOnChange(files, item) {
|
this.$refs.volform.clearValidate(item.field);
|
if (item.onChange) {
|
return item.onChange(files);
|
}
|
return true;
|
},
|
isReadonly(item) {
|
return item.readonly || item.disabled;
|
},
|
getRule(item, formFields) {
|
//2021.07.17增加只读表单不验证
|
//range与swtich暂时不做校验
|
if (
|
// item.readonly ||
|
// item.disabled ||
|
item.type == 'switch' ||
|
item.type == 'range'
|
)
|
return { required: false };
|
// 用户设置的自定义方法
|
if (item.validator && typeof item.validator === 'function') {
|
return {
|
validator: (rule, val, callback) => {
|
// 用户自定义的方法,如果返回了值,直接显示返回的值,验证不通过
|
let message = item.validator(rule, val);
|
if (message) return callback(new Error(message + ''));
|
return callback();
|
},
|
required: item.required,
|
trigger: rule.change.indexOf(item.type) != -1 ? 'change' : 'blur'
|
};
|
}
|
if (['img', 'excel', 'file'].indexOf(item.type) != -1) {
|
return {
|
validator: (rule, val, callback) => {
|
//2021.09.05移除文件上传默认必填
|
if (
|
item.required &&
|
!this.isReadonly(item) &&
|
(!val || !val.length)
|
) {
|
return callback(
|
new Error(item.type == 'img' ? '请上传照片' : '请上传文件')
|
);
|
}
|
return callback();
|
},
|
required: item.required,
|
trigger: 'change'
|
};
|
}
|
// 设置数字的最大值民最小值
|
if (
|
item.type == 'number' ||
|
item.columnType == 'number' ||
|
item.columnType == 'int' ||
|
item.type == 'decimal'
|
) {
|
// 如果是必填项的数字,设置一个默认最大与最值小
|
if (item.required && typeof item.min !== 'number') {
|
item.min = 0; //item.type == "decimal" ? 0.1 : 1;
|
}
|
|
return {
|
required: item.required,
|
message: item.title + '只能是数字',
|
title: item.title,
|
trigger: 'blur',
|
min: item.min,
|
max: item.max,
|
type: item.columnType || item.type,
|
validator: (ruleObj, value, callback) => {
|
if (!ruleObj.min && !ruleObj.max) {
|
if (ruleObj.required) {
|
if ((!value && value != '0') || !rule.decimal.test(value)) {
|
return callback(new Error('只能是数字'));
|
}
|
}
|
return callback();
|
}
|
if (this.isReadonly(item)) return callback();
|
if (ruleObj.type == 'number') {
|
if (!rule.number.test(value)) {
|
ruleObj.message = ruleObj.title + '只能是整数';
|
return callback(new Error(ruleObj.message));
|
}
|
} else {
|
if (!rule.decimal.test(value)) {
|
ruleObj.message = ruleObj.title + '只能是数字';
|
return callback(new Error(ruleObj.message));
|
}
|
}
|
if (
|
ruleObj.min !== undefined &&
|
typeof ruleObj.min === 'number' &&
|
value < ruleObj.min
|
) {
|
ruleObj.message = ruleObj.title + '不能小于' + ruleObj.min;
|
return callback(new Error(ruleObj.message));
|
}
|
if (
|
ruleObj.max !== undefined &&
|
typeof ruleObj.max === 'number' &&
|
value > ruleObj.max
|
) {
|
ruleObj.message = ruleObj.title + '不能大于' + ruleObj.max;
|
return callback(new Error(ruleObj.message));
|
}
|
return callback();
|
}
|
};
|
}
|
|
// 手机、密码验证
|
if (item.type == 'password' || item.type == 'phone') {
|
return {
|
validator:
|
item.type == 'phone' ? this.validatorPhone : this.validatorPwd,
|
required: item.required,
|
trigger: 'blur'
|
};
|
}
|
|
if (!item.required && item.type != 'mail') return { required: false };
|
|
if (!item.hasOwnProperty('type')) item.type = 'text';
|
|
if (inputTypeArr.indexOf(item.type) != -1) {
|
let message =
|
item.title +
|
(item.type == 'mail' ? '必须是一个邮箱地址' : '不能为空');
|
let type = item.type == 'mail' ? 'email' : types[item.columnType];
|
let _rule = {
|
required: true,
|
message: message,
|
trigger: 'blur',
|
type: type,
|
validator: (ruleObj, value, callback) => {
|
if (
|
!this.isReadonly(item) &&
|
(value === '' || value === undefined || value === null)
|
) {
|
return callback(new Error(ruleObj.message));
|
}
|
return callback();
|
}
|
};
|
if (item.type == 'mail') {
|
_rule.validator = undefined;
|
return _rule;
|
}
|
if (item.min) {
|
_rule.min = item.min;
|
_rule.message = item.title + '至少' + item.min + '个字符!';
|
}
|
if (item.max) {
|
return [
|
_rule,
|
{
|
max: item.max,
|
required: true,
|
message: item.title + '最多' + item.max + '个字符!',
|
trigger: 'blur'
|
}
|
];
|
}
|
return _rule;
|
}
|
|
if (item.type == 'radio') {
|
return {
|
required: item.required,
|
message: '请选择' + item.title,
|
trigger: 'change',
|
type: 'string'
|
};
|
}
|
if (
|
item.type == 'date' ||
|
item.type == 'datetime' ||
|
item.type == 'month' ||
|
item.type == 'time'
|
) {
|
return {
|
required: true,
|
message: '请选择' + item.title,
|
trigger: 'change',
|
type: item.range ? 'array' : 'string',
|
validator: (rule, val, callback) => {
|
if (this.isReadonly(item)) return callback();
|
// 用户自定义的方法,如果返回了值,直接显示返回的值,验证不通过
|
if (!val || (item.range && !val.length)) {
|
return callback(new Error('请选择日期'));
|
}
|
return callback();
|
}
|
};
|
}
|
|
if (item.type == 'cascader') {
|
return {
|
type: 'array',
|
required: true,
|
min: item.min || 1,
|
// message: "请选择" + item.title,
|
trigger: 'change',
|
validator: (rule, val, callback) => {
|
if (this.isReadonly(item)) return callback();
|
// 用户自定义的方法,如果返回了值,直接显示返回的值,验证不通过
|
let _arr = this.formFields[item.field];
|
if (!_arr || !_arr.length) {
|
return callback(new Error('请选择' + item.title));
|
}
|
return callback();
|
}
|
};
|
}
|
|
if (
|
['select', 'selectList', 'checkbox', 'cascader', 'treeSelect'].indexOf(
|
item.type
|
) != -1
|
) {
|
let _rule = {
|
type: item.type == 'select' ? 'string' : 'array',
|
required: true,
|
min: item.min || 1,
|
message: '请选择' + item.title,
|
trigger: 'change',
|
validator: (rule, value, callback) => {
|
if (this.isReadonly(item)) return callback();
|
//2021.11.27修复多选没有提示的问题
|
if (value == undefined || value === '') {
|
return callback(new Error(rule.message));
|
} else if (
|
(item.type == 'checkbox' ||
|
item.type == 'selectList' ||
|
item.type == 'treeSelect') &&
|
(!(value instanceof Array) || !value.length)
|
) {
|
return callback(new Error(rule.message));
|
}
|
return callback();
|
}
|
};
|
|
if (_rule.max) {
|
_rule.nax = item.max;
|
_rule.message = '最多只能选择' + item.max + '项';
|
}
|
return _rule;
|
}
|
return {};
|
},
|
compareDate(date1, date2) {
|
if (!date2) {
|
return true;
|
}
|
return (
|
date1.valueOf() <
|
(typeof date2 == 'number' ? date2 : new Date(date2).valueOf())
|
);
|
},
|
getDateOptions(date, item) {
|
//2021.07.17设置时间可选范围
|
if ((!item.min && !item.max) || !date) {
|
return false;
|
}
|
if (item.min && item.min.indexOf(' ') == -1) {
|
//不设置时分秒,后面会自动加上 08:00
|
item.min = item.min + ' 00:00:000';
|
}
|
return (
|
this.compareDate(date, item.min) || !this.compareDate(date, item.max)
|
);
|
},
|
getDateFormat(item) {
|
if (item.type == 'month') {
|
return 'YYYY-MM';
|
}
|
// if (item.type=='time') {
|
// return 'HH:mm:ss'
|
// }
|
//见https://day.js.org/docs/zh-CN/display/format
|
return item.type == 'date' ? 'YYYY-MM-DD' : 'YYYY-MM-DD HH:mm:ss';
|
},
|
dateRangeChange(val, item) {
|
if (!val) {
|
this.$emit('update:formFields');
|
return;
|
}
|
item.onChange && item.onChange(val);
|
},
|
onKeyPress($event, item) {
|
if ($event.keyCode == 13) {
|
return;
|
}
|
item.onKeyPress($event);
|
},
|
filterMethod(value, data) {
|
return data.label.includes(value);
|
},
|
getNode( label,node, data){
|
console.log(label)
|
}
|
}
|
});
|
</script>
|
<style lang="less" scoped>
|
.el-form-item {
|
margin-right: 0;
|
}
|
.el-form-item {
|
.form-imgs {
|
img {
|
float: left;
|
cursor: pointer;
|
object-fit: cover;
|
margin: 0 10px 10px 0;
|
width: 65px;
|
height: 65px;
|
border: 1px solid #c7c7c7;
|
overflow: hidden;
|
border-radius: 5px;
|
box-sizing: content-box;
|
}
|
}
|
}
|
.el-form-item ::v-deep(.el-form-item__label) {
|
padding: 0 0px 0 4px;
|
overflow: hidden;
|
text-overflow: ellipsis;
|
white-space: nowrap;
|
}
|
.el-form-item ::v-deep(.el-range-separator) {
|
text-align: center;
|
width: 13px;
|
padding: 0px 1px;
|
font-size: 12px;
|
}
|
.el-form-item ::v-deep(.el-range__close-icon) {
|
margin-right: -10px;
|
}
|
.form-item-extra {
|
> *:first-child {
|
flex: 1;
|
}
|
display: flex;
|
.form-extra {
|
padding-left: 7px;
|
line-height: 36px;
|
}
|
}
|
.vol-form-item {
|
width: 100%;
|
}
|
.vol-form-item ::v-deep(.el-form-item__content) {
|
display: unset !important;
|
}
|
.vol-form-item ::v-deep(.el-input--large .el-input__inner) {
|
height: 34px !important;
|
}
|
.vol-form-item ::v-deep(.el-input-number--large .el-input-number__increase) {
|
border-top: 1px solid #d4d4d4;
|
}
|
.vol-form-item ::v-deep(.el-input-number--large .el-input-number__decrease) {
|
border-bottom: 1px solid #d4d4d4;
|
}
|
.vol-form-item ::v-deep(.el-input--large.el-date-editor) {
|
height: 36px;
|
}
|
.v-date-range ::v-deep(.el-input__prefix) {
|
display: none;
|
}
|
.v-date-range ::v-deep(.el-input__inner) {
|
padding: 0;
|
}
|
|
.el-form-item ::v-deep(.el-checkbox) {
|
margin-right: 8px;
|
}
|
.el-form-item ::v-deep(.el-checkbox .el-checkbox__label) {
|
padding-left: 5px;
|
}
|
.el-form-item ::v-deep(textarea) {
|
font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB',
|
'Microsoft YaHei', '微软雅黑', Arial, sans-serif !important;
|
}
|
.el-form-item ::v-deep(.el-select .el-select__tags > span) {
|
display: flex;
|
}
|
.el-form-item ::v-deep(.el-select-v2__combobox-input) {
|
height: 30px;
|
}
|
.el-form-item ::v-deep(.el-select__tags) {
|
overflow: hidden;
|
height: 30px;
|
}
|
.el-form-item ::v-deep(.el-select-tags-wrapper) {
|
position: absolute;
|
}
|
|
.el-form-item {
|
vertical-align: top !important;
|
}
|
.form-file-list {
|
a {
|
color: #3ea9ff;
|
}
|
a:hover {
|
cursor: pointer;
|
color: #0281e7;
|
}
|
}
|
</style>
|