147 lines
2.8 KiB
Vue
147 lines
2.8 KiB
Vue
<template>
|
|
<div
|
|
:class="[
|
|
'property-item',
|
|
hovered ? 'hovered' : '',
|
|
selected ? 'selected' : '',
|
|
]"
|
|
:title="propertyKey"
|
|
@click.stop="selectProperty"
|
|
@mouseover.stop="hovered = true"
|
|
@mouseout.stop="hovered = false"
|
|
>
|
|
<div class="property-header">
|
|
<div class="property-info">
|
|
<span class="property-name">{{ property.title || propertyKey }}</span>
|
|
<span class="property-type">{{ property.type }}</span>
|
|
</div>
|
|
<div class="property-actions" v-if="!store.readOnly">
|
|
<button class="btn btn-xs btn-outline-secondary" @click.stop="editProperty">
|
|
编辑
|
|
</button>
|
|
<button class="btn btn-xs btn-outline-danger" @click.stop="deleteProperty">
|
|
删除
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="property-details" v-if="selected">
|
|
<PropertyEditor
|
|
:property="property"
|
|
:property-key="propertyKey"
|
|
@update="updateProperty"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import PropertyEditor from "./PropertyEditor.vue";
|
|
import { ref } from "vue";
|
|
import { useSchemaStore } from "../store";
|
|
|
|
const props = defineProps({
|
|
property: {
|
|
type: Object,
|
|
required: true
|
|
},
|
|
propertyKey: {
|
|
type: String,
|
|
required: true
|
|
},
|
|
selected: {
|
|
type: Boolean,
|
|
default: false
|
|
}
|
|
});
|
|
|
|
const emit = defineEmits(['select', 'edit', 'delete']);
|
|
|
|
const store = useSchemaStore();
|
|
const hovered = ref(false);
|
|
|
|
function selectProperty() {
|
|
emit('select', props.propertyKey);
|
|
}
|
|
|
|
function editProperty() {
|
|
emit('edit', props.propertyKey);
|
|
}
|
|
|
|
function deleteProperty() {
|
|
emit('delete', props.propertyKey);
|
|
}
|
|
|
|
function updateProperty(updatedProperty) {
|
|
store.updateProperty(props.propertyKey, updatedProperty);
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.property-item {
|
|
background-color: var(--fg-color);
|
|
border: 1px solid var(--border-color);
|
|
border-radius: var(--border-radius);
|
|
padding: 12px;
|
|
margin-bottom: 8px;
|
|
cursor: pointer;
|
|
transition: all 0.2s ease;
|
|
|
|
&:hover,
|
|
&.hovered {
|
|
border-color: var(--border-primary);
|
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
&.selected {
|
|
border-color: var(--border-primary);
|
|
background-color: var(--bg-light-gray);
|
|
}
|
|
|
|
.property-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 8px;
|
|
|
|
.property-info {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
|
|
.property-name {
|
|
font-weight: 600;
|
|
color: var(--text-color);
|
|
}
|
|
|
|
.property-type {
|
|
background-color: var(--bg-gray);
|
|
color: var(--text-muted);
|
|
padding: 2px 6px;
|
|
border-radius: var(--border-radius-sm);
|
|
font-size: var(--text-xs);
|
|
}
|
|
}
|
|
|
|
.property-actions {
|
|
display: flex;
|
|
gap: 4px;
|
|
opacity: 0;
|
|
transition: opacity 0.2s ease;
|
|
}
|
|
}
|
|
|
|
&:hover .property-actions,
|
|
&.selected .property-actions {
|
|
opacity: 1;
|
|
}
|
|
|
|
.property-details {
|
|
border-top: 1px solid var(--border-color);
|
|
padding-top: 12px;
|
|
margin-top: 8px;
|
|
}
|
|
}
|
|
</style>
|
|
|
|
|