psl流程调整
This commit is contained in:
parent
02324f62d6
commit
5f622454ff
@ -135,3 +135,16 @@ export async function setRelayState(data: {
|
||||
}) {
|
||||
return await api.post(`${UriBase}/relay/operation`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* PSL操作
|
||||
*/
|
||||
export async function pslOperate(data: {
|
||||
simulationId: string;
|
||||
mapId: number;
|
||||
gateBoxId: string;
|
||||
buttonCode: string;
|
||||
down: boolean;
|
||||
}) {
|
||||
return await api.post(`${UriBase}/psl/operation`, data);
|
||||
}
|
||||
|
@ -11,8 +11,8 @@ import { ContextMenu } from 'src/jl-graphic/ui/ContextMenu';
|
||||
import { MenuItemOptions } from 'src/jl-graphic/ui/Menu';
|
||||
|
||||
import { graphicData } from 'src/protos/stationLayoutGraphics';
|
||||
import { useLineStore } from 'src/stores/line-store';
|
||||
import { GraphicDataBase } from './GraphicDataBase';
|
||||
import { usePslStore } from 'src/stores/psl-store';
|
||||
|
||||
export class GatedBoxData extends GraphicDataBase implements IGatedBox {
|
||||
constructor(data?: graphicData.GatedBox) {
|
||||
@ -141,8 +141,12 @@ export class GatedBoxOperateInteraction extends GraphicInteractionPlugin<GatedBo
|
||||
g.eventMode = 'none';
|
||||
g.off('_leftclick', this.onLeftClick, this);
|
||||
}
|
||||
onLeftClick() {
|
||||
// useLineStore().stateProCountIncrease();
|
||||
useLineStore().increaseGatedBoxCount();
|
||||
onLeftClick(e: FederatedMouseEvent) {
|
||||
const target = e.target as DisplayObject;
|
||||
const gatedBox = target.getGraphic() as GatedBox;
|
||||
usePslStore().setPslParam(
|
||||
gatedBox.datas.id,
|
||||
gatedBox.datas.refGatedBoxMapCode
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,22 @@
|
||||
import * as pb_1 from 'google-protobuf';
|
||||
import { IPslButtonData, PslButton } from 'src/graphics/pslButton/pslButton';
|
||||
import {
|
||||
IPslButtonData,
|
||||
IPslButtonState,
|
||||
PslButton,
|
||||
} from 'src/graphics/pslButton/pslButton';
|
||||
import {
|
||||
GraphicInteractionPlugin,
|
||||
IGraphicScene,
|
||||
JlGraphic,
|
||||
} from 'src/jl-graphic';
|
||||
import { pslGraphicData } from 'src/protos/pslGraphics';
|
||||
import { GraphicDataBase } from './GraphicDataBase';
|
||||
import { GraphicDataBase, GraphicStateBase } from './GraphicDataBase';
|
||||
import { pslOperate } from 'src/api/Simulation';
|
||||
import { useLineStore } from 'src/stores/line-store';
|
||||
import { DisplayObject, FederatedMouseEvent } from 'pixi.js';
|
||||
import { errorNotify } from 'src/utils/CommonNotify';
|
||||
import { usePslStore } from 'src/stores/psl-store';
|
||||
import { state } from 'src/protos/device_state';
|
||||
|
||||
export class PslButtonData extends GraphicDataBase implements IPslButtonData {
|
||||
constructor(data?: pslGraphicData.PslButton) {
|
||||
@ -53,6 +63,43 @@ export class PslButtonData extends GraphicDataBase implements IPslButtonData {
|
||||
return pb_1.Message.equals(this.data, other.data);
|
||||
}
|
||||
}
|
||||
//GraphicStateBase
|
||||
export class PslButtonState
|
||||
extends GraphicStateBase
|
||||
implements IPslButtonState
|
||||
{
|
||||
constructor(proto?: state.ButtonState) {
|
||||
let states;
|
||||
if (proto) {
|
||||
states = proto;
|
||||
} else {
|
||||
states = new state.ButtonState();
|
||||
}
|
||||
super(states, PslButton.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<state.ButtonState>();
|
||||
}
|
||||
clone(): PslButtonState {
|
||||
return new PslButtonState(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 PslButtonOperateInteraction extends GraphicInteractionPlugin<PslButton> {
|
||||
static Name = 'psl_button_operate_menu';
|
||||
constructor(app: IGraphicScene) {
|
||||
@ -70,16 +117,53 @@ export class PslButtonOperateInteraction extends GraphicInteractionPlugin<PslBut
|
||||
g.eventMode = 'static';
|
||||
g.cursor = 'pointer';
|
||||
g.selectable = true;
|
||||
g.on('_leftclick', this.onLeftClick, this);
|
||||
g.on('mousedown', this.onMouseDown, this);
|
||||
g.on('mouseup', this.onMouseUp, this);
|
||||
}
|
||||
|
||||
unbind(g: PslButton): void {
|
||||
g.selectable = false;
|
||||
g.eventMode = 'none';
|
||||
g.off('_leftclick', this.onLeftClick, this);
|
||||
g.off('mousedown', this.onMouseDown, this);
|
||||
g.on('mouseup', this.onMouseUp, this);
|
||||
}
|
||||
onLeftClick() {
|
||||
// useLineStore().stateProCountIncrease();
|
||||
// useLineStore().increaseGatedBoxCount();
|
||||
//FederatedMouseEvent
|
||||
onMouseDown(e: FederatedMouseEvent) {
|
||||
const simulationId = useLineStore().simulationId;
|
||||
const mapId = useLineStore().mapId;
|
||||
const gateBoxId = usePslStore().gatedBoxId;
|
||||
const target = e.target as DisplayObject;
|
||||
const pslButton = target.getGraphic() as PslButton;
|
||||
if (!simulationId || !mapId) {
|
||||
return;
|
||||
}
|
||||
pslOperate({
|
||||
simulationId,
|
||||
mapId,
|
||||
buttonCode: pslButton.datas.code,
|
||||
gateBoxId: gateBoxId,
|
||||
down: true,
|
||||
}).catch((err) => {
|
||||
errorNotify('操作失败', { message: err.origin.response.data.title });
|
||||
});
|
||||
}
|
||||
onMouseUp(e: FederatedMouseEvent) {
|
||||
const simulationId = useLineStore().simulationId;
|
||||
const mapId = useLineStore().mapId;
|
||||
const gateBoxId = usePslStore().gatedBoxId;
|
||||
const target = e.target as DisplayObject;
|
||||
const pslButton = target.getGraphic() as PslButton;
|
||||
if (!simulationId || !mapId) {
|
||||
return;
|
||||
}
|
||||
pslOperate({
|
||||
simulationId,
|
||||
mapId,
|
||||
buttonCode: pslButton.datas.code,
|
||||
gateBoxId: gateBoxId,
|
||||
down: false,
|
||||
}).catch((err) => {
|
||||
errorNotify('操作失败', { message: err.origin.response.data.title });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import {
|
||||
IGraphicScene,
|
||||
IGraphicStorage,
|
||||
} from 'src/jl-graphic';
|
||||
import { getPublishMapInfoByLineId } from 'src/api/PublishApi';
|
||||
import { getPublishMapInfoByName } from 'src/api/PublishApi';
|
||||
import { usePslStore } from 'src/stores/psl-store';
|
||||
import { toUint8Array } from 'js-base64';
|
||||
import { PslKeyTemplate } from 'src/graphics/pslKey/pslKey';
|
||||
@ -14,6 +14,7 @@ import { PslButtonTemplate } from 'src/graphics/pslButton/pslButton';
|
||||
import {
|
||||
PslButtonData,
|
||||
PslButtonOperateInteraction,
|
||||
PslButtonState,
|
||||
} from './graphics/PslButtonInteraction';
|
||||
import { PslLightTemplate } from 'src/graphics/pslLight/pslLight';
|
||||
import { PslLightData } from './graphics/PslLightInteraction';
|
||||
@ -21,6 +22,7 @@ import { pslGraphicData } from 'src/protos/pslGraphics';
|
||||
import { PslTextData } from './graphics/TextContentInteraction';
|
||||
import { TextContentTemplate } from 'src/graphics/textContent/TextContent';
|
||||
import { useLineStore } from 'src/stores/line-store';
|
||||
import { state } from 'src/protos/device_state';
|
||||
|
||||
export function initPslScene(lineApp: IGraphicApp, sceneName: string) {
|
||||
// psl
|
||||
@ -28,8 +30,8 @@ export function initPslScene(lineApp: IGraphicApp, sceneName: string) {
|
||||
dataLoader: loadPslDatas,
|
||||
mouseToolOptions: {
|
||||
boxSelect: false,
|
||||
viewportDrag: true,
|
||||
wheelZoom: true,
|
||||
viewportDrag: false,
|
||||
wheelZoom: false,
|
||||
},
|
||||
});
|
||||
const graphicTemplate = [
|
||||
@ -43,28 +45,38 @@ export function initPslScene(lineApp: IGraphicApp, sceneName: string) {
|
||||
pslScene.on('postdataloaded', () => {
|
||||
handleSubscribe(pslScene);
|
||||
});
|
||||
pslScene.setOptions({
|
||||
mouseToolOptions: {
|
||||
boxSelect: false,
|
||||
viewportDrag: false,
|
||||
wheelZoom: false,
|
||||
},
|
||||
});
|
||||
return lineApp;
|
||||
// pslScene.setOptions({
|
||||
// mouseToolOptions: {
|
||||
// boxSelect: false,
|
||||
// viewportDrag: false,
|
||||
// wheelZoom: false,
|
||||
// },
|
||||
// });
|
||||
return pslScene;
|
||||
}
|
||||
|
||||
function handleSubscribe(pslScene: IGraphicScene) {
|
||||
const lineStore = useLineStore();
|
||||
const pslStore = usePslStore();
|
||||
const simulationId = lineStore.simulationId;
|
||||
const mapId = lineStore.mapId;
|
||||
const pslId = lineStore.pslId;
|
||||
const pslId = pslStore.gatedBoxId;
|
||||
const app = pslScene;
|
||||
app.subscribe({
|
||||
destination: `simulation-psl-${simulationId}_${mapId}_${pslId}-status`,
|
||||
messageConverter: (message: Uint8Array) => {
|
||||
// console.log('收到消息', message);
|
||||
console.log('收到消息', message);
|
||||
const states: GraphicState[] = [];
|
||||
// const storage = state.PushedDevicesStatus.deserialize(message);
|
||||
const storage = state.PushedDevicesStatus.deserialize(message);
|
||||
if (storage.all) {
|
||||
storage.allStatus.buttonState.forEach((item) => {
|
||||
// 道岔
|
||||
if (item.id) {
|
||||
states.push(new PslButtonState(item));
|
||||
}
|
||||
});
|
||||
}
|
||||
console.log(states, 'states');
|
||||
return states;
|
||||
},
|
||||
});
|
||||
@ -72,12 +84,10 @@ function handleSubscribe(pslScene: IGraphicScene) {
|
||||
|
||||
async function loadPslDatas(): Promise<IGraphicStorage> {
|
||||
const pslStore = usePslStore();
|
||||
const mapId = pslStore.mapId;
|
||||
const simulationId = pslStore.simulationId;
|
||||
if (!mapId || !simulationId) {
|
||||
throw new Error('获取数据异常:未获取到地图ID或仿真ID');
|
||||
}
|
||||
const { proto: base64 } = await getPublishMapInfoByLineId(mapId + '');
|
||||
const { proto: base64 } = await getPublishMapInfoByName({
|
||||
name: pslStore.pslMapCode,
|
||||
detail: true,
|
||||
});
|
||||
if (base64) {
|
||||
const storage = pslGraphicData.PslGraphicStorage.deserialize(
|
||||
toUint8Array(base64)
|
||||
|
@ -27,8 +27,10 @@ export interface IPslButtonData extends GraphicData {
|
||||
}
|
||||
|
||||
export interface IPslButtonState extends GraphicState {
|
||||
get state(): number;
|
||||
set state(v: number);
|
||||
get code(): string;
|
||||
set code(v: string);
|
||||
get down(): boolean;
|
||||
set down(v: boolean);
|
||||
}
|
||||
|
||||
export class PslButton extends JlGraphic {
|
||||
|
@ -37,11 +37,18 @@
|
||||
<q-page-container>
|
||||
<div id="line-app-container" class="overflow-hidden"></div>
|
||||
</q-page-container>
|
||||
<q-dialog v-model="pslDialog" @before-hide="pslHide">
|
||||
<q-page-container>
|
||||
<div id="psl-app-container" class="overflow-hidden"></div>
|
||||
</q-page-container>
|
||||
</q-dialog>
|
||||
<draggable-dialog
|
||||
v-model="pslStore.isPslDialogOpen"
|
||||
:width="pslCanvasWidth"
|
||||
:height="pslCanvasHeight"
|
||||
@hide="pslHide"
|
||||
>
|
||||
<div
|
||||
id="psl-app-container"
|
||||
class="overflow-hidden"
|
||||
style="width: 500px; height: 600px"
|
||||
></div>
|
||||
</draggable-dialog>
|
||||
<DraggableDialog
|
||||
:title="`${ibpStore.stationCode}IBP`"
|
||||
v-model="ibpStore.isIbpDialogOpen"
|
||||
@ -83,8 +90,6 @@ import { ISceneName, getSceneName } from 'src/drawApp/lineApp';
|
||||
import { useTestManageStore } from 'src/stores/testManage-store';
|
||||
import { CategoryType } from 'src/components/CategoryType';
|
||||
import TrainInfoEcharts from 'src/components/line-app/infos/TrainInfoEcharts.vue';
|
||||
import { GatedBox } from 'src/graphics/gatedBox/GatedBox';
|
||||
import { getPublishMapInfoByName } from 'src/api/PublishApi';
|
||||
import { useIbpStore } from 'src/stores/ibp-store';
|
||||
|
||||
const $q = useQuasar();
|
||||
@ -95,8 +100,8 @@ const route = useRoute();
|
||||
const router = useRouter();
|
||||
const lineStore = useLineStore();
|
||||
const ibpStore = useIbpStore();
|
||||
const pslStore = usePslStore();
|
||||
const testManageStore = useTestManageStore();
|
||||
const pslDialog = ref(false);
|
||||
const simulationId = (route.query.simulationId as string) || '';
|
||||
const projectId = (route.query.projectId as string) || '';
|
||||
const defaultMapId = (route.query.defaultMapId as string) || '';
|
||||
@ -186,9 +191,6 @@ onMounted(async () => {
|
||||
lineStore.setSimulationId(null);
|
||||
}
|
||||
drawerRight.value = false;
|
||||
// setTimeout(() => {
|
||||
// pslShow();
|
||||
// }, 5000);
|
||||
});
|
||||
onUnmounted(() => {
|
||||
if (dialogInstance.value && lineStore.showLayerDialog) {
|
||||
@ -215,16 +217,34 @@ watch(
|
||||
}
|
||||
);
|
||||
|
||||
const pslCanvasWidth = ref(500);
|
||||
const pslCanvasHeight = ref(600);
|
||||
watch(
|
||||
() => lineStore.gatedBoxCount,
|
||||
() => {
|
||||
if (lineStore.selectedGraphics) {
|
||||
const gatedBox = lineStore.selectedGraphics[0] as GatedBox;
|
||||
pslShow(gatedBox);
|
||||
() => pslStore.isPslDialogOpen,
|
||||
(val: boolean) => {
|
||||
if (val === true) {
|
||||
nextTick(async () => {
|
||||
const container = document.getElementById('psl-app-container');
|
||||
if (!container) return;
|
||||
const pslScene = pslStore.getPslScene();
|
||||
pslScene?.bindDom(container);
|
||||
await pslScene?.reload();
|
||||
if (pslScene) {
|
||||
pslCanvasWidth.value = pslScene.canvas.width;
|
||||
pslCanvasHeight.value = pslScene.canvas.height;
|
||||
container.style.width = pslCanvasWidth.value + 'px';
|
||||
container.style.height = pslCanvasHeight.value + 'px';
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
function pslHide() {
|
||||
pslStore.clearPslParam();
|
||||
lineApp.getScene('psl').destroy();
|
||||
}
|
||||
|
||||
const dialogInstance = ref();
|
||||
|
||||
watch(
|
||||
@ -301,46 +321,6 @@ function switchScene(val: MapInfo) {
|
||||
}
|
||||
}
|
||||
|
||||
function pslShow(gatedBox: GatedBox) {
|
||||
pslDialog.value = true;
|
||||
const pslApp = usePslStore().initLineApp();
|
||||
if (gatedBox.datas.refGatedBoxMapCode) {
|
||||
const params = {
|
||||
name: gatedBox.datas.refGatedBoxMapCode,
|
||||
detail: false,
|
||||
};
|
||||
lineStore.setPslId(gatedBox.datas.id);
|
||||
getPublishMapInfoByName(params).then(async (mapInfo) => {
|
||||
const dom = document.getElementById('psl-app-container');
|
||||
if (dom) {
|
||||
usePslStore().addAllScene([mapInfo]);
|
||||
usePslStore().setMapId(mapInfo.id);
|
||||
usePslStore().setSimulationId(simulationId);
|
||||
sceneName = getSceneNameFn(mapInfo);
|
||||
usePslStore().setSceneName(sceneName);
|
||||
scene = pslApp.getScene(sceneName);
|
||||
scene.bindDom(dom);
|
||||
await scene.reload();
|
||||
dom.style.width = scene.canvas.width + 'px';
|
||||
dom.style.height = scene.canvas.height + 'px';
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$q.notify({
|
||||
type: 'negative',
|
||||
message: '未获取到关联门控箱地图数据',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function pslHide() {
|
||||
const pslApp = usePslStore().initLineApp();
|
||||
lineStore.setPslId('');
|
||||
pslApp.unbindDom();
|
||||
pslApp.destroy();
|
||||
pslDialog.value = false;
|
||||
}
|
||||
|
||||
watch(
|
||||
() => ibpStore.isIbpDialogOpen,
|
||||
(val) => {
|
||||
|
@ -33,8 +33,6 @@ export const useLineStore = defineStore('line', {
|
||||
categoryType: null as CategoryType | null,
|
||||
echartsTrainId: '',
|
||||
trainStateMap: new Map() as Map<Date, TrainState[]>,
|
||||
gatedBoxCount: 0,
|
||||
pslId: '',
|
||||
}),
|
||||
getters: {
|
||||
selectedGraphicType: (state) => {
|
||||
@ -64,7 +62,6 @@ export const useLineStore = defineStore('line', {
|
||||
);
|
||||
});
|
||||
this.selectedGraphics = [];
|
||||
this.gatedBoxCount = 0;
|
||||
return app;
|
||||
},
|
||||
addAllScene(list: MapInfo[]) {
|
||||
@ -135,11 +132,5 @@ export const useLineStore = defineStore('line', {
|
||||
setCategoryType(type: CategoryType | null) {
|
||||
this.categoryType = type;
|
||||
},
|
||||
increaseGatedBoxCount() {
|
||||
this.gatedBoxCount++;
|
||||
},
|
||||
setPslId(id: string) {
|
||||
this.pslId = id;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -1,102 +1,29 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import {
|
||||
IJlCanvas,
|
||||
JlGraphic,
|
||||
IGraphicApp,
|
||||
GraphicState,
|
||||
} from 'src/jl-graphic';
|
||||
import {
|
||||
initLineApp,
|
||||
getLineApp,
|
||||
destroyLineApp,
|
||||
addSceneList,
|
||||
ISceneName,
|
||||
} from 'src/drawApp/lineApp';
|
||||
import { markRaw } from 'vue';
|
||||
import { MapInfo } from 'src/api/ProjectLinkApi';
|
||||
import { initPslScene } from 'src/drawApp/pslScene';
|
||||
import { getLineApp } from 'src/drawApp/lineApp';
|
||||
|
||||
export const usePslStore = defineStore('psl', {
|
||||
state: () => ({
|
||||
selectedGraphics: null as JlGraphic[] | null,
|
||||
lineId: null as number | null,
|
||||
lineName: null as string | null,
|
||||
simulationId: null as string | null,
|
||||
socketStates: null as GraphicState[] | null,
|
||||
stateProCount: 0,
|
||||
showLayer: [] as string[], // 显示的图层(草稿和发布图公用)
|
||||
showLayerDialog: false, // 显示图层控制弹窗(草稿和发布图公用)
|
||||
mapId: null as number | null,
|
||||
sceneName: '', // 场景名称
|
||||
pslMapCode: '',
|
||||
gatedBoxId: '',
|
||||
isPslDialogOpen: false,
|
||||
}),
|
||||
getters: {
|
||||
selectedGraphicType: (state) => {
|
||||
if (state.selectedGraphics) {
|
||||
if (state.selectedGraphics.length === 1) {
|
||||
return state.selectedGraphics[0].type;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
getLineApp(): IGraphicApp {
|
||||
const app = getLineApp();
|
||||
if (app == null) {
|
||||
throw new Error('未初始化app');
|
||||
}
|
||||
return app;
|
||||
getPslScene() {
|
||||
const lineApp = getLineApp();
|
||||
if (!lineApp) return;
|
||||
const pslScene = initPslScene(lineApp, 'psl');
|
||||
return pslScene;
|
||||
},
|
||||
getJlCanvas(): IJlCanvas {
|
||||
return this.getLineApp().canvas;
|
||||
setPslParam(gatedBoxId: string, pslMapCode: string) {
|
||||
this.gatedBoxId = gatedBoxId;
|
||||
this.pslMapCode = pslMapCode;
|
||||
this.isPslDialogOpen = true;
|
||||
},
|
||||
initLineApp() {
|
||||
const app = initLineApp();
|
||||
app.on('graphicselected', () => {
|
||||
this.selectedGraphics = markRaw(
|
||||
app.getScene(this.sceneName).selectedGraphics
|
||||
);
|
||||
});
|
||||
this.selectedGraphics = [];
|
||||
return app;
|
||||
},
|
||||
addAllScene(list: MapInfo[]) {
|
||||
const arr: ISceneName[] = list.map((item) => {
|
||||
return { type: item.type, id: item.id };
|
||||
});
|
||||
addSceneList(arr);
|
||||
},
|
||||
appCurrentScene() {
|
||||
return this.getLineApp().getScene(this.sceneName);
|
||||
},
|
||||
destroy() {
|
||||
this.selectedGraphics = null;
|
||||
destroyLineApp();
|
||||
},
|
||||
setLineId(id: number | null) {
|
||||
this.lineId = id;
|
||||
},
|
||||
setLineName(name: string | null) {
|
||||
this.lineName = name;
|
||||
},
|
||||
setSimulationId(id: string | null) {
|
||||
this.simulationId = id;
|
||||
},
|
||||
setSocketStates(v: GraphicState[] | null) {
|
||||
this.socketStates = v;
|
||||
},
|
||||
stateProCountIncrease() {
|
||||
this.stateProCount++;
|
||||
},
|
||||
setShowLayer(v: string[]) {
|
||||
this.showLayer = v;
|
||||
},
|
||||
setShowLayerDialog(v: boolean) {
|
||||
this.showLayerDialog = v;
|
||||
},
|
||||
setMapId(id: number | null) {
|
||||
this.mapId = id;
|
||||
},
|
||||
setSceneName(sceneName: string) {
|
||||
this.sceneName = sceneName;
|
||||
clearPslParam() {
|
||||
this.gatedBoxId = '';
|
||||
this.pslMapCode = '';
|
||||
this.isPslDialogOpen = false;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user