7f923cd612
Frontend CI / build-and-check (push) Successful in 26s
🚀 Create and publish a Docker image / Detect changes in backend and frontend (push) Successful in 6s
🚀 Create and publish a Docker image / Build & publish backend image (push) Successful in 13s
🚀 Create and publish a Docker image / Build & publish frontend image (push) Successful in 35s
🚀 Create and publish a Docker image / Update stack on Portainer (push) Successful in 3s
Backend CI / build-and-test (pull_request) Successful in 1m6s
Frontend CI / build-and-check (pull_request) Successful in 49s
Frontend Playwright / e2e (pull_request) Successful in 10m53s
78 lines
1.7 KiB
Vue
78 lines
1.7 KiB
Vue
<script setup lang="ts" generic="TRow extends object">
|
|
type Column = { key: string; label: string; align?: 'left' | 'center' | 'right' }
|
|
|
|
defineProps<{
|
|
columns: Column[]
|
|
rows: TRow[]
|
|
}>()
|
|
|
|
defineSlots<Record<string, (props: { row: TRow; value: unknown }) => unknown>>()
|
|
|
|
function getCell(row: TRow, key: string): unknown {
|
|
return (row as Record<string, unknown>)[key]
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="table-wrap">
|
|
<table class="data-table">
|
|
<thead>
|
|
<tr>
|
|
<th v-for="col in columns" :key="col.key" :class="`align-${col.align ?? 'left'}`">
|
|
{{ col.label }}
|
|
</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr v-for="(row, i) in rows" :key="i">
|
|
<td v-for="col in columns" :key="col.key" :class="`align-${col.align ?? 'left'}`">
|
|
<slot :name="col.key" :row="row" :value="getCell(row, col.key)">
|
|
{{ getCell(row, col.key) }}
|
|
</slot>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.table-wrap {
|
|
overflow-x: auto;
|
|
border-radius: var(--radius-md);
|
|
}
|
|
.data-table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
font-size: 14px;
|
|
}
|
|
.data-table th {
|
|
background: var(--color-white-a60);
|
|
border-bottom: 2px solid var(--color-border-glass);
|
|
padding: 10px 14px;
|
|
font-weight: 600;
|
|
color: var(--color-text-secondary);
|
|
white-space: nowrap;
|
|
}
|
|
.data-table td {
|
|
padding: 10px 14px;
|
|
border-bottom: 1px solid var(--color-border-glass);
|
|
color: var(--color-text);
|
|
}
|
|
.data-table tbody tr:last-child td {
|
|
border-bottom: none;
|
|
}
|
|
.data-table tbody tr:hover td {
|
|
background: var(--color-primary-a05);
|
|
}
|
|
.align-left {
|
|
text-align: left;
|
|
}
|
|
.align-center {
|
|
text-align: center;
|
|
}
|
|
.align-right {
|
|
text-align: right;
|
|
}
|
|
</style>
|