Implémentation de cellules éditables avec Element UI

Pour réaliser un formulaire de saisie dans un tableau, les données sont stockées côté back end sous forme de structure tabulaire. Une fois récupérées sous forme de tableau JSON, elles sont affichées dans l'interface utilisateur.

Le principe est d'utiliser l'événement cell-click pour basculer entre l'affichage du contenu textuel et un champ de saisie, en contrôlant un indicateur booléen.

<el-table :data="tableData" border @cell-click="handleCellClick">
  <el-table-column label="Élément à vérifier" align="center" prop="name" />
  <el-table-column prop="result" label="Résultat">
    <template slot-scope="{ row }">
      <el-radio v-model="row.result"
        v-for="dict in resultOptions"
        :key="dict.dictValue"
        :label="dict.dictValue">
        {{ dict.dictLabel }}
      </el-radio>
    </template>
  </el-table-column>
  <el-table-column label="Critère" align="center" prop="checkStandard" width="350" />
  <el-table-column prop="hitch" label="Défaillance">
    <template slot-scope="scope">
      <span v-if="!scope.row.isEditable" style="cursor:pointer">{{ scope.row.hitch }}</span>
      <el-input v-if="scope.row.isEditable"
        v-model="scope.row.hitch"
        placeholder="Emplacement de la défaillance"
        @blur="onCellBlur(scope.row, scope.column)"
        style="width:70%" ref="hitchRef"></el-input>
    </template>
  </el-table-column>
  <el-table-column prop="remark" label="Remarque">
    <template slot-scope="scope">
      <span v-if="!scope.row.isEditable" style="cursor:pointer">{{ scope.row.remark }}</span>
      <el-input v-if="scope.row.isEditable"
        v-model="scope.row.remark"
        placeholder="Saisir une remarque"
        @blur="onCellBlur(scope.row, scope.column)"
        style="width:70%" ref="remarkRef"></el-input>
    </template>
  </el-table-column>
</el-table>

Script correspondant :

data() {
  return {
    tableData: [],
    resultOptions: null,
    submitData: {},
    inspectId: ''
  };
},
created() {
  this.inspectId = this.$route.params?.inspectId;
  this.fetchTableData(this.inspectId);
  this.getDicts("sys_check_result").then(response => {
    this.resultOptions = response.data;
  });
},
methods: {
  fetchTableData(inspectId) {
    this.loading = true;
    listInspectItem(inspectId).then(response => {
      this.tableData = response.data;
      this.tableData.forEach(item => {
        this.$set(item, 'isEditable', false);
      });
      this.loading = false;
    });
  },
  handleCellClick(row, column) {
    if (["remark", "result", "hitch"].includes(column.property)) {
      this.$set(row, 'isEditable', true);
    }
    this.tableData = [...this.tableData]; // Forcer la mise à jour du rendu
    this.$nextTick(() => {
      if (column.property === "remark" && this.$refs.remarkRef) {
        this.$refs.remarkRef.focus();
      } else if (this.$refs.hitchRef) {
        this.$refs.hitchRef.focus();
      }
    });
  },
  onCellBlur(row, column) {
    this.$set(row, 'isEditable', false);
  },
  submitForm(row) {
    this.loading = true;
    const payload = {
      inspectId: this.inspectId,
      ext1: JSON.stringify(this.tableData)
    };
    updateInspect(payload).then(response => {
      if (response.code === 200) {
        this.msgSuccess("Modification réussie");
        this.loading = false;
        this.fetchTableData(this.inspectId);
      } else {
        this.msgError(response.msg);
      }
    });
  }
}

Variante 1 : Utilisation de cell-dblclick et d'une colonne d'index

Template :

<el-table
  :data="tableData"
  border
  @cell-dblclick="onDoubleClick"
  highlight-current-row
  :cell-style="defineRowStyle"
  style="width: 100%">
  <el-table-column type="index" label="#" width="50"></el-table-column>
  <el-table-column prop="username" label="Responsable" width="180">
    <template v-slot="{ row, column, $index }">
      <el-input
        :ref="$index"
        v-show="isCurrentCell(row, column, $index)"
        v-model="row.username"
        @blur="handleBlur"
        placeholder="Saisir le nom">
      </el-input>
      <span v-show="!isCurrentCell(row, column, $index)">{{ row.username }}</span>
    </template>
  </el-table-column>
</el-table>

Script :

methods: {
  onDoubleClick(row, column, cell, event) {
    if (column.property !== 'username') return;
    this.selectedRowIndex = row.rowIndex;
    this.selectedColProperty = column.property;
  },
  defineRowStyle(obj) {
    Object.defineProperty(obj.row, 'rowIndex', {
      value: obj.rowIndex,
      writable: true,
      enumerable: false
    });
  },
  isCurrentCell(row, col, index) {
    return row.rowIndex === this.selectedRowIndex && col.property === this.selectedColProperty;
  }
}

Variante 2 : Gestion de l'état d'édition par propriété dynamique

Template :

<el-table
  :data="tableData"
  border
  highlight-current-row
  style="width: 100%">
  <el-table-column type="index" label="#" width="50"></el-table-column>
  <el-table-column prop="username" label="Responsable" width="180">
    <template v-slot="{ row, column, $index }">
      <el-input
        :ref="$index"
        v-show="row[`${column.property}Editable`]"
        v-model="row.username"
        placeholder="Saisir le nom">
      </el-input>
      <span
        @dblclick="activateEdit($index, column)"
        v-show="!row[`${column.property}Editable`]">
        {{ row.username }}
      </span>
    </template>
  </el-table-column>
</el-table>

Script :

data() {
  return {
    tableData: [{
      username: 'lily',
      usernameEditable: false
    }, {
      username: 'tony',
      usernameEditable: false
    }]
  };
},
methods: {
  activateEdit(rowIndex, col) {
    this.$set(this.tableData[rowIndex], `${col.property}Editable`, true);
  }
}

Étiquettes: Element UI Vue.js Vuex table editing cell-dblclick

Publié le 23 juillet à 14h52