<el-table
ref="table"
...
>
...
const table = ref();
onMounted(() => {
new Sortable(table.value.$el.querySelector('thead tr'), {
handle: 'span',
onEnd(e) {
columns.value.splice(e.newIndex, 0, columns.value.splice(e.oldIndex, 1)[0]);
},
});
});
data: () => ({
active: [ 0, -1 ],
...
methods: {
onKeyDown(e) {
const { items } = this;
const step = ({
ArrowUp: -1,
ArrowDown: 1,
})[e.key];
if (step && items.some(n => n.children?.length)) {
let iItem = this.active[0];
let iChild = this.active[1] + step;
while (!items[iItem].children?.[iChild]) {
iItem = (iItem + step + items.length) % items.length;
iChild = step === 1 ? 0 : ~-items[iItem].children?.length;
}
this.active = [ iItem, iChild ];
}
},
...
<ul>
<li v-for="(item, iItem) in items">
<ul>
<li
v-for="(child, iChild) in item.children"
:class="{ active: active[0] === iItem && active[1] === iChild }"
...
computed: {
children() {
return this.items.flatMap(n => n.children ?? []);
},
...
data: () => ({
active: null,
...
methods: {
onKeyDown({ key }) {
const { length } = this.children;
const step =
key === 'ArrowUp' ? -1 :
key === 'ArrowDown' ? 1 :
0;
if (step && length) {
const active = this.active ?? (step === 1 ? -1 : length);
this.active = Math.max(0, Math.min(length - 1, active + step));
}
},
...
<ul>
<li v-for="item in items">
<ul>
<li
v-for="child in item.children"
:class="{ active: children[active] === child }"
...
чтобы данные не терялись
v-for
назначаем элементу списка класс, который его визуально выделит. Как-то так. const filteredProducts = computed(() => {
const search = searchQuery.value.toLowerCase();
return search.length > 2
? data.value.filter(n => n.title.toLowerCase().includes(search))
: null;
});
<ul v-if="filteredProducts" class="search-result">
<li v-if="!filteredProducts.length">
<h3>ничего не найдено</h3>
</li>
<li v-for="n in filteredProducts">
<div>
<Image :src="n.image" :alt="n.title" width="40" height="40" />
</div>
<h3>{{ n.title }}</h3>
<div>
<p>{{ n.price }}</p>
</div>
</li>
</ul>
v-for
.item.isAdded = true
const quantity = ref(1)
const quantity = defineModel('quantity');
<CartItem
v-for="n in cart"
v-bind="n"
:key="n.id"
@update:quantity="updateQuantity(n, $event)"
@remove="removeFromCart(n)"
/>
const updateQuantity = (item, quantity) => item.quantity = quantity;
const totalPrice = computed(() => cart.value.reduce((acc, n) => acc + n.price * n.quantity, 0));
history: createMemoryHistory(),
Vue-router не изменяет url страницы
The memory history mode doesn't assume a browser environment and therefore doesn't interact with the URL
const status = defineModel('status');
const statuses = [ 'all', 'alive', 'dead', 'unknown' ];
<select v-model="status">
<option v-for="n in statuses">{{ n }}</option>
</select>
defineProps({
data: {
type: Array,
default: () => [],
},
});
<div v-for="n in data">
<span :class="[ 'status', n.status ]"></span>
...
</div>
.status {
border-radius: 50%;
width: 30px;
height: 30px;
&.alive { background: green; }
&.dead { background: red; }
&.unknown { background: orange; }
}
const data = ref([ ... ]);
const status = ref('all');
const filteredData = computed(() => status.value === 'all'
? data.value
: data.value.filter(n => n.status === status.value)
);
<FilterBlock v-model:status="status" />
<CardList :data="filteredData" />
BREAKING: When used on custom components,v-model
prop and event default names are changed:
- prop:
value
->modelValue
;- event:
input
->update:modelValue
;
unshift
, и ничего больше). Давайте закостылим - добавляем колонку, которая будет дублировать данный функционал, а оригинальную спрячем:const columns = [
...
{ name: 'selection' },
];
.xxx tr > :first-child {
display: none;
}
<q-table
...
table-class="xxx"
>
<template #header-cell-selection="props">
<q-th :props="props">
<q-checkbox v-model="props.selected" />
</q-th>
</template>
<template #body-cell-selection="props">
<q-td :props="props">
<q-checkbox v-model="props.selected" />
</q-td>
</template>
</q-table>
.waiting { background: yellow; }
.working { background: green; }
.completed { background: blue; }
const highlight = {
'Ожидание': 'waiting',
'Работа': 'working',
'Завершено': 'completed',
};
<el-table-column label="Процесс">
<template #default="{ row: { process } }">
<span :class="highlight[process]">{{ process }}</span>
</template>
</el-table-column>
const cellClassName = ({ column, row }) =>
column.property === 'process'
? (highlight[row.process] ?? '')
: '';
<el-table
:cell-class-name="cellClassName"
...
>
prefix-icon="calendar-icon"
const blocks = ref(Array.from({ length: 5 }, (_, i) => (-~i) ** 2));
const active = ref(0);
function next() {
active.value = (active.value + 1 + blocks.value.length) % blocks.value.length;
}
let intervalId = null;
onMounted(() => intervalId = setInterval(next, 500));
onUnmounted(() => clearInterval(intervalId));
<div
v-for="(n, i) in blocks"
v-text="n"
:class="[ 'box-item', { active: i === active } ]"
></div>
:nth-child
- не круто. Лучше сделать компонент, принимающий массив цветов и создающий блоки на его основе. Соответственно, вместо класса будет назначаться цвет фона напрямую, как-то так:<div
v-for="(n, i) in colors"
:style="{ backgroundColor: i === active ? n : '' }"
...