Browse Source
- 添加 BayEditor组件用于编辑货架列配置 - 在 PropertyPanel 中集成 BayEditor组件 - 更新 RackPropertySetter以支持货架列配置编辑 - 优化 PropertyPanel 中的数据更新逻辑master
4 changed files with 312 additions and 7 deletions
@ -0,0 +1,302 @@ |
|||
<script setup lang="ts"> |
|||
import { computed, reactive } from "vue"; |
|||
import { ElButton, ElDivider, ElFormItem, ElIcon, ElInputNumber, useFormItem } from "element-plus"; |
|||
import { CopyDocument, Delete } from "@element-plus/icons-vue"; |
|||
import { Typeof } from "@ease-forge/shared"; |
|||
|
|||
defineOptions({ |
|||
name: 'BayEditor', |
|||
}); |
|||
|
|||
interface DtBay { |
|||
/** 列的宽度 */ |
|||
bayWidth: number; |
|||
/** 列偏移 */ |
|||
offset: number; |
|||
/** 每层的高度 */ |
|||
levelHeight: Array<number>; |
|||
} |
|||
|
|||
type DtBays = Array<DtBay>; |
|||
|
|||
// 组件事件定义 |
|||
const emit = defineEmits<{ |
|||
/** 更新内联表格数据 */ |
|||
"update:modelValue": [value: DtBays]; |
|||
}>(); |
|||
|
|||
|
|||
// 定义 Props 类型 |
|||
interface BayEditorProps { |
|||
modelValue: DtBays; |
|||
} |
|||
|
|||
// 读取组件 props 属性 |
|||
const props = withDefaults(defineProps<BayEditorProps>(), {}); |
|||
|
|||
// 定义 State 类型 |
|||
interface BayEditorState { |
|||
numberOfBays: number; |
|||
numberOfLevels: number; |
|||
widthOfBays: number; |
|||
heightOfLevels: number; |
|||
selectIdx?: number; |
|||
} |
|||
|
|||
// state 属性 |
|||
const state = reactive<BayEditorState>({ |
|||
numberOfBays: props.modelValue?.length ?? 1, |
|||
numberOfLevels: props.modelValue?.[0]?.levelHeight?.length ?? 1, |
|||
widthOfBays: props.modelValue?.[0]?.bayWidth ?? 1, |
|||
heightOfLevels: props.modelValue?.[0]?.levelHeight?.length ?? 1, |
|||
}); |
|||
|
|||
// 定义 Data 类型 |
|||
interface BayEditorData { |
|||
labelWidth: number; |
|||
} |
|||
|
|||
// 内部数据 |
|||
const data: BayEditorData = { |
|||
labelWidth: 110, |
|||
}; |
|||
const { formItem } = useFormItem(); |
|||
const list = computed(() => props.modelValue ?? []); |
|||
const selectDay = computed(() => { |
|||
if (Typeof.noValue(state.selectIdx)) return; |
|||
return props.modelValue?.[state.selectIdx]; |
|||
}); |
|||
|
|||
function setSelectIdx(idx: number) { |
|||
state.selectIdx = idx; |
|||
} |
|||
|
|||
function applyBasicSettings() { |
|||
|
|||
} |
|||
|
|||
function deleteBay() { |
|||
|
|||
} |
|||
|
|||
function copyBay() { |
|||
|
|||
} |
|||
|
|||
interface BayEditorExpose { |
|||
state: BayEditorState; |
|||
data: BayEditorData; |
|||
} |
|||
|
|||
const expose: BayEditorExpose = { |
|||
state, |
|||
data, |
|||
}; |
|||
// 定义组件公开内容 |
|||
defineExpose(expose); |
|||
|
|||
export type { |
|||
BayEditorProps, |
|||
BayEditorState, |
|||
} |
|||
</script> |
|||
|
|||
<template> |
|||
<div class="bay-editor"> |
|||
<ElDivider class="bay-editor-title" contentPosition="left">Basic</ElDivider> |
|||
<div class="flex-column-container bay-editor-basic" style="gap: 8px;"> |
|||
<div class="flex-row-container"> |
|||
<ElFormItem label="Number of Bays" :labelWidth="data.labelWidth"> |
|||
<ElInputNumber :controls="false" v-model="state.numberOfBays"/> |
|||
</ElFormItem> |
|||
<ElFormItem label="Number of Levels" :labelWidth="data.labelWidth"> |
|||
<ElInputNumber :controls="false" v-model="state.numberOfLevels"/> |
|||
</ElFormItem> |
|||
</div> |
|||
<div class="flex-row-container"> |
|||
<ElFormItem label="Width of Bays" :labelWidth="data.labelWidth"> |
|||
<ElInputNumber :controls="false" v-model="state.widthOfBays"/> |
|||
</ElFormItem> |
|||
<ElFormItem label="Height of Levels" :labelWidth="data.labelWidth"> |
|||
<ElInputNumber :controls="false" v-model="state.heightOfLevels"/> |
|||
</ElFormItem> |
|||
</div> |
|||
<div class="flex-row-container"> |
|||
<div style="width: 248px;"/> |
|||
<ElButton size="small" @click="applyBasicSettings">Apply Basic Settings</ElButton> |
|||
</div> |
|||
</div> |
|||
<ElDivider class="bay-editor-title" contentPosition="left">Advanced</ElDivider> |
|||
<div class="flex-row-container bay-editor-advanced"> |
|||
<div class="flex-item-fixed flex-column-container bay-editor-bay-list"> |
|||
<div class="flex-item-fixed tools-button-container"> |
|||
<ElIcon :class="['tools-button-icon', { 'tools-button-disabled': false }]" @click="copyBay"> |
|||
<CopyDocument/> |
|||
</ElIcon> |
|||
<ElIcon :class="['tools-button-icon', { 'tools-button-disabled': false }]" @click="deleteBay"> |
|||
<Delete/> |
|||
</ElIcon> |
|||
</div> |
|||
<div class="flex-item-fill bay-editor-bay-list-name"> |
|||
<div |
|||
v-for="(item, idx) in list" |
|||
:class="[ |
|||
'list-item', |
|||
{ |
|||
'list-item-select': state.selectIdx === idx, |
|||
}, |
|||
]" |
|||
@click="setSelectIdx(idx)" |
|||
> |
|||
{{ `Bay ${idx + 1}` }} |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="flex-item-fill flex-column-container bay-editor-bay-info"> |
|||
<template v-if="selectDay"> |
|||
<div class="flex-item-fixed"> |
|||
<ElFormItem label="Bay Width" :labelWidth="80"> |
|||
<ElInputNumber :controls="false" :modelValue="selectDay.bayWidth"/> |
|||
</ElFormItem> |
|||
<div style="height: 8px;"/> |
|||
<ElFormItem label="Bay Offset" :labelWidth="80"> |
|||
<ElInputNumber :controls="false" :modelValue="selectDay.offset"/> |
|||
</ElFormItem> |
|||
</div> |
|||
<div>Level Heights</div> |
|||
<div class="flex-item-fill bay-editor-bay-info-level-height"> |
|||
<ElFormItem |
|||
v-for="(levelHeight, idx) in selectDay.levelHeight" |
|||
:label="`Level ${idx+1}`" |
|||
:labelWidth="64" |
|||
> |
|||
<ElInputNumber :controls="false" :modelValue="levelHeight"/> |
|||
</ElFormItem> |
|||
</div> |
|||
</template> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<style scoped> |
|||
.bay-editor { |
|||
width: 100%; |
|||
} |
|||
|
|||
.bay-editor-title { |
|||
height: 12px; |
|||
user-select: none; |
|||
} |
|||
|
|||
.bay-editor-basic :deep(.el-input-number) { |
|||
width: 80px; |
|||
} |
|||
|
|||
.bay-editor-advanced { |
|||
height: 256px; |
|||
} |
|||
|
|||
.bay-editor-bay-list { |
|||
width: 72px; |
|||
} |
|||
|
|||
.bay-editor-bay-list-name { |
|||
border: 1px solid #dddddd; |
|||
} |
|||
|
|||
.tools-button-container { |
|||
display: flex; |
|||
flex-direction: row; |
|||
flex-wrap: nowrap; |
|||
align-items: center; |
|||
justify-content: center; |
|||
gap: 4px; |
|||
cursor: pointer; |
|||
width: 100%; |
|||
} |
|||
|
|||
.tools-button-container > .tools-button-icon { |
|||
color: #8c8c8c; |
|||
padding: 4px; |
|||
font-size: 22px; |
|||
border-radius: 2px; |
|||
} |
|||
|
|||
.tools-button-container > .tools-button-icon:hover { |
|||
color: #595959; |
|||
background-color: #d9d9d9; |
|||
} |
|||
|
|||
.tools-button-container > .tools-button-icon:active { |
|||
color: #434343; |
|||
background-color: #bfbfbf; |
|||
} |
|||
|
|||
.tools-button-container > .tools-button-disabled { |
|||
cursor: not-allowed; |
|||
} |
|||
|
|||
.tools-button-container > .tools-button-disabled.tools-button-icon, |
|||
.tools-button-container > .tools-button-disabled.tools-button-icon:hover, |
|||
.tools-button-container > .tools-button-disabled.tools-button-icon:active { |
|||
color: #d9d9d9; |
|||
background-color: unset; |
|||
pointer-events: none; |
|||
} |
|||
|
|||
.list-item { |
|||
padding: 4px 0 4px 2px; |
|||
cursor: pointer; |
|||
border-bottom: 1px solid #f0f0f0; |
|||
user-select: none; |
|||
color: #262626; |
|||
white-space: nowrap; |
|||
overflow: hidden; |
|||
text-overflow: ellipsis; |
|||
} |
|||
|
|||
.list-item:hover { |
|||
background-color: #f0f0f0; |
|||
} |
|||
|
|||
.list-item:active { |
|||
background-color: #dddddd; |
|||
} |
|||
|
|||
.list-item.list-item-select, |
|||
.list-item.list-item-select:hover, |
|||
.list-item.list-item-select:active { |
|||
background-color: #d9d9d9; |
|||
} |
|||
|
|||
.bay-editor-bay-info { |
|||
padding-left: 6px; |
|||
} |
|||
|
|||
.bay-editor-bay-info-level-height { |
|||
border: 1px solid #dddddd; |
|||
} |
|||
|
|||
.bay-editor-bay-info-level-height :deep(.el-input__wrapper) { |
|||
border: none; |
|||
outline: none; |
|||
box-shadow: none; |
|||
} |
|||
|
|||
.bay-editor-bay-info-level-height :deep(.el-form-item) { |
|||
border-bottom: 1px solid #dddddd; |
|||
} |
|||
|
|||
.bay-editor-bay-info-level-height :deep(.el-form-item > .el-form-item__label) { |
|||
border-right: 1px solid #dddddd; |
|||
} |
|||
|
|||
.bay-editor-bay-info-level-height :deep(.el-input-number) { |
|||
width: 100%; |
|||
} |
|||
|
|||
.bay-editor-bay-info-level-height :deep(input[type=number].el-input__inner) { |
|||
text-align: left; |
|||
} |
|||
</style> |
|||
Loading…
Reference in new issue