@jnrs/lingshu-smart 2.2.6 → 2.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/App.vue.d.ts +2 -1
- package/dist/App.vue.js +30 -0
- package/dist/assets/data/editor_default.d.ts +8 -1
- package/dist/assets/data/editor_default.js +59 -0
- package/dist/assets/data/editor_default_setting.d.ts +16 -0
- package/dist/assets/data/editor_default_setting.js +58 -0
- package/dist/assets/data/editor_preset.d.ts +8 -1
- package/dist/assets/data/editor_preset.js +51 -0
- package/dist/assets/data/editor_preset_menu.d.ts +9 -0
- package/dist/assets/data/editor_preset_menu.js +290 -0
- package/dist/components/editor/ControlBar/CaseManger.vue.d.ts +3 -15
- package/dist/components/editor/ControlBar/CaseManger.vue.js +465 -0
- package/dist/components/editor/ControlBar/CaseTitleEdit.vue.d.ts +2 -411
- package/dist/components/editor/ControlBar/CaseTitleEdit.vue.js +108 -0
- package/dist/components/editor/ControlBar/ExportImportDialog.vue.d.ts +3 -2
- package/dist/components/editor/ControlBar/ExportImportDialog.vue.js +280 -0
- package/dist/components/editor/ControlBar/MainButton.vue.d.ts +3 -2
- package/dist/components/editor/ControlBar/MainButton.vue.js +183 -0
- package/dist/components/editor/ControlBar/StepButton.vue.d.ts +2 -1
- package/dist/components/editor/ControlBar/StepButton.vue.js +147 -0
- package/dist/components/editor/ControlBar/StepRecordVisualizer.vue.d.ts +2 -1
- package/dist/components/editor/ControlBar/StepRecordVisualizer.vue.js +98 -0
- package/dist/components/editor/DrawingBoard.vue.d.ts +2 -1
- package/dist/components/editor/DrawingBoard.vue.js +31 -0
- package/dist/components/editor/MenuCard/Equipments.vue.d.ts +2 -1
- package/dist/components/editor/MenuCard/Equipments.vue.js +129 -0
- package/dist/components/editor/MenuCard/Layers.vue.d.ts +2 -1
- package/dist/components/editor/MenuCard/Layers.vue.js +201 -0
- package/dist/components/editor/MenuCard/index.vue.d.ts +2 -1
- package/dist/components/editor/MenuCard/index.vue.js +84 -0
- package/dist/components/editor/OptionCard/Equipment/Base.vue.d.ts +2 -1
- package/dist/components/editor/OptionCard/Equipment/Base.vue.js +96 -0
- package/dist/components/editor/OptionCard/Equipment/Binds.vue.d.ts +3 -0
- package/dist/components/editor/OptionCard/Equipment/Binds.vue.js +443 -0
- package/dist/components/editor/OptionCard/Equipment/Bot.vue.d.ts +3 -0
- package/dist/components/editor/OptionCard/Equipment/Bot.vue.js +81 -0
- package/dist/components/editor/OptionCard/Equipment/DataSets.vue.d.ts +2 -1
- package/dist/components/editor/OptionCard/Equipment/DataSets.vue.js +304 -0
- package/dist/components/editor/OptionCard/Equipment/DockStore.vue.d.ts +3 -0
- package/dist/components/editor/OptionCard/Equipment/DockStore.vue.js +597 -0
- package/dist/components/editor/OptionCard/Equipment/Interaction.vue.d.ts +2 -1
- package/dist/components/editor/OptionCard/Equipment/Interaction.vue.js +287 -0
- package/dist/components/editor/OptionCard/Equipment/Position.backup.vue.d.ts +3 -0
- package/dist/components/editor/OptionCard/Equipment/Position.backup.vue.js +440 -0
- package/dist/components/editor/OptionCard/Equipment/Position.vue.d.ts +2 -1
- package/dist/components/editor/OptionCard/Equipment/Position.vue.js +436 -0
- package/dist/components/editor/OptionCard/Equipment/StationSlotGroup.vue.d.ts +2 -1
- package/dist/components/editor/OptionCard/Equipment/StationSlotGroup.vue.js +1131 -0
- package/dist/components/editor/OptionCard/Equipment/Store.vue.d.ts +3 -0
- package/dist/components/editor/OptionCard/Equipment/Store.vue.js +453 -0
- package/dist/components/editor/OptionCard/Equipment/Trailer.vue.d.ts +3 -0
- package/dist/components/editor/OptionCard/Equipment/Trailer.vue.js +1109 -0
- package/dist/components/editor/OptionCard/Equipment/index.vue.d.ts +2 -1
- package/dist/components/editor/OptionCard/Equipment/index.vue.js +100 -0
- package/dist/components/editor/OptionCard/Equipment/models/MachineTool.vue.d.ts +3 -0
- package/dist/components/editor/OptionCard/Equipment/models/MachineTool.vue.js +99 -0
- package/dist/components/editor/OptionCard/Equipment/models/Rail.vue.d.ts +3 -0
- package/dist/components/editor/OptionCard/Equipment/models/Rail.vue.js +225 -0
- package/dist/components/editor/OptionCard/Equipment/models/SingleGripperRobot.vue.d.ts +3 -0
- package/dist/components/editor/OptionCard/Equipment/models/SingleGripperRobot.vue.js +99 -0
- package/dist/components/editor/OptionCard/Scene/Background.vue.d.ts +2 -1
- package/dist/components/editor/OptionCard/Scene/Background.vue.js +543 -0
- package/dist/components/editor/OptionCard/Scene/index.vue.d.ts +2 -1
- package/dist/components/editor/OptionCard/Scene/index.vue.js +200 -0
- package/dist/components/editor/OptionCard/index.vue.d.ts +2 -1
- package/dist/components/editor/OptionCard/index.vue.js +73 -0
- package/dist/components/editor/index.vue.d.ts +4 -13
- package/dist/components/editor/index.vue.js +156 -0
- package/dist/components/index.d.ts +2 -2
- package/dist/components/index.js +4 -25440
- package/dist/components/preview/index.vue.d.ts +6 -5
- package/dist/components/preview/index.vue.js +104 -0
- package/dist/composables/useStepRecord.d.ts +12 -4
- package/dist/composables/useStepRecord.js +135 -0
- package/dist/constants/options.d.ts +8 -1
- package/dist/constants/options.js +55 -0
- package/dist/controller/AppController.d.ts +10 -3
- package/dist/controller/AppController.js +168 -0
- package/dist/controller/EditorController.d.ts +9 -2
- package/dist/controller/EditorController.js +173 -0
- package/dist/index.js +1 -1
- package/dist/locales/en.js +5 -0
- package/dist/locales/i18next.d.ts +1 -1
- package/dist/locales/i18next.js +6 -0
- package/dist/locales/index.d.ts +2 -2
- package/dist/locales/index.js +3 -13
- package/dist/locales/zhCn.js +5 -0
- package/dist/main.d.ts +3 -0
- package/dist/main.js +13 -0
- package/dist/playground/EditPageDemo.vue.d.ts +2 -1
- package/dist/playground/EditPageDemo.vue.js +57 -0
- package/dist/playground/ViewPageDemo.vue.d.ts +2 -27
- package/dist/playground/ViewPageDemo.vue.js +277 -0
- package/dist/stores/index.js +3 -0
- package/dist/stores/useAppStore.d.ts +13 -6
- package/dist/stores/useAppStore.js +78 -0
- package/dist/stores/useDataStore.d.ts +18 -11
- package/dist/stores/useDataStore.js +116 -0
- package/dist/stores/useEditorStore.d.ts +27 -20
- package/dist/stores/useEditorStore.js +263 -0
- package/dist/types/app.types.d.ts +1 -1
- package/dist/types/app.types.js +2 -0
- package/dist/types/base.types.d.ts +4 -2
- package/dist/types/base.types.js +1 -0
- package/dist/types/components/config.types.d.ts +11 -3
- package/dist/types/components/config.types.js +1 -0
- package/dist/types/components/operating.types.d.ts +1 -1
- package/dist/types/components/operating.types.js +1 -0
- package/dist/types/enums.d.ts +5 -2
- package/dist/types/enums.js +46 -0
- package/dist/types/guards.d.ts +12 -2
- package/dist/types/guards.js +28 -0
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.js +8 -0
- package/dist/types/models/config.types.d.ts +74 -29
- package/dist/types/models/config.types.js +1 -0
- package/dist/types/models/operating.types.d.ts +83 -17
- package/dist/types/models/operating.types.js +1 -0
- package/dist/ui/abstract/ComponentAbs.d.ts +9 -2
- package/dist/ui/abstract/ComponentAbs.js +64 -0
- package/dist/ui/abstract/ModelAbs.d.ts +12 -2
- package/dist/ui/abstract/ModelAbs.js +119 -0
- package/dist/ui/components/Entrance.d.ts +8 -1
- package/dist/ui/components/Entrance.js +83 -0
- package/dist/ui/components/Light.d.ts +26 -3
- package/dist/ui/components/Light.js +167 -0
- package/dist/ui/components/Progress.d.ts +8 -1
- package/dist/ui/components/Progress.js +84 -0
- package/dist/ui/components/Resource.d.ts +8 -1
- package/dist/ui/components/Resource.js +70 -0
- package/dist/ui/components/StationSlot.d.ts +24 -2
- package/dist/ui/components/StationSlot.js +207 -0
- package/dist/ui/components/StationSlotGroup.d.ts +8 -1
- package/dist/ui/components/StationSlotGroup.js +110 -0
- package/dist/ui/factories/createGlass.d.ts +9 -1
- package/dist/ui/factories/createGlass.js +51 -0
- package/dist/ui/factories/createModel.d.ts +9 -2
- package/dist/ui/factories/createModel.js +35 -0
- package/dist/ui/factories/createShadow.d.ts +12 -4
- package/dist/ui/factories/createShadow.js +52 -0
- package/dist/ui/factories/createText.d.ts +7 -0
- package/dist/ui/factories/createText.js +46 -0
- package/dist/ui/models/CleaningMachine.d.ts +14 -0
- package/dist/ui/models/CleaningMachine.js +141 -0
- package/dist/ui/models/DoubleJointRobot.d.ts +23 -0
- package/dist/ui/models/DoubleJointRobot.js +545 -0
- package/dist/ui/models/LoadingStation.d.ts +16 -0
- package/dist/ui/models/LoadingStation.js +167 -0
- package/dist/ui/models/MachineTool.d.ts +5 -4
- package/dist/ui/models/MachineTool.js +184 -0
- package/dist/ui/models/MarkingMachine.d.ts +16 -0
- package/dist/ui/models/MarkingMachine.js +148 -0
- package/dist/ui/models/Rail.d.ts +15 -0
- package/dist/ui/models/Rail.js +117 -0
- package/dist/ui/models/ResourceStore.d.ts +12 -0
- package/dist/ui/models/ResourceStore.js +101 -0
- package/dist/ui/models/SingleBot.d.ts +20 -0
- package/dist/ui/models/SingleBot.js +202 -0
- package/dist/ui/models/SingleGripperRobot.d.ts +19 -0
- package/dist/ui/models/SingleGripperRobot.js +272 -0
- package/dist/ui/models/SingleJointRobot.d.ts +22 -0
- package/dist/ui/models/SingleJointRobot.js +346 -0
- package/dist/ui/shapes/NamePlate.d.ts +10 -1
- package/dist/ui/shapes/NamePlate.js +85 -0
- package/dist/ui/shapes/ScrewHole.d.ts +8 -1
- package/dist/ui/shapes/ScrewHole.js +57 -0
- package/dist/ui/shapes/Texture.d.ts +12 -3
- package/dist/ui/shapes/Texture.js +51 -0
- package/dist/ui/utils/attr.d.ts +10 -3
- package/dist/ui/utils/attr.js +50 -0
- package/dist/utils/index.js +92 -0
- package/dist/utils/type-guards.d.ts +1 -1
- package/dist/utils/type-guards.js +105 -0
- package/package.json +2 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ICase, ILayer, ILayerOperating, InteractiveUi } from '
|
|
1
|
+
import type { ICase, ILayer, ILayerOperating, InteractiveUi } from '@/types';
|
|
2
2
|
export interface PreviewActions {
|
|
3
3
|
/**
|
|
4
4
|
* 加载案例列表
|
|
@@ -28,10 +28,11 @@ type __VLS_Props = {
|
|
|
28
28
|
*/
|
|
29
29
|
caseUuid?: string;
|
|
30
30
|
};
|
|
31
|
-
declare const
|
|
32
|
-
currentCase: import(
|
|
31
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {
|
|
32
|
+
currentCase: import("vue").Ref<ICase | null | undefined, ICase | null | undefined>;
|
|
33
33
|
operating: (data: ILayerOperating) => void;
|
|
34
|
-
}, {}, {}, {}, import(
|
|
34
|
+
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
|
|
35
35
|
actions: PreviewActions;
|
|
36
|
-
}, {}, {}, {}, string, import(
|
|
36
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
37
|
+
declare const _default: typeof __VLS_export;
|
|
37
38
|
export default _default;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { vLoading } from 'element-plus';
|
|
2
|
+
import { onMounted, onUnmounted, watch } from 'vue';
|
|
3
|
+
import { storeToRefs } from 'pinia';
|
|
4
|
+
import { debounce } from '@jnrs/shared/lodash';
|
|
5
|
+
import { useDataStore, useAppStore } from '@/stores';
|
|
6
|
+
const sharedStore = useDataStore();
|
|
7
|
+
const previewStore = useAppStore();
|
|
8
|
+
const { casesLoading, cases } = storeToRefs(sharedStore);
|
|
9
|
+
const { updateCases } = sharedStore;
|
|
10
|
+
const { currentCase } = storeToRefs(previewStore);
|
|
11
|
+
const { initContainer, resizeContainer, destroyContainer, operating } = previewStore;
|
|
12
|
+
const props = withDefaults(defineProps(), {
|
|
13
|
+
actions: () => ({})
|
|
14
|
+
});
|
|
15
|
+
// 加载 cases 数据
|
|
16
|
+
const handleLoadCases = async () => {
|
|
17
|
+
if (!props.actions.loadCases) {
|
|
18
|
+
console.warn('加载 cases 数据失败,请先配置 loadCases 方法');
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
casesLoading.value = true;
|
|
22
|
+
try {
|
|
23
|
+
const res = await props.actions.loadCases();
|
|
24
|
+
updateCases(res);
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
console.error('loadCases:', error);
|
|
28
|
+
}
|
|
29
|
+
finally {
|
|
30
|
+
casesLoading.value = false;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
// 重置窗口大小
|
|
34
|
+
const resize = debounce(() => {
|
|
35
|
+
resizeContainer();
|
|
36
|
+
}, 500);
|
|
37
|
+
watch(() => props.caseUuid, async (caseUuid) => {
|
|
38
|
+
if (caseUuid) {
|
|
39
|
+
// 先获取 sharedStore 内的方案列表数据,没有的话就进行获取
|
|
40
|
+
if (!cases.value || cases.value.length === 0) {
|
|
41
|
+
await handleLoadCases();
|
|
42
|
+
}
|
|
43
|
+
initContainer({
|
|
44
|
+
appDomId: 'lingshu-smart-previewApp',
|
|
45
|
+
appEditable: false,
|
|
46
|
+
actions: {
|
|
47
|
+
onTap: props.actions.onTap,
|
|
48
|
+
onMenuTap: props.actions.onMenuTap,
|
|
49
|
+
onDragEnd: props.actions.onDragEnd
|
|
50
|
+
}
|
|
51
|
+
}, caseUuid);
|
|
52
|
+
}
|
|
53
|
+
}, { immediate: false });
|
|
54
|
+
onMounted(async () => {
|
|
55
|
+
window.addEventListener('resize', resize);
|
|
56
|
+
});
|
|
57
|
+
onUnmounted(() => {
|
|
58
|
+
destroyContainer();
|
|
59
|
+
window.removeEventListener('resize', resize);
|
|
60
|
+
});
|
|
61
|
+
const __VLS_exposed = {
|
|
62
|
+
currentCase,
|
|
63
|
+
operating
|
|
64
|
+
};
|
|
65
|
+
defineExpose(__VLS_exposed);
|
|
66
|
+
debugger; /* PartiallyEnd: #3632/scriptSetup.vue */
|
|
67
|
+
const __VLS_defaults = {
|
|
68
|
+
actions: () => ({})
|
|
69
|
+
};
|
|
70
|
+
const __VLS_ctx = {
|
|
71
|
+
...{},
|
|
72
|
+
...{},
|
|
73
|
+
...{},
|
|
74
|
+
...{},
|
|
75
|
+
};
|
|
76
|
+
let __VLS_components;
|
|
77
|
+
let __VLS_directives;
|
|
78
|
+
__VLS_asFunctionalElement(__VLS_intrinsics.div, __VLS_intrinsics.div)({
|
|
79
|
+
...{ class: "lingshu-smart" },
|
|
80
|
+
'element-loading-background': "rgba(0, 0, 0, 0.5)",
|
|
81
|
+
'element-loading-text': "方案加载中",
|
|
82
|
+
});
|
|
83
|
+
__VLS_asFunctionalDirective(__VLS_directives.vLoading)(null, { ...__VLS_directiveBindingRestFields, value: (__VLS_ctx.casesLoading) }, null, null);
|
|
84
|
+
// @ts-ignore
|
|
85
|
+
[vLoading, casesLoading,];
|
|
86
|
+
__VLS_asFunctionalElement(__VLS_intrinsics.canvas, __VLS_intrinsics.canvas)({
|
|
87
|
+
id: "lingshu-smart-previewApp",
|
|
88
|
+
});
|
|
89
|
+
if (!__VLS_ctx.currentCase) {
|
|
90
|
+
// @ts-ignore
|
|
91
|
+
[currentCase,];
|
|
92
|
+
__VLS_asFunctionalElement(__VLS_intrinsics.div, __VLS_intrinsics.div)({
|
|
93
|
+
...{ class: "preset" },
|
|
94
|
+
});
|
|
95
|
+
__VLS_asFunctionalElement(__VLS_intrinsics.i, __VLS_intrinsics.i)({});
|
|
96
|
+
}
|
|
97
|
+
/** @type {__VLS_StyleScopedClasses['lingshu-smart']} */ ;
|
|
98
|
+
/** @type {__VLS_StyleScopedClasses['preset']} */ ;
|
|
99
|
+
const __VLS_export = (await import('vue')).defineComponent({
|
|
100
|
+
setup: () => (__VLS_exposed),
|
|
101
|
+
__typeProps: {},
|
|
102
|
+
props: {},
|
|
103
|
+
});
|
|
104
|
+
export default {};
|
|
@@ -1,19 +1,27 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @Author : TanRui
|
|
3
|
+
* @WeChat : Tan578853789
|
|
4
|
+
* @File : useStepRecord.ts
|
|
5
|
+
* @Date : 2026/03/05
|
|
6
|
+
* @Desc. : 编辑操作记录 Hook (撤销/重做 + 跳转任意历史点)
|
|
7
|
+
* TODO: 未来可迁移至 IndexedDB 以支持持久化
|
|
8
|
+
*/
|
|
9
|
+
import { EnumStepType } from '@/types';
|
|
2
10
|
interface OperationRecordItem {
|
|
3
11
|
hash: string;
|
|
4
12
|
data: string;
|
|
5
13
|
}
|
|
6
14
|
export declare function useStepRecord(): {
|
|
7
15
|
MAX_OPERATION_RECORD_LENGTH: number;
|
|
8
|
-
operationRecord: import(
|
|
16
|
+
operationRecord: import("vue").Ref<{
|
|
9
17
|
hash: string;
|
|
10
18
|
data: string;
|
|
11
19
|
}[], OperationRecordItem[] | {
|
|
12
20
|
hash: string;
|
|
13
21
|
data: string;
|
|
14
22
|
}[]>;
|
|
15
|
-
currentOperationRecordIndex: import(
|
|
16
|
-
operationRecordStatus: import(
|
|
23
|
+
currentOperationRecordIndex: import("vue").Ref<number, number>;
|
|
24
|
+
operationRecordStatus: import("vue").ComputedRef<{
|
|
17
25
|
canUndo: boolean;
|
|
18
26
|
canRedo: boolean;
|
|
19
27
|
totalSteps: number;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @Author : TanRui
|
|
3
|
+
* @WeChat : Tan578853789
|
|
4
|
+
* @File : useStepRecord.ts
|
|
5
|
+
* @Date : 2026/03/05
|
|
6
|
+
* @Desc. : 编辑操作记录 Hook (撤销/重做 + 跳转任意历史点)
|
|
7
|
+
* TODO: 未来可迁移至 IndexedDB 以支持持久化
|
|
8
|
+
*/
|
|
9
|
+
import { EnumStepType } from '@/types';
|
|
10
|
+
import { storeToRefs } from 'pinia';
|
|
11
|
+
import { ref, computed, watch, nextTick } from 'vue';
|
|
12
|
+
import { debounce, cloneDeep } from '@jnrs/shared/lodash';
|
|
13
|
+
import objectHash from 'object-hash';
|
|
14
|
+
import { useDataStore } from '@/stores';
|
|
15
|
+
const MAX_OPERATION_RECORD_LENGTH = 10;
|
|
16
|
+
export function useStepRecord() {
|
|
17
|
+
const sharedStore = useDataStore();
|
|
18
|
+
const { cases } = storeToRefs(sharedStore);
|
|
19
|
+
const { updateCases } = sharedStore;
|
|
20
|
+
const operationRecord = ref([]);
|
|
21
|
+
const currentOperationRecordIndex = ref(-1);
|
|
22
|
+
const isRecordingLocked = ref(false);
|
|
23
|
+
const operationRecordStatus = computed(() => {
|
|
24
|
+
const len = operationRecord.value.length;
|
|
25
|
+
return {
|
|
26
|
+
canUndo: currentOperationRecordIndex.value > 0,
|
|
27
|
+
canRedo: currentOperationRecordIndex.value < len - 1,
|
|
28
|
+
totalSteps: len,
|
|
29
|
+
currentIndex: currentOperationRecordIndex.value
|
|
30
|
+
};
|
|
31
|
+
});
|
|
32
|
+
const recordOperation = debounce((caseData, hash) => {
|
|
33
|
+
if (!caseData || !hash)
|
|
34
|
+
return;
|
|
35
|
+
const caseDataString = JSON.stringify(caseData);
|
|
36
|
+
const currentLen = operationRecord.value.length;
|
|
37
|
+
if (currentOperationRecordIndex.value === currentLen - 1) {
|
|
38
|
+
if (currentLen >= MAX_OPERATION_RECORD_LENGTH) {
|
|
39
|
+
operationRecord.value.shift();
|
|
40
|
+
// 如果移除了最旧项,索引需同步调整(但此时在末尾,不影响)
|
|
41
|
+
}
|
|
42
|
+
if (currentLen > 0 && operationRecord.value[currentLen - 1]?.hash === hash) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
operationRecord.value.push({ hash, data: caseDataString });
|
|
46
|
+
currentOperationRecordIndex.value = operationRecord.value.length - 1;
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
// 在历史中间操作:截断后续
|
|
50
|
+
operationRecord.value.splice(currentOperationRecordIndex.value + 1);
|
|
51
|
+
operationRecord.value.push({ hash, data: caseDataString });
|
|
52
|
+
currentOperationRecordIndex.value = operationRecord.value.length - 1;
|
|
53
|
+
}
|
|
54
|
+
}, 300);
|
|
55
|
+
const applyStateFromIndex = (index) => {
|
|
56
|
+
const record = operationRecord.value[index];
|
|
57
|
+
try {
|
|
58
|
+
const caseData = JSON.parse(record.data);
|
|
59
|
+
updateCases(caseData);
|
|
60
|
+
}
|
|
61
|
+
catch (e) {
|
|
62
|
+
console.error('Failed to parse step record', e);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
const handleOperationRecordChange = (type) => {
|
|
66
|
+
const { canUndo, canRedo } = operationRecordStatus.value;
|
|
67
|
+
if (type === EnumStepType.UNDO && !canUndo)
|
|
68
|
+
return;
|
|
69
|
+
if (type === EnumStepType.REDO && !canRedo)
|
|
70
|
+
return;
|
|
71
|
+
isRecordingLocked.value = true;
|
|
72
|
+
if (type === EnumStepType.UNDO) {
|
|
73
|
+
currentOperationRecordIndex.value -= 1;
|
|
74
|
+
}
|
|
75
|
+
else if (type === EnumStepType.REDO) {
|
|
76
|
+
currentOperationRecordIndex.value += 1;
|
|
77
|
+
}
|
|
78
|
+
applyStateFromIndex(currentOperationRecordIndex.value);
|
|
79
|
+
nextTick(() => {
|
|
80
|
+
isRecordingLocked.value = false;
|
|
81
|
+
});
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* 跳转到任意历史步骤(不修改历史记录,仅切换状态)
|
|
85
|
+
* @param index 目标历史索引(0-based)
|
|
86
|
+
*/
|
|
87
|
+
const jumpToStep = (index) => {
|
|
88
|
+
const len = operationRecord.value.length;
|
|
89
|
+
if (len === 0 || index < 0 || index >= len) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
// 加锁,防止触发 watch 记录
|
|
93
|
+
isRecordingLocked.value = true;
|
|
94
|
+
currentOperationRecordIndex.value = index;
|
|
95
|
+
applyStateFromIndex(index);
|
|
96
|
+
nextTick(() => {
|
|
97
|
+
isRecordingLocked.value = false;
|
|
98
|
+
});
|
|
99
|
+
};
|
|
100
|
+
const resetFlags = (data) => {
|
|
101
|
+
if (!Array.isArray(data))
|
|
102
|
+
return;
|
|
103
|
+
data.forEach((item) => {
|
|
104
|
+
if (item && typeof item === 'object') {
|
|
105
|
+
if (Array.isArray(item.layers)) {
|
|
106
|
+
item.layers.forEach((layer) => {
|
|
107
|
+
if (layer && typeof layer === 'object') {
|
|
108
|
+
layer.selected = false;
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
};
|
|
115
|
+
watch(cases, (nv) => {
|
|
116
|
+
if (isRecordingLocked.value || !nv)
|
|
117
|
+
return;
|
|
118
|
+
const nvClone = cloneDeep(nv);
|
|
119
|
+
resetFlags(nvClone);
|
|
120
|
+
const casesNvHash = objectHash(nvClone);
|
|
121
|
+
const currentRecord = currentOperationRecordIndex.value >= 0 ? operationRecord.value[currentOperationRecordIndex.value] : null;
|
|
122
|
+
if (currentRecord && currentRecord.hash === casesNvHash) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
recordOperation(nvClone, casesNvHash);
|
|
126
|
+
}, { deep: true, immediate: true });
|
|
127
|
+
return {
|
|
128
|
+
MAX_OPERATION_RECORD_LENGTH,
|
|
129
|
+
operationRecord,
|
|
130
|
+
currentOperationRecordIndex,
|
|
131
|
+
operationRecordStatus,
|
|
132
|
+
handleOperationRecordChange,
|
|
133
|
+
jumpToStep
|
|
134
|
+
};
|
|
135
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @Author : TanRui
|
|
3
|
+
* @WeChat : Tan578853789
|
|
4
|
+
* @File : options.ts
|
|
5
|
+
* @Date : 2026/01/21
|
|
6
|
+
* @Desc. : 编辑页面的初始值配置
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* 新方案默认值
|
|
10
|
+
*/
|
|
11
|
+
export const CASE_INIT = {
|
|
12
|
+
uuid: '',
|
|
13
|
+
title: '',
|
|
14
|
+
isActive: false,
|
|
15
|
+
layers: [],
|
|
16
|
+
scene: {
|
|
17
|
+
width: 1920,
|
|
18
|
+
height: 1080,
|
|
19
|
+
zoomType: 'fit',
|
|
20
|
+
fill: {
|
|
21
|
+
type: 'solid',
|
|
22
|
+
color: '#000',
|
|
23
|
+
opacity: 1,
|
|
24
|
+
stops: []
|
|
25
|
+
},
|
|
26
|
+
texture: ''
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* 新图层默认值
|
|
31
|
+
*/
|
|
32
|
+
export const DEFAULT_LAYER = {
|
|
33
|
+
rawData: {
|
|
34
|
+
id: ''
|
|
35
|
+
},
|
|
36
|
+
uuid: '',
|
|
37
|
+
equipmentName: '',
|
|
38
|
+
width: 0,
|
|
39
|
+
height: 0,
|
|
40
|
+
x: 0,
|
|
41
|
+
y: 0,
|
|
42
|
+
percentX: 0,
|
|
43
|
+
percentY: 0,
|
|
44
|
+
rotation: 0,
|
|
45
|
+
scale: 1,
|
|
46
|
+
zIndex: 1,
|
|
47
|
+
// 交互状态
|
|
48
|
+
visible: true,
|
|
49
|
+
clickable: true,
|
|
50
|
+
menuClickable: true,
|
|
51
|
+
draggable: false,
|
|
52
|
+
// 编辑状态
|
|
53
|
+
editable: true,
|
|
54
|
+
selected: false
|
|
55
|
+
};
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @Author : TanRui
|
|
3
|
+
* @WeChat : Tan578853789
|
|
4
|
+
* @File : AppController.ts
|
|
5
|
+
* @Date : 2026/02/05
|
|
6
|
+
* @Desc. : 主画布、模型容器控制器
|
|
7
|
+
*/
|
|
8
|
+
import type { IScene, ILayer, IContainer, ILayerOperating } from '@/types';
|
|
2
9
|
import { App } from 'leafer';
|
|
3
10
|
export declare class AppController {
|
|
4
11
|
app: App;
|
|
@@ -12,8 +19,8 @@ export declare class AppController {
|
|
|
12
19
|
destroy(): void;
|
|
13
20
|
setZoom(zoomType?: 'fit' | number, duration?: number): void;
|
|
14
21
|
setTree(data?: IScene): void;
|
|
15
|
-
findAll(condition: string): import(
|
|
16
|
-
findOne(condition: string): import(
|
|
22
|
+
findAll(condition: string): import("leafer").IUI[];
|
|
23
|
+
findOne(condition: string): import("leafer").IUI | undefined;
|
|
17
24
|
removeOne(condition: string): void;
|
|
18
25
|
clearAll(): void;
|
|
19
26
|
removeModelById(uuid: string): void;
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @Author : TanRui
|
|
3
|
+
* @WeChat : Tan578853789
|
|
4
|
+
* @File : AppController.ts
|
|
5
|
+
* @Date : 2026/02/05
|
|
6
|
+
* @Desc. : 主画布、模型容器控制器
|
|
7
|
+
*/
|
|
8
|
+
import { createModel } from '@/ui/factories/createModel';
|
|
9
|
+
import { App, Box } from 'leafer';
|
|
10
|
+
const INIT_TREE_CONFIG = {
|
|
11
|
+
width: window.innerWidth,
|
|
12
|
+
height: window.innerHeight,
|
|
13
|
+
fill: undefined,
|
|
14
|
+
zoomType: 'fit'
|
|
15
|
+
};
|
|
16
|
+
export class AppController {
|
|
17
|
+
app;
|
|
18
|
+
treeBoxUI = null; // 主容器
|
|
19
|
+
modelsMap; // 模型容器映射,key 为 uuid,value 为模型实例
|
|
20
|
+
businessMap; // 业务模型映射,key 为 `${className}_${businessId}`,value 为模型实例
|
|
21
|
+
appEditable; // 是否可编辑
|
|
22
|
+
actions; // 事件处理
|
|
23
|
+
constructor(options = {}) {
|
|
24
|
+
const { appDomId, autoStart = true, appEditable = false, actions = {} } = options;
|
|
25
|
+
this.appEditable = appEditable;
|
|
26
|
+
this.actions = actions;
|
|
27
|
+
this.app = new App({
|
|
28
|
+
view: appDomId || window,
|
|
29
|
+
start: autoStart,
|
|
30
|
+
ground: {},
|
|
31
|
+
tree: { type: appEditable ? 'design' : 'game' },
|
|
32
|
+
editor: appEditable ? {} : undefined,
|
|
33
|
+
maxFPS: 60,
|
|
34
|
+
fill: '#424242',
|
|
35
|
+
pointer: {
|
|
36
|
+
preventDefault: true,
|
|
37
|
+
preventDefaultMenu: true
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
const treeBoxUI = new Box({
|
|
41
|
+
...INIT_TREE_CONFIG,
|
|
42
|
+
id: 'treeBoxUI',
|
|
43
|
+
stroke: appEditable ? { type: 'solid', color: 'rgb(242, 195, 79)' } : null
|
|
44
|
+
});
|
|
45
|
+
this.treeBoxUI = treeBoxUI;
|
|
46
|
+
this.app.tree.add(treeBoxUI);
|
|
47
|
+
this.modelsMap = new Map();
|
|
48
|
+
this.businessMap = new Map();
|
|
49
|
+
this.setZoom();
|
|
50
|
+
}
|
|
51
|
+
// 启动/重启引擎
|
|
52
|
+
start() {
|
|
53
|
+
this.app.start();
|
|
54
|
+
}
|
|
55
|
+
// 销毁引擎,默认采用异步销毁,不会立即执行
|
|
56
|
+
destroy() {
|
|
57
|
+
this.clearAll();
|
|
58
|
+
this.app.destroy();
|
|
59
|
+
}
|
|
60
|
+
// 控制视图的缩放
|
|
61
|
+
setZoom(zoomType = 'fit', duration = 0.5) {
|
|
62
|
+
requestAnimationFrame(() => {
|
|
63
|
+
if (zoomType === 'fit') {
|
|
64
|
+
const padding = this.appEditable ? [30, 10, 10, 30] : 0;
|
|
65
|
+
this.app.tree.zoom('fit', padding, false, duration);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
this.app.tree.zoom(zoomType, 0, false, duration);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
// 修改 tree 配置,不传参数则重置为默认配置
|
|
73
|
+
setTree(data) {
|
|
74
|
+
if (data) {
|
|
75
|
+
this.treeBoxUI?.set({
|
|
76
|
+
width: data.width,
|
|
77
|
+
height: data.height,
|
|
78
|
+
fill: data.fill
|
|
79
|
+
});
|
|
80
|
+
this.setZoom(data.zoomType);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
this.setTree(INIT_TREE_CONFIG);
|
|
84
|
+
this.setZoom();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// tree 层查找元素,返回数组
|
|
88
|
+
findAll(condition) {
|
|
89
|
+
return this.treeBoxUI?.find(condition) || [];
|
|
90
|
+
}
|
|
91
|
+
// tree 层查找一个元素,返回匹配到的第一个元素
|
|
92
|
+
findOne(condition) {
|
|
93
|
+
return this.treeBoxUI?.findOne(condition);
|
|
94
|
+
}
|
|
95
|
+
// tree 层移除指定的子元素
|
|
96
|
+
removeOne(condition) {
|
|
97
|
+
this.treeBoxUI?.remove(condition);
|
|
98
|
+
}
|
|
99
|
+
// tree 层清空所有元素(移除 + 销毁)
|
|
100
|
+
clearAll() {
|
|
101
|
+
this.modelsMap.clear();
|
|
102
|
+
this.businessMap.clear();
|
|
103
|
+
this.treeBoxUI?.clear();
|
|
104
|
+
}
|
|
105
|
+
// 根据 uuid 移除模型
|
|
106
|
+
removeModelById(uuid) {
|
|
107
|
+
const model = this.modelsMap.get(uuid);
|
|
108
|
+
if (model) {
|
|
109
|
+
model.destroyUI();
|
|
110
|
+
this.modelsMap.delete(uuid);
|
|
111
|
+
const businessId = model.config.rawData?.id;
|
|
112
|
+
if (businessId) {
|
|
113
|
+
this.businessMap.delete(businessId);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// 根据类型和配置添加模型
|
|
118
|
+
addModel(modelConfig) {
|
|
119
|
+
const model = createModel(modelConfig);
|
|
120
|
+
// 存储模型绑定事件
|
|
121
|
+
if (this.actions) {
|
|
122
|
+
model.init().setEventHandler({
|
|
123
|
+
onTap: (layer, data) => {
|
|
124
|
+
this.actions.onTap?.(layer, data);
|
|
125
|
+
},
|
|
126
|
+
onMenuTap: (layer, e) => {
|
|
127
|
+
this.actions.onMenuTap?.(layer, e);
|
|
128
|
+
},
|
|
129
|
+
onDragEnd: (layer, e) => {
|
|
130
|
+
this.actions.onDragEnd?.(layer, e);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
// 添加到树
|
|
135
|
+
if (this.treeBoxUI && model.ui) {
|
|
136
|
+
this.treeBoxUI.add(model.ui);
|
|
137
|
+
}
|
|
138
|
+
const uuid = modelConfig.uuid;
|
|
139
|
+
const businessId = modelConfig.rawData?.id;
|
|
140
|
+
// 存储模型引用
|
|
141
|
+
if (uuid) {
|
|
142
|
+
this.modelsMap.set(uuid, model);
|
|
143
|
+
}
|
|
144
|
+
// 存储业务模型引用
|
|
145
|
+
if (businessId) {
|
|
146
|
+
this.businessMap.set(`${modelConfig.className}_${businessId}`, model);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// 移除并重新添加模型
|
|
150
|
+
rerenderModel(modelConfig) {
|
|
151
|
+
if (modelConfig.uuid) {
|
|
152
|
+
this.removeModelById(modelConfig.uuid);
|
|
153
|
+
}
|
|
154
|
+
this.addModel(modelConfig);
|
|
155
|
+
}
|
|
156
|
+
// 模型操作
|
|
157
|
+
operating(data) {
|
|
158
|
+
const layer = this.businessMap.get(`${data.className}_${data.id}`);
|
|
159
|
+
if (!layer) {
|
|
160
|
+
console.warn('模型操作失败: 模型未匹配到', {
|
|
161
|
+
className: data.className,
|
|
162
|
+
id: data.id
|
|
163
|
+
});
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
layer.handleOperation(data);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
@@ -1,5 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @Author : TanRui
|
|
3
|
+
* @WeChat : Tan578853789
|
|
4
|
+
* @File : EditorController.ts
|
|
5
|
+
* @Date : 2025/02/26
|
|
6
|
+
* @Desc. : 图形编辑类
|
|
7
|
+
*/
|
|
8
|
+
import type { ILayer } from '@/types';
|
|
9
|
+
import type { AppController } from './AppController';
|
|
3
10
|
interface EditorOptions {
|
|
4
11
|
container: AppController;
|
|
5
12
|
zoomMin?: number;
|