diff --git a/src/components/draw-app/TccDrawProperties.vue b/src/components/draw-app/TccDrawProperties.vue new file mode 100644 index 0000000..5a7eb45 --- /dev/null +++ b/src/components/draw-app/TccDrawProperties.vue @@ -0,0 +1,39 @@ + + + diff --git a/src/components/draw-app/properties/CanvasTccProperty.vue b/src/components/draw-app/properties/CanvasTccProperty.vue new file mode 100644 index 0000000..799f61b --- /dev/null +++ b/src/components/draw-app/properties/CanvasTccProperty.vue @@ -0,0 +1,80 @@ + + + diff --git a/src/components/draw-app/properties/TccButtonProperty.vue b/src/components/draw-app/properties/TccButtonProperty.vue new file mode 100644 index 0000000..1d2a699 --- /dev/null +++ b/src/components/draw-app/properties/TccButtonProperty.vue @@ -0,0 +1,43 @@ + + + diff --git a/src/components/draw-app/properties/TccTextProperty.vue b/src/components/draw-app/properties/TccTextProperty.vue new file mode 100644 index 0000000..35bba17 --- /dev/null +++ b/src/components/draw-app/properties/TccTextProperty.vue @@ -0,0 +1,50 @@ + + + diff --git a/src/drawApp/graphics/TccButtonInteraction.ts b/src/drawApp/graphics/TccButtonInteraction.ts new file mode 100644 index 0000000..1dc75b6 --- /dev/null +++ b/src/drawApp/graphics/TccButtonInteraction.ts @@ -0,0 +1,149 @@ +import * as pb_1 from 'google-protobuf'; +import { GraphicInteractionPlugin, IGraphicScene, JlGraphic } from 'jl-graphic'; +import { tccGraphicData } from 'src/protos/tccGraphics'; +import { GraphicDataBase, GraphicStateBase } from './GraphicDataBase'; +import { useLineStore } from 'src/stores/line-store'; +import { DisplayObject, FederatedMouseEvent } from 'pixi.js'; +import { useTccStore } from 'src/stores/tcc-store'; +import { state } from 'src/protos/device_state'; +import { ITccButtonData, ITccButtonState, TccButton } from 'src/graphics/tccButton/TccButton'; + +export class TccButtonData extends GraphicDataBase implements ITccButtonData { + constructor(data?: tccGraphicData.TccButton) { + let tccButton; + if (data) { + tccButton = data; + } else { + tccButton = new tccGraphicData.TccButton({ + common: GraphicDataBase.defaultCommonInfo(TccButton.Type), + }); + } + super(tccButton); + } + + public get data(): tccGraphicData.TccButton { + return this.getData(); + } + + get code(): string { + return this.data.code; + } + set code(v: string) { + this.data.code = v; + } + get buttonColor(): tccGraphicData.TccElementColor { + return this.data.buttonColor; + } + set buttonColor(v: tccGraphicData.TccElementColor) { + this.data.buttonColor = v; + } + get isSelfReset(): boolean { + return this.data.isSelfReset; + } + set isSelfReset(v: boolean) { + this.data.isSelfReset = v; + } + clone(): TccButtonData { + return new TccButtonData(this.data.cloneMessage()); + } + copyFrom(data: TccButtonData): void { + pb_1.Message.copyInto(data.data, this.data); + } + eq(other: TccButtonData): boolean { + return pb_1.Message.equals(this.data, other.data); + } +} +export class TccButtonState + extends GraphicStateBase + implements ITccButtonState +{ + constructor(proto?: state.ButtonState) { + let states; + if (proto) { + states = proto; + } else { + states = new state.ButtonState(); + } + super(states, TccButton.Type); + } + get code(): string { + return this.states.id + ''; + } + get down(): boolean { + return this.states.down; + } + set down(v: boolean) { + this.states.down = v; + } + get states(): state.ButtonState { + return this.getState(); + } + clone(): TccButtonState { + return new TccButtonState(this.states.cloneMessage()); + } + 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); + } +} + +export class TccButtonOperateInteraction extends GraphicInteractionPlugin { + static Name = 'tcc_button_operate_menu'; + constructor(app: IGraphicScene) { + super(TccButtonOperateInteraction.Name, app); + } + static init(app: IGraphicScene) { + return new TccButtonOperateInteraction(app); + } + filter(...grahpics: JlGraphic[]): TccButton[] | undefined { + return grahpics + .filter((g) => g.type === TccButton.Type) + .map((g) => g as TccButton); + } + bind(g: TccButton): void { + g.eventMode = 'static'; + g.cursor = 'pointer'; + g.on('mousedown', this.onMouseDown, this); + g.on('mouseup', this.onMouseUp, this); + g.on('mouseout', this.onMouseOut, this); + } + + unbind(g: TccButton): void { + g.eventMode = 'none'; + g.off('mousedown', this.onMouseDown, this); + g.on('mouseup', this.onMouseUp, this); + g.on('mouseout', this.onMouseOut, this); + } + onMouseOut(e: FederatedMouseEvent) { + const target = e.target as DisplayObject; + const tccButton = target.getGraphic() as TccButton; + if (tccButton.states.down && tccButton.datas.isSelfReset) { + tccButton.states.down = false; + tccButton.doRepaint(); + } + } + onMouseDown(e: FederatedMouseEvent) { + const simulationId = useLineStore().simulationId; + const mapId = useLineStore().mapId; + const tccId = useTccStore().tccId; + const target = e.target as DisplayObject; + const tccButton = target.getGraphic() as TccButton; + if (!simulationId || !mapId) { + return; + } + console.log('按钮按下'); + } + onMouseUp(e: FederatedMouseEvent) { + const simulationId = useLineStore().simulationId; + const mapId = useLineStore().mapId; + const tccId = useTccStore().tccId; + const target = e.target as DisplayObject; + const tccButton = target.getGraphic() as TccButton; + if (!simulationId || !mapId || !tccButton.datas.isSelfReset) { + return; + } + console.log('按钮弹起'); + } +} diff --git a/src/drawApp/graphics/TccTextContentInteraction.ts b/src/drawApp/graphics/TccTextContentInteraction.ts new file mode 100644 index 0000000..76c5dc7 --- /dev/null +++ b/src/drawApp/graphics/TccTextContentInteraction.ts @@ -0,0 +1,59 @@ +import * as pb_1 from 'google-protobuf'; +import { + ITextContentData, + TextContent, +} from 'src/graphics/textContent/TextContent'; +import { tccGraphicData } from 'src/protos/tccGraphics'; +import { GraphicDataBase } from './GraphicDataBase'; + +export class TccTextData extends GraphicDataBase implements ITextContentData { + constructor(data?: tccGraphicData.TccText) { + let tccText; + if (data) { + tccText = data; + } else { + tccText = new tccGraphicData.TccText({ + common: GraphicDataBase.defaultCommonInfo(TextContent.Type), + }); + } + super(tccText); + } + + public get data(): tccGraphicData.TccText { + return this.getData(); + } + + get code(): string { + return this.data.code; + } + set code(v: string) { + this.data.code = v; + } + get content(): string { + return this.data.content; + } + set content(v: string) { + this.data.content = v; + } + get color(): string { + return this.data.color; + } + set color(v: string) { + this.data.color = v; + } + get fontSize(): number { + return this.data.fontSize; + } + set fontSize(v: number) { + this.data.fontSize = v; + } + clone(): TccTextData { + return new TccTextData(this.data.cloneMessage()); + } + copyFrom(data: TccTextData): void { + pb_1.Message.copyInto(data.data, this.data); + } + eq(other: TccTextData): boolean { + return pb_1.Message.equals(this.data, other.data); + } +} diff --git a/src/drawApp/tccApp.ts b/src/drawApp/tccApp.ts new file mode 100644 index 0000000..42876bb --- /dev/null +++ b/src/drawApp/tccApp.ts @@ -0,0 +1,204 @@ +import { fromUint8Array, toUint8Array } from 'js-base64'; +import { + CombinationKey, + IDrawApp, + IGraphicStorage, + KeyListener, + newDrawApp, + GraphicData, + ContextMenu, + MenuItemOptions, +} from 'jl-graphic'; +import { graphicData } from 'src/protos/stationLayoutGraphics'; +import { tccGraphicData } from 'src/protos/tccGraphics'; +import { saveDraft, getDraft } from 'src/api/DraftApi'; +import { useTccDrawStore } from 'src/stores/tcc-draw-store'; +import { successNotify, errorNotify } from '../utils/CommonNotify'; +import { toStorageTransform } from './graphics/GraphicDataBase'; +import { Dialog } from 'quasar'; +import { checkMapData } from 'src/api/Simulation'; +import { TccButtonData, TccButtonState } from './graphics/TccButtonInteraction'; +import { TccButtonDraw } from 'src/graphics/tccButton/TccButtonDrawAssistant'; +import { TccButton, TccButtonTemplate } from 'src/graphics/tccButton/TccButton'; +import { + TextContent, + TextContentTemplate, +} from 'src/graphics/textContent/TextContent'; +import { TccTextData } from './graphics/TccTextContentInteraction'; +import { TextContentDraw } from 'src/graphics/textContent/TextContentDrawAssistant'; + +const UndoOptions: MenuItemOptions = { + name: '撤销', +}; +const RedoOptions: MenuItemOptions = { + name: '重做', +}; +const SelectAllOptions: MenuItemOptions = { + name: '全选', +}; + +export const DefaultCanvasMenu = new ContextMenu({ + name: '绘制-画布菜单', + groups: [ + { + items: [UndoOptions, RedoOptions], + }, + { + items: [SelectAllOptions], + }, + ], +}); + +let drawApp: IDrawApp | null = null; + +export function getTccDrawApp(): IDrawApp | null { + return drawApp; +} + +export function destroyTccDrawApp(): void { + if (drawApp) { + drawApp.destroy(); + drawApp = null; + } +} + +export function initTccDrawApp(): IDrawApp { + drawApp = newDrawApp({ + dataLoader: loadTccDrawDatas, + }); + const app = drawApp; + //根据草稿图类型加载绘图工具 + new TccButtonDraw( + app, + new TccButtonTemplate(new TccButtonData(), new TccButtonState()) + ); + new TextContentDraw(app, new TextContentTemplate(new TccTextData())); + + // 画布右键菜单 + app.registerMenu(DefaultCanvasMenu); + app.canvas.on('_rightclick', (e) => { + if (app.drawing) return; + UndoOptions.disabled = !app.opRecord.hasUndo; + RedoOptions.disabled = !app.opRecord.hasRedo; + + UndoOptions.handler = () => { + app.opRecord.undo(); + }; + RedoOptions.handler = () => { + app.opRecord.redo(); + }; + SelectAllOptions.handler = () => { + app.selectAllGraphics(); + }; + DefaultCanvasMenu.open(e.global); + }); + app.addKeyboardListener( + new KeyListener({ + value: 'KeyS', + global: true, + combinations: [CombinationKey.Ctrl], + onPress: () => { + saveTccDrawToServer(app); + }, + }) + ); + return drawApp; +} + +export function checkTccDataToServer(app: IDrawApp) { + const base64 = saveTccDrawDatas(app); + checkMapData({ mapProto: base64 }) + .then((res) => { + if (res.data.success) { + successNotify('校验数据成功!'); + } else { + let message = ''; + res.data.errors.forEach((err: string) => { + message += `
${err};
`; + }); + Dialog.create({ + title: '校验失败', + message: message, + html: true, + persistent: true, + ok: { + ali: 'center', + }, + }); + } + }) + .catch((err) => { + errorNotify(err.message, err); + }); +} + +export function saveTccDrawToServer(app: IDrawApp) { + const base64 = saveTccDrawDatas(app); + const tccDrawStore = useTccDrawStore(); + const id = tccDrawStore.draftId; + if (!id) { + return; + } + saveDraft(id as number, { proto: base64 }) + .then(() => { + successNotify('保存数据成功!'); + }) + .catch((err) => { + errorNotify(err.message, err); + }); +} + +export function saveTccDrawDatas(app: IDrawApp) { + const storage = new tccGraphicData.TccGraphicStorage(); + const canvasData = app.canvas.saveData(); + storage.canvas = new graphicData.Canvas({ + width: canvasData.width, + height: canvasData.height, + backgroundColor: canvasData.backgroundColor, + viewportTransform: toStorageTransform(canvasData.viewportTransform), + }); + const graphics = app.queryStore.getAllGraphics(); + graphics.forEach((g) => { + if (TccButton.Type === g.type) { + const tccButtonData = (g as TccButton).saveData(); + storage.tccButtons.push((tccButtonData as TccButtonData).data); + } else if (TextContent.Type === g.type) { + const tccTextData = (g as TextContent).saveData(); + storage.tccTexts.push((tccTextData as TccTextData).data); + } + }); + const base64 = fromUint8Array(storage.serialize()); + console.log('保存数据', storage); + return base64; +} + +export async function loadTccDrawDatas(): Promise { + // console.log('加载数据', base64); + const tccDrawStore = useTccDrawStore(); + const id = tccDrawStore.draftId; + if (!id) { + throw new Error('获取数据异常:为获取到草稿地图ID'); + } + const { proto: base64 } = await getDraft(id); + if (base64) { + const storage = tccGraphicData.TccGraphicStorage.deserialize( + toUint8Array(base64) + ); + // app.updateCanvas(storage.canvas); + const datas: GraphicData[] = []; + storage.tccButtons.forEach((tccButton) => { + datas.push(new TccButtonData(tccButton)); + }); + storage.tccTexts.forEach((tccText) => { + datas.push(new TccTextData(tccText)); + }); + return { + canvasProperty: storage.canvas, + datas: datas, + }; + } else { + return Promise.resolve({ + datas: [], + }); + } +} diff --git a/src/drawApp/tccScene.ts b/src/drawApp/tccScene.ts new file mode 100644 index 0000000..7a7b3f1 --- /dev/null +++ b/src/drawApp/tccScene.ts @@ -0,0 +1,94 @@ +import { + GraphicData, + GraphicState, + IGraphicApp, + IGraphicScene, + IGraphicStorage, +} from 'jl-graphic'; +import { getPublishMapInfoByName } from 'src/api/PublishApi'; +import { useTccStore } from 'src/stores/tcc-store'; +import { toUint8Array } from 'js-base64'; +import { useLineStore } from 'src/stores/line-store'; +import { state } from 'src/protos/device_state'; +import { TccButtonData, TccButtonOperateInteraction, TccButtonState } from './graphics/TccButtonInteraction'; +import { tccGraphicData } from 'src/protos/tccGraphics'; +import { TccButtonTemplate } from 'src/graphics/tccButton/TccButton'; + + +export function initTccScene(lineApp: IGraphicApp, sceneName: string) { + const tccScene = lineApp.initScene(sceneName, { + dataLoader: loadTccDatas, + mouseToolOptions: { + boxSelect: false, + viewportDrag: false, + wheelZoom: false, + }, + }); + const graphicTemplate = [ + new TccButtonTemplate(new TccButtonData(), new TccButtonState()), + ]; + TccButtonOperateInteraction.init(tccScene); + tccScene.registerGraphicTemplates(...graphicTemplate); + tccScene.on('postdataloaded', () => { + handleSubscribe(tccScene); + }); + tccScene.setOptions({ + mouseToolOptions: { + boxSelect: false, + viewportDrag: false, + wheelZoom: false, + }, + }); + return tccScene; +} + +function handleSubscribe(tccScene: IGraphicScene) { + const lineStore = useLineStore(); + const tccStore = useTccStore(); + const simulationId = lineStore.simulationId; + const mapId = lineStore.mapId; + const tccId = tccStore.tccId; + const app = tccScene; + app.subscribe({ + destination: `/rtsts/simulation/${simulationId}/tcc/${mapId}/${tccId}`, + messageConverter: (message: Uint8Array) => { + // console.log('收到消息', message); + const states: GraphicState[] = []; + const storage = state.PushedDevicesStatus.deserialize(message); + if (storage.all) { + // storage.allStatus.buttonState.forEach((item) => { + // if (item.id) { + // states.push(new TccButtonState(item)); + // } + // }); + } + // console.log(states, 'states'); + return states; + }, + }); +} + +async function loadTccDatas(): Promise { + const tccStore = useTccStore(); + const { proto: base64 } = await getPublishMapInfoByName({ + name: tccStore.tccMapCode, + detail: true, + }); + if (base64) { + const storage = tccGraphicData.TccGraphicStorage.deserialize( + toUint8Array(base64) + ); + const datas: GraphicData[] = []; + storage.tccButtons.forEach((tccButton) => { + datas.push(new TccButtonData(tccButton)); + }); + return Promise.resolve({ + canvasProperty: storage.canvas, + datas: datas, + }); + } else { + return Promise.resolve({ + datas: [], + }); + } +} diff --git a/src/graphics/tccButton/TccButton.ts b/src/graphics/tccButton/TccButton.ts new file mode 100644 index 0000000..6c5d93b --- /dev/null +++ b/src/graphics/tccButton/TccButton.ts @@ -0,0 +1,104 @@ +import { + GraphicData, + GraphicState, + JlGraphic, + JlGraphicTemplate, +} from 'jl-graphic'; +import Tcc_Button_Assets from './tcc-button-spritesheet.png'; +import Tcc_Button_JSON from './tcc-button-data.json'; + +import { Assets, Sprite, Spritesheet, Texture } from 'pixi.js'; +import { tccGraphicData } from 'src/protos/tccGraphics'; + +interface TccButtonTextures { + redBtn: Texture; + greenBtn: Texture; + redBtnPress: Texture; + greenBtnPress: Texture; +} + +export interface ITccButtonData extends GraphicData { + get code(): string; + set code(v: string); + get buttonColor(): tccGraphicData.TccElementColor; + set buttonColor(v: tccGraphicData.TccElementColor); + get isSelfReset(): boolean; + set isSelfReset(v: boolean); +} + +export interface ITccButtonState extends GraphicState { + get code(): string; + set code(v: string); + get down(): boolean; + set down(v: boolean); +} + +export class TccButton extends JlGraphic { + static Type = 'TccButton'; + _tccButton: Sprite; + tccButtonTextures: TccButtonTextures; + __state = 0; + + constructor(tccButtonTextures: TccButtonTextures) { + super(TccButton.Type); + this.tccButtonTextures = tccButtonTextures; + this._tccButton = new Sprite(); + this._tccButton.texture = this.tccButtonTextures.redBtn; + this._tccButton.anchor.set(0.5); + this.addChild(this._tccButton); + } + get code(): string { + return this.datas.code; + } + get datas(): ITccButtonData { + return this.getDatas(); + } + get states(): ITccButtonState { + return this.getStates(); + } + doRepaint(): void { + this._tccButton.rotation = 0; + if (this.datas.buttonColor === tccGraphicData.TccElementColor.green) { + if (this.states.down) { + this._tccButton.texture = this.tccButtonTextures.greenBtnPress; + } else { + this._tccButton.texture = this.tccButtonTextures.greenBtn; + } + } else { + if (this.states.down) { + this._tccButton.texture = this.tccButtonTextures.redBtnPress; + } else { + this._tccButton.texture = this.tccButtonTextures.redBtn; + } + } + } +} + +export class TccButtonTemplate extends JlGraphicTemplate { + tccButtonTextures?: TccButtonTextures; + constructor(dataTemplate: ITccButtonData, stateTemplate: ITccButtonState) { + super(TccButton.Type, { dataTemplate, stateTemplate }); + this.loadAssets(); + } + new(): TccButton { + if (this.tccButtonTextures) { + const g = new TccButton(this.tccButtonTextures); + g.loadData(this.datas); + g.loadState(this.states); + return g; + } + throw new Error('资源未加载/加载失败'); + } + async loadAssets(): Promise { + const texture = await Assets.load(Tcc_Button_Assets); + const tccButtonSheet = new Spritesheet(texture, Tcc_Button_JSON); + const result = await tccButtonSheet.parse(); + this.tccButtonTextures = { + redBtn: result['red-btn.png'], + greenBtn: result['green-btn.png'], + redBtnPress: result['red-btn-press.png'], + greenBtnPress: result['green-btn-press.png'], + }; + return this.tccButtonTextures as TccButtonTextures; + } +} diff --git a/src/graphics/tccButton/TccButtonDrawAssistant.ts b/src/graphics/tccButton/TccButtonDrawAssistant.ts new file mode 100644 index 0000000..a2d26af --- /dev/null +++ b/src/graphics/tccButton/TccButtonDrawAssistant.ts @@ -0,0 +1,122 @@ +import { DisplayObject, FederatedMouseEvent, Point } from 'pixi.js'; +import { + AbsorbableLine, + AbsorbablePosition, + GraphicDrawAssistant, + GraphicInteractionPlugin, + GraphicTransformEvent, + IDrawApp, + JlGraphic, +} from 'jl-graphic'; +import { ITccButtonData, TccButton, TccButtonTemplate } from './TccButton'; + +export class TccButtonDraw extends GraphicDrawAssistant< + TccButtonTemplate, + ITccButtonData +> { + _tccButton: TccButton | null = null; + constructor(app: IDrawApp, template: TccButtonTemplate) { + super( + app, + template, + 'svguse:../drawIcon.svg#icon-psl-button', + '紧急制动按钮' + ); + TccButtonInteraction.init(app); + } + + bind(): void { + super.bind(); + if (!this._tccButton) { + this._tccButton = this.graphicTemplate.new(); + this.container.addChild(this._tccButton); + } + } + + public get tccButton(): TccButton { + if (!this._tccButton) { + this._tccButton = this.graphicTemplate.new(); + this.container.addChild(this._tccButton); + } + return this._tccButton; + } + + redraw(cp: Point): void { + this.tccButton.position.copyFrom(cp); + } + onLeftUp(e: FederatedMouseEvent): void { + this.tccButton.position.copyFrom(this.toCanvasCoordinates(e.global)); + this.createAndStore(true); + } + prepareData(data: ITccButtonData): boolean { + data.transform = this.tccButton.saveTransform(); + return true; + } + onEsc(): void { + this.finish(); + } +} + +/** + * 构建吸附线 + * @param tccButton + */ +function buildAbsorbablePositions(tccButton: TccButton): AbsorbablePosition[] { + const aps: AbsorbablePosition[] = []; + const tccButtons = tccButton.queryStore.queryByType( + TccButton.Type + ); + const canvas = tccButton.getCanvas(); + tccButtons.forEach((item) => { + if (item.id === tccButton.id) { + return; + } + const ala = new AbsorbableLine( + new Point(item.x, 0), + new Point(item.x, canvas.height) + ); + const alb = new AbsorbableLine( + new Point(0, item.y), + new Point(canvas.width, item.y) + ); + aps.push(ala); + aps.push(alb); + }); + + return aps; +} + +export class TccButtonInteraction extends GraphicInteractionPlugin { + static Name = 'tcc_light_transform'; + constructor(app: IDrawApp) { + super(TccButtonInteraction.Name, app); + } + static init(app: IDrawApp) { + return new TccButtonInteraction(app); + } + filter(...grahpics: JlGraphic[]): TccButton[] | undefined { + return grahpics + .filter((g) => g.type === TccButton.Type) + .map((g) => g as TccButton); + } + bind(g: TccButton): void { + g.eventMode = 'static'; + g.cursor = 'pointer'; + g.scalable = true; + g.rotatable = true; + g.on('transformstart', this.transformstart, this); + } + unbind(g: TccButton): void { + g.eventMode = 'none'; + g.scalable = false; + g.rotatable = false; + g.off('transformstart', this.transformstart, this); + } + transformstart(e: GraphicTransformEvent) { + const target = e.target as DisplayObject; + const tccButton = target.getGraphic() as TccButton; + tccButton.getGraphicApp().setOptions({ + absorbablePositions: buildAbsorbablePositions(tccButton), + }); + } +} diff --git a/src/graphics/tccButton/tcc-button-data.json b/src/graphics/tccButton/tcc-button-data.json new file mode 100644 index 0000000..e79e247 --- /dev/null +++ b/src/graphics/tccButton/tcc-button-data.json @@ -0,0 +1,45 @@ +{ + "frames": { + "green-btn.png": { + "frame": { "x": 0, "y": 0, "w": 64, "h": 64 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 }, + "sourceSize": { "w": 64, "h": 64 }, + "anchor": { "x": 0.5, "y": 0.5 } + }, + "red-btn.png": { + "frame": { "x": 64, "y": 0, "w": 64, "h": 64 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 }, + "sourceSize": { "w": 64, "h": 64 }, + "anchor": { "x": 0.5, "y": 0.5 } + }, + "green-btn-press.png": { + "frame": { "x": 128, "y": 0, "w": 59, "h": 64 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 59, "h": 64 }, + "sourceSize": { "w": 59, "h": 64 }, + "anchor": { "x": 0.5, "y": 0.5 } + }, + "red-btn-press.png": { + "frame": { "x": 187, "y": 0, "w": 59, "h": 64 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 59, "h": 64 }, + "sourceSize": { "w": 59, "h": 64 }, + "anchor": { "x": 0.5, "y": 0.5 } + } + }, + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "1.1", + "image": "psl-button.png", + "format": "RGBA8888", + "size": { "w": 246, "h": 64 }, + "scale": "1", + "smartupdate": "$TexturePacker:SmartUpdate:e7620bd2d73cc0b3e2deea9704e7eefc:f129a1d9e4b9ba57720b3861c22b155b:eb2d421f7759984b7713aa4aa5354134$" + } +} diff --git a/src/graphics/tccButton/tcc-button-spritesheet.png b/src/graphics/tccButton/tcc-button-spritesheet.png new file mode 100644 index 0000000..b8fe1a9 Binary files /dev/null and b/src/graphics/tccButton/tcc-button-spritesheet.png differ diff --git a/src/layouts/TrainControlCabDrawLayout.vue b/src/layouts/TrainControlCabDrawLayout.vue new file mode 100644 index 0000000..c74cdd7 --- /dev/null +++ b/src/layouts/TrainControlCabDrawLayout.vue @@ -0,0 +1,295 @@ + + + diff --git a/src/pages/DraftManage.vue b/src/pages/DraftManage.vue index 9615923..45a12ce 100644 --- a/src/pages/DraftManage.vue +++ b/src/pages/DraftManage.vue @@ -386,6 +386,7 @@ const pictureTypeList: { label: string; value: PictureType }[] = [ { label: 'PSL', value: PictureType.Psl }, { label: '继电器柜布置图', value: PictureType.RelayCabinetLayout }, { label: 'IBP盘', value: PictureType.IBP }, + { label: '列车驾驶台', value: PictureType.TrainControlCab }, ]; const categoryId = ref(''); const pictureType = ref(PictureType.StationLayout); @@ -416,6 +417,8 @@ function goToPath(row: DraftItem) { path = `/pslPainting/${row.id}`; } else if (row.type === PictureType.IBP) { path = `/ibpPainting/${row.id}`; + } else if (row.type === PictureType.TrainControlCab) { + path = `/trainControlCabPainting/${row.id}`; } router.push({ path: path }); } diff --git a/src/protos/picture.ts b/src/protos/picture.ts index 5edceca..cc5a139 100644 --- a/src/protos/picture.ts +++ b/src/protos/picture.ts @@ -9,5 +9,6 @@ export enum PictureType { Psl = 1, RelayCabinetLayout = 2, IBP = 3, - TrainData = 4 + TrainData = 4, + TrainControlCab = 5 } diff --git a/src/protos/tccGraphics.ts b/src/protos/tccGraphics.ts new file mode 100644 index 0000000..ba656d6 --- /dev/null +++ b/src/protos/tccGraphics.ts @@ -0,0 +1,430 @@ +/** + * Generated by the protoc-gen-ts. DO NOT EDIT! + * compiler version: 4.23.1 + * source: tccGraphics.proto + * git: https://github.com/thesayyn/protoc-gen-ts */ +import * as dependency_1 from "./stationLayoutGraphics"; +import * as pb_1 from "google-protobuf"; +export namespace tccGraphicData { + export enum TccElementColor { + green = 0, + red = 1 + } + export class TccGraphicStorage extends pb_1.Message { + #one_of_decls: number[][] = []; + constructor(data?: any[] | { + canvas?: dependency_1.graphicData.Canvas; + tccButtons?: TccButton[]; + tccTexts?: TccText[]; + }) { + super(); + pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [2, 3], this.#one_of_decls); + if (!Array.isArray(data) && typeof data == "object") { + if ("canvas" in data && data.canvas != undefined) { + this.canvas = data.canvas; + } + if ("tccButtons" in data && data.tccButtons != undefined) { + this.tccButtons = data.tccButtons; + } + if ("tccTexts" in data && data.tccTexts != undefined) { + this.tccTexts = data.tccTexts; + } + } + } + get canvas() { + return pb_1.Message.getWrapperField(this, dependency_1.graphicData.Canvas, 1) as dependency_1.graphicData.Canvas; + } + set canvas(value: dependency_1.graphicData.Canvas) { + pb_1.Message.setWrapperField(this, 1, value); + } + get has_canvas() { + return pb_1.Message.getField(this, 1) != null; + } + get tccButtons() { + return pb_1.Message.getRepeatedWrapperField(this, TccButton, 2) as TccButton[]; + } + set tccButtons(value: TccButton[]) { + pb_1.Message.setRepeatedWrapperField(this, 2, value); + } + get tccTexts() { + return pb_1.Message.getRepeatedWrapperField(this, TccText, 3) as TccText[]; + } + set tccTexts(value: TccText[]) { + pb_1.Message.setRepeatedWrapperField(this, 3, value); + } + static fromObject(data: { + canvas?: ReturnType; + tccButtons?: ReturnType[]; + tccTexts?: ReturnType[]; + }): TccGraphicStorage { + const message = new TccGraphicStorage({}); + if (data.canvas != null) { + message.canvas = dependency_1.graphicData.Canvas.fromObject(data.canvas); + } + if (data.tccButtons != null) { + message.tccButtons = data.tccButtons.map(item => TccButton.fromObject(item)); + } + if (data.tccTexts != null) { + message.tccTexts = data.tccTexts.map(item => TccText.fromObject(item)); + } + return message; + } + toObject() { + const data: { + canvas?: ReturnType; + tccButtons?: ReturnType[]; + tccTexts?: ReturnType[]; + } = {}; + if (this.canvas != null) { + data.canvas = this.canvas.toObject(); + } + if (this.tccButtons != null) { + data.tccButtons = this.tccButtons.map((item: TccButton) => item.toObject()); + } + if (this.tccTexts != null) { + data.tccTexts = this.tccTexts.map((item: TccText) => item.toObject()); + } + 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_canvas) + writer.writeMessage(1, this.canvas, () => this.canvas.serialize(writer)); + if (this.tccButtons.length) + writer.writeRepeatedMessage(2, this.tccButtons, (item: TccButton) => item.serialize(writer)); + if (this.tccTexts.length) + writer.writeRepeatedMessage(3, this.tccTexts, (item: TccText) => item.serialize(writer)); + if (!w) + return writer.getResultBuffer(); + } + static deserialize(bytes: Uint8Array | pb_1.BinaryReader): TccGraphicStorage { + const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new TccGraphicStorage(); + while (reader.nextField()) { + if (reader.isEndGroup()) + break; + switch (reader.getFieldNumber()) { + case 1: + reader.readMessage(message.canvas, () => message.canvas = dependency_1.graphicData.Canvas.deserialize(reader)); + break; + case 2: + reader.readMessage(message.tccButtons, () => pb_1.Message.addToRepeatedWrapperField(message, 2, TccButton.deserialize(reader), TccButton)); + break; + case 3: + reader.readMessage(message.tccTexts, () => pb_1.Message.addToRepeatedWrapperField(message, 3, TccText.deserialize(reader), TccText)); + break; + default: reader.skipField(); + } + } + return message; + } + serializeBinary(): Uint8Array { + return this.serialize(); + } + static deserializeBinary(bytes: Uint8Array): TccGraphicStorage { + return TccGraphicStorage.deserialize(bytes); + } + } + export class TccButton extends pb_1.Message { + #one_of_decls: number[][] = []; + constructor(data?: any[] | { + common?: dependency_1.graphicData.CommonInfo; + code?: string; + buttonColor?: TccElementColor; + isSelfReset?: boolean; + }) { + 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 ("code" in data && data.code != undefined) { + this.code = data.code; + } + if ("buttonColor" in data && data.buttonColor != undefined) { + this.buttonColor = data.buttonColor; + } + if ("isSelfReset" in data && data.isSelfReset != undefined) { + this.isSelfReset = data.isSelfReset; + } + } + } + get common() { + return pb_1.Message.getWrapperField(this, dependency_1.graphicData.CommonInfo, 1) as dependency_1.graphicData.CommonInfo; + } + set common(value: dependency_1.graphicData.CommonInfo) { + pb_1.Message.setWrapperField(this, 1, value); + } + get has_common() { + return pb_1.Message.getField(this, 1) != null; + } + get code() { + return pb_1.Message.getFieldWithDefault(this, 2, "") as string; + } + set code(value: string) { + pb_1.Message.setField(this, 2, value); + } + get buttonColor() { + return pb_1.Message.getFieldWithDefault(this, 3, TccElementColor.green) as TccElementColor; + } + set buttonColor(value: TccElementColor) { + pb_1.Message.setField(this, 3, value); + } + get isSelfReset() { + return pb_1.Message.getFieldWithDefault(this, 4, false) as boolean; + } + set isSelfReset(value: boolean) { + pb_1.Message.setField(this, 4, value); + } + static fromObject(data: { + common?: ReturnType; + code?: string; + buttonColor?: TccElementColor; + isSelfReset?: boolean; + }): TccButton { + const message = new TccButton({}); + if (data.common != null) { + message.common = dependency_1.graphicData.CommonInfo.fromObject(data.common); + } + if (data.code != null) { + message.code = data.code; + } + if (data.buttonColor != null) { + message.buttonColor = data.buttonColor; + } + if (data.isSelfReset != null) { + message.isSelfReset = data.isSelfReset; + } + return message; + } + toObject() { + const data: { + common?: ReturnType; + code?: string; + buttonColor?: TccElementColor; + isSelfReset?: boolean; + } = {}; + if (this.common != null) { + data.common = this.common.toObject(); + } + if (this.code != null) { + data.code = this.code; + } + if (this.buttonColor != null) { + data.buttonColor = this.buttonColor; + } + if (this.isSelfReset != null) { + data.isSelfReset = this.isSelfReset; + } + 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.code.length) + writer.writeString(2, this.code); + if (this.buttonColor != TccElementColor.green) + writer.writeEnum(3, this.buttonColor); + if (this.isSelfReset != false) + writer.writeBool(4, this.isSelfReset); + if (!w) + return writer.getResultBuffer(); + } + static deserialize(bytes: Uint8Array | pb_1.BinaryReader): TccButton { + const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new TccButton(); + while (reader.nextField()) { + if (reader.isEndGroup()) + break; + switch (reader.getFieldNumber()) { + case 1: + reader.readMessage(message.common, () => message.common = dependency_1.graphicData.CommonInfo.deserialize(reader)); + break; + case 2: + message.code = reader.readString(); + break; + case 3: + message.buttonColor = reader.readEnum(); + break; + case 4: + message.isSelfReset = reader.readBool(); + break; + default: reader.skipField(); + } + } + return message; + } + serializeBinary(): Uint8Array { + return this.serialize(); + } + static deserializeBinary(bytes: Uint8Array): TccButton { + return TccButton.deserialize(bytes); + } + } + export class TccText extends pb_1.Message { + #one_of_decls: number[][] = []; + constructor(data?: any[] | { + common?: dependency_1.graphicData.CommonInfo; + code?: string; + content?: string; + color?: string; + fontSize?: 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 ("code" in data && data.code != undefined) { + this.code = data.code; + } + if ("content" in data && data.content != undefined) { + this.content = data.content; + } + if ("color" in data && data.color != undefined) { + this.color = data.color; + } + if ("fontSize" in data && data.fontSize != undefined) { + this.fontSize = data.fontSize; + } + } + } + get common() { + return pb_1.Message.getWrapperField(this, dependency_1.graphicData.CommonInfo, 1) as dependency_1.graphicData.CommonInfo; + } + set common(value: dependency_1.graphicData.CommonInfo) { + pb_1.Message.setWrapperField(this, 1, value); + } + get has_common() { + return pb_1.Message.getField(this, 1) != null; + } + get code() { + return pb_1.Message.getFieldWithDefault(this, 2, "") as string; + } + set code(value: string) { + pb_1.Message.setField(this, 2, value); + } + get content() { + return pb_1.Message.getFieldWithDefault(this, 3, "") as string; + } + set content(value: string) { + pb_1.Message.setField(this, 3, value); + } + get color() { + return pb_1.Message.getFieldWithDefault(this, 4, "") as string; + } + set color(value: string) { + pb_1.Message.setField(this, 4, value); + } + get fontSize() { + return pb_1.Message.getFieldWithDefault(this, 5, 0) as number; + } + set fontSize(value: number) { + pb_1.Message.setField(this, 5, value); + } + static fromObject(data: { + common?: ReturnType; + code?: string; + content?: string; + color?: string; + fontSize?: number; + }): TccText { + const message = new TccText({}); + if (data.common != null) { + message.common = dependency_1.graphicData.CommonInfo.fromObject(data.common); + } + if (data.code != null) { + message.code = data.code; + } + if (data.content != null) { + message.content = data.content; + } + if (data.color != null) { + message.color = data.color; + } + if (data.fontSize != null) { + message.fontSize = data.fontSize; + } + return message; + } + toObject() { + const data: { + common?: ReturnType; + code?: string; + content?: string; + color?: string; + fontSize?: number; + } = {}; + if (this.common != null) { + data.common = this.common.toObject(); + } + if (this.code != null) { + data.code = this.code; + } + if (this.content != null) { + data.content = this.content; + } + if (this.color != null) { + data.color = this.color; + } + if (this.fontSize != null) { + data.fontSize = this.fontSize; + } + 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.code.length) + writer.writeString(2, this.code); + if (this.content.length) + writer.writeString(3, this.content); + if (this.color.length) + writer.writeString(4, this.color); + if (this.fontSize != 0) + writer.writeInt32(5, this.fontSize); + if (!w) + return writer.getResultBuffer(); + } + static deserialize(bytes: Uint8Array | pb_1.BinaryReader): TccText { + const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new TccText(); + while (reader.nextField()) { + if (reader.isEndGroup()) + break; + switch (reader.getFieldNumber()) { + case 1: + reader.readMessage(message.common, () => message.common = dependency_1.graphicData.CommonInfo.deserialize(reader)); + break; + case 2: + message.code = reader.readString(); + break; + case 3: + message.content = reader.readString(); + break; + case 4: + message.color = reader.readString(); + break; + case 5: + message.fontSize = reader.readInt32(); + break; + default: reader.skipField(); + } + } + return message; + } + serializeBinary(): Uint8Array { + return this.serialize(); + } + static deserializeBinary(bytes: Uint8Array): TccText { + return TccText.deserialize(bytes); + } + } +} diff --git a/src/router/routes.ts b/src/router/routes.ts index 95b0db7..0e80000 100644 --- a/src/router/routes.ts +++ b/src/router/routes.ts @@ -189,6 +189,14 @@ export const asyncRoutes: RouteRecordRaw[] = [ hidden: true, }, }, + { + path: '/trainControlCabPainting/:id', + name: 'trainControlCabPainting', + component: () => import('src/layouts/TrainControlCabDrawLayout.vue'), + meta: { + hidden: true, + }, + }, { path: '/linemap', name: 'linemap', diff --git a/src/stores/tcc-draw-store.ts b/src/stores/tcc-draw-store.ts new file mode 100644 index 0000000..a403b64 --- /dev/null +++ b/src/stores/tcc-draw-store.ts @@ -0,0 +1,91 @@ +import { defineStore } from 'pinia'; +import { + destroyTccDrawApp, + getTccDrawApp, + initTccDrawApp, +} from 'src/drawApp/tccApp'; +import { DrawAssistant, IJlCanvas, IDrawApp, JlGraphic } from 'jl-graphic'; + +export const useTccDrawStore = defineStore('tccDraw', { + state: () => ({ + drawAssistant: null as DrawAssistant | null, + selectedGraphics: null as JlGraphic[] | null, + draftId: null as number | null, + draftType: 'Tcc', + oneClickType: '' + }), + getters: { + drawMode: (state) => state.drawAssistant != null, + drawGraphicType: (state) => state.drawAssistant?.type, + drawGraphicName: (state) => state.drawAssistant?.description, + drawGraphicTemplate: (state) => state.drawAssistant?.graphicTemplate, + + selectedGraphicType: (state) => { + if (state.selectedGraphics) { + if (state.selectedGraphics.length === 1) { + return state.selectedGraphics[0].type; + } + } + }, + selectedObjName: (state) => { + if (state.selectedGraphics) { + if (state.selectedGraphics.length == 0) { + return '画布'; + } else if (state.selectedGraphics.length == 1) { + return state.selectedGraphics[0].type; + } + return '多选'; + } + return ''; + }, + selectedGraphic: (state) => { + if (state.selectedGraphics) { + if (state.selectedGraphics.length === 1) { + return state.selectedGraphics[0]; + } + } + return null; + }, + }, + actions: { + getDrawApp(): IDrawApp { + const app = getTccDrawApp(); + if (app == null) { + throw new Error('未初始化app'); + } + return app; + }, + getJlCanvas(): IJlCanvas { + return this.getDrawApp().canvas; + }, + initDrawApp() { + const app = initTccDrawApp(); + app.on('interaction-plugin-resume', (plugin) => { + if (plugin.isAppPlugin()) { + if (Object.hasOwn(plugin, '__GraphicDrawAssistant')) { + this.drawAssistant = plugin as DrawAssistant; + } else { + this.drawAssistant = null; + } + } + }); + app.on('graphicselectedchange', () => { + this.selectedGraphics = app.selectedGraphics; + }); + this.selectedGraphics = []; + return app; + }, + destroy() { + // console.log('绘制状态清空,绘制应用销毁'); + this.drawAssistant = null; + this.selectedGraphics = null; + destroyTccDrawApp(); + }, + setDraftId(id: number | null) { + this.draftId = id; + }, + setDraftType(type: string) { + this.draftType = type; + }, + }, +}); diff --git a/src/stores/tcc-store.ts b/src/stores/tcc-store.ts new file mode 100644 index 0000000..7da76d2 --- /dev/null +++ b/src/stores/tcc-store.ts @@ -0,0 +1,33 @@ +import { defineStore } from 'pinia'; +import { initTccScene } from 'src/drawApp/tccScene'; +import { getLineApp } from 'src/drawApp/lineApp'; + +export const useTccStore = defineStore('tcc', { + state: () => ({ + tccMapCode: '', + tccId: 0, + isTccDialogOpen: false, + }), + actions: { + getTccScene() { + const lineApp = getLineApp(); + if (!lineApp) throw Error('未初始化app'); + try { + return lineApp.getScene('tcc'); + } catch (error) { + const tccScene = initTccScene(lineApp, 'tcc'); + return tccScene; + } + }, + setTccParam(tccId: number, tccMapCode: string) { + this.tccId = tccId; + this.tccMapCode = tccMapCode; + this.isTccDialogOpen = true; + }, + clearTccParam() { + this.tccId = 0; + this.tccMapCode = ''; + this.isTccDialogOpen = false; + }, + }, +});