diff --git a/eslint.config.js b/eslint.config.js index e9452df..a492420 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -24,7 +24,7 @@ export default [ files: ['**/*.vue'], languageOptions: { parserOptions: { parser: tseslint.parser } }, rules: { - 'vue/multi-word-component-names': ['warn', { ignores: ['index'] }], + 'vue/multi-word-component-names': ['warn', { ignores: ['index', 'exception'] }], }, }, { diff --git a/public/point/11-dark.png b/public/point/11-dark.png index 9a57193..dcb2bcf 100644 Binary files a/public/point/11-dark.png and b/public/point/11-dark.png differ diff --git a/public/point/11-light.png b/public/point/11-light.png new file mode 100644 index 0000000..bbb6e55 Binary files /dev/null and b/public/point/11-light.png differ diff --git a/public/point/12-dark.png b/public/point/12-dark.png index dc98fea..3755f69 100644 Binary files a/public/point/12-dark.png and b/public/point/12-dark.png differ diff --git a/public/point/12-light.png b/public/point/12-light.png new file mode 100644 index 0000000..16cf5e2 Binary files /dev/null and b/public/point/12-light.png differ diff --git a/public/point/13-dark.png b/public/point/13-dark.png index adf7633..25fed60 100644 Binary files a/public/point/13-dark.png and b/public/point/13-dark.png differ diff --git a/public/point/13-light.png b/public/point/13-light.png new file mode 100644 index 0000000..5384a2e Binary files /dev/null and b/public/point/13-light.png differ diff --git a/public/point/14-dark.png b/public/point/14-dark.png index ecccb54..e966da9 100644 Binary files a/public/point/14-dark.png and b/public/point/14-dark.png differ diff --git a/public/point/14-light.png b/public/point/14-light.png new file mode 100644 index 0000000..d2d9739 Binary files /dev/null and b/public/point/14-light.png differ diff --git a/public/point/15-dark.png b/public/point/15-dark.png index b0fd7ac..b93bac5 100644 Binary files a/public/point/15-dark.png and b/public/point/15-dark.png differ diff --git a/public/point/15-light.png b/public/point/15-light.png new file mode 100644 index 0000000..f8ba60f Binary files /dev/null and b/public/point/15-light.png differ diff --git a/src/apis/map/constant.ts b/src/apis/map/constant.ts index c973444..e5d203c 100644 --- a/src/apis/map/constant.ts +++ b/src/apis/map/constant.ts @@ -44,8 +44,6 @@ export const EDITOR_CONFIG: Options = { keydown: KeydownType.None, strictScope: true, moveConnectedLine: false, - textRotate: false, - textFlip: false, disableInput: true, disableRotate: true, disableSize: true, @@ -53,12 +51,11 @@ export const EDITOR_CONFIG: Options = { disableEmptyLine: true, disableRepeatLine: true, minScale: 0.19, - maxScale: 2.01, + maxScale: 4.01, scaleOff: 0.01, defaultAnchors: [], activeGlobalAlpha: 0, fontSize: 14, lineHeight: 1.5, - textAlign: 'center', - textBaseline: 'top', + fontFamily: 'system-ui', }; diff --git a/src/assets/themes/antd-dark.json b/src/assets/themes/antd-dark.json new file mode 100644 index 0000000..64184af --- /dev/null +++ b/src/assets/themes/antd-dark.json @@ -0,0 +1,9 @@ +{ + "token": { + "colorInfo": "#0ea278", + "colorPrimary": "#0dbb8a", + "colorTextBase": "#fafafa", + "colorBgBase": "#2a2c2c", + "colorBorderSecondary": "#595e5e" + } +} \ No newline at end of file diff --git a/src/assets/themes/editor-dark.json b/src/assets/themes/editor-dark.json index 89ecdbf..1416981 100644 --- a/src/assets/themes/editor-dark.json +++ b/src/assets/themes/editor-dark.json @@ -6,12 +6,12 @@ "strokeActive": "#FCC947", "fill-1": "#14D1A5", "fill-2": "#69C6F5", - "fill-3": "#E48B1D" + "fill-3": "#E48B1D", + "fill-4": "#E48B1D" }, "point-l": { "stroke": "#595959", - "strokeActive": "#FCC947", - "fill": "#1F1F1F" + "strokeActive": "#FCC947" }, "line": "#8C8C8C" } \ No newline at end of file diff --git a/src/components/robot-list.vue b/src/components/robot-list.vue index 3db3b5f..56ac976 100644 --- a/src/components/robot-list.vue +++ b/src/components/robot-list.vue @@ -21,6 +21,7 @@ const mode = ref(Mode.常规); watch(editor.value.mouseClick, (v) => { if (mode.value !== Mode.添加点位) return; if (isEmpty(v)) return; + editor.value.addPoint(v, 1); editor.value.addPoint(v, 11); }); watch(editor.value.mouseBrush, (v) => { @@ -44,4 +45,4 @@ watch(editor.value.mouseBrush, (v) => { - + diff --git a/src/pages/exception.vue b/src/pages/exception.vue new file mode 100644 index 0000000..64d6904 --- /dev/null +++ b/src/pages/exception.vue @@ -0,0 +1,10 @@ + + + + + diff --git a/src/services/editor.service.ts b/src/services/editor.service.ts index 06efffe..d7bfe58 100644 --- a/src/services/editor.service.ts +++ b/src/services/editor.service.ts @@ -1,6 +1,6 @@ import { EDITOR_CONFIG, MapAreaType, type MapPen, MapPointType } from '@api/map'; 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 { cloneDeep, get, pick } from 'lodash-es'; 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 点位 public async addPoint(p: Point, type = MapPointType.普通点): Promise { const pen: MapPen = { ...p, ...this.#mapPoint(type), + ...this.#mapPointImage(type), name: 'point', - text: 'POINT', + tags: ['point', `point-${type}`], + label: 'POINT', point: { type }, }; 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)] }); } - #mapPoint(type: MapPointType): Required> { - const theme = this.data().theme; + #mapPoint(type: MapPointType): Required> { const width = type < 10 ? 24 : 48; const height = type < 10 ? 24 : 60; const lineWidth = type < 10 ? 2 : 3; + const iconSize = type < 10 ? 4 : 10; + return { width, height, lineWidth, iconSize }; + } + #mapPointImage(type: MapPointType): Required> { + const theme = this.data().theme; const image = type < 10 ? '' : `/point/${type}-${theme}.png`; - return { width, height, lineWidth, image }; + return { image, canvasLayer: CanvasLayer.CanvasMain }; } //#endregion @@ -81,6 +91,7 @@ export class EditorService extends Meta2d { if (w * scale < 50 || h * scale < 60) return; const pen: MapPen = { name: 'area', + tags: ['area', `area-${type}`], x: Math.min(p1.x, p2.x), y: Math.min(p1.y, p2.y), width: w, @@ -103,14 +114,31 @@ export class EditorService extends Meta2d { watch( () => sTheme.theme, - (v) => this.setTheme(v), + (v) => this.#load(v), { 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 #listen(e: unknown, v: any) { switch (e) { + case 'opened': + this.#load(); + break; + case 'click': case 'mousedown': case 'mouseup': @@ -132,8 +160,8 @@ export class EditorService extends Meta2d { //#region 绘制函数 function drawPoint(ctx: CanvasRenderingContext2D, pen: MapPen): void { const theme = sTheme.editor; - const active = pen.calculative?.active; - const { x = 0, y = 0, width = 0, height = 0 } = pen.calculative?.worldRect ?? {}; + const { active, iconSize: r = 0, fontSize = 14, lineHeight = 1.5, fontFamily } = pen.calculative ?? {}; + const { x = 0, y = 0, width: w = 0, height: h = 0 } = pen.calculative?.worldRect ?? {}; const { type } = pen.point ?? {}; const { label = '' } = pen ?? {}; @@ -143,28 +171,55 @@ function drawPoint(ctx: CanvasRenderingContext2D, pen: MapPen): void { 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; case MapPointType.电梯点: case MapPointType.自动门点: case MapPointType.充电点: case MapPointType.停靠点: case MapPointType.动作点: - ctx.rect(x, y, width, height); - // ctx.fillStyle = get(theme, 'point-l.fill') ?? ''; - // ctx.fill(); + ctx.roundRect(x, y, w, h, r); ctx.strokeStyle = get(theme, active ? 'point-l.strokeActive' : 'point-l.stroke') ?? ''; ctx.stroke(); break; - default: break; } ctx.fillStyle = get(theme, 'color') ?? ''; - ctx.font = '14px/20px system-ui'; + ctx.font = `${fontSize}px/${lineHeight} ${fontFamily}`; ctx.textAlign = 'center'; - ctx.textBaseline = 'middle'; - ctx.fillText(label, x + width / 2, y - 10); + ctx.textBaseline = 'top'; + ctx.fillText(label, x + w / 2, y - fontSize * lineHeight); ctx.restore(); } function anchorPoint(pen: MapPen): void { diff --git a/src/services/router.ts b/src/services/router.ts index b776985..151a226 100644 --- a/src/services/router.ts +++ b/src/services/router.ts @@ -1,7 +1,12 @@ import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router'; export const ROUTES = Object.freeze([ - { path: '/:pathMatch(.*)*', redirect: '/' }, + { path: '/:pathMatch(.*)*', redirect: '/exception/404' }, + { + path: '/exception/:code', + props: true, + component: () => import('@/exception.vue'), + }, { path: '/scene-editor/:id', props: true, diff --git a/src/services/theme.service.ts b/src/services/theme.service.ts index 6382936..557125b 100644 --- a/src/services/theme.service.ts +++ b/src/services/theme.service.ts @@ -29,7 +29,7 @@ class ThemeService { public get ant(): AntdTheme { switch (this.#theme.value) { case Theme.Dark: - return { algorithm: theme.darkAlgorithm }; + return THEME_MAP[`antd-${this.#theme.value}`] ?? {}; case Theme.Light: default: return { algorithm: theme.defaultAlgorithm };