fix(admin): restore Vite admin source files
This commit is contained in:
1 parent
8fe3a89c36
commit
ac5c0b2dd7
114 files changed
+17272
No files matched your search
@@ -0,0 +1,185 @@
|
||||
<template>
|
||||
<div class="aksrt-repeater">
|
||||
<div v-if="modelValue && modelValue.length" class="aksrt-repeater__list">
|
||||
<div
|
||||
v-for="(item, index) in modelValue"
|
||||
:key="index"
|
||||
class="aksrt-repeater__item"
|
||||
>
|
||||
<div class="aksrt-repeater__item-header">
|
||||
<span class="aksrt-repeater__item-index">#{{ index + 1 }}</span>
|
||||
<div class="aksrt-repeater__item-actions">
|
||||
<n-button
|
||||
v-if="index > 0"
|
||||
size="tiny"
|
||||
quaternary
|
||||
@click="moveUp(index)"
|
||||
>
|
||||
<FaIcon icon="fas fa-arrow-up" :size="12" />
|
||||
</n-button>
|
||||
<n-button
|
||||
v-if="index < modelValue.length - 1"
|
||||
size="tiny"
|
||||
quaternary
|
||||
@click="moveDown(index)"
|
||||
>
|
||||
<FaIcon icon="fas fa-arrow-down" :size="12" />
|
||||
</n-button>
|
||||
<n-button
|
||||
size="tiny"
|
||||
quaternary
|
||||
type="error"
|
||||
@click="removeItem(index)"
|
||||
>
|
||||
<FaIcon icon="fas fa-trash" :size="12" />
|
||||
</n-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="aksrt-repeater__item-fields">
|
||||
<AksField
|
||||
v-for="field in fields"
|
||||
:key="field.key"
|
||||
:schema="field"
|
||||
:model-value="item[field.key]"
|
||||
@update:model-value="updateField(index, field.key, $event)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="aksrt-repeater__empty">
|
||||
<FaIcon icon="fas fa-inbox" :size="20" />
|
||||
<span>{{ emptyText || '暂无数据' }}</span>
|
||||
</div>
|
||||
|
||||
<n-button
|
||||
size="small"
|
||||
dashed
|
||||
block
|
||||
class="aksrt-repeater__add"
|
||||
@click="addItem"
|
||||
>
|
||||
<FaIcon icon="fas fa-plus" :size="12" />
|
||||
{{ addText || '添加项目' }}
|
||||
</n-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
/**
|
||||
* AksRepeaterField — dynamic add/remove rows of structured data.
|
||||
* Each row renders fields based on the provided field schemas.
|
||||
*/
|
||||
import AksField from './AksField.vue'
|
||||
import FaIcon from './FaIcon.vue'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: { type: Array, default: () => [] },
|
||||
/** Array of field schemas for each row */
|
||||
fields: { type: Array, required: true },
|
||||
/** Default value for a new item */
|
||||
defaults: { type: Object, default: () => ({}) },
|
||||
emptyText: { type: String, default: '' },
|
||||
addText: { type: String, default: '' },
|
||||
maxItems: { type: Number, default: Infinity },
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
function createItem() {
|
||||
const item = {}
|
||||
for (const field of props.fields) {
|
||||
item[field.key] = field.default ?? (field.type === 'switch' ? false : '')
|
||||
}
|
||||
return { ...item, ...props.defaults }
|
||||
}
|
||||
|
||||
function addItem() {
|
||||
if (props.modelValue.length >= props.maxItems) return
|
||||
const list = [...props.modelValue, createItem()]
|
||||
emit('update:modelValue', list)
|
||||
}
|
||||
|
||||
function removeItem(index) {
|
||||
const list = props.modelValue.filter((_, i) => i !== index)
|
||||
emit('update:modelValue', list)
|
||||
}
|
||||
|
||||
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) {
|
||||
if (index >= props.modelValue.length - 1) return
|
||||
const list = [...props.modelValue]
|
||||
;[list[index], list[index + 1]] = [list[index + 1], list[index]]
|
||||
emit('update:modelValue', list)
|
||||
}
|
||||
|
||||
function updateField(index, key, value) {
|
||||
const list = [...props.modelValue]
|
||||
list[index] = { ...list[index], [key]: value }
|
||||
emit('update:modelValue', list)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.aksrt-repeater__list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.aksrt-repeater__item {
|
||||
border: 1px solid #eef1f6;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
background: #fafbfc;
|
||||
}
|
||||
|
||||
.aksrt-repeater__item-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.aksrt-repeater__item-index {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #1890ff;
|
||||
background: #e6f4ff;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.aksrt-repeater__item-actions {
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.aksrt-repeater__item-fields {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.aksrt-repeater__empty {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 24px;
|
||||
color: #c0c4cc;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.aksrt-repeater__add {
|
||||
margin-top: 4px;
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user