129 lines
3.8 KiB
Vue
129 lines
3.8 KiB
Vue
<template>
|
|
<div class="aksrt-array-table">
|
|
<n-data-table
|
|
:columns="tableColumns"
|
|
:data="modelValue || []"
|
|
:bordered="false"
|
|
size="small"
|
|
max-height="400"
|
|
/>
|
|
|
|
<n-button
|
|
size="small"
|
|
dashed
|
|
block
|
|
class="aksrt-array-table__add"
|
|
@click="addItem"
|
|
>
|
|
<FaIcon icon="fa fas fa-plus" :size="12" />
|
|
{{ addText || '添加行' }}
|
|
</n-button>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
/**
|
|
* AksArrayTable — table-style editor for array data.
|
|
* Renders rows in a data table with inline editing.
|
|
*/
|
|
import { computed, h } from 'vue'
|
|
import { NInput, NSwitch, NButton, NSelect, NInputNumber } from 'naive-ui'
|
|
import FaIcon from './FaIcon.vue'
|
|
|
|
const props = defineProps({
|
|
modelValue: { type: Array, default: () => [] },
|
|
/** Column definitions: [{ key, label, type?, options?, width? }] */
|
|
columns: { type: Array, required: true },
|
|
defaults: { type: Object, default: () => ({}) },
|
|
addText: { type: String, default: '' },
|
|
maxRows: { type: Number, default: Infinity },
|
|
})
|
|
|
|
const emit = defineEmits(['update:modelValue'])
|
|
|
|
function updateCell(rowIndex, key, value) {
|
|
const list = [...(props.modelValue || [])]
|
|
list[rowIndex] = { ...list[rowIndex], [key]: value }
|
|
emit('update:modelValue', list)
|
|
}
|
|
|
|
function removeItem(index) {
|
|
const list = (props.modelValue || []).filter((_, i) => i !== index)
|
|
emit('update:modelValue', list)
|
|
}
|
|
|
|
function addItem() {
|
|
if ((props.modelValue || []).length >= props.maxRows) return
|
|
const item = {}
|
|
for (const col of props.columns) {
|
|
item[col.key] = col.default ?? (col.type === 'switch' ? false : '')
|
|
}
|
|
emit('update:modelValue', [...(props.modelValue || []), { ...item, ...props.defaults }])
|
|
}
|
|
|
|
function moveUp(index) {
|
|
if (index <= 0) return
|
|
const list = [...(props.modelValue || [])]
|
|
;[list[index - 1], list[index]] = [list[index], list[index - 1]]
|
|
emit('update:modelValue', list)
|
|
}
|
|
|
|
function moveDown(index) {
|
|
const list = [...(props.modelValue || [])]
|
|
if (index >= list.length - 1) return
|
|
;[list[index], list[index + 1]] = [list[index + 1], list[index]]
|
|
emit('update:modelValue', list)
|
|
}
|
|
|
|
const tableColumns = computed(() => {
|
|
const cols = props.columns.map(col => ({
|
|
title: col.label,
|
|
key: col.key,
|
|
width: col.width || 150,
|
|
render(row, rowIndex) {
|
|
const val = row[col.key]
|
|
const update = (v) => updateCell(rowIndex, col.key, v)
|
|
|
|
if (col.type === 'switch') {
|
|
return h(NSwitch, { value: val, size: 'small', 'onUpdate:value': update })
|
|
}
|
|
if (col.type === 'select') {
|
|
return h(NSelect, { value: val, options: col.options || [], size: 'small', 'onUpdate:value': update })
|
|
}
|
|
if (col.type === 'number') {
|
|
return h(NInputNumber, { value: val, size: 'small', style: 'width:100%', 'onUpdate:value': update })
|
|
}
|
|
return h(NInput, { value: val, size: 'small', 'onUpdate:value': update })
|
|
},
|
|
}))
|
|
|
|
// Add actions column
|
|
cols.push({
|
|
title: '操作',
|
|
key: '_actions',
|
|
width: 100,
|
|
render(row, rowIndex) {
|
|
return h('div', { style: 'display:flex;gap:2px' }, [
|
|
rowIndex > 0 ? h(NButton, { size: 'tiny', quaternary: true, onClick: () => moveUp(rowIndex) }, {
|
|
default: () => h(FaIcon, { icon: 'fa fas fa-arrow-up', size: 12 }),
|
|
}) : null,
|
|
rowIndex < (props.modelValue?.length || 0) - 1 ? h(NButton, { size: 'tiny', quaternary: true, onClick: () => moveDown(rowIndex) }, {
|
|
default: () => h(FaIcon, { icon: 'fa fas fa-arrow-down', size: 12 }),
|
|
}) : null,
|
|
h(NButton, { size: 'tiny', quaternary: true, type: 'error', onClick: () => removeItem(rowIndex) }, {
|
|
default: () => h(FaIcon, { icon: 'fa fas fa-trash-alt', size: 12 }),
|
|
}),
|
|
])
|
|
},
|
|
})
|
|
|
|
return cols
|
|
})
|
|
</script>
|
|
|
|
<style scoped>
|
|
.aksrt-array-table__add {
|
|
margin-top: 10px;
|
|
}
|
|
</style>
|