feat: draw point
@ -24,7 +24,7 @@ export default [
|
|||||||
files: ['**/*.vue'],
|
files: ['**/*.vue'],
|
||||||
languageOptions: { parserOptions: { parser: tseslint.parser } },
|
languageOptions: { parserOptions: { parser: tseslint.parser } },
|
||||||
rules: {
|
rules: {
|
||||||
'vue/multi-word-component-names': ['warn', { ignores: ['index'] }],
|
'vue/multi-word-component-names': ['warn', { ignores: ['index', 'exception'] }],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.8 KiB |
BIN
public/point/11-light.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.8 KiB |
BIN
public/point/12-light.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 2.0 KiB |
BIN
public/point/13-light.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 2.1 KiB |
BIN
public/point/14-light.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.5 KiB |
BIN
public/point/15-light.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
@ -44,8 +44,6 @@ export const EDITOR_CONFIG: Options = {
|
|||||||
keydown: KeydownType.None,
|
keydown: KeydownType.None,
|
||||||
strictScope: true,
|
strictScope: true,
|
||||||
moveConnectedLine: false,
|
moveConnectedLine: false,
|
||||||
textRotate: false,
|
|
||||||
textFlip: false,
|
|
||||||
disableInput: true,
|
disableInput: true,
|
||||||
disableRotate: true,
|
disableRotate: true,
|
||||||
disableSize: true,
|
disableSize: true,
|
||||||
@ -53,12 +51,11 @@ export const EDITOR_CONFIG: Options = {
|
|||||||
disableEmptyLine: true,
|
disableEmptyLine: true,
|
||||||
disableRepeatLine: true,
|
disableRepeatLine: true,
|
||||||
minScale: 0.19,
|
minScale: 0.19,
|
||||||
maxScale: 2.01,
|
maxScale: 4.01,
|
||||||
scaleOff: 0.01,
|
scaleOff: 0.01,
|
||||||
defaultAnchors: [],
|
defaultAnchors: [],
|
||||||
activeGlobalAlpha: 0,
|
activeGlobalAlpha: 0,
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
lineHeight: 1.5,
|
lineHeight: 1.5,
|
||||||
textAlign: 'center',
|
fontFamily: 'system-ui',
|
||||||
textBaseline: 'top',
|
|
||||||
};
|
};
|
||||||
|
9
src/assets/themes/antd-dark.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"token": {
|
||||||
|
"colorInfo": "#0ea278",
|
||||||
|
"colorPrimary": "#0dbb8a",
|
||||||
|
"colorTextBase": "#fafafa",
|
||||||
|
"colorBgBase": "#2a2c2c",
|
||||||
|
"colorBorderSecondary": "#595e5e"
|
||||||
|
}
|
||||||
|
}
|
@ -6,12 +6,12 @@
|
|||||||
"strokeActive": "#FCC947",
|
"strokeActive": "#FCC947",
|
||||||
"fill-1": "#14D1A5",
|
"fill-1": "#14D1A5",
|
||||||
"fill-2": "#69C6F5",
|
"fill-2": "#69C6F5",
|
||||||
"fill-3": "#E48B1D"
|
"fill-3": "#E48B1D",
|
||||||
|
"fill-4": "#E48B1D"
|
||||||
},
|
},
|
||||||
"point-l": {
|
"point-l": {
|
||||||
"stroke": "#595959",
|
"stroke": "#595959",
|
||||||
"strokeActive": "#FCC947",
|
"strokeActive": "#FCC947"
|
||||||
"fill": "#1F1F1F"
|
|
||||||
},
|
},
|
||||||
"line": "#8C8C8C"
|
"line": "#8C8C8C"
|
||||||
}
|
}
|
@ -21,6 +21,7 @@ const mode = ref<Mode>(Mode.常规);
|
|||||||
watch(editor.value.mouseClick, (v) => {
|
watch(editor.value.mouseClick, (v) => {
|
||||||
if (mode.value !== Mode.添加点位) return;
|
if (mode.value !== Mode.添加点位) return;
|
||||||
if (isEmpty(v)) return;
|
if (isEmpty(v)) return;
|
||||||
|
editor.value.addPoint(v, 1);
|
||||||
editor.value.addPoint(v, 11);
|
editor.value.addPoint(v, 11);
|
||||||
});
|
});
|
||||||
watch(editor.value.mouseBrush, (v) => {
|
watch(editor.value.mouseBrush, (v) => {
|
||||||
@ -44,4 +45,4 @@ watch(editor.value.mouseBrush, (v) => {
|
|||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
<!-- <style scoped lang="scss"></style> -->
|
||||||
|
10
src/pages/exception.vue
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
type Props = {
|
||||||
|
code: string;
|
||||||
|
};
|
||||||
|
defineProps<Props>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>{{ code }}</template>
|
||||||
|
|
||||||
|
<!-- <style scoped lang="scss"></style> -->
|
@ -1,6 +1,6 @@
|
|||||||
import { EDITOR_CONFIG, MapAreaType, type MapPen, MapPointType } from '@api/map';
|
import { EDITOR_CONFIG, MapAreaType, type MapPen, MapPointType } from '@api/map';
|
||||||
import sTheme from '@core/theme.service';
|
import sTheme from '@core/theme.service';
|
||||||
import { EditType, LockState, Meta2d } from '@meta2d/core';
|
import { CanvasLayer, EditType, LockState, Meta2d } from '@meta2d/core';
|
||||||
import { useObservable } from '@vueuse/rxjs';
|
import { useObservable } from '@vueuse/rxjs';
|
||||||
import { cloneDeep, get, pick } from 'lodash-es';
|
import { cloneDeep, get, pick } from 'lodash-es';
|
||||||
import { debounceTime, filter, map, Subject, switchMap } from 'rxjs';
|
import { debounceTime, filter, map, Subject, switchMap } from 'rxjs';
|
||||||
@ -44,13 +44,19 @@ export class EditorService extends Meta2d {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
public override find(target: string): MapPen[] {
|
||||||
|
return super.find(target);
|
||||||
|
}
|
||||||
|
|
||||||
//#region 点位
|
//#region 点位
|
||||||
public async addPoint(p: Point, type = MapPointType.普通点): Promise<void> {
|
public async addPoint(p: Point, type = MapPointType.普通点): Promise<void> {
|
||||||
const pen: MapPen = {
|
const pen: MapPen = {
|
||||||
...p,
|
...p,
|
||||||
...this.#mapPoint(type),
|
...this.#mapPoint(type),
|
||||||
|
...this.#mapPointImage(type),
|
||||||
name: 'point',
|
name: 'point',
|
||||||
text: 'POINT',
|
tags: ['point', `point-${type}`],
|
||||||
|
label: 'POINT',
|
||||||
point: { type },
|
point: { type },
|
||||||
};
|
};
|
||||||
const { x, y, width, height } = this.getPenRect(pen);
|
const { x, y, width, height } = this.getPenRect(pen);
|
||||||
@ -60,13 +66,17 @@ export class EditorService extends Meta2d {
|
|||||||
this.pushHistory({ type: EditType.Add, pens: [cloneDeep(pen)] });
|
this.pushHistory({ type: EditType.Add, pens: [cloneDeep(pen)] });
|
||||||
}
|
}
|
||||||
|
|
||||||
#mapPoint(type: MapPointType): Required<Pick<MapPen, 'width' | 'height' | 'lineWidth' | 'image'>> {
|
#mapPoint(type: MapPointType): Required<Pick<MapPen, 'width' | 'height' | 'lineWidth' | 'iconSize'>> {
|
||||||
const theme = this.data().theme;
|
|
||||||
const width = type < 10 ? 24 : 48;
|
const width = type < 10 ? 24 : 48;
|
||||||
const height = type < 10 ? 24 : 60;
|
const height = type < 10 ? 24 : 60;
|
||||||
const lineWidth = type < 10 ? 2 : 3;
|
const lineWidth = type < 10 ? 2 : 3;
|
||||||
|
const iconSize = type < 10 ? 4 : 10;
|
||||||
|
return { width, height, lineWidth, iconSize };
|
||||||
|
}
|
||||||
|
#mapPointImage(type: MapPointType): Required<Pick<MapPen, 'image' | 'canvasLayer'>> {
|
||||||
|
const theme = this.data().theme;
|
||||||
const image = type < 10 ? '' : `/point/${type}-${theme}.png`;
|
const image = type < 10 ? '' : `/point/${type}-${theme}.png`;
|
||||||
return { width, height, lineWidth, image };
|
return { image, canvasLayer: CanvasLayer.CanvasMain };
|
||||||
}
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
@ -81,6 +91,7 @@ export class EditorService extends Meta2d {
|
|||||||
if (w * scale < 50 || h * scale < 60) return;
|
if (w * scale < 50 || h * scale < 60) return;
|
||||||
const pen: MapPen = {
|
const pen: MapPen = {
|
||||||
name: 'area',
|
name: 'area',
|
||||||
|
tags: ['area', `area-${type}`],
|
||||||
x: Math.min(p1.x, p2.x),
|
x: Math.min(p1.x, p2.x),
|
||||||
y: Math.min(p1.y, p2.y),
|
y: Math.min(p1.y, p2.y),
|
||||||
width: w,
|
width: w,
|
||||||
@ -103,14 +114,31 @@ export class EditorService extends Meta2d {
|
|||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => sTheme.theme,
|
() => sTheme.theme,
|
||||||
(v) => this.setTheme(v),
|
(v) => this.#load(v),
|
||||||
{ immediate: true },
|
{ immediate: true },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#load(theme?: string): void {
|
||||||
|
if (theme) {
|
||||||
|
this.setTheme(theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.find('point').forEach((pen) => {
|
||||||
|
if (!pen.point?.type) return;
|
||||||
|
if (pen.point.type < 10) return;
|
||||||
|
this.canvas.updateValue(pen, this.#mapPointImage(pen.point.type));
|
||||||
|
});
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
#listen(e: unknown, v: any) {
|
#listen(e: unknown, v: any) {
|
||||||
switch (e) {
|
switch (e) {
|
||||||
|
case 'opened':
|
||||||
|
this.#load();
|
||||||
|
break;
|
||||||
|
|
||||||
case 'click':
|
case 'click':
|
||||||
case 'mousedown':
|
case 'mousedown':
|
||||||
case 'mouseup':
|
case 'mouseup':
|
||||||
@ -132,8 +160,8 @@ export class EditorService extends Meta2d {
|
|||||||
//#region 绘制函数
|
//#region 绘制函数
|
||||||
function drawPoint(ctx: CanvasRenderingContext2D, pen: MapPen): void {
|
function drawPoint(ctx: CanvasRenderingContext2D, pen: MapPen): void {
|
||||||
const theme = sTheme.editor;
|
const theme = sTheme.editor;
|
||||||
const active = pen.calculative?.active;
|
const { active, iconSize: r = 0, fontSize = 14, lineHeight = 1.5, fontFamily } = pen.calculative ?? {};
|
||||||
const { x = 0, y = 0, width = 0, height = 0 } = pen.calculative?.worldRect ?? {};
|
const { x = 0, y = 0, width: w = 0, height: h = 0 } = pen.calculative?.worldRect ?? {};
|
||||||
const { type } = pen.point ?? {};
|
const { type } = pen.point ?? {};
|
||||||
const { label = '' } = pen ?? {};
|
const { label = '' } = pen ?? {};
|
||||||
|
|
||||||
@ -143,28 +171,55 @@ function drawPoint(ctx: CanvasRenderingContext2D, pen: MapPen): void {
|
|||||||
case MapPointType.等待点:
|
case MapPointType.等待点:
|
||||||
case MapPointType.避让点:
|
case MapPointType.避让点:
|
||||||
case MapPointType.临时避让点:
|
case MapPointType.临时避让点:
|
||||||
ctx.lineWidth = 2;
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x + w / 2 - r, y + r);
|
||||||
|
ctx.arcTo(x + w / 2, y, x + w - r, y + h / 2 - r, r);
|
||||||
|
ctx.arcTo(x + w, y + h / 2, x + w / 2 + r, y + h - r, r);
|
||||||
|
ctx.arcTo(x + w / 2, y + h, x + r, y + h / 2 + r, r);
|
||||||
|
ctx.arcTo(x, y + h / 2, x + r, y + h / 2 - r, r);
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.fillStyle = get(theme, `point-s.fill-${type}`) ?? '';
|
||||||
|
ctx.fill();
|
||||||
|
ctx.strokeStyle = get(theme, active ? 'point-s.strokeActive' : 'point-s.stroke') ?? '';
|
||||||
|
if (type === MapPointType.临时避让点) {
|
||||||
|
ctx.lineCap = 'round';
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x + 0.66 * r, y + h / 2 - 0.66 * r);
|
||||||
|
ctx.lineTo(x + r, y + h / 2 - r);
|
||||||
|
ctx.moveTo(x + w / 2 - 0.66 * r, y + 0.66 * r);
|
||||||
|
ctx.lineTo(x + w / 2 - r, y + r);
|
||||||
|
ctx.moveTo(x + w / 2 + 0.66 * r, y + 0.66 * r);
|
||||||
|
ctx.lineTo(x + w / 2 + r, y + r);
|
||||||
|
ctx.moveTo(x + w - 0.66 * r, y + h / 2 - 0.66 * r);
|
||||||
|
ctx.lineTo(x + w - r, y + h / 2 - r);
|
||||||
|
ctx.moveTo(x + w - 0.66 * r, y + h / 2 + 0.66 * r);
|
||||||
|
ctx.lineTo(x + w - r, y + h / 2 + r);
|
||||||
|
ctx.moveTo(x + w / 2 + 0.66 * r, y + h - 0.66 * r);
|
||||||
|
ctx.lineTo(x + w / 2 + r, y + h - r);
|
||||||
|
ctx.moveTo(x + w / 2 - 0.66 * r, y + h - 0.66 * r);
|
||||||
|
ctx.lineTo(x + w / 2 - r, y + h - r);
|
||||||
|
ctx.moveTo(x + 0.66 * r, y + h / 2 + 0.66 * r);
|
||||||
|
ctx.lineTo(x + r, y + h / 2 + r);
|
||||||
|
}
|
||||||
|
ctx.stroke();
|
||||||
break;
|
break;
|
||||||
case MapPointType.电梯点:
|
case MapPointType.电梯点:
|
||||||
case MapPointType.自动门点:
|
case MapPointType.自动门点:
|
||||||
case MapPointType.充电点:
|
case MapPointType.充电点:
|
||||||
case MapPointType.停靠点:
|
case MapPointType.停靠点:
|
||||||
case MapPointType.动作点:
|
case MapPointType.动作点:
|
||||||
ctx.rect(x, y, width, height);
|
ctx.roundRect(x, y, w, h, r);
|
||||||
// ctx.fillStyle = get(theme, 'point-l.fill') ?? '';
|
|
||||||
// ctx.fill();
|
|
||||||
ctx.strokeStyle = get(theme, active ? 'point-l.strokeActive' : 'point-l.stroke') ?? '';
|
ctx.strokeStyle = get(theme, active ? 'point-l.strokeActive' : 'point-l.stroke') ?? '';
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ctx.fillStyle = get(theme, 'color') ?? '';
|
ctx.fillStyle = get(theme, 'color') ?? '';
|
||||||
ctx.font = '14px/20px system-ui';
|
ctx.font = `${fontSize}px/${lineHeight} ${fontFamily}`;
|
||||||
ctx.textAlign = 'center';
|
ctx.textAlign = 'center';
|
||||||
ctx.textBaseline = 'middle';
|
ctx.textBaseline = 'top';
|
||||||
ctx.fillText(label, x + width / 2, y - 10);
|
ctx.fillText(label, x + w / 2, y - fontSize * lineHeight);
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
}
|
}
|
||||||
function anchorPoint(pen: MapPen): void {
|
function anchorPoint(pen: MapPen): void {
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router';
|
import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router';
|
||||||
|
|
||||||
export const ROUTES = Object.freeze<RouteRecordRaw[]>([
|
export const ROUTES = Object.freeze<RouteRecordRaw[]>([
|
||||||
{ path: '/:pathMatch(.*)*', redirect: '/' },
|
{ path: '/:pathMatch(.*)*', redirect: '/exception/404' },
|
||||||
|
{
|
||||||
|
path: '/exception/:code',
|
||||||
|
props: true,
|
||||||
|
component: () => import('@/exception.vue'),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/scene-editor/:id',
|
path: '/scene-editor/:id',
|
||||||
props: true,
|
props: true,
|
||||||
|
@ -29,7 +29,7 @@ class ThemeService {
|
|||||||
public get ant(): AntdTheme {
|
public get ant(): AntdTheme {
|
||||||
switch (this.#theme.value) {
|
switch (this.#theme.value) {
|
||||||
case Theme.Dark:
|
case Theme.Dark:
|
||||||
return { algorithm: theme.darkAlgorithm };
|
return THEME_MAP[`antd-${this.#theme.value}`] ?? {};
|
||||||
case Theme.Light:
|
case Theme.Light:
|
||||||
default:
|
default:
|
||||||
return { algorithm: theme.defaultAlgorithm };
|
return { algorithm: theme.defaultAlgorithm };
|
||||||
|