交互插件结构、名称调整

属性页面基础布局
绘制store创建
This commit is contained in:
walker 2023-05-20 18:46:51 +08:00
parent 8b86abf994
commit addb813a2b
13 changed files with 256 additions and 70 deletions

View File

@ -0,0 +1,15 @@
<template>
<div v-if="drawStore.drawMode" class="q-pa-md">图形模板</div>
<div v-else class="q-pa-md"></div>
</template>
<script setup lang="ts">
import { useDrawStore } from 'src/stores/draw-store';
import { onMounted, ref } from 'vue';
const drawStore = useDrawStore();
onMounted(() => {
console.log('绘制属性组件mounted');
});
</script>

View File

@ -0,0 +1,65 @@
<template>
<q-form @submit="onSubmit" @reset="onReset" class="q-gutter-md">
<q-input
filled
v-model="name"
label="Your name *"
hint="Name and surname"
lazy-rules
:rules="[(val) => (val && val.length > 0) || 'Please type something']"
/>
<q-input
filled
type="number"
v-model="age"
label="Your age *"
lazy-rules
:rules="[
(val) => (val !== null && val !== '') || 'Please type your age',
(val) => (val > 0 && val < 100) || 'Please type a real age',
]"
/>
<q-toggle v-model="accept" label="I accept the license and terms" />
<div>
<q-btn label="Submit" type="submit" color="primary" />
<q-btn label="Reset" type="reset" color="primary" flat class="q-ml-sm" />
</div>
</q-form>
</template>
<script setup lang="ts">
import { useQuasar } from 'quasar';
import { ref } from 'vue';
const $q = useQuasar();
const name = ref(null);
const age = ref(null);
const accept = ref(false);
function onSubmit() {
if (accept.value !== true) {
$q.notify({
color: 'red-5',
textColor: 'white',
icon: 'warning',
message: 'You need to accept the license and terms first',
});
} else {
$q.notify({
color: 'green-4',
textColor: 'white',
icon: 'cloud_done',
message: 'Submitted',
});
}
}
function onReset() {
name.value = null;
age.value = null;
accept.value = false;
}
</script>

View File

@ -0,0 +1,65 @@
<template>
<q-form @submit="onSubmit" @reset="onReset" class="q-gutter-md">
<q-input
filled
v-model="name"
label="Your name *"
hint="Name and surname"
lazy-rules
:rules="[(val) => (val && val.length > 0) || 'Please type something']"
/>
<q-input
filled
type="number"
v-model="age"
label="Your age *"
lazy-rules
:rules="[
(val) => (val !== null && val !== '') || 'Please type your age',
(val) => (val > 0 && val < 100) || 'Please type a real age',
]"
/>
<q-toggle v-model="accept" label="I accept the license and terms" />
<div>
<q-btn label="Submit" type="submit" color="primary" />
<q-btn label="Reset" type="reset" color="primary" flat class="q-ml-sm" />
</div>
</q-form>
</template>
<script setup lang="ts">
import { useQuasar } from 'quasar';
import { ref } from 'vue';
const $q = useQuasar();
const name = ref(null);
const age = ref(null);
const accept = ref(false);
function onSubmit() {
if (accept.value !== true) {
$q.notify({
color: 'red-5',
textColor: 'white',
icon: 'warning',
message: 'You need to accept the license and terms first',
});
} else {
$q.notify({
color: 'green-4',
textColor: 'white',
icon: 'cloud_done',
message: 'Submitted',
});
}
}
function onReset() {
name.value = null;
age.value = null;
accept.value = false;
}
</script>

View File

@ -21,7 +21,7 @@ export interface IProtoGraphicData extends pb_1.Message {
code: string;
}
export abstract class BaseGraphicData implements GraphicData {
export abstract class GraphicDataBase implements GraphicData {
_data: IProtoGraphicData;
constructor(data: IProtoGraphicData) {
this._data = data;
@ -92,10 +92,10 @@ export abstract class BaseGraphicData implements GraphicData {
clone(): GraphicData {
throw new Error('Method not implemented.');
}
copyFrom(gd: BaseGraphicData): void {
copyFrom(gd: GraphicDataBase): void {
pb_1.Message.copyInto(gd._data, this._data);
}
eq(other: BaseGraphicData): boolean {
eq(other: GraphicDataBase): boolean {
return pb_1.Message.equals(this._data, other._data);
}
}

View File

@ -1,16 +1,16 @@
import * as pb_1 from 'google-protobuf';
import { IIscsFanData } from 'src/graphics/iscs-fan/IscsFan';
import { graphicData } from '../protos/draw_data_storage';
import { BaseGraphicData } from './BaseGraphicData';
import { GraphicDataBase } from './GraphicDataBase';
export class IscsFanData extends BaseGraphicData implements IIscsFanData {
export class IscsFanData extends GraphicDataBase implements IIscsFanData {
constructor(data?: graphicData.IscsFan) {
let fan;
if (data) {
fan = data;
} else {
fan = new graphicData.IscsFan({
common: BaseGraphicData.defaultCommonInfo(),
common: GraphicDataBase.defaultCommonInfo(),
});
}
super(fan);

View File

@ -2,14 +2,14 @@ import * as pb_1 from 'google-protobuf';
import { IPointData } from 'pixi.js';
import { ILinkData } from 'src/graphics/link/Link';
import { graphicData } from '../protos/draw_data_storage';
import { BaseGraphicData } from './BaseGraphicData';
import { GraphicDataBase } from './GraphicDataBase';
export class LinkData extends BaseGraphicData implements ILinkData {
export class LinkData extends GraphicDataBase implements ILinkData {
constructor(data?: graphicData.Link) {
let link;
if (!data) {
link = new graphicData.Link({
common: BaseGraphicData.defaultCommonInfo(),
common: GraphicDataBase.defaultCommonInfo(),
});
} else {
link = data;

View File

@ -70,7 +70,22 @@ export const DefaultCanvasMenu = new ContextMenu({
],
});
export function initDrawApp(app: JlDrawApp) {
let drawApp: JlDrawApp | null = null;
export function getDrawApp(): JlDrawApp | null {
return drawApp;
}
export function destroyDrawApp(): void {
if (drawApp) {
drawApp.destroy();
drawApp = null;
}
}
export function initDrawApp(dom: HTMLElement): JlDrawApp {
drawApp = new JlDrawApp(dom);
const app = drawApp;
app.setOptions({
drawAssistants: [
new LinkDraw(app, () => {
@ -137,6 +152,7 @@ export function initDrawApp(app: JlDrawApp) {
},
})
);
return drawApp;
}
const StorageKey = 'graphic-storage';

View File

@ -11,22 +11,19 @@ import {
} from 'pixi.js';
import { GraphicIdGenerator } from '../core/IdGenerator';
import { GraphicData, GraphicTemplate, JlGraphic } from '../core/JlGraphic';
import {
AppDragEvent,
BoundsGraphic,
GraphicTransformEvent,
ShiftData,
TransformPoints,
} from '../plugins';
import { JlOperation } from '../operation/JlOperation';
import {
AppDragEvent,
AppInteractionPlugin,
CombinationKey,
GraphicTransformEvent,
InteractionPlugin,
KeyListener,
ShiftData,
ViewportMovePlugin,
} from '../plugins';
import { CommonMouseTool } from '../plugins/CommonMousePlugin';
import { MenuItemOptions } from '../ui/Menu';
import { DOWN, LEFT, RIGHT, UP, recursiveChildren } from '../utils';
import {
CanvasData,
@ -35,8 +32,6 @@ import {
ICanvasProperties,
JlCanvas,
} from './JlGraphicApp';
import { ContextMenu } from '../ui/ContextMenu';
import { MenuItemOptions, MenuOptions } from '../ui/Menu';
/**
*

View File

@ -276,8 +276,8 @@ export interface GraphicAppEvents extends GlobalMixins.GraphicAppEvents {
graphicstored: [graphic: JlGraphic];
graphicdeleted: [graphic: JlGraphic];
loadfinish: [];
'interaction-plugin-resume': [activeTool: InteractionPlugin]; // 交互插件启用
'interaction-plugin-pause': [activeTool: InteractionPlugin]; // 交互插件停止
'interaction-plugin-resume': [plugin: InteractionPlugin]; // 交互插件启用
'interaction-plugin-pause': [plugin: InteractionPlugin]; // 交互插件停止
'options-update': [options: GraphicAppOptions]; // 配置更新
graphicselectedchange: [graphic: JlGraphic, selected: boolean];
graphicchildselectedchange: [child: DisplayObject, selected: boolean];

View File

@ -8,7 +8,7 @@ import {
} from 'pixi.js';
import {
AppDragEvent,
BaseInteractionPlugin,
InteractionPluginBase,
InteractionPluginType,
KeyListener,
} from '.';
@ -154,7 +154,7 @@ export class GraphicTransformEvent {
}
}
export class GraphicTransformPlugin extends BaseInteractionPlugin {
export class GraphicTransformPlugin extends InteractionPluginBase {
static Name = '__graphic_transform_plugin';
/**

View File

@ -33,9 +33,12 @@ export interface InteractionPlugin {
*
*/
isActive(): boolean;
isAppPlugin(): boolean;
isOtherPlugin(): boolean;
isGraphicPlugin(): boolean;
}
export abstract class BaseInteractionPlugin implements InteractionPlugin {
export abstract class InteractionPluginBase implements InteractionPlugin {
readonly _type: string;
name: string; // 唯一标识
app: GraphicApp;
@ -55,6 +58,7 @@ export abstract class BaseInteractionPlugin implements InteractionPlugin {
resume(): void {
this.bind();
this._pause = false;
this.app.emit('interaction-plugin-resume', this);
}
/**
*
@ -62,6 +66,7 @@ export abstract class BaseInteractionPlugin implements InteractionPlugin {
pause(): void {
this.unbind();
this._pause = true;
this.app.emit('interaction-plugin-pause', this);
}
abstract bind(): void;
@ -72,6 +77,21 @@ export abstract class BaseInteractionPlugin implements InteractionPlugin {
isActive(): boolean {
return !this._pause;
}
isGraphicPlugin(): boolean {
return this._type === InteractionPluginType.Graphic;
}
isAppPlugin(): boolean {
return this._type === InteractionPluginType.App;
}
isOtherPlugin(): boolean {
return this._type === InteractionPluginType.Other;
}
}
export abstract class OtherInteractionPlugin extends InteractionPluginBase {
constructor(app: GraphicApp, name: string) {
super(app, name, InteractionPluginType.Other);
}
}
export class AppDragEvent {
@ -152,7 +172,7 @@ export class AppDragEvent {
/**
*
*/
export class DragPlugin extends BaseInteractionPlugin {
export class DragPlugin extends OtherInteractionPlugin {
static Name = '__drag_operation_plugin';
private threshold = 3;
target: DisplayObject | null = null;
@ -160,7 +180,7 @@ export class DragPlugin extends BaseInteractionPlugin {
startClientPoint: Point | null = null;
drag = false;
constructor(app: GraphicApp) {
super(app, DragPlugin.Name, InteractionPluginType.Other);
super(app, DragPlugin.Name);
app.on('options-update', (options: IGraphicAppConfig) => {
if (options.threshold !== undefined) {
this.threshold = options.threshold;
@ -258,7 +278,7 @@ export class DragPlugin extends BaseInteractionPlugin {
/**
*
*/
export class ViewportMovePlugin extends BaseInteractionPlugin {
export class ViewportMovePlugin extends OtherInteractionPlugin {
static Name = '__viewport_move_plugin';
static MoveInterval = 20; // 移动间隔单位ms
@ -270,7 +290,7 @@ export class ViewportMovePlugin extends BaseInteractionPlugin {
moveSpeedy = 0;
constructor(app: GraphicApp) {
super(app, ViewportMovePlugin.Name, InteractionPluginType.Other);
super(app, ViewportMovePlugin.Name);
}
static new(app: GraphicApp): ViewportMovePlugin {
@ -360,9 +380,7 @@ export class ViewportMovePlugin extends BaseInteractionPlugin {
/**
*
*/
export abstract class AppInteractionPlugin extends BaseInteractionPlugin {
readonly _type = InteractionPluginType.App;
export abstract class AppInteractionPlugin extends InteractionPluginBase {
constructor(name: string, app: GraphicApp) {
super(app, name, InteractionPluginType.App);
}
@ -372,8 +390,7 @@ export abstract class AppInteractionPlugin extends BaseInteractionPlugin {
*/
resume(): void {
this.app.pauseAppInteractionPlugins();
this.bind();
this._pause = false;
super.resume();
}
}
@ -409,16 +426,27 @@ export abstract class GraphicInteractionPlugin<G extends JlGraphic>
isActive(): boolean {
return !this._pause;
}
isAppPlugin(): boolean {
return false;
}
isOtherPlugin(): boolean {
return false;
}
isGraphicPlugin(): boolean {
return true;
}
resume(): void {
const list = this.filter(...this.app.queryStore.getAllGraphics());
this.binds(list);
this._pause = false;
this.app.emit('interaction-plugin-resume', this);
}
pause(): void {
const list = this.filter(...this.app.queryStore.getAllGraphics());
this.unbinds(list);
this._pause = true;
this.app.emit('interaction-plugin-pause', this);
}
/**

View File

@ -97,35 +97,7 @@
<q-drawer show-if-above bordered v-model="rightDrawerOpen" side="right">
<q-resize-observer @resize="onRightResize" />
<!-- drawer content -->
<q-list bordered padding class="rounded-borders text-primary">
<q-item
clickable
v-ripple
:active="link === 'inbox'"
@click="link = 'inbox'"
active-class="my-menu-link"
>
<q-item-section avatar>
<q-icon name="inbox" />
</q-item-section>
<q-item-section>Inbox</q-item-section>
</q-item>
<q-item
clickable
v-ripple
:active="link === 'outbox'"
@click="link = 'outbox'"
active-class="my-menu-link"
>
<q-item-section avatar>
<q-icon name="send" />
</q-item-section>
<q-item-section>Outbox</q-item-section>
</q-item>
</q-list>
<draw-properties></draw-properties>
</q-drawer>
<q-page-container>
@ -135,10 +107,13 @@
</template>
<script setup lang="ts">
import { initDrawApp, loadDrawDatas } from 'src/examples/app';
import { JlDrawApp } from 'src/jlgraphic';
import DrawProperties from 'src/components/draw-app/DrawProperties.vue';
import { getDrawApp, initDrawApp, loadDrawDatas } from 'src/examples/app';
import { useDrawStore } from 'src/stores/draw-store';
import { onMounted, onUnmounted, ref } from 'vue';
const drawStore = useDrawStore();
const leftDrawerOpen = ref(false);
const rightDrawerOpen = ref(false);
function toggleLeftDrawer() {
@ -152,12 +127,11 @@ function toggleRightDrawer() {
const link = ref('outbox');
let drawApp: JlDrawApp | null = null;
onMounted(() => {
console.log('绘制应用layout mounted');
const dom = document.getElementById('draw-app-container');
if (dom) {
drawApp = new JlDrawApp(dom);
initDrawApp(drawApp);
const drawApp = drawStore.initDrawApp(dom);
loadDrawDatas(drawApp);
}
});
@ -194,14 +168,13 @@ function onResize() {
dom.style.width = canvasWidth.value + 'px';
dom.style.height = canvasHeight.value + 'px';
}
const drawApp = getDrawApp();
if (drawApp) {
drawApp.onDomResize(canvasWidth.value, canvasHeight.value);
}
}
onUnmounted(() => {
if (drawApp) {
drawApp.destroy();
}
drawStore.destroy();
});
</script>

29
src/stores/draw-store.ts Normal file
View File

@ -0,0 +1,29 @@
import { defineStore } from 'pinia';
import { destroyDrawApp, initDrawApp } from 'src/examples/app';
import { GraphicDrawAssistant } from 'src/jlgraphic';
export const useDrawStore = defineStore('draw', {
state: () => ({
drawMode: false,
}),
getters: {},
actions: {
initDrawApp(dom: HTMLElement) {
const app = initDrawApp(dom);
app.on('interaction-plugin-resume', (plugin) => {
if (plugin.isAppPlugin()) {
if (plugin instanceof GraphicDrawAssistant) {
this.drawMode = true;
} else {
this.drawMode = false;
}
}
});
return app;
},
destroy() {
this.drawMode = false;
destroyDrawApp();
},
},
});