You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

282 lines
7.8 KiB

<script setup lang="ts">
import { computed, reactive } from "vue";
import { ElIcon, ElRadioButton, ElRadioGroup, useFormItem } from "element-plus";
import { ArrowDown, ArrowUp, Delete, EditPen } from "@element-plus/icons-vue";
import { Typeof } from "@ease-forge/shared";
defineOptions({
name: 'InOutCenterEditor',
});
// 组件事件定义
const emit = defineEmits<{
/** 更新内联表格数据 */
"update:modelValue": [value: ItemJson["dt"]];
}>();
// 定义 Props 类型
interface InOutCenterEditorProps {
modelValue: ItemJson["dt"];
}
// 读取组件 props 属性
const props = withDefaults(defineProps<InOutCenterEditorProps>(), {});
// 定义 State 类型
interface InOutCenterEditorState {
portsType: "center" | "in" | "out";
selectCenterIdx?: number;
selectInIdx?: number;
selectOutIdx?: number;
}
// state 属性
const state = reactive<InOutCenterEditorState>({
portsType: "in",
});
// 定义 Data 类型
interface InOutCenterEditorData {
}
// 内部数据
const data: InOutCenterEditorData = {};
const { formItem } = useFormItem();
const list = computed<Array<string>>(() => {
const portsType = state.portsType;
if (portsType === "center") return props.modelValue?.center ?? [];
if (portsType === "in") return props.modelValue?.in ?? [];
if (portsType === "out") return props.modelValue?.out ?? [];
return [];
});
const selectIdx = computed<number | undefined>({
set: newValue => {
const portsType = state.portsType;
if (portsType === "center") state.selectCenterIdx = newValue;
if (portsType === "in") state.selectInIdx = newValue;
if (portsType === "out") state.selectOutIdx = newValue;
},
get: oldValue => {
const portsType = state.portsType;
if (portsType === "center") return state.selectCenterIdx;
if (portsType === "in") return state.selectInIdx;
if (portsType === "out") return state.selectOutIdx;
},
});
const canUpItem = computed(() => selectIdx.value > 0);
const canDownItem = computed(() => selectIdx.value < (list.value.length - 1));
const canDeleteItem = computed(() => selectIdx.value < list.value.length);
function setSelectIdx(idx: number) {
selectIdx.value = idx;
}
function addItem() {
const [list, modelValue] = getListAndModelValue();
list.push(`D_${Date.now()}`);
emit("update:modelValue", modelValue);
}
function upItem() {
if (!canUpItem.value) return;
const idx = selectIdx.value;
if (Typeof.noValue(idx)) return;
if (idx <= 0) return;
const [list, modelValue] = getListAndModelValue();
const idxUp = idx - 1;
const tmp = list[idxUp];
list[idxUp] = list[idx];
list[idx] = tmp;
emit("update:modelValue", modelValue);
selectIdx.value--;
}
function downItem() {
if (!canDownItem.value) return;
const idx = selectIdx.value;
if (Typeof.noValue(idx)) return;
const [list, modelValue] = getListAndModelValue();
if (idx >= (list.length - 1)) return;
const idxDown = idx + 1;
const tmp = list[idxDown];
list[idxDown] = list[idx];
list[idx] = tmp;
emit("update:modelValue", modelValue);
selectIdx.value++;
}
function deleteItem() {
if (!canDeleteItem.value) return;
if (Typeof.noValue(selectIdx.value)) return;
const [list, modelValue] = getListAndModelValue();
list.splice(selectIdx.value, 1);
emit("update:modelValue", modelValue);
if (selectIdx.value >= list.length) {
setSelectIdx(Math.max(0, selectIdx.value - 1));
}
}
function getListAndModelValue() {
const modelValue = props.modelValue ?? {};
const portsType = state.portsType;
let list: Array<string> = [];
if (portsType === "center") {
if (!modelValue.center) modelValue.center = [];
list = modelValue.center;
} else if (portsType === "in") {
if (!modelValue.in) modelValue.in = [];
list = modelValue.in;
} else if (portsType === "out") {
if (!modelValue.out) modelValue.out = [];
list = modelValue.out;
}
return [list, modelValue];
}
interface InOutCenterEditorExpose {
state: InOutCenterEditorState;
data: InOutCenterEditorData;
}
const expose: InOutCenterEditorExpose = {
state,
data,
};
// 定义组件公开内容
defineExpose(expose);
export type {
InOutCenterEditorProps,
InOutCenterEditorState,
}
</script>
<template>
<div class="flex-column-container in-out-center-editor">
<ElRadioGroup class="flex-item-fixed flex-justify-content-center" v-model="state.portsType" size="small">
<ElRadioButton label="Input Ports" value="in"/>
<ElRadioButton label="Central Ports" value="center"/>
<ElRadioButton label="Output Ports" value="out"/>
</ElRadioGroup>
<div class="flex-item-fill flex-row-container in-out-center-editor-data">
<div class="flex-item-fixed in-out-center-editor-tools">
<div class="tools-button-container">
<ElIcon class="tools-button-icon" @click="addItem">
<EditPen/>
</ElIcon>
<ElIcon :class="['tools-button-icon', { 'tools-button-disabled': !canUpItem }]" @click="upItem">
<ArrowUp/>
</ElIcon>
<ElIcon :class="['tools-button-icon', { 'tools-button-disabled': !canDownItem }]" @click="downItem">
<ArrowDown/>
</ElIcon>
<ElIcon :class="['tools-button-icon', { 'tools-button-disabled': !canDeleteItem }]" @click="deleteItem">
<Delete/>
</ElIcon>
</div>
</div>
<div class="flex-item-fill in-out-center-editor-data-list">
<div
v-for="(item, idx) in list"
:class="[
'list-item',
{
'list-item-select': selectIdx === idx,
},
]"
@click="setSelectIdx(idx)"
>
{{ item }}
</div>
</div>
</div>
</div>
</template>
<style scoped>
.in-out-center-editor {
width: 100%;
height: 160px;
}
.in-out-center-editor-data {
margin-top: 6px;
border: 1px solid #dddddd;
}
.in-out-center-editor-tools {
background-color: #f5f5f5;
border-right: 1px solid #dddddd;
width: 28px;
padding: 4px 0;
}
.tools-button-container {
display: flex;
flex-direction: column;
flex-wrap: nowrap;
align-items: center;
justify-content: center;
gap: 4px;
cursor: pointer;
height: 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;
}
.in-out-center-editor-data-list {
overflow-y: auto;
}
.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;
}
</style>