Редактируемые ячейки
Слот bodyCell подменяет содержимое ячейки: по клику значение превращается в поле ввода, по подтверждению — обратно в текст.
vue
<script setup lang="ts">
import type { NTable } from '@central-design-system/components';
import { CdsButton, CdsFunctionalButton, CdsTable, CdsTextInput, CdsTooltip } from '@central-design-system/components';
import type { Ref, UnwrapRef } from 'vue';
import { computed, reactive, ref } from 'vue';
type TDataItem = { key: string; name: string; age: number; address: string };
const columns: NTable.NColumn.TColumns<TDataItem> = [
{ title: 'Имя', dataIndex: 'name', width: '30%' },
{ title: 'Возраст', dataIndex: 'age' },
{ title: 'Адрес', dataIndex: 'address' },
{ title: 'Действия', dataIndex: 'actions' }
];
const data: Ref<TDataItem[]> = ref([
{ key: '0', name: 'Иван Иванов 0', age: 28, address: 'г. Москва, ул. Ленина, д. 0' },
{ key: '1', name: 'Иван Иванов 1', age: 28, address: 'г. Москва, ул. Ленина, д. 1' }
]);
const count = computed(() => data.value.length + 1);
const editableData: UnwrapRef<Record<string, TDataItem>> = reactive({});
function edit(key: string) {
editableData[key] = data.value.filter((item) => key === item.key)[0];
}
function save(key: string) {
Object.assign(data.value.filter((item) => key === item.key)[0], editableData[key]);
delete editableData[key];
}
function remove(key: string) {
data.value = data.value.filter((item) => item.key !== key);
}
const handleAdd = () => {
data.value.push({
key: `${count.value}`,
name: `Иван Иванов ${count.value}`,
age: 28,
address: `г. Москва, ул. Ленина, д. ${count.value}`
});
};
</script>
<template>
<CdsTable :columns="columns" :data="data">
<template #toolbar>
<div class="flex justify-end">
<CdsButton text="Добавить" @click="handleAdd" />
</div>
</template>
<template #bodyCell="{ column, text, record }">
<template v-if="column.dataIndex === 'name'">
<div class="flex">
<div v-if="editableData[record.key]" class="flex items-center grow-1">
<CdsTextInput
class="grow-1"
v-model="editableData[record.key].name"
hide-messages
@keydown.enter="save(record.key)"
/>
<CdsTooltip location="right">
<template #activator="{ props }">
<CdsFunctionalButton icon="save" class="ml-2" v-bind="props" @click="save(record.key)" />
</template>
Сохранить
</CdsTooltip>
</div>
<div v-else class="flex items-center">
{{ text || ' ' }}
<CdsTooltip location="right">
<template #activator="{ props }">
<CdsFunctionalButton icon="edit" class="ml-2" v-bind="props" @click="edit(record.key)" />
</template>
Редактировать
</CdsTooltip>
</div>
</div>
</template>
<template v-else-if="column.dataIndex === 'actions'">
<CdsTooltip location="right">
<template #activator="{ props }">
<CdsFunctionalButton icon="delete" v-bind="props" @click="remove(record.key)" />
</template>
Удалить
</CdsTooltip>
</template>
</template>
</CdsTable>
</template>Редактируемые строки
Строка целиком переключается в режим правки: поля во всех ячейках, кнопки «Сохранить» и «Отменить» — в колонке действий.
vue
<script setup lang="ts">
import type { NTable } from '@central-design-system/components';
import { CdsFunctionalButton, CdsTable, CdsTextInput, CdsTooltip } from '@central-design-system/components';
import type { UnwrapRef } from 'vue';
import { reactive, ref } from 'vue';
type TDataItem = { key: string; name: string; age: number; address: string };
const columns: NTable.NColumn.TColumns<TDataItem> = [
{ title: 'Имя', dataIndex: 'name', width: '25%' },
{ title: 'Возраст', dataIndex: 'age', width: '15%' },
{ title: 'Адрес', dataIndex: 'address', width: '40%' },
{ title: 'Действия', dataIndex: 'actions' }
];
const data: TDataItem[] = [...Array(20)].map((_, i) => ({
key: i.toString(),
name: `Иван Иванов ${i}`,
age: 28,
address: `г. Москва, ул. Ленина, д. ${i}`
}));
const dataSource = ref(data);
const editableData: UnwrapRef<Record<string, TDataItem>> = reactive({});
function edit(key: string) {
editableData[key] = dataSource.value.filter((item) => key === item.key)[0];
}
function save(key: string) {
Object.assign(dataSource.value.filter((item) => key === item.key)[0], editableData[key]);
delete editableData[key];
}
function cancel(key: string) {
delete editableData[key];
}
</script>
<template>
<CdsTable :columns="columns" :data="dataSource">
<template #bodyCell="{ column, text, record }">
<template v-if="['name', 'age', 'address'].includes(String(column.dataIndex))">
<div>
<CdsTextInput
v-if="editableData[record.key]"
v-model="editableData[record.key][column.dataIndex as keyof TDataItem]"
hide-messages
/>
<template v-else>
{{ text }}
</template>
</div>
</template>
<template v-else-if="column.dataIndex === 'actions'">
<div>
<span v-if="editableData[record.key]">
<CdsTooltip>
<template #activator="{ props }">
<CdsFunctionalButton icon="save" v-bind="props" @click="save(record.key)" />
</template>
Сохранить
</CdsTooltip>
<CdsTooltip>
<template #activator="{ props }">
<CdsFunctionalButton icon="remove" v-bind="props" @click="cancel(record.key)" />
</template>
Отменить
</CdsTooltip>
</span>
<span v-else>
<CdsTooltip location="left">
<template #activator="{ props }">
<CdsFunctionalButton icon="edit" v-bind="props" @click="edit(record.key)" />
</template>
Редактировать
</CdsTooltip>
</span>
</div>
</template>
</template>
</CdsTable>
</template>Редактируемая таблица
Режим правки включается сразу для всей таблицы — удобно для массового заполнения справочников.
vue
<script setup lang="ts">
import type { NTable } from '@central-design-system/components';
import { CdsButton, CdsTable, CdsTextInput } from '@central-design-system/components';
import type { UnwrapRef } from 'vue';
import { reactive, ref } from 'vue';
type TDataItem = { key: string; name: string; age: number; address: string; quantity: number };
const columns: NTable.NColumn.TColumns<TDataItem> = [
{ title: 'Имя', dataIndex: 'name', width: '25%' },
{ title: 'Возраст', dataIndex: 'age', width: '15%' },
{ title: 'Адрес', dataIndex: 'address', width: '40%' },
{ title: 'Количество', dataIndex: 'quantity' }
];
const data: TDataItem[] = [...Array(10)].map((_, i) => ({
key: i.toString(),
name: `Иван Иванов ${i}`,
age: 32,
address: `г. Москва, ул. Ленина, д. ${i}`,
quantity: Math.round(Math.random() * 100)
}));
const dataSource = ref(data);
const editing = ref(false);
const editableData: UnwrapRef<Record<string, TDataItem>> = reactive({});
function edit() {
if (!editing.value) {
editing.value = true;
dataSource.value.forEach((item) => {
editableData[item.key] = { ...item };
});
} else {
editing.value = false;
Object.assign(dataSource.value, editableData);
}
}
function cancel() {
editing.value = false;
dataSource.value.forEach((item) => {
delete editableData[item.key];
});
}
</script>
<template>
<CdsTable :columns="columns" :data="dataSource">
<template #toolbar>
<div class="flex justify-end">
<div class="flex gap-2">
<CdsButton variant="soft" color="brand" :disabled="!editing" @click="cancel">Отменить</CdsButton>
<CdsButton @click="edit">{{ editing ? 'Сохранить' : 'Редактировать' }}</CdsButton>
</div>
</div>
</template>
<template #bodyCell="{ column, text, record }">
<div>
<CdsTextInput
v-if="editing"
v-model="editableData[record.key][column.dataIndex as keyof TDataItem]"
hide-messages
/>
<template v-else>
{{ text }}
</template>
</div>
</template>
</CdsTable>
</template>