Merge remote-tracking branch 'origin/master' into bj-rtss
This commit is contained in:
commit
83eab6c5b3
@ -14,8 +14,11 @@
|
|||||||
"build": "quasar build"
|
"build": "quasar build"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@pixi/graphics-extras": "^7.2.4",
|
||||||
"@quasar/extras": "^1.0.0",
|
"@quasar/extras": "^1.0.0",
|
||||||
"@stomp/stompjs": "^7.0.0",
|
"@stomp/stompjs": "^7.0.0",
|
||||||
|
"alova": "^2.7.1",
|
||||||
|
"axios": "^1.4.0",
|
||||||
"google-protobuf": "^3.21.2",
|
"google-protobuf": "^3.21.2",
|
||||||
"js-base64": "^3.7.5",
|
"js-base64": "^3.7.5",
|
||||||
"pinia": "^2.0.11",
|
"pinia": "^2.0.11",
|
||||||
@ -37,6 +40,7 @@
|
|||||||
"eslint-plugin-vue": "^9.0.0",
|
"eslint-plugin-vue": "^9.0.0",
|
||||||
"prettier": "^2.5.1",
|
"prettier": "^2.5.1",
|
||||||
"protoc-gen-ts": "^0.8.6",
|
"protoc-gen-ts": "^0.8.6",
|
||||||
|
"ts-md5": "^1.3.1",
|
||||||
"typescript": "^4.5.4"
|
"typescript": "^4.5.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -28,7 +28,7 @@ module.exports = configure(function (/* ctx */) {
|
|||||||
// app boot file (/src/boot)
|
// app boot file (/src/boot)
|
||||||
// --> boot files are part of "main.js"
|
// --> boot files are part of "main.js"
|
||||||
// https://v2.quasar.dev/quasar-cli-vite/boot-files
|
// https://v2.quasar.dev/quasar-cli-vite/boot-files
|
||||||
boot: [],
|
boot: ['axios', '@pixi/graphics-extras'],
|
||||||
|
|
||||||
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#css
|
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#css
|
||||||
css: ['app.scss'],
|
css: ['app.scss'],
|
||||||
|
7
src/boot/@pixi/graphics-extras.ts
Normal file
7
src/boot/@pixi/graphics-extras.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { boot } from 'quasar/wrappers';
|
||||||
|
import * as GraphicsExtras from '@pixi/graphics-extras';
|
||||||
|
// "async" is optional;
|
||||||
|
// more info on params: https://v2.quasar.dev/quasar-cli/boot-files
|
||||||
|
export default boot(async (/* { app, router, ... } */) => {
|
||||||
|
GraphicsExtras;
|
||||||
|
});
|
111
src/boot/axios.ts
Normal file
111
src/boot/axios.ts
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
import axios, { AxiosInstance } from 'axios';
|
||||||
|
import { AxiosError } from 'axios';
|
||||||
|
import { Dialog } from 'quasar';
|
||||||
|
import { boot } from 'quasar/wrappers';
|
||||||
|
import { getJwtToken } from 'src/examples/app/configs/TokenManage';
|
||||||
|
import { getHttpBase } from 'src/examples/app/configs/UrlManage';
|
||||||
|
|
||||||
|
declare module '@vue/runtime-core' {
|
||||||
|
interface ComponentCustomProperties {
|
||||||
|
$axios: AxiosInstance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ErrorData {
|
||||||
|
status: number;
|
||||||
|
title: string;
|
||||||
|
detail: string;
|
||||||
|
code: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ApiError {
|
||||||
|
origin: AxiosError;
|
||||||
|
/**
|
||||||
|
* 业务错误代码
|
||||||
|
*/
|
||||||
|
code: number;
|
||||||
|
/**
|
||||||
|
* 错误信息
|
||||||
|
*/
|
||||||
|
title: string;
|
||||||
|
/**
|
||||||
|
* 相关问题描述
|
||||||
|
*/
|
||||||
|
detail?: string;
|
||||||
|
constructor(origin: AxiosError<unknown, unknown>) {
|
||||||
|
this.origin = origin;
|
||||||
|
const response = origin.response;
|
||||||
|
if (response) {
|
||||||
|
const err = response.data as ErrorData;
|
||||||
|
this.code = err.code;
|
||||||
|
this.title = err.title;
|
||||||
|
this.detail = err.detail;
|
||||||
|
} else {
|
||||||
|
this.code = origin.status || -1;
|
||||||
|
this.title = origin.message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static from(err: AxiosError<unknown, unknown>): ApiError {
|
||||||
|
return new ApiError(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否认证失败(登录过期)
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
isAuthError(): boolean {
|
||||||
|
return this.origin.response?.status === 401;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Be careful when using SSR for cross-request state pollution
|
||||||
|
// due to creating a Singleton instance here;
|
||||||
|
// If any client changes this (global) instance, it might be a
|
||||||
|
// good idea to move this instance creation inside of the
|
||||||
|
// "export default () => {}" function below (which runs individually
|
||||||
|
// for each client)
|
||||||
|
const api = axios.create({ baseURL: getHttpBase() });
|
||||||
|
|
||||||
|
export default boot(({ app, router }) => {
|
||||||
|
// for use inside Vue files (Options API) through this.$axios and this.$api
|
||||||
|
|
||||||
|
// 拦截请求,添加
|
||||||
|
api.interceptors.request.use(
|
||||||
|
(config) => {
|
||||||
|
config.headers.Authorization = getJwtToken();
|
||||||
|
return config;
|
||||||
|
},
|
||||||
|
(err: AxiosError) => {
|
||||||
|
return Promise.reject(ApiError.from(err));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
api.interceptors.response.use(
|
||||||
|
(response) => {
|
||||||
|
return response;
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
if (err.response && err.response.status === 401) {
|
||||||
|
Dialog.create({
|
||||||
|
title: '认证失败',
|
||||||
|
message: '认证失败或登录超时,请重新登录',
|
||||||
|
persistent: true,
|
||||||
|
}).onOk(() => {
|
||||||
|
router.push({ name: 'login' });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return Promise.reject(ApiError.from(err));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
app.config.globalProperties.$axios = axios;
|
||||||
|
// ^ ^ ^ this will allow you to use this.$axios (for Vue Options API form)
|
||||||
|
// so you won't necessarily have to import axios in each vue file
|
||||||
|
|
||||||
|
app.config.globalProperties.$api = api;
|
||||||
|
// ^ ^ ^ this will allow you to use this.$api (for Vue Options API form)
|
||||||
|
// so you can easily perform requests against your app's API
|
||||||
|
});
|
||||||
|
|
||||||
|
export { api };
|
@ -10,9 +10,18 @@
|
|||||||
<template v-if="drawStore.drawGraphicType === Link.Type">
|
<template v-if="drawStore.drawGraphicType === Link.Type">
|
||||||
<link-template></link-template>
|
<link-template></link-template>
|
||||||
</template>
|
</template>
|
||||||
|
<template v-if="drawStore.drawGraphicType === Rect.Type">
|
||||||
|
<rect-template></rect-template>
|
||||||
|
</template>
|
||||||
<template v-if="drawStore.drawGraphicType === Platform.Type">
|
<template v-if="drawStore.drawGraphicType === Platform.Type">
|
||||||
<platform-template></platform-template>
|
<platform-template></platform-template>
|
||||||
</template>
|
</template>
|
||||||
|
<template v-if="drawStore.drawGraphicType === Station.Type">
|
||||||
|
<station-template></station-template>
|
||||||
|
</template>
|
||||||
|
<template v-if="drawStore.drawGraphicType === Train.Type">
|
||||||
|
<TrainProperty></TrainProperty>
|
||||||
|
</template>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
</q-card>
|
</q-card>
|
||||||
</div>
|
</div>
|
||||||
@ -33,12 +42,24 @@
|
|||||||
<link-property
|
<link-property
|
||||||
v-if="drawStore.selectedGraphicType === Link.Type"
|
v-if="drawStore.selectedGraphicType === Link.Type"
|
||||||
></link-property>
|
></link-property>
|
||||||
|
<rect-property
|
||||||
|
v-if="drawStore.selectedGraphicType === Rect.Type"
|
||||||
|
></rect-property>
|
||||||
<platform-property
|
<platform-property
|
||||||
v-if="drawStore.selectedGraphicType === Platform.Type"
|
v-if="drawStore.selectedGraphicType === Platform.Type"
|
||||||
></platform-property>
|
></platform-property>
|
||||||
|
<station-property
|
||||||
|
v-if="drawStore.selectedGraphicType === Station.Type"
|
||||||
|
></station-property>
|
||||||
|
<train-property
|
||||||
|
v-if="drawStore.selectedGraphicType === Train.Type"
|
||||||
|
></train-property>
|
||||||
<iscs-fan-property
|
<iscs-fan-property
|
||||||
v-else-if="drawStore.selectedGraphicType === IscsFan.Type"
|
v-else-if="drawStore.selectedGraphicType === IscsFan.Type"
|
||||||
></iscs-fan-property>
|
></iscs-fan-property>
|
||||||
|
<signal-property
|
||||||
|
v-else-if="drawStore.selectedGraphicType === Signal.Type"
|
||||||
|
></signal-property>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
</template>
|
</template>
|
||||||
</q-card>
|
</q-card>
|
||||||
@ -47,15 +68,25 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import LinkTemplate from './templates/LinkTemplate.vue';
|
import LinkTemplate from './templates/LinkTemplate.vue';
|
||||||
|
import RectTemplate from './templates/RectTemplate.vue';
|
||||||
import PlatformTemplate from './templates/PlatformTemplate.vue';
|
import PlatformTemplate from './templates/PlatformTemplate.vue';
|
||||||
|
import StationTemplate from './templates/StationTemplate.vue';
|
||||||
import CanvasProperty from './properties/CanvasProperty.vue';
|
import CanvasProperty from './properties/CanvasProperty.vue';
|
||||||
import LinkProperty from './properties/LinkProperty.vue';
|
import LinkProperty from './properties/LinkProperty.vue';
|
||||||
|
import RectProperty from './properties/RectProperty.vue';
|
||||||
import PlatformProperty from './properties/PlatformProperty.vue';
|
import PlatformProperty from './properties/PlatformProperty.vue';
|
||||||
|
import StationProperty from './properties/StationProperty.vue';
|
||||||
|
import TrainProperty from './properties/TrainProperty.vue';
|
||||||
import IscsFanProperty from './properties/IscsFanProperty.vue';
|
import IscsFanProperty from './properties/IscsFanProperty.vue';
|
||||||
|
import SignalProperty from './properties/SignalProperty.vue';
|
||||||
import { Link } from 'src/graphics/link/Link';
|
import { Link } from 'src/graphics/link/Link';
|
||||||
|
import { Rect } from 'src/graphics/rect/Rect';
|
||||||
import { Platform } from 'src/graphics/platform/Platform';
|
import { Platform } from 'src/graphics/platform/Platform';
|
||||||
|
import { Station } from 'src/graphics/station/Station';
|
||||||
|
import { Train } from 'src/graphics/train/Train';
|
||||||
import { useDrawStore } from 'src/stores/draw-store';
|
import { useDrawStore } from 'src/stores/draw-store';
|
||||||
import { IscsFan } from 'src/graphics/iscs-fan/IscsFan';
|
import { IscsFan } from 'src/graphics/iscs-fan/IscsFan';
|
||||||
|
import { Signal } from 'src/graphics/signal/Signal';
|
||||||
|
|
||||||
const drawStore = useDrawStore();
|
const drawStore = useDrawStore();
|
||||||
</script>
|
</script>
|
||||||
|
@ -58,19 +58,13 @@ const canvas = reactive({
|
|||||||
});
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
console.log('画布属性表单mounted');
|
|
||||||
const jc = drawStore.getJlCanvas();
|
const jc = drawStore.getJlCanvas();
|
||||||
canvas.width = jc.properties.width;
|
canvas.width = jc.properties.width;
|
||||||
canvas.height = jc.properties.height;
|
canvas.height = jc.properties.height;
|
||||||
canvas.backgroundColor = jc.properties.backgroundColor;
|
canvas.backgroundColor = jc.properties.backgroundColor;
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
console.log('画布属性表单unmounted');
|
|
||||||
});
|
|
||||||
|
|
||||||
function onUpdate() {
|
function onUpdate() {
|
||||||
console.log('画布属性更新');
|
|
||||||
const app = drawStore.getDrawApp();
|
const app = drawStore.getDrawApp();
|
||||||
app.updateCanvasAndRecord({
|
app.updateCanvasAndRecord({
|
||||||
...canvas,
|
...canvas,
|
||||||
|
@ -1,19 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<q-form>
|
<q-form>
|
||||||
<q-input outlined readonly v-model="stationModel.id" label="id" hint="" />
|
<q-input outlined readonly v-model="platformModel.id" label="id" hint="" />
|
||||||
<q-input
|
<q-input
|
||||||
outlined
|
outlined
|
||||||
v-model.number="stationModel.lineWidth"
|
v-model.number="platformModel.lineWidth"
|
||||||
type="number"
|
type="number"
|
||||||
@blur="onUpdate"
|
@blur="onUpdate"
|
||||||
label="线宽"
|
label="线宽"
|
||||||
lazy-rules
|
lazy-rules
|
||||||
:rules="[(val) => (val && val > 0) || '画布宽必须大于0']"
|
:rules="[(val) => (val && val > 0) || '画布宽必须大于0']"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<q-input
|
<q-input
|
||||||
outlined
|
outlined
|
||||||
v-model="stationModel.lineColor"
|
v-model="platformModel.lineColor"
|
||||||
@blur="onUpdate"
|
@blur="onUpdate"
|
||||||
label="线色"
|
label="线色"
|
||||||
lazy-rules
|
lazy-rules
|
||||||
@ -23,10 +22,10 @@
|
|||||||
<q-icon name="colorize" class="cursor-pointer">
|
<q-icon name="colorize" class="cursor-pointer">
|
||||||
<q-popup-proxy cover transition-show="scale" transition-hide="scale">
|
<q-popup-proxy cover transition-show="scale" transition-hide="scale">
|
||||||
<q-color
|
<q-color
|
||||||
v-model="stationModel.lineColor"
|
v-model="platformModel.lineColor"
|
||||||
@change="
|
@change="
|
||||||
(val) => {
|
(val) => {
|
||||||
stationModel.lineColor = val;
|
platformModel.lineColor = val;
|
||||||
onUpdate();
|
onUpdate();
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
@ -35,13 +34,20 @@
|
|||||||
</q-icon>
|
</q-icon>
|
||||||
</template>
|
</template>
|
||||||
</q-input>
|
</q-input>
|
||||||
|
|
||||||
<q-select
|
<q-select
|
||||||
outlined
|
outlined
|
||||||
v-model="stationModel.hasdoor"
|
@blur="onUpdate"
|
||||||
:options="options"
|
v-model="hasDoor"
|
||||||
|
:options="optionsDoor"
|
||||||
label="是否有屏蔽门"
|
label="是否有屏蔽门"
|
||||||
/>
|
/>
|
||||||
|
<q-select
|
||||||
|
outlined
|
||||||
|
@blur="onUpdate"
|
||||||
|
v-model="trainDirection"
|
||||||
|
:options="optionsDirection"
|
||||||
|
label="行驶方向"
|
||||||
|
/>
|
||||||
</q-form>
|
</q-form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -49,37 +55,58 @@
|
|||||||
import { PlatformData } from 'src/examples/app/graphics/PlatformInteraction';
|
import { PlatformData } from 'src/examples/app/graphics/PlatformInteraction';
|
||||||
import { Platform } from 'src/graphics/platform/Platform';
|
import { Platform } from 'src/graphics/platform/Platform';
|
||||||
import { useDrawStore } from 'src/stores/draw-store';
|
import { useDrawStore } from 'src/stores/draw-store';
|
||||||
import { onMounted, reactive, watch } from 'vue';
|
import { onMounted, reactive, ref, watch } from 'vue';
|
||||||
|
|
||||||
const drawStore = useDrawStore();
|
const drawStore = useDrawStore();
|
||||||
const stationModel = reactive(new PlatformData());
|
const platformModel = reactive(new PlatformData());
|
||||||
const options = [true, false];
|
const hasDoor = ref('是');
|
||||||
|
const optionsDoor = ['是', '否'];
|
||||||
|
const trainDirection = ref('向左');
|
||||||
|
const optionsDirection = ['向左', '向右'];
|
||||||
|
enum showSelect {
|
||||||
|
是 = 'true',
|
||||||
|
否 = 'false',
|
||||||
|
向左 = 'left',
|
||||||
|
向右 = 'right',
|
||||||
|
}
|
||||||
|
enum showSelectData {
|
||||||
|
true = '是',
|
||||||
|
false = '否',
|
||||||
|
left = '向左',
|
||||||
|
right = '向右',
|
||||||
|
}
|
||||||
|
|
||||||
drawStore.$subscribe;
|
drawStore.$subscribe;
|
||||||
watch(
|
watch(
|
||||||
() => drawStore.selectedGraphic,
|
() => drawStore.selectedGraphic,
|
||||||
(val) => {
|
(val) => {
|
||||||
if (val && val.type == Platform.Type) {
|
if (val && val.type == Platform.Type) {
|
||||||
// console.log('station变更');
|
platformModel.copyFrom(val.saveData() as PlatformData);
|
||||||
stationModel.copyFrom(val.saveData() as PlatformData);
|
hasDoor.value = (showSelectData as never)[platformModel.hasdoor + ''];
|
||||||
|
trainDirection.value = (showSelectData as never)[
|
||||||
|
platformModel.trainDirection
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
//console.log('station 属性表单 mounted');
|
const platform = drawStore.selectedGraphic as Platform;
|
||||||
const station = drawStore.selectedGraphic as Platform;
|
if (platform) {
|
||||||
|
platformModel.copyFrom(platform.saveData());
|
||||||
if (station) {
|
hasDoor.value = (showSelectData as never)[platformModel.hasdoor + ''];
|
||||||
stationModel.copyFrom(station.saveData());
|
trainDirection.value = (showSelectData as never)[
|
||||||
|
platformModel.trainDirection
|
||||||
|
];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function onUpdate() {
|
function onUpdate() {
|
||||||
//console.log(stationModel, 'station 属性更新');
|
platformModel.hasdoor = JSON.parse((showSelect as never)[hasDoor.value]);
|
||||||
const station = drawStore.selectedGraphic as Platform;
|
platformModel.trainDirection = (showSelect as never)[trainDirection.value];
|
||||||
if (station) {
|
const platform = drawStore.selectedGraphic as Platform;
|
||||||
drawStore.getDrawApp().updateGraphicAndRecord(station, stationModel);
|
if (platform) {
|
||||||
|
drawStore.getDrawApp().updateGraphicAndRecord(platform, platformModel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
74
src/components/draw-app/properties/RectProperty.vue
Normal file
74
src/components/draw-app/properties/RectProperty.vue
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
<template>
|
||||||
|
<q-form>
|
||||||
|
<q-input outlined readonly v-model="stationModel.id" label="id" hint="" />
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model.number="stationModel.lineWidth"
|
||||||
|
type="number"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="线宽"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val > 0) || '画布宽必须大于0']"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model="stationModel.lineColor"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="线色"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val.length > 0) || '线色不能为空']"
|
||||||
|
>
|
||||||
|
<template v-slot:append>
|
||||||
|
<q-icon name="colorize" class="cursor-pointer">
|
||||||
|
<q-popup-proxy cover transition-show="scale" transition-hide="scale">
|
||||||
|
<q-color
|
||||||
|
v-model="stationModel.lineColor"
|
||||||
|
@change="
|
||||||
|
(val) => {
|
||||||
|
stationModel.lineColor = val;
|
||||||
|
onUpdate();
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</q-popup-proxy>
|
||||||
|
</q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</q-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { RectData } from 'src/examples/app/graphics/RectInteraction';
|
||||||
|
import { Rect } from 'src/graphics/rect/Rect';
|
||||||
|
import { useDrawStore } from 'src/stores/draw-store';
|
||||||
|
import { onMounted, reactive, watch } from 'vue';
|
||||||
|
|
||||||
|
const drawStore = useDrawStore();
|
||||||
|
const stationModel = reactive(new RectData());
|
||||||
|
|
||||||
|
drawStore.$subscribe;
|
||||||
|
watch(
|
||||||
|
() => drawStore.selectedGraphic,
|
||||||
|
(val) => {
|
||||||
|
if (val && val.type == Rect.Type) {
|
||||||
|
stationModel.copyFrom(val.saveData() as RectData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const station = drawStore.selectedGraphic as Rect;
|
||||||
|
|
||||||
|
if (station) {
|
||||||
|
stationModel.copyFrom(station.saveData());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function onUpdate() {
|
||||||
|
const station = drawStore.selectedGraphic as Rect;
|
||||||
|
if (station) {
|
||||||
|
drawStore.getDrawApp().updateGraphicAndRecord(station, stationModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
57
src/components/draw-app/properties/SignalProperty.vue
Normal file
57
src/components/draw-app/properties/SignalProperty.vue
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<template>
|
||||||
|
<q-form>
|
||||||
|
<q-input outlined readonly v-model="signalModel.id" label="id" hint="" />
|
||||||
|
<q-select
|
||||||
|
outlined
|
||||||
|
@blur="onUpdate"
|
||||||
|
v-model="signalDirection"
|
||||||
|
:options="optionsDirection"
|
||||||
|
label="方向"
|
||||||
|
/>
|
||||||
|
</q-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { SignalData } from 'src/examples/app/graphics/SignalInteraction';
|
||||||
|
import { Signal } from 'src/graphics/signal/Signal';
|
||||||
|
import { useDrawStore } from 'src/stores/draw-store';
|
||||||
|
import { onMounted, reactive, ref, watch } from 'vue';
|
||||||
|
|
||||||
|
const drawStore = useDrawStore();
|
||||||
|
const signalModel = reactive(new SignalData());
|
||||||
|
const signalDirection = ref('向右');
|
||||||
|
const optionsDirection = ['向左', '向右', '向上', '向下'];
|
||||||
|
enum directionSelect {
|
||||||
|
向左 = 'left',
|
||||||
|
向右 = 'right',
|
||||||
|
向上 = 'up',
|
||||||
|
向下 = 'down',
|
||||||
|
}
|
||||||
|
|
||||||
|
drawStore.$subscribe;
|
||||||
|
watch(
|
||||||
|
() => drawStore.selectedGraphic,
|
||||||
|
(val) => {
|
||||||
|
if (val && val.type == Signal.Type) {
|
||||||
|
signalModel.copyFrom(val.saveData() as SignalData);
|
||||||
|
signalDirection.value = (directionSelect as never)[signalModel.direction];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const signal = drawStore.selectedGraphic as Signal;
|
||||||
|
if (signal) {
|
||||||
|
signalModel.copyFrom(signal.saveData());
|
||||||
|
signalDirection.value = (directionSelect as never)[signalModel.direction];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function onUpdate() {
|
||||||
|
signalModel.direction = (directionSelect as never)[signalDirection.value];
|
||||||
|
const signal = drawStore.selectedGraphic as Signal;
|
||||||
|
if (signal) {
|
||||||
|
drawStore.getDrawApp().updateGraphicAndRecord(signal, signalModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
200
src/components/draw-app/properties/StationProperty.vue
Normal file
200
src/components/draw-app/properties/StationProperty.vue
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
<template>
|
||||||
|
<q-form>
|
||||||
|
<q-input outlined readonly v-model="stationModel.id" label="id" hint="" />
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
label="车站名称"
|
||||||
|
@blur="onUpdate"
|
||||||
|
v-model="stationModel.code"
|
||||||
|
lazy-rules
|
||||||
|
/>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model.number="stationModel.codeFontSize"
|
||||||
|
type="number"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="字体大小"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val > 0) || '字体大小必须大于0']"
|
||||||
|
/>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model="stationModel.codeColor"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="字体颜色"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val.length > 0) || '线色不能为空']"
|
||||||
|
>
|
||||||
|
<template v-slot:append>
|
||||||
|
<q-icon name="colorize" class="cursor-pointer">
|
||||||
|
<q-popup-proxy cover transition-show="scale" transition-hide="scale">
|
||||||
|
<q-color
|
||||||
|
v-model="stationModel.codeColor"
|
||||||
|
@change="
|
||||||
|
(val) => {
|
||||||
|
stationModel.codeColor = val;
|
||||||
|
onUpdate();
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</q-popup-proxy>
|
||||||
|
</q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
<q-select
|
||||||
|
outlined
|
||||||
|
@blur="onUpdate"
|
||||||
|
v-model="hasCircle"
|
||||||
|
:options="optionsCircle"
|
||||||
|
label="是否有圆圈"
|
||||||
|
/>
|
||||||
|
<div v-if="stationModel.hasCircle">
|
||||||
|
<q-item-label header>位置</q-item-label>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section no-wrap class="q-gutter-sm column">
|
||||||
|
<div class="row">
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="x"
|
||||||
|
v-model.number="stationModel.circlePoint.x"
|
||||||
|
type="number"
|
||||||
|
step="any"
|
||||||
|
class="col"
|
||||||
|
/>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="y"
|
||||||
|
v-model.number="stationModel.circlePoint.y"
|
||||||
|
type="number"
|
||||||
|
step="any"
|
||||||
|
class="col"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model.number="stationModel.radius"
|
||||||
|
type="number"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="半径"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val > 0) || '半径大小必须大于0']"
|
||||||
|
/>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model="stationModel.fillColor"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="填充颜色"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val.length > 0) || '线色不能为空']"
|
||||||
|
>
|
||||||
|
<template v-slot:append>
|
||||||
|
<q-icon name="colorize" class="cursor-pointer">
|
||||||
|
<q-popup-proxy
|
||||||
|
cover
|
||||||
|
transition-show="scale"
|
||||||
|
transition-hide="scale"
|
||||||
|
>
|
||||||
|
<q-color
|
||||||
|
v-model="stationModel.fillColor"
|
||||||
|
@change="
|
||||||
|
(val) => {
|
||||||
|
stationModel.fillColor = val;
|
||||||
|
onUpdate();
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</q-popup-proxy>
|
||||||
|
</q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model.number="stationModel.borderWidth"
|
||||||
|
type="number"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="边框宽度"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val > 0) || '画布宽必须大于0']"
|
||||||
|
/>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model="stationModel.borderColor"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="边框颜色"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val.length > 0) || '线色不能为空']"
|
||||||
|
>
|
||||||
|
<template v-slot:append>
|
||||||
|
<q-icon name="colorize" class="cursor-pointer">
|
||||||
|
<q-popup-proxy
|
||||||
|
cover
|
||||||
|
transition-show="scale"
|
||||||
|
transition-hide="scale"
|
||||||
|
>
|
||||||
|
<q-color
|
||||||
|
v-model="stationModel.borderColor"
|
||||||
|
@change="
|
||||||
|
(val) => {
|
||||||
|
stationModel.borderColor = val;
|
||||||
|
onUpdate();
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</q-popup-proxy>
|
||||||
|
</q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</div>
|
||||||
|
</q-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { StationData } from 'src/examples/app/graphics/StationInteraction';
|
||||||
|
import { Station } from 'src/graphics/station/Station';
|
||||||
|
import { useDrawStore } from 'src/stores/draw-store';
|
||||||
|
import { onMounted, reactive, ref, watch } from 'vue';
|
||||||
|
|
||||||
|
const drawStore = useDrawStore();
|
||||||
|
const stationModel = reactive(new StationData());
|
||||||
|
const hasCircle = ref('是');
|
||||||
|
const optionsCircle = ['是', '否'];
|
||||||
|
enum showSelect {
|
||||||
|
是 = 'true',
|
||||||
|
否 = 'false',
|
||||||
|
}
|
||||||
|
enum showSelectData {
|
||||||
|
true = '是',
|
||||||
|
false = '否',
|
||||||
|
}
|
||||||
|
|
||||||
|
drawStore.$subscribe;
|
||||||
|
watch(
|
||||||
|
() => drawStore.selectedGraphic,
|
||||||
|
(val) => {
|
||||||
|
if (val && val.type == Station.Type) {
|
||||||
|
stationModel.copyFrom(val.saveData() as StationData);
|
||||||
|
hasCircle.value = (showSelectData as never)[stationModel.hasCircle + ''];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const station = drawStore.selectedGraphic as Station;
|
||||||
|
if (station) {
|
||||||
|
stationModel.copyFrom(station.saveData());
|
||||||
|
hasCircle.value = (showSelectData as never)[stationModel.hasCircle + ''];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function onUpdate() {
|
||||||
|
stationModel.hasCircle = JSON.parse((showSelect as never)[hasCircle.value]);
|
||||||
|
const station = drawStore.selectedGraphic as Station;
|
||||||
|
if (station) {
|
||||||
|
drawStore.getDrawApp().updateGraphicAndRecord(station, stationModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
198
src/components/draw-app/properties/TrainProperty.vue
Normal file
198
src/components/draw-app/properties/TrainProperty.vue
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
<template>
|
||||||
|
<q-form>
|
||||||
|
<q-input outlined readonly v-model="trainModel.id" label="id" hint="" />
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model="trainModel.code"
|
||||||
|
label="车号"
|
||||||
|
hint=""
|
||||||
|
@blur="onUpdate"
|
||||||
|
/>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model="trainModel.codeColor"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="车号颜色"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val.length > 0) || '车号颜色不能为空']"
|
||||||
|
>
|
||||||
|
<template v-slot:append>
|
||||||
|
<q-icon name="colorize" class="cursor-pointer">
|
||||||
|
<q-popup-proxy cover transition-show="scale" transition-hide="scale">
|
||||||
|
<q-color
|
||||||
|
v-model="trainModel.codeColor"
|
||||||
|
@change="
|
||||||
|
(val) => {
|
||||||
|
trainModel.codeColor = val;
|
||||||
|
onUpdate();
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</q-popup-proxy>
|
||||||
|
</q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model="trainModel.headColor"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="箭头颜色"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val.length > 0) || '车箭头颜色不能为空']"
|
||||||
|
>
|
||||||
|
<template v-slot:append>
|
||||||
|
<q-icon name="colorize" class="cursor-pointer">
|
||||||
|
<q-popup-proxy cover transition-show="scale" transition-hide="scale">
|
||||||
|
<q-color
|
||||||
|
v-model="trainModel.headColor"
|
||||||
|
@change="
|
||||||
|
(val) => {
|
||||||
|
trainModel.headColor = val;
|
||||||
|
onUpdate();
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</q-popup-proxy>
|
||||||
|
</q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model.number="trainModel.codeFontSize"
|
||||||
|
type="number"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="字体大小"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val > 0) || '字体大小必须大于0']"
|
||||||
|
/>
|
||||||
|
<q-select
|
||||||
|
outlined
|
||||||
|
@blur="onUpdate"
|
||||||
|
v-model="hasBorder"
|
||||||
|
:options="optionsDoor"
|
||||||
|
label="是否有边框"
|
||||||
|
/>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model.number="trainModel.borderWidth"
|
||||||
|
type="number"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="边框线宽"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val > 0) || '边框线宽必须大于0']"
|
||||||
|
/>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model="trainModel.borderColor"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="边框颜色"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val.length > 0) || '边框颜色不能为空']"
|
||||||
|
>
|
||||||
|
<template v-slot:append>
|
||||||
|
<q-icon name="colorize" class="cursor-pointer">
|
||||||
|
<q-popup-proxy cover transition-show="scale" transition-hide="scale">
|
||||||
|
<q-color
|
||||||
|
v-model="trainModel.borderColor"
|
||||||
|
@change="
|
||||||
|
(val) => {
|
||||||
|
trainModel.borderColor = val;
|
||||||
|
onUpdate();
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</q-popup-proxy>
|
||||||
|
</q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model="trainModel.bodyColor"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="背景颜色"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val.length > 0) || '背景颜色不能为空']"
|
||||||
|
>
|
||||||
|
<template v-slot:append>
|
||||||
|
<q-icon name="colorize" class="cursor-pointer">
|
||||||
|
<q-popup-proxy cover transition-show="scale" transition-hide="scale">
|
||||||
|
<q-color
|
||||||
|
v-model="trainModel.bodyColor"
|
||||||
|
@change="
|
||||||
|
(val) => {
|
||||||
|
trainModel.bodyColor = val;
|
||||||
|
onUpdate();
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</q-popup-proxy>
|
||||||
|
</q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
<q-select
|
||||||
|
outlined
|
||||||
|
@blur="onUpdate"
|
||||||
|
v-model="trainDirection"
|
||||||
|
:options="optionsDirection"
|
||||||
|
label="行驶方向"
|
||||||
|
/>
|
||||||
|
</q-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { TrainData } from 'src/examples/app/graphics/TrainInteraction';
|
||||||
|
import { Train } from 'src/graphics/train/Train';
|
||||||
|
import { useDrawStore } from 'src/stores/draw-store';
|
||||||
|
import { onMounted, reactive, ref, watch } from 'vue';
|
||||||
|
|
||||||
|
const drawStore = useDrawStore();
|
||||||
|
const trainModel = reactive(new TrainData());
|
||||||
|
const hasBorder = ref('是');
|
||||||
|
const optionsDoor = ['是', '否'];
|
||||||
|
const trainDirection = ref('向左');
|
||||||
|
const optionsDirection = ['向左', '向右'];
|
||||||
|
enum showSelect {
|
||||||
|
是 = 'true',
|
||||||
|
否 = 'false',
|
||||||
|
向左 = 'left',
|
||||||
|
向右 = 'right',
|
||||||
|
}
|
||||||
|
enum showSelectData {
|
||||||
|
true = '是',
|
||||||
|
false = '否',
|
||||||
|
left = '向左',
|
||||||
|
right = '向右',
|
||||||
|
}
|
||||||
|
|
||||||
|
drawStore.$subscribe;
|
||||||
|
watch(
|
||||||
|
() => drawStore.selectedGraphic,
|
||||||
|
(val) => {
|
||||||
|
if (val && val.type == Train.Type) {
|
||||||
|
trainModel.copyFrom(val.saveData() as TrainData);
|
||||||
|
hasBorder.value = (showSelectData as never)[trainModel.hasBorder + ''];
|
||||||
|
trainDirection.value = (showSelectData as never)[
|
||||||
|
trainModel.trainDirection
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const train = drawStore.selectedGraphic as Train;
|
||||||
|
if (train) {
|
||||||
|
trainModel.copyFrom(train.saveData());
|
||||||
|
hasBorder.value = (showSelectData as never)[trainModel.hasBorder + ''];
|
||||||
|
trainDirection.value = (showSelectData as never)[trainModel.trainDirection];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function onUpdate() {
|
||||||
|
trainModel.hasBorder = JSON.parse((showSelect as never)[hasBorder.value]);
|
||||||
|
trainModel.trainDirection = (showSelect as never)[trainDirection.value];
|
||||||
|
const train = drawStore.selectedGraphic as Train;
|
||||||
|
if (train) {
|
||||||
|
drawStore.getDrawApp().updateGraphicAndRecord(train, trainModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
76
src/components/draw-app/templates/RectTemplate.vue
Normal file
76
src/components/draw-app/templates/RectTemplate.vue
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
<template>
|
||||||
|
<q-form>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model.number="template.lineWidth"
|
||||||
|
type="number"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="线宽 *"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val > 0) || '线宽必须大于0']"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model="template.lineColor"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="线色 *"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val.length > 0) || '线色不能为空']"
|
||||||
|
>
|
||||||
|
<template v-slot:append>
|
||||||
|
<q-icon name="colorize" class="cursor-pointer">
|
||||||
|
<q-popup-proxy cover transition-show="scale" transition-hide="scale">
|
||||||
|
<q-color
|
||||||
|
v-model="template.lineColor"
|
||||||
|
@change="
|
||||||
|
(val) => {
|
||||||
|
template.lineColor = val;
|
||||||
|
onUpdate();
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</q-popup-proxy>
|
||||||
|
</q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</q-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { LinkTemplate } from 'src/graphics/link/Link';
|
||||||
|
import { useDrawStore } from 'src/stores/draw-store';
|
||||||
|
import { onMounted, reactive } from 'vue';
|
||||||
|
|
||||||
|
const drawStore = useDrawStore();
|
||||||
|
const template = reactive({
|
||||||
|
lineWidth: 1,
|
||||||
|
lineColor: '#0000ff',
|
||||||
|
curve: false,
|
||||||
|
segmentsCount: 10,
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const type = drawStore.drawGraphicType;
|
||||||
|
if (type) {
|
||||||
|
const gt = drawStore.drawGraphicTemplate;
|
||||||
|
if (gt) {
|
||||||
|
const lt = gt as LinkTemplate;
|
||||||
|
template.lineWidth = lt.lineWidth;
|
||||||
|
template.lineColor = lt.lineColor;
|
||||||
|
template.curve = lt.curve;
|
||||||
|
template.segmentsCount = lt.segmentsCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function onUpdate() {
|
||||||
|
const gt = drawStore.drawGraphicTemplate as LinkTemplate;
|
||||||
|
if (gt) {
|
||||||
|
gt.lineWidth = template.lineWidth;
|
||||||
|
gt.lineColor = template.lineColor;
|
||||||
|
gt.curve = template.curve;
|
||||||
|
gt.segmentsCount = template.segmentsCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
70
src/components/draw-app/templates/StationTemplate.vue
Normal file
70
src/components/draw-app/templates/StationTemplate.vue
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<template>
|
||||||
|
<q-form>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model.number="template.lineWidth"
|
||||||
|
type="number"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="字体大小 *"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val > 0) || '线宽必须大于0']"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model="template.lineColor"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="字体颜色 *"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val.length > 0) || '线色不能为空']"
|
||||||
|
>
|
||||||
|
<template v-slot:append>
|
||||||
|
<q-icon name="colorize" class="cursor-pointer">
|
||||||
|
<q-popup-proxy cover transition-show="scale" transition-hide="scale">
|
||||||
|
<q-color
|
||||||
|
v-model="template.lineColor"
|
||||||
|
@change="
|
||||||
|
(val) => {
|
||||||
|
template.lineColor = val;
|
||||||
|
onUpdate();
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</q-popup-proxy>
|
||||||
|
</q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</q-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { LinkTemplate } from 'src/graphics/link/Link';
|
||||||
|
import { useDrawStore } from 'src/stores/draw-store';
|
||||||
|
import { onMounted, reactive } from 'vue';
|
||||||
|
|
||||||
|
const drawStore = useDrawStore();
|
||||||
|
const template = reactive({
|
||||||
|
lineWidth: 1,
|
||||||
|
lineColor: '#0000ff',
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const type = drawStore.drawGraphicType;
|
||||||
|
if (type) {
|
||||||
|
const gt = drawStore.drawGraphicTemplate;
|
||||||
|
if (gt) {
|
||||||
|
const lt = gt as LinkTemplate;
|
||||||
|
template.lineWidth = lt.lineWidth;
|
||||||
|
template.lineColor = lt.lineColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function onUpdate() {
|
||||||
|
const gt = drawStore.drawGraphicTemplate as LinkTemplate;
|
||||||
|
if (gt) {
|
||||||
|
gt.lineWidth = template.lineWidth;
|
||||||
|
gt.lineColor = template.lineColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
69
src/components/draw-app/templates/TrainTemplate.vue
Normal file
69
src/components/draw-app/templates/TrainTemplate.vue
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
<template>
|
||||||
|
<q-form>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model.number="template.codeFontSize"
|
||||||
|
type="number"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="字体大小 *"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val > 0) || '字体大小必须大于0']"
|
||||||
|
/>
|
||||||
|
<q-input
|
||||||
|
outlined
|
||||||
|
v-model="template.headColor"
|
||||||
|
@blur="onUpdate"
|
||||||
|
label="箭头颜色 *"
|
||||||
|
lazy-rules
|
||||||
|
:rules="[(val) => (val && val.length > 0) || '箭头颜色不能为空']"
|
||||||
|
>
|
||||||
|
<template v-slot:append>
|
||||||
|
<q-icon name="colorize" class="cursor-pointer">
|
||||||
|
<q-popup-proxy cover transition-show="scale" transition-hide="scale">
|
||||||
|
<q-color
|
||||||
|
v-model="template.headColor"
|
||||||
|
@change="
|
||||||
|
(val) => {
|
||||||
|
template.headColor = val;
|
||||||
|
onUpdate();
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</q-popup-proxy>
|
||||||
|
</q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</q-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { TrainTemplate } from 'src/graphics/train/Train';
|
||||||
|
import { useDrawStore } from 'src/stores/draw-store';
|
||||||
|
import { onMounted, reactive } from 'vue';
|
||||||
|
|
||||||
|
const drawStore = useDrawStore();
|
||||||
|
const template = reactive({
|
||||||
|
codeFontSize: 22,
|
||||||
|
headColor: '#00FF00',
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const type = drawStore.drawGraphicType;
|
||||||
|
if (type) {
|
||||||
|
const gt = drawStore.drawGraphicTemplate;
|
||||||
|
if (gt) {
|
||||||
|
const lt = gt as TrainTemplate;
|
||||||
|
template.codeFontSize = lt.codeFontSize;
|
||||||
|
template.headColor = lt.headColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function onUpdate() {
|
||||||
|
const gt = drawStore.drawGraphicTemplate as TrainTemplate;
|
||||||
|
if (gt) {
|
||||||
|
gt.codeFontSize = template.codeFontSize;
|
||||||
|
gt.headColor = template.headColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
42
src/examples/app/api/ApiCommon.ts
Normal file
42
src/examples/app/api/ApiCommon.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
export class PageQueryDto {
|
||||||
|
current: number;
|
||||||
|
size: number;
|
||||||
|
orders?: OrderItemDto[];
|
||||||
|
constructor(current: number, size: number, orders?: OrderItemDto[]) {
|
||||||
|
this.current = current;
|
||||||
|
this.size = size;
|
||||||
|
this.orders = orders;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OrderItemDto {
|
||||||
|
column: string;
|
||||||
|
asc: boolean;
|
||||||
|
constructor(column: string, asc: boolean) {
|
||||||
|
this.column = column;
|
||||||
|
this.asc = asc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static asc(column: string): OrderItemDto {
|
||||||
|
return new OrderItemDto(column, true);
|
||||||
|
}
|
||||||
|
static desc(column: string): OrderItemDto {
|
||||||
|
return new OrderItemDto(column, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PageDto<T = unknown> {
|
||||||
|
records: T[];
|
||||||
|
/**
|
||||||
|
* 记录总数
|
||||||
|
*/
|
||||||
|
total: number;
|
||||||
|
/**
|
||||||
|
* 第几页
|
||||||
|
*/
|
||||||
|
current: number;
|
||||||
|
/**
|
||||||
|
* 每页数量
|
||||||
|
*/
|
||||||
|
size: number;
|
||||||
|
}
|
77
src/examples/app/api/UserApi.ts
Normal file
77
src/examples/app/api/UserApi.ts
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import { api } from 'src/boot/axios';
|
||||||
|
import { PageDto, PageQueryDto } from './ApiCommon';
|
||||||
|
import { Md5 } from 'ts-md5';
|
||||||
|
|
||||||
|
const UserUriBase = '/api/user';
|
||||||
|
|
||||||
|
interface RegisterInfo {
|
||||||
|
name: string;
|
||||||
|
mobile: string;
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface User {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
mobile: string;
|
||||||
|
password: string;
|
||||||
|
registerTime: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PasswordSult = '4a6d74126bfd06d69406fcccb7e7d5d9'; // 密码加盐
|
||||||
|
function encryptPassword(password: string): string {
|
||||||
|
const md5 = new Md5();
|
||||||
|
return md5.appendStr(`${password}${PasswordSult}`).end() as string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户注册
|
||||||
|
* @param info
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export async function register(info: RegisterInfo): Promise<User> {
|
||||||
|
const response = await api.post(`${UserUriBase}/register`, {
|
||||||
|
...info,
|
||||||
|
password: encryptPassword(info.password),
|
||||||
|
});
|
||||||
|
return response.data as User;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LoginInfo {
|
||||||
|
account: string;
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户登录
|
||||||
|
* @param loginInfo
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export async function login(loginInfo: LoginInfo): Promise<string> {
|
||||||
|
const info = { ...loginInfo, password: encryptPassword(loginInfo.password) };
|
||||||
|
const response = await api.post(`${UserUriBase}/login`, info);
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PagingQueryParams extends PageQueryDto {
|
||||||
|
name?: string;
|
||||||
|
roleId?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询用户信息
|
||||||
|
* @param params
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export async function pageQuery(
|
||||||
|
params: PagingQueryParams
|
||||||
|
): Promise<PageDto<User>> {
|
||||||
|
const response = await api.get(`${UserUriBase}/paging`, {
|
||||||
|
params: params,
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function distributeRole(query: { userId: number; roleIds: number[] }) {
|
||||||
|
return api.post('/api/role/distribute', query);
|
||||||
|
}
|
@ -7,6 +7,10 @@ message RtssGraphicStorage {
|
|||||||
repeated Link links = 2;
|
repeated Link links = 2;
|
||||||
repeated IscsFan iscsFans = 3;
|
repeated IscsFan iscsFans = 3;
|
||||||
repeated Platform Platforms = 4;
|
repeated Platform Platforms = 4;
|
||||||
|
repeated Station stations = 5;
|
||||||
|
repeated Rect Rects = 6;
|
||||||
|
repeated Train train = 7;
|
||||||
|
repeated Signal signals = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Canvas {
|
message Canvas {
|
||||||
@ -64,17 +68,57 @@ message Link {
|
|||||||
repeated Point points = 7; // 点坐标列表
|
repeated Point points = 7; // 点坐标列表
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message Rect {
|
||||||
|
CommonInfo common = 1;
|
||||||
|
string code = 2;
|
||||||
|
int32 lineWidth = 3; // 线宽
|
||||||
|
string lineColor = 4; // 线色
|
||||||
|
Point point = 5; // 位置坐标
|
||||||
|
float width = 6;//宽度
|
||||||
|
float height = 7; //高度
|
||||||
|
repeated Point points = 8; // 点坐标列表
|
||||||
|
}
|
||||||
|
|
||||||
message Platform {
|
message Platform {
|
||||||
CommonInfo common = 1;
|
CommonInfo common = 1;
|
||||||
string code = 2;
|
string code = 2;
|
||||||
bool hasdoor = 3; // 是否有屏蔽门
|
bool hasdoor = 3; // 是否有屏蔽门
|
||||||
int32 lineWidth = 4; // 线宽
|
string trainDirection = 4; // 行驶方向--屏蔽门上下
|
||||||
string lineColor = 5; // 站台线色
|
int32 lineWidth = 5; // 线宽
|
||||||
string lineColorDoor = 6; // 屏蔽门线色
|
string lineColor = 6; // 站台线色
|
||||||
Point point = 7; // 位置坐标
|
string lineColorDoor = 7; // 屏蔽门线色
|
||||||
float width = 8;//宽度
|
Point point = 8; // 位置坐标
|
||||||
float height = 9; //高度
|
float width = 9;//宽度
|
||||||
repeated string orbitCode = 10;//站台轨
|
float height = 10; //高度
|
||||||
|
repeated string orbitCode = 11;//站台轨
|
||||||
|
}
|
||||||
|
|
||||||
|
message Station {
|
||||||
|
CommonInfo common = 1;
|
||||||
|
string code = 2;
|
||||||
|
bool hasCircle = 3; // 是否有圆圈--线网图
|
||||||
|
int32 radius = 4; // 半径
|
||||||
|
int32 borderWidth = 5; // 边框线宽
|
||||||
|
string borderColor = 6; // 边框颜色
|
||||||
|
string fillColor = 7; // 圆填充颜色
|
||||||
|
string codeColor = 8; // 车站字体颜色
|
||||||
|
int32 codeFontSize = 9; // 车站字体大小
|
||||||
|
Point point = 10; // 位置坐标
|
||||||
|
Point circlePoint = 11; // 位置坐标
|
||||||
|
}
|
||||||
|
|
||||||
|
message Train {
|
||||||
|
CommonInfo common = 1;
|
||||||
|
string code = 2;
|
||||||
|
string codeColor = 3; // 车号颜色
|
||||||
|
int32 codeFontSize = 4; // 车号字体大小
|
||||||
|
Point point = 5; // 位置坐标
|
||||||
|
string trainDirection = 6; // 行驶方向
|
||||||
|
bool hasBorder = 7; // 是否有边框
|
||||||
|
int32 borderWidth = 8; // 边框线宽
|
||||||
|
string borderColor = 9; // 边框颜色
|
||||||
|
string headColor = 10; // 箭头颜色
|
||||||
|
string bodyColor = 11; // 背景色
|
||||||
}
|
}
|
||||||
|
|
||||||
message IscsFan {
|
message IscsFan {
|
||||||
@ -83,3 +127,8 @@ message IscsFan {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message Turnout {}
|
message Turnout {}
|
||||||
|
|
||||||
|
message Signal {
|
||||||
|
CommonInfo common = 1;
|
||||||
|
string code = 2;
|
||||||
|
}
|
||||||
|
14
src/examples/app/app_message/protos/graphic_states.proto
Normal file
14
src/examples/app/app_message/protos/graphic_states.proto
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package graphicStates;
|
||||||
|
|
||||||
|
// 公共属性
|
||||||
|
message CommonState {
|
||||||
|
string code = 1;
|
||||||
|
string graphicType = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message IscsFan {
|
||||||
|
CommonState common = 1;
|
||||||
|
int32 state = 2;
|
||||||
|
}
|
13
src/examples/app/configs/TokenManage.ts
Normal file
13
src/examples/app/configs/TokenManage.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
const JwtTokenKey = 'jwttoken';
|
||||||
|
|
||||||
|
export function saveJwtToken(token: string) {
|
||||||
|
sessionStorage.setItem(JwtTokenKey, `Bearer ${token}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getJwtToken(): string | null {
|
||||||
|
return sessionStorage.getItem(JwtTokenKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function clearJwtToken(): void {
|
||||||
|
sessionStorage.removeItem(JwtTokenKey);
|
||||||
|
}
|
13
src/examples/app/configs/UrlManage.ts
Normal file
13
src/examples/app/configs/UrlManage.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
function getHost(): string {
|
||||||
|
// return '192.168.3.7:9081';
|
||||||
|
return '192.168.3.233:9081';
|
||||||
|
// return '192.168.3.7:9081';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getHttpBase() {
|
||||||
|
return `http://${getHost()}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getWebsocketUrl() {
|
||||||
|
return `ws://${getHost()}/ws-default`;
|
||||||
|
}
|
@ -2,12 +2,14 @@ import * as pb_1 from 'google-protobuf';
|
|||||||
import {
|
import {
|
||||||
ChildTransform,
|
ChildTransform,
|
||||||
GraphicData,
|
GraphicData,
|
||||||
|
GraphicState,
|
||||||
GraphicTransform,
|
GraphicTransform,
|
||||||
IChildTransform,
|
IChildTransform,
|
||||||
IGraphicTransform,
|
IGraphicTransform,
|
||||||
} from 'src/jlgraphic';
|
} from 'src/jlgraphic';
|
||||||
import { toStorageTransform } from '..';
|
import { toStorageTransform } from '..';
|
||||||
import { graphicData } from '../protos/draw_data_storage';
|
import { graphicData } from '../protos/draw_data_storage';
|
||||||
|
import { graphicStates } from '../protos/graphic_states';
|
||||||
|
|
||||||
export interface ICommonInfo {
|
export interface ICommonInfo {
|
||||||
id: string;
|
id: string;
|
||||||
@ -27,10 +29,10 @@ export abstract class GraphicDataBase implements GraphicData {
|
|||||||
this._data = data;
|
this._data = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
static defaultCommonInfo(): graphicData.CommonInfo {
|
static defaultCommonInfo(graphicType: string): graphicData.CommonInfo {
|
||||||
return new graphicData.CommonInfo({
|
return new graphicData.CommonInfo({
|
||||||
id: '',
|
id: '',
|
||||||
graphicType: '',
|
graphicType: graphicType,
|
||||||
transform: new graphicData.Transform({
|
transform: new graphicData.Transform({
|
||||||
position: new graphicData.Point({ x: 0, y: 0 }),
|
position: new graphicData.Point({ x: 0, y: 0 }),
|
||||||
scale: new graphicData.Point({ x: 1, y: 1 }),
|
scale: new graphicData.Point({ x: 1, y: 1 }),
|
||||||
@ -99,3 +101,41 @@ export abstract class GraphicDataBase implements GraphicData {
|
|||||||
return pb_1.Message.equals(this._data, other._data);
|
return pb_1.Message.equals(this._data, other._data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IProtoGraphicState extends pb_1.Message {
|
||||||
|
common: graphicStates.CommonState;
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class GraphicStateBase implements GraphicState {
|
||||||
|
_state: IProtoGraphicState;
|
||||||
|
constructor(state: IProtoGraphicState) {
|
||||||
|
this._state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
static defaultCommonState(graphicType: string): graphicStates.CommonState {
|
||||||
|
return new graphicStates.CommonState({
|
||||||
|
code: '',
|
||||||
|
graphicType: graphicType,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getState<S extends IProtoGraphicState>(): S {
|
||||||
|
return this._state as S;
|
||||||
|
}
|
||||||
|
|
||||||
|
get code(): string {
|
||||||
|
return this._state.common.code;
|
||||||
|
}
|
||||||
|
get graphicType(): string {
|
||||||
|
return this._state.common.graphicType;
|
||||||
|
}
|
||||||
|
clone(): GraphicState {
|
||||||
|
throw new Error('Method not implemented.');
|
||||||
|
}
|
||||||
|
copyFrom(data: GraphicStateBase): void {
|
||||||
|
pb_1.Message.copyInto(data._state, this._state);
|
||||||
|
}
|
||||||
|
eq(data: GraphicStateBase): boolean {
|
||||||
|
return pb_1.Message.equals(this._state, data._state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
import * as pb_1 from 'google-protobuf';
|
import * as pb_1 from 'google-protobuf';
|
||||||
import { IIscsFanData } from 'src/graphics/iscs-fan/IscsFan';
|
import {
|
||||||
|
IIscsFanData,
|
||||||
|
IIscsFanState,
|
||||||
|
IscsFan,
|
||||||
|
} from 'src/graphics/iscs-fan/IscsFan';
|
||||||
import { graphicData } from '../protos/draw_data_storage';
|
import { graphicData } from '../protos/draw_data_storage';
|
||||||
import { GraphicDataBase } from './GraphicDataBase';
|
import { graphicStates } from '../protos/graphic_states';
|
||||||
|
import { GraphicDataBase, GraphicStateBase } from './GraphicDataBase';
|
||||||
|
|
||||||
export class IscsFanData extends GraphicDataBase implements IIscsFanData {
|
export class IscsFanData extends GraphicDataBase implements IIscsFanData {
|
||||||
constructor(data?: graphicData.IscsFan) {
|
constructor(data?: graphicData.IscsFan) {
|
||||||
@ -10,7 +15,7 @@ export class IscsFanData extends GraphicDataBase implements IIscsFanData {
|
|||||||
fan = data;
|
fan = data;
|
||||||
} else {
|
} else {
|
||||||
fan = new graphicData.IscsFan({
|
fan = new graphicData.IscsFan({
|
||||||
common: GraphicDataBase.defaultCommonInfo(),
|
common: GraphicDataBase.defaultCommonInfo(IscsFan.Type),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
super(fan);
|
super(fan);
|
||||||
@ -36,3 +41,32 @@ export class IscsFanData extends GraphicDataBase implements IIscsFanData {
|
|||||||
return pb_1.Message.equals(this.data, other.data);
|
return pb_1.Message.equals(this.data, other.data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class IscsFanState extends GraphicStateBase implements IIscsFanState {
|
||||||
|
constructor(proto?: graphicStates.IscsFan) {
|
||||||
|
let states;
|
||||||
|
if (proto) {
|
||||||
|
states = proto;
|
||||||
|
states.common.graphicType = IscsFan.Type;
|
||||||
|
} else {
|
||||||
|
states = new graphicStates.IscsFan({
|
||||||
|
common: GraphicStateBase.defaultCommonState(IscsFan.Type),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
super(states);
|
||||||
|
}
|
||||||
|
|
||||||
|
get states(): graphicStates.IscsFan {
|
||||||
|
return this.getState<graphicStates.IscsFan>();
|
||||||
|
}
|
||||||
|
|
||||||
|
get state(): number {
|
||||||
|
return this.states.state;
|
||||||
|
}
|
||||||
|
set state(v: number) {
|
||||||
|
this.states.state = v;
|
||||||
|
}
|
||||||
|
clone(): IscsFanState {
|
||||||
|
return new IscsFanState(this.states.cloneMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -33,6 +33,12 @@ export class PlatformData extends GraphicDataBase implements IPlatformData {
|
|||||||
set hasdoor(v: boolean) {
|
set hasdoor(v: boolean) {
|
||||||
this.data.hasdoor = v;
|
this.data.hasdoor = v;
|
||||||
}
|
}
|
||||||
|
get trainDirection(): string {
|
||||||
|
return this.data.trainDirection;
|
||||||
|
}
|
||||||
|
set trainDirection(v: string) {
|
||||||
|
this.data.trainDirection = v;
|
||||||
|
}
|
||||||
get lineWidth(): number {
|
get lineWidth(): number {
|
||||||
return this.data.lineWidth;
|
return this.data.lineWidth;
|
||||||
}
|
}
|
||||||
|
77
src/examples/app/graphics/RectInteraction.ts
Normal file
77
src/examples/app/graphics/RectInteraction.ts
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import * as pb_1 from 'google-protobuf';
|
||||||
|
import { IPointData } from 'pixi.js';
|
||||||
|
import { IRectData } from 'src/graphics/rect/Rect';
|
||||||
|
import { graphicData } from '../protos/draw_data_storage';
|
||||||
|
import { GraphicDataBase } from './GraphicDataBase';
|
||||||
|
|
||||||
|
export class RectData extends GraphicDataBase implements IRectData {
|
||||||
|
constructor(data?: graphicData.Rect) {
|
||||||
|
let rect;
|
||||||
|
if (!data) {
|
||||||
|
rect = new graphicData.Rect({
|
||||||
|
common: GraphicDataBase.defaultCommonInfo(),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
rect = data;
|
||||||
|
}
|
||||||
|
super(rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get data(): graphicData.Rect {
|
||||||
|
return this.getData<graphicData.Rect>();
|
||||||
|
}
|
||||||
|
|
||||||
|
get code(): string {
|
||||||
|
return this.data.code;
|
||||||
|
}
|
||||||
|
set code(v: string) {
|
||||||
|
this.data.code = v;
|
||||||
|
}
|
||||||
|
get lineWidth(): number {
|
||||||
|
return this.data.lineWidth;
|
||||||
|
}
|
||||||
|
set lineWidth(v: number) {
|
||||||
|
this.data.lineWidth = v;
|
||||||
|
}
|
||||||
|
get lineColor(): string {
|
||||||
|
return this.data.lineColor;
|
||||||
|
}
|
||||||
|
set lineColor(v: string) {
|
||||||
|
this.data.lineColor = v;
|
||||||
|
}
|
||||||
|
get point(): IPointData {
|
||||||
|
return this.data.point;
|
||||||
|
}
|
||||||
|
set point(point: IPointData) {
|
||||||
|
this.data.point = new graphicData.Point({ x: point.x, y: point.y });
|
||||||
|
}
|
||||||
|
get width(): number {
|
||||||
|
return this.data.width;
|
||||||
|
}
|
||||||
|
set width(v: number) {
|
||||||
|
this.data.width = v;
|
||||||
|
}
|
||||||
|
get height(): number {
|
||||||
|
return this.data.height;
|
||||||
|
}
|
||||||
|
set height(v: number) {
|
||||||
|
this.data.height = v;
|
||||||
|
}
|
||||||
|
get points(): IPointData[] {
|
||||||
|
return this.data.points;
|
||||||
|
}
|
||||||
|
set points(points: IPointData[]) {
|
||||||
|
this.data.points = points.map(
|
||||||
|
(p) => new graphicData.Point({ x: p.x, y: p.y })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
clone(): RectData {
|
||||||
|
return new RectData(this.data.cloneMessage());
|
||||||
|
}
|
||||||
|
copyFrom(data: RectData): void {
|
||||||
|
pb_1.Message.copyInto(data.data, this.data);
|
||||||
|
}
|
||||||
|
eq(other: RectData): boolean {
|
||||||
|
return pb_1.Message.equals(this.data, other.data);
|
||||||
|
}
|
||||||
|
}
|
37
src/examples/app/graphics/SignalInteraction.ts
Normal file
37
src/examples/app/graphics/SignalInteraction.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import * as pb_1 from 'google-protobuf';
|
||||||
|
import { ISignalData, Signal } from 'src/graphics/signal/Signal';
|
||||||
|
import { graphicData } from '../protos/draw_data_storage';
|
||||||
|
import { GraphicDataBase } from './GraphicDataBase';
|
||||||
|
|
||||||
|
export class SignalData extends GraphicDataBase implements ISignalData {
|
||||||
|
constructor(data?: graphicData.Signal) {
|
||||||
|
let signal;
|
||||||
|
if (!data) {
|
||||||
|
signal = new graphicData.Signal({
|
||||||
|
common: GraphicDataBase.defaultCommonInfo(Signal.Type),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
signal = data;
|
||||||
|
}
|
||||||
|
super(signal);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get data(): graphicData.Signal {
|
||||||
|
return this.getData<graphicData.Signal>();
|
||||||
|
}
|
||||||
|
get code(): string {
|
||||||
|
return this.data.code;
|
||||||
|
}
|
||||||
|
set code(v: string) {
|
||||||
|
this.data.code = v;
|
||||||
|
}
|
||||||
|
clone(): SignalData {
|
||||||
|
return new SignalData(this.data.cloneMessage());
|
||||||
|
}
|
||||||
|
copyFrom(data: SignalData): void {
|
||||||
|
pb_1.Message.copyInto(data.data, this.data);
|
||||||
|
}
|
||||||
|
eq(other: SignalData): boolean {
|
||||||
|
return pb_1.Message.equals(this.data, other.data);
|
||||||
|
}
|
||||||
|
}
|
92
src/examples/app/graphics/StationInteraction.ts
Normal file
92
src/examples/app/graphics/StationInteraction.ts
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
import * as pb_1 from 'google-protobuf';
|
||||||
|
import { IPointData } from 'pixi.js';
|
||||||
|
import { IStationData } from 'src/graphics/station/Station';
|
||||||
|
import { graphicData } from '../protos/draw_data_storage';
|
||||||
|
import { GraphicDataBase } from './GraphicDataBase';
|
||||||
|
|
||||||
|
export class StationData extends GraphicDataBase implements IStationData {
|
||||||
|
constructor(data?: graphicData.Station) {
|
||||||
|
let station;
|
||||||
|
if (!data) {
|
||||||
|
station = new graphicData.Station({
|
||||||
|
common: GraphicDataBase.defaultCommonInfo(),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
station = data;
|
||||||
|
}
|
||||||
|
super(station);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get data(): graphicData.Station {
|
||||||
|
return this.getData<graphicData.Station>();
|
||||||
|
}
|
||||||
|
get code(): string {
|
||||||
|
return this.data.code;
|
||||||
|
}
|
||||||
|
set code(v: string) {
|
||||||
|
this.data.code = v;
|
||||||
|
}
|
||||||
|
get hasCircle(): boolean {
|
||||||
|
return this.data.hasCircle;
|
||||||
|
}
|
||||||
|
set hasCircle(v: boolean) {
|
||||||
|
this.data.hasCircle = v;
|
||||||
|
}
|
||||||
|
get radius(): number {
|
||||||
|
return this.data.radius;
|
||||||
|
}
|
||||||
|
set radius(v: number) {
|
||||||
|
this.data.radius = v;
|
||||||
|
}
|
||||||
|
get borderWidth(): number {
|
||||||
|
return this.data.borderWidth;
|
||||||
|
}
|
||||||
|
set borderWidth(v: number) {
|
||||||
|
this.data.borderWidth = v;
|
||||||
|
}
|
||||||
|
get borderColor(): string {
|
||||||
|
return this.data.borderColor;
|
||||||
|
}
|
||||||
|
set borderColor(v: string) {
|
||||||
|
this.data.borderColor = v;
|
||||||
|
}
|
||||||
|
get fillColor(): string {
|
||||||
|
return this.data.fillColor;
|
||||||
|
}
|
||||||
|
set fillColor(v: string) {
|
||||||
|
this.data.fillColor = v;
|
||||||
|
}
|
||||||
|
get codeColor(): string {
|
||||||
|
return this.data.codeColor;
|
||||||
|
}
|
||||||
|
set codeColor(v: string) {
|
||||||
|
this.data.codeColor = v;
|
||||||
|
}
|
||||||
|
get codeFontSize(): number {
|
||||||
|
return this.data.codeFontSize;
|
||||||
|
}
|
||||||
|
set codeFontSize(v: number) {
|
||||||
|
this.data.codeFontSize = v;
|
||||||
|
}
|
||||||
|
get point(): IPointData {
|
||||||
|
return this.data.point;
|
||||||
|
}
|
||||||
|
set point(point: IPointData) {
|
||||||
|
this.data.point = new graphicData.Point({ x: point.x, y: point.y });
|
||||||
|
}
|
||||||
|
get circlePoint(): IPointData {
|
||||||
|
return this.data.circlePoint;
|
||||||
|
}
|
||||||
|
set circlePoint(point: IPointData) {
|
||||||
|
this.data.circlePoint = new graphicData.Point({ x: point.x, y: point.y });
|
||||||
|
}
|
||||||
|
clone(): StationData {
|
||||||
|
return new StationData(this.data.cloneMessage());
|
||||||
|
}
|
||||||
|
copyFrom(data: StationData): void {
|
||||||
|
pb_1.Message.copyInto(data.data, this.data);
|
||||||
|
}
|
||||||
|
eq(other: StationData): boolean {
|
||||||
|
return pb_1.Message.equals(this.data, other.data);
|
||||||
|
}
|
||||||
|
}
|
92
src/examples/app/graphics/TrainInteraction.ts
Normal file
92
src/examples/app/graphics/TrainInteraction.ts
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
import * as pb_1 from 'google-protobuf';
|
||||||
|
import { IPointData } from 'pixi.js';
|
||||||
|
import { ITrainData } from 'src/graphics/train/Train';
|
||||||
|
import { graphicData } from '../protos/draw_data_storage';
|
||||||
|
import { GraphicDataBase } from './GraphicDataBase';
|
||||||
|
|
||||||
|
export class TrainData extends GraphicDataBase implements ITrainData {
|
||||||
|
constructor(data?: graphicData.Train) {
|
||||||
|
let train;
|
||||||
|
if (!data) {
|
||||||
|
train = new graphicData.Train({
|
||||||
|
common: GraphicDataBase.defaultCommonInfo(),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
train = data;
|
||||||
|
}
|
||||||
|
super(train);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get data(): graphicData.Train {
|
||||||
|
return this.getData<graphicData.Train>();
|
||||||
|
}
|
||||||
|
get code(): string {
|
||||||
|
return this.data.code;
|
||||||
|
}
|
||||||
|
set code(v: string) {
|
||||||
|
this.data.code = v;
|
||||||
|
}
|
||||||
|
get codeColor(): string {
|
||||||
|
return this.data.codeColor;
|
||||||
|
}
|
||||||
|
set codeColor(v: string) {
|
||||||
|
this.data.codeColor = v;
|
||||||
|
}
|
||||||
|
get codeFontSize(): number {
|
||||||
|
return this.data.codeFontSize;
|
||||||
|
}
|
||||||
|
set codeFontSize(v: number) {
|
||||||
|
this.data.codeFontSize = v;
|
||||||
|
}
|
||||||
|
get trainDirection(): string {
|
||||||
|
return this.data.trainDirection;
|
||||||
|
}
|
||||||
|
set trainDirection(v: string) {
|
||||||
|
this.data.trainDirection = v;
|
||||||
|
}
|
||||||
|
get hasBorder(): boolean {
|
||||||
|
return this.data.hasBorder;
|
||||||
|
}
|
||||||
|
set hasBorder(v: boolean) {
|
||||||
|
this.data.hasBorder = v;
|
||||||
|
}
|
||||||
|
get borderWidth(): number {
|
||||||
|
return this.data.borderWidth;
|
||||||
|
}
|
||||||
|
set borderWidth(v: number) {
|
||||||
|
this.data.borderWidth = v;
|
||||||
|
}
|
||||||
|
get borderColor(): string {
|
||||||
|
return this.data.borderColor;
|
||||||
|
}
|
||||||
|
set borderColor(v: string) {
|
||||||
|
this.data.borderColor = v;
|
||||||
|
}
|
||||||
|
get headColor(): string {
|
||||||
|
return this.data.headColor;
|
||||||
|
}
|
||||||
|
set headColor(v: string) {
|
||||||
|
this.data.headColor = v;
|
||||||
|
}
|
||||||
|
get bodyColor(): string {
|
||||||
|
return this.data.bodyColor;
|
||||||
|
}
|
||||||
|
set bodyColor(v: string) {
|
||||||
|
this.data.bodyColor = v;
|
||||||
|
}
|
||||||
|
get point(): IPointData {
|
||||||
|
return this.data.point;
|
||||||
|
}
|
||||||
|
set point(point: IPointData) {
|
||||||
|
this.data.point = new graphicData.Point({ x: point.x, y: point.y });
|
||||||
|
}
|
||||||
|
clone(): TrainData {
|
||||||
|
return new TrainData(this.data.cloneMessage());
|
||||||
|
}
|
||||||
|
copyFrom(data: TrainData): void {
|
||||||
|
pb_1.Message.copyInto(data.data, this.data);
|
||||||
|
}
|
||||||
|
eq(other: TrainData): boolean {
|
||||||
|
return pb_1.Message.equals(this.data, other.data);
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,19 @@
|
|||||||
import { fromUint8Array, toUint8Array } from 'js-base64';
|
import { fromUint8Array, toUint8Array } from 'js-base64';
|
||||||
import { IPointData, Point } from 'pixi.js';
|
import { IPointData, Point } from 'pixi.js';
|
||||||
import { IscsFan } from 'src/graphics/iscs-fan/IscsFan';
|
import { IscsFan, IscsFanTemplate } from 'src/graphics/iscs-fan/IscsFan';
|
||||||
import { IscsFanDraw } from 'src/graphics/iscs-fan/IscsFanDrawAssistant';
|
import { IscsFanDraw } from 'src/graphics/iscs-fan/IscsFanDrawAssistant';
|
||||||
import { Link } from 'src/graphics/link/Link';
|
import { Link, LinkTemplate } from 'src/graphics/link/Link';
|
||||||
import { LinkDraw } from 'src/graphics/link/LinkDrawAssistant';
|
import { LinkDraw } from 'src/graphics/link/LinkDrawAssistant';
|
||||||
|
import { Rect } from 'src/graphics/rect/Rect';
|
||||||
|
import { RectDraw } from 'src/graphics/rect/RectDrawAssistant';
|
||||||
import { Platform } from 'src/graphics/platform/Platform';
|
import { Platform } from 'src/graphics/platform/Platform';
|
||||||
import { PlatformDraw } from 'src/graphics/platform/PlatformDrawAssistant';
|
import { PlatformDraw } from 'src/graphics/platform/PlatformDrawAssistant';
|
||||||
|
import { Station } from 'src/graphics/station/Station';
|
||||||
|
import { Train } from 'src/graphics/train/Train';
|
||||||
|
import { StationDraw } from 'src/graphics/station/StationDrawAssistant';
|
||||||
|
import { Signal, SignalTemplate } from 'src/graphics/signal/Signal';
|
||||||
|
import { SignalDraw } from 'src/graphics/signal/SignalDrawAssistant';
|
||||||
|
import { TrainDraw } from 'src/graphics/train/TrainDrawAssistant';
|
||||||
import {
|
import {
|
||||||
CombinationKey,
|
CombinationKey,
|
||||||
GraphicApp,
|
GraphicApp,
|
||||||
@ -16,10 +24,15 @@ import {
|
|||||||
} from 'src/jlgraphic';
|
} from 'src/jlgraphic';
|
||||||
import { ContextMenu } from 'src/jlgraphic/ui/ContextMenu';
|
import { ContextMenu } from 'src/jlgraphic/ui/ContextMenu';
|
||||||
import { MenuItemOptions } from 'src/jlgraphic/ui/Menu';
|
import { MenuItemOptions } from 'src/jlgraphic/ui/Menu';
|
||||||
import { IscsFanData } from './graphics/IscsFanInteraction';
|
import { IscsFanData, IscsFanState } from './graphics/IscsFanInteraction';
|
||||||
import { LinkData } from './graphics/LinkInteraction';
|
import { LinkData } from './graphics/LinkInteraction';
|
||||||
|
import { RectData } from './graphics/RectInteraction';
|
||||||
import { PlatformData } from './graphics/PlatformInteraction';
|
import { PlatformData } from './graphics/PlatformInteraction';
|
||||||
|
import { StationData } from './graphics/StationInteraction';
|
||||||
|
import { SignalData } from './graphics/SignalInteraction';
|
||||||
|
import { TrainData } from './graphics/TrainInteraction';
|
||||||
import { graphicData } from './protos/draw_data_storage';
|
import { graphicData } from './protos/draw_data_storage';
|
||||||
|
import { Notify } from 'quasar';
|
||||||
|
|
||||||
export function fromStoragePoint(p: graphicData.Point): Point {
|
export function fromStoragePoint(p: graphicData.Point): Point {
|
||||||
return new Point(p.x, p.y);
|
return new Point(p.x, p.y);
|
||||||
@ -91,16 +104,27 @@ export function initDrawApp(dom: HTMLElement): JlDrawApp {
|
|||||||
const app = drawApp;
|
const app = drawApp;
|
||||||
app.setOptions({
|
app.setOptions({
|
||||||
drawAssistants: [
|
drawAssistants: [
|
||||||
new LinkDraw(app, () => {
|
new LinkDraw(app, new LinkTemplate(new LinkData())),
|
||||||
return new LinkData();
|
new IscsFanDraw(
|
||||||
}),
|
app,
|
||||||
new IscsFanDraw(app, () => {
|
new IscsFanTemplate(new IscsFanData(), new IscsFanState())
|
||||||
return new IscsFanData();
|
),
|
||||||
}),
|
new SignalDraw(app, new SignalTemplate(new SignalData())),
|
||||||
new PlatformDraw(app, () => {
|
new TrainDraw(app, () => {
|
||||||
return new PlatformData();
|
return new TrainData();
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
// isSupportDeletion: (g): boolean => {
|
||||||
|
// if (g.type === Signal.Type) {
|
||||||
|
// Notify.create({
|
||||||
|
// type: 'warning',
|
||||||
|
// message: '信号机不支持删除',
|
||||||
|
// timeout: 1000,
|
||||||
|
// });
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
// return true;
|
||||||
|
// },
|
||||||
});
|
});
|
||||||
|
|
||||||
// 画布右键菜单
|
// 画布右键菜单
|
||||||
@ -129,6 +153,14 @@ export function initDrawApp(dom: HTMLElement): JlDrawApp {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
app.addKeyboardListener(
|
||||||
|
new KeyListener({
|
||||||
|
value: 'KeyR',
|
||||||
|
onPress: () => {
|
||||||
|
app.interactionPlugin(Rect.Type).resume();
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
app.addKeyboardListener(
|
app.addKeyboardListener(
|
||||||
new KeyListener({
|
new KeyListener({
|
||||||
value: 'KeyF',
|
value: 'KeyF',
|
||||||
@ -145,13 +177,37 @@ export function initDrawApp(dom: HTMLElement): JlDrawApp {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
app.addKeyboardListener(
|
||||||
|
new KeyListener({
|
||||||
|
value: 'KeyO',
|
||||||
|
onPress: () => {
|
||||||
|
app.interactionPlugin(Station.Type).resume();
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
app.addKeyboardListener(
|
||||||
|
new KeyListener({
|
||||||
|
value: 'KeyH',
|
||||||
|
onPress: () => {
|
||||||
|
app.interactionPlugin(Signal.Type).resume();
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
app.addKeyboardListener(
|
||||||
|
new KeyListener({
|
||||||
|
value: 'KeyT',
|
||||||
|
onPress: () => {
|
||||||
|
app.interactionPlugin(Train.Type).resume();
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
app.addKeyboardListener(
|
app.addKeyboardListener(
|
||||||
new KeyListener({
|
new KeyListener({
|
||||||
value: '1',
|
value: '1',
|
||||||
onPress: () => {
|
onPress: () => {
|
||||||
app.queryStore.queryByType<IscsFan>(IscsFan.Type).forEach((fan) => {
|
app.queryStore.queryByType<IscsFan>(IscsFan.Type).forEach((fan) => {
|
||||||
fan.__state = fan.__state + 1;
|
fan.states.state = fan.states.state + 1;
|
||||||
fan.__state = fan.__state % 5;
|
fan.states.state = fan.states.state % 5;
|
||||||
fan.repaint();
|
fan.repaint();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -183,12 +239,24 @@ export function saveDrawDatas(app: JlDrawApp) {
|
|||||||
if (Link.Type === g.type) {
|
if (Link.Type === g.type) {
|
||||||
const linkData = (g as Link).saveData();
|
const linkData = (g as Link).saveData();
|
||||||
storage.links.push((linkData as LinkData).data);
|
storage.links.push((linkData as LinkData).data);
|
||||||
|
} else if (Rect.Type === g.type) {
|
||||||
|
const rectData = (g as Rect).saveData();
|
||||||
|
storage.Rects.push((rectData as RectData).data);
|
||||||
} else if (IscsFan.Type === g.type) {
|
} else if (IscsFan.Type === g.type) {
|
||||||
const IscsFanData = (g as IscsFan).saveData();
|
const IscsFanData = (g as IscsFan).saveData();
|
||||||
storage.iscsFans.push((IscsFanData as IscsFanData).data);
|
storage.iscsFans.push((IscsFanData as IscsFanData).data);
|
||||||
} else if (Platform.Type === g.type) {
|
} else if (Platform.Type === g.type) {
|
||||||
const platformData = (g as Platform).saveData();
|
const platformData = (g as Platform).saveData();
|
||||||
storage.Platforms.push((platformData as PlatformData).data);
|
storage.Platforms.push((platformData as PlatformData).data);
|
||||||
|
} else if (Station.Type === g.type) {
|
||||||
|
const stationData = (g as Station).saveData();
|
||||||
|
storage.stations.push((stationData as StationData).data);
|
||||||
|
} else if (Signal.Type === g.type) {
|
||||||
|
const signalData = (g as Signal).saveData();
|
||||||
|
storage.signals.push((signalData as SignalData).data);
|
||||||
|
} else if (Train.Type === g.type) {
|
||||||
|
const trainData = (g as Train).saveData();
|
||||||
|
storage.train.push((trainData as TrainData).data);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const base64 = fromUint8Array(storage.serialize());
|
const base64 = fromUint8Array(storage.serialize());
|
||||||
@ -210,12 +278,27 @@ export function loadDrawDatas(app: GraphicApp) {
|
|||||||
storage.links.forEach((link) => {
|
storage.links.forEach((link) => {
|
||||||
datas.push(new LinkData(link));
|
datas.push(new LinkData(link));
|
||||||
});
|
});
|
||||||
|
storage.Rects.forEach((rect) => {
|
||||||
|
datas.push(new RectData(rect));
|
||||||
|
});
|
||||||
storage.iscsFans.forEach((fan) => {
|
storage.iscsFans.forEach((fan) => {
|
||||||
datas.push(new IscsFanData(fan));
|
datas.push(new IscsFanData(fan));
|
||||||
});
|
});
|
||||||
storage.Platforms.forEach((platform) => {
|
storage.Platforms.forEach((platform) => {
|
||||||
datas.push(new PlatformData(platform));
|
datas.push(new PlatformData(platform));
|
||||||
});
|
});
|
||||||
|
storage.stations.forEach((station) => {
|
||||||
|
datas.push(new StationData(station));
|
||||||
|
});
|
||||||
|
storage.signals.forEach((data) => {
|
||||||
|
datas.push(new SignalData(data));
|
||||||
|
});
|
||||||
|
storage.stations.forEach((signal) => {
|
||||||
|
datas.push(new StationData(signal));
|
||||||
|
});
|
||||||
|
storage.stations.forEach((train) => {
|
||||||
|
datas.push(new StationData(train));
|
||||||
|
});
|
||||||
app.loadGraphic(datas);
|
app.loadGraphic(datas);
|
||||||
} else {
|
} else {
|
||||||
app.loadGraphic([]);
|
app.loadGraphic([]);
|
||||||
|
File diff suppressed because it is too large
Load Diff
191
src/examples/app/protos/graphic_states.ts
Normal file
191
src/examples/app/protos/graphic_states.ts
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
/**
|
||||||
|
* Generated by the protoc-gen-ts. DO NOT EDIT!
|
||||||
|
* compiler version: 4.22.2
|
||||||
|
* source: graphic_states.proto
|
||||||
|
* git: https://github.com/thesayyn/protoc-gen-ts */
|
||||||
|
import * as pb_1 from "google-protobuf";
|
||||||
|
export namespace graphicStates {
|
||||||
|
export class CommonState extends pb_1.Message {
|
||||||
|
#one_of_decls: number[][] = [];
|
||||||
|
constructor(data?: any[] | {
|
||||||
|
code?: string;
|
||||||
|
graphicType?: string;
|
||||||
|
}) {
|
||||||
|
super();
|
||||||
|
pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [], this.#one_of_decls);
|
||||||
|
if (!Array.isArray(data) && typeof data == "object") {
|
||||||
|
if ("code" in data && data.code != undefined) {
|
||||||
|
this.code = data.code;
|
||||||
|
}
|
||||||
|
if ("graphicType" in data && data.graphicType != undefined) {
|
||||||
|
this.graphicType = data.graphicType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
get code() {
|
||||||
|
return pb_1.Message.getFieldWithDefault(this, 1, "") as string;
|
||||||
|
}
|
||||||
|
set code(value: string) {
|
||||||
|
pb_1.Message.setField(this, 1, value);
|
||||||
|
}
|
||||||
|
get graphicType() {
|
||||||
|
return pb_1.Message.getFieldWithDefault(this, 2, "") as string;
|
||||||
|
}
|
||||||
|
set graphicType(value: string) {
|
||||||
|
pb_1.Message.setField(this, 2, value);
|
||||||
|
}
|
||||||
|
static fromObject(data: {
|
||||||
|
code?: string;
|
||||||
|
graphicType?: string;
|
||||||
|
}): CommonState {
|
||||||
|
const message = new CommonState({});
|
||||||
|
if (data.code != null) {
|
||||||
|
message.code = data.code;
|
||||||
|
}
|
||||||
|
if (data.graphicType != null) {
|
||||||
|
message.graphicType = data.graphicType;
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
toObject() {
|
||||||
|
const data: {
|
||||||
|
code?: string;
|
||||||
|
graphicType?: string;
|
||||||
|
} = {};
|
||||||
|
if (this.code != null) {
|
||||||
|
data.code = this.code;
|
||||||
|
}
|
||||||
|
if (this.graphicType != null) {
|
||||||
|
data.graphicType = this.graphicType;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
serialize(): Uint8Array;
|
||||||
|
serialize(w: pb_1.BinaryWriter): void;
|
||||||
|
serialize(w?: pb_1.BinaryWriter): Uint8Array | void {
|
||||||
|
const writer = w || new pb_1.BinaryWriter();
|
||||||
|
if (this.code.length)
|
||||||
|
writer.writeString(1, this.code);
|
||||||
|
if (this.graphicType.length)
|
||||||
|
writer.writeString(2, this.graphicType);
|
||||||
|
if (!w)
|
||||||
|
return writer.getResultBuffer();
|
||||||
|
}
|
||||||
|
static deserialize(bytes: Uint8Array | pb_1.BinaryReader): CommonState {
|
||||||
|
const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new CommonState();
|
||||||
|
while (reader.nextField()) {
|
||||||
|
if (reader.isEndGroup())
|
||||||
|
break;
|
||||||
|
switch (reader.getFieldNumber()) {
|
||||||
|
case 1:
|
||||||
|
message.code = reader.readString();
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
message.graphicType = reader.readString();
|
||||||
|
break;
|
||||||
|
default: reader.skipField();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
serializeBinary(): Uint8Array {
|
||||||
|
return this.serialize();
|
||||||
|
}
|
||||||
|
static deserializeBinary(bytes: Uint8Array): CommonState {
|
||||||
|
return CommonState.deserialize(bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export class IscsFan extends pb_1.Message {
|
||||||
|
#one_of_decls: number[][] = [];
|
||||||
|
constructor(data?: any[] | {
|
||||||
|
common?: CommonState;
|
||||||
|
state?: number;
|
||||||
|
}) {
|
||||||
|
super();
|
||||||
|
pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [], this.#one_of_decls);
|
||||||
|
if (!Array.isArray(data) && typeof data == "object") {
|
||||||
|
if ("common" in data && data.common != undefined) {
|
||||||
|
this.common = data.common;
|
||||||
|
}
|
||||||
|
if ("state" in data && data.state != undefined) {
|
||||||
|
this.state = data.state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
get common() {
|
||||||
|
return pb_1.Message.getWrapperField(this, CommonState, 1) as CommonState;
|
||||||
|
}
|
||||||
|
set common(value: CommonState) {
|
||||||
|
pb_1.Message.setWrapperField(this, 1, value);
|
||||||
|
}
|
||||||
|
get has_common() {
|
||||||
|
return pb_1.Message.getField(this, 1) != null;
|
||||||
|
}
|
||||||
|
get state() {
|
||||||
|
return pb_1.Message.getFieldWithDefault(this, 2, 0) as number;
|
||||||
|
}
|
||||||
|
set state(value: number) {
|
||||||
|
pb_1.Message.setField(this, 2, value);
|
||||||
|
}
|
||||||
|
static fromObject(data: {
|
||||||
|
common?: ReturnType<typeof CommonState.prototype.toObject>;
|
||||||
|
state?: number;
|
||||||
|
}): IscsFan {
|
||||||
|
const message = new IscsFan({});
|
||||||
|
if (data.common != null) {
|
||||||
|
message.common = CommonState.fromObject(data.common);
|
||||||
|
}
|
||||||
|
if (data.state != null) {
|
||||||
|
message.state = data.state;
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
toObject() {
|
||||||
|
const data: {
|
||||||
|
common?: ReturnType<typeof CommonState.prototype.toObject>;
|
||||||
|
state?: number;
|
||||||
|
} = {};
|
||||||
|
if (this.common != null) {
|
||||||
|
data.common = this.common.toObject();
|
||||||
|
}
|
||||||
|
if (this.state != null) {
|
||||||
|
data.state = this.state;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
serialize(): Uint8Array;
|
||||||
|
serialize(w: pb_1.BinaryWriter): void;
|
||||||
|
serialize(w?: pb_1.BinaryWriter): Uint8Array | void {
|
||||||
|
const writer = w || new pb_1.BinaryWriter();
|
||||||
|
if (this.has_common)
|
||||||
|
writer.writeMessage(1, this.common, () => this.common.serialize(writer));
|
||||||
|
if (this.state != 0)
|
||||||
|
writer.writeInt32(2, this.state);
|
||||||
|
if (!w)
|
||||||
|
return writer.getResultBuffer();
|
||||||
|
}
|
||||||
|
static deserialize(bytes: Uint8Array | pb_1.BinaryReader): IscsFan {
|
||||||
|
const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new IscsFan();
|
||||||
|
while (reader.nextField()) {
|
||||||
|
if (reader.isEndGroup())
|
||||||
|
break;
|
||||||
|
switch (reader.getFieldNumber()) {
|
||||||
|
case 1:
|
||||||
|
reader.readMessage(message.common, () => message.common = CommonState.deserialize(reader));
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
message.state = reader.readInt32();
|
||||||
|
break;
|
||||||
|
default: reader.skipField();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
serializeBinary(): Uint8Array {
|
||||||
|
return this.serialize();
|
||||||
|
}
|
||||||
|
static deserializeBinary(bytes: Uint8Array): IscsFan {
|
||||||
|
return IscsFan.deserialize(bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
51
src/graphics/CommonGraphics.ts
Normal file
51
src/graphics/CommonGraphics.ts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import { Graphics } from 'pixi.js';
|
||||||
|
export function drawArrow(
|
||||||
|
polygon: Graphics,
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
length: number,
|
||||||
|
radius: number,
|
||||||
|
lineWidth: number
|
||||||
|
) {
|
||||||
|
const trianglAcme = { x, y };
|
||||||
|
const triangleP1 = {
|
||||||
|
x: x - radius - Math.sin(Math.PI / 6),
|
||||||
|
y: y + Math.cos(Math.PI / 6) * radius,
|
||||||
|
};
|
||||||
|
const triangleP2 = {
|
||||||
|
x: x - radius - Math.sin(Math.PI / 6),
|
||||||
|
y: y - Math.cos(Math.PI / 6) * radius,
|
||||||
|
};
|
||||||
|
const lineP1 = {
|
||||||
|
x: x - radius - Math.sin(Math.PI / 6),
|
||||||
|
y: y + lineWidth / 2,
|
||||||
|
};
|
||||||
|
const lineP2 = {
|
||||||
|
x: x - length,
|
||||||
|
y: y + lineWidth / 2,
|
||||||
|
};
|
||||||
|
const lineP3 = {
|
||||||
|
x: x - length,
|
||||||
|
y: y - lineWidth / 2,
|
||||||
|
};
|
||||||
|
const lineP4 = {
|
||||||
|
x: x - radius - Math.sin(Math.PI / 6),
|
||||||
|
y: y - lineWidth / 2,
|
||||||
|
};
|
||||||
|
polygon.drawPolygon(
|
||||||
|
trianglAcme.x,
|
||||||
|
trianglAcme.y,
|
||||||
|
triangleP1.x,
|
||||||
|
triangleP1.y,
|
||||||
|
lineP1.x,
|
||||||
|
lineP1.y,
|
||||||
|
lineP2.x,
|
||||||
|
lineP2.y,
|
||||||
|
lineP3.x,
|
||||||
|
lineP3.y,
|
||||||
|
lineP4.x,
|
||||||
|
lineP4.y,
|
||||||
|
triangleP2.x,
|
||||||
|
triangleP2.y
|
||||||
|
);
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
GraphicAnimation,
|
GraphicAnimation,
|
||||||
GraphicData,
|
GraphicData,
|
||||||
|
GraphicState,
|
||||||
JlGraphic,
|
JlGraphic,
|
||||||
JlGraphicTemplate,
|
JlGraphicTemplate,
|
||||||
} from 'src/jlgraphic';
|
} from 'src/jlgraphic';
|
||||||
@ -23,12 +24,16 @@ export interface IIscsFanData extends GraphicData {
|
|||||||
set code(v: string);
|
set code(v: string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IIscsFanState extends GraphicState {
|
||||||
|
get state(): number;
|
||||||
|
set state(v: number);
|
||||||
|
}
|
||||||
|
|
||||||
export class IscsFan extends JlGraphic {
|
export class IscsFan extends JlGraphic {
|
||||||
static Type = 'IscsFan';
|
static Type = 'IscsFan';
|
||||||
_border: Sprite;
|
_border: Sprite;
|
||||||
_fan: Sprite;
|
_fan: Sprite;
|
||||||
fanTextures: FanTextures;
|
fanTextures: FanTextures;
|
||||||
__state = 0;
|
|
||||||
|
|
||||||
constructor(fanTextures: FanTextures) {
|
constructor(fanTextures: FanTextures) {
|
||||||
super(IscsFan.Type);
|
super(IscsFan.Type);
|
||||||
@ -42,28 +47,31 @@ export class IscsFan extends JlGraphic {
|
|||||||
this.addChild(this._border);
|
this.addChild(this._border);
|
||||||
this.addChild(this._fan);
|
this.addChild(this._fan);
|
||||||
}
|
}
|
||||||
|
get states(): IIscsFanState {
|
||||||
|
return this.getStates<IIscsFanState>();
|
||||||
|
}
|
||||||
doRepaint(): void {
|
doRepaint(): void {
|
||||||
if (this.__state === 0) {
|
if (this.states.state === 0) {
|
||||||
// 停止
|
// 停止
|
||||||
this.stopFanRun();
|
this.stopFanRun();
|
||||||
this._fan.rotation = 0;
|
this._fan.rotation = 0;
|
||||||
this._fan.texture = this.fanTextures.gray;
|
this._fan.texture = this.fanTextures.gray;
|
||||||
} else if (this.__state === 1) {
|
} else if (this.states.state === 1) {
|
||||||
// 正常运行
|
// 正常运行
|
||||||
this._fan.texture = this.fanTextures.green;
|
this._fan.texture = this.fanTextures.green;
|
||||||
// 动画
|
// 动画
|
||||||
this.initFanRun();
|
this.initFanRun();
|
||||||
} else if (this.__state === 2) {
|
} else if (this.states.state === 2) {
|
||||||
// 报警运行
|
// 报警运行
|
||||||
this._fan.texture = this.fanTextures.yellow;
|
this._fan.texture = this.fanTextures.yellow;
|
||||||
// 动画
|
// 动画
|
||||||
this.initFanRun();
|
this.initFanRun();
|
||||||
} else if (this.__state === 3) {
|
} else if (this.states.state === 3) {
|
||||||
// 故障
|
// 故障
|
||||||
this.stopFanRun();
|
this.stopFanRun();
|
||||||
this._fan.rotation = 0;
|
this._fan.rotation = 0;
|
||||||
this._fan.texture = this.fanTextures.red;
|
this._fan.texture = this.fanTextures.red;
|
||||||
} else if (this.__state === 4) {
|
} else if (this.states.state === 4) {
|
||||||
// 通信故障
|
// 通信故障
|
||||||
// 停止
|
// 停止
|
||||||
this.stopFanRun();
|
this.stopFanRun();
|
||||||
@ -99,12 +107,18 @@ export class IscsFan extends JlGraphic {
|
|||||||
|
|
||||||
export class IscsFanTemplate extends JlGraphicTemplate<IscsFan> {
|
export class IscsFanTemplate extends JlGraphicTemplate<IscsFan> {
|
||||||
fanTextures?: FanTextures;
|
fanTextures?: FanTextures;
|
||||||
constructor() {
|
constructor(dataTemplate: IIscsFanData, stateTemplate: IIscsFanState) {
|
||||||
super(IscsFan.Type);
|
super(IscsFan.Type, {
|
||||||
|
dataTemplate,
|
||||||
|
stateTemplate,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
new(): IscsFan {
|
new(): IscsFan {
|
||||||
if (this.fanTextures) {
|
if (this.fanTextures) {
|
||||||
return new IscsFan(this.fanTextures);
|
const g = new IscsFan(this.fanTextures);
|
||||||
|
g.loadData(this.datas);
|
||||||
|
g.loadState(this.states);
|
||||||
|
return g;
|
||||||
}
|
}
|
||||||
throw new Error('资源未加载/加载失败');
|
throw new Error('资源未加载/加载失败');
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
import { FederatedMouseEvent, Point } from 'pixi.js';
|
import { FederatedMouseEvent, Point } from 'pixi.js';
|
||||||
import {
|
import {
|
||||||
|
AbsorbableLine,
|
||||||
|
AbsorbablePosition,
|
||||||
GraphicDrawAssistant,
|
GraphicDrawAssistant,
|
||||||
GraphicInteractionPlugin,
|
GraphicInteractionPlugin,
|
||||||
|
GraphicTransformEvent,
|
||||||
JlDrawApp,
|
JlDrawApp,
|
||||||
JlGraphic,
|
JlGraphic,
|
||||||
} from 'src/jlgraphic';
|
} from 'src/jlgraphic';
|
||||||
@ -13,9 +16,8 @@ export class IscsFanDraw extends GraphicDrawAssistant<
|
|||||||
> {
|
> {
|
||||||
_iscsFan: IscsFan | null = null;
|
_iscsFan: IscsFan | null = null;
|
||||||
|
|
||||||
constructor(app: JlDrawApp, createData: () => IIscsFanData) {
|
constructor(app: JlDrawApp, template: IscsFanTemplate) {
|
||||||
const template = new IscsFanTemplate();
|
super(app, template, IscsFan.Type, '风机');
|
||||||
super(app, template, createData, IscsFan.Type, '风机');
|
|
||||||
IscsFanInteraction.init(app);
|
IscsFanInteraction.init(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,12 +68,42 @@ export class IscsFanInteraction extends GraphicInteractionPlugin<IscsFan> {
|
|||||||
bind(g: IscsFan): void {
|
bind(g: IscsFan): void {
|
||||||
g.eventMode = 'static';
|
g.eventMode = 'static';
|
||||||
g.cursor = 'pointer';
|
g.cursor = 'pointer';
|
||||||
g.scalable = true;
|
// g.scalable = true;
|
||||||
g.rotatable = true;
|
g.rotatable = true;
|
||||||
|
g.on('drag-start', () => {
|
||||||
|
console.log('风机拖拽');
|
||||||
|
});
|
||||||
|
g.on('transformstart', (e: GraphicTransformEvent) => {
|
||||||
|
if (e.isShift()) {
|
||||||
|
g.getGraphicApp().setOptions({
|
||||||
|
absorbablePositions: buildAbsorbablePositions(g),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
unbind(g: IscsFan): void {
|
unbind(g: IscsFan): void {
|
||||||
g.eventMode = 'none';
|
g.eventMode = 'none';
|
||||||
g.scalable = false;
|
// g.scalable = false;
|
||||||
g.rotatable = false;
|
g.rotatable = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildAbsorbablePositions(
|
||||||
|
g: IscsFan
|
||||||
|
): AbsorbablePosition[] | undefined {
|
||||||
|
const app = g.getGraphicApp();
|
||||||
|
const canvas = app.canvas;
|
||||||
|
const store = app.queryStore;
|
||||||
|
const aps: AbsorbablePosition[] = [];
|
||||||
|
store.queryByType(IscsFan.Type).forEach((fan) => {
|
||||||
|
if (fan.id === g.id) return;
|
||||||
|
const p = fan.position;
|
||||||
|
aps.push(
|
||||||
|
new AbsorbableLine(new Point(0, p.y), new Point(canvas.width, p.y))
|
||||||
|
);
|
||||||
|
aps.push(
|
||||||
|
new AbsorbableLine(new Point(p.x, 0), new Point(p.x, canvas.height))
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return aps;
|
||||||
|
}
|
||||||
|
@ -89,8 +89,10 @@ export class LinkTemplate extends JlGraphicTemplate<Link> {
|
|||||||
lineWidth: number;
|
lineWidth: number;
|
||||||
lineColor: string;
|
lineColor: string;
|
||||||
segmentsCount: number;
|
segmentsCount: number;
|
||||||
constructor() {
|
constructor(dataTemplate: ILinkData) {
|
||||||
super(Link.Type);
|
super(Link.Type, {
|
||||||
|
dataTemplate,
|
||||||
|
});
|
||||||
this.lineWidth = 2;
|
this.lineWidth = 2;
|
||||||
this.lineColor = '#000000';
|
this.lineColor = '#000000';
|
||||||
this.curve = false;
|
this.curve = false;
|
||||||
|
@ -30,12 +30,13 @@ import {
|
|||||||
ILineGraphic,
|
ILineGraphic,
|
||||||
PolylineEditPlugin,
|
PolylineEditPlugin,
|
||||||
addWayPoint,
|
addWayPoint,
|
||||||
addWaypointConfig,
|
|
||||||
clearWayPoint,
|
clearWayPoint,
|
||||||
clearWaypointsConfig,
|
|
||||||
getWaypointRangeIndex,
|
getWaypointRangeIndex,
|
||||||
|
removeBezierWayPoint,
|
||||||
|
removeLineWayPoint,
|
||||||
} from 'src/jlgraphic/plugins/GraphicEditPlugin';
|
} from 'src/jlgraphic/plugins/GraphicEditPlugin';
|
||||||
import { ContextMenu } from 'src/jlgraphic/ui/ContextMenu';
|
import { ContextMenu } from 'src/jlgraphic/ui/ContextMenu';
|
||||||
|
import { MenuItemOptions, MenuOptions } from 'src/jlgraphic/ui/Menu';
|
||||||
import { ILinkData, Link, LinkTemplate } from './Link';
|
import { ILinkData, Link, LinkTemplate } from './Link';
|
||||||
|
|
||||||
export interface ILinkDrawOptions {
|
export interface ILinkDrawOptions {
|
||||||
@ -67,8 +68,8 @@ export class LinkDraw extends GraphicDrawAssistant<LinkTemplate, ILinkData> {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
constructor(app: JlDrawApp, createData: () => ILinkData) {
|
constructor(app: JlDrawApp, template: LinkTemplate) {
|
||||||
super(app, new LinkTemplate(), createData, Link.Type, '轨道Link');
|
super(app, template, Link.Type, '轨道Link');
|
||||||
this.container.addChild(this.graphic);
|
this.container.addChild(this.graphic);
|
||||||
this.graphicTemplate.curve = true;
|
this.graphicTemplate.curve = true;
|
||||||
|
|
||||||
@ -251,7 +252,7 @@ function buildAbsorbablePositions(link: Link): AbsorbablePosition[] {
|
|||||||
* @param dp
|
* @param dp
|
||||||
* @param index
|
* @param index
|
||||||
*/
|
*/
|
||||||
function onEditPointCreate(
|
function onPolylineEditPointCreate(
|
||||||
g: ILineGraphic,
|
g: ILineGraphic,
|
||||||
dp: DraggablePoint,
|
dp: DraggablePoint,
|
||||||
index: number
|
index: number
|
||||||
@ -267,8 +268,78 @@ function onEditPointCreate(
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
dp.on('rightclick', (e: FederatedMouseEvent) => {
|
||||||
|
// dp.getGraphicApp().openContextMenu(EditPointContextMenu.DefaultMenu);
|
||||||
|
// 路径中的点
|
||||||
|
const app = dp.getGraphicApp();
|
||||||
|
app.registerMenu(EditPointContextMenu);
|
||||||
|
removeWaypointConfig.handler = () => {
|
||||||
|
removeLineWayPoint(link, index);
|
||||||
|
};
|
||||||
|
clearWaypointsConfig.handler = () => {
|
||||||
|
clearWayPoint(link, false);
|
||||||
|
};
|
||||||
|
EditPointContextMenu.open(e.global);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onCurveEditPointCreate(
|
||||||
|
g: ILineGraphic,
|
||||||
|
dp: DraggablePoint,
|
||||||
|
index: number
|
||||||
|
): void {
|
||||||
|
const link = g as Link;
|
||||||
|
if (index === 0 || index == link.datas.points.length - 1) {
|
||||||
|
// 端点
|
||||||
|
dp.on('transformstart', (e: GraphicTransformEvent) => {
|
||||||
|
if (e.isShift()) {
|
||||||
|
link.getGraphicApp().setOptions({
|
||||||
|
absorbablePositions: buildAbsorbablePositions(link),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const c = index % 3;
|
||||||
|
dp.on('rightclick', (e: FederatedMouseEvent) => {
|
||||||
|
// dp.getGraphicApp().openContextMenu(EditPointContextMenu.DefaultMenu);
|
||||||
|
if (c === 0) {
|
||||||
|
// 路径中的点
|
||||||
|
const app = dp.getGraphicApp();
|
||||||
|
app.registerMenu(EditPointContextMenu);
|
||||||
|
removeWaypointConfig.handler = () => {
|
||||||
|
removeBezierWayPoint(link, index);
|
||||||
|
};
|
||||||
|
clearWaypointsConfig.handler = () => {
|
||||||
|
clearWayPoint(link, true);
|
||||||
|
};
|
||||||
|
EditPointContextMenu.open(e.global);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const addWaypointConfig: MenuItemOptions = {
|
||||||
|
name: '添加路径点',
|
||||||
|
};
|
||||||
|
export const addWaySegmentingConfig: MenuItemOptions = {
|
||||||
|
name: '细分',
|
||||||
|
};
|
||||||
|
export const removeWaypointConfig: MenuItemOptions = {
|
||||||
|
name: '移除路径点',
|
||||||
|
};
|
||||||
|
export const clearWaypointsConfig: MenuItemOptions = {
|
||||||
|
name: '清除所有路径点',
|
||||||
|
};
|
||||||
|
const menuOptions: MenuOptions = {
|
||||||
|
name: '图形编辑点菜单',
|
||||||
|
groups: [
|
||||||
|
{
|
||||||
|
items: [removeWaypointConfig, clearWaypointsConfig],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const EditPointContextMenu = ContextMenu.init(menuOptions);
|
||||||
|
|
||||||
const LinkEditMenu: ContextMenu = ContextMenu.init({
|
const LinkEditMenu: ContextMenu = ContextMenu.init({
|
||||||
name: '轨道编辑菜单',
|
name: '轨道编辑菜单',
|
||||||
groups: [
|
groups: [
|
||||||
@ -311,14 +382,17 @@ export class LinkPointsEditPlugin extends GraphicInteractionPlugin<Link> {
|
|||||||
const target = e.target as DisplayObject;
|
const target = e.target as DisplayObject;
|
||||||
const link = target.getGraphic() as Link;
|
const link = target.getGraphic() as Link;
|
||||||
this.app.updateSelected(link);
|
this.app.updateSelected(link);
|
||||||
|
const p = link.getGraphicApp().toCanvasCoordinates(e.global);
|
||||||
|
console.log('右键坐标', p);
|
||||||
|
|
||||||
addWaypointConfig.handler = () => {
|
addWaypointConfig.handler = () => {
|
||||||
const linePoints = link.linePoints;
|
const linePoints = link.linePoints;
|
||||||
const p = link.screenToLocalPoint(e.global);
|
console.log('添加路径点', linePoints, p);
|
||||||
const { start, end } = getWaypointRangeIndex(
|
const { start, end } = getWaypointRangeIndex(
|
||||||
linePoints,
|
linePoints,
|
||||||
link.datas.curve,
|
link.datas.curve,
|
||||||
p
|
p,
|
||||||
|
link.datas.lineWidth
|
||||||
);
|
);
|
||||||
addWayPoint(link, link.datas.curve, start, end, p);
|
addWayPoint(link, link.datas.curve, start, end, p);
|
||||||
};
|
};
|
||||||
@ -338,7 +412,7 @@ export class LinkPointsEditPlugin extends GraphicInteractionPlugin<Link> {
|
|||||||
);
|
);
|
||||||
if (!lep) {
|
if (!lep) {
|
||||||
lep = new BezierCurveEditPlugin(link, {
|
lep = new BezierCurveEditPlugin(link, {
|
||||||
onEditPointCreate,
|
onEditPointCreate: onCurveEditPointCreate,
|
||||||
});
|
});
|
||||||
link.addAssistantAppend(lep);
|
link.addAssistantAppend(lep);
|
||||||
}
|
}
|
||||||
@ -348,7 +422,9 @@ export class LinkPointsEditPlugin extends GraphicInteractionPlugin<Link> {
|
|||||||
PolylineEditPlugin.Name
|
PolylineEditPlugin.Name
|
||||||
);
|
);
|
||||||
if (!lep) {
|
if (!lep) {
|
||||||
lep = new PolylineEditPlugin(link, { onEditPointCreate });
|
lep = new PolylineEditPlugin(link, {
|
||||||
|
onEditPointCreate: onPolylineEditPointCreate,
|
||||||
|
});
|
||||||
link.addAssistantAppend(lep);
|
link.addAssistantAppend(lep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ import {
|
|||||||
GraphicData,
|
GraphicData,
|
||||||
JlGraphic,
|
JlGraphic,
|
||||||
JlGraphicTemplate,
|
JlGraphicTemplate,
|
||||||
|
VectorText,
|
||||||
getRectangleCenter,
|
getRectangleCenter,
|
||||||
} from 'src/jlgraphic';
|
} from 'src/jlgraphic';
|
||||||
|
|
||||||
@ -11,6 +12,8 @@ export interface IPlatformData extends GraphicData {
|
|||||||
set code(v: string);
|
set code(v: string);
|
||||||
get hasdoor(): boolean; // 是否有屏蔽门
|
get hasdoor(): boolean; // 是否有屏蔽门
|
||||||
set hasdoor(v: boolean);
|
set hasdoor(v: boolean);
|
||||||
|
get trainDirection(): string; // 行驶方向--屏蔽门上下
|
||||||
|
set trainDirection(v: string);
|
||||||
get lineWidth(): number; // 线宽
|
get lineWidth(): number; // 线宽
|
||||||
set lineWidth(v: number);
|
set lineWidth(v: number);
|
||||||
get lineColor(): string; // 站台线色
|
get lineColor(): string; // 站台线色
|
||||||
@ -28,43 +31,90 @@ export interface IPlatformData extends GraphicData {
|
|||||||
eq(other: IPlatformData): boolean;
|
eq(other: IPlatformData): boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//站台颜色
|
||||||
|
export enum PlatformColorEnum {
|
||||||
|
blue = '0x0fe81f', //站台的颜色
|
||||||
|
lightBlue = '0x55d15d',
|
||||||
|
yellow = '0xfbff00',
|
||||||
|
white = '0xffffff',
|
||||||
|
lozengeRed = '0xff0000', //站台旁的菱形图标
|
||||||
|
whiteNumbers = '0xffffff', //站台旁白色数字
|
||||||
|
HCharYellow = '0xfbff00', //站台旁的H字符
|
||||||
|
HCharWhite = '0xffffff',
|
||||||
|
HCharRed = '0xff0000',
|
||||||
|
doorBlue = '0x008000', //屏蔽门的颜色
|
||||||
|
doorRed = '0xff0000',
|
||||||
|
}
|
||||||
|
|
||||||
|
const platformConsts = {
|
||||||
|
width: 60,
|
||||||
|
height: 20,
|
||||||
|
lineWidth: 3,
|
||||||
|
besideFontSize: 12,
|
||||||
|
doorOpenSpacing: 5,
|
||||||
|
doorPlatformSpacing: 10,
|
||||||
|
besideSpacing: 10,
|
||||||
|
};
|
||||||
|
|
||||||
export class Platform extends JlGraphic {
|
export class Platform extends JlGraphic {
|
||||||
static Type = 'Platform';
|
static Type = 'Platform';
|
||||||
|
|
||||||
platformGraphic: Graphics;
|
platformGraphic: Graphics;
|
||||||
doorGraphic: Graphics;
|
doorGraphic: Graphics;
|
||||||
|
doorCloseGraphic: Graphics;
|
||||||
|
besideGraphic: Graphics;
|
||||||
|
codeGraph: VectorText = new VectorText(''); //站台旁数字、字符
|
||||||
constructor() {
|
constructor() {
|
||||||
super(Platform.Type);
|
super(Platform.Type);
|
||||||
this.platformGraphic = new Graphics();
|
this.platformGraphic = new Graphics();
|
||||||
this.doorGraphic = new Graphics();
|
this.doorGraphic = new Graphics();
|
||||||
|
this.doorCloseGraphic = new Graphics();
|
||||||
|
this.besideGraphic = new Graphics();
|
||||||
this.addChild(this.platformGraphic);
|
this.addChild(this.platformGraphic);
|
||||||
this.addChild(this.doorGraphic);
|
this.addChild(this.doorGraphic);
|
||||||
|
this.addChild(this.doorCloseGraphic);
|
||||||
|
this.addChild(this.besideGraphic);
|
||||||
|
this.addChild(this.codeGraph);
|
||||||
|
this.codeGraph.setVectorFontSize(platformConsts.besideFontSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
get datas(): IPlatformData {
|
get datas(): IPlatformData {
|
||||||
return this.getDatas<IPlatformData>();
|
return this.getDatas<IPlatformData>();
|
||||||
}
|
}
|
||||||
doRepaint(): void {
|
doRepaint(): void {
|
||||||
|
const width = this.datas.width;
|
||||||
|
const height = this.datas.height;
|
||||||
//屏蔽门
|
//屏蔽门
|
||||||
const doorGraphic = this.doorGraphic;
|
const doorGraphic = this.doorGraphic;
|
||||||
|
const doorCloseGraphic = this.doorCloseGraphic;
|
||||||
doorGraphic.clear();
|
doorGraphic.clear();
|
||||||
|
doorCloseGraphic.clear();
|
||||||
if (this.datas.hasdoor) {
|
if (this.datas.hasdoor) {
|
||||||
doorGraphic.clear();
|
|
||||||
doorGraphic.lineStyle(
|
doorGraphic.lineStyle(
|
||||||
this.datas.lineWidth,
|
this.datas.lineWidth,
|
||||||
new Color(this.datas.lineColorDoor)
|
new Color(this.datas.lineColorDoor)
|
||||||
);
|
);
|
||||||
const width = this.datas.width;
|
doorGraphic.moveTo(-width / 2 - this.datas.lineWidth / 2, 0);
|
||||||
const height = this.datas.height;
|
doorGraphic.lineTo(-platformConsts.doorOpenSpacing, 0);
|
||||||
doorGraphic.moveTo(
|
doorGraphic.moveTo(platformConsts.doorOpenSpacing, 0);
|
||||||
-width / 2 - this.datas.lineWidth / 2,
|
doorGraphic.lineTo(width / 2 + this.datas.lineWidth / 2, 0);
|
||||||
-height / 2 - 10
|
//屏蔽门闭合
|
||||||
|
doorCloseGraphic.lineStyle(
|
||||||
|
this.datas.lineWidth,
|
||||||
|
new Color(this.datas.lineColorDoor)
|
||||||
);
|
);
|
||||||
doorGraphic.lineTo(
|
doorCloseGraphic.moveTo(-platformConsts.doorOpenSpacing, 0);
|
||||||
width / 2 + this.datas.lineWidth / 2,
|
doorCloseGraphic.lineTo(platformConsts.doorOpenSpacing, 0);
|
||||||
-height / 2 - 10
|
doorGraphic.position.set(
|
||||||
|
0,
|
||||||
|
-height / 2 - platformConsts.doorPlatformSpacing
|
||||||
|
);
|
||||||
|
doorCloseGraphic.position.set(
|
||||||
|
0,
|
||||||
|
-height / 2 - platformConsts.doorPlatformSpacing
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
//站台
|
||||||
const platformGraphic = this.platformGraphic;
|
const platformGraphic = this.platformGraphic;
|
||||||
platformGraphic.clear();
|
platformGraphic.clear();
|
||||||
platformGraphic.lineStyle(
|
platformGraphic.lineStyle(
|
||||||
@ -74,14 +124,64 @@ export class Platform extends JlGraphic {
|
|||||||
platformGraphic.beginFill(this.datas.lineColor, 1);
|
platformGraphic.beginFill(this.datas.lineColor, 1);
|
||||||
platformGraphic.drawRect(0, 0, this.datas.width, this.datas.height);
|
platformGraphic.drawRect(0, 0, this.datas.width, this.datas.height);
|
||||||
platformGraphic.endFill;
|
platformGraphic.endFill;
|
||||||
const rect = new Rectangle(0, 0, this.datas.width, this.datas.height);
|
const rectP = new Rectangle(0, 0, this.datas.width, this.datas.height);
|
||||||
platformGraphic.pivot = getRectangleCenter(rect);
|
platformGraphic.pivot = getRectangleCenter(rectP);
|
||||||
this.position.set(this.datas.point.x, this.datas.point.y);
|
this.position.set(this.datas.point.x, this.datas.point.y);
|
||||||
|
//站台旁菱形图标
|
||||||
|
const besideGraphic = this.besideGraphic;
|
||||||
|
besideGraphic.clear();
|
||||||
|
besideGraphic.lineStyle(1, new Color(PlatformColorEnum.lozengeRed));
|
||||||
|
besideGraphic.drawRect(0, 0, this.datas.height / 4, this.datas.height / 4);
|
||||||
|
const rect = new Rectangle(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
this.datas.height / 4,
|
||||||
|
this.datas.height / 4
|
||||||
|
);
|
||||||
|
besideGraphic.pivot = getRectangleCenter(rect);
|
||||||
|
besideGraphic.rotation = Math.PI / 4;
|
||||||
|
besideGraphic.position.set(
|
||||||
|
-width / 2 - this.datas.lineWidth / 2 - platformConsts.besideSpacing,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
//站台旁的数字、字符
|
||||||
|
const codeGraph = this.codeGraph;
|
||||||
|
codeGraph.text = 'H';
|
||||||
|
codeGraph.anchor.set(0.5);
|
||||||
|
codeGraph.position.set(
|
||||||
|
-width / 2 - this.datas.lineWidth / 2 - platformConsts.besideSpacing,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
codeGraph.style.fill = PlatformColorEnum.HCharYellow;
|
||||||
|
//站台方向
|
||||||
|
if (this.datas.trainDirection == 'right') {
|
||||||
|
doorGraphic.position.set(
|
||||||
|
0,
|
||||||
|
height / 2 + platformConsts.doorPlatformSpacing
|
||||||
|
);
|
||||||
|
doorCloseGraphic.position.set(
|
||||||
|
0,
|
||||||
|
height / 2 + platformConsts.doorPlatformSpacing
|
||||||
|
);
|
||||||
|
besideGraphic.position.set(
|
||||||
|
width / 2 + this.datas.lineWidth / 2 + platformConsts.besideSpacing,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
codeGraph.position.set(
|
||||||
|
width / 2 + this.datas.lineWidth / 2 + platformConsts.besideSpacing,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
//子元素显隐
|
||||||
|
doorCloseGraphic.visible = false;
|
||||||
|
/* besideGraphic.visible = false;
|
||||||
|
codeGraph.visible = false; */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PlatformTemplate extends JlGraphicTemplate<Platform> {
|
export class PlatformTemplate extends JlGraphicTemplate<Platform> {
|
||||||
hasdoor: boolean;
|
hasdoor: boolean;
|
||||||
|
trainDirection: string;
|
||||||
lineWidth: number;
|
lineWidth: number;
|
||||||
lineColor: string;
|
lineColor: string;
|
||||||
lineColorDoor: string;
|
lineColorDoor: string;
|
||||||
@ -89,12 +189,13 @@ export class PlatformTemplate extends JlGraphicTemplate<Platform> {
|
|||||||
height: number;
|
height: number;
|
||||||
constructor() {
|
constructor() {
|
||||||
super(Platform.Type);
|
super(Platform.Type);
|
||||||
this.lineWidth = 2;
|
|
||||||
this.lineColor = '#000000';
|
|
||||||
this.lineColorDoor = '0x008000';
|
|
||||||
this.hasdoor = true;
|
this.hasdoor = true;
|
||||||
this.width = 100;
|
this.trainDirection = 'left';
|
||||||
this.height = 30;
|
this.lineWidth = platformConsts.lineWidth;
|
||||||
|
this.lineColor = PlatformColorEnum.yellow;
|
||||||
|
this.lineColorDoor = PlatformColorEnum.doorBlue;
|
||||||
|
this.width = platformConsts.width;
|
||||||
|
this.height = platformConsts.height;
|
||||||
}
|
}
|
||||||
new(): Platform {
|
new(): Platform {
|
||||||
return new Platform();
|
return new Platform();
|
||||||
|
@ -7,14 +7,15 @@ import {
|
|||||||
} from 'pixi.js';
|
} from 'pixi.js';
|
||||||
import {
|
import {
|
||||||
GraphicDrawAssistant,
|
GraphicDrawAssistant,
|
||||||
|
GraphicInteractionPlugin,
|
||||||
JlDrawApp,
|
JlDrawApp,
|
||||||
KeyListener,
|
JlGraphic,
|
||||||
getRectangleCenter,
|
getRectangleCenter,
|
||||||
} from 'src/jlgraphic';
|
} from 'src/jlgraphic';
|
||||||
|
|
||||||
import { IPlatformData, Platform, PlatformTemplate } from './Platform';
|
import { IPlatformData, Platform, PlatformTemplate } from './Platform';
|
||||||
|
|
||||||
export interface ILinkDrawOptions {
|
export interface IPlatformDrawOptions {
|
||||||
newData: () => IPlatformData;
|
newData: () => IPlatformData;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,15 +27,6 @@ export class PlatformDraw extends GraphicDrawAssistant<
|
|||||||
platformGraphic: Graphics = new Graphics();
|
platformGraphic: Graphics = new Graphics();
|
||||||
doorGraphic: Graphics = new Graphics();
|
doorGraphic: Graphics = new Graphics();
|
||||||
|
|
||||||
// 快捷绘制
|
|
||||||
keypListener: KeyListener = new KeyListener({
|
|
||||||
value: 'KeyP',
|
|
||||||
global: true,
|
|
||||||
onPress: () => {
|
|
||||||
this.graphicTemplate.hasdoor = true;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
constructor(app: JlDrawApp, createData: () => IPlatformData) {
|
constructor(app: JlDrawApp, createData: () => IPlatformData) {
|
||||||
super(
|
super(
|
||||||
app,
|
app,
|
||||||
@ -46,24 +38,20 @@ export class PlatformDraw extends GraphicDrawAssistant<
|
|||||||
this.container.addChild(this.platformGraphic);
|
this.container.addChild(this.platformGraphic);
|
||||||
this.container.addChild(this.doorGraphic);
|
this.container.addChild(this.doorGraphic);
|
||||||
this.graphicTemplate.hasdoor = true;
|
this.graphicTemplate.hasdoor = true;
|
||||||
|
platformInteraction.init(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
bind(): void {
|
bind(): void {
|
||||||
super.bind();
|
super.bind();
|
||||||
this.app.addKeyboardListener(this.keypListener);
|
|
||||||
}
|
}
|
||||||
unbind(): void {
|
unbind(): void {
|
||||||
super.unbind();
|
super.unbind();
|
||||||
this.app.removeKeyboardListener(this.keypListener);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
clearCache(): void {
|
clearCache(): void {
|
||||||
this.platformGraphic.clear();
|
this.platformGraphic.clear();
|
||||||
this.doorGraphic.clear();
|
this.doorGraphic.clear();
|
||||||
}
|
}
|
||||||
onRightClick(): void {
|
|
||||||
this.createAndStore(true);
|
|
||||||
}
|
|
||||||
onLeftDown(e: FederatedPointerEvent): void {
|
onLeftDown(e: FederatedPointerEvent): void {
|
||||||
const { x, y } = this.toCanvasCoordinates(e.global);
|
const { x, y } = this.toCanvasCoordinates(e.global);
|
||||||
const p = new Point(x, y);
|
const p = new Point(x, y);
|
||||||
@ -85,6 +73,7 @@ export class PlatformDraw extends GraphicDrawAssistant<
|
|||||||
const height = template.height;
|
const height = template.height;
|
||||||
doorGraphic.moveTo(-width / 2 - template.lineWidth / 2, -height / 2 - 10);
|
doorGraphic.moveTo(-width / 2 - template.lineWidth / 2, -height / 2 - 10);
|
||||||
doorGraphic.lineTo(width / 2 + template.lineWidth / 2, -height / 2 - 10);
|
doorGraphic.lineTo(width / 2 + template.lineWidth / 2, -height / 2 - 10);
|
||||||
|
doorGraphic.position.set(p.x, p.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
//站台
|
//站台
|
||||||
@ -102,6 +91,7 @@ export class PlatformDraw extends GraphicDrawAssistant<
|
|||||||
prepareData(data: IPlatformData): boolean {
|
prepareData(data: IPlatformData): boolean {
|
||||||
const template = this.graphicTemplate;
|
const template = this.graphicTemplate;
|
||||||
data.hasdoor = template.hasdoor;
|
data.hasdoor = template.hasdoor;
|
||||||
|
data.trainDirection = template.trainDirection;
|
||||||
data.point = this.point;
|
data.point = this.point;
|
||||||
data.lineWidth = template.lineWidth;
|
data.lineWidth = template.lineWidth;
|
||||||
data.lineColor = template.lineColor;
|
data.lineColor = template.lineColor;
|
||||||
@ -111,3 +101,29 @@ export class PlatformDraw extends GraphicDrawAssistant<
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class platformInteraction extends GraphicInteractionPlugin<Platform> {
|
||||||
|
static Name = 'platform_transform';
|
||||||
|
constructor(app: JlDrawApp) {
|
||||||
|
super(platformInteraction.Name, app);
|
||||||
|
}
|
||||||
|
static init(app: JlDrawApp) {
|
||||||
|
return new platformInteraction(app);
|
||||||
|
}
|
||||||
|
filter(...grahpics: JlGraphic[]): Platform[] | undefined {
|
||||||
|
return grahpics
|
||||||
|
.filter((g) => g.type === Platform.Type)
|
||||||
|
.map((g) => g as Platform);
|
||||||
|
}
|
||||||
|
bind(g: Platform): void {
|
||||||
|
g.eventMode = 'static';
|
||||||
|
g.cursor = 'pointer';
|
||||||
|
g.scalable = true;
|
||||||
|
g.rotatable = true;
|
||||||
|
}
|
||||||
|
unbind(g: Platform): void {
|
||||||
|
g.eventMode = 'none';
|
||||||
|
g.scalable = false;
|
||||||
|
g.rotatable = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
86
src/graphics/rect/Rect.ts
Normal file
86
src/graphics/rect/Rect.ts
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import { Color, Graphics, IPointData, Point } from 'pixi.js';
|
||||||
|
import { GraphicData, JlGraphic, JlGraphicTemplate } from 'src/jlgraphic';
|
||||||
|
|
||||||
|
export interface IRectData extends GraphicData {
|
||||||
|
get code(): string; // 编号
|
||||||
|
set code(v: string);
|
||||||
|
get lineWidth(): number; // 线宽
|
||||||
|
set lineWidth(v: number);
|
||||||
|
get lineColor(): string; // 线色
|
||||||
|
set lineColor(v: string);
|
||||||
|
get point(): IPointData; // 位置坐标
|
||||||
|
set point(point: IPointData);
|
||||||
|
get width(): number; // 宽度
|
||||||
|
set width(v: number);
|
||||||
|
get height(): number; // 高度
|
||||||
|
set height(v: number);
|
||||||
|
get points(): IPointData[]; // 线坐标点
|
||||||
|
set points(points: IPointData[]);
|
||||||
|
clone(): IRectData;
|
||||||
|
copyFrom(data: IRectData): void;
|
||||||
|
eq(other: IRectData): boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rectConsts = {
|
||||||
|
lineWidth: 2,
|
||||||
|
lineColor: '0xff0000',
|
||||||
|
width: 60,
|
||||||
|
height: 20,
|
||||||
|
};
|
||||||
|
|
||||||
|
export class Rect extends JlGraphic {
|
||||||
|
static Type = 'Rect';
|
||||||
|
rectGraphic: Graphics;
|
||||||
|
constructor() {
|
||||||
|
super(Rect.Type);
|
||||||
|
this.rectGraphic = new Graphics();
|
||||||
|
this.addChild(this.rectGraphic);
|
||||||
|
}
|
||||||
|
|
||||||
|
get datas(): IRectData {
|
||||||
|
return this.getDatas<IRectData>();
|
||||||
|
}
|
||||||
|
doRepaint(): void {
|
||||||
|
const width = this.datas.width;
|
||||||
|
const height = this.datas.height;
|
||||||
|
if (this.linePoints.length == 0) {
|
||||||
|
const r1 = new Point(this.datas.point.x, this.datas.point.y);
|
||||||
|
const r2 = new Point(r1.x + width, r1.y);
|
||||||
|
const r3 = new Point(r1.x + width, r1.y + height);
|
||||||
|
const r4 = new Point(r1.x, r1.y + height);
|
||||||
|
this.datas.points = [r1, r2, r3, r4, r1];
|
||||||
|
}
|
||||||
|
const rectGraphic = this.rectGraphic;
|
||||||
|
rectGraphic.clear();
|
||||||
|
rectGraphic.lineStyle(
|
||||||
|
this.datas.lineWidth,
|
||||||
|
new Color(this.datas.lineColor)
|
||||||
|
);
|
||||||
|
rectGraphic.drawPolygon(this.datas.points);
|
||||||
|
}
|
||||||
|
get linePoints(): IPointData[] {
|
||||||
|
return this.datas.points;
|
||||||
|
}
|
||||||
|
set linePoints(points: IPointData[]) {
|
||||||
|
const old = this.datas.clone();
|
||||||
|
old.points = points;
|
||||||
|
this.updateData(old);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RectTemplate extends JlGraphicTemplate<Rect> {
|
||||||
|
lineWidth: number;
|
||||||
|
lineColor: string;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
constructor() {
|
||||||
|
super(Rect.Type);
|
||||||
|
this.lineWidth = rectConsts.lineWidth;
|
||||||
|
this.lineColor = rectConsts.lineColor;
|
||||||
|
this.width = rectConsts.width;
|
||||||
|
this.height = rectConsts.height;
|
||||||
|
}
|
||||||
|
new(): Rect {
|
||||||
|
return new Rect();
|
||||||
|
}
|
||||||
|
}
|
261
src/graphics/rect/RectDrawAssistant.ts
Normal file
261
src/graphics/rect/RectDrawAssistant.ts
Normal file
@ -0,0 +1,261 @@
|
|||||||
|
import {
|
||||||
|
FederatedPointerEvent,
|
||||||
|
Graphics,
|
||||||
|
Point,
|
||||||
|
IHitArea,
|
||||||
|
DisplayObject,
|
||||||
|
FederatedMouseEvent,
|
||||||
|
} from 'pixi.js';
|
||||||
|
import {
|
||||||
|
DraggablePoint,
|
||||||
|
GraphicApp,
|
||||||
|
GraphicDrawAssistant,
|
||||||
|
GraphicInteractionPlugin,
|
||||||
|
GraphicTransformEvent,
|
||||||
|
JlDrawApp,
|
||||||
|
JlGraphic,
|
||||||
|
linePoint,
|
||||||
|
} from 'src/jlgraphic';
|
||||||
|
|
||||||
|
import AbsorbablePoint, {
|
||||||
|
AbsorbablePosition,
|
||||||
|
} from 'src/jlgraphic/graphic/AbsorbablePosition';
|
||||||
|
import {
|
||||||
|
ILineGraphic,
|
||||||
|
PolylineEditPlugin,
|
||||||
|
addWaySegmentingConfig,
|
||||||
|
addPolygonSegmentingPoint,
|
||||||
|
clearWayPoint,
|
||||||
|
clearWaypointsConfig,
|
||||||
|
getWayLineIndex,
|
||||||
|
} from 'src/jlgraphic/plugins/GraphicEditPlugin';
|
||||||
|
import { ContextMenu } from 'src/jlgraphic/ui/ContextMenu';
|
||||||
|
|
||||||
|
import { IRectData, Rect, RectTemplate } from './Rect';
|
||||||
|
import { Link } from '../link/Link';
|
||||||
|
|
||||||
|
export interface IRectDrawOptions {
|
||||||
|
newData: () => IRectData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RectDraw extends GraphicDrawAssistant<RectTemplate, IRectData> {
|
||||||
|
point1: Point | null = null;
|
||||||
|
point2: Point | null = null;
|
||||||
|
rectGraphic: Graphics = new Graphics();
|
||||||
|
|
||||||
|
constructor(app: JlDrawApp, createData: () => IRectData) {
|
||||||
|
super(app, new RectTemplate(), createData, Rect.Type, '站台Rect');
|
||||||
|
this.container.addChild(this.rectGraphic);
|
||||||
|
RectPointsEditPlugin.init(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
bind(): void {
|
||||||
|
super.bind();
|
||||||
|
}
|
||||||
|
unbind(): void {
|
||||||
|
super.unbind();
|
||||||
|
}
|
||||||
|
|
||||||
|
clearCache(): void {
|
||||||
|
this.rectGraphic.clear();
|
||||||
|
}
|
||||||
|
onLeftDown(e: FederatedPointerEvent): void {
|
||||||
|
const { x, y } = this.toCanvasCoordinates(e.global);
|
||||||
|
const p = new Point(x, y);
|
||||||
|
if (this.point1 === null) {
|
||||||
|
this.point1 = p;
|
||||||
|
} else {
|
||||||
|
this.point2 = p;
|
||||||
|
this.createAndStore(true);
|
||||||
|
this.point1 = null;
|
||||||
|
this.point2 = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
redraw(p: Point): void {
|
||||||
|
const template = this.graphicTemplate;
|
||||||
|
if (this.point1 === null) return;
|
||||||
|
const rectGraphic = this.rectGraphic;
|
||||||
|
rectGraphic.clear();
|
||||||
|
rectGraphic.lineStyle(template.lineWidth, template.lineColor);
|
||||||
|
rectGraphic.drawRect(...this.normalize(this.point1, p));
|
||||||
|
}
|
||||||
|
//根据画的两个点确定左上角的点的坐标和矩形宽高
|
||||||
|
private normalize(p1: Point, p2: Point): [number, number, number, number] {
|
||||||
|
const { abs } = Math;
|
||||||
|
const x = p1.x < p2.x ? p1.x : p2.x;
|
||||||
|
const y = p1.y < p2.y ? p1.y : p2.y;
|
||||||
|
const w = abs(p1.x - p2.x);
|
||||||
|
const h = abs(p1.y - p2.y);
|
||||||
|
return [x, y, w, h];
|
||||||
|
}
|
||||||
|
prepareData(data: IRectData): boolean {
|
||||||
|
if (this.point1 == null) {
|
||||||
|
console.log('Rect绘制因点不够取消绘制');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const p1 = this.point1 as Point;
|
||||||
|
const p2 = this.point2 as Point;
|
||||||
|
const [x, y, width, height] = this.normalize(p1, p2);
|
||||||
|
const template = this.graphicTemplate;
|
||||||
|
data.point = new Point(x, y);
|
||||||
|
data.lineWidth = template.lineWidth;
|
||||||
|
data.lineColor = template.lineColor;
|
||||||
|
data.width = width;
|
||||||
|
data.height = height;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//碰撞检测
|
||||||
|
export class RectGraphicHitArea implements IHitArea {
|
||||||
|
rect: Rect;
|
||||||
|
constructor(rect: Rect) {
|
||||||
|
this.rect = rect;
|
||||||
|
}
|
||||||
|
contains(x: number, y: number): boolean {
|
||||||
|
let contains = false;
|
||||||
|
const p = new Point(x, y);
|
||||||
|
const rectData = this.rect.datas;
|
||||||
|
//contains = pointPolygon(p, rectData.points, rectData.lineWidth);是否包含多边形内部
|
||||||
|
const tolerance = rectData.lineWidth;
|
||||||
|
for (let i = 0; i < rectData.points.length - 1; i++) {
|
||||||
|
const p1 = rectData.points[i];
|
||||||
|
const p2 = rectData.points[i + 1];
|
||||||
|
contains = contains || linePoint(p1, p2, p, tolerance);
|
||||||
|
if (contains) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return contains;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建吸附位置
|
||||||
|
* @param rect
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function buildAbsorbablePositions(rect: Rect): AbsorbablePosition[] {
|
||||||
|
const aps: AbsorbablePosition[] = [];
|
||||||
|
const rects = rect.queryStore.queryByType<Rect>(Rect.Type);
|
||||||
|
const links = rect.queryStore.queryByType<Link>(Link.Type);
|
||||||
|
|
||||||
|
links.forEach((other) => {
|
||||||
|
const apa = new AbsorbablePoint(
|
||||||
|
other.localToCanvasPoint(other.getStartPoint())
|
||||||
|
);
|
||||||
|
const apb = new AbsorbablePoint(
|
||||||
|
other.localToCanvasPoint(other.getEndPoint())
|
||||||
|
);
|
||||||
|
aps.push(apa, apb);
|
||||||
|
});
|
||||||
|
|
||||||
|
rects.forEach((other) => {
|
||||||
|
if (other.id == rect.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
other.linePoints.forEach((point) => {
|
||||||
|
const absorbablePoint = new AbsorbablePoint(
|
||||||
|
other.localToCanvasPoint(point)
|
||||||
|
);
|
||||||
|
aps.push(absorbablePoint);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return aps;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 端点拖拽添加吸附位置
|
||||||
|
* @param g
|
||||||
|
* @param dp
|
||||||
|
* @param index
|
||||||
|
*/
|
||||||
|
function onEditPointCreate(g: ILineGraphic, dp: DraggablePoint): void {
|
||||||
|
const rect = g as Rect;
|
||||||
|
// 端点
|
||||||
|
dp.on('transformstart', (e: GraphicTransformEvent) => {
|
||||||
|
if (e.isShift()) {
|
||||||
|
rect.getGraphicApp().setOptions({
|
||||||
|
absorbablePositions: buildAbsorbablePositions(rect),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const RectEditMenu: ContextMenu = ContextMenu.init({
|
||||||
|
name: '矩形编辑菜单',
|
||||||
|
groups: [
|
||||||
|
{
|
||||||
|
items: [addWaySegmentingConfig, clearWaypointsConfig],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rect路径编辑
|
||||||
|
*/
|
||||||
|
export class RectPointsEditPlugin extends GraphicInteractionPlugin<Rect> {
|
||||||
|
static Name = 'RectPointsDrag';
|
||||||
|
constructor(app: GraphicApp) {
|
||||||
|
super(RectPointsEditPlugin.Name, app);
|
||||||
|
app.registerMenu(RectEditMenu);
|
||||||
|
}
|
||||||
|
static init(app: GraphicApp): RectPointsEditPlugin {
|
||||||
|
return new RectPointsEditPlugin(app);
|
||||||
|
}
|
||||||
|
filter(...grahpics: JlGraphic[]): Rect[] | undefined {
|
||||||
|
return grahpics.filter((g) => g.type == Rect.Type) as Rect[];
|
||||||
|
}
|
||||||
|
bind(g: Rect): void {
|
||||||
|
g.rectGraphic.eventMode = 'static';
|
||||||
|
g.rectGraphic.cursor = 'pointer';
|
||||||
|
g.rectGraphic.hitArea = new RectGraphicHitArea(g);
|
||||||
|
g.on('_rightclick', this.onContextMenu, this);
|
||||||
|
g.on('selected', this.onSelected, this);
|
||||||
|
g.on('unselected', this.onUnselected, this);
|
||||||
|
}
|
||||||
|
unbind(g: Rect): void {
|
||||||
|
g.off('_rightclick', this.onContextMenu, this);
|
||||||
|
g.off('selected', this.onSelected, this);
|
||||||
|
g.off('unselected', this.onUnselected, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
onContextMenu(e: FederatedMouseEvent) {
|
||||||
|
const target = e.target as DisplayObject;
|
||||||
|
const rect = target.getGraphic() as Rect;
|
||||||
|
this.app.updateSelected(rect);
|
||||||
|
addWaySegmentingConfig.handler = () => {
|
||||||
|
const linePoints = rect.linePoints;
|
||||||
|
const p = rect.screenToLocalPoint(e.global);
|
||||||
|
const { start, end } = getWayLineIndex(linePoints, p);
|
||||||
|
addPolygonSegmentingPoint(rect, start, end);
|
||||||
|
};
|
||||||
|
clearWaypointsConfig.handler = () => {
|
||||||
|
clearWayPoint(rect, false);
|
||||||
|
};
|
||||||
|
RectEditMenu.open(e.global);
|
||||||
|
}
|
||||||
|
|
||||||
|
onSelected(g: DisplayObject): void {
|
||||||
|
const rect = g as Rect;
|
||||||
|
let lep = rect.getAssistantAppend<PolylineEditPlugin>(
|
||||||
|
PolylineEditPlugin.Name
|
||||||
|
);
|
||||||
|
if (!lep) {
|
||||||
|
lep = new PolylineEditPlugin(rect, { onEditPointCreate });
|
||||||
|
rect.addAssistantAppend(lep);
|
||||||
|
}
|
||||||
|
lep.showAll();
|
||||||
|
}
|
||||||
|
onUnselected(g: DisplayObject): void {
|
||||||
|
const rect = g as Rect;
|
||||||
|
const lep = rect.getAssistantAppend<PolylineEditPlugin>(
|
||||||
|
PolylineEditPlugin.Name
|
||||||
|
);
|
||||||
|
if (lep) {
|
||||||
|
lep.hideAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
52
src/graphics/signal/Lamp.ts
Normal file
52
src/graphics/signal/Lamp.ts
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import { Container } from '@pixi/display';
|
||||||
|
import { Graphics } from 'pixi.js';
|
||||||
|
|
||||||
|
export enum LampEnum {
|
||||||
|
lampPostColor = '0xc0c0c0',
|
||||||
|
lampDefaultColor = '0xff0000',
|
||||||
|
logicModeColor = '0x000000',
|
||||||
|
}
|
||||||
|
|
||||||
|
const lampConsts = {
|
||||||
|
lampRadius: 10,
|
||||||
|
logicModeLineWidth: 2,
|
||||||
|
logicModeDistance: 7,
|
||||||
|
};
|
||||||
|
|
||||||
|
export class Lamp extends Container {
|
||||||
|
circleLamp: Graphics = new Graphics();
|
||||||
|
logicMode: Graphics = new Graphics();
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.addChild(this.circleLamp);
|
||||||
|
this.addChild(this.logicMode);
|
||||||
|
}
|
||||||
|
paint(radiusX: number, radiusY: number) {
|
||||||
|
this.circleLamp.clear();
|
||||||
|
this.circleLamp
|
||||||
|
.beginFill(LampEnum.lampDefaultColor, 1)
|
||||||
|
.drawCircle(radiusX, radiusY, lampConsts.lampRadius)
|
||||||
|
.endFill();
|
||||||
|
|
||||||
|
this.logicMode
|
||||||
|
.clear()
|
||||||
|
.lineStyle(lampConsts.logicModeLineWidth, LampEnum.logicModeColor)
|
||||||
|
.moveTo(
|
||||||
|
radiusX - lampConsts.logicModeDistance,
|
||||||
|
radiusY + lampConsts.logicModeDistance
|
||||||
|
)
|
||||||
|
.lineTo(
|
||||||
|
radiusX + lampConsts.logicModeDistance,
|
||||||
|
radiusY - lampConsts.logicModeDistance
|
||||||
|
)
|
||||||
|
.moveTo(
|
||||||
|
radiusX - lampConsts.logicModeDistance,
|
||||||
|
radiusY - lampConsts.logicModeDistance
|
||||||
|
)
|
||||||
|
.lineTo(
|
||||||
|
radiusX + lampConsts.logicModeDistance,
|
||||||
|
radiusY + lampConsts.logicModeDistance
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
51
src/graphics/signal/LampMainBody.ts
Normal file
51
src/graphics/signal/LampMainBody.ts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import { Container } from '@pixi/display';
|
||||||
|
import { Graphics } from 'pixi.js';
|
||||||
|
import { Lamp } from './Lamp';
|
||||||
|
|
||||||
|
export enum LampEnum {
|
||||||
|
lampPostColor = '0xc0c0c0',
|
||||||
|
}
|
||||||
|
|
||||||
|
const lampConsts = {
|
||||||
|
verticalLampPostLength: 20,
|
||||||
|
levelLampPostLength: 5,
|
||||||
|
postLineWidth: 3,
|
||||||
|
lampRadius: 10,
|
||||||
|
};
|
||||||
|
|
||||||
|
export class LampMainBody extends Container {
|
||||||
|
lampNum = 1;
|
||||||
|
lampPost: Graphics = new Graphics();
|
||||||
|
lamps: Lamp[] = [];
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
paint(lampNum: number) {
|
||||||
|
if (lampNum < 1) {
|
||||||
|
throw new Error('信号机灯数量异常');
|
||||||
|
}
|
||||||
|
this.lampNum = lampNum;
|
||||||
|
this.removeChildren(0);
|
||||||
|
this.lampPost = new Graphics();
|
||||||
|
this.lampPost
|
||||||
|
.lineStyle(lampConsts.postLineWidth, LampEnum.lampPostColor)
|
||||||
|
.moveTo(0, -lampConsts.verticalLampPostLength / 2)
|
||||||
|
.lineTo(0, lampConsts.verticalLampPostLength / 2)
|
||||||
|
.moveTo(0, 0)
|
||||||
|
.lineTo(lampConsts.levelLampPostLength, 0);
|
||||||
|
this.addChild(this.lampPost);
|
||||||
|
|
||||||
|
this.lamps = [];
|
||||||
|
for (let i = 0; i < this.lampNum; i++) {
|
||||||
|
const lamp = new Lamp();
|
||||||
|
const radiusX =
|
||||||
|
(1 + i * 2) * lampConsts.lampRadius + lampConsts.levelLampPostLength;
|
||||||
|
lamp.paint(radiusX, 0);
|
||||||
|
this.addChild(lamp);
|
||||||
|
this.lamps.push(lamp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// setState() {}
|
||||||
|
}
|
@ -0,0 +1,102 @@
|
|||||||
|
import { Graphics } from 'pixi.js';
|
||||||
|
import {
|
||||||
|
GraphicData,
|
||||||
|
JlGraphic,
|
||||||
|
JlGraphicTemplate,
|
||||||
|
VectorText,
|
||||||
|
} from 'src/jlgraphic';
|
||||||
|
import { LampMainBody } from './LampMainBody';
|
||||||
|
import { drawArrow } from '../CommonGraphics';
|
||||||
|
|
||||||
|
export interface ISignalData extends GraphicData {
|
||||||
|
get code(): string; // 编号
|
||||||
|
set code(v: string);
|
||||||
|
clone(): ISignalData;
|
||||||
|
copyFrom(data: ISignalData): void;
|
||||||
|
eq(other: ISignalData): boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum SignalColorEnum {
|
||||||
|
humanControlColor = '0xffff00',
|
||||||
|
fleetModeColor = '0x00ff00',
|
||||||
|
codeColor = '0X000000',
|
||||||
|
}
|
||||||
|
|
||||||
|
const signalConsts = {
|
||||||
|
lampNum: 1,
|
||||||
|
codeFontSize: 11,
|
||||||
|
nameOffsetY: 20,
|
||||||
|
fleetModeLength: 33,
|
||||||
|
fleetModeRadius: 10,
|
||||||
|
fleetModeLineWidth: 6,
|
||||||
|
};
|
||||||
|
export class Signal extends JlGraphic {
|
||||||
|
static Type = 'signal';
|
||||||
|
codeGraph: VectorText = new VectorText('');
|
||||||
|
humanControl: Graphics = new Graphics();
|
||||||
|
fleetMode: Graphics = new Graphics();
|
||||||
|
lampMainBody: LampMainBody = new LampMainBody();
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super(Signal.Type);
|
||||||
|
this.codeGraph.name = 'signalCode';
|
||||||
|
this.addChild(this.codeGraph);
|
||||||
|
this.addChild(this.humanControl);
|
||||||
|
this.addChild(this.fleetMode);
|
||||||
|
this.addChild(this.lampMainBody);
|
||||||
|
}
|
||||||
|
|
||||||
|
get datas(): ISignalData {
|
||||||
|
return this.getDatas<ISignalData>();
|
||||||
|
}
|
||||||
|
|
||||||
|
paint(): void {
|
||||||
|
this.lampMainBody.paint(signalConsts.lampNum);
|
||||||
|
this.humanControl.beginFill(SignalColorEnum.humanControlColor, 1);
|
||||||
|
if (this.humanControl.drawRegularPolygon) {
|
||||||
|
this.humanControl.drawRegularPolygon(-10, 0, 10, 3, Math.PI / 2);
|
||||||
|
}
|
||||||
|
this.humanControl.endFill();
|
||||||
|
this.fleetMode.beginFill(SignalColorEnum.fleetModeColor, 1);
|
||||||
|
drawArrow(
|
||||||
|
this.fleetMode,
|
||||||
|
this.lampMainBody.width + signalConsts.fleetModeLength,
|
||||||
|
0,
|
||||||
|
signalConsts.fleetModeLength,
|
||||||
|
signalConsts.fleetModeRadius,
|
||||||
|
signalConsts.fleetModeLineWidth
|
||||||
|
);
|
||||||
|
this.fleetMode.endFill();
|
||||||
|
this.codeGraph.text = this.datas?.code || '信号机编号';
|
||||||
|
this.codeGraph.style.fill = SignalColorEnum.codeColor;
|
||||||
|
this.codeGraph.setVectorFontSize(signalConsts.codeFontSize);
|
||||||
|
this.codeGraph.anchor.set(0.5);
|
||||||
|
this.codeGraph.style.fill = SignalColorEnum.codeColor;
|
||||||
|
const codeTransform = this.datas?.childTransforms?.find(
|
||||||
|
(item) => item.name === 'signalCode'
|
||||||
|
);
|
||||||
|
if (codeTransform) {
|
||||||
|
const position = codeTransform?.transform.position;
|
||||||
|
const rotation = codeTransform?.transform?.rotation;
|
||||||
|
this.codeGraph.position.set(position?.x, position?.y);
|
||||||
|
this.codeGraph.rotation = rotation || 0;
|
||||||
|
} else {
|
||||||
|
this.codeGraph.position.set(0, signalConsts.nameOffsetY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
doRepaint(): void {
|
||||||
|
this.paint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SignalTemplate extends JlGraphicTemplate<Signal> {
|
||||||
|
constructor(dataTemplate: ISignalData) {
|
||||||
|
super(Signal.Type, {
|
||||||
|
dataTemplate,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
new(): Signal {
|
||||||
|
return new Signal();
|
||||||
|
}
|
||||||
|
}
|
133
src/graphics/signal/SignalDrawAssistant.ts
Normal file
133
src/graphics/signal/SignalDrawAssistant.ts
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
import { FederatedPointerEvent, Point } from 'pixi.js';
|
||||||
|
import {
|
||||||
|
AbsorbableLine,
|
||||||
|
AbsorbablePosition,
|
||||||
|
GraphicApp,
|
||||||
|
GraphicDrawAssistant,
|
||||||
|
GraphicInteractionPlugin,
|
||||||
|
GraphicTransformEvent,
|
||||||
|
JlDrawApp,
|
||||||
|
JlGraphic,
|
||||||
|
} from 'src/jlgraphic';
|
||||||
|
import { ISignalData, Signal, SignalTemplate } from './Signal';
|
||||||
|
import { IscsFan } from '../iscs-fan/IscsFan';
|
||||||
|
|
||||||
|
export interface ISignalDrawOptions {
|
||||||
|
newData: () => ISignalData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SignalDraw extends GraphicDrawAssistant<
|
||||||
|
SignalTemplate,
|
||||||
|
ISignalData
|
||||||
|
> {
|
||||||
|
_signal: Signal | null = null;
|
||||||
|
|
||||||
|
constructor(app: JlDrawApp, template: SignalTemplate) {
|
||||||
|
super(
|
||||||
|
app,
|
||||||
|
template,
|
||||||
|
'svguse: ../../drawIcon.svg#icon-signal',
|
||||||
|
'信号机Signal'
|
||||||
|
);
|
||||||
|
|
||||||
|
signalInteraction.init(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get signal(): Signal {
|
||||||
|
if (!this._signal) {
|
||||||
|
this._signal = this.graphicTemplate.new();
|
||||||
|
this.signal.loadData(this.graphicTemplate.datas);
|
||||||
|
this.container.addChild(this.signal);
|
||||||
|
}
|
||||||
|
return this._signal;
|
||||||
|
}
|
||||||
|
|
||||||
|
clearCache(): void {
|
||||||
|
//this.codeGraph.clear();
|
||||||
|
}
|
||||||
|
onRightClick(): void {
|
||||||
|
this.createAndStore(true);
|
||||||
|
}
|
||||||
|
onLeftUp(e: FederatedPointerEvent): void {
|
||||||
|
this.container.position.copyFrom(this.toCanvasCoordinates(e.global));
|
||||||
|
this.createAndStore(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
redraw(p: Point): void {
|
||||||
|
this.signal.paint();
|
||||||
|
this.container.position.set(p.x, p.y);
|
||||||
|
}
|
||||||
|
prepareData(data: ISignalData): boolean {
|
||||||
|
data.transform = this.container.saveTransform();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class signalInteraction extends GraphicInteractionPlugin<Signal> {
|
||||||
|
static Name = 'signal_transform';
|
||||||
|
constructor(app: JlDrawApp) {
|
||||||
|
super(signalInteraction.Name, app);
|
||||||
|
}
|
||||||
|
static init(app: JlDrawApp) {
|
||||||
|
return new signalInteraction(app);
|
||||||
|
}
|
||||||
|
filter(...grahpics: JlGraphic[]): Signal[] | undefined {
|
||||||
|
return grahpics
|
||||||
|
.filter((g) => g.type === Signal.Type)
|
||||||
|
.map((g) => g as Signal);
|
||||||
|
}
|
||||||
|
bind(g: Signal): void {
|
||||||
|
g.eventMode = 'static';
|
||||||
|
g.cursor = 'pointer';
|
||||||
|
g.scalable = true;
|
||||||
|
g.rotatable = true;
|
||||||
|
g.codeGraph.draggable = true;
|
||||||
|
g.codeGraph.selectable = true;
|
||||||
|
g.codeGraph.rotatable = true;
|
||||||
|
g.codeGraph.scalable = true;
|
||||||
|
g.codeGraph.transformSave = true;
|
||||||
|
g.codeGraph.eventMode = 'static';
|
||||||
|
g.codeGraph.on('transformstart', (e: GraphicTransformEvent) => {
|
||||||
|
if (e.isShift()) {
|
||||||
|
console.log(
|
||||||
|
'信号机画布坐标',
|
||||||
|
e.target.position,
|
||||||
|
e.target.getPositionOnCanvas()
|
||||||
|
);
|
||||||
|
const app = g.getGraphicApp();
|
||||||
|
app.setOptions({
|
||||||
|
absorbablePositions: buildAbsorbablePositions(app),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// onScaleDragEnd() {
|
||||||
|
// console.log('-----------------');
|
||||||
|
// }
|
||||||
|
unbind(g: Signal): void {
|
||||||
|
g.eventMode = 'none';
|
||||||
|
g.scalable = false;
|
||||||
|
g.rotatable = false;
|
||||||
|
g.codeGraph.draggable = false;
|
||||||
|
g.codeGraph.selectable = false;
|
||||||
|
g.codeGraph.rotatable = false;
|
||||||
|
g.codeGraph.scalable = false;
|
||||||
|
g.codeGraph.transformSave = false;
|
||||||
|
g.codeGraph.eventMode = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function buildAbsorbablePositions(app: GraphicApp): AbsorbablePosition[] {
|
||||||
|
const canvas = app.canvas;
|
||||||
|
const store = app.queryStore;
|
||||||
|
const aps: AbsorbablePosition[] = [];
|
||||||
|
store.queryByType(IscsFan.Type).forEach((fan) => {
|
||||||
|
const p = fan.position;
|
||||||
|
aps.push(
|
||||||
|
new AbsorbableLine(new Point(0, p.y), new Point(canvas.width, p.y))
|
||||||
|
);
|
||||||
|
aps.push(
|
||||||
|
new AbsorbableLine(new Point(p.x, 0), new Point(p.x, canvas.height))
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return aps;
|
||||||
|
}
|
101
src/graphics/station/Station.ts
Normal file
101
src/graphics/station/Station.ts
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
import { Color, Graphics, IPointData, Point } from 'pixi.js';
|
||||||
|
import {
|
||||||
|
GraphicData,
|
||||||
|
JlGraphic,
|
||||||
|
JlGraphicTemplate,
|
||||||
|
VectorText,
|
||||||
|
} from 'src/jlgraphic';
|
||||||
|
|
||||||
|
export interface IStationData extends GraphicData {
|
||||||
|
get code(): string; // 编号
|
||||||
|
set code(v: string);
|
||||||
|
get hasCircle(): boolean; // 是否有圆圈--线网图
|
||||||
|
set hasCircle(v: boolean);
|
||||||
|
get radius(): number; // 半径
|
||||||
|
set radius(v: number);
|
||||||
|
get borderWidth(): number; // 线宽
|
||||||
|
set borderWidth(v: number);
|
||||||
|
get borderColor(): string; // 圆边框线色
|
||||||
|
set borderColor(v: string);
|
||||||
|
get fillColor(): string; // 圆填充颜色
|
||||||
|
set fillColor(v: string);
|
||||||
|
get codeColor(): string; // 车站字体颜色
|
||||||
|
set codeColor(v: string);
|
||||||
|
get codeFontSize(): number; // 车站字体大小
|
||||||
|
set codeFontSize(v: number);
|
||||||
|
get point(): IPointData; // 位置坐标
|
||||||
|
set point(point: IPointData);
|
||||||
|
get circlePoint(): IPointData; // 位置坐标
|
||||||
|
set circlePoint(point: IPointData);
|
||||||
|
clone(): IStationData;
|
||||||
|
copyFrom(data: IStationData): void;
|
||||||
|
eq(other: IStationData): boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Station extends JlGraphic {
|
||||||
|
static Type = 'station';
|
||||||
|
codeGraph: VectorText = new VectorText(''); //站台旁数字、字符
|
||||||
|
circleGraphic: Graphics;
|
||||||
|
constructor() {
|
||||||
|
super(Station.Type);
|
||||||
|
this.circleGraphic = new Graphics();
|
||||||
|
this.addChild(this.codeGraph);
|
||||||
|
this.addChild(this.circleGraphic);
|
||||||
|
}
|
||||||
|
|
||||||
|
get datas(): IStationData {
|
||||||
|
return this.getDatas<IStationData>();
|
||||||
|
}
|
||||||
|
doRepaint(): void {
|
||||||
|
this.circleGraphic.clear();
|
||||||
|
this.position.set(this.datas.point.x, this.datas.point.y);
|
||||||
|
const codeGraph = this.codeGraph;
|
||||||
|
if (this.datas.code == '') {
|
||||||
|
codeGraph.text = '车站Station';
|
||||||
|
} else {
|
||||||
|
codeGraph.text = this.datas.code;
|
||||||
|
}
|
||||||
|
codeGraph.style.fill = this.datas.codeColor;
|
||||||
|
codeGraph.setVectorFontSize(this.datas.codeFontSize);
|
||||||
|
codeGraph.anchor.set(0.5);
|
||||||
|
if (this.datas.hasCircle) {
|
||||||
|
const circleGraphic = this.circleGraphic;
|
||||||
|
circleGraphic.lineStyle(
|
||||||
|
this.datas.borderWidth,
|
||||||
|
new Color(this.datas.borderColor)
|
||||||
|
);
|
||||||
|
circleGraphic.beginFill(this.datas.fillColor, 1);
|
||||||
|
circleGraphic.drawCircle(0, 0, this.datas.radius);
|
||||||
|
circleGraphic.endFill;
|
||||||
|
circleGraphic.position.set(
|
||||||
|
this.datas.circlePoint.x,
|
||||||
|
this.datas.circlePoint.y
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class StationTemplate extends JlGraphicTemplate<Station> {
|
||||||
|
hasCircle: boolean;
|
||||||
|
radius: number;
|
||||||
|
borderWidth: number;
|
||||||
|
borderColor: string;
|
||||||
|
fillColor: string;
|
||||||
|
codeColor: string;
|
||||||
|
codeFontSize: number;
|
||||||
|
circlePoint: IPointData;
|
||||||
|
constructor() {
|
||||||
|
super(Station.Type);
|
||||||
|
this.hasCircle = false;
|
||||||
|
this.radius = 5;
|
||||||
|
this.borderWidth = 1;
|
||||||
|
this.borderColor = '0xff0000';
|
||||||
|
this.fillColor = '0xff0000';
|
||||||
|
this.codeColor = '0xF48815';
|
||||||
|
this.codeFontSize = 22;
|
||||||
|
this.circlePoint = new Point(0, -20);
|
||||||
|
}
|
||||||
|
new(): Station {
|
||||||
|
return new Station();
|
||||||
|
}
|
||||||
|
}
|
93
src/graphics/station/StationDrawAssistant.ts
Normal file
93
src/graphics/station/StationDrawAssistant.ts
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
import { FederatedPointerEvent, Point } from 'pixi.js';
|
||||||
|
import {
|
||||||
|
GraphicDrawAssistant,
|
||||||
|
GraphicInteractionPlugin,
|
||||||
|
JlDrawApp,
|
||||||
|
JlGraphic,
|
||||||
|
VectorText,
|
||||||
|
} from 'src/jlgraphic';
|
||||||
|
|
||||||
|
import { IStationData, Station, StationTemplate } from './Station';
|
||||||
|
|
||||||
|
export interface IStationDrawOptions {
|
||||||
|
newData: () => IStationData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class StationDraw extends GraphicDrawAssistant<
|
||||||
|
StationTemplate,
|
||||||
|
IStationData
|
||||||
|
> {
|
||||||
|
point: Point = new Point(0, 0);
|
||||||
|
codeGraph: VectorText = new VectorText('');
|
||||||
|
|
||||||
|
constructor(app: JlDrawApp, createData: () => IStationData) {
|
||||||
|
super(app, new StationTemplate(), createData, Station.Type, '车站Station');
|
||||||
|
this.container.addChild(this.codeGraph);
|
||||||
|
stationInteraction.init(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
bind(): void {
|
||||||
|
super.bind();
|
||||||
|
}
|
||||||
|
unbind(): void {
|
||||||
|
super.unbind();
|
||||||
|
}
|
||||||
|
|
||||||
|
clearCache(): void {
|
||||||
|
//this.codeGraph.clear();
|
||||||
|
}
|
||||||
|
onLeftDown(e: FederatedPointerEvent): void {
|
||||||
|
const { x, y } = this.toCanvasCoordinates(e.global);
|
||||||
|
const p = new Point(x, y);
|
||||||
|
this.point = p;
|
||||||
|
this.createAndStore(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
redraw(p: Point): void {
|
||||||
|
const codeGraph = this.codeGraph;
|
||||||
|
codeGraph.text = '车站Station';
|
||||||
|
codeGraph.anchor.set(0.5);
|
||||||
|
codeGraph.style.fill = '0xf48815';
|
||||||
|
codeGraph.position.set(p.x, p.y);
|
||||||
|
codeGraph.setVectorFontSize(22);
|
||||||
|
}
|
||||||
|
prepareData(data: IStationData): boolean {
|
||||||
|
const template = this.graphicTemplate;
|
||||||
|
data.point = this.point;
|
||||||
|
data.hasCircle = template.hasCircle;
|
||||||
|
data.radius = template.radius;
|
||||||
|
data.borderWidth = template.borderWidth;
|
||||||
|
data.borderColor = template.borderColor;
|
||||||
|
data.fillColor = template.fillColor;
|
||||||
|
data.codeColor = template.codeColor;
|
||||||
|
data.codeFontSize = template.codeFontSize;
|
||||||
|
data.circlePoint = template.circlePoint;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class stationInteraction extends GraphicInteractionPlugin<Station> {
|
||||||
|
static Name = 'station_transform';
|
||||||
|
constructor(app: JlDrawApp) {
|
||||||
|
super(stationInteraction.Name, app);
|
||||||
|
}
|
||||||
|
static init(app: JlDrawApp) {
|
||||||
|
return new stationInteraction(app);
|
||||||
|
}
|
||||||
|
filter(...grahpics: JlGraphic[]): Station[] | undefined {
|
||||||
|
return grahpics
|
||||||
|
.filter((g) => g.type === Station.Type)
|
||||||
|
.map((g) => g as Station);
|
||||||
|
}
|
||||||
|
bind(g: Station): void {
|
||||||
|
g.eventMode = 'static';
|
||||||
|
g.cursor = 'pointer';
|
||||||
|
g.scalable = true;
|
||||||
|
g.rotatable = true;
|
||||||
|
}
|
||||||
|
unbind(g: Station): void {
|
||||||
|
g.eventMode = 'none';
|
||||||
|
g.scalable = false;
|
||||||
|
g.rotatable = false;
|
||||||
|
}
|
||||||
|
}
|
185
src/graphics/train/Train.ts
Normal file
185
src/graphics/train/Train.ts
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
import { Color, Graphics, IPointData } from 'pixi.js';
|
||||||
|
import {
|
||||||
|
GraphicData,
|
||||||
|
JlGraphic,
|
||||||
|
JlGraphicTemplate,
|
||||||
|
VectorText,
|
||||||
|
} from 'src/jlgraphic';
|
||||||
|
|
||||||
|
export interface ITrainData extends GraphicData {
|
||||||
|
get code(): string; // 车号
|
||||||
|
set code(v: string);
|
||||||
|
get codeColor(): string; // 车号颜色
|
||||||
|
set codeColor(v: string);
|
||||||
|
get codeFontSize(): number; // 车号字体大小
|
||||||
|
set codeFontSize(v: number);
|
||||||
|
get trainDirection(): string; // 行驶方向
|
||||||
|
set trainDirection(v: string);
|
||||||
|
get hasBorder(): boolean; // 是否有边框
|
||||||
|
set hasBorder(v: boolean);
|
||||||
|
get borderWidth(): number; // 边框线宽
|
||||||
|
set borderWidth(v: number);
|
||||||
|
get borderColor(): string; // 边框颜色
|
||||||
|
set borderColor(v: string);
|
||||||
|
get headColor(): string; // 箭头颜色
|
||||||
|
set headColor(v: string);
|
||||||
|
get bodyColor(): string; // 背景色
|
||||||
|
set bodyColor(v: string);
|
||||||
|
get point(): IPointData; // 位置坐标
|
||||||
|
set point(point: IPointData);
|
||||||
|
clone(): ITrainData;
|
||||||
|
copyFrom(data: ITrainData): void;
|
||||||
|
eq(other: ITrainData): boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 列车颜色
|
||||||
|
export enum TrainColorEnum {
|
||||||
|
headColor = '0x00FF00', // 箭头颜色
|
||||||
|
bodyColor = '0xA388B1', // 背景色
|
||||||
|
codeColor = '0xffffff', // 车号颜色
|
||||||
|
borderColor = '0xA3E198', // 边框的颜色
|
||||||
|
}
|
||||||
|
|
||||||
|
export const trainConsts = {
|
||||||
|
borderWidth: 1,
|
||||||
|
codeFontSize: 22,
|
||||||
|
marginX: 2, // 图形x轴边距
|
||||||
|
pauseW: 2, // 停止框宽度
|
||||||
|
};
|
||||||
|
|
||||||
|
export class Train extends JlGraphic {
|
||||||
|
static Type = 'Train';
|
||||||
|
|
||||||
|
arrowLeft: Graphics;
|
||||||
|
pauseLeft: Graphics;
|
||||||
|
arrowRight: Graphics;
|
||||||
|
pauseRight: Graphics;
|
||||||
|
codeRact: Graphics;
|
||||||
|
codeGraph: VectorText = new VectorText(''); //车号
|
||||||
|
constructor() {
|
||||||
|
super(Train.Type);
|
||||||
|
this.arrowLeft = new Graphics();
|
||||||
|
this.pauseLeft = new Graphics();
|
||||||
|
this.arrowRight = new Graphics();
|
||||||
|
this.pauseRight = new Graphics();
|
||||||
|
this.codeRact = new Graphics();
|
||||||
|
this.addChild(this.arrowLeft);
|
||||||
|
this.addChild(this.pauseLeft);
|
||||||
|
this.addChild(this.arrowRight);
|
||||||
|
this.addChild(this.pauseRight);
|
||||||
|
this.addChild(this.codeRact);
|
||||||
|
this.addChild(this.codeGraph);
|
||||||
|
this.codeGraph.setVectorFontSize(trainConsts.codeFontSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
get datas(): ITrainData {
|
||||||
|
return this.getDatas<ITrainData>();
|
||||||
|
}
|
||||||
|
doRepaint(): void {
|
||||||
|
this.position.set(this.datas.point.x, this.datas.point.y);
|
||||||
|
const codeGraph = this.codeGraph;
|
||||||
|
const codeRact = this.codeRact;
|
||||||
|
if (this.datas.code == '') {
|
||||||
|
codeGraph.text = '01110111';
|
||||||
|
} else {
|
||||||
|
codeGraph.text = this.datas.code;
|
||||||
|
}
|
||||||
|
codeGraph.setVectorFontSize(this.datas.codeFontSize);
|
||||||
|
codeGraph.anchor.set(0.5);
|
||||||
|
const style = {
|
||||||
|
fill: this.datas.codeColor,
|
||||||
|
padding: 5,
|
||||||
|
};
|
||||||
|
codeGraph.style = style;
|
||||||
|
const {
|
||||||
|
x: codeX,
|
||||||
|
y: codeY,
|
||||||
|
width: codeWidth,
|
||||||
|
height: codeHeight,
|
||||||
|
} = codeGraph.getLocalBounds();
|
||||||
|
const marginX = trainConsts.marginX;
|
||||||
|
const pauseW = trainConsts.pauseW;
|
||||||
|
const arrowLeft = this.arrowLeft;
|
||||||
|
arrowLeft.beginFill(this.datas.headColor, 1);
|
||||||
|
arrowLeft.drawPolygon([
|
||||||
|
-codeHeight * 0.4 - marginX - pauseW - marginX - codeWidth / 2,
|
||||||
|
0,
|
||||||
|
-marginX - pauseW - marginX - codeWidth / 2,
|
||||||
|
codeHeight / 2,
|
||||||
|
-marginX - pauseW - marginX - codeWidth / 2,
|
||||||
|
-codeHeight / 2,
|
||||||
|
]);
|
||||||
|
arrowLeft.endFill();
|
||||||
|
this.pauseLeft.beginFill(this.datas.headColor, 1);
|
||||||
|
this.pauseLeft.drawRect(0, 0, pauseW, codeHeight);
|
||||||
|
this.pauseLeft.endFill();
|
||||||
|
this.pauseLeft.position.set(
|
||||||
|
-marginX - pauseW - codeWidth / 2,
|
||||||
|
-codeHeight / 2
|
||||||
|
);
|
||||||
|
this.pauseRight.beginFill(this.datas.headColor, 1);
|
||||||
|
this.pauseRight.drawRect(0, 0, pauseW, codeHeight);
|
||||||
|
this.pauseRight.endFill();
|
||||||
|
this.pauseRight.position.set(marginX + codeWidth / 2, -codeHeight / 2);
|
||||||
|
const arrowRight = this.arrowRight;
|
||||||
|
arrowRight.beginFill(this.datas.headColor, 1);
|
||||||
|
arrowRight.drawPolygon([
|
||||||
|
codeWidth / 2 + marginX + pauseW + marginX + codeHeight * 0.4,
|
||||||
|
0,
|
||||||
|
codeWidth / 2 + marginX + pauseW + marginX,
|
||||||
|
codeHeight / 2,
|
||||||
|
codeWidth / 2 + marginX + pauseW + marginX,
|
||||||
|
-codeHeight / 2,
|
||||||
|
]);
|
||||||
|
arrowRight.endFill();
|
||||||
|
if (this.datas.hasBorder) {
|
||||||
|
codeRact.visible = true;
|
||||||
|
codeRact.lineStyle(
|
||||||
|
this.datas.borderWidth,
|
||||||
|
new Color(this.datas.borderColor)
|
||||||
|
);
|
||||||
|
codeRact.beginFill(new Color(this.datas.bodyColor));
|
||||||
|
codeRact.drawRect(codeX, codeY, codeWidth, codeHeight);
|
||||||
|
codeRact.endFill();
|
||||||
|
} else {
|
||||||
|
codeRact.visible = false;
|
||||||
|
}
|
||||||
|
// 运行方向控制箭头停止显隐
|
||||||
|
if (this.datas.trainDirection == 'right') {
|
||||||
|
this.arrowLeft.visible = false;
|
||||||
|
this.arrowRight.visible = true;
|
||||||
|
this.pauseLeft.visible = false;
|
||||||
|
this.pauseRight.visible = true;
|
||||||
|
} else {
|
||||||
|
this.arrowLeft.visible = true;
|
||||||
|
this.arrowRight.visible = false;
|
||||||
|
this.pauseLeft.visible = true;
|
||||||
|
this.pauseRight.visible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TrainTemplate extends JlGraphicTemplate<Train> {
|
||||||
|
trainDirection: string;
|
||||||
|
codeFontSize: number;
|
||||||
|
hasBorder: boolean;
|
||||||
|
borderWidth: number;
|
||||||
|
borderColor: string;
|
||||||
|
headColor: string;
|
||||||
|
codeColor: string;
|
||||||
|
bodyColor: string;
|
||||||
|
constructor() {
|
||||||
|
super(Train.Type);
|
||||||
|
this.trainDirection = 'left';
|
||||||
|
this.codeFontSize = trainConsts.codeFontSize;
|
||||||
|
this.hasBorder = true;
|
||||||
|
this.borderWidth = trainConsts.borderWidth;
|
||||||
|
this.borderColor = TrainColorEnum.borderColor;
|
||||||
|
this.headColor = TrainColorEnum.headColor;
|
||||||
|
this.codeColor = TrainColorEnum.codeColor;
|
||||||
|
this.bodyColor = TrainColorEnum.bodyColor;
|
||||||
|
}
|
||||||
|
new(): Train {
|
||||||
|
return new Train();
|
||||||
|
}
|
||||||
|
}
|
138
src/graphics/train/TrainDrawAssistant.ts
Normal file
138
src/graphics/train/TrainDrawAssistant.ts
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
import { Color, FederatedPointerEvent, Graphics, Point } from 'pixi.js';
|
||||||
|
import {
|
||||||
|
GraphicDrawAssistant,
|
||||||
|
GraphicInteractionPlugin,
|
||||||
|
JlDrawApp,
|
||||||
|
JlGraphic,
|
||||||
|
VectorText,
|
||||||
|
} from 'src/jlgraphic';
|
||||||
|
|
||||||
|
import { ITrainData, Train, TrainTemplate, trainConsts } from './Train';
|
||||||
|
|
||||||
|
export interface ITrainDrawOptions {
|
||||||
|
newData: () => ITrainData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TrainDraw extends GraphicDrawAssistant<TrainTemplate, ITrainData> {
|
||||||
|
point: Point = new Point(0, 0);
|
||||||
|
arrowLeft: Graphics = new Graphics();
|
||||||
|
pauseLeft: Graphics = new Graphics();
|
||||||
|
codeRact: Graphics = new Graphics();
|
||||||
|
|
||||||
|
constructor(app: JlDrawApp, createData: () => ITrainData) {
|
||||||
|
super(app, new TrainTemplate(), createData, Train.Type, '列车Train');
|
||||||
|
this.container.addChild(this.arrowLeft);
|
||||||
|
this.container.addChild(this.pauseLeft);
|
||||||
|
this.container.addChild(this.codeRact);
|
||||||
|
this.graphicTemplate.hasBorder = true;
|
||||||
|
trainInteraction.init(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
bind(): void {
|
||||||
|
super.bind();
|
||||||
|
}
|
||||||
|
unbind(): void {
|
||||||
|
super.unbind();
|
||||||
|
}
|
||||||
|
|
||||||
|
clearCache(): void {
|
||||||
|
this.arrowLeft.clear();
|
||||||
|
this.pauseLeft.clear();
|
||||||
|
this.codeRact.clear();
|
||||||
|
}
|
||||||
|
onLeftDown(e: FederatedPointerEvent): void {
|
||||||
|
const { x, y } = this.toCanvasCoordinates(e.global);
|
||||||
|
const p = new Point(x, y);
|
||||||
|
this.point = p;
|
||||||
|
this.createAndStore(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
redraw(p: Point): void {
|
||||||
|
const template = this.graphicTemplate;
|
||||||
|
const codeGraph = new VectorText(''); // 车号
|
||||||
|
codeGraph.setVectorFontSize(22);
|
||||||
|
codeGraph.anchor.set(0.5);
|
||||||
|
codeGraph.text = '01110111';
|
||||||
|
const style = { padding: 5 };
|
||||||
|
codeGraph.style = style;
|
||||||
|
const { width, height } = codeGraph.getLocalBounds();
|
||||||
|
codeGraph.destroy();
|
||||||
|
const marginX = trainConsts.marginX;
|
||||||
|
const pauseW = trainConsts.pauseW;
|
||||||
|
// 边框
|
||||||
|
if (template.hasBorder) {
|
||||||
|
const codeRact = this.codeRact;
|
||||||
|
codeRact.clear();
|
||||||
|
codeRact.lineStyle(template.borderWidth, new Color(template.borderColor));
|
||||||
|
codeRact.beginFill(new Color(template.bodyColor));
|
||||||
|
codeRact.drawRect(-width / 2, -height / 2, width, height);
|
||||||
|
codeRact.endFill();
|
||||||
|
codeRact.position.set(p.x, p.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 箭头
|
||||||
|
const arrowLeft = this.arrowLeft;
|
||||||
|
arrowLeft.clear();
|
||||||
|
this.point.set(p.x, p.y);
|
||||||
|
arrowLeft.beginFill(template.headColor, 1);
|
||||||
|
arrowLeft.drawPolygon([
|
||||||
|
-height * 0.4 - marginX - pauseW - marginX - width / 2,
|
||||||
|
0,
|
||||||
|
-marginX - pauseW - marginX - width / 2,
|
||||||
|
height / 2,
|
||||||
|
-marginX - pauseW - marginX - width / 2,
|
||||||
|
-height / 2,
|
||||||
|
]);
|
||||||
|
arrowLeft.endFill();
|
||||||
|
arrowLeft.position.set(this.point.x, this.point.y);
|
||||||
|
|
||||||
|
// 停止框
|
||||||
|
const pauseLeft = this.pauseLeft;
|
||||||
|
pauseLeft.clear();
|
||||||
|
pauseLeft.beginFill(template.headColor, 1);
|
||||||
|
pauseLeft.drawRect(0, 0, pauseW, height);
|
||||||
|
pauseLeft.endFill();
|
||||||
|
pauseLeft.position.set(
|
||||||
|
this.point.x - marginX - pauseW - width / 2,
|
||||||
|
this.point.y - height / 2
|
||||||
|
);
|
||||||
|
}
|
||||||
|
prepareData(data: ITrainData): boolean {
|
||||||
|
const template = this.graphicTemplate;
|
||||||
|
data.code = '01110111';
|
||||||
|
data.codeColor = template.codeColor;
|
||||||
|
data.codeFontSize = template.codeFontSize;
|
||||||
|
data.hasBorder = template.hasBorder;
|
||||||
|
data.trainDirection = template.trainDirection;
|
||||||
|
data.point = this.point;
|
||||||
|
data.borderWidth = template.borderWidth;
|
||||||
|
data.borderColor = template.borderColor;
|
||||||
|
data.headColor = template.headColor;
|
||||||
|
data.bodyColor = template.bodyColor;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class trainInteraction extends GraphicInteractionPlugin<Train> {
|
||||||
|
static Name = 'train_transform';
|
||||||
|
constructor(app: JlDrawApp) {
|
||||||
|
super(trainInteraction.Name, app);
|
||||||
|
}
|
||||||
|
static init(app: JlDrawApp) {
|
||||||
|
return new trainInteraction(app);
|
||||||
|
}
|
||||||
|
filter(...grahpics: JlGraphic[]): Train[] | undefined {
|
||||||
|
return grahpics.filter((g) => g.type === Train.Type).map((g) => g as Train);
|
||||||
|
}
|
||||||
|
bind(g: Train): void {
|
||||||
|
g.eventMode = 'static';
|
||||||
|
g.cursor = 'pointer';
|
||||||
|
g.scalable = true;
|
||||||
|
g.rotatable = true;
|
||||||
|
}
|
||||||
|
unbind(g: Train): void {
|
||||||
|
g.eventMode = 'none';
|
||||||
|
g.scalable = false;
|
||||||
|
g.rotatable = false;
|
||||||
|
}
|
||||||
|
}
|
@ -45,7 +45,6 @@ export abstract class GraphicDrawAssistant<
|
|||||||
icon: string; // 界面显示的图标
|
icon: string; // 界面显示的图标
|
||||||
container: Container = new Container();
|
container: Container = new Container();
|
||||||
graphicTemplate: GT;
|
graphicTemplate: GT;
|
||||||
createGraphicData: () => GD;
|
|
||||||
|
|
||||||
escListener: KeyListener = new KeyListener({
|
escListener: KeyListener = new KeyListener({
|
||||||
value: 'Escape',
|
value: 'Escape',
|
||||||
@ -61,7 +60,6 @@ export abstract class GraphicDrawAssistant<
|
|||||||
constructor(
|
constructor(
|
||||||
graphicApp: JlDrawApp,
|
graphicApp: JlDrawApp,
|
||||||
graphicTemplate: GT,
|
graphicTemplate: GT,
|
||||||
createGraphicData: () => GD,
|
|
||||||
icon: string,
|
icon: string,
|
||||||
description: string
|
description: string
|
||||||
) {
|
) {
|
||||||
@ -69,7 +67,6 @@ export abstract class GraphicDrawAssistant<
|
|||||||
this.app = graphicApp;
|
this.app = graphicApp;
|
||||||
this.type = graphicTemplate.type;
|
this.type = graphicTemplate.type;
|
||||||
this.graphicTemplate = graphicTemplate;
|
this.graphicTemplate = graphicTemplate;
|
||||||
this.createGraphicData = createGraphicData;
|
|
||||||
this.icon = icon;
|
this.icon = icon;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
this.app.registerGraphicTemplates(this.graphicTemplate);
|
this.app.registerGraphicTemplates(this.graphicTemplate);
|
||||||
@ -102,7 +99,9 @@ export abstract class GraphicDrawAssistant<
|
|||||||
unbind(): void {
|
unbind(): void {
|
||||||
this.clearCache();
|
this.clearCache();
|
||||||
const canvas = this.canvas;
|
const canvas = this.canvas;
|
||||||
canvas.removeChild(this.container);
|
if (this.container?.parent) {
|
||||||
|
canvas.removeChild(this.container);
|
||||||
|
}
|
||||||
canvas.off('mousedown', this.onLeftDown, this);
|
canvas.off('mousedown', this.onLeftDown, this);
|
||||||
canvas.off('mousemove', this.onMouseMove, this);
|
canvas.off('mousemove', this.onMouseMove, this);
|
||||||
canvas.off('mouseup', this.onLeftUp, this);
|
canvas.off('mouseup', this.onLeftUp, this);
|
||||||
@ -161,7 +160,7 @@ export abstract class GraphicDrawAssistant<
|
|||||||
* 创建并添加到图形App
|
* 创建并添加到图形App
|
||||||
*/
|
*/
|
||||||
createAndStore(finish: boolean): JlGraphic | null {
|
createAndStore(finish: boolean): JlGraphic | null {
|
||||||
const data = this.createGraphicData();
|
const data = this.graphicTemplate.datas as GD;
|
||||||
data.id = this.nextId();
|
data.id = this.nextId();
|
||||||
data.graphicType = this.graphicTemplate.type;
|
data.graphicType = this.graphicTemplate.type;
|
||||||
if (!this.prepareData(data)) {
|
if (!this.prepareData(data)) {
|
||||||
@ -457,13 +456,13 @@ export class JlDrawApp extends GraphicApp {
|
|||||||
* 删除选中图形对象
|
* 删除选中图形对象
|
||||||
*/
|
*/
|
||||||
deleteSelectedGraphics() {
|
deleteSelectedGraphics() {
|
||||||
const deletes = this.selectedGraphics.slice(
|
const deletes = this.deleteGraphics(...this.selectedGraphics);
|
||||||
0,
|
if (deletes.length > 0) {
|
||||||
this.selectedGraphics.length
|
// 删除图形对象操作记录
|
||||||
);
|
this.opRecord.record(new GraphicDeleteOperation(this, deletes));
|
||||||
this.deleteGraphics(...this.selectedGraphics);
|
} else {
|
||||||
// 删除图形对象操作记录
|
console.debug('没有删除元素,不记录');
|
||||||
this.opRecord.record(new GraphicDeleteOperation(this, deletes));
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateCanvasAndRecord(data: ICanvasProperties) {
|
updateCanvasAndRecord(data: ICanvasProperties) {
|
||||||
|
@ -288,6 +288,7 @@ export interface GraphicAppEvents extends GlobalMixins.GraphicAppEvents {
|
|||||||
drag_op_end: [event: AppDragEvent];
|
drag_op_end: [event: AppDragEvent];
|
||||||
'pre-menu-handle': [menu: MenuItemOptions];
|
'pre-menu-handle': [menu: MenuItemOptions];
|
||||||
'post-menu-handle': [menu: MenuItemOptions];
|
'post-menu-handle': [menu: MenuItemOptions];
|
||||||
|
'websocket-state-change': [app: GraphicApp, connected: boolean];
|
||||||
destroy: [app: GraphicApp];
|
destroy: [app: GraphicApp];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -319,6 +320,11 @@ export interface IGraphicAppConfig {
|
|||||||
* 超出屏幕显示范围是否剪裁,默认true
|
* 超出屏幕显示范围是否剪裁,默认true
|
||||||
*/
|
*/
|
||||||
cullable?: boolean;
|
cullable?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否支持删除操作
|
||||||
|
*/
|
||||||
|
isSupportDeletion?: (g: JlGraphic) => boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -527,7 +533,10 @@ export class GraphicApp extends EventEmitter<GraphicAppEvents> {
|
|||||||
*/
|
*/
|
||||||
handleGraphicStates(graphicStates: GraphicState[]) {
|
handleGraphicStates(graphicStates: GraphicState[]) {
|
||||||
graphicStates.forEach((state) => {
|
graphicStates.forEach((state) => {
|
||||||
const list = this.queryStore.queryByIdOrCode(state.code);
|
const list = this.queryStore.queryByIdOrCodeAndType(
|
||||||
|
state.code,
|
||||||
|
state.graphicType
|
||||||
|
);
|
||||||
if (list.length == 0) {
|
if (list.length == 0) {
|
||||||
const template = this.getGraphicTemplatesByType(state.graphicType);
|
const template = this.getGraphicTemplatesByType(state.graphicType);
|
||||||
const g = template.new();
|
const g = template.new();
|
||||||
@ -630,7 +639,7 @@ export class GraphicApp extends EventEmitter<GraphicAppEvents> {
|
|||||||
this.addGraphics(g);
|
this.addGraphics(g);
|
||||||
});
|
});
|
||||||
// 加载数据关系
|
// 加载数据关系
|
||||||
this.graphicStore.getAllGraphics().forEach((g) => g.loadRealtions());
|
this.graphicStore.getAllGraphics().forEach((g) => g.loadRelations());
|
||||||
// 更新id生成器
|
// 更新id生成器
|
||||||
const max =
|
const max =
|
||||||
this.graphicStore
|
this.graphicStore
|
||||||
@ -725,8 +734,19 @@ export class GraphicApp extends EventEmitter<GraphicAppEvents> {
|
|||||||
* 删除图形
|
* 删除图形
|
||||||
* @param graphics 图形对象
|
* @param graphics 图形对象
|
||||||
*/
|
*/
|
||||||
deleteGraphics(...graphics: JlGraphic[]) {
|
deleteGraphics(...graphics: JlGraphic[]): JlGraphic[] {
|
||||||
graphics.forEach((g) => this.doDeleteGraphics(g));
|
const dels = graphics.filter((g) => {
|
||||||
|
if (
|
||||||
|
this._options?.isSupportDeletion == undefined ||
|
||||||
|
this._options.isSupportDeletion(g)
|
||||||
|
) {
|
||||||
|
this.doDeleteGraphics(g);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
console.debug(`type=${g.type},id=${g.id}的图形不支持删除`);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
return dels;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -888,10 +908,10 @@ export class GraphicApp extends EventEmitter<GraphicAppEvents> {
|
|||||||
this.emit('destroy', this);
|
this.emit('destroy', this);
|
||||||
if (this.wsMsgBroker) {
|
if (this.wsMsgBroker) {
|
||||||
this.wsMsgBroker.close();
|
this.wsMsgBroker.close();
|
||||||
if (!StompCli.hasAppMsgBroker()) {
|
// if (!StompCli.hasAppMsgBroker()) {
|
||||||
// 如果没有其他消息代理,关闭websocket Stomp客户端
|
// // 如果没有其他消息代理,关闭websocket Stomp客户端
|
||||||
StompCli.close();
|
// StompCli.close();
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
this.interactionPluginMap.forEach((plugin) => {
|
this.interactionPluginMap.forEach((plugin) => {
|
||||||
plugin.pause();
|
plugin.pause();
|
||||||
|
@ -37,6 +37,12 @@ export interface GraphicQueryStore {
|
|||||||
* @param v
|
* @param v
|
||||||
*/
|
*/
|
||||||
queryByIdOrCode(v: string): JlGraphic[];
|
queryByIdOrCode(v: string): JlGraphic[];
|
||||||
|
/**
|
||||||
|
* 根据id或code及类型查询图形
|
||||||
|
* @param v
|
||||||
|
* @param type
|
||||||
|
*/
|
||||||
|
queryByIdOrCodeAndType(v: string, type: string): JlGraphic[];
|
||||||
/**
|
/**
|
||||||
* 根据code和类型获取图形
|
* 根据code和类型获取图形
|
||||||
* @param code
|
* @param code
|
||||||
@ -69,6 +75,7 @@ export class GraphicStore implements GraphicQueryStore {
|
|||||||
this.store = new Map<string, JlGraphic>();
|
this.store = new Map<string, JlGraphic>();
|
||||||
this.relationManage = new RelationManage(app);
|
this.relationManage = new RelationManage(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取所有图形对象
|
* 获取所有图形对象
|
||||||
*/
|
*/
|
||||||
@ -125,6 +132,15 @@ export class GraphicStore implements GraphicQueryStore {
|
|||||||
});
|
});
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
queryByIdOrCodeAndType(s: string, type: string): JlGraphic[] {
|
||||||
|
const list: JlGraphic[] = [];
|
||||||
|
this.store.forEach((g) => {
|
||||||
|
if (g.isIdOrCode(s) && g.type === type) {
|
||||||
|
list.push(g);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return list;
|
||||||
|
}
|
||||||
queryByCodeAndType<T extends JlGraphic>(
|
queryByCodeAndType<T extends JlGraphic>(
|
||||||
code: string,
|
code: string,
|
||||||
type: string
|
type: string
|
||||||
|
@ -148,6 +148,23 @@ DisplayObject.prototype.getAllParentScaled =
|
|||||||
});
|
});
|
||||||
return scaled;
|
return scaled;
|
||||||
};
|
};
|
||||||
|
DisplayObject.prototype.getPositionOnCanvas =
|
||||||
|
function getPositionOnCanvas(): Point {
|
||||||
|
if (this.parent.isCanvas()) {
|
||||||
|
return this.position;
|
||||||
|
} else {
|
||||||
|
return this.parent.localToCanvasPoint(this.position);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
DisplayObject.prototype.updatePositionByCanvasPosition =
|
||||||
|
function updatePositionByCanvasPosition(p: IPointData): void {
|
||||||
|
if (this.parent.isCanvas()) {
|
||||||
|
this.position.copyFrom(p);
|
||||||
|
} else {
|
||||||
|
const localPosition = this.parent.canvasToLocalPoint(p);
|
||||||
|
this.position.copyFrom(localPosition);
|
||||||
|
}
|
||||||
|
};
|
||||||
DisplayObject.prototype.saveTransform = function saveTransform() {
|
DisplayObject.prototype.saveTransform = function saveTransform() {
|
||||||
return GraphicTransform.fromObject(this);
|
return GraphicTransform.fromObject(this);
|
||||||
};
|
};
|
||||||
@ -755,7 +772,7 @@ export abstract class JlGraphic extends Container {
|
|||||||
/**
|
/**
|
||||||
* 从数据加载恢复图形关系
|
* 从数据加载恢复图形关系
|
||||||
*/
|
*/
|
||||||
loadRealtions() {}
|
loadRelations() {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取当前图形的所有图形关系
|
* 获取当前图形的所有图形关系
|
||||||
@ -923,15 +940,47 @@ export abstract class JlGraphic extends Container {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type CreateData = () => GraphicData;
|
||||||
|
export type CreateState = () => GraphicState;
|
||||||
|
|
||||||
|
export interface IGraphicTemplateOptions {
|
||||||
|
dataTemplate?: GraphicData;
|
||||||
|
stateTemplate?: GraphicState;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 图形对象模板
|
* 图形对象模板
|
||||||
*/
|
*/
|
||||||
export abstract class JlGraphicTemplate<G extends JlGraphic> {
|
export abstract class JlGraphicTemplate<G extends JlGraphic> {
|
||||||
readonly type: string;
|
readonly type: string;
|
||||||
|
options: IGraphicTemplateOptions;
|
||||||
|
|
||||||
constructor(type: string) {
|
constructor(type: string, options: IGraphicTemplateOptions) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
this.options = options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get datas(): GraphicData {
|
||||||
|
if (this.options.dataTemplate) {
|
||||||
|
return this.options.dataTemplate.clone();
|
||||||
|
}
|
||||||
|
throw new Error(`type=${this.type}的图形模板没有数据模板`);
|
||||||
|
}
|
||||||
|
|
||||||
|
get states(): GraphicState {
|
||||||
|
if (this.options.stateTemplate) {
|
||||||
|
return this.options.stateTemplate.clone();
|
||||||
|
}
|
||||||
|
throw new Error(`type=${this.type}的图形模板没有状态模板`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// getDataCreator<T extends CreateData>(): T {
|
||||||
|
// return this.options.dataCreator as T;
|
||||||
|
// }
|
||||||
|
// getStateCreator<T extends CreateState>(): T {
|
||||||
|
// return this.options.stateCreator as T;
|
||||||
|
// }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化一个新的图形对象
|
* 初始化一个新的图形对象
|
||||||
*/
|
*/
|
||||||
|
70
src/jlgraphic/global.d.ts
vendored
70
src/jlgraphic/global.d.ts
vendored
@ -60,9 +60,28 @@ declare namespace GlobalMixins {
|
|||||||
_rotatable: boolean; // 是否可旋转
|
_rotatable: boolean; // 是否可旋转
|
||||||
rotatable: boolean;
|
rotatable: boolean;
|
||||||
worldAngle: number; // 世界角度,(-180, 180]
|
worldAngle: number; // 世界角度,(-180, 180]
|
||||||
|
/**
|
||||||
|
* 获取所有父级元素叠加缩放
|
||||||
|
*/
|
||||||
getAllParentScaled(): PointType;
|
getAllParentScaled(): PointType;
|
||||||
saveTransform(): GraphicTransform; // 保存变换
|
/**
|
||||||
loadTransform(transform: GraphicTransform): void; // 加载变换
|
* 获取位置在画布的坐标
|
||||||
|
*/
|
||||||
|
getPositionOnCanvas(): PointType;
|
||||||
|
/**
|
||||||
|
* 通过画布坐标更新位置
|
||||||
|
* @param p 画布坐标
|
||||||
|
*/
|
||||||
|
updatePositionByCanvasPosition(p: IPointData): void;
|
||||||
|
/**
|
||||||
|
* 保存变换数据
|
||||||
|
*/
|
||||||
|
saveTransform(): GraphicTransform;
|
||||||
|
/**
|
||||||
|
* 加载变换
|
||||||
|
* @param transform 变换数据
|
||||||
|
*/
|
||||||
|
loadTransform(transform: GraphicTransform): void;
|
||||||
isChild(obj: DisplayObject): boolean; // 是否子元素
|
isChild(obj: DisplayObject): boolean; // 是否子元素
|
||||||
isParent(obj: DisplayObject): boolean; // 是否父元素
|
isParent(obj: DisplayObject): boolean; // 是否父元素
|
||||||
isAssistantAppend(): boolean; // 是否辅助附加图形
|
isAssistantAppend(): boolean; // 是否辅助附加图形
|
||||||
@ -83,14 +102,45 @@ declare namespace GlobalMixins {
|
|||||||
isCanvas(): boolean; // 是否画布对象
|
isCanvas(): boolean; // 是否画布对象
|
||||||
getViewport(): Viewport; // 获取视口
|
getViewport(): Viewport; // 获取视口
|
||||||
getGraphicApp(): GraphicApp; // 获取图形app
|
getGraphicApp(): GraphicApp; // 获取图形app
|
||||||
localToCanvasPoint(p: IPointData): PointType; // 图形本地坐标转为画布坐标
|
/**
|
||||||
localToCanvasPoints(...points: IPointData[]): PointType[]; // 批量转换
|
* 图形本地坐标转为画布坐标
|
||||||
canvasToLocalPoint(p: IPointData): PointType; // 画布坐标转为图形本地坐标
|
* @param p 图形本地坐标
|
||||||
canvasToLocalPoints(...points: IPointData[]): PointType[]; // 批量转换
|
*/
|
||||||
|
localToCanvasPoint(p: IPointData): PointType;
|
||||||
localToScreenPoint(p: IPointData): PointType; // 本地坐标转为屏幕坐标
|
/**
|
||||||
localToScreenPoints(...points: IPointData[]): PointType[]; // 批量
|
* 批量转换图形本地坐标为画布坐标
|
||||||
screenToLocalPoint(p: IPointData): PointType; // 屏幕坐标转为本地坐标
|
* @param points 图形本地坐标
|
||||||
|
*/
|
||||||
|
localToCanvasPoints(...points: IPointData[]): PointType[];
|
||||||
|
/**
|
||||||
|
* 画布坐标转为图形本地坐标
|
||||||
|
* @param p 画布坐标
|
||||||
|
*/
|
||||||
|
canvasToLocalPoint(p: IPointData): PointType;
|
||||||
|
/**
|
||||||
|
* 批量转换画布坐标为图形本地坐标
|
||||||
|
* @param points 画布坐标
|
||||||
|
*/
|
||||||
|
canvasToLocalPoints(...points: IPointData[]): PointType[];
|
||||||
|
/**
|
||||||
|
* 本地坐标转为屏幕坐标
|
||||||
|
* @param p 本地坐标
|
||||||
|
*/
|
||||||
|
localToScreenPoint(p: IPointData): PointType;
|
||||||
|
/**
|
||||||
|
* 批量转换本地坐标为屏幕坐标
|
||||||
|
* @param points 本地坐标
|
||||||
|
*/
|
||||||
|
localToScreenPoints(...points: IPointData[]): PointType[];
|
||||||
|
/**
|
||||||
|
* 屏幕坐标转为本地坐标
|
||||||
|
* @param p 屏幕坐标
|
||||||
|
*/
|
||||||
|
screenToLocalPoint(p: IPointData): PointType;
|
||||||
|
/**
|
||||||
|
* 批量转换屏幕坐标为本地坐标
|
||||||
|
* @param points 屏幕坐标
|
||||||
|
*/
|
||||||
screenToLocalPoints(...points: IPointData[]): PointType[]; // 批量
|
screenToLocalPoints(...points: IPointData[]): PointType[]; // 批量
|
||||||
|
|
||||||
localBoundsToCanvasPoints(): PointType[]; // 本地包围框转为多边形点坐标
|
localBoundsToCanvasPoints(): PointType[]; // 本地包围框转为多边形点坐标
|
||||||
|
@ -11,6 +11,8 @@ import {
|
|||||||
calculateFootPointFromPointToLine,
|
calculateFootPointFromPointToLine,
|
||||||
calculateIntersectionPointOfCircleAndPoint,
|
calculateIntersectionPointOfCircleAndPoint,
|
||||||
distance,
|
distance,
|
||||||
|
distance2,
|
||||||
|
isLineContainOther,
|
||||||
linePoint,
|
linePoint,
|
||||||
} from '../utils';
|
} from '../utils';
|
||||||
import { VectorGraphic, VectorGraphicUtil } from './VectorGraphic';
|
import { VectorGraphic, VectorGraphicUtil } from './VectorGraphic';
|
||||||
@ -19,12 +21,25 @@ import { VectorGraphic, VectorGraphicUtil } from './VectorGraphic';
|
|||||||
* 抽象可吸附位置
|
* 抽象可吸附位置
|
||||||
*/
|
*/
|
||||||
export interface AbsorbablePosition extends Container {
|
export interface AbsorbablePosition extends Container {
|
||||||
|
/**
|
||||||
|
* 是否与另一个可吸附位置重叠(相似,但可能范围不同)
|
||||||
|
* @param other
|
||||||
|
*/
|
||||||
|
isOverlapping(other: AbsorbablePosition): boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 与另一个相似的吸附位置比较范围大小
|
||||||
|
* @param other
|
||||||
|
* @returns >0此吸附范围大,<0另一个吸附范围大,=0两个吸附范围一样大
|
||||||
|
*/
|
||||||
|
compareTo(other: AbsorbablePosition): number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 尝试吸附图形对象
|
* 尝试吸附图形对象
|
||||||
* @param objs 图形对象列表
|
* @param objs 图形对象列表
|
||||||
* @returns 如果吸附成功,返回true,否则false
|
* @returns 如果吸附成功,返回true,否则false
|
||||||
*/
|
*/
|
||||||
tryAbsorb(...objs: DisplayObject[]): boolean;
|
tryAbsorb(...objs: DisplayObject[]): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -57,6 +72,11 @@ export default class AbsorbablePoint
|
|||||||
absorbRange: number;
|
absorbRange: number;
|
||||||
scaledListenerOn = false;
|
scaledListenerOn = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param point 画布坐标
|
||||||
|
* @param absorbRange
|
||||||
|
*/
|
||||||
constructor(point: IPointData, absorbRange = 10) {
|
constructor(point: IPointData, absorbRange = 10) {
|
||||||
super(AbsorbablePointGraphic.geometry);
|
super(AbsorbablePointGraphic.geometry);
|
||||||
this._point = new Point(point.x, point.y);
|
this._point = new Point(point.x, point.y);
|
||||||
@ -65,19 +85,37 @@ export default class AbsorbablePoint
|
|||||||
this.interactive;
|
this.interactive;
|
||||||
VectorGraphicUtil.handle(this);
|
VectorGraphicUtil.handle(this);
|
||||||
}
|
}
|
||||||
tryAbsorb(...objs: DisplayObject[]): boolean {
|
compareTo(other: AbsorbablePosition): number {
|
||||||
for (let i = 0; i < objs.length; i++) {
|
if (other instanceof AbsorbablePoint) {
|
||||||
const obj = objs[i];
|
return this.absorbRange - other.absorbRange;
|
||||||
if (
|
}
|
||||||
distance(this._point.x, this._point.y, obj.position.x, obj.position.y) <
|
throw new Error('非可吸附点');
|
||||||
this.absorbRange
|
}
|
||||||
) {
|
isOverlapping(other: AbsorbablePosition): boolean {
|
||||||
obj.position.copyFrom(this._point);
|
if (other instanceof AbsorbablePoint) {
|
||||||
return true;
|
return (
|
||||||
}
|
this._point.equals(other._point) &&
|
||||||
|
this.absorbRange === other.absorbRange
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
tryAbsorb(...objs: DisplayObject[]): void {
|
||||||
|
for (let i = 0; i < objs.length; i++) {
|
||||||
|
const obj = objs[i];
|
||||||
|
const canvasPosition = obj.getPositionOnCanvas();
|
||||||
|
if (
|
||||||
|
distance(
|
||||||
|
this._point.x,
|
||||||
|
this._point.y,
|
||||||
|
canvasPosition.x,
|
||||||
|
canvasPosition.y
|
||||||
|
) < this.absorbRange
|
||||||
|
) {
|
||||||
|
obj.updatePositionByCanvasPosition(this._point);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
updateOnScaled() {
|
updateOnScaled() {
|
||||||
const scaled = this.getAllParentScaled();
|
const scaled = this.getAllParentScaled();
|
||||||
const scale = Math.max(scaled.x, scaled.y);
|
const scale = Math.max(scaled.x, scaled.y);
|
||||||
@ -94,6 +132,12 @@ export class AbsorbableLine extends Graphics implements AbsorbablePosition {
|
|||||||
absorbRange: number;
|
absorbRange: number;
|
||||||
_color = '#E77E0E';
|
_color = '#E77E0E';
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param p1 画布坐标
|
||||||
|
* @param p2 画布坐标
|
||||||
|
* @param absorbRange
|
||||||
|
*/
|
||||||
constructor(p1: IPointData, p2: IPointData, absorbRange = 20) {
|
constructor(p1: IPointData, p2: IPointData, absorbRange = 20) {
|
||||||
super();
|
super();
|
||||||
this.p1 = new Point(p1.x, p1.y);
|
this.p1 = new Point(p1.x, p1.y);
|
||||||
@ -101,6 +145,22 @@ export class AbsorbableLine extends Graphics implements AbsorbablePosition {
|
|||||||
this.absorbRange = absorbRange;
|
this.absorbRange = absorbRange;
|
||||||
this.redraw();
|
this.redraw();
|
||||||
}
|
}
|
||||||
|
isOverlapping(other: AbsorbablePosition): boolean {
|
||||||
|
if (other instanceof AbsorbableLine) {
|
||||||
|
const contain = isLineContainOther(
|
||||||
|
{ p1: this.p1, p2: this.p2 },
|
||||||
|
{ p1: other.p1, p2: other.p2 }
|
||||||
|
);
|
||||||
|
return contain;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
compareTo(other: AbsorbablePosition): number {
|
||||||
|
if (other instanceof AbsorbableLine) {
|
||||||
|
return distance2(this.p1, this.p2) - distance2(other.p1, other.p2);
|
||||||
|
}
|
||||||
|
throw new Error('非可吸附线');
|
||||||
|
}
|
||||||
redraw() {
|
redraw() {
|
||||||
this.clear();
|
this.clear();
|
||||||
this.lineStyle(1, new Color(this._color));
|
this.lineStyle(1, new Color(this._color));
|
||||||
@ -108,17 +168,19 @@ export class AbsorbableLine extends Graphics implements AbsorbablePosition {
|
|||||||
this.lineTo(this.p2.x, this.p2.y);
|
this.lineTo(this.p2.x, this.p2.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
tryAbsorb(...objs: DisplayObject[]): boolean {
|
tryAbsorb(...objs: DisplayObject[]): void {
|
||||||
for (let i = 0; i < objs.length; i++) {
|
for (let i = 0; i < objs.length; i++) {
|
||||||
const obj = objs[i];
|
const obj = objs[i];
|
||||||
const p = obj.position.clone();
|
const canvasPosition = obj.getPositionOnCanvas();
|
||||||
if (linePoint(this.p1, this.p2, p, this.absorbRange, true)) {
|
if (linePoint(this.p1, this.p2, canvasPosition, this.absorbRange, true)) {
|
||||||
const fp = calculateFootPointFromPointToLine(this.p1, this.p2, p);
|
const fp = calculateFootPointFromPointToLine(
|
||||||
obj.position.copyFrom(fp);
|
this.p1,
|
||||||
return true;
|
this.p2,
|
||||||
|
canvasPosition
|
||||||
|
);
|
||||||
|
obj.updatePositionByCanvasPosition(fp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,6 +193,12 @@ export class AbsorbableCircle extends Graphics implements AbsorbablePosition {
|
|||||||
radius: number;
|
radius: number;
|
||||||
_color = '#E77E0E';
|
_color = '#E77E0E';
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param p 画布坐标
|
||||||
|
* @param radius
|
||||||
|
* @param absorbRange
|
||||||
|
*/
|
||||||
constructor(p: IPointData, radius: number, absorbRange = 10) {
|
constructor(p: IPointData, radius: number, absorbRange = 10) {
|
||||||
super();
|
super();
|
||||||
this.p0 = new Point(p.x, p.y);
|
this.p0 = new Point(p.x, p.y);
|
||||||
@ -138,6 +206,18 @@ export class AbsorbableCircle extends Graphics implements AbsorbablePosition {
|
|||||||
this.absorbRange = absorbRange;
|
this.absorbRange = absorbRange;
|
||||||
this.redraw();
|
this.redraw();
|
||||||
}
|
}
|
||||||
|
isOverlapping(other: AbsorbablePosition): boolean {
|
||||||
|
if (other instanceof AbsorbableCircle) {
|
||||||
|
return this.p0.equals(other.p0) && this.radius === other.radius;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
compareTo(other: AbsorbablePosition): number {
|
||||||
|
if (other instanceof AbsorbableCircle) {
|
||||||
|
return this.absorbRange - other.absorbRange;
|
||||||
|
}
|
||||||
|
throw new Error('非可吸附圆');
|
||||||
|
}
|
||||||
|
|
||||||
redraw() {
|
redraw() {
|
||||||
this.clear();
|
this.clear();
|
||||||
@ -145,14 +225,15 @@ export class AbsorbableCircle extends Graphics implements AbsorbablePosition {
|
|||||||
this.drawCircle(this.p0.x, this.p0.y, this.radius);
|
this.drawCircle(this.p0.x, this.p0.y, this.radius);
|
||||||
}
|
}
|
||||||
|
|
||||||
tryAbsorb(...objs: DisplayObject[]): boolean {
|
tryAbsorb(...objs: DisplayObject[]): void {
|
||||||
for (let i = 0; i < objs.length; i++) {
|
for (let i = 0; i < objs.length; i++) {
|
||||||
const obj = objs[i];
|
const obj = objs[i];
|
||||||
|
const canvasPosition = obj.getPositionOnCanvas();
|
||||||
const len = distance(
|
const len = distance(
|
||||||
this.p0.x,
|
this.p0.x,
|
||||||
this.p0.y,
|
this.p0.y,
|
||||||
obj.position.x,
|
canvasPosition.x,
|
||||||
obj.position.y
|
canvasPosition.y
|
||||||
);
|
);
|
||||||
if (
|
if (
|
||||||
len > this.radius - this.absorbRange &&
|
len > this.radius - this.absorbRange &&
|
||||||
@ -162,12 +243,10 @@ export class AbsorbableCircle extends Graphics implements AbsorbablePosition {
|
|||||||
const p = calculateIntersectionPointOfCircleAndPoint(
|
const p = calculateIntersectionPointOfCircleAndPoint(
|
||||||
this.p0,
|
this.p0,
|
||||||
this.radius,
|
this.radius,
|
||||||
obj.position
|
canvasPosition
|
||||||
);
|
);
|
||||||
obj.position.copyFrom(p);
|
obj.updatePositionByCanvasPosition(p);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,9 +9,20 @@ import type { GraphicApp } from '../app/JlGraphicApp';
|
|||||||
import { GraphicState } from '../core/JlGraphic';
|
import { GraphicState } from '../core/JlGraphic';
|
||||||
|
|
||||||
export interface StompCliOption {
|
export interface StompCliOption {
|
||||||
wsUrl: string; // websocket url
|
/**
|
||||||
token: string; // 认证token
|
* websocket url地址
|
||||||
reconnectDelay?: number; // 重连延时,默认3秒
|
*/
|
||||||
|
wsUrl: string;
|
||||||
|
/**
|
||||||
|
* 认证token
|
||||||
|
*/
|
||||||
|
token?: string;
|
||||||
|
/**
|
||||||
|
* 认证失败处理
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
onAuthenticationFailed?: () => void;
|
||||||
|
reconnectDelay?: number; // 重连延时,默认3秒,设置为0不重连.
|
||||||
heartbeatIncoming?: number; // 服务端过来的心跳间隔,默认30秒
|
heartbeatIncoming?: number; // 服务端过来的心跳间隔,默认30秒
|
||||||
heartbeatOutgoing?: number; // 到服务端的心跳间隔,默认30秒
|
heartbeatOutgoing?: number; // 到服务端的心跳间隔,默认30秒
|
||||||
}
|
}
|
||||||
@ -25,28 +36,28 @@ const DefaultStompOption: StompCliOption = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export class StompCli {
|
export class StompCli {
|
||||||
private static enabled = false;
|
|
||||||
private static options: StompCliOption;
|
|
||||||
private static client: StompClient;
|
private static client: StompClient;
|
||||||
|
private static options: StompCliOption;
|
||||||
private static appMsgBroker: AppWsMsgBroker[] = [];
|
private static appMsgBroker: AppWsMsgBroker[] = [];
|
||||||
|
/**
|
||||||
|
* key-订阅路径
|
||||||
|
*/
|
||||||
|
subscriptions: Map<string, AppStateSubscription> = new Map<
|
||||||
|
string,
|
||||||
|
AppStateSubscription
|
||||||
|
>();
|
||||||
private static connected = false;
|
private static connected = false;
|
||||||
static new(options: StompCliOption) {
|
static new(options: StompCliOption) {
|
||||||
if (StompCli.enabled) {
|
if (StompCli.client) {
|
||||||
// 以及启用
|
// 已经创建
|
||||||
return;
|
return;
|
||||||
// throw new Error('websocket 已连接,若确实需要重新连接,请先断开StompCli.close再重新StompCli.new')
|
|
||||||
}
|
}
|
||||||
StompCli.enabled = true;
|
|
||||||
StompCli.options = Object.assign({}, DefaultStompOption, options);
|
StompCli.options = Object.assign({}, DefaultStompOption, options);
|
||||||
StompCli.client = new StompClient({
|
StompCli.client = new StompClient({
|
||||||
brokerURL: StompCli.options.wsUrl,
|
brokerURL: StompCli.options.wsUrl,
|
||||||
connectHeaders: {
|
connectHeaders: {
|
||||||
Authorization: StompCli.options.token,
|
Authorization: StompCli.options.token ? StompCli.options.token : '',
|
||||||
// Authorization: ''
|
|
||||||
},
|
},
|
||||||
// debug: (str) => {
|
|
||||||
// console.log(str)
|
|
||||||
// }
|
|
||||||
reconnectDelay: StompCli.options.reconnectDelay,
|
reconnectDelay: StompCli.options.reconnectDelay,
|
||||||
heartbeatIncoming: StompCli.options.heartbeatIncoming,
|
heartbeatIncoming: StompCli.options.heartbeatIncoming,
|
||||||
heartbeatOutgoing: StompCli.options.heartbeatOutgoing,
|
heartbeatOutgoing: StompCli.options.heartbeatOutgoing,
|
||||||
@ -55,43 +66,47 @@ export class StompCli {
|
|||||||
StompCli.client.onConnect = () => {
|
StompCli.client.onConnect = () => {
|
||||||
// console.log('websocket连接(重连),重新订阅', StompCli.appMsgBroker.length)
|
// console.log('websocket连接(重连),重新订阅', StompCli.appMsgBroker.length)
|
||||||
StompCli.connected = true;
|
StompCli.connected = true;
|
||||||
|
StompCli.emitConnectStateChangeEvent();
|
||||||
StompCli.appMsgBroker.forEach((broker) => {
|
StompCli.appMsgBroker.forEach((broker) => {
|
||||||
broker.resubscribe();
|
broker.resubscribe();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
StompCli.client.onStompError = (frame: Frame) => {
|
StompCli.client.onStompError = (frame: Frame) => {
|
||||||
console.error(
|
const errMsg = frame.headers['message'];
|
||||||
'Stomp收到error消息,可能是认证失败(暂时没有判断具体错误类型,后需添加判断),关闭Stomp客户端',
|
if (errMsg === '401') {
|
||||||
frame
|
console.warn('认证失败,断开WebSocket连接');
|
||||||
);
|
StompCli.close();
|
||||||
StompCli.close();
|
if (StompCli.options.onAuthenticationFailed) {
|
||||||
|
StompCli.options.onAuthenticationFailed();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error('收到Stomp错误消息', frame);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
StompCli.client.onDisconnect = (frame: Frame) => {
|
StompCli.client.onDisconnect = (frame: Frame) => {
|
||||||
console.log('Stomp 断开连接', frame);
|
console.log('Stomp 断开连接', frame);
|
||||||
StompCli.connected = false;
|
StompCli.connected = false;
|
||||||
// StompCli.appMsgBroker.forEach(broker => {
|
StompCli.emitConnectStateChangeEvent();
|
||||||
// broker.close();
|
|
||||||
// });
|
|
||||||
};
|
};
|
||||||
StompCli.client.onWebSocketClose = (evt: CloseEvent) => {
|
StompCli.client.onWebSocketClose = (evt: CloseEvent) => {
|
||||||
console.log('websocket 关闭', evt);
|
console.log('websocket 关闭', evt);
|
||||||
StompCli.connected = false;
|
StompCli.connected = false;
|
||||||
|
StompCli.emitConnectStateChangeEvent();
|
||||||
};
|
};
|
||||||
// websocket错误处理
|
// websocket错误处理
|
||||||
StompCli.client.onWebSocketError = (err: Event) => {
|
StompCli.client.onWebSocketError = (err: Event) => {
|
||||||
console.log('websocket错误', err);
|
console.log('websocket错误', err);
|
||||||
// StompCli.appMsgBroker.forEach(broker => {
|
|
||||||
// broker.unsbuscribeAll();
|
|
||||||
// });
|
|
||||||
};
|
};
|
||||||
|
|
||||||
StompCli.client.activate();
|
StompCli.client.activate();
|
||||||
}
|
}
|
||||||
|
|
||||||
static isEnabled(): boolean {
|
static emitConnectStateChangeEvent() {
|
||||||
return StompCli.enabled;
|
StompCli.appMsgBroker.forEach((broker) => {
|
||||||
|
broker.app.emit('websocket-state-change', broker.app, StompCli.connected);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static isConnected(): boolean {
|
static isConnected(): boolean {
|
||||||
@ -129,7 +144,6 @@ export class StompCli {
|
|||||||
* 关闭websocket连接
|
* 关闭websocket连接
|
||||||
*/
|
*/
|
||||||
static close() {
|
static close() {
|
||||||
StompCli.enabled = false;
|
|
||||||
StompCli.connected = false;
|
StompCli.connected = false;
|
||||||
if (StompCli.client) {
|
if (StompCli.client) {
|
||||||
StompCli.client.deactivate();
|
StompCli.client.deactivate();
|
||||||
@ -138,21 +152,32 @@ export class StompCli {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 状态订阅消息转换器
|
// 状态订阅消息转换器
|
||||||
export type MessageConverter = (message: Uint8Array) => GraphicState[];
|
export type GraphicStateMessageConvert = (
|
||||||
|
message: Uint8Array
|
||||||
|
) => GraphicState[];
|
||||||
|
|
||||||
|
// 订阅消息处理器
|
||||||
|
export type SubscriptionMessageHandle = (message: Uint8Array) => void;
|
||||||
|
|
||||||
// 图形app状态订阅
|
// 图形app状态订阅
|
||||||
export class AppStateSubscription {
|
export interface AppStateSubscription {
|
||||||
|
/**
|
||||||
|
* 订阅路径
|
||||||
|
*/
|
||||||
destination: string;
|
destination: string;
|
||||||
messageConverter: MessageConverter;
|
/**
|
||||||
subscription?: StompSubscription; // 订阅成功对象,用于取消订阅
|
* 图形状态消息转换
|
||||||
constructor(
|
*/
|
||||||
destination: string,
|
messageConverter?: GraphicStateMessageConvert;
|
||||||
messageConverter: MessageConverter,
|
/**
|
||||||
subscription?: StompSubscription
|
* 订阅消息处理
|
||||||
) {
|
*/
|
||||||
this.destination = destination;
|
messageHandle?: SubscriptionMessageHandle;
|
||||||
this.messageConverter = messageConverter;
|
/**
|
||||||
this.subscription = subscription;
|
* 订阅成功对象,用于取消订阅
|
||||||
}
|
* 非客户端使用
|
||||||
|
*/
|
||||||
|
subscription?: StompSubscription;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -175,8 +200,16 @@ export class AppWsMsgBroker {
|
|||||||
sub.subscription = StompCli.trySubscribe(
|
sub.subscription = StompCli.trySubscribe(
|
||||||
sub.destination,
|
sub.destination,
|
||||||
(message: Message) => {
|
(message: Message) => {
|
||||||
const graphicStates = sub.messageConverter(message.binaryBody);
|
if (sub.messageConverter) {
|
||||||
this.app.handleGraphicStates(graphicStates);
|
const graphicStates = sub.messageConverter(message.binaryBody);
|
||||||
|
this.app.handleGraphicStates(graphicStates);
|
||||||
|
} else if (sub.messageHandle) {
|
||||||
|
sub.messageHandle(message.binaryBody);
|
||||||
|
} else {
|
||||||
|
console.error(
|
||||||
|
`订阅destination:${sub.destination}没有消息处理器或图形状态消息转换器`
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
// console.log('代理订阅结果', sub.subscription)
|
// console.log('代理订阅结果', sub.subscription)
|
||||||
|
@ -194,18 +194,17 @@ export class CommonMouseTool extends AppInteractionPlugin {
|
|||||||
const graphic = this.leftDownTarget.getGraphic();
|
const graphic = this.leftDownTarget.getGraphic();
|
||||||
if (graphic) {
|
if (graphic) {
|
||||||
const app = this.app;
|
const app = this.app;
|
||||||
|
// console.log(this.leftDownTarget.isGraphic());
|
||||||
// 图形选中
|
// 图形选中
|
||||||
if (!e.ctrlKey && !graphic.selected && graphic.selectable) {
|
if (!e.ctrlKey && !graphic.selected && graphic.selectable) {
|
||||||
app.updateSelected(graphic);
|
app.updateSelected(graphic);
|
||||||
graphic.childEdit = false;
|
graphic.childEdit = false;
|
||||||
this.graphicSelect = true;
|
this.graphicSelect = true;
|
||||||
} else if (
|
} else if (!e.ctrlKey && graphic.selected && graphic.childEdit) {
|
||||||
!e.ctrlKey &&
|
if (
|
||||||
graphic.selected &&
|
this.leftDownTarget.isGraphicChild() &&
|
||||||
graphic.childEdit &&
|
this.leftDownTarget.selectable
|
||||||
this.leftDownTarget.isGraphicChild()
|
) {
|
||||||
) {
|
|
||||||
if (this.leftDownTarget.selectable) {
|
|
||||||
graphic.setChildSelected(this.leftDownTarget);
|
graphic.setChildSelected(this.leftDownTarget);
|
||||||
} else {
|
} else {
|
||||||
graphic.exitChildEdit();
|
graphic.exitChildEdit();
|
||||||
|
@ -2,7 +2,6 @@ import {
|
|||||||
Color,
|
Color,
|
||||||
Container,
|
Container,
|
||||||
DisplayObject,
|
DisplayObject,
|
||||||
FederatedMouseEvent,
|
|
||||||
Graphics,
|
Graphics,
|
||||||
IDestroyOptions,
|
IDestroyOptions,
|
||||||
IPointData,
|
IPointData,
|
||||||
@ -10,12 +9,12 @@ import {
|
|||||||
} from 'pixi.js';
|
} from 'pixi.js';
|
||||||
import { JlGraphic } from '../core';
|
import { JlGraphic } from '../core';
|
||||||
import { DraggablePoint } from '../graphic';
|
import { DraggablePoint } from '../graphic';
|
||||||
import { ContextMenu } from '../ui/ContextMenu';
|
|
||||||
import { MenuItemOptions, MenuOptions } from '../ui/Menu';
|
|
||||||
import {
|
import {
|
||||||
|
calculateDistanceFromPointToLine,
|
||||||
calculateFootPointFromPointToLine,
|
calculateFootPointFromPointToLine,
|
||||||
|
calculateLineSegmentingPoint,
|
||||||
calculateMirrorPoint,
|
calculateMirrorPoint,
|
||||||
assertBezierPoints,
|
convertToBezierParams,
|
||||||
distance2,
|
distance2,
|
||||||
linePoint,
|
linePoint,
|
||||||
pointPolygon,
|
pointPolygon,
|
||||||
@ -33,13 +32,13 @@ export abstract class GraphicEditPlugin<
|
|||||||
this.sortableChildren = true;
|
this.sortableChildren = true;
|
||||||
this.graphic.on('transformstart', this.hideAll, this);
|
this.graphic.on('transformstart', this.hideAll, this);
|
||||||
this.graphic.on('transformend', this.showAll, this);
|
this.graphic.on('transformend', this.showAll, this);
|
||||||
this.graphic.on('repaint', this.showAll, this);
|
this.graphic.on('repaint', this.updateEditedPointsPosition, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy(options?: boolean | IDestroyOptions | undefined): void {
|
destroy(options?: boolean | IDestroyOptions | undefined): void {
|
||||||
this.graphic.off('transformstart', this.hideAll, this);
|
this.graphic.off('transformstart', this.hideAll, this);
|
||||||
this.graphic.off('transformend', this.showAll, this);
|
this.graphic.off('transformend', this.showAll, this);
|
||||||
this.graphic.off('repaint', this.showAll, this);
|
this.graphic.off('repaint', this.updateEditedPointsPosition, this);
|
||||||
super.destroy(options);
|
super.destroy(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,26 +53,6 @@ export abstract class GraphicEditPlugin<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const addWaypointConfig: MenuItemOptions = {
|
|
||||||
name: '添加路径点',
|
|
||||||
};
|
|
||||||
export const removeWaypointConfig: MenuItemOptions = {
|
|
||||||
name: '移除路径点',
|
|
||||||
};
|
|
||||||
export const clearWaypointsConfig: MenuItemOptions = {
|
|
||||||
name: '清除所有路径点',
|
|
||||||
};
|
|
||||||
const menuOptions: MenuOptions = {
|
|
||||||
name: '图形编辑点菜单',
|
|
||||||
groups: [
|
|
||||||
{
|
|
||||||
items: [removeWaypointConfig, clearWaypointsConfig],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const EditPointContextMenu = ContextMenu.init(menuOptions);
|
|
||||||
|
|
||||||
export interface ILineGraphic extends JlGraphic {
|
export interface ILineGraphic extends JlGraphic {
|
||||||
get linePoints(): IPointData[];
|
get linePoints(): IPointData[];
|
||||||
set linePoints(points: IPointData[]);
|
set linePoints(points: IPointData[]);
|
||||||
@ -103,10 +82,50 @@ export abstract class LineEditPlugin extends GraphicEditPlugin<ILineGraphic> {
|
|||||||
abstract initEditPoints(): void;
|
abstract initEditPoints(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getWayLineIndex(
|
||||||
|
points: IPointData[],
|
||||||
|
p: IPointData
|
||||||
|
): { start: number; end: number } {
|
||||||
|
let start = 0;
|
||||||
|
let end = 0;
|
||||||
|
let minDistance = 0;
|
||||||
|
for (let i = 1; i < points.length; i++) {
|
||||||
|
const sp = points[i - 1];
|
||||||
|
const ep = points[i];
|
||||||
|
let distance = calculateDistanceFromPointToLine(sp, ep, p);
|
||||||
|
distance = Math.round(distance * 100) / 100;
|
||||||
|
if (i == 1) {
|
||||||
|
minDistance = distance;
|
||||||
|
}
|
||||||
|
if (distance == minDistance) {
|
||||||
|
const minX = Math.min(sp.x, ep.x);
|
||||||
|
const maxX = Math.max(sp.x, ep.x);
|
||||||
|
const minY = Math.min(sp.y, ep.y);
|
||||||
|
const maxY = Math.max(sp.y, ep.y);
|
||||||
|
const point = calculateFootPointFromPointToLine(sp, ep, p);
|
||||||
|
if (
|
||||||
|
point.x >= minX &&
|
||||||
|
point.x <= maxX &&
|
||||||
|
point.y >= minY &&
|
||||||
|
point.y <= maxY
|
||||||
|
) {
|
||||||
|
start = i - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (distance < minDistance) {
|
||||||
|
minDistance = distance;
|
||||||
|
start = i - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end = start + 1;
|
||||||
|
return { start, end };
|
||||||
|
}
|
||||||
|
|
||||||
export function getWaypointRangeIndex(
|
export function getWaypointRangeIndex(
|
||||||
points: IPointData[],
|
points: IPointData[],
|
||||||
curve: boolean,
|
curve: boolean,
|
||||||
p: IPointData
|
p: IPointData,
|
||||||
|
lineWidth: number
|
||||||
): { start: number; end: number } {
|
): { start: number; end: number } {
|
||||||
let start = 0;
|
let start = 0;
|
||||||
let end = 0;
|
let end = 0;
|
||||||
@ -124,17 +143,25 @@ export function getWaypointRangeIndex(
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 贝塞尔曲线
|
// 贝塞尔曲线
|
||||||
assertBezierPoints(points);
|
const bps = convertToBezierParams(points);
|
||||||
for (let i = 0; i < points.length - 3; i += 3) {
|
for (let i = 0; i < bps.length; i++) {
|
||||||
const p1 = points[i];
|
const bp = bps[i];
|
||||||
const cp1 = points[i + 1];
|
if (pointPolygon(p, [bp.p1, bp.cp1, bp.cp2, bp.p2], lineWidth)) {
|
||||||
const cp2 = points[i + 2];
|
start = i * 3;
|
||||||
const p2 = points[i + 3];
|
end = start + 3;
|
||||||
if (pointPolygon(p, [p1, cp1, cp2, p2], 1)) {
|
|
||||||
start = i;
|
|
||||||
end = i + 3;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// assertBezierPoints(points);
|
||||||
|
// for (let i = 0; i < points.length - 3; i += 3) {
|
||||||
|
// const p1 = points[i];
|
||||||
|
// const cp1 = points[i + 1];
|
||||||
|
// const cp2 = points[i + 2];
|
||||||
|
// const p2 = points[i + 3];
|
||||||
|
// if (pointPolygon(p, [p1, cp1, cp2, p2], lineWidth)) {
|
||||||
|
// start = i;
|
||||||
|
// end = i + 3;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
return { start, end };
|
return { start, end };
|
||||||
}
|
}
|
||||||
@ -170,19 +197,7 @@ export class PolylineEditPlugin extends LineEditPlugin {
|
|||||||
for (let i = 0; i < cps.length; i++) {
|
for (let i = 0; i < cps.length; i++) {
|
||||||
const p = cps[i];
|
const p = cps[i];
|
||||||
const dp = new DraggablePoint(p);
|
const dp = new DraggablePoint(p);
|
||||||
dp.on('rightclick', (e: FederatedMouseEvent) => {
|
|
||||||
// dp.getGraphicApp().openContextMenu(EditPointContextMenu.DefaultMenu);
|
|
||||||
// 路径中的点
|
|
||||||
const app = dp.getGraphicApp();
|
|
||||||
app.registerMenu(EditPointContextMenu);
|
|
||||||
removeWaypointConfig.handler = () => {
|
|
||||||
removeLineWayPoint(this.graphic, i);
|
|
||||||
};
|
|
||||||
clearWaypointsConfig.handler = () => {
|
|
||||||
clearWayPoint(this.graphic, false);
|
|
||||||
};
|
|
||||||
EditPointContextMenu.open(e.global);
|
|
||||||
});
|
|
||||||
dp.on('transforming', () => {
|
dp.on('transforming', () => {
|
||||||
const tlp = this.graphic.canvasToLocalPoint(dp.position);
|
const tlp = this.graphic.canvasToLocalPoint(dp.position);
|
||||||
const cp = this.linePoints[i];
|
const cp = this.linePoints[i];
|
||||||
@ -251,6 +266,21 @@ export function addLineWayPoint(
|
|||||||
graphic.linePoints = points;
|
graphic.linePoints = points;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function addPolygonSegmentingPoint(
|
||||||
|
graphic: ILineGraphic,
|
||||||
|
start: number,
|
||||||
|
end: number,
|
||||||
|
knife = 2
|
||||||
|
) {
|
||||||
|
const linePoints = graphic.linePoints;
|
||||||
|
const points = linePoints.slice(0, start + 1);
|
||||||
|
points.push(
|
||||||
|
...calculateLineSegmentingPoint(linePoints[start], linePoints[end], knife)
|
||||||
|
);
|
||||||
|
points.push(...linePoints.slice(end));
|
||||||
|
graphic.linePoints = points;
|
||||||
|
}
|
||||||
|
|
||||||
function assertBezierWayPoint(i: number) {
|
function assertBezierWayPoint(i: number) {
|
||||||
const c = i % 3;
|
const c = i % 3;
|
||||||
if (c !== 0) {
|
if (c !== 0) {
|
||||||
@ -265,6 +295,7 @@ export function addBezierWayPoint(
|
|||||||
p: IPointData
|
p: IPointData
|
||||||
) {
|
) {
|
||||||
if (start === end) {
|
if (start === end) {
|
||||||
|
console.error('添加贝塞尔曲线路径点开始结束点相等', start);
|
||||||
throw new Error('开始结束点不能一致');
|
throw new Error('开始结束点不能一致');
|
||||||
}
|
}
|
||||||
assertBezierWayPoint(start);
|
assertBezierWayPoint(start);
|
||||||
@ -394,21 +425,6 @@ export class BezierCurveEditPlugin extends LineEditPlugin {
|
|||||||
this.drawAuxiliaryLine(line, p, np);
|
this.drawAuxiliaryLine(line, p, np);
|
||||||
this.auxiliaryLines.push(line);
|
this.auxiliaryLines.push(line);
|
||||||
}
|
}
|
||||||
dp.on('rightclick', (e: FederatedMouseEvent) => {
|
|
||||||
// dp.getGraphicApp().openContextMenu(EditPointContextMenu.DefaultMenu);
|
|
||||||
if (c === 0) {
|
|
||||||
// 路径中的点
|
|
||||||
const app = dp.getGraphicApp();
|
|
||||||
app.registerMenu(EditPointContextMenu);
|
|
||||||
removeWaypointConfig.handler = () => {
|
|
||||||
removeBezierWayPoint(this.graphic, i);
|
|
||||||
};
|
|
||||||
clearWaypointsConfig.handler = () => {
|
|
||||||
clearWayPoint(this.graphic, true);
|
|
||||||
};
|
|
||||||
EditPointContextMenu.open(e.global);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
dp.on('transforming', (e: GraphicTransformEvent) => {
|
dp.on('transforming', (e: GraphicTransformEvent) => {
|
||||||
const tlp = this.graphic.canvasToLocalPoint(dp.position);
|
const tlp = this.graphic.canvasToLocalPoint(dp.position);
|
||||||
const cp = this.linePoints[i];
|
const cp = this.linePoints[i];
|
||||||
|
@ -14,7 +14,7 @@ import {
|
|||||||
} from '.';
|
} from '.';
|
||||||
import { GraphicApp } from '../app';
|
import { GraphicApp } from '../app';
|
||||||
import { JlGraphic } from '../core';
|
import { JlGraphic } from '../core';
|
||||||
import { AbsorbablePosition } from '../graphic';
|
import { AbsorbablePosition, VectorText } from '../graphic';
|
||||||
import { DraggablePoint } from '../graphic/DraggablePoint';
|
import { DraggablePoint } from '../graphic/DraggablePoint';
|
||||||
import {
|
import {
|
||||||
angleToAxisx,
|
angleToAxisx,
|
||||||
@ -171,11 +171,40 @@ export class GraphicTransformPlugin extends InteractionPluginBase {
|
|||||||
this.app.canvas.addAssistantAppend(this.apContainer);
|
this.app.canvas.addAssistantAppend(this.apContainer);
|
||||||
app.on('options-update', (options) => {
|
app.on('options-update', (options) => {
|
||||||
if (options.absorbablePositions) {
|
if (options.absorbablePositions) {
|
||||||
this.absorbablePositions = options.absorbablePositions;
|
this.absorbablePositions = this.filterAbsorbablePositions(
|
||||||
|
options.absorbablePositions
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 过滤重复的吸附位置
|
||||||
|
* @param positions
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
filterAbsorbablePositions(
|
||||||
|
positions: AbsorbablePosition[]
|
||||||
|
): AbsorbablePosition[] {
|
||||||
|
const aps: AbsorbablePosition[] = [];
|
||||||
|
for (let i = 0; i < positions.length; i++) {
|
||||||
|
const ap1 = positions[i];
|
||||||
|
let ap: AbsorbablePosition | null = ap1;
|
||||||
|
for (let j = positions.length - 1; j > i; j--) {
|
||||||
|
const ap2 = positions[j];
|
||||||
|
if (ap.isOverlapping(ap2) && ap.compareTo(ap2) <= 0) {
|
||||||
|
ap = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ap != null) {
|
||||||
|
aps.push(ap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// console.log(positions, aps);
|
||||||
|
return aps;
|
||||||
|
}
|
||||||
|
|
||||||
static new(app: GraphicApp) {
|
static new(app: GraphicApp) {
|
||||||
return new GraphicTransformPlugin(app);
|
return new GraphicTransformPlugin(app);
|
||||||
}
|
}
|
||||||
@ -187,7 +216,7 @@ export class GraphicTransformPlugin extends InteractionPluginBase {
|
|||||||
this.app.on('graphicselectedchange', this.onGraphicSelectedChange, this);
|
this.app.on('graphicselectedchange', this.onGraphicSelectedChange, this);
|
||||||
this.app.on(
|
this.app.on(
|
||||||
'graphicchildselectedchange',
|
'graphicchildselectedchange',
|
||||||
this.onGraphicChildSelectedChange,
|
this.onGraphicSelectedChange,
|
||||||
this
|
this
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -198,7 +227,7 @@ export class GraphicTransformPlugin extends InteractionPluginBase {
|
|||||||
this.app.off('graphicselectedchange', this.onGraphicSelectedChange, this);
|
this.app.off('graphicselectedchange', this.onGraphicSelectedChange, this);
|
||||||
this.app.off(
|
this.app.off(
|
||||||
'graphicchildselectedchange',
|
'graphicchildselectedchange',
|
||||||
this.onGraphicChildSelectedChange,
|
this.onGraphicSelectedChange,
|
||||||
this
|
this
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -255,9 +284,10 @@ export class GraphicTransformPlugin extends InteractionPluginBase {
|
|||||||
targets.forEach((target) => {
|
targets.forEach((target) => {
|
||||||
if (target.shiftStartPoint) {
|
if (target.shiftStartPoint) {
|
||||||
target.shiftLastPoint = target.position.clone();
|
target.shiftLastPoint = target.position.clone();
|
||||||
|
const { dx, dy } = e.toTargetShiftLen(target.parent);
|
||||||
target.position.set(
|
target.position.set(
|
||||||
target.shiftStartPoint.x + e.dsx,
|
target.shiftStartPoint.x + dx,
|
||||||
target.shiftStartPoint.y + e.dsy
|
target.shiftStartPoint.y + dy
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -265,9 +295,7 @@ export class GraphicTransformPlugin extends InteractionPluginBase {
|
|||||||
if (this.absorbablePositions) {
|
if (this.absorbablePositions) {
|
||||||
for (let i = 0; i < this.absorbablePositions.length; i++) {
|
for (let i = 0; i < this.absorbablePositions.length; i++) {
|
||||||
const ap = this.absorbablePositions[i];
|
const ap = this.absorbablePositions[i];
|
||||||
if (ap.tryAbsorb(...targets)) {
|
ap.tryAbsorb(...targets);
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 事件发布
|
// 事件发布
|
||||||
@ -335,7 +363,7 @@ export class GraphicTransformPlugin extends InteractionPluginBase {
|
|||||||
br.visible = false;
|
br.visible = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (g.scalable) {
|
if (g.scalable || g.rotatable) {
|
||||||
// 缩放点
|
// 缩放点
|
||||||
let sp = g.getAssistantAppend<TransformPoints>(TransformPoints.Name);
|
let sp = g.getAssistantAppend<TransformPoints>(TransformPoints.Name);
|
||||||
if (!sp) {
|
if (!sp) {
|
||||||
@ -350,19 +378,19 @@ export class GraphicTransformPlugin extends InteractionPluginBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onGraphicChildSelectedChange(child: DisplayObject, selected: boolean) {
|
// onGraphicChildSelectedChange(child: DisplayObject, selected: boolean) {
|
||||||
let br = child.getAssistantAppend<BoundsGraphic>(BoundsGraphic.Name);
|
// let br = child.getAssistantAppend<BoundsGraphic>(BoundsGraphic.Name);
|
||||||
if (!br) {
|
// if (!br) {
|
||||||
// 绘制辅助包围框
|
// // 绘制辅助包围框
|
||||||
br = new BoundsGraphic(child);
|
// br = new BoundsGraphic(child);
|
||||||
}
|
// }
|
||||||
if (selected) {
|
// if (selected) {
|
||||||
br.redraw();
|
// br.redraw();
|
||||||
br.visible = true;
|
// br.visible = true;
|
||||||
} else {
|
// } else {
|
||||||
br.visible = false;
|
// br.visible = false;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -419,6 +447,10 @@ export class TransformPoints extends Container {
|
|||||||
* 起始图形角度
|
* 起始图形角度
|
||||||
*/
|
*/
|
||||||
startAngle = 0;
|
startAngle = 0;
|
||||||
|
/**
|
||||||
|
* 当前角度信息文本辅助
|
||||||
|
*/
|
||||||
|
angleAssistantText: VectorText;
|
||||||
/**
|
/**
|
||||||
* 旋转角度步长
|
* 旋转角度步长
|
||||||
*/
|
*/
|
||||||
@ -433,6 +465,10 @@ export class TransformPoints extends Container {
|
|||||||
this.obj = obj;
|
this.obj = obj;
|
||||||
this.name = TransformPoints.Name;
|
this.name = TransformPoints.Name;
|
||||||
|
|
||||||
|
this.angleAssistantText = new VectorText('');
|
||||||
|
this.angleAssistantText.setVectorFontSize(16);
|
||||||
|
this.angleAssistantText.anchor.set(0.5);
|
||||||
|
|
||||||
// 创建缩放拖拽点
|
// 创建缩放拖拽点
|
||||||
this.ltScalePoint = new DraggablePoint(new Point());
|
this.ltScalePoint = new DraggablePoint(new Point());
|
||||||
this.ltScalePoint.name = TransformPoints.LeftTopName;
|
this.ltScalePoint.name = TransformPoints.LeftTopName;
|
||||||
@ -460,6 +496,19 @@ export class TransformPoints extends Container {
|
|||||||
this.addChild(this.lScalePoint);
|
this.addChild(this.lScalePoint);
|
||||||
this.obj.on('transformstart', this.onObjTransformStart, this);
|
this.obj.on('transformstart', this.onObjTransformStart, this);
|
||||||
this.obj.on('transformend', this.onObjTransformEnd, this);
|
this.obj.on('transformend', this.onObjTransformEnd, this);
|
||||||
|
|
||||||
|
if (this.obj.children && this.obj.children.length > 0) {
|
||||||
|
recursiveChildren(this.obj as Container, (child) => {
|
||||||
|
child.on('transformstart', this.onObjTransformStart, this);
|
||||||
|
child.on('transformend', this.onObjTransformEnd, this);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const pg = this.obj.getGraphic();
|
||||||
|
if (pg != null) {
|
||||||
|
pg.on('transformstart', this.onObjTransformStart, this);
|
||||||
|
pg.on('transformend', this.onObjTransformEnd, this);
|
||||||
|
}
|
||||||
|
|
||||||
this.obj.on('repaint', this.onGraphicRepaint, this);
|
this.obj.on('repaint', this.onGraphicRepaint, this);
|
||||||
this.children.forEach((dp) => {
|
this.children.forEach((dp) => {
|
||||||
dp.on('transformstart', this.onScaleDragStart, this);
|
dp.on('transformstart', this.onScaleDragStart, this);
|
||||||
@ -520,6 +569,19 @@ export class TransformPoints extends Container {
|
|||||||
);
|
);
|
||||||
this.obj.emit('transformstart', GraphicTransformEvent.rotate(this.obj));
|
this.obj.emit('transformstart', GraphicTransformEvent.rotate(this.obj));
|
||||||
// app.emit('transformstart', app.selectedGraphics);
|
// app.emit('transformstart', app.selectedGraphics);
|
||||||
|
this.obj.getCanvas().addAssistantAppends(this.angleAssistantText);
|
||||||
|
this.updateAngleAssistantText(de);
|
||||||
|
}
|
||||||
|
updateAngleAssistantText(de: GraphicTransformEvent) {
|
||||||
|
this.angleAssistantText.text = this.obj.angle + '°';
|
||||||
|
let cursorPoint = de.data?.startPosition;
|
||||||
|
if (de.data?.currentPosition) {
|
||||||
|
cursorPoint = de.data?.currentPosition;
|
||||||
|
}
|
||||||
|
if (cursorPoint) {
|
||||||
|
this.angleAssistantText.position.x = cursorPoint.x;
|
||||||
|
this.angleAssistantText.position.y = cursorPoint.y - 10;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* 旋转移动
|
* 旋转移动
|
||||||
@ -529,11 +591,13 @@ export class TransformPoints extends Container {
|
|||||||
// 旋转角度计算逻辑:取锚点y负方向一点作为旋转点,求旋转点和锚点所形成的直线与x轴角度,此角度+90°即为最终旋转角度,再将旋转角度限制到(-180,180]之间
|
// 旋转角度计算逻辑:取锚点y负方向一点作为旋转点,求旋转点和锚点所形成的直线与x轴角度,此角度+90°即为最终旋转角度,再将旋转角度限制到(-180,180]之间
|
||||||
let angle = angleToAxisx(this.rotatePivot, de.target.position);
|
let angle = angleToAxisx(this.rotatePivot, de.target.position);
|
||||||
angle = Math.floor(angle / this.angleStep) * this.angleStep;
|
angle = Math.floor(angle / this.angleStep) * this.angleStep;
|
||||||
angle = (angle + 90) % 360;
|
const parentAngle = this.obj.parent.worldAngle;
|
||||||
|
angle = (angle + 90 - parentAngle) % 360;
|
||||||
if (angle > 180) {
|
if (angle > 180) {
|
||||||
angle = angle - 360;
|
angle = angle - 360;
|
||||||
}
|
}
|
||||||
this.obj.angle = angle;
|
this.obj.angle = angle;
|
||||||
|
this.updateAngleAssistantText(de);
|
||||||
// this.obj.emit('rotatemove', this.obj);
|
// this.obj.emit('rotatemove', this.obj);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@ -542,6 +606,7 @@ export class TransformPoints extends Container {
|
|||||||
*/
|
*/
|
||||||
onRotateEnd() {
|
onRotateEnd() {
|
||||||
this.showAll();
|
this.showAll();
|
||||||
|
this.obj.getCanvas().removeAssistantAppends(this.angleAssistantText);
|
||||||
this.rotateAngleStepKeyListeners.forEach((listener) =>
|
this.rotateAngleStepKeyListeners.forEach((listener) =>
|
||||||
this.obj.getGraphicApp().removeKeyboardListener(listener)
|
this.obj.getGraphicApp().removeKeyboardListener(listener)
|
||||||
);
|
);
|
||||||
@ -789,6 +854,17 @@ export class BoundsGraphic extends Graphics {
|
|||||||
this.visible = false;
|
this.visible = false;
|
||||||
this.obj.on('transformstart', this.onObjTransformStart, this);
|
this.obj.on('transformstart', this.onObjTransformStart, this);
|
||||||
this.obj.on('transformend', this.onObjTransformEnd, this);
|
this.obj.on('transformend', this.onObjTransformEnd, this);
|
||||||
|
if (this.obj.children && this.obj.children.length > 0) {
|
||||||
|
recursiveChildren(this.obj as Container, (child) => {
|
||||||
|
child.on('transformstart', this.onObjTransformStart, this);
|
||||||
|
child.on('transformend', this.onObjTransformEnd, this);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const pg = this.obj.getGraphic();
|
||||||
|
if (pg != null) {
|
||||||
|
pg.on('transformstart', this.onObjTransformStart, this);
|
||||||
|
pg.on('transformend', this.onObjTransformEnd, this);
|
||||||
|
}
|
||||||
this.obj.on('repaint', this.onGraphicRepaint, this);
|
this.obj.on('repaint', this.onGraphicRepaint, this);
|
||||||
graphic.addAssistantAppend(this);
|
graphic.addAssistantAppend(this);
|
||||||
}
|
}
|
||||||
|
@ -99,7 +99,7 @@ export class AppDragEvent {
|
|||||||
type: 'start' | 'move' | 'end';
|
type: 'start' | 'move' | 'end';
|
||||||
target: DisplayObject;
|
target: DisplayObject;
|
||||||
original: FederatedPointerEvent;
|
original: FederatedPointerEvent;
|
||||||
start: Point;
|
start: Point; // 画布坐标
|
||||||
constructor(
|
constructor(
|
||||||
app: GraphicApp,
|
app: GraphicApp,
|
||||||
type: 'start' | 'move' | 'end',
|
type: 'start' | 'move' | 'end',
|
||||||
@ -146,6 +146,9 @@ export class AppDragEvent {
|
|||||||
return this.original.pointerType === 'touch';
|
return this.original.pointerType === 'touch';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 终点坐标(画布坐标)
|
||||||
|
*/
|
||||||
public get end(): Point {
|
public get end(): Point {
|
||||||
return this.app.toCanvasCoordinates(this.original.global);
|
return this.app.toCanvasCoordinates(this.original.global);
|
||||||
}
|
}
|
||||||
@ -167,6 +170,15 @@ export class AppDragEvent {
|
|||||||
public get dsy(): number {
|
public get dsy(): number {
|
||||||
return this.end.y - this.start.y;
|
return this.end.y - this.start.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换为目标对象的位移距离
|
||||||
|
*/
|
||||||
|
toTargetShiftLen(target: DisplayObject): { dx: number; dy: number } {
|
||||||
|
const sl = target.canvasToLocalPoint(this.start);
|
||||||
|
const el = target.canvasToLocalPoint(this.end);
|
||||||
|
return { dx: el.x - sl.x, dy: el.y - sl.y };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -67,15 +67,16 @@ export function recursiveFindChild(
|
|||||||
container: Container,
|
container: Container,
|
||||||
finder: (child: DisplayObject) => boolean
|
finder: (child: DisplayObject) => boolean
|
||||||
): DisplayObject | null {
|
): DisplayObject | null {
|
||||||
|
let result = null;
|
||||||
for (let i = 0; i < container.children.length; i++) {
|
for (let i = 0; i < container.children.length; i++) {
|
||||||
const child = container.children[i];
|
const child = container.children[i];
|
||||||
if (finder(child)) {
|
if (finder(child)) {
|
||||||
return child;
|
return child;
|
||||||
} else if (child.children) {
|
} else if (child.children) {
|
||||||
return recursiveFindChild(child as Container, finder);
|
result = recursiveFindChild(child as Container, finder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BezierParam {
|
export interface BezierParam {
|
||||||
@ -287,6 +288,31 @@ export function calculateLineMidpoint(p1: IPointData, p2: IPointData): Point {
|
|||||||
return new Point(x, y);
|
return new Point(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算线段细分坐标--线段分成几份
|
||||||
|
* @param p1
|
||||||
|
* @param p2
|
||||||
|
* @param knife
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function calculateLineSegmentingPoint(
|
||||||
|
p1: IPointData,
|
||||||
|
p2: IPointData,
|
||||||
|
knife: number
|
||||||
|
): IPointData[] {
|
||||||
|
const segmentingPoints: IPointData[] = [];
|
||||||
|
const x = p1.x < p2.x ? p1.x : p2.x;
|
||||||
|
const y = p1.y < p2.y ? p1.y : p2.y;
|
||||||
|
const w = Math.abs(p1.x - p2.x);
|
||||||
|
const h = Math.abs(p1.y - p2.y);
|
||||||
|
for (let i = 0; i < knife - 1; i++) {
|
||||||
|
const pointX = x + (w * (i + 1)) / knife;
|
||||||
|
const pointy = y + (h * (i + 1)) / knife;
|
||||||
|
segmentingPoints.push(new Point(pointX, pointy));
|
||||||
|
}
|
||||||
|
return segmentingPoints;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 计算点到直线距离
|
* 计算点到直线距离
|
||||||
* @param p1
|
* @param p1
|
||||||
@ -517,3 +543,153 @@ export function angleOfIncludedAngle(
|
|||||||
}
|
}
|
||||||
return angle;
|
return angle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算两点连线的法向量
|
||||||
|
* @param point1
|
||||||
|
* @param point2
|
||||||
|
* @returns 单位法向量
|
||||||
|
*/
|
||||||
|
export function getNormalVector(
|
||||||
|
point1: IPointData,
|
||||||
|
point2: IPointData
|
||||||
|
): number[] {
|
||||||
|
const x1 = point1.x,
|
||||||
|
y1 = point1.y;
|
||||||
|
const x2 = point2.x,
|
||||||
|
y2 = point2.y;
|
||||||
|
const length = Math.sqrt((y2 - y1) ** 2 + (x2 - x1) ** 2);
|
||||||
|
return [(y2 - y1) / length, (x1 - x2) / length];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 点延向量方向移动
|
||||||
|
* @param point
|
||||||
|
* @param normal 单位向量
|
||||||
|
* @param length 平移长度
|
||||||
|
* @returns 移动后的点
|
||||||
|
*/
|
||||||
|
export function movePointAlongNormal(
|
||||||
|
point: IPointData,
|
||||||
|
normal: number[],
|
||||||
|
length: number
|
||||||
|
): Point {
|
||||||
|
const newPoint = new Point(
|
||||||
|
point.x + length * normal[0],
|
||||||
|
point.y + length * normal[1]
|
||||||
|
);
|
||||||
|
return newPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算两组点各自组成直线的相交点(若两线平行 返回第一组坐标第一个点)
|
||||||
|
* @param line1 两点坐标列表
|
||||||
|
* @param line2 两点坐标列表
|
||||||
|
* @returns 相交点
|
||||||
|
*/
|
||||||
|
export function getIntersectionPoint(line1: number[], line2: number[]) {
|
||||||
|
const a1 = line1[0],
|
||||||
|
b1 = line1[1];
|
||||||
|
const a2 = line1[2],
|
||||||
|
b2 = line1[3];
|
||||||
|
const a3 = line2[0],
|
||||||
|
b3 = line2[1];
|
||||||
|
const a4 = line2[2],
|
||||||
|
b4 = line2[3];
|
||||||
|
const denominator = (a3 - a4) * (b1 - b2) - (a1 - a2) * (b3 - b4);
|
||||||
|
if (denominator === 0) {
|
||||||
|
return new Point(a1, b1);
|
||||||
|
}
|
||||||
|
const x =
|
||||||
|
((a3 - a4) * (a2 * b1 - a1 * b2) - (a1 - a2) * (a4 * b3 - a3 * b4)) /
|
||||||
|
denominator;
|
||||||
|
const y =
|
||||||
|
((b3 - b4) * (b2 * a1 - b1 * a2) - (b1 - b2) * (b4 * a3 - b3 * a4)) /
|
||||||
|
-denominator;
|
||||||
|
return new Point(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否平行线
|
||||||
|
* @param p1
|
||||||
|
* @param p2
|
||||||
|
* @param pa
|
||||||
|
* @param pb
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function isParallelLines(
|
||||||
|
p1: IPointData,
|
||||||
|
p2: IPointData,
|
||||||
|
pa: IPointData,
|
||||||
|
pb: IPointData
|
||||||
|
): boolean {
|
||||||
|
const vle1 = Vector2.direction(Vector2.from(p1), Vector2.from(p2));
|
||||||
|
const vle2 = Vector2.direction(Vector2.from(pa), Vector2.from(pb));
|
||||||
|
if (vle2.equals(vle1)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return vle1.equals(Vector2.direction(Vector2.from(pb), Vector2.from(pa)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 点是否在线段上
|
||||||
|
* @param p1
|
||||||
|
* @param p2
|
||||||
|
* @param p
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function isPointOnLine(
|
||||||
|
p1: IPointData,
|
||||||
|
p2: IPointData,
|
||||||
|
p: IPointData
|
||||||
|
): boolean {
|
||||||
|
const vp1 = Vector2.from(p1);
|
||||||
|
const vp2 = Vector2.from(p2);
|
||||||
|
const vp = Vector2.from(p);
|
||||||
|
if (vp1.equals(vp) || vp2.equals(vp)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const vle = Vector2.direction(vp1, Vector2.from(p2));
|
||||||
|
const vpe = Vector2.direction(vp1, vp);
|
||||||
|
if (vle.equals(vpe)) {
|
||||||
|
return (
|
||||||
|
Vector2.difference(vp1, vp2).squaredLength() >=
|
||||||
|
Vector2.difference(vp1, vp).squaredLength()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 两条线段是否存在包含关系
|
||||||
|
* @param line1
|
||||||
|
* @param line2
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function isLineContainOther(
|
||||||
|
line1: { p1: IPointData; p2: IPointData },
|
||||||
|
line2: { p1: IPointData; p2: IPointData }
|
||||||
|
): boolean {
|
||||||
|
return (
|
||||||
|
(isPointOnLine(line1.p1, line1.p2, line2.p1) &&
|
||||||
|
isPointOnLine(line1.p1, line1.p2, line2.p2)) ||
|
||||||
|
(isPointOnLine(line2.p1, line2.p2, line1.p1) &&
|
||||||
|
isPointOnLine(line2.p1, line2.p2, line1.p2))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 均分线段, 返回各线段端点 */
|
||||||
|
export function splitLineEvenly(
|
||||||
|
p1: IPointData,
|
||||||
|
p2: IPointData,
|
||||||
|
count: number
|
||||||
|
): IPointData[][] {
|
||||||
|
const [stepX, stepY] = [(p2.x - p1.x) / count, (p2.y - p1.y) / count];
|
||||||
|
return Array(count)
|
||||||
|
.fill(1)
|
||||||
|
.map((_, i) => {
|
||||||
|
return [
|
||||||
|
{ x: p1.x + stepX * i, y: p1.y + stepY * i },
|
||||||
|
{ x: p1.x + stepX * (i + 1), y: p1.y + stepY * (i + 1) },
|
||||||
|
];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@ -11,6 +11,19 @@
|
|||||||
Title
|
Title
|
||||||
</q-toolbar-title>
|
</q-toolbar-title>
|
||||||
|
|
||||||
|
<div class="q-gutter-sm row items-center no-wrap">
|
||||||
|
<q-btn
|
||||||
|
round
|
||||||
|
dense
|
||||||
|
flat
|
||||||
|
color="white"
|
||||||
|
:icon="$q.fullscreen.isActive ? 'fullscreen_exit' : 'fullscreen'"
|
||||||
|
@click="toggleFullscreen"
|
||||||
|
v-if="$q.screen.gt.sm"
|
||||||
|
>
|
||||||
|
</q-btn>
|
||||||
|
</div>
|
||||||
|
|
||||||
<q-btn dense flat round icon="menu" @click="toggleRightDrawer" />
|
<q-btn dense flat round icon="menu" @click="toggleRightDrawer" />
|
||||||
</q-toolbar>
|
</q-toolbar>
|
||||||
<q-resize-observer @resize="onHeaderResize" />
|
<q-resize-observer @resize="onHeaderResize" />
|
||||||
@ -100,19 +113,27 @@
|
|||||||
<draw-properties></draw-properties>
|
<draw-properties></draw-properties>
|
||||||
</q-drawer>
|
</q-drawer>
|
||||||
|
|
||||||
<q-page-container>
|
<q-page-container id="app-page">
|
||||||
<div id="draw-app-container"></div>
|
<div id="draw-app-container"></div>
|
||||||
</q-page-container>
|
</q-page-container>
|
||||||
</q-layout>
|
</q-layout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { Base64 } from 'js-base64';
|
||||||
|
import { useQuasar } from 'quasar';
|
||||||
import DrawProperties from 'src/components/draw-app/DrawProperties.vue';
|
import DrawProperties from 'src/components/draw-app/DrawProperties.vue';
|
||||||
import { getDrawApp, loadDrawDatas } from 'src/examples/app';
|
import { getDrawApp, loadDrawDatas } from 'src/examples/app';
|
||||||
|
import { getJwtToken } from 'src/examples/app/configs/TokenManage';
|
||||||
|
import { getWebsocketUrl } from 'src/examples/app/configs/UrlManage';
|
||||||
|
import { AppStateSubscription } from 'src/jlgraphic';
|
||||||
import { useDrawStore } from 'src/stores/draw-store';
|
import { useDrawStore } from 'src/stores/draw-store';
|
||||||
import { onMounted, onUnmounted, ref } from 'vue';
|
import { Ref, onMounted, onUnmounted, ref } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
const $q = useQuasar();
|
||||||
const drawStore = useDrawStore();
|
const drawStore = useDrawStore();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
const leftDrawerOpen = ref(false);
|
const leftDrawerOpen = ref(false);
|
||||||
const rightDrawerOpen = ref(false);
|
const rightDrawerOpen = ref(false);
|
||||||
@ -127,13 +148,46 @@ function toggleRightDrawer() {
|
|||||||
|
|
||||||
const link = ref('outbox');
|
const link = ref('outbox');
|
||||||
|
|
||||||
|
function toggleFullscreen(e: unknown): void {
|
||||||
|
// const dom = document.getElementById('app-page');
|
||||||
|
// if (dom) {
|
||||||
|
// $q.fullscreen.toggle(dom);
|
||||||
|
// }
|
||||||
|
$q.fullscreen.toggle();
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
console.log('绘制应用layout mounted');
|
console.log('绘制应用layout mounted');
|
||||||
|
|
||||||
|
const basic = Base64.decode('Zm9vOmJhcg==');
|
||||||
|
console.log(basic);
|
||||||
const dom = document.getElementById('draw-app-container');
|
const dom = document.getElementById('draw-app-container');
|
||||||
if (dom) {
|
if (dom) {
|
||||||
const drawApp = drawStore.initDrawApp(dom);
|
const drawApp = drawStore.initDrawApp(dom);
|
||||||
|
drawApp.on('websocket-state-change', (app, connected) => {
|
||||||
|
console.log('应用websocket状态变更', connected);
|
||||||
|
});
|
||||||
loadDrawDatas(drawApp);
|
loadDrawDatas(drawApp);
|
||||||
onResize();
|
onResize();
|
||||||
|
drawApp.enableWsStomp({
|
||||||
|
wsUrl: getWebsocketUrl(),
|
||||||
|
token: getJwtToken() as string,
|
||||||
|
onAuthenticationFailed: () => {
|
||||||
|
$q.dialog({
|
||||||
|
title: '认证失败',
|
||||||
|
message: '认证失败或登录超时,请重新登录',
|
||||||
|
persistent: true,
|
||||||
|
}).onOk(() => {
|
||||||
|
router.push({ name: 'login' });
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
drawApp.subscribe({
|
||||||
|
destination: '/queue/line/3/device',
|
||||||
|
messageHandle: (msg) => {
|
||||||
|
console.log('设备消息处理', msg);
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
93
src/pages/UserLogin.vue
Normal file
93
src/pages/UserLogin.vue
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
<template>
|
||||||
|
<q-layout>
|
||||||
|
<q-page-container>
|
||||||
|
<q-page class="flex bg-image flex-center">
|
||||||
|
<q-card
|
||||||
|
v-bind:style="$q.screen.lt.sm ? { width: '80%' } : { width: '30%' }"
|
||||||
|
style="min-width: 350px"
|
||||||
|
>
|
||||||
|
<q-card-section>
|
||||||
|
<q-avatar size="100px" class="absolute-center shadow-10">
|
||||||
|
<img src="icons/favicon-96x96.png" />
|
||||||
|
</q-avatar>
|
||||||
|
</q-card-section>
|
||||||
|
<q-card-section>
|
||||||
|
<div class="text-center q-pt-lg">
|
||||||
|
<div class="col text-h6 ellipsis">登录</div>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
<q-card-section>
|
||||||
|
<q-form @submit="doLogin" class="q-gutter-md">
|
||||||
|
<q-input
|
||||||
|
filled
|
||||||
|
v-model="loginInfo.account"
|
||||||
|
label="账号"
|
||||||
|
lazy-rules
|
||||||
|
type="tel"
|
||||||
|
mask="###########"
|
||||||
|
:rules="[(val) => val.length == 11 || '请输入正确手机格式!']"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<q-input
|
||||||
|
type="password"
|
||||||
|
filled
|
||||||
|
v-model="loginInfo.password"
|
||||||
|
label="密码"
|
||||||
|
lazy-rules
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="text-right">
|
||||||
|
<q-btn flat label="注册" to="/register" color="primary" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="text-center">
|
||||||
|
<q-btn label="登录" type="submit" color="primary" />
|
||||||
|
</div>
|
||||||
|
</q-form>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</q-page>
|
||||||
|
</q-page-container>
|
||||||
|
</q-layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useQuasar } from 'quasar';
|
||||||
|
import { ApiError } from 'src/boot/axios';
|
||||||
|
import { login } from 'src/examples/app/api/UserApi';
|
||||||
|
import {
|
||||||
|
clearJwtToken,
|
||||||
|
saveJwtToken,
|
||||||
|
} from 'src/examples/app/configs/TokenManage';
|
||||||
|
import { reactive } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
const $q = useQuasar();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const loginInfo = reactive({
|
||||||
|
account: '',
|
||||||
|
password: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
async function doLogin() {
|
||||||
|
try {
|
||||||
|
clearJwtToken();
|
||||||
|
const token = await login(loginInfo);
|
||||||
|
saveJwtToken(token);
|
||||||
|
router.push({ name: 'home' });
|
||||||
|
} catch (err) {
|
||||||
|
const apiErr = err as ApiError;
|
||||||
|
$q.notify({
|
||||||
|
type: 'negative',
|
||||||
|
message: apiErr.title,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.bg-image {
|
||||||
|
background-image: linear-gradient(135deg, #5481fd 0%, #0e02b1 80%);
|
||||||
|
}
|
||||||
|
</style>
|
97
src/pages/UserRegister.vue
Normal file
97
src/pages/UserRegister.vue
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
<template>
|
||||||
|
<q-layout>
|
||||||
|
<q-page-container>
|
||||||
|
<q-page class="flex bg-image flex-center">
|
||||||
|
<q-card
|
||||||
|
v-bind:style="$q.screen.lt.sm ? { width: '80%' } : { width: '30%' }"
|
||||||
|
style="min-width: 350px"
|
||||||
|
>
|
||||||
|
<q-card-section>
|
||||||
|
<q-avatar size="100px" class="absolute-center shadow-10">
|
||||||
|
<img src="icons/favicon-96x96.png" />
|
||||||
|
</q-avatar>
|
||||||
|
</q-card-section>
|
||||||
|
<q-card-section>
|
||||||
|
<div class="text-center q-pt-lg">
|
||||||
|
<div class="col text-h6 ellipsis">注册</div>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
<q-card-section>
|
||||||
|
<q-form @submit="doRegister" class="q-gutter-md">
|
||||||
|
<q-input
|
||||||
|
filled
|
||||||
|
v-model="loginInfo.name"
|
||||||
|
label="姓名"
|
||||||
|
lazy-rules
|
||||||
|
/>
|
||||||
|
|
||||||
|
<q-input
|
||||||
|
filled
|
||||||
|
v-model="loginInfo.mobile"
|
||||||
|
label="手机号"
|
||||||
|
lazy-rules
|
||||||
|
type="tel"
|
||||||
|
mask="###########"
|
||||||
|
:rules="[(val) => val.length == 11 || '请输入正确手机格式!']"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<q-input
|
||||||
|
type="password"
|
||||||
|
filled
|
||||||
|
v-model="loginInfo.password"
|
||||||
|
label="密码"
|
||||||
|
lazy-rules
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="text-center q-gutter-md">
|
||||||
|
<q-btn label="注册" type="submit" color="primary" />
|
||||||
|
<q-btn label="取消" to="/login" color="secondary" />
|
||||||
|
</div>
|
||||||
|
</q-form>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</q-page>
|
||||||
|
</q-page-container>
|
||||||
|
</q-layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useQuasar } from 'quasar';
|
||||||
|
import { register } from 'src/examples/app/api/UserApi';
|
||||||
|
import { ApiError } from 'src/boot/axios';
|
||||||
|
import { reactive } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
const $q = useQuasar();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const loginInfo = reactive({
|
||||||
|
name: '',
|
||||||
|
mobile: '',
|
||||||
|
password: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
async function doRegister() {
|
||||||
|
console.log(loginInfo);
|
||||||
|
try {
|
||||||
|
await register(loginInfo);
|
||||||
|
$q.notify({
|
||||||
|
type: 'positive',
|
||||||
|
message: '注册成功',
|
||||||
|
});
|
||||||
|
router.push({ name: 'login' });
|
||||||
|
} catch (err) {
|
||||||
|
const apiErr = err as ApiError;
|
||||||
|
$q.notify({
|
||||||
|
type: 'negative',
|
||||||
|
message: apiErr.title,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.bg-image {
|
||||||
|
background-image: linear-gradient(135deg, #5481fd 0%, #0e02b1 80%);
|
||||||
|
}
|
||||||
|
</style>
|
@ -3,9 +3,22 @@ import { RouteRecordRaw } from 'vue-router';
|
|||||||
const routes: RouteRecordRaw[] = [
|
const routes: RouteRecordRaw[] = [
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
|
name: 'home',
|
||||||
component: () => import('layouts/DrawLayout.vue'),
|
component: () => import('layouts/DrawLayout.vue'),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
path: '/login',
|
||||||
|
name: 'login',
|
||||||
|
component: () => import('pages/UserLogin.vue'),
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
path: '/register',
|
||||||
|
name: 'register',
|
||||||
|
component: () => import('pages/UserRegister.vue'),
|
||||||
|
},
|
||||||
|
|
||||||
// Always leave this as last one,
|
// Always leave this as last one,
|
||||||
// but you can also remove it
|
// but you can also remove it
|
||||||
{
|
{
|
||||||
|
62
yarn.lock
62
yarn.lock
@ -183,6 +183,11 @@
|
|||||||
resolved "https://registry.npmmirror.com/@pixi/filter-noise/-/filter-noise-7.2.4.tgz#0586a00381ec0e63f6c00d49cd58b781eaf07f37"
|
resolved "https://registry.npmmirror.com/@pixi/filter-noise/-/filter-noise-7.2.4.tgz#0586a00381ec0e63f6c00d49cd58b781eaf07f37"
|
||||||
integrity sha512-QAU9Ybj2ZQrWM9ZEjTTC0iLnQcuyNoZNRinxSbg1G0yacpmsSb9wvV5ltIZ66+hfY+90+u2Nudt/v9g6pvOdGg==
|
integrity sha512-QAU9Ybj2ZQrWM9ZEjTTC0iLnQcuyNoZNRinxSbg1G0yacpmsSb9wvV5ltIZ66+hfY+90+u2Nudt/v9g6pvOdGg==
|
||||||
|
|
||||||
|
"@pixi/graphics-extras@^7.2.4":
|
||||||
|
version "7.2.4"
|
||||||
|
resolved "https://registry.npmmirror.com/@pixi/graphics-extras/-/graphics-extras-7.2.4.tgz#72ac967992f239d3671d6e680ac891471619fe07"
|
||||||
|
integrity sha512-0yT91yqF3KLiZI/iLRcfcYlTVpkVyWsfGtWEIorZs0eX+/zYx7um7EJ2h7tFORI/1FxA2maR4td5vpgCwOLJAQ==
|
||||||
|
|
||||||
"@pixi/graphics@7.2.4":
|
"@pixi/graphics@7.2.4":
|
||||||
version "7.2.4"
|
version "7.2.4"
|
||||||
resolved "https://registry.npmmirror.com/@pixi/graphics/-/graphics-7.2.4.tgz#8500b604c36184736926393cb0ca9b9de9afef86"
|
resolved "https://registry.npmmirror.com/@pixi/graphics/-/graphics-7.2.4.tgz#8500b604c36184736926393cb0ca9b9de9afef86"
|
||||||
@ -729,6 +734,11 @@ ajv@^8.0.1:
|
|||||||
require-from-string "^2.0.2"
|
require-from-string "^2.0.2"
|
||||||
uri-js "^4.2.2"
|
uri-js "^4.2.2"
|
||||||
|
|
||||||
|
alova@^2.7.1:
|
||||||
|
version "2.7.1"
|
||||||
|
resolved "https://registry.npmmirror.com/alova/-/alova-2.7.1.tgz#b91b7c80137a44b57792badda9e7ab09acf93f55"
|
||||||
|
integrity sha512-/+lbPt+u/c4rBx4fq83795Xuh8Ohj5ZB4tJ7pxL2BxLgRlLNCT9/I1yGrgJEz/jXTqIZHMI2EeBAIb/B0u76tw==
|
||||||
|
|
||||||
ansi-escapes@^4.2.1:
|
ansi-escapes@^4.2.1:
|
||||||
version "4.3.2"
|
version "4.3.2"
|
||||||
resolved "https://registry.npmmirror.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e"
|
resolved "https://registry.npmmirror.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e"
|
||||||
@ -810,6 +820,11 @@ async@^3.2.3:
|
|||||||
resolved "https://registry.npmmirror.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c"
|
resolved "https://registry.npmmirror.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c"
|
||||||
integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==
|
integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==
|
||||||
|
|
||||||
|
asynckit@^0.4.0:
|
||||||
|
version "0.4.0"
|
||||||
|
resolved "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
|
||||||
|
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
|
||||||
|
|
||||||
autoprefixer@^10.4.2:
|
autoprefixer@^10.4.2:
|
||||||
version "10.4.14"
|
version "10.4.14"
|
||||||
resolved "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.14.tgz#e28d49902f8e759dd25b153264e862df2705f79d"
|
resolved "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.14.tgz#e28d49902f8e759dd25b153264e862df2705f79d"
|
||||||
@ -822,6 +837,15 @@ autoprefixer@^10.4.2:
|
|||||||
picocolors "^1.0.0"
|
picocolors "^1.0.0"
|
||||||
postcss-value-parser "^4.2.0"
|
postcss-value-parser "^4.2.0"
|
||||||
|
|
||||||
|
axios@^1.4.0:
|
||||||
|
version "1.4.0"
|
||||||
|
resolved "https://registry.npmmirror.com/axios/-/axios-1.4.0.tgz#38a7bf1224cd308de271146038b551d725f0be1f"
|
||||||
|
integrity sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==
|
||||||
|
dependencies:
|
||||||
|
follow-redirects "^1.15.0"
|
||||||
|
form-data "^4.0.0"
|
||||||
|
proxy-from-env "^1.1.0"
|
||||||
|
|
||||||
balanced-match@^1.0.0:
|
balanced-match@^1.0.0:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
|
resolved "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
|
||||||
@ -1047,6 +1071,13 @@ colord@^2.9.3:
|
|||||||
resolved "https://registry.npmmirror.com/colord/-/colord-2.9.3.tgz#4f8ce919de456f1d5c1c368c307fe20f3e59fb43"
|
resolved "https://registry.npmmirror.com/colord/-/colord-2.9.3.tgz#4f8ce919de456f1d5c1c368c307fe20f3e59fb43"
|
||||||
integrity sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==
|
integrity sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==
|
||||||
|
|
||||||
|
combined-stream@^1.0.8:
|
||||||
|
version "1.0.8"
|
||||||
|
resolved "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
|
||||||
|
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
|
||||||
|
dependencies:
|
||||||
|
delayed-stream "~1.0.0"
|
||||||
|
|
||||||
commander@^2.19.0:
|
commander@^2.19.0:
|
||||||
version "2.20.3"
|
version "2.20.3"
|
||||||
resolved "https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
|
resolved "https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
|
||||||
@ -1177,6 +1208,11 @@ define-lazy-prop@^2.0.0:
|
|||||||
resolved "https://registry.npmmirror.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f"
|
resolved "https://registry.npmmirror.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f"
|
||||||
integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==
|
integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==
|
||||||
|
|
||||||
|
delayed-stream@~1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
|
||||||
|
integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
|
||||||
|
|
||||||
depd@2.0.0:
|
depd@2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
|
resolved "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
|
||||||
@ -1806,6 +1842,20 @@ flatted@^3.1.0:
|
|||||||
resolved "https://registry.npmmirror.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787"
|
resolved "https://registry.npmmirror.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787"
|
||||||
integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==
|
integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==
|
||||||
|
|
||||||
|
follow-redirects@^1.15.0:
|
||||||
|
version "1.15.2"
|
||||||
|
resolved "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"
|
||||||
|
integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==
|
||||||
|
|
||||||
|
form-data@^4.0.0:
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.npmmirror.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
|
||||||
|
integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
|
||||||
|
dependencies:
|
||||||
|
asynckit "^0.4.0"
|
||||||
|
combined-stream "^1.0.8"
|
||||||
|
mime-types "^2.1.12"
|
||||||
|
|
||||||
forwarded@0.2.0:
|
forwarded@0.2.0:
|
||||||
version "0.2.0"
|
version "0.2.0"
|
||||||
resolved "https://registry.npmmirror.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
|
resolved "https://registry.npmmirror.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
|
||||||
@ -2322,7 +2372,7 @@ mime-db@1.52.0, "mime-db@>= 1.43.0 < 2":
|
|||||||
resolved "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
|
resolved "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
|
||||||
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
|
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
|
||||||
|
|
||||||
mime-types@~2.1.24, mime-types@~2.1.34:
|
mime-types@^2.1.12, mime-types@~2.1.24, mime-types@~2.1.34:
|
||||||
version "2.1.35"
|
version "2.1.35"
|
||||||
resolved "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
|
resolved "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
|
||||||
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
|
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
|
||||||
@ -2681,6 +2731,11 @@ proxy-addr@~2.0.7:
|
|||||||
forwarded "0.2.0"
|
forwarded "0.2.0"
|
||||||
ipaddr.js "1.9.1"
|
ipaddr.js "1.9.1"
|
||||||
|
|
||||||
|
proxy-from-env@^1.1.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
|
||||||
|
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
|
||||||
|
|
||||||
punycode@1.3.2:
|
punycode@1.3.2:
|
||||||
version "1.3.2"
|
version "1.3.2"
|
||||||
resolved "https://registry.npmmirror.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
|
resolved "https://registry.npmmirror.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
|
||||||
@ -3106,6 +3161,11 @@ toidentifier@1.0.1:
|
|||||||
resolved "https://registry.npmmirror.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
|
resolved "https://registry.npmmirror.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
|
||||||
integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
|
integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
|
||||||
|
|
||||||
|
ts-md5@^1.3.1:
|
||||||
|
version "1.3.1"
|
||||||
|
resolved "https://registry.npmmirror.com/ts-md5/-/ts-md5-1.3.1.tgz#f5b860c0d5241dd9bb4e909dd73991166403f511"
|
||||||
|
integrity sha512-DiwiXfwvcTeZ5wCE0z+2A9EseZsztaiZtGrtSaY5JOD7ekPnR/GoIVD5gXZAlK9Na9Kvpo9Waz5rW64WKAWApg==
|
||||||
|
|
||||||
tslib@^1.8.1:
|
tslib@^1.8.1:
|
||||||
version "1.14.1"
|
version "1.14.1"
|
||||||
resolved "https://registry.npmmirror.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
|
resolved "https://registry.npmmirror.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
|
||||||
|
Loading…
Reference in New Issue
Block a user