123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274 |
- <template>
- <el-form
- ref="fForm"
- :key="update"
- :rules="rules"
- class="flex-wrap f-form"
- :class="text ? 'text' : ''"
- :model="form"
- :label-position="labelPosition">
- <el-form-item
- v-for="(item, index) in configList"
- :key="'form-item' + index"
- :ref="'formItem'+item.prop"
- :label-width="setWidth(item, index)"
- :label="item.label"
- :prop="item.prop"
- :style="{
- width: !autoWidth
- ? (item.span && colVal * item.span + '%') || colVal + '%'
- : 'auto'
- }"
- class="pb-10 mb-8"
- :class="getClass(item,index)"
- :rules="item.rules"
- :required="item.disabled ? false : item.required"
- >
- <div :style="{ width: item.width || itemWidth }">
- <components
- v-if="!item.hasSlot && item.itemType !== 'text' && item.itemType"
- :is="'f-' + item.itemType"
- v-model="form[item.prop]"
- v-bind="{ disabled:formItemDisabled(item),...item.attrs }"
- @change="handleEmit($event,item,'change')"
- @blur="handleEmit($event,item,'blur')"
- @focus="handleEmit($event,item,'focus')"
- />
- <span v-else-if="!item.hasSlot && item.itemType === 'text'" class="text-break-all" :class="(item.click?'f-cursor--pointer link-type':'') +(item.className || '')" @click="()=>{item.click && item.click(item,form)}">
- {{
- (item.formatter && item.formatter(form)) ||( formatter(item,form[item.prop]))
- }}
- </span>
- <slot v-else v-bind="item" :name="item.prop" />
-
- <slot v-if="item.orderSlot" :name="item.orderSlot" /></slot>
- </div>
- <!-- label插槽 -->
- <template v-if="item.hasLabelSlot" #label>
- <template v-if="item.hasLabelSlot">
- <slot v-bind="item" :name="item.prop + 'Label'" />
- </template>
- </template>
- </el-form-item>
- <el-form-item
- v-if="$slots.default"
- label-width="0px"
- class="flex-auto slot"
- >
- <slot />
- </el-form-item>
- </el-form>
- </template>
- <script>
- import { formatNumber } from '../../utils/index';
- import formMixins from'../../mixins/formMixins.js'
- export default {
- name: 'FForm',
- mixins:[formMixins],
- props: {
- config: { type: Array, default: () => [] }, // 配置文件
- form: {
- type: Object,
- default: () => {}
- }, // 传进来的共享的form表单值对象
- rules: { type: Object, default: () => ({}) }, // 传进来的共享的form验证规则
- column: { type: Number, default: 4 }, // 总的col布局
- itemWidth: { type: String, default: '100%' }, // form-item项的宽度
- disabled: { type: Boolean, default: false },
- clearable: { type: Boolean, default: true },
- // label位置
- labelPosition: {
- type: String,
- default: 'top'
- },
- // 是否自动宽度
- autoWidth: {
- type: Boolean,
- default: false
- },
- // 是否文字展示
- text: {
- type: Boolean,
- default: false
- },
- },
- data() {
- return {
- configdata: [],
- update: 0
- };
- },
- computed: {
- // 组件内部config
- configList() {
- return this.config;
- },
- // 整体列数对应的col
- colVal() {
- return 100 / this.column;
- },
- labelWidth() {
- // rowList每一行的数据,初始化的时候给一个二维数组,并制定第一个参数为new Array()
- // columnList每一列的数据
- const rowList = [new Array(this.column)];
- const columnList = [];
- // count用来计算是否换行新增rowList的数组元素空数组,用来填补二维数组空位
- // index代表的是rowList的当前操作项的下标
- let count = 0;
- let index = 0;
- this.config.forEach((e, i, arr) => {
- count += e.span || 1;
- rowList[index][count - (e.span || 1)] = {
- name: e.label || '',
- prop: e.prop,
- index: i
- };
- if (
- count + ((arr[i + 1] || {}).span || 1) > this.column &&
- arr[i + 1]
- ) {
- rowList.push(new Array(this.column));
- index++;
- count = 0;
- }
- });
- // 行数据转列数据
- rowList.forEach((e) => {
- for (let i = 0; i < e.length; i++) {
- if (!columnList[i]) {
- columnList[i] = [];
- }
- columnList[i].push(e[i]);
- }
- });
- // 取出每一列最大值nameLength
- // 取出每一列的index下标
- return columnList.map((e) => {
- const nameLength = Math.max.apply(
- null,
- e.map((el) => (el && this.charCode(el.name).length / 4) || 0)
- );
- let hasRules = false;
- e.map((el) => {
- if (
- el &&
- el.name &&
- this.rules[el.prop] &&
- this.rules[el.prop][0].required
- ) {
- hasRules = true;
- }
- });
- const indexList = e.map((el) => el && el.index);
- return { nameLength, indexList, hasRules };
- });
- }
- },
- methods: {
- // 表单重置
- resetFields(){
- this.$refs.fForm.resetFields();
- },
- // 优先子组件自己的 再表单的
- formItemDisabled(item){
- if(item.attrs&&item.attrs.hasOwnProperty('disabled')){
- return item.attrs.disabled
- }
- return this.isDisabled
- },
- // 获取class
- getClass(item, ind) {
- const { labelPosition, column, configList } = this;
- let classStr = ''
- // 计算当前配置是不是没行最后一个
- let allSpan = 0;
- let pr = 'pr-8';
- for (let _index = 0; _index <= Number(ind); _index++) {
- const newSpan = configList[_index].span || 1;
- allSpan += newSpan;
- }
- if (allSpan % column === 0) {
- pr = '';
- }
- classStr += pr;
- if (item.itemClass) {
- classStr += item.itemClass;
- }
- return classStr;
- },
- // 通过labelWidth获取对应index里面的值
- setWidth(item, index) {
- // 增加rules为入参,判断当前项是否有必填校验,如果有那么width加上11px;
- const val = this.labelWidth.find((e) => e.indexList.includes(index));
- const rulesWidth = val.hasRules ? 11 : 0;
- return val.nameLength * 14 + rulesWidth + 16 + 'px';
- },
- // 将字符串转为16进制值,汉子转4位,英文字母和数组转2位
- charCode: (str) =>
- str
- .split('')
- .reduce((total, e) => total + e.codePointAt(0).toString(16), ''),
- // 格式化
- formatter(column, v) {
- if (v === null || v === undefined) return '-';
- let value = v;
- const { dataType } = column;
- const type = dataType || '';
- if (value !== '') {
- if (type === 'money') {
- value = formatNumber(value);
- } else if (type === 'ten-thousand') {
- value = formatNumber(value, 'ten-thousand');
- } else if (type === 'number' || type === 'area') {
- let decimal = 2;
- if (type === 'number') {
- decimal = 0;
- }
- value = formatNumber(value, null, decimal);
- } else if (type === 'rate') {
- value = formatNumber(value, 'rate');
- } else if (type === 'date-d') {
- // value = value
- }
- }
- if(value === '0' || value === 0) return value
- return value || '-';
- },
- // 事件传递
- handleEmit($event,item,type){
- item[type] && item[type]($event);
- this.$emit(type,item,this.form)
- },
- // 校验
- validate(callback){
- return new Promise(resolve=>{
- this.$refs.fForm.validate(resolve);
- })
- }
- }
- };
- </script>
- <style lang="scss" scoped>
- .f-form .el-form-item {
- margin-bottom: 25px;
- // display: flex;
- // align-items: center;
- .el-change-icon {
- transform: rotate(90deg);
- }
- .el-form-item_custom {
- cursor: pointer;
- i {
- margin-left: 2px;
- }
- }
- }
- </style>
|