riku
2023-12-19 3959e507bfa99cc4ced2a6f48f9b4358334d34c4
1. 调试表单选项组件的双向绑定逻辑
已修改10个文件
341 ■■■■■ 文件已修改
src/components.d.ts 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/form/FYForm.vue 42 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/search-option/FYOptionLocation.vue 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/search-option/FYOptionOnlineStatus.vue 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/search-option/FYOptionScene.vue 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/search-option/FYOptionTime.vue 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/search-option/FYOptionUserType.vue 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/search-option/base/FYOptionText.vue 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/composables/formConfirm.js 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fytz/user/components/CompUserInfo.vue 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components.d.ts
@@ -10,6 +10,7 @@
    BaseContentLayout: typeof import('./components/core/BaseContentLayout.vue')['default']
    BasePanelLayout: typeof import('./components/core/BasePanelLayout.vue')['default']
    Content: typeof import('./components/core/Content.vue')['default']
    copy: typeof import('./components/search-option/FYOptionLocation copy.vue')['default']
    ElAside: typeof import('element-plus/es')['ElAside']
    ElAvatar: typeof import('element-plus/es')['ElAvatar']
    ElBacktop: typeof import('element-plus/es')['ElBacktop']
@@ -55,9 +56,7 @@
    ElTabPane: typeof import('element-plus/es')['ElTabPane']
    ElTabs: typeof import('element-plus/es')['ElTabs']
    ElTag: typeof import('element-plus/es')['ElTag']
    ElText: typeof import('element-plus/es')['ElText']
    ElTooltip: typeof import('element-plus/es')['ElTooltip']
    ElTransfer: typeof import('element-plus/es')['ElTransfer']
    ElTree: typeof import('element-plus/es')['ElTree']
    Footer: typeof import('./components/core/Footer.vue')['default']
    FormCol: typeof import('./components/layout/FormCol.vue')['default']
src/components/form/FYForm.vue
@@ -3,7 +3,7 @@
    :inline="false"
    :model="formObj"
    ref="formRef"
    :rules="rules"
    :rules="allRules"
    label-position="right"
    label-width="150px"
  >
@@ -26,7 +26,7 @@
 * 可传入初始表单数据formInfo,表单校验规则rules
 * 实现submit和cancel触发函数
 */
import { defineProps, defineEmits, reactive, ref, watch } from 'vue';
import { defineProps, defineEmits, reactive, ref, watch, computed } from 'vue';
import { useFormConfirm } from '@/composables/formConfirm';
const props = defineProps({
@@ -43,7 +43,7 @@
  //重置按钮是否可用
  useReset: Boolean,
  //触发重置
  reset: Boolean,
  doClear: Boolean,
  //通知编辑状态
  isEdit: Boolean
});
@@ -51,8 +51,32 @@
//触发函数,提交和取消
const emit = defineEmits(['submit', 'cancel', 'update:isEdit']);
const baseRules = reactive({
  _usertype: [
    {
      required: true,
      message: '用户类型不能为空',
      trigger: 'change'
    }
  ],
  _locations: [
    {
      required: true,
      message: '省/市/区/镇不能为空',
      trigger: 'change',
    }
  ],
  _scenetype: [
    {
      required: true,
      message: '场景类型不能为空',
      trigger: 'change'
    }
  ]
});
//表单操作函数
const { formObj, formRef, edit, onSubmit, onCancel, onReset, formProps } = useFormConfirm({
const { formObj, formRef, edit, onSubmit, onCancel, onReset, clear } = useFormConfirm({
  submit: {
    do: submit
  },
@@ -83,21 +107,25 @@
  emit('cancel');
}
const allRules = computed(()=>{
  return {...baseRules.value, ...props.rules}
})
//监听表单初始数据传入
watch(
  () => props.formInfo,
  (nValue) => {
    formObj.value = nValue;
  },
  { deep: false, immediate: false }
  { deep: false, immediate: true }
);
//监听表单重置功能触发
watch(
  () => props.reset,
  () => props.doClear,
  (nValue) => {
    if (nValue) {
      onReset();
      clear();
    }
  }
);
src/components/search-option/FYOptionLocation.vue
@@ -1,7 +1,8 @@
<template>
  <el-form-item :label="placeholder" :prop="prop">
    <el-cascader
      v-model="selectedOptions"
      :model-value="formatedValue"
      @change="handleChange"
      :options="locations"
      :placeholder="placeholder"
      :props="optionProps"
@@ -11,10 +12,7 @@
</template>
<script>
import { enumLocation } from '@/enum/location'
// 记录选项是否是自定义传入的
let customValue = true
import { enumLocation } from '@/enum/location';
export default {
  props: {
@@ -46,50 +44,28 @@
  data() {
    return {
      locations: enumLocation(this.allOption, this.level),
      selectedOptions: [],
      optionProps: {
        checkStrictly: this.checkStrictly
      }
    }
    };
  },
  computed: {
    placeholder() {
      const list = '省/市/区/镇'.split('/')
      const p = []
      const list = '省/市/区/镇'.split('/');
      const p = [];
      for (let i = 0; i < this.level; i++) {
        p.push(list[i])
        p.push(list[i]);
      }
      return p.join('/')
    }
      return p.join('/');
  },
  watch: {
    selectedOptions: {
      handler(nVal, oVal) {
        if (nVal != oVal) {
          const res = this.optionFormat(nVal)
          // 标明此时的value更新是组件内部触发
          customValue = false
          this.$emit('update:value', res)
        }
      },
      deep: true
    },
    value: {
      handler(nVal, oVal) {
        // 只有当值是外部传入时,才触发更新
        if (!customValue) {
          customValue = true
          return
        }
        if (nVal != oVal) {
          this.selectedOptions = this.optionFormatReverse(nVal)
        }
      },
      deep: true,
      immediate: true
    formatedValue() {
      return this.optionFormatReverse(this.value);
    }
  },
  methods: {
    handleChange(value) {
      this.$emit('update:value', this.optionFormat(value));
    },
    /**
     * 地区选项结果格式化
     */
@@ -103,61 +79,61 @@
        dName: null,
        tCode: null,
        tName: null
      }
      };
      if (val.length > 0) {
        res.pCode = val[0][0]
        res.pName = val[0][1]
        res.pCode = val[0][0];
        res.pName = val[0][1];
      }
      if (val.length > 1) {
        res.cCode = val[1][0]
        res.cName = val[1][1]
        res.cCode = val[1][0];
        res.cName = val[1][1];
      }
      if (val.length > 2) {
        res.dCode = val[2][0]
        res.dName = val[2][1]
        res.dCode = val[2][0];
        res.dName = val[2][1];
      }
      if (val.length > 3) {
        res.tCode = val[3][0]
        res.tName = val[3][1]
        res.tCode = val[3][0];
        res.tName = val[3][1];
      }
      return res
      return res;
    },
    optionFormatReverse(val) {
      const res = []
      const res = [];
      if (val) {
        if (val.pCode) {
          res.push([val.pCode, val.pName])
        if (val.pName) {
          res.push([val.pCode, val.pName]);
        }
        if (val.cCode) {
          res.push([val.cCode, val.cName])
        if (val.cName) {
          res.push([val.cCode, val.cName]);
        }
        if (val.dCode) {
          res.push([val.dCode, val.dName])
        if (val.dName) {
          res.push([val.dCode, val.dName]);
        }
        if (val.tCode) {
          res.push([val.tCode, val.tName])
        if (val.tName) {
          res.push([val.tCode, val.tName]);
        } 
      }
      return res
      return res;
    }
  },
  mounted() {
    if (this.initValue) {
      if (this.checkStrictly) {
        this.selectedOptions = [this.locations[0].value]
        this.handleChange([this.locations[0].value]);
      } else {
        const f = (location) => {
          if (location.children && location.children.length > 0) {
            const r = f(location.children[0])
            r.unshift(location.value)
            return r
            const r = f(location.children[0]);
            r.unshift(location.value);
            return r;
          } else {
            return [location.value]
            return [location.value];
          }
        }
        this.selectedOptions = f(this.locations[0])
        };
        this.handleChange(f(this.locations[0]));
      }
    }
  }
}
};
</script>
src/components/search-option/FYOptionOnlineStatus.vue
@@ -1,7 +1,8 @@
<template>
  <el-form-item label="上线状态">
    <el-select
      v-model="selectedOptions"
      :model-value="value"
      @change="handleChange"
      placeholder="上线状态"
      style="width: 75px"
    >
@@ -37,31 +38,16 @@
  data() {
    return {
      onlineStatus: enumOnlineStatus(),
      selectedOptions: {},
    };
  },
  watch: {
    selectedOptions: {
      handler(nVal, oVal) {
        if (nVal != oVal) {
          this.$emit('update:value', nVal);
  methods: {
    handleChange(value) {
      this.$emit('update:value', value);
        }
      },
      deep: true,
    },
    value: {
      handler(nVal, oVal) {
        if (nVal != oVal) {
          this.selectedOptions = nVal;
        }
      },
      deep: true,
      immediate: true
    },
  },
  mounted() {
    if (this.initValue) {
      this.selectedOptions = this.onlineStatus[0];
      this.handleChange(this.onlineStatus[0]);
    }
  },
};
src/components/search-option/FYOptionScene.vue
@@ -1,7 +1,8 @@
<template>
  <el-form-item label="场景类型" :prop="prop">
    <el-select
      v-model="selectedOptions"
      :model-value="value"
      @change="handleChange"
      placeholder="场景类型"
      style="width: 150px"
    >
@@ -43,31 +44,16 @@
  data() {
    return {
      sceneTypes: enumScene(this.type, this.allOption),
      selectedOptions: {},
    };
  },
  watch: {
    selectedOptions: {
      handler(nVal, oVal) {
        if (nVal != oVal) {
          this.$emit('update:value', nVal);
  methods: {
    handleChange(value) {
      this.$emit('update:value', value);
        }
      },
      deep: true,
    },
    value: {
      handler(nVal, oVal) {
        if (nVal != oVal) {
          this.selectedOptions = nVal;
        }
      },
      deep: true,
      immediate: true
    },
  },
  mounted() {
    if (this.initValue) {
      this.selectedOptions = this.sceneTypes[0];
      this.handleChange(this.sceneTypes[0]);
    }
  },
};
src/components/search-option/FYOptionTime.vue
@@ -1,7 +1,8 @@
<template>
  <el-form-item label="时间">
    <el-date-picker
      v-model="selectedOptions"
      :model-value="value"
      @change="handleChange"
      type="month"
      placeholder="选择时间"
      style="width: 150px"
@@ -10,9 +11,9 @@
</template>
<script>
import dayjs from 'dayjs'
import dayjs from 'dayjs';
const MONTH = 'month'
const MONTH = 'month';
export default {
  props: {
@@ -30,41 +31,25 @@
  },
  emits: ['update:value'],
  data() {
    return {
      selectedOptions: ''
    }
  },
  watch: {
    selectedOptions: {
      handler(nVal, oVal) {
        if (nVal != oVal) {
          this.$emit('update:value', nVal)
        }
      }
    },
    value: {
      handler(nVal, oVal) {
        if (nVal != oVal) {
          this.selectedOptions = nVal
        }
      },
      immediate: true
    }
    return {};
  },
  methods: {
    handleChange(value) {
      this.$emit('update:value', value);
    },
    timeFormat() {
      switch (this.type) {
        case MONTH:
          return 'YYYY-MM'
          return 'YYYY-MM';
        default:
          return 'YYYY-MM'
          return 'YYYY-MM';
      }
    }
  },
  mounted() {
    if (this.initValue) {
      this.selectedOptions = new Date()
      this.handleChange(new Date());
    }
  }
}
};
</script>
src/components/search-option/FYOptionUserType.vue
@@ -1,7 +1,8 @@
<template>
  <el-form-item label="用户类型" :prop="prop">
    <el-select
      v-model="selectedOptions"
      :model-value="value"
      @change="handleChange"
      placeholder="用户类型"
      style="width: 150px"
    >
@@ -38,31 +39,16 @@
  data() {
    return {
      userTypes: enumUser(this.allOption),
      selectedOptions: {},
    };
  },
  watch: {
    selectedOptions: {
      handler(nVal, oVal) {
        if (nVal != oVal) {
          this.$emit('update:value', nVal);
  methods: {
    handleChange(value) {
      this.$emit('update:value', value);
        }
      },
      deep: true,
    },
    value: {
      handler(nVal, oVal) {
        if (nVal != oVal) {
          this.selectedOptions = nVal;
        }
      },
      deep: true,
      immediate: true
    },
  },
  mounted() {
    if (this.initValue) {
      this.selectedOptions = this.userTypes[0];
      this.handleChange(this.userTypes[0]);
    }
  },
};
src/components/search-option/base/FYOptionText.vue
@@ -1,6 +1,12 @@
<template>
  <el-form-item :label="label">
    <el-input clearable v-model="searchText" :placeholder="placeholder" />
  <el-form-item :label="label" :prop="prop">
    <el-input
      clearable
      :model-value="value"
      :placeholder="placeholder"
      @input="handleInput"
      style="width: 150px"
    />
  </el-form-item>
</template>
@@ -9,32 +15,24 @@
  props: {
    label: {
      type: String,
      default: '查询项',
      default: '查询项'
    },
    placeholder: {
      type: String,
      default: '输入搜索内容',
      default: '输入搜索内容'
    },
    // 返回结果
    value: String,
    prop: String
  },
  emits: ['update:value'],
  data() {
    return {
      searchText: '',
    };
    return {};
  },
  watch: {
    searchText(nVal, oVal) {
      if (nVal != oVal) {
        this.$emit('update:value', nVal);
  methods: {
    handleInput(value) {
      this.$emit('update:value', value);
      }
    },
    value(nVal, oVal) {
      if (nVal != oVal) {
        this.searchText = nVal;
      }
    },
  },
};
</script>
src/composables/formConfirm.js
@@ -20,10 +20,10 @@
  if (!cancel.title) cancel.title = '取消';
  if (!cancel.msg) cancel.msg = '是否放弃已编辑的内容?';
  const formProps = defineProps({
    // 是否在提交成功后清空表单
    clearAftSubmit: Boolean
  });
  // const formProps = defineProps({
  //   // 是否在提交成功后清空表单
  //   clearAftSubmit: Boolean
  // });
  //表单内容
  const formObj = ref(defaultForm ? defaultForm : {});
@@ -69,24 +69,24 @@
  // 重置表单
  const _reset = function () {
    formRef.value.clearValidate();
    edit.value = false;
    isReset = true;
    formObj.value = useCloned(formObjClone.cloned, {
      manual: true
    }).cloned.value;
    formRef.value.clearValidate();
  };
  // 清空表单
  const clear = function () {
    isReset = true;
    // formRef.value.resetFields();
    formRef.value.resetFields();
    edit.value = false;
  };
  // 提交成功后
  const submited = function () {
    if (formProps.clearAftSubmit) clear();
    // if (formProps.clearAftSubmit) clear();
    edit.value = false;
    formObjClone = useCloned(formObj, { manual: true });
  };
@@ -147,5 +147,5 @@
    }
  };
  return { formProps, formObj, formRef, edit, onSubmit, onCancel, onReset };
  return { formObj, formRef, edit, onSubmit, onCancel, onReset, clear };
}
src/views/fytz/user/components/CompUserInfo.vue
@@ -2,7 +2,7 @@
  <FYForm
    :form-info="_formInfo"
    :rules="rules"
    :reset="active"
    :doClear="active"
    :useCancel="create"
    :useReset="!create"
    :clearAftSubmit="create"
@@ -130,27 +130,6 @@
  //     trigger: 'blur',
  //   },
  // ],
  _usertype: [
    {
      required: true,
      message: '用户类型不能为空',
      trigger: 'change'
    }
  ],
  _locations: [
    {
      required: props.create,
      message: '省/市/区/镇不能为空',
      trigger: 'change'
    }
  ],
  _scenetype: [
    {
      required: true,
      message: '场景类型不能为空',
      trigger: 'change'
    }
  ]
});
// 用户基本信息格式化
@@ -162,7 +141,7 @@
  s._scenetype = getSceneName(s.extension2, 1);
  s._locations = [];
  s._locations = {};
  return s;
}