首页 前端知识 Ant design vue的表格双击编辑功能(即双击开始编辑并自动获得焦点,失去焦点时完成编辑)

Ant design vue的表格双击编辑功能(即双击开始编辑并自动获得焦点,失去焦点时完成编辑)

2024-09-01 00:09:55 前端知识 前端哥 935 968 我要收藏

本文基于Ant Design Vue官方网站的表格(可编辑单元格)(表格 Table - Ant Design Vue (antdv.com))中的样板代码获得双击编辑且获得焦点、失去焦点时完成编辑的功能。

要点:

(一)基本表格

(1)双击时候实现编辑,则在模板中非编辑状态单元格标签上添加dbclick事件。

(2)在<a-input>标签中,添加:ref="setMyEditingInputRef"属性【注:setMyEditingInputRef可以随意取名字,只要和script标签中对应方法名相同即可】,并在<script setup>标签中添加同名方法,在该方法中将标签引用临时保存起来。(注意ref前加上冒号:)

(3)在edit方法中,添加获得焦点的方法,也就是每次进入到编辑状态时,输入框自动获得焦点。(这一步很关键)。

(4)在<a-input>标签中添加blur事件,失去焦点时调用save方法,从而将<a-input>中的数据保存到表格数据中。同时,a-input组件将被销毁,重新显示表格的文字格式单元格。

(二)可展开表格

<template>
  <a-space align="center" style="margin-bottom: 16px">
    CheckStrictly:
    <a-switch v-model:checked="rowSelection.checkStrictly"></a-switch>
  </a-space>
  <a-table :columns="columns" :data-source="data"  Bordered :row-selection="rowSelection" >
    <template #bodyCell="{ column, text, record }">
      <template v-if="column.dataIndex === 'name'||column.dataIndex === 'age'||column.dataIndex === 'address'">
        <div class="editable-cell">
          <div v-if="editableData[record.key+column.key]" class="editable-cell-input-wrapper">
            <a-input v-model:value="editableData[record.key+column.key][column.dataIndex]" 
            :ref="setMyEditingInputRef"  @blur="save(record.key,column.key)"  @pressEnter="save(record.key,column.key)"  />
            <check-outlined class="editable-cell-icon-check" @click="save(record.key,column.key)" />
          </div>
          <div v-else class="editable-cell-text-wrapper" @dblclick="edit(record.key,column.key)">
            {{ text || ' ' }}
            <edit-outlined class="editable-cell-icon" @click="edit(record.key,column.key)"  ></edit-outlined>
          </div>
        </div>
      </template>
      <template v-else-if="column.dataIndex === 'operation'">
        <a-popconfirm
          v-if="data.length"
          title="Sure to delete?"
          @confirm="onDelete(record.key)"
        >
          <a>Delete</a>
        </a-popconfirm>
      </template>
    </template>
  
  </a-table>
</template>
<script lang="ts" setup>
import { ref,UnwrapRef,reactive,nextTick } from 'vue';
import _ from 'lodash-es'
const columns = [
  {
    title: 'Name',
    dataIndex: 'name',
    key: 'name',
  },
  {
    title: 'Age',
    dataIndex: 'age',
    key: 'age',
    width: '12%',
  },
  {
    title: 'Address',
    dataIndex: 'address',
    width: '30%',
    key: 'address',
  },
];

interface DataItem {
  key: string;
  name: string;
  age: number;
  address: string;
  children?: DataItem[];
}

let data= reactive<DataItem[] >([
  {
    key: '1',
    name: 'John Brown sr.',
    age: 60,
    address: 'New York No. 1 Lake Park',
    children: [
      {
        key: '11',
        name: 'John Brown',
        age: 42,
        address: 'New York No. 2 Lake Park',
      },
      {
        key: '12',
        name: 'John Brown jr.',
        age: 30,
        address: 'New York No. 3 Lake Park',
        children: [
          {
            key: '121',
            name: 'Jimmy Brown',
            age: 16,
            address: 'New York No. 3 Lake Park',
          },
        ],
      },
      {
        key: '13',
        name: 'Jim Green sr.',
        age: 72,
        address: 'London No. 1 Lake Park',
        children: [
          {
            key: '131',
            name: 'Jim Green',
            age: 42,
            address: 'London No. 2 Lake Park',
            children: [
              {
                key: '1311',
                name: 'Jim Green jr.',
                age: 25,
                address: 'London No. 3 Lake Park',
              },
              {
                key: '1312',
                name: 'Jimmy Green sr.',
                age: 18,
                address: 'London No. 4 Lake Park',
              },
            ],
          },
        ],
      },
    ],
  },
  {
    key: '2',
    name: 'Joe Black',
    age: 32,
    address: 'Sidney No. 1 Lake Park',
  },
]);
const editableData: UnwrapRef<Record<string, DataItem>> = reactive({});


const myEditingInputRef=ref<HTMLInputElement>()
const setMyEditingInputRef=(el:any)=>{
    if(el){
        myEditingInputRef.value=el
    }
}

/**
 * 从层级数据结构(含children属性)DataItem[]中查询含有指定key的元素,该元素可能位于本级,也可能位于子级别(children)或更深层次中
 * @param data 
 * @param key 
 * @returns {DataItem|undefined} 如果没有找到,则返回undefined,如果找到,则返回实际元素
 */
function getItemFromRankedData(data:DataItem[],key:string):DataItem|undefined
{
  if(data===undefined)
    return undefined;
  for(let it of data)
  {
    if(it.key===key)
      return it;
    if(it.children===undefined)
      continue;

      //使用递归算法在子级、孙级...中查找
    let result= getItemFromRankedData(it.children,key);
    //如果找到,则返回找到的元素;如果未找到,则忽略,进入下一次循环
    if(result!=undefined)
      return result;
  }

  return undefined;
}




const edit = (rowKey: string,colKey:string) => {
  let dataItem:DataItem|undefined=getItemFromRankedData(data,rowKey);//   data.filter(item => key === item.key)[0];
  if(dataItem!==undefined)
  {
    //将当前行的数据复制到临时数组editableData中
    editableData[rowKey+colKey] = _.cloneDeep(dataItem);
    nextTick(()=>{
        const input = myEditingInputRef.value;
        input?.focus();
    })
  }
};


const save = (rowKey: string,colKey:string) => {
 let dataItem= getItemFromRankedData(data,rowKey)
  if(dataItem!=undefined)
  {
    Object.assign(dataItem, editableData[rowKey+colKey]);
    delete editableData[rowKey+colKey];
    console.log(data)
  }
};

const onDelete = (key: string) => {
  data = data.filter(item => item.key !== key);
};

const rowSelection = ref({
  checkStrictly: false,
  onChange: (selectedRowKeys: (string | number)[], selectedRows: DataItem[]) => {
    console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
  },
  onSelect: (record: DataItem, selected: boolean, selectedRows: DataItem[]) => {
    console.log(record, selected, selectedRows);
  },
  onSelectAll: (selected: boolean, selectedRows: DataItem[], changeRows: DataItem[]) => {
    console.log(selected, selectedRows, changeRows);
  },
});
</script>

(三)嵌套子表格

<script lang="ts" setup>
import { reactive, UnwrapRef, ref, nextTick } from 'vue'
import { DownOutlined, EditOutlined, CheckOutlined } from '@ant-design/icons-vue';
import _ from 'lodash-es'



const columns = [
  { title: 'Name', dataIndex: 'Name', id: 'Name', width: '20%' },
  { title: 'Number', dataIndex: 'Number', id: 'Number', width: '20%' },
  { title: 'Count', dataIndex: 'Count', id: 'Count', width: '20%' },
  { title: 'Date', dataIndex: 'createdAt', id: 'createdAt', width: 'auto' },
  { title: 'Action', id: 'operation', width: 'auto' },
];
const innerColumns = [
  { title: 'Name', dataIndex: 'Name', id: 'Name', width: '30%' },
  { title: 'Number', dataIndex: 'Number', id: 'Number', width: '30%' },
  { title: 'MainType', dataIndex: 'MainType', id: 'MainType', width: 'auto' },

];


type SonDataItem = {
  id: string;
  Name: string;
  Number: string;
  MainType: string;
}
type DataItem = {
  id: string;
  Name: string;
  Number: string;
  Count:number;
  sons: SonDataItem[]
}
const dataList: UnwrapRef<DataItem[]> = reactive<DataItem[]>([
  {
    id: 'sdfg01',
    Name: 'LiSi',
    Number: 'sdlgdlkldkg',  
    Count:3,
    sons: [
      {
        id: 'sdfgggsf44001',
        Name: 'WangWu',
        Number: 'skfjkgj',
        MainType: 'HiHi'
      },
      {
        id: 'sdfgggsf44002',
        Name: 'Zhaoyun',
        Number: 'skfjkg24234j',
        MainType: 'whyst'
      }

    ]
  },
  {
    id: 'sdfg02',
    Name: 'LiuYu',
    Number: 'sdlgdlkldkg98',
   Count:5,
    sons: [
      {
        id: 'sdfgggsf44005',
        Name: 'BaiShu',
        Number: 'skfjkgj',
        MainType: '984'
      },
      {
        id: 'sdfgggsf44006',
        Name: 'Wuken',
        Number: 'skfjkg24234j',
        MainType: '345'
      }
     ]
  }
]);

/**********Parent Bridge Item--DOwn********** */

const editableItemData: UnwrapRef<Record<string, DataItem>> = reactive({});
const myEditingInputRef = ref<HTMLInputElement>()
const setMyEditingInputRef = (el: any) => {
  if (el) {
    myEditingInputRef.value = el
  }
}
const editParent = (rowKey: string,colKey:string) => {
  editableItemData[rowKey+colKey] = _.cloneDeep(dataList.filter(item => rowKey === item.id)[0]);

  console.log(myEditingInputRef)
  nextTick(() => {
    console.log(myEditingInputRef)
    const input = myEditingInputRef.value;
    input?.focus();
  })

};

const saveParent = (rowKey: string,colKey:string) => {
  Object.assign(dataList.filter(item => rowKey === item.id)[0], editableItemData[rowKey+colKey]);
  delete editableItemData[rowKey+colKey];
  console.log(dataList)
};


/**********Son BridgeItem--Down********** */
const editableSonItemData: UnwrapRef<Record<string, SonDataItem>> = reactive({});
const myEditingSonBridgeInputRef = ref<HTMLInputElement>()
const setMyEditingSonBridgeInputRef = (el: any) => {
  if (el) {
    myEditingSonBridgeInputRef.value = el
  }
}
const editSon = (rowId: string,colId:string) => {
  editableSonItemData[rowId+colId] = _.cloneDeep((dataList.filter(item => item.sons.findIndex(x => x.id === rowId) >= 0)[0]).sons.find(item => rowId == item.id));
  nextTick(() => {
    const input = myEditingSonBridgeInputRef.value;
    input?.focus();
  })

};
const saveSon = (rowId: string,colId:string) => {
  Object.assign((dataList.filter(item => item.sons.find(x => x.id === rowId) !== undefined)[0]).sons.filter(item => rowId == item.id)[0], editableSonItemData[rowId+colId]);
  delete editableSonItemData[rowId+colId];

};


</script>

<template>
  <a-table :columns="columns" rowkey="id" :data-source="dataList" bordered class="components-table-demo-nested">

    <!-- 本级表格模板 -->
    <template #bodyCell="{ column, record, text }">
      <template v-if="column.id === 'Number' || column.id === 'Name'">
        <div class="editable-cell">
          <div v-if="editableItemData[record.id+column.id]" class="editable-cell-input-wrapper">
            <a-input v-model:value="editableItemData[record.id+column.id][column.id]" :ref="setMyEditingInputRef"
              @blur="saveParent(record.id,column.id)" @pressEnter="saveParent(record.id,column.id)" />
            <check-outlined class="editable-cell-icon-check" @click="saveParent(record.id,column.id)" />
          </div>
          <div v-else @dblclick="editParent(record.id,column.id)" class="editable-cell-input-wrapper">
            {{ text }}
            <edit-outlined class="editable-cell-icon" @click="editParent(record.id,column.id)"  ></edit-outlined>
          </div>
        </div>
      </template>
    </template>
    <!-- 子级表格模板 -->
    <template #expandedRowRender="{ record }">

      <a-table :columns="innerColumns" rowkey="id" :data-source="record.sons" bordered :pagination="false">
        <template #bodyCell="{ column, record, text }">

          <template v-if="column.id === 'Name' || column.id === 'Number'">

            <div class="editable-cell">
              <div v-if="editableSonItemData[record.id+column.id]" class="editable-cell-input-wrapper">
                <a-input v-model:value="editableSonItemData[record.id+column.id][column.id]"
                  :ref="setMyEditingSonBridgeInputRef" @blur="saveSon(record.id,column.id)" @pressEnter="saveSon(record.id,column.id)" />
                <check-outlined class="editable-cell-icon-check" @click="saveSon(record.id,column.id)" />
              </div>
              <div v-else @dblclick="editSon(record.id,column.id)" class="editable-cell-text-wrapper">
                {{ text || '' }}
                <edit-outlined class="editable-cell-icon" @click="editSon(record.id,column.id)"></edit-outlined>
              </div>
            </div>

          </template>
          <template v-else-if="column.id === 'operation'">
            <span class="table-operation">
              <a>Pause</a>
              <a>Stop</a>
              <a-dropdown>
                <template #overlay>
                  <a-menu>
                    <a-menu-item>Action 1</a-menu-item>
                    <a-menu-item>Action 2</a-menu-item>
                  </a-menu>
                </template>
                <a>
                  More
                  <down-outlined />
                </a>
              </a-dropdown>
            </span>
          </template>
          <template v-else>
            <span :style="{ color: 'blue' }">{{ record[column.id] }}</span>
          </template>

        </template>
      </a-table>
    </template>
  </a-table>
</template>

<style lang="less" scoped>
.editable-cell {
  position: relative;

  .editable-cell-input-wrapper,
  .editable-cell-text-wrapper {
    padding-right: 24px;
  }

  .editable-cell-text-wrapper {
    padding: 5px 24px 5px 5px;
  }

  .editable-cell-icon,
  .editable-cell-icon-check {
    position: absolute;
    right: 0;
    width: 20px;
    cursor: pointer;
  }

  .editable-cell-icon {
    margin-top: 4px;
    display: none;
  }

  .editable-cell-icon-check {
    line-height: 28px;
  }

  .editable-cell-icon:hover,
  .editable-cell-icon-check:hover {
    color: #108ee9;
  }

  .editable-add-btn {
    margin-bottom: 8px;
  }
}

.editable-cell:hover .editable-cell-icon {
  display: inline-block;
}
</style>

转载请注明出处或者链接地址:https://www.qianduange.cn//article/17394.html
标签
评论
发布的文章

Spring MVC-JSON

2024-06-02 09:06:53

大家推荐的文章
会员中心 联系我 留言建议 回顶部
复制成功!