Compare commits

..

5 Commits
master ... fan

Author SHA1 Message Date
fan
7158f6a3bf Revert "订阅调整"
This reverts commit 0bbe65b042.
2023-07-31 11:19:30 +08:00
fan
c3ec658ac0 Revert "同步代码"
This reverts commit b189c385ae.
2023-07-31 11:19:24 +08:00
fan
6ddf2d8937 Revert "测试发布"
This reverts commit 4a36daf843.
2023-07-31 11:19:19 +08:00
fan
a444504225 Revert "测试1"
This reverts commit eafb1b8723.
2023-07-31 11:19:14 +08:00
fan
6242bc5ba6 Revert "恢复测试内容"
This reverts commit f09fe81a91.
2023-07-31 11:19:10 +08:00
207 changed files with 13087 additions and 13285 deletions

View File

@ -1,4 +0,0 @@
API=192.168.3.233:9081
HTTP=http://
NS=
WS=ws://

View File

@ -1,4 +0,0 @@
API=test.joylink.club
HTTP=https://
NS=/ncc
WS=wss://

View File

@ -1,4 +0,0 @@
API=localhost:9081
HTTP=http://
NS=
WS=ws://

View File

@ -1,4 +0,0 @@
API=10.255.6.60:9081
HTTP=http://
NS=
WS=ws://

4
.gitmodules vendored
View File

@ -1,3 +1,7 @@
[submodule "xian-ncc-da-message"]
path = xian-ncc-da-message
url = https://git.code.tencent.com/xian-ncc-da/xian-ncc-da-message.git
[submodule "graphic-pixi"]
path = graphic-pixi
url = https://git.code.tencent.com/jl-framework/graphic-pixi.git
branch = xian-ncc-da

View File

@ -1,19 +0,0 @@
const DotEnv = require('dotenv');
module.exports = function () {
let script = process.env.npm_lifecycle_script;
let name = 'dev';
if (script) {
// quasar启动没有这个
script = script.substring(script.indexOf('NODE_ENV=') + 'NODE_ENV='.length);
name = script.substring(0, script.indexOf('&'));
} else {
if (process.env.NODE_ENV == 'production') {
name = 'prod';
}
}
console.log(name);
name = '.env.' + name;
let parsedEnv = DotEnv.config({ path: name }).parsed;
return parsedEnv;
};

@ -1 +1 @@
Subproject commit b5ca64750380db0ff8e4e4a56a471363db684e12
Subproject commit 7fe73a8334f36cf917255a99d75c454abcb96d79

View File

@ -3,43 +3,19 @@
<head>
<title><%= productName %></title>
<meta charset="utf-8" />
<meta name="description" content="<%= productDescription %>" />
<meta name="format-detection" content="telephone=no" />
<meta name="msapplication-tap-highlight" content="no" />
<meta
name="viewport"
content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width<% if (ctx.mode.cordova || ctx.mode.capacitor) { %>, viewport-fit=cover<% } %>"
/>
<meta charset="utf-8">
<meta name="description" content="<%= productDescription %>">
<meta name="format-detection" content="telephone=no">
<meta name="msapplication-tap-highlight" content="no">
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width<% if (ctx.mode.cordova || ctx.mode.capacitor) { %>, viewport-fit=cover<% } %>">
<link
rel="icon"
type="image/png"
sizes="128x128"
href="logo/xian_favicon.png"
/>
<link
rel="icon"
type="image/png"
sizes="96x96"
href="logo/xian_favicon.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="logo/xian_favicon.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="logo/xian_favicon.png"
/>
<!-- <link rel="icon" type="image/ico" href="favicon.ico" /> -->
<link rel="icon" type="image/png" href="logo/xian_favicon.png" />
<link rel="icon" type="image/png" sizes="128x128" href="icons/favicon-128x128.png">
<link rel="icon" type="image/png" sizes="96x96" href="icons/favicon-96x96.png">
<link rel="icon" type="image/png" sizes="32x32" href="icons/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="icons/favicon-16x16.png">
<link rel="icon" type="image/ico" href="favicon.ico">
</head>
<body style="overflow: hidden">
<body>
<!-- quasar:entry-point -->
</body>
</html>

View File

@ -9,25 +9,23 @@
"lint": "eslint --ext .js,.ts,.vue ./",
"format": "prettier --write \"**/*.{js,ts,vue,scss,html,md,json}\" --ignore-path .gitignore",
"test": "echo \"No test specified\" && exit 0",
"dev": "set NODE_ENV=dev&&quasar dev",
"local": "set NODE_ENV=local&&quasar dev",
"build": "set NODE_ENV=dev&&quasar build",
"prod": "set NODE_ENV=prod&&quasar build",
"dev-show": "set NODE_ENV=dev-show&&quasar build",
"dev": "quasar dev",
"build": "quasar build",
"protoc": "node scripts/proto.cjs",
"sync": "node scripts/sync.cjs"
},
"dependencies": {
"@pixi/filter-outline": "^5.2.0",
"@pixi/graphics-extras": "^7.2.4",
"@quasar/extras": "^1.0.0",
"@stomp/stompjs": "^7.0.0",
"axios": "^1.2.1",
"centrifuge": "^4.0.1",
"dotenv": "^16.3.1",
"google-protobuf": "^3.21.2",
"jl-graphic": "git+https://git.code.tencent.com/jl-framework/graphic-pixi.git#v0.1.3",
"js-base64": "^3.7.5",
"pinia": "^2.0.11",
"pixi-viewport": "^5.0.1",
"pixi.js": "^7.2.4",
"quasar": "^2.6.0",
"save-dev": "0.0.1-security",
"vue": "^3.0.0",
"vue-router": "^4.0.0"
},

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

View File

@ -53,23 +53,10 @@ module.exports = configure(function (/* ctx */) {
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#build
build: {
target: {
browser: ['es2020'],
browser: ['es2019', 'edge88', 'firefox78', 'chrome87', 'safari13.1'],
node: 'node16',
},
env: require('./EnvParse.js')(),
chainWebpack(chain) {
chain
.plugin('eslint-webpack-plugin')
.use(ESLintPlugin, [{ extensions: ['js', 'vue'] }]);
chain.plugin('define').use(require('webpack/lib/DefinePlugin'), [
{
'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV),
},
},
]);
},
vueRouterMode: 'history', // available values: 'hash', 'history'
vueRouterBase: BasePath,
// vueDevtools,
@ -84,16 +71,10 @@ module.exports = configure(function (/* ctx */) {
// ignorePublicFolder: true,
// minify: false,
// polyfillModulePreload: true,
distDir: 'dist/xianncc',
// distDir
// distDir: `dist/${BasePath}`,
alias: { 'protobufjs/light': 'protobufjs/dist/light/protobuf.min.js' },
// extendViteConf(viteConf) {
// viteConf.resolve.alias = Object.assign({}, viteConf.resolve.alias, {
// 'protobufjs/light': 'protobufjs/dist/light/protobuf.min.js',
// });
// },
// extendViteConf (viteConf) {},
// viteVuePluginOptions: {},
// vitePlugins: [

70
scripts/sync.cjs Normal file
View File

@ -0,0 +1,70 @@
/**
* 同步图形框架文件到 src/jl-graphic/
*/
const {
readdirSync,
existsSync,
copyFileSync,
mkdirSync,
rmSync,
} = require('fs');
const { resolve } = require('path');
const jlGraphicSrcPath = resolve(__dirname, '../graphic-pixi/src/jlgraphic');
const jlGraphicLibPath = resolve(__dirname, '../src/jl-graphic');
/**
* 检查并初始化当前项目引入的jl-graphic库
*/
function checkAndInitJlGraphicLib() {
const exist = existsSync(jlGraphicLibPath);
if (exist) {
console.log('jl-graphic文件夹已存在,清空');
readdirSync(jlGraphicLibPath, {
withFileTypes: true,
}).forEach((file) => {
if (file.isDirectory()) {
rmSync(resolve(jlGraphicLibPath, file.name), { recursive: true });
} else {
rmSync(resolve(jlGraphicLibPath, file.name));
}
});
} else {
console.log('jl-graphic文件夹不存在,创建');
// 文件夹不存在,创建
mkdirSync(jlGraphicLibPath);
}
}
function copyJlGraphicFiles() {
readdirSync(jlGraphicSrcPath, {
withFileTypes: true,
}).forEach((file) => {
recursiveCopyFiles(file);
});
}
function recursiveCopyFiles(file, path = []) {
if (file.isFile()) {
const fileSrcPath = resolve(jlGraphicSrcPath, ...path, file.name);
const fileDestPath = resolve(jlGraphicLibPath, ...path, file.name);
console.log(`copy file ${fileSrcPath} -> ${fileDestPath}`);
copyFileSync(fileSrcPath, fileDestPath);
} else if (file.isDirectory()) {
const srcDir = resolve(jlGraphicSrcPath, ...path, file.name);
const dirPath = resolve(jlGraphicLibPath, ...path, file.name);
mkdirSync(dirPath);
readdirSync(srcDir, {
withFileTypes: true,
}).forEach((subFile) => {
recursiveCopyFiles(subFile, [...path, file.name]);
});
}
}
function main() {
checkAndInitJlGraphicLib();
copyJlGraphicFiles();
}
main();

View File

@ -1,27 +0,0 @@
import { api } from 'src/boot/axios';
const BaseUrl = '/api/config/device';
export interface ParamsItem {
id?: number;
lineId: number;
configData: string;
}
/**
* /
* @param params
* @returns
*/
export function saveThreshold(data: ParamsItem) {
return api.post(`${BaseUrl}/save`, data);
}
/**
*
* @param lineId 线id
*/
export async function getBaseDataByLineId(lineId: number): Promise<ParamsItem> {
const response = await api.get(`${BaseUrl}/initData/${lineId}`);
return response.data;
}

View File

@ -1,56 +0,0 @@
import { api } from 'src/boot/axios';
import { PageDto, PageQueryDto } from './ApiCommon';
const BaseUrl = '/api/alert/tip/time';
export interface TimeConfigItem {
id?: number;
timeName: string;
startHour: string;
endHour: string;
timeType: string;
publicPeak?: string;
}
export enum TipTimeConfig {
= 'HOLIDAYS_MORN_PEAK',
= 'HOLIDAYS_EVENING_PEAK',
= 'MORN_PEAK',
= 'EVENING_PEARK',
= 'NORMAL_UNPEARK',
}
export enum ShowTipTimeConfig {
HOLIDAYS_MORN_PEAK = '假期早高峰',
HOLIDAYS_EVENING_PEAK = '假期晚高峰',
MORN_PEAK = '早高峰',
EVENING_PEARK = '晚高峰',
NORMAL_UNPEARK = '低峰',
}
/**
* /
* @param data
* @returns
*/
export function creatOrEditTimeConfig(data: TimeConfigItem) {
return api.post(`${BaseUrl}`, data);
}
class PageQueryParams extends PageQueryDto {
timeName?: string;
timeType?: string;
}
/**
*
* @param params
* @returns
*/
export async function alarmTipTimeConfigPageQuery(
params: PageQueryParams
): Promise<PageDto<TimeConfigItem>> {
const response = await api.post(`${BaseUrl}/page`, {
params: params,
});
return response.data;
}

View File

@ -1,68 +1,24 @@
import { api } from 'src/boot/axios';
import { PageDto, PageQueryDto } from './ApiCommon';
import { AlarmInfo } from './DecisionInfo';
const alertUriBase = '/api/alert/mock';
//故障测试
export function mockLocalDemoTestSet(
alertType: string,
data: {
lineId: number;
deviceInfos: {
deviceName: string;
deviceType: string;
status: string;
groupId?: string;
}[];
}
) {
return api.post(`${alertUriBase}/localDemoTest/${alertType}`, data);
}
//故障演示
export function mockAlertSet(data: {
lineId: number;
alertType: string;
deviceCodes: string[];
}) {
export function mockAlertSet(data: { lineId: number; alertType: string }) {
return api.post(`${alertUriBase}/set`, data);
}
/**
*
* @param id 线id
* @param alertType
*/
export async function getDeviceByAlarmType(
id: number,
alertType: string
): Promise<DeviceConfigItem[]> {
const response = await api.post(`${alertUriBase}/find/${id}/${alertType}`);
return response.data;
}
export interface DeviceConfigItem {
name: string;
code: string;
}
export class PagingQueryParams extends PageQueryDto {
alertType?: string;
lineId?: number;
beginDateTime?: string;
endDateTime?: string;
alertStatus?: number;
}
export interface Item {
interface Item {
id: number;
alertType: string;
timeType: string;
locationType: string;
drivingInfo: string;
submissionInfo: string;
alarmStatus: number;
}
/**
@ -73,70 +29,8 @@ export interface Item {
export async function alarmInfoListQuery(
params: PagingQueryParams
): Promise<PageDto<Item>> {
const response = await api.post('/api/alertRecord/page/detail', params);
return response.data;
}
/**
*
* @param id id
* @param tipType
* @param alertLocationId id
*/
export function recordConfirmAlarmInfoByTipType(
id: number,
tipType: string,
alertLocationId?: number
): Promise<AlarmInfo<Item>> {
return api.get(`/api/alertRecord/confirm/${id}/${tipType}`, {
params: { alertLocationId: alertLocationId },
const response = await api.get('/api/alertRecord/page/detail', {
params: params,
});
}
/**
* --
* @param recordId id
*/
export function recordFailAlarmInfoById(recordId: number) {
return api.post(`/api/alertRecord/fail/${recordId}`);
}
/**
* --
* @param recordId id
*/
export function recordManualAlarmInfoById(recordId: number[]) {
return api.post('/api/alertRecord/warn', recordId);
}
export interface IReportParams {
alertTypes?: string[];
beginDateTime?: string;
endDateTime?: string;
alertStatus?: number;
}
interface IReportRes {
alertType: string;
counter: number;
}
/**
*
* @param lineId 线id
*/
export async function recordAlarmReport(
lineId: number,
data: IReportParams
): Promise<IReportRes[]> {
const response = await api.post(`/api/alertRecord/report/${lineId}`, data);
return response.data;
}
/**
*
* @param linId 线id
*/
export function resetApi(linId: number): Promise<string> {
return api.get(`${alertUriBase}/reset/${linId}`);
}

View File

@ -25,7 +25,7 @@ export class OrderItemDto {
}
}
export interface PageDto<T> {
export interface PageDto<T = unknown> {
records: T[];
/**
*

View File

@ -1,65 +0,0 @@
import { api } from 'src/boot/axios';
import { PageDto, PageQueryDto } from './ApiCommon';
export interface IAreaConfigListItem {
id: number;
lineId: number;
areaName: string;
deviceType: string;
}
export async function getDeviceAreaList({
lineId,
deviceType,
current,
size,
areaName,
}: {
lineId?: string;
deviceType?: string;
areaName?: string;
} & PageQueryDto) {
const resp = await api.get<PageDto<IAreaConfigListItem>>(
`/api/config/device/area/page/${lineId}`,
{ params: { current, size, deviceType, areaName } }
);
if (resp.status === 200) {
return resp.data;
} else {
throw Error(resp.statusText);
}
}
export async function deleteDeviceArea(id: number) {
const resp = await api.delete(`/api/config/device/area/${id}`);
if (resp.status === 200) {
return resp;
} else {
throw Error(resp.statusText);
}
}
export interface IAreaConfigItem {
id?: number;
lineId: number;
areaName: string;
deviceType: string;
alertTypes: string[];
data: number[];
}
export function deviceRangeSet(data: IAreaConfigItem) {
return api.post('/api/config/device/area/save', data);
}
/**
* id获取配置信息
* @param id id
*/
export function queryDeviceRangeById(
id: number
): Promise<IAreaConfig<IAreaConfigItem>> {
return api.get(`/api/config/device/area/${id}`);
}
interface IAreaConfig<T = unknown> {
data: T;
}

View File

@ -1,36 +1,29 @@
import { api } from 'src/boot/axios';
import { PageDto, PageQueryDto } from './ApiCommon';
import { TimeConfigItem } from './AlarmTipTimeConfig';
const AlertTipUriBase = '/api/alertTip';
const DraftUriBase = '/api/alertTip';
interface AlarmInfoCreateParams {
id: number;
export interface createParams {
alertType: string;
tipTimeIds: string[];
areaConfigId: number;
timeType: string;
locationType: string;
drivingInfo: string;
submissionInfo: string;
}
export interface AlarmInfoListItem {
interface Item {
id: number;
alertType: string;
timeConfigList: TimeConfigItem[];
areaConfigId: number;
timeType: string;
locationType: string;
drivingInfo: string;
submissionInfo: string;
areaConfigName?: string;
}
export interface AlarmInfo<T = unknown> {
data: T;
}
export class PagingQueryParams extends PageQueryDto {
alertType?: string;
timeType?: string;
areaConfigName?: string;
locationType?: string;
}
/**
@ -40,8 +33,8 @@ export class PagingQueryParams extends PageQueryDto {
*/
export async function alarmInfoPageQuery(
params: PagingQueryParams
): Promise<PageDto<AlarmInfoListItem>> {
const response = await api.get(`${AlertTipUriBase}/page`, {
): Promise<PageDto<Item>> {
const response = await api.get(`${DraftUriBase}/page`, {
params: params,
});
return response.data;
@ -51,29 +44,17 @@ export async function alarmInfoPageQuery(
* id获取决策信息
* @param id 稿id
*/
export function queryAlarmInfoById(
id: number
): Promise<AlarmInfo<AlarmInfoListItem>> {
return api.get(`${AlertTipUriBase}/id/${id}`);
export function queryAlarmInfoById(id: number): Promise<AlarmInfo<Item>> {
return api.get(`${DraftUriBase}/id/${id}`);
}
/**
*
* @param type 稿type
*/
export function queryAlarmInfoByType(
type: string
): Promise<AlarmInfo<AlarmInfoListItem>> {
return api.get(`${AlertTipUriBase}/type/${type}`);
}
/**
*
*
* @param params
* @returns
*/
export function createAlarmInfo(draftData: AlarmInfoCreateParams) {
return api.post(`${AlertTipUriBase}`, draftData);
export function createAlarmInfo(draftData: createParams) {
return api.post(`${DraftUriBase}`, draftData);
}
/**
@ -81,26 +62,18 @@ export function createAlarmInfo(draftData: AlarmInfoCreateParams) {
* @param id 稿id
*/
export function deleteAlarmInfo(id: number) {
return api.delete(`${AlertTipUriBase}/id/${id}`);
return api.delete(`${DraftUriBase}/id/${id}`);
}
/**
*
* @param id 线id
* @param tipType
*
* @param data
* @returns
*/
export async function getDeviceAreaByAlarmType(
id: number,
alertType: string
): Promise<AreaConfigItem[]> {
const response = await api.post(
`${AlertTipUriBase}/find/type/${id}/${alertType}`,
{}
);
return response.data;
export function updataAlarmInfo(id: number, data: Item) {
return api.put(`${DraftUriBase}/id`, data);
}
export interface AreaConfigItem {
id: number;
areaName: string;
export interface AlarmInfo<T = unknown> {
data: T;
}

View File

@ -2,39 +2,39 @@ import { api } from 'src/boot/axios';
const platformUriBase = '/mock/platform/status';
const stationUriBase = '/mock/rtu/status';
const serverUriBase = '/mock/server/send';
export interface mockPlatformParams {
emergstop: boolean;
trainberth: boolean;
close: boolean;
upHold: boolean;
downHold: boolean;
upOccHold: boolean;
downOccHold: boolean;
psdOpen: boolean;
psdCut: boolean;
upSkipstop: boolean;
downSkipstop: boolean;
upTrainSkipstop: boolean;
downTrainSkipstop: boolean;
id: string;
nextSectionRunTime: number;
nextSectionRunLevel: number;
stopTime: number;
}
export function mockPlatformApi(lineId: number, data: mockPlatformParams) {
export function mockPlatformApi(
lineId: number,
data: {
emergstop: boolean;
trainberth: boolean;
close: boolean;
upHold: boolean;
downHold: boolean;
upOccHold: boolean;
downOccHold: boolean;
psdOpen: boolean;
psdCut: boolean;
upSkipstop: boolean;
downSkipstop: boolean;
upTrainSkipstop: boolean;
downTrainSkipstop: boolean;
id: string;
nextSectionRunTime: 0;
nextSectionRunLevel: 0;
stopTime: 0;
}
) {
return api.post(`${platformUriBase}/${lineId}`, data);
}
export function mockStationApi(
lineId: number,
data: {
ipRtuStusDown: boolean;
ipRtuStusInLocalCtrl: boolean;
ipRtuStusInCentralCtrl: boolean;
ipRtuStusInEmergencyCtrl: boolean;
ipRtuStusDown: true;
ipRtuStusInLocalCtrl: true;
ipRtuStusInCentralCtrl: true;
ipRtuStusInEmergencyCtrl: true;
id: string;
}
) {
@ -72,14 +72,3 @@ export function mockSignalApi(
) {
return api.post(`/mock/signal/status/${lineId}`, data);
}
export function mockServerApi(data: {
deviceType: string;
messageId: string;
lineId: number;
rtuId: number;
deviceName: string;
deviceStatus: number;
}) {
return api.post(serverUriBase, data);
}

View File

@ -44,21 +44,6 @@ export enum DevType {
DEVICE_TYPE_GAMA = 'DEVICE_TYPE_GAMA',
}
export const deviceTypeMap = {
DEVICE_TYPE_UNKNOW: '未知设备',
DEVICE_TYPE_RTU: '集中站',
DEVICE_TYPE_STATION: '车站',
DEVICE_TYPE_SIGNAL: '信号机',
DEVICE_TYPE_SWITCH: '道岔',
DEVICE_TYPE_TRACK: '轨道',
DEVICE_TYPE_ENTRY: '方向设备',
DEVICE_TYPE_PLATFORM: '站台',
DEVICE_TYPE_SCADA: '供电区段',
DEVICE_TYPE_WATERPROOF_DOOR: '防淹门',
DEVICE_TYPE_WORK_AREA: '工作区',
DEVICE_TYPE_GAMA: '区域自动驾驶',
};
export function trainMockApi(
lineId: number,
data: {

View File

@ -1,4 +1,7 @@
import { api } from 'src/boot/axios';
import { ITurnoutState } from 'src/graphics/turnout/Turnout';
const base = '/mock/train';
interface State {
id: string;
@ -34,6 +37,9 @@ interface State {
export async function setSwitchStatus(lineId: string, state: State) {
try {
delete state._state;
delete state._graphicType;
state.speedLimit = 0;
return await api.post(`/mock/switch/status/${lineId}`, state);
} catch (err) {
console.error(err);
@ -41,6 +47,8 @@ export async function setSwitchStatus(lineId: string, state: State) {
}
export async function setTrackStatus(lineId: string, state: State) {
try {
delete state._state;
delete state._graphicType;
return await api.post(`/mock/track/status/${lineId}`, state);
} catch (err) {
console.error(err);

View File

@ -1,80 +0,0 @@
<template>
<draggable-dialog
ref="dialogRef"
@show="onDialogShow"
seamless
title="与卡斯柯连接状态信息"
:width="300"
:height="0"
>
<template v-slot:footer>
<q-table
ref="tableRef"
row-key="id"
v-model:pagination="pagination"
:rows="rows"
:columns="columns"
@request="onRequest"
:rows-per-page-options="[10, 20, 50, 100]"
>
</q-table>
</template>
</draggable-dialog>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue';
import DraggableDialog from './common/DraggableDialog.vue';
import { QTable } from 'quasar';
import { useLineNetStore } from 'src/stores/line-net-store';
import { state } from 'src/protos/system_warn_message';
const lineNetStore = useLineNetStore();
const dialogRef = ref<InstanceType<typeof DraggableDialog>>();
const tableRef = ref<QTable>();
const columns: QTable['columns'] = [
{ name: 'lineId', label: '线路ID', field: 'lineId', align: 'center' },
{
name: 'areaName',
label: '实时连接',
field: (row) => (row.occRealConned ? '是' : '否'),
align: 'center',
},
{
name: 'deviceType',
label: '非实时连接',
field: (row) => (row.occUnrealConned ? '是' : '否'),
align: 'center',
},
];
const rows = ref<state.WarnMessage[]>([]);
const pagination = ref({
sortBy: 'desc',
descending: false,
page: 1,
rowsPerPage: 10,
rowsNumber: 10,
});
watch(
() => lineNetStore.connectInfo,
() => {
onDialogShow();
}
);
const onRequest: QTable['onRequest'] = async (props) => {
const { page, rowsPerPage } = props.pagination;
pagination.value.page = page;
pagination.value.rowsPerPage = rowsPerPage;
const datas = lineNetStore.connectInfo?.msgs;
if (datas) {
rows.value = datas.slice((page - 1) * rowsPerPage, page * rowsPerPage - 1);
pagination.value.rowsNumber = datas.length;
}
};
const onDialogShow = () => {
tableRef.value?.requestServerInteraction();
};
</script>

View File

@ -1,30 +0,0 @@
import { type GraphicDataBase } from 'src/drawApp/graphics/GraphicDataBase';
import { IDrawApp, JlGraphic } from 'jl-graphic';
import { useDrawStore } from 'src/stores/draw-store';
import { onMounted, onUnmounted, reactive } from 'vue';
const drawStore = useDrawStore();
export function useFormData<T extends GraphicDataBase>(
source: T,
app: IDrawApp
) {
const data = reactive<T>(source);
onMounted(() => {
app.bindFormData(data);
});
onUnmounted(() => {
app.unbindFormData(data);
});
function onUpdate() {
const graphic = drawStore.selectedGraphic as JlGraphic;
if (graphic) {
app.updateGraphicAndRecord(graphic, data);
}
}
return { data, onUpdate };
}

View File

@ -43,14 +43,16 @@ import { reactive } from 'vue';
const list = reactive([
{
path: '/monitor',
label: '监控',
icon: 'computer',
},
{
path: '/alarmList',
label: '报警列表',
icon: 'access_alarm',
path: '',
label: '系统管理',
icon: 'dataset',
children: [
{
path: '/sysManage/user',
label: '用户管理',
icon: 'manage_accounts',
},
],
},
{
path: '',
@ -65,41 +67,24 @@ const list = reactive([
{
path: '/dataManage/publish',
label: '发布管理',
icon: 'playlist_add_check',
icon: 'app_registration',
},
{
path: '/dataManage/lineInfo',
label: '线路信息管理',
icon: 'format_list_numbered',
icon: 'app_registration',
},
{
path: '/dataManage/decisionInfo',
label: '决策信息管理',
icon: 'format_align_center',
},
{
path: '/dataManage/thresholdValue',
label: '报警故障阈值配置',
icon: 'format_indent_increase',
},
{
path: '/dataManage/alarmTipTimeConfig',
label: '报警提示时间配置',
icon: 'access_time',
icon: 'app_registration',
},
],
},
{
path: '',
label: '系统管理',
icon: 'dataset',
children: [
{
path: '/sysManage/user',
label: '用户管理',
icon: 'manage_accounts',
},
],
path: '/monitor',
label: '监控',
icon: 'computer',
},
]);
</script>

View File

@ -1,6 +1,5 @@
<template>
<draggable-dialog
v-model="showDialog"
seamless
title="报警"
:titleColor="`${bgColor}`"
@ -9,7 +8,6 @@
:width="dialogWidth"
fontColor=""
:height="0"
bgBorder="solid 2px black"
>
<template v-slot:footer>
<div>
@ -19,52 +17,18 @@
<q-separator />
<div class="alarm-message-detail">
<div class="left">
<div class="text">时间{{ alarmInfo.time }}</div>
<div class="text">级别{{ alarmInfo.level }}</div>
<div class="text">线路{{ alarmInfo.lineId }}号线</div>
<div class="text">时间:{{ alarmInfo.time }}</div>
<div class="text">级别:{{ alarmInfo.level }}</div>
<div class="text">线路:{{ alarmInfo.lineId }}号线</div>
</div>
<div class="right">
<div class="text">设备{{ alarmInfo.alertObject }}</div>
<div class="text" v-if="!showFaultType">
类型{{ alarmInfo.alertType }}
</div>
<div class="text" v-else>类型{{ faultType }}</div>
<div class="text">设备:{{ alarmInfo.alertObject }}</div>
<div class="text">类型:{{ alarmInfo.alertType }}</div>
</div>
</div>
</q-card>
</div>
<div class="confirm-message" v-if="showConfirmMmessage">
<q-card class="box-card">
<div class="head">选择故障类型</div>
<q-separator />
<q-form class="detail" ref="myForm" @submit="confirmAlarm">
<q-select
style="width: 200px"
dense
outlined
v-model="faultType"
:options="faultTypeOptions"
label="故障类型"
:rules="[(val) => val.length > 0 || '请选择故障类型!']"
/>
<q-card-actions align="left">
<div class="q-gutter-md">
<q-btn color="primary" label="确认" type="submit" />
<q-btn color="red" label="误报" @click="falseAlarm" />
<q-btn
color="primary"
label="人工接警"
@click="manualAlarm"
/>
</div>
</q-card-actions>
</q-form>
</q-card>
</div>
<div
class="decision-message"
v-if="!showConfirmMmessage && showDecisionmessage"
>
<div class="decision-message">
<q-card class="box-card">
<div class="head">行车方面</div>
<q-separator />
@ -87,30 +51,39 @@
<script setup lang="ts">
import DraggableDialog from '../common/DraggableDialog.vue';
import { useLineNetStore, AlarmInfo } from 'src/stores/line-net-store';
import { onMounted, ref } from 'vue';
import { QForm, useQuasar } from 'quasar';
import { saveAlertTypeData, showAlertTypeData } from './alarmInfoEnum';
import {
recordConfirmAlarmInfoByTipType,
recordFailAlarmInfoById,
recordManualAlarmInfoById,
} from 'src/api/AlertMock';
import { useLineNetStore } from 'src/stores/line-net-store';
import { onMounted, ref, watch } from 'vue';
import { queryAlarmInfoById } from 'src/api/DecisionInfo';
const props = defineProps<{
alarmMeaasge?: AlarmInfo;
waitAlarmMeaasge?: AlarmInfo;
onHandle: (id: string) => void;
}>();
import { useQuasar } from 'quasar';
let bgColor = ref('');
const dialogWidth = window.screen.width * 0.5;
const lineNetStore = useLineNetStore();
const showConfirmMmessage = ref(true);
const faultType = ref('');
let faultTypeOptions = [];
const showFaultType = ref(false);
watch(
() => lineNetStore.alarmInfo,
(val) => {
if (val.length) {
search();
updata();
}
},
{ deep: true }
);
const mapAlarmMessage = new Map([
['I', 'I类信息'],
['II', 'II类信息'],
['III', 'III类信息'],
['IV', 'IV类信息'],
['0', '未知'],
['1', '蓝显'],
['2', '列车延误2分钟'],
['3', '列车延误10分钟'],
['4', '整侧站台门无关闭锁紧信号'],
['5', '整侧站台门无法打开'],
['6', '整侧站台门无法关闭'],
]);
const mapColor = new Map([
['I', 'rgb(250,61,46)'],
@ -127,106 +100,36 @@ const alarmInfo = ref({
alertObject: '',
alertType: '',
locator_device_id: '',
alert_location_id: '',
});
const drivingInfo = ref('');
const submissionInfo = ref('');
let messageUse: AlarmInfo;
const showDialog = ref(true);
const showDecisionmessage = ref(true);
onMounted(() => {
if (props.alarmMeaasge == undefined && props.waitAlarmMeaasge == undefined) {
messageUse = lineNetStore.alarmInfo[0] as AlarmInfo;
} else if (props.waitAlarmMeaasge) {
messageUse = props.waitAlarmMeaasge as AlarmInfo;
} else {
messageUse = props.alarmMeaasge as AlarmInfo;
if (messageUse.alarmStatus == 1) {
showConfirmMmessage.value = false;
searchById();
} else if (messageUse.alarmStatus == 0) {
showConfirmMmessage.value = false;
showDecisionmessage.value = false;
}
}
search();
updata();
switch (alarmInfo.value.alertType) {
case '道岔失表':
faultTypeOptions = ['道岔均失表', '道岔定位失表', '道岔反位失表'];
break;
default:
faultTypeOptions = [alarmInfo.value.alertType];
faultType.value = alarmInfo.value.alertType;
break;
}
});
const myForm = ref<QForm | null>(null);
function confirmAlarm() {
myForm.value?.validate().then(async (res) => {
if (res) {
showConfirmMmessage.value = false;
showFaultType.value = true;
searchByTipType().then(() => {
lineNetStore.alarmInfoListTable?.requestServerInteraction();
props.onHandle(messageUse.id);
});
}
});
}
function falseAlarm() {
showDialog.value = false;
recordFailAlarmInfoById(+messageUse.id).then(() => {
lineNetStore.alarmInfoListTable?.requestServerInteraction();
props.onHandle(messageUse.id);
});
}
function manualAlarm() {
showDialog.value = false;
recordManualAlarmInfoById([+messageUse.id]).then(() => {
lineNetStore.alarmInfoListTable?.requestServerInteraction();
props.onHandle(messageUse.id);
});
}
function updata() {
bgColor.value = mapColor.get(messageUse.level) as string;
alarmInfo.value.time = messageUse.alert_time
bgColor.value = mapColor.get(lineNetStore.alarmInfo[0].level) as string;
alarmInfo.value.time = lineNetStore.alarmInfo[0].alert_time
.substring(0, 19)
.replace('T', ' ');
alarmInfo.value.level = (showAlertTypeData as never)[messageUse.level + ''];
alarmInfo.value.alertType = (showAlertTypeData as never)[
messageUse.alert_type + ''
];
alarmInfo.value.alertObject = messageUse.alert_object.replace(/\[|]/g, '');
alarmInfo.value.lineId = messageUse.line_id;
alarmInfo.value.level = mapAlarmMessage.get(
lineNetStore.alarmInfo[0].level
) as string;
alarmInfo.value.alertType = mapAlarmMessage.get(
lineNetStore.alarmInfo[0].alert_type + ''
) as string;
alarmInfo.value.alertObject = lineNetStore.alarmInfo[0].alert_object.replace(
/\[|]/g,
''
);
alarmInfo.value.lineId = lineNetStore.alarmInfo[0].line_id;
}
async function searchByTipType() {
async function search() {
try {
const type = (saveAlertTypeData as never)[faultType.value];
const response = await recordConfirmAlarmInfoByTipType(
+messageUse.id,
type,
messageUse.alert_location_id
);
drivingInfo.value = JSON.parse(response.data.drivingInfo);
submissionInfo.value = JSON.parse(response.data.submissionInfo);
lineNetStore.treatedAlarm(messageUse);
} catch (err) {
$q.notify({
type: 'negative',
message: '没有此类故障的辅助决策信息',
});
}
}
async function searchById() {
try {
const id = messageUse.alert_tip_id;
const id = lineNetStore.alarmInfo[0].alert_tip_id;
const response = await queryAlarmInfoById(id);
drivingInfo.value = JSON.parse(response.data.drivingInfo);
submissionInfo.value = JSON.parse(response.data.submissionInfo);
@ -251,14 +154,11 @@ async function searchById() {
}
.alarm-message-detail {
display: flex;
flex-flow: row wrap;
.left,
.right {
width: 50%;
padding: 0 5px;
.text {
width: 450px;
word-wrap: break-word;
margin-bottom: 10px;
font-size: 20px;
}
@ -266,26 +166,12 @@ async function searchById() {
}
}
}
.confirm-message {
margin-top: 10px;
.box-card {
height: 180px;
padding: 0 5px;
.head {
padding: 5px 5px;
font-size: 20px;
font-weight: 600;
}
.detail {
margin-top: 10px;
}
}
}
.decision-message {
display: flex;
justify-content: space-between;
margin-top: 10px;
margin: 10px 0;
.box-card {
max-height: 300px;
overflow: auto;
.head {
padding: 5px 5px;

View File

@ -1,138 +0,0 @@
export enum showAlertTypeData {
'未知故障',
'蓝显',
'列车延误2分钟',
'列车延误10分钟',
'整侧站台门无关闭锁紧信号',
'整侧站台门无法打开',
'整侧站台门无法关闭',
'道岔失表',
'道岔均失表',
'道岔定位失表',
'道岔反位失表',
'红光带',
'橙光带',
'计轴红光带',
'计轴大面积红光带',
'计轴橙光带',
'计轴大面积橙光带',
'道岔大面积失表',
'列车信号故障',
'全线蓝显',
'联锁区红光带',
'联锁区橙光带',
'联锁区失表',
'一级联锁',
I = 'I类信息',
II = 'II类信息',
III = 'III类信息',
IV = 'IV类信息',
TRAIN_DELAY_2 = '列车延误2分钟',
TRAIN_DELAY_10 = '列车延误10分钟',
BLUE_DISPLAY = '蓝显',
PLATFORM_DOOR_WITHOUT_LOCKED_SIGNAL = '整侧站台门无关闭锁紧信号',
PLATFORM_DOOR_CANNOT_OPEN = '整侧站台门无法打开',
PLATFORM_DOOR_CANNOT_CLOSE = '整侧站台门无法关闭',
QX = '全线',
YHZ_LSQ = '鱼化寨联锁区',
HJM_LSQ = '胡家庙联锁区',
BCT_LSQ = '北池头联锁区',
BSQ_LSQ = '保税区联锁区',
SWITCH_LOST = '道岔失表',
SWITCH_All_LOST = '道岔均失表',
SWITCH_DW_LOST = '道岔定位失表',
SWITCH_FW_LOST = '道岔反位失表',
AXLE_LED_RED = '计轴红光带',
AXLE_LED_RED_MOST = '计轴大面积红光带',
AXLE_LED_ORANGE = '计轴橙光带',
AXLE_LED_ORANGE_MOST = '计轴大面积橙光带',
SWITCH_LOST_MOST = '道岔大面积失表',
TRAIN_EB_ATP = '列车信号故障',
ALL_LINE_BLUE_DISPLAY = '全线蓝显',
AXLE_LED_RED_INTERLOCK_AREA = '联锁区红光带',
AXLE_LED_ORANGE_INTERLOCK_AREA = '联锁区橙光带',
SWITCH_LOST_INTERLOCK_AREA = '联锁区失表',
INTERLOCK_LEVEL_ONE = '一级联锁',
}
export enum saveAlertTypeData {
2 = 'TRAIN_DELAY_2',
10 = 'TRAIN_DELAY_10',
= 'BLUE_DISPLAY',
= 'PLATFORM_DOOR_WITHOUT_LOCKED_SIGNAL',
= 'PLATFORM_DOOR_CANNOT_OPEN',
= 'PLATFORM_DOOR_CANNOT_CLOSE',
线 = 'QX',
= 'YHZ_LSQ',
= 'HJM_LSQ',
= 'BCT_LSQ',
= 'BSQ_LSQ',
= 'SWITCH_LOST',
= 'SWITCH_All_LOST',
= 'SWITCH_DW_LOST',
= 'SWITCH_FW_LOST',
= 'AXLE_LED_RED',
= 'AXLE_LED_RED_MOST',
= 'AXLE_LED_ORANGE',
= 'AXLE_LED_ORANGE_MOST',
= 'SWITCH_LOST_MOST',
= 'TRAIN_EB_ATP',
线 = 'ALL_LINE_BLUE_DISPLAY',
= 'AXLE_LED_RED_INTERLOCK_AREA',
= 'AXLE_LED_ORANGE_INTERLOCK_AREA',
= 'SWITCH_LOST_INTERLOCK_AREA',
= 'INTERLOCK_LEVEL_ONE',
}
export const GuardConfigTypeData = {
switchLostTimes: { label: '道岔失表', unit: '秒', deviceType: '道岔' },
switchLostMostNums: {
label: '道岔大面积失表',
unit: '个',
deviceType: '道岔',
},
redLedMostNums: { label: '大面积红光带', unit: '个', deviceType: '计轴区段' },
orangeLedMostNums: {
label: '大面积橙光带',
unit: '个',
deviceType: '计轴区段',
},
canNotOpenTimes: {
label: '站台门无法打开',
unit: '秒',
deviceType: '屏蔽门',
},
canNotCloseTimes: {
label: '站台门无法关闭',
unit: '秒',
deviceType: '屏蔽门',
},
trainAtpCutTimes: {
label: '列车制动后导致atp切除的超时时间',
unit: '秒',
deviceType: '信号',
},
};
type findType = string | number;
export function isArraysEqual(arr1: findType[], arr2: findType[]) {
if (arr1.length !== arr2.length) {
return false;
}
return arr1.sort().join(',') === arr2.sort().join(',');
}
export enum ShowAlertStateData {
'未处理' = -1,
'误报',
'确认',
'人工接警',
}
export const alertStatusOptions = [
{ label: '全部', value: 999 },
{ label: '未处理', value: -1 },
{ label: '误报', value: 0 },
{ label: '确认', value: 1 },
{ label: '人工接警', value: 2 },
];

View File

@ -0,0 +1,173 @@
<template>
<draggable-dialog
seamless
title="报警列表"
:width="dialogWidth"
:height="dialogHeight"
>
<q-table
ref="tableRef"
title="报警信息"
:style="{ height: dialogHeight + 'px' }"
:rows="rows"
:columns="columnDefs"
row-key="id"
v-model:pagination="pagination"
:rows-per-page-options="[10, 20, 50, 100]"
:loading="loading"
:filter="filter"
binary-state-sort
@request="onRequest"
>
<template v-slot:top-left>
<q-input
dense
debounce="1000"
v-model.number="filter.lineId"
label="线路ID"
type="number"
/>
<q-btn flat round color="primary" icon="search" />
</template>
<template v-slot:top-right>
<q-input
dense
debounce="1000"
v-model="filter.alertType"
label="故障类型"
></q-input>
<q-btn flat round color="primary" icon="search" />
</template>
</q-table>
</draggable-dialog>
</template>
<script setup lang="ts">
import DraggableDialog from '../common/DraggableDialog.vue';
import { ref, reactive, onMounted } from 'vue';
import { useQuasar, type QTableColumn } from 'quasar';
import { alarmInfoListQuery } from 'src/api/AlertMock';
const $q = useQuasar();
const dialogWidth = window.screen.width * 0.5;
const dialogHeight = window.screen.height * 0.5;
onMounted(() => {
setTimeout(() => {
tableRef.value.requestServerInteraction();
});
});
const columnDefs: QTableColumn[] = [
{
name: 'id',
label: '编号',
field: 'id',
required: true,
align: 'center',
},
{
name: 'alertTime',
label: '时间',
field: 'alertTime',
align: 'center',
},
{
name: 'lineId',
label: '线路',
field: 'lineId',
align: 'center',
},
{
name: 'alertObject',
label: '设备',
field: 'alertObject',
align: 'center',
},
{
name: 'level',
label: '级别',
field: (row) => {
if (row.level) {
return (showAlertTypeData as never)[row.level + ''];
}
},
align: 'center',
},
{
name: 'alertType',
label: '故障类型',
field: (row) => {
if (row.alertType) {
return (showAlertTypeData as never)[row.alertType];
}
},
align: 'center',
},
];
const tableRef = ref();
const rows = reactive([]);
const filter = reactive({
alertType: '',
lineId: '',
});
const loading = ref(false);
const pagination = ref({
sortBy: 'desc',
descending: false,
page: 1,
rowsPerPage: 10,
rowsNumber: 10,
});
async function onRequest(props: any) {
const { page, rowsPerPage, sortBy, descending } = props.pagination;
const filter = props.filter;
loading.value = true;
try {
let response = await alarmInfoListQuery({
current: page,
size: rowsPerPage,
alertType: (saveAlertTypeData as never)[filter.alertType],
lineId: filter.lineId,
});
const pageData = response;
pagination.value.rowsNumber = pageData.total;
pagination.value.page = page;
pagination.value.rowsPerPage = rowsPerPage;
pagination.value.sortBy = sortBy;
pagination.value.descending = descending;
rows.splice(0, rows.length, ...(pageData.records as []));
} catch (err) {
$q.notify({
type: 'negative',
message: '无法获取报警信息列表',
});
} finally {
loading.value = false;
}
}
enum showAlertTypeData {
ALERT_TYPE_UNKNOWN = '未知故障',
BLUE_DISPLAY = '蓝显',
TRAIN_DELAY_2 = '列车延误2分钟',
TRAIN_DELAY_10 = '列车延误10分钟',
PLATFORM_DOOR_WITHOUT_LOCKED_SIGNAL = '站台门无关闭且锁紧信号',
PLATFORM_DOOR_CANNOT_OPEN = '整侧站台门无法打开',
PLATFORM_DOOR_CANNOT_CLOSE = '整侧站台门无法关闭',
I = 'I类信息',
II = 'II类信息',
III = 'III类信息',
IV = 'IV类信息',
}
enum saveAlertTypeData {
列车延误2分钟 = 'TRAIN_DELAY_2',
列车延误10分钟 = 'TRAIN_DELAY_10',
蓝显 = 'BLUE_DISPLAY',
站台门无关闭且锁紧信号 = 'PLATFORM_DOOR_WITHOUT_LOCKED_SIGNAL',
整侧站台门无法打开 = 'PLATFORM_DOOR_CANNOT_OPEN',
整侧站台门无法关闭 = 'PLATFORM_DOOR_CANNOT_CLOSE',
}
</script>

View File

@ -1,363 +0,0 @@
<template>
<draggable-dialog
seamless
title="报警统计"
:width="dialogWidth"
:height="dialogHeight"
>
<q-table
ref="tableRef"
:style="{ height: dialogHeight + 'px' }"
:rows="rows"
:columns="columnDefs"
row-key="index"
v-model:pagination="pagination"
:rows-per-page-options="[0]"
:loading="loading"
:filter="filter"
binary-state-sort
virtual-scroll
>
<template v-slot:top>
<q-form ref="myForm" @submit="onRequest" style="width: 100%">
<div class="q-gutter-md row justify-center items-start">
<q-select
dense
v-model="filter.lineId"
:options="optionsLineId"
emit-value
map-options
options-dense
label="线路ID"
style="width: 100px"
/>
<q-input
dense
v-model="filter.beginDateTime"
label="开始时间"
no-error-icon
mask="####-##-## ##:##:##"
hint="例如2023-09-05 16:05:09"
lazy-rules
:rules="[timeRangeValidation]"
:error="errorBeginTime"
:error-message="errorMessageBeginTime"
>
<template v-slot:prepend>
<q-icon name="event" class="cursor-pointer">
<q-popup-proxy
cover
transition-show="scale"
transition-hide="scale"
>
<q-date
v-model="filter.beginDateTime"
mask="YYYY-MM-DD HH:mm:ss"
landscape
>
<div class="row items-center justify-end">
<q-btn
v-close-popup
label="关闭"
color="primary"
flat
/>
</div>
</q-date>
</q-popup-proxy>
</q-icon>
</template>
<template v-slot:append>
<q-icon name="access_time" class="cursor-pointer">
<q-popup-proxy
cover
transition-show="scale"
transition-hide="scale"
>
<q-time
v-model="filter.beginDateTime"
mask="YYYY-MM-DD HH:mm:ss"
with-seconds
landscape
format24h
>
<div class="row items-center justify-end">
<q-btn
v-close-popup
label="关闭"
color="primary"
flat
/>
</div>
</q-time>
</q-popup-proxy>
</q-icon>
</template>
</q-input>
<q-input
dense
v-model="filter.endDateTime"
label="结束时间"
no-error-icon
mask="####-##-## ##:##:##"
hint="例如2023-09-05 16:05:09"
lazy-rules
:rules="[timeRangeValidation]"
:error="errorEndTime"
:error-message="errorMessageEndTime"
>
<template v-slot:prepend>
<q-icon name="event" class="cursor-pointer">
<q-popup-proxy
cover
transition-show="scale"
transition-hide="scale"
>
<q-date
v-model="filter.endDateTime"
mask="YYYY-MM-DD HH:mm:ss"
landscape
>
<div class="row items-center justify-end">
<q-btn
v-close-popup
label="关闭"
color="primary"
flat
/>
</div>
</q-date>
</q-popup-proxy>
</q-icon>
</template>
<template v-slot:append>
<q-icon name="access_time" class="cursor-pointer">
<q-popup-proxy
cover
transition-show="scale"
transition-hide="scale"
>
<q-time
v-model="filter.endDateTime"
mask="YYYY-MM-DD HH:mm:ss"
with-seconds
landscape
format24h
>
<div class="row items-center justify-end">
<q-btn
v-close-popup
label="关闭"
color="primary"
flat
/>
</div>
</q-time>
</q-popup-proxy>
</q-icon>
</template>
</q-input>
<q-select
dense
v-model="filter.alertTypes"
:options="typeOptions"
multiple
emit-value
map-options
options-dense
class="ellipsis"
label="类型"
style="width: 200px"
/>
<q-select
dense
v-model="filter.alertStatus"
:options="alertStatusOptions"
emit-value
map-options
options-dense
label="处理状态"
style="width: 100px"
/>
<q-btn color="primary" label="查询" type="submit" />
</div>
</q-form>
</template>
<template v-slot:body-cell-index="props">
<q-td :props="props">
<span>{{ props.rowIndex + 1 }}</span>
</q-td>
</template>
</q-table>
</draggable-dialog>
</template>
<script setup lang="ts">
import DraggableDialog from '../common/DraggableDialog.vue';
import { ref, reactive, computed } from 'vue';
import { useQuasar, type QTableColumn, QForm, date } from 'quasar';
import { IReportParams, recordAlarmReport } from 'src/api/AlertMock';
import {
showAlertTypeData,
saveAlertTypeData,
alertStatusOptions,
} from './alarmInfoEnum';
defineProps<{
optionsLineId: { label: string; value: number }[];
}>();
const $q = useQuasar();
const dialogWidth = window.screen.width * 0.6;
const dialogHeight = window.screen.height * 0.5;
const columnDefs: QTableColumn[] = [
{
name: 'index',
label: '编号',
field: 'index',
align: 'center',
},
{
name: 'alertType',
label: '故障类型',
field: (row) => {
if (row.alertType) {
return (showAlertTypeData as never)[row.alertType];
}
},
align: 'center',
},
{
name: 'counter',
label: '数量',
field: 'counter',
align: 'center',
},
];
const typeOptions = computed(() => {
const list: { label: string; value: string }[] = [];
// list.push({ label: '', value: '' });
for (let i in saveAlertTypeData) {
const obj = {
label: i,
value: saveAlertTypeData[i as keyof typeof saveAlertTypeData],
};
list.push(obj);
}
return list;
});
interface Ifilter extends IReportParams {
lineId: number;
}
const rows = reactive([]);
const filter = reactive<Ifilter>({
alertTypes: [],
lineId: 3,
beginDateTime: '',
endDateTime: '',
alertStatus: 999,
});
const loading = ref(false);
const pagination = ref({
sortBy: 'desc',
descending: false,
page: 1,
rowsPerPage: 10,
rowsNumber: 0,
});
function onRequest() {
myForm.value?.validate().then(async (res) => {
if (res) {
loading.value = true;
try {
const params: IReportParams = {};
if (filter.alertTypes?.length) {
Object.assign(params, { alertTypes: filter.alertTypes });
}
if (filter.beginDateTime) {
Object.assign(params, { beginDateTime: filter.beginDateTime });
}
if (filter.endDateTime) {
Object.assign(params, { endDateTime: filter.endDateTime });
}
if (filter.alertStatus != 999) {
Object.assign(params, { alertStatus: filter.alertStatus });
}
const response = await recordAlarmReport(filter.lineId, params);
pagination.value.rowsNumber = response.length;
pagination.value.rowsPerPage = response.length;
rows.splice(0, rows.length, ...(response as []));
} catch (err) {
$q.notify({
type: 'negative',
message: '无法获取报警统计列表',
});
} finally {
loading.value = false;
}
}
});
}
const myForm = ref<QForm | null>(null);
const errorBeginTime = ref(false);
const errorEndTime = ref(false);
const errorMessageBeginTime = ref('');
const errorMessageEndTime = ref('');
function timeRangeValidation() {
const Reg = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/;
if (filter.beginDateTime) {
if (
!date.isValid(filter.beginDateTime) ||
!Reg.test(filter.beginDateTime)
) {
errorBeginTime.value = true;
errorMessageBeginTime.value = '请输入正确的时间!';
return false;
} else {
filter.beginDateTime = date.formatDate(
filter.beginDateTime,
'YYYY-MM-DD HH:mm:ss'
);
}
}
if (filter.endDateTime) {
if (!date.isValid(filter.endDateTime) || !Reg.test(filter.endDateTime)) {
errorEndTime.value = true;
errorMessageEndTime.value = '请输入正确的时间!';
return false;
} else {
filter.endDateTime = date.formatDate(
filter.endDateTime,
'YYYY-MM-DD HH:mm:ss'
);
}
}
if (
filter.beginDateTime &&
filter.endDateTime &&
new Date(filter.endDateTime).getTime() <
new Date(filter.beginDateTime).getTime()
) {
errorBeginTime.value = true;
errorEndTime.value = true;
errorMessageBeginTime.value = '开始时间不能大于结束时间!';
errorMessageEndTime.value = '结束时间不能小于开始时间!';
return false;
}
errorBeginTime.value = false;
errorEndTime.value = false;
errorMessageBeginTime.value = '';
errorMessageEndTime.value = '';
return true;
}
</script>

View File

@ -1,202 +1,64 @@
<template>
<div>
<audio ref="audio">
<source :src="audioSrc" type="audio/mpeg" />
<source src="../../assets/buzzer.mp3" type="audio/mpeg" />
</audio>
</div>
</template>
<script setup lang="ts">
import { watch, onUnmounted, ref, toRaw } from 'vue';
import { AlarmInfo, useLineNetStore } from 'src/stores/line-net-store';
import { DialogChainObject, useQuasar } from 'quasar';
import { watch, onUnmounted, ref, onMounted } from 'vue';
import { useLineNetStore } from 'src/stores/line-net-store';
import { useQuasar } from 'quasar';
import alarmInfoDialog from 'src/components/alarm/alarmInfoDialog.vue';
import { showAlertTypeData } from './alarmInfoEnum';
import allLineBlue from '/alarmMusic/all-line-blue.mp3';
import atpcut from '/alarmMusic/atp-cut.mp3';
import blue from '/alarmMusic/blue.mp3';
import cannotClose from '/alarmMusic/cannot-close.mp3';
import cannotOpen from '/alarmMusic/cannot-open.mp3';
import orangeMost from '/alarmMusic/orange-most.mp3';
import orange from '/alarmMusic/orange.mp3';
import redMost from '/alarmMusic/red-most.mp3';
import red from '/alarmMusic/red.mp3';
import switchLostMost from '/alarmMusic/switch-lost-most.mp3';
import switchLost from '/alarmMusic/switch-lost.mp3';
import buzzer from '/alarmMusic/buzzer.mp3';
const lineNetStore = useLineNetStore();
const audio = ref();
const audioSrc = ref('');
const $q = useQuasar();
let waitShowDialog: AlarmInfo[] = [];
const dialogInstance = ref();
const playAble = ref(false);
const watchInteract = () => {
playAble.value = true;
document.removeEventListener('click', watchInteract);
document.removeEventListener('keydown', watchInteract);
};
watch(
() => lineNetStore.alarmInfo,
(val) => {
if (val.length) {
UpdataAlarmInfoList();
const hasShow = countHasShowFiveDialog();
if (!hasShow) {
const alarmType = val[0].alert_type;
alarm(alarmType, val[0].id);
playAlarmMusic(alarmType);
} else {
waitShowDialog.push(toRaw(lineNetStore.alarmInfo[0]));
}
if (val.length && audio.value.paused) {
playAlarmMusic();
}
}
if (val.length && !lineNetStore.alarmDialog) {
alarm();
}
},
{ deep: true }
);
watch(
() => lineNetStore.closeAllAlarmInfoDialog,
(val) => {
if (val) {
lineNetStore.closeAllAlarmInfoDialog = false;
closeAllDialog();
}
}
);
function playAlarmMusic(type: number) {
for (let i = 0; i < dialogInstance.length; i++) {
if (dialogInstance[i].show == false) {
dialogInstance.splice(i, 1);
i--;
}
}
if (lineNetStore.playAble && audio.value.paused) {
const alarmType = (showAlertTypeData as never)[type + ''];
const mapAlarmMusic = new Map([
['蓝显', blue],
['全线蓝显', allLineBlue],
['整侧站台门无法打开', cannotOpen],
['整侧站台门无法关闭', cannotClose],
['道岔失表', switchLost],
['道岔大面积失表', switchLostMost],
['计轴红光带', red],
['计轴大面积红光带', redMost],
['计轴橙光带', orange],
['计轴大面积橙光带', orangeMost],
['列车信号故障', atpcut],
]);
const music = mapAlarmMusic.get(alarmType);
if (music !== undefined) {
audioSrc.value = music;
} else {
audioSrc.value = buzzer;
}
audio.value.src = audioSrc.value;
function playAlarmMusic() {
if (playAble.value) {
audio.value.play();
}
}
//3
function timingPlayAlarmMusic(dialog: DialogChainObject) {
const timerIndex = dialogInstance.findIndex((item) => item.dialog == dialog);
clearTimeout(dialogInstance[timerIndex].timer);
dialogInstance[timerIndex].timer = setTimeout(() => {
for (let i = 0; i < dialogInstance.length; i++) {
if (
dialogInstance[i].show &&
!dialogInstance[i].hasHandle &&
dialogInstance[i].dialog == dialog
) {
playAlarmMusic(dialogInstance[i].alarmType);
timingPlayAlarmMusic(dialog);
}
}
}, 180000);
}
const dialogInstance: {
dialog: DialogChainObject;
show: boolean;
alarmType: number;
timer: string | number | NodeJS.Timeout | undefined;
id: string;
hasHandle: boolean;
}[] = [];
function countHasShowFiveDialog(): boolean {
let hasShow = 0;
for (let i = 0; i < dialogInstance.length; i++) {
if (dialogInstance[i].show) {
hasShow++;
if (hasShow > 4) break;
}
}
return hasShow > 4 ? true : false;
}
function alarm(alarmType: number, id: string, waitAlarmMeaasge?: AlarmInfo) {
const dialogInstanceItem = $q
.dialog({
component: alarmInfoDialog,
componentProps: {
waitAlarmMeaasge: waitAlarmMeaasge,
onHandle: (id: string) => {
for (let i = 0; i < dialogInstance.length; i++) {
if (dialogInstance[i].id == id) {
dialogInstance[i].hasHandle = true;
break;
}
}
},
},
})
function alarm() {
lineNetStore.alarmDialog = true;
dialogInstance.value = $q
.dialog({ component: alarmInfoDialog })
.onCancel(() => {
alarmWaitDialog();
const index = dialogInstance.findIndex(
(item) => item.dialog == dialogInstanceItem
);
dialogInstance[index].show = false;
lineNetStore.alarmDialog = false;
});
let timer: string | number | NodeJS.Timeout | undefined = undefined;
dialogInstance.push({
dialog: dialogInstanceItem,
show: true,
alarmType,
timer,
hasHandle: false,
id,
});
timingPlayAlarmMusic(dialogInstanceItem);
}
function alarmWaitDialog() {
if (countHasShowFiveDialog() && waitShowDialog.length) {
alarm(
waitShowDialog[0].alert_type,
waitShowDialog[0].id,
waitShowDialog[0]
);
playAlarmMusic(waitShowDialog[0].alert_type);
waitShowDialog.shift();
}
}
onMounted(() => {
document.addEventListener('click', watchInteract);
document.addEventListener('keydown', watchInteract);
});
function closeAllDialog() {
if (dialogInstance.length) {
dialogInstance.forEach((item) => {
clearTimeout(item.timer);
if (item.show) {
item.dialog.hide();
}
});
}
waitShowDialog = [];
}
let timeout: string | number | NodeJS.Timeout | undefined;
function UpdataAlarmInfoList() {
clearTimeout(timeout);
timeout = setTimeout(() => {
lineNetStore.alarmInfoListTable?.requestServerInteraction();
}, 1000);
}
onUnmounted(() => {
clearTimeout(timeout);
closeAllDialog();
if (dialogInstance.value && lineNetStore.alarmDialog) {
dialogInstance.value.hide();
}
});
</script>

View File

@ -1,264 +0,0 @@
<template>
<draggable-dialog
v-model="showsetAlartText"
seamless
title="设置故障演示"
:width="300"
:height="0"
>
<template v-slot:footer>
<div>
<q-card class="q-gutter-sm q-px-sm q-mt-sm">
<q-form ref="myForm" @submit="onSubmit" @reset="onReset">
<q-input
outlined
readonly
label="线路ID"
v-model.number="setAlartTextData.lineId"
type="number"
lazy-rules
:rules="[(val) => val || '请输入线路ID']"
/>
<q-select
outlined
label="故障类型"
v-model="setAlartTextData.alertType"
:options="optionsAlertType"
:rules="[(val) => val.length > 0 || '请选择故障类型!']"
/>
<q-list bordered separator class="rounded-borders">
<q-item>
<q-item-section no-wrap class="q-gutter-y-sm column">
<q-item-label> 框选的设备 </q-item-label>
<div class="q-gutter-sm row">
<q-chip
v-for="item in setAlartTextData.deviceCodes"
:key="item"
square
color="primary"
text-color="white"
removable
@remove="removeSelect(item)"
>
{{ item }}
</q-chip>
</div>
<q-btn
v-show="setAlartTextData.deviceCodes.length > 0"
style="width: 120px"
label="清空框选的设备"
color="red"
@click="clearSelect"
/>
</q-item-section>
</q-item>
</q-list>
<div class="q-gutter-sm q-pa-md row justify-center">
<q-btn
label="提交"
type="submit"
color="primary"
class="q-mr-md"
/>
<q-btn label="重置" type="reset" color="primary" />
</div>
</q-form>
</q-card>
</div>
</template>
</draggable-dialog>
</template>
<script setup lang="ts">
import DraggableDialog from '../common/DraggableDialog.vue';
import { onMounted, onUnmounted, ref, watch } from 'vue';
import { JlGraphic } from 'jl-graphic';
import { Station } from 'src/graphics/station/Station';
import { useLineStore } from 'src/stores/line-store';
import { QForm, useQuasar } from 'quasar';
import { ApiError } from 'src/boot/axios';
import { mockAlertSet } from 'src/api/AlertMock';
import { isArraysEqual, saveAlertTypeData } from './alarmInfoEnum';
import { Section, SectionType } from 'src/graphics/section/Section';
import { LogicSection } from 'src/graphics/logicSection/LogicSection';
const lineStore = useLineStore();
const setAlartTextData = ref<{
lineId: string;
alertType: string;
deviceCodes: string[];
}>({
lineId: '',
alertType: '',
deviceCodes: [],
});
const optionsAlertType = [
'蓝显',
'全线蓝显',
'整侧站台门无法打开',
'整侧站台门无法关闭',
'道岔失表',
'计轴红光带',
'计轴大面积红光带',
'计轴橙光带',
'计轴大面积橙光带',
'列车信号故障',
];
const mapAlertType = new Map([
['蓝显', ['station']],
['全线蓝显', ['station']],
['整侧站台门无法打开', ['Platform']],
['整侧站台门无法关闭', ['Platform']],
['道岔失表', ['Turnout']],
['计轴红光带', ['Section']],
['计轴大面积红光带', ['Section']],
['计轴橙光带', ['Section']],
['计轴大面积橙光带', ['Section']],
['列车信号故障', ['LogicSection', 'Turnout']],
]);
let selectGraphic: JlGraphic[] = [];
watch(
() => lineStore.selectedGraphics,
(val) => {
if (val && val.length > 0 && setAlartTextData.value.alertType) {
const selectGraphicId = selectGraphic.map((g) => g.id);
const appSelectedGraphicsId = lineStore.selectedGraphics?.map(
(g) => g.id
);
if (
appSelectedGraphicsId !== undefined &&
isArraysEqual(selectGraphicId, appSelectedGraphicsId)
) {
return;
}
const deviceFilter = lineStore.selectedGraphics?.filter((g) => {
let select = false;
if (
g.type == Station.Type &&
(g as Station).datas.concentrationStations &&
setAlartTextData.value.alertType == '蓝显'
) {
select = true;
}
if (
(g.type !== Station.Type &&
mapAlertType
.get(setAlartTextData.value.alertType)
?.includes(g.type)) ||
(g.type == Section.Type &&
(g as Section).datas.sectionType === SectionType.TurnoutPhysical &&
mapAlertType
.get(setAlartTextData.value.alertType)
?.includes(LogicSection.Type))
) {
select = true;
}
return select;
}) as JlGraphic[];
if (
[
'道岔失表',
'计轴红光带',
'计轴橙光带',
'列车信号故障',
'整侧站台门无法打开',
'整侧站台门无法关闭',
].includes(setAlartTextData.value.alertType)
) {
if (deviceFilter[0] !== undefined) {
selectGraphic = [deviceFilter[0]];
}
} else {
selectGraphic.push(...deviceFilter);
}
selectGraphic = Array.from(new Set(selectGraphic));
lineStore.getLineApp().updateSelected(...selectGraphic);
setAlartTextData.value.deviceCodes = selectGraphic.map((g) => g.code);
}
}
);
onMounted(() => {
lineStore.getLineApp().emit('options-update', {
mouseToolOptions: {
boxSelect: true,
viewportDrag: true,
viewportDragLeft: false,
wheelZoom: true,
},
});
clearSelect();
onReset();
setAlartTextData.value.lineId = lineStore.lineId as unknown as string;
});
const myForm = ref<QForm | null>(null);
const showsetAlartText = ref(true);
const $q = useQuasar();
async function onSubmit() {
myForm.value?.validate().then(async (res) => {
if (res) {
try {
const params = {
lineId: +setAlartTextData.value.lineId,
alertType: (saveAlertTypeData as never)[
setAlartTextData.value.alertType + ''
],
deviceCodes: setAlartTextData.value.deviceCodes,
};
await mockAlertSet(params);
$q.notify({
type: 'positive',
message: '设置故障演示成功',
});
onReset();
} catch (err) {
const apiErr = err as ApiError;
$q.notify({
type: 'negative',
message: apiErr.title,
});
} finally {
showsetAlartText.value = false;
}
}
});
}
function removeSelect(code: string) {
const removeIndex = setAlartTextData.value.deviceCodes.findIndex(
(item) => item == code
);
selectGraphic.splice(removeIndex, 1);
setAlartTextData.value.deviceCodes.splice(removeIndex, 1);
lineStore.getLineApp().updateSelected(...selectGraphic);
}
function clearSelect() {
setAlartTextData.value.deviceCodes = [];
selectGraphic = [];
lineStore.getLineApp().updateSelected();
}
function onReset() {
setAlartTextData.value = {
lineId: lineStore.lineId as unknown as string,
alertType: '',
deviceCodes: [],
};
selectGraphic = [];
}
onUnmounted(() => {
lineStore.getLineApp().emit('options-update', {
mouseToolOptions: {
boxSelect: false,
viewportDrag: true,
viewportDragLeft: true,
wheelZoom: true,
},
});
});
</script>

View File

@ -1,408 +0,0 @@
<template>
<draggable-dialog
v-model="showsetAlartText"
seamless
title="设置故障测试"
:width="300"
:height="0"
>
<template v-slot:footer>
<div>
<q-card class="q-gutter-sm q-px-sm q-mt-sm">
<q-form ref="myForm" @submit="onSubmit" @reset="onReset">
<q-input
outlined
readonly
label="线路ID"
v-model.number="setAlartTextData.lineId"
type="number"
lazy-rules
:rules="[(val) => val || '请输入线路ID']"
/>
<q-select
outlined
label="故障类型"
v-model="setAlartTextData.alertType"
:options="optionsAlertType"
:rules="[(val) => val.length > 0 || '请选择故障类型!']"
@update:model-value="onChooseAlertType"
/>
<q-list bordered separator class="rounded-borders">
<q-expansion-item
bordered
expand-separator
v-for="(configItem, index) in setAlartTextData.groupList"
:key="configItem"
v-model="configItem.expanded"
:label="configItem.groupName"
@click="toggleItem(index)"
>
<q-card>
<q-item no-wrap class="column">
<q-input
v-if="setAlartTextData.alertType == '列车信号故障'"
outlined
label="车组号"
v-model="configItem.groupId"
lazy-rules
:rules="[(val) => val.length > 0 || '请输入车组号!']"
/>
<q-select
outlined
label="故障测试状态"
v-model="configItem.status"
:options="optionsStatus"
:rules="[(val) => val.length > 0 || '请选择故障类型!']"
map-options
emit-value
/>
<div class="q-gutter-sm row">
<q-chip
v-for="(item, selectIndex) in configItem.deviceInfos"
:key="item"
square
color="primary"
text-color="white"
removable
@remove="removeSelect(selectIndex)"
clickable
@click="clickSelectCenter(selectIndex)"
>
{{ item.deviceName }}
</q-chip>
</div>
<div>
<q-btn
v-show="configItem.deviceInfos.length > 0"
style="width: 100px"
label="清空选择"
color="red"
class="q-mr-md"
@click="clearAllSelect(index)"
/>
<q-btn
v-if="setAlartTextData.alertType == '列车信号故障'"
label="删除测试组"
color="secondary"
@click="deleteSelectConfig(index)"
/>
</div>
</q-item>
</q-card>
</q-expansion-item>
</q-list>
<q-btn
v-if="setAlartTextData.alertType == '列车信号故障'"
class="q-mt-md"
label="增加测试组"
color="secondary"
@click="addSelectConfig"
/>
<div class="q-gutter-sm q-pa-md row justify-center">
<q-btn
label="提交"
type="submit"
color="primary"
class="q-mr-md"
/>
<q-btn label="重置" type="reset" color="primary" />
</div>
</q-form>
</q-card>
</div>
</template>
</draggable-dialog>
</template>
<script setup lang="ts">
import DraggableDialog from '../common/DraggableDialog.vue';
import { onMounted, onUnmounted, ref, watch } from 'vue';
import { JlGraphic } from 'jl-graphic';
import { Station } from 'src/graphics/station/Station';
import { useLineStore } from 'src/stores/line-store';
import { QForm, useQuasar } from 'quasar';
import { ApiError } from 'src/boot/axios';
import { mockLocalDemoTestSet } from 'src/api/AlertMock';
import { isArraysEqual, saveAlertTypeData } from './alarmInfoEnum';
const lineStore = useLineStore();
const setAlartTextData = ref<{
lineId: string;
alertType: string;
groupList: {
groupName: string;
groupId?: string;
status: string;
deviceInfos: {
deviceName: string;
deviceType: string;
}[];
expanded: boolean;
}[];
}>({
lineId: '',
alertType: '',
groupList: [
{
groupName: '测试组',
groupId: '',
status: '',
deviceInfos: [],
expanded: false,
},
],
});
const optionsAlertType = [
'蓝显',
'道岔失表',
'计轴红光带',
'计轴橙光带',
'列车信号故障',
];
const mapAlertType = new Map([
['蓝显', ['station']],
['道岔失表', ['Turnout']],
['计轴红光带', ['LogicSection', 'Turnout']],
['计轴橙光带', ['LogicSection', 'Turnout']],
['列车信号故障', ['LogicSection', 'Turnout']],
]);
enum DeviceType {
station = 'DEVICE_TYPE_RTU',
Turnout = 'DEVICE_TYPE_SWITCH',
LogicSection = 'DEVICE_TYPE_TRACK',
}
const optionsStatus = [
{ label: '正常', value: 'NORMAL' },
{ label: '设置', value: 'BEGIN' },
{ label: '报警', value: 'ALERT' },
];
let selectGraphic: JlGraphic[] = [];
watch(
() => lineStore.selectedGraphics,
(val) => {
if (
val &&
val.length > 0 &&
setAlartTextData.value.alertType &&
clickIndex !== null
) {
const selectGraphicId = selectGraphic.map((g) => g.id);
const appSelectedGraphicsId = lineStore.selectedGraphics?.map(
(g) => g.id
);
if (
appSelectedGraphicsId !== undefined &&
isArraysEqual(selectGraphicId, appSelectedGraphicsId)
) {
return;
}
const deviceFilter = lineStore.selectedGraphics?.filter((g) => {
let select = false;
if (
g.type == Station.Type &&
(g as Station).datas.concentrationStations &&
setAlartTextData.value.alertType == '蓝显'
) {
select = true;
}
if (
g.type !== Station.Type &&
mapAlertType.get(setAlartTextData.value.alertType)?.includes(g.type)
) {
select = true;
}
return select;
}) as JlGraphic[];
if (setAlartTextData.value.alertType !== '列车信号故障') {
selectGraphic.push(...deviceFilter);
} else if (deviceFilter.length) {
selectGraphic = [deviceFilter[0]];
}
selectGraphic = Array.from(new Set(selectGraphic));
lineStore.getLineApp().updateSelected(...selectGraphic);
setAlartTextData.value.groupList[clickIndex].deviceInfos = [];
selectGraphic.forEach((g) => {
setAlartTextData.value.groupList[clickIndex as number].deviceInfos.push(
{
deviceName: g.code,
deviceType: (DeviceType as never)[g.type + ''],
}
);
});
}
}
);
onMounted(() => {
lineStore.getLineApp().emit('options-update', {
mouseToolOptions: {
boxSelect: true,
viewportDrag: true,
viewportDragLeft: false,
wheelZoom: true,
},
});
onReset();
setAlartTextData.value.lineId = lineStore.lineId as unknown as string;
});
const myForm = ref<QForm | null>(null);
const showsetAlartText = ref(true);
const $q = useQuasar();
async function onSubmit() {
myForm.value?.validate().then(async (res) => {
if (res) {
const deviceInfos = setAlartTextData.value.groupList
.map((group) => {
const deviceInfo = group.deviceInfos.map((deviceInfo) => {
const status = group.status;
const groupId = group.groupId || '';
return {
deviceName: deviceInfo.deviceName,
deviceType: deviceInfo.deviceType,
status,
groupId,
};
});
return deviceInfo;
})
.flat();
try {
const params = {
lineId: +setAlartTextData.value.lineId,
deviceInfos,
};
const alertType = (saveAlertTypeData as never)[
setAlartTextData.value.alertType + ''
];
await mockLocalDemoTestSet(alertType, params);
$q.notify({
type: 'positive',
message: '设置故障测试成功',
});
onReset();
} catch (err) {
const apiErr = err as ApiError;
$q.notify({
type: 'negative',
message: apiErr.title,
});
} finally {
showsetAlartText.value = false;
}
}
});
}
let clickIndex: null | number = null;
function toggleItem(index: number) {
const lineApp = lineStore.getLineApp();
selectGraphic = [];
lineApp.updateSelected();
if (setAlartTextData.value.groupList[index].expanded == true) {
clickIndex = index;
const select: JlGraphic[] = [];
setAlartTextData.value.groupList[index].deviceInfos.forEach(
(deviceInfo) => {
const deviceType = (
Object.keys(DeviceType) as Array<keyof typeof DeviceType>
).find((key) => DeviceType[key] === deviceInfo.deviceType) as string;
const g = lineApp.queryStore.queryByCodeAndType(
deviceInfo.deviceName,
deviceType
) as JlGraphic;
select.push(g);
}
);
lineApp.updateSelected(...select);
} else {
clickIndex = null;
}
}
function clickSelectCenter(index: number) {
const lineApp = lineStore.getLineApp();
const clickTarget = setAlartTextData.value.groupList[clickIndex as number];
const deviceType = (
Object.keys(DeviceType) as Array<keyof typeof DeviceType>
).find(
(key) => DeviceType[key] === clickTarget.deviceInfos[index].deviceType
) as string;
const clickGraphic = lineApp.queryStore.queryByCodeAndType(
clickTarget.deviceInfos[index].deviceName,
deviceType
) as JlGraphic;
lineApp.makeGraphicCenterShow(clickGraphic);
}
function removeSelect(removeIndex: number) {
const clickTarget = setAlartTextData.value.groupList[clickIndex as number];
selectGraphic.splice(removeIndex, 1);
clickTarget.deviceInfos.splice(removeIndex, 1);
lineStore.getLineApp().updateSelected(...selectGraphic);
}
function clearAllSelect(index: number) {
setAlartTextData.value.groupList[index].deviceInfos = [];
selectGraphic = [];
lineStore.getLineApp().updateSelected();
}
function addSelectConfig() {
setAlartTextData.value.groupList.push({
groupName: '测试组',
groupId: '',
status: '',
deviceInfos: [],
expanded: false,
});
}
function deleteSelectConfig(index: number) {
setAlartTextData.value.groupList.splice(index, 1);
selectGraphic = [];
lineStore.getLineApp().updateSelected();
}
function onChooseAlertType() {
setAlartTextData.value.groupList = [
{
groupName: '测试组',
groupId: '',
status: '',
deviceInfos: [],
expanded: false,
},
];
}
function onReset() {
setAlartTextData.value = {
lineId: lineStore.lineId as unknown as string,
alertType: '',
groupList: [
{
groupName: '测试组',
groupId: '',
status: '',
deviceInfos: [],
expanded: false,
},
],
};
selectGraphic = [];
}
onUnmounted(() => {
lineStore.getLineApp().emit('options-update', {
mouseToolOptions: {
boxSelect: false,
viewportDrag: true,
viewportDragLeft: true,
wheelZoom: true,
},
});
});
</script>

View File

@ -1,60 +1,8 @@
<template>
<q-dialog
ref="dialogRef"
@hide="onHide"
v-bind="$attrs"
@show="emit('show')"
transitionShow="jump-up"
transitionHide="jump-down"
class="column"
>
<q-card
:style="{
transform: `translate3d(${offset.x}px, ${offset.y}px, 1px)`,
background: `${props.bgColor}`,
border: `${props.bgBorder}`,
}"
style="max-width: 2000px"
>
<q-bar
ref="headerRef"
class="non-selectable q-gutter-l"
style="
cursor: move;
border-top-right-radius: 0;
border-top-left-radius: 0;
"
:style="`height: ${props.titleHeight}px;background: ${props.titleColor}`"
>
<div
:style="`height: 100%; line-height: ${props.titleHeight}px; color:${props.fontColor};font-size: ${props.fontSize}px;`"
>
{{ props.title }}
</div>
<q-space />
<div style="margin-right: 10px"><slot name="titleButton"></slot></div>
<q-btn dense flat icon="sym_o_close" v-close-popup></q-btn>
</q-bar>
<q-scroll-area
:style="`width: ${props.width}px; height: ${props.height}px;`"
>
<slot></slot>
</q-scroll-area>
<div :style="`width: ${props.width}px`">
<slot name="footer"></slot>
</div>
</q-card>
</q-dialog>
</template>
<script setup lang="ts">
import { QBar, useDialogPluginComponent } from 'quasar';
import { ref, onMounted, onUnmounted, reactive } from 'vue';
import { ref, onMounted, onUnmounted, reactive, withDefaults } from 'vue';
const emit = defineEmits({
...useDialogPluginComponent.emitsObject,
show: () => true,
});
const emit = defineEmits([...useDialogPluginComponent.emits]);
const props = withDefaults(
defineProps<{
@ -66,7 +14,6 @@ const props = withDefaults(
width?: number;
height?: number;
bgColor?: string;
bgBorder?: string;
}>(),
{
width: 500,
@ -89,7 +36,6 @@ const offset = reactive({
});
const start = { x: 0, y: 0 };
const startOffset = { x: 0, y: 0 };
onMounted(() => {
window.addEventListener('mousedown', onMouseDown);
@ -100,24 +46,16 @@ onUnmounted(() => {
});
function onMove(e: MouseEvent) {
let y = e.clientY > startOffset.y ? e.clientY : startOffset.y;
if (y > window.innerHeight - props.titleHeight + startOffset.y) {
y = window.innerHeight - props.titleHeight + startOffset.y;
}
[offset.x, offset.y] = [e.clientX - start.x, y - start.y];
[offset.x, offset.y] = [e.screenX - start.x, e.screenY - start.y];
}
function onMouseUp() {
window.removeEventListener('mousemove', onMove);
window.removeEventListener('mouseup', onMouseUp);
startOffset.x = 0;
startOffset.y = 0;
}
function onMouseDown(e: MouseEvent) {
if (!e.target || !headerRef.value?.$el.contains(e.target)) return;
startOffset.x = e.offsetX;
startOffset.y = e.offsetY;
start.x = e.clientX - offset.x;
start.y = e.clientY - offset.y;
if (headerRef.value?.$el !== e.target) return;
start.x = e.screenX - offset.x;
start.y = e.screenY - offset.y;
window.addEventListener('mousemove', onMove);
window.addEventListener('mouseup', onMouseUp);
}
@ -126,3 +64,45 @@ function onHide() {
onDialogHide();
}
</script>
<template>
<q-dialog
ref="dialogRef"
@hide="onHide"
v-bind="$attrs"
transitionShow="jump-up"
transitionHide="jump-down"
class="column"
>
<q-card
:style="{
transform: `translate3d(${offset.x}px, ${offset.y}px, 1px)`,
background: `${props.bgColor}`,
}"
style="max-width: 2000px"
>
<q-bar
ref="headerRef"
class="non-selectable q-gutter-l"
style="cursor: move"
:style="`height: ${props.titleHeight}px;background: ${props.titleColor}`"
>
<div
:style="`color:${props.fontColor};font-size: ${props.fontSize}px;`"
>
{{ props.title }}
</div>
<q-space />
<q-btn dense flat icon="sym_o_close" v-close-popup></q-btn>
</q-bar>
<q-scroll-area
:style="`width: ${props.width}px; height: ${props.height}px;`"
>
<slot></slot>
</q-scroll-area>
<div :style="`width: ${props.width}px`">
<slot name="footer"></slot>
</div>
</q-card>
</q-dialog>
</template>

View File

@ -19,6 +19,9 @@
<template v-if="drawStore.drawGraphicType === Station.Type">
<station-template></station-template>
</template>
<!-- <template v-if="drawStore.drawGraphicType === Train.Type">
<train-template></train-template>
</template> -->
</q-card-section>
</q-card>
</div>
@ -51,6 +54,9 @@
<station-line-property
v-if="drawStore.selectedGraphicType === StationLine.Type"
></station-line-property>
<!-- <train-property
v-if="drawStore.selectedGraphicType === Train.Type"
></train-property> -->
<iscs-fan-property
v-else-if="drawStore.selectedGraphicType === IscsFan.Type"
></iscs-fan-property>
@ -81,11 +87,6 @@
<LogicSectionProperty
v-else-if="drawStore.selectedGraphicType === LogicSection.Type"
/>
<concentrationDividingLine-property
v-else-if="
drawStore.selectedGraphicType === ConcentrationDividingLine.Type
"
/>
</q-card-section>
</template>
</q-card>
@ -95,8 +96,6 @@
<script setup lang="ts">
import LinkTemplate from './templates/LinkTemplate.vue';
import RectTemplate from './templates/RectTemplate.vue';
import ConcentrationDividingLineProperty from './properties/ConcentrationDividingLineProperty.vue';
import { ConcentrationDividingLine } from 'src/graphics/concentrationDividingLine/ConcentrationDividingLine';
import PlatformTemplate from './templates/PlatformTemplate.vue';
import StationTemplate from './templates/StationTemplate.vue';
// import TrainTemplate from './templates/TrainTemplate.vue';

View File

@ -1,64 +0,0 @@
<!-- eslint-disable vue/no-mutating-props -->
<template>
<q-dialog ref="dialogRef" style="width 800px;">
<q-card
style="max-width: 900px"
:style="{ width: `${80 * props.runLinePoints.length}px` }"
>
<q-card-section> <div class="text-h6">划定端点</div> </q-card-section>
<q-card-section class="q-pt-none">
<q-range
class="q-mt-xl"
v-model="model"
color="purple"
style="padding: 0px 30px; font-size: 10px"
markers
:marker-labels="objMarkerLabel"
:min="0"
:max="props.runLinePoints.length - 1"
/>
</q-card-section>
<q-card-actions align="right" class="text-primary">
<q-btn flat label="取消" @click="onDialogCancel" v-close-popup />
<q-btn flat label="确认" @click="onDialogOK(model)" v-close-popup />
</q-card-actions>
</q-card>
</q-dialog>
</template>
<script setup lang="ts">
import { IPointData } from 'pixi.js';
import { useDialogPluginComponent } from 'quasar';
import { ref, onMounted } from 'vue';
const props = defineProps({
runLinePoints: {
type: Array<IPointData>,
required: true,
},
garyPointIndexs: {
type: Array<number>,
required: true,
},
});
const objMarkerLabel = (val: number) =>
`P${val}[${props.runLinePoints[val].x},${props.runLinePoints[val].y}]`;
const model = ref({
min: 0,
max: 0,
});
onMounted(() => {
if (props.garyPointIndexs.length) {
model.value = {
min: props.garyPointIndexs[0],
max: props.garyPointIndexs[props.garyPointIndexs.length - 1],
};
}
});
defineEmits([...useDialogPluginComponent.emits]);
const { dialogRef, onDialogOK, onDialogCancel } = useDialogPluginComponent();
</script>
<style scoped></style>

View File

@ -1,6 +1,12 @@
<template>
<q-form class="q-gutter-sm">
<q-input outlined readonly v-model="axleCountingModel.id" label="id" />
<q-input
outlined
readonly
v-model="axleCountingModel.id"
label="id"
hint=""
/>
<q-input
outlined
label="计轴名称"
@ -10,29 +16,24 @@
lazy-rules
autogrow
/>
<q-list bordered separator class="rounded-borders">
<q-item no-wrap class="q-gutter-y-sm column">
<div>公里标配置</div>
<q-select
outlined
style="margin-top: 10px"
v-model="axleCountingModel.kilometerSystem.coordinateSystem"
:options="CoordinateSystemOptions"
:map-options="true"
:emit-value="true"
@update:model-value="onUpdate"
label="坐标系"
></q-select>
<q-input
outlined
style="margin-top: 10px"
v-model.number="axleCountingModel.kilometerSystem.kilometer"
type="number"
@blur="onUpdate"
label="公里标(mm):"
/>
</q-item>
</q-list>
<q-select
outlined
style="margin-top: 10px"
v-model="kilometerSystem.coordinateSystem"
:options="CoordinateSystemOptions"
:map-options="true"
:emit-value="true"
@update:model-value="onUpdate"
label="坐标系"
></q-select>
<q-input
outlined
style="margin-top: 10px"
v-model.number="kilometerSystem.kilometer"
type="number"
@blur="onUpdate"
label="公里标(mm):"
/>
<q-list bordered separator class="rounded-borders">
<q-item>
<q-item-section no-wrap class="q-gutter-y-sm column">
@ -71,19 +72,17 @@
</template>
<script setup lang="ts">
import { useFormData } from 'src/components/DrawAppFormUtils';
import { AxleCountingData } from 'src/drawApp/graphics/AxleCountingInteraction';
import { AxleCounting } from 'src/graphics/axleCounting/AxleCounting';
import { Section } from 'src/graphics/section/Section';
import { Turnout } from 'src/graphics/turnout/Turnout';
import { useDrawStore } from 'src/stores/draw-store';
import { computed } from 'vue';
import { computed, onMounted, reactive, watch } from 'vue';
const drawStore = useDrawStore();
const { data: axleCountingModel, onUpdate } = useFormData(
new AxleCountingData(),
drawStore.getDrawApp()
);
const axleCountingModel = reactive(new AxleCountingData());
const kilometerSystem = reactive({ coordinateSystem: '', kilometer: 0 });
const CoordinateSystemOptions = [
{ label: '车辆段', value: 'DEPOT' },
{ label: '停车场', value: 'PARKING_LOT' },
@ -91,6 +90,46 @@ const CoordinateSystemOptions = [
{ label: '换线', value: 'TRANSFER' },
];
drawStore.$subscribe;
watch(
() => drawStore.selectedGraphic,
(val) => {
if (val && val.type == AxleCounting.Type) {
axleCountingModel.copyFrom(val.saveData() as AxleCountingData);
if (axleCountingModel.kilometerSystem) {
kilometerSystem.coordinateSystem =
axleCountingModel.kilometerSystem.coordinateSystem;
kilometerSystem.kilometer = axleCountingModel.kilometerSystem.kilometer;
}
}
}
);
onMounted(() => {
const axleCounting = drawStore.selectedGraphic as AxleCounting;
if (axleCounting) {
axleCountingModel.copyFrom(axleCounting.saveData());
if (axleCountingModel.kilometerSystem) {
kilometerSystem.coordinateSystem =
axleCountingModel.kilometerSystem.coordinateSystem;
kilometerSystem.kilometer = axleCountingModel.kilometerSystem.kilometer;
}
}
});
function onUpdate() {
const axleCounting = drawStore.selectedGraphic as AxleCounting;
axleCountingModel.kilometerSystem = {
coordinateSystem: kilometerSystem.coordinateSystem,
kilometer: kilometerSystem.kilometer,
};
if (axleCounting) {
drawStore
.getDrawApp()
.updateGraphicAndRecord(axleCounting, axleCountingModel);
}
}
const sectionRelations = computed(() => {
const axleCounting = drawStore.selectedGraphic as AxleCounting;
const sectionRelations =

View File

@ -1,126 +0,0 @@
<template>
<q-form class="q-gutter-sm">
<q-input
outlined
readonly
v-model="concentrationDividingLineModel.id"
label="id"
/>
<q-select
outlined
style="margin-top: 10px"
v-model="concentrationDividingLineModel.refLeftStationId"
:options="centralizedStations"
emitValue
mapOptions
@update:model-value="onUpdate"
label="左边关联的集中站"
/>
<q-select
outlined
style="margin-top: 10px"
v-model="concentrationDividingLineModel.refRightStationId"
:options="centralizedStations"
emitValue
mapOptions
@update:model-value="onUpdate"
label="右边关联的集中站"
/>
<q-toggle
v-model="
concentrationDividingLineModel.isOtherLineConcentrationDividingLine
"
label="是否与其它线的边界处"
emit-value
@update:model-value="onUpdate"
/>
<q-list bordered separator class="rounded-borders">
<q-item
v-for="sectionRelation in sectionRelations"
:key="sectionRelation.label"
>
<q-item-section no-wrap class="q-gutter-y-sm column">
<q-item-label> {{ sectionRelation.label }} </q-item-label>
<div class="q-gutter-sm row">
<q-chip
v-for="(item, index) in sectionRelation.refSectionInfo"
:key="index"
square
color="primary"
text-color="white"
>
{{ item }}
</q-chip>
</div>
</q-item-section>
</q-item>
</q-list>
</q-form>
</template>
<script setup lang="ts">
import { useFormData } from 'src/components/DrawAppFormUtils';
import { ConcentrationDividingLineData } from 'src/drawApp/graphics/ConcentrationDividingLineInteraction';
import { ConcentrationDividingLine } from 'src/graphics/concentrationDividingLine/ConcentrationDividingLine';
import { Section } from 'src/graphics/section/Section';
import { Station } from 'src/graphics/station/Station';
import { useDrawStore } from 'src/stores/draw-store';
import { computed, onMounted, ref } from 'vue';
const drawStore = useDrawStore();
const { data: concentrationDividingLineModel, onUpdate } = useFormData(
new ConcentrationDividingLineData(),
drawStore.getDrawApp()
);
const centralizedStations = ref<{ label: string; value: number }[]>([]);
const sectionRelations = computed(() => {
const refSectionInfo: { label: string; refSectionInfo: string[] }[] = [
{ label: '左边关联的设备', refSectionInfo: [] },
{ label: '右边关联的设备', refSectionInfo: [] },
];
enum devicePort {
'A',
'B',
'C',
}
const concentrationDividingLine =
drawStore.selectedGraphic as ConcentrationDividingLine;
concentrationDividingLine.datas.nodeConWithSecs.forEach((nodeConWithSec) => {
const refleftSection = nodeConWithSec.leftSection?.id
? `${
drawStore
.getDrawApp()
.queryStore.queryById<Section>(nodeConWithSec.leftSection.id).datas
.code
}(${devicePort[nodeConWithSec.leftSection.devicePort]})`
: '边界';
refSectionInfo[0].refSectionInfo.push(refleftSection);
const refRightSection = nodeConWithSec.rightSection?.id
? `${
drawStore
.getDrawApp()
.queryStore.queryById<Section>(nodeConWithSec.rightSection.id).datas
.code
}(${devicePort[nodeConWithSec.rightSection.devicePort]})`
: '边界';
refSectionInfo[1].refSectionInfo.push(refRightSection);
});
return refSectionInfo;
});
onMounted(() => {
const stations = drawStore
.getDrawApp()
.queryStore.queryByType<Station>(Station.Type);
centralizedStations.value = [{ label: '', value: 0 }];
stations.forEach((station) => {
if (station.datas.concentrationStations || station.datas.depots) {
centralizedStations.value.push({
label: station.datas.name,
value: station.datas.id,
});
}
});
});
</script>

View File

@ -59,13 +59,38 @@
</template>
<script setup lang="ts">
import { useFormData } from 'src/components/DrawAppFormUtils';
import { LinkData } from 'src/drawApp/graphics/LinkInteraction';
import { Link } from 'src/graphics/link/Link';
import { useDrawStore } from 'src/stores/draw-store';
import { onMounted, reactive, watch } from 'vue';
const drawStore = useDrawStore();
const { data: linkModel, onUpdate } = useFormData(
new LinkData(),
drawStore.getDrawApp()
const linkModel = reactive(new LinkData());
drawStore.$subscribe;
watch(
() => drawStore.selectedGraphic,
(val) => {
if (val && val.type == Link.Type) {
// console.log('link');
linkModel.copyFrom(val.saveData() as LinkData);
}
}
);
onMounted(() => {
// console.log('link mounted');
const link = drawStore.selectedGraphic as Link;
if (link) {
linkModel.copyFrom(link.saveData());
}
});
function onUpdate() {
console.log('link 属性更新');
const link = drawStore.selectedGraphic as Link;
if (link) {
drawStore.getDrawApp().updateGraphicAndRecord(link, linkModel);
}
}
</script>

View File

@ -1,15 +1,10 @@
<template>
<q-form>
<q-input outlined readonly v-model="sectionModel.id" label="id" hint="" />
<q-input
outlined
readonly
v-model="logicSectionModel.id"
label="id"
hint=""
/>
<q-input
outlined
v-model="logicSectionModel.code"
v-model="sectionModel.code"
@blur="onUpdate"
label="编号"
/>
@ -17,13 +12,26 @@
</template>
<script setup lang="ts">
import { useFormData } from 'src/components/DrawAppFormUtils';
import { LogicSectionData } from 'src/drawApp/graphics/LogicSectionInteraction';
import { LogicSection } from 'src/graphics/logicSection/LogicSection';
import { useDrawStore } from 'src/stores/draw-store';
import { shallowRef, watchEffect } from 'vue';
const drawStore = useDrawStore();
const { data: logicSectionModel, onUpdate } = useFormData(
new LogicSectionData(),
drawStore.getDrawApp()
);
const sectionModel = shallowRef(new LogicSectionData());
watchEffect(() => {
const section = drawStore.selectedGraphic;
if (section && section instanceof LogicSection) {
sectionModel.value = section.saveData();
}
});
const onUpdate = () => {
const section = drawStore.selectedGraphic as LogicSection;
if (section) {
drawStore.getDrawApp().updateGraphicAndRecord(section, sectionModel.value);
}
};
</script>

View File

@ -52,18 +52,25 @@
</q-form>
</template>
<script setup lang="ts">
import { useFormData } from 'src/components/DrawAppFormUtils';
import { PathLineData } from 'src/drawApp/graphics/PathLineInteraction';
import { PathLine } from 'src/graphics/pathLine/PathLine';
import { useDrawStore } from 'src/stores/draw-store';
import { reactive, onMounted } from 'vue';
import { reactive, onMounted, watch } from 'vue';
import { getLineList } from 'src/api/LineInfoApi';
const drawStore = useDrawStore();
const { data: pathLineModel, onUpdate } = useFormData(
new PathLineData(),
drawStore.getDrawApp()
);
const pathLineModel = reactive(new PathLineData());
const lineList: { label: string; value: string }[] = reactive([]);
drawStore.$subscribe;
watch(
() => drawStore.selectedGraphic,
(val) => {
if (val && val.type == PathLine.Type) {
pathLineModel.copyFrom(val.saveData() as PathLineData);
}
}
);
onMounted(() => {
getLineList()
.then((res) => {
@ -74,5 +81,16 @@ onMounted(() => {
.catch((err) => {
console.error('获取线路列表失败:' + err.message);
});
const pathLine = drawStore.selectedGraphic as PathLine;
if (pathLine) {
pathLineModel.copyFrom(pathLine.saveData());
}
});
function onUpdate() {
const pathLine = drawStore.selectedGraphic as PathLine;
if (pathLine) {
drawStore.getDrawApp().updateGraphicAndRecord(pathLine, pathLineModel);
}
}
</script>

View File

@ -1,6 +1,6 @@
<template>
<q-form class="q-gutter-sm">
<q-input outlined readonly v-model="platformModel.id" label="id" />
<q-input outlined readonly v-model="platformModel.id" label="id" hint="" />
<q-input
outlined
label="站台名称"
@ -10,30 +10,26 @@
lazy-rules
autogrow
/>
<q-toggle
v-model="platformModel.hasdoor"
label="是否有屏蔽门"
emit-value
@update:model-value="onUpdate"
/>
<q-select
v-if="platformModel.hasdoor"
outlined
@blur="onUpdate"
v-model="platformModel.direction"
v-model="hasDoor"
:options="optionsDoor"
label="是否有屏蔽门"
/>
<q-select
outlined
@blur="onUpdate"
v-model="direction"
:options="optionsDirection"
label="方向"
map-options
emit-value
/>
<q-select
outlined
@blur="onUpdate"
v-model="platformModel.up"
v-model="upAndDown"
:options="optionsUpAndDown"
label="上下行"
map-options
emit-value
/>
<q-list bordered separator class="rounded-borders">
<q-item>
@ -46,85 +42,89 @@
</div>
</q-item-section>
</q-item>
<q-item>
<q-item-section no-wrap class="q-gutter-y-sm column">
<q-item-label> 关联的物理区段 </q-item-label>
<div class="q-gutter-sm row">
<q-chip square color="primary" text-color="white">
{{ sectionName }}
</q-chip>
</div>
</q-item-section>
</q-item>
</q-list>
<q-select
outlined
v-model="platformModel.centralizedStation"
:options="centralizedStations"
emitValue
mapOptions
@update:model-value="onUpdate"
label="关联的集中站"
/>
</q-form>
</template>
<script setup lang="ts">
import { useFormData } from 'src/components/DrawAppFormUtils';
import { PlatformData } from 'src/drawApp/graphics/PlatformInteraction';
import { Platform } from 'src/graphics/platform/Platform';
import { Section } from 'src/graphics/section/Section';
import { Station } from 'src/graphics/station/Station';
import { useDrawStore } from 'src/stores/draw-store';
import { computed, onMounted, ref } from 'vue';
import { onMounted, reactive, ref, watch } from 'vue';
const drawStore = useDrawStore();
const { data: platformModel, onUpdate } = useFormData(
new PlatformData(),
drawStore.getDrawApp()
const platformModel = reactive(new PlatformData());
const hasDoor = ref('是');
const optionsDoor = ['是', '否'];
const direction = ref('向上');
const upAndDown = ref('');
const optionsDirection = ['向上', '向下'];
const optionsUpAndDown = ['上行', '下行'];
const stationName = ref('');
enum showSelect {
= 'true',
= 'false',
向上 = 'up',
向下 = 'down',
}
enum showUp {
上行 = 'true',
下行 = 'false',
}
enum showSelectData {
true = '是',
false = '否',
up = '向上',
down = '向下',
}
enum showUpData {
true = '上行',
false = '下行',
}
drawStore.$subscribe;
watch(
() => drawStore.selectedGraphic,
(val) => {
if (val && val.type == Platform.Type) {
platformModel.copyFrom(val.saveData() as PlatformData);
hasDoor.value = (showSelectData as never)[platformModel.hasdoor + ''];
direction.value = (showSelectData as never)[platformModel.direction];
upAndDown.value = (showUpData as never)[platformModel.up + ''];
if (platformModel.refStation) {
const refStation = val.queryStore.queryById<Station>(
platformModel.refStation
) as Station;
stationName.value = refStation.datas.name;
}
}
}
);
const stationName = computed(() => {
const platform = drawStore.selectedGraphic as Platform;
if (platformModel.refStation) {
const refStation = platform.queryStore.queryById<Station>(
platformModel.refStation
);
return refStation.datas.name;
}
return '';
});
const sectionName = computed(() => {
const platform = drawStore.selectedGraphic as Platform;
if (platformModel.refSectionId) {
const refSection = platform.queryStore.queryById<Section>(
platformModel.refSectionId
);
return refSection.datas.code;
}
return '';
});
const optionsDirection = [
{ label: '向上', value: 'up' },
{ label: '向下', value: 'down' },
];
const optionsUpAndDown = [
{ label: '上行', value: true },
{ label: '下行', value: false },
];
const centralizedStations = ref<{ label: string; value: number }[]>([]);
onMounted(() => {
const stations = drawStore
.getDrawApp()
.queryStore.queryByType<Station>(Station.Type);
centralizedStations.value = [{ label: '', value: 0 }];
stations.forEach((station) => {
if (station.datas.concentrationStations || station.datas.depots) {
centralizedStations.value.push({
label: station.datas.name,
value: station.datas.id,
});
const platform = drawStore.selectedGraphic as Platform;
if (platform) {
platformModel.copyFrom(platform.saveData());
hasDoor.value = (showSelectData as never)[platformModel.hasdoor + ''];
direction.value = (showSelectData as never)[platformModel.direction];
upAndDown.value = (showUpData as never)[platformModel.up + ''];
if (platformModel.refStation) {
const refStation = platform.queryStore.queryById<Station>(
platformModel.refStation
) as Station;
stationName.value = refStation.datas.name;
}
});
}
});
function onUpdate() {
platformModel.hasdoor = JSON.parse((showSelect as never)[hasDoor.value]);
platformModel.direction = (showSelect as never)[direction.value];
platformModel.up = JSON.parse((showUp as never)[upAndDown.value]);
const platform = drawStore.selectedGraphic as Platform;
if (platform) {
drawStore.getDrawApp().updateGraphicAndRecord(platform, platformModel);
}
}
</script>

View File

@ -66,13 +66,35 @@
</template>
<script setup lang="ts">
import { useFormData } from 'src/components/DrawAppFormUtils';
import { RectData } from 'src/drawApp/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 { data: rectModel, onUpdate } = useFormData(
new RectData(),
drawStore.getDrawApp()
const rectModel = reactive(new RectData());
drawStore.$subscribe;
watch(
() => drawStore.selectedGraphic,
(val) => {
if (val && val.type == Rect.Type) {
rectModel.copyFrom(val.saveData() as RectData);
}
}
);
onMounted(() => {
const Rect = drawStore.selectedGraphic as Rect;
if (Rect) {
rectModel.copyFrom(Rect.saveData());
}
});
function onUpdate() {
const Rect = drawStore.selectedGraphic as Rect;
if (Rect) {
drawStore.getDrawApp().updateGraphicAndRecord(Rect, rectModel);
}
}
</script>

View File

@ -68,30 +68,6 @@
</q-icon>
</template>
</q-input>
<q-input
outlined
v-model="runLineModel.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="runLineModel.lineColor"
@change="
(val) => {
runLineModel.lineColor = val;
onUpdate();
}
"
/>
</q-popup-proxy>
</q-icon>
</template>
</q-input>
<template :key="item" v-for="(item, index) in runLineModel.points">
<div style="display: flex; margin-top: 5px">
<q-input
@ -142,11 +118,10 @@
</template>
<script setup lang="ts">
import { useFormData } from 'src/components/DrawAppFormUtils';
import { RunLineData } from 'src/drawApp/graphics/RunLineInteraction';
import { RunLine } from 'src/graphics/runLine/RunLine';
import { useDrawStore } from 'src/stores/draw-store';
import { onMounted, reactive } from 'vue';
import { onMounted, reactive, watch, ref } from 'vue';
import { Point } from 'pixi.js';
import {
IStationLineData,
@ -155,13 +130,20 @@ import {
import { getLineList } from 'src/api/LineInfoApi';
const drawStore = useDrawStore();
const { data: runLineModel, onUpdate } = useFormData(
new RunLineData(),
useDrawStore().getDrawApp()
);
const runLineModel = reactive(new RunLineData());
const stationLines: IStationLineData[] = reactive([]);
const lineList: { label: string; value: string }[] = reactive([]);
drawStore.$subscribe;
watch(
() => drawStore.selectedGraphic,
(val) => {
if (val && val.type == RunLine.Type) {
runLineModel.copyFrom(val.saveData() as RunLineData);
}
}
);
onMounted(() => {
getLineList()
.then((res) => {
@ -172,23 +154,31 @@ onMounted(() => {
.catch((err) => {
console.error('获取线路列表失败:' + err.message);
});
const runLine = drawStore.selectedGraphic as RunLine;
const stations = drawStore
.getDrawApp()
.queryStore.queryByType(StationLine.Type) as StationLine[];
stations.forEach((item) => stationLines.push(item.datas));
if (runLine) {
runLineModel.copyFrom(runLine.saveData());
}
});
function onUpdate() {
const runLine = drawStore.selectedGraphic as RunLine;
if (runLine) {
drawStore.getDrawApp().updateGraphicAndRecord(runLine, runLineModel);
}
}
function generatePathLine() {
const runLine = drawStore.selectedGraphic as RunLine;
if (runLine) {
const points = runLineModel.points;
const points1: Point[] = [];
runLineModel.points.forEach((p) =>
points1.push(runLine.localToCanvasPoint(new Point(p.x, p.y)))
);
points.forEach((p) => points1.push(new Point(p.x, p.y)));
runLine.generatePathLine(points1);
}
}
function generateContainSta() {
const runLine = drawStore.selectedGraphic as RunLine;
if (runLine) {

View File

@ -1,6 +1,6 @@
<template>
<q-form class="q-gutter-sm">
<q-input outlined readonly v-model="sectionModel.id" label="id" />
<q-form>
<q-input outlined readonly v-model="sectionModel.id" label="id" hint="" />
<q-input
outlined
v-model="sectionModel.code"
@ -9,17 +9,14 @@
/>
<q-input
outlined
class="q-mt-lg"
v-model="sectionModel.destinationCode"
@blur="onUpdate"
label="目的地码"
/>
<q-checkbox
v-model="sectionModel.turning"
label="是否转换轨"
@update:model-value="onUpdate"
></q-checkbox>
<q-field
v-if="!isTurnoutPhysicalSection"
class="q-mt-lg"
outlined
label="关联区段"
readonly
@ -38,6 +35,7 @@
</q-field>
<q-field
v-if="!isTurnoutPhysicalSection"
class="q-mt-lg"
outlined
label="关联道岔"
readonly
@ -66,33 +64,20 @@
>
</template>
</q-field>
<q-select
outlined
v-model="sectionModel.centralizedStation"
:options="centralizedStations"
emitValue
mapOptions
@update:model-value="onUpdate"
label="关联的集中站"
/>
</q-form>
</template>
<script setup lang="ts">
import { useFormData } from 'src/components/DrawAppFormUtils';
import { SectionData } from 'src/drawApp/graphics/SectionInteraction';
import { AxleCounting } from 'src/graphics/axleCounting/AxleCounting';
import { Section, SectionType } from 'src/graphics/section/Section';
import { Station } from 'src/graphics/station/Station';
import { Turnout } from 'src/graphics/turnout/Turnout';
import { useDrawStore } from 'src/stores/draw-store';
import { computed, onMounted, ref } from 'vue';
import { computed, shallowRef, watchEffect } from 'vue';
const drawStore = useDrawStore();
const { data: sectionModel, onUpdate } = useFormData(
new SectionData(),
drawStore.getDrawApp()
);
const sectionModel = shallowRef(new SectionData());
const sectionRelations = computed(() => {
const section = drawStore.selectedGraphic as Section;
@ -102,15 +87,12 @@ const sectionRelations = computed(() => {
section,
Section.Type
);
return sectionRelations.map((relation) => {
if (relation.getRelationParam(section).param) {
return `${relation.getRelationParam(section).param}: ${
return sectionRelations.map(
(relation) =>
`${relation.getRelationParam(section).param}: ${
relation.getOtherGraphic<Section>(section).datas.code
}(${relation.getOtherRelationParam(section).param})`;
} else {
return relation.getOtherGraphic<Section>(section).datas.code;
}
});
}(${relation.getOtherRelationParam(section).param})`
);
});
const turnoutRelations = computed(() => {
@ -147,21 +129,17 @@ const axleCountingRelations = computed(() => {
(relation) => relation.getOtherGraphic<AxleCounting>(section).datas.code
);
});
const centralizedStations = ref<{ label: string; value: number }[]>([]);
onMounted(() => {
const stations = drawStore
.getDrawApp()
.queryStore.queryByType<Station>(Station.Type);
centralizedStations.value = [{ label: '', value: 0 }];
stations.forEach((station) => {
if (station.datas.concentrationStations || station.datas.depots) {
centralizedStations.value.push({
label: station.datas.name,
value: station.datas.id,
});
}
});
watchEffect(() => {
const section = drawStore.selectedGraphic;
if (section && section instanceof Section) {
sectionModel.value = section.saveData();
}
});
const onUpdate = () => {
const section = drawStore.selectedGraphic as Section;
if (section) {
drawStore.getDrawApp().updateGraphicAndRecord(section, sectionModel.value);
}
};
</script>

View File

@ -16,16 +16,13 @@
</template>
<script setup lang="ts">
import { useFormData } from 'src/components/DrawAppFormUtils';
import { SeparatorData } from 'src/drawApp/graphics/SeparatorInteraction';
import { separatorTypeEnum } from 'src/graphics/separator/Separator';
import { Separator, separatorTypeEnum } from 'src/graphics/separator/Separator';
import { useDrawStore } from 'src/stores/draw-store';
import { onMounted, reactive, watch } from 'vue';
const drawStore = useDrawStore();
const { data: separatorModel, onUpdate } = useFormData(
new SeparatorData(),
drawStore.getDrawApp()
);
const separatorModel = reactive(new SeparatorData());
const typeOptions = [
{ label: '区段分隔符', value: separatorTypeEnum.section },
@ -33,4 +30,28 @@ const typeOptions = [
{ label: '左断路分隔符', value: separatorTypeEnum.endA },
{ label: '右断路分隔符', value: separatorTypeEnum.endB },
];
drawStore.$subscribe;
watch(
() => drawStore.selectedGraphic,
(val) => {
if (val && val.type == Separator.Type) {
separatorModel.copyFrom(val.saveData() as SeparatorData);
}
}
);
onMounted(() => {
const Separator = drawStore.selectedGraphic as Separator;
if (Separator) {
separatorModel.copyFrom(Separator.saveData());
}
});
function onUpdate() {
const Separator = drawStore.selectedGraphic as Separator;
if (Separator) {
drawStore.getDrawApp().updateGraphicAndRecord(Separator, separatorModel);
}
}
</script>

View File

@ -1,6 +1,6 @@
<template>
<q-form class="q-gutter-sm">
<q-input outlined readonly v-model="signalModel.id" label="id" />
<q-form>
<q-input outlined readonly v-model="signalModel.id" label="id" hint="" />
<q-input
outlined
v-model.number="signalModel.code"
@ -9,106 +9,34 @@
/>
<q-select
outlined
v-model="refDevData.deviceType"
:options="DeviceTypeOptions"
readonly
map-options
emit-value
label="关联设备类型:"
></q-select>
<q-input
outlined
v-model="refDevData.code"
:readonly="true"
label="关联设备:"
></q-input>
<q-select
outlined
v-if="refDevData.deviceType === graphicData.RelatedRef.DeviceType.Turnout"
v-model="refDevData.devicePort"
:options="DevicePortOptions"
:readonly="true"
style="margin-top: 10px"
v-model="kilometerSystem.coordinateSystem"
:options="CoordinateSystemOptions"
:map-options="true"
:emit-value="true"
@update:model-value="onUpdate"
label="关联设备端口:"
label="坐标系"
></q-select>
<q-list bordered separator class="rounded-borders">
<q-item no-wrap class="q-gutter-y-sm column">
<div>公里标配置</div>
<q-select
outlined
v-model="signalModel.kilometerSystem.coordinateSystem"
:options="CoordinateSystemOptions"
:map-options="true"
:emit-value="true"
@update:model-value="onUpdate"
label="坐标系"
></q-select>
<q-input
outlined
v-model.number="signalModel.kilometerSystem.kilometer"
type="number"
@blur="onUpdate"
label="公里标(mm):"
/>
</q-item>
</q-list>
<q-select
<q-input
outlined
v-model="signalModel.centralizedStation"
:options="centralizedStations"
emitValue
mapOptions
@update:model-value="onUpdate"
label="关联的集中站"
style="margin-top: 10px"
v-model.number="kilometerSystem.kilometer"
type="number"
@blur="onUpdate"
label="公里标(mm):"
/>
</q-form>
</template>
<script setup lang="ts">
import { useFormData } from 'src/components/DrawAppFormUtils';
import { SignalData } from 'src/drawApp/graphics/SignalInteraction';
import { Section } from 'src/graphics/section/Section';
import { Station } from 'src/graphics/station/Station';
import { Turnout } from 'src/graphics/turnout/Turnout';
import { graphicData } from 'src/protos/stationLayoutGraphics';
import { Signal } from 'src/graphics/signal/Signal';
import { useDrawStore } from 'src/stores/draw-store';
import { computed, onMounted, ref } from 'vue';
import { onMounted, reactive, watch } from 'vue';
const drawStore = useDrawStore();
const { data: signalModel, onUpdate } = useFormData(
new SignalData(),
drawStore.getDrawApp()
);
const refDevData = computed(() => {
return signalModel.refDevice
? {
...signalModel.refDevice.toObject(),
code: drawStore
.getDrawApp()
.queryStore.queryById<Section | Turnout>(signalModel.refDevice.id)
.datas.code,
}
: {
id: 0,
deviceType: graphicData.RelatedRef.DeviceType.Section,
devicePort: graphicData.RelatedRef.DevicePort.A,
code: '',
};
});
const DeviceTypeOptions = [
{ label: '区段', value: graphicData.RelatedRef.DeviceType.Section },
{ label: '道岔', value: graphicData.RelatedRef.DeviceType.Turnout },
];
const DevicePortOptions = [
{ label: 'A端', value: graphicData.RelatedRef.DevicePort.A },
{ label: 'B端', value: graphicData.RelatedRef.DevicePort.B },
{ label: 'C端', value: graphicData.RelatedRef.DevicePort.C },
];
const signalModel = reactive(new SignalData());
const kilometerSystem = reactive({ coordinateSystem: '', kilometer: 0 });
const CoordinateSystemOptions = [
{ label: '车辆段', value: 'DEPOT' },
@ -116,20 +44,42 @@ const CoordinateSystemOptions = [
{ label: '正线', value: 'MAIN_LINE' },
{ label: '换线', value: 'TRANSFER' },
];
const centralizedStations = ref<{ label: string; value: number }[]>([]);
drawStore.$subscribe;
watch(
() => drawStore.selectedGraphic,
(val) => {
if (val && val.type == Signal.Type) {
signalModel.copyFrom(val.saveData() as SignalData);
if (signalModel.kilometerSystem) {
kilometerSystem.coordinateSystem =
signalModel.kilometerSystem.coordinateSystem;
kilometerSystem.kilometer = signalModel.kilometerSystem.kilometer;
}
}
}
);
onMounted(() => {
const stations = drawStore
.getDrawApp()
.queryStore.queryByType<Station>(Station.Type);
centralizedStations.value = [{ label: '', value: 0 }];
stations.forEach((station) => {
if (station.datas.concentrationStations || station.datas.depots) {
centralizedStations.value.push({
label: station.datas.name,
value: station.datas.id,
});
const signal = drawStore.selectedGraphic as Signal;
if (signal) {
signalModel.copyFrom(signal.saveData());
if (signalModel.kilometerSystem) {
kilometerSystem.coordinateSystem =
signalModel.kilometerSystem.coordinateSystem;
kilometerSystem.kilometer = signalModel.kilometerSystem.kilometer;
}
});
}
});
function onUpdate() {
const signal = drawStore.selectedGraphic as Signal;
signalModel.kilometerSystem = {
coordinateSystem: kilometerSystem.coordinateSystem,
kilometer: kilometerSystem.kilometer,
};
if (signal) {
drawStore.getDrawApp().updateGraphicAndRecord(signal, signalModel);
}
}
</script>

View File

@ -1,5 +1,5 @@
<template>
<q-form class="q-gutter-sm">
<q-form>
<q-input
outlined
readonly
@ -24,42 +24,64 @@
<q-select
outlined
@blur="onUpdate"
v-model="stationLineModel.hasTransfer"
v-model="hasTransfer"
:options="optionsCircle"
label="是否有换乘"
map-options
emit-value
/>
<q-select
outlined
v-model="stationLineModel.color"
:options="colorOptions"
:map-options="true"
:emit-value="true"
@update:model-value="onUpdate"
label="站名颜色"
/>
</q-form>
</template>
<script setup lang="ts">
import { useFormData } from 'src/components/DrawAppFormUtils';
import { StationLineData } from 'src/drawApp/graphics/StationLineInteraction';
import { StationLine } from 'src/graphics/stationLine/StationLine';
import { useDrawStore } from 'src/stores/draw-store';
import { graphicData } from 'src/protos/stationLayoutGraphics';
import { onMounted, reactive, ref, watch } from 'vue';
const drawStore = useDrawStore();
const { data: stationLineModel, onUpdate } = useFormData(
new StationLineData(),
drawStore.getDrawApp()
);
const optionsCircle = [
{ label: '是', value: true },
{ label: '否', value: false },
];
const stationLineModel = reactive(new StationLineData());
const hasTransfer = ref('是');
const optionsCircle = ['是', '否'];
enum showSelect {
= 'true',
= 'false',
}
enum showSelectData {
true = '是',
false = '否',
}
const colorOptions = [
{ label: '橘黄色', value: graphicData.StationLine.stationColor.orange },
{ label: '灰色', value: graphicData.StationLine.stationColor.gray },
];
drawStore.$subscribe;
watch(
() => drawStore.selectedGraphic,
(val) => {
if (val && val.type == StationLine.Type) {
stationLineModel.copyFrom(val.saveData() as StationLineData);
hasTransfer.value = (showSelectData as never)[
stationLineModel.hasTransfer + ''
];
}
}
);
onMounted(() => {
const stationLine = drawStore.selectedGraphic as StationLine;
if (stationLine) {
stationLineModel.copyFrom(stationLine.saveData());
hasTransfer.value = (showSelectData as never)[
stationLineModel.hasTransfer + ''
];
}
});
function onUpdate() {
stationLineModel.hasTransfer = JSON.parse(
(showSelect as never)[hasTransfer.value]
);
const stationLine = drawStore.selectedGraphic as StationLine;
if (stationLine) {
drawStore
.getDrawApp()
.updateGraphicAndRecord(stationLine, stationLineModel);
}
}
</script>

View File

@ -19,74 +19,61 @@
lazy-rules
autogrow
/>
<q-list bordered separator class="rounded-borders">
<q-item no-wrap class="q-gutter-y-sm column">
<div>公里标配置</div>
<q-select
outlined
style="margin-top: 10px"
v-model="stationModel.kilometerSystem.coordinateSystem"
:options="CoordinateSystemOptions"
:map-options="true"
:emit-value="true"
@update:model-value="onUpdate"
label="坐标系"
></q-select>
<q-input
outlined
style="margin-top: 10px"
v-model.number="stationModel.kilometerSystem.kilometer"
type="number"
@blur="onUpdate"
label="公里标(mm):"
/>
</q-item>
</q-list>
<q-select
v-if="stationModel.concentrationStations"
outlined
v-model="stationModel.manageStations"
label="集中站管理的车站"
multiple
:options="optionsStations"
map-options
emit-value
style="margin-top: 10px"
v-model="kilometerSystem.coordinateSystem"
:options="CoordinateSystemOptions"
:map-options="true"
:emit-value="true"
@update:model-value="onUpdate"
label="坐标系"
></q-select>
<q-input
outlined
style="margin-top: 10px"
v-model.number="kilometerSystem.kilometer"
type="number"
@blur="onUpdate"
label="公里标(mm):"
/>
<q-toggle
v-model="stationModel.hasControl"
<q-select
outlined
@blur="onUpdate"
v-model="hasControl"
:options="optionsControl"
label="是否有控制"
emit-value
@update:model-value="onUpdate"
/>
<q-toggle
v-model="stationModel.concentrationStations"
<q-select
outlined
@blur="onUpdate"
v-model="concentrationStations"
:options="optionsControl"
label="是否集中站"
emit-value
@update:model-value="onUpdate"
/>
<q-toggle
v-model="stationModel.depots"
label="是否车辆段"
emit-value
@update:model-value="onUpdate"
/>
</q-form>
</template>
<script setup lang="ts">
import { useFormData } from 'src/components/DrawAppFormUtils';
import { StationData } from 'src/drawApp/graphics/StationInteraction';
import { Station } from 'src/graphics/station/Station';
import { useDrawStore } from 'src/stores/draw-store';
import { onMounted, watchEffect } from 'vue';
import { onMounted, reactive, ref, watch } from 'vue';
const drawStore = useDrawStore();
const { data: stationModel, onUpdate } = useFormData(
new StationData(),
drawStore.getDrawApp()
);
let optionsStations: { label: string; value: number }[] = [];
const stationModel = reactive(new StationData());
const hasControl = ref('是');
const concentrationStations = ref('否');
const optionsControl = ['是', '否'];
enum showSelect {
= 'true',
= 'false',
}
enum showSelectData {
true = '是',
false = '否',
}
const kilometerSystem = reactive({ coordinateSystem: '', kilometer: 0 });
const CoordinateSystemOptions = [
{ label: '车辆段', value: 'DEPOT' },
@ -95,22 +82,55 @@ const CoordinateSystemOptions = [
{ label: '换线', value: 'TRANSFER' },
];
watchEffect(() => {
if (
stationModel.concentrationStations &&
!stationModel.manageStations.includes(stationModel.id)
) {
stationModel.manageStations.push(stationModel.id);
onUpdate();
drawStore.$subscribe;
watch(
() => drawStore.selectedGraphic,
(val) => {
if (val && val.type == Station.Type) {
stationModel.copyFrom(val.saveData() as StationData);
hasControl.value = (showSelectData as never)[
stationModel.hasControl + ''
];
concentrationStations.value = (showSelectData as never)[
stationModel.concentrationStations + ''
];
if (stationModel.kilometerSystem) {
kilometerSystem.coordinateSystem =
stationModel.kilometerSystem.coordinateSystem;
kilometerSystem.kilometer = stationModel.kilometerSystem.kilometer;
}
}
}
);
onMounted(() => {
const station = drawStore.selectedGraphic as Station;
if (station) {
stationModel.copyFrom(station.saveData());
hasControl.value = (showSelectData as never)[stationModel.hasControl + ''];
concentrationStations.value = (showSelectData as never)[
stationModel.concentrationStations + ''
];
if (stationModel.kilometerSystem) {
kilometerSystem.coordinateSystem =
stationModel.kilometerSystem.coordinateSystem;
kilometerSystem.kilometer = stationModel.kilometerSystem.kilometer;
}
}
});
onMounted(() => {
optionsStations = drawStore
.getDrawApp()
.queryStore.queryByType<Station>(Station.Type)
.map((g) => {
return { label: g.datas.name, value: g.datas.id };
});
});
function onUpdate() {
stationModel.hasControl = JSON.parse((showSelect as never)[hasControl.value]);
stationModel.concentrationStations = JSON.parse(
(showSelect as never)[concentrationStations.value]
);
stationModel.kilometerSystem = {
coordinateSystem: kilometerSystem.coordinateSystem,
kilometer: kilometerSystem.kilometer,
};
const station = drawStore.selectedGraphic as Station;
if (station) {
drawStore.getDrawApp().updateGraphicAndRecord(station, stationModel);
}
}
</script>

View File

@ -0,0 +1,84 @@
<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-select
outlined
@blur="onUpdate"
v-model="hasBorder"
:options="optionsDoor"
label="是否有边框"
/>
<q-select
outlined
@blur="onUpdate"
v-model="trainDirection"
:options="optionsDirection"
label="行驶方向"
/>
</q-form>
</template>
<script setup lang="ts">
import { TrainData } from 'src/drawApp/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>

View File

@ -36,7 +36,7 @@
</q-item>
<q-item>
<q-item-section no-wrap class="q-gutter-y-sm column">
<q-item-label> 关联的道岔物理区段 </q-item-label>
<q-item-label> 关联的道岔 </q-item-label>
<div class="q-gutter-sm row">
<q-chip
v-for="item in relatedTurnout"
@ -55,20 +55,42 @@
</template>
<script setup lang="ts">
import { useFormData } from 'src/components/DrawAppFormUtils';
import { TrainWindowData } from 'src/drawApp/graphics/TrainWindowInteraction';
import { LogicSection } from 'src/graphics/logicSection/LogicSection';
import { Section } from 'src/graphics/section/Section';
import { TrainWindow } from 'src/graphics/trainWindow/TrainWindow';
import { Turnout } from 'src/graphics/turnout/Turnout';
import { useDrawStore } from 'src/stores/draw-store';
import { computed } from 'vue';
import { computed, onMounted, reactive, watch } from 'vue';
const drawStore = useDrawStore();
const { data: trainWindowModel, onUpdate } = useFormData(
new TrainWindowData(),
drawStore.getDrawApp()
const trainWindowModel = reactive(new TrainWindowData());
drawStore.$subscribe;
watch(
() => drawStore.selectedGraphic,
(val) => {
if (val && val.type == TrainWindow.Type) {
trainWindowModel.copyFrom(val.saveData() as TrainWindowData);
}
}
);
onMounted(() => {
const trainWindow = drawStore.selectedGraphic as TrainWindow;
if (trainWindow) {
trainWindowModel.copyFrom(trainWindow.saveData());
}
});
function onUpdate() {
const trainWindow = drawStore.selectedGraphic as TrainWindow;
if (trainWindow) {
drawStore
.getDrawApp()
.updateGraphicAndRecord(trainWindow, trainWindowModel);
}
}
const relatedLogicSection = computed((): LogicSection[] => {
if (
drawStore.selectedGraphic &&
@ -78,14 +100,12 @@ const relatedLogicSection = computed((): LogicSection[] => {
}
return [];
});
const relatedTurnout = computed((): Section[] => {
const relatedTurnout = computed((): Turnout[] => {
if (
drawStore.selectedGraphic &&
drawStore.selectedGraphic.type === 'TrainWindow'
) {
return (
drawStore.selectedGraphic as TrainWindow
).getRelatedTurnoutsSection();
return (drawStore.selectedGraphic as TrainWindow).getRelatedTurnouts();
}
return [];
});

View File

@ -1,52 +1,48 @@
<template>
<q-form class="q-gutter-sm">
<q-input outlined readonly v-model="turnoutModel.id" label="id" />
<q-form>
<q-input outlined readonly v-model="turnoutModel.id" label="id" hint="" />
<q-input
outlined
v-model="turnoutModel.code"
@blur="onUpdate"
label="编号"
/>
<q-list bordered separator class="rounded-borders">
<q-item no-wrap class="q-gutter-y-sm column">
<div>公里标配置</div>
<q-select
outlined
v-model="turnoutModel.kilometerSystem[0].coordinateSystem"
:options="CoordinateSystemOptions"
:map-options="true"
:emit-value="true"
@update:model-value="onUpdate"
label="坐标系"
></q-select>
<q-input
outlined
v-model.number="turnoutModel.kilometerSystem[0].kilometer"
type="number"
@blur="onUpdate"
label="公里标(mm):"
/>
</q-item>
<q-item no-wrap class="q-gutter-y-sm column">
<div>公里标配置</div>
<q-select
outlined
v-model="turnoutModel.kilometerSystem[1].coordinateSystem"
:options="CoordinateSystemOptions"
:map-options="true"
:emit-value="true"
@update:model-value="onUpdate"
label="坐标系2"
></q-select>
<q-input
outlined
v-model.number="turnoutModel.kilometerSystem[1].kilometer"
type="number"
@blur="onUpdate"
label="公里标(mm):"
/>
</q-item>
</q-list>
<q-select
outlined
style="margin-top: 10px"
v-model="kilometerSystem[0].coordinateSystem"
:options="CoordinateSystemOptions"
:map-options="true"
:emit-value="true"
@update:model-value="onUpdate"
label="坐标系"
></q-select>
<q-input
outlined
style="margin-top: 10px"
v-model.number="kilometerSystem[0].kilometer"
type="number"
@blur="onUpdate"
label="公里标(mm):"
/>
<q-select
outlined
style="margin-top: 10px"
v-model="kilometerSystem[1].coordinateSystem"
:options="CoordinateSystemOptions"
:map-options="true"
:emit-value="true"
@update:model-value="onUpdate"
label="坐标系2"
></q-select>
<q-input
outlined
style="margin-top: 10px"
v-model.number="kilometerSystem[1].kilometer"
type="number"
@blur="onUpdate"
label="公里标(mm):"
/>
<q-field class="q-mt-lg" outlined label="关联区段" readonly stack-label>
<template #control>
<q-chip
@ -71,38 +67,28 @@
>
</template>
</q-field>
<q-select
outlined
v-model="turnoutModel.centralizedStation"
:options="centralizedStations"
emitValue
mapOptions
@update:model-value="onUpdate"
label="关联的集中站"
/>
</q-form>
</template>
<script setup lang="ts">
import { useFormData } from 'src/components/DrawAppFormUtils';
import { TurnoutData } from 'src/drawApp/graphics/TurnoutInteraction';
import { Section } from 'src/graphics/section/Section';
import { Station } from 'src/graphics/station/Station';
import { Turnout } from 'src/graphics/turnout/Turnout';
import { useDrawStore } from 'src/stores/draw-store';
import { computed, onMounted, ref } from 'vue';
import { computed, reactive, shallowRef, watchEffect } from 'vue';
const drawStore = useDrawStore();
const { data: turnoutModel, onUpdate } = useFormData(
new TurnoutData(),
drawStore.getDrawApp()
);
const CoordinateSystemOptions = [
{ label: '车辆段', value: 'DEPOT' },
{ label: '停车场', value: 'PARKING_LOT' },
{ label: '正线', value: 'MAIN_LINE' },
{ label: '换线', value: 'TRANSFER' },
];
const turnoutModel = shallowRef(new TurnoutData());
const kilometerSystem = reactive([
{ coordinateSystem: '', kilometer: 0 },
{ coordinateSystem: '', kilometer: 0 },
]);
const sectionRelations = computed(() => {
const turnout = drawStore.selectedGraphic as Turnout;
@ -112,15 +98,12 @@ const sectionRelations = computed(() => {
turnout,
Section.Type
);
return sectionRelations.map((relation) => {
if (relation.getRelationParam(turnout).param) {
return `${relation.getRelationParam(turnout).param}: ${
return sectionRelations.map(
(relation) =>
`${relation.getRelationParam(turnout).param}: ${
relation.getOtherGraphic<Section>(turnout).datas.code
}(${relation.getOtherRelationParam(turnout).param})`;
} else {
return relation.getOtherGraphic<Section>(turnout).datas.code;
}
});
}(${relation.getOtherRelationParam(turnout).param})`
);
});
const turnoutRelations = computed(() => {
@ -139,20 +122,28 @@ const turnoutRelations = computed(() => {
);
});
const centralizedStations = ref<{ label: string; value: number }[]>([]);
onMounted(() => {
const stations = drawStore
.getDrawApp()
.queryStore.queryByType<Station>(Station.Type);
centralizedStations.value = [{ label: '', value: 0 }];
stations.forEach((station) => {
if (station.datas.concentrationStations || station.datas.depots) {
centralizedStations.value.push({
label: station.datas.name,
value: station.datas.id,
watchEffect(() => {
const turnout = drawStore.selectedGraphic;
if (turnout && turnout instanceof Turnout) {
turnoutModel.value = turnout.saveData();
if (turnoutModel.value.kilometerSystem.length > 0) {
kilometerSystem.forEach((ks, i) => {
ks.coordinateSystem =
turnoutModel.value.kilometerSystem[i].coordinateSystem;
ks.kilometer = turnoutModel.value.kilometerSystem[i].kilometer;
});
}
});
}
});
const onUpdate = () => {
const turnout = drawStore.selectedGraphic as Turnout;
turnoutModel.value.kilometerSystem = kilometerSystem.map((ks) => ({
coordinateSystem: ks.coordinateSystem,
kilometer: ks.kilometer,
}));
if (turnout) {
drawStore.getDrawApp().updateGraphicAndRecord(turnout, turnoutModel.value);
}
};
</script>

View File

@ -1,302 +0,0 @@
<template>
<div v-if="showRangeConfig">
<q-card class="q-gutter-sm q-pa-sm">
<q-card-section>
<div class="text-h6">{{ handleState }}</div>
</q-card-section>
<q-separator inset></q-separator>
<q-form ref="myForm" @submit="onSubmit" @reset="onReset">
<q-input
outlined
label="名称"
v-model="rangeConfig.areaName"
:rules="[(val) => val.trim() != '' || '名称不能为空']"
/>
<q-select
outlined
v-model="rangeConfig.deviceType"
:options="optionsType"
label="设备类型"
:map-options="true"
:emit-value="true"
:rules="[(val) => val.trim() != '' || '设备类型不能为空']"
/>
<q-select
outlined
v-model="rangeConfig.alertTypes"
label="故障类型"
multiple
:options="optionsAlertType"
:rules="[(val) => val.length > 0 || '故障类型不能为空']"
/>
<q-list bordered separator class="rounded-borders">
<q-item>
<q-item-section no-wrap class="q-gutter-y-sm column">
<q-item-label> 框选的设备 </q-item-label>
<div class="q-gutter-sm row">
<q-chip
v-for="item in device"
:key="item"
square
color="primary"
text-color="white"
removable
@remove="removeSelect(item)"
>
{{ item }}
</q-chip>
</div>
<q-btn
v-show="device.length > 0"
style="width: 120px"
label="清空框选的设备"
color="red"
@click="clearSelect"
/>
</q-item-section>
</q-item>
</q-list>
<div class="q-gutter-sm q-pa-md row justify-center">
<q-btn label="提交" type="submit" color="primary" class="q-mr-md" />
<q-btn label="重置" type="reset" color="primary" />
</div>
</q-form>
</q-card>
</div>
</template>
<script setup lang="ts">
// import { useLineStore } from 'src/stores/line-store';
import { reactive, ref, watch } from 'vue';
import { Turnout } from 'src/graphics/turnout/Turnout';
import { Station } from 'src/graphics/station/Station';
import { Platform } from 'src/graphics/platform/Platform';
import { QForm, useQuasar } from 'quasar';
import { useRoute } from 'vue-router';
import {
deviceRangeSet,
IAreaConfigItem,
queryDeviceRangeById,
} from 'src/api/ConfigApi';
import { JlGraphic } from 'jl-graphic';
import { saveAlertTypeData, showAlertTypeData } from '../alarm/alarmInfoEnum';
import { Section } from 'src/graphics/section/Section';
import { useRangeConfigStore } from 'src/stores/range-config-store';
import { getRangeConfigApp } from 'src/drawApp/rangeConfigApp';
import { errorNotify } from 'src/utils/CommonNotify';
defineExpose({ searchById });
const route = useRoute();
const rangeConfigStore = useRangeConfigStore();
const $q = useQuasar();
const showRangeConfig = ref(true);
const rangeConfig = reactive<{
areaName: string;
deviceType: `${DeviceType}` | '';
device: number[];
alertTypes: string[];
}>({
areaName: '',
deviceType: '',
device: [],
alertTypes: [],
});
const device = ref<string[]>([]);
const handleState = ref('新建范围配置');
const optionsType = [
{ label: '轨道', value: Section.Type },
{ label: '道岔', value: Turnout.Type },
{ label: '集中站', value: Station.Type },
{ label: '站台', value: Platform.Type },
];
const optionsAlertType = [
'蓝显',
'全线蓝显',
'列车延误2分钟',
'列车延误10分钟',
'整侧站台门无关闭锁紧信号',
'整侧站台门无法打开',
'整侧站台门无法关闭',
'道岔均失表',
'道岔定位失表',
'道岔反位失表',
'计轴红光带',
'计轴大面积红光带',
'计轴橙光带',
'计轴大面积橙光带',
'道岔大面积失表',
'列车信号故障',
'一级联锁',
];
enum DeviceType {
station = 'DEVICE_TYPE_RTU',
Turnout = 'DEVICE_TYPE_SWITCH',
Section = 'DEVICE_TYPE_TRACK',
Platform = 'DEVICE_TYPE_PLATFORM',
}
enum DeviceTypeShow {
DEVICE_TYPE_RTU = 'station',
DEVICE_TYPE_SWITCH = 'Turnout',
DEVICE_TYPE_TRACK = 'Section',
DEVICE_TYPE_PLATFORM = 'Platform',
}
watch(
() => rangeConfig.alertTypes,
(alertTypes) => {
if (alertTypes[0] == '一级联锁') {
alertTypes.splice(1);
}
if (
alertTypes[0] !== '一级联锁' &&
alertTypes.slice(1).includes('一级联锁')
) {
for (let i = 0; i < alertTypes.length; i++) {
if (alertTypes[i] == '一级联锁') {
alertTypes.splice(i, 1);
break;
}
}
}
}
);
let selectGraphic: JlGraphic[] = [];
watch(
() => rangeConfigStore.selectedGraphics,
(val) => {
if (val && val.length > 0) {
const deviceFilter = rangeConfigStore.selectedGraphics?.filter((g) => {
let select = false;
if (
g.type == Station.Type &&
rangeConfig.deviceType == Station.Type &&
(g as Station).datas.concentrationStations
) {
select = true;
}
if (g.type == rangeConfig.deviceType && g.type !== Station.Type) {
select = true;
}
return select;
}) as JlGraphic[];
selectGraphic.push(...deviceFilter);
selectGraphic = Array.from(new Set(selectGraphic));
getRangeConfigApp().updateSelected(...selectGraphic);
device.value = selectGraphic.map((g) => g.code) as string[];
rangeConfig.device = selectGraphic.map((g) => g.id) as number[];
}
}
);
const myForm = ref<QForm | null>(null);
let editId: number;
let handle = ref('');
let handleError = ref('');
async function onSubmit() {
myForm.value?.validate().then(async (res) => {
let validataOfDevice = false;
if (rangeConfig.device.length) {
validataOfDevice = true;
} else {
errorNotify('请框选设备', '');
}
if (res && validataOfDevice) {
try {
const lineId = +route.params.id as number;
const alertTypes = rangeConfig.alertTypes.map(
(type) => (saveAlertTypeData as never)[type + '']
);
const params: IAreaConfigItem = {
lineId: lineId,
areaName: rangeConfig.areaName,
deviceType: (DeviceType as never)[rangeConfig.deviceType + ''],
alertTypes: alertTypes,
data: rangeConfig.device,
};
if (handleState.value == '新建范围配置') {
handle.value = '创建成功';
handleError.value = '创建失败';
await deviceRangeSet(params);
} else {
params.id = editId;
handle.value = '更新成功';
handleError.value = '更新失败';
await deviceRangeSet(params);
}
$q.notify({
type: 'positive',
message: handle.value,
});
onReset();
showRangeConfig.value = false;
} catch (err) {
$q.notify({
type: 'negative',
message: (err as { title: '' }).title,
});
} finally {
setTimeout(() => {
showRangeConfig.value = true;
}, 0);
}
}
});
}
async function searchById(id: number) {
try {
handleState.value = '编辑范围配置';
clearSelect();
editId = id;
const response = await queryDeviceRangeById(id);
rangeConfig.areaName = response.data.areaName;
rangeConfig.deviceType = (DeviceTypeShow as never)[
response.data.deviceType + ''
];
rangeConfig.alertTypes = response.data.alertTypes.map(
(type) => (showAlertTypeData as never)[type + '']
);
const select: JlGraphic[] = [];
response.data.data.forEach((id: number) => {
const g = getRangeConfigApp().queryStore.queryById(id);
select.push(g);
device.value.push(g.code);
});
getRangeConfigApp().updateSelected(...select);
} catch (err) {
$q.notify({
type: 'negative',
message: '没有需要编辑的详细信息',
});
}
}
function removeSelect(code: string) {
const removeIndex = device.value.findIndex((item) => item == code);
selectGraphic.splice(removeIndex, 1);
device.value.splice(removeIndex, 1);
rangeConfig.device.splice(removeIndex, 1);
getRangeConfigApp().updateSelected(...selectGraphic);
}
function clearSelect() {
device.value = [];
selectGraphic = [];
getRangeConfigApp().updateSelected();
}
function onReset() {
handleState.value = '新建范围配置';
rangeConfig.areaName = '';
rangeConfig.deviceType = '';
rangeConfig.alertTypes = [];
rangeConfig.device = [];
clearSelect();
}
</script>

View File

@ -1,185 +0,0 @@
<script setup lang="ts">
import {
IAreaConfigListItem,
getDeviceAreaList,
deleteDeviceArea,
} from 'src/api/ConfigApi';
import { ref, watch } from 'vue';
import { useRoute } from 'vue-router';
import DraggableDialog from '../common/DraggableDialog.vue';
import { QTable, useQuasar } from 'quasar';
import { errorNotify } from 'src/utils/CommonNotify';
import { deviceTypeMap } from 'src/api/TrainApi';
const $q = useQuasar();
const lineId = useRoute().params.id as string;
const tableRef = ref<QTable>();
const columns: QTable['columns'] = [
{ name: 'id', label: 'ID', field: 'id', align: 'center' },
{ name: 'lineId', label: '线路ID', field: 'lineId', align: 'center' },
{
name: 'areaName',
label: '名称',
field: 'areaName',
align: 'center',
},
{
name: 'deviceType',
label: '设备类型',
field: (row) => deviceTypeMap[row.deviceType as keyof typeof deviceTypeMap],
align: 'center',
},
{ name: 'operations', label: '操作', field: 'operations', align: 'center' },
];
const rows = ref<IAreaConfigListItem[]>([]);
const loading = ref(false);
const pagination = ref({
sortBy: 'desc',
descending: false,
page: 1,
rowsPerPage: 10,
rowsNumber: 10,
});
const searchAreaName = ref('');
watch(
() => searchAreaName.value,
() => {
tableRef.value?.requestServerInteraction();
}
);
const onRequest: QTable['onRequest'] = async (props) => {
const { page, rowsPerPage } = props.pagination;
loading.value = true;
try {
const resp = await getDeviceAreaList({
lineId,
current: page,
size: rowsPerPage,
areaName: searchAreaName.value,
});
pagination.value.page = resp.current;
pagination.value.rowsNumber = resp.total;
pagination.value.rowsPerPage = resp.size;
rows.value = resp.records;
} catch (err) {
$q.notify({
type: 'negative',
message: '无法获取范围列表',
});
} finally {
loading.value = false;
}
};
const onDialogShow = () => {
tableRef.value?.requestServerInteraction();
};
const dialogRef = ref<InstanceType<typeof DraggableDialog>>();
const props = defineProps<{
onEditClick: (row: IAreaConfigListItem) => void;
}>();
function onEdit(row: IAreaConfigListItem) {
props.onEditClick(row);
}
function deleteData(row: IAreaConfigListItem) {
$q.dialog({ message: `确定删除 "${row.areaName}" 吗?`, cancel: true }).onOk(
async () => {
try {
await deleteDeviceArea(row.id);
} catch (err) {
errorNotify('删除失败:', err);
} finally {
tableRef.value?.requestServerInteraction();
}
}
);
}
</script>
<template>
<draggable-dialog
ref="dialogRef"
@show="onDialogShow"
title="范围列表"
:width="800"
:height="0"
>
<template v-slot:footer>
<q-table
ref="tableRef"
row-key="id"
v-model:pagination="pagination"
:loading="loading"
:rows="rows"
:columns="columns"
@request="onRequest"
:rows-per-page-options="[10, 20, 50, 100]"
>
<template v-slot:body-cell="props">
<q-td :props="props" class="custom-column">
{{ props.value }}
<q-tooltip
anchor="bottom middle"
v-if="props.value && props.value.length > 20"
>
<div class="message-tip">
{{ props.value }}
</div>
</q-tooltip>
</q-td>
</template>
<template v-slot:body-cell-operations="props">
<q-td :props="props">
<div class="q-gutter-sm row justify-center">
<q-btn color="primary" label="编辑" @click="onEdit(props.row)" />
<q-btn color="red" label="删除" @click="deleteData(props.row)" />
</div>
</q-td>
</template>
</q-table>
</template>
<template v-slot:titleButton>
<q-btn square color="purple" style="margin-right: 10px" icon="search">
<q-popup-edit
ref="popupEdit"
v-model="searchAreaName"
:cover="false"
:offset="[0, 10]"
v-slot="scope"
>
<q-input
color="accent"
v-model="scope.value"
label="区域名称"
dense
autofocus
@keyup.enter="scope.set"
>
<template v-slot:prepend>
<q-icon name="search" color="accent" />
</template>
</q-input>
</q-popup-edit>
</q-btn>
</template>
</draggable-dialog>
</template>
<style scoped>
.custom-column {
max-width: 250px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.message-tip {
width: 300px;
overflow: auto;
line-height: 22px;
white-space: pre-wrap;
font-size: 14px;
}
</style>

View File

@ -1,33 +0,0 @@
<template>
<!-- 画布或图形对象属性 -->
<div v-if="lineStore.selectedGraphics !== null">
<q-card flat>
<q-card-section>
<div class="text-h6">
{{ lineStore.selectedGraphicType + ' 状态属性' }}
</div>
</q-card-section>
<q-separator inset></q-separator>
<template v-if="lineStore.selectedGraphics.length === 1">
<q-card-section>
<platform-property
v-if="lineStore.selectedGraphicType === Platform.Type"
></platform-property>
<station-property
v-if="lineStore.selectedGraphicType === Station.Type"
></station-property>
</q-card-section>
</template>
</q-card>
</div>
</template>
<script setup lang="ts">
import { useLineStore } from 'src/stores/line-store';
import { Platform } from 'src/graphics/platform/Platform';
import PlatformProperty from './deviceStates/PlatformProperty.vue';
import { Station } from 'src/graphics/station/Station';
import StationProperty from './deviceStates/StationProperty.vue';
const lineStore = useLineStore();
</script>

View File

@ -1,247 +0,0 @@
<template>
<q-form class="q-gutter-sm">
<q-input
outlined
readonly
v-model="lineStore.selectedGraphic.id"
label="id"
hint=""
/>
<q-input
outlined
label="站台名称"
readonly
@blur="onUpdate"
v-model="lineStore.selectedGraphic.datas.code"
/>
<q-select
outlined
@blur="onUpdate"
v-model="psdOpen"
:options="optionsChoose"
label="屏蔽门打开"
/>
<q-select
outlined
@blur="onUpdate"
v-model="psdCut"
:options="optionsChoose"
label="屏蔽门切除"
/>
<q-select
outlined
@blur="onUpdate"
v-model="emergstop"
:options="optionsChoose"
label="是否紧急关闭"
/>
<q-select
outlined
@blur="onUpdate"
v-model="trainberth"
:options="optionsChoose"
label="是否在站台停站"
/>
<q-select
v-if="lineStore.selectedGraphic.datas.up"
outlined
@blur="onUpdate"
v-model="upHold"
:options="optionsChoose"
label="是否上行车站扣车"
/>
<q-select
v-if="lineStore.selectedGraphic.datas.up"
outlined
@blur="onUpdate"
v-model="upOccHold"
:options="optionsChoose"
label="是否上行中心扣车"
/>
<q-select
v-if="!lineStore.selectedGraphic.datas.up"
outlined
@blur="onUpdate"
v-model="downHold"
:options="optionsChoose"
label="是否下行车站扣车"
/>
<q-select
v-if="!lineStore.selectedGraphic.datas.up"
outlined
@blur="onUpdate"
v-model="downOccHold"
:options="optionsChoose"
label="是否下行中心扣车"
/>
<q-select
v-if="lineStore.selectedGraphic.datas.up"
outlined
@blur="onUpdate"
v-model="upSkipstop"
:options="optionsChoose"
label="是否上行跳停"
/>
<q-select
v-if="!lineStore.selectedGraphic.datas.up"
outlined
@blur="onUpdate"
v-model="downSkipstop"
:options="optionsChoose"
label="是否下行跳停"
/>
<q-input
outlined
label="人工设置区间运行等级"
type="textarea"
@blur="onUpdate"
v-model="platformModel.nextSectionRunTime"
lazy-rules
autogrow
/>
<q-input
outlined
label="人工设置停站时间"
type="textarea"
@blur="onUpdate"
v-model="platformModel.stopTime"
lazy-rules
autogrow
/>
</q-form>
</template>
<script setup lang="ts">
import { PlatformState } from 'src/drawApp/graphics/PlatformInteraction';
import { Platform } from 'src/graphics/platform/Platform';
import { useLineStore } from 'src/stores/line-store';
import { onMounted, reactive, ref, watch } from 'vue';
import { mockPlatformApi, mockPlatformParams } from 'src/api/PlatformApi';
const lineStore = useLineStore();
const platformModel = reactive(new PlatformState());
const optionsChoose = ['是', '否'];
const psdOpen = ref('');
const psdCut = ref('');
const emergstop = ref('');
const trainberth = ref('');
const upHold = ref('');
const upOccHold = ref('');
const upSkipstop = ref('');
const downHold = ref('');
const downOccHold = ref('');
const downSkipstop = ref('');
enum showSelect {
= 'true',
= 'false',
}
enum showSelectData {
true = '是',
false = '否',
}
lineStore.$subscribe;
watch(
() => lineStore.selectedGraphic,
(val) => {
if (val && val.type == Platform.Type) {
platformModel.copyFrom((val as Platform).states as PlatformState);
psdOpen.value = (showSelectData as never)[platformModel.psdOpen + ''];
psdCut.value = (showSelectData as never)[platformModel.psdCut + ''];
emergstop.value = (showSelectData as never)[platformModel.emergstop + ''];
trainberth.value = (showSelectData as never)[
platformModel.trainberth + ''
];
upHold.value = (showSelectData as never)[platformModel.upHold + ''];
upOccHold.value = (showSelectData as never)[platformModel.upOccHold + ''];
upSkipstop.value = (showSelectData as never)[
platformModel.upSkipstop + ''
];
downHold.value = (showSelectData as never)[platformModel.downHold + ''];
downOccHold.value = (showSelectData as never)[
platformModel.downOccHold + ''
];
downSkipstop.value = (showSelectData as never)[
platformModel.downSkipstop + ''
];
}
}
);
onMounted(() => {
const platform = lineStore.selectedGraphic as Platform;
if (platform) {
platformModel.copyFrom((platform as Platform).states as PlatformState);
psdOpen.value = (showSelectData as never)[platformModel.psdOpen + ''];
psdCut.value = (showSelectData as never)[platformModel.psdCut + ''];
emergstop.value = (showSelectData as never)[platformModel.emergstop + ''];
trainberth.value = (showSelectData as never)[platformModel.trainberth + ''];
upHold.value = (showSelectData as never)[platformModel.upHold + ''];
upOccHold.value = (showSelectData as never)[platformModel.upOccHold + ''];
upSkipstop.value = (showSelectData as never)[platformModel.upSkipstop + ''];
downHold.value = (showSelectData as never)[platformModel.downHold + ''];
downOccHold.value = (showSelectData as never)[
platformModel.downOccHold + ''
];
downSkipstop.value = (showSelectData as never)[
platformModel.upSkipstop + ''
];
}
});
function onUpdate() {
const platform = lineStore.selectedGraphic as Platform;
platformModel.psdOpen = JSON.parse((showSelect as never)[psdOpen.value]);
platformModel.psdCut = JSON.parse((showSelect as never)[psdCut.value]);
platformModel.emergstop = JSON.parse((showSelect as never)[emergstop.value]);
platformModel.trainberth = JSON.parse(
(showSelect as never)[trainberth.value]
);
platformModel.upHold = JSON.parse((showSelect as never)[upHold.value]);
platformModel.upOccHold = JSON.parse((showSelect as never)[upOccHold.value]);
platformModel.upSkipstop = JSON.parse(
(showSelect as never)[upSkipstop.value]
);
platformModel.downHold = JSON.parse((showSelect as never)[downHold.value]);
platformModel.downOccHold = JSON.parse(
(showSelect as never)[downOccHold.value]
);
platformModel.downSkipstop = JSON.parse(
(showSelect as never)[downSkipstop.value]
);
const data: { [key: string]: boolean | number | string } = {
emergstop: false,
trainberth: false,
close: false,
upHold: false,
upOccHold: false,
downOccHold: false,
psdOpen: false,
psdCut: false,
upSkipstop: false,
downSkipstop: false,
upTrainSkipstop: false,
downTrainSkipstop: false,
downHold: false,
id: platform.id,
nextSectionRunTime: 0,
nextSectionRunLevel: 0,
stopTime: 0,
};
type keyData = 'emergstop' | 'trainberth';
Object.keys(data).forEach((i) => {
data[i] = platformModel[i as keyData] || data[i];
});
if (platform) {
mockPlatformApi(3, data as unknown as mockPlatformParams)
.then((res) => {
console.log(res, '---res--');
})
.catch((err) => {
console.log(err, '---err---');
});
}
}
</script>

View File

@ -1,103 +0,0 @@
<template>
<q-form class="q-gutter-sm">
<q-input
outlined
readonly
v-model="lineStore.selectedGraphic.id"
label="id"
hint=""
/>
<q-input
outlined
label="车站名称"
readonly
@blur="onUpdate"
v-model="lineStore.selectedGraphic.datas.name"
/>
<q-select
outlined
@blur="onUpdate"
v-model="controlChange"
:options="optionsChoose"
label="车站控制模式转换"
/>
<q-checkbox
v-model="stationModel.ipRtuStusDown"
label="是否通信中断"
@update:model-value="onUpdate"
/>
<q-checkbox
v-model="stationModel.ipRtuStusInLocalCtrl"
label="是否站控"
@update:model-value="onUpdate"
/>
</q-form>
</template>
<script setup lang="ts">
import { StationState } from 'src/drawApp/graphics/StationInteraction';
import { Station } from 'src/graphics/station/Station';
import { useLineStore } from 'src/stores/line-store';
import { onMounted, reactive, ref, watch } from 'vue';
import { mockStationApi } from 'src/api/PlatformApi';
const lineStore = useLineStore();
const stationModel = reactive(new StationState());
const optionsChoose = ['中控', '站控且允许转到中控', '站控且不允许转到中控'];
const controlChange = ref('');
lineStore.$subscribe;
watch(
() => lineStore.selectedGraphic,
(val) => {
if (val && val.type == Station.Type) {
stationModel.copyFrom((val as Station).states as StationState);
}
}
);
onMounted(() => {
const station = lineStore.selectedGraphic as Station;
if (station) {
stationModel.copyFrom((station as Station).states as StationState);
}
});
function onUpdate() {
const station = lineStore.selectedGraphic as Station;
const data = {
ipRtuStusDown: false,
ipRtuStusInLocalCtrl: false,
ipRtuStusInCentralCtrl: false,
ipRtuStusInEmergencyCtrl: false,
id: station.id,
};
const lineId = lineStore.lineId as number;
data.ipRtuStusDown = stationModel.ipRtuStusDown;
data.ipRtuStusInLocalCtrl = stationModel.ipRtuStusInLocalCtrl;
if (station) {
switch (controlChange.value) {
case '中控':
data.ipRtuStusInLocalCtrl = false;
data.ipRtuStusDown = false;
break;
case '站控且允许转到中控':
data.ipRtuStusInLocalCtrl = true;
data.ipRtuStusDown = false;
break;
case '站控且不允许转到中控':
data.ipRtuStusInLocalCtrl = true;
data.ipRtuStusDown = true;
break;
}
mockStationApi(lineId, data)
.then((res) => {
console.log(res, '---res--');
})
.catch((err) => {
console.log(err, '---err---');
});
}
}
</script>

View File

@ -1,31 +0,0 @@
import { HandleMessage, StompMessagingClient } from 'jl-graphic';
import { getJwtToken } from 'src/configs/TokenManage';
import { getWebsocketUrl } from 'src/configs/UrlManage';
export function webSocketConnect(destination: string, handler: HandleMessage) {
const socket = new StompMessagingClient({
wsUrl: `${getWebsocketUrl()}`,
token: getJwtToken() as string,
protocol: 'protobuf',
connectTimeout: 30 * 1000,
heartbeat: 60,
retryPeriod: 2 * 1000,
retryTimes: 100,
});
socket.on('connected', () => {
socket?.subscribe(destination, handler);
});
socket.on('disconnected', () => {
console.log('disconnected');
});
return socket;
}
export function closeWebSocketConnect(
socket: StompMessagingClient | null,
destination: string
) {
socket?.unsubscribe0(destination);
socket?.close();
socket = null;
}

View File

@ -1,23 +1,15 @@
function getHost(): string {
const base_api = process.env.API;
// console.log(process.env);
// console.log(' load env :' + process.env.NODE_ENV);
return base_api + '';
// return '192.168.3.7:9081';
// return '192.168.3.47:9081';
// return '192.168.3.37:9081';
// return '192.168.3.15:9081';
return '192.168.3.233:9081';
}
export function getHttpBase() {
return process.env.HTTP + `${getHost()}` + process.env.NS;
return `http://${getHost()}`;
}
export function getWebsocketUrl() {
return process.env.WS + `${getHost()}`+ process.env.NS+'/ws-default';
}
export function getShowSetAlarmTextButton() {
let show = false;
const host = window.location.hostname;
if (process.env.NODE_ENV == 'development' || host == '192.168.3.233') {
show = true;
}
return show;
return `ws://${getHost()}/ws-default`;
}

View File

@ -1,4 +1 @@
// app global css in SCSS form
.my-notif-class {
margin-top: 50px;
}

View File

@ -33,9 +33,6 @@ export class AxleCountingData
this.data.code = v;
}
get kilometerSystem(): KilometerSystem {
if (!this.data.kilometerSystem) {
this.data.kilometerSystem = new graphicData.KilometerSystem();
}
return this.data.kilometerSystem;
}
set kilometerSystem(v: KilometerSystem) {

View File

@ -1,77 +0,0 @@
import * as pb_1 from 'google-protobuf';
import { GraphicDataBase } from './GraphicDataBase';
import {
IConcentrationDividingLineData,
ConcentrationDividingLine,
} from 'src/graphics/concentrationDividingLine/ConcentrationDividingLine';
import { graphicData } from 'src/protos/stationLayoutGraphics';
import { IPointData } from 'pixi.js';
export class ConcentrationDividingLineData
extends GraphicDataBase
implements IConcentrationDividingLineData
{
constructor(data?: graphicData.ConcentrationDividingLine) {
let concentrationDividingLine;
if (!data) {
concentrationDividingLine = new graphicData.ConcentrationDividingLine({
common: GraphicDataBase.defaultCommonInfo(
ConcentrationDividingLine.Type
),
});
} else {
concentrationDividingLine = data;
}
super(concentrationDividingLine);
}
public get data(): graphicData.ConcentrationDividingLine {
return this.getData<graphicData.ConcentrationDividingLine>();
}
get code(): string {
return this.data.code;
}
set code(v: string) {
this.data.code = 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 })
);
}
get refLeftStationId(): number {
return this.data.refLeftStationId;
}
set refLeftStationId(v: number) {
this.data.refLeftStationId = v;
}
get refRightStationId(): number {
return this.data.refRightStationId;
}
set refRightStationId(v: number) {
this.data.refRightStationId = v;
}
get nodeConWithSecs(): graphicData.NodeConWithSec[] {
return this.data.nodeConWithSecs;
}
set nodeConWithSecs(nodes: graphicData.NodeConWithSec[]) {
this.data.nodeConWithSecs = nodes;
}
get isOtherLineConcentrationDividingLine(): boolean {
return this.data.isOtherLineConcentrationDividingLine;
}
set isOtherLineConcentrationDividingLine(v: boolean) {
this.data.isOtherLineConcentrationDividingLine = v;
}
clone(): ConcentrationDividingLineData {
return new ConcentrationDividingLineData(this.data.cloneMessage());
}
copyFrom(data: ConcentrationDividingLineData): void {
pb_1.Message.copyInto(data.data, this.data);
}
eq(other: ConcentrationDividingLineData): boolean {
return pb_1.Message.equals(this.data, other.data);
}
}

View File

@ -6,13 +6,14 @@ import {
GraphicTransform,
IChildTransform,
IGraphicTransform,
} from 'jl-graphic';
} from 'src/jl-graphic';
// import { toStorageTransform } from '..';
import { graphicData } from 'src/protos/stationLayoutGraphics';
import { IPointData, Point } from 'pixi.js';
import { state } from 'src/protos/device_status';
export interface ICommonInfo {
id: number;
id: string;
graphicType: string;
transform: IGraphicTransform;
childTransforms: IChildTransform[];
@ -59,7 +60,7 @@ export abstract class GraphicDataBase implements GraphicData {
static defaultCommonInfo(graphicType: string): graphicData.CommonInfo {
return new graphicData.CommonInfo({
id: 0,
id: '',
graphicType: graphicType,
transform: new graphicData.Transform({
position: new graphicData.Point({ x: 0, y: 0 }),
@ -75,10 +76,10 @@ export abstract class GraphicDataBase implements GraphicData {
return this._data as D;
}
get id(): number {
get id(): string {
return this._data.common.id;
}
set id(v: number) {
set id(v: string) {
this._data.common.id = v;
}
get graphicType(): string {

View File

@ -3,18 +3,20 @@ import { IPointData, DisplayObject, FederatedMouseEvent } from 'pixi.js';
import { ILinkData, Link } from 'src/graphics/link/Link';
import { graphicData } from 'src/protos/stationLayoutGraphics';
import { GraphicDataBase } from './GraphicDataBase';
import { ContextMenu } from 'src/jl-graphic/ui/ContextMenu';
import { MenuItemOptions } from 'src/jl-graphic/ui/Menu';
import {
IGraphicApp,
GraphicApp,
GraphicInteractionPlugin,
JlGraphic,
ContextMenu,
MenuItemOptions,
} from 'src/jl-graphic';
import {
addWayPoint,
clearWayPoint,
getWaypointRangeIndex,
PolylineEditPlugin,
removeLineWayPoint,
} from 'jl-graphic';
} from 'src/jl-graphic/plugins/GraphicEditPlugin';
export class LinkData extends GraphicDataBase implements ILinkData {
constructor(data?: graphicData.Link) {
@ -117,12 +119,12 @@ const EpEditMenu: ContextMenu = ContextMenu.init({
export class DrawLinkPlugin extends GraphicInteractionPlugin<Link> {
static Name = 'link_draw_right_menu';
constructor(app: IGraphicApp) {
constructor(app: GraphicApp) {
super(DrawLinkPlugin.Name, app);
app.registerMenu(LinkEditMenu);
app.registerMenu(EpEditMenu);
}
static init(app: IGraphicApp) {
static init(app: GraphicApp) {
return new DrawLinkPlugin(app);
}
filter(...grahpics: JlGraphic[]): Link[] | undefined {

View File

@ -10,16 +10,15 @@ import { graphicData } from 'src/protos/stationLayoutGraphics';
import { DisplayObject, FederatedMouseEvent, IPointData } from 'pixi.js';
import { state } from 'src/protos/device_status';
import {
IGraphicApp,
GraphicApp,
GraphicInteractionPlugin,
JlGraphic,
MenuItemOptions,
ContextMenu,
} from 'jl-graphic';
} from 'src/jl-graphic';
import { LogicSectionGraphicHitArea } from 'src/graphics/logicSection/LogicSectionDrawAssistant';
import { MenuItemOptions } from 'src/jl-graphic/ui/Menu';
import { ContextMenu } from 'src/jl-graphic/ui/ContextMenu';
import { useLineStore } from 'src/stores/line-store';
import { setTrackStatus } from 'src/api/TurnoutApi';
import { successNotify } from 'src/utils/CommonNotify';
let menuItemHandler: (propName: keyof ILogicSectionState) => void;
@ -55,10 +54,6 @@ const blocked: MenuItemOptions = {
name: '轨道区段封锁 - blocked',
handler: () => menuItemHandler('blocked'),
};
const speedLimit: MenuItemOptions = {
name: '限速 - speedLimit',
handler: () => menuItemHandler('speedLimit'),
};
const LogicSectionMenu = ContextMenu.init({
name: 'LogicSection菜单',
@ -73,7 +68,6 @@ const LogicSectionMenu = ContextMenu.init({
atcInvalid,
overlap,
blocked,
speedLimit,
],
},
],
@ -81,32 +75,25 @@ const LogicSectionMenu = ContextMenu.init({
export class LogicSectionOperationPlugin extends GraphicInteractionPlugin<LogicSection> {
static Name = 'logic_section_menu';
constructor(app: IGraphicApp) {
constructor(app: GraphicApp) {
super(LogicSectionOperationPlugin.Name, app);
app.registerMenu(LogicSectionMenu);
}
filter(...grahpics: JlGraphic[]): LogicSection[] | undefined {
return grahpics.filter((g): g is LogicSection => g instanceof LogicSection);
}
static init(app: IGraphicApp) {
static init(app: GraphicApp) {
return new LogicSectionOperationPlugin(app);
}
bind(g: LogicSection): void {
g.eventMode = 'static';
g.cursor = 'pointer';
g.lineGraphic.hitArea = new LogicSectionGraphicHitArea(g);
g.on('_leftclick', this.onLeftClick, this);
g.on('rightclick', this.onContextMenu, this);
}
unbind(g: LogicSection): void {
g.off('_leftclick', this.onLeftClick, this);
g.off('rightclick', this.onContextMenu);
}
onLeftClick(e: FederatedMouseEvent) {
const target = e.target as DisplayObject;
const section = target.getGraphic() as LogicSection;
this.app.updateSelected(section);
}
onContextMenu(e: FederatedMouseEvent) {
const target = e.target as DisplayObject;
const section = target.getGraphic() as LogicSection;
@ -154,16 +141,10 @@ export class LogicSectionOperationPlugin extends GraphicInteractionPlugin<LogicS
menuItemHandler = (propName) => {
const lineId = useLineStore().lineId?.toString();
if (!lineId) return;
let val: boolean | number;
if (propName !== 'speedLimit') {
val = !section.states[propName];
} else {
val = section.states[propName] > 0 ? 0 : 20;
successNotify(`限速设为${val}`);
}
console.log({ ...section.states });
setTrackStatus(lineId, {
...state,
[propName]: val,
[propName]: !section.states[propName],
});
};
LogicSectionMenu.open(e.global);
@ -314,14 +295,6 @@ export class LogicSectionState
public set limitType(value: LimitType) {
this.states.limitType = value;
}
// 集中站站号
public get rtuId(): number {
return this.states.rtuId;
}
public set rtuId(value: number) {
this.states.rtuId = value;
}
clone(): LogicSectionState {
return new LogicSectionState(this.states.cloneMessage());
}

View File

@ -7,13 +7,13 @@ import {
import { graphicData } from 'src/protos/stationLayoutGraphics';
import { GraphicDataBase, GraphicStateBase } from './GraphicDataBase';
import { state } from 'src/protos/device_status';
import { MenuItemOptions } from 'src/jl-graphic/ui/Menu';
import { ContextMenu } from 'src/jl-graphic/ui/ContextMenu';
import {
IGraphicApp,
GraphicApp,
GraphicInteractionPlugin,
JlGraphic,
MenuItemOptions,
ContextMenu,
} from 'jl-graphic';
} from 'src/jl-graphic';
import { DisplayObject, FederatedMouseEvent } from 'pixi.js';
import { mockPlatformApi } from 'src/api/PlatformApi';
import { useLineStore } from 'src/stores/line-store';
@ -59,24 +59,12 @@ export class PlatformData extends GraphicDataBase implements IPlatformData {
set up(v: boolean) {
this.data.up = v;
}
get refStation(): number {
get refStation(): string {
return this.data.refStation;
}
set refStation(v: number) {
set refStation(v: string) {
this.data.refStation = v;
}
get refSectionId(): number {
return this.data.refSectionId;
}
set refSectionId(v: number) {
this.data.refSectionId = v;
}
get centralizedStation(): number {
return this.data.centralizedStationId;
}
set centralizedStation(v: number) {
this.data.centralizedStationId = v;
}
clone(): PlatformData {
return new PlatformData(this.data.cloneMessage());
@ -200,13 +188,6 @@ export class PlatformState extends GraphicStateBase implements IPlatformState {
set stopTime(v: number) {
this.states.stopTime = v;
}
// 集中站站号
get rtuId(): number {
return this.states.rtuId;
}
set rtuId(value: number) {
this.states.rtuId = value;
}
get states(): state.Platform {
return this.getState<state.Platform>();
}
@ -221,26 +202,86 @@ export class PlatformState extends GraphicStateBase implements IPlatformState {
}
}
const resetConfig: MenuItemOptions = {
name: '重置状态',
const holdConfig: MenuItemOptions = {
name: '扣车',
};
const removeHoldrConfig: MenuItemOptions = {
name: '取消扣车',
};
const batchHoldConfig: MenuItemOptions = {
name: '批量扣车',
};
const removeBatchHoldConfig: MenuItemOptions = {
name: '批量取消扣车',
};
const earlyDepartureConfig: MenuItemOptions = {
name: '提前发车',
};
const skipStopConfig: MenuItemOptions = {
name: '设置跳停',
};
const removeSkipStopConfig: MenuItemOptions = {
name: '取消跳停',
};
const dockTimeConfig: MenuItemOptions = {
name: '设置停站时间',
};
const operatingLevelConfig: MenuItemOptions = {
name: '设置运行等级',
};
const numberOfRegionalTrainsConfig: MenuItemOptions = {
name: '区间列车数量限制',
};
const removeNumberOfRegionalTrainsConfig: MenuItemOptions = {
name: '取消区间列车数量限制',
};
const platformMessadeConfig: MenuItemOptions = {
name: '站台详细信息',
};
const PlatformOperateMenu: ContextMenu = ContextMenu.init({
name: '站台操作菜单',
groups: [
{
items: [resetConfig],
items: [
holdConfig,
removeHoldrConfig,
skipStopConfig,
removeSkipStopConfig,
],
},
],
});
const dispatchPlatformOperateMenu: ContextMenu = ContextMenu.init({
name: '调度仿真站台操作菜单',
groups: [
{
items: [
holdConfig,
removeHoldrConfig,
batchHoldConfig,
removeBatchHoldConfig,
earlyDepartureConfig,
skipStopConfig,
removeSkipStopConfig,
dockTimeConfig,
operatingLevelConfig,
numberOfRegionalTrainsConfig,
removeNumberOfRegionalTrainsConfig,
platformMessadeConfig,
],
},
],
});
export class PlatformOperateInteraction extends GraphicInteractionPlugin<Platform> {
static Name = 'platform_operate_menu';
constructor(app: IGraphicApp) {
constructor(app: GraphicApp) {
super(PlatformOperateInteraction.Name, app);
app.registerMenu(PlatformOperateMenu);
}
static init(app: IGraphicApp) {
static init(app: GraphicApp) {
return new PlatformOperateInteraction(app);
}
filter(...grahpics: JlGraphic[]): Platform[] | undefined {
@ -286,8 +327,70 @@ export class PlatformOperateInteraction extends GraphicInteractionPlugin<Platfor
nextSectionRunLevel: 0,
stopTime: 0,
};
resetConfig.handler = () => {
holdConfig.handler = () => {
const dataCopy = JSON.parse(JSON.stringify(data));
if (platform.datas.direction == 'down') {
//dataCopy.upHold = true; //上行方向车站扣车
dataCopy.trainberth = true; //列车停站
dataCopy.emergstop = true; //紧急关闭
dataCopy.upOccHold = true; //上行方向中心扣车
dataCopy.psdOpen = true;
dataCopy.nextSectionRunLevel = 2;
dataCopy.nextSectionRunTime = 10;
dataCopy.stopTime = 5;
} else {
dataCopy.downHold = true; //下行方向车站扣车
dataCopy.downOccHold = true; //下行方向中心扣车
dataCopy.psdCut = true;
}
mockPlatformApi(lineId, dataCopy)
.then((res) => {
console.log(res, '---res--');
})
.catch((err) => {
console.log(err, '---err---');
});
};
removeHoldrConfig.handler = () => {
const dataCopy = JSON.parse(JSON.stringify(data));
if (platform.datas.direction == 'down') {
dataCopy.upHold = false;
} else {
dataCopy.downHold = false;
}
mockPlatformApi(lineId, dataCopy)
.then((res) => {
console.log(res, '---res--');
})
.catch((err) => {
console.log(err, '---err---');
});
};
skipStopConfig.handler = () => {
const dataCopy = JSON.parse(JSON.stringify(data));
if (platform.datas.direction == 'down') {
dataCopy.upSkipstop = true; //上行方向跳停
} else {
dataCopy.downSkipstop = true; //下行方向跳停
dataCopy.nextSectionRunLevel = 2;
dataCopy.nextSectionRunTime = 10;
dataCopy.stopTime = 5;
}
mockPlatformApi(lineId, dataCopy)
.then((res) => {
console.log(res, '---res--');
})
.catch((err) => {
console.log(err, '---err---');
});
};
removeSkipStopConfig.handler = () => {
const dataCopy = JSON.parse(JSON.stringify(data));
if (platform.datas.direction == 'down') {
dataCopy.upSkipstop = false;
} else {
dataCopy.downSkipstop = false;
}
mockPlatformApi(lineId, dataCopy)
.then((res) => {
console.log(res, '---res--');

View File

@ -4,7 +4,7 @@ import { graphicData } from 'src/protos/stationLayoutGraphics';
import { GraphicDataBase } from './GraphicDataBase';
import { DisplayObject, FederatedMouseEvent, IPointData } from 'pixi.js';
import {
IGraphicApp,
GraphicApp,
GraphicInteractionPlugin,
JlGraphic,
} from 'src/jl-graphic';
@ -128,12 +128,12 @@ const EpEditMenu: ContextMenu = ContextMenu.init({
export class DrawPolygonPlugin extends GraphicInteractionPlugin<Polygon> {
static Name = 'polygon_draw_right_menu';
constructor(app: IGraphicApp) {
constructor(app: GraphicApp) {
super(DrawPolygonPlugin.Name, app);
app.registerMenu(PolygonEditMenu);
app.registerMenu(EpEditMenu);
}
static init(app: IGraphicApp) {
static init(app: GraphicApp) {
return new DrawPolygonPlugin(app);
}
filter(...grahpics: JlGraphic[]): Polygon[] | undefined {

View File

@ -8,21 +8,22 @@ import { graphicData } from 'src/protos/stationLayoutGraphics';
import { GraphicDataBase } from './GraphicDataBase';
import {
GraphicInteractionPlugin,
IGraphicApp,
GraphicApp,
JlGraphic,
ContextMenu,
MenuItemOptions,
} from 'src/jl-graphic';
import { ContextMenu } from 'src/jl-graphic/ui/ContextMenu';
import { MenuItemOptions } from 'src/jl-graphic/ui/Menu';
import { FederatedMouseEvent, DisplayObject, IPointData } from 'pixi.js';
import {
addWayPoint,
clearWayPoint,
getWaypointRangeIndex,
PolylineEditPlugin,
removeLineWayPoint,
} from 'jl-graphic';
import { FederatedMouseEvent, DisplayObject, IPointData } from 'pixi.js';
} from 'src/jl-graphic/plugins/GraphicEditPlugin';
import { RunLineGraphicHitArea } from 'src/graphics/runLine/RunLineDrawAssistant';
import { Dialog } from 'quasar';
import SetDashLineDialog from '../../components/draw-app/dialogs/SetDashLineDialog.vue';
import SetGaryLineDialog from '../../components/draw-app/dialogs/SetGaryLineDialog.vue';
export class RunLineData extends GraphicDataBase implements IRunLineData {
constructor(data?: graphicData.RunLine) {
@ -71,10 +72,10 @@ export class RunLineData extends GraphicDataBase implements IRunLineData {
set containSta(v: string[]) {
this.data.containSta = v;
}
get linkPathLines(): number[] {
get linkPathLines(): string[] {
return this.data.linkPathLines;
}
set linkPathLines(v: number[]) {
set linkPathLines(v: string[]) {
this.data.linkPathLines = v;
}
get lineId(): string {
@ -89,18 +90,6 @@ export class RunLineData extends GraphicDataBase implements IRunLineData {
set dashPointIndexs(v: number[]) {
this.data.dashPointIndexs = v;
}
get grayPointIndexs(): number[] {
return this.data.grayPointIndexs;
}
set grayPointIndexs(v: number[]) {
this.data.grayPointIndexs = v;
}
get lineColor(): string {
return this.data.lineColor;
}
set lineColor(v: string) {
this.data.lineColor = v;
}
clone(): RunLineData {
return new RunLineData(this.data.cloneMessage());
}
@ -124,20 +113,12 @@ export const clearWaypointsConfig: MenuItemOptions = {
export const setDashLineConfig: MenuItemOptions = {
name: '设置虚线段',
};
export const setGrayLineConfig: MenuItemOptions = {
name: '设置灰线段',
};
const RunLineEditMenu: ContextMenu = ContextMenu.init({
name: '运行线编辑菜单',
groups: [
{
items: [
addWaypointConfig,
clearWaypointsConfig,
setDashLineConfig,
setGrayLineConfig,
],
items: [addWaypointConfig, clearWaypointsConfig, setDashLineConfig],
},
],
});
@ -152,12 +133,12 @@ const EpEditMenu: ContextMenu = ContextMenu.init({
export class DrawRunLinePlugin extends GraphicInteractionPlugin<RunLine> {
static Name = 'runline_draw_right_menu';
constructor(app: IGraphicApp) {
constructor(app: GraphicApp) {
super(DrawRunLinePlugin.Name, app);
app.registerMenu(RunLineEditMenu);
app.registerMenu(EpEditMenu);
}
static init(app: IGraphicApp) {
static init(app: GraphicApp) {
return new DrawRunLinePlugin(app);
}
filter(...grahpics: JlGraphic[]): RunLine[] | undefined {
@ -214,7 +195,7 @@ export class DrawRunLinePlugin extends GraphicInteractionPlugin<RunLine> {
};
setDashLineConfig.handler = () => {
Dialog.create({
title: '设置虚线段',
title: '创建列车',
message: '',
component: SetDashLineDialog,
componentProps: {
@ -234,40 +215,17 @@ export class DrawRunLinePlugin extends GraphicInteractionPlugin<RunLine> {
runLine.doRepaint();
});
};
setGrayLineConfig.handler = () => {
console.log(runLine.datas, '11111');
Dialog.create({
title: '设置灰线段',
message: '',
component: SetGaryLineDialog,
componentProps: {
runLinePoints: runLine.datas.points,
garyPointIndexs: runLine.datas.grayPointIndexs,
},
cancel: true,
persistent: true,
}).onOk((data: { min: number; max: number }) => {
const indexList = [];
if (data.min !== data.max) {
for (let i = data.min; i <= data.max; i++) {
indexList.push(i);
}
}
runLine.datas.grayPointIndexs = indexList;
runLine.doRepaint();
});
};
RunLineEditMenu.open(e.global);
}
}
export class RunLineOperateInteraction extends GraphicInteractionPlugin<RunLine> {
static Name = 'runLine_operate_menu';
constructor(app: IGraphicApp) {
constructor(app: GraphicApp) {
super(RunLineOperateInteraction.Name, app);
app.registerMenu(EpEditMenu);
}
static init(app: IGraphicApp) {
static init(app: GraphicApp) {
return new RunLineOperateInteraction(app);
}
filter(...grahpics: JlGraphic[]): RunLine[] | undefined {

View File

@ -2,9 +2,7 @@ import * as pb_1 from 'google-protobuf';
import { GraphicDataBase } from './GraphicDataBase';
import { ISectionData, Section } from 'src/graphics/section/Section';
import { graphicData } from 'src/protos/stationLayoutGraphics';
import { DisplayObject, FederatedMouseEvent, IPointData } from 'pixi.js';
import { GraphicInteractionPlugin, IGraphicApp, JlGraphic } from 'jl-graphic';
import { SectionGraphicHitArea } from 'src/graphics/section/SectionDrawAssistant';
import { IPointData } from 'pixi.js';
export class SectionData extends GraphicDataBase implements ISectionData {
constructor(data?: graphicData.Section) {
@ -53,16 +51,16 @@ export class SectionData extends GraphicDataBase implements ISectionData {
set sectionType(type: graphicData.Section.SectionType) {
this.data.sectionType = type;
}
get axleCountings(): number[] {
return this.data.axleCountings.map((a) => Number(a));
get axleCountings(): string[] {
return this.data.axleCountings;
}
set axleCountings(axleCountings: number[]) {
this.data.axleCountings = axleCountings.map((a) => a.toString());
set axleCountings(axleCountings: string[]) {
this.data.axleCountings = axleCountings;
}
get children(): number[] {
get children(): string[] {
return this.data.children;
}
set children(children: number[]) {
set children(children: string[]) {
this.data.children = children;
}
get destinationCode(): string {
@ -71,18 +69,6 @@ export class SectionData extends GraphicDataBase implements ISectionData {
set destinationCode(destinationCode: string) {
this.data.destinationCode = destinationCode;
}
get turning(): boolean {
return this.data.turning;
}
set turning(v: boolean) {
this.data.turning = v;
}
get centralizedStation(): number {
return this.data.centralizedStationId;
}
set centralizedStation(v: number) {
this.data.centralizedStationId = v;
}
clone(): SectionData {
return new SectionData(this.data.cloneMessage());
}
@ -93,30 +79,3 @@ export class SectionData extends GraphicDataBase implements ISectionData {
return pb_1.Message.equals(this.data, other.data);
}
}
export class sectionOperationPlugin extends GraphicInteractionPlugin<Section> {
static Name = 'logic_section_menu';
constructor(app: IGraphicApp) {
super(sectionOperationPlugin.Name, app);
}
filter(...grahpics: JlGraphic[]): Section[] | undefined {
return grahpics.filter((g): g is Section => g instanceof Section);
}
static init(app: IGraphicApp) {
return new sectionOperationPlugin(app);
}
bind(g: Section): void {
g.eventMode = 'static';
g.cursor = 'pointer';
g.lineGraphic.hitArea = new SectionGraphicHitArea(g);
g.on('_leftclick', this.onLeftClick, this);
}
unbind(g: Section): void {
g.off('_leftclick', this.onLeftClick, this);
}
onLeftClick(e: FederatedMouseEvent) {
const target = e.target as DisplayObject;
const section = target.getGraphic() as Section;
this.app.updateSelected(section);
}
}

View File

@ -9,16 +9,15 @@ import { graphicData } from 'src/protos/stationLayoutGraphics';
import { GraphicDataBase, GraphicStateBase } from './GraphicDataBase';
import {
GraphicInteractionPlugin,
IGraphicApp,
GraphicApp,
JlGraphic,
ContextMenu,
MenuItemOptions,
} from 'jl-graphic';
} from 'src/jl-graphic';
import { ContextMenu } from 'src/jl-graphic/ui/ContextMenu';
import { MenuItemOptions } from 'src/jl-graphic/ui/Menu';
import { FederatedMouseEvent, DisplayObject } from 'pixi.js';
import { state } from 'src/protos/device_status';
import { mockSignalApi } from 'src/api/PlatformApi';
import { useLineStore } from 'src/stores/line-store';
import { SignalGraphicHitArea } from 'src/graphics/signal/SignalDrawAssistant';
export class SignalData extends GraphicDataBase implements ISignalData {
constructor(data?: graphicData.Signal) {
@ -47,27 +46,12 @@ export class SignalData extends GraphicDataBase implements ISignalData {
set mirror(v: boolean) {
this.data.mirror = v;
}
get refDevice(): graphicData.RelatedRef {
return this.data.refDevice;
}
set refDevice(v: graphicData.RelatedRef) {
this.data.refDevice = v;
}
get kilometerSystem(): KilometerSystem {
if (!this.data.kilometerSystem) {
this.data.kilometerSystem = new graphicData.KilometerSystem();
}
return this.data.kilometerSystem;
}
set kilometerSystem(v: KilometerSystem) {
this.data.kilometerSystem = new graphicData.KilometerSystem(v);
}
get centralizedStation(): number {
return this.data.centralizedStationId;
}
set centralizedStation(v: number) {
this.data.centralizedStationId = v;
}
clone(): SignalData {
return new SignalData(this.data.cloneMessage());
}
@ -231,13 +215,6 @@ export class SignalState extends GraphicStateBase implements ISignalState {
set lampFailure(v: boolean) {
this.states.lampFailure = v;
}
// 集中站站号
get rtuId(): number {
return this.states.rtuId;
}
set rtuId(value: number) {
this.states.rtuId = value;
}
get states(): state.Signal {
return this.getState<state.Signal>();
}
@ -298,11 +275,11 @@ const SignalOperateMenu: ContextMenu = ContextMenu.init({
});
export class DrawSignalInteraction extends GraphicInteractionPlugin<Signal> {
static Name = 'signal_draw_right_menu';
constructor(app: IGraphicApp) {
constructor(app: GraphicApp) {
super(DrawSignalInteraction.Name, app);
app.registerMenu(SignalEditMenu);
}
static init(app: IGraphicApp) {
static init(app: GraphicApp) {
return new DrawSignalInteraction(app);
}
filter(...grahpics: JlGraphic[]): Signal[] | undefined {
@ -331,11 +308,11 @@ export class DrawSignalInteraction extends GraphicInteractionPlugin<Signal> {
export class SignalOperateInteraction extends GraphicInteractionPlugin<Signal> {
static Name = 'signal_operate_menu';
constructor(app: IGraphicApp) {
constructor(app: GraphicApp) {
super(SignalOperateInteraction.Name, app);
app.registerMenu(SignalOperateMenu);
}
static init(app: IGraphicApp) {
static init(app: GraphicApp) {
return new SignalOperateInteraction(app);
}
filter(...grahpics: JlGraphic[]): Signal[] | undefined {
@ -347,7 +324,6 @@ export class SignalOperateInteraction extends GraphicInteractionPlugin<Signal> {
g.eventMode = 'static';
g.cursor = 'pointer';
g.selectable = true;
g.lampMainBody.hitArea = new SignalGraphicHitArea(g);
g.on('_rightclick', this.onContextMenu, this);
}

View File

@ -7,17 +7,17 @@ import {
import { graphicData } from 'src/protos/stationLayoutGraphics';
import { GraphicDataBase, GraphicStateBase } from './GraphicDataBase';
import { state } from 'src/protos/device_status';
import { MenuItemOptions } from 'src/jl-graphic/ui/Menu';
import { ContextMenu } from 'src/jl-graphic/ui/ContextMenu';
import {
IGraphicApp,
GraphicApp,
GraphicInteractionPlugin,
JlGraphic,
MenuItemOptions,
ContextMenu,
} from 'jl-graphic';
} from 'src/jl-graphic';
import { DisplayObject, FederatedMouseEvent } from 'pixi.js';
import { KilometerSystem } from 'src/graphics/signal/Signal';
import { useLineStore } from 'src/stores/line-store';
import { mockServerApi, mockStationApi } from 'src/api/PlatformApi';
import { mockStationApi } from 'src/api/PlatformApi';
export class StationData extends GraphicDataBase implements IStationData {
constructor(data?: graphicData.Station) {
@ -42,9 +42,6 @@ export class StationData extends GraphicDataBase implements IStationData {
this.data.code = v;
}
get kilometerSystem(): KilometerSystem {
if (!this.data.kilometerSystem) {
this.data.kilometerSystem = new graphicData.KilometerSystem();
}
return this.data.kilometerSystem;
}
set kilometerSystem(v: KilometerSystem) {
@ -68,18 +65,6 @@ export class StationData extends GraphicDataBase implements IStationData {
set name(v: string) {
this.data.name = v;
}
get manageStations(): number[] {
return this.data.manageStations;
}
set manageStations(v: number[]) {
this.data.manageStations = v;
}
get depots(): boolean {
return this.data.depots;
}
set depots(v: boolean) {
this.data.depots = v;
}
clone(): StationData {
return new StationData(this.data.cloneMessage());
}
@ -103,11 +88,7 @@ export class StationState extends GraphicStateBase implements IStationState {
}
get code(): string {
if (this.states.id.length === 1) {
return '0' + this.states.id;
} else {
return this.states.id;
}
return this.states.id;
}
get ipRtuStusDown(): boolean {
return this.states.ipRtuStusDown;
@ -136,13 +117,6 @@ export class StationState extends GraphicStateBase implements IStationState {
get states(): state.Rtu {
return this.getState<state.Rtu>();
}
// 集中站站号
get rtuId(): number {
return this.states.rtuId;
}
set rtuId(value: number) {
this.states.rtuId = value;
}
clone(): StationState {
return new StationState(this.states.cloneMessage());
}
@ -154,32 +128,32 @@ export class StationState extends GraphicStateBase implements IStationState {
}
}
const resetConfig: MenuItemOptions = {
name: '重置状态',
};
const buleShow: MenuItemOptions = {
name: '蓝显',
};
const cancelBuleShow: MenuItemOptions = {
name: '取消蓝显',
const powerUnlockConfig: MenuItemOptions = {
name: '上电解锁',
};
const chainConfig: MenuItemOptions = {
name: '全站设置连锁自动触发',
};
const removeChainConfig: MenuItemOptions = {
name: '全站取消连锁自动触发',
};
const StationOperateMenu: ContextMenu = ContextMenu.init({
name: '车站操作菜单',
groups: [
{
items: [resetConfig, buleShow, cancelBuleShow],
items: [powerUnlockConfig, chainConfig, removeChainConfig],
},
],
});
export class StationOperateInteraction extends GraphicInteractionPlugin<Station> {
static Name = 'station_operate_menu';
constructor(app: IGraphicApp) {
constructor(app: GraphicApp) {
super(StationOperateInteraction.Name, app);
app.registerMenu(StationOperateMenu);
}
static init(app: IGraphicApp) {
static init(app: GraphicApp) {
return new StationOperateInteraction(app);
}
filter(...grahpics: JlGraphic[]): Station[] | undefined {
@ -213,30 +187,34 @@ export class StationOperateInteraction extends GraphicInteractionPlugin<Station>
ipRtuStusInEmergencyCtrl: false,
id: station.id,
};
buleShow.handler = () => {
const data = {
deviceType: 'DEVICE_TYPE_RTU',
messageId: 'DEVICE_STATUS_CHANGE',
lineId: 3,
rtuId: station.states.rtuId,
deviceName: station.states.rtuId + '',
deviceStatus: 32768,
};
mockServerApi(data);
};
cancelBuleShow.handler = () => {
const data = {
deviceType: 'DEVICE_TYPE_RTU',
messageId: 'DEVICE_STATUS_CHANGE',
lineId: 3,
rtuId: station.states.rtuId,
deviceName: station.states.rtuId + '',
deviceStatus: 1,
};
mockServerApi(data);
};
resetConfig.handler = () => {
powerUnlockConfig.handler = () => {
/* station.states.ipRtuStusInLocalCtrl = true;
station.doRepaint(); */
const dataCopy = JSON.parse(JSON.stringify(data));
dataCopy.ipRtuStusInLocalCtrl = true;
mockStationApi(lineId, dataCopy)
.then((res) => {
console.log(res, '---res--');
})
.catch((err) => {
console.log(err, '---err---');
});
};
chainConfig.handler = () => {
const dataCopy = JSON.parse(JSON.stringify(data));
dataCopy.ipRtuStusInLocalCtrl = true;
dataCopy.ipRtuStusDown = true;
mockStationApi(lineId, dataCopy)
.then((res) => {
console.log(res, '---res--');
})
.catch((err) => {
console.log(err, '---err---');
});
};
removeChainConfig.handler = () => {
const dataCopy = JSON.parse(JSON.stringify(data));
dataCopy.ipRtuStusInLocalCtrl = false;
mockStationApi(lineId, dataCopy)
.then((res) => {
console.log(res, '---res--');

View File

@ -43,12 +43,6 @@ export class StationLineData
set hideName(v: boolean) {
this.data.hideName = v;
}
get color(): graphicData.StationLine.stationColor {
return this.data.codeColor;
}
set color(v: graphicData.StationLine.stationColor) {
this.data.codeColor = v;
}
clone(): StationLineData {
return new StationLineData(this.data.cloneMessage());
}

View File

@ -4,14 +4,14 @@ import { graphicData } from 'src/protos/stationLayoutGraphics';
import { GraphicDataBase, GraphicStateBase } from './GraphicDataBase';
import { state } from 'src/protos/device_status';
import { train } from 'src/protos/train';
import { MenuItemOptions } from 'src/jl-graphic/ui/Menu';
import { ContextMenu } from 'src/jl-graphic/ui/ContextMenu';
import {
IGraphicApp,
GraphicApp,
GraphicInteractionPlugin,
JlGraphic,
VectorText,
MenuItemOptions,
ContextMenu,
} from 'jl-graphic';
} from 'src/jl-graphic';
import {
Color,
Container,
@ -69,11 +69,6 @@ export class TrainState extends GraphicStateBase implements ITrainState {
get code(): string {
return this.states.groupId;
}
get remove(): boolean {
const rtuIdArr = [81, 82];
const hasRtuId = rtuIdArr.includes(this.states.rtuId);
return hasRtuId;
}
get states(): train.TrainInfo {
return this.getState<train.TrainInfo>();
@ -199,12 +194,12 @@ export class TrainState extends GraphicStateBase implements ITrainState {
set rate(v: number) {
this.states.rate = v;
}
// get remove(): train.TrainRemove {
// return this.states.remove;
// }
// set remove(v: train.TrainRemove) {
// this.states.remove = new train.TrainRemove(v);
// }
get remove(): train.TrainRemove {
return this.states.remove;
}
set remove(v: train.TrainRemove) {
this.states.remove = new train.TrainRemove(v);
}
get block(): train.TrainBlock {
return this.states.block;
}
@ -274,12 +269,13 @@ const TrainOperateMenu: ContextMenu = ContextMenu.init({
export class TrainOperateInteraction extends GraphicInteractionPlugin<Train> {
static Name = 'train_operate_menu';
hoverLaber: TrainHoverLabel;
constructor(app: IGraphicApp) {
constructor(app: GraphicApp) {
super(TrainOperateInteraction.Name, app);
this.hoverLaber = new TrainHoverLabel();
app.canvas.addChild(this.hoverLaber);
app.registerMenu(TrainOperateMenu);
}
static init(app: IGraphicApp) {
static init(app: GraphicApp) {
return new TrainOperateInteraction(app);
}
filter(...grahpics: JlGraphic[]): Train[] | undefined {
@ -294,7 +290,7 @@ export class TrainOperateInteraction extends GraphicInteractionPlugin<Train> {
this.onMouseHover(g);
};
g.trainbody.onmouseout = () => {
this.onMouseOut(g);
this.onMouseOut();
};
}
@ -308,15 +304,13 @@ export class TrainOperateInteraction extends GraphicInteractionPlugin<Train> {
onMouseHover(g: Train): void {
if (!this.hoverLaber.isShow) {
g.addChild(this.hoverLaber);
this.hoverLaber.doRepaint(g.states);
const bodyWh = g.trainbody.getBodyWH();
this.hoverLaber.position.set(bodyWh.width / 2, bodyWh.height / 2);
const bodyWh = g.trainbody.localBoundsToCanvasPoints();
this.hoverLaber.position.set(bodyWh[2].x, bodyWh[2].y);
}
}
onMouseOut(g: Train): void {
onMouseOut(): void {
if (this.hoverLaber.isShow) {
g.removeChild(this.hoverLaber);
this.hoverLaber.clear();
}
}
@ -436,7 +430,6 @@ const labelConsts = {
codeFontSize: 12,
};
class TrainHoverLabel extends Container {
static Type = 'TrainHoverLabel';
boxRact: Graphics = new Graphics();
sText: VectorText = new VectorText('');
isShow: boolean;
@ -453,12 +446,7 @@ class TrainHoverLabel extends Container {
fill: labelConsts.textColor,
fontSize: labelConsts.codeFontSize,
};
const codeA = states.groupId;
// const firstChar = codeA.substring(0, 1); // 获取首字符
// if (+firstChar == states.lineId) {
// codeA = codeA.substring(1); // 删除首字符是线路号的字符
// }
const text = `列车类型:计划车\n来 源:人工标记\n车 组 号:${codeA}\n表 号:${states.trainId}\n车 次 号:${states.globalId}\n线 路 号:${states.lineId}`;
const text = `列车类型:计划车\n来 源:人工标记\n车 组 号:${states.groupId}\n表 号:${states.trainId}\n车 次 号:${states.globalId}`;
this.sText.text = text;
this.sText.style = style;
const { width: codeWidth, height: codeHeight } =

View File

@ -31,10 +31,10 @@ export class TrainWindowData
set code(v: string) {
this.data.code = v;
}
get refDeviceId(): number[] {
get refDeviceId(): string[] {
return this.data.refDeviceId;
}
set refDeviceId(v: number[]) {
set refDeviceId(v: string[]) {
this.data.refDeviceId = v;
}
clone(): TrainWindowData {

View File

@ -10,19 +10,18 @@ import { DisplayObject, FederatedMouseEvent, IPointData } from 'pixi.js';
import { KilometerSystem } from 'src/graphics/signal/Signal';
import { state } from 'src/protos/device_status';
import {
IGraphicApp,
GraphicApp,
GraphicInteractionPlugin,
JlGraphic,
ContextMenu,
MenuItemOptions,
} from 'jl-graphic';
} from 'src/jl-graphic';
import {
ForkHitArea,
TurnoutSectionHitArea,
} from 'src/graphics/turnout/TurnoutDrawAssistant';
import { ContextMenu } from 'src/jl-graphic/ui/ContextMenu';
import { MenuItemOptions } from 'src/jl-graphic/ui/Menu';
import { setSwitchStatus } from 'src/api/TurnoutApi';
import { useLineStore } from 'src/stores/line-store';
import { successNotify } from 'src/utils/CommonNotify';
let menuItemHandler: (propName: keyof ITurnoutState) => void;
@ -103,10 +102,7 @@ const ipSingleSwitchStusLostIndication: MenuItemOptions = {
name: '道岔失表示-ipSingleSwitchStusLostIndication',
handler: () => menuItemHandler('ipSingleSwitchStusLostIndication'),
};
const speedLimit: MenuItemOptions = {
name: '限速 - speedLimit',
handler: () => menuItemHandler('speedLimit'),
};
const TurnoutOperateMenu = ContextMenu.init({
name: 'Turnout操作菜单',
groups: [
@ -131,7 +127,6 @@ const TurnoutOperateMenu = ContextMenu.init({
ipSingleSwitchStusTsrBmReverse,
ipSingleSwitchStusBlocked2,
ipSingleSwitchStusLostIndication,
speedLimit,
],
},
],
@ -139,14 +134,14 @@ const TurnoutOperateMenu = ContextMenu.init({
export class TurnoutOperationPlugin extends GraphicInteractionPlugin<Turnout> {
static Name = 'turnout_operate_menu';
constructor(app: IGraphicApp) {
constructor(app: GraphicApp) {
super(TurnoutOperationPlugin.Name, app);
app.registerMenu(TurnoutOperateMenu);
}
filter(...grahpics: JlGraphic[]): Turnout[] | undefined {
return grahpics.filter((g): g is Turnout => g instanceof Turnout);
}
static init(app: IGraphicApp) {
static init(app: GraphicApp) {
return new TurnoutOperationPlugin(app);
}
bind(g: Turnout): void {
@ -158,7 +153,6 @@ export class TurnoutOperationPlugin extends GraphicInteractionPlugin<Turnout> {
sectionGraphic.cursor = 'pointer';
sectionGraphic.hitArea = new TurnoutSectionHitArea(sectionGraphic);
});
g.on('_leftclick', this.onLeftClick, this);
g.on('rightclick', this.onContextMenu, this);
}
unbind(g: Turnout): void {
@ -166,14 +160,8 @@ export class TurnoutOperationPlugin extends GraphicInteractionPlugin<Turnout> {
g.graphics.sections.forEach((sectionGraphic) => {
sectionGraphic.eventMode = 'none';
});
g.off('_leftclick', this.onLeftClick, this);
g.on('rightclick', this.onContextMenu, this);
}
onLeftClick(e: FederatedMouseEvent) {
const target = e.target as DisplayObject;
const section = target.getGraphic() as Turnout;
this.app.updateSelected(section);
}
onContextMenu(e: FederatedMouseEvent) {
const target = e.target as DisplayObject;
const turnout = target.getGraphic() as Turnout;
@ -205,7 +193,6 @@ export class TurnoutOperationPlugin extends GraphicInteractionPlugin<Turnout> {
ipSingleSwitchStusBlocked2: turnout.states.ipSingleSwitchStusBlocked2, // 道岔封锁
ipSingleSwitchStusLostIndication:
turnout.states.ipSingleSwitchStusLostIndication, // 道岔失表示
speedLimit: turnout.states.speedLimit,
};
(Object.keys(state) as unknown as (keyof ITurnoutState)[]).forEach(
(key: keyof ITurnoutState) => {
@ -233,17 +220,10 @@ export class TurnoutOperationPlugin extends GraphicInteractionPlugin<Turnout> {
menuItemHandler = (propName) => {
const lineId = useLineStore().lineId?.toString();
if (!lineId) return;
let val: boolean | number;
if (propName !== 'speedLimit') {
val = !turnout.states[propName];
} else {
val = turnout.states[propName] > 0 ? 0 : 20;
successNotify(`限速设为${val}`);
}
setSwitchStatus(lineId, {
...state,
id: turnout.datas.code,
[propName]: val,
[propName]: !turnout.states[propName],
});
};
TurnoutOperateMenu.open(e.global);
@ -321,24 +301,13 @@ export class TurnoutData extends GraphicDataBase implements ITurnoutData {
this.data.pcRef = ref;
}
get kilometerSystem(): KilometerSystem[] {
return this.data.kilometerSystem.length > 0
? this.data.kilometerSystem
: (this.data.kilometerSystem = [
new graphicData.KilometerSystem(),
new graphicData.KilometerSystem(),
]);
return this.data.kilometerSystem;
}
set kilometerSystem(value: KilometerSystem[]) {
this.data.kilometerSystem = value.map(
(v) => new graphicData.KilometerSystem(v)
);
}
get centralizedStation(): number {
return this.data.centralizedStationId;
}
set centralizedStation(v: number) {
this.data.centralizedStationId = v;
}
clone(): TurnoutData {
return new TurnoutData(this.data.cloneMessage());
}
@ -477,25 +446,8 @@ export class TurnoutStates extends GraphicStateBase implements ITurnoutState {
public set ipSingleSwitchStusLostIndication(value: boolean) {
this.states.ipSingleSwitchStusLostIndication = value;
}
public get id(): string {
return this.states.id;
}
public set id(value: string) {
this.states.id = value;
}
// 集中站站号
public get rtuId(): number {
return this.states.rtuId;
}
public set rtuId(value: number) {
this.states.rtuId = value;
}
get speedLimit(): number {
return this.states.speedLimit;
}
set speedLimit(val: number) {
this.states.speedLimit = val;
}
id?: string;
speedLimit?: number;
get states(): state.Switch {
return this.getState<state.Switch>();
}

View File

@ -7,14 +7,13 @@ import { Signal, SignalTemplate } from 'src/graphics/signal/Signal';
import { SignalDraw } from 'src/graphics/signal/SignalDrawAssistant';
import {
CombinationKey,
GraphicApp,
GraphicData,
IDrawApp,
JlDrawApp,
KeyListener,
newDrawApp,
IGraphicStorage,
ContextMenu,
MenuItemOptions,
} from 'jl-graphic';
} from 'src/jl-graphic';
import { ContextMenu } from 'src/jl-graphic/ui/ContextMenu';
import { MenuItemOptions } from 'src/jl-graphic/ui/Menu';
import { IscsFanData } from './graphics/IscsFanInteraction';
import { LinkData } from './graphics/LinkInteraction';
import { TrainData, TrainState } from './graphics/TrainInteraction';
@ -24,12 +23,6 @@ import {
SignalState,
} from './graphics/SignalInteraction';
import { graphicData } from 'src/protos/stationLayoutGraphics';
import {
ConcentrationDividingLine,
ConcentrationDividingLineTemplate,
} from 'src/graphics/concentrationDividingLine/ConcentrationDividingLine';
import { ConcentrationDividingLineData } from './graphics/ConcentrationDividingLineInteraction';
import { ConcentrationDividingLineDraw } from 'src/graphics/concentrationDividingLine/ConcentrationDividingLineDrawAssistant';
import { Rect, RectTemplate } from 'src/graphics/rect/Rect';
import { RectDraw } from 'src/graphics/rect/RectDrawAssistant';
import { RectData } from './graphics/RectInteraction';
@ -127,7 +120,7 @@ import { FederatedMouseEvent } from 'pixi.js';
// });
// }
function constructMenu(app: IDrawApp): (e: FederatedMouseEvent) => void {
function constructMenu(app: JlDrawApp): (e: FederatedMouseEvent) => void {
const UndoOptions: MenuItemOptions = {
name: '撤销',
};
@ -210,9 +203,9 @@ function constructMenu(app: IDrawApp): (e: FederatedMouseEvent) => void {
};
return handleRightClick;
}
let drawApp: IDrawApp | null = null;
let drawApp: JlDrawApp | null = null;
export function getDrawApp(): IDrawApp | null {
export function getDrawApp(): JlDrawApp | null {
return drawApp;
}
@ -223,18 +216,35 @@ export function destroyDrawApp(): void {
}
}
export function initDrawApp(): IDrawApp {
drawApp = newDrawApp({
dataLoader: loadDrawDatas,
});
export function initDrawApp(dom: HTMLElement): JlDrawApp {
drawApp = new JlDrawApp(dom);
const app = drawApp;
//根据草稿图类型加载绘图工具
let drawAssistants: (
| PlatformDraw
| StationDraw
| SignalDraw
| TurnoutDraw
| RunLineDraw
| SectionDraw
| LogicSectionDraw
| StationLineDraw
| RectDraw
| TrainLineDraw
| PathLineDraw
| TrainWindowDraw
| TrainDraw
| OneClickGenerateDraw
| AxleCountingDraw
| SeparatorDraw
)[] = [];
const draftType = useDrawStore().$state.draftType;
if (draftType === 'Line') {
new PlatformDraw(
app,
new PlatformTemplate(new PlatformData(), new PlatformState())
),
drawAssistants = [
new PlatformDraw(
app,
new PlatformTemplate(new PlatformData(), new PlatformState())
),
new StationDraw(
app,
new StationTemplate(new StationData(), new StationState())
@ -263,15 +273,11 @@ export function initDrawApp(): IDrawApp {
new AxleCountingTemplate(new AxleCountingData())
),
new SeparatorDraw(app, new SeparatorTemplate(new SeparatorData())),
new ConcentrationDividingLineDraw(
app,
new ConcentrationDividingLineTemplate(
new ConcentrationDividingLineData()
)
);
];
DrawSignalInteraction.init(app);
} else {
new StationLineDraw(app, new StationLineTemplate(new StationLineData())),
drawAssistants = [
new StationLineDraw(app, new StationLineTemplate(new StationLineData())),
new RectDraw(app, new RectTemplate(new RectData())),
new RunLineDraw(app, new RunLineTemplate(new RunLineData())),
new TrainLineDraw(
@ -279,11 +285,14 @@ export function initDrawApp(): IDrawApp {
new ItrainLineTemplate(new TrainLineData(), new TrainLineState())
),
new PathLineDraw(app, new PathLineTemplate(new PathLineData())),
DrawRunLinePlugin.init(app);
];
DrawRunLinePlugin.init(app);
}
app.setOptions({ drawAssistants: drawAssistants });
const handleRIghtClick = constructMenu(app);
app.canvas.on('_rightclick', (e) => {
if (app.drawing) return;
if (app._drawing) return;
handleRIghtClick(e);
});
app.addKeyboardListener(
@ -299,7 +308,7 @@ export function initDrawApp(): IDrawApp {
return drawApp;
}
export function saveDrawToServer(app: IDrawApp) {
export function saveDrawToServer(app: JlDrawApp) {
const base64 = saveDrawDatas(app);
const drawStore = useDrawStore();
const id = drawStore.draftId;
@ -316,7 +325,7 @@ export function saveDrawToServer(app: IDrawApp) {
}
// const StorageKey = 'graphic-storage';
export function saveDrawDatas(app: IDrawApp) {
export function saveDrawDatas(app: JlDrawApp) {
const storage = new graphicData.RtssGraphicStorage();
const canvasData = app.canvas.saveData();
storage.canvas = new graphicData.Canvas({
@ -376,105 +385,22 @@ export function saveDrawDatas(app: IDrawApp) {
} else if (LogicSection.Type === g.type) {
const logicSectionData = (g as LogicSection).saveData();
storage.logicSections.push((logicSectionData as LogicSectionData).data);
} else if (g instanceof ConcentrationDividingLine) {
const concentrationDividingLineData = g.saveData();
storage.concentrationDividingLines.push(
(concentrationDividingLineData as ConcentrationDividingLineData).data
);
}
});
// storage.Platforms.forEach((item) => {
// item.common.nid = +item.common.id;
// item.nrefStation = +item.refStation;
// item.nrefSectionId = +item.refSectionId;
// });
// storage.axleCountings.forEach((item) => {
// item.common.nid = +item.common.id;
// item.axleCountingRef = item.axleCountingRef.map((child) => {
// child.nid = +child.id;
// return child;
// });
// });
// storage.iscsFans.forEach((item) => {
// item.common.nid = +item.common.id;
// });
// storage.links.forEach((item) => {
// item.common.nid = +item.common.id;
// });
// storage.logicSections.forEach((item) => {
// item.common.nid = +item.common.id;
// });
// storage.pathLines.forEach((item) => {
// item.common.nid = +item.common.id;
// });
// storage.polygons.forEach((item) => {
// item.common.nid = +item.common.id;
// });
// storage.rects.forEach((item) => {
// item.common.nid = +item.common.id;
// });
// storage.runLines.forEach((item) => {
// item.common.nid = +item.common.id;
// item.nlinkPathLines = item.linkPathLines.map((child) => +child);
// });
// storage.section.forEach((item) => {
// item.common.nid = +item.common.id;
// if (item.paRef) {
// item.paRef.nid = +item.paRef.id;
// }
// if (item.pbRef) {
// item.pbRef.nid = +item.pbRef.id;
// }
// item.nchildren = item.children.map((child) => +child);
// });
// storage.separators.forEach((item) => {
// item.common.nid = +item.common.id;
// });
// storage.signals.forEach((item) => {
// item.common.nid = +item.common.id;
// });
// storage.stationLines.forEach((item) => {
// item.common.nid = +item.common.id;
// });
// storage.stations.forEach((item) => {
// item.common.nid = +item.common.id;
// });
// storage.train.forEach((item) => {
// item.common.nid = +item.common.id;
// });
// storage.trainLines.forEach((item) => {
// item.common.nid = +item.common.id;
// });
// storage.trainWindows.forEach((item) => {
// item.common.nid = +item.common.id;
// item.nrefDeviceId = item.refDeviceId.map((child) => +child);
// });
// storage.turnouts.forEach((item) => {
// item.common.nid = +item.common.id;
// if (item.paRef) {
// item.paRef.nid = +item.paRef.id;
// }
// if (item.pbRef) {
// item.pbRef.nid = +item.pbRef.id;
// }
// if (item.pcRef) {
// item.pcRef.nid = +item.pcRef.id;
// }
// });
const base64 = fromUint8Array(storage.serialize());
console.log('保存数据', storage);
// localStorage.setItem(StorageKey, base64);
return base64;
}
export async function loadDrawDatas(): Promise<IGraphicStorage> {
export async function loadDrawDatas(app: GraphicApp) {
// localStorage.removeItem(StorageKey);
// const base64 = localStorage.getItem(StorageKey);
// console.log('加载数据', base64);
const drawStore = useDrawStore();
const id = drawStore.draftId;
if (!id) {
throw new Error('获取数据异常未获取到草稿地图ID');
return;
}
const { proto: base64 } = await getDraft(id);
if (base64) {
@ -482,6 +408,7 @@ export async function loadDrawDatas(): Promise<IGraphicStorage> {
toUint8Array(base64)
);
console.log('加载数据', storage);
app.updateCanvas(storage.canvas);
const datas: GraphicData[] = [];
storage.links.forEach((link) => {
datas.push(new LinkData(link));
@ -534,15 +461,8 @@ export async function loadDrawDatas(): Promise<IGraphicStorage> {
storage.trainWindows.forEach((trainWindow) => {
datas.push(new TrainWindowData(trainWindow));
});
storage.concentrationDividingLines.forEach((concentrationDividingLine) => {
datas.push(new ConcentrationDividingLineData(concentrationDividingLine));
});
return Promise.resolve({
canvasProperty: storage.canvas,
datas: datas,
});
app.loadGraphic(datas);
} else {
app.loadGraphic([]);
}
return Promise.resolve({
datas: [],
});
}

View File

@ -1,11 +1,11 @@
import {
ClientEngine,
GraphicApp,
GraphicData,
StompCli,
AppWsMsgBroker,
GraphicState,
IGraphicApp,
IGraphicStorage,
newGraphicApp,
} from 'jl-graphic';
GraphicIdGenerator,
} from 'src/jl-graphic';
import {
TrainData,
TrainOperateInteraction,
@ -36,10 +36,7 @@ import {
TurnoutStates,
} from './graphics/TurnoutInteraction';
import { Turnout, TurnoutTemplate } from 'src/graphics/turnout/Turnout';
import {
SectionData,
sectionOperationPlugin,
} from './graphics/SectionInteraction';
import { SectionData } from './graphics/SectionInteraction';
import { SectionTemplate } from 'src/graphics/section/Section';
import { getPublishMapInfoByLineId } from 'src/api/PublishApi';
import { graphicData } from 'src/protos/stationLayoutGraphics';
@ -60,7 +57,12 @@ import {
import { TrainWindowData } from './graphics/TrainWindowInteraction';
import { SeparatorTemplate } from 'src/graphics/separator/Separator';
import { SeparatorData } from './graphics/SeparatorInteraction';
import { ContextMenu, MenuItemOptions } from 'jl-graphic';
let lineApp: GraphicApp | null = null;
let msgBroker: AppWsMsgBroker | null = null;
import { ContextMenu } from 'src/jl-graphic/ui/ContextMenu';
import { MenuItemOptions } from 'src/jl-graphic/ui/Menu';
import {
LogicSection,
LogicSectionTemplate,
@ -70,13 +72,21 @@ import {
LogicSectionOperationPlugin,
LogicSectionState,
} from './graphics/LogicSectionInteraction';
import { Notify, QNotifyUpdateOptions } from 'quasar';
import { useLineNetStore } from 'src/stores/line-net-store';
import { alert } from 'src/protos/alertInfo';
import { useLineNetStore } from 'src/stores/line-net-store';
import { QNotifyUpdateOptions, Notify } from 'quasar';
let lineApp: IGraphicApp | null = null;
// const QuickJumpMenu = new ContextMenu({
// name: '快捷跳转',
// groups: [
// {
// items: [],
// },
// ],
// });
// let QuickJumpMenu: ContextMenu = new ContextMenu();
export function getLineApp() {
export function getLineApp(): GraphicApp | null {
return lineApp;
}
@ -85,29 +95,13 @@ export function destroyLineApp(): void {
lineApp.destroy();
lineApp = null;
}
if (msgBroker) {
msgBroker.close();
}
}
export function initLineApp(): IGraphicApp {
if (lineApp) return lineApp;
lineApp = newGraphicApp({
interactiveGraphicTypeIncludes: [
Signal.Type,
Platform.Type,
Station.Type,
Train.Type,
LogicSection.Type,
Turnout.Type,
],
mouseToolOptions: {
boxSelect: false,
viewportDrag: true,
viewportDragLeft: true,
wheelZoom: true,
},
dataLoader: loadLineDatas,
});
export function initLineApp(dom: HTMLElement): GraphicApp {
lineApp = new GraphicApp(dom);
const graphicTemplate = [
new TrainTemplate(new TrainData(), new TrainState()),
new SignalTemplate(new SignalData(), new SignalState()),
@ -121,100 +115,67 @@ export function initLineApp(): IGraphicApp {
new TrainWindowTemplate(new TrainWindowData()),
];
lineApp.registerGraphicTemplates(...graphicTemplate);
lineApp.setOptions({
mouseToolOptions: {
boxSelect: false,
viewportDrag: true,
wheelZoom: true,
},
interactiveTypeOptions: {
interactiveGraphicTypeIncludes: [
Signal.Type,
Platform.Type,
Station.Type,
Train.Type,
LogicSection.Type,
Turnout.Type,
],
},
});
SignalOperateInteraction.init(lineApp);
PlatformOperateInteraction.init(lineApp);
StationOperateInteraction.init(lineApp);
TrainOperateInteraction.init(lineApp);
TurnoutOperationPlugin.init(lineApp);
LogicSectionOperationPlugin.init(lineApp);
sectionOperationPlugin.init(lineApp);
lineApp.enableWsMassaging({
engine: ClientEngine.Stomp,
wsUrl: getWebsocketUrl(),
token: getJwtToken() as string,
});
let msgNotify: null | ((props?: QNotifyUpdateOptions | undefined) => void) =
null;
lineApp.on('websocket-connect-state-change', (connected) => {
if (!connected && !msgNotify) {
msgNotify = Notify.create({
type: 'negative',
timeout: 0,
position: 'top-right',
message: '与WebSocket服务连接断开',
classes: 'my-notif-class',
});
} else if (msgNotify && connected) {
msgNotify();
msgNotify = null;
}
});
lineApp.reload().then(() => {
if (!lineApp) return;
const quickJumpMenu = new ContextMenu({
name: '快捷跳转',
groups: [
{
items: lineApp.queryStore
.queryByType<Station>(Station.Type)
.map<MenuItemOptions>((station) => ({
name: station.datas.name ?? '',
handler: () => {
lineApp?.makeGraphicCenterShow(station);
},
})),
},
],
});
lineApp.registerMenu(quickJumpMenu);
lineApp.canvas.on('_rightclick', (e) => {
quickJumpMenu.open(e.global);
});
const axleCountings = lineApp.queryStore.queryByType<AxleCounting>(
AxleCounting.Type
);
axleCountings.forEach((axleCounting) => {
axleCounting.visible = false;
});
const trainWindows = lineApp.queryStore.queryByType<TrainWindow>(
TrainWindow.Type
);
trainWindows.forEach((trainWindow) => {
trainWindow.visible = false;
});
handleSubscribe(lineApp);
});
return lineApp;
}
export async function loadLineDatas(): Promise<IGraphicStorage> {
export async function loadLineDatas(app: GraphicApp) {
const lineStore = useLineStore();
const lineId = lineStore.lineId;
if (!lineId) {
throw Error('请先选择线路');
return;
}
const { proto: base64, name: lineName } = await getPublishMapInfoByLineId(
lineId,
'line'
);
lineStore.setLineName(lineName);
const datas: GraphicData[] = [];
if (base64) {
const storage = graphicData.RtssGraphicStorage.deserialize(
toUint8Array(base64)
);
console.log('加载数据', storage);
app.updateCanvas(storage.canvas);
const datas: GraphicData[] = [];
storage.Platforms.forEach((platform) => {
const g = new PlatformData(platform);
datas.push(g);
});
const quickJumpMenuItem: MenuItemOptions[] = [];
storage.stations.forEach((station) => {
datas.push(new StationData(station));
const item: MenuItemOptions = {
name: station.name,
handler: () => {
const g = app.queryStore.queryById(station.common.id);
if (g) {
app.makeGraphicCenterShow(g);
}
},
};
quickJumpMenuItem.push(item);
});
storage.train.forEach((train) => {
datas.push(new TrainData(train));
@ -240,75 +201,97 @@ export async function loadLineDatas(): Promise<IGraphicStorage> {
storage.trainWindows.forEach((trainWindow) => {
datas.push(new TrainWindowData(trainWindow));
});
return Promise.resolve({
canvasProperty: storage.canvas,
datas: datas,
await app.loadGraphic(datas);
//隐藏计轴--和车次窗
const axleCountings = app.queryStore.queryByType<AxleCounting>(
AxleCounting.Type
);
axleCountings.forEach((axleCounting) => {
axleCounting.visible = false;
});
const trainWindows = app.queryStore.queryByType<TrainWindow>(
TrainWindow.Type
);
trainWindows.forEach((trainWindow) => {
trainWindow.visible = false;
});
const QuickJumpMenu = new ContextMenu({
name: '快捷跳转',
groups: [
{
items: quickJumpMenuItem,
},
],
});
app.registerMenu(QuickJumpMenu);
app.canvas.on('_rightclick', (e) => {
QuickJumpMenu.open(e.global);
});
StompCli.new({
wsUrl: `${getWebsocketUrl()}`,
token: getJwtToken() as string,
});
msgBroker = new AppWsMsgBroker(app);
msgBroker.subscribe({
destination: `/queue/line/${lineId}/device`,
messageConverter: (message: Uint8Array) => {
const states: GraphicState[] = [];
const storage = state.WsLineMessage.deserialize(message);
storage.signal.forEach((item) => {
states.push(new SignalState(item));
});
storage.platform.forEach((item) => {
states.push(new PlatformState(item));
});
storage.rtu.forEach((item) => {
states.push(new StationState(item));
});
storage.switch.forEach((item) => {
states.push(new TurnoutStates(item));
});
storage.track.forEach((item) => {
states.push(new LogicSectionState(item));
});
return states;
},
});
msgBroker.subscribe({
destination: `/queue/line/${lineId}/train`,
messageConverter: (message: Uint8Array) => {
const states: GraphicState[] = [];
const trainStorage = state.WsLineTrainMessage.deserialize(message);
trainStorage.trainInfo.forEach((item) => {
states.push(new TrainState(item));
});
return states;
},
});
let msgNotify: null | ((props?: QNotifyUpdateOptions | undefined) => void) =
null;
app.on('websocket-connect-state-change', (connected) => {
if (!connected && !msgNotify) {
msgNotify = Notify.create({
type: 'negative',
timeout: 0,
position: 'top-right',
message: '通信链接已断开!',
});
} else if (msgNotify && connected) {
msgNotify();
msgNotify = null;
}
});
const lineNetStore = useLineNetStore();
msgBroker.subscribe({
destination: '/queue/xian/ncc/alert',
messageHandle: (message: Uint8Array) => {
const storage = alert.NccAlertInfoMessage.deserialize(message);
lineNetStore.setAlarmInfo(storage.messages as []);
},
});
} else {
return Promise.resolve({
datas: [],
});
app.loadGraphic([]);
}
}
function handleSubscribe(lineApp: IGraphicApp) {
const lineStore = useLineStore();
const lineId = lineStore.lineId;
lineApp.subscribe({
destination: `/queue/line/${lineId}/device`,
messageConverter: (message: Uint8Array) => {
const states: GraphicState[] = [];
const storage = state.WsLineMessage.deserialize(message);
storage.signal.forEach((item) => {
if (item.rtuId !== 81 && item.rtuId !== 82 && item.rtuId) {
states.push(new SignalState(item));
}
});
storage.platform.forEach((item) => {
if (item.rtuId !== 81 && item.rtuId !== 82 && item.rtuId) {
states.push(new PlatformState(item));
}
});
storage.rtu.forEach((item) => {
if (item.rtuId !== 81 && item.rtuId !== 82 && item.rtuId) {
states.push(new StationState(item));
}
});
storage.switch.forEach((item) => {
if (item.rtuId !== 81 && item.rtuId !== 82 && item.rtuId) {
states.push(new TurnoutStates(item));
}
});
storage.track.forEach((item) => {
if (item.rtuId !== 81 && item.rtuId !== 82 && item.rtuId) {
states.push(new LogicSectionState(item));
}
});
return states;
},
});
lineApp.subscribe({
destination: `/queue/line/${lineId}/train`,
createOnNotFound: { graphicTypes: [Train.Type] },
messageConverter: (message: Uint8Array) => {
const states: GraphicState[] = [];
const trainStorage = state.WsLineTrainMessage.deserialize(message);
// console.log(trainStorage, '222');
trainStorage.trainInfo.forEach((item) => {
// if (item.rtuId !== 81 && item.rtuId !== 82 && item.rtuId) {
if (item.rtuId) {
states.push(new TrainState(item));
}
});
return states;
},
});
const lineNetStore = useLineNetStore();
lineApp.subscribe({
destination: '/queue/xian/ncc/alert',
messageHandle: (message: Uint8Array) => {
const storage = alert.NccAlertInfoMessage.deserialize(message);
lineNetStore.setAlarmInfo(storage.messages as []);
},
});
}

View File

@ -1,10 +1,10 @@
import {
IGraphicApp,
GraphicApp,
GraphicData,
StompCli,
AppWsMsgBroker,
GraphicState,
newGraphicApp,
IGraphicStorage,
} from 'jl-graphic';
} from 'src/jl-graphic';
import { getPublishLineNet } from 'src/api/PublishApi';
import { graphicData } from 'src/protos/stationLayoutGraphics';
import { state } from 'src/protos/ws_message';
@ -34,11 +34,12 @@ import { toUint8Array } from 'js-base64';
import { getWebsocketUrl } from 'src/configs/UrlManage';
import { getJwtToken } from 'src/configs/TokenManage';
import { alert } from 'src/protos/alertInfo';
import { Notify } from 'quasar';
import { QNotifyUpdateOptions, Notify } from 'quasar';
let lineNetApp: IGraphicApp | null = null;
let lineNetApp: GraphicApp | null = null;
let msgBroker: AppWsMsgBroker | null = null;
export function getLineNetApp(): IGraphicApp | null {
export function getLineNetApp(): GraphicApp | null {
return lineNetApp;
}
@ -47,23 +48,13 @@ export function destroyLineNetApp(): void {
lineNetApp.destroy();
lineNetApp = null;
}
if (msgBroker) {
msgBroker.close();
}
}
export function initLineNetApp(): IGraphicApp {
lineNetApp = newGraphicApp({
interactiveGraphicTypeIncludes: [
RunLine.Type,
StationLine.Type,
TrainLine.Type,
],
mouseToolOptions: {
boxSelect: false,
viewportDrag: true,
viewportDragLeft: true,
wheelZoom: true,
},
dataLoader: loadLineNetDatas,
});
export function initLineNetApp(dom: HTMLElement): GraphicApp {
lineNetApp = new GraphicApp(dom);
const graphicTemplate = [
new RunLineTemplate(new RunLineData()),
new PathLineTemplate(new PathLineData()),
@ -71,47 +62,36 @@ export function initLineNetApp(): IGraphicApp {
new ItrainLineTemplate(new TrainLineData(), new TrainLineState()),
new RectTemplate(new RectData()),
];
lineNetApp.reload().then(() => {
if (!lineNetApp) return;
const pathLineList = lineNetApp.queryStore.queryByType(PathLine.Type);
pathLineList.forEach((pathLine) => {
pathLine.visible = false;
});
handleSubscribe(lineNetApp);
});
lineNetApp.registerGraphicTemplates(...graphicTemplate);
RunLineOperateInteraction.init(lineNetApp);
let msgNotify: ReturnType<Notify['create']> | null = null;
lineNetApp.on('websocket-connect-state-change', (connected) => {
if (!connected && !msgNotify) {
msgNotify = Notify.create({
type: 'negative',
timeout: 0,
position: 'top-right',
message: '与WebSocket服务连接断开',
classes: 'my-notif-class',
});
} else if (msgNotify && connected) {
msgNotify();
msgNotify = null;
}
lineNetApp.setOptions({
mouseToolOptions: {
boxSelect: false,
viewportDrag: true,
wheelZoom: true,
},
interactiveTypeOptions: {
interactiveGraphicTypeIncludes: [
RunLine.Type,
StationLine.Type,
TrainLine.Type,
],
},
});
RunLineOperateInteraction.init(lineNetApp);
return lineNetApp;
}
export async function loadLineNetDatas(): Promise<IGraphicStorage> {
export async function loadLineNetDatas(app: GraphicApp) {
const lineNetStore = useLineNetStore();
const { proto: base64, name: lineNetName } = await getPublishLineNet();
lineNetStore.setLineNetName(lineNetName);
const datas: GraphicData[] = [];
if (base64) {
const storage = graphicData.RtssGraphicStorage.deserialize(
toUint8Array(base64)
);
console.log('加载数据', storage);
app.updateCanvas(storage.canvas);
const datas: GraphicData[] = [];
storage.runLines.forEach((runLine) => {
const g = new RunLineData(runLine);
datas.push(g);
@ -132,40 +112,50 @@ export async function loadLineNetDatas(): Promise<IGraphicStorage> {
const g = new RectData(rect);
datas.push(g);
});
return Promise.resolve({
canvasProperty: storage.canvas,
datas: datas,
await app.loadGraphic(datas);
const pathLineList = app.queryStore.queryByType(PathLine.Type);
pathLineList.forEach((pathLine) => {
pathLine.visible = false;
});
StompCli.new({
wsUrl: `${getWebsocketUrl()}`,
token: getJwtToken() as string,
});
msgBroker = new AppWsMsgBroker(app);
msgBroker.subscribe({
destination: '/queue/lineNet',
messageConverter: (message: Uint8Array) => {
const storage = state.WsLineNetMessage.deserialize(message);
const states: GraphicState[] = [];
storage.offset.forEach((item) => {
states.push(new TrainLineState(item));
});
return states;
},
});
msgBroker.subscribe({
destination: '/queue/xian/ncc/alert',
messageHandle: (message: Uint8Array) => {
const storage = alert.NccAlertInfoMessage.deserialize(message);
lineNetStore.setAlarmInfo(storage.messages as []);
},
});
let msgNotify: null | ((props?: QNotifyUpdateOptions | undefined) => void) =
null;
app.on('websocket-connect-state-change', (connected) => {
if (!connected && !msgNotify) {
msgNotify = Notify.create({
type: 'negative',
timeout: 0,
position: 'top-right',
message: '通信链接已断开!',
});
} else if (msgNotify && connected) {
msgNotify();
msgNotify = null;
}
});
} else {
return Promise.resolve({
datas: [],
});
app.loadGraphic([]);
}
}
function handleSubscribe(lineNetApp: IGraphicApp) {
const lineNetStore = useLineNetStore();
lineNetApp.enableWsMassaging({
wsUrl: `${getWebsocketUrl()}`,
token: getJwtToken() as string,
});
lineNetApp.subscribe({
destination: '/queue/lineNet',
createOnNotFound: { graphicTypes: [TrainLine.Type] },
messageConverter: (message: Uint8Array) => {
const storage = state.WsLineNetMessage.deserialize(message);
const states: GraphicState[] = [];
storage.offset.forEach((item) => {
states.push(new TrainLineState(item));
});
return states;
},
});
lineNetApp.subscribe({
destination: '/queue/xian/ncc/alert',
messageHandle: (message: Uint8Array) => {
const storage = alert.NccAlertInfoMessage.deserialize(message);
lineNetStore.setAlarmInfo(storage.messages as []);
},
});
}

View File

@ -1,210 +0,0 @@
import {
LogicSection,
LogicSectionTemplate,
} from 'src/graphics/logicSection/LogicSection';
import { Platform, PlatformTemplate } from 'src/graphics/platform/Platform';
import { SectionTemplate } from 'src/graphics/section/Section';
import { Signal, SignalTemplate } from 'src/graphics/signal/Signal';
import { Station, StationTemplate } from 'src/graphics/station/Station';
import { Turnout, TurnoutTemplate } from 'src/graphics/turnout/Turnout';
import {
IGraphicApp,
GraphicData,
newGraphicApp,
MenuItemOptions,
ContextMenu,
IGraphicStorage,
} from 'jl-graphic';
import {
LogicSectionData,
LogicSectionState,
} from './graphics/LogicSectionInteraction';
import { SeparatorTemplate } from 'src/graphics/separator/Separator';
import { AxleCountingTemplate } from 'src/graphics/axleCounting/AxleCounting';
import { SignalData, SignalState } from './graphics/SignalInteraction';
import { PlatformData, PlatformState } from './graphics/PlatformInteraction';
import { StationData, StationState } from './graphics/StationInteraction';
import { TurnoutData, TurnoutStates } from './graphics/TurnoutInteraction';
import { SectionData } from './graphics/SectionInteraction';
import { SeparatorData } from './graphics/SeparatorInteraction';
import { AxleCountingData } from './graphics/AxleCountingInteraction';
import { getPublishMapInfoByLineId } from 'src/api/PublishApi';
import { useRangeConfigStore } from 'src/stores/range-config-store';
import { graphicData } from 'src/protos/stationLayoutGraphics';
import { toUint8Array } from 'js-base64';
let rangeConfigApp: IGraphicApp;
export function getRangeConfigApp() {
return rangeConfigApp;
}
export function initRangeConfigApp(lineId: number) {
rangeConfigApp = newGraphicApp({
mouseToolOptions: {
boxSelect: true,
viewportDrag: true,
wheelZoom: true,
},
interactiveGraphicTypeIncludes: [
Signal.Type,
Platform.Type,
Station.Type,
LogicSection.Type,
Turnout.Type,
],
dataLoader: () => loadRangeConfigDatas(lineId),
});
const graphicTemplate = [
new SignalTemplate(new SignalData(), new SignalState()),
new PlatformTemplate(new PlatformData(), new PlatformState()),
new StationTemplate(new StationData(), new StationState()),
new TurnoutTemplate(new TurnoutData(), new TurnoutStates()),
new SectionTemplate(new SectionData()),
new LogicSectionTemplate(new LogicSectionData(), new LogicSectionState()),
new SeparatorTemplate(new SeparatorData()),
new AxleCountingTemplate(new AxleCountingData()),
];
rangeConfigApp.registerGraphicTemplates(...graphicTemplate);
rangeConfigApp.reload().then(() => {
const QuickJumpMenu = new ContextMenu({
name: '快捷跳转',
groups: [
{
items: rangeConfigApp.queryStore
.queryByType<Station>(Station.Type)
.map<MenuItemOptions>((station) => ({
name: station.datas.name ?? '',
handler: () => {
rangeConfigApp?.makeGraphicCenterShow(station);
},
})),
},
],
});
rangeConfigApp.registerMenu(QuickJumpMenu);
rangeConfigApp.canvas.on('_rightclick', (e) => {
QuickJumpMenu.open(e.global);
});
});
return rangeConfigApp;
}
export async function loadRangeConfigDatas(
lineId: number
): Promise<IGraphicStorage> {
if (!lineId) throw Error('请先选择线路');
const store = useRangeConfigStore();
const { proto: base64, name: lineName } = await getPublishMapInfoByLineId(
lineId,
'line'
);
store.setLineName(lineName);
const datas: GraphicData[] = [];
if (base64) {
const storage = graphicData.RtssGraphicStorage.deserialize(
toUint8Array(base64)
);
console.log('加载数据', storage);
storage.Platforms.forEach((platform) => {
datas.push(new PlatformData(platform));
});
storage.stations.forEach((station) => {
datas.push(new StationData(station));
});
storage.turnouts.forEach((turnout) => {
datas.push(new TurnoutData(turnout));
});
storage.section.forEach((section) => {
datas.push(new SectionData(section));
});
storage.logicSections.forEach((section) => {
datas.push(new LogicSectionData(section));
});
storage.separators.forEach((separator) => {
datas.push(new SeparatorData(separator));
});
storage.axleCountings.forEach((axleCounting) => {
datas.push(new AxleCountingData(axleCounting));
});
return Promise.resolve({
canvasProperty: storage.canvas,
datas: datas,
});
} else {
return Promise.resolve({
datas: [],
});
}
}
export async function loadLineDatas(app: IGraphicApp) {
const store = useRangeConfigStore();
const lineId = store.lineId;
if (!lineId) return;
const { proto: base64, name: lineName } = await getPublishMapInfoByLineId(
lineId,
'line'
);
store.setLineName(lineName);
if (base64) {
const storage = graphicData.RtssGraphicStorage.deserialize(
toUint8Array(base64)
);
const datas: GraphicData[] = [];
storage.Platforms.forEach((platform) => {
const g = new PlatformData(platform);
datas.push(g);
});
const quickJumpMenuItem: MenuItemOptions[] = [];
storage.stations.forEach((station) => {
datas.push(new StationData(station));
const item: MenuItemOptions = {
name: station.name,
handler: () => {
const g = app.queryStore.queryById(station.common.id);
if (g) {
app.makeGraphicCenterShow(g);
}
},
};
quickJumpMenuItem.push(item);
});
storage.turnouts.forEach((turnout) => {
datas.push(new TurnoutData(turnout));
});
storage.section.forEach((section) => {
datas.push(new SectionData(section));
});
storage.logicSections.forEach((section) => {
datas.push(new LogicSectionData(section));
});
storage.separators.forEach((separator) => {
datas.push(new SeparatorData(separator));
});
storage.axleCountings.forEach((axleCounting) => {
datas.push(new AxleCountingData(axleCounting));
});
const QuickJumpMenu = new ContextMenu({
name: '快捷跳转',
groups: [
{
items: quickJumpMenuItem,
},
],
});
app.registerMenu(QuickJumpMenu);
app.canvas.on('_rightclick', (e) => {
QuickJumpMenu.open(e.global);
});
}
}
export function destroyRangeConfigApp(): void {
if (rangeConfigApp) {
rangeConfigApp.destroy();
}
}

View File

@ -1,5 +1,5 @@
import { Graphics } from 'pixi.js';
import { calculateMirrorPoint } from 'jl-graphic';
import { calculateMirrorPoint } from 'src/jl-graphic';
import { graphicData } from 'src/protos/stationLayoutGraphics';
import { Turnout, TurnoutPort } from './turnout/Turnout';
import { Section, SectionPort } from './section/Section';
@ -77,7 +77,7 @@ export function drawArrow(
export function createRelatedRefProto(
type: string,
id: number,
id: string,
port?: TurnoutPort | SectionPort
) {
const typeMap = new Map([
@ -111,6 +111,6 @@ export function protoPort2Data(port: graphicData.RelatedRef.DevicePort) {
export interface IRelatedRefData {
deviceType: graphicData.RelatedRef.DeviceType; //关联的设备类型
id: number; //关联的设备ID
id: string; //关联的设备ID
devicePort: graphicData.RelatedRef.DevicePort; //关联的设备端口
}

View File

@ -5,7 +5,7 @@ import {
JlGraphic,
JlGraphicTemplate,
VectorText,
} from 'jl-graphic';
} from 'src/jl-graphic';
import { IRelatedRefData, protoPort2Data } from '../CommonGraphics';
import { KilometerSystem } from '../signal/Signal';

View File

@ -5,10 +5,10 @@ import {
GraphicDrawAssistant,
GraphicIdGenerator,
GraphicInteractionPlugin,
IDrawApp,
JlDrawApp,
JlGraphic,
distance2,
} from 'jl-graphic';
} from 'src/jl-graphic';
import {
IAxleCountingData,
@ -16,7 +16,7 @@ import {
AxleCountingTemplate,
AxleCountingConsts,
} from './AxleCounting';
import { Section, SectionPort } from '../section/Section';
import { Section, SectionPort, SectionType } from '../section/Section';
import { Turnout, TurnoutPort } from '../turnout/Turnout';
import { IRelatedRefData, createRelatedRefProto } from '../CommonGraphics';
import { Signal } from '../signal/Signal';
@ -42,8 +42,8 @@ export class AxleCountingDraw extends GraphicDrawAssistant<
IAxleCountingData
> {
codeGraph: AxleCounting;
constructor(app: IDrawApp, template: AxleCountingTemplate) {
super(app, template, 'sym_o_circle', '计轴');
constructor(app: JlDrawApp, template: AxleCountingTemplate) {
super(app, template, 'sym_o_circle', '不展示');
this.codeGraph = this.graphicTemplate.new();
this.container.addChild(this.codeGraph);
AxleCountingInteraction.init(app);
@ -198,6 +198,12 @@ export class AxleCountingDraw extends GraphicDrawAssistant<
if (axleCountingPs.y > height.y) {
direction = -1;
}
if (
section.datas.sectionType === SectionType.Logic ||
section.datas.children.includes(refDevice.id)
) {
return;
}
if (refDevice.type == Section.Type || refDevice.type == Turnout.Type)
this.draw(
axleCountingPs,
@ -346,10 +352,10 @@ function buildAbsorbablePositions(
export class AxleCountingInteraction extends GraphicInteractionPlugin<AxleCounting> {
static Name = 'AxleCounting_transform';
constructor(app: IDrawApp) {
constructor(app: JlDrawApp) {
super(AxleCountingInteraction.Name, app);
}
static init(app: IDrawApp) {
static init(app: JlDrawApp) {
return new AxleCountingInteraction(app);
}
filter(...grahpics: JlGraphic[]): AxleCounting[] | undefined {

View File

@ -1,224 +0,0 @@
import { IPointData } from 'pixi.js';
import {
GraphicData,
JlGraphic,
JlGraphicTemplate,
calculateDistanceFromPointToLine,
getRectangleCenter,
ILineGraphic,
} from 'jl-graphic';
import { SectionGraphic } from './SectionGraphic';
import { graphicData } from 'src/protos/stationLayoutGraphics';
import { Section, SectionType } from '../section/Section';
import { arePolylinesIntersect } from './ConcentrationDividingLineUtils';
import { createRelatedRefProto } from '../CommonGraphics';
import { Turnout,TurnoutPort } from '../turnout/Turnout';
export interface IConcentrationDividingLineData extends GraphicData {
get code(): string; // 编号
set code(v: string);
get points(): IPointData[]; // 线坐标点
set points(points: IPointData[]);
get refLeftStationId(): number; //左边关联的集中站id
set refLeftStationId(v: number);
get refRightStationId(): number; //右边关联的集中站id
set refRightStationId(v: number);
get nodeConWithSecs(): graphicData.NodeConWithSec[]; // 集中区分割线与区段的交点
set nodeConWithSecs(nodes: graphicData.NodeConWithSec[]);
get isOtherLineConcentrationDividingLine(): boolean; //集中区分割线绘制在其它线的边界处
set isOtherLineConcentrationDividingLine(v: boolean);
clone(): IConcentrationDividingLineData;
copyFrom(data: IConcentrationDividingLineData): void;
eq(other: IConcentrationDividingLineData): boolean;
}
export const ConcentrationDividingLineConsts = {
lineColor: '#f00',
lineWidth: 2,
};
enum devicePort {
'A',
'B',
'C',
}
export class ConcentrationDividingLine
extends JlGraphic
implements ILineGraphic
{
static Type = 'ConcentrationDividingLine';
lineGraphic: SectionGraphic;
constructor() {
super(ConcentrationDividingLine.Type);
this.lineGraphic = new SectionGraphic();
this.transformSave = true;
this.addChild(this.lineGraphic);
}
get datas(): IConcentrationDividingLineData {
return this.getDatas<IConcentrationDividingLineData>();
}
get linePoints(): IPointData[] {
return this.datas.points;
}
set linePoints(points: IPointData[]) {
const old = this.datas.clone();
old.points = points;
this.updateData(old);
}
doRepaint() {
if (this.datas.points.length < 2) {
throw new Error('Link坐标数据异常');
}
this.lineGraphic.clear();
this.lineGraphic.points = this.datas.points;
this.lineGraphic.lineStyle(
ConcentrationDividingLineConsts.lineWidth,
ConcentrationDividingLineConsts.lineColor
);
this.lineGraphic.paint();
}
buildRelation() {
const nodeConWithSecs: graphicData.NodeConWithSec[] = [];
const sections = this.queryStore
.queryByType<Section>(Section.Type)
.filter((g) => g.datas.sectionType == SectionType.Physical);
const hasNodeSection = new Map<number, string>();
sections.forEach((section) => {
const changeSectionData = section.datas.points.map((point) =>
section.localToCanvasPoint(point)
);
const changeConcentrationDividingLineData = this.datas.points.map(
(point) => this.localToCanvasPoint(point)
);
const hasNode = arePolylinesIntersect(
changeSectionData,
changeConcentrationDividingLineData
);
if (hasNode) {
const minA = calculateDistanceFromPointToLine(
hasNode.segment2[0],
hasNode.segment2[1],
section.localToCanvasPoint(section.getStartPoint())
);
const minB = calculateDistanceFromPointToLine(
hasNode.segment2[0],
hasNode.segment2[1],
section.localToCanvasPoint(section.getEndPoint())
);
const relationParam = minA > minB ? TurnoutPort.B : TurnoutPort.A;
const portRefOtherDevice =
relationParam == 'A' ? section.datas.paRef : section.datas.pbRef;
if (
portRefOtherDevice?.id &&
!hasNodeSection.get(section.id) &&
!hasNodeSection.get(portRefOtherDevice.id)
) {
const refDevice = this.queryStore.queryById<Turnout | Section>(
portRefOtherDevice?.id
);
const [leftDevice, rightDevice] =
refDevice.localToCanvasPoint(
getRectangleCenter(refDevice.getLocalBounds())
).x <
section.localToCanvasPoint(
getRectangleCenter(section.getLocalBounds())
).x
? [
{
device: refDevice,
port: devicePort[
portRefOtherDevice.devicePort
] as TurnoutPort,
},
{ device: section, port: relationParam },
]
: [
{ device: section, port: relationParam },
{
device: refDevice,
port: devicePort[
portRefOtherDevice.devicePort
] as TurnoutPort,
},
];
hasNodeSection.set(leftDevice.device.id, '1');
hasNodeSection.set(rightDevice.device.id, '1');
nodeConWithSecs.push(
new graphicData.NodeConWithSec({
leftSection: createRelatedRefProto(
leftDevice.device.type,
leftDevice.device.id,
leftDevice.port
),
rightSection: createRelatedRefProto(
rightDevice.device.type,
rightDevice.device.id,
rightDevice.port
),
})
);
} else if (!hasNodeSection.get(section.id) && !portRefOtherDevice?.id) {
const [leftSectionId, rightSectionId] =
relationParam === 'A'
? [undefined, section.id]
: [section.id, undefined];
hasNodeSection.set(section.id, '1');
if (leftSectionId == undefined) {
nodeConWithSecs.push(
new graphicData.NodeConWithSec({
leftSection: undefined,
rightSection: createRelatedRefProto(
Section.Type,
rightSectionId,
TurnoutPort.A
),
})
);
} else {
nodeConWithSecs.push(
new graphicData.NodeConWithSec({
leftSection: createRelatedRefProto(
Section.Type,
leftSectionId,
TurnoutPort.B
),
rightSection: undefined,
})
);
}
}
}
});
nodeConWithSecs.sort((a, b) => {
const sectionAId = a.leftSection ? a.leftSection.id : a.rightSection.id;
const sectionA = this.queryStore.queryById<Section | Turnout>(sectionAId);
const sectionBId = b.leftSection ? b.leftSection.id : b.rightSection.id;
const sectionB = this.queryStore.queryById<Section | Turnout>(sectionBId);
return (
sectionA.localToCanvasPoint(
getRectangleCenter(sectionA.getLocalBounds())
).y -
sectionB.localToCanvasPoint(
getRectangleCenter(sectionB.getLocalBounds())
).y
);
});
this.datas.nodeConWithSecs = nodeConWithSecs;
}
}
export class ConcentrationDividingLineTemplate extends JlGraphicTemplate<ConcentrationDividingLine> {
constructor(dataTemplate: IConcentrationDividingLineData) {
super(ConcentrationDividingLine.Type, { dataTemplate });
}
new() {
const g = new ConcentrationDividingLine();
g.loadData(this.datas);
return g;
}
}

View File

@ -1,212 +0,0 @@
import {
IGraphicApp,
GraphicDrawAssistant,
GraphicInteractionPlugin,
IDrawApp,
JlGraphic,
linePoint,
PolylineEditPlugin,
addWayPoint,
clearWayPoint,
MenuItemOptions,
ContextMenu
} from 'jl-graphic';
import {
IConcentrationDividingLineData,
ConcentrationDividingLine,
ConcentrationDividingLineConsts,
ConcentrationDividingLineTemplate,
} from './ConcentrationDividingLine';
import {
DisplayObject,
FederatedMouseEvent,
Graphics,
IHitArea,
Point,
} from 'pixi.js';
import { getWayLineIndex } from '../polygon/PolygonUtils';
export class ConcentrationDividingLineDraw extends GraphicDrawAssistant<
ConcentrationDividingLineTemplate,
IConcentrationDividingLineData
> {
points: Point[] = [];
graphic = new Graphics();
constructor(app: IDrawApp, template: ConcentrationDividingLineTemplate) {
super(app, template, 'sym_o_timeline', '集中区分割线');
this.container.addChild(this.graphic);
ConcentrationDividingLinePointEditPlugin.init(app, this);
}
bind(): void {
super.bind();
}
unbind(): void {
super.unbind();
}
onLeftDown(e: FederatedMouseEvent): void {
const { x, y } = this.toCanvasCoordinates(e.global);
const p = new Point(x, y);
this.points.push(p);
}
onRightClick(): void {
if (this.points.length < 2) {
this.finish();
return;
}
this.createAndStore(true);
}
onEsc(): void {
if (this.points.length < 2) {
this.finish();
return;
}
this.createAndStore(true);
}
redraw(p: Point): void {
if (this.points.length < 1) return;
this.graphic.clear();
this.graphic.lineStyle(
ConcentrationDividingLineConsts.lineWidth,
ConcentrationDividingLineConsts.lineColor
);
const ps = [...this.points];
ps.push(p);
ps.forEach((p, i) => {
if (i !== 0) {
this.graphic.lineTo(p.x, p.y);
} else {
this.graphic.moveTo(p.x, p.y);
}
});
}
prepareData(data: IConcentrationDividingLineData): boolean {
if (this.points.length < 2) {
console.log('ConcentrationDividingLine绘制因点不够取消绘制');
return false;
}
data.points = this.points;
return true;
}
clearCache(): void {
this.points = [];
this.graphic.clear();
}
}
export class ConcentrationDividingLineGraphicHitArea implements IHitArea {
concentrationDividingLine: ConcentrationDividingLine;
constructor(concentrationDividingLine: ConcentrationDividingLine) {
this.concentrationDividingLine = concentrationDividingLine;
}
contains(x: number, y: number): boolean {
for (
let i = 1;
i < this.concentrationDividingLine.datas.points.length;
i++
) {
const p1 = this.concentrationDividingLine.datas.points[i - 1];
const p2 = this.concentrationDividingLine.datas.points[i];
if (
linePoint(p1, p2, { x, y }, ConcentrationDividingLineConsts.lineWidth)
) {
return true;
}
}
return false;
}
}
const addWaypointConfig: MenuItemOptions = {
name: '添加路径点',
};
const clearWaypointsConfig: MenuItemOptions = {
name: '清除所有路径点',
};
const ConcentrationDividingLineEditMenu: ContextMenu = ContextMenu.init({
name: '集中区分割线编辑菜单',
groups: [
{
items: [addWaypointConfig, clearWaypointsConfig],
},
],
});
export class ConcentrationDividingLinePointEditPlugin extends GraphicInteractionPlugin<ConcentrationDividingLine> {
static Name = 'ConcentrationDividingLinePointDrag';
drawAssistant: ConcentrationDividingLineDraw;
constructor(app: IGraphicApp, da: ConcentrationDividingLineDraw) {
super(ConcentrationDividingLinePointEditPlugin.Name, app);
this.drawAssistant = da;
app.registerMenu(ConcentrationDividingLineEditMenu);
}
static init(app: IGraphicApp, da: ConcentrationDividingLineDraw) {
return new ConcentrationDividingLinePointEditPlugin(app, da);
}
filter(...grahpics: JlGraphic[]): ConcentrationDividingLine[] | undefined {
return grahpics.filter(
(g) => g.type == ConcentrationDividingLine.Type
) as ConcentrationDividingLine[];
}
bind(g: ConcentrationDividingLine): void {
g.lineGraphic.eventMode = 'static';
g.lineGraphic.cursor = 'pointer';
g.lineGraphic.hitArea = new ConcentrationDividingLineGraphicHitArea(g);
g.transformSave = true;
g.on('selected', this.onSelected, this);
g.on('unselected', this.onUnselected, this);
g.on('_rightclick', this.onContextMenu, this);
}
unbind(g: ConcentrationDividingLine): void {
g.off('selected', this.onSelected, this);
g.off('unselected', this.onUnselected, this);
g.off('_rightclick', this.onContextMenu, this);
}
onContextMenu(e: FederatedMouseEvent) {
const target = e.target as DisplayObject;
const concentrationDividingLine =
target.getGraphic() as ConcentrationDividingLine;
this.app.updateSelected(concentrationDividingLine);
const p = concentrationDividingLine.screenToLocalPoint(e.global);
addWaypointConfig.handler = () => {
const linePoints = concentrationDividingLine.linePoints;
const { start, end } = getWayLineIndex(linePoints, p);
addWayPoint(concentrationDividingLine, false, start, end, p);
};
clearWaypointsConfig.handler = () => {
clearWayPoint(concentrationDividingLine, false);
};
ConcentrationDividingLineEditMenu.open(e.global);
}
onSelected(g: DisplayObject): void {
const concentrationDividingLine = g as ConcentrationDividingLine;
let lep = concentrationDividingLine.getAssistantAppend<PolylineEditPlugin>(
PolylineEditPlugin.Name
);
if (!lep) {
lep = new PolylineEditPlugin(concentrationDividingLine);
concentrationDividingLine.addAssistantAppend(lep);
}
lep.showAll();
}
onUnselected(g: DisplayObject): void {
const concentrationDividingLine = g as ConcentrationDividingLine;
const lep =
concentrationDividingLine.getAssistantAppend<PolylineEditPlugin>(
PolylineEditPlugin.Name
);
if (lep) {
lep.hideAll();
}
}
}

View File

@ -1,195 +0,0 @@
import { IPointData } from 'pixi.js';
import { Section } from '../section/Section';
import { Turnout } from '../turnout/Turnout';
import { graphicData } from 'src/protos/stationLayoutGraphics';
import { IDrawApp, JlGraphic } from 'jl-graphic';
import { GraphicDataBase } from 'src/drawApp/graphics/GraphicDataBase';
import { TurnoutData } from 'src/drawApp/graphics/TurnoutInteraction';
import { SectionData } from 'src/drawApp/graphics/SectionInteraction';
import { SignalData } from 'src/drawApp/graphics/SignalInteraction';
import { Signal } from '../signal/Signal';
import { Platform } from '../platform/Platform';
import { PlatformData } from 'src/drawApp/graphics/PlatformInteraction';
//判断线段与线段有木有交点
export function isSegmentsIntersect(
segment1: IPointData[],
segment2: IPointData[]
) {
const [p1, p2] = segment1;
const [p3, p4] = segment2;
// 判断包围盒是否相交
if (
Math.max(p1.x, p2.x) < Math.min(p3.x, p4.x) ||
Math.min(p1.x, p2.x) > Math.max(p3.x, p4.x) ||
Math.max(p1.y, p2.y) < Math.min(p3.y, p4.y) ||
Math.min(p1.y, p2.y) > Math.max(p3.y, p4.y)
) {
return false;
}
// 计算向量叉积
const cross1 = crossProduct(p3, p1, p4);
const cross2 = crossProduct(p3, p2, p4);
const cross3 = crossProduct(p1, p3, p2);
const cross4 = crossProduct(p1, p4, p2);
if (cross1 * cross2 < 0 && cross3 * cross4 < 0) {
return true;
}
return false;
}
function crossProduct(p1: IPointData, p2: IPointData, p3: IPointData) {
return (p2.x - p1.x) * (p3.y - p1.y) - (p3.x - p1.x) * (p2.y - p1.y);
}
export function getSegmentsFromPolyline(polyline: IPointData[]) {
const segments = [];
for (let i = 0; i < polyline.length - 1; i++) {
const segment = [polyline[i], polyline[i + 1]];
segments.push(segment);
}
return segments;
}
//判断折线与折线有木有交点
export function arePolylinesIntersect(
polyline1: IPointData[],
polyline2: IPointData[]
) {
const segments1 = getSegmentsFromPolyline(polyline1);
const segments2 = getSegmentsFromPolyline(polyline2);
for (const segment1 of segments1) {
for (const segment2 of segments2) {
if (isSegmentsIntersect(segment1, segment2)) {
return { hasnode: true, segment1, segment2 };
}
}
}
return false;
}
//获取指定区间内的物理区段和道岔
export function findContainDevice(
refDevice: Section | Turnout,
refDevicePort: graphicData.RelatedRef.DevicePort,
containDeviceIds: number[],
drawApp: IDrawApp
) {
const devicePort = graphicData.RelatedRef.DevicePort;
containDeviceIds.push(refDevice.id);
switch (true) {
case refDevice instanceof Section:
const sectionPaorbRef =
refDevicePort == devicePort.B
? refDevice.datas.paRef
: refDevice.datas.pbRef;
if (sectionPaorbRef && !containDeviceIds.includes(sectionPaorbRef.id)) {
const pbRefDevice = drawApp.queryStore.queryById<Section | Turnout>(
sectionPaorbRef.id
);
findContainDevice(
pbRefDevice,
sectionPaorbRef.devicePort,
containDeviceIds,
drawApp
);
}
break;
//道岔需要分路--实际的走向
case refDevice instanceof Turnout:
const otherPorts = [devicePort.A, devicePort.B, devicePort.C].filter(
(port) => port !== refDevicePort
);
otherPorts.forEach((port) => {
switch (port) {
case devicePort.A:
const turnoutPaRef = refDevice.datas.paRef;
if (turnoutPaRef && !containDeviceIds.includes(turnoutPaRef.id)) {
const paRefDevice = drawApp.queryStore.queryById<
Section | Turnout
>(turnoutPaRef.id);
findContainDevice(
paRefDevice,
turnoutPaRef.devicePort,
containDeviceIds,
drawApp
);
}
break;
case devicePort.B:
const turnoutPbRef = refDevice.datas.pbRef;
if (turnoutPbRef && !containDeviceIds.includes(turnoutPbRef.id)) {
const pbRefDevice = drawApp.queryStore.queryById<
Section | Turnout
>(turnoutPbRef.id);
findContainDevice(
pbRefDevice,
turnoutPbRef.devicePort,
containDeviceIds,
drawApp
);
}
break;
case devicePort.C:
const turnoutPcRef = (refDevice as Turnout).datas.pcRef;
if (turnoutPcRef && !containDeviceIds.includes(turnoutPcRef.id)) {
const pcRefDevice = drawApp.queryStore.queryById<
Section | Turnout
>(turnoutPcRef.id);
findContainDevice(
pcRefDevice,
turnoutPcRef.devicePort,
containDeviceIds,
drawApp
);
}
break;
}
});
break;
}
}
export function handleCentralizedStationsData(
devices: JlGraphic[],
centralizedStation: number
) {
interface GraphicData {
centralizedStation: number;
}
const dataMap = new Map<string, GraphicDataBase>([
[Turnout.Type, new TurnoutData()],
[Section.Type, new SectionData()],
[Signal.Type, new SignalData()],
[Platform.Type, new PlatformData()],
]);
devices.forEach((device) => {
const data = dataMap.get(device.type);
if (data) {
data.copyFrom(device.saveData());
const dataCopy = data as GraphicDataBase & GraphicData;
dataCopy.centralizedStation = centralizedStation;
device.updateData(data);
}
});
}
//找到公共的元素
type findType = string | number;
export function findCommonElements(arrays: findType[][]) {
if (arrays.length === 0) {
return [];
}
const commonElements: findType[] = [];
arrays[0].forEach((element) => {
if (arrays.every((arr) => arr.includes(element))) {
commonElements.push(element);
}
});
return commonElements;
}

View File

@ -1,57 +0,0 @@
import { Graphics, IPointData } from 'pixi.js';
import { assertBezierPoints, convertToBezierParams } from 'jl-graphic';
export enum DevicePort {
A = 'A',
B = 'B',
C = 'C',
}
export class SectionGraphic extends Graphics {
static Type = 'SectionGraphic';
private _points: IPointData[] = [];
public get points(): IPointData[] {
return this._points;
}
public set points(value: IPointData[]) {
if (!this.isCurve) {
if (value.length < 2) {
throw Error('Polyline must have at least 2 points');
}
} else {
assertBezierPoints(value);
}
this._points = value;
}
private _segmentsCount = 10;
public get segmentsCount(): number {
return this._segmentsCount;
}
public set segmentsCount(value: number) {
if (value < 1) {
throw Error('segmentsCount must be at least 1');
}
this._segmentsCount = value;
}
isCurve = false;
constructor() {
super();
}
paint() {
if (this.isCurve) {
const bps = convertToBezierParams(this.points);
bps.forEach((bp) => {
this.drawBezierCurve(bp.p1, bp.p2, bp.cp1, bp.cp2, this.segmentsCount);
});
} else {
this.moveTo(this.points[0].x, this.points[0].y);
for (let i = 1; i < this.points.length; i++) {
this.lineTo(this.points[i].x, this.points[i].y);
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More