Browse Source

meta editor components

master
修宁 7 months ago
parent
commit
ce87100e28
  1. 1
      package.json
  2. 3
      pnpm-lock.yaml
  3. 12
      src/designer/Viewport.ts
  4. 34
      src/designer/metaComponents/ColorItem.vue
  5. 26
      src/designer/metaComponents/IMetaProp.ts
  6. 32
      src/designer/metaComponents/NumberInput.vue
  7. 32
      src/designer/metaComponents/SwitchItem.vue
  8. 32
      src/designer/metaComponents/TextInput.vue
  9. 210
      src/designer/metaComponents/Transform.vue
  10. 32
      src/designer/metaComponents/UUIDItem.vue
  11. 6
      src/designer/model2DEditor/EsDragControls.ts
  12. 58
      src/designer/model2DEditor/tools/SelectInspect.ts
  13. 96
      src/designer/viewWidgets/property/PropertyView.vue
  14. 24
      src/model/itemType/ItemTypeDefine.ts
  15. 6
      src/model/itemType/ItemTypeLine.ts
  16. 28
      src/runtime/DefineItemType.ts
  17. 9
      src/runtime/EventBus.js
  18. 44
      src/utils/webutils.ts

1
package.json

@ -48,6 +48,7 @@
"@vitejs/plugin-vue": "^5.2.3", "@vitejs/plugin-vue": "^5.2.3",
"@vitejs/plugin-vue-jsx": "^4.2.0", "@vitejs/plugin-vue-jsx": "^4.2.0",
"@vue/tsconfig": "^0.7.0", "@vue/tsconfig": "^0.7.0",
"mitt": "^3.0.1",
"tslib": "2.8.1", "tslib": "2.8.1",
"npm-run-all2": "^7.0.2", "npm-run-all2": "^7.0.2",
"prettier": "3.5.3", "prettier": "3.5.3",

3
pnpm-lock.yaml

@ -114,6 +114,9 @@ importers:
camera-controls: camera-controls:
specifier: 2.10.1 specifier: 2.10.1
version: 2.10.1(three@0.176.0) version: 2.10.1(three@0.176.0)
mitt:
specifier: ^3.0.0
version: 3.0.1
npm-run-all2: npm-run-all2:
specifier: ^7.0.2 specifier: ^7.0.2
version: 7.0.2 version: 7.0.2

12
src/designer/Viewport.ts

@ -9,8 +9,9 @@ import { reactive, watch } from 'vue'
import type { ITool } from '@/designer/model2DEditor/tools/ITool.ts' import type { ITool } from '@/designer/model2DEditor/tools/ITool.ts'
import { CSS3DRenderer } from 'three/examples/jsm/renderers/CSS3DRenderer' import { CSS3DRenderer } from 'three/examples/jsm/renderers/CSS3DRenderer'
import { CSS2DRenderer } from 'three/examples/jsm/renderers/CSS2DRenderer' import { CSS2DRenderer } from 'three/examples/jsm/renderers/CSS2DRenderer'
import { getAllItemTypes } from '@/model/itemType/ItemTypeDefine.ts' import { getAllItemTypes, type ItemTypeMeta } from '@/model/itemType/ItemTypeDefine.ts'
import type { ItemTypeDefineOption } from '@/model/itemType/ItemTypeDefine.ts' import type { ItemTypeDefineOption } from '@/model/itemType/ItemTypeDefine.ts'
import EventBus from '@/runtime/EventBus'
import type Toolbox from '@/model/itemType/Toolbox.ts' import type Toolbox from '@/model/itemType/Toolbox.ts'
import { calcPositionUseSnap } from '@/model/ModelUtils.ts' import { calcPositionUseSnap } from '@/model/ModelUtils.ts'
@ -77,10 +78,6 @@ export default class Viewport {
this.worldModel = worldModel this.worldModel = worldModel
} }
dispatchSignal(signal: string, data?: any) {
// console.log('signal', signal, data)
}
/** /**
* THREE * THREE
*/ */
@ -473,6 +470,11 @@ export interface ViewportState {
selectedObject: THREE.Object3D | null selectedObject: THREE.Object3D | null
/** /**
*
*/
selectedObjectMeta: ItemTypeMeta | null
/**
* *
*/ */
camera: { camera: {

34
src/designer/metaComponents/ColorItem.vue

@ -0,0 +1,34 @@
<template>
<el-form-item>
<template #label>
<div :title="prop.label + ' - ' + prop.field">
{{ prop.label }}
</div>
</template>
<el-color-picker v-model="value" class="prop-editor" type="text" clearable size="small" />
</el-form-item>
</template>
<script>
import IMetaProp from './IMetaProp.ts'
import { getColorFromCode, getColorFromString } from '@/utils/webutils.js'
export default {
mixins: [IMetaProp],
data() {
// '#409EFF' 0x409EFF
return {
value: getColorFromCode(_.get(this.object3D, this.prop.field))
}
},
methods: {
refreshValue() {
this.value = getColorFromCode(_.get(this.object3D, this.prop.field))
}
},
watch: {
value(newValue) {
_.set(this.object3D, this.prop.field, getColorFromString(newValue))
}
}
}
</script>

26
src/designer/metaComponents/IMetaProp.ts

@ -0,0 +1,26 @@
import * as THREE from 'three'
import { type ItemTypeMetaItem } from '@/model/itemType/ItemTypeDefine.ts'
import { defineComponent, type PropType } from 'vue'
import type Viewport from '@/designer/Viewport.ts'
import EventBus from '@/runtime/EventBus'
export default defineComponent({
props: {
prop: Object as PropType<ItemTypeMetaItem>,
viewport: Object as PropType<Viewport>
},
mounted() {
EventBus.$on('objectChanged', (data) => {
//@ts-ignore
if (typeof this.refreshValue === 'function') {
//@ts-ignore
this.refreshValue()
}
})
},
computed: {
object3D(): THREE.Object3D {
return this.viewport.state.selectedObject
}
}
})

32
src/designer/metaComponents/NumberInput.vue

@ -0,0 +1,32 @@
<template>
<el-form-item>
<template #label>
<div :title="prop.label + ' - ' + prop.field">
{{ prop.label }}
</div>
</template>
<el-input v-model="value" class="prop-editor" type="number" clearable size="small" />
</el-form-item>
</template>
<script>
import IMetaProp from './IMetaProp.ts'
export default {
mixins: [IMetaProp],
data() {
return {
value: _.get(this.object3D, this.prop.field)
}
},
methods: {
refreshValue() {
this.value = _.get(this.object3D, this.prop.field)
}
},
watch: {
value(newValue) {
_.set(this.object3D, this.prop.field, newValue)
}
}
}
</script>

32
src/designer/metaComponents/SwitchItem.vue

@ -0,0 +1,32 @@
<template>
<el-form-item>
<template #label>
<div :title="prop.label + ' - ' + prop.field">
{{ prop.label }}
</div>
</template>
<el-switch v-model="value" class="prop-editor" clearable size="small" />
</el-form-item>
</template>
<script>
import IMetaProp from './IMetaProp.ts'
export default {
mixins: [IMetaProp],
data() {
return {
value: _.get(this.object3D, this.prop.field)
}
},
methods: {
refreshValue() {
this.value = _.get(this.object3D, this.prop.field)
}
},
watch: {
value(newValue) {
_.set(this.object3D, this.prop.field, newValue)
}
}
}
</script>

32
src/designer/metaComponents/TextInput.vue

@ -0,0 +1,32 @@
<template>
<el-form-item>
<template #label>
<div :title="prop.label + ' - ' + prop.field">
{{ prop.label }}
</div>
</template>
<el-input v-model="value" class="prop-editor" type="text" clearable size="small" />
</el-form-item>
</template>
<script>
import IMetaProp from './IMetaProp.ts'
export default {
mixins: [IMetaProp],
data() {
return {
value: _.get(this.object3D, this.prop.field)
}
},
methods: {
refreshValue() {
this.value = _.get(this.object3D, this.prop.field)
}
},
watch: {
value(newValue) {
_.set(this.object3D, this.prop.field, newValue)
}
}
}
</script>

210
src/designer/metaComponents/Transform.vue

@ -0,0 +1,210 @@
<template>
<div class="gui-row">
<div class="gui-item-name"></div>
<div class="gui-item">X</div>
<div class="gui-item">Y</div>
<div class="gui-item">Z</div>
</div>
<div class="gui-row">
<div class="gui-item-name">
<component :is="renderIcon('element Rank')"></component>
</div>
<template v-if="!object3DIsNull">
<div class="gui-item">
<el-input-number v-model="position.x" size="small" :precision="3" :controls="false" />
</div>
<div class="gui-item">
<el-input-number v-model="position.y" size="small" :precision="3" :controls="false" />
</div>
<div class="gui-item">
<el-input-number v-model="position.z" size="small" :precision="3" :controls="false" />
</div>
</template>
</div>
<div class="gui-row">
<div class="gui-item-name">
<component :is="renderIcon('element RefreshLeft')"></component>
</div>
<template v-if="!object3DIsNull">
<div class="gui-item">
<el-input-number v-model="radianX" size="small" :precision="3" :controls="false" />
</div>
<div class="gui-item">
<el-input-number v-model="radianY" size="small" :precision="3" :controls="false" />
</div>
<div class="gui-item">
<el-input-number v-model="radianZ" size="small" :precision="3" :controls="false" />
</div>
</template>
</div>
<div class="gui-row">
<div class="gui-item-name">
<component :is="renderIcon('element ScaleToOriginal')"></component>
</div>
<template v-if="!object3DIsNull">
<div class="gui-item">
<el-input-number v-model="scale.x" size="small" :precision="3" :controls="false" />
</div>
<div class="gui-item">
<el-input-number v-model="scale.y" size="small" :precision="3" :controls="false" />
</div>
<div class="gui-item">
<el-input-number v-model="scale.z" size="small" :precision="3" :controls="false" />
</div>
</template>
</div>
</template>
<script>
import _ from 'lodash'
import { renderIcon } from '@/utils/webutils.js'
import { markRaw } from 'vue'
import IMetaProp from './IMetaProp.js'
export default {
mixins: [IMetaProp],
data() {
return {
isBindable: true,
object3DIsNull: true,
position: { x: 0, y: 0, z: 0 },
rotation: { x: 0, y: 0, z: 0 },
scale: { x: 1, y: 1, z: 1 }
}
},
mounted() {
},
methods: {
renderIcon,
refreshValue() {
this.isBindable = false
this.$nextTick(() => {
this.isBindable = true
})
const object3D = this.object3D
if(!object3D) {
this.object3DIsNull = true
return
}
this.position.x = object3D.position.x
this.position.y = object3D.position.y
this.position.z = object3D.position.z
this.rotation.x = object3D.rotation.x
this.rotation.y = object3D.rotation.y
this.rotation.z = object3D.rotation.z
this.scale.x = object3D.scale.x
this.scale.y = object3D.scale.y
this.scale.z = object3D.scale.z
this.object3DIsNull = false
},
},
watch: {
position: {
deep: true,
handler(newVal) {
if (this.object3D && this.isBindable) {
this.object3D.position.x = newVal.x
this.object3D.position.y = newVal.y
this.object3D.position.z = newVal.z
}
}
},
rotation: {
deep: true,
handler(newVal) {
if (this.object3D && this.isBindable) {
this.object3D.rotation.x = newVal.x
this.object3D.rotation.y = newVal.y
this.object3D.rotation.z = newVal.z
}
}
},
scale: {
deep: true,
handler(newVal) {
if (this.object3D && this.isBindable) {
this.object3D.scale.x = newVal.x
this.object3D.scale.y = newVal.y
this.object3D.scale.z = newVal.z
}
}
}
},
computed: {
//
radianX: {
get() {
return Math.round(this.rotation.x * 180 / Math.PI)
},
set(newVal) {
this.rotation.x = newVal * Math.PI / 180
}
},
radianY: {
get() {
return Math.round(this.rotation.y * 180 / Math.PI)
},
set(newVal) {
this.rotation.y = newVal * Math.PI / 180
}
},
radianZ: {
get() {
return Math.round(this.rotation.z * 180 / Math.PI)
},
set(newVal) {
this.rotation.z = newVal * Math.PI / 180
}
}
}
}
</script>
<style lang="less">
.gui-toolbar {
color: #fff;
.el-input-number.is-without-controls .el-input__wrapper {
padding-left: 2px;
padding-right: 2px;
}
.gui-row {
display: flex;
flex-direction: row;
gap: 3px;
padding: 3px 0;
.gui-item-name {
width: 26px;
align-self: stretch;
display: flex;
align-items: center;
justify-content: center;
.el-icon {
font-size: 16px;
}
}
.gui-item {
flex: 1;
text-align: center;
font-size: 12px;
.el-input-number {
width: 100%;
.el-input__wrapper {
background-color: #424242;
box-shadow: none
}
}
}
}
}
</style>

32
src/designer/metaComponents/UUIDItem.vue

@ -0,0 +1,32 @@
<template>
<el-form-item>
<template #label>
<div :title="prop.label + ' - ' + prop.field">
{{ prop.label }}
</div>
</template>
<el-input v-model="value" class="prop-editor" type="text" clearable size="small" />
</el-form-item>
</template>
<script>
import IMetaProp from './IMetaProp.ts'
export default {
mixins: [IMetaProp],
data() {
return {
value: _.get(this.object3D, this.prop.field)
}
},
methods: {
refreshValue() {
this.value = _.get(this.object3D, this.prop.field)
}
},
watch: {
value(newValue) {
_.set(this.object3D, this.prop.field, newValue)
}
}
}
</script>

6
src/designer/model2DEditor/EsDragControls.ts

@ -5,6 +5,7 @@ import Constract from '@/designer/Constract.ts'
import { getItemTypeByName } from '@/model/itemType/ItemTypeDefine.ts' import { getItemTypeByName } from '@/model/itemType/ItemTypeDefine.ts'
import type { ItemTypeDefineOption } from '@/model/itemType/ItemTypeDefine.ts' import type { ItemTypeDefineOption } from '@/model/itemType/ItemTypeDefine.ts'
import { markRaw } from 'vue' import { markRaw } from 'vue'
import EventBus from '@/runtime/EventBus'
// dragControls 绑定函数 // dragControls 绑定函数
let dragStartFn, dragFn, dragEndFn, clickblankFn let dragStartFn, dragFn, dragEndFn, clickblankFn
@ -95,7 +96,10 @@ export default class EsDragControls {
// 拖拽中 // 拖拽中
drag(e) { drag(e) {
this.viewport.dispatchSignal('objectChanged', e.object) EventBus.$emit('objectChanged', {
viewport: this,
object: e.object
})
} }
// 拖拽结束 // 拖拽结束

58
src/designer/model2DEditor/tools/SelectInspect.ts

@ -5,6 +5,7 @@ import type Viewport from '@/designer/Viewport.ts'
import { Line2 } from 'three/examples/jsm/lines/Line2.js' import { Line2 } from 'three/examples/jsm/lines/Line2.js'
import { LineGeometry } from 'three/examples/jsm/lines/LineGeometry.js' import { LineGeometry } from 'three/examples/jsm/lines/LineGeometry.js'
import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial.js' import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial.js'
import { getItemTypeByName } from '@/model/itemType/ItemTypeDefine.ts'
let pdFn, pmFn, puFn let pdFn, pmFn, puFn
@ -63,38 +64,49 @@ export default class SelectInspect implements ITool {
this.viewport.watchList.push(watch(() => this.viewport.state.selectedObject, (selectedObject) => { this.viewport.watchList.push(watch(() => this.viewport.state.selectedObject, (selectedObject) => {
this.disposeSelectionBox() this.disposeSelectionBox()
this.viewport.state.selectedObjectMeta = null // 清除之前的元数据
const expandAmount = 0.2 // 扩展包围盒的大小 if (selectedObject?.userData?.type) {
if (selectedObject !== null) { const type = selectedObject.userData.type
// 避免某些蒙皮网格的帧延迟效应(e.g. Michelle.glb) const itemTypeDefine = getItemTypeByName(type)
selectedObject.updateWorldMatrix(false, true) if (itemTypeDefine) {
this.viewport.state.selectedObjectMeta = itemTypeDefine.getMeta(selectedObject)
const box = new THREE.Box3().setFromObject(selectedObject) const expandAmount = 0.2 // 扩展包围盒的大小
box.expandByScalar(expandAmount)
const size = new THREE.Vector3() // 避免某些蒙皮网格的帧延迟效应(e.g. Michelle.glb)
box.getSize(size) selectedObject.updateWorldMatrix(false, true)
const center = new THREE.Vector3() const box = new THREE.Box3().setFromObject(selectedObject)
box.getCenter(center) box.expandByScalar(expandAmount)
// 创建包围盒几何体 const size = new THREE.Vector3()
const helperGeometry = new THREE.BoxGeometry(size.x, size.y, size.z) box.getSize(size)
const edgesGeometry = new THREE.EdgesGeometry(helperGeometry)
// 使用 LineGeometry 包装 edgesGeometry const center = new THREE.Vector3()
const lineGeom = new LineGeometry() box.getCenter(center)
//@ts-ignore
lineGeom.setPositions(edgesGeometry.attributes.position.array)
const selectionBox = new Line2(lineGeom, this.material) // 创建包围盒几何体
selectionBox.computeLineDistances() const helperGeometry = new THREE.BoxGeometry(size.x, size.y, size.z)
selectionBox.position.copy(center) const edgesGeometry = new THREE.EdgesGeometry(helperGeometry)
selectionBox.name = 'selectionBox'
this.selectionBox = selectionBox
this.viewport.scene.add(selectionBox) // 使用 LineGeometry 包装 edgesGeometry
const lineGeom = new LineGeometry()
//@ts-ignore
lineGeom.setPositions(edgesGeometry.attributes.position.array)
const selectionBox = new Line2(lineGeom, this.material)
selectionBox.computeLineDistances()
selectionBox.position.copy(center)
selectionBox.name = 'selectionBox'
this.selectionBox = selectionBox
this.viewport.scene.add(selectionBox)
}
} }
})) }))
} }

96
src/designer/viewWidgets/property/PropertyView.vue

@ -6,71 +6,75 @@
<component :is="renderIcon('element Search')"></component> <component :is="renderIcon('element Search')"></component>
</template> </template>
</el-input> </el-input>
<span class="close" @click="closeMe('hideReft')"> <span class="close" @click="closeMe()">
<component :is="renderIcon('element Close')" /> <component :is="renderIcon('element Close')" />
</span> </span>
</div> </div>
<div class="calc-right-panel"> <div class="calc-right-panel">
PropertyView1<br /> <el-form label-position="right" class="property-panel-form" size="default" @submit.native.prevent>
PropertyView2<br /> <template v-for="(itemMeta, idx) in selectedObjectMeta">
PropertyView3<br />
PropertyView4<br /> <el-divider v-if="itemMeta.editor === '-'" />
PropertyView5<br />
PropertyView6<br /> <TextInput v-else-if="itemMeta.editor === 'TextInput'"
PropertyView7<br /> :prop="itemMeta" :viewport="viewport" />
PropertyView8<br />
PropertyView9<br /> <Transform v-else-if="itemMeta.editor === 'Transform'"
PropertyView10<br /> :prop="itemMeta" :viewport="viewport" />
PropertyView11<br />
PropertyView12<br /> <SwitchItem v-else-if="itemMeta.editor === 'Switch'"
PropertyView13<br /> :prop="itemMeta" :viewport="viewport" />
PropertyView14<br />
PropertyView15<br /> <ColorItem v-else-if="itemMeta.editor === 'Color'"
PropertyView16<br /> :prop="itemMeta" :viewport="viewport" />
PropertyView17<br />
PropertyView18<br /> <UUIDItem v-else-if="itemMeta.editor === 'UUID'"
PropertyView19<br /> :prop="itemMeta" :viewport="viewport" />
PropertyView20<br />
PropertyView21<br /> <NumberInput v-else-if="itemMeta.editor === 'Number'"
PropertyView22<br /> :prop="itemMeta" :viewport="viewport" />
PropertyView23<br />
PropertyView24<br /> <template v-else>
PropertyView25<br /> 未知编辑器: {{ itemMeta.editor }}
PropertyView26<br /> </template>
PropertyView27<br /> </template>
PropertyView28<br />
PropertyView29<br /> </el-form>
PropertyView30<br />
PropertyView31<br />
PropertyView32<br />
PropertyView33<br />
PropertyView34<br />
PropertyView35<br />
PropertyView36<br />
PropertyView37<br />
PropertyView38<br />
PropertyView39<br />
PropertyView40<br />
</div> </div>
</template> </template>
<script> <script>
import IWidgets from '../IWidgets.js' import IWidgets from '../IWidgets.js'
import TextInput from '@/designer/metaComponents/TextInput.vue'
import Transform from '@/designer/metaComponents/Transform.vue'
import SwitchItem from '@/designer/metaComponents/SwitchItem.vue'
import ColorItem from '@/designer/metaComponents/ColorItem.vue'
import UUIDItem from '@/designer/metaComponents/UUIDItem.vue'
import NumberInput from '@/designer/metaComponents/NumberInput.vue'
export default { export default {
name: 'PropertyView', name: 'PropertyView',
components: {
NumberInput,
UUIDItem,
ColorItem,
SwitchItem,
TextInput, Transform
},
mixins: [IWidgets], mixins: [IWidgets],
data() { data() {
return { return {
itemTypeMeta: null,
searchKeyword: '' searchKeyword: ''
} }
}, },
watch: { computed: {
'state.selectedObject': { selectedObject() {
handler(newVal) { return this.state?.selectedObject
debugger },
} selectedObjectMeta() {
return this.state?.selectedObjectMeta
} }
} }
} }

24
src/model/itemType/ItemTypeDefine.ts

@ -33,21 +33,21 @@ export function getAllItemTypes(): ItemTypeDefineOption[] {
* "点" * "点"
*/ */
export const BASIC_META_OF_POINT: ItemTypeMeta = [ export const BASIC_META_OF_POINT: ItemTypeMeta = [
{ field: 'uuid', editor: 'uuid' }, { field: 'uuid', editor: 'UUID', label: 'uuid' },
{ field: 'name', editor: 'string' }, { field: 'name', editor: 'TextInput', label: '名称' },
{ editor: 'transform' }, { editor: 'Transform' },
{ field: 'color', editor: 'color' }, { field: 'color', editor: 'Color', label: '颜色' },
{ editor: '-' }, { editor: '-' },
{ editor: 'in_out_center' } { editor: 'IN_OUT_CENTER' }
] ]
/** /**
* "物流运输单元", * "物流运输单元",
*/ */
export const BASIC_META_OF_POINT2: ItemTypeMeta = [ export const BASIC_META_OF_POINT2: ItemTypeMeta = [
{ field: 'userData.selectable', editor: 'checkbox' }, { field: 'userData.selectable', editor: 'Switch', label: '可选中' },
{ field: 'userData.protected', editor: 'checkbox' }, { field: 'userData.protected', editor: 'Switch', label: '受保护' },
{ field: 'visible', editor: 'checkbox' } { field: 'visible', editor: 'Switch', label: '可见' }
] ]
/** /**
@ -96,8 +96,8 @@ export type ItemTypeMeta = ItemTypeMetaItem[]
export type ItemTypeMetaItem = { export type ItemTypeMetaItem = {
field?: string field?: string
editor: 'uuid' | 'string' | 'number' | label?: string
'color' | 'transform' | '-' | editor: '-' |
'select' | 'checkbox' | 'text' | 'TextInput' | 'Number' | 'Switch' | 'Select' | 'ButtonGroup' |
'in_out_center' | 'group_button' 'UUID' | 'Color' | 'Transform' | 'IN_OUT_CENTER'
} }

6
src/model/itemType/ItemTypeLine.ts

@ -101,7 +101,9 @@ export default abstract class ItemTypeLine extends ItemType {
createPoint(position: THREE.Vector3, item: ItemJson): THREE.Object3D { createPoint(position: THREE.Vector3, item: ItemJson): THREE.Object3D {
const point = this.createPointBasic(position) const point = this.createPointBasic(position)
point.name = item.name if (item.name) {
point.name = item.name
}
point.uuid = item.id || THREE.MathUtils.generateUUID() point.uuid = item.id || THREE.MathUtils.generateUUID()
point.userData = _.cloneDeep(item.dt) || {} point.userData = _.cloneDeep(item.dt) || {}
_.extend(point.userData, { _.extend(point.userData, {
@ -110,7 +112,7 @@ export default abstract class ItemTypeLine extends ItemType {
label: item.l, label: item.l,
color: item.c, color: item.c,
selectable: true, selectable: true,
protected: false, protected: false
}) })
point.rotation.set( point.rotation.set(

28
src/runtime/DefineItemType.ts

@ -1,28 +0,0 @@
import _ from 'lodash'
import type { ItemTypeDefineOption } from '@/model/itemType/ItemTypeDefine.ts'
const itemTypes: Record<string, ItemTypeDefineOption> = {}
window['itemTypes'] = itemTypes
/**
*
*/
export function defineItemType(option: ItemTypeDefineOption) {
itemTypes[option.name] = option
option.clazz.name = option.name
option.clazz.option = option
return option
}
export function getItemTypeByName(type: string): ItemTypeDefineOption {
const itemType = _.get(itemTypes, type)
if (!itemType) {
console.warn(`未找到物流单元类型定义: ${type}`)
return
}
return itemType
}
export function getAllItemTypes(): ItemTypeDefineOption[] {
return Object.values(itemTypes)
}

9
src/runtime/EventBus.js

@ -0,0 +1,9 @@
import mitt from 'mitt'
const instance = mitt()
export default {
$emit: instance.emit,
$on: instance.on,
$off: instance.off
}

44
src/utils/webutils.ts

@ -5,31 +5,57 @@ import { ElIcon } from 'element-plus'
import * as FaIcon from '@vicons/fa' import * as FaIcon from '@vicons/fa'
import * as ElementPlusIconsVue from '@element-plus/icons-vue' import * as ElementPlusIconsVue from '@element-plus/icons-vue'
/**
* 0x409EFF '#409EFF'
*/
export function getColorFromCode(value: number): string {
if (value === undefined || value === null) {
return '#000000' // 默认黑色
}
if (typeof value !== 'number') {
console.warn('getColorFromCode: value is not a number', value)
return '#000000' // 默认黑色
}
const hex = value.toString(16).toUpperCase()
return `#${hex.padStart(6, '0')}`
}
/**
* '#409EFF' 0x409EFF
*/
export function getColorFromString(value: string): number {
if (!value || value.length !== 7 || value[0] !== '#') {
console.warn('getColorFromString: value is not a valid color string', value)
return 0x000000 // 默认黑色
}
const hex = value.substring(1).toUpperCase()
return parseInt(hex, 16)
}
/** /**
* *
*/ */
export function numberToString(num: number) { export function numberToString(num: number) {
if (num < 0.0001) { if (num < 0.0001) {
return num.toString(); return num.toString()
} }
let fractionDigits = 2; let fractionDigits = 2
if (num < 0.01) { if (num < 0.01) {
fractionDigits = 4; fractionDigits = 4
} else if (num < 0.1) { } else if (num < 0.1) {
fractionDigits = 3; fractionDigits = 3
} }
return num.toFixed(fractionDigits); return num.toFixed(fractionDigits)
} }
/** /**
* *
*/ */
export function getUnitString(mode: CursorMode): string { export function getUnitString(mode: CursorMode): string {
if (mode === 'Measure') return "m"; if (mode === 'Measure') return 'm'
if (mode === 'MeasureArea') return "m²"; if (mode === 'MeasureArea') return 'm²'
if (mode === 'MeasureAngle') return "°"; if (mode === 'MeasureAngle') return '°'
return ""; return ''
} }
export function normalizeShortKey(key: string): string { export function normalizeShortKey(key: string): string {

Loading…
Cancel
Save