封装Form表单【后台控制表单搜索项的显隐和排序】

news2024/9/20 1:07:57

概要

为了实现需求:后台控制表单搜索项的显隐和排序;

整体思路流程

表单搜索项统一配置,封装成一个组件,把不同类别再封装成单个的组件,配置项数组由前端控制(暂由前端配置,这样虽然代码量多,但是对于前端修改配置比较灵活,暂时不考虑数据由后端取过来的原因是,有些配置,如表单类型不好控制)

接下来看具体实现,再回看也许好理解。

具体实现

一、Form表单封装文件,自定义命名为FreeForm.vue

在vue项目中我们可以把它放到src/components这个文件夹下,新建目录sortForm,将此文件放入

HTML部分:

<template>
  <div class="container">
    <!-- 这里加了超过4行展开收起的功能 -->
    <div
      :class="['content', { collapsed: isCollapsed }]"
      :style="contentStyle"
      ref="content"
    >
      <el-form
        :ref="formRef"
        size="small"
        :inline="true"
        :model="model"
        v-bind="$attrs"
        v-show="showSearch"
        :label-width="labelWidth"
        label-position="left"
        label-suffix=":"
      >
      <!-- formItemConfig  表单数据源 -->
        <div
          v-for="(item, index) in formItemConfig"
          :key="index"
          :style="{ display: 'inline-block' }"
        >
        <!-- 渲染表单项 -->
          <el-form-item
            v-if="item.show"
            :label="item.label"
            :prop="item.prop"
            style="width: 100%"
            :label-width="item.labelWidth"
          >
            <!-- 动态渲染组件 -->
            <component
              :is="isComponentName(item)"
              v-model="model[item.prop]"
              :placeholder="placeholder(item)"
              v-bind="item"
              :style="{ width: item.width }"
              :multiple="item.multiple"
              @input="changeValue(item, $event)"
              @click="handleClick(item, $event)"
              @change="handleChange(item, $event)"
            />
          </el-form-item>
        </div>
      </el-form>
    </div>
    <!-- 按钮组:这里是封装了搜索重置按钮 -->
    <div class="form-op-container">
      <el-button type="text" @click="toggle" v-if="parseInt(maxHeight) > 210">
        <div class="button-text">
          {{ buttonText }}
          <div :class="{ isTransIcon: !isCollapsed }">
            <span class="iconfont icon-zhankai"></span>
          </div>
        </div>
      </el-button>
      <div class="header_btns">
        <slot name="header_btns_before"></slot>
      </div>
      <div class="header_btns">
        <el-button size="mini" type="primary" @click="handleSearch"
          >搜索</el-button
        >
        <el-button size="mini" @click="handleReset">重置</el-button>
      </div>
      <div class="header_btns">
        <slot name="header_btns_after"></slot>
      </div>
    </div>
  </div>
</template>

JS部分:

<script>
import { cloneDeep } from "lodash";
/**
 * @desc 表单组件
 * @param {Object} formRef - el-form 的 ref 名称
 * @param {Object} model - 表单数据模型
 * @param {Object} formItemConfig - el-form-item 配置项
 */
export default {
  props: {
    // 表单引用名称
    formRef: {
      type: String,
      default: "formRef",
    },
    // 表单数据模型
    model: {
      type: Object,
      default: () => ({}),
    },
    // 表单项配置
    formItemConfig: {
      type: Array,
      default: () => [],
    },
    showSearch: {
      type: Boolean,
      default: true,
    },
    labelWidth: {
      type: String,
      default: "100px",
    },
  },
  data() {
    return {
      init: null,
      isCollapsed: true,
      maxHeight: "200px",
    };
  },
  computed: {
    contentStyle() {
      return {
        maxHeight: this.isCollapsed ? "200px" : this.maxHeight,
        transition: "max-height 0.2s ease",
      };
    },
    buttonText() {
      return this.isCollapsed ? "展开" : "收起";
    },
    /**
     * 根据组件类型获取需要渲染的组件名称
     */
    isComponentName() {
      return (item) => {
        if (item.component === "el-select") {
          return "SelectForm";
        } else if (item.component === "radio") {
          return "RadioGroupForm";
        } else if (item.component === "checkbox") {
          return "CheckboxGroupForm";
        } else if (item.component === "date-picker") {
          return "DatePickerForm";
        } else if (item.component === "number-range") {
          return "NumberRange";
        } else {
          return item.component || "el-input";
        }
      };
    },
    /**
     * 根据表单项配置获取占位符
     */
    placeholder() {
      return (item) => {
        if (item.placeholder) return item.placeholder;
        const arr = ["el-input", "el-input-number"];
        return !item.component || arr.includes(item.component)
          ? `请输入${item.label || ""}`
          : `请选择${item.label || ""}`;
      };
    },
  },
  methods: {
    // 搜索
    handleSearch() {
      this.$emit("search");
    },
    // 重置
    handleReset() {
      this.resetFields();
      this.$emit("reset");
    },
    refreshRealHeight() {
      this.$nextTick(() => {
        this.maxHeight = this.$refs.content.scrollHeight + "px";
      });
    },
    toggle() {
      this.isCollapsed = !this.isCollapsed;
    },
    resetFields() {
      this.copyAndClear(this.model, this.init);
      this.$refs[this.formRef].resetFields();
    },
    copyAndClear(sourceObj, targetObj) {
      // 删除原对象的所有属性
      for (const key in sourceObj) {
        if (sourceObj.hasOwnProperty(key)) {
          delete sourceObj[key];
        }
      }
      // 遍历原对象的所有属性
      for (const key in targetObj) {
        if (targetObj.hasOwnProperty(key)) {
          // 将属性值复制到目标对象
          sourceObj[key] = targetObj[key];
        }
      }
    },
    /**
     * 验证表单并执行回调函数
     * @param {Function} cb - 表单验证通过后的回调函数
     * @returns {boolean} - 表单验证结果
     */
    validate(cb) {
      this.$refs[this.formRef].validate((valid) => {
        cb(valid, this.model);

        if (valid) {
          // 如果表单验证通过,执行提交操作
        } else {
          // 如果表单验证失败,处理失败情况
          return false;
        }
      });
    },
    /**
     * 处理表单项的点击事件
     * @param {Object} item - 当前点击的表单项配置
     */
    handleClick(item, e) {
      // 处理数据改变的逻辑
      item.onClick ? item.onClick(e) : () => {};
    },
    //change型式的回调
    handleChange(item, e) {
      item.onChange ? item.onChange(e) : () => {};
    },
    /**
     * 更新表单数据模型到父组件
     */
    changeValue(item, e) {
      this.$emit("input", e);
    },
    convertKeysToNested(obj) {
      const result = {};
      for (const key in obj) {
        if (obj.hasOwnProperty(key)) {
          // 递归判断当前对象是否存在 ['a.b'] 的 key
          this.setNestedProperty(result, key, obj[key]);
        }
      }
      return result;
    },
    setNestedProperty(obj, path, value) {
      // 根据path 解析属性路径
      const keys = path.split(".");
      let current = obj;
      for (let i = 0; i < keys.length; i++) {
        const key = keys[i];
        if (i === keys.length - 1) {
          // 不是最叶子节点赋值
          this.$set(current, key, value);
        } else {
          // 不是最叶子节点赋值空对象
          if (!current[key]) {
            this.$set(current, key, {});
          }
          current = current[key];
        }
      }
    },
    getFormParams() {
      return this.convertKeysToNested(this.model);
    },
  },
  watch: {
    model: {
      handler(val) {
        if (!this.init) {
          this.init = cloneDeep(val);
        }
      },
      deep: true,
      immediate: true,
    },
    formItemConfig: {
      handler() {
        this.refreshRealHeight();
      },
      deep: true,
      immediate: true,
    },
  },
  mounted() {
    window.addEventListener("resize", this.refreshRealHeight);
  },
  beforeDestroy() {
    window.removeEventListener("resize", this.refreshRealHeight);
  },
};
</script>

css部分:

<style lang="scss" scoped>
.container {
  width: 100%;
  margin: 0 auto;
}

.content {
  overflow: hidden;
}
.form-op-container {
  display: flex;
  flex-direction: row;
  justify-content: end;
  margin-bottom: 10px;
}
.header_btns {
  display: flex;
  align-items: center;
  justify-content: center;
  margin-left: 10px;
}
.isTransIcon {
  transform: rotateX(180deg);
}
.button-text {
  display: flex;
  gap: 5px;
  align-items: center;
  font-size: 12px;
  .icon-zhankai {
    font-size: 11px;
  }
}
</style>

 注释1:这里就是将不同类型的搜索项进行封装,通过组件名字控制渲染的是什么组件,比如输入框,单选框,select选择器、时间选择器等,还可以传入自己封装的组件。

表单搜索项类别

  • Cascader级联选择器
  • Checkbox 多选框
  • DatePicker 日期选择器
  • Radio 单选框
  • Select 选择器

1.Cascader级联选择器

<template>
  <el-cascader
    :options="options"
    :props="cascaderProps"
    :collapse-tags="collapseTags"
    collapse-tags-tooltip
    clearable
    v-model="internalValue"
    v-on="$listeners"
    v-bind="$attrs"
  />
</template>

<script>
export default {
  name: 'CascaderForm',
  props: {
    value: {
      type: [String, Array, Object],
      default: () => ([]),
    },
    options: {
      type: Array,
      required: true,
    },
    cascaderProps: {
      type: Object,
      default: () => ({}),
    },
    collapseTags: {
      type: Boolean,
      default: true,
    },
  },
  data() {
    return {
      internalValue: this.value,
    };
  },
  watch: {
    value(newVal) {
      this.internalValue = newVal;
    },
    internalValue(newVal) {
      this.$emit('input', newVal);
    },
  },
};
</script>

2. Checkbox 多选框

<template>
  <el-checkbox-group v-model="internalValue" v-on="$listeners" v-bind="$attrs">
    <el-checkbox
      v-for="option in options"
      :key="option.value"
      :label="option.value"
    >
      {{ option.label }}
    </el-checkbox>
  </el-checkbox-group>
</template>

<script>
export default {
  name: "CheckboxGroupForm",
  props: {
    value: {
      type: Array,
      default: () => [],
    },
    options: {
      type: Array,
      default: () => [],
    },
  },
  data() {
    return {
      internalValue: this.value,
    };
  },
  watch: {
    value(newVal) {
      this.internalValue = newVal;
    },
    internalValue(newVal) {
      this.$emit("input", newVal);
    },
  },
};
</script>

3.DatePicker 日期选择器

<template>
  <el-date-picker v-model="internalValue" v-on="$listeners" v-bind="$attrs" />
</template>

<script>
export default {
  name: "DatePickerForm",
  props: {
    value: {
      type: [String, Array, Date],
      default: "",
    },
  },
  data() {
    return {
      internalValue: this.value,
    };
  },
  watch: {
    value(newVal) {
      this.internalValue = newVal;
    },
    internalValue(newVal) {
      this.$emit("input", newVal);
    },
  },
};
</script>

 4.Radio 单选框

<template>
  <el-radio-group
    v-model="internalValue"
    v-on="$listeners"
    v-bind="$attrs"
    size="small"
    class="radioGroupForm"
    :class="$attrs.isButton ? 'is-button' : ''"
  >
    <template v-if="$attrs.isButton">
      <el-radio-button
        v-for="(option, index) in options"
        :key="index"
        :label="option.value"
      >
        {{ option.label }}
      </el-radio-button>
    </template>
    <template v-else>
      <el-radio
        v-for="(option, index) in options"
        :key="index"
        :label="option.value"
      >
        {{ option.label }}
      </el-radio>
    </template>
  </el-radio-group>
</template>

<script>
export default {
  props: {
    value: [String, Number],
    options: {
      type: Array,
      default: () => [],
    },
  },
  data() {
    return {
      internalValue: this.value,
    };
  },
  watch: {
    value(newVal) {
      this.internalValue = newVal;
    },
    internalValue(newVal) {
      this.$emit("input", newVal);
    },
  },
};
</script>

 5.Select 选择器

<template>
  <el-select
    v-bind="$attrs"
    v-on="$listeners"
    v-model="modelValue"
    :multiple="multiple"
    collapse-tags
  >
    <el-option
      v-for="(option, index) in options"
      :key="index"
      :label="option.label"
      :value="option.value"
    >
    </el-option>
  </el-select>
</template>

<script>
export default {
  props: {
    value: {
      required: true,
    },
    options: {
      type: Array,
      default: () => [],
    },
    multiple:{
      type: Boolean,
      default: false
    }
  },
  computed: {
    modelValue: {
      get() {
        return this.value;
      },
      set(val) {
        this.$emit("input", val);
      },
    },
  },
};
</script>

封装控制显隐排序的按钮

HTML部分:

<template>
  <el-dropdown trigger="click">
    <slot name="cus_button" v-if="cusButton" />
    <el-button icon="el-icon-s-operation" size="mini" v-else>列设置</el-button>
    <el-dropdown-menu slot="dropdown">
      <el-tree
        draggable
        :data="formItemConfig"
        :props="defaultProps"
        :allow-drop="allowDrop"
        @node-drag-over="handleNodeDragOver"
      >
        <span slot-scope="{ data }" class="tree-table-setting">
          <el-checkbox
            v-model="data.show"
            :disabled="data.setFromDisabled"
            @change="handleFormChange(data.id)"
          />
          <span class="tree-label">{{
            data.label || data.startPlaceholder || "无标题"
          }}</span>
          <i class="iconfont icon-tuodong1 tree-icon"></i>
        </span>
      </el-tree>
      <el-button
        type="primary"
        size="mini"
        class="save-form-config"
        @click="saveFormConfig"
        >保存</el-button
      >
    </el-dropdown-menu>
  </el-dropdown>
</template>

JS部分:

<script>
import { reportedFormPageInfo } from "@/api/custom/index.js";
export default {
  name: "formConfig",
  props: {
    formItemConfig: {
      type: Array,
      default: () => [],
    },
    cusButton: {
      type: Boolean,
      default: false,
    },
  },
  data() {
    return {
      defaultProps: {
        children: "children",
        label: "label",
        disabled: "setFromDisabled",
      },
      hiddenForms: [], // 存储隐藏表单的项的 id
    };
  },
  methods: {
    // 筛选数组:重新格式化数组中的对象
    filterArray(arr) {
      return arr.map((item, index) => ({
        code: item.id || item,
        sort: index + 1,
      }));
    },
    allowDrop(draggingNode, dropNode, type) {
      // 控制拖放的逻辑:仅允许Tree节点上下拖动
      return type !== "inner";
    },
    // el-tree拖拽删除禁用标志
    handleNodeDragOver(node, enter, e) {
      e.preventDefault(); // 防止默认处理
      e.dataTransfer.dropEffect = "move"; // 设置拖动效果为move
    },
    handleFormChange(itemId) {
      const columnIndex = this.hiddenForms.indexOf(itemId);
      const item = this.formItemConfig.find((item) => item.id === itemId);

      if (item) {
        if (!item.show) {
          // 如果列被隐藏且不在数组中,则添加
          if (columnIndex === -1) {
            this.hiddenForms.push(itemId);
          }
        } else {
          // 如果列被显示且在数组中,则移除
          if (columnIndex !== -1) {
            this.hiddenForms.splice(columnIndex, 1);
          }
        }
      }
      // 返回show为true的数组
      return this.formItemConfig.filter((item) => item.show === true);
    },
    // 保存表单配置
    saveFormConfig() {
      const params = this.filterArray(this.handleFormChange());
      reportedFormPageInfo("custom", params).then((res) => {
        if (res.code === 200) {
          this.$message({
            message: "保存成功",
            type: "success",
            duration: 1500,
          });
        }
      });
    },
  },
};
</script>

 css部分:

<style scoped lang="scss">
::v-deep div[aria-disabled="true"] {
  display: none;
}
.tree-table-setting {
  display: flex;
  align-items: center;
  justify-content: space-around;
  .tree-label {
    width: 120px; /* 设置固定宽度,根据需要调整 */
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    margin-left: 5px;
  }

  .tree-icon {
    margin-left: auto; /* 将图标推到容器的最右边 */
    margin-right: 15px; /* 将图标推到容器的最右边 */
  }
}
.save-form-config {
  margin-left: 24px;
}
.cus-shezhi {
  border: 1px solid #e7e7e7;
  border-radius: 10px;
}
</style>

如何使用 

前端通过配置项可以控制显隐,在FreeForm组件里是通过v-show控制的;

排序的话是element-UI组件里Tree 树形控件可拖拽节点实现的;

现在在前端保存后,刷新后还是会复原的,所以我们要保存起来,如果没有特别高的要求,可以放在localstorage里,这里我们的需求是需要保存在后端,不同的部门看到的表格、表单不一样。我们要把配置好的数据传给后端,但是最开始渲染的时候最好还是从后端拿数据,哪个部门能看到哪些搜索项,所以我们和后端约定了一个方案,提供了一个版本号的概念,前端每次更改有新增表单配置项的时候,就修改版本号,版本号一样就正常从后端拿数据,然后渲染,不一样或者最开始没有版本号的时候,把前端的表单配置项数组上报给后端。

1.在列表页引入该表单组件FreeForm,HTML部分:

    <free-form
      ref="form"
      formRef="freeForm"
      :model="formData"
      :formItemConfig="showFormItemConfig"
      :labelWidth="labelWidth"
      label-position="top"
      :showSearch.sync="showSearch"
      @search="handleQuery"
      @reset="resetQuery"
      @keyup.enter.native="handleQuery"
    >
      <template #header_btns_after>
        <formConfig :formItemConfig="formItemConfig" cusButton>
          <template #cus_button>
            <el-button
              size="mini"
              circle
              icon="iconfont icon-shezhi cus-shezhi"
            />
          </template>
        </formConfig>
      </template>
    </free-form>

2.data里配置:

   // 表单数据
      formData: {
        queryParams: { pageNum: 1, pageSize: 10 },
      },
      labelWidth: "120px",
      formItemConfig: [], // 存储表单项的配置
      version: null,
  
  created() {
    // 初始化表单配置
    this.initFormItemConfig();
    // 获取页面信息:版本号、form表单、table表格配置项
    this.getPageInfo();
  },

这些数据源走的是接口,做了统一的处理: 

  computed: {
    // 获取渲染数据源
    showFormItemConfig() {
      return this.formItemConfig.map((item) => {
        if (item.id === "xxx") {
          return {
            ...item,
            options: this.xxxList,
          };
        } else if (item.id === "zzz") {
          return {
            ...item,
            options: this.zzzList,
          };
        } else if (item.id === "yyy") {
          return {
            ...item,
            options: this.yyyOptions,
            cascaderProps: this.deptProps,
          };
        } else if (
          item.id === "xx" ||
          item.id === "xx" ||
          item.id === "xx"
        ) {
          return {
            ...item,
            options: this.aaaOptions,
            cascaderProps: this.props,
          };
        }
        // 其他配置项的处理逻辑...
        return item;
      });
    },
  },

 

methods:{
    // 初始化表单配置
    initFormItemConfig() {
      this.formItemConfig = [
        {
          id: "namePhone",
          label: "姓名/手机号",
          prop: "queryParams.namePhone",
          clearable: true,
          component: "el-input", // el-input可以省略,默认使用el-input
          placeholder: "姓名/手机号", // placeholder可以省略,默认显示“请输入+label”
          show: true, // 展示与隐藏
          maxlength: "11",
        },
        {
          id: "status",
          label: "状态",
          prop: "queryParams.status",
          clearable: true,
          component: SelectForm, // el-input可以省略,默认使用el-input
          placeholder: "状态", // placeholder可以省略,默认显示“请输入+label”
          show: false, // 展示与隐藏
          multiple: true,
          options: that.dict.type.xx_status,//这里走的是字典
        },
        {
          id: "xxx",
          label: "新之助",
          prop: "queryParams.xxx",
          component: SelectForm, // 可以传入任意组件
          placeholder: "新之助",
          clearable: true,
          options: that.customerManagerList,
          show: false, // 展示与隐藏
          multiple: true,
        },
        {
          id: "xxx",
          label: "上学时间",
          prop: "xxx",
          component: DatePickerForm, // el-input可以省略,默认使用el-input
          type: "daterange",
          startPlaceholder: "上学时间",
          valueFormat: "yyyy-MM-dd",
          hidden: false,
          show: false,
          width: "205px",
          setFromDisabled: false, //设置表单配置项是否禁用
        },
        {
          id: "xxx",
          label: "放学时间",
          prop: "xxx",
          component: DatePickerForm, // el-input可以省略,默认使用el-input
          type: "daterange",
          startPlaceholder: "放学时间",
          valueFormat: "yyyy-MM-dd",
          hidden: false,
          show: false,
          width: "205px",
          setFromDisabled: false, //设置表单配置项是否禁用
        },
        {
          id: "remark",
          label: "备注",
          prop: "queryParams.remark",
          clearable: true,
          component: "el-input", // el-input可以省略,默认使用el-input
          placeholder: "备注", // placeholder可以省略,默认显示“请输入+label”
          show: false, // 展示与隐藏
        },
        {
          id: "xxx",
          label: "xx状态",
          prop: "queryParams.xxx",
          component: SelectForm, // 可以传入任意组件
          placeholder: "xx状态",
          clearable: true,
          options: [
            {
              value: 0,
              label: "xxx",
            },
            {
              value: 1,
              label: "xxx",
            },
          ],
          show: false, // 展示与隐藏
        },

        {
          id: "xxx",
          label: "妮妮",
          prop: "queryParams.xxx",
          component: SelectForm, // 可以传入任意组件
          placeholder: "妮妮",
          clearable: true,
          options: that.provinceList,
          show: false, // 展示与隐藏
        },
        {
          id: "xxx",
          label: "风间",
          prop: "xxx",
          component: CascaderForm, // 可以传入任意组件
          placeholder: "风间",
          clearable: true,
          options: that.deptNameOptions, // 设置 options
          cascaderProps: that.deptProps, // 设置 props
          show: false, // 展示与隐藏
        },
        {
          id: "xxx",
          label: "阿呆",
          prop: "xxx",
          component: CascaderForm, // 可以传入任意组件
          placeholder: "阿呆",
          clearable: true,
          options: that.channelOptions, // 设置 options
          cascaderProps: that.props, // 设置 props
          show: false, // 展示与隐藏
        },
        {
          id: "xxx",
          label: "正南",
          prop: "xxx",
          component: NumberRange,
          clearable: true,
          width: "205px",
          show: false, // 展示与隐藏
          startPlaceholder: "请输入",
          endPlaceholder: "请输入",
        },
      ];
    },
}
   // 获取页面信息:版本号、form表单、table表格配置项
    getPageInfo() {
      // 要保证每个页面的pageCode唯一
      getPageInfo("xx").then((res) => {
        if (res.code === 200) {
          // 获取当前版本号
          this.version = process.env.VUE_APP_VERSION;
          // 判断后端版本号是否存在、是否一致
          if (res.data && res.data.version === this.version) {
            // 不上报版本号,将获取到的表单、表格配置项数组渲染到前端界面
            let searchFields = res.data.searchFields;
            let tableFields = res.data.tableFields;
            this.formItemConfig = this.matchAndModify(
              this.formItemConfig,
              searchFields
            );
            this.tableItemConfig = this.matchAndModify(
              this.tableItemConfig,
              tableFields
            );
            return;
          } else if (!res.data || res.data.version !== this.version) {
            // 版本不一致,将表单、表格配置项数组传给后端
            reportedPageInfo({
              pageCode: "xx",
              version: process.env.VUE_APP_VERSION,
              searchFields: this.filterArray(this.formItemConfig),
              tableFields: this.filterArray(this.tableItemConfig),
            }).then((res) => {
              if (res.code === 200) {
                let searchFields = res.data.searchFields;
                let tableFields = res.data.tableFields;
                // 从后端拿到的表单、表格配置项数组渲染到前端界面
                this.formItemConfig = this.matchAndModify(
                  this.formItemConfig,
                  searchFields
                );
                this.tableItemConfig = this.matchAndModify(
                  this.tableItemConfig,
                  tableFields
                );
              }
            });
          }
        }
      });
    },

 

    // 定义一个方法来处理匹配
    matchAndModify(originalArray, backendArray) {
      // 克隆一份原始数组,以免直接修改原数组
      const clonedArray = [...originalArray];

      // 根据 backendArray 中的顺序对 clonedArray 进行排序
      clonedArray.sort((a, b) => {
        let indexA = backendArray.findIndex((item) => item.code === a.id);
        let indexB = backendArray.findIndex((item) => item.code === b.id);
        return indexA - indexB;
      });

      // 对排序后的 clonedArray 进行遍历和处理
      const res = clonedArray.map((item) => {
        const flag = backendArray.find((backendItem) => {
          return backendItem.code === item.id;
        });
        if (flag) {
          return {
            ...item,
            show: true,
          };
        }
        return item;
      });

      return res;
    },
   // 筛选数组:重新格式化数组中的对象
    filterArray(arr) {
      return arr.map((item, index) => ({
        code: item.id || item,
        sort: index + 1,
      }));
    },

小结

参考文章链接:

https://juejin.cn/post/7022140926906597384icon-default.png?t=N7T8https://juejin.cn/post/7022140926906597384

https://juejin.cn/post/7311602153826402313?searchId=20240726140625580D4C02135D9973239D#heading-21icon-default.png?t=N7T8https://juejin.cn/post/7311602153826402313?searchId=20240726140625580D4C02135D9973239D#heading-21

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2037889.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

央行重提P2P存量业务化解,非吸案开始翻旧账?

沉寂已久的P2P&#xff0c;又突然以另一种意想不到的形式回到公众视野了。2018年全国P2P坍塌式暴雷&#xff0c;平台老板“跑路”“判刑”的消息一时间你方唱罢我登场。当年的某凰金融、某租宝、某信贷等赫赫有名的网贷平台传出的消息无非两类——查封或跑路&#xff0c;这几年…

外卖O2O系统开发源码开源介绍

外卖O2O系统开发源码开源介绍 开源外卖O2O系统源码可以为开发者提供快速搭建外卖平台的基础&#xff0c;节省从零开始的开发时间。 以下是几个推荐的开源项目&#xff1a; flash-waimai 是一个基于Spring Boot和Vue.js的前后端分离的外卖系统&#xff0c;包含手机端和后台管理…

代码随想录算法训练营第三十五天|背包问题理论基础、携带研究材料、分割等和子集

背包问题理论基础 1.背包问题概述 01背包&#xff1a;有n种物品&#xff0c;每种物品只有一个&#xff1b; 完全背包&#xff1a;有n种物品&#xff0c;每种物品有无限个&#xff1b; 多重背包&#xff1a;有n种物品&#xff0c;每种物品的个数各不相同。 2. 01背包 有n件…

旋转目标数据集制作:roLabelImg的安装和使用

目录 创建roLabelImg环境 安装pyqt5和lxml 下载roLabelImg源码包 使用roLabelImg roLabelImg常用操作指令 标注展示 由于最近一些项目需要标注旋转数据集&#xff0c;在网上找了一些教程&#xff0c;但大多数都显得比较杂乱&#xff0c;因此想把这些重新整理一下&#xf…

汽车免拆诊断案例 | 2013款北京现代悦动车发动机偶尔无法起动

故障现象 一辆2013款北京现代悦动车&#xff0c;搭载G4FC发动机&#xff0c;累计行驶里程约为13.9万km。车主反映&#xff0c;发动机偶尔无法起动着机&#xff0c;断开点火开关&#xff0c;等待一会儿又可以起动着机。 故障诊断 接车后反复试车&#xff0c;当发动机无法起动着…

TS RadiMation®软件EUT监测与控制:抗扰度测试的智能解决方案

随着电子设备在各个领域的广泛应用&#xff0c;确保它们在各种电磁环境中可靠运行变得尤为重要。TS RadiMation软件以其卓越的EUT监测与控制功能&#xff0c;为抗扰度测试提供了一站式智能解决方案。 在本文中&#xff0c;我们将深入探讨TS RadiMation如何通过先进的输入通道配…

【MATLAB第108期】基于MATLAB的fast、vbsa、dynia、eet、glue、pawn、rsa敏感性分析模型合集(无目标函数)【更新中】

【MATLAB第108期】基于MATLAB的fast、vbsa、dynia、eet、glue、pawn、rsa敏感性分析模型合集&#xff08;无目标函数&#xff09;【更新中】 一、FAST&#xff08;Fourier Amplitude Sensitivity Test&#xff09; FAST&#xff08;Fourier Amplitude Sensitivity Test&#…

2024年10大最佳研发工时管理系统推荐

这篇文章介绍了以下几个工具&#xff1a;PingCode、Worktile、无鱼项目工时系统、盖雅工厂、泽众ALM、蓝凌KMS、Forecast、EasyRedmine、Trello、Hubstaff。 在选择研发工时管理系统时&#xff0c;很多人都感到无从下手。市面上的工具五花八门&#xff0c;功能和特点各不相同&a…

专题十四_优先级队列

目录 1046. 最后一块石头的重量 解析 题解 703. 数据流中的第 K 大元素 解析 题解 692. 前K个高频单词 解析 题解 1046. 最后一块石头的重量 1046. 最后一块石头的重量 解析 题解 class Solution { public:int lastStoneWeight(vector<int>& stones) {// 专…

idea 对于mybatis-plus框架JRebelX和XRebel热启动失效问题

1.mybatis-plus不需要使用热启动插件&#xff0c;修改完代码后&#xff0c;直接重新编译一下即可&#xff0c;不需要重启 2.如果是mapper.xml文件&#xff0c;则直接安装JRebel MybatisPlus extension 插件即可完成mapper.xml静态文件更改进行热加载

墨水屏显示颜色过程中的问题,数据和像素值提取比较

软件使用步骤参考 数据数量问题 对于一个单层图片来说&#xff0c;可以分辨率可以使用像素的数量来描述。图片的长宽由多少像素组成就是所说的图片的长宽。这种说法也不太准确&#xff0c;一般人为分辨率越大&#xff0c;约清晰。这种认知是在同样长度中有更多像素&#xff0…

计算机毕业设计 助农产品采购平台 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

HDFS 原理和操作

目录 一、操作1. web工具2.命令行----常用命令3.Java APIJavaAPI创建HDFS目录&#xff0c;获取HDFS修改权限JavaAPI上传和下载数据使用JavaAPI获取HDFS元信息 二、HDFS原理解析1.数据上传2.数据下载 三、HDFS的高级特性1.回收站2.快照3.配额Quota4.安全模式5.权限管理命令行Jav…

Linux系统之部署俄罗斯方块网页小游戏(二)

Linux系统之部署俄罗斯方块网页小游戏(二) 一、小游戏介绍1.1 小游戏简介1.2 项目预览二、本次实践介绍2.1 本地环境规划2.2 本次实践介绍三、检查本地环境3.1 检查系统版本3.2 检查系统内核版本3.3 检查软件源四、安装Apache24.1 安装Apache2软件4.2 启动apache2服务4.3 查看…

动态规划(二)——例题

目录 Help Jimmy 题目 解题思路 神奇的口袋 题目 枚举的解法 递归的解法 动态规划的解法 滑雪 题目 解题思路 解法一 解法二 Help Jimmy 题目 "Help Jimmy" 是在下图所示的场景上完成的游戏&#xff1a; 场景中包括多个长度和高度各不相同的平台。地面是…

使用ResMaskingNet情绪识别模型的预训练文件进行情绪识别

使用ResMaskingNet情绪识别模型的预训练文件进行情绪识别 前言&#xff1a;本文只介绍如何应用ResMaskingNet模型进行情绪识别应用&#xff0c;对于ResMaskingNet的模型架构以及训练过程并不做详细介绍 Paper:https://ieeexplore.ieee.org/document/9411919 Code:https://git…

【Redis学习 | 第1篇】Redis介绍+下载+服务启动与停止

文章目录 1. Redis介绍2. Redis入门2.1 Redis简介2.2 Redis下载2.3 Redis服务启动与停止2.4 redis设置密码2.5 redis 如何支持远程连接 1. Redis介绍 Redis是一个基于内存的 key-value 结构数据库。 基于内存存储&#xff0c;读写性能高适合存储热点数据&#xff08;热点商品、…

大厂进阶四:React源码解析之Fiber架构

本文主要内容&#xff1a; 1、React Concurrent 2、React15架构 3、React16架构 4、Fiber架构 5、任务调度循环和fiber构造循环区别 一、React Concurrent React在解决CPU卡顿是会用到React Concurrent的概念&#xff0c;它是React中的一个重要特性和模块&#xff0c;主要的…

Android Basis - 密钥和ID认证

书读百遍其义自现&#xff0c;知识点多复习&#xff0c;看到的越多&#xff0c;理解的也越是深刻。也许此时我看到的点是点&#xff0c;十天半个月之后回头看时可能就是新的点或者线了&#xff0c;写博客也是&#xff0c;越写越深刻。 遇到KeyAttestation在gms中的错误 在cts…

EVAL长度突破限制

目录 突破15位限制 代码 绕过方式 第一种&#xff08;使用echo执行&#xff09; 第二种&#xff08;使用file_get_content追加文件后进行问件包含&#xff09; 第三种&#xff08;使用usort可变长参数&#xff09; 突破7位限制 第一种&#xff08;可以使用>创建文件…