Browse Source

Merge remote-tracking branch 'origin/master'

master
修宁 6 months ago
parent
commit
23739e5cca
  1. 127
      src/editor/BulkCopy.vue
  2. 61
      src/editor/Model2DEditor.vue
  3. 99
      src/editor/propEditors/BayEditor.vue
  4. 6
      src/editor/propEditors/InOutCenterEditor.vue
  5. 136
      src/editor/widgets/property/PropertyPanel.vue

127
src/editor/BulkCopy.vue

@ -0,0 +1,127 @@
<script setup lang="ts">
import { reactive } from "vue";
import DataForm from "@/components/data-form/DataForm.vue";
import type { FormField } from "@/components/data-form/DataFormTypes.ts";
defineOptions({
name: 'BulkCopy',
});
interface BulkCopyConfig {
/** 行数 */
numberOfRows?: number;
/** 列数 */
numberOfColumns?: number;
/** 行间距 */
rowSpace?: number;
/** 列间距 */
columnSpacing?: number;
/** 双排 */
doubleRow?: boolean;
}
// Props
interface BulkCopyProps {
config: BulkCopyConfig;
}
// props
const props = withDefaults(defineProps<BulkCopyProps>(), {});
// State
interface BulkCopyState {
}
// state
const state = reactive<BulkCopyState>({});
// Data
interface BulkCopyData {
formFields?: Array<FormField>;
}
//
const data: BulkCopyData = {
formFields: [
{
dataPath: 'numberOfRows', label: '行数', input: 'InputNumber',
inputProps: {
placeholder: '请输入',
controlsPosition: 'right',
min: 1,
precision: 0,
},
},
{
dataPath: 'numberOfColumns', label: '列数', input: 'InputNumber',
inputProps: {
placeholder: '请输入',
controlsPosition: 'right',
min: 1,
precision: 0,
},
},
{
dataPath: 'rowSpace', label: '行间距', input: 'InputNumber',
inputProps: {
placeholder: '请输入',
controlsPosition: 'right',
min: 0,
// precision:
},
},
{
dataPath: 'columnSpacing', label: '列间距', input: 'InputNumber',
inputProps: {
placeholder: '请输入',
controlsPosition: 'right',
min: 0,
// precision:
},
},
{
dataPath: 'doubleRow', label: '单双排', input: 'Switch',
inputProps: {
// trueValue: true,
// falseValue: false,
inlinePrompt: true,
inactiveText: "单排",
activeText: "双排",
},
},
],
};
interface BulkCopyExpose {
state: BulkCopyState;
data: BulkCopyData;
}
const expose: BulkCopyExpose = {
state,
data,
};
//
defineExpose(expose);
export type {
BulkCopyConfig,
BulkCopyProps,
BulkCopyState,
}
</script>
<template>
<DataForm
:data="props.config"
:formFields="data.formFields"
:columnCount="2"
layout="onlyLabelFixed"
labelWidth="80px"
inputWidth=""
/>
</template>
<style scoped>
</style>

61
src/editor/Model2DEditor.vue

@ -17,6 +17,9 @@
:type="state?.view3DMode===Constract.Mode3D?'primary':''"
@click="state.view3DMode = Constract.Mode3D">3D
</el-button>
<el-button :icon="renderIcon('element Files')" link @click="showBulkCopy" :disabled="!selectedObject">
批量复制
</el-button>
</el-button-group>
</div>
<div class="section-content">
@ -92,13 +95,14 @@
</template>
<script>
import * as THREE from 'three'
import $ from 'jquery'
import { getQueryParams, renderIcon, setQueryParam } from '@/utils/webutils'
import { defineComponent, markRaw } from 'vue'
import { renderIcon, setQueryParam } from '@/utils/webutils'
import { createVNode, defineComponent, markRaw } from 'vue'
import Viewport from '@/core/engine/Viewport'
import Constract from '@/core/Constract'
import EventBus from '@/runtime/EventBus'
import SceneHelp from '@/core/engine/SceneHelp'
import BulkCopy from './BulkCopy.vue'
import lodash from "lodash";
export default defineComponent({
name: 'Model2DEditor',
@ -242,7 +246,51 @@ export default defineComponent({
})
}
},
showBulkCopy() {
const viewport = this.viewport;
const selectedItem = viewport?.state?.selectedItem;
if(!viewport || !selectedItem) return;
const config = {
numberOfRows: 1,
numberOfColumns: 1,
rowSpace: 0,
columnSpacing: 0,
doubleRow: false,
};
system.showDialog(createVNode(BulkCopy, {
config,
}), {
title: '批量复制',
width: 520,
height: 240,
showClose: true,
showMax: true,
showCancelButton: false,
showOkButton: true,
}).then(() => {
if(!config.numberOfRows || !config.numberOfColumns) return;
viewport.stateManager.update(({ getEntity, addEntity }) => {
const xAxle = selectedItem.tf[0][0];
const zAxle = selectedItem.tf[0][2];
const xSize = selectedItem.tf[2][0];
const zSize = selectedItem.tf[2][2];
console.log("item", JSON.stringify(selectedItem.tf));
for (let rCount = 0; rCount < config.numberOfRows; rCount++) {
for (let cCount = 0; cCount < config.numberOfColumns; cCount++) {
if(rCount===0 && cCount===0) continue;
const item = lodash.cloneDeep(selectedItem);
item.id = system.createUUID();
item.tf[0][0] = xAxle + cCount * (xSize + config.columnSpacing);
item.tf[0][2] = zAxle + rCount * (zSize + config.rowSpace);
addEntity(item);
console.log("item", JSON.stringify(item.tf));
}
}
});
console.log("config", config, selectedItem);
}).finally();
},
},
computed: {
state() {
@ -268,7 +316,10 @@ export default defineComponent({
label: item.label
}))
}))
}
},
selectedObject() {
return this.state?.selectedObject;
},
}
})
</script>

99
src/editor/propEditors/BayEditor.vue

@ -1,4 +1,5 @@
<script setup lang="ts">
import lodash from "lodash";
import { computed, reactive } from "vue";
import { ElButton, ElDivider, ElFormItem, ElIcon, ElInputNumber, useFormItem } from "element-plus";
import { CopyDocument, Delete } from "@element-plus/icons-vue";
@ -36,10 +37,15 @@ const props = withDefaults(defineProps<BayEditorProps>(), {});
// State
interface BayEditorState {
/** 列数 */
numberOfBays: number;
/** 层数 */
numberOfLevels: number;
/** 列宽 */
widthOfBays: number;
/** 层高 */
heightOfLevels: number;
/** 选中列 */
selectIdx?: number;
}
@ -61,26 +67,78 @@ const data: BayEditorData = {
labelWidth: 110,
};
const { formItem } = useFormItem();
const list = computed(() => props.modelValue ?? []);
const list = computed(getModelValue);
const selectDay = computed(() => {
if (Typeof.noValue(state.selectIdx)) return;
return props.modelValue?.[state.selectIdx];
});
function setSelectIdx(idx: number) {
function setSelectIdx(idx?: number) {
state.selectIdx = idx;
}
function applyBasicSettings() {
const { numberOfBays, numberOfLevels, widthOfBays, heightOfLevels } = state;
const newValue = getModelValue();
newValue.length = 0;
for (let idx = 0; idx < numberOfBays; idx++) {
const bay: DtBay = {
bayWidth: widthOfBays,
offset: 0,
levelHeight: [],
};
for (let i = 0; i < numberOfLevels; i++) {
bay.levelHeight.push(heightOfLevels);
}
newValue.push(bay);
}
emit("update:modelValue", newValue);
}
function deleteBay() {
const selectIdx = state.selectIdx;
if (Typeof.noValue(selectIdx)) return;
const newValue = getModelValue();
if (selectIdx >= newValue.length) return;
newValue.splice(selectIdx, 1);
emit("update:modelValue", newValue);
if (selectIdx >= newValue.length) {
setSelectIdx(undefined);
}
}
function copyBay() {
const selectIdx = state.selectIdx;
if (Typeof.noValue(selectIdx)) return;
const newValue = getModelValue();
const bay = newValue[selectIdx];
if (!bay) return;
newValue.splice(selectIdx + 1, 0, lodash.cloneDeep(bay));
emit("update:modelValue", newValue);
setSelectIdx(selectIdx + 1);
}
function updateBayWidth(bay: DtBay, bayWidth?: number) {
bay.bayWidth = bayWidth;
const newValue = getModelValue();
emit("update:modelValue", newValue);
}
function updateBayOffset(bay: DtBay, offset?: number) {
bay.offset = offset;
const newValue = getModelValue();
emit("update:modelValue", newValue);
}
function updateBayLevelHeight(bay: DtBay, idx: number, levelHeight?: number) {
if (!bay.levelHeight) bay.levelHeight = [];
bay.levelHeight[idx] = levelHeight;
const newValue = getModelValue();
emit("update:modelValue", newValue);
}
function getModelValue(): DtBays {
return props.modelValue ?? [];
}
interface BayEditorExpose {
@ -130,10 +188,10 @@ export type {
<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">
<ElIcon :class="['tools-button-icon', { 'tools-button-disabled': !selectDay }]" @click="copyBay">
<CopyDocument/>
</ElIcon>
<ElIcon :class="['tools-button-icon', { 'tools-button-disabled': false }]" @click="deleteBay">
<ElIcon :class="['tools-button-icon', { 'tools-button-disabled': !selectDay }]" @click="deleteBay">
<Delete/>
</ElIcon>
</div>
@ -155,21 +213,34 @@ export type {
<div v-if="selectDay" class="flex-item-fill flex-column-container bay-editor-bay-info">
<div class="flex-item-fixed">
<ElFormItem label="Bay Width" :labelWidth="80">
<ElInputNumber :controls="false" :modelValue="selectDay.bayWidth"/>
<ElInputNumber
:controls="false"
:modelValue="selectDay.bayWidth"
@change="(width: number) => updateBayWidth(selectDay, width)"
/>
</ElFormItem>
<div style="height: 8px;"/>
<ElFormItem label="Bay Offset" :labelWidth="80">
<ElInputNumber :controls="false" :modelValue="selectDay.offset"/>
<ElInputNumber
:controls="false"
:modelValue="selectDay.offset"
@change="(offset: number) => updateBayOffset(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"
labelPosition="left"
:label="`Level ${idx+1}`"
:labelWidth="64"
>
<ElInputNumber :controls="false" :modelValue="levelHeight"/>
<ElInputNumber
:controls="false"
:modelValue="levelHeight"
@change="(height: number) => updateBayLevelHeight(selectDay, idx, height)"
/>
</ElFormItem>
</div>
</div>
@ -183,6 +254,7 @@ export type {
<style scoped>
.bay-editor {
width: 100%;
user-select: none;
}
.bay-editor-title {
@ -204,6 +276,7 @@ export type {
.bay-editor-bay-list-name {
border: 1px solid #dddddd;
overflow-y: auto;
}
.tools-button-container {
@ -215,6 +288,9 @@ export type {
gap: 4px;
cursor: pointer;
width: 100%;
background-color: #f5f5f5;
border: 1px solid #dddddd;
border-bottom: none;
}
.tools-button-container > .tools-button-icon {
@ -247,7 +323,7 @@ export type {
}
.list-item {
padding: 4px 0 4px 2px;
padding: 0 2px;
cursor: pointer;
border-bottom: 1px solid #f0f0f0;
user-select: none;
@ -277,6 +353,7 @@ export type {
.bay-editor-bay-info-level-height {
border: 1px solid #dddddd;
overflow-y: auto;
}
.bay-editor-bay-info-level-height :deep(.el-input__wrapper) {
@ -291,6 +368,8 @@ export type {
.bay-editor-bay-info-level-height :deep(.el-form-item > .el-form-item__label) {
border-right: 1px solid #dddddd;
background-color: #f0f0f0;
padding: 0 2px;
}
.bay-editor-bay-info-level-height :deep(.el-input-number) {

6
src/editor/propEditors/InOutCenterEditor.vue

@ -67,7 +67,7 @@ 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) {
function setSelectIdx(idx?: number) {
selectIdx.value = idx;
}
@ -112,7 +112,7 @@ function deleteItem() {
list.splice(selectIdx.value, 1);
emit("update:modelValue", modelValue);
if (selectIdx.value >= list.length) {
setSelectIdx(Math.max(0, selectIdx.value - 1));
setSelectIdx(undefined);
}
}
@ -256,7 +256,7 @@ export type {
}
.list-item {
padding: 4px 0 4px 2px;
padding: 0 2px;
cursor: pointer;
border-bottom: 1px solid #f0f0f0;
user-select: none;

136
src/editor/widgets/property/PropertyPanel.vue

@ -9,23 +9,23 @@ import { defDataFormProps } from '@/editor/widgets/property/PropertyPanelConstan
import Viewport, { type ViewportState } from '@/core/engine/Viewport.ts'
defineOptions({
name: 'PropertyPanel'
name: 'PropertyPanel'
})
// Props
interface PropertyPanelProps {
/** 待编辑数据 */
data?: any;
/** Viewport */
viewport: Viewport;
/** ViewportState */
viewportState: ViewportState;
/** 默认的DataFormProps */
defDataFormProps?: DataFormProps;
/** 最上面平铺的设置器 */
flatten?: PropertyFlattenSetter;
/** 设置器分组集合 */
groups?: Array<PropertySetterGroup>;
/** 待编辑数据 */
data?: any;
/** Viewport */
viewport: Viewport;
/** ViewportState */
viewportState: ViewportState;
/** 默认的DataFormProps */
defDataFormProps?: DataFormProps;
/** 最上面平铺的设置器 */
flatten?: PropertyFlattenSetter;
/** 设置器分组集合 */
groups?: Array<PropertySetterGroup>;
}
// props
@ -33,16 +33,16 @@ const props = withDefaults(defineProps<PropertyPanelProps>(), {})
// State
interface PropertyPanelState {
/** 待编辑数据 */
data?: any;
/** 已展开的分组 */
expandGroups: Array<string>;
/** 待编辑数据 */
data?: any;
/** 已展开的分组 */
expandGroups: Array<string>;
}
// state
const state = reactive<PropertyPanelState>({
data: lodash.cloneDeep(props.data),
expandGroups: []
data: lodash.cloneDeep(props.data),
expandGroups: []
})
// Data
@ -54,84 +54,84 @@ const data: PropertyPanelData = {}
const flattenFormProps = computed(() => getDefFormProps(props.flatten))
function getDefFormProps(setter?: PropertyFlattenSetter) {
const formProps: DataFormProps = {
...defDataFormProps,
...props.defDataFormProps
}
fillFormProps(setter)
return formProps
const formProps: DataFormProps = {
...defDataFormProps,
...props.defDataFormProps
}
fillFormProps(setter)
return formProps
}
function fillFormProps(formProps: DataFormProps, setter?: PropertyFlattenSetter) {
if (!setter) return
if (setter.size) formProps.size = setter.size
if (setter.labelWidth) formProps.labelWidth = setter.labelWidth
if (!setter) return
if (setter.size) formProps.size = setter.size
if (setter.labelWidth) formProps.labelWidth = setter.labelWidth
}
function getCollapseItemId(group: PropertySetterGroup, idx: number) {
return `_${idx}_${group.title}`
return `_${idx}_${group.title}`
}
function onDataChange(newData: any) {
const viewport = props.viewport
if (!viewport) return
viewport.stateManager.update(({ getEntity, putEntity }) => {
const data = getEntity(props.data.id)
lodash.assign(data, newData)
// console.log('onDataChange@1', JSON.stringify(data.dt))
putEntity(data)
})
const viewport = props.viewport
if (!viewport) return
viewport.stateManager.update(({ getEntity, putEntity }) => {
const data = getEntity(props.data.id);
lodash.assign(data, newData);
// console.log('onDataChange@1', JSON.stringify(data.dt))
putEntity(data);
});
}
interface PropertyPanelExpose {
state: PropertyPanelState;
data: PropertyPanelData;
state: PropertyPanelState;
data: PropertyPanelData;
}
const expose: PropertyPanelExpose = {
state,
data
state,
data
}
//
defineExpose(expose)
export type {
PropertyPanelProps,
PropertyPanelState
PropertyPanelProps,
PropertyPanelState
}
</script>
<template>
<div class="property-panel">
<DataForm
v-if="props.flatten"
class="property-panel-form"
v-bind="flattenFormProps"
:data="state.data"
:formFields="props.flatten.fields"
@dataChange="onDataChange"
/>
<ElCollapse v-if="props.groups" v-model="state.expandGroups">
<ElCollapseItem
v-for="(group, idx) in props.groups"
:name="getCollapseItemId(group, idx)"
:title="group.title"
>
<div class="property-panel">
<DataForm
v-if="group"
class="property-panel-form"
v-bind="getDefFormProps(group)"
:data="state.data"
:formFields="props.flatten.fields"
@dataChange="onDataChange"
v-if="props.flatten"
class="property-panel-form"
v-bind="flattenFormProps"
:data="state.data"
:formFields="props.flatten.fields"
@dataChange="onDataChange"
/>
</ElCollapseItem>
</ElCollapse>
</div>
<ElCollapse v-if="props.groups" v-model="state.expandGroups">
<ElCollapseItem
v-for="(group, idx) in props.groups"
:name="getCollapseItemId(group, idx)"
:title="group.title"
>
<DataForm
v-if="group"
class="property-panel-form"
v-bind="getDefFormProps(group)"
:data="state.data"
:formFields="props.flatten.fields"
@dataChange="onDataChange"
/>
</ElCollapseItem>
</ElCollapse>
</div>
</template>
<style scoped>
.property-panel {
padding: 8px 12px 16px 4px;
padding: 8px 12px 16px 4px;
}
</style>

Loading…
Cancel
Save