yui-image-editor 1.0.0

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.
Files changed (52) hide show
  1. package/.editorconfig +8 -0
  2. package/.gitattributes +1 -0
  3. package/.nrmrc +1 -0
  4. package/.oxlintrc.json +10 -0
  5. package/.prettierrc.json +6 -0
  6. package/.vscode/extensions.json +11 -0
  7. package/README.md +73 -0
  8. package/dist/types/index.d.ts +1 -0
  9. package/dist-app/assets/css/main-Dn6XCgL-.css +1 -0
  10. package/dist-app/assets/js/main-Bo-yvzk4.js +3 -0
  11. package/dist-app/favicon.ico +0 -0
  12. package/dist-app/index.html +14 -0
  13. package/e2e/tsconfig.json +4 -0
  14. package/e2e/vue.spec.ts +8 -0
  15. package/env.d.ts +1 -0
  16. package/eslint.config.ts +38 -0
  17. package/index.html +13 -0
  18. package/package.json +94 -0
  19. package/playwright.config.ts +110 -0
  20. package/public/favicon.ico +0 -0
  21. package/src/App.vue +165 -0
  22. package/src/assets/annotation/hjjd.svg +33 -0
  23. package/src/assets/annotation/kjjd.svg +38 -0
  24. package/src/assets/annotation/zbz.svg +19 -0
  25. package/src/assets/base.css +86 -0
  26. package/src/assets/logo.svg +1 -0
  27. package/src/assets/main.css +28 -0
  28. package/src/assets/variable.scss +15 -0
  29. package/src/components/ImageEditor/index.ts +220 -0
  30. package/src/components/ImageEditor/index.vue +344 -0
  31. package/src/components/icons/IconCommunity.vue +7 -0
  32. package/src/components/icons/IconDocumentation.vue +7 -0
  33. package/src/components/icons/IconEcosystem.vue +7 -0
  34. package/src/components/icons/IconSupport.vue +7 -0
  35. package/src/components/icons/IconTooling.vue +19 -0
  36. package/src/index.ts +25 -0
  37. package/src/main.ts +11 -0
  38. package/src/router/index.ts +30 -0
  39. package/src/stores/counter.ts +12 -0
  40. package/src/types/index.ts +196 -0
  41. package/src/types/tui.d.ts +27 -0
  42. package/src/utils/specialMapTool.ts +358 -0
  43. package/src/views/HomeView.vue +12 -0
  44. package/src/views/MapPluginView.vue +16 -0
  45. package/src/views/TestView.vue +12 -0
  46. package/tsconfig.app.json +18 -0
  47. package/tsconfig.json +14 -0
  48. package/tsconfig.node.json +28 -0
  49. package/tsconfig.vitest.json +19 -0
  50. package/vite.config.ts +124 -0
  51. package/vitest.config.ts +27 -0
  52. package/yui-image-editor-1.0.0.tgz +0 -0
@@ -0,0 +1,15 @@
1
+ // src/styles/variable.scss
2
+ // 示例:全局颜色变量
3
+ $primary-color: #165DFF;
4
+ $success-color: #36CFC9;
5
+ $warning-color: #FF7D00;
6
+ $error-color: #F53F3F;
7
+ // 示例:全局字体变量
8
+ $font-size-base: 14px;
9
+ $font-size-large: 16px;
10
+ $font-size-small: 12px;
11
+ // 示例:全局间距变量
12
+ $spacing-xs: 4px;
13
+ $spacing-sm: 8px;
14
+ $spacing-md: 16px;
15
+ $spacing-lg: 24px;
@@ -0,0 +1,220 @@
1
+ export const zhLocale = {
2
+ // 核心菜单
3
+ 'Crop': '裁剪',
4
+ 'Rotate': '旋转',
5
+ 'Flip': '翻转',
6
+ 'Draw': '涂鸦',
7
+ 'Shape': '形状标注',
8
+ 'Icon': '图标标注',
9
+ 'Text': '文字标注',
10
+ 'Mask': '蒙版',
11
+ 'Filter': '滤镜',
12
+ 'Resize': '调整尺寸',
13
+ 'Undo': '撤销',
14
+ 'Redo': '重做',
15
+ 'Delete': '删除',
16
+ 'Save': '保存',
17
+ 'Load': '加载图片',
18
+ 'Download': '下载',
19
+ 'Reset': '重置',
20
+ 'Cancel': '取消',
21
+ 'OK': '确定',
22
+ 'Apply': '应用',
23
+ 'Close': '关闭',
24
+ // 裁剪相关
25
+ 'Free': '自由裁剪',
26
+ 'Original': '原始比例',
27
+ 'Square': '正方形',
28
+ '4:3': '4:3',
29
+ '3:4': '3:4',
30
+ '16:9': '16:9',
31
+ '9:16': '9:16',
32
+ 'Custom': '自定义比例',
33
+ 'Width': '宽度',
34
+ 'Height': '高度',
35
+ 'Ratio': '比例',
36
+ 'Lock ratio': '锁定比例',
37
+ 'Unlock ratio': '解锁比例',
38
+ // 旋转/翻转
39
+ 'Rotate CW': '顺时针旋转',
40
+ 'Rotate CCW': '逆时针旋转',
41
+ 'Flip X': '水平翻转',
42
+ 'Flip Y': '垂直翻转',
43
+ // 形状/涂鸦
44
+ 'Rectangle': '矩形',
45
+ 'Circle': '圆形',
46
+ 'Triangle': '三角形',
47
+ 'Line': '直线',
48
+ 'Pen': '画笔',
49
+ 'Eraser': '橡皮擦',
50
+ 'Color': '颜色',
51
+ 'Opacity': '透明度',
52
+ 'Fill': '填充',
53
+ 'Stroke': '描边',
54
+ // 文字编辑
55
+ 'Font': '字体',
56
+ 'Size': '大小',
57
+ 'Align': '对齐',
58
+ 'Left': '左对齐',
59
+ 'Center': '居中',
60
+ 'Right': '右对齐',
61
+ 'Bold': '粗体',
62
+ 'Italic': '斜体',
63
+ 'Underline': '下划线',
64
+ 'Text color': '文字颜色',
65
+ // 滤镜(含新增项)
66
+ 'Grayscale': '灰度',
67
+ 'Sepia': '怀旧',
68
+ 'Sepia2': '复古2', // 新增
69
+ 'Invert': '反色',
70
+ 'Brightness': '亮度',
71
+ 'Contrast': '对比度',
72
+ 'Saturation': '饱和度',
73
+ 'Hue': '色相',
74
+ 'Blur': '模糊',
75
+ 'Sharpen': '锐化',
76
+ 'Remove White': '去白底', // 新增
77
+ 'Distance': '距离', // 新增
78
+ 'Pixelate': '像素化', // 新增
79
+ 'Color Filter': '色彩滤镜', // 新增
80
+ 'Threshold': '阈值', // 新增
81
+ 'Noise': '杂色/噪点', // 新增(Noise 是「噪点/杂色」滤镜,翻译为「杂色」更符合图片编辑习惯)
82
+ // 提示/弹窗
83
+ 'Image Loaded': '图片加载完成',
84
+ 'Save image as': '保存图片为',
85
+ 'Are you sure you want to reset?': '确定要重置所有编辑吗?',
86
+ 'Failed to load image': '图片加载失败',
87
+ 'Please select an image': '请选择一张图片',
88
+ 'Image size is too large': '图片尺寸过大',
89
+ 'Minimum size is': '最小尺寸为',
90
+ 'Maximum size is': '最大尺寸为',
91
+ // 其他
92
+ 'Zoom': '缩放',
93
+ 'Zoom In': '放大',
94
+ 'Zoom Out': '缩小',
95
+ 'Hand': '抓手',
96
+ 'History': '历史记录',
97
+ 'Delete All': '全部删除',
98
+ 'Fit to canvas': '适配画布',
99
+ 'Actual size': '实际尺寸',
100
+ 'Custom size': '自定义尺寸',
101
+ 'Background color': '背景颜色',
102
+ 'File type': '文件类型',
103
+ 'JPEG': 'JPEG 格式',
104
+ 'PNG': 'PNG 格式',
105
+ 'Quality': '质量',
106
+ 'Duplicate': '复制',
107
+ 'Bring to front': '移到顶层',
108
+ 'Send to back': '移到底层',
109
+ 'Blend mode': '混合模式',
110
+ // 'Arrow-2': '箭头-2',
111
+ // 'Arrow-3': '箭头-3',
112
+ // 'Star-1': '星形-1',
113
+ // 'Star-2': '星形-2',
114
+ // 'Polygon': '多边形',
115
+ 'Arrow': '箭头',
116
+ 'Arrow-2': '标注线',
117
+ 'Arrow-3': '箭头3',
118
+ 'Star-1': '指北针',
119
+ 'Star-2': '空军基地',
120
+ 'Polygon': '海军基地',
121
+ 'Location': '定位',
122
+ 'Heart': '爱心',
123
+ 'Bubble': '气泡',
124
+ 'DeleteAll': '删除全部',
125
+ 'ZoomIn': '放大',
126
+ 'ZoomOut':'缩小',
127
+ 'Text size': '文字大小',
128
+ 'Lock Aspect Ratio': '锁定纵横比'
129
+ }
130
+
131
+ export const customTheme = {
132
+ // // 主容器背景
133
+ // 'common.bi.imageEditor': '#1E1E2E',
134
+ // 'common.bi.backgroundColor': '#2D2D3F',
135
+ // // 菜单基础样式
136
+ // 'menu.backgroundColor': '#2D2D3F',
137
+ // 'menu.border': '1px solid #383850',
138
+ // // 菜单图标颜色
139
+ // 'menu.normalIcon.color': '#94A3B8',
140
+ // 'menu.activeIcon.color': '#36BFFA',
141
+ // 'menu.disabledIcon.color': '#52526B',
142
+ // 'menu.hoverIcon.color': '#74B0FF',
143
+ // // 子菜单样式
144
+ // 'submenu.backgroundColor': '#2D2D3F',
145
+ // 'submenu.border': '1px solid #383850',
146
+ // 'submenu.normalIcon.color': '#94A3B8',
147
+ // 'submenu.activeIcon.color': '#36BFFA',
148
+ // // 按钮样式
149
+ // 'button.backgroundColor': '#383850',
150
+ // 'button.border': '1px solid #484868',
151
+ // 'button.color': '#E2E8F0',
152
+ // 'button.disabledColor': '#52526B',
153
+ // 'button.hover.backgroundColor': '#484868',
154
+ // 'button.active.backgroundColor': '#36BFFA',
155
+ // 'button.active.color': '#FFFFFF',
156
+ // // 滑块样式
157
+ // 'range.pointer.color': '#36BFFA',
158
+ // 'range.bar.color': '#484868',
159
+ // 'range.subbar.color': '#36BFFA',
160
+ // 'range.disabledPointer.color': '#52526B',
161
+ // 'range.disabledBar.color': '#383850',
162
+ // // 颜色选择器
163
+ // 'colorpicker.button.border': '1px solid #484868',
164
+ // 'colorpicker.button.backgroundColor': '#383850',
165
+ // 'colorpicker.title.color': '#E2E8F0',
166
+ // // 弹窗样式
167
+ // 'dialog.backgroundColor': '#2D2D3F',
168
+ // 'dialog.border': '1px solid #383850',
169
+ // 'dialog.title.color': '#E2E8F0',
170
+ // 'dialog.body.color': '#94A3B8',
171
+ // // 裁剪框
172
+ // 'cropzone.border': '2px solid #36BFFA',
173
+ // 'cropzone.handler.color': '#36BFFA',
174
+ // // 选择框
175
+ // 'selection.border': '2px solid #36BFFA',
176
+ // 'selection.handler.color': '#36BFFA',
177
+ // 'selection.rotatingPoint.color': '#74B0FF',
178
+
179
+
180
+
181
+ // 'common.bi.image': '',
182
+ // 'common.bisize.width': '0px',
183
+ // 'common.bisize.height': '0px',
184
+ // 'common.backgroundImage': 'none',
185
+ // 'common.backgroundColor': '#f3f4f6',
186
+ // 'common.border': '1px solid #333',
187
+ // //header
188
+ // 'header.backgroundImage': 'none',
189
+ // 'header.backgroundColor': '#f3f4f6',
190
+ // 'header.border': '0px',
191
+ // // load button
192
+ // 'loadButton.backgroundColor': '#fff',
193
+ // 'loadButton.border': '1px solid #ddd',
194
+ // 'loadButton.color': '#222',
195
+ // 'loadButton.fontFamily': 'NotoSans, sans-serif',
196
+ // 'loadButton.fontSize': '12px',
197
+ // 'loadButton.display': 'none',
198
+ // // download button
199
+ // 'downloadButton.backgroundColor': '#fff',
200
+ // 'downloadButton.border': '1px solid #ddd',
201
+ // 'downloadButton.color': '#222',
202
+ // 'downloadButton.fontFamily': 'NotoSans, sans-serif',
203
+ // 'downloadButton.fontSize': '12px',
204
+ // 'downloadButton.display': 'none',
205
+ // // icon default
206
+ // // submenu primary color
207
+ // // subment labels
208
+ // // checkbox style
209
+ // // rango style
210
+ // // colorpicker style
211
+ // 'colorpicker.button.border': '1px solid #1e1e1e',
212
+ // 'colorpicker.title.color': '#fff',
213
+ // // 'submenu.normalIcon.path': arrowSvg,
214
+ // // 'submenu.activeIcon.path': arrowStar,
215
+ // // 'menu.normalIcon.path': arrowSvg,
216
+ // // 'menu.activeIcon.path': arrowStar,
217
+ // // 'menu.disabledIcon.path': arrowSvg,
218
+ // // 'menu.hoverIcon.path': arrowStar,
219
+ // // 'icon.arrow': ,
220
+ }
@@ -0,0 +1,344 @@
1
+ <!-- eslint-disable vue/multi-word-component-names -->
2
+ <template>
3
+ <div ref="editorContainer" class="editor-container">
4
+ <div ref="editorRef" class="image-editor-wrapper"></div>
5
+ </div>
6
+ </template>
7
+
8
+ <script setup lang="ts">
9
+ /* eslint-disable @typescript-eslint/no-explicit-any */
10
+ import hjjd from '@/assets/annotation/hjjd.svg';
11
+ import kjjd from '@/assets/annotation/kjjd.svg';
12
+ import zbz from '@/assets/annotation/zbz.svg';
13
+ import type { TuiImageEditor } from '@/types';
14
+ import { SpecialMapTool } from '@/utils/specialMapTool';
15
+ import ImageEditor from 'tui-image-editor';
16
+ import type { Ref } from 'vue';
17
+ import { onMounted, onUnmounted, reactive, ref } from 'vue';
18
+ import { customTheme, zhLocale } from './index';
19
+ // ==================== 响应式数据定义 ====================
20
+ const editorRef: Ref<HTMLDivElement | null> = ref(null);
21
+ const editorContainer: Ref<HTMLDivElement | null> = ref(null);
22
+
23
+ let imageEditor: TuiImageEditor | any = null;
24
+ let specialMapTool: SpecialMapTool | null = null;
25
+
26
+ const canvasSize = reactive({ width: 0, height: 0 });
27
+ const mousePosition = reactive({ x: 0, y: 0 });
28
+ const elementCount = ref(0);
29
+ const operationStatus = ref<{ message: string; type: 'success' | 'error' | 'info' } | null>(null);
30
+ const updateImageHandler = () => {
31
+ const imgEl: any = document.querySelectorAll('.tui-image-editor-menu-icon .tui-image-editor-submenu-item .tie-icon-add-button:first-child .tui-image-editor-button>div')
32
+ if (imgEl) {
33
+ imgEl[3].innerHTML = `<img src="${zbz}"/>`
34
+ imgEl[4].innerHTML = `<img src="${kjjd}"/>`
35
+ imgEl[5].innerHTML = `<img src="${hjjd}"/>`
36
+ }
37
+ }
38
+ const addTextHandler = () => {
39
+ const menuEl = document.querySelector('.tui-image-editor-menu-text .tui-image-editor-submenu-item')
40
+ const html = `<li class="tie-text-effect-button">
41
+ <div class="tui-image-editor-button title">
42
+ <div>
43
+ <span class="text-icon" >🏷️</span>
44
+ </div>
45
+ <label> 标题 </label>
46
+ </div>
47
+ <div class="tui-image-editor-button date">
48
+ <div>
49
+ <span class="text-icon">🕒</span>
50
+ </div>
51
+ <label> 时间 </label>
52
+ </div>
53
+ <div class="tui-image-editor-button attached">
54
+ <div>
55
+ <span class="text-icon">📋</span>
56
+ </div>
57
+ <label> 附图标签 </label>
58
+ </div>
59
+ </li>`
60
+ menuEl?.insertAdjacentHTML('afterbegin', html);
61
+ menuEl?.addEventListener('click', function (event: any) {
62
+ const button = event.target.closest('.tui-image-editor-button');
63
+ if (!button) return;
64
+ console.log(button,'===buttton')
65
+ if (button.classList.contains('title')) {
66
+ // 处理标题逻辑
67
+ addTitle();
68
+ } else if (button.classList.contains('date')) {
69
+ // 处理时间逻辑
70
+ addTimestamp();
71
+ } else if (button.classList.contains('attached')) {
72
+ // 处理附图标签逻辑
73
+ addFigureLabel();
74
+ }
75
+ });
76
+ }
77
+
78
+ const addShapeHandler = () => {
79
+ const menuEl = document.querySelector('.tui-image-editor-menu-shape .tui-image-editor-submenu-item')
80
+ const html = `<li class="tie-shape-button">
81
+ <div class="tui-image-editor-button comparison">
82
+ <div>
83
+ <span class="text-icon gray" > ▢ </span>
84
+ </div>
85
+ <label> 对比拉框 </label>
86
+ </div>
87
+ <div class="tui-image-editor-button calibration">
88
+ <div>
89
+ <span class="text-icon gray"> ⬚ </span>
90
+ </div>
91
+ <label> 标定目标 </label>
92
+ </div>
93
+ </li>`
94
+ menuEl?.insertAdjacentHTML('afterbegin', html);
95
+ menuEl?.addEventListener('click', function (event: any) {
96
+ const button = event.target.closest('.tui-image-editor-button');
97
+ if (!button) return;
98
+ if (button.classList.contains('comparison')) {
99
+ // 处理标题逻辑
100
+ addComparisonBox();
101
+ } else if (button.classList.contains('calibration')) {
102
+ // 处理时间逻辑
103
+ addCalibrationTarget();
104
+ } else if (button.classList.contains('attached')) {
105
+ // 处理附图标签逻辑
106
+ // addFigureLabel();
107
+ }
108
+ });
109
+ }
110
+ const addMenuResize = () => {
111
+ const menuEl = document.querySelector('.tui-image-editor-menu-resize')
112
+
113
+ const alignMenuEl = document.querySelector('.tui-image-editor-menu-resize .tui-image-editor-submenu-item .tui-image-editor-submenu-align')
114
+ const statsMenuItem = document.createElement('li');
115
+ statsMenuItem.className = 'tui-image-editor-submenu-align tui-image-editor-submenu-A4';
116
+ statsMenuItem.innerHTML = `
117
+ <div class="tui-image-editor-checkbox-wrap">
118
+ <div class="tui-image-editor-checkbox">
119
+ <label>
120
+ <input type="checkbox" class="tie-lock-aspect-ratio tie-lock-aspect-a4">
121
+ <span>开启 A4-横版</span>
122
+ </label>
123
+ </div>
124
+ </div>
125
+ `
126
+ if (alignMenuEl?.parentNode) {
127
+ alignMenuEl.parentNode.insertBefore(statsMenuItem, alignMenuEl.nextSibling);
128
+ }
129
+ menuEl?.addEventListener('click', function (event: any) {
130
+ const button = event.target.closest('.tui-image-editor-submenu-A4');
131
+ if (!button) return;
132
+ const checkbox:any = document.querySelector('.tie-lock-aspect-a4');
133
+ if(checkbox.checked){
134
+ console.log(checkbox.checked,'===checkbox.checked')
135
+ const currentImageSize = imageEditor.getCanvasSize();
136
+ console.log(currentImageSize,'===currentImageSize')
137
+ const a4Width: number = 1754;
138
+ const a4Height: number = 1240;
139
+ imageEditor.resize({
140
+ width: a4Width, // 新宽度
141
+ height: a4Height // 新高度
142
+ }).then(() => {
143
+ console.log('图像尺寸调整成功');
144
+ }).catch(() => {
145
+ });
146
+ }else{
147
+ imageEditor.resize({
148
+ width: 800, // 新宽度
149
+ height: 600 // 新高度
150
+ }).then(() => {
151
+ console.log('图像尺寸调整成功');
152
+ }).catch(() => {
153
+ });
154
+ }
155
+ });
156
+ }
157
+ //
158
+ // ==================== 生命周期钩子 ====================
159
+ onMounted((): void => {
160
+ if (!editorRef.value) {
161
+ showStatus('编辑器容器未找到', 'error');
162
+ return;
163
+ }
164
+
165
+ try {
166
+ // 初始化图片编辑器
167
+ imageEditor = new (ImageEditor as any)(editorRef.value, {
168
+ includeUI: {
169
+ loadImage: {
170
+ path: 'https://picsum.photos/800/600',
171
+ name: 'DefaultImage'
172
+ },
173
+ initMenu: 'icon',
174
+ menuBarPosition: 'left',
175
+ locale: zhLocale,
176
+ theme: customTheme,
177
+ uiSize: {
178
+ width: '100%',
179
+ height: '100%'
180
+ },
181
+ },
182
+ selectionStyle: {
183
+ cornerSize: 20,
184
+ rotatingPointOffset: 70
185
+ },
186
+ usageStatistics: true,
187
+ cssMaxHeight: document.documentElement.clientWidth,
188
+ cssMaxWidth: document.documentElement.clientHeight,
189
+ }) as TuiImageEditor;
190
+
191
+ // 初始化专题图工具
192
+ specialMapTool = new SpecialMapTool(imageEditor);
193
+
194
+ imageEditor.resetZoom()
195
+ window.onresize = () => {
196
+ imageEditor.ui.resizeEditor()
197
+ }
198
+ // 获取画布尺寸
199
+ updateCanvasSize();
200
+ updateImageHandler();
201
+ addTextHandler();
202
+ addShapeHandler();
203
+ addMenuResize();
204
+ // 监听鼠标移动
205
+ if (editorContainer.value) {
206
+ editorContainer.value.addEventListener('mousemove', handleMouseMove);
207
+ }
208
+ showStatus('专题图工具已就绪', 'success');
209
+ } catch (error) {
210
+ console.error('初始化失败:', error);
211
+ showStatus('初始化失败,请刷新页面重试', 'error');
212
+ }
213
+ });
214
+
215
+ onUnmounted((): void => {
216
+ if (imageEditor) {
217
+ try {
218
+ imageEditor.destroy();
219
+ imageEditor = null;
220
+ specialMapTool = null;
221
+ } catch (error) {
222
+ console.error('释放编辑器资源失败:', error);
223
+ }
224
+ }
225
+
226
+ // 移除事件监听
227
+ if (editorContainer.value) {
228
+ editorContainer.value.removeEventListener('mousemove', handleMouseMove);
229
+ }
230
+ });
231
+
232
+ // ==================== 工具函数 ====================
233
+ const showStatus = (message: string, type: 'success' | 'error' | 'info' = 'info'): void => {
234
+ operationStatus.value = { message, type };
235
+ };
236
+
237
+ const updateCanvasSize = (): void => {
238
+ if (specialMapTool && imageEditor) {
239
+ const size = imageEditor.getCanvasSize();
240
+ canvasSize.width = size.width;
241
+ canvasSize.height = size.height;
242
+ }
243
+ };
244
+
245
+ const handleMouseMove = (event: MouseEvent): void => {
246
+ if (!editorContainer.value) return;
247
+
248
+ const rect = editorContainer.value.getBoundingClientRect();
249
+ mousePosition.x = Math.round(event.clientX - rect.left);
250
+ mousePosition.y = Math.round(event.clientY - rect.top);
251
+ };
252
+
253
+ // ==================== 业务函数 ====================
254
+
255
+ const addTitle = async (): Promise<void> => {
256
+ if (!specialMapTool) {
257
+ showStatus('专题图工具未初始化', 'error');
258
+ return;
259
+ }
260
+
261
+ try {
262
+ await specialMapTool.addTitle('专题图标题');
263
+ elementCount.value = (await specialMapTool.getAllElements()).length;
264
+ showStatus('标题添加成功', 'success');
265
+ } catch (error) {
266
+ console.error('添加标题失败:', error);
267
+ showStatus('添加标题失败', 'error');
268
+ }
269
+ };
270
+
271
+ const addTimestamp = async (): Promise<void> => {
272
+ if (!specialMapTool) {
273
+ showStatus('专题图工具未初始化', 'error');
274
+ return;
275
+ }
276
+
277
+ try {
278
+ await specialMapTool.addTimestamp();
279
+ elementCount.value = (await specialMapTool.getAllElements()).length;
280
+ showStatus('时间戳添加成功', 'success');
281
+ } catch (error) {
282
+ console.error('添加时间戳失败:', error);
283
+ showStatus('添加时间戳失败', 'error');
284
+ }
285
+ };
286
+
287
+ const addFigureLabel = async (): Promise<void> => {
288
+ if (!specialMapTool) {
289
+ showStatus('专题图工具未初始化', 'error');
290
+ return;
291
+ }
292
+
293
+ try {
294
+ await specialMapTool.addFigureLabel('附图');
295
+ elementCount.value = (await specialMapTool.getAllElements()).length;
296
+ showStatus('附图标签添加成功', 'success');
297
+ } catch (error) {
298
+ console.error('添加附图标签失败:', error);
299
+ showStatus('添加附图标签失败', 'error');
300
+ }
301
+ };
302
+
303
+
304
+ const addComparisonBox = async (): Promise<void> => {
305
+ if (!specialMapTool) {
306
+ showStatus('专题图工具未初始化', 'error');
307
+ return;
308
+ }
309
+
310
+ try {
311
+ await specialMapTool.addComparisonBox(400, 400, 150, 100);
312
+ elementCount.value = (await specialMapTool.getAllElements()).length;
313
+ showStatus('对比拉框添加成功', 'success');
314
+ } catch (error) {
315
+ console.error('添加对比拉框失败:', error);
316
+ showStatus('添加对比拉框失败', 'error');
317
+ }
318
+ };
319
+
320
+ const addCalibrationTarget = async (): Promise<void> => {
321
+ if (!specialMapTool) {
322
+ showStatus('专题图工具未初始化', 'error');
323
+ return;
324
+ }
325
+
326
+ try {
327
+ await specialMapTool.addCalibrationTarget(500, 500, 120, 120);
328
+ elementCount.value = (await specialMapTool.getAllElements()).length;
329
+ showStatus('标定目标添加成功', 'success');
330
+ } catch (error) {
331
+ console.error('添加标定目标失败:', error);
332
+ showStatus('添加标定目标失败', 'error');
333
+ }
334
+ };
335
+
336
+
337
+ </script>
338
+
339
+ <style lang="less" scoped>
340
+ .editor-container {
341
+ width: 100%;
342
+ height: 900px;
343
+ }
344
+ </style>
@@ -0,0 +1,7 @@
1
+ <template>
2
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
3
+ <path
4
+ d="M15 4a1 1 0 1 0 0 2V4zm0 11v-1a1 1 0 0 0-1 1h1zm0 4l-.707.707A1 1 0 0 0 16 19h-1zm-4-4l.707-.707A1 1 0 0 0 11 14v1zm-4.707-1.293a1 1 0 0 0-1.414 1.414l1.414-1.414zm-.707.707l-.707-.707.707.707zM9 11v-1a1 1 0 0 0-.707.293L9 11zm-4 0h1a1 1 0 0 0-1-1v1zm0 4H4a1 1 0 0 0 1.707.707L5 15zm10-9h2V4h-2v2zm2 0a1 1 0 0 1 1 1h2a3 3 0 0 0-3-3v2zm1 1v6h2V7h-2zm0 6a1 1 0 0 1-1 1v2a3 3 0 0 0 3-3h-2zm-1 1h-2v2h2v-2zm-3 1v4h2v-4h-2zm1.707 3.293l-4-4-1.414 1.414 4 4 1.414-1.414zM11 14H7v2h4v-2zm-4 0c-.276 0-.525-.111-.707-.293l-1.414 1.414C5.42 15.663 6.172 16 7 16v-2zm-.707 1.121l3.414-3.414-1.414-1.414-3.414 3.414 1.414 1.414zM9 12h4v-2H9v2zm4 0a3 3 0 0 0 3-3h-2a1 1 0 0 1-1 1v2zm3-3V3h-2v6h2zm0-6a3 3 0 0 0-3-3v2a1 1 0 0 1 1 1h2zm-3-3H3v2h10V0zM3 0a3 3 0 0 0-3 3h2a1 1 0 0 1 1-1V0zM0 3v6h2V3H0zm0 6a3 3 0 0 0 3 3v-2a1 1 0 0 1-1-1H0zm3 3h2v-2H3v2zm1-1v4h2v-4H4zm1.707 4.707l.586-.586-1.414-1.414-.586.586 1.414 1.414z"
5
+ />
6
+ </svg>
7
+ </template>
@@ -0,0 +1,7 @@
1
+ <template>
2
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="17" fill="currentColor">
3
+ <path
4
+ d="M11 2.253a1 1 0 1 0-2 0h2zm-2 13a1 1 0 1 0 2 0H9zm.447-12.167a1 1 0 1 0 1.107-1.666L9.447 3.086zM1 2.253L.447 1.42A1 1 0 0 0 0 2.253h1zm0 13H0a1 1 0 0 0 1.553.833L1 15.253zm8.447.833a1 1 0 1 0 1.107-1.666l-1.107 1.666zm0-14.666a1 1 0 1 0 1.107 1.666L9.447 1.42zM19 2.253h1a1 1 0 0 0-.447-.833L19 2.253zm0 13l-.553.833A1 1 0 0 0 20 15.253h-1zm-9.553-.833a1 1 0 1 0 1.107 1.666L9.447 14.42zM9 2.253v13h2v-13H9zm1.553-.833C9.203.523 7.42 0 5.5 0v2c1.572 0 2.961.431 3.947 1.086l1.107-1.666zM5.5 0C3.58 0 1.797.523.447 1.42l1.107 1.666C2.539 2.431 3.928 2 5.5 2V0zM0 2.253v13h2v-13H0zm1.553 13.833C2.539 15.431 3.928 15 5.5 15v-2c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM5.5 15c1.572 0 2.961.431 3.947 1.086l1.107-1.666C9.203 13.523 7.42 13 5.5 13v2zm5.053-11.914C11.539 2.431 12.928 2 14.5 2V0c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM14.5 2c1.573 0 2.961.431 3.947 1.086l1.107-1.666C18.203.523 16.421 0 14.5 0v2zm3.5.253v13h2v-13h-2zm1.553 12.167C18.203 13.523 16.421 13 14.5 13v2c1.573 0 2.961.431 3.947 1.086l1.107-1.666zM14.5 13c-1.92 0-3.703.523-5.053 1.42l1.107 1.666C11.539 15.431 12.928 15 14.5 15v-2z"
5
+ />
6
+ </svg>
7
+ </template>
@@ -0,0 +1,7 @@
1
+ <template>
2
+ <svg xmlns="http://www.w3.org/2000/svg" width="18" height="20" fill="currentColor">
3
+ <path
4
+ d="M11.447 8.894a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm0 1.789a1 1 0 1 0 .894-1.789l-.894 1.789zM7.447 7.106a1 1 0 1 0-.894 1.789l.894-1.789zM10 9a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0H8zm9.447-5.606a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm2 .789a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zM18 5a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0h-2zm-5.447-4.606a1 1 0 1 0 .894-1.789l-.894 1.789zM9 1l.447-.894a1 1 0 0 0-.894 0L9 1zm-2.447.106a1 1 0 1 0 .894 1.789l-.894-1.789zm-6 3a1 1 0 1 0 .894 1.789L.553 4.106zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zm-2-.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 2.789a1 1 0 1 0 .894-1.789l-.894 1.789zM2 5a1 1 0 1 0-2 0h2zM0 7.5a1 1 0 1 0 2 0H0zm8.553 12.394a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 1a1 1 0 1 0 .894 1.789l-.894-1.789zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zM8 19a1 1 0 1 0 2 0H8zm2-2.5a1 1 0 1 0-2 0h2zm-7.447.394a1 1 0 1 0 .894-1.789l-.894 1.789zM1 15H0a1 1 0 0 0 .553.894L1 15zm1-2.5a1 1 0 1 0-2 0h2zm12.553 2.606a1 1 0 1 0 .894 1.789l-.894-1.789zM17 15l.447.894A1 1 0 0 0 18 15h-1zm1-2.5a1 1 0 1 0-2 0h2zm-7.447-5.394l-2 1 .894 1.789 2-1-.894-1.789zm-1.106 1l-2-1-.894 1.789 2 1 .894-1.789zM8 9v2.5h2V9H8zm8.553-4.894l-2 1 .894 1.789 2-1-.894-1.789zm.894 0l-2-1-.894 1.789 2 1 .894-1.789zM16 5v2.5h2V5h-2zm-4.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zm-2.894-1l-2 1 .894 1.789 2-1L8.553.106zM1.447 5.894l2-1-.894-1.789-2 1 .894 1.789zm-.894 0l2 1 .894-1.789-2-1-.894 1.789zM0 5v2.5h2V5H0zm9.447 13.106l-2-1-.894 1.789 2 1 .894-1.789zm0 1.789l2-1-.894-1.789-2 1 .894 1.789zM10 19v-2.5H8V19h2zm-6.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zM2 15v-2.5H0V15h2zm13.447 1.894l2-1-.894-1.789-2 1 .894 1.789zM18 15v-2.5h-2V15h2z"
5
+ />
6
+ </svg>
7
+ </template>
@@ -0,0 +1,7 @@
1
+ <template>
2
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
3
+ <path
4
+ d="M10 3.22l-.61-.6a5.5 5.5 0 0 0-7.666.105 5.5 5.5 0 0 0-.114 7.665L10 18.78l8.39-8.4a5.5 5.5 0 0 0-.114-7.665 5.5 5.5 0 0 0-7.666-.105l-.61.61z"
5
+ />
6
+ </svg>
7
+ </template>
@@ -0,0 +1,19 @@
1
+ <!-- This icon is from <https://github.com/Templarian/MaterialDesign>, distributed under Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0) license-->
2
+ <template>
3
+ <svg
4
+ xmlns="http://www.w3.org/2000/svg"
5
+ xmlns:xlink="http://www.w3.org/1999/xlink"
6
+ aria-hidden="true"
7
+ role="img"
8
+ class="iconify iconify--mdi"
9
+ width="24"
10
+ height="24"
11
+ preserveAspectRatio="xMidYMid meet"
12
+ viewBox="0 0 24 24"
13
+ >
14
+ <path
15
+ d="M20 18v-4h-3v1h-2v-1H9v1H7v-1H4v4h16M6.33 8l-1.74 4H7v-1h2v1h6v-1h2v1h2.41l-1.74-4H6.33M9 5v1h6V5H9m12.84 7.61c.1.22.16.48.16.8V18c0 .53-.21 1-.6 1.41c-.4.4-.85.59-1.4.59H4c-.55 0-1-.19-1.4-.59C2.21 19 2 18.53 2 18v-4.59c0-.32.06-.58.16-.8L4.5 7.22C4.84 6.41 5.45 6 6.33 6H7V5c0-.55.18-1 .57-1.41C7.96 3.2 8.44 3 9 3h6c.56 0 1.04.2 1.43.59c.39.41.57.86.57 1.41v1h.67c.88 0 1.49.41 1.83 1.22l2.34 5.39z"
16
+ fill="currentColor"
17
+ ></path>
18
+ </svg>
19
+ </template>
package/src/index.ts ADDED
@@ -0,0 +1,25 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import ImageEditor from '@/components/ImageEditor/index.vue';
3
+ import { type TuiImageEditor } from '@/types/index';
4
+ import type { App } from 'vue'; // 新增:导入Vue组件类型
5
+ const YuiImageEditor: any = ImageEditor
6
+
7
+ const components = [YuiImageEditor];
8
+
9
+ const install = (app: App) => {
10
+ components.forEach(component => {
11
+ app.component(component.name || 'YuiImageEditor', component);
12
+ });
13
+ };
14
+
15
+
16
+
17
+ export {
18
+ YuiImageEditor as ImageEditor,
19
+ type TuiImageEditor
20
+ };
21
+
22
+ export default {
23
+ install,
24
+ YuiImageEditor
25
+ };
package/src/main.ts ADDED
@@ -0,0 +1,11 @@
1
+ import { createPinia } from 'pinia';
2
+ import 'tui-color-picker/dist/tui-color-picker.css';
3
+ import 'tui-image-editor/dist/tui-image-editor.css';
4
+ import { createApp } from 'vue';
5
+ import App from './App.vue';
6
+ import './assets/main.css';
7
+ import router from './router';
8
+ const app = createApp(App)
9
+ app.use(createPinia())
10
+ app.use(router)
11
+ app.mount('#app')